Module: ArJdbc::PostgreSQL

Included in:
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
Defined in:
lib/arjdbc/postgresql/adapter.rb,
lib/arjdbc/postgresql/column.rb,
lib/arjdbc/postgresql/oid_types.rb

Overview

Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.

Defined Under Namespace

Classes: ArjdbcTypeMapInitializer

Constant Summary collapse

ADAPTER_NAME =
'PostgreSQL'
NATIVE_DATABASE_TYPES =
{
  primary_key:  'bigserial primary key',
  bigint:       { name: 'bigint' },
  binary:       { name: 'bytea' },
  bit:          { name: 'bit' },
  bit_varying:  { name: 'bit varying' },
  boolean:      { name: 'boolean' },
  box:          { name: 'box' },
  char:         { name: 'char' },
  cidr:         { name: 'cidr' },
  circle:       { name: 'circle' },
  citext:       { name: 'citext' },
  date:         { name: 'date' },
  daterange:    { name: 'daterange' },
  datetime:     { name: 'timestamp' },
  decimal:      { name: 'decimal' }, # :limit => 1000
  float:        { name: 'float' },
  hstore:       { name: 'hstore' },
  inet:         { name: 'inet' },
  int4range:    { name: 'int4range' },
  int8range:    { name: 'int8range' },
  integer:      { name: 'integer' },
  interval:     { name: 'interval' },
  json:         { name: 'json' },
  jsonb:        { name: 'jsonb' },
  line:         { name: 'line' },
  lseg:         { name: 'lseg' },
  ltree:        { name: 'ltree' },
  macaddr:      { name: 'macaddr' },
  money:        { name: 'money' },
  numeric:      { name: 'numeric' },
  numrange:     { name: 'numrange' },
  oid:          { name: 'oid' },
  path:         { name: 'path' },
  point:        { name: 'point' },
  polygon:      { name: 'polygon' },
  string:       { name: 'character varying' },
  text:         { name: 'text' },
  time:         { name: 'time' },
  timestamp:    { name: 'timestamp' },
  tsrange:      { name: 'tsrange' },
  tstzrange:    { name: 'tstzrange' },
  tsvector:     { name: 'tsvector' },
  uuid:         { name: 'uuid' },
  xml:          { name: 'xml' }
}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.jdbc_connection_classObject



38
39
40
# File 'lib/arjdbc/postgresql/adapter.rb', line 38

def self.jdbc_connection_class
  ::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
end

Instance Method Details

#adapter_nameObject



47
48
49
# File 'lib/arjdbc/postgresql/adapter.rb', line 47

def adapter_name
  ADAPTER_NAME
end

#add_order_by_for_association_limiting!(sql, options) ⇒ Object

ORDER BY clause for the passed order option.

PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this by wrapping the SQL as a sub-select and ordering in that query.



484
485
486
487
488
489
490
491
492
# File 'lib/arjdbc/postgresql/adapter.rb', line 484

def add_order_by_for_association_limiting!(sql, options)
  return sql if options[:order].blank?

  order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
  order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
  order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')

  sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
end

#all_schemasObject



462
463
464
# File 'lib/arjdbc/postgresql/adapter.rb', line 462

def all_schemas
  select('SELECT nspname FROM pg_namespace').map { |row| row["nspname"] }
end

#build_insert_sql(insert) ⇒ Object

:nodoc:



375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/arjdbc/postgresql/adapter.rb', line 375

def build_insert_sql(insert) # :nodoc:
  sql = +"INSERT #{insert.into} #{insert.values_list}"

  if insert.skip_duplicates?
    sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
  elsif insert.update_duplicates?
    sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
    sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
    sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
  end

  sql << " RETURNING #{insert.returning}" if insert.returning
  sql
end

#build_truncate_statements(table_names) ⇒ Object



458
459
460
# File 'lib/arjdbc/postgresql/adapter.rb', line 458

def build_truncate_statements(table_names)
  ["TRUNCATE TABLE #{table_names.map(&method(:quote_table_name)).join(", ")}"]
end

#check_versionObject

:nodoc:



390
391
392
393
394
# File 'lib/arjdbc/postgresql/adapter.rb', line 390

def check_version # :nodoc:
  if database_version < 90300
    raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
  end
end

#clear_cache!Object

We need to make sure to deallocate all the prepared statements since apparently calling close on the statement object doesn't always free the server resources and calling 'DISCARD ALL' fails if we are inside a transaction



434
435
436
437
438
# File 'lib/arjdbc/postgresql/adapter.rb', line 434

def clear_cache!
  super
  # Make sure all query plans are *really* gone
  @connection.execute 'DEALLOCATE ALL' if active?
end

#client_min_messagesObject

Returns the current client message level.



467
468
469
470
471
# File 'lib/arjdbc/postgresql/adapter.rb', line 467

def client_min_messages
  return nil if redshift? # not supported on Redshift
  # Need to use #execute so we don't try to access the type map before it is initialized
  execute('SHOW client_min_messages', 'SCHEMA').values.first.first
end

#client_min_messages=(level) ⇒ Object

Set the client message level.



474
475
476
477
# File 'lib/arjdbc/postgresql/adapter.rb', line 474

def client_min_messages=(level)
  # Not supported on Redshift
  redshift? ? nil : super
end

#configure_connectionObject

Configures the encoding, verbosity, schema search path, and time zone of the connection. This is called on connection.connect and should not be called manually.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/arjdbc/postgresql/adapter.rb', line 68

def configure_connection
  #if encoding = config[:encoding]
    # The client_encoding setting is set by the driver and should not be altered.
    # If the driver detects a change it will abort the connection.
    # see http://jdbc.postgresql.org/documentation/91/connect.html
    # self.set_client_encoding(encoding)
  #end
  self.client_min_messages = config[:min_messages] || 'warning'
  self.schema_search_path = config[:schema_search_path] || config[:schema_order]

  # Use standard-conforming strings if available so we don't have to do the E'...' dance.
  set_standard_conforming_strings

  # If using Active Record's time zone support configure the connection to return
  # TIMESTAMP WITH ZONE types in UTC.
  # (SET TIME ZONE does not use an equals sign like other SET variables)
  if ActiveRecord::Base.default_timezone == :utc
    execute("SET time zone 'UTC'", 'SCHEMA')
  elsif tz = local_tz
    execute("SET time zone '#{tz}'", 'SCHEMA')
  end unless redshift?

  # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
  execute("SET intervalstyle = iso_8601", "SCHEMA")

  # SET statements from :variables config hash
  # http://www.postgresql.org/docs/8.3/static/sql-set.html
  (config[:variables] || {}).map do |k, v|
    if v == ':default' || v == :default
      # Sets the value to the global or compile default
      execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
    elsif ! v.nil?
      execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
    end
  end
end

#default_index_type?(index) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


371
372
373
# File 'lib/arjdbc/postgresql/adapter.rb', line 371

def default_index_type?(index) # :nodoc:
  index.using == :btree || super
end

#default_sequence_name(table_name, pk = "id") ⇒ Object

:nodoc:



448
449
450
451
452
# File 'lib/arjdbc/postgresql/adapter.rb', line 448

def default_sequence_name(table_name, pk = "id") #:nodoc:
  serial_sequence(table_name, pk)
rescue ActiveRecord::StatementInvalid
  %Q("#{table_name}_#{pk}_seq")
end

#disable_extension(name) ⇒ Object



305
306
307
308
309
# File 'lib/arjdbc/postgresql/adapter.rb', line 305

def disable_extension(name)
  exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
    reload_type_map
  }
end

#enable_extension(name) ⇒ Object



299
300
301
302
303
# File 'lib/arjdbc/postgresql/adapter.rb', line 299

def enable_extension(name)
  exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
    reload_type_map
  }
end

#escape_bytea(string) ⇒ Object

Note:

quote_string implemented as native



496
497
498
499
# File 'lib/arjdbc/postgresql/adapter.rb', line 496

def escape_bytea(string)
  return unless string
  "\\x#{string.unpack("H*")[0]}"
end

#exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) ⇒ Object



397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/arjdbc/postgresql/adapter.rb', line 397

def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
  val = super
  if !use_insert_returning? && pk
    unless sequence_name
      table_ref = extract_table_ref_from_insert_sql(sql)
      sequence_name = default_sequence_name(table_ref, pk)
      return val unless sequence_name
    end
    last_insert_id_result(sequence_name)
  else
    val
  end
end

#execute_batch(statements, name = nil) ⇒ Object



411
412
413
# File 'lib/arjdbc/postgresql/adapter.rb', line 411

def execute_batch(statements, name = nil)
  execute(combine_multi_statements(statements), name)
end

#explain(arel, binds = []) ⇒ Object



415
416
417
418
# File 'lib/arjdbc/postgresql/adapter.rb', line 415

def explain(arel, binds = [])
  sql, binds = to_sql_and_binds(arel, binds)
  ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query("EXPLAIN #{sql}", 'EXPLAIN', binds))
end

#extension_available?(name) ⇒ Boolean

Returns:

  • (Boolean)


311
312
313
# File 'lib/arjdbc/postgresql/adapter.rb', line 311

def extension_available?(name)
  query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
end

#extension_enabled?(name) ⇒ Boolean

Returns:

  • (Boolean)


315
316
317
# File 'lib/arjdbc/postgresql/adapter.rb', line 315

def extension_enabled?(name)
  query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
end

#extensionsObject



319
320
321
# File 'lib/arjdbc/postgresql/adapter.rb', line 319

def extensions
  exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
end

#get_advisory_lock(lock_id) ⇒ Object

:nodoc:



285
286
287
288
289
290
# File 'lib/arjdbc/postgresql/adapter.rb', line 285

def get_advisory_lock(lock_id) # :nodoc:
  unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
    raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
  end
  query_value("SELECT pg_try_advisory_lock(#{lock_id})")
end

#get_database_versionObject

:nodoc:



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/arjdbc/postgresql/adapter.rb', line 338

def get_database_version # :nodoc:
  begin
    version = @connection.database_product
    if match = version.match(/([\d\.]*\d).*?/)
      version = match[1].split('.').map(&:to_i)
      # PostgreSQL version representation does not have more than 4 digits
      # From version 10 onwards, PG has changed its versioning policy to
      # limit it to only 2 digits. i.e. in 10.x, 10 being the major
      # version and x representing the patch release
      # Refer to:
      #   https://www.postgresql.org/support/versioning/
      #   https://www.postgresql.org/docs/10/static/libpq-status.html -> PQserverVersion()
      # for more info

      if version.size >= 3
        (version[0] * 100 + version[1]) * 100 + version[2]
      elsif version.size == 2
        if version[0] >= 10
          version[0] * 100 * 100 + version[1]
        else
          (version[0] * 100 + version[1]) * 100
        end
      elsif version.size == 1
        version[0] * 100 * 100
      else
        0
      end
    else
      0
    end
  end
end

#index_algorithmsObject



234
235
236
# File 'lib/arjdbc/postgresql/adapter.rb', line 234

def index_algorithms
  { concurrently: 'CONCURRENTLY' }
end

#jdbc_column_classObject



43
# File 'lib/arjdbc/postgresql/adapter.rb', line 43

def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn end

#last_insert_id_result(sequence_name) ⇒ Object



454
455
456
# File 'lib/arjdbc/postgresql/adapter.rb', line 454

def last_insert_id_result(sequence_name)
  exec_query("SELECT currval('#{sequence_name}')", 'SQL')
end

#max_identifier_lengthObject

Returns the configured supported identifier length supported by PostgreSQL



324
325
326
# File 'lib/arjdbc/postgresql/adapter.rb', line 324

def max_identifier_length
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
end

#native_database_typesObject



155
156
157
# File 'lib/arjdbc/postgresql/adapter.rb', line 155

def native_database_types
  NATIVE_DATABASE_TYPES
end

#quote_table_name(name) ⇒ Object



502
503
504
505
506
507
508
509
510
511
# File 'lib/arjdbc/postgresql/adapter.rb', line 502

def quote_table_name(name)
  schema, name_part = extract_pg_identifier_from_name(name.to_s)

  unless name_part
    quote_column_name(schema)
  else
    table_name, name_part = extract_pg_identifier_from_name(name_part)
    "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
  end
end

#release_advisory_lock(lock_id) ⇒ Object

:nodoc:



292
293
294
295
296
297
# File 'lib/arjdbc/postgresql/adapter.rb', line 292

def release_advisory_lock(lock_id) # :nodoc:
  unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
    raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
  end
  query_value("SELECT pg_advisory_unlock(#{lock_id})")
end

#remove_column(table_name, column_name, type = nil, **options) ⇒ Object

Need to clear the cache even though the AR adapter doesn't for some reason



517
518
519
520
# File 'lib/arjdbc/postgresql/adapter.rb', line 517

def remove_column(table_name, column_name, type = nil, **options)
  super
  clear_cache!
end

#reset!Object



440
441
442
443
444
445
446
# File 'lib/arjdbc/postgresql/adapter.rb', line 440

def reset!
  clear_cache!
  reset_transaction
  @connection.rollback # Have to deal with rollbacks differently than the AR adapter
  @connection.execute 'DISCARD ALL'
  @connection.configure_connection
end

#session_auth=(user) ⇒ Object

Set the authorized user for this session



329
330
331
332
# File 'lib/arjdbc/postgresql/adapter.rb', line 329

def session_auth=(user)
  clear_cache!
  execute("SET SESSION AUTHORIZATION #{user}")
end

#set_client_encoding(encoding) ⇒ Object



61
62
63
64
# File 'lib/arjdbc/postgresql/adapter.rb', line 61

def set_client_encoding(encoding)
  ActiveRecord::Base.logger.warn "client_encoding is set by the driver and should not be altered, ('#{encoding}' ignored)"
  ActiveRecord::Base.logger.debug "Set the 'allowEncodingChanges' driver property (e.g. using config[:properties]) if you need to override the client encoding when doing a copy."
end

#set_standard_conforming_stringsObject



163
164
165
# File 'lib/arjdbc/postgresql/adapter.rb', line 163

def set_standard_conforming_strings
  execute("SET standard_conforming_strings = on", "SCHEMA")
end

#supports_advisory_locks?Boolean

Returns:

  • (Boolean)


242
243
244
# File 'lib/arjdbc/postgresql/adapter.rb', line 242

def supports_advisory_locks?
  true
end

#supports_bulk_alter?Boolean

Returns:

  • (Boolean)


167
168
169
# File 'lib/arjdbc/postgresql/adapter.rb', line 167

def supports_bulk_alter?
  true
end

#supports_check_constraints?Boolean

Returns:

  • (Boolean)


195
196
197
# File 'lib/arjdbc/postgresql/adapter.rb', line 195

def supports_check_constraints?
  true
end

#supports_comments?Boolean

Returns:

  • (Boolean)


215
216
217
# File 'lib/arjdbc/postgresql/adapter.rb', line 215

def supports_comments?
  true
end

#supports_common_table_expressions?Boolean

Returns:

  • (Boolean)


277
278
279
# File 'lib/arjdbc/postgresql/adapter.rb', line 277

def supports_common_table_expressions?
  true
end

#supports_datetime_with_precision?Boolean

Returns:

  • (Boolean)


207
208
209
# File 'lib/arjdbc/postgresql/adapter.rb', line 207

def supports_datetime_with_precision?
  true
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


238
239
240
# File 'lib/arjdbc/postgresql/adapter.rb', line 238

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)


246
247
248
# File 'lib/arjdbc/postgresql/adapter.rb', line 246

def supports_explain?
  true
end

#supports_expression_index?Boolean

Returns:

  • (Boolean)


183
184
185
# File 'lib/arjdbc/postgresql/adapter.rb', line 183

def supports_expression_index?
  true
end

#supports_extensions?Boolean

Returns:

  • (Boolean)


250
251
252
# File 'lib/arjdbc/postgresql/adapter.rb', line 250

def supports_extensions?
  database_version >= 90200
end

#supports_foreign_keys?Boolean

Returns:

  • (Boolean)


191
192
193
# File 'lib/arjdbc/postgresql/adapter.rb', line 191

def supports_foreign_keys?
  true
end

#supports_foreign_tables?Boolean

Returns:

  • (Boolean)


262
263
264
# File 'lib/arjdbc/postgresql/adapter.rb', line 262

def supports_foreign_tables?
  database_version >= 90300
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)


171
172
173
# File 'lib/arjdbc/postgresql/adapter.rb', line 171

def supports_index_sort_order?
  true
end

#supports_insert_on_conflict?Boolean Also known as: supports_insert_on_duplicate_skip?, supports_insert_on_duplicate_update?, supports_insert_conflict_target?

Returns:

  • (Boolean)


227
228
229
# File 'lib/arjdbc/postgresql/adapter.rb', line 227

def supports_insert_on_conflict?
  database_version >= 90500
end

#supports_insert_returning?Boolean

Returns:

  • (Boolean)


223
224
225
# File 'lib/arjdbc/postgresql/adapter.rb', line 223

def supports_insert_returning?
  true
end

#supports_json?Boolean

Returns:

  • (Boolean)


211
212
213
# File 'lib/arjdbc/postgresql/adapter.rb', line 211

def supports_json?
  database_version >= 90200
end

#supports_lazy_transactions?Boolean

Returns:

  • (Boolean)


281
282
283
# File 'lib/arjdbc/postgresql/adapter.rb', line 281

def supports_lazy_transactions?
  true
end

#supports_materialized_views?Boolean

Returns:

  • (Boolean)


258
259
260
# File 'lib/arjdbc/postgresql/adapter.rb', line 258

def supports_materialized_views?
  database_version >= 90300
end

#supports_optimizer_hints?Boolean

Returns:

  • (Boolean)


270
271
272
273
274
275
# File 'lib/arjdbc/postgresql/adapter.rb', line 270

def supports_optimizer_hints?
  unless defined?(@has_pg_hint_plan)
    @has_pg_hint_plan = extension_available?("pg_hint_plan")
  end
  @has_pg_hint_plan
end

#supports_partial_index?Boolean

Returns:

  • (Boolean)


179
180
181
# File 'lib/arjdbc/postgresql/adapter.rb', line 179

def supports_partial_index?
  true
end

#supports_partitioned_indexes?Boolean

Returns:

  • (Boolean)


175
176
177
# File 'lib/arjdbc/postgresql/adapter.rb', line 175

def supports_partitioned_indexes?
  database_version >= 110_000
end

#supports_pgcrypto_uuid?Boolean

Returns:

  • (Boolean)


266
267
268
# File 'lib/arjdbc/postgresql/adapter.rb', line 266

def supports_pgcrypto_uuid?
  database_version >= 90400
end

#supports_ranges?Boolean

Returns:

  • (Boolean)


254
255
256
# File 'lib/arjdbc/postgresql/adapter.rb', line 254

def supports_ranges?
  database_version >= 90200
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


219
220
221
# File 'lib/arjdbc/postgresql/adapter.rb', line 219

def supports_savepoints?
  true
end

#supports_transaction_isolation?Boolean

Returns:

  • (Boolean)


187
188
189
# File 'lib/arjdbc/postgresql/adapter.rb', line 187

def supports_transaction_isolation?
  true
end

#supports_validate_constraints?Boolean

Returns:

  • (Boolean)


199
200
201
# File 'lib/arjdbc/postgresql/adapter.rb', line 199

def supports_validate_constraints?
  true
end

#supports_views?Boolean

Returns:

  • (Boolean)


203
204
205
# File 'lib/arjdbc/postgresql/adapter.rb', line 203

def supports_views?
  true
end

#use_insert_returning?Boolean

Returns:

  • (Boolean)


334
335
336
# File 'lib/arjdbc/postgresql/adapter.rb', line 334

def use_insert_returning?
  @use_insert_returning
end

#valid_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


159
160
161
# File 'lib/arjdbc/postgresql/adapter.rb', line 159

def valid_type?(type)
  !native_database_types[type].nil?
end

#write_query?(sql) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


426
427
428
# File 'lib/arjdbc/postgresql/adapter.rb', line 426

def write_query?(sql) # :nodoc:
  !READ_QUERY.match?(sql)
end