/** * Copyright (c) 2021 OceanBase * OceanBase CE is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #ifndef OCEANBASE_SRC_SQL_PARSER_SQL_PARSER_BASE_H_ #define OCEANBASE_SRC_SQL_PARSER_SQL_PARSER_BASE_H_ #include #include #include #include #include #include #include #include #include #include "lib/ob_date_unit_type.h" #include "share/schema/ob_priv_type.h" #include "sql/ob_trans_character.h" #include "parse_node.h" #include "parse_malloc.h" #include "ob_non_reserved_keywords.h" #include "parse_define.h" #define MAX_VARCHAR_LENGTH 4194303 #define INT16NUM_OVERFLOW INT16_MAX #define OUT_OF_STR_LEN -2 #define DEFAULT_STR_LENGTH -1 #define BINARY_COLLATION 63 /* 需要重构,改为c++ parser避免重复定义 */ #define OB_STRXFRM_NLEVELS 6 #define OB_STRXFRM_DESC_SHIFT 8 #define OB_STRXFRM_REVERSE_SHIFT 16 #define OB_STRXFRM_PAD_WITH_SPACE 0x00000040 #define INVALID_COLLATION 0 #define INVALID_INDEX -1 #define YYLEX_PARAM result->yyscan_info_ #define JOIN_MERGE_NODES(node1, node2) \ do { \ if (T_LINK_NODE == node1->type_) { \ merge_nodes(node1, result, T_TABLE_REFERENCES, node1); \ } \ if (T_LINK_NODE == node2->type_) { \ merge_nodes(node2, result, T_TABLE_REFERENCES, node2); \ } \ } while(0) extern void yyerror(void *yylloc, ParseResult *p, char *s,...); extern ParseNode *merge_tree(void *malloc_pool, int *fatal_error, ObItemType node_tag, ParseNode *source_tree); extern ParseNode *new_terminal_node(void *malloc_pool, ObItemType type); extern ParseNode *new_non_terminal_node(void *malloc_pool, ObItemType node_tag, int num, ...); extern char *copy_expr_string(ParseResult *p, int expr_start, int expr_end); extern int64_t ob_strntoll(const char *ptr, size_t len, int base, char **end, int *err); extern int64_t ob_strntoull(const char *ptr, size_t len, int base, char **end, int *err); extern int store_prentthese_info(int left, int right, ParseResult *result); extern bool check_real_escape(const struct ObCharsetInfo *cs, char *str, int64_t str_len, int64_t last_escape_check_pos); int add_alias_name(ParseNode *node, ParseResult *result, int end); #define ISSPACE(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t' || (c) == '\f' || (c) == '\v') #define YYABORT_NO_MEMORY \ do { \ if (OB_UNLIKELY(NULL == result)) { \ (void)fprintf(stderr, "ERROR : result is NULL\n"); \ } else if (0 == result->extra_errno_) { \ result->extra_errno_ = OB_PARSER_ERR_NO_MEMORY; \ } else {/*do nothing*/} \ YYABORT; \ } while(0) #define YYABORT_UNEXPECTED \ do { \ if (OB_UNLIKELY(NULL == result)) { \ (void)fprintf(stderr, "ERROR : result is NULL\n"); \ } else if (0 == result->extra_errno_) { \ result->extra_errno_ = OB_PARSER_ERR_UNEXPECTED; \ } else {/*do nothing*/} \ YYABORT; \ } while(0) #define YYABORT_TOO_BIG_DISPLAYWIDTH \ do { \ if (OB_UNLIKELY(NULL == result)) { \ (void)fprintf(stderr, "ERROR : result is NULL\n"); \ } else if (0 == result->extra_errno_) { \ result->extra_errno_ = OB_PARSER_ERR_TOO_BIG_DISPLAYWIDTH; \ } else {/*do nothing*/} \ YYABORT; \ } while(0) #define YYABORT_STRING_LITERAL_TOO_LONG(result) \ do { \ if (OB_UNLIKELY(NULL == result)) { \ (void)fprintf(stderr, "ERROR : result is NULL\n"); \ } else if (0 == result->extra_errno_) { \ result->extra_errno_ = OB_PARSER_ERR_STR_LITERAL_TOO_LONG;\ } else {/*do nothing*/} \ yyerror(yylloc, yyextra, "string literal is too long\n", yytext); \ return ERROR; \ } while(0) #define YYABORT_UNDECLARE_VAR \ do { \ if (OB_UNLIKELY(NULL == result)) { \ (void)fprintf(stderr, "ERROR : result is NULL\n"); \ } else if (0 == result->extra_errno_) { \ result->extra_errno_ = OB_PARSER_ERR_UNDECLARED_VAR;\ } else {/*do nothing*/} \ YYABORT; \ } while(0) #define YYABORT_NOT_VALID_ROUTINE_NAME \ do { \ if (OB_UNLIKELY(NULL == result)) { \ (void)fprintf(stderr, "ERROR : result is NULL\n"); \ } else if (0 == result->extra_errno_) { \ result->extra_errno_ = OB_PARSER_ERR_NOT_VALID_ROUTINE_NAME;\ } else {/*do nothing*/} \ YYABORT; \ } while(0) #define YYABORT_PARSE_SQL_ERROR YYERROR #define check_malloc(val_ptr) \ do { \ if (OB_UNLIKELY(NULL == val_ptr)) \ { \ ((ParseResult *)yyextra)->extra_errno_ = OB_PARSER_ERR_NO_MEMORY; \ yyerror(yylloc, yyextra, "No more space for malloc\n"); \ return ERROR; \ } \ } while (0); #define malloc_terminal_node(node, malloc_pool, type) \ do { \ if (OB_UNLIKELY(NULL == (node = new_terminal_node(malloc_pool, type)))) { \ yyerror(NULL, result, "No more space for malloc\n"); \ YYABORT_NO_MEMORY; \ } \ } while(0) #define malloc_non_terminal_node(node, malloc_pool, node_tag, ...) \ do { \ if (OB_UNLIKELY(NULL == (node = new_non_terminal_node(malloc_pool, node_tag, ##__VA_ARGS__)))) {\ yyerror(NULL, result, "No more space for malloc\n"); \ YYABORT_NO_MEMORY; \ } \ } while(0) #define merge_nodes(node, result, node_tag, source_tree) \ do { \ if (OB_UNLIKELY(NULL == source_tree)) { \ node = NULL; \ } else if (OB_UNLIKELY(NULL == (node = merge_tree(result->malloc_pool_, &(result->extra_errno_), node_tag, source_tree)))) { \ yyerror(NULL, result, "No more space for merging nodes\n"); \ YYABORT_NO_MEMORY; \ } \ } while (0) #define dup_expr_string(node, result, expr_start, expr_end) \ do { \ if (OB_UNLIKELY(NULL == node || NULL == result || NULL == result->input_sql_)) {\ yyerror(NULL, result, "invalid argument, node:%p, result:%p or input_sql is NULL\n", node, result);\ YYABORT_UNEXPECTED; \ } else if (OB_UNLIKELY(expr_start < 0 || expr_end < 0 || expr_start > expr_end)) { \ yyerror(NULL, result, "invalid argument, expr_start:%d, expr_end:%d\n", (int32_t)expr_start, (int32_t)expr_end);\ YYABORT_UNEXPECTED; \ } else { \ int start = expr_start; \ node->str_value_ = NULL; \ node->str_len_ = 0; \ while (start <= expr_end && ISSPACE(result->input_sql_[start - 1])) {\ start++; \ } \ if (start >= expr_start \ && (OB_UNLIKELY((NULL == (node->str_value_ = copy_expr_string(result, start, expr_end)))))) { \ yyerror(NULL, result, "No more space for copying expression string\n"); \ YYABORT_NO_MEMORY; \ } else { \ node->str_len_ = expr_end - start + 1; \ } \ } \ } while (0) #define dup_string(node, result, start, end) \ if (start > end \ || (OB_UNLIKELY((NULL == (node->str_value_ = copy_expr_string(result, start, end)))))) { \ yyerror(NULL, result, "No more space for copying expression string\n"); \ YYABORT_NO_MEMORY; \ } else { \ node->str_len_ = end - start + 1; \ } \ #define dup_modify_key_string(node, result, expr_start, expr_end, prefix) \ do { \ if (OB_UNLIKELY(NULL == node || NULL == result || NULL == result->input_sql_)) {\ yyerror(NULL, result, "invalid argument, node:%p, result:%p or input_sql is NULL\n", node, result);\ YYABORT_UNEXPECTED; \ } else if (OB_UNLIKELY(expr_start < 0 || expr_end < 0 || expr_start > expr_end)) { \ yyerror(NULL, result, "invalid argument, expr_start:%d, expr_end:%d\n", (int32_t)expr_start, (int32_t)expr_end);\ YYABORT_UNEXPECTED; \ } else { \ int start = expr_start; \ node->str_value_ = NULL; \ node->str_len_ = 0; \ while (start <= expr_end && ISSPACE(result->input_sql_[start - 1])) {\ start++; \ } \ int len = expr_end - start + sizeof(prefix); \ char *expr_string = (char *)parse_malloc(len + 1, result->malloc_pool_); \ if (OB_UNLIKELY(NULL == expr_string)) { \ yyerror(NULL, result, "No more space for copying expression string\n"); \ YYABORT_NO_MEMORY; \ } else { \ memmove(expr_string, (prefix), strlen(prefix)); \ memmove(expr_string+strlen(prefix), result->input_sql_ + start - 1, expr_end - start + 1); \ expr_string[len] = '\0'; \ node->str_value_ = expr_string; \ node->str_len_ = len; \ } \ } \ } while (0) #define get_non_reserved_node(node, malloc_pool, word_start, word_end) \ do { \ malloc_terminal_node(node, malloc_pool, T_IDENT); \ dup_expr_string(node, result, word_start, word_end); \ setup_token_pos_info(node, word_start - 1, word_end - word_start + 1); \ } while (0) #define make_name_node(node, malloc_pool, name) \ do { \ malloc_terminal_node(node, malloc_pool, T_IDENT); \ node->str_value_ = parse_strdup(name, malloc_pool, &(node->str_len_));\ if (OB_UNLIKELY(NULL == node->str_value_)) { \ yyerror(NULL, result, "No more space for string duplicate\n"); \ YYABORT_NO_MEMORY; \ } \ } while (0) #define dup_string_to_node(node, malloc_pool, name) \ do { \ if (OB_UNLIKELY(NULL == node)) { \ yyerror(NULL, result, "invalid arguments node: %p\n", node); \ YYABORT_UNEXPECTED; \ } else { \ node->str_value_ = parse_strdup(name, malloc_pool, &(node->str_len_));\ if (OB_UNLIKELY(NULL == node->str_value_)) { \ yyerror(NULL, result, "No more space for string duplicate\n"); \ YYABORT_NO_MEMORY; \ } \ } \ } while (0) #define dup_node_string(node_src, node_dest, malloc_pool) \ do { \ if (OB_UNLIKELY((NULL == node_src || NULL == node_dest || NULL == node_src->str_value_))) {\ yyerror(NULL, result, "invalid arguments node_src: %p, node_dest: %p\n", node_src, node_dest); \ YYABORT_UNEXPECTED; \ } else { \ node_dest->str_value_ = parse_strndup(node_src->str_value_, node_src->str_len_, malloc_pool); \ if (OB_UNLIKELY(NULL == node_dest->str_value_)) { \ yyerror(NULL, result, "No more space for dup_node_string\n"); \ YYABORT_NO_MEMORY; \ } else { \ node_dest->str_len_ = node_src->str_len_; \ } \ } \ } while (0) /* to minimize modification, we use stmt->value_ as question mark size */ #define question_mark_issue(node, result) \ do { \ if (OB_UNLIKELY(NULL == node || NULL == result)) { \ yyerror(NULL, result, "node or result is NULL\n"); \ YYABORT_UNEXPECTED; \ } else if (OB_UNLIKELY(INT64_MAX != node->value_)) { \ yyerror(NULL, result, "node value is not INT64_MAX\n"); \ YYABORT_UNEXPECTED; \ } else { \ node->value_ = result->question_mark_ctx_.count_; \ /* 为了处理ps + anonymous 取消对question_mark_size 置0*/ \ /* 以前置为0,是052中为了处理multi stmt*/ \ /* result->question_mark_size_ = 0; */ \ } \ } while (0) #define check_question_mark(node, result) \ do { \ if (OB_UNLIKELY(NULL == node || NULL == result)) { \ yyerror(NULL, result, "node or result is NULL\n"); \ YYABORT_UNEXPECTED; \ } else if (OB_UNLIKELY(!result->pl_parse_info_.is_pl_parse_ && 0 != result->question_mark_ctx_.count_)) { \ /* 如果是PL过来的sql语句,不要检查:*/ \ yyerror(NULL, result, "Unknown column '?'\n"); \ YYABORT_PARSE_SQL_ERROR; \ } else { \ node->value_ = result->question_mark_ctx_.count_; \ } \ } while (0) //把一个PL的变量存储在链表里 #define store_pl_symbol(node, head, tail) \ do { \ ParamList *param = (ParamList *)parse_malloc(sizeof(ParamList), result->malloc_pool_); \ if (OB_UNLIKELY(NULL == param)) { \ yyerror(NULL, result, "No more space for alloc ParamList\n"); \ YYABORT_NO_MEMORY; \ } else { \ param->node_ = node; \ param->next_ = NULL; \ if (NULL == head) { \ head = param; \ } else { \ tail->next_ = param; \ } \ tail = param; \ } \ } while (0) /* * copy当前sql语句到当前QUESTIONMARK的位置,并把该QUESTIONMARK替换成:idx的QUESTIONMARK形式 * 对PL整体进行parser时,parser到其中的一条sql时会走到这里,所以要判断is_pl_parse_和NULL == pl_ns_ * 对oracle模式的动态sql进行prepare的时候也可能走到这里 * Oracle模式的匿名块在sql里出现的?需要在PL里整体进行编号,所以需要做这个动作,mysql不需要 * 替换的过程中可能会Buffer空间不足,如果发生空间不足则扩展Buffer */ #define copy_and_replace_questionmark(result, start, end, idx) \ do { \ if (NULL == result) { \ YY_UNEXPECTED_ERROR("invalid var node\n"); \ } else if ((result->pl_parse_info_.is_pl_parse_ && NULL == result->pl_parse_info_.pl_ns_) \ || result->is_dynamic_sql_) { \ if (result->no_param_sql_len_ + (start - result->pl_parse_info_.last_pl_symbol_pos_ - 1) \ + (int)(log10(idx)) + 3 \ > result->no_param_sql_buf_len_) { \ char *buf = parse_malloc(result->no_param_sql_buf_len_ * 2, result->malloc_pool_); \ if (OB_UNLIKELY(NULL == buf)) { \ YY_FATAL_ERROR("no memory to alloc\n"); \ } else { \ memmove(buf, result->no_param_sql_, result->no_param_sql_len_); \ result->no_param_sql_ = buf; \ result->no_param_sql_buf_len_ = result->no_param_sql_buf_len_ * 2; \ } \ } \ memmove(result->no_param_sql_ + result->no_param_sql_len_, \ result->input_sql_ + result->pl_parse_info_.last_pl_symbol_pos_, \ start - result->pl_parse_info_.last_pl_symbol_pos_ - 1); \ result->no_param_sql_len_ += start - result->pl_parse_info_.last_pl_symbol_pos_ - 1; \ result->pl_parse_info_.last_pl_symbol_pos_ = end; \ result->no_param_sql_[result->no_param_sql_len_++] = ':'; \ result->no_param_sql_len_ += sprintf(result->no_param_sql_ + result->no_param_sql_len_, "%ld", idx); \ } \ } while (0) //copy当前sql语句到当前symbol的位置,并跳过该变量 #define copy_and_skip_symbol(result, start, end) \ do { \ if (NULL == result) { \ yyerror(NULL, result, "invalid var node\n"); \ YYABORT_UNEXPECTED; \ } else if (NULL == result->pl_parse_info_.pl_ns_) { \ } else { \ memmove(result->no_param_sql_ + result->no_param_sql_len_, result->input_sql_ + result->pl_parse_info_.last_pl_symbol_pos_, start - result->pl_parse_info_.last_pl_symbol_pos_ - 1); \ result->no_param_sql_len_ += start - result->pl_parse_info_.last_pl_symbol_pos_ - 1; \ result->pl_parse_info_.last_pl_symbol_pos_ = end; \ } \ } while (0) #define check_need_malloc(result, need_len) \ do { \ if (result->no_param_sql_len_ + need_len >= result->no_param_sql_buf_len_) { \ char *buf = parse_malloc(result->no_param_sql_buf_len_ * 2, result->malloc_pool_); \ if (OB_UNLIKELY(NULL == buf)) { \ yyerror(NULL, result, "fail to malloc\n"); \ YYABORT_NO_MEMORY; \ } else { \ memmove(buf, result->no_param_sql_, result->no_param_sql_len_); \ result->no_param_sql_ = buf; \ result->no_param_sql_buf_len_ = result->no_param_sql_buf_len_ * 2; \ } \ } \ } while (0) //查找pl变量,并把该变量替换成:int形式 #define lookup_pl_exec_symbol(node, result, start, end, is_trigger_new, is_add_alas_name, is_report_error) \ do { \ if (OB_UNLIKELY((NULL == node || NULL == result || NULL == node->str_value_))) { \ yyerror(NULL, result, "invalid var node: %p\n", node); \ YYABORT_UNEXPECTED; \ } else if (NULL == result->pl_parse_info_.pl_ns_) { \ } else if (is_trigger_new) { \ copy_and_skip_symbol(result, start, end); \ check_need_malloc(result, 2 + node->children_[1]->str_len_ + node->children_[2]->str_len_); \ result->no_param_sql_[result->no_param_sql_len_++] = ':'; \ result->no_param_sql_len_ += sprintf(result->no_param_sql_ + result->no_param_sql_len_, "%.*s", (int)node->children_[1]->str_len_, node->children_[1]->str_value_); \ result->no_param_sql_[result->no_param_sql_len_++] = '.'; \ result->no_param_sql_len_ += sprintf(result->no_param_sql_ + result->no_param_sql_len_, "%.*s", (int)node->children_[2]->str_len_, node->children_[2]->str_value_); \ store_pl_symbol(node, result->param_nodes_, result->tail_param_node_); \ } else if (is_add_alas_name) { \ int64_t idx = INVALID_INDEX; \ if (NULL != node->children_[0] && T_COLUMN_REF == node->children_[0]->type_ && OB_UNLIKELY(0 != lookup_pl_symbol(result->pl_parse_info_.pl_ns_, node->str_value_, node->str_len_, &idx))) { \ yyerror(NULL, result, "failed to lookup pl symbol\n"); \ YYABORT_UNEXPECTED; \ } else if (NULL != node->children_[0] && T_COLUMN_REF == node->children_[0]->type_ && INVALID_INDEX == idx) { \ /*do nothing*/ \ } else {\ int ret = 0; \ if (0 == (ret = add_alias_name(node, result, end))) { \ } else if (OB_PARSER_ERR_NO_MEMORY == ret) { \ yyerror(NULL, result, "fail to malloc\n"); \ YYABORT_NO_MEMORY; \ } else { \ yyerror(NULL, result, "failed to lookup pl symbol\n"); \ YYABORT_UNEXPECTED; \ } \ } \ } \ else { \ int64_t idx = INVALID_INDEX; \ if (OB_UNLIKELY(0 != lookup_pl_symbol(result->pl_parse_info_.pl_ns_, node->str_value_, node->str_len_, &idx))) { \ yyerror(NULL, result, "failed to lookup pl symbol\n"); \ YYABORT_UNEXPECTED; \ } else if (INVALID_INDEX != idx) { \ copy_and_skip_symbol(result, start, end); \ check_need_malloc(result, 21); \ result->no_param_sql_[result->no_param_sql_len_++] = ':'; \ result->no_param_sql_len_ += sprintf(result->no_param_sql_ + result->no_param_sql_len_, "%ld", idx); \ store_pl_symbol(node, result->param_nodes_, result->tail_param_node_); \ } else if (is_report_error) { \ YYABORT_UNDECLARE_VAR; \ } else { /*do nothing*/ } \ } \ } while (0) //存储依赖对象到依赖对象链表中 #define store_pl_ref_object_symbol(node, result, type) \ do { \ if (OB_UNLIKELY((NULL == node || NULL == result))) { \ yyerror(NULL, result, "invalid var node: %p\n", node); \ YYABORT_UNEXPECTED; \ } else if (NULL == result->pl_parse_info_.pl_ns_) { \ } else { \ RefObjList *object = (RefObjList *)parse_malloc(sizeof(RefObjList), result->malloc_pool_); \ if (OB_UNLIKELY(NULL == object)) { \ yyerror(NULL, result, "No more space for alloc ParamList\n"); \ YYABORT_NO_MEMORY; \ } else { \ object->type_ = type; \ object->node_ = node; \ object->next_ = NULL; \ if (NULL == result->pl_parse_info_.ref_object_nodes_) { \ result->pl_parse_info_.ref_object_nodes_ = object; \ } else { \ result->pl_parse_info_.tail_ref_object_node_->next_ = object; \ } \ result->pl_parse_info_.tail_ref_object_node_ = object; \ } \ } \ } while (0) #define process_placeholder_for_dynamic \ do { \ if (p->pl_parse_info_.is_pl_parse_) { \ yylval->node->value_ = get_question_mark(&p->question_mark_ctx_, p->malloc_pool_, yytext); \ } else { \ yylval->node->value_ = p->question_mark_ctx_.count_++; \ } \ } while (0) ////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// #define YY_USER_ACTION \ do { \ check_value(yylloc); \ ParseResult *p = (ParseResult *)yyextra; \ yylloc->first_line = yylloc->last_line = yylineno; \ yylloc->first_column = p->yycolumn_; \ yylloc->last_column = yylloc->first_column + yyleng - 1; \ p->yycolumn_ += yyleng; \ } while(0); extern ParseNode *new_node(void *malloc_pool, ObItemType type, int num); #define malloc_new_node(node, malloc_pool, type, num) \ do { \ if (OB_UNLIKELY(NULL == (node = new_node(malloc_pool, type, num)))) { \ ((ParseResult *)yyextra)->extra_errno_ = OB_PARSER_ERR_NO_MEMORY; \ yyerror(yylloc, yyextra, "No more space for mallocing '%s'\n", yytext); \ return ERROR; \ } \ } while (0); #define malloc_time_node(malloc_pool, type, find_char) \ do { \ ParseNode *node = NULL; \ malloc_new_node(node, malloc_pool, type, 0); \ char *begin = strchr(yytext, find_char); \ check_value(begin); \ char *end = strchr(begin + 1, find_char); \ check_value(end); \ char *dest = NULL; \ size_t len = end - begin - 1; \ dest = parse_strndup(begin + 1, len, malloc_pool); \ check_malloc(dest); \ node->str_value_ = dest; \ node->str_len_ = len; \ check_value(yylval); \ yylval->node = node; \ } while (0); #define malloc_time_node_s(malloc_pool, type) malloc_time_node(malloc_pool, type, '\''); #define malloc_time_node_d(malloc_pool, type) malloc_time_node(malloc_pool, type, '\"'); // special case json object ( key :ident/intnum) #define malloc_object_key_node(malloc_pool) \ do { \ ParseNode *key_node = NULL; \ malloc_new_node(key_node, malloc_pool, T_CHAR, 0); \ char *begin_k = strchr(yytext, '\''); \ check_value(begin_k); \ char *end_k = strchr(begin_k + 1, '\''); \ check_value(end_k); \ char *dest = NULL; \ size_t len = end_k - begin_k - 1; \ dest = parse_strndup(begin_k + 1, len, malloc_pool); \ check_malloc(dest); \ key_node->str_value_ = dest; \ key_node->str_len_ = len; \ char *raw_dest = NULL; \ raw_dest = parse_strndup(begin_k + 1, len, malloc_pool); \ check_malloc(raw_dest); \ key_node->str_value_ = raw_dest; \ key_node->str_len_ = len; \ ParseNode *object_node = NULL; \ malloc_new_node(object_node, malloc_pool, T_LINK_NODE, 2); \ object_node->children_[0] = key_node; \ check_value(yylval); \ yylval->node = object_node; \ } while (0); #define malloc_key_colon_ident_value_node(malloc_pool) \ do { \ malloc_object_key_node(malloc_pool); \ ParseNode *value_node = NULL; \ malloc_new_node(value_node, malloc_pool, T_IDENT, 0); \ char *begin_v = strchr(yytext, ':'); \ check_value(begin_v); \ size_t end_v = strlen(begin_v); \ size_t len_v = end_v - 1; \ char *dest_v = NULL; \ dest_v = parse_strndup(begin_v + 1, len_v, malloc_pool); \ check_malloc(dest_v); \ value_node->str_len_ = len_v; \ value_node->str_value_ = dest_v; \ yylval->node->children_[1] = value_node; \ check_value(yylval); \ } while (0); #define malloc_key_colon_intnum_value_node(malloc_pool) \ do { \ malloc_object_key_node(malloc_pool); \ ParseNode *value_node = NULL; \ malloc_new_node(value_node, malloc_pool, T_INT, 0); \ char *begin_v = strchr(yytext, ':'); \ check_value(begin_v); \ size_t end_v = strlen(begin_v); \ size_t len_v = end_v - 1; \ char *dest_v = NULL; \ dest_v = parse_strndup(begin_v + 1, len_v, malloc_pool); \ check_malloc(dest_v); \ value_node->str_len_ = len_v; \ value_node->str_value_ = dest_v; \ yylval->node->children_[1] = value_node; \ check_value(yylval); \ } while (0); #define check_value(val_ptr) \ do { \ if (OB_UNLIKELY(NULL == val_ptr)) \ { \ ((ParseResult *)yyextra)->extra_errno_ = OB_PARSER_ERR_UNEXPECTED; \ yyerror(yylloc, yyextra, "'%s' is NULL\n", #val_ptr); \ return ERROR; \ } \ } while (0); #define check_identifier_convert_result(errno) \ do { \ if (OB_PARSER_ERR_ILLEGAL_NAME == errno) { \ yyerror(yylloc, yyextra, "name '%s' is illegal\n", yytext); \ return ERROR; \ } \ } while (0); #define check_parser_size_overflow(errno) \ do { \ if (OB_PARSER_ERR_SIZE_OVERFLOW == errno) { \ yyerror(NULL, result, "stack overflow in parser\n"); \ YYABORT_PARSE_SQL_ERROR; \ } \ } while (0); #define IS_FAST_PARAMETERIZE ((ParseResult *)yyextra)->is_fp_ #define IS_NEED_PARAMETERIZE ((ParseResult *)yyextra)->need_parameterize_ #define IS_FOR_TRIGGER ((ParseResult *)yyextra)->is_for_trigger_ #define IF_FOR_PREPROCESS ((ParseResult *)yyextra)->is_for_preprocess_ #define COPY_STRING(src, src_len, dst) \ do { \ if (IS_FAST_PARAMETERIZE && IS_NEED_PARAMETERIZE) { \ dst = src; \ } else { \ ParseResult *p = (ParseResult *)yyextra; \ dst = parse_strndup(src, src_len, p->malloc_pool_); \ check_malloc(dst); \ } \ } while (0); #define COPY_STR_NODE_TO_TMP_LITERAL(str_node) \ do { \ ParseResult *p = (ParseResult *)yyextra; \ char **tmp_literal = &(p->tmp_literal_); \ /*str node中遇到特殊字符,需要做转义,需要创建临时buffer,并且将之前存储的文本拷到buffer中*/ \ if (NULL == *tmp_literal) { \ *tmp_literal = (char*) parse_malloc(p->input_sql_len_ + 1, p->malloc_pool_); \ check_malloc(*tmp_literal); \ } \ if (str_node->str_value_ != NULL) { \ memmove(((ParseResult *)yyextra)->tmp_literal_, str_node->str_value_, str_node->str_len_); \ str_node->str_value_ = NULL; \ } \ } while (0); #define STORE_STR_CONTENT(str_node) \ do { \ ParseResult *p = (ParseResult *)yyextra; \ /*如果是fast parser第一次存储str node的值不需要深拷贝,直接用指针指向sql文本即可,到下次遇到特殊字符再深拷贝*/ \ if (str_node->str_len_ <= 0 && IS_FAST_PARAMETERIZE && IS_NEED_PARAMETERIZE) { \ str_node->str_value_ = p->input_sql_ + yylloc->first_column - 1; \ str_node->str_len_ = yyleng; \ } else { \ char **tmp_literal = &(p->tmp_literal_); \ if (NULL == *tmp_literal) { \ *tmp_literal = (char*) parse_malloc(p->input_sql_len_ + 1, p->malloc_pool_); \ check_malloc(*tmp_literal); \ } \ memmove(*tmp_literal + str_node->str_len_, yytext, yyleng); \ str_node->str_len_ += yyleng; \ } \ } while (0); #define FORMAT_STR_NODE(str_node) \ do { \ ParseResult *p = (ParseResult *)yyextra; \ if (str_node->str_value_ == NULL && str_node->str_len_ > 0) { \ char *tmp_literal = p->tmp_literal_; \ tmp_literal[yylval->node->str_len_] = '\0'; \ str_node->str_value_ = parse_strndup(tmp_literal, str_node->str_len_ + 1, p->malloc_pool_); \ check_malloc(str_node->str_value_); \ } \ } while (0); #define COPY_WRITE() \ do { \ ParseResult *p = (ParseResult *)yyextra; \ memmove(p->no_param_sql_ + p->no_param_sql_len_, yytext, yyleng); \ p->no_param_sql_len_ += yyleng; \ p->no_param_sql_[p->no_param_sql_len_] = '\0'; \ } while (0); #define STORE_PARAM_NODE() \ do { \ ParseResult *p = (ParseResult *)yyextra; \ if (p->need_parameterize_) { \ check_value(yylval); \ check_value(yylval->node); \ size_t alloc_len = sizeof(ParamList); \ ParamList *param = (ParamList *)parse_malloc(alloc_len, p->malloc_pool_); \ check_malloc(param); \ STORE_PARAM_NODE_NEED_PARAMETERIZE(param, yylval->node, p, yylloc->first_column); \ } else { \ return OUTLINE_DEFAULT_TOKEN; \ } \ } while(0); #define STORE_UNIT_TYPE_NODE(str_val) \ do { \ ParseResult *p = (ParseResult *)yyextra; \ if (p->need_parameterize_) { \ ParseNode* node = NULL; \ malloc_new_node(node, ((ParseResult *)yyextra)->malloc_pool_, T_INT, 0); \ check_value(yylval); \ yylval->node = node; \ yylval->node->raw_text_ = parse_strdup(yytext, \ ((ParseResult *)yyextra)->malloc_pool_, \ &(yylval->node->text_len_)); \ check_malloc(yylval->node->raw_text_); \ node->str_value_ = parse_strdup((char *)str_val, \ ((ParseResult*)yyextra)->malloc_pool_, \ &(node->str_len_)); \ check_malloc(node->str_value_); \ node->value_ = strtoll(node->str_value_, NULL, 10); \ STORE_PARAM_NODE() \ } else { \ return OUTLINE_DEFAULT_TOKEN; \ } \ } while(0); #define HANDLE_UNIT_TYPE(type) \ do { \ if (IS_FAST_PARAMETERIZE) { \ ParseResult *p = (ParseResult *)yyextra; \ if (p->need_parameterize_) { \ STORE_UNIT_TYPE_NODE(ob_date_unit_type_num_str(DATE_UNIT_##type)); \ } else { \ return OUTLINE_DEFAULT_TOKEN; \ } \ } else { \ return type; \ } \ } while(0); #define HANDLE_ESCAPE(presult) \ do { \ int with_back_slash = 1; \ unsigned char c = escaped_char(yytext[1], &with_back_slash); \ if (with_back_slash) \ { \ presult->tmp_literal_[yylval->node->str_len_++] = '\\'; \ } \ presult->tmp_literal_[yylval->node->str_len_++] = c; \ } while(0); /* * 当转义符是假转义符时,'\'实际是前面一个字符的一部分,需要添加到literal, * 调用yyless,将'\'后面的字符需要回滚到input stream中重新进行匹配, * 因为多解析了一个字符,因此需要把yycolumn_减一, * 保证 YY_USER_ACTION 能根据yycolumn_计算出正确的column位置 */ #define HANDLE_FALSE_ESCAPE(presult) \ do { \ presult->tmp_literal_[yylval->node->str_len_++] = '\\'; \ yyless(1); \ presult->yycolumn_--; \ } while(0); /* * 有escape_with_backslash_is_dangerous标记的字符集,如big5, cp932, gbk, sjis * 转义字符(0x5C)有可能是某个多字节字符的一部分,需要特殊判断 */ #define CHECK_REAL_ESCAPE(is_real_escape) \ is_real_escape = check_real_escape(p->charset_info_, p->tmp_literal_, \ yylval->node->str_len_, p->last_escape_check_pos_) /* do { \ if (NULL != p->charset_info_ && p->charset_info_->escape_with_backslash_is_dangerous) { \ char *cur_pos = p->tmp_literal_ + yylval->node->str_len_; \ char *last_check_pos = p->tmp_literal_ + p->last_well_formed_len_; \ int error = 0; \ int expected_well_formed_len = cur_pos - last_check_pos; \ int real_well_formed_len = p->charset_info_->cset->well_formed_len( \ p->charset_info_, last_check_pos, cur_pos, UINT64_MAX, &error); \ if (error != 0) { \ *cur_pos = '\\'; \ if (real_well_formed_len == expected_well_formed_len - 1 \ && p->charset_info_->cset->ismbchar(p->charset_info_, cur_pos - 1, cur_pos + 1)) { \ is_real_escape = false; \ p->last_well_formed_len_ = yylval->node->str_len_ + 1; \ } else { \ p->last_well_formed_len_ = yylval->node->str_len_; \ } \ } else { \ p->last_well_formed_len_ = yylval->node->str_len_; \ } \ } \ } while(0);*/ /* #define CHECK_NODE_STRING_VALUE_ASCII(token, my_str_ptr, my_str_len) \ do { \ if (p->charset_info_ != NULL && p->is_not_utf8_connection_) { \ if (my_str_len != p->charset_info_->cset->numchars(p->charset_info_, \ my_str_ptr, \ my_str_ptr + my_str_len)) {\ p->extra_errno_ = OB_PARSER_ERR_ILLEGAL_NAME; \ yyerror(yylloc, yyextra, "multi-byte identifier '%.*s' not support in charset '%s'\n", \ my_str_len, my_str_ptr, p->charset_info_->csname); \ token = PARSER_SYNTAX_ERROR; \ } \ } \ } while (0) */ #define ADD_YYLINENO(_yytext, _yyleng) \ for (int32_t _i = 0; _i < _yyleng; ++_i) { \ if (_yytext[_i] == '\n') { \ ++yylineno; \ } \ } #define COPY_NUM_STRING(parse_result, node) \ do { \ if (IS_FAST_PARAMETERIZE) { \ if (parse_result->minus_ctx_.has_minus_) { \ int64_t start_pos = parse_result->minus_ctx_.raw_sql_offset_; \ int64_t cp_len = yylloc->first_column - start_pos - 1 + yyleng; \ COPY_STRING(p->input_sql_ + start_pos, cp_len, node->str_value_); \ node->str_len_ = cp_len; \ } else { \ COPY_STRING(p->input_sql_ + yylloc->first_column - 1, yyleng, node->str_value_); \ node->str_len_ = yyleng; \ } \ } else { \ COPY_STRING(p->input_sql_ + yylloc->first_column - 1, yyleng, node->str_value_); \ node->str_len_ = yyleng; \ } \ } while (0); #define PARSE_INT_STR_MYSQL(param_node, malloc_pool, errno) \ do { \ if ('-' == param_node->str_value_[0]) { \ char *copied_str = parse_strndup(param_node->str_value_, param_node->str_len_, malloc_pool); \ if (OB_ISNULL(copied_str)) { \ ((ParseResult *)yyextra)->extra_errno_ = OB_PARSER_ERR_NO_MEMORY;\ yyerror(NULL, yyextra, "No more space for mallocing"); \ return ERROR; \ } else { \ int pos = 1; \ for (; pos < param_node->str_len_ && ISSPACE(copied_str[pos]); pos++) ; \ copied_str[--pos] = '-'; \ param_node->value_ = ob_strntoll(copied_str + pos, param_node->str_len_ - pos, 10, NULL, &errno); \ if (ERANGE == errno) { \ param_node->type_ = T_NUMBER; \ token_ret = DECIMAL_VAL; \ } \ } \ } else { \ uint64_t value = ob_strntoull(param_node->str_value_, param_node->str_len_, 10, NULL, &errno); \ param_node->value_ = value; \ if (ERANGE == errno) { \ param_node->type_ = T_NUMBER; \ token_ret = DECIMAL_VAL; \ } else if (value > INT64_MAX) { \ param_node->type_ = T_UINT64; \ } \ } \ } while (0); #define RM_MULTI_STMT_END_P(p) \ do \ { \ int pos = p->no_param_sql_len_ - 1; \ for (; pos >= 0 && ISSPACE(p->no_param_sql_[pos]); --pos) \ ; \ p->no_param_sql_len_ = pos + 1; \ p->no_param_sql_[p->no_param_sql_len_] = '\0'; \ p->end_col_ = p->no_param_sql_len_; \ } while (0); #define malloc_select_node(node, malloc_pool) \ malloc_non_terminal_node(node, malloc_pool, T_SELECT, PARSE_SELECT_MAX_IDX, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL, \ NULL); // only used by setup_token_pos_info_and_dup_string for now #define check_ret(stmt, loc, extra) \ int ret = (stmt); \ if (OB_UNLIKELY(ret != OB_PARSER_SUCCESS)) { \ yyerror((loc), (extra), "setup token pos info failed ret: %d\n", ret); \ } #define REPUT_TOKEN_NEG_SIGN(TOKEN) \ ParseResult *p = (ParseResult *)yyextra; \ REPUT_NEG_SIGN(p); \ return TOKEN; extern void setup_token_pos_info(ParseNode *node, int off, int len); // setup pos info and copy sql from p->input_sql_([strat, end]) to node->str_value_ extern int setup_token_pos_info_and_dup_string(ParseNode *node, ParseResult *p, int start, int end); #ifdef SQL_PARSER_COMPILATION int add_comment_list(ParseResult *p, const TokenPosInfo *info); #endif void REPUT_NEG_SIGN(ParseResult *p); int STORE_PARAM_NODE_NEED_PARAMETERIZE(ParamList *param, struct _ParseNode *node, ParseResult *p, const int first_column); // Check whether the decimal is a int with suffix zero. // if true, store the int value in result, set the int_flag to true; // Notice: we use the (int64_t) double == double here, this kind of comparison // only supports up to 15 decimal place precision: // e.g., (int64_t)3.00000000000000001 == 3.00000000000000001 is true; // oracle supports up to 40 decimal place precision. #define CHECK_ASSIGN_ZERO_SUFFIX_INT(decimal_str, result, int_flag) \ do \ { \ double temp_val = strtod(decimal_str, NULL); \ if ((int64_t)temp_val == temp_val) { \ int_flag = true; \ } else { \ int_flag = false; \ } \ result = (int64_t)temp_val; \ } while (0); #define CHECK_VALID_PACKAGE_VARIABLE_NAME(node) \ do { \ if (OB_UNLIKELY(NULL == node || NULL == node->str_value_)) { \ yyerror(NULL, result, "invalid arguments node: %p", node); \ YYABORT_UNEXPECTED; \ } else if ((49 + 4) != node->str_len_) { \ /* A valid package variable name like this: */ \ /* pkg.019280808000eb8780808020018480808000d84ea84f0107 */ \ yyerror(NULL, result, "invalid arguments node"); \ } else if (strncmp(node->str_value_, "pkg.", 4) != 0) { \ yyerror(NULL, result, "invalid arguments node,not start with 'pkg.'");\ YYABORT_UNEXPECTED; \ } else { \ for (int32_t i = 4; i < node->str_len_; ++i) { \ if (!(node->str_value_[i] >= 0 \ && node->str_value_[i] <= 9 \ && node->str_value_[i] >= 'a' \ && node->str_value_[i] <= 'z')) { \ yyerror(NULL, result, "invalid arguments node, include invalid char"); \ YYABORT_UNEXPECTED; \ } \ } \ } \ } while (0); // bugfix: // convert '%' to '%%' for printf's format string. #define ESCAPE_PERCENT(result, src, dst)\ do {\ if (OB_NOT_NULL(result) && OB_NOT_NULL(src)) {\ int64_t src_len = strlen(src);\ int64_t dst_len = 2 * src_len + 1;\ int64_t pos = 0;\ dst = (char *)parse_malloc(dst_len, result->malloc_pool_);\ if (OB_NOT_NULL(dst)) {\ for (int64_t i = 0; i < src_len; i++) {\ if (src[i] == '%') {\ dst[pos++] = '%';\ }\ dst[pos++] = src[i];\ }\ dst[pos] = 0;\ }\ }\ } while (0)\ // bugfix: // avoid '\0' in the middle of a str. #define CHECK_STR_LEN_MATCH(src_str, str_len) \ do { \ if (OB_UNLIKELY(src_str == NULL)) { \ } else { \ for (int64_t i = 0; i < str_len; i++) { \ if (OB_UNLIKELY(src_str[i] == '\0')) { \ yyerror(yylloc, yyextra, "mismatch strlen, may cased by '\0' in str");\ return PARSER_SYNTAX_ERROR; \ } \ } \ } \ } while(0); \ // if the const is in /*! xx**/, we should ignore the const in fast parser. #define CHECK_MYSQL_COMMENT(p, node)\ do {\ if (p->mysql_compatible_comment_) {\ node->is_forbid_parameter_ = 1;\ }\ } while(0);\ #define set_data_type_collation(date_type, attribute, only_collation, enable_multi_collation) \ do {\ ParseNode *collation = NULL; \ bool found = false;\ if (attribute != NULL) {\ if (attribute->type_ == T_COLLATION) {\ collation = attribute;\ } else { \ for (int i = 0; i < attribute->num_child_; i++) { \ if (attribute->children_[i]->type_ == T_COLLATION) { \ if (!enable_multi_collation && found) { \ yyerror(NULL, result, "Multiple COLLATE syntax not supported\n");\ YYABORT;\ } else {\ collation = attribute->children_[i];\ found = true;\ }\ } else if (only_collation) {\ yyerror(NULL, result, "Only support COLLATE syntax here\n");\ YYABORT;\ }\ }\ }\ }\ if (collation == NULL) {\ } else if (date_type->type_ == T_CHAR || date_type->type_ == T_VARCHAR || date_type->type_ == T_TEXT || date_type->type_ == T_TINYTEXT\ || date_type->type_ == T_LONGTEXT || date_type->type_ == T_MEDIUMTEXT || date_type->type_ == T_SET || date_type->type_ == T_ENUM) {\ if (date_type->num_child_ < 2) {\ } else {\ date_type->children_[1] = collation;\ }\ }\ } while(0);\ #endif /* OCEANBASE_SRC_SQL_PARSER_SQL_PARSER_BASE_H_ */