From 7ca0fcebbcf9f489bfb4599761d847cf79b227e6 Mon Sep 17 00:00:00 2001 From: hmmftg Date: Tue, 8 Aug 2023 12:42:57 +0330 Subject: [PATCH] hide DBOutput to decrease complexity --- dbms/output.go | 76 +++++++++++++++++++++++++++++--- examples/dbms_output/main.go | 84 ++++++++++++++++++++++++++++-------- 2 files changed, 136 insertions(+), 24 deletions(-) diff --git a/dbms/output.go b/dbms/output.go index c940aa30..f93714a2 100644 --- a/dbms/output.go +++ b/dbms/output.go @@ -1,9 +1,12 @@ package dbms import ( + "context" "database/sql" - go_ora "github.com/sijms/go-ora/v2" + "fmt" "io" + + go_ora "github.com/sijms/go-ora/v2" ) type DBOutput struct { @@ -11,17 +14,78 @@ type DBOutput struct { conn *sql.DB } +const ( + MaxBufferSize = 0x7FFF + MinBufferSize = 2000 + KeyInContext = "GO-ORA.DBMS_OUTPUT" +) + +// enable oracle output for current session +// param: +// +// ctx: context of goroutine used in large apps +// for main: context.Background() +// for rest apis: +// http.Request.Context() +// gin.Context +// fiber.Ctx.Context() +// ... +func EnableOutput(ctx context.Context, conn *sql.DB) error { + out, err := NewOutput(conn, MaxBufferSize) + if err != nil { + return err + } + context.WithValue(ctx, KeyInContext, out) + return nil +} + +// disable oracle output for current session +func DisableOutput(ctx context.Context) error { + out := ctx.Value(KeyInContext) + if out == nil { + return fmt.Errorf("invalid context") + } + err := out.(*DBOutput).Close() + if err != nil { + return err + } + return nil +} + +// get oracle output for current session +func GetOutput(ctx context.Context) (string, error) { + out := ctx.Value(KeyInContext) + if out == nil { + return "", fmt.Errorf("invalid context") + } + output, err := out.(*DBOutput).GetOutput() + if err != nil { + return "", err + } + return output, nil +} + +// print oracle output into StringWriter for current session +func PrintOutput(ctx context.Context, w io.StringWriter) error { + output, err := GetOutput(ctx) + if err != nil { + return err + } + _, err = w.WriteString(output) + return err +} + func NewOutput(conn *sql.DB, bufferSize int) (*DBOutput, error) { output := &DBOutput{ bufferSize: bufferSize, conn: conn, } sqlText := `begin dbms_output.enable(:1); end;` - if output.bufferSize > 0x7FFF { - output.bufferSize = 0x7FFF + if output.bufferSize > MaxBufferSize { + output.bufferSize = MaxBufferSize } - if output.bufferSize < 2000 { - output.bufferSize = 2000 + if output.bufferSize < MinBufferSize { + output.bufferSize = MinBufferSize } _, err := output.conn.Exec(sqlText, bufferSize) return output, err @@ -55,7 +119,7 @@ end;` state int output string ) - _, err := db_out.conn.Exec(sqlText, 0x7FFF, go_ora.Out{Dest: &state}, + _, err := db_out.conn.Exec(sqlText, MaxBufferSize, go_ora.Out{Dest: &state}, go_ora.Out{Dest: &output, Size: db_out.bufferSize}) return output, err } diff --git a/examples/dbms_output/main.go b/examples/dbms_output/main.go index 4673522d..2ab51c89 100644 --- a/examples/dbms_output/main.go +++ b/examples/dbms_output/main.go @@ -1,11 +1,13 @@ package main import ( + "context" "database/sql" "fmt" + "os" + "github.com/sijms/go-ora/dbms" _ "github.com/sijms/go-ora/v2" - "os" ) func exec_simple_conn(conn *sql.DB, texts ...string) error { @@ -18,53 +20,99 @@ func exec_simple_conn(conn *sql.DB, texts ...string) error { } return err } -func main() { - conn, err := sql.Open("oracle", os.Getenv("DSN")) + +func addOutput(conn *sql.DB, data string) error { + return exec_simple_conn(conn, fmt.Sprintf(`--sql + BEGIN + DBMS_OUTPUT.PUT_LINE('%s'); + END; + `, data)) +} + +func withoutContext(conn *sql.DB) { + output, err := dbms.NewOutput(conn, 0x7FFF) if err != nil { - fmt.Println("error in connection: ", err) + fmt.Println("can't init DBMS_OUTPUT: ", err) return } defer func() { - err = conn.Close() + err = output.Close() if err != nil { - fmt.Println("error in close: ", err) + fmt.Println("can't end dbms_output: ", err) } }() - output, err := dbms.NewOutput(conn, 0x7FFF) + err = addOutput(conn, "test1") + if err != nil { + fmt.Println("can't write output: ", err) + return + } + line, err := output.GetOutput() + if err != nil { + fmt.Println("can't get output: ", err) + return + } + fmt.Print(line) + err = addOutput(conn, "test2") + if err != nil { + fmt.Println("can't write output: ", err) + return + } + err = output.Print(os.Stdout) + if err != nil { + fmt.Println("can't print: ", err) + return + } +} + +func withContext(conn *sql.DB, ctx context.Context) { + err := dbms.EnableOutput(ctx, conn) if err != nil { fmt.Println("can't init DBMS_OUTPUT: ", err) return } defer func() { - err = output.Close() + err = dbms.DisableOutput(ctx) if err != nil { fmt.Println("can't end dbms_output: ", err) } }() - err = exec_simple_conn(conn, `BEGIN -DBMS_OUTPUT.PUT_LINE('this is a test'); -END;`) + err = addOutput(conn, "test1") if err != nil { fmt.Println("can't write output: ", err) return } - line, err := output.GetOutput() + output, err := dbms.GetOutput(ctx) if err != nil { fmt.Println("can't get output: ", err) return } - fmt.Print(line) - err = exec_simple_conn(conn, `BEGIN -DBMS_OUTPUT.PUT_LINE('this is a test2'); -END;`) + fmt.Print(output) + err = addOutput(conn, "test2") if err != nil { fmt.Println("can't write output: ", err) return } - err = output.Print(os.Stdout) + err = dbms.PrintOutput(ctx, os.Stdout) if err != nil { - fmt.Println("can't print: ", err) + fmt.Println("can't print output: ", err) return } +} + +func main() { + conn, err := sql.Open("oracle", os.Getenv("DSN")) + if err != nil { + fmt.Println("error in connection: ", err) + return + } + defer func() { + err = conn.Close() + if err != nil { + fmt.Println("error in close: ", err) + } + }() + withoutContext(conn) + ctx := context.Background() + withContext(conn, ctx) }