diff --git a/js/src/functions/Sql/SqlEditor.js b/js/src/functions/Sql/SqlEditor.js new file mode 100644 index 0000000000..519143d5c7 --- /dev/null +++ b/js/src/functions/Sql/SqlEditor.js @@ -0,0 +1,117 @@ +import { sqlQueryOptions } from '../../utils/sql'; +import CommonParams from '../../variables/common_params'; +import { AJAX } from '../../ajax'; +import { codemirrorAutocompleteOnInputRead } from '../../utils/sql'; + +import CodeMirror from 'codemirror'; +import 'codemirror/mode/sql/sql.js'; +import 'codemirror/addon/runmode/runmode.js'; +import 'codemirror/addon/hint/show-hint.js'; +import 'codemirror/addon/hint/sql-hint.js'; +import 'codemirror/addon/lint/lint.js'; +import '../../plugins/codemirror/sql-lint'; + +function catchKeypressesFromSqlInlineEdit (event) { + // ctrl-enter is 10 in chrome and ie, but 13 in ff + if ((event.ctrlKey || event.metaKey) && (event.keyCode === 13 || event.keyCode === 10)) { + $('#sql_query_edit_save').trigger('click'); + } +} + +/** + * Creates an SQL editor which supports auto completing etc. + * + * @param $textarea jQuery object wrapping the textarea to be made the editor + * @param options optional options for CodeMirror + * @param resize optional resizing ('vertical', 'horizontal', 'both') + * @param lintOptions additional options for lint + */ + +export function PMA_getSQLEditor ($textarea, options, resize, lintOptions) { + if ($textarea.length > 0 && CommonParams.get('CodemirrorEnable') === true) { + // merge options for CodeMirror + var defaults = { + lineNumbers: true, + matchBrackets: true, + extraKeys: { 'Ctrl-Space': 'autocomplete' }, + hintOptions: { 'completeSingle': false, 'completeOnSingleClick': true }, + indentUnit: 4, + mode: 'text/x-mysql', + lineWrapping: true + }; + + if (CommonParams.get('LintEnable')) { + $.extend(defaults, { + gutters: ['CodeMirror-lint-markers'], + lint: { + 'getAnnotations': CodeMirror.sqlLint, + 'async': true, + 'lintOptions': lintOptions + } + }); + } + + $.extend(true, defaults, options); + + // create CodeMirror editor + var codemirrorEditor = CodeMirror.fromTextArea($textarea[0], defaults); + // allow resizing + if (! resize) { + resize = 'vertical'; + } + var handles = ''; + if (resize === 'vertical') { + handles = 's'; + } + if (resize === 'both') { + handles = 'all'; + } + if (resize === 'horizontal') { + handles = 'e, w'; + } + $(codemirrorEditor.getWrapperElement()) + .css('resize', resize) + .resizable({ + handles: handles, + resize: function () { + codemirrorEditor.setSize($(this).width(), $(this).height()); + } + }); + // enable autocomplete + codemirrorEditor.on('inputRead', codemirrorAutocompleteOnInputRead); + + // page locking + codemirrorEditor.on('change', function (e) { + e.data = { + value: 3, + content: codemirrorEditor.isClean(), + }; + AJAX.lockPageHandler(e); + }); + + return codemirrorEditor; + } + return null; +} + +/** + * Binds the CodeMirror to the text area used to inline edit a query. + */ +export function bindCodeMirrorToInlineEditor () { + var $inline_editor = $('#sql_query_edit'); + if ($inline_editor.length > 0) { + if (CommonParams.get('CodemirrorEnable') === true) { + var height = $inline_editor.css('height'); + sqlQueryOptions.codemirror_inline_editor = PMA_getSQLEditor($inline_editor); + sqlQueryOptions.codemirror_inline_editor.getWrapperElement().style.height = height; + sqlQueryOptions.codemirror_inline_editor.refresh(); + sqlQueryOptions.codemirror_inline_editor.focus(); + $(sqlQueryOptions.codemirror_inline_editor.getWrapperElement()) + .on('keydown', catchKeypressesFromSqlInlineEdit); + } else { + $inline_editor + .focus() + .on('keydown', catchKeypressesFromSqlInlineEdit); + } + } +} diff --git a/js/src/functions/Sql/SqlProfiling.js b/js/src/functions/Sql/SqlProfiling.js new file mode 100644 index 0000000000..3fe91495ab --- /dev/null +++ b/js/src/functions/Sql/SqlProfiling.js @@ -0,0 +1,57 @@ +import { createProfilingChart } from '../chart'; + +/* + * Profiling Chart + */ +export function makeProfilingChart () { + if ($('#profilingchart').length === 0 || + $('#profilingchart').html().length !== 0 || + !$.jqplot || !$.jqplot.Highlighter || !$.jqplot.PieRenderer + ) { + return; + } + + var data = []; + $.each(JSON.parse($('#profilingChartData').html()), function (key, value) { + data.push([key, parseFloat(value)]); + }); + + // Remove chart and data divs contents + $('#profilingchart').html('').show(); + $('#profilingChartData').html(''); + + createProfilingChart('profilingchart', data); +} + +/* + * initialize profiling data tables + */ +export function initProfilingTables () { + if (!$.tablesorter) { + return; + } + + $('#profiletable').tablesorter({ + widgets: ['zebra'], + sortList: [[0, 0]], + textExtraction: function (node) { + if (node.children.length > 0) { + return node.children[0].innerHTML; + } else { + return node.innerHTML; + } + } + }); + + $('#profilesummarytable').tablesorter({ + widgets: ['zebra'], + sortList: [[1, 1]], + textExtraction: function (node) { + if (node.children.length > 0) { + return node.children[0].innerHTML; + } else { + return node.innerHTML; + } + } + }); +} diff --git a/js/src/functions/Sql/SqlQuery.js b/js/src/functions/Sql/SqlQuery.js new file mode 100644 index 0000000000..4d80e80521 --- /dev/null +++ b/js/src/functions/Sql/SqlQuery.js @@ -0,0 +1,325 @@ +import { isStorageSupported } from '../config'; +import Cookies from 'js-cookie'; +import { sqlQueryOptions } from '../../utils/sql'; +import { GlobalVariables, PMA_Messages as PMA_messages } from '../../variables/export_variables'; +import { PMA_ajaxShowMessage } from '../../utils/show_ajax_messages'; +import { PMA_sprintf } from '../../utils/sprintf'; + +/** + * Handles 'Simulate query' button on SQL query box. + * + * @return void + */ +export function PMA_handleSimulateQueryButton () { + var update_re = new RegExp('^\\s*UPDATE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+SET\\s', 'i'); + var delete_re = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i'); + var query = ''; + + if (sqlQueryOptions.codemirror_editor) { + query = sqlQueryOptions.codemirror_editor.getValue(); + } else { + query = $('#sqlquery').val(); + } + + var $simulateDml = $('#simulate_dml'); + if (update_re.test(query) || delete_re.test(query)) { + if (! $simulateDml.length) { + $('#button_submit_query') + .before(''); + } + } else { + if ($simulateDml.length) { + $simulateDml.remove(); + } + } +} + +/** + * Sets current value for query box. + */ +function setQuery (query) { + if (sqlQueryOptions.codemirror_editor) { + sqlQueryOptions.codemirror_editor.setValue(query); + sqlQueryOptions.codemirror_editor.focus(); + } else if (document.sqlform) { + document.sqlform.sql_query.value = query; + document.sqlform.sql_query.focus(); + } +} + +/** + * Create quick sql statements. + * + */ +export function insertQuery (queryType) { + if (queryType === 'clear') { + setQuery(''); + return; + } else if (queryType === 'format') { + if (sqlQueryOptions.codemirror_editor) { + $('#querymessage').html(PMA_messages.strFormatting + + ' '); + var href = 'db_sql_format.php'; + var params = { + 'ajax_request': true, + 'sql': sqlQueryOptions.codemirror_editor.getValue() + }; + $.ajax({ + type: 'POST', + url: href, + data: params, + success: function (data) { + if (data.success) { + sqlQueryOptions.codemirror_editor.setValue(data.sql); + } + $('#querymessage').html(''); + } + }); + } + return; + } else if (queryType === 'saved') { + if (isStorageSupported('localStorage') + && typeof window.localStorage.auto_saved_sql !== 'undefined' + ) { + setQuery(window.localStorage.auto_saved_sql); + } else if (Cookies.get('auto_saved_sql')) { + setQuery(Cookies.get('auto_saved_sql')); + } else { + PMA_ajaxShowMessage(PMA_messages.strNoAutoSavedQuery); + } + return; + } + + var query = ''; + var myListBox = document.sqlform.dummy; + var table = document.sqlform.table.value; + + if (myListBox.options.length > 0) { + sql_box_locked = true; + var columnsList = ''; + var valDis = ''; + var editDis = ''; + var NbSelect = 0; + for (var i = 0; i < myListBox.options.length; i++) { + NbSelect++; + if (NbSelect > 1) { + columnsList += ', '; + valDis += ','; + editDis += ','; + } + columnsList += myListBox.options[i].value; + valDis += '[value-' + NbSelect + ']'; + editDis += myListBox.options[i].value + '=[value-' + NbSelect + ']'; + } + if (queryType === 'selectall') { + query = 'SELECT * FROM `' + table + '` WHERE 1'; + } else if (queryType === 'select') { + query = 'SELECT ' + columnsList + ' FROM `' + table + '` WHERE 1'; + } else if (queryType === 'insert') { + query = 'INSERT INTO `' + table + '`(' + columnsList + ') VALUES (' + valDis + ')'; + } else if (queryType === 'update') { + query = 'UPDATE `' + table + '` SET ' + editDis + ' WHERE 1'; + } else if (queryType === 'delete') { + query = 'DELETE FROM `' + table + '` WHERE 0'; + } + setQuery(query); + sql_box_locked = false; + } +} + +/** + * Confirms a "DROP/DELETE/ALTER" query before + * submitting it if required. + * This function is called by the 'checkSqlQuery()' js function. + * + * @param theForm1 object the form + * @param sqlQuery1 string the sql query string + * + * @return boolean whether to run the query or not + * + * @see checkSqlQuery() + */ +function confirmQuery (theForm1, sqlQuery1) { + // Confirmation is not required in the configuration file + if (PMA_messages.strDoYouReally === '') { + return true; + } + + // Confirms a "DROP/DELETE/ALTER/TRUNCATE" statement + // + // TODO: find a way (if possible) to use the parser-analyser + // for this kind of verification + // For now, I just added a ^ to check for the statement at + // beginning of expression + + var do_confirm_re_0 = new RegExp('^\\s*DROP\\s+(IF EXISTS\\s+)?(TABLE|PROCEDURE)\\s', 'i'); + var do_confirm_re_1 = new RegExp('^\\s*ALTER\\s+TABLE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+DROP\\s', 'i'); + var do_confirm_re_2 = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i'); + var do_confirm_re_3 = new RegExp('^\\s*TRUNCATE\\s', 'i'); + + if (do_confirm_re_0.test(sqlQuery1) || + do_confirm_re_1.test(sqlQuery1) || + do_confirm_re_2.test(sqlQuery1) || + do_confirm_re_3.test(sqlQuery1)) { + var message; + if (sqlQuery1.length > 100) { + message = sqlQuery1.substr(0, 100) + '\n ...'; + } else { + message = sqlQuery1; + } + var is_confirmed = confirm(PMA_sprintf(PMA_messages.strDoYouReally, message)); + // statement is confirmed -> update the + // "is_js_confirmed" form field so the confirm test won't be + // run on the server side and allows to submit the form + if (is_confirmed) { + theForm1.elements.is_js_confirmed.value = 1; + return true; + } else { + // statement is rejected -> do not submit the form + window.focus(); + return false; + } // end if (handle confirm box result) + } // end if (display confirm box) + + return true; +} // end of the 'confirmQuery()' function + +/** + * Displays an error message if the user submitted the sql query form with no + * sql query, else checks for "DROP/DELETE/ALTER" statements + * + * @param theForm object the form + * + * @return boolean always false + * + * @see confirmQuery() + */ +export function checkSqlQuery (theForm) { + // get the textarea element containing the query + var sqlQuery; + if (sqlQueryOptions.codemirror_editor) { + sqlQueryOptions.codemirror_editor.save(); + sqlQuery = sqlQueryOptions.codemirror_editor.getValue(); + } else { + sqlQuery = theForm.elements.sql_query.value; + } + var space_re = new RegExp('\\s+'); + if (typeof(theForm.elements.sql_file) !== 'undefined' && + theForm.elements.sql_file.value.replace(space_re, '') !== '') { + return true; + } + if (typeof(theForm.elements.id_bookmark) !== 'undefined' && + (theForm.elements.id_bookmark.value !== null || theForm.elements.id_bookmark.value !== '') && + theForm.elements.id_bookmark.selectedIndex !== 0) { + return true; + } + var result = false; + // Checks for "DROP/DELETE/ALTER" statements + if (sqlQuery.replace(space_re, '') !== '') { + result = confirmQuery(theForm, sqlQuery); + } else { + alert(PMA_messages.strFormEmpty); + } + + if (sqlQueryOptions.codemirror_editor) { + sqlQueryOptions.codemirror_editor.focus(); + } else if (sqlQueryOptions.codemirror_inline_editor) { + sqlQueryOptions.codemirror_inline_editor.focus(); + } + return result; +} // end of the 'checkSqlQuery()' function + +export function checkSavedQuery () { + if (isStorageSupported('localStorage') + && window.localStorage.auto_saved_sql !== undefined + ) { + PMA_ajaxShowMessage(PMA_messages.strPreviousSaveQuery); + } +} + +/** + * Set query to codemirror if show this query is + * checked and query for the db and table pair exists + */ +export function setShowThisQuery () { + var db = $('input[name="db"]').val(); + var table = $('input[name="table"]').val(); + if (isStorageSupported('localStorage')) { + if (window.localStorage.show_this_query_object !== undefined) { + var stored_db = JSON.parse(window.localStorage.show_this_query_object).db; + var stored_table = JSON.parse(window.localStorage.show_this_query_object).table; + var stored_query = JSON.parse(window.localStorage.show_this_query_object).query; + } + if (window.localStorage.show_this_query !== undefined + && window.localStorage.show_this_query === '1') { + $('input[name="show_query"]').prop('checked', true); + if ((db === stored_db && table === stored_table) || (db === undefined && table === undefined)) { + if (sqlQueryOptions.codemirror_editor) { + sqlQueryOptions.codemirror_editor.setValue(stored_query); + } else if (document.sqlform) { + document.sqlform.sql_query.value = stored_query; + } + } + } else { + $('input[name="show_query"]').prop('checked', false); + } + } +} + + +/** + * Saves SQL query in local storage or cookie + * + * @param string database name + * @param string table name + * @param string SQL query + * @return void + */ +export function PMA_showThisQuery (db, table, query) { + var show_this_query_object = { + 'db': db, + 'table': table, + 'query': query + }; + if (isStorageSupported('localStorage')) { + window.localStorage.show_this_query = 1; + window.localStorage.show_this_query_object = JSON.stringify(show_this_query_object); + } else { + Cookies.set('show_this_quey', 1); + Cookies.set('show_this_query_object', JSON.stringify(show_this_query_object)); + } +} + +/** + * Saves SQL query in local storage or cookie + * + * @param string SQL query + * @return void + */ +export function PMA_autosaveSQL (query) { + if (isStorageSupported('localStorage')) { + window.localStorage.auto_saved_sql = query; + } else { + Cookies.set('auto_saved_sql', query); + } +} + +/** + * Saves SQL query with sort in local storage or cookie + * + * @param string SQL query + * @return void + */ +export function PMA_autosaveSQLSort (query) { + if (query) { + if (isStorageSupported('localStorage')) { + window.localStorage.auto_saved_sql_sort = query; + } else { + Cookies.set('auto_saved_sql_sort', query); + } + } +}