216 lines
12 KiB
C++
216 lines
12 KiB
C++
#ifndef NODE_SQLITE3_SRC_MACROS_H
|
|
#define NODE_SQLITE3_SRC_MACROS_H
|
|
|
|
const char* sqlite_code_string(int code);
|
|
const char* sqlite_authorizer_string(int type);
|
|
#include <vector>
|
|
|
|
// TODO: better way to work around StringConcat?
|
|
#include <napi.h>
|
|
inline Napi::String StringConcat(Napi::Value str1, Napi::Value str2) {
|
|
return Napi::String::New(str1.Env(), str1.As<Napi::String>().Utf8Value() +
|
|
str2.As<Napi::String>().Utf8Value() );
|
|
}
|
|
|
|
// A Napi substitute IsInt32()
|
|
inline bool OtherIsInt(Napi::Number source) {
|
|
double orig_val = source.DoubleValue();
|
|
double int_val = (double)source.Int32Value();
|
|
if (orig_val == int_val) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#define IS_FUNCTION(cb) \
|
|
!cb.IsUndefined() && cb.IsFunction()
|
|
|
|
#define REQUIRE_ARGUMENTS(n) \
|
|
if (info.Length() < (n)) { \
|
|
Napi::TypeError::New(env, "Expected " #n "arguments").ThrowAsJavaScriptException(); \
|
|
return env.Null(); \
|
|
}
|
|
|
|
|
|
#define REQUIRE_ARGUMENT_EXTERNAL(i, var) \
|
|
if (info.Length() <= (i) || !info[i].IsExternal()) { \
|
|
Napi::TypeError::New(env, "Argument " #i " invalid").ThrowAsJavaScriptException(); \
|
|
return env.Null(); \
|
|
} \
|
|
Napi::External var = info[i].As<Napi::External>();
|
|
|
|
|
|
#define REQUIRE_ARGUMENT_FUNCTION(i, var) \
|
|
if (info.Length() <= (i) || !info[i].IsFunction()) { \
|
|
Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \
|
|
return env.Null(); \
|
|
} \
|
|
Napi::Function var = info[i].As<Napi::Function>();
|
|
|
|
|
|
#define REQUIRE_ARGUMENT_STRING(i, var) \
|
|
if (info.Length() <= (i) || !info[i].IsString()) { \
|
|
Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); \
|
|
return env.Null(); \
|
|
} \
|
|
std::string var = info[i].As<Napi::String>();
|
|
|
|
#define REQUIRE_ARGUMENT_INTEGER(i, var) \
|
|
if (info.Length() <= (i) || !info[i].IsNumber()) { \
|
|
Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \
|
|
return env.Null(); \
|
|
} \
|
|
int var(info[i].As<Napi::Number>().Int32Value());
|
|
|
|
#define OPTIONAL_ARGUMENT_FUNCTION(i, var) \
|
|
Napi::Function var; \
|
|
if (info.Length() > i && !info[i].IsUndefined()) { \
|
|
if (!info[i].IsFunction()) { \
|
|
Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \
|
|
return env.Null(); \
|
|
} \
|
|
var = info[i].As<Napi::Function>(); \
|
|
}
|
|
|
|
|
|
#define OPTIONAL_ARGUMENT_INTEGER(i, var, default) \
|
|
int var; \
|
|
if (info.Length() <= (i)) { \
|
|
var = (default); \
|
|
} \
|
|
else if (info[i].IsNumber()) { \
|
|
if (OtherIsInt(info[i].As<Number>())) { \
|
|
var = info[i].As<Napi::Number>().Int32Value(); \
|
|
} \
|
|
} \
|
|
else { \
|
|
Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \
|
|
return env.Null(); \
|
|
}
|
|
|
|
|
|
#define DEFINE_CONSTANT_INTEGER(target, constant, name) \
|
|
Napi::PropertyDescriptor::Value(#name, Napi::Number::New(env, constant), \
|
|
static_cast<napi_property_attributes>(napi_enumerable | napi_configurable)),
|
|
|
|
#define DEFINE_CONSTANT_STRING(target, constant, name) \
|
|
Napi::PropertyDescriptor::Value(#name, Napi::String::New(env, constant), \
|
|
static_cast<napi_property_attributes>(napi_enumerable | napi_configurable)),
|
|
|
|
#define EXCEPTION(msg, errno, name) \
|
|
Napi::Value name = Napi::Error::New(env, \
|
|
StringConcat( \
|
|
StringConcat( \
|
|
Napi::String::New(env, sqlite_code_string(errno)), \
|
|
Napi::String::New(env, ": ") \
|
|
), \
|
|
(msg) \
|
|
).Utf8Value() \
|
|
).Value(); \
|
|
Napi::Object name ##_obj = name.As<Napi::Object>(); \
|
|
(name ##_obj).Set( Napi::String::New(env, "errno"), Napi::Number::New(env, errno)); \
|
|
(name ##_obj).Set( Napi::String::New(env, "code"), \
|
|
Napi::String::New(env, sqlite_code_string(errno)));
|
|
|
|
|
|
#define EMIT_EVENT(obj, argc, argv) \
|
|
TRY_CATCH_CALL((obj), \
|
|
(obj).Get("emit").As<Napi::Function>(),\
|
|
argc, argv \
|
|
);
|
|
|
|
// The Mac OS compiler complains when argv is NULL unless we
|
|
// first assign it to a locally defined variable.
|
|
#define TRY_CATCH_CALL(context, callback, argc, argv, ...) \
|
|
Napi::Value* passed_argv = argv;\
|
|
std::vector<napi_value> args;\
|
|
if ((argc != 0) && (passed_argv != NULL)) {\
|
|
args.assign(passed_argv, passed_argv + argc);\
|
|
}\
|
|
Napi::Value res = (callback).Call(Napi::Value(context), args); \
|
|
if (res.IsEmpty()) return __VA_ARGS__;
|
|
|
|
#define WORK_DEFINITION(name) \
|
|
Napi::Value name(const Napi::CallbackInfo& info); \
|
|
static void Work_Begin##name(Baton* baton); \
|
|
static void Work_##name(napi_env env, void* data); \
|
|
static void Work_After##name(napi_env env, napi_status status, void* data);
|
|
|
|
#define STATEMENT_BEGIN(type) \
|
|
assert(baton); \
|
|
assert(baton->stmt); \
|
|
assert(!baton->stmt->locked); \
|
|
assert(!baton->stmt->finalized); \
|
|
assert(baton->stmt->prepared); \
|
|
baton->stmt->locked = true; \
|
|
baton->stmt->db->pending++; \
|
|
Napi::Env env = baton->stmt->Env(); \
|
|
int status = napi_create_async_work( \
|
|
env, NULL, Napi::String::New(env, "sqlite3.Statement."#type), \
|
|
Work_##type, Work_After##type, baton, &baton->request \
|
|
); \
|
|
assert(status == 0); \
|
|
napi_queue_async_work(env, baton->request);
|
|
|
|
#define STATEMENT_INIT(type) \
|
|
type* baton = static_cast<type*>(data); \
|
|
Statement* stmt = baton->stmt;
|
|
|
|
#define STATEMENT_MUTEX(name) \
|
|
if (!stmt->db->_handle) { \
|
|
stmt->status = SQLITE_MISUSE; \
|
|
stmt->message = "Database handle is closed"; \
|
|
return; \
|
|
} \
|
|
sqlite3_mutex* name = sqlite3_db_mutex(stmt->db->_handle);
|
|
|
|
#define STATEMENT_END() \
|
|
assert(stmt->locked); \
|
|
assert(stmt->db->pending); \
|
|
stmt->locked = false; \
|
|
stmt->db->pending--; \
|
|
stmt->Process(); \
|
|
stmt->db->Process();
|
|
|
|
#define BACKUP_BEGIN(type) \
|
|
assert(baton); \
|
|
assert(baton->backup); \
|
|
assert(!baton->backup->locked); \
|
|
assert(!baton->backup->finished); \
|
|
assert(baton->backup->inited); \
|
|
baton->backup->locked = true; \
|
|
baton->backup->db->pending++; \
|
|
Napi::Env env = baton->backup->Env(); \
|
|
int status = napi_create_async_work( \
|
|
env, NULL, Napi::String::New(env, "sqlite3.Backup."#type), \
|
|
Work_##type, Work_After##type, baton, &baton->request \
|
|
); \
|
|
assert(status == 0); \
|
|
napi_queue_async_work(env, baton->request);
|
|
|
|
#define BACKUP_INIT(type) \
|
|
type* baton = static_cast<type*>(data); \
|
|
Backup* backup = baton->backup;
|
|
|
|
#define BACKUP_END() \
|
|
assert(backup->locked); \
|
|
assert(backup->db->pending); \
|
|
backup->locked = false; \
|
|
backup->db->pending--; \
|
|
backup->Process(); \
|
|
backup->db->Process();
|
|
|
|
#define DELETE_FIELD(field) \
|
|
if (field != NULL) { \
|
|
switch ((field)->type) { \
|
|
case SQLITE_INTEGER: delete (Values::Integer*)(field); break; \
|
|
case SQLITE_FLOAT: delete (Values::Float*)(field); break; \
|
|
case SQLITE_TEXT: delete (Values::Text*)(field); break; \
|
|
case SQLITE_BLOB: delete (Values::Blob*)(field); break; \
|
|
case SQLITE_NULL: delete (Values::Null*)(field); break; \
|
|
} \
|
|
}
|
|
|
|
#endif
|