Archived
212 lines
7.3 KiB
Python
212 lines
7.3 KiB
Python
#
|
|
# Copyright (C) 2009-2020 the sqlparse authors and contributors
|
|
# <see AUTHORS file>
|
|
#
|
|
# This module is part of python-sqlparse and is released under
|
|
# the BSD License: https://opensource.org/licenses/BSD-3-Clause
|
|
|
|
"""SQL formatter"""
|
|
|
|
from sqlparse import filters
|
|
from sqlparse.exceptions import SQLParseError
|
|
|
|
|
|
def validate_options(options):
|
|
"""Validates options."""
|
|
kwcase = options.get("keyword_case")
|
|
if kwcase not in [None, "upper", "lower", "capitalize"]:
|
|
raise SQLParseError("Invalid value for keyword_case: " "{!r}".format(kwcase))
|
|
|
|
idcase = options.get("identifier_case")
|
|
if idcase not in [None, "upper", "lower", "capitalize"]:
|
|
raise SQLParseError("Invalid value for identifier_case: " "{!r}".format(idcase))
|
|
|
|
ofrmt = options.get("output_format")
|
|
if ofrmt not in [None, "sql", "python", "php"]:
|
|
raise SQLParseError("Unknown output format: " "{!r}".format(ofrmt))
|
|
|
|
strip_comments = options.get("strip_comments", False)
|
|
if strip_comments not in [True, False]:
|
|
raise SQLParseError(
|
|
"Invalid value for strip_comments: " "{!r}".format(strip_comments)
|
|
)
|
|
|
|
space_around_operators = options.get("use_space_around_operators", False)
|
|
if space_around_operators not in [True, False]:
|
|
raise SQLParseError(
|
|
"Invalid value for use_space_around_operators: "
|
|
"{!r}".format(space_around_operators)
|
|
)
|
|
|
|
strip_ws = options.get("strip_whitespace", False)
|
|
if strip_ws not in [True, False]:
|
|
raise SQLParseError(
|
|
"Invalid value for strip_whitespace: " "{!r}".format(strip_ws)
|
|
)
|
|
|
|
truncate_strings = options.get("truncate_strings")
|
|
if truncate_strings is not None:
|
|
try:
|
|
truncate_strings = int(truncate_strings)
|
|
except (ValueError, TypeError):
|
|
raise SQLParseError(
|
|
"Invalid value for truncate_strings: " "{!r}".format(truncate_strings)
|
|
)
|
|
if truncate_strings <= 1:
|
|
raise SQLParseError(
|
|
"Invalid value for truncate_strings: " "{!r}".format(truncate_strings)
|
|
)
|
|
options["truncate_strings"] = truncate_strings
|
|
options["truncate_char"] = options.get("truncate_char", "[...]")
|
|
|
|
indent_columns = options.get("indent_columns", False)
|
|
if indent_columns not in [True, False]:
|
|
raise SQLParseError(
|
|
"Invalid value for indent_columns: " "{!r}".format(indent_columns)
|
|
)
|
|
elif indent_columns:
|
|
options["reindent"] = True # enforce reindent
|
|
options["indent_columns"] = indent_columns
|
|
|
|
reindent = options.get("reindent", False)
|
|
if reindent not in [True, False]:
|
|
raise SQLParseError("Invalid value for reindent: " "{!r}".format(reindent))
|
|
elif reindent:
|
|
options["strip_whitespace"] = True
|
|
|
|
reindent_aligned = options.get("reindent_aligned", False)
|
|
if reindent_aligned not in [True, False]:
|
|
raise SQLParseError(
|
|
"Invalid value for reindent_aligned: " "{!r}".format(reindent)
|
|
)
|
|
elif reindent_aligned:
|
|
options["strip_whitespace"] = True
|
|
|
|
indent_after_first = options.get("indent_after_first", False)
|
|
if indent_after_first not in [True, False]:
|
|
raise SQLParseError(
|
|
"Invalid value for indent_after_first: " "{!r}".format(indent_after_first)
|
|
)
|
|
options["indent_after_first"] = indent_after_first
|
|
|
|
indent_tabs = options.get("indent_tabs", False)
|
|
if indent_tabs not in [True, False]:
|
|
raise SQLParseError(
|
|
"Invalid value for indent_tabs: " "{!r}".format(indent_tabs)
|
|
)
|
|
elif indent_tabs:
|
|
options["indent_char"] = "\t"
|
|
else:
|
|
options["indent_char"] = " "
|
|
|
|
indent_width = options.get("indent_width", 2)
|
|
try:
|
|
indent_width = int(indent_width)
|
|
except (TypeError, ValueError):
|
|
raise SQLParseError("indent_width requires an integer")
|
|
if indent_width < 1:
|
|
raise SQLParseError("indent_width requires a positive integer")
|
|
options["indent_width"] = indent_width
|
|
|
|
wrap_after = options.get("wrap_after", 0)
|
|
try:
|
|
wrap_after = int(wrap_after)
|
|
except (TypeError, ValueError):
|
|
raise SQLParseError("wrap_after requires an integer")
|
|
if wrap_after < 0:
|
|
raise SQLParseError("wrap_after requires a positive integer")
|
|
options["wrap_after"] = wrap_after
|
|
|
|
comma_first = options.get("comma_first", False)
|
|
if comma_first not in [True, False]:
|
|
raise SQLParseError("comma_first requires a boolean value")
|
|
options["comma_first"] = comma_first
|
|
|
|
right_margin = options.get("right_margin")
|
|
if right_margin is not None:
|
|
try:
|
|
right_margin = int(right_margin)
|
|
except (TypeError, ValueError):
|
|
raise SQLParseError("right_margin requires an integer")
|
|
if right_margin < 10:
|
|
raise SQLParseError("right_margin requires an integer > 10")
|
|
options["right_margin"] = right_margin
|
|
|
|
return options
|
|
|
|
|
|
def build_filter_stack(stack, options):
|
|
"""Setup and return a filter stack.
|
|
|
|
Args:
|
|
stack: :class:`~sqlparse.filters.FilterStack` instance
|
|
options: Dictionary with options validated by validate_options.
|
|
"""
|
|
# Token filter
|
|
if options.get("keyword_case"):
|
|
stack.preprocess.append(filters.KeywordCaseFilter(options["keyword_case"]))
|
|
|
|
if options.get("identifier_case"):
|
|
stack.preprocess.append(
|
|
filters.IdentifierCaseFilter(options["identifier_case"])
|
|
)
|
|
|
|
if options.get("truncate_strings"):
|
|
stack.preprocess.append(
|
|
filters.TruncateStringFilter(
|
|
width=options["truncate_strings"], char=options["truncate_char"]
|
|
)
|
|
)
|
|
|
|
if options.get("use_space_around_operators", False):
|
|
stack.enable_grouping()
|
|
stack.stmtprocess.append(filters.SpacesAroundOperatorsFilter())
|
|
|
|
# After grouping
|
|
if options.get("strip_comments"):
|
|
stack.enable_grouping()
|
|
stack.stmtprocess.append(filters.StripCommentsFilter())
|
|
|
|
if options.get("strip_whitespace") or options.get("reindent"):
|
|
stack.enable_grouping()
|
|
stack.stmtprocess.append(filters.StripWhitespaceFilter())
|
|
|
|
if options.get("reindent"):
|
|
stack.enable_grouping()
|
|
stack.stmtprocess.append(
|
|
filters.ReindentFilter(
|
|
char=options["indent_char"],
|
|
width=options["indent_width"],
|
|
indent_after_first=options["indent_after_first"],
|
|
indent_columns=options["indent_columns"],
|
|
wrap_after=options["wrap_after"],
|
|
comma_first=options["comma_first"],
|
|
)
|
|
)
|
|
|
|
if options.get("reindent_aligned", False):
|
|
stack.enable_grouping()
|
|
stack.stmtprocess.append(
|
|
filters.AlignedIndentFilter(char=options["indent_char"])
|
|
)
|
|
|
|
if options.get("right_margin"):
|
|
stack.enable_grouping()
|
|
stack.stmtprocess.append(
|
|
filters.RightMarginFilter(width=options["right_margin"])
|
|
)
|
|
|
|
# Serializer
|
|
if options.get("output_format"):
|
|
frmt = options["output_format"]
|
|
if frmt.lower() == "php":
|
|
fltr = filters.OutputPHPFilter()
|
|
elif frmt.lower() == "python":
|
|
fltr = filters.OutputPythonFilter()
|
|
else:
|
|
fltr = None
|
|
if fltr is not None:
|
|
stack.postprocess.append(fltr)
|
|
|
|
return stack
|