ibm_db 2.5.17-universal-darwin-13

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +221 -0
  3. data/LICENSE +18 -0
  4. data/MANIFEST +14 -0
  5. data/ParameterizedQueries README +39 -0
  6. data/README +225 -0
  7. data/ext/Makefile.nt32 +181 -0
  8. data/ext/Makefile.nt32.191 +212 -0
  9. data/ext/extconf.rb +127 -0
  10. data/ext/ibm_db.c +11719 -0
  11. data/ext/ruby_ibm_db.h +240 -0
  12. data/ext/ruby_ibm_db_cli.c +845 -0
  13. data/ext/ruby_ibm_db_cli.h +489 -0
  14. data/init.rb +42 -0
  15. data/lib/IBM_DB.rb +16 -0
  16. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3031 -0
  17. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1965 -0
  18. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +2 -0
  19. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
  20. data/lib/linux/rb18x/ibm_db.bundle +0 -0
  21. data/lib/linux/rb19x/ibm_db.bundle +0 -0
  22. data/lib/linux/rb20x/ibm_db.bundle +0 -0
  23. data/lib/linux/rb21x/ibm_db.bundle +0 -0
  24. data/test/cases/adapter_test.rb +207 -0
  25. data/test/cases/associations/belongs_to_associations_test.rb +711 -0
  26. data/test/cases/associations/cascaded_eager_loading_test.rb +181 -0
  27. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +851 -0
  28. data/test/cases/associations/join_model_test.rb +743 -0
  29. data/test/cases/attribute_methods_test.rb +822 -0
  30. data/test/cases/base_test.rb +2133 -0
  31. data/test/cases/calculations_test.rb +482 -0
  32. data/test/cases/migration_test.rb +2408 -0
  33. data/test/cases/persistence_test.rb +642 -0
  34. data/test/cases/query_cache_test.rb +257 -0
  35. data/test/cases/relations_test.rb +1182 -0
  36. data/test/cases/schema_dumper_test.rb +256 -0
  37. data/test/cases/transaction_callbacks_test.rb +300 -0
  38. data/test/cases/validations/uniqueness_validation_test.rb +299 -0
  39. data/test/cases/xml_serialization_test.rb +408 -0
  40. data/test/config.yml +154 -0
  41. data/test/connections/native_ibm_db/connection.rb +44 -0
  42. data/test/ibm_db_test.rb +25 -0
  43. data/test/models/warehouse_thing.rb +5 -0
  44. data/test/schema/i5/ibm_db_specific_schema.rb +137 -0
  45. data/test/schema/ids/ibm_db_specific_schema.rb +140 -0
  46. data/test/schema/luw/ibm_db_specific_schema.rb +137 -0
  47. data/test/schema/schema.rb +751 -0
  48. data/test/schema/zOS/ibm_db_specific_schema.rb +208 -0
  49. metadata +109 -0
data/init.rb ADDED
@@ -0,0 +1,42 @@
1
+ # +----------------------------------------------------------------------+
2
+ # | Licensed Materials - Property of IBM |
3
+ # | |
4
+ # | (C) Copyright IBM Corporation 2006, 2007. |
5
+ # +----------------------------------------------------------------------+
6
+
7
+ require 'pathname'
8
+
9
+ begin
10
+ puts ".. Attempt to load IBM_DB Ruby driver for IBM Data Servers for this platform: #{RUBY_PLATFORM}"
11
+ unless defined? IBM_DB
12
+ # find IBM_DB driver path relative init.rb
13
+ drv_path = Pathname.new(File.dirname(__FILE__)) + 'lib'
14
+ drv_path += (RUBY_PLATFORM =~ /mswin32/) ? 'mswin32' : 'linux32'
15
+ puts ".. Locate IBM_DB Ruby driver path: #{drv_path}"
16
+ drv_lib = drv_path + 'ibm_db.so'
17
+ require "#{drv_lib.to_s}"
18
+ puts ".. Successfuly loaded IBM_DB Ruby driver: #{drv_lib}"
19
+ end
20
+ rescue
21
+ raise LoadError, "Failed to load IBM_DB Driver !?"
22
+ end
23
+
24
+ # Include IBM_DB in the list of supported adapters
25
+ RAILS_CONNECTION_ADAPTERS << 'ibm_db'
26
+ # load IBM_DB Adapter provided by the plugin
27
+ require 'active_record/connection_adapters/ibm_db_adapter'
28
+
29
+ # Override the frameworks initialization to re-enable ActiveRecord after being
30
+ # disabled during plugin install (i.e. config.frameworks -= [ :active_record ])
31
+ [:load_environment,\
32
+ :initialize_database,\
33
+ :initialize_logger,\
34
+ :initialize_framework_logging,\
35
+ :initialize_framework_settings,\
36
+ :initialize_framework_views,\
37
+ :initialize_dependency_mechanism,\
38
+ :load_environment ].each do |routine|
39
+ Rails::Initializer.run(routine) do |config|
40
+ config.frameworks = [:active_record]
41
+ end
42
+ end
data/lib/IBM_DB.rb ADDED
@@ -0,0 +1,16 @@
1
+ if (RUBY_PLATFORM =~ /mswin32/ || RUBY_PLATFORM =~ /mingw32/ )
2
+ require 'mswin32/ibm_db'
3
+ else
4
+ if (RUBY_VERSION =~ /1.9/ )
5
+ require 'linux/rb19x/ibm_db.bundle'
6
+ elsif (RUBY_VERSION =~ /2.0/ )
7
+ require 'linux/rb20x/ibm_db.bundle'
8
+ elsif (RUBY_VERSION =~ /2.1/ )
9
+ require 'linux/rb21x/ibm_db.bundle'
10
+ else
11
+ require 'linux/rb18x/ibm_db.bundle'
12
+ end
13
+ end
14
+ require 'active_record'
15
+ require 'active_record/connection_adapters/ibm_db_adapter'
16
+
@@ -0,0 +1,3031 @@
1
+ # +----------------------------------------------------------------------+
2
+ # | Licensed Materials - Property of IBM |
3
+ # | |
4
+ # | (C) Copyright IBM Corporation 2006, 2007, 2008, 2009, 2010 |
5
+ # +----------------------------------------------------------------------+
6
+ # | Authors: Antonio Cangiano <cangiano@ca.ibm.com> |
7
+ # | : Mario Ds Briggs <mario.briggs@in.ibm.com> |
8
+ # | : Praveen Devarao <praveendrl@in.ibm.com> |
9
+ # +----------------------------------------------------------------------+
10
+
11
+ require 'active_record/connection_adapters/abstract_adapter'
12
+ require 'arel/visitors/bind_visitor'
13
+
14
+ module ActiveRecord
15
+ class Relation
16
+ def insert(values)
17
+ primary_key_value = nil
18
+
19
+ if primary_key && Hash === values
20
+ primary_key_value = values[values.keys.find { |k|
21
+ k.name == primary_key
22
+ }]
23
+
24
+ if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
25
+ primary_key_value = connection.next_sequence_value(klass.sequence_name)
26
+ values[klass.arel_table[klass.primary_key]] = primary_key_value
27
+ end
28
+ end
29
+
30
+ im = arel.create_insert
31
+ im.into @table
32
+
33
+ conn = @klass.connection
34
+
35
+ substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
36
+ binds = substitutes.map do |arel_attr, value|
37
+ [@klass.columns_hash[arel_attr.name], value]
38
+ end
39
+
40
+ substitutes.each_with_index do |tuple, i|
41
+ tuple[1] = conn.substitute_at(binds[i][0], i)
42
+ end
43
+
44
+ if values.empty? # empty insert
45
+ im.values = Arel.sql(connection.empty_insert_statement_value(klass.primary_key))
46
+ else
47
+ im.insert substitutes
48
+ end
49
+
50
+ conn.insert(
51
+ im,
52
+ 'SQL',
53
+ primary_key,
54
+ primary_key_value,
55
+ nil,
56
+ binds)
57
+ end
58
+ end
59
+
60
+ class Base
61
+ # Method required to handle LOBs and XML fields.
62
+ # An after save callback checks if a marker has been inserted through
63
+ # the insert or update, and then proceeds to update that record with
64
+ # the actual large object through a prepared statement (param binding).
65
+ after_save :handle_lobs
66
+ def handle_lobs()
67
+ if connection.kind_of?(ConnectionAdapters::IBM_DBAdapter)
68
+ # Checks that the insert or update had at least a BLOB, CLOB or XML field
69
+ connection.sql.each do |clob_sql|
70
+ if clob_sql =~ /BLOB\('(.*)'\)/i ||
71
+ clob_sql =~ /@@@IBMTEXT@@@/i ||
72
+ clob_sql =~ /@@@IBMXML@@@/i ||
73
+ clob_sql =~ /@@@IBMBINARY@@@/i
74
+ update_query = "UPDATE #{self.class.table_name} SET ("
75
+ counter = 0
76
+ values = []
77
+ params = []
78
+ # Selects only binary, text and xml columns
79
+ self.class.columns.select{|col| col.type == :binary ||
80
+ col.type == :text ||
81
+ col.type == :xml}.each do |col|
82
+ # Adds the selected columns to the query
83
+ if counter == 0
84
+ update_query << "#{col.name}"
85
+ else
86
+ update_query << ",#{col.name}"
87
+ end
88
+
89
+ # Add a '?' for the parameter or a NULL if the value is nil or empty
90
+ # (except for a CLOB field where '' can be a value)
91
+ if self[col.name].nil? ||
92
+ self[col.name] == {} ||
93
+ self[col.name] == [] ||
94
+ (self[col.name] == '' && col.type != :text)
95
+ params << 'NULL'
96
+ else
97
+ if self.class.serialized_attributes[col.name]
98
+ values << YAML.dump(self[col.name])
99
+ else
100
+ values << self[col.name]
101
+ end
102
+ params << '?'
103
+ end
104
+ counter += 1
105
+ end
106
+ # no subsequent update is required if no relevant columns are found
107
+ next if counter == 0
108
+
109
+ update_query << ") = "
110
+ # IBM_DB accepts 'SET (column) = NULL' but not (NULL),
111
+ # therefore the sql needs to be changed for a single NULL field.
112
+ if params.size==1 && params[0] == 'NULL'
113
+ update_query << "NULL"
114
+ else
115
+ update_query << "(" + params.join(',') + ")"
116
+ end
117
+
118
+ update_query << " WHERE #{self.class.primary_key} = ?"
119
+ values << self[self.class.primary_key.downcase]
120
+
121
+ begin
122
+ unless stmt = IBM_DB.prepare(connection.connection, update_query)
123
+ error_msg = IBM_DB.getErrormsg( connection.connection, IBM_DB::DB_CONN )
124
+ if error_msg && !error_msg.empty?
125
+ raise "Statement prepare for updating LOB/XML column failed : #{error_msg}"
126
+ else
127
+ raise StandardError.new('An unexpected error occurred during update of LOB/XML column')
128
+ end
129
+ end
130
+ connection.log_query(update_query,'update of LOB/XML field(s)in handle_lobs')
131
+
132
+ # rollback any failed LOB/XML field updates (and remove associated marker)
133
+ unless IBM_DB.execute(stmt, values)
134
+ error_msg = "Failed to insert/update LOB/XML field(s) due to: #{IBM_DB.getErrormsg( stmt, IBM_DB::DB_STMT )}"
135
+ connection.execute("ROLLBACK")
136
+ raise error_msg
137
+ end
138
+ rescue StandardError => error
139
+ raise error
140
+ ensure
141
+ IBM_DB.free_stmt(stmt) if stmt
142
+ end
143
+ end # if clob_sql
144
+ end #connection.sql.each
145
+ connection.handle_lobs_triggered = true
146
+ end # if connection.kind_of?
147
+ end # handle_lobs
148
+ private :handle_lobs
149
+
150
+ # Establishes a connection to a specified database using the credentials provided
151
+ # with the +config+ argument. All the ActiveRecord objects will use this connection
152
+ def self.ibm_db_connection(config)
153
+ # Attempts to load the Ruby driver IBM databases
154
+ # while not already loaded or raises LoadError in case of failure.
155
+ begin
156
+ require 'ibm_db' unless defined? IBM_DB
157
+ rescue LoadError
158
+ raise LoadError, "Failed to load IBM_DB Ruby driver."
159
+ end
160
+
161
+ if( config.has_key?(:parameterized) && config[:parameterized] == true )
162
+ require 'active_record/connection_adapters/ibm_db_pstmt'
163
+ end
164
+
165
+ # Check if class TableDefinition responds to indexes method to determine if we are on AR 3 or AR 4.
166
+ # This is a interim hack ti ensure backward compatibility. To remove as we move out of AR 3 support or have a better way to determine which version of AR being run against.
167
+ checkClass = ActiveRecord::ConnectionAdapters::TableDefinition.new(nil)
168
+ if(checkClass.respond_to?(:indexes))
169
+ isAr3 = false
170
+ else
171
+ isAr3 = true
172
+ end
173
+ # Converts all +config+ keys to symbols
174
+ config = config.symbolize_keys
175
+
176
+ # Flag to decide if quoted literal replcement should take place. By default it is ON. Set it to OFF if using Pstmt
177
+ set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_ON
178
+
179
+ # Retrieves database user credentials from the +config+ hash
180
+ # or raises ArgumentError in case of failure.
181
+ if !config.has_key?(:username) || !config.has_key?(:password)
182
+ raise ArgumentError, "Missing argument(s): Username/Password for #{config[:database]} is not specified"
183
+ else
184
+ if(config[:username].to_s.nil? || config[:password].to_s.nil?)
185
+ raise ArgumentError, "Username/Password cannot be nil"
186
+ end
187
+ username = config[:username].to_s
188
+ password = config[:password].to_s
189
+ end
190
+
191
+ if(config.has_key?(:dbops) && config[:dbops] == true)
192
+ return ConnectionAdapters::IBM_DBAdapter.new(nil, isAr3, logger, config, {})
193
+ end
194
+
195
+ # Retrieves the database alias (local catalog name) or remote name
196
+ # (for remote TCP/IP connections) from the +config+ hash
197
+ # or raises ArgumentError in case of failure.
198
+ if config.has_key?(:database)
199
+ database = config[:database].to_s
200
+ else
201
+ raise ArgumentError, "Missing argument: a database name needs to be specified."
202
+ end
203
+
204
+ # Providing default schema (username) when not specified
205
+ config[:schema] = config.has_key?(:schema) ? config[:schema].to_s : config[:username].to_s
206
+
207
+ if(config.has_key?(:parameterized) && config[:parameterized] == true )
208
+ set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_OFF
209
+ end
210
+
211
+ # Extract connection options from the database configuration
212
+ # (in support to formatting, audit and billing purposes):
213
+ # Retrieve database objects fields in lowercase
214
+ conn_options = {IBM_DB::ATTR_CASE => IBM_DB::CASE_LOWER}
215
+ config.each do |key, value|
216
+ if !value.nil?
217
+ case key
218
+ when :app_user # Set connection's user info
219
+ conn_options[IBM_DB::SQL_ATTR_INFO_USERID] = value
220
+ when :account # Set connection's account info
221
+ conn_options[IBM_DB::SQL_ATTR_INFO_ACCTSTR] = value
222
+ when :application # Set connection's application info
223
+ conn_options[IBM_DB::SQL_ATTR_INFO_APPLNAME] = value
224
+ when :workstation # Set connection's workstation info
225
+ conn_options[IBM_DB::SQL_ATTR_INFO_WRKSTNNAME] = value
226
+ end
227
+ end
228
+ end
229
+
230
+ begin
231
+ # Checks if a host name or address has been specified. If so, this implies a TCP/IP connection
232
+ # Returns IBM_DB.Connection object upon succesful DB connection to the database
233
+ # If otherwise the connection fails, +false+ is returned
234
+ if config.has_key?(:host)
235
+ # Retrieves the host address/name
236
+ host = config[:host]
237
+ # A net address connection requires a port. If no port has been specified, 50000 is used by default
238
+ port = config[:port] || 50000
239
+ # Connects to the database specified using the hostname, port, authentication type, username and password info
240
+ # Starting with DB2 9.1FP5 secure connections using SSL are supported.
241
+ # On the client side using CLI this is supported from CLI version V95FP2 and onwards.
242
+ # This feature is set by specifying SECURITY=SSL in the connection string.
243
+ # Below connection string is constructed and SECURITY parameter is appended if the user has specified the :security option
244
+ conn_string = "DRIVER={IBM DB2 ODBC DRIVER};\
245
+ DATABASE=#{database};\
246
+ HOSTNAME=#{host};\
247
+ PORT=#{port};\
248
+ PROTOCOL=TCPIP;\
249
+ UID=#{username};\
250
+ PWD=#{password};"
251
+ conn_string << "SECURITY=#{config[:security]};" if config.has_key?(:security)
252
+ conn_string << "AUTHENTICATION=#{config[:authentication]};" if config.has_key?(:authentication)
253
+ conn_string << "CONNECTTIMEOUT=#{config[:timeout]};" if config.has_key?(:timeout)
254
+
255
+ connection = IBM_DB.connect( conn_string, '', '', conn_options, set_quoted_literal_replacement )
256
+ else
257
+ # No host implies a local catalog-based connection: +database+ represents catalog alias
258
+ connection = IBM_DB.connect( database, username, password, conn_options, set_quoted_literal_replacement )
259
+ end
260
+ rescue StandardError => connect_err
261
+ raise "Failed to connect to [#{database}] due to: #{connect_err}"
262
+ end
263
+ # Verifies that the connection was successful
264
+ if connection
265
+ # Creates an instance of *IBM_DBAdapter* based on the +connection+
266
+ # and credentials provided in +config+
267
+ ConnectionAdapters::IBM_DBAdapter.new(connection, isAr3, logger, config, conn_options)
268
+ else
269
+ # If the connection failure was not caught previoulsy, it raises a Runtime error
270
+ raise "An unexpected error occured during connect attempt to [#{database}]"
271
+ end
272
+ end # method self.ibm_db_connection
273
+
274
+ def self.ibmdb_connection(config)
275
+ #Method to support alising of adapter name as ibmdb [without underscore]
276
+ self.ibm_db_connection(config)
277
+ end
278
+ end # class Base
279
+
280
+ module ConnectionAdapters
281
+ module SchemaStatements
282
+ def create_table_definition(name, temporary, options)
283
+ TableDefinition.new self, name, temporary, options
284
+ end
285
+ end
286
+ class IBM_DBColumn < Column
287
+
288
+ # Casts value (which is a String) to an appropriate instance
289
+ def type_cast(value)
290
+ # Casts the database NULL value to nil
291
+ return nil if value == 'NULL'
292
+ # Invokes parent's method for default casts
293
+ super
294
+ end
295
+
296
+ # Used to convert from BLOBs to Strings
297
+ def self.binary_to_string(value)
298
+ # Returns a string removing the eventual BLOB scalar function
299
+ value.to_s.gsub(/"SYSIBM"."BLOB"\('(.*)'\)/i,'\1')
300
+ end
301
+
302
+ private
303
+ # Mapping IBM data servers SQL datatypes to Ruby data types
304
+ def simplified_type(field_type)
305
+ case field_type
306
+ # if +field_type+ contains 'for bit data' handle it as a binary
307
+ when /for bit data/i
308
+ :binary
309
+ when /smallint/i
310
+ :boolean
311
+ when /int|serial/i
312
+ :integer
313
+ when /decimal|numeric|decfloat/i
314
+ :decimal
315
+ when /float|double|real/i
316
+ :float
317
+ when /timestamp|datetime/i
318
+ :timestamp
319
+ when /time/i
320
+ :time
321
+ when /date/i
322
+ :date
323
+ when /vargraphic/i
324
+ :vargraphic
325
+ when /graphic/i
326
+ :graphic
327
+ when /clob|text/i
328
+ :text
329
+ when /xml/i
330
+ :xml
331
+ when /blob|binary/i
332
+ :binary
333
+ when /char/i
334
+ :string
335
+ when /boolean/i
336
+ :boolean
337
+ when /rowid/i # rowid is a supported datatype on z/OS and i/5
338
+ :rowid
339
+ end
340
+ end # method simplified_type
341
+ end #class IBM_DBColumn
342
+
343
+ class Table
344
+
345
+ #Method to parse the passed arguments and create the ColumnDefinition object of the specified type
346
+ def ibm_parse_column_attributes_args(type, *args)
347
+ options = {}
348
+ if args.last.is_a?(Hash)
349
+ options = args.delete_at(args.length-1)
350
+ end
351
+ args.each do | name |
352
+ column name,type.to_sym,options
353
+ end # end args.each
354
+ end
355
+ private :ibm_parse_column_attributes_args
356
+
357
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type xml
358
+ #This method is different as compared to def char (sql is being issued explicitly
359
+ #as compared to def char where method column(which will generate the sql is being called)
360
+ #in order to handle the DEFAULT and NULL option for the native XML datatype
361
+ def xml(*args )
362
+ options = {}
363
+ if args.last.is_a?(Hash)
364
+ options = args.delete_at(args.length-1)
365
+ end
366
+ sql_segment = "ALTER TABLE #{@base.quote_table_name(@table_name)} ADD COLUMN "
367
+ args.each do | name |
368
+ sql = sql_segment + " #{@base.quote_column_name(name)} xml"
369
+ @base.execute(sql,"add_xml_column")
370
+ end
371
+ return self
372
+ end
373
+
374
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type double
375
+ def double(*args)
376
+ ibm_parse_column_attributes_args('double',*args)
377
+ return self
378
+ end
379
+
380
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type decfloat
381
+ def decfloat(*args)
382
+ ibm_parse_column_attributes_args('decfloat',*args)
383
+ return self
384
+ end
385
+
386
+ def graphic(*args)
387
+ ibm_parse_column_attributes_args('graphic',*args)
388
+ return self
389
+ end
390
+
391
+ def vargraphic(*args)
392
+ ibm_parse_column_attributes_args('vargraphic',*args)
393
+ return self
394
+ end
395
+
396
+ def bigint(*args)
397
+ ibm_parse_column_attributes_args('bigint',*args)
398
+ return self
399
+ end
400
+
401
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type char [character]
402
+ def char(*args)
403
+ ibm_parse_column_attributes_args('char',*args)
404
+ return self
405
+ end
406
+ alias_method :character, :char
407
+ end
408
+
409
+ class TableDefinition
410
+
411
+ def initialize(base, name=nil, temporary=nil, options=nil)
412
+ if(self.respond_to?(:indexes))
413
+ @ar3 = false
414
+ else
415
+ @ar3 = true
416
+ end
417
+ @columns = []
418
+ @columns_hash = {}
419
+ @indexes = {}
420
+ @base = base
421
+ @temporary = temporary
422
+ @options = options
423
+ @name = name
424
+ end
425
+
426
+ def native
427
+ @base.native_database_types
428
+ end
429
+
430
+ #Method to parse the passed arguments and create the ColumnDefinition object of the specified type
431
+ def ibm_parse_column_attributes_args(type, *args)
432
+ options = {}
433
+ if args.last.is_a?(Hash)
434
+ options = args.delete_at(args.length-1)
435
+ end
436
+ args.each do | name |
437
+ column(name,type,options)
438
+ end
439
+ end
440
+ private :ibm_parse_column_attributes_args
441
+
442
+ #Method to support the new syntax of rails 2.0 migrations for columns of type xml
443
+ def xml(*args )
444
+ ibm_parse_column_attributes_args('xml', *args)
445
+ return self
446
+ end
447
+
448
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type double
449
+ def double(*args)
450
+ ibm_parse_column_attributes_args('double',*args)
451
+ return self
452
+ end
453
+
454
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type decfloat
455
+ def decfloat(*args)
456
+ ibm_parse_column_attributes_args('decfloat',*args)
457
+ return self
458
+ end
459
+
460
+ def graphic(*args)
461
+ ibm_parse_column_attributes_args('graphic',*args)
462
+ return self
463
+ end
464
+
465
+ def vargraphic(*args)
466
+ ibm_parse_column_attributes_args('vargraphic',*args)
467
+ return self
468
+ end
469
+
470
+ def bigint(*args)
471
+ ibm_parse_column_attributes_args('bigint',*args)
472
+ return self
473
+ end
474
+
475
+ #Method to support the new syntax of rails 2.0 migrations (short-hand definitions) for columns of type char [character]
476
+ def char(*args)
477
+ ibm_parse_column_attributes_args('char',*args)
478
+ return self
479
+ end
480
+ alias_method :character, :char
481
+
482
+ # Overrides the abstract adapter in order to handle
483
+ # the DEFAULT option for the native XML datatype
484
+ def column(name, type, options ={})
485
+ # construct a column definition where @base is adaptor instance
486
+ if(@ar3)
487
+ column = ColumnDefinition.new(@base, name, type)
488
+ else
489
+ column = ColumnDefinition.new(name, type)
490
+ end
491
+ # DB2 does not accept DEFAULT NULL option for XML
492
+ # for table create, but does accept nullable option
493
+ unless type.to_s == 'xml'
494
+ column.null = options[:null]
495
+ column.default = options[:default]
496
+ else
497
+ column.null = options[:null]
498
+ # Override column object's (instance of ColumnDefinition structure)
499
+ # to_s which is expected to return the create_table SQL fragment
500
+ # and bypass DEFAULT NULL option while still appending NOT NULL
501
+ def column.to_s
502
+ sql = "#{base.quote_column_name(name)} #{type}"
503
+ unless self.null == nil
504
+ sql << " NOT NULL" if (self.null == false)
505
+ end
506
+ return sql
507
+ end
508
+ end
509
+
510
+ column.scale = options[:scale] if options[:scale]
511
+ column.precision = options[:precision] if options[:precision]
512
+ # append column's limit option and yield native limits
513
+ if options[:limit]
514
+ column.limit = options[:limit]
515
+ elsif @base.native_database_types[type.to_sym]
516
+ column.limit = @base.native_database_types[type.to_sym][:limit] if @base.native_database_types[type.to_sym].has_key? :limit
517
+ end
518
+
519
+ unless @columns.nil? or @columns.include? column
520
+ @columns << column
521
+ end
522
+
523
+ @columns_hash[name] = column
524
+
525
+ return self
526
+ end
527
+ end
528
+
529
+ # The IBM_DB Adapter requires the native Ruby driver (ibm_db)
530
+ # for IBM data servers (ibm_db.so).
531
+ # +config+ the hash passed as an initializer argument content:
532
+ # == mandatory parameters
533
+ # adapter: 'ibm_db' // IBM_DB Adapter name
534
+ # username: 'db2user' // data server (database) user
535
+ # password: 'secret' // data server (database) password
536
+ # database: 'ARUNIT' // remote database name (or catalog entry alias)
537
+ # == optional (highly recommended for data server auditing and monitoring purposes)
538
+ # schema: 'rails123' // name space qualifier
539
+ # account: 'tester' // OS account (client workstation)
540
+ # app_user: 'test11' // authenticated application user
541
+ # application: 'rtests' // application name
542
+ # workstation: 'plato' // client workstation name
543
+ # == remote TCP/IP connection (required when no local database catalog entry available)
544
+ # host: 'socrates' // fully qualified hostname or IP address
545
+ # port: '50000' // data server TCP/IP port number
546
+ # security: 'SSL' // optional parameter enabling SSL encryption -
547
+ # // - Available only from CLI version V95fp2 and above
548
+ # authentication: 'SERVER' // AUTHENTICATION type which the client uses -
549
+ # // - to connect to the database server. By default value is SERVER
550
+ # timeout: 10 // Specifies the time in seconds (0 - 32767) to wait for a reply from server -
551
+ # //- when trying to establish a connection before generating a timeout
552
+ # == Parameterized Queries Support
553
+ # parameterized: false // Specifies if the prepared statement support of
554
+ # //- the IBM_DB Adapter is to be turned on or off
555
+ #
556
+ # When schema is not specified, the username value is used instead.
557
+ # The default setting of parameterized is false.
558
+ #
559
+ class IBM_DBAdapter < AbstractAdapter
560
+ attr_reader :connection, :servertype
561
+ attr_accessor :sql,:handle_lobs_triggered, :sql_parameter_values
562
+ attr_reader :schema, :app_user, :account, :application, :workstation
563
+ attr_reader :pstmt_support_on, :set_quoted_literal_replacement
564
+
565
+ # Name of the adapter
566
+ def adapter_name
567
+ 'IBM_DB'
568
+ end
569
+
570
+ class BindSubstitution < Arel::Visitors::IBM_DB # :nodoc:
571
+ include Arel::Visitors::BindVisitor
572
+ end
573
+
574
+ def initialize(connection, ar3, logger, config, conn_options)
575
+ # Caching database connection configuration (+connect+ or +reconnect+ support)
576
+ @connection = connection
577
+ @isAr3 = ar3
578
+ @conn_options = conn_options
579
+ @database = config[:database]
580
+ @username = config[:username]
581
+ @password = config[:password]
582
+ if config.has_key?(:host)
583
+ @host = config[:host]
584
+ @port = config[:port] || 50000 # default port
585
+ end
586
+ @schema = config[:schema]
587
+ @security = config[:security] || nil
588
+ @authentication = config[:authentication] || nil
589
+ @timeout = config[:timeout] || 0 # default timeout value is 0
590
+
591
+ if( config.has_key?(:parameterized) && config[:parameterized] == true )
592
+ @pstmt_support_on = true
593
+ @set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_OFF
594
+ else
595
+ @pstmt_support_on = false
596
+ @set_quoted_literal_replacement = IBM_DB::QUOTED_LITERAL_REPLACEMENT_ON
597
+ end
598
+
599
+ @app_user = @account = @application = @workstation = nil
600
+ # Caching database connection options (auditing and billing support)
601
+ @app_user = conn_options[:app_user] if conn_options.has_key?(:app_user)
602
+ @account = conn_options[:account] if conn_options.has_key?(:account)
603
+ @application = conn_options[:application] if conn_options.has_key?(:application)
604
+ @workstation = conn_options[:workstation] if conn_options.has_key?(:workstation)
605
+
606
+ @sql = []
607
+ @sql_parameter_values = [] #Used only if pstmt support is turned on
608
+
609
+ @handle_lobs_triggered = false
610
+
611
+ # Calls the parent class +ConnectionAdapters+' initializer
612
+ # which sets @connection, @logger, @runtime and @last_verification
613
+ super(@connection, logger)
614
+
615
+ if @connection
616
+ server_info = IBM_DB.server_info( @connection )
617
+ if( server_info )
618
+ case server_info.DBMS_NAME
619
+ when /DB2\//i # DB2 for Linux, Unix and Windows (LUW)
620
+ case server_info.DBMS_VER
621
+ when /09.07/i # DB2 Version 9.7 (Cobra)
622
+ @servertype = IBM_DB2_LUW_COBRA.new(self, @isAr3)
623
+ when /10./i #DB2 version 10.1 and above
624
+ @servertype = IBM_DB2_LUW_COBRA.new(self, @isAr3)
625
+ else # DB2 Version 9.5 or below
626
+ @servertype = IBM_DB2_LUW.new(self, @isAr3)
627
+ end
628
+ when /DB2/i # DB2 for zOS
629
+ case server_info.DBMS_VER
630
+ when /09/ # DB2 for zOS version 9 and version 10
631
+ @servertype = IBM_DB2_ZOS.new(self, @isAr3)
632
+ when /10/
633
+ @servertype = IBM_DB2_ZOS.new(self, @isAr3)
634
+ when /08/ # DB2 for zOS version 8
635
+ @servertype = IBM_DB2_ZOS_8.new(self, @isAr3)
636
+ else # DB2 for zOS version 7
637
+ raise "Only DB2 z/OS version 8 and above are currently supported"
638
+ end
639
+ when /AS/i # DB2 for i5 (iSeries)
640
+ @servertype = IBM_DB2_I5.new(self, @isAr3)
641
+ when /IDS/i # Informix Dynamic Server
642
+ @servertype = IBM_IDS.new(self, @isAr3)
643
+ else
644
+ log( "server_info", "Forcing servertype to LUW: DBMS name could not be retrieved. Check if your client version is of the right level")
645
+ warn "Forcing servertype to LUW: DBMS name could not be retrieved. Check if your client version is of the right level"
646
+ @servertype = IBM_DB2_LUW.new(self, @isAr3)
647
+ end
648
+ else
649
+ error_msg = IBM_DB.getErrormsg( @connection, IBM_DB::DB_CONN )
650
+ IBM_DB.close( @connection )
651
+ raise "Cannot retrieve server information: #{error_msg}"
652
+ end
653
+ end
654
+
655
+ # Executes the +set schema+ statement using the schema identifier provided
656
+ @servertype.set_schema(@schema) if @schema && @schema != @username
657
+
658
+ # Check for the start value for id (primary key column). By default it is 1
659
+ if config.has_key?(:start_id)
660
+ @start_id = config[:start_id]
661
+ else
662
+ @start_id = 1
663
+ end
664
+
665
+ #Check Arel version
666
+ begin
667
+ @arelVersion = Arel::VERSION.to_i
668
+ rescue
669
+ @arelVersion = 0
670
+ end
671
+
672
+ if(@arelVersion >= 3 )
673
+ @visitor = Arel::Visitors::IBM_DB.new self
674
+ end
675
+ end
676
+
677
+ # Optional connection attribute: database name space qualifier
678
+ def schema=(name)
679
+ unless name == @schema
680
+ @schema = name
681
+ @servertype.set_schema(@schema)
682
+ end
683
+ end
684
+
685
+ # Optional connection attribute: authenticated application user
686
+ def app_user=(name)
687
+ unless name == @app_user
688
+ option = {IBM_DB::SQL_ATTR_INFO_USERID => "#{name}"}
689
+ if IBM_DB.set_option( @connection, option, 1 )
690
+ @app_user = IBM_DB.get_option( @connection, IBM_DB::SQL_ATTR_INFO_USERID, 1 )
691
+ end
692
+ end
693
+ end
694
+
695
+ # Optional connection attribute: OS account (client workstation)
696
+ def account=(name)
697
+ unless name == @account
698
+ option = {IBM_DB::SQL_ATTR_INFO_ACCTSTR => "#{name}"}
699
+ if IBM_DB.set_option( @connection, option, 1 )
700
+ @account = IBM_DB.get_option( @connection, IBM_DB::SQL_ATTR_INFO_ACCTSTR, 1 )
701
+ end
702
+ end
703
+ end
704
+
705
+ # Optional connection attribute: application name
706
+ def application=(name)
707
+ unless name == @application
708
+ option = {IBM_DB::SQL_ATTR_INFO_APPLNAME => "#{name}"}
709
+ if IBM_DB.set_option( @connection, option, 1 )
710
+ @application = IBM_DB.get_option( @connection, IBM_DB::SQL_ATTR_INFO_APPLNAME, 1 )
711
+ end
712
+ end
713
+ end
714
+
715
+ # Optional connection attribute: client workstation name
716
+ def workstation=(name)
717
+ unless name == @workstation
718
+ option = {IBM_DB::SQL_ATTR_INFO_WRKSTNNAME => "#{name}"}
719
+ if IBM_DB.set_option( @connection, option, 1 )
720
+ @workstation = IBM_DB.get_option( @connection, IBM_DB::SQL_ATTR_INFO_WRKSTNNAME, 1 )
721
+ end
722
+ end
723
+ end
724
+
725
+ def self.visitor_for(pool)
726
+ Arel::Visitors::IBM_DB.new(pool)
727
+ end
728
+
729
+ def to_sql(arel, binds = [])
730
+ if arel.respond_to?(:ast)
731
+ visitor.accept(arel.ast) do
732
+ quote(*binds.shift.reverse)
733
+ end
734
+ else
735
+ arel
736
+ end
737
+ end
738
+
739
+ # This adapter supports migrations.
740
+ # Current limitations:
741
+ # +rename_column+ is not currently supported by the IBM data servers
742
+ # +remove_column+ is not currently supported by the DB2 for zOS data server
743
+ # Tables containing columns of XML data type do not support +remove_column+
744
+ def supports_migrations?
745
+ true
746
+ end
747
+
748
+ # This Adapter supports DDL transactions.
749
+ # This means CREATE TABLE and other DDL statements can be carried out as a transaction.
750
+ # That is the statements executed can be ROLLED BACK in case of any error during the process.
751
+ def supports_ddl_transactions?
752
+ true
753
+ end
754
+
755
+ def log_query(sql, name) #:nodoc:
756
+ # Used by handle_lobs
757
+ log(sql,name){}
758
+ end
759
+
760
+ #==============================================
761
+ # CONNECTION MANAGEMENT
762
+ #==============================================
763
+
764
+ # Tests the connection status
765
+ def active?
766
+ IBM_DB.active @connection
767
+ rescue
768
+ false
769
+ end
770
+
771
+ # Private method used by +reconnect!+.
772
+ # It connects to the database with the initially provided credentials
773
+ def connect
774
+ # If the type of connection is net based
775
+ if(@username.nil? || @password.nil?)
776
+ raise ArgumentError, "Username/Password cannot be nil"
777
+ end
778
+
779
+ begin
780
+ if @host
781
+ @conn_string = "DRIVER={IBM DB2 ODBC DRIVER};\
782
+ DATABASE=#{@database};\
783
+ HOSTNAME=#{@host};\
784
+ PORT=#{@port};\
785
+ PROTOCOL=TCPIP;\
786
+ UID=#{@username};\
787
+ PWD=#{@password};"
788
+ @conn_string << "SECURITY=#{@security};" if @security
789
+ @conn_string << "AUTHENTICATION=#{@authentication};" if @authentication
790
+ @conn_string << "CONNECTTIMEOUT=#{@timeout};"
791
+ # Connects and assigns the resulting IBM_DB.Connection to the +@connection+ instance variable
792
+ @connection = IBM_DB.connect(@conn_string, '', '', @conn_options, @set_quoted_literal_replacement)
793
+ else
794
+ # Connects to the database using the local alias (@database)
795
+ # and assigns the connection object (IBM_DB.Connection) to @connection
796
+ @connection = IBM_DB.connect(@database, @username, @password, @conn_options, @set_quoted_literal_replacement)
797
+ end
798
+ rescue StandardError => connect_err
799
+ warn "Connection to database #{@database} failed: #{connect_err}"
800
+ @connection = false
801
+ end
802
+ # Sets the schema if different from default (username)
803
+ if @schema && @schema != @username
804
+ @servertype.set_schema(@schema)
805
+ end
806
+ end
807
+ private :connect
808
+
809
+ # Closes the current connection and opens a new one
810
+ def reconnect!
811
+ disconnect!
812
+ connect
813
+ end
814
+
815
+ # Closes the current connection
816
+ def disconnect!
817
+ # Attempts to close the connection. The methods will return:
818
+ # * true if succesfull
819
+ # * false if the connection is already closed
820
+ # * nil if an error is raised
821
+ return nil if @connection.nil? || @connection == false
822
+ IBM_DB.close(@connection) rescue nil
823
+ end
824
+
825
+ #==============================================
826
+ # DATABASE STATEMENTS
827
+ #==============================================
828
+
829
+ def create_table(name, options = {})
830
+ @servertype.setup_for_lob_table
831
+ super
832
+
833
+ #Table definition is complete only when a unique index is created on the primarykey column for DB2 V8 on zOS
834
+
835
+ #create index on id column if options[:id] is nil or id ==true
836
+ #else check if options[:primary_key]is not nil then create an unique index on that column
837
+ if !options[:id].nil? || !options[:primary_key].nil?
838
+ if (!options[:id].nil? && options[:id] == true)
839
+ @servertype.create_index_after_table(name,"id")
840
+ elsif !options[:primary_key].nil?
841
+ @servertype.create_index_after_table(name,options[:primary_key].to_s)
842
+ end
843
+ else
844
+ @servertype.create_index_after_table(name,"id")
845
+ end
846
+ end
847
+
848
+ # Returns an array of hashes with the column names as keys and
849
+ # column values as values. +sql+ is the select query,
850
+ # and +name+ is an optional description for logging
851
+ def prepared_select(sql_param_hash, name = nil)
852
+ # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
853
+
854
+ results = []
855
+ # Invokes the method +prepare+ in order prepare the SQL
856
+ # IBM_DB.Statement is returned from which the statement is executed and results fetched
857
+ pstmt = prepare(sql_param_hash["sqlSegment"], name)
858
+ if(execute_prepared_stmt(pstmt, sql_param_hash["paramArray"]))
859
+ begin
860
+ results = @servertype.select(pstmt)
861
+ rescue StandardError => fetch_error # Handle driver fetch errors
862
+ error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT )
863
+ if error_msg && !error_msg.empty?
864
+ raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
865
+ else
866
+ error_msg = "An unexpected error occurred during data retrieval"
867
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
868
+ raise error_msg
869
+ end
870
+ ensure
871
+ # Ensures to free the resources associated with the statement
872
+ IBM_DB.free_stmt(pstmt) if pstmt
873
+ end
874
+ end
875
+ # The array of record hashes is returned
876
+ results
877
+ end
878
+
879
+ # Returns an array of hashes with the column names as keys and
880
+ # column values as values. +sql+ is the select query,
881
+ # and +name+ is an optional description for logging
882
+ def prepared_select_values(sql_param_hash, name = nil)
883
+ # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
884
+ results = []
885
+ # Invokes the method +prepare+ in order prepare the SQL
886
+ # IBM_DB.Statement is returned from which the statement is executed and results fetched
887
+ pstmt = prepare(sql_param_hash["sqlSegment"], name)
888
+ if(execute_prepared_stmt(pstmt, sql_param_hash["paramArray"]))
889
+ begin
890
+ results = @servertype.select_rows(sql_param_hash["sqlSegment"], name, pstmt, results)
891
+ if results
892
+ return results.map { |v| v[0] }
893
+ else
894
+ nil
895
+ end
896
+ rescue StandardError => fetch_error # Handle driver fetch errors
897
+ error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT )
898
+ if error_msg && !error_msg.empty?
899
+ raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
900
+ else
901
+ error_msg = "An unexpected error occurred during data retrieval"
902
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
903
+ raise error_msg
904
+ end
905
+ ensure
906
+ # Ensures to free the resources associated with the statement
907
+ IBM_DB.free_stmt(pstmt) if pstmt
908
+ end
909
+ end
910
+ # The array of record hashes is returned
911
+ results
912
+ end
913
+
914
+ #Calls the servertype select method to fetch the data
915
+ def fetch_data(stmt)
916
+ if(stmt)
917
+ begin
918
+ return @servertype.select(stmt)
919
+ rescue StandardError => fetch_error # Handle driver fetch errors
920
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
921
+ if error_msg && !error_msg.empty?
922
+ raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
923
+ else
924
+ error_msg = "An unexpected error occurred during data retrieval"
925
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
926
+ raise error_msg
927
+ end
928
+ ensure
929
+ # Ensures to free the resources associated with the statement
930
+ IBM_DB.free_stmt(stmt) if stmt
931
+ end
932
+ end
933
+ end
934
+ =begin
935
+ # Returns an array of hashes with the column names as keys and
936
+ # column values as values. +sql+ is the select query,
937
+ # and +name+ is an optional description for logging
938
+ def select(sql, name = nil)
939
+ # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
940
+ sql.gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" )
941
+
942
+ results = []
943
+ # Invokes the method +execute+ in order to log and execute the SQL
944
+ # IBM_DB.Statement is returned from which results can be fetched
945
+ stmt = execute(sql, name)
946
+
947
+ results = fetch_data(stmt)
948
+ # The array of record hashes is returned
949
+ results
950
+ end
951
+ =end
952
+ def select(sql, name = nil, binds = [])
953
+ # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
954
+ sql.gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" )
955
+
956
+ results = []
957
+
958
+ if(binds.nil? || binds.empty?)
959
+ stmt = execute(sql, name)
960
+ else
961
+ stmt = exec_query(sql, name, binds)
962
+ end
963
+
964
+ cols = IBM_DB.resultCols(stmt)
965
+
966
+ if( stmt )
967
+ results = fetch_data(stmt)
968
+ end
969
+
970
+ if(@isAr3)
971
+ return results
972
+ else
973
+ return ActiveRecord::Result.new(cols, results)
974
+ end
975
+ end
976
+
977
+ #Returns an array of arrays containing the field values.
978
+ #This is an implementation for the abstract method
979
+ #+sql+ is the select query and +name+ is an optional description for logging
980
+ def select_rows(sql, name = nil)
981
+ # Replaces {"= NULL" with " IS NULL"} OR {"IN (NULL)" with " IS NULL"}
982
+ sql.gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" )
983
+
984
+ results = []
985
+ # Invokes the method +execute+ in order to log and execute the SQL
986
+ # IBM_DB.Statement is returned from which results can be fetched
987
+ stmt = execute(sql, name)
988
+ if(stmt)
989
+ begin
990
+ results = @servertype.select_rows(sql, name, stmt, results)
991
+ rescue StandardError => fetch_error # Handle driver fetch errors
992
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
993
+ if error_msg && !error_msg.empty?
994
+ raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
995
+ else
996
+ error_msg = "An unexpected error occurred during data retrieval"
997
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
998
+ raise error_msg
999
+ end
1000
+ ensure
1001
+ # Ensures to free the resources associated with the statement
1002
+ IBM_DB.free_stmt(stmt) if stmt
1003
+ end
1004
+ end
1005
+ # The array of record hashes is returned
1006
+ results
1007
+ end
1008
+
1009
+ # Returns a record hash with the column names as keys and column values
1010
+ # as values.
1011
+ #def select_one(sql, name = nil)
1012
+ # Gets the first hash from the array of hashes returned by
1013
+ # select_all
1014
+ # select_all(sql,name).first
1015
+ #end
1016
+
1017
+ #inserts values from fixtures
1018
+ #overridden to handle LOB's fixture insertion, as, in normal inserts callbacks are triggered but during fixture insertion callbacks are not triggered
1019
+ #hence only markers like @@@IBMBINARY@@@ will be inserted and are not updated to actual data
1020
+ def insert_fixture(fixture, table_name)
1021
+ if(fixture.respond_to?(:keys))
1022
+ insert_query = "INSERT INTO #{quote_table_name(table_name)} ( #{fixture.keys.join(', ')})"
1023
+ else
1024
+ insert_query = "INSERT INTO #{quote_table_name(table_name)} ( #{fixture.key_list})"
1025
+ end
1026
+
1027
+ insert_values = []
1028
+ params = []
1029
+ if @servertype.instance_of? IBM_IDS
1030
+ super
1031
+ return
1032
+ end
1033
+ column_list = columns(table_name)
1034
+ fixture.each do |item|
1035
+ col = nil
1036
+ column_list.each do |column|
1037
+ if column.name.downcase == item.at(0).downcase
1038
+ col= column
1039
+ break
1040
+ end
1041
+ end
1042
+ if item.at(1).nil? ||
1043
+ item.at(1) == {} ||
1044
+ (item.at(1) == '' && !(col.type.to_sym == :text))
1045
+
1046
+ params << 'NULL'
1047
+
1048
+ elsif col.type.to_sym == :xml ||
1049
+ col.type.to_sym == :text ||
1050
+ col.type.to_sym == :binary
1051
+ # Add a '?' for the parameter or a NULL if the value is nil or empty
1052
+ # (except for a CLOB field where '' can be a value)
1053
+ insert_values << quote_value_for_pstmt(item.at(1))
1054
+ params << '?'
1055
+ else
1056
+ insert_values << quote_value_for_pstmt(item.at(1),col)
1057
+ params << '?'
1058
+ end
1059
+ end
1060
+
1061
+ insert_query << " VALUES ("+ params.join(',') + ")"
1062
+ unless stmt = IBM_DB.prepare(@connection, insert_query)
1063
+ error_msg = IBM_DB.getErrormsg( @connection, IBM_DB::DB_CONN )
1064
+ if error_msg && !error_msg.empty?
1065
+ raise "Failed to prepare statement for fixtures insert due to : #{error_msg}"
1066
+ else
1067
+ raise StandardError.new('An unexpected error occurred during preparing SQL for fixture insert')
1068
+ end
1069
+ end
1070
+
1071
+ #log_query(insert_query,'fixture insert')
1072
+ log(insert_query,'fixture insert') do
1073
+ unless IBM_DB.execute(stmt, insert_values)
1074
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
1075
+ IBM_DB.free_stmt(stmt) if stmt
1076
+ raise "Failed to insert due to: #{error_msg}"
1077
+ else
1078
+ IBM_DB.free_stmt(stmt) if stmt
1079
+ end
1080
+ end
1081
+ end
1082
+
1083
+ def empty_insert_statement_value(pkey)
1084
+ "(#{pkey}) VALUES (DEFAULT)"
1085
+ end
1086
+
1087
+ # Perform an insert and returns the last ID generated.
1088
+ # This can be the ID passed to the method or the one auto-generated by the database,
1089
+ # and retrieved by the +last_generated_id+ method.
1090
+ def insert_direct(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
1091
+ if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
1092
+ @sql = []
1093
+ @handle_lobs_triggered = false
1094
+ end
1095
+
1096
+ clear_query_cache if defined? clear_query_cache
1097
+
1098
+ if stmt = execute(sql, name)
1099
+ begin
1100
+ @sql << sql
1101
+ return id_value || @servertype.last_generated_id(stmt)
1102
+ # Ensures to free the resources associated with the statement
1103
+ ensure
1104
+ IBM_DB.free_stmt(stmt) if stmt
1105
+ end
1106
+ end
1107
+ end
1108
+
1109
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [] )
1110
+ sql, binds = [to_sql(arel),binds]
1111
+
1112
+ #unless IBM_DBAdapter.respond_to?(:exec_insert)
1113
+ if binds.nil? || binds.empty?
1114
+ return insert_direct(sql, name, pk, id_value, sequence_name)
1115
+ end
1116
+
1117
+ clear_query_cache if defined? clear_query_cache
1118
+ if stmt = exec_insert(sql, name, binds)
1119
+ begin
1120
+ @sql << sql
1121
+ return id_value || @servertype.last_generated_id(stmt)
1122
+ ensure
1123
+ IBM_DB.free_stmt(stmt) if stmt
1124
+ end
1125
+ end
1126
+ end
1127
+
1128
+ # Praveen
1129
+ # Performs an insert using the prepared statement and returns the last ID generated.
1130
+ # This can be the ID passed to the method or the one auto-generated by the database,
1131
+ # and retrieved by the +last_generated_id+ method.
1132
+ def prepared_insert(pstmt, param_array = nil, id_value = nil)
1133
+ if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
1134
+ @sql = []
1135
+ @sql_parameter_values = []
1136
+ @handle_lobs_triggered = false
1137
+ end
1138
+
1139
+ clear_query_cache if defined? clear_query_cache
1140
+
1141
+ begin
1142
+ if execute_prepared_stmt(pstmt, param_array)
1143
+ @sql << @prepared_sql
1144
+ @sql_parameter_values << param_array
1145
+ return id_value || @servertype.last_generated_id(pstmt)
1146
+ end
1147
+ rescue StandardError => insert_err
1148
+ raise insert_err
1149
+ ensure
1150
+ IBM_DB.free_stmt(pstmt) if pstmt
1151
+ end
1152
+ end
1153
+
1154
+ # Praveen
1155
+ # Prepares and logs +sql+ commands and
1156
+ # returns a +IBM_DB.Statement+ object.
1157
+ def prepare(sql,name = nil)
1158
+ # The +log+ method is defined in the parent class +AbstractAdapter+
1159
+ @prepared_sql = sql
1160
+ log(sql,name) do
1161
+ @servertype.prepare(sql, name)
1162
+ end
1163
+ end
1164
+
1165
+ # Praveen
1166
+ #Executes the prepared statement
1167
+ #ReturnsTrue on success and False on Failure
1168
+ def execute_prepared_stmt(pstmt, param_array = nil)
1169
+ if !param_array.nil? && param_array.size < 1
1170
+ param_array = nil
1171
+ end
1172
+
1173
+ if( !IBM_DB.execute(pstmt, param_array) )
1174
+ error_msg = IBM_DB.getErrormsg(pstmt, IBM_DB::DB_STMT)
1175
+ if !error_msg.empty?
1176
+ error_msg = "Statement execution failed: " + error_msg
1177
+ else
1178
+ error_msg = "Statement execution failed"
1179
+ end
1180
+ IBM_DB.free_stmt(pstmt) if pstmt
1181
+ raise StatementInvalid, error_msg
1182
+ else
1183
+ return true
1184
+ end
1185
+ end
1186
+
1187
+ # Executes +sql+ statement in the context of this connection using
1188
+ # +binds+ as the bind substitutes. +name+ is logged along with
1189
+ # the executed +sql+ statement.
1190
+ def exec_query(sql, name = 'SQL', binds = [])
1191
+ begin
1192
+ param_array = binds.map do |column,value|
1193
+ quote_value_for_pstmt(value, column)
1194
+ end
1195
+
1196
+ stmt = prepare(sql, name)
1197
+
1198
+ if( stmt )
1199
+ if(execute_prepared_stmt(stmt, param_array))
1200
+ return stmt
1201
+ end
1202
+ else
1203
+ return false
1204
+ end
1205
+ ensure
1206
+ @offset = @limit = nil
1207
+ end
1208
+ end
1209
+
1210
+ # Executes and logs +sql+ commands and
1211
+ # returns a +IBM_DB.Statement+ object.
1212
+ def execute(sql, name = nil)
1213
+ # Logs and execute the sql instructions.
1214
+ # The +log+ method is defined in the parent class +AbstractAdapter+
1215
+ log(sql, name) do
1216
+ @servertype.execute(sql, name)
1217
+ end
1218
+ end
1219
+
1220
+ # Executes an "UPDATE" SQL statement
1221
+ def update_direct(sql, name = nil)
1222
+ if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
1223
+ @sql = []
1224
+ @handle_lobs_triggered = false
1225
+ end
1226
+
1227
+ # Logs and execute the given sql query.
1228
+ if stmt = execute(sql, name)
1229
+ begin
1230
+ @sql << sql
1231
+ # Retrieves the number of affected rows
1232
+ IBM_DB.num_rows(stmt)
1233
+ # Ensures to free the resources associated with the statement
1234
+ ensure
1235
+ IBM_DB.free_stmt(stmt) if stmt
1236
+ end
1237
+ end
1238
+ end
1239
+
1240
+ #Praveen
1241
+ def prepared_update(pstmt, param_array = nil )
1242
+ if @handle_lobs_triggered #Ensure the array of sql is cleared if they have been handled in the callback
1243
+ @sql = []
1244
+ @sql_parameter_values = []
1245
+ @handle_lobs_triggered = false
1246
+ end
1247
+
1248
+ clear_query_cache if defined? clear_query_cache
1249
+
1250
+ begin
1251
+ if execute_prepared_stmt(pstmt, param_array)
1252
+ @sql << @prepared_sql
1253
+ @sql_parameter_values << param_array
1254
+ # Retrieves the number of affected rows
1255
+ IBM_DB.num_rows(pstmt)
1256
+ # Ensures to free the resources associated with the statement
1257
+ end
1258
+ rescue StandardError => updt_err
1259
+ raise updt_err
1260
+ ensure
1261
+ IBM_DB.free_stmt(pstmt) if pstmt
1262
+ end
1263
+ end
1264
+ # The delete method executes the delete
1265
+ # statement and returns the number of affected rows.
1266
+ # The method is an alias for +update+
1267
+ alias_method :prepared_delete, :prepared_update
1268
+
1269
+ def update(arel, name = nil, binds = [])
1270
+ sql = to_sql(arel)
1271
+
1272
+ # Make sure the WHERE clause handles NULL's correctly
1273
+ sqlarray = sql.split(/\s*WHERE\s*/)
1274
+ size = sqlarray.size
1275
+ if size > 1
1276
+ sql = sqlarray[0] + " WHERE "
1277
+ if size > 2
1278
+ 1.upto size-2 do |index|
1279
+ sqlarray[index].gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" ) unless sqlarray[index].nil?
1280
+ sql = sql + sqlarray[index] + " WHERE "
1281
+ end
1282
+ end
1283
+ sqlarray[size-1].gsub!( /(=\s*NULL|IN\s*\(NULL\))/i, " IS NULL" ) unless sqlarray[size-1].nil?
1284
+ sql = sql + sqlarray[size-1]
1285
+ end
1286
+
1287
+ clear_query_cache if defined? clear_query_cache
1288
+
1289
+ if binds.nil? || binds.empty?
1290
+ update_direct(sql, name)
1291
+ else
1292
+ begin
1293
+ if stmt = exec_query(sql,name,binds)
1294
+ IBM_DB.num_rows(stmt)
1295
+ end
1296
+ ensure
1297
+ IBM_DB.free_stmt(stmt) if(stmt)
1298
+ end
1299
+ end
1300
+ end
1301
+
1302
+ alias_method :delete, :update
1303
+
1304
+ # Begins the transaction (and turns off auto-committing)
1305
+ def begin_db_transaction
1306
+ # Turns off the auto-commit
1307
+ IBM_DB.autocommit(@connection, IBM_DB::SQL_AUTOCOMMIT_OFF)
1308
+ end
1309
+
1310
+ # Commits the transaction and turns on auto-committing
1311
+ def commit_db_transaction
1312
+ # Commits the transaction
1313
+ IBM_DB.commit @connection rescue nil
1314
+ # Turns on auto-committing
1315
+ IBM_DB.autocommit @connection, IBM_DB::SQL_AUTOCOMMIT_ON
1316
+ end
1317
+
1318
+ # Rolls back the transaction and turns on auto-committing. Must be
1319
+ # done if the transaction block raises an exception or returns false
1320
+ def rollback_db_transaction
1321
+ # ROLLBACK the transaction
1322
+ IBM_DB.rollback(@connection) rescue nil
1323
+ # Turns on auto-committing
1324
+ IBM_DB.autocommit @connection, IBM_DB::SQL_AUTOCOMMIT_ON
1325
+ end
1326
+
1327
+
1328
+ # Modifies a sql statement in order to implement a LIMIT and an OFFSET.
1329
+ # A LIMIT defines the number of rows that should be fetched, while
1330
+ # an OFFSET defines from what row the records must be fetched.
1331
+ # IBM data servers implement a LIMIT in SQL statements through:
1332
+ # FETCH FIRST n ROWS ONLY, where n is the number of rows required.
1333
+ # The implementation of OFFSET is more elaborate, and requires the usage of
1334
+ # subqueries and the ROW_NUMBER() command in order to add row numbering
1335
+ # as an additional column to a copy of the existing table.
1336
+ # ==== Examples
1337
+ # add_limit_offset!('SELECT * FROM staff', {:limit => 10})
1338
+ # generates: "SELECT * FROM staff FETCH FIRST 10 ROWS ONLY"
1339
+ #
1340
+ # add_limit_offset!('SELECT * FROM staff', {:limit => 10, :offset => 30})
1341
+ # generates "SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_rownum
1342
+ # FROM (SELECT * FROM staff) AS I) AS O WHERE sys_row_num BETWEEN 31 AND 40"
1343
+ def add_limit_offset!(sql, options)
1344
+ limit = options[:limit]
1345
+ offset = options[:offset]
1346
+
1347
+ # if the limit is zero
1348
+ if limit && limit == 0
1349
+ # Returns a query that will always generate zero records
1350
+ # (e.g. WHERE sys_row_num BETWEEN 1 and 0)
1351
+ if( @pstmt_support_on )
1352
+ sql = @servertype.query_offset_limit!(sql, 0, limit, options)
1353
+ else
1354
+ sql = @servertype.query_offset_limit(sql, 0, limit)
1355
+ end
1356
+ # If there is a non-zero limit
1357
+ else
1358
+ # If an offset is specified builds the query with offset and limit,
1359
+ # otherwise retrieves only the first +limit+ rows
1360
+ if( @pstmt_support_on )
1361
+ sql = @servertype.query_offset_limit!(sql, offset, limit, options)
1362
+ else
1363
+ sql = @servertype.query_offset_limit(sql, offset, limit)
1364
+ end
1365
+ end
1366
+ # Returns the sql query in any case
1367
+ sql
1368
+ end # method add_limit_offset!
1369
+
1370
+ def default_sequence_name(table, column) # :nodoc:
1371
+ "#{table}_#{column}_seq"
1372
+ end
1373
+
1374
+
1375
+ #==============================================
1376
+ # QUOTING
1377
+ #==============================================
1378
+
1379
+ # Quote date/time values for use in SQL input.
1380
+ # Includes microseconds, if the value is a Time responding to usec.
1381
+ def quoted_date(value) #:nodoc:
1382
+ if value.respond_to?(:usec)
1383
+ "#{super}.#{sprintf("%06d", value.usec)}"
1384
+ else
1385
+ super
1386
+ end
1387
+ end
1388
+
1389
+ def quote_value_for_pstmt(value, column=nil)
1390
+
1391
+ return value.quoted_id if value.respond_to?(:quoted_id)
1392
+
1393
+ case value
1394
+ when String, ActiveSupport::Multibyte::Chars then
1395
+ value = value.to_s
1396
+ if column && [:integer, :float].include?(column.type)
1397
+ value = column.type == :integer ? value.to_i : value.to_f
1398
+ value
1399
+ else
1400
+ value
1401
+ end
1402
+ when NilClass then nil
1403
+ when TrueClass then 1
1404
+ when FalseClass then 0
1405
+ when Float, Fixnum, Bignum then value
1406
+ # BigDecimals need to be output in a non-normalized form and quoted.
1407
+ when BigDecimal then value.to_s('F')
1408
+ when Numeric, Symbol then value.to_s
1409
+ else
1410
+ if value.acts_like?(:date) || value.acts_like?(:time)
1411
+ quoted_date(value)
1412
+ else
1413
+ value.to_yaml
1414
+ end
1415
+ end
1416
+ end
1417
+
1418
+ # Properly quotes the various data types.
1419
+ # +value+ contains the data, +column+ is optional and contains info on the field
1420
+ def quote(value, column = nil)
1421
+ return value.quoted_id if value.respond_to?(:quoted_id)
1422
+
1423
+ case value
1424
+ # If it's a numeric value and the column type is not a string, it shouldn't be quoted
1425
+ # (IBM_DB doesn't accept quotes on numeric types)
1426
+ when Numeric
1427
+ # If the column type is text or string, return the quote value
1428
+ if column && column.type.to_sym == :text || column && column.type.to_sym == :string
1429
+ unless caller[0] =~ /insert_fixture/i
1430
+ "'#{value}'"
1431
+ else
1432
+ "#{value}"
1433
+ end
1434
+ else
1435
+ # value is Numeric, column.type is not a string,
1436
+ # therefore it converts the number to string without quoting it
1437
+ value.to_s
1438
+ end
1439
+ when String, ActiveSupport::Multibyte::Chars
1440
+ if column && column.type.to_sym == :binary && !(column.sql_type =~ /for bit data/i)
1441
+ # If quoting is required for the insert/update of a BLOB
1442
+ unless caller[0] =~ /add_column_options/i
1443
+ # Invokes a convertion from string to binary
1444
+ @servertype.set_binary_value
1445
+ else
1446
+ # Quoting required for the default value of a column
1447
+ @servertype.set_binary_default(value)
1448
+ end
1449
+ elsif column && column.type.to_sym == :text
1450
+ unless caller[0] =~ /add_column_options/i
1451
+ "'@@@IBMTEXT@@@'"
1452
+ else
1453
+ @servertype.set_text_default(quote_string(value))
1454
+ end
1455
+ elsif column && column.type.to_sym == :xml
1456
+ unless caller[0] =~ /add_column_options/i
1457
+ "'<ibm>@@@IBMXML@@@</ibm>'"
1458
+ else
1459
+ "#{value}"
1460
+ end
1461
+ else
1462
+ unless caller[0] =~ /insert_fixture/i
1463
+ super
1464
+ else
1465
+ "#{value}"
1466
+ end
1467
+ end
1468
+ when TrueClass then quoted_true # return '1' for true
1469
+ when FalseClass then quoted_false # return '0' for false
1470
+ when nil then "NULL"
1471
+ when Date, Time then "'#{quoted_date(value)}'"
1472
+ when Symbol then "'#{quote_string(value.to_s)}'"
1473
+ else
1474
+ unless caller[0] =~ /insert_fixture/i
1475
+ "'#{quote_string(YAML.dump(value))}'"
1476
+ else
1477
+ "#{quote_string(YAML.dump(value))}"
1478
+ end
1479
+ end
1480
+ end
1481
+
1482
+ # Quotes a given string, escaping single quote (') characters.
1483
+ def quote_string(string)
1484
+ string.gsub(/'/, "''")
1485
+ end
1486
+
1487
+ # *true* is represented by a smallint 1, *false*
1488
+ # by 0, as no native boolean type exists in DB2.
1489
+ # Numerics are not quoted in DB2.
1490
+ def quoted_true
1491
+ "1"
1492
+ end
1493
+
1494
+ def quoted_false
1495
+ "0"
1496
+ end
1497
+
1498
+ def quote_column_name(name)
1499
+ @servertype.check_reserved_words(name)
1500
+ end
1501
+
1502
+ #==============================================
1503
+ # SCHEMA STATEMENTS
1504
+ #==============================================
1505
+
1506
+ # Returns a Hash of mappings from the abstract data types to the native
1507
+ # database types
1508
+ def native_database_types
1509
+ {
1510
+ :primary_key => { :name => @servertype.primary_key_definition(@start_id)},
1511
+ :string => { :name => "varchar", :limit => 255 },
1512
+ :text => { :name => "clob" },
1513
+ :integer => { :name => "integer" },
1514
+ :float => { :name => "float" },
1515
+ :datetime => { :name => @servertype.get_datetime_mapping },
1516
+ :timestamp => { :name => @servertype.get_datetime_mapping },
1517
+ :time => { :name => @servertype.get_time_mapping },
1518
+ :date => { :name => "date" },
1519
+ :binary => { :name => "blob" },
1520
+
1521
+ # IBM data servers don't have a native boolean type.
1522
+ # A boolean can be represented by a smallint,
1523
+ # adopting the convention that False is 0 and True is 1
1524
+ :boolean => { :name => "smallint"},
1525
+ :xml => { :name => "xml"},
1526
+ :decimal => { :name => "decimal" },
1527
+ :rowid => { :name => "rowid" }, # rowid is a supported datatype on z/OS and i/5
1528
+ :serial => { :name => "serial" }, # rowid is a supported datatype on Informix Dynamic Server
1529
+ :char => { :name => "char" },
1530
+ :double => { :name => @servertype.get_double_mapping },
1531
+ :decfloat => { :name => "decfloat"},
1532
+ :graphic => { :name => "graphic", :limit => 1},
1533
+ :vargraphic => { :name => "vargraphic", :limit => 1},
1534
+ :bigint => { :name => "bigint"}
1535
+ }
1536
+ end
1537
+
1538
+ def build_conn_str_for_dbops()
1539
+ connect_str = "DRIVER={IBM DB2 ODBC DRIVER};ATTACH=true;"
1540
+ if(!@host.nil?)
1541
+ connect_str << "HOSTNAME=#{@host};"
1542
+ connect_str << "PORT=#{@port};"
1543
+ connect_str << "PROTOCOL=TCPIP;"
1544
+ end
1545
+ connect_str << "UID=#{@username};PWD=#{@password};"
1546
+ return connect_str
1547
+ end
1548
+
1549
+ def drop_database(dbName)
1550
+ connect_str = build_conn_str_for_dbops()
1551
+
1552
+ #Ensure connection is closed before trying to drop a database.
1553
+ #As a connect call would have been made by call seeing connection in active
1554
+ disconnect!
1555
+
1556
+ begin
1557
+ dropConn = IBM_DB.connect(connect_str, '', '')
1558
+ rescue StandardError => connect_err
1559
+ raise "Failed to connect to server due to: #{connect_err}"
1560
+ end
1561
+
1562
+ if(IBM_DB.dropDB(dropConn,dbName))
1563
+ IBM_DB.close(dropConn)
1564
+ return true
1565
+ else
1566
+ error = IBM_DB.getErrormsg(dropConn, IBM_DB::DB_CONN)
1567
+ IBM_DB.close(dropConn)
1568
+ raise "Could not drop Database due to: #{error}"
1569
+ end
1570
+ end
1571
+
1572
+ def create_database(dbName, codeSet=nil, mode=nil)
1573
+ connect_str = build_conn_str_for_dbops()
1574
+
1575
+ #Ensure connection is closed before trying to drop a database.
1576
+ #As a connect call would have been made by call seeing connection in active
1577
+ disconnect!
1578
+
1579
+ begin
1580
+ createConn = IBM_DB.connect(connect_str, '', '')
1581
+ rescue StandardError => connect_err
1582
+ raise "Failed to connect to server due to: #{connect_err}"
1583
+ end
1584
+
1585
+ if(IBM_DB.createDB(createConn,dbName,codeSet,mode))
1586
+ IBM_DB.close(createConn)
1587
+ return true
1588
+ else
1589
+ error = IBM_DB.getErrormsg(createConn, IBM_DB::DB_CONN)
1590
+ IBM_DB.close(createConn)
1591
+ raise "Could not create Database due to: #{error}"
1592
+ end
1593
+ end
1594
+
1595
+ # IBM data servers do not support limits on certain data types (unlike MySQL)
1596
+ # Limit is supported for the {float, decimal, numeric, varchar, clob, blob, graphic, vargraphic} data types.
1597
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
1598
+ if type.to_sym == :decfloat
1599
+ sql_segment = native_database_types[type.to_sym][:name].to_s
1600
+ sql_segment << "(#{precision})" if !precision.nil?
1601
+ return sql_segment
1602
+ end
1603
+
1604
+ return super if limit.nil?
1605
+
1606
+ # strip off limits on data types not supporting them
1607
+ if @servertype.limit_not_supported_types.include? type.to_sym
1608
+ return native_database_types[type.to_sym][:name].to_s
1609
+ elsif type.to_sym == :boolean
1610
+ return "smallint"
1611
+ else
1612
+ return super
1613
+ end
1614
+ end
1615
+
1616
+ # Returns the maximum length a table alias identifier can be.
1617
+ # IBM data servers (cross-platform) table limit is 128 characters
1618
+ def table_alias_length
1619
+ 128
1620
+ end
1621
+
1622
+ # Retrieves table's metadata for a specified shema name
1623
+ def tables(name = nil)
1624
+ # Initializes the tables array
1625
+ tables = []
1626
+ # Retrieve table's metadata through IBM_DB driver
1627
+ stmt = IBM_DB.tables(@connection, nil,
1628
+ @servertype.set_case(@schema))
1629
+ if(stmt)
1630
+ begin
1631
+ # Fetches all the records available
1632
+ while tab = IBM_DB.fetch_assoc(stmt)
1633
+ # Adds the lowercase table name to the array
1634
+ if(tab["table_type"]== 'TABLE') #check, so that only tables are dumped,IBM_DB.tables also returns views,alias etc in the schema
1635
+ tables << tab["table_name"].downcase
1636
+ end
1637
+ end
1638
+ rescue StandardError => fetch_error # Handle driver fetch errors
1639
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
1640
+ if error_msg && !error_msg.empty?
1641
+ raise "Failed to retrieve table metadata during fetch: #{error_msg}"
1642
+ else
1643
+ error_msg = "An unexpected error occurred during retrieval of table metadata"
1644
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1645
+ raise error_msg
1646
+ end
1647
+ ensure
1648
+ IBM_DB.free_stmt(stmt) if stmt # Free resources associated with the statement
1649
+ end
1650
+ else # Handle driver execution errors
1651
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN )
1652
+ if error_msg && !error_msg.empty?
1653
+ raise "Failed to retrieve tables metadata due to error: #{error_msg}"
1654
+ else
1655
+ raise StandardError.new('An unexpected error occurred during retrieval of table metadata')
1656
+ end
1657
+ end
1658
+ # Returns the tables array
1659
+ return tables
1660
+ end
1661
+
1662
+ # Returns the primary key of the mentioned table
1663
+ def primary_key(table_name)
1664
+ pk_name = nil
1665
+ stmt = IBM_DB.primary_keys( @connection, nil,
1666
+ @servertype.set_case(@schema),
1667
+ @servertype.set_case(table_name))
1668
+ if(stmt)
1669
+ begin
1670
+ if ( pk_index_row = IBM_DB.fetch_array(stmt) )
1671
+ pk_name = pk_index_row[3].downcase
1672
+ end
1673
+ rescue StandardError => fetch_error # Handle driver fetch errors
1674
+ error_msg = IBM_DB.getErrormsg( stmt, IBM_DB::DB_STMT )
1675
+ if error_msg && !error_msg.empty?
1676
+ raise "Failed to retrieve primarykey metadata during fetch: #{error_msg}"
1677
+ else
1678
+ error_msg = "An unexpected error occurred during retrieval of primary key metadata"
1679
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1680
+ raise error_msg
1681
+ end
1682
+ ensure # Free resources associated with the statement
1683
+ IBM_DB.free_stmt(stmt) if stmt
1684
+ end
1685
+ else
1686
+ error_msg = IBM_DB.getErrormsg( @connection, IBM_DB::DB_CONN )
1687
+ if error_msg && !error_msg.empty?
1688
+ raise "Failed to retrieve primary key metadata due to error: #{error_msg}"
1689
+ else
1690
+ raise StandardError.new('An unexpected error occurred during primary key retrieval')
1691
+ end
1692
+ end
1693
+ return pk_name
1694
+ end
1695
+
1696
+ # Returns an array of non-primary key indexes for a specified table name
1697
+ def indexes(table_name, name = nil)
1698
+ # to_s required because +table_name+ may be a symbol.
1699
+ table_name = table_name.to_s
1700
+ # Checks if a blank table name has been given.
1701
+ # If so it returns an empty array of columns.
1702
+ return [] if table_name.strip.empty?
1703
+
1704
+ indexes = []
1705
+ pk_index = nil
1706
+ index_schema = []
1707
+
1708
+ #fetch the primary keys of the table using function primary_keys
1709
+ #TABLE_SCHEM:: pk_index[1]
1710
+ #TABLE_NAME:: pk_index[2]
1711
+ #COLUMN_NAME:: pk_index[3]
1712
+ #PK_NAME:: pk_index[5]
1713
+ stmt = IBM_DB.primary_keys( @connection, nil,
1714
+ @servertype.set_case(@schema),
1715
+ @servertype.set_case(table_name))
1716
+ if(stmt)
1717
+ begin
1718
+ while ( pk_index_row = IBM_DB.fetch_array(stmt) )
1719
+ if pk_index_row[5]
1720
+ pk_index_name = pk_index_row[5].downcase
1721
+ pk_index_columns = [pk_index_row[3].downcase] # COLUMN_NAME
1722
+ if pk_index
1723
+ pk_index.columns = pk_index.columns + pk_index_columns
1724
+ else
1725
+ pk_index = IndexDefinition.new(table_name, pk_index_name, true, pk_index_columns)
1726
+ end
1727
+ end
1728
+ end
1729
+ rescue StandardError => fetch_error # Handle driver fetch errors
1730
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
1731
+ if error_msg && !error_msg.empty?
1732
+ raise "Failed to retrieve primarykey metadata during fetch: #{error_msg}"
1733
+ else
1734
+ error_msg = "An unexpected error occurred during retrieval of primary key metadata"
1735
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1736
+ raise error_msg
1737
+ end
1738
+ ensure # Free resources associated with the statement
1739
+ IBM_DB.free_stmt(stmt) if stmt
1740
+ end
1741
+ else # Handle driver execution errors
1742
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN )
1743
+ if error_msg && !error_msg.empty?
1744
+ raise "Failed to retrieve primary key metadata due to error: #{error_msg}"
1745
+ else
1746
+ raise StandardError.new('An unexpected error occurred during primary key retrieval')
1747
+ end
1748
+ end
1749
+
1750
+ # Query table statistics for all indexes on the table
1751
+ # "TABLE_NAME: #{index_stats[2]}"
1752
+ # "NON_UNIQUE: #{index_stats[3]}"
1753
+ # "INDEX_NAME: #{index_stats[5]}"
1754
+ # "COLUMN_NAME: #{index_stats[8]}"
1755
+ stmt = IBM_DB.statistics( @connection, nil,
1756
+ @servertype.set_case(@schema),
1757
+ @servertype.set_case(table_name), 1 )
1758
+ if(stmt)
1759
+ begin
1760
+ while ( index_stats = IBM_DB.fetch_array(stmt) )
1761
+ is_composite = false
1762
+ if index_stats[5] # INDEX_NAME
1763
+ index_name = index_stats[5].downcase
1764
+ index_unique = (index_stats[3] == 0)
1765
+ index_columns = [index_stats[8].downcase] # COLUMN_NAME
1766
+ index_qualifier = index_stats[4].downcase #Index_Qualifier
1767
+ # Create an IndexDefinition object and add to the indexes array
1768
+ i = 0;
1769
+ indexes.each do |index|
1770
+ if index.name == index_name && index_schema[i] == index_qualifier
1771
+ index.columns = index.columns + index_columns
1772
+ is_composite = true
1773
+ end
1774
+ i = i+1
1775
+ end
1776
+
1777
+ unless is_composite
1778
+ indexes << IndexDefinition.new(table_name, index_name, index_unique, index_columns)
1779
+ index_schema << index_qualifier
1780
+ end
1781
+ end
1782
+ end
1783
+ rescue StandardError => fetch_error # Handle driver fetch errors
1784
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
1785
+ if error_msg && !error_msg.empty?
1786
+ raise "Failed to retrieve index metadata during fetch: #{error_msg}"
1787
+ else
1788
+ error_msg = "An unexpected error occurred during retrieval of index metadata"
1789
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1790
+ raise error_msg
1791
+ end
1792
+ ensure # Free resources associated with the statement
1793
+ IBM_DB.free_stmt(stmt) if stmt
1794
+ end
1795
+ else # Handle driver execution errors
1796
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN )
1797
+ if error_msg && !error_msg.empty?
1798
+ raise "Failed to retrieve index metadata due to error: #{error_msg}"
1799
+ else
1800
+ raise StandardError.new('An unexpected error occurred during index retrieval')
1801
+ end
1802
+ end
1803
+
1804
+ # remove the primary key index entry.... should not be dumped by the dumper
1805
+
1806
+ i = 0
1807
+ indexes.each do |index|
1808
+ if pk_index && index.columns == pk_index.columns
1809
+ indexes.delete_at(i)
1810
+ end
1811
+ i = i+1
1812
+ end
1813
+ # Returns the indexes array
1814
+ return indexes
1815
+ end
1816
+
1817
+ # Returns an array of Column objects for the table specified by +table_name+
1818
+ def columns(table_name, name = nil)
1819
+ # to_s required because it may be a symbol.
1820
+ table_name = @servertype.set_case(table_name.to_s)
1821
+ # Checks if a blank table name has been given.
1822
+ # If so it returns an empty array
1823
+ return [] if table_name.strip.empty?
1824
+ # +columns+ will contain the resulting array
1825
+ columns = []
1826
+ # Statement required to access all the columns information
1827
+ stmt = IBM_DB.columns( @connection, nil,
1828
+ @servertype.set_case(@schema),
1829
+ @servertype.set_case(table_name) )
1830
+ if(stmt)
1831
+ begin
1832
+ # Fetches all the columns and assigns them to col.
1833
+ # +col+ is an hash with keys/value pairs for a column
1834
+ while col = IBM_DB.fetch_assoc(stmt)
1835
+ column_name = col["column_name"].downcase
1836
+ # Assigns the column default value.
1837
+ column_default_value = col["column_def"]
1838
+ # If there is no default value, it assigns NIL
1839
+ column_default_value = nil if (column_default_value && column_default_value.upcase == 'NULL')
1840
+ # If default value is IDENTITY GENERATED BY DEFAULT (this value is retrieved in case of id columns)
1841
+ column_default_value = nil if (column_default_value && column_default_value.upcase =~ /IDENTITY GENERATED BY DEFAULT/i)
1842
+ # Removes single quotes from the default value
1843
+ column_default_value.gsub!(/^'(.*)'$/, '\1') unless column_default_value.nil?
1844
+ # Assigns the column type
1845
+ column_type = col["type_name"].downcase
1846
+ # Assigns the field length (size) for the column
1847
+ column_length = col["column_size"]
1848
+ column_scale = col["decimal_digits"]
1849
+ # The initializer of the class Column, requires the +column_length+ to be declared
1850
+ # between brackets after the datatype(e.g VARCHAR(50)) for :string and :text types.
1851
+ # If it's a "for bit data" field it does a subsitution in place, if not
1852
+ # it appends the (column_length) string on the supported data types
1853
+ unless column_length.nil? ||
1854
+ column_length == '' ||
1855
+ column_type.sub!(/ \(\) for bit data/i,"(#{column_length}) FOR BIT DATA") ||
1856
+ !column_type =~ /char|lob|graphic/i
1857
+ if column_type =~ /decimal/i
1858
+ column_type << "(#{column_length},#{column_scale})"
1859
+ elsif column_type =~ /smallint|integer|double|date|time|timestamp|xml|bigint/i
1860
+ column_type << "" # override native limits incompatible with table create
1861
+ else
1862
+ column_type << "(#{column_length})"
1863
+ end
1864
+ end
1865
+ # col["NULLABLE"] is 1 if the field is nullable, 0 if not.
1866
+ column_nullable = (col["nullable"] == 1) ? true : false
1867
+ # Make sure the hidden column (db2_generated_rowid_for_lobs) in DB2 z/OS isn't added to the list
1868
+ if !(column_name =~ /db2_generated_rowid_for_lobs/i)
1869
+ # Pushes into the array the *IBM_DBColumn* object, created by passing to the initializer
1870
+ # +column_name+, +default_value+, +column_type+ and +column_nullable+.
1871
+ columns << IBM_DBColumn.new(column_name, column_default_value, column_type, column_nullable)
1872
+ end
1873
+ end
1874
+ rescue StandardError => fetch_error # Handle driver fetch errors
1875
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
1876
+ if error_msg && !error_msg.empty?
1877
+ raise "Failed to retrieve column metadata during fetch: #{error_msg}"
1878
+ else
1879
+ error_msg = "An unexpected error occurred during retrieval of column metadata"
1880
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
1881
+ raise error_msg
1882
+ end
1883
+ ensure # Free resources associated with the statement
1884
+ IBM_DB.free_stmt(stmt) if stmt
1885
+ end
1886
+ else # Handle driver execution errors
1887
+ error_msg = IBM_DB.getErrormsg(@connection, IBM_DB::DB_CONN )
1888
+ if error_msg && !error_msg.empty?
1889
+ raise "Failed to retrieve column metadata due to error: #{error_msg}"
1890
+ else
1891
+ raise StandardError.new('An unexpected error occurred during retrieval of columns metadata')
1892
+ end
1893
+ end
1894
+ # Returns the columns array
1895
+ return columns
1896
+ end
1897
+
1898
+ # Renames a table.
1899
+ # ==== Example
1900
+ # rename_table('octopuses', 'octopi')
1901
+ # Overriden to satisfy IBM data servers syntax
1902
+ def rename_table(name, new_name)
1903
+ # SQL rename table statement
1904
+ rename_table_sql = "RENAME TABLE #{name} TO #{new_name}"
1905
+ stmt = execute(rename_table_sql)
1906
+ # Ensures to free the resources associated with the statement
1907
+ ensure
1908
+ IBM_DB.free_stmt(stmt) if stmt
1909
+ end
1910
+
1911
+ # Renames a column.
1912
+ # ===== Example
1913
+ # rename_column(:suppliers, :description, :name)
1914
+ def rename_column(table_name, column_name, new_column_name)
1915
+ @servertype.rename_column(table_name, column_name, new_column_name)
1916
+ end
1917
+
1918
+ # Removes the column from the table definition.
1919
+ # ===== Examples
1920
+ # remove_column(:suppliers, :qualification)
1921
+ def remove_column(table_name, column_name)
1922
+ @servertype.remove_column(table_name, column_name)
1923
+ end
1924
+
1925
+ # Changes the column's definition according to the new options.
1926
+ # See TableDefinition#column for details of the options you can use.
1927
+ # ===== Examples
1928
+ # change_column(:suppliers, :name, :string, :limit => 80)
1929
+ # change_column(:accounts, :description, :text)
1930
+ def change_column(table_name, column_name, type, options = {})
1931
+ @servertype.change_column(table_name, column_name, type, options)
1932
+ end
1933
+
1934
+ =begin
1935
+ #overrides the abstract adapter method to generate proper sql
1936
+ #specifying the column options, like default value and nullability clause
1937
+ def add_column_options!(sql,options={})
1938
+ #add default null option only if :default option is not specified and
1939
+ #:null option is not specified or is true
1940
+ if (options[:default].nil? && (options[:null].nil? || options[:null] == true))
1941
+ sql << " DEFAULT NULL"
1942
+ else
1943
+ if( !options[:default].nil?)
1944
+ #check, :column option is passed only in case of create_table but not in case of add_column
1945
+ if (!options[:column].nil?)
1946
+ sql << " DEFAULT #{quote(options[:default],options[:column])}"
1947
+ else
1948
+ sql << " DEFAULT #{quote(options[:default])}"
1949
+ end
1950
+ end
1951
+ #append NOT NULL to sql only---
1952
+ #---if options[:null] is not nil and is equal to false
1953
+ unless options[:null] == nil
1954
+ sql << " NOT NULL" if (options[:null] == false)
1955
+ end
1956
+ end
1957
+ end
1958
+ =end
1959
+
1960
+ #Add distinct clause to the sql if there is no order by specified
1961
+ def distinct(columns, order_by)
1962
+ if order_by.nil?
1963
+ "DISTINCT #{columns}"
1964
+ else
1965
+ "#{columns}"
1966
+ end
1967
+ end
1968
+
1969
+ # Sets a new default value for a column. This does not set the default
1970
+ # value to +NULL+, instead, it needs DatabaseStatements#execute which
1971
+ # can execute the appropriate SQL statement for setting the value.
1972
+ # ==== Examples
1973
+ # change_column_default(:suppliers, :qualification, 'new')
1974
+ # change_column_default(:accounts, :authorized, 1)
1975
+ # Method overriden to satisfy IBM data servers syntax.
1976
+ def change_column_default(table_name, column_name, default)
1977
+ @servertype.change_column_default(table_name, column_name, default)
1978
+ end
1979
+
1980
+ #Changes the nullability value of a column
1981
+ def change_column_null(table_name, column_name, null, default = nil)
1982
+ @servertype.change_column_null(table_name, column_name, null, default)
1983
+ end
1984
+
1985
+ # Remove the given index from the table.
1986
+ #
1987
+ # Remove the suppliers_name_index in the suppliers table (legacy support, use the second or third forms).
1988
+ # remove_index :suppliers, :name
1989
+ # Remove the index named accounts_branch_id in the accounts table.
1990
+ # remove_index :accounts, :column => :branch_id
1991
+ # Remove the index named by_branch_party in the accounts table.
1992
+ # remove_index :accounts, :name => :by_branch_party
1993
+ #
1994
+ # You can remove an index on multiple columns by specifying the first column.
1995
+ # add_index :accounts, [:username, :password]
1996
+ # remove_index :accounts, :username
1997
+ # Overriden to use the IBM data servers SQL syntax.
1998
+ def remove_index(table_name, options = {})
1999
+ execute("DROP INDEX #{index_name(table_name, options)}")
2000
+ end
2001
+ end # class IBM_DBAdapter
2002
+
2003
+ # This class contains common code across DB's (DB2 LUW, zOS, i5 and IDS)
2004
+ class IBM_DataServer
2005
+ def initialize(adapter, ar3)
2006
+ @adapter = adapter
2007
+ @isAr3 = ar3
2008
+ end
2009
+
2010
+ def last_generated_id(stmt)
2011
+ end
2012
+
2013
+ def create_index_after_table (table_name,cloumn_name)
2014
+ end
2015
+
2016
+ def setup_for_lob_table ()
2017
+ end
2018
+
2019
+ def reorg_table(table_name)
2020
+ end
2021
+
2022
+ def check_reserved_words(col_name)
2023
+ col_name.to_s
2024
+ end
2025
+
2026
+ # This is supported by the DB2 for Linux, UNIX, Windows data servers
2027
+ # and by the DB2 for i5 data servers
2028
+ def remove_column(table_name, column_name)
2029
+ begin
2030
+ @adapter.execute "ALTER TABLE #{table_name} DROP #{column_name}"
2031
+ reorg_table(table_name)
2032
+ rescue StandardError => exec_err
2033
+ # Provide details on the current XML columns support
2034
+ if exec_err.message.include?('SQLCODE=-1242') && exec_err.message.include?('42997')
2035
+ raise StatementInvalid,
2036
+ "A column that is part of a table containing an XML column cannot be dropped. \
2037
+ To remove the column, the table must be dropped and recreated without the #{column_name} column: #{exec_err}"
2038
+ else
2039
+ raise "#{exec_err}"
2040
+ end
2041
+ end
2042
+ end
2043
+
2044
+ def select(stmt)
2045
+ results = []
2046
+ # Fetches all the results available. IBM_DB.fetch_assoc(stmt) returns
2047
+ # an hash for each single record.
2048
+ # The loop stops when there aren't any more valid records to fetch
2049
+ begin
2050
+ if(@isAr3)
2051
+ while single_hash = IBM_DB.fetch_assoc(stmt)
2052
+ # Add the record to the +results+ array
2053
+ results << single_hash
2054
+ end
2055
+ else
2056
+ while single_hash = IBM_DB.fetch_array(stmt)
2057
+ # Add the record to the +results+ array
2058
+ results << single_hash
2059
+ end
2060
+ end
2061
+ rescue StandardError => fetch_error # Handle driver fetch errors
2062
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
2063
+ if error_msg && !error_msg.empty?
2064
+ raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
2065
+ else
2066
+ error_msg = "An unexpected error occurred during data retrieval"
2067
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
2068
+ raise error_msg
2069
+ end
2070
+ end
2071
+ return results
2072
+ end
2073
+
2074
+ def select_rows(sql, name, stmt, results)
2075
+ # Fetches all the results available. IBM_DB.fetch_array(stmt) returns
2076
+ # an array representing a row in a result set.
2077
+ # The loop stops when there aren't any more valid records to fetch
2078
+ begin
2079
+ while single_array = IBM_DB.fetch_array(stmt)
2080
+ #Add the array to results array
2081
+ results << single_array
2082
+ end
2083
+ rescue StandardError => fetch_error # Handle driver fetch errors
2084
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
2085
+ if error_msg && !error_msg.empty?
2086
+ raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
2087
+ else
2088
+ error_msg = "An unexpected error occurred during data retrieval"
2089
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
2090
+ raise error_msg
2091
+ end
2092
+ end
2093
+ return results
2094
+ end
2095
+
2096
+ # Praveen
2097
+ def prepare(sql,name = nil)
2098
+ begin
2099
+ stmt = IBM_DB.prepare(@adapter.connection, sql)
2100
+ if( stmt )
2101
+ stmt
2102
+ else
2103
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2104
+ end
2105
+ rescue StandardError => prep_err
2106
+ if prep_err && !prep_err.message.empty?
2107
+ raise "Failed to prepare sql #{sql} due to: #{prep_err}"
2108
+ else
2109
+ raise
2110
+ end
2111
+ end
2112
+ end
2113
+
2114
+ def execute(sql, name = nil)
2115
+ begin
2116
+ if stmt = IBM_DB.exec(@adapter.connection, sql)
2117
+ stmt # Return the statement object
2118
+ else
2119
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2120
+ end
2121
+ rescue StandardError => exec_err
2122
+ if exec_err && !exec_err.message.empty?
2123
+ raise "Failed to execute statement due to: #{exec_err}"
2124
+ else
2125
+ raise
2126
+ end
2127
+ end
2128
+ end
2129
+
2130
+ def set_schema(schema)
2131
+ @adapter.execute("SET SCHEMA #{schema}")
2132
+ end
2133
+
2134
+ def query_offset_limit(sql, offset, limit)
2135
+ end
2136
+
2137
+ def query_offset_limit!(sql, offset, limit, options)
2138
+ end
2139
+
2140
+ def get_datetime_mapping
2141
+ end
2142
+
2143
+ def get_time_mapping
2144
+ end
2145
+
2146
+ def get_double_mapping
2147
+ end
2148
+
2149
+ def change_column_default(table_name, column_name, default)
2150
+ end
2151
+
2152
+ def change_column_null(table_name, column_name, null, default)
2153
+ end
2154
+
2155
+ def set_binary_default(value)
2156
+ end
2157
+
2158
+ def set_binary_value
2159
+ end
2160
+
2161
+ def set_text_default
2162
+ end
2163
+
2164
+ def set_case(value)
2165
+ end
2166
+
2167
+ def limit_not_supported_types
2168
+ [:integer, :double, :date, :time, :timestamp, :xml, :bigint]
2169
+ end
2170
+ end # class IBM_DataServer
2171
+
2172
+ class IBM_DB2 < IBM_DataServer
2173
+ def initialize(adapter, ar3)
2174
+ super(adapter,ar3)
2175
+ @limit = @offset = nil
2176
+ end
2177
+
2178
+ def rename_column(table_name, column_name, new_column_name)
2179
+ raise NotImplementedError, "rename_column is not implemented yet in the IBM_DB Adapter"
2180
+ end
2181
+
2182
+ def primary_key_definition(start_id)
2183
+ return "INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH #{start_id}) PRIMARY KEY"
2184
+ end
2185
+
2186
+ # Returns the last automatically generated ID.
2187
+ # This method is required by the +insert+ method
2188
+ # The "stmt" parameter is ignored for DB2 but used for IDS
2189
+ def last_generated_id(stmt)
2190
+ # Queries the db to obtain the last ID that was automatically generated
2191
+ sql = "SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1"
2192
+ stmt = IBM_DB.prepare(@adapter.connection, sql)
2193
+ if(stmt)
2194
+ if(IBM_DB.execute(stmt, nil))
2195
+ begin
2196
+ # Fetches the only record available (containing the last id)
2197
+ IBM_DB.fetch_row(stmt)
2198
+ # Retrieves and returns the result of the query with the last id.
2199
+ IBM_DB.result(stmt,0)
2200
+ rescue StandardError => fetch_error # Handle driver fetch errors
2201
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
2202
+ if error_msg && !error_msg.empty?
2203
+ raise "Failed to retrieve last generated id: #{error_msg}"
2204
+ else
2205
+ error_msg = "An unexpected error occurred during retrieval of last generated id"
2206
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
2207
+ raise error_msg
2208
+ end
2209
+ ensure # Free resources associated with the statement
2210
+ IBM_DB.free_stmt(stmt) if stmt
2211
+ end
2212
+ else
2213
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
2214
+ IBM_DB.free_stmt(stmt) if stmt
2215
+ if error_msg && !error_msg.empty?
2216
+ raise "Failed to retrieve last generated id: #{error_msg}"
2217
+ else
2218
+ error_msg = "An unexpected error occurred during retrieval of last generated id"
2219
+ raise error_msg
2220
+ end
2221
+ end
2222
+ else
2223
+ error_msg = IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2224
+ if error_msg && !error_msg.empty?
2225
+ raise "Failed to retrieve last generated id due to error: #{error_msg}"
2226
+ else
2227
+ raise StandardError.new('An unexpected error occurred during retrieval of last generated id')
2228
+ end
2229
+ end
2230
+ end
2231
+
2232
+ def change_column(table_name, column_name, type, options)
2233
+ data_type = @adapter.type_to_sql(type, options[:limit], options[:precision], options[:scale])
2234
+ begin
2235
+ execute "ALTER TABLE #{table_name} ALTER #{column_name} SET DATA TYPE #{data_type}"
2236
+ rescue StandardError => exec_err
2237
+ if exec_err.message.include?('SQLCODE=-190')
2238
+ raise StatementInvalid,
2239
+ "Please consult documentation for compatible data types while changing column datatype. \
2240
+ The column datatype change to [#{data_type}] is not supported by this data server: #{exec_err}"
2241
+ else
2242
+ raise "#{exec_err}"
2243
+ end
2244
+ end
2245
+ reorg_table(table_name)
2246
+ change_column_null(table_name,column_name,options[:null],nil)
2247
+ change_column_default(table_name, column_name, options[:default])
2248
+ reorg_table(table_name)
2249
+ end
2250
+
2251
+ # DB2 specific ALTER TABLE statement to add a default clause
2252
+ def change_column_default(table_name, column_name, default)
2253
+ # SQL statement which alters column's default value
2254
+ change_column_sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} \
2255
+ SET WITH DEFAULT #{@adapter.quote(default)}"
2256
+
2257
+ stmt = execute(change_column_sql)
2258
+ reorg_table(table_name)
2259
+ ensure
2260
+ IBM_DB.free_stmt(stmt) if stmt
2261
+ end
2262
+
2263
+ #DB2 specific ALTER TABLE statement to change the nullability of a column
2264
+ def change_column_null(table_name, column_name, null, default)
2265
+ if !default.nil?
2266
+ change_column_default(table_name, column_name, default)
2267
+ end
2268
+
2269
+ if !null.nil?
2270
+ if null
2271
+ change_column_sql = "ALTER TABLE #{table_name} ALTER #{column_name} DROP NOT NULL"
2272
+ else
2273
+ change_column_sql = "ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL"
2274
+ end
2275
+ stmt = execute(change_column_sql)
2276
+ reorg_table(table_name)
2277
+ end
2278
+
2279
+ ensure
2280
+ IBM_DB.free_stmt(stmt) if stmt
2281
+ end
2282
+
2283
+ # This method returns the DB2 SQL type corresponding to the Rails
2284
+ # datetime/timestamp type
2285
+ def get_datetime_mapping
2286
+ return "timestamp"
2287
+ end
2288
+
2289
+ # This method returns the DB2 SQL type corresponding to the Rails
2290
+ # time type
2291
+ def get_time_mapping
2292
+ return "time"
2293
+ end
2294
+
2295
+ #This method returns the DB2 SQL type corresponding to Rails double type
2296
+ def get_double_mapping
2297
+ return "double"
2298
+ end
2299
+ =begin
2300
+ # Commenting this code, as offset handling is now part of sql and we need to handle it in select and also
2301
+ # need not set cursor type during prepare or execute
2302
+ # Fetches all the results available. IBM_DB.fetch_assoc(stmt) returns
2303
+ # an hash for each single record.
2304
+ # The loop stops when there aren't any more valid records to fetch
2305
+ def select(stmt)
2306
+ results = []
2307
+ begin
2308
+ if (!@offset.nil? && @offset >= 0) || (!@limit.nil? && @limit > 0)
2309
+ # We know at this point that there is an offset and/or a limit
2310
+ # Check if the cursor type is set correctly
2311
+ cursor_type = IBM_DB.get_option stmt, IBM_DB::SQL_ATTR_CURSOR_TYPE, 0
2312
+ @offset = 0 if @offset.nil?
2313
+ if (cursor_type == IBM_DB::SQL_CURSOR_STATIC)
2314
+ index = 0
2315
+ # Get @limit rows starting at @offset
2316
+ while (index < @limit)
2317
+ # We increment the offset by 1 because for DB2 the offset of the initial row is 1 instead of 0
2318
+ if single_hash = IBM_DB.fetch_assoc(stmt, @offset + index + 1)
2319
+ # Add the record to the +results+ array
2320
+ results << single_hash
2321
+ index = index + 1
2322
+ else
2323
+ # break from the while loop
2324
+ break
2325
+ end
2326
+ end
2327
+ else # cursor != IBM_DB::SQL_CURSOR_STATIC
2328
+ # If the result set contains a LOB, the cursor type will never be SQL_CURSOR_STATIC
2329
+ # because DB2 does not allow this. We can't use the offset mechanism because the cursor
2330
+ # is not scrollable. In this case, ignore first @offset rows and return rows starting
2331
+ # at @offset to @offset + @limit
2332
+ index = 0
2333
+ while (index < @offset + @limit)
2334
+ if single_hash = IBM_DB.fetch_assoc(stmt)
2335
+ # Add the record to the +results+ array only from row @offset to @offset + @limit
2336
+ if (index >= @offset)
2337
+ results << single_hash
2338
+ end
2339
+ index = index + 1
2340
+ else
2341
+ # break from the while loop
2342
+ break
2343
+ end
2344
+ end
2345
+ end
2346
+ # This is the case where limit is set to zero
2347
+ # Simply return an empty +results+
2348
+ elsif (!@limit.nil? && @limit == 0)
2349
+ results
2350
+ # No limits or offsets specified
2351
+ else
2352
+ while single_hash = IBM_DB.fetch_assoc(stmt)
2353
+ # Add the record to the +results+ array
2354
+ results << single_hash
2355
+ end
2356
+ return results
2357
+ end
2358
+ rescue StandardError => fetch_error # Handle driver fetch errors
2359
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
2360
+ if error_msg && !error_msg.empty?
2361
+ raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
2362
+ else
2363
+ error_msg = "An unexpected error occurred during data retrieval"
2364
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
2365
+ raise error_msg
2366
+ end
2367
+ ensure
2368
+ # Assign the instance variables to nil. We will not be using them again
2369
+ @offset = nil
2370
+ @limit = nil
2371
+ end
2372
+ end
2373
+
2374
+ # Fetches all the results available. IBM_DB.fetch_array(stmt) returns
2375
+ # an array for each single record.
2376
+ # The loop stops when there aren't any more valid records to fetch
2377
+ def select_rows(sql, name, stmt, results)
2378
+ begin
2379
+ if (!@offset.nil? && @offset >= 0) || (!@limit.nil? && @limit > 0)
2380
+ # We know at this point that there is an offset and/or a limit
2381
+ # Check if the cursor type is set correctly
2382
+ cursor_type = IBM_DB.get_option stmt, IBM_DB::SQL_ATTR_CURSOR_TYPE, 0
2383
+ @offset = 0 if @offset.nil?
2384
+ if (cursor_type == IBM_DB::SQL_CURSOR_STATIC)
2385
+ index = 0
2386
+ # Get @limit rows starting at @offset
2387
+ while (index < @limit)
2388
+ # We increment the offset by 1 because for DB2 the offset of the initial row is 1 instead of 0
2389
+ if single_array = IBM_DB.fetch_array(stmt, @offset + index + 1)
2390
+ # Add the array to the +results+ array
2391
+ results << single_array
2392
+ index = index + 1
2393
+ else
2394
+ # break from the while loop
2395
+ break
2396
+ end
2397
+ end
2398
+ else # cursor != IBM_DB::SQL_CURSOR_STATIC
2399
+ # If the result set contains a LOB, the cursor type will never be SQL_CURSOR_STATIC
2400
+ # because DB2 does not allow this. We can't use the offset mechanism because the cursor
2401
+ # is not scrollable. In this case, ignore first @offset rows and return rows starting
2402
+ # at @offset to @offset + @limit
2403
+ index = 0
2404
+ while (index < @offset + @limit)
2405
+ if single_array = IBM_DB.fetch_array(stmt)
2406
+ # Add the array to the +results+ array only from row @offset to @offset + @limit
2407
+ if (index >= @offset)
2408
+ results << single_array
2409
+ end
2410
+ index = index + 1
2411
+ else
2412
+ # break from the while loop
2413
+ break
2414
+ end
2415
+ end
2416
+ end
2417
+ # This is the case where limit is set to zero
2418
+ # Simply return an empty +results+
2419
+ elsif (!@limit.nil? && @limit == 0)
2420
+ results
2421
+ # No limits or offsets specified
2422
+ else
2423
+ while single_array = IBM_DB.fetch_array(stmt)
2424
+ # Add the array to the +results+ array
2425
+ results << single_array
2426
+ end
2427
+ end
2428
+ rescue StandardError => fetch_error # Handle driver fetch errors
2429
+ error_msg = IBM_DB.getErrormsg(stmt, IBM_DB::DB_STMT )
2430
+ if error_msg && !error_msg.empty?
2431
+ raise StatementInvalid,"Failed to retrieve data: #{error_msg}"
2432
+ else
2433
+ error_msg = "An unexpected error occurred during data retrieval"
2434
+ error_msg = error_msg + ": #{fetch_error.message}" if !fetch_error.message.empty?
2435
+ raise error_msg
2436
+ end
2437
+ ensure
2438
+ # Assign the instance variables to nil. We will not be using them again
2439
+ @offset = nil
2440
+ @limit = nil
2441
+ end
2442
+ return results
2443
+ end
2444
+
2445
+ # Praveen
2446
+ def prepare(sql,name = nil)
2447
+ # Check if there is a limit and/or an offset
2448
+ # If so then make sure and use a static cursor type
2449
+ begin
2450
+ if (!@offset.nil? && @offset >= 0) || (!@limit.nil? && @limit > 0)
2451
+ # Set the cursor type to static so we can later utilize the offset and limit correctly
2452
+ if stmt = IBM_DB.prepare(@adapter.connection, sql,
2453
+ {IBM_DB::SQL_ATTR_CURSOR_TYPE => IBM_DB::SQL_CURSOR_STATIC})
2454
+ stmt # Return the statement object
2455
+ else
2456
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2457
+ end
2458
+ else
2459
+ if stmt = IBM_DB.prepare(@adapter.connection, sql)
2460
+ stmt # Return the statement object
2461
+ else
2462
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2463
+ end
2464
+ end
2465
+ rescue StandardError => prep_err
2466
+ error_msg = "Failed to prepare sql #{sql}"
2467
+ error_msg = error_msg + ": #{prep_err.message}" if !prep_err.message.empty?
2468
+ raise error_msg
2469
+ end
2470
+ end
2471
+
2472
+ # Praveen
2473
+ def execute(sql, name = nil)
2474
+ # Check if there is a limit and/or an offset
2475
+ # If so then make sure and use a static cursor type
2476
+ begin
2477
+ if (!@offset.nil? && @offset >= 0) || (!@limit.nil? && @limit > 0)
2478
+ # Set the cursor type to static so we can later utilize the offset and limit correctly
2479
+ if stmt = IBM_DB.exec(@adapter.connection, sql,
2480
+ {IBM_DB::SQL_ATTR_CURSOR_TYPE => IBM_DB::SQL_CURSOR_STATIC})
2481
+ stmt # Return the statement object
2482
+ else
2483
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2484
+ end
2485
+ else
2486
+ if stmt = IBM_DB.exec(@adapter.connection, sql)
2487
+ stmt # Return the statement object
2488
+ else
2489
+ raise StatementInvalid, IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2490
+ end
2491
+ end
2492
+ rescue StandardError => exec_err
2493
+ error_msg = "Failed to execute statement"
2494
+ error_msg = error_msg + ": #{exec_err.message}" if !exec_err.message.empty?
2495
+ raise error_msg
2496
+ end
2497
+ end
2498
+ =end
2499
+ def query_offset_limit(sql, offset, limit)
2500
+ if(offset.nil? && limit.nil?)
2501
+ return sql
2502
+ end
2503
+
2504
+ if (offset.nil?)
2505
+ return sql << " FETCH FIRST #{limit} ROWS ONLY"
2506
+ end
2507
+
2508
+ if(limit.nil?)
2509
+ sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2510
+ return sql << ") AS I) AS O WHERE sys_row_num > #{offset}"
2511
+ end
2512
+
2513
+ # Defines what will be the last record
2514
+ last_record = offset + limit
2515
+ # Transforms the SELECT query in order to retrieve/fetch only
2516
+ # a number of records after the specified offset.
2517
+ # 'select' or 'SELECT' is replaced with the partial query below that adds the sys_row_num column
2518
+ # to select with the condition of this column being between offset+1 and the offset+limit
2519
+ sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2520
+ # The final part of the query is appended to include a WHERE...BETWEEN...AND condition,
2521
+ # and retrieve only a LIMIT number of records starting from the OFFSET+1
2522
+ sql << ") AS I) AS O WHERE sys_row_num BETWEEN #{offset+1} AND #{last_record}"
2523
+ end
2524
+
2525
+ def query_offset_limit!(sql, offset, limit, options)
2526
+ if(offset.nil? && limit.nil?)
2527
+ options[:paramArray] = []
2528
+ return sql
2529
+ end
2530
+
2531
+ if (offset.nil?)
2532
+ options[:paramArray] = []
2533
+ return sql << " FETCH FIRST #{limit} ROWS ONLY"
2534
+ end
2535
+
2536
+ if(limit.nil?)
2537
+ sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2538
+ sql << ") AS I) AS O WHERE sys_row_num > ?"
2539
+ options[:paramArray] = [offset]
2540
+ return
2541
+ end
2542
+
2543
+ # Defines what will be the last record
2544
+ last_record = offset + limit
2545
+ # Transforms the SELECT query in order to retrieve/fetch only
2546
+ # a number of records after the specified offset.
2547
+ # 'select' or 'SELECT' is replaced with the partial query below that adds the sys_row_num column
2548
+ # to select with the condition of this column being between offset+1 and the offset+limit
2549
+ sql.sub!(/SELECT/i,"SELECT O.* FROM (SELECT I.*, ROW_NUMBER() OVER () sys_row_num FROM (SELECT")
2550
+ # The final part of the query is appended to include a WHERE...BETWEEN...AND condition,
2551
+ # and retrieve only a LIMIT number of records starting from the OFFSET+1
2552
+ sql << ") AS I) AS O WHERE sys_row_num BETWEEN ? AND ?"
2553
+ options[:paramArray] = [offset+1, last_record]
2554
+ end
2555
+
2556
+ # This method generates the default blob value specified for
2557
+ # DB2 Dataservers
2558
+ def set_binary_default(value)
2559
+ "BLOB('#{value}')"
2560
+ end
2561
+
2562
+ # This method generates the blob value specified for DB2 Dataservers
2563
+ def set_binary_value
2564
+ "BLOB('?')"
2565
+ end
2566
+
2567
+ # This method generates the default clob value specified for
2568
+ # DB2 Dataservers
2569
+ def set_text_default(value)
2570
+ "'#{value}'"
2571
+ end
2572
+
2573
+ # For DB2 Dataservers , the arguments to the meta-data functions
2574
+ # need to be in upper-case
2575
+ def set_case(value)
2576
+ value.upcase
2577
+ end
2578
+ end # class IBM_DB2
2579
+
2580
+ class IBM_DB2_LUW < IBM_DB2
2581
+ # Reorganizes the table for column changes
2582
+ def reorg_table(table_name)
2583
+ execute("CALL ADMIN_CMD('REORG TABLE #{table_name}')")
2584
+ end
2585
+ end # class IBM_DB2_LUW
2586
+
2587
+ class IBM_DB2_LUW_COBRA < IBM_DB2_LUW
2588
+ # Cobra supports parameterised timestamp,
2589
+ # hence overriding following method to allow timestamp datatype to be parameterised
2590
+ def limit_not_supported_types
2591
+ [:integer, :double, :date, :time, :xml, :bigint]
2592
+ end
2593
+
2594
+ # Alter table column for renaming a column
2595
+ # This feature is supported for against DB2 V97 and above only
2596
+ def rename_column(table_name, column_name, new_column_name)
2597
+ _table_name = table_name.to_s
2598
+ _column_name = column_name.to_s
2599
+ _new_column_name = new_column_name.to_s
2600
+
2601
+ nil_condition = _table_name.nil? || _column_name.nil? || _new_column_name.nil?
2602
+ empty_condition = _table_name.empty? ||
2603
+ _column_name.empty? ||
2604
+ _new_column_name.empty? unless nil_condition
2605
+
2606
+ if nil_condition || empty_condition
2607
+ raise ArgumentError,"One of the arguments passed to rename_column is empty or nil"
2608
+ end
2609
+
2610
+ begin
2611
+ rename_column_sql = "ALTER TABLE #{_table_name} RENAME COLUMN #{_column_name} \
2612
+ TO #{_new_column_name}"
2613
+
2614
+ unless stmt = execute(rename_column_sql)
2615
+ error_msg = IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2616
+ if error_msg && !error_msg.empty?
2617
+ raise "Rename column failed : #{error_msg}"
2618
+ else
2619
+ raise StandardError.new('An unexpected error occurred during renaming the column')
2620
+ end
2621
+ end
2622
+
2623
+ reorg_table(_table_name)
2624
+
2625
+ ensure
2626
+ IBM_DB.free_stmt(stmt) if stmt
2627
+ end #End of begin
2628
+ end # End of rename_column
2629
+ end #IBM_DB2_LUW_COBRA
2630
+
2631
+ module HostedDataServer
2632
+ require 'pathname'
2633
+ #find DB2-i5-zOS rezerved words file relative path
2634
+ rfile = Pathname.new(File.dirname(__FILE__)).parent + 'vendor' + 'db2-i5-zOS.yaml'
2635
+ if rfile
2636
+ RESERVED_WORDS = open(rfile.to_s) {|f| YAML.load(f) }
2637
+ def check_reserved_words(col_name)
2638
+ if RESERVED_WORDS[col_name]
2639
+ '"' + RESERVED_WORDS[col_name] + '"'
2640
+ else
2641
+ col_name.to_s
2642
+ end
2643
+ end
2644
+ else
2645
+ raise "Failed to locate IBM_DB Adapter dependency: #{rfile}"
2646
+ end
2647
+ end # module HostedDataServer
2648
+
2649
+ class IBM_DB2_ZOS < IBM_DB2
2650
+ # since v9 doesn't need, suggest putting it in HostedDataServer?
2651
+ def create_index_after_table(table_name,column_name)
2652
+ @adapter.add_index(table_name, column_name, :unique => true)
2653
+ end
2654
+
2655
+ def remove_column(table_name, column_name)
2656
+ raise NotImplementedError,
2657
+ "remove_column is not supported by the DB2 for zOS data server"
2658
+ end
2659
+
2660
+ #Alter table column for renaming a column
2661
+ def rename_column(table_name, column_name, new_column_name)
2662
+ _table_name = table_name.to_s
2663
+ _column_name = column_name.to_s
2664
+ _new_column_name = new_column_name.to_s
2665
+
2666
+ nil_condition = _table_name.nil? || _column_name.nil? || _new_column_name.nil?
2667
+ empty_condition = _table_name.empty? ||
2668
+ _column_name.empty? ||
2669
+ _new_column_name.empty? unless nil_condition
2670
+
2671
+ if nil_condition || empty_condition
2672
+ raise ArgumentError,"One of the arguments passed to rename_column is empty or nil"
2673
+ end
2674
+
2675
+ begin
2676
+ rename_column_sql = "ALTER TABLE #{_table_name} RENAME COLUMN #{_column_name} \
2677
+ TO #{_new_column_name}"
2678
+
2679
+ unless stmt = execute(rename_column_sql)
2680
+ error_msg = IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2681
+ if error_msg && !error_msg.empty?
2682
+ raise "Rename column failed : #{error_msg}"
2683
+ else
2684
+ raise StandardError.new('An unexpected error occurred during renaming the column')
2685
+ end
2686
+ end
2687
+
2688
+ reorg_table(_table_name)
2689
+
2690
+ ensure
2691
+ IBM_DB.free_stmt(stmt) if stmt
2692
+ end #End of begin
2693
+ end # End of rename_column
2694
+
2695
+ # DB2 z/OS only allows NULL or "" (empty) string as DEFAULT value for a BLOB column.
2696
+ # For non-empty string and non-NULL values, the server returns error
2697
+ def set_binary_default(value)
2698
+ "#{value}"
2699
+ end
2700
+
2701
+ def change_column_default(table_name, column_name, default)
2702
+ unless default
2703
+ raise NotImplementedError,
2704
+ "DB2 for zOS data server version 9 does not support changing the column default to NULL"
2705
+ else
2706
+ super
2707
+ end
2708
+ end
2709
+
2710
+ def change_column_null(table_name, column_name, null, default)
2711
+ raise NotImplementedError,
2712
+ "DB2 for zOS data server does not support changing the column's nullability"
2713
+ end
2714
+ end # class IBM_DB2_ZOS
2715
+
2716
+ class IBM_DB2_ZOS_8 < IBM_DB2_ZOS
2717
+ include HostedDataServer
2718
+
2719
+ def query_offset_limit(sql, offset, limit)
2720
+ if (!limit.nil?)
2721
+ sql << " FETCH FIRST #{limit} ROWS ONLY"
2722
+ end
2723
+ return sql
2724
+ end
2725
+
2726
+ def query_offset_limit!(sql, offset, limit, options)
2727
+ if (!limit.nil?)
2728
+ sql << " FETCH FIRST #{limit} ROWS ONLY"
2729
+ end
2730
+ options[:paramArray] = []
2731
+ end
2732
+
2733
+ # This call is needed on DB2 z/OS v8 for the creation of tables
2734
+ # with LOBs. When issued, this call does the following:
2735
+ # DB2 creates LOB table spaces, auxiliary tables, and indexes on auxiliary
2736
+ # tables for LOB columns.
2737
+ def setup_for_lob_table()
2738
+ execute "SET CURRENT RULES = 'STD'"
2739
+ end
2740
+
2741
+ def rename_column(table_name, column_name, new_column_name)
2742
+ raise NotImplementedError, "rename_column is not implemented for DB2 on zOS 8"
2743
+ end
2744
+
2745
+ def change_column_default(table_name, column_name, default)
2746
+ raise NotImplementedError,
2747
+ "DB2 for zOS data server version 8 does not support changing the column default"
2748
+ end
2749
+
2750
+ end # class IBM_DB2_ZOS_8
2751
+
2752
+ class IBM_DB2_I5 < IBM_DB2
2753
+ include HostedDataServer
2754
+ end # class IBM_DB2_I5
2755
+
2756
+ class IBM_IDS < IBM_DataServer
2757
+ # IDS does not support the SET SCHEMA syntax
2758
+ def set_schema(schema)
2759
+ end
2760
+
2761
+ # IDS specific ALTER TABLE statement to rename a column
2762
+ def rename_column(table_name, column_name, new_column_name)
2763
+ _table_name = table_name.to_s
2764
+ _column_name = column_name.to_s
2765
+ _new_column_name = new_column_name.to_s
2766
+
2767
+ nil_condition = _table_name.nil? || _column_name.nil? || _new_column_name.nil?
2768
+ empty_condition = _table_name.empty? ||
2769
+ _column_name.empty? ||
2770
+ _new_column_name.empty? unless nil_condition
2771
+
2772
+ if nil_condition || empty_condition
2773
+ raise ArgumentError,"One of the arguments passed to rename_column is empty or nil"
2774
+ end
2775
+
2776
+ begin
2777
+ rename_column_sql = "RENAME COLUMN #{table_name}.#{column_name} TO \
2778
+ #{new_column_name}"
2779
+
2780
+ unless stmt = execute(rename_column_sql)
2781
+ error_msg = IBM_DB.getErrormsg(@adapter.connection, IBM_DB::DB_CONN )
2782
+ if error_msg && !error_msg.empty?
2783
+ raise "Rename column failed : #{error_msg}"
2784
+ else
2785
+ raise StandardError.new('An unexpected error occurred during renaming the column')
2786
+ end
2787
+ end
2788
+
2789
+ reorg_table(_table_name)
2790
+
2791
+ ensure
2792
+ IBM_DB.free_stmt(stmt) if stmt
2793
+ end #End of begin
2794
+ end # End of rename_column
2795
+
2796
+ def primary_key_definition(start_id)
2797
+ return "SERIAL(#{start_id}) PRIMARY KEY"
2798
+ end
2799
+
2800
+ def change_column(table_name, column_name, type, options)
2801
+ if !options[:null].nil? && !options[:null]
2802
+ execute "ALTER TABLE #{table_name} MODIFY #{column_name} #{@adapter.type_to_sql(type, options[:limit], options[:precision], options[:scale])} NOT NULL"
2803
+ else
2804
+ execute "ALTER TABLE #{table_name} MODIFY #{column_name} #{@adapter.type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
2805
+ end
2806
+ if !options[:default].nil?
2807
+ change_column_default(table_name, column_name, options[:default])
2808
+ end
2809
+ reorg_table(table_name)
2810
+ end
2811
+
2812
+ # IDS specific ALTER TABLE statement to add a default clause
2813
+ # IDS requires the data type to be explicitly specified when adding the
2814
+ # DEFAULT clause
2815
+ def change_column_default(table_name, column_name, default)
2816
+ sql_type = nil
2817
+ is_nullable = true
2818
+ @adapter.columns(table_name).select do |col|
2819
+ if (col.name == column_name)
2820
+ sql_type = @adapter.type_to_sql(col.type, col.limit, col.precision, col.scale)
2821
+ is_nullable = col.null
2822
+ end
2823
+ end
2824
+ # SQL statement which alters column's default value
2825
+ change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{sql_type} DEFAULT #{@adapter.quote(default)}"
2826
+ change_column_sql << " NOT NULL" unless is_nullable
2827
+ stmt = execute(change_column_sql)
2828
+ reorg_table(table_name)
2829
+ # Ensures to free the resources associated with the statement
2830
+ ensure
2831
+ IBM_DB.free_stmt(stmt) if stmt
2832
+ end
2833
+
2834
+ # IDS specific ALTER TABLE statement to change the nullability of a column
2835
+ def change_column_null(table_name,column_name,null,default)
2836
+ if !default.nil?
2837
+ change_column_default table_name, column_name, default
2838
+ end
2839
+ sql_type = nil
2840
+ @adapter.columns(table_name).select do |col|
2841
+ if (col.name == column_name)
2842
+ sql_type = @adapter.type_to_sql(col.type, col.limit, col.precision, col.scale)
2843
+ end
2844
+ end
2845
+ if !null.nil?
2846
+ if !null
2847
+ change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{sql_type} NOT NULL"
2848
+ else
2849
+ change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{sql_type}"
2850
+ end
2851
+ stmt = execute(change_column_sql)
2852
+ reorg_table(table_name)
2853
+ end
2854
+
2855
+ ensure
2856
+ IBM_DB.free_stmt(stmt) if stmt
2857
+ end
2858
+
2859
+ # Reorganizes the table for column changes
2860
+ def reorg_table(table_name)
2861
+ execute("UPDATE STATISTICS FOR TABLE #{table_name}")
2862
+ end
2863
+
2864
+ # This method returns the IDS SQL type corresponding to the Rails
2865
+ # datetime/timestamp type
2866
+ def get_datetime_mapping
2867
+ return "datetime year to fraction(5)"
2868
+ end
2869
+
2870
+ # This method returns the IDS SQL type corresponding to the Rails
2871
+ # time type
2872
+ def get_time_mapping
2873
+ return "datetime hour to second"
2874
+ end
2875
+
2876
+ # This method returns the IDS SQL type corresponding to Rails double type
2877
+ def get_double_mapping
2878
+ return "double precision"
2879
+ end
2880
+
2881
+ # Handling offset/limit as per Informix requirements
2882
+ def query_offset_limit(sql, offset, limit)
2883
+ if limit != 0
2884
+ if !offset.nil?
2885
+ # Modifying the SQL to utilize the skip and limit amounts
2886
+ sql.gsub!(/SELECT/i,"SELECT SKIP #{offset} LIMIT #{limit}")
2887
+ else
2888
+ # Modifying the SQL to retrieve only the first #{limit} rows
2889
+ sql = sql.gsub!("SELECT","SELECT FIRST #{limit}")
2890
+ end
2891
+ else
2892
+ # Modifying the SQL to ensure that no rows will be returned
2893
+ sql.gsub!(/SELECT/i,"SELECT * FROM (SELECT")
2894
+ sql << ") WHERE 0 = 1"
2895
+ end
2896
+ end
2897
+
2898
+ # Handling offset/limit as per Informix requirements
2899
+ def query_offset_limit!(sql, offset, limit, options)
2900
+ if limit != 0
2901
+ if !offset.nil?
2902
+ # Modifying the SQL to utilize the skip and limit amounts
2903
+ sql.gsub!(/SELECT/i,"SELECT SKIP #{offset} LIMIT #{limit}")
2904
+ else
2905
+ # Modifying the SQL to retrieve only the first #{limit} rows
2906
+ sql = sql.gsub!("SELECT","SELECT FIRST #{limit}")
2907
+ end
2908
+ else
2909
+ # Modifying the SQL to ensure that no rows will be returned
2910
+ sql.gsub!(/SELECT/i,"SELECT * FROM (SELECT")
2911
+ sql << ") WHERE 0 = 1"
2912
+ end
2913
+ end
2914
+
2915
+ # Method that returns the last automatically generated ID
2916
+ # on the given +@connection+. This method is required by the +insert+
2917
+ # method. IDS returns the last generated serial value in the SQLCA unlike
2918
+ # DB2 where the generated value has to be retrieved using the
2919
+ # IDENTITY_VAL_LOCAL function. We used the "stmt" parameter to identify
2920
+ # the statement resource from which to get the last generated value
2921
+ def last_generated_id(stmt)
2922
+ IBM_DB.get_last_serial_value(stmt)
2923
+ end
2924
+
2925
+ # This method throws an error when trying to create a default value on a
2926
+ # BLOB/CLOB column for IDS. The documentation states: "if the column is a
2927
+ # BLOB or CLOB datatype, NULL is the only valid default value."
2928
+ def set_binary_default(value)
2929
+ unless (value == 'NULL')
2930
+ raise "Informix Dynamic Server only allows NULL as a valid default value for a BLOB data type"
2931
+ end
2932
+ end
2933
+
2934
+ # For Informix Dynamic Server, we treat binary value same as we treat a
2935
+ # text value. We support literals by converting the insert into a dummy
2936
+ # insert and an update (See handle_lobs method above)
2937
+ def set_binary_value
2938
+ "'@@@IBMBINARY@@@'"
2939
+ end
2940
+
2941
+ # This method throws an error when trying to create a default value on a
2942
+ # BLOB/CLOB column for IDS. The documentation states: "if the column is
2943
+ # a BLOB or CLOB datatype, NULL is the only valid default value."
2944
+ def set_text_default(value)
2945
+ unless (value == 'NULL')
2946
+ raise "Informix Dynamic Server only allows NULL as a valid default value for a CLOB data type"
2947
+ end
2948
+ end
2949
+
2950
+ # For Informix Dynamic Server, the arguments to the meta-data functions
2951
+ # need to be in lower-case
2952
+ def set_case(value)
2953
+ value.downcase
2954
+ end
2955
+ end # class IBM_IDS
2956
+ end # module ConnectionAdapters
2957
+ end # module ActiveRecord
2958
+
2959
+ module Arel
2960
+ module Visitors
2961
+ class Visitor #opening and closing the class to ensure backward compatibility
2962
+ end
2963
+
2964
+ class ToSql < Arel::Visitors::Visitor #opening and closing the class to ensure backward compatibility
2965
+ # In case when using Rails-2.3.x there is no arel used due to which the constructor has to be defined explicitly
2966
+ # to ensure the same code works on any version of Rails
2967
+
2968
+ #Check Arel version
2969
+ begin
2970
+ @arelVersion = Arel::VERSION.to_i
2971
+ rescue
2972
+ @arelVersion = 0
2973
+ end
2974
+
2975
+ if(@arelVersion >= 3)
2976
+ def initialize connection
2977
+ @connection = connection
2978
+ @schema_cache = connection.schema_cache if(connection.respond_to?(:schema_cache))
2979
+ @quoted_tables = {}
2980
+ @quoted_columns = {}
2981
+ @last_column = nil
2982
+ end
2983
+ end
2984
+ end
2985
+
2986
+ class IBM_DB < Arel::Visitors::ToSql
2987
+ private
2988
+
2989
+ def visit_Arel_Nodes_Limit o, a=nil
2990
+ visit o.expr
2991
+ end
2992
+
2993
+ def visit_Arel_Nodes_Offset o, a=nil
2994
+ visit o.expr
2995
+ end
2996
+
2997
+ def visit_Arel_Nodes_SelectStatement o, a=nil
2998
+ #Interim fix for backward compatibility [Arel 4.0.0 and below]
2999
+ if self.method(:visit_Arel_Nodes_SelectCore).arity == 1
3000
+ sql = [
3001
+ (visit(o.with) if o.with),
3002
+ o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
3003
+ ("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
3004
+ ].compact.join ' '
3005
+ else
3006
+ sql = [
3007
+ (visit(o.with) if o.with),
3008
+ o.cores.map { |x| visit_Arel_Nodes_SelectCore x,a }.join,
3009
+ ("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
3010
+ ].compact.join ' '
3011
+ end
3012
+
3013
+ if o.limit
3014
+ limit = visit(o.limit)
3015
+ else
3016
+ limit = nil
3017
+ end
3018
+
3019
+ if o.offset
3020
+ offset = visit(o.offset)
3021
+ else
3022
+ offset = nil
3023
+ end
3024
+ @connection.add_limit_offset!(sql, {:limit => limit, :offset => offset})
3025
+ sql << " #{(visit(o.lock) if o.lock)}"
3026
+ return sql
3027
+ end
3028
+
3029
+ end
3030
+ end
3031
+ end