Class: ROM::SQL::Function

Inherits:
Attribute
  • Object
show all
Defined in:
lib/rom/sql/function.rb

Overview

Specialized attribute type for defining SQL functions

Constant Summary collapse

WINDOW_FRAMES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Hash.new do |cache, frame|
  type = frame.key?(:rows) ? 'ROWS' : 'RANGE'
  bounds = frame[:rows] || frame[:range]
  cache[frame] = "#{ type } BETWEEN #{ frame_limit(bounds[0]) } AND #{ frame_limit(bounds[1])  }"
end

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object (private)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



174
175
176
177
178
179
180
181
182
183
184
# File 'lib/rom/sql/function.rb', line 174

def method_missing(meth, *args)
  if func
    if func.respond_to?(meth)
      meta(func: func.__send__(meth, *args))
    else
      super
    end
  else
    meta(func: Sequel::SQL::Function.new(meth.to_s.upcase, *args))
  end
end

Class Method Details

.frame_limit(value) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/rom/sql/function.rb', line 11

def frame_limit(value)
  case value
  when :current then 'CURRENT ROW'
  when :start then 'UNBOUNDED PRECEDING'
  when :end then 'UNBOUNDED FOLLOWING'
  else
    if value > 0
      "#{ value } FOLLOWING"
    else
      "#{ value.abs } PRECEDING"
    end
  end
end

Instance Method Details

#cast(expr, db_type = TypeSerializer[:default].call(type)) ⇒ ROM::SQL::Attribute

Convert an expression result to another data type

Examples:

users.select { bool::cast(json_data.get_text('activated'), :boolean).as(:activated) }
users.select { bool::cast(json_data.get_text('activated')).as(:activated) }

Parameters:

  • expr (ROM::SQL::Attribute)

    Expression to be cast

  • db_type (String) (defaults to: TypeSerializer[:default].call(type))

    Target database type (usually can be inferred from the target data type)

Returns:



131
132
133
# File 'lib/rom/sql/function.rb', line 131

def cast(expr, db_type = TypeSerializer[:default].call(type))
  Attribute[type].meta(sql_expr: ::Sequel.cast(expr, db_type))
end

#filter(condition = Undefined) {|block| ... } ⇒ SQL::Function

Add a FILTER clause to aggregate function (supported by PostgreSQL 9.4+) Filter aggregate using the specified conditions

Examples:

users.project { int::count(:id).filter(name.is("Jack")).as(:jacks) }.order(nil)
users.project { int::count(:id).filter { name.is("John") }).as(:johns) }.order(nil)

Parameters:

Yields:

  • (block)

    A block with restrictions

Returns:

See Also:



150
151
152
153
154
155
156
157
158
159
# File 'lib/rom/sql/function.rb', line 150

def filter(condition = Undefined, &block)
  if block
    conditions = schema.restriction(&block)
    conditions = conditions & condition unless condition.equal?(Undefined)
  else
    conditions = condition
  end

  super(conditions)
end

#is(other) ⇒ Object

See Also:



66
67
68
69
70
# File 'lib/rom/sql/function.rb', line 66

def is(other)
  ::ROM::SQL::Attribute[::ROM::SQL::Types::Bool].meta(
    sql_expr: ::Sequel::SQL::BooleanExpression.new(:'=', func, other)
  )
end

#nameObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



50
51
52
# File 'lib/rom/sql/function.rb', line 50

def name
  meta[:alias] || super
end

#not(other) ⇒ Object

See Also:



75
76
77
# File 'lib/rom/sql/function.rb', line 75

def not(other)
  !is(other)
end

#over(partition: nil, order: nil, frame: nil) ⇒ SQL::Function

Add an OVER clause making a window function call

Examples:

users.select { [id, int::row_number().over(partition: name, order: id).as(:row_no)] }
users.select { [id, int::row_number().over(partition: [first_name, last_name], order: id).as(:row_no)] }

frame variants

# ROWS BETWEEN 3 PRECEDING AND CURRENT ROW
row_number.over(frame: { rows: [-3, :current] })

# ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING
row_number.over(frame: { rows: [-3, 3] })

# ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
row_number.over(frame: { rows: [:start, :current] })

# ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
row_number.over(frame: { rows: [:current, :end] })

frame shortcuts

# ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
row_number.over(frame: :all)

# ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
row_number.over(frame: :rows)

# RANGE BETWEEN CURRENT ROW AND CURRENT ROW
row_number.over(frame: { range: :current} )

Parameters:

  • :partition (Hash)

    a customizable set of options

  • :order (Hash)

    a customizable set of options

  • :frame (Hash)

    a customizable set of options

Returns:

See Also:



115
116
117
# File 'lib/rom/sql/function.rb', line 115

def over(partition: nil, order: nil, frame: nil)
  super(partition: partition, order: order, frame: WINDOW_FRAMES[frame])
end

#qualified(table_alias = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



57
58
59
60
61
# File 'lib/rom/sql/function.rb', line 57

def qualified(table_alias = nil)
  meta(
    func: ::Sequel::SQL::Function.new(func.name, *func.args.map { |arg| arg.respond_to?(:qualified) ? arg.qualified(table_alias) : arg })
  )
end

#sql_literal(ds) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



41
42
43
44
45
46
47
# File 'lib/rom/sql/function.rb', line 41

def sql_literal(ds)
  if name
    ds.literal(func.as(name))
  else
    ds.literal(func)
  end
end