golang使用oracle oci接口查询oracle数据库

一个例子使用golang的github.com/mattn/go-oci8库查询oracle数据库的例子。
这里例子里面会打印出SELECT的结果,支持select *操作,也就是自动分析查询结果字段,把他们转换成可打印的string格式,然后输出到屏幕。
还模拟了一个简易的desc <table>指令。

package main

import (
    "bufio"
    "database/sql"
    "fmt"
    "log"
    "os"
    "strings"

    _ "github.com/mattn/go-oci8"
)

const (
    username   = "<username>"
    password   = "<password>"
    connstring = "<hostname>:1521/<sid>"
)

func main() {
    connectionStr := ""
    if len(os.Args) > 1 {
        connectionStr = os.Args[1]
    } else {
        connectionStr = fmt.Sprintf("%s/%s@%s", username, password, connstring)
    }
    db, err := sql.Open("oci8", connectionStr)
    if err != nil {
        log.Printf("ERROR: Cannot open [%s/XXXX@%s], %v\n", username, connstring, err)
        return
    }
    defer db.Close()

    if err := db.Ping(); err != nil {
        log.Printf("ERROR: Cannot ping database, %v\n", err)
        return
    }

    var sql string
    scanner := bufio.NewScanner(os.Stdin)
    for {
        fmt.Print("SQL> ")
        scanner.Scan()
        text := scanner.Text()
        
        if text == "" {
            continue
        } else if text == "q" || text == "quit" {
            break
        } else if text[len(text)-1:] == ";" {
            sql = strings.TrimSpace(sql + " " + text[0:len(text)-1])
            //fmt.Printf("SQL=[%s]\n", sql)
            executeSQL(db, strings.TrimSpace(sql))
            sql = ""
        } else {
            sql = sql + " " + text
        }
    }
}

func executeSQL(db *sql.DB, sql string) error {
    command := ""
    spaceIndex := strings.IndexByte(sql, ' ')
    if spaceIndex > 0 {
        command = strings.ToUpper(sql[0:spaceIndex])
    } else {
        command = strings.ToUpper(sql)
    }
    if strings.HasPrefix(command, "SELECT") {
        return executeQuery(db, sql)
    } else if strings.HasPrefix(command, "DESC") {
        sql = "SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE" +
              "  FROM USER_TAB_COLUMNS" +
              " WHERE TABLE_NAME = '"+ strings.ToUpper(sql[spaceIndex + 1:])+ "' ORDER BY COLUMN_ID"
        return executeQuery(db, sql)
    } else {
        return executeUpdate(db, sql)
    }
}

func executeUpdate(db *sql.DB, update string) error {
    result, err := db.Exec(update)
    if err != nil {
        log.Printf("ERROR: Cannot execute update [%s], %v\n", update, err)
        return err
    }
    rows, err := result.RowsAffected()
    if err != nil {
        log.Printf("ERROR: Cannot get rows affected, %v\n", err)
        return err
    }

    fmt.Printf("%d row(s) affected\n", rows)
    return nil
}

func executeQuery(db *sql.DB, query string) error {
    rows, err := db.Query(query)
    if err != nil {
        log.Printf("ERROR: Cannot execute query [%s], %v\n", query, err)
        return err
    }
    defer rows.Close()

    queryResult, err := parseRows(rows)
    if err != nil {
        log.Printf("ERROR: Cannot parse query result, %v\n", err)
        return err
    }

    queryResult.Print()
    return nil
}

func parseRows(rows *sql.Rows) (*QueryResult, error) {
    columns, err := rows.Columns()
    if err != nil {
        log.Printf("ERROR: Cannot get columns, %v\n", err)
        return nil, err
    }
    columnCount := len(columns)

    // handle column data
    queryResult := &QueryResult{}
    for c := 0; c < columnCount; c++ {
        queryResult.ColumnResult = append(queryResult.ColumnResult, columns[c])
    }

    // handle row data
    for rows.Next() {
        rowRaw := make([]interface{}, columnCount)
        rowString := make([]string, columnCount)
        for c := 0; c < columnCount; c++ {
            rowRaw[c] = new(sql.RawBytes)
        }

        err := rows.Scan(rowRaw...)
        if err != nil {
            log.Printf("ERROR: Cannot scan row, %v\n", err)
            return nil, err
        }

        for c := 0; c < columnCount; c++ {
            if fieldRaw, ok := rowRaw[c].(*sql.RawBytes); ok {
                rowString[c] = string(*fieldRaw)
            } else {
                log.Printf("ERROR: Cannot convert index %d column %s to type *sql.RawBytes", c, columns[c])
                return nil, err
            }
        }
        queryResult.RowResult = append(queryResult.RowResult, rowString)
    }

    return queryResult, nil
}

type QueryResult struct {
    ColumnResult []string
    RowResult    [][]string
}

func (result *QueryResult) Print() {
    // calculate maximum column field length
    columnLength := make([]int, len(result.ColumnResult))
    for c := 0; c < len(result.ColumnResult); c++ {
        columnLength[c] = len(result.ColumnResult[c])

        for r := 0; r < len(result.RowResult); r++ {
            if len(result.RowResult[r][c]) > columnLength[c] {
                columnLength[c] = len(result.RowResult[r][c])
            }
        }
    }

    // print header
    for c := 0; c < len(result.ColumnResult); c++ {
        f := fmt.Sprintf("%%-%ds|", columnLength[c])
        fmt.Printf(f, result.ColumnResult[c])
    }
    fmt.Println()
    for c := 0; c < len(result.ColumnResult); c++ {
        fmt.Printf("%s|", strings.Repeat("-", columnLength[c]))
    }
    fmt.Println()


    // print data
    for r := 0; r < len(result.RowResult); r++ {
        for c := 0; c < len(result.ColumnResult); c++ {
            f := fmt.Sprintf("%%-%ds", columnLength[c])
            fmt.Printf(f+"|", result.RowResult[r][c])
        }
        fmt.Println()
    }
}

运行结果:

SQL> create table testtab(a int, b char(10), c varchar2(100));
0 row(s) affected
SQL>
SQL> insert into testtab values(1, 'a', 'aaaaa');
1 row(s) affected
SQL>
SQL> insert into testtab values(2, 'bbbb', 'bbbbbbbb');
1 row(s) affected
SQL>
SQL> select a, c from testtab;
A|C       |
-|--------|
1|aaaaa   |
2|bbbbbbbb|
SQL> select * from testtab;
A|B         |C       |
-|----------|--------|
1|a         |aaaaa   |
2|bbbb      |bbbbbbbb|
SQL>
SQL> desc testtab;
COLUMN_NAME|DATA_TYPE|DATA_LENGTH|DATA_PRECISION|DATA_SCALE|NULLABLE|
-----------|---------|-----------|--------------|----------|--------|
A          |NUMBER   |22         |              |0         |Y       |
B          |CHAR     |10         |              |          |Y       |
C          |VARCHAR2 |100        |              |          |Y       |
SQL>
SQL>
SQL> drop table testtab;
0 row(s) affected
SQL> select * from testtab;
2021/05/28 09:42:07 ERROR: Cannot execute query [select * from testtab], ORA-00942: table or view does not exist
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容