195 lines
9.4 KiB
C++
195 lines
9.4 KiB
C++
#define JS_VALUE_TO_SQLITE(to, value, isolate, ...) \
|
|
if (value->IsNumber()) { \
|
|
return sqlite3_##to##_double( \
|
|
__VA_ARGS__, \
|
|
value.As<v8::Number>()->Value() \
|
|
); \
|
|
} else if (value->IsBigInt()) { \
|
|
bool lossless; \
|
|
int64_t v = value.As<v8::BigInt>()->Int64Value(&lossless); \
|
|
if (lossless) { \
|
|
return sqlite3_##to##_int64(__VA_ARGS__, v); \
|
|
} \
|
|
} else if (value->IsString()) { \
|
|
v8::String::Utf8Value utf8(isolate, value.As<v8::String>()); \
|
|
return sqlite3_##to##_text( \
|
|
__VA_ARGS__, \
|
|
*utf8, \
|
|
utf8.length(), \
|
|
SQLITE_TRANSIENT \
|
|
); \
|
|
} else if (node::Buffer::HasInstance(value)) { \
|
|
const char* data = node::Buffer::Data(value); \
|
|
return sqlite3_##to##_blob( \
|
|
__VA_ARGS__, \
|
|
data ? data : "", \
|
|
node::Buffer::Length(value), \
|
|
SQLITE_TRANSIENT \
|
|
); \
|
|
} else if (value->IsNull() || value->IsUndefined()) { \
|
|
return sqlite3_##to##_null(__VA_ARGS__); \
|
|
}
|
|
|
|
#define SQLITE_VALUE_TO_JS(from, isolate, safe_ints, ...) \
|
|
switch (sqlite3_##from##_type(__VA_ARGS__)) { \
|
|
case SQLITE_INTEGER: \
|
|
if (safe_ints) { \
|
|
return v8::BigInt::New( \
|
|
isolate, \
|
|
sqlite3_##from##_int64(__VA_ARGS__) \
|
|
); \
|
|
} \
|
|
case SQLITE_FLOAT: \
|
|
return v8::Number::New( \
|
|
isolate, \
|
|
sqlite3_##from##_double(__VA_ARGS__) \
|
|
); \
|
|
case SQLITE_TEXT: \
|
|
return StringFromUtf8( \
|
|
isolate, \
|
|
reinterpret_cast<const char*>(sqlite3_##from##_text(__VA_ARGS__)), \
|
|
sqlite3_##from##_bytes(__VA_ARGS__) \
|
|
); \
|
|
case SQLITE_BLOB: \
|
|
return node::Buffer::Copy( \
|
|
isolate, \
|
|
static_cast<const char*>(sqlite3_##from##_blob(__VA_ARGS__)), \
|
|
sqlite3_##from##_bytes(__VA_ARGS__) \
|
|
).ToLocalChecked(); \
|
|
default: \
|
|
assert(sqlite3_##from##_type(__VA_ARGS__) == SQLITE_NULL); \
|
|
return v8::Null(isolate); \
|
|
} \
|
|
assert(false);
|
|
|
|
namespace Data {
|
|
|
|
static const char FLAT = 0;
|
|
static const char PLUCK = 1;
|
|
static const char EXPAND = 2;
|
|
static const char RAW = 3;
|
|
|
|
v8::Local<v8::Value> GetValueJS(v8::Isolate* isolate, sqlite3_stmt* handle, int column, bool safe_ints) {
|
|
SQLITE_VALUE_TO_JS(column, isolate, safe_ints, handle, column);
|
|
}
|
|
|
|
v8::Local<v8::Value> GetValueJS(v8::Isolate* isolate, sqlite3_value* value, bool safe_ints) {
|
|
SQLITE_VALUE_TO_JS(value, isolate, safe_ints, value);
|
|
}
|
|
|
|
v8::Local<v8::Value> GetExpandedRowJS(v8::Isolate* isolate, v8::Local<v8::Context> ctx, sqlite3_stmt* handle, bool safe_ints) {
|
|
v8::Local<v8::Object> row = v8::Object::New(isolate);
|
|
int column_count = sqlite3_column_count(handle);
|
|
for (int i = 0; i < column_count; ++i) {
|
|
const char* table_raw = sqlite3_column_table_name(handle, i);
|
|
v8::Local<v8::String> table = InternalizedFromUtf8(isolate, table_raw == NULL ? "$" : table_raw, -1);
|
|
v8::Local<v8::String> column = InternalizedFromUtf8(isolate, sqlite3_column_name(handle, i), -1);
|
|
v8::Local<v8::Value> value = Data::GetValueJS(isolate, handle, i, safe_ints);
|
|
if (row->HasOwnProperty(ctx, table).FromJust()) {
|
|
row->Get(ctx, table).ToLocalChecked().As<v8::Object>()->Set(ctx, column, value).FromJust();
|
|
} else {
|
|
v8::Local<v8::Object> nested = v8::Object::New(isolate);
|
|
row->Set(ctx, table, nested).FromJust();
|
|
nested->Set(ctx, column, value).FromJust();
|
|
}
|
|
}
|
|
return row;
|
|
}
|
|
|
|
#if !defined(NODE_MODULE_VERSION) || NODE_MODULE_VERSION < 127
|
|
|
|
v8::Local<v8::Value> GetFlatRowJS(v8::Isolate* isolate, v8::Local<v8::Context> ctx, sqlite3_stmt* handle, bool safe_ints) {
|
|
v8::Local<v8::Object> row = v8::Object::New(isolate);
|
|
int column_count = sqlite3_column_count(handle);
|
|
for (int i = 0; i < column_count; ++i) {
|
|
row->Set(ctx,
|
|
InternalizedFromUtf8(isolate, sqlite3_column_name(handle, i), -1),
|
|
Data::GetValueJS(isolate, handle, i, safe_ints)
|
|
).FromJust();
|
|
}
|
|
return row;
|
|
}
|
|
|
|
v8::Local<v8::Value> GetRawRowJS(v8::Isolate* isolate, v8::Local<v8::Context> ctx, sqlite3_stmt* handle, bool safe_ints) {
|
|
v8::Local<v8::Array> row = v8::Array::New(isolate);
|
|
int column_count = sqlite3_column_count(handle);
|
|
for (int i = 0; i < column_count; ++i) {
|
|
row->Set(ctx, i, Data::GetValueJS(isolate, handle, i, safe_ints)).FromJust();
|
|
}
|
|
return row;
|
|
}
|
|
|
|
v8::Local<v8::Value> GetRowJS(v8::Isolate* isolate, v8::Local<v8::Context> ctx, sqlite3_stmt* handle, bool safe_ints, char mode) {
|
|
if (mode == FLAT) return GetFlatRowJS(isolate, ctx, handle, safe_ints);
|
|
if (mode == PLUCK) return GetValueJS(isolate, handle, 0, safe_ints);
|
|
if (mode == EXPAND) return GetExpandedRowJS(isolate, ctx, handle, safe_ints);
|
|
if (mode == RAW) return GetRawRowJS(isolate, ctx, handle, safe_ints);
|
|
assert(false);
|
|
return v8::Local<v8::Value>();
|
|
}
|
|
|
|
#else
|
|
|
|
v8::Local<v8::Value> GetFlatRowJS(v8::Isolate* isolate, sqlite3_stmt* handle, bool safe_ints) {
|
|
int column_count = sqlite3_column_count(handle);
|
|
v8::LocalVector<v8::Name> keys(isolate);
|
|
v8::LocalVector<v8::Value> values(isolate);
|
|
keys.reserve(column_count);
|
|
values.reserve(column_count);
|
|
for (int i = 0; i < column_count; ++i) {
|
|
keys.emplace_back(
|
|
InternalizedFromUtf8(isolate, sqlite3_column_name(handle, i), -1).As<v8::Name>()
|
|
);
|
|
values.emplace_back(
|
|
Data::GetValueJS(isolate, handle, i, safe_ints)
|
|
);
|
|
}
|
|
return v8::Object::New(
|
|
isolate,
|
|
GET_PROTOTYPE(v8::Object::New(isolate)),
|
|
keys.data(),
|
|
values.data(),
|
|
column_count
|
|
);
|
|
}
|
|
|
|
v8::Local<v8::Value> GetRawRowJS(v8::Isolate* isolate, sqlite3_stmt* handle, bool safe_ints) {
|
|
int column_count = sqlite3_column_count(handle);
|
|
v8::LocalVector<v8::Value> row(isolate);
|
|
row.reserve(column_count);
|
|
for (int i = 0; i < column_count; ++i) {
|
|
row.emplace_back(Data::GetValueJS(isolate, handle, i, safe_ints));
|
|
}
|
|
return v8::Array::New(isolate, row.data(), row.size());
|
|
}
|
|
|
|
v8::Local<v8::Value> GetRowJS(v8::Isolate* isolate, v8::Local<v8::Context> ctx, sqlite3_stmt* handle, bool safe_ints, char mode) {
|
|
if (mode == FLAT) return GetFlatRowJS(isolate, handle, safe_ints);
|
|
if (mode == PLUCK) return GetValueJS(isolate, handle, 0, safe_ints);
|
|
if (mode == EXPAND) return GetExpandedRowJS(isolate, ctx, handle, safe_ints);
|
|
if (mode == RAW) return GetRawRowJS(isolate, handle, safe_ints);
|
|
assert(false);
|
|
return v8::Local<v8::Value>();
|
|
}
|
|
|
|
#endif
|
|
|
|
void GetArgumentsJS(v8::Isolate* isolate, v8::Local<v8::Value>* out, sqlite3_value** values, int argument_count, bool safe_ints) {
|
|
assert(argument_count > 0);
|
|
for (int i = 0; i < argument_count; ++i) {
|
|
out[i] = Data::GetValueJS(isolate, values[i], safe_ints);
|
|
}
|
|
}
|
|
|
|
int BindValueFromJS(v8::Isolate* isolate, sqlite3_stmt* handle, int index, v8::Local<v8::Value> value) {
|
|
JS_VALUE_TO_SQLITE(bind, value, isolate, handle, index);
|
|
return value->IsBigInt() ? SQLITE_TOOBIG : -1;
|
|
}
|
|
|
|
void ResultValueFromJS(v8::Isolate* isolate, sqlite3_context* invocation, v8::Local<v8::Value> value, DataConverter* converter) {
|
|
JS_VALUE_TO_SQLITE(result, value, isolate, invocation);
|
|
converter->ThrowDataConversionError(invocation, value->IsBigInt());
|
|
}
|
|
|
|
}
|