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