activerecord-jdbc-adapter-ficoh 1.3.21-java

Sign up to get free protection for your applications and to get access to all the features.
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