add common table expression in MySQL (#314)

This commit is contained in:
Aristoeu
2021-09-02 14:24:01 +08:00
committed by wangzelin.wzl
parent 5f209b30fe
commit 153f16ad8a
33 changed files with 136129 additions and 40650 deletions

View File

@ -12013,6 +12013,16 @@ static struct ObStrErrorInit
ORACLE_ERRNO[-OB_ERR_ILL_NAME_STRING] = 21560;
ORACLE_STR_ERROR[-OB_ERR_ILL_NAME_STRING] = "ORA-21560: unexpected name string '%.*s'";
ORACLE_STR_USER_ERROR[-OB_ERR_ILL_NAME_STRING] = "ORA-21560: unexpected name string '%.*s'";
ERROR_NAME[-OB_ERR_CTE_NEED_QUERY_BLOCKS] = "OB_ERR_CTE_NEED_QUERY_BLOCKS";
ERROR_CAUSE[-OB_ERR_CTE_NEED_QUERY_BLOCKS] = "Internal Error";
ERROR_SOLUTION[-OB_ERR_CTE_NEED_QUERY_BLOCKS] = "Contact OceanBase Support";
MYSQL_ERRNO[-OB_ERR_CTE_NEED_QUERY_BLOCKS] = -1;
SQLSTATE[-OB_ERR_CTE_NEED_QUERY_BLOCKS] = "HY000";
STR_ERROR[-OB_ERR_CTE_NEED_QUERY_BLOCKS] = "Recursive Common Table Expression should have one or more non-recursive query blocks followed by one or more recursive ones";
STR_USER_ERROR[-OB_ERR_CTE_NEED_QUERY_BLOCKS] = "Recursive Common Table Expression should have one or more non-recursive query blocks followed by one or more recursive ones: %s";
ORACLE_ERRNO[-OB_ERR_CTE_NEED_QUERY_BLOCKS] = 600;
ORACLE_STR_ERROR[-OB_ERR_CTE_NEED_QUERY_BLOCKS] = "ORA-00600: internal error code, arguments: -5933, Recursive Common Table Expression should have one or more non-recursive query blocks followed by one or more recursive ones";
ORACLE_STR_USER_ERROR[-OB_ERR_CTE_NEED_QUERY_BLOCKS] = "ORA-00600: internal error code, arguments: -5933, Recursive Common Table Expression should have one or more non-recursive query blocks followed by one or more recursive ones: %s";
ERROR_NAME[-OB_ERR_INCORRECT_VALUE_FOR_FUNCTION] = "OB_ERR_INCORRECT_VALUE_FOR_FUNCTION";
ERROR_CAUSE[-OB_ERR_INCORRECT_VALUE_FOR_FUNCTION] = "Internal Error";
ERROR_SOLUTION[-OB_ERR_INCORRECT_VALUE_FOR_FUNCTION] = "Contact OceanBase Support";

View File

@ -1320,6 +1320,7 @@ DEFINE_ERROR_EXT(OB_ERR_WINDOW_NAME_IS_NOT_DEFINE, -5929, -1, "HY000", "Window n
DEFINE_ORACLE_ERROR(OB_ERR_OPEN_CURSORS_EXCEEDED, -5930, -1, "HY000", "maximum open cursors exceeded", 1000, "maximum open cursors exceeded");
DEFINE_ORACLE_ERROR(OB_ERR_ARG_INVALID, -5931, -1, "HY000", "argument is null, invalid, or out of range", 21560, "argument %.*s is null, invalid, or out of range");
DEFINE_ORACLE_ERROR(OB_ERR_ILL_NAME_STRING, -5932, -1, "HY000", "unexpected name string", 21560, "unexpected name string '%.*s'");
DEFINE_ERROR_EXT(OB_ERR_CTE_NEED_QUERY_BLOCKS, -5933, -1, "HY000", "Recursive Common Table Expression should have one or more non-recursive query blocks followed by one or more recursive ones", "Recursive Common Table Expression should have one or more non-recursive query blocks followed by one or more recursive ones: %s");
DEFINE_ERROR_EXT(OB_ERR_INCORRECT_VALUE_FOR_FUNCTION, -5936, ER_WRONG_VALUE_FOR_TYPE, "HY000", "Incorrect value for function", "Incorrect %.*s value: '%.*s' for function %.*s");
DEFINE_ERROR_EXT(OB_ERR_USER_EXCEED_RESOURCE, -5967, 1226, "42000", "User has exceeded the resource", "User '%.*s' has exceeded the '%s' resource (current value: %lu)");

View File

@ -1026,6 +1026,7 @@ constexpr int OB_ERR_WINDOW_NAME_IS_NOT_DEFINE = -5929;
constexpr int OB_ERR_OPEN_CURSORS_EXCEEDED = -5930;
constexpr int OB_ERR_ARG_INVALID = -5931;
constexpr int OB_ERR_ILL_NAME_STRING = -5932;
constexpr int OB_ERR_CTE_NEED_QUERY_BLOCKS = -5933;
constexpr int OB_ERR_INCORRECT_VALUE_FOR_FUNCTION = -5936;
constexpr int OB_ERR_USER_EXCEED_RESOURCE = -5967;
constexpr int OB_TRANSACTION_SET_VIOLATION = -6001;
@ -2428,6 +2429,7 @@ constexpr int OB_ERR_INVALID_DATE_MSG_FMT_V2 = -4219;
#define OB_ERR_OPEN_CURSORS_EXCEEDED__USER_ERROR_MSG "maximum open cursors exceeded"
#define OB_ERR_ARG_INVALID__USER_ERROR_MSG "argument is null, invalid, or out of range"
#define OB_ERR_ILL_NAME_STRING__USER_ERROR_MSG "unexpected name string"
#define OB_ERR_CTE_NEED_QUERY_BLOCKS__USER_ERROR_MSG "Recursive Common Table Expression should have one or more non-recursive query blocks followed by one or more recursive ones: %s"
#define OB_ERR_INCORRECT_VALUE_FOR_FUNCTION__USER_ERROR_MSG "Incorrect %.*s value: '%.*s' for function %.*s"
#define OB_ERR_USER_EXCEED_RESOURCE__USER_ERROR_MSG "User '%.*s' has exceeded the '%s' resource (current value: %lu)"
#define OB_TRANSACTION_SET_VIOLATION__USER_ERROR_MSG "Transaction set changed during the execution"
@ -3848,6 +3850,7 @@ constexpr int OB_ERR_INVALID_DATE_MSG_FMT_V2 = -4219;
#define OB_ERR_OPEN_CURSORS_EXCEEDED__ORA_USER_ERROR_MSG "ORA-01000: maximum open cursors exceeded"
#define OB_ERR_ARG_INVALID__ORA_USER_ERROR_MSG "ORA-21560: argument %.*s is null, invalid, or out of range"
#define OB_ERR_ILL_NAME_STRING__ORA_USER_ERROR_MSG "ORA-21560: unexpected name string '%.*s'"
#define OB_ERR_CTE_NEED_QUERY_BLOCKS__ORA_USER_ERROR_MSG "ORA-00600: internal error code, arguments: -5933, Recursive Common Table Expression should have one or more non-recursive query blocks followed by one or more recursive ones: %s"
#define OB_ERR_INCORRECT_VALUE_FOR_FUNCTION__ORA_USER_ERROR_MSG "ORA-00600: internal error code, arguments: -5936, Incorrect %.*s value: '%.*s' for function %.*s"
#define OB_ERR_USER_EXCEED_RESOURCE__ORA_USER_ERROR_MSG "ORA-00600: internal error code, arguments: -5967, User '%.*s' has exceeded the '%s' resource (current value: %lu)"
#define OB_TRANSACTION_SET_VIOLATION__ORA_USER_ERROR_MSG "ORA-00600: internal error code, arguments: -6001, Transaction set changed during the execution"

View File

@ -272,6 +272,7 @@ READ { REPUT_TOKEN_NEG_SIGN(READ); }
READ_WRITE { REPUT_TOKEN_NEG_SIGN(READ_WRITE); }
READS { REPUT_TOKEN_NEG_SIGN(READS); }
REAL { REPUT_TOKEN_NEG_SIGN(REAL); }
RECURSIVE { REPUT_TOKEN_NEG_SIGN(RECURSIVE); }
RELEASE { REPUT_TOKEN_NEG_SIGN(RELEASE); }
REFERENCES { REPUT_TOKEN_NEG_SIGN(REFERENCES); }
REGEXP { REPUT_TOKEN_NEG_SIGN(REGEXP); }

View File

@ -331,6 +331,7 @@ END_P SET_VAR DELIMITER
%type <node> insert_vals_list insert_vals value_or_values
%type <node> select_with_parens select_no_parens select_clause select_into no_table_select_with_order_and_limit simple_select_with_order_and_limit select_with_parens_with_order_and_limit select_clause_set select_clause_set_left select_clause_set_right select_clause_set_with_order_and_limit
%type <node> simple_select no_table_select limit_clause select_expr_list
%type <node> with_select with_clause with_list common_table_expr opt_column_alias_name_list alias_name_list column_alias_name
%type <node> opt_where opt_hint_value opt_groupby opt_rollup opt_order_by order_by opt_having groupby_clause
%type <node> opt_limit_clause limit_expr opt_for_update opt_for_update_wait
%type <node> sort_list sort_key opt_asc_desc sort_list_for_group_by sort_key_for_group_by opt_asc_desc_for_group_by opt_column_id
@ -6645,6 +6646,10 @@ select_no_parens opt_when
{
$$ = $1;
}
| with_select
{
$$ = $1;
}
;
// for select_into
@ -6667,7 +6672,10 @@ select_no_parens into_clause
select_with_parens:
'(' select_no_parens ')' { $$ = $2; }
| '(' select_with_parens ')' { $$ = $2; }
;
| '(' with_select ')'
{
$$ = $2;
};
select_no_parens:
select_clause opt_for_update
@ -8880,6 +8888,135 @@ OUTER { $$ = NULL; }
| /* EMPTY */ { $$ = NULL; }
;
/*****************************************************************************
*
* with clause (common table expression) (Mysql CTE grammer implement)
*
*
*****************************************************************************/
with_select:
with_clause select_no_parens opt_when
{
$$ = $2;
$$->children_[PARSE_SELECT_WHEN] = $3;
if (NULL == $$->children_[PARSE_SELECT_FOR_UPD] && NULL != $3)
{
malloc_terminal_node($$->children_[PARSE_SELECT_FOR_UPD], result->malloc_pool_, T_INT);
$$->children_[PARSE_SELECT_FOR_UPD]->value_ = -1;
}
$$->children_[PARSE_SELECT_WITH] = $1;
}
| with_clause select_with_parens
{
$$ = $2;
$$->children_[PARSE_SELECT_WITH] = $1;
}
;
with_clause:
WITH with_list
{
ParseNode *with_list = NULL;
merge_nodes(with_list, result, T_WITH_CLAUSE_LIST, $2);
$$ = with_list;
$$->value_ = 0;
}
|
WITH RECURSIVE with_list
{
ParseNode *with_list = NULL;
merge_nodes(with_list, result, T_WITH_CLAUSE_LIST, $3);
$$ = with_list;
$$->value_ = 1;
}/*
|
WITH RECURSIVE common_table_expr
{
$$ = $3;
$$->value_ = 0;
}*/
;
with_list:
with_list ',' common_table_expr
{
malloc_non_terminal_node($$, result->malloc_pool_, T_LINK_NODE, 2, $1, $3);
}
|common_table_expr
{
$$ = $1;
}
;
common_table_expr:
relation_name opt_column_alias_name_list AS '(' select_no_parens ')'
{
malloc_non_terminal_node($$, result->malloc_pool_, T_WITH_CLAUSE_AS, 5, $1, $2, $5, NULL, NULL);
}
| relation_name opt_column_alias_name_list AS '(' with_select ')'
{
malloc_non_terminal_node($$, result->malloc_pool_, T_WITH_CLAUSE_AS, 5, $1, $2, $5, NULL, NULL);
}
| relation_name opt_column_alias_name_list AS '(' select_with_parens ')'
{
if ($5->children_[PARSE_SELECT_ORDER] != NULL && $5->children_[PARSE_SELECT_FETCH] == NULL) {
yyerror(NULL, result, "only order by clause can't occur subquery\n");
YYABORT_PARSE_SQL_ERROR;
} else {
malloc_non_terminal_node($$, result->malloc_pool_, T_WITH_CLAUSE_AS, 5, $1, $2, $5, NULL, NULL);
}
}
;
opt_column_alias_name_list:
'(' alias_name_list ')'
{
ParseNode *col_alias_list = NULL;
merge_nodes(col_alias_list, result, T_COLUMN_LIST, $2);
$$ = col_alias_list;
}
|/*EMPTY*/
{ $$ = NULL; }
;
alias_name_list:
column_alias_name
{
$$ = $1;
}
|alias_name_list ',' column_alias_name
{
malloc_non_terminal_node($$, result->malloc_pool_, T_LINK_NODE, 2, $1, $3);
}
;
column_alias_name:
column_name
{
$$ = $1;
}
;
/*
search_list:
search_key
{ $$ = $1; }
| search_list ',' search_key
{ malloc_non_terminal_node($$, result->malloc_pool_, T_LINK_NODE, 2, $1, $3); }
;
search_key:
column_name opt_asc_desc
{
malloc_non_terminal_node($$, result->malloc_pool_, T_SORT_KEY, 2, $1, $2);
}
;*/
/*****************************************************************************
*
* CREATE OUTLINE grammar
@ -13189,6 +13326,7 @@ ACCOUNT
| REBUILD
| RECOVER
| RECOVERY
| RECURSIVE
| RECYCLE
| RECYCLEBIN
| ROTATE

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -98,6 +98,7 @@ int ObSelectResolver::do_resolve_set_query_in_cte(const ParseNode& parse_tree)
right_resolver.set_current_level(current_level_);
right_resolver.set_in_set_query(true);
right_resolver.set_parent_namespace_resolver(parent_namespace_resolver_);
right_resolver.set_resolver(&left_resolver);
OC((left_resolver.set_cte_ctx)(cte_ctx_));
OC((right_resolver.set_cte_ctx)(cte_ctx_));
@ -124,6 +125,7 @@ int ObSelectResolver::do_resolve_set_query_in_cte(const ParseNode& parse_tree)
LOG_WARN("failed to identify anchor member", K(ret));
} else if (!need_swap_child) {
left_select_stmt = identify_anchor_resolver.get_child_stmt();
right_resolver.set_resolver(&identify_anchor_resolver);
} else {
left_member = PARSE_SELECT_LATER;
right_member = PARSE_SELECT_FORMER;
@ -136,6 +138,26 @@ int ObSelectResolver::do_resolve_set_query_in_cte(const ParseNode& parse_tree)
}
}
if (OB_SUCC(ret)) {
if (!params_.has_cte_param_list_ && right_resolver.saved_left_resolver != NULL &&
!right_resolver.saved_left_resolver->cte_ctx_.cte_col_names_.empty()) {
right_resolver.cte_ctx_.cte_col_names_.reset();
cte_ctx_.cte_col_names_.reset();
for (int64_t i = 0; OB_SUCC(ret) && i < right_resolver.saved_left_resolver->cte_ctx_.cte_col_names_.count(); ++i) {
if (OB_FAIL(right_resolver.cte_ctx_.cte_col_names_.push_back(
right_resolver.saved_left_resolver->cte_ctx_.cte_col_names_.at(i)))) { // to right resolver
ret = OB_ERR_UNEXPECTED;
LOG_WARN("pass cte column name to child resolver failed");
}
if (OB_FAIL(cte_ctx_.cte_col_names_.push_back(
right_resolver.saved_left_resolver->cte_ctx_.cte_col_names_.at(i)))) { // to parent resolver
ret = OB_ERR_UNEXPECTED;
LOG_WARN("pass cte column name to child resolver failed");
}
}
}
}
if (OB_FAIL(ret)) {
} else if (OB_FALSE_IT(right_resolver.cte_ctx_.set_recursive_right_branch(
left_select_stmt, parse_tree.children_[left_member], !select_stmt->is_set_distinct()))) {
@ -449,6 +471,7 @@ int ObSelectResolver::set_cte_ctx(ObCteResolverCtx& cte_ctx, bool copy_col_name
{
int ret = OB_SUCCESS;
cte_ctx_ = cte_ctx;
cte_ctx_.is_recursive_cte_ = false;
cte_ctx_.cte_col_names_.reset();
cte_ctx_.is_cte_subquery_ = in_subquery;
if (cte_ctx_.is_with_resolver())
@ -1517,6 +1540,13 @@ int ObSelectResolver::resolve_field_list(const ParseNode& node)
} else { /*do nothing*/
}
// add for cte:
if (OB_SUCC(ret) && !params_.has_cte_param_list_) {
if (OB_FAIL(cte_ctx_.cte_col_names_.push_back(select_item.alias_name_))) {
LOG_WARN("push back column alia name failed", K(ret));
}
}
} // end for
// for aggr exprs in having clause to remove duplicate;
@ -2130,12 +2160,12 @@ int ObSelectResolver::resolve_with_clause(const ParseNode* node, bool same_level
ObSelectStmt* select_stmt = NULL;
TableItem* table_item = NULL;
bool duplicate_name = false;
if (NULL != node && cte_ctx_.is_with_resolver() && same_level == false){
LOG_DEBUG("same_level = false, oracle not supported, mysql feature");
}
if (OB_ISNULL(select_stmt = get_select_stmt())) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret), K(select_stmt), K_(node->type));
} else if (NULL != node && cte_ctx_.is_with_resolver() && same_level == false) {
ret = OB_ERR_UNSUPPORTED_USE_OF_CTE;
LOG_WARN("invalid argument, oracle cte do not support a with clause nest", K(select_stmt), K_(node->type));
} else if (OB_ISNULL(node)) {
// do nothing
} else if (OB_UNLIKELY(node->type_ != T_WITH_CLAUSE_LIST)) {
@ -2144,6 +2174,10 @@ int ObSelectResolver::resolve_with_clause(const ParseNode* node, bool same_level
LOG_WARN("resolver with_clause_as met unexpected node type", K_(node->type));
} else {
int num_child = node->num_child_;
if (node->value_ == 0)
params_.has_recursive_word = false;
else
params_.has_recursive_word = true;
for (int64_t i = 0; OB_SUCC(ret) && i < num_child; ++i) {
// alias tblname [(alia colname1, alia colname2)](subquery) [search clause][cycle clause]
ParseNode* child_node = node->children_[i];
@ -2981,7 +3015,7 @@ int ObSelectResolver::add_fake_schema(ObSelectStmt* left_stmt)
ObColumnRefRawExpr* select_expr = static_cast<ObColumnRefRawExpr*>(expr);
ObColumnSchemaV2* new_col = static_cast<ObColumnSchemaV2*>(allocator_->alloc(sizeof(ObColumnSchemaV2)));
new_col = new (new_col) ObColumnSchemaV2(allocator_);
new_col->set_column_name(cte_ctx_.cte_col_names_.at(i));
new_col->set_column_name(saved_left_resolver->cte_ctx_.cte_col_names_.at(i));
new_col->set_tenant_id(tbl_schema->get_tenant_id());
new_col->set_table_id(magic_table_id);
new_col->set_column_id(magic_col_id + i);
@ -3010,7 +3044,9 @@ int ObSelectResolver::get_opt_alias_colnames_for_recursive_cte(ObIArray<ObString
int ret = OB_SUCCESS;
if (OB_ISNULL(parse_tree)) {
LOG_DEBUG("the opt_alias_colnames parse tree is null");
params_.has_cte_param_list_ = false;
} else {
params_.has_cte_param_list_ = true;
int64_t alias_num = parse_tree->num_child_;
for (int64_t i = 0; OB_SUCC(ret) && i < alias_num; ++i) {
if (parse_tree->children_[i]->str_len_ <= 0) {
@ -5456,7 +5492,12 @@ int ObSelectResolver::identify_anchor_member(
if (OB_FAIL(identify_anchor_resolver.resolve_child_stmt(parse_tree))) {
if (OB_ERR_NEED_INIT_BRANCH_IN_RECURSIVE_CTE == ret) {
need_swap_childa = true;
ret = OB_SUCCESS;
if (is_oracle_mode()){
ret = OB_SUCCESS;
} else if (params_.has_recursive_word) {
ret = OB_ERR_CTE_NEED_QUERY_BLOCKS; // mysql error: Recursive Common Table Expression 'cte' should have one or
// more non-recursive query blocks followed by one or more recursive ones
}
} else {
LOG_WARN("Failed to find anchor member", K(ret));
}

View File

@ -229,6 +229,9 @@ public:
}
// function members
TO_STRING_KV(K_(has_calc_found_rows), K_(has_top_limit), K_(in_set_query), K_(in_subquery));
void set_resolver(ObSelectResolver* resolver){
saved_left_resolver = resolver;
}
protected:
int resolve_set_query(const ParseNode& parse_node);
@ -432,6 +435,8 @@ protected:
bool in_subquery_;
// query is subquery in exists
bool in_exists_subquery_;
ObSelectResolver* saved_left_resolver = NULL;
// used to store left resolver and get alias name
ObStandardGroupChecker standard_group_checker_;
const TransposeItem* transpose_item_;

View File

@ -287,6 +287,8 @@ struct ObResolverParams {
new_gen_wid_(1),
is_multi_table_insert_(false),
is_resolve_table_function_expr_(false),
has_cte_param_list_(false),
has_recursive_word(false),
is_column_ref_(true)
{}
bool is_force_trace_log()
@ -340,6 +342,8 @@ private:
public:
bool is_multi_table_insert_; // used to mark is multi table insert
bool is_resolve_table_function_expr_; // used to mark resolve table function expr.
bool has_cte_param_list_;
bool has_recursive_word;
bool is_column_ref_; // used to mark normal column ref
};
} // end namespace sql