Repository: snowflakedb/gosnowflake
Pull request #332 (2020-09-11 16:52:33)

SNOW-184545 Prepared Statements
### Description
Enable prepared statements (and prepared statements with context) for Golang.

### Checklist
- [x] Code compiles correctly
- [x] Run ``make fmt`` to fix inconsistent formats
- [x] Run ``make lint`` to get lint errors and fix all of them
- [x] Created tests which fail without the change (if possible)
- [x] All tests passing
- [ ] Extended the README / documentation, if necessary



Patch:
diff --git a/connection.go b/connection.go
index 9d8373d..e8e93ac 100644
--- a/connection.go
+++ b/connection.go
@@ -68,17 +68,19 @@ func (sc *snowflakeConn) exec(
 	query string,
 	noResult bool,
 	isInternal bool,
+	describeOnly bool,
 	bindings []driver.NamedValue) (
 	*execResponse, error) {
 	var err error
 	counter := atomic.AddUint64(&sc.SequenceCounter, 1) // query sequence counter
 
 	req := execRequest{
-		SQLText:    query,
-		AsyncExec:  noResult,
-		Parameters: map[string]interface{}{},
-		IsInternal: isInternal,
-		SequenceID: counter,
+		SQLText:      query,
+		AsyncExec:    noResult,
+		Parameters:   map[string]interface{}{},
+		IsInternal:   isInternal,
+		DescribeOnly: describeOnly,
+		SequenceID:   counter,
 	}
 	if key := ctx.Value(MultiStatementCount); key != nil {
 		req.Parameters[string(MultiStatementCount)] = key
@@ -190,7 +192,7 @@ func (sc *snowflakeConn) BeginTx(ctx context.Context, opts driver.TxOptions) (dr
 	if sc.rest == nil {
 		return nil, driver.ErrBadConn
 	}
-	_, err := sc.exec(ctx, "BEGIN", false, false, nil)
+	_, err := sc.exec(ctx, "BEGIN", false /* noResult */, false /* isInternal */, false /* describeOnly */, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -220,6 +222,26 @@ func (sc *snowflakeConn) PrepareContext(ctx context.Context, query string) (driv
 	if sc.rest == nil {
 		return nil, driver.ErrBadConn
 	}
+	noResult, err := isAsyncMode(ctx)
+	if err != nil {
+		return nil, err
+	}
+	data, err := sc.exec(ctx, query, noResult, false /* isInternal */, true /* describeOnly */, []driver.NamedValue{})
+	if err != nil {
+		if data != nil {
+			code, err := strconv.Atoi(data.Code)
+			if err != nil {
+				return nil, err
+			}
+			return nil, &SnowflakeError{
+				Number:   code,
+				SQLState: data.Data.SQLState,
+				Message:  err.Error(),
+				QueryID:  data.Data.QueryID,
+			}
+		}
+		return nil, err
+	}
 	stmt := &snowflakeStmt{
 		sc:    sc,
 		query: query,
@@ -227,10 +249,6 @@ func (sc *snowflakeConn) PrepareContext(ctx context.Context, query string) (driv
 	return stmt, nil
 }
 
-func (sc *snowflakeConn) Prepare(query string) (driver.Stmt, error) {
-	return sc.PrepareContext(sc.ctx, query)
-}
-
 func (sc *snowflakeConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
 	logger.WithContext(ctx).Infof("Exec: %#v, %v", query, args)
 	if sc.rest == nil {
@@ -242,7 +260,7 @@ func (sc *snowflakeConn) ExecContext(ctx context.Context, query string, args []d
 	}
 	// TODO handle isInternal
 	ctx = setResultType(ctx, execResultType)
-	data, err := sc.exec(ctx, query, noResult, false, args)
+	data, err := sc.exec(ctx, query, noResult, false /* isInternal */, false /* describeOnly */, args)
 	if err != nil {
 		logger.WithContext(ctx).Infof("error: %v", err)
 		if data != nil {
@@ -339,7 +357,8 @@ func (sc *snowflakeConn) QueryContext(ctx context.Context, query string, args []
 		return nil, err
 	}
 	ctx = setResultType(ctx, queryResultType)
-	data, err := sc.exec(ctx, query, noResult, false, args)
+	// TODO: handle isInternal
+	data, err := sc.exec(ctx, query, noResult, false /* isInternal */, false /* describeOnly */, args)
 	if err != nil {
 		logger.WithContext(ctx).Errorf("error: %v", err)
 		if data != nil {
@@ -424,6 +443,10 @@ func (sc *snowflakeConn) QueryContext(ctx context.Context, query string, args []
 	return rows, err
 }
 
+func (sc *snowflakeConn) Prepare(query string) (driver.Stmt, error) {
+	return sc.PrepareContext(sc.ctx, query)
+}
+
 func (sc *snowflakeConn) Exec(
 	query string,
 	args []driver.Value) (
@@ -447,7 +470,8 @@ func (sc *snowflakeConn) Ping(ctx context.Context) error {
 	if err != nil {
 		return err
 	}
-	_, err = sc.exec(ctx, "SELECT 1", noResult, false, []driver.NamedValue{})
+	// TODO: handle isInternal
+	_, err = sc.exec(ctx, "SELECT 1", noResult, false /* isInternal */, false /* describeOnly */, []driver.NamedValue{})
 	return err
 }
 
diff --git a/query.go b/query.go
index 9df7e30..76f1f07 100644
--- a/query.go
+++ b/query.go
@@ -14,12 +14,13 @@ type execBindParameter struct {
 }
 
 type execRequest struct {
-	SQLText    string                       `json:"sqlText"`
-	AsyncExec  bool                         `json:"asyncExec"`
-	SequenceID uint64                       `json:"sequenceId"`
-	IsInternal bool                         `json:"isInternal"`
-	Parameters map[string]interface{}       `json:"parameters,omitempty"`
-	Bindings   map[string]execBindParameter `json:"bindings,omitempty"`
+	SQLText      string                       `json:"sqlText"`
+	AsyncExec    bool                         `json:"asyncExec"`
+	SequenceID   uint64                       `json:"sequenceId"`
+	IsInternal   bool                         `json:"isInternal"`
+	DescribeOnly bool                         `json:"describeOnly,omitempty"`
+	Parameters   map[string]interface{}       `json:"parameters,omitempty"`
+	Bindings     map[string]execBindParameter `json:"bindings,omitempty"`
 }
 
 type execResponseRowType struct {
diff --git a/result.go b/result.go
index 3001056..0f0ba06 100644
--- a/result.go
+++ b/result.go
@@ -41,7 +41,7 @@ func (res *snowflakeResult) LastInsertId() (int64, error) {
 
 func (res *snowflakeResult) RowsAffected() (int64, error) {
 	if err := res.waitForAsyncExecStatus(); err != nil {
-		return 0, err
+		return -1, err
 	}
 	return res.affectedRows, nil
 }
diff --git a/transaction.go b/transaction.go
index 298347b..31fa8b6 100644
--- a/transaction.go
+++ b/transaction.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2019 Snowflake Computing Inc. All right reserved.
+// Copyright (c) 2017-2021 Snowflake Computing Inc. All right reserved.
 
 package gosnowflake
 
@@ -14,7 +14,7 @@ func (tx *snowflakeTx) Commit() (err error) {
 	if tx.sc == nil || tx.sc.rest == nil {
 		return driver.ErrBadConn
 	}
-	_, err = tx.sc.exec(tx.sc.ctx, "COMMIT", false, false, nil)
+	_, err = tx.sc.exec(tx.sc.ctx, "COMMIT", false /* noResult */, false /* isInternal */, false /* describeOnly */, nil)
 	if err != nil {
 		return
 	}
@@ -26,7 +26,7 @@ func (tx *snowflakeTx) Rollback() (err error) {
 	if tx.sc == nil || tx.sc.rest == nil {
 		return driver.ErrBadConn
 	}
-	_, err = tx.sc.exec(tx.sc.ctx, "ROLLBACK", false, false, nil)
+	_, err = tx.sc.exec(tx.sc.ctx, "ROLLBACK", false /* noResult */, false /* isInternal */, false /* describeOnly */, nil)
 	if err != nil {
 		return
 	}


Test patch:
diff --git a/async_test.go b/async_test.go
new file mode 100644
index 0000000..e46164a
--- /dev/null
+++ b/async_test.go
@@ -0,0 +1,125 @@
+// Copyright (c) 2021 Snowflake Computing Inc. All right reserved.
+
+package gosnowflake
+
+import (
+	"context"
+	"database/sql"
+	"fmt"
+	"testing"
+)
+
+func TestAsyncMode(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	ctx, _ := WithAsyncMode(context.Background())
+	numrows := 100000
+	rows, _ := db.QueryContext(ctx, fmt.Sprintf("SELECT SEQ8(), RANDSTR(1000, RANDOM()) FROM TABLE(GENERATOR(ROWCOUNT=>%v))", numrows))
+	defer rows.Close()
+
+	cnt := 0
+	var idx int
+	var v string
+	// Next() will block and wait until results are available
+	for rows.Next() {
+		err := rows.Scan(&idx, &v)
+		if err != nil {
+			t.Fatal(err)
+		}
+		cnt++
+	}
+	logger.Infof("NextResultSet: %v", rows.NextResultSet())
+
+	if cnt != numrows {
+		t.Errorf("number of rows didn't match. expected: %v, got: %v", numrows, cnt)
+	}
+
+	_, err = db.Exec("create or replace table test_async_exec (value boolean)")
+	if err != nil {
+		t.Error(err)
+	}
+	res, _ := db.ExecContext(ctx, "insert into test_async_exec values (true)")
+	// RowsAffected() will block and wait until results are available
+	count, err := res.RowsAffected()
+	if err != nil {
+		t.Fatalf("res.RowsAffected() returned error: %v", err)
+	}
+	if count != 1 {
+		t.Fatalf("expected 1 affected row, got %d", count)
+	}
+}
+
+func TestAsyncQueryFail(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	ctx, _ := WithAsyncMode(context.Background())
+	rows, err := db.QueryContext(ctx, "selectt 1")
+	if err != nil {
+		t.Fatal("asynchronous query should always return nil error")
+	}
+	defer rows.Close()
+
+	if rows.Next() {
+		t.Fatal("should have no rows available")
+	} else {
+		if err = rows.Err(); err == nil {
+			t.Fatal("should return a syntax error")
+		}
+	}
+}
+
+func TestMultipleAsyncQueries(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	ctx, _ := WithAsyncMode(context.Background())
+	s1 := "foo"
+	s2 := "bar"
+	rows1, _ := db.QueryContext(ctx, fmt.Sprintf("select distinct '%v' from table (generator(timelimit=>%v))", s1, 30))
+	defer rows1.Close()
+	rows2, _ := db.QueryContext(ctx, fmt.Sprintf("select distinct '%v' from table (generator(timelimit=>%v))", s2, 10))
+	defer rows2.Close()
+
+	ch1 := make(chan string)
+	ch2 := make(chan string)
+	go retrieveRows(rows1, ch1)
+	go retrieveRows(rows2, ch2)
+	select {
+	case res := <-ch1:
+		t.Fatalf("value %v should not have been called earlier.", res)
+	case res := <-ch2:
+		if res != s2 {
+			t.Fatalf("query failed. expected: %v, got: %v", s2, res)
+		}
+	}
+}
+
+func retrieveRows(rows *sql.Rows, ch chan string) {
+	var s string
+	for rows.Next() {
+		if err := rows.Scan(&s); err != nil {
+			ch <- err.Error()
+			close(ch)
+			return
+		}
+	}
+	ch <- s
+	close(ch)
+}
diff --git a/connection_test.go b/connection_test.go
index a9fc7d5..09c3e34 100644
--- a/connection_test.go
+++ b/connection_test.go
@@ -1,3 +1,5 @@
+// Copyright (c) 2019-2021 Snowflake Computing Inc. All right reserved.
+
 package gosnowflake
 
 import (
@@ -58,7 +60,7 @@ func TestExecWithEmptyRequestID(t *testing.T) {
 		cfg:  &Config{Params: map[string]*string{}},
 		rest: sr,
 	}
-	_, err := sc.exec(ctx, "", false, false, nil)
+	_, err := sc.exec(ctx, "", false /* noResult */, false /* isInternal */, false /* describeOnly */, nil)
 	if err != nil {
 		t.Fatalf("err: %v", err)
 	}
@@ -89,7 +91,7 @@ func TestExecWithSpecificRequestID(t *testing.T) {
 		cfg:  &Config{Params: map[string]*string{}},
 		rest: sr,
 	}
-	_, err := sc.exec(ctx, "", false, false, nil)
+	_, err := sc.exec(ctx, "", false /* noResult */, false /* isInternal */, false /* describeOnly */, nil)
 	if err != nil {
 		t.Fatalf("err: %v", err)
 	}
@@ -111,7 +113,7 @@ func TestServiceName(t *testing.T) {
 
 	expectServiceName := serviceNameStub
 	for i := 0; i < 5; i++ {
-		sc.exec(context.TODO(), "", false, false, nil)
+		sc.exec(context.TODO(), "", false /* noResult */, false /* isInternal */, false /* describeOnly */, nil)
 		if actualServiceName, ok := sc.cfg.Params[serviceName]; ok {
 			if *actualServiceName != expectServiceName {
 				t.Errorf("service name mis-match. expected %v, actual %v", expectServiceName, actualServiceName)
diff --git a/multistatement_test.go b/multistatement_test.go
new file mode 100644
index 0000000..39a2039
--- /dev/null
+++ b/multistatement_test.go
@@ -0,0 +1,443 @@
+// Copyright (c) 2020-2021 Snowflake Computing Inc. All right reserved.
+
+package gosnowflake
+
+import (
+	"context"
+	"database/sql"
+	"testing"
+)
+
+func TestMultiStatementExecuteNoResultSet(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	_, err = db.Exec("drop table if exists test_multi_statement_txn")
+	if err != nil {
+		t.Fatalf("failed to drop table: %v", err)
+	}
+
+	_, err = db.Exec("create or replace table test_multi_statement_txn(c1 number, c2 string)" +
+		"as select 10, 'z'")
+	if err != nil {
+		t.Fatalf("failed to create table: %v", err)
+	}
+
+	ctx, _ := WithMultiStatement(context.Background(), 4)
+	multiStmtQuery := "begin;\n" +
+		"delete from test_multi_statement_txn;\n" +
+		"insert into test_multi_statement_txn values (1, 'a'), (2, 'b');\n" +
+		"commit;"
+	res, err := db.ExecContext(ctx, multiStmtQuery)
+	if err != nil {
+		t.Fatalf("failed to execute multiple statements: %v", err)
+	}
+	count, err := res.RowsAffected()
+	if err != nil {
+		t.Fatalf("res.RowsAffected() returned error: %v", err)
+	}
+	if count != 3 {
+		t.Fatalf("expected 3 affected rows, got %d", count)
+	}
+
+	_, err = db.Exec("drop table if exists test_multi_statement_txn")
+	if err != nil {
+		t.Fatalf("failed to drop table: %v", err)
+	}
+}
+
+func TestMultiStatementQueryResultSet(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	multiStmtQuery := "select 123;\n" +
+		"select 456;\n" +
+		"select 789;\n" +
+		"select '000';"
+
+	ctx, _ := WithMultiStatement(context.Background(), 4)
+	rows, err := db.QueryContext(ctx, multiStmtQuery)
+	if err != nil {
+		t.Fatalf("failed to query multiple statements: %v", err)
+	}
+	defer rows.Close()
+	var v1, v2, v3 int64
+	var v4 string
+
+	// first statement
+	if rows.Next() {
+		err = rows.Scan(&v1)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v1 != 123 {
+			t.Fatalf("failed to fetch. value: %v", v1)
+		}
+	} else {
+		t.Error("failed to query")
+	}
+
+	// second statement
+	if !rows.NextResultSet() {
+		t.Error("failed to retrieve next result set")
+	}
+	if rows.Next() {
+		err = rows.Scan(&v2)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v2 != 456 {
+			t.Fatalf("failed to fetch. value: %v", v2)
+		}
+	} else {
+		t.Error("failed to query")
+	}
+
+	// third statement
+	if !rows.NextResultSet() {
+		t.Error("failed to retrieve next result set")
+	}
+	if rows.Next() {
+		err = rows.Scan(&v3)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v3 != 789 {
+			t.Fatalf("failed to fetch. value: %v", v3)
+		}
+	} else {
+		t.Error("failed to query")
+	}
+
+	// fourth statement
+	if !rows.NextResultSet() {
+		t.Error("failed to retrieve next result set")
+	}
+	if rows.Next() {
+		err = rows.Scan(&v4)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v4 != "000" {
+			t.Fatalf("failed to fetch. value: %v", v4)
+		}
+	} else {
+		t.Error("failed to query")
+	}
+}
+
+func TestMultiStatementExecuteResultSet(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	_, err = db.Exec("drop table if exists test_multi_statement_txn_rb")
+	if err != nil {
+		t.Fatalf("failed to drop table: %v", err)
+	}
+
+	_, err = db.Exec("create or replace table test_multi_statement_txn_rb(c1 number, c2 string)" +
+		"as select 10, 'z'")
+	if err != nil {
+		t.Fatalf("failed to create table: %v", err)
+	}
+
+	ctx, _ := WithMultiStatement(context.Background(), 6)
+	multiStmtQuery := "begin;\n" +
+		"delete from test_multi_statement_txn_rb;\n" +
+		"insert into test_multi_statement_txn_rb values (1, 'a'), (2, 'b');\n" +
+		"select 1;\n" +
+		"select 2;\n" +
+		"rollback;"
+
+	res, err := db.ExecContext(ctx, multiStmtQuery)
+	if err != nil {
+		t.Fatalf("failed to execute statement: %v", err)
+	}
+	count, err := res.RowsAffected()
+	if err != nil {
+		t.Fatalf("res.RowsAffected() returned error: %s", err.Error())
+	}
+	if count != 3 {
+		t.Fatalf("expected 3 affected rows, got %d", count)
+	}
+
+	_, err = db.Exec("drop table if exists test_multi_statement_txn_rb")
+	if err != nil {
+		t.Fatalf("failed to drop table: %v", err)
+	}
+}
+
+func TestMultiStatementQueryNoResultSet(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	_, err = db.Exec("drop table if exists test_multi_statement_txn")
+	if err != nil {
+		t.Fatalf("failed to drop table: %v", err)
+	}
+
+	_, err = db.Exec("create or replace table test_multi_statement_txn(c1 number, c2 string)" +
+		"as select 10, 'z'")
+	if err != nil {
+		t.Fatalf("failed to create table: %v", err)
+	}
+
+	ctx, _ := WithMultiStatement(context.Background(), 4)
+	multiStmtQuery := "begin;\n" +
+		"delete from test_multi_statement_txn;\n" +
+		"insert into test_multi_statement_txn values (1, 'a'), (2, 'b');\n" +
+		"commit;"
+	_, err = db.QueryContext(ctx, multiStmtQuery)
+	if err != nil {
+		t.Fatalf("failed to query multiple statements: %v", err)
+	}
+
+	_, err = db.Exec("drop table if exists test_multi_statement_txn")
+	if err != nil {
+		t.Fatalf("failed to drop table: %v", err)
+	}
+}
+
+func TestMultiStatementExecuteMix(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	ctx, _ := WithMultiStatement(context.Background(), 3)
+	multiStmtQuery := "create or replace temporary table test_multi (cola int);\n" +
+		"insert into test_multi values (1), (2);\n" +
+		"select cola from test_multi order by cola asc;"
+	res, err := db.ExecContext(ctx, multiStmtQuery)
+	if err != nil {
+		t.Fatal("failed to execute statement: ", err)
+	}
+
+	count, err := res.RowsAffected()
+	if err != nil {
+		t.Fatalf("res.RowsAffected() returned error: %s", err.Error())
+	}
+	if count != 2 {
+		t.Fatalf("expected 3 affected rows, got %d", count)
+	}
+}
+
+func TestMultiStatementQueryMix(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	ctx, _ := WithMultiStatement(context.Background(), 3)
+	multiStmtQuery := "create or replace temporary table test_multi (cola int);\n" +
+		"insert into test_multi values (1), (2);\n" +
+		"select cola from test_multi order by cola asc;"
+	rows, err := db.QueryContext(ctx, multiStmtQuery)
+	if err != nil {
+		t.Fatal("failed to execute statement: ", err)
+	}
+	defer rows.Close()
+
+	// first statement
+	if !rows.Next() {
+		t.Error("failed to query")
+	}
+
+	var count, v int
+	// second statement
+	rows.NextResultSet()
+	if rows.Next() {
+		err = rows.Scan(&count)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if count != 2 {
+			t.Fatalf("expected 2 affected rows, got %d", count)
+		}
+	}
+
+	expected := 1
+	// third statement
+	rows.NextResultSet()
+	for rows.Next() {
+		err = rows.Scan(&v)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v != expected {
+			t.Fatalf("failed to fetch. value: %v", v)
+		}
+		expected++
+	}
+}
+
+func TestMultiStatementCountZero(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	ctx, _ := WithMultiStatement(context.Background(), 0)
+	var v1 int
+	var v2 string
+	var v3 float64
+	var v4 bool
+
+	// first query
+	multiStmtQuery1 := "select 123;\n" +
+		"select '456';"
+	rows1, err := db.QueryContext(ctx, multiStmtQuery1)
+	if err != nil {
+		t.Fatalf("failed to query multiple statements: %v", err)
+	}
+	defer rows1.Close()
+	// first statement
+	if rows1.Next() {
+		err = rows1.Scan(&v1)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v1 != 123 {
+			t.Fatalf("failed to fetch. value: %v", v1)
+		}
+	} else {
+		t.Error("failed to query")
+	}
+
+	// second statement
+	if !rows1.NextResultSet() {
+		t.Error("failed to retrieve next result set")
+	}
+	if rows1.Next() {
+		err = rows1.Scan(&v2)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v2 != "456" {
+			t.Fatalf("failed to fetch. value: %v", v2)
+		}
+	} else {
+		t.Error("failed to query")
+	}
+
+	// second query
+	multiStmtQuery2 := "select 789;\n" +
+		"select 'foo';\n" +
+		"select 0.123;\n" +
+		"select true;"
+	rows2, err := db.QueryContext(ctx, multiStmtQuery2)
+	if err != nil {
+		t.Fatalf("failed to query multiple statements: %v", err)
+	}
+	defer rows2.Close()
+	// first statement
+	if rows2.Next() {
+		err = rows2.Scan(&v1)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v1 != 789 {
+			t.Fatalf("failed to fetch. value: %v", v1)
+		}
+	} else {
+		t.Error("failed to query")
+	}
+
+	// second statement
+	if !rows2.NextResultSet() {
+		t.Error("failed to retrieve next result set")
+	}
+	if rows2.Next() {
+		err = rows2.Scan(&v2)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v2 != "foo" {
+			t.Fatalf("failed to fetch. value: %v", v2)
+		}
+	} else {
+		t.Error("failed to query")
+	}
+
+	// third statement
+	if !rows2.NextResultSet() {
+		t.Error("failed to retrieve next result set")
+	}
+	if rows2.Next() {
+		err = rows2.Scan(&v3)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v3 != 0.123 {
+			t.Fatalf("failed to fetch. value: %v", v3)
+		}
+	} else {
+		t.Error("failed to query")
+	}
+
+	// fourth statement
+	if !rows2.NextResultSet() {
+		t.Error("failed to retrieve next result set")
+	}
+	if rows2.Next() {
+		err = rows2.Scan(&v4)
+		if err != nil {
+			t.Errorf("failed to scan: %#v", err)
+		}
+		if v4 != true {
+			t.Fatalf("failed to fetch. value: %v", v4)
+		}
+	} else {
+		t.Error("failed to query")
+	}
+}
+
+func TestMultiStatementCountMismatch(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	multiStmtQuery := "select 123;\n" +
+		"select 456;\n" +
+		"select 789;\n" +
+		"select '000';"
+
+	ctx, _ := WithMultiStatement(context.Background(), 3)
+	_, err = db.QueryContext(ctx, multiStmtQuery)
+	if err == nil {
+		t.Fatal("should have failed to query multiple statements")
+	}
+}
diff --git a/prepared_statement_test.go b/prepared_statement_test.go
new file mode 100644
index 0000000..17165af
--- /dev/null
+++ b/prepared_statement_test.go
@@ -0,0 +1,53 @@
+// Copyright (c) 2021 Snowflake Computing Inc. All right reserved.
+
+package gosnowflake
+
+import (
+	"database/sql"
+	"testing"
+)
+
+// TestPreparedStatement creates a basic prepared statement, inserting values
+// after the statement has been prepared
+func TestPreparedStatement(t *testing.T) {
+	var db *sql.DB
+	var err error
+
+	if db, err = sql.Open("snowflake", dsn); err != nil {
+		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
+	}
+	defer db.Close()
+
+	db.Exec("create or replace table test(c1 int, c2 string)")
+	intArray := []int{1, 2, 3}
+	strArray := []string{"test1", "test2", "test3"}
+
+	stmt, err := db.Prepare("insert into test values (?, ?)")
+	if err != nil {
+		t.Error(err)
+	}
+	defer stmt.Close()
+	stmt.Exec(intArray, strArray)
+
+	cnt := 0
+	rows, _ := db.Query("select * from test order by 1")
+	defer rows.Close()
+	var v1 int
+	var v2 string
+	for rows.Next() {
+		err := rows.Scan(&v1, &v2)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if intArray[cnt] != v1 {
+			t.Errorf("failed to scan. expected: %v, got %v", intArray[cnt], v1)
+		}
+		if strArray[cnt] != v2 {
+			t.Errorf("failed to scan. expected: %v, got %v", strArray[cnt], v2)
+		}
+		cnt++
+	}
+	if cnt != len(intArray) {
+		t.Errorf("number of rows didn't match. expected: %v, got: %v", len(intArray), cnt)
+	}
+}
diff --git a/statement_test.go b/statement_test.go
index b11d382..68211b9 100644
--- a/statement_test.go
+++ b/statement_test.go
@@ -7,444 +7,9 @@ import (
 	"database/sql"
 	"database/sql/driver"
 	"fmt"
-	"io"
 	"testing"
 )
 
-func TestMultiStatementExecuteNoResultSet(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	_, err = db.Exec("drop table if exists test_multi_statement_txn")
-	if err != nil {
-		t.Fatalf("failed to drop table: %v", err)
-	}
-
-	_, err = db.Exec("create or replace table test_multi_statement_txn(c1 number, c2 string)" +
-		"as select 10, 'z'")
-	if err != nil {
-		t.Fatalf("failed to create table: %v", err)
-	}
-
-	ctx, _ := WithMultiStatement(context.Background(), 4)
-	multiStmtQuery := "begin;\n" +
-		"delete from test_multi_statement_txn;\n" +
-		"insert into test_multi_statement_txn values (1, 'a'), (2, 'b');\n" +
-		"commit;"
-	res, err := db.ExecContext(ctx, multiStmtQuery)
-	if err != nil {
-		t.Fatalf("failed to execute multiple statements: %v", err)
-	}
-	count, err := res.RowsAffected()
-	if err != nil {
-		t.Fatalf("res.RowsAffected() returned error: %v", err)
-	}
-	if count != 3 {
-		t.Fatalf("expected 3 affected rows, got %d", count)
-	}
-
-	_, err = db.Exec("drop table if exists test_multi_statement_txn")
-	if err != nil {
-		t.Fatalf("failed to drop table: %v", err)
-	}
-}
-
-func TestMultiStatementQueryResultSet(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	multiStmtQuery := "select 123;\n" +
-		"select 456;\n" +
-		"select 789;\n" +
-		"select '000';"
-
-	ctx, _ := WithMultiStatement(context.Background(), 4)
-	rows, err := db.QueryContext(ctx, multiStmtQuery)
-	if err != nil {
-		t.Fatalf("failed to query multiple statements: %v", err)
-	}
-	defer rows.Close()
-	var v1, v2, v3 int64
-	var v4 string
-
-	// first statement
-	if rows.Next() {
-		err = rows.Scan(&v1)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v1 != 123 {
-			t.Fatalf("failed to fetch. value: %v", v1)
-		}
-	} else {
-		t.Error("failed to query")
-	}
-
-	// second statement
-	if !rows.NextResultSet() {
-		t.Error("failed to retrieve next result set")
-	}
-	if rows.Next() {
-		err = rows.Scan(&v2)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v2 != 456 {
-			t.Fatalf("failed to fetch. value: %v", v2)
-		}
-	} else {
-		t.Error("failed to query")
-	}
-
-	// third statement
-	if !rows.NextResultSet() {
-		t.Error("failed to retrieve next result set")
-	}
-	if rows.Next() {
-		err = rows.Scan(&v3)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v3 != 789 {
-			t.Fatalf("failed to fetch. value: %v", v3)
-		}
-	} else {
-		t.Error("failed to query")
-	}
-
-	// fourth statement
-	if !rows.NextResultSet() {
-		t.Error("failed to retrieve next result set")
-	}
-	if rows.Next() {
-		err = rows.Scan(&v4)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v4 != "000" {
-			t.Fatalf("failed to fetch. value: %v", v4)
-		}
-	} else {
-		t.Error("failed to query")
-	}
-}
-
-func TestMultiStatementExecuteResultSet(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	_, err = db.Exec("drop table if exists test_multi_statement_txn_rb")
-	if err != nil {
-		t.Fatalf("failed to drop table: %v", err)
-	}
-
-	_, err = db.Exec("create or replace table test_multi_statement_txn_rb(c1 number, c2 string)" +
-		"as select 10, 'z'")
-	if err != nil {
-		t.Fatalf("failed to create table: %v", err)
-	}
-
-	ctx, _ := WithMultiStatement(context.Background(), 6)
-	multiStmtQuery := "begin;\n" +
-		"delete from test_multi_statement_txn_rb;\n" +
-		"insert into test_multi_statement_txn_rb values (1, 'a'), (2, 'b');\n" +
-		"select 1;\n" +
-		"select 2;\n" +
-		"rollback;"
-
-	res, err := db.ExecContext(ctx, multiStmtQuery)
-	if err != nil {
-		t.Fatalf("failed to execute statement: %v", err)
-	}
-	count, err := res.RowsAffected()
-	if err != nil {
-		t.Fatalf("res.RowsAffected() returned error: %s", err.Error())
-	}
-	if count != 3 {
-		t.Fatalf("expected 3 affected rows, got %d", count)
-	}
-
-	_, err = db.Exec("drop table if exists test_multi_statement_txn_rb")
-	if err != nil {
-		t.Fatalf("failed to drop table: %v", err)
-	}
-}
-
-func TestMultiStatementQueryNoResultSet(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	_, err = db.Exec("drop table if exists test_multi_statement_txn")
-	if err != nil {
-		t.Fatalf("failed to drop table: %v", err)
-	}
-
-	_, err = db.Exec("create or replace table test_multi_statement_txn(c1 number, c2 string)" +
-		"as select 10, 'z'")
-	if err != nil {
-		t.Fatalf("failed to create table: %v", err)
-	}
-
-	ctx, _ := WithMultiStatement(context.Background(), 4)
-	multiStmtQuery := "begin;\n" +
-		"delete from test_multi_statement_txn;\n" +
-		"insert into test_multi_statement_txn values (1, 'a'), (2, 'b');\n" +
-		"commit;"
-	_, err = db.QueryContext(ctx, multiStmtQuery)
-	if err != nil {
-		t.Fatalf("failed to query multiple statements: %v", err)
-	}
-
-	_, err = db.Exec("drop table if exists test_multi_statement_txn")
-	if err != nil {
-		t.Fatalf("failed to drop table: %v", err)
-	}
-}
-
-func TestMultiStatementExecuteMix(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	ctx, _ := WithMultiStatement(context.Background(), 3)
-	multiStmtQuery := "create or replace temporary table test_multi (cola int);\n" +
-		"insert into test_multi values (1), (2);\n" +
-		"select cola from test_multi order by cola asc;"
-	res, err := db.ExecContext(ctx, multiStmtQuery)
-	if err != nil {
-		t.Fatal("failed to execute statement: ", err)
-	}
-
-	count, err := res.RowsAffected()
-	if err != nil {
-		t.Fatalf("res.RowsAffected() returned error: %s", err.Error())
-	}
-	if count != 2 {
-		t.Fatalf("expected 3 affected rows, got %d", count)
-	}
-}
-
-func TestMultiStatementQueryMix(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	ctx, _ := WithMultiStatement(context.Background(), 3)
-	multiStmtQuery := "create or replace temporary table test_multi (cola int);\n" +
-		"insert into test_multi values (1), (2);\n" +
-		"select cola from test_multi order by cola asc;"
-	rows, err := db.QueryContext(ctx, multiStmtQuery)
-	if err != nil {
-		t.Fatal("failed to execute statement: ", err)
-	}
-	defer rows.Close()
-
-	// first statement
-	if !rows.Next() {
-		t.Error("failed to query")
-	}
-
-	var count, v int
-	// second statement
-	rows.NextResultSet()
-	if rows.Next() {
-		err = rows.Scan(&count)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if count != 2 {
-			t.Fatalf("expected 2 affected rows, got %d", count)
-		}
-	}
-
-	expected := 1
-	// third statement
-	rows.NextResultSet()
-	for rows.Next() {
-		err = rows.Scan(&v)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v != expected {
-			t.Fatalf("failed to fetch. value: %v", v)
-		}
-		expected++
-	}
-}
-
-func TestMultiStatementCountZero(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	ctx, _ := WithMultiStatement(context.Background(), 0)
-	var v1 int
-	var v2 string
-	var v3 float64
-	var v4 bool
-
-	// first query
-	multiStmtQuery1 := "select 123;\n" +
-		"select '456';"
-	rows1, err := db.QueryContext(ctx, multiStmtQuery1)
-	if err != nil {
-		t.Fatalf("failed to query multiple statements: %v", err)
-	}
-	defer rows1.Close()
-	// first statement
-	if rows1.Next() {
-		err = rows1.Scan(&v1)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v1 != 123 {
-			t.Fatalf("failed to fetch. value: %v", v1)
-		}
-	} else {
-		t.Error("failed to query")
-	}
-
-	// second statement
-	if !rows1.NextResultSet() {
-		t.Error("failed to retrieve next result set")
-	}
-	if rows1.Next() {
-		err = rows1.Scan(&v2)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v2 != "456" {
-			t.Fatalf("failed to fetch. value: %v", v2)
-		}
-	} else {
-		t.Error("failed to query")
-	}
-
-	// second query
-	multiStmtQuery2 := "select 789;\n" +
-		"select 'foo';\n" +
-		"select 0.123;\n" +
-		"select true;"
-	rows2, err := db.QueryContext(ctx, multiStmtQuery2)
-	if err != nil {
-		t.Fatalf("failed to query multiple statements: %v", err)
-	}
-	defer rows2.Close()
-	// first statement
-	if rows2.Next() {
-		err = rows2.Scan(&v1)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v1 != 789 {
-			t.Fatalf("failed to fetch. value: %v", v1)
-		}
-	} else {
-		t.Error("failed to query")
-	}
-
-	// second statement
-	if !rows2.NextResultSet() {
-		t.Error("failed to retrieve next result set")
-	}
-	if rows2.Next() {
-		err = rows2.Scan(&v2)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v2 != "foo" {
-			t.Fatalf("failed to fetch. value: %v", v2)
-		}
-	} else {
-		t.Error("failed to query")
-	}
-
-	// third statement
-	if !rows2.NextResultSet() {
-		t.Error("failed to retrieve next result set")
-	}
-	if rows2.Next() {
-		err = rows2.Scan(&v3)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v3 != 0.123 {
-			t.Fatalf("failed to fetch. value: %v", v3)
-		}
-	} else {
-		t.Error("failed to query")
-	}
-
-	// fourth statement
-	if !rows2.NextResultSet() {
-		t.Error("failed to retrieve next result set")
-	}
-	if rows2.Next() {
-		err = rows2.Scan(&v4)
-		if err != nil {
-			t.Errorf("failed to scan: %#v", err)
-		}
-		if v4 != true {
-			t.Fatalf("failed to fetch. value: %v", v4)
-		}
-	} else {
-		t.Error("failed to query")
-	}
-}
-
-func TestMultiStatementCountMismatch(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	multiStmtQuery := "select 123;\n" +
-		"select 456;\n" +
-		"select 789;\n" +
-		"select '000';"
-
-	ctx, _ := WithMultiStatement(context.Background(), 3)
-	_, err = db.QueryContext(ctx, multiStmtQuery)
-	if err == nil {
-		t.Fatal("should have failed to query multiple statements")
-	}
-}
-
 func TestGetQueryID(t *testing.T) {
 	var db *sql.DB
 	var err error
@@ -472,11 +37,7 @@ func TestGetQueryID(t *testing.T) {
 			t.Fatal("should have returned a query ID string")
 		}
 
-		stmt, err = x.(driver.ConnPrepareContext).PrepareContext(ctx, "selectt 1")
-		if err != nil {
-			return err
-		}
-		rows, err = stmt.(driver.StmtQueryContext).QueryContext(ctx, nil)
+		_, err = x.(driver.ConnPrepareContext).PrepareContext(ctx, "selectt 1")
 		if err == nil {
 			t.Fatal("should have failed to execute query")
 		}
@@ -490,9 +51,6 @@ func TestGetQueryID(t *testing.T) {
 		} else {
 			t.Fatal("should have been able to cast to Snowflake Error")
 		}
-		if rows != nil {
-			t.Fatal("rows should be empty")
-		}
 		return nil
 	})
 	if err != nil {
@@ -500,7 +58,7 @@ func TestGetQueryID(t *testing.T) {
 	}
 }
 
-func TestAsyncMode(t *testing.T) {
+func TestEmitQueryID(t *testing.T) {
 	var db *sql.DB
 	var err error
 
@@ -509,15 +67,23 @@ func TestAsyncMode(t *testing.T) {
 	}
 	defer db.Close()
 
-	ctx, _ := WithAsyncMode(context.Background())
+	queryIDChan := make(chan string, 1)
 	numrows := 100000
-	rows, _ := db.QueryContext(ctx, fmt.Sprintf("SELECT SEQ8(), RANDSTR(1000, RANDOM()) FROM TABLE(GENERATOR(ROWCOUNT=>%v))", numrows))
-	defer rows.Close()
+	ctx, _ := WithAsyncMode(context.Background())
+	ctx = WithQueryIDChan(ctx, queryIDChan)
+
+	goRoutineChan := make(chan string)
+	go func(grCh chan string, qIDch chan string) {
+		queryID := <-queryIDChan
+		grCh <- queryID
+	}(goRoutineChan, queryIDChan)
 
 	cnt := 0
 	var idx int
 	var v string
-	// Next() will block and wait until results are available
+	rows, _ := db.QueryContext(ctx, fmt.Sprintf("SELECT SEQ8(), RANDSTR(1000, RANDOM()) FROM TABLE(GENERATOR(ROWCOUNT=>%v))", numrows))
+	defer rows.Close()
+
 	for rows.Next() {
 		err := rows.Scan(&idx, &v)
 		if err != nil {
@@ -527,167 +93,11 @@ func TestAsyncMode(t *testing.T) {
 	}
 	logger.Infof("NextResultSet: %v", rows.NextResultSet())
 
+	queryID := <-goRoutineChan
+	if queryID == "" {
+		t.Fatal("expected a nonempty query ID")
+	}
 	if cnt != numrows {
 		t.Errorf("number of rows didn't match. expected: %v, got: %v", numrows, cnt)
 	}
-
-	db.Exec("create table test (value boolean)")
-	res, _ := db.ExecContext(ctx, "insert into test values (true)")
-	// RowsAffected() will block and wait until results are available
-	count, err := res.RowsAffected()
-	if err != nil || count != 1 {
-		t.Fatalf("count was invalid. err: %v, count: %v", err, count)
-	}
-}
-
-func TestAsyncQueryFail(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	ctx, _ := WithAsyncMode(context.Background())
-	rows, err := db.QueryContext(ctx, "selectt 1")
-	if err != nil {
-		t.Fatal("asynchronous query should always return nil error")
-	}
-	defer rows.Close()
-
-	if rows.Next() {
-		t.Fatal("should have no rows available")
-	} else {
-		if err = rows.Err(); err == nil {
-			t.Fatal("should return a syntax error")
-		}
-	}
-
-	conn, _ := db.Conn(ctx)
-	err = conn.Raw(func(x interface{}) error {
-		stmt, err := x.(driver.ConnPrepareContext).PrepareContext(ctx, "selectt 1")
-		if err != nil {
-			return err
-		}
-		rows, err := stmt.(driver.StmtQueryContext).QueryContext(ctx, nil)
-		if err != nil {
-			t.Fatal("asynchronous query should always return nil error")
-		}
-		defer rows.Close()
-
-		dest := make([]driver.Value, 1)
-		err = rows.Next(dest)
-		if err == nil {
-			t.Fatal("should return a syntax error")
-		}
-		return nil
-	})
-	if err != nil {
-		t.Fatalf("failed to query statement. err: %v", err)
-	}
-}
-
-func TestMultipleAsyncQueries(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	ctx, _ := WithAsyncMode(context.Background())
-	s1 := "foo"
-	s2 := "bar"
-	rows1, _ := db.QueryContext(ctx, fmt.Sprintf("select distinct '%v' from table (generator(timelimit=>%v))", s1, 30))
-	defer rows1.Close()
-	rows2, _ := db.QueryContext(ctx, fmt.Sprintf("select distinct '%v' from table (generator(timelimit=>%v))", s2, 10))
-	defer rows2.Close()
-
-	ch1 := make(chan string)
-	ch2 := make(chan string)
-	go retrieveRows(rows1, ch1)
-	go retrieveRows(rows2, ch2)
-	select {
-	case res := <-ch1:
-		t.Fatalf("value %v should not have been called earlier.", res)
-	case res := <-ch2:
-		if res != s2 {
-			t.Fatalf("query failed. expected: %v, got: %v", s2, res)
-		}
-	}
-}
-
-func retrieveRows(rows *sql.Rows, ch chan string) {
-	var s string
-	for rows.Next() {
-		if err := rows.Scan(&s); err != nil {
-			ch <- err.Error()
-			close(ch)
-			return
-		}
-	}
-	ch <- s
-	close(ch)
-}
-
-func TestEmitQueryID(t *testing.T) {
-	var db *sql.DB
-	var err error
-
-	if db, err = sql.Open("snowflake", dsn); err != nil {
-		t.Fatalf("failed to open db. %v, err: %v", dsn, err)
-	}
-	defer db.Close()
-
-	queryIDChan := make(chan string, 1)
-	numrows := 100000
-	ctx, _ := WithAsyncMode(context.Background())
-	ctx = WithQueryIDChan(ctx, queryIDChan)
-	conn, _ := db.Conn(ctx)
-
-	dest := make([]driver.Value, 2)
-
-	goRoutineChan := make(chan string)
-	go func(grCh chan string, qIDch chan string) {
-		queryID := <- queryIDChan
-		grCh <- queryID
-	}(goRoutineChan, queryIDChan)
-
-	err = conn.Raw(func(x interface{}) error {
-		stmt, err := x.(driver.ConnPrepareContext).PrepareContext(ctx, fmt.Sprintf("SELECT SEQ8(), RANDSTR(1000, RANDOM()) FROM TABLE(GENERATOR(ROWCOUNT=>%v))", numrows))
-		if err != nil {
-			return err
-		}
-		rows, err := stmt.(driver.StmtQueryContext).QueryContext(ctx, nil)
-		if err != nil {
-			return err
-		}
-		defer rows.Close()
-
-		cnt := 0
-		for {
-			err = rows.Next(dest)
-			if err != nil {
-				if err == io.EOF {
-					break
-				} else {
-					t.Fatalf("unexpected error: %v", err)
-				}
-			}
-			cnt++
-		}
-		queryID := <- goRoutineChan
-		if queryID == "" {
-			t.Fatal("expected a nonempty query ID")
-		}
-		if cnt != numrows {
-			t.Errorf("number of rows didn't match. expected: %v, got: %v", numrows, cnt)
-		}
-		return nil
-	})
-	if err != nil {
-		t.Fatalf("failed to execute query. err: %v", err)
-	}
 }