duckdb 1.3.1.0 → 1.4.1.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: 979c1ffbe5e1137870bd777377ef16cb7ddde8499d5d9fd14fcd6a5fe401ffda
4
- data.tar.gz: 72d970b664aed935c451a95e48818584cf0ecd029ade167bef88235eed62fc6c
3
+ metadata.gz: 172cad0f5262bd839bcae9bd60455db2ae92c3d2777856a807448749b4982d63
4
+ data.tar.gz: 7af629371eb57967458add2ad62d88bf22f4c01eb3fc8e07ec67456c9d0edcb0
5
5
  SHA512:
6
- metadata.gz: cfb967f5ce29966491958e8e0c724716eacb7d36e0692a522b01ebca148e26ae5306cbc9306d64299fec16e79f96967863dad73cdc2c0d77bf4382bacfd25a00
7
- data.tar.gz: 9a1edecd631f9b7e891b49afd71eea1baebdf1defc6488222469b6e435ce39510ecfaadcb3329d387500bbf49d87ac3344ac11ffb17ffb257836c6c6007b5811
6
+ metadata.gz: 92aa3a8b950008ff7327a27f98a76ba8e519d5f7ea1e3120b4d5310dd27bc15a7b330b5893d2a766da3533acf95a1e3b65614e28e9564a22286d8b493988fa9f
7
+ data.tar.gz: 53660ec068a0a15f6065d67c15323612d13b5e91fc578a81554a8e8728c92888096cf817d2649fc15d4af6bb1045ed9f4bdf2232cb67fd8701a4578c37c34e0a
@@ -15,8 +15,8 @@ jobs:
15
15
  runs-on: macos-latest
16
16
  strategy:
17
17
  matrix:
18
- ruby: ['3.2.7', '3.3.8', '3.4.2', 'head', '3.5.0-preview1']
19
- duckdb: ['1.2.2', '1.3.1']
18
+ ruby: ['3.2.9', '3.3.10', '3.4.7', 'head', '3.5.0-preview1']
19
+ duckdb: ['1.4.1', '1.3.2']
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v4
@@ -15,8 +15,8 @@ jobs:
15
15
  runs-on: ubuntu-latest
16
16
  strategy:
17
17
  matrix:
18
- ruby: ['3.2.7', '3.3.8', '3.4.2', 'head', '3.5.0-preview1']
19
- duckdb: ['1.2.2', '1.3.1']
18
+ ruby: ['3.2.9', '3.3.10', '3.4.7', 'head', '3.5.0-preview1']
19
+ duckdb: ['1.4.1', '1.3.2']
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v4
@@ -15,8 +15,8 @@ jobs:
15
15
  runs-on: windows-latest
16
16
  strategy:
17
17
  matrix:
18
- ruby: ['3.2.6', '3.3.8', '3.4.1', 'ucrt', 'mingw', 'mswin', 'head']
19
- duckdb: ['1.2.2', '1.3.1']
18
+ ruby: ['3.2.9', '3.3.10', '3.4.7', 'ucrt', 'mingw', 'mswin', 'head']
19
+ duckdb: ['1.4.1', '1.3.2']
20
20
 
21
21
  steps:
22
22
  - uses: actions/checkout@v4
data/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ # Unreleased
6
+
7
+ # 1.4.1.0 - 2025-11-01
8
+ - add DuckDB::Connection#appender_from_query.
9
+ - add DuckDB::Appender.create_query.
10
+ - add `DuckDB::LogicalType.boolean`, `DuckDB::LogicalType.tinyint`, `DuckDB::LogicalType.smallint`,
11
+ `DuckDB::LogicalType.integer`, `DuckDB::LogicalType.bigint`, `DuckDB::LogicalType.utinyint`,
12
+ `DuckDB::LogicalType.usmallint`, `DuckDB::LogicalType.uinteger`, `DuckDB::LogicalType.ubigint`,
13
+ `DuckDB::LogicalType.float`, `DuckDB::LogicalType.double`, `DuckDB::LogicalType.timestamp`,
14
+ `DuckDB::LogicalType.date`, `DuckDB::LogicalType.time`, `DuckDB::LogicalType.interval`,
15
+ `DuckDB::LogicalType.hugeint`, `DuckDB::LogicalType.uhugeint`, `DuckDB::LogicalType.varchar`,
16
+ `DuckDB::LogicalType.blob`, `DuckDB::LogicalType.timestamp_s`,
17
+ `DuckDB::LogicalType.timestamp_ms`, `DuckDB::LogicalType.timestamp_ns`,
18
+ `DuckDB::LogicalType.bit`, `DuckDB::LogicalType.time_tz`, `DuckDB::LogicalType.timestamp_tz`.
19
+ - add `DuckDB::LogicalType::BOOLEAN`, `DuckDB::LogicalType::TINYINT`, `DuckDB::LogicalType::SMALLINT`,
20
+ `DuckDB::LogicalType::INTEGER`, `DuckDB::LogicalType::BIGINT`, `DuckDB::LogicalType::UTINYINT`,
21
+ `DuckDB::LogicalType::USMALLINT`, `DuckDB::LogicalType::UINTEGER`, `DuckDB::LogicalType::UBIGINT`,
22
+ `DuckDB::LogicalType::FLOAT`, `DuckDB::LogicalType::DOUBLE`, `DuckDB::LogicalType::TIMESTAMP`,
23
+ `DuckDB::LogicalType::DATE`, `DuckDB::LogicalType::TIME`, `DuckDB::LogicalType::INTERVAL`,
24
+ `DuckDB::LogicalType::HUGEINT`, `DuckDB::LogicalType::UHUGEINT`, `DuckDB::LogicalType::VARCHAR`,
25
+ `DuckDB::LogicalType::BLOB`, `DuckDB::LogicalType::TIMESTAMP_S`,
26
+ `DuckDB::LogicalType::TIMESTAMP_MS`, `DuckDB::LogicalType::TIMESTAMP_NS`,
27
+ `DuckDB::LogicalType::BIT`, `DuckDB::LogicalType::TIME_TZ`, `DuckDB::LogicalType::TIMESTAMP_TZ`.
28
+ - Support TIMESTAMP_NS infinity, -infinity value.
29
+ - bump duckdb to 1.3.2, 1.4.1 on CI.
30
+ - add `DuckDB::ValueImpl` class. This class is under construction. You must not use this class directly.
31
+ - add `DuckDB::ScalarFunction` class. This class is under construction.
32
+ - add `DuckDB::ScalarFunction#set_name`.
33
+ - add `DuckDB::LogicalType.new(type_id)` to create a logical type from a type ID.
34
+
5
35
  # 1.3.1.0 - 2025-06-28
6
36
  - Support TIMESTAMP_S, TIMESTAMP_MS infinity, -infinity value.
7
37
  - bump duckdb to 1.3.1 on CI.
data/Gemfile.lock CHANGED
@@ -1,31 +1,31 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- duckdb (1.3.1.0)
4
+ duckdb (1.4.1.0)
5
5
  bigdecimal (>= 3.1.4)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  benchmark-ips (2.14.0)
11
- bigdecimal (3.2.2)
11
+ bigdecimal (3.3.1)
12
12
  mini_portile2 (2.8.9)
13
- minitest (5.25.5)
14
- nokogiri (1.18.8)
13
+ minitest (5.26.0)
14
+ nokogiri (1.18.10)
15
15
  mini_portile2 (~> 2.8.2)
16
16
  racc (~> 1.4)
17
- nokogiri (1.18.8-aarch64-linux-gnu)
17
+ nokogiri (1.18.10-aarch64-linux-gnu)
18
18
  racc (~> 1.4)
19
- nokogiri (1.18.8-arm-linux-gnu)
19
+ nokogiri (1.18.10-arm-linux-gnu)
20
20
  racc (~> 1.4)
21
- nokogiri (1.18.8-arm64-darwin)
21
+ nokogiri (1.18.10-arm64-darwin)
22
22
  racc (~> 1.4)
23
- nokogiri (1.18.8-x86_64-darwin)
23
+ nokogiri (1.18.10-x86_64-darwin)
24
24
  racc (~> 1.4)
25
- nokogiri (1.18.8-x86_64-linux-gnu)
25
+ nokogiri (1.18.10-x86_64-linux-gnu)
26
26
  racc (~> 1.4)
27
27
  racc (1.8.1)
28
- rake (13.3.0)
28
+ rake (13.3.1)
29
29
  rake-compiler (1.3.0)
30
30
  rake
31
31
  ruby_memcheck (3.0.1)
data/duckdb.gemspec CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
  end
28
28
  spec.require_paths = ['lib']
29
29
  spec.extensions = ['ext/duckdb/extconf.rb']
30
- spec.required_ruby_version = '>= 3.1.0'
30
+ spec.required_ruby_version = '>= 3.2.0'
31
31
  spec.add_dependency 'bigdecimal', '>= 3.1.4'
32
32
 
33
33
  spec.add_development_dependency 'bundler', '~> 2.3'
@@ -5,6 +5,11 @@ static VALUE cDuckDBAppender;
5
5
  static void deallocate(void *);
6
6
  static VALUE allocate(VALUE klass);
7
7
  static size_t memsize(const void *p);
8
+
9
+ #ifdef HAVE_DUCKDB_H_GE_V1_4_0
10
+ static VALUE appender_s_create_query(VALUE klass, VALUE con, VALUE query, VALUE types, VALUE table, VALUE columns);
11
+ #endif
12
+
8
13
  static VALUE appender_initialize(VALUE klass, VALUE con, VALUE schema, VALUE table);
9
14
  static VALUE appender_error_message(VALUE self);
10
15
  static VALUE appender__append_bool(VALUE self, VALUE val);
@@ -56,6 +61,69 @@ static size_t memsize(const void *p) {
56
61
  return sizeof(rubyDuckDBAppender);
57
62
  }
58
63
 
64
+ #ifdef HAVE_DUCKDB_H_GE_V1_4_0
65
+ /* call-seq:
66
+ * DuckDB::Appender.create_query -> DuckDB::Appender
67
+ *
68
+ * Returns a new Appender instance created from a query.
69
+ *
70
+ * require 'duckdb'
71
+ * db = DuckDB::Database.open
72
+ * con = db.connect
73
+ * con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
74
+ * query = 'INSERT OR REPLACE INTO users SELECT i, val FROM my_appended_data'
75
+ * types = [DuckDB::LogicalType::INTEGER, DuckDB::LogicalType::VARCHAR]
76
+ * appender = DuckDB::Appender.create_query(con, query, types, 'my_appended_data', %w[i val])
77
+ */
78
+ static VALUE appender_s_create_query(VALUE klass, VALUE con, VALUE query, VALUE types, VALUE table, VALUE columns) {
79
+ rubyDuckDBConnection *ctxcon;
80
+ rubyDuckDBAppender *ctx;
81
+ char *query_str = StringValuePtr(query);
82
+ char *table_name = NULL;
83
+ const char **column_names = NULL;
84
+ idx_t column_count = 0;
85
+ duckdb_logical_type *type_array = NULL;
86
+ VALUE appender = Qnil;
87
+
88
+ if (!rb_obj_is_kind_of(con, cDuckDBConnection)) {
89
+ rb_raise(rb_eTypeError, "1st argument should be instance of DackDB::Connection");
90
+ }
91
+ if (rb_obj_is_kind_of(types, rb_cArray) == Qfalse) {
92
+ rb_raise(rb_eTypeError, "2nd argument should be an Array");
93
+ }
94
+ column_count = RARRAY_LEN(types);
95
+ type_array = ALLOCA_N(duckdb_logical_type, (size_t)column_count);
96
+ for (idx_t i = 0; i < column_count; i++) {
97
+ VALUE type_val = rb_ary_entry(types, i);
98
+ rubyDuckDBLogicalType *type_ctx = get_struct_logical_type(type_val);
99
+ type_array[i] = type_ctx->logical_type;
100
+ }
101
+
102
+ if (table != Qnil) {
103
+ table_name = StringValuePtr(table);
104
+ }
105
+ if (columns != Qnil) {
106
+ if (rb_obj_is_kind_of(columns, rb_cArray) == Qfalse) {
107
+ rb_raise(rb_eTypeError, "4th argument should be an Array or nil");
108
+ }
109
+ idx_t col_count = RARRAY_LEN(columns);
110
+ column_names = ALLOCA_N(const char *, (size_t)col_count);
111
+ for (idx_t i = 0; i < col_count; i++) {
112
+ VALUE col_name_val = rb_ary_entry(columns, i);
113
+ column_names[i] = StringValuePtr(col_name_val);
114
+ }
115
+ }
116
+ ctxcon = get_struct_connection(con);
117
+ appender = allocate(klass);
118
+ TypedData_Get_Struct(appender, rubyDuckDBAppender, &appender_data_type, ctx);
119
+ if (duckdb_appender_create_query(ctxcon->con, query_str, column_count, type_array, table_name, column_names, &ctx->appender) == DuckDBError) {
120
+ rb_raise(eDuckDBError, "failed to create appender from query");
121
+ }
122
+
123
+ return appender;
124
+ }
125
+ #endif
126
+
59
127
  static VALUE appender_initialize(VALUE self, VALUE con, VALUE schema, VALUE table) {
60
128
 
61
129
  rubyDuckDBConnection *ctxcon;
@@ -93,14 +161,27 @@ static VALUE appender_initialize(VALUE self, VALUE con, VALUE schema, VALUE tabl
93
161
  */
94
162
  static VALUE appender_error_message(VALUE self) {
95
163
  rubyDuckDBAppender *ctx;
96
- const char *msg;
164
+ #ifdef HAVE_DUCKDB_H_GE_V1_4_0
165
+ duckdb_error_data error_data;
166
+ #endif
167
+ const char *msg = NULL;
168
+ VALUE rb_msg = Qnil;
97
169
  TypedData_Get_Struct(self, rubyDuckDBAppender, &appender_data_type, ctx);
98
170
 
171
+ #ifdef HAVE_DUCKDB_H_GE_V1_4_0
172
+ error_data = duckdb_appender_error_data(ctx->appender);
173
+ if (duckdb_error_data_has_error(error_data)) {
174
+ msg = duckdb_error_data_message(error_data);
175
+ rb_msg = rb_str_new2(msg);
176
+ }
177
+ duckdb_destroy_error_data(&error_data);
178
+ #else
99
179
  msg = duckdb_appender_error(ctx->appender);
100
- if (msg == NULL) {
101
- return Qnil;
180
+ if (msg != NULL) {
181
+ rb_msg = rb_str_new2(msg);
102
182
  }
103
- return rb_str_new2(msg);
183
+ #endif
184
+ return rb_msg;
104
185
  }
105
186
 
106
187
  /* :nodoc: */
@@ -376,6 +457,9 @@ void rbduckdb_init_duckdb_appender(void) {
376
457
  #endif
377
458
  cDuckDBAppender = rb_define_class_under(mDuckDB, "Appender", rb_cObject);
378
459
  rb_define_alloc_func(cDuckDBAppender, allocate);
460
+ #ifdef HAVE_DUCKDB_H_GE_V1_4_0
461
+ rb_define_singleton_method(cDuckDBAppender, "create_query", appender_s_create_query, 5);
462
+ #endif
379
463
  rb_define_method(cDuckDBAppender, "initialize", appender_initialize, 3);
380
464
  rb_define_method(cDuckDBAppender, "error_message", appender_error_message, 0);
381
465
  rb_define_private_method(cDuckDBAppender, "_end_row", appender__end_row, 0);
@@ -6,6 +6,7 @@ static void close_database(rubyDuckDB *p);
6
6
  static void deallocate(void * ctx);
7
7
  static VALUE allocate(VALUE klass);
8
8
  static size_t memsize(const void *p);
9
+ static duckdb_config create_config_with_ruby_api(void);
9
10
  static VALUE duckdb_database_s_open(int argc, VALUE *argv, VALUE cDuckDBDatabase);
10
11
  static VALUE duckdb_database_s_open_ext(int argc, VALUE *argv, VALUE cDuckDBDatabase);
11
12
  static VALUE duckdb_database_connect(VALUE self);
@@ -43,10 +44,27 @@ rubyDuckDB *rbduckdb_get_struct_database(VALUE obj) {
43
44
  return ctx;
44
45
  }
45
46
 
47
+ static duckdb_config create_config_with_ruby_api(void) {
48
+ duckdb_config config;
49
+
50
+ if (duckdb_create_config(&config) == DuckDBError) {
51
+ rb_raise(eDuckDBError, "failed to create config");
52
+ }
53
+
54
+ if (duckdb_set_config(config, "duckdb_api", "ruby") == DuckDBError) {
55
+ duckdb_destroy_config(&config);
56
+ rb_raise(eDuckDBError, "failed to set duckdb_api config");
57
+ }
58
+
59
+ return config;
60
+ }
61
+
46
62
  /* :nodoc: */
47
63
  static VALUE duckdb_database_s_open(int argc, VALUE *argv, VALUE cDuckDBDatabase) {
48
64
  rubyDuckDB *ctx;
49
65
  VALUE obj;
66
+ duckdb_config config;
67
+ char *perror = NULL;
50
68
 
51
69
  char *pfile = NULL;
52
70
  VALUE file = Qnil;
@@ -59,9 +77,19 @@ static VALUE duckdb_database_s_open(int argc, VALUE *argv, VALUE cDuckDBDatabase
59
77
 
60
78
  obj = allocate(cDuckDBDatabase);
61
79
  TypedData_Get_Struct(obj, rubyDuckDB, &database_data_type, ctx);
62
- if (duckdb_open(pfile, &(ctx->db)) == DuckDBError) {
63
- rb_raise(eDuckDBError, "Failed to open database"); /* FIXME */
80
+
81
+ config = create_config_with_ruby_api();
82
+
83
+ if (duckdb_open_ext(pfile, &(ctx->db), config, &perror) == DuckDBError) {
84
+ VALUE error_msg = rb_str_new_cstr(perror ? perror : "Unknown error");
85
+ if (perror) {
86
+ duckdb_free(perror);
87
+ }
88
+ duckdb_destroy_config(&config);
89
+ rb_raise(eDuckDBError, "failed to open database: %s", StringValueCStr(error_msg));
64
90
  }
91
+
92
+ duckdb_destroy_config(&config);
65
93
  return obj;
66
94
  }
67
95
 
@@ -70,7 +98,9 @@ static VALUE duckdb_database_s_open_ext(int argc, VALUE *argv, VALUE cDuckDBData
70
98
  rubyDuckDB *ctx;
71
99
  VALUE obj;
72
100
  rubyDuckDBConfig *ctx_config;
73
- char *perror;
101
+ duckdb_config config_to_use;
102
+ char *perror = NULL;
103
+ int need_destroy_config = 0;
74
104
 
75
105
  char *pfile = NULL;
76
106
  VALUE file = Qnil;
@@ -84,19 +114,37 @@ static VALUE duckdb_database_s_open_ext(int argc, VALUE *argv, VALUE cDuckDBData
84
114
 
85
115
  obj = allocate(cDuckDBDatabase);
86
116
  TypedData_Get_Struct(obj, rubyDuckDB, &database_data_type, ctx);
117
+
87
118
  if (!NIL_P(config)) {
88
119
  if (!rb_obj_is_kind_of(config, cDuckDBConfig)) {
89
120
  rb_raise(rb_eTypeError, "The second argument must be DuckDB::Config object.");
90
121
  }
91
122
  ctx_config = get_struct_config(config);
92
- if (duckdb_open_ext(pfile, &(ctx->db), ctx_config->config, &perror) == DuckDBError) {
93
- rb_raise(eDuckDBError, "Failed to open database %s", perror);
123
+ /* Set duckdb_api to "ruby" for the provided config */
124
+ if (duckdb_set_config(ctx_config->config, "duckdb_api", "ruby") == DuckDBError) {
125
+ rb_raise(eDuckDBError, "failed to set duckdb_api config");
94
126
  }
127
+ config_to_use = ctx_config->config;
95
128
  } else {
96
- if (duckdb_open(pfile, &(ctx->db)) == DuckDBError) {
97
- rb_raise(eDuckDBError, "Failed to open database"); /* FIXME */
129
+ config_to_use = create_config_with_ruby_api();
130
+ need_destroy_config = 1;
131
+ }
132
+
133
+ if (duckdb_open_ext(pfile, &(ctx->db), config_to_use, &perror) == DuckDBError) {
134
+ VALUE error_msg = rb_str_new_cstr(perror ? perror : "Unknown error");
135
+ if (perror) {
136
+ duckdb_free(perror);
98
137
  }
138
+ if (need_destroy_config) {
139
+ duckdb_destroy_config(&config_to_use);
140
+ }
141
+ rb_raise(eDuckDBError, "failed to open database: %s", StringValueCStr(error_msg));
142
+ }
143
+
144
+ if (need_destroy_config) {
145
+ duckdb_destroy_config(&config_to_use);
99
146
  }
147
+
100
148
  return obj;
101
149
  }
102
150
 
data/ext/duckdb/duckdb.c CHANGED
@@ -40,4 +40,6 @@ Init_duckdb_native(void) {
40
40
  rbduckdb_init_duckdb_converter();
41
41
  rbduckdb_init_duckdb_extracted_statements();
42
42
  rbduckdb_init_duckdb_instance_cache();
43
+ rbduckdb_init_duckdb_value_impl();
44
+ rbduckdb_init_duckdb_scalar_function();
43
45
  }
@@ -64,6 +64,9 @@ have_func('duckdb_create_instance_cache', 'duckdb.h')
64
64
  # check duckdb >= 1.3.0
65
65
  have_func('duckdb_get_table_names', 'duckdb.h')
66
66
 
67
+ # check duckdb >= 1.4.0
68
+ have_func('duckdb_appender_create_query', 'duckdb.h')
69
+
67
70
  $CFLAGS << ' -DDUCKDB_API_NO_DEPRECATED' if ENV['DUCKDB_API_NO_DEPRECATED']
68
71
 
69
72
  create_makefile('duckdb/duckdb_native')
@@ -23,6 +23,7 @@ static VALUE duckdb_logical_type_dictionary_size(VALUE self);
23
23
  static VALUE duckdb_logical_type_dictionary_value_at(VALUE self, VALUE didx);
24
24
  static VALUE duckdb_logical_type__get_alias(VALUE self);
25
25
  static VALUE duckdb_logical_type__set_alias(VALUE self, VALUE aname);
26
+ static VALUE initialize(VALUE self, VALUE type_id_arg);
26
27
 
27
28
  static const rb_data_type_t logical_type_data_type = {
28
29
  "DuckDB/LogicalType",
@@ -40,6 +41,12 @@ static void deallocate(void *ctx) {
40
41
  xfree(p);
41
42
  }
42
43
 
44
+ rubyDuckDBLogicalType *get_struct_logical_type(VALUE obj) {
45
+ rubyDuckDBLogicalType *ctx;
46
+ TypedData_Get_Struct(obj, rubyDuckDBLogicalType, &logical_type_data_type, ctx);
47
+ return ctx;
48
+ }
49
+
43
50
  static VALUE allocate(VALUE klass) {
44
51
  rubyDuckDBLogicalType *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBLogicalType));
45
52
  return TypedData_Wrap_Struct(klass, &logical_type_data_type, ctx);
@@ -49,6 +56,31 @@ static size_t memsize(const void *p) {
49
56
  return sizeof(rubyDuckDBLogicalType);
50
57
  }
51
58
 
59
+ static VALUE initialize(VALUE self, VALUE type_id_arg) {
60
+ rubyDuckDBLogicalType *ctx;
61
+ duckdb_type type = (duckdb_type)NUM2INT(type_id_arg);
62
+ duckdb_logical_type new_logical_type;
63
+
64
+ TypedData_Get_Struct(self, rubyDuckDBLogicalType, &logical_type_data_type, ctx);
65
+
66
+ if (ctx->logical_type) {
67
+ duckdb_destroy_logical_type(&(ctx->logical_type));
68
+ }
69
+
70
+ new_logical_type = duckdb_create_logical_type(type);
71
+
72
+ if (!new_logical_type || duckdb_get_type_id(new_logical_type) == DUCKDB_TYPE_INVALID) {
73
+ if (new_logical_type) {
74
+ duckdb_destroy_logical_type(&new_logical_type);
75
+ }
76
+ rb_raise(rb_eArgError, "Invalid or unsupported logical type ID: %d", type);
77
+ }
78
+
79
+ ctx->logical_type = new_logical_type;
80
+
81
+ return self;
82
+ }
83
+
52
84
  /*
53
85
  * call-seq:
54
86
  * decimal_col.logical_type.type -> Symbol
@@ -439,4 +471,6 @@ void rbduckdb_init_duckdb_logical_type(void) {
439
471
  rb_define_method(cDuckDBLogicalType, "dictionary_value_at", duckdb_logical_type_dictionary_value_at, 1);
440
472
  rb_define_method(cDuckDBLogicalType, "get_alias", duckdb_logical_type__get_alias, 0);
441
473
  rb_define_method(cDuckDBLogicalType, "set_alias", duckdb_logical_type__set_alias, 1);
474
+
475
+ rb_define_method(cDuckDBLogicalType, "initialize", initialize, 1);
442
476
  }
@@ -9,5 +9,5 @@ typedef struct _rubyDuckDBLogicalType rubyDuckDBLogicalType;
9
9
 
10
10
  void rbduckdb_init_duckdb_logical_type(void);
11
11
  VALUE rbduckdb_create_logical_type(duckdb_logical_type logical_type);
12
-
12
+ rubyDuckDBLogicalType *get_struct_logical_type(VALUE obj);
13
13
  #endif
data/ext/duckdb/result.c CHANGED
@@ -50,6 +50,7 @@ static VALUE vector_decimal(duckdb_logical_type ty, void* vector_data, idx_t row
50
50
  static VALUE infinite_timestamp_value(duckdb_timestamp timestamp);
51
51
  static VALUE infinite_timestamp_s_value(duckdb_timestamp_s timestamp_s);
52
52
  static VALUE infinite_timestamp_ms_value(duckdb_timestamp_ms timestamp_ms);
53
+ static VALUE infinite_timestamp_ns_value(duckdb_timestamp_ns timestamp_ns);
53
54
 
54
55
  static VALUE vector_timestamp_s(void* vector_data, idx_t row_idx);
55
56
  static VALUE vector_timestamp_ms(void* vector_data, idx_t row_idx);
@@ -474,10 +475,23 @@ static VALUE vector_timestamp_ms(void* vector_data, idx_t row_idx) {
474
475
  );
475
476
  }
476
477
 
478
+ static VALUE infinite_timestamp_ns_value(duckdb_timestamp_ns timestamp_ns) {
479
+ if (duckdb_is_finite_timestamp_ns(timestamp_ns) == false) {
480
+ return rb_funcall(mDuckDBConverter, id__to_infinity, 1,
481
+ LL2NUM(timestamp_ns.nanos)
482
+ );
483
+ }
484
+ return Qnil;
485
+ }
486
+
477
487
  static VALUE vector_timestamp_ns(void* vector_data, idx_t row_idx) {
478
- duckdb_timestamp data = ((duckdb_timestamp *)vector_data)[row_idx];
488
+ duckdb_timestamp_ns data = ((duckdb_timestamp_ns *)vector_data)[row_idx];
489
+ VALUE obj = infinite_timestamp_ns_value(data);
490
+ if (obj != Qnil) {
491
+ return obj;
492
+ }
479
493
  return rb_funcall(mDuckDBConverter, id__to_time_from_duckdb_timestamp_ns, 1,
480
- LL2NUM(data.micros)
494
+ LL2NUM(data.nanos)
481
495
  );
482
496
  }
483
497
 
@@ -12,6 +12,10 @@
12
12
  #define HAVE_DUCKDB_H_GE_V1_3_0 1
13
13
  #endif
14
14
 
15
+ #ifdef HAVE_DUCKDB_APPENDER_CREATE_QUERY
16
+ #define HAVE_DUCKDB_H_GE_V1_4_0 1
17
+ #endif
18
+
15
19
  #include "./error.h"
16
20
  #include "./database.h"
17
21
  #include "./connection.h"
@@ -28,6 +32,8 @@
28
32
  #include "./appender.h"
29
33
  #include "./config.h"
30
34
  #include "./instance_cache.h"
35
+ #include "./value_impl.h"
36
+ #include "./scalar_function.h"
31
37
 
32
38
  extern VALUE mDuckDB;
33
39
  extern VALUE cDuckDBDatabase;
@@ -40,5 +46,7 @@ extern VALUE cDuckDBPreparedStatement;
40
46
  extern VALUE PositiveInfinity;
41
47
  extern VALUE NegativeInfinity;
42
48
  extern VALUE cDuckDBInstanceCache;
49
+ extern VALUE cDuckDBValueImpl;
50
+ extern VALUE cDuckDBScalarFunction;
43
51
 
44
52
  #endif
@@ -0,0 +1,57 @@
1
+ #include "ruby-duckdb.h"
2
+
3
+ VALUE cDuckDBScalarFunction;
4
+
5
+ static void deallocate(void *);
6
+ static VALUE allocate(VALUE klass);
7
+ static size_t memsize(const void *p);
8
+ static VALUE duckdb_scalar_function_initialize(VALUE self);
9
+ static VALUE rbduckdb_scalar_function_set_name(VALUE self, VALUE name);
10
+
11
+ static const rb_data_type_t scalar_function_data_type = {
12
+ "DuckDB/ScalarFunction",
13
+ {NULL, deallocate, memsize,},
14
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
15
+ };
16
+
17
+ static void deallocate(void * ctx) {
18
+ rubyDuckDBScalarFunction *p = (rubyDuckDBScalarFunction *)ctx;
19
+ duckdb_destroy_scalar_function(&(p->scalar_function));
20
+ xfree(p);
21
+ }
22
+
23
+ static VALUE allocate(VALUE klass) {
24
+ rubyDuckDBScalarFunction *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBScalarFunction));
25
+ return TypedData_Wrap_Struct(klass, &scalar_function_data_type, ctx);
26
+ }
27
+
28
+ static size_t memsize(const void *p) {
29
+ return sizeof(rubyDuckDBScalarFunction);
30
+ }
31
+
32
+ static VALUE duckdb_scalar_function_initialize(VALUE self) {
33
+ rubyDuckDBScalarFunction *p;
34
+ TypedData_Get_Struct(self, rubyDuckDBScalarFunction, &scalar_function_data_type, p);
35
+ p->scalar_function = duckdb_create_scalar_function();
36
+ return self;
37
+ }
38
+
39
+ static VALUE rbduckdb_scalar_function_set_name(VALUE self, VALUE name) {
40
+ rubyDuckDBScalarFunction *p;
41
+ TypedData_Get_Struct(self, rubyDuckDBScalarFunction, &scalar_function_data_type, p);
42
+
43
+ const char *str = StringValuePtr(name);
44
+ duckdb_scalar_function_set_name(p->scalar_function, str);
45
+
46
+ return self;
47
+ }
48
+
49
+ void rbduckdb_init_duckdb_scalar_function(void) {
50
+ #if 0
51
+ VALUE mDuckDB = rb_define_module("DuckDB");
52
+ #endif
53
+ cDuckDBScalarFunction = rb_define_class_under(mDuckDB, "ScalarFunction", rb_cObject);
54
+ rb_define_alloc_func(cDuckDBScalarFunction, allocate);
55
+ rb_define_method(cDuckDBScalarFunction, "initialize", duckdb_scalar_function_initialize, 0);
56
+ rb_define_method(cDuckDBScalarFunction, "set_name", rbduckdb_scalar_function_set_name, 1);
57
+ }
@@ -0,0 +1,14 @@
1
+ #ifndef RUBY_DUCKDB_SCALAR_FUNCTION_H
2
+ #define RUBY_DUCKDB_SCALAR_FUNCTION_H
3
+
4
+ struct _rubyDuckDBScalarFunction {
5
+ duckdb_scalar_function scalar_function;
6
+ };
7
+
8
+ typedef struct _rubyDuckDBScalarFunction rubyDuckDBScalarFunction;
9
+
10
+ void rbduckdb_init_duckdb_scalar_function(void);
11
+
12
+ #endif
13
+
14
+
@@ -0,0 +1,38 @@
1
+ #include "ruby-duckdb.h"
2
+
3
+ VALUE cDuckDBValueImpl;
4
+
5
+ static void deallocate(void *);
6
+ static VALUE allocate(VALUE klass);
7
+ static size_t memsize(const void *p);
8
+
9
+ static const rb_data_type_t value_impl_data_type = {
10
+ "DuckDB/ValueImpl",
11
+ {NULL, deallocate, memsize,},
12
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
13
+ };
14
+
15
+ static void deallocate(void * ctx) {
16
+ rubyDuckDBValueImpl *p = (rubyDuckDBValueImpl *)ctx;
17
+
18
+ duckdb_destroy_value(&(p->value));
19
+ xfree(p);
20
+ }
21
+
22
+ static VALUE allocate(VALUE klass) {
23
+ rubyDuckDBValueImpl *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBValueImpl));
24
+ return TypedData_Wrap_Struct(klass, &value_impl_data_type, ctx);
25
+ }
26
+
27
+ static size_t memsize(const void *p) {
28
+ return sizeof(rubyDuckDBValueImpl);
29
+ }
30
+
31
+ void rbduckdb_init_duckdb_value_impl(void) {
32
+ #if 0
33
+ VALUE mDuckDB = rb_define_module("DuckDB");
34
+ #endif
35
+ cDuckDBValueImpl = rb_define_class_under(mDuckDB, "ValueImpl", rb_cObject);
36
+ rb_define_alloc_func(cDuckDBValueImpl, allocate);
37
+ }
38
+
@@ -0,0 +1,13 @@
1
+ #ifndef RUBY_DUCKDB_VALUE_IMPL_H
2
+ #define RUBY_DUCKDB_VALUE_IMPL_H
3
+
4
+ struct _rubyDuckDBValueImpl {
5
+ duckdb_value value;
6
+ };
7
+
8
+ typedef struct _rubyDuckDBValueImpl rubyDuckDBValueImpl;
9
+
10
+ void rbduckdb_init_duckdb_value_impl(void);
11
+
12
+ #endif
13
+
data/getduckdb.sh CHANGED
@@ -3,8 +3,16 @@
3
3
  MACHINE=`uname -m`
4
4
 
5
5
  case "$MACHINE" in
6
- "x86_64" ) ARC=amd64 ;;
7
- "aarch64" ) ARC=aarch64 ;;
6
+ "x86_64")
7
+ ARCH=amd64
8
+ ;;
9
+ "aarch64")
10
+ if printf '%s\n' '1.3.0' "$DUCKDB_VERSION" | sort -C -V; then
11
+ ARCH=arm64
12
+ else
13
+ ARCH=aarch64
14
+ fi
15
+ ;;
8
16
  esac
9
17
 
10
- wget -O duckdb.zip "https://github.com/duckdb/duckdb/releases/download/v$DUCKDB_VERSION/libduckdb-linux-$ARC.zip"
18
+ wget -O duckdb.zip "https://github.com/duckdb/duckdb/releases/download/v$DUCKDB_VERSION/libduckdb-linux-$ARCH.zip"
@@ -23,6 +23,10 @@ module DuckDB
23
23
  private_constant :RANGE_INT16, :RANGE_INT32, :RANGE_INT64
24
24
  # :startdoc:
25
25
 
26
+ class << self
27
+ alias from_query create_query if DuckDB::Appender.respond_to?(:create_query)
28
+ end
29
+
26
30
  # :call-seq:
27
31
  # appender.begin_row -> self
28
32
  # A nop method, provided for backwards compatibility reasons.
@@ -136,6 +136,28 @@ module DuckDB
136
136
  appender.close
137
137
  end
138
138
 
139
+ if Appender.respond_to?(:create_query)
140
+ # :call-seq:
141
+ # connection.appender_from_query(query, types, table_name = nil, column_names = nil) -> DuckDB::Appender
142
+ #
143
+ # Creates an appender object that executes the given query with any data appended to it.
144
+ # The `table_name` parameter is used to refer to the appended data in the query. If omitted, it defaults to "appended_data".
145
+ # The `column_names` parameter provides names for the columns of the appended data. If omitted, it defaults to "col1", "col2", etc.
146
+ #
147
+ # require 'duckdb'
148
+ # db = DuckDB::Database.open
149
+ # con = db.connect
150
+ # con.query('CREATE TABLE t (i INT PRIMARY KEY, value VARCHAR)')
151
+ # query = 'INSERT OR REPLACE INTO t SELECT i, val FROM my_appended_data'
152
+ # types = [DuckDB::LogicalType::INTEGER, DuckDB::LogicalType::VARCHAR]
153
+ # appender = con.appender_from_query(query, types, 'my_appended_data', %w[i val])
154
+ # appender.append_row(1, 'hello world')
155
+ # appender.close
156
+ def appender_from_query(query, types, table_name = nil, column_names = nil)
157
+ Appender.create_query(self, query, types, table_name, column_names)
158
+ end
159
+ end
160
+
139
161
  private
140
162
 
141
163
  def create_appender(table)
@@ -68,7 +68,11 @@ module DuckDB
68
68
  28 => :union,
69
69
  29 => :bit,
70
70
  30 => :time_tz,
71
- 31 => :timestamp_tz
71
+ 31 => :timestamp_tz,
72
+ 35 => :bignum,
73
+ 36 => :sqlnull,
74
+ 37 => :string_literal,
75
+ 38 => :integer_literal
72
76
  }.freeze
73
77
 
74
78
  module_function
@@ -5,6 +5,55 @@ module DuckDB
5
5
  alias :alias get_alias
6
6
  alias :alias= set_alias
7
7
 
8
+ @logical_types = {}
9
+
10
+ {
11
+ boolean: 1,
12
+ tinyint: 2,
13
+ smallint: 3,
14
+ integer: 4,
15
+ bigint: 5,
16
+ utinyint: 6,
17
+ usmallint: 7,
18
+ uinteger: 8,
19
+ ubigint: 9,
20
+ float: 10,
21
+ double: 11,
22
+ timestamp: 12,
23
+ date: 13,
24
+ time: 14,
25
+ interval: 15,
26
+ hugeint: 16,
27
+ uhugeint: 32,
28
+ varchar: 17,
29
+ blob: 18,
30
+ # decimal: 19,
31
+ timestamp_s: 20,
32
+ timestamp_ms: 21,
33
+ timestamp_ns: 22,
34
+ # enum: 23,
35
+ # list: 24,
36
+ # struct: 25,
37
+ # map: 26,
38
+ # array: 33,
39
+ # uuid: 27,
40
+ # union: 28,
41
+ bit: 29,
42
+ time_tz: 30,
43
+ timestamp_tz: 31,
44
+ any: 34,
45
+ bignum: 35,
46
+ sqlnull: 36,
47
+ string_literal: 37,
48
+ integer_literal: 38
49
+ # time_ns: 39
50
+ }.each do |method_name, type_id|
51
+ define_singleton_method(method_name) do
52
+ @logical_types[type_id] ||= DuckDB::LogicalType.new(type_id)
53
+ end
54
+ const_set(method_name.upcase, send(method_name))
55
+ end
56
+
8
57
  # returns logical type's type symbol
9
58
  # `:unknown` means that the logical type's type is unknown/unsupported by ruby-duckdb.
10
59
  # `:invalid` means that the logical type's type is invalid in duckdb.
@@ -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 = '1.3.1.0'
6
+ VERSION = '1.4.1.0'
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duckdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1.0
4
+ version: 1.4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masaki Suketa
@@ -143,8 +143,12 @@ files:
143
143
  - ext/duckdb/result.c
144
144
  - ext/duckdb/result.h
145
145
  - ext/duckdb/ruby-duckdb.h
146
+ - ext/duckdb/scalar_function.c
147
+ - ext/duckdb/scalar_function.h
146
148
  - ext/duckdb/util.c
147
149
  - ext/duckdb/util.h
150
+ - ext/duckdb/value_impl.c
151
+ - ext/duckdb/value_impl.h
148
152
  - getduckdb.sh
149
153
  - lib/duckdb.rb
150
154
  - lib/duckdb/appender.rb
@@ -181,14 +185,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
181
185
  requirements:
182
186
  - - ">="
183
187
  - !ruby/object:Gem::Version
184
- version: 3.1.0
188
+ version: 3.2.0
185
189
  required_rubygems_version: !ruby/object:Gem::Requirement
186
190
  requirements:
187
191
  - - ">="
188
192
  - !ruby/object:Gem::Version
189
193
  version: '0'
190
194
  requirements: []
191
- rubygems_version: 3.6.7
195
+ rubygems_version: 3.6.9
192
196
  specification_version: 4
193
197
  summary: This module is Ruby binding for DuckDB database engine.
194
198
  test_files: []