Class: ChronoModel::Adapter

Inherits:
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
  • Object
show all
Includes:
DDL, Indexes, Migrations, Upgrade
Defined in:
lib/chrono_model/adapter.rb,
lib/chrono_model/adapter/ddl.rb,
lib/chrono_model/adapter/indexes.rb,
lib/chrono_model/adapter/upgrade.rb,
lib/chrono_model/adapter/migrations.rb,
lib/chrono_model/adapter/migrations_modules/stable.rb

Overview

This class implements all ActiveRecord::ConnectionAdapters::SchemaStatements methods adding support for temporal extensions. It inherits from the Postgres adapter for a clean override of its methods using super.

Defined Under Namespace

Modules: DDL, Indexes, Migrations, MigrationsModules, Upgrade

Constant Summary collapse

TEMPORAL_SCHEMA =

The schema holding current data

'temporal'
HISTORY_SCHEMA =

The schema holding historical data

'history'

Instance Method Summary collapse

Methods included from Upgrade

#chrono_upgrade!

Methods included from Indexes

#add_temporal_indexes, #add_timeline_consistency_constraint, #remove_temporal_indexes, #remove_timeline_consistency_constraint, #timeline_consistency_constraint_name

Methods included from Migrations

#add_column, #change_column, #change_column_default, #change_column_null, #change_table, #create_table, #drop_table, #remove_column, #rename_column, #rename_table

Methods included from MigrationsModules::Stable

#add_index, #remove_index

Constructor Details

#initializeAdapter

Returns a new instance of Adapter.



30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/chrono_model/adapter.rb', line 30

def initialize(*)
  super

  connect!

  unless chrono_supported?
    raise ChronoModel::Error, 'Your database server is not supported by ChronoModel. ' \
                              'Currently, only PostgreSQL >= 9.3 is supported.'
  end

  chrono_setup!
end

Instance Method Details

#chrono_metadata_for(view_name) ⇒ Object

Reads the Gem metadata from the COMMENT set on the given PostgreSQL view name.



159
160
161
162
163
164
165
166
# File 'lib/chrono_model/adapter.rb', line 159

def (view_name)
  comment = select_value(
    "SELECT obj_description(#{quote(view_name)}::regclass)",
    "ChronoModel metadata for #{view_name}"
  ) if data_source_exists?(view_name)

  MultiJson.load(comment || '{}').with_indifferent_access
end

#chrono_metadata_set(view_name, metadata) ⇒ Object

Writes Gem metadata on the COMMENT field in the given VIEW name.



170
171
172
173
174
# File 'lib/chrono_model/adapter.rb', line 170

def (view_name, )
  comment = MultiJson.dump()

  execute %( COMMENT ON VIEW #{view_name} IS #{quote(comment)} )
end

#chrono_setup!Object



53
54
55
56
57
# File 'lib/chrono_model/adapter.rb', line 53

def chrono_setup!
  chrono_ensure_schemas

  chrono_upgrade_warning
end

#chrono_supported?Boolean

Returns true whether the connection adapter supports our implementation of temporal tables. Currently, Chronomodel is supported starting with PostgreSQL 9.3 (90300 in PostgreSQL’s ‘PG_VERSION_NUM` numeric format).

Returns:

  • (Boolean)


49
50
51
# File 'lib/chrono_model/adapter.rb', line 49

def chrono_supported?
  postgresql_version >= 90300 # rubocop:disable Style/NumericLiterals
end

#column_definitionsObject

Runs column_definitions in the temporal schema, as the table there defined is the source for this information.

The default search path is included however, since the table may reference types defined in other schemas, which result in their names becoming schema qualified, which will cause type resolutions to fail.

NOTE: This method is dynamically defined, see the source.



93
# File 'lib/chrono_model/adapter.rb', line 93

def column_definitions; end

#is_chrono?(table) ⇒ Boolean

Returns true if the given name references a temporal table.

Returns:

  • (Boolean)


151
152
153
154
# File 'lib/chrono_model/adapter.rb', line 151

def is_chrono?(table)
  on_temporal_schema { data_source_exists?(table) } &&
    on_history_schema { data_source_exists?(table) }
end

#on_history_schema(&block) ⇒ Object

Evaluates the given block in the history schema.



109
110
111
# File 'lib/chrono_model/adapter.rb', line 109

def on_history_schema(&block)
  on_schema(HISTORY_SCHEMA, &block)
end

#on_schema(schema, recurse: :follow) ⇒ Object

Evaluates the given block in the given schema search path.

Recursion works by saving the old_path the function closure at each recursive call.

See specs for examples and behaviour.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/chrono_model/adapter.rb', line 120

def on_schema(schema, recurse: :follow)
  old_path = schema_search_path

  count_recursions do
    if (recurse == :follow) || (Thread.current['recursions'] == 1)
      self.schema_search_path = schema
    end

    yield
  end
ensure
  # If the transaction is aborted, any execute() call will raise
  # "transaction is aborted errors" - thus calling the Adapter's
  # setter won't update the memoized variable.
  #
  # Here we reset it to +nil+ to refresh it on the next call, as
  # there is no way to know which path will be restored when the
  # transaction ends.
  #
  transaction_aborted =
    chrono_connection.transaction_status == PG::Connection::PQTRANS_INERROR

  if transaction_aborted && Thread.current['recursions'] == 1
    @schema_search_path = nil
  else
    self.schema_search_path = old_path
  end
end

#on_temporal_schema(&block) ⇒ Object

Evaluates the given block in the temporal schema.



103
104
105
# File 'lib/chrono_model/adapter.rb', line 103

def on_temporal_schema(&block)
  on_schema(TEMPORAL_SCHEMA, &block)
end

#primary_key(table_name) ⇒ Object

Runs primary_key, indexes and default_sequence_name in the temporal schema, as the table there defined is the source for this information.

Moreover, the PostgreSQLAdapter indexes method uses current_schema(), thus this is the only (and cleanest) way to make injection work.

Schema nesting is disabled on these calls, make sure to fetch metadata from the first caller’s selected schema and not from the current one.

NOTE: These methods are dynamically defined, see the source.



73
# File 'lib/chrono_model/adapter.rb', line 73

def primary_key(table_name); end

#valid_table_definition_optionsObject



176
177
178
# File 'lib/chrono_model/adapter.rb', line 176

def valid_table_definition_options
  super + %i[temporal journal no_journal full_journal]
end