1
0
mirror of https://github.com/uptrace/go-clickhouse.git synced 2025-08-10 22:21:30 +02:00

126 Commits

Author SHA1 Message Date
Vladimir Mihailenco
12a9b1e85d chore: sync API with Bun 2023-10-01 14:43:38 +03:00
Vladimir Mihailenco
48aa57b286 chore: remove missing releaseConn 2023-10-01 13:19:30 +03:00
Vladimir Mihailenco
a4c3d24dfd feat(chmigrate): support distributed migrations 2023-09-07 13:06:28 +03:00
Vladimir Mihailenco
faaf5d3708 fix: escape cluster name 2023-09-07 11:03:37 +03:00
Vladimir Mihailenco
2d9abe8a1a Merge pull request #70 from kzaitsev/add-union-expr
feat: add union expr
2023-08-29 16:24:48 +03:00
Kirill Zaitsev
51c9cc65ba feat: union expr 2023-08-23 16:18:44 +03:00
Vladimir Mihailenco
e1af2632f1 chore: cleanup 2023-06-29 14:34:31 +03:00
Vladimir Mihailenco
08021a2c8f chore: bump go 2023-05-30 16:25:32 +03:00
Vladimir Mihailenco
03194bc434 chore: update dependencies 2023-05-30 15:45:02 +03:00
Vladimir Mihailenco
13e4911570 feat: split ch.In into ch.List, ch.Array, and ch.In 2023-05-30 15:42:52 +03:00
Vladimir Mihailenco
c0e67d2ec4 chore: improve auto-create database 2023-05-19 09:30:07 +03:00
Vladimir Mihailenco
ad9e630abf chore: remove bfloat16.Map 2023-04-27 09:22:40 +03:00
Vladimir Mihailenco
2d63e3b0ac chore: add chschema.Array 2023-03-29 16:13:10 +03:00
Vladimir Mihailenco
5cd79b48c3 chore: rename WithQuery to Apply 2023-03-29 15:22:39 +03:00
Vladimir Mihailenco
59dcbc03a4 feat: add Prewhere 2023-03-29 15:17:19 +03:00
Vladimir Mihailenco
e4f2453f97 Merge pull request #63 from uptrace/feat/on-cluster-option
feat: add on cluster option
2023-03-08 13:35:01 +02:00
Vladimir Mihailenco
f2100be620 feat: add on cluster option 2023-03-08 13:32:12 +02:00
Vladimir Mihailenco
998717a52f Merge pull request #56 from uptrace/release/v0.3.1
chore: release v0.3.1 (release.sh)
2023-02-16 10:14:24 +02:00
Vladimir Mihailenco
318bf373aa chore: release v0.3.1 (release.sh) 2023-02-16 10:11:51 +02:00
Vladimir Mihailenco
479603ef2b chore: tweak release script 2023-02-16 10:11:44 +02:00
Vladimir Mihailenco
06ec956b4a chore: update dependencies 2023-02-16 10:10:55 +02:00
Vladimir Mihailenco
e7b39d844b chore: support more types 2023-02-16 10:02:03 +02:00
Vladimir Mihailenco
8aad92b762 chore: update readme 2023-01-21 12:28:32 +02:00
Vladimir Mihailenco
e61921245e Merge pull request #55 from uptrace/release/v0.3.0
Release/v0.3.0
2023-01-21 12:26:17 +02:00
Vladimir Mihailenco
abe915c5b5 chore: update readme 2023-01-21 12:25:55 +02:00
Vladimir Mihailenco
e3920ba190 chore: update release script 2023-01-21 12:23:35 +02:00
Vladimir Mihailenco
17b14fa87e chore: release v0.3.0 (release.sh) 2023-01-21 12:22:42 +02:00
Vladimir Mihailenco
a3c6bbc729 chore: support Bool 2023-01-21 12:21:36 +02:00
Vladimir Mihailenco
44465cd278 chore: improve code gen 2023-01-21 12:14:00 +02:00
Vladimir Mihailenco
b5016bdc7a chore: add comment 2023-01-21 11:38:45 +02:00
Antony Dovgal
0f4c06861f fix: move FINAL modifier to the right place (#52)
The documentation (http://devdoc.net/database/ClickhouseDocs_19.4.1.3-docs/query_language/select/)
says FINAL has to be placed right after the table name, before SAMPLE:
  SELECT [DISTINCT] expr_list
    [FROM [db.]table | (subquery) | table_function] [FINAL]
    [SAMPLE sample_coeff]

At the moment it's appended to the end of the query which results in
invalid queries being generated when there are WHERE, ORDER BY or any
other clauses used.
2022-11-23 13:03:58 +02:00
Vladimir Mihailenco
3874d74090 chore: don't lock migrations by default 2022-11-09 10:11:14 +02:00
Vladimir Mihailenco
117fffe8ed chore: silence logs 2022-11-04 10:08:24 +02:00
Vladimir Mihailenco
b73c8fabf8 feat: add queries to create/drop views 2022-11-03 11:58:19 +02:00
Vladimir Mihailenco
79fddecbb5 chore: better WhereGroup 2022-11-01 15:22:10 +02:00
Vladimir Mihailenco
92d13a878d chore: bump default timeouts 2022-10-26 13:38:40 +03:00
Vladimir Mihailenco
0da7ce6381 Merge pull request #46 from uptrace/fix/append-bytes
chore: use unhex to append bytes
2022-09-03 09:41:05 +03:00
Vladimir Mihailenco
95a461b824 chore: use unhex to append bytes 2022-09-03 09:38:20 +03:00
Vladimir Mihailenco
d073ef0ad6 chore: append bytes like a string. Fixes #43 2022-09-01 10:38:41 +03:00
Vladimir Mihailenco
2830e1daf1 Merge pull request #42 from uptrace/release/v0.2.9
chore: release v0.2.9 (release.sh)
2022-08-30 12:47:00 +03:00
Vladimir Mihailenco
225aa3c101 chore: release v0.2.9 (release.sh) 2022-08-30 12:41:05 +03:00
Vladimir Mihailenco
37984d7ac5 chore: cleanup 2022-08-30 10:20:49 +03:00
Vladimir Mihailenco
9a58fde149 chore: remove dependabot 2022-08-30 10:05:26 +03:00
Vladimir Mihailenco
91f83c5f03 chore: update dependencies 2022-08-30 09:22:39 +03:00
Vladimir Mihailenco
7f26fb6fae chore: support multiple env keys 2022-08-28 16:53:07 +03:00
Vladimir Mihailenco
2c9485da7f chmigrate: add MissingMigrations 2022-08-27 10:57:01 +03:00
Vladimir Mihailenco
6bfb99e2c1 chore: cleanup conn pool 2022-07-28 16:57:33 +03:00
Vladimir Mihailenco
8bf4958230 feat: add WithAutoCreateDatabase option 2022-07-28 11:20:11 +03:00
Vladimir Mihailenco
546111f030 chore: go mod tidy 2022-07-22 12:00:19 +03:00
Vladimir Mihailenco
2b235ce448 chore: update dependencies 2022-07-22 11:57:19 +03:00
Vladimir Mihailenco
83a0bd0a2b Merge pull request #35 from uptrace/dependabot/go_modules/github.com/stretchr/testify-1.8.0
chore(deps): bump github.com/stretchr/testify from 1.7.5 to 1.8.0
2022-07-22 11:56:26 +03:00
Vladimir Mihailenco
7f77517af4 Merge pull request #38 from uptrace/feat/bfloat16
feat: add bfloat16 support
2022-07-22 11:56:11 +03:00
Vladimir Mihailenco
510b9caca8 feat: add bfloat16 support 2022-07-22 11:40:45 +03:00
Vladimir Mihailenco
1e8ca4767f feat: add Raw 2022-07-14 10:41:16 +03:00
dependabot[bot]
90c25609ec chore(deps): bump github.com/stretchr/testify from 1.7.5 to 1.8.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.5 to 1.8.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.5...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-04 20:13:30 +00:00
Vladimir Mihailenco
85badc2fe7 chore: add scanning into a map 2022-07-04 17:12:41 +03:00
Vladimir Mihailenco
988091e532 feat(chmigrate): add WithReplicated option 2022-07-02 14:39:22 +03:00
Vladimir Mihailenco
87e8ceb2a0 Merge pull request #34 from uptrace/fix/chmigrate-test
chore(chmigrate): add tests
2022-06-29 11:58:09 +03:00
Vladimir Mihailenco
0b3639be4e chore(chmigrate): add tests 2022-06-29 11:53:31 +03:00
Vladimir Mihailenco
e11b943cfa Merge pull request #31 from odenio/ro/fix_zero_applied_migrations
Fix for upping applying zero migrations
2022-06-28 08:55:48 +03:00
Vladimir Mihailenco
3ab7cb4325 Merge pull request #32 from uptrace/dependabot/go_modules/github.com/stretchr/testify-1.7.5
chore(deps): bump github.com/stretchr/testify from 1.7.3 to 1.7.5
2022-06-28 08:38:59 +03:00
dependabot[bot]
0e08f4d07a chore(deps): bump github.com/stretchr/testify from 1.7.3 to 1.7.5
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.3 to 1.7.5.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.3...v1.7.5)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-27 20:23:02 +00:00
Ryan Olds
e80d6d288b fix(migrate): upping was applying zero migrations 2022-06-27 09:55:38 -07:00
Vladimir Mihailenco
fd29a667a3 chore: update dependencies 2022-06-27 10:05:43 +03:00
Vladimir Mihailenco
1f5dd92e2a chore: cleanup 2022-06-27 10:01:23 +03:00
Vladimir Mihailenco
256e92dea4 Merge pull request #27 from uptrace/dependabot/github_actions/wagoid/commitlint-github-action-5
chore(deps): bump wagoid/commitlint-github-action from 4 to 5
2022-06-25 10:13:22 +03:00
Vladimir Mihailenco
3b62b36406 Merge pull request #28 from uptrace/dependabot/go_modules/github.com/pierrec/lz4/v4-4.1.15
chore(deps): bump github.com/pierrec/lz4/v4 from 4.1.14 to 4.1.15
2022-06-25 10:12:37 +03:00
dependabot[bot]
032e3a79ba chore(deps): bump github.com/pierrec/lz4/v4 from 4.1.14 to 4.1.15
Bumps [github.com/pierrec/lz4/v4](https://github.com/pierrec/lz4) from 4.1.14 to 4.1.15.
- [Release notes](https://github.com/pierrec/lz4/releases)
- [Commits](https://github.com/pierrec/lz4/compare/v4.1.14...v4.1.15)

---
updated-dependencies:
- dependency-name: github.com/pierrec/lz4/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-25 07:12:30 +00:00
Vladimir Mihailenco
380277d9bc Merge pull request #29 from uptrace/dependabot/go_modules/github.com/stretchr/testify-1.7.3
chore(deps): bump github.com/stretchr/testify from 1.7.2 to 1.7.3
2022-06-25 10:11:41 +03:00
Vladimir Mihailenco
d7b10ec656 Merge pull request #26 from odenio/master
Add option that makes migrator only mark migrations applied when migration successful
2022-06-25 10:11:24 +03:00
Ryan Olds
f559e06009 feat(migrate): added option to only mark migration up/down as applied on success
Fixed bug with returning wrong err var

Changed the field name to be more clear

fix: Mark applied/unapplied before up/down when marking applied on success and failure

fix: bug with inverted condition
2022-06-22 09:36:02 -07:00
dependabot[bot]
1de435ecfb chore(deps): bump github.com/stretchr/testify from 1.7.2 to 1.7.3
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.2...v1.7.3)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-20 20:23:36 +00:00
dependabot[bot]
e588da0468 chore(deps): bump wagoid/commitlint-github-action from 4 to 5
Bumps [wagoid/commitlint-github-action](https://github.com/wagoid/commitlint-github-action) from 4 to 5.
- [Release notes](https://github.com/wagoid/commitlint-github-action/releases)
- [Changelog](https://github.com/wagoid/commitlint-github-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/wagoid/commitlint-github-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: wagoid/commitlint-github-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-20 20:23:13 +00:00
Vladimir Mihailenco
1971cfbba3 Merge branch 'master' of github.com:uptrace/go-clickhouse 2022-06-20 10:03:24 +03:00
Vladimir Mihailenco
0262684d54 chore: improve instructions 2022-06-20 10:03:17 +03:00
Vladimir Mihailenco
9ce0d05f25 Merge pull request #24 from uptrace/dependabot/go_modules/github.com/stretchr/testify-1.7.2
chore(deps): bump github.com/stretchr/testify from 1.7.1 to 1.7.2
2022-06-07 09:06:09 +03:00
dependabot[bot]
83c58b3c5d chore(deps): bump github.com/stretchr/testify from 1.7.1 to 1.7.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.1 to 1.7.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.1...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-06 20:14:11 +00:00
Vladimir Mihailenco
2321bce7b1 Merge pull request #23 from uptrace/release/v0.2.8
chore: release v0.2.8 (release.sh)
2022-05-29 15:49:58 +03:00
Vladimir Mihailenco
005f04c00b chore: release v0.2.8 (release.sh) 2022-05-29 15:49:37 +03:00
Vladimir Mihailenco
f7545ceda4 chore: update dependencies 2022-05-29 15:49:17 +03:00
Vladimir Mihailenco
1ecd0e65e5 chore: cleanup 2022-05-18 16:33:03 +03:00
Vladimir Mihailenco
d00947a336 Merge branch 'master' of github.com:uptrace/go-clickhouse 2022-05-18 16:31:54 +03:00
Vladimir Mihailenco
83b354c348 chore: fix build 2022-05-18 16:31:39 +03:00
Vladimir Mihailenco
070531e7f3 Merge pull request #16 from uptrace/dependabot/github_actions/actions/setup-go-3
chore(deps): bump actions/setup-go from 2 to 3
2022-05-18 16:29:21 +03:00
dependabot[bot]
d101df6f41 chore(deps): bump actions/setup-go from 2 to 3
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-18 13:26:51 +00:00
Vladimir Mihailenco
83a1943672 chore: update Go version 2022-05-18 16:26:05 +03:00
Vladimir Mihailenco
d37290b7d2 Merge branch 'master' of github.com:uptrace/go-clickhouse 2022-05-18 16:24:04 +03:00
Vladimir Mihailenco
76504b2778 feat: enable opentelemetry support in protocol 2022-05-18 16:23:57 +03:00
Vladimir Mihailenco
ed0ec37764 Merge pull request #19 from uptrace/dependabot/github_actions/golangci/golangci-lint-action-3.2.0
chore(deps): bump golangci/golangci-lint-action from 3.1.0 to 3.2.0
2022-05-17 08:44:32 +03:00
dependabot[bot]
3565a91842 chore(deps): bump golangci/golangci-lint-action from 3.1.0 to 3.2.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.1.0...v3.2.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-16 20:22:34 +00:00
Vladimir Mihailenco
2b6746025b chore: disable compression in benchmarks 2022-05-08 12:08:42 +03:00
Vladimir Mihailenco
c683e8a787 Merge pull request #18 from uptrace/release/v0.2.7
chore: release v0.2.7 (release.sh)
2022-05-02 09:43:05 +03:00
Vladimir Mihailenco
157546562b chore: release v0.2.7 (release.sh) 2022-05-02 09:40:40 +03:00
Vladimir Mihailenco
3870a5e69c chore: cleanup unused const 2022-05-02 09:38:51 +03:00
Vladimir Mihailenco
ef260678ec feat: allow disabling compression for benchmarks 2022-05-02 09:31:14 +03:00
Vladimir Mihailenco
fa44842a34 Merge pull request #17 from uptrace/release/v0.2.6
chore: release v0.2.6 (release.sh)
2022-04-30 16:59:02 +03:00
Vladimir Mihailenco
c439f56e86 chore: release v0.2.6 (release.sh) 2022-04-30 16:57:57 +03:00
Vladimir Mihailenco
a0752b2b5c chore: automatically close block iterator 2022-04-30 16:54:06 +03:00
Vladimir Mihailenco
1543506535 chore: enable test 2022-04-30 15:02:07 +03:00
Vladimir Mihailenco
658ad14fc0 feat: add proper Rows implementation and some optimizations 2022-04-30 14:56:46 +03:00
Vladimir Mihailenco
c1e00ef235 feat: add support for DateTime64 2022-04-29 18:51:14 +03:00
Vladimir Mihailenco
5ea8b673a3 chore: update readme 2022-04-13 16:46:39 +03:00
Vladimir Mihailenco
4360cd8da9 chore: update readme 2022-04-05 09:55:19 +03:00
Vladimir Mihailenco
577b338610 Merge pull request #15 from uptrace/release/v0.2.5
chore: release v0.2.5 (release.sh)
2022-03-29 13:19:47 +03:00
Vladimir Mihailenco
bc46507e9a chore: release v0.2.5 (release.sh) 2022-03-29 13:16:49 +03:00
Vladimir Mihailenco
d304da6e36 chore: update dependencies 2022-03-29 13:16:28 +03:00
Vladimir Mihailenco
033c41395a fix: continue working with non UTC timezone 2022-03-28 16:41:07 +03:00
Vladimir Mihailenco
43a65a02e4 fix: change rollback to always record migrations 2022-03-25 15:50:00 +02:00
Vladimir Mihailenco
c8398f0d25 Merge pull request #14 from uptrace/release/v0.2.4
Release/v0.2.4
2022-03-23 14:41:46 +02:00
Vladimir Mihailenco
458b684801 chore: release v0.2.4 (release.sh) 2022-03-23 14:38:58 +02:00
Vladimir Mihailenco
b6446dc0e2 chore: fix release script 2022-03-23 14:38:37 +02:00
Vladimir Mihailenco
cd94293b45 Merge pull request #13 from uptrace/release/v0.2.3
chore: release v0.2.3 (release.sh)
2022-03-23 14:04:32 +02:00
Vladimir Mihailenco
57d701c4ed chore: release v0.2.3 (release.sh) 2022-03-23 14:00:14 +02:00
Vladimir Mihailenco
69ecbdf5eb Merge pull request #12 from uptrace/fix/conn-max-idle-time
feat: close idle connections after 30 minutes
2022-03-23 10:53:59 +02:00
Vladimir Mihailenco
99f247ba5e feat: close idle connections after 30 minutes 2022-03-23 10:48:38 +02:00
Vladimir Mihailenco
8e25946197 Merge branch 'master' of github.com:uptrace/go-clickhouse 2022-03-22 12:07:57 +02:00
Vladimir Mihailenco
3b4df51456 chore: add installation 2022-03-22 12:07:50 +02:00
Vladimir Mihailenco
11bf484562 Merge pull request #10 from uptrace/dependabot/go_modules/github.com/stretchr/testify-1.7.1
chore(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.1
2022-03-22 08:25:45 +02:00
dependabot[bot]
b880fd0df4 chore(deps): bump github.com/stretchr/testify from 1.7.0 to 1.7.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.0...v1.7.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-21 20:18:36 +00:00
Vladimir Mihailenco
17991b330b Merge pull request #9 from uptrace/release/v0.2.2
chore: release v0.2.2 (release.sh)
2022-03-21 09:35:51 +02:00
Vladimir Mihailenco
29fc6d8609 chore: release v0.2.2 (release.sh) 2022-03-21 09:34:52 +02:00
Vladimir Mihailenco
55950a9534 chore: go mod tidy 2022-03-21 09:34:10 +02:00
Vladimir Mihailenco
fd24893ad7 Merge pull request #8 from uptrace/release/v0.2.1
chore: release v0.2.1 (release.sh)
2022-03-21 09:22:27 +02:00
Vladimir Mihailenco
1d7d96e1bb chore: release v0.2.1 (release.sh) 2022-03-21 09:19:57 +02:00
Vladimir Mihailenco
56f9249437 chore: go mod tidy 2022-03-21 09:19:15 +02:00
Vladimir Mihailenco
04e796f77e Merge pull request #7 from uptrace/release/v0.2.0
chore: release v0.2.0 (release.sh)
2022-03-21 09:05:19 +02:00
93 changed files with 7765 additions and 2850 deletions

View File

@@ -1,10 +0,0 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly

View File

@@ -13,7 +13,7 @@ jobs:
services:
clickhouse:
image: clickhouse/clickhouse-server:21.12
image: clickhouse/clickhouse-server:22.4
options: >-
--health-cmd "clickhouse-client -q 'select 1'" --health-interval 10s --health-timeout 5s
--health-retries 5
@@ -22,10 +22,9 @@ jobs:
steps:
- name: Set up ${{ matrix.go-version }}
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: 1.18.0-beta1
stable: false
go-version: 1.20.x
- name: Checkout code
uses: actions/checkout@v3

View File

@@ -8,4 +8,4 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: wagoid/commitlint-github-action@v4
- uses: wagoid/commitlint-github-action@v5

View File

@@ -16,4 +16,4 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3.1.0
uses: golangci/golangci-lint-action@v3.2.0

View File

@@ -1,3 +1,124 @@
## 0.3.1 (2023-02-16)
### Bug Fixes
* change rollback to always record migrations ([43a65a0](https://github.com/uptrace/go-clickhouse/commit/43a65a02e4ab92bceb846674092bce1a1097bafe))
* continue working with non UTC timezone ([033c413](https://github.com/uptrace/go-clickhouse/commit/033c41395a3e7cbbaa25fddc127a1e626a0032b5))
* **migrate:** upping was applying zero migrations ([e80d6d2](https://github.com/uptrace/go-clickhouse/commit/e80d6d288b98bcb415e63e31314c0fd1077af668))
* move FINAL modifier to the right place ([#52](https://github.com/uptrace/go-clickhouse/issues/52)) ([0f4c068](https://github.com/uptrace/go-clickhouse/commit/0f4c06861f4d2bf5687063a3b52cbf0c3d3b5c4d))
### Features
* add bfloat16 support ([510b9ca](https://github.com/uptrace/go-clickhouse/commit/510b9caca8b64e62f7a7b12c283be2fcb2761cc5))
* add proper Rows implementation and some optimizations ([658ad14](https://github.com/uptrace/go-clickhouse/commit/658ad14fc0f97a2e51e3a113ea7ae0fd77eb2795))
* add queries to create/drop views ([b73c8fa](https://github.com/uptrace/go-clickhouse/commit/b73c8fabf8b3292d987803fadd18744872862f53))
* add Raw ([1e8ca47](https://github.com/uptrace/go-clickhouse/commit/1e8ca4767fa9a025f87595d95837d87feada4671))
* add support for DateTime64 ([c1e00ef](https://github.com/uptrace/go-clickhouse/commit/c1e00ef235a2ebfeebd2bdec52dde9c56ae27544))
* add WithAutoCreateDatabase option ([8bf4958](https://github.com/uptrace/go-clickhouse/commit/8bf4958230da026b140f47650c6b78b733160e60))
* allow disabling compression for benchmarks ([ef26067](https://github.com/uptrace/go-clickhouse/commit/ef260678ec2acc3fb2c540f8941c4b288d915cdf))
* **chmigrate:** add WithReplicated option ([988091e](https://github.com/uptrace/go-clickhouse/commit/988091e532f120f08738f28fb8c620e086d97c41))
* close idle connections after 30 minutes ([99f247b](https://github.com/uptrace/go-clickhouse/commit/99f247ba5e188995fe8ddb39a3c54764efb98245))
* enable opentelemetry support in protocol ([76504b2](https://github.com/uptrace/go-clickhouse/commit/76504b27784c509a39885a00750fbfc1eee0d8cc))
* initial commit ([092a2db](https://github.com/uptrace/go-clickhouse/commit/092a2dbf28ca070bd6d6cc3426ecbc1d9bc02c6e))
* **migrate:** added option to only mark migration up/down as applied on success ([f559e06](https://github.com/uptrace/go-clickhouse/commit/f559e06009e8fc263fa79cf99f692522a71f61ff))
# [0.3.0](https://github.com/uptrace/go-clickhouse/compare/v0.2.9...v0.3.0) (2023-01-21)
### Bug Fixes
* move FINAL modifier to the right place ([#52](https://github.com/uptrace/go-clickhouse/issues/52)) ([31f2e73](https://github.com/uptrace/go-clickhouse/commit/31f2e731adfb00031ebd82bbb0f2dcfc9e9c5b69))
### Features
* add queries to create/drop views ([cbdda72](https://github.com/uptrace/go-clickhouse/commit/cbdda720552afe9b72ba5fc716e2d6b5a73f56e6))
## [0.2.9](https://github.com/uptrace/go-clickhouse/compare/v0.2.8...v0.2.9) (2022-08-30)
### Bug Fixes
* **migrate:** upping was applying zero migrations ([f1d380c](https://github.com/uptrace/go-clickhouse/commit/f1d380c16590cc2055274c2dc9418792682a8378))
### Features
* add bfloat16 support ([75cc666](https://github.com/uptrace/go-clickhouse/commit/75cc6664576884120b629f38a473135cbe5214bd))
* add Raw ([07c1f88](https://github.com/uptrace/go-clickhouse/commit/07c1f88173bb056e476b56d8a35dc3e5cf00c596))
* add WithAutoCreateDatabase option ([74e949e](https://github.com/uptrace/go-clickhouse/commit/74e949e01d00e10718d375b43c6f72269165a19d))
* **chmigrate:** add WithReplicated option ([76433f0](https://github.com/uptrace/go-clickhouse/commit/76433f0158277aaa93fec681bbfca7af623baf8a))
* **migrate:** added option to only mark migration up/down as applied on success ([0b4f7bf](https://github.com/uptrace/go-clickhouse/commit/0b4f7bf56588c1060375f094406fe530b7086dcf))
## [0.2.8](https://github.com/uptrace/go-clickhouse/compare/v0.2.7...v0.2.8) (2022-05-29)
### Features
* enable opentelemetry support in protocol ([fb79ac4](https://github.com/uptrace/go-clickhouse/commit/fb79ac4b753bbf6ea794acb1d86fd8d116cf539c))
## [0.2.7](https://github.com/uptrace/go-clickhouse/compare/v0.2.6...v0.2.7) (2022-05-02)
### Features
* allow disabling compression for benchmarks ([a0d867b](https://github.com/uptrace/go-clickhouse/commit/a0d867b5f4478ac4879e73e1c8bb7cf0a8565142))
## [0.2.6](https://github.com/uptrace/go-clickhouse/compare/v0.2.5...v0.2.6) (2022-04-30)
### Features
* add proper Rows implementation and some optimizations ([aca5cfe](https://github.com/uptrace/go-clickhouse/commit/aca5cfeb91514cf6dccb4ebc261755940b290449))
* add support for DateTime64 ([1281505](https://github.com/uptrace/go-clickhouse/commit/1281505a77f39e0ff3203eddd969fded776e72f0))
#### 0.2.6 (2022-03-29)
# [](https://github.com/uptrace/go-clickhouse/compare/v0.2.4...v) (2022-03-29)
### Bug Fixes
* change rollback to always record migrations ([d6e6e55](https://github.com/uptrace/go-clickhouse/commit/d6e6e55142d6cb369d838357a0700dd1becd50a8))
* continue working with non UTC timezone ([d003d44](https://github.com/uptrace/go-clickhouse/commit/d003d44e55049b612610d48607809fe3fff5f151))
# [](https://github.com/uptrace/go-clickhouse/compare/v0.2.3...v) (2022-03-23)
# [](https://github.com/uptrace/go-clickhouse/compare/v0.2.2...v) (2022-03-23)
### Features
* close idle connections after 30 minutes ([844981b](https://github.com/uptrace/go-clickhouse/commit/844981bf1a831ab476e8854d413d2ea31c087d42))
# [](https://github.com/uptrace/go-clickhouse/compare/v0.2.1...v) (2022-03-21)
# [](https://github.com/uptrace/go-clickhouse/compare/v0.2.0...v) (2022-03-21)
# (2022-03-21)

View File

@@ -9,14 +9,25 @@ test:
go vet); \
done
.PHONY: go_mod_tidy
go_mod_tidy:
set -e; for dir in $(ALL_GO_MOD_DIRS); do \
echo "go mod tidy in $${dir}"; \
(cd "$${dir}" && go mod tidy -go=1.18); \
done
.PHONY: deps
deps:
set -e; for dir in $(ALL_GO_MOD_DIRS); do \
echo "go get -u ./... && go mod tidy in $${dir}"; \
(cd "$${dir}" && \
go get -u ./... && \
go mod tidy); \
go mod tidy -go=1.18); \
done
fmt:
gofmt -w -s ./
goimports -w -local github.com/uptrace/go-clickhouse ./
codegen:
go run ./ch/internal/codegen/ -dir=ch/chschema

View File

@@ -5,26 +5,24 @@
[![Documentation](https://img.shields.io/badge/ch-documentation-informational)](https://clickhouse.uptrace.dev/)
[![Chat](https://discordapp.com/api/guilds/752070105847955518/widget.png)](https://discord.gg/rWtp5Aj)
This client uses native protocol to communicate with ClickHouse server and requires Go 1.18+ in
order to use generics. This is not a database/sql driver, but the API is compatible.
This ClickHouse client uses native protocol to communicate with ClickHouse server and requires Go
1.18+ in order to use generics. This is not a database/sql driver, but the API is compatible.
Main features are:
- ClickHouse native protocol support and efficient column-oriented design.
- API compatible with database/sql.
- [Bun](https://github.com/uptrace/bun/)-like query builder.
- [Selecting](https://clickhouse.uptrace.dev/guide/query-select.html) into scalars, structs, maps,
slices of maps/structs/scalars.
- [Selecting](https://clickhouse.uptrace.dev/guide/clickhouse-select.html) into scalars, structs,
maps, slices of maps/structs/scalars.
- `Date`, `DateTime`, and `DateTime64`.
- `Array(T)` including nested arrays.
- Enums and `LowCardinality(String)`.
- `Nullable(T)` except `Nullable(Array(T))`.
- [Migrations](https://clickhouse.uptrace.dev/guide/migrations.html).
- [OpenTelemetry](https://clickhouse.uptrace.dev/guide/monitoring.html) support.
- In production at [Uptrace](https://uptrace.dev/)
Unsupported:
- Server timezones other than UTC.
- [Migrations](https://clickhouse.uptrace.dev/guide/clickhouse-migrations.html).
- [OpenTelemetry](https://clickhouse.uptrace.dev/guide/clickhouse-monitoring-performance.html)
support.
- In production at [Uptrace](https://github.com/uptrace/uptrace)
Resources:
@@ -39,17 +37,23 @@ Resources:
**Read** (best of 3 runs):
| Library | Timing |
| ---------------------------------------------------------------------------------------------------------------- | ------ |
| [This library](example/benchmark/read-native/main.go) | 655ms |
| [ClickHouse/clickhouse-go](https://github.com/ClickHouse/clickhouse-go/blob/v2/benchmark/v2/read-native/main.go) | 849ms |
| Library | Timing |
| ------------------------------------------------------------------------------------------------------------------ | ------ |
| [This library](example/benchmark/read-native/main.go) | 655ms |
| [ClickHouse/clickhouse-go](https://github.com/ClickHouse/clickhouse-go/blob/main/benchmark/v2/read-native/main.go) | 849ms |
**Write** (best of 3 runs):
| Library | Timing |
| -------------------------------------------------------------------------------------------------------------------------- | ------ |
| [This library](example/benchmark/write-native-columnar/main.go) | 475ms |
| [ClickHouse/clickhouse-go](https://github.com/ClickHouse/clickhouse-go/blob/v2/benchmark/v2/write-native-columnar/main.go) | 881ms |
| Library | Timing |
| ---------------------------------------------------------------------------------------------------------------------------- | ------ |
| [This library](example/benchmark/write-native-columnar/main.go) | 475ms |
| [ClickHouse/clickhouse-go](https://github.com/ClickHouse/clickhouse-go/blob/main/benchmark/v2/write-native-columnar/main.go) | 881ms |
## Installation
```shell
go get github.com/uptrace/go-clickhouse@latest
```
## Example
@@ -107,3 +111,9 @@ func main() {
fmt.Println(dest)
}
```
## See also
- [Golang ORM](https://github.com/uptrace/bun) for PostgreSQL, MySQL, MSSQL, and SQLite
- [Golang PostgreSQL](https://bun.uptrace.dev/postgres/)
- [Golang HTTP router](https://github.com/uptrace/bunrouter)

29
ch/bench_test.go Normal file
View File

@@ -0,0 +1,29 @@
package ch_test
import (
"context"
"testing"
"github.com/stretchr/testify/require"
)
func BenchmarkNumbers(b *testing.B) {
ctx := context.Background()
db := chDB()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
rows, err := db.QueryContext(ctx, "SELECT number FROM system.numbers_mt LIMIT 1000000")
if err != nil {
b.Fatal(err)
}
var count int
for rows.Next() {
count++
}
require.Equal(b, 1000000, count)
}
}

23
ch/bfloat16/bfloat16.go Normal file
View File

@@ -0,0 +1,23 @@
package bfloat16
import (
"math"
)
type T uint16
func From(f float64) T {
return From32(float32(f))
}
func From32(f float32) T {
return T(math.Float32bits(f) >> 16)
}
func (f T) Float32() float32 {
return math.Float32frombits(uint32(f) << 16)
}
func (f T) Float64() float64 {
return float64(f.Float32())
}

View File

@@ -12,6 +12,7 @@ import (
type (
Safe = chschema.Safe
Name = chschema.Name
Ident = chschema.Ident
CHModel = chschema.CHModel
AfterScanRowHook = chschema.AfterScanRowHook
@@ -70,34 +71,89 @@ func isBadConn(err error, allowTimeout bool) bool {
//------------------------------------------------------------------------------
type ListValues struct {
slice any
}
var _ chschema.QueryAppender = ListValues{}
func List(slice any) ListValues {
return ListValues{
slice: slice,
}
}
func (in ListValues) AppendQuery(fmter chschema.Formatter, b []byte) (_ []byte, err error) {
v := reflect.ValueOf(in.slice)
if v.Kind() != reflect.Slice {
return nil, fmt.Errorf("ch: In(non-slice %T)", in.slice)
}
b = appendList(fmter, b, v)
return b, nil
}
//------------------------------------------------------------------------------
type InValues struct {
slice reflect.Value
err error
slice any
}
var _ chschema.QueryAppender = InValues{}
func In(slice any) InValues {
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Slice {
return InValues{
err: fmt.Errorf("ch: In(non-slice %T)", slice),
}
}
return InValues{
slice: v,
slice: slice,
}
}
func (in InValues) AppendQuery(fmter chschema.Formatter, b []byte) (_ []byte, err error) {
if in.err != nil {
return nil, in.err
v := reflect.ValueOf(in.slice)
if v.Kind() != reflect.Slice {
return nil, fmt.Errorf("ch: In(non-slice %T)", in.slice)
}
return appendIn(fmter, b, in.slice), nil
b = append(b, '(')
b = appendList(fmter, b, v)
b = append(b, ')')
return b, nil
}
func appendIn(fmter chschema.Formatter, b []byte, slice reflect.Value) []byte {
//------------------------------------------------------------------------------
type ArrayValues struct {
slice any
}
var _ chschema.QueryAppender = ArrayValues{}
func Array(slice any) ArrayValues {
return ArrayValues{
slice: slice,
}
}
func (in ArrayValues) AppendQuery(fmter chschema.Formatter, b []byte) (_ []byte, err error) {
v := reflect.ValueOf(in.slice)
if v.Kind() != reflect.Slice {
return nil, fmt.Errorf("ch: Array(non-slice %T)", in.slice)
}
b = append(b, '[')
b = appendList(fmter, b, v)
b = append(b, ']')
return b, nil
}
//------------------------------------------------------------------------------
func appendList(fmter chschema.Formatter, b []byte, slice reflect.Value) []byte {
sliceLen := slice.Len()
if sliceLen == 0 {
return append(b, "NULL"...)
}
for i := 0; i < sliceLen; i++ {
if i > 0 {
b = append(b, ", "...)
@@ -108,13 +164,7 @@ func appendIn(fmter chschema.Formatter, b []byte, slice reflect.Value) []byte {
elem = elem.Elem()
}
if elem.Kind() == reflect.Slice {
b = append(b, '(')
b = appendIn(fmter, b, elem)
b = append(b, ')')
} else {
b = chschema.AppendValue(fmter, b, elem)
}
b = chschema.AppendValue(fmter, b, elem)
}
return b
}

View File

@@ -19,7 +19,6 @@ type Conn struct {
ServerInfo chproto.ServerInfo
pooled bool
Inited bool
createdAt time.Time
usedAt int64 // atomic
@@ -33,6 +32,7 @@ func NewConn(netConn net.Conn) *Conn {
wr: chproto.NewWriter(netConn),
createdAt: time.Now(),
}
cn.SetUsedAt(time.Now())
return cn
}
@@ -45,10 +45,19 @@ func (cn *Conn) SetUsedAt(tm time.Time) {
atomic.StoreInt64(&cn.usedAt, tm.Unix())
}
func (cn *Conn) LocalAddr() net.Addr {
return cn.netConn.LocalAddr()
}
func (cn *Conn) RemoteAddr() net.Addr {
return cn.netConn.RemoteAddr()
}
func (cn *Conn) Reader(ctx context.Context, timeout time.Duration) *chproto.Reader {
_ = cn.netConn.SetReadDeadline(cn.deadline(ctx, timeout))
return cn.rd
}
func (cn *Conn) WithReader(
ctx context.Context,
timeout time.Duration,

View File

@@ -76,11 +76,11 @@ type Config struct {
Dialer func(context.Context) (net.Conn, error)
OnClose func(*Conn) error
PoolSize int
PoolTimeout time.Duration
MinIdleConns int
MaxIdleConns int
MaxConnAge time.Duration
PoolSize int
PoolTimeout time.Duration
MaxIdleConns int
ConnMaxIdleTime time.Duration
ConnMaxLifetime time.Duration
}
type ConnPool struct {
@@ -100,9 +100,6 @@ type ConnPool struct {
connsMu sync.Mutex
conns []*Conn
idleConns []*Conn
poolSize int
idleConnsLen int
}
var _ Pooler = (*ConnPool)(nil)
@@ -116,70 +113,22 @@ func New(cfg *Config) *ConnPool {
idleConns: make([]*Conn, 0, cfg.PoolSize),
}
p.connsMu.Lock()
p.checkMinIdleConns()
p.connsMu.Unlock()
return p
}
func (p *ConnPool) checkMinIdleConns() {
if p.cfg.MinIdleConns == 0 {
return
}
for p.poolSize < p.cfg.PoolSize && p.idleConnsLen < p.cfg.MinIdleConns {
p.poolSize++
p.idleConnsLen++
go func() {
err := p.addIdleConn()
if err != nil {
p.connsMu.Lock()
p.poolSize--
p.idleConnsLen--
p.connsMu.Unlock()
}
}()
}
}
func (p *ConnPool) addIdleConn() error {
cn, err := p.dialConn(context.TODO(), true)
if err != nil {
return err
}
p.connsMu.Lock()
p.conns = append(p.conns, cn)
p.idleConns = append(p.idleConns, cn)
p.connsMu.Unlock()
return nil
}
func (p *ConnPool) NewConn(c context.Context) (*Conn, error) {
return p.newConn(c, false)
}
func (p *ConnPool) newConn(c context.Context, pooled bool) (*Conn, error) {
cn, err := p.dialConn(c, pooled)
func (p *ConnPool) NewConn(ctx context.Context) (*Conn, error) {
cn, err := p.dialConn(ctx)
if err != nil {
return nil, err
}
p.connsMu.Lock()
p.conns = append(p.conns, cn)
if pooled {
// If pool is full remove the cn on next Put.
if p.poolSize >= p.cfg.PoolSize {
cn.pooled = false
} else {
p.poolSize++
}
}
p.connsMu.Unlock()
return cn, nil
}
func (p *ConnPool) dialConn(c context.Context, pooled bool) (*Conn, error) {
func (p *ConnPool) dialConn(ctx context.Context) (*Conn, error) {
if p.closed() {
return nil, ErrClosed
}
@@ -188,7 +137,7 @@ func (p *ConnPool) dialConn(c context.Context, pooled bool) (*Conn, error) {
return nil, p.getLastDialError()
}
netConn, err := p.cfg.Dialer(c)
netConn, err := p.cfg.Dialer(ctx)
if err != nil {
p.setLastDialError(err)
if atomic.AddUint32(&p.dialErrorsNum, 1) == uint32(p.cfg.PoolSize) {
@@ -198,7 +147,6 @@ func (p *ConnPool) dialConn(c context.Context, pooled bool) (*Conn, error) {
}
cn := NewConn(netConn)
cn.pooled = pooled
return cn, nil
}
@@ -254,7 +202,7 @@ func (p *ConnPool) Get(ctx context.Context) (*Conn, error) {
break
}
if p.cfg.MaxConnAge > 0 && time.Since(cn.createdAt) >= p.cfg.MaxConnAge {
if !p.isHealthyConn(cn) {
_ = p.CloseConn(cn)
continue
}
@@ -265,7 +213,7 @@ func (p *ConnPool) Get(ctx context.Context) (*Conn, error) {
atomic.AddUint32(&p.stats.Misses, 1)
newcn, err := p.newConn(ctx, true)
newcn, err := p.NewConn(ctx)
if err != nil {
p.freeTurn()
return nil, err
@@ -326,8 +274,6 @@ func (p *ConnPool) popIdle() *Conn {
idx := len(p.idleConns) - 1
cn := p.idleConns[idx]
p.idleConns = p.idleConns[:idx]
p.idleConnsLen--
p.checkMinIdleConns()
return cn
}
@@ -338,29 +284,24 @@ func (p *ConnPool) Put(cn *Conn) {
return
}
if !cn.pooled {
p.Remove(cn, nil)
return
}
var atMaxCap bool
var shouldCloseConn bool
p.connsMu.Lock()
if len(p.idleConns) < p.cfg.MaxIdleConns {
if p.cfg.MaxIdleConns == 0 || len(p.idleConns) < p.cfg.MaxIdleConns {
p.idleConns = append(p.idleConns, cn)
p.idleConnsLen++
} else {
atMaxCap = true
p.removeConn(cn)
shouldCloseConn = true
}
p.connsMu.Unlock()
if atMaxCap {
p.Remove(cn, nil)
}
p.freeTurn()
if shouldCloseConn {
_ = p.closeConn(cn)
}
}
func (p *ConnPool) Remove(cn *Conn, reason error) {
@@ -376,19 +317,15 @@ func (p *ConnPool) CloseConn(cn *Conn) error {
func (p *ConnPool) removeConnWithLock(cn *Conn) {
p.connsMu.Lock()
defer p.connsMu.Unlock()
p.removeConn(cn)
p.connsMu.Unlock()
}
func (p *ConnPool) removeConn(cn *Conn) {
for i, c := range p.conns {
if c == cn {
p.conns = append(p.conns[:i], p.conns[i+1:]...)
if cn.pooled {
p.poolSize--
p.checkMinIdleConns()
}
return
break
}
}
}
@@ -411,20 +348,19 @@ func (p *ConnPool) Len() int {
// IdleLen returns number of idle connections.
func (p *ConnPool) IdleLen() int {
p.connsMu.Lock()
n := p.idleConnsLen
n := len(p.idleConns)
p.connsMu.Unlock()
return n
}
func (p *ConnPool) Stats() *Stats {
idleLen := p.IdleLen()
return &Stats{
Hits: atomic.LoadUint32(&p.stats.Hits),
Misses: atomic.LoadUint32(&p.stats.Misses),
Timeouts: atomic.LoadUint32(&p.stats.Timeouts),
TotalConns: uint32(p.Len()),
IdleConns: uint32(idleLen),
IdleConns: uint32(p.IdleLen()),
StaleConns: atomic.LoadUint32(&p.stats.StaleConns),
}
}
@@ -446,10 +382,23 @@ func (p *ConnPool) Close() error {
}
}
p.conns = nil
p.poolSize = 0
p.idleConns = nil
p.idleConnsLen = 0
p.connsMu.Unlock()
return firstErr
}
func (p *ConnPool) isHealthyConn(cn *Conn) bool {
now := time.Now()
if p.cfg.ConnMaxLifetime > 0 && now.Sub(cn.createdAt) >= p.cfg.ConnMaxLifetime {
return false
}
if p.cfg.ConnMaxIdleTime > 0 && now.Sub(cn.UsedAt()) >= p.cfg.ConnMaxIdleTime {
atomic.AddUint32(&p.stats.IdleConns, 1)
return false
}
cn.SetUsedAt(now)
return true
}

View File

@@ -17,8 +17,9 @@ type lz4Reader struct {
header []byte
data []byte
pos int
zdata []byte
data []byte
pos int
}
func newLZ4Reader(r *bufio.Reader) *lz4Reader {
@@ -29,16 +30,15 @@ func newLZ4Reader(r *bufio.Reader) *lz4Reader {
}
}
func (r *lz4Reader) Init() {}
func (r *lz4Reader) Release() error {
var err error
if r.Buffered() > 0 {
err = errUnreadData
}
r.data = nil
r.data = r.data[:0]
r.pos = 0
r.zdata = r.zdata[:0]
return err
}
@@ -102,13 +102,13 @@ func (r *lz4Reader) readData() error {
compressedSize := int(binary.LittleEndian.Uint32(r.header[17:])) - compressionHeaderSize
uncompressedSize := int(binary.LittleEndian.Uint32(r.header[21:]))
zdata := make([]byte, compressedSize)
r.zdata = grow(r.zdata, compressedSize)
r.data = grow(r.data, uncompressedSize)
if _, err := io.ReadFull(r.rd, zdata); err != nil {
if _, err := io.ReadFull(r.rd, r.zdata); err != nil {
return err
}
if _, err := lz4.UncompressBlock(zdata, r.data); err != nil {
if _, err := lz4.UncompressBlock(r.zdata, r.data); err != nil {
return err
}

View File

@@ -3,7 +3,6 @@ package chproto
import (
"bufio"
"encoding/binary"
"sync"
"github.com/pierrec/lz4/v4"
@@ -25,50 +24,26 @@ const (
blockSize = 1 << 20 // 1 MB
)
type writeBuffer struct {
buf []byte
}
var writeBufferPool = sync.Pool{
New: func() any {
return &writeBuffer{
buf: make([]byte, blockSize),
}
},
}
func getWriterBuffer() *writeBuffer {
return writeBufferPool.Get().(*writeBuffer)
}
func putWriterBuffer(db *writeBuffer) {
writeBufferPool.Put(db)
}
//------------------------------------------------------------------------------
type lz4Writer struct {
wr *bufio.Writer
data *writeBuffer
pos int
data []byte
pos int
zdata []byte
}
func newLZ4Writer(w *bufio.Writer) *lz4Writer {
return &lz4Writer{
wr: w,
wr: w,
data: make([]byte, blockSize),
}
}
func (w *lz4Writer) Init() {
w.data = getWriterBuffer()
w.pos = 0
}
func (w *lz4Writer) Close() error {
err := w.flush()
putWriterBuffer(w.data)
w.data = nil
w.pos = 0
return err
}
@@ -77,7 +52,7 @@ func (w *lz4Writer) Flush() error {
}
func (w *lz4Writer) WriteByte(c byte) error {
w.data.buf[w.pos] = c
w.data[w.pos] = c
w.pos++
return w.checkFlush()
}
@@ -89,7 +64,7 @@ func (w *lz4Writer) WriteString(s string) (int, error) {
func (w *lz4Writer) Write(data []byte) (int, error) {
var written int
for len(data) > 0 {
n := copy(w.data.buf[w.pos:], data)
n := copy(w.data[w.pos:], data)
data = data[n:]
w.pos += n
if err := w.checkFlush(); err != nil {
@@ -101,7 +76,7 @@ func (w *lz4Writer) Write(data []byte) (int, error) {
}
func (w *lz4Writer) checkFlush() error {
if w.pos < len(w.data.buf) {
if w.pos < len(w.data) {
return nil
}
return w.flush()
@@ -113,23 +88,23 @@ func (w *lz4Writer) flush() error {
}
zlen := headerSize + lz4.CompressBlockBound(w.pos)
zdata := make([]byte, zlen)
w.zdata = grow(w.zdata, zlen)
compressedSize, err := compress(zdata[headerSize:], w.data.buf[:w.pos])
compressedSize, err := compress(w.zdata[headerSize:], w.data[:w.pos])
if err != nil {
return err
}
compressedSize += compressionHeaderSize
zdata[16] = lz4Compression
binary.LittleEndian.PutUint32(zdata[17:], uint32(compressedSize))
binary.LittleEndian.PutUint32(zdata[21:], uint32(w.pos))
w.zdata[16] = lz4Compression
binary.LittleEndian.PutUint32(w.zdata[17:], uint32(compressedSize))
binary.LittleEndian.PutUint32(w.zdata[21:], uint32(w.pos))
checkSum := cityhash102.CityHash128(zdata[16:], uint32(compressedSize))
binary.LittleEndian.PutUint64(zdata[0:], checkSum.Lower64())
binary.LittleEndian.PutUint64(zdata[8:], checkSum.Higher64())
checkSum := cityhash102.CityHash128(w.zdata[16:], uint32(compressedSize))
binary.LittleEndian.PutUint64(w.zdata[0:], checkSum.Lower64())
binary.LittleEndian.PutUint64(w.zdata[8:], checkSum.Higher64())
w.wr.Write(zdata[:checksumSize+compressedSize])
w.wr.Write(w.zdata[:checksumSize+compressedSize])
w.pos = 0
return nil

View File

@@ -11,23 +11,22 @@ const (
)
const (
CompressionDisabled = 0
CompressionEnabled = 1
)
const (
ServerHello = 0
ServerData = 1
ServerException = 2
ServerProgress = 3
ServerPong = 4
ServerEndOfStream = 5
ServerProfileInfo = 6
ServerTotals = 7
ServerExtremes = 8
ServerTablesStatus = 9
ServerLog = 10
ServerTableColumns = 11
ServerHello = 0
ServerData = 1
ServerException = 2
ServerProgress = 3
ServerPong = 4
ServerEndOfStream = 5
ServerProfileInfo = 6
ServerTotals = 7
ServerExtremes = 8
ServerTablesStatus = 9
ServerLog = 10
ServerTableColumns = 11
ServerPartUUIDs = 12
ServerReadTaskRequest = 13
ServerProfileEvents = 14
ServerTreeReadTaskRequest = 15
)
const (
@@ -35,3 +34,21 @@ const (
QueryInitial = 1
QuerySecondary = 2
)
// see https://github.com/ClickHouse/ClickHouse/blob/master/src/Core/Protocol.h
const (
DBMS_MIN_REVISION_WITH_CLIENT_INFO = 54032
DBMS_MIN_REVISION_WITH_SERVER_TIMEZONE = 54058
DBMS_MIN_REVISION_WITH_QUOTA_KEY_IN_CLIENT_INFO = 54060
DBMS_MIN_REVISION_WITH_SERVER_DISPLAY_NAME = 54372
DBMS_MIN_REVISION_WITH_VERSION_PATCH = 54401
DBMS_MIN_REVISION_WITH_CLIENT_WRITE_INFO = 54420
DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS = 54429
DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET = 54441
DBMS_MIN_REVISION_WITH_OPENTELEMETRY = 54442
DBMS_MIN_PROTOCOL_VERSION_WITH_DISTRIBUTED_DEPTH = 54448
DBMS_MIN_PROTOCOL_VERSION_WITH_INITIAL_QUERY_START_TIME = 54449
DBMS_MIN_PROTOCOL_VERSION_WITH_INCREMENTAL_PROFILE_EVENTS = 54451
DBMS_MIN_REVISION_WITH_PARALLEL_REPLICAS = 54453
DBMS_TCP_PROTOCOL_VERSION = DBMS_MIN_REVISION_WITH_PARALLEL_REPLICAS
)

View File

@@ -36,15 +36,18 @@ func NewReader(r io.Reader) *Reader {
}
}
func (r *Reader) WithCompression(fn func() error) error {
r.zr.Init()
r.rd = r.zr
func (r *Reader) WithCompression(enabled bool, fn func() error) error {
if enabled {
r.rd = r.zr
}
firstErr := fn()
r.rd = r.br
if err := r.zr.Release(); err != nil && firstErr == nil {
firstErr = err
if enabled {
r.rd = r.br
if err := r.zr.Release(); err != nil && firstErr == nil {
firstErr = err
}
}
return firstErr
@@ -70,7 +73,7 @@ func (r *Reader) Uvarint() (uint64, error) {
return binary.ReadUvarint(r.rd)
}
func (r *Reader) Uint8() (uint8, error) {
func (r *Reader) UInt8() (uint8, error) {
c, err := r.rd.ReadByte()
if err != nil {
return 0, err
@@ -78,7 +81,7 @@ func (r *Reader) Uint8() (uint8, error) {
return c, nil
}
func (r *Reader) Uint16() (uint16, error) {
func (r *Reader) UInt16() (uint16, error) {
b, err := r.readNTemp(2)
if err != nil {
return 0, err
@@ -86,7 +89,7 @@ func (r *Reader) Uint16() (uint16, error) {
return binary.LittleEndian.Uint16(b), nil
}
func (r *Reader) Uint32() (uint32, error) {
func (r *Reader) UInt32() (uint32, error) {
b, err := r.readNTemp(4)
if err != nil {
return 0, err
@@ -94,7 +97,7 @@ func (r *Reader) Uint32() (uint32, error) {
return binary.LittleEndian.Uint32(b), nil
}
func (r *Reader) Uint64() (uint64, error) {
func (r *Reader) UInt64() (uint64, error) {
b, err := r.readNTemp(8)
if err != nil {
return 0, err
@@ -103,27 +106,27 @@ func (r *Reader) Uint64() (uint64, error) {
}
func (r *Reader) Int8() (int8, error) {
num, err := r.Uint8()
num, err := r.UInt8()
return int8(num), err
}
func (r *Reader) Int16() (int16, error) {
num, err := r.Uint16()
num, err := r.UInt16()
return int16(num), err
}
func (r *Reader) Int32() (int32, error) {
num, err := r.Uint32()
num, err := r.UInt32()
return int32(num), err
}
func (r *Reader) Int64() (int64, error) {
num, err := r.Uint64()
num, err := r.UInt64()
return int64(num), err
}
func (r *Reader) Float32() (float32, error) {
num, err := r.Uint32()
num, err := r.UInt32()
if err != nil {
return 0, err
}
@@ -131,7 +134,7 @@ func (r *Reader) Float32() (float32, error) {
}
func (r *Reader) Float64() (float64, error) {
num, err := r.Uint64()
num, err := r.UInt64()
if err != nil {
return 0, err
}
@@ -180,7 +183,7 @@ func (r *Reader) readNTemp(n int) ([]byte, error) {
}
func (r *Reader) DateTime() (time.Time, error) {
sec, err := r.Uint32()
sec, err := r.UInt32()
if err != nil {
return time.Time{}, err
}
@@ -191,7 +194,7 @@ func (r *Reader) DateTime() (time.Time, error) {
}
func (r *Reader) Date() (time.Time, error) {
days, err := r.Uint16()
days, err := r.UInt16()
if err != nil {
return time.Time{}, err
}

View File

@@ -1,9 +1,5 @@
package chproto
import (
"fmt"
)
type ServerInfo struct {
Name string
MinorVersion uint64
@@ -25,19 +21,20 @@ func (srv *ServerInfo) ReadFrom(rd *Reader) (err error) {
return err
}
timezone, err := rd.String()
if err != nil {
return err
if srv.Revision >= DBMS_MIN_REVISION_WITH_SERVER_TIMEZONE {
if _, err := rd.String(); err != nil { // timezone
return err
}
}
if timezone != "UTC" {
return fmt.Errorf("ch: ClickHouse server uses timezone=%q, expected UTC", timezone)
if srv.Revision >= DBMS_MIN_REVISION_WITH_SERVER_DISPLAY_NAME {
if _, err := rd.String(); err != nil { // display name
return err
}
}
if _, err = rd.String(); err != nil { // display name
return err
}
if _, err = rd.Uvarint(); err != nil { // server version patch
return err
if srv.Revision >= DBMS_MIN_REVISION_WITH_VERSION_PATCH {
if _, err := rd.Uvarint(); err != nil { // server version patch
return err
}
}
return nil

View File

@@ -10,7 +10,10 @@ import (
"github.com/uptrace/go-clickhouse/ch/internal"
)
const uuidLen = 16
const (
uuidLen = 16
secsInDay = 24 * 3600
)
type writer interface {
io.Writer
@@ -39,20 +42,23 @@ func NewWriter(w io.Writer) *Writer {
}
}
func (w *Writer) WithCompression(fn func() error) {
func (w *Writer) WithCompression(enabled bool, fn func() error) {
if w.err != nil {
return
}
w.zw.Init()
w.wr = w.zw
if enabled {
w.wr = w.zw
}
w.err = fn()
if err := w.zw.Close(); err != nil && w.err == nil {
w.err = err
if enabled {
if err := w.zw.Close(); err != nil && w.err == nil {
w.err = err
}
w.wr = w.bw
}
w.wr = w.bw
}
func (w *Writer) Flush() (err error) {
@@ -73,7 +79,7 @@ func (w *Writer) Write(b []byte) {
w.err = err
}
func (w *Writer) writeByte(c byte) {
func (w *Writer) WriteByte(c byte) {
if w.err != nil {
return
}
@@ -85,7 +91,7 @@ func (w *Writer) Bool(flag bool) {
if flag {
num = 1
}
w.Uint8(num)
w.UInt8(num)
}
func (w *Writer) Uvarint(num uint64) {
@@ -93,47 +99,47 @@ func (w *Writer) Uvarint(num uint64) {
w.Write(w.buf[:n])
}
func (w *Writer) Uint8(num uint8) {
w.writeByte(num)
func (w *Writer) UInt8(num uint8) {
w.WriteByte(num)
}
func (w *Writer) Uint16(num uint16) {
func (w *Writer) UInt16(num uint16) {
binary.LittleEndian.PutUint16(w.buf, num)
w.Write(w.buf[:2])
}
func (w *Writer) Uint32(num uint32) {
func (w *Writer) UInt32(num uint32) {
binary.LittleEndian.PutUint32(w.buf, num)
w.Write(w.buf[:4])
}
func (w *Writer) Uint64(num uint64) {
func (w *Writer) UInt64(num uint64) {
binary.LittleEndian.PutUint64(w.buf, num)
w.Write(w.buf[:8])
}
func (w *Writer) Int8(num int8) {
w.Uint8(uint8(num))
w.UInt8(uint8(num))
}
func (w *Writer) Int16(num int16) {
w.Uint16(uint16(num))
w.UInt16(uint16(num))
}
func (w *Writer) Int32(num int32) {
w.Uint32(uint32(num))
w.UInt32(uint32(num))
}
func (w *Writer) Int64(num int64) {
w.Uint64(uint64(num))
w.UInt64(uint64(num))
}
func (w *Writer) Float32(num float32) {
w.Uint32(math.Float32bits(num))
w.UInt32(math.Float32bits(num))
}
func (w *Writer) Float64(num float64) {
w.Uint64(math.Float64bits(num))
w.UInt64(math.Float64bits(num))
}
func (w *Writer) String(s string) {
@@ -172,13 +178,11 @@ func packUUID(b []byte) []byte {
}
func (w *Writer) DateTime(tm time.Time) {
w.Uint32(uint32(unixTime(tm)))
w.UInt32(uint32(unixTime(tm)))
}
const secsInDay = 24 * 3600
func (w *Writer) Date(tm time.Time) {
w.Uint16(uint16(unixTime(tm) / secsInDay))
w.UInt16(uint16(unixTime(tm) / secsInDay))
}
func unixTime(tm time.Time) int64 {

View File

@@ -106,7 +106,10 @@ func AppendString(b []byte, s string) []byte {
}
func AppendTime(b []byte, tm time.Time) []byte {
return tm.UTC().AppendFormat(b, "'2006-01-02 15:04:05'")
b = append(b, "toDateTime('"...)
b = tm.UTC().AppendFormat(b, "2006-01-02 15:04:05")
b = append(b, "', 'UTC')"...)
return b
}
func AppendBytes(b []byte, bytes []byte) []byte {
@@ -114,15 +117,12 @@ func AppendBytes(b []byte, bytes []byte) []byte {
return AppendNull(b)
}
b = append(b, '\'')
tmp := make([]byte, hex.EncodedLen(len(bytes)))
hex.Encode(tmp, bytes)
b = append(b, "\\x"...)
b = append(b, "unhex('"...)
b = append(b, tmp...)
b = append(b, '\'')
b = append(b, "')"...)
return b
}

View File

@@ -37,13 +37,13 @@ func (b *Block) Column(colName, colType string) *Column {
var col *Column
if b.Table != nil {
col = b.Table.NewColumn(colName, colType, b.NumRow)
col = b.Table.NewColumn(colName, colType)
}
if col == nil {
col = &Column{
Name: colName,
Type: colType,
Columnar: NewColumnFromCHType(colType, b.NumRow),
Columnar: NewColumn(colType, nil),
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,354 +0,0 @@
package chschema
import (
"fmt"
"reflect"
"github.com/uptrace/go-clickhouse/ch/chproto"
)
type ArrayColumnar interface {
WriteOffset(wr *chproto.Writer, offset int) int
WriteData(wr *chproto.Writer) error
}
type ArrayLCStringColumn struct {
*LCStringColumn
}
func (c ArrayLCStringColumn) Type() reflect.Type {
return stringSliceType
}
func (c *ArrayLCStringColumn) WriteTo(wr *chproto.Writer) error {
c.writeData(wr)
return nil
}
func (c *ArrayLCStringColumn) ReadFrom(rd *chproto.Reader, numRow int) error {
if numRow == 0 {
return nil
}
return c.readData(rd, numRow)
}
//------------------------------------------------------------------------------
type ArrayColumn struct {
Column reflect.Value
typ reflect.Type
elem Columnar
arrayElem ArrayColumnar
}
var _ Columnar = (*ArrayColumn)(nil)
func NewArrayColumn(typ reflect.Type, chType string, numRow int) Columnar {
elemType := chArrayElemType(chType)
if elemType == "" {
panic(fmt.Errorf("invalid array type: %q (Go type is %s)",
chType, typ.String()))
}
elem := NewColumn(typ.Elem(), elemType, 0)
var arrayElem ArrayColumnar
if _, ok := elem.(*LCStringColumn); ok {
panic("not reached")
}
arrayElem, _ = elem.(ArrayColumnar)
c := &ArrayColumn{
typ: reflect.SliceOf(typ),
elem: elem,
arrayElem: arrayElem,
}
c.Column = reflect.MakeSlice(c.typ, 0, numRow)
return c
}
func (c ArrayColumn) Type() reflect.Type {
return c.typ.Elem()
}
func (c *ArrayColumn) Reset(numRow int) {
if c.Column.Cap() >= numRow {
c.Column = c.Column.Slice(0, 0)
} else {
c.Column = reflect.MakeSlice(c.typ, 0, numRow)
}
}
func (c *ArrayColumn) Set(v any) {
c.Column = reflect.ValueOf(v)
}
func (c *ArrayColumn) Value() any {
return c.Column.Interface()
}
func (c *ArrayColumn) Nullable(nulls Uint8Column) any {
panic("not implemented")
}
func (c *ArrayColumn) Len() int {
return c.Column.Len()
}
func (c *ArrayColumn) Index(idx int) any {
return c.Column.Index(idx).Interface()
}
func (c ArrayColumn) Slice(s, e int) any {
return c.Column.Slice(s, e).Interface()
}
func (c *ArrayColumn) ConvertAssign(idx int, v reflect.Value) error {
v.Set(c.Column.Index(idx))
return nil
}
func (c *ArrayColumn) AppendValue(v reflect.Value) {
c.Column = reflect.Append(c.Column, v)
}
func (c *ArrayColumn) ReadFrom(rd *chproto.Reader, numRow int) error {
if c.Column.Cap() >= numRow {
c.Column = c.Column.Slice(0, numRow)
} else {
c.Column = reflect.MakeSlice(c.typ, numRow, numRow)
}
if numRow == 0 {
return nil
}
offsets := make([]int, numRow)
for i := 0; i < len(offsets); i++ {
offset, err := rd.Uint64()
if err != nil {
return err
}
offsets[i] = int(offset)
}
if err := c.elem.ReadFrom(rd, offsets[len(offsets)-1]); err != nil {
return err
}
var prev int
for i, offset := range offsets {
c.Column.Index(i).Set(reflect.ValueOf(c.elem.Slice(prev, offset)))
prev = offset
}
return nil
}
func (c *ArrayColumn) WriteTo(wr *chproto.Writer) error {
_ = c.WriteOffset(wr, 0)
colLen := c.Column.Len()
for i := 0; i < colLen; i++ {
// TODO: add SetValue or SetPointer
c.elem.Set(c.Column.Index(i).Interface())
var err error
if c.arrayElem != nil {
err = c.arrayElem.WriteData(wr)
} else {
err = c.elem.WriteTo(wr)
}
if err != nil {
return err
}
}
return nil
}
func (c *ArrayColumn) WriteOffset(wr *chproto.Writer, offset int) int {
colLen := c.Column.Len()
for i := 0; i < colLen; i++ {
el := c.Column.Index(i)
offset += el.Len()
wr.Uint64(uint64(offset))
}
if c.arrayElem == nil {
return offset
}
offset = 0
for i := 0; i < colLen; i++ {
el := c.Column.Index(i)
c.elem.Set(el.Interface()) // Use SetValue or SetPointer
offset = c.arrayElem.WriteOffset(wr, offset)
}
return offset
}
//------------------------------------------------------------------------------
type StringArrayColumn struct {
Column [][]string
elem Columnar
stringElem *StringColumn
lcElem *LCStringColumn
}
var _ Columnar = (*StringArrayColumn)(nil)
func NewStringArrayColumn(typ reflect.Type, chType string, numRow int) Columnar {
if _, funcType := aggFuncNameAndType(chType); funcType != "" {
chType = funcType
}
elemType := chArrayElemType(chType)
if elemType == "" {
panic(fmt.Errorf("invalid array type: %q (Go type is %s)",
chType, typ.String()))
}
columnar := NewColumn(typ.Elem(), elemType, 0)
var stringElem *StringColumn
var lcElem *LCStringColumn
switch v := columnar.(type) {
case *StringColumn:
stringElem = v
case *LCStringColumn:
stringElem = &v.StringColumn
lcElem = v
columnar = &ArrayLCStringColumn{v}
case *EnumColumn:
stringElem = &v.StringColumn
default:
panic(fmt.Errorf("unsupported column: %T", v))
}
return &StringArrayColumn{
Column: make([][]string, 0, numRow),
elem: columnar,
stringElem: stringElem,
lcElem: lcElem,
}
}
func (c *StringArrayColumn) Reset(numRow int) {
if cap(c.Column) >= numRow {
c.Column = c.Column[:0]
} else {
c.Column = make([][]string, 0, numRow)
}
}
func (c *StringArrayColumn) Type() reflect.Type {
return stringSliceType
}
func (c *StringArrayColumn) Set(v any) {
c.Column = v.([][]string)
}
func (c *StringArrayColumn) Value() any {
return c.Column
}
func (c *StringArrayColumn) Nullable(nulls Uint8Column) any {
panic("not implemented")
}
func (c *StringArrayColumn) Len() int {
return len(c.Column)
}
func (c *StringArrayColumn) Index(idx int) any {
return c.Column[idx]
}
func (c StringArrayColumn) Slice(s, e int) any {
return c.Column[s:e]
}
func (c *StringArrayColumn) ConvertAssign(idx int, v reflect.Value) error {
v.Set(reflect.ValueOf(c.Column[idx]))
return nil
}
func (c *StringArrayColumn) AppendValue(v reflect.Value) {
c.Column = append(c.Column, v.Interface().([]string))
}
func (c *StringArrayColumn) ReadFrom(rd *chproto.Reader, numRow int) error {
if numRow == 0 {
return nil
}
if cap(c.Column) >= numRow {
c.Column = c.Column[:numRow]
} else {
c.Column = make([][]string, numRow)
}
if c.lcElem != nil {
if err := c.lcElem.readPrefix(rd, numRow); err != nil {
return err
}
}
offsets := make([]int, numRow)
for i := 0; i < len(offsets); i++ {
offset, err := rd.Uint64()
if err != nil {
return err
}
offsets[i] = int(offset)
}
if err := c.elem.ReadFrom(rd, offsets[len(offsets)-1]); err != nil {
return err
}
var prev int
for i, offset := range offsets {
c.Column[i] = c.stringElem.Column[prev:offset]
prev = offset
}
return nil
}
func (c *StringArrayColumn) WriteTo(wr *chproto.Writer) error {
if c.lcElem != nil {
c.lcElem.writePrefix(wr)
}
_ = c.WriteOffset(wr, 0)
return c.WriteData(wr)
}
var _ ArrayColumnar = (*StringArrayColumn)(nil)
func (c *StringArrayColumn) WriteOffset(wr *chproto.Writer, offset int) int {
for _, el := range c.Column {
offset += len(el)
wr.Uint64(uint64(offset))
}
return offset
}
func (c *StringArrayColumn) WriteData(wr *chproto.Writer) error {
for _, ss := range c.Column {
c.stringElem.Column = ss
if err := c.elem.WriteTo(wr); err != nil {
return err
}
}
return nil
}

3008
ch/chschema/column_gen.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,319 @@
package chschema
import (
"fmt"
"math"
"reflect"
"github.com/uptrace/go-clickhouse/ch/chproto"
)
type LCStringColumn struct {
StringColumn
}
var _ Columnar = (*LCStringColumn)(nil)
func NewLCStringColumn() Columnar {
return new(LCStringColumn)
}
func (c *LCStringColumn) ReadFrom(rd *chproto.Reader, numRow int) error {
if numRow == 0 {
return nil
}
if err := c.readPrefix(rd, numRow); err != nil {
return err
}
return c.readData(rd, numRow)
}
func (c *LCStringColumn) readPrefix(rd *chproto.Reader, numRow int) error {
version, err := rd.Int64()
if err != nil {
return err
}
if version != 1 {
return fmt.Errorf("ch: got version=%d, wanted 1", version)
}
return nil
}
func (c *LCStringColumn) readData(rd *chproto.Reader, numRow int) error {
if numRow == 0 {
return nil
}
flags, err := rd.Int64()
if err != nil {
return err
}
lcKey := newLCKeyType(flags & 0xf)
dictSize, err := rd.UInt64()
if err != nil {
return err
}
dict := make([]string, dictSize)
for i := range dict {
s, err := rd.String()
if err != nil {
return err
}
dict[i] = s
}
numKey, err := rd.UInt64()
if err != nil {
return err
}
if int(numKey) != numRow {
return fmt.Errorf("%d != %d", numKey, numRow)
}
if cap(c.Column) >= int(numKey) {
c.Column = c.Column[:numKey]
} else {
c.Column = make([]string, numKey)
}
for i := 0; i < int(numKey); i++ {
key, err := lcKey.read(rd)
if err != nil {
return err
}
c.Column[i] = dict[key]
}
return nil
}
func (c *LCStringColumn) WriteTo(wr *chproto.Writer) error {
c.writePrefix(wr)
c.writeData(wr)
return nil
}
func (c *LCStringColumn) writePrefix(wr *chproto.Writer) {
wr.Int64(1)
}
func (c *LCStringColumn) writeData(wr *chproto.Writer) {
if len(c.Column) == 0 {
return
}
keys := make([]int, len(c.Column))
var lc lowCard
for i, s := range c.Column {
keys[i] = lc.Add(s)
}
const hasAdditionalKeys = 1 << 9
const needUpdateDict = 1 << 10
dict := lc.Dict()
lcKey := newLCKey(int64(len(dict)))
wr.Int64(int64(lcKey.typ) | hasAdditionalKeys | needUpdateDict)
wr.Int64(int64(len(dict)))
for _, s := range dict {
wr.String(s)
}
wr.Int64(int64(len(keys)))
for _, key := range keys {
lcKey.write(wr, key)
}
}
//------------------------------------------------------------------------------
type ArrayLCStringColumn struct {
ArrayStringColumn
lc LCStringColumn
}
var _ Columnar = (*ArrayLCStringColumn)(nil)
func NewArrayLCStringColumn() Columnar {
return new(ArrayLCStringColumn)
}
func (c *ArrayLCStringColumn) ConvertAssign(idx int, dest reflect.Value) error {
dest.Set(reflect.ValueOf(c.Column[idx]))
return nil
}
func (c *ArrayLCStringColumn) ReadFrom(rd *chproto.Reader, numRow int) error {
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
if err := c.lc.readPrefix(rd, numRow); err != nil {
return err
}
offsets, err := c.readOffsets(rd, numRow)
if err != nil {
return err
}
if err := c.lc.readData(rd, offsets[len(offsets)-1]); err != nil {
return err
}
var prev int
for i, offset := range offsets {
c.Column[i] = c.lc.Column[prev:offset]
prev = offset
}
return nil
}
func (c *ArrayLCStringColumn) WriteTo(wr *chproto.Writer) error {
c.lc.writePrefix(wr)
_ = c.WriteOffset(wr, 0)
return c.WriteData(wr)
}
func (c *ArrayLCStringColumn) WriteData(wr *chproto.Writer) error {
for _, ss := range c.Column {
c.lc.Column = ss
c.lc.writeData(wr)
}
return nil
}
//------------------------------------------------------------------------------
type lcKey struct {
typ int8
read func(*chproto.Reader) (int, error)
write func(*chproto.Writer, int)
}
func newLCKey(numKey int64) lcKey {
if numKey <= math.MaxUint8 {
return newLCKeyType(0)
}
if numKey <= math.MaxUint16 {
return newLCKeyType(1)
}
if numKey <= math.MaxUint32 {
return newLCKeyType(2)
}
return newLCKeyType(3)
}
func newLCKeyType(typ int64) lcKey {
switch typ {
case 0:
return lcKey{
typ: 0,
read: func(rd *chproto.Reader) (int, error) {
n, err := rd.UInt8()
return int(n), err
},
write: func(wr *chproto.Writer, n int) {
wr.UInt8(uint8(n))
},
}
case 1:
return lcKey{
typ: int8(1),
read: func(rd *chproto.Reader) (int, error) {
n, err := rd.UInt16()
return int(n), err
},
write: func(wr *chproto.Writer, n int) {
wr.UInt16(uint16(n))
},
}
case 2:
return lcKey{
typ: 2,
read: func(rd *chproto.Reader) (int, error) {
n, err := rd.UInt32()
return int(n), err
},
write: func(wr *chproto.Writer, n int) {
wr.UInt32(uint32(n))
},
}
case 3:
return lcKey{
typ: 3,
read: func(rd *chproto.Reader) (int, error) {
n, err := rd.UInt64()
return int(n), err
},
write: func(wr *chproto.Writer, n int) {
wr.UInt64(uint64(n))
},
}
default:
panic("not reached")
}
}
//------------------------------------------------------------------------------
type lowCard struct {
slice sliceMap
dict map[string]int
}
func (lc *lowCard) Add(word string) int {
if i, ok := lc.dict[word]; ok {
return i
}
if lc.dict == nil {
lc.dict = make(map[string]int)
}
i := lc.slice.Add(word)
lc.dict[word] = i
return i
}
func (lc *lowCard) Dict() []string {
return lc.slice.Slice()
}
//------------------------------------------------------------------------------
type sliceMap struct {
ss []string
}
func (m sliceMap) Len() int {
return len(m.ss)
}
func (m sliceMap) Get(word string) (int, bool) {
for i, s := range m.ss {
if s == word {
return i, true
}
}
return 0, false
}
func (m *sliceMap) Add(word string) int {
m.ss = append(m.ss, word)
return len(m.ss) - 1
}
func (m sliceMap) Slice() []string {
return m.ss
}

View File

@@ -7,21 +7,35 @@ import (
)
type NullableColumn struct {
Nulls Uint8Column
Nulls UInt8Column
Values Columnar
nullable reflect.Value // reflect.Slice
}
func NullableNewColumnFunc(fn NewColumnFunc) NewColumnFunc {
return func(typ reflect.Type, chType string, numRow int) Columnar {
func NewNullableColumnFunc(fn NewColumnFunc) NewColumnFunc {
return func() Columnar {
return &NullableColumn{
Values: fn(typ, chType, numRow),
Values: fn(),
}
}
}
var _ Columnar = (*NullableColumn)(nil)
func (c *NullableColumn) Init(chType string) error {
return nil
}
func (c *NullableColumn) AllocForReading(numRow int) {
c.Nulls.AllocForReading(numRow)
c.Values.AllocForReading(numRow)
}
func (c *NullableColumn) ResetForWriting(numRow int) {
c.Nulls.ResetForWriting(numRow)
c.Values.ResetForWriting(numRow)
}
func (c *NullableColumn) Type() reflect.Type {
return reflect.PtrTo(c.Values.Type())
}
@@ -44,7 +58,7 @@ func (c *NullableColumn) Value() any {
return c.nullable.Interface()
}
func (c *NullableColumn) Nullable(nulls Uint8Column) any {
func (c *NullableColumn) Nullable(nulls UInt8Column) any {
panic("not implemented")
}

View File

@@ -0,0 +1,217 @@
//go:build !amd64 && !arm64
package chschema
import (
"github.com/uptrace/go-clickhouse/ch/chproto"
)
func (c *Int8Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.Int8()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *Int8Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.Int8(n)
}
return nil
}
func (c *UInt8Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.UInt8()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *UInt8Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.UInt8(n)
}
return nil
}
func (c *Int16Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.Int16()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *Int16Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.Int16(n)
}
return nil
}
func (c *UInt16Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.UInt16()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *UInt16Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.UInt16(n)
}
return nil
}
func (c *Int32Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.Int32()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *Int32Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.Int32(n)
}
return nil
}
func (c *UInt32Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.UInt32()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *UInt32Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.UInt32(n)
}
return nil
}
func (c *Int64Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.Int64()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *Int64Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.Int64(n)
}
return nil
}
func (c *UInt64Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.UInt64()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *UInt64Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.UInt64(n)
}
return nil
}
func (c *Float32Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.Float32()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *Float32Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.Float32(n)
}
return nil
}
func (c *Float64Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.Float64()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *Float64Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.Float64(n)
}
return nil
}

View File

@@ -0,0 +1,351 @@
//go:build amd64 || arm64
package chschema
import (
"io"
"reflect"
"unsafe"
"github.com/uptrace/go-clickhouse/ch/chproto"
)
func (c *Int8Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = 8 / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *Int8Column) WriteTo(wr *chproto.Writer) error {
const size = 8 / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}
func (c *UInt8Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = 8 / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *UInt8Column) WriteTo(wr *chproto.Writer) error {
const size = 8 / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}
func (c *Int16Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = 16 / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *Int16Column) WriteTo(wr *chproto.Writer) error {
const size = 16 / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}
func (c *UInt16Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = 16 / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *UInt16Column) WriteTo(wr *chproto.Writer) error {
const size = 16 / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}
func (c *Int32Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = 32 / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *Int32Column) WriteTo(wr *chproto.Writer) error {
const size = 32 / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}
func (c *UInt32Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = 32 / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *UInt32Column) WriteTo(wr *chproto.Writer) error {
const size = 32 / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}
func (c *Int64Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = 64 / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *Int64Column) WriteTo(wr *chproto.Writer) error {
const size = 64 / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}
func (c *UInt64Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = 64 / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *UInt64Column) WriteTo(wr *chproto.Writer) error {
const size = 64 / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}
func (c *Float32Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = 32 / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *Float32Column) WriteTo(wr *chproto.Writer) error {
const size = 32 / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}
func (c *Float64Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = 64 / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *Float64Column) WriteTo(wr *chproto.Writer) error {
const size = 64 / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}

View File

@@ -40,7 +40,7 @@ func parseEnum(s string) *enumInfo {
}
func _parseEnum(chType string) (*enumInfo, error) {
s := enumType(chType)
s := chEnumType(chType)
if s == "" {
return nil, fmt.Errorf("can't parse enum type: %q", chType)
}

View File

@@ -27,6 +27,10 @@ func NewFormatter() Formatter {
return Formatter{}
}
func (f Formatter) AppendName(b []byte, ident string) []byte {
return AppendName(b, ident)
}
func (f Formatter) AppendIdent(b []byte, ident string) []byte {
return AppendIdent(b, ident)
}

View File

@@ -1,53 +0,0 @@
package chschema
type lowCard struct {
slice sliceMap
dict map[string]int
}
func (lc *lowCard) Add(word string) int {
if i, ok := lc.dict[word]; ok {
return i
}
if lc.dict == nil {
lc.dict = make(map[string]int)
}
i := lc.slice.Add(word)
lc.dict[word] = i
return i
}
func (lc *lowCard) Dict() []string {
return lc.slice.Slice()
}
//------------------------------------------------------------------------------
type sliceMap struct {
ss []string
}
func (m sliceMap) Len() int {
return len(m.ss)
}
func (m sliceMap) Get(word string) (int, bool) {
for i, s := range m.ss {
if s == word {
return i, true
}
}
return 0, false
}
func (m *sliceMap) Add(word string) int {
m.ss = append(m.ss, word)
return len(m.ss) - 1
}
func (m sliceMap) Slice() []string {
return m.ss
}

View File

@@ -1,6 +1,7 @@
package chschema
import (
"reflect"
"strings"
"github.com/uptrace/go-clickhouse/ch/internal"
@@ -27,20 +28,50 @@ func (s Safe) AppendQuery(fmter Formatter, b []byte) ([]byte, error) {
//------------------------------------------------------------------------------
// FQN represents a fully qualified SQL name, for example, table or column name.
type FQN string
// Name represents a SQL identifier, for example, table or column name.
type Name string
var _ QueryAppender = (*FQN)(nil)
var _ QueryAppender = (*Name)(nil)
func (s FQN) AppendQuery(fmter Formatter, b []byte) ([]byte, error) {
func (s Name) AppendQuery(fmter Formatter, b []byte) ([]byte, error) {
return fmter.AppendName(b, string(s)), nil
}
func AppendName(b []byte, field string) []byte {
return appendName(b, internal.Bytes(field))
}
func appendName(b, src []byte) []byte {
const quote = '"'
b = append(b, quote)
for _, c := range src {
if c == quote {
b = append(b, quote, quote)
} else {
b = append(b, c)
}
}
b = append(b, quote)
return b
}
//------------------------------------------------------------------------------
// Ident represents a fully qualified SQL name, for example, table or column name.
type Ident string
var _ QueryAppender = (*Name)(nil)
func (s Ident) AppendQuery(fmter Formatter, b []byte) ([]byte, error) {
return fmter.AppendIdent(b, string(s)), nil
}
func AppendFQN(b []byte, field string) []byte {
return appendFQN(b, internal.Bytes(field))
func AppendIdent(b []byte, field string) []byte {
return appendIdent(b, internal.Bytes(field))
}
func appendFQN(b, src []byte) []byte {
func appendIdent(b, src []byte) []byte {
const quote = '"'
var quoted bool
@@ -77,34 +108,6 @@ loop:
return b
}
// Ident represents a SQL identifier, for example, table or column name.
type Ident string
var _ QueryAppender = (*Ident)(nil)
func (s Ident) AppendQuery(fmter Formatter, b []byte) ([]byte, error) {
return fmter.AppendIdent(b, string(s)), nil
}
func AppendIdent(b []byte, field string) []byte {
return appendIdent(b, internal.Bytes(field))
}
func appendIdent(b, src []byte) []byte {
const quote = '"'
b = append(b, quote)
for _, c := range src {
if c == quote {
b = append(b, quote, quote)
} else {
b = append(b, c)
}
}
b = append(b, quote)
return b
}
//------------------------------------------------------------------------------
type QueryWithArgs struct {
@@ -126,7 +129,7 @@ func SafeQuery(query string, args []any) QueryWithArgs {
}
}
func UnsafeIdent(ident string) QueryWithArgs {
func UnsafeName(ident string) QueryWithArgs {
return QueryWithArgs{Query: ident}
}
@@ -134,9 +137,13 @@ func (q QueryWithArgs) IsZero() bool {
return q.Query == "" && q.Args == nil
}
func (q QueryWithArgs) IsEmpty() bool {
return q.Query == ""
}
func (q QueryWithArgs) AppendQuery(fmter Formatter, b []byte) ([]byte, error) {
if q.Args == nil {
return fmter.AppendIdent(b, q.Query), nil
return fmter.AppendName(b, q.Query), nil
}
return fmter.AppendQuery(b, q.Query, q.Args...), nil
}
@@ -159,3 +166,43 @@ func SafeQueryWithSep(query string, args []any, sep string) QueryWithSep {
Sep: sep,
}
}
//------------------------------------------------------------------------------
type ArrayValue struct {
v reflect.Value
}
func Array(vi interface{}) *ArrayValue {
return &ArrayValue{
v: reflect.ValueOf(vi),
}
}
var _ QueryAppender = (*ArrayValue)(nil)
func (a *ArrayValue) AppendQuery(fmter Formatter, b []byte) ([]byte, error) {
if !a.v.IsValid() || a.v.Len() == 0 {
b = append(b, "[]"...)
return b, nil
}
typ := a.v.Type()
elemType := typ.Elem()
appendElem := Appender(elemType)
b = append(b, '[')
ln := a.v.Len()
for i := 0; i < ln; i++ {
if i > 0 {
b = append(b, ',')
}
elem := a.v.Index(i)
b = appendElem(fmter, b, elem)
}
b = append(b, ']')
return b, nil
}

View File

@@ -13,8 +13,7 @@ import (
)
const (
discardUnknownColumnsFlag = internal.Flag(1) << iota
columnarFlag
columnarFlag = internal.Flag(1) << iota
afterScanBlockHookFlag
)
@@ -155,7 +154,7 @@ func (t *Table) addFields(typ reflect.Type, baseIndex []int) {
}
}
if f.NewColumn == nil {
f.NewColumn = ColumnFactory(f.Type, f.CHType)
f.NewColumn = ColumnFactory(f.CHType, f.Type)
}
}
}
@@ -212,7 +211,7 @@ func (t *Table) newField(f reflect.StructField, index []int, tag tagparser.Tag)
field.CHType = s
field.setFlag(customTypeFlag)
} else {
field.CHType = clickhouseType(f.Type)
field.CHType = chType(f.Type)
}
if tag.HasOption("lc") {
@@ -252,7 +251,7 @@ func (t *Table) addField(field *Field) {
t.FieldMap[field.CHName] = field
}
func (t *Table) NewColumn(colName, colType string, numRow int) *Column {
func (t *Table) NewColumn(colName, colType string) *Column {
field, ok := t.FieldMap[colName]
if !ok {
internal.Logger.Printf("ch: %s has no column=%q", t, colName)
@@ -260,22 +259,20 @@ func (t *Table) NewColumn(colName, colType string, numRow int) *Column {
}
if colType != field.CHType {
if field.CHType != chtype.Any {
internal.Logger.Printf("got column type %q, but %s.%s has type %q",
colType, t.Type.Name(), field.GoName, field.CHType)
}
return &Column{
Name: colName,
Type: colType,
Columnar: ColumnFactory(field.Type, colType)(field.Type, colType, numRow),
Columnar: NewColumn(colType, field.Type),
}
}
col := field.NewColumn()
col.Init(field.CHType)
return &Column{
Name: colName,
Type: field.CHType,
Columnar: field.NewColumn(field.Type, field.CHType, numRow),
Columnar: col,
}
}
@@ -291,7 +288,7 @@ func (t *Table) AppendNamedArg(
}
func quoteTableName(s string) Safe {
return Safe(appendFQN(nil, internal.Bytes(s)))
return Safe(appendIdent(nil, internal.Bytes(s)))
}
func quoteColumnName(s string) Safe {

View File

@@ -4,15 +4,42 @@ import (
"fmt"
"net"
"reflect"
"strconv"
"strings"
"time"
"github.com/uptrace/go-clickhouse/ch/bfloat16"
"github.com/uptrace/go-clickhouse/ch/chtype"
"github.com/uptrace/go-clickhouse/ch/internal"
)
var chType = [...]string{
reflect.Bool: chtype.UInt8,
var (
boolType = reflect.TypeOf(false)
int8Type = reflect.TypeOf(int8(0))
int16Type = reflect.TypeOf(int16(0))
int32Type = reflect.TypeOf(int32(0))
int64Type = reflect.TypeOf(int64(0))
uint8Type = reflect.TypeOf(uint8(0))
uint16Type = reflect.TypeOf(uint16(0))
uint32Type = reflect.TypeOf(uint32(0))
uint64Type = reflect.TypeOf(uint64(0))
float32Type = reflect.TypeOf(float32(0))
float64Type = reflect.TypeOf(float64(0))
stringType = reflect.TypeOf("")
bytesType = reflect.TypeOf((*[]byte)(nil)).Elem()
uuidType = reflect.TypeOf((*UUID)(nil)).Elem()
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
ipType = reflect.TypeOf((*net.IP)(nil)).Elem()
ipNetType = reflect.TypeOf((*net.IPNet)(nil)).Elem()
bfloat16MapType = reflect.TypeOf((*map[bfloat16.T]uint64)(nil)).Elem()
sliceUint64Type = reflect.TypeOf((*[]uint64)(nil)).Elem()
sliceFloat32Type = reflect.TypeOf((*[]float32)(nil)).Elem()
)
var chTypes = [...]string{
reflect.Bool: chtype.Bool,
reflect.Int: chtype.Int64,
reflect.Int8: chtype.Int8,
reflect.Int16: chtype.Int16,
@@ -40,8 +67,181 @@ var chType = [...]string{
reflect.UnsafePointer: "",
}
// keep in sync with ColumnFactory
func clickhouseType(typ reflect.Type) string {
type NewColumnFunc func() Columnar
func NewColumn(chType string, typ reflect.Type) Columnar {
col := ColumnFactory(chType, typ)()
col.Init(chType)
return col
}
func ColumnFactory(chType string, typ reflect.Type) NewColumnFunc {
switch chType {
case chtype.Int8:
return NewInt8Column
case chtype.Int16:
return NewInt16Column
case chtype.Int32:
return NewInt32Column
case chtype.Int64:
return NewInt64Column
case chtype.UInt8:
return NewUInt8Column
case chtype.UInt16:
return NewUInt16Column
case chtype.UInt32:
return NewUInt32Column
case chtype.UInt64:
return NewUInt64Column
case chtype.Float32:
return NewFloat32Column
case chtype.Float64:
return NewFloat64Column
case chtype.String:
if typ == bytesType {
return NewBytesColumn
}
return NewStringColumn
case "LowCardinality(String)":
return NewLCStringColumn
case chtype.Bool:
return NewBoolColumn
case chtype.UUID:
return NewUUIDColumn
case chtype.IPv6:
return NewIPColumn
case chtype.DateTime:
return NewDateTimeColumn
case chtype.Date:
return NewDateColumn
case "Array(Int8)":
return NewArrayInt8Column
case "Array(UInt8)":
return NewArrayUInt8Column
case "Array(Int16)":
return NewArrayInt16Column
case "Array(UInt16)":
return NewArrayUInt16Column
case "Array(Int32)":
return NewArrayInt32Column
case "Array(UInt32)":
return NewArrayUInt32Column
case "Array(Int64)":
return NewArrayInt64Column
case "Array(UInt64)":
return NewArrayUInt64Column
case "Array(Float32)":
return NewArrayFloat32Column
case "Array(Float64)":
return NewArrayFloat64Column
case "Array(String)":
return NewArrayStringColumn
case "Array(LowCardinality(String))":
return NewArrayLCStringColumn
case "Array(DateTime)":
return NewArrayDateTimeColumn
case "Array(Bool)":
return NewArrayBoolColumn
case "Array(Array(Int8))":
return NewArrayArrayInt8Column
case "Array(Array(UInt8))":
return NewArrayArrayUInt8Column
case "Array(Array(Int16))":
return NewArrayArrayInt16Column
case "Array(Array(UInt16))":
return NewArrayArrayUInt16Column
case "Array(Array(Int32))":
return NewArrayArrayInt32Column
case "Array(Array(UInt32))":
return NewArrayArrayUInt32Column
case "Array(Array(Int64))":
return NewArrayArrayInt64Column
case "Array(Array(UInt64))":
return NewArrayArrayUInt64Column
case "Array(Array(Float32))":
return NewArrayArrayFloat32Column
case "Array(Array(Float64))":
return NewArrayArrayFloat64Column
case "Array(Array(String))":
return NewArrayArrayStringColumn
case "Array(Array(DateTime))":
return NewArrayArrayDateTimeColumn
case "Array(Array(Bool))":
return NewArrayArrayStringColumn
case chtype.Any:
return nil
}
if chType := chEnumType(chType); chType != "" {
return NewEnumColumn
}
if chType := chArrayElemType(chType); chType != "" {
if chType := chEnumType(chType); chType != "" {
return NewArrayEnumColumn
}
}
if isDateTime64Type(chType) {
return NewDateTime64Column
}
if chType := chDateTimeType(chType); chType != "" {
return ColumnFactory(chType, typ)
}
if chType := chNullableType(chType); chType != "" {
if typ != nil {
typ = typ.Elem()
}
return NewNullableColumnFunc(ColumnFactory(chType, typ))
}
if chType := chSimpleAggFunc(chType); chType != "" {
return ColumnFactory(chType, typ)
}
if funcName, _ := aggFuncNameAndType(chType); funcName != "" {
switch funcName {
case "quantileBFloat16", "quantilesBFloat16":
return NewBFloat16HistColumn
default:
panic(fmt.Errorf("unsupported ClickHouse type: %s", chType))
}
}
if typ == nil {
panic(fmt.Errorf("unsupported ClickHouse column: %s", chType))
}
kind := typ.Kind()
switch kind {
case reflect.Ptr:
if typ.Elem().Kind() == reflect.Struct {
return NewJSONColumn
}
return NewNullableColumnFunc(ColumnFactory(chNullableType(chType), typ.Elem()))
case reflect.Slice:
switch elem := typ.Elem(); elem.Kind() {
case reflect.Ptr:
if elem.Elem().Kind() == reflect.Struct {
return NewJSONColumn
}
case reflect.Struct:
if elem != timeType {
return NewJSONColumn
}
}
}
panic(fmt.Errorf("unsupported ClickHouse column: %s", chType))
}
func chType(typ reflect.Type) string {
switch typ {
case timeType:
return chtype.DateTime
@@ -55,7 +255,7 @@ func clickhouseType(typ reflect.Type) string {
if typ.Elem().Kind() == reflect.Struct {
return chtype.String
}
return fmt.Sprintf("Nullable(%s)", clickhouseType(typ.Elem()))
return fmt.Sprintf("Nullable(%s)", chType(typ.Elem()))
case reflect.Slice:
switch elem := typ.Elem(); elem.Kind() {
case reflect.Ptr:
@@ -70,265 +270,28 @@ func clickhouseType(typ reflect.Type) string {
return chtype.String // []byte
}
return "Array(" + clickhouseType(typ.Elem()) + ")"
return "Array(" + chType(typ.Elem()) + ")"
case reflect.Array:
if isUUID(typ) {
return chtype.UUID
}
}
if s := chType[kind]; s != "" {
if s := chTypes[kind]; s != "" {
return s
}
panic(fmt.Errorf("ch: unsupported Go type: %s", typ))
}
type NewColumnFunc func(typ reflect.Type, chType string, numRow int) Columnar
var kindToColumn = [...]NewColumnFunc{
reflect.Bool: NewBoolColumn,
reflect.Int: NewInt64Column,
reflect.Int8: NewInt8Column,
reflect.Int16: NewInt16Column,
reflect.Int32: NewInt32Column,
reflect.Int64: NewInt64Column,
reflect.Uint: NewUint64Column,
reflect.Uint8: NewUint8Column,
reflect.Uint16: NewUint16Column,
reflect.Uint32: NewUint32Column,
reflect.Uint64: NewUint64Column,
reflect.Uintptr: nil,
reflect.Float32: NewFloat32Column,
reflect.Float64: NewFloat64Column,
reflect.Complex64: nil,
reflect.Complex128: nil,
reflect.Array: nil,
reflect.Chan: nil,
reflect.Func: nil,
reflect.Interface: nil,
reflect.Map: NewJSONColumn,
reflect.Ptr: nil,
reflect.Slice: nil,
reflect.String: NewStringColumn,
reflect.Struct: NewJSONColumn,
reflect.UnsafePointer: nil,
}
// keep in sync with clickhouseType
func ColumnFactory(typ reflect.Type, chType string) NewColumnFunc {
if chType == chtype.Any {
return nil
}
if s := lowCardinalityType(chType); s != "" {
switch s {
case chtype.String:
return NewLCStringColumn
}
panic(fmt.Errorf("got %s, wanted LowCardinality(String)", chType))
}
if s := enumType(chType); s != "" {
return NewEnumColumn
}
if strings.HasPrefix(chType, "SimpleAggregateFunction(") {
chType = chSubType(chType, "SimpleAggregateFunction(")
} else if s := dateTimeType(chType); s != "" {
chType = s
}
switch typ {
case timeType:
switch chType {
case chtype.DateTime:
return NewDateTimeColumn
case chtype.Date:
return NewDateColumn
case chtype.Int64:
return NewTimeColumn
}
case ipType:
return NewIPColumn
}
kind := typ.Kind()
switch kind {
case reflect.Ptr:
if typ.Elem().Kind() == reflect.Struct {
return NewJSONColumn
}
return NullableNewColumnFunc(ColumnFactory(typ.Elem(), nullableType(chType)))
case reflect.Slice:
switch elem := typ.Elem(); elem.Kind() {
case reflect.Ptr:
if elem.Elem().Kind() == reflect.Struct {
return NewJSONColumn
}
case reflect.Uint8:
if chType == chtype.String {
return NewBytesColumn
}
case reflect.String:
return NewStringArrayColumn
case reflect.Struct:
if elem != timeType {
return NewJSONColumn
}
}
return NewArrayColumn
case reflect.Array:
if isUUID(typ) {
return NewUUIDColumn
}
case reflect.Interface:
return columnFromCHType(chType)
}
switch chType {
case chtype.DateTime:
switch typ {
case uint32Type:
return NewUint32Column
case int64Type:
return NewInt64TimeColumn
default:
return NewDateTimeColumn
}
}
fn := kindToColumn[kind]
if fn != nil {
return fn
}
panic(fmt.Errorf("unsupported go_type=%q ch_type=%q", typ.String(), chType))
}
func columnFromCHType(chType string) NewColumnFunc {
switch chType {
case chtype.String:
return NewStringColumn
case chtype.UUID:
return NewUUIDColumn
case chtype.Int8:
return NewInt8Column
case chtype.Int16:
return NewInt16Column
case chtype.Int32:
return NewInt32Column
case chtype.Int64:
return NewInt64Column
case chtype.UInt8:
return NewUint8Column
case chtype.UInt16:
return NewUint16Column
case chtype.UInt32:
return NewUint32Column
case chtype.UInt64:
return NewUint64Column
case chtype.Float32:
return NewFloat32Column
case chtype.Float64:
return NewFloat64Column
case chtype.DateTime:
return NewDateTimeColumn
case chtype.Date:
return NewDateColumn
case chtype.IPv6:
return NewIPColumn
default:
return nil
}
}
var (
boolType = reflect.TypeOf(false)
int8Type = reflect.TypeOf(int8(0))
int16Type = reflect.TypeOf(int16(0))
int32Type = reflect.TypeOf(int32(0))
int64Type = reflect.TypeOf(int64(0))
uint8Type = reflect.TypeOf(uint8(0))
uint16Type = reflect.TypeOf(uint16(0))
uint32Type = reflect.TypeOf(uint32(0))
uint64Type = reflect.TypeOf(uint64(0))
float32Type = reflect.TypeOf(float32(0))
float64Type = reflect.TypeOf(float64(0))
stringType = reflect.TypeOf("")
bytesType = reflect.TypeOf((*[]byte)(nil)).Elem()
uuidType = reflect.TypeOf((*UUID)(nil)).Elem()
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
ipType = reflect.TypeOf((*net.IP)(nil)).Elem()
ipNetType = reflect.TypeOf((*net.IPNet)(nil)).Elem()
bfloat16HistType = reflect.TypeOf((*map[chtype.BFloat16]uint64)(nil)).Elem()
int64SliceType = reflect.TypeOf((*[]int64)(nil)).Elem()
uint64SliceType = reflect.TypeOf((*[]uint64)(nil)).Elem()
float32SliceType = reflect.TypeOf((*[]float32)(nil)).Elem()
float64SliceType = reflect.TypeOf((*[]float64)(nil)).Elem()
stringSliceType = reflect.TypeOf((*[]string)(nil)).Elem()
)
func goType(chType string) reflect.Type {
switch chType {
case chtype.Int8:
return int8Type
case chtype.Int32:
return int32Type
case chtype.Int64:
return int64Type
case chtype.UInt8:
return uint8Type
case chtype.UInt16:
return uint16Type
case chtype.UInt32:
return uint32Type
case chtype.UInt64:
return uint64Type
case chtype.Float32:
return float32Type
case chtype.Float64:
return float64Type
case chtype.String:
return stringType
case chtype.UUID:
return uuidType
case chtype.DateTime:
return timeType
case chtype.Date:
return timeType
case chtype.IPv6:
return ipType
default:
}
if s := chArrayElemType(chType); s != "" {
return reflect.SliceOf(goType(s))
}
if s := lowCardinalityType(chType); s != "" {
return goType(s)
}
if s := enumType(chType); s != "" {
return stringType
}
if s := dateTimeType(chType); s != "" {
return timeType
}
if s := nullableType(chType); s != "" {
return reflect.PtrTo(goType(s))
}
if _, funcType := aggFuncNameAndType(chType); funcType != "" {
return goType(funcType)
}
panic(fmt.Errorf("unsupported ClickHouse type=%q", chType))
}
func chArrayElemType(s string) string {
if s := chSubType(s, "SimpleAggregateFunction("); s != "" {
if i := strings.Index(s, ", "); i >= 0 {
s = s[i+2:]
}
return chSubType(s, "Array(")
}
s = chSubType(s, "Array(")
if s == "" {
return ""
@@ -347,15 +310,23 @@ func chArrayElemType(s string) string {
return s
}
func lowCardinalityType(s string) string {
return chSubType(s, "LowCardinality(")
}
func enumType(s string) string {
func chEnumType(s string) string {
return chSubType(s, "Enum8(")
}
func dateTimeType(s string) string {
func chSimpleAggFunc(s string) string {
s = chSubType(s, "SimpleAggregateFunction(")
if s == "" {
return ""
}
i := strings.Index(s, ", ")
if i == -1 {
return ""
}
return s[i+2:]
}
func chDateTimeType(s string) string {
s = chSubType(s, "DateTime(")
if s == "" {
return ""
@@ -366,12 +337,36 @@ func dateTimeType(s string) string {
return chtype.DateTime
}
func nullableType(s string) string {
func isDateTime64Type(s string) bool {
return chSubType(s, "DateTime64(") != ""
}
func parseDateTime64Prec(s string) int {
s = chSubType(s, "DateTime64(")
if s == "" {
return 0
}
prec, err := strconv.Atoi(s)
if err != nil {
return 0
}
return prec
}
func chNullableType(s string) string {
return chSubType(s, "Nullable(")
}
func aggFuncNameAndType(chType string) (funcName, funcType string) {
s := chSubType(chType, "SimpleAggregateFunction(")
var s string
for _, prefix := range []string{"SimpleAggregateFunction(", "AggregateFunction("} {
s = chSubType(chType, prefix)
if s != "" {
break
}
}
if s == "" {
return "", ""
}

View File

@@ -1,20 +1,22 @@
package chtype
const (
Any = "_" // for decoding into interface{}
String = "String"
UUID = "UUID"
Int8 = "Int8"
Int16 = "Int16"
Int32 = "Int32"
Int64 = "Int64"
UInt8 = "UInt8"
UInt16 = "UInt16"
UInt32 = "UInt32"
UInt64 = "UInt64"
Float32 = "Float32"
Float64 = "Float64"
DateTime = "DateTime"
Date = "Date"
IPv6 = "IPv6"
Any = "_" // for decoding into interface{}
Bool = "Bool"
String = "String"
UUID = "UUID"
Int8 = "Int8"
Int16 = "Int16"
Int32 = "Int32"
Int64 = "Int64"
UInt8 = "UInt8"
UInt16 = "UInt16"
UInt32 = "UInt32"
UInt64 = "UInt64"
Float32 = "Float32"
Float64 = "Float64"
DateTime = "DateTime"
DateTime64 = "DateTime64"
Date = "Date"
IPv6 = "IPv6"
)

View File

@@ -1,13 +0,0 @@
package chtype
import "math"
type BFloat16 uint16
func ToBFloat16(f float64) BFloat16 {
return BFloat16(math.Float32bits(float32(f)) >> 16)
}
func (f BFloat16) Float32() float32 {
return math.Float32frombits(uint32(f) << 16)
}

View File

@@ -17,16 +17,19 @@ import (
const (
discardUnknownColumnsFlag internal.Flag = 1 << iota
autoCreateDatabaseFlag
)
type Config struct {
chpool.Config
Network string
Compression bool
Addr string
User string
Password string
Database string
Cluster string
DialTimeout time.Duration
TLSConfig *tls.Config
@@ -40,37 +43,44 @@ type Config struct {
MaxRetryBackoff time.Duration
}
func (cfg *Config) netDialer() *net.Dialer {
func (conf *Config) clone() *Config {
clone := *conf
return &clone
}
func (conf *Config) netDialer() *net.Dialer {
return &net.Dialer{
Timeout: cfg.DialTimeout,
Timeout: conf.DialTimeout,
KeepAlive: 5 * time.Minute,
}
}
func defaultConfig() *Config {
var cfg *Config
var conf *Config
poolSize := 2 * runtime.GOMAXPROCS(0)
cfg = &Config{
Network: "tcp",
conf = &Config{
Config: chpool.Config{
PoolSize: poolSize,
PoolTimeout: 30 * time.Second,
MaxIdleConns: poolSize,
ConnMaxIdleTime: 30 * time.Minute,
},
Compression: true,
Addr: "localhost:9000",
User: "default",
Database: "default",
DialTimeout: 5 * time.Second,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 10 * time.Second,
MaxRetries: 2,
MinRetryBackoff: 500 * time.Millisecond,
MaxRetryBackoff: time.Second,
Config: chpool.Config{
PoolSize: poolSize,
MaxIdleConns: poolSize,
PoolTimeout: 30 * time.Second,
},
MaxRetries: 3,
MinRetryBackoff: time.Millisecond,
MaxRetryBackoff: 3 * time.Second,
}
return cfg
return conf
}
type Option func(db *DB)
@@ -81,51 +91,70 @@ func WithDiscardUnknownColumns() Option {
}
}
// WithAddr configures TCP host:port or Unix socket depending on Network.
// WithCompression enables/disables LZ4 compression.
func WithCompression(enabled bool) Option {
return func(db *DB) {
db.conf.Compression = enabled
}
}
func WithAutoCreateDatabase(enabled bool) Option {
return func(db *DB) {
db.flags.Set(autoCreateDatabaseFlag)
}
}
// WithAddr configures TCP host:port.
func WithAddr(addr string) Option {
return func(db *DB) {
db.cfg.Addr = addr
db.conf.Addr = addr
}
}
// WithTLSConfig configures TLS config for secure connections.
func WithTLSConfig(cfg *tls.Config) Option {
func WithTLSConfig(conf *tls.Config) Option {
return func(db *DB) {
db.cfg.TLSConfig = cfg
db.conf.TLSConfig = conf
}
}
func WithQuerySettings(params map[string]any) Option {
return func(db *DB) {
db.cfg.QuerySettings = params
db.conf.QuerySettings = params
}
}
func WithInsecure(on bool) Option {
return func(db *DB) {
if on {
db.cfg.TLSConfig = nil
db.conf.TLSConfig = nil
} else {
db.cfg.TLSConfig = &tls.Config{InsecureSkipVerify: true}
db.conf.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
}
}
func WithUser(user string) Option {
return func(db *DB) {
db.cfg.User = user
db.conf.User = user
}
}
func WithPassword(password string) Option {
return func(db *DB) {
db.cfg.Password = password
db.conf.Password = password
}
}
func WithDatabase(database string) Option {
return func(db *DB) {
db.cfg.Database = database
db.conf.Database = database
}
}
func WithCluster(cluster string) Option {
return func(db *DB) {
db.conf.Cluster = cluster
}
}
@@ -133,7 +162,7 @@ func WithDatabase(database string) Option {
// Default is 5 seconds.
func WithDialTimeout(timeout time.Duration) Option {
return func(db *DB) {
db.cfg.DialTimeout = timeout
db.conf.DialTimeout = timeout
}
}
@@ -141,7 +170,7 @@ func WithDialTimeout(timeout time.Duration) Option {
// with a timeout instead of blocking.
func WithReadTimeout(timeout time.Duration) Option {
return func(db *DB) {
db.cfg.ReadTimeout = timeout
db.conf.ReadTimeout = timeout
}
}
@@ -149,15 +178,15 @@ func WithReadTimeout(timeout time.Duration) Option {
// with a timeout instead of blocking.
func WithWriteTimeout(timeout time.Duration) Option {
return func(db *DB) {
db.cfg.WriteTimeout = timeout
db.conf.WriteTimeout = timeout
}
}
func WithTimeout(timeout time.Duration) Option {
return func(db *DB) {
db.cfg.DialTimeout = timeout
db.cfg.ReadTimeout = timeout
db.cfg.WriteTimeout = timeout
db.conf.DialTimeout = timeout
db.conf.ReadTimeout = timeout
db.conf.WriteTimeout = timeout
}
}
@@ -165,7 +194,7 @@ func WithTimeout(timeout time.Duration) Option {
// Default is to retry query 2 times.
func WithMaxRetries(maxRetries int) Option {
return func(db *DB) {
db.cfg.MaxRetries = maxRetries
db.conf.MaxRetries = maxRetries
}
}
@@ -173,7 +202,7 @@ func WithMaxRetries(maxRetries int) Option {
// Default is 250 milliseconds; -1 disables backoff.
func WithMinRetryBackoff(backoff time.Duration) Option {
return func(db *DB) {
db.cfg.MinRetryBackoff = backoff
db.conf.MinRetryBackoff = backoff
}
}
@@ -181,7 +210,7 @@ func WithMinRetryBackoff(backoff time.Duration) Option {
// Default is 4 seconds; -1 disables backoff.
func WithMaxRetryBackoff(backoff time.Duration) Option {
return func(db *DB) {
db.cfg.MaxRetryBackoff = backoff
db.conf.MaxRetryBackoff = backoff
}
}
@@ -189,25 +218,30 @@ func WithMaxRetryBackoff(backoff time.Duration) Option {
// Default is 2 connections per every CPU as reported by runtime.NumCPU.
func WithPoolSize(poolSize int) Option {
return func(db *DB) {
db.cfg.PoolSize = poolSize
db.cfg.MaxIdleConns = poolSize
db.conf.PoolSize = poolSize
db.conf.MaxIdleConns = poolSize
}
}
// WithMinIdleConns configures minimum number of idle connections which is useful when establishing
// new connection is slow.
func WithMinIdleConns(minIdleConns int) Option {
// WithConnMaxLifetime sets the maximum amount of time a connection may be reused.
// Expired connections may be closed lazily before reuse.
// If d <= 0, connections are not closed due to a connection's age.
func WithConnMaxLifetime(d time.Duration) Option {
return func(db *DB) {
db.cfg.MinIdleConns = minIdleConns
db.conf.ConnMaxLifetime = d
}
}
// WithMaxConnAge configures Connection age at which client retires (closes) the connection.
// It is useful with proxies like HAProxy.
// Default is to not close aged connections.
func WithMaxConnAge(timeout time.Duration) Option {
// SetConnMaxIdleTime sets the maximum amount of time a connection may be idle.
// Expired connections may be closed lazily before reuse.
//
// If d <= 0, connections are not closed due to a connection's idle time.
//
// ClickHouse closes idle connections after 1 hour (see idle_connection_timeout).
func WithConnMaxIdleTime(d time.Duration) Option {
return func(db *DB) {
db.cfg.MaxConnAge = timeout
db.conf.ConnMaxIdleTime = d
}
}
@@ -217,7 +251,7 @@ func WithMaxConnAge(timeout time.Duration) Option {
// ReadTimeout + 1 second.
func WithPoolTimeout(timeout time.Duration) Option {
return func(db *DB) {
db.cfg.PoolTimeout = timeout
db.conf.PoolTimeout = timeout
}
}
@@ -244,25 +278,27 @@ func parseDSN(dsn string) ([]Option, error) {
switch u.Scheme {
case "ch", "clickhouse":
if u.Host != "" {
addr := u.Host
if !strings.Contains(addr, ":") {
addr += ":5432"
}
opts = append(opts, WithAddr(addr))
}
if len(u.Path) > 1 {
opts = append(opts, WithDatabase(u.Path[1:]))
}
if host := q.string("host"); host != "" {
opts = append(opts, WithAddr(host))
}
// ok
default:
return nil, errors.New("ch: unknown scheme: " + u.Scheme)
}
if u.Host != "" {
addr := u.Host
if !strings.Contains(addr, ":") {
addr += ":5432"
}
opts = append(opts, WithAddr(addr))
}
if len(u.Path) > 1 {
opts = append(opts, WithDatabase(u.Path[1:]))
}
if host := q.string("host"); host != "" {
opts = append(opts, WithAddr(host))
}
if u.User != nil {
opts = append(opts, WithUser(u.User.Username()))
if password, ok := u.User.Password(); ok {

354
ch/db.go
View File

@@ -24,7 +24,7 @@ type DBStats struct {
}
type DB struct {
cfg *Config
conf *Config
pool *chpool.ConnPool
queryHooks []QueryHook
@@ -35,32 +35,38 @@ type DB struct {
}
func Connect(opts ...Option) *DB {
db := &DB{
cfg: defaultConfig(),
db := newDB(defaultConfig(), opts...)
if db.flags.Has(autoCreateDatabaseFlag) {
db.autoCreateDatabase()
}
for _, opt := range opts {
opt(db)
}
db.pool = newConnPool(db.cfg)
return db
}
func newConnPool(cfg *Config) *chpool.ConnPool {
poolcfg := cfg.Config
poolcfg.Dialer = func(ctx context.Context) (net.Conn, error) {
if cfg.TLSConfig != nil {
func newDB(conf *Config, opts ...Option) *DB {
db := &DB{
conf: conf,
}
for _, opt := range opts {
opt(db)
}
db.pool = newConnPool(db.conf)
return db
}
func newConnPool(conf *Config) *chpool.ConnPool {
poolconf := conf.Config
poolconf.Dialer = func(ctx context.Context) (net.Conn, error) {
if conf.TLSConfig != nil {
return tls.DialWithDialer(
cfg.netDialer(),
cfg.Network,
cfg.Addr,
cfg.TLSConfig,
conf.netDialer(),
"tcp",
conf.Addr,
conf.TLSConfig,
)
}
return cfg.netDialer().DialContext(ctx, cfg.Network, cfg.Addr)
return conf.netDialer().DialContext(ctx, "tcp", conf.Addr)
}
return chpool.New(&poolcfg)
return chpool.New(&poolconf)
}
// Close closes the database client, releasing any open resources.
@@ -72,20 +78,20 @@ func (db *DB) Close() error {
}
func (db *DB) String() string {
return fmt.Sprintf("DB<addr: %s>", db.cfg.Addr)
return fmt.Sprintf("DB<addr: %s>", db.conf.Addr)
}
func (db *DB) Config() *Config {
return db.cfg
return db.conf
}
func (db *DB) WithTimeout(d time.Duration) *DB {
newcfg := *db.cfg
newcfg.ReadTimeout = d
newcfg.WriteTimeout = d
newconf := *db.conf
newconf.ReadTimeout = d
newconf.WriteTimeout = d
clone := db.clone()
clone.cfg = &newcfg
clone.conf = &newconf
return clone
}
@@ -106,6 +112,37 @@ func (db *DB) Stats() DBStats {
}
}
func (db *DB) autoCreateDatabase() {
ctx := context.Background()
switch err := db.Ping(ctx); err := err.(type) {
case nil: // all is good
return
case *Error:
if err.Code != 81 { // 81 - database does not exist
return
}
default:
// ignore the error
return
}
conf := db.conf.clone()
conf.Database = ""
query := "CREATE DATABASE IF NOT EXISTS ?"
if conf.Cluster != "" {
query += " ON CLUSTER ?"
}
tmp := newDB(conf)
defer tmp.Close()
if _, err := tmp.Exec(query, Name(db.conf.Database), Name(db.conf.Cluster)); err != nil {
internal.Logger.Printf("create database %q failed: %s", db.conf.Database, err)
}
}
func (db *DB) getConn(ctx context.Context) (*chpool.Conn, error) {
cn, err := db.pool.Get(ctx)
if err != nil {
@@ -157,60 +194,24 @@ func (db *DB) _withConn(ctx context.Context, fn func(*chpool.Conn) error) error
return err
}
var done chan struct{}
if ctxDone := ctx.Done(); ctxDone != nil {
done = make(chan struct{})
go func() {
select {
case <-done:
// fn has finished, skip cancel
case <-ctxDone:
db.cancelConn(ctx, cn)
// Signal end of conn use.
done <- struct{}{}
}
}()
}
var fnErr error
defer func() {
if done != nil {
select {
case <-done: // wait for cancel to finish request
case done <- struct{}{}: // signal fn finish, skip cancel goroutine
}
}
db.releaseConn(cn, err)
db.releaseConn(cn, fnErr)
}()
// err is used in releaseConn above
err = fn(cn)
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
db.cancelConn(ctx, cn)
}
return err
}
func (db *DB) cancelConn(ctx context.Context, cn *chpool.Conn) {
if err := cn.WithWriter(ctx, db.cfg.WriteTimeout, func(wr *chproto.Writer) {
writeCancel(wr)
}); err != nil {
internal.Logger.Printf("writeCancel failed: %s", err)
}
_ = cn.Close()
fnErr = fn(cn)
return fnErr
}
func (db *DB) Ping(ctx context.Context) error {
return db.withConn(ctx, func(cn *chpool.Conn) error {
if err := cn.WithWriter(ctx, db.cfg.WriteTimeout, func(wr *chproto.Writer) {
if err := cn.WithWriter(ctx, db.conf.WriteTimeout, func(wr *chproto.Writer) {
writePing(wr)
}); err != nil {
return err
}
return cn.WithReader(ctx, db.cfg.ReadTimeout, func(rd *chproto.Reader) error {
return cn.WithReader(ctx, db.conf.ReadTimeout, func(rd *chproto.Reader) error {
return readPong(rd)
})
})
@@ -225,11 +226,47 @@ func (db *DB) ExecContext(
) (sql.Result, error) {
query = db.FormatQuery(query, args...)
ctx, evt := db.beforeQuery(ctx, nil, query, args, nil)
res, err := db.query(ctx, nil, query)
res, err := db.exec(ctx, query)
db.afterQuery(ctx, evt, res, err)
return res, err
}
func (db *DB) exec(ctx context.Context, query string) (*result, error) {
var res *result
var lastErr error
for attempt := 0; attempt <= db.conf.MaxRetries; attempt++ {
if attempt > 0 {
if err := internal.Sleep(ctx, db.retryBackoff()); err != nil {
return nil, err
}
}
res, lastErr = db._exec(ctx, query)
if !db.shouldRetry(lastErr) {
break
}
}
return res, lastErr
}
func (db *DB) _exec(ctx context.Context, query string) (*result, error) {
var res *result
err := db.withConn(ctx, func(cn *chpool.Conn) error {
if err := cn.WithWriter(ctx, db.conf.WriteTimeout, func(wr *chproto.Writer) {
db.writeQuery(ctx, cn, wr, query)
db.writeBlock(ctx, wr, nil)
}); err != nil {
return err
}
return cn.WithReader(ctx, db.conf.ReadTimeout, func(rd *chproto.Reader) error {
var err error
res, err = db.readDataBlocks(cn, rd)
return err
})
})
return res, err
}
func (db *DB) Query(query string, args ...any) (*Rows, error) {
return db.QueryContext(context.Background(), query, args...)
}
@@ -237,16 +274,16 @@ func (db *DB) Query(query string, args ...any) (*Rows, error) {
func (db *DB) QueryContext(
ctx context.Context, query string, args ...any,
) (*Rows, error) {
rows := newRows()
query = db.FormatQuery(query, args...)
ctx, evt := db.beforeQuery(ctx, nil, query, args, nil)
res, err := db.query(ctx, rows, query)
db.afterQuery(ctx, evt, res, err)
blocks, err := db.query(ctx, query)
db.afterQuery(ctx, evt, nil, err)
if err != nil {
return nil, err
}
return rows, nil
return newRows(ctx, blocks), nil
}
func (db *DB) QueryRow(query string, args ...any) *Row {
@@ -258,50 +295,41 @@ func (db *DB) QueryRowContext(ctx context.Context, query string, args ...any) *R
return &Row{rows: rows, err: err}
}
func (db *DB) query(ctx context.Context, model Model, query string) (*result, error) {
var res *result
func (db *DB) query(ctx context.Context, query string) (*blockIter, error) {
var blocks *blockIter
var lastErr error
for attempt := 0; attempt <= db.cfg.MaxRetries; attempt++ {
for attempt := 0; attempt <= db.conf.MaxRetries; attempt++ {
if attempt > 0 {
lastErr = internal.Sleep(ctx, db.retryBackoff(attempt-1))
if lastErr != nil {
break
if err := internal.Sleep(ctx, db.retryBackoff()); err != nil {
return nil, err
}
}
res, lastErr = db._query(ctx, model, query)
blocks, lastErr = db._query(ctx, query)
if !db.shouldRetry(lastErr) {
break
}
}
if lastErr == nil {
if model, ok := model.(AfterScanRowHook); ok {
if err := model.AfterScanRow(ctx); err != nil {
lastErr = err
}
}
}
return res, lastErr
return blocks, lastErr
}
func (db *DB) _query(ctx context.Context, model Model, query string) (*result, error) {
var res *result
err := db.withConn(ctx, func(cn *chpool.Conn) error {
if err := cn.WithWriter(ctx, db.cfg.WriteTimeout, func(wr *chproto.Writer) {
db.writeQuery(wr, query)
writeBlock(ctx, wr, nil)
}); err != nil {
return err
}
return cn.WithReader(ctx, db.cfg.ReadTimeout, func(rd *chproto.Reader) error {
var err error
res, err = readDataBlocks(rd, model)
return err
})
})
return res, err
func (db *DB) _query(ctx context.Context, query string) (*blockIter, error) {
cn, err := db.getConn(ctx)
if err != nil {
return nil, err
}
if err := cn.WithWriter(ctx, db.conf.WriteTimeout, func(wr *chproto.Writer) {
db.writeQuery(ctx, cn, wr, query)
db.writeBlock(ctx, wr, nil)
}); err != nil {
db.releaseConn(cn, err)
return nil, err
}
return newBlockIter(db, cn), nil
}
func (db *DB) insert(
@@ -312,11 +340,10 @@ func (db *DB) insert(
var res *result
var lastErr error
for attempt := 0; attempt <= db.cfg.MaxRetries; attempt++ {
for attempt := 0; attempt <= db.conf.MaxRetries; attempt++ {
if attempt > 0 {
lastErr = internal.Sleep(ctx, db.retryBackoff(attempt-1))
if lastErr != nil {
break
if err := internal.Sleep(ctx, db.retryBackoff()); err != nil {
return nil, err
}
}
@@ -334,30 +361,30 @@ func (db *DB) _insert(
) (*result, error) {
var res *result
err := db.withConn(ctx, func(cn *chpool.Conn) error {
if err := cn.WithWriter(ctx, db.cfg.WriteTimeout, func(wr *chproto.Writer) {
db.writeQuery(wr, query)
writeBlock(ctx, wr, nil)
if err := cn.WithWriter(ctx, db.conf.WriteTimeout, func(wr *chproto.Writer) {
db.writeQuery(ctx, cn, wr, query)
db.writeBlock(ctx, wr, nil)
}); err != nil {
return err
}
if err := cn.WithReader(ctx, db.cfg.ReadTimeout, func(rd *chproto.Reader) error {
_, err := readSampleBlock(rd)
if err := cn.WithReader(ctx, db.conf.ReadTimeout, func(rd *chproto.Reader) error {
_, err := db.readSampleBlock(rd)
return err
}); err != nil {
return err
}
if err := cn.WithWriter(ctx, db.cfg.WriteTimeout, func(wr *chproto.Writer) {
writeBlock(ctx, wr, block)
writeBlock(ctx, wr, nil)
if err := cn.WithWriter(ctx, db.conf.WriteTimeout, func(wr *chproto.Writer) {
db.writeBlock(ctx, wr, block)
db.writeBlock(ctx, wr, nil)
}); err != nil {
return err
}
return cn.WithReader(ctx, db.cfg.ReadTimeout, func(rd *chproto.Reader) error {
return cn.WithReader(ctx, db.conf.ReadTimeout, func(rd *chproto.Reader) error {
var err error
res, err = readPacket(rd)
res, err = readPacket(cn, rd)
if err != nil {
return err
}
@@ -372,6 +399,10 @@ func (db *DB) NewSelect() *SelectQuery {
return NewSelectQuery(db)
}
func (db *DB) NewRaw(query string, args ...any) *RawQuery {
return NewRawQuery(db, query, args...)
}
func (db *DB) NewInsert() *InsertQuery {
return NewInsertQuery(db)
}
@@ -388,6 +419,14 @@ func (db *DB) NewTruncateTable() *TruncateTableQuery {
return NewTruncateTableQuery(db)
}
func (db *DB) NewCreateView() *CreateViewQuery {
return NewCreateViewQuery(db)
}
func (db *DB) NewDropView() *DropViewQuery {
return NewDropViewQuery(db)
}
func (db *DB) ResetModel(ctx context.Context, models ...any) error {
for _, model := range models {
if _, err := db.NewDropTable().Model(model).IfExists().Exec(ctx); err != nil {
@@ -412,22 +451,28 @@ func (db *DB) WithFormatter(fmter chschema.Formatter) *DB {
func (db *DB) shouldRetry(err error) bool {
switch err {
case driver.ErrBadConn:
return true
case nil, context.Canceled, context.DeadlineExceeded:
return false
case driver.ErrBadConn:
return true
}
if err, ok := err.(*Error); ok {
// https://github.com/ClickHouse/ClickHouse/blob/master/src/Common/ErrorCodes.cpp
const (
timeoutExceeded = 159
tooSlow = 160
tooManySimultaneousQueries = 202
memoryLimitExceeded = 241
cannotDecompress = 271
)
switch err.Code {
case timeoutExceeded, tooManySimultaneousQueries, memoryLimitExceeded:
case timeoutExceeded,
tooSlow,
tooManySimultaneousQueries,
memoryLimitExceeded,
cannotDecompress:
return true
}
}
@@ -435,9 +480,8 @@ func (db *DB) shouldRetry(err error) bool {
return false
}
func (db *DB) retryBackoff(attempt int) time.Duration {
return internal.RetryBackoff(
attempt, db.cfg.MinRetryBackoff, db.cfg.MaxRetryBackoff)
func (db *DB) retryBackoff() time.Duration {
return internal.RetryBackoff(db.conf.MinRetryBackoff, db.conf.MaxRetryBackoff)
}
func (db *DB) FormatQuery(query string, args ...any) string {
@@ -454,21 +498,37 @@ func (db *DB) makeQueryBytes() []byte {
// Rows is the result of a query. Its cursor starts before the first row of the result set.
// Use Next to advance from row to row.
type Rows struct {
blocks []*chschema.Block
ctx context.Context
blocks *blockIter
block *chschema.Block
block *chschema.Block
blockIndex int
rowIndex int
rowIndex int
hasNext bool
closed bool
}
func newRows() *Rows {
return new(Rows)
func newRows(ctx context.Context, blocks *blockIter) *Rows {
return &Rows{
ctx: ctx,
blocks: blocks,
block: new(chschema.Block),
}
}
func (rs *Rows) Close() error {
if !rs.closed {
for rs.blocks.Next(rs.ctx, rs.block) {
}
rs.close()
}
return nil
}
func (rs *Rows) close() {
rs.closed = true
_ = rs.blocks.Close()
}
func (rs *Rows) ColumnTypes() ([]*sql.ColumnType, error) {
return nil, errors.New("not implemented")
}
@@ -478,25 +538,25 @@ func (rs *Rows) Columns() ([]string, error) {
}
func (rs *Rows) Err() error {
return nil
return rs.blocks.Err()
}
func (rs *Rows) Next() bool {
if rs.block != nil && rs.rowIndex < rs.block.NumRow {
rs.rowIndex++
return true
if rs.closed {
return false
}
for rs.blockIndex < len(rs.blocks) {
rs.block = rs.blocks[rs.blockIndex]
rs.blockIndex++
if rs.block.NumRow > 0 {
rs.rowIndex = 1
return true
for rs.rowIndex >= rs.block.NumRow {
if !rs.blocks.Next(rs.ctx, rs.block) {
rs.close()
return false
}
rs.rowIndex = 0
}
return false
rs.hasNext = true
rs.rowIndex++
return true
}
func (rs *Rows) NextResultSet() bool {
@@ -504,9 +564,14 @@ func (rs *Rows) NextResultSet() bool {
}
func (rs *Rows) Scan(dest ...any) error {
if rs.block == nil {
if rs.closed {
return rs.Err()
}
if !rs.hasNext {
return errors.New("ch: Scan called without calling Next")
}
rs.hasNext = false
if rs.block.NumColumn != len(dest) {
return fmt.Errorf("ch: got %d columns, but Scan has %d values",
@@ -522,11 +587,6 @@ func (rs *Rows) Scan(dest ...any) error {
return nil
}
func (rs *Rows) ScanBlock(block *chschema.Block) error {
rs.blocks = append(rs.blocks, block)
return nil
}
type Row struct {
rows *Rows
err error

View File

@@ -3,8 +3,12 @@ package ch_test
import (
"context"
"database/sql"
"encoding/hex"
"fmt"
"os"
"reflect"
"runtime"
"strings"
"testing"
"time"
@@ -20,7 +24,7 @@ func chDB(opts ...ch.Option) *ch.DB {
dsn = "clickhouse://localhost:9000/test?sslmode=disable"
}
opts = append(opts, ch.WithDSN(dsn))
opts = append(opts, ch.WithDSN(dsn), ch.WithAutoCreateDatabase(true))
db := ch.Connect(opts...)
db.AddQueryHook(chdebug.NewQueryHook(
chdebug.WithEnabled(false),
@@ -29,6 +33,30 @@ func chDB(opts ...ch.Option) *ch.DB {
return db
}
func TestAutoCreateDatabase(t *testing.T) {
ctx := context.Background()
dbName := "auto_create_database"
{
db := ch.Connect()
defer db.Close()
_, err := db.Exec("DROP DATABASE IF EXISTS ?", ch.Name(dbName))
require.NoError(t, err)
}
{
db := ch.Connect(
ch.WithDatabase(dbName),
ch.WithAutoCreateDatabase(true),
)
defer db.Close()
err := db.Ping(ctx)
require.NoError(t, err)
}
}
func TestCHError(t *testing.T) {
ctx := context.Background()
@@ -57,13 +85,6 @@ func TestCHTimeout(t *testing.T) {
ctx, "SELECT sleepEachRow(0.01) from numbers(10000) settings max_block_size=10")
require.Error(t, err)
require.Contains(t, err.Error(), "i/o timeout")
require.Eventually(t, func() bool {
var num int
err := db.NewSelect().ColumnExpr("count()").TableExpr("system.processes").Scan(ctx, &num)
require.NoError(t, err)
return num == 1
}, time.Second, 100*time.Millisecond)
}
func TestDSNSetting(t *testing.T) {
@@ -139,7 +160,7 @@ func TestPlaceholder(t *testing.T) {
params := struct {
A int
B int
Alias ch.Ident
Alias ch.Name
}{
A: 1,
B: 2,
@@ -244,6 +265,86 @@ func TestScanArrayUint8(t *testing.T) {
require.Equal(t, map[string]any{"ns": []uint8{0, 1, 2}}, m)
}
func TestDateTime64(t *testing.T) {
type Model struct {
Time time.Time `ch:"type:DateTime64(9)"`
}
ctx := context.Background()
db := chDB()
defer db.Close()
err := db.ResetModel(ctx, (*Model)(nil))
require.NoError(t, err)
in := &Model{Time: time.Unix(0, 12345678912345)}
_, err = db.NewInsert().Model(in).Exec(ctx)
require.NoError(t, err)
out := new(Model)
err = db.NewSelect().Model(out).Scan(ctx)
require.NoError(t, err)
require.Equal(t, in.Time.UnixNano(), out.Time.UnixNano())
}
func TestInvalidType(t *testing.T) {
t.Skip()
ctx := context.Background()
db := chDB()
defer db.Close()
var dest struct {
Numbers []float32
}
err := db.NewSelect().
ColumnExpr("groupArray(number) AS numbers").
TableExpr("numbers(10)").
Scan(ctx, &dest)
require.NoError(t, err)
require.Equal(t, []float64{}, dest.Numbers)
}
func TestClickhouse(t *testing.T) {
ctx := context.Background()
db := chDB()
defer db.Close()
tests := []func(ctx context.Context, t *testing.T, db *ch.DB){
testWhereBytes,
}
for _, fn := range tests {
t.Run(funcName(fn), func(t *testing.T) {
fn(ctx, t, db)
})
}
}
func testWhereBytes(ctx context.Context, t *testing.T, db *ch.DB) {
type Data struct {
Bytes []byte
}
err := db.ResetModel(ctx, (*Data)(nil))
require.NoError(t, err)
src, _ := hex.DecodeString("5C00CC")
data := &Data{Bytes: src}
_, err = db.NewInsert().Model(data).Exec(context.Background())
require.NoError(t, err)
got := new(Data)
err = db.NewSelect().Model(got).
Where("bytes = ?", data.Bytes).
Scan(ctx)
require.NoError(t, err)
require.Equal(t, data.Bytes, got.Bytes)
}
type Event struct {
ch.CHModel `ch:"goch_events,partition:toYYYYMM(created_at)"`
@@ -282,12 +383,13 @@ func TestORM(t *testing.T) {
testORMSlice,
testORMColumnarStruct,
testORMInvalidEnumValue,
testORMInsertSelect,
}
for _, fn := range tests {
_, err := db.NewTruncateTable().Model((*Event)(nil)).Exec(ctx)
require.NoError(t, err)
t.Run("", func(t *testing.T) {
t.Run(funcName(fn), func(t *testing.T) {
fn(t, db)
})
}
@@ -470,6 +572,37 @@ func testORMInvalidEnumValue(t *testing.T, db *ch.DB) {
require.Equal(t, "invalid", dest.Kind)
}
func testORMInsertSelect(t *testing.T, db *ch.DB) {
ctx := context.Background()
for i := 0; i < 100; i++ {
src := &Event{
ID: 1,
Name: "hello",
Count: 42,
Keys: []string{"foo", "bar"},
Values: [][]string{{}, {"hello", "world"}},
Kind: "hello",
CreatedAt: time.Now(),
}
_, err := db.NewInsert().Model(src).Exec(ctx)
require.NoError(t, err)
}
var dest []Event
err := db.NewSelect().Model(&dest).Scan(ctx)
require.NoError(t, err)
require.Equal(t, 100, len(dest))
}
func funcName(x interface{}) string {
s := runtime.FuncForPC(reflect.ValueOf(x).Pointer()).Name()
if i := strings.LastIndexByte(s, '.'); i >= 0 {
return s[i+1:]
}
return s
}
func strptr(s string) *string {
return &s
}

View File

@@ -0,0 +1,238 @@
package chschema
import (
"time"
"reflect"
"github.com/uptrace/go-clickhouse/ch/chproto"
)
{{- range . }}
{{ if not .IsCustom }}
type {{ .Name }}Column struct {
{{ if gt .Size 0 }}Numeric{{ end }}ColumnOf[{{ .GoType }}]
}
var _ Columnar = (*{{ .Name }}Column)(nil)
func New{{ .Name }}Column() Columnar {
return new({{ .Name }}Column)
}
var _{{ .Name }}Type = reflect.TypeOf((*{{ .GoType }})(nil)).Elem()
func (c *{{ .Name }}Column) Type() reflect.Type {
return _{{ .Name }}Type
}
{{ if .GoReflect }}
func (c *{{ .Name }}Column) AppendValue(v reflect.Value) {
c.Column = append(c.Column, {{ .GoType }}(v.{{ .GoReflect }}()))
}
{{ end }}
{{ if eq .Size 0 }}
func (c *{{ .Name }}Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.{{ .Name }}()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *{{ .Name }}Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.{{ .Name }}(n)
}
return nil
}
{{ end }}
{{ end }}
//------------------------------------------------------------------------------
type Array{{ .Name }}Column struct {
ColumnOf[[]{{ .GoType }}]
elem {{ .Name }}Column
}
var (
_ Columnar = (*Array{{ .Name }}Column)(nil)
_ ArrayColumnar = (*Array{{ .Name }}Column)(nil)
)
func NewArray{{ .Name }}Column() Columnar {
return new(Array{{ .Name }}Column)
}
func (c *Array{{ .Name }}Column) Init(chType string) error {
return c.elem.Init(chArrayElemType(chType))
}
func (c *Array{{ .Name }}Column) Type() reflect.Type {
return reflect.TypeOf((*[]{{ .GoType }})(nil)).Elem()
}
func (c *Array{{ .Name }}Column) ReadFrom(rd *chproto.Reader, numRow int) error {
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
offsets, err := c.readOffsets(rd, numRow)
if err != nil {
return err
}
if err := c.elem.ReadFrom(rd, offsets[len(offsets)-1]); err != nil {
return err
}
var prev int
for i, offset := range offsets {
c.Column[i] = c.elem.Column[prev:offset]
prev = offset
}
return nil
}
func (c *Array{{ .Name }}Column) readOffsets(rd *chproto.Reader, numRow int) ([]int, error) {
offsets := make([]int, numRow)
for i := range offsets {
offset, err := rd.UInt64()
if err != nil {
return nil, err
}
offsets[i] = int(offset)
}
return offsets, nil
}
func (c *Array{{ .Name }}Column) WriteTo(wr *chproto.Writer) error {
_ = c.WriteOffset(wr, 0)
return c.WriteData(wr)
}
func (c *Array{{ .Name }}Column) WriteOffset(wr *chproto.Writer, offset int) int {
for _, el := range c.Column {
offset += len(el)
wr.UInt64(uint64(offset))
}
return offset
}
func (c *Array{{ .Name }}Column) WriteData(wr *chproto.Writer) error {
for _, ss := range c.Column {
c.elem.Column = ss
if err := c.elem.WriteTo(wr); err != nil {
return err
}
}
return nil
}
//------------------------------------------------------------------------------
type ArrayArray{{ .Name }}Column struct {
ColumnOf[[][]{{ .GoType }}]
elem Array{{ .Name }}Column
}
var (
_ Columnar = (*ArrayArray{{ .Name }}Column)(nil)
_ ArrayColumnar = (*ArrayArray{{ .Name }}Column)(nil)
)
func NewArrayArray{{ .Name }}Column() Columnar {
return new(ArrayArray{{ .Name }}Column)
}
func (c *ArrayArray{{ .Name }}Column) Init(chType string) error {
return c.elem.Init(chArrayElemType(chArrayElemType(chType)))
}
func (c *ArrayArray{{ .Name }}Column) Type() reflect.Type {
return reflect.TypeOf((*[][]{{ .GoType }})(nil)).Elem()
}
func (c *ArrayArray{{ .Name }}Column) ReadFrom(rd *chproto.Reader, numRow int) error {
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
offsets, err := c.readOffsets(rd, numRow)
if err != nil {
return err
}
if err := c.elem.ReadFrom(rd, offsets[len(offsets)-1]); err != nil {
return err
}
var prev int
for i, offset := range offsets {
c.Column[i] = c.elem.Column[prev:offset]
prev = offset
}
return nil
}
func (c *ArrayArray{{ .Name }}Column) readOffsets(rd *chproto.Reader, numRow int) ([]int, error) {
offsets := make([]int, numRow)
for i := range offsets {
offset, err := rd.UInt64()
if err != nil {
return nil, err
}
offsets[i] = int(offset)
}
return offsets, nil
}
func (c *ArrayArray{{ .Name }}Column) WriteTo(wr *chproto.Writer) error {
_ = c.WriteOffset(wr, 0)
return c.WriteData(wr)
}
func (c *ArrayArray{{ .Name }}Column) WriteOffset(wr *chproto.Writer, offset int) int {
for _, el := range c.Column {
offset += len(el)
wr.UInt64(uint64(offset))
}
offset = 0
for _, elem := range c.Column {
c.elem.Column = elem
offset = c.elem.WriteOffset(wr, offset)
}
return offset
}
func (c *ArrayArray{{ .Name }}Column) WriteData(wr *chproto.Writer) error {
for _, ss := range c.Column {
c.elem.Column = ss
if err := c.elem.WriteData(wr); err != nil {
return err
}
}
return nil
}
{{- end }}

View File

@@ -0,0 +1,34 @@
//go:build !amd64 && !arm64
package chschema
import (
"github.com/uptrace/go-clickhouse/ch/chproto"
)
{{- range . }}
{{ if eq .Size 0 }} {{ continue }} {{ end }}
func (c *{{ .Name }}Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.AllocForReading(numRow)
for i := range c.Column {
n, err := rd.{{ .Name }}()
if err != nil {
return err
}
c.Column[i] = n
}
return nil
}
func (c *{{ .Name }}Column) WriteTo(wr *chproto.Writer) error {
for _, n := range c.Column {
wr.{{ .Name }}(n)
}
return nil
}
{{- end }}

View File

@@ -0,0 +1,51 @@
//go:build amd64 || arm64
package chschema
import (
"io"
"reflect"
"unsafe"
"github.com/uptrace/go-clickhouse/ch/chproto"
)
{{- range . }}
{{ if eq .Size 0 }} {{ continue }} {{ end }}
func (c *{{ .Name }}Column) ReadFrom(rd *chproto.Reader, numRow int) error {
const size = {{ .Size }} / 8
if numRow == 0 {
return nil
}
c.AllocForReading(numRow)
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
dest := *(*[]byte)(unsafe.Pointer(&slice))
_, err := io.ReadFull(rd, dest)
return err
}
func (c *{{ .Name }}Column) WriteTo(wr *chproto.Writer) error {
const size = {{ .Size }} / 8
if len(c.Column) == 0 {
return nil
}
slice := *(*reflect.SliceHeader)(unsafe.Pointer(&c.Column))
slice.Len *= size
slice.Cap *= size
src := *(*[]byte)(unsafe.Pointer(&slice))
wr.Write(src)
return nil
}
{{- end }}

View File

@@ -79,24 +79,10 @@ func MakeSliceNextElemFunc(v reflect.Value) func() reflect.Value {
}
}
func RetryBackoff(retry int, minBackoff, maxBackoff time.Duration) time.Duration {
if retry < 0 {
panic("not reached")
}
if minBackoff == 0 {
return 0
}
d := minBackoff << uint(retry)
if d < minBackoff {
func RetryBackoff(minBackoff, maxBackoff time.Duration) time.Duration {
backoff := minBackoff + time.Duration(rand.Int63n(int64(maxBackoff)))
if backoff > maxBackoff {
return maxBackoff
}
d = minBackoff + time.Duration(rand.Int63n(int64(d)))
if d > maxBackoff || d < minBackoff {
d = maxBackoff
}
return d
return backoff
}

View File

@@ -10,30 +10,125 @@ import (
"github.com/uptrace/go-clickhouse/ch/chpool"
"github.com/uptrace/go-clickhouse/ch/chproto"
"github.com/uptrace/go-clickhouse/ch/chschema"
"go.opentelemetry.io/otel/trace"
)
const (
clientName = "go-clickhouse"
chVersionMajor = 19
chVersionMinor = 17
chVersionPatch = 5
chRevision = 54428
chVersionMajor = 1
chVersionMinor = 1
chProtoVersion = chproto.DBMS_TCP_PROTOCOL_VERSION
)
var (
osUser = os.Getenv("USER")
hostname, _ = os.Hostname()
)
type blockIter struct {
db *DB
cn *chpool.Conn
stickyErr error
}
func newBlockIter(db *DB, cn *chpool.Conn) *blockIter {
return &blockIter{
db: db,
cn: cn,
}
}
func (it *blockIter) Close() error {
if it.cn != nil {
it.close()
}
return nil
}
func (it *blockIter) close() {
it.db.releaseConn(it.cn, it.stickyErr)
it.cn = nil
}
func (it *blockIter) Err() error {
return it.stickyErr
}
func (it *blockIter) Next(ctx context.Context, block *chschema.Block) bool {
if it.cn == nil {
return false
}
ok, err := it.read(ctx, block)
if err != nil {
it.stickyErr = err
it.close()
return false
}
if !ok {
it.close()
return false
}
return true
}
func (it *blockIter) read(ctx context.Context, block *chschema.Block) (bool, error) {
rd := it.cn.Reader(ctx, it.db.conf.ReadTimeout)
for {
packet, err := rd.Uvarint()
if err != nil {
return false, err
}
switch packet {
case chproto.ServerData:
if err := it.db.readBlock(rd, block, true); err != nil {
return false, err
}
return true, nil
case chproto.ServerException:
return false, readException(rd)
case chproto.ServerProgress:
if err := readProgress(it.cn, rd); err != nil {
return false, err
}
case chproto.ServerProfileInfo:
if err := readProfileInfo(rd); err != nil {
return false, err
}
case chproto.ServerTableColumns:
if err := readServerTableColumns(rd); err != nil {
return false, err
}
case chproto.ServerProfileEvents:
block := new(chschema.Block)
if err := it.db.readBlock(rd, block, false); err != nil {
return false, err
}
case chproto.ServerEndOfStream:
return false, nil
default:
return false, fmt.Errorf("ch: blockIter.Next: unexpected packet: %d", packet)
}
}
}
func (db *DB) hello(ctx context.Context, cn *chpool.Conn) error {
err := cn.WithWriter(ctx, db.cfg.WriteTimeout, func(wr *chproto.Writer) {
wr.Uvarint(chproto.ClientHello)
err := cn.WithWriter(ctx, db.conf.WriteTimeout, func(wr *chproto.Writer) {
wr.WriteByte(chproto.ClientHello)
writeClientInfo(wr)
wr.String(db.cfg.Database)
wr.String(db.cfg.User)
wr.String(db.cfg.Password)
wr.String(db.conf.Database)
wr.String(db.conf.User)
wr.String(db.conf.Password)
})
if err != nil {
return err
}
return cn.WithReader(ctx, db.cfg.ReadTimeout, func(rd *chproto.Reader) error {
return cn.WithReader(ctx, db.conf.ReadTimeout, func(rd *chproto.Reader) error {
packet, err := rd.Uvarint()
if err != nil {
return err
@@ -53,7 +148,7 @@ func writeClientInfo(wr *chproto.Writer) {
wr.String(clientName)
wr.Uvarint(chVersionMajor)
wr.Uvarint(chVersionMinor)
wr.Uvarint(chRevision)
wr.Uvarint(chProtoVersion)
}
func readException(rd *chproto.Reader) (err error) {
@@ -109,7 +204,7 @@ func readProfileInfo(rd *chproto.Reader) error {
return nil
}
func readProgress(rd *chproto.Reader) error {
func readProgress(cn *chpool.Conn, rd *chproto.Reader) error {
if _, err := rd.Uvarint(); err != nil {
return err
}
@@ -119,17 +214,19 @@ func readProgress(rd *chproto.Reader) error {
if _, err := rd.Uvarint(); err != nil {
return err
}
if _, err := rd.Uvarint(); err != nil {
return err
}
if _, err := rd.Uvarint(); err != nil {
return err
if cn.ServerInfo.Revision >= chproto.DBMS_MIN_REVISION_WITH_CLIENT_WRITE_INFO {
if _, err := rd.Uvarint(); err != nil {
return err
}
if _, err := rd.Uvarint(); err != nil {
return err
}
}
return nil
}
func writePing(wr *chproto.Writer) {
wr.Uvarint(chproto.ClientPing)
wr.WriteByte(chproto.ClientPing)
}
func readPong(rd *chproto.Reader) error {
@@ -152,38 +249,81 @@ func readPong(rd *chproto.Reader) error {
}
}
var hostname string
func (db *DB) writeQuery(wr *chproto.Writer, query string) {
if hostname == "" {
hostname, _ = os.Hostname()
}
wr.Uvarint(chproto.ClientQuery)
wr.String("")
func (db *DB) writeQuery(ctx context.Context, cn *chpool.Conn, wr *chproto.Writer, query string) {
wr.WriteByte(chproto.ClientQuery)
wr.String("") // query id
// TODO: use QuerySecondary - https://github.com/ClickHouse/ClickHouse/blob/master/dbms/src/Client/Connection.cpp#L388-L404
wr.Uvarint(chproto.QueryInitial)
wr.WriteByte(chproto.QueryInitial)
wr.String("") // initial user
wr.String("") // initial query id
wr.String("[::ffff:127.0.0.1]:0")
wr.Uvarint(1) // iface type TCP
wr.String(hostname)
wr.String(cn.LocalAddr().String())
if cn.ServerInfo.Revision >= chproto.DBMS_MIN_PROTOCOL_VERSION_WITH_INITIAL_QUERY_START_TIME {
wr.Int64(0) // initial_query_start_time_microseconds
}
wr.WriteByte(1) // interface [tcp - 1, http - 2]
wr.String(osUser)
wr.String(hostname)
writeClientInfo(wr)
wr.String("") // quota key
wr.Uvarint(chVersionPatch) // client version patch
if cn.ServerInfo.Revision >= chproto.DBMS_MIN_REVISION_WITH_QUOTA_KEY_IN_CLIENT_INFO {
wr.String("") // quota key
}
if cn.ServerInfo.Revision >= chproto.DBMS_MIN_PROTOCOL_VERSION_WITH_DISTRIBUTED_DEPTH {
wr.Uvarint(0)
}
if cn.ServerInfo.Revision >= chproto.DBMS_MIN_REVISION_WITH_VERSION_PATCH {
wr.Uvarint(0) // client version patch
}
if cn.ServerInfo.Revision >= chproto.DBMS_MIN_REVISION_WITH_OPENTELEMETRY {
if spanCtx := trace.SpanContextFromContext(ctx); spanCtx.IsValid() {
wr.WriteByte(1)
{
v := spanCtx.TraceID()
wr.UUID(v[:])
}
{
v := spanCtx.SpanID()
wr.Write(reverseBytes(v[:]))
}
wr.String(spanCtx.TraceState().String())
wr.WriteByte(byte(spanCtx.TraceFlags()))
} else {
wr.WriteByte(0)
}
}
if cn.ServerInfo.Revision >= chproto.DBMS_MIN_REVISION_WITH_PARALLEL_REPLICAS {
wr.Uvarint(0) // collaborate_with_initiator
wr.Uvarint(0) // count_participating_replicas
wr.Uvarint(0) // number_of_current_replica
}
db.writeSettings(wr)
db.writeSettings(cn, wr)
wr.Uvarint(2)
wr.Uvarint(chproto.CompressionEnabled)
if cn.ServerInfo.Revision >= chproto.DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET {
wr.String("")
}
wr.Uvarint(2) // state complete
wr.Bool(db.conf.Compression)
wr.String(query)
}
func (db *DB) writeSettings(wr *chproto.Writer) {
for key, value := range db.cfg.QuerySettings {
func reverseBytes(b []byte) []byte {
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
return b
}
func (db *DB) writeSettings(cn *chpool.Conn, wr *chproto.Writer) {
for key, value := range db.conf.QuerySettings {
wr.String(key)
if cn.ServerInfo.Revision > chproto.DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS {
wr.Bool(true) // is_important
wr.String(fmt.Sprint(value))
continue
}
switch value := value.(type) {
case string:
wr.String(value)
@@ -198,21 +338,22 @@ func (db *DB) writeSettings(wr *chproto.Writer) {
default:
panic(fmt.Errorf("%s setting has unsupported type: %T", key, value))
}
}
wr.String("")
wr.String("") // end of settings
}
var emptyBlock chschema.Block
func writeBlock(ctx context.Context, wr *chproto.Writer, block *chschema.Block) {
func (db *DB) writeBlock(ctx context.Context, wr *chproto.Writer, block *chschema.Block) {
if block == nil {
block = &emptyBlock
}
wr.Uvarint(chproto.ClientData)
wr.WriteByte(chproto.ClientData)
wr.String("")
wr.WithCompression(func() error {
wr.WithCompression(db.conf.Compression, func() error {
writeBlockInfo(wr)
return block.WriteTo(wr)
})
@@ -228,7 +369,7 @@ func writeBlockInfo(wr *chproto.Writer) {
wr.Uvarint(0)
}
func readSampleBlock(rd *chproto.Reader) (*chschema.Block, error) {
func (db *DB) readSampleBlock(rd *chproto.Reader) (*chschema.Block, error) {
for {
packet, err := rd.Uvarint()
if err != nil {
@@ -238,7 +379,7 @@ func readSampleBlock(rd *chproto.Reader) (*chschema.Block, error) {
switch packet {
case chproto.ServerData:
block := new(chschema.Block)
if err := readBlock(rd, block); err != nil {
if err := db.readBlock(rd, block, true); err != nil {
return nil, err
}
return block, nil
@@ -254,8 +395,9 @@ func readSampleBlock(rd *chproto.Reader) (*chschema.Block, error) {
}
}
func readDataBlocks(rd *chproto.Reader, model Model) (*result, error) {
func (db *DB) readDataBlocks(cn *chpool.Conn, rd *chproto.Reader) (*result, error) {
var res *result
block := new(chschema.Block)
for {
packet, err := rd.Uvarint()
if err != nil {
@@ -263,13 +405,8 @@ func readDataBlocks(rd *chproto.Reader, model Model) (*result, error) {
}
switch packet {
case chproto.ServerData:
block := new(chschema.Block)
if model, ok := model.(TableModel); ok {
block.Table = model.Table()
}
if err := readBlock(rd, block); err != nil {
case chproto.ServerData, chproto.ServerTotals, chproto.ServerExtremes:
if err := db.readBlock(rd, block, true); err != nil {
return nil, err
}
@@ -277,16 +414,10 @@ func readDataBlocks(rd *chproto.Reader, model Model) (*result, error) {
res = new(result)
}
res.affected += block.NumRow
if model != nil {
if err := model.ScanBlock(block); err != nil {
return nil, err
}
}
case chproto.ServerException:
return nil, readException(rd)
case chproto.ServerProgress:
if err := readProgress(rd); err != nil {
if err := readProgress(cn, rd); err != nil {
return nil, err
}
case chproto.ServerProfileInfo:
@@ -297,6 +428,11 @@ func readDataBlocks(rd *chproto.Reader, model Model) (*result, error) {
if err := readServerTableColumns(rd); err != nil {
return nil, err
}
case chproto.ServerProfileEvents:
block := new(chschema.Block)
if err := db.readBlock(rd, block, false); err != nil {
return nil, err
}
case chproto.ServerEndOfStream:
return res, nil
default:
@@ -305,7 +441,7 @@ func readDataBlocks(rd *chproto.Reader, model Model) (*result, error) {
}
}
func readPacket(rd *chproto.Reader) (*result, error) {
func readPacket(cn *chpool.Conn, rd *chproto.Reader) (*result, error) {
packet, err := rd.Uvarint()
if err != nil {
return nil, err
@@ -316,7 +452,7 @@ func readPacket(rd *chproto.Reader) (*result, error) {
case chproto.ServerException:
return nil, readException(rd)
case chproto.ServerProgress:
if err := readProgress(rd); err != nil {
if err := readProgress(cn, rd); err != nil {
return nil, err
}
return res, nil
@@ -337,13 +473,12 @@ func readPacket(rd *chproto.Reader) (*result, error) {
}
}
// TODO: return block
func readBlock(rd *chproto.Reader, block *chschema.Block) error {
func (db *DB) readBlock(rd *chproto.Reader, block *chschema.Block, compressible bool) error {
if _, err := rd.String(); err != nil {
return err
}
return rd.WithCompression(func() error {
return rd.WithCompression(compressible && db.conf.Compression, func() error {
if err := readBlockInfo(rd); err != nil {
return err
}
@@ -409,10 +544,6 @@ func readBlockInfo(rd *chproto.Reader) error {
return nil
}
func writeCancel(wr *chproto.Writer) {
wr.Uvarint(chproto.ClientCancel)
}
func readServerTableColumns(rd *chproto.Reader) error {
_, err := rd.String()
if err != nil {

View File

@@ -32,6 +32,15 @@ type baseQuery struct {
flags internal.Flag
}
func (q *baseQuery) clone() baseQuery {
clone := *q
clone.with = lazyClone(clone.with)
clone.tables = lazyClone(clone.tables)
clone.columns = lazyClone(clone.columns)
clone.settings = lazyClone(clone.settings)
return clone
}
func (q *baseQuery) DB() *DB {
return q.db
}
@@ -94,13 +103,46 @@ func (q *baseQuery) newModel(values ...any) (Model, error) {
return q.tableModel, nil
}
func (q *baseQuery) query(ctx context.Context, model Model, query string) (*result, error) {
blocks, err := q.db.query(ctx, query)
if err != nil {
return nil, err
}
res := &result{
model: model,
}
block := new(chschema.Block)
if model, ok := model.(TableModel); ok {
block.Table = model.Table()
}
for blocks.Next(ctx, block) {
if err := model.ScanBlock(block); err != nil {
return nil, err
}
res.affected += block.NumRow
}
if err := blocks.Err(); err != nil {
return nil, err
}
if model, ok := model.(AfterScanRowHook); ok {
if err := model.AfterScanRow(ctx); err != nil {
return nil, err
}
}
return res, nil
}
func (q *baseQuery) exec(
ctx context.Context,
iquery Query,
query string,
) (sql.Result, error) {
ctx, event := q.db.beforeQuery(ctx, iquery, query, nil, q.tableModel)
res, err := q.db.query(ctx, nil, query)
res, err := q.db.exec(ctx, query)
q.db.afterQuery(ctx, event, res, err)
return res, err
}
@@ -256,7 +298,7 @@ func (q *baseQuery) addColumn(column chschema.QueryWithArgs) {
func (q *baseQuery) excludeColumn(columns []string) {
if q.columns == nil {
for _, f := range q.table.Fields {
q.columns = append(q.columns, chschema.UnsafeIdent(f.CHName))
q.columns = append(q.columns, chschema.UnsafeName(f.CHName))
}
}
@@ -323,63 +365,70 @@ func (q *baseQuery) appendSettings(fmter chschema.Formatter, b []byte) (_ []byte
return b, nil
}
//------------------------------------------------------------------------------
type WhereQuery struct {
where []chschema.QueryWithSep
}
func (q *WhereQuery) addWhere(where chschema.QueryWithSep) {
q.where = append(q.where, where)
}
func (q *WhereQuery) WhereGroup(sep string, fn func(*WhereQuery)) {
q.addWhereGroup(sep, fn)
}
func (q *WhereQuery) addWhereGroup(sep string, fn func(*WhereQuery)) {
q2 := new(WhereQuery)
fn(q2)
if len(q2.where) > 0 {
q2.where[0].Sep = ""
q.addWhere(chschema.SafeQueryWithSep("", nil, sep+"("))
q.where = append(q.where, q2.where...)
q.addWhere(chschema.SafeQueryWithSep("", nil, ")"))
func (q *baseQuery) appendColumns(fmter chschema.Formatter, b []byte) (_ []byte, err error) {
switch {
case q.columns != nil:
for i, f := range q.columns {
if i > 0 {
b = append(b, ", "...)
}
b, err = f.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
case q.table != nil:
b = appendTableColumns(b, q.table.CHAlias, q.table.Fields)
default:
b = append(b, '*')
}
}
//------------------------------------------------------------------------------
type whereBaseQuery struct {
baseQuery
WhereQuery
}
func (q *whereBaseQuery) mustAppendWhere(fmter chschema.Formatter, b []byte) ([]byte, error) {
if len(q.where) == 0 {
err := errors.New("ch: Update and Delete queries require at least one Where")
return nil, err
}
return q.appendWhere(fmter, b)
}
func (q *whereBaseQuery) appendWhere(fmter chschema.Formatter, b []byte) (_ []byte, err error) {
if len(q.where) == 0 {
return b, nil
}
b = append(b, " WHERE "...)
b, err = appendWhere(fmter, b, q.where)
if err != nil {
return nil, err
}
return b, nil
}
func appendTableColumns(b []byte, table chschema.Safe, fields []*chschema.Field) []byte {
for i, f := range fields {
if i > 0 {
b = append(b, ", "...)
}
if len(table) > 0 {
b = append(b, table...)
b = append(b, '.')
}
b = append(b, f.Column...)
}
return b
}
//------------------------------------------------------------------------------
type whereQuery struct {
filters []chschema.QueryWithSep
}
func (q *whereQuery) clone() whereQuery {
clone := *q
clone.filters = lazyClone(clone.filters)
return clone
}
func (q *whereQuery) addFilter(filter chschema.QueryWithSep) {
q.filters = append(q.filters, filter)
}
func (q *whereQuery) addGroup(sep string, filters []chschema.QueryWithSep) {
if len(filters) == 0 {
return
}
q.addFilter(chschema.SafeQueryWithSep("", nil, sep))
q.addFilter(chschema.SafeQueryWithSep("", nil, "("))
filters[0].Sep = ""
q.filters = append(q.filters, filters...)
q.addFilter(chschema.SafeQueryWithSep("", nil, ")"))
}
func appendWhere(
fmter chschema.Formatter, b []byte, where []chschema.QueryWithSep,
) (_ []byte, err error) {
@@ -388,7 +437,7 @@ func appendWhere(
b = append(b, where.Sep...)
}
if where.Query == "" && where.Args == nil {
if where.Query == "" {
continue
}
@@ -401,3 +450,11 @@ func appendWhere(
}
return b, nil
}
func lazyClone[S ~[]E, E any](s S) S {
// Preserve nil in case it matters.
if s == nil {
return nil
}
return s[:len(s):len(s)]
}

View File

@@ -10,17 +10,16 @@ import (
)
type InsertQuery struct {
whereBaseQuery
baseQuery
where whereQuery
}
var _ Query = (*InsertQuery)(nil)
func NewInsertQuery(db *DB) *InsertQuery {
return &InsertQuery{
whereBaseQuery: whereBaseQuery{
baseQuery: baseQuery{
db: db,
},
baseQuery: baseQuery{
db: db,
},
}
}
@@ -34,7 +33,7 @@ func (q *InsertQuery) Model(model any) *InsertQuery {
func (q *InsertQuery) Table(tables ...string) *InsertQuery {
for _, table := range tables {
q.addTable(chschema.UnsafeIdent(table))
q.addTable(chschema.UnsafeName(table))
}
return q
}
@@ -44,6 +43,11 @@ func (q *InsertQuery) TableExpr(query string, args ...any) *InsertQuery {
return q
}
func (q *InsertQuery) ModelTable(table string) *InsertQuery {
q.modelTableName = chschema.UnsafeName(table)
return q
}
func (q *InsertQuery) ModelTableExpr(query string, args ...any) *InsertQuery {
q.modelTableName = chschema.SafeQuery(query, args)
return q
@@ -58,7 +62,7 @@ func (q *InsertQuery) Setting(query string, args ...any) *InsertQuery {
func (q *InsertQuery) Column(columns ...string) *InsertQuery {
for _, column := range columns {
q.addColumn(chschema.UnsafeIdent(column))
q.addColumn(chschema.UnsafeName(column))
}
return q
}
@@ -76,17 +80,12 @@ func (q *InsertQuery) ExcludeColumn(columns ...string) *InsertQuery {
//------------------------------------------------------------------------------
func (q *InsertQuery) Where(query string, args ...any) *InsertQuery {
q.addWhere(chschema.SafeQueryWithSep(query, args, " AND "))
q.where.addFilter(chschema.SafeQueryWithSep(query, args, " AND "))
return q
}
func (q *InsertQuery) WhereOr(query string, args ...any) *InsertQuery {
q.addWhere(chschema.SafeQueryWithSep(query, args, " OR "))
return q
}
func (q *InsertQuery) WhereGroup(sep string, fn func(*WhereQuery)) *InsertQuery {
q.addWhereGroup(sep, fn)
q.where.addFilter(chschema.SafeQueryWithSep(query, args, " OR "))
return q
}
@@ -157,10 +156,9 @@ func (q *InsertQuery) appendValues(
return nil, err
}
if len(q.where) > 0 {
if len(q.where.filters) > 0 {
b = append(b, " WHERE "...)
b, err = appendWhere(fmter, b, q.where)
b, err = appendWhere(fmter, b, q.where.filters)
if err != nil {
return nil, err
}
@@ -191,13 +189,22 @@ func (q *InsertQuery) Exec(ctx context.Context) (sql.Result, error) {
}
query := internal.String(queryBytes)
fields, err := q.getFields()
if err != nil {
return nil, err
ctx, evt := q.db.beforeQuery(ctx, q, query, nil, q.tableModel)
var res *result
var retErr error
if q.tableModel != nil {
fields, err := q.getFields()
if err != nil {
return nil, err
}
res, retErr = q.db.insert(ctx, q.tableModel, query, fields)
} else {
res, retErr = q.db.exec(ctx, query)
}
ctx, evt := q.db.beforeQuery(ctx, q, query, nil, q.tableModel)
res, err := q.db.insert(ctx, q.tableModel, query, fields)
q.db.afterQuery(ctx, evt, res, err)
return res, err
q.db.afterQuery(ctx, evt, res, retErr)
return res, retErr
}

69
ch/query_raw.go Normal file
View File

@@ -0,0 +1,69 @@
package ch
import (
"context"
"database/sql"
"github.com/uptrace/go-clickhouse/ch/chschema"
)
type RawQuery struct {
baseQuery
query string
args []any
}
func NewRawQuery(db *DB, query string, args ...any) *RawQuery {
return &RawQuery{
baseQuery: baseQuery{
db: db,
},
query: query,
args: args,
}
}
func (q *RawQuery) Scan(ctx context.Context, dest ...any) error {
return q.scan(ctx, dest, false)
}
func (q *RawQuery) ScanColumns(ctx context.Context, dest ...any) error {
return q.scan(ctx, dest, true)
}
func (q *RawQuery) scan(ctx context.Context, dest []any, columnar bool) error {
if q.err != nil {
return q.err
}
model, err := q.newModel(dest...)
if err != nil {
return err
}
query := q.db.FormatQuery(q.query, q.args...)
ctx, evt := q.db.beforeQuery(ctx, q, query, nil, model)
res, err := q.baseQuery.query(ctx, model, query)
q.db.afterQuery(ctx, evt, res, err)
if err != nil {
return err
}
if !columnar && useQueryRowModel(model) {
if res.affected == 0 {
return sql.ErrNoRows
}
}
return nil
}
func (q *RawQuery) AppendQuery(fmter chschema.Formatter, b []byte) ([]byte, error) {
return fmter.AppendQuery(b, q.query, q.args), nil
}
func (q *RawQuery) Operation() string {
return "SELECT"
}

View File

@@ -13,31 +13,53 @@ import (
)
type SelectQuery struct {
whereBaseQuery
baseQuery
sample chschema.QueryWithArgs
distinctOn []chschema.QueryWithArgs
joins []joinQuery
prewhere whereQuery
where whereQuery
group []chschema.QueryWithArgs
having []chschema.QueryWithArgs
order []chschema.QueryWithArgs
limit int
offset int
final bool
union []union
}
type union struct {
expr string
query *SelectQuery
}
var _ Query = (*SelectQuery)(nil)
func NewSelectQuery(db *DB) *SelectQuery {
return &SelectQuery{
whereBaseQuery: whereBaseQuery{
baseQuery: baseQuery{
db: db,
},
baseQuery: baseQuery{
db: db,
},
}
}
func (q *SelectQuery) Clone() *SelectQuery {
clone := *q
clone.baseQuery = clone.baseQuery.clone()
clone.prewhere = clone.prewhere.clone()
clone.where = clone.where.clone()
clone.distinctOn = lazyClone(clone.distinctOn)
clone.joins = lazyClone(clone.joins)
clone.group = lazyClone(clone.group)
clone.having = lazyClone(clone.having)
clone.order = lazyClone(clone.order)
return &clone
}
func (q *SelectQuery) Operation() string {
return "SELECT"
}
@@ -95,7 +117,7 @@ func (q *SelectQuery) DistinctOn(query string, args ...any) *SelectQuery {
func (q *SelectQuery) Table(tables ...string) *SelectQuery {
for _, table := range tables {
q.addTable(chschema.UnsafeIdent(table))
q.addTable(chschema.UnsafeName(table))
}
return q
}
@@ -105,6 +127,11 @@ func (q *SelectQuery) TableExpr(query string, args ...any) *SelectQuery {
return q
}
func (q *SelectQuery) ModelTable(table string) *SelectQuery {
q.modelTableName = chschema.UnsafeName(table)
return q
}
func (q *SelectQuery) ModelTableExpr(query string, args ...any) *SelectQuery {
q.modelTableName = chschema.SafeQuery(query, args)
return q
@@ -119,7 +146,7 @@ func (q *SelectQuery) Sample(query string, args ...any) *SelectQuery {
func (q *SelectQuery) Column(columns ...string) *SelectQuery {
for _, column := range columns {
q.addColumn(chschema.UnsafeIdent(column))
q.addColumn(chschema.UnsafeName(column))
}
return q
}
@@ -136,6 +163,24 @@ func (q *SelectQuery) ExcludeColumn(columns ...string) *SelectQuery {
//------------------------------------------------------------------------------
func (q *SelectQuery) Union(other *SelectQuery) *SelectQuery {
return q.addUnion(" UNION ", other)
}
func (q *SelectQuery) UnionAll(other *SelectQuery) *SelectQuery {
return q.addUnion(" UNION ALL ", other)
}
func (q *SelectQuery) addUnion(expr string, other *SelectQuery) *SelectQuery {
q.union = append(q.union, union{
expr: expr,
query: other,
})
return q
}
//------------------------------------------------------------------------------
func (q *SelectQuery) Join(join string, args ...any) *SelectQuery {
q.joins = append(q.joins, joinQuery{
join: chschema.SafeQuery(join, args),
@@ -163,18 +208,53 @@ func (q *SelectQuery) joinOn(cond string, args []any, sep string) *SelectQuery {
//------------------------------------------------------------------------------
func (q *SelectQuery) Prewhere(query string, args ...any) *SelectQuery {
q.prewhere.addFilter(chschema.SafeQueryWithSep(query, args, " AND "))
return q
}
func (q *SelectQuery) PrewhereOr(query string, args ...any) *SelectQuery {
q.prewhere.addFilter(chschema.SafeQueryWithSep(query, args, " OR "))
return q
}
func (q *SelectQuery) PrewhereGroup(sep string, fn func(*SelectQuery) *SelectQuery) *SelectQuery {
saved := q.prewhere.filters
q.prewhere.filters = nil
q = fn(q)
filters := q.prewhere.filters
q.prewhere.filters = saved
q.prewhere.addGroup(sep, filters)
return q
}
//------------------------------------------------------------------------------
func (q *SelectQuery) Where(query string, args ...any) *SelectQuery {
q.addWhere(chschema.SafeQueryWithSep(query, args, " AND "))
q.where.addFilter(chschema.SafeQueryWithSep(query, args, " AND "))
return q
}
func (q *SelectQuery) WhereOr(query string, args ...any) *SelectQuery {
q.addWhere(chschema.SafeQueryWithSep(query, args, " OR "))
q.where.addFilter(chschema.SafeQueryWithSep(query, args, " OR "))
return q
}
func (q *SelectQuery) WhereGroup(sep string, fn func(*WhereQuery)) *SelectQuery {
q.addWhereGroup(sep, fn)
func (q *SelectQuery) WhereGroup(sep string, fn func(*SelectQuery) *SelectQuery) *SelectQuery {
saved := q.where.filters
q.where.filters = nil
q = fn(q)
filters := q.where.filters
q.where.filters = saved
q.where.addGroup(sep, filters)
return q
}
@@ -182,7 +262,7 @@ func (q *SelectQuery) WhereGroup(sep string, fn func(*WhereQuery)) *SelectQuery
func (q *SelectQuery) Group(columns ...string) *SelectQuery {
for _, column := range columns {
q.group = append(q.group, chschema.UnsafeIdent(column))
q.group = append(q.group, chschema.UnsafeName(column))
}
return q
}
@@ -205,7 +285,7 @@ func (q *SelectQuery) Order(orders ...string) *SelectQuery {
index := strings.IndexByte(order, ' ')
if index == -1 {
q.order = append(q.order, chschema.UnsafeIdent(order))
q.order = append(q.order, chschema.UnsafeName(order))
continue
}
@@ -216,11 +296,11 @@ func (q *SelectQuery) Order(orders ...string) *SelectQuery {
case "ASC", "DESC", "ASC NULLS FIRST", "DESC NULLS FIRST",
"ASC NULLS LAST", "DESC NULLS LAST":
q.order = append(q.order, chschema.SafeQuery("? ?", []any{
Ident(field),
Name(field),
Safe(sort),
}))
default:
q.order = append(q.order, chschema.UnsafeIdent(order))
q.order = append(q.order, chschema.UnsafeName(order))
}
}
return q
@@ -278,6 +358,10 @@ func (q *SelectQuery) appendQuery(
b = append(b, `WITH "_count_wrapper" AS (`...)
}
if len(q.union) > 0 {
b = append(b, '(')
}
if len(q.with) > 0 {
b, err = q.appendWith(fmter, b)
if err != nil {
@@ -316,6 +400,9 @@ func (q *SelectQuery) appendQuery(
return nil, err
}
}
if q.final {
b = append(b, " FINAL"...)
}
if !q.sample.IsZero() {
b = append(b, " SAMPLE "...)
b, err = q.sample.AppendQuery(fmter, b)
@@ -332,9 +419,19 @@ func (q *SelectQuery) appendQuery(
}
}
b, err = q.appendWhere(fmter, b)
if err != nil {
return nil, err
if len(q.prewhere.filters) > 0 {
b = append(b, " PREWHERE "...)
b, err = appendWhere(fmter, b, q.prewhere.filters)
if err != nil {
return nil, err
}
}
if len(q.where.filters) > 0 {
b = append(b, " WHERE "...)
b, err = appendWhere(fmter, b, q.where.filters)
if err != nil {
return nil, err
}
}
if len(q.group) > 0 {
@@ -387,10 +484,23 @@ func (q *SelectQuery) appendQuery(
b = append(b, " OFFSET "...)
b = strconv.AppendInt(b, int64(q.offset), 10)
}
if q.final {
b = append(b, " FINAL"...)
}
if len(q.union) > 0 {
b = append(b, ')')
for _, u := range q.union {
b = append(b, u.expr...)
b = append(b, '(')
b, err = u.query.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
b = append(b, ')')
}
} else if cteCount {
}
if cteCount {
b = append(b, `) SELECT `...)
b = append(b, "count()"...)
b = append(b, ` FROM "_count_wrapper"`...)
@@ -412,7 +522,7 @@ func (q *SelectQuery) appendWith(fmter chschema.Formatter, b []byte) (_ []byte,
}
if with.cte {
b = chschema.AppendIdent(b, with.name)
b = chschema.AppendName(b, with.name)
b = append(b, " AS "...)
b = append(b, "("...)
}
@@ -426,47 +536,13 @@ func (q *SelectQuery) appendWith(fmter chschema.Formatter, b []byte) (_ []byte,
b = append(b, ")"...)
} else {
b = append(b, " AS "...)
b = chschema.AppendIdent(b, with.name)
b = chschema.AppendName(b, with.name)
}
}
b = append(b, ' ')
return b, nil
}
func (q *SelectQuery) appendColumns(fmter chschema.Formatter, b []byte) (_ []byte, err error) {
switch {
case q.columns != nil:
for i, f := range q.columns {
if i > 0 {
b = append(b, ", "...)
}
b, err = f.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
case q.table != nil:
b = appendTableColumns(b, q.table.CHAlias, q.table.Fields)
default:
b = append(b, '*')
}
return b, nil
}
func appendTableColumns(b []byte, table chschema.Safe, fields []*chschema.Field) []byte {
for i, f := range fields {
if i > 0 {
b = append(b, ", "...)
}
if len(table) > 0 {
b = append(b, table...)
b = append(b, '.')
}
b = append(b, f.Column...)
}
return b
}
func (q *SelectQuery) Scan(ctx context.Context, values ...any) error {
return q.scan(ctx, false, values...)
}
@@ -496,7 +572,7 @@ func (q *SelectQuery) scan(ctx context.Context, columnar bool, values ...any) er
query := internal.String(queryBytes)
ctx, evt := q.db.beforeQuery(ctx, q, query, nil, model)
res, err := q.db.query(ctx, model, query)
res, err := q.query(ctx, model, query)
q.db.afterQuery(ctx, evt, res, err)
if err != nil {
return err

View File

@@ -12,6 +12,8 @@ type CreateTableQuery struct {
baseQuery
ifNotExists bool
as chschema.QueryWithArgs
onCluster chschema.QueryWithArgs
engine chschema.QueryWithArgs
ttl chschema.QueryWithArgs
partition chschema.QueryWithArgs
@@ -33,11 +35,15 @@ func (q *CreateTableQuery) Model(model any) *CreateTableQuery {
return q
}
// ------------------------------------------------------------------------------
func (q *CreateTableQuery) Apply(fn func(*CreateTableQuery) *CreateTableQuery) *CreateTableQuery {
return fn(q)
}
//------------------------------------------------------------------------------
func (q *CreateTableQuery) Table(tables ...string) *CreateTableQuery {
for _, table := range tables {
q.addTable(chschema.UnsafeIdent(table))
q.addTable(chschema.UnsafeName(table))
}
return q
}
@@ -47,11 +53,21 @@ func (q *CreateTableQuery) TableExpr(query string, args ...any) *CreateTableQuer
return q
}
func (q *CreateTableQuery) ModelTable(table string) *CreateTableQuery {
q.modelTableName = chschema.UnsafeName(table)
return q
}
func (q *CreateTableQuery) ModelTableExpr(query string, args ...any) *CreateTableQuery {
q.modelTableName = chschema.SafeQuery(query, args)
return q
}
func (q *CreateTableQuery) As(table string) *CreateTableQuery {
q.as = chschema.UnsafeName(table)
return q
}
func (q *CreateTableQuery) ColumnExpr(query string, args ...any) *CreateTableQuery {
q.addColumn(chschema.SafeQuery(query, args))
return q
@@ -64,6 +80,11 @@ func (q *CreateTableQuery) IfNotExists() *CreateTableQuery {
return q
}
func (q *CreateTableQuery) OnCluster(cluster string) *CreateTableQuery {
q.onCluster = chschema.UnsafeName(cluster)
return q
}
func (q *CreateTableQuery) Engine(query string, args ...any) *CreateTableQuery {
q.engine = chschema.SafeQuery(query, args)
return q
@@ -101,10 +122,6 @@ func (q *CreateTableQuery) AppendQuery(fmter chschema.Formatter, b []byte) (_ []
if q.err != nil {
return nil, q.err
}
if q.table == nil {
return nil, errNilModel
}
b = append(b, "CREATE TABLE "...)
if q.ifNotExists {
b = append(b, "IF NOT EXISTS "...)
@@ -115,36 +132,54 @@ func (q *CreateTableQuery) AppendQuery(fmter chschema.Formatter, b []byte) (_ []
return nil, err
}
b = append(b, " ("...)
for i, field := range q.table.Fields {
if i > 0 {
b = append(b, ", "...)
}
b = append(b, field.CHName...)
b = append(b, " "...)
b = append(b, field.CHType...)
if field.NotNull {
b = append(b, " NOT NULL"...)
}
if field.CHDefault != "" {
b = append(b, " DEFAULT "...)
b = append(b, field.CHDefault...)
}
}
for i, col := range q.columns {
if i > 0 || len(q.table.Fields) > 0 {
b = append(b, ", "...)
}
b, err = col.AppendQuery(fmter, b)
if !q.onCluster.IsEmpty() {
b = append(b, " ON CLUSTER "...)
b, err = q.onCluster.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
b = append(b, ")"...)
if !q.as.IsEmpty() {
b = append(b, " AS "...)
b, err = q.as.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
if q.table != nil {
b = append(b, " ("...)
for i, field := range q.table.Fields {
if i > 0 {
b = append(b, ", "...)
}
b = append(b, field.CHName...)
b = append(b, " "...)
b = append(b, field.CHType...)
if field.NotNull {
b = append(b, " NOT NULL"...)
}
if field.CHDefault != "" {
b = append(b, " DEFAULT "...)
b = append(b, field.CHDefault...)
}
}
for i, col := range q.columns {
if i > 0 || len(q.table.Fields) > 0 {
b = append(b, ", "...)
}
b, err = col.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
b = append(b, ")"...)
}
b = append(b, " Engine = "...)
@@ -171,17 +206,19 @@ func (q *CreateTableQuery) AppendQuery(fmter chschema.Formatter, b []byte) (_ []
return nil, err
}
b = append(b, ')')
} else if len(q.table.PKs) > 0 {
b = append(b, " ORDER BY ("...)
for i, pk := range q.table.PKs {
if i > 0 {
b = append(b, ", "...)
} else if q.table != nil {
if len(q.table.PKs) > 0 {
b = append(b, " ORDER BY ("...)
for i, pk := range q.table.PKs {
if i > 0 {
b = append(b, ", "...)
}
b = append(b, pk.CHName...)
}
b = append(b, pk.CHName...)
b = append(b, ')')
} else if q.table.CHEngine == "" {
b = append(b, " ORDER BY tuple()"...)
}
b = append(b, ')')
} else if q.table.CHEngine == "" {
b = append(b, " ORDER BY tuple()"...)
}
if !q.ttl.IsZero() {
@@ -201,7 +238,7 @@ func (q *CreateTableQuery) AppendQuery(fmter chschema.Formatter, b []byte) (_ []
}
func (q *CreateTableQuery) appendPartition(fmter chschema.Formatter, b []byte) ([]byte, error) {
if q.partition.IsZero() && q.table.CHPartition == "" {
if q.partition.IsZero() && (q.table == nil || q.table.CHPartition == "") {
return b, nil
}

View File

@@ -11,7 +11,8 @@ import (
type DropTableQuery struct {
baseQuery
ifExists bool
ifExists bool
onCluster chschema.QueryWithArgs
}
var _ Query = (*DropTableQuery)(nil)
@@ -34,7 +35,7 @@ func (q *DropTableQuery) Model(model any) *DropTableQuery {
func (q *DropTableQuery) Table(tables ...string) *DropTableQuery {
for _, table := range tables {
q.addTable(chschema.UnsafeIdent(table))
q.addTable(chschema.UnsafeName(table))
}
return q
}
@@ -56,6 +57,11 @@ func (q *DropTableQuery) IfExists() *DropTableQuery {
return q
}
func (q *DropTableQuery) OnCluster(cluster string) *DropTableQuery {
q.onCluster = chschema.UnsafeName(cluster)
return q
}
//------------------------------------------------------------------------------
func (q *DropTableQuery) Operation() string {
@@ -77,6 +83,14 @@ func (q *DropTableQuery) AppendQuery(fmter chschema.Formatter, b []byte) (_ []by
return nil, err
}
if !q.onCluster.IsEmpty() {
b = append(b, " ON CLUSTER "...)
b, err = q.onCluster.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
return b, nil
}

View File

@@ -34,7 +34,7 @@ func (q *TruncateTableQuery) Model(model any) *TruncateTableQuery {
func (q *TruncateTableQuery) Table(tables ...string) *TruncateTableQuery {
for _, table := range tables {
q.addTable(chschema.UnsafeIdent(table))
q.addTable(chschema.UnsafeName(table))
}
return q
}

View File

@@ -76,6 +76,64 @@ func TestQuery(t *testing.T) {
Order("id").
Setting("ttl_only_drop_parts = 1")
},
func(db *ch.DB) chschema.QueryAppender {
return db.NewDropView().View("view_name")
},
func(db *ch.DB) chschema.QueryAppender {
return db.NewDropView().IfExists().ViewExpr("view_name")
},
func(db *ch.DB) chschema.QueryAppender {
return db.NewCreateView().
Materialized().
IfNotExists().
View("view_name").
To("dest_table").
Column("col1").
ColumnExpr("col1 AS alias").
TableExpr("src_table AS alias").
Where("foo = bar").
Group("group1").
GroupExpr("group2, group3").
OrderExpr("order2, order3")
},
func(db *ch.DB) chschema.QueryAppender {
return db.NewSelect().
Model((*Model)(nil)).
Final()
},
func(db *ch.DB) chschema.QueryAppender {
return db.NewSelect().
Model((*Model)(nil)).
Where("id = ?", 1).
Final()
},
func(db *ch.DB) chschema.QueryAppender {
return db.NewSelect().
Model((*Model)(nil)).
Where("id = ?", 1).
Final().
Group("id").
OrderExpr("id")
},
func(db *ch.DB) chschema.QueryAppender {
q1 := db.NewSelect().Model(new(Model)).Where("1")
q2 := db.NewSelect().Model(new(Model))
return q1.Union(q2)
},
func(db *ch.DB) chschema.QueryAppender {
q1 := db.NewSelect().Model(new(Model)).Where("1")
q2 := db.NewSelect().Model(new(Model))
return q1.UnionAll(q2)
},
func(db *ch.DB) chschema.QueryAppender {
return db.NewCreateTable().
Table("my-table_dist").
As("my-table").
Engine("Distributed(?, currentDatabase(), ?, rand())",
ch.Name("my-cluster"), ch.Name("my-table")).
OnCluster("my-cluster").
IfNotExists()
},
}
db := chDB()

275
ch/query_view_create.go Normal file
View File

@@ -0,0 +1,275 @@
package ch
import (
"context"
"database/sql"
"github.com/uptrace/go-clickhouse/ch/chschema"
"github.com/uptrace/go-clickhouse/ch/internal"
)
type CreateViewQuery struct {
baseQuery
materialized bool
ifNotExists bool
view chschema.QueryWithArgs
onCluster chschema.QueryWithArgs
to chschema.QueryWithArgs
where whereQuery
group []chschema.QueryWithArgs
order chschema.QueryWithArgs
}
var _ Query = (*CreateViewQuery)(nil)
func NewCreateViewQuery(db *DB) *CreateViewQuery {
return &CreateViewQuery{
baseQuery: baseQuery{
db: db,
},
}
}
func (q *CreateViewQuery) Model(model any) *CreateViewQuery {
q.setTableModel(model)
return q
}
func (q *CreateViewQuery) Apply(fn func(*CreateViewQuery) *CreateViewQuery) *CreateViewQuery {
return fn(q)
}
//------------------------------------------------------------------------------
func (q *CreateViewQuery) View(view string) *CreateViewQuery {
q.view = chschema.UnsafeName(view)
return q
}
func (q *CreateViewQuery) ViewExpr(query string, args ...any) *CreateViewQuery {
q.view = chschema.SafeQuery(query, args)
return q
}
func (q *CreateViewQuery) OnCluster(cluster string) *CreateViewQuery {
q.onCluster = chschema.UnsafeName(cluster)
return q
}
func (q *CreateViewQuery) OnClusterExpr(query string, args ...any) *CreateViewQuery {
q.onCluster = chschema.SafeQuery(query, args)
return q
}
func (q *CreateViewQuery) To(to string) *CreateViewQuery {
q.to = chschema.UnsafeName(to)
return q
}
func (q *CreateViewQuery) ToExpr(query string, args ...any) *CreateViewQuery {
q.to = chschema.SafeQuery(query, args)
return q
}
func (q *CreateViewQuery) Table(tables ...string) *CreateViewQuery {
for _, table := range tables {
q.addTable(chschema.UnsafeName(table))
}
return q
}
func (q *CreateViewQuery) TableExpr(query string, args ...any) *CreateViewQuery {
q.addTable(chschema.SafeQuery(query, args))
return q
}
func (q *CreateViewQuery) ModelTableExpr(query string, args ...any) *CreateViewQuery {
q.modelTableName = chschema.SafeQuery(query, args)
return q
}
//------------------------------------------------------------------------------
func (q *CreateViewQuery) Column(columns ...string) *CreateViewQuery {
for _, column := range columns {
q.addColumn(chschema.UnsafeName(column))
}
return q
}
func (q *CreateViewQuery) ColumnExpr(query string, args ...any) *CreateViewQuery {
q.addColumn(chschema.SafeQuery(query, args))
return q
}
func (q *CreateViewQuery) ExcludeColumn(columns ...string) *CreateViewQuery {
q.excludeColumn(columns)
return q
}
//------------------------------------------------------------------------------
func (q *CreateViewQuery) Materialized() *CreateViewQuery {
q.materialized = true
return q
}
func (q *CreateViewQuery) IfNotExists() *CreateViewQuery {
q.ifNotExists = true
return q
}
//------------------------------------------------------------------------------
func (q *CreateViewQuery) Where(query string, args ...any) *CreateViewQuery {
q.where.addFilter(chschema.SafeQueryWithSep(query, args, " AND "))
return q
}
func (q *CreateViewQuery) WhereOr(query string, args ...any) *CreateViewQuery {
q.where.addFilter(chschema.SafeQueryWithSep(query, args, " OR "))
return q
}
func (q *CreateViewQuery) WhereGroup(sep string, fn func(*CreateViewQuery) *CreateViewQuery) *CreateViewQuery {
saved := q.where.filters
q.where.filters = nil
q = fn(q)
filters := q.where.filters
q.where.filters = saved
q.where.addGroup(sep, filters)
return q
}
//------------------------------------------------------------------------------
func (q *CreateViewQuery) Group(columns ...string) *CreateViewQuery {
for _, column := range columns {
q.group = append(q.group, chschema.UnsafeName(column))
}
return q
}
func (q *CreateViewQuery) GroupExpr(group string, args ...any) *CreateViewQuery {
q.group = append(q.group, chschema.SafeQuery(group, args))
return q
}
func (q *CreateViewQuery) OrderExpr(query string, args ...any) *CreateViewQuery {
q.order = chschema.SafeQuery(query, args)
return q
}
func (q *CreateViewQuery) Setting(query string, args ...any) *CreateViewQuery {
q.settings = append(q.settings, chschema.SafeQuery(query, args))
return q
}
//------------------------------------------------------------------------------
func (q *CreateViewQuery) Operation() string {
return "CREATE VIEW"
}
var _ chschema.QueryAppender = (*CreateViewQuery)(nil)
func (q *CreateViewQuery) AppendQuery(fmter chschema.Formatter, b []byte) (_ []byte, err error) {
if q.err != nil {
return nil, q.err
}
b = append(b, "CREATE "...)
if q.materialized {
b = append(b, "MATERIALIZED "...)
}
b = append(b, "VIEW "...)
if q.ifNotExists {
b = append(b, "IF NOT EXISTS "...)
}
b, err = q.view.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
if !q.onCluster.IsEmpty() {
b = append(b, " ON CLUSTER "...)
b, err = q.onCluster.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
b = append(b, " TO "...)
b, err = q.to.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
b = append(b, " AS "...)
b = append(b, "SELECT "...)
b, err = q.appendColumns(fmter, b)
if err != nil {
return nil, err
}
b = append(b, " FROM "...)
b, err = q.appendTablesWithAlias(fmter, b)
if err != nil {
return nil, err
}
if len(q.where.filters) > 0 {
b = append(b, " WHERE "...)
b, err = appendWhere(fmter, b, q.where.filters)
if err != nil {
return nil, err
}
}
if len(q.group) > 0 {
b = append(b, " GROUP BY "...)
for i, f := range q.group {
if i > 0 {
b = append(b, ", "...)
}
b, err = f.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
}
if !q.order.IsZero() {
b = append(b, " ORDER BY "...)
b, err = q.order.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
b, err = q.appendSettings(fmter, b)
if err != nil {
return nil, err
}
return b, nil
}
//------------------------------------------------------------------------------
func (q *CreateViewQuery) Exec(ctx context.Context, dest ...any) (sql.Result, error) {
queryBytes, err := q.AppendQuery(q.db.fmter, q.db.makeQueryBytes())
if err != nil {
return nil, err
}
query := internal.String(queryBytes)
return q.exec(ctx, q, query)
}

108
ch/query_view_drop.go Normal file
View File

@@ -0,0 +1,108 @@
package ch
import (
"context"
"database/sql"
"github.com/uptrace/go-clickhouse/ch/chschema"
"github.com/uptrace/go-clickhouse/ch/internal"
)
type DropViewQuery struct {
baseQuery
ifExists bool
view chschema.QueryWithArgs
onCluster chschema.QueryWithArgs
}
var _ Query = (*DropViewQuery)(nil)
func NewDropViewQuery(db *DB) *DropViewQuery {
q := &DropViewQuery{
baseQuery: baseQuery{
db: db,
},
}
return q
}
func (q *DropViewQuery) Model(model any) *DropViewQuery {
q.setTableModel(model)
return q
}
func (q *DropViewQuery) Apply(fn func(*DropViewQuery) *DropViewQuery) *DropViewQuery {
return fn(q)
}
//------------------------------------------------------------------------------
func (q *DropViewQuery) IfExists() *DropViewQuery {
q.ifExists = true
return q
}
func (q *DropViewQuery) View(view string) *DropViewQuery {
q.view = chschema.UnsafeName(view)
return q
}
func (q *DropViewQuery) ViewExpr(query string, args ...any) *DropViewQuery {
q.view = chschema.SafeQuery(query, args)
return q
}
func (q *DropViewQuery) OnCluster(cluster string) *DropViewQuery {
q.onCluster = chschema.UnsafeName(cluster)
return q
}
func (q *DropViewQuery) OnClusterExpr(query string, args ...any) *DropViewQuery {
q.onCluster = chschema.SafeQuery(query, args)
return q
}
//------------------------------------------------------------------------------
func (q *DropViewQuery) Operation() string {
return "DROP TABLE"
}
func (q *DropViewQuery) AppendQuery(fmter chschema.Formatter, b []byte) (_ []byte, err error) {
if q.err != nil {
return nil, q.err
}
b = append(b, "DROP VIEW "...)
if q.ifExists {
b = append(b, "IF EXISTS "...)
}
b, err = q.view.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
if !q.onCluster.IsEmpty() {
b = append(b, " ON CLUSTER "...)
b, err = q.onCluster.AppendQuery(fmter, b)
if err != nil {
return nil, err
}
}
return b, nil
}
//------------------------------------------------------------------------------
func (q *DropViewQuery) Exec(ctx context.Context, dest ...any) (sql.Result, error) {
queryBytes, err := q.AppendQuery(q.db.fmter, q.db.makeQueryBytes())
if err != nil {
return nil, err
}
query := internal.String(queryBytes)
return q.exec(ctx, q, query)
}

1
ch/testdata/snapshots/TestQuery-11 vendored Normal file
View File

@@ -0,0 +1 @@
DROP VIEW "view_name"

1
ch/testdata/snapshots/TestQuery-12 vendored Normal file
View File

@@ -0,0 +1 @@
DROP VIEW IF EXISTS view_name

1
ch/testdata/snapshots/TestQuery-13 vendored Normal file
View File

@@ -0,0 +1 @@
CREATE MATERIALIZED VIEW IF NOT EXISTS "view_name" TO "dest_table" AS SELECT "col1", col1 AS alias FROM src_table AS alias WHERE (foo = bar) GROUP BY "group1", group2, group3 ORDER BY order2, order3

1
ch/testdata/snapshots/TestQuery-14 vendored Normal file
View File

@@ -0,0 +1 @@
SELECT "model"."id", "model"."string", "model"."bytes" FROM "models" AS "model" FINAL

1
ch/testdata/snapshots/TestQuery-15 vendored Normal file
View File

@@ -0,0 +1 @@
SELECT "model"."id", "model"."string", "model"."bytes" FROM "models" AS "model" FINAL WHERE (id = 1)

1
ch/testdata/snapshots/TestQuery-16 vendored Normal file
View File

@@ -0,0 +1 @@
SELECT "model"."id", "model"."string", "model"."bytes" FROM "models" AS "model" FINAL WHERE (id = 1) GROUP BY "id" ORDER BY id

1
ch/testdata/snapshots/TestQuery-17 vendored Normal file
View File

@@ -0,0 +1 @@
(SELECT "model"."id", "model"."string", "model"."bytes" FROM "models" AS "model" WHERE (1)) UNION (SELECT "model"."id", "model"."string", "model"."bytes" FROM "models" AS "model")

1
ch/testdata/snapshots/TestQuery-18 vendored Normal file
View File

@@ -0,0 +1 @@
(SELECT "model"."id", "model"."string", "model"."bytes" FROM "models" AS "model" WHERE (1)) UNION ALL (SELECT "model"."id", "model"."string", "model"."bytes" FROM "models" AS "model")

1
ch/testdata/snapshots/TestQuery-19 vendored Normal file
View File

@@ -0,0 +1 @@
CREATE TABLE IF NOT EXISTS "my-table_dist" ON CLUSTER "my-cluster" AS "my-table" Engine = Distributed("my-cluster", currentDatabase(), "my-table", rand())

View File

@@ -43,14 +43,17 @@ func WithWriter(w io.Writer) Option {
// - CHDEBUG=0 - disables the hook.
// - CHDEBUG=1 - enables the hook.
// - CHDEBUG=2 - enables the hook and verbose mode.
func FromEnv(key string) Option {
if key == "" {
key = "CHDEBUG"
func FromEnv(keys ...string) Option {
if len(keys) == 0 {
keys = []string{"CHDEBUG"}
}
return func(h *QueryHook) {
if env, ok := os.LookupEnv(key); ok {
h.enabled = env != "" && env != "0"
h.verbose = env == "2"
for _, key := range keys {
if env, ok := os.LookupEnv(key); ok {
h.enabled = env != "" && env != "0"
h.verbose = env == "2"
break
}
}
}
}

View File

@@ -5,16 +5,18 @@ go 1.18
replace github.com/uptrace/go-clickhouse => ./..
require (
github.com/fatih/color v1.13.0
github.com/uptrace/go-clickhouse v0.2.0
github.com/fatih/color v1.15.0
github.com/uptrace/go-clickhouse v0.3.1
)
require (
github.com/codemodus/kace v0.5.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/sys v0.8.0 // indirect
)

View File

@@ -2,26 +2,28 @@ github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnL
github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA=
github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 h1:s/+U+w0teGzcoH2mdIlFQ6KfVKGaYpgyGdUefZrn9TU=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

164
chmigrate/chmigrate_test.go Normal file
View File

@@ -0,0 +1,164 @@
package chmigrate_test
import (
"context"
"errors"
"os"
"reflect"
"runtime"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/uptrace/go-clickhouse/ch"
"github.com/uptrace/go-clickhouse/chdebug"
"github.com/uptrace/go-clickhouse/chmigrate"
)
func TestMigrate(t *testing.T) {
type Test struct {
run func(t *testing.T, db *ch.DB)
}
tests := []Test{
{run: testChmigrateUpAndDown},
{run: testChmigrateUpError},
}
db := chDB()
for _, test := range tests {
t.Run(funcName(test.run), func(t *testing.T) {
test.run(t, db)
})
}
}
func testChmigrateUpAndDown(t *testing.T, db *ch.DB) {
ctx := context.Background()
var history []string
migrations := chmigrate.NewMigrations()
migrations.Add(chmigrate.Migration{
Name: "20060102150405",
Up: func(ctx context.Context, db *ch.DB) error {
history = append(history, "up1")
return nil
},
Down: func(ctx context.Context, db *ch.DB) error {
history = append(history, "down1")
return nil
},
})
migrations.Add(chmigrate.Migration{
Name: "20060102160405",
Up: func(ctx context.Context, db *ch.DB) error {
history = append(history, "up2")
return nil
},
Down: func(ctx context.Context, db *ch.DB) error {
history = append(history, "down2")
return nil
},
})
m := chmigrate.NewMigrator(db, migrations)
err := m.Reset(ctx)
require.NoError(t, err)
group, err := m.Migrate(ctx)
require.NoError(t, err)
require.Equal(t, int64(1), group.ID)
require.Len(t, group.Migrations, 2)
require.Equal(t, []string{"up1", "up2"}, history)
history = nil
group, err = m.Rollback(ctx)
require.NoError(t, err)
require.Equal(t, int64(1), group.ID)
require.Len(t, group.Migrations, 2)
require.Equal(t, []string{"down2", "down1"}, history)
}
func testChmigrateUpError(t *testing.T, db *ch.DB) {
ctx := context.Background()
var history []string
migrations := chmigrate.NewMigrations()
migrations.Add(chmigrate.Migration{
Name: "20060102150405",
Up: func(ctx context.Context, db *ch.DB) error {
history = append(history, "up1")
return nil
},
Down: func(ctx context.Context, db *ch.DB) error {
history = append(history, "down1")
return nil
},
})
migrations.Add(chmigrate.Migration{
Name: "20060102160405",
Up: func(ctx context.Context, db *ch.DB) error {
history = append(history, "up2")
return errors.New("failed")
},
Down: func(ctx context.Context, db *ch.DB) error {
history = append(history, "down2")
return nil
},
})
migrations.Add(chmigrate.Migration{
Name: "20060102170405",
Up: func(ctx context.Context, db *ch.DB) error {
history = append(history, "up3")
return errors.New("failed")
},
Down: func(ctx context.Context, db *ch.DB) error {
history = append(history, "down3")
return nil
},
})
m := chmigrate.NewMigrator(db, migrations)
err := m.Reset(ctx)
require.NoError(t, err)
group, err := m.Migrate(ctx)
require.Error(t, err)
require.Equal(t, "failed", err.Error())
require.Equal(t, int64(1), group.ID)
require.Len(t, group.Migrations, 2)
require.Equal(t, []string{"up1", "up2"}, history)
history = nil
group, err = m.Rollback(ctx)
require.NoError(t, err)
require.Equal(t, int64(1), group.ID)
require.Len(t, group.Migrations, 2)
require.Equal(t, []string{"down2", "down1"}, history)
}
func chDB(opts ...ch.Option) *ch.DB {
dsn := os.Getenv("CH")
if dsn == "" {
dsn = "clickhouse://localhost:9000/test?sslmode=disable"
}
opts = append(opts, ch.WithDSN(dsn))
db := ch.Connect(opts...)
db.AddQueryHook(chdebug.NewQueryHook(
chdebug.WithEnabled(false),
chdebug.FromEnv("CHDEBUG"),
))
return db
}
func funcName(x interface{}) string {
s := runtime.FuncForPC(reflect.ValueOf(x).Pointer()).Name()
if i := strings.LastIndexByte(s, '.'); i >= 0 {
return s[i+1:]
}
return s
}

View File

@@ -5,6 +5,7 @@ import (
"bytes"
"context"
"fmt"
"io"
"io/fs"
"sort"
"strings"
@@ -14,9 +15,8 @@ import (
)
type Migration struct {
ch.CHModel `ch:"engine:CollapsingMergeTree(sign)"`
Name string `ch:",pk"`
Comment string `ch:"-"`
GroupID int64
MigratedAt time.Time
Sign int8
@@ -26,7 +26,7 @@ type Migration struct {
}
func (m *Migration) String() string {
return m.Name
return fmt.Sprintf("%s_%s", m.Name, m.Comment)
}
func (m *Migration) IsApplied() bool {
@@ -41,45 +41,48 @@ func NewSQLMigrationFunc(fsys fs.FS, name string) MigrationFunc {
if err != nil {
return err
}
return Exec(ctx, db, f)
}
}
scanner := bufio.NewScanner(f)
var queries []string
// Exec reads and executes the SQL migration in the f.
func Exec(ctx context.Context, db *ch.DB, f io.Reader) error {
scanner := bufio.NewScanner(f)
var queries []string
var query []byte
for scanner.Scan() {
b := scanner.Bytes()
var query []byte
for scanner.Scan() {
b := scanner.Bytes()
const prefix = "--migration:"
if bytes.HasPrefix(b, []byte(prefix)) {
b = b[len(prefix):]
if bytes.Equal(b, []byte("split")) {
queries = append(queries, string(query))
query = query[:0]
continue
}
return fmt.Errorf("ch: unknown directive: %q", b)
const prefix = "--migration:"
if bytes.HasPrefix(b, []byte(prefix)) {
b = b[len(prefix):]
if bytes.Equal(b, []byte("split")) {
queries = append(queries, string(query))
query = query[:0]
continue
}
query = append(query, b...)
query = append(query, '\n')
return fmt.Errorf("ch: unknown directive: %q", b)
}
if len(query) > 0 {
queries = append(queries, string(query))
}
if err := scanner.Err(); err != nil {
query = append(query, b...)
query = append(query, '\n')
}
if len(query) > 0 {
queries = append(queries, string(query))
}
if err := scanner.Err(); err != nil {
return err
}
for _, q := range queries {
if _, err := db.ExecContext(ctx, q); err != nil {
return err
}
for _, q := range queries {
_, err = db.ExecContext(ctx, q)
if err != nil {
return err
}
}
return nil
}
return nil
}
const goTemplate = `package %s
@@ -128,7 +131,7 @@ func (ms MigrationSlice) String() string {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(ms[i].Name)
sb.WriteString(ms[i].String())
}
return sb.String()

View File

@@ -50,15 +50,16 @@ func (m *Migrations) MustRegister(up, down MigrationFunc) {
func (m *Migrations) Register(up, down MigrationFunc) error {
fpath := migrationFile()
name, err := extractMigrationName(fpath)
name, comment, err := extractMigrationName(fpath)
if err != nil {
return err
}
m.Add(Migration{
Name: name,
Up: up,
Down: down,
Name: name,
Comment: comment,
Up: up,
Down: down,
})
return nil
@@ -89,7 +90,7 @@ func (m *Migrations) Discover(fsys fs.FS) error {
return nil
}
name, err := extractMigrationName(path)
name, comment, err := extractMigrationName(path)
if err != nil {
return err
}
@@ -98,6 +99,8 @@ func (m *Migrations) Discover(fsys fs.FS) error {
if err != nil {
return err
}
migration.Comment = comment
migrationFunc := NewSQLMigrationFunc(fsys, path)
if strings.HasSuffix(path, ".up.sql") {
@@ -154,15 +157,15 @@ func migrationFile() string {
return ""
}
var fnameRE = regexp.MustCompile(`^(\d{14})_[0-9a-z_\-]+\.`)
var fnameRE = regexp.MustCompile(`^(\d{14})_([0-9a-z_\-]+)\.`)
func extractMigrationName(fpath string) (string, error) {
func extractMigrationName(fpath string) (string, string, error) {
fname := filepath.Base(fpath)
matches := fnameRE.FindStringSubmatch(fname)
if matches == nil {
return "", fmt.Errorf("chmigrate: unsupported migration name format: %q", fname)
return "", "", fmt.Errorf("chmigrate: unsupported migration name format: %q", fname)
}
return matches[1], nil
return matches[1], matches[2], nil
}

View File

@@ -17,7 +17,7 @@ type MigratorOption func(m *Migrator)
func WithTableName(table string) MigratorOption {
return func(m *Migrator) {
m.table = table
m.migrationsTable = table
}
}
@@ -27,14 +27,44 @@ func WithLocksTableName(table string) MigratorOption {
}
}
func WithReplicated(on bool) MigratorOption {
return func(m *Migrator) {
m.replicated = on
}
}
func WithDistributed(on bool) MigratorOption {
return func(m *Migrator) {
m.distributed = on
}
}
func WithOnCluster(cluster string) MigratorOption {
return func(m *Migrator) {
m.cluster = cluster
}
}
// WithMarkAppliedOnSuccess sets the migrator to only mark migrations as applied/unapplied
// when their up/down is successful.
func WithMarkAppliedOnSuccess(enabled bool) MigratorOption {
return func(m *Migrator) {
m.markAppliedOnSuccess = enabled
}
}
type Migrator struct {
db *ch.DB
migrations *Migrations
ms MigrationSlice
table string
locksTable string
migrationsTable string
locksTable string
replicated bool
distributed bool
cluster string
markAppliedOnSuccess bool
}
func NewMigrator(db *ch.DB, migrations *Migrations, opts ...MigratorOption) *Migrator {
@@ -44,8 +74,8 @@ func NewMigrator(db *ch.DB, migrations *Migrations, opts ...MigratorOption) *Mig
ms: migrations.ms,
table: "ch_migrations",
locksTable: "ch_migration_locks",
migrationsTable: "ch_migrations",
locksTable: "ch_migration_locks",
}
for _, opt := range opts {
opt(m)
@@ -66,7 +96,7 @@ func (m *Migrator) MigrationsWithStatus(ctx context.Context) (MigrationSlice, er
func (m *Migrator) migrationsWithStatus(ctx context.Context) (MigrationSlice, int64, error) {
sorted := m.migrations.Sorted()
applied, err := m.selectAppliedMigrations(ctx)
applied, err := m.AppliedMigrations(ctx)
if err != nil {
return nil, 0, err
}
@@ -84,37 +114,76 @@ func (m *Migrator) migrationsWithStatus(ctx context.Context) (MigrationSlice, in
}
func (m *Migrator) Init(ctx context.Context) error {
if m.distributed {
if m.cluster == "" {
return errors.New("chmigrate: distributed requires a cluster name")
}
}
if _, err := m.db.NewCreateTable().
Model((*Migration)(nil)).
ModelTableExpr(m.table).
Apply(func(q *ch.CreateTableQuery) *ch.CreateTableQuery {
if m.replicated {
return q.Engine("ReplicatedCollapsingMergeTree(sign)")
}
return q.Engine("CollapsingMergeTree(sign)")
}).
ModelTable(m.migrationsTable).
OnCluster(m.cluster).
IfNotExists().
Exec(ctx); err != nil {
return err
}
if _, err := m.db.NewCreateTable().
Model((*migrationLock)(nil)).
ModelTableExpr(m.locksTable).
Apply(func(q *ch.CreateTableQuery) *ch.CreateTableQuery {
if m.replicated {
return q.Engine("ReplicatedMergeTree")
}
return q.Engine("MergeTree")
}).
ModelTable(m.locksTable).
OnCluster(m.cluster).
IfNotExists().
Exec(ctx); err != nil {
return err
}
if m.distributed {
if _, err := m.db.NewCreateTable().
Table(m.distTable(m.migrationsTable)).
As(m.migrationsTable).
Engine("Distributed(?, currentDatabase(), ?, rand())",
ch.Ident(m.cluster), ch.Ident(m.migrationsTable)).
OnCluster(m.cluster).
IfNotExists().
Exec(ctx); err != nil {
return err
}
}
return nil
}
func (m *Migrator) Reset(ctx context.Context) error {
if _, err := m.db.NewDropTable().
Model((*Migration)(nil)).
ModelTableExpr(m.table).
IfExists().
Exec(ctx); err != nil {
return err
tables := []string{
m.migrationsTable,
m.locksTable,
}
if _, err := m.db.NewDropTable().
Model((*migrationLock)(nil)).
ModelTableExpr(m.locksTable).
IfExists().
Exec(ctx); err != nil {
return err
if m.distributed {
tables = append(tables,
m.distTable(m.migrationsTable),
)
}
for _, tableName := range tables {
if _, err := m.db.NewDropTable().
Table(tableName).
OnCluster(m.cluster).
IfExists().
Exec(ctx); err != nil {
return err
}
}
return m.Init(ctx)
}
@@ -127,41 +196,51 @@ func (m *Migrator) Migrate(ctx context.Context, opts ...MigrationOption) (*Migra
return nil, err
}
if err := m.Lock(ctx); err != nil {
return nil, err
}
defer m.Unlock(ctx) //nolint:errcheck
migrations, lastGroupID, err := m.migrationsWithStatus(ctx)
if err != nil {
return nil, err
}
group := &MigrationGroup{
Migrations: migrations.Unapplied(),
// Get the unapplied migrations we will range over and apply
migrations = migrations.Unapplied()
if len(migrations) == 0 {
return &MigrationGroup{}, nil
}
if len(group.Migrations) == 0 {
return group, nil
}
group.ID = lastGroupID + 1
for i := range group.Migrations {
migration := &group.Migrations[i]
migration.GroupID = group.ID
// Create empty group to track applied migrations
applied := new(MigrationGroup)
applied.ID = lastGroupID + 1
// Always mark migration as applied so the rollback has a chance to fix the database.
if err := m.MarkApplied(ctx, migration); err != nil {
return nil, err
for i := range migrations {
migration := &migrations[i]
migration.GroupID = applied.ID
if !m.markAppliedOnSuccess {
if err := m.MarkApplied(ctx, migration); err != nil {
return applied, err
}
// Add to the group before upping the migration when always marking applied
applied.Migrations = append(applied.Migrations, *migration)
}
if !cfg.nop && migration.Up != nil {
if err := migration.Up(ctx, m.db); err != nil {
return group, err
return applied, err
}
}
if m.markAppliedOnSuccess {
if err := m.MarkApplied(ctx, migration); err != nil {
return applied, err
}
// Add to the group after upping the migration when marking applied on success
applied.Migrations = append(applied.Migrations, *migration)
}
}
return group, nil
return applied, nil
}
func (m *Migrator) Rollback(ctx context.Context, opts ...MigrationOption) (*MigrationGroup, error) {
@@ -171,11 +250,6 @@ func (m *Migrator) Rollback(ctx context.Context, opts ...MigrationOption) (*Migr
return nil, err
}
if err := m.Lock(ctx); err != nil {
return nil, err
}
defer m.Unlock(ctx) //nolint:errcheck
migrations, err := m.MigrationsWithStatus(ctx)
if err != nil {
return nil, err
@@ -183,17 +257,35 @@ func (m *Migrator) Rollback(ctx context.Context, opts ...MigrationOption) (*Migr
lastGroup := migrations.LastGroup()
// Create empty group to track unapplied migrations
unapplied := new(MigrationGroup)
unapplied.ID = lastGroup.ID
for i := len(lastGroup.Migrations) - 1; i >= 0; i-- {
migration := &lastGroup.Migrations[i]
if !m.markAppliedOnSuccess {
if err := m.MarkUnapplied(ctx, migration); err != nil {
return unapplied, err
}
// Add to the group before downing the migration when always marking unapplied
unapplied.Migrations = append(unapplied.Migrations, *migration)
}
if !cfg.nop && migration.Down != nil {
if err := migration.Down(ctx, m.db); err != nil {
return nil, err
return unapplied, err
}
}
if err := m.MarkUnapplied(ctx, migration); err != nil {
return nil, err
if m.markAppliedOnSuccess {
if err := m.MarkUnapplied(ctx, migration); err != nil {
return unapplied, err
}
// Add to the group after downing the migration when marking unapplied on success
unapplied.Migrations = append(unapplied.Migrations, *migration)
}
}
@@ -301,7 +393,7 @@ func (m *Migrator) MarkApplied(ctx context.Context, migration *Migration) error
migration.MigratedAt = time.Now()
_, err := m.db.NewInsert().
Model(migration).
ModelTableExpr(m.table).
ModelTable(m.distTable(m.migrationsTable)).
Exec(ctx)
return err
}
@@ -311,18 +403,41 @@ func (m *Migrator) MarkUnapplied(ctx context.Context, migration *Migration) erro
migration.Sign = -1
_, err := m.db.NewInsert().
Model(migration).
ModelTableExpr(m.table).
ModelTable(m.distTable(m.migrationsTable)).
Exec(ctx)
return err
}
// selectAppliedMigrations selects applied (applied) migrations in descending order.
func (m *Migrator) selectAppliedMigrations(ctx context.Context) (MigrationSlice, error) {
func (m *Migrator) TruncateTable(ctx context.Context) error {
_, err := m.db.Exec("TRUNCATE TABLE ?", ch.Ident(m.distTable(m.migrationsTable)))
return err
}
// MissingMigrations returns applied migrations that can no longer be found.
func (m *Migrator) MissingMigrations(ctx context.Context) (MigrationSlice, error) {
applied, err := m.AppliedMigrations(ctx)
if err != nil {
return nil, err
}
existing := migrationMap(m.migrations.ms)
for i := len(applied) - 1; i >= 0; i-- {
m := &applied[i]
if _, ok := existing[m.Name]; ok {
applied = append(applied[:i], applied[i+1:]...)
}
}
return applied, nil
}
// AppliedMigrations selects applied migrations in descending order.
func (m *Migrator) AppliedMigrations(ctx context.Context) (MigrationSlice, error) {
var ms MigrationSlice
if err := m.db.NewSelect().
ColumnExpr("*").
Model(&ms).
ModelTableExpr(m.table).
ModelTable(m.distTable(m.migrationsTable)).
Final().
Scan(ctx); err != nil {
return nil, err
@@ -330,10 +445,6 @@ func (m *Migrator) selectAppliedMigrations(ctx context.Context) (MigrationSlice,
return ms, nil
}
func (m *Migrator) formattedTableName(db *ch.DB) string {
return db.Formatter().FormatQuery(m.table)
}
func (m *Migrator) validate() error {
if len(m.ms) == 0 {
return errors.New("chmigrate: there are no any migrations")
@@ -341,6 +452,13 @@ func (m *Migrator) validate() error {
return nil
}
func (m *Migrator) distTable(table string) string {
if m.distributed {
return table + "_dist"
}
return table
}
//------------------------------------------------------------------------------
type migrationLock struct {
@@ -351,7 +469,7 @@ func (m *Migrator) Lock(ctx context.Context) error {
if _, err := m.db.ExecContext(
ctx,
"ALTER TABLE ? ADD COLUMN ? Int8",
ch.Safe(m.locksTable), ch.Safe("col1"),
ch.Safe(m.locksTable), ch.Safe("lock"),
); err != nil {
return fmt.Errorf("chmigrate: migrations table is already locked (%w)", err)
}
@@ -362,7 +480,7 @@ func (m *Migrator) Unlock(ctx context.Context) error {
if _, err := m.db.ExecContext(
ctx,
"ALTER TABLE ? DROP COLUMN ?",
ch.Safe(m.locksTable), ch.Safe("col1"),
ch.Safe(m.locksTable), ch.Safe("lock"),
); err != nil && !strings.Contains(err.Error(), "Cannot find column") {
return fmt.Errorf("chmigrate: migrations table is already unlocked (%w)", err)
}

View File

@@ -6,17 +6,20 @@ replace github.com/uptrace/go-clickhouse => ./..
replace github.com/uptrace/go-clickhouse/chdebug => ../chdebug
exclude go.opentelemetry.io/proto/otlp v0.15.0
require (
github.com/uptrace/go-clickhouse v0.2.0
go.opentelemetry.io/otel v1.5.0
go.opentelemetry.io/otel/trace v1.5.0
github.com/uptrace/go-clickhouse v0.3.1
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/trace v1.16.0
)
require (
github.com/codemodus/kace v0.5.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
)

View File

@@ -1,35 +1,29 @@
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y=
github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA=
github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.opentelemetry.io/otel v1.5.0 h1:DhCU8oR2sJH9rfnwPdoV/+BJ7UIN5kXHL8DuSGrPU8E=
go.opentelemetry.io/otel v1.5.0/go.mod h1:Jm/m+rNp/z0eqJc74H7LPwQ3G87qkU/AnnAydAjSAHk=
go.opentelemetry.io/otel/trace v1.5.0 h1:AKQZ9zJsBRFAp7zLdyGNkqG2rToCDIt3i5tcLzQlbmU=
go.opentelemetry.io/otel/trace v1.5.0/go.mod h1:sq55kfhjXYr1zVSyexg0w1mpa03AYXR5eyTkB9NPPdE=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 h1:s/+U+w0teGzcoH2mdIlFQ6KfVKGaYpgyGdUefZrn9TU=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 h1:8IVLkfbr2cLhv0a/vKq4UFUcJym8RmDoDboxCFWEjYE=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -7,17 +7,19 @@ replace github.com/uptrace/go-clickhouse => ../..
replace github.com/uptrace/go-clickhouse/chdebug => ../../chdebug
require (
github.com/uptrace/go-clickhouse v0.2.0
github.com/uptrace/go-clickhouse/chdebug v0.2.0
github.com/uptrace/go-clickhouse v0.3.1
github.com/uptrace/go-clickhouse/chdebug v0.3.1
)
require (
github.com/codemodus/kace v0.5.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/sys v0.8.0 // indirect
)

View File

@@ -2,26 +2,28 @@ github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnL
github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA=
github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 h1:s/+U+w0teGzcoH2mdIlFQ6KfVKGaYpgyGdUefZrn9TU=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -1,4 +1,4 @@
# go-clickhouse benchmark examples
These examples allow to compare performance with
[clickhouse-go](https://github.com/ClickHouse/clickhouse-go/tree/v2/benchmark/v2).
[clickhouse-go](https://github.com/ClickHouse/clickhouse-go/tree/main/benchmark/v2).

View File

@@ -6,18 +6,13 @@ replace github.com/uptrace/go-clickhouse => ../..
replace github.com/uptrace/go-clickhouse/chdebug => ../../chdebug
require (
github.com/uptrace/go-clickhouse v0.2.0
github.com/uptrace/go-clickhouse/chdebug v0.2.0
)
require github.com/uptrace/go-clickhouse v0.3.1
require (
github.com/codemodus/kace v0.5.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
)

View File

@@ -2,26 +2,21 @@ github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnL
github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA=
github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 h1:s/+U+w0teGzcoH2mdIlFQ6KfVKGaYpgyGdUefZrn9TU=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -6,7 +6,6 @@ import (
"time"
"github.com/uptrace/go-clickhouse/ch"
"github.com/uptrace/go-clickhouse/chdebug"
)
const query = `
@@ -40,11 +39,10 @@ func benchmark(ctx context.Context, db *ch.DB) error {
func main() {
ctx := context.Background()
db := ch.Connect(ch.WithDatabase("test"))
db.AddQueryHook(chdebug.NewQueryHook(
chdebug.WithEnabled(false),
chdebug.FromEnv(""),
))
db := ch.Connect(
ch.WithDatabase("test"),
ch.WithCompression(false),
)
start := time.Now()
if err := benchmark(ctx, db); err != nil {

View File

@@ -6,7 +6,6 @@ import (
"time"
"github.com/uptrace/go-clickhouse/ch"
"github.com/uptrace/go-clickhouse/chdebug"
)
type Model struct {
@@ -35,11 +34,10 @@ func benchmark(ctx context.Context, db *ch.DB) error {
func main() {
ctx := context.Background()
db := ch.Connect(ch.WithDatabase("test"))
db.AddQueryHook(chdebug.NewQueryHook(
chdebug.WithEnabled(false),
chdebug.FromEnv(""),
))
db := ch.Connect(
ch.WithDatabase("test"),
ch.WithCompression(false),
)
if err := db.ResetModel(ctx, (*Model)(nil)); err != nil {
panic(err)

View File

@@ -36,4 +36,5 @@ To create a SQL migration:
go run . db create_sql sql_migration_name
```
See [docs](https://clickhouse.uptrace.dev/guide/migrations.html) for details.
See [ClickHouse migrations](https://clickhouse.uptrace.dev/guide/clickhouse-migrations.html) for
details.

View File

@@ -7,20 +7,23 @@ replace github.com/uptrace/go-clickhouse => ../..
replace github.com/uptrace/go-clickhouse/chdebug => ../../chdebug
require (
github.com/uptrace/go-clickhouse v0.2.0
github.com/uptrace/go-clickhouse/chdebug v0.2.0
github.com/urfave/cli/v2 v2.4.0
github.com/uptrace/go-clickhouse v0.3.1
github.com/uptrace/go-clickhouse/chdebug v0.3.1
github.com/urfave/cli/v2 v2.25.5
)
require (
github.com/codemodus/kace v0.5.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/sys v0.8.0 // indirect
)

View File

@@ -1,36 +1,37 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y=
github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA=
github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04=
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/urfave/cli/v2 v2.4.0 h1:m2pxjjDFgDxSPtO8WSdbndj17Wu2y8vOT86wE/tjr+I=
github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 h1:s/+U+w0teGzcoH2mdIlFQ6KfVKGaYpgyGdUefZrn9TU=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc=
github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -49,15 +49,23 @@ func newDBCommand(db *ch.DB, migrator *chmigrate.Migrator) *cli.Command {
Name: "migrate",
Usage: "migrate database",
Action: func(c *cli.Context) error {
if err := migrator.Lock(c.Context); err != nil {
return err
}
defer migrator.Unlock(c.Context) //nolint:errcheck
group, err := migrator.Migrate(c.Context)
if err != nil {
return err
}
if group.IsZero() {
fmt.Printf("there are no new migrations to run (database is up to date)\n")
return nil
}
fmt.Printf("migrated to %s\n", group)
return nil
},
},
@@ -65,15 +73,23 @@ func newDBCommand(db *ch.DB, migrator *chmigrate.Migrator) *cli.Command {
Name: "rollback",
Usage: "rollback the last migration group",
Action: func(c *cli.Context) error {
if err := migrator.Lock(c.Context); err != nil {
return err
}
defer migrator.Unlock(c.Context) //nolint:errcheck
group, err := migrator.Rollback(c.Context)
if err != nil {
return err
}
if group.IsZero() {
fmt.Printf("there are no groups to roll back\n")
return nil
}
fmt.Printf("rolled back %s\n", group)
return nil
},
},

View File

@@ -1,7 +1,10 @@
# Example for go-clickhouse OpenTelemetry instrumentation
See [Performance and errors monitoring](https://clickhouse.uptrace.dev/guide/monitoring.html) for
details.
To run this example, you need to create `test` ClickHouse database:
```shell
clickhouse-client -q "CREATE DATABASE test"
```
You can run this example with different OpenTelemetry exporters by providing environment variables.
@@ -23,6 +26,10 @@ OTEL_EXPORTER_JAEGER_ENDPOINT=http://localhost:14268/api/traces go run .
UPTRACE_DSN="https://<token>@uptrace.dev/<project_id>" go run .
```
See
[Performance and errors monitoring](https://clickhouse.uptrace.dev/guide/clickhouse-monitoring-performance.html)
for details.
## Links
- [Find instrumentations](https://opentelemetry.uptrace.dev/instrumentations/?lang=go)

View File

@@ -8,43 +8,46 @@ replace github.com/uptrace/go-clickhouse/chdebug => ../../chdebug
replace github.com/uptrace/go-clickhouse/chotel => ../../chotel
exclude go.opentelemetry.io/proto/otlp v0.15.0
require (
github.com/brianvoe/gofakeit/v5 v5.11.2
github.com/uptrace/go-clickhouse v0.2.0
github.com/uptrace/go-clickhouse/chotel v0.2.0
github.com/uptrace/opentelemetry-go-extra/otelplay v0.1.10
go.opentelemetry.io/otel v1.5.0
github.com/uptrace/go-clickhouse v0.3.1
github.com/uptrace/go-clickhouse/chotel v0.3.1
github.com/uptrace/opentelemetry-go-extra/otelplay v0.2.1
go.opentelemetry.io/otel v1.16.0
)
require (
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/codemodus/kace v0.5.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/uptrace/uptrace-go v1.4.0 // indirect
go.opentelemetry.io/contrib/instrumentation/runtime v0.30.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.5.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.5.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.5.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.5.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.5.0 // indirect
go.opentelemetry.io/otel/internal/metric v0.27.0 // indirect
go.opentelemetry.io/otel/metric v0.27.0 // indirect
go.opentelemetry.io/otel/sdk v1.5.0 // indirect
go.opentelemetry.io/otel/sdk/metric v0.27.0 // indirect
go.opentelemetry.io/otel/trace v1.5.0 // indirect
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
google.golang.org/grpc v1.45.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/uptrace/uptrace-go v1.16.0 // indirect
go.opentelemetry.io/contrib/instrumentation/runtime v0.42.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.39.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/sdk v1.16.0 // indirect
go.opentelemetry.io/otel/sdk/metric v0.39.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e // indirect
google.golang.org/grpc v1.55.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
)

View File

@@ -1,20 +1,56 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y=
github.com/brianvoe/gofakeit/v5 v5.11.2 h1:Ny5Nsf4z2023ZvYP8ujW8p5B1t5sxhdFaQ/0IYXbeSA=
github.com/brianvoe/gofakeit/v5 v5.11.2/go.mod h1:/ZENnKqX+XrN8SORLe/fu5lZDIo1tuPncWuRD+eyhSI=
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@@ -26,20 +62,38 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
@@ -49,154 +103,350 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/uptrace/opentelemetry-go-extra/otelplay v0.1.10 h1:STSRpcp18xb6huePD0yj4uiUFd1cOdb2xexhhPNWwCo=
github.com/uptrace/opentelemetry-go-extra/otelplay v0.1.10/go.mod h1:7UDvwITVMKhXZGmxKa/lJujNIOKb5ReIRAVwwrc60bI=
github.com/uptrace/uptrace-go v1.4.0 h1:WPYpiCi84nSAXVdtZQamcmDnTZz9724EONo8v1ud9/0=
github.com/uptrace/uptrace-go v1.4.0/go.mod h1:KAPqyJuSaRA6YOea6UZEuSeXtWap92Qws9rPYNW8GhU=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/contrib/instrumentation/runtime v0.30.0 h1:chMoQpB7M6RQs0DrlblcJ7oC+SuGf9p4O1HfigUiOms=
go.opentelemetry.io/contrib/instrumentation/runtime v0.30.0/go.mod h1:qGVpMUNpULYYXRYCQB3DNUe+of22Y+M28i1Oke8SuKQ=
go.opentelemetry.io/otel v1.4.0/go.mod h1:jeAqMFKy2uLIxCtKxoFj0FAL5zAPKQagc3+GtBWakzk=
go.opentelemetry.io/otel v1.5.0 h1:DhCU8oR2sJH9rfnwPdoV/+BJ7UIN5kXHL8DuSGrPU8E=
go.opentelemetry.io/otel v1.5.0/go.mod h1:Jm/m+rNp/z0eqJc74H7LPwQ3G87qkU/AnnAydAjSAHk=
go.opentelemetry.io/otel/exporters/jaeger v1.5.0 h1:ZR7nhLSfLufS5AHk/iN11Q+W9XYwsJrVZ1Frb833d+Y=
go.opentelemetry.io/otel/exporters/jaeger v1.5.0/go.mod h1:rSeUArMBRe1eQLo1T0WxOazohN1M2mYThWJQmn1BjRQ=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.5.0 h1:lC0ldaVQwBpO1G5IaOYRbBCa67h6ioGkK6qYkqZbYOI=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.5.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0 h1:t1aPfMj5oZzv2EaRmdC2QPQg1a7MaBjraOh4Hjwuia8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.27.0/go.mod h1:aZnoYVx7GIuMROciGC3cjZhYxMD/lKroRJUnFY0afu0=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0 h1:RJURCSrqUjJiCY3GuFCVP2EPKOQLwNXQ4FI3aH2KoHg=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0/go.mod h1:LIc1eCpkU94tPnXxH40ya41Oyxm7sL+oDvxCYPFpnV8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.5.0 h1:Arn+HOtC6neocvr6J4ykfILvtiSwoDkkLFMaVLFKBnY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.5.0/go.mod h1:VoN81wyy6jVVCzHImh8S+IYhw+oAUj6XgEsTkP8DyrQ=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.5.0 h1:dOXExSS490NJaVZD496oIK2Z22S1JQnOsrrMh/p/mLU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.5.0/go.mod h1:Es/Ag4ORtjwWCRjS0aEXgmxB5VqKQlnp481/P5aZyPQ=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.5.0 h1:/Lu2JuL9Mb+B+kSv/RsDMgA/5FaBaxfyfMnICFepiBs=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.5.0/go.mod h1:5gUXICq93HyDh8Rij7p8ilJEC1Sqk0u3lSGs62i8hJQ=
go.opentelemetry.io/otel/internal/metric v0.27.0 h1:9dAVGAfFiiEq5NVB9FUJ5et+btbDQAUIJehJ+ikyryk=
go.opentelemetry.io/otel/internal/metric v0.27.0/go.mod h1:n1CVxRqKqYZtqyTh9U/onvKapPGv7y/rpyOTI+LFNzw=
go.opentelemetry.io/otel/metric v0.27.0 h1:HhJPsGhJoKRSegPQILFbODU56NS/L1UE4fS1sC5kIwQ=
go.opentelemetry.io/otel/metric v0.27.0/go.mod h1:raXDJ7uP2/Jc0nVZWQjJtzoyssOYWu/+pjZqRzfvZ7g=
go.opentelemetry.io/otel/sdk v1.4.0/go.mod h1:71GJPNJh4Qju6zJuYl1CrYtXbrgfau/M9UAggqiy1UE=
go.opentelemetry.io/otel/sdk v1.5.0 h1:QKhWBbcOC9fDCZKCfPFjWTWpfIlJR+i9xiUDYrLVmZs=
go.opentelemetry.io/otel/sdk v1.5.0/go.mod h1:CU4J1v+7iEljnm1G14QjdFWOXUyYLHVh0Lh+/BTYyFg=
go.opentelemetry.io/otel/sdk/metric v0.27.0 h1:CDEu96Js5IP7f4bJ8eimxF09V5hKYmE7CeyKSjmAL1s=
go.opentelemetry.io/otel/sdk/metric v0.27.0/go.mod h1:lOgrT5C3ORdbqp2LsDrx+pBj6gbZtQ5Omk27vH3EaW0=
go.opentelemetry.io/otel/trace v1.4.0/go.mod h1:uc3eRsqDfWs9R7b92xbQbU42/eTNz4N+gLP8qJCi4aE=
go.opentelemetry.io/otel/trace v1.5.0 h1:AKQZ9zJsBRFAp7zLdyGNkqG2rToCDIt3i5tcLzQlbmU=
go.opentelemetry.io/otel/trace v1.5.0/go.mod h1:sq55kfhjXYr1zVSyexg0w1mpa03AYXR5eyTkB9NPPdE=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/uptrace/opentelemetry-go-extra/otelplay v0.2.1 h1:HOqi3aZ4VnduY66JatQnejArkmSlHOdksSezbJQ2Kx0=
github.com/uptrace/opentelemetry-go-extra/otelplay v0.2.1/go.mod h1:VkRwJRe6BQeaX9FhNKil09LQSazSeC9NYz+P//Rhr2E=
github.com/uptrace/uptrace-go v1.16.0 h1:yB9vt1hBYYoXWExNx0okubLOjd339d7lH+/5o+Lp+MY=
github.com/uptrace/uptrace-go v1.16.0/go.mod h1:Ssc5wLpoL+9V0qkT5FtrIiru9SY4xb7q1UVLjSpxpCg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/contrib/instrumentation/runtime v0.42.0 h1:EbmAUG9hEAMXyfWEasIt2kmh/WmXUznUksChApTgBGc=
go.opentelemetry.io/contrib/instrumentation/runtime v0.42.0/go.mod h1:rD9feqRYP24P14t5kmhNMqsqm1jvKmpx2H2rKVw52V8=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/exporters/jaeger v1.16.0 h1:YhxxmXZ011C0aDZKoNw+juVWAmEfv/0W2XBOv9aHTaA=
go.opentelemetry.io/otel/exporters/jaeger v1.16.0/go.mod h1:grYbBo/5afWlPpdPZYhyn78Bk04hnvxn2+hvxQhKIQM=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 h1:t4ZwRPU+emrcvM2e9DHd0Fsf0JTPVcbfa/BhTDF03d0=
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0/go.mod h1:vLarbg68dH2Wa77g71zmKQqlQ8+8Rq3GRG31uc0WcWI=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 h1:f6BwB2OACc3FCbYVznctQ9V6KK7Vq6CjmYXJ7DeSs4E=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0/go.mod h1:UqL5mZ3qs6XYhDnZaW1Ps4upD+PX6LipH40AoeuIlwU=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.39.0 h1:rm+Fizi7lTM2UefJ1TO347fSRcwmIsUAaZmYmIGBRAo=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.39.0/go.mod h1:sWFbI3jJ+6JdjOVepA5blpv/TJ20Hw+26561iMbWcwU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 h1:cbsD4cUcviQGXdw8+bo5x2wazq10SKz8hEbtCRPcU78=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0/go.mod h1:JgXSGah17croqhJfhByOLVY719k1emAXC8MVhCIJlRs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 h1:TVQp/bboR4mhZSav+MdgXB8FaRho1RC8UwVn3T0vjVc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0/go.mod h1:I33vtIe0sR96wfrUcilIzLoA3mLHhRmz9S9Te0S3gDo=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 h1:+XWJd3jf75RXJq29mxbuXhCXFDG3S3R4vBUeSI2P7tE=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0/go.mod h1:hqgzBPTf4yONMFgdZvL/bK42R/iinTyVQtiWihs3SZc=
go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE=
go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4=
go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI=
go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.12.0 h1:CMJ/3Wp7iOWES+CYLfnBv+DVmPbB+kmy9PJ92XvlR6c=
go.opentelemetry.io/proto/otlp v0.12.0/go.mod h1:TsIjwGWIx5VFYv9KGVlOpxoBl5Dy+63SUguV7GGvlSQ=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 h1:s/+U+w0teGzcoH2mdIlFQ6KfVKGaYpgyGdUefZrn9TU=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e h1:fNKDNuUyC4WH+inqDMpfXDdfvwfYILbsX+oskGZ8hxg=
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e h1:Ao9GzfUMPH3zjVfzXG5rlWlk+Q8MXWKwWpwVQE1MXfw=
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e h1:AZX1ra8YbFMSb7+1pI8S9v4rrgRR7jU1FmuFSSjTVcQ=
google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e h1:NumxXLPfHSndr3wBBdeKiVHjGVFzi9RX2HwwQke94iY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -205,16 +455,27 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -6,4 +6,6 @@ To run this example:
go run .
```
See [docs](https://clickhouse.uptrace.dev/guide/placeholders.html) for details.
See
[ClickHouse SQL placeholders](https://clickhouse.uptrace.dev/guide/clickhouse-sql-placeholders.html)
for details.

View File

@@ -7,17 +7,19 @@ replace github.com/uptrace/go-clickhouse => ../..
replace github.com/uptrace/go-clickhouse/chdebug => ../../chdebug
require (
github.com/uptrace/go-clickhouse v0.2.0
github.com/uptrace/go-clickhouse/chdebug v0.2.0
github.com/uptrace/go-clickhouse v0.3.1
github.com/uptrace/go-clickhouse/chdebug v0.3.1
)
require (
github.com/codemodus/kace v0.5.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/sys v0.8.0 // indirect
)

View File

@@ -2,26 +2,28 @@ github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnL
github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA=
github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 h1:s/+U+w0teGzcoH2mdIlFQ6KfVKGaYpgyGdUefZrn9TU=
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

20
go.mod
View File

@@ -8,20 +8,22 @@ require (
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible
github.com/codemodus/kace v0.5.1
github.com/jinzhu/inflection v1.0.0
github.com/pierrec/lz4/v4 v4.1.14
github.com/stretchr/testify v1.7.0
github.com/uptrace/go-clickhouse/chdebug v0.0.0-00010101000000-000000000000
golang.org/x/exp v0.0.0-20220307200941-a1099baf94bf
github.com/pierrec/lz4/v4 v4.1.17
github.com/stretchr/testify v1.8.3
github.com/uptrace/go-clickhouse/chdebug v0.3.1
go.opentelemetry.io/otel/trace v1.16.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
golang.org/x/sys v0.8.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

49
go.sum
View File

@@ -2,11 +2,11 @@ github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnL
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible/go.mod h1:Au1Xw1sgaJ5iSFktEhYsS0dbQiS1B0/XMXl+42y9Ilk=
github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA=
github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@@ -14,30 +14,29 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/exp v0.0.0-20220307200941-a1099baf94bf h1:IoCgLaQjznvljAcEatPiHdqZi9oIBU5w0AJrlRkzX7s=
golang.org/x/exp v0.0.0-20220307200941-a1099baf94bf/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 h1:8IVLkfbr2cLhv0a/vKq4UFUcJym8RmDoDboxCFWEjYE=
golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,6 +1,6 @@
{
"name": "uptrace/go-clickhouse",
"version": "0.2.0",
"name": "goclickhouse",
"version": "0.3.1",
"main": "index.js",
"repository": "git@github.com:uptrace/go-clickhouse.git",
"author": "Vladimir Mihailenco <vladimir.webdev@gmail.com>",

View File

@@ -41,24 +41,22 @@ fi
git checkout master
PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; \
PACKAGE_DIRS=$(find . -mindepth 1 -type f -name 'go.mod' -exec dirname {} \; \
| sed 's/^\.\///' \
| sort)
for dir in $PACKAGE_DIRS
do
printf "${dir}: go get -u && go mod tidy\n"
(cd ./${dir} && go get -u && go mod tidy)
printf "${dir}: go get -u && go mod tidy -compat=1.18\n"
(cd ${dir} && go mod tidy -compat=1.18)
done
for dir in $PACKAGE_DIRS
do
sed --in-place \
"s/uptrace\/go-clickhouse\([^ ]*\) v.*/uptrace\/go-clickhouse\1 ${TAG}/" "${dir}/go.mod"
(cd ./${dir} && go mod tidy)
done
sed --in-place "s/\(return \)\"[^\"]*\"/\1\"${TAG#v}\"/" ./version.go
sed --in-place "s/\(\"version\": \)\"[^\"]*\"/\1\"${TAG#v}\"/" ./package.json

View File

@@ -2,5 +2,5 @@ package clickhouse
// Version is the current release version.
func Version() string {
return "0.2.0"
return "0.3.1"
}