tgbyte-activerecord-jdbc-adapter 1.2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. data/.gitignore +22 -0
  2. data/.travis.yml +8 -0
  3. data/Gemfile +14 -0
  4. data/Gemfile.lock +40 -0
  5. data/History.txt +488 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.rdoc +199 -0
  8. data/Rakefile +60 -0
  9. data/activerecord-jdbc-adapter.gemspec +23 -0
  10. data/bench/bench_attributes.rb +13 -0
  11. data/bench/bench_attributes_new.rb +14 -0
  12. data/bench/bench_create.rb +12 -0
  13. data/bench/bench_find_all.rb +12 -0
  14. data/bench/bench_find_all_mt.rb +25 -0
  15. data/bench/bench_model.rb +85 -0
  16. data/bench/bench_new.rb +12 -0
  17. data/bench/bench_new_valid.rb +12 -0
  18. data/bench/bench_valid.rb +13 -0
  19. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  20. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  21. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  22. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  23. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/jndi_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/activerecord-jdbc-adapter.rb +8 -0
  32. data/lib/arel/engines/sql/compilers/db2_compiler.rb +9 -0
  33. data/lib/arel/engines/sql/compilers/derby_compiler.rb +6 -0
  34. data/lib/arel/engines/sql/compilers/h2_compiler.rb +6 -0
  35. data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +15 -0
  36. data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +6 -0
  37. data/lib/arel/engines/sql/compilers/mssql_compiler.rb +46 -0
  38. data/lib/arel/visitors/compat.rb +13 -0
  39. data/lib/arel/visitors/db2.rb +17 -0
  40. data/lib/arel/visitors/derby.rb +32 -0
  41. data/lib/arel/visitors/firebird.rb +24 -0
  42. data/lib/arel/visitors/hsqldb.rb +26 -0
  43. data/lib/arel/visitors/sql_server.rb +46 -0
  44. data/lib/arjdbc.rb +24 -0
  45. data/lib/arjdbc/db2.rb +2 -0
  46. data/lib/arjdbc/db2/adapter.rb +510 -0
  47. data/lib/arjdbc/derby.rb +7 -0
  48. data/lib/arjdbc/derby/adapter.rb +358 -0
  49. data/lib/arjdbc/derby/connection_methods.rb +19 -0
  50. data/lib/arjdbc/discover.rb +92 -0
  51. data/lib/arjdbc/firebird.rb +2 -0
  52. data/lib/arjdbc/firebird/adapter.rb +136 -0
  53. data/lib/arjdbc/h2.rb +4 -0
  54. data/lib/arjdbc/h2/adapter.rb +54 -0
  55. data/lib/arjdbc/h2/connection_methods.rb +13 -0
  56. data/lib/arjdbc/hsqldb.rb +4 -0
  57. data/lib/arjdbc/hsqldb/adapter.rb +184 -0
  58. data/lib/arjdbc/hsqldb/connection_methods.rb +15 -0
  59. data/lib/arjdbc/informix.rb +3 -0
  60. data/lib/arjdbc/informix/adapter.rb +138 -0
  61. data/lib/arjdbc/informix/connection_methods.rb +11 -0
  62. data/lib/arjdbc/jdbc.rb +2 -0
  63. data/lib/arjdbc/jdbc/adapter.rb +356 -0
  64. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  65. data/lib/arjdbc/jdbc/base_ext.rb +15 -0
  66. data/lib/arjdbc/jdbc/callbacks.rb +44 -0
  67. data/lib/arjdbc/jdbc/column.rb +47 -0
  68. data/lib/arjdbc/jdbc/compatibility.rb +51 -0
  69. data/lib/arjdbc/jdbc/connection.rb +134 -0
  70. data/lib/arjdbc/jdbc/connection_methods.rb +16 -0
  71. data/lib/arjdbc/jdbc/core_ext.rb +24 -0
  72. data/lib/arjdbc/jdbc/discover.rb +18 -0
  73. data/lib/arjdbc/jdbc/driver.rb +35 -0
  74. data/lib/arjdbc/jdbc/extension.rb +47 -0
  75. data/lib/arjdbc/jdbc/java.rb +14 -0
  76. data/lib/arjdbc/jdbc/jdbc.rake +131 -0
  77. data/lib/arjdbc/jdbc/missing_functionality_helper.rb +88 -0
  78. data/lib/arjdbc/jdbc/quoted_primary_key.rb +28 -0
  79. data/lib/arjdbc/jdbc/railtie.rb +9 -0
  80. data/lib/arjdbc/jdbc/rake_tasks.rb +10 -0
  81. data/lib/arjdbc/jdbc/require_driver.rb +16 -0
  82. data/lib/arjdbc/jdbc/type_converter.rb +126 -0
  83. data/lib/arjdbc/mimer.rb +2 -0
  84. data/lib/arjdbc/mimer/adapter.rb +142 -0
  85. data/lib/arjdbc/mssql.rb +4 -0
  86. data/lib/arjdbc/mssql/adapter.rb +477 -0
  87. data/lib/arjdbc/mssql/connection_methods.rb +31 -0
  88. data/lib/arjdbc/mssql/limit_helpers.rb +101 -0
  89. data/lib/arjdbc/mssql/lock_helpers.rb +72 -0
  90. data/lib/arjdbc/mssql/tsql_helper.rb +61 -0
  91. data/lib/arjdbc/mysql.rb +4 -0
  92. data/lib/arjdbc/mysql/adapter.rb +505 -0
  93. data/lib/arjdbc/mysql/connection_methods.rb +28 -0
  94. data/lib/arjdbc/oracle.rb +3 -0
  95. data/lib/arjdbc/oracle/adapter.rb +428 -0
  96. data/lib/arjdbc/oracle/connection_methods.rb +12 -0
  97. data/lib/arjdbc/postgresql.rb +4 -0
  98. data/lib/arjdbc/postgresql/adapter.rb +825 -0
  99. data/lib/arjdbc/postgresql/connection_methods.rb +23 -0
  100. data/lib/arjdbc/sqlite3.rb +4 -0
  101. data/lib/arjdbc/sqlite3/adapter.rb +389 -0
  102. data/lib/arjdbc/sqlite3/connection_methods.rb +35 -0
  103. data/lib/arjdbc/sybase.rb +2 -0
  104. data/lib/arjdbc/sybase/adapter.rb +46 -0
  105. data/lib/arjdbc/version.rb +8 -0
  106. data/lib/generators/jdbc/USAGE +10 -0
  107. data/lib/generators/jdbc/jdbc_generator.rb +9 -0
  108. data/lib/jdbc_adapter.rb +2 -0
  109. data/lib/jdbc_adapter/rake_tasks.rb +3 -0
  110. data/lib/jdbc_adapter/version.rb +3 -0
  111. data/lib/pg.rb +26 -0
  112. data/pom.xml +57 -0
  113. data/rails_generators/jdbc_generator.rb +15 -0
  114. data/rails_generators/templates/config/initializers/jdbc.rb +7 -0
  115. data/rails_generators/templates/lib/tasks/jdbc.rake +8 -0
  116. data/rakelib/bundler_ext.rb +11 -0
  117. data/rakelib/compile.rake +23 -0
  118. data/rakelib/db.rake +39 -0
  119. data/rakelib/rails.rake +41 -0
  120. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +62 -0
  121. data/src/java/arjdbc/derby/DerbyModule.java +324 -0
  122. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +70 -0
  123. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +74 -0
  124. data/src/java/arjdbc/jdbc/AdapterJavaService.java +68 -0
  125. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +36 -0
  126. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1326 -0
  127. data/src/java/arjdbc/jdbc/SQLBlock.java +48 -0
  128. data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +127 -0
  129. data/src/java/arjdbc/mysql/MySQLModule.java +134 -0
  130. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +161 -0
  131. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +85 -0
  132. data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +82 -0
  133. data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +126 -0
  134. data/test/abstract_db_create.rb +135 -0
  135. data/test/activerecord/connection_adapters/type_conversion_test.rb +31 -0
  136. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +25 -0
  137. data/test/activerecord/jall.sh +7 -0
  138. data/test/activerecord/jtest.sh +3 -0
  139. data/test/db/db2.rb +11 -0
  140. data/test/db/derby.rb +12 -0
  141. data/test/db/h2.rb +11 -0
  142. data/test/db/hsqldb.rb +13 -0
  143. data/test/db/informix.rb +11 -0
  144. data/test/db/jdbc.rb +12 -0
  145. data/test/db/jndi_config.rb +40 -0
  146. data/test/db/logger.rb +3 -0
  147. data/test/db/mssql.rb +9 -0
  148. data/test/db/mysql.rb +10 -0
  149. data/test/db/oracle.rb +34 -0
  150. data/test/db/postgres.rb +9 -0
  151. data/test/db/sqlite3.rb +11 -0
  152. data/test/db2_reset_column_information_test.rb +8 -0
  153. data/test/db2_simple_test.rb +66 -0
  154. data/test/derby_migration_test.rb +68 -0
  155. data/test/derby_multibyte_test.rb +12 -0
  156. data/test/derby_reset_column_information_test.rb +8 -0
  157. data/test/derby_row_locking_test.rb +9 -0
  158. data/test/derby_simple_test.rb +139 -0
  159. data/test/generic_jdbc_connection_test.rb +29 -0
  160. data/test/h2_change_column_test.rb +68 -0
  161. data/test/h2_simple_test.rb +41 -0
  162. data/test/has_many_through.rb +79 -0
  163. data/test/helper.rb +87 -0
  164. data/test/hsqldb_simple_test.rb +6 -0
  165. data/test/informix_simple_test.rb +48 -0
  166. data/test/jdbc_common.rb +28 -0
  167. data/test/jndi_callbacks_test.rb +36 -0
  168. data/test/jndi_test.rb +25 -0
  169. data/test/manualTestDatabase.rb +191 -0
  170. data/test/models/add_not_null_column_to_table.rb +9 -0
  171. data/test/models/auto_id.rb +15 -0
  172. data/test/models/data_types.rb +30 -0
  173. data/test/models/entry.rb +40 -0
  174. data/test/models/mixed_case.rb +22 -0
  175. data/test/models/reserved_word.rb +15 -0
  176. data/test/models/string_id.rb +17 -0
  177. data/test/models/thing.rb +16 -0
  178. data/test/models/validates_uniqueness_of_string.rb +19 -0
  179. data/test/mssql_db_create_test.rb +26 -0
  180. data/test/mssql_identity_insert_test.rb +19 -0
  181. data/test/mssql_ignore_system_views_test.rb +27 -0
  182. data/test/mssql_legacy_types_test.rb +58 -0
  183. data/test/mssql_limit_offset_test.rb +136 -0
  184. data/test/mssql_multibyte_test.rb +18 -0
  185. data/test/mssql_null_test.rb +14 -0
  186. data/test/mssql_reset_column_information_test.rb +8 -0
  187. data/test/mssql_row_locking_sql_test.rb +159 -0
  188. data/test/mssql_row_locking_test.rb +9 -0
  189. data/test/mssql_simple_test.rb +55 -0
  190. data/test/mysql_db_create_test.rb +27 -0
  191. data/test/mysql_index_length_test.rb +58 -0
  192. data/test/mysql_info_test.rb +123 -0
  193. data/test/mysql_multibyte_test.rb +10 -0
  194. data/test/mysql_nonstandard_primary_key_test.rb +42 -0
  195. data/test/mysql_reset_column_information_test.rb +8 -0
  196. data/test/mysql_simple_test.rb +125 -0
  197. data/test/oracle_reset_column_information_test.rb +8 -0
  198. data/test/oracle_simple_test.rb +18 -0
  199. data/test/oracle_specific_test.rb +83 -0
  200. data/test/postgres_db_create_test.rb +32 -0
  201. data/test/postgres_drop_db_test.rb +16 -0
  202. data/test/postgres_information_schema_leak_test.rb +29 -0
  203. data/test/postgres_mixed_case_test.rb +29 -0
  204. data/test/postgres_native_type_mapping_test.rb +89 -0
  205. data/test/postgres_nonseq_pkey_test.rb +38 -0
  206. data/test/postgres_reserved_test.rb +22 -0
  207. data/test/postgres_reset_column_information_test.rb +8 -0
  208. data/test/postgres_schema_search_path_test.rb +48 -0
  209. data/test/postgres_simple_test.rb +167 -0
  210. data/test/postgres_table_alias_length_test.rb +15 -0
  211. data/test/postgres_type_conversion_test.rb +34 -0
  212. data/test/row_locking.rb +90 -0
  213. data/test/simple.rb +717 -0
  214. data/test/sqlite3_reset_column_information_test.rb +8 -0
  215. data/test/sqlite3_simple_test.rb +316 -0
  216. data/test/sybase_jtds_simple_test.rb +28 -0
  217. data/test/sybase_reset_column_information_test.rb +8 -0
  218. metadata +275 -0
@@ -0,0 +1,12 @@
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
+ end
11
+ end
12
+
@@ -0,0 +1,4 @@
1
+ require 'arjdbc/jdbc'
2
+ jdbc_require_driver 'jdbc/postgres'
3
+ require 'arjdbc/postgresql/connection_methods'
4
+ require 'arjdbc/postgresql/adapter'
@@ -0,0 +1,825 @@
1
+ module ActiveRecord::ConnectionAdapters
2
+ PostgreSQLAdapter = Class.new(AbstractAdapter) unless const_defined?(:PostgreSQLAdapter)
3
+ end
4
+
5
+ module ::ArJdbc
6
+ module PostgreSQL
7
+ def self.extended(mod)
8
+ (class << mod; self; end).class_eval do
9
+ alias_chained_method :columns, :query_cache, :pg_columns
10
+ end
11
+ end
12
+
13
+ def self.column_selector
14
+ [/postgre/i, lambda {|cfg,col| col.extend(::ArJdbc::PostgreSQL::Column)}]
15
+ end
16
+
17
+ def self.jdbc_connection_class
18
+ ::ActiveRecord::ConnectionAdapters::PostgresJdbcConnection
19
+ end
20
+
21
+ # column behavior based on postgresql_adapter in rails project
22
+ # https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L41
23
+ module Column
24
+ def self.included(base)
25
+ class << base
26
+ attr_accessor :money_precision
27
+ def string_to_time(string)
28
+ return string unless String === string
29
+
30
+ case string
31
+ when 'infinity' then 1.0 / 0.0
32
+ when '-infinity' then -1.0 / 0.0
33
+ else
34
+ super
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ private
41
+ # Extracts the value from a Postgresql column default definition
42
+ def default_value(default)
43
+ case default
44
+ # This is a performance optimization for Ruby 1.9.2 in development.
45
+ # If the value is nil, we return nil straight away without checking
46
+ # the regular expressions. If we check each regular expression,
47
+ # Regexp#=== will call NilClass#to_str, which will trigger
48
+ # method_missing (defined by whiny nil in ActiveSupport) which
49
+ # makes this method very very slow.
50
+ when NilClass
51
+ nil
52
+ # Numeric types
53
+ when /\A\(?(-?\d+(\.\d*)?\)?)\z/
54
+ $1
55
+ # Character types
56
+ when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
57
+ $1
58
+ # Character types (8.1 formatting)
59
+ when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
60
+ $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
61
+ # Binary data types
62
+ when /\A'(.*)'::bytea\z/m
63
+ $1
64
+ # Date/time types
65
+ when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
66
+ $1
67
+ when /\A'(.*)'::interval\z/
68
+ $1
69
+ # Boolean type
70
+ when 'true'
71
+ true
72
+ when 'false'
73
+ false
74
+ # Geometric types
75
+ when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
76
+ $1
77
+ # Network address types
78
+ when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
79
+ $1
80
+ # Bit string types
81
+ when /\AB'(.*)'::"?bit(?: varying)?"?\z/
82
+ $1
83
+ # XML type
84
+ when /\A'(.*)'::xml\z/m
85
+ $1
86
+ # Arrays
87
+ when /\A'(.*)'::"?\D+"?\[\]\z/
88
+ $1
89
+ # Object identifier types
90
+ when /\A-?\d+\z/
91
+ $1
92
+ else
93
+ # Anything else is blank, some user type, or some function
94
+ # and we can't know the value of that, so return nil.
95
+ nil
96
+ end
97
+ end
98
+
99
+ def extract_limit(sql_type)
100
+ case sql_type
101
+ when /^bigint/i then 8
102
+ when /^smallint/i then 2
103
+ else super
104
+ end
105
+ end
106
+
107
+ # Extracts the scale from PostgreSQL-specific data types.
108
+ def extract_scale(sql_type)
109
+ # Money type has a fixed scale of 2.
110
+ sql_type =~ /^money/ ? 2 : super
111
+ end
112
+
113
+ # Extracts the precision from PostgreSQL-specific data types.
114
+ def extract_precision(sql_type)
115
+ if sql_type == 'money'
116
+ self.class.money_precision
117
+ else
118
+ super
119
+ end
120
+ end
121
+
122
+ # Maps PostgreSQL-specific data types to logical Rails types.
123
+ def simplified_type(field_type)
124
+ case field_type
125
+ # Numeric and monetary types
126
+ when /^(?:real|double precision)$/ then :float
127
+ # Monetary types
128
+ when 'money' then :decimal
129
+ # Character types
130
+ when /^(?:character varying|bpchar)(?:\(\d+\))?$/ then :string
131
+ # Binary data types
132
+ when 'bytea' then :binary
133
+ # Date/time types
134
+ when /^timestamp with(?:out)? time zone$/ then :datetime
135
+ when 'interval' then :string
136
+ # Geometric types
137
+ when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/ then :string
138
+ # Network address types
139
+ when /^(?:cidr|inet|macaddr)$/ then :string
140
+ # Bit strings
141
+ when /^bit(?: varying)?(?:\(\d+\))?$/ then :string
142
+ # XML type
143
+ when 'xml' then :xml
144
+ # tsvector type
145
+ when 'tsvector' then :tsvector
146
+ # Arrays
147
+ when /^\D+\[\]$/ then :string
148
+ # Object identifier types
149
+ when 'oid' then :integer
150
+ # UUID type
151
+ when 'uuid' then :string
152
+ # Small and big integer types
153
+ when /^(?:small|big)int$/ then :integer
154
+ # Pass through all types that are not specific to PostgreSQL.
155
+ else
156
+ super
157
+ end
158
+ end
159
+ end
160
+
161
+ # constants taken from postgresql_adapter in rails project
162
+ ADAPTER_NAME = 'PostgreSQL'
163
+
164
+ NATIVE_DATABASE_TYPES = {
165
+ :primary_key => "serial primary key",
166
+ :string => { :name => "character varying", :limit => 255 },
167
+ :text => { :name => "text" },
168
+ :integer => { :name => "integer" },
169
+ :float => { :name => "float" },
170
+ :decimal => { :name => "decimal" },
171
+ :datetime => { :name => "timestamp" },
172
+ :timestamp => { :name => "timestamp" },
173
+ :time => { :name => "time" },
174
+ :date => { :name => "date" },
175
+ :binary => { :name => "bytea" },
176
+ :boolean => { :name => "boolean" },
177
+ :xml => { :name => "xml" },
178
+ :tsvector => { :name => "tsvector" }
179
+ }
180
+
181
+ def adapter_name #:nodoc:
182
+ ADAPTER_NAME
183
+ end
184
+
185
+ def self.arel2_visitors(config)
186
+ {}.tap {|v| %w(postgresql pg jdbcpostgresql).each {|a| v[a] = ::Arel::Visitors::PostgreSQL } }
187
+ end
188
+
189
+ def postgresql_version
190
+ @postgresql_version ||=
191
+ begin
192
+ value = select_value('SELECT version()')
193
+ if value =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
194
+ ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
195
+ else
196
+ 0
197
+ end
198
+ end
199
+ end
200
+
201
+ def native_database_types
202
+ NATIVE_DATABASE_TYPES
203
+ end
204
+
205
+ # Does PostgreSQL support migrations?
206
+ def supports_migrations?
207
+ true
208
+ end
209
+
210
+ # Does PostgreSQL support standard conforming strings?
211
+ def supports_standard_conforming_strings?
212
+ # Temporarily set the client message level above error to prevent unintentional
213
+ # error messages in the logs when working on a PostgreSQL database server that
214
+ # does not support standard conforming strings.
215
+ client_min_messages_old = client_min_messages
216
+ self.client_min_messages = 'panic'
217
+
218
+ # postgres-pr does not raise an exception when client_min_messages is set higher
219
+ # than error and "SHOW standard_conforming_strings" fails, but returns an empty
220
+ # PGresult instead.
221
+ has_support = select('SHOW standard_conforming_strings').to_a[0][0] rescue false
222
+ self.client_min_messages = client_min_messages_old
223
+ has_support
224
+ end
225
+
226
+ def supports_insert_with_returning?
227
+ postgresql_version >= 80200
228
+ end
229
+
230
+ def supports_ddl_transactions?
231
+ true
232
+ end
233
+
234
+ def supports_savepoints?
235
+ true
236
+ end
237
+
238
+ def supports_count_distinct? #:nodoc:
239
+ false
240
+ end
241
+
242
+ def create_savepoint
243
+ execute("SAVEPOINT #{current_savepoint_name}")
244
+ end
245
+
246
+ def rollback_to_savepoint
247
+ execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
248
+ end
249
+
250
+ def release_savepoint
251
+ execute("RELEASE SAVEPOINT #{current_savepoint_name}")
252
+ end
253
+
254
+ # Returns the configured supported identifier length supported by PostgreSQL,
255
+ # or report the default of 63 on PostgreSQL 7.x.
256
+ def table_alias_length
257
+ @table_alias_length ||= (postgresql_version >= 80000 ? select_one('SHOW max_identifier_length')['max_identifier_length'].to_i : 63)
258
+ end
259
+
260
+ def default_sequence_name(table_name, pk = nil)
261
+ default_pk, default_seq = pk_and_sequence_for(table_name)
262
+ default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
263
+ end
264
+
265
+ # Resets sequence to the max value of the table's pk if present.
266
+ def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
267
+ unless pk and sequence
268
+ default_pk, default_sequence = pk_and_sequence_for(table)
269
+ pk ||= default_pk
270
+ sequence ||= default_sequence
271
+ end
272
+ if pk
273
+ if sequence
274
+ quoted_sequence = quote_column_name(sequence)
275
+
276
+ select_value <<-end_sql, 'Reset sequence'
277
+ 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)
278
+ end_sql
279
+ else
280
+ @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
281
+ end
282
+ end
283
+ end
284
+
285
+ # Find a table's primary key and sequence.
286
+ def pk_and_sequence_for(table) #:nodoc:
287
+ # First try looking for a sequence with a dependency on the
288
+ # given table's primary key.
289
+ result = select(<<-end_sql, 'PK and serial sequence')[0]
290
+ SELECT attr.attname, seq.relname
291
+ FROM pg_class seq,
292
+ pg_attribute attr,
293
+ pg_depend dep,
294
+ pg_namespace name,
295
+ pg_constraint cons
296
+ WHERE seq.oid = dep.objid
297
+ AND seq.relkind = 'S'
298
+ AND attr.attrelid = dep.refobjid
299
+ AND attr.attnum = dep.refobjsubid
300
+ AND attr.attrelid = cons.conrelid
301
+ AND attr.attnum = cons.conkey[1]
302
+ AND cons.contype = 'p'
303
+ AND dep.refobjid = '#{quote_table_name(table)}'::regclass
304
+ end_sql
305
+
306
+ if result.nil? or result.empty?
307
+ # If that fails, try parsing the primary key's default value.
308
+ # Support the 7.x and 8.0 nextval('foo'::text) as well as
309
+ # the 8.1+ nextval('foo'::regclass).
310
+ result = select(<<-end_sql, 'PK and custom sequence')[0]
311
+ SELECT attr.attname,
312
+ CASE
313
+ WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
314
+ substr(split_part(def.adsrc, '''', 2),
315
+ strpos(split_part(def.adsrc, '''', 2), '.')+1)
316
+ ELSE split_part(def.adsrc, '''', 2)
317
+ END as relname
318
+ FROM pg_class t
319
+ JOIN pg_attribute attr ON (t.oid = attrelid)
320
+ JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
321
+ JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
322
+ WHERE t.oid = '#{quote_table_name(table)}'::regclass
323
+ AND cons.contype = 'p'
324
+ AND def.adsrc ~* 'nextval'
325
+ end_sql
326
+ end
327
+
328
+ [result["attname"], result["relname"]]
329
+ rescue
330
+ nil
331
+ end
332
+
333
+ def primary_key(table)
334
+ pk_and_sequence = pk_and_sequence_for(table)
335
+ pk_and_sequence && pk_and_sequence.first
336
+ end
337
+
338
+ # taken from rails postgresql adapter
339
+ # https://github.com/gfmurphy/rails/blob/master/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L611
340
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
341
+ unless pk
342
+ table_ref = extract_table_ref_from_insert_sql(sql)
343
+ pk = primary_key(table_ref) if table_ref
344
+ end
345
+
346
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
347
+
348
+ [sql, binds]
349
+ end
350
+
351
+ def pg_columns(table_name, name=nil)
352
+ column_definitions(table_name).map do |row|
353
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new(
354
+ row["column_name"], row["column_default"], row["column_type"],
355
+ row["column_not_null"] == "f")
356
+ end
357
+ end
358
+
359
+ # current database name
360
+ def current_database
361
+ exec_query("select current_database() as database").
362
+ first["database"]
363
+ end
364
+
365
+ # current database encoding
366
+ def encoding
367
+ exec_query(<<-end_sql).first["encoding"]
368
+ SELECT pg_encoding_to_char(pg_database.encoding) as encoding
369
+ FROM pg_database
370
+ WHERE pg_database.datname LIKE '#{current_database}'
371
+ end_sql
372
+ end
373
+
374
+ # Sets the maximum number columns postgres has, default 32
375
+ def multi_column_index_limit=(limit)
376
+ @multi_column_index_limit = limit
377
+ end
378
+
379
+ # Gets the maximum number columns postgres has, default 32
380
+ def multi_column_index_limit
381
+ defined?(@multi_column_index_limit) && @multi_column_index_limit || 32
382
+ end
383
+
384
+ # Based on postgresql_adapter.rb
385
+ def indexes(table_name, name = nil)
386
+ schema_search_path = @config[:schema_search_path] || select_rows('SHOW search_path')[0][0]
387
+ schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
388
+ result = select_rows(<<-SQL, name)
389
+ SELECT i.relname, d.indisunique, a.attname, a.attnum, d.indkey
390
+ FROM pg_class t, pg_class i, pg_index d, pg_attribute a,
391
+ generate_series(0,#{multi_column_index_limit - 1}) AS s(i)
392
+ WHERE i.relkind = 'i'
393
+ AND d.indexrelid = i.oid
394
+ AND d.indisprimary = 'f'
395
+ AND t.oid = d.indrelid
396
+ AND t.relname = '#{table_name}'
397
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
398
+ AND a.attrelid = t.oid
399
+ AND d.indkey[s.i]=a.attnum
400
+ ORDER BY i.relname
401
+ SQL
402
+
403
+ current_index = nil
404
+ indexes = []
405
+
406
+ insertion_order = []
407
+ index_order = nil
408
+
409
+ result.each do |row|
410
+ if current_index != row[0]
411
+
412
+ (index_order = row[4].split(' ')).each_with_index{ |v, i| index_order[i] = v.to_i }
413
+ indexes << ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, row[0], row[1] == "t", [])
414
+ current_index = row[0]
415
+ end
416
+ insertion_order = row[3]
417
+ ind = index_order.index(insertion_order)
418
+ indexes.last.columns[ind] = row[2]
419
+ end
420
+
421
+ indexes
422
+ end
423
+
424
+ # take id from result of insert query
425
+ def last_inserted_id(result)
426
+ Hash[Array(*result)].fetch("id") { result }
427
+ end
428
+
429
+ def last_insert_id(table, sequence_name)
430
+ Integer(select_value("SELECT currval('#{sequence_name}')"))
431
+ end
432
+
433
+ def recreate_database(name)
434
+ drop_database(name)
435
+ create_database(name)
436
+ end
437
+
438
+ def create_database(name, options = {})
439
+ execute "CREATE DATABASE \"#{name}\" ENCODING='#{options[:encoding] || 'utf8'}'"
440
+ end
441
+
442
+ def drop_database(name)
443
+ execute "DROP DATABASE IF EXISTS \"#{name}\""
444
+ end
445
+
446
+ def create_schema(schema_name, pg_username)
447
+ execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
448
+ end
449
+
450
+ def drop_schema(schema_name)
451
+ execute("DROP SCHEMA \"#{schema_name}\"")
452
+ end
453
+
454
+ def all_schemas
455
+ select('select nspname from pg_namespace').map {|r| r["nspname"] }
456
+ end
457
+
458
+ def structure_dump
459
+ database = @config[:database]
460
+ if database.nil?
461
+ if @config[:url] =~ /\/([^\/]*)$/
462
+ database = $1
463
+ else
464
+ raise "Could not figure out what database this url is for #{@config["url"]}"
465
+ end
466
+ end
467
+
468
+ ENV['PGHOST'] = @config[:host] if @config[:host]
469
+ ENV['PGPORT'] = @config[:port].to_s if @config[:port]
470
+ ENV['PGPASSWORD'] = @config[:password].to_s if @config[:password]
471
+ search_path = @config[:schema_search_path]
472
+ search_path = "--schema=#{search_path}" if search_path
473
+
474
+ @connection.connection.close
475
+ begin
476
+ definition = `pg_dump -i -U "#{@config[:username]}" -s -x -O #{search_path} #{database}`
477
+ raise "Error dumping database" if $?.exitstatus == 1
478
+
479
+ # need to patch away any references to SQL_ASCII as it breaks the JDBC driver
480
+ definition.gsub(/SQL_ASCII/, 'UNICODE')
481
+ ensure
482
+ reconnect!
483
+ end
484
+ end
485
+
486
+ # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
487
+ #
488
+ # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
489
+ # requires that the ORDER BY include the distinct column.
490
+ #
491
+ # distinct("posts.id", "posts.created_at desc")
492
+ def distinct(columns, orders) #:nodoc:
493
+ return "DISTINCT #{columns}" if orders.empty?
494
+
495
+ # Construct a clean list of column names from the ORDER BY clause, removing
496
+ # any ASC/DESC modifiers
497
+ order_columns = orders.collect { |s| s.gsub(/\s+(ASC|DESC)\s*/i, '') }.
498
+ reject(&:blank?)
499
+ order_columns = order_columns.
500
+ zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
501
+
502
+ "DISTINCT #{columns}, #{order_columns * ', '}"
503
+ end
504
+
505
+ # ORDER BY clause for the passed order option.
506
+ #
507
+ # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
508
+ # by wrapping the sql as a sub-select and ordering in that query.
509
+ def add_order_by_for_association_limiting!(sql, options)
510
+ return sql if options[:order].blank?
511
+
512
+ order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
513
+ order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
514
+ order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')
515
+
516
+ sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
517
+ end
518
+
519
+ # from postgres_adapter.rb in rails project
520
+ # https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L412
521
+ # Quotes PostgreSQL-specific data types for SQL input.
522
+ def quote(value, column = nil) #:nodoc:
523
+ return super unless column
524
+
525
+ case value
526
+ when Float
527
+ return super unless value.infinite? && column.type == :datetime
528
+ "'#{value.to_s.downcase}'"
529
+ when Numeric
530
+ return super unless column.sql_type == 'money'
531
+ # Not truly string input, so doesn't require (or allow) escape string syntax.
532
+ "'#{value}'"
533
+ when String
534
+ case column.sql_type
535
+ when 'bytea' then "'#{escape_bytea(value)}'"
536
+ when 'xml' then "xml '#{quote_string(value)}'"
537
+ when /^bit/
538
+ case value
539
+ when /^[01]*$/ then "B'#{value}'" # Bit-string notation
540
+ when /^[0-9A-F]*$/i then "X'#{value}'" # Hexadecimal notation
541
+ end
542
+ else
543
+ super
544
+ end
545
+ else
546
+ super
547
+ end
548
+ end
549
+
550
+ def escape_bytea(s)
551
+ if s
552
+ result = ''
553
+ s.each_byte { |c| result << sprintf('\\\\%03o', c) }
554
+ result
555
+ end
556
+ end
557
+
558
+ def quote_table_name(name)
559
+ schema, name_part = extract_pg_identifier_from_name(name.to_s)
560
+
561
+ unless name_part
562
+ quote_column_name(schema)
563
+ else
564
+ table_name, name_part = extract_pg_identifier_from_name(name_part)
565
+ "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
566
+ end
567
+ end
568
+
569
+ def quote_column_name(name)
570
+ %("#{name.to_s.gsub("\"", "\"\"")}")
571
+ end
572
+
573
+ def quoted_date(value) #:nodoc:
574
+ if value.acts_like?(:time) && value.respond_to?(:usec)
575
+ "#{super}.#{sprintf("%06d", value.usec)}"
576
+ else
577
+ super
578
+ end
579
+ end
580
+
581
+ def disable_referential_integrity(&block) #:nodoc:
582
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
583
+ yield
584
+ ensure
585
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
586
+ end
587
+
588
+ def rename_table(name, new_name)
589
+ execute "ALTER TABLE #{name} RENAME TO #{new_name}"
590
+ end
591
+
592
+ # Adds a new column to the named table.
593
+ # See TableDefinition#column for details of the options you can use.
594
+ def add_column(table_name, column_name, type, options = {})
595
+ default = options[:default]
596
+ notnull = options[:null] == false
597
+
598
+ # Add the column.
599
+ execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}")
600
+
601
+ change_column_default(table_name, column_name, default) if options_include_default?(options)
602
+ change_column_null(table_name, column_name, false, default) if notnull
603
+ end
604
+
605
+ # Changes the column of a table.
606
+ def change_column(table_name, column_name, type, options = {})
607
+ quoted_table_name = quote_table_name(table_name)
608
+
609
+ begin
610
+ execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
611
+ rescue ActiveRecord::StatementInvalid => e
612
+ raise e if postgresql_version > 80000
613
+ # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
614
+ begin
615
+ begin_db_transaction
616
+ tmp_column_name = "#{column_name}_ar_tmp"
617
+ add_column(table_name, tmp_column_name, type, options)
618
+ execute "UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"
619
+ remove_column(table_name, column_name)
620
+ rename_column(table_name, tmp_column_name, column_name)
621
+ commit_db_transaction
622
+ rescue
623
+ rollback_db_transaction
624
+ end
625
+ end
626
+
627
+ change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
628
+ change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
629
+ end
630
+
631
+ # Changes the default value of a table column.
632
+ def change_column_default(table_name, column_name, default)
633
+ execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
634
+ end
635
+
636
+ def change_column_null(table_name, column_name, null, default = nil)
637
+ unless null || default.nil?
638
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
639
+ end
640
+ execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
641
+ end
642
+
643
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
644
+ execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
645
+ end
646
+
647
+ def remove_index!(table_name, index_name) #:nodoc:
648
+ execute "DROP INDEX #{quote_table_name(index_name)}"
649
+ end
650
+
651
+ def index_name_length
652
+ 63
653
+ end
654
+
655
+ # Maps logical Rails types to PostgreSQL-specific data types.
656
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
657
+ return super unless type.to_s == 'integer'
658
+ return 'integer' unless limit
659
+
660
+ case limit
661
+ when 1, 2; 'smallint'
662
+ when 3, 4; 'integer'
663
+ when 5..8; 'bigint'
664
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
665
+ end
666
+ end
667
+
668
+ def tables(name = nil)
669
+ exec_query(<<-SQL, 'SCHEMA').map { |row| row["tablename"] }
670
+ SELECT tablename
671
+ FROM pg_tables
672
+ WHERE schemaname = ANY (current_schemas(false))
673
+ SQL
674
+ end
675
+
676
+ def table_exists?(name)
677
+ schema, table = extract_schema_and_table(name.to_s)
678
+ return false unless table # Abstract classes is having nil table name
679
+
680
+ binds = [[nil, table.gsub(/(^"|"$)/,'')]]
681
+ binds << [nil, schema] if schema
682
+
683
+ exec_query(<<-SQL, 'SCHEMA', binds).first["table_count"] > 0
684
+ SELECT COUNT(*) as table_count
685
+ FROM pg_tables
686
+ WHERE tablename = ?
687
+ AND schemaname = #{schema ? "?" : "ANY (current_schemas(false))"}
688
+ SQL
689
+ end
690
+
691
+ # Extracts the table and schema name from +name+
692
+ def extract_schema_and_table(name)
693
+ schema, table = name.split('.', 2)
694
+
695
+ unless table # A table was provided without a schema
696
+ table = schema
697
+ schema = nil
698
+ end
699
+
700
+ if name =~ /^"/ # Handle quoted table names
701
+ table = name
702
+ schema = nil
703
+ end
704
+ [schema, table]
705
+ end
706
+
707
+ private
708
+ def translate_exception(exception, message)
709
+ case exception.message
710
+ when /duplicate key value violates unique constraint/
711
+ ::ActiveRecord::RecordNotUnique.new(message, exception)
712
+ when /violates foreign key constraint/
713
+ ::ActiveRecord::InvalidForeignKey.new(message, exception)
714
+ else
715
+ super
716
+ end
717
+ end
718
+
719
+ # Returns the list of a table's column names, data types, and default values.
720
+ #
721
+ # The underlying query is roughly:
722
+ # SELECT column.name, column.type, default.value
723
+ # FROM column LEFT JOIN default
724
+ # ON column.table_id = default.table_id
725
+ # AND column.num = default.column_num
726
+ # WHERE column.table_id = get_table_id('table_name')
727
+ # AND column.num > 0
728
+ # AND NOT column.is_dropped
729
+ # ORDER BY column.num
730
+ #
731
+ # If the table name is not prefixed with a schema, the database will
732
+ # take the first match from the schema search path.
733
+ #
734
+ # Query implementation notes:
735
+ # - format_type includes the column size constraint, e.g. varchar(50)
736
+ # - ::regclass is a function that gives the id for a table name
737
+ def column_definitions(table_name) #:nodoc:
738
+ exec_query(<<-end_sql, 'SCHEMA')
739
+ 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
740
+ FROM pg_attribute a LEFT JOIN pg_attrdef d
741
+ ON a.attrelid = d.adrelid AND a.attnum = d.adnum
742
+ WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
743
+ AND a.attnum > 0 AND NOT a.attisdropped
744
+ ORDER BY a.attnum
745
+ end_sql
746
+ end
747
+
748
+ def extract_pg_identifier_from_name(name)
749
+ match_data = name[0,1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
750
+
751
+ if match_data
752
+ rest = name[match_data[0].length..-1]
753
+ rest = rest[1..-1] if rest[0,1] == "."
754
+ [match_data[1], (rest.length > 0 ? rest : nil)]
755
+ end
756
+ end
757
+
758
+ # from rails postgresl_adapter
759
+ def extract_table_ref_from_insert_sql(sql)
760
+ sql[/into\s+([^\(]*).*values\s*\(/i]
761
+ $1.strip if $1
762
+ end
763
+ end
764
+ end
765
+
766
+ module ActiveRecord::ConnectionAdapters
767
+ remove_const(:PostgreSQLAdapter) if const_defined?(:PostgreSQLAdapter)
768
+
769
+ class PostgreSQLColumn < JdbcColumn
770
+ include ArJdbc::PostgreSQL::Column
771
+
772
+ def initialize(name, *args)
773
+ if Hash === name
774
+ super
775
+ else
776
+ super(nil, name, *args)
777
+ end
778
+ end
779
+
780
+ def call_discovered_column_callbacks(*)
781
+ end
782
+ end
783
+
784
+ class PostgresJdbcConnection < JdbcConnection
785
+ alias :java_native_database_types :set_native_database_types
786
+
787
+ # override to prevent connection from loading hash from jdbc
788
+ # metadata, which can be expensive. We can do this since
789
+ # native_database_types is defined in the adapter to use a static hash
790
+ # not relying on the driver's metadata
791
+ def set_native_database_types
792
+ @native_types = {}
793
+ end
794
+ end
795
+
796
+ class PostgreSQLAdapter < JdbcAdapter
797
+ include ArJdbc::PostgreSQL
798
+
799
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
800
+ def xml(*args)
801
+ options = args.extract_options!
802
+ column(args[0], "xml", options)
803
+ end
804
+
805
+ def tsvector(*args)
806
+ options = args.extract_options!
807
+ column(args[0], "tsvector", options)
808
+ end
809
+ end
810
+
811
+ def table_definition
812
+ TableDefinition.new(self)
813
+ end
814
+
815
+ def jdbc_connection_class(spec)
816
+ ::ArJdbc::PostgreSQL.jdbc_connection_class
817
+ end
818
+
819
+ def jdbc_column_class
820
+ ActiveRecord::ConnectionAdapters::PostgreSQLColumn
821
+ end
822
+
823
+ alias_chained_method :columns, :query_cache, :pg_columns
824
+ end
825
+ end