Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/common/ducklake_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,19 @@ string ToSQLString(DuckLakeMetadataManager &metadata_manager, const Value &value
// `'value'::type` operator: SQLite's parser rejects `::` outright,
// which breaks SQLite-backed metadata backends that ship these
// inlined-INSERT batches directly to SQLite.
if (!use_native_type) {
// When the backend stores this type as VARCHAR, emit a plain quoted
// string with no type annotation. The CAST(... AS VARCHAR) suffix
// survives verbatim inside STRUCT string literals and breaks the
// struct-from-string parser on read-back.
return EscapeVarcharForSQL(value.ToString());
}
return StringUtil::Format("CAST('%s' AS %s)", value.ToString(), value_type);
case LogicalTypeId::INTERVAL: {
auto interval = IntervalValue::Get(value);
if (!use_native_type) {
return EscapeVarcharForSQL(value.ToString());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the format of value.ToString() the same as '%d months %d days %lld microseconds'?

}
return StringUtil::Format("CAST('%d months %d days %lld microseconds' AS %s)", interval.months, interval.days,
interval.micros, value_type);
}
Expand Down
39 changes: 39 additions & 0 deletions test/sql/data_inlining/data_inlining_struct_non_native_types.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# name: test/sql/data_inlining/data_inlining_struct_non_native_types.test
# description: STRUCT columns with non-natively-supported field types (e.g. TIMESTAMPTZ on Postgres)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we parameterize the test to the other affected types?

i.e.,

	case LogicalTypeId::UUID:
	case LogicalTypeId::DATE:
	case LogicalTypeId::TIME:
	case LogicalTypeId::TIME_NS:
	case LogicalTypeId::TIMESTAMP:
	case LogicalTypeId::TIME_TZ:
	case LogicalTypeId::TIMESTAMP_TZ:
	case LogicalTypeId::TIMESTAMP_SEC:
	case LogicalTypeId::TIMESTAMP_MS:
	case LogicalTypeId::TIMESTAMP_NS:
	case LogicalTypeId::BLOB:
	case LogicalTypeId::GEOMETRY:

# must round-trip correctly through inlining
# group: [data_inlining]

require ducklake

require parquet

require-env DUCKLAKE_CI

test-env DUCKLAKE_CONNECTION __TEST_DIR__/{UUID}.db

test-env DATA_PATH __TEST_DIR__

statement ok
ATTACH 'ducklake:${DUCKLAKE_CONNECTION}' AS ducklake (DATA_PATH '${DATA_PATH}/struct_non_native_files', DATA_INLINING_ROW_LIMIT 10)

# STRUCT with TIMESTAMPTZ — non-native on Postgres, triggers CAST(... AS VARCHAR) in ToSQLString
statement ok
CREATE TABLE ducklake.t (id VARCHAR, attrs STRUCT(name VARCHAR, ts TIMESTAMPTZ));

statement ok
INSERT INTO ducklake.t VALUES ('row1', {'name': 'foo', 'ts': TIMESTAMPTZ '2026-05-13 04:56:37+00'});

# Verify the row was inlined (no parquet file written)
query I
SELECT COUNT(*) FROM GLOB('${DATA_PATH}/struct_non_native_files/main/t/**/*.parquet')
----
0

# Must round-trip without error
statement ok
SET timezone='UTC';

query II
SELECT id, attrs.ts FROM ducklake.t;
----
row1 2026-05-13 04:56:37+00
Loading