Skip to content

Commit 0f6eb27

Browse files
lhimoclaude
andcommitted
fix: scope SchemaCreation to IBM_DBAdapter to prevent breaking other adapters
The SchemaCreation class was previously defined by reopening the base ActiveRecord::ConnectionAdapters::SchemaCreation class directly. This caused IBM DB2-specific methods (e.g. visit_TableDefinition calling @conn.servertype) to be invoked for ALL database adapters, not just IBM DB2 connections. When an application uses a non-IBM adapter (e.g. PostgreSQL) alongside the ibm_db gem, Rails' pending migration check triggers the overridden visit_TableDefinition, which calls @conn.servertype on a PostgreSQLAdapter instance, raising: NoMethodError: undefined method 'servertype' for an instance of ActiveRecord::ConnectionAdapters::PostgreSQLAdapter This fix moves SchemaCreation to be a proper subclass nested inside IBM_DBAdapter (following the same pattern used by PostgreSQL, MySQL, and SQLite adapters in Rails) and adds a schema_creation method override so the IBM-specific SchemaCreation is only used when the active connection is an IBM DB2 connection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: lhimo <36077138+lhimo@users.noreply.github.com>
1 parent d422e56 commit 0f6eb27

1 file changed

Lines changed: 112 additions & 107 deletions

File tree

IBM_DB_Adapter/ibm_db/lib/active_record/connection_adapters/ibm_db_adapter.rb

Lines changed: 112 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -186,113 +186,6 @@ def unique_constraints_in_create(table, stream)
186186
end
187187
end
188188

189-
class SchemaCreation
190-
private
191-
192-
def visit_TableDefinition(o)
193-
create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
194-
create_sql << 'IF NOT EXISTS ' if o.if_not_exists
195-
create_sql << "#{quote_table_name(o.name)} "
196-
197-
statements = o.columns.map { |c| accept c }
198-
statements << accept(o.primary_keys) if o.primary_keys
199-
200-
if supports_indexes_in_create?
201-
statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
202-
end
203-
204-
statements.concat(o.foreign_keys.map { |fk| accept fk }) if use_foreign_keys?
205-
206-
statements.concat(o.check_constraints.map { |chk| accept chk }) if supports_check_constraints?
207-
208-
@conn.puts_log "visit_TableDefinition #{@conn.servertype}"
209-
if !@conn.servertype.instance_of? IBM_IDS
210-
statements.concat(o.unique_constraints.map { |exc| accept exc }) if supports_unique_constraints?
211-
end
212-
213-
create_sql << "(#{statements.join(', ')})" if statements.present?
214-
add_table_options!(create_sql, o)
215-
create_sql << " AS (#{to_sql(o.as)}) WITH DATA" if o.as
216-
create_sql
217-
end
218-
219-
def visit_ColumnDefinition(o)
220-
if @conn.instance_of? IBM_DBAdapter
221-
@conn.puts_log "visit_ColumnDefinition #{o.name} #{o} #{@conn} #{@conn.servertype}"
222-
end
223-
o.sql_type = type_to_sql(o.type, **o.options)
224-
column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
225-
add_column_options!(column_sql, column_options(o))
226-
column_sql
227-
end
228-
229-
def add_column_options!(sql, options)
230-
if options_include_default?(options)
231-
sql << " DEFAULT #{quote_default_expression(options[:default],
232-
options[:column])}"
233-
end
234-
sql << ' GENERATED BY DEFAULT AS IDENTITY (START WITH 1000)' if options[:auto_increment] == true
235-
sql << ' PRIMARY KEY' if options[:primary_key] == true
236-
# must explicitly check for :null to allow change_column to work on migrations
237-
sql << ' NOT NULL' if options[:null] == false
238-
sql
239-
end
240-
241-
def visit_AlterTable(o)
242-
sql = +"ALTER TABLE #{quote_table_name(o.name)} "
243-
sql << o.adds.map { |col| accept col }.join(" ")
244-
sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
245-
sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
246-
sql << o.check_constraint_adds.map { |con| visit_AddCheckConstraint con }.join(" ")
247-
sql << o.check_constraint_drops.map { |con| visit_DropCheckConstraint con }.join(" ")
248-
sql << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
249-
sql << o.exclusion_constraint_adds.map { |con| visit_AddExclusionConstraint con }.join(" ")
250-
sql << o.exclusion_constraint_drops.map { |con| visit_DropExclusionConstraint con }.join(" ")
251-
sql << o.unique_constraint_adds.map { |con| visit_AddUniqueConstraint con }.join(" ")
252-
sql << o.unique_constraint_drops.map { |con| visit_DropUniqueConstraint con }.join(" ")
253-
end
254-
255-
def visit_ValidateConstraint(name)
256-
"VALIDATE CONSTRAINT #{quote_column_name(name)}"
257-
end
258-
259-
def visit_UniqueConstraintDefinition(o)
260-
column_name = Array(o.column).map { |column| quote_column_name(column) }.join(", ")
261-
262-
sql = ["CONSTRAINT"]
263-
sql << quote_column_name(o.name)
264-
sql << "UNIQUE"
265-
266-
if o.using_index
267-
sql << "USING INDEX #{quote_column_name(o.using_index)}"
268-
else
269-
sql << "(#{column_name})"
270-
end
271-
272-
# if o.deferrable
273-
# sql << "DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}"
274-
# end
275-
276-
sql.join(" ")
277-
end
278-
279-
def visit_AddExclusionConstraint(o)
280-
"ADD #{accept(o)}"
281-
end
282-
283-
def visit_DropExclusionConstraint(name)
284-
"DROP CONSTRAINT #{quote_column_name(name)}"
285-
end
286-
287-
def visit_AddUniqueConstraint(o)
288-
"ADD #{accept(o)}"
289-
end
290-
291-
def visit_DropUniqueConstraint(name)
292-
"DROP CONSTRAINT #{quote_column_name(name)}"
293-
end
294-
295-
end
296189
end
297190

298191
class Base
@@ -659,6 +552,118 @@ class IBM_DBAdapter < AbstractAdapter
659552
:set_quoted_literal_replacement
660553
attr_accessor :sql, :handle_lobs_triggered, :sql_parameter_values
661554

555+
# IBM DB2-specific SchemaCreation subclass.
556+
# This is scoped to IBM_DBAdapter so it does not interfere with other
557+
# adapters (e.g. PostgreSQL, MySQL) that may coexist in the same
558+
# application via Rails' multi-database support.
559+
class SchemaCreation < ConnectionAdapters::SchemaCreation
560+
private
561+
562+
def visit_TableDefinition(o)
563+
create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
564+
create_sql << 'IF NOT EXISTS ' if o.if_not_exists
565+
create_sql << "#{quote_table_name(o.name)} "
566+
567+
statements = o.columns.map { |c| accept c }
568+
statements << accept(o.primary_keys) if o.primary_keys
569+
570+
if supports_indexes_in_create?
571+
statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
572+
end
573+
574+
statements.concat(o.foreign_keys.map { |fk| accept fk }) if use_foreign_keys?
575+
576+
statements.concat(o.check_constraints.map { |chk| accept chk }) if supports_check_constraints?
577+
578+
@conn.puts_log "visit_TableDefinition #{@conn.servertype}"
579+
if !@conn.servertype.instance_of? IBM_IDS
580+
statements.concat(o.unique_constraints.map { |exc| accept exc }) if supports_unique_constraints?
581+
end
582+
583+
create_sql << "(#{statements.join(', ')})" if statements.present?
584+
add_table_options!(create_sql, o)
585+
create_sql << " AS (#{to_sql(o.as)}) WITH DATA" if o.as
586+
create_sql
587+
end
588+
589+
def visit_ColumnDefinition(o)
590+
@conn.puts_log "visit_ColumnDefinition #{o.name} #{o} #{@conn} #{@conn.servertype}"
591+
o.sql_type = type_to_sql(o.type, **o.options)
592+
column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
593+
add_column_options!(column_sql, column_options(o))
594+
column_sql
595+
end
596+
597+
def add_column_options!(sql, options)
598+
if options_include_default?(options)
599+
sql << " DEFAULT #{quote_default_expression(options[:default],
600+
options[:column])}"
601+
end
602+
sql << ' GENERATED BY DEFAULT AS IDENTITY (START WITH 1000)' if options[:auto_increment] == true
603+
sql << ' PRIMARY KEY' if options[:primary_key] == true
604+
# must explicitly check for :null to allow change_column to work on migrations
605+
sql << ' NOT NULL' if options[:null] == false
606+
sql
607+
end
608+
609+
def visit_AlterTable(o)
610+
sql = +"ALTER TABLE #{quote_table_name(o.name)} "
611+
sql << o.adds.map { |col| accept col }.join(" ")
612+
sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
613+
sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
614+
sql << o.check_constraint_adds.map { |con| visit_AddCheckConstraint con }.join(" ")
615+
sql << o.check_constraint_drops.map { |con| visit_DropCheckConstraint con }.join(" ")
616+
sql << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
617+
sql << o.exclusion_constraint_adds.map { |con| visit_AddExclusionConstraint con }.join(" ")
618+
sql << o.exclusion_constraint_drops.map { |con| visit_DropExclusionConstraint con }.join(" ")
619+
sql << o.unique_constraint_adds.map { |con| visit_AddUniqueConstraint con }.join(" ")
620+
sql << o.unique_constraint_drops.map { |con| visit_DropUniqueConstraint con }.join(" ")
621+
end
622+
623+
def visit_ValidateConstraint(name)
624+
"VALIDATE CONSTRAINT #{quote_column_name(name)}"
625+
end
626+
627+
def visit_UniqueConstraintDefinition(o)
628+
column_name = Array(o.column).map { |column| quote_column_name(column) }.join(", ")
629+
630+
sql = ["CONSTRAINT"]
631+
sql << quote_column_name(o.name)
632+
sql << "UNIQUE"
633+
634+
if o.using_index
635+
sql << "USING INDEX #{quote_column_name(o.using_index)}"
636+
else
637+
sql << "(#{column_name})"
638+
end
639+
640+
sql.join(" ")
641+
end
642+
643+
def visit_AddExclusionConstraint(o)
644+
"ADD #{accept(o)}"
645+
end
646+
647+
def visit_DropExclusionConstraint(name)
648+
"DROP CONSTRAINT #{quote_column_name(name)}"
649+
end
650+
651+
def visit_AddUniqueConstraint(o)
652+
"ADD #{accept(o)}"
653+
end
654+
655+
def visit_DropUniqueConstraint(name)
656+
"DROP CONSTRAINT #{quote_column_name(name)}"
657+
end
658+
end
659+
660+
# Returns the IBM DB2-specific SchemaCreation instance, ensuring that
661+
# IBM-specific schema methods are only applied to IBM DB2 connections
662+
# and do not affect other adapters.
663+
def schema_creation # :nodoc:
664+
SchemaCreation.new(self)
665+
end
666+
662667
# Name of the adapter
663668
def adapter_name
664669
'IBM_DB'

0 commit comments

Comments
 (0)