Class: SqlBeautifier::Query
- Inherits:
-
Object
- Object
- SqlBeautifier::Query
- Defined in:
- lib/sql_beautifier/query.rb
Constant Summary collapse
- COMPACT_CLAUSE_KEYS =
i[ select from where order_by limit ].freeze
- LEADING_WHITESPACE_PATTERN =
%r{\A[[:space:]]*}- CLAUSE_KEYWORD_PREFIX_PATTERN =
%r{\A(?:where|from|using)(?:[[:space:]]|$)}i
Instance Attribute Summary collapse
-
#clauses ⇒ Object
readonly
Returns the value of attribute clauses.
-
#depth ⇒ Object
readonly
Returns the value of attribute depth.
-
#table_registry ⇒ Object
readonly
Returns the value of attribute table_registry.
Class Method Summary collapse
- .find_top_level_subquery(text, start_position) ⇒ Object
- .format_as_subquery(inner_sql, base_indent:) ⇒ Object
- .format_subqueries_in_text(text, depth:) ⇒ Object
- .parse(normalized_sql, depth: 0) ⇒ Object
- .select_follows?(text, position) ⇒ Boolean
- .subquery_base_indent_for(text, subquery_position, default_base_indent) ⇒ Object
Instance Method Summary collapse
- #compact? ⇒ Boolean
-
#initialize(clauses:, depth: 0) ⇒ Query
constructor
A new instance of Query.
- #render ⇒ Object
Constructor Details
#initialize(clauses:, depth: 0) ⇒ Query
Returns a new instance of Query.
115 116 117 118 119 |
# File 'lib/sql_beautifier/query.rb', line 115 def initialize(clauses:, depth: 0) @clauses = clauses @depth = depth @table_registry = TableRegistry.new(@clauses[:from]) if @clauses[:from].present? end |
Instance Attribute Details
#clauses ⇒ Object (readonly)
Returns the value of attribute clauses.
16 17 18 |
# File 'lib/sql_beautifier/query.rb', line 16 def clauses @clauses end |
#depth ⇒ Object (readonly)
Returns the value of attribute depth.
17 18 19 |
# File 'lib/sql_beautifier/query.rb', line 17 def depth @depth end |
#table_registry ⇒ Object (readonly)
Returns the value of attribute table_registry.
18 19 20 |
# File 'lib/sql_beautifier/query.rb', line 18 def table_registry @table_registry end |
Class Method Details
.find_top_level_subquery(text, start_position) ⇒ Object
70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/sql_beautifier/query.rb', line 70 def self.find_top_level_subquery(text, start_position) scanner = Scanner.new(text, position: start_position) until scanner.finished? consumed = scanner.scan_quoted_or_sentinel! next if consumed return scanner.position if scanner.current_char == Constants::OPEN_PARENTHESIS && select_follows?(text, scanner.position) scanner.advance! end nil end |
.format_as_subquery(inner_sql, base_indent:) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/sql_beautifier/query.rb', line 26 def self.format_as_subquery(inner_sql, base_indent:) indent_spaces = SqlBeautifier.config_for(:indent_spaces) || 4 subquery_indent = base_indent + indent_spaces formatted = Formatter.new(inner_sql, depth: subquery_indent).call return "(#{inner_sql})" unless formatted indentation = Util.whitespace(subquery_indent) indented_lines = formatted.chomp.lines.map do |line| line.strip.empty? ? "\n" : "#{indentation}#{line}" end.join "(\n#{indented_lines}\n#{Util.whitespace(base_indent)})" end |
.format_subqueries_in_text(text, depth:) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/sql_beautifier/query.rb', line 40 def self.format_subqueries_in_text(text, depth:) output = +"" position = 0 while position < text.length subquery_position = find_top_level_subquery(text, position) unless subquery_position output << text[position..] break end output << text[position...subquery_position] closing_position = Scanner.new(text).find_matching_parenthesis(subquery_position) unless closing_position output << text[subquery_position..] break end inner_sql = text[(subquery_position + 1)...closing_position].strip base_indent = subquery_base_indent_for(text, subquery_position, depth) output << format_as_subquery(inner_sql, base_indent: base_indent) position = closing_position + 1 end output end |
.parse(normalized_sql, depth: 0) ⇒ Object
20 21 22 23 24 |
# File 'lib/sql_beautifier/query.rb', line 20 def self.parse(normalized_sql, depth: 0) clauses = Tokenizer.split_into_clauses(normalized_sql) new(clauses: clauses, depth: depth) end |
.select_follows?(text, position) ⇒ Boolean
108 109 110 111 112 113 |
# File 'lib/sql_beautifier/query.rb', line 108 def self.select_follows?(text, position) remaining_text = text[(position + 1)..] return false unless remaining_text remaining_text.match?(%r{\A[[:space:]]*select(?:[[:space:]]|\()}i) end |
.subquery_base_indent_for(text, subquery_position, default_base_indent) ⇒ Object
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/sql_beautifier/query.rb', line 85 def self.subquery_base_indent_for(text, subquery_position, default_base_indent) line_start_position = text.rindex("\n", subquery_position - 1) line_start_position = line_start_position ? line_start_position + 1 : 0 line_before_subquery = text[line_start_position...subquery_position] line_leading_spaces = line_before_subquery[LEADING_WHITESPACE_PATTERN].to_s.length keyword_column_width = SqlBeautifier.config_for(:keyword_column_width) paren_position_on_line = subquery_position - line_start_position clause_body_position = line_leading_spaces + keyword_column_width if line_before_subquery.lstrip.match?(CLAUSE_KEYWORD_PREFIX_PATTERN) if paren_position_on_line == clause_body_position default_base_indent.zero? ? clause_body_position : line_leading_spaces else clause_body_position end elsif paren_position_on_line > line_leading_spaces line_leading_spaces else default_base_indent end end |
Instance Method Details
#compact? ⇒ Boolean
140 141 142 |
# File 'lib/sql_beautifier/query.rb', line 140 def compact? compact_clause_set? && single_select_column? && single_from_table? && one_or_fewer_conditions? end |
#render ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/sql_beautifier/query.rb', line 121 def render parts = [] append_clause!(parts, :select, Clauses::Select) append_from_clause!(parts) append_clause!(parts, :where, Clauses::Where) append_clause!(parts, :group_by, Clauses::GroupBy) append_clause!(parts, :having, Clauses::Having) append_clause!(parts, :order_by, Clauses::OrderBy) append_clause!(parts, :limit, Clauses::Limit) output = parts.join(clause_separator) return nil if output.empty? output = self.class.format_subqueries_in_text(output, depth: @depth) output = @table_registry.apply_aliases(output) if @table_registry "#{output}\n" end |