Skip to content
7 changes: 4 additions & 3 deletions lib/column/enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func extractEnumNamedValues(chType Type) (typ string, values []string, indexes [
var skippedValueTokens []int
var indexFound bool
var valueFound bool
var valueIndex = 0
valueIndex := 0

for c := 0; c < len(src); c++ {
token := src[c]
Expand Down Expand Up @@ -144,14 +144,15 @@ func extractEnumNamedValues(chType Type) (typ string, values []string, indexes [
}

foundName := src[foundValueOffset : foundValueOffset+foundValueLen]
for _, skipped := range skippedValueTokens {
foundName = append(foundName[:skipped], foundName[skipped+1:]...)
for i, skipped := range skippedValueTokens {
foundName = append(foundName[:skipped-i], foundName[skipped-i+1:]...)
}

indexes = append(indexes, valueIndex)
values = append(values, string(foundName))
indexFound = false
valueFound = false
skippedValueTokens = skippedValueTokens[:0]
}
}

Expand Down
34 changes: 34 additions & 0 deletions lib/column/enum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,40 @@ func TestExtractEnumNamedValues(t *testing.T) {
0: "b",
},
},
{
name: "Enum8 with escaped quote in multiple values",
chType: `Enum8('a\'b'=1,'c\'d'=2)`,
expectedType: "Enum8",
expectedValues: map[int]string{
1: "a'b",
2: "c'd",
},
},
{
name: "Enum8 with multiple escaped quotes in one value",
chType: `Enum8('a\'b\'c'=1)`,
expectedType: "Enum8",
expectedValues: map[int]string{
1: "a'b'c",
},
},
{
name: "Enum16 with escaped quote in multiple values",
chType: `Enum16('a\'b'=1,'c\'d'=2)`,
expectedType: "Enum16",
expectedValues: map[int]string{
1: "a'b",
2: "c'd",
},
},
{
name: "Enum16 with multiple escaped quotes in one value",
chType: `Enum16('a\'b\'c'=1)`,
expectedType: "Enum16",
expectedValues: map[int]string{
1: "a'b'c",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
91 changes: 91 additions & 0 deletions tests/issues/1839_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package issues

import (
"context"
"testing"

"github.com/stretchr/testify/require"

"github.com/ClickHouse/clickhouse-go/v2"
clickhouse_tests "github.com/ClickHouse/clickhouse-go/v2/tests"
)

func Test1839(t *testing.T) {
testEnv, err := clickhouse_tests.GetTestEnvironment("issues")
require.NoError(t, err)
conn, err := clickhouse_tests.TestClientWithDefaultSettings(testEnv)
require.NoError(t, err)
t.Cleanup(func() { conn.Close() })

require.NoError(t, conn.Exec(t.Context(), `
CREATE TABLE test_1839 (
col Enum8('a\'b' = 1, 'c\'d' = 2, 'a\'b\'c' = 3)
) ENGINE = MergeTree ORDER BY tuple()
`), "Create table failed")

t.Cleanup(func() {
if err := conn.Exec(context.Background(), "DROP TABLE IF EXISTS test_1839"); err != nil {
t.Logf("DROP TABLE test_1839 failed: %v", err)
}
})

batch, err := conn.PrepareBatch(t.Context(), "INSERT INTO test_1839")
require.NoError(t, err, "PrepareBatch failed")
require.NoError(t, batch.Append("a'b"), "Append failed for %q", "a'b")
require.NoError(t, batch.Append("c'd"), "Append failed for %q", "c'd")
require.NoError(t, batch.Append("a'b'c"), "Append failed for %q", "a'b'c")
require.NoError(t, batch.Send(), "Send failed")

rows, err := conn.Query(t.Context(), "SELECT col FROM test_1839 ORDER BY col")
require.NoError(t, err, "SELECT col failed")
defer rows.Close()

var results []string
for rows.Next() {
var s string
require.NoError(t, rows.Scan(&s))
results = append(results, s)
}
require.NoError(t, rows.Err(), "Scan failed")
require.Equal(t, []string{"a'b", "c'd", "a'b'c"}, results)
}

func Test1839HTTP(t *testing.T) {
testEnv, err := clickhouse_tests.GetTestEnvironment("issues")
require.NoError(t, err)
conn, err := clickhouse_tests.GetConnection("issues", t, clickhouse.HTTP, clickhouse_tests.TestClientDefaultSettings(testEnv), nil, nil)
require.NoError(t, err)
t.Cleanup(func() { conn.Close() })

require.NoError(t, conn.Exec(t.Context(), `
CREATE TABLE test_1839_http (
col Enum8('a\'b' = 1, 'c\'d' = 2, 'a\'b\'c' = 3)
) ENGINE = MergeTree ORDER BY tuple()
`), "Create table failed")

t.Cleanup(func() {
if err := conn.Exec(context.Background(), "DROP TABLE IF EXISTS test_1839_http"); err != nil {
t.Logf("DROP TABLE test_1839_http failed: %v", err)
}
})

batch, err := conn.PrepareBatch(t.Context(), "INSERT INTO test_1839_http")
require.NoError(t, err, "PrepareBatch failed")
require.NoError(t, batch.Append("a'b"), "Append failed for %q", "a'b")
require.NoError(t, batch.Append("c'd"), "Append failed for %q", "c'd")
require.NoError(t, batch.Append("a'b'c"), "Append failed for %q", "a'b'c")
require.NoError(t, batch.Send(), "Send failed")

rows, err := conn.Query(t.Context(), "SELECT col FROM test_1839_http ORDER BY col")
require.NoError(t, err, "SELECT col failed")
defer rows.Close()

var results []string
for rows.Next() {
var s string
require.NoError(t, rows.Scan(&s))
results = append(results, s)
}
require.NoError(t, rows.Err(), "Scan failed")
require.Equal(t, []string{"a'b", "c'd", "a'b'c"}, results)
}
Loading