aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp')
-rw-r--r--src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp1409
1 files changed, 0 insertions, 1409 deletions
diff --git a/src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp b/src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp
deleted file mode 100644
index 7f740ae66..000000000
--- a/src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp
+++ /dev/null
@@ -1,1409 +0,0 @@
-#include "root.h"
-
-#include "JSSQLStatement.h"
-#include "JavaScriptCore/JSObjectInlines.h"
-#include "wtf/text/ExternalStringImpl.h"
-
-#include "JavaScriptCore/FunctionPrototype.h"
-#include "JavaScriptCore/HeapAnalyzer.h"
-
-#include "JavaScriptCore/JSDestructibleObjectHeapCellType.h"
-#include "JavaScriptCore/SlotVisitorMacros.h"
-#include "JavaScriptCore/ObjectConstructor.h"
-#include "JavaScriptCore/SubspaceInlines.h"
-#include "wtf/GetPtr.h"
-#include "wtf/PointerPreparations.h"
-#include "wtf/URL.h"
-#include "JavaScriptCore/TypedArrayInlines.h"
-#include "JavaScriptCore/PropertyNameArray.h"
-#include "Buffer.h"
-#include "GCDefferalContext.h"
-#include "Buffer.h"
-
-/* ******************************************************************************** */
-// Lazy Load SQLite on macOS
-// This seemed to be about 3% faster on macOS
-// but it might be noise
-// it's kind of hard to tell
-// it should be strictly better though because
-// instead of two pointers, one for DYLD_STUB$$ and one for the actual library
-// we only call one pointer for the actual library
-// and it means there's less work for DYLD to do on startup
-// i.e. it shouldn't have any impact on startup time
-#ifdef LAZY_LOAD_SQLITE
-#include "lazy_sqlite3.h"
-#else
-static inline int lazyLoadSQLite()
-{
- return 0;
-}
-
-#endif
-/* ******************************************************************************** */
-
-static WTF::String sqliteString(const char* str)
-{
- auto res = WTF::String::fromUTF8(str);
- sqlite3_free((void*)str);
- return res;
-}
-
-static void sqlite_free_typed_array(void* ctx, void* buf)
-{
- sqlite3_free((void*)buf);
-}
-
-/** This is like a 3x perf improvement for allocating objects **/
-#define SQL_USE_PROTOTYPE 1
-
-static int DEFAULT_SQLITE_FLAGS
- = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
-static unsigned int DEFAULT_SQLITE_PREPARE_FLAGS = SQLITE_PREPARE_PERSISTENT;
-static int MAX_SQLITE_PREPARE_FLAG = SQLITE_PREPARE_PERSISTENT | SQLITE_PREPARE_NORMALIZE | SQLITE_PREPARE_NO_VTAB;
-
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementPrepareStatementFunction);
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementExecuteFunction);
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementOpenStatementFunction);
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementIsInTransactionFunction);
-
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementLoadExtensionFunction);
-
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunction);
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRun);
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionGet);
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionAll);
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRows);
-
-static JSC_DECLARE_CUSTOM_GETTER(jsSqlStatementGetColumnNames);
-static JSC_DECLARE_CUSTOM_GETTER(jsSqlStatementGetColumnCount);
-
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementSerialize);
-static JSC_DECLARE_HOST_FUNCTION(jsSQLStatementDeserialize);
-
-#define CHECK_THIS \
- if (UNLIKELY(!castedThis)) { \
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQLStatement"_s)); \
- return JSValue::encode(jsUndefined()); \
- }
-
-#define DO_REBIND(param) \
- if (param.isObject()) { \
- JSC::JSValue reb = castedThis->rebind(lexicalGlobalObject, param, true); \
- if (UNLIKELY(!reb.isNumber())) { \
- return JSValue::encode(reb); /* this means an error */ \
- } \
- } else { \
- throwException(lexicalGlobalObject, scope, createTypeError(lexicalGlobalObject, "Expected object or array"_s)); \
- return JSValue::encode(jsUndefined()); \
- }
-
-#define CHECK_PREPARED \
- if (UNLIKELY(castedThis->stmt == nullptr || castedThis->db == nullptr)) { \
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Statement has finalized"_s)); \
- return JSValue::encode(jsUndefined()); \
- }
-
-namespace WebCore {
-using namespace JSC;
-
-class JSSQLStatement : public JSC::JSNonFinalObject {
-public:
- using Base = JSC::JSNonFinalObject;
- static JSSQLStatement* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, sqlite3_stmt* stmt, sqlite3* db)
- {
- JSSQLStatement* ptr = new (NotNull, JSC::allocateCell<JSSQLStatement>(globalObject->vm())) JSSQLStatement(structure, *globalObject, stmt, db);
- ptr->finishCreation(globalObject->vm());
- return ptr;
- }
- static void destroy(JSC::JSCell*);
- template<typename, SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
- {
- return WebCore::subspaceForImpl<JSSQLStatement, UseCustomHeapCellType::No>(
- vm,
- [](auto& spaces) { return spaces.m_clientSubspaceForJSSQLStatement.get(); },
- [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSSQLStatement = WTFMove(space); },
- [](auto& spaces) { return spaces.m_subspaceForJSSQLStatement.get(); },
- [](auto& spaces, auto&& space) { spaces.m_subspaceForJSSQLStatement = WTFMove(space); });
- }
- DECLARE_VISIT_CHILDREN;
- DECLARE_EXPORT_INFO;
-
- // static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
-
- JSC::JSValue rebind(JSGlobalObject* globalObject, JSC::JSValue values, bool clone);
- static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
- {
- return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
- }
-
- ~JSSQLStatement();
-
- sqlite3_stmt* stmt;
- sqlite3* db;
- bool hasExecuted = false;
- PropertyNameArray columnNames;
- mutable WriteBarrier<JSC::JSArray> _columnNames;
- mutable WriteBarrier<JSC::JSObject> _prototype;
-
-protected:
- JSSQLStatement(JSC::Structure* structure, JSDOMGlobalObject& globalObject, sqlite3_stmt* stmt, sqlite3* db)
- : Base(globalObject.vm(), structure)
- , columnNames(globalObject.vm(), PropertyNameMode::Strings, PrivateSymbolMode::Exclude)
- , _columnNames(globalObject.vm(), this, nullptr)
- , _prototype(globalObject.vm(), this, nullptr)
-
- {
- this->stmt = stmt;
- this->db = db;
- }
-
- void finishCreation(JSC::VM&);
-};
-
-void JSSQLStatement::destroy(JSC::JSCell* cell)
-{
- JSSQLStatement* thisObject = static_cast<JSSQLStatement*>(cell);
- sqlite3_finalize(thisObject->stmt);
- thisObject->stmt = nullptr;
-}
-
-void JSSQLStatementConstructor::destroy(JSC::JSCell* cell)
-{
-}
-
-static inline bool rebindValue(JSC::JSGlobalObject* lexicalGlobalObject, sqlite3_stmt* stmt, int i, JSC::JSValue value, JSC::ThrowScope& scope, bool clone)
-{
-#define CHECK_BIND(param) \
- int result = param; \
- if (UNLIKELY(result != SQLITE_OK)) { \
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(result)))); \
- return false; \
- }
- // only clone if necessary
- // SQLite has a way to call a destructor
- // but there doesn't seem to be a way to pass a pointer?
- // we can't use it if there's no pointer to ref/unref
- auto transientOrStatic = (void (*)(void*))(clone ? SQLITE_TRANSIENT : SQLITE_STATIC);
-
- if (value.isUndefinedOrNull()) {
- CHECK_BIND(sqlite3_bind_null(stmt, i));
- } else if (value.isBoolean()) {
- CHECK_BIND(sqlite3_bind_int(stmt, i, value.toBoolean(lexicalGlobalObject) ? 1 : 0));
- } else if (value.isAnyInt()) {
- int64_t val = value.asAnyInt();
- if (val < INT_MIN || val > INT_MAX) {
- CHECK_BIND(sqlite3_bind_int64(stmt, i, val));
- } else {
- CHECK_BIND(sqlite3_bind_int(stmt, i, val))
- }
- } else if (value.isNumber()) {
- CHECK_BIND(sqlite3_bind_double(stmt, i, value.asDouble()))
- } else if (value.isString()) {
- auto* str = value.toStringOrNull(lexicalGlobalObject);
- if (UNLIKELY(!str)) {
- throwException(lexicalGlobalObject, scope, createTypeError(lexicalGlobalObject, "Expected string"_s));
- return false;
- }
-
- auto roped = str->tryGetValue(lexicalGlobalObject);
- if (UNLIKELY(!roped)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Out of memory :("_s));
- return false;
- }
-
- if (roped.is8Bit()) {
- CHECK_BIND(sqlite3_bind_text(stmt, i, reinterpret_cast<const char*>(roped.characters8()), roped.length(), transientOrStatic));
- } else {
- CHECK_BIND(sqlite3_bind_text16(stmt, i, roped.characters16(), roped.length() * 2, transientOrStatic));
- }
-
- } else if (UNLIKELY(value.isHeapBigInt())) {
- CHECK_BIND(sqlite3_bind_int64(stmt, i, JSBigInt::toBigInt64(value)));
- } else if (JSC::JSArrayBufferView* buffer = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(value)) {
- CHECK_BIND(sqlite3_bind_blob(stmt, i, buffer->vector(), buffer->byteLength(), transientOrStatic));
- } else {
- throwException(lexicalGlobalObject, scope, createTypeError(lexicalGlobalObject, "Binding expected string, TypedArray, boolean, number, bigint or null"_s));
- return false;
- }
-
- return true;
-#undef CHECK_BIND
-}
-
-// this function does the equivalent of
-// Object.entries(obj)
-// except without the intermediate array of arrays
-static JSC::JSValue rebindObject(JSC::JSGlobalObject* globalObject, JSC::JSValue targetValue, JSC::ThrowScope& scope, sqlite3_stmt* stmt, bool clone)
-{
- JSObject* target = targetValue.toObject(globalObject);
- RETURN_IF_EXCEPTION(scope, {});
- JSC::VM& vm = globalObject->vm();
- PropertyNameArray properties(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
- target->methodTable()->getOwnPropertyNames(target, globalObject, properties, DontEnumPropertiesMode::Include);
- RETURN_IF_EXCEPTION(scope, {});
- int count = 0;
-
- for (const auto& propertyName : properties) {
- PropertySlot slot(target, PropertySlot::InternalMethodType::GetOwnProperty);
- bool hasProperty = target->methodTable()->getOwnPropertySlot(target, globalObject, propertyName, slot);
- RETURN_IF_EXCEPTION(scope, JSValue());
- if (!hasProperty)
- continue;
- if (slot.attributes() & PropertyAttribute::DontEnum)
- continue;
-
- JSValue value;
- if (LIKELY(!slot.isTaintedByOpaqueObject()))
- value = slot.getValue(globalObject, propertyName);
- else
- value = target->get(globalObject, propertyName);
-
- // Ensure this gets freed on scope clear
- auto utf8 = WTF::String(propertyName.string()).utf8();
-
- int index = sqlite3_bind_parameter_index(stmt, utf8.data());
- if (index == 0) {
- throwException(globalObject, scope, createError(globalObject, "Unknown parameter \"" + propertyName.string() + "\""_s));
- return JSValue();
- }
-
- if (!rebindValue(globalObject, stmt, index, value, scope, clone))
- return JSValue();
- RETURN_IF_EXCEPTION(scope, {});
- count++;
- }
-
- return jsNumber(count);
-}
-
-static JSC::JSValue rebindStatement(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSValue values, JSC::ThrowScope& scope, sqlite3_stmt* stmt, bool clone)
-{
- sqlite3_clear_bindings(stmt);
- JSC::JSArray* array = jsDynamicCast<JSC::JSArray*>(values);
- int max = sqlite3_bind_parameter_count(stmt);
-
- if (!array) {
- if (JSC::JSObject* object = values.getObject()) {
- auto res = rebindObject(lexicalGlobalObject, object, scope, stmt, clone);
- RETURN_IF_EXCEPTION(scope, {});
- return res;
- }
-
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected array"_s));
- return jsUndefined();
- }
-
- int count = array->length();
-
- if (count == 0) {
- return jsNumber(0);
- }
-
- if (count != max) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected " + String::number(max) + " values, got " + String::number(count)));
- return jsUndefined();
- }
-
- int i = 0;
- for (; i < count; i++) {
- JSC::JSValue value = array->getIndexQuickly(i);
- rebindValue(lexicalGlobalObject, stmt, i + 1, value, scope, clone);
- RETURN_IF_EXCEPTION(scope, {});
- }
-
- return jsNumber(i);
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementSetCustomSQLite, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- JSValue thisValue = callFrame->thisValue();
- JSSQLStatementConstructor* thisObject = jsDynamicCast<JSSQLStatementConstructor*>(thisValue.getObject());
- if (UNLIKELY(!thisObject)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQL"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- if (callFrame->argumentCount() < 1) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected 1 argument"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- JSC::JSValue sqliteStrValue = callFrame->argument(0);
- if (UNLIKELY(!sqliteStrValue.isString())) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQLite path"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
-#ifdef LAZY_LOAD_SQLITE
- if (sqlite3_handle) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "SQLite already loaded\nThis function can only be called before SQLite has been loaded and exactly once. SQLite auto-loads when the first time you open a Database."_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- sqlite3_lib_path = sqliteStrValue.toWTFString(lexicalGlobalObject).utf8().data();
- if (lazyLoadSQLite() == -1) {
- sqlite3_handle = nullptr;
- WTF::String msg = WTF::String::fromUTF8(dlerror());
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, msg));
- return JSValue::encode(JSC::jsUndefined());
- }
-#endif
-
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsBoolean(true)));
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementDeserialize, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- JSValue thisValue = callFrame->thisValue();
- JSSQLStatementConstructor* thisObject = jsDynamicCast<JSSQLStatementConstructor*>(thisValue.getObject());
- if (UNLIKELY(!thisObject)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQL"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- if (callFrame->argumentCount() < 1) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected 1 argument"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- JSC::JSArrayBufferView* array = jsDynamicCast<JSC::JSArrayBufferView*>(callFrame->argument(0));
- if (UNLIKELY(!array)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected Uint8Array or Buffer"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- if (UNLIKELY(array->isDetached())) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "TypedArray is detached"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- size_t byteLength = array->byteLength();
- void* ptr = array->vector();
- if (UNLIKELY(ptr == nullptr || byteLength == 0)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "ArrayBuffer must not be empty"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
- void* data = sqlite3_malloc64(byteLength);
- if (UNLIKELY(data == nullptr)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Failed to allocate memory"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
- if (byteLength) {
- memcpy(data, ptr, byteLength);
- }
-
- unsigned int flags = SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_RESIZEABLE;
-
- if (callFrame->argumentCount() > 1 and callFrame->argument(1).toBoolean(lexicalGlobalObject)) {
- flags |= SQLITE_DESERIALIZE_READONLY;
- }
-
- sqlite3* db = nullptr;
- if (sqlite3_open_v2(":memory:", &db, DEFAULT_SQLITE_FLAGS, nullptr) != SQLITE_OK) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Failed to open SQLite"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- int status = sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);
- assert(status == SQLITE_OK);
- status = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, NULL);
- assert(status == SQLITE_OK);
-
- status = sqlite3_deserialize(db, "main", reinterpret_cast<unsigned char*>(data), byteLength, byteLength, flags);
- if (status == SQLITE_BUSY) {
- sqlite3_free(data);
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "SQLITE_BUSY"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- if (status != SQLITE_OK) {
- sqlite3_free(data);
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, status == SQLITE_ERROR ? "unable to deserialize database"_s : sqliteString(sqlite3_errstr(status))));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- auto count = thisObject->databases.size();
- thisObject->databases.append(db);
- RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(count)));
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementSerialize, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- JSValue thisValue = callFrame->thisValue();
- JSSQLStatementConstructor* thisObject = jsDynamicCast<JSSQLStatementConstructor*>(thisValue.getObject());
- if (UNLIKELY(!thisObject)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQL"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- int32_t dbIndex = callFrame->argument(0).toInt32(lexicalGlobalObject);
- if (UNLIKELY(dbIndex < 0 || dbIndex >= thisObject->databases.size())) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Invalid database handle"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- sqlite3* db = thisObject->databases[dbIndex];
- if (UNLIKELY(!db)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Can't do this on a closed database"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- WTF::String attachedName = callFrame->argument(1).toWTFString(lexicalGlobalObject);
- RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
-
- if (attachedName.isEmpty()) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected attached database name"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
- sqlite3_int64 length = -1;
- unsigned char* data = sqlite3_serialize(db, attachedName.utf8().data(), &length, 0);
- if (UNLIKELY(data == nullptr && length)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Out of memory"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- RELEASE_AND_RETURN(scope, JSBuffer__bufferFromPointerAndLengthAndDeinit(lexicalGlobalObject, reinterpret_cast<char*>(data), static_cast<unsigned int>(length), data, sqlite_free_typed_array));
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementLoadExtensionFunction, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- JSValue thisValue = callFrame->thisValue();
- JSSQLStatementConstructor* thisObject = jsDynamicCast<JSSQLStatementConstructor*>(thisValue.getObject());
- if (UNLIKELY(!thisObject)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQL"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- int32_t dbIndex = callFrame->argument(0).toInt32(lexicalGlobalObject);
- if (UNLIKELY(dbIndex < 0 || dbIndex >= thisObject->databases.size())) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Invalid database handle"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- JSC::JSValue extension = callFrame->argument(1);
- if (UNLIKELY(!extension.isString())) {
- throwException(lexicalGlobalObject, scope, createTypeError(lexicalGlobalObject, "Expected string"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- auto extensionString = extension.toWTFString(lexicalGlobalObject);
- RETURN_IF_EXCEPTION(scope, {});
-
- sqlite3* db = thisObject->databases[dbIndex];
- if (UNLIKELY(!db)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Can't do this on a closed database"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- auto entryPointStr = callFrame->argumentCount() > 2 && callFrame->argument(2).isString() ? callFrame->argument(2).toWTFString(lexicalGlobalObject) : String();
- const char* entryPoint = entryPointStr.length() == 0 ? NULL : entryPointStr.utf8().data();
- char* error;
- int rc = sqlite3_load_extension(db, extensionString.utf8().data(), entryPoint, &error);
-
- // TODO: can we disable loading extensions after this?
- if (rc != SQLITE_OK) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, sqliteString(error != nullptr ? error : sqlite3_errmsg(db))));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
-}
-
-// This runs a query one-off
-// without the overhead of a long-lived statement object
-// does not return anything
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteFunction, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- JSValue thisValue = callFrame->thisValue();
- JSSQLStatementConstructor* thisObject = jsDynamicCast<JSSQLStatementConstructor*>(thisValue.getObject());
- if (UNLIKELY(!thisObject)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQL"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- if (callFrame->argumentCount() < 2) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected at least 2 arguments"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- int handle = callFrame->argument(0).toInt32(lexicalGlobalObject);
- if (thisObject->databases.size() < handle) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Invalid database handle"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
- sqlite3* db = thisObject->databases[handle];
-
- if (UNLIKELY(!db)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Database has closed"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- JSC::JSValue sqlValue = callFrame->argument(1);
- if (UNLIKELY(!sqlValue.isString())) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQL string"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- EnsureStillAliveScope bindingsAliveScope = callFrame->argumentCount() > 2 ? callFrame->argument(2) : jsUndefined();
-
- auto sqlString = sqlValue.toWTFString(lexicalGlobalObject);
- if (UNLIKELY(sqlString.length() == 0)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "SQL string mustn't be blank"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- // TODO: trim whitespace & newlines before sending
- // we don't because webkit doesn't expose a function that makes this super
- // easy without using unicode whitespace definition the
- // StringPrototype.trim() implementation GC allocates a new JSString* and
- // potentially re-allocates the string (not 100% sure if reallocates) so we
- // can't use that here
- sqlite3_stmt* statement = nullptr;
-
- int rc = SQLITE_OK;
- if (sqlString.is8Bit()) {
- rc = sqlite3_prepare_v3(db, reinterpret_cast<const char*>(sqlString.characters8()), sqlString.length(), 0, &statement, nullptr);
- } else {
- rc = sqlite3_prepare16_v3(db, sqlString.characters16(), sqlString.length() * 2, 0, &statement, nullptr);
- }
-
- if (rc != SQLITE_OK || statement == nullptr) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errmsg(db))));
- // sqlite3 handles when the pointer is null
- sqlite3_finalize(statement);
- return JSValue::encode(JSC::jsUndefined());
- }
-
- if (!bindingsAliveScope.value().isUndefinedOrNull()) {
- if (bindingsAliveScope.value().isObject()) {
- JSC::JSValue reb = rebindStatement(lexicalGlobalObject, bindingsAliveScope.value(), scope, statement, false);
- if (UNLIKELY(!reb.isNumber())) {
- sqlite3_finalize(statement);
- return JSValue::encode(reb); /* this means an error */
- }
- } else {
- throwException(lexicalGlobalObject, scope, createTypeError(lexicalGlobalObject, "Expected bindings to be an object or array"_s));
- sqlite3_finalize(statement);
- return JSValue::encode(jsUndefined());
- }
- }
-
- rc = sqlite3_step(statement);
-
- // we don't care about the results, therefore the row-by-row output doesn't matter
- // that's why we don't bother to loop through the results
- if (rc != SQLITE_DONE and rc != SQLITE_ROW) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(rc))));
- // we finalize after just incase something about error messages in
- // sqlite depends on the existence of the most recent statement i don't
- // think that's actually how this works - just being cautious
- sqlite3_finalize(statement);
- return JSValue::encode(JSC::jsUndefined());
- }
-
- sqlite3_finalize(statement);
- return JSValue::encode(jsUndefined());
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementIsInTransactionFunction, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- JSValue thisValue = callFrame->thisValue();
- JSSQLStatementConstructor* thisObject = jsDynamicCast<JSSQLStatementConstructor*>(thisValue.getObject());
- if (UNLIKELY(!thisObject)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQLStatement"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- JSC::JSValue dbNumber = callFrame->argument(0);
-
- if (!dbNumber.isNumber()) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Invalid database handle"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- int handle = dbNumber.toInt32(lexicalGlobalObject);
-
- if (handle < 0 || handle > thisObject->databases.size()) {
- throwException(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "Invalid database handle"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- sqlite3* db = thisObject->databases[handle];
-
- if (UNLIKELY(!db)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Database has closed"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(!sqlite3_get_autocommit(db))));
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementPrepareStatementFunction, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- JSValue thisValue = callFrame->thisValue();
- JSSQLStatementConstructor* thisObject = jsDynamicCast<JSSQLStatementConstructor*>(thisValue.getObject());
- if (UNLIKELY(!thisObject)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQLStatement"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- JSC::JSValue dbNumber = callFrame->argument(0);
- JSC::JSValue sqlValue = callFrame->argument(1);
- JSC::JSValue bindings = callFrame->argument(2);
- JSC::JSValue prepareFlagsValue = callFrame->argument(3);
-
- if (!dbNumber.isNumber() || !sqlValue.isString()) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "SQLStatement requires a number and a string"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- int handle = dbNumber.toInt32(lexicalGlobalObject);
- if (handle < 0 || handle > thisObject->databases.size()) {
- throwException(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "Invalid database handle"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- sqlite3* db = thisObject->databases[handle];
- if (!db) {
- throwException(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "Cannot use a closed database"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- auto sqlString = sqlValue.toWTFString(lexicalGlobalObject);
- if (!sqlString.length()) {
- throwException(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "Invalid SQL statement"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- unsigned int flags = DEFAULT_SQLITE_PREPARE_FLAGS;
- if (prepareFlagsValue.isNumber()) {
-
- int prepareFlags = prepareFlagsValue.toInt32(lexicalGlobalObject);
- if (prepareFlags < 0 || prepareFlags > MAX_SQLITE_PREPARE_FLAG) {
- throwException(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "Invalid prepare flags"_s));
- return JSValue::encode(JSC::jsUndefined());
- }
- flags = static_cast<unsigned int>(prepareFlags);
- }
-
- sqlite3_stmt* statement = nullptr;
-
- int rc = SQLITE_OK;
- if (sqlString.is8Bit()) {
- rc = sqlite3_prepare_v3(db, reinterpret_cast<const char*>(sqlString.characters8()), sqlString.length(), flags, &statement, nullptr);
- } else {
- rc = sqlite3_prepare16_v3(db, sqlString.characters16(), sqlString.length() * 2, flags, &statement, nullptr);
- }
-
- if (rc != SQLITE_OK) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errmsg(db))));
- return JSValue::encode(JSC::jsUndefined());
- }
-
- auto* structure = JSSQLStatement::createStructure(vm, lexicalGlobalObject, lexicalGlobalObject->objectPrototype());
- // auto* structure = JSSQLStatement::createStructure(vm, globalObject(), thisObject->getDirect(vm, vm.propertyNames->prototype));
- JSSQLStatement* sqlStatement = JSSQLStatement::create(structure, reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject), statement, db);
- sqlStatement->db = db;
- if (bindings.isObject()) {
- auto* castedThis = sqlStatement;
- DO_REBIND(bindings)
- }
- return JSValue::encode(JSValue(sqlStatement));
-}
-
-JSSQLStatementConstructor* JSSQLStatementConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
-{
- NativeExecutable* executable = vm.getHostFunction(jsSQLStatementPrepareStatementFunction, callHostFunctionAsConstructor, String("SQLStatement"_s));
- JSSQLStatementConstructor* ptr = new (NotNull, JSC::allocateCell<JSSQLStatementConstructor>(vm)) JSSQLStatementConstructor(vm, executable, globalObject, structure);
- ptr->finishCreation(vm);
-
- return ptr;
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementOpenStatementFunction, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- JSValue thisValue = callFrame->thisValue();
- JSSQLStatementConstructor* constructor = jsDynamicCast<JSSQLStatementConstructor*>(thisValue.getObject());
- if (!constructor) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQLStatement"_s));
- return JSValue::encode(jsUndefined());
- }
-
- if (callFrame->argumentCount() < 1) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected 1 argument"_s));
- return JSValue::encode(jsUndefined());
- }
-
- JSValue pathValue = callFrame->argument(0);
- if (!pathValue.isString()) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected string"_s));
- return JSValue::encode(jsUndefined());
- }
-
-#if LAZY_LOAD_SQLITE
- if (UNLIKELY(lazyLoadSQLite() < 0)) {
- WTF::String msg = WTF::String::fromUTF8(dlerror());
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, msg));
- return JSValue::encode(JSC::jsUndefined());
- }
-#endif
-
- auto catchScope = DECLARE_CATCH_SCOPE(vm);
- String path = pathValue.toWTFString(lexicalGlobalObject);
- RETURN_IF_EXCEPTION(catchScope, JSValue::encode(jsUndefined()));
- catchScope.clearException();
- int openFlags = DEFAULT_SQLITE_FLAGS;
- if (callFrame->argumentCount() > 1) {
- JSValue flags = callFrame->argument(1);
- if (!flags.isNumber()) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected number"_s));
- return JSValue::encode(jsUndefined());
- }
-
- openFlags = flags.toInt32(lexicalGlobalObject);
- }
-
- sqlite3* db = nullptr;
- int statusCode = sqlite3_open_v2(path.utf8().data(), &db, openFlags, nullptr);
- if (statusCode != SQLITE_OK) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errmsg(db))));
-
- return JSValue::encode(jsUndefined());
- }
-
- int status = sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);
- assert(status == SQLITE_OK);
- status = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, NULL);
- assert(status == SQLITE_OK);
-
- auto count = constructor->databases.size();
- constructor->databases.append(db);
- RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(count)));
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementCloseStatementFunction, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
-
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- JSValue thisValue = callFrame->thisValue();
- JSSQLStatementConstructor* constructor = jsDynamicCast<JSSQLStatementConstructor*>(thisValue.getObject());
-
- if (!constructor) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected SQLStatement"_s));
- return JSValue::encode(jsUndefined());
- }
-
- if (callFrame->argumentCount() < 1) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected 1 argument"_s));
- return JSValue::encode(jsUndefined());
- }
-
- JSValue dbNumber = callFrame->argument(0);
- if (!dbNumber.isNumber()) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Expected number"_s));
- return JSValue::encode(jsUndefined());
- }
-
- int dbIndex = dbNumber.toInt32(lexicalGlobalObject);
-
- if (dbIndex < 0 || dbIndex >= constructor->databases.size()) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, "Invalid database handle"_s));
- return JSValue::encode(jsUndefined());
- }
-
- sqlite3* db = constructor->databases[dbIndex];
- // no-op if already closed
- if (!db) {
- return JSValue::encode(jsUndefined());
- }
-
- int statusCode = sqlite3_close_v2(db);
- if (statusCode != SQLITE_OK) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errmsg(db))));
- return JSValue::encode(jsUndefined());
- }
-
- constructor->databases[dbIndex] = nullptr;
- return JSValue::encode(jsUndefined());
-}
-
-/* Hash table for constructor */
-static const HashTableValue JSSQLStatementConstructorTableValues[] = {
- { "open"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementOpenStatementFunction), (intptr_t)(2) } },
- { "close"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementCloseStatementFunction), (intptr_t)(1) } },
- { "prepare"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementPrepareStatementFunction), (intptr_t)(2) } },
- { "run"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementExecuteFunction), (intptr_t)(3) } },
- { "isInTransaction"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementIsInTransactionFunction), (intptr_t)(1) } },
- { "loadExtension"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementLoadExtensionFunction), (intptr_t)(2) } },
- { "setCustomSQLite"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementSetCustomSQLite), (intptr_t)(1) } },
- { "serialize"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementSerialize), (intptr_t)(1) } },
- { "deserialize"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementDeserialize), (intptr_t)(2) } },
-};
-
-const ClassInfo JSSQLStatementConstructor::s_info = { "SQLStatement"_s, nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSSQLStatementConstructor) };
-
-void JSSQLStatementConstructor::finishCreation(VM& vm)
-{
- Base::finishCreation(vm);
- auto* structure = JSSQLStatement::createStructure(vm, globalObject(), globalObject()->objectPrototype());
- auto* proto = JSSQLStatement::create(structure, reinterpret_cast<Zig::GlobalObject*>(globalObject()), nullptr, nullptr);
- this->putDirect(vm, vm.propertyNames->prototype, proto, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
-
- reifyStaticProperties(vm, JSSQLStatementConstructor::info(), JSSQLStatementConstructorTableValues, *this);
- JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
-}
-
-static inline JSC::JSValue constructResultObject(JSC::JSGlobalObject* lexicalGlobalObject, JSSQLStatement* castedThis);
-static inline JSC::JSValue constructResultObject(JSC::JSGlobalObject* lexicalGlobalObject, JSSQLStatement* castedThis)
-{
- auto& columnNames = castedThis->columnNames.data()->propertyNameVector();
- int count = columnNames.size();
- auto& vm = lexicalGlobalObject->vm();
-
-#if SQL_USE_PROTOTYPE == 1
- JSC::JSObject* result = JSC::JSFinalObject::create(vm, castedThis->_prototype.get()->structure());
-#else
- JSC::JSObject* result = JSC::JSFinalObject::create(vm, JSC::JSFinalObject::createStructure(vm, lexicalGlobalObject, lexicalGlobalObject->objectPrototype(), count));
-#endif
- auto* stmt = castedThis->stmt;
-
- for (int i = 0; i < count; i++) {
- auto name = columnNames[i];
-
- switch (sqlite3_column_type(stmt, i)) {
- case SQLITE_INTEGER: {
- result->putDirect(vm, name, jsNumber(sqlite3_column_int(stmt, i)), 0);
- break;
- }
- case SQLITE_FLOAT: {
- result->putDirect(vm, name, jsNumber(sqlite3_column_double(stmt, i)), 0);
- break;
- }
- case SQLITE_TEXT: {
- size_t len = sqlite3_column_bytes(stmt, i);
- const unsigned char* text = len > 0 ? sqlite3_column_text(stmt, i) : nullptr;
-
- if (len > 64) {
- result->putDirect(vm, name, JSC::JSValue::decode(Bun__encoding__toStringUTF8(text, len, lexicalGlobalObject)), 0);
- continue;
- }
-
- result->putDirect(vm, name, jsString(vm, WTF::String::fromUTF8(text, len)), 0);
- break;
- }
- case SQLITE_BLOB: {
- size_t len = sqlite3_column_bytes(stmt, i);
- const void* blob = len > 0 ? sqlite3_column_blob(stmt, i) : nullptr;
- JSC::JSUint8Array* array = JSC::JSUint8Array::createUninitialized(lexicalGlobalObject, lexicalGlobalObject->m_typedArrayUint8.get(lexicalGlobalObject), len);
- memcpy(array->vector(), blob, len);
- result->putDirect(vm, name, array, 0);
- break;
- }
- default: {
- result->putDirect(vm, name, jsNull(), 0);
- break;
- }
- }
- }
-
- return JSValue(result);
-}
-
-static inline JSC::JSArray* constructResultRow(JSC::JSGlobalObject* lexicalGlobalObject, JSSQLStatement* castedThis, ObjectInitializationScope& scope, JSC::GCDeferralContext* deferralContext);
-static inline JSC::JSArray* constructResultRow(JSC::JSGlobalObject* lexicalGlobalObject, JSSQLStatement* castedThis, ObjectInitializationScope& scope, JSC::GCDeferralContext* deferralContext)
-{
- int count = castedThis->columnNames.size();
- auto& vm = lexicalGlobalObject->vm();
-
- JSC::JSArray* result = JSArray::create(vm, lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), count);
- auto* stmt = castedThis->stmt;
-
- for (int i = 0; i < count; i++) {
-
- switch (sqlite3_column_type(stmt, i)) {
- case SQLITE_INTEGER: {
- result->initializeIndex(scope, i, jsNumber(sqlite3_column_int(stmt, i)));
- break;
- }
- case SQLITE_FLOAT: {
- result->initializeIndex(scope, i, jsNumber(sqlite3_column_double(stmt, i)));
- break;
- }
- case SQLITE_TEXT: {
- size_t len = sqlite3_column_bytes(stmt, i);
- const unsigned char* text = len > 0 ? sqlite3_column_text(stmt, i) : nullptr;
- if (UNLIKELY(text == nullptr || len == 0)) {
- result->initializeIndex(scope, i, jsEmptyString(vm));
- continue;
- }
- result->initializeIndex(scope, i, len < 64 ? jsString(vm, WTF::String::fromUTF8(text, len)) : JSC::JSValue::decode(Bun__encoding__toStringUTF8(text, len, lexicalGlobalObject)));
- break;
- }
- case SQLITE_BLOB: {
- size_t len = sqlite3_column_bytes(stmt, i);
- const void* blob = len > 0 ? sqlite3_column_blob(stmt, i) : nullptr;
- JSC::JSUint8Array* array = JSC::JSUint8Array::createUninitialized(lexicalGlobalObject, lexicalGlobalObject->m_typedArrayUint8.get(lexicalGlobalObject), len);
- memcpy(array->vector(), blob, len);
- result->initializeIndex(scope, i, array);
- break;
- }
- default: {
- result->initializeIndex(scope, i, jsNull());
- break;
- }
- }
- }
-
- return result;
-}
-
-static inline JSC::JSArray* constructResultRow(JSC::JSGlobalObject* lexicalGlobalObject, JSSQLStatement* castedThis, ObjectInitializationScope& scope)
-{
- return constructResultRow(lexicalGlobalObject, castedThis, scope, nullptr);
-}
-
-static void initializeColumnNames(JSC::JSGlobalObject* lexicalGlobalObject, JSSQLStatement* castedThis)
-{
- castedThis->hasExecuted = true;
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto& names = castedThis->columnNames;
-
- auto* stmt = castedThis->stmt;
-
- int count = sqlite3_column_count(stmt);
- if (count == 0)
- return;
- JSC::ObjectInitializationScope initializationScope(vm);
- JSC::JSObject* object = JSC::constructEmptyObject(lexicalGlobalObject, lexicalGlobalObject->objectPrototype(), count);
-
- for (int i = 0; i < count; i++) {
- const char* name = sqlite3_column_name(stmt, i);
-
- if (name == nullptr)
- break;
-
- size_t len = strlen(name);
- if (len == 0)
- break;
-
- auto wtfString = WTF::String::fromUTF8(name, len);
- auto str = JSValue(jsString(vm, wtfString));
- auto key = str.toPropertyKey(lexicalGlobalObject);
- JSC::JSValue primitive = JSC::jsUndefined();
- auto decl = sqlite3_column_decltype(stmt, i);
- if (decl != nullptr) {
- switch (decl[0]) {
- case 'F':
- case 'D':
- case 'I': {
- primitive = jsNumber(0);
- break;
- }
- case 'V':
- case 'T': {
- primitive = jsEmptyString(vm);
- break;
- }
- }
- }
-
- object->putDirect(vm, key, primitive, 0);
- names.add(key);
- }
- castedThis->_prototype.set(vm, castedThis, object);
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionAll, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
-
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
- auto castedThis = jsDynamicCast<JSSQLStatement*>(callFrame->thisValue());
-
- CHECK_THIS
-
- auto* stmt = castedThis->stmt;
- CHECK_PREPARED
- int statusCode = sqlite3_reset(stmt);
-
- if (UNLIKELY(statusCode != SQLITE_OK)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(statusCode))));
- return JSValue::encode(jsUndefined());
- }
-
- if (callFrame->argumentCount() > 0) {
- auto arg0 = callFrame->argument(0);
- DO_REBIND(arg0);
- }
-
- if (!castedThis->hasExecuted) {
- initializeColumnNames(lexicalGlobalObject, castedThis);
- }
-
- auto& columnNames = castedThis->columnNames;
-
- int status = sqlite3_step(stmt);
-
- size_t columnCount = columnNames.size();
- int counter = 0;
-
- if (status == SQLITE_ROW) {
- // this is a count from UPDATE or another query like that
- if (columnCount == 0) {
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(jsNumber(sqlite3_changes(castedThis->db))));
- }
-
- JSC::JSArray* resultArray = JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0);
- {
- JSC::ObjectInitializationScope initializationScope(vm);
- JSC::GCDeferralContext deferralContext(vm);
-
- while (status == SQLITE_ROW) {
- JSC::JSValue result = constructResultObject(lexicalGlobalObject, castedThis);
- resultArray->push(lexicalGlobalObject, result);
- status = sqlite3_step(stmt);
- }
- }
-
- if (UNLIKELY(status != SQLITE_DONE)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
- sqlite3_reset(stmt);
- return JSValue::encode(jsUndefined());
- }
-
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(resultArray));
- } else if (status == SQLITE_DONE) {
- if (columnCount == 0) {
- RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(0)));
- }
-
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0)));
- } else {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
- sqlite3_reset(stmt);
- return JSValue::encode(jsUndefined());
- }
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionGet, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
-
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
- auto castedThis = jsDynamicCast<JSSQLStatement*>(callFrame->thisValue());
-
- CHECK_THIS
-
- auto* stmt = castedThis->stmt;
- CHECK_PREPARED
-
- int statusCode = sqlite3_reset(stmt);
- if (UNLIKELY(statusCode != SQLITE_OK)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(statusCode))));
- return JSValue::encode(jsUndefined());
- }
-
- if (callFrame->argumentCount() > 0) {
- auto arg0 = callFrame->argument(0);
- DO_REBIND(arg0);
- }
-
- if (!castedThis->hasExecuted) {
- initializeColumnNames(lexicalGlobalObject, castedThis);
- }
-
- auto& columnNames = castedThis->columnNames;
- // {
- // JSC::ObjectInitializationScope initializationScope(vm);
- // array =
- // }
- int status = sqlite3_step(stmt);
-
- size_t columnCount = columnNames.size();
- int counter = 0;
-
- if (status == SQLITE_ROW) {
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(constructResultObject(lexicalGlobalObject, castedThis)));
- } else if (status == SQLITE_DONE) {
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsNull()));
- } else {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
- sqlite3_reset(stmt);
- return JSValue::encode(jsUndefined());
- }
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRows, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
-
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
- auto castedThis = jsDynamicCast<JSSQLStatement*>(callFrame->thisValue());
-
- CHECK_THIS;
-
- auto* stmt = castedThis->stmt;
- CHECK_PREPARED
-
- int statusCode = sqlite3_reset(stmt);
- if (UNLIKELY(statusCode != SQLITE_OK)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(statusCode))));
- sqlite3_reset(stmt);
- return JSValue::encode(jsUndefined());
- }
-
- int count = callFrame->argumentCount();
- if (count > 0) {
- auto arg0 = callFrame->argument(0);
- DO_REBIND(arg0);
- }
-
- if (!castedThis->hasExecuted) {
- initializeColumnNames(lexicalGlobalObject, castedThis);
- }
-
- auto& columnNames = castedThis->columnNames;
- int status = sqlite3_step(stmt);
-
- size_t columnCount = columnNames.size();
- int counter = 0;
-
- if (status == SQLITE_ROW) {
- // this is a count from UPDATE or another query like that
- if (columnCount == 0) {
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(jsNumber(sqlite3_changes(castedThis->db))));
- }
-
- JSC::ObjectInitializationScope initializationScope(vm);
- JSC::GCDeferralContext deferralContext(vm);
-
- JSC::JSArray* resultArray = JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0);
- {
-
- while (status == SQLITE_ROW) {
- JSC::JSValue result = constructResultRow(lexicalGlobalObject, castedThis, initializationScope, &deferralContext);
- resultArray->push(lexicalGlobalObject, result);
- status = sqlite3_step(stmt);
- }
- }
-
- if (UNLIKELY(status != SQLITE_DONE)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
- sqlite3_reset(stmt);
- return JSValue::encode(jsUndefined());
- }
-
- // sqlite3_reset(stmt);
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(resultArray));
- } else if (status == SQLITE_DONE) {
- if (columnCount == 0) {
- RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(0)));
- }
-
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0)));
- } else {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
- sqlite3_reset(stmt);
- return JSValue::encode(jsUndefined());
- }
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRun, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
-
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
- auto castedThis = jsDynamicCast<JSSQLStatement*>(callFrame->thisValue());
-
- CHECK_THIS
-
- auto* stmt = castedThis->stmt;
- CHECK_PREPARED
-
- int statusCode = sqlite3_reset(stmt);
- if (UNLIKELY(statusCode != SQLITE_OK)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(statusCode))));
- return JSValue::encode(jsUndefined());
- }
-
- if (callFrame->argumentCount() > 0) {
- auto arg0 = callFrame->argument(0);
- DO_REBIND(arg0);
- }
-
- if (!castedThis->hasExecuted) {
- initializeColumnNames(lexicalGlobalObject, castedThis);
- }
-
- int status = sqlite3_step(stmt);
-
- if (status == SQLITE_ROW || status == SQLITE_DONE) {
- // sqlite3_reset(stmt);
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(jsUndefined()));
- } else {
- sqlite3_reset(stmt);
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
- return JSValue::encode(jsUndefined());
- }
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementToStringFunction, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- JSSQLStatement* castedThis = jsDynamicCast<JSSQLStatement*>(callFrame->thisValue());
- auto scope = DECLARE_THROW_SCOPE(vm);
-
- CHECK_THIS
-
- char* string = sqlite3_expanded_sql(castedThis->stmt);
- if (!string) {
- RELEASE_AND_RETURN(scope, JSValue::encode(jsEmptyString(vm)));
- }
- size_t length = strlen(string);
- JSString* jsString = JSC::jsString(vm, WTF::String::fromUTF8(string, length));
- sqlite3_free(string);
-
- RELEASE_AND_RETURN(scope, JSValue::encode(jsString));
-}
-
-JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetColumnNames, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- JSSQLStatement* castedThis = jsDynamicCast<JSSQLStatement*>(JSValue::decode(thisValue));
- auto scope = DECLARE_THROW_SCOPE(vm);
- CHECK_THIS
-
- if (!castedThis->hasExecuted) {
- initializeColumnNames(lexicalGlobalObject, castedThis);
- }
-
- auto* array = castedThis->_columnNames.get();
- if (array == nullptr) {
- if (castedThis->columnNames.size() > 0) {
- array = ownPropertyKeys(lexicalGlobalObject, castedThis->_prototype.get(), PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude, CachedPropertyNamesKind::Keys);
- } else {
- array = JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0);
- }
-
- castedThis->_columnNames.set(vm, castedThis, array);
- }
-
- return JSC::JSValue::encode(array);
-}
-
-JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetColumnCount, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- JSSQLStatement* castedThis = jsDynamicCast<JSSQLStatement*>(JSValue::decode(thisValue));
- auto scope = DECLARE_THROW_SCOPE(vm);
- CHECK_THIS
- CHECK_PREPARED
-
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsNumber(sqlite3_column_count(castedThis->stmt))));
-}
-
-JSC_DEFINE_CUSTOM_GETTER(jsSqlStatementGetParamCount, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- JSSQLStatement* castedThis = jsDynamicCast<JSSQLStatement*>(JSValue::decode(thisValue));
- auto scope = DECLARE_THROW_SCOPE(vm);
- CHECK_THIS
- CHECK_PREPARED
-
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(sqlite3_bind_parameter_count(castedThis->stmt))));
-}
-
-JSC_DEFINE_HOST_FUNCTION(jsSQLStatementFunctionFinalize, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- JSSQLStatement* castedThis = jsDynamicCast<JSSQLStatement*>(callFrame->thisValue());
- auto scope = DECLARE_THROW_SCOPE(vm);
- CHECK_THIS
-
- if (castedThis->stmt) {
- sqlite3_finalize(castedThis->stmt);
- castedThis->stmt = nullptr;
- }
-
- RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
-}
-
-const ClassInfo JSSQLStatement::s_info = { "SQLStatement"_s, nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSSQLStatement) };
-
-/* Hash table for prototype */
-static const HashTableValue JSSQLStatementTableValues[] = {
- { "run"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementExecuteStatementFunctionRun), (intptr_t)(1) } },
- { "get"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementExecuteStatementFunctionGet), (intptr_t)(1) } },
- { "all"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementExecuteStatementFunctionAll), (intptr_t)(1) } },
- { "values"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementExecuteStatementFunctionRows), (intptr_t)(1) } },
- { "finalize"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementFunctionFinalize), (intptr_t)(0) } },
- { "toString"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsSQLStatementToStringFunction), (intptr_t)(0) } },
- { "columns"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsSqlStatementGetColumnNames), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
- { "columnsCount"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsSqlStatementGetColumnCount), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
- { "paramsCount"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor), NoIntrinsic, { (intptr_t) static_cast<PropertySlot::GetValueFunc>(jsSqlStatementGetParamCount), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(0) } },
-};
-
-void JSSQLStatement::finishCreation(VM& vm)
-{
- Base::finishCreation(vm);
- reifyStaticProperties(vm, JSSQLStatement::info(), JSSQLStatementTableValues, *this);
-}
-
-JSSQLStatement::~JSSQLStatement()
-{
- if (this->stmt) {
- sqlite3_finalize(this->stmt);
- }
-}
-
-JSC::JSValue JSSQLStatement::rebind(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSValue values, bool clone)
-{
- JSC::VM& vm = lexicalGlobalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
- auto* stmt = this->stmt;
- auto val = rebindStatement(lexicalGlobalObject, values, scope, stmt, clone);
- if (val.isNumber()) {
- RELEASE_AND_RETURN(scope, val);
- } else {
- return val;
- }
-}
-
-template<typename Visitor>
-void JSSQLStatement::visitChildrenImpl(JSCell* cell, Visitor& visitor)
-{
- JSSQLStatement* thisObject = jsCast<JSSQLStatement*>(cell);
- ASSERT_GC_OBJECT_INHERITS(thisObject, info());
- Base::visitChildren(thisObject, visitor);
- visitor.append(thisObject->_columnNames);
- visitor.append(thisObject->_prototype);
-}
-
-DEFINE_VISIT_CHILDREN(JSSQLStatement);
-}