mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-01-07 22:53:45 -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:
parent
459121566c
commit
f7045b58d4
Binary file not shown.
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 190 KiB |
@ -486,26 +486,27 @@ Query Tool window navigation:
|
||||
Use the fields on the *SQL formatting* panel to specify your preferences for
|
||||
reformatting of SQL.
|
||||
|
||||
* Use the *Comma-first notation* option to specify whether to place commas
|
||||
before or after column names.
|
||||
* Use the *Data type case* option to specify whether to change data types
|
||||
into upper, lower, or preserve case.
|
||||
* Use the *Expression width* option to specify maximum number of characters
|
||||
in parenthesized expressions to be kept on single line.
|
||||
* Use the *Function case* option to specify whether to change function
|
||||
names into upper, lower, or preserve case.
|
||||
* Use the *Identifier case* option to specify whether to change identifiers
|
||||
(object names) into upper, lower, or capitalized case.
|
||||
* Use the *Keyword case* option to specify whether to change keywords into
|
||||
upper, lower, or capitalized case.
|
||||
* Use the *Re-indent aligned?* option to specify that indentations of statements
|
||||
should be changed, aligned by keywords.
|
||||
* Use the *Re-indent?* option to specify that indentations of statements should
|
||||
be changed.
|
||||
upper, lower, or preserve case.
|
||||
* Use *Lines between queries* to specify how many empty lines to leave
|
||||
between SQL statements. If set to zero it puts no new line.
|
||||
* Use *Logical operator new line* to specify newline placement before or
|
||||
after logical operators (AND, OR, XOR).
|
||||
* Use *New line before semicolon?* to specify whether to place query
|
||||
separator (;) on a separate line.
|
||||
* Use the *Spaces around operators?* option to specify whether or not to include
|
||||
spaces on either side of operators.
|
||||
* Use the *Strip comments?* option to specify whether or not comments should be
|
||||
removed.
|
||||
* Use the *Tab size* option to specify the number of spaces per tab or indent.
|
||||
* Use the *Use spaces?* option to select whether to use spaces or tabs when
|
||||
indenting.
|
||||
* Use the *Wrap after N characters* option to specify the column limit for
|
||||
wrapping column separated lists (e.g. of column names in a table). If set to
|
||||
0 (zero), each item will be on it's own line.
|
||||
|
||||
The Schema Diff Node
|
||||
********************
|
||||
|
@ -152,6 +152,7 @@
|
||||
"snapsvg-cjs": "^0.0.6",
|
||||
"socket.io-client": "^4.5.0",
|
||||
"split.js": "^1.5.10",
|
||||
"sql-formatter": "^15.1.2",
|
||||
"styled-components": "^5.2.1",
|
||||
"uplot": "^1.6.24",
|
||||
"uplot-react": "^1.1.4",
|
||||
|
@ -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',
|
||||
|
@ -6237,7 +6237,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:^2.20.0, commander@npm:^2.8.1":
|
||||
"commander@npm:^2.19.0, commander@npm:^2.20.0, commander@npm:^2.8.1":
|
||||
version: 2.20.3
|
||||
resolution: "commander@npm:2.20.3"
|
||||
checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e
|
||||
@ -7260,6 +7260,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"discontinuous-range@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "discontinuous-range@npm:1.0.0"
|
||||
checksum: 8ee88d7082445b6eadc7c03bebe6dc978f96760c45e9f65d16ca66174d9e086a9e3855ee16acf65625e1a07a846a17de674f02a5964a6aebe5963662baf8b5c8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dnd-core@npm:14.0.1":
|
||||
version: 14.0.1
|
||||
resolution: "dnd-core@npm:14.0.1"
|
||||
@ -8714,6 +8721,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-stdin@npm:=8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "get-stdin@npm:8.0.0"
|
||||
checksum: 40128b6cd25781ddbd233344f1a1e4006d4284906191ed0a7d55ec2c1a3e44d650f280b2c9eeab79c03ac3037da80257476c0e4e5af38ddfb902d6ff06282d77
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-stream@npm:3.0.0, get-stream@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "get-stream@npm:3.0.0"
|
||||
@ -11877,6 +11891,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"moo@npm:^0.5.0":
|
||||
version: 0.5.2
|
||||
resolution: "moo@npm:0.5.2"
|
||||
checksum: 5a41ddf1059fd0feb674d917c4774e41c877f1ca980253be4d3aae1a37f4bc513f88815041243f36f5cf67a62fb39324f3f997cf7fb17b6cb00767c165e7c499
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mousetrap@npm:^1.6.3":
|
||||
version: 1.6.5
|
||||
resolution: "mousetrap@npm:1.6.5"
|
||||
@ -11956,6 +11977,23 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nearley@npm:^2.20.1":
|
||||
version: 2.20.1
|
||||
resolution: "nearley@npm:2.20.1"
|
||||
dependencies:
|
||||
commander: ^2.19.0
|
||||
moo: ^0.5.0
|
||||
railroad-diagrams: ^1.0.0
|
||||
randexp: 0.4.6
|
||||
bin:
|
||||
nearley-railroad: bin/nearley-railroad.js
|
||||
nearley-test: bin/nearley-test.js
|
||||
nearley-unparse: bin/nearley-unparse.js
|
||||
nearleyc: bin/nearleyc.js
|
||||
checksum: 42c2c330c13c7991b48221c5df00f4352c2f8851636ae4d1f8ca3c8e193fc1b7668c78011d1cad88cca4c1c4dc087425420629c19cc286d7598ec15533aaef26
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"neatequal@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "neatequal@npm:1.0.0"
|
||||
@ -13709,6 +13747,23 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"railroad-diagrams@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "railroad-diagrams@npm:1.0.0"
|
||||
checksum: 9e312af352b5ed89c2118edc0c06cef2cc039681817f65266719606e4e91ff6ae5374c707cc9033fe29a82c2703edf3c63471664f97f0167c85daf6f93496319
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"randexp@npm:0.4.6":
|
||||
version: 0.4.6
|
||||
resolution: "randexp@npm:0.4.6"
|
||||
dependencies:
|
||||
discontinuous-range: 1.0.0
|
||||
ret: ~0.1.10
|
||||
checksum: 3c0d440a3f89d6d36844aa4dd57b5cdb0cab938a41956a16da743d3a3578ab32538fc41c16cc0984b6938f2ae4cbc0216967e9829e52191f70e32690d8e3445d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"randombytes@npm:^2.0.0, randombytes@npm:^2.0.1, randombytes@npm:^2.0.5, randombytes@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "randombytes@npm:2.1.0"
|
||||
@ -14676,6 +14731,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ret@npm:~0.1.10":
|
||||
version: 0.1.15
|
||||
resolution: "ret@npm:0.1.15"
|
||||
checksum: d76a9159eb8c946586567bd934358dfc08a36367b3257f7a3d7255fdd7b56597235af23c6afa0d7f0254159e8051f93c918809962ebd6df24ca2a83dbe4d4151
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"retry@npm:^0.12.0":
|
||||
version: 0.12.0
|
||||
resolution: "retry@npm:0.12.0"
|
||||
@ -14868,6 +14930,7 @@ __metadata:
|
||||
snapsvg-cjs: ^0.0.6
|
||||
socket.io-client: ^4.5.0
|
||||
split.js: ^1.5.10
|
||||
sql-formatter: ^15.1.2
|
||||
style-loader: ^3.3.2
|
||||
styled-components: ^5.2.1
|
||||
stylis: ^4.0.7
|
||||
@ -15508,6 +15571,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sql-formatter@npm:^15.1.2":
|
||||
version: 15.1.2
|
||||
resolution: "sql-formatter@npm:15.1.2"
|
||||
dependencies:
|
||||
argparse: ^2.0.1
|
||||
get-stdin: =8.0.0
|
||||
nearley: ^2.20.1
|
||||
bin:
|
||||
sql-formatter: bin/sql-formatter-cli.cjs
|
||||
checksum: 77379dd209bd65bac89f53d28e8e409c32878f008ec37572dcee59231db306c690771cf3a318e02a84a920d8059f22bb1e30d197ea29c1abecbfcd96c3e672ff
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ssri@npm:^10.0.0":
|
||||
version: 10.0.4
|
||||
resolution: "ssri@npm:10.0.4"
|
||||
|
Loading…
Reference in New Issue
Block a user