activerecord-jdbc-adapter 1.0.0.beta1-java → 1.0.0.beta2-java
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 +37 -0
- data/Manifest.txt +8 -0
- data/README.txt +41 -88
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +9 -0
- data/lib/arel/engines/sql/compilers/mssql_compiler.rb +34 -0
- data/lib/arjdbc/db2/adapter.rb +232 -52
- data/lib/arjdbc/derby/adapter.rb +28 -1
- data/lib/arjdbc/derby/connection_methods.rb +1 -1
- data/lib/arjdbc/discover.rb +1 -1
- data/lib/arjdbc/firebird/adapter.rb +26 -0
- data/lib/arjdbc/h2/adapter.rb +13 -0
- data/lib/arjdbc/hsqldb/adapter.rb +8 -6
- data/lib/arjdbc/informix/adapter.rb +4 -0
- data/lib/arjdbc/jdbc/adapter.rb +27 -5
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/connection.rb +76 -45
- data/lib/arjdbc/jdbc/jdbc.rake +22 -20
- data/lib/arjdbc/jdbc/type_converter.rb +9 -2
- data/lib/arjdbc/mssql/adapter.rb +102 -24
- data/lib/arjdbc/mssql/connection_methods.rb +19 -2
- data/lib/arjdbc/mssql/tsql_helper.rb +1 -0
- data/lib/arjdbc/mysql/adapter.rb +6 -0
- data/lib/arjdbc/mysql/connection_methods.rb +8 -7
- data/lib/arjdbc/oracle/adapter.rb +8 -6
- data/lib/arjdbc/postgresql/adapter.rb +51 -19
- data/lib/arjdbc/version.rb +1 -1
- data/lib/jdbc_adapter/rake_tasks.rb +3 -0
- data/rails_generators/templates/lib/tasks/jdbc.rake +2 -2
- data/rakelib/package.rake +2 -0
- data/rakelib/test.rake +6 -3
- data/src/java/arjdbc/derby/DerbyModule.java +30 -1
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +74 -0
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +7 -3
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +45 -30
- data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +54 -1
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +85 -0
- data/test/abstract_db_create.rb +6 -1
- data/test/db/jndi_config.rb +20 -10
- data/test/db2_simple_test.rb +34 -1
- data/test/derby_simple_test.rb +78 -0
- data/test/generic_jdbc_connection_test.rb +21 -1
- data/test/jndi_callbacks_test.rb +2 -1
- data/test/jndi_test.rb +1 -11
- data/test/models/entry.rb +20 -0
- data/test/mssql_limit_offset_test.rb +28 -0
- data/test/mssql_simple_test.rb +7 -1
- data/test/mysql_info_test.rb +49 -6
- data/test/mysql_simple_test.rb +4 -0
- data/test/oracle_simple_test.rb +3 -47
- data/test/oracle_specific_test.rb +83 -0
- data/test/postgres_db_create_test.rb +6 -0
- data/test/postgres_drop_db_test.rb +16 -0
- data/test/postgres_simple_test.rb +17 -0
- data/test/postgres_table_alias_length_test.rb +15 -0
- data/test/simple.rb +17 -4
- metadata +33 -7
data/lib/arjdbc/mssql/adapter.rb
CHANGED
@@ -55,6 +55,25 @@ module ::ArJdbc
|
|
55
55
|
tp
|
56
56
|
end
|
57
57
|
|
58
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
59
|
+
# MSSQL's NVARCHAR(n | max) column supports either a number between 1 and
|
60
|
+
# 4000, or the word "MAX", which corresponds to 2**30-1 UCS-2 characters.
|
61
|
+
#
|
62
|
+
# It does not accept NVARCHAR(1073741823) here, so we have to change it
|
63
|
+
# to NVARCHAR(MAX), even though they are logically equivalent.
|
64
|
+
#
|
65
|
+
# MSSQL Server 2000 is skipped here because I don't know how it will behave.
|
66
|
+
#
|
67
|
+
# See: http://msdn.microsoft.com/en-us/library/ms186939.aspx
|
68
|
+
if type.to_s == 'string' and limit == 1073741823 and sqlserver_version != "2000"
|
69
|
+
'NVARCHAR(MAX)'
|
70
|
+
elsif %w( boolean date datetime ).include?(type.to_s)
|
71
|
+
super(type) # cannot specify limit/precision/scale with these types
|
72
|
+
else
|
73
|
+
super
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
58
77
|
module Column
|
59
78
|
attr_accessor :identity, :is_special
|
60
79
|
|
@@ -67,9 +86,9 @@ module ::ArJdbc
|
|
67
86
|
when /timestamp/i then :timestamp
|
68
87
|
when /time/i then :time
|
69
88
|
when /date/i then :date
|
70
|
-
when /text|ntext/i
|
89
|
+
when /text|ntext|xml/i then :text
|
71
90
|
when /binary|image|varbinary/i then :binary
|
72
|
-
when /char|nchar|nvarchar|string|varchar/i then :string
|
91
|
+
when /char|nchar|nvarchar|string|varchar/i then (@limit == 1073741823 ? (@limit = nil; :text) : :string)
|
73
92
|
when /bit/i then :boolean
|
74
93
|
when /uniqueidentifier/i then :string
|
75
94
|
end
|
@@ -94,7 +113,15 @@ module ::ArJdbc
|
|
94
113
|
when :binary then unquote value
|
95
114
|
else value
|
96
115
|
end
|
116
|
+
end
|
97
117
|
|
118
|
+
def extract_limit(sql_type)
|
119
|
+
case sql_type
|
120
|
+
when /text|ntext|xml|binary|image|varbinary|bit/
|
121
|
+
nil
|
122
|
+
else
|
123
|
+
super
|
124
|
+
end
|
98
125
|
end
|
99
126
|
|
100
127
|
def is_utf8?
|
@@ -108,10 +135,14 @@ module ::ArJdbc
|
|
108
135
|
def cast_to_time(value)
|
109
136
|
return value if value.is_a?(Time)
|
110
137
|
time_array = ParseDate.parsedate(value)
|
138
|
+
return nil if !time_array.any?
|
111
139
|
time_array[0] ||= 2000
|
112
140
|
time_array[1] ||= 1
|
113
141
|
time_array[2] ||= 1
|
114
|
-
Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
|
142
|
+
return Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
|
143
|
+
|
144
|
+
# Try DateTime instead - the date may be outside the time period support by Time.
|
145
|
+
DateTime.new(*time_array[0..5]) rescue nil
|
115
146
|
end
|
116
147
|
|
117
148
|
def cast_to_date(value)
|
@@ -127,8 +158,18 @@ module ::ArJdbc
|
|
127
158
|
return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
|
128
159
|
end
|
129
160
|
end
|
161
|
+
if value.is_a?(DateTime)
|
162
|
+
begin
|
163
|
+
# Attempt to convert back to a Time, but it could fail for dates significantly in the past/future.
|
164
|
+
return Time.mktime(value.year, value.mon, value.day, value.hour, value.min, value.sec)
|
165
|
+
rescue ArgumentError
|
166
|
+
return value
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
130
170
|
return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
|
131
|
-
|
171
|
+
|
172
|
+
return value.is_a?(Date) ? value : nil
|
132
173
|
end
|
133
174
|
|
134
175
|
# These methods will only allow the adapter to insert binary data with a length of 7K or less
|
@@ -143,7 +184,9 @@ module ::ArJdbc
|
|
143
184
|
return value.quoted_id if value.respond_to?(:quoted_id)
|
144
185
|
|
145
186
|
case value
|
146
|
-
|
187
|
+
# SQL Server 2000 doesn't let you insert an integer into a NVARCHAR
|
188
|
+
# column, so we include Integer here.
|
189
|
+
when String, ActiveSupport::Multibyte::Chars, Integer
|
147
190
|
value = value.to_s
|
148
191
|
if column && column.type == :binary
|
149
192
|
"'#{quote_string(ArJdbc::MsSQL::Column.string_to_binary(value))}'" # ' (for ruby-mode)
|
@@ -181,6 +224,10 @@ module ::ArJdbc
|
|
181
224
|
quote false
|
182
225
|
end
|
183
226
|
|
227
|
+
def adapter_name #:nodoc:
|
228
|
+
'MsSQL'
|
229
|
+
end
|
230
|
+
|
184
231
|
module SqlServer2000LimitOffset
|
185
232
|
def add_limit_offset!(sql, options)
|
186
233
|
limit = options[:limit]
|
@@ -205,7 +252,13 @@ module ::ArJdbc
|
|
205
252
|
#I am not sure this will cover all bases. but all the tests pass
|
206
253
|
new_order = "#{order}, #{table_name}.id" if order.index("#{table_name}.id").nil?
|
207
254
|
new_order ||= order
|
208
|
-
|
255
|
+
|
256
|
+
if (rest_of_query.match(/WHERE/).nil?)
|
257
|
+
new_sql = "#{select} TOP #{limit} #{rest_of_query} WHERE #{table_name}.id NOT IN (#{select} TOP #{offset} #{table_name}.id #{rest} ORDER BY #{new_order}) ORDER BY #{order} "
|
258
|
+
else
|
259
|
+
new_sql = "#{select} TOP #{limit} #{rest_of_query} AND #{table_name}.id NOT IN (#{select} TOP #{offset} #{table_name}.id #{rest} ORDER BY #{new_order}) ORDER BY #{order} "
|
260
|
+
end
|
261
|
+
|
209
262
|
sql.replace(new_sql)
|
210
263
|
end
|
211
264
|
end
|
@@ -223,8 +276,11 @@ module ::ArJdbc
|
|
223
276
|
sql.sub!(/ ORDER BY.*$/i, '')
|
224
277
|
find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
|
225
278
|
whole, select, rest_of_query = find_select.match(sql).to_a
|
226
|
-
|
227
|
-
|
279
|
+
if rest_of_query.strip!.first == '*'
|
280
|
+
from_table = /.*FROM\s*\b(\w*)\b/i.match(rest_of_query).to_a[1]
|
281
|
+
end
|
282
|
+
new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(ORDER BY #{order}) AS _row_num, #{from_table + '.' if from_table}#{rest_of_query}"
|
283
|
+
new_sql << ") AS t WHERE t._row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
|
228
284
|
sql.replace(new_sql)
|
229
285
|
end
|
230
286
|
end
|
@@ -260,12 +316,14 @@ module ::ArJdbc
|
|
260
316
|
end
|
261
317
|
|
262
318
|
def rename_table(name, new_name)
|
319
|
+
clear_cached_table(name)
|
263
320
|
execute "EXEC sp_rename '#{name}', '#{new_name}'"
|
264
321
|
end
|
265
322
|
|
266
323
|
# Adds a new column to the named table.
|
267
324
|
# See TableDefinition#column for details of the options you can use.
|
268
325
|
def add_column(table_name, column_name, type, options = {})
|
326
|
+
clear_cached_table(table_name)
|
269
327
|
add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
270
328
|
add_column_options!(add_column_sql, options)
|
271
329
|
# TODO: Add support to mimic date columns, using constraints to mark them as such in the database
|
@@ -274,15 +332,18 @@ module ::ArJdbc
|
|
274
332
|
end
|
275
333
|
|
276
334
|
def rename_column(table, column, new_column_name)
|
335
|
+
clear_cached_table(table)
|
277
336
|
execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
|
278
337
|
end
|
279
338
|
|
280
339
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
340
|
+
clear_cached_table(table_name)
|
281
341
|
change_column_type(table_name, column_name, type, options)
|
282
342
|
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
283
343
|
end
|
284
344
|
|
285
345
|
def change_column_type(table_name, column_name, type, options = {}) #:nodoc:
|
346
|
+
clear_cached_table(table_name)
|
286
347
|
sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
287
348
|
if options.has_key?(:null)
|
288
349
|
sql += (options[:null] ? " NULL" : " NOT NULL")
|
@@ -291,6 +352,7 @@ module ::ArJdbc
|
|
291
352
|
end
|
292
353
|
|
293
354
|
def change_column_default(table_name, column_name, default) #:nodoc:
|
355
|
+
clear_cached_table(table_name)
|
294
356
|
remove_default_constraint(table_name, column_name)
|
295
357
|
unless default.nil?
|
296
358
|
execute "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
|
@@ -298,12 +360,14 @@ module ::ArJdbc
|
|
298
360
|
end
|
299
361
|
|
300
362
|
def remove_column(table_name, column_name)
|
363
|
+
clear_cached_table(table_name)
|
301
364
|
remove_check_constraints(table_name, column_name)
|
302
365
|
remove_default_constraint(table_name, column_name)
|
303
366
|
execute "ALTER TABLE #{table_name} DROP COLUMN [#{column_name}]"
|
304
367
|
end
|
305
368
|
|
306
369
|
def remove_default_constraint(table_name, column_name)
|
370
|
+
clear_cached_table(table_name)
|
307
371
|
defaults = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
|
308
372
|
defaults.each {|constraint|
|
309
373
|
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
|
@@ -311,6 +375,7 @@ module ::ArJdbc
|
|
311
375
|
end
|
312
376
|
|
313
377
|
def remove_check_constraints(table_name, column_name)
|
378
|
+
clear_cached_table(table_name)
|
314
379
|
# TODO remove all constraints in single method
|
315
380
|
constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'"
|
316
381
|
constraints.each do |constraint|
|
@@ -323,17 +388,31 @@ module ::ArJdbc
|
|
323
388
|
end
|
324
389
|
|
325
390
|
def columns(table_name, name = nil)
|
391
|
+
# It's possible for table_name to be an empty string, or nil, if something attempts to issue SQL
|
392
|
+
# which doesn't involve a table. IE. "SELECT 1" or "SELECT * from someFunction()".
|
393
|
+
return [] if table_name.blank?
|
394
|
+
table_name = table_name.to_s if table_name.is_a?(Symbol)
|
395
|
+
|
396
|
+
# Remove []'s from around the table name, valid in a select statement, but not when matching metadata.
|
397
|
+
table_name = table_name.gsub(/[\[\]]/, '')
|
398
|
+
|
326
399
|
return [] if table_name =~ /^information_schema\./i
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
400
|
+
@table_columns = {} unless @table_columns
|
401
|
+
unless @table_columns[table_name]
|
402
|
+
@table_columns[table_name] = super
|
403
|
+
@table_columns[table_name].each do |col|
|
404
|
+
col.identity = true if col.sql_type =~ /identity/i
|
405
|
+
col.is_special = true if col.sql_type =~ /text|ntext|image|xml/i
|
406
|
+
end
|
331
407
|
end
|
332
|
-
|
408
|
+
@table_columns[table_name]
|
333
409
|
end
|
334
410
|
|
335
411
|
def _execute(sql, name = nil)
|
336
|
-
|
412
|
+
# Match the start of the sql to determine appropriate behaviour. Be aware of
|
413
|
+
# multi-line sql which might begin with 'create stored_proc' and contain 'insert into ...' lines.
|
414
|
+
# Possible improvements include ignoring comment blocks prior to the first statement.
|
415
|
+
if sql.lstrip =~ /\Ainsert/i
|
337
416
|
if query_requires_identity_insert?(sql)
|
338
417
|
table_name = get_table_name(sql)
|
339
418
|
with_identity_insert_enabled(table_name) do
|
@@ -342,9 +421,9 @@ module ::ArJdbc
|
|
342
421
|
else
|
343
422
|
@connection.execute_insert(sql)
|
344
423
|
end
|
345
|
-
elsif sql.lstrip =~
|
424
|
+
elsif sql.lstrip =~ /\A(create|exec)/i
|
346
425
|
@connection.execute_update(sql)
|
347
|
-
elsif sql.lstrip =~
|
426
|
+
elsif sql.lstrip =~ /\A\(?\s*(select|show)/i
|
348
427
|
repair_special_columns(sql)
|
349
428
|
@connection.execute_query(sql)
|
350
429
|
else
|
@@ -392,12 +471,9 @@ module ::ArJdbc
|
|
392
471
|
end
|
393
472
|
|
394
473
|
def identity_column(table_name)
|
395
|
-
|
396
|
-
@table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
|
397
|
-
@table_columns[table_name].each do |col|
|
474
|
+
columns(table_name).each do |col|
|
398
475
|
return col.name if col.identity
|
399
476
|
end
|
400
|
-
|
401
477
|
return nil
|
402
478
|
end
|
403
479
|
|
@@ -420,9 +496,7 @@ module ::ArJdbc
|
|
420
496
|
|
421
497
|
def get_special_columns(table_name)
|
422
498
|
special = []
|
423
|
-
|
424
|
-
@table_columns[table_name] ||= columns(table_name)
|
425
|
-
@table_columns[table_name].each do |col|
|
499
|
+
columns(table_name).each do |col|
|
426
500
|
special << col.name if col.is_special
|
427
501
|
end
|
428
502
|
special
|
@@ -449,7 +523,11 @@ module ::ArJdbc
|
|
449
523
|
# Look for an id column. Return it, without changing case, to cover dbs with a case-sensitive collation.
|
450
524
|
columns(table_name).each { |column| return column.name if column.name =~ /^id$/i }
|
451
525
|
# Give up and provide something which is going to crash almost certainly
|
452
|
-
|
526
|
+
columns(table_name)[0].name
|
527
|
+
end
|
528
|
+
|
529
|
+
def clear_cached_table(name)
|
530
|
+
(@table_columns ||= {}).delete(name)
|
453
531
|
end
|
454
532
|
end
|
455
533
|
end
|
@@ -4,9 +4,26 @@ class ActiveRecord::Base
|
|
4
4
|
require "arjdbc/mssql"
|
5
5
|
config[:host] ||= "localhost"
|
6
6
|
config[:port] ||= 1433
|
7
|
-
config[:url] ||= "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
|
8
7
|
config[:driver] ||= "net.sourceforge.jtds.jdbc.Driver"
|
9
|
-
|
8
|
+
|
9
|
+
url = "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
|
10
|
+
|
11
|
+
# Instance is often a preferrable alternative to port when dynamic ports are used.
|
12
|
+
# If instance is specified then port is essentially ignored.
|
13
|
+
url << ";instance=#{config[:instance]}" if config[:instance]
|
14
|
+
|
15
|
+
# This will enable windows domain-based authentication and will require the JTDS native libraries be available.
|
16
|
+
url << ";domain=#{config[:domain]}" if config[:domain]
|
17
|
+
|
18
|
+
# AppName is shown in sql server as additional information against the connection.
|
19
|
+
url << ";appname=#{config[:appname]}" if config[:appname]
|
20
|
+
config[:url] ||= url
|
21
|
+
|
22
|
+
if !config[:domain]
|
23
|
+
config[:username] ||= "sa"
|
24
|
+
config[:password] ||= ""
|
25
|
+
end
|
26
|
+
jdbc_connection(config)
|
10
27
|
end
|
11
28
|
alias_method :jdbcmssql_connection, :mssql_connection
|
12
29
|
end
|
@@ -10,6 +10,7 @@ module TSqlMethods
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
13
|
+
limit = nil if %w(text binary).include? type.to_s
|
13
14
|
return 'uniqueidentifier' if (type.to_s == 'uniqueidentifier')
|
14
15
|
return super unless type.to_s == 'integer'
|
15
16
|
|
data/lib/arjdbc/mysql/adapter.rb
CHANGED
@@ -58,6 +58,8 @@ module ::ArJdbc
|
|
58
58
|
when /^mediumint/i; 3
|
59
59
|
when /^smallint/i; 2
|
60
60
|
when /^tinyint/i; 1
|
61
|
+
when /^(datetime|timestamp)/
|
62
|
+
nil
|
61
63
|
else
|
62
64
|
super
|
63
65
|
end
|
@@ -92,6 +94,10 @@ module ::ArJdbc
|
|
92
94
|
"= BINARY"
|
93
95
|
end
|
94
96
|
|
97
|
+
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
98
|
+
where_sql
|
99
|
+
end
|
100
|
+
|
95
101
|
# QUOTING ==================================================
|
96
102
|
|
97
103
|
def quote(value, column = nil)
|
@@ -1,18 +1,18 @@
|
|
1
1
|
# Don't need to load native mysql adapter
|
2
2
|
$LOADED_FEATURES << "active_record/connection_adapters/mysql_adapter.rb"
|
3
|
+
$LOADED_FEATURES << "active_record/connection_adapters/mysql2_adapter.rb"
|
3
4
|
|
4
5
|
class ActiveRecord::Base
|
5
6
|
class << self
|
6
7
|
def mysql_connection(config)
|
7
8
|
require "arjdbc/mysql"
|
8
9
|
config[:port] ||= 3306
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
10
|
+
options = (config[:options] ||= {})
|
11
|
+
options['zeroDateTimeBehavior'] ||= 'convertToNull'
|
12
|
+
options['jdbcCompliantTruncation'] ||= 'false'
|
13
|
+
options['useUnicode'] ||= 'true'
|
14
|
+
options['characterEncoding'] = config[:encoding] || 'utf8'
|
15
|
+
config[:url] ||= "jdbc:mysql://#{config[:host]}:#{config[:port]}/#{config[:database]}"
|
16
16
|
config[:driver] ||= "com.mysql.jdbc.Driver"
|
17
17
|
config[:adapter_class] = ActiveRecord::ConnectionAdapters::MysqlAdapter
|
18
18
|
connection = jdbc_connection(config)
|
@@ -20,6 +20,7 @@ class ActiveRecord::Base
|
|
20
20
|
connection
|
21
21
|
end
|
22
22
|
alias_method :jdbcmysql_connection, :mysql_connection
|
23
|
+
alias_method :mysql2_connection, :mysql_connection
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
@@ -33,6 +33,10 @@ module ::ArJdbc
|
|
33
33
|
[/oracle/i, lambda {|cfg,col| col.extend(::ArJdbc::Oracle::Column)}]
|
34
34
|
end
|
35
35
|
|
36
|
+
def self.jdbc_connection_class
|
37
|
+
::ActiveRecord::ConnectionAdapters::OracleJdbcConnection
|
38
|
+
end
|
39
|
+
|
36
40
|
module Column
|
37
41
|
def primary=(val)
|
38
42
|
super
|
@@ -351,17 +355,15 @@ module ::ArJdbc
|
|
351
355
|
return value.to_i.to_s
|
352
356
|
end
|
353
357
|
quoted = super
|
354
|
-
if value.acts_like?(:date)
|
355
|
-
quoted =
|
358
|
+
if value.acts_like?(:date)
|
359
|
+
quoted = %Q{DATE'#{quoted_date(value)}'}
|
360
|
+
elsif value.acts_like?(:time)
|
361
|
+
quoted = %Q{TIMESTAMP'#{quoted_date(value)}'}
|
356
362
|
end
|
357
363
|
quoted
|
358
364
|
end
|
359
365
|
end
|
360
366
|
|
361
|
-
def quoted_date(value)
|
362
|
-
%Q{TIMESTAMP'#{super}'}
|
363
|
-
end
|
364
|
-
|
365
367
|
def quoted_true #:nodoc:
|
366
368
|
'1'
|
367
369
|
end
|
@@ -31,7 +31,7 @@ module ::ArJdbc
|
|
31
31
|
return :string if field_type =~ /\[\]$/i || field_type =~ /^interval/i
|
32
32
|
return :string if field_type =~ /^(?:point|lseg|box|"?path"?|polygon|circle)/i
|
33
33
|
return :datetime if field_type =~ /^timestamp/i
|
34
|
-
return :float if field_type =~ /^real
|
34
|
+
return :float if field_type =~ /^(?:real|double precision)$/i
|
35
35
|
return :binary if field_type =~ /^bytea/i
|
36
36
|
return :boolean if field_type =~ /^bool/i
|
37
37
|
super
|
@@ -72,6 +72,8 @@ module ::ArJdbc
|
|
72
72
|
tp[:string][:limit] = 255
|
73
73
|
tp[:integer][:limit] = nil
|
74
74
|
tp[:boolean][:limit] = nil
|
75
|
+
tp[:float] = { :name => "float" }
|
76
|
+
tp[:decimal] = { :name => "decimal" }
|
75
77
|
tp
|
76
78
|
end
|
77
79
|
|
@@ -124,6 +126,10 @@ module ::ArJdbc
|
|
124
126
|
true
|
125
127
|
end
|
126
128
|
|
129
|
+
def supports_count_distinct? #:nodoc:
|
130
|
+
false
|
131
|
+
end
|
132
|
+
|
127
133
|
def create_savepoint
|
128
134
|
execute("SAVEPOINT #{current_savepoint_name}")
|
129
135
|
end
|
@@ -139,7 +145,7 @@ module ::ArJdbc
|
|
139
145
|
# Returns the configured supported identifier length supported by PostgreSQL,
|
140
146
|
# or report the default of 63 on PostgreSQL 7.x.
|
141
147
|
def table_alias_length
|
142
|
-
@table_alias_length ||= (postgresql_version >= 80000 ?
|
148
|
+
@table_alias_length ||= (postgresql_version >= 80000 ? select_one('SHOW max_identifier_length')['max_identifier_length'].to_i : 63)
|
143
149
|
end
|
144
150
|
|
145
151
|
def default_sequence_name(table_name, pk = nil)
|
@@ -167,12 +173,6 @@ module ::ArJdbc
|
|
167
173
|
end
|
168
174
|
end
|
169
175
|
|
170
|
-
def quote_regclass(table_name)
|
171
|
-
table_name.to_s.split('.').map do |part|
|
172
|
-
part =~ /".*"/i ? part : quote_table_name(part)
|
173
|
-
end.join('.')
|
174
|
-
end
|
175
|
-
|
176
176
|
# Find a table's primary key and sequence.
|
177
177
|
def pk_and_sequence_for(table) #:nodoc:
|
178
178
|
# First try looking for a sequence with a dependency on the
|
@@ -191,7 +191,7 @@ module ::ArJdbc
|
|
191
191
|
AND attr.attrelid = cons.conrelid
|
192
192
|
AND attr.attnum = cons.conkey[1]
|
193
193
|
AND cons.contype = 'p'
|
194
|
-
AND dep.refobjid = '#{table}'::regclass
|
194
|
+
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
195
195
|
end_sql
|
196
196
|
|
197
197
|
if result.nil? or result.empty?
|
@@ -210,7 +210,7 @@ module ::ArJdbc
|
|
210
210
|
JOIN pg_attribute attr ON (t.oid = attrelid)
|
211
211
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
212
212
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
213
|
-
WHERE t.oid = '#{table}'::regclass
|
213
|
+
WHERE t.oid = '#{quote_table_name(table)}'::regclass
|
214
214
|
AND cons.contype = 'p'
|
215
215
|
AND def.adsrc ~* 'nextval'
|
216
216
|
end_sql
|
@@ -313,7 +313,7 @@ module ::ArJdbc
|
|
313
313
|
end
|
314
314
|
|
315
315
|
def drop_database(name)
|
316
|
-
execute "DROP DATABASE \"#{name}\""
|
316
|
+
execute "DROP DATABASE IF EXISTS \"#{name}\""
|
317
317
|
end
|
318
318
|
|
319
319
|
def create_schema(schema_name, pg_username)
|
@@ -396,13 +396,23 @@ module ::ArJdbc
|
|
396
396
|
sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
|
397
397
|
end
|
398
398
|
|
399
|
-
def quote(value, column = nil)
|
400
|
-
return
|
399
|
+
def quote(value, column = nil) #:nodoc:
|
400
|
+
return super unless column
|
401
401
|
|
402
|
-
if value.kind_of?(String) && column
|
402
|
+
if value.kind_of?(String) && column.type == :binary
|
403
403
|
"'#{escape_bytea(value)}'"
|
404
|
-
elsif
|
405
|
-
|
404
|
+
elsif value.kind_of?(String) && column.sql_type == 'xml'
|
405
|
+
"xml '#{quote_string(value)}'"
|
406
|
+
elsif value.kind_of?(Numeric) && column.sql_type == 'money'
|
407
|
+
# Not truly string input, so doesn't require (or allow) escape string syntax.
|
408
|
+
"'#{value}'"
|
409
|
+
elsif value.kind_of?(String) && column.sql_type =~ /^bit/
|
410
|
+
case value
|
411
|
+
when /^[01]*$/
|
412
|
+
"B'#{value}'" # Bit-string notation
|
413
|
+
when /^[0-9A-F]*$/i
|
414
|
+
"X'#{value}'" # Hexadecimal notation
|
415
|
+
end
|
406
416
|
else
|
407
417
|
super
|
408
418
|
end
|
@@ -416,6 +426,17 @@ module ::ArJdbc
|
|
416
426
|
end
|
417
427
|
end
|
418
428
|
|
429
|
+
def quote_table_name(name)
|
430
|
+
schema, name_part = extract_pg_identifier_from_name(name.to_s)
|
431
|
+
|
432
|
+
unless name_part
|
433
|
+
quote_column_name(schema)
|
434
|
+
else
|
435
|
+
table_name, name_part = extract_pg_identifier_from_name(name_part)
|
436
|
+
"#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
419
440
|
def quote_column_name(name)
|
420
441
|
%("#{name}")
|
421
442
|
end
|
@@ -440,7 +461,7 @@ module ::ArJdbc
|
|
440
461
|
end
|
441
462
|
|
442
463
|
def add_column(table_name, column_name, type, options = {})
|
443
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}")
|
464
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}")
|
444
465
|
change_column_default(table_name, column_name, options[:default]) unless options[:default].nil?
|
445
466
|
if options[:null] == false
|
446
467
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)} = '#{options[:default]}'") if options[:default]
|
@@ -450,12 +471,12 @@ module ::ArJdbc
|
|
450
471
|
|
451
472
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
452
473
|
begin
|
453
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit])}"
|
474
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
454
475
|
rescue ActiveRecord::StatementInvalid
|
455
476
|
# This is PG7, so we use a more arcane way of doing it.
|
456
477
|
begin_db_transaction
|
457
478
|
add_column(table_name, "#{column_name}_ar_tmp", type, options)
|
458
|
-
execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit])})"
|
479
|
+
execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"
|
459
480
|
remove_column(table_name, column_name)
|
460
481
|
rename_column(table_name, "#{column_name}_ar_tmp", column_name)
|
461
482
|
commit_db_transaction
|
@@ -497,6 +518,17 @@ module ::ArJdbc
|
|
497
518
|
def tables
|
498
519
|
@connection.tables(database_name, nil, nil, ["TABLE"])
|
499
520
|
end
|
521
|
+
|
522
|
+
private
|
523
|
+
def extract_pg_identifier_from_name(name)
|
524
|
+
match_data = name[0,1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
|
525
|
+
|
526
|
+
if match_data
|
527
|
+
rest = name[match_data[0].length..-1]
|
528
|
+
rest = rest[1..-1] if rest[0,1] == "."
|
529
|
+
[match_data[1], (rest.length > 0 ? rest : nil)]
|
530
|
+
end
|
531
|
+
end
|
500
532
|
end
|
501
533
|
end
|
502
534
|
|