Module: ActiveRecord::ConnectionAdapters::OracleEnhanced::DatabaseStatements

Included in:
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
Defined in:
lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb

Instance Method Summary collapse

Instance Method Details

#affected_rows(result) ⇒ Object



81
82
83
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 81

def affected_rows(result)
  result[:affected_rows_count]
end

#begin_db_transactionObject

:nodoc:



197
198
199
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 197

def begin_db_transaction # :nodoc:
  _connection.autocommit = false
end

#begin_isolated_db_transaction(isolation) ⇒ Object



211
212
213
214
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 211

def begin_isolated_db_transaction(isolation)
  begin_db_transaction
  execute "SET TRANSACTION ISOLATION LEVEL  #{transaction_isolation_levels.fetch(isolation)}"
end

#build_explain_clause(options = []) ⇒ Object



100
101
102
103
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 100

def build_explain_clause(options = [])
  # Oracle does not have anything similar to "EXPLAIN ANALYZE"
  # https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/EXPLAIN-PLAN.html#GUID-FD540872-4ED3-4936-96A2-362539931BA0
end

#cast_result(result) ⇒ Object



73
74
75
76
77
78
79
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 73

def cast_result(result)
  if result.nil?
    ActiveRecord::Result.empty
  else
    ActiveRecord::Result.new(result[:columns], result[:rows])
  end
end

#commit_db_transactionObject

:nodoc:



216
217
218
219
220
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 216

def commit_db_transaction # :nodoc:
  _connection.commit
ensure
  _connection.autocommit = true
end

#create_savepoint(name = current_savepoint_name) ⇒ Object

:nodoc:



228
229
230
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 228

def create_savepoint(name = current_savepoint_name) # :nodoc:
  execute("SAVEPOINT #{name}", "TRANSACTION")
end

#default_sequence_name(table_name, primary_key = nil) ⇒ Object

Returns default sequence name for table. Will take all or first 26 characters of table name and append _seq suffix



242
243
244
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 242

def default_sequence_name(table_name, primary_key = nil)
  table_name.to_s.gsub((/(^|\.)([\w$-]{1,#{sequence_name_length - 4}})([\w$-]*)$/), '\1\2_seq')
end

#empty_insert_statement_valueObject

Oracle Database does not support this feature Refer community.oracle.com/ideas/13845 and consider to vote if you need this feature.

Raises:

  • (NotImplementedError)


277
278
279
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 277

def empty_insert_statement_value
  raise NotImplementedError
end

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

New method in ActiveRecord 3.1



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

def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
  sql, binds = sql_for_insert(sql, pk, binds, returning)
  type_casted_binds = type_casted_binds(binds)

  log(sql, name, binds, type_casted_binds) do
    cached = false
    cursor = nil
    returning_id_col = returning_id_index = nil
    with_retry do
      if binds.nil? || binds.empty?
        cursor = _connection.prepare(sql)
      else
        unless @statements.key?(sql)
          @statements[sql] = _connection.prepare(sql)
        end

        cursor = @statements[sql]

        cursor.bind_params(type_casted_binds)

        if /:returning_id/.match?(sql)
          # it currently expects that returning_id comes last part of binds
          returning_id_index = binds.size
          cursor.bind_returning_param(returning_id_index, Integer)
        end

        cached = true
      end

      cursor.exec_update
    end

    rows = []
    if returning_id_index
      returning_id = cursor.get_returning_param(returning_id_index, Integer).to_i
      rows << [returning_id]
    end
    cursor.close unless cached
    build_result(columns: returning_id_col || [], rows: rows)
  end
end

#exec_rollback_db_transactionObject

:nodoc:



222
223
224
225
226
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 222

def exec_rollback_db_transaction # :nodoc:
  _connection.rollback
ensure
  _connection.autocommit = true
end

#exec_rollback_to_savepoint(name = current_savepoint_name) ⇒ Object

:nodoc:



232
233
234
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 232

def exec_rollback_to_savepoint(name = current_savepoint_name) # :nodoc:
  execute("ROLLBACK TO #{name}", "TRANSACTION")
end

#exec_update(sql, name = nil, binds = []) ⇒ Object Also known as: exec_delete

New method in ActiveRecord 3.1



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

def exec_update(sql, name = nil, binds = [])
  type_casted_binds = type_casted_binds(binds)

  log(sql, name, binds, type_casted_binds) do
    with_retry do
      cached = false
      if binds.nil? || binds.empty?
        cursor = _connection.prepare(sql)
      else
        if @statements.key?(sql)
          cursor = @statements[sql]
        else
          cursor = @statements[sql] = _connection.prepare(sql)
        end

        cursor.bind_params(type_casted_binds)

        cached = true
      end

      res = cursor.exec_update
      cursor.close unless cached
      res
    end
  end
end

#executeObject

Executes a SQL statement



23
24
25
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 23

def execute(...)
  super
end

#explain(arel, binds = [], options = []) ⇒ Object



89
90
91
92
93
94
95
96
97
98
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 89

def explain(arel, binds = [], options = [])
  sql = "EXPLAIN PLAN FOR #{to_sql(arel, binds)}"
  return if /FROM all_/.match?(sql)
  if ORACLE_ENHANCED_CONNECTION == :jdbc
    exec_query(sql, "EXPLAIN", binds)
  else
    exec_query(sql, "EXPLAIN")
  end
  select_values("SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)", "EXPLAIN").join("\n")
end

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



115
116
117
118
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 115

def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
  pk = nil if id_value
  Array(super || id_value)
end

#insert_fixture(fixture, table_name) ⇒ Object

Inserts the given fixture into the table. Overridden to properly handle lobs.



247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 247

def insert_fixture(fixture, table_name) # :nodoc:
  super

  if ActiveRecord::Base.pluralize_table_names
    klass = table_name.to_s.singularize.camelize
  else
    klass = table_name.to_s.camelize
  end

  klass = klass.constantize rescue nil
  if klass.respond_to?(:ancestors) && klass.ancestors.include?(ActiveRecord::Base)
    write_lobs(table_name, klass, fixture, klass.lob_columns)
  end
end

#insert_fixtures_set(fixture_set, tables_to_delete = []) ⇒ Object



262
263
264
265
266
267
268
269
270
271
272
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 262

def insert_fixtures_set(fixture_set, tables_to_delete = [])
  disable_referential_integrity do
    transaction(requires_new: true) do
      tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }

      fixture_set.each do |table_name, rows|
        rows.each { |row| insert_fixture(row, table_name) }
      end
    end
  end
end

#raw_execute(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: false) ⇒ Object

Low level execution of a SQL statement on the connection returning adapter specific result object.



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

def raw_execute(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: false)
  sql = preprocess_query(sql)

  type_casted_binds = type_casted_binds(binds)
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
    log(sql, name, binds, type_casted_binds, async: async) do
      cursor = nil
      cached = false
      with_retry do
        if binds.nil? || binds.empty?
          cursor = conn.prepare(sql)
        else
          unless @statements.key? sql
            @statements[sql] = conn.prepare(sql)
          end

          cursor = @statements[sql]
          cursor.bind_params(type_casted_binds)

          cached = true
        end
        cursor.exec
      end

      columns = cursor.get_col_names.map do |col_name|
        oracle_downcase(col_name)
      end

      rows = []
      if cursor.select_statement?
        fetch_options = { get_lob_value: (name != "Writable Large Object") }
        while row = cursor.fetch(fetch_options)
          rows << row
        end
      end

      affected_rows_count = cursor.row_count

      cursor.close unless cached

      { columns: columns, rows: rows, affected_rows_count: affected_rows_count }
    end
  end
end

#release_savepoint(name = current_savepoint_name) ⇒ Object

:nodoc:



236
237
238
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 236

def release_savepoint(name = current_savepoint_name) # :nodoc:
  # there is no RELEASE SAVEPOINT statement in Oracle
end

#returning_column_values(result) ⇒ Object



193
194
195
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 193

def returning_column_values(result)
  result.rows.first
end

#sql_for_insert(sql, pk, binds, _returning) ⇒ Object

New method in ActiveRecord 3.1 Will add RETURNING clause in case of trigger generated primary keys



107
108
109
110
111
112
113
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 107

def sql_for_insert(sql, pk, binds, _returning)
  unless pk == false || pk.nil? || pk.is_a?(Array) || pk.is_a?(String)
    sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO :returning_id"
    (binds = binds.dup) << ActiveRecord::Relation::QueryAttribute.new("returning_id", nil, Type::OracleEnhanced::Integer.new)
  end
  super
end

#supports_explain?Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 85

def supports_explain?
  true
end

#transaction_isolation_levelsObject



201
202
203
204
205
206
207
208
209
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 201

def transaction_isolation_levels
  # Oracle database supports `READ COMMITTED` and `SERIALIZABLE`
  # No read uncommitted nor repeatable read supppoted
  # http://docs.oracle.com/cd/E11882_01/server.112/e26088/statements_10005.htm#SQLRF55422
  {
    read_committed:   "READ COMMITTED",
    serializable:     "SERIALIZABLE"
  }
end

#write_lobs(table_name, klass, attributes, columns) ⇒ Object

Writes LOB values from attributes for specified columns



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 282

def write_lobs(table_name, klass, attributes, columns) # :nodoc:
  id = quote(attributes[klass.primary_key])
  columns.each do |col|
    value = attributes[col.name]
    # changed sequence of next two lines - should check if value is nil before converting to yaml
    next unless value
    value = klass.attribute_types[col.name].serialize(value)
    # value can be nil after serialization because ActiveRecord serializes [] and {} as nil
    next unless value
    uncached do
      unless lob_record = select_one(sql = <<~SQL.squish, "Writable Large Object")
        SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)}
        WHERE #{quote_column_name(klass.primary_key)} = #{id} FOR UPDATE
      SQL
        raise ActiveRecord::RecordNotFound, "statement #{sql} returned no rows"
      end
      lob = lob_record[col.name]
      _connection.write_lob(lob, value.to_s, col.type == :binary)
    end
  end
end

#write_query?(sql) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


16
17
18
19
20
# File 'lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb', line 16

def write_query?(sql) # :nodoc:
  !READ_QUERY.match?(sql)
rescue ArgumentError # Invalid encoding
  !READ_QUERY.match?(sql.b)
end