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
 }