activerecord-jdbc-adapter 0.9.0.1 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/History.txt +31 -0
  2. data/Manifest.txt +7 -0
  3. data/README.txt +15 -2
  4. data/Rakefile +28 -30
  5. data/lib/active_record/connection_adapters/h2_adapter.rb +13 -1
  6. data/lib/active_record/connection_adapters/jdbc_adapter.rb +78 -96
  7. data/lib/jdbc_adapter/jdbc.rake +15 -5
  8. data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
  9. data/lib/jdbc_adapter/jdbc_cachedb.rb +4 -4
  10. data/lib/jdbc_adapter/jdbc_db2.rb +5 -7
  11. data/lib/jdbc_adapter/jdbc_derby.rb +57 -30
  12. data/lib/jdbc_adapter/jdbc_firebird.rb +2 -2
  13. data/lib/jdbc_adapter/jdbc_hsqldb.rb +53 -46
  14. data/lib/jdbc_adapter/jdbc_informix.rb +4 -5
  15. data/lib/jdbc_adapter/jdbc_mimer.rb +2 -2
  16. data/lib/jdbc_adapter/jdbc_mssql.rb +25 -23
  17. data/lib/jdbc_adapter/jdbc_mysql.rb +20 -22
  18. data/lib/jdbc_adapter/jdbc_oracle.rb +115 -117
  19. data/lib/jdbc_adapter/jdbc_postgre.rb +129 -59
  20. data/lib/jdbc_adapter/jdbc_sqlite3.rb +149 -28
  21. data/lib/jdbc_adapter/jdbc_sybase.rb +13 -2
  22. data/lib/jdbc_adapter/missing_functionality_helper.rb +12 -3
  23. data/lib/jdbc_adapter/version.rb +1 -1
  24. data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +6 -1101
  25. data/src/java/jdbc_adapter/JdbcDerbySpec.java +26 -23
  26. data/src/java/jdbc_adapter/JdbcMySQLSpec.java +79 -28
  27. data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +35 -0
  28. data/src/java/jdbc_adapter/RubyJdbcConnection.java +1149 -0
  29. data/src/java/jdbc_adapter/SQLBlock.java +12 -3
  30. data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +41 -0
  31. data/test/activerecord/connection_adapters/type_conversion_test.rb +1 -1
  32. data/test/db/derby.rb +0 -3
  33. data/test/db/h2.rb +0 -3
  34. data/test/db/hsqldb.rb +1 -4
  35. data/test/db/mysql.rb +1 -0
  36. data/test/db/oracle.rb +5 -0
  37. data/test/db/sqlite3.rb +7 -3
  38. data/test/derby_migration_test.rb +21 -0
  39. data/test/has_many_through.rb +11 -4
  40. data/test/jdbc_common.rb +13 -1
  41. data/test/models/data_types.rb +11 -1
  42. data/test/models/mixed_case.rb +20 -0
  43. data/test/mysql_multibyte_test.rb +4 -0
  44. data/test/oracle_simple_test.rb +1 -1
  45. data/test/postgres_mixed_case_test.rb +19 -0
  46. data/test/simple.rb +220 -41
  47. data/test/sqlite3_simple_test.rb +83 -0
  48. data/test/sybase_jtds_simple_test.rb +6 -0
  49. metadata +12 -10
@@ -1,3 +1,4 @@
1
+
1
2
  module ::JdbcSpec
2
3
  # Don't need to load native postgres adapter
3
4
  $LOADED_FEATURES << "active_record/connection_adapters/postgresql_adapter.rb"
@@ -14,12 +15,16 @@ module ::JdbcSpec
14
15
  end
15
16
 
16
17
  module PostgreSQL
18
+ def self.adapter_matcher(name, *)
19
+ name =~ /postgre/i ? self : false
20
+ end
21
+
17
22
  def self.column_selector
18
23
  [/postgre/i, lambda {|cfg,col| col.extend(::JdbcSpec::PostgreSQL::Column)}]
19
24
  end
20
25
 
21
- def self.adapter_selector
22
- [/postgre/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::PostgreSQL)}]
26
+ def self.jdbc_connection_class
27
+ ::ActiveRecord::ConnectionAdapters::PostgresJdbcConnection
23
28
  end
24
29
 
25
30
  module Column
@@ -42,6 +47,7 @@ module ::JdbcSpec
42
47
  end
43
48
 
44
49
  def cast_to_boolean(value)
50
+ return nil if value.nil?
45
51
  if value == true || value == false
46
52
  value
47
53
  else
@@ -49,24 +55,7 @@ module ::JdbcSpec
49
55
  end
50
56
  end
51
57
 
52
- def cast_to_date_or_time(value)
53
- return value if value.is_a? Date
54
- return nil if value.blank?
55
- guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
56
- end
57
-
58
- def cast_to_time(value)
59
- return value if value.is_a? Time
60
- time_array = ParseDate.parsedate value
61
- time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
62
- Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
63
- end
64
-
65
- def guess_date_or_time(value)
66
- (value.hour == 0 and value.min == 0 and value.sec == 0) ?
67
- Date.new(value.year, value.month, value.day) : value
68
- end
69
-
58
+ # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
70
59
  def default_value(value)
71
60
  # Boolean types
72
61
  return "t" if value =~ /true/i
@@ -95,13 +84,62 @@ module ::JdbcSpec
95
84
  tp
96
85
  end
97
86
 
87
+ def postgresql_version
88
+ @postgresql_version ||=
89
+ begin
90
+ select('SELECT version()').to_a[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
91
+ ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
92
+ rescue
93
+ 0
94
+ end
95
+ end
96
+
97
+ # Does PostgreSQL support migrations?
98
+ def supports_migrations?
99
+ true
100
+ end
101
+
102
+ # Does PostgreSQL support standard conforming strings?
103
+ def supports_standard_conforming_strings?
104
+ # Temporarily set the client message level above error to prevent unintentional
105
+ # error messages in the logs when working on a PostgreSQL database server that
106
+ # does not support standard conforming strings.
107
+ client_min_messages_old = client_min_messages
108
+ self.client_min_messages = 'panic'
109
+
110
+ # postgres-pr does not raise an exception when client_min_messages is set higher
111
+ # than error and "SHOW standard_conforming_strings" fails, but returns an empty
112
+ # PGresult instead.
113
+ has_support = select('SHOW standard_conforming_strings').to_a[0][0] rescue false
114
+ self.client_min_messages = client_min_messages_old
115
+ has_support
116
+ end
117
+
118
+ def supports_insert_with_returning?
119
+ postgresql_version >= 80200
120
+ end
121
+
122
+ def supports_ddl_transactions?
123
+ true
124
+ end
125
+
126
+ def supports_savepoints?
127
+ true
128
+ end
129
+
130
+ # Returns the configured supported identifier length supported by PostgreSQL,
131
+ # or report the default of 63 on PostgreSQL 7.x.
132
+ def table_alias_length
133
+ @table_alias_length ||= (postgresql_version >= 80000 ? select('SHOW max_identifier_length').to_a[0][0].to_i : 63)
134
+ end
135
+
98
136
  def default_sequence_name(table_name, pk = nil)
99
137
  default_pk, default_seq = pk_and_sequence_for(table_name)
100
138
  default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
101
139
  end
102
140
 
103
141
  # Resets sequence to the max value of the table's pk if present.
104
- def reset_pk_sequence!(table, pk = nil, sequence = nil)
142
+ def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
105
143
  unless pk and sequence
106
144
  default_pk, default_sequence = pk_and_sequence_for(table)
107
145
  pk ||= default_pk
@@ -109,28 +147,35 @@ module ::JdbcSpec
109
147
  end
110
148
  if pk
111
149
  if sequence
150
+ quoted_sequence = quote_column_name(sequence)
151
+
112
152
  select_value <<-end_sql, 'Reset sequence'
113
- SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false)
114
- end_sql
153
+ SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
154
+ end_sql
115
155
  else
116
156
  @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
117
157
  end
118
158
  end
119
159
  end
120
160
 
161
+ def quote_regclass(table_name)
162
+ table_name.to_s.split('.').map do |part|
163
+ part =~ /".*"/i ? part : quote_table_name(part)
164
+ end.join('.')
165
+ end
166
+
121
167
  # Find a table's primary key and sequence.
122
- def pk_and_sequence_for(table)
168
+ def pk_and_sequence_for(table) #:nodoc:
123
169
  # First try looking for a sequence with a dependency on the
124
170
  # given table's primary key.
125
- result = select(<<-end_sql, 'PK and serial sequence')[0]
126
- SELECT attr.attname AS nm, name.nspname AS nsp, seq.relname AS rel
171
+ result = select(<<-end_sql, 'PK and serial sequence')[0]
172
+ SELECT attr.attname, seq.relname
127
173
  FROM pg_class seq,
128
174
  pg_attribute attr,
129
175
  pg_depend dep,
130
176
  pg_namespace name,
131
177
  pg_constraint cons
132
178
  WHERE seq.oid = dep.objid
133
- AND seq.relnamespace = name.oid
134
179
  AND seq.relkind = 'S'
135
180
  AND attr.attrelid = dep.refobjid
136
181
  AND attr.attnum = dep.refobjsubid
@@ -140,15 +185,19 @@ module ::JdbcSpec
140
185
  AND dep.refobjid = '#{table}'::regclass
141
186
  end_sql
142
187
 
143
- if result.nil? or result.empty?
144
- # If that fails, try parsing the primary key's default value.
145
- # Support the 7.x and 8.0 nextval('foo'::text) as well as
146
- # the 8.1+ nextval('foo'::regclass).
147
- # TODO: assumes sequence is in same schema as table.
148
- result = select(<<-end_sql, 'PK and custom sequence')[0]
149
- SELECT attr.attname AS nm, name.nspname AS nsp, split_part(def.adsrc, '\\\'', 2) AS rel
188
+ if result.nil? or result.empty?
189
+ # If that fails, try parsing the primary key's default value.
190
+ # Support the 7.x and 8.0 nextval('foo'::text) as well as
191
+ # the 8.1+ nextval('foo'::regclass).
192
+ result = query(<<-end_sql, 'PK and custom sequence')[0]
193
+ SELECT attr.attname,
194
+ CASE
195
+ WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
196
+ substr(split_part(def.adsrc, '''', 2),
197
+ strpos(split_part(def.adsrc, '''', 2), '.')+1)
198
+ ELSE split_part(def.adsrc, '''', 2)
199
+ END as relname
150
200
  FROM pg_class t
151
- JOIN pg_namespace name ON (t.relnamespace = name.oid)
152
201
  JOIN pg_attribute attr ON (t.oid = attrelid)
153
202
  JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
154
203
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
@@ -156,17 +205,40 @@ module ::JdbcSpec
156
205
  AND cons.contype = 'p'
157
206
  AND def.adsrc ~* 'nextval'
158
207
  end_sql
208
+ end
209
+
210
+ [result["attname"], result["relname"]]
211
+ rescue
212
+ nil
213
+ end
214
+
215
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
216
+ # Extract the table from the insert sql. Yuck.
217
+ table = sql.split(" ", 4)[2].gsub('"', '')
218
+
219
+ # Try an insert with 'returning id' if available (PG >= 8.2)
220
+ if supports_insert_with_returning?
221
+ pk, sequence_name = *pk_and_sequence_for(table) unless pk
222
+ if pk
223
+ id = select_value("#{sql} RETURNING #{quote_column_name(pk)}")
224
+ clear_query_cache
225
+ return id
159
226
  end
160
- # check for existence of . in sequence name as in public.foo_sequence. if it does not exist, join the current namespace
161
- result['rel']['.'] ? [result['nm'], result['rel']] : [result['nm'], "#{result['nsp']}.#{result['rel']}"]
162
- rescue
163
- nil
164
227
  end
165
228
 
166
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
229
+ # Otherwise, insert then grab last_insert_id.
167
230
  execute(sql, name)
168
- table = sql.split(" ", 4)[2]
169
- id_value || pk && last_insert_id(table, sequence_name || default_sequence_name(table, pk))
231
+
232
+ # If neither pk nor sequence name is given, look them up.
233
+ unless pk || sequence_name
234
+ pk, sequence_name = *pk_and_sequence_for(table)
235
+ end
236
+
237
+ # If a pk is given, fallback to default sequence name.
238
+ # Don't fetch last insert id for a table without a pk.
239
+ if pk && sequence_name ||= default_sequence_name(table, pk)
240
+ last_insert_id(table, sequence_name)
241
+ end
170
242
  end
171
243
 
172
244
  def columns(table_name, name=nil)
@@ -248,18 +320,11 @@ module ::JdbcSpec
248
320
 
249
321
  @connection.connection.close
250
322
  begin
251
- file = "db/#{RAILS_ENV}_structure.sql"
252
- `pg_dump -i -U "#{@config[:username]}" -s -x -O -f #{file} #{search_path} #{database}`
323
+ definition = `pg_dump -i -U "#{@config[:username]}" -s -x -O #{search_path} #{database}`
253
324
  raise "Error dumping database" if $?.exitstatus == 1
254
325
 
255
326
  # need to patch away any references to SQL_ASCII as it breaks the JDBC driver
256
- lines = File.readlines(file)
257
- File.open(file, "w") do |io|
258
- lines.each do |line|
259
- line.gsub!(/SQL_ASCII/, 'UNICODE')
260
- io.write(line)
261
- end
262
- end
327
+ definition.gsub(/SQL_ASCII/, 'UNICODE')
263
328
  ensure
264
329
  reconnect!
265
330
  end
@@ -333,8 +398,12 @@ module ::JdbcSpec
333
398
  %("#{name}")
334
399
  end
335
400
 
336
- def quoted_date(value)
337
- value.strftime("%Y-%m-%d %H:%M:%S")
401
+ def quoted_date(value) #:nodoc:
402
+ if value.acts_like?(:time) && value.respond_to?(:usec)
403
+ "#{super}.#{sprintf("%06d", value.usec)}"
404
+ else
405
+ super
406
+ end
338
407
  end
339
408
 
340
409
  def disable_referential_integrity(&block) #:nodoc:
@@ -349,17 +418,17 @@ module ::JdbcSpec
349
418
  end
350
419
 
351
420
  def add_column(table_name, column_name, type, options = {})
352
- execute("ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}")
421
+ execute("ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}")
353
422
  change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
354
423
  if options[:null] == false
355
- execute("UPDATE #{table_name} SET #{column_name} = '#{options[:default]}'") if options[:default]
356
- execute("ALTER TABLE #{table_name} ALTER #{column_name} SET NOT NULL")
424
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)} = '#{options[:default]}'") if options[:default]
425
+ execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} SET NOT NULL")
357
426
  end
358
427
  end
359
428
 
360
429
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
361
430
  begin
362
- execute "ALTER TABLE #{table_name} ALTER #{column_name} TYPE #{type_to_sql(type, options[:limit])}"
431
+ execute "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit])}"
363
432
  rescue ActiveRecord::StatementInvalid
364
433
  # This is PG7, so we use a more arcane way of doing it.
365
434
  begin_db_transaction
@@ -373,11 +442,11 @@ module ::JdbcSpec
373
442
  end
374
443
 
375
444
  def change_column_default(table_name, column_name, default) #:nodoc:
376
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT '#{default}'"
445
+ execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT '#{default}'"
377
446
  end
378
447
 
379
448
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
380
- execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
449
+ execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
381
450
  end
382
451
 
383
452
  def remove_index(table_name, options) #:nodoc:
@@ -401,3 +470,4 @@ module ::JdbcSpec
401
470
  end
402
471
  end
403
472
  end
473
+
@@ -1,35 +1,94 @@
1
1
  module ::JdbcSpec
2
2
  module ActiveRecordExtensions
3
3
  def sqlite3_connection(config)
4
+ parse_sqlite3_config!(config)
5
+
4
6
  config[:url] ||= "jdbc:sqlite:#{config[:database]}"
5
7
  config[:driver] ||= "org.sqlite.JDBC"
6
8
  jdbc_connection(config)
7
9
  end
10
+
11
+ def parse_sqlite3_config!(config)
12
+ config[:database] ||= config[:dbfile]
13
+
14
+ # Allow database path relative to RAILS_ROOT, but only if
15
+ # the database path is not the special path that tells
16
+ # Sqlite to build a database only in memory.
17
+ if Object.const_defined?(:RAILS_ROOT) && ':memory:' != config[:database]
18
+ config[:database] = File.expand_path(config[:database], RAILS_ROOT)
19
+ end
20
+ end
8
21
  end
9
22
 
10
23
  module SQLite3
24
+ def self.adapter_matcher(name, *)
25
+ name =~ /sqlite/i ? self : false
26
+ end
27
+
11
28
  def self.column_selector
12
29
  [/sqlite/i, lambda {|cfg,col| col.extend(::JdbcSpec::SQLite3::Column)}]
13
30
  end
14
31
 
15
- def self.adapter_selector
16
- [/sqlite/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::SQLite3)}]
32
+ def self.jdbc_connection_class
33
+ ::ActiveRecord::ConnectionAdapters::Sqlite3JdbcConnection
17
34
  end
18
35
 
19
36
  module Column
20
37
 
38
+ def type_cast(value)
39
+ return nil if value.nil?
40
+ case type
41
+ when :string then value
42
+ when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
43
+ when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
44
+ when :float then value.to_f
45
+ when :datetime then JdbcSpec::SQLite3::Column.cast_to_date_or_time(value)
46
+ when :date then JdbcSpec::SQLite3::Column.cast_to_date_or_time(value)
47
+ when :time then JdbcSpec::SQLite3::Column.cast_to_time(value)
48
+ when :decimal then self.class.value_to_decimal(value)
49
+ when :boolean then self.class.value_to_boolean(value)
50
+ else value
51
+ end
52
+ end
53
+
21
54
  private
22
55
  def simplified_type(field_type)
23
56
  case field_type
24
- when /^integer\(1\)$/i then :boolean
57
+ when /boolean/i then :boolean
25
58
  when /text/i then :string
26
59
  when /int/i then :integer
27
- when /real/i then @scale == 0 ? :integer : :decimal
28
- when /date|time/i then :datetime
60
+ when /float/i then :float
61
+ when /real/i then @scale == 0 ? :integer : :decimal
62
+ when /datetime/i then :datetime
63
+ when /date/i then :date
64
+ when /time/i then :time
29
65
  when /blob/i then :binary
30
66
  end
31
67
  end
32
68
 
69
+ def extract_precision(sql_type)
70
+ case sql_type
71
+ when /^(real)\((\d+)(,\d+)?\)/i then $2.to_i
72
+ else super
73
+ end
74
+ end
75
+
76
+ def extract_scale(sql_type)
77
+ case sql_type
78
+ when /^(real)\((\d+)\)/i then 0
79
+ when /^(real)\((\d+)(,(\d+))\)/i then $4.to_i
80
+ else super
81
+ end
82
+ end
83
+
84
+ # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
85
+ def default_value(value)
86
+ # jdbc returns column default strings with actual single quotes around the value.
87
+ return $1 if value =~ /^'(.*)'$/
88
+
89
+ value
90
+ end
91
+
33
92
  def self.cast_to_date_or_time(value)
34
93
  return value if value.is_a? Date
35
94
  return nil if value.blank?
@@ -38,7 +97,9 @@ module ::JdbcSpec
38
97
 
39
98
  def self.cast_to_time(value)
40
99
  return value if value.is_a? Time
41
- Time.at(value) rescue nil
100
+ time_array = ParseDate.parsedate value
101
+ time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
102
+ Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
42
103
  end
43
104
 
44
105
  def self.guess_date_or_time(value)
@@ -47,40 +108,40 @@ module ::JdbcSpec
47
108
  end
48
109
  end
49
110
 
50
- def type_cast(value)
51
- return nil if value.nil?
52
- case type
53
- when :string then value
54
- when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
55
- when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
56
- when :float then value.to_f
57
- when :datetime then JdbcSpec::SQLite3::Column.cast_to_date_or_time(value)
58
- when :time then JdbcSpec::SQLite3::Column.cast_to_time(value)
59
- when :decimal then self.class.value_to_decimal(value)
60
- when :boolean then self.class.value_to_boolean(value)
61
- else value
62
- end
111
+ def adapter_name #:nodoc:
112
+ 'SQLite'
113
+ end
114
+
115
+ def supports_count_distinct? #:nodoc:
116
+ sqlite_version >= '3.2.6'
117
+ end
118
+
119
+ def supports_autoincrement? #:nodoc:
120
+ sqlite_version >= '3.1.0'
121
+ end
122
+
123
+ def sqlite_version
124
+ @sqlite_version ||= select_value('select sqlite_version(*)')
63
125
  end
64
126
 
65
127
  def modify_types(tp)
66
128
  tp[:primary_key] = "INTEGER PRIMARY KEY AUTOINCREMENT"
67
129
  tp[:float] = { :name => "REAL" }
68
130
  tp[:decimal] = { :name => "REAL" }
69
- tp[:datetime] = { :name => "INTEGER" }
70
- tp[:timestamp] = { :name => "INTEGER" }
71
- tp[:time] = { :name => "INTEGER" }
72
- tp[:date] = { :name => "INTEGER" }
73
- tp[:boolean] = { :name => "INTEGER", :limit => 1}
131
+ tp[:datetime] = { :name => "DATETIME" }
132
+ tp[:timestamp] = { :name => "DATETIME" }
133
+ tp[:time] = { :name => "TIME" }
134
+ tp[:date] = { :name => "DATE" }
135
+ tp[:boolean] = { :name => "BOOLEAN" }
74
136
  tp
75
137
  end
76
138
 
77
139
  def quote(value, column = nil) # :nodoc:
78
140
  return value.quoted_id if value.respond_to?(:quoted_id)
79
-
80
141
  case value
81
142
  when String
82
143
  if column && column.type == :binary
83
- "'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}'"
144
+ "'#{quote_string(column.class.string_to_binary(value))}'"
84
145
  else
85
146
  "'#{quote_string(value)}'"
86
147
  end
@@ -88,16 +149,27 @@ module ::JdbcSpec
88
149
  end
89
150
  end
90
151
 
152
+ def quote_column_name(name) #:nodoc:
153
+ name = name.to_s
154
+ # Did not find reference on values needing quoting, but these
155
+ # happen in AR unit tests
156
+ if name == "references" || name =~ /-/
157
+ %Q("#{name}")
158
+ else
159
+ name
160
+ end
161
+ end
162
+
91
163
  def quote_string(str)
92
164
  str.gsub(/'/, "''")
93
165
  end
94
166
 
95
167
  def quoted_true
96
- '1'
168
+ %Q{'t'}
97
169
  end
98
170
 
99
171
  def quoted_false
100
- '0'
172
+ %Q{'f'}
101
173
  end
102
174
 
103
175
  def add_column(table_name, column_name, type, options = {})
@@ -184,5 +256,54 @@ module ::JdbcSpec
184
256
  ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, name, unique, cols)
185
257
  end
186
258
  end
259
+
260
+ def recreate_database(name)
261
+ tables.each{ |table| drop_table(table) }
262
+ end
263
+
264
+ def _execute(sql, name = nil)
265
+ if ActiveRecord::ConnectionAdapters::JdbcConnection::select?(sql)
266
+ @connection.execute_query(sql)
267
+ else
268
+ affected_rows = @connection.execute_update(sql)
269
+ ActiveRecord::ConnectionAdapters::JdbcConnection::insert?(sql) ? last_insert_id(sql.split(" ", 4)[2], nil) : affected_rows
270
+ end
271
+ end
272
+
273
+ def table_structure(table_name)
274
+ returning structure = @connection.execute_query("PRAGMA table_info(#{quote_table_name(table_name)})") do
275
+ raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
276
+ end
277
+ end
278
+
279
+ def columns(table_name, name = nil) #:nodoc:
280
+ table_structure(table_name).map do |field|
281
+ ::ActiveRecord::ConnectionAdapters::JdbcColumn.new(@config, field['name'], field['dflt_value'], field['type'], field['notnull'] == "0")
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ module ActiveRecord
288
+ module ConnectionAdapters
289
+ class JdbcColumn < Column
290
+ def self.string_to_binary(value)
291
+ value.gsub(/\0|%/n) do |b|
292
+ case b
293
+ when "\0" then "%00"
294
+ when "\%" then "%25"
295
+ end
296
+ end
297
+ end
298
+
299
+ def self.binary_to_string(value)
300
+ value.gsub(/%00|%25/n) do |b|
301
+ case b
302
+ when "%00" then "\0"
303
+ when "%25" then "%"
304
+ end
305
+ end
306
+ end
307
+ end
187
308
  end
188
309
  end