From 4c0a495a1815cd027de41a9e5189b2aee01b9fd8 Mon Sep 17 00:00:00 2001 From: luozihao <1165977584@qq.com> Date: Fri, 29 Apr 2022 17:10:28 +0800 Subject: [PATCH 1/2] fixed the bug of join --- .../optimizer/prep/prepjointree.cpp | 11 ++++ src/test/regress/expected/join_test_alias.out | 55 +++++++++++++++++++ src/test/regress/parallel_schedule0 | 1 + src/test/regress/sql/join_test_alias.sql | 53 ++++++++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 src/test/regress/expected/join_test_alias.out create mode 100644 src/test/regress/sql/join_test_alias.sql diff --git a/src/gausskernel/optimizer/prep/prepjointree.cpp b/src/gausskernel/optimizer/prep/prepjointree.cpp index ff6b31939..2187138e8 100755 --- a/src/gausskernel/optimizer/prep/prepjointree.cpp +++ b/src/gausskernel/optimizer/prep/prepjointree.cpp @@ -1181,6 +1181,17 @@ static Node* pull_up_simple_subquery(PlannerInfo* root, Node* jtnode, RangeTblEn return jtnode; } + /* + * We must flatten any join alias Vars in the subquery's targetlist, + * because pulling up the subquery's subqueries might have changed their + * expansions into arbitrary expressions, which could affect + * pullup_replace_vars' decisions about whether PlaceHolderVar wrappers + * are needed for tlist entries. (Likely it'd be better to do + * flatten_join_alias_vars on the whole query tree at some earlier stage, + * maybe even in the rewriter; but for now let's just fix this case here.) + */ + subquery->targetList = (List *) flatten_join_alias_vars(subroot, (Node *) subquery->targetList); + /* * Adjust level-0 varnos in subquery so that we can append its rangetable * to upper query's. We have to fix the subquery's append_rel_list as diff --git a/src/test/regress/expected/join_test_alias.out b/src/test/regress/expected/join_test_alias.out new file mode 100644 index 000000000..da5494779 --- /dev/null +++ b/src/test/regress/expected/join_test_alias.out @@ -0,0 +1,55 @@ +CREATE TABLE t11 ( +sec_code character(6) NOT NULL, +issue_type character(3) NOT NULL +); +CREATE TABLE t22 ( +company_code character(6) NOT NULL, +list_profit_flag character(1) NOT NULL +); +CREATE TABLE t33 ( +company_code character(6) NOT NULL, +issue_flag character(6) NOT NULL +); +insert into t11 values(1,'S04'); +insert into t22 values(1,'Y'); +insert into t33 values(1,'1'); +insert into t33 values(2,'2'); +select +T5.issue_type +,T6.is_type +from +( +select T1.sec_code +,issue_type +--,case when list_profit_flag='Y' then '是' else '否' end as list_profit_flag +from +( +select sec_code +,case when issue_type in ('S04','S05','S06','S09') then '增发' +when issue_type in ('S01','S02') then '首发' +else '其他' end as issue_type +from t11 +)T1 +left join +( +select list_profit_flag +,company_code +from t22 +)T2 +on T1.sec_code=T2.company_code +)T5 +full join +( +SELECT CASE +WHEN issue_flag = ANY (ARRAY['1'::bpchar, '3'::bpchar, '11'::bpchar]) THEN '首发'::text +ELSE '增发'::text +END AS is_type from t33 +)T6 +on T5.issue_type=T6.is_type +; + issue_type | is_type +------------+--------- + 增发 | 增发 + | 首发 +(2 rows) + diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 52dc78697..67b1c3fc6 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -916,3 +916,4 @@ test: fdw_audit test: gs_global_config_audit test: detail test: gs_dump_encrypt +test: join_test_alias diff --git a/src/test/regress/sql/join_test_alias.sql b/src/test/regress/sql/join_test_alias.sql new file mode 100644 index 000000000..e57761476 --- /dev/null +++ b/src/test/regress/sql/join_test_alias.sql @@ -0,0 +1,53 @@ +CREATE TABLE t11 ( +sec_code character(6) NOT NULL, +issue_type character(3) NOT NULL +); + +CREATE TABLE t22 ( +company_code character(6) NOT NULL, +list_profit_flag character(1) NOT NULL +); + +CREATE TABLE t33 ( +company_code character(6) NOT NULL, +issue_flag character(6) NOT NULL +); + +insert into t11 values(1,'S04'); +insert into t22 values(1,'Y'); +insert into t33 values(1,'1'); +insert into t33 values(2,'2'); + +select +T5.issue_type +,T6.is_type +from +( +select T1.sec_code +,issue_type +--,case when list_profit_flag='Y' then '是' else '否' end as list_profit_flag +from +( +select sec_code +,case when issue_type in ('S04','S05','S06','S09') then '增发' +when issue_type in ('S01','S02') then '首发' +else '其他' end as issue_type +from t11 +)T1 +left join +( +select list_profit_flag +,company_code +from t22 +)T2 +on T1.sec_code=T2.company_code +)T5 +full join +( +SELECT CASE +WHEN issue_flag = ANY (ARRAY['1'::bpchar, '3'::bpchar, '11'::bpchar]) THEN '首发'::text +ELSE '增发'::text +END AS is_type from t33 +)T6 +on T5.issue_type=T6.is_type +; \ No newline at end of file From c1335bebc9fac2bb66464258b85f8bcb2b296c46 Mon Sep 17 00:00:00 2001 From: TotaJ Date: Fri, 13 May 2022 14:55:39 +0800 Subject: [PATCH 2/2] Fix deserialize func and view. --- src/common/backend/nodes/readfuncs.cpp | 84 +++++++++---- src/common/backend/utils/cache/lsyscache.cpp | 7 +- src/include/utils/lsyscache.h | 2 +- .../regress/expected/deserialize_func.out | 116 ++++++++++++++++++ src/test/regress/parallel_schedule0 | 2 +- src/test/regress/pg_regress.cpp | 2 +- src/test/regress/sql/deserialize_func.sql | 63 ++++++++++ 7 files changed, 245 insertions(+), 31 deletions(-) create mode 100644 src/test/regress/expected/deserialize_func.out create mode 100644 src/test/regress/sql/deserialize_func.sql diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp index e16993a4b..fa90dc7c5 100755 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -70,6 +70,7 @@ THR_LOCAL bool skip_read_extern_fields = false; +#define IS_DATANODE_BUT_NOT_SINGLENODE (IS_PGXC_DATANODE && !IS_SINGLE_NODE) /* * Macros to simplify reading of different kinds of fields. Use these * wherever possible to reduce the chance for silly typos. Note that these @@ -401,24 +402,27 @@ THR_LOCAL bool skip_read_extern_fields = false; token = pg_strtok(&length); /* skip :fldname */ \ local_node->fldname = _readBitmapset() -#define READ_TYPEINFO_FIELD(fldname) \ - do { \ - if (local_node->fldname >= FirstBootstrapObjectId) { \ - IF_EXIST(exprtypename) \ - { \ - char* exprtypename = NULL; \ - char* exprtypenamespace = NULL; \ - token = pg_strtok(&length); \ - token = pg_strtok(&length); \ - exprtypename = nullable_string(token, length); \ - token = pg_strtok(&length); \ - token = pg_strtok(&length); \ - exprtypenamespace = nullable_string(token, length); \ - local_node->fldname = get_typeoid(get_namespace_oid(exprtypenamespace, false), exprtypename); \ - pfree_ext(exprtypename); \ - pfree_ext(exprtypenamespace); \ - } \ - } \ +#define READ_TYPEINFO_FIELD(fldname) \ + do { \ + if (local_node->fldname >= FirstBootstrapObjectId) { \ + IF_EXIST(exprtypename) \ + { \ + char* exprtypename = NULL; \ + char* exprtypenamespace = NULL; \ + token = pg_strtok(&length); \ + token = pg_strtok(&length); \ + exprtypename = nullable_string(token, length); \ + token = pg_strtok(&length); \ + token = pg_strtok(&length); \ + exprtypenamespace = nullable_string(token, length); \ + /* No need to reset field on CN or singlenode, keep pg_strtok() for forward compatibility */ \ + if (IS_DATANODE_BUT_NOT_SINGLENODE) { \ + local_node->fldname = get_typeoid(get_namespace_oid(exprtypenamespace, false), exprtypename); \ + } \ + pfree_ext(exprtypename); \ + pfree_ext(exprtypenamespace); \ + } \ + } \ } while (0) #define READ_TYPEINFO(typePtr) \ @@ -493,9 +497,30 @@ THR_LOCAL bool skip_read_extern_fields = false; token = pg_strtok(&length); \ token = pg_strtok(&length); \ funcnamespace = nullable_string(token, length); \ - if (IS_PGXC_DATANODE && !skip_read_extern_fields) { \ - local_node->fldname = \ - get_func_oid(funcname, get_namespace_oid(funcnamespace, false), (Expr*)local_node); \ + bool notfound = false; \ + if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) { \ + Oid funcoid = InvalidOid; \ + do { \ + Oid nspid = get_namespace_oid(funcnamespace, true); \ + if (!OidIsValid(nspid)) { \ + notfound = true; \ + break; \ + } \ + funcoid = get_func_oid(funcname, nspid, (Expr*)local_node); \ + } while (0); \ + if (notfound || !OidIsValid(funcoid)) { \ + ereport(ERROR, \ + (errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_OBJECT), \ + errmsg("Cannot identify function %s.%s while deserializing field.", \ + funcname, funcnamespace), \ + errdetail("Function with oid %u or its namespace may be renamed", \ + local_node->fldname), \ + errhint("Please rebuild column defalt expression, views etc. that are" \ + " related to this renamed object."), \ + errcause("Object renamed after recorded as nodetree."), \ + erraction("Rebuild relevant object."))); \ + } \ + local_node->fldname = funcoid; \ } \ pfree_ext(funcname); \ pfree_ext(funcnamespace); \ @@ -525,7 +550,7 @@ THR_LOCAL bool skip_read_extern_fields = false; token = pg_strtok(&length); \ token = pg_strtok(&length); \ oprrightname = nullable_string(token, length); \ - if (IS_PGXC_DATANODE) { \ + if (IS_DATANODE_BUT_NOT_SINGLENODE) { \ namespaceId = get_namespace_oid(opnamespace, false); \ oprleft = get_typeoid(namespaceId, oprleftname); \ oprright = oprleft; \ @@ -568,7 +593,7 @@ THR_LOCAL bool skip_read_extern_fields = false; token = pg_strtok(&length); \ token = pg_strtok(&length); \ oprrightname = nullable_string(token, length); \ - if (IS_PGXC_DATANODE) { \ + if (IS_DATANODE_BUT_NOT_SINGLENODE) { \ namespaceId = get_namespace_oid(opnamespace, false); \ oprleft = get_typeoid(namespaceId, oprleftname); \ oprright = oprleft; \ @@ -2126,14 +2151,21 @@ static FuncExpr* _readFuncExpr(void) ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("NULL seqNamespace for nextval()"))); } - if (!IS_PGXC_COORDINATOR && !skip_read_extern_fields) { - Oid seqid = get_valid_relname_relid(seqNamespace, seqName); + if (IS_DATANODE_BUT_NOT_SINGLENODE && !skip_read_extern_fields) { + Oid seqid = get_valid_relname_relid(seqNamespace, seqName, true); + Const* firstArg = (Const*)linitial(local_node->args); if (OidIsValid(seqid)) { - Const* firstArg = (Const*)linitial(local_node->args); if (firstArg != NULL) { firstArg->constvalue = ObjectIdGetDatum(seqid); } + } else { + ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Cannot identify sequence %s.%s while deserializing field.", seqNamespace, seqName), + errdetail("Sequence with oid %u or its namespace may be renamed", + DatumGetObjectId(firstArg->constvalue)), + errhint("Please rebuild column defalt expression, views etc. that are related to this sequence"), + errcause("Object renamed after recorded as nodetree."), erraction("Rebuild relevant object."))); } } pfree_ext(seqName); diff --git a/src/common/backend/utils/cache/lsyscache.cpp b/src/common/backend/utils/cache/lsyscache.cpp index 4485b621b..20d59cbaa 100644 --- a/src/common/backend/utils/cache/lsyscache.cpp +++ b/src/common/backend/utils/cache/lsyscache.cpp @@ -1723,7 +1723,7 @@ char* get_relname_relid_extend( extern bool StreamTopConsumerAmI(); /* same as get_relname_relid except we check for cache invalidation here */ -Oid get_valid_relname_relid(const char* relnamespace, const char* relname) +Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool nsp_missing_ok) { Oid nspid = InvalidOid; Oid oldnspid = InvalidOid; @@ -1747,7 +1747,10 @@ Oid get_valid_relname_relid(const char* relnamespace, const char* relname) if (EnableLocalSysCache()) { thrd_inval_count = t_thrd.lsc_cxt.lsc->inval_cxt.SIMCounter; } - nspid = get_namespace_oid(relnamespace, false); + nspid = get_namespace_oid(relnamespace, nsp_missing_ok); + if (!OidIsValid(nspid)) { + return InvalidOid; + } relid = get_relname_relid(relname, nspid); /* * In bootstrap processing mode, we don't bother with locking diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 8bb68826b..197254789 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -213,7 +213,7 @@ extern bool check_rel_is_partitioned(Oid relid); extern Oid partid_get_parentid(Oid partid); extern bool is_not_strict_agg(Oid funcOid); extern bool is_pgxc_class_table(Oid tableoid); -extern Oid get_valid_relname_relid(const char* relnamespace, const char* relname); +extern Oid get_valid_relname_relid(const char* relnamespace, const char* relname, bool nsp_missing_ok = false); extern bool get_func_iswindow(Oid funcid); extern char get_func_prokind(Oid funcid); extern char get_typecategory(Oid typid); diff --git a/src/test/regress/expected/deserialize_func.out b/src/test/regress/expected/deserialize_func.out new file mode 100644 index 000000000..a627a99de --- /dev/null +++ b/src/test/regress/expected/deserialize_func.out @@ -0,0 +1,116 @@ +create schema deserialize_func1; +set current_schema = deserialize_func1; +create sequence test_seq; +create or replace function test_func() return int +as +begin +return 1; +end; +/ +create table t1(key int, id int default nextval('test_seq')); +create table t2(key int, id int default test_func()); +select * from pg_get_tabledef('deserialize_func1.t1'); + pg_get_tabledef +------------------------------------------------------ + SET search_path = deserialize_func1; + + CREATE TABLE t1 ( + + key integer, + + id integer DEFAULT nextval('test_seq'::regclass)+ + ) + + WITH (orientation=row, compression=no); +(1 row) + +select * from pg_get_tabledef('deserialize_func1.t2'); + pg_get_tabledef +----------------------------------------- + SET search_path = deserialize_func1; + + CREATE TABLE t2 ( + + key integer, + + id integer DEFAULT test_func() + + ) + + WITH (orientation=row, compression=no); +(1 row) + +alter schema deserialize_func1 rename to deserialize_func2; +select * from pg_get_tabledef('deserialize_func2.t1'); + pg_get_tabledef +------------------------------------------------------------------------ + SET search_path = deserialize_func2; + + CREATE TABLE t1 ( + + key integer, + + id integer DEFAULT nextval('deserialize_func2.test_seq'::regclass)+ + ) + + WITH (orientation=row, compression=no); +(1 row) + +select * from pg_get_tabledef('deserialize_func2.t2'); + pg_get_tabledef +------------------------------------------------------ + SET search_path = deserialize_func2; + + CREATE TABLE t2 ( + + key integer, + + id integer DEFAULT deserialize_func2.test_func()+ + ) + + WITH (orientation=row, compression=no); +(1 row) + +alter table deserialize_func2.t1 alter column id drop default; +alter table deserialize_func2.t1 alter column id set default nextval('deserialize_func2.test_seq'); +select * from pg_get_tabledef('deserialize_func2.t1'); + pg_get_tabledef +------------------------------------------------------------------------ + SET search_path = deserialize_func2; + + CREATE TABLE t1 ( + + key integer, + + id integer DEFAULT nextval('deserialize_func2.test_seq'::regclass)+ + ) + + WITH (orientation=row, compression=no); +(1 row) + +alter table deserialize_func2.t2 alter column id drop default; +alter table deserialize_func2.t2 alter column id set default deserialize_func2.test_func(); +select * from pg_get_tabledef('deserialize_func2.t2'); + pg_get_tabledef +------------------------------------------------------ + SET search_path = deserialize_func2; + + CREATE TABLE t2 ( + + key integer, + + id integer DEFAULT deserialize_func2.test_func()+ + ) + + WITH (orientation=row, compression=no); +(1 row) + +set current_schema = deserialize_func2; +create type type1 as (a int); +create view v1 as select '(1)'::type1; +alter type type1 rename to type2; +select pg_get_viewdef('v1'); + pg_get_viewdef +------------------------------- + SELECT '(1)'::type2 AS type1; +(1 row) + +drop view v1 cascade; +create table atest12 as select x as a, 10001-x as b from generate_series(1, 10000) x; +create function func1(integer, integer) returns boolean as $$begin return $1 < $2; end $$ language plpgsql immutable; +create operator <<< (procedure=func1, leftarg=integer, rightarg=integer, restrict=scalarltsel); +create view atest12v as select * from atest12 where b <<< 5; +select pg_get_viewdef('atest12v'); + pg_get_viewdef +------------------------------------------------- + SELECT * FROM atest12 WHERE (atest12.b <<< 5); +(1 row) + +drop schema if exists deserialize_func1 cascade; +NOTICE: schema "deserialize_func1" does not exist, skipping +drop schema if exists deserialize_func2 cascade; +NOTICE: drop cascades to 9 other objects +DETAIL: drop cascades to sequence test_seq +drop cascades to function test_func() +drop cascades to table t1 +drop cascades to table t2 +drop cascades to type type2 +drop cascades to table atest12 +drop cascades to function func1(integer,integer) +drop cascades to operator <<<(integer,integer) +drop cascades to view atest12v diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 52dc78697..e2086c3d8 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -25,7 +25,7 @@ test: pldeveloper_gs_source test: index_advisor test: pl_debugger_server pl_debugger_client test: update_for_wait_s1 update_for_wait_s2 -test: plan_hint plan_hint_set plan_hint_no_expand plan_hint_iud null_test_opt +test: plan_hint plan_hint_set plan_hint_no_expand plan_hint_iud null_test_opt deserialize_func test: large_sequence int16 gs_dump_sequence test: gs_dump_tableof test: analyze_commands diff --git a/src/test/regress/pg_regress.cpp b/src/test/regress/pg_regress.cpp index e1fa1c1b0..6c3320c3c 100644 --- a/src/test/regress/pg_regress.cpp +++ b/src/test/regress/pg_regress.cpp @@ -5238,7 +5238,7 @@ static void check_global_variables() } } -#define BASE_PGXC_LIKE_MACRO_NUM 1401 +#define BASE_PGXC_LIKE_MACRO_NUM 1402 static void check_pgxc_like_macros() { #ifdef BUILD_BY_CMAKE diff --git a/src/test/regress/sql/deserialize_func.sql b/src/test/regress/sql/deserialize_func.sql new file mode 100644 index 000000000..cec56cbbb --- /dev/null +++ b/src/test/regress/sql/deserialize_func.sql @@ -0,0 +1,63 @@ +create schema deserialize_func1; +set current_schema = deserialize_func1; + +create sequence test_seq; + +create or replace function test_func() return int +as +begin +return 1; +end; +/ + +create table t1(key int, id int default nextval('test_seq')); + +create table t2(key int, id int default test_func()); + +select * from pg_get_tabledef('deserialize_func1.t1'); + +select * from pg_get_tabledef('deserialize_func1.t2'); + +alter schema deserialize_func1 rename to deserialize_func2; + +select * from pg_get_tabledef('deserialize_func2.t1'); + +select * from pg_get_tabledef('deserialize_func2.t2'); + +alter table deserialize_func2.t1 alter column id drop default; + +alter table deserialize_func2.t1 alter column id set default nextval('deserialize_func2.test_seq'); + +select * from pg_get_tabledef('deserialize_func2.t1'); + +alter table deserialize_func2.t2 alter column id drop default; + +alter table deserialize_func2.t2 alter column id set default deserialize_func2.test_func(); + +select * from pg_get_tabledef('deserialize_func2.t2'); + +set current_schema = deserialize_func2; + +create type type1 as (a int); + +create view v1 as select '(1)'::type1; + +alter type type1 rename to type2; + +select pg_get_viewdef('v1'); + +drop view v1 cascade; + +create table atest12 as select x as a, 10001-x as b from generate_series(1, 10000) x; + +create function func1(integer, integer) returns boolean as $$begin return $1 < $2; end $$ language plpgsql immutable; + +create operator <<< (procedure=func1, leftarg=integer, rightarg=integer, restrict=scalarltsel); + +create view atest12v as select * from atest12 where b <<< 5; + +select pg_get_viewdef('atest12v'); + +drop schema if exists deserialize_func1 cascade; + +drop schema if exists deserialize_func2 cascade; \ No newline at end of file