This file is indexed.

/usr/lib/ruby/vendor_ruby/sequel/ast_transformer.rb is in ruby-sequel 3.33.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
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::ComplexExpression
        SQL::ComplexExpression.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.aliaz)
      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
        SQL::Function.new(o.f, *v(o.args))
      when SQL::Subscript
        SQL::Subscript.new(v(o.f), v(o.sub))
      when SQL::WindowFunction
        SQL::WindowFunction.new(v(o.function), v(o.window))
      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), v(o.table_alias))
      when SQL::JoinUsingClause
        SQL::JoinUsingClause.new(v(o.using), o.join_type, v(o.table), v(o.table_alias))
      when SQL::JoinClause
        SQL::JoinClause.new(o.join_type, v(o.table), v(o.table_alias))
      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
    # Store the dataset to use as the basis for qualification, 
    # and the table used to qualify unqualified columns. 
    def initialize(ds, table)
      @ds = ds
      @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 = @ds.send(: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

  # +Unbinder+ is used to take a dataset filter and return a modified version
  # that unbinds already bound values and returns a dataset with bound value
  # placeholders and a hash of bind values.  You can then prepare the dataset
  # and use the bound variables to execute it with the same values.
  #
  # This class only does a limited form of unbinding where the variable names
  # and values can be associated unambiguously.  The only cases it handles
  # are <tt>SQL::ComplexExpression<tt> with an operator in +UNBIND_OPS+, a
  # first argument that's an instance of a member of +UNBIND_KEY_CLASSES+, and
  # a second argument that's an instance of a member of +UNBIND_VALUE_CLASSES+.
  #
  # So it can handle cases like:
  #
  #   DB.filter(:a=>1).exclude(:b=>2).where{c > 3}
  #
  # But it cannot handle cases like:
  #
  #   DB.filter(:a + 1 < 0)
  class Unbinder < ASTTransformer
    # The <tt>SQL::ComplexExpression<tt> operates that will be considered
    # for transformation.
    UNBIND_OPS = [:'=', :'!=', :<, :>, :<=, :>=]

    # The key classes (first argument of the ComplexExpression) that will
    # considered for transformation.
    UNBIND_KEY_CLASSES = [Symbol, SQL::Identifier, SQL::QualifiedIdentifier]

    # The value classes (second argument of the ComplexExpression) that
    # will be considered for transformation.
    UNBIND_VALUE_CLASSES = [Numeric, String, Date, Time]

    # The hash of bind variables that were extracted from the dataset filter.
    attr_reader :binds

    # Intialize an empty +binds+ hash.
    def initialize
      @binds = {}
    end

    private

    # Create a suitable bound variable key for the object, which should be
    # an instance of one of the +UNBIND_KEY_CLASSES+.
    def bind_key(obj)
      case obj
      when Symbol, String
        obj
      when SQL::Identifier
        bind_key(obj.value)
      when SQL::QualifiedIdentifier
        :"#{bind_key(obj.table)}.#{bind_key(obj.column)}"
      else
        raise Error, "unhandled object in Sequel::Unbinder#bind_key: #{obj}"
      end
    end

    # Handle <tt>SQL::ComplexExpression</tt> instances with suitable ops
    # and arguments, substituting the value with a bound variable placeholder
    # and assigning it an entry in the +binds+ hash with a matching key.
    def v(o)
      if o.is_a?(SQL::ComplexExpression) && UNBIND_OPS.include?(o.op)
        l, r = o.args
        if UNBIND_KEY_CLASSES.any?{|c| l.is_a?(c)} && UNBIND_VALUE_CLASSES.any?{|c| r.is_a?(c)} && !r.is_a?(LiteralString)
          key = bind_key(l)
          if (old = binds[key]) && old != r
            raise UnbindDuplicate, "two different values for #{key.inspect}: #{[r, old].inspect}" 
          end
          binds[key] = r
          SQL::ComplexExpression.new(o.op, l, :"$#{key}")
        else
          super
        end
      else
        super
      end
    end
  end
end