From ce8a0a5ca2ca24b89efe70a46b5f35be8a25d950 Mon Sep 17 00:00:00 2001 From: chenbd Date: Wed, 24 May 2023 16:09:20 +0800 Subject: [PATCH 1/3] dolphin ignore index request --- doc/src/sgml/ref/select.sgmlin | 2 +- src/common/backend/parser/gram.y | 9 +- src/common/backend/parser/parser.cpp | 28 +- src/gausskernel/optimizer/util/pathnode.cpp | 44 +- src/include/nodes/parsenodes_common.h | 3 +- src/include/optimizer/pathnode.h | 3 + src/test/regress/expected/mysql_indexhint.out | 410 ++++++++++++++++++ src/test/regress/sql/mysql_indexhint.sql | 206 +++++++++ 8 files changed, 687 insertions(+), 18 deletions(-) diff --git a/doc/src/sgml/ref/select.sgmlin b/doc/src/sgml/ref/select.sgmlin index e57abfa475..91a3808c20 100644 --- a/doc/src/sgml/ref/select.sgmlin +++ b/doc/src/sgml/ref/select.sgmlin @@ -71,7 +71,7 @@ SUBPARTITION { ( subpartition_name ) | FOR ( subpartition_value [, ...] )} where nlssort_expression_clause can be: NLSSORT ( column_name, ' NLS_SORT = { SCHINESE_PINYIN_M | generic_m_ci } ' ) where index_hints can be: -FORCE { INDEX | KEY } ( index_name [, ...] ) +{ FORCE | IGNORE } { INDEX | KEY } ( index_name [, ...] ) | USE { INDEX | KEY } ( [ index_name [, ...] ] ) NOTICE: [into_option] is only available in CENTRALIZED mode and B-format database. NOTICE: [index_hints] is only available in CENTRALIZED mode and B-format database. diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index bf6b1e1512..d8484d061b 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -969,7 +969,7 @@ static void setDelimiterName(core_yyscan_t yyscanner, char*input, VariableSetStm END_OF_PROC EVENT_TRIGGER NOT_IN NOT_BETWEEN NOT_LIKE NOT_ILIKE NOT_SIMILAR - FORCE_INDEX USE_INDEX + FORCE_INDEX USE_INDEX IGNORE_INDEX /* Precedence: lowest to highest */ %nonassoc COMMENT @@ -14416,6 +14416,13 @@ index_hint_definition: n->indexnames = $3; $$ = (Node*)n; } + | IGNORE_INDEX '(' key_usage_list ')' + { + IndexHintDefinition* n = makeNode(IndexHintDefinition); + n->index_type = INDEX_HINT_IGNORE; + n->indexnames = $3; + $$ = (Node*)n; + } ; index_hint_list: diff --git a/src/common/backend/parser/parser.cpp b/src/common/backend/parser/parser.cpp index a8b8ce44da..4b7ff68994 100644 --- a/src/common/backend/parser/parser.cpp +++ b/src/common/backend/parser/parser.cpp @@ -619,8 +619,8 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) break; case USE_P: /* - * USE INDEX \USE KEY must be reduced to one token,to allow KEY\USE as table / column alias. - */ + * USE INDEX \USE KEY must be reduced to one token,to allow KEY\USE as table / column alias. + */ GET_NEXT_TOKEN(); switch (next_token) { @@ -641,8 +641,8 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) break; case FORCE: /* - * FORCE INDEX \FORCE KEY must be reduced to one token,to allow KEY\FORCE as table / column alias. - */ + * FORCE INDEX \FORCE KEY must be reduced to one token,to allow KEY\FORCE as table / column alias. + */ GET_NEXT_TOKEN(); switch (next_token) { @@ -661,6 +661,26 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) break; } break; + case IGNORE: + /* + * IGNORE INDEX \IGNORE KEY must be reduced to one token,to allow KEY\IGNORE as table / column alias. + */ + GET_NEXT_TOKEN(); + + switch (next_token) { + case KEY: + case INDEX: + cur_token = IGNORE_INDEX; + break; + default: + /* save the lookahead token for next time */ + SET_LOOKAHEAD_TOKEN(); + /* and back up the output info to cur_token */ + lvalp->core_yystype = cur_yylval; + *llocp = cur_yylloc; + break; + } + break; default: break; } diff --git a/src/gausskernel/optimizer/util/pathnode.cpp b/src/gausskernel/optimizer/util/pathnode.cpp index afa419a29d..a8987b560e 100755 --- a/src/gausskernel/optimizer/util/pathnode.cpp +++ b/src/gausskernel/optimizer/util/pathnode.cpp @@ -1362,18 +1362,28 @@ void set_hint_value(RelOptInfo* join_rel, Path* new_path, HintState* hstate) } } -bool find_index_hint_value(List* indexhintList, Oid pathindexOid, bool* isUse) +/* + * @Description: find index_hint to this new path. + * Level of the ignore index is the highest + */ +bool find_index_hint_value(List* indexhintList, Oid pathindexOid, short* hintMask) { ListCell* lc = NULL; Oid indexOid; + bool result = false; foreach(lc, indexhintList) { indexOid = ((IndexHintRelationData*)lfirst(lc))->indexOid; - *isUse = (((IndexHintRelationData*)lfirst(lc))->index_type == INDEX_HINT_USE); + if (((IndexHintRelationData*)lfirst(lc))->index_type == INDEX_HINT_USE) { + *hintMask |= HINT_MATCH_USE; + } if (pathindexOid == indexOid) { - return true; + if (((IndexHintRelationData*)lfirst(lc))->index_type == INDEX_HINT_IGNORE) { + *hintMask |= HINT_MATCH_IGNORE; + } + result = true; } } - return false; + return result; } void set_index_hint_value(Path* new_path, List* indexhintList) @@ -1381,8 +1391,9 @@ void set_index_hint_value(Path* new_path, List* indexhintList) if (indexhintList == NULL) return ; IndexPath* index_path = (IndexPath*)new_path; - bool useIndex = false; - bool hintUse = false; + bool matchIndex = false; + bool isIndexScan = false; + short hintMask = 0; switch (new_path->pathtype) { case T_SeqScan: @@ -1392,21 +1403,32 @@ void set_index_hint_value(Path* new_path, List* indexhintList) #endif /* ENABLE_MULTIPLE_NODES */ case T_SubqueryScan: case T_ForeignScan: { - useIndex = find_index_hint_value(indexhintList, InvalidOid, &hintUse); + matchIndex = find_index_hint_value(indexhintList, InvalidOid, &hintMask); break; } case T_IndexScan: case T_IndexOnlyScan: { - useIndex = find_index_hint_value(indexhintList, index_path->indexinfo->indexoid, &hintUse); + isIndexScan = true; + matchIndex = find_index_hint_value(indexhintList, index_path->indexinfo->indexoid, &hintMask); break; } default: break; } - if (useIndex) - new_path->hint_value++; - else if (hintUse) + if (matchIndex) { + if ((hintMask & HINT_MATCH_IGNORE) > 0) { + /* ignore index is the top level concerned any hint shoule be reset to zero */ + if (new_path->hint_value > 0) { + new_path->hint_value = 0; + } + new_path->hint_value--; + } else { + new_path->hint_value++; + } + } else if ((hintMask & HINT_MATCH_USE) > 0 && !isIndexScan) { + /* use index should both add seqscan and when matched*/ new_path->hint_value++; + } return ; } diff --git a/src/include/nodes/parsenodes_common.h b/src/include/nodes/parsenodes_common.h index 90fd738f47..681ba3c652 100644 --- a/src/include/nodes/parsenodes_common.h +++ b/src/include/nodes/parsenodes_common.h @@ -2465,7 +2465,8 @@ typedef struct AutoIncrement { } AutoIncrement; typedef enum IndexHintType { - INDEX_HINT_USE =1, + INDEX_HINT_IGNORE = 0, + INDEX_HINT_USE = 1, INDEX_HINT_FORCE, INDEX_HINT_MIX, INDEX_HINT_NOT_EXISTS diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 6ff21d6003..8cce2273ad 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -24,6 +24,9 @@ #define FUZZY_FACTOR 1.01 #define SMALL_FUZZY_FACTOR 1.0000000001 +/* micros used in index hints check*/ +#define HINT_MATCH_USE 1 +#define HINT_MATCH_IGNORE 2 typedef enum { COSTS_EQUAL, /* path costs are fuzzily equal */ COSTS_BETTER1, /* first path is cheaper than second */ diff --git a/src/test/regress/expected/mysql_indexhint.out b/src/test/regress/expected/mysql_indexhint.out index 040b48621e..96363d2511 100644 --- a/src/test/regress/expected/mysql_indexhint.out +++ b/src/test/regress/expected/mysql_indexhint.out @@ -454,6 +454,416 @@ explain (costs off)select user_no from list_list subpartition for (100,4) use i Selected Subpartitions: 1:1 (6 rows) +--ignore index test +create table db_1130449_tb (col1 int ,col2 int,col3 int,col4 varchar(10),primary key(col1)); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "db_1130449_tb_pkey" for table "db_1130449_tb" +insert into db_1130449_tb values(1,1,1,'a'); +insert into db_1130449_tb values(2,2,2,'a'); +insert into db_1130449_tb values(3,3,2,'a'); +insert into db_1130449_tb values(4,4,3,'b'); +insert into db_1130449_tb values(5,5,3,'b'); +insert into db_1130449_tb values(6,6,6,'b'); +insert into db_1130449_tb values(7,7,7,'a'); +insert into db_1130449_tb values(8,8,8,'c'); +insert into db_1130449_tb values(9,9,9,'c'); +insert into db_1130449_tb values(10,null,1,'c'); +create unique index index_1130449 on db_1130449_tb (col2); +analyze db_1130449_tb; +select * from db_1130449_tb ignore index (index_1130449) where col2= 3; + col1 | col2 | col3 | col4 +------+------+------+------ + 3 | 3 | 2 | a +(1 row) + +select * from db_1130449_tb ignore index (index_1130449) where col2= 3 and col4 = 'a'; + col1 | col2 | col3 | col4 +------+------+------+------ + 3 | 3 | 2 | a +(1 row) + +select * from db_1130449_tb IGNORE INDEX (index_1130449) where col2= 3; + col1 | col2 | col3 | col4 +------+------+------+------ + 3 | 3 | 2 | a +(1 row) + +explain (costs off )select * from db_1130449_tb ignore index (index_1130449) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1130449_tb + Filter: (col2 = 3) +(2 rows) + +explain (costs off )select * from db_1130449_tb ignore index (index_1130449) where col2= 3 and col4 = 'a'; + QUERY PLAN +------------------------------------------------------- + Seq Scan on db_1130449_tb + Filter: ((col2 = 3) AND ((col4)::text = 'a'::text)) +(2 rows) + +explain (costs off) select * from db_1130449_tb IGNORE INDEX (index_1130449) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1130449_tb + Filter: (col2 = 3) +(2 rows) + +explain (costs off) select * from db_1130449_tb FORCE INDEX (index_1130449) IGNORE INDEX (index_1130449) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1130449_tb + Filter: (col2 = 3) +(2 rows) + +--multi index ignored gram test +create table db_1130452_tb (col1 int ,col2 int,col3 int,col4 varchar(10)); +insert into db_1130452_tb values(1,1,1,'a'); +insert into db_1130452_tb values(1,2,2,'a'); +insert into db_1130452_tb values(2,2,2,'a'); +insert into db_1130452_tb values(2,2,3,'b'); +insert into db_1130452_tb values(2,3,3,'b'); +insert into db_1130452_tb values(3,3,4,'b'); +insert into db_1130452_tb values(3,3,4,'a'); +insert into db_1130452_tb values(3,4,5,'c'); +insert into db_1130452_tb values(4,4,5,'c'); +insert into db_1130452_tb values(4,null,1,'c'); +create index index_1130452_1 on db_1130452_tb (col1); +create index index_1130452_2 on db_1130452_tb (col2); +create index index_1130452_3 on db_1130452_tb (col3); +create index index_1130452_4 on db_1130452_tb (col4); +analyze db_1130452_tb; +select * from db_1130452_tb ignore index (index_1130452_1) ignore index (index_1130452_2) where col2= 3 order by 1,2,3; + col1 | col2 | col3 | col4 +------+------+------+------ + 2 | 3 | 3 | b + 3 | 3 | 4 | b + 3 | 3 | 4 | a +(3 rows) + +select * from db_1130452_tb ignore index (index_1130452_2) ignore index (index_1130452_1) where col2= 3 order by 1,2,3; + col1 | col2 | col3 | col4 +------+------+------+------ + 2 | 3 | 3 | b + 3 | 3 | 4 | b + 3 | 3 | 4 | a +(3 rows) + +select * from db_1130452_tb ignore index (index_1130452_2) ignore index (index_1130452_2) where col2= 3 order by 1,2,3; + col1 | col2 | col3 | col4 +------+------+------+------ + 2 | 3 | 3 | b + 3 | 3 | 4 | b + 3 | 3 | 4 | a +(3 rows) + +select * from db_1130452_tb ignore index (index_1130452_1) ignore index (index_1130452_2)ignore index (index_1130452_3) ignore index (index_1130452_4) where col2= 3 and col1 = 2 and col3 = 3 and col4='b' order by 1,2,3; + col1 | col2 | col3 | col4 +------+------+------+------ + 2 | 3 | 3 | b +(1 row) + +select * from db_1130452_tb ignore index (index_1130452_1, index_1130452_2) where col2= 3 order by 1,2,3; + col1 | col2 | col3 | col4 +------+------+------+------ + 2 | 3 | 3 | b + 3 | 3 | 4 | b + 3 | 3 | 4 | a +(3 rows) + +select * from db_1130452_tb ignore index (index_1130452_2, index_1130452_1) where col2= 3 order by 1,2,3; + col1 | col2 | col3 | col4 +------+------+------+------ + 2 | 3 | 3 | b + 3 | 3 | 4 | b + 3 | 3 | 4 | a +(3 rows) + +select * from db_1130452_tb ignore index (index_1130452_2, index_1130452_2) where col2= 3 order by 1,2,3; + col1 | col2 | col3 | col4 +------+------+------+------ + 2 | 3 | 3 | b + 3 | 3 | 4 | b + 3 | 3 | 4 | a +(3 rows) + +select * from db_1130452_tb ignore index (index_1130452_1, index_1130452_2, index_1130452_3, index_1130452_4) where col2= 3 and col1 = 2 and col3 = 3 and col4='b' order by 1,2,3; + col1 | col2 | col3 | col4 +------+------+------+------ + 2 | 3 | 3 | b +(1 row) + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_1) ignore index (index_1130452_2) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1130452_tb + Filter: (col2 = 3) +(2 rows) + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_2) ignore index (index_1130452_1) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1130452_tb + Filter: (col2 = 3) +(2 rows) + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_2) ignore index (index_1130452_2) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1130452_tb + Filter: (col2 = 3) +(2 rows) + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_1) ignore index (index_1130452_2)ignore index (index_1130452_3) ignore index (index_1130452_4) where col2= 3 and col1 = 2 and col3 = 3 and col4='b'; + QUERY PLAN +------------------------------------------------------------------------------------- + Seq Scan on db_1130452_tb + Filter: ((col2 = 3) AND (col3 = 3) AND (col1 = 2) AND ((col4)::text = 'b'::text)) +(2 rows) + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_1 ,index_1130452_2) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1130452_tb + Filter: (col2 = 3) +(2 rows) + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_2 ,index_1130452_1) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1130452_tb + Filter: (col2 = 3) +(2 rows) + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_2, index_1130452_2) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1130452_tb + Filter: (col2 = 3) +(2 rows) + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_1, index_1130452_2, index_1130452_3, index_1130452_4) where col2= 3 and col1 = 2 and col3 = 3 and col4='b'; + QUERY PLAN +------------------------------------------------------------------------------------- + Seq Scan on db_1130452_tb + Filter: ((col2 = 3) AND (col3 = 3) AND (col1 = 2) AND ((col4)::text = 'b'::text)) +(2 rows) + +--error report +create table db_1130456_tb (col1 int ,col2 int,col3 int,col4 varchar(10)); +insert into db_1130456_tb values(1,1,1,'a'); +insert into db_1130456_tb values(1,2,2,'a'); +insert into db_1130456_tb values(2,2,2,'a'); +insert into db_1130456_tb values(2,2,3,'b'); +insert into db_1130456_tb values(2,3,3,'b'); +insert into db_1130456_tb values(3,3,4,'b'); +insert into db_1130456_tb values(3,3,4,'a'); +insert into db_1130456_tb values(3,4,5,'c'); +insert into db_1130456_tb values(4,4,5,'c'); +insert into db_1130456_tb values(4,null,1,'c'); +create index index_1130456_1 on db_1130456_tb (col1); +create index index_1130456_2 on db_1130456_tb (col2); +create index index_1130456_3 on db_1130456_tb (col3); +create index index_1130456_4 on db_1130456_tb (col4); +analyze db_1130456_tb; +create table db_1130456_tb_1 as select * from db_1130456_tb; +create index index_1130456_5 on db_1130456_tb_1 (col1); +select * from db_1130456_tb ignore index (index_1130456_5) where col2= 3; +ERROR: index not exists in relation db_1130456_tb +select * from db_1130456_tb ignore index (index_1130456_6) where col2= 3; +ERROR: index not exists in relation db_1130456_tb +--in partition table +CREATE TABLE db_1130473_tb +( +col1 INTEGER NOT NULL, +col2 INTEGER NOT NULL, +col3 INTEGER , +CA_STREET_NAME VARCHAR(60) , +CA_STREET_TYPE CHAR(15) , +CA_SUITE_NUMBER CHAR(10) , +CA_CITY VARCHAR(60) , +CA_COUNTY VARCHAR(30) , +CA_STATE CHAR(2) , +CA_ZIP CHAR(10) , +CA_COUNTRY VARCHAR(20) , +CA_GMT_OFFSET DECIMAL(5,2) , +CA_LOCATION_TYPE CHAR(20) +) +PARTITION BY list(col1) +( +partition p1 values (4), +partition p2 values (5), +partition p3 values (1), +partition p4 values (2), +partition p5 values (3)); +CREATE INDEX ds_db_1130473_tb_index2 ON db_1130473_tb(col1) LOCAL ; +insert into db_1130473_tb select 1,v,v from generate_series(1,120) as v; +insert into db_1130473_tb select 2,v,v from generate_series(1,200) as v; +insert into db_1130473_tb select 3,v,v from generate_series(1,100) as v; +insert into db_1130473_tb select 4,v,v from generate_series(1,300) as v; +insert into db_1130473_tb select 5,v,v from generate_series(1,500) as v; +create index "Index_1130473%%_1" on db_1130473_tb (col1) local; +create index INDEX_1130473_2 on db_1130473_tb (col2); +analyze db_1130473_tb; +select max(col2)+1 from db_1130473_tb ignore index ("Index_1130473%%_1") where col1>= 3 ; + ?column? +---------- + 501 +(1 row) + +select max(col2)+1 from db_1130473_tb ignore index ("Index_1130473%%_1") ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + ?column? +---------- + 501 +(1 row) + +explain (costs off) select max(col2)+1 from db_1130473_tb ignore index ("Index_1130473%%_1") where col1>= 3 ; + QUERY PLAN +-------------------------------------------------------------------------- + Result + InitPlan 1 (returns $0) + -> Limit + -> Index Scan Backward using index_1130473_2 on db_1130473_tb + Filter: (col1 >= 3) +(5 rows) + +explain (costs off) select max(col2)+1 from db_1130473_tb ignore index ("Index_1130473%%_1") ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + QUERY PLAN +----------------------------------------------------- + Aggregate + -> Partition Iterator + Iterations: 5 + -> Partitioned Seq Scan on db_1130473_tb + Filter: ((col2 >= 3) AND (col1 >= 3)) + Selected Partitions: 1..5 +(6 rows) + +select max(col2)+1 from db_1130473_tb partition (p1) ignore index ("Index_1130473%%_1") where col1>= 3 ; + ?column? +---------- + 301 +(1 row) + +select max(col2)+1 from db_1130473_tb partition (p1) ignore index ("Index_1130473%%_1") ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + ?column? +---------- + 301 +(1 row) + +explain (costs off) select max(col2)+1 from db_1130473_tb partition (p1) ignore index ("Index_1130473%%_1") where col1>= 3 ; + QUERY PLAN +--------------------------------------------- + Aggregate + -> Partitioned Seq Scan on db_1130473_tb + Filter: (col1 >= 3) + Selected Partitions: 4 +(4 rows) + +explain (costs off) select max(col2)+1 from db_1130473_tb partition (p1) ignore index ("Index_1130473%%_1") ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + QUERY PLAN +----------------------------------------------- + Aggregate + -> Partitioned Seq Scan on db_1130473_tb + Filter: ((col2 >= 3) AND (col1 >= 3)) + Selected Partitions: 4 +(4 rows) + +select max(col2)+1 from db_1130473_tb partition (p1) ignore index (ds_db_1130473_tb_index2) where col1>= 3 ; + ?column? +---------- + 301 +(1 row) + +select max(col2)+1 from db_1130473_tb partition (p1) ignore index (ds_db_1130473_tb_index2) ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + ?column? +---------- + 301 +(1 row) + +explain (costs off) select max(col2)+1 from db_1130473_tb partition (p1) ignore index (ds_db_1130473_tb_index2) where col1>= 3 ; + QUERY PLAN +--------------------------------------------- + Aggregate + -> Partitioned Seq Scan on db_1130473_tb + Filter: (col1 >= 3) + Selected Partitions: 4 +(4 rows) + +explain (costs off) select max(col2)+1 from db_1130473_tb partition (p1) ignore index (ds_db_1130473_tb_index2) ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + QUERY PLAN +----------------------------------------------- + Aggregate + -> Partitioned Seq Scan on db_1130473_tb + Filter: ((col2 >= 3) AND (col1 >= 3)) + Selected Partitions: 4 +(4 rows) + +-- with scan hint +create table db_1131004_tb (col1 int ,col2 int,col3 int,col4 varchar(10),primary key(col1)); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "db_1131004_tb_pkey" for table "db_1131004_tb" +insert into db_1131004_tb values(1,1,1,'a'); +insert into db_1131004_tb values(2,2,2,'a'); +insert into db_1131004_tb values(3,3,2,'a'); +insert into db_1131004_tb values(4,4,3,'b'); +insert into db_1131004_tb values(5,5,3,'b'); +insert into db_1131004_tb values(6,6,6,'b'); +insert into db_1131004_tb values(7,7,7,'a'); +insert into db_1131004_tb values(8,8,8,'c'); +insert into db_1131004_tb values(9,9,9,'c'); +insert into db_1131004_tb values(10,null,1,'c'); +create unique index index_1131004 on db_1131004_tb (col2); +analyze db_1131004_tb; +select /*+ indexscan(db_1131004_tb ) */* from db_1131004_tb ignore index (index_1131004) where col2= 3; + col1 | col2 | col3 | col4 +------+------+------+------ + 3 | 3 | 2 | a +(1 row) + +explain (costs off) select /*+ indexscan(db_1131004_tb ) */* from db_1131004_tb ignore index (index_1131004) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1131004_tb + Filter: (col2 = 3) +(2 rows) + +select /*+ indexonlyscan(db_1131004_tb ) */col2 from db_1131004_tb ignore index (index_1131004) where col2= 3; + col2 +------ + 3 +(1 row) + +explain (costs off) select /*+ indexonlyscan(db_1131004_tb ) */col2 from db_1131004_tb ignore index (index_1131004) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1131004_tb + Filter: (col2 = 3) +(2 rows) + +select /*+ indexscan(db_1131004_tb index_1131004 ) */* from db_1131004_tb ignore index (index_1131004) where col2= 3; + col1 | col2 | col3 | col4 +------+------+------+------ + 3 | 3 | 2 | a +(1 row) + +explain (costs off) select /*+ indexscan(db_1131004_tb index_1131004) */* from db_1131004_tb ignore index (index_1131004) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1131004_tb + Filter: (col2 = 3) +(2 rows) + +select /*+ indexonlyscan(db_1131004_tb index_1131004) */col2 from db_1131004_tb ignore index (index_1131004) where col2= 3; + col2 +------ + 3 +(1 row) + +explain (costs off) select /*+ indexonlyscan(db_1131004_tb index_1131004) */col2 from db_1131004_tb ignore index (index_1131004) where col2= 3; + QUERY PLAN +--------------------------- + Seq Scan on db_1131004_tb + Filter: (col2 = 3) +(2 rows) + \c postgres DROP DATABASE IF EXISTS db_1097149; DROP DATABASE IF EXISTS db_ID1097168; diff --git a/src/test/regress/sql/mysql_indexhint.sql b/src/test/regress/sql/mysql_indexhint.sql index 5e3e139380..5f7f984623 100644 --- a/src/test/regress/sql/mysql_indexhint.sql +++ b/src/test/regress/sql/mysql_indexhint.sql @@ -250,6 +250,212 @@ analyze list_list; explain (costs off) select user_no from list_list subpartition (pa1) use index (idx_list) where user_no = 1; explain (costs off)select user_no from list_list subpartition for (100,4) use index (idx_list) where user_no = 1; +--ignore index test +create table db_1130449_tb (col1 int ,col2 int,col3 int,col4 varchar(10),primary key(col1)); +insert into db_1130449_tb values(1,1,1,'a'); +insert into db_1130449_tb values(2,2,2,'a'); +insert into db_1130449_tb values(3,3,2,'a'); +insert into db_1130449_tb values(4,4,3,'b'); +insert into db_1130449_tb values(5,5,3,'b'); +insert into db_1130449_tb values(6,6,6,'b'); +insert into db_1130449_tb values(7,7,7,'a'); +insert into db_1130449_tb values(8,8,8,'c'); +insert into db_1130449_tb values(9,9,9,'c'); +insert into db_1130449_tb values(10,null,1,'c'); + +create unique index index_1130449 on db_1130449_tb (col2); +analyze db_1130449_tb; + +select * from db_1130449_tb ignore index (index_1130449) where col2= 3; + +select * from db_1130449_tb ignore index (index_1130449) where col2= 3 and col4 = 'a'; + +select * from db_1130449_tb IGNORE INDEX (index_1130449) where col2= 3; + +explain (costs off )select * from db_1130449_tb ignore index (index_1130449) where col2= 3; + +explain (costs off )select * from db_1130449_tb ignore index (index_1130449) where col2= 3 and col4 = 'a'; + +explain (costs off) select * from db_1130449_tb IGNORE INDEX (index_1130449) where col2= 3; + +explain (costs off) select * from db_1130449_tb FORCE INDEX (index_1130449) IGNORE INDEX (index_1130449) where col2= 3; + +--multi index ignored gram test +create table db_1130452_tb (col1 int ,col2 int,col3 int,col4 varchar(10)); +insert into db_1130452_tb values(1,1,1,'a'); +insert into db_1130452_tb values(1,2,2,'a'); +insert into db_1130452_tb values(2,2,2,'a'); +insert into db_1130452_tb values(2,2,3,'b'); +insert into db_1130452_tb values(2,3,3,'b'); +insert into db_1130452_tb values(3,3,4,'b'); +insert into db_1130452_tb values(3,3,4,'a'); +insert into db_1130452_tb values(3,4,5,'c'); +insert into db_1130452_tb values(4,4,5,'c'); +insert into db_1130452_tb values(4,null,1,'c'); + +create index index_1130452_1 on db_1130452_tb (col1); +create index index_1130452_2 on db_1130452_tb (col2); +create index index_1130452_3 on db_1130452_tb (col3); +create index index_1130452_4 on db_1130452_tb (col4); +analyze db_1130452_tb; + +select * from db_1130452_tb ignore index (index_1130452_1) ignore index (index_1130452_2) where col2= 3 order by 1,2,3; + +select * from db_1130452_tb ignore index (index_1130452_2) ignore index (index_1130452_1) where col2= 3 order by 1,2,3; + +select * from db_1130452_tb ignore index (index_1130452_2) ignore index (index_1130452_2) where col2= 3 order by 1,2,3; + +select * from db_1130452_tb ignore index (index_1130452_1) ignore index (index_1130452_2)ignore index (index_1130452_3) ignore index (index_1130452_4) where col2= 3 and col1 = 2 and col3 = 3 and col4='b' order by 1,2,3; + +select * from db_1130452_tb ignore index (index_1130452_1, index_1130452_2) where col2= 3 order by 1,2,3; + +select * from db_1130452_tb ignore index (index_1130452_2, index_1130452_1) where col2= 3 order by 1,2,3; + +select * from db_1130452_tb ignore index (index_1130452_2, index_1130452_2) where col2= 3 order by 1,2,3; + +select * from db_1130452_tb ignore index (index_1130452_1, index_1130452_2, index_1130452_3, index_1130452_4) where col2= 3 and col1 = 2 and col3 = 3 and col4='b' order by 1,2,3; + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_1) ignore index (index_1130452_2) where col2= 3; + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_2) ignore index (index_1130452_1) where col2= 3; + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_2) ignore index (index_1130452_2) where col2= 3; + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_1) ignore index (index_1130452_2)ignore index (index_1130452_3) ignore index (index_1130452_4) where col2= 3 and col1 = 2 and col3 = 3 and col4='b'; + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_1 ,index_1130452_2) where col2= 3; + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_2 ,index_1130452_1) where col2= 3; + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_2, index_1130452_2) where col2= 3; + +explain (costs off )select * from db_1130452_tb ignore index (index_1130452_1, index_1130452_2, index_1130452_3, index_1130452_4) where col2= 3 and col1 = 2 and col3 = 3 and col4='b'; + +--error report +create table db_1130456_tb (col1 int ,col2 int,col3 int,col4 varchar(10)); +insert into db_1130456_tb values(1,1,1,'a'); +insert into db_1130456_tb values(1,2,2,'a'); +insert into db_1130456_tb values(2,2,2,'a'); +insert into db_1130456_tb values(2,2,3,'b'); +insert into db_1130456_tb values(2,3,3,'b'); +insert into db_1130456_tb values(3,3,4,'b'); +insert into db_1130456_tb values(3,3,4,'a'); +insert into db_1130456_tb values(3,4,5,'c'); +insert into db_1130456_tb values(4,4,5,'c'); +insert into db_1130456_tb values(4,null,1,'c'); + +create index index_1130456_1 on db_1130456_tb (col1); +create index index_1130456_2 on db_1130456_tb (col2); +create index index_1130456_3 on db_1130456_tb (col3); +create index index_1130456_4 on db_1130456_tb (col4); +analyze db_1130456_tb; + +create table db_1130456_tb_1 as select * from db_1130456_tb; + +create index index_1130456_5 on db_1130456_tb_1 (col1); + +select * from db_1130456_tb ignore index (index_1130456_5) where col2= 3; + +select * from db_1130456_tb ignore index (index_1130456_6) where col2= 3; + +--in partition table +CREATE TABLE db_1130473_tb +( +col1 INTEGER NOT NULL, +col2 INTEGER NOT NULL, +col3 INTEGER , +CA_STREET_NAME VARCHAR(60) , +CA_STREET_TYPE CHAR(15) , +CA_SUITE_NUMBER CHAR(10) , +CA_CITY VARCHAR(60) , +CA_COUNTY VARCHAR(30) , +CA_STATE CHAR(2) , +CA_ZIP CHAR(10) , +CA_COUNTRY VARCHAR(20) , +CA_GMT_OFFSET DECIMAL(5,2) , +CA_LOCATION_TYPE CHAR(20) +) +PARTITION BY list(col1) +( +partition p1 values (4), +partition p2 values (5), +partition p3 values (1), +partition p4 values (2), +partition p5 values (3)); + + + +CREATE INDEX ds_db_1130473_tb_index2 ON db_1130473_tb(col1) LOCAL ; +insert into db_1130473_tb select 1,v,v from generate_series(1,120) as v; +insert into db_1130473_tb select 2,v,v from generate_series(1,200) as v; +insert into db_1130473_tb select 3,v,v from generate_series(1,100) as v; +insert into db_1130473_tb select 4,v,v from generate_series(1,300) as v; +insert into db_1130473_tb select 5,v,v from generate_series(1,500) as v; + +create index "Index_1130473%%_1" on db_1130473_tb (col1) local; +create index INDEX_1130473_2 on db_1130473_tb (col2); + +analyze db_1130473_tb; + +select max(col2)+1 from db_1130473_tb ignore index ("Index_1130473%%_1") where col1>= 3 ; + +select max(col2)+1 from db_1130473_tb ignore index ("Index_1130473%%_1") ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + +explain (costs off) select max(col2)+1 from db_1130473_tb ignore index ("Index_1130473%%_1") where col1>= 3 ; + +explain (costs off) select max(col2)+1 from db_1130473_tb ignore index ("Index_1130473%%_1") ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + +select max(col2)+1 from db_1130473_tb partition (p1) ignore index ("Index_1130473%%_1") where col1>= 3 ; + +select max(col2)+1 from db_1130473_tb partition (p1) ignore index ("Index_1130473%%_1") ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + +explain (costs off) select max(col2)+1 from db_1130473_tb partition (p1) ignore index ("Index_1130473%%_1") where col1>= 3 ; + +explain (costs off) select max(col2)+1 from db_1130473_tb partition (p1) ignore index ("Index_1130473%%_1") ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + +select max(col2)+1 from db_1130473_tb partition (p1) ignore index (ds_db_1130473_tb_index2) where col1>= 3 ; + +select max(col2)+1 from db_1130473_tb partition (p1) ignore index (ds_db_1130473_tb_index2) ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + +explain (costs off) select max(col2)+1 from db_1130473_tb partition (p1) ignore index (ds_db_1130473_tb_index2) where col1>= 3 ; + +explain (costs off) select max(col2)+1 from db_1130473_tb partition (p1) ignore index (ds_db_1130473_tb_index2) ignore index (INDEX_1130473_2) where col2>= 3 and col1 >=3 ; + + +-- with scan hint + +create table db_1131004_tb (col1 int ,col2 int,col3 int,col4 varchar(10),primary key(col1)); +insert into db_1131004_tb values(1,1,1,'a'); +insert into db_1131004_tb values(2,2,2,'a'); +insert into db_1131004_tb values(3,3,2,'a'); +insert into db_1131004_tb values(4,4,3,'b'); +insert into db_1131004_tb values(5,5,3,'b'); +insert into db_1131004_tb values(6,6,6,'b'); +insert into db_1131004_tb values(7,7,7,'a'); +insert into db_1131004_tb values(8,8,8,'c'); +insert into db_1131004_tb values(9,9,9,'c'); +insert into db_1131004_tb values(10,null,1,'c'); + +create unique index index_1131004 on db_1131004_tb (col2); +analyze db_1131004_tb; + +select /*+ indexscan(db_1131004_tb ) */* from db_1131004_tb ignore index (index_1131004) where col2= 3; + +explain (costs off) select /*+ indexscan(db_1131004_tb ) */* from db_1131004_tb ignore index (index_1131004) where col2= 3; + +select /*+ indexonlyscan(db_1131004_tb ) */col2 from db_1131004_tb ignore index (index_1131004) where col2= 3; + +explain (costs off) select /*+ indexonlyscan(db_1131004_tb ) */col2 from db_1131004_tb ignore index (index_1131004) where col2= 3; + + +select /*+ indexscan(db_1131004_tb index_1131004 ) */* from db_1131004_tb ignore index (index_1131004) where col2= 3; + +explain (costs off) select /*+ indexscan(db_1131004_tb index_1131004) */* from db_1131004_tb ignore index (index_1131004) where col2= 3; + +select /*+ indexonlyscan(db_1131004_tb index_1131004) */col2 from db_1131004_tb ignore index (index_1131004) where col2= 3; + +explain (costs off) select /*+ indexonlyscan(db_1131004_tb index_1131004) */col2 from db_1131004_tb ignore index (index_1131004) where col2= 3; + \c postgres DROP DATABASE IF EXISTS db_1097149; -- Gitee From e9775edba81122b52410b09c1b31b1e7e144ab14 Mon Sep 17 00:00:00 2001 From: nnuanyang Date: Mon, 30 Oct 2023 00:39:29 -0700 Subject: [PATCH 2/3] =?UTF-8?q?@variable=20=E9=80=92=E5=BD=92=E8=B0=83?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/backend/parser/gram.y | 1 + src/common/backend/parser/parser.cpp | 8 + src/gausskernel/process/tcop/postgres.cpp | 3 + src/gausskernel/runtime/executor/execQual.cpp | 21 ++- .../runtime/executor/nodeIndexscan.cpp | 2 +- .../runtime/executor/nodeResult.cpp | 47 ++++- .../runtime/executor/nodeSubplan.cpp | 18 +- src/include/knl/knl_session.h | 4 + .../set_user_defined_variables_test.source | 104 +++++++++++ .../set_user_defined_variables_test.source | 162 ++++++++++++++++++ 10 files changed, 364 insertions(+), 6 deletions(-) diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index d8484d061b..641f77eb5e 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -25407,6 +25407,7 @@ a_expr: c_expr { $$ = $1; } errmsg("@var_name := expr is not yet supported in distributed database."))); #endif if (DB_IS_CMPT(B_FORMAT) && (u_sess->attr.attr_common.enable_set_variable_b_format || ENABLE_SET_VARIABLES)) { + u_sess->parser_cxt.has_equal_uservar = true; UserSetElem *n = makeNode(UserSetElem); n->name = list_make1((Node *)$1); n->val = (Expr *)$3; diff --git a/src/common/backend/parser/parser.cpp b/src/common/backend/parser/parser.cpp index 4b7ff68994..2e698cf19b 100644 --- a/src/common/backend/parser/parser.cpp +++ b/src/common/backend/parser/parser.cpp @@ -48,6 +48,11 @@ static void resetForbidTruncateFlag() u_sess->parser_cxt.isForbidTruncate = false; } +static void resetHasSetUservarFlag() +{ + u_sess->parser_cxt.has_set_uservar = false; +} + /* * raw_parser * Given a query in string form, do lexical and grammatical analysis. @@ -75,6 +80,9 @@ List* raw_parser(const char* str, List** query_string_locationlist) /* reset u_sess->parser_cxt.isForbidTruncate */ resetForbidTruncateFlag(); + /* reset u_sess->parser_cxt.has_set_uservar */ + resetHasSetUservarFlag(); + /* initialize the flex scanner */ yyscanner = scanner_init(str, &yyextra.core_yy_extra, &ScanKeywords, ScanKeywordTokens); diff --git a/src/gausskernel/process/tcop/postgres.cpp b/src/gausskernel/process/tcop/postgres.cpp index 5b287f244e..beb33ae50c 100755 --- a/src/gausskernel/process/tcop/postgres.cpp +++ b/src/gausskernel/process/tcop/postgres.cpp @@ -8309,6 +8309,9 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam u_sess->pcache_cxt.gpc_in_try_store = false; u_sess->plsql_cxt.have_error = false; u_sess->parser_cxt.isPerform = false; + u_sess->parser_cxt.in_userset = false; + u_sess->parser_cxt.has_set_uservar = false; + u_sess->parser_cxt.has_equal_uservar = false; u_sess->parser_cxt.stmt = NULL; OpFusion::tearDown(u_sess->exec_cxt.CurrentOpFusionObj); /* init pbe execute status when long jump */ diff --git a/src/gausskernel/runtime/executor/execQual.cpp b/src/gausskernel/runtime/executor/execQual.cpp index bf1db1347a..3a6f6b3b8d 100644 --- a/src/gausskernel/runtime/executor/execQual.cpp +++ b/src/gausskernel/runtime/executor/execQual.cpp @@ -620,6 +620,9 @@ static Datum ExecEvalScalarVar(ExprState* exprstate, ExprContext* econtext, bool /* INDEX_VAR is handled by default case */ default: /* get the tuple from the relation being scanned */ slot = econtext->ecxt_scantuple; + if (u_sess->parser_cxt.in_userset) { + u_sess->parser_cxt.has_set_uservar = true; + } break; } @@ -719,6 +722,9 @@ static Datum ExecEvalScalarVarFast(ExprState* exprstate, ExprContext* econtext, /* INDEX_VAR is handled by default case */ default: /* get the tuple from the relation being scanned */ slot = econtext->ecxt_scantuple; + if (u_sess->parser_cxt.in_userset) { + u_sess->parser_cxt.has_set_uservar = true; + } break; } @@ -1103,6 +1109,9 @@ static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* is } else { con = makeConst(UNKNOWNOID, -1, InvalidOid, -2, (Datum)0, true, false); } + if (u_sess->parser_cxt.in_userset) { + u_sess->parser_cxt.has_set_uservar = true; + } } else if (IsA(exprstate->expr, SetVariableExpr)) { SetVariableExpr* setvar = (SetVariableExpr*)transformSetVariableExpr((SetVariableExpr*)exprstate->expr); con = (Const*)setvar->value; @@ -1289,6 +1298,9 @@ static Datum ExecEvalUserSetElm(ExprState* exprstate, ExprContext* econtext, boo Node* res = NULL; char* value = NULL; + if (nodeTag(usestate->instate) != T_CaseExprState && DB_IS_CMPT(B_FORMAT)) + u_sess->parser_cxt.in_userset = true; + Datum result = ExecEvalExpr(usestate->instate, econtext, isNull, isDone); if (*isNull) { @@ -1328,6 +1340,7 @@ static Datum ExecEvalUserSetElm(ExprState* exprstate, ExprContext* econtext, boo } check_set_user_message(&elemcopy); + u_sess->parser_cxt.in_userset = false; return result; } @@ -1346,6 +1359,10 @@ static Datum ExecEvalParamExec(ExprState* exprstate, ExprContext* econtext, bool if (isDone != NULL) *isDone = ExprSingleResult; + if (u_sess->parser_cxt.in_userset) { + u_sess->parser_cxt.has_set_uservar = true; + } + /* * PARAM_EXEC params (internal executor parameters) are stored in the * ecxt_param_exec_vals array, and can be accessed by array index. @@ -1355,7 +1372,9 @@ static Datum ExecEvalParamExec(ExprState* exprstate, ExprContext* econtext, bool /* Parameter not evaluated yet, so go do it */ ExecSetParamPlan((SubPlanState*)prm->execPlan, econtext); /* ExecSetParamPlan should have processed this param... */ - Assert(prm->execPlan == NULL); + if (!u_sess->parser_cxt.has_set_uservar || !DB_IS_CMPT(B_FORMAT)) { + Assert(prm->execPlan == NULL); + } prm->isConst = true; prm->valueType = expression->paramtype; } diff --git a/src/gausskernel/runtime/executor/nodeIndexscan.cpp b/src/gausskernel/runtime/executor/nodeIndexscan.cpp index ff808cccfd..d29184feab 100644 --- a/src/gausskernel/runtime/executor/nodeIndexscan.cpp +++ b/src/gausskernel/runtime/executor/nodeIndexscan.cpp @@ -168,7 +168,7 @@ static TupleTableSlot* ExecIndexScan(PlanState* state) /* * If we have runtime keys and they've not already been set up, do it now. */ - if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady) { + if (node->iss_NumRuntimeKeys != 0 && (!node->iss_RuntimeKeysReady || (u_sess->parser_cxt.has_set_uservar && DB_IS_CMPT(B_FORMAT)))) { /* * set a flag for partitioned table, so we can deal with it specially * when we rescan the partitioned table diff --git a/src/gausskernel/runtime/executor/nodeResult.cpp b/src/gausskernel/runtime/executor/nodeResult.cpp index 73cde60491..3291dae1cb 100644 --- a/src/gausskernel/runtime/executor/nodeResult.cpp +++ b/src/gausskernel/runtime/executor/nodeResult.cpp @@ -76,7 +76,7 @@ static TupleTableSlot* ExecResult(PlanState* state) /* * check constant qualifications like (2 > 1), if not already done */ - if (node->rs_checkqual) { + if ((node->rs_checkqual || (u_sess->parser_cxt.has_set_uservar && DB_IS_CMPT(B_FORMAT))) && !u_sess->parser_cxt.has_equal_uservar) { bool qualResult = ExecQual((List*)node->resconstantqual, econtext, false); node->rs_checkqual = false; @@ -116,6 +116,21 @@ static TupleTableSlot* ExecResult(PlanState* state) if (node->ps.ps_TupFromTlist) { result_slot = ExecProject(node->ps.ps_ProjInfo, &is_done); if (is_done == ExprMultipleResult) { + if (u_sess->parser_cxt.has_equal_uservar) { + u_sess->parser_cxt.has_equal_uservar = false; + bool qualResult = ExecQual((List*)node->resconstantqual, econtext, false); + + node->rs_checkqual = false; + if (!qualResult) { + node->rs_done = true; + /* + * Mark this constant qualification check failure for future corner cases. Currently only used in GDS + * foreign scan + */ + u_sess->exec_cxt.exec_result_checkqual_fail = true; + return NULL; + } + } return result_slot; } /* Done with that source tuple... */ @@ -159,6 +174,21 @@ static TupleTableSlot* ExecResult(PlanState* state) node->rs_done = true; } + if (u_sess->parser_cxt.has_equal_uservar && node->resconstantqual != NULL) { + bool qualResult = ExecQual((List*)node->resconstantqual, econtext, false); + + node->rs_checkqual = false; + if (!qualResult) { + node->rs_done = true; + /* + * Mark this constant qualification check failure for future corner cases. Currently only used in GDS + * foreign scan + */ + u_sess->exec_cxt.exec_result_checkqual_fail = true; + return NULL; + } + } + /* * form the result tuple using ExecProject(), and return it --- unless * the projection produces an empty set, in which case we must loop @@ -166,6 +196,21 @@ static TupleTableSlot* ExecResult(PlanState* state) */ result_slot = ExecProject(node->ps.ps_ProjInfo, &is_done); + if (u_sess->parser_cxt.has_equal_uservar && node->resconstantqual != NULL) { + u_sess->parser_cxt.has_equal_uservar = false; + bool qualResult = ExecQual((List*)node->resconstantqual, econtext, false); + + node->rs_checkqual = false; + if (!qualResult) { + node->rs_done = true; + /* + * Mark this constant qualification check failure for future corner cases. Currently only used in GDS + * foreign scan + */ + u_sess->exec_cxt.exec_result_checkqual_fail = true; + return NULL; + } + } if (is_done != ExprEndResult) { node->ps.ps_TupFromTlist = (is_done == ExprMultipleResult); return result_slot; diff --git a/src/gausskernel/runtime/executor/nodeSubplan.cpp b/src/gausskernel/runtime/executor/nodeSubplan.cpp index 2443036716..767ed98f1d 100644 --- a/src/gausskernel/runtime/executor/nodeSubplan.cpp +++ b/src/gausskernel/runtime/executor/nodeSubplan.cpp @@ -899,6 +899,12 @@ void ExecSetParamPlan(SubPlanState* node, ExprContext* econtext) */ MemoryContext oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + if (u_sess->parser_cxt.has_set_uservar && DB_IS_CMPT(B_FORMAT)) { + if (nodeTag(planstate) == T_SeqScanState) { + scan_handler_tbl_restrpos(castNode(SeqScanState, planstate)->ss_currentScanDesc); + } + } + /* * Run the plan. (If it needs to be rescanned, the first ExecProcNode * call will take care of that.) @@ -958,9 +964,15 @@ void ExecSetParamPlan(SubPlanState* node, ExprContext* econtext) int paramid = lfirst_int(l); ParamExecData* prm = &(econtext->ecxt_param_exec_vals[paramid]); - prm->execPlan = NULL; - prm->value = tableam_tops_tuple_getattr(node->curTuple, i, tdesc, &(prm->isnull)); - i++; + if (u_sess->parser_cxt.has_set_uservar && DB_IS_CMPT(B_FORMAT)) { + prm->value = tableam_tops_tuple_getattr(node->curTuple, i, tdesc, &(prm->isnull)); + i++; + return; + } else { + prm->execPlan = NULL; + prm->value = tableam_tops_tuple_getattr(node->curTuple, i, tdesc, &(prm->isnull)); + i++; + } } } diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 5756e2f369..46f23e0b3d 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -446,6 +446,10 @@ typedef struct knl_u_parser_context { bool isForbidTruncate; bool isPerform; void* stmt; + + bool in_userset; + bool has_set_uservar; + bool has_equal_uservar; } knl_u_parser_context; typedef struct knl_u_trigger_context { diff --git a/src/test/regress/input/set_user_defined_variables_test.source b/src/test/regress/input/set_user_defined_variables_test.source index 212395c20c..44d23115a4 100644 --- a/src/test/regress/input/set_user_defined_variables_test.source +++ b/src/test/regress/input/set_user_defined_variables_test.source @@ -629,6 +629,110 @@ select @a, @b; set @a := @c := 2, @b := @d := @a, @@session_timeout = 700, @e := @f := @a; select @a, @b, @c, @d, @e, @f, @@session_timeout; +set b_format_behavior_compat_options="enable_set_variables"; +-- @variable recursive call +CREATE TABLE demo ( + id int NOT NULL AUTO_INCREMENT, + name varchar(255) NOT NULL, + parent_id int NOT NULL, +PRIMARY KEY ( id ) +); +INSERT INTO demo VALUES ('1', 'A', '0'); +INSERT INTO demo VALUES ('2', 'B', '1'); +INSERT INTO demo VALUES ('3', 'C', '1'); +INSERT INTO demo VALUES ('4', 'D', '2'); +INSERT INTO demo VALUES ('5', 'E', '4'); +INSERT INTO demo VALUES ('6', 'F', '1'); +INSERT INTO demo VALUES ('7', 'G', '1'); + +SELECT @r , +(SELECT @r:= parent_id FROM demo WHERE id = @r) AS parent_id, +@l:= @l+ 1 AS lvl +FROM +(SELECT @r:= 5, @l:= 0) vars, +demo h +WHERE @r<> 0; + +SELECT @r , +(SELECT @r:= parent_id FROM demo WHERE id = @r) AS parent_id, +@l:= @l+ 1 AS lvl +FROM demo +WHERE @r<> 0; + +drop table if exists my_table; +CREATE TABLE my_table ( +id int, +name varchar(255), +parent_id int +); +INSERT INTO my_table VALUES ('1', 'A', '0'); +INSERT INTO my_table VALUES ('2', 'B', '2'); +INSERT INTO my_table VALUES ('3', 'C', '1'); +INSERT INTO my_table VALUES ('4', 'D', '2'); +INSERT INTO my_table VALUES ('5', 'E', '4'); +INSERT INTO my_table VALUES ('6', 'F', '1'); +INSERT INTO my_table VALUES ('7', 'G', '1'); +SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2, +@l:= @l+ 1 AS lvl FROM +(SELECT @r:= 5, @l:= 0) vars, +my_table h WHERE @r<> 0; + +SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2, +@l:= @l+ 1 AS lvl FROM +(SELECT @r:= 0, @l:= 0) vars, +my_table h WHERE @r<> 0; + +drop table if exists my_table; +create table my_table(id int, num_col int); +insert into my_table values +(1,5), +(2,10), +(3,15), +(4,20); +-- 设置初始值 +SET @sum := 0; +SET @counter := 0; +-- 递归查询和计算 +SELECT +@counter := @counter + 1 AS iteration, +@sum := @sum + num_col AS running_sum, +num_col +FROM +my_table +WHERE +@counter <= (SELECT COUNT(*) FROM my_table) +ORDER BY +id; +-- 输出最终的累加和 +SELECT @sum AS total_sum; +drop table if exists my_table; +CREATE TABLE my_table ( + id INT, + parent_id INT, + name VARCHAR(50) +); +INSERT INTO my_table (id, parent_id, name) VALUES +(1, NULL, 'Node 1'), +(2, 1, 'Node 1.1'), +(3, 1, 'Node 1.2'), +(4, 2, 'Node 1.1.1'), +(5, 4, 'Node 1.1.1.1'), +(6, NULL, 'Node 2'), +(7, 6, 'Node 2.1'), +(8, 7, 'Node 2.1.1'); +SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2, + @l:= @l+ 1 AS lvl FROM +(SELECT @r:= 5, @l:= 0) vars, +my_table h WHERE @r <>0; +SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2 FROM +(SELECT @r:= 5) vars, +my_table h WHERE @r<> 0; +SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2 FROM +(SELECT @r:= 5) vars, +my_table h WHERE @r<> 0; +SELECT (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id1 FROM +my_table h WHERE @r<> 0; + \c regression drop database if exists test_set; diff --git a/src/test/regress/output/set_user_defined_variables_test.source b/src/test/regress/output/set_user_defined_variables_test.source index 3d7c73f16a..b25f0073e7 100644 --- a/src/test/regress/output/set_user_defined_variables_test.source +++ b/src/test/regress/output/set_user_defined_variables_test.source @@ -1304,6 +1304,168 @@ select @a, @b, @c, @d, @e, @f, @@session_timeout; 2 | 1 | 2 | 1 | 1 | 1 | 700 (1 row) +set b_format_behavior_compat_options="enable_set_variables"; +-- @variable recursive call +CREATE TABLE demo ( + id int NOT NULL AUTO_INCREMENT, + name varchar(255) NOT NULL, + parent_id int NOT NULL, +PRIMARY KEY ( id ) +); +NOTICE: CREATE TABLE will create implicit sequence "demo_id_seq" for serial column "demo.id" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "demo_pkey" for table "demo" +INSERT INTO demo VALUES ('1', 'A', '0'); +INSERT INTO demo VALUES ('2', 'B', '1'); +INSERT INTO demo VALUES ('3', 'C', '1'); +INSERT INTO demo VALUES ('4', 'D', '2'); +INSERT INTO demo VALUES ('5', 'E', '4'); +INSERT INTO demo VALUES ('6', 'F', '1'); +INSERT INTO demo VALUES ('7', 'G', '1'); +SELECT @r , +(SELECT @r:= parent_id FROM demo WHERE id = @r) AS parent_id, +@l:= @l+ 1 AS lvl +FROM +(SELECT @r:= 5, @l:= 0) vars, +demo h +WHERE @r<> 0; + @r | parent_id | lvl +----+-----------+----- + 5 | 4 | 1 + 4 | 2 | 2 + 2 | 1 | 3 + 1 | 0 | 4 +(4 rows) + +SELECT @r , +(SELECT @r:= parent_id FROM demo WHERE id = @r) AS parent_id, +@l:= @l+ 1 AS lvl +FROM demo +WHERE @r<> 0; + @r | parent_id | lvl +----+-----------+----- +(0 rows) + +drop table if exists my_table; +NOTICE: table "my_table" does not exist, skipping +CREATE TABLE my_table ( +id int, +name varchar(255), +parent_id int +); +INSERT INTO my_table VALUES ('1', 'A', '0'); +INSERT INTO my_table VALUES ('2', 'B', '2'); +INSERT INTO my_table VALUES ('3', 'C', '1'); +INSERT INTO my_table VALUES ('4', 'D', '2'); +INSERT INTO my_table VALUES ('5', 'E', '4'); +INSERT INTO my_table VALUES ('6', 'F', '1'); +INSERT INTO my_table VALUES ('7', 'G', '1'); +SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2, +@l:= @l+ 1 AS lvl FROM +(SELECT @r:= 5, @l:= 0) vars, +my_table h WHERE @r<> 0; + @r | parent_id2 | lvl +----+------------+----- + 5 | 4 | 1 + 4 | 2 | 2 + 2 | 2 | 3 + 2 | 2 | 4 + 2 | 2 | 5 + 2 | 2 | 6 + 2 | 2 | 7 +(7 rows) + +SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2, +@l:= @l+ 1 AS lvl FROM +(SELECT @r:= 0, @l:= 0) vars, +my_table h WHERE @r<> 0; + @r | parent_id2 | lvl +----+------------+----- +(0 rows) + +drop table if exists my_table; +create table my_table(id int, num_col int); +insert into my_table values +(1,5), +(2,10), +(3,15), +(4,20); +-- 设置初始值 +SET @sum := 0; +SET @counter := 0; +-- 递归查询和计算 +SELECT +@counter := @counter + 1 AS iteration, +@sum := @sum + num_col AS running_sum, +num_col +FROM +my_table +WHERE +@counter <= (SELECT COUNT(*) FROM my_table) +ORDER BY +id; + iteration | running_sum | num_col +-----------+-------------+--------- + 1 | 5 | 5 + 2 | 15 | 10 + 3 | 30 | 15 + 4 | 50 | 20 +(4 rows) + +-- 输出最终的累加和 +SELECT @sum AS total_sum; + total_sum +----------- + 50 +(1 row) + +drop table if exists my_table; +CREATE TABLE my_table ( + id INT, + parent_id INT, + name VARCHAR(50) +); +INSERT INTO my_table (id, parent_id, name) VALUES +(1, NULL, 'Node 1'), +(2, 1, 'Node 1.1'), +(3, 1, 'Node 1.2'), +(4, 2, 'Node 1.1.1'), +(5, 4, 'Node 1.1.1.1'), +(6, NULL, 'Node 2'), +(7, 6, 'Node 2.1'), +(8, 7, 'Node 2.1.1'); +SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2, + @l:= @l+ 1 AS lvl FROM +(SELECT @r:= 5, @l:= 0) vars, +my_table h WHERE @r <>0; + @r | parent_id2 | lvl +----+------------+----- + 5 | 4 | 1 + 4 | 2 | 2 + 2 | 1 | 3 + 1 | | 4 +(4 rows) + +SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2 FROM +(SELECT @r:= 5) vars, +my_table h WHERE @r<> 0; +ERROR: failed to find conversion function from bigint to integer +SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2 FROM +(SELECT @r:= 5) vars, +my_table h WHERE @r<> 0; + @r | parent_id2 +----+------------ + 5 | 4 + 4 | 2 + 2 | 1 + 1 | +(4 rows) + +SELECT (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id1 FROM +my_table h WHERE @r<> 0; + parent_id1 +------------ +(0 rows) + \c regression drop database if exists test_set; \! @abs_bindir@/gs_guc reload -Z datanode -D @abs_srcdir@/tmp_check/datanode1 -c "enable_set_variable_b_format=off" >/dev/null 2>&1 -- Gitee From 6d4318bbc092e81fedaa8a30d63d75c6d09fcb83 Mon Sep 17 00:00:00 2001 From: chenbd Date: Wed, 24 May 2023 16:09:20 +0800 Subject: [PATCH 3/3] view sql security --- doc/src/sgml/ref/alter_view.sgmlin | 4 +- doc/src/sgml/ref/create_view.sgmlin | 4 +- src/bin/pg_dump/pg_dump.cpp | 35 ++- src/bin/pg_dump/pg_dump.h | 1 + src/common/backend/nodes/copyfuncs.cpp | 1 + src/common/backend/nodes/equalfuncs.cpp | 1 + src/common/backend/parser/gram.y | 117 ++++++++- .../interfaces/libpq/frontend_parser/gram.y | 3 +- src/gausskernel/optimizer/commands/view.cpp | 20 ++ .../optimizer/rewrite/rewriteHandler.cpp | 55 ++++ .../storage/access/common/reloptions.cpp | 8 + src/include/commands/view.h | 1 + src/include/nodes/parsenodes_common.h | 7 + src/include/parser/kwlist.h | 1 + src/include/utils/rel.h | 30 +++ src/test/regress/expected/b_compatibility.out | 242 ++++++++++++++++++ src/test/regress/sql/b_compatibility.sql | 139 ++++++++++ 17 files changed, 657 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/alter_view.sgmlin b/doc/src/sgml/ref/alter_view.sgmlin index 9d3f2b3c4c..7695779f53 100644 --- a/doc/src/sgml/ref/alter_view.sgmlin +++ b/doc/src/sgml/ref/alter_view.sgmlin @@ -24,8 +24,10 @@ ALTER VIEW [ IF EXISTS ] view_name SET ( {view_option_name [= view_option_value]} [, ... ] ); ALTER VIEW [ IF EXISTS ] view_name RESET ( view_option_name [, ... ] ); -ALTER [DEFINER = user] VIEW view_name [ ( column_name [, ...] ) ] +ALTER [DEFINER = user] [ SQL SECURITY { DEFINER | INVOKER } ] + VIEW view_name [ ( column_name [, ...] ) ] AS query; +NOTICE: SQL SECURITY option is only available in CENTRALIZED mode and B-format database. \ No newline at end of file diff --git a/doc/src/sgml/ref/create_view.sgmlin b/doc/src/sgml/ref/create_view.sgmlin index 168f58b4dc..1b457d2f92 100644 --- a/doc/src/sgml/ref/create_view.sgmlin +++ b/doc/src/sgml/ref/create_view.sgmlin @@ -10,10 +10,12 @@ -CREATE [ OR REPLACE ] [DEFINER = user] [ TEMP | TEMPORARY ] VIEW view_name [ ( column_name [, ...] ) ] +CREATE [ OR REPLACE ] [ DEFINER = user ] [ SQL SECURITY { DEFINER | INVOKER } ] + [ TEMP | TEMPORARY ] VIEW view_name [ ( column_name [, ...] ) ] [ WITH ( {view_option_name [= view_option_value]} [, ... ] ) ] AS query [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]; +NOTICE: SQL SECURITY option is only available in CENTRALIZED mode and B-format database. \ No newline at end of file diff --git a/src/bin/pg_dump/pg_dump.cpp b/src/bin/pg_dump/pg_dump.cpp index 81e530c7b3..f6f9921ee4 100644 --- a/src/bin/pg_dump/pg_dump.cpp +++ b/src/bin/pg_dump/pg_dump.cpp @@ -6689,6 +6689,7 @@ TableInfo* getTables(Archive* fout, int* numTables) int i_parttype = 0; int i_relrowmovement = 0; int i_relhsblockchain = 0; + int i_viewsecurity = 0; int1 i_relcmprs = 0; SimpleOidListCell* cell = NULL; int count = 0; @@ -6791,9 +6792,14 @@ TableInfo* getTables(Archive* fout, int* numTables) "(SELECT pg_catalog.string_agg(node_name,',') AS pgxc_node_names from pgxc_node n where n.oid " "in (select pg_catalog.unnest(nodeoids) from pgxc_class v where v.pcrelid=c.oid) ) , " #endif - "pg_catalog.array_to_string(pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, " - "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " - "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " + "pg_catalog.array_to_string(pg_catalog.array_remove(pg_catalog.array_remove(pg_catalog.array_remove" + "(pg_catalog.array_remove(c.reloptions, 'view_sql_security=definer'), 'view_sql_security=invoker'), " + "'check_option=local'), 'check_option=cascaded'), ', ') AS reloptions, " + "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " + "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " + "CASE WHEN 'view_sql_security=definer' = ANY (c.reloptions) THEN 'DEFINER'::text " + "WHEN 'view_sql_security=invoker' = ANY (c.reloptions) THEN 'INVOKER'::text " + "ELSE NULL END AS viewsecurity, " "pg_catalog.array_to_string(array(SELECT 'toast.' || " "x FROM pg_catalog.unnest(tc.reloptions) x), ', ') AS toast_reloptions " "FROM pg_class c " @@ -6843,9 +6849,14 @@ TableInfo* getTables(Archive* fout, int* numTables) "(SELECT pg_catalog.string_agg(node_name,',') AS pgxc_node_names from pgxc_node n where n.oid " "in (select pg_catalog.unnest(nodeoids) from pgxc_class v where v.pcrelid=c.oid) ) , " #endif - "pg_catalog.array_to_string(pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, " - "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " - "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " + "pg_catalog.array_to_string(pg_catalog.array_remove(pg_catalog.array_remove(pg_catalog.array_remove" + "(pg_catalog.array_remove(c.reloptions, 'view_sql_security=definer'), 'view_sql_security=invoker'), " + "'check_option=local'), 'check_option=cascaded'), ', ') AS reloptions, " + "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " + "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, " + "CASE WHEN 'view_sql_security=definer' = ANY (c.reloptions) THEN 'DEFINER'::text " + "WHEN 'view_sql_security=invoker' = ANY (c.reloptions) THEN 'INVOKER'::text " + "ELSE NULL END AS viewsecurity, " "pg_catalog.array_to_string(array(SELECT 'toast.' || " "x FROM pg_catalog.unnest(tc.reloptions) x), ', ') AS toast_reloptions " "FROM pg_class c " @@ -7199,6 +7210,7 @@ TableInfo* getTables(Archive* fout, int* numTables) i_reltablespace = PQfnumber(res, "reltablespace"); i_reloptions = PQfnumber(res, "reloptions"); i_checkoption = PQfnumber(res, "checkoption"); + i_viewsecurity = PQfnumber(res, "viewsecurity"); i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); @@ -7377,6 +7389,11 @@ TableInfo* getTables(Archive* fout, int* numTables) tblinfo[i].checkoption = gs_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = gs_strdup(PQgetvalue(res, i, i_toastreloptions)); + /* b_format database views' sql security options */ + if (i_viewsecurity == -1 || PQgetisnull(res, i, i_viewsecurity)) + tblinfo[i].viewsecurity = NULL; + else + tblinfo[i].viewsecurity = gs_strdup(PQgetvalue(res, i, i_viewsecurity)); /* other fields were zeroed above */ #ifdef ENABLE_MOT @@ -18612,7 +18629,11 @@ static void dumpViewSchema( appendPQExpBuffer(q, "%s CASCADE;\n", fmtId(tbinfo->dobj.name)); } - appendPQExpBuffer(q, "CREATE VIEW %s(%s)", fmtId(tbinfo->dobj.name), schemainfo); + appendPQExpBuffer(q, "CREATE "); + if (tbinfo->viewsecurity != NULL) + appendPQExpBuffer(q, "\n SQL SECURITY %s \n ", tbinfo->viewsecurity); + + appendPQExpBuffer(q, "VIEW %s(%s)", fmtId(tbinfo->dobj.name), schemainfo); if ((tbinfo->reloptions != NULL) && strlen(tbinfo->reloptions) > 0) appendPQExpBuffer(q, " WITH (%s)", tbinfo->reloptions); appendPQExpBuffer(q, " AS\n "); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 5cd34f47e1..ac2b9f201e 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -257,6 +257,7 @@ typedef struct _tableInfo { DumpId autoincconstraint; DumpId autoincindex; char* autoinc_seqname; + char* viewsecurity; #ifdef PGXC /* PGXC table locator Data */ char pgxclocatortype; /* Type of PGXC table locator */ diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index 0f9f7c7867..175e543057 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -5613,6 +5613,7 @@ static ViewStmt* _copyViewStmt(const ViewStmt* from) COPY_SCALAR_FIELD(relkind); COPY_STRING_FIELD(definer); COPY_SCALAR_FIELD(is_alter); + COPY_SCALAR_FIELD(viewSecurityOption); COPY_SCALAR_FIELD(withCheckOption); return newnode; diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp index 6eceb5f946..e86f4af901 100644 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -1712,6 +1712,7 @@ static bool _equalViewStmt(const ViewStmt* a, const ViewStmt* b) COMPARE_SCALAR_FIELD(relkind); COMPARE_STRING_FIELD(definer); COMPARE_SCALAR_FIELD(is_alter); + COMPARE_SCALAR_FIELD(viewSecurityOption); COMPARE_SCALAR_FIELD(withCheckOption); return true; diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 641f77eb5e..d7792864c6 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -689,7 +689,7 @@ static void setDelimiterName(core_yyscan_t yyscanner, char*input, VariableSetStm %type OptRelative %type OptGPI %type OptTableSpace OptConsTableSpace OptConsTableSpaceWithEmpty OptTableSpaceOwner LoggingStr size_clause OptMaxSize OptDatafileSize OptReuse OptAuto OptNextStr OptDatanodeName -%type opt_check_option +%type opt_check_option view_security_expression view_security_option %type opt_provider security_label @@ -924,7 +924,7 @@ static void setDelimiterName(core_yyscan_t yyscanner, char*input, VariableSetStm SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHIPPABLE SHOW SHUTDOWN SIBLINGS SIMILAR SIMPLE SIZE SKIP SLAVE SLICE SMALLDATETIME SMALLDATETIME_FORMAT_P SMALLINT SNAPSHOT SOME SOURCE_P SPACE SPILL SPLIT STABLE STANDALONE_P START STARTS STARTWITH STATEMENT STATEMENT_ID STATISTICS STDIN STDOUT STORAGE STORE_P STORED STRATIFY STREAM STRICT_P STRIP_P SUBPARTITION SUBPARTITIONS SUBSCRIPTION SUBSTRING - SYMMETRIC SYNONYM SYSDATE SYSID SYSTEM_P SYS_REFCURSOR STARTING SHOW_ERRORS + SYMMETRIC SYNONYM SYSDATE SYSID SYSTEM_P SYS_REFCURSOR STARTING SHOW_ERRORS SQL_P TABLE TABLES TABLESAMPLE TABLESPACE TARGET TEMP TEMPLATE TEMPORARY TERMINATED TEXT_P THAN THEN TIME TIME_FORMAT_P TIMECAPSULE TIMESTAMP TIMESTAMP_FORMAT_P TIMESTAMPDIFF TINYINT TO TRAILING TRANSACTION TRANSFORM TREAT TRIGGER TRIM TRUE_P @@ -15921,6 +15921,24 @@ invoker_rights: AUTHID DEFINER } } ; +view_security_option: DEFINER + { + $$ = VIEW_SQL_SECURITY_DEFINER; + } + | INVOKER + { + $$ = VIEW_SQL_SECURITY_INVOKER; + } + ; +view_security_expression: SQL_P SECURITY view_security_option + { + if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) { + $$ = $3; + } else { + parser_yyerror("not support SQL SECURITY EXPRESSION"); + } + } + ; definer_expression: DEFINER '=' UserId { @@ -17540,6 +17558,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->sql_statement = NULL; n->is_alter = true; n->withCheckOption = (ViewCheckOption)$7; + n->viewSecurityOption = VIEW_SQL_SECURITY_NONE; $$ = (Node *) n; } | ALTER definer_expression VIEW qualified_name opt_column_list AS SelectStmt opt_check_option @@ -17560,9 +17579,53 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->replace = true; n->sql_statement = NULL; n->is_alter = true; + n->viewSecurityOption = VIEW_SQL_SECURITY_NONE; + n->withCheckOption = (ViewCheckOption)$8; + $$ = (Node *) n; + } + | ALTER view_security_expression VIEW qualified_name opt_column_list AS SelectStmt opt_check_option + { +#ifndef ENABLE_MULTIPLE_NODES + if (u_sess->attr.attr_sql.sql_compatibility != B_FORMAT) +#endif + { + ereport(errstate, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("ALTER VIEW AS is not supported."))); + } + ViewStmt *n = makeNode(ViewStmt); + n->view = $4; + n->aliases = $5; + n->query = $7; + n->replace = true; + n->sql_statement = NULL; + n->is_alter = true; + n->viewSecurityOption = (ViewSecurityOption)$2; n->withCheckOption = (ViewCheckOption)$8; $$ = (Node *) n; } + | ALTER definer_expression view_security_expression VIEW qualified_name opt_column_list AS SelectStmt opt_check_option + { +#ifndef ENABLE_MULTIPLE_NODES + if (u_sess->attr.attr_sql.sql_compatibility != B_FORMAT) +#endif + { + ereport(errstate, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("ALTER VIEW AS is not supported."))); + } + ViewStmt *n = makeNode(ViewStmt); + n->definer = $2; + n->view = $5; + n->aliases = $6; + n->query = $8; + n->replace = true; + n->sql_statement = NULL; + n->is_alter = true; + n->viewSecurityOption = (ViewSecurityOption)$3; + n->withCheckOption = (ViewCheckOption)$9; + $$ = (Node *) n; + } | ALTER MATERIALIZED VIEW qualified_name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); @@ -18997,6 +19060,7 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list opt_reloptions n->options = $6; n->sql_statement = NULL; n->withCheckOption = (ViewCheckOption)$9; + n->viewSecurityOption = VIEW_SQL_SECURITY_NONE; $$ = (Node *) n; } | CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list opt_reloptions @@ -19011,6 +19075,7 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list opt_reloptions n->options = $8; n->sql_statement = NULL; n->withCheckOption = (ViewCheckOption)$11; + n->viewSecurityOption = VIEW_SQL_SECURITY_NONE; $$ = (Node *) n; } | CREATE opt_or_replace definer_expression OptTemp VIEW qualified_name opt_column_list opt_reloptions @@ -19026,6 +19091,53 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list opt_reloptions n->options = $8; n->sql_statement = NULL; n->withCheckOption = (ViewCheckOption)$11; + n->viewSecurityOption = VIEW_SQL_SECURITY_NONE; + $$ = (Node *) n; + } + | CREATE view_security_expression OptTemp VIEW qualified_name opt_column_list opt_reloptions + AS SelectStmt opt_check_option + { + ViewStmt *n = makeNode(ViewStmt); + n->view = $5; + n->view->relpersistence = $3; + n->aliases = $6; + n->query = $9; + n->replace = false; + n->options = $7; + n->sql_statement = NULL; + n->withCheckOption = (ViewCheckOption)$10; + n->viewSecurityOption = (ViewSecurityOption)$2; + $$ = (Node *) n; + } + | CREATE OR REPLACE view_security_expression OptTemp VIEW qualified_name opt_column_list opt_reloptions + AS SelectStmt opt_check_option + { + ViewStmt *n = makeNode(ViewStmt); + n->view = $7; + n->view->relpersistence = $5; + n->aliases = $8; + n->query = $11; + n->replace = true; + n->options = $9; + n->sql_statement = NULL; + n->withCheckOption = (ViewCheckOption)$12; + n->viewSecurityOption = (ViewSecurityOption)$4; + $$ = (Node *) n; + } + | CREATE opt_or_replace definer_expression view_security_expression OptTemp VIEW qualified_name opt_column_list opt_reloptions + AS SelectStmt opt_check_option + { + ViewStmt *n = makeNode(ViewStmt); + n->definer = $3; + n->view = $7; + n->view->relpersistence = $5; + n->aliases = $8; + n->query = $11; + n->replace = $2; + n->options = $9; + n->sql_statement = NULL; + n->withCheckOption = (ViewCheckOption)$12; + n->viewSecurityOption = (ViewSecurityOption)$4; $$ = (Node *) n; } ; @@ -28779,6 +28891,7 @@ unreserved_keyword: | SPACE | SPILL | SPLIT + | SQL_P | STABLE | STANDALONE_P | START diff --git a/src/common/interfaces/libpq/frontend_parser/gram.y b/src/common/interfaces/libpq/frontend_parser/gram.y index aa4f447e5a..8886ed6aaf 100755 --- a/src/common/interfaces/libpq/frontend_parser/gram.y +++ b/src/common/interfaces/libpq/frontend_parser/gram.y @@ -586,7 +586,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHIPPABLE SHOW SHUTDOWN SIBLINGS SIMILAR SIMPLE SIZE SKIP SLAVE SLICE SMALLDATETIME SMALLDATETIME_FORMAT_P SMALLINT SNAPSHOT SOME SOURCE_P SPACE SPILL SPLIT STABLE STANDALONE_P START STARTS STARTWITH STATEMENT STATEMENT_ID STATISTICS STDIN STDOUT STORAGE STORE_P STORED STRATIFY STREAM STRICT_P STRIP_P SUBPARTITION SUBPARTITIONS SUBSCRIPTION SUBSTRING SWLEVEL SWNOCYCLE SWROWNUM - SYMMETRIC SYNONYM SYSDATE SYSID SYSTEM_P SYS_REFCURSOR STARTING + SYMMETRIC SYNONYM SYSDATE SYSID SYSTEM_P SYS_REFCURSOR STARTING SQL_P TABLE TABLES TABLESAMPLE TABLESPACE TARGET TEMP TEMPLATE TEMPORARY TERMINATED TEXT_P THAN THEN TIME TIME_FORMAT_P TIMECAPSULE TIMESTAMP TIMESTAMP_FORMAT_P TIMESTAMPDIFF TINYINT TO TRAILING TRANSACTION TRANSFORM TREAT TRIGGER TRIM TRUE_P @@ -11961,6 +11961,7 @@ unreserved_keyword: | SOURCE_P | SPACE | SPILL + | SQL_P | STABLE | STANDALONE_P | START diff --git a/src/gausskernel/optimizer/commands/view.cpp b/src/gausskernel/optimizer/commands/view.cpp index ca191887b4..ab4674fb40 100644 --- a/src/gausskernel/optimizer/commands/view.cpp +++ b/src/gausskernel/optimizer/commands/view.cpp @@ -67,6 +67,18 @@ void validateWithCheckOption(const char *value) } } +/*--------------------------------------------------------------------- + * Validator for "view_sql_security" reloption on views. The allowed values + * are "local" and "cascaded". + */ +void validateViewSecurityOption(const char *value) +{ + if (value == NULL || (pg_strcasecmp(value, "definer") != 0 && pg_strcasecmp(value, "invoker") != 0)) { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid value for \"view_sql_security\" option"), + errdetail("Valid values are \"INVOKER\", and \"DEFINER\"."))); + } +} static void setEncryptedColumnRef(ColumnDef *def, TargetEntry *tle) { def->clientLogicColumnRef = (ClientLogicColumnRef*)palloc(sizeof(ClientLogicColumnRef)); @@ -655,6 +667,14 @@ ObjectAddress DefineView(ViewStmt* stmt, const char* queryString, bool send_remo else if (stmt->withCheckOption == CASCADED_CHECK_OPTION) stmt->options = lappend(stmt->options, makeDefElem("check_option", (Node*)makeString("cascaded"))); + /* + * For B format sql security option ,add it to the list of reloptions. + */ + if (stmt->viewSecurityOption == VIEW_SQL_SECURITY_DEFINER) + stmt->options = lappend(stmt->options, makeDefElem("view_sql_security", (Node*)makeString("definer"))); + else if (stmt->viewSecurityOption == VIEW_SQL_SECURITY_INVOKER) + stmt->options = lappend(stmt->options, makeDefElem("view_sql_security", (Node*)makeString("invoker"))); + /* * Check that the view is auto-updatable if WITH CHECK OPTION was * specified. diff --git a/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp b/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp index 7f00f2c245..6517938346 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp @@ -293,6 +293,30 @@ static bool acquireLocksOnSubLinks(Node* node, void* context) return expression_tree_walker(node, (bool (*)())acquireLocksOnSubLinks, context); } +/* + * Walker to pass down views' invoker info to RangeTableEnrty or FuncExpr in a Query + * B format mode use this feature + */ +static bool viewSecurityPassDown(Node* node, void* context) +{ + Oid* asUser = (Oid*)context; + if (node == NULL) + return false; + if (IsA(node, RangeTblEntry)) { + RangeTblEntry* rte = (RangeTblEntry*)node; + /* Do what we came for */ + if (rte->rtekind == RTE_RELATION) { + rte->checkAsUser = *asUser; + } + /* allow rangetable entry continue */ + return false; + } + /* + * Do NOT recurse into Query nodes, because fireRIRrules already processed + * subselects of subselects for us. + */ + return expression_tree_walker(node, (bool (*)())viewSecurityPassDown, context); +} /* * rewriteRuleAction - * Rewrite the rule action with appropriate qualifiers (taken from @@ -1866,6 +1890,8 @@ static Query* ApplyRetrieveRule(Query* parsetree, RewriteRule* rule, int rt_inde RangeTblEntry* subrte = NULL; RowMarkClause* rc = NULL; bool is_flt_frame = parsetree->is_flt_frame; + /* b_format view sql security option use */ + Oid checkAsUser = InvalidOid; if (list_length(rule->actions) != 1) { ereport(ERROR, (errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), errmsg("expected just one rule action"))); @@ -1975,6 +2001,35 @@ static Query* ApplyRetrieveRule(Query* parsetree, RewriteRule* rule, int rt_inde /* Pass the is_flt_frame from parsetree to rule_action */ rule_action->is_flt_frame = is_flt_frame; + /* + * in B format database ,deal with security options + * cause checkAsUser shoule be seted as definers' oid + * we should do some check here ,after expand views' query definition + */ + + /* get from here, before Recursive call this function, transform outside view first */ + rte = rt_fetch(rt_index, parsetree->rtable); + + if (DB_IS_CMPT(B_FORMAT) && rte->relkind == RELKIND_VIEW) { + if (RelationHasViewSecurityDefinerOption(relation)) { + checkAsUser = RelationGetOwner(relation); + } else if (RelationHasViewSecurityInvokerOption(relation)) { + /* for invoker ,if checkAsUser is seted as owner id, we shoule use it */ + checkAsUser = rte->checkAsUser == InvalidOid ? GetUserId() : rte->checkAsUser; + } else { + /* default is definer in b format database */ + checkAsUser = RelationGetOwner(relation); + } + if (checkAsUser != RelationGetOwner(relation)) { + /* set all relations' and functions' invoker information */ + query_tree_walker((Query *)rule_action, (bool (*)())viewSecurityPassDown, (void *)&checkAsUser, QTW_EXAMINE_RTES); + } + } else if (RelationHasViewSecurityOption(relation)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SQL Security option should only used in view and B format database"))); + } + /* * Recursively expand any view references inside the view. * diff --git a/src/gausskernel/storage/access/common/reloptions.cpp b/src/gausskernel/storage/access/common/reloptions.cpp index de569fef0d..bf185d32ba 100644 --- a/src/gausskernel/storage/access/common/reloptions.cpp +++ b/src/gausskernel/storage/access/common/reloptions.cpp @@ -520,6 +520,13 @@ static relopt_string stringRelOpts[] = { validateWithCheckOption, NULL }, + { + {"view_sql_security", "View has SQL SECURITY OPTION defined (INVOKER or DEFINER).", RELOPT_KIND_VIEW}, + 0, + true, + validateViewSecurityOption, + NULL + }, /* list terminator */ {{NULL}} }; @@ -1999,6 +2006,7 @@ bytea *default_reloptions(Datum reloptions, bool validate, relopt_kind kind) { "compress_diff_convert", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, compress) + offsetof(PageCompressOpts, compressDiffConvert)}, { "check_option", RELOPT_TYPE_STRING, offsetof(StdRdOptions, check_option_offset)}, + { "view_sql_security", RELOPT_TYPE_STRING, offsetof(StdRdOptions, view_security_option_offset)}, { "collate", RELOPT_TYPE_INT, offsetof(StdRdOptions, collate)} }; diff --git a/src/include/commands/view.h b/src/include/commands/view.h index 13a4263ee8..c7dcc1adbf 100644 --- a/src/include/commands/view.h +++ b/src/include/commands/view.h @@ -18,6 +18,7 @@ #include "nodes/parsenodes.h" extern void validateWithCheckOption(const char* value); +extern void validateViewSecurityOption(const char* value); extern ObjectAddress DefineView(ViewStmt* stmt, const char* queryString, bool send_remote = true, bool isFirstNode = true); extern bool IsViewTemp(ViewStmt* stmt, const char* queryString); extern void StoreViewQuery(Oid viewOid, Query *viewParse, bool replace); diff --git a/src/include/nodes/parsenodes_common.h b/src/include/nodes/parsenodes_common.h index 681ba3c652..538411f904 100644 --- a/src/include/nodes/parsenodes_common.h +++ b/src/include/nodes/parsenodes_common.h @@ -2116,6 +2116,12 @@ typedef enum ViewCheckOption { CASCADED_CHECK_OPTION } ViewCheckOption; +typedef enum ViewSecurityOption { + VIEW_SQL_SECURITY_NONE, + VIEW_SQL_SECURITY_DEFINER, + VIEW_SQL_SECURITY_INVOKER +} ViewSecurityOption; + typedef struct ViewStmt { NodeTag type; RangeVar *view; /* the view to be created */ @@ -2130,6 +2136,7 @@ typedef struct ViewStmt { char *mv_sql; char* definer; bool is_alter; + ViewSecurityOption viewSecurityOption; /* sql secureity option, b format */ #ifdef ENABLE_MULTIPLE_NODES struct PGXCSubCluster* subcluster; /* subcluster of table */ #endif diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index b8b24176eb..352b01b07f 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -570,6 +570,7 @@ PG_KEYWORD("source", SOURCE_P, UNRESERVED_KEYWORD) PG_KEYWORD("space", SPACE, UNRESERVED_KEYWORD) PG_KEYWORD("spill", SPILL, UNRESERVED_KEYWORD) PG_KEYWORD("split", SPLIT, UNRESERVED_KEYWORD) +PG_KEYWORD("sql", SQL_P, UNRESERVED_KEYWORD) PG_KEYWORD("stable", STABLE, UNRESERVED_KEYWORD) PG_KEYWORD("standalone", STANDALONE_P, UNRESERVED_KEYWORD) PG_KEYWORD("start", START, UNRESERVED_KEYWORD) diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index de94d04d62..1a1b0d8a76 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -408,6 +408,7 @@ typedef struct StdRdOptions { bool on_commit_delete_rows; /* global temp table */ PageCompressOpts compress; /* page compress related reloptions. */ int check_option_offset; /* for views */ + int view_security_option_offset; /* for views */ Oid collate; /* table's default collation in b format. */ } StdRdOptions; @@ -456,7 +457,36 @@ typedef struct StdRdOptions { #define RelationGetTargetPageFreeSpacePrune(relation, defaultff) \ (BLCKSZ * (100 - 0.9 * RelationGetFillFactor(relation, defaultff)) / 100) +/* + * RelationHasViewSecurityOption + * Returns true if the relation is a view defined with sql security + * or the cascaded check option. + */ +#define RelationHasViewSecurityOption(relation) \ + ((relation)->rd_options && \ + ((StdRdOptions *) (relation)->rd_options)->view_security_option_offset != 0) +/* + * RelationHasViewSecurityDefinerOption + * Returns true if the relation is a view defined sql security definer + * option. + */ +#define RelationHasViewSecurityDefinerOption(relation) \ + (RelationHasViewSecurityOption(relation) && \ + strcmp((char *) (relation)->rd_options + \ + ((StdRdOptions *) (relation)->rd_options)->view_security_option_offset, \ + "definer") == 0) + +/* + * RelationHasViewSecurityInvokerOption + * Returns true if the relation is a view defined with sql security invoker + * option. + */ +#define RelationHasViewSecurityInvokerOption(relation) \ + (RelationHasViewSecurityOption(relation) && \ + strcmp((char *) (relation)->rd_options + \ + ((StdRdOptions *) (relation)->rd_options)->view_security_option_offset, \ + "invoker") == 0) /* * RelationIsSecurityView * Returns whether the relation is security view, or not diff --git a/src/test/regress/expected/b_compatibility.out b/src/test/regress/expected/b_compatibility.out index 9164b96496..4d7e47f802 100644 --- a/src/test/regress/expected/b_compatibility.out +++ b/src/test/regress/expected/b_compatibility.out @@ -2481,7 +2481,249 @@ select select_into_null_func('aaa') is null; drop table test_table_030; drop function select_into_null_func; reset behavior_compat_options; +-- view sql security test +create database db_a1144425 dbcompatibility 'B'; +\c db_a1144425; +create user use_a_1144425 identified by 'A@123456'; +create user use_b_1144425 identified by 'A@123456'; +create table sql_security_1144425(id int,cal int); +insert into sql_security_1144425 values(1,1); +insert into sql_security_1144425 values(2,2); +insert into sql_security_1144425 values(3,3); +--user a can create view in shcema +grant all on schema public to use_a_1144425; +-- definer +create definer=use_a_1144425 sql security definer view v_1144425_2 as select * from sql_security_1144425; +-- invoker +create definer=use_a_1144425 sql security invoker view v_1144425_1 as select * from sql_security_1144425; +--user a call view shoule be failed cause table operation +set role use_a_1144425 password 'A@123456'; +select * from sql_security_1144425 order by 1,2; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +-- call definer\invoker view shoule be failed +select * from v_1144425_2 order by 1,2; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +select * from v_1144425_1 order by 1,2; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +-- root user shoule success in invoker ,failed in definer +reset role; +select * from v_1144425_2 order by 1,2; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +select * from v_1144425_1 order by 1,2; + id | cal +----+----- + 1 | 1 + 2 | 2 + 3 | 3 +(3 rows) + +--user b call view shoule filed , no view permission both +set role use_b_1144425 password 'A@123456'; +select * from v_1144425_2 order by 1,2; +ERROR: permission denied for relation v_1144425_2 +DETAIL: N/A +select * from v_1144425_1 order by 1,2; +ERROR: permission denied for relation v_1144425_1 +DETAIL: N/A +--give user b permission of view , no table permission both +reset role; +grant all on table v_1144425_2 to use_b_1144425; +grant all on table v_1144425_1 to use_b_1144425; +set role use_b_1144425 password 'A@123456'; +select * from v_1144425_2 order by 1,2; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +select * from v_1144425_1 order by 1,2; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +-- give user b permission of table , invoker success +reset role; +grant all on table sql_security_1144425 to use_b_1144425; +set role use_b_1144425 password 'A@123456'; +select * from v_1144425_2 order by 1,2; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +select * from v_1144425_1 order by 1,2; + id | cal +----+----- + 1 | 1 + 2 | 2 + 3 | 3 +(3 rows) + +-- give user a permission of table , definer success +reset role; +grant all on table sql_security_1144425 to use_a_1144425; +set role use_b_1144425 password 'A@123456'; +select * from v_1144425_2 order by 1,2; + id | cal +----+----- + 1 | 1 + 2 | 2 + 3 | 3 +(3 rows) + +-- revoke user b permision of table , definer success, invoker failed +reset role; +revoke all on table sql_security_1144425 from use_b_1144425; +set role use_b_1144425 password 'A@123456'; +select * from v_1144425_2 order by 1,2; + id | cal +----+----- + 1 | 1 + 2 | 2 + 3 | 3 +(3 rows) + +select * from v_1144425_1 order by 1,2; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +reset role; +-- test auth passdown for multi view; +--clear all +drop table sql_security_1144425 cascade; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to view v_1144425_2 +drop cascades to view v_1144425_1 +drop user use_a_1144425 cascade; +drop user use_b_1144425 cascade; +create user use_a_1144425 identified by 'A@123456'; +create user use_b_1144425 identified by 'A@123456'; +create table sql_security_1144425(id int,cal int); +insert into sql_security_1144425 values(1,1); +--user a can create view in shcema +grant all on schema public to use_a_1144425; +-- definer view v_d_inner +create definer=use_a_1144425 sql security definer view v_d_inner as select * from sql_security_1144425; +-- invoker view v_i_inner +create definer=use_a_1144425 sql security invoker view v_i_inner as select * from sql_security_1144425; +-- definer view v_d_d_outer +create definer=use_a_1144425 sql security definer view v_d_d_outer as select * from v_d_inner; +-- definer view v_d_i_outer +create definer=use_a_1144425 sql security definer view v_d_i_outer as select * from v_i_inner; +-- definer view v_i_d_outer +create definer=use_a_1144425 sql security invoker view v_i_d_outer as select * from v_d_inner; +-- definer view v_i_i_outer +create definer=use_a_1144425 sql security invoker view v_i_i_outer as select * from v_i_inner; +-- root could only success in vii +select * from v_i_i_outer; + id | cal +----+----- + 1 | 1 +(1 row) + +select * from v_i_d_outer; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +select * from v_d_i_outer; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +select * from v_d_d_outer; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +-- give user b permisson of view; +grant all on table v_i_i_outer to use_b_1144425; +grant all on table v_i_d_outer to use_b_1144425; +grant all on table v_d_i_outer to use_b_1144425; +grant all on table v_d_d_outer to use_b_1144425; +-- user b got error of table ,in vdi ,vdd. vii ,vid got view error +set role use_b_1144425 password 'A@123456'; +select * from v_i_i_outer; +ERROR: permission denied for relation v_i_inner +DETAIL: N/A +select * from v_i_d_outer; +ERROR: permission denied for relation v_d_inner +DETAIL: N/A +select * from v_d_i_outer; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +select * from v_d_d_outer; +ERROR: permission denied for relation sql_security_1144425 +DETAIL: N/A +--now give user a permission of table +reset role; +grant all on table sql_security_1144425 to use_a_1144425; +-- root could success in all +select * from v_i_i_outer; + id | cal +----+----- + 1 | 1 +(1 row) + +select * from v_i_d_outer; + id | cal +----+----- + 1 | 1 +(1 row) + +select * from v_d_i_outer; + id | cal +----+----- + 1 | 1 +(1 row) + +select * from v_d_d_outer; + id | cal +----+----- + 1 | 1 +(1 row) + +-- user b only success in vdd , vdi +set role use_b_1144425 password 'A@123456'; +select * from v_i_i_outer; +ERROR: permission denied for relation v_i_inner +DETAIL: N/A +select * from v_i_d_outer; +ERROR: permission denied for relation v_d_inner +DETAIL: N/A +select * from v_d_i_outer; + id | cal +----+----- + 1 | 1 +(1 row) + +select * from v_d_d_outer; + id | cal +----+----- + 1 | 1 +(1 row) + +reset role; +-- coverage test +create view v1 as select * from sql_security_1144425; +create or replace view v1 as select * from sql_security_1144425; +create or replace definer=use_a_1144425 view v1 as select * from sql_security_1144425; +create sql security invoker view v2 as select * from sql_security_1144425; +create or replace sql security invoker view v2 as select * from sql_security_1144425; +create or replace definer=use_a_1144425 sql security invoker view v2 as select * from sql_security_1144425; +alter view v1 as select * from sql_security_1144425; +alter definer=use_a_1144425 view v1 as select * from sql_security_1144425; +alter sql security definer view v2 as select * from sql_security_1144425; +alter definer=use_a_1144425 sql security definer view v2 as select * from sql_security_1144425; +alter sql security definer view v1 as select * from sql_security_1144425; +alter definer=use_a_1144425 sql security definer view v1 as select * from sql_security_1144425; +--pg_dump test +--see view_definer_test.source +--clear all +drop table sql_security_1144425 cascade; +NOTICE: drop cascades to 8 other objects +DETAIL: drop cascades to view v_d_inner +drop cascades to view v_d_d_outer +drop cascades to view v_i_d_outer +drop cascades to view v_i_inner +drop cascades to view v_d_i_outer +drop cascades to view v_i_i_outer +drop cascades to view v2 +drop cascades to view v1 +drop user use_a_1144425 cascade; +drop user use_b_1144425 cascade; +-- sql security end \c regression drop database b_cmpt_db; +drop database db_a1144425; DROP USER test_c; DROP USER test_d; diff --git a/src/test/regress/sql/b_compatibility.sql b/src/test/regress/sql/b_compatibility.sql index 86f4fd19c0..3ed919fd89 100644 --- a/src/test/regress/sql/b_compatibility.sql +++ b/src/test/regress/sql/b_compatibility.sql @@ -1500,7 +1500,146 @@ drop table test_table_030; drop function select_into_null_func; reset behavior_compat_options; +-- view sql security test + +create database db_a1144425 dbcompatibility 'B'; + +\c db_a1144425; +create user use_a_1144425 identified by 'A@123456'; +create user use_b_1144425 identified by 'A@123456'; +create table sql_security_1144425(id int,cal int); +insert into sql_security_1144425 values(1,1); +insert into sql_security_1144425 values(2,2); +insert into sql_security_1144425 values(3,3); +--user a can create view in shcema +grant all on schema public to use_a_1144425; + +-- definer +create definer=use_a_1144425 sql security definer view v_1144425_2 as select * from sql_security_1144425; +-- invoker +create definer=use_a_1144425 sql security invoker view v_1144425_1 as select * from sql_security_1144425; + +--user a call view shoule be failed cause table operation +set role use_a_1144425 password 'A@123456'; +select * from sql_security_1144425 order by 1,2; +-- call definer\invoker view shoule be failed +select * from v_1144425_2 order by 1,2; +select * from v_1144425_1 order by 1,2; +-- root user shoule success in invoker ,failed in definer +reset role; +select * from v_1144425_2 order by 1,2; +select * from v_1144425_1 order by 1,2; +--user b call view shoule filed , no view permission both +set role use_b_1144425 password 'A@123456'; +select * from v_1144425_2 order by 1,2; +select * from v_1144425_1 order by 1,2; +--give user b permission of view , no table permission both +reset role; +grant all on table v_1144425_2 to use_b_1144425; +grant all on table v_1144425_1 to use_b_1144425; +set role use_b_1144425 password 'A@123456'; +select * from v_1144425_2 order by 1,2; +select * from v_1144425_1 order by 1,2; +-- give user b permission of table , invoker success +reset role; +grant all on table sql_security_1144425 to use_b_1144425; +set role use_b_1144425 password 'A@123456'; +select * from v_1144425_2 order by 1,2; +select * from v_1144425_1 order by 1,2; +-- give user a permission of table , definer success +reset role; +grant all on table sql_security_1144425 to use_a_1144425; +set role use_b_1144425 password 'A@123456'; +select * from v_1144425_2 order by 1,2; +-- revoke user b permision of table , definer success, invoker failed +reset role; +revoke all on table sql_security_1144425 from use_b_1144425; +set role use_b_1144425 password 'A@123456'; +select * from v_1144425_2 order by 1,2; +select * from v_1144425_1 order by 1,2; +reset role; +-- test auth passdown for multi view; +--clear all +drop table sql_security_1144425 cascade; +drop user use_a_1144425 cascade; +drop user use_b_1144425 cascade; + +create user use_a_1144425 identified by 'A@123456'; +create user use_b_1144425 identified by 'A@123456'; +create table sql_security_1144425(id int,cal int); +insert into sql_security_1144425 values(1,1); +--user a can create view in shcema +grant all on schema public to use_a_1144425; +-- definer view v_d_inner +create definer=use_a_1144425 sql security definer view v_d_inner as select * from sql_security_1144425; +-- invoker view v_i_inner +create definer=use_a_1144425 sql security invoker view v_i_inner as select * from sql_security_1144425; +-- definer view v_d_d_outer +create definer=use_a_1144425 sql security definer view v_d_d_outer as select * from v_d_inner; +-- definer view v_d_i_outer +create definer=use_a_1144425 sql security definer view v_d_i_outer as select * from v_i_inner; +-- definer view v_i_d_outer +create definer=use_a_1144425 sql security invoker view v_i_d_outer as select * from v_d_inner; +-- definer view v_i_i_outer +create definer=use_a_1144425 sql security invoker view v_i_i_outer as select * from v_i_inner; +-- root could only success in vii +select * from v_i_i_outer; +select * from v_i_d_outer; +select * from v_d_i_outer; +select * from v_d_d_outer; +-- give user b permisson of view; +grant all on table v_i_i_outer to use_b_1144425; +grant all on table v_i_d_outer to use_b_1144425; +grant all on table v_d_i_outer to use_b_1144425; +grant all on table v_d_d_outer to use_b_1144425; +-- user b got error of table ,in vdi ,vdd. vii ,vid got view error +set role use_b_1144425 password 'A@123456'; +select * from v_i_i_outer; +select * from v_i_d_outer; +select * from v_d_i_outer; +select * from v_d_d_outer; +--now give user a permission of table +reset role; +grant all on table sql_security_1144425 to use_a_1144425; +-- root could success in all +select * from v_i_i_outer; +select * from v_i_d_outer; +select * from v_d_i_outer; +select * from v_d_d_outer; +-- user b only success in vdd , vdi +set role use_b_1144425 password 'A@123456'; +select * from v_i_i_outer; +select * from v_i_d_outer; +select * from v_d_i_outer; +select * from v_d_d_outer; +reset role; +-- coverage test +create view v1 as select * from sql_security_1144425; +create or replace view v1 as select * from sql_security_1144425; +create or replace definer=use_a_1144425 view v1 as select * from sql_security_1144425; +create sql security invoker view v2 as select * from sql_security_1144425; +create or replace sql security invoker view v2 as select * from sql_security_1144425; +create or replace definer=use_a_1144425 sql security invoker view v2 as select * from sql_security_1144425; +alter view v1 as select * from sql_security_1144425; +alter definer=use_a_1144425 view v1 as select * from sql_security_1144425; +alter sql security definer view v2 as select * from sql_security_1144425; +alter definer=use_a_1144425 sql security definer view v2 as select * from sql_security_1144425; +alter sql security definer view v1 as select * from sql_security_1144425; +alter definer=use_a_1144425 sql security definer view v1 as select * from sql_security_1144425; + +--pg_dump test +--see view_definer_test.source + +--clear all +drop table sql_security_1144425 cascade; +drop user use_a_1144425 cascade; +drop user use_b_1144425 cascade; + +-- sql security end + + \c regression drop database b_cmpt_db; +drop database db_a1144425; DROP USER test_c; DROP USER test_d; -- Gitee