Files
MyGoNavi/third_party/go-irisnative/driver.go
Syngnat 992d2dee45 feat(iris): 新增 InterSystems IRIS 数据源支持
- 后端新增 IRIS 连接、查询、DDL、索引元数据和 DataGrid 编辑能力
- 接入 optional driver-agent、构建标签、revision 生成和变更检测流程
- 前端新增 IRIS 连接入口、方言映射、能力配置和图标展示
- 修复 IRIS 主键识别、事务开启错误处理和驱动连接关闭问题
- 补充后端、前端和构建脚本相关回归测试
Refs #408
2026-05-17 10:32:08 +08:00

217 lines
4.0 KiB
Go

package intersystems
import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"net"
"unicode"
_ "io"
_ "math"
_ "reflect"
_ "strconv"
_ "strings"
_ "time"
_ "unsafe"
"github.com/caretdev/go-irisnative/src/connection"
)
var (
ErrCouldNotDetectUsername = errors.New("intersystems: Could not detect default username. Please provide one explicitly")
)
var (
_ driver.Driver = Driver{}
)
type values map[string]string
// Driver implements database/sql/driver.Driver.
type Driver struct{}
func (d Driver) Open(name string) (driver.Conn, error) {
return Open(name)
}
func init() {
sql.Register("intersystems", &Driver{})
sql.Register("iris", &Driver{})
}
func Open(dsn string) (_ driver.Conn, err error) {
c, err := NewConnector(dsn)
if err != nil {
return nil, err
}
return c.open(context.Background())
}
type conn struct {
c connection.Connection
tx bool
}
func (c *Connector) open(ctx context.Context) (cn *conn, err error) {
o := make(values)
for k, v := range c.opts {
o[k] = v
}
host := o["host"]
addr := net.JoinHostPort(host, o["port"])
namespace := o["namespace"]
login := o["user"]
password := o["password"]
cn = &conn{}
cn.c, err = connection.Connect(addr, namespace, login, password)
if err != nil {
return nil, err
}
return cn, nil
}
func (cn *conn) Begin() (driver.Tx, error) {
return cn.c.BeginTx(driver.TxOptions{})
}
func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
return cn.c.BeginTx(opts)
}
func (cn *conn) Close() (err error) {
cn.c.Disconnect()
return nil
}
func (cn *conn) Prepare(q string) (st driver.Stmt, err error) {
return cn.c.Prepare(q)
}
func (cn *conn) Commit() error {
if !cn.tx {
panic("transaction already closed")
}
cn.tx = false
cn.c.Commit()
return nil
}
func (cn *conn) Rollback() error {
if !cn.tx {
panic("transaction already closed")
}
cn.tx = false
cn.c.Rollback()
return nil
}
func (cn *conn) Exec(query string, args []driver.NamedValue) (res driver.Result, err error) {
parameters := make([]interface{}, len(args))
for i, a := range args {
parameters[i] = a
}
_, err = cn.c.DirectUpdate(query, parameters...)
if err != nil {
return nil, err
}
return res, nil
}
func (cn *conn) Query(query string, args []driver.NamedValue) (rows driver.Rows, err error) {
parameters := make([]interface{}, len(args))
for i, a := range args {
parameters[i] = a
}
// var rs *connection.ResultSet
_, err = cn.c.Query(query, parameters...)
if err != nil {
return nil, err
}
// rows = &connection.Rows{
// cn: cn.c,
// rs: rs,
// }
return
}
func parseOpts(name string, o values) error {
s := newScanner(name)
for {
var (
keyRunes, valRunes []rune
r rune
ok bool
)
if r, ok = s.SkipSpaces(); !ok {
break
}
// Scan the key
for !unicode.IsSpace(r) && r != '=' {
keyRunes = append(keyRunes, r)
if r, ok = s.Next(); !ok {
break
}
}
// Skip any whitespace if we're not at the = yet
if r != '=' {
r, ok = s.SkipSpaces()
}
// The current character should be =
if r != '=' || !ok {
return fmt.Errorf(`missing "=" after %q in connection info string"`, string(keyRunes))
}
// Skip any whitespace after the =
if r, ok = s.SkipSpaces(); !ok {
// If we reach the end here, the last value is just an empty string as per libpq.
o[string(keyRunes)] = ""
break
}
if r != '\'' {
for !unicode.IsSpace(r) {
if r == '\\' {
if r, ok = s.Next(); !ok {
return fmt.Errorf(`missing character after backslash`)
}
}
valRunes = append(valRunes, r)
if r, ok = s.Next(); !ok {
break
}
}
} else {
quote:
for {
if r, ok = s.Next(); !ok {
return fmt.Errorf(`unterminated quoted string literal in connection string`)
}
switch r {
case '\'':
break quote
case '\\':
r, _ = s.Next()
fallthrough
default:
valRunes = append(valRunes, r)
}
}
}
o[string(keyRunes)] = string(valRunes)
}
return nil
}