From 47272104fe59162dfb157b3825ab193c62bdb448 Mon Sep 17 00:00:00 2001 From: Jan Was Date: Sun, 10 Jul 2022 18:05:22 +0200 Subject: [PATCH] Support passing slices as query args --- README.md | 25 +++++++++++++++++++++++- trino/integration_test.go | 41 +++++++++++++++++++++++++++++++++++++++ trino/trino.go | 15 +++++++++++--- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b289da0..0706547 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,30 @@ https://user@localhost:8443?session_properties=query_max_run_time=10m,query_prio ## Data types -The driver supports most Trino data types, except: +### Query arguments + +When passing arguments to queries, the driver supports the following Go data types: +* integers +* `bool` +* `string` +* slices + +It's not yet possible to pass: +* `nil` +* `float32` or `float64` +* `byte` +* `time.Time` or `time.Duration` +* `json.RawMessage` +* maps + +To use the unsupported types, pass them as strings and use casts in the query, like so: +```sql +SELECT * FROM table WHERE col_double = cast(? AS DOUBLE) OR col_timestamp = CAST(? AS TIMESTAMP) +``` + +### Response rows + +When reading response rows, the driver supports most Trino data types, except: * time and timestamps with precision - all time types are returned as `time.Time` * `DECIMAL` - returned as string * `IPADDRESS` - returned as string diff --git a/trino/integration_test.go b/trino/integration_test.go index c986078..506b72b 100644 --- a/trino/integration_test.go +++ b/trino/integration_test.go @@ -391,6 +391,47 @@ func TestIntegrationTypeConversion(t *testing.T) { } } +func TestIntegrationArgsConversion(t *testing.T) { + dsn := *integrationServerFlag + dsn += "?session_properties=parse_decimal_literals_as_double=true" + db := integrationOpen(t, dsn) + value := 0 + err := db.QueryRow(` + SELECT 1 FROM (VALUES ( + CAST(1 AS TINYINT), + CAST(1 AS SMALLINT), + CAST(1 AS INTEGER), + CAST(1 AS BIGINT), + CAST(1 AS REAL), + CAST(1 AS DOUBLE), + TIMESTAMP '2017-07-10 01:02:03.004 UTC', + CAST('string' AS VARCHAR), + ARRAY['A', 'B'] + )) AS t(col_tiny, col_small, col_int, col_big, col_real, col_double, col_ts, col_varchar, col_array ) + WHERE 1=1 + AND col_tiny = ? + AND col_small = ? + AND col_int = ? + AND col_big = ? + AND col_real = cast(? as real) + AND col_double = cast(? as double) + AND col_ts = cast(? as timestamp) + AND col_varchar = ? + AND col_array = ?`, + int16(1), + int16(1), + int32(1), + int64(1), + Numeric("1"), + Numeric("1"), + "2017-07-10 01:02:03.004 UTC", + "string", + []string{"A", "B"}).Scan(&value) + if err != nil { + t.Fatal(err) + } +} + func TestIntegrationNoResults(t *testing.T) { db := integrationOpen(t) rows, err := db.Query("SELECT 1 LIMIT 0") diff --git a/trino/trino.go b/trino/trino.go index 99bc513..936d753 100644 --- a/trino/trino.go +++ b/trino/trino.go @@ -65,6 +65,7 @@ import ( "math" "net/http" "net/url" + "reflect" "regexp" "sort" "strconv" @@ -544,9 +545,10 @@ type driverStmt struct { } var ( - _ driver.Stmt = &driverStmt{} - _ driver.StmtQueryContext = &driverStmt{} - _ driver.StmtExecContext = &driverStmt{} + _ driver.Stmt = &driverStmt{} + _ driver.StmtQueryContext = &driverStmt{} + _ driver.StmtExecContext = &driverStmt{} + _ driver.NamedValueChecker = &driverStmt{} ) func (st *driverStmt) Close() error { @@ -583,6 +585,13 @@ func (st *driverStmt) ExecContext(ctx context.Context, args []driver.NamedValue) return rows, nil } +func (st *driverStmt) CheckNamedValue(arg *driver.NamedValue) error { + if reflect.TypeOf(arg.Value).Kind() == reflect.Slice { + return nil + } + return driver.ErrSkip +} + type stmtResponse struct { ID string `json:"id"` InfoURI string `json:"infoUri"`