duckdb 0.8.0 → 0.8.1.1

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: 8e8040d795ad975b4472fe439aa2cfb403ad5f24f9c602bca7dbdf6103c37b65
4
- data.tar.gz: 82cd61d78fec67f62be8ad0280305b224ae605ffd2ea453855168dcaa77f4d32
3
+ metadata.gz: a81125e254f14fe183bff2a5e471274ed995f22dcb7b3b62470c222bc4e69568
4
+ data.tar.gz: 2c84267e72092bde6c2948a3b02d1eeb7ae5ad7a941bdf7b134b4ecc2076415f
5
5
  SHA512:
6
- metadata.gz: b54b53aa6ec96ef9e8607bea7f96b8ef9fc7c4e2189675e575eeabdfaa2d608320c02ac86dc8ca7453cd9d314ceb943241a0a858689c85b8221374ff5dc6e00e
7
- data.tar.gz: 86936495132694846dd8931998c9f716d405571dd34842f6f8563ad531f2e0b8fb678f4477cff9ec33a1c0d3affc615884a4dd8063909f57cb8e4a1da2a0434f
6
+ metadata.gz: af45ace99bfb16d3d880a47236ee95c6ba8dea03c32f734f1b0491f4f996b862578570501ebc70a00d9fa24c18180883f4961839f7df43b077b7cc2ea1c5bff3
7
+ data.tar.gz: d0307462d6e9be514087c6eb31750557896f792b4eaa0aa6d2558162eacbbd3ffc7d7289fcf392e11246e0dd55daa2a3c50f534f917749a92a038547d4453d64
@@ -15,8 +15,8 @@ jobs:
15
15
  runs-on: macos-latest
16
16
  strategy:
17
17
  matrix:
18
- ruby: ['2.7.8', '3.0.6', '3.1.4', '3.2.2', 'head']
19
- duckdb: ['0.7.1', '0.8.0']
18
+ ruby: ['2.7.8', '3.0.6', '3.1.4', '3.2.2', '3.3.0-preview1', 'head']
19
+ duckdb: ['0.7.1', '0.8.1']
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v3
@@ -15,8 +15,8 @@ jobs:
15
15
  runs-on: ubuntu-latest
16
16
  strategy:
17
17
  matrix:
18
- ruby: ['2.7.8', '3.0.6', '3.1.4', '3.2.2', 'head']
19
- duckdb: ['0.7.1', '0.8.0']
18
+ ruby: ['2.7.8', '3.0.6', '3.1.4', '3.2.2', '3.3.0-preview1', 'head']
19
+ duckdb: ['0.7.1', '0.8.1']
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v3
@@ -16,7 +16,7 @@ jobs:
16
16
  strategy:
17
17
  matrix:
18
18
  ruby: ['2.7.8', '3.0.6', '3.1.4', '3.2.2', 'ucrt', 'mingw', 'mswin', 'head']
19
- duckdb: ['0.7.1', '0.8.0']
19
+ duckdb: ['0.7.1', '0.8.1']
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # ChangeLog
2
2
 
3
+ # 0.8.1.1
4
+ - DuckDB::Result#chunk_each supports:
5
+ - UTINYINT
6
+ - USMALLINT
7
+ - UINTEGER
8
+ - UBIGINT
9
+ - fix memory leak of:
10
+ - `DuckDB::Result#_enum_dictionary_value`
11
+ - `DuckDB::Result#_enum_dictionary_size`
12
+ - `DuckDB::Result#_enum_internal_type`
13
+
14
+ # 0.8.1
15
+ - bump duckdb to 0.8.1.
16
+ - add `DuckDB::Result#chunk_each`, `DuckDB::Result.use_chunk_each=`, `DuckDB::Result#use_chunk_each?`
17
+ The current behavior of `DuckDB::Result#each` is same as older version.
18
+ But `DuckDB::Result#each` behavior will be changed like as `DuckDB::Result#chunk_each` in near future release.
19
+ And there are some breaking changes.
20
+ Write `DuckdDB::Result.use_chunk_each = true` if you want to try new behavior.
21
+ ```
22
+ DuckDB::Result.use_chunk_each = true
23
+
24
+ result = con.query('SELECT ....')
25
+ result.each do |record| # <= each method behavior is same as DuckDB::Result#chunk_each
26
+ ...
27
+ end
28
+ ```
29
+ Thanks to @stephenprater.
30
+ - support enum type in DuckDB::Result#chunk_each.
31
+ - support uuid type in DuckDB::Result#chunk_each.
32
+
33
+ ## Breaking Change
34
+
35
+ - DuckDB::Config.set_config does not raise exception when invalid key specified.
36
+ Instead, DuckDB::Database.open raises DuckDB::Error with invalid key configuration.
37
+
3
38
  # 0.8.0
4
39
  - bump duckdb to 0.8.0
5
40
  - add DuckDB::Result#_to_decimal_internal
data/Dockerfile CHANGED
@@ -1,7 +1,7 @@
1
- ARG RUBY_VERSION=3.2.1
1
+ ARG RUBY_VERSION=3.2.2
2
2
  FROM ruby:${RUBY_VERSION}
3
3
 
4
- ARG DUCKDB_VERSION=0.7.1
4
+ ARG DUCKDB_VERSION=0.8.0
5
5
 
6
6
  RUN apt update -qq && \
7
7
  apt install -y build-essential curl git wget
data/Gemfile CHANGED
@@ -7,3 +7,7 @@ if /(linux|darwin)/ =~ RUBY_PLATFORM
7
7
  gem 'benchmark-ips'
8
8
  gem 'stackprof'
9
9
  end
10
+
11
+ if /linux/ =~ RUBY_PLATFORM
12
+ gem 'ruby_memcheck'
13
+ end
data/Gemfile.lock CHANGED
@@ -1,17 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- duckdb (0.8.0)
4
+ duckdb (0.8.1.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  benchmark-ips (2.12.0)
10
- minitest (5.18.0)
10
+ mini_portile2 (2.8.2)
11
+ minitest (5.18.1)
12
+ nokogiri (1.15.2)
13
+ mini_portile2 (~> 2.8.2)
14
+ racc (~> 1.4)
15
+ nokogiri (1.15.2-x86_64-linux)
16
+ racc (~> 1.4)
17
+ racc (1.7.1)
11
18
  rake (13.0.6)
12
- rake-compiler (1.2.1)
19
+ rake-compiler (1.2.3)
13
20
  rake
14
- stackprof (0.2.24)
21
+ ruby_memcheck (1.3.2)
22
+ nokogiri
23
+ stackprof (0.2.25)
15
24
 
16
25
  PLATFORMS
17
26
  ruby
@@ -24,7 +33,8 @@ DEPENDENCIES
24
33
  minitest (~> 5.0)
25
34
  rake (~> 13.0)
26
35
  rake-compiler
36
+ ruby_memcheck
27
37
  stackprof
28
38
 
29
39
  BUNDLED WITH
30
- 2.4.9
40
+ 2.4.10
data/Rakefile CHANGED
@@ -1,19 +1,41 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ ruby_memcheck_avaiable = begin
4
+ require 'ruby_memcheck'
5
+ true
6
+ rescue LoadError
7
+ false
8
+ end
3
9
 
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
10
+
11
+ if ruby_memcheck_avaiable
12
+ RubyMemcheck.config(
13
+ binary_name: 'duckdb/duckdb_native',
14
+ valgrind_options: ['--max-threads=1000']
15
+ )
16
+ end
17
+
18
+ test_config = lambda do |t|
19
+ t.libs << 'test'
20
+ t.libs << 'lib'
21
+ t.test_files = FileList['test/**/*_test.rb']
22
+ end
23
+
24
+ Rake::TestTask.new(test: :compile, &test_config)
25
+
26
+ if ruby_memcheck_avaiable
27
+ namespace :test do
28
+ RubyMemcheck::TestTask.new(valgrind: :compile, &test_config)
29
+ end
8
30
  end
9
31
 
10
- require "rake/extensiontask"
32
+ require 'rake/extensiontask'
11
33
 
12
- task :build => :compile
34
+ task build: :compile
13
35
 
14
- Rake::ExtensionTask.new("duckdb_native") do |ext|
36
+ Rake::ExtensionTask.new('duckdb_native') do |ext|
15
37
  ext.ext_dir = 'ext/duckdb'
16
- ext.lib_dir = "lib/duckdb"
38
+ ext.lib_dir = 'lib/duckdb'
17
39
  end
18
40
 
19
- task :default => [:clobber, :compile, :test]
41
+ task default: %i[clobber compile test]
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'duckdb'
5
+ require 'benchmark/ips'
6
+
7
+ db = DuckDB::Database.open
8
+ con = db.connect
9
+ con.query('CREATE TABLE hugeints (hugeint_val HUGEINT)')
10
+ con.query('INSERT INTO hugeints VALUES (1234567890123456789012345678901234)')
11
+ result = con.query('SELECT hugeint_val FROM hugeints')
12
+
13
+ Benchmark.ips do |x|
14
+ x.report('hugeint_convert') { result.each.to_a[0][0] }
15
+ end
16
+
17
+ __END__
18
+
19
+ ## before
20
+ ```
21
+ ✦ ❯ ruby benchmark/get_converter_module_ips.rb
22
+ Warming up --------------------------------------
23
+ hugeint_convert 45.376k i/100ms
24
+ Calculating -------------------------------------
25
+ hugeint_convert 552.127k (± 0.7%) i/s - 2.768M in 5.013483s
26
+ ```
@@ -0,0 +1,6 @@
1
+ #ifndef RUBY_DUCKDB_CONVERTER_H
2
+ #define RUBY_DUCKDB_CONVERTER_H
3
+
4
+ void init_duckdb_converter(void);
5
+
6
+ #endif
@@ -0,0 +1,7 @@
1
+ #include "ruby-duckdb.h"
2
+
3
+ VALUE mDuckDBConverter;
4
+
5
+ void init_duckdb_converter(void) {
6
+ mDuckDBConverter = rb_define_module_under(mDuckDB, "Converter");
7
+ }
data/ext/duckdb/duckdb.c CHANGED
@@ -2,23 +2,17 @@
2
2
 
3
3
  VALUE mDuckDB;
4
4
 
5
- #ifdef HAVE_DUCKDB_H_GE_V060
6
5
  static VALUE duckdb_s_library_version(VALUE self);
7
- #endif
8
6
 
9
- #ifdef HAVE_DUCKDB_H_GE_V060
10
7
  static VALUE duckdb_s_library_version(VALUE self) {
11
8
  return rb_str_new2(duckdb_library_version());
12
9
  }
13
- #endif
14
10
 
15
11
  void
16
12
  Init_duckdb_native(void) {
17
13
  mDuckDB = rb_define_module("DuckDB");
18
14
 
19
- #ifdef HAVE_DUCKDB_H_GE_V060
20
15
  rb_define_singleton_method(mDuckDB, "library_version", duckdb_s_library_version, 0);
21
- #endif
22
16
 
23
17
  init_duckdb_error();
24
18
  init_duckdb_database();
@@ -29,5 +23,5 @@ Init_duckdb_native(void) {
29
23
  init_duckdb_blob();
30
24
  init_duckdb_appender();
31
25
  init_duckdb_config();
32
-
26
+ init_duckdb_converter();
33
27
  }
@@ -20,12 +20,12 @@ end
20
20
 
21
21
  dir_config('duckdb')
22
22
 
23
- check_duckdb_library('duckdb_pending_prepared', '0.5.0')
24
-
25
- # check duckdb >= 0.6.0
26
- have_func('duckdb_value_string', 'duckdb.h')
23
+ check_duckdb_library('duckdb_extract_statements', '0.7.0')
27
24
 
28
25
  # check duckdb >= 0.7.0
29
26
  have_func('duckdb_extract_statements', 'duckdb.h')
30
27
 
28
+ # check duckdb >= 0.8.0
29
+ have_func('duckdb_string_is_inlined', 'duckdb.h')
30
+
31
31
  create_makefile('duckdb/duckdb_native')
data/ext/duckdb/result.c CHANGED
@@ -37,6 +37,23 @@ static VALUE duckdb_result__enum_internal_type(VALUE oDuckDBResult, VALUE col_id
37
37
  static VALUE duckdb_result__enum_dictionary_size(VALUE oDuckDBResult, VALUE col_idx);
38
38
  static VALUE duckdb_result__enum_dictionary_value(VALUE oDuckDBResult, VALUE col_idx, VALUE idx);
39
39
 
40
+ #ifdef HAVE_DUCKDB_H_GE_V080
41
+ static VALUE vector_date(void *vector_data, idx_t row_idx);
42
+ static VALUE vector_timestamp(void* vector_data, idx_t row_idx);
43
+ static VALUE vector_interval(void* vector_data, idx_t row_idx);
44
+ static VALUE vector_blob(void* vector_data, idx_t row_idx);
45
+ static VALUE vector_varchar(void* vector_data, idx_t row_idx);
46
+ static VALUE vector_hugeint(void* vector_data, idx_t row_idx);
47
+ static VALUE vector_decimal(duckdb_logical_type ty, void* vector_data, idx_t row_idx);
48
+ static VALUE vector_enum(duckdb_logical_type ty, void* vector_data, idx_t row_idx);
49
+ static VALUE vector_list(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx);
50
+ static VALUE vector_map(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx);
51
+ static VALUE vector_struct(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx);
52
+ static VALUE vector_uuid(void* vector_data, idx_t row_idx);
53
+ static VALUE vector_value(duckdb_vector vector, idx_t row_idx);
54
+ static VALUE duckdb_result_chunk_each(VALUE oDuckDBResult);
55
+ #endif
56
+
40
57
  static const rb_data_type_t result_data_type = {
41
58
  "DuckDB/Result",
42
59
  {NULL, deallocate, memsize,},
@@ -300,25 +317,15 @@ static VALUE duckdb_result__to_double(VALUE oDuckDBResult, VALUE row_idx, VALUE
300
317
 
301
318
  static VALUE duckdb_result__to_string(VALUE oDuckDBResult, VALUE row_idx, VALUE col_idx) {
302
319
  rubyDuckDBResult *ctx;
303
- #ifdef HAVE_DUCKDB_H_GE_V060
304
320
  duckdb_string p;
305
- #else
306
- char *p;
307
- #endif
308
321
  VALUE obj;
322
+
309
323
  TypedData_Get_Struct(oDuckDBResult, rubyDuckDBResult, &result_data_type, ctx);
310
324
 
311
- #ifdef HAVE_DUCKDB_H_GE_V060
312
325
  p = duckdb_value_string(&(ctx->result), NUM2LL(col_idx), NUM2LL(row_idx));
313
326
  if (p.data) {
314
327
  obj = rb_utf8_str_new(p.data, p.size);
315
328
  duckdb_free(p.data);
316
- #else
317
- p = duckdb_value_varchar(&(ctx->result), NUM2LL(col_idx), NUM2LL(row_idx));
318
- if (p) {
319
- obj = rb_utf8_str_new_cstr(p);
320
- duckdb_free(p);
321
- #endif
322
329
  return obj;
323
330
  }
324
331
  return Qnil;
@@ -326,23 +333,13 @@ static VALUE duckdb_result__to_string(VALUE oDuckDBResult, VALUE row_idx, VALUE
326
333
 
327
334
  static VALUE duckdb_result__to_string_internal(VALUE oDuckDBResult, VALUE row_idx, VALUE col_idx) {
328
335
  rubyDuckDBResult *ctx;
329
- #ifdef HAVE_DUCKDB_H_GE_V060
330
336
  duckdb_string p;
331
- #else
332
- char *p;
333
- #endif
334
337
  VALUE obj;
335
338
  TypedData_Get_Struct(oDuckDBResult, rubyDuckDBResult, &result_data_type, ctx);
336
339
 
337
- #ifdef HAVE_DUCKDB_H_GE_V060
338
340
  p = duckdb_value_string_internal(&(ctx->result), NUM2LL(col_idx), NUM2LL(row_idx));
339
341
  if (p.data) {
340
342
  obj = rb_utf8_str_new(p.data, p.size);
341
- #else
342
- p = duckdb_value_varchar_internal(&(ctx->result), NUM2LL(col_idx), NUM2LL(row_idx));
343
- if (p) {
344
- obj = rb_utf8_str_new_cstr(p);
345
- #endif
346
343
  return obj;
347
344
  }
348
345
  return Qnil;
@@ -365,6 +362,7 @@ static VALUE duckdb_result__enum_internal_type(VALUE oDuckDBResult, VALUE col_id
365
362
  if (logical_type) {
366
363
  type = LL2NUM(duckdb_enum_internal_type(logical_type));
367
364
  }
365
+ duckdb_destroy_logical_type(&logical_type);
368
366
  return type;
369
367
  }
370
368
 
@@ -378,6 +376,7 @@ static VALUE duckdb_result__enum_dictionary_size(VALUE oDuckDBResult, VALUE col_
378
376
  if (logical_type) {
379
377
  size = UINT2NUM(duckdb_enum_dictionary_size(logical_type));
380
378
  }
379
+ duckdb_destroy_logical_type(&logical_type);
381
380
  return size;
382
381
  }
383
382
 
@@ -396,6 +395,7 @@ static VALUE duckdb_result__enum_dictionary_value(VALUE oDuckDBResult, VALUE col
396
395
  duckdb_free(p);
397
396
  }
398
397
  }
398
+ duckdb_destroy_logical_type(&logical_type);
399
399
  return value;
400
400
  }
401
401
 
@@ -403,6 +403,324 @@ VALUE create_result(void) {
403
403
  return allocate(cDuckDBResult);
404
404
  }
405
405
 
406
+ #ifdef HAVE_DUCKDB_H_GE_V080
407
+ static VALUE vector_date(void *vector_data, idx_t row_idx) {
408
+ duckdb_date_struct date = duckdb_from_date(((duckdb_date *) vector_data)[row_idx]);
409
+
410
+ return rb_funcall(mDuckDBConverter, rb_intern("_to_date"), 3,
411
+ INT2FIX(date.year),
412
+ INT2FIX(date.month),
413
+ INT2FIX(date.day)
414
+ );
415
+ }
416
+
417
+ static VALUE vector_timestamp(void* vector_data, idx_t row_idx) {
418
+ duckdb_timestamp_struct data = duckdb_from_timestamp(((duckdb_timestamp *)vector_data)[row_idx]);
419
+ return rb_funcall(mDuckDBConverter, rb_intern("_to_time"), 7,
420
+ INT2FIX(data.date.year),
421
+ INT2FIX(data.date.month),
422
+ INT2FIX(data.date.day),
423
+ INT2FIX(data.time.hour),
424
+ INT2FIX(data.time.min),
425
+ INT2FIX(data.time.sec),
426
+ INT2NUM(data.time.micros)
427
+ );
428
+ }
429
+
430
+ static VALUE vector_interval(void* vector_data, idx_t row_idx) {
431
+ duckdb_interval data = ((duckdb_interval *)vector_data)[row_idx];
432
+ return rb_funcall(mDuckDBConverter, rb_intern("_to_interval_from_vector"), 3,
433
+ INT2NUM(data.months),
434
+ INT2NUM(data.days),
435
+ LL2NUM(data.micros)
436
+ );
437
+ }
438
+
439
+ static VALUE vector_blob(void* vector_data, idx_t row_idx) {
440
+ duckdb_string_t s = (((duckdb_string_t *)vector_data)[row_idx]);
441
+ if(duckdb_string_is_inlined(s)) {
442
+ return rb_str_new(s.value.inlined.inlined, s.value.inlined.length);
443
+ } else {
444
+ return rb_str_new(s.value.pointer.ptr, s.value.pointer.length);
445
+ }
446
+ }
447
+
448
+ static VALUE vector_varchar(void* vector_data, idx_t row_idx) {
449
+ duckdb_string_t s = (((duckdb_string_t *)vector_data)[row_idx]);
450
+ if(duckdb_string_is_inlined(s)) {
451
+ return rb_utf8_str_new(s.value.inlined.inlined, s.value.inlined.length);
452
+ } else {
453
+ return rb_utf8_str_new(s.value.pointer.ptr, s.value.pointer.length);
454
+ }
455
+ }
456
+
457
+ static VALUE vector_hugeint(void* vector_data, idx_t row_idx) {
458
+ duckdb_hugeint hugeint = ((duckdb_hugeint *)vector_data)[row_idx];
459
+ return rb_funcall(mDuckDBConverter, rb_intern("_to_hugeint_from_vector"), 2,
460
+ ULL2NUM(hugeint.lower),
461
+ LL2NUM(hugeint.upper)
462
+ );
463
+ }
464
+
465
+ static VALUE vector_decimal(duckdb_logical_type ty, void* vector_data, idx_t row_idx) {
466
+ uint8_t width = duckdb_decimal_width(ty);
467
+ uint8_t scale = duckdb_decimal_scale(ty);
468
+ duckdb_type type = duckdb_decimal_internal_type(ty);
469
+ duckdb_hugeint value;
470
+
471
+ value.upper = 0;
472
+ value.lower = 0;
473
+
474
+ switch(duckdb_decimal_internal_type(ty)) {
475
+ case DUCKDB_TYPE_HUGEINT:
476
+ value = ((duckdb_hugeint *) vector_data)[row_idx];
477
+ break;
478
+ default:
479
+ rb_warn("Unknown decimal internal type %d", type);
480
+ }
481
+
482
+ return rb_funcall(mDuckDBConverter, rb_intern("_to_decimal_from_vector"), 4,
483
+ INT2FIX(width),
484
+ INT2FIX(scale),
485
+ ULL2NUM(value.lower),
486
+ LL2NUM(value.upper)
487
+ );
488
+ }
489
+
490
+ static VALUE vector_enum(duckdb_logical_type ty, void* vector_data, idx_t row_idx) {
491
+ duckdb_type type = duckdb_enum_internal_type(ty);
492
+ uint8_t index;
493
+ char *p;
494
+ VALUE value = Qnil;
495
+
496
+ switch(type) {
497
+ case DUCKDB_TYPE_UTINYINT:
498
+ index = ((uint8_t *) vector_data)[row_idx];
499
+ p = duckdb_enum_dictionary_value(ty, index);
500
+ if (p) {
501
+ value = rb_utf8_str_new_cstr(p);
502
+ duckdb_free(p);
503
+ }
504
+ break;
505
+ default:
506
+ rb_warn("Unknown enum internal type %d", type);
507
+ }
508
+ return value;
509
+ }
510
+
511
+ static VALUE vector_list(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx) {
512
+ // Lists are stored as vectors within vectors
513
+
514
+ VALUE ary = Qnil;
515
+ VALUE element = Qnil;
516
+ idx_t i;
517
+
518
+ // rb_warn("ruby-duckdb does not support List yet");
519
+
520
+ duckdb_logical_type child_logical_type = duckdb_list_type_child_type(ty);
521
+ // duckdb_type child_type = duckdb_get_type_id(child_logical_type);
522
+
523
+ duckdb_list_entry list_entry = ((duckdb_list_entry *)vector)[row_idx];
524
+ ary = rb_ary_new2(list_entry.length);
525
+
526
+ for (i = list_entry.offset; i < list_entry.offset + list_entry.length; ++i) {
527
+ /*
528
+ * FIXME: How to get the child element?
529
+ */
530
+ // element = ???
531
+ rb_ary_store(ary, i - list_entry.offset, element);
532
+ }
533
+ duckdb_destroy_logical_type(&child_logical_type);
534
+ return ary;
535
+ }
536
+
537
+ static VALUE vector_map(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx) {
538
+ VALUE hash = rb_hash_new();
539
+
540
+ duckdb_logical_type key_logical_type = duckdb_map_type_key_type(ty);
541
+ duckdb_logical_type value_logical_type = duckdb_map_type_value_type(ty);
542
+ // duckdb_type key_type = duckdb_get_type_id(key_logical_type);
543
+ // duckdb_type value_type = duckdb_get_type_id(value_logical_type);
544
+
545
+ /*
546
+ * FIXME: How to get key and value?
547
+ *
548
+ * rb_hash_aset(hash, key, value);
549
+ */
550
+ duckdb_destroy_logical_type(&key_logical_type);
551
+ duckdb_destroy_logical_type(&value_logical_type);
552
+ return hash;
553
+ }
554
+
555
+ static VALUE vector_struct(duckdb_logical_type ty, duckdb_vector vector, idx_t row_idx) {
556
+ VALUE hash = rb_hash_new();
557
+ VALUE value = Qnil;
558
+ VALUE key = Qnil;
559
+ char *p;
560
+
561
+ idx_t child_count = duckdb_struct_type_child_count(ty);
562
+
563
+ for (idx_t i = 0; i < child_count; ++i) {
564
+ p = duckdb_struct_type_child_name(ty, i);
565
+ if (p) {
566
+ key = rb_str_new2(p);
567
+ // FIXME
568
+ // How to get Struct values?
569
+ // value = ???
570
+ // duckdb_vector child_vector = duckdb_struct_vector_get_child(vector, i);
571
+ // VALUE value = vector_value(child_vector, i);
572
+ rb_hash_aset(hash, key, value);
573
+ duckdb_free(p);
574
+ }
575
+ }
576
+
577
+ return hash;
578
+ }
579
+
580
+ static VALUE vector_uuid(void* vector_data, idx_t row_idx) {
581
+ duckdb_hugeint hugeint = ((duckdb_hugeint *)vector_data)[row_idx];
582
+ return rb_funcall(mDuckDBConverter, rb_intern("_to_uuid_from_vector"), 2,
583
+ ULL2NUM(hugeint.lower),
584
+ LL2NUM(hugeint.upper)
585
+ );
586
+ }
587
+
588
+ static VALUE vector_value(duckdb_vector vector, idx_t row_idx) {
589
+ uint64_t *validity;
590
+ duckdb_logical_type ty;
591
+ duckdb_type type_id;
592
+ void* vector_data;
593
+ VALUE obj = Qnil;
594
+
595
+ validity = duckdb_vector_get_validity(vector);
596
+ if (!duckdb_validity_row_is_valid(validity, row_idx)) {
597
+ return Qnil;
598
+ }
599
+
600
+ ty = duckdb_vector_get_column_type(vector);
601
+ type_id = duckdb_get_type_id(ty);
602
+ vector_data = duckdb_vector_get_data(vector);
603
+
604
+ switch(type_id) {
605
+ case DUCKDB_TYPE_INVALID:
606
+ obj = Qnil;
607
+ break;
608
+ case DUCKDB_TYPE_BOOLEAN:
609
+ obj = (((bool*) vector_data)[row_idx]) ? Qtrue : Qfalse;
610
+ break;
611
+ case DUCKDB_TYPE_TINYINT:
612
+ obj = INT2FIX(((int8_t *) vector_data)[row_idx]);
613
+ break;
614
+ case DUCKDB_TYPE_SMALLINT:
615
+ obj = INT2FIX(((int16_t *) vector_data)[row_idx]);
616
+ break;
617
+ case DUCKDB_TYPE_INTEGER:
618
+ obj = INT2NUM(((int32_t *) vector_data)[row_idx]);
619
+ break;
620
+ case DUCKDB_TYPE_BIGINT:
621
+ obj = LL2NUM(((int64_t *) vector_data)[row_idx]);
622
+ break;
623
+ case DUCKDB_TYPE_UTINYINT:
624
+ obj = INT2FIX(((uint8_t *) vector_data)[row_idx]);
625
+ break;
626
+ case DUCKDB_TYPE_USMALLINT:
627
+ obj = INT2FIX(((uint16_t *) vector_data)[row_idx]);
628
+ break;
629
+ case DUCKDB_TYPE_UINTEGER:
630
+ obj = UINT2NUM(((uint32_t *) vector_data)[row_idx]);
631
+ break;
632
+ case DUCKDB_TYPE_UBIGINT:
633
+ obj = ULL2NUM(((uint64_t *) vector_data)[row_idx]);
634
+ break;
635
+ case DUCKDB_TYPE_HUGEINT:
636
+ obj = vector_hugeint(vector_data, row_idx);
637
+ break;
638
+ case DUCKDB_TYPE_FLOAT:
639
+ obj = DBL2NUM((((float *) vector_data)[row_idx]));
640
+ break;
641
+ case DUCKDB_TYPE_DOUBLE:
642
+ obj = DBL2NUM((((double *) vector_data)[row_idx]));
643
+ break;
644
+ case DUCKDB_TYPE_DATE:
645
+ obj = vector_date(vector_data, row_idx);
646
+ break;
647
+ case DUCKDB_TYPE_TIMESTAMP:
648
+ obj = vector_timestamp(vector_data, row_idx);
649
+ break;
650
+ case DUCKDB_TYPE_INTERVAL:
651
+ obj = vector_interval(vector_data, row_idx);
652
+ break;
653
+ case DUCKDB_TYPE_VARCHAR:
654
+ obj = vector_varchar(vector_data, row_idx);
655
+ break;
656
+ case DUCKDB_TYPE_BLOB:
657
+ obj = vector_blob(vector_data, row_idx);
658
+ break;
659
+ case DUCKDB_TYPE_DECIMAL:
660
+ obj = vector_decimal(ty, vector_data, row_idx);
661
+ break;
662
+ case DUCKDB_TYPE_ENUM:
663
+ obj = vector_enum(ty, vector_data, row_idx);
664
+ break;
665
+ case DUCKDB_TYPE_LIST:
666
+ obj = vector_list(ty, vector_data, row_idx);
667
+ break;
668
+ case DUCKDB_TYPE_MAP:
669
+ obj = vector_map(ty, vector_data, row_idx);
670
+ break;
671
+ case DUCKDB_TYPE_STRUCT:
672
+ obj = vector_struct(ty, vector_data, row_idx);
673
+ break;
674
+ case DUCKDB_TYPE_UUID:
675
+ obj = vector_uuid(vector_data, row_idx);
676
+ break;
677
+ default:
678
+ rb_warn("Unknown type %d", type_id);
679
+ obj = Qnil;
680
+ }
681
+
682
+ duckdb_destroy_logical_type(&ty);
683
+ return obj;
684
+ }
685
+
686
+ static VALUE duckdb_result_chunk_each(VALUE oDuckDBResult) {
687
+ rubyDuckDBResult *ctx;
688
+ VALUE row;
689
+ idx_t col_count;
690
+ idx_t row_count;
691
+ idx_t chunk_count;
692
+ idx_t col_idx;
693
+ idx_t row_idx;
694
+ idx_t chunk_idx;
695
+ duckdb_data_chunk chunk;
696
+ duckdb_vector vector;
697
+ VALUE val;
698
+
699
+ TypedData_Get_Struct(oDuckDBResult, rubyDuckDBResult, &result_data_type, ctx);
700
+
701
+ col_count = duckdb_column_count(&(ctx->result));
702
+ chunk_count = duckdb_result_chunk_count(ctx->result);
703
+
704
+ RETURN_ENUMERATOR(oDuckDBResult, 0, 0);
705
+
706
+ for (chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) {
707
+ chunk = duckdb_result_get_chunk(ctx->result, chunk_idx);
708
+ row_count = duckdb_data_chunk_get_size(chunk);
709
+ for (row_idx = 0; row_idx < row_count; row_idx++) {
710
+ row = rb_ary_new2(col_count);
711
+ for (col_idx = 0; col_idx < col_count; col_idx++) {
712
+ vector = duckdb_data_chunk_get_vector(chunk, col_idx);
713
+ val = vector_value(vector, row_idx);
714
+ rb_ary_store(row, col_idx, val);
715
+ }
716
+ rb_yield(row);
717
+ }
718
+ duckdb_destroy_data_chunk(&chunk);
719
+ }
720
+ return Qnil;
721
+ }
722
+ #endif
723
+
406
724
  void init_duckdb_result(void) {
407
725
  cDuckDBResult = rb_define_class_under(mDuckDB, "Result", rb_cObject);
408
726
  rb_define_alloc_func(cDuckDBResult, allocate);
@@ -428,4 +746,7 @@ void init_duckdb_result(void) {
428
746
  rb_define_private_method(cDuckDBResult, "_enum_internal_type", duckdb_result__enum_internal_type, 1);
429
747
  rb_define_private_method(cDuckDBResult, "_enum_dictionary_size", duckdb_result__enum_dictionary_size, 1);
430
748
  rb_define_private_method(cDuckDBResult, "_enum_dictionary_value", duckdb_result__enum_dictionary_value, 2);
749
+ #ifdef HAVE_DUCKDB_H_GE_V080
750
+ rb_define_method(cDuckDBResult, "chunk_each", duckdb_result_chunk_each, 0);
751
+ #endif
431
752
  }
@@ -4,14 +4,14 @@
4
4
  #include "ruby.h"
5
5
  #include <duckdb.h>
6
6
 
7
- #ifdef HAVE_DUCKDB_VALUE_STRING
8
- #define HAVE_DUCKDB_H_GE_V060 1
9
- #endif
10
-
11
7
  #ifdef HAVE_DUCKDB_EXTRACT_STATEMENTS
12
8
  #define HAVE_DUCKDB_H_GE_V070 1
13
9
  #endif
14
10
 
11
+ #ifdef HAVE_DUCKDB_STRING_IS_INLINED
12
+ #define HAVE_DUCKDB_H_GE_V080 1
13
+ #endif
14
+
15
15
  #include "./error.h"
16
16
  #include "./database.h"
17
17
  #include "./connection.h"
@@ -19,6 +19,7 @@
19
19
  #include "./column.h"
20
20
  #include "./prepared_statement.h"
21
21
  #include "./util.h"
22
+ #include "./converter.h"
22
23
 
23
24
  #include "./blob.h"
24
25
  #include "./appender.h"
@@ -30,5 +31,6 @@ extern VALUE cDuckDBConnection;
30
31
  extern VALUE cDuckDBBlob;
31
32
  extern VALUE cDuckDBConfig;
32
33
  extern VALUE eDuckDBError;
34
+ extern VALUE mDuckDBConverter;
33
35
 
34
36
  #endif
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'date'
4
4
  require 'time'
5
- require_relative './converter'
5
+ require_relative 'converter'
6
6
 
7
7
  module DuckDB
8
8
  # The DuckDB::Appender encapsulates DuckDB Appender.
@@ -194,17 +194,9 @@ module DuckDB
194
194
  when TrueClass, FalseClass
195
195
  append_bool(value)
196
196
  when Time
197
- if respond_to?(:append_timestamp)
198
- append_timestamp(value)
199
- else
200
- append_varchar(value.strftime('%Y-%m-%d %H:%M:%S.%N'))
201
- end
197
+ append_timestamp(value)
202
198
  when Date
203
- if respond_to?(:append_date)
204
- append_date(value)
205
- else
206
- append_varchar(value.strftime('%Y-%m-%d'))
207
- end
199
+ append_date(value)
208
200
  else
209
201
  raise(DuckDB::Error, "not supported type #{value} (#{value.class})")
210
202
  end
@@ -1,8 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'date'
4
+
3
5
  module DuckDB
4
6
  module Converter
5
7
  HALF_HUGEINT = 1 << 64
8
+ FLIP_HUGEINT = 1 << 63
9
+
10
+ module_function
11
+
12
+ def _to_date(year, month, day)
13
+ Date.new(year, month, day)
14
+ end
15
+
16
+ def _to_time(year, month, day, hour, minute, second, microsecond)
17
+ Time.local(year, month, day, hour, minute, second, microsecond)
18
+ end
19
+
20
+ def _to_hugeint_from_vector(lower, upper)
21
+ (upper * HALF_HUGEINT) + lower
22
+ end
23
+
24
+ def _to_decimal_from_vector(_width, scale, lower, upper)
25
+ v = _to_hugeint_from_vector(lower, upper).to_s
26
+ v[-scale, 0] = '.'
27
+ BigDecimal(v)
28
+ end
29
+
30
+ def _to_interval_from_vector(months, days, micros)
31
+ hash = { year: 0, month: 0, day: 0, hour: 0, min: 0, sec: 0, usec: 0 }
32
+ hash[:year] = months / 12
33
+ hash[:month] = months % 12
34
+ hash[:day] = days
35
+ hash[:hour] = micros / 3_600_000_000
36
+ hash[:min] = (micros % 3_600_000_000) / 60_000_000
37
+ hash[:sec] = (micros % 60_000_000) / 1_000_000
38
+ hash[:usec] = micros % 1_000_000
39
+ hash
40
+ end
41
+
42
+ def _to_uuid_from_vector(lower, upper)
43
+ upper = upper ^ FLIP_HUGEINT
44
+ upper += HALF_HUGEINT if upper.negative?
45
+
46
+ str = _to_hugeint_from_vector(lower, upper).to_s(16).rjust(32, '0')
47
+ "#{str[0, 8]}-#{str[8, 4]}-#{str[12, 4]}-#{str[16, 4]}-#{str[20, 12]}"
48
+ end
6
49
 
7
50
  private
8
51
 
data/lib/duckdb/result.rb CHANGED
@@ -42,11 +42,27 @@ module DuckDB
42
42
  alias column_size column_count
43
43
  alias row_size row_count
44
44
 
45
+ def self.use_chunk_each=(val)
46
+ raise DuckDB::Error, 'chunk_each is not available. Install duckdb >= 0.8.0 and rerun `gem install duckdb`.' unless instance_methods.include?(:chunk_each)
47
+
48
+ @use_chunk_each = val
49
+ end
50
+
51
+ def self.use_chunk_each?
52
+ !!@use_chunk_each
53
+ end
54
+
45
55
  def each
46
- return to_enum { row_size } unless block_given?
56
+ if self.class.use_chunk_each?
57
+ return chunk_each unless block_given?
58
+
59
+ chunk_each { |row| yield row }
60
+ else
61
+ return to_enum { row_size } unless block_given?
47
62
 
48
- row_count.times do |row_index|
49
- yield row(row_index)
63
+ row_count.times do |row_index|
64
+ yield row(row_index)
65
+ end
50
66
  end
51
67
  end
52
68
 
@@ -3,5 +3,5 @@
3
3
  module DuckDB
4
4
  # The version string of ruby-duckdb.
5
5
  # Currently, ruby-duckdb is NOT semantic versioning.
6
- VERSION = '0.8.0'.freeze
6
+ VERSION = '0.8.1.1'
7
7
  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.8.0
4
+ version: 0.8.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masaki Suketa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-20 00:00:00.000000000 Z
11
+ date: 2023-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -87,6 +87,7 @@ files:
87
87
  - LICENSE
88
88
  - README.md
89
89
  - Rakefile
90
+ - benchmark/get_converter_module_ips.rb
90
91
  - benchmark/to_bigdecimal_ips.rb
91
92
  - benchmark/to_hugeint_ips.rb
92
93
  - benchmark/to_hugeint_profile.rb
@@ -104,6 +105,8 @@ files:
104
105
  - ext/duckdb/config.h
105
106
  - ext/duckdb/connection.c
106
107
  - ext/duckdb/connection.h
108
+ - ext/duckdb/converter.h
109
+ - ext/duckdb/conveter.c
107
110
  - ext/duckdb/database.c
108
111
  - ext/duckdb/database.h
109
112
  - ext/duckdb/duckdb.c