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

2 回复

根据您提供的代码和错误信息,问题似乎在于Go代码如何将TYPE_SLICE参数传递给Oracle存储过程。

在Go代码中,您将slice_out定义为[]string,这是正确的。然而,您正在使用的库(go-ora)内部处理数组值到Oracle的编码和传递,而看起来TYPE_SLICE的编码处理不正确。

为了在Go中适应TABLE OF VARCHAR2数据结构,您可以尝试以下方法:

  1. 修改库源代码(github.com/sijms/go-ora/v2/parameter_encode_array.go)中的encodeArrayString函数,以正确处理TYPE_SLICE数据类型。您可能需要为TYPE_SLICE实现自定义编码逻辑,以匹配Oracle中预期的格式。
  2. 或者,您可以尝试使用另一个为传递数组类型提供更好支持的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)
}

关键点:

  1. Oracle 自定义类型需要特殊的处理方式
  2. go-ora 库提供了 Collection 类型来处理 Oracle 集合
  3. 不能直接使用 []string 作为 TYPE_SLICE 的参数类型
  4. 需要使用库提供的特定类型或实现自定义的 driver.Valuer 接口
回到顶部