diff options
author | Pavan Deolasee | 2017-05-08 08:18:41 +0000 |
---|---|---|
committer | Pavan Deolasee | 2017-05-08 08:18:41 +0000 |
commit | 7b8dd5ed87768a589e8d62fdebff823fa2e73ff1 (patch) | |
tree | 702bd5a30d83176d42ca1c7023d3de86326a5674 | |
parent | e81eae5b0deca6f0ced3d12dfafed32f8b863a43 (diff) |
Use correct namespace while inserting rows via CTAS
We transform CREATE TABLE AS SELECT into a CREATE TABLE, followed by INSERT
INTO. But the generated INSERT INTO statement was not qualifying the table name
with schema, unless the original query has use qualified names. This results
into incorrect behaviour when tables are created in implicit schemas such
as "temporary" schemas. In passing also fix some places where we should be
quoting identifiers correctly.
Report, a test case and some initial analysis by Tomas Vondra. Patch and
further test cases by me.
Backpatched to XL9_5_STABLE
-rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 8 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 15 | ||||
-rw-r--r-- | src/test/regress/expected/xl_create_table.out | 76 | ||||
-rw-r--r-- | src/test/regress/parallel_schedule | 2 | ||||
-rw-r--r-- | src/test/regress/serial_schedule | 1 | ||||
-rw-r--r-- | src/test/regress/sql/xl_create_table.sql | 30 |
6 files changed, 120 insertions, 12 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 4815c35e58..4fd96d6a8c 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -4055,11 +4055,9 @@ QueryRewriteCTAS(Query *parsetree) /* Now, finally build the INSERT INTO statement */ initStringInfo(&cquery); - if (relation->schemaname) - appendStringInfo(&cquery, "INSERT INTO %s.%s", - relation->schemaname, relation->relname); - else - appendStringInfo(&cquery, "INSERT INTO %s", relation->relname); + appendStringInfo(&cquery, "INSERT INTO %s.%s", + quote_identifier(get_namespace_name(RangeVarGetCreationNamespace(relation))), + quote_identifier(relation->relname)); appendStringInfo(&cquery, " %s %s", selectstr, into->skipData ? "LIMIT 0" : ""); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index c3834f097e..66e7553e51 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -6329,8 +6329,8 @@ get_utility_query_def(Query *query, deparse_context *context) stmt->if_not_exists ? "IF NOT EXISTS " : ""); if (!istemp && relation->schemaname && relation->schemaname[0]) - appendStringInfo(buf, "%s.", relation->schemaname); - appendStringInfo(buf, "%s", relation->relname); + appendStringInfo(buf, "%s.", quote_identifier(relation->schemaname)); + appendStringInfo(buf, "%s", quote_identifier(relation->relname)); appendStringInfo(buf, "("); foreach(column, stmt->tableElts) @@ -6344,13 +6344,13 @@ get_utility_query_def(Query *query, deparse_context *context) { ColumnDef *coldef = (ColumnDef *) node; TypeName *typename = coldef->typeName; + Type type; #ifdef XCP appendStringInfo(buf, "%s %s", quote_identifier(coldef->colname), format_type_with_typemod(typename->typeOid, typename->typemod)); #else - Type type; /* error out if we have no recourse at all */ if (!OidIsValid(typename->typeOid)) @@ -6433,7 +6433,8 @@ get_utility_query_def(Query *query, deparse_context *context) break; case DISTTYPE_MODULO: - appendStringInfo(buf, " DISTRIBUTE BY MODULO(%s)", stmt->distributeby->colname); + appendStringInfo(buf, " DISTRIBUTE BY MODULO(%s)", + quote_identifier(stmt->distributeby->colname)); break; default: @@ -6456,7 +6457,8 @@ get_utility_query_def(Query *query, deparse_context *context) Assert(stmt->subcluster->members); foreach(cell, stmt->subcluster->members) { - appendStringInfo(buf, " %s", strVal(lfirst(cell))); + appendStringInfo(buf, " %s", + quote_identifier(strVal(lfirst(cell)))); if (cell->next) appendStringInfo(buf, ","); } @@ -6470,7 +6472,8 @@ get_utility_query_def(Query *query, deparse_context *context) Assert(stmt->subcluster->members); foreach(cell, stmt->subcluster->members) { - appendStringInfo(buf, " %s", strVal(lfirst(cell))); + appendStringInfo(buf, " %s", + quote_identifier(strVal(lfirst(cell)))); if (cell->next) appendStringInfo(buf, ","); } diff --git a/src/test/regress/expected/xl_create_table.out b/src/test/regress/expected/xl_create_table.out new file mode 100644 index 0000000000..c409129f0a --- /dev/null +++ b/src/test/regress/expected/xl_create_table.out @@ -0,0 +1,76 @@ +-- check that CTAS inserts rows into correct table +CREATE TABLE ctas_t1 AS SELECT 1; +CREATE TEMP TABLE ctas_t1 AS SELECT 2; +SELECT * FROM ctas_t1; + ?column? +---------- + 2 +(1 row) + +SELECT * FROM public.ctas_t1; + ?column? +---------- + 1 +(1 row) + +SELECT * FROM pg_temp.ctas_t1; + ?column? +---------- + 2 +(1 row) + +-- this drops the temp table +DROP TABLE ctas_t1; +-- also drop the public table +DROP TABLE ctas_t1; +-- same tests with special table name +CREATE TABLE "ctas t1" AS SELECT 1; +CREATE TEMP TABLE "ctas t1" AS SELECT 2; +SELECT * FROM "ctas t1"; + ?column? +---------- + 2 +(1 row) + +SELECT * FROM public."ctas t1"; + ?column? +---------- + 1 +(1 row) + +SELECT * FROM pg_temp."ctas t1"; + ?column? +---------- + 2 +(1 row) + +DROP TABLE "ctas t1"; +DROP TABLE "ctas t1"; +CREATE SCHEMA "ctas schema"; +SET search_path TO "ctas schema"; +CREATE TABLE "ctas t1" AS SELECT 1; +CREATE TEMP TABLE "ctas t1" AS SELECT 2; +SELECT * FROM "ctas t1"; + ?column? +---------- + 2 +(1 row) + +SELECT * FROM public."ctas t1"; +ERROR: relation "public.ctas t1" does not exist +LINE 1: SELECT * FROM public."ctas t1"; + ^ +SELECT * FROM "ctas schema"."ctas t1"; + ?column? +---------- + 1 +(1 row) + +SELECT * FROM pg_temp."ctas t1"; + ?column? +---------- + 2 +(1 row) + +DROP TABLE "ctas t1"; +DROP TABLE "ctas t1"; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index ff7c8cac2e..d1a33b97a6 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -139,4 +139,4 @@ test: xc_prepared_xacts test: xc_notrans_block # This runs XL specific tests -test: xl_primary_key xl_foreign_key xl_distribution_column_types xl_alter_table xl_distribution_column_types_modulo xl_plan_pushdown xl_functions xl_limitations xl_user_defined_functions xl_join xl_distributed_xact +test: xl_primary_key xl_foreign_key xl_distribution_column_types xl_alter_table xl_distribution_column_types_modulo xl_plan_pushdown xl_functions xl_limitations xl_user_defined_functions xl_join xl_distributed_xact xl_create_table diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index e4ced6c15f..c08abd1a3d 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -196,3 +196,4 @@ test: xl_limitations test: xl_user_defined_functions test: xl_join test: xl_distributed_xact +test: xl_create_table diff --git a/src/test/regress/sql/xl_create_table.sql b/src/test/regress/sql/xl_create_table.sql new file mode 100644 index 0000000000..a790b8d433 --- /dev/null +++ b/src/test/regress/sql/xl_create_table.sql @@ -0,0 +1,30 @@ +-- check that CTAS inserts rows into correct table +CREATE TABLE ctas_t1 AS SELECT 1; +CREATE TEMP TABLE ctas_t1 AS SELECT 2; +SELECT * FROM ctas_t1; +SELECT * FROM public.ctas_t1; +SELECT * FROM pg_temp.ctas_t1; +-- this drops the temp table +DROP TABLE ctas_t1; +-- also drop the public table +DROP TABLE ctas_t1; + +-- same tests with special table name +CREATE TABLE "ctas t1" AS SELECT 1; +CREATE TEMP TABLE "ctas t1" AS SELECT 2; +SELECT * FROM "ctas t1"; +SELECT * FROM public."ctas t1"; +SELECT * FROM pg_temp."ctas t1"; +DROP TABLE "ctas t1"; +DROP TABLE "ctas t1"; + +CREATE SCHEMA "ctas schema"; +SET search_path TO "ctas schema"; +CREATE TABLE "ctas t1" AS SELECT 1; +CREATE TEMP TABLE "ctas t1" AS SELECT 2; +SELECT * FROM "ctas t1"; +SELECT * FROM public."ctas t1"; +SELECT * FROM "ctas schema"."ctas t1"; +SELECT * FROM pg_temp."ctas t1"; +DROP TABLE "ctas t1"; +DROP TABLE "ctas t1"; |