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.
- data/History.txt +31 -0
- data/Manifest.txt +7 -0
- data/README.txt +15 -2
- data/Rakefile +28 -30
- data/lib/active_record/connection_adapters/h2_adapter.rb +13 -1
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +78 -96
- data/lib/jdbc_adapter/jdbc.rake +15 -5
- data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
- data/lib/jdbc_adapter/jdbc_cachedb.rb +4 -4
- data/lib/jdbc_adapter/jdbc_db2.rb +5 -7
- data/lib/jdbc_adapter/jdbc_derby.rb +57 -30
- data/lib/jdbc_adapter/jdbc_firebird.rb +2 -2
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +53 -46
- data/lib/jdbc_adapter/jdbc_informix.rb +4 -5
- data/lib/jdbc_adapter/jdbc_mimer.rb +2 -2
- data/lib/jdbc_adapter/jdbc_mssql.rb +25 -23
- data/lib/jdbc_adapter/jdbc_mysql.rb +20 -22
- data/lib/jdbc_adapter/jdbc_oracle.rb +115 -117
- data/lib/jdbc_adapter/jdbc_postgre.rb +129 -59
- data/lib/jdbc_adapter/jdbc_sqlite3.rb +149 -28
- data/lib/jdbc_adapter/jdbc_sybase.rb +13 -2
- data/lib/jdbc_adapter/missing_functionality_helper.rb +12 -3
- data/lib/jdbc_adapter/version.rb +1 -1
- data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +6 -1101
- data/src/java/jdbc_adapter/JdbcDerbySpec.java +26 -23
- data/src/java/jdbc_adapter/JdbcMySQLSpec.java +79 -28
- data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +35 -0
- data/src/java/jdbc_adapter/RubyJdbcConnection.java +1149 -0
- data/src/java/jdbc_adapter/SQLBlock.java +12 -3
- data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +41 -0
- data/test/activerecord/connection_adapters/type_conversion_test.rb +1 -1
- data/test/db/derby.rb +0 -3
- data/test/db/h2.rb +0 -3
- data/test/db/hsqldb.rb +1 -4
- data/test/db/mysql.rb +1 -0
- data/test/db/oracle.rb +5 -0
- data/test/db/sqlite3.rb +7 -3
- data/test/derby_migration_test.rb +21 -0
- data/test/has_many_through.rb +11 -4
- data/test/jdbc_common.rb +13 -1
- data/test/models/data_types.rb +11 -1
- data/test/models/mixed_case.rb +20 -0
- data/test/mysql_multibyte_test.rb +4 -0
- data/test/oracle_simple_test.rb +1 -1
- data/test/postgres_mixed_case_test.rb +19 -0
- data/test/simple.rb +220 -41
- data/test/sqlite3_simple_test.rb +83 -0
- data/test/sybase_jtds_simple_test.rb +6 -0
- 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.
|
22
|
-
|
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
|
-
|
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
|
-
|
114
|
-
|
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
|
-
|
126
|
-
SELECT attr.attname
|
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
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
-
|
229
|
+
# Otherwise, insert then grab last_insert_id.
|
167
230
|
execute(sql, name)
|
168
|
-
|
169
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
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.
|
16
|
-
|
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
|
57
|
+
when /boolean/i then :boolean
|
25
58
|
when /text/i then :string
|
26
59
|
when /int/i then :integer
|
27
|
-
when /
|
28
|
-
when /
|
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
|
-
|
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
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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 => "
|
70
|
-
tp[:timestamp] = { :name => "
|
71
|
-
tp[:time] = { :name => "
|
72
|
-
tp[:date] = { :name => "
|
73
|
-
tp[:boolean] = { :name => "
|
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(
|
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
|
-
'
|
168
|
+
%Q{'t'}
|
97
169
|
end
|
98
170
|
|
99
171
|
def quoted_false
|
100
|
-
'
|
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
|