Background
Trigger-based primary key generation was removed in 61f8a305 (2018, Rails 6 / adapter 6.0) without a prior deprecation cycle. Since then, several users have run into the missing functionality and reverse-engineered the fix in user space:
In retrospect, the 2018 removal was a breaking change without notice, and there is still demand for the trigger-based path — especially on Oracle 11.2 where GENERATED AS IDENTITY is not available, and for legacy schemas with custom triggers.
Relationship to identity work
Proposal
Add a primary_key_trigger: true option to create_table, mirroring the shape of identity: true:
create_table :foo # sequence + prefetch (current default)
create_table :foo, identity: true # identity column (Phase 1, Oracle 12.1+)
create_table :foo, primary_key_trigger: true # sequence + BEFORE INSERT trigger + RETURNING
Behavior
- Creates the table with a
NUMBER PK column
- Creates the backing
<table>_seq sequence (same as today)
- Creates a
BEFORE INSERT FOR EACH ROW trigger that populates :NEW.id from <table>_seq.NEXTVAL when the inserted value is NULL
prefetch_primary_key? returns false for the table (so Rails issues INSERT without computing the id)
INSERT uses RETURNING <pk> INTO :returning_id to round-trip the generated id
Validation (symmetric with identity:)
primary_key_trigger: true + id: false → ArgumentError
primary_key_trigger: true + non-:primary_key id: (e.g., :uuid, :integer) → ArgumentError
primary_key_trigger: true + composite primary_key: → ArgumentError
primary_key_trigger: true + identity: true → ArgumentError (mutually exclusive)
Detection / round-trip
prefetch_primary_key? needs to detect trigger-based tables in addition to identity ones — query all_triggers for BEFORE INSERT FOR EACH ROW triggers tied to the PK column, and cache the result in @do_not_prefetch_primary_key alongside the identity check
schema_dumper should emit , primary_key_trigger: true for round-trip
structure_dump already supports trigger emission via structure_dump: db_stored_code
Implementation notes
Why now
Background
Trigger-based primary key generation was removed in
61f8a305(2018, Rails 6 / adapter 6.0) without a prior deprecation cycle. Since then, several users have run into the missing functionality and reverse-engineered the fix in user space:Trigger based primary key sequence not returned when prefetch_primary_key? is false(reporter monkey-patchessql_for_insertto addRETURNINGfor String pk)Getting primary key with RETURNING INTO doesn't work in Rails 7 and laterAdapter 7.1.0 ignores self.sequence_name, includes ID in INSERT instead of using RETURNING IDIn retrospect, the 2018 removal was a breaking change without notice, and there is still demand for the trigger-based path — especially on Oracle 11.2 where
GENERATED AS IDENTITYis not available, and for legacy schemas with custom triggers.Relationship to identity work
create_table :foo, identity: truefor Oracle 12.1+ (GENERATED BY DEFAULT AS IDENTITY)Migration[8.2]+(depends on unmerged Rails extension point)Proposal
Add a
primary_key_trigger: trueoption tocreate_table, mirroring the shape ofidentity: true:Behavior
NUMBERPK column<table>_seqsequence (same as today)BEFORE INSERT FOR EACH ROWtrigger that populates:NEW.idfrom<table>_seq.NEXTVALwhen the inserted value isNULLprefetch_primary_key?returnsfalsefor the table (so Rails issuesINSERTwithout computing the id)INSERTusesRETURNING <pk> INTO :returning_idto round-trip the generated idValidation (symmetric with
identity:)primary_key_trigger: true+id: false→ArgumentErrorprimary_key_trigger: true+ non-:primary_keyid:(e.g.,:uuid,:integer) →ArgumentErrorprimary_key_trigger: true+ compositeprimary_key:→ArgumentErrorprimary_key_trigger: true+identity: true→ArgumentError(mutually exclusive)Detection / round-trip
prefetch_primary_key?needs to detect trigger-based tables in addition to identity ones — queryall_triggersforBEFORE INSERT FOR EACH ROWtriggers tied to the PK column, and cache the result in@do_not_prefetch_primary_keyalongside the identity checkschema_dumpershould emit, primary_key_trigger: truefor round-tripstructure_dumpalready supports trigger emission viastructure_dump: db_stored_codeImplementation notes
create_sequence_and_triggerexists and is currently called for the default sequence-based path. The work is mostly in:create_tableprefetch_primary_key?/ a new helperschema_dumperidentity_primary_key_spec.rbsql_for_insertString-pkRETURNINGfix shipped as part of Add explicit identity: option to create_table for Oracle 12.1+ #2579 already addresses the underlying gap reported in Trigger based primary key sequence not returned when prefetch_primary_key? is false #2426, so this issue is purely about restoring thecreate_table-level ergonomics.Why now
identity:makes the adapter's behavior easier to reason about