Skip to content

Commit

Permalink
Merge pull request #383 from MichaelS11/cursor
Browse files Browse the repository at this point in the history
Added ref cursor sub query
  • Loading branch information
MichaelS11 committed Mar 25, 2020
2 parents d01a807 + 431baf8 commit 6d59bec
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 31 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ services:
language: bash

before_install:
- docker pull wnameless/oracle-xe-11g
- docker run -d wnameless/oracle-xe-11g
- docker pull wnameless/oracle-xe-11g-r2
- docker run -d wnameless/oracle-xe-11g-r2

script:
- docker run --rm -i -e TESTDIR=$(pwd) -v $(pwd):$(pwd) wnameless/oracle-xe-11g /bin/bash < test.sh
- docker run --rm -i -e TESTDIR=$(pwd) -v $(pwd):$(pwd) wnameless/oracle-xe-11g-r2 /bin/bash < test.sh
28 changes: 17 additions & 11 deletions c_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,24 @@ func cGoStringN(s *C.OraText, size int) string {

// freeDefines frees defines
func freeDefines(defines []oci8Define) {
for _, define := range defines {
if define.pbuf != nil {
freeBuffer(define.pbuf, define.dataType)
define.pbuf = nil
for i := 0; i < len(defines); i++ {
if len(defines[i].subDefines) > 0 {
freeDefines(defines[i].subDefines)
}
if define.length != nil {
C.free(unsafe.Pointer(define.length))
define.length = nil
defines[i].subDefines = nil
if defines[i].pbuf != nil {
freeBuffer(defines[i].pbuf, defines[i].dataType)
defines[i].pbuf = nil
}
if define.indicator != nil {
C.free(unsafe.Pointer(define.indicator))
define.indicator = nil
if defines[i].length != nil {
C.free(unsafe.Pointer(defines[i].length))
defines[i].length = nil
}
define.defineHandle = nil // should be freed by oci statement close
if defines[i].indicator != nil {
C.free(unsafe.Pointer(defines[i].indicator))
defines[i].indicator = nil
}
defines[i].defineHandle = nil // should be freed by oci statement close
}
}

Expand Down Expand Up @@ -124,6 +128,8 @@ func freeBuffer(buffer unsafe.Pointer, dataType C.ub2) {
C.OCIDescriptorFree(*(*unsafe.Pointer)(buffer), C.OCI_DTYPE_INTERVAL_DS)
case C.SQLT_INTERVAL_YM:
C.OCIDescriptorFree(*(*unsafe.Pointer)(buffer), C.OCI_DTYPE_INTERVAL_YM)
case C.SQLT_RSET:
C.OCIDescriptorFree(*(*unsafe.Pointer)(buffer), C.OCI_HTYPE_STMT)
default:
C.free(buffer)
}
Expand Down
2 changes: 1 addition & 1 deletion globals.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ type (
length *C.ub2
indicator *C.sb2
defineHandle *C.OCIDefine
subDefines []oci8Define
}

oci8Bind struct {
Expand All @@ -118,7 +119,6 @@ type (
OCI8Rows struct {
stmt *OCI8Stmt
defines []oci8Define
e bool
closed bool
ctx context.Context
}
Expand Down
149 changes: 149 additions & 0 deletions oci8_sql_number_go112_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// +build go1.12

package oci8

import (
"context"
"database/sql"
"testing"
)

// TestDestructiveNumberCursor checks select cursor
func TestDestructiveNumberCursor(t *testing.T) {
if TestDisableDatabase || TestDisableDestructive {
t.SkipNow()
}

tableName := "number_cursor_" + TestTimeString
err := testExec(t, "create table "+tableName+" ( A INTEGER )", nil)
if err != nil {
t.Fatal("create table error:", err)
}

defer testDropTable(t, tableName)

testDestructiveNumberCursorInsert(t, tableName)

testDestructiveNumberCursorSelect(t, tableName)
}

func testDestructiveNumberCursorInsert(t *testing.T, tableName string) {
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, "insert into "+tableName+" ( A ) values (:1)")
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}

defer func() {
err = stmt.Close()
if err != nil {
if t.Failed() {
t.Logf("stmt close error: %v", err)
} else {
t.Fatal("stmt close error:", err)
}
}
}()

ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx, 1)
cancel()
if err != nil {
t.Fatal("exec error:", err)
}

ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
_, err = stmt.ExecContext(ctx, 2)
cancel()
if err != nil {
stmt.Close()
t.Fatal("exec error:", err)
}
}

func testDestructiveNumberCursorSelect(t *testing.T, tableName string) {
ctx, cancel := context.WithTimeout(context.Background(), TestContextTimeout)
stmt, err := TestDB.PrepareContext(ctx, "select 1.5, cursor(select A from "+tableName+" order by A) from dual")
cancel()
if err != nil {
t.Fatal("prepare error:", err)
}

defer func() {
err = stmt.Close()
if err != nil {
if t.Failed() {
t.Logf("stmt close err: %v", err)
} else {
t.Fatal("stmt close error:", err)
}
}
}()

var rows *sql.Rows
ctx, cancel = context.WithTimeout(context.Background(), TestContextTimeout)
rows, err = stmt.QueryContext(ctx)
if err != nil {
cancel()
t.Fatal("query error:", err)
}

defer func() {
cancel()
err = rows.Close()
if err != nil {
if t.Failed() {
t.Logf("rows close error: %v", err)
} else {
t.Fatal("rows close error:", err)
}
}
}()

if !rows.Next() {
t.Fatal("expected row")
}

var float float64
var subRows *sql.Rows
err = rows.Scan(&float, &subRows)
if err != nil {
t.Fatal("scan error:", err)
}

if float != 1.5 {
t.Fatal("float != 1.5")
}

if subRows == nil {
t.Fatal("subRows is nil")
}

if !subRows.Next() {
t.Fatal("expected row")
}

var aInt int64
err = subRows.Scan(&aInt)
if err != nil {
t.Fatal("scan error:", err)
}

if aInt != 1 {
t.Fatal("aInt != 1")
}

if !subRows.Next() {
t.Fatal("expected row")
}

err = subRows.Scan(&aInt)
if err != nil {
t.Fatal("scan error:", err)
}

if aInt != 2 {
t.Fatal("aInt != 2")
}
}
22 changes: 20 additions & 2 deletions rows.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ func (rows *OCI8Rows) Close() error {
// Columns returns column names
func (rows *OCI8Rows) Columns() []string {
names := make([]string, len(rows.defines))
for i, define := range rows.defines {
names[i] = define.name
for i := 0; i < len(rows.defines); i++ {
names[i] = rows.defines[i].name
}
return names
}
Expand Down Expand Up @@ -199,6 +199,24 @@ func (rows *OCI8Rows) Next(dest []driver.Value) error {
}
dest[i] = (int64(years) * 12) + int64(months)

// SQLT_RSET - ref cursor
case C.SQLT_RSET:
stmtP := (**C.OCIStmt)(rows.defines[i].pbuf)
subStmt := &OCI8Stmt{conn: rows.stmt.conn, stmt: *stmtP}
if rows.defines[i].subDefines == nil {
var err error
rows.defines[i].subDefines, err = subStmt.makeDefines(rows.ctx)
if err != nil {
return err
}
}
subRows := &OCI8Rows{
stmt: subStmt,
defines: rows.defines[i].subDefines,
ctx: rows.ctx,
}
dest[i] = subRows

// default
default:
return fmt.Errorf("Unhandled column type: %d", rows.defines[i].dataType)
Expand Down
46 changes: 34 additions & 12 deletions statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,8 +424,29 @@ func (stmt *OCI8Stmt) query(ctx context.Context, binds []oci8Bind) (driver.Rows,
return nil, err
}

var defines []oci8Define
defines, err = stmt.makeDefines(ctx)
if err != nil {
return nil, err
}

if ctx.Err() != nil {
freeDefines(defines)
return nil, ctx.Err()
}

rows := &OCI8Rows{
stmt: stmt,
defines: defines,
ctx: ctx,
}

return rows, nil
}

func (stmt *OCI8Stmt) makeDefines(ctx context.Context) ([]oci8Define, error) {
var paramCountUb4 C.ub4 // number of columns in the select-list
_, err = stmt.ociAttrGet(unsafe.Pointer(&paramCountUb4), C.OCI_ATTR_PARAM_COUNT)
_, err := stmt.ociAttrGet(unsafe.Pointer(&paramCountUb4), C.OCI_ATTR_PARAM_COUNT)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -597,6 +618,17 @@ func (stmt *OCI8Stmt) query(ctx context.Context, binds []oci8Bind) (driver.Rows,
defines[i].maxSize = 40
defines[i].pbuf = C.malloc(C.size_t(defines[i].maxSize))

case C.SQLT_RSET: // ref cursor
defines[i].dataType = dataType
defines[i].maxSize = C.sb4(sizeOfNilPointer)
var stmtP *unsafe.Pointer
stmtP, _, err = stmt.conn.ociHandleAlloc(C.OCI_HTYPE_STMT, 0)
if err != nil {
freeDefines(defines)
return nil, err
}
defines[i].pbuf = unsafe.Pointer(stmtP)

default:
defines[i].dataType = C.SQLT_AFC
defines[i].maxSize = C.sb4(maxSize)
Expand All @@ -622,17 +654,7 @@ func (stmt *OCI8Stmt) query(ctx context.Context, binds []oci8Bind) (driver.Rows,
}
}

if ctx.Err() != nil {
return nil, ctx.Err()
}

rows := &OCI8Rows{
stmt: stmt,
defines: defines,
ctx: ctx,
}

return rows, nil
return defines, nil
}

// getRowid returns the rowid
Expand Down
34 changes: 32 additions & 2 deletions test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@ apt-get -qq -y install git pkg-config gcc wget 2>&1 > /dev/null

echo "installing go"
cd /tmp/
wget -nv https://dl.google.com/go/go1.11.5.linux-amd64.tar.gz
wget -nv https://dl.google.com/go/go1.13.8.linux-amd64.tar.gz
wget -nv https://dl.google.com/go/go1.12.17.linux-amd64.tar.gz
wget -nv https://dl.google.com/go/go1.11.13.linux-amd64.tar.gz
wget -nv https://dl.google.com/go/go1.10.8.linux-amd64.tar.gz
wget -nv https://dl.google.com/go/go1.9.7.linux-amd64.tar.gz

mkdir -p /usr/local/goFiles1.13.x
tar -xf /tmp/go1.13.8.linux-amd64.tar.gz
mv /tmp/go /usr/local/go1.13.x

mkdir -p /usr/local/goFiles1.12.x
tar -xf /tmp/go1.12.17.linux-amd64.tar.gz
mv /tmp/go /usr/local/go1.12.x

mkdir -p /usr/local/goFiles1.11.x
tar -xf /tmp/go1.11.5.linux-amd64.tar.gz
tar -xf /tmp/go1.11.13.linux-amd64.tar.gz
mv /tmp/go /usr/local/go1.11.x

mkdir -p /usr/local/goFiles1.10.x
Expand Down Expand Up @@ -64,6 +74,26 @@ PKGCONFIG
export PATH_SAVE=${PATH}


echo "testing go-oci8 Go 1.13.x"
export PATH=/usr/local/go1.13.x/bin:${PATH_SAVE}
export GOROOT=/usr/local/go1.13.x
export GOPATH=/usr/local/goFiles1.13.x
mkdir -p ${GOPATH}/src/github.com/mattn/go-oci8
cp -r ${TESTDIR}/* ${GOPATH}/src/github.com/mattn/go-oci8/

go test -v github.com/mattn/go-oci8 -args -disableDatabase=false -hostValid ${DOCKER_IP} -username scott -password tiger


echo "testing go-oci8 Go 1.12.x"
export PATH=/usr/local/go1.12.x/bin:${PATH_SAVE}
export GOROOT=/usr/local/go1.12.x
export GOPATH=/usr/local/goFiles1.12.x
mkdir -p ${GOPATH}/src/github.com/mattn/go-oci8
cp -r ${TESTDIR}/* ${GOPATH}/src/github.com/mattn/go-oci8/

go test -v github.com/mattn/go-oci8 -args -disableDatabase=false -hostValid ${DOCKER_IP} -username scott -password tiger


echo "testing go-oci8 Go 1.11.x"
export PATH=/usr/local/go1.11.x/bin:${PATH_SAVE}
export GOROOT=/usr/local/go1.11.x
Expand Down

0 comments on commit 6d59bec

Please sign in to comment.