1 2 3 4 5 6 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 33 34 35 36 37 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 71 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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
# frozen-string-literal: true
module Sequel
# The +ASTTransformer+ class is designed to handle the abstract syntax trees
# that Sequel uses internally and produce modified copies of them. By itself
# it only produces a straight copy. It's designed to be subclassed and have
# subclasses returned modified copies of the specific nodes that need to
# be modified.
class ASTTransformer
# Return +obj+ or a potentially transformed version of it.
def transform(obj)
v(obj)
end
private
# Recursive version that handles all of Sequel's internal object types
# and produces copies of them.
def v(o)
case o
when Symbol, Numeric, String, Class, TrueClass, FalseClass, NilClass
o
when Array
o.map{|x| v(x)}
when Hash
h = {}
o.each{|k, val| h[v(k)] = v(val)}
h
when SQL::NumericExpression
if o.op == :extract
o.class.new(o.op, o.args[0], v(o.args[1]))
else
o.class.new(o.op, *v(o.args))
end
when SQL::ComplexExpression
o.class.new(o.op, *v(o.args))
when SQL::Identifier
SQL::Identifier.new(v(o.value))
when SQL::QualifiedIdentifier
SQL::QualifiedIdentifier.new(v(o.table), v(o.column))
when SQL::OrderedExpression
SQL::OrderedExpression.new(v(o.expression), o.descending, :nulls=>o.nulls)
when SQL::AliasedExpression
SQL::AliasedExpression.new(v(o.expression), o.alias, o.columns)
when SQL::CaseExpression
args = [v(o.conditions), v(o.default)]
args << v(o.expression) if o.expression?
SQL::CaseExpression.new(*args)
when SQL::Cast
SQL::Cast.new(v(o.expr), o.type)
when SQL::Function
h = {}
o.opts.each do |k, val|
h[k] = v(val)
end
SQL::Function.new!(o.name, v(o.args), h)
when SQL::Subscript
SQL::Subscript.new(v(o.expression), v(o.sub))
when SQL::Window
opts = o.opts.dup
opts[:partition] = v(opts[:partition]) if opts[:partition]
opts[:order] = v(opts[:order]) if opts[:order]
SQL::Window.new(opts)
when SQL::PlaceholderLiteralString
args = if o.args.is_a?(Hash)
h = {}
o.args.each{|k,val| h[k] = v(val)}
h
else
v(o.args)
end
SQL::PlaceholderLiteralString.new(o.str, args, o.parens)
when SQL::JoinOnClause
SQL::JoinOnClause.new(v(o.on), o.join_type, v(o.table_expr))
when SQL::JoinUsingClause
SQL::JoinUsingClause.new(v(o.using), o.join_type, v(o.table_expr))
when SQL::JoinClause
SQL::JoinClause.new(o.join_type, v(o.table_expr))
when SQL::DelayedEvaluation
SQL::DelayedEvaluation.new(lambda{|ds| v(o.call(ds))})
when SQL::Wrapper
SQL::Wrapper.new(v(o.value))
when SQL::Expression
if o.respond_to?(:sequel_ast_transform)
o.sequel_ast_transform(method(:v))
else
o
end
else
o
end
end
end
# Handles qualifying existing datasets, so that unqualified columns
# in the dataset are qualified with a given table name.
class Qualifier < ASTTransformer
# Set the table used to qualify unqualified columns
def initialize(table)
@table = table
end
private
# Turn <tt>SQL::Identifier</tt>s and symbols that aren't implicitly
# qualified into <tt>SQL::QualifiedIdentifier</tt>s. For symbols that
# are not implicitly qualified by are implicitly aliased, return an
# <tt>SQL::AliasedExpression</tt>s with a qualified version of the symbol.
def v(o)
case o
when Symbol
t, column, aliaz = Sequel.split_symbol(o)
if t
o
elsif aliaz
SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(@table, SQL::Identifier.new(column)), aliaz)
else
SQL::QualifiedIdentifier.new(@table, o)
end
when SQL::Identifier
SQL::QualifiedIdentifier.new(@table, o)
when SQL::QualifiedIdentifier, SQL::JoinClause
# Return these directly, so we don't accidentally qualify symbols in them.
o
else
super
end
end
end
end
|