Module: Fast::SQL
- Defined in:
- lib/fast/sql.rb,
lib/fast/sql/rewriter.rb
Overview
This module contains methods to parse SQL statements and rewrite them. It uses PGQuery to parse the SQL statements. It uses Parser to rewrite the SQL statements. It uses Fast::Source::Map to map the AST nodes to the SQL tokens.
Defined Under Namespace
Classes: Node, Rewriter, SourceBuffer
Class Method Summary collapse
-
.clean_structure(stmt) ⇒ Hash
Clean up the hash structure returned by PgQuery.
-
.parse(statement, buffer_name: "(sql)") ⇒ Object
Parses SQL statements Using PGQuery.
-
.parse_file(file) ⇒ Object
Fast::SQL::Node with the parsed content.
-
.replace(pattern, ast, &replacement) ⇒ Object
String with the content updated in case the pattern matches.
-
.replace_file(pattern, file, &replacement) ⇒ Object
Replace a SQL file with the given pattern.
- .rewriter_for(pattern, ast, &replacement) ⇒ Fast::SQL::Rewriter
-
.sql_tree_to_ast(obj, source_buffer: nil, source_map: nil) ⇒ Array
Transform a sql tree into an AST.
Class Method Details
.clean_structure(stmt) ⇒ Hash
Clean up the hash structure returned by PgQuery
129 130 131 132 133 134 135 136 137 138 |
# File 'lib/fast/sql.rb', line 129 def clean_structure(stmt) res_hash = stmt.map do |key, value| value = clean_structure(value) if value.is_a?(Hash) value = value.map(&Fast::SQL.method(:clean_structure)) if value.is_a?(Array) value = nil if [{}, [], "", :SETOP_NONE, :LIMIT_OPTION_DEFAULT, false].include?(value) key = key.to_s.tr('-','_').to_sym [key, value] end res_hash.to_h.compact end |
.parse(statement, buffer_name: "(sql)") ⇒ Object
Parses SQL statements Using PGQuery
111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/fast/sql.rb', line 111 def parse(statement, buffer_name: "(sql)") return [] if statement.nil? source_buffer = SQL::SourceBuffer.new(buffer_name, source: statement) tree = PgQuery.parse(statement).tree first, *, last = source_buffer.tokens stmts = tree.stmts.map do |stmt| from = stmt.stmt_location to = stmt.stmt_len.zero? ? last.end : from + stmt.stmt_len expression = Fast::Source.range(source_buffer, from, to) source_map = Fast::Source.map(expression) sql_tree_to_ast(clean_structure(stmt.stmt.to_h), source_buffer: source_buffer, source_map: source_map) end.flatten stmts.one? ? stmts.first : stmts end |
.parse_file(file) ⇒ Object
Returns Fast::SQL::Node with the parsed content.
21 22 23 |
# File 'lib/fast/sql/rewriter.rb', line 21 def parse_file(file) parse(IO.read(file), buffer_name: file) end |
.replace(pattern, ast, &replacement) ⇒ Object
Returns string with the content updated in case the pattern matches.
6 7 8 |
# File 'lib/fast/sql/rewriter.rb', line 6 def replace(pattern, ast, &replacement) rewriter_for(pattern, ast, &replacement).rewrite! end |
.replace_file(pattern, file, &replacement) ⇒ Object
Replace a SQL file with the given pattern. Use a replacement code block to change the content.
30 31 32 33 34 35 36 37 |
# File 'lib/fast/sql/rewriter.rb', line 30 def replace_file(pattern, file, &replacement) original = IO.read(file) ast = parse_file(file) content = replace(pattern, ast, &replacement) if content != original File.open(file, 'w+') { |f| f.print content } end end |
.rewriter_for(pattern, ast, &replacement) ⇒ Fast::SQL::Rewriter
12 13 14 15 16 17 18 |
# File 'lib/fast/sql/rewriter.rb', line 12 def rewriter_for(pattern, ast, &replacement) rewriter = Rewriter.new rewriter.ast = ast rewriter.search = pattern rewriter.replacement = replacement rewriter end |
.sql_tree_to_ast(obj, source_buffer: nil, source_map: nil) ⇒ Array
Transform a sql tree into an AST. Populates the location of the AST nodes with the source map.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/fast/sql.rb', line 144 def sql_tree_to_ast(obj, source_buffer: nil, source_map: nil) recursive = -> (e) { sql_tree_to_ast(e, source_buffer: source_buffer, source_map: source_map.dup) } case obj when Array obj.map(&recursive).flatten.compact when Hash if (start = obj.delete(:location)) if (token = source_buffer.tokens.find{|e|e.start == start}) expression = Fast::Source.range(source_buffer, token.start, token.end) source_map = Fast::Source.map(expression) end end obj.map do |key, value| children = [*recursive.call(value)] Node.new(key, children, location: source_map) end.compact else obj end end |