From 0cd61093b9cc47e1375b10415175fec16fa9fb99 Mon Sep 17 00:00:00 2001 From: Dan Sosedoff Date: Mon, 19 Dec 2022 13:26:13 -0600 Subject: [PATCH] Display empty schemas on the sidebar (#621) * Display empty schemas on the sidebar * Add schemas fetching test * Skip pg_temp schemas * Exclude pg_temp tables from other queries --- pkg/client/client_test.go | 7 +++ pkg/statements/sql/objects.sql | 4 +- pkg/statements/sql/schemas.sql | 3 ++ static/js/app.js | 80 ++++++++++++++++++++++------------ 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 252c4db..eaa4209 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -234,6 +234,12 @@ func testDatabases(t *testing.T) { assertMatches(t, []string{"booktown", "postgres"}, res) } +func testSchemas(t *testing.T) { + res, err := testClient.Schemas() + assert.NoError(t, err) + assert.Equal(t, []string{"public"}, res) +} + func testObjects(t *testing.T) { res, err := testClient.Objects() objects := ObjectsFromResult(res) @@ -617,6 +623,7 @@ func TestAll(t *testing.T) { testInfo(t) testActivity(t) testDatabases(t) + testSchemas(t) testObjects(t) testTable(t) testTableRows(t) diff --git a/pkg/statements/sql/objects.sql b/pkg/statements/sql/objects.sql index d970710..415624e 100644 --- a/pkg/statements/sql/objects.sql +++ b/pkg/statements/sql/objects.sql @@ -20,7 +20,7 @@ WITH all_objects AS ( pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('r','v','m','S','s','') - AND n.nspname !~ '^pg_toast' + AND n.nspname !~ '^pg_(toast|temp)' AND n.nspname NOT IN ('information_schema', 'pg_catalog') AND has_schema_privilege(n.nspname, 'USAGE') @@ -38,7 +38,7 @@ WITH all_objects AS ( JOIN pg_catalog.pg_proc p ON p.pronamespace = n.oid WHERE - n.nspname !~ '^pg_toast' + n.nspname !~ '^pg_(toast|temp)' AND n.nspname NOT IN ('information_schema', 'pg_catalog') ) SELECT * FROM all_objects diff --git a/pkg/statements/sql/schemas.sql b/pkg/statements/sql/schemas.sql index e68c28d..123349c 100644 --- a/pkg/statements/sql/schemas.sql +++ b/pkg/statements/sql/schemas.sql @@ -2,5 +2,8 @@ SELECT schema_name FROM information_schema.schemata +WHERE + schema_name NOT IN ('information_schema', 'pg_catalog') + AND schema_name !~ '^pg_(toast|temp)' ORDER BY schema_name ASC diff --git a/static/js/app.js b/static/js/app.js index 1b853b8..62213c6 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -86,6 +86,7 @@ function apiCall(method, path, params, cb) { function getInfo(cb) { apiCall("get", "/info", {}, cb); } function getConnection(cb) { apiCall("get", "/connection", {}, cb); } +function getSchemas(cb) { apiCall("get", "/schemas", {}, cb); } function getObjects(cb) { apiCall("get", "/objects", {}, cb); } function getTables(cb) { apiCall("get", "/tables", {}, cb); } function getTableRows(table, opts, cb) { apiCall("get", "/tables/" + table + "/rows", opts, cb); } @@ -161,44 +162,65 @@ function buildSchemaSection(name, objects) { function loadSchemas() { $("#objects").html(""); - getObjects(function(data) { - if (Object.keys(data).length == 0) { - data["public"] = { - table: [], - view: [], - materialized_view: [], - function: [], - sequence: [] - }; + var emptyObjectList = function() { + return { + table: [], + view: [], + materialized_view: [], + function: [], + sequence: [] + } + } + + getSchemas(function(schemasData) { + if (schemasData.error) { + alert("Error while fetching schemas: " + schemasData.error); + return; } - for (schema in data) { - $(buildSchemaSection(schema, data[schema])).appendTo("#objects"); - } + getObjects(function(data) { + if (data.error) { + alert("Error while fetching database objects: " + data.error); + return; + } - if (Object.keys(data).length == 1) { - $(".schema").addClass("expanded"); - } + if (Object.keys(data).length == 0) { + data["public"] = emptyObjectList(); + } - // Clear out all autocomplete objects - autocompleteObjects = []; - for (schema in data) { - for (kind in data[schema]) { - if (!(kind == "table" || kind == "view" || kind == "materialized_view" || kind == "function")) { - continue + for (schemaName of schemasData) { + // Allow users to see empty schemas if we dont have any objects in them + if (!data[schemaName]) { + data[schemaName] = emptyObjectList(); } - for (item in data[schema][kind]) { - autocompleteObjects.push({ - caption: data[schema][kind][item].name, - value: data[schema][kind][item].name, - meta: kind - }); + $(buildSchemaSection(schemaName, data[schemaName])).appendTo("#objects"); + } + + if (Object.keys(data).length == 1) { + $(".schema").addClass("expanded"); + } + + // Clear out all autocomplete objects + autocompleteObjects = []; + for (schema in data) { + for (kind in data[schema]) { + if (!(kind == "table" || kind == "view" || kind == "materialized_view" || kind == "function")) { + continue + } + + for (item in data[schema][kind]) { + autocompleteObjects.push({ + caption: data[schema][kind][item].name, + value: data[schema][kind][item].name, + meta: kind + }); + } } } - } - bindContextMenus(); + bindContextMenus(); + }); }); }