Skip to content

Commit be5c836

Browse files
authored
fix(client): add the ability to switch to other schemas (#267)
* feat(oracle): add the ability to switch to other schemas * feat(client): fix the queries to query against ALL* tables * fix(client): fix set search_path query for postgres * feat(client): add a way to handle the schema in the client side for the oracle databases * feat(oracle): add a way to build queries based on if the schema flag is present or not * docs(config): fix a typo in the example config file * docs(readme): update the readme file to include the use of the schema for the oracle databases * refactor(client): refactor the oracle client to remove the else statements * docs(docs): update the documents in the docs directory
1 parent 68bcf9d commit be5c836

File tree

7 files changed

+135
-45
lines changed

7 files changed

+135
-45
lines changed

.dblab.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ database:
1919
- name: "oracle"
2020
host: "localhost"
2121
port: 1521
22-
db: "FREEPDB1 "
22+
db: "FREEPDB1"
2323
password: "password"
2424
user: "system"
2525
driver: "oracle"

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ Flags:
104104
--limit uint Size of the result set from the table content query (should be greater than zero, otherwise the app will error out) (default 100)
105105
--pass string Password for user
106106
--port string Server port
107-
--schema string Database schema (postgres only)
107+
--schema string Database schema (postgres and oracle only)
108108
--socket string Path to a Unix socket file
109109
--ssh-host string SSH Server Hostname/IP
110110
--ssh-key string File with private key for SSH authentication
@@ -152,11 +152,16 @@ $ dblab --url 'oracle://user:password@localhost:1521/db'
152152
$ dblab --url 'sqlserver://SA:myStrong(!)Password@localhost:1433?database=tempdb&encrypt=true&trustservercertificate=false&connection+timeout=30'
153153
```
154154

155-
if you're using PostgreSQL, you have the option to define the schema you want to work with, the default value is `public`.
155+
if you're using PostgreSQL or Oracle, you have the option to define the schema you want to work with, the default value is `public` for Postgres, empty for Oracle.
156156

157157
```sh
158+
# Postgres
158159
$ dblab --host localhost --user myuser --db users --pass password --schema myschema --ssl disable --port 5432 --driver postgres --limit 50
159160
$ dblab --url postgres://user:password@host:port/database?sslmode=[mode] --schema myschema
161+
162+
# Oracle
163+
$ dblab --host localhost --user user2 --db FREEPDB1 --pass password --port 1521 --driver oracle --limit 50 --schema user1
164+
$ dblab --url 'oracle://user2:password@localhost:1521/FREEPDB1' --schema user1
160165
```
161166

162167
As a request made in [#125](https://github.com/danvergara/dblab/issues/125), support for MySQL/MariaDB sockets was integrated.
@@ -288,9 +293,10 @@ database:
288293
- name: "oracle"
289294
host: "localhost"
290295
port: 1521
291-
db: "FREEPDB1 "
296+
db: "FREEPDB1"
297+
schema: "user1"
292298
password: "password"
293-
user: "system"
299+
user: "user2"
294300
driver: "oracle"
295301
ssl: "enable"
296302
wallet: "path/to/wallet"

cmd/root.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ func init() {
188188
rootCmd.Flags().StringVarP(&user, "user", "", "", "Database user")
189189
rootCmd.Flags().StringVarP(&pass, "pass", "", "", "Password for user")
190190
rootCmd.Flags().StringVarP(&db, "db", "", "", "Database name (optional)")
191-
rootCmd.Flags().StringVarP(&schema, "schema", "", "", "Database schema (postgres only)")
191+
rootCmd.Flags().
192+
StringVarP(&schema, "schema", "", "", "Database schema (postgres and oracle only)")
192193
rootCmd.Flags().StringVarP(&ssl, "ssl", "", "", "SSL mode")
193194
rootCmd.Flags().
194195
UintVarP(&limit, "limit", "", 100, "Size of the result set from the table content query (should be greater than zero, otherwise the app will error out)")

docs/quickstart.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,20 @@ dblab --url mysql://user:password@tcp(host:port)/db
2626
dblab --url file:test.db?cache=shared&mode=memory
2727
```
2828

29-
if you're using PostgreSQL, you have the option to define the schema you want to work with, the default value is `public`.
29+
if you're using PostgreSQL or Oracle, you have the option to define the schema you want to work with, the default value is `public` for Postgres, empty for Oracle.
3030

3131
```{ .sh .copy }
3232
dblab --host localhost --user myuser --db users --pass password --schema myschema --ssl disable --port 5432 --driver postgres --limit 50
3333
```
3434
```{ .sh .copy }
3535
dblab --url postgres://user:password@host:port/database?sslmode=[mode] --schema myschema
3636
```
37+
```{ .sh .copy }
38+
dblab --host localhost --user user2 --db FREEPDB1 --pass password --port 1521 --driver oracle --limit 50 --schema user1
39+
```
40+
```{ .sh .copy }
41+
dblab --url 'oracle://user2:password@localhost:1521/FREEPDB1' --schema user1
42+
```
3743

3844
As a request made in [#125](https://github.com/danvergara/dblab/issues/125), support for MySQL/MariaDB sockets was integrated.
3945

@@ -80,6 +86,17 @@ database:
8086
user: "postgres"
8187
schema: "public"
8288
driver: "postgres"
89+
- name: "oracle"
90+
host: "localhost"
91+
port: 1521
92+
db: "FREEPDB1"
93+
schema: "user1"
94+
password: "password"
95+
user: "user2"
96+
driver: "oracle"
97+
ssl: "enable"
98+
wallet: "path/to/wallet"
99+
ssl-verify: true
83100
# should be greater than 0, otherwise the app will error out
84101
limit: 50
85102
```

docs/usage.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Flags:
4444
--limit uint Size of the result set from the table content query (should be greater than zero, otherwise the app will error out) (default 100)
4545
--pass string Password for user
4646
--port string Server port
47-
--schema string Database schema (postgres only)
47+
--schema string Database schema (postgres and oracle only)
4848
--socket string Path to a Unix socket file
4949
--ssh-host string SSH Server Hostname/IP
5050
--ssh-key string File with private key for SSH authentication
@@ -158,7 +158,9 @@ dblab --url 'oracle://user:password@localhost:1521/db'
158158
dblab --url 'sqlserver://SA:myStrong(!)Password@localhost:1433?database=tempdb&encrypt=true&trustservercertificate=false&connection+timeout=30'
159159
```
160160

161-
if you're using PostgreSQL, you have the option to define the schema you want to work with, the default value is `public`.
161+
if you're using PostgreSQL or Oracle, you have the option to define the schema you want to work with, the default value is `public` for Postgres, empty for Oracle.
162+
163+
**Postgres**
162164

163165
```{ .sh .copy }
164166
dblab --host localhost --user myuser --db users --pass password --schema myschema --ssl disable --port 5432 --driver postgres --limit 50
@@ -167,6 +169,15 @@ dblab --host localhost --user myuser --db users --pass password --schema myschem
167169
dblab --url postgres://user:password@host:port/database?sslmode=[mode] --schema myschema
168170
```
169171

172+
**Oracle**
173+
174+
```{ .sh .copy }
175+
dblab --host localhost --user user2 --db FREEPDB1 --pass password --port 1521 --driver oracle --limit 50 --schema user1
176+
```
177+
```{ .sh .copy }
178+
dblab --url 'oracle://user2:password@localhost:1521/FREEPDB1' --schema user1
179+
```
180+
170181
As a request made in [#125](https://github.com/danvergara/dblab/issues/125), support for MySQL/MariaDB sockets was integrated.
171182

172183
```{ .sh .copy }
@@ -300,9 +311,10 @@ database:
300311
- name: "oracle"
301312
host: "localhost"
302313
port: 1521
303-
db: "FREEPDB1 "
314+
db: "FREEPDB1"
315+
schema: "user1"
304316
password: "password"
305-
user: "system"
317+
user: "user2"
306318
driver: "oracle"
307319
ssl: "enable"
308320
wallet: "path/to/wallet"

pkg/client/client.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ func New(opts command.Options) (*Client, error) {
6464
}
6565

6666
if opts.Schema == "" {
67-
c.schema = "public"
67+
switch c.driver {
68+
case drivers.Postgres, drivers.PostgreSQL, drivers.PostgresSSH:
69+
c.schema = "public"
70+
}
6871
} else {
6972
c.schema = opts.Schema
7073
}
@@ -78,7 +81,7 @@ func New(opts command.Options) (*Client, error) {
7881
case drivers.SQLite:
7982
c.databaseQuerier = newSQLite()
8083
case drivers.Oracle:
81-
c.databaseQuerier = newOracle()
84+
c.databaseQuerier = newOracle(c.schema)
8285
case drivers.SQLServer:
8386
c.databaseQuerier = newMSSQL()
8487
default:
@@ -107,9 +110,15 @@ func New(opts command.Options) (*Client, error) {
107110

108111
switch c.driver {
109112
case drivers.PostgreSQL, drivers.Postgres, drivers.PostgresSSH:
110-
if _, err = db.Exec(fmt.Sprintf("set search_path='%s'", c.schema)); err != nil {
113+
if _, err = db.Exec(fmt.Sprintf("set search_path = '%s'", c.schema)); err != nil {
111114
return nil, err
112115
}
116+
case drivers.Oracle:
117+
if c.schema != "" {
118+
if _, err = db.Exec(fmt.Sprintf("ALTER SESSION SET CURRENT_SCHEMA = %s", c.schema)); err != nil {
119+
return nil, err
120+
}
121+
}
113122
}
114123

115124
pm, err := pagination.New(c.limit, 0, "")

pkg/client/oracle.go

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,68 @@ import (
88
)
99

1010
// oracle struct is in charge of perform all the oracle related queries.
11-
type oracle struct{}
11+
type oracle struct {
12+
schema string
13+
}
1214

1315
// a validation to see if oracle is implementing databaseQuerier.
1416
var _ databaseQuerier = (*oracle)(nil)
1517

1618
// returns a pointer to a oracle struct, it receives an schema as a parameter.
17-
func newOracle() *oracle {
18-
o := oracle{}
19+
func newOracle(schema string) *oracle {
20+
// If the schema is no empty, the client queries against the ALL_* tables,
21+
// where the OWNER is equal to the schema/user the dblab user has access to.
22+
// Otherwise, the client will query against the USER_* tables,
23+
// meaning it only cares about what the user has direct access to.
24+
o := oracle{schema: schema}
1925

2026
return &o
2127
}
2228

23-
func (p *oracle) ShowTablesPerDB(dabase string) (string, []interface{}, error) {
29+
func (o *oracle) ShowTablesPerDB(dabase string) (string, []interface{}, error) {
2430
return "", nil, nil
2531
}
2632

27-
func (p *oracle) ShowDatabases() (string, []interface{}, error) {
33+
func (o *oracle) ShowDatabases() (string, []interface{}, error) {
2834
return "", nil, nil
2935
}
3036

3137
// ShowTables returns a query to retrieve all the tables.
32-
func (p *oracle) ShowTables() (string, []interface{}, error) {
33-
query := "SELECT table_name FROM user_tables"
34-
return query, nil, nil
38+
func (o *oracle) ShowTables() (string, []interface{}, error) {
39+
var query sq.SelectBuilder
40+
41+
query = sq.Select("TABLE_NAME").
42+
From("USER_TABLES")
43+
44+
if o.schema != "" {
45+
query = sq.Select("TABLE_NAME").
46+
From("ALL_TABLES").
47+
Where(sq.Eq{"OWNER": strings.ToUpper(o.schema)})
48+
}
49+
50+
sql, args, err := query.OrderBy("1").PlaceholderFormat(sq.Colon).ToSql()
51+
if err != nil {
52+
return "", nil, err
53+
}
54+
55+
return sql, args, nil
3556
}
3657

3758
// TableStructure returns a query string to get all the relevant information of a given table.
38-
func (p *oracle) TableStructure(tableName string) (string, []interface{}, error) {
39-
query := sq.Select("*").
40-
From("user_tab_columns").
41-
Where(sq.Eq{"table_name": strings.ToUpper(tableName)}).
42-
OrderBy("column_id").
43-
PlaceholderFormat(sq.Colon)
44-
45-
sql, args, err := query.ToSql()
59+
func (o *oracle) TableStructure(tableName string) (string, []interface{}, error) {
60+
var query sq.SelectBuilder
61+
62+
query = sq.Select("*").
63+
From("USER_TAB_COLUMNS").
64+
Where(sq.Eq{"TABLE_NAME": strings.ToUpper(tableName)})
65+
66+
if o.schema != "" {
67+
query = sq.Select("*").
68+
From("ALL_TAB_COLUMNS").
69+
Where(sq.Eq{"TABLE_NAME": strings.ToUpper(tableName), "OWNER": strings.ToUpper(o.schema)})
70+
}
71+
72+
sql, args, err := query.OrderBy("1").PlaceholderFormat(sq.Colon).ToSql()
4673
if err != nil {
4774
return "", nil, err
4875
}
@@ -51,16 +78,26 @@ func (p *oracle) TableStructure(tableName string) (string, []interface{}, error)
5178
}
5279

5380
// Constraints returns all the constraints of a given table.
54-
func (p *oracle) Constraints(tableName string) (string, []interface{}, error) {
55-
query := sq.Select(
56-
`constraint_name`,
57-
`constraint_type`,
81+
func (o *oracle) Constraints(tableName string) (string, []interface{}, error) {
82+
var query sq.SelectBuilder
83+
84+
query = sq.Select(
85+
`CONSTRAINT_NAME`,
86+
`CONSTRAINT_TYPE`,
5887
).
59-
From("user_constraints").
60-
Where(sq.Eq{"table_name": tableName}).
61-
PlaceholderFormat(sq.Colon)
88+
From("USER_CONSTRAINTS").
89+
Where(sq.Eq{"TABLE_NAME": strings.ToUpper(tableName)})
90+
91+
if o.schema != "" {
92+
query = sq.Select(
93+
`CONSTRAINT_NAME`,
94+
`CONSTRAINT_TYPE`,
95+
).
96+
From("ALL_CONSTRAINTS").
97+
Where(sq.Eq{"TABLE_NAME": strings.ToUpper(tableName), "OWNER": strings.ToUpper(o.schema)})
98+
}
6299

63-
sql, args, err := query.ToSql()
100+
sql, args, err := query.PlaceholderFormat(sq.Colon).ToSql()
64101
if err != nil {
65102
return "", nil, err
66103
}
@@ -69,15 +106,23 @@ func (p *oracle) Constraints(tableName string) (string, []interface{}, error) {
69106
}
70107

71108
// Indexes returns the indexes of a table.
72-
func (p *oracle) Indexes(tableName string) (string, []interface{}, error) {
73-
sql, args, err := sq.Select("*").
74-
From("all_indexes").
75-
Where(sq.Eq{"table_name": tableName}).
76-
PlaceholderFormat(sq.Colon).
77-
ToSql()
109+
func (o *oracle) Indexes(tableName string) (string, []interface{}, error) {
110+
var query sq.SelectBuilder
111+
112+
query = sq.Select("*").
113+
From("USER_INDEXES").
114+
Where(sq.Eq{"TABLE_NAME": strings.ToUpper(tableName)})
115+
116+
if o.schema != "" {
117+
query = sq.Select("*").
118+
From("ALL_INDEXES").
119+
Where(sq.Eq{"TABLE_NAME": strings.ToUpper(tableName), "OWNER": strings.ToUpper(o.schema)})
120+
}
121+
122+
sql, args, err := query.PlaceholderFormat(sq.Colon).ToSql()
78123
if err != nil {
79124
return "", nil, err
80125
}
81126

82-
return sql, args, err
127+
return sql, args, nil
83128
}

0 commit comments

Comments
 (0)