From 9b0ab9d84cdf9e4ba31e6266066c4c5d10dc6c96 Mon Sep 17 00:00:00 2001 From: kennytm Date: Wed, 14 Nov 2018 23:09:09 +0800 Subject: [PATCH] executor: properly escape backquotes in identifiers in SHOW CREATE TABLE (#8302) --- executor/show.go | 32 +++++++++++++++++++++++++------- executor/show_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/executor/show.go b/executor/show.go index b00ac27d74957..c680ba7a2012c 100644 --- a/executor/show.go +++ b/executor/show.go @@ -462,19 +462,35 @@ func getDefaultCollate(charsetName string) string { return "" } +// escape the identifier for pretty-printing. +// For instance, the identifier "foo `bar`" will become "`foo ``bar```". +// The sqlMode controls whether to escape with backquotes (`) or double quotes +// (`"`) depending on whether mysql.ModeANSIQuotes is enabled. +func escape(cis model.CIStr, sqlMode mysql.SQLMode) string { + var quote string + if sqlMode&mysql.ModeANSIQuotes != 0 { + quote = `"` + } else { + quote = "`" + } + return quote + strings.Replace(cis.O, quote, quote+quote, -1) + quote +} + func (e *ShowExec) fetchShowCreateTable() error { tb, err := e.getTable() if err != nil { return errors.Trace(err) } + sqlMode := e.ctx.GetSessionVars().SQLMode + // TODO: let the result more like MySQL. var buf bytes.Buffer - buf.WriteString(fmt.Sprintf("CREATE TABLE `%s` (\n", tb.Meta().Name.O)) + buf.WriteString(fmt.Sprintf("CREATE TABLE %s (\n", escape(tb.Meta().Name, sqlMode))) var pkCol *table.Column var hasAutoIncID bool for i, col := range tb.Cols() { - buf.WriteString(fmt.Sprintf(" `%s` %s", col.Name.O, col.GetTypeDesc())) + buf.WriteString(fmt.Sprintf(" %s %s", escape(col.Name, sqlMode), col.GetTypeDesc())) if col.IsGenerated() { // It's a generated column. buf.WriteString(fmt.Sprintf(" GENERATED ALWAYS AS (%s)", col.GeneratedExprString)) @@ -530,7 +546,7 @@ func (e *ShowExec) fetchShowCreateTable() error { if pkCol != nil { // If PKIsHanle, pk info is not in tb.Indices(). We should handle it here. buf.WriteString(",\n") - buf.WriteString(fmt.Sprintf(" PRIMARY KEY (`%s`)", pkCol.Name.O)) + buf.WriteString(fmt.Sprintf(" PRIMARY KEY (%s)", escape(pkCol.Name, sqlMode))) } if len(tb.Indices()) > 0 { @@ -548,14 +564,14 @@ func (e *ShowExec) fetchShowCreateTable() error { if idxInfo.Primary { buf.WriteString(" PRIMARY KEY ") } else if idxInfo.Unique { - buf.WriteString(fmt.Sprintf(" UNIQUE KEY `%s` ", idxInfo.Name.O)) + buf.WriteString(fmt.Sprintf(" UNIQUE KEY %s ", escape(idxInfo.Name, sqlMode))) } else { - buf.WriteString(fmt.Sprintf(" KEY `%s` ", idxInfo.Name.O)) + buf.WriteString(fmt.Sprintf(" KEY %s ", escape(idxInfo.Name, sqlMode))) } cols := make([]string, 0, len(idxInfo.Columns)) for _, c := range idxInfo.Columns { - colInfo := fmt.Sprintf("`%s`", c.Name.String()) + colInfo := escape(c.Name, sqlMode) if c.Length != types.UnspecifiedLength { colInfo = fmt.Sprintf("%s(%s)", colInfo, strconv.Itoa(c.Length)) } @@ -623,8 +639,10 @@ func (e *ShowExec) fetchShowCreateDatabase() error { return infoschema.ErrDatabaseNotExists.GenByArgs(e.DBName.O) } + sqlMode := e.ctx.GetSessionVars().SQLMode + var buf bytes.Buffer - fmt.Fprintf(&buf, "CREATE DATABASE `%s`", db.Name.O) + fmt.Fprintf(&buf, "CREATE DATABASE %s", escape(db.Name, sqlMode)) if s := db.Charset; len(s) > 0 { fmt.Fprintf(&buf, " /* !40100 DEFAULT CHARACTER SET %s */", s) } diff --git a/executor/show_test.go b/executor/show_test.go index ef105db383c19..6e77a157b8602 100644 --- a/executor/show_test.go +++ b/executor/show_test.go @@ -525,3 +525,32 @@ func (s *testSuite) TestShowTableStatus(c *C) { c.Assert(row.GetString(3), Equals, "Compact") } } + +func (s *testSuite) TestShowEscape(c *C) { + tk := testkit.NewTestKit(c, s.store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists `t``abl\"e`") + tk.MustExec("create table `t``abl\"e`(`c``olum\"n` int(11) primary key)") + tk.MustQuery("show create table `t``abl\"e`").Check(testutil.RowsWithSep("|", + ""+ + "t`abl\"e CREATE TABLE `t``abl\"e` (\n"+ + " `c``olum\"n` int(11) NOT NULL,\n"+ + " PRIMARY KEY (`c``olum\"n`)\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin", + )) + + // ANSI_QUOTES will change the SHOW output + tk.MustExec("set @old_sql_mode=@@sql_mode") + tk.MustExec("set sql_mode=ansi_quotes") + tk.MustQuery("show create table \"t`abl\"\"e\"").Check(testutil.RowsWithSep("|", + ""+ + "t`abl\"e CREATE TABLE \"t`abl\"\"e\" (\n"+ + " \"c`olum\"\"n\" int(11) NOT NULL,\n"+ + " PRIMARY KEY (\"c`olum\"\"n\")\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin", + )) + + tk.MustExec("rename table \"t`abl\"\"e\" to t") + tk.MustExec("set sql_mode=@old_sql_mode") +}