Class: ActiveRecord::ConnectionAdapters::QueryIntent

Inherits:
Object
  • Object
show all
Defined in:
activerecord/lib/active_record/connection_adapters/query_intent.rb

Overview

:nodoc:

Defined Under Namespace

Classes: EventBuffer

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(adapter:, arel: nil, raw_sql: nil, processed_sql: nil, name: "SQL", binds: [], prepare: false, allow_async: false, allow_retry: false, materialize_transactions: true, batch: false) ⇒ QueryIntent

Returns a new instance of QueryIntent.



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
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 37

def initialize(adapter:, arel: nil, raw_sql: nil, processed_sql: nil, name: "SQL", binds: [], prepare: false, allow_async: false,
               allow_retry: false, materialize_transactions: true, batch: false)
  if arel.nil? && raw_sql.nil? && processed_sql.nil?
    raise ArgumentError, "One of arel, raw_sql, or processed_sql must be provided"
  end

  @adapter = adapter
  @arel = arel
  @raw_sql = raw_sql
  @name = name
  @binds = binds
  @prepare = prepare
  @allow_async = allow_async
  @ran_async = nil
  @allow_retry = allow_retry
  @materialize_transactions = materialize_transactions
  @batch = batch
  @processed_sql = processed_sql
  @type_casted_binds = nil
  @notification_payload = nil
  @raw_result = nil
  @raw_result_available = false
  @executed = false
  @write_query = nil

  # Deferred execution state
  @pool = adapter.pool
  @session = nil
  @mutex = ActiveSupport::Concurrency::NullLock
  @error = nil
  @lock_wait = nil
  @event_buffer = nil
end

Instance Attribute Details

#adapterObject

Returns the value of attribute adapter.



35
36
37
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 35

def adapter
  @adapter
end

#allow_asyncObject (readonly)

Returns the value of attribute allow_async.



32
33
34
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 32

def allow_async
  @allow_async
end

#allow_retryObject (readonly)

Returns the value of attribute allow_retry.



32
33
34
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 32

def allow_retry
  @allow_retry
end

#arelObject (readonly)

Returns the value of attribute arel.



32
33
34
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 32

def arel
  @arel
end

#batchObject (readonly)

Returns the value of attribute batch.



32
33
34
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 32

def batch
  @batch
end

#bindsObject

Returns the value of attribute binds.



35
36
37
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 35

def binds
  @binds
end

#lock_waitObject (readonly)

Returns the value of attribute lock_wait.



32
33
34
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 32

def lock_wait
  @lock_wait
end

#materialize_transactionsObject (readonly)

Returns the value of attribute materialize_transactions.



32
33
34
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 32

def materialize_transactions
  @materialize_transactions
end

#nameObject (readonly)

Returns the value of attribute name.



32
33
34
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 32

def name
  @name
end

#notification_payloadObject

Returns the value of attribute notification_payload.



35
36
37
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 35

def notification_payload
  @notification_payload
end

#poolObject (readonly)

Returns the value of attribute pool.



32
33
34
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 32

def pool
  @pool
end

#prepareObject (readonly)

Returns the value of attribute prepare.



32
33
34
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 32

def prepare
  @prepare
end

#ran_asyncObject

Returns the value of attribute ran_async.



35
36
37
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 35

def ran_async
  @ran_async
end

#raw_sqlObject

Returns raw SQL, compiling from arel if needed, memoized



137
138
139
140
141
142
143
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 137

def raw_sql
  @raw_sql ||
    begin
      compile_arel!
      @raw_sql
    end
end

#sessionObject

Returns the value of attribute session.



32
33
34
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 32

def session
  @session
end

Instance Method Details

#affected_rowsObject



225
226
227
228
229
230
231
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 225

def affected_rows
  raise "Cannot call affected_rows before query has executed" unless @executed
  raise "Cannot call affected_rows after cast_result has been called" if defined?(@cast_result)

  ensure_result
  @affected_rows ||= adapter.send(:affected_rows, @raw_result)
end

#cancelObject



131
132
133
134
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 131

def cancel
  return unless pending?
  @error = FutureResult::Canceled.new
end

#canceled?Boolean

Was this intent canceled?

Returns:

  • (Boolean)


127
128
129
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 127

def canceled?
  @session && !@session.active?
end

#cast_resultObject



217
218
219
220
221
222
223
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 217

def cast_result
  raise "Cannot call cast_result before query has executed" unless @executed
  raise "Cannot call cast_result after affected_rows has been called" if defined?(@affected_rows)

  ensure_result
  @cast_result ||= adapter.send(:cast_result, @raw_result)
end

#ensure_resultObject

Ensure the result is available, blocking if necessary

Raises:

  • (@error)


205
206
207
208
209
210
211
212
213
214
215
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 205

def ensure_result
  if @session
    # Async was scheduled: wait for result (sets lock_wait)
    execute_or_wait
  end

  @event_buffer&.flush

  # Raise any error captured during deferred execution
  raise @error if @error
end

#execute!Object



163
164
165
166
167
168
169
170
171
172
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 163

def execute!
  if can_run_async?
    async_schedule!(ActiveRecord::Base.asynchronous_queries_session)
  else
    @ran_async = false
    run_query!
  end
ensure
  @executed = true
end

#execute_or_skipObject

Called by background thread to execute if not already done



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
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 95

def execute_or_skip
  return unless pending?

  @session.synchronize do
    return unless pending?

    @pool.with_connection do |connection|
      return unless @mutex.try_lock
      begin
        if pending?
          @event_buffer = EventBuffer.new(self, ActiveSupport::Notifications.instrumenter)
          ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = @event_buffer

          @adapter = connection
          @ran_async = true
          run_query!
        end
      rescue => error
        @error = error
      ensure
        @mutex.unlock
      end
    end
  end
end

#finishObject



182
183
184
185
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 182

def finish
  affected_rows # just to consume/close the result
  nil
end

#future_resultObject



174
175
176
177
178
179
180
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 174

def future_result
  if pending? || can_run_async?
    FutureResult.new(self)
  else
    FutureResult.wrap(cast_result)
  end
end

#has_binds?Boolean

Returns:

  • (Boolean)


158
159
160
161
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 158

def has_binds?
  compile_arel!
  binds && !binds.empty?
end

#inspectObject

Returns a string representation showing key attributes



90
91
92
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 90

def inspect
  "#<#{self.class.name} name=#{name.inspect} allow_retry=#{allow_retry} materialize_transactions=#{materialize_transactions}>"
end

#pending?Boolean

Is this intent still pending (result not yet available)?

Returns:

  • (Boolean)


122
123
124
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 122

def pending?
  !@raw_result_available && @session&.active?
end

#processed_sqlObject

Returns preprocessed SQL, memoized



146
147
148
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 146

def processed_sql
  @processed_sql ||= preprocess_query
end

#raw_resultObject

Access the raw result, ensuring it’s available first



199
200
201
202
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 199

def raw_result
  ensure_result
  @raw_result
end

#raw_result=(value) ⇒ Object

Internal setter for raw result



188
189
190
191
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 188

def raw_result=(value)
  @raw_result = value
  @raw_result_available = true
end

#raw_result_available?Boolean

Check if result has been populated yet (without blocking)

Returns:

  • (Boolean)


194
195
196
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 194

def raw_result_available?
  @raw_result_available
end

#to_hObject

Returns a hash representation of the QueryIntent for debugging/introspection



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 72

def to_h
  {
    arel: arel,
    raw_sql: raw_sql,
    processed_sql: processed_sql,
    name: name,
    binds: binds,
    prepare: prepare,
    allow_async: allow_async,
    allow_retry: allow_retry,
    materialize_transactions: materialize_transactions,
    batch: batch,
    type_casted_binds: type_casted_binds,
    notification_payload: notification_payload
  }
end

#type_casted_bindsObject



150
151
152
153
154
155
156
# File 'activerecord/lib/active_record/connection_adapters/query_intent.rb', line 150

def type_casted_binds
  @type_casted_binds ||=
    begin
      compile_arel!
      adapter.type_casted_binds(binds)
    end
end