duckdb 0.0.5 → 0.0.9
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_by_github.yml +66 -0
- data/.travis.yml +17 -0
- data/CHANGELOG.md +43 -0
- data/Gemfile.lock +6 -6
- data/README.md +23 -0
- data/duckdb.gemspec +22 -19
- data/ext/duckdb/connection.c +38 -5
- data/ext/duckdb/connection.h +2 -0
- data/ext/duckdb/database.c +28 -6
- data/ext/duckdb/database.h +2 -0
- data/ext/duckdb/duckdb.c +3 -1
- data/ext/duckdb/error.c +2 -1
- data/ext/duckdb/extconf.rb +3 -2
- data/ext/duckdb/prepared_statement.c +200 -0
- data/ext/duckdb/prepared_statement.h +13 -0
- data/ext/duckdb/result.c +24 -13
- data/ext/duckdb/ruby-duckdb.h +1 -0
- data/lib/duckdb.rb +5 -1
- data/lib/duckdb/connection.rb +51 -0
- data/lib/duckdb/database.rb +77 -0
- data/lib/duckdb/prepared_statement.rb +48 -0
- data/lib/duckdb/result.rb +19 -0
- data/lib/duckdb/version.rb +3 -1
- metadata +24 -18
- data/TODO +0 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf81ef5bf9902f3bf915c375d87d4e56b5b753a290aad2020c20757b1aaaa86b
|
|
4
|
+
data.tar.gz: 8ef86fc4daba6aac83b83d48ee98613e4ca62fee7d9520610cf05b9afa4baaa8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c5006b08f821979184ee6edd3f7ef2a47339aab1487b6208cf4ea7af2d55e846fb5f197810e265f27ca327bd4004a2778569ed7bf7adf64e371df7eb7b11c253
|
|
7
|
+
data.tar.gz: 174232c0eef492cf9d98dc979dbe7f6960c57cb72d76d9adf2cee59cf5a09cc0643919044fa4e111c65a65b53e2bbe15b3e6891482630e72813ab73f9e6f464b
|
|
@@ -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.6', '2.7.2', 'head']
|
|
12
|
+
duckdb: ['0.2.0', '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.0 cache
|
|
23
|
+
id: duckdb-cache-v0_2_0
|
|
24
|
+
uses: actions/cache@v1.1.0
|
|
25
|
+
with:
|
|
26
|
+
path: duckdb-v0.2.0
|
|
27
|
+
key: ${{ runner.os }}-duckdb-v0_2_0_001
|
|
28
|
+
restore-keys: |
|
|
29
|
+
${{ runner.os }}-duckdb-v0_2_0
|
|
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.0
|
|
41
|
+
if: steps.duckdb-cache-v0_2_0.outputs.cache-hit != 'true'
|
|
42
|
+
run: |
|
|
43
|
+
git clone -b v0.2.0 https://github.com/cwida/duckdb.git duckdb-tmp-v0.2.0
|
|
44
|
+
cd duckdb-tmp-v0.2.0 && make && cd ..
|
|
45
|
+
rm -rf duckdb-v0.2.0
|
|
46
|
+
mkdir -p duckdb-v0.2.0/build/release/src duckdb-v0.2.0/src
|
|
47
|
+
cp -rip duckdb-tmp-v0.2.0/build/release/src/*.so duckdb-v0.2.0/build/release/src
|
|
48
|
+
cp -rip duckdb-tmp-v0.2.0/src/include duckdb-v0.2.0/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/
|
data/.travis.yml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
language: ruby
|
|
2
|
+
cache:
|
|
3
|
+
bundler: true
|
|
4
|
+
directories:
|
|
5
|
+
- ${HOME}/duckdb-v0.2.1
|
|
6
|
+
before_install:
|
|
7
|
+
- yes | gem update --system
|
|
8
|
+
- if [[ ! -d ${HOME}/duckdb-v0.2.1/build ]]; then cd ${HOME} && git clone -b v0.2.1 https://github.com/cwida/duckdb.git duckdb-v0.2.1 && cd duckdb-v0.2.1 && make && cd ${TRAVIS_BUILD_DIR}; fi
|
|
9
|
+
|
|
10
|
+
env:
|
|
11
|
+
- DUCKDB_VERSION=0.2.1
|
|
12
|
+
rvm:
|
|
13
|
+
- 2.5.8
|
|
14
|
+
- 2.6.6
|
|
15
|
+
- 2.7.2
|
|
16
|
+
- ruby-head
|
|
17
|
+
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,5 +1,48 @@
|
|
|
1
1
|
# ChangeLog
|
|
2
2
|
|
|
3
|
+
## 0.0.9
|
|
4
|
+
|
|
5
|
+
- bump DuckDB to v0.2.1
|
|
6
|
+
- bump Ruby to v2.7.2
|
|
7
|
+
- bunmp DuckDB to v0.2.0
|
|
8
|
+
|
|
9
|
+
## 0.0.8.1
|
|
10
|
+
|
|
11
|
+
- update Gemfile.lock
|
|
12
|
+
- unsupport Ruby 2.4
|
|
13
|
+
|
|
14
|
+
## 0.0.8
|
|
15
|
+
|
|
16
|
+
- remove test with Ruby 2.4.10
|
|
17
|
+
- bump DuckDB to v0.1.8
|
|
18
|
+
- bump DuckDB to v0.1.8
|
|
19
|
+
- bump DuckDB to v0.1.7
|
|
20
|
+
- current ruby-duckdb supports DuckDB version 0.1.5 and 0.1.6
|
|
21
|
+
- support Ruby 2.7.1
|
|
22
|
+
- bump DuckDB to v0.1.6
|
|
23
|
+
- current ruby-duckdb supports DuckDB version 0.1.5 and 0.1.6
|
|
24
|
+
- DuckDB::Connection#connect accepts block
|
|
25
|
+
- add DuckDB::Connection#connect
|
|
26
|
+
- DuckDB::Database#connect accepts block
|
|
27
|
+
- DuckDB::Database.open accepts block
|
|
28
|
+
- update duckdb.gemspec, required ruby version >= 2.4.0
|
|
29
|
+
|
|
30
|
+
## 0.0.7
|
|
31
|
+
|
|
32
|
+
- bump DuckDB to v0.1.5
|
|
33
|
+
- DuckDB version must be 0.1.5 or later.
|
|
34
|
+
- add DuckDB::Connection#connect, alias method open
|
|
35
|
+
- add DuckDB::Connection#disconnect, alias method close
|
|
36
|
+
- add DuckDB::Database#close
|
|
37
|
+
|
|
38
|
+
## 0.0.6
|
|
39
|
+
|
|
40
|
+
- add alias `execute` of `DuckDB::Connection#query`
|
|
41
|
+
- support `duckdb version 0.1.3`
|
|
42
|
+
- add `DuckDB:PreparedStatement`
|
|
43
|
+
- create CI (GitHub Actions / Travis-CI)
|
|
44
|
+
- create database only once in result_test.rb
|
|
45
|
+
|
|
3
46
|
## 0.0.5
|
|
4
47
|
|
|
5
48
|
- add `DuckDB::Error`
|
data/Gemfile.lock
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
duckdb (0.0.
|
|
4
|
+
duckdb (0.0.9)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
-
minitest (5.
|
|
10
|
-
rake (
|
|
11
|
-
rake-compiler (1.
|
|
9
|
+
minitest (5.14.2)
|
|
10
|
+
rake (13.0.1)
|
|
11
|
+
rake-compiler (1.1.1)
|
|
12
12
|
rake
|
|
13
13
|
|
|
14
14
|
PLATFORMS
|
|
@@ -18,8 +18,8 @@ DEPENDENCIES
|
|
|
18
18
|
bundler (~> 2.0)
|
|
19
19
|
duckdb!
|
|
20
20
|
minitest (~> 5.0)
|
|
21
|
-
rake (~>
|
|
21
|
+
rake (~> 13.0)
|
|
22
22
|
rake-compiler
|
|
23
23
|
|
|
24
24
|
BUNDLED WITH
|
|
25
|
-
2.
|
|
25
|
+
2.1.4
|
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# ruby-duckdb
|
|
2
2
|
|
|
3
|
+
[](https://travis-ci.com/suketa/ruby-duckdb)
|
|
4
|
+
[](https://github.com/suketa/ruby-duckdb/actions?query=workflow%3AUbuntu)
|
|
3
5
|
|
|
4
6
|
## Description
|
|
5
7
|
|
|
@@ -40,3 +42,24 @@ result.each do |row|
|
|
|
40
42
|
p row
|
|
41
43
|
end
|
|
42
44
|
```
|
|
45
|
+
|
|
46
|
+
Or, you can use block.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
require 'duckdb'
|
|
50
|
+
|
|
51
|
+
DuckDB::Database.open do |db|
|
|
52
|
+
db.connect do |con|
|
|
53
|
+
con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
|
|
54
|
+
|
|
55
|
+
con.query("INSERT into users VALUES(1, 'Alice')")
|
|
56
|
+
con.query("INSERT into users VALUES(2, 'Bob')")
|
|
57
|
+
con.query("INSERT into users VALUES(3, 'Cathy')")
|
|
58
|
+
|
|
59
|
+
result = con.query('SELECT * from users')
|
|
60
|
+
result.each do |row|
|
|
61
|
+
p row
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
```
|
data/duckdb.gemspec
CHANGED
|
@@ -1,32 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
-
require
|
|
5
|
+
require 'duckdb/version'
|
|
4
6
|
|
|
5
7
|
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name =
|
|
8
|
+
spec.name = 'duckdb'
|
|
7
9
|
spec.version = DuckDB::VERSION
|
|
8
|
-
spec.authors = [
|
|
9
|
-
spec.email = [
|
|
10
|
+
spec.authors = ['Masaki Suketa']
|
|
11
|
+
spec.email = ['masaki.suketa@nifty.ne.jp']
|
|
10
12
|
|
|
11
|
-
spec.summary =
|
|
12
|
-
spec.description =
|
|
13
|
-
spec.homepage =
|
|
14
|
-
spec.license =
|
|
13
|
+
spec.summary = 'This module is Ruby binding for DuckDB database engine.'
|
|
14
|
+
spec.description = 'This module is Ruby binding for DuckDB database engine. You must have the DuckDB engine installed to build/use this module.'
|
|
15
|
+
spec.homepage = 'https://github.com/suketa/ruby-duckdb'
|
|
16
|
+
spec.license = 'MIT'
|
|
15
17
|
|
|
16
|
-
spec.metadata[
|
|
17
|
-
spec.metadata[
|
|
18
|
-
spec.metadata[
|
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/suketa/ruby-duckdb'
|
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/suketa/ruby-duckdb/blob/master/CHANGELOG.md'
|
|
19
21
|
|
|
20
22
|
# Specify which files should be added to the gem when it is released.
|
|
21
23
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
22
|
-
spec.files
|
|
24
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
23
25
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
24
26
|
end
|
|
25
|
-
spec.require_paths = [
|
|
26
|
-
spec.extensions = [
|
|
27
|
+
spec.require_paths = ['lib']
|
|
28
|
+
spec.extensions = ['ext/duckdb/extconf.rb']
|
|
29
|
+
spec.required_ruby_version = '>= 2.5.0'
|
|
27
30
|
|
|
28
|
-
spec.add_development_dependency
|
|
29
|
-
spec.add_development_dependency
|
|
30
|
-
spec.add_development_dependency
|
|
31
|
-
spec.add_development_dependency
|
|
31
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
|
32
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
|
33
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
|
34
|
+
spec.add_development_dependency 'rake-compiler'
|
|
32
35
|
end
|
data/ext/duckdb/connection.c
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#include "ruby-duckdb.h"
|
|
2
2
|
|
|
3
|
-
static VALUE cDuckDBConnection;
|
|
4
|
-
|
|
5
3
|
static void deallocate(void *ctx)
|
|
6
4
|
{
|
|
7
5
|
rubyDuckDBConnection *p = (rubyDuckDBConnection *)ctx;
|
|
@@ -16,7 +14,8 @@ static VALUE allocate(VALUE klass)
|
|
|
16
14
|
return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
|
|
17
15
|
}
|
|
18
16
|
|
|
19
|
-
VALUE create_connection(VALUE oDuckDBDatabase)
|
|
17
|
+
VALUE create_connection(VALUE oDuckDBDatabase)
|
|
18
|
+
{
|
|
20
19
|
rubyDuckDB *ctxdb;
|
|
21
20
|
rubyDuckDBConnection *ctxcon;
|
|
22
21
|
VALUE obj;
|
|
@@ -34,8 +33,36 @@ VALUE create_connection(VALUE oDuckDBDatabase) {
|
|
|
34
33
|
return obj;
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
static VALUE
|
|
36
|
+
static VALUE duckdb_connection_disconnect(VALUE self)
|
|
37
|
+
{
|
|
38
|
+
rubyDuckDBConnection *ctx;
|
|
39
|
+
|
|
40
|
+
Data_Get_Struct(self, rubyDuckDBConnection, ctx);
|
|
41
|
+
duckdb_disconnect(&(ctx->con));
|
|
42
|
+
|
|
43
|
+
return self;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static VALUE duckdb_connection_connect(VALUE self, VALUE oDuckDBDatabase)
|
|
47
|
+
{
|
|
48
|
+
rubyDuckDBConnection *ctx;
|
|
49
|
+
rubyDuckDB *ctxdb;
|
|
50
|
+
|
|
51
|
+
if (!rb_obj_is_kind_of(oDuckDBDatabase, cDuckDBDatabase)) {
|
|
52
|
+
rb_raise(rb_eTypeError, "The first argument must be DuckDB::Database object.");
|
|
53
|
+
}
|
|
54
|
+
Data_Get_Struct(oDuckDBDatabase, rubyDuckDB, ctxdb);
|
|
55
|
+
Data_Get_Struct(self, rubyDuckDBConnection, ctx);
|
|
56
|
+
|
|
57
|
+
if (duckdb_connect(ctxdb->db, &(ctx->con)) == DuckDBError) {
|
|
58
|
+
rb_raise(eDuckDBError, "connection error");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return self;
|
|
62
|
+
}
|
|
38
63
|
|
|
64
|
+
static VALUE duckdb_connection_query_sql(VALUE self, VALUE str)
|
|
65
|
+
{
|
|
39
66
|
rubyDuckDBConnection *ctx;
|
|
40
67
|
rubyDuckDBResult *ctxr;
|
|
41
68
|
|
|
@@ -44,6 +71,10 @@ static VALUE duckdb_connection_query(VALUE self, VALUE str) {
|
|
|
44
71
|
Data_Get_Struct(self, rubyDuckDBConnection, ctx);
|
|
45
72
|
Data_Get_Struct(result, rubyDuckDBResult, ctxr);
|
|
46
73
|
|
|
74
|
+
if (!(ctx->con)) {
|
|
75
|
+
rb_raise(eDuckDBError, "Database connection closed");
|
|
76
|
+
}
|
|
77
|
+
|
|
47
78
|
if (duckdb_query(ctx->con, StringValueCStr(str), &(ctxr->result)) == DuckDBError) {
|
|
48
79
|
rb_raise(eDuckDBError, "%s", ctxr->result.error_message);
|
|
49
80
|
}
|
|
@@ -55,5 +86,7 @@ void init_duckdb_connection(void)
|
|
|
55
86
|
cDuckDBConnection = rb_define_class_under(mDuckDB, "Connection", rb_cObject);
|
|
56
87
|
rb_define_alloc_func(cDuckDBConnection, allocate);
|
|
57
88
|
|
|
58
|
-
rb_define_method(cDuckDBConnection, "
|
|
89
|
+
rb_define_method(cDuckDBConnection, "disconnect", duckdb_connection_disconnect, 0);
|
|
90
|
+
rb_define_private_method(cDuckDBConnection, "_connect", duckdb_connection_connect, 1);
|
|
91
|
+
rb_define_private_method(cDuckDBConnection, "query_sql", duckdb_connection_query_sql, 1);
|
|
59
92
|
}
|
data/ext/duckdb/connection.h
CHANGED
data/ext/duckdb/database.c
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
#include "ruby-duckdb.h"
|
|
2
2
|
|
|
3
|
+
static void close_database(rubyDuckDB *p)
|
|
4
|
+
{
|
|
5
|
+
duckdb_close(&(p->db));
|
|
6
|
+
}
|
|
7
|
+
|
|
3
8
|
static void deallocate(void * ctx)
|
|
4
9
|
{
|
|
5
10
|
rubyDuckDB *p = (rubyDuckDB *)ctx;
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
close_database(p);
|
|
8
13
|
xfree(p);
|
|
9
14
|
}
|
|
10
15
|
|
|
@@ -36,13 +41,30 @@ static VALUE duckdb_database_s_open(int argc, VALUE *argv, VALUE cDuckDBDatabase
|
|
|
36
41
|
return obj;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
static VALUE duckdb_database_connect(VALUE self)
|
|
44
|
+
static VALUE duckdb_database_connect(VALUE self)
|
|
45
|
+
{
|
|
40
46
|
return create_connection(self);
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
/*
|
|
50
|
+
* call-seq:
|
|
51
|
+
* duckdb.close -> DuckDB::Database
|
|
52
|
+
*
|
|
53
|
+
* closes DuckDB database.
|
|
54
|
+
*/
|
|
55
|
+
static VALUE duckdb_database_close(VALUE self)
|
|
56
|
+
{
|
|
57
|
+
rubyDuckDB *ctx;
|
|
58
|
+
Data_Get_Struct(self, rubyDuckDB, ctx);
|
|
59
|
+
close_database(ctx);
|
|
60
|
+
return self;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void init_duckdb_database(void)
|
|
64
|
+
{
|
|
65
|
+
cDuckDBDatabase = rb_define_class_under(mDuckDB, "Database", rb_cObject);
|
|
45
66
|
rb_define_alloc_func(cDuckDBDatabase, allocate);
|
|
46
|
-
rb_define_singleton_method(cDuckDBDatabase, "
|
|
47
|
-
|
|
67
|
+
rb_define_singleton_method(cDuckDBDatabase, "_open", duckdb_database_s_open, -1);
|
|
68
|
+
rb_define_private_method(cDuckDBDatabase, "_connect", duckdb_database_connect, 0);
|
|
69
|
+
rb_define_method(cDuckDBDatabase, "close", duckdb_database_close, 0);
|
|
48
70
|
}
|
data/ext/duckdb/database.h
CHANGED
data/ext/duckdb/duckdb.c
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
VALUE mDuckDB;
|
|
4
4
|
|
|
5
5
|
void
|
|
6
|
-
Init_duckdb_native(void)
|
|
6
|
+
Init_duckdb_native(void)
|
|
7
|
+
{
|
|
7
8
|
mDuckDB = rb_define_module("DuckDB");
|
|
8
9
|
|
|
9
10
|
init_duckdb_error();
|
|
10
11
|
init_duckdb_database();
|
|
11
12
|
init_duckdb_connection();
|
|
12
13
|
init_duckdb_result();
|
|
14
|
+
init_duckdb_prepared_statement();
|
|
13
15
|
}
|
data/ext/duckdb/error.c
CHANGED
data/ext/duckdb/extconf.rb
CHANGED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#include "ruby-duckdb.h"
|
|
2
|
+
|
|
3
|
+
static VALUE cDuckDBPreparedStatement;
|
|
4
|
+
|
|
5
|
+
static void deallocate(void *ctx)
|
|
6
|
+
{
|
|
7
|
+
rubyDuckDBPreparedStatement *p = (rubyDuckDBPreparedStatement *)ctx;
|
|
8
|
+
|
|
9
|
+
duckdb_destroy_prepare(&(p->prepared_statement));
|
|
10
|
+
xfree(p);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static VALUE allocate(VALUE klass)
|
|
14
|
+
{
|
|
15
|
+
rubyDuckDBPreparedStatement *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBPreparedStatement));
|
|
16
|
+
return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static VALUE duckdb_prepared_statement_initialize(VALUE self, VALUE con, VALUE query)
|
|
20
|
+
{
|
|
21
|
+
rubyDuckDBConnection *ctxcon;
|
|
22
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
23
|
+
|
|
24
|
+
if (!rb_obj_is_kind_of(con, cDuckDBConnection)) {
|
|
25
|
+
rb_raise(rb_eTypeError, "1st argument should be instance of DackDB::Connection");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
29
|
+
Data_Get_Struct(con, rubyDuckDBConnection, ctxcon);
|
|
30
|
+
|
|
31
|
+
if (duckdb_prepare(ctxcon->con, StringValuePtr(query), &(ctx->prepared_statement)) == DuckDBError) {
|
|
32
|
+
/* TODO: include query parameter information in error message. */
|
|
33
|
+
rb_raise(eDuckDBError, "failed to prepare statement");
|
|
34
|
+
}
|
|
35
|
+
return self;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static VALUE duckdb_prepared_statement_nparams(VALUE self)
|
|
39
|
+
{
|
|
40
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
41
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
42
|
+
|
|
43
|
+
if (duckdb_nparams(ctx->prepared_statement, &(ctx->nparams)) == DuckDBError) {
|
|
44
|
+
rb_raise(eDuckDBError, "failed to get number of parameters");
|
|
45
|
+
}
|
|
46
|
+
return rb_int2big(ctx->nparams);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
static VALUE duckdb_prepared_statement_execute(VALUE self)
|
|
51
|
+
{
|
|
52
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
53
|
+
rubyDuckDBResult *ctxr;
|
|
54
|
+
VALUE result = create_result();
|
|
55
|
+
|
|
56
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
57
|
+
Data_Get_Struct(result, rubyDuckDBResult, ctxr);
|
|
58
|
+
if (duckdb_execute_prepared(ctx->prepared_statement, &(ctxr->result)) == DuckDBError) {
|
|
59
|
+
rb_raise(eDuckDBError, "%s", ctxr->result.error_message);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static idx_t check_index(VALUE vidx)
|
|
65
|
+
{
|
|
66
|
+
idx_t idx = FIX2INT(vidx);
|
|
67
|
+
if (idx <= 0) {
|
|
68
|
+
rb_raise(rb_eArgError, "index of parameter must be greater than 0");
|
|
69
|
+
}
|
|
70
|
+
return idx;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static VALUE duckdb_prepared_statement_bind_boolean(VALUE self, VALUE vidx, VALUE val)
|
|
74
|
+
{
|
|
75
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
76
|
+
idx_t idx = check_index(vidx);
|
|
77
|
+
|
|
78
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
79
|
+
if (val != Qtrue && val != Qfalse) {
|
|
80
|
+
rb_raise(rb_eArgError, "binding value must be boolean");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (duckdb_bind_boolean(ctx->prepared_statement, idx, (val == Qtrue)) == DuckDBError) {
|
|
84
|
+
rb_raise(eDuckDBError, "fail to bind %ld parameter", idx);
|
|
85
|
+
}
|
|
86
|
+
return self;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static VALUE duckdb_prepared_statement_bind_int16(VALUE self, VALUE vidx, VALUE val)
|
|
90
|
+
{
|
|
91
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
92
|
+
idx_t idx = check_index(vidx);
|
|
93
|
+
int16_t i16val = NUM2INT(val);
|
|
94
|
+
|
|
95
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
96
|
+
|
|
97
|
+
if (duckdb_bind_int16(ctx->prepared_statement, idx, i16val) == DuckDBError) {
|
|
98
|
+
rb_raise(eDuckDBError, "fail to bind %ld parameter", idx);
|
|
99
|
+
}
|
|
100
|
+
return self;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static VALUE duckdb_prepared_statement_bind_int32(VALUE self, VALUE vidx, VALUE val)
|
|
104
|
+
{
|
|
105
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
106
|
+
idx_t idx = check_index(vidx);
|
|
107
|
+
int32_t i32val = NUM2LONG(val);
|
|
108
|
+
|
|
109
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
110
|
+
|
|
111
|
+
if (duckdb_bind_int32(ctx->prepared_statement, idx, i32val) == DuckDBError) {
|
|
112
|
+
rb_raise(eDuckDBError, "fail to bind %ld parameter", idx);
|
|
113
|
+
}
|
|
114
|
+
return self;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static VALUE duckdb_prepared_statement_bind_int64(VALUE self, VALUE vidx, VALUE val)
|
|
118
|
+
{
|
|
119
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
120
|
+
idx_t idx = check_index(vidx);
|
|
121
|
+
int64_t i64val = NUM2LL(val);
|
|
122
|
+
|
|
123
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
124
|
+
|
|
125
|
+
if (duckdb_bind_int64(ctx->prepared_statement, idx, i64val) == DuckDBError) {
|
|
126
|
+
rb_raise(eDuckDBError, "fail to bind %ld parameter", idx);
|
|
127
|
+
}
|
|
128
|
+
return self;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static VALUE duckdb_prepared_statement_bind_float(VALUE self, VALUE vidx, VALUE val)
|
|
132
|
+
{
|
|
133
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
134
|
+
idx_t idx = check_index(vidx);
|
|
135
|
+
double dbl = NUM2DBL(val);
|
|
136
|
+
|
|
137
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
138
|
+
|
|
139
|
+
if (duckdb_bind_float(ctx->prepared_statement, idx, (float)dbl) == DuckDBError) {
|
|
140
|
+
rb_raise(eDuckDBError, "fail to bind %ld parameter", idx);
|
|
141
|
+
}
|
|
142
|
+
return self;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static VALUE duckdb_prepared_statement_bind_double(VALUE self, VALUE vidx, VALUE val)
|
|
146
|
+
{
|
|
147
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
148
|
+
idx_t idx = check_index(vidx);
|
|
149
|
+
double dbl = NUM2DBL(val);
|
|
150
|
+
|
|
151
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
152
|
+
|
|
153
|
+
if (duckdb_bind_double(ctx->prepared_statement, idx, dbl) == DuckDBError) {
|
|
154
|
+
rb_raise(eDuckDBError, "fail to bind %ld parameter", idx);
|
|
155
|
+
}
|
|
156
|
+
return self;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static VALUE duckdb_prepared_statement_bind_varchar(VALUE self, VALUE vidx, VALUE str)
|
|
160
|
+
{
|
|
161
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
162
|
+
idx_t idx = check_index(vidx);
|
|
163
|
+
|
|
164
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
165
|
+
if (duckdb_bind_varchar(ctx->prepared_statement, idx, StringValuePtr(str)) == DuckDBError) {
|
|
166
|
+
rb_raise(eDuckDBError, "fail to bind %ld parameter", idx);
|
|
167
|
+
}
|
|
168
|
+
return self;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static VALUE duckdb_prepared_statement_bind_null(VALUE self, VALUE vidx)
|
|
172
|
+
{
|
|
173
|
+
rubyDuckDBPreparedStatement *ctx;
|
|
174
|
+
idx_t idx = check_index(vidx);
|
|
175
|
+
|
|
176
|
+
Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
|
|
177
|
+
if (duckdb_bind_null(ctx->prepared_statement, idx) == DuckDBError) {
|
|
178
|
+
rb_raise(eDuckDBError, "fail to bind %ld parameter", idx);
|
|
179
|
+
}
|
|
180
|
+
return self;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
void init_duckdb_prepared_statement(void)
|
|
184
|
+
{
|
|
185
|
+
cDuckDBPreparedStatement = rb_define_class_under(mDuckDB, "PreparedStatement", rb_cObject);
|
|
186
|
+
|
|
187
|
+
rb_define_alloc_func(cDuckDBPreparedStatement, allocate);
|
|
188
|
+
|
|
189
|
+
rb_define_method(cDuckDBPreparedStatement, "initialize", duckdb_prepared_statement_initialize, 2);
|
|
190
|
+
rb_define_method(cDuckDBPreparedStatement, "execute", duckdb_prepared_statement_execute, 0);
|
|
191
|
+
rb_define_method(cDuckDBPreparedStatement, "nparams", duckdb_prepared_statement_nparams, 0);
|
|
192
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_boolean", duckdb_prepared_statement_bind_boolean, 2);
|
|
193
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_int16", duckdb_prepared_statement_bind_int16, 2);
|
|
194
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_int32", duckdb_prepared_statement_bind_int32, 2);
|
|
195
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_int64", duckdb_prepared_statement_bind_int64, 2);
|
|
196
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_float", duckdb_prepared_statement_bind_float, 2);
|
|
197
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_double", duckdb_prepared_statement_bind_double, 2);
|
|
198
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_varchar", duckdb_prepared_statement_bind_varchar, 2);
|
|
199
|
+
rb_define_method(cDuckDBPreparedStatement, "bind_null", duckdb_prepared_statement_bind_null, 1);
|
|
200
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#ifndef RUBY_DUCKDB_PREPARED_STATEMENT_H
|
|
2
|
+
#define RUBY_DUCKDB_PREPARED_STATEMENT_H
|
|
3
|
+
|
|
4
|
+
struct _rubyDuckDBPreparedStatement {
|
|
5
|
+
duckdb_prepared_statement prepared_statement;
|
|
6
|
+
idx_t nparams;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
typedef struct _rubyDuckDBPreparedStatement rubyDuckDBPreparedStatement;
|
|
10
|
+
|
|
11
|
+
void init_duckdb_prepared_statement(void);
|
|
12
|
+
|
|
13
|
+
#endif
|
data/ext/duckdb/result.c
CHANGED
|
@@ -16,37 +16,44 @@ static VALUE allocate(VALUE klass)
|
|
|
16
16
|
return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
static VALUE to_ruby_obj_boolean(duckdb_result *result,
|
|
19
|
+
static VALUE to_ruby_obj_boolean(duckdb_result *result, idx_t col_idx, idx_t row_idx)
|
|
20
|
+
{
|
|
20
21
|
bool bval = duckdb_value_boolean(result, col_idx, row_idx);
|
|
21
22
|
return bval ? Qtrue : Qnil;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
static VALUE to_ruby_obj_smallint(duckdb_result *result,
|
|
25
|
+
static VALUE to_ruby_obj_smallint(duckdb_result *result, idx_t col_idx, idx_t row_idx)
|
|
26
|
+
{
|
|
25
27
|
int16_t i16val = duckdb_value_int16(result, col_idx, row_idx);
|
|
26
28
|
return INT2FIX(i16val);
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
static VALUE to_ruby_obj_integer(duckdb_result *result,
|
|
31
|
+
static VALUE to_ruby_obj_integer(duckdb_result *result, idx_t col_idx, idx_t row_idx)
|
|
32
|
+
{
|
|
30
33
|
int32_t i32val = duckdb_value_int32(result, col_idx, row_idx);
|
|
31
34
|
return INT2NUM(i32val);
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
static VALUE to_ruby_obj_bigint(duckdb_result *result,
|
|
37
|
+
static VALUE to_ruby_obj_bigint(duckdb_result *result, idx_t col_idx, idx_t row_idx)
|
|
38
|
+
{
|
|
35
39
|
int64_t i64val = duckdb_value_int64(result, col_idx, row_idx);
|
|
36
40
|
return rb_int2big(i64val);
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
static VALUE to_ruby_obj_float(duckdb_result *result,
|
|
43
|
+
static VALUE to_ruby_obj_float(duckdb_result *result, idx_t col_idx, idx_t row_idx)
|
|
44
|
+
{
|
|
40
45
|
float fval = duckdb_value_float(result, col_idx, row_idx);
|
|
41
46
|
return DBL2NUM(fval);
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
static VALUE to_ruby_obj_double(duckdb_result *result,
|
|
49
|
+
static VALUE to_ruby_obj_double(duckdb_result *result, idx_t col_idx, idx_t row_idx)
|
|
50
|
+
{
|
|
45
51
|
double dval = duckdb_value_double(result, col_idx, row_idx);
|
|
46
52
|
return DBL2NUM(dval);
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
static VALUE to_ruby_obj(duckdb_result *result,
|
|
55
|
+
static VALUE to_ruby_obj(duckdb_result *result, idx_t col_idx, idx_t row_idx)
|
|
56
|
+
{
|
|
50
57
|
char *p;
|
|
51
58
|
VALUE obj = Qnil;
|
|
52
59
|
if (result->columns[col_idx].nullmask[row_idx]) {
|
|
@@ -73,8 +80,9 @@ static VALUE to_ruby_obj(duckdb_result *result, size_t col_idx, size_t row_idx)
|
|
|
73
80
|
return obj;
|
|
74
81
|
}
|
|
75
82
|
|
|
76
|
-
static VALUE row_array(rubyDuckDBResult *ctx,
|
|
77
|
-
|
|
83
|
+
static VALUE row_array(rubyDuckDBResult *ctx, idx_t row_idx)
|
|
84
|
+
{
|
|
85
|
+
idx_t col_idx;
|
|
78
86
|
VALUE ary = rb_ary_new2(ctx->result.column_count);
|
|
79
87
|
for(col_idx = 0; col_idx < ctx->result.column_count; col_idx++) {
|
|
80
88
|
rb_ary_store(ary, col_idx, to_ruby_obj(&(ctx->result), col_idx, row_idx));
|
|
@@ -82,16 +90,18 @@ static VALUE row_array(rubyDuckDBResult *ctx, size_t row_idx) {
|
|
|
82
90
|
return ary;
|
|
83
91
|
}
|
|
84
92
|
|
|
85
|
-
static VALUE duckdb_result_row_size(VALUE oDuckDBResult, VALUE args, VALUE obj)
|
|
93
|
+
static VALUE duckdb_result_row_size(VALUE oDuckDBResult, VALUE args, VALUE obj)
|
|
94
|
+
{
|
|
86
95
|
rubyDuckDBResult *ctx;
|
|
87
96
|
Data_Get_Struct(oDuckDBResult, rubyDuckDBResult, ctx);
|
|
88
97
|
|
|
89
98
|
return LONG2FIX(ctx->result.row_count);
|
|
90
99
|
}
|
|
91
100
|
|
|
92
|
-
static VALUE duckdb_result_each(VALUE oDuckDBResult)
|
|
101
|
+
static VALUE duckdb_result_each(VALUE oDuckDBResult)
|
|
102
|
+
{
|
|
93
103
|
rubyDuckDBResult *ctx;
|
|
94
|
-
|
|
104
|
+
idx_t row_idx = 0;
|
|
95
105
|
|
|
96
106
|
RETURN_SIZED_ENUMERATOR(oDuckDBResult, 0, 0, duckdb_result_row_size);
|
|
97
107
|
|
|
@@ -102,7 +112,8 @@ static VALUE duckdb_result_each(VALUE oDuckDBResult) {
|
|
|
102
112
|
return oDuckDBResult;
|
|
103
113
|
}
|
|
104
114
|
|
|
105
|
-
VALUE create_result(void)
|
|
115
|
+
VALUE create_result(void)
|
|
116
|
+
{
|
|
106
117
|
return allocate(cDuckDBResult);
|
|
107
118
|
}
|
|
108
119
|
|
data/ext/duckdb/ruby-duckdb.h
CHANGED
data/lib/duckdb.rb
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
require 'duckdb/version'
|
|
2
1
|
require 'duckdb/duckdb_native'
|
|
2
|
+
require 'duckdb/version'
|
|
3
|
+
require 'duckdb/database'
|
|
4
|
+
require 'duckdb/connection'
|
|
3
5
|
require 'duckdb/result'
|
|
6
|
+
require 'duckdb/prepared_statement'
|
|
4
7
|
|
|
8
|
+
# DuckDB provides Ruby interface of DuckDB.
|
|
5
9
|
module DuckDB
|
|
6
10
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module DuckDB
|
|
2
|
+
# The DuckDB::Connection encapsulates connection with DuckDB database.
|
|
3
|
+
#
|
|
4
|
+
# require 'duckdb'
|
|
5
|
+
# db = DuckDB::Database.open
|
|
6
|
+
# con = db.connect
|
|
7
|
+
# con.query(sql)
|
|
8
|
+
class Connection
|
|
9
|
+
#
|
|
10
|
+
# executes sql with args.
|
|
11
|
+
# The first argument sql must be SQL string.
|
|
12
|
+
# The rest arguments are parameters of SQL string.
|
|
13
|
+
# The parameters must be '?' in SQL statement.
|
|
14
|
+
#
|
|
15
|
+
# require 'duckdb'
|
|
16
|
+
# db = DuckDB::Database.open('duckdb_file')
|
|
17
|
+
# con = db.connect
|
|
18
|
+
# users = con.query('SELECT * FROM users')
|
|
19
|
+
# sql = 'SELECT * FROM users WHERE name = ? AND email = ?'
|
|
20
|
+
# dave = con.query(sql, 'Dave', 'dave@example.com')
|
|
21
|
+
#
|
|
22
|
+
def query(sql, *args)
|
|
23
|
+
return query_sql(sql) if args.empty?
|
|
24
|
+
|
|
25
|
+
stmt = PreparedStatement.new(self, sql)
|
|
26
|
+
args.each_with_index do |arg, i|
|
|
27
|
+
stmt.bind(i + 1, arg)
|
|
28
|
+
end
|
|
29
|
+
stmt.execute
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# connects DuckDB database
|
|
34
|
+
# The first argument is DuckDB::Database object
|
|
35
|
+
#
|
|
36
|
+
def connect(db)
|
|
37
|
+
conn = _connect(db)
|
|
38
|
+
return conn unless block_given?
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
yield conn
|
|
42
|
+
ensure
|
|
43
|
+
conn.disconnect
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
alias execute query
|
|
48
|
+
alias open connect
|
|
49
|
+
alias close disconnect
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module DuckDB
|
|
2
|
+
# The Database class encapsulates a DuckDB database.
|
|
3
|
+
#
|
|
4
|
+
# The usage is as follows:
|
|
5
|
+
#
|
|
6
|
+
# require 'duckdb'
|
|
7
|
+
#
|
|
8
|
+
# db = DuckDB::Database.open # database in memory
|
|
9
|
+
# con = db.connect
|
|
10
|
+
#
|
|
11
|
+
# con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
|
|
12
|
+
#
|
|
13
|
+
# con.query("INSERT into users VALUES(1, 'Alice')")
|
|
14
|
+
# con.query("INSERT into users VALUES(2, 'Bob')")
|
|
15
|
+
# con.query("INSERT into users VALUES(3, 'Cathy')")
|
|
16
|
+
#
|
|
17
|
+
# result = con.query('SELECT * from users')
|
|
18
|
+
# result.each do |row|
|
|
19
|
+
# p row
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
class Database
|
|
23
|
+
private_class_method :_open
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
##
|
|
27
|
+
# Opens database.
|
|
28
|
+
# The first argument is DuckDB database file path to open.
|
|
29
|
+
# If there is no argument, the method opens DuckDB database in memory.
|
|
30
|
+
# The method yields block if block is given.
|
|
31
|
+
#
|
|
32
|
+
# DuckDB::Database.open('duckdb_database.db') #=> DuckDB::Database
|
|
33
|
+
#
|
|
34
|
+
# DuckDB::Database.open #=> opens DuckDB::Database in memory.
|
|
35
|
+
#
|
|
36
|
+
# DuckDB::Database.open do |db|
|
|
37
|
+
# con = db.connect
|
|
38
|
+
# con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
def open(*args)
|
|
42
|
+
db = _open(*args)
|
|
43
|
+
return db unless block_given?
|
|
44
|
+
|
|
45
|
+
begin
|
|
46
|
+
yield db
|
|
47
|
+
ensure
|
|
48
|
+
db.close
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# connects database.
|
|
55
|
+
#
|
|
56
|
+
# The method yields block and disconnects the database if block given
|
|
57
|
+
#
|
|
58
|
+
# db = DuckDB::Database.open
|
|
59
|
+
#
|
|
60
|
+
# con = db.connect # => DuckDB::Connection
|
|
61
|
+
#
|
|
62
|
+
# db.connect do |con|
|
|
63
|
+
# con.query('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
def connect
|
|
67
|
+
conn = _connect
|
|
68
|
+
return conn unless block_given?
|
|
69
|
+
|
|
70
|
+
begin
|
|
71
|
+
yield conn
|
|
72
|
+
ensure
|
|
73
|
+
conn.disconnect
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
|
|
3
|
+
module DuckDB
|
|
4
|
+
# The DuckDB::PreparedStatement encapsulates connection with DuckDB prepared
|
|
5
|
+
# statement.
|
|
6
|
+
#
|
|
7
|
+
# require 'duckdb'
|
|
8
|
+
# db = DuckDB::Database.open('duckdb_database')
|
|
9
|
+
# con = db.connect
|
|
10
|
+
# sql ='SELECT name, email FROM users WHERE email = ?'
|
|
11
|
+
# stmt = PreparedStatement.new(con, sql)
|
|
12
|
+
# stmt.bind(1, 'email@example.com')
|
|
13
|
+
# stmt.execute
|
|
14
|
+
class PreparedStatement
|
|
15
|
+
|
|
16
|
+
# binds i-th parameter with SQL prepared statement.
|
|
17
|
+
# The first argument is index of parameter. The index of first parameter is
|
|
18
|
+
# 1 not 0.
|
|
19
|
+
# The second argument value is the value of prepared statement parameter.
|
|
20
|
+
#
|
|
21
|
+
# require 'duckdb'
|
|
22
|
+
# db = DuckDB::Database.open('duckdb_database')
|
|
23
|
+
# con = db.connect
|
|
24
|
+
# sql ='SELECT name, email FROM users WHERE email = ?'
|
|
25
|
+
# stmt = PreparedStatement.new(con, sql)
|
|
26
|
+
# stmt.bind(1, 'email@example.com')
|
|
27
|
+
def bind(i, value)
|
|
28
|
+
case value
|
|
29
|
+
when NilClass
|
|
30
|
+
respond_to?(:bind_null) ? bind_null(i) : rb_raise(DuckDB::Error, 'This bind method does not support nil value. Re-compile ruby-duckdb with DuckDB version >= 0.1.1')
|
|
31
|
+
when Float
|
|
32
|
+
bind_double(i, value)
|
|
33
|
+
when Integer
|
|
34
|
+
bind_int64(i, value)
|
|
35
|
+
when String
|
|
36
|
+
bind_varchar(i, value)
|
|
37
|
+
when TrueClass, FalseClass
|
|
38
|
+
bind_boolean(i, value)
|
|
39
|
+
when Time
|
|
40
|
+
bind_varchar(i, value.strftime('%Y-%m-%d %H:%M:%S.%N'))
|
|
41
|
+
when Date
|
|
42
|
+
bind_varchar(i, value.strftime('%Y-%m-%d'))
|
|
43
|
+
else
|
|
44
|
+
rb_raise(DuckDB::Error, "not supported type #{value} (value.class)")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/duckdb/result.rb
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
module DuckDB
|
|
2
|
+
# The Result class encapsulates a execute result of DuckDB database.
|
|
3
|
+
#
|
|
4
|
+
# The usage is as follows:
|
|
5
|
+
#
|
|
6
|
+
# require 'duckdb'
|
|
7
|
+
#
|
|
8
|
+
# db = DuckDB::Database.open # database in memory
|
|
9
|
+
# con = db.connect
|
|
10
|
+
#
|
|
11
|
+
# con.execute('CREATE TABLE users (id INTEGER, name VARCHAR(30))')
|
|
12
|
+
#
|
|
13
|
+
# con.execute("INSERT into users VALUES(1, 'Alice')")
|
|
14
|
+
# con.execute("INSERT into users VALUES(2, 'Bob')")
|
|
15
|
+
# con.execute("INSERT into users VALUES(3, 'Cathy')")
|
|
16
|
+
#
|
|
17
|
+
# result = con.execute('SELECT * from users')
|
|
18
|
+
# result.each do |row|
|
|
19
|
+
# p row
|
|
20
|
+
# end
|
|
2
21
|
class Result
|
|
3
22
|
include Enumerable
|
|
4
23
|
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.0.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Masaki Suketa
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-10-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -25,47 +25,47 @@ dependencies:
|
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '2.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: minitest
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '5.0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '5.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: rake
|
|
42
|
+
name: rake
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- - "
|
|
45
|
+
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
47
|
+
version: '13.0'
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- - "
|
|
52
|
+
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
54
|
+
version: '13.0'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
56
|
+
name: rake-compiler
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - "
|
|
59
|
+
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
61
|
+
version: '0'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- - "
|
|
66
|
+
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
68
|
+
version: '0'
|
|
69
69
|
description: This module is Ruby binding for DuckDB database engine. You must have
|
|
70
70
|
the DuckDB engine installed to build/use this module.
|
|
71
71
|
email:
|
|
@@ -75,14 +75,15 @@ extensions:
|
|
|
75
75
|
- ext/duckdb/extconf.rb
|
|
76
76
|
extra_rdoc_files: []
|
|
77
77
|
files:
|
|
78
|
+
- ".github/workflows/test_by_github.yml"
|
|
78
79
|
- ".gitignore"
|
|
80
|
+
- ".travis.yml"
|
|
79
81
|
- CHANGELOG.md
|
|
80
82
|
- Gemfile
|
|
81
83
|
- Gemfile.lock
|
|
82
84
|
- LICENSE
|
|
83
85
|
- README.md
|
|
84
86
|
- Rakefile
|
|
85
|
-
- TODO
|
|
86
87
|
- bin/console
|
|
87
88
|
- bin/setup
|
|
88
89
|
- duckdb.gemspec
|
|
@@ -94,10 +95,15 @@ files:
|
|
|
94
95
|
- ext/duckdb/error.c
|
|
95
96
|
- ext/duckdb/error.h
|
|
96
97
|
- ext/duckdb/extconf.rb
|
|
98
|
+
- ext/duckdb/prepared_statement.c
|
|
99
|
+
- ext/duckdb/prepared_statement.h
|
|
97
100
|
- ext/duckdb/result.c
|
|
98
101
|
- ext/duckdb/result.h
|
|
99
102
|
- ext/duckdb/ruby-duckdb.h
|
|
100
103
|
- lib/duckdb.rb
|
|
104
|
+
- lib/duckdb/connection.rb
|
|
105
|
+
- lib/duckdb/database.rb
|
|
106
|
+
- lib/duckdb/prepared_statement.rb
|
|
101
107
|
- lib/duckdb/result.rb
|
|
102
108
|
- lib/duckdb/version.rb
|
|
103
109
|
homepage: https://github.com/suketa/ruby-duckdb
|
|
@@ -115,14 +121,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
115
121
|
requirements:
|
|
116
122
|
- - ">="
|
|
117
123
|
- !ruby/object:Gem::Version
|
|
118
|
-
version:
|
|
124
|
+
version: 2.5.0
|
|
119
125
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
126
|
requirements:
|
|
121
127
|
- - ">="
|
|
122
128
|
- !ruby/object:Gem::Version
|
|
123
129
|
version: '0'
|
|
124
130
|
requirements: []
|
|
125
|
-
rubygems_version: 3.
|
|
131
|
+
rubygems_version: 3.1.2
|
|
126
132
|
signing_key:
|
|
127
133
|
specification_version: 4
|
|
128
134
|
summary: This module is Ruby binding for DuckDB database engine.
|