duckdb 0.0.12 → 0.2.8.0

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