1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Oracle Corporation code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Oracle Corporation
20 : * Portions created by the Initial Developer are Copyright (C) 2004
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
25 : * Shawn Wilsher <me@shawnwilsher.com>
26 : * John Zhang <jzhang@aptana.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either the GNU General Public License Version 2 or later (the "GPL"), or
30 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include <limits.h>
43 : #include <stdio.h>
44 :
45 : #include "nsError.h"
46 : #include "nsMemory.h"
47 : #include "nsThreadUtils.h"
48 : #include "nsIClassInfoImpl.h"
49 : #include "nsIProgrammingLanguage.h"
50 : #include "Variant.h"
51 :
52 : #include "mozIStorageError.h"
53 :
54 : #include "mozStorageBindingParams.h"
55 : #include "mozStorageConnection.h"
56 : #include "mozStorageStatementJSHelper.h"
57 : #include "mozStoragePrivateHelpers.h"
58 : #include "mozStorageStatementParams.h"
59 : #include "mozStorageStatementRow.h"
60 : #include "mozStorageStatement.h"
61 : #include "sampler.h"
62 :
63 : #include "prlog.h"
64 :
65 : #include "mozilla/FunctionTimer.h"
66 :
67 : #ifdef PR_LOGGING
68 : extern PRLogModuleInfo* gStorageLog;
69 : #endif
70 :
71 : namespace mozilla {
72 : namespace storage {
73 :
74 : ////////////////////////////////////////////////////////////////////////////////
75 : //// nsIClassInfo
76 :
77 396 : NS_IMPL_CI_INTERFACE_GETTER5(
78 : Statement,
79 : mozIStorageStatement,
80 : mozIStorageBaseStatement,
81 : mozIStorageBindingParams,
82 : mozIStorageValueArray,
83 : mozilla::storage::StorageBaseStatementInternal
84 396 : )
85 :
86 : class StatementClassInfo : public nsIClassInfo
87 1464 : {
88 : public:
89 : NS_DECL_ISUPPORTS
90 :
91 : NS_IMETHODIMP
92 396 : GetInterfaces(PRUint32 *_count, nsIID ***_array)
93 : {
94 396 : return NS_CI_INTERFACE_GETTER_NAME(Statement)(_count, _array);
95 : }
96 :
97 : NS_IMETHODIMP
98 16934 : GetHelperForLanguage(PRUint32 aLanguage, nsISupports **_helper)
99 : {
100 16934 : if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
101 16934 : static StatementJSHelper sJSHelper;
102 16934 : *_helper = &sJSHelper;
103 16934 : return NS_OK;
104 : }
105 :
106 0 : *_helper = nsnull;
107 0 : return NS_OK;
108 : }
109 :
110 : NS_IMETHODIMP
111 0 : GetContractID(char **_contractID)
112 : {
113 0 : *_contractID = nsnull;
114 0 : return NS_OK;
115 : }
116 :
117 : NS_IMETHODIMP
118 0 : GetClassDescription(char **_desc)
119 : {
120 0 : *_desc = nsnull;
121 0 : return NS_OK;
122 : }
123 :
124 : NS_IMETHODIMP
125 0 : GetClassID(nsCID **_id)
126 : {
127 0 : *_id = nsnull;
128 0 : return NS_OK;
129 : }
130 :
131 : NS_IMETHODIMP
132 0 : GetImplementationLanguage(PRUint32 *_language)
133 : {
134 0 : *_language = nsIProgrammingLanguage::CPLUSPLUS;
135 0 : return NS_OK;
136 : }
137 :
138 : NS_IMETHODIMP
139 30602 : GetFlags(PRUint32 *_flags)
140 : {
141 30602 : *_flags = nsnull;
142 30602 : return NS_OK;
143 : }
144 :
145 : NS_IMETHODIMP
146 0 : GetClassIDNoAlloc(nsCID *_cid)
147 : {
148 0 : return NS_ERROR_NOT_AVAILABLE;
149 : }
150 : };
151 :
152 17794 : NS_IMETHODIMP_(nsrefcnt) StatementClassInfo::AddRef() { return 2; }
153 17794 : NS_IMETHODIMP_(nsrefcnt) StatementClassInfo::Release() { return 1; }
154 17364 : NS_IMPL_QUERY_INTERFACE1(StatementClassInfo, nsIClassInfo)
155 :
156 1464 : static StatementClassInfo sStatementClassInfo;
157 :
158 : ////////////////////////////////////////////////////////////////////////////////
159 : //// Statement
160 :
161 36811 : Statement::Statement()
162 : : StorageBaseStatementInternal()
163 : , mDBStatement(NULL)
164 : , mColumnNames()
165 36811 : , mExecuting(false)
166 : {
167 36811 : }
168 :
169 : nsresult
170 36811 : Statement::initialize(Connection *aDBConnection,
171 : const nsACString &aSQLStatement)
172 : {
173 36811 : NS_ASSERTION(aDBConnection, "No database connection given!");
174 36811 : NS_ASSERTION(!mDBStatement, "Statement already initialized!");
175 :
176 36811 : sqlite3 *db = aDBConnection->GetNativeConnection();
177 36811 : NS_ASSERTION(db, "We should never be called with a null sqlite3 database!");
178 :
179 36811 : int srv = aDBConnection->prepareStatement(PromiseFlatCString(aSQLStatement),
180 36811 : &mDBStatement);
181 36811 : if (srv != SQLITE_OK) {
182 : #ifdef PR_LOGGING
183 49 : PR_LOG(gStorageLog, PR_LOG_ERROR,
184 : ("Sqlite statement prepare error: %d '%s'", srv,
185 : ::sqlite3_errmsg(db)));
186 49 : PR_LOG(gStorageLog, PR_LOG_ERROR,
187 : ("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get()));
188 : #endif
189 49 : return NS_ERROR_FAILURE;
190 : }
191 :
192 : #ifdef PR_LOGGING
193 36762 : PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Initialized statement '%s' (0x%p)",
194 : PromiseFlatCString(aSQLStatement).get(),
195 : mDBStatement));
196 : #endif
197 :
198 36762 : mDBConnection = aDBConnection;
199 36762 : mParamCount = ::sqlite3_bind_parameter_count(mDBStatement);
200 36762 : mResultColumnCount = ::sqlite3_column_count(mDBStatement);
201 36762 : mColumnNames.Clear();
202 :
203 196705 : for (PRUint32 i = 0; i < mResultColumnCount; i++) {
204 159943 : const char *name = ::sqlite3_column_name(mDBStatement, i);
205 159943 : (void)mColumnNames.AppendElement(nsDependentCString(name));
206 : }
207 :
208 : #ifdef DEBUG
209 : // We want to try and test for LIKE and that consumers are using
210 : // escapeStringForLIKE instead of just trusting user input. The idea to
211 : // check to see if they are binding a parameter after like instead of just
212 : // using a string. We only do this in debug builds because it's expensive!
213 36762 : const nsCaseInsensitiveCStringComparator c;
214 36762 : nsACString::const_iterator start, end, e;
215 36762 : aSQLStatement.BeginReading(start);
216 36762 : aSQLStatement.EndReading(end);
217 36762 : e = end;
218 73634 : while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) {
219 : // We have a LIKE in here, so we perform our tests
220 : // FindInReadable moves the iterator, so we have to get a new one for
221 : // each test we perform.
222 110 : nsACString::const_iterator s1, s2, s3;
223 110 : s1 = s2 = s3 = start;
224 :
225 221 : if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) ||
226 114 : ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) ||
227 114 : ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) {
228 : // At this point, we didn't find a LIKE statement followed by ?, :,
229 : // or @, all of which are valid characters for binding a parameter.
230 : // We will warn the consumer that they may not be safely using LIKE.
231 : NS_WARNING("Unsafe use of LIKE detected! Please ensure that you "
232 : "are using mozIStorageStatement::escapeStringForLIKE "
233 : "and that you are binding that result to the statement "
234 1 : "to prevent SQL injection attacks.");
235 : }
236 :
237 : // resetting start and e
238 110 : start = e;
239 110 : e = end;
240 : }
241 : #endif
242 :
243 36762 : return NS_OK;
244 : }
245 :
246 : mozIStorageBindingParams *
247 237392 : Statement::getParams()
248 : {
249 : nsresult rv;
250 :
251 : // If we do not have an array object yet, make it.
252 237392 : if (!mParamsArray) {
253 177378 : nsCOMPtr<mozIStorageBindingParamsArray> array;
254 88689 : rv = NewBindingParamsArray(getter_AddRefs(array));
255 88689 : NS_ENSURE_SUCCESS(rv, nsnull);
256 :
257 177378 : mParamsArray = static_cast<BindingParamsArray *>(array.get());
258 : }
259 :
260 : // If there isn't already any rows added, we'll have to add one to use.
261 237392 : if (mParamsArray->length() == 0) {
262 266067 : nsRefPtr<BindingParams> params(new BindingParams(mParamsArray, this));
263 88689 : NS_ENSURE_TRUE(params, nsnull);
264 :
265 88689 : rv = mParamsArray->AddParams(params);
266 88689 : NS_ENSURE_SUCCESS(rv, nsnull);
267 :
268 : // We have to unlock our params because AddParams locks them. This is safe
269 : // because no reference to the params object was, or ever will be given out.
270 88689 : params->unlock(this);
271 :
272 : // We also want to lock our array at this point - we don't want anything to
273 : // be added to it. Nothing has, or will ever get a reference to it, but we
274 : // will get additional safety checks via assertions by doing this.
275 177378 : mParamsArray->lock();
276 : }
277 :
278 237392 : return *mParamsArray->begin();
279 : }
280 :
281 73594 : Statement::~Statement()
282 : {
283 36797 : (void)internalFinalize(true);
284 36797 : }
285 :
286 : ////////////////////////////////////////////////////////////////////////////////
287 : //// nsISupports
288 :
289 757368 : NS_IMPL_THREADSAFE_ADDREF(Statement)
290 794150 : NS_IMPL_THREADSAFE_RELEASE(Statement)
291 :
292 567887 : NS_INTERFACE_MAP_BEGIN(Statement)
293 567887 : NS_INTERFACE_MAP_ENTRY(mozIStorageStatement)
294 244077 : NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
295 244074 : NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
296 244074 : NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray)
297 242293 : NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
298 137333 : if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
299 16934 : foundInterface = static_cast<nsIClassInfo *>(&sStatementClassInfo);
300 : }
301 : else
302 120399 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageStatement)
303 101347 : NS_INTERFACE_MAP_END
304 :
305 :
306 : ////////////////////////////////////////////////////////////////////////////////
307 : //// StorageBaseStatementInternal
308 :
309 : Connection *
310 0 : Statement::getOwner()
311 : {
312 0 : return mDBConnection;
313 : }
314 :
315 : int
316 16240 : Statement::getAsyncStatement(sqlite3_stmt **_stmt)
317 : {
318 : // If we have no statement, we shouldn't be calling this method!
319 16240 : NS_ASSERTION(mDBStatement != NULL, "We have no statement to clone!");
320 :
321 : // If we do not yet have a cached async statement, clone our statement now.
322 16240 : if (!mAsyncStatement) {
323 9524 : nsDependentCString sql(::sqlite3_sql(mDBStatement));
324 4762 : int rc = mDBConnection->prepareStatement(sql, &mAsyncStatement);
325 4762 : if (rc != SQLITE_OK) {
326 0 : *_stmt = nsnull;
327 0 : return rc;
328 : }
329 :
330 : #ifdef PR_LOGGING
331 4762 : PR_LOG(gStorageLog, PR_LOG_NOTICE,
332 : ("Cloned statement 0x%p to 0x%p", mDBStatement, mAsyncStatement));
333 : #endif
334 : }
335 :
336 16240 : *_stmt = mAsyncStatement;
337 16240 : return SQLITE_OK;
338 : }
339 :
340 : nsresult
341 16241 : Statement::getAsynchronousStatementData(StatementData &_data)
342 : {
343 16241 : if (!mDBStatement)
344 1 : return NS_ERROR_UNEXPECTED;
345 :
346 : sqlite3_stmt *stmt;
347 16240 : int rc = getAsyncStatement(&stmt);
348 16240 : if (rc != SQLITE_OK)
349 0 : return convertResultCode(rc);
350 :
351 16240 : _data = StatementData(stmt, bindingParamsArray(), this);
352 :
353 16240 : return NS_OK;
354 : }
355 :
356 : already_AddRefed<mozIStorageBindingParams>
357 34 : Statement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
358 : {
359 68 : nsCOMPtr<mozIStorageBindingParams> params = new BindingParams(aOwner, this);
360 34 : return params.forget();
361 : }
362 :
363 :
364 : ////////////////////////////////////////////////////////////////////////////////
365 : //// mozIStorageStatement
366 :
367 : // proxy to StorageBaseStatementInternal using its define helper.
368 105035 : MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement, (void)0;)
369 :
370 : NS_IMETHODIMP
371 0 : Statement::Clone(mozIStorageStatement **_statement)
372 : {
373 0 : nsRefPtr<Statement> statement(new Statement());
374 0 : NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
375 :
376 0 : nsCAutoString sql(::sqlite3_sql(mDBStatement));
377 0 : nsresult rv = statement->initialize(mDBConnection, sql);
378 0 : NS_ENSURE_SUCCESS(rv, rv);
379 :
380 0 : statement.forget(_statement);
381 0 : return NS_OK;
382 : }
383 :
384 : NS_IMETHODIMP
385 20461 : Statement::Finalize()
386 : {
387 20461 : return internalFinalize(false);
388 : }
389 :
390 : nsresult
391 57258 : Statement::internalFinalize(bool aDestructing)
392 : {
393 57258 : if (!mDBStatement)
394 20496 : return NS_OK;
395 :
396 : #ifdef PR_LOGGING
397 36762 : PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s'",
398 : ::sqlite3_sql(mDBStatement)));
399 : #endif
400 :
401 36762 : int srv = ::sqlite3_finalize(mDBStatement);
402 36762 : mDBStatement = NULL;
403 :
404 36762 : if (mAsyncStatement) {
405 : // If the destructor called us, there are no pending async statements (they
406 : // hold a reference to us) and we can/must just kill the statement directly.
407 4762 : if (aDestructing)
408 0 : destructorAsyncFinalize();
409 : else
410 4762 : asyncFinalize();
411 : }
412 :
413 : // We are considered dead at this point, so any wrappers for row or params
414 : // need to lose their reference to us.
415 36762 : if (mStatementParamsHolder) {
416 : nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
417 21440 : do_QueryInterface(mStatementParamsHolder);
418 : nsCOMPtr<mozIStorageStatementParams> iParams =
419 21440 : do_QueryWrappedNative(wrapper);
420 10720 : StatementParams *params = static_cast<StatementParams *>(iParams.get());
421 10720 : params->mStatement = nsnull;
422 10720 : mStatementParamsHolder = nsnull;
423 : }
424 :
425 36762 : if (mStatementRowHolder) {
426 : nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
427 5270 : do_QueryInterface(mStatementRowHolder);
428 : nsCOMPtr<mozIStorageStatementRow> iRow =
429 5270 : do_QueryWrappedNative(wrapper);
430 2635 : StatementRow *row = static_cast<StatementRow *>(iRow.get());
431 2635 : row->mStatement = nsnull;
432 2635 : mStatementRowHolder = nsnull;
433 : }
434 :
435 36762 : return convertResultCode(srv);
436 : }
437 :
438 : NS_IMETHODIMP
439 99527 : Statement::GetParameterCount(PRUint32 *_parameterCount)
440 : {
441 99527 : if (!mDBStatement)
442 0 : return NS_ERROR_NOT_INITIALIZED;
443 :
444 99527 : *_parameterCount = mParamCount;
445 99527 : return NS_OK;
446 : }
447 :
448 : NS_IMETHODIMP
449 16 : Statement::GetParameterName(PRUint32 aParamIndex,
450 : nsACString &_name)
451 : {
452 16 : if (!mDBStatement)
453 0 : return NS_ERROR_NOT_INITIALIZED;
454 16 : ENSURE_INDEX_VALUE(aParamIndex, mParamCount);
455 :
456 : const char *name = ::sqlite3_bind_parameter_name(mDBStatement,
457 16 : aParamIndex + 1);
458 16 : if (name == NULL) {
459 : // this thing had no name, so fake one
460 0 : nsCAutoString name(":");
461 0 : name.AppendInt(aParamIndex);
462 0 : _name.Assign(name);
463 : }
464 : else {
465 16 : _name.Assign(nsDependentCString(name));
466 : }
467 :
468 16 : return NS_OK;
469 : }
470 :
471 : NS_IMETHODIMP
472 264239 : Statement::GetParameterIndex(const nsACString &aName,
473 : PRUint32 *_index)
474 : {
475 264239 : if (!mDBStatement)
476 0 : return NS_ERROR_NOT_INITIALIZED;
477 :
478 : // We do not accept any forms of names other than ":name", but we need to add
479 : // the colon for SQLite.
480 528478 : nsCAutoString name(":");
481 264239 : name.Append(aName);
482 264239 : int ind = ::sqlite3_bind_parameter_index(mDBStatement, name.get());
483 264239 : if (ind == 0) // Named parameter not found.
484 29 : return NS_ERROR_INVALID_ARG;
485 :
486 264210 : *_index = ind - 1; // SQLite indexes are 1-based, we are 0-based.
487 :
488 264210 : return NS_OK;
489 : }
490 :
491 : NS_IMETHODIMP
492 68 : Statement::GetColumnCount(PRUint32 *_columnCount)
493 : {
494 68 : if (!mDBStatement)
495 0 : return NS_ERROR_NOT_INITIALIZED;
496 :
497 68 : *_columnCount = mResultColumnCount;
498 68 : return NS_OK;
499 : }
500 :
501 : NS_IMETHODIMP
502 4 : Statement::GetColumnName(PRUint32 aColumnIndex,
503 : nsACString &_name)
504 : {
505 4 : if (!mDBStatement)
506 0 : return NS_ERROR_NOT_INITIALIZED;
507 4 : ENSURE_INDEX_VALUE(aColumnIndex, mResultColumnCount);
508 :
509 4 : const char *cname = ::sqlite3_column_name(mDBStatement, aColumnIndex);
510 4 : _name.Assign(nsDependentCString(cname));
511 :
512 4 : return NS_OK;
513 : }
514 :
515 : NS_IMETHODIMP
516 85158 : Statement::GetColumnIndex(const nsACString &aName,
517 : PRUint32 *_index)
518 : {
519 85158 : if (!mDBStatement)
520 0 : return NS_ERROR_NOT_INITIALIZED;
521 :
522 : // Surprisingly enough, SQLite doesn't provide an API for this. We have to
523 : // determine it ourselves sadly.
524 1038019 : for (PRUint32 i = 0; i < mResultColumnCount; i++) {
525 1038014 : if (mColumnNames[i].Equals(aName)) {
526 85153 : *_index = i;
527 85153 : return NS_OK;
528 : }
529 : }
530 :
531 5 : return NS_ERROR_INVALID_ARG;
532 : }
533 :
534 : NS_IMETHODIMP
535 124402 : Statement::Reset()
536 : {
537 124402 : if (!mDBStatement)
538 0 : return NS_ERROR_NOT_INITIALIZED;
539 :
540 : #ifdef DEBUG
541 124402 : PR_LOG(gStorageLog, PR_LOG_DEBUG, ("Resetting statement: '%s'",
542 : ::sqlite3_sql(mDBStatement)));
543 :
544 124402 : checkAndLogStatementPerformance(mDBStatement);
545 : #endif
546 :
547 124402 : mParamsArray = nsnull;
548 124402 : (void)sqlite3_reset(mDBStatement);
549 124402 : (void)sqlite3_clear_bindings(mDBStatement);
550 :
551 124402 : mExecuting = false;
552 :
553 124402 : return NS_OK;
554 : }
555 :
556 : NS_IMETHODIMP
557 12 : Statement::BindParameters(mozIStorageBindingParamsArray *aParameters)
558 : {
559 12 : if (!mDBStatement)
560 0 : return NS_ERROR_NOT_INITIALIZED;
561 :
562 12 : BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
563 12 : if (array->getOwner() != this)
564 1 : return NS_ERROR_UNEXPECTED;
565 :
566 11 : if (array->length() == 0)
567 1 : return NS_ERROR_UNEXPECTED;
568 :
569 10 : mParamsArray = array;
570 10 : mParamsArray->lock();
571 :
572 10 : return NS_OK;
573 : }
574 :
575 : NS_IMETHODIMP
576 49508 : Statement::Execute()
577 : {
578 49508 : if (!mDBStatement)
579 0 : return NS_ERROR_NOT_INITIALIZED;
580 :
581 : bool ret;
582 49508 : nsresult rv = ExecuteStep(&ret);
583 49508 : nsresult rv2 = Reset();
584 :
585 49508 : return NS_FAILED(rv) ? rv : rv2;
586 : }
587 :
588 : NS_IMETHODIMP
589 137514 : Statement::ExecuteStep(bool *_moreResults)
590 : {
591 275028 : SAMPLE_LABEL("storage", "Statement::ExecuteStep");
592 137514 : if (!mDBStatement)
593 0 : return NS_ERROR_NOT_INITIALIZED;
594 :
595 : NS_TIME_FUNCTION_MIN_FMT(5, "mozIStorageStatement::ExecuteStep(%s) (0x%p)",
596 : mDBConnection->getFilename().get(), mDBStatement);
597 :
598 : // Bind any parameters first before executing.
599 137514 : if (mParamsArray) {
600 : // If we have more than one row of parameters to bind, they shouldn't be
601 : // calling this method (and instead use executeAsync).
602 72623 : if (mParamsArray->length() != 1)
603 0 : return NS_ERROR_UNEXPECTED;
604 :
605 72623 : BindingParamsArray::iterator row = mParamsArray->begin();
606 : nsCOMPtr<IStorageBindingParamsInternal> bindingInternal =
607 145246 : do_QueryInterface(*row);
608 145246 : nsCOMPtr<mozIStorageError> error = bindingInternal->bind(mDBStatement);
609 72623 : if (error) {
610 : PRInt32 srv;
611 0 : (void)error->GetResult(&srv);
612 0 : return convertResultCode(srv);
613 : }
614 :
615 : // We have bound, so now we can clear our array.
616 145246 : mParamsArray = nsnull;
617 : }
618 137514 : int srv = mDBConnection->stepStatement(mDBStatement);
619 :
620 : #ifdef PR_LOGGING
621 137514 : if (srv != SQLITE_ROW && srv != SQLITE_DONE) {
622 100 : nsCAutoString errStr;
623 50 : (void)mDBConnection->GetLastErrorString(errStr);
624 50 : PR_LOG(gStorageLog, PR_LOG_DEBUG,
625 : ("Statement::ExecuteStep error: %s", errStr.get()));
626 : }
627 : #endif
628 :
629 : // SQLITE_ROW and SQLITE_DONE are non-errors
630 137514 : if (srv == SQLITE_ROW) {
631 : // we got a row back
632 66236 : mExecuting = true;
633 66236 : *_moreResults = true;
634 66236 : return NS_OK;
635 : }
636 71278 : else if (srv == SQLITE_DONE) {
637 : // statement is done (no row returned)
638 71228 : mExecuting = false;
639 71228 : *_moreResults = false;
640 71228 : return NS_OK;
641 : }
642 50 : else if (srv == SQLITE_BUSY || srv == SQLITE_MISUSE) {
643 0 : mExecuting = false;
644 : }
645 50 : else if (mExecuting) {
646 : #ifdef PR_LOGGING
647 2 : PR_LOG(gStorageLog, PR_LOG_ERROR,
648 : ("SQLite error after mExecuting was true!"));
649 : #endif
650 2 : mExecuting = false;
651 : }
652 :
653 50 : return convertResultCode(srv);
654 : }
655 :
656 : NS_IMETHODIMP
657 94689 : Statement::GetState(PRInt32 *_state)
658 : {
659 94689 : if (!mDBStatement)
660 3 : *_state = MOZ_STORAGE_STATEMENT_INVALID;
661 94686 : else if (mExecuting)
662 21543 : *_state = MOZ_STORAGE_STATEMENT_EXECUTING;
663 : else
664 73143 : *_state = MOZ_STORAGE_STATEMENT_READY;
665 :
666 94689 : return NS_OK;
667 : }
668 :
669 : NS_IMETHODIMP
670 3 : Statement::GetColumnDecltype(PRUint32 aParamIndex,
671 : nsACString &_declType)
672 : {
673 3 : if (!mDBStatement)
674 0 : return NS_ERROR_NOT_INITIALIZED;
675 :
676 3 : ENSURE_INDEX_VALUE(aParamIndex, mResultColumnCount);
677 :
678 2 : _declType.Assign(::sqlite3_column_decltype(mDBStatement, aParamIndex));
679 2 : return NS_OK;
680 : }
681 :
682 : ////////////////////////////////////////////////////////////////////////////////
683 : //// mozIStorageValueArray (now part of mozIStorageStatement too)
684 :
685 : NS_IMETHODIMP
686 12 : Statement::GetNumEntries(PRUint32 *_length)
687 : {
688 12 : *_length = mResultColumnCount;
689 12 : return NS_OK;
690 : }
691 :
692 : NS_IMETHODIMP
693 169629 : Statement::GetTypeOfIndex(PRUint32 aIndex,
694 : PRInt32 *_type)
695 : {
696 169629 : if (!mDBStatement)
697 0 : return NS_ERROR_NOT_INITIALIZED;
698 :
699 169629 : ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
700 :
701 169629 : if (!mExecuting)
702 0 : return NS_ERROR_UNEXPECTED;
703 :
704 169629 : int t = ::sqlite3_column_type(mDBStatement, aIndex);
705 169629 : switch (t) {
706 : case SQLITE_INTEGER:
707 30945 : *_type = mozIStorageStatement::VALUE_TYPE_INTEGER;
708 30945 : break;
709 : case SQLITE_FLOAT:
710 407 : *_type = mozIStorageStatement::VALUE_TYPE_FLOAT;
711 407 : break;
712 : case SQLITE_TEXT:
713 117364 : *_type = mozIStorageStatement::VALUE_TYPE_TEXT;
714 117364 : break;
715 : case SQLITE_BLOB:
716 2005 : *_type = mozIStorageStatement::VALUE_TYPE_BLOB;
717 2005 : break;
718 : case SQLITE_NULL:
719 18908 : *_type = mozIStorageStatement::VALUE_TYPE_NULL;
720 18908 : break;
721 : default:
722 0 : return NS_ERROR_FAILURE;
723 : }
724 :
725 169629 : return NS_OK;
726 : }
727 :
728 : NS_IMETHODIMP
729 86446 : Statement::GetInt32(PRUint32 aIndex,
730 : PRInt32 *_value)
731 : {
732 86446 : if (!mDBStatement)
733 0 : return NS_ERROR_NOT_INITIALIZED;
734 :
735 86446 : ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
736 :
737 86446 : if (!mExecuting)
738 0 : return NS_ERROR_UNEXPECTED;
739 :
740 86446 : *_value = ::sqlite3_column_int(mDBStatement, aIndex);
741 86446 : return NS_OK;
742 : }
743 :
744 : NS_IMETHODIMP
745 83490 : Statement::GetInt64(PRUint32 aIndex,
746 : PRInt64 *_value)
747 : {
748 83490 : if (!mDBStatement)
749 0 : return NS_ERROR_NOT_INITIALIZED;
750 :
751 83490 : ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
752 :
753 83490 : if (!mExecuting)
754 0 : return NS_ERROR_UNEXPECTED;
755 :
756 83490 : *_value = ::sqlite3_column_int64(mDBStatement, aIndex);
757 :
758 83490 : return NS_OK;
759 : }
760 :
761 : NS_IMETHODIMP
762 29776 : Statement::GetDouble(PRUint32 aIndex,
763 : double *_value)
764 : {
765 29776 : if (!mDBStatement)
766 0 : return NS_ERROR_NOT_INITIALIZED;
767 :
768 29776 : ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
769 :
770 29776 : if (!mExecuting)
771 0 : return NS_ERROR_UNEXPECTED;
772 :
773 29776 : *_value = ::sqlite3_column_double(mDBStatement, aIndex);
774 :
775 29776 : return NS_OK;
776 : }
777 :
778 : NS_IMETHODIMP
779 93697 : Statement::GetUTF8String(PRUint32 aIndex,
780 : nsACString &_value)
781 : {
782 : // Get type of Index will check aIndex for us, so we don't have to.
783 : PRInt32 type;
784 93697 : nsresult rv = GetTypeOfIndex(aIndex, &type);
785 93697 : NS_ENSURE_SUCCESS(rv, rv);
786 93697 : if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
787 : // NULL columns should have IsVoid set to distinguish them from the empty
788 : // string.
789 4162 : _value.Truncate(0);
790 4162 : _value.SetIsVoid(true);
791 : }
792 : else {
793 : const char *value =
794 : reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement,
795 89535 : aIndex));
796 89535 : _value.Assign(value, ::sqlite3_column_bytes(mDBStatement, aIndex));
797 : }
798 93697 : return NS_OK;
799 : }
800 :
801 : NS_IMETHODIMP
802 4292 : Statement::GetString(PRUint32 aIndex,
803 : nsAString &_value)
804 : {
805 : // Get type of Index will check aIndex for us, so we don't have to.
806 : PRInt32 type;
807 4292 : nsresult rv = GetTypeOfIndex(aIndex, &type);
808 4292 : NS_ENSURE_SUCCESS(rv, rv);
809 4292 : if (type == mozIStorageStatement::VALUE_TYPE_NULL) {
810 : // NULL columns should have IsVoid set to distinguish them from the empty
811 : // string.
812 2542 : _value.Truncate(0);
813 2542 : _value.SetIsVoid(true);
814 : } else {
815 : const PRUnichar *value =
816 : static_cast<const PRUnichar *>(::sqlite3_column_text16(mDBStatement,
817 1750 : aIndex));
818 1750 : _value.Assign(value, ::sqlite3_column_bytes16(mDBStatement, aIndex) / 2);
819 : }
820 4292 : return NS_OK;
821 : }
822 :
823 : NS_IMETHODIMP
824 2570 : Statement::GetBlob(PRUint32 aIndex,
825 : PRUint32 *_size,
826 : PRUint8 **_blob)
827 : {
828 2570 : if (!mDBStatement)
829 0 : return NS_ERROR_NOT_INITIALIZED;
830 :
831 2570 : ENSURE_INDEX_VALUE(aIndex, mResultColumnCount);
832 :
833 2570 : if (!mExecuting)
834 0 : return NS_ERROR_UNEXPECTED;
835 :
836 2570 : int size = ::sqlite3_column_bytes(mDBStatement, aIndex);
837 2570 : void *blob = nsnull;
838 2570 : if (size) {
839 2569 : blob = nsMemory::Clone(::sqlite3_column_blob(mDBStatement, aIndex), size);
840 2569 : NS_ENSURE_TRUE(blob, NS_ERROR_OUT_OF_MEMORY);
841 : }
842 :
843 2570 : *_blob = static_cast<PRUint8 *>(blob);
844 2570 : *_size = size;
845 2570 : return NS_OK;
846 : }
847 :
848 : NS_IMETHODIMP
849 2 : Statement::GetSharedUTF8String(PRUint32 aIndex,
850 : PRUint32 *_length,
851 : const char **_value)
852 : {
853 2 : if (_length)
854 2 : *_length = ::sqlite3_column_bytes(mDBStatement, aIndex);
855 :
856 : *_value = reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement,
857 2 : aIndex));
858 2 : return NS_OK;
859 : }
860 :
861 : NS_IMETHODIMP
862 24639 : Statement::GetSharedString(PRUint32 aIndex,
863 : PRUint32 *_length,
864 : const PRUnichar **_value)
865 : {
866 24639 : if (_length)
867 24639 : *_length = ::sqlite3_column_bytes16(mDBStatement, aIndex);
868 :
869 : *_value = static_cast<const PRUnichar *>(::sqlite3_column_text16(mDBStatement,
870 24639 : aIndex));
871 24639 : return NS_OK;
872 : }
873 :
874 : NS_IMETHODIMP
875 3093 : Statement::GetSharedBlob(PRUint32 aIndex,
876 : PRUint32 *_size,
877 : const PRUint8 **_blob)
878 : {
879 3093 : *_size = ::sqlite3_column_bytes(mDBStatement, aIndex);
880 : *_blob = static_cast<const PRUint8 *>(::sqlite3_column_blob(mDBStatement,
881 3093 : aIndex));
882 3093 : return NS_OK;
883 : }
884 :
885 : NS_IMETHODIMP
886 5286 : Statement::GetIsNull(PRUint32 aIndex,
887 : bool *_isNull)
888 : {
889 : // Get type of Index will check aIndex for us, so we don't have to.
890 : PRInt32 type;
891 5286 : nsresult rv = GetTypeOfIndex(aIndex, &type);
892 5286 : NS_ENSURE_SUCCESS(rv, rv);
893 5286 : *_isNull = (type == mozIStorageStatement::VALUE_TYPE_NULL);
894 5286 : return NS_OK;
895 : }
896 :
897 : ////////////////////////////////////////////////////////////////////////////////
898 : //// mozIStorageBindingParams
899 :
900 237392 : BOILERPLATE_BIND_PROXIES(
901 : Statement,
902 : if (!mDBStatement) return NS_ERROR_NOT_INITIALIZED;
903 : )
904 :
905 : } // namespace storage
906 4392 : } // namespace mozilla
|