Golang中database/sql的varchar2表与[]string不匹配问题
Golang中database/sql的varchar2表与[]string不匹配问题 在 Oracle 中,我有这种类型
create or replace TYPE TYPE_SLICE
AS TABLE OF varchar2(4000);
在 Golang 中,我使用的是 https://github.com/sijms/go-ora 这个库。
我运行一个存储过程,它有一个类型为 slice 的 OUT 参数
CREATE OR REPLACE PROCEDURE PR_GO_SLICE(
text1 IN VARCHAR2,
text2 IN VARCHAR2,
slice_out OUT TYPE_SLICE
)AS
example_slice TYPE_SLICE;
BEGIN
example_slice := TYPE_SLICE();
example_slice.extend;
example_slice.extend;
example_slice (example_slice.LAST) := text1;
example_slice (example_slice.LAST) := text2;
example_slice(1) := 'test1';
example_slice(2) := 'test2';
END PR_GO_SLICE;
Go 代码相当直接
func test_slice(db *sql.DB) {
var (
text1 string
text2 string
slice_out []string
)
text1 = `hello`
text2 = `world`
// prepare a callable statement
cstmt, err := db.Prepare("BEGIN PR_GO_SLICE(:1, :2, :3); END;")
if err != nil {
fmt.Println("prepare:" + err.Error())
panic(err)
}
defer cstmt.Close()
_, err = cstmt.Exec(text1, text2, sql.Out{Dest: &slice_out})
if err != nil {
fmt.Println("exec: " + err.Error())
panic(err)
}
fmt.Printf("text1 : %v#\ntext2 : %#v\nslice_out:%#v\n", text1, text2, slice_out)
return
}
但我从 Oracle 得到了错误
ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'PR_GO_SLICE'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
这似乎是因为从 Golang 发送到 Oracle 的 TYPE_SLICE 数据类型不正确。
深入研究这个库,作为参数发送的值实际上在这个函数中,即 Bytes()
//github.com/sijms/go-ora/v2/parameter_encode_array.go - LOC 278
func (par *ParameterInfo) encodeArrayString(conn *Connection, value []string) {
par.DataType = NCHAR
par.ContFlag = 16
session := conn.session
arrayBuffer := bytes.Buffer{}
session.WriteUint(&arrayBuffer, par.MaxNoOfArrayElements, 4, true, true)
if len(value) > 0 {
for _, tempVal := range value {
strConv, _ := conn.getStrConv(par.CharsetID)
tempBytes := strConv.Encode(tempVal)
session.WriteClr(&arrayBuffer, tempBytes)
if par.MaxLen < len(tempBytes) {
par.MaxLen = len(tempBytes)
}
}
par.BValue = arrayBuffer.Bytes()
if par.MaxLen == 0 {
par.MaxLen = 1
par.MaxCharLen = 0
}
} else {
par.MaxLen = conn.maxLen.varchar
par.MaxCharLen = par.MaxLen / converters.MaxBytePerChar(par.CharsetID)
}
}
有没有办法在 Golang 中适配 TABLE OF VARCHAR2 这种数据结构?基于 driver.Value,我认为它应该是 []string 吗?还是其他什么?
此致
更多关于Golang中database/sql的varchar2表与[]string不匹配问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
根据您提供的代码和错误信息,问题似乎在于Go代码如何将TYPE_SLICE参数传递给Oracle存储过程。
在Go代码中,您将slice_out定义为[]string,这是正确的。然而,您正在使用的库(go-ora)内部处理数组值到Oracle的编码和传递,而看起来TYPE_SLICE的编码处理不正确。
为了在Go中适应TABLE OF VARCHAR2数据结构,您可以尝试以下方法:
- 修改库源代码(github.com/sijms/go-ora/v2/parameter_encode_array.go)中的
encodeArrayString函数,以正确处理TYPE_SLICE数据类型。您可能需要为TYPE_SLICE实现自定义编码逻辑,以匹配Oracle中预期的格式。 - 或者,您可以尝试使用另一个为传递数组类型提供更好支持的Go Oracle驱动。一些流行的选项包括“GitHub - mattn/go-oci8: Oracle driver for Go using database/sql”和“GitHub - godror/godror: GO DRiver for ORacle DB”。这些驱动可能为Oracle数据类型(包括数组)提供更广泛的支持。
在对库进行任何更改或尝试其他驱动之前,重要的是查阅您正在使用的库或驱动提供的文档和示例,看看它们是否提供了关于在Oracle中使用数组类型的特定指导。
请记住,彻底测试任何修改或替代驱动,以确保与您的特定用例的兼容性和正确性。
更多关于Golang中database/sql的varchar2表与[]string不匹配问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在 Oracle 中处理自定义类型 TYPE_SLICE 时,不能直接使用 []string 作为参数。go-ora 库需要特定的类型来处理 Oracle 的集合类型。以下是正确的实现方式:
import (
"database/sql"
"fmt"
"github.com/sijms/go-ora/v2"
)
func test_slice(db *sql.DB) {
var (
text1 string
text2 string
slice_out go_ora.Out
)
text1 = "hello"
text2 = "world"
// 创建 Oracle 集合类型
collection := go_ora.NewCollection("TYPE_SLICE", db)
defer collection.Close()
slice_out = go_ora.Out{Dest: &collection}
cstmt, err := db.Prepare("BEGIN PR_GO_SLICE(:1, :2, :3); END;")
if err != nil {
fmt.Println("prepare:" + err.Error())
panic(err)
}
defer cstmt.Close()
_, err = cstmt.Exec(
go_ora.Out{Dest: &text1, In: true},
go_ora.Out{Dest: &text2, In: true},
slice_out,
)
if err != nil {
fmt.Println("exec: " + err.Error())
panic(err)
}
// 从集合中获取数据
var results []string
for i := 0; i < collection.Len(); i++ {
val, _ := collection.Get(i)
results = append(results, val.(string))
}
fmt.Printf("text1: %v\ntext2: %v\nslice_out: %#v\n", text1, text2, results)
}
或者使用 go-ora 的专用类型:
import (
"context"
"fmt"
"github.com/sijms/go-ora/v2"
)
func test_slice_ora() {
url := go_ora.BuildUrl("server", 1521, "service", "user", "password", nil)
conn, err := go_ora.NewConnection(url)
if err != nil {
panic(err)
}
err = conn.Open()
if err != nil {
panic(err)
}
defer conn.Close()
stmt := go_ora.NewStmt("BEGIN PR_GO_SLICE(:1, :2, :3); END;", conn)
defer stmt.Close()
text1 := "hello"
text2 := "world"
// 创建输出参数
sliceParam := go_ora.Parameter{
Direction: go_ora.Output,
Type: go_ora.RefCursor, // 或者使用适当的类型
}
stmt.AddParam(":1", &text1, 100, go_ora.Input)
stmt.AddParam(":2", &text2, 100, go_ora.Input)
stmt.AddParam(":3", &sliceParam, 0, go_ora.Output)
_, err = stmt.Exec(nil)
if err != nil {
panic(err)
}
fmt.Printf("Results: %v\n", sliceParam.Value)
}
对于 database/sql 接口,需要实现自定义的 Valuer 接口:
import (
"database/sql"
"database/sql/driver"
"fmt"
)
type OracleSlice []string
func (os OracleSlice) Value() (driver.Value, error) {
// 实现将 []string 转换为 Oracle 集合类型的逻辑
return os, nil
}
func test_slice_custom(db *sql.DB) {
text1 := "hello"
text2 := "world"
var sliceOut OracleSlice
cstmt, err := db.Prepare("BEGIN PR_GO_SLICE(:1, :2, :3); END;")
if err != nil {
panic(err)
}
defer cstmt.Close()
// 使用 sql.Out 包装自定义类型
_, err = cstmt.Exec(
sql.Named("text1", text1),
sql.Named("text2", text2),
sql.Named("slice_out", sql.Out{Dest: &sliceOut}),
)
if err != nil {
panic(err)
}
fmt.Printf("slice_out: %#v\n", sliceOut)
}
关键点:
- Oracle 自定义类型需要特殊的处理方式
go-ora库提供了Collection类型来处理 Oracle 集合- 不能直接使用
[]string作为TYPE_SLICE的参数类型 - 需要使用库提供的特定类型或实现自定义的
driver.Valuer接口

