activerecord-jdbc-adapter 1.2.9.1 → 1.3.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -0
- data/Appraisals +12 -4
- data/Gemfile +3 -3
- data/Gemfile.lock +19 -19
- data/History.txt +90 -16
- data/LICENSE.txt +2 -1
- data/README.md +14 -1
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/gemfiles/rails23.gemfile +5 -5
- data/gemfiles/rails23.gemfile.lock +27 -27
- data/gemfiles/rails30.gemfile +3 -3
- data/gemfiles/rails30.gemfile.lock +8 -8
- data/gemfiles/rails31.gemfile +4 -4
- data/gemfiles/rails31.gemfile.lock +18 -18
- data/gemfiles/rails32.gemfile +4 -4
- data/gemfiles/rails32.gemfile.lock +17 -17
- data/gemfiles/rails40.gemfile +17 -0
- data/gemfiles/rails40.gemfile.lock +126 -0
- data/lib/activerecord-jdbc-adapter.rb +0 -7
- data/lib/arjdbc.rb +6 -5
- data/lib/arjdbc/db2.rb +1 -1
- data/lib/arjdbc/db2/adapter.rb +52 -29
- data/lib/arjdbc/db2/connection_methods.rb +13 -14
- data/lib/arjdbc/derby.rb +1 -1
- data/lib/arjdbc/derby/adapter.rb +29 -9
- data/lib/arjdbc/derby/connection_methods.rb +17 -20
- data/lib/arjdbc/firebird.rb +1 -1
- data/lib/arjdbc/h2.rb +2 -2
- data/lib/arjdbc/h2/adapter.rb +1 -1
- data/lib/arjdbc/h2/connection_methods.rb +12 -16
- data/lib/arjdbc/hsqldb.rb +1 -1
- data/lib/arjdbc/hsqldb/connection_methods.rb +13 -16
- data/lib/arjdbc/informix.rb +1 -1
- data/lib/arjdbc/informix/connection_methods.rb +8 -10
- data/lib/arjdbc/jdbc.rb +1 -1
- data/lib/arjdbc/jdbc/adapter.rb +125 -53
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/base_ext.rb +34 -9
- data/lib/arjdbc/jdbc/column.rb +15 -2
- data/lib/arjdbc/jdbc/connection.rb +0 -2
- data/lib/arjdbc/jdbc/connection_methods.rb +10 -3
- data/lib/arjdbc/jdbc/driver.rb +2 -2
- data/lib/arjdbc/jdbc/extension.rb +35 -21
- data/lib/arjdbc/jdbc/java.rb +0 -2
- data/lib/arjdbc/jdbc/missing_functionality_helper.rb +35 -25
- data/lib/arjdbc/jdbc/railtie.rb +2 -9
- data/lib/arjdbc/mimer.rb +1 -1
- data/lib/arjdbc/mssql.rb +2 -2
- data/lib/arjdbc/mssql/adapter.rb +271 -92
- data/lib/arjdbc/mssql/connection_methods.rb +30 -32
- data/lib/arjdbc/mssql/explain_support.rb +107 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +48 -18
- data/lib/arjdbc/mysql.rb +1 -1
- data/lib/arjdbc/mysql/adapter.rb +63 -14
- data/lib/arjdbc/mysql/connection_methods.rb +22 -24
- data/lib/arjdbc/mysql/explain_support.rb +2 -5
- data/lib/arjdbc/oracle.rb +1 -1
- data/lib/arjdbc/oracle/adapter.rb +78 -38
- data/lib/arjdbc/oracle/connection_methods.rb +9 -10
- data/lib/arjdbc/postgresql.rb +1 -1
- data/lib/arjdbc/postgresql/adapter.rb +964 -380
- data/lib/arjdbc/postgresql/column_cast.rb +136 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +19 -21
- data/lib/arjdbc/postgresql/explain_support.rb +3 -6
- data/lib/arjdbc/railtie.rb +9 -0
- data/lib/arjdbc/sqlite3.rb +1 -1
- data/lib/arjdbc/sqlite3/adapter.rb +73 -26
- data/lib/arjdbc/sqlite3/connection_methods.rb +27 -28
- data/lib/arjdbc/sqlite3/explain_support.rb +2 -5
- data/lib/arjdbc/sybase.rb +1 -1
- data/lib/arjdbc/version.rb +5 -4
- data/pom.xml +8 -0
- data/rakelib/02-test.rake +57 -51
- data/rakelib/compile.rake +17 -5
- data/rakelib/rails.rake +42 -31
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +4 -3
- data/src/java/arjdbc/derby/DerbyModule.java +98 -85
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +70 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +0 -4
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +26 -15
- data/src/java/arjdbc/jdbc/Callable.java +44 -0
- data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +10 -2
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1675 -834
- data/src/java/arjdbc/jdbc/SQLBlock.java +9 -3
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +73 -36
- data/src/java/arjdbc/mysql/MySQLModule.java +11 -10
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +86 -80
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +27 -7
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -0
- data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +25 -67
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +52 -49
- data/src/java/arjdbc/util/QuotingUtils.java +6 -6
- data/test/abstract_db_create.rb +11 -11
- data/test/activerecord/connection_adapters/type_conversion_test.rb +18 -12
- data/test/db/db2.rb +1 -1
- data/test/{db2_binary_test.rb → db/db2/binary_test.rb} +0 -0
- data/test/db/db2/has_many_through_test.rb +6 -0
- data/test/{db2_reset_column_information_test.rb → db/db2/reset_column_information_test.rb} +1 -2
- data/test/{db2_serialize_test.rb → db/db2/serialize_test.rb} +0 -0
- data/test/{db2_simple_test.rb → db/db2/simple_test.rb} +1 -8
- data/test/db/db2/test_helper.rb +6 -0
- data/test/{db2_test.rb → db/db2/unit_test.rb} +1 -1
- data/test/db/derby.rb +1 -1
- data/test/{derby_binary_test.rb → db/derby/binary_test.rb} +0 -0
- data/test/{derby_migration_test.rb → db/derby/migration_test.rb} +0 -0
- data/test/{derby_reset_column_information_test.rb → db/derby/reset_column_information_test.rb} +0 -0
- data/test/{derby_row_locking_test.rb → db/derby/row_locking_test.rb} +1 -4
- data/test/db/derby/schema_dump_test.rb +5 -0
- data/test/{derby_serialize_test.rb → db/derby/serialize_test.rb} +0 -0
- data/test/{derby_simple_test.rb → db/derby/simple_test.rb} +23 -38
- data/test/db/derby/test_helper.rb +6 -0
- data/test/db/derby/unit_test.rb +32 -0
- data/test/db/derby/xml_column_test.rb +17 -0
- data/test/db/h2.rb +1 -1
- data/test/{h2_binary_test.rb → db/h2/binary_test.rb} +0 -0
- data/test/{h2_change_column_test.rb → db/h2/change_column_test.rb} +1 -0
- data/test/{h2_schema_dump_test.rb → db/h2/schema_dump_test.rb} +0 -0
- data/test/{h2_serialize_test.rb → db/h2/serialize_test.rb} +0 -0
- data/test/{h2_simple_test.rb → db/h2/simple_test.rb} +3 -1
- data/test/db/hsqldb.rb +1 -1
- data/test/{hsqldb_binary_test.rb → db/hsqldb/binary_test.rb} +0 -0
- data/test/{hsqldb_schema_dump_test.rb → db/hsqldb/schema_dump_test.rb} +0 -0
- data/test/{hsqldb_serialize_test.rb → db/hsqldb/serialize_test.rb} +0 -0
- data/test/{hsqldb_simple_test.rb → db/hsqldb/simple_test.rb} +3 -1
- data/test/db/informix.rb +1 -1
- data/test/db/jdbc.rb +3 -2
- data/test/db/jdbc_derby.rb +1 -1
- data/test/db/jdbc_h2.rb +1 -1
- data/test/db/jdbc_mysql.rb +1 -1
- data/test/db/jdbc_postgres.rb +1 -1
- data/test/db/jndi_config.rb +1 -2
- data/test/db/jndi_pooled_config.rb +2 -3
- data/test/db/mssql.rb +2 -2
- data/test/{mssql_binary_test.rb → db/mssql/binary_test.rb} +0 -0
- data/test/{mssql_db_create_test.rb → db/mssql/db_create_test.rb} +1 -1
- data/test/db/mssql/exec_proc_test.rb +46 -0
- data/test/{mssql_identity_insert_test.rb → db/mssql/identity_insert_test.rb} +0 -0
- data/test/db/mssql/ignore_system_views_test.rb +40 -0
- data/test/{mssql_limit_offset_test.rb → db/mssql/limit_offset_test.rb} +10 -1
- data/test/{mssql_multibyte_test.rb → db/mssql/multibyte_test.rb} +0 -0
- data/test/db/mssql/multiple_connections_test.rb +71 -0
- data/test/{mssql_reset_column_information_test.rb → db/mssql/reset_column_information_test.rb} +0 -0
- data/test/{mssql_row_locking_test.rb → db/mssql/row_locking_test.rb} +0 -0
- data/test/{mssql_serialize_test.rb → db/mssql/serialize_test.rb} +1 -1
- data/test/db/mssql/simple_test.rb +140 -0
- data/test/db/mssql/transaction_test.rb +6 -0
- data/test/db/mssql/types_test.rb +205 -0
- data/test/{mssql_test.rb → db/mssql/unit_test.rb} +2 -2
- data/test/db/mysql.rb +1 -2
- data/test/db/mysql/_rails_test_mysql.32.out +6768 -0
- data/test/{mysql_binary_test.rb → db/mysql/binary_test.rb} +0 -0
- data/test/db/mysql/connection_test.rb +51 -0
- data/test/{mysql_db_create_test.rb → db/mysql/db_create_test.rb} +0 -0
- data/test/{mysql_index_length_test.rb → db/mysql/index_length_test.rb} +0 -0
- data/test/{mysql_multibyte_test.rb → db/mysql/multibyte_test.rb} +0 -0
- data/test/{mysql_nonstandard_primary_key_test.rb → db/mysql/nonstandard_primary_key_test.rb} +0 -0
- data/test/{mysql_reset_column_information_test.rb → db/mysql/reset_column_information_test.rb} +0 -0
- data/test/{mysql_schema_dump_test.rb → db/mysql/schema_dump_test.rb} +9 -1
- data/test/{mysql_serialize_test.rb → db/mysql/serialize_test.rb} +0 -0
- data/test/{mysql_simple_test.rb → db/mysql/simple_test.rb} +16 -8
- data/test/db/mysql/transaction_test.rb +6 -0
- data/test/db/mysql/types_test.rb +30 -0
- data/test/{mysql_test.rb → db/mysql/unit_test.rb} +1 -1
- data/test/db/mysql_config.rb +1 -1
- data/test/db/oracle.rb +1 -1
- data/test/{oracle_binary_test.rb → db/oracle/binary_test.rb} +0 -0
- data/test/{oracle_limit_test.rb → db/oracle/limit_test.rb} +0 -0
- data/test/db/oracle/multibyte_test.rb +22 -0
- data/test/{oracle_reset_column_information_test.rb → db/oracle/reset_column_information_test.rb} +0 -0
- data/test/{oracle_serialize_test.rb → db/oracle/serialize_test.rb} +0 -0
- data/test/{oracle_simple_test.rb → db/oracle/simple_test.rb} +14 -19
- data/test/{oracle_specific_test.rb → db/oracle/specific_test.rb} +62 -16
- data/test/db/oracle/transaction_test.rb +31 -0
- data/test/db/oracle/unit_test.rb +31 -0
- data/test/db/postgres.rb +1 -1
- data/test/db/postgres/_rails_test_postgres.32.out +6777 -0
- data/test/db/postgres/a_custom_primary_key_test.rb +50 -0
- data/test/db/postgres/array_type_test.rb +101 -0
- data/test/{postgres_binary_test.rb → db/postgres/binary_test.rb} +0 -0
- data/test/db/postgres/connection_test.rb +55 -0
- data/test/db/postgres/data_types_test.rb +703 -0
- data/test/{postgres_db_create_test.rb → db/postgres/db_create_test.rb} +1 -1
- data/test/{postgres_drop_db_test.rb → db/postgres/db_drop_test.rb} +2 -0
- data/test/db/postgres/hstore_test.rb +200 -0
- data/test/db/postgres/information_schema_leak_test.rb +30 -0
- data/test/db/postgres/json_test.rb +86 -0
- data/test/db/postgres/ltree_test.rb +50 -0
- data/test/{postgres_mixed_case_test.rb → db/postgres/mixed_case_test.rb} +0 -0
- data/test/db/postgres/native_types_test.rb +128 -0
- data/test/{postgres_reserved_test.rb → db/postgres/reserved_test.rb} +0 -0
- data/test/{postgres_reset_column_information_test.rb → db/postgres/reset_column_information_test.rb} +0 -0
- data/test/{postgres_row_locking_test.rb → db/postgres/row_locking_test.rb} +0 -0
- data/test/{postgres_schema_dump_test.rb → db/postgres/schema_dump_test.rb} +4 -4
- data/test/db/postgres/schema_test.rb +113 -0
- data/test/{postgres_simple_test.rb → db/postgres/simple_test.rb} +48 -8
- data/test/{postgres_table_alias_length_test.rb → db/postgres/table_alias_length_test.rb} +2 -1
- data/test/db/postgres/transaction_test.rb +6 -0
- data/test/{postgres_test.rb → db/postgres/unit_test.rb} +3 -3
- data/test/db/sqlite3.rb +1 -1
- data/test/db/sqlite3/_rails_test_sqlite3.32.out +6502 -0
- data/test/db/sqlite3/has_many_though_test.rb +6 -0
- data/test/{sqlite3_reset_column_information_test.rb → db/sqlite3/reset_column_information_test.rb} +0 -0
- data/test/{sqlite3_schema_dump_test.rb → db/sqlite3/schema_dump_test.rb} +0 -0
- data/test/{sqlite3_serialize_test.rb → db/sqlite3/serialize_test.rb} +0 -0
- data/test/{sqlite3_simple_test.rb → db/sqlite3/simple_test.rb} +63 -63
- data/test/db/sqlite3/transaction_test.rb +32 -0
- data/test/{sqlite3_type_conversion_test.rb → db/sqlite3/type_conversion_test.rb} +0 -0
- data/test/has_many_through.rb +29 -64
- data/test/jdbc/oracle.rb +11 -0
- data/test/jndi_test.rb +16 -4
- data/test/models/auto_id.rb +1 -1
- data/test/models/rights_and_roles.rb +57 -0
- data/test/row_locking.rb +3 -0
- data/test/schema_dump.rb +24 -10
- data/test/simple.rb +359 -104
- data/test/test_helper.rb +4 -2
- data/test/transaction.rb +109 -0
- metadata +119 -86
- data/lib/arjdbc/jdbc/compatibility.rb +0 -51
- data/lib/arjdbc/jdbc/core_ext.rb +0 -24
- data/lib/arjdbc/jdbc/discover.rb +0 -18
- data/test/derby_schema_dump_test.rb +0 -9
- data/test/mssql_ignore_system_views_test.rb +0 -30
- data/test/mssql_legacy_types_test.rb +0 -58
- data/test/mssql_null_test.rb +0 -14
- data/test/mssql_simple_test.rb +0 -51
- data/test/postgres_information_schema_leak_test.rb +0 -28
- data/test/postgres_native_type_mapping_test.rb +0 -93
- data/test/postgres_nonseq_pkey_test.rb +0 -38
- data/test/postgres_schema_search_path_test.rb +0 -48
- data/test/postgres_type_conversion_test.rb +0 -33
@@ -8,12 +8,9 @@ module ::ArJdbc
|
|
8
8
|
def explain(arel, binds = [])
|
9
9
|
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
10
10
|
start = Time.now.to_f
|
11
|
-
|
11
|
+
result = exec_query(sql, "EXPLAIN", binds)
|
12
12
|
elapsed = Time.now.to_f - start
|
13
|
-
|
14
|
-
keys = raw_result[0] ? raw_result[0].keys : {}
|
15
|
-
rows = raw_result.map { |hash| hash.values }
|
16
|
-
ExplainPrettyPrinter.new.pp ActiveRecord::Result.new(keys, rows), elapsed
|
13
|
+
ExplainPrettyPrinter.new.pp result, elapsed
|
17
14
|
end
|
18
15
|
|
19
16
|
class ExplainPrettyPrinter # :nodoc:
|
data/lib/arjdbc/oracle.rb
CHANGED
@@ -5,7 +5,7 @@ module ArJdbc
|
|
5
5
|
|
6
6
|
@@_lob_callback_added = nil
|
7
7
|
|
8
|
-
def self.extended(
|
8
|
+
def self.extended(base)
|
9
9
|
unless @@_lob_callback_added
|
10
10
|
ActiveRecord::Base.class_eval do
|
11
11
|
def after_save_with_oracle_lob
|
@@ -14,7 +14,7 @@ module ArJdbc
|
|
14
14
|
next if value.nil? || (value == '')
|
15
15
|
|
16
16
|
connection.write_large_object(
|
17
|
-
column.type == :binary, column.name,
|
17
|
+
column.type == :binary, column.name,
|
18
18
|
self.class.table_name, self.class.primary_key,
|
19
19
|
quote_value(id), value
|
20
20
|
)
|
@@ -48,18 +48,9 @@ module ArJdbc
|
|
48
48
|
|
49
49
|
module Column
|
50
50
|
|
51
|
-
def primary=(
|
51
|
+
def primary=(value)
|
52
52
|
super
|
53
|
-
if
|
54
|
-
@type = :integer
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def extract_limit(sql_type)
|
59
|
-
case sql_type
|
60
|
-
when /^(clob|date)/i; nil
|
61
|
-
else super
|
62
|
-
end
|
53
|
+
@type = :integer if value && @sql_type =~ /^NUMBER$/i
|
63
54
|
end
|
64
55
|
|
65
56
|
def type_cast(value)
|
@@ -67,6 +58,7 @@ module ArJdbc
|
|
67
58
|
case type
|
68
59
|
when :datetime then ArJdbc::Oracle::Column.string_to_time(value)
|
69
60
|
when :timestamp then ArJdbc::Oracle::Column.string_to_time(value)
|
61
|
+
when :boolean then ArJdbc::Oracle::Column.value_to_boolean(value)
|
70
62
|
else
|
71
63
|
super
|
72
64
|
end
|
@@ -76,11 +68,25 @@ module ArJdbc
|
|
76
68
|
case type
|
77
69
|
when :datetime then "ArJdbc::Oracle::Column.string_to_time(#{var_name})"
|
78
70
|
when :timestamp then "ArJdbc::Oracle::Column.string_to_time(#{var_name})"
|
71
|
+
when :boolean then "ArJdbc::Oracle::Column.value_to_boolean(#{var_name})"
|
79
72
|
else
|
80
73
|
super
|
81
74
|
end
|
82
75
|
end
|
83
76
|
|
77
|
+
# convert a value to a boolean
|
78
|
+
def self.value_to_boolean(value)
|
79
|
+
# NOTE: Oracle JDBC meta-data gets us DECIMAL for NUMBER(1) values
|
80
|
+
# thus we're likely to get a column back as BigDecimal (e.g. 1.0)
|
81
|
+
if value.is_a?(String)
|
82
|
+
value.blank? ? nil : value == '1'
|
83
|
+
elsif value.is_a?(Numeric)
|
84
|
+
value.to_i == 1 # <BigDecimal:7b5bfe,'0.1E1',1(4)>
|
85
|
+
else
|
86
|
+
!! value
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
84
90
|
def self.string_to_time(string)
|
85
91
|
return string unless string.is_a?(String)
|
86
92
|
return nil if string.empty?
|
@@ -101,19 +107,28 @@ module ArJdbc
|
|
101
107
|
|
102
108
|
private
|
103
109
|
|
110
|
+
def extract_limit(sql_type)
|
111
|
+
case sql_type
|
112
|
+
when /^(clob|date)/i then nil
|
113
|
+
when /^xml/i then @sql_type = 'XMLTYPE'; nil
|
114
|
+
else super
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
104
118
|
def simplified_type(field_type)
|
105
119
|
case field_type
|
106
|
-
when /^number\(1\)$/i
|
107
|
-
when /char/i
|
108
|
-
when /float|double/i
|
109
|
-
when /int/i
|
110
|
-
when /num|dec|real/i
|
120
|
+
when /^number\(1\)$/i then :boolean
|
121
|
+
when /char/i then :string
|
122
|
+
when /float|double/i then :float
|
123
|
+
when /int/i then :integer
|
124
|
+
when /num|dec|real/i then extract_scale(field_type) == 0 ? :integer : :decimal
|
111
125
|
# Oracle TIMESTAMP stores the date and time to up to 9 digits of sub-second precision
|
112
|
-
when /TIMESTAMP/i
|
126
|
+
when /TIMESTAMP/i then :timestamp
|
113
127
|
# Oracle DATE stores the date and time to the second
|
114
|
-
when /DATE|TIME/i
|
115
|
-
when /CLOB/i
|
116
|
-
when /BLOB/i
|
128
|
+
when /DATE|TIME/i then :datetime
|
129
|
+
when /CLOB/i then :text
|
130
|
+
when /BLOB/i then :binary
|
131
|
+
when /XML/i then :xml
|
117
132
|
else
|
118
133
|
super
|
119
134
|
end
|
@@ -135,6 +150,22 @@ module ArJdbc
|
|
135
150
|
end
|
136
151
|
|
137
152
|
end
|
153
|
+
|
154
|
+
class TableDefinition < ::ActiveRecord::ConnectionAdapters::TableDefinition # :nodoc:
|
155
|
+
def raw(*args)
|
156
|
+
options = args.extract_options!
|
157
|
+
column(args[0], 'raw', options)
|
158
|
+
end
|
159
|
+
|
160
|
+
def xml(*args)
|
161
|
+
options = args.extract_options!
|
162
|
+
column(args[0], 'xml', options)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def table_definition(*args)
|
167
|
+
new_table_definition(TableDefinition, *args)
|
168
|
+
end
|
138
169
|
|
139
170
|
def self.arel2_visitors(config)
|
140
171
|
{ 'oracle' => Arel::Visitors::Oracle }
|
@@ -160,6 +191,7 @@ module ArJdbc
|
|
160
191
|
:binary => { :name => "BLOB" },
|
161
192
|
:boolean => { :name => "NUMBER", :limit => 1 },
|
162
193
|
:raw => { :name => "RAW", :limit => 2000 },
|
194
|
+
:xml => { :name => 'XMLTYPE' }
|
163
195
|
}
|
164
196
|
|
165
197
|
def native_database_types
|
@@ -439,14 +471,17 @@ module ArJdbc
|
|
439
471
|
end
|
440
472
|
private :extract_order_columns
|
441
473
|
|
442
|
-
def tables
|
474
|
+
def tables # :nodoc:
|
443
475
|
@connection.tables(nil, oracle_schema)
|
444
476
|
end
|
445
477
|
|
446
478
|
# NOTE: better to use current_schema instead of the configured one ?!
|
447
|
-
|
448
479
|
def columns(table_name, name = nil) # :nodoc:
|
449
|
-
@connection.columns_internal(table_name.to_s,
|
480
|
+
@connection.columns_internal(table_name.to_s, nil, oracle_schema)
|
481
|
+
end
|
482
|
+
|
483
|
+
def tablespace(table_name)
|
484
|
+
select_value "SELECT tablespace_name FROM user_tables WHERE table_name='#{table_name.to_s.upcase}'"
|
450
485
|
end
|
451
486
|
|
452
487
|
# QUOTING ==================================================
|
@@ -468,15 +503,17 @@ module ArJdbc
|
|
468
503
|
end
|
469
504
|
|
470
505
|
def quote(value, column = nil) # :nodoc:
|
471
|
-
# Arel 2 passes SqlLiterals through
|
472
|
-
return value if sql_literal?(value)
|
506
|
+
return value if sql_literal?(value) # Arel 2 passes SqlLiterals through
|
473
507
|
|
474
|
-
|
508
|
+
column_type = column && column.type
|
509
|
+
if column_type == :text || column_type == :binary
|
475
510
|
if /(.*?)\([0-9]+\)/ =~ column.sql_type
|
476
511
|
%Q{empty_#{ $1.downcase }()}
|
477
512
|
else
|
478
513
|
%Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
|
479
514
|
end
|
515
|
+
elsif column_type == :xml
|
516
|
+
"XMLTYPE('#{quote_string(value)}')" # XMLTYPE ?
|
480
517
|
else
|
481
518
|
if column.respond_to?(:primary) && column.primary && column.klass != String
|
482
519
|
return value.to_i.to_s
|
@@ -510,23 +547,29 @@ module ArJdbc
|
|
510
547
|
def explain(arel, binds = [])
|
511
548
|
sql = "EXPLAIN PLAN FOR #{to_sql(arel)}"
|
512
549
|
return if sql =~ /FROM all_/
|
513
|
-
|
550
|
+
execute(sql, 'EXPLAIN', binds)
|
514
551
|
select_values("SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)", 'EXPLAIN').join("\n")
|
515
552
|
end
|
516
553
|
|
517
554
|
def select(sql, name = nil, binds = [])
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
555
|
+
result = super # AR::Result (4.0) or Array (<= 3.2)
|
556
|
+
result.columns.delete('raw_rnum_') if result.respond_to?(:columns)
|
557
|
+
result.each { |row| row.delete('raw_rnum_') } # Hash rows even for AR::Result
|
558
|
+
result
|
559
|
+
end
|
560
|
+
|
561
|
+
# @override as <code>#execute_insert</code> not working for Oracle e.g.
|
562
|
+
# getLong not implemented for class oracle.jdbc.driver.T4CRowidAccessor:
|
563
|
+
# INSERT INTO binaries (data, id, name, short_data) VALUES (?, ?, ?, ?)
|
564
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) # :nodoc:
|
565
|
+
execute(sql, name, binds)
|
523
566
|
end
|
524
567
|
|
525
568
|
private
|
526
569
|
|
527
570
|
def _execute(sql, name = nil)
|
528
571
|
if self.class.select?(sql)
|
529
|
-
@connection.
|
572
|
+
@connection.execute_query_raw(sql)
|
530
573
|
else
|
531
574
|
@connection.execute_update(sql)
|
532
575
|
end
|
@@ -560,8 +603,5 @@ module ActiveRecord::ConnectionAdapters
|
|
560
603
|
|
561
604
|
class OracleColumn < JdbcColumn
|
562
605
|
include ArJdbc::Oracle::Column
|
563
|
-
|
564
|
-
def call_discovered_column_callbacks(*)
|
565
|
-
end
|
566
606
|
end
|
567
607
|
end
|
@@ -1,12 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
alias_method :jdbcoracle_connection, :oracle_connection
|
1
|
+
ArJdbc::ConnectionMethods.module_eval do
|
2
|
+
def oracle_connection(config)
|
3
|
+
config[:port] ||= 1521
|
4
|
+
config[:url] ||= "jdbc:oracle:thin:@#{config[:host]}:#{config[:port]}:#{config[:database]}"
|
5
|
+
config[:driver] ||= "oracle.jdbc.driver.OracleDriver"
|
6
|
+
config[:adapter_spec] = ::ArJdbc::Oracle
|
7
|
+
config[:connection_alive_sql] ||= 'SELECT 1 FROM DUAL'
|
8
|
+
jdbc_connection(config)
|
11
9
|
end
|
10
|
+
alias_method :jdbcoracle_connection, :oracle_connection
|
12
11
|
end
|
data/lib/arjdbc/postgresql.rb
CHANGED
@@ -1,113 +1,251 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require 'arjdbc/postgresql/column_cast'
|
1
3
|
require 'arjdbc/postgresql/explain_support'
|
2
4
|
|
3
|
-
module
|
5
|
+
module ArJdbc
|
4
6
|
module PostgreSQL
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
mod.configure_connection
|
7
|
+
|
8
|
+
AR4_COMPAT = ::ActiveRecord::VERSION::MAJOR > 3 unless const_defined?(:AR4_COMPAT) # :nodoc:
|
9
|
+
|
10
|
+
def self.extended(base)
|
11
|
+
base.configure_connection
|
11
12
|
end
|
12
13
|
|
13
14
|
def self.column_selector
|
14
|
-
[/postgre/i, lambda {|cfg,
|
15
|
+
[ /postgre/i, lambda { |cfg, column| column.extend(::ArJdbc::PostgreSQL::Column) } ]
|
15
16
|
end
|
16
17
|
|
17
18
|
def self.jdbc_connection_class
|
18
|
-
::ActiveRecord::ConnectionAdapters::
|
19
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
|
19
20
|
end
|
20
|
-
|
21
|
+
|
22
|
+
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
23
|
+
# This is called by #connect and should not be called manually.
|
21
24
|
def configure_connection
|
22
|
-
|
25
|
+
if encoding = config[:encoding]
|
26
|
+
self.set_client_encoding(encoding)
|
27
|
+
end
|
28
|
+
self.client_min_messages = config[:min_messages] || 'warning'
|
29
|
+
self.schema_search_path = config[:schema_search_path] || config[:schema_order]
|
30
|
+
|
31
|
+
# Use standard-conforming strings if available so we don't have to do the E'...' dance.
|
32
|
+
set_standard_conforming_strings
|
33
|
+
|
34
|
+
# If using Active Record's time zone support configure the connection to return
|
35
|
+
# TIMESTAMP WITH ZONE types in UTC.
|
36
|
+
# (SET TIME ZONE does not use an equals sign like other SET variables)
|
37
|
+
if ActiveRecord::Base.default_timezone == :utc
|
38
|
+
execute("SET time zone 'UTC'", 'SCHEMA')
|
39
|
+
elsif defined?(@local_tz) && @local_tz
|
40
|
+
execute("SET time zone '#{@local_tz}'", 'SCHEMA')
|
41
|
+
end # if defined? ActiveRecord::Base.default_timezone
|
42
|
+
|
43
|
+
# SET statements from :variables config hash
|
44
|
+
# http://www.postgresql.org/docs/8.3/static/sql-set.html
|
45
|
+
(config[:variables] || {}).map do |k, v|
|
46
|
+
if v == ':default' || v == :default
|
47
|
+
# Sets the value to the global or compile default
|
48
|
+
execute("SET SESSION #{k.to_s} TO DEFAULT", 'SCHEMA')
|
49
|
+
elsif ! v.nil?
|
50
|
+
execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# constants taken from postgresql_adapter in rails project
|
56
|
+
ADAPTER_NAME = 'PostgreSQL'
|
57
|
+
|
58
|
+
def adapter_name #:nodoc:
|
59
|
+
ADAPTER_NAME
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.arel2_visitors(config)
|
63
|
+
{
|
64
|
+
'postgresql' => ::Arel::Visitors::PostgreSQL,
|
65
|
+
'jdbcpostgresql' => ::Arel::Visitors::PostgreSQL,
|
66
|
+
'pg' => ::Arel::Visitors::PostgreSQL
|
67
|
+
}
|
23
68
|
end
|
24
69
|
|
25
|
-
|
26
|
-
|
70
|
+
def postgresql_version
|
71
|
+
@postgresql_version ||=
|
72
|
+
begin
|
73
|
+
value = select_value('SELECT version()')
|
74
|
+
if value =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
|
75
|
+
($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
|
76
|
+
else
|
77
|
+
0
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def use_insert_returning?
|
83
|
+
if ( @use_insert_returning ||= nil ).nil?
|
84
|
+
@use_insert_returning = supports_insert_with_returning?
|
85
|
+
end
|
86
|
+
@use_insert_returning
|
87
|
+
end
|
88
|
+
|
89
|
+
# column behavior based on postgresql_adapter in rails
|
27
90
|
module Column
|
91
|
+
|
28
92
|
def self.included(base)
|
29
93
|
class << base
|
94
|
+
include ArJdbc::PostgreSQL::Column::Cast
|
95
|
+
# include ArJdbc::PostgreSQL::Column::ArrayParser
|
30
96
|
attr_accessor :money_precision
|
31
|
-
def string_to_time(string)
|
32
|
-
return string unless String === string
|
33
|
-
|
34
|
-
case string
|
35
|
-
when 'infinity' then 1.0 / 0.0
|
36
|
-
when '-infinity' then -1.0 / 0.0
|
37
|
-
else
|
38
|
-
super
|
39
|
-
end
|
40
|
-
end
|
41
97
|
end
|
42
98
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
99
|
+
|
100
|
+
attr_accessor :array
|
101
|
+
def array?; array; end # in case we remove the array reader
|
102
|
+
|
103
|
+
# Extracts the value from a PostgreSQL column default definition.
|
104
|
+
#
|
105
|
+
# @override JdbcColumn#default_value
|
106
|
+
# NOTE: based on `self.extract_value_from_default(default)` code
|
46
107
|
def default_value(default)
|
108
|
+
# This is a performance optimization for Ruby 1.9.2 in development.
|
109
|
+
# If the value is nil, we return nil straight away without checking
|
110
|
+
# the regular expressions. If we check each regular expression,
|
111
|
+
# Regexp#=== will call NilClass#to_str, which will trigger
|
112
|
+
# method_missing (defined by whiny nil in ActiveSupport) which
|
113
|
+
# makes this method very very slow.
|
114
|
+
return default unless default
|
115
|
+
|
47
116
|
case default
|
48
|
-
|
49
|
-
|
50
|
-
# the regular expressions. If we check each regular expression,
|
51
|
-
# Regexp#=== will call NilClass#to_str, which will trigger
|
52
|
-
# method_missing (defined by whiny nil in ActiveSupport) which
|
53
|
-
# makes this method very very slow.
|
54
|
-
when NilClass
|
55
|
-
nil
|
117
|
+
when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
|
118
|
+
$1
|
56
119
|
# Numeric types
|
57
|
-
|
58
|
-
|
120
|
+
when /\A\(?(-?\d+(\.\d*)?\)?)\z/
|
121
|
+
$1
|
59
122
|
# Character types
|
60
|
-
|
61
|
-
|
62
|
-
# Character types (8.1 formatting)
|
63
|
-
when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
|
64
|
-
$1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
|
123
|
+
when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
|
124
|
+
$1
|
65
125
|
# Binary data types
|
66
|
-
|
67
|
-
|
126
|
+
when /\A'(.*)'::bytea\z/m
|
127
|
+
$1
|
68
128
|
# Date/time types
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
129
|
+
when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
|
130
|
+
$1
|
131
|
+
when /\A'(.*)'::interval\z/
|
132
|
+
$1
|
73
133
|
# Boolean type
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
134
|
+
when 'true'
|
135
|
+
true
|
136
|
+
when 'false'
|
137
|
+
false
|
78
138
|
# Geometric types
|
79
|
-
|
80
|
-
|
139
|
+
when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
|
140
|
+
$1
|
81
141
|
# Network address types
|
82
|
-
|
83
|
-
|
142
|
+
when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
|
143
|
+
$1
|
84
144
|
# Bit string types
|
85
|
-
|
86
|
-
|
145
|
+
when /\AB'(.*)'::"?bit(?: varying)?"?\z/
|
146
|
+
$1
|
87
147
|
# XML type
|
88
|
-
|
89
|
-
|
148
|
+
when /\A'(.*)'::xml\z/m
|
149
|
+
$1
|
90
150
|
# Arrays
|
91
|
-
|
92
|
-
|
151
|
+
when /\A'(.*)'::"?\D+"?\[\]\z/
|
152
|
+
$1
|
153
|
+
# Hstore
|
154
|
+
when /\A'(.*)'::hstore\z/
|
155
|
+
$1
|
156
|
+
# JSON
|
157
|
+
when /\A'(.*)'::json\z/
|
158
|
+
$1
|
93
159
|
# Object identifier types
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
160
|
+
when /\A-?\d+\z/
|
161
|
+
$1
|
162
|
+
else
|
163
|
+
# Anything else is blank, some user type, or some function
|
164
|
+
# and we can't know the value of that, so return nil.
|
165
|
+
nil
|
100
166
|
end
|
101
167
|
end
|
102
168
|
|
169
|
+
# Casts value (which is a String) to an appropriate instance.
|
170
|
+
def type_cast(value)
|
171
|
+
return if value.nil?
|
172
|
+
return super if encoded? # respond_to?(:encoded?) only since AR-3.2
|
173
|
+
|
174
|
+
# NOTE: we do not use OID::Type
|
175
|
+
# @oid_type.type_cast value
|
176
|
+
|
177
|
+
return value if array? # handled on the connection (JDBC) side
|
178
|
+
|
179
|
+
case type
|
180
|
+
when :hstore then self.class.string_to_hstore value
|
181
|
+
when :json then self.class.string_to_json value
|
182
|
+
when :cidr, :inet then self.class.string_to_cidr value
|
183
|
+
when :macaddr then value
|
184
|
+
when :tsvector then value
|
185
|
+
else
|
186
|
+
case sql_type
|
187
|
+
when 'money'
|
188
|
+
# Because money output is formatted according to the locale, there
|
189
|
+
# are two cases to consider (note the decimal separators) :
|
190
|
+
# (1) $12,345,678.12
|
191
|
+
# (2) $12.345.678,12
|
192
|
+
case value
|
193
|
+
when /^-?\D+[\d,]+\.\d{2}$/ # (1)
|
194
|
+
value.gsub!(/[^-\d.]/, '')
|
195
|
+
when /^-?\D+[\d.]+,\d{2}$/ # (2)
|
196
|
+
value.gsub!(/[^-\d,]/, '')
|
197
|
+
value.sub!(/,/, '.')
|
198
|
+
end
|
199
|
+
self.class.value_to_decimal value
|
200
|
+
when /^point/
|
201
|
+
if value.is_a?(String)
|
202
|
+
self.class.string_to_point value
|
203
|
+
else
|
204
|
+
value
|
205
|
+
end
|
206
|
+
when /(.*?)range$/
|
207
|
+
return if value.nil? || value == 'empty'
|
208
|
+
return value if value.is_a?(::Range)
|
209
|
+
|
210
|
+
extracted = extract_bounds(value)
|
211
|
+
|
212
|
+
case $1 # subtype
|
213
|
+
when 'date' # :date
|
214
|
+
from = self.class.value_to_date(extracted[:from])
|
215
|
+
from -= 1.day if extracted[:exclude_start]
|
216
|
+
to = self.class.value_to_date(extracted[:to])
|
217
|
+
when 'num' # :decimal
|
218
|
+
from = BigDecimal.new(extracted[:from].to_s)
|
219
|
+
# FIXME: add exclude start for ::Range, same for timestamp ranges
|
220
|
+
to = BigDecimal.new(extracted[:to].to_s)
|
221
|
+
when 'ts', 'tstz' # :time
|
222
|
+
from = self.class.string_to_time(extracted[:from])
|
223
|
+
to = self.class.string_to_time(extracted[:to])
|
224
|
+
when 'int4', 'int8' # :integer
|
225
|
+
from = to_integer(extracted[:from]) rescue value ? 1 : 0
|
226
|
+
from -= 1 if extracted[:exclude_start]
|
227
|
+
to = to_integer(extracted[:to]) rescue value ? 1 : 0
|
228
|
+
else
|
229
|
+
return value
|
230
|
+
end
|
231
|
+
|
232
|
+
::Range.new(from, to, extracted[:exclude_end])
|
233
|
+
else super
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end if AR4_COMPAT
|
237
|
+
|
238
|
+
private
|
239
|
+
|
103
240
|
def extract_limit(sql_type)
|
104
241
|
case sql_type
|
105
|
-
when /^bigint/i
|
106
|
-
when /^smallint/i
|
242
|
+
when /^bigint/i; 8
|
243
|
+
when /^smallint/i; 2
|
244
|
+
when /^timestamp/i; nil
|
107
245
|
else super
|
108
246
|
end
|
109
247
|
end
|
110
|
-
|
248
|
+
|
111
249
|
# Extracts the scale from PostgreSQL-specific data types.
|
112
250
|
def extract_scale(sql_type)
|
113
251
|
# Money type has a fixed scale of 2.
|
@@ -118,11 +256,13 @@ module ::ArJdbc
|
|
118
256
|
def extract_precision(sql_type)
|
119
257
|
if sql_type == 'money'
|
120
258
|
self.class.money_precision
|
259
|
+
elsif sql_type =~ /timestamp/i
|
260
|
+
$1.to_i if sql_type =~ /\((\d+)\)/
|
121
261
|
else
|
122
262
|
super
|
123
263
|
end
|
124
264
|
end
|
125
|
-
|
265
|
+
|
126
266
|
# Maps PostgreSQL-specific data types to logical Rails types.
|
127
267
|
def simplified_type(field_type)
|
128
268
|
case field_type
|
@@ -160,56 +300,219 @@ module ::ArJdbc
|
|
160
300
|
super
|
161
301
|
end
|
162
302
|
end
|
163
|
-
|
303
|
+
|
304
|
+
def simplified_type(field_type) # :nodoc:
|
305
|
+
case field_type
|
306
|
+
# Numeric and monetary types
|
307
|
+
when /^(?:real|double precision)$/ then :float
|
308
|
+
# Monetary types
|
309
|
+
when 'money' then :decimal
|
310
|
+
when 'hstore' then :hstore
|
311
|
+
when 'ltree' then :ltree
|
312
|
+
# Network address types
|
313
|
+
when 'inet' then :inet
|
314
|
+
when 'cidr' then :cidr
|
315
|
+
when 'macaddr' then :macaddr
|
316
|
+
# Character types
|
317
|
+
when /^(?:character varying|bpchar)(?:\(\d+\))?$/ then :string
|
318
|
+
# Binary data types
|
319
|
+
when 'bytea' then :binary
|
320
|
+
# Date/time types
|
321
|
+
when /^timestamp with(?:out)? time zone$/ then :datetime
|
322
|
+
when /^interval(?:|\(\d+\))$/ then :string
|
323
|
+
# Geometric types
|
324
|
+
when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/ then :string
|
325
|
+
# Bit strings
|
326
|
+
when /^bit(?: varying)?(?:\(\d+\))?$/ then :string
|
327
|
+
# XML type
|
328
|
+
when 'xml' then :xml
|
329
|
+
# tsvector type
|
330
|
+
when 'tsvector' then :tsvector
|
331
|
+
# Arrays
|
332
|
+
when /^\D+\[\]$/ then :string
|
333
|
+
# Object identifier types
|
334
|
+
when 'oid' then :integer
|
335
|
+
# UUID type
|
336
|
+
when 'uuid' then :uuid
|
337
|
+
# JSON type
|
338
|
+
when 'json' then :json
|
339
|
+
# Small and big integer types
|
340
|
+
when /^(?:small|big)int$/ then :integer
|
341
|
+
when /(num|date|tstz|ts|int4|int8)range$/
|
342
|
+
field_type.to_sym
|
343
|
+
# Pass through all types that are not specific to PostgreSQL.
|
344
|
+
else
|
345
|
+
super
|
346
|
+
end
|
347
|
+
end if AR4_COMPAT
|
348
|
+
|
349
|
+
# OID Type::Range helpers :
|
350
|
+
|
351
|
+
def extract_bounds(value)
|
352
|
+
f, t = value[1..-2].split(',')
|
353
|
+
{
|
354
|
+
:from => (value[1] == ',' || f == '-infinity') ? infinity(:negative => true) : f,
|
355
|
+
:to => (value[-2] == ',' || t == 'infinity') ? infinity : t,
|
356
|
+
:exclude_start => (value[0] == '('), :exclude_end => (value[-1] == ')')
|
357
|
+
}
|
358
|
+
end if AR4_COMPAT
|
359
|
+
|
360
|
+
def infinity(options = {})
|
361
|
+
::Float::INFINITY * (options[:negative] ? -1 : 1)
|
362
|
+
end if AR4_COMPAT
|
363
|
+
|
364
|
+
def to_integer(value)
|
365
|
+
(value.respond_to?(:infinite?) && value.infinite?) ? value : value.to_i
|
366
|
+
end if AR4_COMPAT
|
367
|
+
|
368
|
+
end # Column
|
369
|
+
|
370
|
+
ActiveRecordError = ::ActiveRecord::ActiveRecordError # :nodoc:
|
371
|
+
|
372
|
+
# Maps logical Rails types to PostgreSQL-specific data types.
|
373
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
374
|
+
case type.to_sym
|
375
|
+
when :'binary'
|
376
|
+
# PostgreSQL doesn't support limits on binary (bytea) columns.
|
377
|
+
# The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
|
378
|
+
case limit
|
379
|
+
when nil, 0..0x3fffffff; super(type, nil, nil, nil)
|
380
|
+
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
|
381
|
+
end
|
382
|
+
when :'text'
|
383
|
+
# PostgreSQL doesn't support limits on text columns.
|
384
|
+
# The hard limit is 1Gb, according to section 8.3 in the manual.
|
385
|
+
case limit
|
386
|
+
when nil, 0..0x3fffffff; super(type, nil, nil, nil)
|
387
|
+
else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
|
388
|
+
end
|
389
|
+
when :'integer'
|
390
|
+
return 'integer' unless limit
|
164
391
|
|
165
|
-
|
166
|
-
|
392
|
+
case limit
|
393
|
+
when 1, 2; 'smallint'
|
394
|
+
when 3, 4; 'integer'
|
395
|
+
when 5..8; 'bigint'
|
396
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
|
397
|
+
end
|
398
|
+
when :'datetime'
|
399
|
+
return super unless precision
|
167
400
|
|
168
|
-
|
169
|
-
|
401
|
+
case precision
|
402
|
+
when 0..6; "timestamp(#{precision})"
|
403
|
+
else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
404
|
+
end
|
405
|
+
else
|
406
|
+
super
|
407
|
+
end
|
170
408
|
end
|
171
409
|
|
172
|
-
def
|
173
|
-
|
174
|
-
'postgresql' => ::Arel::Visitors::PostgreSQL,
|
175
|
-
'jdbcpostgresql' => ::Arel::Visitors::PostgreSQL,
|
176
|
-
'pg' => ::Arel::Visitors::PostgreSQL
|
177
|
-
}
|
178
|
-
end
|
410
|
+
def type_cast(value, column, array_member = false)
|
411
|
+
return super unless column
|
179
412
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
413
|
+
case value
|
414
|
+
when String
|
415
|
+
return super(value, column) unless 'bytea' == column.sql_type
|
416
|
+
value # { :value => value, :format => 1 }
|
417
|
+
when Array
|
418
|
+
return super(value, column) unless column.array
|
419
|
+
column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
420
|
+
column_class.array_to_string(value, column, self)
|
421
|
+
when NilClass
|
422
|
+
if column.array && array_member
|
423
|
+
'NULL'
|
424
|
+
elsif column.array
|
425
|
+
value
|
426
|
+
else
|
427
|
+
super(value, column)
|
189
428
|
end
|
190
|
-
|
191
|
-
|
429
|
+
when Hash
|
430
|
+
case column.sql_type
|
431
|
+
when 'hstore'
|
432
|
+
column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
433
|
+
column_class.hstore_to_string(value)
|
434
|
+
when 'json'
|
435
|
+
column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
436
|
+
column_class.json_to_string(value)
|
437
|
+
else super(value, column)
|
438
|
+
end
|
439
|
+
when IPAddr
|
440
|
+
return super unless column.sql_type == 'inet' || column.sql_type == 'cidr'
|
441
|
+
column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
442
|
+
column_class.cidr_to_string(value)
|
443
|
+
else
|
444
|
+
super(value, column)
|
445
|
+
end
|
446
|
+
end if AR4_COMPAT
|
447
|
+
|
192
448
|
NATIVE_DATABASE_TYPES = {
|
193
449
|
:primary_key => "serial primary key",
|
194
|
-
:string
|
195
|
-
:text
|
196
|
-
:integer
|
197
|
-
:float
|
198
|
-
:decimal
|
199
|
-
:datetime
|
200
|
-
:timestamp
|
201
|
-
:time
|
202
|
-
:date
|
203
|
-
:binary
|
204
|
-
:boolean
|
205
|
-
:xml
|
206
|
-
:tsvector => { :name => "tsvector" }
|
450
|
+
:string => { :name => "character varying", :limit => 255 },
|
451
|
+
:text => { :name => "text" },
|
452
|
+
:integer => { :name => "integer" },
|
453
|
+
:float => { :name => "float" },
|
454
|
+
:decimal => { :name => "decimal" },
|
455
|
+
:datetime => { :name => "timestamp" },
|
456
|
+
:timestamp => { :name => "timestamp" },
|
457
|
+
:time => { :name => "time" },
|
458
|
+
:date => { :name => "date" },
|
459
|
+
:binary => { :name => "bytea" },
|
460
|
+
:boolean => { :name => "boolean" },
|
461
|
+
:xml => { :name => "xml" },
|
207
462
|
}
|
208
463
|
|
464
|
+
NATIVE_DATABASE_TYPES.update({
|
465
|
+
:tsvector => { :name => "tsvector" },
|
466
|
+
:hstore => { :name => "hstore" },
|
467
|
+
:inet => { :name => "inet" },
|
468
|
+
:cidr => { :name => "cidr" },
|
469
|
+
:macaddr => { :name => "macaddr" },
|
470
|
+
:uuid => { :name => "uuid" },
|
471
|
+
:json => { :name => "json" },
|
472
|
+
:ltree => { :name => "ltree" },
|
473
|
+
# ranges :
|
474
|
+
:daterange => { :name => "daterange" },
|
475
|
+
:numrange => { :name => "numrange" },
|
476
|
+
:tsrange => { :name => "tsrange" },
|
477
|
+
:tstzrange => { :name => "tstzrange" },
|
478
|
+
:int4range => { :name => "int4range" },
|
479
|
+
:int8range => { :name => "int8range" },
|
480
|
+
}) if AR4_COMPAT
|
481
|
+
|
209
482
|
def native_database_types
|
210
483
|
NATIVE_DATABASE_TYPES
|
211
484
|
end
|
212
485
|
|
486
|
+
# Adds `:array` option to the default set provided by the AbstractAdapter
|
487
|
+
def prepare_column_options(column, types)
|
488
|
+
spec = super
|
489
|
+
spec[:array] = 'true' if column.respond_to?(:array) && column.array
|
490
|
+
spec
|
491
|
+
end if AR4_COMPAT
|
492
|
+
|
493
|
+
# Adds `:array` as a valid migration key
|
494
|
+
def migration_keys
|
495
|
+
super + [:array]
|
496
|
+
end if AR4_COMPAT
|
497
|
+
|
498
|
+
def add_column_options!(sql, options)
|
499
|
+
if options[:array] || options[:column].try(:array)
|
500
|
+
sql << '[]'
|
501
|
+
end
|
502
|
+
|
503
|
+
column = options.fetch(:column) { return super }
|
504
|
+
if column.type == :uuid && options[:default] =~ /\(\)/
|
505
|
+
sql << " DEFAULT #{options[:default]}"
|
506
|
+
else
|
507
|
+
super
|
508
|
+
end
|
509
|
+
end if AR4_COMPAT
|
510
|
+
|
511
|
+
# Enable standard-conforming strings if available.
|
512
|
+
def set_standard_conforming_strings # native adapter API compatibility
|
513
|
+
self.standard_conforming_strings=(true)
|
514
|
+
end
|
515
|
+
|
213
516
|
# Enable standard-conforming strings if available.
|
214
517
|
def standard_conforming_strings=(enable)
|
215
518
|
client_min_messages = self.client_min_messages
|
@@ -269,14 +572,27 @@ module ::ArJdbc
|
|
269
572
|
true
|
270
573
|
end
|
271
574
|
|
575
|
+
def supports_transaction_isolation? # :nodoc:
|
576
|
+
true
|
577
|
+
end
|
578
|
+
|
272
579
|
def supports_index_sort_order? # :nodoc:
|
273
580
|
true
|
274
581
|
end
|
275
582
|
|
583
|
+
# Range datatypes weren't introduced until PostgreSQL 9.2
|
584
|
+
def supports_ranges? # :nodoc:
|
585
|
+
postgresql_version >= 90200
|
586
|
+
end if AR4_COMPAT
|
587
|
+
|
276
588
|
def supports_savepoints? # :nodoc:
|
277
589
|
true
|
278
590
|
end
|
279
591
|
|
592
|
+
def supports_transaction_isolation?(level = nil)
|
593
|
+
true
|
594
|
+
end
|
595
|
+
|
280
596
|
def create_savepoint
|
281
597
|
execute("SAVEPOINT #{current_savepoint_name}")
|
282
598
|
end
|
@@ -288,11 +604,48 @@ module ::ArJdbc
|
|
288
604
|
def release_savepoint
|
289
605
|
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
290
606
|
end
|
607
|
+
|
608
|
+
def supports_extensions? # :nodoc:
|
609
|
+
postgresql_version >= 90200
|
610
|
+
end # NOTE: only since AR-4.0 but should not hurt on other versions
|
611
|
+
|
612
|
+
def enable_extension(name)
|
613
|
+
execute("CREATE EXTENSION IF NOT EXISTS \"#{name}\"")
|
614
|
+
end
|
615
|
+
|
616
|
+
def disable_extension(name)
|
617
|
+
execute("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE")
|
618
|
+
end
|
291
619
|
|
620
|
+
def extension_enabled?(name)
|
621
|
+
if supports_extensions?
|
622
|
+
rows = select_rows("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL)", 'SCHEMA')
|
623
|
+
rows.first.first
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
def extensions
|
628
|
+
if supports_extensions?
|
629
|
+
rows = select_rows "SELECT extname from pg_extension", "SCHEMA"
|
630
|
+
rows.map { |row| row.first }
|
631
|
+
else
|
632
|
+
[]
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
# Set the authorized user for this session
|
637
|
+
def session_auth=(user)
|
638
|
+
execute "SET SESSION AUTHORIZATION #{user}"
|
639
|
+
end
|
640
|
+
|
292
641
|
# Returns the configured supported identifier length supported by PostgreSQL,
|
293
642
|
# or report the default of 63 on PostgreSQL 7.x.
|
294
643
|
def table_alias_length
|
295
|
-
@table_alias_length ||= (
|
644
|
+
@table_alias_length ||= (
|
645
|
+
postgresql_version >= 80000 ?
|
646
|
+
select_one('SHOW max_identifier_length')['max_identifier_length'].to_i :
|
647
|
+
63
|
648
|
+
)
|
296
649
|
end
|
297
650
|
|
298
651
|
def default_sequence_name(table_name, pk = nil)
|
@@ -302,21 +655,16 @@ module ::ArJdbc
|
|
302
655
|
|
303
656
|
# Resets sequence to the max value of the table's pk if present.
|
304
657
|
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
|
305
|
-
|
658
|
+
if ! pk || ! sequence
|
306
659
|
default_pk, default_sequence = pk_and_sequence_for(table)
|
307
|
-
pk ||= default_pk
|
308
|
-
sequence ||= default_sequence
|
660
|
+
pk ||= default_pk; sequence ||= default_sequence
|
309
661
|
end
|
310
|
-
if pk
|
311
|
-
|
312
|
-
quoted_sequence = quote_column_name(sequence)
|
662
|
+
if pk && sequence
|
663
|
+
quoted_sequence = quote_column_name(sequence)
|
313
664
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
else
|
318
|
-
@logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
|
319
|
-
end
|
665
|
+
select_value <<-end_sql, 'Reset Sequence'
|
666
|
+
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)
|
667
|
+
end_sql
|
320
668
|
end
|
321
669
|
end
|
322
670
|
|
@@ -324,7 +672,7 @@ module ::ArJdbc
|
|
324
672
|
def pk_and_sequence_for(table) #:nodoc:
|
325
673
|
# First try looking for a sequence with a dependency on the
|
326
674
|
# given table's primary key.
|
327
|
-
result = select(<<-end_sql, 'PK and
|
675
|
+
result = select(<<-end_sql, 'PK and Serial Sequence')[0]
|
328
676
|
SELECT attr.attname, seq.relname
|
329
677
|
FROM pg_class seq,
|
330
678
|
pg_attribute attr,
|
@@ -341,11 +689,11 @@ module ::ArJdbc
|
|
341
689
|
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
342
690
|
end_sql
|
343
691
|
|
344
|
-
if result.nil?
|
692
|
+
if result.nil? || result.empty?
|
345
693
|
# If that fails, try parsing the primary key's default value.
|
346
694
|
# Support the 7.x and 8.0 nextval('foo'::text) as well as
|
347
695
|
# the 8.1+ nextval('foo'::regclass).
|
348
|
-
result = select(<<-end_sql, 'PK and
|
696
|
+
result = select(<<-end_sql, 'PK and Custom Sequence')[0]
|
349
697
|
SELECT attr.attname,
|
350
698
|
CASE
|
351
699
|
WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
|
@@ -363,43 +711,36 @@ module ::ArJdbc
|
|
363
711
|
end_sql
|
364
712
|
end
|
365
713
|
|
366
|
-
[result[
|
714
|
+
[ result['attname'], result['relname'] ]
|
367
715
|
rescue
|
368
716
|
nil
|
369
717
|
end
|
370
|
-
|
371
|
-
# Insert logic for pre-AR-3.1 adapters
|
718
|
+
|
372
719
|
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
if supports_insert_with_returning? && id_value.nil?
|
378
|
-
pk, sequence_name = *pk_and_sequence_for(table) unless pk
|
379
|
-
if pk
|
380
|
-
sql = to_sql(sql, binds)
|
381
|
-
return select_value("#{sql} RETURNING #{quote_column_name(pk)}")
|
382
|
-
end
|
720
|
+
unless pk
|
721
|
+
# Extract the table from the insert sql. Yuck.
|
722
|
+
table_ref = extract_table_ref_from_insert_sql(sql)
|
723
|
+
pk = primary_key(table_ref) if table_ref
|
383
724
|
end
|
384
725
|
|
385
|
-
#
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
726
|
+
if pk && use_insert_returning? # && id_value.nil?
|
727
|
+
select_value("#{to_sql(sql, binds)} RETURNING #{quote_column_name(pk)}")
|
728
|
+
else
|
729
|
+
execute(sql, name, binds) # super
|
730
|
+
unless id_value
|
731
|
+
table_ref ||= extract_table_ref_from_insert_sql(sql)
|
732
|
+
# If neither PK nor sequence name is given, look them up.
|
733
|
+
if table_ref && ! ( pk ||= primary_key(table_ref) ) && ! sequence_name
|
734
|
+
pk, sequence_name = pk_and_sequence_for(table_ref)
|
735
|
+
end
|
736
|
+
# If a PK is given, fallback to default sequence name.
|
737
|
+
# Don't fetch last insert id for a table without a PK.
|
738
|
+
if pk && sequence_name ||= default_sequence_name(table_ref, pk)
|
739
|
+
id_value = last_insert_id(table_ref, sequence_name)
|
740
|
+
end
|
400
741
|
end
|
742
|
+
id_value
|
401
743
|
end
|
402
|
-
id_value
|
403
744
|
end
|
404
745
|
|
405
746
|
# taken from rails postgresql_adapter.rb
|
@@ -411,138 +752,163 @@ module ::ArJdbc
|
|
411
752
|
|
412
753
|
sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
|
413
754
|
|
414
|
-
[sql, binds]
|
755
|
+
[ sql, binds ]
|
415
756
|
end
|
416
757
|
|
417
758
|
def primary_key(table)
|
418
|
-
|
419
|
-
|
759
|
+
result = select(<<-end_sql, 'SCHEMA').first
|
760
|
+
SELECT attr.attname
|
761
|
+
FROM pg_attribute attr
|
762
|
+
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
|
763
|
+
WHERE cons.contype = 'p'
|
764
|
+
AND cons.conrelid = '#{quote_table_name(table)}'::regclass
|
765
|
+
end_sql
|
766
|
+
|
767
|
+
result && result["attname"]
|
768
|
+
# pk_and_sequence = pk_and_sequence_for(table)
|
769
|
+
# pk_and_sequence && pk_and_sequence.first
|
420
770
|
end
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
771
|
+
|
772
|
+
# Returns an array of schema names.
|
773
|
+
def schema_names
|
774
|
+
select_values(
|
775
|
+
"SELECT nspname FROM pg_namespace" <<
|
776
|
+
" WHERE nspname !~ '^pg_.*' AND nspname NOT IN ('information_schema')" <<
|
777
|
+
" ORDER by nspname;",
|
778
|
+
'SCHEMA')
|
428
779
|
end
|
429
|
-
|
780
|
+
|
781
|
+
# Returns true if schema exists.
|
782
|
+
def schema_exists?(name)
|
783
|
+
select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", 'SCHEMA').to_i > 0
|
784
|
+
end
|
785
|
+
|
786
|
+
# Returns the current schema name.
|
787
|
+
def current_schema
|
788
|
+
select_value('SELECT current_schema', 'SCHEMA')
|
789
|
+
end
|
790
|
+
|
430
791
|
# current database name
|
431
792
|
def current_database
|
432
|
-
|
433
|
-
first["database"]
|
793
|
+
select_value('SELECT current_database()', 'SCHEMA')
|
434
794
|
end
|
435
795
|
|
436
|
-
# current database encoding
|
796
|
+
# Returns the current database encoding format.
|
437
797
|
def encoding
|
438
|
-
|
439
|
-
SELECT pg_encoding_to_char(pg_database.encoding)
|
440
|
-
FROM pg_database
|
441
|
-
WHERE pg_database.datname LIKE '#{current_database}'
|
442
|
-
|
798
|
+
select_value(
|
799
|
+
"SELECT pg_encoding_to_char(pg_database.encoding)" <<
|
800
|
+
" FROM pg_database" <<
|
801
|
+
" WHERE pg_database.datname LIKE '#{current_database}'",
|
802
|
+
'SCHEMA')
|
443
803
|
end
|
444
804
|
|
445
|
-
#
|
446
|
-
def
|
447
|
-
|
805
|
+
# Returns the current database collation.
|
806
|
+
def collation
|
807
|
+
select_value(
|
808
|
+
"SELECT pg_database.datcollate" <<
|
809
|
+
" FROM pg_database" <<
|
810
|
+
" WHERE pg_database.datname LIKE '#{current_database}'",
|
811
|
+
'SCHEMA')
|
448
812
|
end
|
449
|
-
|
450
|
-
#
|
451
|
-
def
|
452
|
-
|
813
|
+
|
814
|
+
# Returns the current database ctype.
|
815
|
+
def ctype
|
816
|
+
select_value(
|
817
|
+
"SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'",
|
818
|
+
'SCHEMA')
|
453
819
|
end
|
454
820
|
|
455
|
-
#
|
456
|
-
def
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
AND d.indkey[s.i]=a.attnum
|
470
|
-
ORDER BY i.relname
|
471
|
-
SQL
|
472
|
-
|
473
|
-
current_index = nil
|
474
|
-
indexes = []
|
475
|
-
|
476
|
-
insertion_order = []
|
477
|
-
index_order = nil
|
478
|
-
|
479
|
-
result.each do |row|
|
480
|
-
if current_index != row[0]
|
481
|
-
|
482
|
-
(index_order = row[4].split(' ')).each_with_index{ |v, i| index_order[i] = v.to_i }
|
483
|
-
indexes << ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, row[0], row[1] == "t", [])
|
484
|
-
current_index = row[0]
|
485
|
-
end
|
486
|
-
insertion_order = row[3]
|
487
|
-
ind = index_order.index(insertion_order)
|
488
|
-
indexes.last.columns[ind] = row[2]
|
821
|
+
# Returns the active schema search path.
|
822
|
+
def schema_search_path
|
823
|
+
@schema_search_path ||= select_value('SHOW search_path', 'SCHEMA')
|
824
|
+
end
|
825
|
+
|
826
|
+
# Sets the schema search path to a string of comma-separated schema names.
|
827
|
+
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
828
|
+
# See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
|
829
|
+
#
|
830
|
+
# This should be not be called manually but set in database.yml.
|
831
|
+
def schema_search_path=(schema_csv)
|
832
|
+
if schema_csv
|
833
|
+
execute "SET search_path TO #{schema_csv}"
|
834
|
+
@schema_search_path = schema_csv
|
489
835
|
end
|
490
|
-
|
491
|
-
indexes
|
492
836
|
end
|
493
837
|
|
494
838
|
# take id from result of insert query
|
495
839
|
def last_inserted_id(result)
|
496
|
-
if result.is_a?
|
840
|
+
if result.is_a? Integer
|
497
841
|
result
|
498
842
|
else
|
499
843
|
result.first.first[1]
|
500
844
|
end
|
501
845
|
end
|
502
846
|
|
503
|
-
def last_insert_id(table, sequence_name)
|
504
|
-
|
847
|
+
def last_insert_id(table, sequence_name = nil)
|
848
|
+
sequence_name = table if sequence_name.nil? # AR-4.0 1 argument
|
849
|
+
Integer(select_value("SELECT currval('#{sequence_name}')", 'SQL'))
|
505
850
|
end
|
506
|
-
|
851
|
+
|
507
852
|
def recreate_database(name, options = {})
|
508
853
|
drop_database(name)
|
509
854
|
create_database(name, options)
|
510
855
|
end
|
511
|
-
|
856
|
+
|
857
|
+
# Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
|
858
|
+
# <tt>:encoding</tt>, <tt>:collation</tt>, <tt>:ctype</tt>,
|
859
|
+
# <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
|
860
|
+
# <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
|
861
|
+
#
|
862
|
+
# Example:
|
863
|
+
# create_database config[:database], config
|
864
|
+
# create_database 'foo_development', encoding: 'unicode'
|
512
865
|
def create_database(name, options = {})
|
513
|
-
options = options.
|
514
|
-
|
515
|
-
|
866
|
+
options = { :encoding => 'utf8' }.merge!(options.symbolize_keys)
|
867
|
+
|
868
|
+
option_string = options.sum do |key, value|
|
516
869
|
case key
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
870
|
+
when :owner
|
871
|
+
" OWNER = \"#{value}\""
|
872
|
+
when :template
|
873
|
+
" TEMPLATE = \"#{value}\""
|
874
|
+
when :encoding
|
875
|
+
" ENCODING = '#{value}'"
|
876
|
+
when :collation
|
877
|
+
" LC_COLLATE = '#{value}'"
|
878
|
+
when :ctype
|
879
|
+
" LC_CTYPE = '#{value}'"
|
880
|
+
when :tablespace
|
881
|
+
" TABLESPACE = \"#{value}\""
|
882
|
+
when :connection_limit
|
883
|
+
" CONNECTION LIMIT = #{value}"
|
884
|
+
else
|
885
|
+
""
|
527
886
|
end
|
528
887
|
end
|
529
|
-
|
888
|
+
|
889
|
+
execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
|
530
890
|
end
|
531
891
|
|
532
892
|
def drop_database(name)
|
533
|
-
execute "DROP DATABASE IF EXISTS
|
893
|
+
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
534
894
|
end
|
535
895
|
|
536
|
-
|
537
|
-
|
896
|
+
# Creates a schema for the given schema name.
|
897
|
+
def create_schema(schema_name, pg_username = nil)
|
898
|
+
if pg_username.nil? # AR 4.0 compatibility - accepts only single argument
|
899
|
+
execute "CREATE SCHEMA #{schema_name}"
|
900
|
+
else
|
901
|
+
execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
|
902
|
+
end
|
538
903
|
end
|
539
904
|
|
540
|
-
|
541
|
-
|
905
|
+
# Drops the schema for the given schema name.
|
906
|
+
def drop_schema schema_name
|
907
|
+
execute "DROP SCHEMA #{schema_name} CASCADE"
|
542
908
|
end
|
543
909
|
|
544
910
|
def all_schemas
|
545
|
-
select('
|
911
|
+
select('SELECT nspname FROM pg_namespace').map { |row| row["nspname"] }
|
546
912
|
end
|
547
913
|
|
548
914
|
def structure_dump
|
@@ -572,31 +938,9 @@ module ::ArJdbc
|
|
572
938
|
end
|
573
939
|
end
|
574
940
|
|
575
|
-
# Sets the schema search path to a string of comma-separated schema names.
|
576
|
-
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
577
|
-
# See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
|
578
|
-
#
|
579
|
-
# This should be not be called manually but set in database.yml.
|
580
|
-
def schema_search_path=(schema_csv)
|
581
|
-
if schema_csv
|
582
|
-
execute "SET search_path TO #{schema_csv}"
|
583
|
-
@schema_search_path = schema_csv
|
584
|
-
end
|
585
|
-
end
|
586
|
-
|
587
|
-
# Returns the active schema search path.
|
588
|
-
def schema_search_path
|
589
|
-
@schema_search_path ||= exec_query('SHOW search_path', 'SCHEMA')[0]['search_path']
|
590
|
-
end
|
591
|
-
|
592
|
-
# Returns the current schema name.
|
593
|
-
def current_schema
|
594
|
-
exec_query('SELECT current_schema', 'SCHEMA')[0]["current_schema"]
|
595
|
-
end
|
596
|
-
|
597
941
|
# Returns the current client message level.
|
598
942
|
def client_min_messages
|
599
|
-
|
943
|
+
select_value('SHOW client_min_messages', 'SCHEMA')
|
600
944
|
end
|
601
945
|
|
602
946
|
# Set the client message level.
|
@@ -604,6 +948,16 @@ module ::ArJdbc
|
|
604
948
|
execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
|
605
949
|
end
|
606
950
|
|
951
|
+
# Gets the maximum number columns postgres has, default 32
|
952
|
+
def multi_column_index_limit
|
953
|
+
defined?(@multi_column_index_limit) && @multi_column_index_limit || 32
|
954
|
+
end
|
955
|
+
|
956
|
+
# Sets the maximum number columns postgres has, default 32
|
957
|
+
def multi_column_index_limit=(limit)
|
958
|
+
@multi_column_index_limit = limit
|
959
|
+
end
|
960
|
+
|
607
961
|
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
|
608
962
|
#
|
609
963
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
@@ -637,22 +991,33 @@ module ::ArJdbc
|
|
637
991
|
sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
|
638
992
|
end
|
639
993
|
|
640
|
-
# from postgres_adapter.rb in rails project
|
641
|
-
# https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L412
|
642
|
-
# Quotes PostgreSQL-specific data types for SQL input.
|
643
994
|
def quote(value, column = nil) #:nodoc:
|
644
995
|
return super unless column
|
645
996
|
|
997
|
+
# TODO recent 4.0 (master) seems to be passing a ColumnDefinition here :
|
998
|
+
# NoMethodError: undefined method `sql_type' for #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::ColumnDefinition:0x634f6b>
|
999
|
+
# .../activerecord-jdbc-adapter/lib/arjdbc/postgresql/adapter.rb:1014:in `quote'
|
1000
|
+
# .../gems/rails-817e8fad5a84/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb:698:in `add_column_options!'
|
1001
|
+
# .../activerecord-jdbc-adapter/lib/arjdbc/postgresql/adapter.rb:507:in `add_column_options!'
|
1002
|
+
# .../gems/rails-817e8fad5a84/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:168:in `add_column_options!'
|
1003
|
+
# .../gems/rails-817e8fad5a84/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:135:in `visit_ColumnDefinition'
|
1004
|
+
sql_type = column.sql_type rescue nil
|
1005
|
+
|
646
1006
|
case value
|
647
1007
|
when Float
|
648
|
-
|
649
|
-
|
1008
|
+
if value.infinite? && column.type == :datetime
|
1009
|
+
"'#{value.to_s.downcase}'"
|
1010
|
+
elsif value.infinite? || value.nan?
|
1011
|
+
"'#{value.to_s}'"
|
1012
|
+
else
|
1013
|
+
super
|
1014
|
+
end
|
650
1015
|
when Numeric
|
651
|
-
return super unless
|
1016
|
+
return super unless sql_type == 'money'
|
652
1017
|
# Not truly string input, so doesn't require (or allow) escape string syntax.
|
653
|
-
"'#{value}'"
|
1018
|
+
( column.type == :string || column.type == :text ) ? "'#{value}'" : super
|
654
1019
|
when String
|
655
|
-
case
|
1020
|
+
case sql_type
|
656
1021
|
when 'bytea' then "E'#{escape_bytea(value)}'::bytea" # "'#{escape_bytea(value)}'"
|
657
1022
|
when 'xml' then "xml '#{quote_string(value)}'"
|
658
1023
|
when /^bit/
|
@@ -669,6 +1034,35 @@ module ::ArJdbc
|
|
669
1034
|
else
|
670
1035
|
super
|
671
1036
|
end
|
1037
|
+
when Array
|
1038
|
+
if column.array && AR4_COMPAT
|
1039
|
+
column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
1040
|
+
"'#{column_class.array_to_string(value, column, self)}'"
|
1041
|
+
else
|
1042
|
+
super
|
1043
|
+
end
|
1044
|
+
when Hash
|
1045
|
+
if sql_type == 'hstore' && AR4_COMPAT
|
1046
|
+
column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
1047
|
+
super(column_class.hstore_to_string(value), column)
|
1048
|
+
elsif sql_type == 'json' && AR4_COMPAT
|
1049
|
+
column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
1050
|
+
super(column_class.json_to_string(value), column)
|
1051
|
+
else super
|
1052
|
+
end
|
1053
|
+
when Range
|
1054
|
+
if /range$/ =~ sql_type && AR4_COMPAT
|
1055
|
+
column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
1056
|
+
"'#{column_class.range_to_string(value)}'::#{sql_type}"
|
1057
|
+
else
|
1058
|
+
super
|
1059
|
+
end
|
1060
|
+
when IPAddr
|
1061
|
+
if (sql_type == 'inet' || sql_type == 'cidr') && AR4_COMPAT
|
1062
|
+
column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
1063
|
+
super(column_class.cidr_to_string(value), column)
|
1064
|
+
else super
|
1065
|
+
end
|
672
1066
|
else
|
673
1067
|
super
|
674
1068
|
end
|
@@ -711,30 +1105,50 @@ module ::ArJdbc
|
|
711
1105
|
%("#{name.to_s.gsub("\"", "\"\"")}")
|
712
1106
|
end
|
713
1107
|
|
1108
|
+
# Quote date/time values for use in SQL input.
|
1109
|
+
# Includes microseconds if the value is a Time responding to usec.
|
714
1110
|
def quoted_date(value) #:nodoc:
|
1111
|
+
result = super
|
715
1112
|
if value.acts_like?(:time) && value.respond_to?(:usec)
|
716
|
-
"#{
|
717
|
-
else
|
718
|
-
super
|
1113
|
+
"#{result}.#{sprintf("%06d", value.usec)}"
|
719
1114
|
end
|
1115
|
+
result = "#{result.sub(/^-/, '')} BC" if value.year < 0
|
1116
|
+
result
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
def supports_disable_referential_integrity? # :nodoc:
|
1120
|
+
true
|
720
1121
|
end
|
721
1122
|
|
722
1123
|
def disable_referential_integrity # :nodoc:
|
723
|
-
|
1124
|
+
if supports_disable_referential_integrity?
|
1125
|
+
begin
|
1126
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
|
1127
|
+
rescue
|
1128
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER USER" }.join(";"))
|
1129
|
+
end
|
1130
|
+
end
|
724
1131
|
yield
|
725
1132
|
ensure
|
726
|
-
|
1133
|
+
if supports_disable_referential_integrity?
|
1134
|
+
begin
|
1135
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
|
1136
|
+
rescue
|
1137
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER USER" }.join(";"))
|
1138
|
+
end
|
1139
|
+
end
|
727
1140
|
end
|
728
1141
|
|
729
|
-
def rename_table(
|
730
|
-
execute "ALTER TABLE #{
|
1142
|
+
def rename_table(table_name, new_name)
|
1143
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
731
1144
|
pk, seq = pk_and_sequence_for(new_name)
|
732
|
-
if seq == "#{
|
1145
|
+
if seq == "#{table_name}_#{pk}_seq"
|
733
1146
|
new_seq = "#{new_name}_#{pk}_seq"
|
734
1147
|
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
|
735
1148
|
end
|
1149
|
+
rename_table_indexes(table_name, new_name) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements
|
736
1150
|
end
|
737
|
-
|
1151
|
+
|
738
1152
|
# Adds a new column to the named table.
|
739
1153
|
# See TableDefinition#column for details of the options you can use.
|
740
1154
|
def add_column(table_name, column_name, type, options = {})
|
@@ -786,8 +1200,9 @@ module ::ArJdbc
|
|
786
1200
|
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
787
1201
|
end
|
788
1202
|
|
789
|
-
def rename_column(table_name, column_name, new_column_name)
|
1203
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
790
1204
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
1205
|
+
rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements
|
791
1206
|
end
|
792
1207
|
|
793
1208
|
def remove_index!(table_name, index_name) #:nodoc:
|
@@ -798,64 +1213,128 @@ module ::ArJdbc
|
|
798
1213
|
63
|
799
1214
|
end
|
800
1215
|
|
801
|
-
#
|
802
|
-
def
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
else
|
815
|
-
super
|
1216
|
+
# Returns the list of all column definitions for a table.
|
1217
|
+
def columns(table_name, name = nil)
|
1218
|
+
klass = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
1219
|
+
column_definitions(table_name).map do |row|
|
1220
|
+
# name, type, default, notnull, oid, fmod
|
1221
|
+
name = row[0]; type = row[1]; default = row[2]
|
1222
|
+
notnull = row[3]; oid = row[4]; fmod = row[5]
|
1223
|
+
# oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) { OID::Identity.new }
|
1224
|
+
notnull = notnull == 't' if notnull.is_a?(String) # JDBC gets true/false
|
1225
|
+
# for ID columns we get a bit of non-sense default :
|
1226
|
+
# e.g. "nextval('mixed_cases_id_seq'::regclass"
|
1227
|
+
default = nil if default =~ /^nextval\(.*?\:\:regclass\)$/
|
1228
|
+
klass.new(name, default, oid, type, ! notnull)
|
816
1229
|
end
|
817
1230
|
end
|
818
1231
|
|
1232
|
+
# Returns the list of a table's column names, data types, and default values.
|
1233
|
+
#
|
1234
|
+
# If the table name is not prefixed with a schema, the database will
|
1235
|
+
# take the first match from the schema search path.
|
1236
|
+
#
|
1237
|
+
# Query implementation notes:
|
1238
|
+
# - format_type includes the column size constraint, e.g. varchar(50)
|
1239
|
+
# - ::regclass is a function that gives the id for a table name
|
1240
|
+
def column_definitions(table_name) #:nodoc:
|
1241
|
+
select_rows(<<-end_sql, 'SCHEMA')
|
1242
|
+
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
1243
|
+
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
|
1244
|
+
FROM pg_attribute a LEFT JOIN pg_attrdef d
|
1245
|
+
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
1246
|
+
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
|
1247
|
+
AND a.attnum > 0 AND NOT a.attisdropped
|
1248
|
+
ORDER BY a.attnum
|
1249
|
+
end_sql
|
1250
|
+
end
|
1251
|
+
private :column_definitions
|
1252
|
+
|
819
1253
|
def tables(name = nil)
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
1254
|
+
select_values(<<-SQL, 'SCHEMA')
|
1255
|
+
SELECT tablename
|
1256
|
+
FROM pg_tables
|
1257
|
+
WHERE schemaname = ANY (current_schemas(false))
|
824
1258
|
SQL
|
825
1259
|
end
|
826
1260
|
|
827
1261
|
def table_exists?(name)
|
828
1262
|
schema, table = extract_schema_and_table(name.to_s)
|
829
|
-
return false unless table #
|
830
|
-
|
831
|
-
binds = [[nil, table.gsub(/(^"|"$)/,'')]]
|
832
|
-
binds << [nil, schema] if schema
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
1263
|
+
return false unless table # abstract classes - nil table name
|
1264
|
+
|
1265
|
+
binds = [[ nil, table.gsub(/(^"|"$)/,'') ]]
|
1266
|
+
binds << [ nil, schema ] if schema
|
1267
|
+
|
1268
|
+
exec_query_raw(<<-SQL, 'SCHEMA', binds).first["table_count"] > 0
|
1269
|
+
SELECT COUNT(*) as table_count
|
1270
|
+
FROM pg_tables
|
1271
|
+
WHERE tablename = ?
|
1272
|
+
AND schemaname = #{schema ? "?" : "ANY (current_schemas(false))"}
|
839
1273
|
SQL
|
840
1274
|
end
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
1275
|
+
|
1276
|
+
IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition # :nodoc:
|
1277
|
+
if ActiveRecord::VERSION::MAJOR < 3 ||
|
1278
|
+
( ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR <= 1 )
|
1279
|
+
# NOTE: make sure we accept 6 arguments (>= 3.2) as well as 5 (<= 3.1) :
|
1280
|
+
# allow 6 on 3.1 : Struct.new(:table, :name, :unique, :columns, :lengths)
|
1281
|
+
IndexDefinition.class_eval do
|
1282
|
+
def initialize(table, name, unique = nil, columns = nil, lengths = nil, orders = nil)
|
1283
|
+
super(table, name, unique, columns, lengths) # @see {#indexes}
|
1284
|
+
end
|
849
1285
|
end
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
# Returns an array of indexes for the given table.
|
1289
|
+
def indexes(table_name, name = nil)
|
1290
|
+
# NOTE: maybe it's better to leave things of to the JDBC API ?!
|
1291
|
+
result = select_rows(<<-SQL, 'SCHEMA')
|
1292
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
|
1293
|
+
FROM pg_class t
|
1294
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
1295
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
1296
|
+
WHERE i.relkind = 'i'
|
1297
|
+
AND d.indisprimary = 'f'
|
1298
|
+
AND t.relname = '#{table_name}'
|
1299
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
|
1300
|
+
ORDER BY i.relname
|
1301
|
+
SQL
|
1302
|
+
|
1303
|
+
result.map! do |row|
|
1304
|
+
index_name = row[0]
|
1305
|
+
unique = row[1].is_a?(String) ? row[1] == 't' : row[1] # JDBC gets us a boolean
|
1306
|
+
indkey = row[2].is_a?(Java::OrgPostgresqlUtil::PGobject) ? row[2].value : row[2]
|
1307
|
+
indkey = indkey.split(" ")
|
1308
|
+
inddef = row[3]
|
1309
|
+
oid = row[4]
|
1310
|
+
|
1311
|
+
columns = select_rows(<<-SQL, "SCHEMA")
|
1312
|
+
SELECT a.attnum, a.attname
|
1313
|
+
FROM pg_attribute a
|
1314
|
+
WHERE a.attrelid = #{oid}
|
1315
|
+
AND a.attnum IN (#{indkey.join(",")})
|
1316
|
+
SQL
|
1317
|
+
|
1318
|
+
columns = Hash[ columns.each { |column| column[0] = column[0].to_s } ]
|
1319
|
+
column_names = columns.values_at(*indkey).compact
|
850
1320
|
|
851
|
-
|
852
|
-
|
853
|
-
|
1321
|
+
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
|
1322
|
+
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
|
1323
|
+
orders = desc_order_columns.any? ? Hash[ desc_order_columns.map { |column| [column, :desc] } ] : {}
|
1324
|
+
|
1325
|
+
column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
|
854
1326
|
end
|
855
|
-
|
1327
|
+
result.compact!
|
1328
|
+
result
|
856
1329
|
end
|
857
1330
|
|
1331
|
+
# #override due RETURNING clause - can't do an {#execute_insert}
|
1332
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) # :nodoc:
|
1333
|
+
execute(sql, name, binds)
|
1334
|
+
end
|
1335
|
+
|
858
1336
|
private
|
1337
|
+
|
859
1338
|
def translate_exception(exception, message)
|
860
1339
|
case exception.message
|
861
1340
|
when /duplicate key value violates unique constraint/
|
@@ -866,42 +1345,29 @@ module ::ArJdbc
|
|
866
1345
|
super
|
867
1346
|
end
|
868
1347
|
end
|
1348
|
+
|
1349
|
+
# Extracts the table and schema name from +name+
|
1350
|
+
def extract_schema_and_table(name)
|
1351
|
+
schema, table = name.split('.', 2)
|
869
1352
|
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
# ORDER BY column.num
|
881
|
-
#
|
882
|
-
# If the table name is not prefixed with a schema, the database will
|
883
|
-
# take the first match from the schema search path.
|
884
|
-
#
|
885
|
-
# Query implementation notes:
|
886
|
-
# - format_type includes the column size constraint, e.g. varchar(50)
|
887
|
-
# - ::regclass is a function that gives the id for a table name
|
888
|
-
def column_definitions(table_name) #:nodoc:
|
889
|
-
exec_query(<<-end_sql, 'SCHEMA')
|
890
|
-
SELECT a.attname as column_name, format_type(a.atttypid, a.atttypmod) as column_type, d.adsrc as column_default, a.attnotnull as column_not_null
|
891
|
-
FROM pg_attribute a LEFT JOIN pg_attrdef d
|
892
|
-
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
893
|
-
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
|
894
|
-
AND a.attnum > 0 AND NOT a.attisdropped
|
895
|
-
ORDER BY a.attnum
|
896
|
-
end_sql
|
1353
|
+
unless table # A table was provided without a schema
|
1354
|
+
table = schema
|
1355
|
+
schema = nil
|
1356
|
+
end
|
1357
|
+
|
1358
|
+
if name =~ /^"/ # Handle quoted table names
|
1359
|
+
table = name
|
1360
|
+
schema = nil
|
1361
|
+
end
|
1362
|
+
[schema, table]
|
897
1363
|
end
|
898
1364
|
|
899
1365
|
def extract_pg_identifier_from_name(name)
|
900
|
-
match_data = name[0,1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
|
1366
|
+
match_data = name[0, 1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
|
901
1367
|
|
902
1368
|
if match_data
|
903
1369
|
rest = name[match_data[0].length..-1]
|
904
|
-
rest = rest[1..-1] if rest[0,1] == "."
|
1370
|
+
rest = rest[1..-1] if rest[0, 1] == "."
|
905
1371
|
[match_data[1], (rest.length > 0 ? rest : nil)]
|
906
1372
|
end
|
907
1373
|
end
|
@@ -910,75 +1376,193 @@ module ::ArJdbc
|
|
910
1376
|
def extract_table_ref_from_insert_sql(sql) # :nodoc:
|
911
1377
|
sql[/into\s+([^\(]*).*values\s*\(/i]
|
912
1378
|
$1.strip if $1
|
1379
|
+
# sql.split(" ", 4)[2].gsub('"', '')
|
913
1380
|
end
|
914
1381
|
|
915
1382
|
end
|
916
1383
|
end
|
917
1384
|
|
918
1385
|
module ActiveRecord::ConnectionAdapters
|
919
|
-
|
1386
|
+
|
1387
|
+
PostgreSQLJdbcConnection.class_eval do
|
1388
|
+
|
1389
|
+
# alias :java_native_database_types :set_native_database_types
|
1390
|
+
|
1391
|
+
# @override to prevent connection from loading hash from JDBC meta-data,
|
1392
|
+
# which can be expensive. We can do this since {#native_database_types} is
|
1393
|
+
# defined in the adapter to use a hash not relying on driver's meta-data
|
1394
|
+
def set_native_database_types; @native_types = {}; end
|
1395
|
+
|
1396
|
+
end
|
1397
|
+
|
1398
|
+
remove_const(:PostgreSQLColumn) if const_defined?(:PostgreSQLColumn)
|
920
1399
|
|
921
1400
|
class PostgreSQLColumn < JdbcColumn
|
922
1401
|
include ArJdbc::PostgreSQL::Column
|
923
|
-
|
924
|
-
def initialize(name,
|
925
|
-
|
926
|
-
|
1402
|
+
|
1403
|
+
def initialize(name, default, oid_type, sql_type = nil, null = true)
|
1404
|
+
@oid_type = oid_type
|
1405
|
+
if sql_type =~ /\[\]$/
|
1406
|
+
@array = true
|
1407
|
+
super(name, default, sql_type[0..sql_type.length - 3], null)
|
927
1408
|
else
|
928
|
-
|
1409
|
+
@array = false
|
1410
|
+
super(name, default, sql_type, null)
|
929
1411
|
end
|
930
1412
|
end
|
931
|
-
|
932
|
-
def call_discovered_column_callbacks(*)
|
933
|
-
end
|
934
|
-
end
|
935
|
-
|
936
|
-
class PostgresJdbcConnection < JdbcConnection
|
937
|
-
alias :java_native_database_types :set_native_database_types
|
938
|
-
|
939
|
-
# override to prevent connection from loading hash from jdbc
|
940
|
-
# metadata, which can be expensive. We can do this since
|
941
|
-
# native_database_types is defined in the adapter to use a static hash
|
942
|
-
# not relying on the driver's metadata
|
943
|
-
def set_native_database_types
|
944
|
-
@native_types = {}
|
945
|
-
end
|
1413
|
+
|
946
1414
|
end
|
947
1415
|
|
1416
|
+
remove_const(:PostgreSQLAdapter) if const_defined?(:PostgreSQLAdapter)
|
1417
|
+
|
948
1418
|
class PostgreSQLAdapter < JdbcAdapter
|
949
1419
|
include ArJdbc::PostgreSQL
|
950
1420
|
include ArJdbc::PostgreSQL::ExplainSupport
|
951
1421
|
|
952
1422
|
def initialize(*args)
|
953
1423
|
super
|
1424
|
+
|
1425
|
+
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
1426
|
+
@local_tz = nil
|
1427
|
+
@table_alias_length = nil
|
1428
|
+
|
954
1429
|
configure_connection
|
1430
|
+
|
1431
|
+
@local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
|
1432
|
+
@use_insert_returning = config.key?(:insert_returning) ?
|
1433
|
+
self.class.type_cast_config_to_boolean(config[:insert_returning]) : nil
|
955
1434
|
end
|
956
1435
|
|
957
|
-
class
|
1436
|
+
class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
|
1437
|
+
attr_accessor :array
|
1438
|
+
end
|
1439
|
+
|
1440
|
+
module ColumnMethods
|
958
1441
|
def xml(*args)
|
959
1442
|
options = args.extract_options!
|
960
|
-
column(args[0],
|
1443
|
+
column(args[0], 'xml', options)
|
961
1444
|
end
|
962
1445
|
|
963
1446
|
def tsvector(*args)
|
964
1447
|
options = args.extract_options!
|
965
|
-
column(args[0],
|
1448
|
+
column(args[0], 'tsvector', options)
|
1449
|
+
end
|
1450
|
+
|
1451
|
+
def int4range(name, options = {})
|
1452
|
+
column(name, 'int4range', options)
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
def int8range(name, options = {})
|
1456
|
+
column(name, 'int8range', options)
|
1457
|
+
end
|
1458
|
+
|
1459
|
+
def tsrange(name, options = {})
|
1460
|
+
column(name, 'tsrange', options)
|
1461
|
+
end
|
1462
|
+
|
1463
|
+
def tstzrange(name, options = {})
|
1464
|
+
column(name, 'tstzrange', options)
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
def numrange(name, options = {})
|
1468
|
+
column(name, 'numrange', options)
|
1469
|
+
end
|
1470
|
+
|
1471
|
+
def daterange(name, options = {})
|
1472
|
+
column(name, 'daterange', options)
|
1473
|
+
end
|
1474
|
+
|
1475
|
+
def hstore(name, options = {})
|
1476
|
+
column(name, 'hstore', options)
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
def ltree(name, options = {})
|
1480
|
+
column(name, 'ltree', options)
|
1481
|
+
end
|
1482
|
+
|
1483
|
+
def inet(name, options = {})
|
1484
|
+
column(name, 'inet', options)
|
1485
|
+
end
|
1486
|
+
|
1487
|
+
def cidr(name, options = {})
|
1488
|
+
column(name, 'cidr', options)
|
1489
|
+
end
|
1490
|
+
|
1491
|
+
def macaddr(name, options = {})
|
1492
|
+
column(name, 'macaddr', options)
|
1493
|
+
end
|
1494
|
+
|
1495
|
+
def uuid(name, options = {})
|
1496
|
+
column(name, 'uuid', options)
|
1497
|
+
end
|
1498
|
+
|
1499
|
+
def json(name, options = {})
|
1500
|
+
column(name, 'json', options)
|
966
1501
|
end
|
967
1502
|
end
|
968
1503
|
|
969
|
-
|
970
|
-
|
1504
|
+
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
1505
|
+
include ColumnMethods
|
1506
|
+
|
1507
|
+
def primary_key(name, type = :primary_key, options = {})
|
1508
|
+
return super unless type == :uuid
|
1509
|
+
options[:default] ||= 'uuid_generate_v4()'
|
1510
|
+
options[:primary_key] = true
|
1511
|
+
column name, type, options
|
1512
|
+
end if ActiveRecord::VERSION::MAJOR > 3 # 3.2 super expects (name)
|
1513
|
+
|
1514
|
+
def column(name, type = nil, options = {})
|
1515
|
+
super
|
1516
|
+
column = self[name]
|
1517
|
+
# NOTE: <= 3.1 no #new_column_definition hard-coded ColumnDef.new :
|
1518
|
+
# column = self[name] || ColumnDefinition.new(@base, name, type)
|
1519
|
+
# thus we simply do not support array column definitions on <= 3.1
|
1520
|
+
if column.is_a?(ColumnDefinition)
|
1521
|
+
column.array = options[:array]
|
1522
|
+
end
|
1523
|
+
self
|
1524
|
+
end
|
1525
|
+
|
1526
|
+
private
|
1527
|
+
|
1528
|
+
if ActiveRecord::VERSION::MAJOR > 3
|
1529
|
+
|
1530
|
+
def create_column_definition(name, type)
|
1531
|
+
ColumnDefinition.new name, type
|
1532
|
+
end
|
1533
|
+
|
1534
|
+
else # no #create_column_definition on 3.2
|
1535
|
+
|
1536
|
+
def new_column_definition(base, name, type)
|
1537
|
+
definition = ColumnDefinition.new base, name, type
|
1538
|
+
@columns << definition
|
1539
|
+
@columns_hash[name] = definition
|
1540
|
+
definition
|
1541
|
+
end
|
1542
|
+
|
1543
|
+
end
|
1544
|
+
|
971
1545
|
end
|
972
1546
|
|
1547
|
+
def table_definition(*args)
|
1548
|
+
new_table_definition(TableDefinition, *args)
|
1549
|
+
end
|
1550
|
+
|
1551
|
+
class Table < ActiveRecord::ConnectionAdapters::Table
|
1552
|
+
include ColumnMethods
|
1553
|
+
end
|
1554
|
+
|
1555
|
+
def update_table_definition(table_name, base)
|
1556
|
+
Table.new(table_name, base)
|
1557
|
+
end if ActiveRecord::VERSION::MAJOR > 3
|
1558
|
+
|
973
1559
|
def jdbc_connection_class(spec)
|
974
1560
|
::ArJdbc::PostgreSQL.jdbc_connection_class
|
975
1561
|
end
|
976
1562
|
|
977
1563
|
def jdbc_column_class
|
978
|
-
ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
1564
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
979
1565
|
end
|
980
|
-
|
981
|
-
alias_chained_method :columns, :query_cache, :pg_columns
|
982
1566
|
|
983
1567
|
# some QUOTING caching :
|
984
1568
|
|
@@ -1006,4 +1590,4 @@ module ActiveRecord::ConnectionAdapters
|
|
1006
1590
|
end
|
1007
1591
|
|
1008
1592
|
# Don't need to load native postgres adapter
|
1009
|
-
$LOADED_FEATURES << 'active_record/connection_adapters/postgresql_adapter.rb'
|
1593
|
+
$LOADED_FEATURES << 'active_record/connection_adapters/postgresql_adapter.rb'
|