Skip to content

Commit 51db0a6

Browse files
authored
Merge pull request #1077 from suketa/add-date-support-to-scalar-function
Add DATE support to scalar functions
2 parents 4c7401b + 8c41886 commit 51db0a6

File tree

3 files changed

+41
-5
lines changed

3 files changed

+41
-5
lines changed

ext/duckdb/scalar_function.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,22 @@ static void vector_set_value_at(duckdb_vector vector, duckdb_logical_type elemen
278278
((duckdb_timestamp *)vector_data)[index] = ts;
279279
break;
280280
}
281+
case DUCKDB_TYPE_DATE: {
282+
// Convert Ruby Date to DuckDB date
283+
// Ruby Date is defined in date library, check with Class name
284+
VALUE date_class = rb_const_get(rb_cObject, rb_intern("Date"));
285+
if (!rb_obj_is_kind_of(value, date_class)) {
286+
rb_raise(rb_eTypeError, "Expected Date object for DATE");
287+
}
288+
289+
VALUE year = rb_funcall(value, rb_intern("year"), 0);
290+
VALUE month = rb_funcall(value, rb_intern("month"), 0);
291+
VALUE day = rb_funcall(value, rb_intern("day"), 0);
292+
293+
duckdb_date date = rbduckdb_to_duckdb_date_from_value(year, month, day);
294+
((duckdb_date *)vector_data)[index] = date;
295+
break;
296+
}
281297
default:
282298
rb_raise(rb_eArgError, "Unsupported return type for scalar function");
283299
break;

lib/duckdb/scalar_function.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module DuckDB
44
# DuckDB::ScalarFunction encapsulates DuckDB's scalar function
55
class ScalarFunction
66
# Sets the return type for the scalar function.
7-
# Currently supports BIGINT, BLOB, BOOLEAN, DOUBLE, FLOAT, INTEGER, TIMESTAMP, and VARCHAR types.
7+
# Currently supports BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, INTEGER, TIMESTAMP, and VARCHAR types.
88
#
99
# @param logical_type [DuckDB::LogicalType] the return type
1010
# @return [DuckDB::ScalarFunction] self
@@ -13,9 +13,9 @@ def return_type=(logical_type)
1313
raise DuckDB::Error, 'logical_type must be a DuckDB::LogicalType' unless logical_type.is_a?(DuckDB::LogicalType)
1414

1515
# Check if the type is supported
16-
unless %i[bigint blob boolean double float integer timestamp varchar].include?(logical_type.type)
16+
unless %i[bigint blob boolean date double float integer timestamp varchar].include?(logical_type.type)
1717
raise DuckDB::Error,
18-
'Only BIGINT, BLOB, BOOLEAN, DOUBLE, FLOAT, INTEGER, TIMESTAMP, and VARCHAR return types are ' \
18+
'Only BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, INTEGER, TIMESTAMP, and VARCHAR return types are ' \
1919
'currently supported'
2020
end
2121

test/duckdb_test/scalar_function_test.rb

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ def test_return_type_setter
3737

3838
def test_return_type_setter_raises_error_for_unsupported_type
3939
sf = DuckDB::ScalarFunction.new
40-
timestamp_type = DuckDB::LogicalType.new(13) # DUCKDB_TYPE_TIMESTAMP (unsupported)
40+
time_type = DuckDB::LogicalType.new(14) # DUCKDB_TYPE_TIME (unsupported)
4141

4242
error = assert_raises(DuckDB::Error) do
43-
sf.return_type = timestamp_type
43+
sf.return_type = time_type
4444
end
4545

4646
assert_match(/only.*supported/i, error.message)
@@ -271,5 +271,25 @@ def test_scalar_function_timestamp_return_type # rubocop:disable Metrics/AbcSize
271271
assert_equal Time.new(2024, 1, 15, 11, 30, 0), rows[0][0]
272272
assert_equal Time.new(2024, 12, 26, 0, 59, 59), rows[1][0]
273273
end
274+
275+
def test_scalar_function_date_return_type # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
276+
@con.execute('SET threads=1')
277+
@con.execute('CREATE TABLE test_table (d DATE)')
278+
@con.execute("INSERT INTO test_table VALUES ('2024-01-15'), ('2024-12-25')")
279+
280+
sf = DuckDB::ScalarFunction.new
281+
sf.name = 'add_one_day'
282+
sf.add_parameter(DuckDB::LogicalType.new(13)) # DATE (type ID 13)
283+
sf.return_type = DuckDB::LogicalType.new(13) # DATE
284+
sf.set_function { |date| date + 1 } # Add 1 day
285+
286+
@con.register_scalar_function(sf)
287+
result = @con.execute('SELECT add_one_day(d) FROM test_table ORDER BY d')
288+
rows = result.to_a
289+
290+
assert_equal 2, rows.size
291+
assert_equal Date.new(2024, 1, 16), rows[0][0]
292+
assert_equal Date.new(2024, 12, 26), rows[1][0]
293+
end
274294
end
275295
end

0 commit comments

Comments
 (0)