neo-activerecord-jdbc-adapter 5.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.travis.yml +438 -0
  4. data/.yardopts +4 -0
  5. data/Appraisals +41 -0
  6. data/CONTRIBUTING.md +44 -0
  7. data/Gemfile +62 -0
  8. data/History.md +1191 -0
  9. data/LICENSE.txt +25 -0
  10. data/README.md +266 -0
  11. data/RUNNING_TESTS.md +88 -0
  12. data/Rakefile +100 -0
  13. data/Rakefile.jdbc +20 -0
  14. data/activerecord-jdbc-adapter.gemspec +42 -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 +60 -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 +19 -0
  43. data/lib/arjdbc/abstract/database_statements.rb +92 -0
  44. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  45. data/lib/arjdbc/common_jdbc_methods.rb +13 -0
  46. data/lib/arjdbc/db2.rb +4 -0
  47. data/lib/arjdbc/db2/adapter.rb +789 -0
  48. data/lib/arjdbc/db2/as400.rb +130 -0
  49. data/lib/arjdbc/db2/column.rb +167 -0
  50. data/lib/arjdbc/db2/connection_methods.rb +44 -0
  51. data/lib/arjdbc/derby.rb +3 -0
  52. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  53. data/lib/arjdbc/derby/adapter.rb +556 -0
  54. data/lib/arjdbc/derby/connection_methods.rb +20 -0
  55. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  56. data/lib/arjdbc/discover.rb +115 -0
  57. data/lib/arjdbc/firebird.rb +4 -0
  58. data/lib/arjdbc/firebird/adapter.rb +434 -0
  59. data/lib/arjdbc/firebird/connection_methods.rb +23 -0
  60. data/lib/arjdbc/h2.rb +3 -0
  61. data/lib/arjdbc/h2/adapter.rb +303 -0
  62. data/lib/arjdbc/h2/connection_methods.rb +27 -0
  63. data/lib/arjdbc/hsqldb.rb +3 -0
  64. data/lib/arjdbc/hsqldb/adapter.rb +297 -0
  65. data/lib/arjdbc/hsqldb/connection_methods.rb +28 -0
  66. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  67. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  68. data/lib/arjdbc/informix.rb +5 -0
  69. data/lib/arjdbc/informix/adapter.rb +162 -0
  70. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  71. data/lib/arjdbc/jdbc.rb +59 -0
  72. data/lib/arjdbc/jdbc/adapter.rb +899 -0
  73. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  74. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  75. data/lib/arjdbc/jdbc/base_ext.rb +44 -0
  76. data/lib/arjdbc/jdbc/callbacks.rb +51 -0
  77. data/lib/arjdbc/jdbc/column.rb +97 -0
  78. data/lib/arjdbc/jdbc/connection.rb +133 -0
  79. data/lib/arjdbc/jdbc/connection_methods.rb +36 -0
  80. data/lib/arjdbc/jdbc/driver.rb +43 -0
  81. data/lib/arjdbc/jdbc/extension.rb +59 -0
  82. data/lib/arjdbc/jdbc/java.rb +15 -0
  83. data/lib/arjdbc/jdbc/jdbc.rake +4 -0
  84. data/lib/arjdbc/jdbc/quoted_primary_key.rb +28 -0
  85. data/lib/arjdbc/jdbc/railtie.rb +2 -0
  86. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -0
  87. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
  88. data/lib/arjdbc/jdbc/type_cast.rb +166 -0
  89. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  90. data/lib/arjdbc/mimer.rb +3 -0
  91. data/lib/arjdbc/mimer/adapter.rb +142 -0
  92. data/lib/arjdbc/mssql.rb +7 -0
  93. data/lib/arjdbc/mssql/adapter.rb +808 -0
  94. data/lib/arjdbc/mssql/column.rb +200 -0
  95. data/lib/arjdbc/mssql/connection_methods.rb +79 -0
  96. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  97. data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
  98. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  99. data/lib/arjdbc/mssql/types.rb +343 -0
  100. data/lib/arjdbc/mssql/utils.rb +82 -0
  101. data/lib/arjdbc/mysql.rb +3 -0
  102. data/lib/arjdbc/mysql/adapter.rb +1006 -0
  103. data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
  104. data/lib/arjdbc/mysql/column.rb +162 -0
  105. data/lib/arjdbc/mysql/connection_methods.rb +145 -0
  106. data/lib/arjdbc/mysql/explain_support.rb +82 -0
  107. data/lib/arjdbc/mysql/schema_creation.rb +58 -0
  108. data/lib/arjdbc/oracle.rb +4 -0
  109. data/lib/arjdbc/oracle/adapter.rb +952 -0
  110. data/lib/arjdbc/oracle/column.rb +126 -0
  111. data/lib/arjdbc/oracle/connection_methods.rb +21 -0
  112. data/lib/arjdbc/postgresql.rb +3 -0
  113. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
  114. data/lib/arjdbc/postgresql/adapter.rb +825 -0
  115. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  116. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  117. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  118. data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
  119. data/lib/arjdbc/postgresql/column.rb +51 -0
  120. data/lib/arjdbc/postgresql/connection_methods.rb +54 -0
  121. data/lib/arjdbc/postgresql/name.rb +24 -0
  122. data/lib/arjdbc/postgresql/oid_types.rb +178 -0
  123. data/lib/arjdbc/railtie.rb +11 -0
  124. data/lib/arjdbc/sqlite3.rb +3 -0
  125. data/lib/arjdbc/sqlite3/adapter.rb +703 -0
  126. data/lib/arjdbc/sqlite3/connection_methods.rb +40 -0
  127. data/lib/arjdbc/sybase.rb +2 -0
  128. data/lib/arjdbc/sybase/adapter.rb +47 -0
  129. data/lib/arjdbc/tasks.rb +13 -0
  130. data/lib/arjdbc/tasks/database_tasks.rb +54 -0
  131. data/lib/arjdbc/tasks/databases.rake +91 -0
  132. data/lib/arjdbc/tasks/databases3.rake +215 -0
  133. data/lib/arjdbc/tasks/databases4.rake +39 -0
  134. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  135. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  136. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  137. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  138. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  139. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  140. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
  141. data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
  142. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  143. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  144. data/lib/arjdbc/util/table_copier.rb +110 -0
  145. data/lib/arjdbc/version.rb +8 -0
  146. data/lib/generators/jdbc/USAGE +9 -0
  147. data/lib/generators/jdbc/jdbc_generator.rb +17 -0
  148. data/lib/jdbc_adapter.rb +2 -0
  149. data/lib/jdbc_adapter/rake_tasks.rb +4 -0
  150. data/lib/jdbc_adapter/version.rb +4 -0
  151. data/pom.xml +114 -0
  152. data/rails_generators/jdbc_generator.rb +15 -0
  153. data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
  154. data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
  155. data/rakelib/01-tomcat.rake +51 -0
  156. data/rakelib/02-test.rake +121 -0
  157. data/rakelib/bundler_ext.rb +11 -0
  158. data/rakelib/compile.rake +62 -0
  159. data/rakelib/db.rake +58 -0
  160. data/rakelib/rails.rake +75 -0
  161. data/src/java/arjdbc/ArJdbcModule.java +178 -0
  162. data/src/java/arjdbc/db2/DB2Module.java +71 -0
  163. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +142 -0
  164. data/src/java/arjdbc/derby/DerbyModule.java +179 -0
  165. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +164 -0
  166. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
  167. data/src/java/arjdbc/h2/H2Module.java +44 -0
  168. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +67 -0
  169. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +68 -0
  170. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +75 -0
  171. data/src/java/arjdbc/jdbc/AdapterJavaService.java +45 -0
  172. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  173. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +45 -0
  174. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3616 -0
  175. data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
  176. data/src/java/arjdbc/mssql/MSSQLModule.java +102 -0
  177. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +195 -0
  178. data/src/java/arjdbc/mysql/MySQLModule.java +147 -0
  179. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +397 -0
  180. data/src/java/arjdbc/oracle/OracleModule.java +75 -0
  181. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +465 -0
  182. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +752 -0
  183. data/src/java/arjdbc/sqlite3/SQLite3Module.java +78 -0
  184. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +351 -0
  185. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  186. data/src/java/arjdbc/util/QuotingUtils.java +111 -0
  187. metadata +255 -0
@@ -0,0 +1,126 @@
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(Column) } ]
7
+ end
8
+
9
+ # @see ActiveRecord::ConnectionAdapters::JdbcColumn
10
+ module Column
11
+
12
+ def self.included(base)
13
+ # NOTE: assumes a standalone OracleColumn class
14
+ class << base; include Cast; end # unless AR42
15
+ end
16
+
17
+ def primary=(value)
18
+ super
19
+ @type = :integer if value && @sql_type =~ /^NUMBER$/i
20
+ end unless AR42
21
+
22
+ def type_cast(value)
23
+ return nil if value.nil?
24
+ case type
25
+ when :datetime then self.class.string_to_time(value)
26
+ when :timestamp then self.class.string_to_time(value)
27
+ when :boolean then self.class.value_to_boolean(value)
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ def type_cast_code(var_name)
34
+ case type
35
+ when :datetime then "#{self.class.name}.string_to_time(#{var_name})"
36
+ when :timestamp then "#{self.class.name}.string_to_time(#{var_name})"
37
+ when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
38
+ else
39
+ super
40
+ end
41
+ end
42
+
43
+ def sql_type
44
+ (@sql_type || '').start_with?('XMLTYPE') ? 'XMLTYPE' : @sql_type
45
+ end
46
+
47
+ private
48
+
49
+ def extract_limit(sql_type)
50
+ case sql_type
51
+ when /^(clob|date)/i then nil
52
+ when /^xml/i then nil
53
+ else super
54
+ end
55
+ end unless AR42
56
+
57
+ def simplified_type(field_type)
58
+ case field_type
59
+ when /char/i then :string
60
+ when /float|double/i then :float
61
+ when /int/i then :integer
62
+ when /^number\(1\)$/i then Oracle.emulate_booleans? ? :boolean : :integer
63
+ when /^num|dec|real/i then extract_scale(field_type) == 0 ? :integer : :decimal
64
+ # Oracle TIMESTAMP stores the date and time to up to 9 digits of sub-second precision
65
+ when /TIMESTAMP/i then :timestamp
66
+ # Oracle DATE stores the date and time to the second
67
+ when /DATE|TIME/i then :datetime
68
+ when /CLOB/i then :text
69
+ when /BLOB/i then :binary
70
+ when /XML/i then :xml
71
+ else
72
+ super
73
+ end
74
+ end unless AR42
75
+
76
+ # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
77
+ def default_value(value)
78
+ return nil unless value
79
+ value = value.strip # Not sure why we need this for Oracle?
80
+ upcase = value.upcase
81
+
82
+ return nil if upcase == "NULL"
83
+ # SYSDATE default should be treated like a NULL value
84
+ return nil if upcase == "SYSDATE"
85
+ # jdbc returns column default strings with actual single quotes around the value.
86
+ return $1 if value =~ /^'(.*)'$/
87
+
88
+ value
89
+ end
90
+
91
+ module Cast
92
+
93
+ # Convert a value to a boolean.
94
+ def value_to_boolean(value)
95
+ # NOTE: Oracle JDBC meta-data gets us DECIMAL for NUMBER(1) values
96
+ # thus we're likely to get a column back as BigDecimal (e.g. 1.0)
97
+ if value.is_a?(String)
98
+ value.blank? ? nil : value == '1'
99
+ elsif value.is_a?(Numeric)
100
+ value.to_i == 1 # <BigDecimal:7b5bfe,'0.1E1',1(4)>
101
+ else
102
+ !! value
103
+ end
104
+ end
105
+
106
+ # @override
107
+ def string_to_time(string)
108
+ return string unless string.is_a?(String)
109
+ return nil if string.empty?
110
+ return Time.now if string.index('CURRENT') == 0 # TODO seems very wrong
111
+
112
+ super(string)
113
+ end
114
+
115
+ # @private
116
+ def guess_date_or_time(value)
117
+ return value if value.is_a? Date
118
+ ( value && value.hour == 0 && value.min == 0 && value.sec == 0 ) ?
119
+ Date.new(value.year, value.month, value.day) : value
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+ end
126
+ 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,825 @@
1
+ # frozen_string_literal: false
2
+ ArJdbc.load_java_part :PostgreSQL
3
+
4
+ require 'ipaddr'
5
+ require 'active_record/connection_adapters/postgresql/column'
6
+ require 'active_record/connection_adapters/postgresql/database_statements'
7
+ require 'active_record/connection_adapters/postgresql/explain_pretty_printer'
8
+ require 'active_record/connection_adapters/postgresql/quoting'
9
+ require 'active_record/connection_adapters/postgresql/schema_dumper'
10
+ require 'active_record/connection_adapters/postgresql/schema_statements'
11
+ require 'active_record/connection_adapters/postgresql/type_metadata'
12
+ require 'active_record/connection_adapters/postgresql/utils'
13
+ require 'arjdbc/common_jdbc_methods'
14
+ require 'arjdbc/abstract/database_statements'
15
+ require 'arjdbc/abstract/transaction_support'
16
+ require 'arjdbc/postgresql/base/array_decoder'
17
+ require 'arjdbc/postgresql/base/array_encoder'
18
+ require 'arjdbc/postgresql/name'
19
+
20
+ module ArJdbc
21
+ # Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.
22
+ module PostgreSQL
23
+
24
+ require 'arjdbc/postgresql/column'
25
+ require 'arel/visitors/postgresql_jdbc'
26
+ # @private
27
+ IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition
28
+
29
+ # @private
30
+ ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
31
+
32
+ # @private
33
+ Type = ::ActiveRecord::Type
34
+
35
+ # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
36
+ def self.jdbc_connection_class
37
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
38
+ end
39
+
40
+ # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
41
+ def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn end
42
+
43
+ # @private
44
+ def init_connection(jdbc_connection)
45
+ meta = jdbc_connection.meta_data
46
+ if meta.driver_version.index('JDBC3') # e.g. 'PostgreSQL 9.2 JDBC4 (build 1002)'
47
+ config[:connection_alive_sql] ||= 'SELECT 1'
48
+ else
49
+ # NOTE: since the loaded Java driver class can't change :
50
+ PostgreSQL.send(:remove_method, :init_connection) rescue nil
51
+ end
52
+ end
53
+
54
+ ADAPTER_NAME = 'PostgreSQL'.freeze
55
+
56
+ def adapter_name
57
+ ADAPTER_NAME
58
+ end
59
+
60
+ def postgresql_version
61
+ @postgresql_version ||=
62
+ begin
63
+ version = select_version
64
+ if version =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
65
+ ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
66
+ else
67
+ 0
68
+ end
69
+ end
70
+ end
71
+
72
+ def select_version
73
+ @_version ||= select_value('SELECT version()')
74
+ end
75
+ private :select_version
76
+
77
+ def redshift?
78
+ # SELECT version() :
79
+ # 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
80
+ if ( redshift = config[:redshift] ).nil?
81
+ redshift = !! (select_version || '').index('Redshift')
82
+ end
83
+ redshift
84
+ end
85
+ private :redshift?
86
+
87
+ def use_insert_returning?
88
+ if @use_insert_returning.nil?
89
+ @use_insert_returning = supports_insert_with_returning?
90
+ end
91
+ @use_insert_returning
92
+ end
93
+
94
+ def set_client_encoding(encoding)
95
+ ActiveRecord::Base.logger.warn "client_encoding is set by the driver and should not be altered, ('#{encoding}' ignored)"
96
+ 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."
97
+ end
98
+
99
+ # Configures the encoding, verbosity, schema search path, and time zone of the connection.
100
+ # This is called on `connection.connect` and should not be called manually.
101
+ def configure_connection
102
+ #if encoding = config[:encoding]
103
+ # The client_encoding setting is set by the driver and should not be altered.
104
+ # If the driver detects a change it will abort the connection.
105
+ # see http://jdbc.postgresql.org/documentation/91/connect.html
106
+ # self.set_client_encoding(encoding)
107
+ #end
108
+ self.client_min_messages = config[:min_messages] || 'warning'
109
+ self.schema_search_path = config[:schema_search_path] || config[:schema_order]
110
+
111
+ # Use standard-conforming strings if available so we don't have to do the E'...' dance.
112
+ set_standard_conforming_strings
113
+
114
+ # If using Active Record's time zone support configure the connection to return
115
+ # TIMESTAMP WITH ZONE types in UTC.
116
+ # (SET TIME ZONE does not use an equals sign like other SET variables)
117
+ if ActiveRecord::Base.default_timezone == :utc
118
+ execute("SET time zone 'UTC'", 'SCHEMA')
119
+ elsif tz = local_tz
120
+ execute("SET time zone '#{tz}'", 'SCHEMA')
121
+ end unless redshift?
122
+
123
+ # SET statements from :variables config hash
124
+ # http://www.postgresql.org/docs/8.3/static/sql-set.html
125
+ (config[:variables] || {}).map do |k, v|
126
+ if v == ':default' || v == :default
127
+ # Sets the value to the global or compile default
128
+ execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
129
+ elsif ! v.nil?
130
+ execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
131
+ end
132
+ end
133
+ end
134
+
135
+ # @private
136
+ ActiveRecordError = ::ActiveRecord::ActiveRecordError
137
+
138
+ NATIVE_DATABASE_TYPES = {
139
+ :primary_key => "serial primary key",
140
+ :string => { :name => "character varying" },
141
+ :text => { :name => "text" },
142
+ :integer => { :name => "integer" },
143
+ :float => { :name => "float" },
144
+ :numeric => { :name => "numeric" },
145
+ :decimal => { :name => "decimal" }, # :limit => 1000
146
+ :datetime => { :name => "timestamp" },
147
+ :timestamp => { :name => "timestamp" },
148
+ :time => { :name => "time" },
149
+ :date => { :name => "date" },
150
+ :binary => { :name => "bytea" },
151
+ :boolean => { :name => "boolean" },
152
+ :xml => { :name => "xml" },
153
+ # AR-JDBC added :
154
+ #:timestamptz => { :name => "timestamptz" },
155
+ #:timetz => { :name => "timetz" },
156
+ :money => { :name=>"money" },
157
+ :char => { :name => "char" },
158
+ :serial => { :name => "serial" }, # auto-inc integer, bigserial, smallserial
159
+ :bigserial => "bigserial",
160
+ :bigint => { :name => "bigint" },
161
+ :bit => { :name => "bit" },
162
+ :bit_varying => { :name => "bit varying" },
163
+ :tsvector => { :name => "tsvector" },
164
+ :hstore => { :name => "hstore" },
165
+ :inet => { :name => "inet" },
166
+ :cidr => { :name => "cidr" },
167
+ :macaddr => { :name => "macaddr" },
168
+ :uuid => { :name => "uuid" },
169
+ :json => { :name => "json" },
170
+ :jsonb => { :name => "jsonb" },
171
+ :ltree => { :name => "ltree" },
172
+ # ranges :
173
+ :daterange => { :name => "daterange" },
174
+ :numrange => { :name => "numrange" },
175
+ :tsrange => { :name => "tsrange" },
176
+ :tstzrange => { :name => "tstzrange" },
177
+ :int4range => { :name => "int4range" },
178
+ :int8range => { :name => "int8range" }
179
+ }
180
+
181
+ def native_database_types
182
+ NATIVE_DATABASE_TYPES
183
+ end
184
+
185
+ # Enable standard-conforming strings if available.
186
+ def set_standard_conforming_strings
187
+ self.standard_conforming_strings=(true)
188
+ end
189
+
190
+ # Enable standard-conforming strings if available.
191
+ def standard_conforming_strings=(enable)
192
+ client_min_messages = self.client_min_messages
193
+ begin
194
+ self.client_min_messages = 'panic'
195
+ value = enable ? "on" : "off"
196
+ execute("SET standard_conforming_strings = #{value}", 'SCHEMA')
197
+ @standard_conforming_strings = ( value == "on" )
198
+ rescue
199
+ @standard_conforming_strings = :unsupported
200
+ ensure
201
+ self.client_min_messages = client_min_messages
202
+ end
203
+ end
204
+
205
+ def standard_conforming_strings?
206
+ if @standard_conforming_strings.nil?
207
+ client_min_messages = self.client_min_messages
208
+ begin
209
+ self.client_min_messages = 'panic'
210
+ value = select_one('SHOW standard_conforming_strings', 'SCHEMA')['standard_conforming_strings']
211
+ @standard_conforming_strings = ( value == "on" )
212
+ rescue
213
+ @standard_conforming_strings = :unsupported
214
+ ensure
215
+ self.client_min_messages = client_min_messages
216
+ end
217
+ end
218
+ @standard_conforming_strings == true # return false if :unsupported
219
+ end
220
+
221
+ def supports_ddl_transactions?; true end
222
+
223
+ def supports_explain?; true end
224
+
225
+ def supports_index_sort_order?; true end
226
+
227
+ def supports_migrations?; true end
228
+
229
+ def supports_partial_index?; true end
230
+
231
+ def supports_primary_key?; true end # Supports finding primary key on non-Active Record tables
232
+
233
+ def supports_savepoints?; true end
234
+
235
+ def supports_transaction_isolation?(level = nil); true end
236
+
237
+ def supports_views?; true end
238
+
239
+ # Does PostgreSQL support standard conforming strings?
240
+ def supports_standard_conforming_strings?
241
+ standard_conforming_strings?
242
+ @standard_conforming_strings != :unsupported
243
+ end
244
+
245
+ def supports_hex_escaped_bytea?
246
+ postgresql_version >= 90000
247
+ end
248
+
249
+ def supports_insert_with_returning?
250
+ postgresql_version >= 80200
251
+ end
252
+
253
+ # Range data-types weren't introduced until PostgreSQL 9.2.
254
+ def supports_ranges?
255
+ postgresql_version >= 90200
256
+ end
257
+
258
+ def supports_extensions?
259
+ postgresql_version >= 90200
260
+ end # NOTE: only since AR-4.0 but should not hurt on other versions
261
+
262
+ def enable_extension(name)
263
+ execute("CREATE EXTENSION IF NOT EXISTS \"#{name}\"")
264
+ end
265
+
266
+ def disable_extension(name)
267
+ execute("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE")
268
+ end
269
+
270
+ def extension_enabled?(name)
271
+ if supports_extensions?
272
+ rows = select_rows("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL)", 'SCHEMA')
273
+ available = rows.first.first # true/false or 't'/'f'
274
+ available == true || available == 't'
275
+ end
276
+ end
277
+
278
+ def extensions
279
+ if supports_extensions?
280
+ rows = select_rows "SELECT extname from pg_extension", "SCHEMA"
281
+ rows.map { |row| row.first }
282
+ else
283
+ []
284
+ end
285
+ end
286
+
287
+ def index_algorithms
288
+ { :concurrently => 'CONCURRENTLY' }
289
+ end
290
+
291
+ # Set the authorized user for this session.
292
+ def session_auth=(user)
293
+ execute "SET SESSION AUTHORIZATION #{user}"
294
+ end
295
+
296
+ # Returns the configured supported identifier length supported by PostgreSQL,
297
+ # or report the default of 63 on PostgreSQL 7.x.
298
+ def table_alias_length
299
+ @table_alias_length ||= (
300
+ postgresql_version >= 80000 ?
301
+ select_one('SHOW max_identifier_length')['max_identifier_length'].to_i :
302
+ 63
303
+ )
304
+ end
305
+
306
+ # @override due to the super method not being able to handle a missing pk and
307
+ # RETURNING not being included in the sql
308
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
309
+ if pk || (sql.is_a?(String) && sql =~ /RETURNING "?\S+"?$/)
310
+ ar_postgres_adapter_exec_insert(sql, name, binds, pk, sequence_name)
311
+ else
312
+ super
313
+ end
314
+ end
315
+
316
+ # @note Only for "better" AR 4.0 compatibility.
317
+ # @private
318
+ def query(sql, name = nil)
319
+ log(sql, name) do
320
+ result = []
321
+ @connection.execute_query_raw(sql, nil) do |*values|
322
+ # We need to use #deep_dup here because it appears that
323
+ # the java method is reusing an object in some cases
324
+ # which makes all of the entries in the "result"
325
+ # array end up with the same values as the last row
326
+ result << values.deep_dup
327
+ end
328
+ result
329
+ end
330
+ end
331
+
332
+ def last_insert_id_result(sequence_name)
333
+ select_value("SELECT currval('#{sequence_name}')", 'SQL')
334
+ end
335
+
336
+ # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
337
+ # <tt>:encoding</tt>, <tt>:collation</tt>, <tt>:ctype</tt>,
338
+ # <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
339
+ # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
340
+ #
341
+ # Example:
342
+ # create_database config[:database], config
343
+ # create_database 'foo_development', encoding: 'unicode'
344
+ def create_database(name, options = {})
345
+ options = { :encoding => 'utf8' }.merge!(options.symbolize_keys)
346
+
347
+ option_string = options.sum do |key, value|
348
+ case key
349
+ when :owner
350
+ " OWNER = \"#{value}\""
351
+ when :template
352
+ " TEMPLATE = \"#{value}\""
353
+ when :encoding
354
+ " ENCODING = '#{value}'"
355
+ when :collation
356
+ " LC_COLLATE = '#{value}'"
357
+ when :ctype
358
+ " LC_CTYPE = '#{value}'"
359
+ when :tablespace
360
+ " TABLESPACE = \"#{value}\""
361
+ when :connection_limit
362
+ " CONNECTION LIMIT = #{value}"
363
+ else
364
+ ""
365
+ end
366
+ end
367
+
368
+ execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
369
+ end
370
+
371
+ # Creates a schema for the given schema name.
372
+ def create_schema(schema_name, pg_username = nil)
373
+ if pg_username.nil? # AR 4.0 compatibility - accepts only single argument
374
+ execute "CREATE SCHEMA #{schema_name}"
375
+ else
376
+ execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
377
+ end
378
+ end
379
+
380
+ # Drops the schema for the given schema name.
381
+ def drop_schema schema_name
382
+ execute "DROP SCHEMA #{schema_name} CASCADE"
383
+ end
384
+
385
+ def all_schemas
386
+ select('SELECT nspname FROM pg_namespace').map { |row| row["nspname"] }
387
+ end
388
+
389
+ # Returns the current client message level.
390
+ def client_min_messages
391
+ return nil if redshift? # not supported on Redshift
392
+ select_value('SHOW client_min_messages', 'SCHEMA')
393
+ end
394
+
395
+ # Set the client message level.
396
+ def client_min_messages=(level)
397
+ # NOTE: for now simply ignore the writer (no warn on Redshift) so that
398
+ # the AR copy-pasted PpstgreSQL parts stay the same as much as possible
399
+ return nil if redshift? # not supported on Redshift
400
+ execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
401
+ end
402
+
403
+ # Gets the maximum number columns postgres has, default 32
404
+ def multi_column_index_limit
405
+ defined?(@multi_column_index_limit) && @multi_column_index_limit || 32
406
+ end
407
+
408
+ # Sets the maximum number columns postgres has, default 32
409
+ def multi_column_index_limit=(limit)
410
+ @multi_column_index_limit = limit
411
+ end
412
+
413
+ # @override
414
+ def distinct(columns, orders)
415
+ "DISTINCT #{columns_for_distinct(columns, orders)}"
416
+ end
417
+
418
+ # PostgreSQL requires the ORDER BY columns in the select list for distinct
419
+ # queries, and requires that the ORDER BY include the distinct column.
420
+ # @override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).
421
+ def columns_for_distinct(columns, orders)
422
+ if orders.is_a?(String)
423
+ orders = orders.split(','); orders.each(&:strip!)
424
+ end
425
+
426
+ order_columns = orders.reject(&:blank?).map! do |column|
427
+ column = column.is_a?(String) ? column.dup : column.to_sql # AREL node
428
+ column.gsub!(/\s+(?:ASC|DESC)\s*/i, '') # remove any ASC/DESC modifiers
429
+ column.gsub!(/\s*NULLS\s+(?:FIRST|LAST)?\s*/i, '')
430
+ column
431
+ end
432
+ order_columns.reject!(&:empty?)
433
+ i = -1; order_columns.map! { |column| "#{column} AS alias_#{i += 1}" }
434
+
435
+ columns = [ columns ]; columns.flatten!
436
+ columns.push( *order_columns ).join(', ')
437
+ end
438
+
439
+ # ORDER BY clause for the passed order option.
440
+ #
441
+ # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON,
442
+ # so we work around this by wrapping the SQL as a sub-select and ordering
443
+ # in that query.
444
+ def add_order_by_for_association_limiting!(sql, options)
445
+ return sql if options[:order].blank?
446
+
447
+ order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
448
+ order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
449
+ order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')
450
+
451
+ sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
452
+ end
453
+
454
+ # Quotes a string, escaping any ' (single quote) and \ (backslash) chars.
455
+ # @return [String]
456
+ # @override
457
+ def quote_string(string)
458
+ quoted = string.gsub("'", "''")
459
+ unless standard_conforming_strings?
460
+ quoted.gsub!(/\\/, '\&\&')
461
+ end
462
+ quoted
463
+ end
464
+
465
+ def quote_bit(value)
466
+ "B'#{value}'"
467
+ end
468
+
469
+ def escape_bytea(string)
470
+ return unless string
471
+ if supports_hex_escaped_bytea?
472
+ "\\\\x#{string.unpack("H*")[0]}"
473
+ else
474
+ result = ''
475
+ string.each_byte { |c| result << sprintf('\\\\%03o', c) }
476
+ result
477
+ end
478
+ end
479
+
480
+ # @override
481
+ def quote_table_name(name)
482
+ schema, name_part = extract_pg_identifier_from_name(name.to_s)
483
+
484
+ unless name_part
485
+ quote_column_name(schema)
486
+ else
487
+ table_name, name_part = extract_pg_identifier_from_name(name_part)
488
+ "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
489
+ end
490
+ end
491
+
492
+ # @override
493
+ def quote_column_name(name)
494
+ %("#{name.to_s.gsub("\"", "\"\"")}")
495
+ end
496
+
497
+ # Quote date/time values for use in SQL input.
498
+ # Includes microseconds if the value is a Time responding to `usec`.
499
+ # @override
500
+ def quoted_date(value)
501
+ result = super
502
+ if value.acts_like?(:time) && value.respond_to?(:usec) && !AR50
503
+ result = "#{result}.#{sprintf("%06d", value.usec)}"
504
+ end
505
+ result = "#{result.sub(/^-/, '')} BC" if value.year < 0
506
+ result
507
+ end if ::ActiveRecord::VERSION::MAJOR >= 3
508
+
509
+ # @override
510
+ def supports_disable_referential_integrity?
511
+ true
512
+ end
513
+
514
+ def disable_referential_integrity
515
+ if supports_disable_referential_integrity?
516
+ begin
517
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
518
+ rescue
519
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER USER" }.join(";"))
520
+ end
521
+ end
522
+ yield
523
+ ensure
524
+ if supports_disable_referential_integrity?
525
+ begin
526
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
527
+ rescue
528
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER USER" }.join(";"))
529
+ end
530
+ end
531
+ end
532
+
533
+
534
+ # Changes the column of a table.
535
+ def change_column(table_name, column_name, type, options = {})
536
+ quoted_table_name = quote_table_name(table_name)
537
+ quoted_column_name = quote_table_name(column_name)
538
+
539
+ sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
540
+ sql_type << "[]" if options[:array]
541
+
542
+ sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
543
+ sql << " USING #{options[:using]}" if options[:using]
544
+ if options[:cast_as]
545
+ sql << " USING CAST(#{quoted_column_name} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
546
+ end
547
+ begin
548
+ execute sql
549
+ rescue ActiveRecord::StatementInvalid => e
550
+ raise e if postgresql_version > 80000
551
+ change_column_pg7(table_name, column_name, type, options)
552
+ end
553
+
554
+ change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
555
+ change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
556
+ end # unless const_defined? :SchemaCreation
557
+
558
+ def change_column_pg7(table_name, column_name, type, options)
559
+ quoted_table_name = quote_table_name(table_name)
560
+ # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
561
+ begin
562
+ begin_db_transaction
563
+ tmp_column_name = "#{column_name}_ar_tmp"
564
+ add_column(table_name, tmp_column_name, type, options)
565
+ execute "UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{sql_type})"
566
+ remove_column(table_name, column_name)
567
+ rename_column(table_name, tmp_column_name, column_name)
568
+ commit_db_transaction
569
+ rescue
570
+ rollback_db_transaction
571
+ end
572
+ end
573
+ private :change_column_pg7
574
+
575
+ def remove_index!(table_name, index_name)
576
+ execute "DROP INDEX #{quote_table_name(index_name)}"
577
+ end
578
+
579
+ # @override
580
+ def supports_foreign_keys?; true end
581
+
582
+ # @private
583
+ def column_for(table_name, column_name)
584
+ column_name = column_name.to_s
585
+ for column in columns(table_name)
586
+ return column if column.name == column_name
587
+ end
588
+ nil
589
+ end
590
+
591
+ # Returns the list of a table's column names, data types, and default values.
592
+ #
593
+ # If the table name is not prefixed with a schema, the database will
594
+ # take the first match from the schema search path.
595
+ #
596
+ # Query implementation notes:
597
+ # - format_type includes the column size constraint, e.g. varchar(50)
598
+ # - ::regclass is a function that gives the id for a table name
599
+ def column_definitions(table_name)
600
+ rows = select_rows(<<-end_sql, 'SCHEMA')
601
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
602
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
603
+ (SELECT c.collname FROM pg_collation c, pg_type t
604
+ WHERE c.oid = a.attcollation AND t.oid = a.atttypid
605
+ AND a.attcollation <> t.typcollation),
606
+ col_description(a.attrelid, a.attnum) AS comment
607
+ FROM pg_attribute a
608
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
609
+ WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
610
+ AND a.attnum > 0 AND NOT a.attisdropped
611
+ ORDER BY a.attnum
612
+ end_sql
613
+
614
+ # Force the notnull attribute to a boolean
615
+ rows.each do |row|
616
+ row[3] = row[3] == 't' if row[3].is_a?(String)
617
+ end
618
+ end
619
+ private :column_definitions
620
+
621
+ def truncate(table_name, name = nil)
622
+ execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
623
+ end
624
+
625
+ # Returns an array of indexes for the given table.
626
+ def indexes(table_name, name = nil)
627
+ # NOTE: maybe it's better to leave things of to the JDBC API ?!
628
+ result = select_rows(<<-SQL, 'SCHEMA')
629
+ SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
630
+ FROM pg_class t
631
+ INNER JOIN pg_index d ON t.oid = d.indrelid
632
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
633
+ WHERE i.relkind = 'i'
634
+ AND d.indisprimary = 'f'
635
+ AND t.relname = '#{table_name}'
636
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
637
+ ORDER BY i.relname
638
+ SQL
639
+
640
+ result.map! do |row|
641
+ index_name = row[0]
642
+ unique = row[1].is_a?(String) ? row[1] == 't' : row[1] # JDBC gets us a boolean
643
+ indkey = row[2].is_a?(Java::OrgPostgresqlUtil::PGobject) ? row[2].value : row[2]
644
+ indkey = indkey.split(" ")
645
+ inddef = row[3]
646
+ oid = row[4]
647
+
648
+ columns = select_rows(<<-SQL, "SCHEMA")
649
+ SELECT a.attnum, a.attname
650
+ FROM pg_attribute a
651
+ WHERE a.attrelid = #{oid}
652
+ AND a.attnum IN (#{indkey.join(",")})
653
+ SQL
654
+
655
+ columns = Hash[ columns.each { |column| column[0] = column[0].to_s } ]
656
+ column_names = columns.values_at(*indkey).compact
657
+
658
+ unless column_names.empty?
659
+ # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
660
+ desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
661
+ orders = desc_order_columns.any? ? Hash[ desc_order_columns.map { |column| [column, :desc] } ] : {}
662
+
663
+ if ::ActiveRecord::VERSION::MAJOR > 3 # AR4 supports `where` and `using` index options
664
+ where = inddef.scan(/WHERE (.+)$/).flatten[0]
665
+ using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
666
+
667
+ IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
668
+ else
669
+ new_index_definition(table_name, index_name, unique, column_names, [], orders)
670
+ end
671
+ end
672
+ end
673
+ result.compact!
674
+ result
675
+ end
676
+
677
+ # @private
678
+ def column_name_for_operation(operation, node)
679
+ case operation
680
+ when 'maximum' then 'max'
681
+ when 'minimum' then 'min'
682
+ when 'average' then 'avg'
683
+ else operation.downcase
684
+ end
685
+ end
686
+
687
+ private
688
+
689
+ def translate_exception(exception, message)
690
+ case exception.message
691
+ when /duplicate key value violates unique constraint/
692
+ ::ActiveRecord::RecordNotUnique.new(message)
693
+ when /violates foreign key constraint/
694
+ ::ActiveRecord::InvalidForeignKey.new(message)
695
+ when /value too long/
696
+ ::ActiveRecord::ValueTooLong.new(message)
697
+ else
698
+ super
699
+ end
700
+ end
701
+
702
+ # @private `Utils.extract_schema_and_table` from AR
703
+ def extract_schema_and_table(name)
704
+ result = name.scan(/[^".\s]+|"[^"]*"/)[0, 2]
705
+ result.each { |m| m.gsub!(/(^"|"$)/, '') }
706
+ result.unshift(nil) if result.size == 1 # schema == nil
707
+ result # [schema, table]
708
+ end
709
+
710
+ def extract_pg_identifier_from_name(name)
711
+ match_data = name[0, 1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
712
+
713
+ if match_data
714
+ rest = name[match_data[0].length..-1]
715
+ rest = rest[1..-1] if rest[0, 1] == "."
716
+ [match_data[1], (rest.length > 0 ? rest : nil)]
717
+ end
718
+ end
719
+
720
+ def extract_table_ref_from_insert_sql(sql)
721
+ sql[/into\s+([^\(]*).*values\s*\(/i]
722
+ $1.strip if $1
723
+ end
724
+
725
+ def local_tz
726
+ @local_tz ||= execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
727
+ end
728
+
729
+ end
730
+ end
731
+
732
+ require 'arjdbc/util/quoted_cache'
733
+
734
+ module ActiveRecord::ConnectionAdapters
735
+
736
+ # NOTE: seems needed on 4.x due loading of '.../postgresql/oid' which
737
+ # assumes: class PostgreSQLAdapter < AbstractAdapter
738
+ remove_const(:PostgreSQLAdapter) if const_defined?(:PostgreSQLAdapter)
739
+
740
+ class PostgreSQLAdapter < JdbcAdapter
741
+
742
+ # This makes it so we can use the bulk of the core logic while
743
+ # keeping the actual database access defined in this adapter
744
+ # These methods come from ActiveRecord::Abstract::DatabaseStatements so
745
+ # we need to hang on to references to them because the versions in
746
+ # ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements
747
+ # have PG gem specific implementations that override the Abstract implementations
748
+ SAVED_METHODS = ['select_rows', 'select_value', 'select_values']
749
+ SAVED_METHOD_PREFIX = 'abstract_adapter'
750
+
751
+ SAVED_METHODS.each do |base_name|
752
+ alias_method :"#{SAVED_METHOD_PREFIX}_#{base_name}", base_name
753
+ end
754
+
755
+ include ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnDumper
756
+ include ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements
757
+ include ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements
758
+ include ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting
759
+
760
+ # Restore saved methods
761
+ SAVED_METHODS.each do |base_name|
762
+ alias_method base_name, :"#{SAVED_METHOD_PREFIX}_#{base_name}"
763
+ end
764
+
765
+ # We need this so we can call arjdbc logic or ActiveRecord postgres adapter logic
766
+ # to support when a sql statement already has a 'RETURNING' clause
767
+ alias_method :ar_postgres_adapter_exec_insert, :exec_insert
768
+
769
+ include ArJdbc::CommonJdbcMethods
770
+ include ArJdbc::Abstract::DatabaseStatements
771
+ include ArJdbc::Abstract::TransactionSupport
772
+ include ArJdbc::PostgreSQL
773
+
774
+ require 'arjdbc/postgresql/oid_types'
775
+ include ::ArJdbc::PostgreSQL::OIDTypes
776
+
777
+ load 'arjdbc/postgresql/_bc_time_cast_patch.rb'
778
+
779
+ include ::ArJdbc::PostgreSQL::ColumnHelpers
780
+
781
+ include ::ArJdbc::Util::QuotedCache
782
+
783
+ def initialize(*args)
784
+ # @local_tz is initialized as nil to avoid warnings when connect tries to use it
785
+ @local_tz = nil
786
+
787
+ super # configure_connection happens in super
788
+
789
+ @table_alias_length = nil
790
+
791
+ initialize_type_map(@type_map = Type::HashLookupTypeMap.new)
792
+
793
+ @use_insert_returning = @config.key?(:insert_returning) ?
794
+ self.class.type_cast_config_to_boolean(@config[:insert_returning]) : nil
795
+ end
796
+
797
+ def arel_visitor # :nodoc:
798
+ Arel::Visitors::PostgreSQL.new(self)
799
+ end
800
+
801
+ require 'active_record/connection_adapters/postgresql/schema_definitions'
802
+
803
+ ColumnDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnDefinition
804
+ ColumnMethods = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnMethods
805
+ TableDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition
806
+ Table = ActiveRecord::ConnectionAdapters::PostgreSQL::Table
807
+
808
+ def schema_creation # :nodoc:
809
+ PostgreSQL::SchemaCreation.new self
810
+ end
811
+
812
+ def table_definition(*args)
813
+ new_table_definition(TableDefinition, *args)
814
+ end
815
+
816
+ def update_table_definition(table_name, base)
817
+ Table.new(table_name, base)
818
+ end
819
+
820
+ def jdbc_connection_class(spec)
821
+ ::ArJdbc::PostgreSQL.jdbc_connection_class
822
+ end
823
+
824
+ end
825
+ end