ActiveRecord-JDBC 0.0.1 → 0.2.0

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.
@@ -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