Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trigger based primary key sequence not returned when prefetch_primary_key? is false #2426

Open
jrsimmons-trevipay opened this issue Mar 5, 2025 · 0 comments

Comments

@jrsimmons-trevipay
Copy link

We noticed an issue after upgrading to v7.0.3 that looks related to the changes from #2157. Perhaps we're doing something wrong, but wanted to bring it up just in case it's a bug.

Model:

class Customer < ApplicationRecord
  self.table_name     = 'customers'
  self.primary_key    = :customer_id

  def self.prefetch_primary_key?
    false
  end
  ...
end

Overrides applied:

module ActiveRecord::ConnectionAdapters::OracleEnhanced
  module DatabaseStatements
    # We are overriding this specific method so we can retrieve the original ID upon inserting records.
    # We just removed the `|| pk.is_a?(String)` part of the conditional at the end of the `unless` line
    def sql_for_insert(sql, pk, binds)
      unless pk == false || pk.nil? || pk.is_a?(Array)
        sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO :returning_id"
        (binds = binds.dup) << ActiveRecord::Relation::QueryAttribute.new("returning_id", nil, ActiveRecord::Type::OracleEnhanced::Integer.new)
      end
      super
    end

    # We are overriding this specific method so we can specify the bind_returning_param
    # and get_returning_param (although not necessary since it's not used in the method)
    # as a `String` or else we will get a "ORA-01722: invalid number" error
    def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
      sql, binds = sql_for_insert(sql, pk, binds)

      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 without_prepared_statement?(binds)
            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, String)
            end

            cached = true
          end

          cursor.exec_update
        end

        rows = []
        if returning_id_index
          returning_id = cursor.get_returning_param(returning_id_index, String)
          rows << [returning_id]
        end
        cursor.close unless cached
        build_result(columns: returning_id_col || [], rows: rows)
      end
    end
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant