Use on-demand loading for results in the query tool. Fixes #2137

With a 27420 row query, pgAdmin III runs the query in 5.873s on my laptop. pgAdmin 4 now takes ~1s.
This commit is contained in:
Harshal Dhumal
2017-06-27 09:03:04 -04:00
committed by Dave Page
parent 15cb9fc35b
commit c65158312d
28 changed files with 1953 additions and 887 deletions

View File

@@ -3,6 +3,7 @@ import 'slickgrid/slick-default-theme.css';
import 'slickgrid/css/smoothness/jquery-ui-1.11.3.custom.css';
import 'slickgrid/slick.core';
import 'slickgrid/slick.grid';
import 'slickgrid/slick.dataview';
import 'slickgrid/slick.editors';
import 'slickgrid/slick.formatters';
import 'slickgrid/plugins/slick.autotooltips';

View File

@@ -4,13 +4,18 @@ define([
'slickgrid',
], function ($, RangeSelectionHelper) {
var ColumnSelector = function () {
var Slick = window.Slick;
var gridEventBus = new Slick.EventHandler();
var Slick = window.Slick,
gridEventBus = new Slick.EventHandler(),
onBeforeColumnSelectAll = new Slick.Event(),
onColumnSelectAll = new Slick.Event();
var init = function (grid) {
gridEventBus.subscribe(grid.onHeaderClick, handleHeaderClick.bind(null, grid));
grid.getSelectionModel().onSelectedRangesChanged
.subscribe(handleSelectedRangesChanged.bind(null, grid));
onColumnSelectAll.subscribe(function(e, args) {
updateRanges(args.grid, args.column.id);
});
};
var handleHeaderClick = function (grid, event, args) {
@@ -21,11 +26,20 @@ define([
if (isColumnSelectable(columnDefinition)) {
var $columnHeader = $(event.target);
if (hasClickedChildOfColumnHeader(event)) {
if ($(event.target).hasClass('slick-resizable-handle')) {
return;
}
$columnHeader = $(event.target).parents('.slick-header-column');
}
$columnHeader.toggleClass('selected');
updateRanges(grid, columnDefinition.id);
if ($columnHeader.hasClass('selected')) {
onBeforeColumnSelectAll.notify(args, event);
}
if (!(event.isPropagationStopped() || event.isImmediatePropagationStopped())) {
updateRanges(grid, columnDefinition.id);
}
}
};
@@ -107,6 +121,8 @@ define([
$.extend(this, {
'init': init,
'getColumnDefinitions': getColumnDefinitions,
'onBeforeColumnSelectAll': onBeforeColumnSelectAll,
'onColumnSelectAll': onColumnSelectAll,
});
};
return ColumnSelector;

View File

@@ -12,19 +12,19 @@ function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) {
var grid = self.slickgrid;
var columnDefinitions = grid.getColumns();
var selectedRanges = grid.getSelectionModel().getSelectedRanges();
var data = grid.getData();
var dataView = grid.getData();
var rows = grid.getSelectedRows();
if (RangeSelectionHelper.areAllRangesCompleteRows(grid, selectedRanges)) {
self.copied_rows = rows.map(function (rowIndex) {
return data[rowIndex];
return grid.getDataItem(rowIndex);
});
setPasteRowButtonEnablement(self.can_edit, true);
} else {
self.copied_rows = [];
setPasteRowButtonEnablement(self.can_edit, false);
}
var csvText = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, selectedRanges);
var csvText = rangeBoundaryNavigator.rangesToCsv(dataView.getItems(), columnDefinitions, selectedRanges);
if (csvText) {
clipboard.copyTextToClipboard(csvText);
}

View File

@@ -6,21 +6,31 @@ define(['jquery',
'sources/url_for',
], function ($, gettext, ColumnSelector, RowSelector, RangeSelectionHelper, url_for) {
var GridSelector = function (columnDefinitions) {
var rowSelector = new RowSelector(columnDefinitions);
var columnSelector = new ColumnSelector(columnDefinitions);
var Slick = window.Slick,
rowSelector = new RowSelector(columnDefinitions),
columnSelector = new ColumnSelector(columnDefinitions),
onBeforeGridSelectAll = new Slick.Event(),
onGridSelectAll = new Slick.Event(),
onBeforeGridColumnSelectAll = columnSelector.onBeforeColumnSelectAll,
onGridColumnSelectAll = columnSelector.onColumnSelectAll;
var init = function (grid) {
this.grid = grid;
grid.onHeaderClick.subscribe(function (event, eventArguments) {
if (eventArguments.column.selectAllOnClick) {
toggleSelectAll(grid);
if (eventArguments.column.selectAllOnClick && !$(event.target).hasClass('slick-resizable-handle')) {
toggleSelectAll(grid, event, eventArguments);
}
});
grid.getSelectionModel().onSelectedRangesChanged
.subscribe(handleSelectedRangesChanged.bind(null, grid));
.subscribe(handleSelectedRangesChanged.bind(null, grid));
grid.registerPlugin(rowSelector);
grid.registerPlugin(columnSelector);
onGridSelectAll.subscribe(function(e, args) {
RangeSelectionHelper.selectAll(args.grid);
});
};
var getColumnDefinitions = function (columnDefinitions) {
@@ -45,11 +55,14 @@ define(['jquery',
}
}
function toggleSelectAll(grid) {
function toggleSelectAll(grid, event, eventArguments) {
if (RangeSelectionHelper.isEntireGridSelected(grid)) {
selectNone(grid);
} else {
RangeSelectionHelper.selectAll(grid);
onBeforeGridSelectAll.notify(eventArguments, event);
if (!(event.isPropagationStopped() || event.isImmediatePropagationStopped())) {
RangeSelectionHelper.selectAll(grid);
}
}
}
@@ -61,6 +74,10 @@ define(['jquery',
$.extend(this, {
'init': init,
'getColumnDefinitions': getColumnDefinitions,
'onBeforeGridSelectAll': onBeforeGridSelectAll,
'onGridSelectAll': onGridSelectAll,
'onBeforeGridColumnSelectAll': onBeforeGridColumnSelectAll,
'onGridColumnSelectAll': onGridColumnSelectAll,
});
};

View File

@@ -58,6 +58,7 @@ function (RangeSelectionHelper) {
},
rangesToCsv: function (data, columnDefinitions, selectedRanges) {
var rowRangeBounds = selectedRanges.map(function (range) {
return [range.fromRow, range.toRow];
});
@@ -72,6 +73,7 @@ function (RangeSelectionHelper) {
var csvRows = this.mapOver2DArray(rowRangeBounds, colRangeBounds, this.csvCell.bind(this, data, columnDefinitions), function (rowData) {
return rowData.join(',');
});
return csvRows.join('\n');
},
@@ -101,7 +103,7 @@ function (RangeSelectionHelper) {
},
csvCell: function (data, columnDefinitions, rowId, colId) {
var val = data[rowId][columnDefinitions[colId].pos];
var val = data[rowId][columnDefinitions[colId].field];
if (val && _.isObject(val)) {
val = '\'' + JSON.stringify(val) + '\'';

View File

@@ -82,7 +82,8 @@ define([
formatter: function (rowIndex) {
return '<span ' +
'data-row="' + rowIndex + '" ' +
'data-cell-type="row-header-selector"/>';
'data-cell-type="row-header-selector">' +
(rowIndex+1) + '</span>';
},
width: 30,
});

View File

@@ -22,53 +22,44 @@ define(
$(selector).prop('disabled', false);
}
function getRowPrimaryKeyValuesToStage(selectedRows, primaryKeyColumnIndices, gridData) {
function getRowPrimaryKeyValuesToStage(selectedRows, primaryKeys, dataView, client_primary_key) {
return _.reduce(selectedRows, function (primaryKeyValuesToStage, dataGridRowIndex) {
var gridRow = gridData[dataGridRowIndex];
if (isRowMissingPrimaryKeys(gridRow, primaryKeyColumnIndices)) {
var gridRow = dataView.getItem(dataGridRowIndex);
if (isRowMissingPrimaryKeys(gridRow, primaryKeys)) {
return primaryKeyValuesToStage;
}
var tempPK = gridRow.__temp_PK;
primaryKeyValuesToStage[tempPK] = getSingleRowPrimaryKeyValueToStage(primaryKeyColumnIndices, gridRow);
var tempPK = gridRow[client_primary_key];
primaryKeyValuesToStage[tempPK] = getSingleRowPrimaryKeyValueToStage(primaryKeys, gridRow);
return primaryKeyValuesToStage;
}, {});
}
function isRowMissingPrimaryKeys(gridRow, primaryKeyColumnIndices) {
function isRowMissingPrimaryKeys(gridRow, primaryKeys) {
if (_.isUndefined(gridRow)) {
return true;
}
return !_.isUndefined(
_.find(primaryKeyColumnIndices, function (pkIndex) {
return _.isUndefined(gridRow[pkIndex]);
_.find(primaryKeys , function (pk) {
return _.isUndefined(gridRow[pk]);
})
);
}
function getSingleRowPrimaryKeyValueToStage(primaryKeyColumnIndices, gridRow) {
function getSingleRowPrimaryKeyValueToStage(primaryKeys, gridRow) {
var rowToStage = {};
if (primaryKeyColumnIndices.length) {
_.each(_.keys(gridRow), function (columnPos) {
if (_.contains(primaryKeyColumnIndices, Number(columnPos)))
rowToStage[columnPos] = gridRow[columnPos];
if (primaryKeys && primaryKeys.length) {
_.each(_.keys(gridRow), function (columnNames) {
if (_.contains(primaryKeys, columnNames))
rowToStage[columnNames] = gridRow[columnNames];
});
}
return rowToStage;
}
function getPrimaryKeysForSelectedRows(self, selectedRows) {
var primaryKeyColumnIndices = _.map(_.keys(self.keys), function (columnName) {
var columnInfo = _.findWhere(self.columns, {name: columnName});
return columnInfo['pos'];
});
var gridData = self.grid.getData();
var stagedRows = getRowPrimaryKeyValuesToStage(selectedRows, primaryKeyColumnIndices, gridData);
var dataView = self.grid.getData();
var stagedRows = getRowPrimaryKeyValuesToStage(selectedRows, _.keys(self.keys), dataView, self.client_primary_key);
return stagedRows;
}
@@ -114,4 +105,4 @@ define(
};
return setStagedRows;
}
);
);

View File

@@ -76,18 +76,18 @@
last_value = (column_type === 'number') ?
(_.isEmpty(last_value) || last_value) : last_value;
item[args.column.pos] = state;
item[args.column.field] = state;
if (last_value && _.isNull(state) &&
(_.isUndefined(grid.copied_rows[row]) ||
_.isUndefined(grid.copied_rows[row][cell]))
) {
item[args.column.pos] = undefined;
item[args.column.field] = undefined;
if (grid.copied_rows[row] == undefined) grid.copied_rows[row] = [];
grid.copied_rows[row][cell] = 1;
}
}
else {
item[args.column.pos] = state;
item[args.column.field] = state;
}
}
@@ -189,14 +189,14 @@
this.loadValue = function (item) {
var col = args.column;
if (_.isUndefined(item[args.column.pos]) && col.has_default_val) {
if (_.isUndefined(item[args.column.field]) && col.has_default_val) {
$input.val(defaultValue = "");
}
else if (item[args.column.pos] === "") {
else if (item[args.column.field] === "") {
$input.val(defaultValue = "''");
}
else {
$input.val(defaultValue = item[args.column.pos]);
$input.val(defaultValue = item[args.column.field]);
$input.select();
}
};
@@ -323,7 +323,7 @@
};
this.loadValue = function (item) {
var data = defaultValue = item[args.column.pos];
var data = defaultValue = item[args.column.field];
if (data && typeof data === "object" && !Array.isArray(data)) {
data = JSON.stringify(data);
} else if (Array.isArray(data)) {
@@ -443,7 +443,7 @@
};
this.loadValue = function (item) {
$input.val(defaultValue = item[args.column.pos]);
$input.val(defaultValue = item[args.column.field]);
$input.select();
};
@@ -452,7 +452,7 @@
};
this.applyValue = function (item, state) {
item[args.column.pos] = state;
item[args.column.field] = state;
};
this.isValueChanged = function () {
@@ -531,13 +531,13 @@
};
this.loadValue = function (item) {
defaultValue = item[args.column.pos];
if (_.isNull(defaultValue)|| _.isUndefined(defaultValue)) {
defaultValue = item[args.column.field];
if (_.isNull(defaultValue)||_.isUndefined(defaultValue)) {
$select.prop('indeterminate', true);
$select.data('checked', 2);
}
else {
defaultValue = !!item[args.column.pos];
defaultValue = !!item[args.column.field];
if (defaultValue) {
$select.prop('checked', true);
$select.data('checked', 0);
@@ -556,7 +556,7 @@
};
this.applyValue = function (item, state) {
item[args.column.pos] = state;
item[args.column.field] = state;
};
this.isValueChanged = function () {
@@ -648,7 +648,7 @@
};
this.loadValue = function (item) {
var data = defaultValue = item[args.column.pos];
var data = defaultValue = item[args.column.field];
if (typeof data === "object" && !Array.isArray(data)) {
data = JSON.stringify(data);
} else if (Array.isArray(data)) {
@@ -671,7 +671,7 @@
};
this.applyValue = function (item, state) {
item[args.column.pos] = state;
item[args.column.field] = state;
};
this.isValueChanged = function () {
@@ -725,7 +725,7 @@
};
this.loadValue = function (item) {
var value = item[args.column.pos];
var value = item[args.column.field];
// Check if value is null or undefined
if (value === undefined && typeof value === "undefined") {
@@ -858,7 +858,7 @@
};
this.loadValue = function (item) {
defaultValue = item[args.column.pos];
defaultValue = item[args.column.field];
$input.val(defaultValue);
$input[0].defaultValue = defaultValue;
$input.select();