Skip to content

Commit

Permalink
Merge pull request #30 from JoshuaWise/experimental-bind-map
Browse files Browse the repository at this point in the history
Named parameter binding rewrite
  • Loading branch information
JoshuaWise authored Apr 30, 2017
2 parents f700521 + 0215d67 commit 0985983
Show file tree
Hide file tree
Showing 28 changed files with 273 additions and 259 deletions.
3 changes: 0 additions & 3 deletions lib/null-factory.js

This file was deleted.

9 changes: 5 additions & 4 deletions src/binder/bind-args.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
// Anonymous parameters are either directly in the arguments object, or in an
// Array (or Array-like object).
// If an error occurs, error is set to an appropriately descriptive string.
// Regardless of whether an error occurs, the return value is the number of
// parameters that were bound.
// Regardless of whether an error occurs, the return value is a number whose
// first bit indicates whether it tried to bind an object, and whose other 31
// bits describe the number of parameters that were bound.

int Binder::BindArgs(Nan::NAN_METHOD_ARGS_TYPE info, int len, Query* query) {
bool bound_object = false;
Expand All @@ -24,7 +25,7 @@ int Binder::BindArgs(Nan::NAN_METHOD_ARGS_TYPE info, int len, Query* query) {
}

// Objects
if (arg->IsObject() && !arg->IsArrayBufferView()) {
if (arg->IsObject() && !node::Buffer::HasInstance(arg)) {
v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(arg);
if (IsPlainObject(obj)) {
if (bound_object) {
Expand All @@ -49,5 +50,5 @@ int Binder::BindArgs(Nan::NAN_METHOD_ARGS_TYPE info, int len, Query* query) {
count += 1;

}
return count;
return (count << 1) | static_cast<int>(bound_object);
}
2 changes: 1 addition & 1 deletion src/binder/bind-array.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ int Binder::BindArray(v8::Local<v8::Array> arr) {
for (int i=0; i<len; ++i) {
Nan::MaybeLocal<v8::Value> maybeValue = Nan::Get(arr, i);
if (maybeValue.IsEmpty()) {
error = COPY("An error was thrown while trying to get values from the given array.");
error = COPY("");
return i;
}
BindValue(maybeValue.ToLocalChecked());
Expand Down
41 changes: 13 additions & 28 deletions src/binder/bind-object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,34 @@
// Regardless of whether an error occurs, the return value is the number of
// parameters that were bound.

int Binder::BindObject(v8::Local<v8::Object> obj, v8::Local<v8::Object> bindMap) {
// Get array of properties.
Nan::MaybeLocal<v8::Array> maybeKeys = Nan::GetOwnPropertyNames(obj);
if (maybeKeys.IsEmpty()) {
error = COPY("An error was thrown while trying to get the property names of the given object.");
return 0;
}
v8::Local<v8::Array> keys = maybeKeys.ToLocalChecked();

// Get property count.
unsigned int key_length = keys->Length();
int len = key_length > 0x7ffffffeU ? 0x7ffffffe : static_cast<int>(key_length);
int Binder::BindObject(v8::Local<v8::Object> obj, BindMap* bindMap) {
Nan::HandleScope scope;
int len = bindMap->length;
BindPair* pairs = bindMap->pairs;

// Loop through each property.
for (int i=0; i<len; ++i) {
v8::Local<v8::String> key = Nan::New(pairs[i].name).ToLocalChecked();

// Get current property name.
Nan::MaybeLocal<v8::Value> maybeKey = Nan::Get(keys, i);
if (maybeKey.IsEmpty()) {
error = COPY("An error was thrown while trying to get the property names of the given object.");
// Check if the named parameter was provided.
v8::Maybe<bool> has_property = Nan::HasOwnProperty(obj, key);
if (has_property.IsNothing()) {
error = COPY("");
return i;
}
v8::Local<v8::String> key = v8::Local<v8::String>::Cast(maybeKey.ToLocalChecked());

// Get the parameter index of the current named parameter.
v8::Local<v8::Value> indexValue = Nan::Get(bindMap, key).ToLocalChecked();
if (indexValue->IsUndefined()) {
Nan::Utf8String utf8(key);
CONCAT3(message, "The named parameter \"", *utf8, "\" does not exist.");
if (!has_property.FromJust()) {
CONCAT3(message, "Missing named parameter \"", pairs[i].name, "\".");
error = COPY(message.c_str());
return i;
}
int index = static_cast<int>(v8::Local<v8::Number>::Cast(indexValue)->Value());

// Get the current property value.
Nan::MaybeLocal<v8::Value> maybeValue = Nan::Get(obj, key);
if (maybeValue.IsEmpty()) {
error = COPY("An error was thrown while trying to get property values of the given object.");
error = COPY("");
return i;
}

// Bind value.
BindValue(maybeValue.ToLocalChecked(), index);
BindValue(maybeValue.ToLocalChecked(), pairs[i].index);
if (error) {
return i;
}
Expand Down
22 changes: 14 additions & 8 deletions src/binder/binder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "../objects/query.h"
#include "../util/macros.h"
#include "../util/data.h"
#include "../util/bind-map.h"

#include "is-plain-object.cc"
#include "next-anon-index.cc"
Expand All @@ -23,12 +24,17 @@ Binder::~Binder() {
}

void Binder::Bind(Nan::NAN_METHOD_ARGS_TYPE info, int len, Query* query) {
int count = BindArgs(info, len, query);
if (!error && count != param_count) {
if (count < param_count) {
error = COPY("Too few parameter values were provided.");
} else {
error = COPY("Too many parameter values were provided.");
}
}
int result = BindArgs(info, len, query);
int count = result >> 1;
if (!error && count != param_count) {
if (count < param_count) {
if (!(result & 1) && query->GetBindMap()->length) {
error = COPY("Missing named parameters.");
} else {
error = COPY("Too few parameter values were provided.");
}
} else {
error = COPY("Too many parameter values were provided.");
}
}
}
3 changes: 2 additions & 1 deletion src/binder/binder.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <sqlite3.h>
#include <nan.h>
class Query;
class BindMap;

class Binder {
public:
Expand All @@ -19,7 +20,7 @@ class Binder {

void BindValue(v8::Local<v8::Value>, int = 0);
int BindArray(v8::Local<v8::Array>);
virtual int BindObject(v8::Local<v8::Object>, v8::Local<v8::Object>); // This should only be invoked once
virtual int BindObject(v8::Local<v8::Object>, BindMap*); // This should only be invoked once
int BindArgs(Nan::NAN_METHOD_ARGS_TYPE, int, Query*);

sqlite3_stmt* handle;
Expand Down
85 changes: 23 additions & 62 deletions src/multi-binder/bind-object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,83 +5,44 @@
// parameters that were bound. Unlike the normal Binder, this will bind
// parameters to all handles, not just the current one.

int MultiBinder::BindObject(v8::Local<v8::Object> obj, v8::Local<v8::Object> bindMap) {
// Get array of properties.
Nan::MaybeLocal<v8::Array> maybeKeys = Nan::GetOwnPropertyNames(obj);
if (maybeKeys.IsEmpty()) {
error = COPY("An error was thrown while trying to get the property names of the given object.");
return 0;
}
v8::Local<v8::Array> keys = maybeKeys.ToLocalChecked();

// Get property count.
unsigned int key_length = keys->Length();
int len = key_length > 0x7ffffffeU ? 0x7ffffffe : static_cast<int>(key_length);
int bound_count = 0;
int MultiBinder::BindObject(v8::Local<v8::Object> obj, BindMap* bindMap) {
Nan::HandleScope scope;
int len = bindMap->length;
BindPair* pairs = bindMap->pairs;

// Save current handle.
sqlite3_stmt* current_handle = handle;

// Loop through each property.
for (int i=0; i<len; ++i) {
v8::Local<v8::String> key = Nan::New(pairs[i].name).ToLocalChecked();

// Get current property name.
Nan::MaybeLocal<v8::Value> maybeKey = Nan::Get(keys, i);
if (maybeKey.IsEmpty()) {
error = COPY("An error was thrown while trying to get the property names of the given object.");
return bound_count;
// Check if the named parameter was provided.
v8::Maybe<bool> has_property = Nan::HasOwnProperty(obj, key);
if (has_property.IsNothing()) {
error = COPY("");
return i;
}
if (!has_property.FromJust()) {
CONCAT3(message, "Missing named parameter \"", pairs[i].name, "\".");
error = COPY(message.c_str());
return i;
}
v8::Local<v8::String> key = v8::Local<v8::String>::Cast(maybeKey.ToLocalChecked());

// Get the current property value.
Nan::MaybeLocal<v8::Value> maybeValue = Nan::Get(obj, key);
if (maybeValue.IsEmpty()) {
error = COPY("An error was thrown while trying to get property values of the given object.");
return bound_count;
error = COPY("");
return i;
}
v8::Local<v8::Value> value = maybeValue.ToLocalChecked();


bool someoneHadNamedParameter = false;


// Loop through each handle.
for (unsigned int h=0; h<handle_count; ++h) {
handle = handles[h];

// Get the parameter index of the current named parameter.
v8::Local<v8::Value> currentMap = Nan::Get(bindMap, h).ToLocalChecked();
if (currentMap->IsUndefined()) {
continue;
}
v8::Local<v8::Value> indexValue = Nan::Get(v8::Local<v8::Object>::Cast(currentMap), key).ToLocalChecked();
if (!indexValue->IsUndefined()) {
int index = static_cast<int>(v8::Local<v8::Number>::Cast(indexValue)->Value());

// Bind value.
BindValue(value, index);
if (error) {
return bound_count;
}
++bound_count;

if (!someoneHadNamedParameter) {
someoneHadNamedParameter = true;
}
}

}


// If no handles had this named parameter, provide an error.
if (!someoneHadNamedParameter) {
Nan::Utf8String utf8(key);
CONCAT3(message, "The named parameter \"", *utf8, "\" does not exist.");
error = COPY(message.c_str());
return bound_count;
int index = pairs[i].index;
handle = handles[BindMap::GetTransactionIndex(index)];
BindValue(maybeValue.ToLocalChecked(), BindMap::GetParameterIndex(index));
if (error) {
return i;
}
}

handle = current_handle;
return bound_count;
return len;
}
10 changes: 8 additions & 2 deletions src/multi-binder/multi-binder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "../binder/binder.h"
#include "../objects/query.h"
#include "../util/macros.h"
#include "../util/bind-map.h"

#include "next-anon-index.cc"
#include "bind-object.cc"
Expand All @@ -16,14 +17,19 @@ MultiBinder::MultiBinder(sqlite3_stmt** handles, unsigned int handle_count)
, param_count_sum(param_count) {}

void MultiBinder::Bind(Nan::NAN_METHOD_ARGS_TYPE info, int len, Query* query) {
int count = BindArgs(info, len, query);
int result = BindArgs(info, len, query);
int count = result >> 1;
if (!error) {
while (handle_index + 1 < handle_count) {
param_count_sum += sqlite3_bind_parameter_count(handles[++handle_index]);
}
if (count != param_count_sum) {
if (count < param_count_sum) {
error = COPY("Too few parameter values were provided.");
if (!(result & 1) && query->GetBindMap()->length) {
error = COPY("Missing named parameters.");
} else {
error = COPY("Too few parameter values were provided.");
}
} else {
error = COPY("Too many parameter values were provided.");
}
Expand Down
3 changes: 2 additions & 1 deletion src/multi-binder/multi-binder.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <nan.h>
#include "../binder/binder.h"
class Query;
class BindMap;

class MultiBinder : public Binder {
public:
Expand All @@ -13,7 +14,7 @@ class MultiBinder : public Binder {

protected:
int NextAnonIndex();
int BindObject(v8::Local<v8::Object>, v8::Local<v8::Object>); // This should only be invoked once per handle
int BindObject(v8::Local<v8::Object>, BindMap*); // This should only be invoked once per handle

sqlite3_stmt** const handles;
unsigned int const handle_count;
Expand Down
4 changes: 2 additions & 2 deletions src/objects/database/create-statement.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ NAN_METHOD(Database::CreateStatement) {
return Nan::ThrowTypeError("This operation is not available while in readonly mode.");
}
Nan::ForceSet(statement, NEW_INTERNAL_STRING_FAST("source"), source, FROZEN);
Nan::ForceSet(statement, NEW_INTERNAL_STRING_FAST("database"), info.This(), FROZEN);
Nan::ForceSet(statement, NEW_INTERNAL_STRING_FAST("database"), info.This(), FROZEN);
if (db->safe_ints) {stmt->state |= SAFE_INTS;}

// Pushes onto stmts set.
stmt->id = NEXT_STATEMENT_ID++;
stmt->extras->id = NEXT_STATEMENT_ID++;
db->stmts.insert(db->stmts.end(), stmt);

info.GetReturnValue().Set(statement);
Expand Down
9 changes: 6 additions & 3 deletions src/objects/database/create-transaction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ NAN_METHOD(Database::CreateTransaction) {
}

unsigned int len = sources->Length();
v8::Local<v8::Array> digestedSources = Nan::New<v8::Array>(len);
if (!(len > 0)) {
return Nan::ThrowTypeError("No SQL statements were provided.");
}
if (len > max_transaction_length) {
return Nan::ThrowTypeError("Too many SQL statements were provided.");
}
v8::Local<v8::Array> digestedSources = Nan::New<v8::Array>(len);

// Validate and digest source strings.
v8::Local<v8::String> semicolon = Nan::New(";").ToLocalChecked();
Expand Down Expand Up @@ -83,11 +86,11 @@ NAN_METHOD(Database::CreateTransaction) {
}
}
Nan::ForceSet(transaction, NEW_INTERNAL_STRING_FAST("source"), joinedSource, FROZEN);
Nan::ForceSet(transaction, NEW_INTERNAL_STRING_FAST("database"), info.This(), FROZEN);
Nan::ForceSet(transaction, NEW_INTERNAL_STRING_FAST("database"), info.This(), FROZEN);
if (db->safe_ints) {trans->state |= SAFE_INTS;}

// Pushes onto transs set.
trans->id = NEXT_TRANSACTION_ID++;
trans->extras->id = NEXT_TRANSACTION_ID++;
db->transs.insert(db->transs.end(), trans);

info.GetReturnValue().Set(transaction);
Expand Down
9 changes: 2 additions & 7 deletions src/objects/database/database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
#include "../../util/macros.h"
#include "../../util/data.h"
#include "../../util/transaction-handles.h"
#include "../../util/bind-map.h"

const int max_buffer_size = node::Buffer::kMaxLength > 0x7fffffffU ? 0x7fffffff : static_cast<int>(node::Buffer::kMaxLength);
const int max_string_size = v8::String::kMaxLength;
const v8::PropertyAttribute FROZEN = static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly);
bool CONSTRUCTING_PRIVILEGES = false;
sqlite3_uint64 NEXT_STATEMENT_ID = 0;
sqlite3_uint64 NEXT_TRANSACTION_ID = 0;
Nan::Persistent<v8::Function> NullFactory;

#include "new.cc"
#include "close.cc"
Expand Down Expand Up @@ -66,11 +66,6 @@ void Database::Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module)

Nan::Set(exports, Nan::New("Database").ToLocalChecked(),
Nan::GetFunction(t).ToLocalChecked());

// Save NullFactory to persistent handle.
v8::Local<v8::Function> require = v8::Local<v8::Function>::Cast(Nan::Get(module, Nan::New("require").ToLocalChecked()).ToLocalChecked());
v8::Local<v8::Value> args[1] = {Nan::New("../../lib/null-factory.js").ToLocalChecked()};
NullFactory.Reset(v8::Local<v8::Function>::Cast(Nan::Call(require, module, 1, args).ToLocalChecked()));
}

// Returns an SQLite3 result code.
Expand Down Expand Up @@ -100,7 +95,7 @@ int Database::OpenHandles(const char* filename) {
sqlite3_limit(db_handle, SQLITE_LIMIT_SQL_LENGTH, max_string_size);
sqlite3_limit(db_handle, SQLITE_LIMIT_COLUMN, 0x7fffffff);
sqlite3_limit(db_handle, SQLITE_LIMIT_COMPOUND_SELECT, 0x7fffffff);
sqlite3_limit(db_handle, SQLITE_LIMIT_VARIABLE_NUMBER, 0x7fffffff);
sqlite3_limit(db_handle, SQLITE_LIMIT_VARIABLE_NUMBER, parameter_mask);

return t_handles.open(db_handle);
}
Loading

0 comments on commit 0985983

Please sign in to comment.