activerecord-jdbc-adapter 1.0.0.beta1-java → 1.0.0.beta2-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|