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.
- data/LICENSE +1 -0
- data/install.rb +25 -25
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +151 -57
- data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +1133 -0
- data/lib/jdbc_adapter.rb +1 -0
- data/test/manualTestDatabase.rb +195 -0
- metadata +10 -6
@@ -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
|