Initial support for multiple schemas
This commit is contained in:
4
main.go
4
main.go
@@ -41,8 +41,8 @@ func initClient() {
|
|||||||
exitWithMessage(err.Error())
|
exitWithMessage(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Checking tables...")
|
fmt.Println("Checking database objects...")
|
||||||
_, err = cl.Tables()
|
_, err = cl.Objects()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exitWithMessage(err.Error())
|
exitWithMessage(err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,17 @@ func GetDatabases(c *gin.Context) {
|
|||||||
serveResult(names, err, c)
|
serveResult(names, err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetObjects(c *gin.Context) {
|
||||||
|
result, err := DB(c).Objects()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(400, NewError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
objects := client.ObjectsFromResult(result)
|
||||||
|
c.JSON(200, objects)
|
||||||
|
}
|
||||||
|
|
||||||
func RunQuery(c *gin.Context) {
|
func RunQuery(c *gin.Context) {
|
||||||
query := strings.TrimSpace(c.Request.FormValue("query"))
|
query := strings.TrimSpace(c.Request.FormValue("query"))
|
||||||
|
|
||||||
@@ -135,13 +146,8 @@ func ExplainQuery(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetSchemas(c *gin.Context) {
|
func GetSchemas(c *gin.Context) {
|
||||||
names, err := DB(c).Schemas()
|
res, err := DB(c).Schemas()
|
||||||
serveResult(names, err, c)
|
serveResult(res, err, c)
|
||||||
}
|
|
||||||
|
|
||||||
func GetTables(c *gin.Context) {
|
|
||||||
names, err := DB(c).Tables()
|
|
||||||
serveResult(names, err, c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTable(c *gin.Context) {
|
func GetTable(c *gin.Context) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func SetupRoutes(router *gin.Engine) {
|
|||||||
api.GET("/sequences", GetSequences)
|
api.GET("/sequences", GetSequences)
|
||||||
api.GET("/activity", GetActivity)
|
api.GET("/activity", GetActivity)
|
||||||
api.GET("/schemas", GetSchemas)
|
api.GET("/schemas", GetSchemas)
|
||||||
api.GET("/tables", GetTables)
|
api.GET("/objects", GetObjects)
|
||||||
api.GET("/tables/:table", GetTable)
|
api.GET("/tables/:table", GetTable)
|
||||||
api.GET("/tables/:table/rows", GetTableRows)
|
api.GET("/tables/:table/rows", GetTableRows)
|
||||||
api.GET("/tables/:table/info", GetTableInfo)
|
api.GET("/tables/:table/info", GetTableInfo)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package client
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
|
|
||||||
@@ -28,6 +29,14 @@ type RowsOptions struct {
|
|||||||
SortOrder string // Sort direction (ASC, DESC)
|
SortOrder string // Sort direction (ASC, DESC)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getSchemaAndTable(str string) (string, string) {
|
||||||
|
chunks := strings.Split(str, ".")
|
||||||
|
if len(chunks) == 1 {
|
||||||
|
return "public", chunks[0]
|
||||||
|
}
|
||||||
|
return chunks[0], chunks[1]
|
||||||
|
}
|
||||||
|
|
||||||
func New() (*Client, error) {
|
func New() (*Client, error) {
|
||||||
str, err := connection.BuildString(command.Opts)
|
str, err := connection.BuildString(command.Opts)
|
||||||
|
|
||||||
@@ -89,16 +98,18 @@ func (client *Client) Schemas() ([]string, error) {
|
|||||||
return client.fetchRows(statements.PG_SCHEMAS)
|
return client.fetchRows(statements.PG_SCHEMAS)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Tables() ([]string, error) {
|
func (client *Client) Objects() (*Result, error) {
|
||||||
return client.fetchRows(statements.PG_TABLES)
|
return client.query(statements.PG_OBJECTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Table(table string) (*Result, error) {
|
func (client *Client) Table(table string) (*Result, error) {
|
||||||
return client.query(statements.PG_TABLE_SCHEMA, table)
|
schema, table := getSchemaAndTable(table)
|
||||||
|
return client.query(statements.PG_TABLE_SCHEMA, schema, table)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) TableRows(table string, opts RowsOptions) (*Result, error) {
|
func (client *Client) TableRows(table string, opts RowsOptions) (*Result, error) {
|
||||||
sql := fmt.Sprintf(`SELECT * FROM "%s"`, table)
|
schema, table := getSchemaAndTable(table)
|
||||||
|
sql := fmt.Sprintf(`SELECT * FROM "%s"."%s"`, schema, table)
|
||||||
|
|
||||||
if opts.Where != "" {
|
if opts.Where != "" {
|
||||||
sql += fmt.Sprintf(" WHERE %s", opts.Where)
|
sql += fmt.Sprintf(" WHERE %s", opts.Where)
|
||||||
@@ -124,7 +135,8 @@ func (client *Client) TableRows(table string, opts RowsOptions) (*Result, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) TableRowsCount(table string, opts RowsOptions) (*Result, error) {
|
func (client *Client) TableRowsCount(table string, opts RowsOptions) (*Result, error) {
|
||||||
sql := fmt.Sprintf(`SELECT COUNT(1) FROM "%s"`, table)
|
schema, table := getSchemaAndTable(table)
|
||||||
|
sql := fmt.Sprintf(`SELECT COUNT(1) FROM "%s"."%s"`, schema, table)
|
||||||
|
|
||||||
if opts.Where != "" {
|
if opts.Where != "" {
|
||||||
sql += fmt.Sprintf(" WHERE %s", opts.Where)
|
sql += fmt.Sprintf(" WHERE %s", opts.Where)
|
||||||
@@ -138,7 +150,8 @@ func (client *Client) TableInfo(table string) (*Result, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) TableIndexes(table string) (*Result, error) {
|
func (client *Client) TableIndexes(table string) (*Result, error) {
|
||||||
res, err := client.query(statements.PG_TABLE_INDEXES, table)
|
schema, table := getSchemaAndTable(table)
|
||||||
|
res, err := client.query(statements.PG_TABLE_INDEXES, schema, table)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -148,7 +161,8 @@ func (client *Client) TableIndexes(table string) (*Result, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) TableConstraints(table string) (*Result, error) {
|
func (client *Client) TableConstraints(table string) (*Result, error) {
|
||||||
res, err := client.query(statements.PG_TABLE_CONSTRAINTS, table)
|
schema, table := getSchemaAndTable(table)
|
||||||
|
res, err := client.query(statements.PG_TABLE_CONSTRAINTS, schema, table)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ var (
|
|||||||
testCommands map[string]string
|
testCommands map[string]string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func mapKeys(data map[string]*Objects) []string {
|
||||||
|
result := []string{}
|
||||||
|
for k, _ := range data {
|
||||||
|
result = append(result, k)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func setupCommands() {
|
func setupCommands() {
|
||||||
testCommands = map[string]string{
|
testCommands = map[string]string{
|
||||||
"createdb": "createdb",
|
"createdb": "createdb",
|
||||||
@@ -112,10 +120,11 @@ func test_Databases(t *testing.T) {
|
|||||||
assert.Contains(t, res, "postgres")
|
assert.Contains(t, res, "postgres")
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_Tables(t *testing.T) {
|
func test_Objects(t *testing.T) {
|
||||||
res, err := testClient.Tables()
|
res, err := testClient.Objects()
|
||||||
|
objects := ObjectsFromResult(res)
|
||||||
|
|
||||||
expected := []string{
|
tables := []string{
|
||||||
"alternate_stock",
|
"alternate_stock",
|
||||||
"authors",
|
"authors",
|
||||||
"book_backup",
|
"book_backup",
|
||||||
@@ -132,19 +141,21 @@ func test_Tables(t *testing.T) {
|
|||||||
"my_list",
|
"my_list",
|
||||||
"numeric_values",
|
"numeric_values",
|
||||||
"publishers",
|
"publishers",
|
||||||
"recent_shipments",
|
|
||||||
"schedules",
|
"schedules",
|
||||||
"shipments",
|
"shipments",
|
||||||
"states",
|
"states",
|
||||||
"stock",
|
"stock",
|
||||||
"stock_backup",
|
"stock_backup",
|
||||||
"stock_view",
|
|
||||||
"subjects",
|
"subjects",
|
||||||
"text_sorting",
|
"text_sorting",
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
assert.Equal(t, expected, res)
|
assert.Equal(t, []string{"schema", "name", "type", "owner"}, res.Columns)
|
||||||
|
assert.Equal(t, []string{"public"}, mapKeys(objects))
|
||||||
|
assert.Equal(t, tables, objects["public"].Tables)
|
||||||
|
assert.Equal(t, []string{"recent_shipments", "stock_view"}, objects["public"].Views)
|
||||||
|
assert.Equal(t, []string{"author_ids", "book_ids", "shipments_ship_id_seq", "subject_ids"}, objects["public"].Sequences)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_Table(t *testing.T) {
|
func test_Table(t *testing.T) {
|
||||||
@@ -284,7 +295,7 @@ func TestAll(t *testing.T) {
|
|||||||
test_Test(t)
|
test_Test(t)
|
||||||
test_Info(t)
|
test_Info(t)
|
||||||
test_Databases(t)
|
test_Databases(t)
|
||||||
test_Tables(t)
|
test_Objects(t)
|
||||||
test_Table(t)
|
test_Table(t)
|
||||||
test_TableRows(t)
|
test_TableRows(t)
|
||||||
test_TableInfo(t)
|
test_TableInfo(t)
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ type Result struct {
|
|||||||
Rows []Row `json:"rows"`
|
Rows []Row `json:"rows"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Objects struct {
|
||||||
|
Tables []string `json:"tables"`
|
||||||
|
Views []string `json:"views"`
|
||||||
|
Sequences []string `json:"sequences"`
|
||||||
|
}
|
||||||
|
|
||||||
// Due to big int number limitations in javascript, numbers should be encoded
|
// Due to big int number limitations in javascript, numbers should be encoded
|
||||||
// as strings so they could be properly loaded on the frontend.
|
// as strings so they could be properly loaded on the frontend.
|
||||||
func (res *Result) PrepareBigints() {
|
func (res *Result) PrepareBigints() {
|
||||||
@@ -98,3 +104,32 @@ func (res *Result) JSON() []byte {
|
|||||||
data, _ := json.Marshal(res.Format())
|
data, _ := json.Marshal(res.Format())
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ObjectsFromResult(res *Result) map[string]*Objects {
|
||||||
|
objects := map[string]*Objects{}
|
||||||
|
|
||||||
|
for _, row := range res.Rows {
|
||||||
|
schema := row[0].(string)
|
||||||
|
name := row[1].(string)
|
||||||
|
object_type := row[2].(string)
|
||||||
|
|
||||||
|
if objects[schema] == nil {
|
||||||
|
objects[schema] = &Objects{
|
||||||
|
Tables: []string{},
|
||||||
|
Views: []string{},
|
||||||
|
Sequences: []string{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch object_type {
|
||||||
|
case "table":
|
||||||
|
objects[schema].Tables = append(objects[schema].Tables, name)
|
||||||
|
case "view":
|
||||||
|
objects[schema].Views = append(objects[schema].Views, name)
|
||||||
|
case "sequence":
|
||||||
|
objects[schema].Sequences = append(objects[schema].Sequences, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return objects
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,49 +1,110 @@
|
|||||||
package statements
|
package statements
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PG_DATABASES = `SELECT datname FROM pg_database WHERE NOT datistemplate ORDER BY datname ASC`
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
PG_SCHEMAS = `SELECT schema_name FROM information_schema.schemata ORDER BY schema_name ASC`
|
PG_DATABASES = `
|
||||||
|
SELECT
|
||||||
|
datname
|
||||||
|
FROM
|
||||||
|
pg_database
|
||||||
|
WHERE
|
||||||
|
NOT datistemplate
|
||||||
|
ORDER BY
|
||||||
|
datname ASC`
|
||||||
|
|
||||||
PG_INFO = `SELECT
|
// ---------------------------------------------------------------------------
|
||||||
session_user
|
|
||||||
, current_user
|
|
||||||
, current_database()
|
|
||||||
, current_schemas(false)
|
|
||||||
, inet_client_addr()
|
|
||||||
, inet_client_port()
|
|
||||||
, inet_server_addr()
|
|
||||||
, inet_server_port()
|
|
||||||
, version()`
|
|
||||||
|
|
||||||
PG_TABLE_INDEXES = `SELECT indexname, indexdef FROM pg_indexes WHERE tablename = $1`
|
PG_SCHEMAS = `
|
||||||
|
SELECT
|
||||||
|
schema_name
|
||||||
|
FROM
|
||||||
|
information_schema.schemata
|
||||||
|
ORDER BY
|
||||||
|
schema_name ASC`
|
||||||
|
|
||||||
PG_TABLE_CONSTRAINTS = `SELECT
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PG_INFO = `
|
||||||
|
SELECT
|
||||||
|
session_user,
|
||||||
|
current_user,
|
||||||
|
current_database(),
|
||||||
|
current_schemas(false),
|
||||||
|
inet_client_addr(),
|
||||||
|
inet_client_port(),
|
||||||
|
inet_server_addr(),
|
||||||
|
inet_server_port(),
|
||||||
|
version()`
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PG_TABLE_INDEXES = `
|
||||||
|
SELECT
|
||||||
|
indexname, indexdef
|
||||||
|
FROM
|
||||||
|
pg_indexes
|
||||||
|
WHERE
|
||||||
|
schemaname = $1 AND
|
||||||
|
tablename = $2`
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PG_TABLE_CONSTRAINTS = `
|
||||||
|
SELECT
|
||||||
pg_get_constraintdef(c.oid, true) as condef
|
pg_get_constraintdef(c.oid, true) as condef
|
||||||
FROM pg_constraint c
|
FROM
|
||||||
JOIN pg_namespace n ON n.oid = c.connamespace
|
pg_constraint c
|
||||||
JOIN pg_class cl ON cl.oid = c.conrelid
|
JOIN
|
||||||
WHERE n.nspname = 'public'
|
pg_namespace n ON n.oid = c.connamespace
|
||||||
AND relname = $1
|
JOIN
|
||||||
ORDER BY contype desc`
|
pg_class cl ON cl.oid = c.conrelid
|
||||||
|
WHERE
|
||||||
|
n.nspname = $1 AND
|
||||||
|
relname = $2
|
||||||
|
ORDER BY
|
||||||
|
contype desc`
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
PG_TABLE_INFO = `SELECT
|
PG_TABLE_INFO = `
|
||||||
pg_size_pretty(pg_table_size($1)) AS data_size
|
SELECT
|
||||||
, pg_size_pretty(pg_indexes_size($1)) AS index_size
|
pg_size_pretty(pg_table_size($1)) AS data_size,
|
||||||
, pg_size_pretty(pg_total_relation_size($1)) AS total_size
|
pg_size_pretty(pg_indexes_size($1)) AS index_size,
|
||||||
, (SELECT reltuples FROM pg_class WHERE oid = $1::regclass) AS rows_count`
|
pg_size_pretty(pg_total_relation_size($1)) AS total_size,
|
||||||
|
(SELECT reltuples FROM pg_class WHERE oid = $1::regclass) AS rows_count`
|
||||||
|
|
||||||
PG_TABLE_SCHEMA = `SELECT
|
// ---------------------------------------------------------------------------
|
||||||
column_name, data_type, is_nullable, character_maximum_length, character_set_catalog, column_default
|
|
||||||
FROM information_schema.columns
|
|
||||||
WHERE table_name = $1`
|
|
||||||
|
|
||||||
PG_TABLES = `SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_schema,table_name`
|
PG_TABLE_SCHEMA = `
|
||||||
|
SELECT
|
||||||
|
column_name,
|
||||||
|
data_type,
|
||||||
|
is_nullable,
|
||||||
|
character_maximum_length,
|
||||||
|
character_set_catalog,
|
||||||
|
column_default
|
||||||
|
FROM
|
||||||
|
information_schema.columns
|
||||||
|
WHERE
|
||||||
|
table_schema = $1 AND
|
||||||
|
table_name = $2`
|
||||||
|
|
||||||
PG_SEQUENCES = `SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema = 'public' ORDER BY sequence_name`
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
PG_ACTIVITY = `SELECT
|
PG_SEQUENCES = `
|
||||||
|
SELECT
|
||||||
|
sequence_name
|
||||||
|
FROM
|
||||||
|
information_schema.sequences
|
||||||
|
WHERE
|
||||||
|
sequence_schema = 'public'
|
||||||
|
ORDER BY sequence_name`
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PG_ACTIVITY = `
|
||||||
|
SELECT
|
||||||
datname,
|
datname,
|
||||||
query,
|
query,
|
||||||
state,
|
state,
|
||||||
@@ -54,6 +115,34 @@ WHERE table_name = $1`
|
|||||||
datid,
|
datid,
|
||||||
application_name,
|
application_name,
|
||||||
client_addr
|
client_addr
|
||||||
FROM pg_stat_activity
|
FROM
|
||||||
WHERE state IS NOT NULL`
|
pg_stat_activity
|
||||||
|
WHERE
|
||||||
|
state IS NOT NULL`
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PG_OBJECTS = `
|
||||||
|
SELECT
|
||||||
|
n.nspname as "schema",
|
||||||
|
c.relname as "name",
|
||||||
|
CASE c.relkind
|
||||||
|
WHEN 'r' THEN 'table'
|
||||||
|
WHEN 'v' THEN 'view'
|
||||||
|
WHEN 'm' THEN 'materialized_view'
|
||||||
|
WHEN 'i' THEN 'index'
|
||||||
|
WHEN 'S' THEN 'sequence'
|
||||||
|
WHEN 's' THEN 'special'
|
||||||
|
WHEN 'f' THEN 'foreign_table'
|
||||||
|
END as "type",
|
||||||
|
pg_catalog.pg_get_userbyid(c.relowner) as "owner"
|
||||||
|
FROM
|
||||||
|
pg_catalog.pg_class c
|
||||||
|
LEFT JOIN
|
||||||
|
pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||||
|
WHERE
|
||||||
|
c.relkind IN ('r','v','S','s','') AND
|
||||||
|
n.nspname !~ '^pg_toast' AND
|
||||||
|
n.nspname NOT IN ('information_schema', 'pg_catalog')
|
||||||
|
ORDER BY 1, 2`
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar div.tables-list #tables, #sequences {
|
#sidebar div.tables-list #tables, #sequences, #objects {
|
||||||
padding: 50px 0 0;
|
padding: 50px 0 0;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar ul {
|
/*#sidebar ul {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: auto;
|
width: auto;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
#body {
|
#body {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -288,6 +288,9 @@
|
|||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#objects {
|
||||||
|
}
|
||||||
|
|
||||||
#output {
|
#output {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
@@ -565,6 +568,90 @@
|
|||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Sidebar Schema Objects */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.schema {}
|
||||||
|
.schema i { display: inline-block; margin-right: 4px; }
|
||||||
|
.schema i.fa-folder-o { display: inline-block; }
|
||||||
|
.schema i.fa-folder-open-o { display: none; }
|
||||||
|
.schema.expanded i.fa-folder-open-o { display: inline-block; }
|
||||||
|
.schema.expanded i.fa-folder-o { display: none; }
|
||||||
|
|
||||||
|
.schema .schema-name {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 13px;
|
||||||
|
display: block;
|
||||||
|
line-height: 30px;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0px 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema.expanded .schema-container {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container .schema-group .fa-chevron-down {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container .schema-group .schema-group-title {
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 30px;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container .schema-group ul {
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container .schema-group ul li {
|
||||||
|
list-style: none;
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0px;
|
||||||
|
line-height: 30px;
|
||||||
|
height: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0px 8px;
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container .schema-group ul li i {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container .schema-group ul li.active {
|
||||||
|
background: #dedede !important;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container .schema-group ul li:hover {
|
||||||
|
background: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container .schema-group.expanded .fa-chevron-down {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container .schema-group.expanded .fa-chevron-right {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema .schema-container .schema-group.expanded ul {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Ace Customizations */
|
/* Ace Customizations */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|||||||
@@ -38,8 +38,7 @@
|
|||||||
<i class="fa fa-database"></i> <span class="current-database" id="current_database"></span>
|
<i class="fa fa-database"></i> <span class="current-database" id="current_database"></span>
|
||||||
<span class="refresh" id="refresh_tables" title="Refresh tables list"><i class="fa fa-refresh"></i></span>
|
<span class="refresh" id="refresh_tables" title="Refresh tables list"><i class="fa fa-refresh"></i></span>
|
||||||
</div>
|
</div>
|
||||||
<ul id="tables"></ul>
|
<div id="objects"></div>
|
||||||
<ul id="sequences"></ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-information">
|
<div class="table-information">
|
||||||
|
|||||||
158
static/js/app.js
158
static/js/app.js
@@ -1,7 +1,8 @@
|
|||||||
var editor;
|
var editor = null;
|
||||||
var connected = false;
|
var connected = false;
|
||||||
var bookmarks = {};
|
var bookmarks = {};
|
||||||
var default_rows_limit = 100;
|
var default_rows_limit = 100;
|
||||||
|
var currentTable = null;
|
||||||
|
|
||||||
var filterOptions = {
|
var filterOptions = {
|
||||||
"equal": "= 'DATA'",
|
"equal": "= 'DATA'",
|
||||||
@@ -75,6 +76,7 @@ function apiCall(method, path, params, cb) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getObjects(cb) { apiCall("get", "/objects", {}, cb); }
|
||||||
function getTables(cb) { apiCall("get", "/tables", {}, cb); }
|
function getTables(cb) { apiCall("get", "/tables", {}, cb); }
|
||||||
function getTableRows(table, opts, cb) { apiCall("get", "/tables/" + table + "/rows", opts, cb); }
|
function getTableRows(table, opts, cb) { apiCall("get", "/tables/" + table + "/rows", opts, cb); }
|
||||||
function getTableStructure(table, cb) { apiCall("get", "/tables/" + table, {}, cb); }
|
function getTableStructure(table, cb) { apiCall("get", "/tables/" + table, {}, cb); }
|
||||||
@@ -83,36 +85,70 @@ function getTableConstraints(table, cb) { apiCall("get", "/tables/" + table + "/
|
|||||||
function getHistory(cb) { apiCall("get", "/history", {}, cb); }
|
function getHistory(cb) { apiCall("get", "/history", {}, cb); }
|
||||||
function getBookmarks(cb) { apiCall("get", "/bookmarks", {}, cb); }
|
function getBookmarks(cb) { apiCall("get", "/bookmarks", {}, cb); }
|
||||||
function getSequences(cb) { apiCall("get", "/sequences", {}, cb); }
|
function getSequences(cb) { apiCall("get", "/sequences", {}, cb); }
|
||||||
|
function executeQuery(query, cb) { apiCall("post", "/query", { query: query }, cb); }
|
||||||
|
function explainQuery(query, cb) { apiCall("post", "/explain", { query: query }, cb); }
|
||||||
|
|
||||||
function encodeQuery(query) {
|
function encodeQuery(query) {
|
||||||
return window.btoa(query);
|
return window.btoa(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeQuery(query, cb) {
|
function buildSchemaSection(name, objects) {
|
||||||
apiCall("post", "/query", { query: query }, cb);
|
var section = "";
|
||||||
|
|
||||||
|
var titles = {
|
||||||
|
"tables": "Tables",
|
||||||
|
"views": "Views",
|
||||||
|
"sequences": "Sequences"
|
||||||
|
};
|
||||||
|
|
||||||
|
var icons = {
|
||||||
|
"tables": '<i class="fa fa-table"></i>',
|
||||||
|
"views": '<i class="fa fa-table"></i>',
|
||||||
|
"sequences": '<i class="fa fa-circle-o"></i>'
|
||||||
|
};
|
||||||
|
|
||||||
|
var klass = "";
|
||||||
|
if (name == "public") klass = "expanded";
|
||||||
|
|
||||||
|
section += "<div class='schema " + klass + "'>";
|
||||||
|
section += "<div class='schema-name'><i class='fa fa-folder-o'></i><i class='fa fa-folder-open-o'></i> " + name + "</div>";
|
||||||
|
section += "<div class='schema-container'>";
|
||||||
|
|
||||||
|
for (group of ["tables", "views", "sequences"]) {
|
||||||
|
if (objects[group].length == 0) continue;
|
||||||
|
|
||||||
|
group_klass = "";
|
||||||
|
if (name == "public" && group == "tables") group_klass = "expanded";
|
||||||
|
|
||||||
|
section += "<div class='schema-group " + group_klass + "'>";
|
||||||
|
section += "<div class='schema-group-title'><i class='fa fa-chevron-right'></i><i class='fa fa-chevron-down'></i> " + titles[group] + " (" + objects[group].length + ")</div>";
|
||||||
|
section += "<ul>"
|
||||||
|
|
||||||
|
for (item of objects[group]) {
|
||||||
|
var id = name + "." + item;
|
||||||
|
section += "<li class='schema-" + group + "' data-type='" + group + "' data-id='" + id + "'>" + icons[group] + " " + item + "</li>";
|
||||||
|
}
|
||||||
|
section += "</ul></div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
function explainQuery(query, cb) {
|
section += "</div></div>";
|
||||||
apiCall("post", "/explain", { query: query }, cb);
|
|
||||||
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadTables() {
|
function loadSchemas() {
|
||||||
$("#tables li").remove();
|
$("#objects").html("");
|
||||||
|
|
||||||
getTables(function(data) {
|
getObjects(function(data) {
|
||||||
data.forEach(function(item) {
|
for (schema in data) {
|
||||||
$("<li><span><i class='fa fa-table'></i> " + item + " </span></li>").appendTo("#tables");
|
$(buildSchemaSection(schema, data[schema])).appendTo("#objects");
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadSequences() {
|
if (Object.keys(data).length == 1) {
|
||||||
$("#sequences li").remove();
|
$(".schema").addClass("expanded");
|
||||||
|
}
|
||||||
|
|
||||||
getSequences(function(data) {
|
bindContextMenus();
|
||||||
data.forEach(function(item) {
|
|
||||||
$("<li><span><i class='fa fa-circle-o'></i> " + item + " </span></li>").appendTo("#sequences");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +167,7 @@ function unescapeHtml(str){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentTable() {
|
function getCurrentTable() {
|
||||||
return $("#tables").attr("data-current") || "";
|
return currentTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetTable() {
|
function resetTable() {
|
||||||
@@ -158,8 +194,7 @@ function performTableAction(table, action, el) {
|
|||||||
case "delete":
|
case "delete":
|
||||||
executeQuery("DROP TABLE " + table, function(data) {
|
executeQuery("DROP TABLE " + table, function(data) {
|
||||||
if (data.error) alert(data.error);
|
if (data.error) alert(data.error);
|
||||||
loadTables();
|
loadSchemas();
|
||||||
loadSequences();
|
|
||||||
resetTable();
|
resetTable();
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@@ -459,8 +494,7 @@ function runQuery() {
|
|||||||
|
|
||||||
// Refresh tables list if table was added or removed
|
// Refresh tables list if table was added or removed
|
||||||
if (query.match(re)) {
|
if (query.match(re)) {
|
||||||
loadTables();
|
loadSchemas();
|
||||||
loadSequences();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -639,6 +673,21 @@ function getConnectionString() {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bindContextMenus() {
|
||||||
|
$(".schema-group ul").each(function(id, el) {
|
||||||
|
$(el).contextmenu({
|
||||||
|
target: "#tables_context_menu",
|
||||||
|
scopes: "li.schema-tables",
|
||||||
|
onItem: function(context, e) {
|
||||||
|
var el = $(e.target);
|
||||||
|
var table = $(context[0]).data("id");
|
||||||
|
var action = el.data("action");
|
||||||
|
performTableAction(table, action, el);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$("#table_content").on("click", function() { showTableContent(); });
|
$("#table_content").on("click", function() { showTableContent(); });
|
||||||
$("#table_structure").on("click", function() { showTableStructure(); });
|
$("#table_structure").on("click", function() { showTableStructure(); });
|
||||||
@@ -674,6 +723,26 @@ $(document).ready(function() {
|
|||||||
$(this).addClass("selected");
|
$(this).addClass("selected");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#objects").on("click", ".schema-group-title", function(e) {
|
||||||
|
$(this).parent().toggleClass("expanded");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#objects").on("click", ".schema-name", function(e) {
|
||||||
|
$(this).parent().toggleClass("expanded");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#objects").on("click", "li", function(e) {
|
||||||
|
currentTable = $(this).data("id");
|
||||||
|
|
||||||
|
$("#objects li").removeClass("active");
|
||||||
|
$(this).addClass("active");
|
||||||
|
$(".current-page").data("page", 1);
|
||||||
|
$(".filters select, .filters input").val("");
|
||||||
|
|
||||||
|
showTableInfo();
|
||||||
|
showTableContent();
|
||||||
|
});
|
||||||
|
|
||||||
$("#results").on("click", "th", function(e) {
|
$("#results").on("click", "th", function(e) {
|
||||||
var sortColumn = this.attributes['data'].value;
|
var sortColumn = this.attributes['data'].value;
|
||||||
var contentTab = $('#table_content').hasClass('selected');
|
var contentTab = $('#table_content').hasClass('selected');
|
||||||
@@ -712,43 +781,8 @@ $(document).ready(function() {
|
|||||||
$(this).html(textarea).css("max-height", "200px");
|
$(this).html(textarea).css("max-height", "200px");
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#tables").on("click", "li", function() {
|
|
||||||
$("#tables li.selected").removeClass("selected");
|
|
||||||
$("#sequences li.selected").removeClass("selected");
|
|
||||||
$(this).addClass("selected");
|
|
||||||
$("#tables").attr("data-current", $.trim($(this).text()));
|
|
||||||
$(".current-page").data("page", 1);
|
|
||||||
$(".filters select, .filters input").val("");
|
|
||||||
|
|
||||||
showTableContent();
|
|
||||||
showTableInfo();
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#tables").contextmenu({
|
|
||||||
target: "#tables_context_menu",
|
|
||||||
scopes: "li",
|
|
||||||
onItem: function(context, e) {
|
|
||||||
var el = $(e.target);
|
|
||||||
var table = $.trim($(context[0]).text());
|
|
||||||
var action = el.data("action");
|
|
||||||
performTableAction(table, action, el);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#sequences").on("click", "li", function() {
|
|
||||||
$("#tables li.selected").removeClass("selected");
|
|
||||||
$("#sequences li.selected").removeClass("selected");
|
|
||||||
|
|
||||||
$(this).addClass("selected");
|
|
||||||
$("#tables").attr("data-current", $.trim($(this).text()));
|
|
||||||
|
|
||||||
showTableContent();
|
|
||||||
$(".table-information ul").hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#refresh_tables").on("click", function() {
|
$("#refresh_tables").on("click", function() {
|
||||||
loadTables();
|
loadSchemas();
|
||||||
loadSequences();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#rows_filter").on("submit", function(e) {
|
$("#rows_filter").on("submit", function(e) {
|
||||||
@@ -920,8 +954,7 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
connected = true;
|
connected = true;
|
||||||
loadTables();
|
loadSchemas();
|
||||||
loadSequences();
|
|
||||||
|
|
||||||
$("#connection_window").hide();
|
$("#connection_window").hide();
|
||||||
$("#current_database").text(resp.current_database);
|
$("#current_database").text(resp.current_database);
|
||||||
@@ -940,8 +973,7 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
connected = true;
|
connected = true;
|
||||||
loadTables();
|
loadSchemas();
|
||||||
loadSequences();
|
|
||||||
|
|
||||||
$("#current_database").text(resp.current_database);
|
$("#current_database").text(resp.current_database);
|
||||||
$("#main").show();
|
$("#main").show();
|
||||||
|
|||||||
Reference in New Issue
Block a user