Repository: snowflakedb/gosnowflake
Pull request #525 (2022-01-18 00:37:48)

Refactor & Lint
### Description
Refactoring and linting based on code path & relevancy.

### 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
- [x] Extended the README / documentation, if necessary



Patch:
diff --git a/chunk_arrow.go b/arrow_chunk.go
similarity index 96%
rename from chunk_arrow.go
rename to arrow_chunk.go
index 764ba64..b9f043b 100644
--- a/chunk_arrow.go
+++ b/arrow_chunk.go
@@ -52,9 +52,7 @@ func (arc *arrowResultChunk) decodeArrowChunk(rowType []execResponseRowType, hig
 	}
 }
 
-/**
-Build arrow chunk based on RowSet of base64
-*/
+// Build arrow chunk based on RowSet of base64
 func buildFirstArrowChunk(rowsetBase64 string) arrowResultChunk {
 	rowSetBytes, err := base64.StdEncoding.DecodeString(rowsetBase64)
 	if err != nil {
diff --git a/async.go b/async.go
new file mode 100644
index 0000000..ccf2808
--- /dev/null
+++ b/async.go
@@ -0,0 +1,147 @@
+// Copyright (c) 2021-2022 Snowflake Computing Inc. All rights reserved.
+
+package gosnowflake
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/url"
+	"strconv"
+	"time"
+)
+
+func (sr *snowflakeRestful) processAsync(
+	ctx context.Context,
+	respd *execResponse,
+	headers map[string]string,
+	timeout time.Duration,
+	cfg *Config) (*execResponse, error) {
+	// placeholder object to return to user while retrieving results
+	rows := new(snowflakeRows)
+	res := new(snowflakeResult)
+	switch resType := getResultType(ctx); resType {
+	case execResultType:
+		res.queryID = respd.Data.QueryID
+		res.status = QueryStatusInProgress
+		res.errChannel = make(chan error)
+		respd.Data.AsyncResult = res
+	case queryResultType:
+		rows.queryID = respd.Data.QueryID
+		rows.status = QueryStatusInProgress
+		rows.errChannel = make(chan error)
+		respd.Data.AsyncRows = rows
+	default:
+		return respd, nil
+	}
+
+	// spawn goroutine to retrieve asynchronous results
+	go sr.getAsync(ctx, headers, sr.getFullURL(respd.Data.GetResultURL, nil), timeout, res, rows, cfg)
+	return respd, nil
+}
+
+func (sr *snowflakeRestful) getAsync(
+	ctx context.Context,
+	headers map[string]string,
+	URL *url.URL,
+	timeout time.Duration,
+	res *snowflakeResult,
+	rows *snowflakeRows,
+	cfg *Config) error {
+	resType := getResultType(ctx)
+	var errChannel chan error
+	sfError := &SnowflakeError{
+		Number: -1,
+	}
+	if resType == execResultType {
+		errChannel = res.errChannel
+		sfError.QueryID = res.queryID
+	} else {
+		errChannel = rows.errChannel
+		sfError.QueryID = rows.queryID
+	}
+	defer close(errChannel)
+	token, _, _ := sr.TokenAccessor.GetTokens()
+	headers[headerAuthorizationKey] = fmt.Sprintf(headerSnowflakeToken, token)
+	resp, err := sr.FuncGet(ctx, sr, URL, headers, timeout)
+	if err != nil {
+		logger.WithContext(ctx).Errorf("failed to get response. err: %v", err)
+		sfError.Message = err.Error()
+		errChannel <- sfError
+		close(errChannel)
+		return err
+	}
+	if resp.Body != nil {
+		defer resp.Body.Close()
+	}
+
+	respd := execResponse{}
+	err = json.NewDecoder(resp.Body).Decode(&respd)
+	resp.Body.Close()
+	if err != nil {
+		logger.WithContext(ctx).Errorf("failed to decode JSON. err: %v", err)
+		sfError.Message = err.Error()
+		errChannel <- sfError
+		close(errChannel)
+		return err
+	}
+
+	sc := &snowflakeConn{rest: sr, cfg: cfg}
+	if respd.Success {
+		if resType == execResultType {
+			res.insertID = -1
+			if isDml(respd.Data.StatementTypeID) {
+				res.affectedRows, err = updateRows(respd.Data)
+				if err != nil {
+					return err
+				}
+			} else if isMultiStmt(&respd.Data) {
+				r, err := sc.handleMultiExec(ctx, respd.Data)
+				if err != nil {
+					res.errChannel <- err
+					close(errChannel)
+					return err
+				}
+				res.affectedRows, err = r.RowsAffected()
+				if err != nil {
+					res.errChannel <- err
+					close(errChannel)
+					return err
+				}
+			}
+			res.queryID = respd.Data.QueryID
+			res.errChannel <- nil // mark exec status complete
+		} else {
+			rows.sc = sc
+			rows.queryID = respd.Data.QueryID
+			if isMultiStmt(&respd.Data) {
+				if err = sc.handleMultiQuery(ctx, respd.Data, rows); err != nil {
+					rows.errChannel <- err
+					close(errChannel)
+					return err
+				}
+			} else {
+				rows.addDownloader(populateChunkDownloader(ctx, sc, respd.Data))
+			}
+			rows.ChunkDownloader.start()
+			rows.errChannel <- nil // mark query status complete
+		}
+	} else {
+		var code int
+		if respd.Code != "" {
+			code, err = strconv.Atoi(respd.Code)
+			if err != nil {
+				code = -1
+			}
+		} else {
+			code = -1
+		}
+		errChannel <- &SnowflakeError{
+			Number:   code,
+			SQLState: respd.Data.SQLState,
+			Message:  respd.Message,
+			QueryID:  respd.Data.QueryID,
+		}
+	}
+	return nil
+}
diff --git a/bind_uploader.go b/bind_uploader.go
index 1a5708d..b56ed97 100644
--- a/bind_uploader.go
+++ b/bind_uploader.go
@@ -10,6 +10,8 @@ import (
 	"reflect"
 	"strconv"
 	"strings"
+
+	"github.com/google/uuid"
 )
 
 const (
@@ -167,6 +169,34 @@ func (bu *bindUploader) createCSVRecord(data []string) []byte {
 	return []byte(b.String())
 }
 
+func (sc *snowflakeConn) processBindings(
+	ctx context.Context,
+	bindings []driver.NamedValue,
+	describeOnly bool,
+	requestID uuid.UUID,
+	req *execRequest) error {
+	arrayBindThreshold := sc.getArrayBindStageThreshold()
+	numBinds := arrayBindValueCount(bindings)
+	if 0 < arrayBindThreshold && arrayBindThreshold <= numBinds && !describeOnly && isArrayBind(bindings) {
+		uploader := bindUploader{
+			sc:        sc,
+			ctx:       ctx,
+			stagePath: "@" + bindStageName + "/" + requestID.String(),
+		}
+		uploader.upload(bindings)
+		req.Bindings = nil
+		req.BindStage = uploader.stagePath
+	} else {
+		var err error
+		req.Bindings, err = getBindValues(bindings)
+		if err != nil {
+			return err
+		}
+		req.BindStage = ""
+	}
+	return nil
+}
+
 func getBindValues(bindings []driver.NamedValue) (map[string]execBindParameter, error) {
 	tsmode := timestampNtzType
 	idx := 1
diff --git a/chunk_downloader.go b/chunk_downloader.go
index d5e633f..642a2b5 100644
--- a/chunk_downloader.go
+++ b/chunk_downloader.go
@@ -216,10 +216,7 @@ func (scd *snowflakeChunkDownloader) getChunkMetas() []execResponseChunk {
 }
 
 func (scd *snowflakeChunkDownloader) getQueryResultFormat() resultFormat {
-	if scd.QueryResultFormat == "json" {
-		return jsonFormat
-	}
-	return arrowFormat
+	return resultFormat(scd.QueryResultFormat)
 }
 
 func (scd *snowflakeChunkDownloader) setNextChunkDownloader(nextDownloader chunkDownloader) {
diff --git a/cmd/logger/logger.go b/cmd/logger/logger.go
index 82ee3f0..328d941 100644
--- a/cmd/logger/logger.go
+++ b/cmd/logger/logger.go
@@ -9,7 +9,7 @@ import (
 )
 
 type testLogger struct {
-	rlog.Logger
+	*rlog.Logger
 }
 
 func (log *testLogger) SetLogLevel(level string) error {
@@ -22,7 +22,7 @@ func (log *testLogger) SetLogLevel(level string) error {
 }
 
 func createTestLogger() testLogger {
-	var logging = testLogger{*rlog.New()}
+	var logging = testLogger{rlog.New()}
 	var formatter = rlog.JSONFormatter{CallerPrettyfier: sf.SFCallerPrettyfier}
 	logging.SetReportCaller(true)
 	logging.SetFormatter(&formatter)
diff --git a/cmd/selectmany/selectmany.go b/cmd/selectmany/selectmany.go
index 7689d56..73faafe 100644
--- a/cmd/selectmany/selectmany.go
+++ b/cmd/selectmany/selectmany.go
@@ -19,8 +19,10 @@ import (
 	sf "github.com/snowflakedb/gosnowflake"
 )
 
-var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
-var memprofile = flag.String("memprofile", "", "write memory profile to this file")
+var (
+	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
+	memprofile = flag.String("memprofile", "", "write memory profile to this file")
+)
 
 // getDSN constructs a DSN based on the test connection parameters
 func getDSN() (string, *sf.Config, error) {
@@ -95,18 +97,11 @@ func run(dsn string) {
 		log.Fatalf("failed to run a query. %v, err: %v", query, err)
 	}
 	defer rows.Close()
-	var v1 int
-	var v2 int
-	var v3 int
-	var v4 int
-	var v5 int
-	var v6 int
-	var v7 int
+	var v1, v2, v3, v4, v5, v6, v7 int
 	fmt.Printf("Fetching the results. It may take long. You may stop by Ctrl+C.\n")
 	counter := 0
 	for rows.Next() {
-		err := rows.Scan(&v1, &v2, &v3, &v4, &v5, &v6, &v7)
-		if err != nil {
+		if err = rows.Scan(&v1, &v2, &v3, &v4, &v5, &v6, &v7); err != nil {
 			log.Fatalf("failed to get result. err: %v", err)
 		}
 		if counter%10000 == 0 {
diff --git a/connection.go b/connection.go
index 89f13db..aa99a02 100644
--- a/connection.go
+++ b/connection.go
@@ -7,7 +7,6 @@ import (
 	"database/sql"
 	"database/sql/driver"
 	"encoding/json"
-	"fmt"
 	"net/http"
 	"net/url"
 	"os"
@@ -16,7 +15,6 @@ import (
 	"strings"
 	"sync"
 	"sync/atomic"
-	"time"
 )
 
 const (
@@ -91,32 +89,16 @@ func (sc *snowflakeConn) exec(
 	}
 	logger.WithContext(ctx).Infof("parameters: %v", req.Parameters)
 
+	// handle bindings, if required
 	requestID := getOrGenerateRequestIDFromContext(ctx)
 	if len(bindings) > 0 {
-		arrayBindThreshold := sc.getArrayBindStageThreshold()
-		numBinds := arrayBindValueCount(bindings)
-		if 0 < arrayBindThreshold && arrayBindThreshold <= numBinds &&
-			!describeOnly && isArrayBind(bindings) {
-			// bulk array insert binding
-			uploader := bindUploader{
-				sc:        sc,
-				ctx:       ctx,
-				stagePath: "@" + bindStageName + "/" + requestID.String(),
-			}
-			uploader.upload(bindings)
-			req.Bindings = nil
-			req.BindStage = uploader.stagePath
-		} else {
-			// variable or array binding
-			req.Bindings, err = getBindValues(bindings)
-			if err != nil {
-				return nil, err
-			}
-			req.BindStage = ""
+		if err = sc.processBindings(ctx, bindings, describeOnly, requestID, &req); err != nil {
+			return nil, err
 		}
 	}
 	logger.WithContext(ctx).Infof("bindings: %v", req.Bindings)
 
+	// populate headers
 	headers := getHeaders()
 	if isFileTransfer(query) {
 		headers[httpHeaderAccept] = headerContentTypeApplicationJSON
@@ -130,21 +112,17 @@ func (sc *snowflakeConn) exec(
 		return nil, err
 	}
 
-	var data *execResponse
-	data, err = sc.rest.FuncPostQuery(ctx, sc.rest, &url.Values{}, headers,
+	data, err := sc.rest.FuncPostQuery(ctx, sc.rest, &url.Values{}, headers,
 		jsonBody, sc.rest.RequestTimeout, requestID, sc.cfg)
 	if err != nil {
 		return data, err
 	}
-	var code int
+	code := -1
 	if data.Code != "" {
 		code, err = strconv.Atoi(data.Code)
 		if err != nil {
-			code = -1
 			return data, err
 		}
-	} else {
-		code = -1
 	}
 	logger.WithContext(ctx).Infof("Success: %v, Code: %v", data.Success, code)
 	if !data.Success {
@@ -155,6 +133,8 @@ func (sc *snowflakeConn) exec(
 			QueryID:  data.Data.QueryID,
 		}).exceptionTelemetry(sc)
 	}
+
+	// handle PUT/GET commands
 	if isFileTransfer(query) {
 		data, err = sc.processFileTransfer(ctx, data, query, isInternal)
 		if err != nil {
@@ -450,183 +430,6 @@ func (sc *snowflakeConn) GetQueryStatus(
 	}, nil
 }
 
-func (sc *snowflakeConn) handleMultiExec(
-	ctx context.Context,
-	data execResponseData) (
-	driver.Result, error) {
-	var updatedRows int64
-	childResults := getChildResults(data.ResultIDs, data.ResultTypes)
-	for _, child := range childResults {
-		resultPath := fmt.Sprintf(urlQueriesResultFmt, child.id)
-		childData, err := sc.getQueryResultResp(ctx, resultPath)
-		if err != nil {
-			logger.Errorf("error: %v", err)
-			code, err := strconv.Atoi(childData.Code)
-			if err != nil {
-				return nil, err
-			}
-			if childData != nil {
-				return nil, (&SnowflakeError{
-					Number:   code,
-					SQLState: childData.Data.SQLState,
-					Message:  err.Error(),
-					QueryID:  childData.Data.QueryID,
-				}).exceptionTelemetry(sc)
-			}
-			return nil, err
-		}
-		if isDml(childData.Data.StatementTypeID) {
-			count, err := updateRows(childData.Data)
-			if err != nil {
-				logger.WithContext(ctx).Errorf("error: %v", err)
-				if childData != nil {
-					code, err := strconv.Atoi(childData.Code)
-					if err != nil {
-						return nil, err
-					}
-					return nil, (&SnowflakeError{
-						Number:   code,
-						SQLState: childData.Data.SQLState,
-						Message:  err.Error(),
-						QueryID:  childData.Data.QueryID,
-					}).exceptionTelemetry(sc)
-				}
-				return nil, err
-			}
-			updatedRows += count
-		}
-	}
-	logger.WithContext(ctx).Infof("number of updated rows: %#v", updatedRows)
-	return &snowflakeResult{
-		affectedRows: updatedRows,
-		insertID:     -1,
-		queryID:      sc.QueryID,
-	}, nil
-}
-
-// Fill the corresponding rows and add chunk downloader into the rows when
-// iterating across the childResults
-func (sc *snowflakeConn) handleMultiQuery(
-	ctx context.Context,
-	data execResponseData,
-	rows *snowflakeRows) error {
-	childResults := getChildResults(data.ResultIDs, data.ResultTypes)
-	for _, child := range childResults {
-		if err := sc.rowsForRunningQuery(ctx, child.id, rows); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func getAsync(
-	ctx context.Context,
-	sr *snowflakeRestful,
-	headers map[string]string,
-	URL *url.URL,
-	timeout time.Duration,
-	res *snowflakeResult,
-	rows *snowflakeRows,
-	cfg *Config,
-) error {
-	resType := getResultType(ctx)
-	var errChannel chan error
-	sfError := &SnowflakeError{
-		Number: -1,
-	}
-	if resType == execResultType {
-		errChannel = res.errChannel
-		sfError.QueryID = res.queryID
-	} else {
-		errChannel = rows.errChannel
-		sfError.QueryID = rows.queryID
-	}
-	defer close(errChannel)
-	token, _, _ := sr.TokenAccessor.GetTokens()
-	headers[headerAuthorizationKey] = fmt.Sprintf(headerSnowflakeToken, token)
-	resp, err := sr.FuncGet(ctx, sr, URL, headers, timeout)
-	if err != nil {
-		logger.WithContext(ctx).Errorf("failed to get response. err: %v", err)
-		sfError.Message = err.Error()
-		errChannel <- sfError
-		close(errChannel)
-		return err
-	}
-	if resp.Body != nil {
-		defer resp.Body.Close()
-	}
-
-	respd := execResponse{}
-	err = json.NewDecoder(resp.Body).Decode(&respd)
-	resp.Body.Close()
-	if err != nil {
-		logger.WithContext(ctx).Errorf("failed to decode JSON. err: %v", err)
-		sfError.Message = err.Error()
-		errChannel <- sfError
-		close(errChannel)
-		return err
-	}
-
-	sc := &snowflakeConn{rest: sr, cfg: cfg}
-	if respd.Success {
-		if resType == execResultType {
-			res.insertID = -1
-			if isDml(respd.Data.StatementTypeID) {
-				res.affectedRows, err = updateRows(respd.Data)
-				if err != nil {
-					return err
-				}
-			} else if isMultiStmt(&respd.Data) {
-				r, err := sc.handleMultiExec(ctx, respd.Data)
-				if err != nil {
-					res.errChannel <- err
-					close(errChannel)
-					return err
-				}
-				res.affectedRows, err = r.RowsAffected()
-				if err != nil {
-					res.errChannel <- err
-					close(errChannel)
-					return err
-				}
-			}
-			res.queryID = respd.Data.QueryID
-			res.errChannel <- nil // mark exec status complete
-		} else {
-			rows.sc = sc
-			rows.queryID = respd.Data.QueryID
-			if isMultiStmt(&respd.Data) {
-				if err = sc.handleMultiQuery(ctx, respd.Data, rows); err != nil {
-					rows.errChannel <- err
-					close(errChannel)
-					return err
-				}
-			} else {
-				rows.addDownloader(populateChunkDownloader(ctx, sc, respd.Data))
-			}
-			rows.ChunkDownloader.start()
-			rows.errChannel <- nil // mark query status complete
-		}
-	} else {
-		var code int
-		if respd.Code != "" {
-			code, err = strconv.Atoi(respd.Code)
-			if err != nil {
-				code = -1
-			}
-		} else {
-			code = -1
-		}
-		errChannel <- &SnowflakeError{
-			Number:   code,
-			SQLState: respd.Data.SQLState,
-			Message:  respd.Message,
-			QueryID:  respd.Data.QueryID,
-		}
-	}
-	return nil
-}
-
 func buildSnowflakeConn(ctx context.Context, config Config) (*snowflakeConn, error) {
 	sc := &snowflakeConn{
 		SequenceCounter: 0,
diff --git a/connection_util.go b/connection_util.go
index 56b5334..062ff91 100644
--- a/connection_util.go
+++ b/connection_util.go
@@ -224,24 +224,6 @@ func getResumeQueryID(ctx context.Context) (string, error) {
 	return strVal, nil
 }
 
-type childResult struct {
-	id  string
-	typ string
-}
-
-func getChildResults(IDs string, types string) []childResult {
-	if IDs == "" {
-		return nil
-	}
-	queryIDs := strings.Split(IDs, ",")
-	resultTypes := strings.Split(types, ",")
-	res := make([]childResult, len(queryIDs))
-	for i, id := range queryIDs {
-		res[i] = childResult{id, resultTypes[i]}
-	}
-	return res
-}
-
 // returns snowflake chunk downloader by default or stream based chunk
 // downloader if option provided through context
 func populateChunkDownloader(
diff --git a/file_transfer_agent.go b/file_transfer_agent.go
index 304872a..883ad12 100644
--- a/file_transfer_agent.go
+++ b/file_transfer_agent.go
@@ -29,8 +29,10 @@ import (
 	"github.com/gabriel-vasile/mimetype"
 )
 
-type cloudType string
-type commandType string
+type (
+	cloudType   string
+	commandType string
+)
 
 const (
 	fileProtocol              = "file://"
@@ -44,8 +46,9 @@ const (
 	uploadCommand   commandType = "UPLOAD"
 	downloadCommand commandType = "DOWNLOAD"
 	unknownCommand  commandType = "UNKNOWN"
-	putRegexp       string      = `(?i)^(?:/\*.*\*/\s*)*put\s+`
-	getRegexp       string      = `(?i)^(?:/\*.*\*/\s*)*get\s+`
+
+	putRegexp string = `(?i)^(?:/\*.*\*/\s*)*put\s+`
+	getRegexp string = `(?i)^(?:/\*.*\*/\s*)*get\s+`
 )
 
 const (
@@ -346,8 +349,7 @@ func (sfa *snowflakeFileTransferAgent) initFileMetadata() error {
 				Number:   ErrFileNotExists,
 				SQLState: sfa.data.SQLState,
 				QueryID:  sfa.data.QueryID,
-				Message: fmt.Sprintf("file does not exist: %v",
-					fileName),
+				Message:  fmt.Sprintf("file does not exist: %v", fileName),
 			}).exceptionTelemetry(sfa.sc)
 		}
 		if sfa.sourceStream != nil {
@@ -369,16 +371,14 @@ func (sfa *snowflakeFileTransferAgent) initFileMetadata() error {
 						Number:   ErrFileNotExists,
 						SQLState: sfa.data.SQLState,
 						QueryID:  sfa.data.QueryID,
-						Message: fmt.Sprintf("file does not exist: %v",
-							fileName),
+						Message:  fmt.Sprintf("file does not exist: %v", fileName),
 					}).exceptionTelemetry(sfa.sc)
 				} else if fi.IsDir() {
 					return (&SnowflakeError{
 						Number:   ErrFileNotExists,
 						SQLState: sfa.data.SQLState,
 						QueryID:  sfa.data.QueryID,
-						Message: fmt.Sprintf("not a file but a directory: %v",
-							fileName),
+						Message:  fmt.Sprintf("not a file but a directory: %v", fileName),
 					}).exceptionTelemetry(sfa.sc)
 				}
 				sfa.fileMetadata = append(sfa.fileMetadata, &fileMetadata{
@@ -403,11 +403,9 @@ func (sfa *snowflakeFileTransferAgent) initFileMetadata() error {
 		for _, fileName := range sfa.srcFiles {
 			if len(fileName) > 0 {
 				firstPathSep := strings.Index(fileName, "/")
-				var dstFileName string
+				dstFileName := fileName
 				if firstPathSep >= 0 {
 					dstFileName = fileName[firstPathSep+1:]
-				} else {
-					dstFileName = fileName
 				}
 				sfa.fileMetadata = append(sfa.fileMetadata, &fileMetadata{
 					name:              baseName(fileName),
@@ -633,7 +631,8 @@ func (sfa *snowflakeFileTransferAgent) getLocalFilePathFromCommand(command strin
 	filePathBeginIdx := strings.Index(command, fileProtocol)
 	isFilePathQuoted := command[filePathBeginIdx-1] == '\''
 	filePathBeginIdx += len(fileProtocol)
-	filePath, filePathEndIdx := "", 0
+	var filePathEndIdx int
+	filePath := ""
 
 	if isFilePathQuoted {
 		filePathEndIdx = filePathBeginIdx + strings.Index(command[filePathBeginIdx:], "'")
@@ -1016,7 +1015,8 @@ func (sfa *snowflakeFileTransferAgent) renewExpiredClient() (cloudClient, error)
 }
 
 func (sfa *snowflakeFileTransferAgent) result() (*execResponse, error) {
-	data := new(execResponseData)
+	// inherit old response data
+	data := sfa.data
 	rowset := make([]fileTransferResultType, 0)
 	if sfa.commandType == uploadCommand {
 		if len(sfa.results) > 0 {
diff --git a/local_util.go b/local_storage_client.go
similarity index 100%
rename from local_util.go
rename to local_storage_client.go
diff --git a/multistatement.go b/multistatement.go
new file mode 100644
index 0000000..2903402
--- /dev/null
+++ b/multistatement.go
@@ -0,0 +1,98 @@
+// Copyright (c) 2021-2022 Snowflake Computing Inc. All rights reserved.
+
+package gosnowflake
+
+import (
+	"context"
+	"database/sql/driver"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+type childResult struct {
+	id  string
+	typ string
+}
+
+func getChildResults(IDs string, types string) []childResult {
+	if IDs == "" {
+		return nil
+	}
+	queryIDs := strings.Split(IDs, ",")
+	resultTypes := strings.Split(types, ",")
+	res := make([]childResult, len(queryIDs))
+	for i, id := range queryIDs {
+		res[i] = childResult{id, resultTypes[i]}
+	}
+	return res
+}
+
+func (sc *snowflakeConn) handleMultiExec(
+	ctx context.Context,
+	data execResponseData) (
+	driver.Result, error) {
+	var updatedRows int64
+	childResults := getChildResults(data.ResultIDs, data.ResultTypes)
+	for _, child := range childResults {
+		resultPath := fmt.Sprintf(urlQueriesResultFmt, child.id)
+		childData, err := sc.getQueryResultResp(ctx, resultPath)
+		if err != nil {
+			logger.Errorf("error: %v", err)
+			code, err := strconv.Atoi(childData.Code)
+			if err != nil {
+				return nil, err
+			}
+			if childData != nil {
+				return nil, (&SnowflakeError{
+					Number:   code,
+					SQLState: childData.Data.SQLState,
+					Message:  err.Error(),
+					QueryID:  childData.Data.QueryID,
+				}).exceptionTelemetry(sc)
+			}
+			return nil, err
+		}
+		if isDml(childData.Data.StatementTypeID) {
+			count, err := updateRows(childData.Data)
+			if err != nil {
+				logger.WithContext(ctx).Errorf("error: %v", err)
+				if childData != nil {
+					code, err := strconv.Atoi(childData.Code)
+					if err != nil {
+						return nil, err
+					}
+					return nil, (&SnowflakeError{
+						Number:   code,
+						SQLState: childData.Data.SQLState,
+						Message:  err.Error(),
+						QueryID:  childData.Data.QueryID,
+					}).exceptionTelemetry(sc)
+				}
+				return nil, err
+			}
+			updatedRows += count
+		}
+	}
+	logger.WithContext(ctx).Infof("number of updated rows: %#v", updatedRows)
+	return &snowflakeResult{
+		affectedRows: updatedRows,
+		insertID:     -1,
+		queryID:      sc.QueryID,
+	}, nil
+}
+
+// Fill the corresponding rows and add chunk downloader into the rows when
+// iterating across the childResults
+func (sc *snowflakeConn) handleMultiQuery(
+	ctx context.Context,
+	data execResponseData,
+	rows *snowflakeRows) error {
+	childResults := getChildResults(data.ResultIDs, data.ResultTypes)
+	for _, child := range childResults {
+		if err := sc.rowsForRunningQuery(ctx, child.id, rows); err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/restful.go b/restful.go
index fa671c1..f43ce9f 100644
--- a/restful.go
+++ b/restful.go
@@ -43,11 +43,10 @@ const (
 	heartBeatPath            = "/session/heartbeat"
 )
 
-// FuncGetType httpclient GET method to return http.Response
-type FuncGetType func(context.Context, *snowflakeRestful, *url.URL, map[string]string, time.Duration) (*http.Response, error)
-
-// FuncPostType httpclient POST method to return http.Response
-type FuncPostType func(context.Context, *snowflakeRestful, *url.URL, map[string]string, []byte, time.Duration, bool) (*http.Response, error)
+type (
+	funcGetType  func(context.Context, *snowflakeRestful, *url.URL, map[string]string, time.Duration) (*http.Response, error)
+	funcPostType func(context.Context, *snowflakeRestful, *url.URL, map[string]string, []byte, time.Duration, bool) (*http.Response, error)
+)
 
 type snowflakeRestful struct {
 	Host           string
@@ -64,8 +63,8 @@ type snowflakeRestful struct {
 
 	FuncPostQuery       func(context.Context, *snowflakeRestful, *url.Values, map[string]string, []byte, time.Duration, uuid.UUID, *Config) (*execResponse, error)
 	FuncPostQueryHelper func(context.Context, *snowflakeRestful, *url.Values, map[string]string, []byte, time.Duration, uuid.UUID, *Config) (*execResponse, error)
-	FuncPost            FuncPostType
-	FuncGet             FuncGetType
+	FuncPost            funcPostType
+	FuncGet             funcGetType
 	FuncRenewSession    func(context.Context, *snowflakeRestful, time.Duration) error
 	FuncPostAuth        func(context.Context, *snowflakeRestful, *url.Values, map[string]string, []byte, time.Duration) (*authResponse, error)
 	FuncCloseSession    func(context.Context, *snowflakeRestful, time.Duration) error
@@ -182,8 +181,7 @@ func postRestfulQuery(
 		return data, err
 	}
 
-	err = sr.FuncCancelQuery(context.TODO(), sr, requestID, timeout)
-	if err != nil {
+	if err = sr.FuncCancelQuery(context.TODO(), sr, requestID, timeout); err != nil {
 		return nil, err
 	}
 	return nil, ctx.Err()
@@ -211,7 +209,6 @@ func postRestfulQueryHelper(
 	var resp *http.Response
 	fullURL := sr.getFullURL(queryRequestPath, params)
 	resp, err = sr.FuncPost(ctx, sr, fullURL, headers, body, timeout, true)
-
 	if err != nil {
 		return nil, err
 	}
@@ -220,14 +217,12 @@ func postRestfulQueryHelper(
 	if resp.StatusCode == http.StatusOK {
 		logger.WithContext(ctx).Infof("postQuery: resp: %v", resp)
 		var respd execResponse
-		err = json.NewDecoder(resp.Body).Decode(&respd)
-		if err != nil {
+		if err = json.NewDecoder(resp.Body).Decode(&respd); err != nil {
 			logger.WithContext(ctx).Errorf("failed to decode JSON. err: %v", err)
 			return nil, err
 		}
 		if respd.Code == sessionExpiredCode {
-			err = sr.renewExpiredSessionToken(ctx, timeout, token)
-			if err != nil {
+			if err = sr.renewExpiredSessionToken(ctx, timeout, token); err != nil {
 				return nil, err
 			}
 			return sr.FuncPostQuery(ctx, sr, params, headers, body, timeout, requestID, cfg)
@@ -239,44 +234,21 @@ func postRestfulQueryHelper(
 			ctx = WithQueryIDChan(ctx, nil)
 		}
 
-		var resultURL string
 		isSessionRenewed := false
-		noResult := isAsyncMode(ctx)
 
 		// if asynchronous query in progress, kick off retrieval but return object
-		if respd.Code == queryInProgressAsyncCode && noResult {
-			// placeholder object to return to user while retrieving results
-			rows := new(snowflakeRows)
-			res := new(snowflakeResult)
-			switch resType := getResultType(ctx); resType {
-			case execResultType:
-				res.queryID = respd.Data.QueryID
-				res.status = QueryStatusInProgress
-				res.errChannel = make(chan error)
-				respd.Data.AsyncResult = res
-			case queryResultType:
-				rows.queryID = respd.Data.QueryID
-				rows.status = QueryStatusInProgress
-				rows.errChannel = make(chan error)
-				respd.Data.AsyncRows = rows
-			default:
-				return &respd, nil
-			}
-
-			// spawn goroutine to retrieve asynchronous results
-			go getAsync(ctx, sr, headers, sr.getFullURL(respd.Data.GetResultURL, nil), timeout, res, rows, cfg)
-			return &respd, nil
+		if respd.Code == queryInProgressAsyncCode && isAsyncMode(ctx) {
+			return sr.processAsync(ctx, &respd, headers, timeout, cfg)
 		}
 		for isSessionRenewed || respd.Code == queryInProgressCode ||
 			respd.Code == queryInProgressAsyncCode {
 			if !isSessionRenewed {
-				resultURL = respd.Data.GetResultURL
+				fullURL = sr.getFullURL(respd.Data.GetResultURL, nil)
 			}
 
 			logger.Info("ping pong")
-			token, _, _ := sr.TokenAccessor.GetTokens()
+			token, _, _ = sr.TokenAccessor.GetTokens()
 			headers[headerAuthorizationKey] = fmt.Sprintf(headerSnowflakeToken, token)
-			fullURL := sr.getFullURL(resultURL, nil)
 
 			resp, err = sr.FuncGet(ctx, sr, fullURL, headers, timeout)
 			if err != nil {
@@ -291,8 +263,7 @@ func postRestfulQueryHelper(
 				return nil, err
 			}
 			if respd.Code == sessionExpiredCode {
-				err = sr.renewExpiredSessionToken(ctx, timeout, token)
-				if err != nil {
+				if err = sr.renewExpiredSessionToken(ctx, timeout, token); err != nil {
 					return nil, err
 				}
 				isSessionRenewed = true
@@ -336,8 +307,7 @@ func closeSession(ctx context.Context, sr *snowflakeRestful, timeout time.Durati
 	defer resp.Body.Close()
 	if resp.StatusCode == http.StatusOK {
 		var respd renewSessionResponse
-		err = json.NewDecoder(resp.Body).Decode(&respd)
-		if err != nil {
+		if err = json.NewDecoder(resp.Body).Decode(&respd); err != nil {
 			logger.WithContext(ctx).Errorf("failed to decode JSON. err: %v", err)
 			return err
 		}
@@ -468,8 +438,7 @@ func cancelQuery(ctx context.Context, sr *snowflakeRestful, requestID uuid.UUID,
 	defer resp.Body.Close()
 	if resp.StatusCode == http.StatusOK {
 		var respd cancelQueryResponse
-		err = json.NewDecoder(resp.Body).Decode(&respd)
-		if err != nil {
+		if err = json.NewDecoder(resp.Body).Decode(&respd); err != nil {
 			logger.WithContext(ctx).Errorf("failed to decode JSON. err: %v", err)
 			return err
 		}
diff --git a/result.go b/result.go
index 4678f36..7fbe3cf 100644
--- a/result.go
+++ b/result.go
@@ -5,8 +5,6 @@ package gosnowflake
 type queryStatus string
 
 const (
-	// QueryStatusWaiting denotes a query execution waiting to happen
-	QueryStatusWaiting queryStatus = "queryStatusWaiting"
 	// QueryStatusInProgress denotes a query execution in progress
 	QueryStatusInProgress queryStatus = "queryStatusInProgress"
 	// QueryStatusComplete denotes a completed query execution
diff --git a/retry.go b/retry.go
index f0ec754..e44d6f5 100644
--- a/retry.go
+++ b/retry.go
@@ -25,19 +25,18 @@ func init() {
 	random = rand.New(rand.NewSource(time.Now().UnixNano()))
 }
 
-// requestGUIDKey is attached to every request against Snowflake
-const requestGUIDKey string = "request_guid"
-
-// retryCounterKey is attached to query-request from the second time
-const retryCounterKey string = "retryCounter"
-
-// requestIDKey is attached to all requests to Snowflake
-const requestIDKey string = "requestId"
+const (
+	// requestGUIDKey is attached to every request against Snowflake
+	requestGUIDKey string = "request_guid"
+	// retryCounterKey is attached to query-request from the second time
+	retryCounterKey string = "retryCounter"
+	// requestIDKey is attached to all requests to Snowflake
+	requestIDKey string = "requestId"
+)
 
-// This class takes in an url during construction and replace the
-// value of request_guid every time the replace() is called
-// When the url does not contain request_guid, just return the original
-// url
+// This class takes in an url during construction and replaces the value of
+// request_guid every time replace() is called. If the url does not contain
+// request_guid, just return the original url
 type requestGUIDReplacer interface {
 	// replace the url with new ID
 	replace() *url.URL
diff --git a/rows.go b/rows.go
index 356d01b..bc15f5f 100644
--- a/rows.go
+++ b/rows.go
@@ -145,7 +145,7 @@ func (rows *snowflakeRows) GetStatus() queryStatus {
 }
 
 func (rows *snowflakeRows) Next(dest []driver.Value) (err error) {
-	if err := rows.waitForAsyncQueryStatus(); err != nil {
+	if err = rows.waitForAsyncQueryStatus(); err != nil {
 		return err
 	}
 	row, err := rows.ChunkDownloader.next()
@@ -165,7 +165,7 @@ func (rows *snowflakeRows) Next(dest []driver.Value) (err error) {
 		for i, n := 0, len(row.RowSet); i < n; i++ {
 			// could move to chunk downloader so that each go routine
 			// can convert data
-			err := stringToValue(&dest[i], rows.ChunkDownloader.getRowType()[i], row.RowSet[i])
+			err = stringToValue(&dest[i], rows.ChunkDownloader.getRowType()[i], row.RowSet[i])
 			if err != nil {
 				return err
 			}
diff --git a/util.go b/util.go
index 9ef1409..eb3cdf0 100644
--- a/util.go
+++ b/util.go
@@ -17,21 +17,13 @@ import (
 type contextKey string
 
 const (
-	// multiStatementCount controls the number of queries to execute in a single API call
-	multiStatementCount contextKey = "MULTI_STATEMENT_COUNT"
-	// asyncMode tells the server to not block the request on executing the entire query
-	asyncMode contextKey = "ASYNC_MODE_QUERY"
-	// queryIDChannel is the channel to receive the query ID from
-	queryIDChannel contextKey = "QUERY_ID_CHANNEL"
-	// snowflakeRequestIDKey is optional context key to specify request id
+	multiStatementCount   contextKey = "MULTI_STATEMENT_COUNT"
+	asyncMode             contextKey = "ASYNC_MODE_QUERY"
+	queryIDChannel        contextKey = "QUERY_ID_CHANNEL"
 	snowflakeRequestIDKey contextKey = "SNOWFLAKE_REQUEST_ID"
-	// fetchResultByID the queryID of query result to fetch
-	fetchResultByID contextKey = "SF_FETCH_RESULT_BY_ID"
-	// fileStreamFile is the address of the file to be uploaded via PUT
-	fileStreamFile contextKey = "STREAMING_PUT_FILE"
-	// fileTransferOptions allows the user to pass in custom
-	fileTransferOptions contextKey = "FILE_TRANSFER_OPTIONS"
-	// enableHigherPrecision returns numbers with higher precision in a *big format
+	fetchResultByID       contextKey = "SF_FETCH_RESULT_BY_ID"
+	fileStreamFile        contextKey = "STREAMING_PUT_FILE"
+	fileTransferOptions   contextKey = "FILE_TRANSFER_OPTIONS"
 	enableHigherPrecision contextKey = "ENABLE_HIGHER_PRECISION"
 )
 


Test patch:
diff --git a/arrow_test.go b/arrow_test.go
new file mode 100644
index 0000000..949efd9
--- /dev/null
+++ b/arrow_test.go
@@ -0,0 +1,411 @@
+// Copyright (c) 2020-2022 Snowflake Computing Inc. All rights reserved.
+
+package gosnowflake
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"math/big"
+	"reflect"
+	"strings"
+	"testing"
+)
+
+func TestArrowBigInt(t *testing.T) {
+	db := openDB(t)
+	dbt := &DBTest{t, db}
+
+	testcases := []struct {
+		num  string
+		prec int
+		sc   int
+	}{
+		{"10000000000000000000000000000000000000", 38, 0},
+		{"-10000000000000000000000000000000000000", 38, 0},
+		{"12345678901234567890123456789012345678", 38, 0},
+		{"-12345678901234567890123456789012345678", 38, 0},
+		{"99999999999999999999999999999999999999", 38, 0},
+		{"-99999999999999999999999999999999999999", 38, 0},
+	}
+
+	for _, tc := range testcases {
+		rows := dbt.mustQueryContext(WithHigherPrecision(context.Background()),
+			fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+		if !rows.Next() {
+			dbt.Error("failed to query")
+		}
+		defer rows.Close()
+		var v *big.Int
+		if err := rows.Scan(&v); err != nil {
+			dbt.Errorf("failed to scan. %#v", err)
+		}
+
+		b, ok := new(big.Int).SetString(tc.num, 10)
+		if !ok {
+			dbt.Errorf("failed to convert %v big.Int.", tc.num)
+		}
+		if v.Cmp(b) != 0 {
+			dbt.Errorf("big.Int value mismatch: expected %v, got %v", b, v)
+		}
+	}
+}
+
+func TestArrowBigFloat(t *testing.T) {
+	db := openDB(t)
+	dbt := &DBTest{t, db}
+
+	testcases := []struct {
+		num  string
+		prec int
+		sc   int
+	}{
+		{"1.23", 30, 2},
+		{"1.0000000000000000000000000000000000000", 38, 37},
+		{"-1.0000000000000000000000000000000000000", 38, 37},
+		{"1.2345678901234567890123456789012345678", 38, 37},
+		{"-1.2345678901234567890123456789012345678", 38, 37},
+		{"9.9999999999999999999999999999999999999", 38, 37},
+		{"-9.9999999999999999999999999999999999999", 38, 37},
+	}
+
+	for _, tc := range testcases {
+		rows := dbt.mustQueryContext(WithHigherPrecision(context.Background()),
+			fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+		if !rows.Next() {
+			dbt.Error("failed to query")
+		}
+		defer rows.Close()
+		var v *big.Float
+		if err := rows.Scan(&v); err != nil {
+			dbt.Errorf("failed to scan. %#v", err)
+		}
+
+		prec := v.Prec()
+		b, ok := new(big.Float).SetPrec(prec).SetString(tc.num)
+		if !ok {
+			dbt.Errorf("failed to convert %v to big.Float.", tc.num)
+		}
+		if v.Cmp(b) != 0 {
+			dbt.Errorf("big.Float value mismatch: expected %v, got %v", b, v)
+		}
+	}
+}
+
+func TestArrowIntPrecision(t *testing.T) {
+	db := openDB(t)
+	dbt := &DBTest{t, db}
+
+	intTestcases := []struct {
+		num  string
+		prec int
+		sc   int
+	}{
+		{"10000000000000000000000000000000000000", 38, 0},
+		{"-10000000000000000000000000000000000000", 38, 0},
+		{"12345678901234567890123456789012345678", 38, 0},
+		{"-12345678901234567890123456789012345678", 38, 0},
+		{"99999999999999999999999999999999999999", 38, 0},
+		{"-99999999999999999999999999999999999999", 38, 0},
+	}
+
+	t.Run("arrow_disabled_scan_int64", func(t *testing.T) {
+		for _, tc := range intTestcases {
+			dbt.mustExec(forceJSON)
+			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v int64
+			if err := rows.Scan(&v); err == nil {
+				dbt.Error("should fail to scan")
+			}
+		}
+	})
+	t.Run("arrow_disabled_scan_string", func(t *testing.T) {
+		for _, tc := range intTestcases {
+			dbt.mustExec(forceJSON)
+			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v int64
+			if err := rows.Scan(&v); err == nil {
+				dbt.Error("should fail to scan")
+			}
+		}
+	})
+	t.Run("arrow_enabled_scan_big_int", func(t *testing.T) {
+		for _, tc := range intTestcases {
+			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v string
+			if err := rows.Scan(&v); err != nil {
+				dbt.Errorf("failed to scan. %#v", err)
+			}
+			if !strings.EqualFold(v, tc.num) {
+				dbt.Errorf("int value mismatch: expected %v, got %v", tc.num, v)
+			}
+		}
+	})
+	t.Run("arrow_high_precision_enabled_scan_big_int", func(t *testing.T) {
+		for _, tc := range intTestcases {
+			rows := dbt.mustQueryContext(
+				WithHigherPrecision(context.Background()),
+				fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v *big.Int
+			if err := rows.Scan(&v); err != nil {
+				dbt.Errorf("failed to scan. %#v", err)
+			}
+
+			b, ok := new(big.Int).SetString(tc.num, 10)
+			if !ok {
+				dbt.Errorf("failed to convert %v big.Int.", tc.num)
+			}
+			if v.Cmp(b) != 0 {
+				dbt.Errorf("big.Int value mismatch: expected %v, got %v", b, v)
+			}
+		}
+	})
+}
+
+// TestArrowFloatPrecision tests the different variable types allowed in the
+// rows.Scan() method. Note that for lower precision types we do not attempt
+// to check the value as precision could be lost.
+func TestArrowFloatPrecision(t *testing.T) {
+	db := openDB(t)
+	dbt := &DBTest{t, db}
+
+	fltTestcases := []struct {
+		num  string
+		prec int
+		sc   int
+	}{
+		{"1.23", 30, 2},
+		{"1.0000000000000000000000000000000000000", 38, 37},
+		{"-1.0000000000000000000000000000000000000", 38, 37},
+		{"1.2345678901234567890123456789012345678", 38, 37},
+		{"-1.2345678901234567890123456789012345678", 38, 37},
+		{"9.9999999999999999999999999999999999999", 38, 37},
+		{"-9.9999999999999999999999999999999999999", 38, 37},
+	}
+
+	t.Run("arrow_disabled_scan_float64", func(t *testing.T) {
+		for _, tc := range fltTestcases {
+			dbt.mustExec(forceJSON)
+			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v float64
+			if err := rows.Scan(&v); err != nil {
+				dbt.Errorf("failed to scan. %#v", err)
+			}
+		}
+	})
+	t.Run("arrow_disabled_scan_float32", func(t *testing.T) {
+		for _, tc := range fltTestcases {
+			dbt.mustExec(forceJSON)
+			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v float32
+			if err := rows.Scan(&v); err != nil {
+				dbt.Errorf("failed to scan. %#v", err)
+			}
+		}
+	})
+	t.Run("arrow_disabled_scan_string", func(t *testing.T) {
+		for _, tc := range fltTestcases {
+			dbt.mustExec(forceJSON)
+			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v string
+			if err := rows.Scan(&v); err != nil {
+				dbt.Errorf("failed to scan. %#v", err)
+			}
+			if !strings.EqualFold(v, tc.num) {
+				dbt.Errorf("int value mismatch: expected %v, got %v", tc.num, v)
+			}
+		}
+	})
+	t.Run("arrow_enabled_scan_float64", func(t *testing.T) {
+		for _, tc := range fltTestcases {
+			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v float64
+			if err := rows.Scan(&v); err != nil {
+				dbt.Errorf("failed to scan. %#v", err)
+			}
+		}
+	})
+	t.Run("arrow_enabled_scan_float32", func(t *testing.T) {
+		for _, tc := range fltTestcases {
+			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v float32
+			if err := rows.Scan(&v); err != nil {
+				dbt.Errorf("failed to scan. %#v", err)
+			}
+		}
+	})
+	t.Run("arrow_enabled_scan_string", func(t *testing.T) {
+		for _, tc := range fltTestcases {
+			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v string
+			if err := rows.Scan(&v); err != nil {
+				dbt.Errorf("failed to scan. %#v", err)
+			}
+		}
+	})
+	t.Run("arrow_high_precision_enabled_scan_big_float", func(t *testing.T) {
+		for _, tc := range fltTestcases {
+			rows := dbt.mustQueryContext(
+				WithHigherPrecision(context.Background()),
+				fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
+			if !rows.Next() {
+				dbt.Error("failed to query")
+			}
+			defer rows.Close()
+			var v *big.Float
+			if err := rows.Scan(&v); err != nil {
+				dbt.Errorf("failed to scan. %#v", err)
+			}
+
+			prec := v.Prec()
+			b, ok := new(big.Float).SetPrec(prec).SetString(tc.num)
+			if !ok {
+				dbt.Errorf("failed to convert %v to big.Float.", tc.num)
+			}
+			if v.Cmp(b) != 0 {
+				dbt.Errorf("big.Float value mismatch: expected %v, got %v", b, v)
+			}
+		}
+	})
+}
+
+func TestArrowVariousTypes(t *testing.T) {
+	runTests(t, dsn, func(dbt *DBTest) {
+		rows := dbt.mustQueryContext(
+			WithHigherPrecision(context.Background()), selectVariousTypes)
+		defer rows.Close()
+		if !rows.Next() {
+			dbt.Error("failed to query")
+		}
+		cc, err := rows.Columns()
+		if err != nil {
+			dbt.Errorf("columns: %v", cc)
+		}
+		ct, err := rows.ColumnTypes()
+		if err != nil {
+			dbt.Errorf("column types: %v", ct)
+		}
+		var v1 *big.Float
+		var v2 int
+		var v3 string
+		var v4 float64
+		var v5 []byte
+		var v6 bool
+		if err = rows.Scan(&v1, &v2, &v3, &v4, &v5, &v6); err != nil {
+			dbt.Errorf("failed to scan: %#v", err)
+		}
+		if v1.Cmp(big.NewFloat(1.0)) != 0 {
+			dbt.Errorf("failed to scan. %#v", *v1)
+		}
+		if ct[0].Name() != "C1" || ct[1].Name() != "C2" || ct[2].Name() != "C3" || ct[3].Name() != "C4" || ct[4].Name() != "C5" || ct[5].Name() != "C6" {
+			dbt.Errorf("failed to get column names: %#v", ct)
+		}
+		if ct[0].ScanType() != reflect.TypeOf(float64(0)) {
+			dbt.Errorf("failed to get scan type. expected: %v, got: %v", reflect.TypeOf(float64(0)), ct[0].ScanType())
+		}
+		if ct[1].ScanType() != reflect.TypeOf(int64(0)) {
+			dbt.Errorf("failed to get scan type. expected: %v, got: %v", reflect.TypeOf(int64(0)), ct[1].ScanType())
+		}
+		var pr, sc int64
+		var cLen int64
+		pr, sc = dbt.mustDecimalSize(ct[0])
+		if pr != 30 || sc != 2 {
+			dbt.Errorf("failed to get precision and scale. %#v", ct[0])
+		}
+		dbt.mustFailLength(ct[0])
+		if canNull := dbt.mustNullable(ct[0]); canNull {
+			dbt.Errorf("failed to get nullable. %#v", ct[0])
+		}
+		if cLen != 0 {
+			dbt.Errorf("failed to get length. %#v", ct[0])
+		}
+		if v2 != 2 {
+			dbt.Errorf("failed to scan. %#v", v2)
+		}
+		pr, sc = dbt.mustDecimalSize(ct[1])
+		if pr != 38 || sc != 0 {
+			dbt.Errorf("failed to get precision and scale. %#v", ct[1])
+		}
+		dbt.mustFailLength(ct[1])
+		if canNull := dbt.mustNullable(ct[1]); canNull {
+			dbt.Errorf("failed to get nullable. %#v", ct[1])
+		}
+		if v3 != "t3" {
+			dbt.Errorf("failed to scan. %#v", v3)
+		}
+		dbt.mustFailDecimalSize(ct[2])
+		if cLen = dbt.mustLength(ct[2]); cLen != 2 {
+			dbt.Errorf("failed to get length. %#v", ct[2])
+		}
+		if canNull := dbt.mustNullable(ct[2]); canNull {
+			dbt.Errorf("failed to get nullable. %#v", ct[2])
+		}
+		if v4 != 4.2 {
+			dbt.Errorf("failed to scan. %#v", v4)
+		}
+		dbt.mustFailDecimalSize(ct[3])
+		dbt.mustFailLength(ct[3])
+		if canNull := dbt.mustNullable(ct[3]); canNull {
+			dbt.Errorf("failed to get nullable. %#v", ct[3])
+		}
+		if !bytes.Equal(v5, []byte{0xab, 0xcd}) {
+			dbt.Errorf("failed to scan. %#v", v5)
+		}
+		dbt.mustFailDecimalSize(ct[4])
+		if cLen = dbt.mustLength(ct[4]); cLen != 8388608 { // BINARY
+			dbt.Errorf("failed to get length. %#v", ct[4])
+		}
+		if canNull := dbt.mustNullable(ct[4]); canNull {
+			dbt.Errorf("failed to get nullable. %#v", ct[4])
+		}
+		if !v6 {
+			dbt.Errorf("failed to scan. %#v", v6)
+		}
+		dbt.mustFailDecimalSize(ct[5])
+		dbt.mustFailLength(ct[5])
+		/*canNull = dbt.mustNullable(ct[5])
+		if canNull {
+			dbt.Errorf("failed to get nullable. %#v", ct[5])
+		}*/
+	})
+}
diff --git a/benchmark/largesetresult/largesetresult_test.go b/benchmark/largesetresult/largesetresult_test.go
index 493a7b4..3569b4b 100644
--- a/benchmark/largesetresult/largesetresult_test.go
+++ b/benchmark/largesetresult/largesetresult_test.go
@@ -110,17 +110,10 @@ func runLargeResultSet() {
 		log.Fatalf("failed to run a query. %v, err: %v", query, err)
 	}
 	defer rows.Close()
-	var v1 int
-	var v2 int
-	var v3 int
-	var v4 int
-	var v5 int
-	var v6 int
-	var v7 int
+	var v1, v2, v3, v4, v5, v6, v7 int
 	counter := 0
 	for rows.Next() {
-		err := rows.Scan(&v1, &v2, &v3, &v4, &v5, &v6, &v7)
-		if err != nil {
+		if err = rows.Scan(&v1, &v2, &v3, &v4, &v5, &v6, &v7); err != nil {
 			log.Fatalf("failed to get result. err: %v", err)
 		}
 		if counter%1000000 == 0 {
diff --git a/connection_test.go b/connection_test.go
index fa75776..010a004 100644
--- a/connection_test.go
+++ b/connection_test.go
@@ -25,6 +25,25 @@ const (
 	serviceNameAppend = "a"
 )
 
+func TestInvalidConnection(t *testing.T) {
+	db := openDB(t)
+	if err := db.Close(); err != nil {
+		t.Error("should not cause error in Close")
+	}
+	if err := db.Close(); err != nil {
+		t.Error("should not cause error in the second call of Close")
+	}
+	if _, err := db.Exec("CREATE TABLE OR REPLACE test0(c1 int)"); err == nil {
+		t.Error("should fail to run Exec")
+	}
+	if _, err := db.Query("SELECT CURRENT_TIMESTAMP()"); err == nil {
+		t.Error("should fail to run Query")
+	}
+	if _, err := db.Begin(); err == nil {
+		t.Error("should fail to run Begin")
+	}
+}
+
 // postQueryMock generates a response based on the X-Snowflake-Service header,
 // to generate a response with the SERVICE_NAME field appending a character at
 // the end of the header. This way it could test both the send and receive logic
@@ -276,7 +295,7 @@ func returnQueryIsErrStatus(ctx context.Context, rest *snowflakeRestful, fullURL
 // of that query.
 func fetchResultByQueryID(
 	t *testing.T,
-	customGet FuncGetType,
+	customGet funcGetType,
 	expectedFetchErr *SnowflakeError) error {
 	config, err := ParseDSN(dsn)
 	if err != nil {
diff --git a/driver_test.go b/driver_test.go
index 72051cf..28414a0 100644
--- a/driver_test.go
+++ b/driver_test.go
@@ -3,18 +3,15 @@
 package gosnowflake
 
 import (
-	"bytes"
 	"context"
 	"crypto/rsa"
 	"database/sql"
 	"flag"
 	"fmt"
-	"math/big"
 	"net/http"
 	"net/url"
 	"os"
 	"os/signal"
-	"reflect"
 	"strings"
 	"syscall"
 	"testing"
@@ -171,41 +168,6 @@ type DBTest struct {
 	db *sql.DB
 }
 
-type RowsExtended struct {
-	rows      *sql.Rows
-	closeChan *chan bool
-}
-
-func (rs *RowsExtended) Close() error {
-	*rs.closeChan <- true
-	close(*rs.closeChan)
-	return rs.rows.Close()
-}
-
-func (rs *RowsExtended) ColumnTypes() ([]*sql.ColumnType, error) {
-	return rs.rows.ColumnTypes()
-}
-
-func (rs *RowsExtended) Columns() ([]string, error) {
-	return rs.rows.Columns()
-}
-
-func (rs *RowsExtended) Err() error {
-	return rs.rows.Err()
-}
-
-func (rs *RowsExtended) Next() bool {
-	return rs.rows.Next()
-}
-
-func (rs *RowsExtended) NextResultSet() bool {
-	return rs.rows.NextResultSet()
-}
-
-func (rs *RowsExtended) Scan(dest ...interface{}) error {
-	return rs.rows.Scan(dest...)
-}
-
 func (dbt *DBTest) mustQuery(query string, args ...interface{}) (rows *RowsExtended) {
 	// handler interrupt signal
 	ctx, cancel := context.WithCancel(context.Background())
@@ -620,45 +582,6 @@ func testInt(t *testing.T, json bool) {
 	})
 }
 
-func TestArrowBigInt(t *testing.T) {
-	db := openDB(t)
-	dbt := &DBTest{t, db}
-
-	testcases := []struct {
-		num  string
-		prec int
-		sc   int
-	}{
-		{"10000000000000000000000000000000000000", 38, 0},
-		{"-10000000000000000000000000000000000000", 38, 0},
-		{"12345678901234567890123456789012345678", 38, 0},
-		{"-12345678901234567890123456789012345678", 38, 0},
-		{"99999999999999999999999999999999999999", 38, 0},
-		{"-99999999999999999999999999999999999999", 38, 0},
-	}
-
-	for _, tc := range testcases {
-		rows := dbt.mustQueryContext(WithHigherPrecision(context.Background()),
-			fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-		if !rows.Next() {
-			dbt.Error("failed to query")
-		}
-		defer rows.Close()
-		var v *big.Int
-		if err := rows.Scan(&v); err != nil {
-			dbt.Errorf("failed to scan. %#v", err)
-		}
-
-		b, ok := new(big.Int).SetString(tc.num, 10)
-		if !ok {
-			dbt.Errorf("failed to convert %v big.Int.", tc.num)
-		}
-		if v.Cmp(b) != 0 {
-			dbt.Errorf("big.Int value mismatch: expected %v, got %v", b, v)
-		}
-	}
-}
-
 func TestFloat32(t *testing.T) {
 	testFloat32(t, false)
 }
@@ -724,365 +647,6 @@ func testFloat64(t *testing.T, json bool) {
 	})
 }
 
-func TestArrowBigFloat(t *testing.T) {
-	db := openDB(t)
-	dbt := &DBTest{t, db}
-
-	testcases := []struct {
-		num  string
-		prec int
-		sc   int
-	}{
-		{"1.23", 30, 2},
-		{"1.0000000000000000000000000000000000000", 38, 37},
-		{"-1.0000000000000000000000000000000000000", 38, 37},
-		{"1.2345678901234567890123456789012345678", 38, 37},
-		{"-1.2345678901234567890123456789012345678", 38, 37},
-		{"9.9999999999999999999999999999999999999", 38, 37},
-		{"-9.9999999999999999999999999999999999999", 38, 37},
-	}
-
-	for _, tc := range testcases {
-		rows := dbt.mustQueryContext(WithHigherPrecision(context.Background()),
-			fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-		if !rows.Next() {
-			dbt.Error("failed to query")
-		}
-		defer rows.Close()
-		var v *big.Float
-		if err := rows.Scan(&v); err != nil {
-			dbt.Errorf("failed to scan. %#v", err)
-		}
-
-		prec := v.Prec()
-		b, ok := new(big.Float).SetPrec(prec).SetString(tc.num)
-		if !ok {
-			dbt.Errorf("failed to convert %v to big.Float.", tc.num)
-		}
-		if v.Cmp(b) != 0 {
-			dbt.Errorf("big.Float value mismatch: expected %v, got %v", b, v)
-		}
-	}
-}
-
-func TestArrowIntPrecision(t *testing.T) {
-	db := openDB(t)
-	dbt := &DBTest{t, db}
-
-	intTestcases := []struct {
-		num  string
-		prec int
-		sc   int
-	}{
-		{"10000000000000000000000000000000000000", 38, 0},
-		{"-10000000000000000000000000000000000000", 38, 0},
-		{"12345678901234567890123456789012345678", 38, 0},
-		{"-12345678901234567890123456789012345678", 38, 0},
-		{"99999999999999999999999999999999999999", 38, 0},
-		{"-99999999999999999999999999999999999999", 38, 0},
-	}
-
-	t.Run("arrow_disabled_scan_int64", func(t *testing.T) {
-		for _, tc := range intTestcases {
-			dbt.mustExec(forceJSON)
-			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v int64
-			if err := rows.Scan(&v); err == nil {
-				dbt.Error("should fail to scan")
-			}
-		}
-	})
-	t.Run("arrow_disabled_scan_string", func(t *testing.T) {
-		for _, tc := range intTestcases {
-			dbt.mustExec(forceJSON)
-			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v int64
-			if err := rows.Scan(&v); err == nil {
-				dbt.Error("should fail to scan")
-			}
-		}
-	})
-	t.Run("arrow_enabled_scan_big_int", func(t *testing.T) {
-		for _, tc := range intTestcases {
-			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v string
-			if err := rows.Scan(&v); err != nil {
-				dbt.Errorf("failed to scan. %#v", err)
-			}
-			if !strings.EqualFold(v, tc.num) {
-				dbt.Errorf("int value mismatch: expected %v, got %v", tc.num, v)
-			}
-		}
-	})
-	t.Run("arrow_high_precision_enabled_scan_big_int", func(t *testing.T) {
-		for _, tc := range intTestcases {
-			rows := dbt.mustQueryContext(
-				WithHigherPrecision(context.Background()),
-				fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v *big.Int
-			if err := rows.Scan(&v); err != nil {
-				dbt.Errorf("failed to scan. %#v", err)
-			}
-
-			b, ok := new(big.Int).SetString(tc.num, 10)
-			if !ok {
-				dbt.Errorf("failed to convert %v big.Int.", tc.num)
-			}
-			if v.Cmp(b) != 0 {
-				dbt.Errorf("big.Int value mismatch: expected %v, got %v", b, v)
-			}
-		}
-	})
-}
-
-// TestArrowFloatPrecision tests the different variable types allowed in the
-// rows.Scan() method. Note that for lower precision types we do not attempt
-// to check the value as precision could be lost.
-func TestArrowFloatPrecision(t *testing.T) {
-	db := openDB(t)
-	dbt := &DBTest{t, db}
-
-	fltTestcases := []struct {
-		num  string
-		prec int
-		sc   int
-	}{
-		{"1.23", 30, 2},
-		{"1.0000000000000000000000000000000000000", 38, 37},
-		{"-1.0000000000000000000000000000000000000", 38, 37},
-		{"1.2345678901234567890123456789012345678", 38, 37},
-		{"-1.2345678901234567890123456789012345678", 38, 37},
-		{"9.9999999999999999999999999999999999999", 38, 37},
-		{"-9.9999999999999999999999999999999999999", 38, 37},
-	}
-
-	t.Run("arrow_disabled_scan_float64", func(t *testing.T) {
-		for _, tc := range fltTestcases {
-			dbt.mustExec(forceJSON)
-			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v float64
-			if err := rows.Scan(&v); err != nil {
-				dbt.Errorf("failed to scan. %#v", err)
-			}
-		}
-	})
-	t.Run("arrow_disabled_scan_float32", func(t *testing.T) {
-		for _, tc := range fltTestcases {
-			dbt.mustExec(forceJSON)
-			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v float32
-			if err := rows.Scan(&v); err != nil {
-				dbt.Errorf("failed to scan. %#v", err)
-			}
-		}
-	})
-	t.Run("arrow_disabled_scan_string", func(t *testing.T) {
-		for _, tc := range fltTestcases {
-			dbt.mustExec(forceJSON)
-			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v string
-			if err := rows.Scan(&v); err != nil {
-				dbt.Errorf("failed to scan. %#v", err)
-			}
-			if !strings.EqualFold(v, tc.num) {
-				dbt.Errorf("int value mismatch: expected %v, got %v", tc.num, v)
-			}
-		}
-	})
-	t.Run("arrow_enabled_scan_float64", func(t *testing.T) {
-		for _, tc := range fltTestcases {
-			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v float64
-			if err := rows.Scan(&v); err != nil {
-				dbt.Errorf("failed to scan. %#v", err)
-			}
-		}
-	})
-	t.Run("arrow_enabled_scan_float32", func(t *testing.T) {
-		for _, tc := range fltTestcases {
-			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v float32
-			if err := rows.Scan(&v); err != nil {
-				dbt.Errorf("failed to scan. %#v", err)
-			}
-		}
-	})
-	t.Run("arrow_enabled_scan_string", func(t *testing.T) {
-		for _, tc := range fltTestcases {
-			rows := dbt.mustQuery(fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v string
-			if err := rows.Scan(&v); err != nil {
-				dbt.Errorf("failed to scan. %#v", err)
-			}
-		}
-	})
-	t.Run("arrow_high_precision_enabled_scan_big_float", func(t *testing.T) {
-		for _, tc := range fltTestcases {
-			rows := dbt.mustQueryContext(
-				WithHigherPrecision(context.Background()),
-				fmt.Sprintf(selectNumberSQL, tc.num, tc.prec, tc.sc))
-			if !rows.Next() {
-				dbt.Error("failed to query")
-			}
-			defer rows.Close()
-			var v *big.Float
-			if err := rows.Scan(&v); err != nil {
-				dbt.Errorf("failed to scan. %#v", err)
-			}
-
-			prec := v.Prec()
-			b, ok := new(big.Float).SetPrec(prec).SetString(tc.num)
-			if !ok {
-				dbt.Errorf("failed to convert %v to big.Float.", tc.num)
-			}
-			if v.Cmp(b) != 0 {
-				dbt.Errorf("big.Float value mismatch: expected %v, got %v", b, v)
-			}
-		}
-	})
-}
-
-func TestArrowVariousTypes(t *testing.T) {
-	runTests(t, dsn, func(dbt *DBTest) {
-		rows := dbt.mustQueryContext(
-			WithHigherPrecision(context.Background()), selectVariousTypes)
-		defer rows.Close()
-		if !rows.Next() {
-			dbt.Error("failed to query")
-		}
-		cc, err := rows.Columns()
-		if err != nil {
-			dbt.Errorf("columns: %v", cc)
-		}
-		ct, err := rows.ColumnTypes()
-		if err != nil {
-			dbt.Errorf("column types: %v", ct)
-		}
-		var v1 *big.Float
-		var v2 int
-		var v3 string
-		var v4 float64
-		var v5 []byte
-		var v6 bool
-		if err = rows.Scan(&v1, &v2, &v3, &v4, &v5, &v6); err != nil {
-			dbt.Errorf("failed to scan: %#v", err)
-		}
-		if v1.Cmp(big.NewFloat(1.0)) != 0 {
-			dbt.Errorf("failed to scan. %#v", *v1)
-		}
-		if ct[0].Name() != "C1" || ct[1].Name() != "C2" || ct[2].Name() != "C3" || ct[3].Name() != "C4" || ct[4].Name() != "C5" || ct[5].Name() != "C6" {
-			dbt.Errorf("failed to get column names: %#v", ct)
-		}
-		if ct[0].ScanType() != reflect.TypeOf(float64(0)) {
-			dbt.Errorf("failed to get scan type. expected: %v, got: %v", reflect.TypeOf(float64(0)), ct[0].ScanType())
-		}
-		if ct[1].ScanType() != reflect.TypeOf(int64(0)) {
-			dbt.Errorf("failed to get scan type. expected: %v, got: %v", reflect.TypeOf(int64(0)), ct[1].ScanType())
-		}
-		var pr, sc int64
-		var cLen int64
-		pr, sc = dbt.mustDecimalSize(ct[0])
-		if pr != 30 || sc != 2 {
-			dbt.Errorf("failed to get precision and scale. %#v", ct[0])
-		}
-		dbt.mustFailLength(ct[0])
-		if canNull := dbt.mustNullable(ct[0]); canNull {
-			dbt.Errorf("failed to get nullable. %#v", ct[0])
-		}
-		if cLen != 0 {
-			dbt.Errorf("failed to get length. %#v", ct[0])
-		}
-		if v2 != 2 {
-			dbt.Errorf("failed to scan. %#v", v2)
-		}
-		pr, sc = dbt.mustDecimalSize(ct[1])
-		if pr != 38 || sc != 0 {
-			dbt.Errorf("failed to get precision and scale. %#v", ct[1])
-		}
-		dbt.mustFailLength(ct[1])
-		if canNull := dbt.mustNullable(ct[1]); canNull {
-			dbt.Errorf("failed to get nullable. %#v", ct[1])
-		}
-		if v3 != "t3" {
-			dbt.Errorf("failed to scan. %#v", v3)
-		}
-		dbt.mustFailDecimalSize(ct[2])
-		if cLen = dbt.mustLength(ct[2]); cLen != 2 {
-			dbt.Errorf("failed to get length. %#v", ct[2])
-		}
-		if canNull := dbt.mustNullable(ct[2]); canNull {
-			dbt.Errorf("failed to get nullable. %#v", ct[2])
-		}
-		if v4 != 4.2 {
-			dbt.Errorf("failed to scan. %#v", v4)
-		}
-		dbt.mustFailDecimalSize(ct[3])
-		dbt.mustFailLength(ct[3])
-		if canNull := dbt.mustNullable(ct[3]); canNull {
-			dbt.Errorf("failed to get nullable. %#v", ct[3])
-		}
-		if !bytes.Equal(v5, []byte{0xab, 0xcd}) {
-			dbt.Errorf("failed to scan. %#v", v5)
-		}
-		dbt.mustFailDecimalSize(ct[4])
-		if cLen = dbt.mustLength(ct[4]); cLen != 8388608 { // BINARY
-			dbt.Errorf("failed to get length. %#v", ct[4])
-		}
-		if canNull := dbt.mustNullable(ct[4]); canNull {
-			dbt.Errorf("failed to get nullable. %#v", ct[4])
-		}
-		if !v6 {
-			dbt.Errorf("failed to scan. %#v", v6)
-		}
-		dbt.mustFailDecimalSize(ct[5])
-		dbt.mustFailLength(ct[5])
-		/*canNull = dbt.mustNullable(ct[5])
-		if canNull {
-			dbt.Errorf("failed to get nullable. %#v", ct[5])
-		}*/
-	})
-}
-
 func TestString(t *testing.T) {
 	testString(t, false)
 }
@@ -1763,42 +1327,6 @@ func queryTest(dbt *DBTest) (*map[int]string, error) {
 	return &results, nil
 }
 
-// Special cases where rows are already closed
-func TestRowsClose(t *testing.T) {
-	runTests(t, dsn, func(dbt *DBTest) {
-		rows, err := dbt.db.Query("SELECT 1")
-		if err != nil {
-			dbt.Fatal(err)
-		}
-		if err = rows.Close(); err != nil {
-			dbt.Fatal(err)
-		}
-
-		if rows.Next() {
-			dbt.Fatal("unexpected row after rows.Close()")
-		}
-		if err = rows.Err(); err != nil {
-			dbt.Fatal(err)
-		}
-	})
-}
-
-func TestResultNoRows(t *testing.T) {
-	// DDL
-	runTests(t, dsn, func(dbt *DBTest) {
-		row, err := dbt.db.Exec("CREATE OR REPLACE TABLE test(c1 int)")
-		if err != nil {
-			t.Fatalf("failed to execute DDL. err: %v", err)
-		}
-		if _, err = row.RowsAffected(); err == nil {
-			t.Fatal("should have failed to get RowsAffected")
-		}
-		if _, err = row.LastInsertId(); err == nil {
-			t.Fatal("should have failed to get LastInsertID")
-		}
-	})
-}
-
 func TestCancelQuery(t *testing.T) {
 	runTests(t, dsn, func(dbt *DBTest) {
 		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
@@ -1814,25 +1342,6 @@ func TestCancelQuery(t *testing.T) {
 	})
 }
 
-func TestInvalidConnection(t *testing.T) {
-	db := openDB(t)
-	if err := db.Close(); err != nil {
-		t.Error("should not cause error in Close")
-	}
-	if err := db.Close(); err != nil {
-		t.Error("should not cause error in the second call of Close")
-	}
-	if _, err := db.Exec("CREATE TABLE OR REPLACE test0(c1 int)"); err == nil {
-		t.Error("should fail to run Exec")
-	}
-	if _, err := db.Query("SELECT CURRENT_TIMESTAMP()"); err == nil {
-		t.Error("should fail to run Query")
-	}
-	if _, err := db.Begin(); err == nil {
-		t.Error("should fail to run Begin")
-	}
-}
-
 func TestPing(t *testing.T) {
 	db := openDB(t)
 	if err := db.Ping(); err != nil {
@@ -1874,34 +1383,6 @@ $$
 	})
 }
 
-func TestTransactionOptions(t *testing.T) {
-	var tx *sql.Tx
-	var err error
-
-	db := openDB(t)
-	defer db.Close()
-
-	tx, err = db.BeginTx(context.Background(), &sql.TxOptions{})
-	if err != nil {
-		t.Fatal("failed to start transaction.")
-	}
-	if err = tx.Rollback(); err != nil {
-		t.Fatal("failed to rollback")
-	}
-	if _, err = db.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true}); err == nil {
-		t.Fatal("should have failed.")
-	}
-	if driverErr, ok := err.(*SnowflakeError); !ok || driverErr.Number != ErrNoReadOnlyTransaction {
-		t.Fatalf("should have returned Snowflake Error: %v", errMsgNoReadOnlyTransaction)
-	}
-	if _, err = db.BeginTx(context.Background(), &sql.TxOptions{Isolation: 100}); err == nil {
-		t.Fatal("should have failed.")
-	}
-	if driverErr, ok := err.(*SnowflakeError); !ok || driverErr.Number != ErrNoDefaultTransactionIsolationLevel {
-		t.Fatalf("should have returned Snowflake Error: %v", errMsgNoDefaultTransactionIsolationLevel)
-	}
-}
-
 func TestTimezoneSessionParameter(t *testing.T) {
 	createDSN(PSTLocation)
 	db := openDB(t)
diff --git a/rows_test.go b/rows_test.go
index 0eb0eab..49633c3 100644
--- a/rows_test.go
+++ b/rows_test.go
@@ -4,6 +4,7 @@ package gosnowflake
 
 import (
 	"context"
+	"database/sql"
 	"database/sql/driver"
 	"fmt"
 	"io"
@@ -13,11 +14,82 @@ import (
 	"time"
 )
 
+type RowsExtended struct {
+	rows      *sql.Rows
+	closeChan *chan bool
+}
+
+func (rs *RowsExtended) Close() error {
+	*rs.closeChan <- true
+	close(*rs.closeChan)
+	return rs.rows.Close()
+}
+
+func (rs *RowsExtended) ColumnTypes() ([]*sql.ColumnType, error) {
+	return rs.rows.ColumnTypes()
+}
+
+func (rs *RowsExtended) Columns() ([]string, error) {
+	return rs.rows.Columns()
+}
+
+func (rs *RowsExtended) Err() error {
+	return rs.rows.Err()
+}
+
+func (rs *RowsExtended) Next() bool {
+	return rs.rows.Next()
+}
+
+func (rs *RowsExtended) NextResultSet() bool {
+	return rs.rows.NextResultSet()
+}
+
+func (rs *RowsExtended) Scan(dest ...interface{}) error {
+	return rs.rows.Scan(dest...)
+}
+
 // test variables
 var (
 	rowsInChunk = 123
 )
 
+// Special cases where rows are already closed
+func TestRowsClose(t *testing.T) {
+	runTests(t, dsn, func(dbt *DBTest) {
+		rows, err := dbt.db.Query("SELECT 1")
+		if err != nil {
+			dbt.Fatal(err)
+		}
+		if err = rows.Close(); err != nil {
+			dbt.Fatal(err)
+		}
+
+		if rows.Next() {
+			dbt.Fatal("unexpected row after rows.Close()")
+		}
+		if err = rows.Err(); err != nil {
+			dbt.Fatal(err)
+		}
+	})
+}
+
+func TestResultNoRows(t *testing.T) {
+	// DDL
+	runTests(t, dsn, func(dbt *DBTest) {
+		row, err := dbt.db.Exec("CREATE OR REPLACE TABLE test(c1 int)")
+		if err != nil {
+			t.Fatalf("failed to execute DDL. err: %v", err)
+		}
+		if _, err = row.RowsAffected(); err == nil {
+			t.Fatal("should have failed to get RowsAffected")
+		}
+		if _, err = row.LastInsertId(); err == nil {
+			t.Fatal("should have failed to get LastInsertID")
+		}
+	})
+}
+
 func TestRowsWithoutChunkDownloader(t *testing.T) {
 	sts1 := "1"
 	sts2 := "Test1"
diff --git a/s3_util_test.go b/s3_storage_client_test.go
similarity index 100%
rename from s3_util_test.go
rename to s3_storage_client_test.go
diff --git a/transaction_test.go b/transaction_test.go
new file mode 100644
index 0000000..fc403cd
--- /dev/null
+++ b/transaction_test.go
@@ -0,0 +1,37 @@
+// Copyright (c) 2017-2022 Snowflake Computing Inc. All rights reserved.
+
+package gosnowflake
+
+import (
+	"context"
+	"database/sql"
+	"testing"
+)
+
+func TestTransactionOptions(t *testing.T) {
+	var tx *sql.Tx
+	var err error
+
+	db := openDB(t)
+	defer db.Close()
+
+	tx, err = db.BeginTx(context.Background(), &sql.TxOptions{})
+	if err != nil {
+		t.Fatal("failed to start transaction.")
+	}
+	if err = tx.Rollback(); err != nil {
+		t.Fatal("failed to rollback")
+	}
+	if _, err = db.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true}); err == nil {
+		t.Fatal("should have failed.")
+	}
+	if driverErr, ok := err.(*SnowflakeError); !ok || driverErr.Number != ErrNoReadOnlyTransaction {
+		t.Fatalf("should have returned Snowflake Error: %v", errMsgNoReadOnlyTransaction)
+	}
+	if _, err = db.BeginTx(context.Background(), &sql.TxOptions{Isolation: 100}); err == nil {
+		t.Fatal("should have failed.")
+	}
+	if driverErr, ok := err.(*SnowflakeError); !ok || driverErr.Number != ErrNoDefaultTransactionIsolationLevel {
+		t.Fatalf("should have returned Snowflake Error: %v", errMsgNoDefaultTransactionIsolationLevel)
+	}
+}