Module: SqlBeautifier::Tokenizer
- Defined in:
- lib/sql_beautifier/tokenizer.rb
Class Method Summary collapse
- .find_all_top_level_join_positions(text) ⇒ Object
- .find_earliest_top_level_join_keyword(text, search_offset) ⇒ Object
- .find_top_level_keyword(sql, keyword) ⇒ Object
- .first_clause_position(sql) ⇒ Object
- .outer_parentheses_wrap_all?(text) ⇒ Boolean
- .scan_top_level_conjunctions(text) ⇒ Object
- .split_by_top_level_commas(text) ⇒ Object
- .split_into_clauses(sql) ⇒ Object
- .split_top_level_conditions(text) ⇒ Object
- .top_level?(sql, target_position) ⇒ Boolean
Class Method Details
.find_all_top_level_join_positions(text) ⇒ Object
176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/sql_beautifier/tokenizer.rb', line 176 def find_all_top_level_join_positions(text) positions = [] search_offset = 0 while search_offset < text.length earliest_match = find_earliest_top_level_join_keyword(text, search_offset) break unless earliest_match positions << earliest_match search_offset = earliest_match[:position] + earliest_match[:keyword].length end positions end |
.find_earliest_top_level_join_keyword(text, search_offset) ⇒ Object
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/sql_beautifier/tokenizer.rb', line 191 def find_earliest_top_level_join_keyword(text, search_offset) earliest_match = nil Constants::JOIN_KEYWORDS_BY_LENGTH.each do |keyword| remaining_text = text[search_offset..] keyword_position = find_top_level_keyword(remaining_text, keyword) next unless keyword_position absolute_position = search_offset + keyword_position next if earliest_match && absolute_position >= earliest_match[:position] earliest_match = { position: absolute_position, keyword: keyword, } end earliest_match end |
.find_top_level_keyword(sql, keyword) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/sql_beautifier/tokenizer.rb', line 7 def find_top_level_keyword(sql, keyword) keyword_pattern = %r{#{Regexp.escape(keyword)}}i search_position = 0 scanner = Scanner.new(sql) while search_position < sql.length match = sql.match(keyword_pattern, search_position) return nil unless match match_position = match.begin(0) if scanner.inside_sentinel?(match_position) search_position = scanner.sentinel_end_position(match_position) || (match_position + 1) next end previous_character = scanner.character_before(match_position) next_character = scanner.character_after(match_position, keyword.length) return match_position if scanner.word_boundary?(previous_character) && scanner.word_boundary?(next_character) && top_level?(sql, match_position) search_position = match_position + 1 end nil end |
.first_clause_position(sql) ⇒ Object
34 35 36 |
# File 'lib/sql_beautifier/tokenizer.rb', line 34 def first_clause_position(sql) Constants::CLAUSE_KEYWORDS.filter_map { |keyword| find_top_level_keyword(sql, keyword) }.min end |
.outer_parentheses_wrap_all?(text) ⇒ Boolean
142 143 144 145 146 147 148 149 |
# File 'lib/sql_beautifier/tokenizer.rb', line 142 def outer_parentheses_wrap_all?(text) trimmed_text = text.strip return false unless trimmed_text.start_with?(Constants::OPEN_PARENTHESIS) closing_parenthesis_position = Scanner.new(trimmed_text).find_matching_parenthesis(0) closing_parenthesis_position == trimmed_text.length - 1 end |
.scan_top_level_conjunctions(text) ⇒ Object
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/sql_beautifier/tokenizer.rb', line 211 def scan_top_level_conjunctions(text) scanner = Scanner.new(text) conjunction_boundaries = [] inside_between = false case_depth = 0 until scanner.finished? next if scanner.skip_quoted_or_sentinel! case scanner.current_char when Constants::SINGLE_QUOTE scanner.enter_single_quote! when Constants::DOUBLE_QUOTE scanner.enter_double_quote! when Constants::OPEN_PARENTHESIS scanner.increment_depth! scanner.advance! when Constants::CLOSE_PARENTHESIS scanner.decrement_depth! scanner.advance! else if scanner.parenthesis_depth.zero? if scanner.keyword_at?("case") case_depth += 1 scanner.advance!("case".length) next end if scanner.keyword_at?("end") && case_depth.positive? case_depth -= 1 scanner.advance!("end".length) next end if case_depth.zero? inside_between = true if scanner.keyword_at?(Constants::BETWEEN_KEYWORD) matched_conjunction = scanner.detect_conjunction_at if matched_conjunction if matched_conjunction == "and" && inside_between inside_between = false else conjunction_boundaries << { conjunction: matched_conjunction, position: scanner.position, } end scanner.advance!(matched_conjunction.length) next end end end scanner.advance! end end conjunction_boundaries end |
.split_by_top_level_commas(text) ⇒ Object
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/sql_beautifier/tokenizer.rb', line 72 def split_by_top_level_commas(text) scanner = Scanner.new(text) segments = [] current_segment = +"" until scanner.finished? consumed = scanner.scan_quoted_or_sentinel! if consumed current_segment << consumed next end character = scanner.current_char case character when Constants::OPEN_PARENTHESIS scanner.increment_depth! current_segment << character when Constants::CLOSE_PARENTHESIS scanner.decrement_depth! current_segment << character when Constants::COMMA if scanner.parenthesis_depth.zero? segments << current_segment.strip current_segment = +"" else current_segment << character end else current_segment << character end scanner.advance! end segments << current_segment.strip unless current_segment.strip.empty? segments end |
.split_into_clauses(sql) ⇒ Object
38 39 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 69 70 |
# File 'lib/sql_beautifier/tokenizer.rb', line 38 def split_into_clauses(sql) boundaries = Constants::CLAUSE_KEYWORDS.filter_map do |keyword| keyword_position = find_top_level_keyword(sql, keyword) next unless keyword_position { keyword: keyword, position: keyword_position, } end boundaries.sort_by! { |boundary| boundary[:position] } clauses = {} boundaries.each_with_index do |boundary, boundary_index| content_start = boundary[:position] + boundary[:keyword].length content_end = begin if boundary_index + 1 < boundaries.length boundaries[boundary_index + 1][:position] else sql.length end end clause_symbol = boundary[:keyword].tr(" ", "_").to_sym clauses[clause_symbol] = sql[content_start...content_end].strip end clauses end |
.split_top_level_conditions(text) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/sql_beautifier/tokenizer.rb', line 114 def split_top_level_conditions(text) conjunction_boundaries = scan_top_level_conjunctions(text) return [[nil, text.strip]] if conjunction_boundaries.empty? condition_pairs = [] first_condition_text = text[0...conjunction_boundaries.first[:position]].strip condition_pairs << [nil, first_condition_text] conjunction_boundaries.each_with_index do |boundary, boundary_index| content_start = boundary[:position] + boundary[:conjunction].length content_end = begin if boundary_index + 1 < conjunction_boundaries.length conjunction_boundaries[boundary_index + 1][:position] else text.length end end condition_text = text[content_start...content_end].strip condition_pairs << [boundary[:conjunction], condition_text] end condition_pairs end |
.top_level?(sql, target_position) ⇒ Boolean
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/sql_beautifier/tokenizer.rb', line 151 def top_level?(sql, target_position) scanner = Scanner.new(sql) while scanner.position < target_position next if scanner.skip_quoted_or_sentinel! case scanner.current_char when Constants::SINGLE_QUOTE scanner.enter_single_quote! when Constants::DOUBLE_QUOTE scanner.enter_double_quote! when Constants::OPEN_PARENTHESIS scanner.increment_depth! scanner.advance! when Constants::CLOSE_PARENTHESIS scanner.decrement_depth! scanner.advance! else scanner.advance! end end scanner.top_level? end |