activerecord-jdbc-adapter 0.9.0.1 → 0.9.1

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