暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

使用golang完成一个简易的oracle客户端

数据库驾驶舱 2024-08-14
83

此客户端不依赖oracle库文件,但是只能在linux上编译,windows上gcc环境无法适配

package main

import (
 "database/sql"
 "flag"
 "fmt"
 "log"
 "strings"
 "syscall"

 _ "github.com/sijms/go-ora/v2"
 "golang.org/x/crypto/ssh/terminal"
)

// 全局变量定义
var (
 username = flag.String("u""""数据库用户名")
 password = flag.String("p""""数据库密码")
 dsn      = flag.String("dsn""""Oracle DSN (数据源名称) 例如 192.168.1.100:1521/orcl")
 execSQL  = flag.String("e""select * from dual""要执行的 SQL 语句")
)

// 初始化数据库连接
func initDB() (*sql.DB, error) {
 // 构建连接字符串
 connString := fmt.Sprintf("oracle://%s:%s@%s", *username, *password, *dsn)
 db, err := sql.Open("oracle", connString)
 if err != nil {
  return nil, err
 }
 // 测试数据库连接
 if err := db.Ping(); err != nil {
  return nil, err
 }
 return db, nil
}

// 查询数据库并格式化输出
func queryDB(db *sql.DB, query string) {
 // 执行查询语句
 rows, err := db.Query(query)
 if err != nil {
  log.Fatalf("查询失败: %v", err)
 }
 defer rows.Close()

 // 获取列名
 cols, err := rows.Columns()
 if err != nil {
  log.Fatalf("获取列名失败: %v", err)
 }

 // 创建一个接收列值的切片
 values := make([]sql.RawBytes, len(cols))
 scanArgs := make([]interface{}, len(cols))
 for i := range values {
  scanArgs[i] = &values[i]
 }

 // 用来存储每列的最大宽度
 maxWidths := make([]intlen(cols))
 for i, col := range cols {
  maxWidths[i] = len(col) // 初始化为列名的长度
 }

 // 存储每行的数据,用于最后的输出
 var results [][]string

 // 读取数据行
 for rows.Next() {
  if err := rows.Scan(scanArgs...); err != nil {
   log.Fatalf("扫描行数据失败: %v", err)
  }
  rowStrings := make([]stringlen(cols))
  for i, val := range values {
   strVal := string(val)
   rowStrings[i] = strVal
   // 更新列的最大宽度
   if len(strVal) > maxWidths[i] {
    maxWidths[i] = len(strVal)
   }
  }
  results = append(results, rowStrings)
 }
 if err := rows.Err(); err != nil {
  log.Fatalf("读取行数据失败: %v", err)
 }

 // 打印列名
 for i, col := range cols {
  fmt.Printf("%-*s ", maxWidths[i], col)
 }
 fmt.Println()

 // 打印所有行
 for _, rowStrings := range results {
  for i, val := range rowStrings {
   fmt.Printf("%-*s ", maxWidths[i], val)
  }
  fmt.Println()
 }
}

// 执行数据库命令(增、删、改)
func execDB(db *sql.DB, query string) {
 // 执行 SQL 语句
 result, err := db.Exec(query)
 if err != nil {
  log.Fatalf("执行失败: %v", err)
 }

 // 获取受影响的行数
 rowsAffected, err := result.RowsAffected()
 if err != nil {
  log.Fatalf("获取受影响的行数失败: %v", err)
 }

 fmt.Printf("操作成功! 受影响的行数: %d\n", rowsAffected)
}

// 交互输入密码
func promptPassword() string {
 fmt.Print("Enter Password: ")
 bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
 if err != nil {
  log.Fatalf("Error reading password: %v", err)
 }
 fmt.Println() // 读完密码后换行
 return string(bytePassword)
}

func main() {
 flag.Parse()

 // 检查是否缺少必要的参数
 if *username == "" || *dsn == "" {
  fmt.Println("缺少必要的参数.")
  flag.Usage()
  return
 }

 // 如果没有通过参数提供密码,则交互式输入密码
 if *password == "" {
  *password = promptPassword()
 }

 // 初始化数据库连接
 db, err := initDB()
 if err != nil {
  log.Fatalf("数据库初始化失败: %v", err)
 }
 defer db.Close()

 // 判断 SQL 语句类型,并调用相应的函数
 if strings.HasPrefix(strings.ToLower(*execSQL), "select") || strings.HasPrefix(strings.ToLower(*execSQL), "with") {
  queryDB(db, *execSQL)
 } else {
  execDB(db, *execSQL)
 }
}

// 初始化函数定义帮助信息
func init() {
 flag.Usage = func() {
  fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", flag.CommandLine.Name())
  flag.PrintDefaults()
 }
}

输出示例:

./gooracli -u scott -p tiger -dsn 192.168.1.100:1521/orcl -e "select * from emp"
EMPNO ENAME  JOB       MGR  HIREDATE                  SAL  COMM DEPTNO 
7902  FORD   ANALYST   7566 1981-12-03T00:00:00+08:00 3000      20     
7369  SMITH  CLERK     7902 1980-12-17T00:00:00+08:00 800       20     
7499  ALLEN  SALESMAN  7698 1981-02-20T00:00:00+08:00 1600 300  30     
7521  WARD   SALESMAN  7698 1981-02-22T00:00:00+08:00 1250 500  30     
7566  JONES  MANAGER   7839 1981-04-02T00:00:00+08:00 2975      20     
7654  MARTIN SALESMAN  7698 1981-09-28T00:00:00+08:00 1250 1400 30     
7698  BLAKE  MANAGER   7839 1981-05-01T00:00:00+08:00 2850      30     
7782  CLARK  MANAGER   7839 1981-06-09T00:00:00+08:00 2450      10     
7788  SCOTT  ANALYST   7566 1987-04-19T00:00:00+08:00 3000      20     
7839  KING   PRESIDENT      1981-11-17T00:00:00+08:00 5000      10     
7844  TURNER SALESMAN  7698 1981-09-08T00:00:00+08:00 1500 0    30     
7876  ADAMS  CLERK     7788 1987-05-23T00:00:00+08:00 1100      20     
7900  JAMES1 CLERK     7698 1981-12-03T00:00:00+08:00 950       30     
7934  MILLER CLERK     7782 1982-01-23T00:00:00+08:00 1300      10     

./gooracli -u scott -p tiger -dsn 192.168.1.100:1521/orcl -e "update emp set ename='JAMES1' where EMPNO=7900"
操作成功! 受影响的行数: 1

./gooracli -u scott -p tiger -dsn 192.168.1.100:1521/orcl -e "insert into emp(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values (7902, 'FORD', 'ANALYST', 7566, to_date('03-12-1981', 'dd-mm-yyyy'), 3000.00, null, 20)"
操作成功! 受影响的行数: 1

./gooracli -u scott -p tiger -dsn 192.168.1.100:1521/orcl -e "delete from emp where EMPNO=7902"
操作成功! 受影响的行数: 0

./gooracli -u scott -dsn 192.168.1.100:1521/orcl
Enter Password: 
DUMMY 
X     

「欢迎关注我们的公众号,获取更多技术分享与经验交流。」


文章转载自数据库驾驶舱,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论