1 Star 1 Fork 0

tdus / godror

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
z_plsql_types_test.go 24.30 KB
一键复制 编辑 原始数据 按行查看 历史
Tamás Gulácsi 提交于 2020-04-25 07:42 . Make shadow happy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
// Copyright 2019, 2020 The Godror Authors
//
//
// SPDX-License-Identifier: UPL-1.0 OR Apache-2.0
package godror_test
import (
"bytes"
"context"
"database/sql"
"database/sql/driver"
"fmt"
"strings"
"testing"
"time"
errors "golang.org/x/xerrors"
godror "github.com/godror/godror"
)
var _ godror.ObjectScanner = new(MyRecord)
var _ godror.ObjectWriter = new(MyRecord)
var _ godror.ObjectScanner = new(MyTable)
// MYRecord represents TEST_PKG_TYPES.MY_RECORD
type MyRecord struct {
*godror.Object
ID int64
Txt string
}
type coder interface{ Code() int }
func (r *MyRecord) Scan(src interface{}) error {
switch obj := src.(type) {
case *godror.Object:
id, err := obj.Get("ID")
if err != nil {
return err
}
r.ID = id.(int64)
txt, err := obj.Get("TXT")
if err != nil {
return err
}
r.Txt = string(txt.([]byte))
default:
return fmt.Errorf("Cannot scan from type %T", src)
}
return nil
}
// WriteObject update godror.Object with struct attributes values.
// Implement this method if you need the record as an input parameter.
func (r MyRecord) WriteObject() error {
// all attributes must be initialized or you get an "ORA-21525: attribute number or (collection element at index) %s violated its constraints"
err := r.ResetAttributes()
if err != nil {
return err
}
var data godror.Data
err = r.GetAttribute(&data, "ID")
if err != nil {
return err
}
data.SetInt64(r.ID)
r.SetAttribute("ID", &data)
if r.Txt != "" {
err = r.GetAttribute(&data, "TXT")
if err != nil {
return err
}
data.SetBytes([]byte(r.Txt))
r.SetAttribute("TXT", &data)
}
return nil
}
// MYTable represents TEST_PKG_TYPES.MY_TABLE
type MyTable struct {
godror.ObjectCollection
Items []*MyRecord
}
func (t *MyTable) Scan(src interface{}) error {
switch obj := src.(type) {
case *godror.Object:
collection := obj.Collection()
t.Items = make([]*MyRecord, 0)
for i, err := collection.First(); err == nil; i, err = collection.Next(i) {
var data godror.Data
err = collection.GetItem(&data, i)
if err != nil {
return err
}
o := data.GetObject()
defer o.Close()
var item MyRecord
err = item.Scan(o)
if err != nil {
return err
}
t.Items = append(t.Items, &item)
}
default:
return fmt.Errorf("Cannot scan from type %T", src)
}
return nil
}
func (r MyTable) WriteObject() error {
if len(r.Items) == 0 {
return nil
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
conn, err := godror.DriverConn(ctx, testDb)
if err != nil {
return err
}
data, err := conn.NewData(r.Items[0], len(r.Items), 0)
if err != nil {
return err
}
for i, item := range r.Items {
err = item.WriteObject()
if err != nil {
return err
}
d := data[i]
d.SetObject(item.ObjectRef())
r.Append(d)
}
return nil
}
func createPackages(ctx context.Context) error {
qry := []string{`CREATE OR REPLACE PACKAGE test_pkg_types AS
TYPE my_other_record IS RECORD (
id NUMBER(5),
txt VARCHAR2(200)
);
TYPE my_record IS RECORD (
id NUMBER(5),
other test_pkg_types.my_other_record,
txt VARCHAR2(200)
);
TYPE my_table IS
TABLE OF my_record;
END test_pkg_types;`,
`CREATE OR REPLACE PACKAGE test_pkg_sample AS
PROCEDURE test_record (
id IN NUMBER,
txt IN VARCHAR,
rec OUT test_pkg_types.my_record
);
PROCEDURE test_record_in (
rec IN OUT test_pkg_types.my_record
);
FUNCTION test_table (
x NUMBER
) RETURN test_pkg_types.my_table;
PROCEDURE test_table_in (
tb IN OUT test_pkg_types.my_table
);
END test_pkg_sample;`,
`CREATE OR REPLACE PACKAGE BODY test_pkg_sample AS
PROCEDURE test_record (
id IN NUMBER,
txt IN VARCHAR,
rec OUT test_pkg_types.my_record
) IS
BEGIN
rec.id := id;
rec.txt := txt;
END test_record;
PROCEDURE test_record_in (
rec IN OUT test_pkg_types.my_record
) IS
BEGIN
rec.id := rec.id + 1;
rec.txt := rec.txt || ' changed';
END test_record_in;
FUNCTION test_table (
x NUMBER
) RETURN test_pkg_types.my_table IS
tb test_pkg_types.my_table;
item test_pkg_types.my_record;
BEGIN
tb := test_pkg_types.my_table();
FOR c IN (
SELECT
level "LEV"
FROM
"SYS"."DUAL" "A1"
CONNECT BY
level <= x
) LOOP
item.id := c.lev;
item.txt := 'test - ' || ( c.lev * 2 );
tb.extend();
tb(tb.count) := item;
END LOOP;
RETURN tb;
END test_table;
PROCEDURE test_table_in (
tb IN OUT test_pkg_types.my_table
) IS
BEGIN
null;
END test_table_in;
END test_pkg_sample;`}
for _, ddl := range qry {
_, err := testDb.ExecContext(ctx, ddl)
if err != nil {
return err
}
}
return nil
}
func dropPackages(ctx context.Context) {
testDb.ExecContext(ctx, `DROP PACKAGE test_pkg_types`)
testDb.ExecContext(ctx, `DROP PACKAGE test_pkg_sample`)
}
func TestPLSQLTypes(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
serverVersion, err := godror.ServerVersion(ctx, testDb)
if err != nil {
t.Fatal(err)
}
clientVersion, err := godror.ClientVersion(ctx, testDb)
if err != nil {
t.Fatal(err)
}
if serverVersion.Version < 12 || clientVersion.Version < 12 {
t.Skip("client or server < 12")
}
err = createPackages(ctx)
if err != nil {
t.Fatal(err)
}
defer dropPackages(ctx)
conn, err := godror.DriverConn(ctx, testDb)
if err != nil {
t.Fatal(err)
}
//t.Run("Record", func(t *testing.T) {
// you must have execute privilege on package and use uppercase
{
objType, err := conn.GetObjectType("TEST_PKG_TYPES.MY_RECORD")
if err != nil {
t.Fatal(err)
}
obj, err := objType.NewObject()
if err != nil {
t.Fatal(err)
}
defer obj.Close()
for tName, tCase := range map[string]struct {
ID int64
txt string
want MyRecord
}{
"default": {ID: 1, txt: "test", want: MyRecord{obj, 1, "test"}},
"emptyTxt": {ID: 2, txt: "", want: MyRecord{obj, 2, ""}},
"zeroValues": {want: MyRecord{Object: obj}},
} {
rec := MyRecord{Object: obj}
params := []interface{}{
sql.Named("id", tCase.ID),
sql.Named("txt", tCase.txt),
sql.Named("rec", sql.Out{Dest: &rec}),
}
_, err = testDb.ExecContext(ctx, `begin test_pkg_sample.test_record(:id, :txt, :rec); end;`, params...)
if err != nil {
var cdr coder
if errors.As(err, &cdr); cdr.Code() == 21779 {
t.Skip(err)
}
t.Fatal(err)
}
if rec != tCase.want {
t.Errorf("%s: record got %v, wanted %v", tName, rec, tCase.want)
}
}
}
//})
//t.Run("Record IN OUT", func(t *testing.T) {
// you must have execute privilege on package and use uppercase
{
objType, err := conn.GetObjectType("TEST_PKG_TYPES.MY_RECORD")
if err != nil {
t.Fatal(err)
}
for tName, tCase := range map[string]struct {
in MyRecord
wantID int64
wantTxt string
}{
"zeroValues": {in: MyRecord{}, wantID: 1, wantTxt: " changed"},
"default": {in: MyRecord{ID: 1, Txt: "test"}, wantID: 2, wantTxt: "test changed"},
"emptyTxt": {in: MyRecord{ID: 2, Txt: ""}, wantID: 3, wantTxt: " changed"},
} {
obj, err := objType.NewObject()
if err != nil {
t.Fatal(err)
}
defer obj.Close()
rec := MyRecord{Object: obj, ID: tCase.in.ID, Txt: tCase.in.Txt}
params := []interface{}{
sql.Named("rec", sql.Out{Dest: &rec, In: true}),
}
_, err = testDb.ExecContext(ctx, `begin test_pkg_sample.test_record_in(:rec); end;`, params...)
if err != nil {
var cdr coder
if errors.As(err, &cdr); cdr.Code() == 21779 {
t.Skip(err)
}
t.Fatal(err)
}
if rec.ID != tCase.wantID {
t.Errorf("%s: ID got %d, wanted %d", tName, rec.ID, tCase.wantID)
}
if rec.Txt != tCase.wantTxt {
t.Errorf("%s: Txt got %s, wanted %s", tName, rec.Txt, tCase.wantTxt)
}
}
}
//})
//t.Run("Table", func(t *testing.T) {
{
// you must have execute privilege on package and use uppercase
objType, err := conn.GetObjectType("TEST_PKG_TYPES.MY_TABLE")
if err != nil {
t.Fatal(err)
}
items := []*MyRecord{&MyRecord{ID: 1, Txt: "test - 2"}, &MyRecord{ID: 2, Txt: "test - 4"}}
for tName, tCase := range map[string]struct {
in int64
want MyTable
}{
"one": {in: 1, want: MyTable{Items: items[:1]}},
"two": {in: 2, want: MyTable{Items: items}},
} {
obj, err := objType.NewObject()
if err != nil {
t.Fatal(err)
}
defer obj.Close()
tb := MyTable{ObjectCollection: obj.Collection()}
params := []interface{}{
sql.Named("x", tCase.in),
sql.Named("tb", sql.Out{Dest: &tb}),
}
_, err = testDb.ExecContext(ctx, `begin :tb := test_pkg_sample.test_table(:x); end;`, params...)
if err != nil {
var cdr coder
if errors.As(err, &cdr); cdr.Code() == 30757 {
t.Skip(err)
}
t.Fatal(err)
}
if len(tb.Items) != len(tCase.want.Items) {
t.Errorf("%s: table got %v items, wanted %v items", tName, len(tb.Items), len(tCase.want.Items))
} else {
for i := 0; i < len(tb.Items); i++ {
got := tb.Items[i]
want := tCase.want.Items[i]
if got.ID != want.ID {
t.Errorf("%s: record ID got %v, wanted %v", tName, got.ID, want.ID)
}
if got.Txt != want.Txt {
t.Errorf("%s: record TXT got %v, wanted %v", tName, got.Txt, want.Txt)
}
}
}
}
}
//})
//t.Run("Table IN", func(t *testing.T) {
{
// you must have execute privilege on package and use uppercase
tableObjType, err := conn.GetObjectType("TEST_PKG_TYPES.MY_TABLE")
if err != nil {
t.Fatal(err)
}
recordObjType, err := conn.GetObjectType("TEST_PKG_TYPES.MY_RECORD")
if err != nil {
t.Fatal(err)
}
items := make([]*MyRecord, 0)
obj1, err := recordObjType.NewObject()
if err != nil {
t.Fatal(err)
}
defer obj1.Close()
items = append(items, &MyRecord{ID: 1, Txt: "test - 2", Object: obj1})
obj2, err := recordObjType.NewObject()
if err != nil {
t.Fatal(err)
}
defer obj2.Close()
items = append(items, &MyRecord{ID: 2, Txt: "test - 4", Object: obj2})
for tName, tCase := range map[string]struct {
want MyTable
}{
"one": {want: MyTable{Items: items[:1]}},
"two": {want: MyTable{Items: items}},
} {
obj, err := tableObjType.NewObject()
if err != nil {
t.Fatal(err)
}
defer obj.Close()
tb := MyTable{ObjectCollection: obj.Collection(), Items: tCase.want.Items}
params := []interface{}{
sql.Named("tb", sql.Out{Dest: &tb, In: true}),
}
_, err = testDb.ExecContext(ctx, `begin test_pkg_sample.test_table_in(:tb); end;`, params...)
if err != nil {
var cdr coder
if errors.As(err, &cdr); cdr.Code() == 30757 {
t.Skip(err)
}
t.Fatal(err)
}
if len(tb.Items) != len(tCase.want.Items) {
t.Errorf("%s: table got %v items, wanted %v items", tName, len(tb.Items), len(tCase.want.Items))
} else {
for i := 0; i < len(tb.Items); i++ {
got := tb.Items[i]
want := tCase.want.Items[i]
if got.ID != want.ID {
t.Errorf("%s: record ID got %v, wanted %v", tName, got.ID, want.ID)
}
if got.Txt != want.Txt {
t.Errorf("%s: record TXT got %v, wanted %v", tName, got.Txt, want.Txt)
}
}
}
}
}
//})
}
func TestSelectObjectTable(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
const objTypeName, objTableName, pkgName = "test_selectObject", "test_selectObjTab", "test_selectObjPkg"
cleanup := func() {
testDb.Exec("DROP PACKAGE " + pkgName)
testDb.Exec("DROP TYPE " + objTableName)
testDb.Exec("DROP TYPE " + objTypeName)
}
cleanup()
for _, qry := range []string{
`CREATE OR REPLACE TYPE ` + objTypeName + ` AS OBJECT (
AA NUMBER(2),
BB NUMBER(13,2),
CC NUMBER(13,2),
DD NUMBER(13,2),
MSG varchar2(100))`,
"CREATE OR REPLACE TYPE " + objTableName + " AS TABLE OF " + objTypeName,
`CREATE OR REPLACE PACKAGE ` + pkgName + ` AS
function FUNC_1( p1 in varchar2, p2 in varchar2) RETURN ` + objTableName + `;
END;`,
`CREATE OR REPLACE PACKAGE BODY ` + pkgName + ` AS
FUNCTION func_1( p1 IN VARCHAR2, p2 IN VARCHAR2) RETURN ` + objTableName + ` is
ret ` + objTableName + ` := ` + objTableName + `();
begin
ret.extend;
ret(ret.count):= ` + objTypeName + `( 11, 22, 33, 44, p1||'success!'||p2);
ret.extend;
ret(ret.count):= ` + objTypeName + `( 55, 66, 77, 88, p1||'failed!'||p2);
return ret;
end;
END;`,
} {
if _, err := testDb.ExecContext(ctx, qry); err != nil {
t.Error(errors.Errorf("%s: %w", qry, err))
}
}
defer cleanup()
const qry = "select " + pkgName + ".FUNC_1('aa','bb') from dual"
rows, err := testDb.QueryContext(ctx, qry)
if err != nil {
t.Fatal(errors.Errorf("%s: %w", qry, err))
}
defer rows.Close()
for rows.Next() {
var objI interface{}
if err = rows.Scan(&objI); err != nil {
t.Fatal(errors.Errorf("%s: %w", qry, err))
}
obj := objI.(*godror.Object).Collection()
defer obj.Close()
t.Log(obj.FullName())
i, err := obj.First()
if err != nil {
t.Fatal(err)
}
var objData, attrData godror.Data
for {
if err = obj.GetItem(&objData, i); err != nil {
t.Fatal(err)
}
if err = objData.GetObject().GetAttribute(&attrData, "MSG"); err != nil {
t.Fatal(err)
}
msg := string(attrData.GetBytes())
t.Logf("%d. msg: %+v", i, msg)
if i, err = obj.Next(i); err != nil {
break
}
}
}
}
func TestFuncBool(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
const pkgName = "test_bool"
cleanup := func() { testDb.Exec("DROP PROCEDURE " + pkgName) }
cleanup()
conn, err := testDb.Conn(ctx)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
if err = godror.EnableDbmsOutput(ctx, conn); err != nil {
t.Error(err)
}
const crQry = "CREATE OR REPLACE PROCEDURE " + pkgName + `(p_in IN BOOLEAN, p_not OUT BOOLEAN, p_num OUT NUMBER) IS
BEGIN
DBMS_OUTPUT.PUT_LINE('in='||(CASE WHEN p_in THEN 'Y' ELSE 'N' END));
p_not := NOT p_in;
p_num := CASE WHEN p_in THEN 1 ELSE 0 END;
END;`
if _, err = conn.ExecContext(ctx, crQry); err != nil {
t.Fatal(errors.Errorf("%s: %w", crQry, err))
}
defer cleanup()
const qry = "BEGIN " + pkgName + "(p_in=>:1, p_not=>:2, p_num=>:3); END;"
var buf bytes.Buffer
for _, in := range []bool{true, false} {
var out bool
var num int
if _, err = conn.ExecContext(ctx, qry, in, sql.Out{Dest: &out}, sql.Out{Dest: &num}); err != nil {
if srv, err := godror.ServerVersion(ctx, conn); err != nil {
t.Log(err)
} else if srv.Version < 18 {
t.Skipf("%q: %v", qry, err)
} else {
t.Errorf("%q: %v", qry, err)
}
continue
}
t.Logf("in:%v not:%v num:%v", in, out, num)
want := 0
if in {
want = 1
}
if num != want || out != !in {
buf.Reset()
if err = godror.ReadDbmsOutput(ctx, &buf, conn); err != nil {
t.Error(err)
}
t.Log(buf.String())
t.Errorf("got %v/%v wanted %v/%v", out, num, want, !in)
}
}
}
func TestPlSqlObjectDirect(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
testCon, err := godror.DriverConn(ctx, testDb)
if err != nil {
t.Fatal(err)
}
const crea = `CREATE OR REPLACE PACKAGE test_pkg_obj IS
TYPE int_tab_typ IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;
TYPE rec_typ IS RECORD (int PLS_INTEGER, num NUMBER, vc VARCHAR2(1000), c CHAR(10), dt DATE);
TYPE tab_typ IS TABLE OF rec_typ INDEX BY PLS_INTEGER;
PROCEDURE modify(p_obj IN OUT NOCOPY tab_typ, p_int IN PLS_INTEGER);
END;`
const crea2 = `CREATE OR REPLACE PACKAGE BODY test_pkg_obj IS
PROCEDURE modify(p_obj IN OUT NOCOPY tab_typ, p_int IN PLS_INTEGER) IS
v_idx PLS_INTEGER := NVL(p_obj.LAST, 0) + 1;
BEGIN
p_obj(v_idx).int := p_int;
p_obj(v_idx).num := 314/100;
p_obj(v_idx).vc := 'abraka';
p_obj(v_idx).c := 'X';
p_obj(v_idx).dt := SYSDATE;
END modify;
END;`
if err = prepExec(ctx, testCon, crea); err != nil {
t.Fatal(err)
}
//defer prepExec(ctx, testCon, "DROP PACKAGE test_pkg_obj")
if err = prepExec(ctx, testCon, crea2); err != nil {
t.Fatal(err)
}
//defer tl.enableLogging(t)()
clientVersion, _ := godror.ClientVersion(ctx, testDb)
serverVersion, _ := godror.ServerVersion(ctx, testDb)
t.Logf("clientVersion: %#v, serverVersion: %#v", clientVersion, serverVersion)
cOt, err := testCon.GetObjectType(strings.ToUpper("test_pkg_obj.tab_typ"))
if err != nil {
if clientVersion.Version >= 12 && serverVersion.Version >= 12 {
t.Fatal(fmt.Sprintf("%+v", err))
}
t.Log(err)
t.Skipf("client=%d or server=%d < 12", clientVersion.Version, serverVersion.Version)
}
t.Log(cOt)
// create object from the type
coll, err := cOt.NewCollection()
if err != nil {
t.Fatal(err)
}
defer coll.Close()
// create an element object
elt, err := cOt.CollectionOf.NewObject()
if err != nil {
t.Fatal(err)
}
defer elt.Close()
elt.ResetAttributes()
if err = elt.Set("C", "Z"); err != nil {
t.Fatal(err)
}
if err = elt.Set("INT", int32(-2)); err != nil {
t.Fatal(err)
}
// append to the collection
t.Logf("elt: %s", elt)
coll.AppendObject(elt)
const mod = "BEGIN test_pkg_obj.modify(:1, :2); END;"
if err = prepExec(ctx, testCon, mod,
driver.NamedValue{Ordinal: 1, Value: coll},
driver.NamedValue{Ordinal: 2, Value: 42},
); err != nil {
t.Error(err)
}
t.Logf("coll: %s", coll)
var data godror.Data
for i, err := coll.First(); err == nil; i, err = coll.Next(i) {
if err = coll.GetItem(&data, i); err != nil {
t.Fatal(err)
}
elt.ResetAttributes()
elt = data.GetObject()
t.Logf("elt[%d]: %s", i, elt)
for attr := range elt.Attributes {
val, err := elt.Get(attr)
if err != nil {
if godror.DpiVersionNumber <= 30201 {
t.Log(err, attr)
} else {
t.Error(err, attr)
}
}
t.Logf("elt[%d].%s=%v", i, attr, val)
}
}
}
func prepExec(ctx context.Context, testCon driver.ConnPrepareContext, qry string, args ...driver.NamedValue) error {
stmt, err := testCon.PrepareContext(ctx, qry)
if err != nil {
return errors.Errorf("%s: %w", qry, err)
}
_, err = stmt.(driver.StmtExecContext).ExecContext(ctx, args)
stmt.Close()
if err != nil {
return errors.Errorf("%s: %w", qry, err)
}
return nil
}
func TestPlSqlObject(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
conn, err := testDb.Conn(ctx)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
pkg := strings.ToUpper("test_pkg_obj" + tblSuffix)
qry := `CREATE OR REPLACE PACKAGE ` + pkg + ` IS
TYPE int_tab_typ IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;
TYPE rec_typ IS RECORD (int PLS_INTEGER, num NUMBER, vc VARCHAR2(1000), c CHAR(1000), dt DATE);
TYPE tab_typ IS TABLE OF rec_typ INDEX BY PLS_INTEGER;
END;`
if _, err = conn.ExecContext(ctx, qry); err != nil {
t.Fatal(errors.Errorf("%s: %w", qry, err))
}
defer testDb.Exec("DROP PACKAGE " + pkg)
tx, err := conn.BeginTx(ctx, nil)
if err != nil {
t.Fatal(err)
}
defer tx.Rollback()
defer tl.enableLogging(t)()
ot, err := godror.GetObjectType(ctx, tx, pkg+strings.ToUpper(".int_tab_typ"))
if err != nil {
if clientVersion.Version >= 12 && serverVersion.Version >= 12 {
t.Fatal(fmt.Sprintf("%+v", err))
}
t.Log(err)
t.Skip("client or server version < 12")
}
t.Log(ot)
}
func TestCallWithObject(t *testing.T) {
t.Parallel()
cleanup := func() {
for _, drop := range []string{
"DROP PROCEDURE test_cwo_getSum",
"DROP TYPE test_cwo_tbl_t",
"DROP TYPE test_cwo_rec_t",
} {
testDb.Exec(drop)
}
}
const crea = `CREATE OR REPLACE TYPE test_cwo_rec_t FORCE AS OBJECT (
numberpart1 VARCHAR2(6),
numberpart2 VARCHAR2(10),
code VARCHAR(7),
CONSTRUCTOR FUNCTION test_cwo_rec_t RETURN SELF AS RESULT
);
CREATE OR REPLACE TYPE test_cwo_tbl_t FORCE AS TABLE OF test_cwo_rec_t;
CREATE OR REPLACE PROCEDURE test_cwo_getSum(
p_operation_id IN OUT VARCHAR2,
a_languagecode_i IN VARCHAR2,
a_username_i IN VARCHAR2,
a_channelcode_i IN VARCHAR2,
a_mcalist_i IN test_cwo_tbl_t,
a_validfrom_i IN DATE,
a_validto_i IN DATE,
a_statuscode_list_i IN VARCHAR2 ,
a_type_list_o OUT SYS_REFCURSOR
) IS
cnt PLS_INTEGER;
BEGIN
cnt := a_mcalist_i.COUNT;
OPEN a_type_list_o FOR
SELECT cnt FROM DUAL;
END;
`
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
cleanup()
for _, qry := range strings.Split(crea, "CREATE OR") {
if qry == "" {
continue
}
qry = "CREATE OR" + qry
if _, err := testDb.ExecContext(ctx, qry); err != nil {
t.Fatal(errors.Errorf("%s: %w", qry, err))
}
}
var p_operation_id string
var a_languagecode_i string
var a_username_i string
var a_channelcode_i string
var a_mcalist_i *godror.Object
var a_validfrom_i string
var a_validto_i string
var a_statuscode_list_i string
var a_type_list_o driver.Rows
tx, err := testDb.BeginTx(ctx, nil)
if err != nil {
t.Fatal(err)
}
defer tx.Rollback()
typ, err := godror.GetObjectType(ctx, tx, "test_cwo_tbl_t")
if err != nil {
t.Fatal(err)
}
if a_mcalist_i, err = typ.NewObject(); err != nil {
t.Fatal(err)
}
if typ, err = godror.GetObjectType(ctx, tx, "test_cwo_rec_t"); err != nil {
t.Fatal(err)
}
elt, err := typ.NewObject()
if err != nil {
t.Fatal(err)
}
if err = elt.Set("NUMBERPART1", "np1"); err != nil {
t.Fatal(err)
}
if err = a_mcalist_i.Collection().Append(elt); err != nil {
t.Fatal(err)
}
const qry = `BEGIN test_cwo_getSum(:v1,:v2,:v3,:v4,:v5,:v6,:v7,:v8,:v9); END;`
if _, err = tx.ExecContext(ctx, qry,
sql.Named("v1", sql.Out{Dest: &p_operation_id, In: true}),
sql.Named("v2", &a_languagecode_i),
sql.Named("v3", &a_username_i),
sql.Named("v4", &a_channelcode_i),
sql.Named("v5", &a_mcalist_i),
sql.Named("v6", &a_validfrom_i),
sql.Named("v7", &a_validto_i),
sql.Named("v8", &a_statuscode_list_i),
sql.Named("v9", sql.Out{Dest: &a_type_list_o}),
); err != nil {
t.Fatal(err)
}
rows, err := godror.WrapRows(ctx, tx, a_type_list_o)
if err != nil {
t.Fatal(err)
}
defer rows.Close()
var i int
for rows.Next() {
var n int
if err = rows.Scan(&n); err != nil {
t.Fatal(err)
}
i++
t.Logf("%d. %d", i, n)
}
}
func BenchmarkObjArray(b *testing.B) {
cleanup := func() { testDb.Exec("DROP FUNCTION test_objarr"); testDb.Exec("DROP TYPE test_vc2000_arr") }
cleanup()
qry := "CREATE OR REPLACE TYPE test_vc2000_arr AS TABLE OF VARCHAR2(2000)"
if _, err := testDb.Exec(qry); err != nil {
b.Fatal(errors.Errorf("%s: %w", qry, err))
}
defer cleanup()
qry = `CREATE OR REPLACE FUNCTION test_objarr(p_arr IN test_vc2000_arr) RETURN PLS_INTEGER IS BEGIN RETURN p_arr.COUNT; END;`
if _, err := testDb.Exec(qry); err != nil {
b.Fatal(errors.Errorf("%s: %w", qry, err))
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
b.Run("object", func(b *testing.B) {
b.StopTimer()
const qry = `BEGIN :1 := test_objarr(:2); END;`
stmt, err := testDb.PrepareContext(ctx, qry)
if err != nil {
b.Fatal(errors.Errorf("%s: %w", qry, err))
}
defer stmt.Close()
typ, err := godror.GetObjectType(ctx, testDb, "TEST_VC2000_ARR")
if err != nil {
b.Fatal(err)
}
obj, err := typ.NewObject()
if err != nil {
b.Fatal(err)
}
defer obj.Close()
coll := obj.Collection()
var rc int
b.StartTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
length, err := coll.Len()
if err != nil {
b.Fatal(err)
}
b.StartTimer()
if b.N < 1024 {
for length < b.N {
if err = coll.Append(fmt.Sprintf("--test--%010d--", i)); err != nil {
b.Fatal(err)
}
length++
}
}
if _, err := stmt.ExecContext(ctx, sql.Out{Dest: &rc}, obj); err != nil {
b.Fatal(err)
}
if rc != length {
b.Error("got", rc, "wanted", length)
}
}
})
b.Run("plsarr", func(b *testing.B) {
b.StopTimer()
const qry = `DECLARE
TYPE vc2000_tab_typ IS TABLE OF VARCHAR2(2000) INDEX BY PLS_INTEGER;
v_tbl vc2000_tab_typ := :1;
v_idx PLS_INTEGER;
v_arr test_vc2000_arr := test_vc2000_arr();
BEGIN
-- copy the PL/SQL associative array to the nested table:
v_idx := v_tbl.FIRST;
WHILE v_idx IS NOT NULL LOOP
v_arr.EXTEND;
v_arr(v_arr.LAST) := v_tbl(v_idx);
v_idx := v_tbl.NEXT(v_idx);
END LOOP;
-- call the procedure:
:2 := test_objarr(p_arr=>v_arr);
END;`
stmt, err := testDb.PrepareContext(ctx, qry)
if err != nil {
b.Fatal(errors.Errorf("%s: %w", qry, err))
}
defer stmt.Close()
b.StartTimer()
var rc int
var array []string
for i := 0; i < b.N; i++ {
if b.N < 1024 {
for len(array) < b.N {
array = append(array, fmt.Sprintf("--test--%010d--", i))
}
}
if _, err := stmt.ExecContext(ctx, godror.PlSQLArrays, array, sql.Out{Dest: &rc}); err != nil {
b.Fatal(err)
}
if rc != len(array) {
b.Error(rc)
}
}
})
}
1
https://gitee.com/tdus/godror.git
git@gitee.com:tdus/godror.git
tdus
godror
godror
master

搜索帮助