Module: ArJdbc::DB2

Defined in:
lib/arjdbc/db2/adapter.rb

Defined Under Namespace

Modules: Column

Constant Summary collapse

HAVE_LIMIT =

only record precision and scale for types that can set them via CREATE TABLE: publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.sql.ref.doc/doc/r0000927.html

%w(FLOAT DECFLOAT CHAR VARCHAR CLOB BLOB NCHAR NCLOB DBCLOB GRAPHIC VARGRAPHIC)
HAVE_PRECISION =

TIMESTAMP

%w(DECIMAL NUMERIC)
HAVE_SCALE =
%w(DECIMAL NUMERIC)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.column_selectorObject



3
4
5
6
# File 'lib/arjdbc/db2/adapter.rb', line 3

def self.column_selector
  [ /(db2|as400)/i,
    lambda { |cfg, column| column.extend(::ArJdbc::DB2::Column) } ]
end

.jdbc_connection_classObject



8
9
10
# File 'lib/arjdbc/db2/adapter.rb', line 8

def self.jdbc_connection_class
  ::ActiveRecord::ConnectionAdapters::DB2JdbcConnection
end

Instance Method Details

#_execute(sql, name = nil) ⇒ Object



93
94
95
96
97
98
99
100
101
# File 'lib/arjdbc/db2/adapter.rb', line 93

def _execute(sql, name = nil)
  if ActiveRecord::ConnectionAdapters::JdbcConnection::select?(sql)
    @connection.execute_query(sql)
  elsif ActiveRecord::ConnectionAdapters::JdbcConnection::insert?(sql)
    (@connection.execute_insert(sql) or last_insert_id(sql)).to_i
  else
    @connection.execute_update(sql)
  end
end

#adapter_nameObject



146
147
148
# File 'lib/arjdbc/db2/adapter.rb', line 146

def adapter_name
  'DB2'
end

#add_limit_offset!(sql, options) ⇒ Object



155
156
157
# File 'lib/arjdbc/db2/adapter.rb', line 155

def add_limit_offset!(sql, options)
  replace_limit_offset!(sql, options[:limit], options[:offset])
end

#add_quotes(name) ⇒ Object



334
335
336
337
# File 'lib/arjdbc/db2/adapter.rb', line 334

def add_quotes(name)
  return name unless name
  %Q{"#{name}"}
end

#arel2_visitorsObject



150
151
152
153
# File 'lib/arjdbc/db2/adapter.rb', line 150

def arel2_visitors
  require 'arel/visitors/db2'
  {'db2' => ::Arel::Visitors::DB2, 'as400' => ::Arel::Visitors::DB2}
end

#change_column(table_name, column_name, type, options = {}) ⇒ Object



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/arjdbc/db2/adapter.rb', line 264

def change_column(table_name, column_name, type, options = {})
  data_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
  sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DATA TYPE #{data_type}"
  as400? ? execute_and_auto_confirm(sql) : execute(sql)
  reorg_table(table_name)

  if options.include?(:default) and options.include?(:null)
    # which to run first?
    if options[:null] or options[:default].nil?
      change_column_null(table_name, column_name, options[:null])
      change_column_default(table_name, column_name, options[:default])
    else
      change_column_default(table_name, column_name, options[:default])
      change_column_null(table_name, column_name, options[:null])
    end
  elsif options.include?(:default)
    change_column_default(table_name, column_name, options[:default])
  elsif options.include?(:null)
    change_column_null(table_name, column_name, options[:null])
  end
end

#change_column_default(table_name, column_name, default) ⇒ Object



255
256
257
258
259
260
261
262
# File 'lib/arjdbc/db2/adapter.rb', line 255

def change_column_default(table_name, column_name, default)
  if default.nil?
    execute_and_auto_confirm "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} DROP DEFAULT"
  else
    execute_and_auto_confirm "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET WITH DEFAULT #{quote(default)}"
  end
  reorg_table(table_name)
end

#change_column_null(table_name, column_name, null) ⇒ Object



246
247
248
249
250
251
252
253
# File 'lib/arjdbc/db2/adapter.rb', line 246

def change_column_null(table_name, column_name, null)
  if null
    execute_and_auto_confirm "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} DROP NOT NULL"
  else
    execute_and_auto_confirm "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET NOT NULL"
  end
  reorg_table(table_name)
end

#columns(table_name, name = nil) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/arjdbc/db2/adapter.rb', line 311

def columns(table_name, name = nil)
  cols = @connection.columns(table_name, name, db2_schema)

  # scrub out sizing info when CREATE TABLE doesn't support it
  # but JDBC reports it (doh!)
  for col in cols
    base_sql_type = col.sql_type.sub(/\(.*/, "").upcase
    col.limit = nil unless HAVE_LIMIT.include?(base_sql_type)
    col.precision = nil unless HAVE_PRECISION.include?(base_sql_type)
    #col.scale = nil unless HAVE_SCALE.include?(base_sql_type)
  end

  cols
end

#execute_and_auto_confirm(sql) ⇒ Object

holy moly batman! all this to tell AS400 “yes i am sure”



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/arjdbc/db2/adapter.rb', line 104

def execute_and_auto_confirm(sql)
  begin
    @connection.execute_update "call qsys.qcmdexc('QSYS/CHGJOB INQMSGRPY(*SYSRPYL)',0000000031.00000)"
    @connection.execute_update "call qsys.qcmdexc('ADDRPYLE SEQNBR(9876) MSGID(CPA32B2) RPY(''I'')',0000000045.00000)"
  rescue Exception => e
    raise "Could not call CHGJOB INQMSGRPY(*SYSRPYL) and ADDRPYLE SEQNBR(9876) MSGID(CPA32B2) RPY('I').\n" +
      "Do you have authority to do this?\n\n" + e.to_s
  end

  r = execute sql

  begin
    @connection.execute_update "call qsys.qcmdexc('QSYS/CHGJOB INQMSGRPY(*DFT)',0000000027.00000)"
    @connection.execute_update "call qsys.qcmdexc('RMVRPYLE SEQNBR(9876)',0000000021.00000)"
  rescue Exception => e
    raise "Could not call CHGJOB INQMSGRPY(*DFT) and RMVRPYLE SEQNBR(9876).\n" +
      "Do you have authority to do this?\n\n" + e.to_s
  end
  r
end

#expand_double_quotes(name) ⇒ Object



345
346
347
348
# File 'lib/arjdbc/db2/adapter.rb', line 345

def expand_double_quotes(name)
  return name unless name && name['"']
  name.gsub(/"/,'""')
end

#indexes(table_name, name = nil) ⇒ Object



330
331
332
# File 'lib/arjdbc/db2/adapter.rb', line 330

def indexes(table_name, name = nil)
  @connection.indexes(table_name, name, db2_schema)
end

#jdbc_columns(table_name, name = nil) ⇒ Object



326
327
328
# File 'lib/arjdbc/db2/adapter.rb', line 326

def jdbc_columns(table_name, name = nil)
  columns(table_name, name)
end

#last_insert_id(sql) ⇒ Object



125
126
127
128
129
130
131
# File 'lib/arjdbc/db2/adapter.rb', line 125

def last_insert_id(sql)
  table_name = sql.split(/\s/)[2]
  result = select(ActiveRecord::Base.send(:sanitize_sql,
      %[select IDENTITY_VAL_LOCAL() as last_insert_id from #{table_name}],
      nil))
  result.last['last_insert_id']
end

#modify_types(tp) ⇒ Object



133
134
135
136
137
138
139
# File 'lib/arjdbc/db2/adapter.rb', line 133

def modify_types(tp)
  tp[:primary_key] = 'int not null generated by default as identity (start with 1) primary key'
  tp[:string][:limit] = 255
  tp[:integer][:limit] = nil
  tp[:boolean] = {:name => "smallint"}
  tp
end

#pk_and_sequence_for(table) ⇒ Object



177
178
179
180
181
182
183
184
185
# File 'lib/arjdbc/db2/adapter.rb', line 177

def pk_and_sequence_for(table)
  # In JDBC/DB2 side, only upcase names of table and column are handled.
  keys = super(table.upcase)
  if keys && keys[0]
    # In ActiveRecord side, only downcase names of table and column are handled.
    keys[0] = keys[0].downcase
  end
  keys
end

#quote(value, column = nil) ⇒ Object

:nodoc:



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/arjdbc/db2/adapter.rb', line 191

def quote(value, column = nil) # :nodoc:
  if column && column.respond_to?(:primary) && column.primary && column.klass != String
    return value.to_i.to_s
  end
  if column && (column.type == :decimal || column.type == :integer) && value
    return value.to_s
  end
  case value
  when String
    if column && column.type == :binary
      "BLOB('#{quote_string(value)}')"
    else
      "'#{quote_string(value)}'"
    end
  else super
  end
end

#quote_column_name(column_name) ⇒ Object



187
188
189
# File 'lib/arjdbc/db2/adapter.rb', line 187

def quote_column_name(column_name)
  column_name
end

#quote_string(string) ⇒ Object



209
210
211
# File 'lib/arjdbc/db2/adapter.rb', line 209

def quote_string(string)
  string.gsub(/'/, "''") # ' (for ruby-mode)
end

#quoted_falseObject



217
218
219
# File 'lib/arjdbc/db2/adapter.rb', line 217

def quoted_false
  '0'
end

#quoted_trueObject



213
214
215
# File 'lib/arjdbc/db2/adapter.rb', line 213

def quoted_true
  '1'
end

#recreate_database(name) ⇒ Object



227
228
229
# File 'lib/arjdbc/db2/adapter.rb', line 227

def recreate_database(name)
  tables.each {|table| drop_table("#{db2_schema}.#{table}")}
end

#remove_column(table_name, column_name) ⇒ Object



287
288
289
290
291
292
# File 'lib/arjdbc/db2/adapter.rb', line 287

def remove_column(table_name, column_name) #:nodoc:
  sql = "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"

  as400? ? execute_and_auto_confirm(sql) : execute(sql)
  reorg_table(table_name)
end

#remove_index(table_name, options = { }) ⇒ Object



231
232
233
# File 'lib/arjdbc/db2/adapter.rb', line 231

def remove_index(table_name, options = { })
  execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object



237
238
239
240
241
242
243
244
# File 'lib/arjdbc/db2/adapter.rb', line 237

def rename_column(table_name, column_name, new_column_name) #:nodoc:
  if as400?
    raise NotImplementedError, "rename_column is not supported on IBM i"
  else
    execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
    reorg_table(table_name)
  end
end

#rename_table(name, new_name) ⇒ Object



295
296
297
298
# File 'lib/arjdbc/db2/adapter.rb', line 295

def rename_table(name, new_name) #:nodoc:
  execute "RENAME TABLE #{name} TO #{new_name}"
  reorg_table(new_name)
end

#reorg_table(table_name) ⇒ Object



221
222
223
224
225
# File 'lib/arjdbc/db2/adapter.rb', line 221

def reorg_table(table_name)
  unless as400?
    @connection.execute_update "call sysproc.admin_cmd ('REORG TABLE #{table_name}')"
  end
end

#replace_limit_offset!(sql, limit, offset) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/arjdbc/db2/adapter.rb', line 159

def replace_limit_offset!(sql, limit, offset)
  if limit
    limit = limit.to_i
    if !offset
      if limit == 1
        sql << " FETCH FIRST ROW ONLY"
      else
        sql << " FETCH FIRST #{limit} ROWS ONLY"
      end
    else
      offset = offset.to_i
      sql.gsub!(/SELECT/i, 'SELECT B.* FROM (SELECT A.*, row_number() over () AS internal$rownum FROM (SELECT')
      sql << ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rownum <= #{limit + offset}"
    end
  end
  sql
end

#strip_quotes(str) ⇒ Object



339
340
341
342
343
# File 'lib/arjdbc/db2/adapter.rb', line 339

def strip_quotes(str)
  return str unless str
  return str unless /^(["']).*\1$/ =~ str
  str[1..-2]
end

#structure_dumpObject

:nodoc:



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/arjdbc/db2/adapter.rb', line 350

def structure_dump #:nodoc:
  definition=""
  rs = @connection.connection..getTables(nil,db2_schema.upcase,nil,["TABLE"].to_java(:string))
  while rs.next
    tname = rs.getString(3)
    definition << "CREATE TABLE #{tname} (\n"
    rs2 = @connection.connection..getColumns(nil,db2_schema.upcase,tname,nil)
    first_col = true
    while rs2.next
      col_name = add_quotes(rs2.getString(4));
      default = ""
      d1 = rs2.getString(13)
      # IBM i (as400 toolbox driver) will return an empty string if there is no default
      if @config[:url] =~ /^jdbc:as400:/
        default = !d1.blank? ? " DEFAULT #{d1}" : ""
      else
        default = d1 ? " DEFAULT #{d1}" : ""
      end

      type = rs2.getString(6)
      col_precision = rs2.getString(7)
      col_scale = rs2.getString(9)
      col_size = ""
      if HAVE_SCALE.include?(type) and col_scale
        col_size = "(#{col_precision},#{col_scale})"
      elsif (HAVE_LIMIT + HAVE_PRECISION).include?(type) and col_precision
        col_size = "(#{col_precision})"
      end
      nulling = (rs2.getString(18) == 'NO' ? " NOT NULL" : "")
      create_col_string = add_quotes(expand_double_quotes(strip_quotes(col_name))) +
        " " +
        type +
        col_size +
        "" +
        nulling +
        default
      if !first_col
        create_col_string = ",\n #{create_col_string}"
      else
        create_col_string = " #{create_col_string}"
      end

      definition << create_col_string

      first_col = false
    end
    definition << ");\n\n"
  end
  definition
end

#tablesObject



300
301
302
# File 'lib/arjdbc/db2/adapter.rb', line 300

def tables
  @connection.tables(nil, db2_schema, nil, ["TABLE"])
end

#type_to_sql(type, limit = nil, precision = nil, scale = nil) ⇒ Object



141
142
143
144
# File 'lib/arjdbc/db2/adapter.rb', line 141

def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  limit = nil if type.to_sym == :integer
  super(type, limit, precision, scale)
end