Golang godror - execute a PL/SQL Cursor - oracle

I'm trying to retrieve cursor result of a PL/SQL function in golang with godror.
https://github.com/godror/godror
Using cursors returned by stored procedures
Use ExecContext and an interface{} or a database/sql/driver.Rows as the sql.Out destination, then either use the driver.Rows interface, or transform it into a regular *sql.Rows with godror.WrapRows, or (since Go 1.12) just Scan into *sql.Rows.
db, err := sql.Open("godror", "api/user#localhost/DEV")
if err != nil {
fmt.Println(err)
panic(err)
}
defer db.Close()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
const query = `
DECLARE
BEGIN
:cursor := pkg_api.my_func(pstr_logn_nm => 'my_name');
END;
`
stmt, err := db.PrepareContext(ctx, query)
if err != nil {
fmt.Println(err)
panic(err)
}
var rows driver.Rows
_, err = stmt.ExecContext(ctx, sql.Out{Dest: &rows})
if err != nil {
fmt.Println(err)
}
var r []driver.Value
err = rows.Next(r)
if err != nil {
fmt.Println(err) // column count mismatch: we have 10 columns, but given 0 destination
}
defer rows.Close()
fmt.Println(rows.Columns()) // [COL_1 COL_2 COL_3 COL_4 COL_5 COL_6 COL_7 COL_8 COL_9 COL_10]
I have an error: column count mismatch: we have 10 columns, but given 0 destination
To my point of view it's because I have to define an interface with some columns.
If I try to change driver.Rows by sql.Rows I have this error:
arg: unknown type sql.Rows
If I try to create an interface I:
type I interface {
NM() string
}
var r []I
err = rows.Next(r)
I have this error: cannot use r (type []I) as type []driver.Value in argument to rows.Next
I'm also new in Golang, if someone has an idea :)
Thanks a lot!

This solved my problem!
r := make([]driver.Value, len(rows.Columns()))
err = rows.Next(r)
if err != nil {
fmt.Println(err) // column count mismatch: we have 10 columns, but given 0 destination
}
defer rows.Close()
https://github.com/godror/godror/issues/62

Related

Event data from Smart Contract parsing issue with go-ethereum

I am unable to get the full data emitted from SC event by using types.Log channel. Is there any way so that I can have all the data from an event emitted?
The event I'm trying to parse:
PairCreated(address indexed,address indexed,address,uint)
My code:
for {
select {
case err := <-sub.Err():
log.Fatal(err)
case vLog := <-logs:
fmt.Printf("Log Block Number: %d\n", vLog.BlockNumber)
fmt.Printf("Log Index: %d\n", vLog.Index)
event := make(map[string]interface{})
err := contractAbi.UnpackIntoMap(event, "PairCreated", vLog.Data)
if err != nil {
log.Fatal(err)
}
fmt.Println(event)
}
}
I could only parse the last two arguments of the event.
I understood what was wrong here.
If an argument is declared as indexed that argument goes to Topics instead of Data. And there can be at most 3 topics. So, I tried to unpack the topics but failed. And succeeded with the following way:
token1 := common.HexToAddress(vLog.Topics[1].Hex())
token2 := common.HexToAddress(vLog.Topics[2].Hex())
And pair was in Data
So, the final code is:
for {
select {
case err := <-sub.Err():
log.Fatal(err)
case vLog := <-logs:
fmt.Printf("Log Block Number: %d\n", vLog.BlockNumber)
fmt.Printf("Log Index: %d\n", vLog.Index)
event := make(map[string]interface{})
err := contractAbi.UnpackIntoMap(event, "PairCreated", vLog.Data)
if err != nil {
log.Fatal(err)
}
fmt.Println(event)
token1 := common.HexToAddress(vLog.Topics[1].Hex())
token2 := common.HexToAddress(vLog.Topics[2].Hex())
}
}

How to update multiple rows using sqlx?

I'd like to update multiple rows in a single query:
//threadIDs is a variable length slice of integers like [3 5 6]
query := "UPDATE message SET recipient_deleted=? WHERE recipient_id=? AND thread_id IN ?"
_, err := database.SQL.Exec( query, 1, userID, threadIDs)
if err != nil {
log.Fatal(err)
}
But I get this runtime error:
sql: converting argument $3 type: unsupported type []int, a slice of int
How can I fix this?
You can use sqlx.In which returns a new query and a new args slice that you then use instead of your original values, like so:
query := "UPDATE message SET recipient_deleted=? WHERE recipient_id=? AND thread_id IN (?)"
qry, args, err := sqlx.In(query, 1, userID, threadIDs)
if err != nil {
panic(err)
}
if _, err := database.SQL.Exec(qry, args...); err != nil {
panic(err)
}

How to repeatedly call a function for each iteration in a loop, get its results then append the results into a slice (Golang?

I have the ff:
func getSlice(distinctSymbols []string) []symbols {
// Prepare connection
stmt1, err := db.Prepare("Select count(*) from stockticker_daily where symbol = $1;")
checkError(err)
defer stmt1.Close()
stmt2, err := db.Prepare("Select date from stockticker_daily where symbol = $1 order by date asc limit 1;")
checkError(err)
defer stmt2.Close()
var symbolsSlice []symbols
c := make(chan symbols)
for _, symbol := range distinctSymbols {
go worker(symbol, stmt1, stmt2, c)
**symbolsFromChannel := <-c**
**symbolsSlice = append(symbolsSlice, symbolsFromChannel})**
}
return symbolsSlice
}
func worker(symbol string, stmt1 *sql.Stmt, stmt2 *sql.Stmt, symbolsChan chan symbols) {
var countdp int
var earliestdate string
row := stmt1.QueryRow(symbol)
if err := row.Scan(&countdp); err != nil {
log.Fatal(err)
}
row = stmt2.QueryRow(symbol)
if err := row.Scan(&earliestdate); err != nil {
log.Fatal(err)
}
symbolsChan <- symbols{symbol, countdp, earliestdate}
}
Please take a look at the first function, I know it won't work as I expect since the line symbolsFromChannel := <-c will block until it receives from the channel, so the iteration on the goroutine go worker will not continue unless the block is removed. What is the best or correct way to do that?
Just do the loop twice, e.g.
for _, symbol := range distinctSymbols {
go worker(symbol, stmt1, stmt2, c)
}
for range distinctSymbols {
symbolsSlice = append(symbolsSlice, <-c)
}

Query for an integer array from PostreSQL always returns []uint8

Take a simple PostreSQL db with an integer array:
CREATE TABLE foo (
id serial PRIMARY KEY,
bar integer[]
);
INSERT INTO foo VALUES(DEFAULT, '{1234567, 20, 30, 40}');
Using pq, these values are for some reason being retrieved as an array of []uint8.
The documentation says that integer types are returned as int64. Does this not apply to arrays as well?
db, err := sql.Open("postgres", "user=a_user password=your_pwd dbname=blah")
if err != nil {
fmt.Println(err)
}
var ret []int
err = db.QueryRow("SELECT bar FROM foo WHERE id=$1", 1).Scan(&ret)
if err != nil {
fmt.Println(err)
}
fmt.Println(ret)
Output:
sql: Scan error on column index 0: unsupported Scan, storing driver.Value type []uint8 into type *[]int64
[]
You cannot use a slice of int as a driver.Value. The arguments to Scan must be of one of the supported types, or implement the sql.Scanner interface.
The reason you're seeing []uint8 in the error message is that the raw value returned from the database is a []byte slice, for which []uint8 is a synonym.
To interpret that []byte slice appropriately as a custom PostgreSQL array type, you should use the appropriate array types defined in the pq package, such as the Int64Array.
Try something like this:
var ret pq.Int64Array
err = db.QueryRow("SELECT bar FROM foo WHERE id=$1", 1).Scan(&ret)
if err != nil {
fmt.Println(err)
}
fmt.Println(ret)
The problem will be more severe if you use fetching multiple rows.
The above code works for a single row, to fetch multiple rows use like this
`rows, err := db.QueryContext(ctx, stmt, courseCode)
if err != nil {
return nil, err
}
defer rows.Close()
var feedbacks []*Feedback1
for rows.Next() {
var feedback Feedback1
var ret pq.Int64Array
var ret1 pq.Int64Array
err := rows.Scan(
&feedback.ID,
&ret,
&ret1,
)
if err != nil {
return nil, err
}
//for loop to convert int64 to int
for i:=0;i<len(ret);i++{
feedback.UnitFeedback = append(feedback.UnitFeedback,int(ret[i]))}
for i:=0;i<len(ret1);i++{
feedback.GeneralFeedback = append(feedback.GeneralFeedback,int(ret1[i]))}
feedbacks = append(feedbacks, &feedback)
}`

Go Using db.Query to return more than one column

In the Go SQL docs they give an example here of a query that only returns 1 column (poor example in my opinion, at least return 2...)
age := 27
rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
The docs state here that
Scan copies the columns in the current row into the values pointed at by dest.
How does this work with a struct, lets say I have a struct
type User struct{
Name string
Age int
}
and I modify my query to SELECT name, age from users where age=?
How do I unpack *Rows into my struct? I did find this example, but it didn't deal with structs. I will be following Active Record pattern conventions so my structs will map to my database via snake case conversion.
Looking at the source, it seems the copy is done with ... syntax on destination pointers:
func (rs *Rows) Scan(dest ...interface{}) error
So in your example, you can do for instance:
for rows.Next() {
u := User{} // An empty user
...
if err := rows.Scan(&u.Name, &u.Age); err != nil {
...
}
}
As long as you pass the exact number of pointers, this should work, whether they are from a struct or not.

Resources