Line data Source code
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 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "sqlite3.h"
8 :
9 : #include "jsfriendapi.h"
10 :
11 : #include "nsPrintfCString.h"
12 : #include "nsString.h"
13 : #include "nsError.h"
14 : #include "mozilla/Mutex.h"
15 : #include "mozilla/CondVar.h"
16 : #include "nsQueryObject.h"
17 : #include "nsThreadUtils.h"
18 : #include "nsJSUtils.h"
19 :
20 : #include "Variant.h"
21 : #include "mozStoragePrivateHelpers.h"
22 : #include "mozIStorageStatement.h"
23 : #include "mozIStorageCompletionCallback.h"
24 : #include "mozIStorageBindingParams.h"
25 :
26 : #include "mozilla/Logging.h"
27 : extern mozilla::LazyLogModule gStorageLog;
28 :
29 : namespace mozilla {
30 : namespace storage {
31 :
32 : nsresult
33 74 : convertResultCode(int aSQLiteResultCode)
34 : {
35 : // Drop off the extended result bits of the result code.
36 74 : int rc = aSQLiteResultCode & 0xFF;
37 :
38 74 : switch (rc) {
39 : case SQLITE_OK:
40 : case SQLITE_ROW:
41 : case SQLITE_DONE:
42 74 : return NS_OK;
43 : case SQLITE_CORRUPT:
44 : case SQLITE_NOTADB:
45 0 : return NS_ERROR_FILE_CORRUPTED;
46 : case SQLITE_PERM:
47 : case SQLITE_CANTOPEN:
48 0 : return NS_ERROR_FILE_ACCESS_DENIED;
49 : case SQLITE_BUSY:
50 0 : return NS_ERROR_STORAGE_BUSY;
51 : case SQLITE_LOCKED:
52 0 : return NS_ERROR_FILE_IS_LOCKED;
53 : case SQLITE_READONLY:
54 0 : return NS_ERROR_FILE_READ_ONLY;
55 : case SQLITE_IOERR:
56 0 : return NS_ERROR_STORAGE_IOERR;
57 : case SQLITE_FULL:
58 : case SQLITE_TOOBIG:
59 0 : return NS_ERROR_FILE_NO_DEVICE_SPACE;
60 : case SQLITE_NOMEM:
61 0 : return NS_ERROR_OUT_OF_MEMORY;
62 : case SQLITE_MISUSE:
63 0 : return NS_ERROR_UNEXPECTED;
64 : case SQLITE_ABORT:
65 : case SQLITE_INTERRUPT:
66 0 : return NS_ERROR_ABORT;
67 : case SQLITE_CONSTRAINT:
68 0 : return NS_ERROR_STORAGE_CONSTRAINT;
69 : }
70 :
71 : // generic error
72 : #ifdef DEBUG
73 0 : nsAutoCString message;
74 0 : message.AppendLiteral("SQLite returned error code ");
75 0 : message.AppendInt(rc);
76 0 : message.AppendLiteral(" , Storage will convert it to NS_ERROR_FAILURE");
77 0 : NS_WARNING_ASSERTION(rc == SQLITE_ERROR, message.get());
78 : #endif
79 0 : return NS_ERROR_FAILURE;
80 : }
81 :
82 : void
83 17 : checkAndLogStatementPerformance(sqlite3_stmt *aStatement)
84 : {
85 : // Check to see if the query performed sorting operations or not. If it
86 : // did, it may need to be optimized!
87 17 : int count = ::sqlite3_stmt_status(aStatement, SQLITE_STMTSTATUS_SORT, 1);
88 17 : if (count <= 0)
89 33 : return;
90 :
91 1 : const char *sql = ::sqlite3_sql(aStatement);
92 :
93 : // Check to see if this is marked to not warn
94 1 : if (::strstr(sql, "/* do not warn (bug "))
95 1 : return;
96 :
97 : // CREATE INDEX always sorts (sorting is a necessary step in creating
98 : // an index). So ignore the warning there.
99 0 : if (::strstr(sql, "CREATE INDEX") || ::strstr(sql, "CREATE UNIQUE INDEX"))
100 0 : return;
101 :
102 0 : nsAutoCString message("Suboptimal indexes for the SQL statement ");
103 : #ifdef MOZ_STORAGE_SORTWARNING_SQL_DUMP
104 : message.Append('`');
105 : message.Append(sql);
106 : message.AppendLiteral("` [");
107 : message.AppendInt(count);
108 : message.AppendLiteral(" sort operation(s)]");
109 : #else
110 0 : nsPrintfCString address("0x%p", aStatement);
111 0 : message.Append(address);
112 : #endif
113 0 : message.AppendLiteral(" (http://mzl.la/1FuID0j).");
114 0 : NS_WARNING(message.get());
115 : }
116 :
117 : nsIVariant *
118 6 : convertJSValToVariant(
119 : JSContext *aCtx,
120 : const JS::Value& aValue)
121 : {
122 6 : if (aValue.isInt32())
123 0 : return new IntegerVariant(aValue.toInt32());
124 :
125 6 : if (aValue.isDouble())
126 0 : return new FloatVariant(aValue.toDouble());
127 :
128 6 : if (aValue.isString()) {
129 10 : nsAutoJSString value;
130 5 : if (!value.init(aCtx, aValue.toString()))
131 0 : return nullptr;
132 5 : return new TextVariant(value);
133 : }
134 :
135 1 : if (aValue.isBoolean())
136 2 : return new IntegerVariant(aValue.isTrue() ? 1 : 0);
137 :
138 0 : if (aValue.isNull())
139 0 : return new NullVariant();
140 :
141 0 : if (aValue.isObject()) {
142 0 : JS::Rooted<JSObject*> obj(aCtx, &aValue.toObject());
143 : // We only support Date instances, all others fail.
144 : bool valid;
145 0 : if (!js::DateIsValid(aCtx, obj, &valid) || !valid)
146 0 : return nullptr;
147 :
148 : double msecd;
149 0 : if (!js::DateGetMsecSinceEpoch(aCtx, obj, &msecd))
150 0 : return nullptr;
151 :
152 0 : msecd *= 1000.0;
153 0 : int64_t msec = msecd;
154 :
155 0 : return new IntegerVariant(msec);
156 : }
157 :
158 0 : return nullptr;
159 : }
160 :
161 : Variant_base *
162 39 : convertVariantToStorageVariant(nsIVariant* aVariant)
163 : {
164 78 : RefPtr<Variant_base> variant = do_QueryObject(aVariant);
165 39 : if (variant) {
166 : // JS helpers already convert the JS representation to a Storage Variant,
167 : // in such a case there's nothing left to do here, so just pass-through.
168 0 : return variant;
169 : }
170 :
171 39 : if (!aVariant)
172 0 : return new NullVariant();
173 :
174 : uint16_t dataType;
175 39 : nsresult rv = aVariant->GetDataType(&dataType);
176 39 : NS_ENSURE_SUCCESS(rv, nullptr);
177 :
178 39 : switch (dataType) {
179 : case nsIDataType::VTYPE_BOOL:
180 : case nsIDataType::VTYPE_INT8:
181 : case nsIDataType::VTYPE_INT16:
182 : case nsIDataType::VTYPE_INT32:
183 : case nsIDataType::VTYPE_UINT8:
184 : case nsIDataType::VTYPE_UINT16:
185 : case nsIDataType::VTYPE_UINT32:
186 : case nsIDataType::VTYPE_INT64:
187 : case nsIDataType::VTYPE_UINT64: {
188 : int64_t v;
189 17 : rv = aVariant->GetAsInt64(&v);
190 17 : NS_ENSURE_SUCCESS(rv, nullptr);
191 17 : return new IntegerVariant(v);
192 : }
193 : case nsIDataType::VTYPE_FLOAT:
194 : case nsIDataType::VTYPE_DOUBLE: {
195 : double v;
196 0 : rv = aVariant->GetAsDouble(&v);
197 0 : NS_ENSURE_SUCCESS(rv, nullptr);
198 0 : return new FloatVariant(v);
199 : }
200 : case nsIDataType::VTYPE_CHAR:
201 : case nsIDataType::VTYPE_CHAR_STR:
202 : case nsIDataType::VTYPE_STRING_SIZE_IS:
203 : case nsIDataType::VTYPE_UTF8STRING:
204 : case nsIDataType::VTYPE_CSTRING: {
205 26 : nsCString v;
206 13 : rv = aVariant->GetAsAUTF8String(v);
207 13 : NS_ENSURE_SUCCESS(rv, nullptr);
208 13 : return new UTF8TextVariant(v);
209 : }
210 : case nsIDataType::VTYPE_WCHAR:
211 : case nsIDataType::VTYPE_DOMSTRING:
212 : case nsIDataType::VTYPE_WCHAR_STR:
213 : case nsIDataType::VTYPE_WSTRING_SIZE_IS:
214 : case nsIDataType::VTYPE_ASTRING: {
215 16 : nsString v;
216 8 : rv = aVariant->GetAsAString(v);
217 8 : NS_ENSURE_SUCCESS(rv, nullptr);
218 8 : return new TextVariant(v);
219 : }
220 : case nsIDataType::VTYPE_ARRAY: {
221 : uint16_t type;
222 : nsIID iid;
223 : uint32_t len;
224 : void *rawArray;
225 : // Note this copies the array data.
226 0 : rv = aVariant->GetAsArray(&type, &iid, &len, &rawArray);
227 0 : NS_ENSURE_SUCCESS(rv, nullptr);
228 0 : if (type == nsIDataType::VTYPE_UINT8) {
229 0 : std::pair<uint8_t *, int> v(static_cast<uint8_t *>(rawArray), len);
230 : // Take ownership of the data avoiding a further copy.
231 0 : return new AdoptedBlobVariant(v);
232 : }
233 : MOZ_FALLTHROUGH;
234 : }
235 : case nsIDataType::VTYPE_EMPTY:
236 : case nsIDataType::VTYPE_EMPTY_ARRAY:
237 : case nsIDataType::VTYPE_VOID:
238 1 : return new NullVariant();
239 : case nsIDataType::VTYPE_ID:
240 : case nsIDataType::VTYPE_INTERFACE:
241 : case nsIDataType::VTYPE_INTERFACE_IS:
242 : default:
243 0 : NS_WARNING("Unsupported variant type");
244 0 : return nullptr;
245 : }
246 :
247 : return nullptr;
248 : }
249 :
250 : namespace {
251 0 : class CallbackEvent : public Runnable
252 : {
253 : public:
254 0 : explicit CallbackEvent(mozIStorageCompletionCallback* aCallback)
255 0 : : Runnable("storage::CallbackEvent")
256 0 : , mCallback(aCallback)
257 : {
258 0 : }
259 :
260 0 : NS_IMETHOD Run() override
261 : {
262 0 : (void)mCallback->Complete(NS_OK, nullptr);
263 0 : return NS_OK;
264 : }
265 : private:
266 : nsCOMPtr<mozIStorageCompletionCallback> mCallback;
267 : };
268 : } // namespace
269 : already_AddRefed<nsIRunnable>
270 0 : newCompletionEvent(mozIStorageCompletionCallback *aCallback)
271 : {
272 0 : NS_ASSERTION(aCallback, "Passing a null callback is a no-no!");
273 0 : nsCOMPtr<nsIRunnable> event = new CallbackEvent(aCallback);
274 0 : return event.forget();
275 : }
276 :
277 : } // namespace storage
278 : } // namespace mozilla
|