diff --git a/include/pgduckdb/pgduckdb_ruleutils.h b/include/pgduckdb/pgduckdb_ruleutils.h index c6d05aa2..d57e3015 100644 --- a/include/pgduckdb/pgduckdb_ruleutils.h +++ b/include/pgduckdb/pgduckdb_ruleutils.h @@ -4,5 +4,6 @@ char *pgduckdb_relation_name(Oid relid); char *pgduckdb_function_name(Oid function_oid); char *pgduckdb_get_querydef(Query *); char *pgduckdb_get_tabledef(Oid relation_id); +bool pgduckdb_is_not_default_expr(Node *node, void *context); List *pgduckdb_db_and_schema(const char *postgres_schema_name, bool is_duckdb_table); const char *pgduckdb_db_and_schema_string(const char *postgres_schema_name, bool is_duckdb_table); diff --git a/src/pgduckdb_ruleutils.cpp b/src/pgduckdb_ruleutils.cpp index cbe9ec4f..17b88161 100644 --- a/src/pgduckdb_ruleutils.cpp +++ b/src/pgduckdb_ruleutils.cpp @@ -8,6 +8,7 @@ extern "C" { #include "catalog/pg_class.h" #include "catalog/pg_collation.h" #include "commands/dbcommands.h" +#include "nodes/nodeFuncs.h" #include "lib/stringinfo.h" #include "utils/builtins.h" #include "utils/guc.h" @@ -366,4 +367,30 @@ pgduckdb_get_tabledef(Oid relation_oid) { return (buffer.data); } + +/* + * Recursively check Const nodes and Var nodes for handling more complex DEFAULT clauses + */ +bool +pgduckdb_is_not_default_expr(Node *node, void *context) { + if (node == NULL) { + return false; + } + + if (IsA(node, Var)) { + return true; + } else if (IsA(node, Const)) { + /* If location is -1, it comes from the DEFAULT clause */ + Const *con = (Const *) node; + if (con->location != -1) { + return true; + } + } + +#if PG_VERSION_NUM >= 160000 + return expression_tree_walker(node, pgduckdb_is_not_default_expr, context); +#else + return expression_tree_walker(node, (bool (*)())((void *)pgduckdb_is_not_default_expr), context); +#endif +} } diff --git a/src/vendor/pg_ruleutils_14.c b/src/vendor/pg_ruleutils_14.c index 9b44f7db..572954ea 100644 --- a/src/vendor/pg_ruleutils_14.c +++ b/src/vendor/pg_ruleutils_14.c @@ -6637,6 +6637,16 @@ get_insert_query_def(Query *query, deparse_context *context, if (tle->resjunk) continue; /* ignore junk entries */ + /* + * If it's an INSERT ... SELECT or multi-row VALUES, the entry + * with the default value is ignored unless it is specified + */ + if (values_rte || select_rte) + { + if (!pgduckdb_is_not_default_expr((Node *) tle, NULL)) + continue; + } + appendStringInfoString(buf, sep); sep = ", "; diff --git a/src/vendor/pg_ruleutils_15.c b/src/vendor/pg_ruleutils_15.c index 7d1815bb..42cabfc1 100644 --- a/src/vendor/pg_ruleutils_15.c +++ b/src/vendor/pg_ruleutils_15.c @@ -6718,6 +6718,16 @@ get_insert_query_def(Query *query, deparse_context *context, if (tle->resjunk) continue; /* ignore junk entries */ + /* + * If it's an INSERT ... SELECT or multi-row VALUES, the entry + * with the default value is ignored unless it is specified + */ + if (values_rte || select_rte) + { + if (!pgduckdb_is_not_default_expr((Node *) tle, NULL)) + continue; + } + appendStringInfoString(buf, sep); sep = ", "; diff --git a/src/vendor/pg_ruleutils_16.c b/src/vendor/pg_ruleutils_16.c index a96e1509..7ed7ae78 100644 --- a/src/vendor/pg_ruleutils_16.c +++ b/src/vendor/pg_ruleutils_16.c @@ -6678,6 +6678,16 @@ get_insert_query_def(Query *query, deparse_context *context, if (tle->resjunk) continue; /* ignore junk entries */ + /* + * If it's an INSERT ... SELECT or multi-row VALUES, the entry + * with the default value is ignored unless it is specified + */ + if (values_rte || select_rte) + { + if (!pgduckdb_is_not_default_expr((Node *) tle, NULL)) + continue; + } + appendStringInfoString(buf, sep); sep = ", "; diff --git a/src/vendor/pg_ruleutils_17.c b/src/vendor/pg_ruleutils_17.c index 46e8fafe..b9b78a83 100644 --- a/src/vendor/pg_ruleutils_17.c +++ b/src/vendor/pg_ruleutils_17.c @@ -6692,6 +6692,16 @@ get_insert_query_def(Query *query, deparse_context *context, if (tle->resjunk) continue; /* ignore junk entries */ + /* + * If it's an INSERT ... SELECT or multi-row VALUES, the entry + * with the default value is ignored unless it is specified + */ + if (values_rte || select_rte) + { + if (!pgduckdb_is_not_default_expr((Node *) tle, NULL)) + continue; + } + appendStringInfoString(buf, sep); sep = ", "; diff --git a/test/regression/expected/temporary_tables.out b/test/regression/expected/temporary_tables.out index e873eef2..cc426925 100644 --- a/test/regression/expected/temporary_tables.out +++ b/test/regression/expected/temporary_tables.out @@ -246,4 +246,146 @@ pg_temp main CREATE TABLE webpages(column00 INTEGER, column01 VARCHAR, column02 (1 row) -DROP TABLE webpages, t, t_heap, t_heap2; +-- multi-VALUES +CREATE TEMP TABLE ta (a int DEFAULT 3, b int) USING duckdb; +INSERT INTO ta (b) VALUES (123), (456); +INSERT INTO ta (a, b) VALUES (123, 456), (456, 123); +SELECT * FROM ta; + a | b +-----+----- + 3 | 123 + 3 | 456 + 123 | 456 + 456 | 123 +(4 rows) + +CREATE TEMP TABLE tb (a int DEFAULT 3, b int, c varchar DEFAULT 'pg_duckdb') USING duckdb; +INSERT INTO tb (a) VALUES (123), (456); +INSERT INTO tb (b) VALUES (123), (456); +INSERT INTO tb (c) VALUES ('ta'), ('tb'); +SELECT * FROM tb; + a | b | c +-----+-----+----------- + 123 | | pg_duckdb + 456 | | pg_duckdb + 3 | 123 | pg_duckdb + 3 | 456 | pg_duckdb + 3 | | ta + 3 | | tb +(6 rows) + +-- INSERT ... SELECT +TRUNCATE TABLE ta; +INSERT INTO ta (a) SELECT 789; +INSERT INTO ta (b) SELECT 789; +INSERT INTO ta (a) SELECT * FROM t_heap; +INSERT INTO ta (b) SELECT * FROM t_heap; +SELECT * FROM ta; + a | b +-----+----- + 789 | + 3 | 789 + 1 | + 3 | 1 +(4 rows) + +INSERT INTO ta (a) SELECT generate_series(1, 3); -- no support +ERROR: (PGDuckDB/Duckdb_ExecCustomScan) Conversion Error: Unimplemented type for cast (BIGINT[] -> INTEGER) +LINE 1: INSERT INTO pg_temp.main.ta (a) SELECT generate_series(1, 3) AS generate_serie... + ^ +TRUNCATE TABLE tb; +INSERT INTO tb (a) SELECT 789; +INSERT INTO tb (b) SELECT 789; +INSERT INTO tb (a) SELECT * FROM t_heap; +INSERT INTO tb (b) SELECT * FROM t_heap; +SELECT * FROM tb; + a | b | c +-----+-----+----------- + 789 | | pg_duckdb + 3 | 789 | pg_duckdb + 1 | | pg_duckdb + 3 | 1 | pg_duckdb +(4 rows) + +TRUNCATE TABLE tb; +INSERT INTO tb (c) SELECT 'ta'; +INSERT INTO tb (c) SELECT 'ta' || 'tb'; +INSERT INTO tb (a) SELECT (2)::numeric; +INSERT INTO tb (b) SELECT (3)::numeric; +INSERT INTO tb (c) SELECT t.a FROM (SELECT 'ta' || 'tb' AS a) t; +INSERT INTO tb (b, c) SELECT t.b, t.c FROM (SELECT (3)::numeric AS b, 'ta' || 'tb' AS c) t; +INSERT INTO tb (a, b, c) SELECT 1, 2, 'tb'; +INSERT INTO tb SELECT * FROM (SELECT (3)::numeric AS a, (3)::numeric AS b, 'ta' || 'tb' AS c) t; +SELECT * FROM tb; + a | b | c +---+---+----------- + 3 | | ta + 3 | | tatb + 2 | | pg_duckdb + 3 | 3 | pg_duckdb + 3 | | tatb + 3 | 3 | tatb + 1 | 2 | tb + 3 | 3 | tatb +(8 rows) + +CREATE TEMP TABLE tc (a int DEFAULT 3, b int, c varchar DEFAULT 'pg_duckdb', d varchar DEFAULT 'a' || 'b', e int DEFAULT 1 + 2) USING duckdb; +INSERT INTO tc (a) VALUES (123), (456); +INSERT INTO tc (b) VALUES (123), (456); +INSERT INTO tc (c) VALUES ('ta'), ('tb'); +SELECT * FROM tc; + a | b | c | d | e +-----+-----+-----------+----+--- + 123 | | pg_duckdb | ab | 3 + 456 | | pg_duckdb | ab | 3 + 3 | 123 | pg_duckdb | ab | 3 + 3 | 456 | pg_duckdb | ab | 3 + 3 | | ta | ab | 3 + 3 | | tb | ab | 3 +(6 rows) + +TRUNCATE TABLE tc; +INSERT INTO tc (a) SELECT 789; +INSERT INTO tc (b) SELECT 789; +INSERT INTO tc (a) SELECT * FROM t_heap; +INSERT INTO tc (b) SELECT * FROM t_heap; +SELECT * FROM tc; + a | b | c | d | e +-----+-----+-----------+----+--- + 789 | | pg_duckdb | ab | 3 + 3 | 789 | pg_duckdb | ab | 3 + 1 | | pg_duckdb | ab | 3 + 3 | 1 | pg_duckdb | ab | 3 +(4 rows) + +TRUNCATE TABLE tc; +INSERT INTO tc (c) SELECT 'ta'; +INSERT INTO tc (c) SELECT 'ta' || 'tb'; +INSERT INTO tc (a) SELECT (2)::numeric; +INSERT INTO tc (b) SELECT (3)::numeric; +INSERT INTO tc (c) SELECT t.a FROM (SELECT 'ta' || 'tb' AS a) t; +INSERT INTO tc (b, c) SELECT t.b, t.c FROM (SELECT (3)::numeric AS b, 'ta' || 'tb' AS c) t; +INSERT INTO tc (a, b, c) SELECT 1, 2, 'tb'; +INSERT INTO tc SELECT * FROM (SELECT (3)::numeric AS a, (3)::numeric AS b, 'ta' || 'tb' AS c) t; +SELECT * FROM tc; + a | b | c | d | e +---+---+-----------+----+--- + 3 | | ta | ab | 3 + 3 | | tatb | ab | 3 + 2 | | pg_duckdb | ab | 3 + 3 | 3 | pg_duckdb | ab | 3 + 3 | | tatb | ab | 3 + 3 | 3 | tatb | ab | 3 + 1 | 2 | tb | ab | 3 + 3 | 3 | tatb | ab | 3 +(8 rows) + +CREATE TEMP TABLE td (a int, ts timestamp default now()) USING duckdb; +INSERT INTO td (a) SELECT 1; +SELECT a FROM td; + a +--- + 1 +(1 row) + +DROP TABLE webpages, t, t_heap, t_heap2, ta, tb, tc, td; diff --git a/test/regression/sql/temporary_tables.sql b/test/regression/sql/temporary_tables.sql index 2c2b94a0..9515232c 100644 --- a/test/regression/sql/temporary_tables.sql +++ b/test/regression/sql/temporary_tables.sql @@ -164,4 +164,71 @@ SELECT * FROM t_heap2; SELECT duckdb.raw_query($$ SELECT database_name, schema_name, sql FROM duckdb_tables() $$); -DROP TABLE webpages, t, t_heap, t_heap2; +-- multi-VALUES +CREATE TEMP TABLE ta (a int DEFAULT 3, b int) USING duckdb; +INSERT INTO ta (b) VALUES (123), (456); +INSERT INTO ta (a, b) VALUES (123, 456), (456, 123); +SELECT * FROM ta; + +CREATE TEMP TABLE tb (a int DEFAULT 3, b int, c varchar DEFAULT 'pg_duckdb') USING duckdb; +INSERT INTO tb (a) VALUES (123), (456); +INSERT INTO tb (b) VALUES (123), (456); +INSERT INTO tb (c) VALUES ('ta'), ('tb'); +SELECT * FROM tb; + +-- INSERT ... SELECT +TRUNCATE TABLE ta; +INSERT INTO ta (a) SELECT 789; +INSERT INTO ta (b) SELECT 789; +INSERT INTO ta (a) SELECT * FROM t_heap; +INSERT INTO ta (b) SELECT * FROM t_heap; +SELECT * FROM ta; +INSERT INTO ta (a) SELECT generate_series(1, 3); -- no support + +TRUNCATE TABLE tb; +INSERT INTO tb (a) SELECT 789; +INSERT INTO tb (b) SELECT 789; +INSERT INTO tb (a) SELECT * FROM t_heap; +INSERT INTO tb (b) SELECT * FROM t_heap; +SELECT * FROM tb; + +TRUNCATE TABLE tb; +INSERT INTO tb (c) SELECT 'ta'; +INSERT INTO tb (c) SELECT 'ta' || 'tb'; +INSERT INTO tb (a) SELECT (2)::numeric; +INSERT INTO tb (b) SELECT (3)::numeric; +INSERT INTO tb (c) SELECT t.a FROM (SELECT 'ta' || 'tb' AS a) t; +INSERT INTO tb (b, c) SELECT t.b, t.c FROM (SELECT (3)::numeric AS b, 'ta' || 'tb' AS c) t; +INSERT INTO tb (a, b, c) SELECT 1, 2, 'tb'; +INSERT INTO tb SELECT * FROM (SELECT (3)::numeric AS a, (3)::numeric AS b, 'ta' || 'tb' AS c) t; +SELECT * FROM tb; + +CREATE TEMP TABLE tc (a int DEFAULT 3, b int, c varchar DEFAULT 'pg_duckdb', d varchar DEFAULT 'a' || 'b', e int DEFAULT 1 + 2) USING duckdb; +INSERT INTO tc (a) VALUES (123), (456); +INSERT INTO tc (b) VALUES (123), (456); +INSERT INTO tc (c) VALUES ('ta'), ('tb'); +SELECT * FROM tc; + +TRUNCATE TABLE tc; +INSERT INTO tc (a) SELECT 789; +INSERT INTO tc (b) SELECT 789; +INSERT INTO tc (a) SELECT * FROM t_heap; +INSERT INTO tc (b) SELECT * FROM t_heap; +SELECT * FROM tc; + +TRUNCATE TABLE tc; +INSERT INTO tc (c) SELECT 'ta'; +INSERT INTO tc (c) SELECT 'ta' || 'tb'; +INSERT INTO tc (a) SELECT (2)::numeric; +INSERT INTO tc (b) SELECT (3)::numeric; +INSERT INTO tc (c) SELECT t.a FROM (SELECT 'ta' || 'tb' AS a) t; +INSERT INTO tc (b, c) SELECT t.b, t.c FROM (SELECT (3)::numeric AS b, 'ta' || 'tb' AS c) t; +INSERT INTO tc (a, b, c) SELECT 1, 2, 'tb'; +INSERT INTO tc SELECT * FROM (SELECT (3)::numeric AS a, (3)::numeric AS b, 'ta' || 'tb' AS c) t; +SELECT * FROM tc; + +CREATE TEMP TABLE td (a int, ts timestamp default now()) USING duckdb; +INSERT INTO td (a) SELECT 1; +SELECT a FROM td; + +DROP TABLE webpages, t, t_heap, t_heap2, ta, tb, tc, td;