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.



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

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



460
461
462
# File 'lib/arjdbc/postgresql/adapter.rb', line 460

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

#build_insert_sql(insert) ⇒ Object

:nodoc:



437
438
439
440
441
442
443
444
445
446
447
448
449
# File 'lib/arjdbc/postgresql/adapter.rb', line 437

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.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
  end

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

#build_truncate_statements(*table_names) ⇒ Object



451
452
453
# File 'lib/arjdbc/postgresql/adapter.rb', line 451

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

#check_versionObject

:nodoc:



84
85
86
87
88
# File 'lib/arjdbc/postgresql/adapter.rb', line 84

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



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

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.



465
466
467
468
469
# File 'lib/arjdbc/postgresql/adapter.rb', line 465

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.



472
473
474
475
# File 'lib/arjdbc/postgresql/adapter.rb', line 472

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.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/arjdbc/postgresql/adapter.rb', line 108

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 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

From AR 5.1 postgres_adapter.rb

Returns:

  • (Boolean)


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

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

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

:nodoc:



427
428
429
430
431
# File 'lib/arjdbc/postgresql/adapter.rb', line 427

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



339
340
341
342
343
# File 'lib/arjdbc/postgresql/adapter.rb', line 339

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

#enable_extension(name) ⇒ Object



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

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



494
495
496
497
# File 'lib/arjdbc/postgresql/adapter.rb', line 494

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



372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/arjdbc/postgresql/adapter.rb', line 372

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



386
387
388
389
390
391
392
# File 'lib/arjdbc/postgresql/adapter.rb', line 386

def execute_batch(statements, name = nil)
  if statements.is_a? Array
    execute(combine_multi_statements(statements), name)
  else
    execute(statements, name)
  end
end

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



394
395
396
397
# File 'lib/arjdbc/postgresql/adapter.rb', line 394

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)


345
346
347
# File 'lib/arjdbc/postgresql/adapter.rb', line 345

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

#extension_enabled?(name) ⇒ Boolean

Returns:

  • (Boolean)


349
350
351
# File 'lib/arjdbc/postgresql/adapter.rb', line 349

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

#extensionsObject



353
354
355
# File 'lib/arjdbc/postgresql/adapter.rb', line 353

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

#get_advisory_lock(lock_id) ⇒ Object

:nodoc:



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

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:



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/arjdbc/postgresql/adapter.rb', line 51

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



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

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



433
434
435
# File 'lib/arjdbc/postgresql/adapter.rb', line 433

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



358
359
360
# File 'lib/arjdbc/postgresql/adapter.rb', line 358

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

#native_database_typesObject



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

def native_database_types
  NATIVE_DATABASE_TYPES
end

#quote_table_name(name) ⇒ Object



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

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:



326
327
328
329
330
331
# File 'lib/arjdbc/postgresql/adapter.rb', line 326

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



515
516
517
518
# File 'lib/arjdbc/postgresql/adapter.rb', line 515

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

#reset!Object



419
420
421
422
423
424
425
# File 'lib/arjdbc/postgresql/adapter.rb', line 419

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



363
364
365
366
# File 'lib/arjdbc/postgresql/adapter.rb', line 363

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

#set_client_encoding(encoding) ⇒ Object



101
102
103
104
# File 'lib/arjdbc/postgresql/adapter.rb', line 101

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



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

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

#supports_advisory_locks?Boolean

Returns:

  • (Boolean)


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

def supports_advisory_locks?
  true
end

#supports_bulk_alter?Boolean

Returns:

  • (Boolean)


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

def supports_bulk_alter?
  true
end

#supports_comments?Boolean

Returns:

  • (Boolean)


244
245
246
# File 'lib/arjdbc/postgresql/adapter.rb', line 244

def supports_comments?
  true
end

#supports_common_table_expressions?Boolean

Returns:

  • (Boolean)


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

def supports_common_table_expressions?
  true
end

#supports_datetime_with_precision?Boolean

Returns:

  • (Boolean)


236
237
238
# File 'lib/arjdbc/postgresql/adapter.rb', line 236

def supports_datetime_with_precision?
  true
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


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

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)


275
276
277
# File 'lib/arjdbc/postgresql/adapter.rb', line 275

def supports_explain?
  true
end

#supports_expression_index?Boolean

Returns:

  • (Boolean)


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

def supports_expression_index?
  true
end

#supports_extensions?Boolean

Returns:

  • (Boolean)


279
280
281
# File 'lib/arjdbc/postgresql/adapter.rb', line 279

def supports_extensions?
  database_version >= 90200
end

#supports_foreign_keys?Boolean

Returns:

  • (Boolean)


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

def supports_foreign_keys?
  true
end

#supports_foreign_tables?Boolean

we don't really support this yet, its a reminder :)

Returns:

  • (Boolean)


291
292
293
# File 'lib/arjdbc/postgresql/adapter.rb', line 291

def supports_foreign_tables? # we don't really support this yet, its a reminder :)
  database_version >= 90300
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)


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

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)


256
257
258
# File 'lib/arjdbc/postgresql/adapter.rb', line 256

def supports_insert_on_conflict?
  database_version >= 90500
end

#supports_insert_returning?Boolean

Returns:

  • (Boolean)


252
253
254
# File 'lib/arjdbc/postgresql/adapter.rb', line 252

def supports_insert_returning?
  true
end

#supports_json?Boolean

Returns:

  • (Boolean)


240
241
242
# File 'lib/arjdbc/postgresql/adapter.rb', line 240

def supports_json?
  database_version >= 90200
end

#supports_lazy_transactions?Boolean

Returns:

  • (Boolean)


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

def supports_lazy_transactions?
  true
end

#supports_materialized_views?Boolean

Returns:

  • (Boolean)


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

def supports_materialized_views?
  database_version >= 90300
end

#supports_optimizer_hints?Boolean

Returns:

  • (Boolean)


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

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)


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

def supports_partial_index?
  true
end

#supports_pgcrypto_uuid?Boolean

Returns:

  • (Boolean)


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

def supports_pgcrypto_uuid?
  database_version >= 90400
end

#supports_ranges?Boolean

Returns:

  • (Boolean)


283
284
285
# File 'lib/arjdbc/postgresql/adapter.rb', line 283

def supports_ranges?
  database_version >= 90200
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


248
249
250
# File 'lib/arjdbc/postgresql/adapter.rb', line 248

def supports_savepoints?
  true
end

#supports_transaction_isolation?Boolean

Returns:

  • (Boolean)


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

def supports_transaction_isolation?
  true
end

#supports_validate_constraints?Boolean

Returns:

  • (Boolean)


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

def supports_validate_constraints?
  true
end

#supports_views?Boolean

Returns:

  • (Boolean)


232
233
234
# File 'lib/arjdbc/postgresql/adapter.rb', line 232

def supports_views?
  true
end

#truncate(table_name, name = nil) ⇒ Object



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

def truncate(table_name, name = nil)
  ActiveRecord::Base.clear_query_caches_for_current_thread if @query_cache_enabled
  execute("TRUNCATE TABLE #{quote_table_name(table_name)}", name)
end

#use_insert_returning?Boolean

Returns:

  • (Boolean)


368
369
370
# File 'lib/arjdbc/postgresql/adapter.rb', line 368

def use_insert_returning?
  @use_insert_returning
end

#valid_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#write_query?(sql) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


405
406
407
# File 'lib/arjdbc/postgresql/adapter.rb', line 405

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