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.
Files changed (191) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +462 -0
  4. data/.yardopts +4 -0
  5. data/Appraisals +36 -0
  6. data/CONTRIBUTING.md +49 -0
  7. data/Gemfile +68 -0
  8. data/History.md +1191 -0
  9. data/LICENSE.txt +25 -0
  10. data/README.md +277 -0
  11. data/RUNNING_TESTS.md +88 -0
  12. data/Rakefile +298 -0
  13. data/Rakefile.jdbc +20 -0
  14. data/activerecord-jdbc-adapter.gemspec +63 -0
  15. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  16. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  19. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  20. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  21. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  22. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
  23. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  25. data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
  28. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  31. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  32. data/lib/activerecord-jdbc-adapter.rb +1 -0
  33. data/lib/arel/visitors/compat.rb +64 -0
  34. data/lib/arel/visitors/db2.rb +137 -0
  35. data/lib/arel/visitors/derby.rb +112 -0
  36. data/lib/arel/visitors/firebird.rb +79 -0
  37. data/lib/arel/visitors/h2.rb +25 -0
  38. data/lib/arel/visitors/hsqldb.rb +32 -0
  39. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  40. data/lib/arel/visitors/sql_server.rb +225 -0
  41. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  42. data/lib/arjdbc.rb +22 -0
  43. data/lib/arjdbc/db2.rb +4 -0
  44. data/lib/arjdbc/db2/adapter.rb +802 -0
  45. data/lib/arjdbc/db2/as400.rb +137 -0
  46. data/lib/arjdbc/db2/column.rb +177 -0
  47. data/lib/arjdbc/db2/connection_methods.rb +45 -0
  48. data/lib/arjdbc/derby.rb +3 -0
  49. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  50. data/lib/arjdbc/derby/adapter.rb +567 -0
  51. data/lib/arjdbc/derby/connection_methods.rb +16 -0
  52. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  53. data/lib/arjdbc/discover.rb +104 -0
  54. data/lib/arjdbc/firebird.rb +4 -0
  55. data/lib/arjdbc/firebird/adapter.rb +468 -0
  56. data/lib/arjdbc/firebird/connection_methods.rb +20 -0
  57. data/lib/arjdbc/h2.rb +3 -0
  58. data/lib/arjdbc/h2/adapter.rb +335 -0
  59. data/lib/arjdbc/h2/connection_methods.rb +22 -0
  60. data/lib/arjdbc/hsqldb.rb +3 -0
  61. data/lib/arjdbc/hsqldb/adapter.rb +304 -0
  62. data/lib/arjdbc/hsqldb/connection_methods.rb +23 -0
  63. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  64. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  65. data/lib/arjdbc/informix.rb +5 -0
  66. data/lib/arjdbc/informix/adapter.rb +160 -0
  67. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  68. data/lib/arjdbc/jdbc.rb +62 -0
  69. data/lib/arjdbc/jdbc/adapter.rb +997 -0
  70. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  71. data/lib/arjdbc/jdbc/arel_support.rb +149 -0
  72. data/lib/arjdbc/jdbc/base_ext.rb +34 -0
  73. data/lib/arjdbc/jdbc/callbacks.rb +52 -0
  74. data/lib/arjdbc/jdbc/column.rb +83 -0
  75. data/lib/arjdbc/jdbc/connection.rb +26 -0
  76. data/lib/arjdbc/jdbc/connection_methods.rb +59 -0
  77. data/lib/arjdbc/jdbc/driver.rb +44 -0
  78. data/lib/arjdbc/jdbc/error.rb +75 -0
  79. data/lib/arjdbc/jdbc/extension.rb +69 -0
  80. data/lib/arjdbc/jdbc/java.rb +13 -0
  81. data/lib/arjdbc/jdbc/type_cast.rb +154 -0
  82. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  83. data/lib/arjdbc/mssql.rb +7 -0
  84. data/lib/arjdbc/mssql/adapter.rb +822 -0
  85. data/lib/arjdbc/mssql/column.rb +207 -0
  86. data/lib/arjdbc/mssql/connection_methods.rb +72 -0
  87. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  88. data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
  89. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  90. data/lib/arjdbc/mssql/types.rb +343 -0
  91. data/lib/arjdbc/mssql/utils.rb +82 -0
  92. data/lib/arjdbc/mysql.rb +3 -0
  93. data/lib/arjdbc/mysql/adapter.rb +998 -0
  94. data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
  95. data/lib/arjdbc/mysql/column.rb +167 -0
  96. data/lib/arjdbc/mysql/connection_methods.rb +137 -0
  97. data/lib/arjdbc/mysql/explain_support.rb +82 -0
  98. data/lib/arjdbc/mysql/schema_creation.rb +58 -0
  99. data/lib/arjdbc/oracle.rb +4 -0
  100. data/lib/arjdbc/oracle/adapter.rb +968 -0
  101. data/lib/arjdbc/oracle/column.rb +136 -0
  102. data/lib/arjdbc/oracle/connection_methods.rb +21 -0
  103. data/lib/arjdbc/postgresql.rb +3 -0
  104. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
  105. data/lib/arjdbc/postgresql/adapter.rb +1498 -0
  106. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  107. data/lib/arjdbc/postgresql/base/oid.rb +412 -0
  108. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -0
  109. data/lib/arjdbc/postgresql/base/schema_definitions.rb +132 -0
  110. data/lib/arjdbc/postgresql/column.rb +640 -0
  111. data/lib/arjdbc/postgresql/connection_methods.rb +44 -0
  112. data/lib/arjdbc/postgresql/explain_support.rb +53 -0
  113. data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
  114. data/lib/arjdbc/postgresql/oid_types.rb +265 -0
  115. data/lib/arjdbc/postgresql/schema_creation.rb +60 -0
  116. data/lib/arjdbc/railtie.rb +11 -0
  117. data/lib/arjdbc/sqlite3.rb +3 -0
  118. data/lib/arjdbc/sqlite3/adapter.rb +654 -0
  119. data/lib/arjdbc/sqlite3/connection_methods.rb +36 -0
  120. data/lib/arjdbc/sqlite3/explain_support.rb +29 -0
  121. data/lib/arjdbc/sybase.rb +2 -0
  122. data/lib/arjdbc/sybase/adapter.rb +47 -0
  123. data/lib/arjdbc/tasks.rb +13 -0
  124. data/lib/arjdbc/tasks/database_tasks.rb +66 -0
  125. data/lib/arjdbc/tasks/databases.rake +91 -0
  126. data/lib/arjdbc/tasks/databases3.rake +239 -0
  127. data/lib/arjdbc/tasks/databases4.rake +39 -0
  128. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  129. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  130. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  131. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  132. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  133. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  134. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
  135. data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
  136. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  137. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  138. data/lib/arjdbc/util/table_copier.rb +108 -0
  139. data/lib/arjdbc/version.rb +8 -0
  140. data/lib/generators/jdbc/USAGE +9 -0
  141. data/lib/generators/jdbc/jdbc_generator.rb +17 -0
  142. data/pom.xml +285 -0
  143. data/rails_generators/jdbc_generator.rb +15 -0
  144. data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
  145. data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
  146. data/rakelib/01-tomcat.rake +51 -0
  147. data/rakelib/02-test.rake +151 -0
  148. data/rakelib/bundler_ext.rb +11 -0
  149. data/rakelib/db.rake +58 -0
  150. data/rakelib/rails.rake +77 -0
  151. data/src/java/arjdbc/ArJdbcModule.java +288 -0
  152. data/src/java/arjdbc/db2/DB2Module.java +77 -0
  153. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +128 -0
  154. data/src/java/arjdbc/derby/DerbyModule.java +180 -0
  155. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +153 -0
  156. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
  157. data/src/java/arjdbc/h2/H2Module.java +50 -0
  158. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +86 -0
  159. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +74 -0
  160. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +76 -0
  161. data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
  162. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  163. data/src/java/arjdbc/jdbc/ConnectionFactory.java +77 -0
  164. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
  165. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  166. data/src/java/arjdbc/jdbc/DriverWrapper.java +128 -0
  167. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +32 -0
  168. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4541 -0
  169. data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
  170. data/src/java/arjdbc/jdbc/WithResultSet.java +37 -0
  171. data/src/java/arjdbc/mssql/MSSQLModule.java +91 -0
  172. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +193 -0
  173. data/src/java/arjdbc/mysql/MySQLModule.java +140 -0
  174. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +456 -0
  175. data/src/java/arjdbc/oracle/OracleModule.java +81 -0
  176. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +477 -0
  177. data/src/java/arjdbc/postgresql/ByteaUtils.java +171 -0
  178. data/src/java/arjdbc/postgresql/DriverImplementation.java +78 -0
  179. data/src/java/arjdbc/postgresql/PGDriverImplementation.java +535 -0
  180. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +189 -0
  181. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +489 -0
  182. data/src/java/arjdbc/sqlite3/SQLite3Module.java +93 -0
  183. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +405 -0
  184. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  185. data/src/java/arjdbc/util/DateTimeUtils.java +517 -0
  186. data/src/java/arjdbc/util/NumberUtils.java +50 -0
  187. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  188. data/src/java/arjdbc/util/QuotingUtils.java +139 -0
  189. data/src/java/arjdbc/util/StringCache.java +60 -0
  190. data/src/java/arjdbc/util/StringHelper.java +155 -0
  191. 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,3 @@
1
+ require 'arjdbc'
2
+ require 'arjdbc/postgresql/adapter'
3
+ require 'arjdbc/postgresql/connection_methods'
@@ -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