Ensure that the macro query result should be download properly. Fixes #5965

This commit is contained in:
Rahul Shirsat 2020-12-14 11:58:53 +05:30 committed by Akshay Joshi
parent 296d22ad83
commit 6475a70514
18 changed files with 147 additions and 76 deletions

View File

@ -122,7 +122,7 @@ You can:
* Select and copy from the displayed result set. * Select and copy from the displayed result set.
* Use the *Execute/Refresh* options to retrieve query execution information and * Use the *Execute/Refresh* options to retrieve query execution information and
set query execution options. set query execution options.
* Use the *Download as CSV/TXT* icon to download the content of the *Data Output* * Use the *Save results to file* icon to save the content of the *Data Output*
tab as a comma-delimited file. tab as a comma-delimited file.
* Edit the data in the result set of a SELECT query if it is updatable. * Edit the data in the result set of a SELECT query if it is updatable.

View File

@ -179,10 +179,10 @@ Query Execution
| | | | | | | |
| | * Select *Clear History* to erase the content of the *History* tab. | | | | * Select *Clear History* to erase the content of the *History* tab. | |
+----------------------+---------------------------------------------------------------------------------------------------+----------------+ +----------------------+---------------------------------------------------------------------------------------------------+----------------+
| *Download as CSV/TXT*| Click the *Download as CSV/TXT* icon to download the result set of the current query as a *.csv* | F8 | | *Save results to* | Click the Save results to file icon to save the result set of the current query as a delimited | F8 |
| | or as a *.txt* file. if *CSV field seperator* set to comma(,) else as a *.txt* file. | | | *file* | text file (CSV, if the field separator is set to a comma). This button will only be enabled when | |
| | You can specify the CSV/TXT settings through *Preferences -> SQL Editor -> CSV/TXT output* | | | | a query has been executed and there are results in the data grid. You can specify the CSV/TXT | |
| | dialogue. | | | | settings in the Preference Dialogue under SQL Editor -> CSV/TXT output. | |
+----------------------+---------------------------------------------------------------------------------------------------+----------------+ +----------------------+---------------------------------------------------------------------------------------------------+----------------+
| *Macros* | Click the *Macros* icon to manage the macros. You can create, edit or clear the macros through | | | *Macros* | Click the *Macros* icon to manage the macros. You can create, edit or clear the macros through | |
| | the *Manage Macros* option. | | | | the *Manage Macros* option. | |

View File

@ -17,4 +17,5 @@ Housekeeping
Bug fixes Bug fixes
********* *********
| `Issue #5965 <https://redmine.postgresql.org/issues/5965>`_ - Ensure that the macro query result should be download properly.
| `Issue #6058 <https://redmine.postgresql.org/issues/6058>`_ - Ensure that the rename panel should be disabled when the SQL file opened in the query tool. | `Issue #6058 <https://redmine.postgresql.org/issues/6058>`_ - Ensure that the rename panel should be disabled when the SQL file opened in the query tool.

View File

@ -191,7 +191,7 @@ function keyboardShortcutsQueryTool(
let executeKeys = sqlEditorController.preferences.execute_query; let executeKeys = sqlEditorController.preferences.execute_query;
let explainKeys = sqlEditorController.preferences.explain_query; let explainKeys = sqlEditorController.preferences.explain_query;
let explainAnalyzeKeys = sqlEditorController.preferences.explain_analyze_query; let explainAnalyzeKeys = sqlEditorController.preferences.explain_analyze_query;
let downloadCsvKeys = sqlEditorController.preferences.download_csv; let downloadCsvKeys = sqlEditorController.preferences.download_results;
let nextTabKeys = sqlEditorController.preferences.move_next; let nextTabKeys = sqlEditorController.preferences.move_next;
let previousTabKeys = sqlEditorController.preferences.move_previous; let previousTabKeys = sqlEditorController.preferences.move_previous;
let switchPanelKeys = sqlEditorController.preferences.switch_panel; let switchPanelKeys = sqlEditorController.preferences.switch_panel;
@ -211,8 +211,10 @@ function keyboardShortcutsQueryTool(
this._stopEventPropagation(event); this._stopEventPropagation(event);
queryToolActions.explainAnalyze(sqlEditorController); queryToolActions.explainAnalyze(sqlEditorController);
} else if (this.validateShortcutKeys(downloadCsvKeys, event)) { } else if (this.validateShortcutKeys(downloadCsvKeys, event)) {
this._stopEventPropagation(event); if(!sqlEditorController.is_save_results_to_file_disabled) {
queryToolActions.download(sqlEditorController); this._stopEventPropagation(event);
queryToolActions.download(sqlEditorController);
}
} else if (this.validateShortcutKeys(toggleCaseKeys, event)) { } else if (this.validateShortcutKeys(toggleCaseKeys, event)) {
this._stopEventPropagation(event); this._stopEventPropagation(event);
queryToolActions.toggleCaseOfSelectedText(sqlEditorController); queryToolActions.toggleCaseOfSelectedText(sqlEditorController);

View File

@ -43,6 +43,7 @@ export function callRenderAfterPoll(sqlEditor, alertify, res) {
if (isNotificationEnabled(sqlEditor)) { if (isNotificationEnabled(sqlEditor)) {
alertify.success(msg, sqlEditor.info_notifier_timeout); alertify.success(msg, sqlEditor.info_notifier_timeout);
} }
sqlEditor.enable_disable_download_btn(true);
} }
if (isQueryTool(sqlEditor)) { if (isQueryTool(sqlEditor)) {

View File

@ -83,6 +83,7 @@ class ExecuteQuery {
} else { } else {
self.loadingScreen.hide(); self.loadingScreen.hide();
self.enableSQLEditorButtons(); self.enableSQLEditorButtons();
self.disableDownloadButton();
// Enable/Disable commit and rollback button. // Enable/Disable commit and rollback button.
if (result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INTRANS if (result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INTRANS
|| result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR) { || result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR) {
@ -201,7 +202,7 @@ class ExecuteQuery {
this.loadingScreen.show(gettext('Running query...')); this.loadingScreen.show(gettext('Running query...'));
$('#btn-flash').prop('disabled', true); $('#btn-flash').prop('disabled', true);
$('#btn-download').prop('disabled', true); this.disableDownloadButton();
this.sqlServerObject.query_start_time = new Date(); this.sqlServerObject.query_start_time = new Date();
if (typeof sqlStatement === 'object') { if (typeof sqlStatement === 'object') {
@ -281,6 +282,10 @@ class ExecuteQuery {
} }
} }
disableDownloadButton() {
this.sqlServerObject.enable_disable_download_btn(true);
}
enableSQLEditorButtons() { enableSQLEditorButtons() {
this.sqlServerObject.disable_tool_buttons(false); this.sqlServerObject.disable_tool_buttons(false);
} }

View File

@ -81,13 +81,7 @@ let queryToolActions = {
}, },
download: function (sqlEditorController) { download: function (sqlEditorController) {
let sqlQuery = sqlEditorController.gridView.query_tool_obj.getSelection();
if (!sqlQuery) {
sqlQuery = sqlEditorController.gridView.query_tool_obj.getValue();
}
if (!sqlQuery) return;
let extension = sqlEditorController.preferences.csv_field_separator === ',' ? '.csv': '.txt'; let extension = sqlEditorController.preferences.csv_field_separator === ',' ? '.csv': '.txt';
let filename = 'data-' + new Date().getTime() + extension; let filename = 'data-' + new Date().getTime() + extension;
@ -95,7 +89,7 @@ let queryToolActions = {
filename = sqlEditorController.table_name + extension; filename = sqlEditorController.table_name + extension;
} }
sqlEditorController.trigger_csv_download(sqlQuery, filename); sqlEditorController.trigger_csv_download(filename);
}, },
commentBlockCode: function (sqlEditorController) { commentBlockCode: function (sqlEditorController) {

View File

@ -112,11 +112,11 @@ function updateUIPreferences(sqlEditor) {
.attr('aria-label', .attr('aria-label',
shortcut_title(gettext('Explain Analyze'),preferences.explain_analyze_query)); shortcut_title(gettext('Explain Analyze'),preferences.explain_analyze_query));
$el.find('#btn-download') $el.find('#btn-save-results-to-file')
.attr('title', .attr('title',
shortcut_title(gettext('Download as CSV/TXT'),preferences.download_csv)) shortcut_title(gettext('Save results to file'),preferences.download_results))
.attr('aria-label', .attr('aria-label',
shortcut_title(gettext('Download as CSV/TXT'),preferences.download_csv)); shortcut_title(gettext('Save results to file'),preferences.download_results));
$el.find('#btn-save-data') $el.find('#btn-save-data')
.attr('title', .attr('title',

View File

@ -374,9 +374,9 @@
</ul> </ul>
</div> </div>
<div class="btn-group" role="group" aria-label=""> <div class="btn-group" role="group" aria-label="">
<button id="btn-download" type="button" class="btn btn-sm btn-primary-icon" <button id="btn-save-results-to-file" type="button" class="btn btn-sm btn-primary-icon"
title="" title=""
tabindex="0"> tabindex="0" disabled>
<i class="fa fa-download sql-icon-lg" aria-hidden="true" role="img"></i> <i class="fa fa-download sql-icon-lg" aria-hidden="true" role="img"></i>
</button> </button>
</div> </div>

View File

@ -1336,7 +1336,7 @@ def start_query_download_tool(trans_id):
) )
data = request.values if request.values else None data = request.values if request.values else None
if data is None or (data and 'query' not in data): if data is None:
return make_json_response( return make_json_response(
status=410, status=410,
success=0, success=0,
@ -1346,12 +1346,9 @@ def start_query_download_tool(trans_id):
) )
try: try:
sql = data['query']
# This returns generator of records. # This returns generator of records.
status, gen = sync_conn.execute_on_server_as_csv( status, gen = sync_conn.execute_on_server_as_csv(records=2000)
sql, records=2000
)
if not status: if not status:
return make_json_response( return make_json_response(
@ -1362,6 +1359,7 @@ def start_query_download_tool(trans_id):
r = Response( r = Response(
gen( gen(
trans_obj,
quote=blueprint.csv_quoting.get(), quote=blueprint.csv_quoting.get(),
quote_char=blueprint.csv_quote_char.get(), quote_char=blueprint.csv_quote_char.get(),
field_separator=blueprint.csv_field_separator.get(), field_separator=blueprint.csv_field_separator.get(),

View File

@ -136,7 +136,7 @@ define('tools.querytool', [
'click #btn-flash': 'on_flash', 'click #btn-flash': 'on_flash',
'click #btn-flash-menu': 'on_flash', 'click #btn-flash-menu': 'on_flash',
'click #btn-cancel-query': 'on_cancel_query', 'click #btn-cancel-query': 'on_cancel_query',
'click #btn-download': 'on_download', 'click #btn-save-results-to-file': 'on_download',
'click #btn-clear': 'on_clear', 'click #btn-clear': 'on_clear',
'click #btn-auto-commit': 'on_auto_commit', 'click #btn-auto-commit': 'on_auto_commit',
'click #btn-auto-rollback': 'on_auto_rollback', 'click #btn-auto-rollback': 'on_auto_rollback',
@ -1358,7 +1358,7 @@ define('tools.querytool', [
self.handler.fetching_rows = true; self.handler.fetching_rows = true;
$('#btn-flash').prop('disabled', true); $('#btn-flash').prop('disabled', true);
$('#btn-download').prop('disabled', true); $('#btn-save-results-to-file').prop('disabled', true);
if (fetch_all) { if (fetch_all) {
self.handler.trigger( self.handler.trigger(
@ -1382,7 +1382,7 @@ define('tools.querytool', [
.done(function(res) { .done(function(res) {
self.handler.has_more_rows = res.data.has_more_rows; self.handler.has_more_rows = res.data.has_more_rows;
$('#btn-flash').prop('disabled', false); $('#btn-flash').prop('disabled', false);
$('#btn-download').prop('disabled', false); $('#btn-save-results-to-file').prop('disabled', false);
self.handler.trigger('pgadmin-sqleditor:loading-icon:hide'); self.handler.trigger('pgadmin-sqleditor:loading-icon:hide');
self.update_grid_data(res.data.result); self.update_grid_data(res.data.result);
self.handler.fetching_rows = false; self.handler.fetching_rows = false;
@ -1392,7 +1392,7 @@ define('tools.querytool', [
}) })
.fail(function(e) { .fail(function(e) {
$('#btn-flash').prop('disabled', false); $('#btn-flash').prop('disabled', false);
$('#btn-download').prop('disabled', false); $('#btn-save-results-to-file').prop('disabled', false);
self.handler.trigger('pgadmin-sqleditor:loading-icon:hide'); self.handler.trigger('pgadmin-sqleditor:loading-icon:hide');
self.handler.has_more_rows = false; self.handler.has_more_rows = false;
self.handler.fetching_rows = false; self.handler.fetching_rows = false;
@ -2534,6 +2534,7 @@ define('tools.querytool', [
self.server_type = url_params.server_type; self.server_type = url_params.server_type;
self.url_params = url_params; self.url_params = url_params;
self.is_transaction_buttons_disabled = true; self.is_transaction_buttons_disabled = true;
self.is_save_results_to_file_disabled = true;
// We do not allow to call the start multiple times. // We do not allow to call the start multiple times.
if (self.gridView) if (self.gridView)
@ -2783,7 +2784,7 @@ define('tools.querytool', [
); );
$('#btn-flash').prop('disabled', true); $('#btn-flash').prop('disabled', true);
$('#btn-download').prop('disabled', true); self.enable_disable_download_btn(true);
self.trigger( self.trigger(
'pgadmin-sqleditor:loading-icon:message', 'pgadmin-sqleditor:loading-icon:message',
@ -3058,7 +3059,12 @@ define('tools.querytool', [
// Hide the loading icon // Hide the loading icon
self_col.trigger('pgadmin-sqleditor:loading-icon:hide'); self_col.trigger('pgadmin-sqleditor:loading-icon:hide');
$('#btn-flash').prop('disabled', false); $('#btn-flash').prop('disabled', false);
$('#btn-download').prop('disabled', false); if (!_.isUndefined(data) && Array.isArray(data.result) && data.result.length > 0) {
self.enable_disable_download_btn(false);
}
else {
self.enable_disable_download_btn(true);
}
}.bind(self) }.bind(self)
); );
}, },
@ -3239,7 +3245,6 @@ define('tools.querytool', [
if (status != 'Busy') { if (status != 'Busy') {
$('#btn-flash').prop('disabled', false); $('#btn-flash').prop('disabled', false);
$('#btn-download').prop('disabled', false);
self.trigger('pgadmin-sqleditor:loading-icon:hide'); self.trigger('pgadmin-sqleditor:loading-icon:hide');
if(!self.total_time) { if(!self.total_time) {
@ -3301,6 +3306,12 @@ define('tools.querytool', [
return (self.get('can_edit')); return (self.get('can_edit'));
}, },
enable_disable_download_btn: function(is_save_results_to_file_disabled) {
var self = this;
self.is_save_results_to_file_disabled = is_save_results_to_file_disabled;
$('#btn-save-results-to-file').prop('disabled', is_save_results_to_file_disabled);
},
rows_to_delete: function(data) { rows_to_delete: function(data) {
let self = this; let self = this;
let tmp_keys = self.primary_keys; let tmp_keys = self.primary_keys;
@ -4373,10 +4384,9 @@ define('tools.querytool', [
} }
self.disable_tool_buttons(false); self.disable_tool_buttons(false);
is_query_running = false; is_query_running = false;
if(!_.isUndefined(self.download_csv_obj)) { if(!_.isUndefined(self.download_results_obj)) {
self.download_csv_obj.abort(); self.download_results_obj.abort();
$('#btn-flash').prop('disabled', false); $('#btn-flash').prop('disabled', false);
$('#btn-download').prop('disabled', false);
self.trigger( self.trigger(
'pgadmin-sqleditor:loading-icon:hide'); 'pgadmin-sqleditor:loading-icon:hide');
} }
@ -4394,25 +4404,25 @@ define('tools.querytool', [
}, },
// Trigger query result download to csv. // Trigger query result download to csv.
trigger_csv_download: function(query, filename) { trigger_csv_download: function(filename) {
var self = this, var self = this,
url = url_for('sqleditor.query_tool_download', { url = url_for('sqleditor.query_tool_download', {
'trans_id': self.transId, 'trans_id': self.transId,
}), }),
data = { query: query, filename: filename }; data = { filename: filename };
// Disable the Execute button // Disable the Execute button
$('#btn-flash').prop('disabled', true); $('#btn-flash').prop('disabled', true);
$('#btn-download').prop('disabled', true); self.enable_disable_download_btn(true);
self.disable_tool_buttons(true); self.disable_tool_buttons(true);
self.set_sql_message(''); self.set_sql_message('');
self.trigger( self.trigger(
'pgadmin-sqleditor:loading-icon:show', 'pgadmin-sqleditor:loading-icon:show',
gettext('Downloading CSV...') gettext('Downloading Results...')
); );
// Get the CSV file // Get the CSV file
self.download_csv_obj = $.ajax({ self.download_results_obj = $.ajax({
type: 'POST', type: 'POST',
url: url, url: url,
data: data, data: data,
@ -4443,19 +4453,19 @@ define('tools.querytool', [
} }
document.body.removeChild(link); document.body.removeChild(link);
self.download_csv_obj = undefined; self.download_results_obj = undefined;
} }
// Enable the execute button // Enable the execute button
$('#btn-flash').prop('disabled', false); $('#btn-flash').prop('disabled', false);
$('#btn-download').prop('disabled', false); self.enable_disable_download_btn(false);
self.disable_tool_buttons(false); self.disable_tool_buttons(false);
self.trigger('pgadmin-sqleditor:loading-icon:hide'); self.trigger('pgadmin-sqleditor:loading-icon:hide');
}).fail(function(err) { }).fail(function(err) {
let msg = ''; let msg = '';
// Enable the execute button // Enable the execute button
$('#btn-flash').prop('disabled', false); $('#btn-flash').prop('disabled', false);
$('#btn-download').prop('disabled', false); self.enable_disable_download_btn(false);
self.disable_tool_buttons(false); self.disable_tool_buttons(false);
self.trigger('pgadmin-sqleditor:loading-icon:hide'); self.trigger('pgadmin-sqleditor:loading-icon:hide');

View File

@ -96,7 +96,7 @@ class TestDownloadCSV(BaseTestGenerator):
] ]
def setUp(self): def setUp(self):
self._db_name = 'download_csv_' + str(random.randint(10000, 65535)) self._db_name = 'download_results_' + str(random.randint(10000, 65535))
self._sid = self.server_information['server_id'] self._sid = self.server_information['server_id']
server_con = server_utils.connect_server(self, self._sid) server_con = server_utils.connect_server(self, self._sid)
@ -105,6 +105,24 @@ class TestDownloadCSV(BaseTestGenerator):
self.server, self._db_name self.server, self._db_name
) )
# This method is responsible for initiating query hit at least once,
# so that download csv works
def initiate_sql_query_tool(self, trans_id, sql_query):
# This code is to ensure to create a async cursor so that downloading
# csv can work.
# Start query tool transaction
url = '/sqleditor/query_tool/start/{0}'.format(trans_id)
response = self.tester.post(url, data=json.dumps({"sql": sql_query}),
content_type='html/json')
self.assertEqual(response.status_code, 200)
# Query tool polling
url = '/sqleditor/poll/{0}'.format(trans_id)
response = self.tester.get(url)
return response
def runTest(self): def runTest(self):
db_con = database_utils.connect_database(self, db_con = database_utils.connect_database(self,
@ -121,6 +139,8 @@ class TestDownloadCSV(BaseTestGenerator):
response = self.tester.post(url) response = self.tester.post(url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
res = self.initiate_sql_query_tool(self.trans_id, self.sql)
# If invalid tx test then make the Tx id invalid so that tests fails # If invalid tx test then make the Tx id invalid so that tests fails
if not self.is_valid_tx: if not self.is_valid_tx:
self.trans_id = self.trans_id + '007' self.trans_id = self.trans_id + '007'
@ -129,7 +149,11 @@ class TestDownloadCSV(BaseTestGenerator):
url = self.donwload_url.format(self.trans_id) url = self.donwload_url.format(self.trans_id)
# Disable the console logging from Flask logger # Disable the console logging from Flask logger
self.app.logger.disabled = True self.app.logger.disabled = True
if self.filename is None: if not self.is_valid and self.is_valid_tx:
# When user enters wrong query, poll will throw 500, so expecting
# 500, as poll is never called for a wrong query.
self.assertEqual(res.status_code, 500)
elif self.filename is None:
if self.download_as_txt: if self.download_as_txt:
with patch('pgadmin.tools.sqleditor.blueprint.' with patch('pgadmin.tools.sqleditor.blueprint.'
'csv_field_separator.get', return_value=';'), patch( 'csv_field_separator.get', return_value=';'), patch(

View File

@ -377,8 +377,8 @@ def register_query_tool_preferences(self):
self.preference.register( self.preference.register(
'keyboard_shortcuts', 'keyboard_shortcuts',
'download_csv', 'download_results',
gettext('Download CSV'), gettext('Download Results'),
'keyboardshortcut', 'keyboardshortcut',
{ {
'alt': False, 'alt': False,

View File

@ -728,27 +728,35 @@ WHERE db.datname = current_database()""")
if self.async_ == 1: if self.async_ == 1:
self._wait(cur.connection) self._wait(cur.connection)
def execute_on_server_as_csv(self, def execute_on_server_as_csv(self, params=None,
query, params=None, formatted_exception_msg=False, records=2000):
formatted_exception_msg=False,
records=2000):
""" """
To fetch query result and generate CSV output To fetch query result and generate CSV output
Args: Args:
query: SQL
params: Additional parameters params: Additional parameters
formatted_exception_msg: For exception formatted_exception_msg: For exception
records: Number of initial records records: Number of initial records
Returns: Returns:
Generator response Generator response
""" """
status, cur = self.__cursor() cur = self.__async_cursor
self.row_count = 0 if not cur:
return False, self.CURSOR_NOT_FOUND
if not status: if self.conn.isexecuting():
return False, str(cur) return False, gettext(
query_id = random.randint(1, 9999999) "Asynchronous query execution/operation underway."
)
encoding = self.python_encoding
query = None
try:
query = str(cur.query, encoding) \
if cur and cur.query is not None else None
except Exception:
current_app.logger.warning('Error encoding query')
pass
current_app.logger.log( current_app.logger.log(
25, 25,
@ -757,13 +765,14 @@ WHERE db.datname = current_database()""")
server_id=self.manager.sid, server_id=self.manager.sid,
conn_id=self.conn_id, conn_id=self.conn_id,
query=query, query=query,
query_id=query_id query_id=self.__async_query_id
) )
) )
try: try:
# Unregistering type casting for large size data types. # Unregistering type casting for large size data types.
unregister_numeric_typecasters(self.conn) unregister_numeric_typecasters(self.conn)
self.__internal_blocking_execute(cur, query, params) if self.async_ == 1:
self._wait(cur.connection)
except psycopg2.Error as pe: except psycopg2.Error as pe:
cur.close() cur.close()
errmsg = self._formatted_exception_msg(pe, formatted_exception_msg) errmsg = self._formatted_exception_msg(pe, formatted_exception_msg)
@ -775,7 +784,7 @@ WHERE db.datname = current_database()""")
server_id=self.manager.sid, server_id=self.manager.sid,
conn_id=self.conn_id, conn_id=self.conn_id,
errmsg=errmsg, errmsg=errmsg,
query_id=query_id query_id=self.__async_query_id
) )
) )
return False, errmsg return False, errmsg
@ -809,13 +818,12 @@ WHERE db.datname = current_database()""")
return results return results
def gen(quote='strings', quote_char="'", field_separator=',', def gen(trans_obj, quote='strings', quote_char="'",
replace_nulls_with=None): field_separator=',', replace_nulls_with=None):
cur.scroll(0, mode='absolute')
results = cur.fetchmany(records) results = cur.fetchmany(records)
if not results: if not results:
if not cur.closed:
cur.close()
yield gettext('The query executed did not return any data.') yield gettext('The query executed did not return any data.')
return return
@ -857,8 +865,6 @@ WHERE db.datname = current_database()""")
results = cur.fetchmany(records) results = cur.fetchmany(records)
if not results: if not results:
if not cur.closed:
cur.close()
break break
res_io = StringIO() res_io = StringIO()
@ -874,6 +880,17 @@ WHERE db.datname = current_database()""")
results = handle_null_values(results, replace_nulls_with) results = handle_null_values(results, replace_nulls_with)
csv_writer.writerows(results) csv_writer.writerows(results)
yield res_io.getvalue() yield res_io.getvalue()
try:
# try to reset the cursor scroll back to where it was,
# bypass error, if cannot scroll back
rows_fetched_from = trans_obj.get_fetched_row_cnt()
cur.scroll(rows_fetched_from, mode='absolute')
except psycopg2.Error:
# bypassing the error as cursor tried to scroll on the
# specified position, but end of records found
pass
# Registering back type caster for large size data types to string # Registering back type caster for large size data types to string
# which was unregistered at starting # which was unregistered at starting
register_string_typecasters(self.conn) register_string_typecasters(self.conn)
@ -1224,6 +1241,7 @@ WHERE db.datname = current_database()""")
Args: Args:
records: no of records to fetch. use -1 to fetchall. records: no of records to fetch. use -1 to fetchall.
formatted_exception_msg: formatted_exception_msg:
for_download: if True, will fetch all records and reset the cursor
Returns: Returns:

View File

@ -24,6 +24,7 @@ describe('#callRenderAfterPoll', () => {
disable_tool_buttons: jasmine.createSpy('SQLEditor.disable_tool_buttons'), disable_tool_buttons: jasmine.createSpy('SQLEditor.disable_tool_buttons'),
disable_transaction_buttons: jasmine.createSpy('SQLEditor.disable_transaction_buttons'), disable_transaction_buttons: jasmine.createSpy('SQLEditor.disable_transaction_buttons'),
reset_data_store: jasmine.createSpy('SQLEditor.reset_data_store'), reset_data_store: jasmine.createSpy('SQLEditor.reset_data_store'),
enable_disable_download_btn: jasmine.createSpy('SQLEditor.enable_disable_download_btn'),
query_start_time: new Date(), query_start_time: new Date(),
}; };
alertify = jasmine.createSpyObj('alertify', ['success']); alertify = jasmine.createSpyObj('alertify', ['success']);
@ -115,6 +116,14 @@ describe('#callRenderAfterPoll', () => {
); );
}); });
}); });
it('disables the save results button', () => {
callRenderAfterPoll(sqlEditorSpy, alertify, queryResult);
expect(sqlEditorSpy.enable_disable_download_btn).toHaveBeenCalledWith(true);
expect(sqlEditorSpy.trigger).toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
});
}); });
}); });
@ -212,6 +221,14 @@ describe('#callRenderAfterPoll', () => {
); );
}); });
}); });
it('disables the save results button', () => {
callRenderAfterPoll(sqlEditorSpy, alertify, queryResult);
expect(sqlEditorSpy.enable_disable_download_btn).toHaveBeenCalledWith(true);
expect(sqlEditorSpy.trigger).toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
});
}); });
}); });
}); });

View File

@ -45,6 +45,7 @@ describe('ExecuteQuery', () => {
'handle_connection_lost', 'handle_connection_lost',
'update_notifications', 'update_notifications',
'disable_transaction_buttons', 'disable_transaction_buttons',
'enable_disable_download_btn',
]); ]);
sqlEditorMock.transId = 123; sqlEditorMock.transId = 123;
sqlEditorMock.rows_affected = 1000; sqlEditorMock.rows_affected = 1000;

View File

@ -71,7 +71,7 @@ describe('the keyboard shortcuts', () => {
key_code: F7_KEY, key_code: F7_KEY,
}, },
}, },
download_csv: { download_results: {
alt: false, alt: false,
shift: false, shift: false,
control: false, control: false,

View File

@ -277,7 +277,7 @@ describe('queryToolActions', () => {
it('does nothing', () => { it('does nothing', () => {
queryToolActions.download(sqlEditorController); queryToolActions.download(sqlEditorController);
expect(sqlEditorController.trigger_csv_download).not.toHaveBeenCalled(); expect(sqlEditorController.trigger_csv_download).toHaveBeenCalled();
}); });
}); });
@ -298,21 +298,21 @@ describe('queryToolActions', () => {
})); }));
}); });
it('calls trigger_csv_download with the query and the filename with .csv extension', () => { it('calls trigger_csv_download with filename having .csv extension', () => {
let filename = 'data-' + time + '.csv'; let filename = 'data-' + time + '.csv';
queryToolActions.download(sqlEditorController); queryToolActions.download(sqlEditorController);
expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith(selectedQueryString, filename); expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith(filename);
}); });
it('calls trigger_csv_download with the query and the filename with .txt extension', () => { it('calls trigger_csv_download filename having .txt extension', () => {
sqlEditorController.preferences.csv_field_separator = ';'; sqlEditorController.preferences.csv_field_separator = ';';
let filename = 'data-' + time + '.txt'; let filename = 'data-' + time + '.txt';
queryToolActions.download(sqlEditorController); queryToolActions.download(sqlEditorController);
expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith(selectedQueryString, filename); expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith(filename);
}); });
}); });
@ -333,12 +333,12 @@ describe('queryToolActions', () => {
})); }));
}); });
it('calls trigger_csv_download with the query and the filename', () => { it('calls trigger_csv_download with filename', () => {
let filename = 'data-' + time + '.csv'; let filename = 'data-' + time + '.csv';
queryToolActions.download(sqlEditorController); queryToolActions.download(sqlEditorController);
expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith(entireQueryString, filename); expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith(filename);
}); });
}); });
}); });
@ -351,7 +351,7 @@ describe('queryToolActions', () => {
queryToolActions.download(sqlEditorController); queryToolActions.download(sqlEditorController);
expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith(query, 'iAmATable' + '.csv'); expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith('iAmATable' + '.csv');
}); });
}); });