Skip to content

Commit

Permalink
Merge pull request #1454 from ClickHouse/dynamic_type
Browse files Browse the repository at this point in the history
Add Dynamic Type
  • Loading branch information
SpencerTorres authored Jan 13, 2025
2 parents d7cae99 + ecd8d93 commit 75a2e2a
Show file tree
Hide file tree
Showing 20 changed files with 1,911 additions and 9 deletions.
11 changes: 11 additions & 0 deletions chcol.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import "github.com/ClickHouse/clickhouse-go/v2/lib/chcol"

type (
Variant = chcol.Variant
Dynamic = chcol.Dynamic
)

// NewVariant creates a new Variant with the given value
Expand All @@ -34,3 +35,13 @@ func NewVariant(v any) Variant {
func NewVariantWithType(v any, chType string) Variant {
return chcol.NewVariantWithType(v, chType)
}

// NewDynamic creates a new Dynamic with the given value
func NewDynamic(v any) Dynamic {
return chcol.NewDynamic(v)
}

// NewDynamicWithType creates a new Dynamic with the given value and ClickHouse type
func NewDynamicWithType(v any, chType string) Dynamic {
return chcol.NewDynamicWithType(v, chType)
}
141 changes: 141 additions & 0 deletions examples/clickhouse_api/dynamic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Licensed to ClickHouse, Inc. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. ClickHouse, Inc. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package clickhouse_api

import (
"context"
"fmt"
"github.com/ClickHouse/clickhouse-go/v2"
)

func DynamicExample() error {
ctx := context.Background()

conn, err := GetNativeConnection(clickhouse.Settings{
"allow_experimental_dynamic_type": true,
}, nil, nil)
if err != nil {
return err
}

if !CheckMinServerVersion(conn, 24, 8, 0) {
fmt.Print("unsupported clickhouse version for Dynamic type")
return nil
}

err = conn.Exec(ctx, "DROP TABLE IF EXISTS go_dynamic_example")
if err != nil {
return err
}

err = conn.Exec(ctx, `
CREATE TABLE go_dynamic_example (
c Dynamic
) ENGINE = Memory
`)
if err != nil {
return err
}

batch, err := conn.PrepareBatch(ctx, "INSERT INTO go_dynamic_example (c)")
if err != nil {
return err
}

if err = batch.Append(true); err != nil {
return err
}

if err = batch.Append(int64(42)); err != nil {
return err
}

if err = batch.Append("example"); err != nil {
return err
}

if err = batch.Append(clickhouse.NewDynamic("example dynamic")); err != nil {
return err
}

if err = batch.Append(clickhouse.NewDynamicWithType("example dynamic with specific type", "String")); err != nil {
return err
}

if err = batch.Append(nil); err != nil {
return err
}

if err = batch.Send(); err != nil {
return err
}

// Switch on Go Type

rows, err := conn.Query(ctx, "SELECT c FROM go_dynamic_example")
if err != nil {
return err
}

for i := 0; rows.Next(); i++ {
var row clickhouse.Dynamic
err := rows.Scan(&row)
if err != nil {
return fmt.Errorf("failed to scan row index %d: %w", i, err)
}

switch row.Any().(type) {
case bool:
fmt.Printf("row at index %d is Bool: %v\n", i, row.Any())
case int64:
fmt.Printf("row at index %d is Int64: %v\n", i, row.Any())
case string:
fmt.Printf("row at index %d is String: %v\n", i, row.Any())
case nil:
fmt.Printf("row at index %d is NULL\n", i)
}
}

// Switch on ClickHouse Type

rows, err = conn.Query(ctx, "SELECT c FROM go_dynamic_example")
if err != nil {
return err
}

for i := 0; rows.Next(); i++ {
var row clickhouse.Dynamic
err := rows.Scan(&row)
if err != nil {
return fmt.Errorf("failed to scan row index %d: %w", i, err)
}

switch row.Type() {
case "Bool":
fmt.Printf("row at index %d is bool: %v\n", i, row.Any())
case "Int64":
fmt.Printf("row at index %d is int64: %v\n", i, row.Any())
case "String":
fmt.Printf("row at index %d is string: %v\n", i, row.Any())
case "":
fmt.Printf("row at index %d is nil\n", i)
}
}

return nil
}
5 changes: 4 additions & 1 deletion examples/clickhouse_api/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ func TestSSLNoVerify(t *testing.T) {
}

func TestVariantExample(t *testing.T) {
t.Skip("unsupported version")
require.NoError(t, VariantExample())
}

func TestDynamicExample(t *testing.T) {
require.NoError(t, DynamicExample())
}
4 changes: 4 additions & 0 deletions examples/clickhouse_api/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ func GetNativeTestEnvironment() (clickhouse_tests.ClickHouseTestEnvironment, err
func GetNativeConnectionWithOptions(settings clickhouse.Settings, tlsConfig *tls.Config, compression *clickhouse.Compression) (driver.Conn, error) {
return clickhouse_tests.GetConnection(TestSet, settings, tlsConfig, compression)
}

func CheckMinServerVersion(conn driver.Conn, major, minor, patch uint64) bool {
return clickhouse_tests.CheckMinServerServerVersion(conn, major, minor, patch)
}
5 changes: 5 additions & 0 deletions examples/clickhouse_api/variant.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ func VariantExample() error {
return err
}

if !CheckMinServerVersion(conn, 24, 4, 0) {
fmt.Print("unsupported clickhouse version for Variant type")
return nil
}

err = conn.Exec(ctx, "DROP TABLE IF EXISTS go_variant_example")
if err != nil {
return err
Expand Down
153 changes: 153 additions & 0 deletions examples/std/dynamic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Licensed to ClickHouse, Inc. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. ClickHouse, Inc. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package std

import (
"context"
"fmt"
"github.com/ClickHouse/clickhouse-go/v2"
)

func DynamicExample() error {
ctx := context.Background()

conn, err := GetStdOpenDBConnection(clickhouse.Native, nil, nil, nil)
if err != nil {
return err
}

if !CheckMinServerVersion(conn, 24, 8, 0) {
fmt.Print("unsupported clickhouse version for Dynamic type")
return nil
}

_, err = conn.ExecContext(ctx, "SET allow_experimental_dynamic_type = 1")
if err != nil {
return err
}

defer func() {
conn.Exec("DROP TABLE go_dynamic_example")
}()

_, err = conn.ExecContext(ctx, "DROP TABLE IF EXISTS go_dynamic_example")
if err != nil {
return err
}

_, err = conn.ExecContext(ctx, `
CREATE TABLE go_dynamic_example (
c Dynamic
) ENGINE = Memory
`)
if err != nil {
return err
}

tx, err := conn.BeginTx(ctx, nil)
if err != nil {
return err
}

batch, err := tx.PrepareContext(ctx, "INSERT INTO go_dynamic_example (c)")
if err != nil {
return err
}

if _, err = batch.ExecContext(ctx, true); err != nil {
return err
}

if _, err = batch.ExecContext(ctx, int64(42)); err != nil {
return err
}

if _, err = batch.ExecContext(ctx, "example"); err != nil {
return err
}

if _, err = batch.ExecContext(ctx, clickhouse.NewDynamic("example dynamic")); err != nil {
return err
}

if _, err = batch.ExecContext(ctx, clickhouse.NewDynamicWithType("example dynamic with specific type", "String")); err != nil {
return err
}

if _, err = batch.ExecContext(ctx, nil); err != nil {
return err
}

if err = tx.Commit(); err != nil {
return err
}

// Switch on Go Type

rows, err := conn.QueryContext(ctx, "SELECT c FROM go_dynamic_example")
if err != nil {
return err
}

for i := 0; rows.Next(); i++ {
var row clickhouse.Dynamic
err := rows.Scan(&row)
if err != nil {
return fmt.Errorf("failed to scan row index %d: %w", i, err)
}

switch row.Any().(type) {
case bool:
fmt.Printf("row at index %d is Bool: %v\n", i, row.Any())
case int64:
fmt.Printf("row at index %d is Int64: %v\n", i, row.Any())
case string:
fmt.Printf("row at index %d is String: %v\n", i, row.Any())
case nil:
fmt.Printf("row at index %d is NULL\n", i)
}
}

// Switch on ClickHouse Type

rows, err = conn.QueryContext(ctx, "SELECT c FROM go_dynamic_example")
if err != nil {
return err
}

for i := 0; rows.Next(); i++ {
var row clickhouse.Dynamic
err := rows.Scan(&row)
if err != nil {
return fmt.Errorf("failed to scan row index %d: %w", i, err)
}

switch row.Type() {
case "Bool":
fmt.Printf("row at index %d is bool: %v\n", i, row.Any())
case "Int64":
fmt.Printf("row at index %d is int64: %v\n", i, row.Any())
case "String":
fmt.Printf("row at index %d is string: %v\n", i, row.Any())
case "":
fmt.Printf("row at index %d is nil\n", i)
}
}

return nil
}
5 changes: 4 additions & 1 deletion examples/std/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ func TestConnectionSettings(t *testing.T) {
}

func TestVariantExample(t *testing.T) {
t.Skip("unsupported version")
require.NoError(t, VariantExample())
}

func TestDynamicExample(t *testing.T) {
require.NoError(t, DynamicExample())
}
4 changes: 4 additions & 0 deletions examples/std/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ func GetStdOpenDBConnection(protocol clickhouse.Protocol, settings clickhouse.Se
func GetStdTestEnvironment() (clickhouse_tests.ClickHouseTestEnvironment, error) {
return clickhouse_tests.GetTestEnvironment(TestSet)
}

func CheckMinServerVersion(conn *sql.DB, major, minor, patch uint64) bool {
return clickhouse_tests_std.CheckMinServerVersion(conn, major, minor, patch)
}
21 changes: 17 additions & 4 deletions examples/std/variant.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,26 @@ import (
func VariantExample() error {
ctx := context.Background()

conn, err := GetStdOpenDBConnection(clickhouse.Native, clickhouse.Settings{
"allow_experimental_variant_type": true,
"allow_suspicious_variant_types": true,
}, nil, nil)
conn, err := GetStdOpenDBConnection(clickhouse.Native, nil, nil, nil)
if err != nil {
return err
}

if !CheckMinServerVersion(conn, 24, 4, 0) {
fmt.Print("unsupported clickhouse version for Variant type")
return nil
}

_, err = conn.ExecContext(ctx, "SET allow_experimental_variant_type = 1")
if err != nil {
return err
}

_, err = conn.ExecContext(ctx, "SET allow_suspicious_variant_types = 1")
if err != nil {
return err
}

defer func() {
conn.Exec("DROP TABLE go_variant_example")
}()
Expand Down
Loading

0 comments on commit 75a2e2a

Please sign in to comment.