activerecord-jdbc-adapter 0.9.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +163 -0
- data/Manifest.txt +130 -65
- data/README.txt +64 -72
- data/lib/active_record/connection_adapters/derby_adapter.rb +1 -13
- data/lib/active_record/connection_adapters/h2_adapter.rb +1 -13
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -13
- data/lib/active_record/connection_adapters/informix_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -633
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -13
- data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -13
- data/lib/activerecord-jdbc-adapter.rb +6 -0
- data/lib/arel/engines/sql/compilers/db2_compiler.rb +9 -0
- data/lib/arel/engines/sql/compilers/derby_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/h2_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +15 -0
- data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +6 -0
- data/lib/arel/engines/sql/compilers/mssql_compiler.rb +46 -0
- data/lib/arel/visitors/db2.rb +15 -0
- data/lib/arel/visitors/derby.rb +19 -0
- data/lib/arel/visitors/hsqldb.rb +24 -0
- data/lib/arel/visitors/mssql.rb +44 -0
- data/lib/arjdbc.rb +29 -0
- data/lib/arjdbc/db2.rb +2 -0
- data/lib/arjdbc/db2/adapter.rb +413 -0
- data/lib/arjdbc/derby.rb +7 -0
- data/lib/{jdbc_adapter/jdbc_derby.rb → arjdbc/derby/adapter.rb} +58 -132
- data/lib/arjdbc/derby/connection_methods.rb +18 -0
- data/lib/arjdbc/discover.rb +92 -0
- data/lib/arjdbc/firebird.rb +2 -0
- data/lib/{jdbc_adapter/jdbc_firebird.rb → arjdbc/firebird/adapter.rb} +36 -14
- data/lib/arjdbc/h2.rb +4 -0
- data/lib/arjdbc/h2/adapter.rb +36 -0
- data/lib/arjdbc/h2/connection_methods.rb +12 -0
- data/lib/arjdbc/hsqldb.rb +4 -0
- data/lib/{jdbc_adapter/jdbc_hsqldb.rb → arjdbc/hsqldb/adapter.rb} +34 -62
- data/lib/arjdbc/hsqldb/connection_methods.rb +14 -0
- data/lib/arjdbc/informix.rb +3 -0
- data/lib/{jdbc_adapter/jdbc_informix.rb → arjdbc/informix/adapter.rb} +10 -19
- data/lib/arjdbc/informix/connection_methods.rb +10 -0
- data/lib/arjdbc/jdbc.rb +2 -0
- data/lib/arjdbc/jdbc/adapter.rb +285 -0
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/callbacks.rb +44 -0
- data/lib/arjdbc/jdbc/column.rb +38 -0
- data/lib/arjdbc/jdbc/compatibility.rb +51 -0
- data/lib/arjdbc/jdbc/connection.rb +128 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +16 -0
- data/lib/arjdbc/jdbc/core_ext.rb +24 -0
- data/lib/arjdbc/jdbc/discover.rb +18 -0
- data/lib/arjdbc/jdbc/driver.rb +44 -0
- data/lib/arjdbc/jdbc/extension.rb +47 -0
- data/lib/arjdbc/jdbc/java.rb +14 -0
- data/lib/{jdbc_adapter → arjdbc/jdbc}/jdbc.rake +36 -26
- data/lib/{jdbc_adapter → arjdbc/jdbc}/missing_functionality_helper.rb +5 -5
- data/lib/arjdbc/jdbc/quoted_primary_key.rb +28 -0
- data/lib/arjdbc/jdbc/railtie.rb +9 -0
- data/lib/arjdbc/jdbc/rake_tasks.rb +10 -0
- data/lib/arjdbc/jdbc/require_driver.rb +16 -0
- data/lib/arjdbc/jdbc/type_converter.rb +126 -0
- data/lib/arjdbc/mimer.rb +2 -0
- data/lib/{jdbc_adapter/jdbc_mimer.rb → arjdbc/mimer/adapter.rb} +17 -16
- data/lib/arjdbc/mssql.rb +4 -0
- data/lib/arjdbc/mssql/adapter.rb +472 -0
- data/lib/arjdbc/mssql/connection_methods.rb +30 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +92 -0
- data/lib/{jdbc_adapter → arjdbc/mssql}/tsql_helper.rb +2 -1
- data/lib/arjdbc/mysql.rb +4 -0
- data/lib/arjdbc/mysql/adapter.rb +416 -0
- data/lib/arjdbc/mysql/connection_methods.rb +27 -0
- data/lib/arjdbc/oracle.rb +3 -0
- data/lib/{jdbc_adapter/jdbc_oracle.rb → arjdbc/oracle/adapter.rb} +68 -33
- data/lib/arjdbc/oracle/connection_methods.rb +11 -0
- data/lib/arjdbc/postgresql.rb +4 -0
- data/lib/{jdbc_adapter/jdbc_postgre.rb → arjdbc/postgresql/adapter.rb} +147 -72
- data/lib/arjdbc/postgresql/connection_methods.rb +21 -0
- data/lib/arjdbc/sqlite3.rb +4 -0
- data/lib/arjdbc/sqlite3/adapter.rb +381 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +34 -0
- data/lib/arjdbc/sybase.rb +2 -0
- data/lib/arjdbc/sybase/adapter.rb +46 -0
- data/lib/arjdbc/version.rb +8 -0
- data/lib/generators/jdbc/jdbc_generator.rb +9 -0
- data/lib/jdbc_adapter.rb +2 -27
- data/lib/jdbc_adapter/rake_tasks.rb +3 -10
- data/lib/jdbc_adapter/version.rb +3 -5
- data/lib/pg.rb +23 -1
- data/rails_generators/jdbc_generator.rb +2 -2
- data/rails_generators/templates/{jdbc.rb → config/initializers/jdbc.rb} +1 -1
- data/rails_generators/templates/{jdbc.rake → lib/tasks/jdbc.rake} +2 -2
- data/rakelib/compile.rake +5 -3
- data/rakelib/db.rake +19 -0
- data/rakelib/package.rake +12 -6
- data/rakelib/test.rake +24 -14
- data/src/java/{jdbc_adapter/JdbcDerbySpec.java → arjdbc/derby/DerbyModule.java} +63 -34
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +70 -0
- data/src/java/{jdbc_adapter/MssqlRubyJdbcConnection.java → arjdbc/informix/InformixRubyJdbcConnection.java} +15 -12
- data/src/java/{jdbc_adapter/JdbcAdapterInternalService.java → arjdbc/jdbc/AdapterJavaService.java} +21 -8
- data/src/java/{jdbc_adapter → arjdbc/jdbc}/JdbcConnectionFactory.java +6 -6
- data/src/java/{jdbc_adapter → arjdbc/jdbc}/RubyJdbcConnection.java +210 -67
- data/src/java/arjdbc/jdbc/SQLBlock.java +48 -0
- data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +127 -0
- data/src/java/{jdbc_adapter/JdbcMySQLSpec.java → arjdbc/mysql/MySQLModule.java} +12 -12
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +87 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +85 -0
- data/src/java/{jdbc_adapter/PostgresRubyJdbcConnection.java → arjdbc/postgresql/PostgresqlRubyJdbcConnection.java} +11 -9
- data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +64 -0
- data/test/abstract_db_create.rb +98 -19
- data/test/activerecord/connection_adapters/type_conversion_test.rb +1 -1
- data/test/db/db2.rb +4 -2
- data/test/db/derby.rb +12 -14
- data/test/db/hsqldb.rb +3 -2
- data/test/db/jndi_config.rb +22 -12
- data/test/db/mssql.rb +5 -5
- data/test/db/sqlite3.rb +2 -6
- data/test/db2_simple_test.rb +56 -0
- data/test/derby_migration_test.rb +50 -3
- data/test/derby_simple_test.rb +87 -0
- data/test/generic_jdbc_connection_test.rb +21 -1
- data/test/h2_simple_test.rb +35 -0
- data/test/jdbc_common.rb +4 -5
- data/test/jndi_callbacks_test.rb +3 -1
- data/test/jndi_test.rb +1 -11
- data/test/models/entry.rb +20 -0
- data/test/models/mixed_case.rb +7 -2
- data/test/models/string_id.rb +18 -0
- data/test/mssql_db_create_test.rb +26 -0
- data/test/mssql_identity_insert_test.rb +19 -0
- data/test/mssql_legacy_types_test.rb +58 -0
- data/test/mssql_limit_offset_test.rb +136 -0
- data/test/mssql_multibyte_test.rb +18 -0
- data/test/mssql_simple_test.rb +49 -0
- data/test/mysql_db_create_test.rb +8 -6
- data/test/mysql_info_test.rb +123 -0
- data/test/mysql_simple_test.rb +19 -1
- data/test/oracle_simple_test.rb +9 -20
- data/test/oracle_specific_test.rb +83 -0
- data/test/pick_rails_version.rb +3 -0
- data/test/postgres_db_create_test.rb +11 -0
- data/test/postgres_drop_db_test.rb +16 -0
- data/test/postgres_mixed_case_test.rb +11 -1
- data/test/postgres_nonseq_pkey_test.rb +0 -2
- data/test/postgres_schema_search_path_test.rb +44 -0
- data/test/postgres_simple_test.rb +53 -0
- data/test/postgres_table_alias_length_test.rb +15 -0
- data/test/simple.rb +103 -9
- data/test/sqlite3_simple_test.rb +1 -0
- data/test/sybase_jtds_simple_test.rb +22 -0
- metadata +239 -137
- data/lib/active_record/connection_adapters/cachedb_adapter.rb +0 -1
- data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +0 -26
- data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
- data/lib/jdbc_adapter/jdbc_cachedb.rb +0 -33
- data/lib/jdbc_adapter/jdbc_db2.rb +0 -191
- data/lib/jdbc_adapter/jdbc_mssql.rb +0 -333
- data/lib/jdbc_adapter/jdbc_mysql.rb +0 -236
- data/lib/jdbc_adapter/jdbc_sqlite3.rb +0 -379
- data/lib/jdbc_adapter/jdbc_sybase.rb +0 -50
- data/src/java/jdbc_adapter/SQLBlock.java +0 -27
- data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +0 -41
- data/test/cachedb_simple_test.rb +0 -6
- data/test/db/cachedb.rb +0 -9
- data/test/jdbc_adapter/jdbc_db2_test.rb +0 -26
- data/test/jdbc_adapter/jdbc_sybase_test.rb +0 -33
- data/test/minirunit.rb +0 -109
- data/test/minirunit/testConnect.rb +0 -14
- data/test/minirunit/testH2.rb +0 -73
- data/test/minirunit/testHsqldb.rb +0 -73
- data/test/minirunit/testLoadActiveRecord.rb +0 -3
- data/test/minirunit/testMysql.rb +0 -83
- data/test/minirunit/testRawSelect.rb +0 -24
@@ -0,0 +1,27 @@
|
|
1
|
+
# Don't need to load native mysql adapter
|
2
|
+
$LOADED_FEATURES << "active_record/connection_adapters/mysql_adapter.rb"
|
3
|
+
$LOADED_FEATURES << "active_record/connection_adapters/mysql2_adapter.rb"
|
4
|
+
|
5
|
+
class ActiveRecord::Base
|
6
|
+
class << self
|
7
|
+
def mysql_connection(config)
|
8
|
+
require "arjdbc/mysql"
|
9
|
+
config[:port] ||= 3306
|
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
|
+
config[:driver] ||= "com.mysql.jdbc.Driver"
|
17
|
+
config[:adapter_class] = ActiveRecord::ConnectionAdapters::MysqlAdapter
|
18
|
+
connection = jdbc_connection(config)
|
19
|
+
::ArJdbc::MySQL.kill_cancel_timer(connection.raw_connection)
|
20
|
+
connection
|
21
|
+
end
|
22
|
+
alias_method :jdbcmysql_connection, :mysql_connection
|
23
|
+
alias_method :mysql2_connection, :mysql_connection
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
@@ -1,13 +1,8 @@
|
|
1
|
-
module ::
|
2
|
-
|
3
|
-
|
4
|
-
config[:port] ||= 1521
|
5
|
-
config[:url] ||= "jdbc:oracle:thin:@#{config[:host]}:#{config[:port]}:#{config[:database]}"
|
6
|
-
config[:driver] ||= "oracle.jdbc.driver.OracleDriver"
|
7
|
-
jdbc_connection(config)
|
8
|
-
end
|
9
|
-
end
|
1
|
+
module ActiveRecord::ConnectionAdapters
|
2
|
+
OracleAdapter = Class.new(AbstractAdapter) unless const_defined?(:OracleAdapter)
|
3
|
+
end
|
10
4
|
|
5
|
+
module ::ArJdbc
|
11
6
|
module Oracle
|
12
7
|
def self.extended(mod)
|
13
8
|
unless @lob_callback_added
|
@@ -26,24 +21,34 @@ module ::JdbcSpec
|
|
26
21
|
ActiveRecord::Base.after_save :after_save_with_oracle_lob
|
27
22
|
@lob_callback_added = true
|
28
23
|
end
|
29
|
-
|
30
|
-
|
24
|
+
require 'arjdbc/jdbc/quoted_primary_key'
|
25
|
+
ActiveRecord::Base.extend ArJdbc::QuotedPrimaryKeyExtension
|
26
|
+
mod.class.class_eval do
|
27
|
+
alias_chained_method :insert, :query_dirty, :insert
|
28
|
+
alias_chained_method :columns, :query_cache, :columns
|
31
29
|
end
|
32
30
|
end
|
33
31
|
|
34
|
-
def self.
|
35
|
-
|
32
|
+
def self.column_selector
|
33
|
+
[/oracle/i, lambda {|cfg,col| col.extend(::ArJdbc::Oracle::Column)}]
|
36
34
|
end
|
37
35
|
|
38
|
-
def self.
|
39
|
-
|
36
|
+
def self.jdbc_connection_class
|
37
|
+
::ActiveRecord::ConnectionAdapters::OracleJdbcConnection
|
40
38
|
end
|
41
39
|
|
42
40
|
module Column
|
41
|
+
def primary=(val)
|
42
|
+
super
|
43
|
+
if val && @sql_type =~ /^NUMBER$/i
|
44
|
+
@type = :integer
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
43
48
|
def type_cast(value)
|
44
49
|
return nil if value.nil?
|
45
50
|
case type
|
46
|
-
when :datetime then
|
51
|
+
when :datetime then ArJdbc::Oracle::Column.string_to_time(value, self.class)
|
47
52
|
else
|
48
53
|
super
|
49
54
|
end
|
@@ -51,7 +56,7 @@ module ::JdbcSpec
|
|
51
56
|
|
52
57
|
def type_cast_code(var_name)
|
53
58
|
case type
|
54
|
-
when :datetime then "
|
59
|
+
when :datetime then "ArJdbc::Oracle::Column.string_to_time(#{var_name}, self.class)"
|
55
60
|
else
|
56
61
|
super
|
57
62
|
end
|
@@ -63,8 +68,8 @@ module ::JdbcSpec
|
|
63
68
|
end
|
64
69
|
|
65
70
|
def self.guess_date_or_time(value)
|
66
|
-
(value.hour == 0 && value.min == 0 && value.sec == 0) ?
|
67
|
-
|
71
|
+
(value && value.hour == 0 && value.min == 0 && value.sec == 0) ?
|
72
|
+
Date.new(value.year, value.month, value.day) : value
|
68
73
|
end
|
69
74
|
|
70
75
|
private
|
@@ -74,7 +79,7 @@ module ::JdbcSpec
|
|
74
79
|
when /char/i then :string
|
75
80
|
when /float|double/i then :float
|
76
81
|
when /int/i then :integer
|
77
|
-
when /num|dec|real/i then
|
82
|
+
when /num|dec|real/i then extract_scale(field_type) == 0 ? :integer : :decimal
|
78
83
|
when /date|time/i then :datetime
|
79
84
|
when /clob/i then :text
|
80
85
|
when /blob/i then :binary
|
@@ -101,9 +106,18 @@ module ::JdbcSpec
|
|
101
106
|
end
|
102
107
|
|
103
108
|
def adapter_name
|
104
|
-
'
|
109
|
+
'Oracle'
|
105
110
|
end
|
106
111
|
|
112
|
+
def arel2_visitors
|
113
|
+
{ 'oracle' => Arel::Visitors::Oracle }
|
114
|
+
end
|
115
|
+
|
116
|
+
# TODO: use this instead of the QuotedPrimaryKey logic and execute_id_insert?
|
117
|
+
# def prefetch_primary_key?(table_name = nil)
|
118
|
+
# columns(table_name).detect {|c| c.primary } if table_name
|
119
|
+
# end
|
120
|
+
|
107
121
|
def table_alias_length
|
108
122
|
30
|
109
123
|
end
|
@@ -115,8 +129,9 @@ module ::JdbcSpec
|
|
115
129
|
def create_table(name, options = {}) #:nodoc:
|
116
130
|
super(name, options)
|
117
131
|
seq_name = options[:sequence_name] || "#{name}_seq"
|
132
|
+
start_value = options[:sequence_start_value] || 10000
|
118
133
|
raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
|
119
|
-
execute "CREATE SEQUENCE #{seq_name} START WITH
|
134
|
+
execute "CREATE SEQUENCE #{seq_name} START WITH #{start_value}" unless options[:id] == false
|
120
135
|
end
|
121
136
|
|
122
137
|
def rename_table(name, new_name) #:nodoc:
|
@@ -138,14 +153,27 @@ module ::JdbcSpec
|
|
138
153
|
recreate_database(name)
|
139
154
|
end
|
140
155
|
|
141
|
-
def
|
142
|
-
|
156
|
+
def next_sequence_value(sequence_name)
|
157
|
+
# avoid #select or #select_one so that the sequence values aren't cached
|
158
|
+
execute("select #{sequence_name}.nextval id from dual").first['id'].to_i
|
159
|
+
end
|
160
|
+
|
161
|
+
def sql_literal?(value)
|
162
|
+
defined?(::Arel::SqlLiteral) && ::Arel::SqlLiteral === value
|
163
|
+
end
|
164
|
+
|
165
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
166
|
+
if (id_value && !sql_literal?(id_value)) || pk.nil?
|
167
|
+
# Pre-assigned id or table without a primary key
|
168
|
+
# Presence of #to_sql means an Arel literal bind variable
|
169
|
+
# that should use #execute_id_insert below
|
143
170
|
execute sql, name
|
144
|
-
else
|
171
|
+
else
|
172
|
+
# Assume the sql contains a bind-variable for the id
|
145
173
|
# Extract the table from the insert sql. Yuck.
|
146
174
|
table = sql.split(" ", 4)[2].gsub('"', '')
|
147
175
|
sequence_name ||= default_sequence_name(table)
|
148
|
-
id_value =
|
176
|
+
id_value = next_sequence_value(sequence_name)
|
149
177
|
log(sql, name) do
|
150
178
|
@connection.execute_id_insert(sql,id_value)
|
151
179
|
end
|
@@ -313,6 +341,12 @@ module ::JdbcSpec
|
|
313
341
|
#
|
314
342
|
# see: abstract/quoting.rb
|
315
343
|
|
344
|
+
# See ACTIVERECORD_JDBC-33 for details -- better to not quote
|
345
|
+
# table names, esp. if they have schemas.
|
346
|
+
def quote_table_name(name) #:nodoc:
|
347
|
+
name.to_s
|
348
|
+
end
|
349
|
+
|
316
350
|
# Camelcase column names need to be quoted.
|
317
351
|
# Nonquoted identifiers can contain only alphanumeric characters from your
|
318
352
|
# database character set and the underscore (_), dollar sign ($), and pound sign (#).
|
@@ -328,6 +362,9 @@ module ::JdbcSpec
|
|
328
362
|
end
|
329
363
|
|
330
364
|
def quote(value, column = nil) #:nodoc:
|
365
|
+
# Arel 2 passes SqlLiterals through
|
366
|
+
return value if sql_literal?(value)
|
367
|
+
|
331
368
|
if column && [:text, :binary].include?(column.type)
|
332
369
|
if /(.*?)\([0-9]+\)/ =~ column.sql_type
|
333
370
|
%Q{empty_#{ $1.downcase }()}
|
@@ -335,21 +372,19 @@ module ::JdbcSpec
|
|
335
372
|
%Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
|
336
373
|
end
|
337
374
|
else
|
338
|
-
if column.respond_to?(:primary) && column.primary
|
375
|
+
if column.respond_to?(:primary) && column.primary && column.klass != String
|
339
376
|
return value.to_i.to_s
|
340
377
|
end
|
341
378
|
quoted = super
|
342
|
-
if value.acts_like?(:date)
|
343
|
-
quoted =
|
379
|
+
if value.acts_like?(:date)
|
380
|
+
quoted = %Q{DATE'#{quoted_date(value)}'}
|
381
|
+
elsif value.acts_like?(:time)
|
382
|
+
quoted = %Q{TIMESTAMP'#{quoted_date(value)}'}
|
344
383
|
end
|
345
384
|
quoted
|
346
385
|
end
|
347
386
|
end
|
348
387
|
|
349
|
-
def quoted_date(value)
|
350
|
-
%Q{TIMESTAMP'#{value.strftime("%Y-%m-%d %H:%M:%S")}'}
|
351
|
-
end
|
352
|
-
|
353
388
|
def quoted_true #:nodoc:
|
354
389
|
'1'
|
355
390
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class ActiveRecord::Base
|
2
|
+
class << self
|
3
|
+
def oracle_connection(config)
|
4
|
+
config[:port] ||= 1521
|
5
|
+
config[:url] ||= "jdbc:oracle:thin:@#{config[:host]}:#{config[:port]}:#{config[:database]}"
|
6
|
+
config[:driver] ||= "oracle.jdbc.driver.OracleDriver"
|
7
|
+
jdbc_connection(config)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
@@ -1,29 +1,17 @@
|
|
1
|
+
module ActiveRecord::ConnectionAdapters
|
2
|
+
PostgreSQLAdapter = Class.new(AbstractAdapter) unless const_defined?(:PostgreSQLAdapter)
|
3
|
+
end
|
1
4
|
|
2
|
-
module ::
|
3
|
-
# Don't need to load native postgres adapter
|
4
|
-
$LOADED_FEATURES << "active_record/connection_adapters/postgresql_adapter.rb"
|
5
|
-
|
6
|
-
module ActiveRecordExtensions
|
7
|
-
add_method_to_remove_from_ar_base(:postgresql_connection)
|
8
|
-
|
9
|
-
def postgresql_connection(config)
|
10
|
-
require File.dirname(__FILE__) + "/../active_record/connection_adapters/postgresql_adapter"
|
11
|
-
config[:host] ||= "localhost"
|
12
|
-
config[:port] ||= 5432
|
13
|
-
config[:url] ||= "jdbc:postgresql://#{config[:host]}:#{config[:port]}/#{config[:database]}"
|
14
|
-
config[:url] << config[:pg_params] if config[:pg_params]
|
15
|
-
config[:driver] ||= "org.postgresql.Driver"
|
16
|
-
jdbc_connection(config)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
5
|
+
module ::ArJdbc
|
20
6
|
module PostgreSQL
|
21
|
-
def self.
|
22
|
-
|
7
|
+
def self.extended(mod)
|
8
|
+
mod.class.class_eval do
|
9
|
+
alias_chained_method :insert, :query_dirty, :insert
|
10
|
+
end
|
23
11
|
end
|
24
12
|
|
25
13
|
def self.column_selector
|
26
|
-
[/postgre/i, lambda {|cfg,col| col.extend(::
|
14
|
+
[/postgre/i, lambda {|cfg,col| col.extend(::ArJdbc::PostgreSQL::Column)}]
|
27
15
|
end
|
28
16
|
|
29
17
|
def self.jdbc_connection_class
|
@@ -38,12 +26,21 @@ module ::JdbcSpec
|
|
38
26
|
end
|
39
27
|
end
|
40
28
|
|
29
|
+
def extract_limit(sql_type)
|
30
|
+
case sql_type
|
31
|
+
when /^bigint/i; 8
|
32
|
+
when /^smallint/i; 2
|
33
|
+
when /^(bool|text|date|time)/i; nil # ACTIVERECORD_JDBC-135,139
|
34
|
+
else super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
41
38
|
def simplified_type(field_type)
|
42
39
|
return :integer if field_type =~ /^serial/i
|
43
40
|
return :string if field_type =~ /\[\]$/i || field_type =~ /^interval/i
|
44
41
|
return :string if field_type =~ /^(?:point|lseg|box|"?path"?|polygon|circle)/i
|
45
42
|
return :datetime if field_type =~ /^timestamp/i
|
46
|
-
return :float if field_type =~ /^real
|
43
|
+
return :float if field_type =~ /^(?:real|double precision)$/i
|
47
44
|
return :binary if field_type =~ /^bytea/i
|
48
45
|
return :boolean if field_type =~ /^bool/i
|
49
46
|
super
|
@@ -83,10 +80,25 @@ module ::JdbcSpec
|
|
83
80
|
tp[:primary_key] = "serial primary key"
|
84
81
|
tp[:string][:limit] = 255
|
85
82
|
tp[:integer][:limit] = nil
|
86
|
-
tp[:boolean]
|
83
|
+
tp[:boolean] = { :name => "boolean" }
|
84
|
+
tp[:float] = { :name => "float" }
|
85
|
+
tp[:text] = { :name => "text" }
|
86
|
+
tp[:datetime] = { :name => "timestamp" }
|
87
|
+
tp[:timestamp] = { :name => "timestamp" }
|
88
|
+
tp[:time] = { :name => "time" }
|
89
|
+
tp[:date] = { :name => "date" }
|
90
|
+
tp[:decimal] = { :name => "decimal" }
|
87
91
|
tp
|
88
92
|
end
|
89
93
|
|
94
|
+
def adapter_name #:nodoc:
|
95
|
+
'PostgreSQL'
|
96
|
+
end
|
97
|
+
|
98
|
+
def arel2_visitors
|
99
|
+
{'jdbcpostgresql' => ::Arel::Visitors::PostgreSQL}
|
100
|
+
end
|
101
|
+
|
90
102
|
def postgresql_version
|
91
103
|
@postgresql_version ||=
|
92
104
|
begin
|
@@ -132,10 +144,26 @@ module ::JdbcSpec
|
|
132
144
|
true
|
133
145
|
end
|
134
146
|
|
147
|
+
def supports_count_distinct? #:nodoc:
|
148
|
+
false
|
149
|
+
end
|
150
|
+
|
151
|
+
def create_savepoint
|
152
|
+
execute("SAVEPOINT #{current_savepoint_name}")
|
153
|
+
end
|
154
|
+
|
155
|
+
def rollback_to_savepoint
|
156
|
+
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
|
157
|
+
end
|
158
|
+
|
159
|
+
def release_savepoint
|
160
|
+
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
161
|
+
end
|
162
|
+
|
135
163
|
# Returns the configured supported identifier length supported by PostgreSQL,
|
136
164
|
# or report the default of 63 on PostgreSQL 7.x.
|
137
165
|
def table_alias_length
|
138
|
-
@table_alias_length ||= (postgresql_version >= 80000 ?
|
166
|
+
@table_alias_length ||= (postgresql_version >= 80000 ? select_one('SHOW max_identifier_length')['max_identifier_length'].to_i : 63)
|
139
167
|
end
|
140
168
|
|
141
169
|
def default_sequence_name(table_name, pk = nil)
|
@@ -163,12 +191,6 @@ module ::JdbcSpec
|
|
163
191
|
end
|
164
192
|
end
|
165
193
|
|
166
|
-
def quote_regclass(table_name)
|
167
|
-
table_name.to_s.split('.').map do |part|
|
168
|
-
part =~ /".*"/i ? part : quote_table_name(part)
|
169
|
-
end.join('.')
|
170
|
-
end
|
171
|
-
|
172
194
|
# Find a table's primary key and sequence.
|
173
195
|
def pk_and_sequence_for(table) #:nodoc:
|
174
196
|
# First try looking for a sequence with a dependency on the
|
@@ -187,14 +209,14 @@ module ::JdbcSpec
|
|
187
209
|
AND attr.attrelid = cons.conrelid
|
188
210
|
AND attr.attnum = cons.conkey[1]
|
189
211
|
AND cons.contype = 'p'
|
190
|
-
AND dep.refobjid = '#{table}'::regclass
|
212
|
+
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
191
213
|
end_sql
|
192
214
|
|
193
215
|
if result.nil? or result.empty?
|
194
216
|
# If that fails, try parsing the primary key's default value.
|
195
217
|
# Support the 7.x and 8.0 nextval('foo'::text) as well as
|
196
218
|
# the 8.1+ nextval('foo'::regclass).
|
197
|
-
result =
|
219
|
+
result = select(<<-end_sql, 'PK and custom sequence')[0]
|
198
220
|
SELECT attr.attname,
|
199
221
|
CASE
|
200
222
|
WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
|
@@ -206,7 +228,7 @@ module ::JdbcSpec
|
|
206
228
|
JOIN pg_attribute attr ON (t.oid = attrelid)
|
207
229
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
208
230
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
209
|
-
WHERE t.oid = '#{table}'::regclass
|
231
|
+
WHERE t.oid = '#{quote_table_name(table)}'::regclass
|
210
232
|
AND cons.contype = 'p'
|
211
233
|
AND def.adsrc ~* 'nextval'
|
212
234
|
end_sql
|
@@ -222,10 +244,7 @@ module ::JdbcSpec
|
|
222
244
|
table = sql.split(" ", 4)[2].gsub('"', '')
|
223
245
|
|
224
246
|
# Try an insert with 'returning id' if available (PG >= 8.2)
|
225
|
-
if supports_insert_with_returning? && id_value.nil?
|
226
|
-
# Disabled, as it causes:
|
227
|
-
# ActiveRecord::ActiveRecordError: A result was returned when none was expected
|
228
|
-
# This was previously disabled because postgresql_version returned 0
|
247
|
+
if supports_insert_with_returning? && id_value.nil?
|
229
248
|
pk, sequence_name = *pk_and_sequence_for(table) unless pk
|
230
249
|
if pk
|
231
250
|
id_value = select_value("#{sql} RETURNING #{quote_column_name(pk)}")
|
@@ -255,7 +274,7 @@ module ::JdbcSpec
|
|
255
274
|
end
|
256
275
|
|
257
276
|
def columns(table_name, name=nil)
|
258
|
-
schema_name =
|
277
|
+
schema_name = @config[:schema_search_path]
|
259
278
|
if table_name =~ /\./
|
260
279
|
parts = table_name.split(/\./)
|
261
280
|
table_name = parts.pop
|
@@ -312,7 +331,24 @@ module ::JdbcSpec
|
|
312
331
|
end
|
313
332
|
|
314
333
|
def drop_database(name)
|
315
|
-
execute "DROP DATABASE \"#{name}\""
|
334
|
+
execute "DROP DATABASE IF EXISTS \"#{name}\""
|
335
|
+
end
|
336
|
+
|
337
|
+
def create_schema(schema_name, pg_username)
|
338
|
+
execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
|
339
|
+
end
|
340
|
+
|
341
|
+
def drop_schema(schema_name)
|
342
|
+
execute("DROP SCHEMA \"#{schema_name}\"")
|
343
|
+
end
|
344
|
+
|
345
|
+
def all_schemas
|
346
|
+
select('select nspname from pg_namespace').map {|r| r["nspname"] }
|
347
|
+
end
|
348
|
+
|
349
|
+
def primary_key(table)
|
350
|
+
pk_and_sequence = pk_and_sequence_for(table)
|
351
|
+
pk_and_sequence && pk_and_sequence.first
|
316
352
|
end
|
317
353
|
|
318
354
|
def structure_dump
|
@@ -343,15 +379,6 @@ module ::JdbcSpec
|
|
343
379
|
end
|
344
380
|
end
|
345
381
|
|
346
|
-
def _execute(sql, name = nil)
|
347
|
-
case sql.strip
|
348
|
-
when /\A\(?\s*(select|show)/i then
|
349
|
-
@connection.execute_query(sql)
|
350
|
-
else
|
351
|
-
@connection.execute_update(sql)
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
382
|
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
|
356
383
|
#
|
357
384
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
@@ -387,13 +414,23 @@ module ::JdbcSpec
|
|
387
414
|
sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
|
388
415
|
end
|
389
416
|
|
390
|
-
def quote(value, column = nil)
|
391
|
-
return
|
417
|
+
def quote(value, column = nil) #:nodoc:
|
418
|
+
return super unless column
|
392
419
|
|
393
|
-
if value.kind_of?(String) && column
|
420
|
+
if value.kind_of?(String) && column.type == :binary
|
394
421
|
"'#{escape_bytea(value)}'"
|
395
|
-
elsif
|
396
|
-
|
422
|
+
elsif value.kind_of?(String) && column.sql_type == 'xml'
|
423
|
+
"xml '#{quote_string(value)}'"
|
424
|
+
elsif value.kind_of?(Numeric) && column.sql_type == 'money'
|
425
|
+
# Not truly string input, so doesn't require (or allow) escape string syntax.
|
426
|
+
"'#{value}'"
|
427
|
+
elsif value.kind_of?(String) && column.sql_type =~ /^bit/
|
428
|
+
case value
|
429
|
+
when /^[01]*$/
|
430
|
+
"B'#{value}'" # Bit-string notation
|
431
|
+
when /^[0-9A-F]*$/i
|
432
|
+
"X'#{value}'" # Hexadecimal notation
|
433
|
+
end
|
397
434
|
else
|
398
435
|
super
|
399
436
|
end
|
@@ -407,6 +444,17 @@ module ::JdbcSpec
|
|
407
444
|
end
|
408
445
|
end
|
409
446
|
|
447
|
+
def quote_table_name(name)
|
448
|
+
schema, name_part = extract_pg_identifier_from_name(name.to_s)
|
449
|
+
|
450
|
+
unless name_part
|
451
|
+
quote_column_name(schema)
|
452
|
+
else
|
453
|
+
table_name, name_part = extract_pg_identifier_from_name(name_part)
|
454
|
+
"#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
410
458
|
def quote_column_name(name)
|
411
459
|
%("#{name}")
|
412
460
|
end
|
@@ -430,32 +478,48 @@ module ::JdbcSpec
|
|
430
478
|
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
|
431
479
|
end
|
432
480
|
|
481
|
+
# Adds a new column to the named table.
|
482
|
+
# See TableDefinition#column for details of the options you can use.
|
433
483
|
def add_column(table_name, column_name, type, options = {})
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
484
|
+
default = options[:default]
|
485
|
+
notnull = options[:null] == false
|
486
|
+
|
487
|
+
# Add the column.
|
488
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}")
|
489
|
+
|
490
|
+
change_column_default(table_name, column_name, default) if options_include_default?(options)
|
491
|
+
change_column_null(table_name, column_name, false, default) if notnull
|
440
492
|
end
|
441
493
|
|
442
|
-
|
494
|
+
# Changes the column of a table.
|
495
|
+
def change_column(table_name, column_name, type, options = {})
|
496
|
+
quoted_table_name = quote_table_name(table_name)
|
497
|
+
|
443
498
|
begin
|
444
|
-
execute "ALTER TABLE #{
|
445
|
-
rescue ActiveRecord::StatementInvalid
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
499
|
+
execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
500
|
+
rescue ActiveRecord::StatementInvalid => e
|
501
|
+
raise e if postgresql_version > 80000
|
502
|
+
# This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
|
503
|
+
begin
|
504
|
+
begin_db_transaction
|
505
|
+
tmp_column_name = "#{column_name}_ar_tmp"
|
506
|
+
add_column(table_name, tmp_column_name, type, options)
|
507
|
+
execute "UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"
|
508
|
+
remove_column(table_name, column_name)
|
509
|
+
rename_column(table_name, tmp_column_name, column_name)
|
510
|
+
commit_db_transaction
|
511
|
+
rescue
|
512
|
+
rollback_db_transaction
|
513
|
+
end
|
453
514
|
end
|
454
|
-
|
515
|
+
|
516
|
+
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
517
|
+
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
455
518
|
end
|
456
519
|
|
457
|
-
|
458
|
-
|
520
|
+
# Changes the default value of a table column.
|
521
|
+
def change_column_default(table_name, column_name, default)
|
522
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
|
459
523
|
end
|
460
524
|
|
461
525
|
def change_column_null(table_name, column_name, null, default = nil)
|
@@ -488,6 +552,17 @@ module ::JdbcSpec
|
|
488
552
|
def tables
|
489
553
|
@connection.tables(database_name, nil, nil, ["TABLE"])
|
490
554
|
end
|
555
|
+
|
556
|
+
private
|
557
|
+
def extract_pg_identifier_from_name(name)
|
558
|
+
match_data = name[0,1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
|
559
|
+
|
560
|
+
if match_data
|
561
|
+
rest = name[match_data[0].length..-1]
|
562
|
+
rest = rest[1..-1] if rest[0,1] == "."
|
563
|
+
[match_data[1], (rest.length > 0 ? rest : nil)]
|
564
|
+
end
|
565
|
+
end
|
491
566
|
end
|
492
567
|
end
|
493
568
|
|