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:07 UTC

[1/4] calcite-avatica-go git commit: Add HSQLDB support and move phoenix support into an adapter

Repository: calcite-avatica-go
Updated Branches:
  refs/heads/master 7891426fd -> b9d80926c


http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/statement.go
----------------------------------------------------------------------
diff --git a/statement.go b/statement.go
index a7e0908..b01333b 100644
--- a/statement.go
+++ b/statement.go
@@ -46,7 +46,11 @@ func (s *stmt) Close() error {
 		StatementId:  s.statementID,
 	})
 
-	return err
+	if err != nil {
+		return s.conn.avaticaErrorToResponseErrorOrError(err)
+	}
+
+	return nil
 }
 
 // NumInput returns the number of placeholder parameters.
@@ -91,7 +95,7 @@ func (s *stmt) exec(ctx context.Context, args []namedValue) (driver.Result, erro
 	res, err := s.conn.httpClient.post(ctx, msg)
 
 	if err != nil {
-		return nil, err
+		return nil, s.conn.avaticaErrorToResponseErrorOrError(err)
 	}
 
 	results := res.(*message.ExecuteResponse).Results
@@ -136,7 +140,7 @@ func (s *stmt) query(ctx context.Context, args []namedValue) (driver.Rows, error
 	res, err := s.conn.httpClient.post(ctx, msg)
 
 	if err != nil {
-		return nil, err
+		return nil, s.conn.avaticaErrorToResponseErrorOrError(err)
 	}
 
 	resultSet := res.(*message.ExecuteResponse).Results

http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/transaction.go
----------------------------------------------------------------------
diff --git a/transaction.go b/transaction.go
index 1d428e4..51b8cc7 100644
--- a/transaction.go
+++ b/transaction.go
@@ -35,7 +35,11 @@ func (t *tx) Commit() error {
 		ConnectionId: t.conn.connectionId,
 	})
 
-	return err
+	if err != nil {
+		return t.conn.avaticaErrorToResponseErrorOrError(err)
+	}
+
+	return nil
 }
 
 // Rollback rolls back a transaction
@@ -47,7 +51,11 @@ func (t *tx) Rollback() error {
 		ConnectionId: t.conn.connectionId,
 	})
 
-	return err
+	if err != nil {
+		return t.conn.avaticaErrorToResponseErrorOrError(err)
+	}
+
+	return nil
 }
 
 // enableAutoCommit enables auto-commit on the server
@@ -62,5 +70,9 @@ func (t *tx) enableAutoCommit() error {
 		},
 	})
 
-	return err
+	if err != nil {
+		return t.conn.avaticaErrorToResponseErrorOrError(err)
+	}
+
+	return nil
 }


[3/4] calcite-avatica-go git commit: Add HSQLDB support and move phoenix support into an adapter

Posted by fr...@apache.org.
Add HSQLDB support and move phoenix support into an adapter


Project: http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/commit/2968def4
Tree: http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/tree/2968def4
Diff: http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/diff/2968def4

Branch: refs/heads/master
Commit: 2968def4e8dfefdbec731b710769a07fed5d6ff4
Parents: 7891426
Author: Francis Chuang <fr...@boostport.com>
Authored: Sun Apr 15 14:27:20 2018 +1000
Committer: Francis Chuang <fr...@boostport.com>
Committed: Mon Apr 16 14:23:14 2018 +1000

----------------------------------------------------------------------
 adapter.go                  |   30 ++
 connection.go               |   44 +-
 connection_go18.go          |    2 +-
 driver.go                   |   44 +-
 driver_go18_hsqldb_test.go  |  578 +++++++++++++++++++++
 driver_go18_phoenix_test.go |  768 ++++++++++++++++++++++++++++
 driver_go18_test.go         |  736 ---------------------------
 driver_hsqldb_test.go       | 1028 +++++++++++++++++++++++++++++++++++++
 driver_phoenix_test.go      | 1044 ++++++++++++++++++++++++++++++++++++++
 driver_test.go              |  977 +----------------------------------
 errors.go                   |  324 ------------
 errors/errors.go            |   63 +++
 gen-protobuf.bat            |    2 +-
 gen-protobuf.sh             |    2 +-
 generic/generic.go          |  125 +++++
 hsqldb/hsqldb.go            |  125 +++++
 http_client.go              |   16 +-
 internal/column.go          |   39 ++
 moby.yml                    |    9 +-
 phoenix/phoenix.go          |  365 +++++++++++++
 rows.go                     |  106 +---
 rows_go18.go                |   12 +-
 statement.go                |   10 +-
 transaction.go              |   18 +-
 24 files changed, 4311 insertions(+), 2156 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/adapter.go
----------------------------------------------------------------------
diff --git a/adapter.go b/adapter.go
new file mode 100644
index 0000000..ea0ba93
--- /dev/null
+++ b/adapter.go
@@ -0,0 +1,30 @@
+/*
+ * 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 (
+	"github.com/apache/calcite-avatica-go/errors"
+	"github.com/apache/calcite-avatica-go/internal"
+	"github.com/apache/calcite-avatica-go/message"
+)
+
+type Adapter interface {
+	GetPingStatement() string
+	GetColumnTypeDefinition(*message.ColumnMetaData) *internal.Column
+	ErrorResponseToResponseError(*message.ErrorResponse) errors.ResponseError
+}

http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/connection.go
----------------------------------------------------------------------
diff --git a/connection.go b/connection.go
index ef6a599..cf8ad9b 100644
--- a/connection.go
+++ b/connection.go
@@ -20,6 +20,7 @@ package avatica
 import (
 	"database/sql/driver"
 
+	"github.com/apache/calcite-avatica-go/errors"
 	"github.com/apache/calcite-avatica-go/message"
 	"golang.org/x/net/context"
 )
@@ -28,6 +29,7 @@ type conn struct {
 	connectionId string
 	config       *Config
 	httpClient   *httpClient
+	adapter      Adapter
 }
 
 // Prepare returns a prepared statement, bound to this connection.
@@ -47,7 +49,7 @@ func (c *conn) prepare(ctx context.Context, query string) (driver.Stmt, error) {
 	})
 
 	if err != nil {
-		return nil, err
+		return nil, c.avaticaErrorToResponseErrorOrError(err)
 	}
 
 	prepareResponse := response.(*message.PrepareResponse)
@@ -80,7 +82,11 @@ func (c *conn) Close() error {
 
 	c.connectionId = ""
 
-	return err
+	if err != nil {
+		return c.avaticaErrorToResponseErrorOrError(err)
+	}
+
+	return nil
 }
 
 // Begin starts and returns a new transaction.
@@ -107,7 +113,7 @@ func (c *conn) begin(ctx context.Context, isolationLevel isoLevel) (driver.Tx, e
 	})
 
 	if err != nil {
-		return nil, err
+		return nil, c.avaticaErrorToResponseErrorOrError(err)
 	}
 
 	return &tx{
@@ -135,7 +141,7 @@ func (c *conn) exec(ctx context.Context, query string, args []namedValue) (drive
 	})
 
 	if err != nil {
-		return nil, err
+		return nil, c.avaticaErrorToResponseErrorOrError(err)
 	}
 
 	res, err := c.httpClient.post(ctx, &message.PrepareAndExecuteRequest{
@@ -147,7 +153,7 @@ func (c *conn) exec(ctx context.Context, query string, args []namedValue) (drive
 	})
 
 	if err != nil {
-		return nil, err
+		return nil, c.avaticaErrorToResponseErrorOrError(err)
 	}
 
 	// Currently there is only 1 ResultSet per response for exec
@@ -178,7 +184,7 @@ func (c *conn) query(ctx context.Context, query string, args []namedValue) (driv
 	})
 
 	if err != nil {
-		return nil, err
+		return nil, c.avaticaErrorToResponseErrorOrError(err)
 	}
 
 	res, err := c.httpClient.post(ctx, &message.PrepareAndExecuteRequest{
@@ -190,10 +196,34 @@ func (c *conn) query(ctx context.Context, query string, args []namedValue) (driv
 	})
 
 	if err != nil {
-		return nil, err
+		return nil, c.avaticaErrorToResponseErrorOrError(err)
 	}
 
 	resultSets := res.(*message.ExecuteResponse).Results
 
 	return newRows(c, st.(*message.CreateStatementResponse).StatementId, resultSets), nil
 }
+
+func (c *conn) avaticaErrorToResponseErrorOrError(err error) error {
+
+	avaticaErr, ok := err.(avaticaError)
+
+	if !ok {
+		return err
+	}
+
+	if c.adapter != nil {
+		return c.adapter.ErrorResponseToResponseError(avaticaErr.message)
+	}
+
+	return errors.ResponseError{
+		Exceptions:   avaticaErr.message.Exceptions,
+		ErrorMessage: avaticaErr.message.ErrorMessage,
+		Severity:     int8(avaticaErr.message.Severity),
+		ErrorCode:    errors.ErrorCode(avaticaErr.message.ErrorCode),
+		SqlState:     errors.SQLState(avaticaErr.message.SqlState),
+		Metadata: &errors.RPCMetadata{
+			ServerAddress: avaticaErr.message.GetMetadata().ServerAddress,
+		},
+	}
+}

http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/connection_go18.go
----------------------------------------------------------------------
diff --git a/connection_go18.go b/connection_go18.go
index 332e04f..1c75b0d 100644
--- a/connection_go18.go
+++ b/connection_go18.go
@@ -75,7 +75,7 @@ func (c *conn) ExecContext(ctx context.Context, query string, args []driver.Name
 
 func (c *conn) Ping(ctx context.Context) error {
 
-	_, err := c.ExecContext(ctx, "SELECT 1", []driver.NamedValue{})
+	_, err := c.ExecContext(ctx, c.adapter.GetPingStatement(), []driver.NamedValue{})
 
 	if err != nil {
 		return fmt.Errorf("Error pinging database: %s", err)

http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/driver.go
----------------------------------------------------------------------
diff --git a/driver.go b/driver.go
index 3137314..6457f40 100644
--- a/driver.go
+++ b/driver.go
@@ -36,7 +36,10 @@ import (
 	"database/sql/driver"
 	"fmt"
 
+	"github.com/apache/calcite-avatica-go/generic"
+	"github.com/apache/calcite-avatica-go/hsqldb"
 	"github.com/apache/calcite-avatica-go/message"
+	"github.com/apache/calcite-avatica-go/phoenix"
 	"github.com/satori/go.uuid"
 	"golang.org/x/net/context"
 )
@@ -88,6 +91,12 @@ func (a *Driver) Open(dsn string) (driver.Conn, error) {
 		info["password"] = config.password
 	}
 
+	conn := &conn{
+		connectionId: connectionId.String(),
+		httpClient:   httpClient,
+		config:       config,
+	}
+
 	// Open a connection to the server
 	req := &message.OpenConnectionRequest{
 		ConnectionId: connectionId.String(),
@@ -101,18 +110,43 @@ func (a *Driver) Open(dsn string) (driver.Conn, error) {
 	_, err = httpClient.post(context.Background(), req)
 
 	if err != nil {
-		return nil, err
+		return nil, conn.avaticaErrorToResponseErrorOrError(err)
 	}
 
-	conn := &conn{
-		connectionId: connectionId.String(),
-		httpClient:   httpClient,
-		config:       config,
+	response, err := httpClient.post(context.Background(), &message.DatabasePropertyRequest{
+		ConnectionId: connectionId.String(),
+	})
+
+	if err != nil {
+		return nil, conn.avaticaErrorToResponseErrorOrError(err)
 	}
 
+	databasePropertyResponse := response.(*message.DatabasePropertyResponse)
+
+	adapter := ""
+
+	for _, property := range databasePropertyResponse.Props {
+		if property.Key.Name == "GET_DRIVER_NAME" {
+			adapter = property.Value.StringValue
+		}
+	}
+
+	conn.adapter = getAdapter(adapter)
+
 	return conn, nil
 }
 
+func getAdapter(e string) Adapter {
+	switch e {
+	case "HSQL Database Engine Driver":
+		return hsqldb.Adapter{}
+	case "PhoenixEmbeddedDriver":
+		return phoenix.Adapter{}
+	default:
+		return generic.Adapter{}
+	}
+}
+
 func init() {
 	sql.Register("avatica", &Driver{})
 }

http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/driver_go18_hsqldb_test.go
----------------------------------------------------------------------
diff --git a/driver_go18_hsqldb_test.go b/driver_go18_hsqldb_test.go
new file mode 100644
index 0000000..21c59be
--- /dev/null
+++ b/driver_go18_hsqldb_test.go
@@ -0,0 +1,578 @@
+// +build go1.8
+
+/*
+ * 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 (
+	"database/sql"
+	"math"
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestHSQLDBContext(t *testing.T) {
+
+	skipTestIfNotHSQLDB(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+
+		// Create and seed table
+		dbt.mustExecContext(getContext(), "CREATE TABLE "+dbt.tableName+" (id BIGINT PRIMARY KEY, val VARCHAR(1))")
+
+		dbt.mustExecContext(getContext(), "INSERT INTO "+dbt.tableName+" VALUES (1,'A')")
+
+		dbt.mustExecContext(getContext(), "INSERT INTO "+dbt.tableName+" VALUES (2,'B')")
+
+		rows := dbt.mustQueryContext(getContext(), "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)
+			}
+		}
+
+		// Test transactions and prepared statements
+		_, err := dbt.db.BeginTx(getContext(), &sql.TxOptions{Isolation: sql.LevelReadUncommitted, ReadOnly: true})
+
+		if err == nil {
+			t.Error("Expected an error while creating a read only transaction, but no error was returned")
+		}
+
+		tx, err := dbt.db.BeginTx(getContext(), &sql.TxOptions{Isolation: sql.LevelReadCommitted})
+
+		if err != nil {
+			t.Errorf("Unexpected error while creating transaction: %s", err)
+		}
+
+		stmt, err := tx.PrepareContext(getContext(), "INSERT INTO "+dbt.tableName+" VALUES(?,?)")
+
+		if err != nil {
+			t.Errorf("Unexpected error while preparing statement: %s", err)
+		}
+
+		res, err := stmt.ExecContext(getContext(), 3, "C")
+
+		if err != nil {
+			t.Errorf("Unexpected error while executing statement: %s", err)
+		}
+
+		affected, err := res.RowsAffected()
+
+		if err != nil {
+			t.Errorf("Error getting affected rows: %s", err)
+		}
+
+		if affected != 1 {
+			t.Errorf("Expected 1 affected row, got %d", affected)
+		}
+
+		err = tx.Commit()
+
+		if err != nil {
+			t.Errorf("Error committing transaction: %s", err)
+		}
+
+		stmt2, err := dbt.db.PrepareContext(getContext(), "SELECT * FROM "+dbt.tableName+" WHERE id = ?")
+
+		if err != nil {
+			t.Errorf("Error preparing statement: %s", err)
+		}
+
+		row := stmt2.QueryRowContext(getContext(), 3)
+
+		if err != nil {
+			t.Errorf("Error querying for row: %s", err)
+		}
+
+		var (
+			queryID  int64
+			queryVal string
+		)
+
+		err = row.Scan(&queryID, &queryVal)
+
+		if err != nil {
+			t.Errorf("Error scanning results into variable: %s", err)
+		}
+
+		if queryID != 3 {
+			t.Errorf("Expected scanned id to be %d, got %d", 3, queryID)
+		}
+
+		if queryVal != "C" {
+			t.Errorf("Expected scanned string to be %s, got %s", "C", queryVal)
+		}
+	})
+}
+
+func TestHSQLDBMultipleResultSets(t *testing.T) {
+
+	skipTestIfNotHSQLDB(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+		// Create and seed table
+		dbt.mustExecContext(getContext(), "CREATE TABLE "+dbt.tableName+" (id BIGINT PRIMARY KEY, val VARCHAR(1))")
+
+		dbt.mustExecContext(getContext(), "INSERT INTO "+dbt.tableName+" VALUES (1,'A')")
+
+		dbt.mustExecContext(getContext(), "INSERT INTO "+dbt.tableName+" VALUES (2,'B')")
+
+		rows, err := dbt.db.QueryContext(getContext(), "SELECT * FROM "+dbt.tableName+" WHERE id = 1")
+
+		if err != nil {
+			t.Errorf("Unexpected error while executing query: %s", err)
+		}
+
+		defer rows.Close()
+
+		for rows.Next() {
+			var (
+				id  int64
+				val string
+			)
+
+			if err := rows.Scan(&id, &val); err != nil {
+				t.Errorf("Error while scanning row into variables: %s", err)
+			}
+
+			if id != 1 {
+				t.Errorf("Expected id to be %d, got %d", 1, id)
+			}
+
+			if val != "A" {
+				t.Errorf("Expected value to be %s, got %s", "A", val)
+			}
+		}
+
+		if rows.NextResultSet() {
+			t.Error("There should be no more result sets, but got another result set")
+		}
+	})
+}
+
+func TestHSQLDBColumnTypes(t *testing.T) {
+
+	skipTestIfNotHSQLDB(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+
+		// Create and seed table
+		dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+				int INTEGER PRIMARY KEY,
+				bint BIGINT,
+				tint TINYINT,
+				sint SMALLINT,
+				dbl DOUBLE,
+				dec DECIMAL(10, 5),
+				dec2 DECIMAL,
+				bool BOOLEAN,
+				tm TIME,
+				dt DATE,
+				tmstmp TIMESTAMP,
+				var VARCHAR(10),
+				ch CHAR(3),
+				bin BINARY(20),
+				varbin VARBINARY(20)
+			    )`)
+
+		// Select
+		rows, err := dbt.db.QueryContext(getContext(), "SELECT * FROM "+dbt.tableName)
+
+		if err != nil {
+			t.Errorf("Unexpected error while selecting from table: %s", err)
+		}
+
+		columnNames, err := rows.Columns()
+
+		if err != nil {
+			t.Errorf("Error getting column names: %s", err)
+		}
+
+		expectedColumnNames := []string{"INT", "BINT", "TINT", "SINT", "DBL", "DEC", "DEC2", "BOOL", "TM", "DT", "TMSTMP", "VAR", "CH", "BIN", "VARBIN"}
+
+		if !reflect.DeepEqual(columnNames, expectedColumnNames) {
+			t.Error("Column names does not match expected column names")
+		}
+
+		type decimalSize struct {
+			precision int64
+			scale     int64
+			ok        bool
+		}
+
+		type length struct {
+			length int64
+			ok     bool
+		}
+
+		type nullable struct {
+			nullable bool
+			ok       bool
+		}
+
+		expectedColumnTypes := []struct {
+			databaseTypeName string
+			decimalSize      decimalSize
+			length           length
+			name             string
+			nullable         nullable
+			scanType         reflect.Type
+		}{
+			{
+				databaseTypeName: "INTEGER",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "INT",
+				nullable: nullable{
+					nullable: false,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "BIGINT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "BINT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "TINYINT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "TINT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "SMALLINT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "SINT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "DOUBLE",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "DBL",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(float64(0)),
+			},
+			{
+				databaseTypeName: "DECIMAL",
+				decimalSize: decimalSize{
+					precision: 10,
+					scale:     5,
+					ok:        true,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "DEC",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(""),
+			},
+			{
+				databaseTypeName: "DECIMAL",
+				decimalSize: decimalSize{
+					precision: 128,
+					scale:     math.MaxInt64,
+					ok:        true,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "DEC2",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(""),
+			},
+			{
+				databaseTypeName: "BOOLEAN",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "BOOL",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(bool(false)),
+			},
+			{
+				databaseTypeName: "TIME",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "TM",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(time.Time{}),
+			},
+			{
+				databaseTypeName: "DATE",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "DT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(time.Time{}),
+			},
+			{
+				databaseTypeName: "TIMESTAMP",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "TMSTMP",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(time.Time{}),
+			},
+			{
+				databaseTypeName: "VARCHAR",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 10,
+					ok:     true,
+				},
+				name: "VAR",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(""),
+			},
+			{
+				databaseTypeName: "CHARACTER",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 3,
+					ok:     true,
+				},
+				name: "CH",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(""),
+			},
+			{
+				databaseTypeName: "BINARY",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 20,
+					ok:     true,
+				},
+				name: "BIN",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf([]byte{}),
+			},
+			{
+				databaseTypeName: "VARBINARY",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 20,
+					ok:     true,
+				},
+				name: "VARBIN",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf([]byte{}),
+			},
+		}
+
+		columnTypes, err := rows.ColumnTypes()
+
+		if err != nil {
+			t.Errorf("Error getting column types: %s", err)
+		}
+
+		for index, columnType := range columnTypes {
+
+			expected := expectedColumnTypes[index]
+
+			if columnType.DatabaseTypeName() != expected.databaseTypeName {
+				t.Errorf("Expected database type name for index %d to be %s, got %s", index, expected.databaseTypeName, columnType.DatabaseTypeName())
+			}
+
+			precision, scale, ok := columnType.DecimalSize()
+
+			if precision != expected.decimalSize.precision {
+				t.Errorf("Expected decimal precision for index %d to be %d, got %d", index, expected.decimalSize.precision, precision)
+			}
+
+			if scale != expected.decimalSize.scale {
+				t.Errorf("Expected decimal scale for index %d to be %d, got %d", index, expected.decimalSize.scale, scale)
+			}
+
+			if ok != expected.decimalSize.ok {
+				t.Errorf("Expected decimal ok for index %d to be %t, got %t", index, expected.decimalSize.ok, ok)
+			}
+
+			length, ok := columnType.Length()
+
+			if length != expected.length.length {
+				t.Errorf("Expected length for index %d to be %d, got %d", index, expected.length.length, length)
+			}
+
+			if ok != expected.length.ok {
+				t.Errorf("Expected length ok for index %d to be %t, got %t", index, expected.length.ok, ok)
+			}
+
+			if columnType.Name() != expected.name {
+				t.Errorf("Expected column name for index %d to be %s, got %s", index, expected.name, columnType.Name())
+			}
+
+			nullable, ok := columnType.Nullable()
+
+			if nullable != expected.nullable.nullable {
+				t.Errorf("Expected nullable for index %d to be %t, got %t", index, expected.nullable.nullable, nullable)
+			}
+
+			if ok != expected.nullable.ok {
+				t.Errorf("Expected nullable ok for index %d to be %t, got %t", index, expected.nullable.ok, ok)
+			}
+
+			if columnType.ScanType() != expected.scanType {
+				t.Errorf("Expected scan type for index %d to be %s, got %s", index, expected.scanType, columnType.ScanType())
+			}
+		}
+
+	})
+}

http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/driver_go18_phoenix_test.go
----------------------------------------------------------------------
diff --git a/driver_go18_phoenix_test.go b/driver_go18_phoenix_test.go
new file mode 100644
index 0000000..bb7b322
--- /dev/null
+++ b/driver_go18_phoenix_test.go
@@ -0,0 +1,768 @@
+// +build go1.8
+
+/*
+ * 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 (
+	"database/sql"
+	"math"
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestPhoenixContext(t *testing.T) {
+
+	skipTestIfNotPhoenix(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+
+		// Create and seed table
+		dbt.mustExecContext(getContext(), "CREATE TABLE "+dbt.tableName+" (id BIGINT PRIMARY KEY, val VARCHAR) TRANSACTIONAL=false")
+
+		dbt.mustExecContext(getContext(), "UPSERT INTO "+dbt.tableName+" VALUES (1,'A')")
+
+		dbt.mustExecContext(getContext(), "UPSERT INTO "+dbt.tableName+" VALUES (2,'B')")
+
+		rows := dbt.mustQueryContext(getContext(), "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)
+			}
+		}
+
+		// Test transactions and prepared statements
+		_, err := dbt.db.BeginTx(getContext(), &sql.TxOptions{Isolation: sql.LevelReadUncommitted, ReadOnly: true})
+
+		if err == nil {
+			t.Error("Expected an error while creating a read only transaction, but no error was returned")
+		}
+
+		tx, err := dbt.db.BeginTx(getContext(), &sql.TxOptions{Isolation: sql.LevelReadCommitted})
+
+		if err != nil {
+			t.Errorf("Unexpected error while creating transaction: %s", err)
+		}
+
+		stmt, err := tx.PrepareContext(getContext(), "UPSERT INTO "+dbt.tableName+" VALUES(?,?)")
+
+		if err != nil {
+			t.Errorf("Unexpected error while preparing statement: %s", err)
+		}
+
+		res, err := stmt.ExecContext(getContext(), 3, "C")
+
+		if err != nil {
+			t.Errorf("Unexpected error while executing statement: %s", err)
+		}
+
+		affected, err := res.RowsAffected()
+
+		if err != nil {
+			t.Errorf("Error getting affected rows: %s", err)
+		}
+
+		if affected != 1 {
+			t.Errorf("Expected 1 affected row, got %d", affected)
+		}
+
+		err = tx.Commit()
+
+		if err != nil {
+			t.Errorf("Error committing transaction: %s", err)
+		}
+
+		stmt2, err := dbt.db.PrepareContext(getContext(), "SELECT * FROM "+dbt.tableName+" WHERE id = ?")
+
+		if err != nil {
+			t.Errorf("Error preparing statement: %s", err)
+		}
+
+		row := stmt2.QueryRowContext(getContext(), 3)
+
+		if err != nil {
+			t.Errorf("Error querying for row: %s", err)
+		}
+
+		var (
+			queryID  int64
+			queryVal string
+		)
+
+		err = row.Scan(&queryID, &queryVal)
+
+		if err != nil {
+			t.Errorf("Error scanning results into variable: %s", err)
+		}
+
+		if queryID != 3 {
+			t.Errorf("Expected scanned id to be %d, got %d", 3, queryID)
+		}
+
+		if queryVal != "C" {
+			t.Errorf("Expected scanned string to be %s, got %s", "C", queryVal)
+		}
+	})
+}
+
+func TestPhoenixMultipleResultSets(t *testing.T) {
+
+	skipTestIfNotPhoenix(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+		// Create and seed table
+		dbt.mustExecContext(getContext(), "CREATE TABLE "+dbt.tableName+" (id BIGINT PRIMARY KEY, val VARCHAR) TRANSACTIONAL=false")
+
+		dbt.mustExecContext(getContext(), "UPSERT INTO "+dbt.tableName+" VALUES (1,'A')")
+
+		dbt.mustExecContext(getContext(), "UPSERT INTO "+dbt.tableName+" VALUES (2,'B')")
+
+		rows, err := dbt.db.QueryContext(getContext(), "SELECT * FROM "+dbt.tableName+" WHERE id = 1")
+
+		if err != nil {
+			t.Errorf("Unexpected error while executing query: %s", err)
+		}
+
+		defer rows.Close()
+
+		for rows.Next() {
+			var (
+				id  int64
+				val string
+			)
+
+			if err := rows.Scan(&id, &val); err != nil {
+				t.Errorf("Error while scanning row into variables: %s", err)
+			}
+
+			if id != 1 {
+				t.Errorf("Expected id to be %d, got %d", 1, id)
+			}
+
+			if val != "A" {
+				t.Errorf("Expected value to be %s, got %s", "A", val)
+			}
+		}
+
+		if rows.NextResultSet() {
+			t.Error("There should be no more result sets, but got another result set")
+		}
+	})
+}
+
+func TestPhoenixColumnTypes(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(10, 5),
+				dec2 DECIMAL,
+				bool BOOLEAN,
+				tm TIME,
+				dt DATE,
+				tmstmp TIMESTAMP,
+				utm UNSIGNED_TIME,
+				udt UNSIGNED_DATE,
+				utmstmp UNSIGNED_TIMESTAMP,
+				var VARCHAR(10),
+				ch CHAR(3),
+				bin BINARY(20),
+				varbin VARBINARY
+			    ) TRANSACTIONAL=false`)
+
+		// Select
+		rows, err := dbt.db.QueryContext(getContext(), "SELECT * FROM "+dbt.tableName)
+
+		if err != nil {
+			t.Errorf("Unexpected error while selecting from table: %s", err)
+		}
+
+		columnNames, err := rows.Columns()
+
+		if err != nil {
+			t.Errorf("Error getting column names: %s", err)
+		}
+
+		expectedColumnNames := []string{"INT", "UINT", "BINT", "ULONG", "TINT", "UTINT", "SINT", "USINT", "FLT", "UFLT", "DBL", "UDBL", "DEC", "DEC2", "BOOL", "TM", "DT", "TMSTMP", "UTM", "UDT", "UTMSTMP", "VAR", "CH", "BIN", "VARBIN"}
+
+		if !reflect.DeepEqual(columnNames, expectedColumnNames) {
+			t.Error("Column names does not match expected column names")
+		}
+
+		type decimalSize struct {
+			precision int64
+			scale     int64
+			ok        bool
+		}
+
+		type length struct {
+			length int64
+			ok     bool
+		}
+
+		type nullable struct {
+			nullable bool
+			ok       bool
+		}
+
+		expectedColumnTypes := []struct {
+			databaseTypeName string
+			decimalSize      decimalSize
+			length           length
+			name             string
+			nullable         nullable
+			scanType         reflect.Type
+		}{
+			{
+				databaseTypeName: "INTEGER",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "INT",
+				nullable: nullable{
+					nullable: false,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "UNSIGNED_INT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "UINT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "BIGINT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "BINT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "UNSIGNED_LONG",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "ULONG",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "TINYINT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "TINT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "UNSIGNED_TINYINT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "UTINT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "SMALLINT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "SINT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "UNSIGNED_SMALLINT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "USINT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(int64(0)),
+			},
+			{
+				databaseTypeName: "FLOAT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "FLT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(float64(0)),
+			},
+			{
+				databaseTypeName: "UNSIGNED_FLOAT",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "UFLT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(float64(0)),
+			},
+			{
+				databaseTypeName: "DOUBLE",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "DBL",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(float64(0)),
+			},
+			{
+				databaseTypeName: "UNSIGNED_DOUBLE",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "UDBL",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(float64(0)),
+			},
+			{
+				databaseTypeName: "DECIMAL",
+				decimalSize: decimalSize{
+					precision: 10,
+					scale:     5,
+					ok:        true,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "DEC",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(""),
+			},
+			{
+				databaseTypeName: "DECIMAL",
+				decimalSize: decimalSize{
+					precision: math.MaxInt64,
+					scale:     math.MaxInt64,
+					ok:        true,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "DEC2",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(""),
+			},
+			{
+				databaseTypeName: "BOOLEAN",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "BOOL",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(bool(false)),
+			},
+			{
+				databaseTypeName: "TIME",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "TM",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(time.Time{}),
+			},
+			{
+				databaseTypeName: "DATE",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "DT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(time.Time{}),
+			},
+			{
+				databaseTypeName: "TIMESTAMP",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "TMSTMP",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(time.Time{}),
+			},
+			{
+				databaseTypeName: "UNSIGNED_TIME",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "UTM",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(time.Time{}),
+			},
+			{
+				databaseTypeName: "UNSIGNED_DATE",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "UDT",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(time.Time{}),
+			},
+			{
+				databaseTypeName: "UNSIGNED_TIMESTAMP",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 0,
+					ok:     false,
+				},
+				name: "UTMSTMP",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(time.Time{}),
+			},
+			{
+				databaseTypeName: "VARCHAR",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 10,
+					ok:     true,
+				},
+				name: "VAR",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(""),
+			},
+			{
+				databaseTypeName: "CHAR",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 3,
+					ok:     true,
+				},
+				name: "CH",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf(""),
+			},
+			{
+				databaseTypeName: "BINARY",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: 20,
+					ok:     true,
+				},
+				name: "BIN",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf([]byte{}),
+			},
+			{
+				databaseTypeName: "VARBINARY",
+				decimalSize: decimalSize{
+					precision: 0,
+					scale:     0,
+					ok:        false,
+				},
+				length: length{
+					length: math.MaxInt64,
+					ok:     true,
+				},
+				name: "VARBIN",
+				nullable: nullable{
+					nullable: true,
+					ok:       true,
+				},
+				scanType: reflect.TypeOf([]byte{}),
+			},
+		}
+
+		columnTypes, err := rows.ColumnTypes()
+
+		if err != nil {
+			t.Errorf("Error getting column types: %s", err)
+		}
+
+		for index, columnType := range columnTypes {
+
+			expected := expectedColumnTypes[index]
+
+			if columnType.DatabaseTypeName() != expected.databaseTypeName {
+				t.Errorf("Expected database type name for index %d to be %s, got %s", index, expected.databaseTypeName, columnType.DatabaseTypeName())
+			}
+
+			precision, scale, ok := columnType.DecimalSize()
+
+			if precision != expected.decimalSize.precision {
+				t.Errorf("Expected decimal precision for index %d to be %d, got %d", index, expected.decimalSize.precision, precision)
+			}
+
+			if scale != expected.decimalSize.scale {
+				t.Errorf("Expected decimal scale for index %d to be %d, got %d", index, expected.decimalSize.scale, scale)
+			}
+
+			if ok != expected.decimalSize.ok {
+				t.Errorf("Expected decimal ok for index %d to be %t, got %t", index, expected.decimalSize.ok, ok)
+			}
+
+			length, ok := columnType.Length()
+
+			if length != expected.length.length {
+				t.Errorf("Expected length for index %d to be %d, got %d", index, expected.length.length, length)
+			}
+
+			if ok != expected.length.ok {
+				t.Errorf("Expected length ok for index %d to be %t, got %t", index, expected.length.ok, ok)
+			}
+
+			if columnType.Name() != expected.name {
+				t.Errorf("Expected column name for index %d to be %s, got %s", index, expected.name, columnType.Name())
+			}
+
+			nullable, ok := columnType.Nullable()
+
+			if nullable != expected.nullable.nullable {
+				t.Errorf("Expected nullable for index %d to be %t, got %t", index, expected.nullable.nullable, nullable)
+			}
+
+			if ok != expected.nullable.ok {
+				t.Errorf("Expected nullable ok for index %d to be %t, got %t", index, expected.nullable.ok, ok)
+			}
+
+			if columnType.ScanType() != expected.scanType {
+				t.Errorf("Expected scan type for index %d to be %s, got %s", index, expected.scanType, columnType.ScanType())
+			}
+		}
+
+	})
+}

http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/driver_go18_test.go
----------------------------------------------------------------------
diff --git a/driver_go18_test.go b/driver_go18_test.go
index be760f6..1be71e0 100644
--- a/driver_go18_test.go
+++ b/driver_go18_test.go
@@ -22,8 +22,6 @@ package avatica
 import (
 	"context"
 	"database/sql"
-	"math"
-	"reflect"
 	"testing"
 	"time"
 )
@@ -54,109 +52,6 @@ func getContext() context.Context {
 	return ctx
 }
 
-func TestContext(t *testing.T) {
-
-	runTests(t, dsn, func(dbt *DBTest) {
-
-		// Create and seed table
-		dbt.mustExecContext(getContext(), "CREATE TABLE "+dbt.tableName+" (id BIGINT PRIMARY KEY, val VARCHAR) TRANSACTIONAL=false")
-
-		dbt.mustExecContext(getContext(), "UPSERT INTO "+dbt.tableName+" VALUES (1,'A')")
-
-		dbt.mustExecContext(getContext(), "UPSERT INTO "+dbt.tableName+" VALUES (2,'B')")
-
-		rows := dbt.mustQueryContext(getContext(), "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)
-			}
-		}
-
-		// Test transactions and prepared statements
-		_, err := dbt.db.BeginTx(getContext(), &sql.TxOptions{Isolation: sql.LevelReadUncommitted, ReadOnly: true})
-
-		if err == nil {
-			t.Error("Expected an error while creating a read only transaction, but no error was returned")
-		}
-
-		tx, err := dbt.db.BeginTx(getContext(), &sql.TxOptions{Isolation: sql.LevelReadCommitted})
-
-		if err != nil {
-			t.Errorf("Unexpected error while creating transaction: %s", err)
-		}
-
-		stmt, err := tx.PrepareContext(getContext(), "UPSERT INTO "+dbt.tableName+" VALUES(?,?)")
-
-		if err != nil {
-			t.Errorf("Unexpected error while preparing statement: %s", err)
-		}
-
-		res, err := stmt.ExecContext(getContext(), 3, "C")
-
-		if err != nil {
-			t.Errorf("Unexpected error while executing statement: %s", err)
-		}
-
-		affected, err := res.RowsAffected()
-
-		if err != nil {
-			t.Errorf("Error getting affected rows: %s", err)
-		}
-
-		if affected != 1 {
-			t.Errorf("Expected 1 affected row, got %d", affected)
-		}
-
-		err = tx.Commit()
-
-		if err != nil {
-			t.Errorf("Error committing transaction: %s", err)
-		}
-
-		stmt2, err := dbt.db.PrepareContext(getContext(), "SELECT * FROM "+dbt.tableName+" WHERE id = ?")
-
-		if err != nil {
-			t.Errorf("Error preparing statement: %s", err)
-		}
-
-		row := stmt2.QueryRowContext(getContext(), 3)
-
-		if err != nil {
-			t.Errorf("Error querying for row: %s", err)
-		}
-
-		var (
-			queryID  int64
-			queryVal string
-		)
-
-		err = row.Scan(&queryID, &queryVal)
-
-		if err != nil {
-			t.Errorf("Error scanning results into variable: %s", err)
-		}
-
-		if queryID != 3 {
-			t.Errorf("Expected scanned id to be %d, got %d", 3, queryID)
-		}
-
-		if queryVal != "C" {
-			t.Errorf("Expected scanned string to be %s, got %s", "C", queryVal)
-		}
-	})
-}
-
 func TestPing(t *testing.T) {
 	runTests(t, dsn, func(dbt *DBTest) {
 		err := dbt.db.Ping()
@@ -176,634 +71,3 @@ func TestInvalidPing(t *testing.T) {
 		}
 	})
 }
-
-func TestMultipleResultSets(t *testing.T) {
-
-	runTests(t, dsn, func(dbt *DBTest) {
-		// Create and seed table
-		dbt.mustExecContext(getContext(), "CREATE TABLE "+dbt.tableName+" (id BIGINT PRIMARY KEY, val VARCHAR) TRANSACTIONAL=false")
-
-		dbt.mustExecContext(getContext(), "UPSERT INTO "+dbt.tableName+" VALUES (1,'A')")
-
-		dbt.mustExecContext(getContext(), "UPSERT INTO "+dbt.tableName+" VALUES (2,'B')")
-
-		rows, err := dbt.db.QueryContext(getContext(), "SELECT * FROM "+dbt.tableName+" WHERE id = 1")
-
-		if err != nil {
-			t.Errorf("Unexpected error while executing query: %s", err)
-		}
-
-		defer rows.Close()
-
-		for rows.Next() {
-			var (
-				id  int64
-				val string
-			)
-
-			if err := rows.Scan(&id, &val); err != nil {
-				t.Errorf("Error while scanning row into variables: %s", err)
-			}
-
-			if id != 1 {
-				t.Errorf("Expected id to be %d, got %d", 1, id)
-			}
-
-			if val != "A" {
-				t.Errorf("Expected value to be %s, got %s", "A", val)
-			}
-		}
-
-		if rows.NextResultSet() {
-			t.Error("There should be no more result sets, but got another result set")
-		}
-	})
-}
-
-func TestColumnTypes(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(10, 5),
-				dec2 DECIMAL,
-				bool BOOLEAN,
-				tm TIME,
-				dt DATE,
-				tmstmp TIMESTAMP,
-				utm UNSIGNED_TIME,
-				udt UNSIGNED_DATE,
-				utmstmp UNSIGNED_TIMESTAMP,
-				var VARCHAR(10),
-				ch CHAR(3),
-				bin BINARY(20),
-				varbin VARBINARY
-			    ) TRANSACTIONAL=false`)
-
-		// Select
-		rows, err := dbt.db.QueryContext(getContext(), "SELECT * FROM "+dbt.tableName)
-
-		if err != nil {
-			t.Errorf("Unexpected error while selecting from table: %s", err)
-		}
-
-		columnNames, err := rows.Columns()
-
-		if err != nil {
-			t.Errorf("Error getting column names: %s", err)
-		}
-
-		expectedColumnNames := []string{"INT", "UINT", "BINT", "ULONG", "TINT", "UTINT", "SINT", "USINT", "FLT", "UFLT", "DBL", "UDBL", "DEC", "DEC2", "BOOL", "TM", "DT", "TMSTMP", "UTM", "UDT", "UTMSTMP", "VAR", "CH", "BIN", "VARBIN"}
-
-		if !reflect.DeepEqual(columnNames, expectedColumnNames) {
-			t.Error("Column names does not match expected column names")
-		}
-
-		type decimalSize struct {
-			precision int64
-			scale     int64
-			ok        bool
-		}
-
-		type length struct {
-			length int64
-			ok     bool
-		}
-
-		type nullable struct {
-			nullable bool
-			ok       bool
-		}
-
-		expectedColumnTypes := []struct {
-			databaseTypeName string
-			decimalSize      decimalSize
-			length           length
-			name             string
-			nullable         nullable
-			scanType         reflect.Type
-		}{
-			{
-				databaseTypeName: "INTEGER",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "INT",
-				nullable: nullable{
-					nullable: false,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(int64(0)),
-			},
-			{
-				databaseTypeName: "UNSIGNED_INT",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "UINT",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(int64(0)),
-			},
-			{
-				databaseTypeName: "BIGINT",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "BINT",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(int64(0)),
-			},
-			{
-				databaseTypeName: "UNSIGNED_LONG",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "ULONG",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(int64(0)),
-			},
-			{
-				databaseTypeName: "TINYINT",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "TINT",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(int64(0)),
-			},
-			{
-				databaseTypeName: "UNSIGNED_TINYINT",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "UTINT",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(int64(0)),
-			},
-			{
-				databaseTypeName: "SMALLINT",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "SINT",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(int64(0)),
-			},
-			{
-				databaseTypeName: "UNSIGNED_SMALLINT",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "USINT",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(int64(0)),
-			},
-			{
-				databaseTypeName: "FLOAT",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "FLT",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(float64(0)),
-			},
-			{
-				databaseTypeName: "UNSIGNED_FLOAT",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "UFLT",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(float64(0)),
-			},
-			{
-				databaseTypeName: "DOUBLE",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "DBL",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(float64(0)),
-			},
-			{
-				databaseTypeName: "UNSIGNED_DOUBLE",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "UDBL",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(float64(0)),
-			},
-			{
-				databaseTypeName: "DECIMAL",
-				decimalSize: decimalSize{
-					precision: 10,
-					scale:     5,
-					ok:        true,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "DEC",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(""),
-			},
-			{
-				databaseTypeName: "DECIMAL",
-				decimalSize: decimalSize{
-					precision: math.MaxInt64,
-					scale:     math.MaxInt64,
-					ok:        true,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "DEC2",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(""),
-			},
-			{
-				databaseTypeName: "BOOLEAN",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "BOOL",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(false),
-			},
-			{
-				databaseTypeName: "TIME",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "TM",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(time.Time{}),
-			},
-			{
-				databaseTypeName: "DATE",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "DT",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(time.Time{}),
-			},
-			{
-				databaseTypeName: "TIMESTAMP",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "TMSTMP",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(time.Time{}),
-			},
-			{
-				databaseTypeName: "UNSIGNED_TIME",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "UTM",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(time.Time{}),
-			},
-			{
-				databaseTypeName: "UNSIGNED_DATE",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "UDT",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(time.Time{}),
-			},
-			{
-				databaseTypeName: "UNSIGNED_TIMESTAMP",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 0,
-					ok:     false,
-				},
-				name: "UTMSTMP",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(time.Time{}),
-			},
-			{
-				databaseTypeName: "VARCHAR",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 10,
-					ok:     true,
-				},
-				name: "VAR",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(""),
-			},
-			{
-				databaseTypeName: "CHAR",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 3,
-					ok:     true,
-				},
-				name: "CH",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf(""),
-			},
-			{
-				databaseTypeName: "BINARY",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: 20,
-					ok:     true,
-				},
-				name: "BIN",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf([]byte{}),
-			},
-			{
-				databaseTypeName: "VARBINARY",
-				decimalSize: decimalSize{
-					precision: 0,
-					scale:     0,
-					ok:        false,
-				},
-				length: length{
-					length: math.MaxInt64,
-					ok:     true,
-				},
-				name: "VARBIN",
-				nullable: nullable{
-					nullable: true,
-					ok:       true,
-				},
-				scanType: reflect.TypeOf([]byte{}),
-			},
-		}
-
-		columnTypes, err := rows.ColumnTypes()
-
-		if err != nil {
-			t.Errorf("Error getting column types: %s", err)
-		}
-
-		for index, columnType := range columnTypes {
-
-			expected := expectedColumnTypes[index]
-
-			if columnType.DatabaseTypeName() != expected.databaseTypeName {
-				t.Errorf("Expected database type name for index %d to be %s, got %s", index, expected.databaseTypeName, columnType.DatabaseTypeName())
-			}
-
-			precision, scale, ok := columnType.DecimalSize()
-
-			if precision != expected.decimalSize.precision {
-				t.Errorf("Expected decimal precision for index %d to be %d, got %d", index, expected.decimalSize.precision, precision)
-			}
-
-			if scale != expected.decimalSize.scale {
-				t.Errorf("Expected decimal scale for index %d to be %d, got %d", index, expected.decimalSize.scale, scale)
-			}
-
-			if ok != expected.decimalSize.ok {
-				t.Errorf("Expected decimal ok for index %d to be %t, got %t", index, expected.decimalSize.ok, ok)
-			}
-
-			length, ok := columnType.Length()
-
-			if length != expected.length.length {
-				t.Errorf("Expected length for index %d to be %d, got %d", index, expected.length.length, length)
-			}
-
-			if ok != expected.length.ok {
-				t.Errorf("Expected length ok for index %d to be %t, got %t", index, expected.length.ok, ok)
-			}
-
-			if columnType.Name() != expected.name {
-				t.Errorf("Expected column name for index %d to be %s, got %s", index, expected.name, columnType.Name())
-			}
-
-			nullable, ok := columnType.Nullable()
-
-			if nullable != expected.nullable.nullable {
-				t.Errorf("Expected nullable for index %d to be %t, got %t", index, expected.nullable.nullable, nullable)
-			}
-
-			if ok != expected.nullable.ok {
-				t.Errorf("Expected nullable ok for index %d to be %t, got %t", index, expected.nullable.ok, ok)
-			}
-
-			if columnType.ScanType() != expected.scanType {
-				t.Errorf("Expected scan type for index %d to be %s, got %s", index, expected.scanType, columnType.ScanType())
-			}
-		}
-
-	})
-}

http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/2968def4/driver_hsqldb_test.go
----------------------------------------------------------------------
diff --git a/driver_hsqldb_test.go b/driver_hsqldb_test.go
new file mode 100644
index 0000000..7597cd2
--- /dev/null
+++ b/driver_hsqldb_test.go
@@ -0,0 +1,1028 @@
+/*
+ * 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"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+)
+
+func skipTestIfNotHSQLDB(t *testing.T) {
+
+	val := os.Getenv("AVATICA_FLAVOR")
+
+	if val != "HSQLDB" {
+		t.Skip("Skipping Apache Avatica HSQLDB test")
+	}
+}
+
+func TestHSQLDBConnectionMustBeOpenedWithAutoCommitTrue(t *testing.T) {
+
+	skipTestIfNotHSQLDB(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+
+		// Create and seed table
+		dbt.mustExec("CREATE TABLE " + dbt.tableName + " (id BIGINT PRIMARY KEY, val VARCHAR(1))")
+
+		dbt.mustExec("INSERT INTO " + dbt.tableName + " VALUES (1,'A')")
+
+		dbt.mustExec("INSERT 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 TestHSQLDBZeroValues(t *testing.T) {
+
+	skipTestIfNotHSQLDB(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(1))")
+
+		dbt.mustExec("INSERT 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 TestHSQLDBDataTypes(t *testing.T) {
+
+	// TODO; Test case for Time type is currently commented out due to CALCITE-1951
+
+	skipTestIfNotHSQLDB(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+
+		// Create and seed table
+		/*dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+			int INTEGER PRIMARY KEY,
+			tint TINYINT,
+			sint SMALLINT,
+			bint BIGINT,
+			num NUMERIC(10,3),
+			dec DECIMAL(10,3),
+			re REAL,
+			flt FLOAT,
+			dbl DOUBLE,
+			bool BOOLEAN,
+			ch CHAR(3),
+			var VARCHAR(128),
+			bin BINARY(20),
+			varbin VARBINARY(128),
+			dt DATE,
+			tm TIME,
+			tmstmp TIMESTAMP,
+		    )`)*/
+
+		dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+				int INTEGER PRIMARY KEY,
+				tint TINYINT,
+				sint SMALLINT,
+				bint BIGINT,
+				num NUMERIC(10,3),
+				dec DECIMAL(10,3),
+				re REAL,
+				flt FLOAT,
+				dbl DOUBLE,
+				bool BOOLEAN,
+				ch CHAR(3),
+				var VARCHAR(128),
+				bin BINARY(20),
+				varbin VARBINARY(128),
+				dt DATE,
+				tmstmp TIMESTAMP,
+			    )`)
+
+		var (
+			integerValue int       = -20
+			tintValue    int       = -128
+			sintValue    int       = -32768
+			bintValue    int       = -9223372036854775807
+			numValue     string    = "1.333"
+			decValue     string    = "1.333"
+			reValue      float64   = 3.555
+			fltValue     float64   = -3.555
+			dblValue     float64   = -9.555
+			booleanValue bool      = true
+			chValue      string    = "a"
+			varcharValue string    = "test string"
+			binValue     []byte    = make([]byte, 20, 20)
+			varbinValue  []byte    = []byte("testtesttest")
+			dtValue      time.Time = time.Date(2100, 2, 1, 0, 0, 0, 0, time.UTC)
+			// tmValue      time.Time = time.Date(0, 1, 1, 21, 21, 21, 222000000, time.UTC)
+			tmstmpValue time.Time = time.Date(2100, 2, 1, 21, 21, 21, 222000000, time.UTC)
+		)
+
+		copy(binValue[:], []byte("test"))
+
+		// dbt.mustExec(`INSERT INTO `+dbt.tableName+` (int, tint, sint, bint, num, dec, re, flt, dbl, bool, ch, var, bin, varbin, dt, tm, tmstmp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
+		dbt.mustExec(`INSERT INTO `+dbt.tableName+` (int, tint, sint, bint, num, dec, re, flt, dbl, bool, ch, var, bin, varbin, dt, tmstmp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
+			integerValue,
+			tintValue,
+			sintValue,
+			bintValue,
+			numValue,
+			decValue,
+			reValue,
+			fltValue,
+			dblValue,
+			booleanValue,
+			chValue,
+			varcharValue,
+			binValue,
+			varbinValue,
+			dtValue,
+			// tmValue,
+			tmstmpValue,
+		)
+
+		rows := dbt.mustQuery("SELECT * FROM " + dbt.tableName)
+		defer rows.Close()
+
+		var (
+			integer int
+			tint    int
+			sint    int
+			bint    int
+			num     string
+			dec     string
+			re      float64
+			flt     float64
+			dbl     float64
+			boolean bool
+			ch      string
+			varchar string
+			bin     []byte
+			varbin  []byte
+			dt      time.Time
+			// tm      time.Time
+			tmstmp time.Time
+		)
+
+		for rows.Next() {
+
+			// err := rows.Scan(&integer, &tint, &sint, &bint, &num, &dec, &re, &flt, &dbl, &boolean, &ch, &varchar, &bin, &varbin, &dt, &tm, &tmstmp)
+			err := rows.Scan(&integer, &tint, &sint, &bint, &num, &dec, &re, &flt, &dbl, &boolean, &ch, &varchar, &bin, &varbin, &dt, &tmstmp)
+
+			if err != nil {
+				dbt.Fatal(err)
+			}
+		}
+
+		comparisons := []struct {
+			result   interface{}
+			expected interface{}
+		}{
+			{integer, integerValue},
+			{tint, tintValue},
+			{sint, sintValue},
+			{bint, bintValue},
+			{num, numValue},
+			{dec, decValue},
+			{re, reValue},
+			{flt, fltValue},
+			{dbl, dblValue},
+			{boolean, booleanValue},
+			{ch, chValue + "  "}, // HSQLDB pads CHAR columns if a length is specified
+			{varchar, varcharValue},
+			{bin, binValue},
+			{varbin, varbinValue},
+			{dt, dtValue},
+			//			{tm, tmValue},
+			{tmstmp, tmstmpValue},
+		}
+
+		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)
+			}
+		}
+	})
+}
+
+// TODO: Test case commented out due to CALCITE-1951
+/*func TestHSQLDBLocations(t *testing.T) {
+
+	skipTestIfNotHSQLDB(t)
+
+	query := "?location=Australia/Melbourne"
+
+	runTests(t, dsn+query, func(dbt *DBTest) {
+
+		// Create and seed table
+		dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+				tm TIME(6) PRIMARY KEY,
+				dt DATE,
+				tmstmp TIMESTAMP
+			    )`)
+
+		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(`INSERT INTO `+dbt.tableName+`(tm, dt, tmstmp) 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.Errorf("Expected %v, got %v.", tt.expected, tt.result)
+			}
+		}
+	})
+}*/
+
+func TestHSQLDBDateAndTimestampsBefore1970(t *testing.T) {
+
+	skipTestIfNotHSQLDB(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
+			    )`)
+
+		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(`INSERT INTO `+dbt.tableName+`(int, dt, tmstmp) 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 TestHSQLDBStoreAndRetrieveBinaryData(t *testing.T) {
+
+	skipTestIfNotHSQLDB(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+
+		// Create and seed table
+		// TODO: Switch VARBINARY to BLOB once avatica supports BLOBs and CBLOBs. CALCITE-1957
+		dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+				int INTEGER PRIMARY KEY,
+				bin VARBINARY(999999)
+			    )`)
+
+		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(`INSERT INTO `+dbt.tableName+` (int, bin) 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 TestHSQLDBCommittingTransactions(t *testing.T) {
+
+	skipTestIfNotHSQLDB(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
+			    )`)
+
+		tx, err := dbt.db.Begin()
+
+		if err != nil {
+			t.Fatalf("Unable to create transaction: %s", err)
+		}
+
+		stmt, err := tx.Prepare(`INSERT INTO ` + dbt.tableName + `(int) 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 TestHSQLDBRollingBackTransactions(t *testing.T) {
+
+	skipTestIfNotHSQLDB(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
+			    )`)
+
+		tx, err := dbt.db.Begin()
+
+		if err != nil {
+			t.Fatalf("Unable to create transaction: %s", err)
+		}
+
+		stmt, err := tx.Prepare(`INSERT INTO ` + dbt.tableName + `(int) 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 TestHSQLDBPreparedStatements(t *testing.T) {
+
+	skipTestIfNotHSQLDB(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+
+		// Create and seed table
+		dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+				int INTEGER PRIMARY KEY
+			    )`)
+
+		stmt, err := dbt.db.Prepare(`INSERT INTO ` + dbt.tableName + `(int) 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 TestHSQLDBFetchingMoreRows(t *testing.T) {
+
+	skipTestIfNotHSQLDB(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
+			    )`)
+
+		stmt, err := dbt.db.Prepare(`INSERT INTO ` + dbt.tableName + `(int) 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 TestHSQLDBExecuteShortcut(t *testing.T) {
+
+	skipTestIfNotHSQLDB(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+
+		// Create and seed table
+		dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+				int INTEGER PRIMARY KEY
+			    )`)
+
+		res, err := dbt.db.Exec(`INSERT INTO ` + dbt.tableName + `(int) 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 TestHSQLDBQueryShortcut(t *testing.T) {
+
+	skipTestIfNotHSQLDB(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
+			    )`)
+
+		stmt, err := dbt.db.Prepare(`INSERT INTO ` + dbt.tableName + `(int) 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)
+		}
+	})
+}
+
+// TODO: Test disabled due to CALCITE-2250
+/*func TestHSQLDBOptimisticConcurrency(t *testing.T) {
+
+	skipTestIfNotHSQLDB(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(64),
+				version INTEGER
+			    )`)
+
+		stmt, err := dbt.db.Prepare(`INSERT INTO ` + dbt.tableName + `(id, msg, version) 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(`INSERT INTO `+dbt.tableName+`(id, msg, version) 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(`INSERT INTO `+dbt.tableName+`(id, msg, version) 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 TestHSQLDBLastInsertIDShouldReturnError(t *testing.T) {
+
+	skipTestIfNotHSQLDB(t)
+
+	runTests(t, dsn, func(dbt *DBTest) {
+
+		dbt.mustExec(`CREATE TABLE ` + dbt.tableName + ` (
+				id INTEGER IDENTITY PRIMARY KEY,
+				msg VARCHAR(3),
+				version INTEGER
+			    )`)
+
+		res, err := dbt.db.Exec(`INSERT INTO ` + dbt.tableName + `(msg, version) VALUES('abc', 1)`)
+
+		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 TestHSQLDBSchemaSupport(t *testing.T) {
+
+	skipTestIfNotHSQLDB(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 avaticatest.` + dbt.tableName + ` (
+				int INTEGER PRIMARY KEY
+			    );`)
+
+		defer dbt.mustExec(`DROP TABLE IF EXISTS avaticatest.` + dbt.tableName)
+
+		_, err := dbt.db.Exec(`INSERT INTO avaticatest.` + dbt.tableName + `(int) 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 TestHSQLDBMultipleSchemaSupport(t *testing.T) {
+
+	skipTestIfNotHSQLDB(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) {
+
+		dbt.mustExec(`SET INITIAL SCHEMA avaticatest2`)
+
+		// Create and seed table
+		dbt.mustExec(`CREATE TABLE avaticatest2.` + dbt.tableName + ` (
+				int INTEGER PRIMARY KEY
+			    )`)
+
+		defer dbt.mustExec(`DROP TABLE IF EXISTS avaticatest2.` + dbt.tableName)
+
+		_, err := dbt.db.Exec(`INSERT INTO avaticatest2.` + dbt.tableName + `(int) 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)
+		}
+	})
+}
+
+// TODO: Test disabled due to CALCITE-1049
+/*func TestHSQLDBErrorCodeParsing(t *testing.T) {
+
+	skipTestIfNotHSQLDB(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)
+	}
+}*/


[2/4] calcite-avatica-go git commit: Add HSQLDB support and move phoenix support into an adapter

Posted by fr...@apache.org.
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
 }


[4/4] calcite-avatica-go git commit: Update travis.yaml to run HSQLDB tests

Posted by fr...@apache.org.
Update travis.yaml to run HSQLDB tests


Project: http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/commit/b9d80926
Tree: http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/tree/b9d80926
Diff: http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/diff/b9d80926

Branch: refs/heads/master
Commit: b9d80926cffa64f33b6aa8436f1b316f1de86c29
Parents: 2968def
Author: Francis Chuang <fr...@boostport.com>
Authored: Mon Apr 16 14:26:53 2018 +1000
Committer: Francis Chuang <fr...@boostport.com>
Committed: Mon Apr 16 14:51:38 2018 +1000

----------------------------------------------------------------------
 .travis.yml | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite-avatica-go/blob/b9d80926/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index feb6007..842f63f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,21 +33,23 @@ services:
 
 env:
   global:
-  - AVATICA_IMAGE=boostport/hbase-phoenix-all-in-one:1.3-4.13
-  - AVATICA_HOST=http://localhost:8765
+  - PHOENIX_IMAGE=boostport/hbase-phoenix-all-in-one:1.3-4.13
+  - PHOENIX_HOST=http://localhost:8765
+  - HSQLDB_IMAGE=f21global/calcite-avatica:1.11.0-hypersql
+  - HSQLDB_HOST=http://localhost:8766
 
 before_install:
   - go get -u github.com/golang/dep/cmd/dep
   - dep ensure -v
-  - docker pull $AVATICA_IMAGE
-  - docker run -d -p 8765:8765 $AVATICA_IMAGE
+  - docker pull $PHOENIX_IMAGE
+  - docker pull $HSQLDB_IMAGE
+  - docker run -d -p 8765:8765 $PHOENIX_IMAGE
+  - docker run -d -p 8766:8765 $HSQLDB_IMAGE -u jdbc:hsqldb:mem:public
   - docker ps -a
 
-install:
-  - go build
-
 script:
-  - go test -cover -v ./...
+  - export AVATICA_FLAVOR=HSQLDB && go test -v ./...
+  - export AVATICA_FLAVOR=PHOENIX && go test -v ./...
 
 git:
   depth: 10000