Class: ActiveRecord::ConnectionAdapters::MaterializeAdapter

Overview

The Materialize adapter works with the native C (bitbucket.org/ged/ruby-pg) driver.

Options:

  • :host - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets, the default is to connect to localhost.

  • :port - Defaults to 5432.

  • :username - Defaults to be the same as the operating system name of the user running the application.

  • :password - Password to be used if the server demands password authentication.

  • :database - Defaults to be the same as the user name.

  • :schema_search_path - An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the :schema_order option.

  • :encoding - An optional client encoding that is used in a SET client_encoding TO <encoding> call on the connection.

  • :min_messages - An optional client min messages that is used in a SET client_min_messages TO <min_messages> call on the connection.

  • :variables - An optional hash of additional parameters that will be used in SET SESSION key = val calls on the connection.

  • :insert_returning - An optional boolean to control the use of RETURNING for INSERT statements defaults to true.

Any further options are used as connection parameters to libpq. See www.postgresql.org/docs/current/static/libpq-connect.html for the list of parameters.

In addition, default connection parameters of libpq can be set per environment variables. See www.postgresql.org/docs/current/static/libpq-envars.html .

Defined Under Namespace

Classes: StatementPool

Constant Summary collapse

ADAPTER_NAME =
"Materialize"
NATIVE_DATABASE_TYPES =
{
  primary_key: "bigint",
  string:      { name: "character varying" },
  text:        { name: "text" },
  integer:     { name: "integer" },
  bigint:      { name: "int8" },
  float:       { name: "float4" },
  double:      { name: "float8" },
  decimal:     { name: "decimal" },
  datetime:    { name: "timestamp" },
  timestampz:  { name: "timestamp with time zone" },
  time:        { name: "time" },
  date:        { name: "date" },
  binary:      { name: "bytea" },
  boolean:     { name: "boolean" },
  map:         { name: "map" },
  json:        { name: "jsonb" },
  interval:    { name: "interval" },
  record:      { name: "record" },
  oid:         { name: "oid" },
}
OID =

:nodoc:

Materialize::OID
OPERATION_ALIASES =

:nodoc:

{ # :nodoc:
  "maximum" => "max",
  "minimum" => "min",
  "average" => "avg",
}

Constants included from ActiveRecord::ConnectionAdapters::Materialize::DatabaseStatements

ActiveRecord::ConnectionAdapters::Materialize::DatabaseStatements::BYTEA_COLUMN_TYPE_OID, ActiveRecord::ConnectionAdapters::Materialize::DatabaseStatements::MONEY_COLUMN_TYPE_OID

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ActiveRecord::ConnectionAdapters::Materialize::Schema::ViewStatements

#materialized_views, #view_sql

Methods included from ActiveRecord::ConnectionAdapters::Materialize::Schema::SourceStatements

#create_source, #drop_source, #select_database_config, #source_exists?, #source_options, #sources

Methods included from ActiveRecord::ConnectionAdapters::Materialize::DatabaseStatements

#begin_db_transaction, #begin_isolated_db_transaction, #commit_db_transaction, #exec_delete, #exec_query, #exec_rollback_db_transaction, #execute, #explain, #query, #result_as_array, #write_query?

Methods included from ActiveRecord::ConnectionAdapters::Materialize::SchemaStatements

#add_column, #add_index, #change_column, #change_column_default, #change_column_null, #collation, #columns_for_distinct, #create_database, #create_schema, #create_schema_dumper, #ctype, #current_database, #current_schema, #default_sequence_name, #drop_database, #drop_schema, #drop_table, #encoding, #index_name_exists?, #indexes, #primary_keys, #recreate_database, #remove_index, #rename_column, #rename_index, #rename_table, #schema_exists?, #schema_names, #schema_search_path, #table_comment, #table_options, #type_to_sql, #update_table_definition, #validate_constraint, #validate_foreign_key

Methods included from ActiveRecord::ConnectionAdapters::Materialize::ReferentialIntegrity

#disable_referential_integrity

Methods included from ActiveRecord::ConnectionAdapters::Materialize::Quoting

#column_name_matcher, #column_name_with_order_matcher, #escape_bytea, #lookup_cast_type_from_column, #quote_column_name, #quote_default_expression, #quote_schema_name, #quote_string, #quote_table_name, #quote_table_name_for_assignment, #quoted_binary, #quoted_date, #unescape_bytea

Constructor Details

#initialize(connection, logger, connection_parameters, config) ⇒ MaterializeAdapter

Initializes and connects a Materialize adapter.



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 192

def initialize(connection, logger, connection_parameters, config)
  super(connection, logger, config)

  @connection_parameters = connection_parameters

  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
  @local_tz = nil
  @max_identifier_length = nil

  configure_connection
  add_pg_encoders
  add_pg_decoders

  @type_map = Type::HashLookupTypeMap.new
  initialize_type_map
  @local_tz = execute("SHOW TIMEZONE", "SCHEMA").first["TimeZone"]
end

Class Method Details

.database_exists?(config) ⇒ Boolean

Returns:

  • (Boolean)


210
211
212
213
214
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 210

def self.database_exists?(config)
  !!ActiveRecord::Base.materialize_connection(config)
rescue ActiveRecord::NoDatabaseError
  false
end

Instance Method Details

#active?Boolean

Is this connection alive and ready for queries?

Returns:

  • (Boolean)


217
218
219
220
221
222
223
224
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 217

def active?
  @lock.synchronize do
    @connection.query "SELECT 1"
  end
  true
rescue PG::Error
  false
end

#build_insert_sql(insert) ⇒ Object

:nodoc:



349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 349

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

#check_versionObject

:nodoc:



363
364
365
366
367
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 363

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

#column_name_for_operation(operation, node) ⇒ Object

:nodoc:



329
330
331
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 329

def column_name_for_operation(operation, node) # :nodoc:
  OPERATION_ALIASES.fetch(operation) { operation.downcase }
end

#create_unlogged_tablesObject

:singleton-method: Materialize allows the creation of “unlogged” tables, which do not record data in the Materialize Write-Ahead Log. This can make the tables faster, but significantly increases the risk of data loss if the database crashes. As a result, this should not be used in production environments. If you would like all created tables to be unlogged in the test environment you can add the following line to your test.rb file:

ActiveRecord::ConnectionAdapters::MaterializeAdapter.create_unlogged_tables = true


108
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 108

class_attribute :create_unlogged_tables, default: false

#default_index_type?(index) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


345
346
347
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 345

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

#discard!Object

:nodoc:



258
259
260
261
262
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 258

def discard! # :nodoc:
  super
  @connection.socket_io.reopen(IO::NULL) rescue nil
  @connection = nil
end

#disconnect!Object

Disconnects from the database if already connected. Otherwise, this method does nothing.



251
252
253
254
255
256
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 251

def disconnect!
  @lock.synchronize do
    super
    @connection.close rescue nil
  end
end

#get_advisory_lock(lock_id) ⇒ Object

:nodoc:



304
305
306
307
308
309
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 304

def get_advisory_lock(lock_id) # :nodoc:
  unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
    raise(ArgumentError, "Materialize 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

Returns the version of the connected Materialize server.



340
341
342
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 340

def get_database_version # :nodoc:
  @connection.server_version
end

#max_identifier_lengthObject

Returns the configured supported identifier length supported by Materialize



319
320
321
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 319

def max_identifier_length
  @max_identifier_length || 64
end

#native_database_typesObject

:nodoc:



264
265
266
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 264

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#reconnect!Object

Close then reopen the connection.



227
228
229
230
231
232
233
234
235
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 227

def reconnect!
  @lock.synchronize do
    super
    @connection.reset
    configure_connection
  rescue PG::ConnectionBad
    connect
  end
end

#release_advisory_lock(lock_id) ⇒ Object

:nodoc:



311
312
313
314
315
316
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 311

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

#reset!Object



237
238
239
240
241
242
243
244
245
246
247
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 237

def reset!
  @lock.synchronize do
    clear_cache!
    reset_transaction
    unless @connection.transaction_status == ::PG::PQTRANS_IDLE
      @connection.query "ROLLBACK"
    end
    @connection.query "DISCARD ALL"
    configure_connection
  end
end

#session_auth=(user) ⇒ Object

Set the authorized user for this session



324
325
326
327
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 324

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

#supports_advisory_locks?Boolean

Returns:

  • (Boolean)


272
273
274
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 272

def supports_advisory_locks?
  true
end

#supports_bulk_alter?Boolean

Returns:

  • (Boolean)


141
142
143
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 141

def supports_bulk_alter?
  true
end

#supports_common_table_expressions?Boolean

Returns:

  • (Boolean)


296
297
298
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 296

def supports_common_table_expressions?
  true
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


268
269
270
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 268

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)


276
277
278
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 276

def supports_explain?
  true
end

#supports_insert_returning?Boolean

TODO: test insert returning

Returns:

  • (Boolean)


159
160
161
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 159

def supports_insert_returning?
  true
end

#supports_json?Boolean

Returns:

  • (Boolean)


154
155
156
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 154

def supports_json?
  true
end

#supports_lazy_transactions?Boolean

Returns:

  • (Boolean)


300
301
302
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 300

def supports_lazy_transactions?
  true
end

#supports_materialized_views?Boolean

Returns:

  • (Boolean)


285
286
287
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 285

def supports_materialized_views?
  true
end

#supports_optimizer_hints?Boolean

Returns:

  • (Boolean)


289
290
291
292
293
294
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 289

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_ranges?Boolean

Returns:

  • (Boolean)


280
281
282
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 280

def supports_ranges?
  true
end

#supports_transaction_isolation?Boolean

TODO: test transaction isolation

Returns:

  • (Boolean)


146
147
148
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 146

def supports_transaction_isolation?
  true
end

#supports_views?Boolean

Returns:

  • (Boolean)


150
151
152
# File 'lib/active_record/connection_adapters/materialize_adapter.rb', line 150

def supports_views?
  true
end