lib/chrono_model/adapter.rb in chrono_model-0.3.0 vs lib/chrono_model/adapter.rb in chrono_model-0.3.1
- old
+ new
@@ -6,13 +6,18 @@
# 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.
#
class Adapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
- TEMPORAL_SCHEMA = 'temporal' # The schema holding current data
- HISTORY_SCHEMA = 'history' # The schema holding historical data
+ # The schema holding current data
+ TEMPORAL_SCHEMA = 'temporal'
+ # The schema holding historical data
+ HISTORY_SCHEMA = 'history'
+
+ # Chronomodel is supported starting with PostgreSQL >= 9.0
+ #
def chrono_supported?
postgresql_version >= 90000
end
# Creates the given table, possibly creating the temporal schema
@@ -85,10 +90,28 @@
execute "ALTER TABLE #{table_name} SET SCHEMA #{TEMPORAL_SCHEMA}"
_on_history_schema { chrono_create_history_for(table_name) }
chrono_create_view_for(table_name)
TableCache.add! table_name
+
+ # Optionally copy the plain table data, setting up history
+ # retroactively.
+ #
+ if options[:copy_data]
+ seq = _on_history_schema { serial_sequence(table_name, primary_key(table_name)) }
+ from = options[:validity] || '0001-01-01 00:00:00'
+
+ execute %[
+ INSERT INTO #{HISTORY_SCHEMA}.#{table_name}
+ SELECT *,
+ nextval('#{seq}') AS hid,
+ timestamp '#{from}' AS valid_from,
+ timestamp '9999-12-31 00:00:00' AS valid_to,
+ timezone('UTC', now()) AS recorded_at
+ FROM #{TEMPORAL_SCHEMA}.#{table_name}
+ ]
+ end
end
chrono_alter(table_name) { super table_name, options, &block }
else
if options[:temporal] == false && is_chrono?(table_name)
@@ -351,19 +374,35 @@
fields, values = columns.join(', '), columns.map {|c| "new.#{c}"}.join(', ')
# INSERT - inert data both in the temporal table and in the history one.
#
- execute <<-SQL
- CREATE OR REPLACE RULE #{table}_ins AS ON INSERT TO #{table} DO INSTEAD (
+ if sequence.present?
+ execute <<-SQL
+ CREATE OR REPLACE RULE #{table}_ins AS ON INSERT TO #{table} DO INSTEAD (
- INSERT INTO #{current} ( #{fields} ) VALUES ( #{values} );
+ INSERT INTO #{current} ( #{fields} ) VALUES ( #{values} );
- INSERT INTO #{history} ( #{pk}, #{fields}, valid_from )
- VALUES ( currval('#{sequence}'), #{values}, timezone('UTC', now()) )
- RETURNING #{pk}, #{fields}
- )
- SQL
+ INSERT INTO #{history} ( #{pk}, #{fields}, valid_from )
+ VALUES ( currval('#{sequence}'), #{values}, timezone('UTC', now()) )
+ RETURNING #{pk}, #{fields}
+ )
+ SQL
+ else
+ fields_with_pk = "#{pk}, " << fields
+ values_with_pk = "new.#{pk}, " << values
+
+ execute <<-SQL
+ CREATE OR REPLACE RULE #{table}_ins AS ON INSERT TO #{table} DO INSTEAD (
+
+ INSERT INTO #{current} ( #{fields_with_pk} ) VALUES ( #{values_with_pk} );
+
+ INSERT INTO #{history} ( #{fields_with_pk}, valid_from )
+ VALUES ( #{values_with_pk}, timezone('UTC', now()) )
+ RETURNING #{fields_with_pk}
+ )
+ SQL
+ end
# UPDATE - set the last history entry validity to now, save the current data
# in a new history entry and update the temporal table with the new data.
#
execute <<-SQL