duckdb 0.0.12 → 0.2.8.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.
@@ -0,0 +1,17 @@
1
+ #ifndef RUBY_DUCKDB_APPENDER_H
2
+ #define RUBY_DUCKDB_APPENDER_H
3
+
4
+ #ifdef HAVE_DUCKDB_APPENDER_CREATE
5
+
6
+ struct _rubyDuckDBAppender {
7
+ duckdb_appender appender;
8
+ };
9
+
10
+ typedef struct _rubyDuckDBAppender rubyDuckDBAppender;
11
+
12
+ void init_duckdb_appender(void);
13
+
14
+ #endif
15
+
16
+ #endif
17
+
@@ -0,0 +1,80 @@
1
+ #include "ruby-duckdb.h"
2
+
3
+ #ifdef HAVE_DUCKDB_CREATE_CONFIG
4
+
5
+ VALUE cDuckDBConfig;
6
+
7
+ static void deallocate(void *);
8
+ static VALUE allocate(VALUE klass);
9
+ static VALUE config_s_size(VALUE klass);
10
+ static VALUE config_s_get_config_flag(VALUE self, VALUE value);
11
+ static VALUE config_initialize(VALUE self);
12
+ static VALUE config_set_config(VALUE self, VALUE key, VALUE value);
13
+
14
+ static void deallocate(void * ctx)
15
+ {
16
+ rubyDuckDBConfig *p = (rubyDuckDBConfig *)ctx;
17
+
18
+ duckdb_destroy_config(&(p->config));
19
+ xfree(p);
20
+ }
21
+
22
+ static VALUE allocate(VALUE klass)
23
+ {
24
+ rubyDuckDBConfig *ctx = xcalloc((size_t)1, sizeof(rubyDuckDBConfig));
25
+ return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
26
+ }
27
+
28
+ static VALUE config_initialize(VALUE self) {
29
+ rubyDuckDBConfig *ctx;
30
+
31
+ Data_Get_Struct(self, rubyDuckDBConfig, ctx);
32
+
33
+ if (duckdb_create_config(&(ctx->config)) == DuckDBError) {
34
+ rb_raise(eDuckDBError, "failed to create config");
35
+ }
36
+ return self;
37
+ }
38
+
39
+ static VALUE config_s_size(VALUE self) {
40
+ return INT2NUM(duckdb_config_count());
41
+ }
42
+
43
+ static VALUE config_s_get_config_flag(VALUE klass, VALUE value) {
44
+ const char *pkey;
45
+ const char *pdesc;
46
+
47
+ size_t i = NUM2INT(value);
48
+
49
+ if (duckdb_get_config_flag(i, &pkey, &pdesc) == DuckDBError) {
50
+ rb_raise(eDuckDBError, "failed to get config information of index %ld", i);
51
+ }
52
+
53
+ return rb_ary_new3(2, rb_str_new2(pkey), rb_str_new2(pdesc));
54
+ }
55
+
56
+ static VALUE config_set_config(VALUE self, VALUE key, VALUE value) {
57
+ const char *pkey = StringValuePtr(key);
58
+ const char *pval = StringValuePtr(value);
59
+
60
+ rubyDuckDBConfig *ctx;
61
+ Data_Get_Struct(self, rubyDuckDBConfig, ctx);
62
+
63
+ if (duckdb_set_config(ctx->config, pkey, pval) == DuckDBError) {
64
+ rb_raise(eDuckDBError, "failed to set config %s => %s", pkey, pval);
65
+ }
66
+ return self;
67
+ }
68
+
69
+ void init_duckdb_config(void) {
70
+ cDuckDBConfig = rb_define_class_under(mDuckDB, "Config", rb_cObject);
71
+ rb_define_alloc_func(cDuckDBConfig, allocate);
72
+ rb_define_singleton_method(cDuckDBConfig, "size", config_s_size, 0);
73
+ rb_define_singleton_method(cDuckDBConfig, "get_config_flag", config_s_get_config_flag, 1);
74
+
75
+ rb_define_method(cDuckDBConfig, "initialize", config_initialize, 0);
76
+ rb_define_method(cDuckDBConfig, "set_config", config_set_config, 2);
77
+ }
78
+
79
+ #endif
80
+
@@ -0,0 +1,18 @@
1
+ #ifndef RUBY_DUCKDB_CONFIG_H
2
+ #define RUBY_DUCKDB_CONFIG_H
3
+
4
+ #ifdef HAVE_DUCKDB_CREATE_CONFIG
5
+
6
+ struct _rubyDuckDBConfig {
7
+ duckdb_config config;
8
+ };
9
+
10
+ typedef struct _rubyDuckDBConfig rubyDuckDBConfig;
11
+
12
+ void init_duckdb_config(void);
13
+
14
+ #endif
15
+
16
+ #endif
17
+
18
+
@@ -2,6 +2,12 @@
2
2
 
3
3
  VALUE cDuckDBDatabase;
4
4
 
5
+ static void close_database(rubyDuckDB *p);
6
+ static void deallocate(void * ctx);
7
+ static VALUE allocate(VALUE klass);
8
+ static VALUE duckdb_database_s_open(int argc, VALUE *argv, VALUE cDuckDBDatabase);
9
+ static VALUE duckdb_database_s_open_ext(int argc, VALUE *argv, VALUE cDuckDBDatabase);
10
+
5
11
  static void close_database(rubyDuckDB *p)
6
12
  {
7
13
  duckdb_close(&(p->db));
@@ -25,6 +31,7 @@ static VALUE duckdb_database_s_open(int argc, VALUE *argv, VALUE cDuckDBDatabase
25
31
  {
26
32
  rubyDuckDB *ctx;
27
33
  VALUE obj;
34
+
28
35
  char *pfile = NULL;
29
36
  VALUE file = Qnil;
30
37
 
@@ -36,13 +43,49 @@ static VALUE duckdb_database_s_open(int argc, VALUE *argv, VALUE cDuckDBDatabase
36
43
 
37
44
  obj = allocate(cDuckDBDatabase);
38
45
  Data_Get_Struct(obj, rubyDuckDB, ctx);
39
- if (duckdb_open(pfile, &(ctx->db)) == DuckDBError)
40
- {
46
+ if (duckdb_open(pfile, &(ctx->db)) == DuckDBError) {
41
47
  rb_raise(eDuckDBError, "Failed to open database"); /* FIXME */
42
48
  }
43
49
  return obj;
44
50
  }
45
51
 
52
+ #ifdef HAVE_DUCKDB_OPEN_EXT
53
+ static VALUE duckdb_database_s_open_ext(int argc, VALUE *argv, VALUE cDuckDBDatabase)
54
+ {
55
+ rubyDuckDB *ctx;
56
+ VALUE obj;
57
+ rubyDuckDBConfig *ctx_config;
58
+ char *perror;
59
+
60
+ char *pfile = NULL;
61
+ VALUE file = Qnil;
62
+ VALUE config = Qnil;
63
+
64
+ rb_scan_args(argc, argv, "02", &file, &config);
65
+
66
+ if (!NIL_P(file)) {
67
+ pfile = StringValuePtr(file);
68
+ }
69
+
70
+ obj = allocate(cDuckDBDatabase);
71
+ Data_Get_Struct(obj, rubyDuckDB, ctx);
72
+ if (!NIL_P(config)) {
73
+ if (!rb_obj_is_kind_of(config, cDuckDBConfig)) {
74
+ rb_raise(rb_eTypeError, "The second argument must be DuckDB::Config object.");
75
+ }
76
+ Data_Get_Struct(config, rubyDuckDBConfig, ctx_config);
77
+ if (duckdb_open_ext(pfile, &(ctx->db), ctx_config->config, &perror) == DuckDBError) {
78
+ rb_raise(eDuckDBError, "Failed to open database %s", perror);
79
+ }
80
+ } else {
81
+ if (duckdb_open(pfile, &(ctx->db)) == DuckDBError) {
82
+ rb_raise(eDuckDBError, "Failed to open database"); /* FIXME */
83
+ }
84
+ }
85
+ return obj;
86
+ }
87
+ #endif /* HAVE_DUCKDB_OPEN_EXT */
88
+
46
89
  static VALUE duckdb_database_connect(VALUE self)
47
90
  {
48
91
  return create_connection(self);
@@ -67,6 +110,9 @@ void init_duckdb_database(void)
67
110
  cDuckDBDatabase = rb_define_class_under(mDuckDB, "Database", rb_cObject);
68
111
  rb_define_alloc_func(cDuckDBDatabase, allocate);
69
112
  rb_define_singleton_method(cDuckDBDatabase, "_open", duckdb_database_s_open, -1);
113
+ #ifdef HAVE_DUCKDB_OPEN_EXT
114
+ rb_define_singleton_method(cDuckDBDatabase, "_open_ext", duckdb_database_s_open_ext, -1);
115
+ #endif
70
116
  rb_define_private_method(cDuckDBDatabase, "_connect", duckdb_database_connect, 0);
71
117
  rb_define_method(cDuckDBDatabase, "close", duckdb_database_close, 0);
72
118
  }
data/ext/duckdb/duckdb.c CHANGED
@@ -18,4 +18,16 @@ Init_duckdb_native(void)
18
18
  init_duckdb_blob();
19
19
 
20
20
  #endif /* HAVE_DUCKDB_VALUE_BLOB */
21
+
22
+ #ifdef HAVE_DUCKDB_APPENDER_CREATE
23
+
24
+ init_duckdb_appender();
25
+
26
+ #endif /* HAVE_DUCKDB_APPENDER_CREATE */
27
+
28
+ #ifdef HAVE_DUCKDB_CREATE_CONFIG
29
+
30
+ init_duckdb_config();
31
+
32
+ #endif /* HAVE_DUCKDB_CREATE_CONFIG */
21
33
  }
@@ -3,5 +3,10 @@ require 'mkmf'
3
3
  dir_config('duckdb')
4
4
  if have_library('duckdb')
5
5
  have_func('duckdb_value_blob', 'duckdb.h')
6
+ have_func('duckdb_bind_blob', 'duckdb.h')
7
+ have_func('duckdb_appender_create', 'duckdb.h')
8
+ have_func('duckdb_free', 'duckdb.h')
9
+ have_func('duckdb_create_config', 'duckdb.h')
10
+ have_func('duckdb_open_ext', 'duckdb.h')
6
11
  create_makefile('duckdb/duckdb_native')
7
12
  end
@@ -70,7 +70,7 @@ static idx_t check_index(VALUE vidx)
70
70
  return idx;
71
71
  }
72
72
 
73
- static VALUE duckdb_prepared_statement_bind_boolean(VALUE self, VALUE vidx, VALUE val)
73
+ static VALUE duckdb_prepared_statement_bind_bool(VALUE self, VALUE vidx, VALUE val)
74
74
  {
75
75
  rubyDuckDBPreparedStatement *ctx;
76
76
  idx_t idx = check_index(vidx);
@@ -86,6 +86,20 @@ static VALUE duckdb_prepared_statement_bind_boolean(VALUE self, VALUE vidx, VALU
86
86
  return self;
87
87
  }
88
88
 
89
+ static VALUE duckdb_prepared_statement_bind_int8(VALUE self, VALUE vidx, VALUE val)
90
+ {
91
+ rubyDuckDBPreparedStatement *ctx;
92
+ idx_t idx = check_index(vidx);
93
+ int8_t i8val = (int8_t)NUM2INT(val);
94
+
95
+ Data_Get_Struct(self, rubyDuckDBPreparedStatement, ctx);
96
+
97
+ if (duckdb_bind_int8(ctx->prepared_statement, idx, i8val) == DuckDBError) {
98
+ rb_raise(eDuckDBError, "fail to bind %llu parameter", (unsigned long long)idx);
99
+ }
100
+ return self;
101
+ }
102
+
89
103
  static VALUE duckdb_prepared_statement_bind_int16(VALUE self, VALUE vidx, VALUE val)
90
104
  {
91
105
  rubyDuckDBPreparedStatement *ctx;
@@ -203,7 +217,8 @@ void init_duckdb_prepared_statement(void)
203
217
  rb_define_method(cDuckDBPreparedStatement, "initialize", duckdb_prepared_statement_initialize, 2);
204
218
  rb_define_method(cDuckDBPreparedStatement, "execute", duckdb_prepared_statement_execute, 0);
205
219
  rb_define_method(cDuckDBPreparedStatement, "nparams", duckdb_prepared_statement_nparams, 0);
206
- rb_define_method(cDuckDBPreparedStatement, "bind_boolean", duckdb_prepared_statement_bind_boolean, 2);
220
+ rb_define_method(cDuckDBPreparedStatement, "bind_bool", duckdb_prepared_statement_bind_bool, 2);
221
+ rb_define_method(cDuckDBPreparedStatement, "bind_int8", duckdb_prepared_statement_bind_int8, 2);
207
222
  rb_define_method(cDuckDBPreparedStatement, "bind_int16", duckdb_prepared_statement_bind_int16, 2);
208
223
  rb_define_method(cDuckDBPreparedStatement, "bind_int32", duckdb_prepared_statement_bind_int32, 2);
209
224
  rb_define_method(cDuckDBPreparedStatement, "bind_int64", duckdb_prepared_statement_bind_int64, 2);
data/ext/duckdb/result.c CHANGED
@@ -55,8 +55,19 @@ static VALUE to_ruby_obj_double(duckdb_result *result, idx_t col_idx, idx_t row_
55
55
  #ifdef HAVE_DUCKDB_VALUE_BLOB
56
56
  static VALUE to_ruby_obj_string_from_blob(duckdb_result *result, idx_t col_idx, idx_t row_idx)
57
57
  {
58
+ VALUE str;
58
59
  duckdb_blob bval = duckdb_value_blob(result, col_idx, row_idx);
59
- return rb_str_new(bval.data, bval.size);
60
+ str = rb_str_new(bval.data, bval.size);
61
+
62
+ if (bval.data) {
63
+ #ifdef HAVE_DUCKDB_FREE
64
+ duckdb_free(bval.data);
65
+ #else
66
+ free(bval.data);
67
+ #endif
68
+ }
69
+
70
+ return str;
60
71
  }
61
72
  #endif /* HAVE_DUCKDB_VALUE_BLOB */
62
73
 
@@ -86,8 +97,17 @@ static VALUE to_ruby_obj(duckdb_result *result, idx_t col_idx, idx_t row_idx)
86
97
  #endif /* HAVE_DUCKDB_VALUE_BLOB */
87
98
  default:
88
99
  p = duckdb_value_varchar(result, col_idx, row_idx);
89
- obj = rb_str_new2(p);
90
- free(p);
100
+ if (p) {
101
+ obj = rb_str_new2(p);
102
+ #ifdef HAVE_DUCKDB_FREE
103
+ duckdb_free(p);
104
+ #else
105
+ free(p);
106
+ #endif /* HAVE_DUCKDB_FREE */
107
+ if (result->columns[col_idx].type == DUCKDB_TYPE_HUGEINT) {
108
+ obj = rb_funcall(obj, rb_intern("to_i"), 0);
109
+ }
110
+ }
91
111
  }
92
112
  return obj;
93
113
  }
@@ -15,6 +15,18 @@
15
15
 
16
16
  #endif /* HAVE_DUCKDB_VALUE_BLOB */
17
17
 
18
+ #ifdef HAVE_DUCKDB_APPENDER_CREATE
19
+
20
+ #include "./appender.h"
21
+
22
+ #endif /* HAVE_DUCKDB_APPENDER_CREATE */
23
+
24
+ #ifdef HAVE_DUCKDB_CREATE_CONFIG
25
+
26
+ #include "./config.h"
27
+
28
+ #endif /* HAVE_DUCKDB_CREATE_CONFIG */
29
+
18
30
  extern VALUE mDuckDB;
19
31
  extern VALUE cDuckDBDatabase;
20
32
  extern VALUE cDuckDBConnection;
@@ -25,6 +37,12 @@ extern VALUE cDuckDBBlob;
25
37
 
26
38
  #endif /* HAVE_DUCKDB_VALUE_BLOB */
27
39
 
40
+ #ifdef HAVE_DUCKDB_CREATE_CONFIG
41
+
42
+ extern VALUE cDuckDBConfig;
43
+
44
+ #endif /* HAVE_DUCKDB_CREATE_CONFIG */
45
+
28
46
  extern VALUE eDuckDBError;
29
47
 
30
48
  #endif
@@ -0,0 +1,102 @@
1
+ require 'date'
2
+
3
+ module DuckDB
4
+ if defined?(DuckDB::Appender)
5
+ # The DuckDB::Appender encapsulates DuckDB Appender.
6
+ #
7
+ # require 'duckdb'
8
+ # db = DuckDB::Database.open
9
+ # con = db.connect
10
+ # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
11
+ # appender = con.appender('users')
12
+ # appender.append_row(1, 'Alice')
13
+ #
14
+ class Appender
15
+ RANGE_INT16 = -32_768..32_767
16
+ RANGE_INT32 = -2_147_483_648..2_147_483_647
17
+ RANGE_INT64 = -9_223_372_036_854_775_808..9_223_372_036_854_775_807
18
+
19
+ def append_hugeint(value)
20
+ case value
21
+ when Integer
22
+ append_varchar(value.to_s)
23
+ else
24
+ rb_raise(ArgumentError, "2nd argument `#{value}` must be Integer.")
25
+ end
26
+ end
27
+
28
+ #
29
+ # appends value.
30
+ #
31
+ # require 'duckdb'
32
+ # db = DuckDB::Database.open
33
+ # con = db.connect
34
+ # con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
35
+ # appender = con.appender('users')
36
+ # appender.begin_row
37
+ # appender.append(1)
38
+ # appender.append('Alice')
39
+ # appender.end_row
40
+ #
41
+ def append(value)
42
+ case value
43
+ when NilClass
44
+ append_null
45
+ when Float
46
+ append_double(value)
47
+ when Integer
48
+ case value
49
+ when RANGE_INT16
50
+ append_int16(value)
51
+ when RANGE_INT32
52
+ append_int32(value)
53
+ when RANGE_INT64
54
+ append_int64(value)
55
+ else
56
+ append_hugeint(value)
57
+ end
58
+ when String
59
+ if defined?(DuckDB::Blob)
60
+ blob?(value) ? append_blob(value) : append_varchar(value)
61
+ else
62
+ append_varchar(value)
63
+ end
64
+ when TrueClass, FalseClass
65
+ append_bool(value)
66
+ when Time
67
+ append_varchar(value.strftime('%Y-%m-%d %H:%M:%S.%N'))
68
+ when Date
69
+ append_varchar(value.strftime('%Y-%m-%d'))
70
+ else
71
+ rb_raise(DuckDB::Error, "not supported type #{value} (value.class)")
72
+ end
73
+ end
74
+
75
+ #
76
+ # append a row.
77
+ #
78
+ # appender.append_row(1, 'Alice')
79
+ #
80
+ # is same as:
81
+ #
82
+ # appender.begin_row
83
+ # appender.append(1)
84
+ # appender.append('Alice')
85
+ # appender.end_row
86
+ #
87
+ def append_row(*args)
88
+ begin_row
89
+ args.each do |arg|
90
+ append(arg)
91
+ end
92
+ end_row
93
+ end
94
+
95
+ private
96
+
97
+ def blob?(value)
98
+ value.instance_of?(DuckDB::Blob) || value.encoding == Encoding::BINARY
99
+ end
100
+ end
101
+ end
102
+ end