Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DuckDB node.js API #1112

Merged
merged 37 commits into from Nov 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
380f590
first very limited version
hannes Jul 30, 2020
c6c6191
travis attempt
hannes Jul 30, 2020
fcfb687
typo
hannes Jul 30, 2020
b86da72
added test
hannes Jul 30, 2020
cc2cd8d
Merge remote-tracking branch 'origin/master' into nodejs
hannes Sep 27, 2020
8c2e653
switching to async nodejs implementation from sqlite
hannes Sep 27, 2020
b45baa5
fix build and add make file
hannes Sep 28, 2020
61ff468
auto-run configure
hannes Sep 28, 2020
224923a
making .each work
hannes Sep 28, 2020
8c237c4
fixed error handling, export ERROR symbol since the test cases look f…
hannes Sep 28, 2020
f5772bf
import tests
hannes Sep 28, 2020
c2027e6
some updates to make test cases run
hannes Sep 29, 2020
cc9d33a
Merge remote-tracking branch 'origin/master' into nodejs
hannes Sep 29, 2020
5e89fb2
some more tests pass
hannes Sep 29, 2020
ea4cbeb
Merge remote-tracking branch 'origin/master' into nodejs
hannes Oct 4, 2020
541b372
fixing more test cases, this node js stuff is a mess
hannes Oct 5, 2020
4c836a3
mc
hannes Oct 5, 2020
3dad2de
Merge branch 'master' into nodejs
hannes Oct 30, 2020
9750600
re-implemeting
hannes Nov 5, 2020
9bb8179
second part of js rewrite
hannes Nov 6, 2020
8e74f13
more ops but now needs refactor
hannes Nov 8, 2020
4cee6fb
each implemented as well. next some error handling
hannes Nov 8, 2020
48feba0
Merge remote-tracking branch 'origin/master' into nodejs
hannes Nov 8, 2020
cc5374e
Merge remote-tracking branch 'upstream/master' into nodejs
hannes Nov 8, 2020
edef087
fixing assertion for node debug
hannes Nov 8, 2020
81e8e94
intermediate commit very broken but moving boxes
hannes Nov 11, 2020
3b77c95
works again ^^ starting to grind through tests
hannes Nov 11, 2020
ceb82e8
mc
hannes Nov 11, 2020
8184e16
moar tests pass
hannes Nov 12, 2020
9601c83
Merge branch 'master' of github.com:cwida/duckdb into nodejs
hannes Nov 12, 2020
f263088
passing most test cases whee
hannes Nov 12, 2020
6f75c22
comma
hannes Nov 12, 2020
83fe2eb
added gha test for node
hannes Nov 12, 2020
34c37ba
looks like the pwd does not stay the same between build phases
hannes Nov 12, 2020
16cda8a
deps
hannes Nov 12, 2020
1aa43fe
dont build nodejs by default
hannes Nov 12, 2020
ae619b6
some minor updates
hannes Nov 12, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/main.yml
Expand Up @@ -672,3 +672,29 @@ jobs:
- name: Deploy
run: python scripts/asset-upload-gha.py duckdb_r_src.tar.gz=tools/rpkg/duckdb_*.tar.gz


nodejs:
name: node.js Package
runs-on: ubuntu-latest
needs: linux-debug

steps:
- uses: actions/checkout@v2

- uses: actions/setup-python@v2
with:
python-version: '3.7'


- name: Install
run: sudo apt-get install -y nodejs

- name: Build
run: |
cd tools/nodejs
./configure
make

- name: Test
run: |
make -C tools/nodejs test
2 changes: 1 addition & 1 deletion src/main/prepared_statement_data.cpp
Expand Up @@ -14,7 +14,7 @@ PreparedStatementData::~PreparedStatementData() {
void PreparedStatementData::Bind(vector<Value> values) {
// set parameters
if (values.size() != value_map.size()) {
throw BinderException("Parameter/argument count mismatch for prepared statement");
throw BinderException("Parameter/argument count mismatch for prepared statement. Expected %llu, got %llu", value_map.size(), values.size());
}
// bind the values
for (idx_t i = 0; i < values.size(); i++) {
Expand Down
68 changes: 34 additions & 34 deletions third_party/re2/re2/perl_groups.cc
Expand Up @@ -20,12 +20,12 @@ static const URange16 code3[] = { /* \w */
{ 0x61, 0x7a },
};
const UGroup perl_groups[] = {
{ "\\d", +1, code1, 1 },
{ "\\D", -1, code1, 1 },
{ "\\s", +1, code2, 3 },
{ "\\S", -1, code2, 3 },
{ "\\w", +1, code3, 4 },
{ "\\W", -1, code3, 4 },
{ "\\d", +1, code1, 1 , nullptr, 0},
{ "\\D", -1, code1, 1 , nullptr, 0},
{ "\\s", +1, code2, 3 , nullptr, 0},
{ "\\S", -1, code2, 3 , nullptr, 0},
{ "\\w", +1, code3, 4 , nullptr, 0},
{ "\\W", -1, code3, 4 , nullptr, 0},
};
const int num_perl_groups = 6;
static const URange16 code4[] = { /* [:alnum:] */
Expand Down Expand Up @@ -85,34 +85,34 @@ static const URange16 code17[] = { /* [:xdigit:] */
{ 0x61, 0x66 },
};
const UGroup posix_groups[] = {
{ "[:alnum:]", +1, code4, 3 },
{ "[:^alnum:]", -1, code4, 3 },
{ "[:alpha:]", +1, code5, 2 },
{ "[:^alpha:]", -1, code5, 2 },
{ "[:ascii:]", +1, code6, 1 },
{ "[:^ascii:]", -1, code6, 1 },
{ "[:blank:]", +1, code7, 2 },
{ "[:^blank:]", -1, code7, 2 },
{ "[:cntrl:]", +1, code8, 2 },
{ "[:^cntrl:]", -1, code8, 2 },
{ "[:digit:]", +1, code9, 1 },
{ "[:^digit:]", -1, code9, 1 },
{ "[:graph:]", +1, code10, 1 },
{ "[:^graph:]", -1, code10, 1 },
{ "[:lower:]", +1, code11, 1 },
{ "[:^lower:]", -1, code11, 1 },
{ "[:print:]", +1, code12, 1 },
{ "[:^print:]", -1, code12, 1 },
{ "[:punct:]", +1, code13, 4 },
{ "[:^punct:]", -1, code13, 4 },
{ "[:space:]", +1, code14, 2 },
{ "[:^space:]", -1, code14, 2 },
{ "[:upper:]", +1, code15, 1 },
{ "[:^upper:]", -1, code15, 1 },
{ "[:word:]", +1, code16, 4 },
{ "[:^word:]", -1, code16, 4 },
{ "[:xdigit:]", +1, code17, 3 },
{ "[:^xdigit:]", -1, code17, 3 },
{ "[:alnum:]", +1, code4, 3 , nullptr, 0},
{ "[:^alnum:]", -1, code4, 3 , nullptr, 0},
{ "[:alpha:]", +1, code5, 2 , nullptr, 0},
{ "[:^alpha:]", -1, code5, 2 , nullptr, 0},
{ "[:ascii:]", +1, code6, 1 , nullptr, 0},
{ "[:^ascii:]", -1, code6, 1 , nullptr, 0},
{ "[:blank:]", +1, code7, 2 , nullptr, 0},
{ "[:^blank:]", -1, code7, 2 , nullptr, 0},
{ "[:cntrl:]", +1, code8, 2 , nullptr, 0},
{ "[:^cntrl:]", -1, code8, 2 , nullptr, 0},
{ "[:digit:]", +1, code9, 1 , nullptr, 0},
{ "[:^digit:]", -1, code9, 1 , nullptr, 0},
{ "[:graph:]", +1, code10, 1 , nullptr, 0},
{ "[:^graph:]", -1, code10, 1 , nullptr, 0},
{ "[:lower:]", +1, code11, 1 , nullptr, 0},
{ "[:^lower:]", -1, code11, 1 , nullptr, 0},
{ "[:print:]", +1, code12, 1 , nullptr, 0},
{ "[:^print:]", -1, code12, 1 , nullptr, 0},
{ "[:punct:]", +1, code13, 4 , nullptr, 0},
{ "[:^punct:]", -1, code13, 4 , nullptr, 0},
{ "[:space:]", +1, code14, 2 , nullptr, 0},
{ "[:^space:]", -1, code14, 2 , nullptr, 0},
{ "[:upper:]", +1, code15, 1 , nullptr, 0},
{ "[:^upper:]", -1, code15, 1, nullptr, 0},
{ "[:word:]", +1, code16, 4 , nullptr, 0},
{ "[:^word:]", -1, code16, 4 , nullptr, 0},
{ "[:xdigit:]", +1, code17, 3 , nullptr, 0},
{ "[:^xdigit:]", -1, code17, 3 , nullptr, 0},
};
const int num_posix_groups = 28;

Expand Down
3 changes: 3 additions & 0 deletions tools/CMakeLists.txt
Expand Up @@ -9,3 +9,6 @@ endif()
if(BUILD_REST)
add_subdirectory(rest)
endif()

# just to make CLion happier
# add_subdirectory(nodejs)
8 changes: 8 additions & 0 deletions tools/nodejs/.gitignore
@@ -0,0 +1,8 @@
build-*
node_modules
src/duckdb.cpp
src/duckdb.hpp
lib/binding
package-lock.json
test/support/big.db*
test/tmp/*
7 changes: 7 additions & 0 deletions tools/nodejs/CMakeLists.txt
@@ -0,0 +1,7 @@
include_directories(src)
include_directories(/usr/local/lib/node_modules/node-addon-api/)
include_directories(/usr/local/Cellar/node/14.12.0/include/node/)

set(CMAKE_SHARED_LINKER_FLAGS "-bundle" "-undefined dynamic_lookup")
add_library(node_duckdb src/duckdb_node.cpp src/database.cpp src/connection.cpp src/statement.cpp src/utils.cpp)
target_link_libraries(node_duckdb duckdb_static Threads::Threads)
29 changes: 29 additions & 0 deletions tools/nodejs/Makefile
@@ -0,0 +1,29 @@
#https://www.gnu.org/prep/standards/html_node/Standard-Targets.html#Standard-Targets

all: build

src/duckdb.cpp:
./configure

./node_modules:
npm install --build-from-source

build: ./node_modules src/duckdb.cpp
./node_modules/.bin/node-pre-gyp build --loglevel=silent

debug: ./node_modules src/duckdb.cpp
./node_modules/.bin/node-pre-gyp build --debug --verbose

clean:
@rm -rf ./build
rm -rf lib/binding/
rm -f test/support/big.db-journal
rm -rf ./node_modules/
rm -rf src/duckdb.*

test:
npm test

check: test

.PHONY: test clean build
73 changes: 73 additions & 0 deletions tools/nodejs/binding.gyp
@@ -0,0 +1,73 @@
{
"targets": [
{
"target_name": "<(module_name)",
"sources": [
"src/duckdb_node.cpp",
"src/database.cpp",
"src/connection.cpp",
"src/statement.cpp",
"src/utils.cpp",
"src/duckdb.cpp" # comment this out to build against existing lib
],
"include_dirs": [
"<!@(node -p \"require('node-addon-api').include\")"
],
'defines': [
'NAPI_DISABLE_CPP_EXCEPTIONS=1',
"NAPI_VERSION=3"],
"cflags_cc": [
"-frtti",
"-fexceptions"
],
"cflags_cc!": [
"-fno-rrti"
"-fno-exceptions",
],
"cflags": [
"-frtti",
"-fexceptions"
],
"cflags!": [
"-fno-rrti"
"-fno-exceptions",
],
"xcode_settings": {
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
"GCC_ENABLE_CPP_RTTI": "YES",
"CLANG_CXX_LIBRARY": "libc++",
"MACOSX_DEPLOYMENT_TARGET": "10.15",
'CLANG_CXX_LANGUAGE_STANDARD':'c++11',
'OTHER_CFLAGS' : ['-fexceptions', '-frtti']

},
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1
}
},
# uncomment this to build against existing lib
# "libraries": [
# "/Users/hannes/source/duckdb/build/release/src/libduckdb_static.a",
# "/Users/hannes/source/duckdb/build/release/third_party/fmt/libfmt.a",
# "/Users/hannes/source/duckdb/build/release/third_party/libpg_query/libpg_query.a",
# "/Users/hannes/source/duckdb/build/release/third_party/utf8proc/libutf8proc.a",
# "/Users/hannes/source/duckdb/build/release/third_party/re2/libduckdb_re2.a"
#
# ]
},
{
"target_name": "action_after_build",
"type": "none",
"dependencies": [
"<!(node -p \"require('node-addon-api').gyp\")"
],
"copies": [
{
"files": [ "<(PRODUCT_DIR)/<(module_name).node" ],
"destination": "<(module_path)"
}
]
}
]
}
17 changes: 17 additions & 0 deletions tools/nodejs/configure
@@ -0,0 +1,17 @@
#!/bin/sh

set -e
set -x

cd `dirname $0`

if [ ! -f "../../scripts/amalgamation.py" ]; then
echo "Could find neither the amalgamation build script"
exit 1
fi

(cd ../.. && python scripts/amalgamation.py --source=tools/nodejs/src/duckdb.cpp --header=tools/nodejs/src/duckdb.hpp)
# (cd ../.. && python extension/parquet/parquet_amalgamation.py --source=tools/rpkg/src/parquet-extension.cpp --header=tools/rpkg/src/parquet-extension.h)
# cp src/parquet-extension.h src/parquet-extension.h.tmp
# sed 's/duckdb[.]hpp/duckdb.h/g' src/parquet-extension.h.tmp > src/parquet-extension.h
# rm src/parquet-extension.h.tmp
1 change: 1 addition & 0 deletions tools/nodejs/duckdb.js
@@ -0,0 +1 @@
module.exports = require('./lib/duckdb');
5 changes: 5 additions & 0 deletions tools/nodejs/lib/duckdb-binding.js
@@ -0,0 +1,5 @@
var binary = require('node-pre-gyp');
var path = require('path');
var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
var binding = require(binding_path);
module.exports = exports = binding;
74 changes: 74 additions & 0 deletions tools/nodejs/lib/duckdb.js
@@ -0,0 +1,74 @@
var path = require('path');
var duckdb = require('./duckdb-binding.js');
// TODO do we need the emitter?
//var EventEmitter = require('events').EventEmitter;
module.exports = exports = duckdb;


// some wrappers for compatibilities sake
var Database = duckdb.Database;
var Connection = duckdb.Connection;
var Statement = duckdb.Statement;


Connection.prototype.run = function(sql) {
var statement = new Statement(this, sql);
return statement.run.apply(statement, arguments);
}

Connection.prototype.all = function(sql) {
var statement = new Statement(this,sql);
return statement.all.apply(statement, arguments);
}

Connection.prototype.each = function(sql) {
var statement = new Statement(this, sql);
return statement.each.apply(statement, arguments);
}

Database.prototype.prepare = function() {
if (this.default_connection == undefined) {
this.default_connection = new duckdb.Connection(this);
}
return this.default_connection.prepare.apply(this.default_connection, arguments);
}

Database.prototype.run = function() {
if (this.default_connection == undefined) {
this.default_connection = new Connection(this);
}
this.default_connection.run.apply(this.default_connection, arguments);
return this;
}

Database.prototype.each = function() {
if (this.default_connection == undefined) {
this.default_connection = new Connection(this);
}
this.default_connection.each.apply(this.default_connection, arguments);
return this;
}

Database.prototype.all = function() {
if (this.default_connection == undefined) {
this.default_connection = new Connection(this);
}
this.default_connection.all.apply(this.default_connection, arguments);
return this;
}

Database.prototype.exec = function() {
if (this.default_connection == undefined) {
this.default_connection = new Connection(this);
}
this.default_connection.exec.apply(this.default_connection, arguments);
return this;
}

Database.prototype.get = function() {
throw 'get() is not implemented because its evil';
}

Statement.prototype.get = function() {
throw 'get() is not implemented because its evil';
}
1 change: 1 addition & 0 deletions tools/nodejs/lib/index.js
@@ -0,0 +1 @@
module.exports = require('./duckdb');