Skip to content
Draft
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: 8 additions & 2 deletions .github/workflows/build_and_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,25 @@ jobs:
matrix:
include:
- version: REL_14_STABLE
repo: postgres/postgres
type: Release
- version: REL_15_STABLE
repo: postgres/postgres
type: Release
- version: REL_16_STABLE
repo: postgres/postgres
type: Release
# For PG17 we build DuckDB as a static library. There's nothing special
# about the static library, nor PG17. This is only done so that we have
# CI coverage for our logic to build a static library version.
- version: REL_17_STABLE
repo: postgres/postgres
type: ReleaseStatic
- version: REL_17_STABLE
repo: postgres/postgres
type: Debug
- version: REL_18_BETA1
- version: cf/5214
repo: postgresql-cfbot/postgresql
type: Release

# Not enabled for now (waiting for April 2025 code freeze)
Expand Down Expand Up @@ -89,7 +95,7 @@ jobs:
- name: Checkout PostgreSQL code
run: |
rm -rf postgres
git clone --branch ${{ matrix.version }} --single-branch --depth 1 https://github.com/postgres/postgres.git
git clone --branch ${{ matrix.version }} --single-branch --depth 1 https://github.com/${{ matrix.repo }}.git postgres

- name: Compute Version SHAs
id: versions
Expand Down
2 changes: 1 addition & 1 deletion include/pgduckdb/pgduckdb_ruleutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ bool pgduckdb_is_unresolved_type(Oid type_oid);
bool pgduckdb_is_fake_type(Oid type_oid);
bool pgduckdb_var_is_duckdb_row(Var *var);
bool pgduckdb_func_returns_duckdb_row(RangeTblFunction *rtfunc);
Var *pgduckdb_duckdb_row_subscript_var(Expr *expr);
Var *pgduckdb_duckdb_subscript_var(Expr *expr);
bool pgduckdb_reconstruct_star_step(StarReconstructionContext *ctx, ListCell *tle_cell);
bool pgduckdb_replace_subquery_with_view(Query *query, StringInfo buf);
int pgduckdb_show_type(Const *constval, int original_showtype);
Expand Down
92 changes: 74 additions & 18 deletions src/pg/pgduckdb_subscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,35 @@ AddSubscriptExpressions(SubscriptingRef *sbsref, struct ParseState *pstate, A_In
}
}

bool
AddSubscriptExpressions(SubscriptingRef *sbsref, struct ParseState *pstate, Node *subscript, bool is_slice) {
if (IsA(subscript, A_Indices)) {
// If the subscript is an A_Indices node, we can add the expressions directly
AddSubscriptExpressions(sbsref, pstate, castNode(A_Indices, subscript), is_slice);
return true;
}

if (IsA(subscript, String)) {
sbsref->refupperindexpr = lappend(sbsref->refupperindexpr, subscript);
return true;
}

if (IsA(subscript, A_Star)) {
sbsref->refupperindexpr = lappend(sbsref->refupperindexpr, NULL);
return true;
}

return false;
}

/*
* DuckdbSubscriptTransform is called by the parser when a subscripting
* operation is performed on a duckdb type that can be indexed by arbitrary
* expressions. All this does is parse those expressions and make sure the
* subscript returns an an duckdb.unresolved_type again.
*/
void
DuckdbSubscriptTransform(SubscriptingRef *sbsref, List *indirection, struct ParseState *pstate, bool is_slice,
DuckdbSubscriptTransform(SubscriptingRef *sbsref, List **indirection, struct ParseState *pstate, bool is_slice,
bool is_assignment, const char *type_name) {
/*
* We need to populate our cache for some of the code below. Normally this
Expand All @@ -111,18 +132,22 @@ DuckdbSubscriptTransform(SubscriptingRef *sbsref, List *indirection, struct Pars
elog(ERROR, "Assignment to %s is not supported", type_name);
}

if (indirection == NIL) {
if (*indirection == NIL) {
elog(ERROR, "Subscripting %s with an empty subscript is not supported", type_name);
}

// Transform each subscript expression
foreach_node(A_Indices, subscript, indirection) {
AddSubscriptExpressions(sbsref, pstate, subscript, is_slice);
foreach_ptr(Node, subscript, *indirection) {
if (!AddSubscriptExpressions(sbsref, pstate, subscript, is_slice)) {
break;
}
}

// Set the result type of the subscripting operation
sbsref->refrestype = pgduckdb::DuckdbUnresolvedTypeOid();
sbsref->reftypmod = -1;

*indirection = list_delete_first_n(*indirection, list_length(sbsref->refupperindexpr));
}

/*
Expand All @@ -136,7 +161,7 @@ DuckdbSubscriptTransform(SubscriptingRef *sbsref, List *indirection, struct Pars
* Currently this is used for duckdb.row and duckdb.struct types.
*/
void
DuckdbTextSubscriptTransform(SubscriptingRef *sbsref, List *indirection, struct ParseState *pstate, bool is_slice,
DuckdbTextSubscriptTransform(SubscriptingRef *sbsref, List **indirection, struct ParseState *pstate, bool is_slice,
bool is_assignment, const char *type_name) {
/*
* We need to populate our cache for some of the code below. Normally this
Expand All @@ -151,33 +176,40 @@ DuckdbTextSubscriptTransform(SubscriptingRef *sbsref, List *indirection, struct
elog(ERROR, "Assignment to %s is not supported", type_name);
}

if (indirection == NIL) {
if (*indirection == NIL) {
elog(ERROR, "Subscripting %s with an empty subscript is not supported", type_name);
}

bool first = true;

// Transform each subscript expression
foreach_node(A_Indices, subscript, indirection) {
/* The first subscript needs to be a TEXT constant, since it should be
* a column reference. But the subscripts after that can be anything,
* DuckDB should interpret those. */
if (first) {
sbsref->refupperindexpr =
lappend(sbsref->refupperindexpr, CoerceSubscriptToText(pstate, subscript, type_name));
foreach_ptr(Node, subscript, *indirection) {
/*
* If the first subscript is an index expression then it needs to be
* coerced to text, since it should be a column reference. But the
* subscripts after that can be anything, DuckDB should interpret
* those.
*/
if (first && IsA(subscript, A_Indices)) {
sbsref->refupperindexpr = lappend(sbsref->refupperindexpr,
CoerceSubscriptToText(pstate, castNode(A_Indices, subscript), type_name));
if (is_slice) {
sbsref->reflowerindexpr = lappend(sbsref->reflowerindexpr, NULL);
}
first = false;
continue;
}

AddSubscriptExpressions(sbsref, pstate, subscript, is_slice);
if (!AddSubscriptExpressions(sbsref, pstate, subscript, is_slice)) {
break;
}
}

// Set the result type of the subscripting operation
sbsref->refrestype = pgduckdb::DuckdbUnresolvedTypeOid();
sbsref->reftypmod = -1;

*indirection = list_delete_first_n(*indirection, list_length(sbsref->refupperindexpr));
}

static bool
Expand Down Expand Up @@ -229,8 +261,14 @@ DuckdbSubscriptExecSetup(const SubscriptingRef * /*sbsref*/, SubscriptingRefStat
}

void
DuckdbRowSubscriptTransform(SubscriptingRef *sbsref, List *indirection, struct ParseState *pstate, bool is_slice,
#if PG_VERSION_NUM >= 180000
DuckdbRowSubscriptTransform(SubscriptingRef *sbsref, List **indirection, struct ParseState *pstate, bool is_slice,
bool is_assignment) {
#else
DuckdbRowSubscriptTransform(SubscriptingRef *sbsref, List *indirection_, struct ParseState *pstate, bool is_slice,
bool is_assignment) {
List **indirection = &indirection_;
#endif
DuckdbTextSubscriptTransform(sbsref, indirection, pstate, is_slice, is_assignment, "duckdb.row");
}

Expand All @@ -249,8 +287,14 @@ static SubscriptRoutines duckdb_row_subscript_routines = {
};

void
DuckdbUnresolvedTypeSubscriptTransform(SubscriptingRef *sbsref, List *indirection, struct ParseState *pstate,
#if PG_VERSION_NUM >= 180000
DuckdbUnresolvedTypeSubscriptTransform(SubscriptingRef *sbsref, List **indirection, struct ParseState *pstate,
bool is_slice, bool is_assignment) {
#else
DuckdbUnresolvedTypeSubscriptTransform(SubscriptingRef *sbsref, List *indirection_, struct ParseState *pstate,
bool is_slice, bool is_assignment) {
List **indirection = &indirection_;
#endif
DuckdbSubscriptTransform(sbsref, indirection, pstate, is_slice, is_assignment, "duckdb.unresolved_type");
}

Expand All @@ -269,8 +313,14 @@ static SubscriptRoutines duckdb_unresolved_type_subscript_routines = {
};

void
DuckdbStructSubscriptTransform(SubscriptingRef *sbsref, List *indirection, struct ParseState *pstate, bool is_slice,
#if PG_VERSION_NUM >= 180000
DuckdbStructSubscriptTransform(SubscriptingRef *sbsref, List **indirection, struct ParseState *pstate, bool is_slice,
bool is_assignment) {
#else
DuckdbStructSubscriptTransform(SubscriptingRef *sbsref, List *indirection_, struct ParseState *pstate, bool is_slice,
bool is_assignment) {
List **indirection = &indirection_;
#endif
DuckdbTextSubscriptTransform(sbsref, indirection, pstate, is_slice, is_assignment, "duckdb.struct");
}

Expand All @@ -289,8 +339,14 @@ static SubscriptRoutines duckdb_struct_subscript_routines = {
};

void
DuckdbMapSubscriptTransform(SubscriptingRef *sbsref, List *indirection, struct ParseState *pstate, bool is_slice,
#if PG_VERSION_NUM >= 180000
DuckdbMapSubscriptTransform(SubscriptingRef *sbsref, List **indirection, struct ParseState *pstate, bool is_slice,
bool is_assignment) {
#else
DuckdbMapSubscriptTransform(SubscriptingRef *sbsref, List *indirection_, struct ParseState *pstate, bool is_slice,
bool is_assignment) {
List **indirection = &indirection_;
#endif
DuckdbSubscriptTransform(sbsref, indirection, pstate, is_slice, is_assignment, "duckdb.map");
}

Expand Down
17 changes: 2 additions & 15 deletions src/pgduckdb_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,31 +331,18 @@ 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();
return prev_executor_start_hook(queryDesc, eflags);
prev_executor_start_hook(queryDesc, eflags);
return;
}

#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
22 changes: 18 additions & 4 deletions src/pgduckdb_ruleutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ pgduckdb_func_returns_duckdb_row(RangeTblFunction *rtfunc) {
* the Var of the duckdb row if it is.
*/
Var *
pgduckdb_duckdb_row_subscript_var(Expr *expr) {
pgduckdb_duckdb_subscript_var(Expr *expr) {
if (!expr) {
return NULL;
}
Expand All @@ -143,9 +143,6 @@ pgduckdb_duckdb_row_subscript_var(Expr *expr) {

Var *refexpr = (Var *)subscript->refexpr;

if (!pgduckdb_var_is_duckdb_row(refexpr)) {
return NULL;
}
return refexpr;
}

Expand Down Expand Up @@ -353,6 +350,14 @@ pgduckdb_subscript_has_custom_alias(Plan *plan, List *rtable, Var *subscript_var
int varno;
int varattno;

if (strcmp(colname, "?column?") == 0) {
/*
* If the column name is "?column?", then it means that Postgres
* couldn't figure out a decent alias.
*/
return false;
}

/*
* If we have a syntactic referent for the Var, and we're working from a
* parse tree, prefer to use the syntactic referent. Otherwise, fall back
Expand Down Expand Up @@ -391,6 +396,15 @@ pgduckdb_strip_first_subscript(SubscriptingRef *sbsref, StringInfo buf) {
}

Assert(sbsref->refupperindexpr);

if (linitial(sbsref->refupperindexpr) == NULL) {
return sbsref;
}

if (IsA(linitial(sbsref->refupperindexpr), String)) {
return sbsref;
}

Oid typoutput;
bool typIsVarlena;
Const *constval = castNode(Const, linitial(sbsref->refupperindexpr));
Expand Down
5 changes: 0 additions & 5 deletions src/scan/postgres_table_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,8 @@ 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
2 changes: 1 addition & 1 deletion src/vendor/pg_ruleutils_14.c
Original file line number Diff line number Diff line change
Expand Up @@ -6075,7 +6075,7 @@ get_target_list(List *targetList, deparse_context *context,
* to the column name are still valid.
*/
if (!duckdb_skip_as && outermost_targetlist) {
Var *subscript_var = pgduckdb_duckdb_row_subscript_var(tle->expr);
Var *subscript_var = pgduckdb_duckdb_subscript_var(tle->expr);
if (subscript_var) {
/*
* This cannot be moved to pgduckdb_ruleutils, because of
Expand Down
2 changes: 1 addition & 1 deletion src/vendor/pg_ruleutils_15.c
Original file line number Diff line number Diff line change
Expand Up @@ -6156,7 +6156,7 @@ get_target_list(List *targetList, deparse_context *context,
* to the column name are still valid.
*/
if (!duckdb_skip_as && outermost_targetlist) {
Var *subscript_var = pgduckdb_duckdb_row_subscript_var(tle->expr);
Var *subscript_var = pgduckdb_duckdb_subscript_var(tle->expr);
if (subscript_var) {
/*
* This cannot be moved to pgduckdb_ruleutils, because of
Expand Down
2 changes: 1 addition & 1 deletion src/vendor/pg_ruleutils_16.c
Original file line number Diff line number Diff line change
Expand Up @@ -6115,7 +6115,7 @@ get_target_list(List *targetList, deparse_context *context,
* to the column name are still valid.
*/
if (!duckdb_skip_as && outermost_targetlist) {
Var *subscript_var = pgduckdb_duckdb_row_subscript_var(tle->expr);
Var *subscript_var = pgduckdb_duckdb_subscript_var(tle->expr);
if (subscript_var) {
/*
* This cannot be moved to pgduckdb_ruleutils, because of
Expand Down
2 changes: 1 addition & 1 deletion src/vendor/pg_ruleutils_17.c
Original file line number Diff line number Diff line change
Expand Up @@ -6129,7 +6129,7 @@ get_target_list(List *targetList, deparse_context *context,
* to the column name are still valid.
*/
if (!duckdb_skip_as && outermost_targetlist) {
Var *subscript_var = pgduckdb_duckdb_row_subscript_var(tle->expr);
Var *subscript_var = pgduckdb_duckdb_subscript_var(tle->expr);
if (subscript_var) {
/*
* This cannot be moved to pgduckdb_ruleutils, because of
Expand Down
Loading
Loading