duckdb 0.0.10 → 0.2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test_on_macos.yml +28 -0
- data/.github/workflows/test_on_ubuntu.yml +66 -0
- data/.github/workflows/test_on_windows.yml +37 -0
- data/.travis.yml +6 -5
- data/CHANGELOG.md +40 -1
- data/Gemfile.lock +5 -5
- data/README.md +86 -0
- data/ext/duckdb/appender.c +315 -0
- data/ext/duckdb/appender.h +17 -0
- data/ext/duckdb/blob.c +11 -0
- data/ext/duckdb/blob.h +14 -0
- data/ext/duckdb/duckdb.c +12 -0
- data/ext/duckdb/extconf.rb +4 -0
- data/ext/duckdb/prepared_statement.c +34 -2
- data/ext/duckdb/result.c +34 -2
- data/ext/duckdb/ruby-duckdb.h +19 -0
- data/lib/duckdb.rb +1 -0
- data/lib/duckdb/appender.rb +102 -0
- data/lib/duckdb/connection.rb +29 -0
- data/lib/duckdb/prepared_statement.rb +31 -3
- data/lib/duckdb/version.rb +1 -1
- metadata +14 -7
- data/.github/workflows/test_by_github.yml +0 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 309d9f5ae00645a93cd5bf8bd706f12391e1386336516d172e1963b963df5ab4
|
4
|
+
data.tar.gz: 8814748ae38a1b501d21b69f47b262124771ddc382dfcdbba3d7b3b88b3cb691
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96d87f3dfa1e8366e98a0a93e1f6506508c885131eb4287ec2463eea8040fb1cc87436a10871cecb83b524d97173443d9b1c42330a5f311c210ffb921b64a7d9
|
7
|
+
data.tar.gz: fed84bbde6a9c6b696e9a52bf769810788b7b49d845ddca065cafcfc535540d62127d395524ca4fee86cfba9213c82fe74204063ea674d14498d7f82f0c34e33
|
@@ -0,0 +1,28 @@
|
|
1
|
+
name: MacOS
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: macos-latest
|
8
|
+
strategy:
|
9
|
+
matrix:
|
10
|
+
ruby: ['2.5.8', '2.6.7', '2.7.3', '3.0.1', 'head']
|
11
|
+
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
|
15
|
+
- name: Set up Ruby
|
16
|
+
uses: ruby/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: ${{ matrix.ruby }}
|
19
|
+
|
20
|
+
- name: Install latest duckdb by brew
|
21
|
+
run: |
|
22
|
+
brew install duckdb
|
23
|
+
|
24
|
+
- name: Build and test with Rake with Ruby ${{ matrix.ruby }}
|
25
|
+
run: |
|
26
|
+
ruby -v
|
27
|
+
bundle install --jobs 4 --retry 3
|
28
|
+
bundle exec rake
|
@@ -0,0 +1,66 @@
|
|
1
|
+
name: Ubuntu
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
strategy:
|
10
|
+
matrix:
|
11
|
+
ruby: ['2.5.8', '2.6.7', '2.7.3', '3.0.1', 'head']
|
12
|
+
duckdb: ['0.2.6', '0.2.7']
|
13
|
+
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v2
|
16
|
+
|
17
|
+
- name: Set up Ruby
|
18
|
+
uses: ruby/setup-ruby@v1
|
19
|
+
with:
|
20
|
+
ruby-version: ${{ matrix.ruby }}
|
21
|
+
|
22
|
+
- name: duckdb 0.2.6 cache
|
23
|
+
id: duckdb-cache-v0_2_6
|
24
|
+
uses: actions/cache@v1.1.0
|
25
|
+
with:
|
26
|
+
path: duckdb-v0.2.6
|
27
|
+
key: ${{ runner.os }}-duckdb-v0_2_6_001
|
28
|
+
restore-keys: |
|
29
|
+
${{ runner.os }}-duckdb-v0_2_6
|
30
|
+
|
31
|
+
- name: duckdb 0.2.7 cache
|
32
|
+
id: duckdb-cache-v0_2_7
|
33
|
+
uses: actions/cache@v1.1.0
|
34
|
+
with:
|
35
|
+
path: duckdb-v0.2.7
|
36
|
+
key: ${{ runner.os }}-duckdb-v0_2_7_001
|
37
|
+
restore-keys: |
|
38
|
+
${{ runner.os }}-duckdb-v0_2_7
|
39
|
+
|
40
|
+
- name: Build duckdb 0.2.6
|
41
|
+
if: steps.duckdb-cache-v0_2_6.outputs.cache-hit != 'true'
|
42
|
+
run: |
|
43
|
+
git clone -b v0.2.6 https://github.com/cwida/duckdb.git duckdb-tmp-v0.2.6
|
44
|
+
cd duckdb-tmp-v0.2.6 && make && cd ..
|
45
|
+
rm -rf duckdb-v0.2.6
|
46
|
+
mkdir -p duckdb-v0.2.6/build/release/src duckdb-v0.2.6/src
|
47
|
+
cp -rip duckdb-tmp-v0.2.6/build/release/src/*.so duckdb-v0.2.6/build/release/src
|
48
|
+
cp -rip duckdb-tmp-v0.2.6/src/include duckdb-v0.2.6/src/
|
49
|
+
|
50
|
+
- name: Build duckdb 0.2.7
|
51
|
+
if: steps.duckdb-cache-v0_2_7.outputs.cache-hit != 'true'
|
52
|
+
run: |
|
53
|
+
git clone -b v0.2.7 https://github.com/cwida/duckdb.git duckdb-tmp-v0.2.7
|
54
|
+
cd duckdb-tmp-v0.2.7 && make && cd ..
|
55
|
+
rm -rf duckdb-v0.2.7
|
56
|
+
mkdir -p duckdb-v0.2.7/build/release/src duckdb-v0.2.7/src
|
57
|
+
cp -rip duckdb-tmp-v0.2.7/build/release/src/*.so duckdb-v0.2.7/build/release/src
|
58
|
+
cp -rip duckdb-tmp-v0.2.7/src/include duckdb-v0.2.7/src/
|
59
|
+
|
60
|
+
- name: Build and test with Rake with Ruby ${{ matrix.ruby }}
|
61
|
+
env:
|
62
|
+
DUCKDB_VERSION: ${{ matrix.duckdb }}
|
63
|
+
run: |
|
64
|
+
gem install bundler
|
65
|
+
bundle install --jobs 4 --retry 3
|
66
|
+
bundle exec rake -- --with-duckdb-include=${GITHUB_WORKSPACE}/duckdb-v${DUCKDB_VERSION}/src/include --with-duckdb-lib=${GITHUB_WORKSPACE}/duckdb-v${DUCKDB_VERSION}/build/release/src/
|
@@ -0,0 +1,37 @@
|
|
1
|
+
name: Windows
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: windows-latest
|
8
|
+
strategy:
|
9
|
+
matrix:
|
10
|
+
ruby: ['2.5.8', '2.6.7', '2.7.3', '3.0.1', 'head']
|
11
|
+
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
|
15
|
+
- name: Set up Ruby
|
16
|
+
uses: ruby/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: ${{ matrix.ruby }}
|
19
|
+
|
20
|
+
- name: download duckdb binary for windows 64bit
|
21
|
+
run: |
|
22
|
+
curl -OL https://github.com/duckdb/duckdb/releases/download/v0.2.6/libduckdb-windows-amd64.zip
|
23
|
+
|
24
|
+
- name: extract zip file
|
25
|
+
run: |
|
26
|
+
7z x libduckdb-windows-amd64.zip
|
27
|
+
|
28
|
+
- name: Build with Rake with Ruby ${{ matrix.ruby }}
|
29
|
+
run: |
|
30
|
+
bundle install
|
31
|
+
bundle exec rake build -- --with-duckdb-include=../../../.. --with-duckdb-lib=../../../..
|
32
|
+
|
33
|
+
# FIXME: rake test fails with LoadError
|
34
|
+
# C:/hostedtoolcache/windows/Ruby/2.7.3/x64/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:83:in `require': 126: The specified module could not be found. - D:/a/ruby-duckdb/ruby-duckdb/lib/duckdb/duckdb_native.so (LoadError)`
|
35
|
+
# - name: rake test
|
36
|
+
# run: |
|
37
|
+
# rake test
|
data/.travis.yml
CHANGED
@@ -2,16 +2,17 @@ language: ruby
|
|
2
2
|
cache:
|
3
3
|
bundler: true
|
4
4
|
directories:
|
5
|
-
- ${HOME}/duckdb-v0.2.
|
5
|
+
- ${HOME}/duckdb-v0.2.7
|
6
6
|
before_install:
|
7
7
|
- yes | gem update --system
|
8
|
-
- if [[ ! -d ${HOME}/duckdb-v0.2.
|
8
|
+
- if [[ ! -d ${HOME}/duckdb-v0.2.7/build ]]; then cd ${HOME} && git clone -b v0.2.7 https://github.com/cwida/duckdb.git duckdb-v0.2.7 && cd duckdb-v0.2.7 && make && cd ${TRAVIS_BUILD_DIR}; fi
|
9
9
|
|
10
10
|
env:
|
11
|
-
- DUCKDB_VERSION=0.2.
|
11
|
+
- DUCKDB_VERSION=0.2.7
|
12
12
|
rvm:
|
13
13
|
- 2.5.8
|
14
|
-
- 2.6.
|
15
|
-
- 2.7.
|
14
|
+
- 2.6.7
|
15
|
+
- 2.7.3
|
16
|
+
- 3.0.1
|
16
17
|
- ruby-head
|
17
18
|
script: bundle exec rake -- --with-duckdb-include=${HOME}/duckdb-v${DUCKDB_VERSION}/src/include --with-duckdb-lib=${HOME}/duckdb-v${DUCKDB_VERSION}/build/release/src/
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,48 @@
|
|
1
1
|
# ChangeLog
|
2
2
|
|
3
|
+
# 0.2.7.0
|
4
|
+
|
5
|
+
- call duckdb_free after calling duckdb_value_blob, duckdb_value_varchar.
|
6
|
+
- bump DuckDB to v0.2.7 in CI
|
7
|
+
- rake build on Windows in github actions.
|
8
|
+
- There is a issue (LoadError) when running rake test on Windows (in GitHub actions).
|
9
|
+
|
10
|
+
# 0.2.6.1
|
11
|
+
|
12
|
+
- add DuckDB::PreparedStatement#bind_int8
|
13
|
+
- DuckDB::Connection#appender accepts block.
|
14
|
+
- add DuckDB::Appender#append_row.
|
15
|
+
- support HUGEINT type.
|
16
|
+
- add DuckDB::Appender#append.
|
17
|
+
- rename PreparedStatement#bind_boolean to PreparedStatement#bind_bool.
|
18
|
+
- add DuckDB::Connection#appender.
|
19
|
+
|
20
|
+
# 0.2.6.0
|
21
|
+
|
22
|
+
- change version policy
|
23
|
+
- ruby-duckdb W.X.Y.Z supports duckdb W.X.Y
|
24
|
+
- add DuckDB::Appender
|
25
|
+
- bump DuckDB to v0.2.6 in CI.
|
26
|
+
- remove unnecessary duckdb header file from MacOS CI.
|
27
|
+
- add DuckDB::Connection#prepared_statement.
|
28
|
+
|
29
|
+
## 0.0.12
|
30
|
+
|
31
|
+
- bump DuckDB to v0.2.5
|
32
|
+
- support BLOB type (with DuckDB version 0.2.5 or later)
|
33
|
+
|
34
|
+
## 0.0.11
|
35
|
+
|
36
|
+
- fix failure in test_close in test/duckdb_test/database_test.rb because DuckDB error message was changed.
|
37
|
+
- bump DuckDb to v0.2.4
|
38
|
+
- add test CI with Ruby 3.0.0
|
39
|
+
- add test CI on MacOS.
|
40
|
+
- bunp DuckDB to v0.2.3
|
41
|
+
|
3
42
|
## 0.0.10
|
4
43
|
|
5
44
|
- bump DuckDB to v0.2.2
|
6
|
-
- fix to build failure on
|
45
|
+
- fix to build failure on MacOS.
|
7
46
|
|
8
47
|
## 0.0.9
|
9
48
|
|
data/Gemfile.lock
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
duckdb (0.0
|
4
|
+
duckdb (0.2.7.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
minitest (5.14.
|
10
|
-
rake (13.0.
|
9
|
+
minitest (5.14.4)
|
10
|
+
rake (13.0.3)
|
11
11
|
rake-compiler (1.1.1)
|
12
12
|
rake
|
13
13
|
|
14
14
|
PLATFORMS
|
15
|
-
|
15
|
+
x86_64-linux
|
16
16
|
|
17
17
|
DEPENDENCIES
|
18
18
|
bundler (~> 2.0)
|
@@ -22,4 +22,4 @@ DEPENDENCIES
|
|
22
22
|
rake-compiler
|
23
23
|
|
24
24
|
BUNDLED WITH
|
25
|
-
2.
|
25
|
+
2.2.17
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.com/suketa/ruby-duckdb)
|
4
4
|
[](https://github.com/suketa/ruby-duckdb/actions?query=workflow%3AUbuntu)
|
5
|
+
[](https://github.com/suketa/ruby-duckdb/actions?query=workflow%3AMacOS)
|
5
6
|
|
6
7
|
## Description
|
7
8
|
|
@@ -63,3 +64,88 @@ DuckDB::Database.open do |db|
|
|
63
64
|
end
|
64
65
|
end
|
65
66
|
```
|
67
|
+
|
68
|
+
### using BLOB column
|
69
|
+
|
70
|
+
BLOB is available with DuckDB v0.2.5 or later.
|
71
|
+
Use `DuckDB::Blob.new` or use sting#force_encoding(Encoding::BINARY)
|
72
|
+
|
73
|
+
```
|
74
|
+
require 'duckdb'
|
75
|
+
|
76
|
+
DuckDB::Database.open do |db|
|
77
|
+
db.connect do |con|
|
78
|
+
con.query('CREATE TABLE blob_table (binary_data BLOB)')
|
79
|
+
stmt = DuckDB::PreparedStatement.new(con, 'INSERT INTO blob_table VALUES ($1)')
|
80
|
+
|
81
|
+
stmt.bind(1, DuckDB::Blob.new("\0\1\2\3\4\5"))
|
82
|
+
# stmt.bind(1, "\0\1\2\3\4\5".force_encoding(Encoding::BINARY))
|
83
|
+
stmt.execute
|
84
|
+
|
85
|
+
result = con.query('SELECT binary_data FROM blob_table')
|
86
|
+
p result.first.first
|
87
|
+
end
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
### Appender
|
92
|
+
|
93
|
+
Appender class provides Ruby interface of [DuckDB Appender](https://duckdb.org/docs/data/appender)
|
94
|
+
|
95
|
+
```
|
96
|
+
require 'duckdb'
|
97
|
+
require 'benchmark'
|
98
|
+
|
99
|
+
def insert
|
100
|
+
DuckDB::Database.open do |db|
|
101
|
+
db.connect do |con|
|
102
|
+
con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
|
103
|
+
10000.times do
|
104
|
+
con.query("INSERT into users VALUES(1, 'Alice')")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def prepare
|
111
|
+
DuckDB::Database.open do |db|
|
112
|
+
db.connect do |con|
|
113
|
+
con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
|
114
|
+
stmt = con.prepared_statement('INSERT INTO users VALUES($1, $2)')
|
115
|
+
10000.times do
|
116
|
+
stmt.bind(1, 1)
|
117
|
+
stmt.bind(2, 'Alice')
|
118
|
+
stmt.execute
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def append
|
125
|
+
DuckDB::Database.open do |db|
|
126
|
+
db.connect do |con|
|
127
|
+
con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
|
128
|
+
appender = con.appender('users')
|
129
|
+
10000.times do
|
130
|
+
appender.begin_row
|
131
|
+
appender.append(1)
|
132
|
+
appender.append('Alice')
|
133
|
+
appender.end_row
|
134
|
+
end
|
135
|
+
appender.flush
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
Benchmark.bm(8) do |x|
|
141
|
+
x.report('insert') { insert }
|
142
|
+
x.report('prepare') { prepare }
|
143
|
+
x.report('append') { append }
|
144
|
+
end
|
145
|
+
|
146
|
+
# =>
|
147
|
+
# user system total real
|
148
|
+
# insert 0.637439 0.000000 0.637439 ( 0.637486 )
|
149
|
+
# prepare 0.230457 0.000000 0.230457 ( 0.230460 )
|
150
|
+
# append 0.012666 0.000000 0.012666 ( 0.012670 )
|
151
|
+
```
|
@@ -0,0 +1,315 @@
|
|
1
|
+
#include "ruby-duckdb.h"
|
2
|
+
|
3
|
+
#ifdef HAVE_DUCKDB_APPENDER_CREATE
|
4
|
+
|
5
|
+
static VALUE cDuckDBAppender;
|
6
|
+
|
7
|
+
static void deallocate(void *);
|
8
|
+
static VALUE allocate(VALUE klass);
|
9
|
+
static VALUE appender_initialize(VALUE klass, VALUE con, VALUE schema, VALUE table);
|
10
|
+
static VALUE appender_begin_row(VALUE self);
|
11
|
+
static VALUE appender_end_row(VALUE self);
|
12
|
+
static VALUE appender_append_bool(VALUE self, VALUE val);
|
13
|
+
static VALUE appender_append_int8(VALUE self, VALUE val);
|
14
|
+
static VALUE appender_append_int16(VALUE self, VALUE val);
|
15
|
+
static VALUE appender_append_int32(VALUE self, VALUE val);
|
16
|
+
static VALUE appender_append_int64(VALUE self, VALUE val);
|
17
|
+
static VALUE appender_append_uint8(VALUE self, VALUE val);
|
18
|
+
static VALUE appender_append_uint16(VALUE self, VALUE val);
|
19
|
+
static VALUE appender_append_uint32(VALUE self, VALUE val);
|
20
|
+
static VALUE appender_append_uint64(VALUE self, VALUE val);
|
21
|
+
static VALUE appender_append_float(VALUE self, VALUE val);
|
22
|
+
static VALUE appender_append_double(VALUE self, VALUE val);
|
23
|
+
static VALUE appender_append_varchar(VALUE self, VALUE val);
|
24
|
+
static VALUE appender_append_varchar_length(VALUE self, VALUE val, VALUE len);
|
25
|
+
static VALUE appender_append_blob(VALUE self, VALUE val);
|
26
|
+
static VALUE appender_append_null(VALUE self);
|
27
|
+
static VALUE appender_flush(VALUE self);
|
28
|
+
static VALUE appender_close(VALUE self);
|
29
|
+
|
30
|
+
static void deallocate(void * ctx)
|
31
|
+
{
|
32
|
+
rubyDuckDBAppender *p = (rubyDuckDBAppender *)ctx;
|
33
|
+
|
34
|
+
duckdb_appender_destroy(&(p->appender));
|
35
|
+
xfree(p);
|
36
|
+
}
|
37
|
+
|
38
|
+
static VALUE allocate(VALUE klass)
|
39
|
+
{
|
40
|
+
rubyDuckDBAppender *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBAppender));
|
41
|
+
return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
|
42
|
+
}
|
43
|
+
|
44
|
+
static VALUE appender_initialize(VALUE self, VALUE con, VALUE schema, VALUE table) {
|
45
|
+
|
46
|
+
rubyDuckDBConnection *ctxcon;
|
47
|
+
rubyDuckDBAppender *ctx;
|
48
|
+
char *pschema = 0;
|
49
|
+
|
50
|
+
if (!rb_obj_is_kind_of(con, cDuckDBConnection)) {
|
51
|
+
rb_raise(rb_eTypeError, "1st argument should be instance of DackDB::Connection");
|
52
|
+
}
|
53
|
+
|
54
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
55
|
+
Data_Get_Struct(con, rubyDuckDBConnection, ctxcon);
|
56
|
+
|
57
|
+
if (schema != Qnil) {
|
58
|
+
pschema = StringValuePtr(schema);
|
59
|
+
}
|
60
|
+
|
61
|
+
if (duckdb_appender_create(ctxcon->con, pschema, StringValuePtr(table), &(ctx->appender)) == DuckDBError) {
|
62
|
+
rb_raise(eDuckDBError, "failed to create appender");
|
63
|
+
}
|
64
|
+
return self;
|
65
|
+
}
|
66
|
+
|
67
|
+
static VALUE appender_begin_row(VALUE self) {
|
68
|
+
rubyDuckDBAppender *ctx;
|
69
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
70
|
+
|
71
|
+
if (duckdb_appender_begin_row(ctx->appender) == DuckDBError) {
|
72
|
+
rb_raise(eDuckDBError, "failed to flush");
|
73
|
+
}
|
74
|
+
return self;
|
75
|
+
}
|
76
|
+
|
77
|
+
static VALUE appender_end_row(VALUE self) {
|
78
|
+
rubyDuckDBAppender *ctx;
|
79
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
80
|
+
|
81
|
+
if (duckdb_appender_end_row(ctx->appender) == DuckDBError) {
|
82
|
+
rb_raise(eDuckDBError, "failed to flush");
|
83
|
+
}
|
84
|
+
return self;
|
85
|
+
}
|
86
|
+
|
87
|
+
static VALUE appender_append_bool(VALUE self, VALUE val) {
|
88
|
+
rubyDuckDBAppender *ctx;
|
89
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
90
|
+
|
91
|
+
if (val != Qtrue && val != Qfalse) {
|
92
|
+
rb_raise(rb_eArgError, "argument must be boolean");
|
93
|
+
}
|
94
|
+
|
95
|
+
if (duckdb_append_bool(ctx->appender, (val == Qtrue)) == DuckDBError) {
|
96
|
+
rb_raise(eDuckDBError, "failed to append boolean");
|
97
|
+
}
|
98
|
+
return self;
|
99
|
+
}
|
100
|
+
|
101
|
+
static VALUE appender_append_int8(VALUE self, VALUE val) {
|
102
|
+
rubyDuckDBAppender *ctx;
|
103
|
+
int8_t i8val = (int8_t)NUM2INT(val);
|
104
|
+
|
105
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
106
|
+
|
107
|
+
if (duckdb_append_int8(ctx->appender, i8val) == DuckDBError) {
|
108
|
+
rb_raise(eDuckDBError, "failed to append");
|
109
|
+
}
|
110
|
+
return self;
|
111
|
+
}
|
112
|
+
|
113
|
+
static VALUE appender_append_int16(VALUE self, VALUE val) {
|
114
|
+
rubyDuckDBAppender *ctx;
|
115
|
+
int16_t i16val = (int16_t)NUM2INT(val);
|
116
|
+
|
117
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
118
|
+
|
119
|
+
if (duckdb_append_int16(ctx->appender, i16val) == DuckDBError) {
|
120
|
+
rb_raise(eDuckDBError, "failed to append");
|
121
|
+
}
|
122
|
+
return self;
|
123
|
+
}
|
124
|
+
|
125
|
+
static VALUE appender_append_int32(VALUE self, VALUE val) {
|
126
|
+
rubyDuckDBAppender *ctx;
|
127
|
+
int32_t i32val = (int32_t)NUM2INT(val);
|
128
|
+
|
129
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
130
|
+
|
131
|
+
if (duckdb_append_int32(ctx->appender, i32val) == DuckDBError) {
|
132
|
+
rb_raise(eDuckDBError, "failed to append");
|
133
|
+
}
|
134
|
+
return self;
|
135
|
+
}
|
136
|
+
|
137
|
+
static VALUE appender_append_int64(VALUE self, VALUE val) {
|
138
|
+
rubyDuckDBAppender *ctx;
|
139
|
+
int64_t i64val = (int64_t)NUM2LL(val);
|
140
|
+
|
141
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
142
|
+
|
143
|
+
if (duckdb_append_int64(ctx->appender, i64val) == DuckDBError) {
|
144
|
+
rb_raise(eDuckDBError, "failed to append");
|
145
|
+
}
|
146
|
+
return self;
|
147
|
+
}
|
148
|
+
|
149
|
+
static VALUE appender_append_uint8(VALUE self, VALUE val) {
|
150
|
+
rubyDuckDBAppender *ctx;
|
151
|
+
int8_t ui8val = (uint8_t)NUM2UINT(val);
|
152
|
+
|
153
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
154
|
+
|
155
|
+
if (duckdb_append_uint8(ctx->appender, ui8val) == DuckDBError) {
|
156
|
+
rb_raise(eDuckDBError, "failed to append");
|
157
|
+
}
|
158
|
+
return self;
|
159
|
+
}
|
160
|
+
|
161
|
+
static VALUE appender_append_uint16(VALUE self, VALUE val) {
|
162
|
+
rubyDuckDBAppender *ctx;
|
163
|
+
uint16_t ui16val = (uint16_t)NUM2UINT(val);
|
164
|
+
|
165
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
166
|
+
|
167
|
+
if (duckdb_append_uint16(ctx->appender, ui16val) == DuckDBError) {
|
168
|
+
rb_raise(eDuckDBError, "failed to append");
|
169
|
+
}
|
170
|
+
return self;
|
171
|
+
}
|
172
|
+
|
173
|
+
static VALUE appender_append_uint32(VALUE self, VALUE val) {
|
174
|
+
rubyDuckDBAppender *ctx;
|
175
|
+
uint32_t ui32val = (uint32_t)NUM2UINT(val);
|
176
|
+
|
177
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
178
|
+
|
179
|
+
if (duckdb_append_uint32(ctx->appender, ui32val) == DuckDBError) {
|
180
|
+
rb_raise(eDuckDBError, "failed to append");
|
181
|
+
}
|
182
|
+
return self;
|
183
|
+
}
|
184
|
+
|
185
|
+
static VALUE appender_append_uint64(VALUE self, VALUE val) {
|
186
|
+
rubyDuckDBAppender *ctx;
|
187
|
+
uint64_t ui64val = (uint64_t)NUM2ULL(val);
|
188
|
+
|
189
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
190
|
+
|
191
|
+
if (duckdb_append_uint64(ctx->appender, ui64val) == DuckDBError) {
|
192
|
+
rb_raise(eDuckDBError, "failed to append");
|
193
|
+
}
|
194
|
+
return self;
|
195
|
+
}
|
196
|
+
|
197
|
+
static VALUE appender_append_float(VALUE self, VALUE val) {
|
198
|
+
rubyDuckDBAppender *ctx;
|
199
|
+
float fval = (float)NUM2DBL(val);
|
200
|
+
|
201
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
202
|
+
|
203
|
+
if (duckdb_append_float(ctx->appender, fval) == DuckDBError) {
|
204
|
+
rb_raise(eDuckDBError, "failed to append");
|
205
|
+
}
|
206
|
+
return self;
|
207
|
+
}
|
208
|
+
|
209
|
+
static VALUE appender_append_double(VALUE self, VALUE val) {
|
210
|
+
rubyDuckDBAppender *ctx;
|
211
|
+
double dval = NUM2DBL(val);
|
212
|
+
|
213
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
214
|
+
|
215
|
+
if (duckdb_append_double(ctx->appender, dval) == DuckDBError) {
|
216
|
+
rb_raise(eDuckDBError, "failed to append");
|
217
|
+
}
|
218
|
+
return self;
|
219
|
+
}
|
220
|
+
|
221
|
+
static VALUE appender_append_varchar(VALUE self, VALUE val) {
|
222
|
+
rubyDuckDBAppender *ctx;
|
223
|
+
char *pval = StringValuePtr(val);
|
224
|
+
|
225
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
226
|
+
|
227
|
+
if (duckdb_append_varchar(ctx->appender, pval) == DuckDBError) {
|
228
|
+
rb_raise(eDuckDBError, "failed to append");
|
229
|
+
}
|
230
|
+
return self;
|
231
|
+
}
|
232
|
+
|
233
|
+
static VALUE appender_append_varchar_length(VALUE self, VALUE val, VALUE len) {
|
234
|
+
rubyDuckDBAppender *ctx;
|
235
|
+
|
236
|
+
char *pval = StringValuePtr(val);
|
237
|
+
idx_t length = (idx_t)NUM2ULL(len);
|
238
|
+
|
239
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
240
|
+
|
241
|
+
if (duckdb_append_varchar_length(ctx->appender, pval, length) == DuckDBError) {
|
242
|
+
rb_raise(eDuckDBError, "failed to append");
|
243
|
+
}
|
244
|
+
return self;
|
245
|
+
}
|
246
|
+
|
247
|
+
static VALUE appender_append_blob(VALUE self, VALUE val) {
|
248
|
+
rubyDuckDBAppender *ctx;
|
249
|
+
|
250
|
+
char *pval = StringValuePtr(val);
|
251
|
+
idx_t length = (idx_t)RSTRING_LEN(val);
|
252
|
+
|
253
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
254
|
+
|
255
|
+
if (duckdb_append_blob(ctx->appender, (void *)pval, length) == DuckDBError) {
|
256
|
+
rb_raise(eDuckDBError, "failed to append");
|
257
|
+
}
|
258
|
+
return self;
|
259
|
+
}
|
260
|
+
|
261
|
+
static VALUE appender_append_null(VALUE self) {
|
262
|
+
rubyDuckDBAppender *ctx;
|
263
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
264
|
+
|
265
|
+
if (duckdb_append_null(ctx->appender) == DuckDBError) {
|
266
|
+
rb_raise(eDuckDBError, "failed to append");
|
267
|
+
}
|
268
|
+
return self;
|
269
|
+
}
|
270
|
+
|
271
|
+
static VALUE appender_flush(VALUE self) {
|
272
|
+
rubyDuckDBAppender *ctx;
|
273
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
274
|
+
|
275
|
+
if (duckdb_appender_flush(ctx->appender) == DuckDBError) {
|
276
|
+
rb_raise(eDuckDBError, "failed to flush");
|
277
|
+
}
|
278
|
+
return self;
|
279
|
+
}
|
280
|
+
|
281
|
+
static VALUE appender_close(VALUE self) {
|
282
|
+
rubyDuckDBAppender *ctx;
|
283
|
+
Data_Get_Struct(self, rubyDuckDBAppender, ctx);
|
284
|
+
|
285
|
+
if (duckdb_appender_close(ctx->appender) == DuckDBError) {
|
286
|
+
rb_raise(eDuckDBError, "failed to flush");
|
287
|
+
}
|
288
|
+
return self;
|
289
|
+
}
|
290
|
+
|
291
|
+
void init_duckdb_appender(void) {
|
292
|
+
cDuckDBAppender = rb_define_class_under(mDuckDB, "Appender", rb_cObject);
|
293
|
+
rb_define_alloc_func(cDuckDBAppender, allocate);
|
294
|
+
rb_define_method(cDuckDBAppender, "initialize", appender_initialize, 3);
|
295
|
+
rb_define_method(cDuckDBAppender, "begin_row", appender_begin_row, 0);
|
296
|
+
rb_define_method(cDuckDBAppender, "end_row", appender_end_row, 0);
|
297
|
+
rb_define_method(cDuckDBAppender, "append_bool", appender_append_bool, 1);
|
298
|
+
rb_define_method(cDuckDBAppender, "append_int8", appender_append_int8, 1);
|
299
|
+
rb_define_method(cDuckDBAppender, "append_int16", appender_append_int16, 1);
|
300
|
+
rb_define_method(cDuckDBAppender, "append_int32", appender_append_int32, 1);
|
301
|
+
rb_define_method(cDuckDBAppender, "append_int64", appender_append_int64, 1);
|
302
|
+
rb_define_method(cDuckDBAppender, "append_uint8", appender_append_uint8, 1);
|
303
|
+
rb_define_method(cDuckDBAppender, "append_uint16", appender_append_uint16, 1);
|
304
|
+
rb_define_method(cDuckDBAppender, "append_uint32", appender_append_uint32, 1);
|
305
|
+
rb_define_method(cDuckDBAppender, "append_uint64", appender_append_uint64, 1);
|
306
|
+
rb_define_method(cDuckDBAppender, "append_float", appender_append_float, 1);
|
307
|
+
rb_define_method(cDuckDBAppender, "append_double", appender_append_double, 1);
|
308
|
+
rb_define_method(cDuckDBAppender, "append_varchar", appender_append_varchar, 1);
|
309
|
+
rb_define_method(cDuckDBAppender, "append_varchar_length", appender_append_varchar_length, 2);
|
310
|
+
rb_define_method(cDuckDBAppender, "append_blob", appender_append_blob, 1);
|
311
|
+
rb_define_method(cDuckDBAppender, "append_null", appender_append_null, 0);
|
312
|
+
rb_define_method(cDuckDBAppender, "flush", appender_flush, 0);
|
313
|
+
rb_define_method(cDuckDBAppender, "close", appender_close, 0);
|
314
|
+
}
|
315
|
+
#endif /* HAVE_DUCKDB_APPENDER_CREATE */
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#ifndef RUBY_DUCKDB_APPENDER_H
|
2
|
+
#define RUBY_DUCKDB_APPENDER_H
|
3
|
+
|
4
|
+
#ifdef HAVE_DUCKDB_APPENDER_CREATE
|
5
|
+
|
6
|
+
struct _rubyDuckDBAppender {
|
7
|
+
duckdb_appender appender;
|
8
|
+
};
|
9
|
+
|
10
|
+
typedef struct _rubyDuckDBAppender rubyDuckDBAppender;
|
11
|
+
|
12
|
+
void init_duckdb_appender(void);
|
13
|
+
|
14
|
+
#endif
|
15
|
+
|
16
|
+
#endif
|
17
|
+
|
data/ext/duckdb/blob.c
ADDED
data/ext/duckdb/blob.h
ADDED
data/ext/duckdb/duckdb.c
CHANGED
@@ -12,4 +12,16 @@ Init_duckdb_native(void)
|
|
12
12
|
init_duckdb_connection();
|
13
13
|
init_duckdb_result();
|
14
14
|
init_duckdb_prepared_statement();
|
15
|
+
|
16
|
+
#ifdef HAVE_DUCKDB_VALUE_BLOB
|
17
|
+
|
18
|
+
init_duckdb_blob();
|
19
|
+
|
20
|
+
#endif /* HAVE_DUCKDB_VALUE_BLOB */
|
21
|
+
|
22
|
+
#ifdef HAVE_DUCKDB_APPENDER_CREATE
|
23
|
+
|
24
|
+
init_duckdb_appender();
|
25
|
+
|
26
|
+
#endif /* HAVE_DUCKDB_APPENDER_CREATE */
|
15
27
|
}
|
data/ext/duckdb/extconf.rb
CHANGED
@@ -2,5 +2,9 @@ require 'mkmf'
|
|
2
2
|
|
3
3
|
dir_config('duckdb')
|
4
4
|
if have_library('duckdb')
|
5
|
+
have_func('duckdb_value_blob', 'duckdb.h')
|
6
|
+
have_func('duckdb_bind_blob', 'duckdb.h')
|
7
|
+
have_func('duckdb_appender_create', 'duckdb.h')
|
8
|
+
have_func('duckdb_free', 'duckdb.h')
|
5
9
|
create_makefile('duckdb/duckdb_native')
|
6
10
|
end
|
@@ -70,7 +70,7 @@ static idx_t check_index(VALUE vidx)
|
|
70
70
|
return idx;
|
71
71
|
}
|
72
72
|
|
73
|
-
static VALUE
|
73
|
+
static VALUE duckdb_prepared_statement_bind_bool(VALUE self, VALUE vidx, VALUE val)
|
74
74
|
{
|
75
75
|
rubyDuckDBPreparedStatement *ctx;
|
76
76
|
idx_t idx = check_index(vidx);
|
@@ -86,6 +86,20 @@ static VALUE duckdb_prepared_statement_bind_boolean(VALUE self, VALUE vidx, VALU
|
|
86
86
|
return self;
|
87
87
|
}
|
88
88
|
|
89
|
+
static VALUE duckdb_prepared_statement_bind_int8(VALUE self, VALUE vidx, VALUE val)
|
90
|
+
{
|
91
|
+
rubyDuckDBPreparedStatement *ctx;
|
92
|
+
idx_t idx = check_index(vidx);
|
93
|
+
int8_t i8val = (int8_t)NUM2INT(val);
|
94
|
+
|
95
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
96
|
+
|
97
|
+
if (duckdb_bind_int8(ctx->prepared_statement, idx, i8val) == DuckDBError) {
|
98
|
+
rb_raise(eDuckDBError, "fail to bind %llu parameter", (unsigned long long)idx);
|
99
|
+
}
|
100
|
+
return self;
|
101
|
+
}
|
102
|
+
|
89
103
|
static VALUE duckdb_prepared_statement_bind_int16(VALUE self, VALUE vidx, VALUE val)
|
90
104
|
{
|
91
105
|
rubyDuckDBPreparedStatement *ctx;
|
@@ -168,6 +182,20 @@ static VALUE duckdb_prepared_statement_bind_varchar(VALUE self, VALUE vidx, VALU
|
|
168
182
|
return self;
|
169
183
|
}
|
170
184
|
|
185
|
+
#ifdef HAVE_DUCKDB_VALUE_BLOB
|
186
|
+
static VALUE duckdb_prepared_statement_bind_blob(VALUE self, VALUE vidx, VALUE blob)
|
187
|
+
{
|
188
|
+
rubyDuckDBPreparedStatement *ctx;
|
189
|
+
idx_t idx = check_index(vidx);
|
190
|
+
|
191
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
192
|
+
if (duckdb_bind_blob(ctx->prepared_statement, idx, (const void *)StringValuePtr(blob), (idx_t)RSTRING_LEN(blob)) == DuckDBError) {
|
193
|
+
rb_raise(eDuckDBError, "fail to bind %llu parameter", (unsigned long long)idx);
|
194
|
+
}
|
195
|
+
return self;
|
196
|
+
}
|
197
|
+
#endif /* HAVE_DUCKDB_VALUE_BLOB */
|
198
|
+
|
171
199
|
static VALUE duckdb_prepared_statement_bind_null(VALUE self, VALUE vidx)
|
172
200
|
{
|
173
201
|
rubyDuckDBPreparedStatement *ctx;
|
@@ -189,12 +217,16 @@ void init_duckdb_prepared_statement(void)
|
|
189
217
|
rb_define_method(cDuckDBPreparedStatement, "initialize", duckdb_prepared_statement_initialize, 2);
|
190
218
|
rb_define_method(cDuckDBPreparedStatement, "execute", duckdb_prepared_statement_execute, 0);
|
191
219
|
rb_define_method(cDuckDBPreparedStatement, "nparams", duckdb_prepared_statement_nparams, 0);
|
192
|
-
rb_define_method(cDuckDBPreparedStatement, "
|
220
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_bool", duckdb_prepared_statement_bind_bool, 2);
|
221
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_int8", duckdb_prepared_statement_bind_int8, 2);
|
193
222
|
rb_define_method(cDuckDBPreparedStatement, "bind_int16", duckdb_prepared_statement_bind_int16, 2);
|
194
223
|
rb_define_method(cDuckDBPreparedStatement, "bind_int32", duckdb_prepared_statement_bind_int32, 2);
|
195
224
|
rb_define_method(cDuckDBPreparedStatement, "bind_int64", duckdb_prepared_statement_bind_int64, 2);
|
196
225
|
rb_define_method(cDuckDBPreparedStatement, "bind_float", duckdb_prepared_statement_bind_float, 2);
|
197
226
|
rb_define_method(cDuckDBPreparedStatement, "bind_double", duckdb_prepared_statement_bind_double, 2);
|
198
227
|
rb_define_method(cDuckDBPreparedStatement, "bind_varchar", duckdb_prepared_statement_bind_varchar, 2);
|
228
|
+
#ifdef HAVE_DUCKDB_VALUE_BLOB
|
229
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_blob", duckdb_prepared_statement_bind_blob, 2);
|
230
|
+
#endif /* HAVE_DUCKDB_VALUE_BLOB */
|
199
231
|
rb_define_method(cDuckDBPreparedStatement, "bind_null", duckdb_prepared_statement_bind_null, 1);
|
200
232
|
}
|
data/ext/duckdb/result.c
CHANGED
@@ -52,6 +52,25 @@ static VALUE to_ruby_obj_double(duckdb_result *result, idx_t col_idx, idx_t row_
|
|
52
52
|
return DBL2NUM(dval);
|
53
53
|
}
|
54
54
|
|
55
|
+
#ifdef HAVE_DUCKDB_VALUE_BLOB
|
56
|
+
static VALUE to_ruby_obj_string_from_blob(duckdb_result *result, idx_t col_idx, idx_t row_idx)
|
57
|
+
{
|
58
|
+
VALUE str;
|
59
|
+
duckdb_blob bval = duckdb_value_blob(result, col_idx, row_idx);
|
60
|
+
str = rb_str_new(bval.data, bval.size);
|
61
|
+
|
62
|
+
if (bval.data) {
|
63
|
+
#ifdef HAVE_DUCKDB_FREE
|
64
|
+
duckdb_free(bval.data);
|
65
|
+
#else
|
66
|
+
free(bval.data);
|
67
|
+
#endif
|
68
|
+
}
|
69
|
+
|
70
|
+
return str;
|
71
|
+
}
|
72
|
+
#endif /* HAVE_DUCKDB_VALUE_BLOB */
|
73
|
+
|
55
74
|
static VALUE to_ruby_obj(duckdb_result *result, idx_t col_idx, idx_t row_idx)
|
56
75
|
{
|
57
76
|
char *p;
|
@@ -72,10 +91,23 @@ static VALUE to_ruby_obj(duckdb_result *result, idx_t col_idx, idx_t row_idx)
|
|
72
91
|
return to_ruby_obj_float(result, col_idx, row_idx);
|
73
92
|
case DUCKDB_TYPE_DOUBLE:
|
74
93
|
return to_ruby_obj_double(result, col_idx, row_idx);
|
94
|
+
#ifdef HAVE_DUCKDB_VALUE_BLOB
|
95
|
+
case DUCKDB_TYPE_BLOB:
|
96
|
+
return to_ruby_obj_string_from_blob(result, col_idx, row_idx);
|
97
|
+
#endif /* HAVE_DUCKDB_VALUE_BLOB */
|
75
98
|
default:
|
76
99
|
p = duckdb_value_varchar(result, col_idx, row_idx);
|
77
|
-
|
78
|
-
|
100
|
+
if (p) {
|
101
|
+
obj = rb_str_new2(p);
|
102
|
+
#ifdef HAVE_DUCKDB_FREE
|
103
|
+
duckdb_free(p);
|
104
|
+
#else
|
105
|
+
free(p);
|
106
|
+
#endif /* HAVE_DUCKDB_FREE */
|
107
|
+
if (result->columns[col_idx].type == DUCKDB_TYPE_HUGEINT) {
|
108
|
+
obj = rb_funcall(obj, rb_intern("to_i"), 0);
|
109
|
+
}
|
110
|
+
}
|
79
111
|
}
|
80
112
|
return obj;
|
81
113
|
}
|
data/ext/duckdb/ruby-duckdb.h
CHANGED
@@ -9,9 +9,28 @@
|
|
9
9
|
#include "./result.h"
|
10
10
|
#include "./prepared_statement.h"
|
11
11
|
|
12
|
+
#ifdef HAVE_DUCKDB_VALUE_BLOB
|
13
|
+
|
14
|
+
#include "./blob.h"
|
15
|
+
|
16
|
+
#endif /* HAVE_DUCKDB_VALUE_BLOB */
|
17
|
+
|
18
|
+
#ifdef HAVE_DUCKDB_APPENDER_CREATE
|
19
|
+
|
20
|
+
#include "./appender.h"
|
21
|
+
|
22
|
+
#endif /* HAVE_DUCKDB_APPENDER_CREATE */
|
23
|
+
|
12
24
|
extern VALUE mDuckDB;
|
13
25
|
extern VALUE cDuckDBDatabase;
|
14
26
|
extern VALUE cDuckDBConnection;
|
27
|
+
|
28
|
+
#ifdef HAVE_DUCKDB_VALUE_BLOB
|
29
|
+
|
30
|
+
extern VALUE cDuckDBBlob;
|
31
|
+
|
32
|
+
#endif /* HAVE_DUCKDB_VALUE_BLOB */
|
33
|
+
|
15
34
|
extern VALUE eDuckDBError;
|
16
35
|
|
17
36
|
#endif
|
data/lib/duckdb.rb
CHANGED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module DuckDB
|
4
|
+
if defined?(DuckDB::Appender)
|
5
|
+
# The DuckDB::Appender encapsulates DuckDB Appender.
|
6
|
+
#
|
7
|
+
# require 'duckdb'
|
8
|
+
# db = DuckDB::Database.open
|
9
|
+
# con = db.connect
|
10
|
+
# con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
|
11
|
+
# appender = con.appender('users')
|
12
|
+
# appender.append_row(1, 'Alice')
|
13
|
+
#
|
14
|
+
class Appender
|
15
|
+
RANGE_INT16 = (-32_768..32_767).freeze
|
16
|
+
RANGE_INT32 = (-2_147_483_648..2_147_483_647).freeze
|
17
|
+
RANGE_INT64 = (-9_223_372_036_854_775_808..9_223_372_036_854_775_807).freeze
|
18
|
+
|
19
|
+
def append_hugeint(value)
|
20
|
+
case value
|
21
|
+
when Integer
|
22
|
+
append_varchar(value.to_s)
|
23
|
+
else
|
24
|
+
rb_raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# appends value.
|
30
|
+
#
|
31
|
+
# require 'duckdb'
|
32
|
+
# db = DuckDB::Database.open
|
33
|
+
# con = db.connect
|
34
|
+
# con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
|
35
|
+
# appender = con.appender('users')
|
36
|
+
# appender.begin_row
|
37
|
+
# appender.append(1)
|
38
|
+
# appender.append('Alice')
|
39
|
+
# appender.end_row
|
40
|
+
#
|
41
|
+
def append(value)
|
42
|
+
case value
|
43
|
+
when NilClass
|
44
|
+
append_null
|
45
|
+
when Float
|
46
|
+
append_double(value)
|
47
|
+
when Integer
|
48
|
+
case value
|
49
|
+
when RANGE_INT16
|
50
|
+
append_int16(value)
|
51
|
+
when RANGE_INT32
|
52
|
+
append_int32(value)
|
53
|
+
when RANGE_INT64
|
54
|
+
append_int64(value)
|
55
|
+
else
|
56
|
+
append_hugeint(value)
|
57
|
+
end
|
58
|
+
when String
|
59
|
+
if defined?(DuckDB::Blob)
|
60
|
+
blob?(value) ? append_blob(value) : append_varchar(value)
|
61
|
+
else
|
62
|
+
append_varchar(value)
|
63
|
+
end
|
64
|
+
when TrueClass, FalseClass
|
65
|
+
append_bool(value)
|
66
|
+
when Time
|
67
|
+
append_varchar(value.strftime('%Y-%m-%d %H:%M:%S.%N'))
|
68
|
+
when Date
|
69
|
+
append_varchar(value.strftime('%Y-%m-%d'))
|
70
|
+
else
|
71
|
+
rb_raise(DuckDB::Error, "not supported type #{value} (value.class)")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# append a row.
|
77
|
+
#
|
78
|
+
# appender.append_row(1, 'Alice')
|
79
|
+
#
|
80
|
+
# is same as:
|
81
|
+
#
|
82
|
+
# appender.begin_row
|
83
|
+
# appender.append(1)
|
84
|
+
# appender.append('Alice')
|
85
|
+
# appender.end_row
|
86
|
+
#
|
87
|
+
def append_row(*args)
|
88
|
+
begin_row
|
89
|
+
args.each do |arg|
|
90
|
+
append(arg)
|
91
|
+
end
|
92
|
+
end_row
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def blob?(value)
|
98
|
+
value.instance_of?(DuckDB::Blob) || value.encoding == Encoding::BINARY
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/duckdb/connection.rb
CHANGED
@@ -44,6 +44,35 @@ module DuckDB
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
#
|
48
|
+
# returns PreparedStatement object.
|
49
|
+
# The first argument is SQL string.
|
50
|
+
#
|
51
|
+
def prepared_statement(str)
|
52
|
+
PreparedStatement.new(self, str)
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# returns Appender object.
|
57
|
+
# The first argument is table name
|
58
|
+
#
|
59
|
+
def appender(table)
|
60
|
+
appender = create_appender(table)
|
61
|
+
|
62
|
+
return appender unless block_given?
|
63
|
+
|
64
|
+
yield appender
|
65
|
+
appender.flush
|
66
|
+
appender.close
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def create_appender(table)
|
72
|
+
t1, t2 = table.split('.')
|
73
|
+
t2 ? Appender.new(self, t1, t2) : Appender.new(self, t2, t1)
|
74
|
+
end
|
75
|
+
|
47
76
|
alias execute query
|
48
77
|
alias open connect
|
49
78
|
alias close disconnect
|
@@ -13,6 +13,19 @@ module DuckDB
|
|
13
13
|
# stmt.execute
|
14
14
|
class PreparedStatement
|
15
15
|
|
16
|
+
RANGE_INT16 = -32768..32767
|
17
|
+
RANGE_INT32 = -2147483648..2147483647
|
18
|
+
RANGE_INT64 = -9223372036854775808..9223372036854775807
|
19
|
+
|
20
|
+
def bind_hugeint(i, value)
|
21
|
+
case value
|
22
|
+
when Integer
|
23
|
+
bind_varchar(i, value.to_s)
|
24
|
+
else
|
25
|
+
rb_raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
16
29
|
# binds i-th parameter with SQL prepared statement.
|
17
30
|
# The first argument is index of parameter. The index of first parameter is
|
18
31
|
# 1 not 0.
|
@@ -31,11 +44,20 @@ module DuckDB
|
|
31
44
|
when Float
|
32
45
|
bind_double(i, value)
|
33
46
|
when Integer
|
34
|
-
|
47
|
+
case value
|
48
|
+
when RANGE_INT64
|
49
|
+
bind_int64(i, value)
|
50
|
+
else
|
51
|
+
bind_varchar(i, value.to_s)
|
52
|
+
end
|
35
53
|
when String
|
36
|
-
|
54
|
+
if defined?(DuckDB::Blob)
|
55
|
+
blob?(value) ? bind_blob(i, value) : bind_varchar(i, value)
|
56
|
+
else
|
57
|
+
bind_varchar(i, value)
|
58
|
+
end
|
37
59
|
when TrueClass, FalseClass
|
38
|
-
|
60
|
+
bind_bool(i, value)
|
39
61
|
when Time
|
40
62
|
bind_varchar(i, value.strftime('%Y-%m-%d %H:%M:%S.%N'))
|
41
63
|
when Date
|
@@ -44,5 +66,11 @@ module DuckDB
|
|
44
66
|
rb_raise(DuckDB::Error, "not supported type #{value} (value.class)")
|
45
67
|
end
|
46
68
|
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def blob?(value)
|
73
|
+
value.instance_of?(DuckDB::Blob) || value.encoding == Encoding::BINARY
|
74
|
+
end
|
47
75
|
end
|
48
76
|
end
|
data/lib/duckdb/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: duckdb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.2.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Masaki Suketa
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -75,7 +75,9 @@ extensions:
|
|
75
75
|
- ext/duckdb/extconf.rb
|
76
76
|
extra_rdoc_files: []
|
77
77
|
files:
|
78
|
-
- ".github/workflows/
|
78
|
+
- ".github/workflows/test_on_macos.yml"
|
79
|
+
- ".github/workflows/test_on_ubuntu.yml"
|
80
|
+
- ".github/workflows/test_on_windows.yml"
|
79
81
|
- ".gitignore"
|
80
82
|
- ".travis.yml"
|
81
83
|
- CHANGELOG.md
|
@@ -87,6 +89,10 @@ files:
|
|
87
89
|
- bin/console
|
88
90
|
- bin/setup
|
89
91
|
- duckdb.gemspec
|
92
|
+
- ext/duckdb/appender.c
|
93
|
+
- ext/duckdb/appender.h
|
94
|
+
- ext/duckdb/blob.c
|
95
|
+
- ext/duckdb/blob.h
|
90
96
|
- ext/duckdb/connection.c
|
91
97
|
- ext/duckdb/connection.h
|
92
98
|
- ext/duckdb/database.c
|
@@ -101,6 +107,7 @@ files:
|
|
101
107
|
- ext/duckdb/result.h
|
102
108
|
- ext/duckdb/ruby-duckdb.h
|
103
109
|
- lib/duckdb.rb
|
110
|
+
- lib/duckdb/appender.rb
|
104
111
|
- lib/duckdb/connection.rb
|
105
112
|
- lib/duckdb/database.rb
|
106
113
|
- lib/duckdb/prepared_statement.rb
|
@@ -113,7 +120,7 @@ metadata:
|
|
113
120
|
homepage_uri: https://github.com/suketa/ruby-duckdb
|
114
121
|
source_code_uri: https://github.com/suketa/ruby-duckdb
|
115
122
|
changelog_uri: https://github.com/suketa/ruby-duckdb/blob/master/CHANGELOG.md
|
116
|
-
post_install_message:
|
123
|
+
post_install_message:
|
117
124
|
rdoc_options: []
|
118
125
|
require_paths:
|
119
126
|
- lib
|
@@ -128,8 +135,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
135
|
- !ruby/object:Gem::Version
|
129
136
|
version: '0'
|
130
137
|
requirements: []
|
131
|
-
rubygems_version: 3.2.
|
132
|
-
signing_key:
|
138
|
+
rubygems_version: 3.2.15
|
139
|
+
signing_key:
|
133
140
|
specification_version: 4
|
134
141
|
summary: This module is Ruby binding for DuckDB database engine.
|
135
142
|
test_files: []
|
@@ -1,66 +0,0 @@
|
|
1
|
-
name: Ubuntu
|
2
|
-
|
3
|
-
on: [push]
|
4
|
-
|
5
|
-
jobs:
|
6
|
-
build:
|
7
|
-
|
8
|
-
runs-on: ubuntu-latest
|
9
|
-
strategy:
|
10
|
-
matrix:
|
11
|
-
ruby: ['2.5.8', '2.6.6', '2.7.2', 'head']
|
12
|
-
duckdb: ['0.2.2', '0.2.1']
|
13
|
-
|
14
|
-
steps:
|
15
|
-
- uses: actions/checkout@v2
|
16
|
-
|
17
|
-
- name: Set up Ruby
|
18
|
-
uses: ruby/setup-ruby@v1
|
19
|
-
with:
|
20
|
-
ruby-version: ${{ matrix.ruby }}
|
21
|
-
|
22
|
-
- name: duckdb 0.2.2 cache
|
23
|
-
id: duckdb-cache-v0_2_2
|
24
|
-
uses: actions/cache@v1.1.0
|
25
|
-
with:
|
26
|
-
path: duckdb-v0.2.2
|
27
|
-
key: ${{ runner.os }}-duckdb-v0_2_2_001
|
28
|
-
restore-keys: |
|
29
|
-
${{ runner.os }}-duckdb-v0_2_2
|
30
|
-
|
31
|
-
- name: duckdb 0.2.1 cache
|
32
|
-
id: duckdb-cache-v0_2_1
|
33
|
-
uses: actions/cache@v1.1.0
|
34
|
-
with:
|
35
|
-
path: duckdb-v0.2.1
|
36
|
-
key: ${{ runner.os }}-duckdb-v0_2_1_001
|
37
|
-
restore-keys: |
|
38
|
-
${{ runner.os }}-duckdb-v0_2_1
|
39
|
-
|
40
|
-
- name: Build duckdb 0.2.2
|
41
|
-
if: steps.duckdb-cache-v0_2_2.outputs.cache-hit != 'true'
|
42
|
-
run: |
|
43
|
-
git clone -b v0.2.2 https://github.com/cwida/duckdb.git duckdb-tmp-v0.2.2
|
44
|
-
cd duckdb-tmp-v0.2.2 && make && cd ..
|
45
|
-
rm -rf duckdb-v0.2.2
|
46
|
-
mkdir -p duckdb-v0.2.2/build/release/src duckdb-v0.2.2/src
|
47
|
-
cp -rip duckdb-tmp-v0.2.2/build/release/src/*.so duckdb-v0.2.2/build/release/src
|
48
|
-
cp -rip duckdb-tmp-v0.2.2/src/include duckdb-v0.2.2/src/
|
49
|
-
|
50
|
-
- name: Build duckdb 0.2.1
|
51
|
-
if: steps.duckdb-cache-v0_2_1.outputs.cache-hit != 'true'
|
52
|
-
run: |
|
53
|
-
git clone -b v0.2.1 https://github.com/cwida/duckdb.git duckdb-tmp-v0.2.1
|
54
|
-
cd duckdb-tmp-v0.2.1 && make && cd ..
|
55
|
-
rm -rf duckdb-v0.2.1
|
56
|
-
mkdir -p duckdb-v0.2.1/build/release/src duckdb-v0.2.1/src
|
57
|
-
cp -rip duckdb-tmp-v0.2.1/build/release/src/*.so duckdb-v0.2.1/build/release/src
|
58
|
-
cp -rip duckdb-tmp-v0.2.1/src/include duckdb-v0.2.1/src/
|
59
|
-
|
60
|
-
- name: Build and test with Rake with Ruby ${{ matrix.ruby }}
|
61
|
-
env:
|
62
|
-
DUCKDB_VERSION: ${{ matrix.duckdb }}
|
63
|
-
run: |
|
64
|
-
gem install bundler
|
65
|
-
bundle install --jobs 4 --retry 3
|
66
|
-
bundle exec rake -- --with-duckdb-include=${GITHUB_WORKSPACE}/duckdb-v${DUCKDB_VERSION}/src/include --with-duckdb-lib=${GITHUB_WORKSPACE}/duckdb-v${DUCKDB_VERSION}/build/release/src/
|