From b9f2b554847f81e29e2ba6f0b9616d2baf96613f Mon Sep 17 00:00:00 2001 From: Roman Nozdrin Date: Wed, 24 Jan 2018 09:40:44 +0300 Subject: [PATCH] MCOL-1384 Parser accepts quotes with qualified dbobject identifiers. --- dbcon/ddlpackage/CMakeLists.txt | 4 ++ dbcon/ddlpackage/ddl.l | 100 +++++++++++++++++++++++++++++--- dbcon/ddlpackage/ddl.y | 17 +++++- dbcon/mysql/ha_calpont_ddl.cpp | 74 +++++++++++------------ 4 files changed, 149 insertions(+), 46 deletions(-) diff --git a/dbcon/ddlpackage/CMakeLists.txt b/dbcon/ddlpackage/CMakeLists.txt index 27d2a3015..ae2f82fa9 100644 --- a/dbcon/ddlpackage/CMakeLists.txt +++ b/dbcon/ddlpackage/CMakeLists.txt @@ -9,6 +9,10 @@ ADD_CUSTOM_COMMAND( DEPENDS ddl.y ddl.l ) +# Parser puts extra info to stderr. +INCLUDE(../../check_compiler_flag.cmake) +MY_CHECK_AND_SET_COMPILER_FLAG("-DYYDEBUG" DEBUG) + ########### next target ############### set(ddlpackage_LIB_SRCS diff --git a/dbcon/ddlpackage/ddl.l b/dbcon/ddlpackage/ddl.l index ac51fe020..f65ef161d 100644 --- a/dbcon/ddlpackage/ddl.l +++ b/dbcon/ddlpackage/ddl.l @@ -18,6 +18,7 @@ /* $Id: ddl.l 9341 2013-03-27 14:10:35Z chao $ */ %{ +#include #include #include #include @@ -31,10 +32,11 @@ #endif using namespace ddlpackage; +typedef enum { NOOP, STRIP_QUOTES, STRIP_QUOTES_FQ } copy_action_t; int lineno = 1; void ddlerror(struct pass_to_bison* x, char const *s); -static char* scanner_copy(char *str, yyscan_t yyscanner); +static char* scanner_copy(char *str, yyscan_t yyscanner, copy_action_t action = NOOP ); %} @@ -54,6 +56,10 @@ horiz_space [ \t\f] newline [\n\r] non_newline [^\n\r] +quote ' +double_quote \" +grave_accent ` + comment ("--"{non_newline}*) self [,()\[\].;\:\+\-\*\/\%\^\<\>\=] whitespace ({space}+|{comment}) @@ -62,6 +68,12 @@ digit [0-9] ident_start [A-Za-z\200-\377_] ident_cont [A-Za-z\200-\377_0-9\$] identifier {ident_start}{ident_cont}* +/* fully qualified names regexes */ +fq_identifier {identifier}\.{identifier} +identifier_quoted {grave_accent}{identifier}{grave_accent} +identifier_double_quoted {double_quote}{identifier}{double_quote} +fq_quoted ({identifier_quoted}|{identifier})\.({identifier_quoted}|{identifier}) +fq_double_quoted ({identifier_double_quoted}|{identifier})\.({identifier_double_quoted}|{identifier}) integer [-+]?{digit}+ decimal ([-+]?({digit}*\.{digit}+)|({digit}+\.{digit}*)) @@ -69,11 +81,16 @@ real ({integer}|{decimal})[Ee][-+]?{digit}+ realfail1 ({integer}|{decimal})[Ee] realfail2 ({integer}|{decimal})[Ee][-+] -quote ' -grave_accent ` %% + +{identifier_quoted} { ddlget_lval(yyscanner)->str = scanner_copy( ddlget_text(yyscanner), yyscanner, STRIP_QUOTES ); return IDENT; } +{identifier_double_quoted} { ddlget_lval(yyscanner)->str = scanner_copy( ddlget_text(yyscanner), yyscanner, STRIP_QUOTES ); return IDENT; } +{fq_identifier} { ddlget_lval(yyscanner)->str = scanner_copy(ddlget_text(yyscanner), yyscanner); return FQ_IDENT; } +{fq_quoted} { ddlget_lval(yyscanner)->str = scanner_copy(ddlget_text(yyscanner), yyscanner, STRIP_QUOTES_FQ); return FQ_IDENT; } +{fq_double_quoted} { ddlget_lval(yyscanner)->str = scanner_copy(ddlget_text(yyscanner), yyscanner, STRIP_QUOTES_FQ); return FQ_IDENT; } + ACTION {return ACTION;} ADD {return ADD;} ALTER {return ALTER;} @@ -198,6 +215,11 @@ using namespace ddlpackage; */ void scanner_init(const char* str, yyscan_t yyscanner) { +#ifdef YYDEBUG + extern int ddldebug; + ddldebug = 1; +#endif + size_t slen = strlen(str); scan_data* pScanData = (scan_data*)ddlget_extra(yyscanner); @@ -246,10 +268,72 @@ void scanner_finish(yyscan_t yyscanner) pScanData->valbuf.clear(); } -char* scanner_copy (char *str, yyscan_t yyscanner) +char* scanner_copy (char *str, yyscan_t yyscanner, copy_action_t action) { - char* nv = strdup(str); - if(nv) - ((scan_data*)ddlget_extra(yyscanner))->valbuf.push_back(nv); - return nv; + char* result; + char* nv = strdup(str); + result = nv; + + // free strduped memory later to prevent possible memory leak + if(nv) + ((scan_data*)ddlget_extra(yyscanner))->valbuf.push_back(nv); + + if(action == STRIP_QUOTES) + { + nv[strlen(str) - 1] = '\0'; + result = nv + 1; + } + else if (action == STRIP_QUOTES_FQ) + { + bool move_left = false; + bool move_right = false; + char* left = nv; + char* tmp_first = nv; + // MCOL-1384 Loop through all comas in this quoted fq id + // looking for $quote_sign.$quote_sign sequence. + char* fq_delimiter; + int tmp_pos = 0; + while((fq_delimiter = strchr(tmp_first, '.')) != NULL) + { + if( (*(fq_delimiter -1) == '`' && *(fq_delimiter + 1) == '`') || + (*(fq_delimiter -1) == '"' && *(fq_delimiter + 1) == '"') ) + { + tmp_pos += fq_delimiter - tmp_first; + break; + } + tmp_first = fq_delimiter; + } + + char* fq_delimiter_orig = str + tmp_pos; + char* right = fq_delimiter + 1; + char* right_orig = fq_delimiter_orig + 1; + // MCOL-1384 Strip quotes from the left part. + if(*left == '"' || *left == '`') + { + result = left + 1; + *(fq_delimiter - 1) = '.'; + move_left = true; + } + else + { + fq_delimiter += 1; + } + + int right_length = strlen(right); + // MCOL-1384 Strip quotes from the right part. + if(*right == '`' || *right == '"') + { + right += 1; right_orig += 1; + right_length -= 2; + move_right = true; + *(fq_delimiter + right_length) = '\0'; + } + + if(move_left || move_right) + { + strncpy(fq_delimiter, right_orig, right_length); + } + } + + return result; } diff --git a/dbcon/ddlpackage/ddl.y b/dbcon/ddlpackage/ddl.y index 8d36b2c2b..982167287 100644 --- a/dbcon/ddlpackage/ddl.y +++ b/dbcon/ddlpackage/ddl.y @@ -48,6 +48,7 @@ */ %{ +#include "string.h" #include "sqlparser.h" #ifdef _MSC_VER @@ -121,7 +122,7 @@ REFERENCES RENAME RESTRICT SET SMALLINT TABLE TEXT TIME TINYBLOB TINYTEXT TINYINT TO UNIQUE UNSIGNED UPDATE USER SESSION_USER SYSTEM_USER VARCHAR VARBINARY VARYING WITH ZONE DOUBLE IDB_FLOAT REAL CHARSET IDB_IF EXISTS CHANGE TRUNCATE -%token IDENT FCONST SCONST CP_SEARCH_CONDITION_TEXT ICONST DATE +%token FQ_IDENT IDENT FCONST SCONST CP_SEARCH_CONDITION_TEXT ICONST DATE /* Notes: * 1. "ata" stands for alter_table_action @@ -611,7 +612,19 @@ table_name: ; qualified_name: - IDENT '.' IDENT {$$ = new QualifiedName($1, $3);} + FQ_IDENT { + char* delimeterPosition = strchr(const_cast($1), '.'); + if( delimeterPosition ) + { + *delimeterPosition = '\0'; + char* schemaName = const_cast($1); + char* tableName = delimeterPosition + 1; + $$ = new QualifiedName(schemaName, tableName); + *delimeterPosition = '.'; + } + else + $$ = new QualifiedName($1); + } | IDENT { if (x->fDBSchema.size()) $$ = new QualifiedName((char*)x->fDBSchema.c_str(), $1); diff --git a/dbcon/mysql/ha_calpont_ddl.cpp b/dbcon/mysql/ha_calpont_ddl.cpp index 611f1da3b..74b413667 100755 --- a/dbcon/mysql/ha_calpont_ddl.cpp +++ b/dbcon/mysql/ha_calpont_ddl.cpp @@ -2039,51 +2039,53 @@ int ha_calpont_impl_delete_table_(const char *db, const char *name, cal_connecti int ha_calpont_impl_rename_table_(const char* from, const char* to, cal_connection_info& ci) { - THD *thd = current_thd; - string emsg; + THD* thd = current_thd; + string emsg; - ostringstream stmt1; - pair fromPair; - pair toPair; - string stmt; + pair fromPair; + pair toPair; + string stmt; - //this is replcated DDL, treat it just like SSO - if (thd->slave_thread) - return 0; + //this is replcated DDL, treat it just like SSO + if (thd->slave_thread) + return 0; - //@bug 5660. Error out REAL DDL/DML on slave node. - // When the statement gets here, it's NOT SSO or RESTRICT - if (ci.isSlaveNode) - { - string emsg = logging::IDBErrorInfo::instance()->errorMsg(ERR_DML_DDL_SLAVE); - setError(current_thd, ER_CHECK_NOT_IMPLEMENTED, emsg); - return 1; - } + //@bug 5660. Error out REAL DDL/DML on slave node. + // When the statement gets here, it's NOT SSO or RESTRICT + if (ci.isSlaveNode) + { + string emsg = logging::IDBErrorInfo::instance()->errorMsg(ERR_DML_DDL_SLAVE); + setError(current_thd, ER_CHECK_NOT_IMPLEMENTED, emsg); + return 1; + } - fromPair = parseTableName(from); - toPair = parseTableName(to); + fromPair = parseTableName(from); + toPair = parseTableName(to); - if (fromPair.first != toPair.first) - { - thd->get_stmt_da()->set_overwrite_status(true); - thd->raise_error_printf(ER_CHECK_NOT_IMPLEMENTED, "Both tables must be in the same database to use RENAME TABLE"); - return -1; - } + if (fromPair.first != toPair.first) + { + thd->get_stmt_da()->set_overwrite_status(true); + thd->raise_error_printf(ER_CHECK_NOT_IMPLEMENTED, "Both tables must be in the same database to use RENAME TABLE"); + return -1; + } - stmt1 << "alter table " << fromPair.second << " rename to " << toPair.second << ";"; + // This explicitely shields both db objects with quotes that the lexer strips down later. + stmt = "alter table `" + fromPair.second + "` rename to `" + toPair.second + "`;"; + string db; - stmt = stmt1.str(); - string db; - if ( fromPair.first.length() !=0 ) - db = fromPair.first; - else if ( thd->db ) - db = thd->db; + if ( thd->db ) + db = thd->db; + else if ( fromPair.first.length() != 0 ) + db = fromPair.first; + else + db = toPair.first; - int rc = ProcessDDLStatement(stmt, db, "", tid2sid(thd->thread_id), emsg); - if (rc != 0) - push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 9999, emsg.c_str()); + int rc = ProcessDDLStatement(stmt, db, "", tid2sid(thd->thread_id), emsg); - return rc; + if (rc != 0) + push_warning(thd, Sql_condition::WARN_LEVEL_ERROR, 9999, emsg.c_str()); + + return rc; }