You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by fr...@apache.org on 2018/04/16 05:02:08 UTC
[2/4] calcite-avatica-go git commit: Add HSQLDB support and move
phoenix support into an adapter
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/driver_phoenix_test.go
----------------------------------------------------------------------
diff --git a/driver_phoenix_test.go b/driver_phoenix_test.go
new file mode 100644
index 0000000..b33830b
--- /dev/null
+++ b/driver_phoenix_test.go
@@ -0,0 +1,1044 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 avatica
+
+import (
+ "bytes"
+ "crypto/sha256"
+ "database/sql"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/apache/calcite-avatica-go/errors"
+)
+
+func skipTestIfNotPhoenix(t *testing.T) {
+
+ val := os.Getenv("AVATICA_FLAVOR")
+
+ if val != "PHOENIX" {
+ t.Skip("Skipping Apache Phoenix test")
+ }
+}
+
+func TestPhoenixConnectionMustBeOpenedWithAutoCommitTrue(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ runTests(t, dsn, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec("CREATE TABLE " + dbt.tableName + " (id BIGINT PRIMARY KEY, val VARCHAR) TRANSACTIONAL=false")
+
+ dbt.mustExec("UPSERT INTO " + dbt.tableName + " VALUES (1,'A')")
+
+ dbt.mustExec("UPSERT INTO " + dbt.tableName + " VALUES (2,'B')")
+
+ rows := dbt.mustQuery("SELECT COUNT(*) FROM " + dbt.tableName)
+ defer rows.Close()
+
+ for rows.Next() {
+
+ var count int
+
+ err := rows.Scan(&count)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ if count != 2 {
+ dbt.Fatalf("There should be 2 rows, got %d", count)
+ }
+ }
+
+ })
+}
+
+func TestPhoenixZeroValues(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ runTests(t, dsn, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec("CREATE TABLE " + dbt.tableName + " (int INTEGER PRIMARY KEY, flt FLOAT, bool BOOLEAN, str VARCHAR) TRANSACTIONAL=false")
+
+ dbt.mustExec("UPSERT INTO " + dbt.tableName + " VALUES (0, 0.0, false, '')")
+
+ rows := dbt.mustQuery("SELECT * FROM " + dbt.tableName)
+ defer rows.Close()
+
+ for rows.Next() {
+
+ var i int
+ var flt float64
+ var b bool
+ var s string
+
+ err := rows.Scan(&i, &flt, &b, &s)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ if i != 0 {
+ dbt.Fatalf("Integer should be 0, got %v", i)
+ }
+
+ if flt != 0.0 {
+ dbt.Fatalf("Float should be 0.0, got %v", flt)
+ }
+
+ if b != false {
+ dbt.Fatalf("Boolean should be false, got %v", b)
+ }
+
+ if s != "" {
+ dbt.Fatalf("String should be \"\", got %v", s)
+ }
+ }
+
+ })
+}
+
+func TestPhoenixDataTypes(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ runTests(t, dsn, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY,
+ uint UNSIGNED_INT,
+ bint BIGINT,
+ ulong UNSIGNED_LONG,
+ tint TINYINT,
+ utint UNSIGNED_TINYINT,
+ sint SMALLINT,
+ usint UNSIGNED_SMALLINT,
+ flt FLOAT,
+ uflt UNSIGNED_FLOAT,
+ dbl DOUBLE,
+ udbl UNSIGNED_DOUBLE,
+ dec DECIMAL,
+ bool BOOLEAN,
+ tm TIME,
+ dt DATE,
+ tmstmp TIMESTAMP,
+ utm UNSIGNED_TIME,
+ udt UNSIGNED_DATE,
+ utmstmp UNSIGNED_TIMESTAMP,
+ var VARCHAR,
+ ch CHAR(3),
+ bin BINARY(20),
+ varbin VARBINARY
+ ) TRANSACTIONAL=false`)
+
+ var (
+ integerValue int = -20
+ uintegerValue int = 5
+ bintValue int = -9223372036854775807
+ ulongValue int = 9223372036854775807
+ tintValue int = -128
+ utintValue int = 126
+ sintValue int = -32768
+ usintValue int = 32767
+ fltValue float64 = -3.555
+ ufltValue float64 = 3.555
+ dblValue float64 = -9.555
+ udblValue float64 = 9.555
+ decValue string = "1.333"
+ booleanValue bool = true
+ tmValue time.Time = time.Date(0, 1, 1, 21, 21, 21, 222000000, time.UTC)
+ dtValue time.Time = time.Date(2100, 2, 1, 0, 0, 0, 0, time.UTC)
+ tmstmpValue time.Time = time.Date(2100, 2, 1, 21, 21, 21, 222000000, time.UTC)
+ utmValue time.Time = time.Date(0, 1, 1, 21, 21, 21, 222000000, time.UTC)
+ udtValue time.Time = time.Date(2100, 2, 1, 0, 0, 0, 0, time.UTC)
+ utmstmpValue time.Time = time.Date(2100, 2, 1, 21, 21, 21, 222000000, time.UTC)
+ varcharValue string = "test string"
+ chValue string = "a"
+ binValue []byte = make([]byte, 20, 20)
+ varbinValue []byte = []byte("testtesttest")
+ )
+
+ copy(binValue[:], []byte("test"))
+
+ dbt.mustExec(`UPSERT INTO `+dbt.tableName+` VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
+ integerValue,
+ uintegerValue,
+ bintValue,
+ ulongValue,
+ tintValue,
+ utintValue,
+ sintValue,
+ usintValue,
+ fltValue,
+ ufltValue,
+ dblValue,
+ udblValue,
+ decValue,
+ booleanValue,
+ tmValue,
+ dtValue,
+ tmstmpValue,
+ utmValue,
+ udtValue,
+ utmstmpValue,
+ varcharValue,
+ chValue,
+ binValue,
+ varbinValue,
+ )
+
+ rows := dbt.mustQuery("SELECT * FROM " + dbt.tableName)
+ defer rows.Close()
+
+ var (
+ integer int
+ uinteger int
+ bint int
+ ulong int
+ tint int
+ utint int
+ sint int
+ usint int
+ flt float64
+ uflt float64
+ dbl float64
+ udbl float64
+ dec string
+ boolean bool
+ tm time.Time
+ dt time.Time
+ tmstmp time.Time
+ utm time.Time
+ udt time.Time
+ utmstmp time.Time
+ varchar string
+ ch string
+ bin []byte
+ varbin []byte
+ )
+
+ for rows.Next() {
+
+ err := rows.Scan(&integer, &uinteger, &bint, &ulong, &tint, &utint, &sint, &usint, &flt, &uflt, &dbl, &udbl, &dec, &boolean, &tm, &dt, &tmstmp, &utm, &udt, &utmstmp, &varchar, &ch, &bin, &varbin)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ comparisons := []struct {
+ result interface{}
+ expected interface{}
+ }{
+ {integer, integerValue},
+ {uinteger, uintegerValue},
+ {bint, bintValue},
+ {ulong, ulongValue},
+ {tint, tintValue},
+ {utint, utintValue},
+ {sint, sintValue},
+ {usint, usintValue},
+ {flt, fltValue},
+ {uflt, ufltValue},
+ {dbl, dblValue},
+ {udbl, udblValue},
+ {dec, decValue},
+ {boolean, booleanValue},
+ {tm, tmValue},
+ {dt, dtValue},
+ {tmstmp, tmstmpValue},
+ {utm, utmValue},
+ {udt, udtValue},
+ {utmstmp, utmstmpValue},
+ {varchar, varcharValue},
+ {ch, chValue},
+ {bin, binValue},
+ {varbin, varbinValue},
+ }
+
+ for _, tt := range comparisons {
+
+ if v, ok := tt.expected.(time.Time); ok {
+
+ if !v.Equal(tt.result.(time.Time)) {
+ dbt.Fatalf("Expected %v, got %v.", tt.expected, tt.result)
+ }
+
+ } else if v, ok := tt.expected.([]byte); ok {
+
+ if !bytes.Equal(v, tt.result.([]byte)) {
+ dbt.Fatalf("Expected %v, got %v.", tt.expected, tt.result)
+ }
+
+ } else if tt.expected != tt.result {
+ dbt.Errorf("Expected %v, got %v.", tt.expected, tt.result)
+ }
+ }
+ })
+}
+
+func TestPhoenixLocations(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ query := "?location=Australia/Melbourne"
+
+ runTests(t, dsn+query, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ tm TIME PRIMARY KEY,
+ dt DATE,
+ tmstmp TIMESTAMP
+ ) TRANSACTIONAL=false`)
+
+ loc, err := time.LoadLocation("Australia/Melbourne")
+
+ if err != nil {
+ dbt.Fatalf("Unexpected error: %s", err)
+ }
+
+ var (
+ tmValue time.Time = time.Date(0, 1, 1, 21, 21, 21, 222000000, loc)
+ dtValue time.Time = time.Date(2100, 2, 1, 0, 0, 0, 0, loc)
+ tmstmpValue time.Time = time.Date(2100, 2, 1, 21, 21, 21, 222000000, loc)
+ )
+
+ dbt.mustExec(`UPSERT INTO `+dbt.tableName+` VALUES (?, ?, ?)`,
+ tmValue,
+ dtValue,
+ tmstmpValue,
+ )
+
+ rows := dbt.mustQuery("SELECT * FROM " + dbt.tableName)
+ defer rows.Close()
+
+ var (
+ tm time.Time
+ dt time.Time
+ tmstmp time.Time
+ )
+
+ for rows.Next() {
+
+ err := rows.Scan(&tm, &dt, &tmstmp)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ comparisons := []struct {
+ result time.Time
+ expected time.Time
+ }{
+ {tm, tmValue},
+ {dt, dtValue},
+ {tmstmp, tmstmpValue},
+ }
+
+ for _, tt := range comparisons {
+
+ if !tt.result.Equal(tt.expected) {
+ dbt.Fatalf("Expected %v, got %v.", tt.expected, tt.result)
+ }
+ }
+ })
+}
+
+func TestPhoenixDateAndTimestampsBefore1970(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ runTests(t, dsn, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY,
+ dt DATE,
+ tmstmp TIMESTAMP
+ ) TRANSACTIONAL=false`)
+
+ var (
+ integerValue int = 1
+ dtValue time.Time = time.Date(1945, 5, 20, 0, 0, 0, 0, time.UTC)
+ tmstmpValue time.Time = time.Date(1911, 5, 20, 21, 21, 21, 222000000, time.UTC)
+ )
+
+ dbt.mustExec(`UPSERT INTO `+dbt.tableName+` VALUES (?, ?, ?)`,
+ integerValue,
+ dtValue,
+ tmstmpValue,
+ )
+
+ rows := dbt.mustQuery("SELECT dt, tmstmp FROM " + dbt.tableName)
+ defer rows.Close()
+
+ var (
+ dt time.Time
+ tmstmp time.Time
+ )
+
+ for rows.Next() {
+ err := rows.Scan(&dt, &tmstmp)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ comparisons := []struct {
+ result time.Time
+ expected time.Time
+ }{
+ {dt, dtValue},
+ {tmstmp, tmstmpValue},
+ }
+
+ for _, tt := range comparisons {
+ if !tt.expected.Equal(tt.result) {
+ dbt.Fatalf("Expected %v, got %v.", tt.expected, tt.result)
+ }
+ }
+ })
+}
+
+func TestPhoenixStoreAndRetrieveBinaryData(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ runTests(t, dsn, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY,
+ bin VARBINARY
+ ) TRANSACTIONAL=false`)
+
+ filePath := filepath.Join("test-fixtures", "gopher.png")
+
+ file, err := ioutil.ReadFile(filePath)
+
+ if err != nil {
+ t.Fatalf("Unable to read text-fixture: %s", filePath)
+ }
+
+ hash := sha256.Sum256(file)
+
+ dbt.mustExec(`UPSERT INTO `+dbt.tableName+` VALUES (?, ?)`,
+ 1,
+ file,
+ )
+
+ rows := dbt.mustQuery("SELECT bin FROM " + dbt.tableName)
+ defer rows.Close()
+
+ var receivedFile []byte
+
+ for rows.Next() {
+
+ err := rows.Scan(&receivedFile)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ ioutil.WriteFile("test-fixtures/gopher.png", receivedFile, os.ModePerm)
+
+ receivedHash := sha256.Sum256(receivedFile)
+
+ if !bytes.Equal(hash[:], receivedHash[:]) {
+ t.Fatalf("Hash of stored file (%x) does not equal hash of retrieved file (%x).", hash[:], receivedHash[:])
+ }
+ })
+}
+
+func TestPhoenixCommittingTransactions(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ query := "?transactionIsolation=4"
+
+ runTests(t, dsn+query, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY
+ ) TRANSACTIONAL=true`)
+
+ tx, err := dbt.db.Begin()
+
+ if err != nil {
+ t.Fatalf("Unable to create transaction: %s", err)
+ }
+
+ stmt, err := tx.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?)`)
+
+ if err != nil {
+ t.Fatalf("Could not prepare statement: %s", err)
+ }
+
+ totalRows := 6
+
+ for i := 1; i <= totalRows; i++ {
+ _, err := stmt.Exec(i)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ r := tx.QueryRow("SELECT COUNT(*) FROM " + dbt.tableName)
+
+ var count int
+
+ err = r.Scan(&count)
+
+ if err != nil {
+ t.Fatalf("Unable to scan row result: %s", err)
+ }
+
+ if count != totalRows {
+ t.Fatalf("Expected %d rows, got %d", totalRows, count)
+ }
+
+ // Commit the transaction
+ tx.Commit()
+
+ rows := dbt.mustQuery("SELECT COUNT(*) FROM " + dbt.tableName)
+
+ var countAfterRollback int
+
+ for rows.Next() {
+ err := rows.Scan(&countAfterRollback)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ if countAfterRollback != totalRows {
+ t.Fatalf("Expected %d rows, got %d", totalRows, countAfterRollback)
+ }
+ })
+}
+
+func TestPhoenixRollingBackTransactions(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ query := "?transactionIsolation=4"
+
+ runTests(t, dsn+query, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY
+ ) TRANSACTIONAL=true`)
+
+ tx, err := dbt.db.Begin()
+
+ if err != nil {
+ t.Fatalf("Unable to create transaction: %s", err)
+ }
+
+ stmt, err := tx.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?)`)
+
+ if err != nil {
+ t.Fatalf("Could not prepare statement: %s", err)
+ }
+
+ totalRows := 6
+
+ for i := 1; i <= totalRows; i++ {
+ _, err := stmt.Exec(i)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ r := tx.QueryRow(`SELECT COUNT(*) FROM ` + dbt.tableName)
+
+ var count int
+
+ err = r.Scan(&count)
+
+ if err != nil {
+ t.Fatalf("Unable to scan row result: %s", err)
+ }
+
+ if count != totalRows {
+ t.Fatalf("Expected %d rows, got %d", totalRows, count)
+ }
+
+ // Rollback the transaction
+ tx.Rollback()
+
+ rows := dbt.mustQuery(`SELECT COUNT(*) FROM ` + dbt.tableName)
+
+ var countAfterRollback int
+
+ for rows.Next() {
+ err := rows.Scan(&countAfterRollback)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ if countAfterRollback != 0 {
+ t.Fatalf("Expected %d rows, got %d", 0, countAfterRollback)
+ }
+ })
+}
+
+func TestPhoenixPreparedStatements(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ runTests(t, dsn, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY
+ ) TRANSACTIONAL=false`)
+
+ stmt, err := dbt.db.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?)`)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ totalRows := 6
+
+ for i := 1; i <= totalRows; i++ {
+ _, err := stmt.Exec(i)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ queryStmt, err := dbt.db.Prepare(`SELECT * FROM ` + dbt.tableName + ` WHERE int = ?`)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ var res int
+
+ for i := 1; i <= totalRows; i++ {
+
+ err := queryStmt.QueryRow(i).Scan(&res)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ if res != i {
+ dbt.Fatalf("Unexpected query result. Expected %d, got %d.", i, res)
+ }
+ }
+ })
+}
+
+func TestPhoenixFetchingMoreRows(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ query := "?maxRowsTotal=-1&frameMaxSize=1"
+
+ runTests(t, dsn+query, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY
+ ) TRANSACTIONAL=false`)
+
+ stmt, err := dbt.db.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?)`)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ totalRows := 6
+
+ for i := 1; i <= totalRows; i++ {
+ _, err := stmt.Exec(i)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ rows := dbt.mustQuery(`SELECT * FROM ` + dbt.tableName)
+ defer rows.Close()
+
+ count := 0
+
+ for rows.Next() {
+ count++
+ }
+
+ if count != totalRows {
+ dbt.Fatalf("Expected %d rows to be retrieved, retrieved %d", totalRows, count)
+ }
+ })
+}
+
+func TestPhoenixExecuteShortcut(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ runTests(t, dsn, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY
+ ) TRANSACTIONAL=false`)
+
+ res, err := dbt.db.Exec(`UPSERT INTO ` + dbt.tableName + ` VALUES(1)`)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ affected, err := res.RowsAffected()
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ if affected != 1 {
+ dbt.Fatalf("Expected 1 row to be affected, %d affected", affected)
+ }
+ })
+}
+
+func TestPhoenixQueryShortcut(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ query := "?maxRowsTotal=-1&frameMaxSize=1"
+
+ runTests(t, dsn+query, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY
+ ) TRANSACTIONAL=false`)
+
+ stmt, err := dbt.db.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?)`)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ totalRows := 6
+
+ for i := 1; i <= totalRows; i++ {
+ _, err := stmt.Exec(i)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ rows := dbt.mustQuery(`SELECT * FROM ` + dbt.tableName)
+ defer rows.Close()
+
+ count := 0
+
+ for rows.Next() {
+ count++
+ }
+
+ if count != totalRows {
+ dbt.Fatalf("Expected %d rows to be retrieved, retrieved %d", totalRows, count)
+ }
+ })
+}
+
+func TestPhoenixOptimisticConcurrency(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ query := "?transactionIsolation=4"
+
+ runTests(t, dsn+query, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ id INTEGER PRIMARY KEY,
+ msg VARCHAR,
+ version INTEGER
+ ) TRANSACTIONAL=true`)
+
+ stmt, err := dbt.db.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?, ?, ?)`)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ totalRows := 6
+
+ for i := 1; i <= totalRows; i++ {
+ _, err := stmt.Exec(i, fmt.Sprintf("message version %d", i), i)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+ }
+
+ // Start the transactions
+ tx1, err := dbt.db.Begin()
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ tx2, err := dbt.db.Begin()
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ // Select from first transaction
+ _ = tx1.QueryRow(`SELECT MAX(version) FROM ` + dbt.tableName)
+
+ // Modify using second transaction
+ _, err = tx2.Exec(`UPSERT INTO `+dbt.tableName+` VALUES(?, ?, ?)`, 7, "message value 7", 7)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ err = tx2.Commit()
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ // Modify using tx1
+ _, err = tx1.Exec(`UPSERT INTO `+dbt.tableName+` VALUES(?, ?, ?)`, 7, "message value 7", 7)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ err = tx1.Commit()
+
+ if err == nil {
+ dbt.Fatal("Expected an error, but did not receive any.")
+ }
+
+ errName := err.(errors.ResponseError).Name
+
+ if errName != "transaction_conflict_exception" {
+ dbt.Fatal("Expected transaction_conflict")
+ }
+ })
+}
+
+func TestPhoenixLastInsertIDShouldReturnError(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ runTests(t, dsn, func(dbt *DBTest) {
+
+ dbt.mustExec(`DROP SEQUENCE IF EXISTS test_sequence`)
+
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ id INTEGER PRIMARY KEY,
+ msg VARCHAR,
+ version INTEGER
+ ) TRANSACTIONAL=false`)
+
+ dbt.mustExec(`CREATE SEQUENCE test_sequence`)
+
+ res, err := dbt.db.Exec(`UPSERT INTO ` + dbt.tableName + ` VALUES(NEXT VALUE FOR test_sequence, 'abc', 1)`)
+
+ dbt.mustExec(`DROP SEQUENCE test_sequence`)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ _, err = res.LastInsertId()
+
+ if err == nil {
+ dbt.Fatal("Expected an error as Avatica does not support LastInsertId(), but there was no error.")
+ }
+ })
+}
+
+func TestPhoenixSchemaSupport(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ db, err := sql.Open("avatica", dsn)
+
+ if err != nil {
+ t.Fatalf("error connecting: %s", err.Error())
+ }
+
+ defer db.Close()
+
+ _, err = db.Exec("CREATE SCHEMA IF NOT EXISTS avaticatest")
+
+ if err != nil {
+ t.Fatalf("error creating schema: %s", err)
+ }
+
+ defer db.Exec("DROP SCHEMA IF EXISTS avaticatest")
+
+ path := "/avaticatest"
+
+ runTests(t, dsn+path, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY
+ ) TRANSACTIONAL=false`)
+
+ defer dbt.mustExec(`DROP TABLE IF EXISTS ` + dbt.tableName)
+
+ _, err := dbt.db.Exec(`UPSERT INTO ` + dbt.tableName + ` VALUES(1)`)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ rows := dbt.mustQuery(`SELECT * FROM avaticatest.` + dbt.tableName)
+ defer rows.Close()
+
+ count := 0
+
+ for rows.Next() {
+ count++
+ }
+
+ if count != 1 {
+ dbt.Errorf("Expected 1 row, got %d rows back,", count)
+ }
+ })
+}
+
+func TestPhoenixMultipleSchemaSupport(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ db, err := sql.Open("avatica", dsn)
+
+ if err != nil {
+ t.Fatalf("error connecting: %s", err.Error())
+ }
+
+ defer db.Close()
+
+ _, err = db.Exec("CREATE SCHEMA IF NOT EXISTS avaticatest1")
+
+ if err != nil {
+ t.Fatalf("error creating schema: %s", err)
+ }
+
+ defer db.Exec("DROP SCHEMA IF EXISTS avaticatest1")
+
+ _, err = db.Exec("CREATE SCHEMA IF NOT EXISTS avaticatest2")
+
+ if err != nil {
+ t.Fatalf("error creating schema: %s", err)
+ }
+
+ defer db.Exec("DROP SCHEMA IF EXISTS avaticatest2")
+
+ path := "/avaticatest1"
+
+ runTests(t, dsn+path, func(dbt *DBTest) {
+
+ // Create and seed table
+ dbt.mustExec(`CREATE TABLE avaticatest2.` + dbt.tableName + ` (
+ int INTEGER PRIMARY KEY
+ ) TRANSACTIONAL=false`)
+
+ defer dbt.mustExec(`DROP TABLE IF EXISTS avaticatest2.` + dbt.tableName)
+
+ _, err := dbt.db.Exec(`UPSERT INTO avaticatest2.` + dbt.tableName + ` VALUES(1)`)
+
+ if err != nil {
+ dbt.Fatal(err)
+ }
+
+ rows := dbt.mustQuery(`SELECT * FROM avaticatest2.` + dbt.tableName)
+ defer rows.Close()
+
+ count := 0
+
+ for rows.Next() {
+ count++
+ }
+
+ if count != 1 {
+ dbt.Errorf("Expected 1 row, got %d rows back,", count)
+ }
+ })
+}
+
+func TestPhoenixErrorCodeParsing(t *testing.T) {
+
+ skipTestIfNotPhoenix(t)
+
+ db, err := sql.Open("avatica", dsn)
+
+ if err != nil {
+ t.Fatalf("error connecting: %s", err.Error())
+ }
+
+ defer db.Close()
+
+ _, err = db.Query("SELECT * FROM table_that_does_not_exist")
+
+ if err == nil {
+ t.Error("Expected error due to selecting from non-existent table, but there was no error.")
+ }
+
+ resErr, ok := err.(errors.ResponseError)
+
+ if !ok {
+ t.Fatalf("Error type was not ResponseError")
+ }
+
+ if resErr.ErrorCode != 1012 {
+ t.Errorf("Expected error code to be %d, got %d.", 1012, resErr.ErrorCode)
+ }
+
+ if resErr.SqlState != "42M03" {
+ t.Errorf("Expected SQL state to be %s, got %s.", "42M03", resErr.SqlState)
+ }
+}
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/driver_test.go
----------------------------------------------------------------------
diff --git a/driver_test.go b/driver_test.go
index 2d0f8b0..39f648a 100644
--- a/driver_test.go
+++ b/driver_test.go
@@ -18,16 +18,12 @@
package avatica
import (
- "bytes"
"context"
- "crypto/sha256"
"database/sql"
"fmt"
- "io/ioutil"
"math/rand"
"net/http"
"os"
- "path/filepath"
"testing"
"time"
)
@@ -46,9 +42,19 @@ func init() {
return defaultValue
}
- dsn = env("AVATICA_HOST", "http://phoenix:8765")
+ var serverAddr string
- // Wait for the phoenix server to be ready
+ if val := os.Getenv("AVATICA_FLAVOR"); val == "PHOENIX" {
+ serverAddr = env("PHOENIX_HOST", "http://phoenix:8765")
+ } else if val == "HSQLDB" {
+ serverAddr = env("HSQLDB_HOST", "http://hsqldb:8765")
+ } else {
+ panic("The AVATICA_FLAVOR environment variable should be either PHOENIX or HSQLDB")
+ }
+
+ dsn = serverAddr
+
+ // Wait for the avatica server to be ready
ctx, _ := context.WithTimeout(context.Background(), 5*time.Minute)
ticker := time.NewTicker(2 * time.Second)
@@ -56,10 +62,10 @@ func init() {
select {
case <-ctx.Done():
- panic("Timed out while waiting for the phoenix server to be ready after 5 minutes.")
+ panic("Timed out while waiting for the avatica server to be ready after 5 minutes.")
case <-ticker.C:
- resp, err := http.Get(dsn)
+ resp, err := http.Get(serverAddr)
if err == nil {
resp.Body.Close()
@@ -131,837 +137,6 @@ func runTests(t *testing.T, dsn string, tests ...func(dbt *DBTest)) {
}
}
-func TestConnectionMustBeOpenedWithAutoCommitTrue(t *testing.T) {
-
- runTests(t, dsn, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec("CREATE TABLE " + dbt.tableName + " (id BIGINT PRIMARY KEY, val VARCHAR) TRANSACTIONAL=false")
-
- dbt.mustExec("UPSERT INTO " + dbt.tableName + " VALUES (1,'A')")
-
- dbt.mustExec("UPSERT INTO " + dbt.tableName + " VALUES (2,'B')")
-
- rows := dbt.mustQuery("SELECT COUNT(*) FROM " + dbt.tableName)
- defer rows.Close()
-
- for rows.Next() {
-
- var count int
-
- err := rows.Scan(&count)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- if count != 2 {
- dbt.Fatalf("There should be 2 rows, got %d", count)
- }
- }
-
- })
-}
-
-func TestZeroValues(t *testing.T) {
-
- runTests(t, dsn, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec("CREATE TABLE " + dbt.tableName + " (int INTEGER PRIMARY KEY, flt FLOAT, bool BOOLEAN, str VARCHAR) TRANSACTIONAL=false")
-
- dbt.mustExec("UPSERT INTO " + dbt.tableName + " VALUES (0, 0.0, false, '')")
-
- rows := dbt.mustQuery("SELECT * FROM " + dbt.tableName)
- defer rows.Close()
-
- for rows.Next() {
-
- var i int
- var flt float64
- var b bool
- var s string
-
- err := rows.Scan(&i, &flt, &b, &s)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- if i != 0 {
- dbt.Fatalf("Integer should be 0, got %v", i)
- }
-
- if flt != 0.0 {
- dbt.Fatalf("Float should be 0.0, got %v", flt)
- }
-
- if b != false {
- dbt.Fatalf("Boolean should be false, got %v", b)
- }
-
- if s != "" {
- dbt.Fatalf("String should be \"\", got %v", s)
- }
- }
-
- })
-}
-
-func TestDataTypes(t *testing.T) {
-
- runTests(t, dsn, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY,
- uint UNSIGNED_INT,
- bint BIGINT,
- ulong UNSIGNED_LONG,
- tint TINYINT,
- utint UNSIGNED_TINYINT,
- sint SMALLINT,
- usint UNSIGNED_SMALLINT,
- flt FLOAT,
- uflt UNSIGNED_FLOAT,
- dbl DOUBLE,
- udbl UNSIGNED_DOUBLE,
- dec DECIMAL,
- bool BOOLEAN,
- tm TIME,
- dt DATE,
- tmstmp TIMESTAMP,
- utm UNSIGNED_TIME,
- udt UNSIGNED_DATE,
- utmstmp UNSIGNED_TIMESTAMP,
- var VARCHAR,
- ch CHAR(3),
- bin BINARY(20),
- varbin VARBINARY
- ) TRANSACTIONAL=false`)
-
- var (
- integerValue int = -20
- uintegerValue int = 5
- bintValue int = -9223372036854775807
- ulongValue int = 9223372036854775807
- tintValue int = -128
- utintValue int = 126
- sintValue int = -32768
- usintValue int = 32767
- fltValue float64 = -3.555
- ufltValue float64 = 3.555
- dblValue float64 = -9.555
- udblValue float64 = 9.555
- decValue string = "1.333"
- booleanValue bool = true
- tmValue time.Time = time.Date(0, 1, 1, 21, 21, 21, 222000000, time.UTC)
- dtValue time.Time = time.Date(2100, 2, 1, 0, 0, 0, 0, time.UTC)
- tmstmpValue time.Time = time.Date(2100, 2, 1, 21, 21, 21, 222000000, time.UTC)
- utmValue time.Time = time.Date(0, 1, 1, 21, 21, 21, 222000000, time.UTC)
- udtValue time.Time = time.Date(2100, 2, 1, 0, 0, 0, 0, time.UTC)
- utmstmpValue time.Time = time.Date(2100, 2, 1, 21, 21, 21, 222000000, time.UTC)
- varcharValue string = "test string"
- chValue string = "a"
- binValue []byte = make([]byte, 20, 20)
- varbinValue []byte = []byte("testtesttest")
- )
-
- copy(binValue[:], []byte("test"))
-
- dbt.mustExec(`UPSERT INTO `+dbt.tableName+` VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
- integerValue,
- uintegerValue,
- bintValue,
- ulongValue,
- tintValue,
- utintValue,
- sintValue,
- usintValue,
- fltValue,
- ufltValue,
- dblValue,
- udblValue,
- decValue,
- booleanValue,
- tmValue,
- dtValue,
- tmstmpValue,
- utmValue,
- udtValue,
- utmstmpValue,
- varcharValue,
- chValue,
- binValue,
- varbinValue,
- )
-
- rows := dbt.mustQuery("SELECT * FROM " + dbt.tableName)
- defer rows.Close()
-
- var (
- integer int
- uinteger int
- bint int
- ulong int
- tint int
- utint int
- sint int
- usint int
- flt float64
- uflt float64
- dbl float64
- udbl float64
- dec string
- boolean bool
- tm time.Time
- dt time.Time
- tmstmp time.Time
- utm time.Time
- udt time.Time
- utmstmp time.Time
- varchar string
- ch string
- bin []byte
- varbin []byte
- )
-
- for rows.Next() {
-
- err := rows.Scan(&integer, &uinteger, &bint, &ulong, &tint, &utint, &sint, &usint, &flt, &uflt, &dbl, &udbl, &dec, &boolean, &tm, &dt, &tmstmp, &utm, &udt, &utmstmp, &varchar, &ch, &bin, &varbin)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- comparisons := []struct {
- result interface{}
- expected interface{}
- }{
- {integer, integerValue},
- {uinteger, uintegerValue},
- {bint, bintValue},
- {ulong, ulongValue},
- {tint, tintValue},
- {utint, utintValue},
- {sint, sintValue},
- {usint, usintValue},
- {flt, fltValue},
- {uflt, ufltValue},
- {dbl, dblValue},
- {udbl, udblValue},
- {dec, decValue},
- {boolean, booleanValue},
- {tm, tmValue},
- {dt, dtValue},
- {tmstmp, tmstmpValue},
- {utm, utmValue},
- {udt, udtValue},
- {utmstmp, utmstmpValue},
- {varchar, varcharValue},
- {ch, chValue},
- {bin, binValue},
- {varbin, varbinValue},
- }
-
- for _, tt := range comparisons {
-
- if v, ok := tt.expected.(time.Time); ok {
-
- if !v.Equal(tt.result.(time.Time)) {
- dbt.Fatalf("Expected %v, got %v.", tt.expected, tt.result)
- }
-
- } else if v, ok := tt.expected.([]byte); ok {
-
- if !bytes.Equal(v, tt.result.([]byte)) {
- dbt.Fatalf("Expected %v, got %v.", tt.expected, tt.result)
- }
-
- } else if tt.expected != tt.result {
-
- dbt.Fatalf("Expected %v, got %v.", tt.expected, tt.result)
- }
- }
- })
-}
-
-func TestLocations(t *testing.T) {
-
- query := "?location=Australia/Melbourne"
-
- runTests(t, dsn+query, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- tm TIME PRIMARY KEY,
- dt DATE,
- tmstmp TIMESTAMP
- ) TRANSACTIONAL=false`)
-
- loc, err := time.LoadLocation("Australia/Melbourne")
-
- if err != nil {
- dbt.Fatalf("Unexpected error: %s", err)
- }
-
- var (
- tmValue time.Time = time.Date(0, 1, 1, 21, 21, 21, 222000000, loc)
- dtValue time.Time = time.Date(2100, 2, 1, 0, 0, 0, 0, loc)
- tmstmpValue time.Time = time.Date(2100, 2, 1, 21, 21, 21, 222000000, loc)
- )
-
- dbt.mustExec(`UPSERT INTO `+dbt.tableName+` VALUES (?, ?, ?)`,
- tmValue,
- dtValue,
- tmstmpValue,
- )
-
- rows := dbt.mustQuery("SELECT * FROM " + dbt.tableName)
- defer rows.Close()
-
- var (
- tm time.Time
- dt time.Time
- tmstmp time.Time
- )
-
- for rows.Next() {
-
- err := rows.Scan(&tm, &dt, &tmstmp)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- comparisons := []struct {
- result time.Time
- expected time.Time
- }{
- {tm, tmValue},
- {dt, dtValue},
- {tmstmp, tmstmpValue},
- }
-
- for _, tt := range comparisons {
-
- if !tt.result.Equal(tt.expected) {
- dbt.Fatalf("Expected %v, got %v.", tt.expected, tt.result)
- }
- }
- })
-}
-
-func TestDateAndTimestampsBefore1970(t *testing.T) {
-
- runTests(t, dsn, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY,
- dt DATE,
- tmstmp TIMESTAMP
- ) TRANSACTIONAL=false`)
-
- var (
- integerValue int = 1
- dtValue time.Time = time.Date(1945, 5, 20, 0, 0, 0, 0, time.UTC)
- tmstmpValue time.Time = time.Date(1911, 5, 20, 21, 21, 21, 222000000, time.UTC)
- )
-
- dbt.mustExec(`UPSERT INTO `+dbt.tableName+` VALUES (?, ?, ?)`,
- integerValue,
- dtValue,
- tmstmpValue,
- )
-
- rows := dbt.mustQuery("SELECT dt, tmstmp FROM " + dbt.tableName)
- defer rows.Close()
-
- var (
- dt time.Time
- tmstmp time.Time
- )
-
- for rows.Next() {
- err := rows.Scan(&dt, &tmstmp)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- comparisons := []struct {
- result time.Time
- expected time.Time
- }{
- {dt, dtValue},
- {tmstmp, tmstmpValue},
- }
-
- for _, tt := range comparisons {
- if !tt.expected.Equal(tt.result) {
- dbt.Fatalf("Expected %v, got %v.", tt.expected, tt.result)
- }
- }
- })
-}
-
-func TestStoreAndRetrieveBinaryData(t *testing.T) {
-
- runTests(t, dsn, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY,
- bin VARBINARY
- ) TRANSACTIONAL=false`)
-
- filePath := filepath.Join("test-fixtures", "gopher.png")
-
- file, err := ioutil.ReadFile(filePath)
-
- if err != nil {
- t.Fatalf("Unable to read text-fixture: %s", filePath)
- }
-
- hash := sha256.Sum256(file)
-
- dbt.mustExec(`UPSERT INTO `+dbt.tableName+` VALUES (?, ?)`,
- 1,
- file,
- )
-
- rows := dbt.mustQuery("SELECT bin FROM " + dbt.tableName)
- defer rows.Close()
-
- var receivedFile []byte
-
- for rows.Next() {
-
- err := rows.Scan(&receivedFile)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- ioutil.WriteFile("test-fixtures/gopher.png", receivedFile, os.ModePerm)
-
- receivedHash := sha256.Sum256(receivedFile)
-
- if !bytes.Equal(hash[:], receivedHash[:]) {
- t.Fatalf("Hash of stored file (%x) does not equal hash of retrieved file (%x).", hash[:], receivedHash[:])
- }
- })
-}
-
-func TestCommittingTransactions(t *testing.T) {
-
- query := "?transactionIsolation=4"
-
- runTests(t, dsn+query, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY
- ) TRANSACTIONAL=true`)
-
- tx, err := dbt.db.Begin()
-
- if err != nil {
- t.Fatalf("Unable to create transaction: %s", err)
- }
-
- stmt, err := tx.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?)`)
-
- if err != nil {
- t.Fatalf("Could not prepare statement: %s", err)
- }
-
- totalRows := 6
-
- for i := 1; i <= totalRows; i++ {
- _, err := stmt.Exec(i)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- r := tx.QueryRow("SELECT COUNT(*) FROM " + dbt.tableName)
-
- var count int
-
- err = r.Scan(&count)
-
- if err != nil {
- t.Fatalf("Unable to scan row result: %s", err)
- }
-
- if count != totalRows {
- t.Fatalf("Expected %d rows, got %d", totalRows, count)
- }
-
- // Commit the transaction
- tx.Commit()
-
- rows := dbt.mustQuery("SELECT COUNT(*) FROM " + dbt.tableName)
-
- var countAfterRollback int
-
- for rows.Next() {
- err := rows.Scan(&countAfterRollback)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- if countAfterRollback != totalRows {
- t.Fatalf("Expected %d rows, got %d", totalRows, countAfterRollback)
- }
- })
-}
-
-func TestRollingBackTransactions(t *testing.T) {
-
- query := "?transactionIsolation=4"
-
- runTests(t, dsn+query, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY
- ) TRANSACTIONAL=true`)
-
- tx, err := dbt.db.Begin()
-
- if err != nil {
- t.Fatalf("Unable to create transaction: %s", err)
- }
-
- stmt, err := tx.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?)`)
-
- if err != nil {
- t.Fatalf("Could not prepare statement: %s", err)
- }
-
- totalRows := 6
-
- for i := 1; i <= totalRows; i++ {
- _, err := stmt.Exec(i)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- r := tx.QueryRow(`SELECT COUNT(*) FROM ` + dbt.tableName)
-
- var count int
-
- err = r.Scan(&count)
-
- if err != nil {
- t.Fatalf("Unable to scan row result: %s", err)
- }
-
- if count != totalRows {
- t.Fatalf("Expected %d rows, got %d", totalRows, count)
- }
-
- // Rollback the transaction
- tx.Rollback()
-
- rows := dbt.mustQuery(`SELECT COUNT(*) FROM ` + dbt.tableName)
-
- var countAfterRollback int
-
- for rows.Next() {
- err := rows.Scan(&countAfterRollback)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- if countAfterRollback != 0 {
- t.Fatalf("Expected %d rows, got %d", 0, countAfterRollback)
- }
- })
-}
-
-func TestPreparedStatements(t *testing.T) {
-
- runTests(t, dsn, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY
- ) TRANSACTIONAL=false`)
-
- stmt, err := dbt.db.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?)`)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- totalRows := 6
-
- for i := 1; i <= totalRows; i++ {
- _, err := stmt.Exec(i)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- queryStmt, err := dbt.db.Prepare(`SELECT * FROM ` + dbt.tableName + ` WHERE int = ?`)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- var res int
-
- for i := 1; i <= totalRows; i++ {
-
- err := queryStmt.QueryRow(i).Scan(&res)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- if res != i {
- dbt.Fatalf("Unexpected query result. Expected %d, got %d.", i, res)
- }
- }
- })
-}
-
-func TestFetchingMoreRows(t *testing.T) {
-
- query := "?maxRowsTotal=-1&frameMaxSize=1"
-
- runTests(t, dsn+query, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY
- ) TRANSACTIONAL=false`)
-
- stmt, err := dbt.db.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?)`)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- totalRows := 6
-
- for i := 1; i <= totalRows; i++ {
- _, err := stmt.Exec(i)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- rows := dbt.mustQuery(`SELECT * FROM ` + dbt.tableName)
- defer rows.Close()
-
- count := 0
-
- for rows.Next() {
- count++
- }
-
- if count != totalRows {
- dbt.Fatalf("Expected %d rows to be retrieved, retrieved %d", totalRows, count)
- }
- })
-}
-
-func TestExecuteShortcut(t *testing.T) {
-
- runTests(t, dsn, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY
- ) TRANSACTIONAL=false`)
-
- res, err := dbt.db.Exec(`UPSERT INTO ` + dbt.tableName + ` VALUES(1)`)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- affected, err := res.RowsAffected()
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- if affected != 1 {
- dbt.Fatalf("Expected 1 row to be affected, %d affected", affected)
- }
- })
-}
-
-func TestQueryShortcut(t *testing.T) {
-
- query := "?maxRowsTotal=-1&frameMaxSize=1"
-
- runTests(t, dsn+query, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY
- ) TRANSACTIONAL=false`)
-
- stmt, err := dbt.db.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?)`)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- totalRows := 6
-
- for i := 1; i <= totalRows; i++ {
- _, err := stmt.Exec(i)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- rows := dbt.mustQuery(`SELECT * FROM ` + dbt.tableName)
- defer rows.Close()
-
- count := 0
-
- for rows.Next() {
- count++
- }
-
- if count != totalRows {
- dbt.Fatalf("Expected %d rows to be retrieved, retrieved %d", totalRows, count)
- }
- })
-}
-
-func TestOptimisticConcurrency(t *testing.T) {
-
- query := "?transactionIsolation=4"
-
- runTests(t, dsn+query, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- id INTEGER PRIMARY KEY,
- msg VARCHAR,
- version INTEGER
- ) TRANSACTIONAL=true`)
-
- stmt, err := dbt.db.Prepare(`UPSERT INTO ` + dbt.tableName + ` VALUES(?, ?, ?)`)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- totalRows := 6
-
- for i := 1; i <= totalRows; i++ {
- _, err := stmt.Exec(i, fmt.Sprintf("message version %d", i), i)
-
- if err != nil {
- dbt.Fatal(err)
- }
- }
-
- // Start the transactions
- tx1, err := dbt.db.Begin()
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- tx2, err := dbt.db.Begin()
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- // Select from first transaction
- _ = tx1.QueryRow(`SELECT MAX(version) FROM ` + dbt.tableName)
-
- // Modify using second transaction
- _, err = tx2.Exec(`UPSERT INTO `+dbt.tableName+` VALUES(?, ?, ?)`, 7, "message value 7", 7)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- err = tx2.Commit()
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- // Modify using tx1
- _, err = tx1.Exec(`UPSERT INTO `+dbt.tableName+` VALUES(?, ?, ?)`, 7, "message value 7", 7)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- err = tx1.Commit()
-
- if err == nil {
- dbt.Fatal("Expected an error, but did not receive any.")
- }
-
- errName := err.(ResponseError).Name()
-
- if errName != "transaction_conflict_exception" {
- dbt.Fatal("Expected transaction_conflict")
- }
- })
-}
-
-func TestLastInsertIDShouldReturnError(t *testing.T) {
-
- runTests(t, dsn, func(dbt *DBTest) {
-
- dbt.mustExec(`DROP SEQUENCE IF EXISTS test_sequence`)
-
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- id INTEGER PRIMARY KEY,
- msg VARCHAR,
- version INTEGER
- ) TRANSACTIONAL=false`)
-
- dbt.mustExec(`CREATE SEQUENCE test_sequence`)
-
- res, err := dbt.db.Exec(`UPSERT INTO ` + dbt.tableName + ` VALUES(NEXT VALUE FOR test_sequence, 'abc', 1)`)
-
- dbt.mustExec(`DROP SEQUENCE test_sequence`)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- _, err = res.LastInsertId()
-
- if err == nil {
- dbt.Fatal("Expected an error as Avatica does not support LastInsertId(), but there was no error.")
- }
- })
-}
-
func TestConnectionToInvalidServerShouldReturnError(t *testing.T) {
runTests(t, "http://invalid-server:8765", func(dbt *DBTest) {
@@ -976,127 +151,3 @@ func TestConnectionToInvalidServerShouldReturnError(t *testing.T) {
}
})
}
-
-func TestSchemaSupport(t *testing.T) {
-
- db, err := sql.Open("avatica", dsn)
-
- if err != nil {
- t.Fatalf("error connecting: %s", err.Error())
- }
-
- defer db.Close()
-
- db.Exec("CREATE SCHEMA IF NOT EXISTS avaticatest")
- defer db.Exec("DROP SCHEMA IF EXISTS avaticatest")
-
- path := "/avaticatest"
-
- runTests(t, dsn+path, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY
- ) TRANSACTIONAL=false`)
-
- defer dbt.mustExec(`DROP TABLE IF EXISTS ` + dbt.tableName)
-
- _, err := dbt.db.Exec(`UPSERT INTO ` + dbt.tableName + ` VALUES(1)`)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- rows := dbt.mustQuery(`SELECT * FROM avaticatest.` + dbt.tableName)
- defer rows.Close()
-
- count := 0
-
- for rows.Next() {
- count++
- }
-
- if count != 1 {
- dbt.Errorf("Expected 1 row, got %d rows back,", count)
- }
- })
-}
-
-func TestMultipleSchemaSupport(t *testing.T) {
-
- db, err := sql.Open("avatica", dsn)
-
- if err != nil {
- t.Fatalf("error connecting: %s", err.Error())
- }
-
- defer db.Close()
-
- db.Exec("CREATE SCHEMA IF NOT EXISTS avaticatest1")
- defer db.Exec("DROP SCHEMA IF EXISTS avaticatest1")
-
- db.Exec("CREATE SCHEMA IF NOT EXISTS avaticatest2")
- defer db.Exec("DROP SCHEMA IF EXISTS avaticatest2")
-
- path := "/avaticatest1"
-
- runTests(t, dsn+path, func(dbt *DBTest) {
-
- // Create and seed table
- dbt.mustExec(`CREATE TABLE avaticatest2.` + dbt.tableName + ` (
- int INTEGER PRIMARY KEY
- ) TRANSACTIONAL=false`)
-
- defer dbt.mustExec(`DROP TABLE IF EXISTS avaticatest2.` + dbt.tableName)
-
- _, err := dbt.db.Exec(`UPSERT INTO avaticatest2.` + dbt.tableName + ` VALUES(1)`)
-
- if err != nil {
- dbt.Fatal(err)
- }
-
- rows := dbt.mustQuery(`SELECT * FROM avaticatest2.` + dbt.tableName)
- defer rows.Close()
-
- count := 0
-
- for rows.Next() {
- count++
- }
-
- if count != 1 {
- dbt.Errorf("Expected 1 row, got %d rows back,", count)
- }
- })
-}
-
-func TestErrorCodeParsing(t *testing.T) {
-
- db, err := sql.Open("avatica", dsn)
-
- if err != nil {
- t.Fatalf("error connecting: %s", err.Error())
- }
-
- defer db.Close()
-
- _, err = db.Query("SELECT * FROM table_that_does_not_exist")
-
- if err == nil {
- t.Error("Expected error due to selecting from non-existent table, but there was no error.")
- }
-
- resErr, ok := err.(ResponseError)
-
- if !ok {
- t.Fatalf("Error type was not ResponseError")
- }
-
- if resErr.ErrorCode != 1012 {
- t.Errorf("Expected error code to be %d, got %d.", 1012, resErr.ErrorCode)
- }
-
- if resErr.SqlState != "42M03" {
- t.Errorf("Expected SQL state to be %s, got %s.", "42M03", resErr.SqlState)
- }
-}
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/errors.go
----------------------------------------------------------------------
diff --git a/errors.go b/errors.go
deleted file mode 100644
index 16a842a..0000000
--- a/errors.go
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF 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 avatica
-
-import (
- "regexp"
- "strconv"
-
- "github.com/apache/calcite-avatica-go/message"
-)
-
-// Error severity codes
-const (
- Eunknown int8 = iota
- Efatal
- Eerror
- Ewarning
-)
-
-// RPCMetadata contains metadata about the call that caused the error.
-type RPCMetadata struct {
- ServerAddress string
-}
-
-// ErrorCode represents the error code returned by the avatica server
-type ErrorCode uint32
-
-// SQLState represents the SQL code returned by the avatica server
-type SQLState string
-
-// ResponseError is an error type that contains detailed information on
-// what caused the server to return an error.
-type ResponseError struct {
- Exceptions []string
- HasExceptions bool
- ErrorMessage string
- Severity int8
- ErrorCode ErrorCode
- SqlState SQLState
- Metadata *RPCMetadata
-}
-
-func (r ResponseError) Error() string {
-
- msg := "An error was encountered while processing your request"
-
- if r.ErrorMessage != "" {
- msg += ": " + r.ErrorMessage
- } else if len(r.Exceptions) > 0 {
- msg += ":\n" + r.Exceptions[0]
- }
-
- return msg
-}
-
-// Name returns the name of the error encountered by the server.
-func (r ResponseError) Name() string {
- return errorCodeNames[r.ErrorCode]
-}
-
-// errorResponseToReponseError converts an error protocol buffer response
-// to a native golang error.
-func errorResponseToResponseError(message *message.ErrorResponse) ResponseError {
-
- var (
- errorCode int
- sqlState string
- )
-
- re := regexp.MustCompile(`ERROR (\d+) \(([0-9a-zA-Z]+)\)`)
- codes := re.FindStringSubmatch(message.ErrorMessage)
-
- if len(codes) > 1 {
- errorCode, _ = strconv.Atoi(codes[1])
- }
-
- if len(codes) > 2 {
- sqlState = codes[2]
- }
-
- err := ResponseError{
- Exceptions: message.Exceptions,
- ErrorMessage: message.ErrorMessage,
- Severity: int8(message.Severity),
- ErrorCode: ErrorCode(errorCode),
- SqlState: SQLState(sqlState),
- Metadata: &RPCMetadata{
- ServerAddress: message.GetMetadata().ServerAddress,
- },
- }
-
- return err
-}
-
-var errorCodeNames = map[ErrorCode]string{
- // Connection exceptions (errorcode 01, sqlstate 08)
- 101: "io_exception",
- 102: "malformed_connection_url",
- 103: "cannot_establish_connection",
-
- // Data exceptions (errorcode 02, sqlstate 22)
- 201: "illegal_data",
- 202: "divide_by_zero",
- 203: "type_mismatch",
- 204: "value_in_upsert_not_constant",
- 205: "malformed_url",
- 206: "data_exceeds_max_capacity",
- 207: "missing_max_length",
- 208: "nonpositive_max_length",
- 209: "decimal_precision_out_of_range",
- 212: "server_arithmetic_error",
- 213: "value_outside_range",
- 214: "value_in_list_not_constant",
- 215: "single_row_subquery_returns_multiple_rows",
- 216: "subquery_returns_different_number_of_fields",
- 217: "ambiguous_join_condition",
- 218: "constraint_violation",
-
- 301: "concurrent_table_mutation",
- 302: "cannot_index_column_on_type",
-
- // Invalid cursor state (errorcode 04, sqlstate 24)
- 401: "cursor_before_first_row",
- 402: "cursor_past_last_row",
-
- // Syntax error or access rule violation (errorcode 05, sqlstate 42)
- 501: "ambiguous_table",
- 502: "ambiguous_column",
- 503: "index_missing_pk_columns",
- 504: "column_not_found",
- 505: "read_only_table",
- 506: "cannot_drop_pk",
- 509: "primary_key_missing",
- 510: "primary_key_already_exists",
- 511: "order_by_not_in_select_distinct",
- 512: "invalid_primary_key_constraint",
- 513: "array_not_allowed_in_primary_key",
- 514: "column_exist_in_def",
- 515: "order_by_array_not_supported",
- 516: "non_equality_array_comparison",
- 517: "invalid_not_null_constraint",
-
- // Invalid transaction state (errorcode 05, sqlstate 25)
- 518: "read_only_connection",
- 519: "varbinary_array_not_supported",
-
- // Expression index exceptions
- 520: "aggregate_expression_not_allowed_in_index",
- 521: "non_deterministic_expression_not_allowed_in_index",
- 522: "stateless_expression_not_allowed_in_index",
-
- // Transaction exceptions
- 523: "transaction_conflict_exception",
- 524: "transaction_exception",
-
- // Union all related errors
- 525: "select_column_num_in_unionall_diffs",
- 526: "select_column_type_in_unionall_diffs",
-
- // Row timestamp column related errors
- 527: "rowtimestamp_one_pk_col_only",
- 528: "rowtimestamp_pk_col_only",
- 529: "rowtimestamp_create_only",
- 530: "rowtimestamp_col_invalid_type",
- 531: "rowtimestamp_not_allowed_on_view",
- 532: "invalid_scn",
-
- // Column family related exceptions
- 1000: "single_pk_may_not_be_null",
- 1001: "column_family_not_found",
- 1002: "properties_for_family",
- 1003: "primary_key_with_family_name",
- 1004: "primary_key_out_of_order",
- 1005: "varbinary_in_row_key",
- 1006: "not_nullable_column_in_row_key",
- 1015: "varbinary_last_pk",
- 1023: "nullable_fixed_width_last_pk",
- 1036: "cannot_modify_view_pk",
- 1037: "base_table_column",
-
- // Key/value column related errors
- 1007: "key_value_not_null",
-
- // View related errors
- 1008: "view_with_table_config",
- 1009: "view_with_properties",
-
- // Table related errors that are not in standard code
- 1010: "cannot_mutate_table",
- 1011: "unexpected_mutation_code",
- 1012: "table_undefined",
- 1013: "table_already_exist",
-
- // Syntax error
- 1014: "type_not_supported_for_operator",
- 1016: "aggregate_in_group_by",
- 1017: "aggregate_in_where",
- 1018: "aggregate_with_not_group_by_column",
- 1019: "only_aggregate_in_having_clause",
- 1020: "upsert_column_numbers_mismatch",
-
- // Table properties exception
- 1021: "invalid_bucket_num",
- 1022: "no_splits_on_salted_table",
- 1024: "salt_only_on_create_table",
- 1025: "set_unsupported_prop_on_alter_table",
- 1038: "cannot_add_not_nullable_column",
- 1026: "no_mutable_indexes",
- 1028: "invalid_index_state_transition",
- 1029: "invalid_mutable_index_config",
- 1030: "cannot_create_tenant_specific_table",
- 1034: "default_column_family_only_on_create_table",
- 1040: "insufficient_multi_tenant_columns",
- 1041: "tenantid_is_of_wrong_type",
- 1045: "view_where_is_constant",
- 1046: "cannot_update_view_column",
- 1047: "too_many_indexes",
- 1048: "no_local_index_on_table_with_immutable_rows",
- 1049: "column_family_not_allowed_table_property",
- 1050: "column_family_not_allowed_for_ttl",
- 1051: "cannot_alter_property",
- 1052: "cannot_set_property_for_column_not_added",
- 1053: "cannot_set_table_property_add_column",
- 1054: "no_local_indexes",
- 1055: "unallowed_local_indexes",
- 1056: "desc_varbinary_not_supported",
- 1057: "no_table_specified_for_wildcard_select",
- 1058: "unsupported_group_by_expressions",
- 1069: "default_column_family_on_shared_table",
- 1070: "only_table_may_be_declared_transactional",
- 1071: "tx_may_not_switch_to_non_tx",
- 1072: "store_nulls_must_be_true_for_transactional",
- 1073: "cannot_start_transaction_with_scn_set",
- 1074: "tx_max_versions_must_be_greater_than_one",
- 1075: "cannot_specify_scn_for_txn_table",
- 1076: "null_transaction_context",
- 1077: "transaction_failed",
- 1078: "cannot_create_txn_table_if_txns_disabled",
- 1079: "cannot_alter_to_be_txn_if_txns_disabled",
- 1080: "cannot_create_txn_table_with_row_timestamp",
- 1081: "cannot_alter_to_be_txn_with_row_timestamp",
- 1082: "tx_must_be_enabled_to_set_tx_context",
- 1083: "tx_must_be_enabled_to_set_auto_flush",
- 1084: "tx_must_be_enabled_to_set_isolation_level",
- 1085: "tx_unable_to_get_write_fence",
- 1086: "sequence_not_castable_to_auto_partition_id_column",
- 1087: "cannot_coerce_auto_partition_id",
-
- // Sequence related
- 1200: "sequence_already_exist",
- 1201: "sequence_undefined",
- 1202: "start_with_must_be_constant",
- 1203: "increment_by_must_be_constant",
- 1204: "cache_must_be_non_negative_constant",
- 1205: "invalid_use_of_next_value_for",
- 1206: "cannot_call_current_before_next_value",
- 1207: "empty_sequence_cache",
- 1208: "minvalue_must_be_constant",
- 1209: "maxvalue_must_be_constant",
- 1210: "minvalue_must_be_less_than_or_equal_to_maxvalue",
- 1211: "starts_with_must_be_between_min_max_value",
- 1212: "sequence_val_reached_max_value",
- 1213: "sequence_val_reached_min_value",
- 1214: "increment_by_must_not_be_zero",
- 1215: "num_seq_to_allocate_must_be_constant",
- 1216: "num_seq_to_allocate_not_supported",
- 1217: "auto_partition_sequence_undefined",
-
- // Parser error. (errorcode 06, sqlstate 42p)
- 601: "parser_error",
- 602: "missing_token",
- 603: "unwanted_token",
- 604: "mismatched_token",
- 605: "unknown_function",
-
- // Implementation defined class. execution exceptions (errorcode 11, sqlstate xcl)
- 1101: "resultset_closed",
- 1102: "get_table_regions_fail",
- 1103: "execute_query_not_applicable",
- 1104: "execute_update_not_applicable",
- 1105: "split_point_not_constant",
- 1106: "batch_exception",
- 1107: "execute_update_with_non_empty_batch",
- 1108: "stale_region_boundary_cache",
- 1109: "cannot_split_local_index",
- 1110: "cannot_salt_local_index",
- 1120: "index_failure_block_write",
- 1130: "update_cache_frequency_invalid",
- 1131: "cannot_drop_col_append_only_schema",
- 1132: "view_append_only_schema",
-
- // Implementation defined class. phoenix internal error. (errorcode 20, sqlstate int)
- 2001: "cannot_call_method_on_type",
- 2002: "class_not_unwrappable",
- 2003: "param_index_out_of_bound",
- 2004: "param_value_unbound",
- 2005: "interrupted_exception",
- 2006: "incompatible_client_server_jar",
- 2007: "outdated_jars",
- 2008: "index_metadata_not_found",
- 2009: "unknown_error_code",
- 6000: "operation_timed_out",
- 6001: "function_undefined",
- 6002: "function_already_exist",
- 6003: "unallowed_user_defined_functions",
- 721: "schema_already_exists",
- 722: "schema_not_found",
- 723: "cannot_mutate_schema",
-}
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/errors/errors.go
----------------------------------------------------------------------
diff --git a/errors/errors.go b/errors/errors.go
new file mode 100644
index 0000000..ee79af9
--- /dev/null
+++ b/errors/errors.go
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 errors
+
+// Error severity codes
+const (
+ Eunknown int8 = iota
+ Efatal
+ Eerror
+ Ewarning
+)
+
+// RPCMetadata contains metadata about the call that caused the error.
+type RPCMetadata struct {
+ ServerAddress string
+}
+
+// ErrorCode represents the error code returned by the avatica server
+type ErrorCode uint32
+
+// SQLState represents the SQL code returned by the avatica server
+type SQLState string
+
+// ResponseError is an error type that contains detailed information on
+// what caused the server to return an error.
+type ResponseError struct {
+ Exceptions []string
+ HasExceptions bool
+ ErrorMessage string
+ Severity int8
+ ErrorCode ErrorCode
+ SqlState SQLState
+ Metadata *RPCMetadata
+ Name string
+}
+
+func (r ResponseError) Error() string {
+
+ msg := "An error was encountered while processing your request"
+
+ if r.ErrorMessage != "" {
+ msg += ": " + r.ErrorMessage
+ } else if len(r.Exceptions) > 0 {
+ msg += ":\n" + r.Exceptions[0]
+ }
+
+ return msg
+}
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/gen-protobuf.bat
----------------------------------------------------------------------
diff --git a/gen-protobuf.bat b/gen-protobuf.bat
index 143b6dc..83c51cc 100644
--- a/gen-protobuf.bat
+++ b/gen-protobuf.bat
@@ -14,7 +14,7 @@ rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
-SET AVATICA_VER=rel/avatica-1.10.0
+SET AVATICA_VER=rel/avatica-1.11.0
IF EXIST message\ rmdir /Q /S message
IF EXIST avatica-tmp\ rmdir /Q /S avatica-tmp
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/gen-protobuf.sh
----------------------------------------------------------------------
diff --git a/gen-protobuf.sh b/gen-protobuf.sh
index 73d9793..55b236f 100644
--- a/gen-protobuf.sh
+++ b/gen-protobuf.sh
@@ -18,7 +18,7 @@ set -e
rm -rf message avatica-tmp
-export AVATICA_VER="rel/avatica-1.10.0"
+export AVATICA_VER="rel/avatica-1.11.0"
mkdir -p avatica-tmp
pushd avatica-tmp &> /dev/null
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/generic/generic.go
----------------------------------------------------------------------
diff --git a/generic/generic.go b/generic/generic.go
new file mode 100644
index 0000000..8629375
--- /dev/null
+++ b/generic/generic.go
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 generic
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "time"
+
+ "github.com/apache/calcite-avatica-go/errors"
+ "github.com/apache/calcite-avatica-go/internal"
+ "github.com/apache/calcite-avatica-go/message"
+)
+
+type Adapter struct {
+}
+
+func (a Adapter) GetPingStatement() string {
+ return "SELECT 1"
+}
+
+func (a Adapter) GetColumnTypeDefinition(col *message.ColumnMetaData) *internal.Column {
+
+ column := &internal.Column{
+ Name: col.ColumnName,
+ TypeName: col.Type.Name,
+ Nullable: col.Nullable != 0,
+ }
+
+ // Handle precision and length
+ switch col.Type.Name {
+ case "DECIMAL":
+
+ precision := int64(col.Precision)
+
+ if precision == 0 {
+ precision = math.MaxInt64
+ }
+
+ scale := int64(col.Scale)
+
+ if scale == 0 {
+ scale = math.MaxInt64
+ }
+
+ column.PrecisionScale = &internal.PrecisionScale{
+ Precision: precision,
+ Scale: scale,
+ }
+ case "VARCHAR", "CHARACTER VARYING", "CHAR", "CHARACTER", "BINARY", "VARBINARY", "BINARY VARYING":
+ column.Length = int64(col.Precision)
+ }
+
+ // Handle scan types
+ switch col.Type.Name {
+ case "INTEGER", "BIGINT", "TINYINT", "SMALLINT":
+ column.ScanType = reflect.TypeOf(int64(0))
+
+ case "REAL", "FLOAT", "DOUBLE":
+ column.ScanType = reflect.TypeOf(float64(0))
+
+ case "DECIMAL", "NUMERIC", "VARCHAR", "CHAR", "CHARACTER", "CHARACTER VARYING":
+ column.ScanType = reflect.TypeOf("")
+
+ case "BOOLEAN":
+ column.ScanType = reflect.TypeOf(false)
+
+ case "TIME", "DATE", "TIMESTAMP", "TIMESTAMP WITH LOCAL TIME ZONE", "TIMESTAMP WITH TIME ZONE":
+ column.ScanType = reflect.TypeOf(time.Time{})
+
+ case "BINARY", "VARBINARY", "BINARY VARYING":
+ column.ScanType = reflect.TypeOf([]byte{})
+
+ default:
+ panic(fmt.Sprintf("scantype for %s is not implemented", col.Type.Name))
+ }
+
+ // Handle rep type special cases for decimals, floats, date, time and timestamp
+ switch col.Type.Name {
+ case "DECIMAL", "NUMERIC":
+ column.Rep = message.Rep_BIG_DECIMAL
+ case "FLOAT", "REAL":
+ column.Rep = message.Rep_FLOAT
+ case "TIME":
+ column.Rep = message.Rep_JAVA_SQL_TIME
+ case "DATE":
+ column.Rep = message.Rep_JAVA_SQL_DATE
+ case "TIMESTAMP", "TIMESTAMP WITH LOCAL TIME ZONE", "TIMESTAMP WITH TIME ZONE":
+ column.Rep = message.Rep_JAVA_SQL_TIMESTAMP
+ default:
+ column.Rep = col.Type.Rep
+ }
+
+ return column
+}
+
+func (a Adapter) ErrorResponseToResponseError(message *message.ErrorResponse) errors.ResponseError {
+
+ return errors.ResponseError{
+ Exceptions: message.Exceptions,
+ ErrorMessage: message.ErrorMessage,
+ Severity: int8(message.Severity),
+ ErrorCode: errors.ErrorCode(message.ErrorCode),
+ SqlState: errors.SQLState(message.SqlState),
+ Metadata: &errors.RPCMetadata{
+ ServerAddress: message.GetMetadata().ServerAddress,
+ },
+ }
+}
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/hsqldb/hsqldb.go
----------------------------------------------------------------------
diff --git a/hsqldb/hsqldb.go b/hsqldb/hsqldb.go
new file mode 100644
index 0000000..808449f
--- /dev/null
+++ b/hsqldb/hsqldb.go
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 hsqldb
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "time"
+
+ "github.com/apache/calcite-avatica-go/errors"
+ "github.com/apache/calcite-avatica-go/internal"
+ "github.com/apache/calcite-avatica-go/message"
+)
+
+type Adapter struct {
+}
+
+func (a Adapter) GetPingStatement() string {
+ return "SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
+}
+
+func (a Adapter) GetColumnTypeDefinition(col *message.ColumnMetaData) *internal.Column {
+
+ column := &internal.Column{
+ Name: col.ColumnName,
+ TypeName: col.Type.Name,
+ Nullable: col.Nullable != 0,
+ }
+
+ // Handle precision and length
+ switch col.Type.Name {
+ case "DECIMAL", "NUMERIC":
+
+ precision := int64(col.Precision)
+
+ if precision == 0 {
+ precision = math.MaxInt64
+ }
+
+ scale := int64(col.Scale)
+
+ if scale == 0 {
+ scale = math.MaxInt64
+ }
+
+ column.PrecisionScale = &internal.PrecisionScale{
+ Precision: precision,
+ Scale: scale,
+ }
+ case "VARCHAR", "CHAR", "CHARACTER", "BINARY", "VARBINARY", "BIT", "BITVARYING":
+ column.Length = int64(col.Precision)
+ }
+
+ // Handle scan types
+ switch col.Type.Name {
+ case "INTEGER", "BIGINT", "TINYINT", "SMALLINT":
+ column.ScanType = reflect.TypeOf(int64(0))
+
+ case "FLOAT", "DOUBLE":
+ column.ScanType = reflect.TypeOf(float64(0))
+
+ case "DECIMAL", "NUMERIC", "VARCHAR", "CHAR", "CHARACTER":
+ column.ScanType = reflect.TypeOf("")
+
+ case "BOOLEAN":
+ column.ScanType = reflect.TypeOf(false)
+
+ case "TIME", "DATE", "TIMESTAMP":
+ column.ScanType = reflect.TypeOf(time.Time{})
+
+ case "BINARY", "VARBINARY":
+ column.ScanType = reflect.TypeOf([]byte{})
+
+ default:
+ panic(fmt.Sprintf("scantype for %s is not implemented", col.Type.Name))
+ }
+
+ // Handle rep type special cases for decimals, floats, date, time and timestamp
+ switch col.Type.Name {
+ case "DECIMAL", "NUMERIC":
+ column.Rep = message.Rep_BIG_DECIMAL
+ case "FLOAT":
+ column.Rep = message.Rep_FLOAT
+ case "TIME":
+ column.Rep = message.Rep_JAVA_SQL_TIME
+ case "DATE":
+ column.Rep = message.Rep_JAVA_SQL_DATE
+ case "TIMESTAMP":
+ column.Rep = message.Rep_JAVA_SQL_TIMESTAMP
+ default:
+ column.Rep = col.Type.Rep
+ }
+
+ return column
+}
+
+func (a Adapter) ErrorResponseToResponseError(message *message.ErrorResponse) errors.ResponseError {
+
+ return errors.ResponseError{
+ Exceptions: message.Exceptions,
+ ErrorMessage: message.ErrorMessage,
+ Severity: int8(message.Severity),
+ ErrorCode: errors.ErrorCode(message.ErrorCode),
+ SqlState: errors.SQLState(message.SqlState),
+ Metadata: &errors.RPCMetadata{
+ ServerAddress: message.GetMetadata().ServerAddress,
+ },
+ }
+}
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/http_client.go
----------------------------------------------------------------------
diff --git a/http_client.go b/http_client.go
index dff7a02..b58a62a 100644
--- a/http_client.go
+++ b/http_client.go
@@ -55,12 +55,18 @@ type httpClientAuthConfig struct {
// httpClient wraps the default http.Client to communicate with the Avatica server.
type httpClient struct {
- host string
- authConfig httpClientAuthConfig
+ host string
+ authConfig httpClientAuthConfig
+ httpClient *http.Client
+ kerberosClient client.Client
+}
- httpClient *http.Client
+type avaticaError struct {
+ message *avaticaMessage.ErrorResponse
+}
- kerberosClient client.Client
+func (e avaticaError) Error() string {
+ return fmt.Sprintf("avatica encountered an error: %s", e.message.ErrorMessage)
}
// NewHTTPClient creates a new httpClient from a host.
@@ -202,7 +208,7 @@ func (c *httpClient) post(ctx context.Context, message proto.Message) (proto.Mes
}
}
- return nil, errorResponseToResponseError(v)
+ return nil, avaticaError{v}
}
return inner, nil
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/internal/column.go
----------------------------------------------------------------------
diff --git a/internal/column.go b/internal/column.go
new file mode 100644
index 0000000..81fee3f
--- /dev/null
+++ b/internal/column.go
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 internal
+
+import (
+ "reflect"
+
+ "github.com/apache/calcite-avatica-go/message"
+)
+
+type Column struct {
+ Name string
+ TypeName string
+ Rep message.Rep
+ Length int64
+ Nullable bool
+ PrecisionScale *PrecisionScale
+ ScanType reflect.Type
+}
+
+type PrecisionScale struct {
+ Precision int64
+ Scale int64
+}
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/moby.yml
----------------------------------------------------------------------
diff --git a/moby.yml b/moby.yml
index 07cc28a..d1ceefc 100644
--- a/moby.yml
+++ b/moby.yml
@@ -16,13 +16,18 @@
services:
- id: phoenix
image: boostport/hbase-phoenix-all-in-one:1.3-4.13
+
+ - id: hsqldb
+ image: f21global/calcite-avatica:1.11.0-hypersql
+ command: -u jdbc:hsqldb:mem:public
ports:
- "8765"
dev:
image: golang:1.10-alpine
env:
- AVATICA_HOST: http://phoenix:8765
+ PHOENIX_HOST: http://phoenix:8765
+ HSQLDB_HOST: http://hsqldb:8765
steps:
- type: script
name: Set up workspace
@@ -34,4 +39,4 @@ dev:
name: Run tests
cwd: $GOPATH/src/github.com/apache/calcite-avatica-go
options:
- command: go test -v $(go list ./... | grep -v /vendor/)
\ No newline at end of file
+ command: export AVATICA_FLAVOR=HSQLDB && go test -v ./... && export AVATICA_FLAVOR=PHOENIX && go test -v ./...
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/phoenix/phoenix.go
----------------------------------------------------------------------
diff --git a/phoenix/phoenix.go b/phoenix/phoenix.go
new file mode 100644
index 0000000..08ad4f9
--- /dev/null
+++ b/phoenix/phoenix.go
@@ -0,0 +1,365 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 phoenix
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "regexp"
+ "strconv"
+ "time"
+
+ "github.com/apache/calcite-avatica-go/errors"
+ "github.com/apache/calcite-avatica-go/internal"
+ "github.com/apache/calcite-avatica-go/message"
+)
+
+type Adapter struct {
+}
+
+func (a Adapter) GetPingStatement() string {
+ return "SELECT 1"
+}
+
+func (a Adapter) GetColumnTypeDefinition(col *message.ColumnMetaData) *internal.Column {
+
+ column := &internal.Column{
+ Name: col.ColumnName,
+ TypeName: col.Type.Name,
+ Nullable: col.Nullable != 0,
+ }
+
+ // Handle precision and length
+ switch col.Type.Name {
+ case "DECIMAL":
+
+ precision := int64(col.Precision)
+
+ if precision == 0 {
+ precision = math.MaxInt64
+ }
+
+ scale := int64(col.Scale)
+
+ if scale == 0 {
+ scale = math.MaxInt64
+ }
+
+ column.PrecisionScale = &internal.PrecisionScale{
+ Precision: precision,
+ Scale: scale,
+ }
+ case "VARCHAR", "CHAR", "BINARY":
+ column.Length = int64(col.Precision)
+ case "VARBINARY":
+ column.Length = math.MaxInt64
+ }
+
+ // Handle scan types
+ switch col.Type.Name {
+ case "INTEGER", "UNSIGNED_INT", "BIGINT", "UNSIGNED_LONG", "TINYINT", "UNSIGNED_TINYINT", "SMALLINT", "UNSIGNED_SMALLINT":
+ column.ScanType = reflect.TypeOf(int64(0))
+
+ case "FLOAT", "UNSIGNED_FLOAT", "DOUBLE", "UNSIGNED_DOUBLE":
+ column.ScanType = reflect.TypeOf(float64(0))
+
+ case "DECIMAL", "VARCHAR", "CHAR":
+ column.ScanType = reflect.TypeOf("")
+
+ case "BOOLEAN":
+ column.ScanType = reflect.TypeOf(false)
+
+ case "TIME", "DATE", "TIMESTAMP", "UNSIGNED_TIME", "UNSIGNED_DATE", "UNSIGNED_TIMESTAMP":
+ column.ScanType = reflect.TypeOf(time.Time{})
+
+ case "BINARY", "VARBINARY":
+ column.ScanType = reflect.TypeOf([]byte{})
+
+ default:
+ panic(fmt.Sprintf("scantype for %s is not implemented", col.Type.Name))
+ }
+
+ // Handle rep type special cases for decimals, floats, date, time and timestamp
+ switch col.Type.Name {
+ case "DECIMAL":
+ column.Rep = message.Rep_BIG_DECIMAL
+ case "FLOAT":
+ column.Rep = message.Rep_FLOAT
+ case "UNSIGNED_FLOAT":
+ column.Rep = message.Rep_FLOAT
+ case "TIME", "UNSIGNED_TIME":
+ column.Rep = message.Rep_JAVA_SQL_TIME
+ case "DATE", "UNSIGNED_DATE":
+ column.Rep = message.Rep_JAVA_SQL_DATE
+ case "TIMESTAMP", "UNSIGNED_TIMESTAMP":
+ column.Rep = message.Rep_JAVA_SQL_TIMESTAMP
+ default:
+ column.Rep = col.Type.Rep
+ }
+
+ return column
+}
+
+func (a Adapter) ErrorResponseToResponseError(message *message.ErrorResponse) errors.ResponseError {
+ var (
+ errorCode int
+ sqlState string
+ )
+
+ re := regexp.MustCompile(`ERROR (\d+) \(([0-9a-zA-Z]+)\)`)
+ codes := re.FindStringSubmatch(message.ErrorMessage)
+
+ if len(codes) > 1 {
+ errorCode, _ = strconv.Atoi(codes[1])
+ }
+
+ if len(codes) > 2 {
+ sqlState = codes[2]
+ }
+
+ err := errors.ResponseError{
+ Exceptions: message.Exceptions,
+ ErrorMessage: message.ErrorMessage,
+ Severity: int8(message.Severity),
+ ErrorCode: errors.ErrorCode(errorCode),
+ SqlState: errors.SQLState(sqlState),
+ Metadata: &errors.RPCMetadata{
+ ServerAddress: message.GetMetadata().ServerAddress,
+ },
+ Name: errorCodeNames[uint32(errorCode)],
+ }
+
+ return err
+}
+
+var errorCodeNames = map[uint32]string{
+ // Connection exceptions (errorcode 01, sqlstate 08)
+ 101: "io_exception",
+ 102: "malformed_connection_url",
+ 103: "cannot_establish_connection",
+
+ // Data exceptions (errorcode 02, sqlstate 22)
+ 201: "illegal_data",
+ 202: "divide_by_zero",
+ 203: "type_mismatch",
+ 204: "value_in_upsert_not_constant",
+ 205: "malformed_url",
+ 206: "data_exceeds_max_capacity",
+ 207: "missing_max_length",
+ 208: "nonpositive_max_length",
+ 209: "decimal_precision_out_of_range",
+ 212: "server_arithmetic_error",
+ 213: "value_outside_range",
+ 214: "value_in_list_not_constant",
+ 215: "single_row_subquery_returns_multiple_rows",
+ 216: "subquery_returns_different_number_of_fields",
+ 217: "ambiguous_join_condition",
+ 218: "constraint_violation",
+
+ 301: "concurrent_table_mutation",
+ 302: "cannot_index_column_on_type",
+
+ // Invalid cursor state (errorcode 04, sqlstate 24)
+ 401: "cursor_before_first_row",
+ 402: "cursor_past_last_row",
+
+ // Syntax error or access rule violation (errorcode 05, sqlstate 42)
+ 501: "ambiguous_table",
+ 502: "ambiguous_column",
+ 503: "index_missing_pk_columns",
+ 504: "column_not_found",
+ 505: "read_only_table",
+ 506: "cannot_drop_pk",
+ 509: "primary_key_missing",
+ 510: "primary_key_already_exists",
+ 511: "order_by_not_in_select_distinct",
+ 512: "invalid_primary_key_constraint",
+ 513: "array_not_allowed_in_primary_key",
+ 514: "column_exist_in_def",
+ 515: "order_by_array_not_supported",
+ 516: "non_equality_array_comparison",
+ 517: "invalid_not_null_constraint",
+
+ // Invalid transaction state (errorcode 05, sqlstate 25)
+ 518: "read_only_connection",
+ 519: "varbinary_array_not_supported",
+
+ // Expression index exceptions
+ 520: "aggregate_expression_not_allowed_in_index",
+ 521: "non_deterministic_expression_not_allowed_in_index",
+ 522: "stateless_expression_not_allowed_in_index",
+
+ // Transaction exceptions
+ 523: "transaction_conflict_exception",
+ 524: "transaction_exception",
+
+ // Union all related errors
+ 525: "select_column_num_in_unionall_diffs",
+ 526: "select_column_type_in_unionall_diffs",
+
+ // Row timestamp column related errors
+ 527: "rowtimestamp_one_pk_col_only",
+ 528: "rowtimestamp_pk_col_only",
+ 529: "rowtimestamp_create_only",
+ 530: "rowtimestamp_col_invalid_type",
+ 531: "rowtimestamp_not_allowed_on_view",
+ 532: "invalid_scn",
+
+ // Column family related exceptions
+ 1000: "single_pk_may_not_be_null",
+ 1001: "column_family_not_found",
+ 1002: "properties_for_family",
+ 1003: "primary_key_with_family_name",
+ 1004: "primary_key_out_of_order",
+ 1005: "varbinary_in_row_key",
+ 1006: "not_nullable_column_in_row_key",
+ 1015: "varbinary_last_pk",
+ 1023: "nullable_fixed_width_last_pk",
+ 1036: "cannot_modify_view_pk",
+ 1037: "base_table_column",
+
+ // Key/value column related errors
+ 1007: "key_value_not_null",
+
+ // View related errors
+ 1008: "view_with_table_config",
+ 1009: "view_with_properties",
+
+ // Table related errors that are not in standard code
+ 1010: "cannot_mutate_table",
+ 1011: "unexpected_mutation_code",
+ 1012: "table_undefined",
+ 1013: "table_already_exist",
+
+ // Syntax error
+ 1014: "type_not_supported_for_operator",
+ 1016: "aggregate_in_group_by",
+ 1017: "aggregate_in_where",
+ 1018: "aggregate_with_not_group_by_column",
+ 1019: "only_aggregate_in_having_clause",
+ 1020: "upsert_column_numbers_mismatch",
+
+ // Table properties exception
+ 1021: "invalid_bucket_num",
+ 1022: "no_splits_on_salted_table",
+ 1024: "salt_only_on_create_table",
+ 1025: "set_unsupported_prop_on_alter_table",
+ 1038: "cannot_add_not_nullable_column",
+ 1026: "no_mutable_indexes",
+ 1028: "invalid_index_state_transition",
+ 1029: "invalid_mutable_index_config",
+ 1030: "cannot_create_tenant_specific_table",
+ 1034: "default_column_family_only_on_create_table",
+ 1040: "insufficient_multi_tenant_columns",
+ 1041: "tenantid_is_of_wrong_type",
+ 1045: "view_where_is_constant",
+ 1046: "cannot_update_view_column",
+ 1047: "too_many_indexes",
+ 1048: "no_local_index_on_table_with_immutable_rows",
+ 1049: "column_family_not_allowed_table_property",
+ 1050: "column_family_not_allowed_for_ttl",
+ 1051: "cannot_alter_property",
+ 1052: "cannot_set_property_for_column_not_added",
+ 1053: "cannot_set_table_property_add_column",
+ 1054: "no_local_indexes",
+ 1055: "unallowed_local_indexes",
+ 1056: "desc_varbinary_not_supported",
+ 1057: "no_table_specified_for_wildcard_select",
+ 1058: "unsupported_group_by_expressions",
+ 1069: "default_column_family_on_shared_table",
+ 1070: "only_table_may_be_declared_transactional",
+ 1071: "tx_may_not_switch_to_non_tx",
+ 1072: "store_nulls_must_be_true_for_transactional",
+ 1073: "cannot_start_transaction_with_scn_set",
+ 1074: "tx_max_versions_must_be_greater_than_one",
+ 1075: "cannot_specify_scn_for_txn_table",
+ 1076: "null_transaction_context",
+ 1077: "transaction_failed",
+ 1078: "cannot_create_txn_table_if_txns_disabled",
+ 1079: "cannot_alter_to_be_txn_if_txns_disabled",
+ 1080: "cannot_create_txn_table_with_row_timestamp",
+ 1081: "cannot_alter_to_be_txn_with_row_timestamp",
+ 1082: "tx_must_be_enabled_to_set_tx_context",
+ 1083: "tx_must_be_enabled_to_set_auto_flush",
+ 1084: "tx_must_be_enabled_to_set_isolation_level",
+ 1085: "tx_unable_to_get_write_fence",
+ 1086: "sequence_not_castable_to_auto_partition_id_column",
+ 1087: "cannot_coerce_auto_partition_id",
+
+ // Sequence related
+ 1200: "sequence_already_exist",
+ 1201: "sequence_undefined",
+ 1202: "start_with_must_be_constant",
+ 1203: "increment_by_must_be_constant",
+ 1204: "cache_must_be_non_negative_constant",
+ 1205: "invalid_use_of_next_value_for",
+ 1206: "cannot_call_current_before_next_value",
+ 1207: "empty_sequence_cache",
+ 1208: "minvalue_must_be_constant",
+ 1209: "maxvalue_must_be_constant",
+ 1210: "minvalue_must_be_less_than_or_equal_to_maxvalue",
+ 1211: "starts_with_must_be_between_min_max_value",
+ 1212: "sequence_val_reached_max_value",
+ 1213: "sequence_val_reached_min_value",
+ 1214: "increment_by_must_not_be_zero",
+ 1215: "num_seq_to_allocate_must_be_constant",
+ 1216: "num_seq_to_allocate_not_supported",
+ 1217: "auto_partition_sequence_undefined",
+
+ // Parser error. (errorcode 06, sqlstate 42p)
+ 601: "parser_error",
+ 602: "missing_token",
+ 603: "unwanted_token",
+ 604: "mismatched_token",
+ 605: "unknown_function",
+
+ // Implementation defined class. execution exceptions (errorcode 11, sqlstate xcl)
+ 1101: "resultset_closed",
+ 1102: "get_table_regions_fail",
+ 1103: "execute_query_not_applicable",
+ 1104: "execute_update_not_applicable",
+ 1105: "split_point_not_constant",
+ 1106: "batch_exception",
+ 1107: "execute_update_with_non_empty_batch",
+ 1108: "stale_region_boundary_cache",
+ 1109: "cannot_split_local_index",
+ 1110: "cannot_salt_local_index",
+ 1120: "index_failure_block_write",
+ 1130: "update_cache_frequency_invalid",
+ 1131: "cannot_drop_col_append_only_schema",
+ 1132: "view_append_only_schema",
+
+ // Implementation defined class. phoenix internal error. (errorcode 20, sqlstate int)
+ 2001: "cannot_call_method_on_type",
+ 2002: "class_not_unwrappable",
+ 2003: "param_index_out_of_bound",
+ 2004: "param_value_unbound",
+ 2005: "interrupted_exception",
+ 2006: "incompatible_client_server_jar",
+ 2007: "outdated_jars",
+ 2008: "index_metadata_not_found",
+ 2009: "unknown_error_code",
+ 6000: "operation_timed_out",
+ 6001: "function_undefined",
+ 6002: "function_already_exist",
+ 6003: "unallowed_user_defined_functions",
+ 721: "schema_already_exists",
+ 722: "schema_not_found",
+ 723: "cannot_mutate_schema",
+}
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/rows.go
----------------------------------------------------------------------
diff --git a/rows.go b/rows.go
index b8fd54c..e205e19 100644
--- a/rows.go
+++ b/rows.go
@@ -19,33 +19,16 @@ package avatica
import (
"database/sql/driver"
- "fmt"
"io"
- "math"
- "reflect"
"time"
+ "github.com/apache/calcite-avatica-go/internal"
"github.com/apache/calcite-avatica-go/message"
"golang.org/x/net/context"
)
-type precisionScale struct {
- precision int64
- scale int64
-}
-
-type column struct {
- name string
- typeName string
- rep message.Rep
- length int64
- nullable bool
- precisionScale *precisionScale
- scanType reflect.Type
-}
-
type resultSet struct {
- columns []*column
+ columns []*internal.Column
done bool
offset uint64
data [][]*message.TypedValue
@@ -68,7 +51,7 @@ func (r *rows) Columns() []string {
var cols []string
for _, column := range r.resultSets[r.currentResultSet].columns {
- cols = append(cols, column.name)
+ cols = append(cols, column.Name)
}
return cols
@@ -110,7 +93,7 @@ func (r *rows) Next(dest []driver.Value) error {
})
if err != nil {
- return err
+ return r.conn.avaticaErrorToResponseErrorOrError(err)
}
frame := res.(*message.FetchResponse).Frame
@@ -140,7 +123,7 @@ func (r *rows) Next(dest []driver.Value) error {
}
for i, val := range resultSet.data[resultSet.currentRow] {
- dest[i] = typedValueToNative(resultSet.columns[i].rep, val, r.conn.config)
+ dest[i] = typedValueToNative(resultSet.columns[i].Rep, val, r.conn.config)
}
resultSet.currentRow++
@@ -158,84 +141,10 @@ func newRows(conn *conn, statementID uint32, resultSets []*message.ResultSetResp
break
}
- var columns []*column
+ var columns []*internal.Column
for _, col := range result.Signature.Columns {
-
- column := &column{
- name: col.ColumnName,
- typeName: col.Type.Name,
- nullable: col.Nullable != 0,
- }
-
- // Handle precision and length
- switch col.Type.Name {
- case "DECIMAL":
-
- precision := int64(col.Precision)
-
- if precision == 0 {
- precision = math.MaxInt64
- }
-
- scale := int64(col.Scale)
-
- if scale == 0 {
- scale = math.MaxInt64
- }
-
- column.precisionScale = &precisionScale{
- precision: precision,
- scale: scale,
- }
- case "VARCHAR", "CHAR", "BINARY":
- column.length = int64(col.Precision)
- case "VARBINARY":
- column.length = math.MaxInt64
- }
-
- // Handle scan types
- switch col.Type.Name {
- case "INTEGER", "UNSIGNED_INT", "BIGINT", "UNSIGNED_LONG", "TINYINT", "UNSIGNED_TINYINT", "SMALLINT", "UNSIGNED_SMALLINT":
- column.scanType = reflect.TypeOf(int64(0))
-
- case "FLOAT", "UNSIGNED_FLOAT", "DOUBLE", "UNSIGNED_DOUBLE":
- column.scanType = reflect.TypeOf(float64(0))
-
- case "DECIMAL", "VARCHAR", "CHAR":
- column.scanType = reflect.TypeOf("")
-
- case "BOOLEAN":
- column.scanType = reflect.TypeOf(false)
-
- case "TIME", "DATE", "TIMESTAMP", "UNSIGNED_TIME", "UNSIGNED_DATE", "UNSIGNED_TIMESTAMP":
- column.scanType = reflect.TypeOf(time.Time{})
-
- case "BINARY", "VARBINARY":
- column.scanType = reflect.TypeOf([]byte{})
-
- default:
- panic(fmt.Sprintf("scantype for %s is not implemented", col.Type.Name))
- }
-
- // Handle rep type special cases for decimals, floats, date, time and timestamp
- switch col.Type.Name {
- case "DECIMAL":
- column.rep = message.Rep_BIG_DECIMAL
- case "FLOAT":
- column.rep = message.Rep_FLOAT
- case "UNSIGNED_FLOAT":
- column.rep = message.Rep_FLOAT
- case "TIME", "UNSIGNED_TIME":
- column.rep = message.Rep_JAVA_SQL_TIME
- case "DATE", "UNSIGNED_DATE":
- column.rep = message.Rep_JAVA_SQL_DATE
- case "TIMESTAMP", "UNSIGNED_TIMESTAMP":
- column.rep = message.Rep_JAVA_SQL_TIMESTAMP
- default:
- column.rep = col.Type.Rep
- }
-
+ column := conn.adapter.GetColumnTypeDefinition(col)
columns = append(columns, column)
}
@@ -271,7 +180,6 @@ func newRows(conn *conn, statementID uint32, resultSets []*message.ResultSetResp
// typedValueToNative converts values from avatica's types to Go's native types
func typedValueToNative(rep message.Rep, v *message.TypedValue, config *Config) interface{} {
-
switch rep {
case message.Rep_BOOLEAN, message.Rep_PRIMITIVE_BOOLEAN:
return v.BoolValue
http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/rows_go18.go
----------------------------------------------------------------------
diff --git a/rows_go18.go b/rows_go18.go
index 3dcf95c..792c211 100644
--- a/rows_go18.go
+++ b/rows_go18.go
@@ -44,11 +44,11 @@ func (r *rows) NextResultSet() error {
func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
- return r.resultSets[r.currentResultSet].columns[index].typeName
+ return r.resultSets[r.currentResultSet].columns[index].TypeName
}
func (r *rows) ColumnTypeLength(index int) (length int64, ok bool) {
- l := r.resultSets[r.currentResultSet].columns[index].length
+ l := r.resultSets[r.currentResultSet].columns[index].Length
if l == 0 {
return 0, false
@@ -58,20 +58,20 @@ func (r *rows) ColumnTypeLength(index int) (length int64, ok bool) {
}
func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) {
- return r.resultSets[r.currentResultSet].columns[index].nullable, true
+ return r.resultSets[r.currentResultSet].columns[index].Nullable, true
}
func (r *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) {
- ps := r.resultSets[r.currentResultSet].columns[index].precisionScale
+ ps := r.resultSets[r.currentResultSet].columns[index].PrecisionScale
if ps != nil {
- return ps.precision, ps.scale, true
+ return ps.Precision, ps.Scale, true
}
return 0, 0, false
}
func (r *rows) ColumnTypeScanType(index int) reflect.Type {
- return r.resultSets[r.currentResultSet].columns[index].scanType
+ return r.resultSets[r.currentResultSet].columns[index].ScanType
}