diff --git a/res/gtk/icons/category-search-symbolic.svg b/res/gtk/icons/category-search-symbolic.svg new file mode 100644 index 0000000..e4de46c --- /dev/null +++ b/res/gtk/icons/category-search-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/gtk/query-view.blp b/res/gtk/query-view.blp index f2eab86..82b461b 100644 --- a/res/gtk/query-view.blp +++ b/res/gtk/query-view.blp @@ -14,7 +14,7 @@ template $PsequelQueryView : Adw.Bin { Box { orientation: vertical; - width-request: 280; + width-request: 300; Box { margin-start: 8; @@ -40,58 +40,137 @@ template $PsequelQueryView : Adw.Bin { Separator {} - Box { + Adw.ViewSwitcher tables_views_switcher { + + styles ["big-icon"] + height-request: 40; margin-start: 8; margin-end: 8; - margin-top: 4; - margin-bottom: 8; - - Label { - styles ["title-3"] - margin-top: 8; - margin-start: 4; - halign: start; - margin-bottom: 0; - label: "Tables"; - } - - Separator { - hexpand: true; - styles ["spacer"] - } - - ToggleButton search_btn { - styles ["flat"] - tooltip-text: "Search tables"; - icon-name: "loupe-large-symbolic"; - - toggled => $on_show_search(); - } - + margin-top: 8; + // margin-bottom: 8; + policy: wide; + stack: sql_views; } - - - Revealer { - transition-duration: 300; - reveal-child: bind search_btn.active; - styles ["background"] - SearchEntry search_entry { - placeholder-text: "Search Tables"; - margin-start: 4; - margin-bottom: 4; + Adw.ViewStack sql_views { + hexpand: true; + vexpand: true; + Adw.ViewStackPage { + icon-name: "table-symbolic"; + + child: Box { + orientation: vertical; + Box { + margin-start: 8; + margin-end: 8; + margin-top: 4; + margin-bottom: 8; + + Label { + styles ["title-3"] + margin-top: 8; + margin-start: 4; + halign: start; + margin-bottom: 0; + label: "Tables"; + } + + Separator { + hexpand: true; + styles ["spacer"] + } + + ToggleButton search_btn { + styles ["flat"] + tooltip-text: "Search tables"; + icon-name: "loupe-large-symbolic"; + + toggled => $on_show_search(); + } + } - search-changed => $on_search(); + Revealer { + transition-duration: 300; + reveal-child: bind search_btn.active; + styles ["background"] + SearchEntry search_table_entry { + placeholder-text: "Search Tables"; + margin-start: 4; + margin-bottom: 4; + + search-changed => $on_search(); + } + + } + + ScrolledWindow { + vexpand: true; + ListBox table_list { + styles ["navigation-sidebar"] + selected-rows-changed => $table_selected(); + } + + } + }; } - - } - - ScrolledWindow { - vexpand: true; - ListBox table_list { - styles ["navigation-sidebar"] + + Adw.ViewStackPage { + icon-name: "category-search-symbolic"; + + child: Box { + orientation: vertical; + Box { + margin-start: 8; + margin-end: 8; + margin-top: 4; + margin-bottom: 8; + + Label { + styles ["title-3"] + margin-top: 8; + margin-start: 4; + halign: start; + margin-bottom: 0; + label: "Views"; + } + + Separator { + hexpand: true; + styles ["spacer"] + } + + ToggleButton search_views_btn { + styles ["flat"] + tooltip-text: "Search tables"; + icon-name: "loupe-large-symbolic"; + + toggled => $on_show_view_search(); + } + } + + Revealer { + transition-duration: 300; + reveal-child: bind search_views_btn.active; + styles ["background"] + SearchEntry search_views_entry { + placeholder-text: "Search Views"; + margin-start: 4; + margin-bottom: 4; + + search-changed => $on_view_search(); + } + + } + + ScrolledWindow { + vexpand: true; + ListBox views_list { + styles ["navigation-sidebar"] + selected-rows-changed => $view_selected(); + } + } + }; } - } Box { diff --git a/res/gtk/style.css b/res/gtk/style.css index 69acb06..49d9fce 100644 --- a/res/gtk/style.css +++ b/res/gtk/style.css @@ -1,3 +1,7 @@ .text-bold { font-size: 105%; +} + +.big-icon image { + -gtk-icon-size: 20px; } \ No newline at end of file diff --git a/res/gtk/table-data.blp b/res/gtk/table-data.blp new file mode 100644 index 0000000..e69de29 diff --git a/res/gtk/table-structure.blp b/res/gtk/table-structure.blp index b3b1d58..896f8d0 100644 --- a/res/gtk/table-structure.blp +++ b/res/gtk/table-structure.blp @@ -9,7 +9,6 @@ template $PsequelTableStructure : Gtk.Box { margin-top: 12; margin-bottom: 12; - styles ["frame"] // width-request: 960; // height-request: 800; @@ -19,12 +18,12 @@ template $PsequelTableStructure : Gtk.Box { label: "Columns"; halign: start; } - ColumnView columns { - - show-column-separators: true; - show-row-separators: true; - vexpand: true; - styles ["frame"] + ScrolledWindow { + ColumnView columns { + show-column-separators: true; + show-row-separators: true; + vexpand: true; + } } Label { @@ -32,12 +31,14 @@ template $PsequelTableStructure : Gtk.Box { label: "Indexes"; halign: start; } - ColumnView indexes { + + ScrolledWindow { + ColumnView indexes { - show-column-separators: true; - show-row-separators: true; - styles ["frame"] - vexpand: true; + show-column-separators: true; + show-row-separators: true; + vexpand: true; + } } Label { @@ -45,11 +46,13 @@ template $PsequelTableStructure : Gtk.Box { label: "Foreign Keys"; halign: start; } - ColumnView foreign_key { - show-column-separators: true; - show-row-separators: true; - styles ["frame"] - vexpand: true; + ScrolledWindow { + ColumnView foreign_key { + + show-column-separators: true; + show-row-separators: true; + vexpand: true; + } } } diff --git a/res/gtk/welcome.blp b/res/gtk/welcome.blp index c25c248..c1b02de 100644 --- a/res/gtk/welcome.blp +++ b/res/gtk/welcome.blp @@ -14,7 +14,7 @@ template $PsequelConnectionView : Adw.Bin { [start] $PsequelConnectionSidebar sidebar { - width-request: 280; + width-request: 300; form: form; } diff --git a/res/psequel.gresource.xml b/res/psequel.gresource.xml index fbf43ef..55ba3bd 100644 --- a/res/psequel.gresource.xml +++ b/res/psequel.gresource.xml @@ -22,5 +22,6 @@ gtk/icons/columns-symbolic.svg gtk/icons/step-out-symbolic.svg gtk/icons/arrow-into-box-symbolic.svg + gtk/icons/category-search-symbolic.svg diff --git a/src/application.vala b/src/application.vala index 395b787..2d12dde 100644 --- a/src/application.vala +++ b/src/application.vala @@ -59,7 +59,7 @@ namespace Psequel { } query_service = new QueryService (background); - table_list = new ObservableArrayList (); + table_list = new ObservableArrayList (); signals = new AppSignals (); load_user_data (); diff --git a/src/models/table.vala b/src/models/table.vala index bc87f94..654476e 100644 --- a/src/models/table.vala +++ b/src/models/table.vala @@ -2,43 +2,62 @@ using Postgres; using Gee; namespace Psequel { - public class Table : Object { - // Keep references to result so data is not destroy. - private Result result; + public delegate Relation.Row TransFormsFunc (Relation.Row row); + + public class Relation : Object { + public int rows { get; private set; } - private int cols { get; private set; } + public int cols { get; private set; } - private Header header { get; private set; } - public ArrayList data; + private ArrayList data; + private ArrayList headers; - public Table (owned Result res) { + public Relation (owned Result res) { Object (); - result = (owned) res; - load_data (); + load_data ((owned) res); } - private void load_data () { + private Relation.from_data (ArrayList headers, ArrayList data) { + this.headers = headers; + this.data = data; + this.rows = data.size; + this.cols = headers.size; + } + + private void load_data (owned Result result) { assert_nonnull (result); rows = result.get_n_tuples (); cols = result.get_n_fields (); - header = new Header (); + this.headers = new ArrayList (); for (int i = 0; i < cols; i++) { - header.add_field (result.get_field_name (i)); + headers.add (result.get_field_name (i)); } this.data = new ArrayList (); for (int i = 0; i < rows; i++) { - data.add (new Row (header)); + data.add (new Row ()); for (int j = 0; j < cols; j++) { data[i].add_field (result.get_value (i, j)); } } } + public Relation transform (ArrayList new_headers, TransFormsFunc func) { + + var new_rows = new ArrayList (); + + assert_nonnull (this.data); + foreach (var row in this.data) { + new_rows.add (func (row)); + } + + return new Relation.from_data (new_headers, new_rows); + } + public string to_string () { return @"Table ($rows x $cols)"; } @@ -53,30 +72,39 @@ namespace Psequel { return data.get (index); } + /** * Helper class for ease of use with Table. DO NOT use it outside of Table class. */ public class Row : Object { - private ArrayList data; - private Header header; + private ArrayList data; public int size { get { return data.size; } } - internal Row (Header header) { - this.data = new ArrayList (); - this.header = header; + internal Row () { + this.data = new ArrayList (); } public void add_field (string item) { - assert (data.size < header.size); data.add (item); } - public new unowned string @get (int index) { + public void insert_field (int index, string item) { + data.insert (index, item); + } + + public void remove_at (int index) { + assert (index < size); + assert (index >= 0); + + data.remove_at (index); + } + + public new string @get (int index) { return data.get (index); } @@ -90,33 +118,5 @@ namespace Psequel { return builder.free_and_steal (); } } - - /** - * Helper class for ease of use with Table. DO NOT use it outside of Table class. - */ - public class Header : Object { - private ArrayList data; - - public int size { - get { return data.size; } - } - - internal Header () { - this.data = new ArrayList (); - } - - public void add_field (string item) { - data.add (item); - } - - public new unowned string @get (int index) { - return data.get (index); - } - - public string to_string () { - var row_data = data.fold ((seed, item) => seed + item.dup () + ",", ""); - return row_data; - } - } } } \ No newline at end of file diff --git a/src/services/query_service.vala b/src/services/query_service.vala index bd787d6..febefc8 100644 --- a/src/services/query_service.vala +++ b/src/services/query_service.vala @@ -15,7 +15,7 @@ namespace Psequel { this.background = background; } - public async Table db_schemas () throws PsequelError { + public async Relation db_schemas () throws PsequelError { var stmt = "select schema_name from information_schema.schemata;"; var res = yield exec_query (stmt); @@ -23,7 +23,58 @@ namespace Psequel { return res; } - public async Table db_table_info (string schema, string table_name) throws PsequelError { + public async Relation db_table_fk (string schema, string table_name) throws PsequelError { + string stmt = """ + SELECT conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef + FROM pg_catalog.pg_constraint r + WHERE r.conrelid = $1::regclass AND r.contype = 'f'; + """; + + var params = new ArrayList (); + params.add (new Variant.string (@"$schema.$table_name")); + + var headers = new ArrayList (); + headers.add_all_array ({ + "Key Name", + "Columns", + "Foreign Table", + "Foreign Columns", + "On Update", + "On Delete", + }); + + var raw = yield exec_query_params (stmt, params); + var result = raw.transform (headers, (old_row) => { + var new_row = new Relation.Row (); + new_row.add_field (old_row[0]); + + var fk_def = old_row[1]; + + // Match the index type and column from fk_def + var regex = /FOREIGN KEY \(([$a-zA-Z_, ]+)\) REFERENCES ([a-zA-Z_, ]+)\(([a-zA-Z_, ]+)\)( ON UPDATE (CASCADE))?( ON DELETE (RESTRICT))?/; + MatchInfo match_info; + if (regex.match (fk_def, 0, out match_info)) { + + new_row.add_field (match_info.fetch (1)); + new_row.add_field (match_info.fetch (2)); + new_row.add_field (match_info.fetch (3)); + new_row.add_field (match_info.fetch (5) == "" ? "NO ACTION" : match_info.fetch (5)); + new_row.add_field (match_info.fetch (7) == "" ? "NO ACTION" : match_info.fetch (7)); + } else { + debug ("Regex not match: %s", fk_def); + assert_not_reached (); + } + debug ("%s", new_row.to_string ()); + + return new_row; + }); + + + return result; + + } + + public async Relation db_table_info (string schema, string table_name) throws PsequelError { string stmt = """ SELECT column_name AS "Column Name", data_type AS "Type", @@ -43,7 +94,57 @@ namespace Psequel { return yield exec_query_params (stmt, params); } - public async Table db_tablenames (string schema = "public") throws PsequelError { + public async Relation db_table_indexes (string schema, string table_name) throws PsequelError { + string stmt = """ + SELECT indexname, indexdef FROM pg_indexes + WHERE schemaname = $1 + AND tablename = $2; + """; + + var params = new ArrayList (); + params.add (new Variant.string (schema)); + params.add (new Variant.string (table_name)); + + var headers = new ArrayList (); + headers.add_all_array ({ + "Index Name", + "Unique", + "Type", + "Columns", + }); + + var raw_result = yield exec_query_params (stmt, params); + + var result = raw_result.transform (headers, (old_row) => { + var new_row = new Relation.Row (); + new_row.add_field (old_row[0]); + + var indexdef = old_row[1]; + if (indexdef.contains ("UNIQUE")) { + new_row.add_field ("YES"); + } else { + new_row.add_field ("NO"); + } + + // Match the index type and column from indexdef, group 1 is type, group 2 is the column list. + var regex = /USING (btree|hash|gist|spgist|gin|brin|[\w]+) \(([a-zA-Z1-9+\-*\/_, ()]+)\)/; + MatchInfo match_info; + if (regex.match (indexdef, 0, out match_info)) { + + new_row.add_field (match_info.fetch (1)); + new_row.add_field (match_info.fetch (2)); + } else { + debug ("Regex not match: %s", indexdef); + assert_not_reached (); + } + + return new_row; + }); + + return result; + } + + public async Relation db_tablenames (string schema = "public") throws PsequelError { var builder = new StringBuilder ("select tablename from pg_tables where schemaname="); builder.append (@"\'$schema\';"); @@ -51,7 +152,22 @@ namespace Psequel { string stmt = builder.free_and_steal (); var res = yield exec_query_internal (stmt); - var table = new Table ((owned) res); + var table = new Relation ((owned) res); + + return table; + } + + public async Relation db_views (string schema = "public") throws PsequelError { + + string stmt = """ + select table_name from INFORMATION_SCHEMA.views WHERE table_schema = $1; + """; + var params = new ArrayList (); + params.add (new Variant.string (schema)); + + var res = yield exec_query_params_internal (stmt, params); + + var table = new Relation ((owned) res); return table; } @@ -93,24 +209,24 @@ namespace Psequel { } } - public async Table exec_query (string query) throws PsequelError { + public async Relation exec_query (string query) throws PsequelError { var result = yield exec_query_internal (query); // check query status check_query_status (result); - var table = new Table ((owned) result); + var table = new Relation ((owned) result); return table; } - public async Table exec_query_params (string query, ArrayList params) throws PsequelError { + public async Relation exec_query_params (string query, ArrayList params) throws PsequelError { var result = yield exec_query_params_internal (query, params); // check query status check_query_status (result); - var table = new Table ((owned) result); + var table = new Relation ((owned) result); return table; } diff --git a/src/services/resource_manager.vala b/src/services/resource_manager.vala index 6788f13..1d2086c 100644 --- a/src/services/resource_manager.vala +++ b/src/services/resource_manager.vala @@ -31,7 +31,7 @@ namespace Psequel { /** * List of table in current schema. */ - public ObservableArrayList table_list; + public ObservableArrayList table_list; /** * Application diff --git a/src/services/signal.vala b/src/services/signal.vala index a9987b7..f6f7a92 100644 --- a/src/services/signal.vala +++ b/src/services/signal.vala @@ -9,8 +9,11 @@ namespace Psequel { * Emit when the table list changed. */ public signal void table_list_changed (); + public signal void views_list_changed (); + public signal void table_selected_changed (string schema, string table); + public signal void database_connected (); /** * Should only init onces by the resource manager. Should be single on but i'm lazy diff --git a/src/ui/query_view.vala b/src/ui/query_view.vala index e008b50..eea0a7e 100644 --- a/src/ui/query_view.vala +++ b/src/ui/query_view.vala @@ -9,9 +9,12 @@ namespace Psequel { private unowned AppSignals signals; - private ObservableArrayList table_names; + private ObservableArrayList table_names; + private ObservableArrayList views_names; + private Gtk.FilterListModel tablelist_model; + private Gtk.FilterListModel viewslist_model; private Gtk.StringList schema_model; public QueryView () { @@ -21,9 +24,11 @@ namespace Psequel { construct { query_service = ResourceManager.instance ().query_service; signals = ResourceManager.instance ().signals; + // tables_views_switcher.get_ set_up_table_list (); set_up_schema (); + set_up_views_list (); connect_signals (); } @@ -47,10 +52,28 @@ namespace Psequel { tablelist_model.get_filter ().changed (Gtk.FilterChange.DIFFERENT); } + [GtkCallback] + private void on_view_search (Gtk.SearchEntry entry) { + debug ("Search views: %s", entry.text); + viewslist_model.get_filter ().changed (Gtk.FilterChange.DIFFERENT); + } + + [GtkCallback] + private void view_selected () { + debug ("view selected."); + } + [GtkCallback] private void on_show_search (Gtk.ToggleButton btn) { if (btn.active) { - search_entry.grab_focus (); + search_table_entry.grab_focus (); + } + } + + [GtkCallback] + private void on_show_view_search (Gtk.ToggleButton btn) { + if (btn.active) { + search_views_entry.grab_focus (); } } @@ -59,21 +82,52 @@ namespace Psequel { */ private void schema_changed () { signals.table_list_changed (); + signals.views_list_changed (); } + + [GtkCallback] + private void table_selected () { + var row = table_list.get_selected_row (); + + if (row == null) { + signals.table_selected_changed ("", ""); + } else { + var cur_schema = schema.get_selected_item () as Gtk.StringObject; + assert_nonnull (cur_schema); + + var label = row.child.get_last_child () as Gtk.Label; + + debug ("Emit table_selected_changed"); + signals.table_selected_changed (cur_schema.string, label.get_label ()); + } + } /** * Filter table name base on seach entry. */ private bool search_filter_func (Object item) { - assert (item is Table.Row); + assert (item is Relation.Row); - var row = item as Table.Row; + var row = item as Relation.Row; var table_name = row.get (0); - var search_text = search_entry.text; + var search_text = search_table_entry.text; return table_name.contains (search_text); } + /** + * Filter table name base on seach entry. + */ + private bool view_filter_func (Object item) { + assert (item is Relation.Row); + + var row = item as Relation.Row; + var view_name = row.get (0); + var search_text = search_views_entry.text; + + return view_name.contains (search_text); + } + /** * Reload schema list to the drop down by fetching database. */ @@ -104,6 +158,7 @@ namespace Psequel { var relations = yield query_service.db_tablenames (cur_schema); + table_list.unselect_all (); table_names.clear (); foreach (var item in relations) { table_names.add (item); @@ -112,10 +167,25 @@ namespace Psequel { debug ("Tables list reloaded, got %d tables in schema %s", table_names.size, cur_schema); } + private async void reload_views () throws PsequelError { + + var cur_schema = ((Gtk.StringObject) schema.selected_item).string ?? "public"; + + var relations = yield query_service.db_views (cur_schema); + + views_list.unselect_all (); + views_names.clear (); + foreach (var item in relations) { + views_names.add (item); + } + + debug ("Views list reloaded, got %d views in schema %s", table_names.size, cur_schema); + } + /** Create row widget from query result. */ private Gtk.ListBoxRow table_row_factory (Object obj) { - var row_data = obj as Table.Row; + var row_data = obj as Relation.Row; var row = new Gtk.ListBoxRow (); @@ -132,14 +202,40 @@ namespace Psequel { return row; } + private Gtk.ListBoxRow view_row_factory (Object obj) { + var row_data = obj as Relation.Row; + + var row = new Gtk.ListBoxRow (); + + var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 8); + var icon = new Gtk.Image (); + icon.icon_name = "category-search-symbolic"; + var label = new Gtk.Label (row_data[0]); + + box.append (icon); + box.append (label); + + row.child = box; + + return row; + } + private void set_up_table_list () { - this.table_names = new ObservableArrayList (); + this.table_names = new ObservableArrayList (); var filter = new Gtk.CustomFilter (search_filter_func); this.tablelist_model = new Gtk.FilterListModel (table_names, filter); this.table_list.bind_model (tablelist_model, table_row_factory); } + private void set_up_views_list () { + this.views_names = new ObservableArrayList (); + + var filter = new Gtk.CustomFilter (view_filter_func); + this.viewslist_model = new Gtk.FilterListModel (views_names, filter); + this.views_list.bind_model (viewslist_model, view_row_factory); + } + private void set_up_schema () { this.schema_model = new Gtk.StringList (null); this.schema.set_model (schema_model); @@ -151,6 +247,11 @@ namespace Psequel { reload_tables.begin (); }); + signals.views_list_changed.connect (() => { + debug ("Handle views_list_changed."); + reload_views.begin (); + }); + signals.database_connected.connect (() => { debug ("Handle database_connected."); reload_schema.begin (); @@ -163,7 +264,13 @@ namespace Psequel { private unowned Gtk.ListBox table_list; [GtkChild] - private unowned Gtk.SearchEntry search_entry; + private unowned Gtk.SearchEntry search_table_entry; + + [GtkChild] + private unowned Gtk.ListBox views_list; + + [GtkChild] + private unowned Gtk.SearchEntry search_views_entry; [GtkChild] private unowned Gtk.DropDown schema; diff --git a/src/ui/table_structure.vala b/src/ui/table_structure.vala index 81ae23a..0c0aae2 100644 --- a/src/ui/table_structure.vala +++ b/src/ui/table_structure.vala @@ -6,6 +6,12 @@ namespace Psequel { private AppSignals signals; private QueryService query_service; + // private Table table; + private ObservableArrayList columns_model; + private ObservableArrayList index_model; + private ObservableArrayList fk_model; + // private Gtk.SingleSelection selection_model; + public TableStructure () { Object (); } @@ -15,17 +21,133 @@ namespace Psequel { query_service = ResourceManager.instance ().query_service; signals = ResourceManager.instance ().signals; - signals.database_connected.connect (() => { - query_service.db_table_info.begin ("public", "city", (object, res) => { - Table table = query_service.db_table_info.end (res); + columns_model = new ObservableArrayList (); + index_model = new ObservableArrayList (); + fk_model = new ObservableArrayList (); - debug (table.to_string ()); - foreach (var item in table) { - debug (item.to_string ()); - } - }); + + signals.table_selected_changed.connect ((schema, tbname) => { + debug ("%s, %s", schema, tbname); + reload_table_columns.begin (schema, tbname); + reload_table_indexes.begin (schema, tbname); + reload_table_fk.begin (schema, tbname); }); + var columns_title = new string[] { + "Column Name", + "Type", + "Length", + "Nullable", + "Default Value" + }; + + var index_titles = new string[] { + "Index Name", + "Unique", + "Type", + "Columns", + }; + + var fk_titles = new string[] { + "Key Name", + "Columns", + "Foreign Table", + "Foreign Columns", + "On Update", + "On Delete", + }; + + set_up_view (columns_title, columns_model, columns); + set_up_view (index_titles, index_model, indexes); + set_up_view (fk_titles, fk_model, foreign_key); + } + + void set_up_view (string[] titles, ListModel model, Gtk.ColumnView view) { + for (int i = 0; i < titles.length; i++) { + var factory = new Gtk.SignalListItemFactory (); + factory.set_data ("index", i); + + factory.setup.connect ((_fact, _item) => { + var label = new Gtk.Label (null); + label.halign = Gtk.Align.START; + label.margin_start = 8; + _item.child = label; + }); + + factory.bind.connect ((_fact, _item) => { + var row = _item.item as Relation.Row; + var label = _item.child as Gtk.Label; + int index = _fact.get_data ("index"); + label.label = row[index]; + }); + + Gtk.ColumnViewColumn column = new Gtk.ColumnViewColumn (titles[i], factory); + column.set_expand (true); + view.append_column (column); + } + + var selection_model = new Gtk.SingleSelection (model); + view.set_model (selection_model); + } + + private async void reload_table_columns (string schema, string tbname) { + try { + var relation = yield query_service.db_table_info (schema, tbname); + + if (relation.cols != columns.columns.get_n_items ()) { + debug ("Programming Error: Query result cols != view columns"); + assert_not_reached (); + } + + columns_model.clear (); + foreach (var row in relation) { + columns_model.add (row); + } + + } catch (PsequelError err) { + debug (err.message); + // ResourceManager.instance ().app + } + } + + private async void reload_table_indexes (string schema, string tbname) { + try { + var relation = yield query_service.db_table_indexes (schema, tbname); + + if (relation.cols != indexes.columns.get_n_items ()) { + debug ("Programming Error: Query result cols != view columns"); + assert_not_reached (); + } + + index_model.clear (); + foreach (var row in relation) { + index_model.add (row); + } + + } catch (PsequelError err) { + debug (err.message); + // ResourceManager.instance ().app + } + } + + private async void reload_table_fk (string schema, string tbname) { + try { + var relation = yield query_service.db_table_fk (schema, tbname); + + if (relation.cols != foreign_key.columns.get_n_items ()) { + debug ("Programming Error: Query result cols != view columns"); + assert_not_reached (); + } + + fk_model.clear (); + foreach (var row in relation) { + fk_model.add (row); + } + + } catch (PsequelError err) { + debug (err.message); + // ResourceManager.instance ().app + } } [GtkChild] @@ -35,4 +157,5 @@ namespace Psequel { [GtkChild] private unowned Gtk.ColumnView foreign_key; } + } \ No newline at end of file