Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't close connection with Out parameter Object type with field type CLob(string) #321

Open
bikain opened this issue Dec 3, 2023 · 4 comments

Comments

@bikain
Copy link

bikain commented Dec 3, 2023

Describe the bug
If use out Oracle object with field type CLOB, the connection is not closed.

To Reproduce
Oracle type

create or replace type typeClob as object(
  id number,
  value clob
)

Golang:

package main

import (
	"context"
	"database/sql"
	"fmt"
	"log/slog"
	"os"
	"time"

	godror "github.com/godror/godror"
)

func connect() *sql.DB {
	var params godror.ConnectionParams

	params.Username = "***"
	params.Password = godror.NewPassword("***")
	params.ConnectString = "***"

	params.PoolParams.SessionTimeout = time.Duration(10) * time.Second
	params.PoolParams.WaitTimeout = time.Duration(2) * time.Second
	params.PoolParams.MaxLifeTime = time.Duration(30) * time.Second
	params.PoolParams.SessionIncrement = int(1)
	params.PoolParams.MinSessions = int(1)
	params.PoolParams.MaxSessions = int(3)

	params.ConnParams.ConnClass = "POOLED"
	fmt.Println("params:", params)
	return sql.OpenDB(godror.NewConnector(params))
}

func Exec(db *sql.DB, query string, variables ...any) error {

	context := context.Background()

	tx, err := db.BeginTx(context, nil)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	stmt, err := tx.PrepareContext(context, query)
	if err != nil {
		return err
	}
	defer stmt.Close()

	_, err = stmt.ExecContext(context, variables...)
	if err != nil {
		return err
	}

	return nil
}

type clob struct {
	godror.ObjectTypeName `godror:"typeClob"`
	Id                    float64 `godror:"ID"`
	Value                 string  `godror:"VALUE"`
}

func testConnectInClob(db *sql.DB, i float64) {
	fmt.Println("start In clob", i)
	var c clob
	c.Id = 1
	c.Value = `test`

	var res float64
	err := Exec(db, `
	declare
		v_child typeClob;
	begin
		v_child := :c;
		v_child.id := v_child.id + 5;
		:res := v_child.id;
	end;`,
		sql.Named(`c`, c),
		sql.Named(`res`, sql.Out{In: false, Dest: &res}),
	)
	fmt.Println("result", i, res, err)
}

func testConnectOutClob(db *sql.DB, i float64) {
	fmt.Println("start Out clob", i)
	var c clob

	err := Exec(db, `
	declare
		v_child typeClob := typeClob(id => null, value => null);
	begin
		v_child.id :=:i + 5;
		v_child.value := 'hello world ' || v_child.id;
		:v_res := v_child;
	end;`,
		sql.Named(`i`, i),
		sql.Named(`v_res`, sql.Out{In: false, Dest: &c}),
	)
	fmt.Println("result", i, c, err)
}

func main() {
	fmt.Println("Hello world")
	lvl := new(slog.LevelVar)
	lvl.Set(slog.LevelDebug)

	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
		Level:     lvl,
		AddSource: true,
	}))

	godror.SetLogger(logger)
	db := connect()
	defer db.Close()

	for i := 0; i < 5; i++ {
		testConnectInClob(db, float64(i))
	}

	for i := 0; i < 5; i++ {
		testConnectOutClob(db, float64(i))
	}
	fmt.Scanf(" ")
}

Expected behavior
All test OK - if use variable type Object with field type of clob, session closed and pool can reuse for new connection.
For this need add code:
lob.go
func (dlr *dpiLobReader) Read(p []byte) (int, error)

defer dlr.Close()   // this add
return dlr.bufR, err

Error output
time=2023-12-03T15:42:03.258+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/conn.go:1117 msg="ResetSession re-acquire session" pool="gin2\tfc44cd87\tfsgdev\t1\t3\t1\t2s\t30s\t10s\tfalse\tfalse\tfalse\tUTC\t0\t0s"
result 3 0 pool=0x1d074d176a0 stats=busy=3 open=3 max=3 maxLifetime=30s timeout=10s waitTimeout=5s params={authMode:0 connectionClass:0x1d07bb80690 connectionClassLength:6 purity:0 newPassword: newPasswordLength:0 appContext: numAppContext:0 externalAuth:0 externalHandle: pool:0x1d074d176a0 tag: tagLength:0 matchAnyTag:0 outTag: outTagLength:0 outTagFound:0 shardingKeyColumns: numShardingKeyColumns:0 superShardingKeyColumns: numSuperShardingKeyColumns:0 outNewSession:0}: ORA-24496: OCISessionGet() timed out waiting for a free connection.

start Out clob 4
result 4 0 pool=0x1d074d176a0 stats=busy=3 open=3 max=3 maxLifetime=30s timeout=10s waitTimeout=5s params={authMode:0 connectionClass:0x1d07bb806b0 connectionClassLength:6 purity:0 newPassword: newPasswordLength:0 appContext: numAppContext:0 externalAuth:0 externalHandle: pool:0x1d074d176a0 tag: tagLength:0 matchAnyTag:0 outTag: outTagLength:0 outTagFound:0 shardingKeyColumns: numShardingKeyColumns:0 superShardingKeyColumns: numSuperShardingKeyColumns:0 outNewSession:0}: ORA-24496: OCISessionGet() timed out waiting for a free connection.

Your oracle client version
e.g. 12.2.0

Your godror version
e.g. v0.40.4

Your go version
e.g. 1.21

Your gcc version
e.g. 10.3.0

Machine (please complete the following information):

OS: windows
Architecture: x86_64
Version: 10

@bikain
Copy link
Author

bikain commented Dec 3, 2023

For fix, minimun need add code:
lob.go
func (dlr *dpiLobReader) Read(p []byte) (int, error)

defer dlr.Close()   // this add
return dlr.bufR, err

@tgulacsi
Copy link
Contributor

tgulacsi commented Dec 3, 2023

The suggested fix will not work, as more than one Read may be required.

I cannot reproduce the error.

@bikain
Copy link
Author

bikain commented Dec 3, 2023

How to reproduce two(ore more) read?
Now i test type with two field Clob. all ok with this fix.

create or replace type typeClob as object(
  id number,
  value clob,
  info clob
)

@bikain
Copy link
Author

bikain commented Dec 4, 2023

I reproduced multiple reading: i make in database clob 40000+ length.
I add to logger save caller Read:

func (dlr *dpiLobReader) Read(p []byte) (int, error) {
	dlr.mu.Lock()
	defer dlr.mu.Unlock()
	logger := getLogger(context.TODO())
	_, file, line, _ := runtime.Caller(1)

And all ok. All clob save. And in log i see:

time=2023-12-04T09:14:42.402+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:171 msg="lob caller" Read="C:/Program Files/Go/src/bytes/buffer.go:211"
time=2023-12-04T09:14:42.403+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:173 msg=Read bufR=512 bufW=40013 buf=1048000
time=2023-12-04T09:14:42.404+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:171 msg="lob caller" Read="C:/Program Files/Go/src/bytes/buffer.go:211"
time=2023-12-04T09:14:42.405+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:173 msg=Read bufR=1024 bufW=40013 buf=1048000
time=2023-12-04T09:14:42.411+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:171 msg="lob caller" Read="C:/Program Files/Go/src/bytes/buffer.go:211"
time=2023-12-04T09:14:42.413+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:173 msg=Read bufR=2048 bufW=40013 buf=1048000
time=2023-12-04T09:14:42.417+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:171 msg="lob caller" Read="C:/Program Files/Go/src/bytes/buffer.go:211"
time=2023-12-04T09:14:42.418+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:173 msg=Read bufR=4096 bufW=40013 buf=1048000
time=2023-12-04T09:14:42.419+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:171 msg="lob caller" Read="C:/Program Files/Go/src/bytes/buffer.go:211"
time=2023-12-04T09:14:42.420+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:173 msg=Read bufR=8192 bufW=40013 buf=1048000
time=2023-12-04T09:14:42.429+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:171 msg="lob caller" Read="C:/Program Files/Go/src/bytes/buffer.go:211"
time=2023-12-04T09:14:42.430+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:173 msg=Read bufR=16384 bufW=40013 buf=1048000
time=2023-12-04T09:14:42.430+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:171 msg="lob caller" Read="C:/Program Files/Go/src/bytes/buffer.go:211"
time=2023-12-04T09:14:42.431+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:173 msg=Read bufR=32768 bufW=40013 buf=1048000
time=2023-12-04T09:14:42.432+07:00 level=DEBUG source=C:/Temp_folder/golang/OracleNew/godror-main/lob.go:171 msg="lob caller" Read="C:/Program Files/Go/src/bytes/buffer.go:211"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants