activerecord-jdbc-adapter 0.6
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/History.txt +61 -0
- data/LICENSE +21 -0
- data/Manifest.txt +64 -0
- data/README.txt +116 -0
- data/Rakefile +146 -0
- data/lib/active_record/connection_adapters/derby_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +575 -0
- data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +10 -0
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +13 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +13 -0
- data/lib/jdbc_adapter.rb +32 -0
- data/lib/jdbc_adapter/jdbc_db2.rb +104 -0
- data/lib/jdbc_adapter/jdbc_derby.rb +362 -0
- data/lib/jdbc_adapter/jdbc_firebird.rb +109 -0
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +168 -0
- data/lib/jdbc_adapter/jdbc_mimer.rb +134 -0
- data/lib/jdbc_adapter/jdbc_mssql.rb +356 -0
- data/lib/jdbc_adapter/jdbc_mysql.rb +168 -0
- data/lib/jdbc_adapter/jdbc_oracle.rb +340 -0
- data/lib/jdbc_adapter/jdbc_postgre.rb +347 -0
- data/lib/jdbc_adapter/missing_functionality_helper.rb +72 -0
- data/lib/jdbc_adapter/version.rb +5 -0
- data/lib/jdbc_adapter_internal.jar +0 -0
- data/lib/tasks/jdbc_databases.rake +72 -0
- data/src/java/JDBCDerbySpec.java +323 -0
- data/src/java/JDBCMySQLSpec.java +89 -0
- data/src/java/JdbcAdapterInternalService.java +953 -0
- data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
- data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
- data/test/db/derby.rb +18 -0
- data/test/db/h2.rb +11 -0
- data/test/db/hsqldb.rb +15 -0
- data/test/db/jdbc.rb +11 -0
- data/test/db/jndi_config.rb +30 -0
- data/test/db/logger.rb +3 -0
- data/test/db/mysql.rb +9 -0
- data/test/db/postgres.rb +9 -0
- data/test/derby_multibyte_test.rb +12 -0
- data/test/derby_simple_test.rb +12 -0
- data/test/generic_jdbc_connection_test.rb +9 -0
- data/test/h2_simple_test.rb +7 -0
- data/test/hsqldb_simple_test.rb +6 -0
- data/test/jdbc_adapter/jdbc_db2_test.rb +21 -0
- data/test/jdbc_common.rb +6 -0
- data/test/jndi_test.rb +37 -0
- data/test/manualTestDatabase.rb +195 -0
- data/test/minirunit.rb +109 -0
- data/test/minirunit/testConnect.rb +14 -0
- data/test/minirunit/testH2.rb +73 -0
- data/test/minirunit/testHsqldb.rb +73 -0
- data/test/minirunit/testLoadActiveRecord.rb +3 -0
- data/test/minirunit/testMysql.rb +83 -0
- data/test/minirunit/testRawSelect.rb +24 -0
- data/test/models/auto_id.rb +18 -0
- data/test/models/data_types.rb +18 -0
- data/test/models/entry.rb +20 -0
- data/test/mysql_multibyte_test.rb +6 -0
- data/test/mysql_simple_test.rb +13 -0
- data/test/postgres_simple_test.rb +12 -0
- data/test/simple.rb +157 -0
- metadata +112 -0
@@ -0,0 +1,347 @@
|
|
1
|
+
module ::JdbcSpec
|
2
|
+
module ActiveRecordExtensions
|
3
|
+
def postgresql_connection(config)
|
4
|
+
config[:port] ||= 5432
|
5
|
+
config[:url] ||= "jdbc:postgresql://#{config[:host]}:#{config[:port]}/#{config[:database]}"
|
6
|
+
config[:driver] ||= "org.postgresql.Driver"
|
7
|
+
jdbc_connection(config)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module PostgreSQL
|
12
|
+
def self.column_selector
|
13
|
+
[/postgre/i, lambda {|cfg,col| col.extend(::JdbcSpec::PostgreSQL::Column)}]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.adapter_selector
|
17
|
+
[/postgre/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::PostgreSQL)}]
|
18
|
+
end
|
19
|
+
|
20
|
+
module Column
|
21
|
+
def type_cast(value)
|
22
|
+
case type
|
23
|
+
when :boolean then cast_to_boolean(value)
|
24
|
+
else super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def simplified_type(field_type)
|
29
|
+
return :integer if field_type =~ /^serial/i
|
30
|
+
return :string if field_type =~ /\[\]$/i || field_type =~ /^interval/i
|
31
|
+
return :string if field_type =~ /^(?:point|lseg|box|"?path"?|polygon|circle)/i
|
32
|
+
return :datetime if field_type =~ /^timestamp/i
|
33
|
+
return :float if field_type =~ /^real|^money/i
|
34
|
+
return :binary if field_type =~ /^bytea/i
|
35
|
+
return :boolean if field_type =~ /^bool/i
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
def cast_to_boolean(value)
|
40
|
+
if value == true || value == false
|
41
|
+
value
|
42
|
+
else
|
43
|
+
%w(true t 1).include?(value.to_s.downcase)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def cast_to_date_or_time(value)
|
48
|
+
return value if value.is_a? Date
|
49
|
+
return nil if value.blank?
|
50
|
+
guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
|
51
|
+
end
|
52
|
+
|
53
|
+
def cast_to_time(value)
|
54
|
+
return value if value.is_a? Time
|
55
|
+
time_array = ParseDate.parsedate value
|
56
|
+
time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
|
57
|
+
Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def guess_date_or_time(value)
|
61
|
+
(value.hour == 0 and value.min == 0 and value.sec == 0) ?
|
62
|
+
Date.new(value.year, value.month, value.day) : value
|
63
|
+
end
|
64
|
+
|
65
|
+
def default_value(value)
|
66
|
+
# Boolean types
|
67
|
+
return "t" if value =~ /true/i
|
68
|
+
return "f" if value =~ /false/i
|
69
|
+
|
70
|
+
# Char/String/Bytea type values
|
71
|
+
return $1 if value =~ /^'(.*)'::(bpchar|text|character varying|bytea)$/
|
72
|
+
|
73
|
+
# Numeric values
|
74
|
+
return value if value =~ /^-?[0-9]+(\.[0-9]*)?/
|
75
|
+
|
76
|
+
# Fixed dates / timestamp
|
77
|
+
return $1 if value =~ /^'(.+)'::(date|timestamp)/
|
78
|
+
|
79
|
+
# Anything else is blank, some user type, or some function
|
80
|
+
# and we can't know the value of that, so return nil.
|
81
|
+
return nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def modify_types(tp)
|
86
|
+
tp[:primary_key] = "serial primary key"
|
87
|
+
tp[:string][:limit] = 255
|
88
|
+
tp[:integer][:limit] = nil
|
89
|
+
tp[:boolean][:limit] = nil
|
90
|
+
tp
|
91
|
+
end
|
92
|
+
|
93
|
+
def default_sequence_name(table_name, pk = nil)
|
94
|
+
default_pk, default_seq = pk_and_sequence_for(table_name)
|
95
|
+
default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
|
96
|
+
end
|
97
|
+
|
98
|
+
# Resets sequence to the max value of the table's pk if present.
|
99
|
+
def reset_pk_sequence!(table, pk = nil, sequence = nil)
|
100
|
+
unless pk and sequence
|
101
|
+
default_pk, default_sequence = pk_and_sequence_for(table)
|
102
|
+
pk ||= default_pk
|
103
|
+
sequence ||= default_sequence
|
104
|
+
end
|
105
|
+
if pk
|
106
|
+
if sequence
|
107
|
+
select_value <<-end_sql, 'Reset sequence'
|
108
|
+
SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
|
109
|
+
end_sql
|
110
|
+
else
|
111
|
+
@logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Find a table's primary key and sequence.
|
117
|
+
def pk_and_sequence_for(table)
|
118
|
+
# First try looking for a sequence with a dependency on the
|
119
|
+
# given table's primary key.
|
120
|
+
result = select(<<-end_sql, 'PK and serial sequence')[0]
|
121
|
+
SELECT attr.attname AS nm, name.nspname AS nsp, seq.relname AS rel
|
122
|
+
FROM pg_class seq,
|
123
|
+
pg_attribute attr,
|
124
|
+
pg_depend dep,
|
125
|
+
pg_namespace name,
|
126
|
+
pg_constraint cons
|
127
|
+
WHERE seq.oid = dep.objid
|
128
|
+
AND seq.relnamespace = name.oid
|
129
|
+
AND seq.relkind = 'S'
|
130
|
+
AND attr.attrelid = dep.refobjid
|
131
|
+
AND attr.attnum = dep.refobjsubid
|
132
|
+
AND attr.attrelid = cons.conrelid
|
133
|
+
AND attr.attnum = cons.conkey[1]
|
134
|
+
AND cons.contype = 'p'
|
135
|
+
AND dep.refobjid = '#{table}'::regclass
|
136
|
+
end_sql
|
137
|
+
|
138
|
+
if result.nil? or result.empty?
|
139
|
+
# If that fails, try parsing the primary key's default value.
|
140
|
+
# Support the 7.x and 8.0 nextval('foo'::text) as well as
|
141
|
+
# the 8.1+ nextval('foo'::regclass).
|
142
|
+
# TODO: assumes sequence is in same schema as table.
|
143
|
+
result = select(<<-end_sql, 'PK and custom sequence')[0]
|
144
|
+
SELECT attr.attname AS nm, name.nspname AS nsp, split_part(def.adsrc, '\\\'', 2) AS rel
|
145
|
+
FROM pg_class t
|
146
|
+
JOIN pg_namespace name ON (t.relnamespace = name.oid)
|
147
|
+
JOIN pg_attribute attr ON (t.oid = attrelid)
|
148
|
+
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
149
|
+
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
150
|
+
WHERE t.oid = '#{table}'::regclass
|
151
|
+
AND cons.contype = 'p'
|
152
|
+
AND def.adsrc ~* 'nextval'
|
153
|
+
end_sql
|
154
|
+
end
|
155
|
+
# check for existence of . in sequence name as in public.foo_sequence. if it does not exist, join the current namespace
|
156
|
+
result['rel']['.'] ? [result['nm'], result['rel']] : [result['nm'], "#{result['nsp']}.#{result['rel']}"]
|
157
|
+
rescue
|
158
|
+
nil
|
159
|
+
end
|
160
|
+
|
161
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
162
|
+
execute(sql, name)
|
163
|
+
table = sql.split(" ", 4)[2]
|
164
|
+
id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
|
165
|
+
end
|
166
|
+
|
167
|
+
def columns(table_name, name=nil)
|
168
|
+
schema_name = "public"
|
169
|
+
if table_name =~ /\./
|
170
|
+
parts = table_name.split(/\./)
|
171
|
+
table_name = parts.pop
|
172
|
+
schema_name = parts.join(".")
|
173
|
+
end
|
174
|
+
@connection.columns_internal(table_name, name, schema_name)
|
175
|
+
end
|
176
|
+
|
177
|
+
def last_insert_id(table, sequence_name)
|
178
|
+
Integer(select_value("SELECT currval('#{sequence_name}')"))
|
179
|
+
end
|
180
|
+
|
181
|
+
# the point here is really just to empty the database, not recreate it
|
182
|
+
# so we delete all tables
|
183
|
+
def recreate_database(name)
|
184
|
+
tables.each{|t| drop_table(t)}
|
185
|
+
end
|
186
|
+
|
187
|
+
def structure_dump
|
188
|
+
abcs = ActiveRecord::Base.configurations
|
189
|
+
|
190
|
+
database = nil
|
191
|
+
if abcs[RAILS_ENV]["url"] =~ /\/([^\/]*)$/
|
192
|
+
database = $1
|
193
|
+
else
|
194
|
+
raise "Could not figure out what database this url is for #{abcs[RAILS_ENV]["url"]}"
|
195
|
+
end
|
196
|
+
|
197
|
+
ENV['PGHOST'] = abcs[RAILS_ENV]["host"] if abcs[RAILS_ENV]["host"]
|
198
|
+
ENV['PGPORT'] = abcs[RAILS_ENV]["port"].to_s if abcs[RAILS_ENV]["port"]
|
199
|
+
ENV['PGPASSWORD'] = abcs[RAILS_ENV]["password"].to_s if abcs[RAILS_ENV]["password"]
|
200
|
+
search_path = abcs[RAILS_ENV]["schema_search_path"]
|
201
|
+
search_path = "--schema=#{search_path}" if search_path
|
202
|
+
|
203
|
+
@connection.connection.close
|
204
|
+
begin
|
205
|
+
file = "db/#{RAILS_ENV}_structure.sql"
|
206
|
+
`pg_dump -i -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f #{file} #{search_path} #{database}`
|
207
|
+
raise "Error dumping database" if $?.exitstatus == 1
|
208
|
+
|
209
|
+
# need to patch away any references to SQL_ASCII as it breaks the JDBC driver
|
210
|
+
lines = File.readlines(file)
|
211
|
+
File.open(file, "w") do |io|
|
212
|
+
lines.each do |line|
|
213
|
+
line.gsub!(/SQL_ASCII/, 'UNICODE')
|
214
|
+
io.write(line)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
ensure
|
218
|
+
reconnect!
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def _execute(sql, name = nil)
|
223
|
+
case sql.strip
|
224
|
+
when /\A\(?\s*(select|show)/i:
|
225
|
+
@connection.execute_query(sql)
|
226
|
+
else
|
227
|
+
@connection.execute_update(sql)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
|
232
|
+
#
|
233
|
+
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
234
|
+
# requires that the ORDER BY include the distinct column.
|
235
|
+
#
|
236
|
+
# distinct("posts.id", "posts.created_at desc")
|
237
|
+
def distinct(columns, order_by)
|
238
|
+
return "DISTINCT #{columns}" if order_by.blank?
|
239
|
+
|
240
|
+
# construct a clean list of column names from the ORDER BY clause, removing
|
241
|
+
# any asc/desc modifiers
|
242
|
+
order_columns = order_by.split(',').collect { |s| s.split.first }
|
243
|
+
order_columns.delete_if(&:blank?)
|
244
|
+
order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
|
245
|
+
|
246
|
+
# return a DISTINCT ON() clause that's distinct on the columns we want but includes
|
247
|
+
# all the required columns for the ORDER BY to work properly
|
248
|
+
sql = "DISTINCT ON (#{columns}) #{columns}, "
|
249
|
+
sql << order_columns * ', '
|
250
|
+
end
|
251
|
+
|
252
|
+
# ORDER BY clause for the passed order option.
|
253
|
+
#
|
254
|
+
# PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
|
255
|
+
# by wrapping the sql as a sub-select and ordering in that query.
|
256
|
+
def add_order_by_for_association_limiting!(sql, options)
|
257
|
+
return sql if options[:order].blank?
|
258
|
+
|
259
|
+
order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
|
260
|
+
order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
|
261
|
+
order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')
|
262
|
+
|
263
|
+
sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
|
264
|
+
end
|
265
|
+
|
266
|
+
def quote(value, column = nil)
|
267
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
268
|
+
|
269
|
+
if value.kind_of?(String) && column && column.type == :binary
|
270
|
+
"'#{escape_bytea(value)}'"
|
271
|
+
elsif column && column.type == :primary_key
|
272
|
+
return value.to_s
|
273
|
+
else
|
274
|
+
super
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def escape_bytea(s)
|
279
|
+
if s
|
280
|
+
result = ''
|
281
|
+
s.each_byte { |c| result << sprintf('\\\\%03o', c) }
|
282
|
+
result
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def quote_column_name(name)
|
287
|
+
%("#{name}")
|
288
|
+
end
|
289
|
+
|
290
|
+
def quoted_date(value)
|
291
|
+
value.strftime("%Y-%m-%d %H:%M:%S.#{sprintf("%06d", value.usec)}")
|
292
|
+
end
|
293
|
+
|
294
|
+
def rename_table(name, new_name)
|
295
|
+
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
|
296
|
+
end
|
297
|
+
|
298
|
+
def add_column(table_name, column_name, type, options = {})
|
299
|
+
execute("ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}")
|
300
|
+
execute("ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL") if options[:null] == false
|
301
|
+
change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
|
302
|
+
end
|
303
|
+
|
304
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
305
|
+
begin
|
306
|
+
execute "ALTER TABLE #{table_name} ALTER #{column_name} TYPE #{type_to_sql(type, options[:limit])}"
|
307
|
+
rescue ActiveRecord::StatementInvalid
|
308
|
+
# This is PG7, so we use a more arcane way of doing it.
|
309
|
+
begin_db_transaction
|
310
|
+
add_column(table_name, "#{column_name}_ar_tmp", type, options)
|
311
|
+
execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit])})"
|
312
|
+
remove_column(table_name, column_name)
|
313
|
+
rename_column(table_name, "#{column_name}_ar_tmp", column_name)
|
314
|
+
commit_db_transaction
|
315
|
+
end
|
316
|
+
change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
|
317
|
+
end
|
318
|
+
|
319
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
320
|
+
execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT '#{default}'"
|
321
|
+
end
|
322
|
+
|
323
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
324
|
+
execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
|
325
|
+
end
|
326
|
+
|
327
|
+
def remove_index(table_name, options) #:nodoc:
|
328
|
+
execute "DROP INDEX #{index_name(table_name, options)}"
|
329
|
+
end
|
330
|
+
|
331
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
332
|
+
return super unless type.to_s == 'integer'
|
333
|
+
|
334
|
+
if limit.nil? || limit == 4
|
335
|
+
'integer'
|
336
|
+
elsif limit < 4
|
337
|
+
'smallint'
|
338
|
+
else
|
339
|
+
'bigint'
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def tables
|
344
|
+
@connection.tables(database_name, nil, nil, ["TABLE"])
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module JdbcSpec
|
2
|
+
module MissingFunctionalityHelper
|
3
|
+
#Taken from SQLite adapter
|
4
|
+
|
5
|
+
def alter_table(table_name, options = {}) #:nodoc:
|
6
|
+
table_name = table_name.to_s.downcase
|
7
|
+
altered_table_name = "altered_#{table_name}"
|
8
|
+
caller = lambda {|definition| yield definition if block_given?}
|
9
|
+
|
10
|
+
transaction do
|
11
|
+
move_table(table_name, altered_table_name, options)
|
12
|
+
move_table(altered_table_name, table_name, &caller)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def move_table(from, to, options = {}, &block) #:nodoc:
|
17
|
+
copy_table(from, to, options, &block)
|
18
|
+
drop_table(from)
|
19
|
+
end
|
20
|
+
|
21
|
+
def copy_table(from, to, options = {}) #:nodoc:
|
22
|
+
create_table(to, options) do |@definition|
|
23
|
+
columns(from).each do |column|
|
24
|
+
column_name = options[:rename] ?
|
25
|
+
(options[:rename][column.name] ||
|
26
|
+
options[:rename][column.name.to_sym] ||
|
27
|
+
column.name) : column.name
|
28
|
+
column_name = column_name.to_s
|
29
|
+
@definition.column(column_name, column.type,
|
30
|
+
:limit => column.limit, :default => column.default,
|
31
|
+
:null => column.null)
|
32
|
+
end
|
33
|
+
@definition.primary_key(primary_key(from))
|
34
|
+
yield @definition if block_given?
|
35
|
+
end
|
36
|
+
|
37
|
+
copy_table_indexes(from, to)
|
38
|
+
copy_table_contents(from, to,
|
39
|
+
@definition.columns,
|
40
|
+
options[:rename] || {})
|
41
|
+
end
|
42
|
+
|
43
|
+
def copy_table_indexes(from, to) #:nodoc:
|
44
|
+
indexes(from).each do |index|
|
45
|
+
name = index.name.downcase
|
46
|
+
if to == "altered_#{from}"
|
47
|
+
name = "temp_#{name}"
|
48
|
+
elsif from == "altered_#{to}"
|
49
|
+
name = name[5..-1]
|
50
|
+
end
|
51
|
+
|
52
|
+
# index name can't be the same
|
53
|
+
opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
|
54
|
+
opts[:unique] = true if index.unique
|
55
|
+
add_index(to, index.columns, opts)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
|
60
|
+
column_mappings = Hash[*columns.map {|col| [col.name, col.name]}.flatten]
|
61
|
+
rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
|
62
|
+
from_columns = columns(from).collect {|col| col.name}
|
63
|
+
columns = columns.find_all{|col| from_columns.include?(column_mappings[col.name])}
|
64
|
+
execute("SELECT * FROM #{from}").each do |row|
|
65
|
+
sql = "INSERT INTO #{to} ("+columns.map(&:name)*','+") VALUES ("
|
66
|
+
sql << columns.map {|col| quote(row[column_mappings[col.name]],col)} * ', '
|
67
|
+
sql << ')'
|
68
|
+
execute sql
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
Binary file
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
# Task redefine code from public domain
|
3
|
+
module Rake
|
4
|
+
module TaskManager
|
5
|
+
def redefine_task(task_class, args, &block)
|
6
|
+
task_name, deps = resolve_args(args)
|
7
|
+
task_name = task_class.scope_name(@scope, task_name)
|
8
|
+
deps = [deps] unless deps.respond_to?(:to_ary)
|
9
|
+
deps = deps.collect {|d| d.to_s }
|
10
|
+
task = @tasks[task_name.to_s] = task_class.new(task_name, self)
|
11
|
+
task.application = self
|
12
|
+
task.add_comment(@last_comment)
|
13
|
+
@last_comment = nil
|
14
|
+
task.enhance(deps, &block)
|
15
|
+
task
|
16
|
+
end
|
17
|
+
end
|
18
|
+
class Task
|
19
|
+
class << self
|
20
|
+
def redefine_task(args, &block)
|
21
|
+
Rake.application.redefine_task(self, args, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def redefine_task(args, &block)
|
28
|
+
Rake::Task.redefine_task(args, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
if RUBY_PLATFORM =~ /java/
|
32
|
+
namespace :db do
|
33
|
+
redefine_task :drop => :environment do
|
34
|
+
begin
|
35
|
+
config = ActiveRecord::Base.configurations[environment_name]
|
36
|
+
ActiveRecord::Base.establish_connection(config)
|
37
|
+
db = ActiveRecord::Base.connection.database_name
|
38
|
+
ActiveRecord::Base.connection.recreate_database(db)
|
39
|
+
rescue
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
namespace :structure do
|
44
|
+
redefine_task :dump => :environment do
|
45
|
+
abcs = ActiveRecord::Base.configurations
|
46
|
+
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
|
47
|
+
File.open("db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
|
48
|
+
if ActiveRecord::Base.connection.supports_migrations?
|
49
|
+
File.open("db/#{RAILS_ENV}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
namespace :test do
|
54
|
+
redefine_task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
|
55
|
+
abcs = ActiveRecord::Base.configurations
|
56
|
+
ActiveRecord::Base.establish_connection(:test)
|
57
|
+
ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') if abcs["test"]["adapter"] =~ /mysql/i
|
58
|
+
IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl|
|
59
|
+
ActiveRecord::Base.connection.execute(ddl)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
redefine_task :purge => :environment do
|
64
|
+
abcs = ActiveRecord::Base.configurations
|
65
|
+
ActiveRecord::Base.establish_connection(:test)
|
66
|
+
db = ActiveRecord::Base.connection.database_name
|
67
|
+
ActiveRecord::Base.connection.recreate_database(db)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|