77
88namespace duckdb {
99
10+ static bool IsDigit (char c) {
11+ return c >= ' 0' && c <= ' 9' ;
12+ }
13+
14+ static bool HasFourDigitDatePrefix (const string &value) {
15+ return value.size () >= 10 && IsDigit (value[0 ]) && IsDigit (value[1 ]) && IsDigit (value[2 ]) && IsDigit (value[3 ]) &&
16+ value[4 ] == ' -' && IsDigit (value[5 ]) && IsDigit (value[6 ]) && value[7 ] == ' -' && IsDigit (value[8 ]) &&
17+ IsDigit (value[9 ]);
18+ }
19+
1020PostgresMetadataManager::PostgresMetadataManager (DuckLakeTransaction &transaction)
1121 : DuckLakeMetadataManager(transaction) {
1222}
@@ -120,13 +130,42 @@ string PostgresMetadataManager::GetPostgresStatsType(const LogicalType &type) {
120130 return " REAL" ;
121131 case LogicalTypeId::DOUBLE :
122132 return " DOUBLE PRECISION" ;
133+ case LogicalTypeId::DATE :
134+ return " DATE" ;
135+ case LogicalTypeId::TIMESTAMP :
136+ case LogicalTypeId::TIMESTAMP_SEC :
137+ case LogicalTypeId::TIMESTAMP_MS :
138+ return " TIMESTAMP" ;
123139 default :
124140 return type.ToString ();
125141 }
126142}
127143
144+ bool PostgresMetadataManager::IsPostgresTemporalStatsType (const LogicalType &type) {
145+ switch (type.id ()) {
146+ case LogicalTypeId::DATE :
147+ case LogicalTypeId::TIMESTAMP :
148+ case LogicalTypeId::TIMESTAMP_SEC :
149+ case LogicalTypeId::TIMESTAMP_MS :
150+ return true ;
151+ default :
152+ return false ;
153+ }
154+ }
155+
156+ bool PostgresMetadataManager::CanCastTemporalValueForValueComparison (const Value &val, const LogicalType &type) {
157+ auto value = val.ToString ();
158+ if (!HasFourDigitDatePrefix (value)) {
159+ return false ;
160+ }
161+ if (type.id () == LogicalTypeId::DATE ) {
162+ return value.size () == 10 ;
163+ }
164+ return IsPostgresTemporalStatsType (type);
165+ }
166+
128167bool PostgresMetadataManager::CanCastStatsForValueComparison (const LogicalType &type) {
129- return type.IsNumeric () || type.id () == LogicalTypeId::BOOLEAN ;
168+ return type.IsNumeric () || type.id () == LogicalTypeId::BOOLEAN || IsPostgresTemporalStatsType (type) ;
130169}
131170
132171string PostgresMetadataManager::CastValueToTarget (const Value &val, const LogicalType &type) {
@@ -138,13 +177,27 @@ string PostgresMetadataManager::CastValueToTarget(const Value &val, const Logica
138177 return val.ToString ();
139178 }
140179 auto literal = DuckLakeUtil::SQLLiteralToString (val.ToString ());
180+ if (IsPostgresTemporalStatsType (type) && CanCastTemporalValueForValueComparison (val, type)) {
181+ return literal + " ::" + GetPostgresStatsType (type);
182+ }
141183 if (RequiresValueComparison (type) && CanCastStatsForValueComparison (type)) {
142184 return literal + " ::" + GetPostgresStatsType (type);
143185 }
144186 return literal;
145187}
146188
147189string PostgresMetadataManager::CastStatsToTarget (const string &stats, const LogicalType &type) {
190+ if (IsPostgresTemporalStatsType (type)) {
191+ string regex;
192+ if (type.id () == LogicalTypeId::DATE ) {
193+ regex = " '^[0-9]{4}-(0[1-9]|1[0-2])-([0][1-9]|[12][0-9]|3[01])$'" ;
194+ } else {
195+ regex =
196+ " '^[0-9]{4}-(0[1-9]|1[0-2])-([0][1-9]|[12][0-9]|3[01])( [0-9]{2}:[0-9]{2}:[0-9]{2}(\\ .[0-9]{1,6})?)?$'" ;
197+ }
198+ return StringUtil::Format (" (CASE WHEN %s ~ %s THEN %s::%s END)" , stats, regex, stats,
199+ GetPostgresStatsType (type));
200+ }
148201 if (RequiresValueComparison (type) && CanCastStatsForValueComparison (type)) {
149202 return stats + " ::" + GetPostgresStatsType (type);
150203 }
@@ -156,9 +209,17 @@ string PostgresMetadataManager::GenerateConstantFilter(const ConstantFilter &con
156209 if (RequiresValueComparison (type) && !CanCastStatsForValueComparison (type)) {
157210 return string ();
158211 }
212+ if (IsPostgresTemporalStatsType (type) && !CanCastTemporalValueForValueComparison (constant_filter.constant , type)) {
213+ return string ();
214+ }
159215 auto constant_str = CastValueToTarget (constant_filter.constant , type);
160216 auto min_value = CastStatsToTarget (" min_value" , type);
161217 auto max_value = CastStatsToTarget (" max_value" , type);
218+ if (IsPostgresTemporalStatsType (type)) {
219+ auto postgres_type = GetPostgresStatsType (type);
220+ min_value = StringUtil::Format (" COALESCE(%s, '-infinity'::%s)" , min_value, postgres_type);
221+ max_value = StringUtil::Format (" COALESCE(%s, 'infinity'::%s)" , max_value, postgres_type);
222+ }
162223 switch (constant_filter.comparison_type ) {
163224 case ExpressionType::COMPARE_EQUAL :
164225 referenced_stats.insert (" min_value" );
@@ -215,7 +276,12 @@ unique_ptr<QueryResult> PostgresMetadataManager::ExecuteQuery(DuckLakeSnapshot s
215276 query = StringUtil::Replace (query, " {DATA_PATH}" , data_path);
216277
217278 auto passthrough_query = StringUtil::Format (" CALL %s(%s, %s)" , command, catalog_literal, SQLString (query));
218- return transaction.Query (passthrough_query);
279+ auto result = transaction.Query (passthrough_query);
280+ if (command == " postgres_execute" && !result->HasError ()) {
281+ while (result->Fetch ()) {
282+ }
283+ }
284+ return result;
219285}
220286
221287unique_ptr<QueryResult> PostgresMetadataManager::ExecuteQuery (string &query, string command) {
@@ -241,14 +307,34 @@ unique_ptr<QueryResult> PostgresMetadataManager::Query(string &query) {
241307
242308string PostgresMetadataManager::GetLatestSnapshotQuery () const {
243309 return R"(
244- SELECT * FROM postgres_query({METADATA_CATALOG_NAME_LITERAL},
245- 'SELECT snapshot_id, schema_version, next_catalog_id, next_file_id
246- FROM {METADATA_SCHEMA_ESCAPED}.ducklake_snapshot WHERE snapshot_id = (
247- SELECT MAX(snapshot_id) FROM {METADATA_SCHEMA_ESCAPED}.ducklake_snapshot
248- );')
310+ SELECT * FROM postgres_query({METADATA_CATALOG_NAME_LITERAL},
311+ 'SELECT snapshot_id, schema_version, next_catalog_id, next_file_id
312+ FROM {METADATA_SCHEMA_ESCAPED}.ducklake_snapshot WHERE snapshot_id = (
313+ SELECT MAX(snapshot_id) FROM {METADATA_SCHEMA_ESCAPED}.ducklake_snapshot
314+ );')
249315 )" ;
250316}
251317
318+ bool PostgresMetadataManager::InlinedDeletionTableExists (TableIndex, DuckLakeSnapshot snapshot,
319+ const string &table_name) {
320+ auto query = StringUtil::Format (R"(
321+ SELECT EXISTS (
322+ SELECT 1
323+ FROM information_schema.tables
324+ WHERE table_schema = {METADATA_SCHEMA_NAME_LITERAL}
325+ AND table_name = %s
326+ ))" ,
327+ DuckLakeUtil::SQLLiteralToString (table_name));
328+ auto result = Query (snapshot, query);
329+ if (result->HasError ()) {
330+ return false ;
331+ }
332+ for (auto &row : *result) {
333+ return row.GetValue <bool >(0 );
334+ }
335+ return false ;
336+ }
337+
252338// We need a specialized function here to do a reinterpret for postgres from BLOB to VARCHAR
253339shared_ptr<DuckLakeInlinedData>
254340PostgresMetadataManager::TransformInlinedData (QueryResult &result, const vector<LogicalType> &expected_types) {
0 commit comments