pgweb/client.go

242 lines
4.3 KiB
Go
Raw Normal View History

2014-10-11 02:14:17 +04:00
package main
import (
"bytes"
"encoding/csv"
"fmt"
2014-10-11 02:14:17 +04:00
"reflect"
2014-11-11 08:10:05 +03:00
"github.com/jmoiron/sqlx"
2014-10-11 02:14:17 +04:00
)
type Client struct {
db *sqlx.DB
history []HistoryRecord
connectionString string
2014-10-11 02:14:17 +04:00
}
2014-11-21 07:33:08 +03:00
type Row []interface{}
2014-10-11 02:14:17 +04:00
type Result struct {
2014-11-21 07:33:08 +03:00
Columns []string `json:"columns"`
Rows []Row `json:"rows"`
2014-10-11 02:14:17 +04:00
}
// Struct to hold table rows browsing options
type RowsOptions struct {
Limit int // Number of rows to fetch
SortColumn string // Column to sort by
SortOrder string // Sort direction (ASC, DESC)
}
2014-10-11 02:14:17 +04:00
func NewClient() (*Client, error) {
str, err := buildConnectionString(options)
if options.Debug && str != "" {
fmt.Println("Creating a new client for:", str)
}
if err != nil {
return nil, err
}
db, err := sqlx.Open("postgres", str)
2014-10-11 02:14:17 +04:00
if err != nil {
return nil, err
}
client := Client{
db: db,
connectionString: str,
history: NewHistory(),
}
return &client, nil
2014-10-11 02:14:17 +04:00
}
func NewClientFromUrl(url string) (*Client, error) {
if options.Debug {
fmt.Println("Creating a new client for:", url)
}
db, err := sqlx.Open("postgres", url)
if err != nil {
return nil, err
}
client := Client{
db: db,
connectionString: url,
history: NewHistory(),
}
return &client, nil
}
2014-10-12 07:38:32 +04:00
func (client *Client) Test() error {
return client.db.Ping()
}
2014-10-16 06:59:43 +04:00
func (client *Client) Info() (*Result, error) {
return client.query(PG_INFO)
2014-10-16 06:59:43 +04:00
}
2014-10-16 01:05:23 +04:00
func (client *Client) Databases() ([]string, error) {
return client.fetchRows(PG_DATABASES)
2014-10-16 01:05:23 +04:00
}
2014-10-11 02:14:17 +04:00
func (client *Client) Tables() ([]string, error) {
return client.fetchRows(PG_TABLES)
2014-10-11 02:14:17 +04:00
}
2014-10-16 06:54:40 +04:00
func (client *Client) Table(table string) (*Result, error) {
return client.query(PG_TABLE_SCHEMA, table)
2014-10-16 06:54:40 +04:00
}
func (client *Client) TableRows(table string, opts RowsOptions) (*Result, error) {
sql := fmt.Sprintf(`SELECT * FROM "%s"`, table)
if opts.SortColumn != "" {
if opts.SortOrder == "" {
opts.SortOrder = "ASC"
}
sql += fmt.Sprintf(" ORDER BY %s %s", opts.SortColumn, opts.SortOrder)
}
if opts.Limit > 0 {
sql += fmt.Sprintf(" LIMIT %d", opts.Limit)
}
return client.query(sql)
}
func (client *Client) TableInfo(table string) (*Result, error) {
return client.query(PG_TABLE_INFO, table)
}
2014-10-11 22:20:16 +04:00
func (client *Client) TableIndexes(table string) (*Result, error) {
res, err := client.query(PG_TABLE_INDEXES, table)
2014-10-11 22:20:16 +04:00
if err != nil {
return nil, err
}
return res, err
}
2014-10-11 02:14:17 +04:00
func (client *Client) Query(query string) (*Result, error) {
res, err := client.query(query)
// Save history records only if query did not fail
if err == nil {
client.history = append(client.history, NewHistoryRecord(query))
}
return res, err
}
func (client *Client) query(query string, args ...interface{}) (*Result, error) {
rows, err := client.db.Queryx(query, args...)
2014-10-11 02:14:17 +04:00
if err != nil {
return nil, err
}
defer rows.Close()
cols, err := rows.Columns()
if err != nil {
return nil, err
}
2015-01-14 06:18:11 +03:00
result := Result{Columns: cols}
2014-10-11 02:14:17 +04:00
for rows.Next() {
obj, err := rows.SliceScan()
for i, item := range obj {
if item == nil {
obj[i] = nil
} else {
t := reflect.TypeOf(item).Kind().String()
if t == "slice" {
obj[i] = string(item.([]byte))
}
}
}
if err == nil {
result.Rows = append(result.Rows, obj)
}
}
return &result, nil
}
func (res *Result) Format() []map[string]interface{} {
var items []map[string]interface{}
for _, row := range res.Rows {
item := make(map[string]interface{})
for i, c := range res.Columns {
item[c] = row[i]
}
items = append(items, item)
}
return items
}
func (res *Result) CSV() []byte {
buff := &bytes.Buffer{}
writer := csv.NewWriter(buff)
2014-10-11 05:41:15 +04:00
writer.Write(res.Columns)
for _, row := range res.Rows {
record := make([]string, len(res.Columns))
for i, item := range row {
if item != nil {
record[i] = fmt.Sprintf("%v", item)
} else {
record[i] = ""
}
}
err := writer.Write(record)
if err != nil {
fmt.Println(err)
break
}
}
writer.Flush()
return buff.Bytes()
}
// Fetch all rows as strings for a single column
func (client *Client) fetchRows(q string) ([]string, error) {
res, err := client.query(q)
if err != nil {
return nil, err
}
// Init empty slice so json.Marshal will encode it to "[]" instead of "null"
results := make([]string, 0)
for _, row := range res.Rows {
results = append(results, row[0].(string))
}
return results, nil
}