Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .github/workflows/build_and_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ jobs:
type: ReleaseStatic
- version: REL_17_STABLE
type: Debug
- version: REL_18_BETA1
type: Release

# Not enabled for now (waiting for April 2025 code freeze)
# - version: master
Expand Down
2 changes: 1 addition & 1 deletion Makefile.global
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ $(error Minimum version of PostgreSQL required is $(PG_MIN_VER) (but have $(PG_V
endif

# If you want to test with an unsupported PG version you can pass a different value to make
PG_MAX_VER ?= 17
PG_MAX_VER ?= 18
ifeq ($(shell expr "$(PG_MAX_VER)" \>= "$(PG_VER)"), 0)
$(error Maximum supported version of PostgreSQL is $(PG_MAX_VER) (but have $(PG_VER)))
endif
Expand Down
2 changes: 2 additions & 0 deletions include/pgduckdb/pg/declarations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,6 @@ struct Plan;
struct FuncExpr;

typedef struct FunctionCallInfoBaseData *FunctionCallInfo;

struct ExplainState;
}
10 changes: 10 additions & 0 deletions include/pgduckdb/pg/explain.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "pgduckdb/pg/declarations.hpp"
#include "duckdb/common/enums/explain_format.hpp"

namespace pgduckdb::pg {
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es);

duckdb::ExplainFormat DuckdbExplainFormat(ExplainState *es);

bool IsExplainAnalyze(ExplainState *es);
} // namespace pgduckdb::pg
5 changes: 5 additions & 0 deletions include/pgduckdb/pg/locale.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "pgduckdb/pg/declarations.hpp"

namespace pgduckdb::pg {
bool IsCLocale(Oid collation_id);
}
7 changes: 7 additions & 0 deletions include/pgduckdb/vendor/pg_ruleutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ extern List *pgduckdb_set_deparse_context_plan(List *dpcontext,
struct Plan *plan, List *ancestors);
extern List *pgduckdb_select_rtable_names_for_explain(List *rtable,
Bitmapset *rels_used);
#if PG_VERSION_NUM >= 180000
extern char *get_window_frame_options_for_explain(int frameOptions,
Node *startOffset,
Node *endOffset,
List *dpcontext,
bool forceprefix);
#endif
extern char *pgduckdb_generate_collation_name(Oid collid);
extern char *pgduckdb_generate_opclass_name(Oid opclass);
extern char *pgduckdb_get_range_partbound_string(List *bound_datums);
Expand Down
33 changes: 33 additions & 0 deletions src/pg/explain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "pgduckdb/pg/explain.hpp"
#include "pgduckdb/pgduckdb_utils.hpp"

extern "C" {
#include "postgres.h"

#if PG_VERSION_NUM >= 180000
#include "commands/explain_format.h"
#include "commands/explain_state.h"
#else
#include "commands/explain.h"
#endif
}

namespace pgduckdb::pg {
void
ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es) {
PostgresFunctionGuard(::ExplainPropertyText, qlabel, value, es);
}

duckdb::ExplainFormat
DuckdbExplainFormat(ExplainState *es) {
if (es->format == EXPLAIN_FORMAT_JSON)
return duckdb::ExplainFormat::JSON;

return duckdb::ExplainFormat::DEFAULT;
}

bool
IsExplainAnalyze(ExplainState *es) {
return es->analyze;
}
} // namespace pgduckdb::pg
32 changes: 32 additions & 0 deletions src/pg/locale.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "pgduckdb/pg/locale.hpp"

extern "C" {
#include "postgres.h"
}

/*
* Needs to be done outside of 'extern "C"' block AND before including
* pg_locale.h. Otherwise you get many compilation errors about templates being
* used in C linkage. It also needs to be done after including postgres.h,
* because otherwise USE_ICU does not exist.
*/
#ifdef USE_ICU
#include <unicode/ucol.h>
#endif

extern "C" {
#include "utils/pg_locale.h"
}

namespace pgduckdb::pg {

bool
IsCLocale(Oid collation_id) {
#if PG_VERSION_NUM >= 180000
return pg_newlocale_from_collation(collation_id)->collate_is_c;
#else
return lc_ctype_is_c(collation_id);
#endif
}

} // namespace pgduckdb::pg
2 changes: 1 addition & 1 deletion src/pg/relations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ GetAttName(const Form_pg_attribute att) {

Form_pg_attribute
GetAttr(const TupleDesc tupleDesc, int i) {
return &tupleDesc->attrs[i];
return TupleDescAttr(tupleDesc, i);
}

bool
Expand Down
5 changes: 5 additions & 0 deletions src/pgduckdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ extern "C" {
#include "pgduckdb/pgduckdb_xact.hpp"

extern "C" {

#ifdef PG_MODULE_MAGIC_EXT
PG_MODULE_MAGIC_EXT(.name = "pg_duckdb", .version = "1.0.0");
#else
PG_MODULE_MAGIC;
#endif

void
_PG_init(void) {
Expand Down
4 changes: 4 additions & 0 deletions src/pgduckdb_ddl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,11 @@ DuckdbHandleDDLPre(PlannedStmt *pstmt, const char *query_string) {
*/
MemoryContext view_query_context = GetMemoryChunkContext(stmt->into->viewQuery);
MemoryContextSwitchTo(view_query_context);
#if PG_VERSION_NUM >= 180000
stmt->into->viewQuery = (Query *)copyObjectImpl(stmt->query);
#else
stmt->into->viewQuery = (Node *)copyObjectImpl(stmt->query);
#endif
MemoryContextSwitchTo(oldcontext);
}
} else if (IsA(parsetree, RefreshMatViewStmt)) {
Expand Down
26 changes: 19 additions & 7 deletions src/pgduckdb_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "pgduckdb/pgduckdb_planner.hpp"
#include "pgduckdb/pg/transactions.hpp"
#include "pgduckdb/pg/explain.hpp"
#include "pgduckdb/pgduckdb_xact.hpp"
#include "pgduckdb/pgduckdb_hooks.hpp"
#include "pgduckdb/pgduckdb_utils.hpp"
Expand Down Expand Up @@ -330,17 +331,31 @@ DuckdbExecutorStartHook_Cpp(QueryDesc *queryDesc) {
pgduckdb::ClaimCurrentCommandId();
}

#if PG_VERSION_NUM >= 180000
static bool
#else
static void
#endif
DuckdbExecutorStartHook(QueryDesc *queryDesc, int eflags) {
pgduckdb::executor_nest_level++;
if (!pgduckdb::IsExtensionRegistered()) {
pgduckdb::MarkStatementNotTopLevel();
prev_executor_start_hook(queryDesc, eflags);
return;
return prev_executor_start_hook(queryDesc, eflags);
}

#if PG_VERSION_NUM >= 180000
if (!prev_executor_start_hook(queryDesc, eflags)) {
return false;
}
#else
prev_executor_start_hook(queryDesc, eflags);
#endif

InvokeCPPFunc(DuckdbExecutorStartHook_Cpp, queryDesc);

#if PG_VERSION_NUM >= 180000
return true;
#endif
}

/*
Expand Down Expand Up @@ -390,11 +405,8 @@ DuckdbExplainOneQueryHook(Query *query, int cursorOptions, IntoClause *into, Exp
* EXPLAIN queries are also always re-planned (see
* standard_ExplainOneQuery).
*/
duckdb_explain_analyze = es->analyze;
if (es->format == EXPLAIN_FORMAT_JSON)
duckdb_explain_format = duckdb::ExplainFormat::JSON;
else
duckdb_explain_format = duckdb::ExplainFormat::DEFAULT;
duckdb_explain_analyze = pgduckdb::pg::IsExplainAnalyze(es);
duckdb_explain_format = pgduckdb::pg::DuckdbExplainFormat(es);
duckdb_explain_ctas = into != NULL;
prev_explain_one_query_hook(query, cursorOptions, into, es, queryString, params, queryEnv);
}
Expand Down
10 changes: 4 additions & 6 deletions src/pgduckdb_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "pgduckdb/pgduckdb_planner.hpp"
#include "pgduckdb/pgduckdb_types.hpp"
#include "pgduckdb/vendor/pg_explain.hpp"
#include "pgduckdb/pg/explain.hpp"

extern "C" {
#include "postgres.h"
Expand Down Expand Up @@ -363,11 +364,8 @@ Duckdb_ExplainCustomScan_Cpp(CustomScanState *node, ExplainState *es) {
* the intended output. Since EXPLAIN EXECUTE is pretty rare for people to
* run, we consider this fine for now.
*/
duckdb_explain_analyze = es->analyze;
if (es->format == EXPLAIN_FORMAT_JSON)
duckdb_explain_format = duckdb::ExplainFormat::JSON;
else
duckdb_explain_format = duckdb::ExplainFormat::DEFAULT;
duckdb_explain_analyze = pgduckdb::pg::IsExplainAnalyze(es);
duckdb_explain_format = pgduckdb::pg::DuckdbExplainFormat(es);

DuckdbScanState *duckdb_scan_state = (DuckdbScanState *)node;
ExecuteQuery(duckdb_scan_state);
Expand Down Expand Up @@ -399,7 +397,7 @@ Duckdb_ExplainCustomScan_Cpp(CustomScanState *node, ExplainState *es) {
appendStringInfoString(es->str, "\"DuckDB Execution Plan\": ");
formatDuckDbPlanForPG(value.c_str(), es);
} else
ExplainPropertyText("DuckDB Execution Plan", explain_output.str().c_str(), es);
pgduckdb::pg::ExplainPropertyText("DuckDB Execution Plan", explain_output.str().c_str(), es);
}

static inline void
Expand Down
4 changes: 2 additions & 2 deletions src/pgduckdb_ruleutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "pgduckdb/pgduckdb_types.hpp"
#include "pgduckdb/pgduckdb_ddl.hpp"
#include "pgduckdb/pg/relations.hpp"
#include "pgduckdb/pg/locale.hpp"

extern "C" {
#include "postgres.h"
Expand Down Expand Up @@ -718,8 +719,7 @@ pgduckdb_get_tabledef(Oid relation_oid) {
* this?
*/
Oid collation = column->attcollation;
if (collation != InvalidOid && collation != DEFAULT_COLLATION_OID && collation != C_COLLATION_OID &&
collation != POSIX_COLLATION_OID) {
if (collation != InvalidOid && collation != DEFAULT_COLLATION_OID && !pgduckdb::pg::IsCLocale(collation)) {
elog(ERROR, "DuckDB does not support column collations");
}
}
Expand Down
15 changes: 14 additions & 1 deletion src/pgduckdb_table_am.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,16 @@ duckdb_estimate_rel_size(Relation /*rel*/, int32 *attr_widths, BlockNumber *page
* ------------------------------------------------------------------------
*/

#if PG_VERSION_NUM >= 180000

static bool
duckdb_scan_bitmap_next_tuple(TableScanDesc /*scan*/, TupleTableSlot * /*slot*/, bool * /*recheck*/,
uint64 * /*lossy_pages*/, uint64 * /*exact_pages*/) {
NOT_IMPLEMENTED();
}

#else

static bool
duckdb_scan_bitmap_next_block(TableScanDesc /*scan*/, TBMIterateResult * /*tbmres*/) {
NOT_IMPLEMENTED();
Expand All @@ -392,6 +402,8 @@ duckdb_scan_bitmap_next_tuple(TableScanDesc /*scan*/, TBMIterateResult * /*tbmre
NOT_IMPLEMENTED();
}

#endif

static bool
duckdb_scan_sample_next_block(TableScanDesc /*scan*/, SampleScanState * /*scanstate*/) {
NOT_IMPLEMENTED();
Expand Down Expand Up @@ -467,8 +479,9 @@ static const TableAmRoutine duckdb_methods = {.type = T_TableAmRoutine,
.relation_fetch_toast_slice = NULL,

.relation_estimate_size = duckdb_estimate_rel_size,

#if PG_VERSION_NUM < 180000
.scan_bitmap_next_block = duckdb_scan_bitmap_next_block,
#endif
.scan_bitmap_next_tuple = duckdb_scan_bitmap_next_tuple,
.scan_sample_next_block = duckdb_scan_sample_next_block,
.scan_sample_next_tuple = duckdb_scan_sample_next_tuple};
Expand Down
21 changes: 11 additions & 10 deletions src/pgduckdb_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,7 @@ ConvertDuckToPostgresArray(TupleTableSlot *slot, duckdb::Value &value, idx_t col

bool
ConvertDuckToPostgresValue(TupleTableSlot *slot, duckdb::Value &value, idx_t col) {
Oid oid = slot->tts_tupleDescriptor->attrs[col].atttypid;
Oid oid = TupleDescAttr(slot->tts_tupleDescriptor, col)->atttypid;

switch (oid) {
case BITOID:
Expand Down Expand Up @@ -1965,17 +1965,18 @@ InsertTupleIntoChunk(duckdb::DataChunk &output, PostgresScanLocalState &scan_loc
auto &array_mask = duckdb::FlatVector::Validity(result);
array_mask.SetInvalid(scan_local_state.output_vector_size);
} else {
auto attr = slot->tts_tupleDescriptor->attrs[duckdb_output_index];
if (attr.attlen == -1) {
auto attr = TupleDescAttr(slot->tts_tupleDescriptor, duckdb_output_index);
if (attr->attlen == -1) {
bool should_free = false;
Datum detoasted_value = DetoastPostgresDatum(
reinterpret_cast<varlena *>(slot->tts_values[duckdb_output_index]), &should_free);
ConvertPostgresToDuckValue(attr.atttypid, detoasted_value, result, scan_local_state.output_vector_size);
ConvertPostgresToDuckValue(attr->atttypid, detoasted_value, result,
scan_local_state.output_vector_size);
if (should_free) {
duckdb_free(reinterpret_cast<void *>(detoasted_value));
}
} else {
ConvertPostgresToDuckValue(attr.atttypid, slot->tts_values[duckdb_output_index], result,
ConvertPostgresToDuckValue(attr->atttypid, slot->tts_values[duckdb_output_index], result,
scan_local_state.output_vector_size);
}
}
Expand Down Expand Up @@ -2019,8 +2020,8 @@ InsertTuplesIntoChunk(duckdb::DataChunk &output, PostgresScanLocalState &scan_lo

for (int duckdb_output_index = 0; duckdb_output_index < natts; duckdb_output_index++) {
auto &result = output.data[duckdb_output_index];
auto attr = slots[0]->tts_tupleDescriptor->attrs[duckdb_output_index];
bool is_safe_type = IsThreadSafeTypeForPostgresToDuckDB(attr.atttypid, result.GetType().id());
auto attr = TupleDescAttr(slots[0]->tts_tupleDescriptor, duckdb_output_index);
bool is_safe_type = IsThreadSafeTypeForPostgresToDuckDB(attr->atttypid, result.GetType().id());

std::unique_ptr<std::lock_guard<std::recursive_mutex>> lock_guard;
MemoryContext old_ctx = NULL;
Expand All @@ -2034,17 +2035,17 @@ InsertTuplesIntoChunk(duckdb::DataChunk &output, PostgresScanLocalState &scan_lo
auto &array_mask = duckdb::FlatVector::Validity(result);
array_mask.SetInvalid(scan_local_state.output_vector_size + row);
} else {
if (attr.attlen == -1) {
if (attr->attlen == -1) {
bool should_free = false;
Datum detoasted_value = DetoastPostgresDatum(
reinterpret_cast<varlena *>(slots[row]->tts_values[duckdb_output_index]), &should_free);
ConvertPostgresToDuckValue(attr.atttypid, detoasted_value, result,
ConvertPostgresToDuckValue(attr->atttypid, detoasted_value, result,
scan_local_state.output_vector_size + row);
if (should_free) {
duckdb_free(reinterpret_cast<void *>(detoasted_value));
}
} else {
ConvertPostgresToDuckValue(attr.atttypid, slots[row]->tts_values[duckdb_output_index], result,
ConvertPostgresToDuckValue(attr->atttypid, slots[row]->tts_values[duckdb_output_index], result,
scan_local_state.output_vector_size + row);
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/scan/postgres_table_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ extern "C" {
#include "miscadmin.h"
#include "access/xact.h"
#include "commands/explain.h"
#if PG_VERSION_NUM >= 180000
#include "commands/explain_format.h"
#include "commands/explain_state.h"
#endif
#include "executor/executor.h"
#include "executor/execParallel.h"
#include "executor/tqueue.h"
Expand Down Expand Up @@ -61,8 +65,13 @@ PostgresTableReader::InitUnsafe(const char *table_scan_query, bool count_tuples_

PlannedStmt *planned_stmt = standard_planner(query, table_scan_query, 0, nullptr);

#if PG_VERSION_NUM >= 180000
table_scan_query_desc = CreateQueryDesc(planned_stmt, nullptr, table_scan_query, GetActiveSnapshot(),
InvalidSnapshot, None_Receiver, nullptr, nullptr, 0);
#else
table_scan_query_desc = CreateQueryDesc(planned_stmt, table_scan_query, GetActiveSnapshot(), InvalidSnapshot,
None_Receiver, nullptr, nullptr, 0);
#endif

ExecutorStart(table_scan_query_desc, 0);

Expand Down
Loading