From 06510b299c5ef0e290d55a215f437dd74b812bc5 Mon Sep 17 00:00:00 2001 From: Kuba Kaflik Date: Mon, 5 Aug 2024 13:25:03 +0200 Subject: [PATCH] Fix INSERT statement normalization match backtick table name (#1366) --- batch.go | 9 +++++-- batch_test.go | 35 +++++++++++++++++++++++++ tests/issues/1345_test.go | 54 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 tests/issues/1345_test.go diff --git a/batch.go b/batch.go index 9f17d37ebf..15d118675f 100644 --- a/batch.go +++ b/batch.go @@ -25,10 +25,15 @@ import ( "github.com/pkg/errors" ) -var normalizeInsertQueryMatch = regexp.MustCompile(`(?i)(INSERT\s+INTO\s+([^( ]+)(?:\s*\([^()]*(?:\([^()]*\)[^()]*)*\))?)(?:\s*VALUES)?`) +var normalizeInsertQueryMatch = regexp.MustCompile(`(?i)(INSERT\s+INTO\s+([^(]+)(?:\s*\([^()]*(?:\([^()]*\)[^()]*)*\))?)(?:\s*VALUES)?`) +var truncateFormat = regexp.MustCompile(`\sFORMAT\s+[^\s]+`) +var truncateValues = regexp.MustCompile(`\sVALUES\s.*$`) var extractInsertColumnsMatch = regexp.MustCompile(`INSERT INTO .+\s\((?P.+)\)$`) func extractNormalizedInsertQueryAndColumns(query string) (normalizedQuery string, tableName string, columns []string, err error) { + query = truncateFormat.ReplaceAllString(query, "") + query = truncateValues.ReplaceAllString(query, "") + matches := normalizeInsertQueryMatch.FindStringSubmatch(query) if len(matches) == 0 { err = errors.Errorf("invalid INSERT query: %s", query) @@ -36,7 +41,7 @@ func extractNormalizedInsertQueryAndColumns(query string) (normalizedQuery strin } normalizedQuery = fmt.Sprintf("%s FORMAT Native", matches[1]) - tableName = matches[2] + tableName = strings.TrimSpace(matches[2]) columns = make([]string, 0) matches = extractInsertColumnsMatch.FindStringSubmatch(matches[1]) diff --git a/batch_test.go b/batch_test.go index 1fd075bd8d..b7a858b5de 100644 --- a/batch_test.go +++ b/batch_test.go @@ -45,6 +45,27 @@ func TestExtractNormalizedInsertQueryAndColumns(t *testing.T) { expectedColumns: []string{"col1", "col2"}, expectedError: false, }, + { + query: "INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2`", + expectedNormalizedQuery: "INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2` FORMAT Native", + expectedTableName: "`_test_1345# $.ДБ`.`2. Таблица №2`", + expectedColumns: []string{}, + expectedError: false, + }, + { + query: "INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2` (col1, col2)", + expectedNormalizedQuery: "INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2` (col1, col2) FORMAT Native", + expectedTableName: "`_test_1345# $.ДБ`.`2. Таблица №2`", + expectedColumns: []string{"col1", "col2"}, + expectedError: false, + }, + { + query: "INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2` (col1, col2) VALUES (1, 2)", + expectedNormalizedQuery: "INSERT INTO `_test_1345# $.ДБ`.`2. Таблица №2` (col1, col2) FORMAT Native", + expectedTableName: "`_test_1345# $.ДБ`.`2. Таблица №2`", + expectedColumns: []string{"col1", "col2"}, + expectedError: false, + }, { query: "INSERT INTO table_name (col1, col2) VALUES (1, 2) FORMAT Native", expectedNormalizedQuery: "INSERT INTO table_name (col1, col2) FORMAT Native", @@ -66,6 +87,20 @@ func TestExtractNormalizedInsertQueryAndColumns(t *testing.T) { expectedColumns: []string{}, expectedError: false, }, + { + query: "INSERT INTO table_name FORMAT JSONEachRow", + expectedNormalizedQuery: "INSERT INTO table_name FORMAT Native", + expectedTableName: "table_name", + expectedColumns: []string{}, + expectedError: false, + }, + { + query: "INSERT INTO `table_name` VALUES (1, 2)", + expectedNormalizedQuery: "INSERT INTO `table_name` FORMAT Native", + expectedTableName: "`table_name`", + expectedColumns: []string{}, + expectedError: false, + }, { query: "SELECT * FROM table_name", expectedError: true, diff --git a/tests/issues/1345_test.go b/tests/issues/1345_test.go new file mode 100644 index 0000000000..cfae947de8 --- /dev/null +++ b/tests/issues/1345_test.go @@ -0,0 +1,54 @@ +package issues + +import ( + "context" + "testing" + + "github.com/ClickHouse/clickhouse-go/v2/tests" + "github.com/stretchr/testify/require" +) + +func TestIssue1345(t *testing.T) { + ctx := context.Background() + + conn, err := tests.GetConnection("issues", nil, nil, nil) + require.NoError(t, err) + defer conn.Close() + + require.NoError(t, conn.Exec(ctx, "CREATE DATABASE IF NOT EXISTS `_test_1345#$.ДБ`")) + defer conn.Exec(ctx, "DROP TABLE `_test_1345#$.ДБ`") + + require.NoError(t, conn.Exec(ctx, "CREATE TABLE IF NOT EXISTS `_test_1345#$.ДБ`.`2. Таблица №2` (i UInt64, s String) ENGINE = Memory()")) + + batch, err := conn.PrepareBatch(ctx, "INSERT INTO `_test_1345#$.ДБ`.`2. Таблица №2`") + require.NoError(t, err) + + var ( + i = uint64(32) + s = "b" + ) + + err = batch.Append(i, s) + require.NoError(t, err) + + err = batch.Send() + require.NoError(t, err) + + rows, err := conn.Query(ctx, "SELECT * FROM `_test_1345#$.ДБ`.`2. Таблица №2`") + require.NoError(t, err) + + require.True(t, rows.Next()) + + var ( + actualInt uint64 + actualStr string + ) + err = rows.Scan(&actualInt, &actualStr) + require.NoError(t, err) + + require.Equal(t, i, actualInt) + require.Equal(t, s, actualStr) + + require.NoError(t, rows.Close()) + require.NoError(t, rows.Err()) +}