db2 2.5.6 → 2.5.10

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