Skip to content

Commit

Permalink
feat: optimize table data view perf
Browse files Browse the repository at this point in the history
  • Loading branch information
ppvan committed Mar 17, 2024
1 parent 7d3c503 commit 593d958
Show file tree
Hide file tree
Showing 17 changed files with 136 additions and 38 deletions.
4 changes: 2 additions & 2 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ project(
app_is_development = false
app_id = 'me.ppvan.psequel'

if get_option('profile') == 'development'
add_project_arguments('-D', 'DEVEL', language: 'vala')
if get_option('buildtype') == 'debug'
add_project_arguments('--debug', language: 'vala')
app_is_development = true
endif

Expand Down
2 changes: 1 addition & 1 deletion meson.options
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
option ('profile',
type: 'combo',
value: 'development',
choices: ['default', 'development'],
choices: ['release', 'development'],
description: 'The build profile for the app. If none specified, "development" is used.'
)
1 change: 1 addition & 0 deletions res/gtk/query-editor.blp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ template $PsequelQueryEditor: Adw.Bin {

$PsequelQueryResults query_results {
wellcome_message: "Run a query";
show_loading: true;
current-relation: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.current-relation;
is-loading: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.is-loading;
err-msg: bind template.query-history-viewmodel as <$PsequelQueryHistoryViewModel>.err-msg;
Expand Down
1 change: 1 addition & 0 deletions res/gtk/table-data-view.blp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ template $PsequelTableDataView: Gtk.Box {
current-relation: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.current-relation;
is-loading: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.is-loading;
err-msg: bind template.tabledata-viewmodel as <$PsequelTableDataViewModel>.err-msg;
show-loading: false;
}

Box {
Expand Down
1 change: 1 addition & 0 deletions res/gtk/view-data-view.blp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ template $PsequelViewDataView: Gtk.Box {
current-relation: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.current-relation;
is-loading: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.is-loading;
err-msg: bind template.viewdata-viewmodel as <$PsequelViewDataViewModel>.err-msg;
show-loading: false;
}

Box {
Expand Down
31 changes: 30 additions & 1 deletion src/application.vala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ namespace Psequel {

public int color_scheme { get; set; }
public const int MAX_COLUMNS = 128;
public const int PRE_ALLOCATED_CELL = 1024;
public const int BATCH_SIZE = 16;

public static List<uint> tasks;
public static bool is_running = false;

public Application () {
Object (application_id: Config.APP_ID, flags: ApplicationFlags.DEFAULT_FLAGS);
Expand All @@ -60,14 +65,34 @@ namespace Psequel {
this.set_accels_for_action ("win.export", { "<Ctrl><Shift>e" });
this.set_accels_for_action ("win.run-query", { "<Ctrl>Return" });

// this.set_accels_for_action ("conn.dupplicate", { "<Ctrl>D" });
// this.set_accels_for_action ("conn.dupplicate", { "<Ctrl>D" });
}

public override void activate () {
base.activate ();

var window = new_window ();
window.present ();


// Pre-allocated widget, scheduled after window presented
DataCell.cell_pool = new List<DataCell> ();
var id = Idle.add (() => {

size_t empty_cells = DataCell.cell_pool.length ();
if (empty_cells < Application.PRE_ALLOCATED_CELL) {
debug ("Empty Cell: %llu", empty_cells);
for (size_t i = 0; i < Application.BATCH_SIZE; i++) {
DataCell.cell_pool.append (new DataCell());
}
} else if (empty_cells >= Application.PRE_ALLOCATED_CELL) {
return false;
}

return Application.is_running;
}, Priority.DEFAULT_IDLE);

Application.tasks.append (id);
}

public override void startup () {
Expand All @@ -83,6 +108,9 @@ namespace Psequel {
container.register (settings);
container.register (this);

Application.tasks = new List<uint>();
this.is_running = true;

debug ("Begin to load resources");
try {
// Don't change the max_thread because libpq did not support many query with 1 connection.
Expand All @@ -98,6 +126,7 @@ namespace Psequel {

public override void shutdown () {
base.shutdown ();
Application.is_running = false;
}

public void update_color_scheme () {
Expand Down
4 changes: 4 additions & 0 deletions src/models/Relation.vala
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ namespace Psequel {
return @"Table ($rows x $cols)";
}

public List<Row> steal() {
return (owned)this.data;
}

public string name { get; set; }

public new Row @get (int index) {
Expand Down
7 changes: 4 additions & 3 deletions src/services/SQLService.vala
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ namespace Psequel {
}

/** Select info from a table. */
public async Relation select (BaseTable table, int page) throws PsequelError {
public async Relation select (BaseTable table, int page, int size = query_limit) throws PsequelError {
string schema_name = active_db.escape_identifier (table.schema.name);
string escape_tbname = active_db.escape_identifier (table.name);
int offset = page * query_limit;
int offset = page * size;
int limit = size;

string stmt = @"SELECT * FROM $schema_name.$escape_tbname LIMIT $query_limit OFFSET $offset";
string stmt = @"SELECT * FROM $schema_name.$escape_tbname LIMIT $limit OFFSET $offset";
var query = new Query (stmt);
return yield exec_query (query);
}
Expand Down
56 changes: 43 additions & 13 deletions src/ui/schema/QueryResult.vala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Psequel {
const string ERROR = "error";

public string wellcome_message { get; set; }
public bool show_loading { get; set;}

private Relation _current_relation;
public Relation current_relation {
Expand Down Expand Up @@ -43,20 +44,30 @@ namespace Psequel {
private Gtk.SortListModel sort_model;
private Gtk.SelectionModel selection_model;

public class QueryResults () {
public class QueryResults (bool show_loading) {
Object ();
}

construct {
stack.visible_child_name = EMPTY;
rows = new ObservableList<Relation.Row> ();
alloc_columns ();

this.bind_property ("is-loading", stack, "visible-child-name", BindingFlags.SYNC_CREATE, (binging, from, ref to) => {
bool current_name = from.get_boolean ();
if (show_loading && current_name) {
to.set_string (LOADING);
} else {
// to.set_string (MAIN);
}
});
}

private void on_current_relation_change () {
debug ("%d ", current_relation.rows);
stack.visible_child_name = LOADING;
load_data_to_view.begin (current_relation, (obj, res) => {
load_data_to_view.end (res);
stack.visible_child_name = MAIN;
});
}
Expand All @@ -66,15 +77,14 @@ namespace Psequel {
}

private async void load_data_to_view (Relation relation) {
// if (relation == null) {
// return;
// }

var columns = data_view.columns;
uint n = columns.get_n_items ();
debug ("Begin add rows to views");
for (int i = 0; i < n; i++) {
var col = columns.get_item (i) as Gtk.ColumnViewColumn;
for (int i = 0;; i++) {
var raw_col = columns.get_item (i);
if (raw_col == null) {
break;
}
var col = raw_col as Gtk.ColumnViewColumn;
if (i >= relation.cols) {
col.set_visible (false);
continue;
Expand All @@ -86,11 +96,11 @@ namespace Psequel {

this.selection_model.unselect_all ();
this.sort_model.sorter = data_view.get_sorter ();
rows.clear ();

foreach (var row in relation) {
rows.append (row);
}
rows.freeze_notify ();
rows.clear ();
rows.append_all (relation.steal ());
rows.thaw_notify ();
}

private void alloc_columns () {
Expand All @@ -101,7 +111,14 @@ namespace Psequel {
factory.setup.connect ((_fact, obj) => {

var _item = (Gtk.ListItem) obj;
var cell = new DataCell ();
if (DataCell.cell_pool == null || DataCell.cell_pool.next == null) {
for (size_t j = 0; j < Application.BATCH_SIZE; j++) {
DataCell.cell_pool.append (new DataCell());
}
}

var cell = DataCell.cell_pool.data;
DataCell.cell_pool = (owned)DataCell.cell_pool.next;
_item.child = cell;
});

Expand All @@ -113,6 +130,19 @@ namespace Psequel {
cell.bind_data (row, index);
});

factory.unbind.connect ((obj) => {
var _item = (Gtk.ListItem) obj;
var row = _item.item as Relation.Row;
var cell = _item.child as Psequel.DataCell;
cell.unbind_data (row);
});

factory.teardown.connect ((obj) => {
var _item = (Gtk.ListItem) obj;
var cell = _item.child as Psequel.DataCell;
DataCell.cell_pool.append ((owned)cell);
});

Gtk.ColumnViewColumn column = new Gtk.ColumnViewColumn ("", factory);
column.set_expand (true);
// column.fixed_width = 200;
Expand Down
14 changes: 11 additions & 3 deletions src/ui/schema/TableDataView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace Psequel {
[GtkTemplate (ui = "/me/ppvan/psequel/gtk/table-data-view.ui")]
public class TableDataView : Gtk.Box {

public TableDataViewModel tabledata_viewmodel {get; set;}
public TableDataViewModel tabledata_viewmodel { get; set; }

public TableDataView () {
Object ();
Expand All @@ -14,8 +14,16 @@ namespace Psequel {
}

[GtkCallback]
private async void reload_data () {
yield tabledata_viewmodel.reload_data ();
private void reload_data (Gtk.Button btn) {
btn.sensitive = false;
tabledata_viewmodel.reload_data.begin ((obj, res) => {
var window = get_parrent_window (this);
Adw.Toast toast = new Adw.Toast ("Data Reloaded") {
timeout = 1,
};
window.add_toast (toast);
btn.sensitive = true;
});
}

[GtkCallback]
Expand Down
1 change: 0 additions & 1 deletion src/ui/schema/ViewDataView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ namespace Psequel {

[GtkTemplate (ui = "/me/ppvan/psequel/gtk/view-data-view.ui")]
public class ViewDataView : Gtk.Box {

public ViewDataViewModel viewdata_viewmodel {get; set;}

public ViewDataView () {
Expand Down
19 changes: 18 additions & 1 deletion src/ui/views/SchemaView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,24 @@ namespace Psequel {

[GtkCallback]
private void reload_btn_clicked (Gtk.Button btn) {
schema_viewmodel.reload.begin ();

btn.sensitive = false;
schema_viewmodel.reload.begin ((obj, res) => {
var window = get_parrent_window (this);
Adw.Toast toast;
try {
schema_viewmodel.reload.end (res);
toast = new Adw.Toast ("Schema Reloaded") {
timeout = 1,
};
} catch (Psequel.PsequelError err) {
toast = new Adw.Toast (err.message) {
timeout = 1,
};
}
window.add_toast (toast);
btn.sensitive = true;
});
}

[GtkCallback]
Expand Down
6 changes: 6 additions & 0 deletions src/ui/widgets/DataCell.vala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ namespace Psequel {
private Relation.Row current_row;
private int current_index;

public static List<DataCell> cell_pool;

const ActionEntry[] ACTION_ENTRIES = {
{ "copy", on_cell_copy },
{ "edit", on_cell_edit },
Expand All @@ -37,6 +39,10 @@ namespace Psequel {
this.label.label = row[index];
}

public void unbind_data(Psequel.Relation.Row row) {

}

[GtkCallback]
public void on_right_clicked() {
popover.popup();
Expand Down
5 changes: 3 additions & 2 deletions src/utils/ObservableList.vala
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ namespace Psequel {
this._data = new ListStore (typeof (T));

// Forward item changed event.
this._data.items_changed.connect (this.items_changed);
this._data.items_changed.connect (() => {
// this._data.items_changed.connect (this.items_changed);
this._data.items_changed.connect ((pos, removed, added) => {
this.items_changed(pos, removed, added);
this.size = (int)this._data.get_n_items ();
});
}
Expand Down
6 changes: 2 additions & 4 deletions src/utils/helpers.vala
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
namespace Psequel {

public Window get_parrent_window (Gtk.Widget widget) {
var window = widget.get_root ();

var window_type = GLib.Type.from_name ("GtkWindow");
var window = widget.get_ancestor (window_type);

if (window is Gtk.Window) {
if (window is Psequel.Window) {
return (Window) window;
} else {
warning ("Widget %s root is not a window", widget.name);
Expand Down
10 changes: 6 additions & 4 deletions src/viewmodels/TableDataViewModel.vala
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
namespace Psequel {

public class TableDataViewModel : BaseViewModel, Observer {

public const int MAX_FETCHED_ROW = 50;

public Table? selected_table { get; set; }
// public View? current_view {get; set;}

Expand Down Expand Up @@ -33,11 +36,10 @@ namespace Psequel {

this.notify["current-relation"].connect (() => {

int offset = sql_service.query_limit * current_page;
int offset = MAX_FETCHED_ROW * current_page;
row_ranges = @"Rows $(1 + offset) - $(offset + current_relation.rows)";


if (current_relation.rows < sql_service.query_limit) {
if (current_relation.rows < MAX_FETCHED_ROW) {
has_next_page = false;

} else {
Expand Down Expand Up @@ -78,7 +80,7 @@ namespace Psequel {

try {
is_loading = true;
current_relation = yield sql_service.select (table, page);
current_relation = yield sql_service.select (table, page, MAX_FETCHED_ROW);

is_loading = false;
debug ("Rows: %d", current_relation.rows);
Expand Down
Loading

0 comments on commit 593d958

Please sign in to comment.