summaryrefslogtreecommitdiffstats
path: root/storage/mozStorageStatementData.h
blob: 1008496671c19401ce41e729c118902167339218 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: sw=2 ts=2 sts=2 et
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozStorageStatementData_h
#define mozStorageStatementData_h

#include "sqlite3.h"

#include "nsTArray.h"
#include "MainThreadUtils.h"

#include "mozStorageBindingParamsArray.h"
#include "mozStorageConnection.h"
#include "StorageBaseStatementInternal.h"
#include "mozStoragePrivateHelpers.h"

struct sqlite3_stmt;

namespace mozilla {
namespace storage {

class StatementData {
 public:
  StatementData(sqlite3_stmt* aStatement,
                already_AddRefed<BindingParamsArray> aParamsArray,
                StorageBaseStatementInternal* aStatementOwner)
      : mStatement(aStatement),
        mParamsArray(aParamsArray),
        mQueryStatusRecorded(false),
        mStatementOwner(aStatementOwner) {
    MOZ_ASSERT(mStatementOwner, "Must have a statement owner!");
  }
  StatementData(const StatementData& aSource)
      : mStatement(aSource.mStatement),
        mParamsArray(aSource.mParamsArray),
        mQueryStatusRecorded(false),
        mStatementOwner(aSource.mStatementOwner) {
    MOZ_ASSERT(mStatementOwner, "Must have a statement owner!");
  }
  StatementData() : mStatement(nullptr), mQueryStatusRecorded(false) {}
  ~StatementData() {
    // We need to ensure that mParamsArray is released on the main thread,
    // as the binding arguments may be XPConnect values, which are safe
    // to release only on the main thread.
    NS_ReleaseOnMainThread("StatementData::mParamsArray",
                           mParamsArray.forget());
  }

  /**
   * Return the sqlite statement, fetching it from the storage statement.  In
   * the case of AsyncStatements this may actually create the statement
   */
  inline int getSqliteStatement(sqlite3_stmt** _stmt) {
    if (!mStatement) {
      int rc = mStatementOwner->getAsyncStatement(&mStatement);
      MaybeRecordQueryStatus(rc);
      NS_ENSURE_TRUE(rc == SQLITE_OK, rc);
    }
    *_stmt = mStatement;
    return SQLITE_OK;
  }

  operator BindingParamsArray*() const { return mParamsArray; }

  /**
   * NULLs out our sqlite3_stmt (it is held by the owner) after reseting it and
   * clear all bindings to it.  This is expected to occur on the async thread.
   */
  inline void reset() {
    MOZ_ASSERT(mStatementOwner, "Must have a statement owner!");
    // In the AsyncStatement case we may never have populated mStatement if the
    // AsyncExecuteStatements got canceled or a failure occurred in constructing
    // the statement.
    if (mStatement) {
      (void)::sqlite3_reset(mStatement);
      (void)::sqlite3_clear_bindings(mStatement);
      mStatement = nullptr;

      if (!mQueryStatusRecorded) {
        mStatementOwner->getOwner()->RecordQueryStatus(SQLITE_OK);
      }
    }
  }

  /**
   * Indicates if this statement has parameters to be bound before it is
   * executed.
   *
   * @return true if the statement has parameters to bind against, false
   *         otherwise.
   */
  inline bool hasParametersToBeBound() const { return !!mParamsArray; }
  /**
   * Indicates the number of implicit statements generated by this statement
   * requiring a transaction for execution.  For example a single statement
   * with N BindingParams will execute N implicit staments.
   *
   * @return number of statements requiring a transaction for execution.
   *
   * @note In the case of AsyncStatements this may actually create the
   *       statement.
   */
  inline uint32_t needsTransaction() {
    MOZ_ASSERT(!NS_IsMainThread());
    // Be sure to use the getSqliteStatement helper, since sqlite3_stmt_readonly
    // can only analyze prepared statements and AsyncStatements are prepared
    // lazily.
    sqlite3_stmt* stmt;
    int rc = getSqliteStatement(&stmt);
    if (SQLITE_OK != rc || ::sqlite3_stmt_readonly(stmt)) {
      return 0;
    }
    return mParamsArray ? mParamsArray->length() : 1;
  }

  void MaybeRecordQueryStatus(int srv) {
    if (mQueryStatusRecorded || !isErrorCode(srv)) {
      return;
    }

    mStatementOwner->getOwner()->RecordQueryStatus(srv);
    mQueryStatusRecorded = true;
  }

 private:
  sqlite3_stmt* mStatement;
  RefPtr<BindingParamsArray> mParamsArray;
  bool mQueryStatusRecorded;

  /**
   * We hold onto a reference of the statement's owner so it doesn't get
   * destroyed out from under us.
   */
  nsCOMPtr<StorageBaseStatementInternal> mStatementOwner;
};

}  // namespace storage
}  // namespace mozilla

#endif  // mozStorageStatementData_h