activerecord-jdbc-adapter 1.2.9.1 → 1.3.0.beta1

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