Skip to content

Commit 61f8a30

Browse files
committed
Drop trigger based primary key support for Rails 6
It is planned to support identity type based primary key support in Oracle enhanced adapter 6. The current implementation of primary key generation is very complicated. To make it simple, Oracle enhanced adapter 6 will drop trigger based primary key support. `rake db:structure:dump` still dumps triggers if `structure_dump: db_stored_code` is configured for those who maintain triggers by themselves. Closes rsim#1643
1 parent 4630a39 commit 61f8a30

File tree

7 files changed

+4
-313
lines changed

7 files changed

+4
-313
lines changed

lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb

-11
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ def tables(stream)
1313
# add table prefix or suffix for schema_migrations
1414
next if ignored? tbl
1515
table(tbl, stream)
16-
# add primary key trigger if table has it
17-
primary_key_trigger(tbl, stream)
1816
end
1917
# following table definitions
2018
# add foreign keys if table has them
@@ -27,15 +25,6 @@ def tables(stream)
2725
synonyms(stream)
2826
end
2927

30-
def primary_key_trigger(table_name, stream)
31-
if @connection.has_primary_key_trigger?(table_name)
32-
pk, _pk_seq = @connection.pk_and_sequence_for(table_name)
33-
stream.print " add_primary_key_trigger #{table_name.inspect}"
34-
stream.print ", primary_key: \"#{pk}\"" if pk != "id"
35-
stream.print "\n\n"
36-
end
37-
end
38-
3928
def synonyms(stream)
4029
syns = @connection.synonyms
4130
syns.each do |syn|

lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb

+2-24
Original file line numberDiff line numberDiff line change
@@ -658,33 +658,11 @@ def column_for(table_name, column_name)
658658
end
659659

660660
def create_sequence_and_trigger(table_name, options)
661+
# TODO: Needs rename since no triggers created
662+
# This method will be removed since sequence will not be created separately
661663
seq_name = options[:sequence_name] || default_sequence_name(table_name)
662664
seq_start_value = options[:sequence_start_value] || default_sequence_start_value
663665
execute "CREATE SEQUENCE #{quote_table_name(seq_name)} START WITH #{seq_start_value}"
664-
665-
create_primary_key_trigger(table_name, options) if options[:primary_key_trigger]
666-
end
667-
668-
def create_primary_key_trigger(table_name, options)
669-
seq_name = options[:sequence_name] || default_sequence_name(table_name)
670-
trigger_name = options[:trigger_name] || default_trigger_name(table_name)
671-
primary_key = options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)
672-
execute <<-SQL
673-
CREATE OR REPLACE TRIGGER #{quote_table_name(trigger_name)}
674-
BEFORE INSERT ON #{quote_table_name(table_name)} FOR EACH ROW
675-
BEGIN
676-
IF inserting THEN
677-
IF :new.#{quote_column_name(primary_key)} IS NULL THEN
678-
SELECT #{quote_table_name(seq_name)}.NEXTVAL INTO :new.#{quote_column_name(primary_key)} FROM dual;
679-
END IF;
680-
END IF;
681-
END;
682-
SQL
683-
end
684-
685-
def default_trigger_name(table_name)
686-
# truncate table name if necessary to fit in max length of identifier
687-
"#{table_name.to_s[0, table_name_length - 4]}_pkt"
688666
end
689667

690668
def rebuild_primary_key_index_to_default_tablespace(table_name, options)

lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb

-28
This file was deleted.

lib/active_record/connection_adapters/oracle_enhanced_adapter.rb

+2-21
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
require "active_record/connection_adapters/oracle_enhanced/schema_definitions"
3939
require "active_record/connection_adapters/oracle_enhanced/schema_dumper"
4040
require "active_record/connection_adapters/oracle_enhanced/schema_statements"
41-
require "active_record/connection_adapters/oracle_enhanced/schema_statements_ext"
4241
require "active_record/connection_adapters/oracle_enhanced/context_index"
4342
require "active_record/connection_adapters/oracle_enhanced/column"
4443
require "active_record/connection_adapters/oracle_enhanced/quoting"
@@ -142,7 +141,6 @@ module ConnectionAdapters #:nodoc:
142141
class OracleEnhancedAdapter < AbstractAdapter
143142
include OracleEnhanced::DatabaseStatements
144143
include OracleEnhanced::SchemaStatements
145-
include OracleEnhanced::SchemaStatementsExt
146144
include OracleEnhanced::ContextIndex
147145
include OracleEnhanced::Quoting
148146
include OracleEnhanced::DatabaseLimits
@@ -415,7 +413,7 @@ def discard!
415413
# when inserting a new database record (see #prefetch_primary_key?).
416414
def next_sequence_value(sequence_name)
417415
# if sequence_name is set to :autogenerated then it means that primary key will be populated by trigger
418-
return nil if sequence_name == AUTOGENERATED_SEQUENCE_NAME
416+
raise ArgumentError "Trigger based primary key is not supported" if sequence_name == AUTOGENERATED_SEQUENCE_NAME
419417
# call directly connection method to avoid prepared statement which causes fetching of next sequence value twice
420418
select_value(<<-SQL.strip.gsub(/\s+/, " "), "next sequence value")
421419
SELECT #{quote_table_name(sequence_name)}.NEXTVAL FROM dual
@@ -428,7 +426,7 @@ def prefetch_primary_key?(table_name = nil)
428426
return true if table_name.nil?
429427
table_name = table_name.to_s
430428
owner, desc_table_name = @connection.describe(table_name)
431-
do_not_prefetch = !has_primary_key?(table_name, owner, desc_table_name) || has_primary_key_trigger?(table_name, owner, desc_table_name)
429+
do_not_prefetch = !has_primary_key?(table_name, owner, desc_table_name)
432430
!do_not_prefetch
433431
end
434432

@@ -494,23 +492,6 @@ def default_tablespace
494492
SQL
495493
end
496494

497-
# check if table has primary key trigger with _pkt suffix
498-
def has_primary_key_trigger?(table_name, owner = nil, desc_table_name = nil)
499-
(owner, desc_table_name) = @connection.describe(table_name) unless owner
500-
501-
trigger_name = default_trigger_name(table_name).upcase
502-
503-
!!select_value(<<-SQL.strip.gsub(/\s+/, " "), "Primary Key Trigger", [bind_string("owner", owner), bind_string("trigger_name", trigger_name), bind_string("owner", owner), bind_string("table_name", desc_table_name)])
504-
SELECT trigger_name
505-
FROM all_triggers
506-
WHERE owner = :owner
507-
AND trigger_name = :trigger_name
508-
AND table_owner = :owner
509-
AND table_name = :table_name
510-
AND status = 'ENABLED'
511-
SQL
512-
end
513-
514495
def column_definitions(table_name)
515496
(owner, desc_table_name) = @connection.describe(table_name)
516497

spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb

-18
Original file line numberDiff line numberDiff line change
@@ -110,24 +110,6 @@ def drop_test_posts_table
110110
end
111111
end
112112

113-
describe "table with primary key trigger" do
114-
115-
after(:each) do
116-
drop_test_posts_table
117-
end
118-
119-
it "should include primary key trigger in schema dump" do
120-
create_test_posts_table(primary_key_trigger: true)
121-
expect(standard_dump).to match(/create_table "test_posts".*add_primary_key_trigger "test_posts"/m)
122-
end
123-
124-
it "should include primary key trigger with non-default primary key in schema dump" do
125-
create_test_posts_table(primary_key_trigger: true, primary_key: "post_id")
126-
expect(standard_dump).to match(/create_table "test_posts", primary_key: "post_id".*add_primary_key_trigger "test_posts", primary_key: "post_id"/m)
127-
end
128-
129-
end
130-
131113
describe "foreign key constraints" do
132114
before(:all) do
133115
schema_define do

spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb

-204
Original file line numberDiff line numberDiff line change
@@ -153,172 +153,6 @@ class ::TestEmployee < ActiveRecord::Base; end
153153

154154
end
155155

156-
describe "create table with primary key trigger" do
157-
def create_table_with_trigger(options = {})
158-
options.merge! primary_key_trigger: true, force: true
159-
schema_define do
160-
create_table :test_employees, options do |t|
161-
t.string :first_name
162-
t.string :last_name
163-
end
164-
end
165-
end
166-
167-
def create_table_and_separately_trigger(options = {})
168-
options.merge! force: true
169-
schema_define do
170-
create_table :test_employees, options do |t|
171-
t.string :first_name
172-
t.string :last_name
173-
end
174-
add_primary_key_trigger :test_employees, options
175-
end
176-
end
177-
178-
def drop_table_with_trigger(options = {})
179-
seq_name = options[:sequence_name]
180-
schema_define do
181-
drop_table :test_employees, (seq_name ? { sequence_name: seq_name } : {})
182-
end
183-
Object.send(:remove_const, "TestEmployee")
184-
ActiveRecord::Base.clear_cache!
185-
end
186-
187-
describe "with default primary key" do
188-
before(:all) do
189-
@conn = ActiveRecord::Base.connection
190-
create_table_with_trigger
191-
class ::TestEmployee < ActiveRecord::Base
192-
end
193-
end
194-
195-
after(:all) do
196-
drop_table_with_trigger
197-
end
198-
199-
it "should populate primary key using trigger" do
200-
expect do
201-
@conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
202-
end.not_to raise_error
203-
end
204-
205-
it "should return new key value using connection insert method" do
206-
insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, "id")
207-
expect(@conn.select_value("SELECT test_employees_seq.currval FROM dual")).to eq(insert_id)
208-
end
209-
210-
it "should create new record for model" do
211-
e = TestEmployee.create!(first_name: "Raimonds")
212-
expect(@conn.select_value("SELECT test_employees_seq.currval FROM dual")).to eq(e.id)
213-
end
214-
215-
it "should not generate NoMethodError for :returning_id:Symbol" do
216-
set_logger
217-
@conn.reconnect! unless @conn.active?
218-
@conn.insert("INSERT INTO test_employees (first_name) VALUES ('Yasuo')", nil, "id")
219-
expect(@logger.output(:error)).not_to match(/^Could not log "sql.active_record" event. NoMethodError: undefined method `name' for :returning_id:Symbol/)
220-
clear_logger
221-
end
222-
223-
end
224-
225-
describe "with separate creation of primary key trigger" do
226-
before(:all) do
227-
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
228-
@conn = ActiveRecord::Base.connection
229-
create_table_and_separately_trigger
230-
class ::TestEmployee < ActiveRecord::Base
231-
end
232-
end
233-
234-
after(:all) do
235-
drop_table_with_trigger
236-
end
237-
238-
it "should populate primary key using trigger" do
239-
expect do
240-
@conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
241-
end.not_to raise_error
242-
end
243-
244-
it "should return new key value using connection insert method" do
245-
insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, "id")
246-
expect(@conn.select_value("SELECT test_employees_seq.currval FROM dual")).to eq(insert_id)
247-
end
248-
249-
it "should create new record for model" do
250-
e = TestEmployee.create!(first_name: "Raimonds")
251-
expect(@conn.select_value("SELECT test_employees_seq.currval FROM dual")).to eq(e.id)
252-
end
253-
end
254-
255-
describe "with non-default primary key and non-default sequence name" do
256-
before(:all) do
257-
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
258-
@conn = ActiveRecord::Base.connection
259-
@primary_key = "employee_id"
260-
@sequence_name = "test_employees_s"
261-
create_table_with_trigger(primary_key: @primary_key, sequence_name: @sequence_name)
262-
class ::TestEmployee < ActiveRecord::Base
263-
self.primary_key = "employee_id"
264-
end
265-
end
266-
267-
after(:all) do
268-
drop_table_with_trigger(sequence_name: @sequence_name)
269-
end
270-
271-
it "should populate primary key using trigger" do
272-
expect do
273-
@conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
274-
end.not_to raise_error
275-
end
276-
277-
it "should return new key value using connection insert method" do
278-
insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, @primary_key)
279-
expect(@conn.select_value("SELECT #{@sequence_name}.currval FROM dual")).to eq(insert_id)
280-
end
281-
282-
it "should create new record for model with autogenerated sequence option" do
283-
e = TestEmployee.create!(first_name: "Raimonds")
284-
expect(@conn.select_value("SELECT #{@sequence_name}.currval FROM dual")).to eq(e.id)
285-
end
286-
end
287-
288-
describe "with non-default sequence name and non-default trigger name" do
289-
before(:all) do
290-
ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
291-
@conn = ActiveRecord::Base.connection
292-
@sequence_name = "test_employees_s"
293-
create_table_with_trigger(sequence_name: @sequence_name, trigger_name: "test_employees_t1")
294-
class ::TestEmployee < ActiveRecord::Base
295-
self.sequence_name = :autogenerated
296-
end
297-
end
298-
299-
after(:all) do
300-
drop_table_with_trigger(sequence_name: @sequence_name)
301-
end
302-
303-
it "should populate primary key using trigger" do
304-
expect do
305-
@conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
306-
end.not_to raise_error
307-
end
308-
309-
it "should return new key value using connection insert method" do
310-
insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, "id")
311-
expect(@conn.select_value("SELECT #{@sequence_name}.currval FROM dual")).to eq(insert_id)
312-
end
313-
314-
it "should create new record for model with autogenerated sequence option" do
315-
e = TestEmployee.create!(first_name: "Raimonds")
316-
expect(@conn.select_value("SELECT #{@sequence_name}.currval FROM dual")).to eq(e.id)
317-
end
318-
end
319-
320-
end
321-
322156
describe "table and column comments" do
323157

324158
def create_test_employees_table(table_comment = nil, column_comments = {})
@@ -474,44 +308,6 @@ class ::TestEmployee < ActiveRecord::Base; end
474308

475309
end
476310

477-
describe "create triggers" do
478-
479-
before(:all) do
480-
@conn = ActiveRecord::Base.connection
481-
schema_define do
482-
create_table :test_employees do |t|
483-
t.string :first_name
484-
t.string :last_name
485-
end
486-
end
487-
class ::TestEmployee < ActiveRecord::Base; end
488-
end
489-
490-
after(:all) do
491-
schema_define do
492-
drop_table :test_employees
493-
end
494-
Object.send(:remove_const, "TestEmployee")
495-
ActiveRecord::Base.clear_cache!
496-
end
497-
498-
it "should create table trigger with :new reference" do
499-
expect do
500-
@conn.execute <<-SQL
501-
CREATE OR REPLACE TRIGGER test_employees_pkt
502-
BEFORE INSERT ON test_employees FOR EACH ROW
503-
BEGIN
504-
IF inserting THEN
505-
IF :new.id IS NULL THEN
506-
SELECT test_employees_seq.NEXTVAL INTO :new.id FROM dual;
507-
END IF;
508-
END IF;
509-
END;
510-
SQL
511-
end.not_to raise_error
512-
end
513-
end
514-
515311
describe "add index" do
516312
before(:all) do
517313
@conn = ActiveRecord::Base.connection

0 commit comments

Comments
 (0)