ActiveRecord-JDBC 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1133 @@
1
+
2
+ module JdbcSpec
3
+ module HSQLDB
4
+ module Column
5
+ def type_cast(value)
6
+ return nil if value.nil? || value =~ /^\s*null\s*$/i
7
+ case type
8
+ when :string then value
9
+ when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
10
+ when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
11
+ when :float then value.to_f
12
+ when :datetime then cast_to_date_or_time(value)
13
+ when :timestamp then cast_to_time(value)
14
+ when :binary then value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*")
15
+ when :time then cast_to_time(value)
16
+ else value
17
+ end
18
+ end
19
+ def cast_to_date_or_time(value)
20
+ return value if value.is_a? Date
21
+ return nil if value.blank?
22
+ guess_date_or_time (value.is_a? Time) ? value : cast_to_time(value)
23
+ end
24
+
25
+ def cast_to_time(value)
26
+ return value if value.is_a? Time
27
+ time_array = ParseDate.parsedate value
28
+ time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
29
+ Time.send(Base.default_timezone, *time_array) rescue nil
30
+ end
31
+
32
+ def guess_date_or_time(value)
33
+ (value.hour == 0 and value.min == 0 and value.sec == 0) ?
34
+ Date.new(value.year, value.month, value.day) : value
35
+ end
36
+ end
37
+
38
+ def modify_types(tp)
39
+ tp[:primary_key] = "INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY"
40
+ tp[:integer][:limit] = nil
41
+ tp[:boolean][:limit] = nil
42
+ tp[:datetime] = { :name => "DATETIME" }
43
+ tp[:timestamp] = { :name => "DATETIME" }
44
+ tp[:time] = { :name => "DATETIME" }
45
+ tp[:date] = { :name => "DATETIME" }
46
+ tp
47
+ end
48
+
49
+ def quote(value, column = nil) # :nodoc:
50
+ case value
51
+ when String
52
+ if column && column.type == :binary
53
+ "'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}'"
54
+ else
55
+ "'#{quote_string(value)}'"
56
+ end
57
+ else super
58
+ end
59
+ end
60
+
61
+ def quoted_true
62
+ '1'
63
+ end
64
+
65
+ def quoted_false
66
+ '0'
67
+ end
68
+
69
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
70
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"
71
+ end
72
+
73
+ def change_column_default(table_name, column_name, default) #:nodoc:
74
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{quote(default)}"
75
+ end
76
+
77
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
78
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} RENAME TO #{new_column_name}"
79
+ end
80
+
81
+ def rename_table(name, new_name)
82
+ execute "ALTER TABLE #{name} RENAME TO #{new_name}"
83
+ end
84
+
85
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
86
+ execute(sql, name)
87
+ table = sql.split(" ", 4)[2]
88
+ id_value || last_insert_id(table, nil)
89
+ end
90
+
91
+ def last_insert_id(table, sequence_name)
92
+ Integer(select_value("SELECT IDENTITY() FROM #{table}"))
93
+ end
94
+
95
+ def add_limit_offset!(sql, options) #:nodoc:
96
+ offset = options[:offset] || 0
97
+ bef = sql[7..-1]
98
+ if limit = options[:limit]
99
+ sql.replace "select limit #{offset} #{limit} #{bef}"
100
+ elsif offset > 0
101
+ sql.replace "select limit #{offset} 0 #{bef}"
102
+ end
103
+ end
104
+ end
105
+
106
+ module Oracle
107
+ module Column
108
+ def type_cast(value)
109
+ return nil if value.nil? || value =~ /^\s*null\s*$/i
110
+ case type
111
+ when :string then value
112
+ when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
113
+ when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
114
+ when :float then value.to_f
115
+ when :datetime then cast_to_date_or_time(value)
116
+ when :time then cast_to_time(value)
117
+ else value
118
+ end
119
+ end
120
+
121
+ private
122
+ def simplified_type(field_type)
123
+ case field_type
124
+ when /char/i : :string
125
+ when /num|float|double|dec|real|int/i : @scale == 0 ? :integer : :float
126
+ when /date|time/i : @name =~ /_at$/ ? :time : :datetime
127
+ when /clob/i : :text
128
+ when /blob/i : :binary
129
+ end
130
+ end
131
+
132
+ def cast_to_date_or_time(value)
133
+ return value if value.is_a? Date
134
+ return nil if value.blank?
135
+ guess_date_or_time (value.is_a? Time) ? value : cast_to_time(value)
136
+ end
137
+
138
+ def cast_to_time(value)
139
+ return value if value.is_a? Time
140
+ time_array = ParseDate.parsedate value
141
+ time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
142
+ Time.send(Base.default_timezone, *time_array) rescue nil
143
+ end
144
+
145
+ def guess_date_or_time(value)
146
+ (value.hour == 0 and value.min == 0 and value.sec == 0) ?
147
+ Date.new(value.year, value.month, value.day) : value
148
+ end
149
+ end
150
+
151
+ def default_sequence_name(table, column) #:nodoc:
152
+ "#{table}_seq"
153
+ end
154
+
155
+ def create_table(name, options = {}) #:nodoc:
156
+ super(name, options)
157
+ execute "CREATE SEQUENCE #{name}_seq START WITH 10000" unless options[:id] == false
158
+ end
159
+
160
+ def rename_table(name, new_name) #:nodoc:
161
+ execute "RENAME #{name} TO #{new_name}"
162
+ execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil
163
+ end
164
+
165
+ def drop_table(name) #:nodoc:
166
+ super(name)
167
+ execute "DROP SEQUENCE #{name}_seq" rescue nil
168
+ end
169
+
170
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
171
+ if pk.nil? # Who called us? What does the sql look like? No idea!
172
+ execute sql, name
173
+ elsif id_value # Pre-assigned id
174
+ log(sql, name) { @connection.execute_insert sql,pk }
175
+ else # Assume the sql contains a bind-variable for the id
176
+ id_value = select_one("select #{sequence_name}.nextval id from dual")['id']
177
+ log(sql, name) {
178
+ execute_prepared_insert(sql,id_value)
179
+ }
180
+ end
181
+ id_value
182
+ end
183
+
184
+ def execute_prepared_insert(sql, id)
185
+ @stmts ||= {}
186
+ @stmts[sql] ||= @connection.ps(sql)
187
+ stmt = @stmts[sql]
188
+ stmt.setLong(1,id)
189
+ stmt.executeUpdate
190
+ id
191
+ end
192
+
193
+ def modify_types(tp)
194
+ tp[:primary_key] = "NUMBER(38) NOT NULL PRIMARY KEY"
195
+ tp[:datetime] = { :name => "DATE" }
196
+ tp[:timestamp] = { :name => "DATE" }
197
+ tp[:time] = { :name => "DATE" }
198
+ tp[:date] = { :name => "DATE" }
199
+ tp
200
+ end
201
+
202
+ def add_limit_offset!(sql, options) #:nodoc:
203
+ offset = options[:offset] || 0
204
+
205
+ if limit = options[:limit]
206
+ sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
207
+ elsif offset > 0
208
+ sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
209
+ end
210
+ end
211
+
212
+ def current_database #:nodoc:
213
+ select_one("select sys_context('userenv','db_name') db from dual")["db"]
214
+ end
215
+
216
+ def indexes(table_name, name = nil) #:nodoc:
217
+ result = select_all(<<-SQL, name)
218
+ SELECT lower(i.index_name) as index_name, i.uniqueness, lower(c.column_name) as column_name
219
+ FROM user_indexes i, user_ind_columns c
220
+ WHERE i.table_name = '#{table_name.to_s.upcase}'
221
+ AND c.index_name = i.index_name
222
+ AND i.index_name NOT IN (SELECT index_name FROM user_constraints WHERE constraint_type = 'P')
223
+ ORDER BY i.index_name, c.column_position
224
+ SQL
225
+
226
+ current_index = nil
227
+ indexes = []
228
+
229
+ result.each do |row|
230
+ if current_index != row['index_name']
231
+ indexes << IndexDefinition.new(table_name, row['index_name'], row['uniqueness'] == "UNIQUE", [])
232
+ current_index = row['index_name']
233
+ end
234
+
235
+ indexes.last.columns << row['column_name']
236
+ end
237
+
238
+ indexes
239
+ end
240
+
241
+ def remove_index(table_name, options = {}) #:nodoc:
242
+ execute "DROP INDEX #{index_name(table_name, options)}"
243
+ end
244
+
245
+ def change_column_default(table_name, column_name, default) #:nodoc:
246
+ execute "ALTER TABLE #{table_name} MODIFY #{column_name} DEFAULT #{quote(default)}"
247
+ end
248
+
249
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
250
+ change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit])}"
251
+ add_column_options!(change_column_sql, options)
252
+ execute(change_column_sql)
253
+ end
254
+
255
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
256
+ execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} to #{new_column_name}"
257
+ end
258
+
259
+ def remove_column(table_name, column_name) #:nodoc:
260
+ execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
261
+ end
262
+
263
+ def structure_dump #:nodoc:
264
+ s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
265
+ structure << "create sequence #{seq.to_a.first.last};\n\n"
266
+ end
267
+
268
+ select_all("select table_name from user_tables").inject(s) do |structure, table|
269
+ ddl = "create table #{table.to_a.first.last} (\n "
270
+ cols = select_all(%Q{
271
+ select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
272
+ from user_tab_columns
273
+ where table_name = '#{table.to_a.first.last}'
274
+ order by column_id
275
+ }).map do |row|
276
+ col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"
277
+ if row['data_type'] =='NUMBER' and !row['data_precision'].nil?
278
+ col << "(#{row['data_precision'].to_i}"
279
+ col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil?
280
+ col << ')'
281
+ elsif row['data_type'].include?('CHAR')
282
+ col << "(#{row['data_length'].to_i})"
283
+ end
284
+ col << " default #{row['data_default']}" if !row['data_default'].nil?
285
+ col << ' not null' if row['nullable'] == 'N'
286
+ col
287
+ end
288
+ ddl << cols.join(",\n ")
289
+ ddl << ");\n\n"
290
+ structure << ddl
291
+ end
292
+ end
293
+
294
+ def structure_drop #:nodoc:
295
+ s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq|
296
+ drop << "drop sequence #{seq.to_a.first.last};\n\n"
297
+ end
298
+
299
+ select_all("select table_name from user_tables").inject(s) do |drop, table|
300
+ drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n"
301
+ end
302
+ end
303
+
304
+ # QUOTING ==================================================
305
+ #
306
+ # see: abstract/quoting.rb
307
+
308
+ # camelCase column names need to be quoted; not that anyone using Oracle
309
+ # would really do this, but handling this case means we pass the test...
310
+ def quote_column_name(name) #:nodoc:
311
+ name =~ /[A-Z]/ ? "\"#{name}\"" : name
312
+ end
313
+
314
+ def quote_string(string) #:nodoc:
315
+ string.gsub(/'/, "''")
316
+ end
317
+
318
+ def quote(value, column = nil) #:nodoc:
319
+ if column && column.type == :binary
320
+ if /(.*?)\([0-9]+\)/ =~ column.sql_type
321
+ %Q{empty_#{ $1 }()}
322
+ else
323
+ %Q{empty_#{ column.sql_type rescue 'blob' }()}
324
+ end
325
+ else
326
+ if column && column.type == :primary_key
327
+ return value.to_s
328
+ end
329
+ case value
330
+ when String : %Q{'#{quote_string(value)}'}
331
+ when NilClass : 'null'
332
+ when TrueClass : '1'
333
+ when FalseClass : '0'
334
+ when Numeric : value.to_s
335
+ when Date, Time : %Q{TIMESTAMP'#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
336
+ else %Q{'#{quote_string(value.to_yaml)}'}
337
+ end
338
+ end
339
+ end
340
+ end
341
+
342
+ module PostgreSQL
343
+ module Column
344
+ def type_cast(value)
345
+ return nil if value.nil? || value =~ /^\s*null\s*$/i
346
+ case type
347
+ when :string then value
348
+ when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
349
+ when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
350
+ when :float then value.to_f
351
+ when :datetime then cast_to_date_or_time(value)
352
+ when :timestamp then cast_to_time(value)
353
+ when :time then cast_to_time(value)
354
+ else value
355
+ end
356
+ end
357
+ def cast_to_date_or_time(value)
358
+ return value if value.is_a? Date
359
+ return nil if value.blank?
360
+ guess_date_or_time (value.is_a? Time) ? value : cast_to_time(value)
361
+ end
362
+
363
+ def cast_to_time(value)
364
+ return value if value.is_a? Time
365
+ time_array = ParseDate.parsedate value
366
+ time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
367
+ Time.send(Base.default_timezone, *time_array) rescue nil
368
+ end
369
+
370
+ def guess_date_or_time(value)
371
+ (value.hour == 0 and value.min == 0 and value.sec == 0) ?
372
+ Date.new(value.year, value.month, value.day) : value
373
+ end
374
+ def default_value(value)
375
+ # Boolean types
376
+ return "t" if value =~ /true/i
377
+ return "f" if value =~ /false/i
378
+
379
+ # Char/String/Bytea type values
380
+ return $1 if value =~ /^'(.*)'::(bpchar|text|character varying|bytea)$/
381
+
382
+ # Numeric values
383
+ return value if value =~ /^-?[0-9]+(\.[0-9]*)?/
384
+
385
+ # Fixed dates / timestamp
386
+ return $1 if value =~ /^'(.+)'::(date|timestamp)/
387
+
388
+ # Anything else is blank, some user type, or some function
389
+ # and we can't know the value of that, so return nil.
390
+ return nil
391
+ end
392
+ end
393
+
394
+ def modify_types(tp)
395
+ tp[:primary_key] = "serial primary key"
396
+ tp[:string][:limit] = 255
397
+ tp[:integer][:limit] = nil
398
+ tp[:boolean][:limit] = nil
399
+ tp
400
+ end
401
+
402
+ def default_sequence_name(table_name, pk = nil)
403
+ default_pk, default_seq = pk_and_sequence_for(table_name)
404
+ default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
405
+ end
406
+
407
+ # Find a table's primary key and sequence.
408
+ def pk_and_sequence_for(table)
409
+ # First try looking for a sequence with a dependency on the
410
+ # given table's primary key.
411
+ result = select(<<-end_sql, 'PK and serial sequence')[0]
412
+ SELECT attr.attname AS nm, name.nspname AS nsp, seq.relname AS rel
413
+ FROM pg_class seq,
414
+ pg_attribute attr,
415
+ pg_depend dep,
416
+ pg_namespace name,
417
+ pg_constraint cons
418
+ WHERE seq.oid = dep.objid
419
+ AND seq.relnamespace = name.oid
420
+ AND seq.relkind = 'S'
421
+ AND attr.attrelid = dep.refobjid
422
+ AND attr.attnum = dep.refobjsubid
423
+ AND attr.attrelid = cons.conrelid
424
+ AND attr.attnum = cons.conkey[1]
425
+ AND cons.contype = 'p'
426
+ AND dep.refobjid = '#{table}'::regclass
427
+ end_sql
428
+
429
+ if result.nil? or result.empty?
430
+ # If that fails, try parsing the primary key's default value.
431
+ # Support the 7.x and 8.0 nextval('foo'::text) as well as
432
+ # the 8.1+ nextval('foo'::regclass).
433
+ # TODO: assumes sequence is in same schema as table.
434
+ result = select(<<-end_sql, 'PK and custom sequence')[0]
435
+ SELECT attr.attname AS nm, name.nspname AS nsp, split_part(def.adsrc, '\\\'', 2) AS rel
436
+ FROM pg_class t
437
+ JOIN pg_namespace name ON (t.relnamespace = name.oid)
438
+ JOIN pg_attribute attr ON (t.oid = attrelid)
439
+ JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
440
+ JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
441
+ WHERE t.oid = '#{table}'::regclass
442
+ AND cons.contype = 'p'
443
+ AND def.adsrc ~* 'nextval'
444
+ end_sql
445
+ end
446
+ # check for existence of . in sequence name as in public.foo_sequence. if it does not exist, join the current namespace
447
+ result['rel']['.'] ? [result['nm'], result['rel']] : [result['nm'], "#{result['nsp']}.#{result['rel']}"]
448
+ rescue
449
+ nil
450
+ end
451
+
452
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
453
+ execute(sql, name)
454
+ table = sql.split(" ", 4)[2]
455
+ id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
456
+ end
457
+
458
+ def last_insert_id(table, sequence_name)
459
+ Integer(select_value("SELECT currval('#{sequence_name}')"))
460
+ end
461
+
462
+ def quote(value, column = nil)
463
+ if value.kind_of?(String) && column && column.type == :binary
464
+ "'#{escape_bytea(value)}'"
465
+ elsif column && column.type == :primary_key
466
+ return value.to_s
467
+ else
468
+ super
469
+ end
470
+ end
471
+
472
+ def quote_column_name(name)
473
+ %("#{name}")
474
+ end
475
+
476
+ def rename_table(name, new_name)
477
+ execute "ALTER TABLE #{name} RENAME TO #{new_name}"
478
+ end
479
+
480
+ def add_column(table_name, column_name, type, options = {})
481
+ execute("ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}")
482
+ execute("ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL") if options[:null] == false
483
+ change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
484
+ end
485
+
486
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
487
+ begin
488
+ execute "ALTER TABLE #{table_name} ALTER #{column_name} TYPE #{type_to_sql(type, options[:limit])}"
489
+ rescue ActiveRecord::StatementInvalid
490
+ # This is PG7, so we use a more arcane way of doing it.
491
+ begin_db_transaction
492
+ add_column(table_name, "#{column_name}_ar_tmp", type, options)
493
+ execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit])})"
494
+ remove_column(table_name, column_name)
495
+ rename_column(table_name, "#{column_name}_ar_tmp", column_name)
496
+ commit_db_transaction
497
+ end
498
+ change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
499
+ end
500
+
501
+ def change_column_default(table_name, column_name, default) #:nodoc:
502
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT '#{default}'"
503
+ end
504
+
505
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
506
+ execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
507
+ end
508
+
509
+ def remove_index(table_name, options) #:nodoc:
510
+ execute "DROP INDEX #{index_name(table_name, options)}"
511
+ end
512
+ end
513
+
514
+ module MySQL
515
+ def modify_types(tp)
516
+ tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
517
+ tp[:decimal] = { :name => "decimal" }
518
+ tp
519
+ end
520
+
521
+ # QUOTING ==================================================
522
+
523
+ def quote(value, column = nil)
524
+ if column && column.type == :primary_key
525
+ return value.to_s
526
+ end
527
+ super
528
+ end
529
+
530
+ def quote_column_name(name) #:nodoc:
531
+ "`#{name}`"
532
+ end
533
+
534
+ # from active_record/vendor/mysql.rb
535
+ def quote_string(str) #:nodoc:
536
+ str.gsub(/([\0\n\r\032\'\"\\])/) do
537
+ case $1
538
+ when "\0" then "\\0"
539
+ when "\n" then "\\n"
540
+ when "\r" then "\\r"
541
+ when "\032" then "\\Z"
542
+ else "\\"+$1
543
+ end
544
+ end
545
+ end
546
+
547
+ def quoted_true
548
+ "1"
549
+ end
550
+
551
+ def quoted_false
552
+ "0"
553
+ end
554
+
555
+ # SCHEMA STATEMENTS ========================================
556
+
557
+ def structure_dump #:nodoc:
558
+ if supports_views?
559
+ sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
560
+ else
561
+ sql = "SHOW TABLES"
562
+ end
563
+
564
+ select_all(sql).inject("") do |structure, table|
565
+ table.delete('Table_type')
566
+ structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n"
567
+ end
568
+ end
569
+
570
+ def recreate_database(name) #:nodoc:
571
+ drop_database(name)
572
+ create_database(name)
573
+ end
574
+
575
+ def create_database(name) #:nodoc:
576
+ execute "CREATE DATABASE `#{name}`"
577
+ end
578
+
579
+ def drop_database(name) #:nodoc:
580
+ execute "DROP DATABASE IF EXISTS `#{name}`"
581
+ end
582
+
583
+ def current_database
584
+ select_one("SELECT DATABASE() as db")["db"]
585
+ end
586
+
587
+ def indexes(table_name, name = nil)#:nodoc:
588
+ indexes = []
589
+ current_index = nil
590
+ execute("SHOW KEYS FROM #{table_name}", name).each do |row|
591
+ if current_index != row[2]
592
+ next if row[2] == "PRIMARY" # skip the primary key
593
+ current_index = row[2]
594
+ indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
595
+ end
596
+
597
+ indexes.last.columns << row[4]
598
+ end
599
+ indexes
600
+ end
601
+
602
+ def create_table(name, options = {}) #:nodoc:
603
+ super(name, {:options => "ENGINE=InnoDB"}.merge(options))
604
+ end
605
+
606
+ def rename_table(name, new_name)
607
+ execute "RENAME TABLE #{name} TO #{new_name}"
608
+ end
609
+
610
+ def change_column_default(table_name, column_name, default) #:nodoc:
611
+ current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["type"]
612
+
613
+ type, limit = native_sql_to_type(current_type)
614
+
615
+ change_column(table_name, column_name, type, { :default => default, :limit => limit })
616
+ end
617
+
618
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
619
+ options[:default] ||= select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["default"]
620
+
621
+ change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit])}"
622
+ add_column_options!(change_column_sql, options)
623
+ execute(change_column_sql)
624
+ end
625
+
626
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
627
+ current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["type"]
628
+ execute "ALTER TABLE #{table_name} CHANGE #{column_name} #{new_column_name} #{current_type}"
629
+ end
630
+ end
631
+
632
+ module Derby
633
+ module Column
634
+ def type_cast(value)
635
+ return nil if value.nil? || value =~ /^\s*null\s*$/i
636
+ case type
637
+ when :string then value
638
+ when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
639
+ when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
640
+ when :float then value.to_f
641
+ when :datetime then cast_to_date_or_time(value)
642
+ when :timestamp then cast_to_time(value)
643
+ when :binary then value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*")
644
+ when :time then cast_to_time(value)
645
+ else value
646
+ end
647
+ end
648
+ def cast_to_date_or_time(value)
649
+ return value if value.is_a? Date
650
+ return nil if value.blank?
651
+ guess_date_or_time (value.is_a? Time) ? value : cast_to_time(value)
652
+ end
653
+
654
+ def cast_to_time(value)
655
+ return value if value.is_a? Time
656
+ time_array = ParseDate.parsedate value
657
+ time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
658
+ Time.send(Base.default_timezone, *time_array) rescue nil
659
+ end
660
+
661
+ def guess_date_or_time(value)
662
+ (value.hour == 0 and value.min == 0 and value.sec == 0) ?
663
+ Date.new(value.year, value.month, value.day) : value
664
+ end
665
+ end
666
+
667
+ def modify_types(tp)
668
+ tp[:primary_key] = "int generated by default as identity NOT NULL PRIMARY KEY"
669
+ tp[:integer][:limit] = nil
670
+ tp
671
+ end
672
+
673
+ def add_limit_offset!(sql, options) # :nodoc:
674
+ @limit = options[:limit]
675
+ @offset = options[:offset]
676
+ end
677
+
678
+ def select_all(sql, name = nil)
679
+ @limit ||= -1
680
+ @offset ||= 0
681
+ select(sql, name)[@offset..(@offset+@limit)]
682
+ ensure
683
+ @limit = @offset = nil
684
+ end
685
+
686
+ def select_one(sql, name = nil)
687
+ @offset ||= 0
688
+ select(sql, name)[@offset]
689
+ ensure
690
+ @limit = @offset = nil
691
+ end
692
+
693
+ def execute(sql, name = nil)
694
+ log_no_bench(sql, name) do
695
+ if sql =~ /^select/i
696
+ @limit ||= -1
697
+ @offset ||= 0
698
+ @connection.execute_query(sql)[@offset..(@offset+@limit)]
699
+ else
700
+ @connection.execute_update(sql)
701
+ end
702
+ end
703
+ ensure
704
+ @limit = @offset = nil
705
+ end
706
+
707
+ def remove_index(table_name, options) #:nodoc:
708
+ execute "DROP INDEX #{index_name(table_name, options)}"
709
+ end
710
+
711
+ def rename_table(name, new_name)
712
+ execute "RENAME TABLE #{name} TO #{new_name}"
713
+ end
714
+
715
+ def quote(value, column = nil) # :nodoc:
716
+ if column && column.type == :primary_key
717
+ return value.to_s
718
+ end
719
+ case value
720
+ when String
721
+ if column && column.type == :binary
722
+ "CAST(x'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}' AS BLOB)"
723
+ else
724
+ vi = value.to_i
725
+ if vi.to_s == value
726
+ value
727
+ else
728
+ "'#{quote_string(value)}'"
729
+ end
730
+ end
731
+ else super
732
+ end
733
+ end
734
+
735
+ def quoted_true
736
+ '1'
737
+ end
738
+
739
+ def quoted_false
740
+ '0'
741
+ end
742
+ end
743
+
744
+ module FireBird
745
+ def modify_types(tp)
746
+ tp[:primary_key] = 'INTEGER NOT NULL PRIMARY KEY'
747
+ tp[:string][:limit] = 252
748
+ tp[:integer][:limit] = nil
749
+ tp
750
+ end
751
+
752
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) # :nodoc:
753
+ execute(sql, name)
754
+ id_value
755
+ end
756
+
757
+ def add_limit_offset!(sql, options) # :nodoc:
758
+ if options[:limit]
759
+ limit_string = "FIRST #{options[:limit]}"
760
+ limit_string << " SKIP #{options[:offset]}" if options[:offset]
761
+ sql.sub!(/\A(\s*SELECT\s)/i, '\&' + limit_string + ' ')
762
+ end
763
+ end
764
+
765
+ def prefetch_primary_key?(table_name = nil)
766
+ true
767
+ end
768
+
769
+ def default_sequence_name(table_name, primary_key) # :nodoc:
770
+ "#{table_name}_seq"
771
+ end
772
+
773
+ def next_sequence_value(sequence_name)
774
+ select_one("SELECT GEN_ID(#{sequence_name}, 1 ) FROM RDB$DATABASE;")["gen_id"]
775
+ end
776
+
777
+ def create_table(name, options = {}) #:nodoc:
778
+ super(name, options)
779
+ execute "CREATE GENERATOR #{name}_seq"
780
+ end
781
+
782
+ def rename_table(name, new_name) #:nodoc:
783
+ execute "RENAME #{name} TO #{new_name}"
784
+ execute "UPDATE RDB$GENERATORS SET RDB$GENERATOR_NAME='#{new_name}_seq' WHERE RDB$GENERATOR_NAME='#{name}_seq'" rescue nil
785
+ end
786
+
787
+ def drop_table(name) #:nodoc:
788
+ super(name)
789
+ execute "DROP GENERATOR #{name}_seq" rescue nil
790
+ end
791
+
792
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
793
+ execute "ALTER TABLE #{table_name} ALTER #{column_name} TYPE #{type_to_sql(type, options[:limit])}"
794
+ end
795
+
796
+ def rename_column(table_name, column_name, new_column_name)
797
+ execute "ALTER TABLE #{table_name} ALTER #{column_name} TO #{new_column_name}"
798
+ end
799
+
800
+ def remove_index(table_name, options) #:nodoc:
801
+ execute "DROP INDEX #{index_name(table_name, options)}"
802
+ end
803
+
804
+ def quote(value, column = nil) # :nodoc:
805
+ if [Time, DateTime].include?(value.class)
806
+ "CAST('#{value.strftime("%Y-%m-%d %H:%M:%S")}' AS TIMESTAMP)"
807
+ else
808
+ if column && column.type == :primary_key
809
+ return value.to_s
810
+ end
811
+ super
812
+ end
813
+ end
814
+
815
+ def quote_string(string) # :nodoc:
816
+ string.gsub(/'/, "''")
817
+ end
818
+
819
+ def quote_column_name(column_name) # :nodoc:
820
+ %Q("#{ar_to_fb_case(column_name)}")
821
+ end
822
+
823
+ def quoted_true # :nodoc:
824
+ quote(1)
825
+ end
826
+
827
+ def quoted_false # :nodoc:
828
+ quote(0)
829
+ end
830
+
831
+ private
832
+
833
+ # Maps uppercase Firebird column names to lowercase for ActiveRecord;
834
+ # mixed-case columns retain their original case.
835
+ def fb_to_ar_case(column_name)
836
+ column_name =~ /[[:lower:]]/ ? column_name : column_name.to_s.downcase
837
+ end
838
+
839
+ # Maps lowercase ActiveRecord column names to uppercase for Fierbird;
840
+ # mixed-case columns retain their original case.
841
+ def ar_to_fb_case(column_name)
842
+ column_name =~ /[[:upper:]]/ ? column_name : column_name.to_s.upcase
843
+ end
844
+ end
845
+
846
+ module DB2
847
+ module Column
848
+ def type_cast(value)
849
+ return nil if value.nil? || value =~ /^\s*null\s*$/i
850
+ case type
851
+ when :string then value
852
+ when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
853
+ when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
854
+ when :float then value.to_f
855
+ when :datetime then cast_to_date_or_time(value)
856
+ when :timestamp then cast_to_time(value)
857
+ when :time then cast_to_time(value)
858
+ else value
859
+ end
860
+ end
861
+ def cast_to_date_or_time(value)
862
+ return value if value.is_a? Date
863
+ return nil if value.blank?
864
+ guess_date_or_time (value.is_a? Time) ? value : cast_to_time(value)
865
+ end
866
+
867
+ def cast_to_time(value)
868
+ return value if value.is_a? Time
869
+ time_array = ParseDate.parsedate value
870
+ time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
871
+ Time.send(Base.default_timezone, *time_array) rescue nil
872
+ end
873
+
874
+ def guess_date_or_time(value)
875
+ (value.hour == 0 and value.min == 0 and value.sec == 0) ?
876
+ Date.new(value.year, value.month, value.day) : value
877
+ end
878
+ end
879
+
880
+ def modify_types(tp)
881
+ tp[:primary_key] = 'int generated by default as identity (start with 42) primary key'
882
+ tp[:string][:limit] = 255
883
+ tp[:integer][:limit] = nil
884
+ tp[:boolean][:limit] = nil
885
+ tp
886
+ end
887
+
888
+ def add_limit_offset!(sql, options)
889
+ if limit = options[:limit]
890
+ offset = options[:offset] || 0
891
+ sql.gsub!(/SELECT/i, 'SELECT B.* FROM (SELECT A.*, row_number() over () AS internal$rownum FROM (SELECT')
892
+ sql << ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rownum <= #{limit + offset}"
893
+ end
894
+ end
895
+
896
+ def quote_column_name(column_name)
897
+ column_name
898
+ end
899
+
900
+ def quote(value, column = nil) # :nodoc:
901
+ if column && column.type == :primary_key
902
+ return value.to_s
903
+ end
904
+ case value
905
+ when String
906
+ if column && column.type == :binary
907
+ "BLOB('#{quote_string(value)}')"
908
+ else
909
+ "'#{quote_string(value)}'"
910
+ end
911
+ else super
912
+ end
913
+ end
914
+
915
+ def quote_string(string)
916
+ string.gsub(/'/, "''") # ' (for ruby-mode)
917
+ end
918
+
919
+ def quoted_true
920
+ '1'
921
+ end
922
+
923
+ def quoted_false
924
+ '0'
925
+ end
926
+ end
927
+
928
+ module MsSQL
929
+ module Column
930
+ def simplified_type(field_type)
931
+ case field_type
932
+ when /int|bigint|smallint|tinyint/i then :integer
933
+ when /float|double|decimal|money|numeric|real|smallmoney/i then @scale == 0 ? :integer : :float
934
+ when /datetime|smalldatetime/i then :datetime
935
+ when /timestamp/i then :timestamp
936
+ when /time/i then :time
937
+ when /text|ntext/i then :text
938
+ when /binary|image|varbinary/i then :binary
939
+ when /char|nchar|nvarchar|string|varchar/i then :string
940
+ when /bit/i then :boolean
941
+ when /uniqueidentifier/i then :string
942
+ end
943
+ end
944
+
945
+ def type_cast(value)
946
+ return nil if value.nil? || value =~ /^\s*null\s*$/i
947
+ case type
948
+ when :string then value
949
+ when :integer then value == true || value == false ? value == true ? 1 : 0 : value.to_i
950
+ when :primary_key then value == true || value == false ? value == true ? 1 : 0 : value.to_i
951
+ when :float then value.to_f
952
+ when :datetime then cast_to_datetime(value)
953
+ when :timestamp then cast_to_time(value)
954
+ when :time then cast_to_time(value)
955
+ when :date then cast_to_datetime(value)
956
+ when :boolean then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
957
+ else value
958
+ end
959
+ end
960
+
961
+ def cast_to_time(value)
962
+ return value if value.is_a?(Time)
963
+ time_array = ParseDate.parsedate(value)
964
+ time_array[0] ||= 2000
965
+ time_array[1] ||= 1
966
+ time_array[2] ||= 1
967
+ Time.send(Base.default_timezone, *time_array) rescue nil
968
+ end
969
+
970
+ def cast_to_datetime(value)
971
+ if value.is_a?(Time)
972
+ if value.year != 0 and value.month != 0 and value.day != 0
973
+ return value
974
+ else
975
+ return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
976
+ end
977
+ end
978
+ return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
979
+ value
980
+ end
981
+
982
+ # These methods will only allow the adapter to insert binary data with a length of 7K or less
983
+ # because of a SQL Server statement length policy.
984
+ def self.string_to_binary(value)
985
+ value.gsub(/(\r|\n|\0|\x1a)/) do
986
+ case $1
987
+ when "\r" then "%00"
988
+ when "\n" then "%01"
989
+ when "\0" then "%02"
990
+ when "\x1a" then "%03"
991
+ end
992
+ end
993
+ end
994
+
995
+ def self.binary_to_string(value)
996
+ value.gsub(/(%00|%01|%02|%03)/) do
997
+ case $1
998
+ when "%00" then "\r"
999
+ when "%01" then "\n"
1000
+ when "%02\0" then "\0"
1001
+ when "%03" then "\x1a"
1002
+ end
1003
+ end
1004
+ end
1005
+ end
1006
+
1007
+ def modify_types(tp)
1008
+ tp[:primary_key] = "int NOT NULL IDENTITY(1, 1) PRIMARY KEY"
1009
+ tp[:integer][:limit] = nil
1010
+ tp[:boolean][:limit] = nil
1011
+ tp[:binary] = { :name => "image"}
1012
+ tp
1013
+ end
1014
+
1015
+ def quote(value, column = nil)
1016
+ if column && column.type == :primary_key
1017
+ return value.to_s
1018
+ end
1019
+ case value
1020
+ when String
1021
+ if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
1022
+ val = quote_string(column.class.string_to_binary(value))
1023
+ "'#{val}'"
1024
+ else
1025
+ "'#{quote_string(value)}'"
1026
+ end
1027
+ when NilClass then "NULL"
1028
+ when TrueClass then '1'
1029
+ when FalseClass then '0'
1030
+ when Float, Fixnum, Bignum then value.to_s
1031
+ when Date then "'#{value.to_s}'"
1032
+ when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
1033
+ else "'#{quote_string(value.to_yaml)}'"
1034
+ end
1035
+ end
1036
+
1037
+ def quote_string(string)
1038
+ string.gsub(/\'/, "''")
1039
+ end
1040
+
1041
+ def quoted_true
1042
+ "1"
1043
+ end
1044
+
1045
+ def quoted_false
1046
+ "0"
1047
+ end
1048
+
1049
+ def quote_column_name(name)
1050
+ "[#{name}]"
1051
+ end
1052
+
1053
+ def add_limit_offset!(sql, options)
1054
+ if options[:limit] and options[:offset]
1055
+ total_rows = @connection.select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT\b/i, "SELECT TOP 1000000000")}) tally")[0][:TotalRows].to_i
1056
+ if (options[:limit] + options[:offset]) >= total_rows
1057
+ options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
1058
+ end
1059
+ sql.sub!(/^\s*SELECT/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT TOP #{options[:limit] + options[:offset]} ")
1060
+ sql << ") AS tmp1"
1061
+ if options[:order]
1062
+ options[:order] = options[:order].split(',').map do |field|
1063
+ parts = field.split(" ")
1064
+ tc = parts[0]
1065
+ if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
1066
+ tc.gsub!(/\./, '\\.\\[')
1067
+ tc << '\\]'
1068
+ end
1069
+ if sql =~ /#{tc} AS (t\d_r\d\d?)/
1070
+ parts[0] = $1
1071
+ end
1072
+ parts.join(' ')
1073
+ end.join(', ')
1074
+ sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
1075
+ else
1076
+ sql << " ) AS tmp2"
1077
+ end
1078
+ elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
1079
+ sql.sub!(/^\s*SELECT([\s]*distinct)?/i) do
1080
+ "SELECT#{$1} TOP #{options[:limit]}"
1081
+ end unless options[:limit].nil?
1082
+ end
1083
+ end
1084
+
1085
+ def recreate_database(name)
1086
+ drop_database(name)
1087
+ create_database(name)
1088
+ end
1089
+
1090
+ def drop_database(name)
1091
+ execute "DROP DATABASE #{name}"
1092
+ end
1093
+
1094
+ def create_database(name)
1095
+ execute "CREATE DATABASE #{name}"
1096
+ end
1097
+
1098
+ def rename_table(name, new_name)
1099
+ execute "EXEC sp_rename '#{name}', '#{new_name}'"
1100
+ end
1101
+
1102
+ def rename_column(table, column, new_column_name)
1103
+ execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
1104
+ end
1105
+
1106
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
1107
+ sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit])}"]
1108
+ if options[:default]
1109
+ remove_default_constraint(table_name, column_name)
1110
+ sql_commands << "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{options[:default]} FOR #{column_name}"
1111
+ end
1112
+ sql_commands.each {|c|
1113
+ execute(c)
1114
+ }
1115
+ end
1116
+
1117
+ def remove_column(table_name, column_name)
1118
+ remove_default_constraint(table_name, column_name)
1119
+ execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
1120
+ end
1121
+
1122
+ def remove_default_constraint(table_name, column_name)
1123
+ defaults = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
1124
+ defaults.each {|constraint|
1125
+ execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
1126
+ }
1127
+ end
1128
+
1129
+ def remove_index(table_name, options = {})
1130
+ execute "DROP INDEX #{table_name}.#{index_name(table_name, options)}"
1131
+ end
1132
+ end
1133
+ end