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