Class: ChronoModel::Adapter

Inherits:
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
  • Object
show all
Includes:
DDL, Indexes, Migrations, TSRange, Upgrade
Defined in:
lib/chrono_model/adapter.rb,
lib/chrono_model/adapter/ddl.rb,
lib/chrono_model/adapter/indexes.rb,
lib/chrono_model/adapter/tsrange.rb,
lib/chrono_model/adapter/upgrade.rb,
lib/chrono_model/adapter/migrations.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, TSRange, 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 TSRange

#initialize_type_map

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, #add_index, #change_column, #change_column_default, #change_column_null, #change_table, #create_table, #drop_table, #remove_column, #remove_index, #rename_column, #rename_table

Instance Method Details

#chrono_metadata_for(view_name) ⇒ Object

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



143
144
145
146
147
148
149
# File 'lib/chrono_model/adapter.rb', line 143

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.



153
154
155
156
157
# File 'lib/chrono_model/adapter.rb', line 153

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

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

#chrono_setup!Object



36
37
38
39
40
# File 'lib/chrono_model/adapter.rb', line 36

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.

Returns:

  • (Boolean)


32
33
34
# File 'lib/chrono_model/adapter.rb', line 32

def chrono_supported?
  postgresql_version >= 90300
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.



76
77
# File 'lib/chrono_model/adapter.rb', line 76

def column_definitions
end

#is_chrono?(table) ⇒ Boolean

Returns true if the given name references a temporal table.

Returns:

  • (Boolean)


135
136
137
138
# File 'lib/chrono_model/adapter.rb', line 135

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.



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

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.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/chrono_model/adapter.rb', line 103

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

  count_recursions do
    if recurse == :follow or 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 =
    @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.



86
87
88
# File 'lib/chrono_model/adapter.rb', line 86

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.



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

def primary_key(table_name)
end