Skip to content

Commit ee09cde

Browse files
authored
Merge pull request #144 from praveen-db2/master
Support for Ruby 3.1 and Rails 7.0
2 parents 77b8688 + 02910ae commit ee09cde

21 files changed

Lines changed: 1040 additions & 1012 deletions

IBM_DB_Adapter/ibm_db/IBM_DB.gemspec

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require 'pathname'
1010
Gem::Specification.new do |spec|
1111
# Required spec
1212
spec.name = 'ibm_db'
13-
spec.version = '5.3.2'
13+
spec.version = '5.4.0'
1414
spec.summary = 'Rails Driver and Adapter for IBM Data Servers: {DB2 on Linux/Unix/Windows, DB2 on zOS, DB2 on i5/OS, Informix (IDS)}'
1515

1616
# Optional spec
@@ -19,8 +19,8 @@ Gem::Specification.new do |spec|
1919
spec.homepage = 'https://github.com/ibmdb/ruby-ibmdb'
2020
spec.required_ruby_version = '>= 2.5.0'
2121
spec.add_dependency('zip')
22-
spec.add_dependency('activerecord', '<6.2')
23-
spec.requirements << 'ActiveRecord, at least 6.1'
22+
spec.add_dependency('activerecord', '<7.1')
23+
spec.requirements << 'ActiveRecord, at least 7.0'
2424

2525
candidates = Dir.glob("**/*")
2626
spec.files = candidates.delete_if do |item|

IBM_DB_Adapter/ibm_db/README

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
=====================================================================
2-
README for the IBM_DB Adapter (5.0.0) and Driver (3.0.5)
2+
README for the IBM_DB Adapter >= 5.0.0 and Driver >= 3.0.5
33
For ActiveRecord Version >= 5.0.7 (and Rails >= 5.0.7)
44
=====================================================================
55

IBM_DB_Adapter/ibm_db/ext/ibm_db.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
+----------------------------------------------------------------------+
1313
*/
1414

15-
#define MODULE_RELEASE "3.0.6"
15+
#define MODULE_RELEASE "3.1.0"
1616

1717
#ifdef HAVE_CONFIG_H
1818
#include "config.h"
@@ -2507,8 +2507,7 @@ static VALUE _ruby_ibm_db_connect_helper( int argc, VALUE *argv, int isPersisten
25072507
}
25082508
/* Call the function where the actual logic is being run*/
25092509
#ifdef UNICODE_SUPPORT_VERSION_H
2510-
ibm_Ruby_Thread_Call ( (void *)_ruby_ibm_db_connect_helper2, helper_args, RUBY_UBF_IO, NULL);
2511-
2510+
_ruby_ibm_db_connect_helper2( helper_args );
25122511

25132512
conn_res = helper_args->conn_res;
25142513

@@ -6988,8 +6987,7 @@ VALUE ibm_db_execute(int argc, VALUE *argv, VALUE self)
69886987
bind_array->error = &error;
69896988

69906989
#ifdef UNICODE_SUPPORT_VERSION_H
6991-
ibm_Ruby_Thread_Call ( (void *)_ruby_ibm_db_execute_helper, bind_array,
6992-
RUBY_UBF_IO, NULL );
6990+
_ruby_ibm_db_execute_helper( bind_array );
69936991
ret_value = bind_array->return_value;
69946992
#else
69956993
ret_value = _ruby_ibm_db_execute_helper( bind_array );
@@ -10078,8 +10076,7 @@ VALUE ibm_db_fetch_row(int argc, VALUE *argv, VALUE self)
1007810076
helper_args->error = &error;
1007910077

1008010078
#ifdef UNICODE_SUPPORT_VERSION_H
10081-
ibm_Ruby_Thread_Call ( (void *)_ruby_ibm_db_fetch_row_helper, helper_args,
10082-
RUBY_UBF_IO, NULL );
10079+
_ruby_ibm_db_fetch_row_helper( helper_args );
1008310080
ret_val = helper_args->return_value;
1008410081
#else
1008510082
ret_val = _ruby_ibm_db_fetch_row_helper( helper_args );
@@ -10247,8 +10244,7 @@ VALUE ibm_db_fetch_assoc(int argc, VALUE *argv, VALUE self) {
1024710244
helper_args->funcType = FETCH_ASSOC;
1024810245

1024910246
#ifdef UNICODE_SUPPORT_VERSION_H
10250-
ibm_Ruby_Thread_Call ( (void *)_ruby_ibm_db_bind_fetch_helper, helper_args,
10251-
RUBY_UBF_IO, NULL );
10247+
_ruby_ibm_db_bind_fetch_helper( helper_args );
1025210248
ret_val = helper_args->return_value;
1025310249

1025410250
#else

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

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ def create_table
5151
module ConnectionAdapters
5252
class SchemaDumper
5353
private
54+
def header(stream)
55+
stream.puts <<~HEADER
56+
ActiveRecord::Schema[#{ActiveRecord::Migration.current_version}].define(#{define_params}) do
57+
HEADER
58+
end
59+
5460
def default_primary_key?(column)
5561
schema_type(column) == :integer
5662
end
@@ -75,11 +81,11 @@ def visit_TableDefinition(o)
7581
end
7682

7783
if supports_foreign_keys?
78-
statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
84+
statements.concat(o.foreign_keys.map { |fk| accept fk })
7985
end
8086

8187
if supports_check_constraints?
82-
statements.concat(o.check_constraints.map { |expression, options| check_constraint_in_create(o.name, expression, options) })
88+
statements.concat(o.check_constraints.map { |chk| accept chk })
8389
end
8490

8591
create_sql << "(#{statements.join(', ')})" if statements.present?
@@ -423,7 +429,7 @@ def internal_string_options_for_primary_key # :nodoc:
423429
end
424430

425431
def drop_table(table_name, options={})
426-
puts_log "drop_table"
432+
puts_log "drop_table - #{table_name}"
427433
if options[:if_exists]
428434
execute("DROP TABLE IF EXISTS #{quote_table_name(table_name)}")
429435
else
@@ -833,7 +839,9 @@ def get_database_version
833839

834840
def prepared_statements?
835841
puts_log "prepared_statements?"
836-
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
842+
prepare = @prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
843+
puts_log "prepare = #{prepare}"
844+
prepare
837845
end
838846
alias :prepared_statements :prepared_statements?
839847

@@ -1112,21 +1120,43 @@ def fetch_data(stmt)
11121120
end
11131121
end
11141122

1115-
def select(sql, name = nil, binds = [])
1123+
def select(sql, name = nil, binds = [], prepare: false, async: false)
11161124
puts_log "select #{sql}"
1125+
puts_log "prepare = #{prepare}"
1126+
11171127
# Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"
11181128
begin
11191129
sql.gsub( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" )
11201130
rescue
11211131
# ...
11221132
end
11231133

1134+
if async && async_enabled?
1135+
if current_transaction.joinable?
1136+
raise AsynchronousQueryInsideTransactionError, "Asynchronous queries are not allowed inside transactions"
1137+
end
1138+
1139+
future_result = async.new(
1140+
pool,
1141+
sql,
1142+
name,
1143+
binds,
1144+
prepare: prepare,
1145+
)
1146+
if supports_concurrent_connections? && current_transaction.closed?
1147+
future_result.schedule!(ActiveRecord::Base.asynchronous_queries_session)
1148+
else
1149+
future_result.execute!(self)
1150+
end
1151+
return future_result
1152+
end
1153+
11241154
results = []
11251155

11261156
if(binds.nil? || binds.empty?)
11271157
stmt = execute(sql, name)
11281158
else
1129-
stmt = exec_query_ret_stmt(sql, name, binds, prepare = false)
1159+
stmt = exec_query_ret_stmt(sql, name, binds, prepare)
11301160
end
11311161

11321162
cols = IBM_DB.resultCols(stmt)
@@ -1452,7 +1482,8 @@ def explain(arel, binds = [])
14521482
# the executed +sql+ statement.
14531483
# Here prepare argument is not used, by default this method creates prepared statment and execute.
14541484
def exec_query_ret_stmt(sql, name = 'SQL', binds = [], prepare = false)
1455-
puts_log "exec_query_ret_stmt"
1485+
puts_log "exec_query_ret_stmt #{sql}"
1486+
sql = transform_query(sql)
14561487
check_if_write_query(sql)
14571488
materialize_transactions
14581489
mark_transaction_written_if_write(sql)
@@ -1461,6 +1492,8 @@ def exec_query_ret_stmt(sql, name = 'SQL', binds = [], prepare = false)
14611492
puts_log "Binds = #{binds}"
14621493
param_array = type_casted_binds(binds)
14631494
puts_log "Param array = #{param_array}"
1495+
puts_log "Prepare flag = #{prepare}"
1496+
puts_log "#{caller}"
14641497

14651498
stmt = @servertype.prepare(sql, name)
14661499
if prepare
@@ -1526,6 +1559,7 @@ def execute(sql, name=nil)
15261559
#sql='INSERT INTO ar_internal_metadata (key, value, created_at, updated_at) VALUES ('10', '10', '10', '10')
15271560
puts_log "execute"
15281561
puts_log "#{sql}"
1562+
sql = transform_query(sql)
15291563
check_if_write_query(sql)
15301564
materialize_transactions
15311565
mark_transaction_written_if_write(sql)
@@ -1725,6 +1759,9 @@ def unquoted_false
17251759

17261760
def quote_table_name(name)
17271761
puts_log "quote_table_name"
1762+
if name.start_with?'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
1763+
name = "\"#{name}\""
1764+
end
17281765
name
17291766
#@servertype.check_reserved_words(name).gsub('"', '').gsub("'",'')
17301767
end
@@ -2378,7 +2415,9 @@ def columns(table_name)
23782415
column_nullable = (col["nullable"] == 1) ? true : false
23792416
# Make sure the hidden column (db2_generated_rowid_for_lobs) in DB2 z/OS isn't added to the list
23802417
if !(column_name.match(/db2_generated_rowid_for_lobs/i))
2418+
puts_log "Column type = #{column_type}"
23812419
ruby_type = simplified_type(column_type)
2420+
puts_log "Ruby type after = #{ruby_type}"
23822421
precision = extract_precision(ruby_type)
23832422

23842423
if column_type.match(/timestamp|integer|bigint|date|time|blob/i)
@@ -2429,7 +2468,7 @@ def columns(table_name)
24292468
else
24302469
error_msg = "An unexpected error occurred during retrieval of column metadata"
24312470
error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
2432-
raise error_msg
2471+
# raise error_msg
24332472
end
24342473
ensure # Free resources associated with the statement
24352474
IBM_DB.free_stmt(stmt) if stmt
@@ -2447,6 +2486,10 @@ def columns(table_name)
24472486
return columns
24482487
end
24492488

2489+
def extract_precision(sql_type)
2490+
$1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
2491+
end
2492+
24502493
def extract_default_function(default_value, default)
24512494
default if has_default_function?(default_value, default)
24522495
end
@@ -2491,7 +2534,7 @@ def foreign_keys(table_name)
24912534
else
24922535
error_msg = "An unexpected error occurred during retrieval of foreign key metadata"
24932536
error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
2494-
raise error_msg
2537+
# raise error_msg
24952538
end
24962539
ensure # Free resources associated with the statement
24972540
IBM_DB.free_stmt(stmt) if stmt
@@ -2578,6 +2621,7 @@ def add_column(table_name, column_name, type, **options) # :nodoc:
25782621
puts_log "add_column"
25792622
clear_cache!
25802623
puts_log "add_column info #{table_name}, #{column_name}, #{type}, #{options}"
2624+
puts_log caller
25812625
if (!type.nil? && type.to_s == "primary_key") or (options.key?(:primary_key) and options[:primary_key] == true)
25822626
if !type.nil? and type.to_s != "primary_key"
25832627
execute "ALTER TABLE #{table_name} ADD COLUMN #{column_name} #{type} NOT NULL DEFAULT 0"
@@ -2685,6 +2729,15 @@ def table_options(table_name) # :nodoc:
26852729
end
26862730
end
26872731

2732+
def remove_columns(table_name, *column_names, type: nil, **options)
2733+
if column_names.empty?
2734+
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
2735+
end
2736+
2737+
remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
2738+
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(' ')}"
2739+
end
2740+
26882741
# Renames a table.
26892742
# ==== Example
26902743
# rename_table('octopuses', 'octopi')
@@ -2782,7 +2835,7 @@ def add_foreign_keyList(fkey_list, table_name, column_name, new_column_name)
27822835
puts_log "add_foreign_keyList = #{table_name}, #{column_name}, #{fkey_list}"
27832836
fkey_list.each do |fkey|
27842837
if fkey.options[:column] == column_name
2785-
add_foreign_key(table_name, fkey.to_table, column: new_column_name)
2838+
add_foreign_key(table_name, strip_table_name_prefix_and_suffix(fkey.to_table), column: new_column_name)
27862839
end
27872840
end
27882841
end
@@ -2917,6 +2970,7 @@ def initialize_type_map(m = type_map) # :nodoc:
29172970

29182971
class SchemaDumper < ConnectionAdapters::SchemaDumper
29192972
def dump(stream) # Like in abstract class, we no need to call header() & trailer().
2973+
header(stream)
29202974
extensions(stream)
29212975
tables(stream)
29222976
stream
@@ -3724,7 +3778,7 @@ def visit_Arel_Nodes_ValuesList(o, collector)
37243778
row.each_with_index do |value, k|
37253779
collector << ", " unless k == 0
37263780
case value
3727-
when Nodes::SqlLiteral, Nodes::BindParam
3781+
when Nodes::SqlLiteral, Nodes::BindParam, ActiveModel::Attribute
37283782
collector = visit(value, collector)
37293783
#collector << quote(value).to_s
37303784
else

0 commit comments

Comments
 (0)