ibm_db 5.5.1 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES +3 -0
- data/README +1 -0
- data/ext/Makefile +29 -30
- data/ext/ibm_db.c +14 -28
- data/ext/ibm_db.o +0 -0
- data/ext/ibm_db.so +0 -0
- data/ext/mkmf.log +15 -11
- data/ext/ruby_ibm_db_cli.o +0 -0
- data/lib/active_record/connection_adapters/ibm_db_adapter.rb +478 -407
- metadata +10 -11
- data/ext/ibm_db.bundle +0 -0
- data/lib/ibm_db.so +0 -1
@@ -1,7 +1,7 @@
|
|
1
1
|
# +----------------------------------------------------------------------+
|
2
2
|
# | Licensed Materials - Property of IBM |
|
3
3
|
# | |
|
4
|
-
# | (C) Copyright IBM Corporation 2006 -
|
4
|
+
# | (C) Copyright IBM Corporation 2006 - 2025 |
|
5
5
|
# +----------------------------------------------------------------------+
|
6
6
|
# | Authors: Antonio Cangiano <cangiano@ca.ibm.com> |
|
7
7
|
# | : Mario Ds Briggs <mario.briggs@in.ibm.com> |
|
@@ -17,6 +17,76 @@ require 'active_record/connection_adapters/sql_type_metadata'
|
|
17
17
|
require 'active_record/connection_adapters/statement_pool'
|
18
18
|
require 'active_record/connection_adapters'
|
19
19
|
|
20
|
+
# Ensure ActiveRecord and Rails Generators are loaded
|
21
|
+
require "active_record"
|
22
|
+
ActiveRecord::ConnectionAdapters.register(
|
23
|
+
"ibm_db",
|
24
|
+
"ActiveRecord::ConnectionAdapters::IBM_DBAdapter",
|
25
|
+
"active_record/connection_adapters/ibm_db_adapter"
|
26
|
+
)
|
27
|
+
require "rails/generators/database"
|
28
|
+
|
29
|
+
module Rails
|
30
|
+
module Generators
|
31
|
+
class Database
|
32
|
+
DATABASES << "ibm_db" unless DATABASES.include?("ibm_db")
|
33
|
+
|
34
|
+
class << self
|
35
|
+
alias_method :original_build, :build
|
36
|
+
|
37
|
+
def build(database_name)
|
38
|
+
return IBMDB.new if database_name == "ibm_db"
|
39
|
+
original_build(database_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
alias_method :original_all, :all
|
43
|
+
|
44
|
+
def all
|
45
|
+
original_all + [IBMDB.new]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class IBMDB < Database
|
51
|
+
def name
|
52
|
+
"ibm_db"
|
53
|
+
end
|
54
|
+
|
55
|
+
def service
|
56
|
+
{
|
57
|
+
"image" => "ibm_db:latest",
|
58
|
+
"restart" => "unless-stopped",
|
59
|
+
"networks" => ["default"],
|
60
|
+
"volumes" => ["ibm-db-data:/var/lib/ibmdb"],
|
61
|
+
"environment" => {
|
62
|
+
"IBM_DB_ALLOW_EMPTY_PASSWORD" => "true",
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def port
|
68
|
+
nil # Default DB2 port
|
69
|
+
end
|
70
|
+
|
71
|
+
def gem
|
72
|
+
["ibm_db", [">= 5.5"]]
|
73
|
+
end
|
74
|
+
|
75
|
+
def base_package
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def build_package
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def feature_name
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
20
90
|
module CallChain
|
21
91
|
def self.caller_method(depth = 1)
|
22
92
|
parse_caller(caller(depth + 1).first).last
|
@@ -48,7 +118,7 @@ module ActiveRecord
|
|
48
118
|
|
49
119
|
module Persistence
|
50
120
|
module ClassMethods
|
51
|
-
def _insert_record(values, returning) # :nodoc:
|
121
|
+
def _insert_record(connection, values, returning) # :nodoc:
|
52
122
|
primary_key = self.primary_key
|
53
123
|
primary_key_value = nil
|
54
124
|
|
@@ -61,16 +131,18 @@ module ActiveRecord
|
|
61
131
|
|
62
132
|
im = Arel::InsertManager.new(arel_table)
|
63
133
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
134
|
+
with_connection do |c|
|
135
|
+
if values.empty?
|
136
|
+
im.insert(connection.empty_insert_statement_value(primary_key, arel_table[name].relation.name))
|
137
|
+
else
|
138
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
139
|
+
end
|
69
140
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
141
|
+
connection.insert(
|
142
|
+
im, "#{self} Create", primary_key || false, primary_key_value,
|
143
|
+
returning: returning
|
144
|
+
)
|
145
|
+
end
|
74
146
|
end
|
75
147
|
end
|
76
148
|
end
|
@@ -145,6 +217,9 @@ module ActiveRecord
|
|
145
217
|
end
|
146
218
|
|
147
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
|
148
223
|
o.sql_type = type_to_sql(o.type, **o.options)
|
149
224
|
column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
|
150
225
|
add_column_options!(column_sql, column_options(o))
|
@@ -220,41 +295,6 @@ module ActiveRecord
|
|
220
295
|
end
|
221
296
|
end
|
222
297
|
|
223
|
-
class Relation
|
224
|
-
def insert(values)
|
225
|
-
primary_key_value = nil
|
226
|
-
|
227
|
-
if primary_key && values.is_a?(Hash)
|
228
|
-
primary_key_value = values[values.keys.find do |k|
|
229
|
-
k.name == primary_key
|
230
|
-
end]
|
231
|
-
|
232
|
-
if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
|
233
|
-
primary_key_value = connection.next_sequence_value(klass.sequence_name)
|
234
|
-
values[klass.arel_table[klass.primary_key]] = primary_key_value
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
im = arel.create_insert
|
239
|
-
im.into @table
|
240
|
-
|
241
|
-
conn = @klass.connection
|
242
|
-
substitutes = values.sort_by { |arel_attr, _| arel_attr.name }
|
243
|
-
binds = substitutes.map do |arel_attr, value|
|
244
|
-
[@klass.columns_hash[arel_attr.name], value]
|
245
|
-
end
|
246
|
-
|
247
|
-
substitutes, binds = substitute_values values
|
248
|
-
if values.empty? # empty insert
|
249
|
-
im.values = Arel.sql(connection.empty_insert_statement_value(klass.primary_key, klass.table_name))
|
250
|
-
else
|
251
|
-
im.insert substitutes
|
252
|
-
end
|
253
|
-
|
254
|
-
conn.insert(im, 'SQL', primary_key, primary_key_value, nil, binds)
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
298
|
class Base
|
259
299
|
# Method required to handle LOBs and XML fields.
|
260
300
|
# An after save callback checks if a marker has been inserted through
|
@@ -262,87 +302,91 @@ module ActiveRecord
|
|
262
302
|
# the actual large object through a prepared statement (param binding).
|
263
303
|
after_save :handle_lobs
|
264
304
|
def handle_lobs
|
265
|
-
return unless self.class.
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
305
|
+
# return unless self.class.with_connection.is_a?(ConnectionAdapters::IBM_DBAdapter)
|
306
|
+
|
307
|
+
self.class.with_connection do |conn|
|
308
|
+
if conn.is_a?(ConnectionAdapters::IBM_DBAdapter)
|
309
|
+
# Checks that the insert or update had at least a BLOB, CLOB or XML field
|
310
|
+
conn.sql.each do |clob_sql|
|
311
|
+
next unless clob_sql =~ /BLOB\('(.*)'\)/i ||
|
312
|
+
clob_sql =~ /@@@IBMTEXT@@@/i ||
|
313
|
+
clob_sql =~ /@@@IBMXML@@@/i ||
|
314
|
+
clob_sql =~ /@@@IBMBINARY@@@/i
|
315
|
+
|
316
|
+
update_query = "UPDATE #{self.class.table_name} SET ("
|
317
|
+
counter = 0
|
318
|
+
values = []
|
319
|
+
params = []
|
320
|
+
# Selects only binary, text and xml columns
|
321
|
+
self.class.columns.select { |col| col.sql_type.to_s =~ /blob|binary|clob|text|xml/i }.each do |col|
|
322
|
+
update_query << if counter.zero?
|
323
|
+
"#{col.name}".to_s
|
324
|
+
else
|
325
|
+
",#{col.name}".to_s
|
326
|
+
end
|
327
|
+
|
328
|
+
# Add a '?' for the parameter or a NULL if the value is nil or empty
|
329
|
+
# (except for a CLOB field where '' can be a value)
|
330
|
+
if self[col.name].nil? ||
|
331
|
+
self[col.name] == {} ||
|
332
|
+
self[col.name] == [] ||
|
333
|
+
(self[col.name] == '' && !(col.sql_type.to_s =~ /text|clob/i))
|
334
|
+
params << 'NULL'
|
335
|
+
else
|
336
|
+
values << if col.cast_type.is_a?(::ActiveRecord::Type::Serialized)
|
337
|
+
YAML.dump(self[col.name])
|
282
338
|
else
|
283
|
-
|
339
|
+
self[col.name]
|
284
340
|
end
|
341
|
+
params << '?'
|
342
|
+
end
|
343
|
+
counter += 1
|
344
|
+
end
|
285
345
|
|
286
|
-
|
287
|
-
|
288
|
-
if self[col.name].nil? ||
|
289
|
-
self[col.name] == {} ||
|
290
|
-
self[col.name] == [] ||
|
291
|
-
(self[col.name] == '' && !(col.sql_type.to_s =~ /text|clob/i))
|
292
|
-
params << 'NULL'
|
293
|
-
else
|
294
|
-
values << if col.cast_type.is_a?(::ActiveRecord::Type::Serialized)
|
295
|
-
YAML.dump(self[col.name])
|
296
|
-
else
|
297
|
-
self[col.name]
|
298
|
-
end
|
299
|
-
params << '?'
|
300
|
-
end
|
301
|
-
counter += 1
|
302
|
-
end
|
303
|
-
|
304
|
-
# no subsequent update is required if no relevant columns are found
|
305
|
-
next if counter.zero?
|
346
|
+
# no subsequent update is required if no relevant columns are found
|
347
|
+
next if counter.zero?
|
306
348
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
349
|
+
update_query << ') = '
|
350
|
+
# IBM_DB accepts 'SET (column) = NULL' but not (NULL),
|
351
|
+
# therefore the sql needs to be changed for a single NULL field.
|
352
|
+
update_query << if params.size == 1 && params[0] == 'NULL'
|
353
|
+
'NULL'
|
354
|
+
else
|
355
|
+
'(' + params.join(',') + ')'
|
356
|
+
end
|
315
357
|
|
316
|
-
|
317
|
-
|
358
|
+
update_query << " WHERE #{self.class.primary_key} = ?"
|
359
|
+
values << self[self.class.primary_key.downcase]
|
318
360
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
361
|
+
begin
|
362
|
+
unless (stmt = IBM_DB.prepare(conn.connection, update_query))
|
363
|
+
error_msg = IBM_DB.getErrormsg(conn.connection, IBM_DB::DB_CONN)
|
364
|
+
if error_msg && !error_msg.empty?
|
365
|
+
raise "Statement prepare for updating LOB/XML column failed : #{error_msg}"
|
366
|
+
end
|
367
|
+
raise StandardError.new('An unexpected error occurred during update of LOB/XML column')
|
368
|
+
end
|
327
369
|
|
328
|
-
|
370
|
+
conn.log_query(update_query, 'update of LOB/XML field(s)in handle_lobs')
|
329
371
|
|
330
|
-
|
331
|
-
|
332
|
-
|
372
|
+
# rollback any failed LOB/XML field updates (and remove associated marker)
|
373
|
+
unless IBM_DB.execute(stmt, values)
|
374
|
+
error_msg = "Failed to insert/update LOB/XML field(s) due to: #{IBM_DB.getErrormsg(stmt,
|
333
375
|
IBM_DB::DB_STMT)}"
|
334
|
-
|
335
|
-
|
376
|
+
conn.execute('ROLLBACK')
|
377
|
+
raise error_msg
|
378
|
+
end
|
379
|
+
rescue StandardError => e
|
380
|
+
raise e
|
381
|
+
ensure
|
382
|
+
IBM_DB.free_stmt(stmt) if stmt
|
383
|
+
end
|
384
|
+
# if clob_sql
|
385
|
+
# connection.sql.each
|
336
386
|
end
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
IBM_DB.free_stmt(stmt) if stmt
|
341
|
-
end
|
342
|
-
# if clob_sql
|
343
|
-
# connection.sql.each
|
344
|
-
end
|
345
|
-
self.class.connection.handle_lobs_triggered = true
|
387
|
+
conn.handle_lobs_triggered = true
|
388
|
+
end # if conn.is_a?
|
389
|
+
end # with_connection
|
346
390
|
# if connection.kind_of?
|
347
391
|
# handle_lobs
|
348
392
|
end
|
@@ -387,10 +431,6 @@ module ActiveRecord
|
|
387
431
|
username = config[:username].to_s
|
388
432
|
password = config[:password].to_s
|
389
433
|
|
390
|
-
if config.has_key?(:dbops) && config[:dbops] == true
|
391
|
-
return ConnectionAdapters::IBM_DBAdapter.new(nil, isAr3, logger, config, {})
|
392
|
-
end
|
393
|
-
|
394
434
|
# Retrieves the database alias (local catalog name) or remote name
|
395
435
|
# (for remote TCP/IP connections) from the +config+ hash
|
396
436
|
# or raises ArgumentError in case of failure.
|
@@ -453,17 +493,13 @@ module ActiveRecord
|
|
453
493
|
# No host implies a local catalog-based connection: +database+ represents catalog alias
|
454
494
|
connection = IBM_DB.connect(database, username, password, conn_options, set_quoted_literal_replacement)
|
455
495
|
end
|
496
|
+
return connection, isAr3, config, conn_options
|
456
497
|
rescue StandardError => e
|
457
498
|
raise "Failed to connect to [#{database}] due to: #{e}"
|
458
499
|
end
|
459
500
|
# Verifies that the connection was successful
|
460
501
|
raise "An unexpected error occured during connect attempt to [#{database}]" unless connection
|
461
502
|
|
462
|
-
# Creates an instance of *IBM_DBAdapter* based on the +connection+
|
463
|
-
# and credentials provided in +config+
|
464
|
-
ConnectionAdapters::IBM_DBAdapter.new(connection, isAr3, logger, config, conn_options)
|
465
|
-
|
466
|
-
# If the connection failure was not caught previoulsy, it raises a Runtime error
|
467
503
|
# method self.ibm_db_connection
|
468
504
|
end
|
469
505
|
|
@@ -475,37 +511,26 @@ module ActiveRecord
|
|
475
511
|
end
|
476
512
|
|
477
513
|
module ConnectionAdapters
|
478
|
-
class Column
|
479
|
-
def self.binary_to_string(value)
|
480
|
-
puts_log 'binary_to_string'
|
481
|
-
# Returns a string removing the eventual BLOB scalar function
|
482
|
-
value.to_s.gsub(/"SYSIBM"."BLOB"\('(.*)'\)/i, '\1')
|
483
|
-
end
|
484
|
-
|
485
|
-
# whether the column is auto-populated by the database using a sequence
|
486
|
-
def auto_incremented_by_db?
|
487
|
-
true
|
488
|
-
end
|
489
|
-
|
490
|
-
def auto_increment?
|
491
|
-
true
|
492
|
-
end
|
493
|
-
alias_method :auto_incremented_by_db?, :auto_increment?
|
494
|
-
end
|
495
|
-
|
496
514
|
module Quoting
|
497
515
|
def lookup_cast_type_from_column(column) # :nodoc:
|
498
516
|
lookup_cast_type(column.sql_type_metadata.sql_type)
|
499
517
|
end
|
500
|
-
end
|
501
518
|
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
519
|
+
module ClassMethods
|
520
|
+
def quote_table_name(name)
|
521
|
+
if name.start_with? '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
|
522
|
+
name = "\"#{name}\""
|
523
|
+
else
|
524
|
+
name = name.to_s
|
525
|
+
end
|
526
|
+
name
|
527
|
+
# @servertype.check_reserved_words(name).gsub('"', '').gsub("'",'')
|
528
|
+
end
|
529
|
+
|
530
|
+
def quote_column_name(name)
|
531
|
+
name = name.to_s
|
532
|
+
name.gsub('"', '').gsub("'", '')
|
533
|
+
end
|
509
534
|
end
|
510
535
|
end
|
511
536
|
|
@@ -599,209 +624,6 @@ module ActiveRecord
|
|
599
624
|
# class IBM_DBColumn
|
600
625
|
end
|
601
626
|
|
602
|
-
module ColumnMethods
|
603
|
-
def primary_key(name, type = :primary_key, **options)
|
604
|
-
puts_log '16'
|
605
|
-
column(name, type, options.merge(primary_key: true))
|
606
|
-
end
|
607
|
-
|
608
|
-
# #class Table
|
609
|
-
class Table < ActiveRecord::ConnectionAdapters::Table
|
610
|
-
include ColumnMethods
|
611
|
-
|
612
|
-
# Method to parse the passed arguments and create the ColumnDefinition object of the specified type
|
613
|
-
def ibm_parse_column_attributes_args(type, *args)
|
614
|
-
puts_log 'ibm_parse_column_attributes_args'
|
615
|
-
options = {}
|
616
|
-
options = args.delete_at(args.length - 1) if args.last.is_a?(Hash)
|
617
|
-
args.each do |name|
|
618
|
-
column name, type.to_sym, options
|
619
|
-
# end args.each
|
620
|
-
end
|
621
|
-
end
|
622
|
-
private :ibm_parse_column_attributes_args
|
623
|
-
|
624
|
-
# Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type xml
|
625
|
-
# This method is different as compared to def char (sql is being issued explicitly
|
626
|
-
# as compared to def char where method column(which will generate the sql is being called)
|
627
|
-
# in order to handle the DEFAULT and NULL option for the native XML datatype
|
628
|
-
def xml(*args)
|
629
|
-
puts_log '18'
|
630
|
-
args.delete_at(args.length - 1) if args.last.is_a?(Hash)
|
631
|
-
sql_segment = "ALTER TABLE #{@base.quote_table_name(@table_name)} ADD COLUMN "
|
632
|
-
args.each do |name|
|
633
|
-
sql = sql_segment + " #{@base.quote_column_name(name)} xml"
|
634
|
-
@base.execute(sql, 'add_xml_column')
|
635
|
-
end
|
636
|
-
self
|
637
|
-
end
|
638
|
-
|
639
|
-
# Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type double
|
640
|
-
def double(*args)
|
641
|
-
puts_log '19'
|
642
|
-
ibm_parse_column_attributes_args('double', *args)
|
643
|
-
self
|
644
|
-
end
|
645
|
-
|
646
|
-
# Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type decfloat
|
647
|
-
def decfloat(*args)
|
648
|
-
puts_log '20'
|
649
|
-
ibm_parse_column_attributes_args('decfloat', *args)
|
650
|
-
self
|
651
|
-
end
|
652
|
-
|
653
|
-
def graphic(*args)
|
654
|
-
puts_log '21'
|
655
|
-
ibm_parse_column_attributes_args('graphic', *args)
|
656
|
-
self
|
657
|
-
end
|
658
|
-
|
659
|
-
def vargraphic(*args)
|
660
|
-
puts_log '22'
|
661
|
-
ibm_parse_column_attributes_args('vargraphic', *args)
|
662
|
-
self
|
663
|
-
end
|
664
|
-
|
665
|
-
def bigint(*args)
|
666
|
-
puts_log '23'
|
667
|
-
ibm_parse_column_attributes_args('bigint', *args)
|
668
|
-
self
|
669
|
-
end
|
670
|
-
|
671
|
-
# Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type char [character]
|
672
|
-
def char(*args)
|
673
|
-
puts_log '24'
|
674
|
-
ibm_parse_column_attributes_args('char', *args)
|
675
|
-
self
|
676
|
-
end
|
677
|
-
alias character char
|
678
|
-
# end of class Table
|
679
|
-
end
|
680
|
-
|
681
|
-
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
682
|
-
include ColumnMethods
|
683
|
-
|
684
|
-
def native
|
685
|
-
puts_log '25'
|
686
|
-
@base.native_database_types
|
687
|
-
end
|
688
|
-
|
689
|
-
# Method to parse the passed arguments and create the ColumnDefinition object of the specified type
|
690
|
-
def ibm_parse_column_attributes_args(type, *args)
|
691
|
-
puts_log '26'
|
692
|
-
options = {}
|
693
|
-
options = args.delete_at(args.length - 1) if args.last.is_a?(Hash)
|
694
|
-
args.each do |name|
|
695
|
-
column(name, type, options)
|
696
|
-
end
|
697
|
-
end
|
698
|
-
private :ibm_parse_column_attributes_args
|
699
|
-
|
700
|
-
# Method to support the new syntax of rails 2.0 migrations for columns of type xml
|
701
|
-
def xml(*args)
|
702
|
-
puts_log '27'
|
703
|
-
ibm_parse_column_attributes_args('xml', *args)
|
704
|
-
self
|
705
|
-
end
|
706
|
-
|
707
|
-
# Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type double
|
708
|
-
def double(*args)
|
709
|
-
puts_log '28'
|
710
|
-
ibm_parse_column_attributes_args('double', *args)
|
711
|
-
self
|
712
|
-
end
|
713
|
-
|
714
|
-
# Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type decfloat
|
715
|
-
def decfloat(*args)
|
716
|
-
puts_log '29'
|
717
|
-
ibm_parse_column_attributes_args('decfloat', *args)
|
718
|
-
self
|
719
|
-
end
|
720
|
-
|
721
|
-
def graphic(*args)
|
722
|
-
puts_log '30'
|
723
|
-
ibm_parse_column_attributes_args('graphic', *args)
|
724
|
-
self
|
725
|
-
end
|
726
|
-
|
727
|
-
def vargraphic(*args)
|
728
|
-
puts_log '31'
|
729
|
-
ibm_parse_column_attributes_args('vargraphic', *args)
|
730
|
-
self
|
731
|
-
end
|
732
|
-
|
733
|
-
def bigint(*args)
|
734
|
-
puts_log '32'
|
735
|
-
ibm_parse_column_attributes_args('bigint', *args)
|
736
|
-
self
|
737
|
-
end
|
738
|
-
|
739
|
-
# Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type char [character]
|
740
|
-
def char(*args)
|
741
|
-
puts_log '33'
|
742
|
-
ibm_parse_column_attributes_args('char', *args)
|
743
|
-
self
|
744
|
-
end
|
745
|
-
alias character char
|
746
|
-
|
747
|
-
# Overrides the abstract adapter in order to handle
|
748
|
-
# the DEFAULT option for the native XML datatype
|
749
|
-
def column(name, type, index: nil, **options)
|
750
|
-
puts_log '34 column'
|
751
|
-
name = name.to_s
|
752
|
-
type = type.to_sym if type
|
753
|
-
|
754
|
-
if @columns_hash[name]
|
755
|
-
unless @columns_hash[name].primary_key?
|
756
|
-
raise ArgumentError, "you can't define an already defined column '#{name}'."
|
757
|
-
end
|
758
|
-
|
759
|
-
raise ArgumentError,
|
760
|
-
"you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
|
761
|
-
|
762
|
-
end
|
763
|
-
|
764
|
-
# construct a column definition where @base is adaptor instance
|
765
|
-
column = new_column_definition(name, type, **options)
|
766
|
-
|
767
|
-
# DB2 does not accept DEFAULT NULL option for XML
|
768
|
-
# for table create, but does accept nullable option
|
769
|
-
if type.to_s == 'xml'
|
770
|
-
column.null = options[:null]
|
771
|
-
# Override column object's (instance of ColumnDefinition structure)
|
772
|
-
# to_s which is expected to return the create_table SQL fragment
|
773
|
-
# and bypass DEFAULT NULL option while still appending NOT NULL
|
774
|
-
def column.to_s
|
775
|
-
sql = "#{base.quote_column_name(name)} #{type}"
|
776
|
-
sql << ' NOT NULL' if !null.nil? && (null == false)
|
777
|
-
sql
|
778
|
-
end
|
779
|
-
else
|
780
|
-
column.null = options[:null]
|
781
|
-
column.default = options[:default]
|
782
|
-
end
|
783
|
-
|
784
|
-
column.scale = options[:scale] if options[:scale]
|
785
|
-
column.precision = options[:precision] if options[:precision]
|
786
|
-
# append column's limit option and yield native limits
|
787
|
-
if options[:limit]
|
788
|
-
column.limit = options[:limit]
|
789
|
-
elsif @base.native_database_types[type.to_sym]
|
790
|
-
if @base.native_database_types[type.to_sym].has_key? :limit
|
791
|
-
column.limit = @base.native_database_types[type.to_sym][:limit]
|
792
|
-
end
|
793
|
-
end
|
794
|
-
|
795
|
-
@columns << column unless @columns.nil? or @columns.include? column
|
796
|
-
|
797
|
-
@columns_hash[name] = column
|
798
|
-
|
799
|
-
self
|
800
|
-
end
|
801
|
-
|
802
|
-
end # end of class TableDefinition
|
803
|
-
end # end of module ColumnMethods
|
804
|
-
|
805
627
|
# The IBM_DB Adapter requires the native Ruby driver (ibm_db)
|
806
628
|
# for IBM data servers (ibm_db.so).
|
807
629
|
# +config+ the hash passed as an initializer argument content:
|
@@ -842,6 +664,42 @@ module ActiveRecord
|
|
842
664
|
'IBM_DB'
|
843
665
|
end
|
844
666
|
|
667
|
+
include Savepoints
|
668
|
+
|
669
|
+
def create_savepoint(name = current_savepoint_name)
|
670
|
+
puts_log 'create_savepoint'
|
671
|
+
# Turns off auto-committing
|
672
|
+
auto_commit_off
|
673
|
+
# Create savepoint
|
674
|
+
internal_execute("SAVEPOINT #{name} ON ROLLBACK RETAIN CURSORS", 'TRANSACTION')
|
675
|
+
end
|
676
|
+
|
677
|
+
class Column < ActiveRecord::ConnectionAdapters::Column
|
678
|
+
attr_reader :rowid
|
679
|
+
|
680
|
+
def initialize(*, auto_increment: nil, rowid: false, generated_type: nil, **)
|
681
|
+
super
|
682
|
+
@auto_increment = auto_increment
|
683
|
+
@rowid = rowid
|
684
|
+
@generated_type = generated_type
|
685
|
+
end
|
686
|
+
|
687
|
+
def self.binary_to_string(value)
|
688
|
+
# Returns a string removing the eventual BLOB scalar function
|
689
|
+
value.to_s.gsub(/"SYSIBM"."BLOB"\('(.*)'\)/i, '\1')
|
690
|
+
end
|
691
|
+
|
692
|
+
# whether the column is auto-populated by the database using a sequence
|
693
|
+
def auto_increment?
|
694
|
+
@auto_increment
|
695
|
+
end
|
696
|
+
|
697
|
+
def auto_incremented_by_db?
|
698
|
+
auto_increment? || rowid
|
699
|
+
end
|
700
|
+
alias_method :auto_incremented_by_db?, :auto_increment?
|
701
|
+
end
|
702
|
+
|
845
703
|
class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
|
846
704
|
attr_reader :constraint_validations, :exclusion_constraint_adds, :exclusion_constraint_drops, :unique_constraint_adds, :unique_constraint_drops
|
847
705
|
def initialize(td)
|
@@ -889,6 +747,11 @@ module ActiveRecord
|
|
889
747
|
options = @conn.unique_constraint_options(name, column_name, options)
|
890
748
|
UniqueConstraintDefinition.new(name, column_name, options)
|
891
749
|
end
|
750
|
+
|
751
|
+
def references(*args, **options)
|
752
|
+
super(*args, type: :integer, **options)
|
753
|
+
end
|
754
|
+
alias :belongs_to :references
|
892
755
|
end # end of class TableDefinition
|
893
756
|
|
894
757
|
UniqueConstraintDefinition = Struct.new(:table_name, :column, :options) do
|
@@ -927,8 +790,9 @@ module ActiveRecord
|
|
927
790
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
928
791
|
end
|
929
792
|
|
930
|
-
def initialize(
|
793
|
+
def initialize(args)
|
931
794
|
# Caching database connection configuration (+connect+ or +reconnect+ support)\
|
795
|
+
connection, ar3, config, conn_options = ActiveRecord::Base.ibm_db_connection(args)
|
932
796
|
@config = config
|
933
797
|
@connection = connection
|
934
798
|
@isAr3 = ar3
|
@@ -963,8 +827,7 @@ module ActiveRecord
|
|
963
827
|
@handle_lobs_triggered = false
|
964
828
|
|
965
829
|
# Calls the parent class +ConnectionAdapters+' initializer
|
966
|
-
|
967
|
-
super(@connection, logger, @config)
|
830
|
+
super(@config)
|
968
831
|
|
969
832
|
if @connection
|
970
833
|
server_info = IBM_DB.server_info(@connection)
|
@@ -1143,6 +1006,15 @@ module ActiveRecord
|
|
1143
1006
|
true
|
1144
1007
|
end
|
1145
1008
|
|
1009
|
+
#IBM Db2 does not natively support skipping rows on insert when there's a duplicate key
|
1010
|
+
def supports_insert_on_duplicate_skip?
|
1011
|
+
false
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
def supports_insert_on_duplicate_update?
|
1015
|
+
false
|
1016
|
+
end
|
1017
|
+
|
1146
1018
|
# This adapter supports migrations.
|
1147
1019
|
# Current limitations:
|
1148
1020
|
# +rename_column+ is not currently supported by the IBM data servers
|
@@ -1262,6 +1134,7 @@ module ActiveRecord
|
|
1262
1134
|
@set_quoted_literal_replacement)
|
1263
1135
|
puts_log "Connection Established B = #{@connection}"
|
1264
1136
|
end
|
1137
|
+
@raw_connection = @connection
|
1265
1138
|
rescue StandardError => e
|
1266
1139
|
warn "Connection to database #{@database} failed: #{e}"
|
1267
1140
|
puts_log "Connection to database #{@database} failed: #{e}"
|
@@ -1318,6 +1191,7 @@ module ActiveRecord
|
|
1318
1191
|
IBM_DB.close(@connection)
|
1319
1192
|
puts_log "Connection closed #{Thread.current}"
|
1320
1193
|
@connection = nil
|
1194
|
+
@raw_connection = nil
|
1321
1195
|
rescue StandardError => e
|
1322
1196
|
puts_log "Connection close failure #{e.message}, #{Thread.current}"
|
1323
1197
|
end
|
@@ -1325,13 +1199,24 @@ module ActiveRecord
|
|
1325
1199
|
end
|
1326
1200
|
end
|
1327
1201
|
|
1202
|
+
# Check the connection back in to the connection pool
|
1203
|
+
def close
|
1204
|
+
pool.checkin self
|
1205
|
+
disconnect!
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
def connected?
|
1209
|
+
puts_log "connected? #{@connection}"
|
1210
|
+
!(@connection.nil?)
|
1211
|
+
end
|
1212
|
+
|
1328
1213
|
#==============================================
|
1329
1214
|
# DATABASE STATEMENTS
|
1330
1215
|
#==============================================
|
1331
1216
|
|
1332
1217
|
def create_table(name, id: :primary_key, primary_key: nil, force: nil, **options)
|
1333
1218
|
puts_log "create_table name=#{name}, id=#{id}, primary_key=#{primary_key}, force=#{force}"
|
1334
|
-
puts_log "create_table Options = #{options}"
|
1219
|
+
puts_log "create_table Options 1 = #{options}"
|
1335
1220
|
puts_log "primary_key_prefix_type = #{ActiveRecord::Base.primary_key_prefix_type}"
|
1336
1221
|
puts_log caller
|
1337
1222
|
@servertype.setup_for_lob_table
|
@@ -1356,6 +1241,7 @@ module ActiveRecord
|
|
1356
1241
|
options[:auto_increment] = true if options[:auto_increment].nil? and %i[integer bigint].include?(id)
|
1357
1242
|
end
|
1358
1243
|
|
1244
|
+
puts_log "create_table Options 2 = #{options}"
|
1359
1245
|
super(name, id: id, primary_key: primary_key, force: force, **options)
|
1360
1246
|
end
|
1361
1247
|
|
@@ -1381,8 +1267,8 @@ module ActiveRecord
|
|
1381
1267
|
end
|
1382
1268
|
end
|
1383
1269
|
|
1384
|
-
def select(sql, name = nil, binds = [], prepare: false, async: false)
|
1385
|
-
puts_log "select #{sql}"
|
1270
|
+
def select(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false)
|
1271
|
+
puts_log "select sql = #{sql}"
|
1386
1272
|
puts_log "binds = #{binds}"
|
1387
1273
|
puts_log "prepare = #{prepare}"
|
1388
1274
|
|
@@ -1414,20 +1300,21 @@ module ActiveRecord
|
|
1414
1300
|
end
|
1415
1301
|
|
1416
1302
|
results = []
|
1303
|
+
cols = []
|
1417
1304
|
|
1418
1305
|
stmt = if binds.nil? || binds.empty?
|
1419
|
-
internal_execute(sql, name)
|
1306
|
+
internal_execute(sql, name, allow_retry: allow_retry)
|
1420
1307
|
else
|
1421
|
-
exec_query_ret_stmt(sql, name, binds, prepare: prepare, async: async)
|
1308
|
+
exec_query_ret_stmt(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry)
|
1422
1309
|
end
|
1423
1310
|
|
1424
|
-
cols = IBM_DB.resultCols(stmt)
|
1425
|
-
|
1426
1311
|
if stmt
|
1312
|
+
cols = IBM_DB.resultCols(stmt)
|
1427
1313
|
results = fetch_data(stmt)
|
1428
|
-
puts_log "Results = #{results}"
|
1429
1314
|
end
|
1430
1315
|
|
1316
|
+
puts_log "select cols = #{cols}, results = #{results}"
|
1317
|
+
|
1431
1318
|
if @isAr3
|
1432
1319
|
results
|
1433
1320
|
else
|
@@ -1437,11 +1324,13 @@ module ActiveRecord
|
|
1437
1324
|
end
|
1438
1325
|
end
|
1439
1326
|
|
1327
|
+
puts_log "select final results = #{results} #{caller}"
|
1440
1328
|
results
|
1441
1329
|
end
|
1442
1330
|
|
1443
1331
|
def translate_exception(exception, message:, sql:, binds:)
|
1444
|
-
puts_log "translate_exception - #{message}"
|
1332
|
+
puts_log "translate_exception - exception = #{exception}, message = #{message}"
|
1333
|
+
puts_log "translate_exception #{caller}"
|
1445
1334
|
error_msg1 = /SQL0803N One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because the primary key, unique constraint or unique index identified by .* constrains table .* from having duplicate values for the index key/
|
1446
1335
|
error_msg2 = /SQL0204N .* is an undefined name/
|
1447
1336
|
error_msg3 = /SQL0413N Overflow occurred during numeric data type conversion/
|
@@ -1471,8 +1360,13 @@ module ActiveRecord
|
|
1471
1360
|
elsif exception.message.match?(/called on a closed database/i)
|
1472
1361
|
puts_log 'ConnectionNotEstablished exception'
|
1473
1362
|
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
1363
|
+
elsif message.strip.start_with?("FrozenError") or
|
1364
|
+
message.strip.start_with?("ActiveRecord::Encryption::Errors::Encoding:") or
|
1365
|
+
message.strip.start_with?("ActiveRecord::Encryption::Errors::Encryption") or
|
1366
|
+
message.strip.start_with?("ActiveRecord::ConnectionFailed")
|
1367
|
+
exception
|
1474
1368
|
else
|
1475
|
-
super
|
1369
|
+
super(message, message: exception, sql: sql, binds: binds)
|
1476
1370
|
end
|
1477
1371
|
end
|
1478
1372
|
|
@@ -1611,11 +1505,52 @@ module ActiveRecord
|
|
1611
1505
|
" VALUES (#{val})"
|
1612
1506
|
end
|
1613
1507
|
|
1508
|
+
def getTableIdentityColumn(table_name)
|
1509
|
+
query = "SELECT COLNAME FROM SYSCAT.COLUMNS WHERE TABNAME = #{quote(table_name.upcase)} AND IDENTITY = 'Y'"
|
1510
|
+
puts_log "getTableIdentityColumn table_name = #{table_name}, query = #{query}"
|
1511
|
+
rows = execute_without_logging(query).rows
|
1512
|
+
puts_log "getTableIdentityColumn rows = #{rows}"
|
1513
|
+
if rows.any?
|
1514
|
+
return rows.first
|
1515
|
+
end
|
1516
|
+
end
|
1517
|
+
|
1518
|
+
def return_insert (stmt, sql, binds, pk, id_value = nil, returning: nil)
|
1519
|
+
puts_log "return_insert sql = #{sql}, pk = #{pk}, returning = #{returning}"
|
1520
|
+
@sql << sql
|
1521
|
+
|
1522
|
+
table_name = sql[/\AINSERT\s+INTO\s+([^\s\(]+)/i, 1]
|
1523
|
+
rowID = getTableIdentityColumn(table_name)
|
1524
|
+
#Identity column exist.
|
1525
|
+
if Array(rowID).any?
|
1526
|
+
val = @servertype.last_generated_id(stmt)
|
1527
|
+
#returning required is just an ID, or nothing is expected to return
|
1528
|
+
only_returning_id = Array(returning).empty? ||
|
1529
|
+
(Array(returning).size == 1 && Array(rowID).first == Array(returning).first)
|
1530
|
+
unless only_returning_id
|
1531
|
+
cols = Array(returning).join(', ')
|
1532
|
+
query = "SELECT #{cols} FROM #{table_name} WHERE #{Array(rowID).first} = #{val}"
|
1533
|
+
puts_log "return_insert val = #{val}, cols = #{cols}, table_name = #{table_name}"
|
1534
|
+
puts_log "return_insert query = #{query}"
|
1535
|
+
rows = execute_without_logging(query).rows
|
1536
|
+
puts_log "return_insert rows = #{rows}"
|
1537
|
+
return rows.first
|
1538
|
+
end
|
1539
|
+
end
|
1540
|
+
|
1541
|
+
puts_log "return_insert id_value = #{id_value}, val = #{val}"
|
1542
|
+
if !returning.nil?
|
1543
|
+
[id_value || val]
|
1544
|
+
else
|
1545
|
+
id_value || val
|
1546
|
+
end
|
1547
|
+
end
|
1548
|
+
|
1614
1549
|
# Perform an insert and returns the last ID generated.
|
1615
1550
|
# This can be the ID passed to the method or the one auto-generated by the database,
|
1616
1551
|
# and retrieved by the +last_generated_id+ method.
|
1617
|
-
def insert_direct(sql, name = nil, _pk = nil, id_value = nil,
|
1618
|
-
puts_log
|
1552
|
+
def insert_direct(sql, name = nil, _pk = nil, id_value = nil, returning: nil)
|
1553
|
+
puts_log "insert_direct sql = #{sql}, name = #{name}, _pk = #{_pk}, returning = #{returning}"
|
1619
1554
|
if @handle_lobs_triggered # Ensure the array of sql is cleared if they have been handled in the callback
|
1620
1555
|
@sql = []
|
1621
1556
|
@handle_lobs_triggered = false
|
@@ -1624,9 +1559,7 @@ module ActiveRecord
|
|
1624
1559
|
return unless stmt = execute(sql, name)
|
1625
1560
|
|
1626
1561
|
begin
|
1627
|
-
|
1628
|
-
return [@servertype.last_generated_id(stmt)] unless returning.nil?
|
1629
|
-
id_value || @servertype.last_generated_id(stmt)
|
1562
|
+
return_insert(stmt, sql, nil, _pk, id_value, returning: returning)
|
1630
1563
|
# Ensures to free the resources associated with the statement
|
1631
1564
|
ensure
|
1632
1565
|
IBM_DB.free_stmt(stmt) if stmt
|
@@ -1634,7 +1567,7 @@ module ActiveRecord
|
|
1634
1567
|
end
|
1635
1568
|
|
1636
1569
|
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
|
1637
|
-
puts_log "insert Binds P = #{binds}"
|
1570
|
+
puts_log "insert Binds P = #{binds}, name = #{name}, pk = #{pk}, id_value = #{id_value}, returning = #{returning}"
|
1638
1571
|
puts_log caller
|
1639
1572
|
if @arelVersion < 6
|
1640
1573
|
sql = to_sql(arel)
|
@@ -1646,27 +1579,30 @@ module ActiveRecord
|
|
1646
1579
|
puts_log "insert Binds A = #{binds}"
|
1647
1580
|
puts_log "insert SQL = #{sql}"
|
1648
1581
|
# unless IBM_DBAdapter.respond_to?(:exec_insert)
|
1649
|
-
return insert_direct(sql, name, pk, id_value,
|
1582
|
+
return insert_direct(sql, name, pk, id_value, returning: returning) if binds.nil? || binds.empty?
|
1650
1583
|
|
1651
1584
|
ActiveRecord::Base.clear_query_caches_for_current_thread
|
1652
1585
|
|
1653
1586
|
return unless stmt = exec_insert_db2(sql, name, binds, pk, sequence_name, returning)
|
1654
1587
|
|
1655
1588
|
begin
|
1656
|
-
|
1657
|
-
return [@servertype.last_generated_id(stmt)] unless returning.nil?
|
1658
|
-
id_value || @servertype.last_generated_id(stmt)
|
1589
|
+
return_insert(stmt, sql, binds, pk, id_value, returning: returning)
|
1659
1590
|
ensure
|
1660
1591
|
IBM_DB.free_stmt(stmt) if stmt
|
1661
1592
|
end
|
1662
1593
|
end
|
1663
1594
|
|
1664
1595
|
def exec_insert_db2(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning = nil)
|
1665
|
-
puts_log
|
1596
|
+
puts_log "exec_insert_db2 sql = #{sql}, name = #{name}, binds = #{binds}, pk = #{pk}, returning = #{returning}"
|
1666
1597
|
sql, binds = sql_for_insert(sql, pk, binds, returning)
|
1667
1598
|
exec_query_ret_stmt(sql, name, binds, prepare: false)
|
1668
1599
|
end
|
1669
1600
|
|
1601
|
+
def build_insert_sql(insert) # :nodoc:
|
1602
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
1603
|
+
sql
|
1604
|
+
end
|
1605
|
+
|
1670
1606
|
def last_inserted_id(result)
|
1671
1607
|
puts_log 'last_inserted_id'
|
1672
1608
|
result
|
@@ -1745,7 +1681,7 @@ module ActiveRecord
|
|
1745
1681
|
!READ_QUERY.match?(sql.b)
|
1746
1682
|
end
|
1747
1683
|
|
1748
|
-
def explain(arel, binds = [])
|
1684
|
+
def explain(arel, binds = [], options = [])
|
1749
1685
|
sql = "EXPLAIN ALL SET QUERYNO = 1 FOR #{to_sql(arel, binds)}"
|
1750
1686
|
stmt = execute(sql, 'EXPLAIN')
|
1751
1687
|
result = select("select * from explain_statement where explain_level = 'P' and queryno = 1", 'EXPLAIN')
|
@@ -1755,15 +1691,55 @@ module ActiveRecord
|
|
1755
1691
|
IBM_DB.free_stmt(stmt) if stmt
|
1756
1692
|
end
|
1757
1693
|
|
1694
|
+
def execute_without_logging(sql, name = nil, binds = [], prepare: true, async: false)
|
1695
|
+
puts_log "execute_without_logging sql = #{sql}, name = #{name}, binds = #{binds}"
|
1696
|
+
|
1697
|
+
sql = transform_query(sql)
|
1698
|
+
check_if_write_query(sql)
|
1699
|
+
mark_transaction_written_if_write(sql)
|
1700
|
+
cols = nil
|
1701
|
+
results = nil
|
1702
|
+
begin
|
1703
|
+
param_array = type_casted_binds(binds)
|
1704
|
+
puts_log "execute_without_logging Param array = #{param_array}"
|
1705
|
+
puts_log "execute_without_logging #{caller}"
|
1706
|
+
|
1707
|
+
stmt = @servertype.prepare(sql, name)
|
1708
|
+
@statements[sql] = stmt if prepare
|
1709
|
+
|
1710
|
+
puts_log "execute_without_logging Statement = #{stmt}"
|
1711
|
+
|
1712
|
+
execute_prepared_stmt(stmt, param_array)
|
1713
|
+
|
1714
|
+
if stmt and sql.strip.upcase.start_with?("SELECT")
|
1715
|
+
cols = IBM_DB.resultCols(stmt)
|
1716
|
+
results = fetch_data(stmt) if stmt
|
1717
|
+
|
1718
|
+
puts_log "execute_without_logging columns = #{cols}"
|
1719
|
+
puts_log "execute_without_logging result = #{results}"
|
1720
|
+
end
|
1721
|
+
rescue => e
|
1722
|
+
raise translate_exception_class(e, sql, binds)
|
1723
|
+
ensure
|
1724
|
+
@offset = @limit = nil
|
1725
|
+
end
|
1726
|
+
if @isAr3
|
1727
|
+
results
|
1728
|
+
elsif results.nil?
|
1729
|
+
ActiveRecord::Result.empty
|
1730
|
+
else
|
1731
|
+
ActiveRecord::Result.new(cols, results)
|
1732
|
+
end
|
1733
|
+
end
|
1734
|
+
|
1758
1735
|
# Executes +sql+ statement in the context of this connection using
|
1759
1736
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
1760
1737
|
# the executed +sql+ statement.
|
1761
1738
|
# Here prepare argument is not used, by default this method creates prepared statment and execute.
|
1762
|
-
def exec_query_ret_stmt(sql, name = 'SQL', binds = [], prepare: false, async: false)
|
1739
|
+
def exec_query_ret_stmt(sql, name = 'SQL', binds = [], prepare: false, async: false, allow_retry: false)
|
1763
1740
|
puts_log "exec_query_ret_stmt #{sql}"
|
1764
1741
|
sql = transform_query(sql)
|
1765
1742
|
check_if_write_query(sql)
|
1766
|
-
#materialize_transactions
|
1767
1743
|
mark_transaction_written_if_write(sql)
|
1768
1744
|
begin
|
1769
1745
|
puts_log "SQL = #{sql}"
|
@@ -1778,7 +1754,7 @@ module ActiveRecord
|
|
1778
1754
|
|
1779
1755
|
puts_log "Statement = #{stmt}"
|
1780
1756
|
log(sql, name, binds, param_array, async: async) do
|
1781
|
-
with_raw_connection do |conn|
|
1757
|
+
with_raw_connection(allow_retry: allow_retry) do |conn|
|
1782
1758
|
return false unless stmt
|
1783
1759
|
return stmt if execute_prepared_stmt(stmt, param_array)
|
1784
1760
|
end
|
@@ -1799,7 +1775,10 @@ module ActiveRecord
|
|
1799
1775
|
puts_log "select_prepared sql before = #{sql}"
|
1800
1776
|
puts_log "select_prepared Binds = #{binds}"
|
1801
1777
|
stmt = exec_query_ret_stmt(sql, name, binds, prepare: prepare, async: async)
|
1802
|
-
|
1778
|
+
cols = nil
|
1779
|
+
results = nil
|
1780
|
+
|
1781
|
+
if stmt and sql.strip.upcase.start_with?("SELECT")
|
1803
1782
|
cols = IBM_DB.resultCols(stmt)
|
1804
1783
|
|
1805
1784
|
results = fetch_data(stmt) if stmt
|
@@ -1807,12 +1786,11 @@ module ActiveRecord
|
|
1807
1786
|
puts_log "select_prepared columns = #{cols}"
|
1808
1787
|
puts_log "select_prepared sql after = #{sql}"
|
1809
1788
|
puts_log "select_prepared result = #{results}"
|
1810
|
-
else
|
1811
|
-
cols = nil
|
1812
|
-
results = nil
|
1813
1789
|
end
|
1814
1790
|
if @isAr3
|
1815
1791
|
results
|
1792
|
+
elsif results.nil?
|
1793
|
+
ActiveRecord::Result.empty
|
1816
1794
|
else
|
1817
1795
|
ActiveRecord::Result.new(cols, results)
|
1818
1796
|
end
|
@@ -1829,19 +1807,36 @@ module ActiveRecord
|
|
1829
1807
|
def execute(sql, name = nil, allow_retry: false)
|
1830
1808
|
puts_log "execute #{sql}"
|
1831
1809
|
ActiveRecord::Base.clear_query_caches_for_current_thread
|
1832
|
-
internal_execute(sql, name, allow_retry: allow_retry)
|
1810
|
+
stmt = internal_execute(sql, name, allow_retry: allow_retry)
|
1811
|
+
cols = nil
|
1812
|
+
results = nil
|
1813
|
+
puts_log "raw_execute stmt = #{stmt}"
|
1814
|
+
if sql.strip.upcase.start_with?("SELECT") and stmt
|
1815
|
+
cols = IBM_DB.resultCols(stmt)
|
1816
|
+
results = fetch_data(stmt)
|
1817
|
+
|
1818
|
+
puts_log "execute columns = #{cols}"
|
1819
|
+
puts_log "execute result = #{results}"
|
1820
|
+
end
|
1821
|
+
if results.nil? || results.empty?
|
1822
|
+
stmt
|
1823
|
+
else
|
1824
|
+
formatted = cols.each_with_index.map { |col, i| { col => results[i].first } }
|
1825
|
+
puts_log "raw_execute formatted = #{formatted}"
|
1826
|
+
formatted.to_s
|
1827
|
+
end
|
1833
1828
|
end
|
1834
1829
|
|
1835
1830
|
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
1836
1831
|
# Logs and execute the sql instructions.
|
1837
1832
|
# The +log+ method is defined in the parent class +AbstractAdapter+
|
1838
1833
|
# sql='INSERT INTO ar_internal_metadata (key, value, created_at, updated_at) VALUES ('10', '10', '10', '10')
|
1839
|
-
puts_log "raw_execute #{sql} #{Thread.current}"
|
1834
|
+
puts_log "raw_execute sql = #{sql} #{Thread.current}"
|
1840
1835
|
log(sql, name, async: async) do
|
1841
1836
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
1842
1837
|
verify!
|
1843
1838
|
puts_log "raw_execute executes query #{Thread.current}"
|
1844
|
-
result
|
1839
|
+
result= @servertype.execute(sql, name)
|
1845
1840
|
puts_log "raw_execute result = #{result} #{Thread.current}"
|
1846
1841
|
verified!
|
1847
1842
|
result
|
@@ -2009,7 +2004,8 @@ module ActiveRecord
|
|
2009
2004
|
end
|
2010
2005
|
|
2011
2006
|
def default_sequence_name(table, column) # :nodoc:
|
2012
|
-
puts_log
|
2007
|
+
puts_log "default_sequence_name table = #{table}, column = #{column}"
|
2008
|
+
return nil if column.is_a?(Array)
|
2013
2009
|
"#{table}_#{column}_seq"
|
2014
2010
|
end
|
2015
2011
|
|
@@ -2078,6 +2074,7 @@ module ActiveRecord
|
|
2078
2074
|
|
2079
2075
|
def quote_table_name(name)
|
2080
2076
|
puts_log "quote_table_name #{name}"
|
2077
|
+
puts_log caller
|
2081
2078
|
if name.start_with? '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
|
2082
2079
|
name = "\"#{name}\""
|
2083
2080
|
else
|
@@ -2089,7 +2086,7 @@ module ActiveRecord
|
|
2089
2086
|
end
|
2090
2087
|
|
2091
2088
|
def quote_column_name(name)
|
2092
|
-
puts_log
|
2089
|
+
puts_log "quote_column_name #{name}"
|
2093
2090
|
@servertype.check_reserved_words(name).gsub('"', '').gsub("'", '')
|
2094
2091
|
end
|
2095
2092
|
|
@@ -2106,7 +2103,7 @@ module ActiveRecord
|
|
2106
2103
|
def native_database_types
|
2107
2104
|
{
|
2108
2105
|
primary_key: { name: @servertype.primary_key_definition(@start_id) },
|
2109
|
-
string: { name: 'varchar', limit:
|
2106
|
+
string: { name: 'varchar', limit: 400 },
|
2110
2107
|
text: { name: 'clob' },
|
2111
2108
|
integer: { name: 'integer' },
|
2112
2109
|
float: { name: 'float' },
|
@@ -2509,7 +2506,7 @@ module ActiveRecord
|
|
2509
2506
|
next if is_composite
|
2510
2507
|
|
2511
2508
|
sql = "select remarks from syscat.indexes where tabname = #{quote(table_name.upcase)} and indname = #{quote(index_stats[5])}"
|
2512
|
-
comment = single_value_from_rows(
|
2509
|
+
comment = single_value_from_rows(execute_without_logging(sql, "SCHEMA").rows)
|
2513
2510
|
|
2514
2511
|
indexes << IndexDefinition.new(table_name, index_name, index_unique, index_columns,
|
2515
2512
|
comment: comment)
|
@@ -2669,22 +2666,34 @@ module ActiveRecord
|
|
2669
2666
|
# sql = "select * from sysibm.sqlcolumns where table_name = #{quote(table_name.upcase)}"
|
2670
2667
|
if @debug == true
|
2671
2668
|
sql = "select * from syscat.columns where tabname = #{quote(table_name.upcase)}"
|
2672
|
-
puts_log "SYSIBM.SQLCOLUMNS = #{
|
2669
|
+
puts_log "SYSIBM.SQLCOLUMNS = #{execute_without_logging(sql).rows}"
|
2673
2670
|
end
|
2674
2671
|
|
2672
|
+
pri_key = primary_key(table_name)
|
2673
|
+
|
2675
2674
|
if stmt
|
2676
2675
|
begin
|
2677
2676
|
# Fetches all the columns and assigns them to col.
|
2678
2677
|
# +col+ is an hash with keys/value pairs for a column
|
2679
2678
|
while col = IBM_DB.fetch_assoc(stmt)
|
2680
|
-
|
2679
|
+
rowid = false
|
2680
|
+
puts_log "def columns fecthed = #{col}"
|
2681
2681
|
column_name = col['column_name'].downcase
|
2682
|
+
sql = "select 1 FROM syscat.columns where tabname = #{quote(table_name.upcase)} and generated = 'D' and colname = '#{col['column_name']}'"
|
2683
|
+
rows = execute_without_logging(sql).rows
|
2684
|
+
auto_increment = rows.dig(0, 0) == 1 ? true : nil
|
2685
|
+
puts_log "def columns auto_increment = #{rows}, #{auto_increment}"
|
2686
|
+
|
2682
2687
|
# Assigns the column default value.
|
2683
2688
|
column_default_value = col['column_def']
|
2684
2689
|
default_value = extract_value_from_default(column_default_value)
|
2685
2690
|
# Assigns the column type
|
2686
2691
|
column_type = col['type_name'].downcase
|
2687
2692
|
|
2693
|
+
if Array(pri_key).include?(column_name) and column_type =~ /integer|bigint/i
|
2694
|
+
rowid = true
|
2695
|
+
puts_log "def columns rowid = true"
|
2696
|
+
end
|
2688
2697
|
# Assigns the field length (size) for the column
|
2689
2698
|
|
2690
2699
|
column_length = if column_type =~ /integer|bigint/i
|
@@ -2743,7 +2752,9 @@ module ActiveRecord
|
|
2743
2752
|
|
2744
2753
|
column_type = 'boolean' if ruby_type.to_s == 'boolean'
|
2745
2754
|
|
2755
|
+
puts_log "Inside def columns() - default_value = #{default_value}, column_default_value = #{column_default_value}"
|
2746
2756
|
default_function = extract_default_function(default_value, column_default_value)
|
2757
|
+
puts_log "Inside def columns() - default_function = #{default_function}"
|
2747
2758
|
|
2748
2759
|
sqltype_metadata = SqlTypeMetadata.new(
|
2749
2760
|
# sql_type: sql_type,
|
@@ -2755,7 +2766,7 @@ module ActiveRecord
|
|
2755
2766
|
)
|
2756
2767
|
|
2757
2768
|
columns << Column.new(column_name, default_value, sqltype_metadata, column_nullable, default_function,
|
2758
|
-
comment: col['remarks'])
|
2769
|
+
comment: col['remarks'], auto_increment: auto_increment, rowid: rowid)
|
2759
2770
|
end
|
2760
2771
|
rescue StandardError => e # Handle driver fetch errors
|
2761
2772
|
error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT)
|
@@ -2789,6 +2800,7 @@ module ActiveRecord
|
|
2789
2800
|
|
2790
2801
|
def has_default_function?(default_value, default)
|
2791
2802
|
!default_value && /\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP/.match?(default)
|
2803
|
+
!default_value && /(\w+\(.*\)|CURRENT(?:[_\s]TIME|[_\s]DATE|[_\s]TIMESTAMP))/i.match?(default)
|
2792
2804
|
end
|
2793
2805
|
|
2794
2806
|
def foreign_keys(table_name)
|
@@ -2932,9 +2944,10 @@ module ActiveRecord
|
|
2932
2944
|
|
2933
2945
|
# Adds comment for given table or drops it if +comment+ is a +nil+
|
2934
2946
|
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
2935
|
-
puts_log
|
2947
|
+
puts_log "change_table_comment table_name = #{table_name}, comment_or_changes = #{comment_or_changes}"
|
2936
2948
|
clear_cache!
|
2937
2949
|
comment = extract_new_comment_value(comment_or_changes)
|
2950
|
+
puts_log "change_table_comment new_comment = #{comment}"
|
2938
2951
|
if comment.nil?
|
2939
2952
|
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS ''"
|
2940
2953
|
else
|
@@ -2963,9 +2976,9 @@ module ActiveRecord
|
|
2963
2976
|
end
|
2964
2977
|
|
2965
2978
|
def table_comment(table_name) # :nodoc:
|
2966
|
-
puts_log
|
2979
|
+
puts_log "table_comment table_name = #{table_name}"
|
2967
2980
|
sql = "select remarks from syscat.tables where tabname = #{quote(table_name.upcase)}"
|
2968
|
-
single_value_from_rows(
|
2981
|
+
single_value_from_rows(execute_without_logging(sql).rows)
|
2969
2982
|
end
|
2970
2983
|
|
2971
2984
|
def add_index(table_name, column_name, **options) # :nodoc:
|
@@ -3087,7 +3100,7 @@ module ActiveRecord
|
|
3087
3100
|
# rename_table('octopuses', 'octopi')
|
3088
3101
|
# Overriden to satisfy IBM data servers syntax
|
3089
3102
|
def rename_table(name, new_name, **options)
|
3090
|
-
puts_log
|
3103
|
+
puts_log "rename_table name = #{name}, new_name = #{new_name}"
|
3091
3104
|
validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
|
3092
3105
|
clear_cache!
|
3093
3106
|
schema_cache.clear_data_source_cache!(name.to_s)
|
@@ -3108,19 +3121,20 @@ module ActiveRecord
|
|
3108
3121
|
end
|
3109
3122
|
|
3110
3123
|
def add_reference(table_name, ref_name, **options) # :nodoc:
|
3124
|
+
puts_log "add_reference table_name = #{table_name}, ref_name = #{ref_name}"
|
3111
3125
|
super(table_name, ref_name, type: :integer, **options)
|
3112
3126
|
end
|
3113
3127
|
alias :add_belongs_to :add_reference
|
3114
3128
|
|
3115
3129
|
def drop_table_indexes(index_list)
|
3116
|
-
puts_log
|
3130
|
+
puts_log "drop_table_indexes index_list = #{index_list}"
|
3117
3131
|
index_list.each do |indexs|
|
3118
3132
|
remove_index(indexs.table, name: indexs.name)
|
3119
3133
|
end
|
3120
3134
|
end
|
3121
3135
|
|
3122
3136
|
def create_table_indexes(index_list, new_table)
|
3123
|
-
puts_log
|
3137
|
+
puts_log "create_table_indexes index_list = #{index_list}, new_table = #{new_table}"
|
3124
3138
|
index_list.each do |indexs|
|
3125
3139
|
generated_index_name = index_name(indexs.table, column: indexs.columns)
|
3126
3140
|
custom_index_name = indexs.name
|
@@ -3412,6 +3426,7 @@ module ActiveRecord
|
|
3412
3426
|
puts_log "remove_unique_constraint table_name = #{table_name}, column_name = #{column_name}, options = #{options}"
|
3413
3427
|
unique_name_to_delete = unique_constraint_for!(table_name, column: column_name, **options).name
|
3414
3428
|
|
3429
|
+
puts_log "remove_unique_constraint unique_name_to_delete = #{unique_name_to_delete}"
|
3415
3430
|
at = create_alter_table(table_name)
|
3416
3431
|
at.drop_unique_constraint(unique_name_to_delete)
|
3417
3432
|
|
@@ -3419,7 +3434,7 @@ module ActiveRecord
|
|
3419
3434
|
end
|
3420
3435
|
|
3421
3436
|
def unique_constraint_name(table_name, **options)
|
3422
|
-
puts_log "unique_constraint_name"
|
3437
|
+
puts_log "unique_constraint_name table_name = #{table_name}, options = #{options}"
|
3423
3438
|
options.fetch(:name) do
|
3424
3439
|
column_or_index = Array(options[:column] || options[:using_index]).map(&:to_s)
|
3425
3440
|
identifier = "#{table_name}_#{column_or_index * '_and_'}_unique"
|
@@ -3430,16 +3445,72 @@ module ActiveRecord
|
|
3430
3445
|
end
|
3431
3446
|
|
3432
3447
|
def unique_constraint_for(table_name, **options)
|
3433
|
-
|
3434
|
-
|
3448
|
+
puts_log "unique_constraint_for table_name = #{table_name}, options = #{options}"
|
3449
|
+
name = unique_constraint_name(table_name, **options)
|
3450
|
+
puts_log "unique_constraint_for name = #{name}"
|
3451
|
+
uq = unique_constraints(table_name)
|
3452
|
+
puts_log "unique_constraint_for unique_constraints = #{uq}"
|
3453
|
+
uq.detect { |unique_constraint| unique_constraint.defined_for?(name: name) }
|
3435
3454
|
end
|
3436
3455
|
|
3437
3456
|
def unique_constraint_for!(table_name, column: nil, **options)
|
3438
|
-
puts_log "unique_constraint_for table_name = #{table_name}, column = #{column}, options = #{options}"
|
3457
|
+
puts_log "unique_constraint_for! table_name = #{table_name}, column = #{column}, options = #{options}"
|
3439
3458
|
unique_constraint_for(table_name, column: column, **options) ||
|
3440
3459
|
raise(ArgumentError, "Table '#{table_name}' has no unique constraint for #{column || options}")
|
3441
3460
|
end
|
3442
3461
|
|
3462
|
+
def foreign_key_name(table_name, options)
|
3463
|
+
puts_log "foreign_key_name table_name = #{table_name}, options = #{options}"
|
3464
|
+
options.fetch(:name) do
|
3465
|
+
columns = Array(options.fetch(:column)).map(&:to_s)
|
3466
|
+
identifier = "#{table_name}_#{columns * '_and_'}_fk"
|
3467
|
+
hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
|
3468
|
+
|
3469
|
+
"fk_rails_#{hashed_identifier}"
|
3470
|
+
end
|
3471
|
+
end
|
3472
|
+
|
3473
|
+
def foreign_key_for(from_table, **options)
|
3474
|
+
puts_log "foreign_key_for from_table = #{from_table}, options = #{options}"
|
3475
|
+
return unless use_foreign_keys?
|
3476
|
+
fks = foreign_keys(from_table)
|
3477
|
+
puts_log "foreign_key_for fks = #{fks}"
|
3478
|
+
if options.key?(:column) && options.key?(:to_table) && options[:to_table] != nil
|
3479
|
+
name = foreign_key_name(from_table, options)
|
3480
|
+
puts_log "foreign_key_for name = #{options}"
|
3481
|
+
fks.detect { |fk| fk.defined_for?(name: name) }
|
3482
|
+
else
|
3483
|
+
fks.detect { |fk| fk.defined_for?(**options) }
|
3484
|
+
end
|
3485
|
+
end
|
3486
|
+
|
3487
|
+
def foreign_key_for!(from_table, to_table: nil, **options)
|
3488
|
+
puts_log "foreign_key_for! from_table = #{from_table}, to_table = #{to_table}, options = #{options}"
|
3489
|
+
foreign_key_for(from_table, to_table: to_table, **options) ||
|
3490
|
+
raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
3491
|
+
end
|
3492
|
+
|
3493
|
+
def foreign_key_exists?(from_table, to_table = nil, **options)
|
3494
|
+
puts_log "foreign_key_exists? from_table = #{from_table}, to_table = #{to_table}, options = #{options}"
|
3495
|
+
foreign_key_for(from_table, to_table: to_table, **options).present?
|
3496
|
+
end
|
3497
|
+
|
3498
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
3499
|
+
puts_log "remove_foreign_key from_table = #{from_table}, to_table = #{to_table}, options = #{options}"
|
3500
|
+
#to_table ||= options[:to_table]
|
3501
|
+
return unless use_foreign_keys?
|
3502
|
+
#return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
3503
|
+
return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
|
3504
|
+
|
3505
|
+
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
3506
|
+
puts_log "remove_foreign_key fk_name_to_delete = #{fk_name_to_delete}"
|
3507
|
+
|
3508
|
+
at = create_alter_table from_table
|
3509
|
+
at.drop_foreign_key fk_name_to_delete
|
3510
|
+
|
3511
|
+
execute schema_creation.accept(at)
|
3512
|
+
end
|
3513
|
+
|
3443
3514
|
def create_table_definition(name, **options)
|
3444
3515
|
puts_log "create_table_definition name = #{name}"
|
3445
3516
|
puts_log caller
|