diff --git a/CHANGELOG.md b/CHANGELOG.md index bb99684f..17b3fce9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file. - add `DuckDB::ScalarFunction::BindInfo#set_error(message)` to report an error early at planning time (wraps `duckdb_scalar_function_bind_set_error`). - add `DuckDB::ScalarFunction::BindInfo#get_argument(index)` to return the expression at the given argument index as a `DuckDB::Expression` object (wraps `duckdb_scalar_function_bind_get_argument`). Raises `ArgumentError` for out-of-range index. - add `DuckDB::Expression#foldable?` to check whether an expression can be folded to a constant at query planning time (wraps `duckdb_expression_is_foldable`). Returns `true` for literals and constant arithmetic, `false` for column references and non-deterministic functions. +- add `DuckDB::LogicalType.create_map` to create a map logical type. ## Breaking changes - rename `DuckDB::BindInfo` to `DuckDB::TableFunction::BindInfo`. `DuckDB::BindInfo` still works but emits a deprecation warning. diff --git a/ext/duckdb/logical_type.c b/ext/duckdb/logical_type.c index f2dc79e6..515145d5 100644 --- a/ext/duckdb/logical_type.c +++ b/ext/duckdb/logical_type.c @@ -25,6 +25,7 @@ static VALUE duckdb_logical_type__get_alias(VALUE self); static VALUE duckdb_logical_type__set_alias(VALUE self, VALUE aname); static VALUE duckdb_logical_type_s_create_array_type(VALUE klass, VALUE child, VALUE array_size); static VALUE duckdb_logical_type_s_create_list_type(VALUE klass, VALUE child); +static VALUE duckdb_logical_type_s_create_map_type(VALUE klass, VALUE key, VALUE value); static VALUE initialize(VALUE self, VALUE type_id_arg); static const rb_data_type_t logical_type_data_type = { @@ -471,6 +472,24 @@ static VALUE duckdb_logical_type_s_create_list_type(VALUE klass, VALUE child) { return rbduckdb_create_logical_type(new_type); } +/* + * call-seq: + * DuckDB::LogicalType._create_map_type(key_type, value_type) -> DuckDB::LogicalType + * + * Return a map logical type from the given key and value logical types. + */ +static VALUE duckdb_logical_type_s_create_map_type(VALUE klass, VALUE key, VALUE value) { + rubyDuckDBLogicalType *key_ctx = get_struct_logical_type(key); + rubyDuckDBLogicalType *value_ctx = get_struct_logical_type(value); + duckdb_logical_type new_type = duckdb_create_map_type(key_ctx->logical_type, value_ctx->logical_type); + + if (!new_type) { + rb_raise(eDuckDBError, "Failed to create map type"); + } + + return rbduckdb_create_logical_type(new_type); +} + VALUE rbduckdb_create_logical_type(duckdb_logical_type logical_type) { VALUE obj; rubyDuckDBLogicalType *ctx; @@ -513,6 +532,8 @@ void rbduckdb_init_duckdb_logical_type(void) { duckdb_logical_type_s_create_array_type, 2); rb_define_private_method(rb_singleton_class(cDuckDBLogicalType), "_create_list_type", duckdb_logical_type_s_create_list_type, 1); + rb_define_private_method(rb_singleton_class(cDuckDBLogicalType), "_create_map_type", + duckdb_logical_type_s_create_map_type, 2); rb_define_method(cDuckDBLogicalType, "initialize", initialize, 1); } diff --git a/lib/duckdb/logical_type.rb b/lib/duckdb/logical_type.rb index de3a82ec..6b366ee6 100644 --- a/lib/duckdb/logical_type.rb +++ b/lib/duckdb/logical_type.rb @@ -96,6 +96,21 @@ def create_list(type) _create_list_type(LogicalType.resolve(type)) end + # Creates a map logical type with the given key and value types. + # + # The +key_type+ and +value_type+ arguments can be symbols or + # DuckDB::LogicalType instances. + # + # require 'duckdb' + # + # map_type = DuckDB::LogicalType.create_map(:integer, :varchar) + # map_type.type #=> :map + # map_type.key_type.type #=> :integer + # map_type.value_type.type #=> :varchar + def create_map(key_type, value_type) + _create_map_type(LogicalType.resolve(key_type), LogicalType.resolve(value_type)) + end + private def raise_resolve_error(symbol) diff --git a/test/duckdb_test/logical_type_test.rb b/test/duckdb_test/logical_type_test.rb index 144aaad8..411579aa 100644 --- a/test/duckdb_test/logical_type_test.rb +++ b/test/duckdb_test/logical_type_test.rb @@ -416,6 +416,30 @@ def test_s_create_list_with_invalid_arg assert_raises(DuckDB::Error) { DuckDB::LogicalType.create_list(:nonexistent) } end + def test_s_create_map_with_logical_type + map_type = DuckDB::LogicalType.create_map(DuckDB::LogicalType::INTEGER, DuckDB::LogicalType::VARCHAR) + + assert_equal(:map, map_type.type) + assert_equal(:integer, map_type.key_type.type) + assert_equal(:varchar, map_type.value_type.type) + end + + def test_s_create_map_with_symbol + map_type = DuckDB::LogicalType.create_map(:integer, :varchar) + + assert_equal(:map, map_type.type) + assert_equal(:integer, map_type.key_type.type) + assert_equal(:varchar, map_type.value_type.type) + end + + def test_s_create_map_with_invalid_key_type + assert_raises(DuckDB::Error) { DuckDB::LogicalType.create_map(:nonexistent, :varchar) } + end + + def test_s_create_map_with_invalid_value_type + assert_raises(DuckDB::Error) { DuckDB::LogicalType.create_map(:integer, :nonexistent) } + end + def test_new_with_primitive_like_complex_type # DUCKDB_TYPE_BIT = 29 bit_type = DuckDB::LogicalType.new(29)