一个例子使用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