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