Skip to content

Commit 018bfe7

Browse files
committed
feat: working on a sql to csv tool
1 parent 39c0d57 commit 018bfe7

File tree

6 files changed

+430
-3
lines changed

6 files changed

+430
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@ bin/*
3636

3737
# Project ignores
3838
_codemeta.json
39+
testout

cmd/sql2csv/sql2csv.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"database/sql"
6+
"os"
7+
"path"
8+
9+
// Caltech Library Packages
10+
"github.com/caltechlibrary/datatools"
11+
)
12+
13+
const (
14+
helpText = `% {app_name} (1) user manual
15+
% R. S. Doiel
16+
% 2023-01-03
17+
18+
# NAME
19+
20+
{app_name}
21+
22+
# SYNOPSIS
23+
24+
{app_name} DB_CONNECT_FILE DATABASE_NAME SQL_STATEMENT
25+
26+
# DESCRIPTION
27+
28+
{app_name} takes a config file describing a SQL database connection
29+
and runs the SQL statement provided as the final parameter. The
30+
output is rendered in CSV format.
31+
32+
# OPTIONS
33+
34+
-help
35+
: display help
36+
37+
-version
38+
: display version
39+
40+
-license
41+
: display license
42+
43+
# EXAMPLE
44+
45+
Using the ".my.cnf" configuration file, display ten rows
46+
from table "mytable" in "mydata" database.
47+
48+
{app_name} .my.cnf mydata 'SELECT * FROM mytable LIMIT 10'
49+
50+
The CSV output is written standard out and can be redirected into
51+
a file if desired.
52+
53+
{app_name} .my.cnf mydata 'SELECT * FROM mytable LIMIT 10' \
54+
>ten-rows.csv
55+
56+
{app_name} {version}
57+
`
58+
59+
)
60+
61+
func fmtTxt(src string, name string, version string) string {
62+
return strings.ReplaceAll(strings.ReplaceAll(src, `{app_name}`, name), `{version}`, version)
63+
}
64+
65+
func main() {
66+
67+
appName := path.Base(os.Argv[0])
68+
showHelp, showLicense, showVersion := false, false, false
69+
70+
flag.BoolVar(&showHelp, "help", showHelp, "display help")
71+
flag.BoolVar(&showLicense, "license", showLicense, "display license")
72+
flag.BoolVar(&showVersion, "version", showVersion, "display version")
73+
flag.Parse()
74+
args := flag.Args()
75+
76+
if showHelp {
77+
fmt.Fprintf(os.Stdout, "%s", fmtTxt(helpText, appName, datatools.Version))
78+
os.Exit(0)
79+
}
80+
if showVersion {
81+
fmt.Fprintf(os.Stdout, "%s %s\n", appName, datatools.Version)
82+
os.Exit(0)
83+
}
84+
if showLicense {
85+
fmt.Fprintf(os.Stdout, "%s %s\n%s\n", appName, datatools.Version, fmtTxt(datatools.LicenseText, appName, datatools.Version))
86+
os.Exit(0)
87+
}
88+
89+
if len(args) != 3 {
90+
fmt.Fprintln(os.Stderr, "expected DB_CONF_FILE DB_NAME SQL_QUERY")
91+
os.Exit(1)
92+
}
93+
// FIXME: Open DB connection using the config file and DB name
94+
// Create and execute a the SQL query provided on the command line
95+
// Loop through the returned rows and output each row CSV encoded
96+
// First row should be used to build render header row
97+
98+
}

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ require (
1010
github.com/caltechlibrary/tmplfn v0.0.21
1111
github.com/dexyk/stringosim v0.0.0-20170922105913-9d0b3e91a842
1212
github.com/ghodss/yaml v1.0.0
13-
github.com/google/uuid v1.2.0
13+
github.com/glebarez/go-sqlite v1.20.0
14+
github.com/go-sql-driver/mysql v1.7.0
15+
github.com/google/uuid v1.3.0
16+
github.com/lib/pq v1.10.7
1417
github.com/tealeg/xlsx v1.0.5
1518
gopkg.in/yaml.v2 v2.4.0 // indirect
1619
)

go.sum

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,98 @@ github.com/caltechlibrary/dotpath v0.0.2 h1:E3KHd5gNDSaHsbw2jG3XeEZvE2oZ6kRNIe5/
88
github.com/caltechlibrary/dotpath v0.0.2/go.mod h1:PjkHwEouoEUa4FrmG0I50k8fs8wXmrLvazHYBIoW4eQ=
99
github.com/caltechlibrary/tmplfn v0.0.21 h1:ZwYdmoPZZhaONXgVf93wgJnRJ9uESYgIMsw0edsafHY=
1010
github.com/caltechlibrary/tmplfn v0.0.21/go.mod h1:TWdYP/x2Phg206b75YTA/oHHvDMrGMeOt7u5byMxWl4=
11+
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
12+
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
13+
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
1114
github.com/dexyk/stringosim v0.0.0-20170922105913-9d0b3e91a842 h1:FWXGhOthNyZKdK0YVyDrkg5dCXOfKvexcRG37U1v6AQ=
1215
github.com/dexyk/stringosim v0.0.0-20170922105913-9d0b3e91a842/go.mod h1:PfVoEMbmPGFArz22/wIefW9CzuQhdnE+C9ikEzJvb9Q=
16+
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
1317
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
1418
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
15-
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
16-
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
19+
github.com/glebarez/go-sqlite v1.20.0 h1:6D9uRXq3Kd+W7At+hOU2eIAeahv6qcYfO8jzmvb4Dr8=
20+
github.com/glebarez/go-sqlite v1.20.0/go.mod h1:uTnJoqtwMQjlULmljLT73Cg7HB+2X6evsBHODyyq1ak=
21+
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
22+
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
23+
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
24+
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
25+
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
26+
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
27+
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
28+
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
29+
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
1730
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
1831
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
1932
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
2033
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
2134
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
35+
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
36+
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
37+
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
38+
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
39+
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
40+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
41+
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
42+
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
2243
github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
2344
github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
45+
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
46+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
47+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
48+
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
49+
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
50+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
51+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
52+
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
53+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
54+
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
55+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
56+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
57+
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
58+
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
59+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
60+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
61+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
62+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
63+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
64+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
65+
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
66+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
67+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
68+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
69+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
2470
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2571
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
2672
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2773
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
2874
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
75+
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
76+
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
77+
modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
78+
modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
79+
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
80+
modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI=
81+
modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0=
82+
modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g=
83+
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
84+
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
85+
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
86+
modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA=
87+
modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0=
88+
modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
89+
modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
90+
modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
91+
modernc.org/libc v1.21.5 h1:xBkU9fnHV+hvZuPSRszN0AXDG4M7nwPLwTWwkYcvLCI=
92+
modernc.org/libc v1.21.5/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
93+
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
94+
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
95+
modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
96+
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
97+
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
98+
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
99+
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
100+
modernc.org/sqlite v1.20.0 h1:80zmD3BGkm8BZ5fUi/4lwJQHiO3GXgIUvZRXpoIfROY=
101+
modernc.org/sqlite v1.20.0/go.mod h1:EsYz8rfOvLCiYTy5ZFsOYzoCcRMu98YYkwAcCw5YIYw=
102+
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
103+
modernc.org/tcl v1.15.0/go.mod h1:xRoGotBZ6dU+Zo2tca+2EqVEeMmOUBzHnhIwq4YrVnE=
104+
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
105+
modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ=

sqlcsv.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package datatools
2+
3+
import (
4+
"database/sql"
5+
"encoding/csv"
6+
"fmt"
7+
"os"
8+
//"path"
9+
"strings"
10+
11+
// Database specific drivers
12+
_ "github.com/glebarez/go-sqlite"
13+
_ "github.com/go-sql-driver/mysql"
14+
_ "github.com/lib/pq"
15+
)
16+
17+
// SQLSrouce represents a wrapper SQL database drivers
18+
// using a common struct.
19+
type SQLStore struct {
20+
// Protocol holds the database type string, e.g. mysql, sqlite, pg
21+
Protocol string
22+
// Host name of service where to connect
23+
Host string
24+
// Port of service
25+
Port string
26+
// Database name you're going to query against
27+
Database string
28+
// User name for access a database service
29+
User string
30+
// Password for accessing a database service
31+
Password string
32+
33+
// FIXME: need to have the CSV encoding options for writing
34+
// result of query
35+
WriteHeaderRow bool
36+
37+
// workpath is the working directory to use when accessing SQLite3
38+
// related database paths
39+
workPath string
40+
41+
// driverName is the database driver is the type of database we're accessing
42+
driverName string
43+
44+
// the data source name
45+
dsn string
46+
47+
// The db handle of the opened connection
48+
db *sql.DB
49+
}
50+
51+
func dsnFixUp(driverName string, dsn string, workPath string) string {
52+
switch driverName {
53+
case "postgres":
54+
return fmt.Sprintf("%s://%s", driverName, dsn)
55+
case "sqlite":
56+
// NOTE: the db needs to be stored in the dataset directory
57+
// to keep the dataset easily movable.
58+
//dbName := path.Base(dsn)
59+
return dsn
60+
//path.Join(workPath, dbName)
61+
}
62+
return dsn
63+
}
64+
65+
// OpenSQLStore opens a mysql, postgres or SQLite database
66+
// based on a data source name expressed as a URL.
67+
// The URL is formed by using the "protocol" to identify
68+
// the service (e.g. "mysql://", "sqlite3://", "pg://")
69+
// followed by a data source name per golang sql package
70+
// documentation.
71+
func OpenSQLStore(dsnURL string) (*SQLStore, error) {
72+
if !strings.Contains(dsnURL, "://") {
73+
return nil, fmt.Errorf("missing protocol in url scheme")
74+
}
75+
driverName, dsn, ok := strings.Cut(dsnURL, "://")
76+
if !ok {
77+
return nil, fmt.Errorf("could not parse DSN URI, got %q", dsnURL)
78+
}
79+
fmt.Printf("DEBUG driverName %q, dsn %q\n", driverName, dsn)
80+
var err error
81+
store := new(SQLStore)
82+
store.driverName = driverName
83+
store.workPath, err = os.Getwd()
84+
if err != nil {
85+
return nil, err
86+
}
87+
store.dsn = dsnFixUp(driverName, dsn, store.workPath)
88+
89+
db, err := sql.Open(store.driverName, store.dsn)
90+
if err != nil {
91+
return nil, err
92+
}
93+
store.db = db
94+
store.WriteHeaderRow = true
95+
return store, nil
96+
}
97+
98+
// Close the previously openned database resource
99+
func (store *SQLStore) Close() error {
100+
if store.db != nil {
101+
return store.db.Close()
102+
}
103+
return nil
104+
}
105+
106+
// QueryToCSV runs a SQL query statement and returns to the results
107+
// CSV encoded via an io.Writer
108+
func (store *SQLStore) QueryToCSV(out *csv.Writer, stmt string) error {
109+
fmt.Printf("DEBUG trying %q\n", stmt)
110+
rows, err := store.db.Query(stmt)
111+
if err != nil {
112+
fmt.Printf("DEBUG store -> %+v\n", store)
113+
fmt.Printf("DEBUG erorr from stmt %q -> %s\n", stmt, err)
114+
return err
115+
}
116+
defer rows.Close()
117+
fmt.Printf("DEBUG rows queries with %q\n", stmt)
118+
columns, err := rows.Columns()
119+
if err != nil {
120+
return err
121+
}
122+
fmt.Printf("DEBUG column names -> %s\n", strings.Join(columns, ", "))
123+
// Write out our header row is configurable
124+
if store.WriteHeaderRow {
125+
if err := out.Write(columns); err != nil {
126+
return err
127+
}
128+
}
129+
// Make an array of cells
130+
cells := make([]string, len(columns))
131+
for rows.Next() {
132+
// Retrieve the raw column data from the row
133+
vals := make([]string, len(columns))
134+
if err := rows.Scan(vals...); err != nil {
135+
return nil
136+
}
137+
fmt.Printf("DEBUG vals -> %+v\n", vals)
138+
// Convert values to strings for CSV write
139+
for i := 0; i < len(columns); i++ {
140+
switch vals[i].(type) {
141+
case string:
142+
cells[i] = vals[i].(string)
143+
default:
144+
cells[i] = fmt.Sprintf("%+v", vals[i])
145+
}
146+
}
147+
if err := out.Write(cells); err != nil {
148+
return err
149+
}
150+
}
151+
out.Flush()
152+
if err := rows.Err(); err != nil {
153+
return err
154+
}
155+
if err := out.Error(); err != nil {
156+
return err
157+
}
158+
return nil
159+
}

0 commit comments

Comments
 (0)