duckdb 0.2.8.0 → 0.2.9.0

Sign up to get free protection for your applications and to get access to all the features.
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/