ActiveRecord-JDBC 0.3.1 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.4
2
+
3
+ - Release coincides with JRuby 1.0 release
4
+ - Shoring up PostgreSQL (courtesy Dudley Flanders) and HSQL (courtesy Matthew Williams)
5
+ - Fix timestamps on Oracle to use DATE (as everything else)
6
+ - Derby fixes: Fix for open result set issue, better structure dump, quoting, column type changing
7
+ - Sybase type recognition fix (courtesy Dean Mao)
8
+
1
9
  == 0.3.1
2
10
 
3
11
  * Derby critical fixes shortly after 0.3
data/Manifest.txt CHANGED
@@ -26,6 +26,7 @@ test/db/jndi_config.rb
26
26
  test/db/logger.rb
27
27
  test/db/mysql.rb
28
28
  test/db/postgres.rb
29
+ test/derby_multibyte_test.rb
29
30
  test/derby_simple_test.rb
30
31
  test/h2_simple_test.rb
31
32
  test/hsqldb_simple_test.rb
@@ -45,3 +46,4 @@ test/models/entry.rb
45
46
  test/mysql_simple_test.rb
46
47
  test/postgres_simple_test.rb
47
48
  test/simple.rb
49
+ lib/tasks/jdbc_databases.rake
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rake'
2
2
  require 'rake/testtask'
3
3
 
4
- task :default => :test
4
+ task :default => [:java_compile, :test]
5
5
 
6
6
  def java_classpath_arg # myriad of ways to discover JRuby classpath
7
7
  begin
@@ -40,13 +40,13 @@ end
40
40
  desc "Run AR-JDBC tests"
41
41
  if RUBY_PLATFORM =~ /java/
42
42
  # TODO: add more databases into the standard tests here.
43
- task :test => [:java_compile, :test_mysql, :test_derby]
43
+ task :test => [:test_mysql, :test_derby, :test_hsqldb]
44
44
  else
45
45
  task :test => [:test_mysql]
46
46
  end
47
47
 
48
48
  Rake::TestTask.new(:test_mysql) do |t|
49
- t.test_files = FileList['test/mysql_simple_test.rb']
49
+ t.test_files = FileList['test/mysql_*_test.rb']
50
50
  t.libs << 'test'
51
51
  end
52
52
 
@@ -56,13 +56,13 @@ Rake::TestTask.new(:test_hsqldb) do |t|
56
56
  end
57
57
 
58
58
  Rake::TestTask.new(:test_derby) do |t|
59
- t.test_files = FileList['test/derby_simple_test.rb',
59
+ t.test_files = FileList['test/derby_*_test.rb',
60
60
  'test/activerecord/connection_adapters/type_conversion_test.rb']
61
61
  t.libs << 'test'
62
62
  end
63
63
 
64
64
  Rake::TestTask.new(:test_postgresql) do |t|
65
- t.test_files = FileList['test/postgres_simple_test.rb']
65
+ t.test_files = FileList['test/postgres_*_test.rb']
66
66
  t.libs << 'test'
67
67
  end
68
68
 
@@ -75,7 +75,7 @@ end
75
75
 
76
76
  begin
77
77
  MANIFEST = FileList["History.txt", "Manifest.txt", "README.txt",
78
- "Rakefile", "LICENSE", "lib/**/*.rb", "lib/jdbc_adapter_internal.jar", "test/**/*.rb"]
78
+ "Rakefile", "LICENSE", "lib/**/*.rb", "lib/jdbc_adapter_internal.jar", "test/**/*.rb", "lib/**/*.rake"]
79
79
 
80
80
  file "Manifest.txt" => :manifest
81
81
  task :manifest do
@@ -84,16 +84,15 @@ begin
84
84
  Rake::Task['manifest'].invoke # Always regen manifest, so Hoe has up-to-date list of files
85
85
 
86
86
  require 'hoe'
87
- Hoe.new("ActiveRecord-JDBC", "0.3.1") do |p|
87
+ Hoe.new("ActiveRecord-JDBC", "0.4") do |p|
88
88
  p.rubyforge_name = "jruby-extras"
89
89
  p.url = "http://jruby-extras.rubyforge.org/ActiveRecord-JDBC"
90
90
  p.author = "Nick Sieger, Ola Bini and JRuby contributors"
91
- p.email = "nick@nicksieger.com, ola.bini@ki.se"
91
+ p.email = "nick@nicksieger.com, ola.bini@gmail.com"
92
92
  p.summary = "JDBC adapter for ActiveRecord, for use within JRuby on Rails."
93
93
  p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
94
94
  p.description = p.paragraphs_of('README.txt', 0...1).join("\n\n")
95
- p.extra_deps.reject!{|d| d.first == "hoe"}
96
- end
95
+ end.spec.dependencies.delete_if { |dep| dep.name == "hoe" }
97
96
  rescue LoadError
98
97
  puts "You really need Hoe installed to be able to package this gem"
99
98
  end
@@ -94,14 +94,16 @@ module ActiveRecord
94
94
  lambda {|r| r['type_name'] =~ /^real$/i},
95
95
  lambda {|r| r['precision'] == '15'}],
96
96
  :datetime => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
97
- lambda {|r| r['type_name'] =~ /^datetime/i},
98
- lambda {|r| r['type_name'] =~ /^timestamp$/i}],
97
+ lambda {|r| r['type_name'] =~ /^datetime$/i},
98
+ lambda {|r| r['type_name'] =~ /^timestamp$/i},
99
+ lambda {|r| r['type_name'] =~ /^date/i}],
99
100
  :timestamp => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
100
101
  lambda {|r| r['type_name'] =~ /^timestamp$/i},
101
- lambda {|r| r['type_name'] =~ /^datetime/i} ],
102
+ lambda {|r| r['type_name'] =~ /^datetime/i},
103
+ lambda {|r| r['type_name'] =~ /^date/i}],
102
104
  :time => [ lambda {|r| Jdbc::Types::TIME == r['data_type'].to_i},
103
105
  lambda {|r| r['type_name'] =~ /^time$/i},
104
- lambda {|r| r['type_name'] =~ /^datetime$/i}],
106
+ lambda {|r| r['type_name'] =~ /^date/i}],
105
107
  :date => [ lambda {|r| Jdbc::Types::DATE == r['data_type'].to_i},
106
108
  lambda {|r| r['type_name'] =~ /^date$/i}],
107
109
  :binary => [ lambda {|r| [Jdbc::Types::LONGVARBINARY,Jdbc::Types::BINARY,Jdbc::Types::BLOB].include?(r['data_type'].to_i)},
@@ -118,6 +120,7 @@ module ActiveRecord
118
120
 
119
121
  def initialize(types)
120
122
  @types = types
123
+ @types.each {|t| t['type_name'] ||= t['local_type_name']} # Sybase driver seems to want 'local_type_name'
121
124
  end
122
125
 
123
126
  def choose_best_types
@@ -139,7 +142,7 @@ module ActiveRecord
139
142
  return new_types.first if new_types.length == 1
140
143
  types = new_types if new_types.length > 0
141
144
  end
142
- raise "unable to choose type from: #{types.collect{|t| [t['type_name'],t]}.inspect} for #{ar_type}"
145
+ raise "unable to choose type for #{ar_type} from:\n#{types.collect{|t| t['type_name']}.inspect}"
143
146
  end
144
147
  end
145
148
 
@@ -193,7 +196,7 @@ module ActiveRecord
193
196
  end
194
197
 
195
198
  class JdbcConnection
196
- attr_reader :adapter
199
+ attr_reader :adapter, :connection
197
200
 
198
201
  def initialize(config)
199
202
  @config = config.symbolize_keys!
@@ -410,10 +413,10 @@ module ActiveRecord
410
413
  def _execute(sql, name = nil)
411
414
  log_no_bench(sql, name) do
412
415
  case sql.strip
413
- when /^(select|show)/i:
414
- @connection.execute_query(sql)
415
416
  when /^insert/i:
416
- @connection.execute_insert(sql)
417
+ @connection.execute_insert(sql)
418
+ when /^\(?\s*(select|show)/i:
419
+ @connection.execute_query(sql)
417
420
  else
418
421
  @connection.execute_update(sql)
419
422
  end
@@ -437,6 +440,10 @@ module ActiveRecord
437
440
  @connection.tables
438
441
  end
439
442
 
443
+ def indexes(table_name)
444
+ @connection.indexes(table_name)
445
+ end
446
+
440
447
  def begin_db_transaction
441
448
  @connection.begin
442
449
  end
@@ -2,21 +2,55 @@ require 'jdbc_adapter/missing_functionality_helper'
2
2
 
3
3
  module JdbcSpec
4
4
  module Derby
5
+ def self.monkey_rails
6
+ unless @already_monkeyd
7
+ # Needed because Rails is broken wrt to quoting of
8
+ # some values. Most databases are nice about it,
9
+ # but not Derby. The real issue is that you can't
10
+ # compare a CHAR value to a NUMBER column.
11
+ ::ActiveRecord::Associations::ClassMethods.module_eval do
12
+ private
13
+
14
+ def select_limited_ids_list(options, join_dependency)
15
+ connection.select_all(
16
+ construct_finder_sql_for_association_limiting(options, join_dependency),
17
+ "#{name} Load IDs For Limited Eager Loading"
18
+ ).collect { |row| connection.quote(row[primary_key], columns_hash[primary_key]) }.join(", ")
19
+ end
20
+ end
21
+
22
+ @already_monkeyd = true
23
+ end
24
+ end
25
+
26
+ def self.extended(*args)
27
+ monkey_rails
28
+ end
29
+
30
+ def self.included(*args)
31
+ monkey_rails
32
+ end
33
+
5
34
  module Column
6
35
  def type_cast(value)
7
36
  return nil if value.nil? || value =~ /^\s*null\s*$/i
8
37
  case type
9
38
  when :string then value
39
+ when :text then value
10
40
  when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
11
41
  when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
42
+ when :decimal then self.class.value_to_decimal(value)
12
43
  when :float then value.to_f
13
44
  when :datetime then cast_to_date_or_time(value)
45
+ when :date then self.class.string_to_date(value)
14
46
  when :timestamp then cast_to_time(value)
15
47
  when :binary then value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*")
16
48
  when :time then cast_to_time(value)
49
+ when :boolean then self.class.value_to_boolean(value)
17
50
  else value
18
51
  end
19
52
  end
53
+
20
54
  def cast_to_date_or_time(value)
21
55
  return value if value.is_a? Date
22
56
  return nil if value.blank?
@@ -37,6 +71,7 @@ module JdbcSpec
37
71
 
38
72
  def simplified_type(field_type)
39
73
  return :boolean if field_type =~ /smallint/i
74
+ return :float if field_type =~ /real/i
40
75
  super
41
76
  end
42
77
  end
@@ -46,6 +81,7 @@ module JdbcSpec
46
81
  def modify_types(tp)
47
82
  tp[:primary_key] = "int generated by default as identity NOT NULL PRIMARY KEY"
48
83
  tp[:integer][:limit] = nil
84
+ tp[:string][:limit] = 256
49
85
  tp[:boolean] = {:name => "smallint"}
50
86
  tp
51
87
  end
@@ -72,8 +108,8 @@ module JdbcSpec
72
108
 
73
109
  # Set the sequence to the max value of the table's column.
74
110
  def reset_sequence!(table, column, sequence = nil)
75
- mpk = select_value("SELECT MAX(#{column}) FROM #{table}")
76
- execute("ALTER TABLE #{table} ALTER COLUMN #{column} RESTART WITH #{mpk.to_i + 1}")
111
+ mpk = select_value("SELECT MAX(#{quote_column_name column}) FROM #{table}")
112
+ execute("ALTER TABLE #{table} ALTER COLUMN #{quote_column_name column} RESTART WITH #{mpk.to_i + 1}")
77
113
  end
78
114
 
79
115
  def reset_pk_sequence!(table, pk = nil, sequence = nil)
@@ -88,7 +124,9 @@ module JdbcSpec
88
124
  def _execute(sql, name = nil)
89
125
  log_no_bench(sql, name) do
90
126
  case sql.strip
91
- when /^(select|show)/i:
127
+ when /^insert/i:
128
+ @connection.execute_insert(sql)
129
+ when /^\(?\s*(select|show)/i:
92
130
  @offset ||= 0
93
131
  if !@limit || @limit == -1
94
132
  range = @offset..-1
@@ -98,8 +136,6 @@ module JdbcSpec
98
136
  max = @offset+@limit+1
99
137
  end
100
138
  @connection.execute_query(sql,max)[range] || []
101
- when /^insert/i:
102
- @connection.execute_insert(sql)
103
139
  else
104
140
  @connection.execute_update(sql)
105
141
  end
@@ -125,7 +161,7 @@ module JdbcSpec
125
161
  COLUMN_TYPE_STMT = "SELECT COLUMNDATATYPE, COLUMNDEFAULT FROM SYS.SYSCOLUMNS WHERE REFERENCEID = '%s' AND COLUMNNAME = '%s'"
126
162
 
127
163
  AUTO_INC_STMT = "SELECT AUTOINCREMENTSTART, AUTOINCREMENTINC, COLUMNNAME, REFERENCEID, COLUMNDEFAULT FROM SYS.SYSCOLUMNS WHERE REFERENCEID = '%s' AND COLUMNNAME = '%s'"
128
-
164
+ AUTO_INC_STMT2 = "SELECT AUTOINCREMENTSTART, AUTOINCREMENTINC, COLUMNNAME, REFERENCEID, COLUMNDEFAULT FROM SYS.SYSCOLUMNS WHERE REFERENCEID = (SELECT T.TABLEID FROM SYS.SYSTABLES T WHERE T.TABLENAME = '%s') AND COLUMNNAME = '%s'"
129
165
 
130
166
  def add_quotes(name)
131
167
  return name unless name
@@ -160,6 +196,43 @@ module JdbcSpec
160
196
  end
161
197
  false
162
198
  end
199
+
200
+ def reinstate_auto_increment(name, refid, coldef)
201
+ stmt = AUTO_INC_STMT % [refid, strip_quotes(name)]
202
+ data = execute(stmt).first
203
+ if data
204
+ start = data['autoincrementstart']
205
+ if start
206
+ coldef << " GENERATED " << (data['columndefault'].nil? ? "ALWAYS" : "BY DEFAULT ")
207
+ coldef << "AS IDENTITY (START WITH "
208
+ coldef << start
209
+ coldef << ", INCREMENT BY "
210
+ coldef << data['autoincrementinc']
211
+ coldef << ")"
212
+ return true
213
+ end
214
+ end
215
+ false
216
+ end
217
+
218
+ def auto_increment_stmt(tname, cname)
219
+ stmt = AUTO_INC_STMT2 % [tname, strip_quotes(cname)]
220
+ data = execute(stmt).first
221
+ if data
222
+ start = data['autoincrementstart']
223
+ if start
224
+ coldef = ""
225
+ coldef << " GENERATED " << (data['columndefault'].nil? ? "ALWAYS" : "BY DEFAULT ")
226
+ coldef << "AS IDENTITY (START WITH "
227
+ coldef << start
228
+ coldef << ", INCREMENT BY "
229
+ coldef << data['autoincrementinc']
230
+ coldef << ")"
231
+ return coldef
232
+ end
233
+ end
234
+ ""
235
+ end
163
236
 
164
237
  def create_column(name, refid, colno)
165
238
  stmt = COLUMN_TYPE_STMT % [refid, strip_quotes(name)]
@@ -176,36 +249,57 @@ module JdbcSpec
176
249
  coldef
177
250
  end
178
251
 
252
+ SIZEABLE = %w(VARCHAR CLOB BLOB)
253
+
179
254
  def structure_dump #:nodoc:
180
- data = ""
181
- execute("select tablename, tableid from sys.systables where schemaid not in (select schemaid from sys.sysschemas where schemaname LIKE 'SYS%')").each do |tbl|
182
- tid = tbl["tableid"]
183
- tname = tbl["tablename"]
184
- data << "CREATE TABLE #{tname} (\n"
255
+ definition=""
256
+ rs = @connection.connection.meta_data.getTables(nil,nil,nil,["TABLE"].to_java(:string))
257
+ while rs.next
258
+ tname = rs.getString(3)
259
+ definition << "CREATE TABLE #{tname} (\n"
260
+ rs2 = @connection.connection.meta_data.getColumns(nil,nil,tname,nil)
185
261
  first_col = true
186
- execute(COLUMN_INFO_STMT % tid).each do |col|
187
- col_name = add_quotes(col['columnname']);
188
- create_col_string = create_column(col_name, col['referenceid'],col['columnnumber'].to_i)
262
+ while rs2.next
263
+ col_name = add_quotes(rs2.getString(4));
264
+ default = ""
265
+ d1 = rs2.getString(13)
266
+ if d1 =~ /^GENERATED_/
267
+ default = auto_increment_stmt(tname, col_name)
268
+ elsif d1
269
+ default = " DEFAULT #{d1}"
270
+ end
271
+
272
+ type = rs2.getString(6)
273
+ col_size = rs2.getString(7)
274
+ nulling = (rs2.getString(18) == 'NO' ? " NOT NULL" : "")
275
+ create_col_string = add_quotes(expand_double_quotes(strip_quotes(col_name))) +
276
+ " " +
277
+ type +
278
+ (SIZEABLE.include?(type) ? "(#{col_size})" : "") +
279
+ nulling +
280
+ default
189
281
  if !first_col
190
282
  create_col_string = ",\n #{create_col_string}"
191
283
  else
192
284
  create_col_string = " #{create_col_string}"
193
285
  end
194
-
195
- data << create_col_string
196
-
286
+
287
+ definition << create_col_string
288
+
197
289
  first_col = false
198
290
  end
199
- data << ");\n\n"
291
+ definition << ");\n\n"
200
292
  end
201
- data
293
+ definition
202
294
  end
203
295
 
204
296
  # Support for removing columns added via derby bug issue:
205
297
  # https://issues.apache.org/jira/browse/DERBY-1489
206
298
  #
207
- # This feature has not made it into a formal release and is not in Java 6. We will
208
- # need to conditionally support this somehow (supposed to arrive for 10.3.0.0)
299
+ # This feature has not made it into a formal release and is not in Java 6.
300
+ # If the normal strategy fails we fall back on a strategy by creating a new
301
+ # table without the new column and there after moving the data to the new
302
+ #
209
303
  def remove_column(table_name, column_name)
210
304
  begin
211
305
  execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name} RESTRICT"
@@ -213,39 +307,46 @@ module JdbcSpec
213
307
  alter_table(table_name) do |definition|
214
308
  definition.columns.delete(definition[column_name])
215
309
  end
216
- # raise NotImplementedError, "remove_column is not support on this Derby version"
217
310
  end
218
311
  end
219
312
 
220
313
  # Notes about changing in Derby:
221
314
  # http://db.apache.org/derby/docs/10.2/ref/rrefsqlj81859.html#rrefsqlj81859__rrefsqlj37860)
222
- # Derby cannot: Change the column type or decrease the precision of an existing type, but
223
- # can increase the types precision only if it is a VARCHAR.
224
315
  #
225
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
226
- # Derby can't change the datatype or size unless the type is varchar
227
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DATA TYPE #{type_to_sql(type, options[:limit])}" if type == :string
228
- if options.include? :null
316
+ # We support changing columns using the strategy outlined in:
317
+ # https://issues.apache.org/jira/browse/DERBY-1515
318
+ #
319
+ # This feature has not made it into a formal release and is not in Java 6. We will
320
+ # need to conditionally support this somehow (supposed to arrive for 10.3.0.0)
321
+ def change_column(table_name, column_name, type, options = {})
322
+ # null/not nulling is easy, handle that separately
323
+ if options.include?(:null)
229
324
  # This seems to only work with 10.2 of Derby
230
- if options[:null] == false
325
+ if options.delete(:null) == false
231
326
  execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} NOT NULL"
232
327
  else
233
328
  execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} NULL"
234
329
  end
235
330
  end
236
- end
237
331
 
238
- # There seems to be more than one thing wrong with
239
- # changing defaults for VARCHAR columns right now... DERBY-2371
240
- # among others
241
- def change_column_default(table_name, column_name, default) #:nodoc:
242
- begin
243
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} DEFAULT #{quote(default)}"
244
- rescue
245
- alter_table(table_name) do |definition|
246
- definition[column_name].default = default
332
+ # anything left to do?
333
+ unless options.empty?
334
+ begin
335
+ execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DATA TYPE #{type_to_sql(type, options[:limit])}"
336
+ rescue
337
+ transaction do
338
+ temp_new_column_name = "#{column_name}_newtype"
339
+ # 1) ALTER TABLE t ADD COLUMN c1_newtype NEWTYPE;
340
+ add_column table_name, temp_new_column_name, type, options
341
+ # 2) UPDATE t SET c1_newtype = c1;
342
+ execute "UPDATE #{table_name} SET #{temp_new_column_name} = CAST(#{column_name} AS #{type_to_sql(type, options[:limit])})"
343
+ # 3) ALTER TABLE t DROP COLUMN c1;
344
+ remove_column table_name, column_name
345
+ # 4) ALTER TABLE t RENAME COLUMN c1_newtype to c1;
346
+ rename_column table_name, temp_new_column_name, column_name
347
+ end
247
348
  end
248
- end
349
+ end
249
350
  end
250
351
 
251
352
  # Support for renaming columns:
@@ -268,15 +369,8 @@ module JdbcSpec
268
369
 
269
370
  def recreate_database(db_name)
270
371
  tables.each do |t|
271
- drop_table t rescue nil
372
+ drop_table t
272
373
  end
273
- end
274
-
275
- # For migrations, exclude the primary key index as recommended
276
- # by the HSQLDB docs. This is not a great test for primary key
277
- # index.
278
- def indexes(table_name)
279
- @connection.indexes(table_name)
280
374
  end
281
375
 
282
376
  def quote(value, column = nil) # :nodoc:
@@ -288,6 +382,8 @@ module JdbcSpec
288
382
  case column.type
289
383
  when :binary
290
384
  "CAST(x'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}' AS BLOB)"
385
+ when :text
386
+ "CAST('#{quote_string(value)}' AS CLOB)"
291
387
  when :string
292
388
  "'#{quote_string(value)}'"
293
389
  else
@@ -295,12 +391,23 @@ module JdbcSpec
295
391
  if vi.to_s == value
296
392
  value
297
393
  else
298
- "'#{quote_string(value)}'"
394
+ super
299
395
  end
300
396
  end
301
397
  else
302
398
  super
303
399
  end
400
+ when Float, Fixnum, Bignum
401
+ if column
402
+ case column.type
403
+ when :string
404
+ "'#{quote_string(value.to_s)}'"
405
+ else
406
+ super
407
+ end
408
+ else
409
+ super
410
+ end
304
411
  else super
305
412
  end
306
413
  end
@@ -313,6 +420,10 @@ module JdbcSpec
313
420
  def quote_column_name(name) #:nodoc:
314
421
  if /^references$/i =~ name
315
422
  %Q{"#{name.upcase}"}
423
+ elsif /[A-Z]/ =~ name && /[a-z]/ =~ name
424
+ %Q{"#{name}"}
425
+ elsif name =~ /\s/
426
+ %Q{"#{name.upcase}"}
316
427
  else
317
428
  name
318
429
  end
@@ -327,3 +438,4 @@ module JdbcSpec
327
438
  end
328
439
  end
329
440
  end
441
+
@@ -110,7 +110,9 @@ module JdbcSpec
110
110
  end
111
111
 
112
112
  def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
113
- execute(sql, name)
113
+ log_no_bench(sql,name) do
114
+ @connection.execute_update(sql)
115
+ end
114
116
  table = sql.split(" ", 4)[2]
115
117
  id_value || last_insert_id(table, nil)
116
118
  end
@@ -136,17 +138,7 @@ module JdbcSpec
136
138
  # system tables SYSTEM.*, but H2 seems to name them without
137
139
  # any kind of convention
138
140
  def tables
139
- @connection.tables do |result_row|
140
- result_row.get_string(ActiveRecord::ConnectionAdapters::Jdbc::TableMetaData::TABLE_TYPE) !~ /^SYSTEM TABLE$/i
141
- end
141
+ @connection.tables.select {|row| row.to_s !~ /^system_/i }
142
142
  end
143
-
144
- # For migrations, exclude the primary key index as recommended
145
- # by the HSQLDB docs. This is not a great test for primary key
146
- # index.
147
- def indexes(table_name, name = nil)
148
- @connection.indexes(table_name.to_s)
149
- end
150
-
151
143
  end
152
144
  end
@@ -58,11 +58,6 @@ module JdbcSpec
58
58
  def quote_column_name(name) #:nodoc:
59
59
  "`#{name}`"
60
60
  end
61
-
62
- # from active_record/vendor/mysql.rb
63
- def quote_string(str) #:nodoc:
64
- @connection.mysql_quote_string(str)
65
- end
66
61
 
67
62
  def quoted_true
68
63
  "1"
@@ -104,10 +99,6 @@ module JdbcSpec
104
99
  select_one("SELECT DATABASE() as db")["db"]
105
100
  end
106
101
 
107
- def indexes(table_name, name = nil)#:nodoc:
108
- @connection.indexes(table_name)
109
- end
110
-
111
102
  def create_table(name, options = {}) #:nodoc:
112
103
  super(name, {:options => "ENGINE=InnoDB"}.merge(options))
113
104
  end
@@ -10,6 +10,8 @@ module JdbcSpec
10
10
  when :float then value.to_f
11
11
  when :datetime then cast_to_date_or_time(value)
12
12
  when :time then cast_to_time(value)
13
+ when :decimal then self.class.value_to_decimal(value)
14
+ when :boolean then self.class.value_to_boolean(value)
13
15
  else value
14
16
  end
15
17
  end
@@ -57,6 +57,24 @@ module JdbcSpec
57
57
  default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
58
58
  end
59
59
 
60
+ # Resets sequence to the max value of the table's pk if present.
61
+ def reset_pk_sequence!(table, pk = nil, sequence = nil)
62
+ unless pk and sequence
63
+ default_pk, default_sequence = pk_and_sequence_for(table)
64
+ pk ||= default_pk
65
+ sequence ||= default_sequence
66
+ end
67
+ if pk
68
+ if sequence
69
+ select_value <<-end_sql, 'Reset sequence'
70
+ SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
71
+ end_sql
72
+ else
73
+ @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
74
+ end
75
+ end
76
+ end
77
+
60
78
  # Find a table's primary key and sequence.
61
79
  def pk_and_sequence_for(table)
62
80
  # First try looking for a sequence with a dependency on the
@@ -185,5 +203,9 @@ module JdbcSpec
185
203
  'bigint'
186
204
  end
187
205
  end
206
+
207
+ def tables
208
+ @connection.tables(database_name, nil, nil, ["TABLE"])
209
+ end
188
210
  end
189
211
  end
@@ -3,12 +3,12 @@ module JdbcSpec
3
3
  #Taken from SQLite adapter
4
4
 
5
5
  def alter_table(table_name, options = {}) #:nodoc:
6
- table_name = table_name.downcase
6
+ table_name = table_name.to_s.downcase
7
7
  altered_table_name = "altered_#{table_name}"
8
8
  caller = lambda {|definition| yield definition if block_given?}
9
9
 
10
10
  transaction do
11
- move_table(table_name, altered_table_name)
11
+ move_table(table_name, altered_table_name, options)
12
12
  move_table(altered_table_name, table_name, &caller)
13
13
  end
14
14
  end
@@ -25,7 +25,7 @@ module JdbcSpec
25
25
  (options[:rename][column.name] ||
26
26
  options[:rename][column.name.to_sym] ||
27
27
  column.name) : column.name
28
-
28
+ column_name = column_name.to_s
29
29
  @definition.column(column_name, column.type,
30
30
  :limit => column.limit, :default => column.default,
31
31
  :null => column.null)
@@ -36,7 +36,7 @@ module JdbcSpec
36
36
 
37
37
  copy_table_indexes(from, to)
38
38
  copy_table_contents(from, to,
39
- @definition.columns.map {|column| column.name},
39
+ @definition.columns,
40
40
  options[:rename] || {})
41
41
  end
42
42
 
@@ -57,13 +57,13 @@ module JdbcSpec
57
57
  end
58
58
 
59
59
  def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
60
- column_mappings = Hash[*columns.map {|name| [name, name]}.flatten]
60
+ column_mappings = Hash[*columns.map {|col| [col.name, col.name]}.flatten]
61
61
  rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
62
62
  from_columns = columns(from).collect {|col| col.name}
63
- columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
63
+ columns = columns.find_all{|col| from_columns.include?(column_mappings[col.name])}
64
64
  execute("SELECT * FROM #{from}").each do |row|
65
- sql = "INSERT INTO #{to} ("+columns*','+") VALUES ("
66
- sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
65
+ sql = "INSERT INTO #{to} ("+columns.map(&:name)*','+") VALUES ("
66
+ sql << columns.map {|col| quote(row[column_mappings[col.name]],col)} * ', '
67
67
  sql << ')'
68
68
  execute sql
69
69
  end
Binary file
@@ -0,0 +1,62 @@
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
+ namespace :structure do
34
+ redefine_task :dump => :environment do
35
+ abcs = ActiveRecord::Base.configurations
36
+ ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
37
+ File.open("db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
38
+ if ActiveRecord::Base.connection.supports_migrations?
39
+ File.open("db/#{RAILS_ENV}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
40
+ end
41
+ end
42
+ end
43
+ namespace :test do
44
+ redefine_task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
45
+ abcs = ActiveRecord::Base.configurations
46
+ ActiveRecord::Base.establish_connection(:test)
47
+ ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') if abcs["test"]["adapter"] =~ /mysql/i
48
+ IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl|
49
+ ActiveRecord::Base.connection.execute(ddl)
50
+ end
51
+ end
52
+
53
+ redefine_task :purge => :environment do
54
+ abcs = ActiveRecord::Base.configurations
55
+ ActiveRecord::Base.establish_connection(:test)
56
+ db = ActiveRecord::Base.connection.database_name
57
+ ActiveRecord::Base.connection.recreate_database(db)
58
+ end
59
+ end
60
+ end
61
+ end
62
+
@@ -0,0 +1,12 @@
1
+ # To run this script, run the following in a mysql instance:
2
+ #
3
+ # drop database if exists weblog_development;
4
+ # create database weblog_development;
5
+ # grant all on weblog_development.* to blog@localhost;
6
+
7
+ require 'jdbc_common'
8
+ require 'db/derby'
9
+
10
+ class DerbyMultibyteTest < Test::Unit::TestCase
11
+ include MultibyteTestMethods
12
+ end
@@ -3,6 +3,7 @@
3
3
  # drop database if exists weblog_development;
4
4
  # create database weblog_development;
5
5
  # grant all on weblog_development.* to blog@localhost;
6
+ # flush privileges;
6
7
 
7
8
  require 'jdbc_common'
8
9
  require 'db/mysql'
data/test/simple.rb CHANGED
@@ -114,3 +114,40 @@ module SimpleTestMethods
114
114
  end
115
115
 
116
116
  end
117
+
118
+ class ActiveRecord::Base
119
+ cattr_accessor :defined_connections
120
+ end
121
+
122
+ module MultibyteTestMethods
123
+ include MigrationSetup
124
+
125
+ def setup
126
+ super
127
+ config = ActiveRecord::Base.defined_connections["ActiveRecord::Base"].config
128
+ props = java.util.Properties.new
129
+ props.setProperty("user", config[:username])
130
+ props.setProperty("password", config[:password])
131
+ @java_con = java.sql.DriverManager.getConnection(config[:url], props)
132
+ @java_con.setAutoCommit(true)
133
+ end
134
+
135
+ def teardown
136
+ @java_con.close
137
+ super
138
+ end
139
+
140
+ def test_select_multibyte_string
141
+ @java_con.createStatement().execute("insert into entries (title) values ('テスト')")
142
+ entry = Entry.find(:first)
143
+ assert_equal "テスト", entry.title
144
+ assert_equal entry, Entry.find_by_title("テスト")
145
+ end
146
+
147
+ def test_update_multibyte_string
148
+ Entry.create!(:title => "テスト")
149
+ rs = @java_con.createStatement().executeQuery("select title from entries")
150
+ assert rs.next
151
+ assert_equal "テスト", rs.getString(1)
152
+ end
153
+ end
metadata CHANGED
@@ -3,12 +3,12 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: ActiveRecord-JDBC
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.1
7
- date: 2007-05-07 00:00:00 -05:00
6
+ version: "0.4"
7
+ date: 2007-06-07 00:00:00 -07:00
8
8
  summary: JDBC adapter for ActiveRecord, for use within JRuby on Rails.
9
9
  require_paths:
10
10
  - lib
11
- email: nick@nicksieger.com, ola.bini@ki.se
11
+ email: nick@nicksieger.com, ola.bini@gmail.com
12
12
  homepage: http://jruby-extras.rubyforge.org/ActiveRecord-JDBC
13
13
  rubyforge_project: jruby-extras
14
14
  description: ActiveRecord-JDBC is a database adapter for Rails' ActiveRecord component that can be used with JRuby[http://www.jruby.org/]. It allows use of virtually any JDBC-compliant database with your JRuby on Rails application.
@@ -57,6 +57,7 @@ files:
57
57
  - test/db/logger.rb
58
58
  - test/db/mysql.rb
59
59
  - test/db/postgres.rb
60
+ - test/derby_multibyte_test.rb
60
61
  - test/derby_simple_test.rb
61
62
  - test/h2_simple_test.rb
62
63
  - test/hsqldb_simple_test.rb
@@ -76,12 +77,16 @@ files:
76
77
  - test/mysql_simple_test.rb
77
78
  - test/postgres_simple_test.rb
78
79
  - test/simple.rb
80
+ - lib/tasks/jdbc_databases.rake
79
81
  test_files: []
80
82
 
81
- rdoc_options: []
82
-
83
- extra_rdoc_files: []
84
-
83
+ rdoc_options:
84
+ - --main
85
+ - README.txt
86
+ extra_rdoc_files:
87
+ - History.txt
88
+ - Manifest.txt
89
+ - README.txt
85
90
  executables: []
86
91
 
87
92
  extensions: []