diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index e2229d733..193552a91 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -43,6 +43,7 @@ extern bool canAddHist; #define DEFAULT_RETRY_TIMES 5 #define MAX_RETRY_TIMES 10 #define ERRCODE_LENGTH 5 +#define DELIMITER_LENGTH 16 #if defined(__LP64__) || defined(__64BIT__) typedef unsigned int GS_UINT32; diff --git a/src/bin/psql/mainloop.cpp b/src/bin/psql/mainloop.cpp index 172167085..d9e014f99 100644 --- a/src/bin/psql/mainloop.cpp +++ b/src/bin/psql/mainloop.cpp @@ -100,6 +100,144 @@ static void SetSessionTimeout(const char* session_timeout) PQclear(StRes); } + +static void JudgeEndStateInBFormat(const char* inputLine, bool &is_b_format, char* delimiter_name, bool is_new_lines) +{ + /* Convert inputLine to lowercase */ + char *inputLine_temp = pg_strdup(inputLine); + inputLine_temp = pg_strtolower(inputLine_temp); + + /* Determine whether the command is a delimiter command, and if so, save the result. */ + static bool is_just_one_check = false; + static bool is_just_two_check = false; + PGresult* res = NULL ; + char *tokenPtr = strstr(inputLine_temp, "delimiter"); + char *tokenPtr1 = strstr(inputLine_temp, "\\c" ); + errno_t rc = 0; + + if (!is_just_one_check) { + res = PQexec(pset.db, "show sql_compatibility"); + if (res != NULL && PQresultStatus(res) == PGRES_TUPLES_OK) { + is_b_format = strcmp (PQgetvalue(res, 0, 0), "B") == 0; + } + PQclear(res); + res = NULL; + is_just_one_check = true; + } + + if (tokenPtr1 != NULL) { + is_just_one_check = false; + } + + if (is_b_format) { + if (!is_just_two_check && is_new_lines) { + res = PQexec(pset.db, "show delimiter_name"); + if (res != NULL && PQresultStatus(res) == PGRES_TUPLES_OK) { + rc = strcpy_s(delimiter_name, DELIMITER_LENGTH, PQgetvalue(res, 0, 0)); + securec_check_c(rc, "\0", "\0"); + } + PQclear(res); + res = NULL; + is_just_two_check = true; + } + } else if (strcmp(delimiter_name,";") != 0) { + rc = strcpy_s(delimiter_name, DELIMITER_LENGTH, ";"); + securec_check_c(rc, "\0", "\0"); + } + + if (tokenPtr != NULL || tokenPtr1 != NULL) { + is_just_two_check = false; + } + + free(inputLine_temp); + inputLine_temp =NULL; +} + +static bool is_match_delimiter_name(const char* left, const char* right) +{ + if (strlen(left) < strlen(right)) { + return false; + } + while (*right) { + if (*left++ != *right++) { + return false; + } + } + return true ; +} + +static char* get_correct_str(char*str, const char *delimiter_name, bool is_new_lines) +{ + /* Determine whether it is a delimiter command. */ + char *str_temp = pg_strdup(str); + str_temp = pg_strtolower(str_temp); + bool is_delimiter = false; + char *token = strstr(str_temp, "delimiter"); + errno_t rc = 0; + if(token != NULL) { + is_delimiter = true; + char* pos = str_temp; + while(pos != token) { + if(*pos == ' ') { + pos++; + } else { + is_delimiter = false; + break; + } + } + if(is_delimiter) { + char* end = pos + strlen("delimiter"); + if(*end != ' ' && *end != '\0') { + is_delimiter = false; + } + } + } + if (is_new_lines && is_delimiter && strstr(str, delimiter_name) == NULL) { + Size slen1 = strlen(str) + strlen(delimiter_name) + DELIMITER_LENGTH; + char* result1 = (char *) pg_malloc(slen1); + rc = sprintf_s(result1, slen1, "%s %s", str, delimiter_name); + securec_check_ss_c(rc, "", ""); + free(str_temp); + str_temp =NULL; + return result1; + } + free(str_temp); + str_temp =NULL; + + if (!((*delimiter_name >= 'a' && *delimiter_name <='z') || (*delimiter_name >= 'A' && *delimiter_name <= 'Z'))) { + return pg_strdup(str); + } + Size slen = 2 * strlen(str) + 1; + char* result = (char *) pg_malloc(slen + 2); + char* temp = result; + char* pos; + char* end_of_str = str + strlen(str); + char special_str = 0; + char in; + for (pos = str; pos < end_of_str; pos++) { + in = *pos; + if (!special_str && is_match_delimiter_name(pos , delimiter_name)) { + *temp++ =' '; + int delimiter_name_length = strlen(delimiter_name); + while ( delimiter_name_length > 0 && *pos != '\0') { + *temp++ = *pos++; + delimiter_name_length--; + } + pos--; + *temp++ = ' '; + } else { + if (in == special_str) { + special_str = 0; + } else if (!special_str && (in == '\'' || in == '"' || in == '`')) { + special_str = (char)in; + } + *temp++ = *pos; + } + } + *temp = '\0'; + return result; +} + /* * Main processing loop for reading lines of input * and sending them to the backend. @@ -142,6 +280,9 @@ int MainLoop(FILE* source, char* querystring) /* Save the stmts and counts info in parallel execute mode. */ int query_count = 0; char** query_stmts = NULL; + bool is_b_format = false; + char delimiter_name[DELIMITER_LENGTH]=";"; + char *line_temp = NULL; errno_t rc = 0; @@ -318,11 +459,19 @@ int MainLoop(FILE* source, char* querystring) /* Setting this will not have effect until next line. */ die_on_error = pset.on_error_stop; - + /* Add processing of sql mode and terminator */ + bool is_new_lines = query_buf->len == 0 ? true : false; + JudgeEndStateInBFormat(line, is_b_format, delimiter_name, is_new_lines); /* * Parse line, looking for command separators. */ - psql_scan_setup(scan_state, line, (int)strlen(line)); + if (is_b_format) { + line_temp = get_correct_str(line, delimiter_name, is_new_lines); + psql_scan_setup(scan_state, line_temp, (int)strlen(line_temp)); + free(line_temp); + } else { + psql_scan_setup(scan_state, line, (int)strlen(line)); + } success = true; line_saved_in_history = false; @@ -330,7 +479,7 @@ int MainLoop(FILE* source, char* querystring) PsqlScanResult scan_result; promptStatus_t prompt_tmp = prompt_status; - scan_result = psql_scan(scan_state, query_buf, &prompt_tmp); + scan_result = psql_scan(scan_state, query_buf, &prompt_tmp,is_b_format,delimiter_name); prompt_status = prompt_tmp; if (PQExpBufferBroken(query_buf)) { diff --git a/src/bin/psql/psqlscan.h b/src/bin/psql/psqlscan.h index 8634e5898..c246f6fd3 100644 --- a/src/bin/psql/psqlscan.h +++ b/src/bin/psql/psqlscan.h @@ -39,7 +39,8 @@ extern void psql_scan_destroy(PsqlScanState state); extern void psql_scan_setup(PsqlScanState state, const char* line, int line_len); extern void psql_scan_finish(PsqlScanState state); -extern PsqlScanResult psql_scan(PsqlScanState state, PQExpBuffer query_buf, promptStatus_t* prompt); +extern PsqlScanResult psql_scan(PsqlScanState state, PQExpBuffer query_buf, promptStatus_t* prompt, + bool is_b_format,char* delimiter_name); extern void psql_scan_reset(PsqlScanState state); diff --git a/src/bin/psql/psqlscan.l b/src/bin/psql/psqlscan.l index d7edd6af3..a736bbdb7 100644 --- a/src/bin/psql/psqlscan.l +++ b/src/bin/psql/psqlscan.l @@ -146,6 +146,8 @@ typedef struct PsqlScanStateData For sql like "Declare foo CURSOR XXX, butt_num is 1. Using this we can judge if the sql is a DECLARE CURSOR stmt */ bool include_ora_comment; /* dont igore comment when ture, such as single line comment after function/procedure param */ + bool is_b_format; + char *delimiter_name; } PsqlScanStateData; static PsqlScanState cur_state; /* current state while active */ @@ -196,6 +198,7 @@ static void analyze_state(const char* text,PsqlScanState state); static int upperstrcmp(const char *str1,const char *str2); static GramIdentify keywordRead(const char* text); static bool IsTranscationTokens(char* yytext, char* token); +static bool judge_end_state(const char* text); #define YY_DECL int yylex(PsqlScanState lex_param) @@ -656,6 +659,13 @@ other . RESET_XP_STATUS(); return LEXRES_SEMI; } + + if (cur_state->is_b_format == true ) { + if (strstr(yytext, cur_state->delimiter_name) != NULL && strcmp(";" ,cur_state->delimiter_name) != 0) { + RESET_XP_STATUS(); + return LEXRES_SEMI; + } + } } } @@ -796,9 +806,13 @@ other . ";" { ECHO; - - if (lex_param->begin_state == BEGIN_CURSOR || + bool is_end = true; + if (cur_state->is_b_format && strcmp(yytext , cur_state->delimiter_name) != 0) { + is_end = false; + } + if ((lex_param->begin_state == BEGIN_CURSOR || (!lex_param->declare_encountered && cur_state->paren_depth == 0)) + && is_end == true) { RESET_XP_STATUS(); return LEXRES_SEMI; @@ -889,6 +903,13 @@ other . {self} { ECHO; + if ((lex_param->begin_state == BEGIN_CURSOR || + ( !lex_param->declare_encountered && cur_state->paren_depth == 0)) + && judge_end_state(yytext)) + { + RESET_XP_STATUS() ; + return LEXRES_SEMI; + } } {operator} { @@ -943,6 +964,13 @@ other . yyless(nchars); } ECHO; + if ((lex_param->begin_state == BEGIN_CURSOR || + ( !lex_param->declare_encountered && cur_state->paren_depth == 0)) + && judge_end_state(yytext)) + { + RESET_XP_STATUS() ; + return LEXRES_SEMI; + } } {param} { @@ -983,6 +1011,13 @@ other . {identifier} { ECHO; analyze_state(yytext,(PsqlScanStateData*)lex_param); + if ((lex_param->begin_state == BEGIN_CURSOR || + ( !lex_param->declare_encountered && cur_state->paren_depth == 0)) + && judge_end_state(yytext)) + { + RESET_XP_STATUS() ; + return LEXRES_SEMI; + } } {other} { @@ -1373,7 +1408,9 @@ psql_scan_setup(PsqlScanState state, PsqlScanResult psql_scan(PsqlScanState state, PQExpBuffer query_buf, - promptStatus_t *prompt) + promptStatus_t *prompt, + bool is_b_format, + char* delimiter_name) { PsqlScanResult result; int lexresult; @@ -1391,6 +1428,14 @@ psql_scan(PsqlScanState state, yy_switch_to_buffer(state->scanbufhandle); BEGIN(state->start_state); + state->is_b_format = is_b_format; + if (is_b_format) { + if (state->delimiter_name) { + free(state->delimiter_name); + state->delimiter_name = NULL; + } + state->delimiter_name = pg_strdup(delimiter_name); + } /* And lex. */ lexresult = yylex((PsqlScanStateData*)state); @@ -1522,6 +1567,10 @@ psql_scan_finish(PsqlScanState state) free(state->scanbuf); } state->scanbuf = NULL; + if (state->delimiter_name) { + free(state->delimiter_name); + } + state->delimiter_name = NULL; } /* @@ -1553,6 +1602,11 @@ psql_scan_reset(PsqlScanState state) state->begin_state = BEGIN_UNDEFINED; state->butt_num = 0; state->declare_encountered = false; + state->is_b_format = false; + if (state->delimiter_name) { + free(state->delimiter_name); + } + state->delimiter_name = NULL; } /* @@ -2060,6 +2114,14 @@ static bool IsTranscationTokens(char* yytext, char* token) return false; } +static bool judge_end_state(const char* text) +{ + if (cur_state->is_b_format == true) { + return (strcmp(yytext, cur_state->delimiter_name) ==0); + } + return false; +} + /* * escape_variable --- process :'VARIABLE' or :"VARIABLE" * diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 132c6c085..6ca48c547 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -264,6 +264,8 @@ static bool CheckWhetherInColList(char *colname, List *col_list); static int GetFillerColIndex(char *filler_col_name, List *col_list); static void RemoveFillerCol(List *filler_list, List *col_list); static int errstate; + +static void setDelimiterName(core_yyscan_t yyscanner, char*input, VariableSetStmt*n); %} %define api.pure @@ -372,7 +374,7 @@ static int errstate; CreateWeakPasswordDictionaryStmt DropWeakPasswordDictionaryStmt AlterGlobalConfigStmt DropGlobalConfigStmt CreatePublicationStmt AlterPublicationStmt - CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt + CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt DelimiterStmt ShrinkStmt /* */ @@ -788,8 +790,7 @@ static int errstate; %type load_col_data_type %type load_col_sequence_item_sart column_sequence_item_step column_sequence_item_sart %type trigger_order - - +%type delimiter_str_name delimiter_str_names /* * Non-keyword token types. These are hard-wired into the "flex" lexer. * They must be listed first so that their numeric codes do not depend on @@ -930,6 +931,9 @@ static int errstate; VALID_BEGIN DECLARE_CURSOR ON_UPDATE_TIME START_WITH CONNECT_BY + END_OF_INPUT + END_OF_INPUT_COLON + END_OF_PROC /* Precedence: lowest to highest */ %nonassoc COMMENT @@ -1032,6 +1036,38 @@ stmtmulti: stmtmulti ';' stmt else $$ = $1; } + | stmtmulti ';' END_OF_INPUT stmt + { + if ($4 != NULL) + { + if (IsA($4, List)) + { + $$ = list_concat($1, (List*)$4); + } + else + { + $$ = lappend($1, $4); + } + } + else + $$ = $1; + } + | stmtmulti END_OF_INPUT_COLON stmt + { + if ($3 != NULL) + { + if (IsA($3, List)) + { + $$ = list_concat($1, (List*)$3); + } + else + { + $$ = lappend($1, $3); + } + } + else + $$ = $1; + } | { #ifndef ENABLE_MULTIPLE_NODES @@ -1249,6 +1285,7 @@ stmt : | ShrinkStmt | /*EMPTY*/ { $$ = NULL; } + | DelimiterStmt ; /***************************************************************************** @@ -14399,7 +14436,7 @@ subprogram_body: { tok = YYLEX; /* adapt A db's label */ - if (!(tok == ';' || tok == 0) + if (!(tok == ';' || (tok == 0 || tok == END_OF_PROC)) && tok != IF_P && tok != CASE && tok != LOOP) @@ -14410,7 +14447,7 @@ subprogram_body: { if (blocklevel == 1 && (pre_tok == ';' || pre_tok == BEGIN_P) - && (tok == ';' || tok == 0)) + && (tok == ';' || (tok == 0 || tok == END_OF_PROC))) { /* Save the end of procedure body. */ proc_e = yylloc; @@ -25774,6 +25811,43 @@ ColLabel: IDENT { $$ = $1; } } ; +DelimiterStmt: DELIMITER delimiter_str_names END_OF_INPUT + { + VariableSetStmt *n = makeNode(VariableSetStmt); + setDelimiterName(yyscanner, $2, n); + $$ = (Node *)n; + } + | DELIMITER delimiter_str_names END_OF_INPUT_COLON + { + VariableSetStmt *n = makeNode(VariableSetStmt); + setDelimiterName(yyscanner, $2, n); + $$ = (Node *)n; + } + ; + +delimiter_str_names: delimiter_str_names delimiter_str_name + { + $$ = $1 ; + } + | delimiter_str_name + { + $$ = $1; + } + ; + +delimiter_str_name: ColId_or_Sconst + { + $$ = $1; + } + | all_Op + { + $$ = $1; + } + | ';' + { + $$ = ";"; + } + ; /* * Keyword category lists. Generally, every keyword present in @@ -28537,6 +28611,22 @@ static void RemoveFillerCol(List *filler_list, List *col_list) return; } +static void setDelimiterName(core_yyscan_t yyscanner, char*input, VariableSetStmt*n) +{ + errno_t rc = 0; + base_yy_extra_type *yyextra = pg_yyget_extra(yyscanner); + if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) { + if (strlen(input) >= DELIMITER_LENGTH) { + parser_yyerror("syntax error"); + } + n->is_local = false; + n->kind = VAR_SET_VALUE; + n->name = "delimiter_name"; + n->args = list_make1(makeStringConst(input, -1)); + } +} + + static FuncCall* MakePriorAsFunc() { List *funcName = list_make1(makeString("prior")); diff --git a/src/common/backend/parser/parser.cpp b/src/common/backend/parser/parser.cpp index 7523ec002..4e3490e3a 100644 --- a/src/common/backend/parser/parser.cpp +++ b/src/common/backend/parser/parser.cpp @@ -155,6 +155,7 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) int next_token; core_YYSTYPE cur_yylval; YYLTYPE cur_yylloc; + errno_t rc = 0; /* Get next token --- we might already have it */ if (yyextra->lookahead_num != 0) { @@ -166,6 +167,32 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) cur_token = core_yylex(&(lvalp->core_yystype), llocp, yyscanner); } + if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT && yyextra->lookahead_num == 0) { + bool is_last_colon; + if (cur_token == int(';')) { + is_last_colon = true; + } else { + is_last_colon = false; + } + if (yyextra->core_yy_extra.is_delimiter_name == true) { + if (strcmp(";",u_sess->attr.attr_common.delimiter_name) == 0) { + cur_token = END_OF_INPUT_COLON; + } else { + if (yyextra->core_yy_extra.is_last_colon == false ) { + cur_token = END_OF_INPUT_COLON; + } else { + cur_token = END_OF_INPUT; + } + } + } + if (yyextra->core_yy_extra.is_proc_end == true) { + cur_token = END_OF_PROC; + } + yyextra->core_yy_extra.is_proc_end = false; + yyextra->core_yy_extra.is_delimiter_name = false; + yyextra->core_yy_extra.is_last_colon = is_last_colon; + } + /* Do we need to look ahead for a possible multiword token? */ switch (cur_token) { case NULLS_P: diff --git a/src/common/backend/parser/scan.l b/src/common/backend/parser/scan.l index 9c03aee24..b4abce9ca 100755 --- a/src/common/backend/parser/scan.l +++ b/src/common/backend/parser/scan.l @@ -93,6 +93,7 @@ static bool is_utf16_surrogate_first(pg_wchar c); static bool is_utf16_surrogate_second(pg_wchar c); static pg_wchar surrogate_pair_to_codepoint(pg_wchar first, pg_wchar second); static void addunicode(pg_wchar c, yyscan_t yyscanner); +static void set_is_delimiter_name(char* text, core_yyscan_t yyscanner ); #define yyerror(msg) scanner_yyerror(msg, yyscanner) @@ -598,6 +599,7 @@ other . BEGIN(INITIAL); yylval->str = litbuf_udeescape('\\', yyscanner); yyextra->is_hint_str = false; + set_is_delimiter_name(yytext,yyscanner); return SCONST; } {xusstop2} { @@ -867,6 +869,7 @@ other . /* reset is_createstmt to parse next sql */ yyextra->is_createstmt = false; } + set_is_delimiter_name(yytext,yyscanner); } yyextra->is_hint_str = false; return yytext[0]; @@ -919,6 +922,7 @@ other . } SET_YYLLOC(); + set_is_delimiter_name(yytext,yyscanner); if (nchars < (int)yyleng) { @@ -1144,6 +1148,7 @@ other . ident = downcase_truncate_identifier(yytext, yyleng, yyextra->warnOnTruncateIdent); yylval->str = ident; yyextra->ident_quoted = false; + set_is_delimiter_name(yytext,yyscanner); return IDENT; } @@ -1339,6 +1344,9 @@ scanner_init(const char *str, /* plpgsql keyword params */ yyext->isPlpgsqlKeyWord = false; yyext->plKeywordValue = NULL; + yyext->is_delimiter_name = false; + yyext->is_last_colon = false; + yyext->is_proc_end = false; // Added CALL for procedure and function getDynaParamSeq("init", true, true, NULL); @@ -1420,6 +1428,24 @@ addlitchar(unsigned char ychar, core_yyscan_t yyscanner) yyextra->literallen += 1; } +static void set_is_delimiter_name(char* text, core_yyscan_t yyscanner) +{ + if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) { + if (strcmp(text,u_sess->attr.attr_common.delimiter_name) == 0 && yyextra->paren_depth == 0 && !yyextra->in_slash_proc_body) { + if (strcmp(text,";") != 0) { + yyextra->query_string_locationlist = lappend_int(yyextra->query_string_locationlist, *yylloc); + yyextra->is_createstmt = false; + } + yyextra->is_delimiter_name = true; + } else { + yyextra->is_delimiter_name = false; + } + if (strcmp(text,u_sess->attr.attr_common.delimiter_name) == 0 && strcmp(text,";") != 0 && yyextra->in_slash_proc_body) { + yyextra->is_proc_end = true; + } + } +} + /* * Create a palloc'd copy of literalbuf, adding a trailing null. diff --git a/src/common/backend/utils/misc/guc.cpp b/src/common/backend/utils/misc/guc.cpp index 11cb258db..4f002d910 100755 --- a/src/common/backend/utils/misc/guc.cpp +++ b/src/common/backend/utils/misc/guc.cpp @@ -3295,6 +3295,18 @@ static void InitConfigureNamesString() NULL, NULL, show_lcgroup_name}, + { {"delimiter_name", + PGC_USERSET, + NODE_ALL, + UNGROUPED, + gettext_noop( "Shows delimiter name."), + NULL, + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE}, + &u_sess->attr.attr_common.delimiter_name, + ";", + NULL, + NULL, + NULL}, {{"search_path", PGC_USERSET, NODE_ALL, diff --git a/src/include/knl/knl_guc/knl_session_attr_common.h b/src/include/knl/knl_guc/knl_session_attr_common.h index f83cc51d6..57042dad6 100644 --- a/src/include/knl/knl_guc/knl_session_attr_common.h +++ b/src/include/knl/knl_guc/knl_session_attr_common.h @@ -236,6 +236,7 @@ typedef struct knl_session_attr_common { void** extension_session_vars_array; char* threadpool_reset_percent_item; int threadpool_reset_percent_list[2]; + char* delimiter_name; } knl_session_attr_common; #endif /* SRC_INCLUDE_KNL_KNL_SESSION_ATTR_COMMON_H_ */ diff --git a/src/include/parser/scanner.h b/src/include/parser/scanner.h index a779a81bb..1a23cff14 100644 --- a/src/include/parser/scanner.h +++ b/src/include/parser/scanner.h @@ -49,6 +49,8 @@ typedef union core_YYSTYPE { */ #define YYLTYPE int +#define DELIMITER_LENGTH 16 + /* * Another important component of the scanner's API is the token code numbers. * However, those are not defined in this file, because bison insists on @@ -117,6 +119,9 @@ typedef struct core_yy_extra_type { int func_param_end; /* function and procedure param string end pos,exclude right parenthesis */ bool isPlpgsqlKeyWord; const PlpgsqlKeywordValue* plKeywordValue; + bool is_delimiter_name; + bool is_last_colon; + bool is_proc_end; } core_yy_extra_type; #ifdef FRONTEND_PARSER diff --git a/src/test/regress/expected/mysql_delimiter.out b/src/test/regress/expected/mysql_delimiter.out new file mode 100644 index 000000000..17c7a400f --- /dev/null +++ b/src/test/regress/expected/mysql_delimiter.out @@ -0,0 +1,99 @@ +-- B db compatibility case +drop database if exists my_test; +NOTICE: database "my_test" does not exist, skipping +create database my_test dbcompatibility 'B'; +\c my_test +--Test default delimiter +select 1; + ?column? +---------- + 1 +(1 row) + +--Test delimiter aa +delimiter aa; +select 1aa + ?column? +---------- + 1 +(1 row) + +select 1aaselect 1;aa + ?column? +---------- + 1 +(1 row) + + ?column? +---------- + 1 +(1 row) + +select kaa +ERROR: column "k" does not exist +LINE 1: select k aa + ^ +CONTEXT: referenced column: k +delimiter ;aa +--Test delimiter // +delimiter //; +select 1// + ?column? +---------- + 1 +(1 row) + +delimiter ;// +--Test delimiter length +delimiter aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; +ERROR: syntax error at or near ";" +LINE 1: delimiter aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + ^ +--Test delimiter % +delimiter %; +select 1% + ?column? +---------- + 1 +(1 row) + +delimiter ;% +--Test delimiter 'Mysql' +delimiter 'Mysql'; +select 1Mysql + ?column? +---------- + 1 +(1 row) + +delimiter ;Mysql +--Test other +delimiter sds; +delimiter aasds +select 1aa + ?column? +---------- + 1 +(1 row) + +delimiter ;aa +-- +delimiter asd ss; +select 1asd + ?column? +---------- + 1 +(1 row) + +delimiter ;asd +delimiter bb +delimiter aa +select 1aa + ?column? +---------- + 1 +(1 row) + +delimiter ; +\c regression +drop database my_test; diff --git a/src/test/regress/output/recovery_2pc_tools.source b/src/test/regress/output/recovery_2pc_tools.source index b2853a2a8..6cb6f76d8 100644 --- a/src/test/regress/output/recovery_2pc_tools.source +++ b/src/test/regress/output/recovery_2pc_tools.source @@ -198,6 +198,7 @@ select name,vartype,unit,min_val,max_val from pg_settings where name <> 'qunit_c default_transaction_read_only | bool | | | default_with_oids | bool | | | defer_csn_cleanup_time | integer | ms | 0 | 2147483647 + delimiter_name | string | | | dfs_partition_directory_length | integer | | 92 | 7999 dirty_page_percent_max | real | | 0.1 | 1 disable_memory_protect | bool | | | diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index d051f5c58..49841e013 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -984,7 +984,7 @@ test: fdw_audit test: gs_global_config_audit test: detail declare_multiple_variable test: gs_dump_encrypt substr -test: composite_datum_record mysql_function b_comments mysql_syntax +test: composite_datum_record mysql_function b_comments mysql_syntax mysql_delimiter test: join_test_alias alter_ctable_compress test: ignore/ignore_type_transform ignore/ignore_not_null_constraints ignore/ignore_unique_constraints ignore/ignore_no_matched_partition diff --git a/src/test/regress/sql/mysql_delimiter.sql b/src/test/regress/sql/mysql_delimiter.sql new file mode 100644 index 000000000..54bc0d2c4 --- /dev/null +++ b/src/test/regress/sql/mysql_delimiter.sql @@ -0,0 +1,51 @@ +-- B db compatibility case +drop database if exists my_test; +create database my_test dbcompatibility 'B'; +\c my_test + +--Test default delimiter +select 1; + +--Test delimiter aa +delimiter aa; +select 1aa +select 1aaselect 1;aa +select kaa +delimiter ;aa + +--Test delimiter // +delimiter //; +select 1// +delimiter ;// + +--Test delimiter length +delimiter aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + +--Test delimiter % +delimiter %; +select 1% +delimiter ;% + +--Test delimiter 'Mysql' +delimiter 'Mysql'; +select 1Mysql +delimiter ;Mysql + +--Test other +delimiter sds; +delimiter aasds +select 1aa +delimiter ;aa + +-- +delimiter asd ss; +select 1asd +delimiter ;asd + +delimiter bb +delimiter aa +select 1aa +delimiter ; + +\c regression +drop database my_test; \ No newline at end of file