gin-base/database/drivers/oracle/oracle_do_exec.go

121 lines
3.2 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package oracle
import (
"context"
"database/sql"
"fmt"
"strings"
"git.magicany.cc/black1552/gin-base/database"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
const (
returningClause = " RETURNING %s INTO ?"
)
// DoExec commits the sql string and its arguments to underlying driver
// through given link object and returns the execution result.
// It handles INSERT statements specially to support LastInsertId.
func (d *Driver) DoExec(
ctx context.Context, link database.Link, sql string, args ...interface{},
) (result sql.Result, err error) {
var (
isUseCoreDoExec = true
primaryKey string
pkField database.TableField
)
// Transaction checks.
if link == nil {
if tx := database.TXFromCtx(ctx, d.GetGroup()); tx != nil {
link = tx
} else if link, err = d.MasterLink(); err != nil {
return nil, err
}
} else if !link.IsTransaction() {
if tx := database.TXFromCtx(ctx, d.GetGroup()); tx != nil {
link = tx
}
}
// Check if it is an insert operation with primary key from context.
if value := ctx.Value(internalPrimaryKeyInCtx); value != nil {
if field, ok := value.(database.TableField); ok {
pkField = field
isUseCoreDoExec = false
}
}
// Check if it is an INSERT statement with primary key.
if !isUseCoreDoExec && pkField.Name != "" && strings.Contains(strings.ToUpper(sql), "INSERT INTO") {
primaryKey = pkField.Name
// Oracle supports RETURNING clause to get the last inserted id
sql += fmt.Sprintf(returningClause, d.QuoteWord(primaryKey))
} else {
// Use default DoExec for non-INSERT or no primary key scenarios
return d.Core.DoExec(ctx, link, sql, args...)
}
// Only the insert operation with primary key can execute the following code
// SQL filtering.
sql, args = d.FormatSqlBeforeExecuting(sql, args)
sql, args, err = d.DoFilter(ctx, link, sql, args)
if err != nil {
return nil, err
}
// Prepare output variable for RETURNING clause
var lastInsertId int64
// Append the output parameter for the RETURNING clause
args = append(args, &lastInsertId)
// Link execution.
_, err = d.DoCommit(ctx, database.DoCommitInput{
Link: link,
Sql: sql,
Args: args,
Stmt: nil,
Type: database.SqlTypeExecContext,
IsTransaction: link.IsTransaction(),
})
if err != nil {
return &Result{
lastInsertId: 0,
rowsAffected: 0,
lastInsertIdError: err,
}, err
}
// Get rows affected from the result
// For single insert with RETURNING clause, affected is always 1
var affected int64 = 1
// Check if the primary key field type supports LastInsertId
if !strings.Contains(strings.ToLower(pkField.Type), "int") {
return &Result{
lastInsertId: 0,
rowsAffected: affected,
lastInsertIdError: gerror.NewCodef(
gcode.CodeNotSupported,
"LastInsertId is not supported by primary key type: %s",
pkField.Type,
),
}, nil
}
return &Result{
lastInsertId: lastInsertId,
rowsAffected: affected,
}, nil
}