activerecord-jdbc-adapter-ficoh 1.3.21-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/.travis.yml +462 -0
- data/.yardopts +4 -0
- data/Appraisals +36 -0
- data/CONTRIBUTING.md +49 -0
- data/Gemfile +68 -0
- data/History.md +1191 -0
- data/LICENSE.txt +25 -0
- data/README.md +277 -0
- data/RUNNING_TESTS.md +88 -0
- data/Rakefile +298 -0
- data/Rakefile.jdbc +20 -0
- data/activerecord-jdbc-adapter.gemspec +63 -0
- data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
- 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 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
- data/lib/activerecord-jdbc-adapter.rb +1 -0
- data/lib/arel/visitors/compat.rb +64 -0
- data/lib/arel/visitors/db2.rb +137 -0
- data/lib/arel/visitors/derby.rb +112 -0
- data/lib/arel/visitors/firebird.rb +79 -0
- data/lib/arel/visitors/h2.rb +25 -0
- data/lib/arel/visitors/hsqldb.rb +32 -0
- data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
- data/lib/arel/visitors/sql_server.rb +225 -0
- data/lib/arel/visitors/sql_server/ng42.rb +293 -0
- data/lib/arjdbc.rb +22 -0
- data/lib/arjdbc/db2.rb +4 -0
- data/lib/arjdbc/db2/adapter.rb +802 -0
- data/lib/arjdbc/db2/as400.rb +137 -0
- data/lib/arjdbc/db2/column.rb +177 -0
- data/lib/arjdbc/db2/connection_methods.rb +45 -0
- data/lib/arjdbc/derby.rb +3 -0
- data/lib/arjdbc/derby/active_record_patch.rb +13 -0
- data/lib/arjdbc/derby/adapter.rb +567 -0
- data/lib/arjdbc/derby/connection_methods.rb +16 -0
- data/lib/arjdbc/derby/schema_creation.rb +15 -0
- data/lib/arjdbc/discover.rb +104 -0
- data/lib/arjdbc/firebird.rb +4 -0
- data/lib/arjdbc/firebird/adapter.rb +468 -0
- data/lib/arjdbc/firebird/connection_methods.rb +20 -0
- data/lib/arjdbc/h2.rb +3 -0
- data/lib/arjdbc/h2/adapter.rb +335 -0
- data/lib/arjdbc/h2/connection_methods.rb +22 -0
- data/lib/arjdbc/hsqldb.rb +3 -0
- data/lib/arjdbc/hsqldb/adapter.rb +304 -0
- data/lib/arjdbc/hsqldb/connection_methods.rb +23 -0
- data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
- data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
- data/lib/arjdbc/informix.rb +5 -0
- data/lib/arjdbc/informix/adapter.rb +160 -0
- data/lib/arjdbc/informix/connection_methods.rb +9 -0
- data/lib/arjdbc/jdbc.rb +62 -0
- data/lib/arjdbc/jdbc/adapter.rb +997 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
- data/lib/arjdbc/jdbc/arel_support.rb +149 -0
- data/lib/arjdbc/jdbc/base_ext.rb +34 -0
- data/lib/arjdbc/jdbc/callbacks.rb +52 -0
- data/lib/arjdbc/jdbc/column.rb +83 -0
- data/lib/arjdbc/jdbc/connection.rb +26 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +59 -0
- data/lib/arjdbc/jdbc/driver.rb +44 -0
- data/lib/arjdbc/jdbc/error.rb +75 -0
- data/lib/arjdbc/jdbc/extension.rb +69 -0
- data/lib/arjdbc/jdbc/java.rb +13 -0
- data/lib/arjdbc/jdbc/type_cast.rb +154 -0
- data/lib/arjdbc/jdbc/type_converter.rb +142 -0
- data/lib/arjdbc/mssql.rb +7 -0
- data/lib/arjdbc/mssql/adapter.rb +822 -0
- data/lib/arjdbc/mssql/column.rb +207 -0
- data/lib/arjdbc/mssql/connection_methods.rb +72 -0
- data/lib/arjdbc/mssql/explain_support.rb +99 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
- data/lib/arjdbc/mssql/lock_methods.rb +77 -0
- data/lib/arjdbc/mssql/types.rb +343 -0
- data/lib/arjdbc/mssql/utils.rb +82 -0
- data/lib/arjdbc/mysql.rb +3 -0
- data/lib/arjdbc/mysql/adapter.rb +998 -0
- data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
- data/lib/arjdbc/mysql/column.rb +167 -0
- data/lib/arjdbc/mysql/connection_methods.rb +137 -0
- data/lib/arjdbc/mysql/explain_support.rb +82 -0
- data/lib/arjdbc/mysql/schema_creation.rb +58 -0
- data/lib/arjdbc/oracle.rb +4 -0
- data/lib/arjdbc/oracle/adapter.rb +968 -0
- data/lib/arjdbc/oracle/column.rb +136 -0
- data/lib/arjdbc/oracle/connection_methods.rb +21 -0
- data/lib/arjdbc/postgresql.rb +3 -0
- data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
- data/lib/arjdbc/postgresql/adapter.rb +1498 -0
- data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
- data/lib/arjdbc/postgresql/base/oid.rb +412 -0
- data/lib/arjdbc/postgresql/base/pgconn.rb +8 -0
- data/lib/arjdbc/postgresql/base/schema_definitions.rb +132 -0
- data/lib/arjdbc/postgresql/column.rb +640 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +44 -0
- data/lib/arjdbc/postgresql/explain_support.rb +53 -0
- data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
- data/lib/arjdbc/postgresql/oid_types.rb +265 -0
- data/lib/arjdbc/postgresql/schema_creation.rb +60 -0
- data/lib/arjdbc/railtie.rb +11 -0
- data/lib/arjdbc/sqlite3.rb +3 -0
- data/lib/arjdbc/sqlite3/adapter.rb +654 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +36 -0
- data/lib/arjdbc/sqlite3/explain_support.rb +29 -0
- data/lib/arjdbc/sybase.rb +2 -0
- data/lib/arjdbc/sybase/adapter.rb +47 -0
- data/lib/arjdbc/tasks.rb +13 -0
- data/lib/arjdbc/tasks/database_tasks.rb +66 -0
- data/lib/arjdbc/tasks/databases.rake +91 -0
- data/lib/arjdbc/tasks/databases3.rake +239 -0
- data/lib/arjdbc/tasks/databases4.rake +39 -0
- data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
- data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
- data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
- data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
- data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
- data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
- data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
- data/lib/arjdbc/util/quoted_cache.rb +60 -0
- data/lib/arjdbc/util/serialized_attributes.rb +98 -0
- data/lib/arjdbc/util/table_copier.rb +108 -0
- data/lib/arjdbc/version.rb +8 -0
- data/lib/generators/jdbc/USAGE +9 -0
- data/lib/generators/jdbc/jdbc_generator.rb +17 -0
- data/pom.xml +285 -0
- data/rails_generators/jdbc_generator.rb +15 -0
- data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
- data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
- data/rakelib/01-tomcat.rake +51 -0
- data/rakelib/02-test.rake +151 -0
- data/rakelib/bundler_ext.rb +11 -0
- data/rakelib/db.rake +58 -0
- data/rakelib/rails.rake +77 -0
- data/src/java/arjdbc/ArJdbcModule.java +288 -0
- data/src/java/arjdbc/db2/DB2Module.java +77 -0
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +128 -0
- data/src/java/arjdbc/derby/DerbyModule.java +180 -0
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +153 -0
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
- data/src/java/arjdbc/h2/H2Module.java +50 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +86 -0
- data/src/java/arjdbc/hsqldb/HSQLDBModule.java +74 -0
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +76 -0
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
- data/src/java/arjdbc/jdbc/Callable.java +44 -0
- data/src/java/arjdbc/jdbc/ConnectionFactory.java +77 -0
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
- data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
- data/src/java/arjdbc/jdbc/DriverWrapper.java +128 -0
- data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +32 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4541 -0
- data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
- data/src/java/arjdbc/jdbc/WithResultSet.java +37 -0
- data/src/java/arjdbc/mssql/MSSQLModule.java +91 -0
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +193 -0
- data/src/java/arjdbc/mysql/MySQLModule.java +140 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +456 -0
- data/src/java/arjdbc/oracle/OracleModule.java +81 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +477 -0
- data/src/java/arjdbc/postgresql/ByteaUtils.java +171 -0
- data/src/java/arjdbc/postgresql/DriverImplementation.java +78 -0
- data/src/java/arjdbc/postgresql/PGDriverImplementation.java +535 -0
- data/src/java/arjdbc/postgresql/PostgreSQLModule.java +189 -0
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +489 -0
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +93 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +405 -0
- data/src/java/arjdbc/util/CallResultSet.java +826 -0
- data/src/java/arjdbc/util/DateTimeUtils.java +517 -0
- data/src/java/arjdbc/util/NumberUtils.java +50 -0
- data/src/java/arjdbc/util/ObjectSupport.java +65 -0
- data/src/java/arjdbc/util/QuotingUtils.java +139 -0
- data/src/java/arjdbc/util/StringCache.java +60 -0
- data/src/java/arjdbc/util/StringHelper.java +155 -0
- metadata +288 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
module ArJdbc
|
2
|
+
module Oracle
|
3
|
+
|
4
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcColumn#column_types
|
5
|
+
def self.column_selector
|
6
|
+
[ /oracle/i, lambda { |config, column| column.extend(ColumnMethods) } ]
|
7
|
+
end
|
8
|
+
|
9
|
+
# @private
|
10
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcColumn
|
11
|
+
module ColumnMethods
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
# NOTE: assumes a standalone OracleColumn class
|
15
|
+
class << base; include Cast; end # unless AR42
|
16
|
+
end
|
17
|
+
|
18
|
+
def primary=(value)
|
19
|
+
super
|
20
|
+
@type = :integer if value && @sql_type =~ /^NUMBER$/i
|
21
|
+
end unless AR42
|
22
|
+
|
23
|
+
def type_cast(value)
|
24
|
+
return nil if value.nil?
|
25
|
+
case type
|
26
|
+
when :datetime then self.class.string_to_time(value)
|
27
|
+
when :timestamp then self.class.string_to_time(value)
|
28
|
+
when :boolean then self.class.value_to_boolean(value)
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def type_cast_code(var_name)
|
35
|
+
case type
|
36
|
+
when :datetime then "#{self.class.name}.string_to_time(#{var_name})"
|
37
|
+
when :timestamp then "#{self.class.name}.string_to_time(#{var_name})"
|
38
|
+
when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
|
39
|
+
else
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def sql_type
|
45
|
+
(@sql_type || '').start_with?('XMLTYPE') ? 'XMLTYPE' : @sql_type
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def extract_limit(sql_type)
|
51
|
+
case sql_type
|
52
|
+
when /^(clob|date)/i then nil
|
53
|
+
when /^xml/i then nil
|
54
|
+
else super
|
55
|
+
end
|
56
|
+
end unless AR42
|
57
|
+
|
58
|
+
def simplified_type(field_type)
|
59
|
+
case field_type
|
60
|
+
when /char/i then :string
|
61
|
+
when /float|double/i then :float
|
62
|
+
when /int/i then :integer
|
63
|
+
when /^number\(1\)$/i then Oracle.emulate_booleans? ? :boolean : :integer
|
64
|
+
when /^num|dec|real/i then extract_scale(field_type) == 0 ? :integer : :decimal
|
65
|
+
# Oracle TIMESTAMP stores the date and time to up to 9 digits of sub-second precision
|
66
|
+
when /TIMESTAMP/i then :timestamp
|
67
|
+
# Oracle DATE stores the date and time to the second
|
68
|
+
when /DATE|TIME/i then :datetime
|
69
|
+
when /CLOB/i then :text
|
70
|
+
when /BLOB/i then :binary
|
71
|
+
when /XML/i then :xml
|
72
|
+
else
|
73
|
+
super
|
74
|
+
end
|
75
|
+
end unless AR42
|
76
|
+
|
77
|
+
# Post process default value from JDBC into a Rails-friendly format (columns{-internal})
|
78
|
+
def default_value(value)
|
79
|
+
return nil unless value
|
80
|
+
value = value.strip # Not sure why we need this for Oracle?
|
81
|
+
upcase = value.upcase
|
82
|
+
|
83
|
+
return nil if upcase == "NULL"
|
84
|
+
# SYSDATE default should be treated like a NULL value
|
85
|
+
return nil if upcase == "SYSDATE"
|
86
|
+
# jdbc returns column default strings with actual single quotes around the value.
|
87
|
+
return $1 if value =~ /^'(.*)'$/
|
88
|
+
|
89
|
+
value
|
90
|
+
end
|
91
|
+
|
92
|
+
module Cast
|
93
|
+
|
94
|
+
# Convert a value to a boolean.
|
95
|
+
def value_to_boolean(value)
|
96
|
+
# NOTE: Oracle JDBC meta-data gets us DECIMAL for NUMBER(1) values
|
97
|
+
# thus we're likely to get a column back as BigDecimal (e.g. 1.0)
|
98
|
+
if value.is_a?(String)
|
99
|
+
value.blank? ? nil : value == '1'
|
100
|
+
elsif value.is_a?(Numeric)
|
101
|
+
value.to_i == 1 # <BigDecimal:7b5bfe,'0.1E1',1(4)>
|
102
|
+
else
|
103
|
+
!! value
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# @override
|
108
|
+
def string_to_time(string)
|
109
|
+
return string unless string.is_a?(String)
|
110
|
+
return nil if string.empty?
|
111
|
+
return Time.now if string.index('CURRENT') == 0 # TODO seems very wrong
|
112
|
+
|
113
|
+
super(string)
|
114
|
+
end
|
115
|
+
|
116
|
+
# @private
|
117
|
+
def guess_date_or_time(value)
|
118
|
+
return value if value.is_a? Date
|
119
|
+
( value && value.hour == 0 && value.min == 0 && value.sec == 0 ) ?
|
120
|
+
Date.new(value.year, value.month, value.day) : value
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.const_missing(name)
|
128
|
+
if name.to_sym == :Column
|
129
|
+
ArJdbc.deprecate("#{self.name}::Column will change to refer to the actual column class, please use ColumnMethods instead", :once)
|
130
|
+
return ColumnMethods
|
131
|
+
end
|
132
|
+
super
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
ArJdbc::ConnectionMethods.module_eval do
|
2
|
+
# Unless a connection URL (`url: jdbc:oracle:...`) is specified we'll use the
|
3
|
+
# *thin* method to connect to the Oracle DB.
|
4
|
+
# @note Oracle's JDBC driver should be on the class-path.
|
5
|
+
def oracle_connection(config)
|
6
|
+
config[:adapter_spec] ||= ::ArJdbc::Oracle
|
7
|
+
config[:adapter_class] = ActiveRecord::ConnectionAdapters::OracleAdapter unless config.key?(:adapter_class)
|
8
|
+
|
9
|
+
return jndi_connection(config) if jndi_config?(config)
|
10
|
+
|
11
|
+
config[:port] ||= 1521
|
12
|
+
config[:url] ||= "jdbc:oracle:thin:@#{config[:host]}:#{config[:port]}:#{config[:database] || 'XE'}"
|
13
|
+
config[:driver] ||= "oracle.jdbc.driver.OracleDriver"
|
14
|
+
config[:connection_alive_sql] ||= 'SELECT 1 FROM DUAL'
|
15
|
+
unless config.key?(:statement_escape_processing)
|
16
|
+
config[:statement_escape_processing] = true
|
17
|
+
end
|
18
|
+
jdbc_connection(config)
|
19
|
+
end
|
20
|
+
alias_method :jdbcoracle_connection, :oracle_connection
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime.class_eval do
|
2
|
+
def cast_value(value)
|
3
|
+
if value.is_a?(::String)
|
4
|
+
case value
|
5
|
+
when 'infinity' then ::Float::INFINITY
|
6
|
+
when '-infinity' then -::Float::INFINITY
|
7
|
+
#when / BC$/
|
8
|
+
# astronomical_year = format("%04d", value[/^\d+/].to_i)
|
9
|
+
# super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
|
10
|
+
else
|
11
|
+
if value.end_with?(' BC')
|
12
|
+
DateTime.parse("-#{value}"[0...-3])
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
else
|
18
|
+
value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,1498 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
ArJdbc.load_java_part :PostgreSQL
|
3
|
+
|
4
|
+
require 'ipaddr'
|
5
|
+
|
6
|
+
module ArJdbc
|
7
|
+
# Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.
|
8
|
+
module PostgreSQL
|
9
|
+
|
10
|
+
# @deprecated no longer used
|
11
|
+
# @private
|
12
|
+
AR4_COMPAT = AR40
|
13
|
+
# @deprecated no longer used
|
14
|
+
# @private
|
15
|
+
AR42_COMPAT = AR42
|
16
|
+
|
17
|
+
require 'arjdbc/postgresql/column'
|
18
|
+
require 'arjdbc/postgresql/explain_support'
|
19
|
+
require 'arjdbc/postgresql/schema_creation' # AR 4.x
|
20
|
+
|
21
|
+
# @private
|
22
|
+
IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition
|
23
|
+
|
24
|
+
# @private
|
25
|
+
ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition if ::ActiveRecord::ConnectionAdapters.const_defined? :ForeignKeyDefinition
|
26
|
+
|
27
|
+
# @private
|
28
|
+
Type = ::ActiveRecord::Type if AR42
|
29
|
+
|
30
|
+
JdbcConnection = ::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
|
31
|
+
|
32
|
+
# @deprecated
|
33
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
|
34
|
+
def self.jdbc_connection_class; JdbcConnection end
|
35
|
+
|
36
|
+
# @see ActiveRecord::ConnectionAdapters::Jdbc::ArelSupport
|
37
|
+
def self.arel_visitor_type(config = nil)
|
38
|
+
require 'arel/visitors/postgresql_jdbc'
|
39
|
+
::Arel::Visitors::PostgreSQL
|
40
|
+
end
|
41
|
+
|
42
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#bind_substitution
|
43
|
+
# @private
|
44
|
+
class BindSubstitution < ::Arel::Visitors::PostgreSQL
|
45
|
+
include ::Arel::Visitors::BindVisitor
|
46
|
+
end if defined? ::Arel::Visitors::BindVisitor
|
47
|
+
|
48
|
+
ADAPTER_NAME = 'PostgreSQL'.freeze
|
49
|
+
|
50
|
+
def adapter_name
|
51
|
+
ADAPTER_NAME
|
52
|
+
end
|
53
|
+
|
54
|
+
def postgresql_version
|
55
|
+
@postgresql_version ||=
|
56
|
+
begin
|
57
|
+
version = select_version
|
58
|
+
if version =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
|
59
|
+
($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
|
60
|
+
else
|
61
|
+
0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def select_version
|
67
|
+
@_version ||= select_value('SELECT version()')
|
68
|
+
end
|
69
|
+
private :select_version
|
70
|
+
|
71
|
+
def redshift?
|
72
|
+
# SELECT version() :
|
73
|
+
# PostgreSQL 8.0.2 on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3), Redshift 1.0.647
|
74
|
+
if ( redshift = config[:redshift] ).nil?
|
75
|
+
redshift = !! (select_version || '').index('Redshift')
|
76
|
+
end
|
77
|
+
redshift
|
78
|
+
end
|
79
|
+
private :redshift?
|
80
|
+
|
81
|
+
def use_insert_returning?
|
82
|
+
if @use_insert_returning.nil?
|
83
|
+
@use_insert_returning = supports_insert_with_returning?
|
84
|
+
end
|
85
|
+
@use_insert_returning
|
86
|
+
end
|
87
|
+
|
88
|
+
def set_client_encoding(encoding)
|
89
|
+
ActiveRecord::Base.logger.warn "client_encoding is set by the driver and should not be altered, ('#{encoding}' ignored)"
|
90
|
+
ActiveRecord::Base.logger.debug "Set the 'allowEncodingChanges' driver property (e.g. using config[:properties]) if you need to override the client encoding when doing a copy."
|
91
|
+
end
|
92
|
+
|
93
|
+
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
94
|
+
# This is called on `connection.connect` and should not be called manually.
|
95
|
+
def configure_connection
|
96
|
+
#if encoding = config[:encoding]
|
97
|
+
# The client_encoding setting is set by the driver and should not be altered.
|
98
|
+
# If the driver detects a change it will abort the connection.
|
99
|
+
# see http://jdbc.postgresql.org/documentation/91/connect.html
|
100
|
+
# self.set_client_encoding(encoding)
|
101
|
+
#end
|
102
|
+
self.client_min_messages = config[:min_messages] || 'warning'
|
103
|
+
self.schema_search_path = config[:schema_search_path] || config[:schema_order]
|
104
|
+
|
105
|
+
# Use standard-conforming strings if available so we don't have to do the E'...' dance.
|
106
|
+
set_standard_conforming_strings
|
107
|
+
|
108
|
+
# If using Active Record's time zone support configure the connection to return
|
109
|
+
# TIMESTAMP WITH ZONE types in UTC.
|
110
|
+
# (SET TIME ZONE does not use an equals sign like other SET variables)
|
111
|
+
if ActiveRecord::Base.default_timezone == :utc
|
112
|
+
execute("SET time zone 'UTC'", 'SCHEMA')
|
113
|
+
elsif tz = local_tz
|
114
|
+
execute("SET time zone '#{tz}'", 'SCHEMA')
|
115
|
+
end unless redshift?
|
116
|
+
|
117
|
+
# SET statements from :variables config hash
|
118
|
+
# http://www.postgresql.org/docs/8.3/static/sql-set.html
|
119
|
+
(config[:variables] || {}).map do |k, v|
|
120
|
+
if v == ':default' || v == :default
|
121
|
+
# Sets the value to the global or compile default
|
122
|
+
execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
|
123
|
+
elsif ! v.nil?
|
124
|
+
execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# @private
|
130
|
+
ActiveRecordError = ::ActiveRecord::ActiveRecordError
|
131
|
+
|
132
|
+
# Maps logical Rails types to PostgreSQL-specific data types.
|
133
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
134
|
+
case type.to_s
|
135
|
+
when 'binary'
|
136
|
+
# PostgreSQL doesn't support limits on binary (bytea) columns.
|
137
|
+
# The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
|
138
|
+
case limit
|
139
|
+
when nil, 0..0x3fffffff; super(type)
|
140
|
+
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
|
141
|
+
end
|
142
|
+
when 'text'
|
143
|
+
# PostgreSQL doesn't support limits on text columns.
|
144
|
+
# The hard limit is 1Gb, according to section 8.3 in the manual.
|
145
|
+
case limit
|
146
|
+
when nil, 0..0x3fffffff; super(type)
|
147
|
+
else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
|
148
|
+
end
|
149
|
+
when 'integer'
|
150
|
+
return 'integer' unless limit
|
151
|
+
|
152
|
+
case limit
|
153
|
+
when 1, 2; 'smallint'
|
154
|
+
when 3, 4; 'integer'
|
155
|
+
when 5..8; 'bigint'
|
156
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
|
157
|
+
end
|
158
|
+
when 'datetime'
|
159
|
+
return super unless precision
|
160
|
+
|
161
|
+
case precision
|
162
|
+
when 0..6; "timestamp(#{precision})"
|
163
|
+
else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
164
|
+
end
|
165
|
+
else
|
166
|
+
super
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def type_cast(value, column, array_member = false)
|
171
|
+
return super(value, nil) unless column
|
172
|
+
|
173
|
+
case value
|
174
|
+
when String
|
175
|
+
return super(value, column) unless 'bytea' == column.sql_type
|
176
|
+
value # { :value => value, :format => 1 }
|
177
|
+
when Array
|
178
|
+
case column.sql_type
|
179
|
+
when 'point'
|
180
|
+
Column.point_to_string(value)
|
181
|
+
when 'json', 'jsonb'
|
182
|
+
Column.json_to_string(value)
|
183
|
+
else
|
184
|
+
return super(value, column) unless column.array?
|
185
|
+
Column.array_to_string(value, column, self)
|
186
|
+
end
|
187
|
+
when NilClass
|
188
|
+
if column.array? && array_member
|
189
|
+
'NULL'
|
190
|
+
elsif column.array?
|
191
|
+
value
|
192
|
+
else
|
193
|
+
super(value, column)
|
194
|
+
end
|
195
|
+
when Hash
|
196
|
+
case column.sql_type
|
197
|
+
when 'hstore'
|
198
|
+
Column.hstore_to_string(value, array_member)
|
199
|
+
when 'json', 'jsonb'
|
200
|
+
Column.json_to_string(value)
|
201
|
+
else super(value, column)
|
202
|
+
end
|
203
|
+
when IPAddr
|
204
|
+
return super unless column.sql_type == 'inet' || column.sql_type == 'cidr'
|
205
|
+
Column.cidr_to_string(value)
|
206
|
+
when Range
|
207
|
+
return super(value, column) unless /range$/ =~ column.sql_type
|
208
|
+
Column.range_to_string(value)
|
209
|
+
else
|
210
|
+
super(value, column)
|
211
|
+
end
|
212
|
+
end if AR40 && ! AR42
|
213
|
+
|
214
|
+
# @private
|
215
|
+
def _type_cast(value)
|
216
|
+
case value
|
217
|
+
when Type::Binary::Data
|
218
|
+
# Return a bind param hash with format as binary.
|
219
|
+
# See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
|
220
|
+
# for more information
|
221
|
+
{ :value => value.to_s, :format => 1 }
|
222
|
+
when OID::Xml::Data, OID::Bit::Data
|
223
|
+
value.to_s
|
224
|
+
else
|
225
|
+
super
|
226
|
+
end
|
227
|
+
end if AR42
|
228
|
+
private :_type_cast if AR42
|
229
|
+
|
230
|
+
NATIVE_DATABASE_TYPES = {
|
231
|
+
:primary_key => "serial primary key",
|
232
|
+
:string => { :name => "character varying", :limit => 255 },
|
233
|
+
:text => { :name => "text" },
|
234
|
+
:integer => { :name => "integer" },
|
235
|
+
:float => { :name => "float" },
|
236
|
+
:numeric => { :name => "numeric" },
|
237
|
+
:decimal => { :name => "decimal" }, # :limit => 1000
|
238
|
+
:datetime => { :name => "timestamp" },
|
239
|
+
:timestamp => { :name => "timestamp" },
|
240
|
+
:time => { :name => "time" },
|
241
|
+
:date => { :name => "date" },
|
242
|
+
:binary => { :name => "bytea" },
|
243
|
+
:boolean => { :name => "boolean" },
|
244
|
+
:xml => { :name => "xml" },
|
245
|
+
# AR-JDBC added :
|
246
|
+
#:timestamptz => { :name => "timestamptz" },
|
247
|
+
#:timetz => { :name => "timetz" },
|
248
|
+
:money => { :name=>"money" },
|
249
|
+
:char => { :name => "char" },
|
250
|
+
:serial => { :name => "serial" }, # auto-inc integer, bigserial, smallserial
|
251
|
+
}
|
252
|
+
|
253
|
+
NATIVE_DATABASE_TYPES.update({
|
254
|
+
:tsvector => { :name => "tsvector" },
|
255
|
+
:hstore => { :name => "hstore" },
|
256
|
+
:inet => { :name => "inet" },
|
257
|
+
:cidr => { :name => "cidr" },
|
258
|
+
:macaddr => { :name => "macaddr" },
|
259
|
+
:uuid => { :name => "uuid" },
|
260
|
+
:json => { :name => "json" },
|
261
|
+
:jsonb => { :name => "jsonb" },
|
262
|
+
:ltree => { :name => "ltree" },
|
263
|
+
# ranges :
|
264
|
+
:daterange => { :name => "daterange" },
|
265
|
+
:numrange => { :name => "numrange" },
|
266
|
+
:tsrange => { :name => "tsrange" },
|
267
|
+
:tstzrange => { :name => "tstzrange" },
|
268
|
+
:int4range => { :name => "int4range" },
|
269
|
+
:int8range => { :name => "int8range" },
|
270
|
+
}) if AR40
|
271
|
+
|
272
|
+
NATIVE_DATABASE_TYPES.update(
|
273
|
+
:string => { :name => "character varying" },
|
274
|
+
:bigserial => "bigserial",
|
275
|
+
:bigint => { :name => "bigint" },
|
276
|
+
:bit => { :name => "bit" },
|
277
|
+
:bit_varying => { :name => "bit varying" }
|
278
|
+
) if AR42
|
279
|
+
|
280
|
+
def native_database_types
|
281
|
+
NATIVE_DATABASE_TYPES
|
282
|
+
end
|
283
|
+
|
284
|
+
# Adds `:array` option to the default set provided by the `AbstractAdapter`.
|
285
|
+
# @override
|
286
|
+
def prepare_column_options(column, types)
|
287
|
+
spec = super
|
288
|
+
spec[:array] = 'true' if column.respond_to?(:array) && column.array
|
289
|
+
spec[:default] = "\"#{column.default_function}\"" if column.default_function
|
290
|
+
spec
|
291
|
+
end if AR40
|
292
|
+
|
293
|
+
# Adds `:array` as a valid migration key.
|
294
|
+
# @override
|
295
|
+
def migration_keys
|
296
|
+
super + [:array]
|
297
|
+
end if AR40
|
298
|
+
|
299
|
+
# Enable standard-conforming strings if available.
|
300
|
+
def set_standard_conforming_strings
|
301
|
+
if postgresql_version >= 80200 # N/A (or read-only in PG 8.1)
|
302
|
+
self.standard_conforming_strings=(true)
|
303
|
+
end
|
304
|
+
# AR 4.2 no longer does the hustle since its claiming PG >= 8.2
|
305
|
+
# ... execute('SET standard_conforming_strings = on', 'SCHEMA')
|
306
|
+
end
|
307
|
+
|
308
|
+
# Does PostgreSQL support migrations?
|
309
|
+
def supports_migrations?
|
310
|
+
true
|
311
|
+
end
|
312
|
+
|
313
|
+
# Does PostgreSQL support finding primary key on non-Active Record tables?
|
314
|
+
def supports_primary_key?
|
315
|
+
true
|
316
|
+
end
|
317
|
+
|
318
|
+
def supports_hex_escaped_bytea?
|
319
|
+
postgresql_version >= 90000
|
320
|
+
end
|
321
|
+
|
322
|
+
def supports_insert_with_returning?
|
323
|
+
postgresql_version >= 80200
|
324
|
+
end
|
325
|
+
|
326
|
+
def supports_ddl_transactions?; true end
|
327
|
+
|
328
|
+
def supports_transaction_isolation?; true end
|
329
|
+
|
330
|
+
def supports_index_sort_order?; true end
|
331
|
+
|
332
|
+
def supports_partial_index?; true end if AR40
|
333
|
+
|
334
|
+
# Range data-types weren't introduced until PostgreSQL 9.2.
|
335
|
+
def supports_ranges?
|
336
|
+
postgresql_version >= 90200
|
337
|
+
end if AR40
|
338
|
+
|
339
|
+
def supports_transaction_isolation?(level = nil)
|
340
|
+
true
|
341
|
+
end
|
342
|
+
|
343
|
+
# @override
|
344
|
+
def supports_views?; true end
|
345
|
+
|
346
|
+
# NOTE: handled by JdbcAdapter only to have statements in logs :
|
347
|
+
|
348
|
+
# @override
|
349
|
+
def begin_db_transaction
|
350
|
+
# PG driver doesn't really do anything on setAutoCommit(false)
|
351
|
+
# except for commit-ing a previous pending transaction if any
|
352
|
+
log('/* BEGIN */') { @connection.begin }
|
353
|
+
end
|
354
|
+
|
355
|
+
# @override
|
356
|
+
def commit_db_transaction
|
357
|
+
log('COMMIT') { @connection.commit }
|
358
|
+
end
|
359
|
+
|
360
|
+
# @override
|
361
|
+
def rollback_db_transaction
|
362
|
+
log('ROLLBACK') { @connection.rollback }
|
363
|
+
end
|
364
|
+
|
365
|
+
# Starts a database transaction.
|
366
|
+
# @param isolation the transaction isolation to use
|
367
|
+
# @since 1.3.0
|
368
|
+
# @override on **AR-4.0**
|
369
|
+
def begin_isolated_db_transaction(isolation)
|
370
|
+
name = isolation.to_s.upcase; name.sub!('_', ' ')
|
371
|
+
log("/* BEGIN */; SET TRANSACTION ISOLATION LEVEL #{name}") do
|
372
|
+
@connection.begin(isolation)
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
# @override
|
377
|
+
def supports_savepoints?; true end
|
378
|
+
|
379
|
+
# @override
|
380
|
+
def create_savepoint(name = current_savepoint_name(true))
|
381
|
+
log("SAVEPOINT #{name}") { @connection.create_savepoint(name) }
|
382
|
+
end
|
383
|
+
|
384
|
+
# @override
|
385
|
+
def rollback_to_savepoint(name = current_savepoint_name(true))
|
386
|
+
log("ROLLBACK TO SAVEPOINT #{name}") { @connection.rollback_savepoint(name) }
|
387
|
+
end
|
388
|
+
|
389
|
+
# @override
|
390
|
+
def release_savepoint(name = current_savepoint_name(false))
|
391
|
+
log("RELEASE SAVEPOINT #{name}") { @connection.release_savepoint(name) }
|
392
|
+
end
|
393
|
+
|
394
|
+
def supports_extensions?
|
395
|
+
postgresql_version >= 90200
|
396
|
+
end # NOTE: only since AR-4.0 but should not hurt on other versions
|
397
|
+
|
398
|
+
def enable_extension(name)
|
399
|
+
execute("CREATE EXTENSION IF NOT EXISTS \"#{name}\"")
|
400
|
+
end
|
401
|
+
|
402
|
+
def disable_extension(name)
|
403
|
+
execute("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE")
|
404
|
+
end
|
405
|
+
|
406
|
+
def extension_enabled?(name)
|
407
|
+
if supports_extensions?
|
408
|
+
rows = select_rows("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL)", 'SCHEMA')
|
409
|
+
available = rows.first.first # true/false or 't'/'f'
|
410
|
+
available == true || available == 't'
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
def extensions
|
415
|
+
if supports_extensions?
|
416
|
+
rows = select_rows "SELECT extname from pg_extension", "SCHEMA"
|
417
|
+
rows.map { |row| row.first }
|
418
|
+
else
|
419
|
+
[]
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def index_algorithms
|
424
|
+
{ :concurrently => 'CONCURRENTLY' }
|
425
|
+
end
|
426
|
+
|
427
|
+
# Set the authorized user for this session.
|
428
|
+
def session_auth=(user)
|
429
|
+
execute "SET SESSION AUTHORIZATION #{user}"
|
430
|
+
end
|
431
|
+
|
432
|
+
# Returns the configured supported identifier length supported by PostgreSQL,
|
433
|
+
# or report the default of 63 on PostgreSQL 7.x.
|
434
|
+
def table_alias_length
|
435
|
+
@table_alias_length ||= (
|
436
|
+
postgresql_version >= 80000 ?
|
437
|
+
select_one('SHOW max_identifier_length')['max_identifier_length'].to_i :
|
438
|
+
63
|
439
|
+
)
|
440
|
+
end
|
441
|
+
|
442
|
+
def default_sequence_name(table_name, pk = nil)
|
443
|
+
default_pk, default_seq = pk_and_sequence_for(table_name)
|
444
|
+
default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
|
445
|
+
end
|
446
|
+
|
447
|
+
# Resets sequence to the max value of the table's primary key if present.
|
448
|
+
def reset_pk_sequence!(table, pk = nil, sequence = nil)
|
449
|
+
if ! pk || ! sequence
|
450
|
+
default_pk, default_sequence = pk_and_sequence_for(table)
|
451
|
+
pk ||= default_pk; sequence ||= default_sequence
|
452
|
+
end
|
453
|
+
if pk && sequence
|
454
|
+
quoted_sequence = quote_column_name(sequence)
|
455
|
+
|
456
|
+
select_value <<-end_sql, 'Reset Sequence'
|
457
|
+
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)
|
458
|
+
end_sql
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
# Find a table's primary key and sequence.
|
463
|
+
def pk_and_sequence_for(table)
|
464
|
+
# try looking for a seq with a dependency on the table's primary key :
|
465
|
+
result = select(<<-end_sql, 'PK and Serial Sequence')[0]
|
466
|
+
SELECT attr.attname, seq.relname
|
467
|
+
FROM pg_class seq,
|
468
|
+
pg_attribute attr,
|
469
|
+
pg_depend dep,
|
470
|
+
pg_constraint cons
|
471
|
+
WHERE seq.oid = dep.objid
|
472
|
+
AND seq.relkind = 'S'
|
473
|
+
AND attr.attrelid = dep.refobjid
|
474
|
+
AND attr.attnum = dep.refobjsubid
|
475
|
+
AND attr.attrelid = cons.conrelid
|
476
|
+
AND attr.attnum = cons.conkey[1]
|
477
|
+
AND cons.contype = 'p'
|
478
|
+
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
479
|
+
end_sql
|
480
|
+
|
481
|
+
if result.nil? || result.empty?
|
482
|
+
# if that fails, try parsing the primary key's default value :
|
483
|
+
result = select(<<-end_sql, 'PK and Custom Sequence')[0]
|
484
|
+
SELECT attr.attname,
|
485
|
+
CASE
|
486
|
+
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
|
487
|
+
WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
|
488
|
+
substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
|
489
|
+
strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
|
490
|
+
ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
|
491
|
+
END as relname
|
492
|
+
FROM pg_class t
|
493
|
+
JOIN pg_attribute attr ON (t.oid = attrelid)
|
494
|
+
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
495
|
+
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
496
|
+
WHERE t.oid = '#{quote_table_name(table)}'::regclass
|
497
|
+
AND cons.contype = 'p'
|
498
|
+
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
499
|
+
end_sql
|
500
|
+
end
|
501
|
+
|
502
|
+
[ result['attname'], result['relname'] ]
|
503
|
+
rescue
|
504
|
+
nil
|
505
|
+
end
|
506
|
+
|
507
|
+
def primary_key(table)
|
508
|
+
result = select(<<-end_sql, 'SCHEMA').first
|
509
|
+
SELECT attr.attname
|
510
|
+
FROM pg_attribute attr
|
511
|
+
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey)
|
512
|
+
WHERE cons.contype = 'p' AND cons.conrelid = '#{quote_table_name(table)}'::regclass
|
513
|
+
end_sql
|
514
|
+
|
515
|
+
result && result['attname']
|
516
|
+
# pk_and_sequence = pk_and_sequence_for(table)
|
517
|
+
# pk_and_sequence && pk_and_sequence.first
|
518
|
+
end
|
519
|
+
|
520
|
+
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
521
|
+
unless pk
|
522
|
+
# Extract the table from the insert sql. Yuck.
|
523
|
+
table_ref = extract_table_ref_from_insert_sql(sql)
|
524
|
+
pk = primary_key(table_ref) if table_ref
|
525
|
+
end
|
526
|
+
|
527
|
+
if pk && use_insert_returning? # && id_value.nil?
|
528
|
+
select_value("#{to_sql(sql, binds)} RETURNING #{quote_column_name(pk)}")
|
529
|
+
else
|
530
|
+
execute(sql, name, binds) # super
|
531
|
+
unless id_value
|
532
|
+
table_ref ||= extract_table_ref_from_insert_sql(sql)
|
533
|
+
# If neither PK nor sequence name is given, look them up.
|
534
|
+
if table_ref && ! ( pk ||= primary_key(table_ref) ) && ! sequence_name
|
535
|
+
pk, sequence_name = pk_and_sequence_for(table_ref)
|
536
|
+
end
|
537
|
+
# If a PK is given, fallback to default sequence name.
|
538
|
+
# Don't fetch last insert id for a table without a PK.
|
539
|
+
if pk && sequence_name ||= default_sequence_name(table_ref, pk)
|
540
|
+
id_value = last_insert_id(table_ref, sequence_name)
|
541
|
+
end
|
542
|
+
end
|
543
|
+
id_value
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
# @override
|
548
|
+
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
549
|
+
unless pk
|
550
|
+
# Extract the table from the insert sql. Yuck.
|
551
|
+
table_ref = extract_table_ref_from_insert_sql(sql)
|
552
|
+
pk = primary_key(table_ref) if table_ref
|
553
|
+
end
|
554
|
+
|
555
|
+
if pk && use_insert_returning?
|
556
|
+
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
|
557
|
+
end
|
558
|
+
|
559
|
+
[ sql, binds ]
|
560
|
+
end
|
561
|
+
|
562
|
+
# @override due RETURNING clause
|
563
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
564
|
+
# NOTE: 3.2 does not pass the PK on #insert (passed only into #sql_for_insert) :
|
565
|
+
# sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
|
566
|
+
# 3.2 :
|
567
|
+
# value = exec_insert(sql, name, binds)
|
568
|
+
# 4.x :
|
569
|
+
# value = exec_insert(sql, name, binds, pk, sequence_name)
|
570
|
+
if use_insert_returning? && ( pk || (sql.is_a?(String) && sql =~ /RETURNING "?\S+"?$/) )
|
571
|
+
exec_query(sql, name, binds) # due RETURNING clause returns a result set
|
572
|
+
else
|
573
|
+
result = super
|
574
|
+
if pk
|
575
|
+
unless sequence_name
|
576
|
+
table_ref = extract_table_ref_from_insert_sql(sql)
|
577
|
+
sequence_name = default_sequence_name(table_ref, pk)
|
578
|
+
return result unless sequence_name
|
579
|
+
end
|
580
|
+
last_insert_id_result(sequence_name)
|
581
|
+
else
|
582
|
+
result
|
583
|
+
end
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
# @note Only for "better" AR 4.0 compatibility.
|
588
|
+
# @private
|
589
|
+
def query(sql, name = nil)
|
590
|
+
log(sql, name) do
|
591
|
+
result = []
|
592
|
+
@connection.execute_query_raw(sql, nil) do |*values|
|
593
|
+
result << values
|
594
|
+
end
|
595
|
+
result
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
# Returns an array of schema names.
|
600
|
+
def schema_names
|
601
|
+
select_values(
|
602
|
+
"SELECT nspname FROM pg_namespace" <<
|
603
|
+
" WHERE nspname !~ '^pg_.*' AND nspname NOT IN ('information_schema')" <<
|
604
|
+
" ORDER by nspname;",
|
605
|
+
'SCHEMA')
|
606
|
+
end
|
607
|
+
|
608
|
+
# Returns true if schema exists.
|
609
|
+
def schema_exists?(name)
|
610
|
+
select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", 'SCHEMA').to_i > 0
|
611
|
+
end
|
612
|
+
|
613
|
+
# Returns the current schema name.
|
614
|
+
def current_schema
|
615
|
+
select_value('SELECT current_schema', 'SCHEMA')
|
616
|
+
end
|
617
|
+
|
618
|
+
# current database name
|
619
|
+
def current_database
|
620
|
+
select_value('SELECT current_database()', 'SCHEMA')
|
621
|
+
end
|
622
|
+
|
623
|
+
# Returns the current database encoding format.
|
624
|
+
def encoding
|
625
|
+
select_value(
|
626
|
+
"SELECT pg_encoding_to_char(pg_database.encoding)" <<
|
627
|
+
" FROM pg_database" <<
|
628
|
+
" WHERE pg_database.datname LIKE '#{current_database}'",
|
629
|
+
'SCHEMA')
|
630
|
+
end
|
631
|
+
|
632
|
+
# Returns the current database collation.
|
633
|
+
def collation
|
634
|
+
select_value(
|
635
|
+
"SELECT pg_database.datcollate" <<
|
636
|
+
" FROM pg_database" <<
|
637
|
+
" WHERE pg_database.datname LIKE '#{current_database}'",
|
638
|
+
'SCHEMA')
|
639
|
+
end
|
640
|
+
|
641
|
+
# Returns the current database ctype.
|
642
|
+
def ctype
|
643
|
+
select_value(
|
644
|
+
"SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'",
|
645
|
+
'SCHEMA')
|
646
|
+
end
|
647
|
+
|
648
|
+
# Returns the active schema search path.
|
649
|
+
def schema_search_path
|
650
|
+
@schema_search_path ||= select_value('SHOW search_path', 'SCHEMA')
|
651
|
+
end
|
652
|
+
|
653
|
+
# Sets the schema search path to a string of comma-separated schema names.
|
654
|
+
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
655
|
+
# See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
|
656
|
+
#
|
657
|
+
# This should be not be called manually but set in database.yml.
|
658
|
+
def schema_search_path=(schema_csv)
|
659
|
+
if schema_csv
|
660
|
+
execute "SET search_path TO #{schema_csv}"
|
661
|
+
@schema_search_path = schema_csv
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
# Take an id from the result of an INSERT query.
|
666
|
+
# @return [Integer, NilClass]
|
667
|
+
def last_inserted_id(result)
|
668
|
+
return nil if result.nil?
|
669
|
+
return result if result.is_a? Integer
|
670
|
+
# <ActiveRecord::Result @hash_rows=nil, @columns=["id"], @rows=[[3]]>
|
671
|
+
# but it will work with [{ 'id' => 1 }] Hash wrapped results as well
|
672
|
+
result.first.first[1] # .first = { "id"=>1 } .first = [ "id", 1 ]
|
673
|
+
end
|
674
|
+
|
675
|
+
def last_insert_id(table, sequence_name = nil)
|
676
|
+
sequence_name = table if sequence_name.nil? # AR-4.0 1 argument
|
677
|
+
last_insert_id_result(sequence_name)
|
678
|
+
end
|
679
|
+
|
680
|
+
def last_insert_id_result(sequence_name)
|
681
|
+
select_value("SELECT currval('#{sequence_name}')", 'SQL')
|
682
|
+
end
|
683
|
+
|
684
|
+
def recreate_database(name, options = {})
|
685
|
+
drop_database(name)
|
686
|
+
create_database(name, options)
|
687
|
+
end
|
688
|
+
|
689
|
+
# Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
|
690
|
+
# <tt>:encoding</tt>, <tt>:collation</tt>, <tt>:ctype</tt>,
|
691
|
+
# <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
|
692
|
+
# <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
|
693
|
+
#
|
694
|
+
# Example:
|
695
|
+
# create_database config[:database], config
|
696
|
+
# create_database 'foo_development', encoding: 'unicode'
|
697
|
+
def create_database(name, options = {})
|
698
|
+
options = { :encoding => 'utf8' }.merge!(options.symbolize_keys)
|
699
|
+
|
700
|
+
option_string = options.sum do |key, value|
|
701
|
+
case key
|
702
|
+
when :owner
|
703
|
+
" OWNER = \"#{value}\""
|
704
|
+
when :template
|
705
|
+
" TEMPLATE = \"#{value}\""
|
706
|
+
when :encoding
|
707
|
+
" ENCODING = '#{value}'"
|
708
|
+
when :collation
|
709
|
+
" LC_COLLATE = '#{value}'"
|
710
|
+
when :ctype
|
711
|
+
" LC_CTYPE = '#{value}'"
|
712
|
+
when :tablespace
|
713
|
+
" TABLESPACE = \"#{value}\""
|
714
|
+
when :connection_limit
|
715
|
+
" CONNECTION LIMIT = #{value}"
|
716
|
+
else
|
717
|
+
""
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
|
722
|
+
end
|
723
|
+
|
724
|
+
def drop_database(name)
|
725
|
+
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
726
|
+
end
|
727
|
+
|
728
|
+
# Creates a schema for the given schema name.
|
729
|
+
def create_schema(schema_name, pg_username = nil)
|
730
|
+
if pg_username.nil? # AR 4.0 compatibility - accepts only single argument
|
731
|
+
execute "CREATE SCHEMA #{schema_name}"
|
732
|
+
else
|
733
|
+
execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
# Drops the schema for the given schema name.
|
738
|
+
def drop_schema schema_name
|
739
|
+
execute "DROP SCHEMA #{schema_name} CASCADE"
|
740
|
+
end
|
741
|
+
|
742
|
+
def all_schemas
|
743
|
+
select('SELECT nspname FROM pg_namespace').map { |row| row["nspname"] }
|
744
|
+
end
|
745
|
+
|
746
|
+
# Returns the current client message level.
|
747
|
+
def client_min_messages
|
748
|
+
return nil if redshift? # not supported on Redshift
|
749
|
+
select_value('SHOW client_min_messages', 'SCHEMA')
|
750
|
+
end
|
751
|
+
|
752
|
+
# Set the client message level.
|
753
|
+
def client_min_messages=(level)
|
754
|
+
# NOTE: for now simply ignore the writer (no warn on Redshift) so that
|
755
|
+
# the AR copy-pasted PpstgreSQL parts stay the same as much as possible
|
756
|
+
return nil if redshift? # not supported on Redshift
|
757
|
+
execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
|
758
|
+
end
|
759
|
+
|
760
|
+
# Gets the maximum number columns postgres has, default 32
|
761
|
+
def multi_column_index_limit
|
762
|
+
@multi_column_index_limit ||= 32
|
763
|
+
end
|
764
|
+
|
765
|
+
# Sets the maximum number columns postgres has, default 32
|
766
|
+
def multi_column_index_limit=(limit)
|
767
|
+
@multi_column_index_limit = limit
|
768
|
+
end
|
769
|
+
|
770
|
+
# @override
|
771
|
+
def distinct(columns, orders)
|
772
|
+
"DISTINCT #{columns_for_distinct(columns, orders)}"
|
773
|
+
end
|
774
|
+
|
775
|
+
# PostgreSQL requires the ORDER BY columns in the select list for distinct
|
776
|
+
# queries, and requires that the ORDER BY include the distinct column.
|
777
|
+
# @override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).
|
778
|
+
def columns_for_distinct(columns, orders)
|
779
|
+
if orders.is_a?(String)
|
780
|
+
orders = orders.split(','); orders.each(&:strip!)
|
781
|
+
end
|
782
|
+
|
783
|
+
order_columns = orders.reject(&:blank?).map! do |column|
|
784
|
+
column = column.is_a?(String) ? column.dup : column.to_sql # AREL node
|
785
|
+
column.gsub!(/\s+(?:ASC|DESC)\s*/i, '') # remove any ASC/DESC modifiers
|
786
|
+
column.gsub!(/\s*NULLS\s+(?:FIRST|LAST)?\s*/i, '')
|
787
|
+
column
|
788
|
+
end
|
789
|
+
order_columns.reject!(&:empty?)
|
790
|
+
i = -1; order_columns.map! { |column| "#{column} AS alias_#{i += 1}" }
|
791
|
+
|
792
|
+
columns = [ columns ]; columns.flatten!
|
793
|
+
columns.push( *order_columns ).join(', ')
|
794
|
+
end
|
795
|
+
|
796
|
+
# ORDER BY clause for the passed order option.
|
797
|
+
#
|
798
|
+
# PostgreSQL does not allow arbitrary ordering when using DISTINCT ON,
|
799
|
+
# so we work around this by wrapping the SQL as a sub-select and ordering
|
800
|
+
# in that query.
|
801
|
+
def add_order_by_for_association_limiting!(sql, options)
|
802
|
+
return sql if options[:order].blank?
|
803
|
+
|
804
|
+
order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
|
805
|
+
order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
|
806
|
+
order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')
|
807
|
+
|
808
|
+
sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
|
809
|
+
end
|
810
|
+
|
811
|
+
# @return [String]
|
812
|
+
# @override
|
813
|
+
def quote(value, column = nil)
|
814
|
+
return super unless column && column.type
|
815
|
+
return value if sql_literal?(value)
|
816
|
+
|
817
|
+
case value
|
818
|
+
when Float
|
819
|
+
if value.infinite? && ( column.type == :datetime || column.type == :timestamp )
|
820
|
+
"'#{value.to_s.downcase}'"
|
821
|
+
elsif value.infinite? || value.nan?
|
822
|
+
"'#{value.to_s}'"
|
823
|
+
else super
|
824
|
+
end
|
825
|
+
when Numeric
|
826
|
+
if column.respond_to?(:sql_type) && column.sql_type == 'money'
|
827
|
+
"'#{value}'"
|
828
|
+
elsif column.type == :string || column.type == :text
|
829
|
+
"'#{value}'"
|
830
|
+
else super
|
831
|
+
end
|
832
|
+
when String
|
833
|
+
return "E'#{escape_bytea(value)}'::bytea" if column.type == :binary
|
834
|
+
return "xml '#{quote_string(value)}'" if column.type == :xml
|
835
|
+
sql_type = column.respond_to?(:sql_type) && column.sql_type
|
836
|
+
sql_type && sql_type.start_with?('bit') ? quote_bit(value) : super
|
837
|
+
when Array
|
838
|
+
if AR40 && column.array? # will be always falsy in AR < 4.0
|
839
|
+
"'#{Column.array_to_string(value, column, self).gsub(/'/, "''")}'"
|
840
|
+
elsif column.type == :json # only in AR-4.0
|
841
|
+
super(Column.json_to_string(value), column)
|
842
|
+
elsif column.type == :jsonb # only in AR-4.0
|
843
|
+
super(Column.json_to_string(value), column)
|
844
|
+
elsif column.type == :point # only in AR-4.0
|
845
|
+
super(Column.point_to_string(value), column)
|
846
|
+
else super
|
847
|
+
end
|
848
|
+
when Hash
|
849
|
+
if column.type == :hstore # only in AR-4.0
|
850
|
+
super(Column.hstore_to_string(value), column)
|
851
|
+
elsif column.type == :json # only in AR-4.0
|
852
|
+
super(Column.json_to_string(value), column)
|
853
|
+
elsif column.type == :jsonb # only in AR-4.0
|
854
|
+
super(Column.json_to_string(value), column)
|
855
|
+
else super
|
856
|
+
end
|
857
|
+
when Range
|
858
|
+
sql_type = column.respond_to?(:sql_type) && column.sql_type
|
859
|
+
if sql_type && sql_type.end_with?('range') && AR40
|
860
|
+
escaped = quote_string(Column.range_to_string(value))
|
861
|
+
"'#{escaped}'::#{sql_type}"
|
862
|
+
else super
|
863
|
+
end
|
864
|
+
when IPAddr
|
865
|
+
if column.type == :inet || column.type == :cidr # only in AR-4.0
|
866
|
+
super(Column.cidr_to_string(value), column)
|
867
|
+
else super
|
868
|
+
end
|
869
|
+
else
|
870
|
+
super
|
871
|
+
end
|
872
|
+
end unless AR42
|
873
|
+
|
874
|
+
# @private
|
875
|
+
def _quote(value)
|
876
|
+
case value
|
877
|
+
when Type::Binary::Data
|
878
|
+
"E'#{escape_bytea(value.to_s)}'"
|
879
|
+
when OID::Xml::Data
|
880
|
+
"xml '#{quote_string(value.to_s)}'"
|
881
|
+
when OID::Bit::Data
|
882
|
+
if value.binary?
|
883
|
+
"B'#{value}'"
|
884
|
+
elsif value.hex?
|
885
|
+
"X'#{value}'"
|
886
|
+
end
|
887
|
+
when Float
|
888
|
+
if value.infinite? || value.nan?
|
889
|
+
"'#{value}'"
|
890
|
+
else
|
891
|
+
super
|
892
|
+
end
|
893
|
+
else
|
894
|
+
super
|
895
|
+
end
|
896
|
+
end if AR42
|
897
|
+
private :_quote if AR42
|
898
|
+
|
899
|
+
# @return [String]
|
900
|
+
def quote_bit(value)
|
901
|
+
case value
|
902
|
+
# NOTE: as reported with #60 this is not quite "right" :
|
903
|
+
# "0103" will be treated as hexadecimal string
|
904
|
+
# "0102" will be treated as hexadecimal string
|
905
|
+
# "0101" will be treated as binary string
|
906
|
+
# "0100" will be treated as binary string
|
907
|
+
# ... but is kept due Rails compatibility
|
908
|
+
when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation
|
909
|
+
when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation
|
910
|
+
end
|
911
|
+
end
|
912
|
+
|
913
|
+
def quote_bit(value)
|
914
|
+
"B'#{value}'"
|
915
|
+
end if AR40
|
916
|
+
|
917
|
+
def escape_bytea(string)
|
918
|
+
return unless string
|
919
|
+
if supports_hex_escaped_bytea?
|
920
|
+
"\\\\x#{string.unpack("H*")[0]}"
|
921
|
+
else
|
922
|
+
result = ''
|
923
|
+
string.each_byte { |c| result << sprintf('\\\\%03o', c) }
|
924
|
+
result
|
925
|
+
end
|
926
|
+
end
|
927
|
+
|
928
|
+
# @override
|
929
|
+
def quote_table_name(name)
|
930
|
+
schema, name_part = extract_pg_identifier_from_name(name.to_s)
|
931
|
+
|
932
|
+
unless name_part
|
933
|
+
quote_column_name(schema)
|
934
|
+
else
|
935
|
+
table_name, name_part = extract_pg_identifier_from_name(name_part)
|
936
|
+
"#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
|
937
|
+
end
|
938
|
+
end
|
939
|
+
|
940
|
+
# @override
|
941
|
+
def quote_table_name_for_assignment(table, attr)
|
942
|
+
quote_column_name(attr)
|
943
|
+
end if AR40
|
944
|
+
|
945
|
+
# @private
|
946
|
+
def quote_default_value(value, column)
|
947
|
+
# Do not quote function default values for UUID columns
|
948
|
+
if column.type == :uuid && value =~ /\(\)/
|
949
|
+
value
|
950
|
+
else
|
951
|
+
quote(value, column)
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
# Quote date/time values for use in SQL input.
|
956
|
+
# Includes microseconds if the value is a Time responding to `usec`.
|
957
|
+
# @override
|
958
|
+
def quoted_date(value)
|
959
|
+
result = super
|
960
|
+
if value.acts_like?(:time) && value.respond_to?(:usec)
|
961
|
+
result = "#{result}.#{sprintf("%06d", value.usec)}"
|
962
|
+
end
|
963
|
+
result = "#{result.sub(/^-/, '')} BC" if value.year < 0
|
964
|
+
result
|
965
|
+
end if ::ActiveRecord::VERSION::MAJOR >= 3
|
966
|
+
|
967
|
+
# @override
|
968
|
+
def supports_disable_referential_integrity?
|
969
|
+
true
|
970
|
+
end
|
971
|
+
|
972
|
+
def disable_referential_integrity
|
973
|
+
if supports_disable_referential_integrity?
|
974
|
+
begin
|
975
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
|
976
|
+
rescue
|
977
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER USER" }.join(";"))
|
978
|
+
end
|
979
|
+
end
|
980
|
+
yield
|
981
|
+
ensure
|
982
|
+
if supports_disable_referential_integrity?
|
983
|
+
begin
|
984
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
|
985
|
+
rescue
|
986
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER USER" }.join(";"))
|
987
|
+
end
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
991
|
+
def rename_table(table_name, new_name)
|
992
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
993
|
+
pk, seq = pk_and_sequence_for(new_name)
|
994
|
+
if seq == "#{table_name}_#{pk}_seq"
|
995
|
+
new_seq = "#{new_name}_#{pk}_seq"
|
996
|
+
idx = "#{table_name}_pkey"
|
997
|
+
new_idx = "#{new_name}_pkey"
|
998
|
+
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
|
999
|
+
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
1000
|
+
end
|
1001
|
+
rename_table_indexes(table_name, new_name) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
# Adds a new column to the named table.
|
1005
|
+
# See TableDefinition#column for details of the options you can use.
|
1006
|
+
def add_column(table_name, column_name, type, options = {})
|
1007
|
+
default = options[:default]
|
1008
|
+
notnull = options[:null] == false
|
1009
|
+
|
1010
|
+
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
|
1011
|
+
sql_type << "[]" if options[:array]
|
1012
|
+
|
1013
|
+
# Add the column.
|
1014
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{sql_type}")
|
1015
|
+
|
1016
|
+
change_column_default(table_name, column_name, default) if options_include_default?(options)
|
1017
|
+
change_column_null(table_name, column_name, false, default) if notnull
|
1018
|
+
end if ::ActiveRecord::VERSION::MAJOR < 4
|
1019
|
+
|
1020
|
+
# @private documented above
|
1021
|
+
def add_column(table_name, column_name, type, options = {}); super end if AR42
|
1022
|
+
|
1023
|
+
# Changes the column of a table.
|
1024
|
+
def change_column(table_name, column_name, type, options = {})
|
1025
|
+
quoted_table_name = quote_table_name(table_name)
|
1026
|
+
quoted_column_name = quote_table_name(column_name)
|
1027
|
+
|
1028
|
+
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
|
1029
|
+
sql_type << "[]" if options[:array]
|
1030
|
+
|
1031
|
+
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
|
1032
|
+
sql << " USING #{options[:using]}" if options[:using]
|
1033
|
+
if options[:cast_as]
|
1034
|
+
sql << " USING CAST(#{quoted_column_name} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
|
1035
|
+
end
|
1036
|
+
begin
|
1037
|
+
execute sql
|
1038
|
+
rescue ActiveRecord::StatementInvalid => e
|
1039
|
+
raise e if postgresql_version > 80000
|
1040
|
+
change_column_pg7(table_name, column_name, type, options)
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
1044
|
+
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
1045
|
+
end # unless const_defined? :SchemaCreation
|
1046
|
+
|
1047
|
+
def change_column_pg7(table_name, column_name, type, options)
|
1048
|
+
quoted_table_name = quote_table_name(table_name)
|
1049
|
+
# This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
|
1050
|
+
begin
|
1051
|
+
begin_db_transaction
|
1052
|
+
tmp_column_name = "#{column_name}_ar_tmp"
|
1053
|
+
add_column(table_name, tmp_column_name, type, options)
|
1054
|
+
execute "UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{sql_type})"
|
1055
|
+
remove_column(table_name, column_name)
|
1056
|
+
rename_column(table_name, tmp_column_name, column_name)
|
1057
|
+
commit_db_transaction
|
1058
|
+
rescue
|
1059
|
+
rollback_db_transaction
|
1060
|
+
end
|
1061
|
+
end
|
1062
|
+
private :change_column_pg7
|
1063
|
+
|
1064
|
+
# Changes the default value of a table column.
|
1065
|
+
def change_column_default(table_name, column_name, default)
|
1066
|
+
if column = column_for(table_name, column_name) # (backwards) compatible with AR 3.x - 4.x
|
1067
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote_default_value(default, column)}"
|
1068
|
+
else
|
1069
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
|
1070
|
+
end
|
1071
|
+
end unless AR42 # unless const_defined? :SchemaCreation
|
1072
|
+
|
1073
|
+
# @private documented above
|
1074
|
+
def change_column_default(table_name, column_name, default)
|
1075
|
+
return unless column = column_for(table_name, column_name)
|
1076
|
+
|
1077
|
+
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
1078
|
+
if default.nil?
|
1079
|
+
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
1080
|
+
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
1081
|
+
execute alter_column_query % "DROP DEFAULT"
|
1082
|
+
else
|
1083
|
+
execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
|
1084
|
+
end
|
1085
|
+
end if AR42
|
1086
|
+
|
1087
|
+
# @private
|
1088
|
+
def change_column_null(table_name, column_name, null, default = nil)
|
1089
|
+
unless null || default.nil?
|
1090
|
+
if column = column_for(table_name, column_name) # (backwards) compatible with AR 3.x - 4.x
|
1091
|
+
execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL"
|
1092
|
+
else
|
1093
|
+
execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL"
|
1094
|
+
end
|
1095
|
+
end
|
1096
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
1097
|
+
end unless AR42 # unless const_defined? :SchemaCreation
|
1098
|
+
|
1099
|
+
# @private
|
1100
|
+
def change_column_null(table_name, column_name, null, default = nil)
|
1101
|
+
unless null || default.nil?
|
1102
|
+
column = column_for(table_name, column_name)
|
1103
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
|
1104
|
+
end
|
1105
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
1106
|
+
end if AR42
|
1107
|
+
|
1108
|
+
def rename_column(table_name, column_name, new_column_name)
|
1109
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
1110
|
+
rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements
|
1111
|
+
end # unless const_defined? :SchemaCreation
|
1112
|
+
|
1113
|
+
def add_index(table_name, column_name, options = {})
|
1114
|
+
index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
|
1115
|
+
execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
|
1116
|
+
end if AR40
|
1117
|
+
|
1118
|
+
def remove_index!(table_name, index_name)
|
1119
|
+
execute "DROP INDEX #{quote_table_name(index_name)}"
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
def rename_index(table_name, old_name, new_name)
|
1123
|
+
validate_index_length!(table_name, new_name) if respond_to?(:validate_index_length!)
|
1124
|
+
|
1125
|
+
execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
# @override
|
1129
|
+
def supports_foreign_keys?; true end
|
1130
|
+
|
1131
|
+
def foreign_keys(table_name)
|
1132
|
+
fk_info = select_all "" <<
|
1133
|
+
"SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete " <<
|
1134
|
+
"FROM pg_constraint c " <<
|
1135
|
+
"JOIN pg_class t1 ON c.conrelid = t1.oid " <<
|
1136
|
+
"JOIN pg_class t2 ON c.confrelid = t2.oid " <<
|
1137
|
+
"JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid " <<
|
1138
|
+
"JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid " <<
|
1139
|
+
"JOIN pg_namespace t3 ON c.connamespace = t3.oid " <<
|
1140
|
+
"WHERE c.contype = 'f' " <<
|
1141
|
+
" AND t1.relname = #{quote(table_name)} " <<
|
1142
|
+
" AND t3.nspname = ANY (current_schemas(false)) " <<
|
1143
|
+
"ORDER BY c.conname "
|
1144
|
+
|
1145
|
+
fk_info.map! do |row|
|
1146
|
+
options = {
|
1147
|
+
:column => row['column'], :name => row['name'], :primary_key => row['primary_key']
|
1148
|
+
}
|
1149
|
+
options[:on_delete] = extract_foreign_key_action(row['on_delete'])
|
1150
|
+
options[:on_update] = extract_foreign_key_action(row['on_update'])
|
1151
|
+
|
1152
|
+
ForeignKeyDefinition.new(table_name, row['to_table'], options)
|
1153
|
+
end
|
1154
|
+
end if defined? ForeignKeyDefinition
|
1155
|
+
|
1156
|
+
# @private
|
1157
|
+
def extract_foreign_key_action(specifier)
|
1158
|
+
case specifier
|
1159
|
+
when 'c'; :cascade
|
1160
|
+
when 'n'; :nullify
|
1161
|
+
when 'r'; :restrict
|
1162
|
+
end
|
1163
|
+
end
|
1164
|
+
private :extract_foreign_key_action
|
1165
|
+
|
1166
|
+
def index_name_length
|
1167
|
+
63
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
# Returns the list of all column definitions for a table.
|
1171
|
+
def columns(table_name, name = nil)
|
1172
|
+
column_definitions(table_name).map! do |row|
|
1173
|
+
# |name, type, default, notnull, oid, fmod|
|
1174
|
+
name = row[0]; type = row[1]; default = row[2]
|
1175
|
+
notnull = row[3]; oid = row[4]; fmod = row[5]
|
1176
|
+
# oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) { OID::Identity.new }
|
1177
|
+
notnull = notnull == 't' if notnull.is_a?(String) # JDBC gets true/false
|
1178
|
+
# for ID columns we get a bit of non-sense default :
|
1179
|
+
# e.g. "nextval('mixed_cases_id_seq'::regclass"
|
1180
|
+
if default =~ /^nextval\(.*?\:\:regclass\)$/
|
1181
|
+
default = nil
|
1182
|
+
elsif default =~ /^\(([-+]?[\d\.]+)\)$/ # e.g. "(-1)" for a negative default
|
1183
|
+
default = $1
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
Column.new(name, default, oid, type, ! notnull, fmod, self)
|
1187
|
+
end
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
# @private documented above
|
1191
|
+
def columns(table_name)
|
1192
|
+
column = jdbc_column_class
|
1193
|
+
# Limit, precision, and scale are all handled by the superclass.
|
1194
|
+
column_definitions(table_name).map! do |row|
|
1195
|
+
# |name, type, default, notnull, oid, fmod|
|
1196
|
+
name = row[0]; type = row[1]; default = row[2]
|
1197
|
+
notnull = row[3]; oid = row[4]; fmod = row[5]
|
1198
|
+
notnull = notnull == 't' if notnull.is_a?(String) # JDBC gets true/false
|
1199
|
+
|
1200
|
+
oid_type = get_oid_type(oid.to_i, fmod.to_i, name, type)
|
1201
|
+
default_value = extract_value_from_default(oid, default)
|
1202
|
+
default_function = extract_default_function(default_value, default)
|
1203
|
+
|
1204
|
+
column.new(name, default_value, oid_type, type, ! notnull, default_function, oid, self)
|
1205
|
+
end
|
1206
|
+
end if AR42
|
1207
|
+
|
1208
|
+
# @private only for API compatibility
|
1209
|
+
def new_column(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
|
1210
|
+
jdbc_column_class.new(name, default, cast_type, sql_type, null, default_function)
|
1211
|
+
end if AR42
|
1212
|
+
|
1213
|
+
# @private
|
1214
|
+
def column_for(table_name, column_name)
|
1215
|
+
column_name = column_name.to_s
|
1216
|
+
for column in columns(table_name)
|
1217
|
+
return column if column.name == column_name
|
1218
|
+
end
|
1219
|
+
nil
|
1220
|
+
end
|
1221
|
+
|
1222
|
+
# Returns the list of a table's column names, data types, and default values.
|
1223
|
+
#
|
1224
|
+
# If the table name is not prefixed with a schema, the database will
|
1225
|
+
# take the first match from the schema search path.
|
1226
|
+
#
|
1227
|
+
# Query implementation notes:
|
1228
|
+
# - format_type includes the column size constraint, e.g. varchar(50)
|
1229
|
+
# - ::regclass is a function that gives the id for a table name
|
1230
|
+
def column_definitions(table_name)
|
1231
|
+
select_rows(<<-end_sql, 'SCHEMA')
|
1232
|
+
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
1233
|
+
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
|
1234
|
+
FROM pg_attribute a LEFT JOIN pg_attrdef d
|
1235
|
+
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
1236
|
+
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
|
1237
|
+
AND a.attnum > 0 AND NOT a.attisdropped
|
1238
|
+
ORDER BY a.attnum
|
1239
|
+
end_sql
|
1240
|
+
end
|
1241
|
+
private :column_definitions
|
1242
|
+
|
1243
|
+
# @private
|
1244
|
+
TABLES_SQL = 'SELECT tablename FROM pg_tables WHERE schemaname = ANY (current_schemas(false))'
|
1245
|
+
private_constant :TABLES_SQL rescue nil
|
1246
|
+
|
1247
|
+
# @override
|
1248
|
+
def tables(name = nil)
|
1249
|
+
select_values(TABLES_SQL, 'SCHEMA')
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
# @private
|
1253
|
+
TABLE_EXISTS_SQL_PREFIX = 'SELECT COUNT(*) as table_count FROM pg_class c'
|
1254
|
+
TABLE_EXISTS_SQL_PREFIX << ' LEFT JOIN pg_namespace n ON n.oid = c.relnamespace'
|
1255
|
+
if AR42 # -- (r)elation/table, (v)iew, (m)aterialized view
|
1256
|
+
TABLE_EXISTS_SQL_PREFIX << " WHERE c.relkind IN ('r','v','m')"
|
1257
|
+
else
|
1258
|
+
TABLE_EXISTS_SQL_PREFIX << " WHERE c.relkind IN ('r','v')"
|
1259
|
+
end
|
1260
|
+
TABLE_EXISTS_SQL_PREFIX << " AND c.relname = ?"
|
1261
|
+
private_constant :TABLE_EXISTS_SQL_PREFIX rescue nil
|
1262
|
+
|
1263
|
+
# Returns true if table exists.
|
1264
|
+
# If the schema is not specified as part of +name+ then it will only find tables within
|
1265
|
+
# the current schema search path (regardless of permissions to access tables in other schemas)
|
1266
|
+
def table_exists?(name)
|
1267
|
+
schema, table = extract_schema_and_table(name.to_s)
|
1268
|
+
return false unless table
|
1269
|
+
|
1270
|
+
binds = [[nil, table]]
|
1271
|
+
binds << [nil, schema] if schema
|
1272
|
+
|
1273
|
+
sql = "#{TABLE_EXISTS_SQL_PREFIX} AND n.nspname = #{schema ? "?" : 'ANY (current_schemas(false))'}"
|
1274
|
+
|
1275
|
+
log(sql, 'SCHEMA', binds) do
|
1276
|
+
@connection.execute_query_raw(sql, binds).first['table_count'] > 0
|
1277
|
+
end
|
1278
|
+
end
|
1279
|
+
alias data_source_exists? table_exists?
|
1280
|
+
|
1281
|
+
# @private
|
1282
|
+
DATA_SOURCES_SQL = 'SELECT c.relname FROM pg_class c'
|
1283
|
+
DATA_SOURCES_SQL << ' LEFT JOIN pg_namespace n ON n.oid = c.relnamespace'
|
1284
|
+
DATA_SOURCES_SQL << " WHERE c.relkind IN ('r', 'v','m')" # -- (r)elation/table, (v)iew, (m)aterialized view
|
1285
|
+
DATA_SOURCES_SQL << ' AND n.nspname = ANY (current_schemas(false))'
|
1286
|
+
private_constant :DATA_SOURCES_SQL rescue nil
|
1287
|
+
|
1288
|
+
# @override
|
1289
|
+
def data_sources
|
1290
|
+
select_values(DATA_SOURCES_SQL, 'SCHEMA')
|
1291
|
+
end
|
1292
|
+
|
1293
|
+
def drop_table(table_name, options = {})
|
1294
|
+
execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
def truncate(table_name, name = nil)
|
1298
|
+
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
def index_name_exists?(table_name, index_name, default)
|
1302
|
+
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
1303
|
+
SELECT COUNT(*)
|
1304
|
+
FROM pg_class t
|
1305
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
1306
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
1307
|
+
WHERE i.relkind = 'i'
|
1308
|
+
AND i.relname = '#{index_name}'
|
1309
|
+
AND t.relname = '#{table_name}'
|
1310
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
|
1311
|
+
SQL
|
1312
|
+
end if AR42
|
1313
|
+
|
1314
|
+
# Returns an array of indexes for the given table.
|
1315
|
+
def indexes(table_name, name = nil)
|
1316
|
+
# NOTE: maybe it's better to leave things of to the JDBC API ?!
|
1317
|
+
result = select_rows(<<-SQL, 'SCHEMA')
|
1318
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
|
1319
|
+
FROM pg_class t
|
1320
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
1321
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
1322
|
+
WHERE i.relkind = 'i'
|
1323
|
+
AND d.indisprimary = 'f'
|
1324
|
+
AND t.relname = '#{table_name}'
|
1325
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
|
1326
|
+
ORDER BY i.relname
|
1327
|
+
SQL
|
1328
|
+
|
1329
|
+
result.map! do |row|
|
1330
|
+
index_name = row[0]
|
1331
|
+
unique = row[1].is_a?(String) ? row[1] == 't' : row[1] # JDBC gets us a boolean
|
1332
|
+
# NOTE: this hack should no longer be needed ...
|
1333
|
+
# indkey = row[2].is_a?(Java::OrgPostgresqlUtil::PGobject) ? row[2].value : row[2]
|
1334
|
+
# indkey = indkey.split(" ")
|
1335
|
+
indkey = row[2].split(' ')
|
1336
|
+
inddef = row[3]
|
1337
|
+
oid = row[4]
|
1338
|
+
|
1339
|
+
columns = select_rows(<<-SQL, "SCHEMA")
|
1340
|
+
SELECT a.attnum, a.attname
|
1341
|
+
FROM pg_attribute a
|
1342
|
+
WHERE a.attrelid = #{oid}
|
1343
|
+
AND a.attnum IN (#{indkey.join(",")})
|
1344
|
+
SQL
|
1345
|
+
|
1346
|
+
columns = Hash[ columns.each { |column| column[0] = column[0].to_s } ]
|
1347
|
+
column_names = columns.values_at(*indkey).compact
|
1348
|
+
|
1349
|
+
unless column_names.empty?
|
1350
|
+
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
|
1351
|
+
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
|
1352
|
+
orders = desc_order_columns.any? ? Hash[ desc_order_columns.map { |column| [column, :desc] } ] : {}
|
1353
|
+
|
1354
|
+
if ::ActiveRecord::VERSION::MAJOR > 3 # AR4 supports `where` and `using` index options
|
1355
|
+
where = inddef.scan(/WHERE (.+)$/).flatten[0]
|
1356
|
+
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
1357
|
+
|
1358
|
+
IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
|
1359
|
+
else
|
1360
|
+
new_index_definition(table_name, index_name, unique, column_names, [], orders)
|
1361
|
+
end
|
1362
|
+
end
|
1363
|
+
end
|
1364
|
+
result.compact!
|
1365
|
+
result
|
1366
|
+
end
|
1367
|
+
|
1368
|
+
# @private
|
1369
|
+
def column_name_for_operation(operation, node)
|
1370
|
+
case operation
|
1371
|
+
when 'maximum' then 'max'
|
1372
|
+
when 'minimum' then 'min'
|
1373
|
+
when 'average' then 'avg'
|
1374
|
+
else operation.downcase
|
1375
|
+
end
|
1376
|
+
end if AR42
|
1377
|
+
|
1378
|
+
private
|
1379
|
+
|
1380
|
+
def translate_exception(exception, message)
|
1381
|
+
case exception.message
|
1382
|
+
when /duplicate key value violates unique constraint/
|
1383
|
+
::ActiveRecord::RecordNotUnique.new(message, exception)
|
1384
|
+
when /violates foreign key constraint/
|
1385
|
+
::ActiveRecord::InvalidForeignKey.new(message, exception)
|
1386
|
+
else
|
1387
|
+
super
|
1388
|
+
end
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
# @private `Utils.extract_schema_and_table` from AR
|
1392
|
+
def extract_schema_and_table(name)
|
1393
|
+
result = name.scan(/[^".\s]+|"[^"]*"/)[0, 2]
|
1394
|
+
result.each { |m| m.gsub!(/(^"|"$)/, '') }
|
1395
|
+
result.unshift(nil) if result.size == 1 # schema == nil
|
1396
|
+
result # [schema, table]
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
def extract_pg_identifier_from_name(name)
|
1400
|
+
match_data = name[0, 1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
|
1401
|
+
|
1402
|
+
if match_data
|
1403
|
+
rest = name[match_data[0].length..-1]
|
1404
|
+
rest = rest[1..-1] if rest[0, 1] == "."
|
1405
|
+
[match_data[1], (rest.length > 0 ? rest : nil)]
|
1406
|
+
end
|
1407
|
+
end
|
1408
|
+
|
1409
|
+
def extract_table_ref_from_insert_sql(sql)
|
1410
|
+
sql[/into\s+([^\(]*).*values\s*\(/i]
|
1411
|
+
$1.strip if $1
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
def local_tz
|
1415
|
+
@local_tz ||= execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
end
|
1419
|
+
end
|
1420
|
+
|
1421
|
+
require 'arjdbc/util/quoted_cache'
|
1422
|
+
|
1423
|
+
module ActiveRecord::ConnectionAdapters
|
1424
|
+
|
1425
|
+
remove_const(:PostgreSQLColumn) if const_defined?(:PostgreSQLColumn)
|
1426
|
+
class PostgreSQLColumn < JdbcColumn
|
1427
|
+
include ::ArJdbc::PostgreSQL::ColumnMethods
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
# NOTE: seems needed on 4.x due loading of '.../postgresql/oid' which
|
1431
|
+
# assumes: class PostgreSQLAdapter < AbstractAdapter
|
1432
|
+
remove_const(:PostgreSQLAdapter) if const_defined?(:PostgreSQLAdapter)
|
1433
|
+
class PostgreSQLAdapter < JdbcAdapter
|
1434
|
+
include ::ArJdbc::PostgreSQL
|
1435
|
+
include ::ArJdbc::PostgreSQL::ExplainSupport
|
1436
|
+
|
1437
|
+
require 'arjdbc/postgresql/oid_types' if ::ArJdbc::AR40
|
1438
|
+
include ::ArJdbc::PostgreSQL::OIDTypes if ::ArJdbc::PostgreSQL.const_defined?(:OIDTypes)
|
1439
|
+
|
1440
|
+
load 'arjdbc/postgresql/_bc_time_cast_patch.rb' if ::ArJdbc::AR42
|
1441
|
+
|
1442
|
+
include ::ArJdbc::PostgreSQL::ColumnHelpers if ::ArJdbc::AR42
|
1443
|
+
|
1444
|
+
include ::ArJdbc::Util::QuotedCache
|
1445
|
+
|
1446
|
+
def initialize(*args)
|
1447
|
+
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
1448
|
+
@local_tz = nil
|
1449
|
+
|
1450
|
+
super # configure_connection happens in super
|
1451
|
+
|
1452
|
+
@table_alias_length = nil
|
1453
|
+
|
1454
|
+
initialize_type_map(@type_map = Type::HashLookupTypeMap.new) if ::ArJdbc::AR42
|
1455
|
+
|
1456
|
+
@use_insert_returning = @config.key?(:insert_returning) ?
|
1457
|
+
self.class.type_cast_config_to_boolean(@config[:insert_returning]) : nil
|
1458
|
+
end
|
1459
|
+
|
1460
|
+
if ::ArJdbc::AR42
|
1461
|
+
require 'active_record/connection_adapters/postgresql/schema_definitions'
|
1462
|
+
else
|
1463
|
+
require 'arjdbc/postgresql/base/schema_definitions'
|
1464
|
+
end
|
1465
|
+
|
1466
|
+
ColumnDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnDefinition
|
1467
|
+
|
1468
|
+
TableDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition
|
1469
|
+
|
1470
|
+
def table_definition(*args)
|
1471
|
+
new_table_definition(TableDefinition, *args)
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
Table = ActiveRecord::ConnectionAdapters::PostgreSQL::Table
|
1475
|
+
|
1476
|
+
def update_table_definition(table_name, base)
|
1477
|
+
Table.new(table_name, base)
|
1478
|
+
end if ::ActiveRecord::VERSION::MAJOR > 3
|
1479
|
+
|
1480
|
+
def jdbc_connection_class(spec)
|
1481
|
+
::ArJdbc::PostgreSQL.jdbc_connection_class
|
1482
|
+
end
|
1483
|
+
|
1484
|
+
if ::ActiveRecord::VERSION::MAJOR < 4 # Rails 3.x compatibility
|
1485
|
+
PostgreSQLJdbcConnection.raw_array_type = true if PostgreSQLJdbcConnection.raw_array_type? == nil
|
1486
|
+
PostgreSQLJdbcConnection.raw_hstore_type = true if PostgreSQLJdbcConnection.raw_hstore_type? == nil
|
1487
|
+
end
|
1488
|
+
|
1489
|
+
# Column = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
1490
|
+
|
1491
|
+
end
|
1492
|
+
end
|
1493
|
+
|
1494
|
+
module ArJdbc
|
1495
|
+
module PostgreSQL
|
1496
|
+
Column = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
|
1497
|
+
end
|
1498
|
+
end
|