Class: DuckDB::Appender

Inherits:
Object
  • Object
show all
Includes:
Converter
Defined in:
lib/duckdb/appender.rb,
ext/duckdb/appender.c

Overview

The DuckDB::Appender encapsulates DuckDB Appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
appender = con.appender('users')
appender.append_row(1, 'Alice')

Constant Summary

Constants included from Converter

Converter::EPOCH, Converter::EPOCH_UTC, Converter::HALF_HUGEINT, Converter::HALF_HUGEINT_BIT, Converter::LOWER_HUGEINT_MASK, Converter::RANGE_HUGEINT, Converter::RANGE_INT16, Converter::RANGE_INT32, Converter::RANGE_INT64, Converter::RANGE_INT8, Converter::RANGE_UHUGEINT, Converter::RANGE_UINT16, Converter::RANGE_UINT32, Converter::RANGE_UINT64, Converter::RANGE_UINT8

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Converter

_decimal_to_unscaled, _hugeint_lower, _hugeint_upper, _parse_date, _parse_deciaml, _parse_time, _to_date, _to_decimal_from_hugeint, _to_decimal_from_value, _to_hugeint_from_vector, _to_infinity, _to_interval_from_vector, _to_query_progress, _to_time, _to_time_from_duckdb_time, _to_time_from_duckdb_time_ns, _to_time_from_duckdb_time_tz, _to_time_from_duckdb_timestamp_ms, _to_time_from_duckdb_timestamp_ns, _to_time_from_duckdb_timestamp_s, _to_time_from_duckdb_timestamp_tz, decimal_to_hugeint, default_timezone_utc?, format_timestamp_with_micro, format_timezone_offset, integer_to_hugeint

Constructor Details

#initialize(con, schema, table) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'ext/duckdb/appender.c', line 126

static VALUE appender_initialize(VALUE self, VALUE con, VALUE schema, VALUE table) {

    rubyDuckDBConnection *ctxcon;
    rubyDuckDBAppender *ctx;
    char *pschema = 0;

    if (!rb_obj_is_kind_of(con, cDuckDBConnection)) {
        rb_raise(rb_eTypeError, "1st argument should be instance of DackDB::Connection");
    }

    TypedData_Get_Struct(self, rubyDuckDBAppender, &appender_data_type, ctx);
    ctxcon = get_struct_connection(con);

    if (schema != Qnil) {
        pschema = StringValuePtr(schema);
    }

    if (duckdb_appender_create(ctxcon->con, pschema, StringValuePtr(table), &(ctx->appender)) == DuckDBError) {
        rb_raise(eDuckDBError, "failed to create appender");
    }
    return self;
}

Class Method Details

.DuckDB::Appender.create_queryDuckDB::Appender Also known as: from_query

Returns a new Appender instance created from a query.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
query = 'INSERT OR REPLACE INTO users SELECT i, val FROM my_appended_data'
types = [DuckDB::LogicalType::INTEGER, DuckDB::LogicalType::VARCHAR]
appender = DuckDB::Appender.create_query(con, query, types, 'my_appended_data', %w[i val])

Returns:



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'ext/duckdb/appender.c', line 78

static VALUE appender_s_create_query(VALUE klass, VALUE con, VALUE query, VALUE types, VALUE table, VALUE columns) {
    rubyDuckDBConnection *ctxcon;
    rubyDuckDBAppender *ctx;
    char *query_str = StringValuePtr(query);
    char *table_name = NULL;
    const char **column_names = NULL;
    idx_t column_count = 0;
    duckdb_logical_type *type_array = NULL;
    VALUE appender = Qnil;

    if (!rb_obj_is_kind_of(con, cDuckDBConnection)) {
        rb_raise(rb_eTypeError, "1st argument should be instance of DackDB::Connection");
    }
    if (rb_obj_is_kind_of(types, rb_cArray) == Qfalse) {
        rb_raise(rb_eTypeError, "2nd argument should be an Array");
    }
    column_count = RARRAY_LEN(types);
    type_array = ALLOCA_N(duckdb_logical_type, (size_t)column_count);
    for (idx_t i = 0; i < column_count; i++) {
        VALUE type_val = rb_ary_entry(types, i);
        rubyDuckDBLogicalType *type_ctx = get_struct_logical_type(type_val);
        type_array[i] = type_ctx->logical_type;
    }

    if (table != Qnil) {
        table_name = StringValuePtr(table);
    }
    if (columns != Qnil) {
        if (rb_obj_is_kind_of(columns, rb_cArray) == Qfalse) {
            rb_raise(rb_eTypeError, "4th argument should be an Array or nil");
        }
        idx_t col_count = RARRAY_LEN(columns);
        column_names = ALLOCA_N(const char *, (size_t)col_count);
        for (idx_t i = 0; i < col_count; i++) {
            VALUE col_name_val = rb_ary_entry(columns, i);
            column_names[i] = StringValuePtr(col_name_val);
        }
    }
    ctxcon = get_struct_connection(con);
    appender = allocate(klass);
    TypedData_Get_Struct(appender, rubyDuckDBAppender, &appender_data_type, ctx);
    if (duckdb_appender_create_query(ctxcon->con, query_str, column_count, type_array, table_name, column_names, &ctx->appender) == DuckDBError) {
        rb_raise(eDuckDBError, "failed to create appender from query");
    }

    return appender;
}

Instance Method Details

#append(value) ⇒ Object

appends value.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
appender = con.appender('users')
appender.append(1)
appender.append('Alice')
appender.end_row

rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength



609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
# File 'lib/duckdb/appender.rb', line 609

def append(value)
  case value
  when NilClass
    append_null
  when Float
    append_double(value)
  when Integer
    append_integer_value(value)
  when String
    append_string_value(value)
  when TrueClass, FalseClass
    append_bool(value)
  when Time
    append_timestamp(value)
  when Date
    append_date(value)
  when DuckDB::Interval
    append_interval(value)
  else
    raise(DuckDB::Error, "not supported type #{value} (#{value.class})")
  end
end

#append_blob(value) ⇒ Object

call-seq:

appender.append_blob(val) -> self

Appends a varchar value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE values (value BLOB)')
appender = con.appender('values')
appender
  .append('\0\1\2\3\4\5'.encode(Encoding::BINARY))
  .end_row
  .flush


380
381
382
383
384
# File 'lib/duckdb/appender.rb', line 380

def append_blob(value)
  return self if _append_blob(value)

  raise_appender_error('failed to append_blob')
end

#append_bool(value) ⇒ Object

call-seq:

appender.append_bool(val) -> self

Appends a boolean value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, active BOOLEAN)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_bool(true)
  .end_row
  .flush


111
112
113
114
115
# File 'lib/duckdb/appender.rb', line 111

def append_bool(value)
  return self if _append_bool(value)

  raise_appender_error('failed to append_bool')
end

#append_data_chunk(chunk) ⇒ Object

call-seq:

appender.append_data_chunk(chunk) -> self

Raises:

  • (ArgumentError)


590
591
592
593
594
595
596
# File 'lib/duckdb/appender.rb', line 590

def append_data_chunk(chunk)
  raise ArgumentError, "expected DuckDB::DataChunk, got #{chunk.class}" unless chunk.is_a?(DuckDB::DataChunk)

  return self if _append_data_chunk(chunk)

  raise_appender_error('failed to append_data_chunk')
end

#append_date(value) ⇒ Object

call-seq:

appender.append_date(val) -> self

Appends a date value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE dates (date_value DATE)')
appender = con.appender('dates')
appender.append_date(Date.today)
# or
# appender.append_date(Time.now)
# appender.append_date('2021-10-10')
appender.end_row
appender.flush


488
489
490
491
492
493
494
# File 'lib/duckdb/appender.rb', line 488

def append_date(value)
  date = _parse_date(value)

  return self if _append_date(date.year, date.month, date.day)

  raise_appender_error('failed to append_date')
end

#append_defaultObject

call-seq:

appender.append_default -> self

Appends a default value to the current row in the appender. If the column does not have a default value, this method appends a NULL value.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE values (value INTEGER DEFAULT 1)')
appender = con.appender('values')
appender
  .append_default
  .end_row
  .flush


422
423
424
425
426
# File 'lib/duckdb/appender.rb', line 422

def append_default
  return self if _append_default

  raise_appender_error('failed to append_default')
end

#append_double(value) ⇒ Object

call-seq:

appender.append_double(val) -> self

Appends a double value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE numbers (num DOUBLE)')
appender = con.appender('numbers')
appender
  .append_double(1.23)
  .end_row
  .flush


320
321
322
323
324
# File 'lib/duckdb/appender.rb', line 320

def append_double(value)
  return self if _append_double(value)

  raise_appender_error('failed to append_double')
end

#append_float(value) ⇒ Object

call-seq:

appender.append_float(val) -> self

Appends a float value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE numbers (num FLOAT)')
appender = con.appender('numbers')
appender
  .append_float(1.23)
  .end_row
  .flush


300
301
302
303
304
# File 'lib/duckdb/appender.rb', line 300

def append_float(value)
  return self if _append_float(value)

  raise_appender_error('failed to append_float')
end

#append_hugeint(value) ⇒ Object

call-seq:

appender.append_hugeint(val) -> self

Appends a huge int value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE numbers (num HUGEINT)')
appender = con.appender('numbers')
appender
  .append_hugeint(-170_141_183_460_469_231_731_687_303_715_884_105_727)
  .end_row
  .flush


442
443
444
445
446
447
448
# File 'lib/duckdb/appender.rb', line 442

def append_hugeint(value)
  lower, upper = integer_to_hugeint(value)

  return self if _append_hugeint(lower, upper)

  raise_appender_error('failed to append_hugeint')
end

#append_int16(value) ⇒ Object

call-seq:

appender.append_int16(val) -> self

Appends an int16(SMALLINT) value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, age SMALLINT)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_int16(20)
  .end_row
  .flush


154
155
156
157
158
# File 'lib/duckdb/appender.rb', line 154

def append_int16(value)
  return self if _append_int16(value)

  raise_appender_error('failed to append_int16')
end

#append_int32(value) ⇒ Object

call-seq:

appender.append_int32(val) -> self

Appends an int32(INTEGER) value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, age INTEGER)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_int32(20)
  .end_row
  .flush


175
176
177
178
179
# File 'lib/duckdb/appender.rb', line 175

def append_int32(value)
  return self if _append_int32(value)

  raise_appender_error('failed to append_int32')
end

#append_int64(value) ⇒ Object

call-seq:

appender.append_int64(val) -> self

Appends an int64(BIGINT) value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, age BIGINT)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_int64(20)
  .end_row
  .flush


196
197
198
199
200
# File 'lib/duckdb/appender.rb', line 196

def append_int64(value)
  return self if _append_int64(value)

  raise_appender_error('failed to append_int64')
end

#append_int8(value) ⇒ Object

call-seq:

appender.append_int8(val) -> self

Appends an int8(TINYINT) value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, age TINYINT)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_int8(20)
  .end_row
  .flush


133
134
135
136
137
# File 'lib/duckdb/appender.rb', line 133

def append_int8(value)
  return self if _append_int8(value)

  raise_appender_error('failed to append_int8')
end

#append_interval(value) ⇒ Object

call-seq:

appender.append_interval(val) -> self

Appends an interval value to the current row in the appender. The argument must be ISO8601 duration format.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE intervals (interval_value INTERVAL)')
appender = con.appender('intervals')
appender
  .append_interval('P1Y2D') # => append 1 year 2 days interval.
  .end_row
  .flush


558
559
560
561
562
563
564
# File 'lib/duckdb/appender.rb', line 558

def append_interval(value)
  value = Interval.to_interval(value)

  return self if _append_interval(value.interval_months, value.interval_days, value.interval_micros)

  raise_appender_error('failed to append_interval')
end

#append_nullObject

call-seq:

appender.append_null -> self

Appends a NULL value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE values (value INTEGER)')
appender = con.appender('values')
appender
  .append_null
  .end_row
  .flush


400
401
402
403
404
# File 'lib/duckdb/appender.rb', line 400

def append_null
  return self if _append_null

  raise_appender_error('failed to append_null')
end

#append_row(*args) ⇒ Object

append a row.

appender.append_row(1, 'Alice')

is same as:

appender.append(2)
appender.append('Alice')
appender.end_row


642
643
644
645
646
647
# File 'lib/duckdb/appender.rb', line 642

def append_row(*args)
  args.each do |arg|
    append(arg)
  end
  end_row
end

#append_time(value) ⇒ Object

call-seq:

appender.append_time(val) -> self

Appends a time value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE times (time_value TIME)')
appender = con.appender('times')
appender.append_time(Time.now)
# or
# appender.append_time('01:01:01')
appender.end_row
appender.flush


511
512
513
514
515
516
517
# File 'lib/duckdb/appender.rb', line 511

def append_time(value)
  time = _parse_time(value)

  return self if _append_time(time.hour, time.min, time.sec, time.usec)

  raise_appender_error('failed to append_time')
end

#append_timestamp(value) ⇒ Object

call-seq:

appender.append_timestamp(val) -> self

Appends a timestamp value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE timestamps (timestamp_value TIMESTAMP)')
appender = con.appender('timestamps')
appender.append_time(Time.now)
# or
# appender.append_time(Date.today)
# appender.append_time('2021-08-01 01:01:01')
appender.end_row
appender.flush


535
536
537
538
539
540
541
# File 'lib/duckdb/appender.rb', line 535

def append_timestamp(value)
  time = to_time(value)

  return self if _append_timestamp(time.year, time.month, time.day, time.hour, time.min, time.sec, time.nsec / 1000)

  raise_appender_error('failed to append_timestamp')
end

#append_uhugeint(value) ⇒ Object

call-seq:

appender.append_uhugeint(val) -> self

Appends an unsigned huge int value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE numbers (num UHUGEINT)')
appender = con.appender('numbers')
appender
  .append_hugeint(340_282_366_920_938_463_463_374_607_431_768_211_455)
  .end_row
  .flush


464
465
466
467
468
469
470
# File 'lib/duckdb/appender.rb', line 464

def append_uhugeint(value)
  lower, upper = integer_to_hugeint(value)

  return self if _append_uhugeint(lower, upper)

  raise_appender_error('failed to append_uhugeint')
end

#append_uint16(value) ⇒ Object

call-seq:

appender.append_uint16(val) -> self

Appends an uint16 value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, age USMALLINT)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_uint16(20)
  .end_row
  .flush


238
239
240
241
242
# File 'lib/duckdb/appender.rb', line 238

def append_uint16(value)
  return self if _append_uint16(value)

  raise_appender_error('failed to append_uint16')
end

#append_uint32(value) ⇒ Object

call-seq:

appender.append_uint32(val) -> self

Appends an uint32 value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, age UINTEGER)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_uint32(20)
  .end_row
  .flush


259
260
261
262
263
# File 'lib/duckdb/appender.rb', line 259

def append_uint32(value)
  return self if _append_uint32(value)

  raise_appender_error('failed to append_uint32')
end

#append_uint64(value) ⇒ Object

call-seq:

appender.append_uint64(val) -> self

Appends an uint64 value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, age UBIGINT)')
appender = con.appender('users')
Appender
  .append_int32(1)
  .append_uint64(20)
  .end_row
  .flush


280
281
282
283
284
# File 'lib/duckdb/appender.rb', line 280

def append_uint64(value)
  return self if _append_uint64(value)

  raise_appender_error('failed to append_uint64')
end

#append_uint8(value) ⇒ Object

call-seq:

appender.append_uint8(val) -> self

Appends an uint8 value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, age UTINYINT)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_uint8(20)
  .end_row
  .flush


217
218
219
220
221
# File 'lib/duckdb/appender.rb', line 217

def append_uint8(value)
  return self if _append_uint8(value)

  raise_appender_error('failed to append_uint8')
end

#append_value(value) ⇒ Object

call-seq:

appender.append_value(val) -> self

Appends a DuckDB::Value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE numbers (num INTEGER)')
appender = con.appender('numbers')
appender
  .append_value(DuckDB::Value.create_int32(42))
  .end_row
  .flush

Raises:

  • (ArgumentError)


580
581
582
583
584
585
586
# File 'lib/duckdb/appender.rb', line 580

def append_value(value)
  raise ArgumentError, "expected DuckDB::Value, got #{value.class}" unless value.is_a?(DuckDB::Value)

  return self if _append_value(value)

  raise_appender_error('failed to append_value')
end

#append_varchar(value) ⇒ Object

call-seq:

appender.append_varchar(val) -> self

Appends a varchar value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE names (name VARCHAR)')
appender = con.appender('names')
appender
  .append_varchar('Alice')
  .end_row
  .flush


340
341
342
343
344
# File 'lib/duckdb/appender.rb', line 340

def append_varchar(value)
  return self if _append_varchar(value)

  raise_appender_error('failed to append_varchar')
end

#append_varchar_length(value, length) ⇒ Object

call-seq:

appender.append_varchar_length(val, len) -> self

Appends a varchar value to the current row in the appender.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE names (name VARCHAR)')
appender = con.appender('names')
appender
  .append_varchar_length('Alice', 5)
  .end_row
  .flush


360
361
362
363
364
# File 'lib/duckdb/appender.rb', line 360

def append_varchar_length(value, length)
  return self if _append_varchar_length(value, length)

  raise_appender_error('failed to append_varchar_length')
end

#begin_rowObject

:call-seq:

appender.begin_row -> self

A nop method, provided for backwards compatibility reasons. Does nothing. Only ‘end_row` is required.



27
28
29
# File 'lib/duckdb/appender.rb', line 27

def begin_row
  self
end

#closeObject

:call-seq:

appender.close -> self

Closes the appender by flushing all intermediate states and closing it for further appends. If flushing the data triggers a constraint violation or any other error, then all data is invalidated, and this method raises DuckDB::Error.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_varchar('Alice')
  .end_row
  .close


90
91
92
93
94
# File 'lib/duckdb/appender.rb', line 90

def close
  return self if _close

  raise_appender_error('failed to close')
end

#end_rowObject

call-seq:

appender.end_row -> self

Finish the current row of appends. After end_row is called, the next row can be appended.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_varchar('Alice')
  .end_row


44
45
46
47
48
# File 'lib/duckdb/appender.rb', line 44

def end_row
  return self if _end_row

  raise_appender_error('failed to end_row')
end

#error_messageString

Returns the error message of the appender. If there is no error, then it returns nil.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
appender = con.appender('users')
appender.error_message # => nil

Returns:

  • (String)


161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'ext/duckdb/appender.c', line 161

static VALUE appender_error_message(VALUE self) {
    rubyDuckDBAppender *ctx;
    duckdb_error_data error_data;
    const char *msg = NULL;
    VALUE rb_msg = Qnil;
    TypedData_Get_Struct(self, rubyDuckDBAppender, &appender_data_type, ctx);

    error_data = duckdb_appender_error_data(ctx->appender);
    if (duckdb_error_data_has_error(error_data)) {
        msg = duckdb_error_data_message(error_data);
        rb_msg = rb_str_new2(msg);
    }
    duckdb_destroy_error_data(&error_data);
    return rb_msg;
}

#flushObject

:call-seq:

appender.flush -> self

Flushes the appender to the table, forcing the cache of the appender to be cleared. If flushing the data triggers a constraint violation or any other error, then all data is invalidated, and this method raises DuckDB::Error.

require 'duckdb'
db = DuckDB::Database.open
con = db.connect
con.query('CREATE TABLE users (id INTEGER, name VARCHAR)')
appender = con.appender('users')
appender
  .append_int32(1)
  .append_varchar('Alice')
  .end_row
  .flush


67
68
69
70
71
# File 'lib/duckdb/appender.rb', line 67

def flush
  return self if _flush

  raise_appender_error('failed to flush')
end