Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 3a31656

Browse files
author
Yury Yurochko
committed
feat: support per query settings (partially)
1 parent d6cb182 commit 3a31656

File tree

5 files changed

+119
-1
lines changed

5 files changed

+119
-1
lines changed

bootstrap.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,19 @@ func open(dsn string) (*clickhouse, error) {
149149
connOpenStrategy = connOpenInOrder
150150
}
151151

152+
settings, err := makeQuerySettings(query)
153+
if err != nil {
154+
return nil, err
155+
}
156+
152157
if v, err := strconv.ParseBool(query.Get("compress")); err == nil {
153158
compress = v
154159
}
155160

156161
var (
157162
ch = clickhouse{
158163
logf: func(string, ...interface{}) {},
164+
settings: settings,
159165
compress: compress,
160166
blockSize: blockSize,
161167
ServerInfo: data.ServerInfo{

clickhouse.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type clickhouse struct {
4747
buffer *bufio.Writer
4848
decoder *binary.Decoder
4949
encoder *binary.Encoder
50+
settings *querySettings
5051
compress bool
5152
blockSize int
5253
inTransaction bool

clickhouse_send_query.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,15 @@ func (ch *clickhouse) sendQuery(query string) error {
2929
ch.encoder.String("")
3030
}
3131

32-
if err := ch.encoder.String(""); err != nil { // settings
32+
// the settings are written as list of contiguous name-value pairs, finished with empty name
33+
if !ch.settings.IsEmpty() {
34+
ch.logf("[query settings] %s", ch.settings.settingsStr)
35+
if err := ch.settings.Serialize(ch.encoder); err != nil {
36+
return err
37+
}
38+
}
39+
// empty string is a marker of the end of the settings
40+
if err := ch.encoder.String(""); err != nil {
3341
return err
3442
}
3543
if err := ch.encoder.Uvarint(protocol.StateComplete); err != nil {

issues_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,18 @@ func TestNullableEnumWithoutLeadZero(t *testing.T) {
234234
}
235235
}
236236
}
237+
238+
func TestQuerySettings(t *testing.T) {
239+
settings := "max_memory_usage=1000&max_execution_time=10&max_execution_speed=10"
240+
connect, err := sql.Open(
241+
"clickhouse",
242+
"tcp://127.0.0.1:9000?debug=true"+"&"+settings,
243+
)
244+
require.Nil(t, err)
245+
require.Nil(t, connect.Ping())
246+
defer connect.Close()
247+
248+
_, err = connect.Query(`SELECT * FROM system.parts`)
249+
require.NotNil(t, err)
250+
require.Contains(t, err.Error(), "Memory limit")
251+
}

query_settings.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package clickhouse
2+
3+
import (
4+
"fmt"
5+
"net/url"
6+
"strconv"
7+
8+
"github.com/kshvakov/clickhouse/lib/binary"
9+
)
10+
11+
type querySettingType int
12+
13+
// all possible query setting's data type
14+
// TODO: support remaining data types
15+
const (
16+
uintQS querySettingType = iota + 1
17+
)
18+
19+
// description of single query setting
20+
type querySettingInfo struct {
21+
name string
22+
qsType querySettingType
23+
}
24+
25+
// all possible query settings
26+
// TODO: support remaining query serrings
27+
var querySettingList = []querySettingInfo{
28+
{"max_memory_usage", uintQS},
29+
{"max_execution_time", uintQS},
30+
{"max_execution_speed", uintQS},
31+
}
32+
33+
type querySettingValueEncoder func(enc *binary.Encoder) error
34+
35+
type querySettings struct {
36+
settings map[string]querySettingValueEncoder
37+
settingsStr string // used for debug output
38+
}
39+
40+
func makeQuerySettings(query url.Values) (*querySettings, error) {
41+
qs := &querySettings{
42+
settings: make(map[string]querySettingValueEncoder),
43+
settingsStr: "",
44+
}
45+
46+
for _, info := range querySettingList {
47+
valueStr := query.Get(info.name)
48+
if valueStr == "" {
49+
continue
50+
}
51+
52+
switch info.qsType {
53+
case uintQS:
54+
value, err := strconv.ParseUint(valueStr, 10, 64)
55+
if err != nil {
56+
return nil, err
57+
}
58+
qs.settings[info.name] = func(enc *binary.Encoder) error { return enc.Uvarint(value) }
59+
default:
60+
err := fmt.Errorf("query setting %s has unsupported data type", info.name)
61+
return nil, err
62+
}
63+
64+
if qs.settingsStr != "" {
65+
qs.settingsStr += "&"
66+
}
67+
qs.settingsStr += info.name + "=" + valueStr
68+
}
69+
70+
return qs, nil
71+
}
72+
73+
func (qs *querySettings) IsEmpty() bool {
74+
return len(qs.settings) == 0
75+
}
76+
77+
func (qs *querySettings) Serialize(enc *binary.Encoder) error {
78+
for name, fn := range qs.settings {
79+
if err := enc.String(name); err != nil {
80+
return err
81+
}
82+
if err := fn(enc); err != nil {
83+
return err
84+
}
85+
}
86+
87+
return nil
88+
}

0 commit comments

Comments
 (0)