Skip to content

Commit 05758b0

Browse files
author
Mariano Gappa
authored
Merge pull request #18 from jackyzhen/postgres-support
Postgres support
2 parents 99d526e + 5bd877a commit 05758b0

10 files changed

+423
-146
lines changed

.databases.json.example

+10-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@
1414
"dbName": "prod_database",
1515
"dbServer": "prod.db.server.com",
1616
"user": "user",
17-
"pass": "pass"
17+
"pass": "pass",
18+
"sqlType": "mysql"
19+
},
20+
"postgresdb": {
21+
"appServer": "pg.server.com",
22+
"dbName": "pg_database",
23+
"dbServer": "pg.db.server.com",
24+
"user": "postgres",
25+
"pass": "postgres",
26+
"sqlType": "postgres"
1827
}
1928
}

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
FROM golang:1.11
22

3-
RUN apt-get update && apt-get install -y --no-install-recommends mysql-client && rm -rf /var/lib/apt/lists/*
3+
RUN apt-get update && apt-get install -y --no-install-recommends mysql-client postgresql-client && rm -rf /var/lib/apt/lists/*
44

55
ENTRYPOINT [ "go", "test", "-v", "." ]

README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ MySQL pipe
88

99
## What does it do?
1010

11-
- `sql` allows you to pipe STDIN (hopefully containing SQL) to one or more pre-configured MySQL databases
11+
- `sql` allows you to pipe STDIN (hopefully containing SQL) to one or more pre-configured MySQL or PostgreSQL databases
1212
- output comes out in `\t`-separated format, allowing further piping (e.g. works really well with [chart](https://github.com/MarianoGappa/chart))
1313
- when more than one database is queried, the requests are made in parallel
14-
- `sql` can either run `mysql` locally, run `mysql` locally but connecting to a remote host (by configuring a `dbServer`), or `ssh` to a remote host and from there run `mysql` to either a local or remote host (by configuring an `appServer` and a `dbServer`)
14+
- `sql` can either run `mysql/psql` locally, run `mysql/psql` locally but connecting to a remote host (by configuring a `dbServer`), or `ssh` to a remote host and from there run `mysql/psql` to either a local or remote host (by configuring an `appServer` and a `dbServer`)
1515

1616
## Installation
1717

@@ -37,6 +37,8 @@ $ compinit
3737

3838
Create a `.databases.json` dotfile in your home folder or in any [XDG-compliant](https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) directory. [This](.databases.json.example) is an example file.
3939

40+
`sql` decides to execute with MySQL or PostgreSQL depending on the `sqlType` property set for a database, *defaulting to to MySQL if not set.*
41+
4042
## Example usages
4143

4244
```
@@ -50,8 +52,9 @@ sql all "SELECT * FROM users WHERE name = 'John'"
5052
## Notes
5153

5254
- when more than one database is queried, the resulting rows are prefixed with the database identifier
53-
- the `all` special keyword means "sql to all configured databases"
55+
- the `all` special keyword means "sql to all configured databases".
5456
- `sql` assumes that you have correctly configured SSH keys on all servers you `ssh` to
57+
- `sql` will error if all targeted databases do not have the same sql type.
5558

5659
## Beware!
5760

@@ -61,7 +64,7 @@ sql all "SELECT * FROM users WHERE name = 'John'"
6164

6265
## Dependencies
6366

64-
- mysql
67+
- mysql-client and/or postgresql-client
6568
- ssh (only if you configure an "appServer")
6669

6770
## Contribute

config.go

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type database struct {
1515
DbName string
1616
User string
1717
Pass string
18+
SQLType string
1819
}
1920

2021
func mustReadDatabasesConfigFile() map[string]database {

main.go

+3-79
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
package main
22

33
import (
4-
"bufio"
54
"context"
65
"flag"
76
"fmt"
87
"log"
98
"os"
10-
"os/exec"
11-
"strings"
129
"sync"
1310
)
1411

@@ -84,11 +81,13 @@ func _main(databases map[string]database, databasesArgs []string, query string,
8481
var wg sync.WaitGroup
8582
wg.Add(len(targetDatabases))
8683

84+
sqlRunner := mustNewSQLRunner(quitContext, println, query, len(targetDatabases) > 1)
85+
8786
returnCode := 0
8887
for _, k := range targetDatabases {
8988
go func(db database, k string) {
9089
defer wg.Done()
91-
if r := runSQL(quitContext, db, query, k, len(targetDatabases) > 1, println); !r {
90+
if r := sqlRunner.runSQL(db, k); !r {
9291
returnCode = 1
9392
}
9493
}(databases[k], k)
@@ -97,78 +96,3 @@ func _main(databases map[string]database, databasesArgs []string, query string,
9796
wg.Wait()
9897
return returnCode
9998
}
100-
101-
func runSQL(quitContext context.Context, db database, query string, key string, prependKey bool, println func(string)) bool {
102-
userOption := ""
103-
if db.User != "" {
104-
userOption = fmt.Sprintf("-u %v ", db.User)
105-
}
106-
107-
passOption := ""
108-
if db.Pass != "" {
109-
passOption = fmt.Sprintf("-p%v ", db.Pass)
110-
}
111-
112-
hostOption := ""
113-
if db.DbServer != "" {
114-
hostOption = fmt.Sprintf("-h %v ", db.DbServer)
115-
}
116-
117-
prepend := ""
118-
if prependKey {
119-
prepend = key + "\t"
120-
}
121-
122-
mysql := "mysql"
123-
options := fmt.Sprintf(" -Nsr %v%v%v%v -e ", userOption, passOption, hostOption, db.DbName)
124-
125-
var cmd *exec.Cmd
126-
if db.AppServer != "" {
127-
escapedQuery := fmt.Sprintf(`'%v'`, strings.Replace(query, `'`, `'"'"'`, -1))
128-
cmd = exec.CommandContext(quitContext, "ssh", db.AppServer, mysql+options+escapedQuery)
129-
} else {
130-
args := append(trimEmpty(strings.Split(options, " ")), query)
131-
cmd = exec.CommandContext(quitContext, mysql, args...)
132-
}
133-
134-
stdout, err := cmd.StdoutPipe()
135-
if err != nil {
136-
log.Printf("Cannot create pipe for STDOUT of running command on %v; not running. err=%v\n", key, err)
137-
return false
138-
}
139-
140-
stderr, err := cmd.StderrPipe()
141-
if err != nil {
142-
log.Printf("Cannot create pipe for STDERR of running command on %v; not running. err=%v\n", key, err)
143-
return false
144-
}
145-
146-
if err := cmd.Start(); err != nil {
147-
log.Printf("Cannot start command on %v; not running. err=%v\n", key, err)
148-
return false
149-
}
150-
151-
scanner := bufio.NewScanner(stdout)
152-
for scanner.Scan() {
153-
println(prepend + scanner.Text())
154-
}
155-
156-
stderrLines := []string{}
157-
scanner = bufio.NewScanner(stderr)
158-
for scanner.Scan() {
159-
stderrLines = append(stderrLines, scanner.Text())
160-
}
161-
162-
cmd.Wait()
163-
164-
result := true
165-
if len(stderrLines) > 0 {
166-
result = false
167-
log.Println(key + " had errors:")
168-
for _, v := range stderrLines {
169-
log.Println(key + " [ERROR] " + v)
170-
}
171-
}
172-
173-
return result
174-
}

0 commit comments

Comments
 (0)