3 Star 0 Fork 0

GBase8s/go-gci

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
gci_sql_test.go 39.20 KB
一键复制 编辑 原始数据 按行查看 历史
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712
package gbase8s
import (
"context"
"database/sql"
"fmt"
"log"
"os"
"reflect"
"strconv"
"strings"
"sync"
"testing"
"time"
)
// testGetDB connects to the test database and returns the database connection
func testGetDB(params string) *sql.DB {
Driver.Logger = log.New(os.Stderr, "gbase8s ", log.Ldate|log.Ltime|log.LUTC|log.Lshortfile)
var openString string
// [username/[password]@]host[:port][/service_name][?param1=value1&...&paramN=valueN]
if len(TestUsername) > 0 {
if len(TestPassword) > 0 {
openString = TestUsername + "/" + TestPassword + "@"
} else {
openString = TestUsername + "@"
}
}
openString += TestHostValid + params
//openString = "gbase8s://root:rootroot@192.168.43.170:16761/d1?GBASEDBTSERVER=ol_gbasedbt1210_1&PROTOCOL=onsoctcp&sqlmode=oracle&delimident=1&gbasedbtdir=%2Fhome%2Fgtest%2FGBaseGCI_3.5.0_8_20240909_13044d_RHEL6_x86_64%2Frelyon%2F&GCI_FACTORY=4&GoGCILogTrace=0"
db, err := sql.Open("gbase8s", openString)
if err != nil {
fmt.Println("Open error:", err)
return nil
}
if db == nil {
fmt.Println("db is nil")
return nil
}
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
err = db.PingContext(ctx)
cancel()
if err != nil {
fmt.Println("PingContext error:", err)
db.Close()
return nil
}
return db
}
func testDropTable(t *testing.T, tableName string) {
err := testExec(t, "drop table "+tableName, nil)
if err != nil {
t.Errorf("drop table %v error: %v", tableName, err)
}
}
func testExecQuery(t *testing.T, query string, args []interface{}) {
err := testExec(t, query, args)
if err != nil {
t.Errorf("query %v error: %v", query, err)
}
}
// testGetRows runs a statement and returns the rows as [][]interface{}
func testGetRows(t testing.TB, stmt *sql.Stmt, args []interface{}) ([][]interface{}, error) {
// get rows
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
defer cancel()
var rows *sql.Rows
rows, err := stmt.QueryContext(ctx, args...)
if err != nil {
return nil, fmt.Errorf("query error: %v", err)
}
// get column infomration
var columns []string
columns, err = rows.Columns()
if err != nil {
rows.Close()
return nil, fmt.Errorf("columns error: %v", err)
}
// create values
values := make([][]interface{}, 0, 1)
// get values
pRowInterface := make([]interface{}, len(columns))
for rows.Next() {
rowInterface := make([]interface{}, len(columns))
for i := 0; i < len(rowInterface); i++ {
pRowInterface[i] = &rowInterface[i]
}
err = rows.Err()
if err != nil {
rows.Close()
return nil, fmt.Errorf("rows error: %v", err)
}
err = rows.Scan(pRowInterface...)
if err != nil {
rows.Close()
return nil, fmt.Errorf("scan error: %v", err)
}
values = append(values, rowInterface)
}
err = rows.Err()
if err != nil {
rows.Close()
return nil, fmt.Errorf("rows error: %v", err)
}
err = rows.Close()
if err != nil {
return nil, fmt.Errorf("close error: %v", err)
}
// return values
return values, nil
}
// testExec runs an exec query and returns error
func testExec(t *testing.T, query string, args []interface{}) error {
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
return fmt.Errorf("prepare error: %v", err)
}
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx, args...)
cancel()
if err != nil {
stmt.Close()
return fmt.Errorf("exec error: %v", err)
}
err = stmt.Close()
if err != nil {
return fmt.Errorf("stmt close error: %v", err)
}
return nil
}
// testExecRows runs exec query for each arg row and returns error
func testExecRows(t *testing.T, query string, args [][]interface{}) error {
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
return fmt.Errorf("prepare error: %v", err)
}
for i := 0; i < len(args); i++ {
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx, args[i]...)
cancel()
if err != nil {
stmt.Close()
return fmt.Errorf("exec - row %v - error: %v", i, err)
}
}
err = stmt.Close()
if err != nil {
return fmt.Errorf("stmt close error: %v", err)
}
return nil
}
// testRunExecResults runs testRunExecResult for each execResults
func testRunExecResults(t *testing.T, execResults testExecResults) {
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, execResults.query)
cancel()
if err != nil {
t.Errorf("prepare error: %v - query: %v", err, execResults.query)
return
}
for _, execResult := range execResults.execResults {
testRunExecResult(t, execResult, execResults.query, stmt)
}
err = stmt.Close()
if err != nil {
t.Errorf("close error: %v - query: %v", err, execResults.query)
}
}
// testRunExecResult runs exec query for execResult and tests result
func testRunExecResult(t *testing.T, execResult testExecResult, query string, stmt *sql.Stmt) {
var rv reflect.Value
results := make(map[string]interface{}, len(execResult.args))
// change args to namedArgs
namedArgs := make([]interface{}, 0, len(execResult.args))
for key, value := range execResult.args {
// make pointer
rv = reflect.ValueOf(value.Dest)
out := reflect.New(rv.Type())
if !out.Elem().CanSet() {
t.Fatalf("unable to set pointer: %v - query: %v", key, query)
return
}
out.Elem().Set(rv)
results[key] = out.Interface()
namedArgs = append(namedArgs, sql.Named(key, sql.Out{Dest: out.Interface(), In: value.In}))
}
// exec query with namedArgs
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
_, err := stmt.ExecContext(ctx, namedArgs...)
cancel()
if err != nil {
t.Errorf("exec error: %v - query: %v - args: %v", err, query, execResult.args)
return
}
// check results
for key, value := range execResult.results {
// check if have result
result, ok := results[key]
if !ok {
t.Errorf("result not found: %v - query: %v", key, query)
continue
}
// get result from result pointer
rv = reflect.ValueOf(result)
rv = reflect.Indirect(rv)
result = rv.Interface()
// check if value matches result
if !reflect.DeepEqual(result, value) {
t.Errorf("arg: %v - received: %T, %v - expected: %T, %v - query: %v",
key, result, result, value, value, query)
}
}
}
// testRunQueryResults runs testRunQueryResult for each queryResults
func testRunQueryResults(t *testing.T, queryResults testQueryResults) {
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, queryResults.query)
cancel()
if err != nil {
t.Errorf("prepare error: %v - query: %v", err, queryResults.query)
return
}
for _, queryResult := range queryResults.queryResults {
testRunQueryResult(t, queryResult, queryResults.query, stmt)
}
err = stmt.Close()
if err != nil {
t.Errorf("close error: %v - query: %v", err, queryResults.query)
}
}
// testRunQueryResult runs a single testQueryResults test
func testRunQueryResult(t *testing.T, queryResult testQueryResult, query string, stmt *sql.Stmt) {
result, err := testGetRows(t, stmt, queryResult.args)
if err != nil {
t.Errorf("get rows error: %v - query: %v", err, query)
return
}
if result == nil && queryResult.results != nil {
t.Errorf("result is nil - query: %v", query)
return
}
if len(result) != len(queryResult.results) {
t.Errorf("result rows len %v not equal to results len %v - query: %v",
len(result), len(queryResult.results), query)
return
}
for i := 0; i < len(result); i++ {
if len(result[i]) != len(queryResult.results[i]) {
t.Errorf("result columns len %v not equal to results len %v - query: %v",
len(result[i]), len(queryResult.results[i]), query)
continue
}
for j := 0; j < len(result[i]); j++ {
bad := false
type1 := reflect.TypeOf(result[i][j])
type2 := reflect.TypeOf(queryResult.results[i][j])
switch {
case type1 == nil || type2 == nil:
if type1 != type2 {
bad = true
}
case type1 == TestTypeTime || type2 == TestTypeTime:
if type1 != type2 {
bad = true
break
}
time1 := result[i][j].(time.Time)
time2 := queryResult.results[i][j].(time.Time)
if !time1.Equal(time2) {
bad = true
}
case type1.Kind() == reflect.Slice || type2.Kind() == reflect.Slice:
if !reflect.DeepEqual(result[i][j], queryResult.results[i][j]) {
bad = true
}
default:
if result[i][j] != queryResult.results[i][j] {
bad = true
}
}
if bad {
t.Errorf("result - row %v, %v - received: %T, %v - expected: %T, %v - query: %v",
i, j, result[i][j], result[i][j], queryResult.results[i][j], queryResult.results[i][j], query)
}
}
}
}
// TestConnect checks basic invalid connection
func TestConnect(t *testing.T) {
if TestDisableDatabase {
t.SkipNow()
}
t.Parallel()
// invalid
db, err := sql.Open("gbase8s", TestHostInvalid)
if err != nil {
t.Fatal("open error:", err)
}
if db == nil {
t.Fatal("db is nil")
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
err = db.PingContext(ctx)
cancel()
if err == nil || len(err.Error()) < 4 || err.Error()[0:4] != "GCI-" {
t.Fatalf("ping error - received: %v - expected GCI- error", err)
}
err = db.Close()
if err != nil {
t.Fatal("close error:", err)
}
// wrong username
db, err = sql.Open("gbase8s", "dFQXYoApiU2YbquMQnfPyqxR2kAoeuWngDvtTpl3@"+TestHostValid)
if err != nil {
t.Fatal("open error:", err)
}
if db == nil {
t.Fatal("db is nil")
}
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
err = db.PingContext(ctx)
cancel()
if err == nil || len(err.Error()) < 4 || err.Error()[0:4] != "GCI-" {
t.Fatalf("ping error - received: %v - expected GCI- error", err)
}
err = db.Close()
if err != nil {
t.Fatal("close error:", err)
}
}
// TestSelectParallel checks parallel select from dual
func TestSelectParallel(t *testing.T) {
if TestDisableDatabase {
t.SkipNow()
}
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, "select :1 from dual")
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
var waitGroup sync.WaitGroup
waitGroup.Add(50)
for i := 0; i < 50; i++ {
go func(num int) {
defer waitGroup.Done()
var result [][]interface{}
result, err = testGetRows(t, stmt, []interface{}{num})
if err != nil {
t.Fatal("get rows error:", err)
}
if result == nil {
t.Fatal("result is nil")
}
if len(result) != 1 {
t.Fatal("len result not equal to 1")
}
if len(result[0]) != 1 {
t.Fatal("len result[0] not equal to 1")
}
data, ok := result[0][0].(float64)
if !ok {
t.Fatal("result not float64")
}
if data != float64(num) {
t.Fatal("result not equal to:", num)
}
}(i)
}
waitGroup.Wait()
err = stmt.Close()
if err != nil {
t.Fatal("stmt close error:", err)
}
}
// TestContextTimeoutBreak checks that ExecContext timeout works
func TestContextTimeoutBreak(t *testing.T) {
if TestDisableDatabase {
t.SkipNow()
}
t.Parallel()
// define
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
_, err := TestDB.ExecContext(ctx, `
create or replace function SLEEP_SECONDS (p_seconds number) return integer is
begin
dbms_lock.sleep(p_seconds);
return 1;
end;
`)
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
// exec
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, "begin SYS.DBMS_LOCK.SLEEP(1); end;")
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), 200*time.Millisecond)
_, err = stmt.ExecContext(ctx)
cancel()
expected := "GCI-01013"
if err == nil || len(err.Error()) < len(expected) || !strings.Contains(err.Error(), expected) {
t.Fatalf("stmt exec - expected: %v - received: %v", expected, err)
}
err = stmt.Close()
if err != nil {
t.Fatal("stmt close error:", err)
}
// query
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = TestDB.PrepareContext(ctx, "select SLEEP_SECONDS(1) from dual")
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), 200*time.Millisecond)
_, err = stmt.QueryContext(ctx)
cancel()
if err == nil || len(err.Error()) < len(expected) || !strings.Contains(err.Error(), expected) {
t.Fatalf("stmt query - expected: %v - received: %v", expected, err)
}
err = stmt.Close()
if err != nil {
t.Fatal("stmt close error:", err)
}
}
// TestDestructiveTransaction tests a transaction
func TestDestructiveTransaction(t *testing.T) {
if TestDisableDatabase || TestDisableDestructive {
t.SkipNow()
}
err := testExec(t, "create table TRANSACTION_"+TestTimeString+
" ( A INT, B INT, C INT )", nil)
if err != nil {
t.Fatal("create table error:", err)
}
defer testExecQuery(t, "drop table TRANSACTION_"+TestTimeString, nil)
err = testExecRows(t, "insert into TRANSACTION_"+TestTimeString+" ( A, B, C ) values (:1, :2, :3)",
[][]interface{}{
{1, 2, 3},
{4, 5, 6},
{6, 7, 8},
})
if err != nil {
t.Fatal("insert error:", err)
}
// TODO: How should context work? Probably should have more context create and cancel.
var tx1 *sql.Tx
var tx2 *sql.Tx
ctx, cancel := context.WithTimeout(context.Background(), 2*TestContextTimeout)
defer cancel()
tx1, err = TestDB.BeginTx(ctx, nil)
if err != nil {
t.Fatal("begin tx error:", err)
}
tx2, err = TestDB.BeginTx(ctx, nil)
if err != nil {
t.Fatal("begin tx error:", err)
}
queryResults := testQueryResults{
query: "select A, B, C from TRANSACTION_" + TestTimeString + " order by A",
queryResults: []testQueryResult{
{
results: [][]interface{}{
{int64(1), int64(2), int64(3)},
{int64(4), int64(5), int64(6)},
{int64(6), int64(7), int64(8)},
},
},
},
}
testRunQueryResults(t, queryResults)
var result sql.Result
result, err = tx1.ExecContext(ctx, "update TRANSACTION_"+TestTimeString+" set B = :1 where A = :2", []interface{}{22, 1}...)
if err != nil {
t.Fatal("exec error:", err)
}
var count int64
count, err = result.RowsAffected()
if err != nil {
t.Fatal("rows affected error:", err)
}
if count != 1 {
t.Fatalf("rows affected %v not equal to 1", count)
}
result, err = tx2.ExecContext(ctx, "update TRANSACTION_"+TestTimeString+" set B = :1 where A = :2", []interface{}{55, 4}...)
if err != nil {
t.Fatal("exec error:", err)
}
count, err = result.RowsAffected()
if err != nil {
t.Fatal("rows affected error:", err)
}
if count != 1 {
t.Fatalf("rows affected %v not equal to 1", count)
}
queryResults = testQueryResults{
query: "select A, B, C from TRANSACTION_" + TestTimeString + " order by A",
queryResults: []testQueryResult{
{
results: [][]interface{}{
{int64(1), int64(2), int64(3)},
{int64(4), int64(5), int64(6)},
{int64(6), int64(7), int64(8)},
},
},
},
}
testRunQueryResults(t, queryResults)
// tx1 with rows A = 1
var stmt *sql.Stmt
stmt, err = tx1.PrepareContext(ctx, "select A, B, C from TRANSACTION_"+TestTimeString+" where A = :1")
if err != nil {
t.Fatal("prepare error:", err)
}
var rows [][]interface{}
rows, err = testGetRows(t, stmt, []interface{}{1})
if err != nil {
t.Fatal("get rows error:", err)
}
if result == nil {
t.Fatal("rows is nil")
}
if len(rows) != 1 {
t.Fatal("len rows not equal to 1")
}
if len(rows[0]) != 3 {
t.Fatal("len rows[0] not equal to 3")
}
data, ok := rows[0][0].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected := int64(1)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
data, ok = rows[0][1].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(22)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
data, ok = rows[0][2].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(3)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
// tx1 with rows A = 4
rows, err = testGetRows(t, stmt, []interface{}{4})
if err != nil {
t.Fatal("get rows error:", err)
}
if rows == nil {
t.Fatal("rows is nil")
}
if len(rows) != 1 {
t.Fatal("len rows not equal to 1")
}
if len(rows[0]) != 3 {
t.Fatal("len rows[0] not equal to 3")
}
data, ok = rows[0][0].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(4)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
data, ok = rows[0][1].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(5)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
data, ok = rows[0][2].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(6)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
// tx2 with rows A = 1
stmt, err = tx2.PrepareContext(ctx, "select A, B, C from TRANSACTION_"+TestTimeString+" where A = :1")
if err != nil {
t.Fatal("prepare error:", err)
}
rows, err = testGetRows(t, stmt, []interface{}{1})
if err != nil {
t.Fatal("get rows error:", err)
}
if rows == nil {
t.Fatal("rows is nil")
}
if len(rows) != 1 {
t.Fatal("len rows not equal to 1")
}
if len(rows[0]) != 3 {
t.Fatal("len rows[0] not equal to 3")
}
data, ok = rows[0][0].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(1)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
data, ok = rows[0][1].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(2)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
data, ok = rows[0][2].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(3)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
// tx2 with rows A = 4
rows, err = testGetRows(t, stmt, []interface{}{4})
if err != nil {
t.Fatal("get rows error:", err)
}
if result == nil {
t.Fatal("rows is nil")
}
if len(rows) != 1 {
t.Fatal("len rows not equal to 1")
}
if len(rows[0]) != 3 {
t.Fatal("len rows[0] not equal to 3")
}
data, ok = rows[0][0].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(4)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
data, ok = rows[0][1].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(55)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
data, ok = rows[0][2].(int64)
if !ok {
t.Fatal("rows not int64")
}
expected = int64(6)
if data != expected {
t.Fatal("rows not equal to:", expected)
}
err = tx1.Commit()
if err != nil {
t.Fatal("commit err:", err)
}
err = tx2.Commit()
if err != nil {
t.Fatal("commit err:", err)
}
queryResults = testQueryResults{
query: "select A, B, C from TRANSACTION_" + TestTimeString + " order by A",
queryResults: []testQueryResult{
{
results: [][]interface{}{
{int64(1), int64(22), int64(3)},
{int64(4), int64(55), int64(6)},
{int64(6), int64(7), int64(8)},
},
},
},
}
testRunQueryResults(t, queryResults)
// Run new transactions after the others finished successfully.
var tx3 *sql.Tx
var tx4 *sql.Tx
tx3, err = TestDB.BeginTx(ctx, nil)
if err != nil {
t.Fatal("begin tx error:", err)
}
tx4, err = TestDB.BeginTx(ctx, nil)
if err != nil {
t.Fatal("begin tx error:", err)
}
result, err = tx4.ExecContext(ctx, "update TRANSACTION_"+TestTimeString+" set B = :1 where A = :2", []interface{}{99, 1}...)
if err != nil {
t.Fatal("exec error:", err)
}
count, err = result.RowsAffected()
if err != nil {
t.Fatal("rows affected error:", err)
}
if count != 1 {
t.Fatalf("rows affected %v not equal to 1", count)
}
result, err = tx3.ExecContext(ctx, "update TRANSACTION_"+TestTimeString+" set B = :1 where A = :2", []interface{}{88, 6}...)
if err != nil {
t.Fatal("exec error:", err)
}
count, err = result.RowsAffected()
if err != nil {
t.Fatal("rows affected error:", err)
}
if count != 1 {
t.Fatalf("rows affected %v not equal to 1", count)
}
queryResults = testQueryResults{
query: "select A, B, C from TRANSACTION_" + TestTimeString + " order by A",
queryResults: []testQueryResult{
{
results: [][]interface{}{
{int64(1), int64(22), int64(3)},
{int64(4), int64(55), int64(6)},
{int64(6), int64(7), int64(8)},
},
},
},
}
testRunQueryResults(t, queryResults)
err = tx3.Commit()
if err != nil {
t.Fatal("commit err:", err)
}
err = tx4.Rollback()
if err != nil {
t.Fatal("commit err:", err)
}
queryResults = testQueryResults{
query: "select A, B, C from TRANSACTION_" + TestTimeString + " order by A",
queryResults: []testQueryResult{
{
results: [][]interface{}{
{int64(1), int64(22), int64(3)},
{int64(4), int64(55), int64(6)},
{int64(6), int64(88), int64(8)},
},
},
},
}
testRunQueryResults(t, queryResults)
}
// TestSelectDualNull checks null from dual
func TestSelectDualNull(t *testing.T) {
if TestDisableDatabase {
t.SkipNow()
}
t.Parallel()
queryResults := testQueryResults{
query: "select null from dual",
queryResults: []testQueryResult{{
results: [][]interface{}{{nil}}}}}
testRunQueryResults(t, queryResults)
}
func TestInsertRowid(t *testing.T) {
if TestDisableDatabase || TestDisableDestructive {
t.SkipNow()
}
// INSERT_ROWID
tableName := "INSERT_ROWID_" + TestTimeString
query := "create table " + tableName + " ( A INTEGER )"
// create table
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx)
cancel()
if err != nil {
stmt.Close()
t.Fatal("exec error:", err)
}
err = stmt.Close()
if err != nil {
t.Fatal("stmt close error:", err)
}
// drop table
defer func() {
query = "drop table " + tableName
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx)
cancel()
if err != nil {
stmt.Close()
t.Fatal("exec error:", err)
}
err = stmt.Close()
if err != nil {
t.Fatal("stmt close error:", err)
}
}()
// insert into table
query = "insert into " + tableName + " ( A ) values (:1) returning rowid into :rowid2"
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
rowids := make([]string, 3)
var result sql.Result
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
result, err = stmt.ExecContext(ctx, 1, sql.Named("rowid2", sql.Out{Dest: &rowids[0]}))
cancel()
if err != nil {
stmt.Close()
t.Fatal("exec error:", err)
}
var id int64
id, err = result.LastInsertId()
if err != nil {
stmt.Close()
t.Fatal("exec error:", err)
}
rowids[1] = GetLastInsertId(id)
err = stmt.Close()
if err != nil {
t.Fatal("stmt close error", err)
}
// get select rowid
query = "select rowid from " + tableName
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
var rows *sql.Rows
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
rows, err = stmt.QueryContext(ctx)
if err != nil {
cancel()
stmt.Close()
t.Fatal("query error:", err)
}
if !rows.Next() {
cancel()
rows.Close()
stmt.Close()
t.Fatal("expected row")
}
err = rows.Scan(&rowids[2])
if err != nil {
cancel()
rows.Close()
stmt.Close()
t.Fatal("scan error:", err)
}
if rows.Next() {
cancel()
rows.Close()
stmt.Close()
t.Fatal("more than one row")
}
err = rows.Err()
if err != nil {
cancel()
rows.Close()
stmt.Close()
t.Fatal("rows error:", err)
}
cancel()
err = rows.Close()
if err != nil {
stmt.Close()
t.Fatal("rows close error", err)
}
err = stmt.Close()
if err != nil {
t.Fatal("stmt close error", err)
}
// select rowids
query = "select A from " + tableName + " where rowid = :1"
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
var data int64
for _, rowid := range rowids {
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
rows, err = stmt.QueryContext(ctx, rowid)
if err != nil {
cancel()
stmt.Close()
t.Fatal("query error:", err)
}
if !rows.Next() {
cancel()
rows.Close()
stmt.Close()
t.Fatal("expected row")
}
err = rows.Scan(&data)
if err != nil {
cancel()
rows.Close()
stmt.Close()
t.Fatal("scan error:", err)
}
if data != 1 {
cancel()
rows.Close()
stmt.Close()
t.Fatal("row not equal to 1")
}
if rows.Next() {
cancel()
rows.Close()
stmt.Close()
t.Fatal("more than one row")
}
err = rows.Err()
if err != nil {
cancel()
rows.Close()
stmt.Close()
t.Fatal("rows error:", err)
}
cancel()
err = rows.Close()
if err != nil {
stmt.Close()
t.Fatal("rows close error", err)
}
}
err = stmt.Close()
if err != nil {
t.Fatal("stmt close error", err)
}
}
// TestNullBool tests NullBool
func TestNullBool(t *testing.T) {
if TestDisableDatabase {
t.SkipNow()
}
t.Parallel()
query := `
declare
function GET_BOOL(p_bool1 NUMERIC) return NUMERIC as
begin
if p_bool1 is null then
return 1;
end if;
return 0;
end GET_BOOL;
begin
:bool1 := GET_BOOL(:bool1);
end;`
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
var nullBool1 sql.NullBool
nullBool1.Bool = false
nullBool1.Valid = false
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx, sql.Named("bool1", sql.Out{Dest: &nullBool1, In: true}))
cancel()
if err != nil {
t.Fatal("exec error:", err)
}
if !nullBool1.Valid {
t.Fatal("nullBool1 not Valid")
}
if !nullBool1.Bool {
t.Fatal("nullBool1 is false")
}
nullBool1.Bool = true
nullBool1.Valid = true
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx, sql.Named("bool1", sql.Out{Dest: &nullBool1, In: true}))
cancel()
if err != nil {
t.Fatal("exec error:", err)
}
if !nullBool1.Valid {
t.Fatal("nullBool1 not Valid")
}
if nullBool1.Bool {
t.Fatal("nullBool1 is true")
}
query = `
declare
function GET_BOOL(p_bool1 NUMERIC) return NUMERIC as
begin
return null;
end GET_BOOL;
begin
:bool1 := GET_BOOL(:bool1);
end;`
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
nullBool1.Bool = true
nullBool1.Valid = true
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx, sql.Named("bool1", sql.Out{Dest: &nullBool1, In: true}))
cancel()
if err != nil {
t.Fatal("exec error:", err)
}
if nullBool1.Valid {
t.Fatal("nullBool1 is Valid")
}
if nullBool1.Bool {
t.Fatal("nullBool1 is true")
}
}
// TestQuestionMark tests question mark placeholder
func TestQuestionMark(t *testing.T) {
if TestDisableDatabase {
t.SkipNow()
}
t.Parallel()
var err error
db := testGetDB("?questionph=true")
if db == nil {
t.Fatal("db is null")
}
defer func() {
err = db.Close()
if err != nil {
t.Fatal("db close error:", err)
}
}()
var stmt *sql.Stmt
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = db.PrepareContext(ctx, "select ?, ?, ? from dual")
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
var result [][]interface{}
result, err = testGetRows(t, stmt, []interface{}{1, 2.25, "three"})
if err != nil {
t.Fatal("get rows error:", err)
}
if result == nil {
t.Fatal("result is nil")
}
if len(result) != 1 {
t.Fatal("len result not equal to 1")
}
if len(result[0]) != 3 {
t.Fatal("len result[0] not equal to 3")
}
float, ok := result[0][0].(float64)
if !ok {
t.Fatal("result not float64")
}
if float != float64(1) {
t.Fatal("result not equal:", float)
}
float, ok = result[0][1].(float64)
if !ok {
t.Fatal("result not float64")
}
if float != float64(2.25) {
t.Fatal("result not equal:", float)
}
var aString string
aString, ok = result[0][2].(string)
if !ok {
t.Fatal("result not string")
}
if aString != "three" {
t.Fatal("result not equal:", aString)
}
}
func BenchmarkSimpleInsert(b *testing.B) {
if TestDisableDatabase || TestDisableDestructive {
b.SkipNow()
}
b.StopTimer()
// SIMPLE_INSERT
tableName := "SIMPLE_INSERT_" + TestTimeString
query := "create table " + tableName + " ( A INTEGER )"
// create table
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
b.Fatal("prepare error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx)
cancel()
if err != nil {
stmt.Close()
b.Fatal("exec error:", err)
}
err = stmt.Close()
if err != nil {
b.Fatal("stmt close error:", err)
}
// drop table
defer func() {
query = "drop table " + tableName
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
b.Fatal("prepare error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx)
cancel()
if err != nil {
stmt.Close()
b.Fatal("exec error:", err)
}
err = stmt.Close()
if err != nil {
b.Fatal("stmt close error:", err)
}
}()
// insert into table
query = "insert into " + tableName + " ( A ) values (:1)"
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
b.Fatal("prepare error:", err)
}
b.ResetTimer()
b.StartTimer()
for n := 0; n < b.N; n++ {
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx, n)
cancel()
if err != nil {
stmt.Close()
b.Fatal("exec error:", err)
}
}
err = stmt.Close()
if err != nil {
b.Fatal("stmt close error", err)
}
}
func benchmarkSelectSetup(b *testing.B) {
fmt.Println("benchmark select setup start")
benchmarkSelectTableName = "BM_SELECT_" + TestTimeString
// create table
tableName := benchmarkSelectTableName
query := "create table " + tableName +
"( A INTEGER, B INTEGER, C INTEGER, D INTEGER, E VARCHAR2(255), F VARCHAR2(255), G VARCHAR2(255), H VARCHAR2(255) )"
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
b.Fatal("prepare error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx)
cancel()
if err != nil {
stmt.Close()
b.Fatal("exec error:", err)
}
// enable drop table in TestMain
benchmarkSelectTableCreated = true
err = stmt.Close()
if err != nil {
b.Fatal("stmt close error:", err)
}
// insert into table
query = "insert into " + tableName + ` ( A, B, C, D, E, F, G, H )
select :1, :2, :3, :4, :5, :6, :7, :8 from dual
union all select :9, :10, :11, :12, :13, :14, :15, :16 from dual
union all select :17, :18, :19, :20, :21, :22, :23, :24 from dual
union all select :25, :26, :27, :28, :29, :30, :31, :32 from dual
union all select :33, :34, :35, :36, :37, :38, :39, :40 from dual
union all select :41, :42, :43, :44, :45, :46, :47, :48 from dual
union all select :49, :50, :51, :52, :53, :54, :55, :56 from dual
union all select :57, :58, :59, :60, :61, :62, :63, :64 from dual
union all select :65, :66, :67, :68, :69, :70, :71, :72 from dual
union all select :73, :74, :75, :76, :77, :78, :79, :80 from dual`
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
b.Fatal("prepare error:", err)
}
insertString := strings.Repeat("a", 255)
for i := 0; i < 5000; i += 10 {
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx,
i, i+20000, i+40000, i+60000, insertString, insertString, insertString, insertString,
i+1, i+20001, i+40001, i+60001, insertString, insertString, insertString, insertString,
i+2, i+20002, i+40002, i+60002, insertString, insertString, insertString, insertString,
i+3, i+20003, i+40003, i+60003, insertString, insertString, insertString, insertString,
i+4, i+20004, i+40004, i+60004, insertString, insertString, insertString, insertString,
i+5, i+20005, i+40005, i+60005, insertString, insertString, insertString, insertString,
i+6, i+20006, i+40006, i+60006, insertString, insertString, insertString, insertString,
i+7, i+20007, i+40007, i+60007, insertString, insertString, insertString, insertString,
i+8, i+20008, i+40008, i+60008, insertString, insertString, insertString, insertString,
i+9, i+20009, i+40009, i+60009, insertString, insertString, insertString, insertString)
cancel()
if err != nil {
stmt.Close()
b.Fatal("exec error:", err)
}
}
err = stmt.Close()
if err != nil {
b.Fatal("stmt close error", err)
}
// select from table to warm up database cache
query = "select A, B, C, D, E, F, G, H from " + tableName
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = TestDB.PrepareContext(ctx, query)
cancel()
if err != nil {
b.Fatal("prepare error:", err)
}
defer func() {
err = stmt.Close()
if err != nil {
b.Fatal("stmt close error", err)
}
}()
var rows *sql.Rows
ctx, cancel = context.WithTimeout(context.Background(), 20*TestContextTimeout)
defer cancel()
rows, err = stmt.QueryContext(ctx)
if err != nil {
b.Fatal("exec error:", err)
}
defer func() {
err = rows.Close()
if err != nil {
b.Fatal("row close error:", err)
}
}()
var data1 int64
var data2 int64
var data3 int64
var data4 int64
var data5 string
var data6 string
var data7 string
var data8 string
var count int64
for rows.Next() {
err = rows.Scan(&data1, &data2, &data3, &data4, &data5, &data6, &data7, &data8)
if err != nil {
b.Fatal("scan error:", err)
}
count++
}
fmt.Printf("select data is %v bytes\n", (count*4*8)+(count*4*255))
err = rows.Err()
if err != nil {
b.Fatal("err error:", err)
}
b.ResetTimer()
fmt.Println("benchmark select setup end")
}
func benchmarkPrefetchSelect(b *testing.B, prefetchRows int64, prefetchMemory int64, n *int) {
benchmarkSelectTableOnce.Do(func() { benchmarkSelectSetup(b) })
var err error
db := testGetDB("?prefetch_rows=" + strconv.FormatInt(prefetchRows, 10) + "&prefetch_memory=" + strconv.FormatInt(prefetchMemory, 10))
if db == nil {
b.Fatal("db is null")
}
defer func() {
err = db.Close()
if err != nil {
b.Fatal("db close error:", err)
}
}()
b.StartTimer()
var stmt *sql.Stmt
tableName := benchmarkSelectTableName
query := "select A, B, C, D, E, F, G, H from " + tableName
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err = db.PrepareContext(ctx, query)
cancel()
if err != nil {
b.Fatal("prepare error:", err)
}
defer func() {
err = stmt.Close()
if err != nil {
b.Fatal("stmt close error", err)
}
}()
var rows *sql.Rows
ctx, cancel = context.WithTimeout(context.Background(), 20*TestContextTimeout)
defer cancel()
rows, err = stmt.QueryContext(ctx)
if err != nil {
b.Fatal("exec error:", err)
}
defer func() {
err = rows.Close()
if err != nil {
b.Fatal("row close error:", err)
}
}()
var data1 int64
var data2 int64
var data3 int64
var data4 int64
var data5 string
var data6 string
var data7 string
var data8 string
for ; rows.Next() && *n < b.N; *n++ {
err = rows.Scan(&data1, &data2, &data3, &data4, &data5, &data6, &data7, &data8)
if err != nil {
b.Fatal("scan error:", err)
}
}
b.StopTimer()
err = rows.Err()
if err != nil {
b.Fatal("err error:", err)
}
}
// TestSelectParallelWithStatementCaching checks parallel select from dual but with statement caching enabled
func TestSelectParallelWithStatementCaching(t *testing.T) {
if TestDisableDatabase {
t.SkipNow()
}
db := testGetDB("?stmt_cache_size=100")
if db == nil {
t.Fatal("db is null")
}
var waitGroup sync.WaitGroup
waitGroup.Add(50)
for i := 0; i < 50; i++ {
go func(num int) {
defer waitGroup.Done()
selectNumFromDual(t, db, float64(num))
}(i)
}
waitGroup.Wait()
}
// selectNumFromDual will execute a "select :1 from dual" where the parameter is the num param of this function
func selectNumFromDual(t testing.TB, db *sql.DB, num float64) {
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := db.PrepareContext(ctx, "select :1 from dual")
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}
defer func() {
if stmt != nil {
err := stmt.Close()
if err != nil {
t.Fatal("stmt close error:", err)
}
}
}()
var result [][]interface{}
result, err = testGetRows(t, stmt, []interface{}{num})
if err != nil {
t.Fatal("get rows error:", err)
}
if result == nil {
t.Fatal("result is nil")
}
if len(result) != 1 {
t.Fatal("len result not equal to 1")
}
if len(result[0]) != 1 {
t.Fatal("len result[0] not equal to 1")
}
data, ok := result[0][0].(float64)
if !ok {
t.Fatal("result not float64")
}
if data != num {
t.Fatal("result not equal to:", num)
}
}
func BenchmarkSelectNoCaching(b *testing.B) {
if TestDisableDatabase || TestDisableDestructive {
b.SkipNow()
}
for i := 0; i < b.N; i++ {
selectNumFromDual(b, TestDB, float64(i))
}
}
func BenchmarkSelectWithCaching(b *testing.B) {
b.StopTimer()
if TestDisableDatabase || TestDisableDestructive {
b.SkipNow()
}
db := testGetDB("?stmt_cache_size=100")
if db == nil {
b.Fatal("db is null")
}
defer func() {
err := db.Close()
if err != nil {
b.Fatal("db close error:", err)
}
}()
b.StartTimer()
for i := 0; i < b.N; i++ {
selectNumFromDual(b, db, float64(i))
}
}
func BenchmarkPrefetchR0M32768(b *testing.B) {
b.StopTimer()
if TestDisableDatabase || TestDisableDestructive {
b.SkipNow()
}
for n := 0; n < b.N; {
benchmarkPrefetchSelect(b, 0, 32768, &n)
}
}
func BenchmarkPrefetchR0M16384(b *testing.B) {
b.StopTimer()
if TestDisableDatabase || TestDisableDestructive {
b.SkipNow()
}
for n := 0; n < b.N; {
benchmarkPrefetchSelect(b, 0, 16384, &n)
}
}
func BenchmarkPrefetchR0M8192(b *testing.B) {
b.StopTimer()
if TestDisableDatabase || TestDisableDestructive {
b.SkipNow()
}
for n := 0; n < b.N; {
benchmarkPrefetchSelect(b, 0, 8192, &n)
}
}
func BenchmarkPrefetchR0M4096(b *testing.B) {
b.StopTimer()
if TestDisableDatabase || TestDisableDestructive {
b.SkipNow()
}
for n := 0; n < b.N; {
benchmarkPrefetchSelect(b, 0, 4096, &n)
}
}
func BenchmarkPrefetchR1000M0(b *testing.B) {
b.StopTimer()
if TestDisableDatabase || TestDisableDestructive {
b.SkipNow()
}
for n := 0; n < b.N; {
benchmarkPrefetchSelect(b, 1000, 0, &n)
}
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/GBase8s/go-gci.git
git@gitee.com:GBase8s/go-gci.git
GBase8s
go-gci
go-gci
master

搜索帮助

D67c1975 1850385 1daf7b77 1850385