duckdb 0.2.8.0 → 0.2.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a07cf98bca895ec39cc43754e6f69544e22c16cab023554daa632dc0be14d527
4
- data.tar.gz: d2e96a7556e9ab139fb4b03f8306961d011c80ff9eb259bb8f29cdbd390ea757
3
+ metadata.gz: af998f52f46c0ef8f51501333cbaf39d720daaa389eb618d7d7ad79627d70ad9
4
+ data.tar.gz: 8e1c5323fa2f917c9ab6050a0350e5e25b646559492501f636af026b79cb20f8
5
5
  SHA512:
6
- metadata.gz: fc64b4021083e0837de1c22470b015be7337f2e43754771c36b4265f133542bb7161762fa51a46f2ec9f9ac992bf67bb45e7d8031206f4cd385f248bddcff14d
7
- data.tar.gz: 131cb3b1ee245cb765b8b7081d8fb900634055cf6570e6220121991e275d53e7be44ac1c1274c64f4c8991cd3252823274fd887fa3b49a31c8dc9578dd4aade6
6
+ metadata.gz: 14809adeffb7bc79f2102e29f89a6fe6ea276ab38eaeabed9f3c18a023566c3bbad06c1ab31e55499e77d3f0f9ced186d618c24639b8c16ceff1ee61304f9f67
7
+ data.tar.gz: 15d164af6af9d985765d7b9fa87dcf7f9cf89fb2d691c9d44ea2d2bb72341a77213647cb08bcacc66094eb1622d250fe203f41a9529866af9f358518800f7183
@@ -7,7 +7,8 @@ jobs:
7
7
  runs-on: macos-latest
8
8
  strategy:
9
9
  matrix:
10
- ruby: ['2.5.8', '2.6.8', '2.7.4', '3.0.2', 'head']
10
+ ruby: ['2.6.8', '2.7.4', '3.0.2', 'head']
11
+ duckdb: ['0.2.8', '0.2.9']
11
12
 
12
13
  steps:
13
14
  - uses: actions/checkout@v2
@@ -17,9 +18,32 @@ jobs:
17
18
  with:
18
19
  ruby-version: ${{ matrix.ruby }}
19
20
 
20
- - name: Install latest duckdb by brew
21
+ - name: duckdb cache
22
+ id: duckdb-cache
23
+ uses: actions/cache@v2
24
+ with:
25
+ path: /usr/local/Cellar/duckdb@${{ matrix.duckdb }}
26
+ key: ${{ runner.os }}-duckdb-v${{ matrix.duckdb }}
27
+
28
+ - name: Install duckdb v${{ matrix.duckdb }} by brew
29
+ env:
30
+ DUCKDB_VERSION: ${{ matrix.duckdb }}
31
+ if: steps.duckdb-cache.outputs.cache-hit != 'true'
32
+ run: |
33
+ brew tap-new duckdb/taps
34
+ brew extract duckdb duckdb/taps --version $DUCKDB_VERSION
35
+ brew install duckdb/taps/duckdb@$DUCKDB_VERSION
36
+
37
+ - name: setup duckdb v${{ matrix.duckdb }} headers and libraries
38
+ env:
39
+ DUCKDB_VERSION: ${{ matrix.duckdb }}
21
40
  run: |
22
- brew install duckdb
41
+ if [ ! -L /usr/local/include/duckdb.h ]; then
42
+ header=`find /usr/local/Cellar/duckdb@$DUCKDB_VERSION -name "duckdb.h"`
43
+ lib=`find /usr/local/Cellar/duckdb@$DUCKDB_VERSION -name "libduckdb.dylib"`
44
+ ln -s $header /usr/local/include/duckdb.h
45
+ ln -s $lib /usr/local/lib/libduckdb.dylib
46
+ fi
23
47
 
24
48
  - name: Build and test with Rake with Ruby ${{ matrix.ruby }}
25
49
  run: |
@@ -8,8 +8,8 @@ jobs:
8
8
  runs-on: ubuntu-latest
9
9
  strategy:
10
10
  matrix:
11
- ruby: ['2.5.8', '2.6.8', '2.7.4', '3.0.2', 'head']
12
- duckdb: ['0.2.8', '0.2.7']
11
+ ruby: ['2.6.8', '2.7.4', '3.0.2', 'head']
12
+ duckdb: ['0.2.8', '0.2.9']
13
13
 
14
14
  steps:
15
15
  - uses: actions/checkout@v2
@@ -19,43 +19,24 @@ jobs:
19
19
  with:
20
20
  ruby-version: ${{ matrix.ruby }}
21
21
 
22
- - name: duckdb 0.2.8 cache
23
- id: duckdb-cache-v0_2_8
24
- uses: actions/cache@v1.1.0
22
+ - name: duckdb cache
23
+ id: duckdb-cache
24
+ uses: actions/cache@v2
25
25
  with:
26
- path: duckdb-v0.2.8
27
- key: ${{ runner.os }}-duckdb-v0_2_8_001
28
- restore-keys: |
29
- ${{ runner.os }}-duckdb-v0_2_8
26
+ path: duckdb-v${{ matrix.duckdb }}
27
+ key: ${{ runner.os }}-duckdb-v${{ matrix.duckdb }}
30
28
 
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.8
41
- if: steps.duckdb-cache-v0_2_8.outputs.cache-hit != 'true'
42
- run: |
43
- git clone -b v0.2.8 https://github.com/cwida/duckdb.git duckdb-tmp-v0.2.8
44
- cd duckdb-tmp-v0.2.8 && make && cd ..
45
- rm -rf duckdb-v0.2.8
46
- mkdir -p duckdb-v0.2.8/build/release/src duckdb-v0.2.8/src
47
- cp -rip duckdb-tmp-v0.2.8/build/release/src/*.so duckdb-v0.2.8/build/release/src
48
- cp -rip duckdb-tmp-v0.2.8/src/include duckdb-v0.2.8/src/
49
-
50
- - name: Build duckdb 0.2.7
51
- if: steps.duckdb-cache-v0_2_7.outputs.cache-hit != 'true'
29
+ - name: Build duckdb ${{ matrix.duckdb }}
30
+ env:
31
+ DUCKDB_VERSION: ${{ matrix.duckdb }}
32
+ if: steps.duckdb-cache.outputs.cache-hit != 'true'
52
33
  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/
34
+ git clone -b v$DUCKDB_VERSION https://github.com/cwida/duckdb.git duckdb-tmp-v$DUCKDB_VERSION
35
+ cd duckdb-tmp-v$DUCKDB_VERSION && make && cd ..
36
+ rm -rf duckdb-v$DUCKDB_VERSION
37
+ mkdir -p duckdb-v$DUCKDB_VERSION/build/release/src duckdb-v$DUCKDB_VERSION/src
38
+ cp -rip duckdb-tmp-v$DUCKDB_VERSION/build/release/src/*.so duckdb-v$DUCKDB_VERSION/build/release/src
39
+ cp -rip duckdb-tmp-v$DUCKDB_VERSION/src/include duckdb-v$DUCKDB_VERSION/src/
59
40
 
60
41
  - name: Build and test with Rake with Ruby ${{ matrix.ruby }}
61
42
  env:
@@ -7,7 +7,8 @@ jobs:
7
7
  runs-on: windows-latest
8
8
  strategy:
9
9
  matrix:
10
- ruby: ['2.5.8', '2.6.7', '2.7.3', '3.0.1', 'head']
10
+ ruby: ['2.6.8', '2.7.4', '3.0.2', 'head']
11
+ duckdb: ['0.2.8', '0.2.9']
11
12
 
12
13
  steps:
13
14
  - uses: actions/checkout@v2
@@ -18,8 +19,10 @@ jobs:
18
19
  ruby-version: ${{ matrix.ruby }}
19
20
 
20
21
  - name: download duckdb binary for windows 64bit
22
+ env:
23
+ DUCKDB_VERSION: ${{ matrix.duckdb }}
21
24
  run: |
22
- curl -OL https://github.com/duckdb/duckdb/releases/download/v0.2.6/libduckdb-windows-amd64.zip
25
+ curl -OL https://github.com/duckdb/duckdb/releases/download/v${env:DUCKDB_VERSION}/libduckdb-windows-amd64.zip
23
26
 
24
27
  - name: extract zip file
25
28
  run: |
@@ -29,9 +32,12 @@ jobs:
29
32
  run: |
30
33
  bundle install
31
34
  bundle exec rake build -- --with-duckdb-include=../../../.. --with-duckdb-lib=../../../..
35
+ - name: setup duckdb.dll
36
+ run: |
37
+ cp duckdb.dll C:/Windows/System32/
32
38
 
33
39
  # FIXME: rake test fails with LoadError
34
40
  # 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
41
+ - name: rake test
42
+ run: |
43
+ rake test
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # ChangeLog
2
2
 
3
+ # 0.2.9.0
4
+
5
+ - add DuckDB::Appender#append
6
+ - breaking change.
7
+ - append_timestamp is called when the argument is Time object.
8
+ - append_date is called when the argument is Date object.
9
+ - add DuckDB::Appender#append_timestamp.
10
+ - add DuckDB::Appender#append_interval. append_interval is expremental.
11
+ - add DuckDB::Result#rows_changed
12
+ - refactoring DuckDB::Append#append_hugeint with duckdb v0.2.9
13
+ - test 2 versions of duckdb on github actions macos CI.
14
+ - fix windows CI failes
15
+ - update github actions CI on ubuntu
16
+ - fix to build with duckdb v0.2.9
17
+ - use duckdb_prepare_error when get error message of prepared statement.
18
+ - add DuckDB::Appender#append_date
19
+ - add DuckDB::Appender#append_time
20
+
21
+ # 0.2.8.0
22
+
3
23
  - DuckDB::Database.open accepts 2-nd argument as DuckDB::Config object.
4
24
  - add DuckDB::Config
5
25
  - bump duckdb to 0.2.8 in CI
data/CONTRIBUTION.md ADDED
@@ -0,0 +1,24 @@
1
+ # Contribution Guide
2
+
3
+ ## Issue
4
+
5
+ If you spot a problem, [search if an issue already exists](https://github.com/suketa/ruby-duckdb/issues).
6
+ If a related issue doesn't exist, you can open a [new issue](https://github.com/suketa/ruby-duckdb/issues/new).
7
+
8
+
9
+ ## Fix Issues or Add New Features.
10
+
11
+ 1. install [Ruby](https://www.ruby-lang.org/) into your local machine.
12
+ 2. install [duckdb](https://duckdb.org/) into your local machine.
13
+ 3. fork the repository and `git clone` to your local machine.
14
+ 4. run `bundle install`
15
+ 5. run `rake build`
16
+ or you might run with C duckdb header and library directories:
17
+ `rake build -- --with-duckdb-include=/duckdb_header_directory --with-duckdb-lib=/duckdb_library_directory`
18
+ 6. run `rake test`
19
+ 7. create new branch to change the code.
20
+ 8. change the code.
21
+ 9. write test.
22
+ 10. run `rake test` and confirm all tests pass.
23
+ 11. git push.
24
+ 12. create PR.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- duckdb (0.2.8.0)
4
+ duckdb (0.2.9.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # ruby-duckdb
2
2
 
3
- [![Build Status](https://travis-ci.com/suketa/ruby-duckdb.svg?branch=master)](https://travis-ci.com/suketa/ruby-duckdb)
4
3
  [![](https://github.com/suketa/ruby-duckdb/workflows/Ubuntu/badge.svg)](https://github.com/suketa/ruby-duckdb/actions?query=workflow%3AUbuntu)
5
4
  [![](https://github.com/suketa/ruby-duckdb/workflows/MacOS/badge.svg)](https://github.com/suketa/ruby-duckdb/actions?query=workflow%3AMacOS)
5
+ [![](https://github.com/suketa/ruby-duckdb/workflows/Windows/badge.svg)](https://github.com/suketa/ruby-duckdb/actions?query=workflow%3AWindows)
6
6
 
7
7
  ## Description
8
8
 
@@ -18,10 +18,10 @@ You must have [DuckDB](http://www.duckdb.org) engine installed in order to build
18
18
  gem install duckdb
19
19
  ```
20
20
 
21
- or you must specify the location of the include and lib files:
21
+ or you must specify the location of the C header and library files:
22
22
 
23
23
  ```
24
- gem install duckdb -- --with-duckdb-include=/duckdb_include_directory --with-duckdb-lib=/duckdb_library_directory
24
+ gem install duckdb -- --with-duckdb-include=/duckdb_header_directory --with-duckdb-lib=/duckdb_library_directory
25
25
  ```
26
26
 
27
27
  ## Usage
data/duckdb.gemspec CHANGED
@@ -21,12 +21,12 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  # Specify which files should be added to the gem when it is released.
23
23
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
25
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
26
  end
27
27
  spec.require_paths = ['lib']
28
28
  spec.extensions = ['ext/duckdb/extconf.rb']
29
- spec.required_ruby_version = '>= 2.5.0'
29
+ spec.required_ruby_version = '>= 2.6.0'
30
30
 
31
31
  spec.add_development_dependency 'bundler', '~> 2.0'
32
32
  spec.add_development_dependency 'minitest', '~> 5.0'
@@ -24,6 +24,27 @@ static VALUE appender_append_varchar(VALUE self, VALUE val);
24
24
  static VALUE appender_append_varchar_length(VALUE self, VALUE val, VALUE len);
25
25
  static VALUE appender_append_blob(VALUE self, VALUE val);
26
26
  static VALUE appender_append_null(VALUE self);
27
+
28
+ #ifdef HAVE_DUCKDB_APPEND_DATE
29
+ static VALUE appender__append_date(VALUE self, VALUE yearval, VALUE monthval, VALUE dayval);
30
+ #endif
31
+
32
+ #ifdef HAVE_DUCKDB_APPEND_INTERVAL
33
+ static VALUE appender__append_interval(VALUE self, VALUE months, VALUE days, VALUE micros);
34
+ #endif
35
+
36
+ #ifdef HAVE_DUCKDB_APPEND_TIME
37
+ static VALUE appender__append_time(VALUE self, VALUE hour, VALUE min, VALUE sec, VALUE micros);
38
+ #endif
39
+
40
+ #ifdef HAVE_DUCKDB_APPEND_TIMESTAMP
41
+ static VALUE appender__append_timestamp(VALUE self, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros);
42
+ #endif
43
+
44
+ #ifdef HAVE_DUCKDB_APPEND_HUGEINT
45
+ static VALUE appender__append_hugeint(VALUE self, VALUE lower, VALUE upper);
46
+ #endif
47
+
27
48
  static VALUE appender_flush(VALUE self);
28
49
  static VALUE appender_close(VALUE self);
29
50
 
@@ -268,6 +289,106 @@ static VALUE appender_append_null(VALUE self) {
268
289
  return self;
269
290
  }
270
291
 
292
+ #ifdef HAVE_DUCKDB_APPEND_DATE
293
+ static VALUE appender__append_date(VALUE self, VALUE yearval, VALUE monthval, VALUE dayval) {
294
+ duckdb_date_struct dt_struct;
295
+ duckdb_date dt;
296
+ rubyDuckDBAppender *ctx;
297
+
298
+ Data_Get_Struct(self, rubyDuckDBAppender, ctx);
299
+ dt_struct.year = NUM2INT(yearval);
300
+ dt_struct.month = NUM2INT(monthval);
301
+ dt_struct.day = NUM2INT(dayval);
302
+ dt = duckdb_to_date(dt_struct);
303
+
304
+ if (duckdb_append_date(ctx->appender, dt) == DuckDBError) {
305
+ rb_raise(eDuckDBError, "failed to append date");
306
+ }
307
+ return self;
308
+ }
309
+ #endif
310
+
311
+ #ifdef HAVE_DUCKDB_APPEND_INTERVAL
312
+ static VALUE appender__append_interval(VALUE self, VALUE months, VALUE days, VALUE micros) {
313
+ duckdb_interval interval;
314
+ rubyDuckDBAppender *ctx;
315
+
316
+ Data_Get_Struct(self, rubyDuckDBAppender, ctx);
317
+ interval.months = NUM2INT(months);
318
+ interval.days = NUM2INT(days);
319
+ interval.micros = NUM2LL(micros);
320
+
321
+ if (duckdb_append_interval(ctx->appender, interval) == DuckDBError) {
322
+ rb_raise(eDuckDBError, "failed to append interval");
323
+ }
324
+ return self;
325
+ }
326
+ #endif
327
+
328
+ #ifdef HAVE_DUCKDB_APPEND_TIME
329
+ static VALUE appender__append_time(VALUE self, VALUE hour, VALUE min, VALUE sec, VALUE micros) {
330
+ duckdb_time_struct time_st;
331
+ duckdb_time time;
332
+ rubyDuckDBAppender *ctx;
333
+
334
+ Data_Get_Struct(self, rubyDuckDBAppender, ctx);
335
+ time_st.hour = NUM2INT(hour);
336
+ time_st.min = NUM2INT(min);
337
+ time_st.sec = NUM2INT(sec);
338
+ time_st.micros = NUM2INT(micros);
339
+
340
+ time = duckdb_to_time(time_st);
341
+
342
+ if (duckdb_append_time(ctx->appender, time) == DuckDBError) {
343
+ rb_raise(eDuckDBError, "failed to append time");
344
+ }
345
+ return self;
346
+ }
347
+ #endif
348
+
349
+ #ifdef HAVE_DUCKDB_APPEND_TIMESTAMP
350
+ static VALUE appender__append_timestamp(VALUE self, VALUE year, VALUE month, VALUE day, VALUE hour, VALUE min, VALUE sec, VALUE micros) {
351
+ duckdb_timestamp_struct timestamp_st;
352
+ duckdb_timestamp timestamp;
353
+
354
+ rubyDuckDBAppender *ctx;
355
+
356
+ Data_Get_Struct(self, rubyDuckDBAppender, ctx);
357
+
358
+ timestamp_st.date.year = NUM2INT(year);
359
+ timestamp_st.date.month = NUM2INT(month);
360
+ timestamp_st.date.day = NUM2INT(day);
361
+ timestamp_st.time.hour = NUM2INT(hour);
362
+ timestamp_st.time.min = NUM2INT(min);
363
+ timestamp_st.time.sec = NUM2INT(sec);
364
+ timestamp_st.time.micros = NUM2INT(micros);
365
+
366
+ timestamp = duckdb_to_timestamp(timestamp_st);
367
+
368
+ if (duckdb_append_timestamp(ctx->appender, timestamp) == DuckDBError) {
369
+ rb_raise(eDuckDBError, "failed to append timestamp");
370
+ }
371
+ return self;
372
+ }
373
+ #endif
374
+
375
+ #ifdef HAVE_DUCKDB_APPEND_HUGEINT
376
+ static VALUE appender__append_hugeint(VALUE self, VALUE lower, VALUE upper) {
377
+ duckdb_hugeint hugeint;
378
+
379
+ hugeint.lower = NUM2ULL(lower);
380
+ hugeint.upper = NUM2LL(upper);
381
+
382
+ rubyDuckDBAppender *ctx;
383
+
384
+ Data_Get_Struct(self, rubyDuckDBAppender, ctx);
385
+ if (duckdb_append_hugeint(ctx->appender, hugeint) == DuckDBError) {
386
+ rb_raise(eDuckDBError, "failed to append hugeint");
387
+ }
388
+ return self;
389
+ }
390
+ #endif
391
+
271
392
  static VALUE appender_flush(VALUE self) {
272
393
  rubyDuckDBAppender *ctx;
273
394
  Data_Get_Struct(self, rubyDuckDBAppender, ctx);
@@ -309,6 +430,21 @@ void init_duckdb_appender(void) {
309
430
  rb_define_method(cDuckDBAppender, "append_varchar_length", appender_append_varchar_length, 2);
310
431
  rb_define_method(cDuckDBAppender, "append_blob", appender_append_blob, 1);
311
432
  rb_define_method(cDuckDBAppender, "append_null", appender_append_null, 0);
433
+ #ifdef HAVE_DUCKDB_APPEND_DATE
434
+ rb_define_private_method(cDuckDBAppender, "_append_date", appender__append_date, 3);
435
+ #endif
436
+ #ifdef HAVE_DUCKDB_APPEND_INTERVAL
437
+ rb_define_private_method(cDuckDBAppender, "_append_interval", appender__append_interval, 3);
438
+ #endif
439
+ #ifdef HAVE_DUCKDB_APPEND_TIME
440
+ rb_define_private_method(cDuckDBAppender, "_append_time", appender__append_time, 4);
441
+ #endif
442
+ #ifdef HAVE_DUCKDB_APPEND_TIMESTAMP
443
+ rb_define_private_method(cDuckDBAppender, "_append_timestamp", appender__append_timestamp, 7);
444
+ #endif
445
+ #ifdef HAVE_DUCKDB_APPEND_HUGEINT
446
+ rb_define_private_method(cDuckDBAppender, "_append_hugeint", appender__append_hugeint, 2);
447
+ #endif
312
448
  rb_define_method(cDuckDBAppender, "flush", appender_flush, 0);
313
449
  rb_define_method(cDuckDBAppender, "close", appender_close, 0);
314
450
  }
data/ext/duckdb/config.c CHANGED
@@ -37,7 +37,7 @@ static VALUE config_initialize(VALUE self) {
37
37
  }
38
38
 
39
39
  static VALUE config_s_size(VALUE self) {
40
- return INT2NUM(duckdb_config_count());
40
+ return ULONG2NUM(duckdb_config_count());
41
41
  }
42
42
 
43
43
  static VALUE config_s_get_config_flag(VALUE klass, VALUE value) {
@@ -2,11 +2,23 @@ require 'mkmf'
2
2
 
3
3
  dir_config('duckdb')
4
4
  if have_library('duckdb')
5
+ if have_func('duckdb_nparams(NULL)', 'duckdb.h')
6
+ $defs << '-DHAVE_DUCKDB_NPARAMS_029'
7
+ elsif have_func('duckdb_nparams(NULL, NULL)', 'duckdb.h')
8
+ $defs << '-DHAVE_DUCKDB_NPARAMS_028'
9
+ end
10
+
5
11
  have_func('duckdb_value_blob', 'duckdb.h')
6
12
  have_func('duckdb_bind_blob', 'duckdb.h')
7
13
  have_func('duckdb_appender_create', 'duckdb.h')
8
14
  have_func('duckdb_free', 'duckdb.h')
9
15
  have_func('duckdb_create_config', 'duckdb.h')
10
16
  have_func('duckdb_open_ext', 'duckdb.h')
17
+ have_func('duckdb_prepare_error', 'duckdb.h')
18
+ have_func('duckdb_append_date', 'duckdb.h')
19
+ have_func('duckdb_append_interval', 'duckdb.h')
20
+ have_func('duckdb_append_time', 'duckdb.h')
21
+ have_func('duckdb_append_timestamp', 'duckdb.h')
22
+ have_func('duckdb_append_hugeint', 'duckdb.h')
11
23
  create_makefile('duckdb/duckdb_native')
12
24
  end
@@ -29,8 +29,13 @@ static VALUE duckdb_prepared_statement_initialize(VALUE self, VALUE con, VALUE q
29
29
  Data_Get_Struct(con, rubyDuckDBConnection, ctxcon);
30
30
 
31
31
  if (duckdb_prepare(ctxcon->con, StringValuePtr(query), &(ctx->prepared_statement)) == DuckDBError) {
32
+ #ifdef HAVE_DUCKDB_PREPARE_ERROR
33
+ const char *error = duckdb_prepare_error(ctx->prepared_statement);
34
+ rb_raise(eDuckDBError, "%s", error);
35
+ #else
32
36
  /* TODO: include query parameter information in error message. */
33
37
  rb_raise(eDuckDBError, "failed to prepare statement");
38
+ #endif
34
39
  }
35
40
  return self;
36
41
  }
@@ -39,11 +44,14 @@ static VALUE duckdb_prepared_statement_nparams(VALUE self)
39
44
  {
40
45
  rubyDuckDBPreparedStatement *ctx;
41
46
  Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
42
-
47
+ #ifdef HAVE_DUCKDB_NPARAMS_029
48
+ return rb_int2big(duckdb_nparams(ctx->prepared_statement));
49
+ #else
43
50
  if (duckdb_nparams(ctx->prepared_statement, &(ctx->nparams)) == DuckDBError) {
44
51
  rb_raise(eDuckDBError, "failed to get number of parameters");
45
52
  }
46
53
  return rb_int2big(ctx->nparams);
54
+ #endif
47
55
  }
48
56
 
49
57
 
data/ext/duckdb/result.c CHANGED
@@ -2,59 +2,50 @@
2
2
 
3
3
  static VALUE cDuckDBResult;
4
4
 
5
- static void deallocate(void *ctx)
6
- {
5
+ static void deallocate(void *ctx) {
7
6
  rubyDuckDBResult *p = (rubyDuckDBResult *)ctx;
8
7
 
9
8
  duckdb_destroy_result(&(p->result));
10
9
  xfree(p);
11
10
  }
12
11
 
13
- static VALUE allocate(VALUE klass)
14
- {
12
+ static VALUE allocate(VALUE klass) {
15
13
  rubyDuckDBResult *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBResult));
16
14
  return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
17
15
  }
18
16
 
19
- static VALUE to_ruby_obj_boolean(duckdb_result *result, idx_t col_idx, idx_t row_idx)
20
- {
17
+ static VALUE to_ruby_obj_boolean(duckdb_result *result, idx_t col_idx, idx_t row_idx) {
21
18
  bool bval = duckdb_value_boolean(result, col_idx, row_idx);
22
19
  return bval ? Qtrue : Qnil;
23
20
  }
24
21
 
25
- static VALUE to_ruby_obj_smallint(duckdb_result *result, idx_t col_idx, idx_t row_idx)
26
- {
22
+ static VALUE to_ruby_obj_smallint(duckdb_result *result, idx_t col_idx, idx_t row_idx) {
27
23
  int16_t i16val = duckdb_value_int16(result, col_idx, row_idx);
28
24
  return INT2FIX(i16val);
29
25
  }
30
26
 
31
- static VALUE to_ruby_obj_integer(duckdb_result *result, idx_t col_idx, idx_t row_idx)
32
- {
27
+ static VALUE to_ruby_obj_integer(duckdb_result *result, idx_t col_idx, idx_t row_idx) {
33
28
  int32_t i32val = duckdb_value_int32(result, col_idx, row_idx);
34
29
  return INT2NUM(i32val);
35
30
  }
36
31
 
37
- static VALUE to_ruby_obj_bigint(duckdb_result *result, idx_t col_idx, idx_t row_idx)
38
- {
32
+ static VALUE to_ruby_obj_bigint(duckdb_result *result, idx_t col_idx, idx_t row_idx) {
39
33
  int64_t i64val = duckdb_value_int64(result, col_idx, row_idx);
40
34
  return rb_int2big(i64val);
41
35
  }
42
36
 
43
- static VALUE to_ruby_obj_float(duckdb_result *result, idx_t col_idx, idx_t row_idx)
44
- {
37
+ static VALUE to_ruby_obj_float(duckdb_result *result, idx_t col_idx, idx_t row_idx) {
45
38
  float fval = duckdb_value_float(result, col_idx, row_idx);
46
39
  return DBL2NUM(fval);
47
40
  }
48
41
 
49
- static VALUE to_ruby_obj_double(duckdb_result *result, idx_t col_idx, idx_t row_idx)
50
- {
42
+ static VALUE to_ruby_obj_double(duckdb_result *result, idx_t col_idx, idx_t row_idx) {
51
43
  double dval = duckdb_value_double(result, col_idx, row_idx);
52
44
  return DBL2NUM(dval);
53
45
  }
54
46
 
55
47
  #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
- {
48
+ static VALUE to_ruby_obj_string_from_blob(duckdb_result *result, idx_t col_idx, idx_t row_idx) {
58
49
  VALUE str;
59
50
  duckdb_blob bval = duckdb_value_blob(result, col_idx, row_idx);
60
51
  str = rb_str_new(bval.data, bval.size);
@@ -71,8 +62,7 @@ static VALUE to_ruby_obj_string_from_blob(duckdb_result *result, idx_t col_idx,
71
62
  }
72
63
  #endif /* HAVE_DUCKDB_VALUE_BLOB */
73
64
 
74
- static VALUE to_ruby_obj(duckdb_result *result, idx_t col_idx, idx_t row_idx)
75
- {
65
+ static VALUE to_ruby_obj(duckdb_result *result, idx_t col_idx, idx_t row_idx) {
76
66
  char *p;
77
67
  VALUE obj = Qnil;
78
68
  if (result->columns[col_idx].nullmask[row_idx]) {
@@ -112,8 +102,7 @@ static VALUE to_ruby_obj(duckdb_result *result, idx_t col_idx, idx_t row_idx)
112
102
  return obj;
113
103
  }
114
104
 
115
- static VALUE row_array(rubyDuckDBResult *ctx, idx_t row_idx)
116
- {
105
+ static VALUE row_array(rubyDuckDBResult *ctx, idx_t row_idx) {
117
106
  idx_t col_idx;
118
107
  VALUE ary = rb_ary_new2(ctx->result.column_count);
119
108
  for(col_idx = 0; col_idx < ctx->result.column_count; col_idx++) {
@@ -122,16 +111,14 @@ static VALUE row_array(rubyDuckDBResult *ctx, idx_t row_idx)
122
111
  return ary;
123
112
  }
124
113
 
125
- static VALUE duckdb_result_row_size(VALUE oDuckDBResult, VALUE args, VALUE obj)
126
- {
114
+ static VALUE duckdb_result_row_size(VALUE oDuckDBResult, VALUE args, VALUE obj) {
127
115
  rubyDuckDBResult *ctx;
128
116
  Data_Get_Struct(oDuckDBResult, rubyDuckDBResult, ctx);
129
117
 
130
118
  return LONG2FIX(ctx->result.row_count);
131
119
  }
132
120
 
133
- static VALUE duckdb_result_each(VALUE oDuckDBResult)
134
- {
121
+ static VALUE duckdb_result_each(VALUE oDuckDBResult) {
135
122
  rubyDuckDBResult *ctx;
136
123
  idx_t row_idx = 0;
137
124
 
@@ -144,15 +131,42 @@ static VALUE duckdb_result_each(VALUE oDuckDBResult)
144
131
  return oDuckDBResult;
145
132
  }
146
133
 
147
- VALUE create_result(void)
148
- {
134
+ /*
135
+ * call-seq:
136
+ * result.rows_changed -> integer
137
+ *
138
+ * Returns the count of rows changed.
139
+ *
140
+ * DuckDB::Database.open do |db|
141
+ * db.connect do |con|
142
+ * r = con.query('CREATE TABLE t2 (id INT)')
143
+ * r.rows_changed # => 0
144
+ * r = con.query('INSERT INTO t2 VALUES (1), (2), (3)')
145
+ * r.rows_changed # => 3
146
+ * r = con.query('UPDATE t2 SET id = id + 1 WHERE id > 1')
147
+ * r.rows_changed # => 2
148
+ * r = con.query('DELETE FROM t2 WHERE id = 0')
149
+ * r.rows_changed # => 0
150
+ * r = con.query('DELETE FROM t2 WHERE id = 4')
151
+ * r.rows_changed # => 1
152
+ * end
153
+ * end
154
+ *
155
+ */
156
+ static VALUE duckdb_result_rows_changed(VALUE oDuckDBResult) {
157
+ rubyDuckDBResult *ctx;
158
+ Data_Get_Struct(oDuckDBResult, rubyDuckDBResult, ctx);
159
+ return LL2NUM(ctx->result.rows_changed);
160
+ }
161
+
162
+ VALUE create_result(void) {
149
163
  return allocate(cDuckDBResult);
150
164
  }
151
165
 
152
- void init_duckdb_result(void)
153
- {
166
+ void init_duckdb_result(void) {
154
167
  cDuckDBResult = rb_define_class_under(mDuckDB, "Result", rb_cObject);
155
168
  rb_define_alloc_func(cDuckDBResult, allocate);
156
169
 
157
170
  rb_define_method(cDuckDBResult, "each", duckdb_result_each, 0);
171
+ rb_define_method(cDuckDBResult, "rows_changed", duckdb_result_rows_changed, 0);
158
172
  }
@@ -1,4 +1,5 @@
1
1
  require 'date'
2
+ require 'time'
2
3
 
3
4
  module DuckDB
4
5
  if defined?(DuckDB::Appender)
@@ -16,13 +17,158 @@ module DuckDB
16
17
  RANGE_INT32 = -2_147_483_648..2_147_483_647
17
18
  RANGE_INT64 = -9_223_372_036_854_775_808..9_223_372_036_854_775_807
18
19
 
20
+ #
21
+ # appends huge int value.
22
+ #
23
+ # require 'duckdb'
24
+ # db = DuckDB::Database.open
25
+ # con = db.connect
26
+ # con.query('CREATE TABLE numbers (num HUGEINT)')
27
+ # appender = con.appender('numbers')
28
+ # appender
29
+ # .begin_row
30
+ # .append_hugeint(-170_141_183_460_469_231_731_687_303_715_884_105_727)
31
+ # .end_row
32
+ #
19
33
  def append_hugeint(value)
20
34
  case value
21
35
  when Integer
22
- append_varchar(value.to_s)
36
+ if respond_to?(:_append_hugeint, true)
37
+ half = 1 << 64
38
+ upper = value / half
39
+ lower = value - upper * half
40
+ _append_hugeint(lower, upper)
41
+ else
42
+ append_varchar(value.to_s)
43
+ end
44
+ else
45
+ raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
46
+ end
47
+ end
48
+
49
+ #
50
+ # appends date value.
51
+ #
52
+ # require 'duckdb'
53
+ # db = DuckDB::Database.open
54
+ # con = db.connect
55
+ # con.query('CREATE TABLE dates (date_value DATE)')
56
+ # appender = con.appender('dates')
57
+ # appender.begin_row
58
+ # appender.append_date(Date.today)
59
+ # # or
60
+ # # appender.append_date(Time.now)
61
+ # # appender.append_date('2021-10-10')
62
+ # appender.end_row
63
+ # appender.flush
64
+ #
65
+ def append_date(value)
66
+ case value
67
+ when Date, Time
68
+ date = value
69
+ when String
70
+ begin
71
+ date = Date.parse(value)
72
+ rescue
73
+ raise(ArgumentError, "Cannot parse argument `#{value}` to Date.")
74
+ end
75
+ else
76
+ raise(ArgumentError, "Argument `#{value}` must be Date, Time or String.")
77
+ end
78
+
79
+ _append_date(date.year, date.month, date.day)
80
+ end
81
+
82
+ #
83
+ # appends time value.
84
+ #
85
+ # require 'duckdb'
86
+ # db = DuckDB::Database.open
87
+ # con = db.connect
88
+ # con.query('CREATE TABLE times (time_value TIME)')
89
+ # appender = con.appender('times')
90
+ # appender.begin_row
91
+ # appender.append_time(Time.now)
92
+ # # or
93
+ # # appender.append_time('01:01:01')
94
+ # appender.end_row
95
+ # appender.flush
96
+ #
97
+ def append_time(value)
98
+ case value
99
+ when Time
100
+ time = value
101
+ when String
102
+ begin
103
+ time = Time.parse(value)
104
+ rescue
105
+ raise(ArgumentError, "Cannot parse argument `#{value}` to Time.")
106
+ end
107
+ else
108
+ raise(ArgumentError, "Argument `#{value}` must be Time or String.")
109
+ end
110
+
111
+ _append_time(time.hour, time.min, time.sec, time.usec)
112
+ end
113
+
114
+ #
115
+ # appends timestamp value.
116
+ #
117
+ # require 'duckdb'
118
+ # db = DuckDB::Database.open
119
+ # con = db.connect
120
+ # con.query('CREATE TABLE timestamps (timestamp_value TIMESTAMP)')
121
+ # appender = con.appender('timestamps')
122
+ # appender.begin_row
123
+ # appender.append_time(Time.now)
124
+ # # or
125
+ # # appender.append_time(Date.today)
126
+ # # appender.append_time('2021-08-01 01:01:01')
127
+ # appender.end_row
128
+ # appender.flush
129
+ #
130
+ def append_timestamp(value)
131
+ case value
132
+ when Time
133
+ time = value
134
+ when Date
135
+ time = value.to_time
136
+ when String
137
+ begin
138
+ time = Time.parse(value)
139
+ rescue
140
+ raise(ArgumentError, "Cannot parse argument `#{value.class} #{value}` to Time.")
141
+ end
23
142
  else
24
- rb_raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
143
+ raise(ArgumentError, "Argument `#{value.class} #{value}` must be Time or Date or String.")
25
144
  end
145
+ _append_timestamp(time.year, time.month, time.day, time.hour, time.min, time.sec, time.nsec / 1000)
146
+ end
147
+
148
+ #
149
+ # appends interval.
150
+ # The argument must be ISO8601 duration format.
151
+ # WARNING: This method is expremental.
152
+ #
153
+ # require 'duckdb'
154
+ # db = DuckDB::Database.open
155
+ # con = db.connect
156
+ # con.query('CREATE TABLE intervals (interval_value INTERVAL)')
157
+ # appender = con.appender('intervals')
158
+ # appender
159
+ # .begin_row
160
+ # .append_interval('P1Y2D') # => append 1 year 2 days interval.
161
+ # .end_row
162
+ # .flush
163
+ #
164
+ def append_interval(value)
165
+ raise ArgumentError, "Argument `#{value}` must be a string." unless value.is_a?(String)
166
+
167
+ hash = iso8601_interval_to_hash(value)
168
+
169
+ months, days, micros = hash_to__append_interval_args(hash)
170
+
171
+ _append_interval(months, days, micros)
26
172
  end
27
173
 
28
174
  #
@@ -64,11 +210,19 @@ module DuckDB
64
210
  when TrueClass, FalseClass
65
211
  append_bool(value)
66
212
  when Time
67
- append_varchar(value.strftime('%Y-%m-%d %H:%M:%S.%N'))
213
+ if respond_to?(:append_timestamp)
214
+ append_timestamp(value)
215
+ else
216
+ append_varchar(value.strftime('%Y-%m-%d %H:%M:%S.%N'))
217
+ end
68
218
  when Date
69
- append_varchar(value.strftime('%Y-%m-%d'))
219
+ if respond_to?(:append_date)
220
+ append_date(value)
221
+ else
222
+ append_varchar(value.strftime('%Y-%m-%d'))
223
+ end
70
224
  else
71
- rb_raise(DuckDB::Error, "not supported type #{value} (value.class)")
225
+ raise(DuckDB::Error, "not supported type #{value} (#{value.class})")
72
226
  end
73
227
  end
74
228
 
@@ -97,6 +251,53 @@ module DuckDB
97
251
  def blob?(value)
98
252
  value.instance_of?(DuckDB::Blob) || value.encoding == Encoding::BINARY
99
253
  end
254
+
255
+ def iso8601_interval_to_hash(value)
256
+ digit = ''
257
+ time = false
258
+ hash = {}
259
+ hash.default = 0
260
+
261
+ value.each_char do |c|
262
+ if '-0123456789.'.include?(c)
263
+ digit += c
264
+ elsif c == 'T'
265
+ time = true
266
+ digit = ''
267
+ elsif c == 'M'
268
+ m_interval_to_hash(hash, digit, time)
269
+ digit = ''
270
+ elsif c == 'S'
271
+ s_interval_to_hash(hash, digit)
272
+ digit = ''
273
+ elsif 'YDH'.include?(c)
274
+ hash[c] = digit.to_i
275
+ digit = ''
276
+ elsif c != 'P'
277
+ raise ArgumentError, "The argument `#{value}` can't be parse."
278
+ end
279
+ end
280
+ hash
281
+ end
282
+
283
+ def m_interval_to_hash(hash, digit, time)
284
+ key = time ? 'TM' : 'M'
285
+ hash[key] = digit.to_i
286
+ end
287
+
288
+ def s_interval_to_hash(hash, digit)
289
+ sec, msec = digit.split('.')
290
+ hash['S'] = sec.to_i
291
+ hash['MS'] = "#{msec}000000"[0, 6].to_i
292
+ hash['MS'] *= -1 if hash['S'].negative?
293
+ end
294
+
295
+ def hash_to__append_interval_args(hash)
296
+ months = hash['Y'] * 12 + hash['M']
297
+ days = hash['D']
298
+ micros = (hash['H'] * 3600 + hash['TM'] * 60 + hash['S']) * 1_000_000 + hash['MS']
299
+ [months, days, micros]
300
+ end
100
301
  end
101
302
  end
102
303
  end
@@ -22,7 +22,7 @@ module DuckDB
22
22
  when Integer
23
23
  bind_varchar(i, value.to_s)
24
24
  else
25
- rb_raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
25
+ raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
26
26
  end
27
27
  end
28
28
 
@@ -40,7 +40,7 @@ module DuckDB
40
40
  def bind(i, value)
41
41
  case value
42
42
  when NilClass
43
- 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')
43
+ respond_to?(:bind_null) ? bind_null(i) : raise(DuckDB::Error, 'This bind method does not support nil value. Re-compile ruby-duckdb with DuckDB version >= 0.1.1')
44
44
  when Float
45
45
  bind_double(i, value)
46
46
  when Integer
@@ -63,7 +63,7 @@ module DuckDB
63
63
  when Date
64
64
  bind_varchar(i, value.strftime('%Y-%m-%d'))
65
65
  else
66
- rb_raise(DuckDB::Error, "not supported type #{value} (value.class)")
66
+ raise(DuckDB::Error, "not supported type `#{value}` (#{value.class})")
67
67
  end
68
68
  end
69
69
 
@@ -1,5 +1,5 @@
1
1
  module DuckDB
2
2
  # The version string of ruby-duckdb.
3
3
  # Currently, ruby-duckdb is NOT semantic versioning.
4
- VERSION = '0.2.8.0'.freeze
4
+ VERSION = '0.2.9.0'.freeze
5
5
  end
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.2.8.0
4
+ version: 0.2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masaki Suketa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-04 00:00:00.000000000 Z
11
+ date: 2021-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -79,8 +79,8 @@ files:
79
79
  - ".github/workflows/test_on_ubuntu.yml"
80
80
  - ".github/workflows/test_on_windows.yml"
81
81
  - ".gitignore"
82
- - ".travis.yml"
83
82
  - CHANGELOG.md
83
+ - CONTRIBUTION.md
84
84
  - Gemfile
85
85
  - Gemfile.lock
86
86
  - LICENSE
@@ -131,7 +131,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
131
131
  requirements:
132
132
  - - ">="
133
133
  - !ruby/object:Gem::Version
134
- version: 2.5.0
134
+ version: 2.6.0
135
135
  required_rubygems_version: !ruby/object:Gem::Requirement
136
136
  requirements:
137
137
  - - ">="
data/.travis.yml DELETED
@@ -1,18 +0,0 @@
1
- language: ruby
2
- cache:
3
- bundler: true
4
- directories:
5
- - ${HOME}/duckdb-v0.2.8
6
- before_install:
7
- - yes | gem update --system
8
- - if [[ ! -d ${HOME}/duckdb-v0.2.8/build ]]; then cd ${HOME} && git clone -b v0.2.8 https://github.com/cwida/duckdb.git duckdb-v0.2.8 && cd duckdb-v0.2.8 && make && cd ${TRAVIS_BUILD_DIR}; fi
9
-
10
- env:
11
- - DUCKDB_VERSION=0.2.8
12
- rvm:
13
- - 2.5.8
14
- - 2.6.8
15
- - 2.7.4
16
- - 3.0.2
17
- - ruby-head
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/