activerecord-jdbc-adapter 5.0.pre1 → 50.0
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.
Potentially problematic release.
This version of activerecord-jdbc-adapter might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.gitignore +1 -2
- data/.travis.yml +15 -416
- data/Gemfile +35 -37
- data/README.md +23 -118
- data/RUNNING_TESTS.md +31 -26
- data/Rakefile +2 -3
- data/lib/arjdbc/abstract/connection_management.rb +21 -0
- data/lib/arjdbc/abstract/core.rb +62 -0
- data/lib/arjdbc/abstract/database_statements.rb +46 -0
- data/lib/arjdbc/abstract/statement_cache.rb +58 -0
- data/lib/arjdbc/abstract/transaction_support.rb +86 -0
- data/lib/arjdbc/derby/adapter.rb +6 -1
- data/lib/arjdbc/discover.rb +0 -7
- data/lib/arjdbc/firebird/adapter.rb +2 -2
- data/lib/arjdbc/jdbc.rb +2 -2
- data/lib/arjdbc/jdbc/adapter.rb +10 -252
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/connection.rb +6 -0
- data/lib/arjdbc/mysql/adapter.rb +82 -946
- data/lib/arjdbc/mysql/connection_methods.rb +4 -2
- data/lib/arjdbc/postgresql/adapter.rb +270 -970
- data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
- data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
- data/lib/arjdbc/postgresql/base/pgconn.rb +8 -5
- data/lib/arjdbc/postgresql/column.rb +10 -599
- data/lib/arjdbc/postgresql/connection_methods.rb +9 -0
- data/lib/arjdbc/postgresql/name.rb +24 -0
- data/lib/arjdbc/postgresql/oid_types.rb +28 -109
- data/lib/arjdbc/sqlite3/adapter.rb +18 -42
- data/lib/arjdbc/tasks/database_tasks.rb +1 -3
- data/lib/arjdbc/tasks/db2_database_tasks.rb +2 -2
- data/lib/arjdbc/version.rb +1 -1
- data/pom.xml +3 -3
- data/rakelib/02-test.rake +0 -12
- data/rakelib/compile.rake +1 -1
- data/rakelib/db.rake +7 -5
- data/rakelib/rails.rake +67 -64
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +1 -17
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +518 -1260
- data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +53 -134
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -240
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +0 -20
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +85 -10
- metadata +16 -29
- data/Appraisals +0 -41
- data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
- data/lib/arjdbc/common_jdbc_methods.rb +0 -89
- data/lib/arjdbc/mysql/bulk_change_table.rb +0 -150
- data/lib/arjdbc/mysql/column.rb +0 -162
- data/lib/arjdbc/mysql/explain_support.rb +0 -82
- data/lib/arjdbc/mysql/schema_creation.rb +0 -58
- data/lib/arjdbc/oracle.rb +0 -4
- data/lib/arjdbc/oracle/adapter.rb +0 -952
- data/lib/arjdbc/oracle/column.rb +0 -126
- data/lib/arjdbc/oracle/connection_methods.rb +0 -21
- data/lib/arjdbc/postgresql/base/oid.rb +0 -412
- data/lib/arjdbc/postgresql/base/schema_definitions.rb +0 -131
- data/lib/arjdbc/postgresql/explain_support.rb +0 -53
- data/lib/arjdbc/postgresql/oid/bytea.rb +0 -2
- data/lib/arjdbc/postgresql/schema_creation.rb +0 -60
- data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +0 -297
- data/lib/arjdbc/tasks/oracle_database_tasks.rb +0 -65
- data/src/java/arjdbc/oracle/OracleModule.java +0 -75
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +0 -465
@@ -1,7 +1,7 @@
|
|
1
1
|
ArJdbc::ConnectionMethods.module_eval do
|
2
2
|
def mysql_connection(config)
|
3
3
|
config[:adapter_spec] ||= ::ArJdbc::MySQL
|
4
|
-
config[:adapter_class] = ActiveRecord::ConnectionAdapters::
|
4
|
+
config[:adapter_class] = ActiveRecord::ConnectionAdapters::Mysql2Adapter unless config.key?(:adapter_class)
|
5
5
|
|
6
6
|
return jndi_connection(config) if jndi_config?(config)
|
7
7
|
|
@@ -29,6 +29,8 @@ ArJdbc::ConnectionMethods.module_eval do
|
|
29
29
|
|
30
30
|
mariadb_driver = ! mysql_driver && driver[0, 12] == 'org.mariadb.' # org.mariadb.jdbc.Driver
|
31
31
|
|
32
|
+
config[:prepared_statements] ||= true if ENV['PREPARED_STATEMENTS'] == 'true'
|
33
|
+
|
32
34
|
properties = ( config[:properties] ||= {} )
|
33
35
|
if mysql_driver
|
34
36
|
properties['zeroDateTimeBehavior'] ||= 'convertToNull'
|
@@ -78,7 +80,7 @@ ArJdbc::ConnectionMethods.module_eval do
|
|
78
80
|
|
79
81
|
def mariadb_connection(config)
|
80
82
|
config[:adapter_spec] ||= ::ArJdbc::MySQL
|
81
|
-
config[:adapter_class] = ActiveRecord::ConnectionAdapters::
|
83
|
+
config[:adapter_class] = ActiveRecord::ConnectionAdapters::Mysql2Adapter unless config.key?(:adapter_class)
|
82
84
|
|
83
85
|
return jndi_connection(config) if jndi_config?(config)
|
84
86
|
|
@@ -2,30 +2,38 @@
|
|
2
2
|
ArJdbc.load_java_part :PostgreSQL
|
3
3
|
|
4
4
|
require 'ipaddr'
|
5
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
6
|
+
require 'active_record/connection_adapters/postgresql/column'
|
7
|
+
require 'active_record/connection_adapters/postgresql/explain_pretty_printer'
|
8
|
+
require 'active_record/connection_adapters/postgresql/quoting'
|
9
|
+
require 'active_record/connection_adapters/postgresql/referential_integrity'
|
10
|
+
require 'active_record/connection_adapters/postgresql/schema_dumper'
|
11
|
+
require 'active_record/connection_adapters/postgresql/schema_statements'
|
12
|
+
require 'active_record/connection_adapters/postgresql/type_metadata'
|
13
|
+
require 'active_record/connection_adapters/postgresql/utils'
|
14
|
+
require 'arjdbc/abstract/core'
|
15
|
+
require 'arjdbc/abstract/connection_management'
|
16
|
+
require 'arjdbc/abstract/database_statements'
|
17
|
+
require 'arjdbc/abstract/statement_cache'
|
18
|
+
require 'arjdbc/abstract/transaction_support'
|
19
|
+
require 'arjdbc/postgresql/base/array_decoder'
|
20
|
+
require 'arjdbc/postgresql/base/array_encoder'
|
21
|
+
require 'arjdbc/postgresql/name'
|
5
22
|
|
6
23
|
module ArJdbc
|
7
24
|
# Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.
|
8
25
|
module PostgreSQL
|
9
26
|
|
10
|
-
# @deprecated no longer used
|
11
|
-
# @private
|
12
|
-
AR4_COMPAT = AR40
|
13
|
-
# @deprecated no longer used
|
14
|
-
# @private
|
15
|
-
AR42_COMPAT = AR42
|
16
|
-
|
17
27
|
require 'arjdbc/postgresql/column'
|
18
|
-
require 'arjdbc/postgresql/explain_support'
|
19
|
-
require 'arjdbc/postgresql/schema_creation' # AR 4.x
|
20
28
|
require 'arel/visitors/postgresql_jdbc'
|
21
29
|
# @private
|
22
30
|
IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition
|
23
31
|
|
24
32
|
# @private
|
25
|
-
ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
|
33
|
+
ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
|
26
34
|
|
27
35
|
# @private
|
28
|
-
Type = ::ActiveRecord::Type
|
36
|
+
Type = ::ActiveRecord::Type
|
29
37
|
|
30
38
|
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
|
31
39
|
def self.jdbc_connection_class
|
@@ -130,172 +138,61 @@ module ArJdbc
|
|
130
138
|
# @private
|
131
139
|
ActiveRecordError = ::ActiveRecord::ActiveRecordError
|
132
140
|
|
133
|
-
# Maps logical Rails types to PostgreSQL-specific data types.
|
134
|
-
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
135
|
-
case type.to_s
|
136
|
-
when 'binary'
|
137
|
-
# PostgreSQL doesn't support limits on binary (bytea) columns.
|
138
|
-
# The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
|
139
|
-
case limit
|
140
|
-
when nil, 0..0x3fffffff; super(type)
|
141
|
-
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
|
142
|
-
end
|
143
|
-
when 'text'
|
144
|
-
# PostgreSQL doesn't support limits on text columns.
|
145
|
-
# The hard limit is 1Gb, according to section 8.3 in the manual.
|
146
|
-
case limit
|
147
|
-
when nil, 0..0x3fffffff; super(type)
|
148
|
-
else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
|
149
|
-
end
|
150
|
-
when 'integer'
|
151
|
-
return 'integer' unless limit
|
152
|
-
|
153
|
-
case limit
|
154
|
-
when 1, 2; 'smallint'
|
155
|
-
when 3, 4; 'integer'
|
156
|
-
when 5..8; 'bigint'
|
157
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
|
158
|
-
end
|
159
|
-
when 'datetime'
|
160
|
-
return super unless precision
|
161
|
-
|
162
|
-
case precision
|
163
|
-
when 0..6; "timestamp(#{precision})"
|
164
|
-
else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
165
|
-
end
|
166
|
-
else
|
167
|
-
super
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
def type_cast(value, column, array_member = false)
|
172
|
-
return super(value, nil) unless column
|
173
|
-
|
174
|
-
case value
|
175
|
-
when String
|
176
|
-
return super(value, column) unless 'bytea' == column.sql_type
|
177
|
-
value # { :value => value, :format => 1 }
|
178
|
-
when Array
|
179
|
-
case column.sql_type
|
180
|
-
when 'point'
|
181
|
-
jdbc_column_class.point_to_string(value)
|
182
|
-
when 'json', 'jsonb'
|
183
|
-
jdbc_column_class.json_to_string(value)
|
184
|
-
else
|
185
|
-
return super(value, column) unless column.array?
|
186
|
-
jdbc_column_class.array_to_string(value, column, self)
|
187
|
-
end
|
188
|
-
when NilClass
|
189
|
-
if column.array? && array_member
|
190
|
-
'NULL'
|
191
|
-
elsif column.array?
|
192
|
-
value
|
193
|
-
else
|
194
|
-
super(value, column)
|
195
|
-
end
|
196
|
-
when Hash
|
197
|
-
case column.sql_type
|
198
|
-
when 'hstore'
|
199
|
-
jdbc_column_class.hstore_to_string(value, array_member)
|
200
|
-
when 'json', 'jsonb'
|
201
|
-
jdbc_column_class.json_to_string(value)
|
202
|
-
else super(value, column)
|
203
|
-
end
|
204
|
-
when IPAddr
|
205
|
-
return super unless column.sql_type == 'inet' || column.sql_type == 'cidr'
|
206
|
-
jdbc_column_class.cidr_to_string(value)
|
207
|
-
when Range
|
208
|
-
return super(value, column) unless /range$/ =~ column.sql_type
|
209
|
-
jdbc_column_class.range_to_string(value)
|
210
|
-
else
|
211
|
-
super(value, column)
|
212
|
-
end
|
213
|
-
end if AR40 && ! AR42
|
214
|
-
|
215
|
-
# @private
|
216
|
-
def _type_cast(value)
|
217
|
-
case value
|
218
|
-
when Type::Binary::Data
|
219
|
-
# Return a bind param hash with format as binary.
|
220
|
-
# See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
|
221
|
-
# for more information
|
222
|
-
{ :value => value.to_s, :format => 1 }
|
223
|
-
when OID::Xml::Data, OID::Bit::Data
|
224
|
-
value.to_s
|
225
|
-
else
|
226
|
-
super
|
227
|
-
end
|
228
|
-
end if AR42
|
229
|
-
private :_type_cast if AR42
|
230
|
-
|
231
141
|
NATIVE_DATABASE_TYPES = {
|
232
|
-
:
|
233
|
-
:
|
234
|
-
:
|
235
|
-
:
|
236
|
-
:
|
237
|
-
:
|
238
|
-
:
|
239
|
-
:
|
240
|
-
:
|
241
|
-
:
|
242
|
-
:
|
243
|
-
:
|
244
|
-
:
|
245
|
-
:
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
:
|
250
|
-
:
|
251
|
-
:
|
142
|
+
bigserial: 'bigserial',
|
143
|
+
primary_key: 'serial primary key',
|
144
|
+
bigint: { name: 'bigint' },
|
145
|
+
binary: { name: 'bytea' },
|
146
|
+
bit: { name: 'bit' },
|
147
|
+
bit_varying: { name: 'bit varying' },
|
148
|
+
boolean: { name: 'boolean' },
|
149
|
+
box: { name: 'box' },
|
150
|
+
char: { name: 'char' },
|
151
|
+
cidr: { name: 'cidr' },
|
152
|
+
circle: { name: 'circle' },
|
153
|
+
citext: { name: 'citext' },
|
154
|
+
date: { name: 'date' },
|
155
|
+
daterange: { name: 'daterange' },
|
156
|
+
datetime: { name: 'timestamp' },
|
157
|
+
decimal: { name: 'decimal' }, # :limit => 1000
|
158
|
+
float: { name: 'float' },
|
159
|
+
hstore: { name: 'hstore' },
|
160
|
+
inet: { name: 'inet' },
|
161
|
+
int4range: { name: 'int4range' },
|
162
|
+
int8range: { name: 'int8range' },
|
163
|
+
integer: { name: 'integer' },
|
164
|
+
interval: { name: 'interval' }, # This doesn't get added to AR's postgres adapter until 5.1 but it fixes broken tests in 5.0 ...
|
165
|
+
json: { name: 'json' },
|
166
|
+
jsonb: { name: 'jsonb' },
|
167
|
+
line: { name: 'line' },
|
168
|
+
lseg: { name: 'lseg' },
|
169
|
+
ltree: { name: 'ltree' },
|
170
|
+
macaddr: { name: 'macaddr' },
|
171
|
+
money: { name: 'money' },
|
172
|
+
numeric: { name: 'numeric' },
|
173
|
+
numrange: { name: 'numrange' },
|
174
|
+
path: { name: 'path' },
|
175
|
+
point: { name: 'point' },
|
176
|
+
polygon: { name: 'polygon' },
|
177
|
+
serial: { name: 'serial' }, # auto-inc integer, bigserial, smallserial
|
178
|
+
string: { name: 'character varying' },
|
179
|
+
text: { name: 'text' },
|
180
|
+
time: { name: 'time' },
|
181
|
+
timestamp: { name: 'timestamp' },
|
182
|
+
tsrange: { name: 'tsrange' },
|
183
|
+
tstzrange: { name: 'tstzrange' },
|
184
|
+
tsvector: { name: 'tsvector' },
|
185
|
+
uuid: { name: 'uuid' },
|
186
|
+
xml: { name: 'xml' }
|
252
187
|
}
|
253
188
|
|
254
|
-
NATIVE_DATABASE_TYPES.update({
|
255
|
-
:tsvector => { :name => "tsvector" },
|
256
|
-
:hstore => { :name => "hstore" },
|
257
|
-
:inet => { :name => "inet" },
|
258
|
-
:cidr => { :name => "cidr" },
|
259
|
-
:macaddr => { :name => "macaddr" },
|
260
|
-
:uuid => { :name => "uuid" },
|
261
|
-
:json => { :name => "json" },
|
262
|
-
:jsonb => { :name => "jsonb" },
|
263
|
-
:ltree => { :name => "ltree" },
|
264
|
-
# ranges :
|
265
|
-
:daterange => { :name => "daterange" },
|
266
|
-
:numrange => { :name => "numrange" },
|
267
|
-
:tsrange => { :name => "tsrange" },
|
268
|
-
:tstzrange => { :name => "tstzrange" },
|
269
|
-
:int4range => { :name => "int4range" },
|
270
|
-
:int8range => { :name => "int8range" },
|
271
|
-
}) if AR40
|
272
|
-
|
273
|
-
NATIVE_DATABASE_TYPES.update(
|
274
|
-
:string => { :name => "character varying" },
|
275
|
-
:bigserial => "bigserial",
|
276
|
-
:bigint => { :name => "bigint" },
|
277
|
-
:bit => { :name => "bit" },
|
278
|
-
:bit_varying => { :name => "bit varying" }
|
279
|
-
) if AR42
|
280
|
-
|
281
189
|
def native_database_types
|
282
190
|
NATIVE_DATABASE_TYPES
|
283
191
|
end
|
284
192
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
spec = super
|
289
|
-
spec[:array] = 'true' if column.respond_to?(:array) && column.array
|
290
|
-
spec[:default] = "\"#{column.default_function}\"" if column.default_function
|
291
|
-
spec
|
292
|
-
end if AR40
|
293
|
-
|
294
|
-
# Adds `:array` as a valid migration key.
|
295
|
-
# @override
|
296
|
-
def migration_keys
|
297
|
-
super + [:array]
|
298
|
-
end if AR40
|
193
|
+
def valid_type?(type)
|
194
|
+
!native_database_types[type].nil?
|
195
|
+
end
|
299
196
|
|
300
197
|
# Enable standard-conforming strings if available.
|
301
198
|
def set_standard_conforming_strings
|
@@ -333,15 +230,25 @@ module ArJdbc
|
|
333
230
|
@standard_conforming_strings == true # return false if :unsupported
|
334
231
|
end
|
335
232
|
|
336
|
-
|
337
|
-
def supports_migrations?
|
338
|
-
true
|
339
|
-
end
|
233
|
+
def supports_ddl_transactions?; true end
|
340
234
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
235
|
+
def supports_explain?; true end
|
236
|
+
|
237
|
+
def supports_expression_index?; true end
|
238
|
+
|
239
|
+
def supports_index_sort_order?; true end
|
240
|
+
|
241
|
+
def supports_migrations?; true end
|
242
|
+
|
243
|
+
def supports_partial_index?; true end
|
244
|
+
|
245
|
+
def supports_primary_key?; true end # Supports finding primary key on non-Active Record tables
|
246
|
+
|
247
|
+
def supports_savepoints?; true end
|
248
|
+
|
249
|
+
def supports_transaction_isolation?(level = nil); true end
|
250
|
+
|
251
|
+
def supports_views?; true end
|
345
252
|
|
346
253
|
# Does PostgreSQL support standard conforming strings?
|
347
254
|
def supports_standard_conforming_strings?
|
@@ -357,50 +264,9 @@ module ArJdbc
|
|
357
264
|
postgresql_version >= 80200
|
358
265
|
end
|
359
266
|
|
360
|
-
def supports_ddl_transactions?; true end
|
361
|
-
|
362
|
-
def supports_transaction_isolation?; true end
|
363
|
-
|
364
|
-
def supports_index_sort_order?; true end
|
365
|
-
|
366
|
-
def supports_partial_index?; true end if AR40
|
367
|
-
|
368
267
|
# Range data-types weren't introduced until PostgreSQL 9.2.
|
369
268
|
def supports_ranges?
|
370
269
|
postgresql_version >= 90200
|
371
|
-
end if AR40
|
372
|
-
|
373
|
-
def supports_transaction_isolation?(level = nil)
|
374
|
-
true
|
375
|
-
end
|
376
|
-
|
377
|
-
# @override
|
378
|
-
def supports_views?; true end
|
379
|
-
|
380
|
-
if ArJdbc::AR50
|
381
|
-
def views
|
382
|
-
select_values("SELECT table_name FROM INFORMATION_SCHEMA.views WHERE table_schema = ANY (current_schemas(false))")
|
383
|
-
end
|
384
|
-
end
|
385
|
-
|
386
|
-
# NOTE: handled by JdbcAdapter we override only to have save-point in logs :
|
387
|
-
|
388
|
-
# @override
|
389
|
-
def supports_savepoints?; true end
|
390
|
-
|
391
|
-
# @override
|
392
|
-
def create_savepoint(name = current_savepoint_name(true))
|
393
|
-
log("SAVEPOINT #{name}", 'Savepoint') { super }
|
394
|
-
end
|
395
|
-
|
396
|
-
# @override
|
397
|
-
def rollback_to_savepoint(name = current_savepoint_name(true))
|
398
|
-
log("ROLLBACK TO SAVEPOINT #{name}", 'Savepoint') { super }
|
399
|
-
end
|
400
|
-
|
401
|
-
# @override
|
402
|
-
def release_savepoint(name = current_savepoint_name(false))
|
403
|
-
log("RELEASE SAVEPOINT #{name}", 'Savepoint') { super }
|
404
270
|
end
|
405
271
|
|
406
272
|
def supports_extensions?
|
@@ -441,159 +307,75 @@ module ArJdbc
|
|
441
307
|
execute "SET SESSION AUTHORIZATION #{user}"
|
442
308
|
end
|
443
309
|
|
310
|
+
# Came from postgres_adapter
|
311
|
+
def get_advisory_lock(lock_id) # :nodoc:
|
312
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
313
|
+
raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
|
314
|
+
end
|
315
|
+
select_value("SELECT pg_try_advisory_lock(#{lock_id});")
|
316
|
+
end
|
317
|
+
|
318
|
+
# Came from postgres_adapter
|
319
|
+
def release_advisory_lock(lock_id) # :nodoc:
|
320
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
321
|
+
raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
|
322
|
+
end
|
323
|
+
select_value("SELECT pg_advisory_unlock(#{lock_id})") == 't'.freeze
|
324
|
+
end
|
325
|
+
|
444
326
|
# Returns the configured supported identifier length supported by PostgreSQL,
|
445
327
|
# or report the default of 63 on PostgreSQL 7.x.
|
446
328
|
def table_alias_length
|
447
329
|
@table_alias_length ||= (
|
448
330
|
postgresql_version >= 80000 ?
|
449
|
-
select_one('SHOW max_identifier_length')['max_identifier_length'].to_i :
|
331
|
+
select_one('SHOW max_identifier_length', 'SCHEMA'.freeze)['max_identifier_length'].to_i :
|
450
332
|
63
|
451
333
|
)
|
452
334
|
end
|
453
335
|
|
454
|
-
def
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
if pk && sequence
|
466
|
-
quoted_sequence = quote_column_name(sequence)
|
467
|
-
|
468
|
-
select_value <<-end_sql, 'Reset Sequence'
|
469
|
-
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)
|
470
|
-
end_sql
|
471
|
-
end
|
472
|
-
end
|
473
|
-
|
474
|
-
# Find a table's primary key and sequence.
|
475
|
-
def pk_and_sequence_for(table)
|
476
|
-
# try looking for a seq with a dependency on the table's primary key :
|
477
|
-
result = select(<<-end_sql, 'PK and Serial Sequence')[0]
|
478
|
-
SELECT attr.attname, seq.relname
|
479
|
-
FROM pg_class seq,
|
480
|
-
pg_attribute attr,
|
481
|
-
pg_depend dep,
|
482
|
-
pg_constraint cons
|
483
|
-
WHERE seq.oid = dep.objid
|
484
|
-
AND seq.relkind = 'S'
|
485
|
-
AND attr.attrelid = dep.refobjid
|
486
|
-
AND attr.attnum = dep.refobjsubid
|
487
|
-
AND attr.attrelid = cons.conrelid
|
488
|
-
AND attr.attnum = cons.conkey[1]
|
489
|
-
AND cons.contype = 'p'
|
490
|
-
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
491
|
-
end_sql
|
492
|
-
|
493
|
-
if result.nil? || result.empty?
|
494
|
-
# if that fails, try parsing the primary key's default value :
|
495
|
-
result = select(<<-end_sql, 'PK and Custom Sequence')[0]
|
496
|
-
SELECT attr.attname,
|
497
|
-
CASE
|
498
|
-
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
|
499
|
-
WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
|
500
|
-
substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
|
501
|
-
strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
|
502
|
-
ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
|
503
|
-
END as relname
|
504
|
-
FROM pg_class t
|
505
|
-
JOIN pg_attribute attr ON (t.oid = attrelid)
|
506
|
-
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
507
|
-
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
508
|
-
WHERE t.oid = '#{quote_table_name(table)}'::regclass
|
509
|
-
AND cons.contype = 'p'
|
510
|
-
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
511
|
-
end_sql
|
336
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
337
|
+
val = super
|
338
|
+
if !use_insert_returning? && pk
|
339
|
+
unless sequence_name
|
340
|
+
table_ref = extract_table_ref_from_insert_sql(sql)
|
341
|
+
sequence_name = default_sequence_name(table_ref, pk)
|
342
|
+
return val unless sequence_name
|
343
|
+
end
|
344
|
+
last_insert_id_result(sequence_name)
|
345
|
+
else
|
346
|
+
val
|
512
347
|
end
|
513
|
-
|
514
|
-
[ result['attname'], result['relname'] ]
|
515
|
-
rescue
|
516
|
-
nil
|
517
348
|
end
|
518
349
|
|
519
|
-
def
|
520
|
-
|
521
|
-
|
522
|
-
FROM pg_attribute attr
|
523
|
-
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey)
|
524
|
-
WHERE cons.contype = 'p' AND cons.conrelid = '#{quote_table_name(table)}'::regclass
|
525
|
-
end_sql
|
526
|
-
|
527
|
-
result && result['attname']
|
528
|
-
# pk_and_sequence = pk_and_sequence_for(table)
|
529
|
-
# pk_and_sequence && pk_and_sequence.first
|
350
|
+
def explain(arel, binds = [])
|
351
|
+
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
352
|
+
ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
|
530
353
|
end
|
531
354
|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
end
|
538
|
-
|
539
|
-
if pk && use_insert_returning? # && id_value.nil?
|
540
|
-
select_value("#{to_sql(sql, binds)} RETURNING #{quote_column_name(pk)}")
|
355
|
+
# Take an id from the result of an INSERT query.
|
356
|
+
# @return [Integer, NilClass]
|
357
|
+
def last_inserted_id(result)
|
358
|
+
if result.is_a?(Hash) || result.is_a?(ActiveRecord::Result)
|
359
|
+
result.first.first[1] # .first = { "id"=>1 } .first = [ "id", 1 ]
|
541
360
|
else
|
542
|
-
|
543
|
-
unless id_value
|
544
|
-
table_ref ||= extract_table_ref_from_insert_sql(sql)
|
545
|
-
# If neither PK nor sequence name is given, look them up.
|
546
|
-
if table_ref && ! ( pk ||= primary_key(table_ref) ) && ! sequence_name
|
547
|
-
pk, sequence_name = pk_and_sequence_for(table_ref)
|
548
|
-
end
|
549
|
-
# If a PK is given, fallback to default sequence name.
|
550
|
-
# Don't fetch last insert id for a table without a PK.
|
551
|
-
if pk && sequence_name ||= default_sequence_name(table_ref, pk)
|
552
|
-
id_value = last_insert_id(table_ref, sequence_name)
|
553
|
-
end
|
554
|
-
end
|
555
|
-
id_value
|
361
|
+
result
|
556
362
|
end
|
557
363
|
end
|
558
364
|
|
559
|
-
#
|
560
|
-
|
561
|
-
unless pk
|
365
|
+
def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
|
366
|
+
if pk.nil?
|
562
367
|
# Extract the table from the insert sql. Yuck.
|
563
368
|
table_ref = extract_table_ref_from_insert_sql(sql)
|
564
369
|
pk = primary_key(table_ref) if table_ref
|
565
370
|
end
|
566
371
|
|
372
|
+
pk = nil if pk.is_a?(Array)
|
373
|
+
|
567
374
|
if pk && use_insert_returning?
|
568
375
|
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
|
569
376
|
end
|
570
377
|
|
571
|
-
|
572
|
-
end
|
573
|
-
|
574
|
-
# @override due RETURNING clause
|
575
|
-
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
576
|
-
# NOTE: 3.2 does not pass the PK on #insert (passed only into #sql_for_insert) :
|
577
|
-
# sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
|
578
|
-
# 3.2 :
|
579
|
-
# value = exec_insert(sql, name, binds)
|
580
|
-
# 4.x :
|
581
|
-
# value = exec_insert(sql, name, binds, pk, sequence_name)
|
582
|
-
if use_insert_returning? && ( pk || (sql.is_a?(String) && sql =~ /RETURNING "?\S+"?$/) )
|
583
|
-
exec_query(sql, name, binds) # due RETURNING clause returns a result set
|
584
|
-
else
|
585
|
-
result = super
|
586
|
-
if pk
|
587
|
-
unless sequence_name
|
588
|
-
table_ref = extract_table_ref_from_insert_sql(sql)
|
589
|
-
sequence_name = default_sequence_name(table_ref, pk)
|
590
|
-
return result unless sequence_name
|
591
|
-
end
|
592
|
-
last_insert_id_result(sequence_name)
|
593
|
-
else
|
594
|
-
result
|
595
|
-
end
|
596
|
-
end
|
378
|
+
super
|
597
379
|
end
|
598
380
|
|
599
381
|
# @note Only for "better" AR 4.0 compatibility.
|
@@ -601,103 +383,29 @@ module ArJdbc
|
|
601
383
|
def query(sql, name = nil)
|
602
384
|
log(sql, name) do
|
603
385
|
result = []
|
604
|
-
@connection.execute_query_raw(sql,
|
605
|
-
|
386
|
+
@connection.execute_query_raw(sql, []) do |*values|
|
387
|
+
# We need to use #deep_dup here because it appears that
|
388
|
+
# the java method is reusing an object in some cases
|
389
|
+
# which makes all of the entries in the "result"
|
390
|
+
# array end up with the same values as the last row
|
391
|
+
result << values.deep_dup
|
606
392
|
end
|
607
393
|
result
|
608
394
|
end
|
609
395
|
end
|
610
396
|
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
'SCHEMA')
|
618
|
-
end
|
619
|
-
|
620
|
-
# Returns true if schema exists.
|
621
|
-
def schema_exists?(name)
|
622
|
-
select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", 'SCHEMA').to_i > 0
|
623
|
-
end
|
624
|
-
|
625
|
-
# Returns the current schema name.
|
626
|
-
def current_schema
|
627
|
-
select_value('SELECT current_schema', 'SCHEMA')
|
628
|
-
end
|
629
|
-
|
630
|
-
# current database name
|
631
|
-
def current_database
|
632
|
-
select_value('SELECT current_database()', 'SCHEMA')
|
633
|
-
end
|
634
|
-
|
635
|
-
# Returns the current database encoding format.
|
636
|
-
def encoding
|
637
|
-
select_value(
|
638
|
-
"SELECT pg_encoding_to_char(pg_database.encoding)" <<
|
639
|
-
" FROM pg_database" <<
|
640
|
-
" WHERE pg_database.datname LIKE '#{current_database}'",
|
641
|
-
'SCHEMA')
|
642
|
-
end
|
643
|
-
|
644
|
-
# Returns the current database collation.
|
645
|
-
def collation
|
646
|
-
select_value(
|
647
|
-
"SELECT pg_database.datcollate" <<
|
648
|
-
" FROM pg_database" <<
|
649
|
-
" WHERE pg_database.datname LIKE '#{current_database}'",
|
650
|
-
'SCHEMA')
|
651
|
-
end
|
652
|
-
|
653
|
-
# Returns the current database ctype.
|
654
|
-
def ctype
|
655
|
-
select_value(
|
656
|
-
"SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'",
|
657
|
-
'SCHEMA')
|
658
|
-
end
|
659
|
-
|
660
|
-
# Returns the active schema search path.
|
661
|
-
def schema_search_path
|
662
|
-
@schema_search_path ||= select_value('SHOW search_path', 'SCHEMA')
|
663
|
-
end
|
664
|
-
|
665
|
-
# Sets the schema search path to a string of comma-separated schema names.
|
666
|
-
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
667
|
-
# See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
|
668
|
-
#
|
669
|
-
# This should be not be called manually but set in database.yml.
|
670
|
-
def schema_search_path=(schema_csv)
|
671
|
-
if schema_csv
|
672
|
-
execute "SET search_path TO #{schema_csv}"
|
673
|
-
@schema_search_path = schema_csv
|
674
|
-
end
|
675
|
-
end
|
676
|
-
|
677
|
-
# Take an id from the result of an INSERT query.
|
678
|
-
# @return [Integer, NilClass]
|
679
|
-
def last_inserted_id(result)
|
680
|
-
return nil if result.nil?
|
681
|
-
return result if result.is_a? Integer
|
682
|
-
# <ActiveRecord::Result @hash_rows=nil, @columns=["id"], @rows=[[3]]>
|
683
|
-
# but it will work with [{ 'id' => 1 }] Hash wrapped results as well
|
684
|
-
result.first.first[1] # .first = { "id"=>1 } .first = [ "id", 1 ]
|
685
|
-
end
|
686
|
-
|
687
|
-
def last_insert_id(table, sequence_name = nil)
|
688
|
-
sequence_name = table if sequence_name.nil? # AR-4.0 1 argument
|
689
|
-
last_insert_id_result(sequence_name)
|
397
|
+
def reset!
|
398
|
+
clear_cache!
|
399
|
+
reset_transaction
|
400
|
+
@connection.rollback # Have to deal with rollbacks differently than the AR adapter
|
401
|
+
@connection.execute 'DISCARD ALL'
|
402
|
+
configure_connection
|
690
403
|
end
|
691
404
|
|
692
405
|
def last_insert_id_result(sequence_name)
|
693
406
|
select_value("SELECT currval('#{sequence_name}')", 'SQL')
|
694
407
|
end
|
695
408
|
|
696
|
-
def recreate_database(name, options = {})
|
697
|
-
drop_database(name)
|
698
|
-
create_database(name, options)
|
699
|
-
end
|
700
|
-
|
701
409
|
# Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
|
702
410
|
# <tt>:encoding</tt>, <tt>:collation</tt>, <tt>:ctype</tt>,
|
703
411
|
# <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
|
@@ -733,56 +441,10 @@ module ArJdbc
|
|
733
441
|
execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
|
734
442
|
end
|
735
443
|
|
736
|
-
def drop_database(name)
|
737
|
-
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
738
|
-
end
|
739
|
-
|
740
|
-
# Creates a schema for the given schema name.
|
741
|
-
def create_schema(schema_name, pg_username = nil)
|
742
|
-
if pg_username.nil? # AR 4.0 compatibility - accepts only single argument
|
743
|
-
execute "CREATE SCHEMA #{schema_name}"
|
744
|
-
else
|
745
|
-
execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
|
746
|
-
end
|
747
|
-
end
|
748
|
-
|
749
|
-
# Drops the schema for the given schema name.
|
750
|
-
def drop_schema schema_name
|
751
|
-
execute "DROP SCHEMA #{schema_name} CASCADE"
|
752
|
-
end
|
753
|
-
|
754
444
|
def all_schemas
|
755
445
|
select('SELECT nspname FROM pg_namespace').map { |row| row["nspname"] }
|
756
446
|
end
|
757
447
|
|
758
|
-
# @deprecated no longer used - handled with (AR built-in) Rake tasks
|
759
|
-
def structure_dump
|
760
|
-
database = @config[:database]
|
761
|
-
if database.nil?
|
762
|
-
if @config[:url] =~ /\/([^\/]*)$/
|
763
|
-
database = $1
|
764
|
-
else
|
765
|
-
raise "Could not figure out what database this url is for #{@config["url"]}"
|
766
|
-
end
|
767
|
-
end
|
768
|
-
|
769
|
-
ENV['PGHOST'] = @config[:host] if @config[:host]
|
770
|
-
ENV['PGPORT'] = @config[:port].to_s if @config[:port]
|
771
|
-
ENV['PGPASSWORD'] = @config[:password].to_s if @config[:password]
|
772
|
-
search_path = "--schema=#{@config[:schema_search_path]}" if @config[:schema_search_path]
|
773
|
-
|
774
|
-
@connection.connection.close
|
775
|
-
begin
|
776
|
-
definition = `pg_dump -i -U "#{@config[:username]}" -s -x -O #{search_path} #{database}`
|
777
|
-
raise "Error dumping database" if $?.exitstatus == 1
|
778
|
-
|
779
|
-
# need to patch away any references to SQL_ASCII as it breaks the JDBC driver
|
780
|
-
definition.gsub(/SQL_ASCII/, 'UNICODE')
|
781
|
-
ensure
|
782
|
-
reconnect!
|
783
|
-
end
|
784
|
-
end
|
785
|
-
|
786
448
|
# Returns the current client message level.
|
787
449
|
def client_min_messages
|
788
450
|
return nil if redshift? # not supported on Redshift
|
@@ -797,42 +459,6 @@ module ArJdbc
|
|
797
459
|
execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
|
798
460
|
end
|
799
461
|
|
800
|
-
# Gets the maximum number columns postgres has, default 32
|
801
|
-
def multi_column_index_limit
|
802
|
-
defined?(@multi_column_index_limit) && @multi_column_index_limit || 32
|
803
|
-
end
|
804
|
-
|
805
|
-
# Sets the maximum number columns postgres has, default 32
|
806
|
-
def multi_column_index_limit=(limit)
|
807
|
-
@multi_column_index_limit = limit
|
808
|
-
end
|
809
|
-
|
810
|
-
# @override
|
811
|
-
def distinct(columns, orders)
|
812
|
-
"DISTINCT #{columns_for_distinct(columns, orders)}"
|
813
|
-
end
|
814
|
-
|
815
|
-
# PostgreSQL requires the ORDER BY columns in the select list for distinct
|
816
|
-
# queries, and requires that the ORDER BY include the distinct column.
|
817
|
-
# @override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).
|
818
|
-
def columns_for_distinct(columns, orders)
|
819
|
-
if orders.is_a?(String)
|
820
|
-
orders = orders.split(','); orders.each(&:strip!)
|
821
|
-
end
|
822
|
-
|
823
|
-
order_columns = orders.reject(&:blank?).map! do |column|
|
824
|
-
column = column.is_a?(String) ? column.dup : column.to_sql # AREL node
|
825
|
-
column.gsub!(/\s+(?:ASC|DESC)\s*/i, '') # remove any ASC/DESC modifiers
|
826
|
-
column.gsub!(/\s*NULLS\s+(?:FIRST|LAST)?\s*/i, '')
|
827
|
-
column
|
828
|
-
end
|
829
|
-
order_columns.reject!(&:empty?)
|
830
|
-
i = -1; order_columns.map! { |column| "#{column} AS alias_#{i += 1}" }
|
831
|
-
|
832
|
-
columns = [ columns ]; columns.flatten!
|
833
|
-
columns.push( *order_columns ).join(', ')
|
834
|
-
end
|
835
|
-
|
836
462
|
# ORDER BY clause for the passed order option.
|
837
463
|
#
|
838
464
|
# PostgreSQL does not allow arbitrary ordering when using DISTINCT ON,
|
@@ -848,94 +474,6 @@ module ArJdbc
|
|
848
474
|
sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
|
849
475
|
end
|
850
476
|
|
851
|
-
# @return [String]
|
852
|
-
# @override
|
853
|
-
def quote(value, column = nil)
|
854
|
-
return super unless column && column.type
|
855
|
-
return value if sql_literal?(value)
|
856
|
-
|
857
|
-
case value
|
858
|
-
when Float
|
859
|
-
if value.infinite? && ( column.type == :datetime || column.type == :timestamp )
|
860
|
-
"'#{value.to_s.downcase}'"
|
861
|
-
elsif value.infinite? || value.nan?
|
862
|
-
"'#{value.to_s}'"
|
863
|
-
else super
|
864
|
-
end
|
865
|
-
when Numeric
|
866
|
-
if column.respond_to?(:sql_type) && column.sql_type == 'money'
|
867
|
-
"'#{value}'"
|
868
|
-
elsif column.type == :string || column.type == :text
|
869
|
-
"'#{value}'"
|
870
|
-
else super
|
871
|
-
end
|
872
|
-
when String
|
873
|
-
return "E'#{escape_bytea(value)}'::bytea" if column.type == :binary
|
874
|
-
return "xml '#{quote_string(value)}'" if column.type == :xml
|
875
|
-
sql_type = column.respond_to?(:sql_type) && column.sql_type
|
876
|
-
sql_type && sql_type[0, 3] == 'bit' ? quote_bit(value) : super
|
877
|
-
when Array
|
878
|
-
if AR40 && column.array? # will be always falsy in AR < 4.0
|
879
|
-
"'#{jdbc_column_class.array_to_string(value, column, self).gsub(/'/, "''")}'"
|
880
|
-
elsif column.type == :json # only in AR-4.0
|
881
|
-
super(jdbc_column_class.json_to_string(value), column)
|
882
|
-
elsif column.type == :jsonb # only in AR-4.0
|
883
|
-
super(jdbc_column_class.json_to_string(value), column)
|
884
|
-
elsif column.type == :point # only in AR-4.0
|
885
|
-
super(jdbc_column_class.point_to_string(value), column)
|
886
|
-
else super
|
887
|
-
end
|
888
|
-
when Hash
|
889
|
-
if column.type == :hstore # only in AR-4.0
|
890
|
-
super(jdbc_column_class.hstore_to_string(value), column)
|
891
|
-
elsif column.type == :json # only in AR-4.0
|
892
|
-
super(jdbc_column_class.json_to_string(value), column)
|
893
|
-
elsif column.type == :jsonb # only in AR-4.0
|
894
|
-
super(jdbc_column_class.json_to_string(value), column)
|
895
|
-
else super
|
896
|
-
end
|
897
|
-
when Range
|
898
|
-
sql_type = column.respond_to?(:sql_type) && column.sql_type
|
899
|
-
if sql_type && sql_type[-5, 5] == 'range' && AR40
|
900
|
-
escaped = quote_string(jdbc_column_class.range_to_string(value))
|
901
|
-
"'#{escaped}'::#{sql_type}"
|
902
|
-
else super
|
903
|
-
end
|
904
|
-
when IPAddr
|
905
|
-
if column.type == :inet || column.type == :cidr # only in AR-4.0
|
906
|
-
super(jdbc_column_class.cidr_to_string(value), column)
|
907
|
-
else super
|
908
|
-
end
|
909
|
-
else
|
910
|
-
super
|
911
|
-
end
|
912
|
-
end unless AR42
|
913
|
-
|
914
|
-
# @private
|
915
|
-
def _quote(value)
|
916
|
-
case value
|
917
|
-
when Type::Binary::Data
|
918
|
-
"E'#{escape_bytea(value.to_s)}'"
|
919
|
-
when OID::Xml::Data
|
920
|
-
"xml '#{quote_string(value.to_s)}'"
|
921
|
-
when OID::Bit::Data
|
922
|
-
if value.binary?
|
923
|
-
"B'#{value}'"
|
924
|
-
elsif value.hex?
|
925
|
-
"X'#{value}'"
|
926
|
-
end
|
927
|
-
when Float
|
928
|
-
if value.infinite? || value.nan?
|
929
|
-
"'#{value}'"
|
930
|
-
else
|
931
|
-
super
|
932
|
-
end
|
933
|
-
else
|
934
|
-
super
|
935
|
-
end
|
936
|
-
end if AR42
|
937
|
-
private :_quote if AR42
|
938
|
-
|
939
477
|
# Quotes a string, escaping any ' (single quote) and \ (backslash) chars.
|
940
478
|
# @return [String]
|
941
479
|
# @override
|
@@ -947,24 +485,6 @@ module ArJdbc
|
|
947
485
|
quoted
|
948
486
|
end
|
949
487
|
|
950
|
-
# @return [String]
|
951
|
-
def quote_bit(value)
|
952
|
-
case value
|
953
|
-
# NOTE: as reported with #60 this is not quite "right" :
|
954
|
-
# "0103" will be treated as hexadecimal string
|
955
|
-
# "0102" will be treated as hexadecimal string
|
956
|
-
# "0101" will be treated as binary string
|
957
|
-
# "0100" will be treated as binary string
|
958
|
-
# ... but is kept due Rails compatibility
|
959
|
-
when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation
|
960
|
-
when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation
|
961
|
-
end
|
962
|
-
end
|
963
|
-
|
964
|
-
def quote_bit(value)
|
965
|
-
"B'#{value}'"
|
966
|
-
end if AR40
|
967
|
-
|
968
488
|
def escape_bytea(string)
|
969
489
|
return unless string
|
970
490
|
if supports_hex_escaped_bytea?
|
@@ -988,25 +508,11 @@ module ArJdbc
|
|
988
508
|
end
|
989
509
|
end
|
990
510
|
|
991
|
-
# @override
|
992
|
-
def quote_table_name_for_assignment(table, attr)
|
993
|
-
quote_column_name(attr)
|
994
|
-
end if AR40
|
995
|
-
|
996
511
|
# @override
|
997
512
|
def quote_column_name(name)
|
998
513
|
%("#{name.to_s.gsub("\"", "\"\"")}")
|
999
514
|
end
|
1000
|
-
|
1001
|
-
# @private
|
1002
|
-
def quote_default_value(value, column)
|
1003
|
-
# Do not quote function default values for UUID columns
|
1004
|
-
if column.type == :uuid && value =~ /\(\)/
|
1005
|
-
value
|
1006
|
-
else
|
1007
|
-
quote(value, column)
|
1008
|
-
end
|
1009
|
-
end
|
515
|
+
alias_method :quote_schema_name, :quote_column_name
|
1010
516
|
|
1011
517
|
# Quote date/time values for use in SQL input.
|
1012
518
|
# Includes microseconds if the value is a Time responding to `usec`.
|
@@ -1020,62 +526,6 @@ module ArJdbc
|
|
1020
526
|
result
|
1021
527
|
end if ::ActiveRecord::VERSION::MAJOR >= 3
|
1022
528
|
|
1023
|
-
# @override
|
1024
|
-
def supports_disable_referential_integrity?
|
1025
|
-
true
|
1026
|
-
end
|
1027
|
-
|
1028
|
-
def disable_referential_integrity
|
1029
|
-
if supports_disable_referential_integrity?
|
1030
|
-
begin
|
1031
|
-
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
|
1032
|
-
rescue
|
1033
|
-
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER USER" }.join(";"))
|
1034
|
-
end
|
1035
|
-
end
|
1036
|
-
yield
|
1037
|
-
ensure
|
1038
|
-
if supports_disable_referential_integrity?
|
1039
|
-
begin
|
1040
|
-
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
|
1041
|
-
rescue
|
1042
|
-
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER USER" }.join(";"))
|
1043
|
-
end
|
1044
|
-
end
|
1045
|
-
end
|
1046
|
-
|
1047
|
-
def rename_table(table_name, new_name)
|
1048
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
1049
|
-
pk, seq = pk_and_sequence_for(new_name)
|
1050
|
-
if seq == "#{table_name}_#{pk}_seq"
|
1051
|
-
new_seq = "#{new_name}_#{pk}_seq"
|
1052
|
-
idx = "#{table_name}_pkey"
|
1053
|
-
new_idx = "#{new_name}_pkey"
|
1054
|
-
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
|
1055
|
-
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
1056
|
-
end
|
1057
|
-
rename_table_indexes(table_name, new_name) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements
|
1058
|
-
end
|
1059
|
-
|
1060
|
-
# Adds a new column to the named table.
|
1061
|
-
# See TableDefinition#column for details of the options you can use.
|
1062
|
-
def add_column(table_name, column_name, type, options = {})
|
1063
|
-
default = options[:default]
|
1064
|
-
notnull = options[:null] == false
|
1065
|
-
|
1066
|
-
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
|
1067
|
-
sql_type << "[]" if options[:array]
|
1068
|
-
|
1069
|
-
# Add the column.
|
1070
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{sql_type}")
|
1071
|
-
|
1072
|
-
change_column_default(table_name, column_name, default) if options_include_default?(options)
|
1073
|
-
change_column_null(table_name, column_name, false, default) if notnull
|
1074
|
-
end if ::ActiveRecord::VERSION::MAJOR < 4
|
1075
|
-
|
1076
|
-
# @private documented above
|
1077
|
-
def add_column(table_name, column_name, type, options = {}); super end if AR42
|
1078
|
-
|
1079
529
|
# Changes the column of a table.
|
1080
530
|
def change_column(table_name, column_name, type, options = {})
|
1081
531
|
quoted_table_name = quote_table_name(table_name)
|
@@ -1117,156 +567,13 @@ module ArJdbc
|
|
1117
567
|
end
|
1118
568
|
private :change_column_pg7
|
1119
569
|
|
1120
|
-
# Changes the default value of a table column.
|
1121
|
-
def change_column_default(table_name, column_name, default)
|
1122
|
-
if column = column_for(table_name, column_name) # (backwards) compatible with AR 3.x - 4.x
|
1123
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote_default_value(default, column)}"
|
1124
|
-
else
|
1125
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
|
1126
|
-
end
|
1127
|
-
end unless AR42 # unless const_defined? :SchemaCreation
|
1128
|
-
|
1129
|
-
# @private documented above
|
1130
|
-
def change_column_default(table_name, column_name, default)
|
1131
|
-
return unless column = column_for(table_name, column_name)
|
1132
|
-
|
1133
|
-
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
1134
|
-
if default.nil?
|
1135
|
-
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
1136
|
-
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
1137
|
-
execute alter_column_query % "DROP DEFAULT"
|
1138
|
-
else
|
1139
|
-
execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
|
1140
|
-
end
|
1141
|
-
end if AR42
|
1142
|
-
|
1143
|
-
# @private
|
1144
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
1145
|
-
unless null || default.nil?
|
1146
|
-
if column = column_for(table_name, column_name) # (backwards) compatible with AR 3.x - 4.x
|
1147
|
-
execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL"
|
1148
|
-
else
|
1149
|
-
execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL"
|
1150
|
-
end
|
1151
|
-
end
|
1152
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
1153
|
-
end unless AR42 # unless const_defined? :SchemaCreation
|
1154
|
-
|
1155
|
-
# @private
|
1156
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
1157
|
-
unless null || default.nil?
|
1158
|
-
column = column_for(table_name, column_name)
|
1159
|
-
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
|
1160
|
-
end
|
1161
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
1162
|
-
end if AR42
|
1163
|
-
|
1164
|
-
def rename_column(table_name, column_name, new_column_name)
|
1165
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
1166
|
-
rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements
|
1167
|
-
end # unless const_defined? :SchemaCreation
|
1168
|
-
|
1169
|
-
def add_index(table_name, column_name, options = {})
|
1170
|
-
index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
|
1171
|
-
execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
|
1172
|
-
end if AR40
|
1173
|
-
|
1174
570
|
def remove_index!(table_name, index_name)
|
1175
571
|
execute "DROP INDEX #{quote_table_name(index_name)}"
|
1176
572
|
end
|
1177
573
|
|
1178
|
-
def rename_index(table_name, old_name, new_name)
|
1179
|
-
validate_index_length!(table_name, new_name) if respond_to?(:validate_index_length!)
|
1180
|
-
|
1181
|
-
execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
|
1182
|
-
end
|
1183
|
-
|
1184
574
|
# @override
|
1185
575
|
def supports_foreign_keys?; true end
|
1186
576
|
|
1187
|
-
def foreign_keys(table_name)
|
1188
|
-
fk_info = select_all "" <<
|
1189
|
-
"SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete " <<
|
1190
|
-
"FROM pg_constraint c " <<
|
1191
|
-
"JOIN pg_class t1 ON c.conrelid = t1.oid " <<
|
1192
|
-
"JOIN pg_class t2 ON c.confrelid = t2.oid " <<
|
1193
|
-
"JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid " <<
|
1194
|
-
"JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid " <<
|
1195
|
-
"JOIN pg_namespace t3 ON c.connamespace = t3.oid " <<
|
1196
|
-
"WHERE c.contype = 'f' " <<
|
1197
|
-
" AND t1.relname = #{quote(table_name)} " <<
|
1198
|
-
" AND t3.nspname = ANY (current_schemas(false)) " <<
|
1199
|
-
"ORDER BY c.conname "
|
1200
|
-
|
1201
|
-
fk_info.map! do |row|
|
1202
|
-
options = {
|
1203
|
-
:column => row['column'], :name => row['name'], :primary_key => row['primary_key']
|
1204
|
-
}
|
1205
|
-
options[:on_delete] = extract_foreign_key_action(row['on_delete'])
|
1206
|
-
options[:on_update] = extract_foreign_key_action(row['on_update'])
|
1207
|
-
|
1208
|
-
ForeignKeyDefinition.new(table_name, row['to_table'], options)
|
1209
|
-
end
|
1210
|
-
end if defined? ForeignKeyDefinition
|
1211
|
-
|
1212
|
-
# @private
|
1213
|
-
def extract_foreign_key_action(specifier)
|
1214
|
-
case specifier
|
1215
|
-
when 'c'; :cascade
|
1216
|
-
when 'n'; :nullify
|
1217
|
-
when 'r'; :restrict
|
1218
|
-
end
|
1219
|
-
end
|
1220
|
-
private :extract_foreign_key_action
|
1221
|
-
|
1222
|
-
def index_name_length
|
1223
|
-
63
|
1224
|
-
end
|
1225
|
-
|
1226
|
-
# Returns the list of all column definitions for a table.
|
1227
|
-
def columns(table_name, name = nil)
|
1228
|
-
column = jdbc_column_class
|
1229
|
-
column_definitions(table_name).map! do |row|
|
1230
|
-
# |name, type, default, notnull, oid, fmod|
|
1231
|
-
name = row[0]; type = row[1]; default = row[2]
|
1232
|
-
notnull = row[3]; oid = row[4]; fmod = row[5]
|
1233
|
-
# oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) { OID::Identity.new }
|
1234
|
-
notnull = notnull == 't' if notnull.is_a?(String) # JDBC gets true/false
|
1235
|
-
# for ID columns we get a bit of non-sense default :
|
1236
|
-
# e.g. "nextval('mixed_cases_id_seq'::regclass"
|
1237
|
-
if default =~ /^nextval\(.*?\:\:regclass\)$/
|
1238
|
-
default = nil
|
1239
|
-
elsif default =~ /^\(([-+]?[\d\.]+)\)$/ # e.g. "(-1)" for a negative default
|
1240
|
-
default = $1
|
1241
|
-
end
|
1242
|
-
|
1243
|
-
column.new(name, default, oid, type, ! notnull, fmod, self)
|
1244
|
-
end
|
1245
|
-
end
|
1246
|
-
|
1247
|
-
# @private documented above
|
1248
|
-
def columns(table_name)
|
1249
|
-
column = jdbc_column_class
|
1250
|
-
# Limit, precision, and scale are all handled by the superclass.
|
1251
|
-
column_definitions(table_name).map! do |row|
|
1252
|
-
# |name, type, default, notnull, oid, fmod|
|
1253
|
-
name = row[0]; type = row[1]; default = row[2]
|
1254
|
-
notnull = row[3]; oid = row[4]; fmod = row[5]
|
1255
|
-
notnull = notnull == 't' if notnull.is_a?(String) # JDBC gets true/false
|
1256
|
-
|
1257
|
-
oid_type = get_oid_type(oid.to_i, fmod.to_i, name, type)
|
1258
|
-
default_value = extract_value_from_default(oid, default)
|
1259
|
-
default_function = extract_default_function(default_value, default)
|
1260
|
-
|
1261
|
-
column.new(name, default_value, oid_type, type, ! notnull, default_function, oid, self)
|
1262
|
-
end
|
1263
|
-
end if AR42
|
1264
|
-
|
1265
|
-
# @private only for API compatibility
|
1266
|
-
def new_column(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
|
1267
|
-
jdbc_column_class.new(name, default, cast_type, sql_type, null, default_function)
|
1268
|
-
end if AR42
|
1269
|
-
|
1270
577
|
# @private
|
1271
578
|
def column_for(table_name, column_name)
|
1272
579
|
column_name = column_name.to_s
|
@@ -1285,139 +592,84 @@ module ArJdbc
|
|
1285
592
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
1286
593
|
# - ::regclass is a function that gives the id for a table name
|
1287
594
|
def column_definitions(table_name)
|
1288
|
-
select_rows(<<-end_sql, 'SCHEMA')
|
595
|
+
rows = select_rows(<<-end_sql, 'SCHEMA')
|
1289
596
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
1290
|
-
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
|
1291
|
-
|
1292
|
-
|
597
|
+
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
598
|
+
(SELECT c.collname FROM pg_collation c, pg_type t
|
599
|
+
WHERE c.oid = a.attcollation AND t.oid = a.atttypid
|
600
|
+
AND a.attcollation <> t.typcollation),
|
601
|
+
col_description(a.attrelid, a.attnum) AS comment
|
602
|
+
FROM pg_attribute a
|
603
|
+
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
1293
604
|
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
|
1294
605
|
AND a.attnum > 0 AND NOT a.attisdropped
|
1295
606
|
ORDER BY a.attnum
|
1296
607
|
end_sql
|
1297
|
-
end
|
1298
|
-
private :column_definitions
|
1299
608
|
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
# @override
|
1305
|
-
def tables(name = nil)
|
1306
|
-
select_values(TABLES_SQL, 'SCHEMA')
|
1307
|
-
end
|
1308
|
-
|
1309
|
-
# @private
|
1310
|
-
TABLE_EXISTS_SQL_PREFIX = 'SELECT COUNT(*) as table_count FROM pg_class c'
|
1311
|
-
TABLE_EXISTS_SQL_PREFIX << ' LEFT JOIN pg_namespace n ON n.oid = c.relnamespace'
|
1312
|
-
if AR42 # -- (r)elation/table, (v)iew, (m)aterialized view
|
1313
|
-
TABLE_EXISTS_SQL_PREFIX << " WHERE c.relkind IN ('r','v','m')"
|
1314
|
-
else
|
1315
|
-
TABLE_EXISTS_SQL_PREFIX << " WHERE c.relkind IN ('r','v')"
|
1316
|
-
end
|
1317
|
-
TABLE_EXISTS_SQL_PREFIX << " AND c.relname = ?"
|
1318
|
-
private_constant :TABLE_EXISTS_SQL_PREFIX rescue nil
|
1319
|
-
|
1320
|
-
# Returns true if table exists.
|
1321
|
-
# If the schema is not specified as part of +name+ then it will only find tables within
|
1322
|
-
# the current schema search path (regardless of permissions to access tables in other schemas)
|
1323
|
-
def table_exists?(name)
|
1324
|
-
schema, table = extract_schema_and_table(name.to_s)
|
1325
|
-
return false unless table
|
1326
|
-
|
1327
|
-
binds = [[nil, table]]
|
1328
|
-
binds << [nil, schema] if schema
|
1329
|
-
|
1330
|
-
sql = "#{TABLE_EXISTS_SQL_PREFIX} AND n.nspname = #{schema ? "?" : 'ANY (current_schemas(false))'}"
|
1331
|
-
|
1332
|
-
log(sql, 'SCHEMA', binds) do
|
1333
|
-
@connection.execute_query_raw(sql, binds).first['table_count'] > 0
|
609
|
+
# Force the notnull attribute to a boolean
|
610
|
+
rows.each do |row|
|
611
|
+
row[3] = row[3] == 't' if row[3].is_a?(String)
|
1334
612
|
end
|
1335
613
|
end
|
1336
|
-
|
1337
|
-
|
1338
|
-
# @private
|
1339
|
-
DATA_SOURCES_SQL = 'SELECT c.relname FROM pg_class c'
|
1340
|
-
DATA_SOURCES_SQL << ' LEFT JOIN pg_namespace n ON n.oid = c.relnamespace'
|
1341
|
-
DATA_SOURCES_SQL << " WHERE c.relkind IN ('r', 'v','m')" # -- (r)elation/table, (v)iew, (m)aterialized view
|
1342
|
-
DATA_SOURCES_SQL << ' AND n.nspname = ANY (current_schemas(false))'
|
1343
|
-
private_constant :DATA_SOURCES_SQL rescue nil
|
1344
|
-
|
1345
|
-
# @override
|
1346
|
-
def data_sources
|
1347
|
-
select_values(DATA_SOURCES_SQL, 'SCHEMA')
|
1348
|
-
end
|
1349
|
-
|
1350
|
-
def drop_table(table_name, options = {})
|
1351
|
-
execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
1352
|
-
end
|
614
|
+
private :column_definitions
|
1353
615
|
|
1354
616
|
def truncate(table_name, name = nil)
|
1355
617
|
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
1356
618
|
end
|
1357
619
|
|
1358
|
-
def index_name_exists?(table_name, index_name, default)
|
1359
|
-
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
1360
|
-
SELECT COUNT(*)
|
1361
|
-
FROM pg_class t
|
1362
|
-
INNER JOIN pg_index d ON t.oid = d.indrelid
|
1363
|
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
1364
|
-
WHERE i.relkind = 'i'
|
1365
|
-
AND i.relname = '#{index_name}'
|
1366
|
-
AND t.relname = '#{table_name}'
|
1367
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
|
1368
|
-
SQL
|
1369
|
-
end if AR42
|
1370
|
-
|
1371
620
|
# Returns an array of indexes for the given table.
|
1372
621
|
def indexes(table_name, name = nil)
|
1373
|
-
#
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
622
|
+
# FIXME: AR version => table = Utils.extract_schema_qualified_name(table_name.to_s)
|
623
|
+
schema, table = extract_schema_and_table(table_name.to_s)
|
624
|
+
|
625
|
+
result = query(<<-SQL, 'SCHEMA')
|
626
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
|
627
|
+
pg_catalog.obj_description(i.oid, 'pg_class') AS comment,
|
628
|
+
(SELECT COUNT(*) FROM pg_opclass o
|
629
|
+
JOIN (SELECT unnest(string_to_array(d.indclass::text, ' '))::int oid) c
|
630
|
+
ON o.oid = c.oid WHERE o.opcdefault = 'f')
|
631
|
+
FROM pg_class t
|
632
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
633
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
634
|
+
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
635
|
+
WHERE i.relkind = 'i'
|
636
|
+
AND d.indisprimary = 'f'
|
637
|
+
AND t.relname = '#{table}'
|
638
|
+
AND n.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
|
639
|
+
ORDER BY i.relname
|
1384
640
|
SQL
|
1385
641
|
|
1386
|
-
result.map
|
642
|
+
result.map do |row|
|
1387
643
|
index_name = row[0]
|
644
|
+
# FIXME: These values [1,2] are returned in a different format than AR expects, maybe we could update it on the Java side to be more accurate
|
1388
645
|
unique = row[1].is_a?(String) ? row[1] == 't' : row[1] # JDBC gets us a boolean
|
1389
646
|
indkey = row[2].is_a?(Java::OrgPostgresqlUtil::PGobject) ? row[2].value : row[2]
|
1390
|
-
indkey = indkey.split(" ")
|
647
|
+
indkey = indkey.split(" ").map(&:to_i)
|
1391
648
|
inddef = row[3]
|
1392
649
|
oid = row[4]
|
650
|
+
comment = row[5]
|
651
|
+
opclass = row[6]
|
1393
652
|
|
1394
|
-
|
1395
|
-
SELECT a.attnum, a.attname
|
1396
|
-
FROM pg_attribute a
|
1397
|
-
WHERE a.attrelid = #{oid}
|
1398
|
-
AND a.attnum IN (#{indkey.join(",")})
|
1399
|
-
SQL
|
653
|
+
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
|
1400
654
|
|
1401
|
-
|
1402
|
-
|
655
|
+
if indkey.include?(0) || opclass > 0
|
656
|
+
columns = expressions
|
657
|
+
else
|
658
|
+
columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
|
659
|
+
SELECT a.attnum, a.attname
|
660
|
+
FROM pg_attribute a
|
661
|
+
WHERE a.attrelid = #{oid}
|
662
|
+
AND a.attnum IN (#{indkey.join(",")})
|
663
|
+
SQL
|
1403
664
|
|
1404
|
-
unless column_names.empty?
|
1405
665
|
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
if ::ActiveRecord::VERSION::MAJOR > 3 # AR4 supports `where` and `using` index options
|
1410
|
-
where = inddef.scan(/WHERE (.+)$/).flatten[0]
|
1411
|
-
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
1412
|
-
|
1413
|
-
IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
|
1414
|
-
else
|
1415
|
-
new_index_definition(table_name, index_name, unique, column_names, [], orders)
|
1416
|
-
end
|
666
|
+
orders = Hash[
|
667
|
+
expressions.scan(/(\w+) DESC/).flatten.map { |order_column| [order_column, :desc] }
|
668
|
+
]
|
1417
669
|
end
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
670
|
+
|
671
|
+
IndexDefinition.new(table_name, index_name, unique, columns, [], orders, where, nil, using.to_sym, comment.presence)
|
672
|
+
end.compact
|
1421
673
|
end
|
1422
674
|
|
1423
675
|
# @private
|
@@ -1428,16 +680,39 @@ module ArJdbc
|
|
1428
680
|
when 'average' then 'avg'
|
1429
681
|
else operation.downcase
|
1430
682
|
end
|
1431
|
-
end
|
683
|
+
end
|
1432
684
|
|
1433
685
|
private
|
1434
686
|
|
687
|
+
# Pulled from ActiveRecord's Postgres adapter and modified to use execute
|
688
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
689
|
+
@case_insensitive_cache ||= {}
|
690
|
+
@case_insensitive_cache[column.sql_type] ||= begin
|
691
|
+
sql = <<-end_sql
|
692
|
+
SELECT exists(
|
693
|
+
SELECT * FROM pg_proc
|
694
|
+
WHERE proname = 'lower'
|
695
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
696
|
+
) OR exists(
|
697
|
+
SELECT * FROM pg_proc
|
698
|
+
INNER JOIN pg_cast
|
699
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
700
|
+
WHERE proname = 'lower'
|
701
|
+
AND castsource = #{quote column.sql_type}::regtype
|
702
|
+
)
|
703
|
+
end_sql
|
704
|
+
select_rows(sql, 'SCHEMA').first.first == 't'
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
1435
708
|
def translate_exception(exception, message)
|
1436
709
|
case exception.message
|
1437
710
|
when /duplicate key value violates unique constraint/
|
1438
|
-
::ActiveRecord::RecordNotUnique.new(message
|
711
|
+
::ActiveRecord::RecordNotUnique.new(message)
|
1439
712
|
when /violates foreign key constraint/
|
1440
|
-
::ActiveRecord::InvalidForeignKey.new(message
|
713
|
+
::ActiveRecord::InvalidForeignKey.new(message)
|
714
|
+
when /value too long/
|
715
|
+
::ActiveRecord::ValueTooLong.new(message)
|
1441
716
|
else
|
1442
717
|
super
|
1443
718
|
end
|
@@ -1477,30 +752,39 @@ require 'arjdbc/util/quoted_cache'
|
|
1477
752
|
|
1478
753
|
module ActiveRecord::ConnectionAdapters
|
1479
754
|
|
1480
|
-
remove_const(:PostgreSQLColumn) if const_defined?(:PostgreSQLColumn)
|
1481
|
-
|
1482
|
-
class PostgreSQLColumn < JdbcColumn
|
1483
|
-
include ::ArJdbc::PostgreSQL::Column
|
1484
|
-
end
|
1485
|
-
|
1486
755
|
# NOTE: seems needed on 4.x due loading of '.../postgresql/oid' which
|
1487
756
|
# assumes: class PostgreSQLAdapter < AbstractAdapter
|
1488
757
|
remove_const(:PostgreSQLAdapter) if const_defined?(:PostgreSQLAdapter)
|
1489
758
|
|
1490
|
-
class PostgreSQLAdapter <
|
1491
|
-
|
1492
|
-
|
759
|
+
class PostgreSQLAdapter < AbstractAdapter
|
760
|
+
|
761
|
+
# Try to use as much of the built in postgres logic as possible
|
762
|
+
# maybe someday we can extend the actual adapter
|
763
|
+
include ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnDumper
|
764
|
+
include ActiveRecord::ConnectionAdapters::PostgreSQL::ReferentialIntegrity
|
765
|
+
include ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements
|
766
|
+
include ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting
|
767
|
+
|
768
|
+
include ArJdbc::Abstract::Core
|
769
|
+
include ArJdbc::Abstract::ConnectionManagement
|
770
|
+
include ArJdbc::Abstract::DatabaseStatements
|
771
|
+
include ArJdbc::Abstract::StatementCache
|
772
|
+
include ArJdbc::Abstract::TransactionSupport
|
773
|
+
include ArJdbc::PostgreSQL
|
1493
774
|
|
1494
|
-
require 'arjdbc/postgresql/oid_types'
|
1495
|
-
include ::ArJdbc::PostgreSQL::OIDTypes
|
775
|
+
require 'arjdbc/postgresql/oid_types'
|
776
|
+
include ::ArJdbc::PostgreSQL::OIDTypes
|
1496
777
|
|
1497
|
-
load 'arjdbc/postgresql/_bc_time_cast_patch.rb'
|
778
|
+
load 'arjdbc/postgresql/_bc_time_cast_patch.rb'
|
1498
779
|
|
1499
|
-
include ::ArJdbc::PostgreSQL::ColumnHelpers
|
780
|
+
include ::ArJdbc::PostgreSQL::ColumnHelpers
|
1500
781
|
|
1501
782
|
include ::ArJdbc::Util::QuotedCache
|
1502
783
|
|
1503
|
-
|
784
|
+
# AR expects OID to be available on the adapter
|
785
|
+
OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
|
786
|
+
|
787
|
+
def initialize(connection, logger = nil, config = {})
|
1504
788
|
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
1505
789
|
@local_tz = nil
|
1506
790
|
|
@@ -1508,7 +792,7 @@ module ActiveRecord::ConnectionAdapters
|
|
1508
792
|
|
1509
793
|
@table_alias_length = nil
|
1510
794
|
|
1511
|
-
initialize_type_map(@type_map = Type::HashLookupTypeMap.new)
|
795
|
+
initialize_type_map(@type_map = Type::HashLookupTypeMap.new)
|
1512
796
|
|
1513
797
|
@use_insert_returning = @config.key?(:insert_returning) ?
|
1514
798
|
self.class.type_cast_config_to_boolean(@config[:insert_returning]) : nil
|
@@ -1518,34 +802,50 @@ module ActiveRecord::ConnectionAdapters
|
|
1518
802
|
Arel::Visitors::PostgreSQL.new(self)
|
1519
803
|
end
|
1520
804
|
|
1521
|
-
|
1522
|
-
require 'active_record/connection_adapters/postgresql/schema_definitions'
|
1523
|
-
else
|
1524
|
-
require 'arjdbc/postgresql/base/schema_definitions'
|
1525
|
-
end
|
805
|
+
require 'active_record/connection_adapters/postgresql/schema_definitions'
|
1526
806
|
|
1527
807
|
ColumnDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnDefinition
|
1528
|
-
|
1529
808
|
ColumnMethods = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnMethods
|
1530
809
|
TableDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition
|
810
|
+
Table = ActiveRecord::ConnectionAdapters::PostgreSQL::Table
|
1531
811
|
|
1532
|
-
def
|
1533
|
-
|
812
|
+
def create_table_definition(*args) # :nodoc:
|
813
|
+
TableDefinition.new(*args)
|
1534
814
|
end
|
1535
815
|
|
1536
|
-
|
816
|
+
def exec_query(sql, name = nil, binds = [], prepare: false)
|
817
|
+
super
|
818
|
+
rescue ActiveRecord::StatementInvalid => e
|
819
|
+
raise unless e.cause.message.include?('cached plan must not change result type'.freeze)
|
820
|
+
|
821
|
+
if open_transactions > 0
|
822
|
+
# In a transaction, have to fail it - See AR code for details
|
823
|
+
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
|
824
|
+
else
|
825
|
+
# Not in a transaction, clear the prepared statement and try again
|
826
|
+
delete_cached_statement(sql)
|
827
|
+
retry
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
def schema_creation # :nodoc:
|
832
|
+
PostgreSQL::SchemaCreation.new self
|
833
|
+
end
|
1537
834
|
|
1538
835
|
def update_table_definition(table_name, base)
|
1539
836
|
Table.new(table_name, base)
|
1540
|
-
end
|
837
|
+
end
|
1541
838
|
|
1542
839
|
def jdbc_connection_class(spec)
|
1543
840
|
::ArJdbc::PostgreSQL.jdbc_connection_class
|
1544
841
|
end
|
1545
842
|
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
843
|
+
private
|
844
|
+
|
845
|
+
# Prepared statements aren't schema aware so we need to make sure we
|
846
|
+
# store different PreparedStatement objects for different schemas
|
847
|
+
def cached_statement_key(sql)
|
848
|
+
"#{schema_search_path}-#{sql}"
|
1549
849
|
end
|
1550
850
|
|
1551
851
|
end
|