mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Fixed an issue where format sql was messing up operator. #5083
- The formatting is done on client side now using sql-formattor JS lib. Backend API based formatting is removed. - Added new options data type case, expression width, function case, lines between queries, logical operator new line and new line before semicolon available in the new lib. - Removed old options comma-first notations, re-indent aligned, re-indent, strip comments and wrap after N characters as not available in the new library. - Capitalise casing is replaced with Preserve casing as it is not supported by the new library. - Also fixes #6785, #6990, #7115.
This commit is contained in:
@@ -120,9 +120,6 @@ class MiscModule(PgAdminModule):
|
||||
from .file_manager import blueprint as module
|
||||
self.submodules.append(module)
|
||||
|
||||
from .sql import blueprint as module
|
||||
self.submodules.append(module)
|
||||
|
||||
from .statistics import blueprint as module
|
||||
self.submodules.append(module)
|
||||
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""A blueprint module providing utility functions for the application."""
|
||||
|
||||
import sqlparse
|
||||
from flask import request, url_for
|
||||
from flask_security import login_required
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.ajax import make_json_response
|
||||
from pgadmin.utils.preferences import Preferences
|
||||
|
||||
MODULE_NAME = 'sql'
|
||||
|
||||
|
||||
class SQLModule(PgAdminModule):
|
||||
def get_exposed_url_endpoints(self):
|
||||
"""
|
||||
Returns:
|
||||
list: URL endpoints
|
||||
"""
|
||||
return [
|
||||
'sql.format', 'sql.format'
|
||||
]
|
||||
|
||||
|
||||
# Initialise the module
|
||||
blueprint = SQLModule(MODULE_NAME, __name__, url_prefix='/misc/sql')
|
||||
|
||||
|
||||
def sql_format(sql):
|
||||
"""
|
||||
This function takes a SQL statement, formats it, and returns it
|
||||
"""
|
||||
p = Preferences.module('sqleditor')
|
||||
use_spaces = p.preference('use_spaces').get()
|
||||
output = sqlparse.format(sql,
|
||||
keyword_case=p.preference(
|
||||
'keyword_case').get(),
|
||||
identifier_case=p.preference(
|
||||
'identifier_case').get(),
|
||||
strip_comments=p.preference(
|
||||
'strip_comments').get(),
|
||||
reindent=p.preference(
|
||||
'reindent').get(),
|
||||
reindent_aligned=p.preference(
|
||||
'reindent_aligned').get(),
|
||||
use_space_around_operators=p.preference(
|
||||
'spaces_around_operators').get(),
|
||||
comma_first=p.preference(
|
||||
'comma_first').get(),
|
||||
wrap_after=p.preference(
|
||||
'wrap_after').get(),
|
||||
indent_tabs=not use_spaces,
|
||||
indent_width=p.preference(
|
||||
'tab_size').get() if use_spaces else 1)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
@blueprint.route("/format", methods=['POST'], endpoint="format")
|
||||
@login_required
|
||||
def sql_format_wrapper():
|
||||
"""
|
||||
This endpoint takes a SQL statement, formats it, and returns it
|
||||
"""
|
||||
sql = ''
|
||||
if request.data:
|
||||
sql = sql_format(request.get_json()['sql'])
|
||||
|
||||
return make_json_response(
|
||||
data={'sql': sql},
|
||||
status=200
|
||||
)
|
||||
@@ -8,6 +8,7 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
import React, {useContext, useCallback, useEffect } from 'react';
|
||||
import { format } from 'sql-formatter';
|
||||
import { QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
|
||||
import CodeMirror from '../../../../../../static/js/components/CodeMirror';
|
||||
import {PANELS, QUERY_TOOL_EVENTS} from '../QueryToolConstants';
|
||||
@@ -444,19 +445,35 @@ export default function Query() {
|
||||
});
|
||||
eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_FORMAT_SQL, ()=>{
|
||||
let selection = true, sql = editor.current?.getSelection();
|
||||
let sqlEditorPref = preferencesStore.getPreferencesForModule('sqleditor');
|
||||
/* New library does not support capitalize casing
|
||||
so if a user has set capitalize casing we will
|
||||
use preserve casing which is default for the library.
|
||||
*/
|
||||
let formatPrefs = {
|
||||
language: 'postgresql',
|
||||
keywordCase: sqlEditorPref.keyword_case === 'capitalize' ? 'preserve' : sqlEditorPref.keyword_case,
|
||||
identifierCase: sqlEditorPref.identifier_case === 'capitalize' ? 'preserve' : sqlEditorPref.identifier_case,
|
||||
dataTypeCase: sqlEditorPref.data_type_case,
|
||||
functionCase: sqlEditorPref.function_case,
|
||||
logicalOperatorNewline: sqlEditorPref.logical_operator_new_line,
|
||||
expressionWidth: sqlEditorPref.expression_width,
|
||||
linesBetweenQueries: sqlEditorPref.lines_between_queries,
|
||||
tabWidth: sqlEditorPref.tab_size,
|
||||
useTabs: !sqlEditorPref.use_spaces,
|
||||
denseOperators: !sqlEditorPref.spaces_around_operators,
|
||||
newlineBeforeSemicolon: sqlEditorPref.new_line_before_semicolon
|
||||
};
|
||||
if(sql == '') {
|
||||
sql = editor.current.getValue();
|
||||
selection = false;
|
||||
}
|
||||
queryToolCtx.api.post(url_for('sql.format'), {
|
||||
'sql': sql,
|
||||
}).then((res)=>{
|
||||
if(selection) {
|
||||
editor.current.replaceSelection(res.data.data.sql, 'around');
|
||||
} else {
|
||||
editor.current.setValue(res.data.data.sql);
|
||||
}
|
||||
}).catch(()=>{/* failure should be ignored */});
|
||||
let formattedSql = format(sql,formatPrefs);
|
||||
if(selection) {
|
||||
editor.current.replaceSelection(formattedSql, 'around');
|
||||
} else {
|
||||
editor.current.setValue(formattedSql);
|
||||
}
|
||||
});
|
||||
eventBus.registerListener(QUERY_TOOL_EVENTS.EDITOR_TOGGLE_CASE, ()=>{
|
||||
let selectedText = editor.current?.getSelection();
|
||||
|
||||
@@ -735,10 +735,10 @@ def register_query_tool_preferences(self):
|
||||
gettext("Keyword case"), 'radioModern', 'upper',
|
||||
options=[{'label': gettext('Upper case'), 'value': 'upper'},
|
||||
{'label': gettext('Lower case'), 'value': 'lower'},
|
||||
{'label': gettext('Capitalized'), 'value': 'capitalize'}],
|
||||
{'label': gettext('Preserve'), 'value': 'preserve'}],
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext(
|
||||
'Convert keywords to upper, lower, or capitalized casing.'
|
||||
'Convert keywords to upper, lower, or preserve casing.'
|
||||
)
|
||||
)
|
||||
|
||||
@@ -747,35 +747,35 @@ def register_query_tool_preferences(self):
|
||||
gettext("Identifier case"), 'radioModern', 'upper',
|
||||
options=[{'label': gettext('Upper case'), 'value': 'upper'},
|
||||
{'label': gettext('Lower case'), 'value': 'lower'},
|
||||
{'label': gettext('Capitalized'), 'value': 'capitalize'}],
|
||||
{'label': gettext('Preserve'), 'value': 'preserve'}],
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext(
|
||||
'Convert identifiers to upper, lower, or capitalized casing.'
|
||||
'Convert identifiers to upper, lower, or preserve casing.'
|
||||
)
|
||||
)
|
||||
|
||||
self.strip_comments = self.preference.register(
|
||||
'editor', 'strip_comments',
|
||||
gettext("Strip comments?"), 'boolean', False,
|
||||
self.function_case = self.preference.register(
|
||||
'editor', 'function_case',
|
||||
gettext("Function case"), 'radioModern', 'upper',
|
||||
options=[{'label': gettext('Upper case'), 'value': 'upper'},
|
||||
{'label': gettext('Lower case'), 'value': 'lower'},
|
||||
{'label': gettext('Preserve'), 'value': 'preserve'}],
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext('If set to True, comments will be removed.')
|
||||
help_str=gettext(
|
||||
'Convert function names to upper, lower, or preserve casing.'
|
||||
)
|
||||
)
|
||||
|
||||
self.reindent = self.preference.register(
|
||||
'editor', 'reindent',
|
||||
gettext("Re-indent?"), 'boolean', True,
|
||||
self.data_type_case = self.preference.register(
|
||||
'editor', 'data_type_case',
|
||||
gettext("Data type case"), 'radioModern', 'upper',
|
||||
options=[{'label': gettext('Upper case'), 'value': 'upper'},
|
||||
{'label': gettext('Lower case'), 'value': 'lower'},
|
||||
{'label': gettext('Preserve'), 'value': 'preserve'}],
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext('If set to True, the indentations of the '
|
||||
'statements are changed.')
|
||||
)
|
||||
|
||||
self.reindent_aligned = self.preference.register(
|
||||
'editor', 'reindent_aligned',
|
||||
gettext("Re-indent aligned?"), 'boolean', False,
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext('If set to True, the indentations of the '
|
||||
'statements are changed, and statements are '
|
||||
'aligned by keywords.')
|
||||
help_str=gettext(
|
||||
'Convert data types to upper, lower, or preserve casing.'
|
||||
)
|
||||
)
|
||||
|
||||
self.spaces_around_operators = self.preference.register(
|
||||
@@ -786,23 +786,6 @@ def register_query_tool_preferences(self):
|
||||
'operators.')
|
||||
)
|
||||
|
||||
self.comma_first = self.preference.register(
|
||||
'editor', 'comma_first',
|
||||
gettext("Comma-first notation?"), 'boolean', False,
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext('If set to True, comma-first notation for column '
|
||||
'names is used.')
|
||||
)
|
||||
|
||||
self.wrap_after = self.preference.register(
|
||||
'editor', 'wrap_after',
|
||||
gettext("Wrap after N characters"), 'integer', 4,
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext("The column limit (in characters) for wrapping "
|
||||
"comma-separated lists. If zero, it puts "
|
||||
"every item in the list on its own line.")
|
||||
)
|
||||
|
||||
self.tab_size = self.preference.register(
|
||||
'editor', 'tab_size',
|
||||
gettext("Tab size"), 'integer', 4,
|
||||
@@ -824,6 +807,49 @@ def register_query_tool_preferences(self):
|
||||
)
|
||||
)
|
||||
|
||||
self.expression_width = self.preference.register(
|
||||
'editor', 'expression_width',
|
||||
gettext("Expression Width"), 'integer', 50,
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext(
|
||||
'maximum number of characters in parenthesized expressions to be '
|
||||
'kept on single line.'
|
||||
)
|
||||
)
|
||||
|
||||
self.logical_operator_new_line = self.preference.register(
|
||||
'editor', 'logical_operator_new_line',
|
||||
gettext("Logical operator new line"), 'radioModern', 'before',
|
||||
options=[{'label': gettext('Before'), 'value': 'before'},
|
||||
{'label': gettext('After'), 'value': 'after'}],
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext(
|
||||
'Decides newline placement before or after logical operators '
|
||||
'(AND, OR, XOR).'
|
||||
)
|
||||
)
|
||||
|
||||
self.lines_between_queries = self.preference.register(
|
||||
'editor', 'lines_between_queries',
|
||||
gettext("Lines between queries"), 'integer', 1,
|
||||
min_val=0,
|
||||
max_val=5,
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext(
|
||||
'Decides how many empty lines to leave between SQL statements. '
|
||||
'If zero it puts no new line.'
|
||||
)
|
||||
)
|
||||
|
||||
self.new_line_before_semicolon = self.preference.register(
|
||||
'editor', 'new_line_before_semicolon',
|
||||
gettext("New line before semicolon?"), 'boolean', False,
|
||||
category_label=PREF_LABEL_SQL_FORMATTING,
|
||||
help_str=gettext(
|
||||
'Whether to place query separator (;) on a separate line.'
|
||||
)
|
||||
)
|
||||
|
||||
self.row_limit = self.preference.register(
|
||||
'graph_visualiser', 'row_limit',
|
||||
gettext("Row Limit"), 'integer',
|
||||
|
||||
Reference in New Issue
Block a user