Module: ActiveRecord::ConnectionAdapters::Sunstone::DatabaseStatements

Defined in:
lib/active_record/connection_adapters/sunstone/database_statements.rb

Defined Under Namespace

Classes: SunstonePartialQueryCollector

Instance Method Summary collapse

Instance Method Details

#cacheable_query(klass, arel) ⇒ Object

This is used in the StatementCache object. It returns an object that can be used to query the database repeatedly.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 59

def cacheable_query(klass, arel) # :nodoc:
  if prepared_statements
    sql, binds = visitor.compile(arel.ast, collector)
    query = klass.query(sql)
  elsif self.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
    collector = SunstonePartialQueryCollector.new(self.collector)
    parts, binds = visitor.compile(arel.ast, collector)
    query = StatementCache::PartialQuery.new(parts, true)
  else
    collector = klass.partial_query_collector
    parts, binds = visitor.compile(arel.ast, collector)
    query = klass.partial_query(parts)
  end
  [query, binds]
end

#delete(arel, name = nil, binds = []) ⇒ Object



195
196
197
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 195

def delete(arel, name = nil, binds = [])
  exec_delete(arel, name, binds)
end

#exec_insert(arel, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) ⇒ Object

Executes insert sql statement in the context of this connection using binds as the bind substitutes. name is logged along with the executed sql statement. Some adapters support the ‘returning` keyword argument which allows to control the result of the query: `nil` is the default value and maintains default behavior. If an array of column names is passed - the result will contain values of the specified columns from the inserted row.

TODO: Add support for returning



110
111
112
113
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 110

def exec_insert(arel, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
  sar, binds = sar_for_insert(arel, pk, binds, returning)
  internal_exec_query(sar, name, binds)
end

#insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) ⇒ Object



182
183
184
185
186
187
188
189
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 182

def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
  sar, binds = to_sar_and_binds(arel, binds)
  value = exec_insert(sar, name, binds, pk, sequence_name)

  return returning_column_values(value) unless returning.nil?

  id_value || last_inserted_id(value)
end

#internal_exec_query(arel, name = 'SAR', binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) ⇒ Object



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
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 115

def internal_exec_query(arel, name = 'SAR', binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true)
  sars = []
  multiple_requests = arel.is_a?(Arel::Collectors::Sunstone)
  type_casted_binds = binds#type_casted_binds(binds)
  
  if multiple_requests
    allowed_limit = limit_definition(arel.table)
    limit_bind_index = nil#binds.find_index { |x| x.name == 'LIMIT' }
    requested_limit = if limit_bind_index
      type_casted_binds[limit_bind_index]
    else
      arel.limit
    end

    if allowed_limit.nil?
      multiple_requests = false
    elsif requested_limit && requested_limit <= allowed_limit
      multiple_requests = false
    else
      multiple_requests = true
    end
  end

  send_request = lambda { |req_arel|
    sar = to_sar(req_arel, type_casted_binds)
    sars.push(sar)
    log_mess = sar.path.split('?', 2)
    log("#{sar.method} #{log_mess[0]} #{(log_mess[1] && !log_mess[1].empty?) ? MessagePack.unpack(CGI.unescape(log_mess[1])) : '' }", name) do
      with_raw_connection do |conn|
        response = conn.send_request(sar)
        if response.is_a?(Net::HTTPNoContent)
          nil
        else
          JSON.parse(response.body)
        end
      end
    end
  }

  result = if multiple_requests
    binds.delete_at(limit_bind_index) if limit_bind_index

    limit, offset, results = allowed_limit, 0, []
    while requested_limit ? offset < requested_limit : true
      split_arel = arel.dup
      split_arel.limit = limit
      split_arel.offset = offset
      request_results = send_request.call(split_arel)
      results = results + request_results
      break if request_results.size < limit
      offset = offset + limit
    end
    results
  else
    send_request.call(arel)
  end
  
  if sars[0].instance_variable_defined?(:@sunstone_calculation) && sars[0].instance_variable_get(:@sunstone_calculation)
    # this is a count, min, max.... yea i know..
    ActiveRecord::Result.new(['all'], [result], {:all => @type_map.lookup('integer', {})})
  elsif result.is_a?(Array)
    ActiveRecord::Result.new(result[0] ? result[0].keys : [], result.map{|r| r.values})
  else
    ActiveRecord::Result.new(result.keys, [result.values])
  end
end

#last_inserted_id(result) ⇒ Object



199
200
201
202
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 199

def last_inserted_id(result)
  row = result.rows.first
  row && row['id']
end

#returning_column_values(result) ⇒ Object



204
205
206
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 204

def returning_column_values(result)
  result.rows.first
end

#sar_for_insert(sql, pk, binds, returning) ⇒ Object



51
52
53
54
55
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 51

def sar_for_insert(sql, pk, binds, returning)
  # TODO: when StandardAPI supports returning we can do this; it might
  # already need to investigate
  to_sar_and_binds(sql, binds)
end

#select_all(arel, name = nil, binds = [], preparable: nil, async: false) ⇒ Object

Returns an ActiveRecord::Result instance.



93
94
95
96
97
98
99
100
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 93

def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
  arel = arel_from_relation(arel)
  sar, binds, preparable = to_sar_and_binds(arel, binds, preparable)

  select(sar, name, binds, prepare: prepared_statements && preparable, async: async && FutureResult::SelectAll)
rescue ::RangeError
  ActiveRecord::Result.empty(async: async)
end

#to_sar(arel_or_sar_string, binds = nil) ⇒ Object

Converts an arel AST to a Sunstone API Request



21
22
23
24
25
26
27
28
29
30
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 21

def to_sar(arel_or_sar_string, binds = nil)
  if arel_or_sar_string.respond_to?(:ast)
    sar = visitor.accept(arel_or_sar_string.ast, collector)
    binds = sar.binds if binds.nil?
  else
    sar = arel_or_sar_string
  end

  sar.compile(binds)
end

#to_sar_and_binds(arel_or_sar_string, binds = [], preparable = nil) ⇒ Object

:nodoc:



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 32

def to_sar_and_binds(arel_or_sar_string, binds = [], preparable = nil) # :nodoc:
  # Arel::TreeManager -> Arel::Node
  if arel_or_sar_string.respond_to?(:ast)
    arel_or_sar_string = arel_or_sar_string.ast
  end

  if Arel.arel_node?(arel_or_sar_string) && !(String === arel_or_sar_string)
    unless binds.empty?
      raise "Passing bind parameters with an arel AST is forbidden. " \
        "The values must be stored on the AST directly"
    end
    
    sar = visitor.accept(arel_or_sar_string, collector)
    [sar.freeze, sar.binds, false]
  else
    [arel_or_sar_string.dup.freeze, binds, false]
  end
end

#to_sql(arel, binds = []) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 8

def to_sql(arel, binds = [])
  if arel.respond_to?(:ast)
    unless binds.empty?
      raise "Passing bind parameters with an arel AST is forbidden. " \
        "The values must be stored on the AST directly"
    end
    Arel::Visitors::ToSql.new(self).accept(arel.ast, Arel::Collectors::SubstituteBinds.new(self, Arel::Collectors::SQLString.new)).value
  else
    arel.dup.freeze
  end
end

#update(arel, name = nil, binds = []) ⇒ Object



191
192
193
# File 'lib/active_record/connection_adapters/sunstone/database_statements.rb', line 191

def update(arel, name = nil, binds = [])
  exec_update(arel, name, binds)
end