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 Parser::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.
- .sql_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
130 131 132 133 134 135 136 137 138 139 |
# File 'lib/fast/sql.rb', line 130 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
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/fast/sql.rb', line 110 def parse(statement, buffer_name: "(sql)") return [] if statement.nil? source_buffer = SQL::SourceBuffer.new(buffer_name, source: statement) tree = PgQuery.parse(statement).tree stmts = tree.stmts.map do |stmt| v = clean_structure(stmt.stmt.to_h) inner_stmt = statement[stmt.stmt_location, stmt.stmt_len] first, *, last = source_buffer.tokens from = stmt.stmt_location to = from.zero? ? last.end : from + stmt.stmt_len expression = Parser::Source::Range.new(source_buffer, from, to) source_map = Parser::Source::Map.new(expression) sql_tree_to_ast(v, 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) sql_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 |
.sql_rewriter_for(pattern, ast, &replacement) ⇒ Fast::SQL::Rewriter
12 13 14 15 16 17 18 |
# File 'lib/fast/sql/rewriter.rb', line 12 def sql_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.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/fast/sql.rb', line 145 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 = Parser::Source::Range.new(source_buffer, token.start, token.end) source_map = Parser::Source::Map.new(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 |