@@ -28,17 +28,16 @@ structure by e.g. keeping the view rules in sync or dropping/recreating it when
2828migrations. A schema dumper is available as well.
2929
3030Data extraction at a single point in time and even ` JOIN ` s between temporal and non-temporal data
31- is implemented using
32- [ Common Table Expressions] ( http://www.postgresql.org/docs/9.0/static/queries-with.html )
33- (WITH queries) and a ` WHERE date >= valid_from AND date < valid_to ` clause, generated automatically
34- by the provided ` TimeMachine ` module to be included in your models.
31+ is implemented using sub-selects and a ` WHERE ` generated by the provided ` TimeMachine ` module to
32+ be included in your models.
3533
36- Optimal temporal timestamps indexing is provided using GiST spatial search.
34+ The ` WHERE ` is optimized using a spatial GiST index in which time is represented represented by
35+ boxes and the filtering is done using the overlapping (` && ` ) geometry operator.
3736
3837All timestamps are (forcibly) stored in the UTC time zone, bypassing the ` AR::Base.config.default_timezone `
3938setting.
4039
41- See [ README.sql] ( https://github.com/ifad/chronomodel/blob/master/README.sql ) for the plain SQL
40+ See [ README.sql] ( https://github.com/ifad/chronomodel/blob/master/README.sql ) for the plain SQL
4241defining this temporal schema for a single table.
4342
4443
@@ -121,35 +120,50 @@ will make an `as_of` class method available to your model. E.g.:
121120
122121Will execute:
123122
124- WITH countries AS (
125- SELECT * FROM history.countries WHERE #{1.year.ago.utc} >= valid_from AND #{1.year.ago.utc} < valid_to
126- ) SELECT * FROM countries
123+ SELECT "countries".* FROM (
124+ SELECT "history"."countries".* FROM "history"."countries"
125+ WHERE box(
126+ point( date_part( 'epoch', '#{1.year.ago.utc}'::timestamp ), 0 ),
127+ point( date_part( 'epoch', '#{1.year.ago.utc}'::timestamp ), 0 )
128+ ) &&
129+ box(
130+ point( date_part( 'epoch', "history"."addresses"."valid_from" ), 0 ),
131+ point( date_part( 'epoch', "history"."addresses"."valid_to" ), 0 ),
132+ )
133+ ) AS "countries"
127134
128135This work on associations using temporal extensions as well:
129136
130137 Country.as_of(1.year.ago).first.compositions
131138
132139Will execute:
133140
134- WITH countries AS (
135- SELECT * FROM history.countries WHERE #{1.year.ago.utc} >= valid_from AND #{1.year.ago.utc} < valid_to
136- ) SELECT * FROM countries LIMIT 1
141+ # ... countries history query ...
142+ LIMIT 1
143+
144+ SELECT * FROM (
145+ SELECT "history"."compositions".* FROM "history"."compositions"
146+ WHERE box(
147+ point( date_part( 'epoch', '#{above_timestamp}'::timestamp ), 0 ),
148+ point( date_part( 'epoch', '#{above_timestamp}'::timestamp ), 0 )
149+ ) &&
150+ box(
151+ point( date_part( 'epoch', "history"."addresses"."valid_from" ), 0 ),
152+ point( date_part( 'epoch', "history"."addresses"."valid_to" ), 0 ),
153+ )
154+ ) AS "compositions" WHERE country_id = X
137155
138- WITH compositions AS (
139- SELECT * FROM history.countries WHERE #{above_timestamp} >= valid_from AND #{above_timestamp} < valid_to
140- ) SELECT * FROM compositions WHERE country_id = X
141-
142156And ` .joins ` works as well:
143157
144158 Country.as_of(1.month.ago).joins(:compositions)
145-
159+
146160Will execute:
147161
148- WITH countries AS (
149- SELECT * FROM history.countries WHERE #{1.year.ago.utc} >= valid_from AND #{1.year.ago.utc} < valid_to
150- ), compositions AS (
151- SELECT * FROM history.countries WHERE #{above_timestamp} >= valid_from AND #{above_timestamp} < valid_to
152- ) SELECT * FROM countries INNER JOIN countries ON compositions.country_id = countries.id
162+ SELECT " countries".* FROM (
163+ # .. countries history query ..
164+ ) AS "countries" INNER JOIN (
165+ # .. compositions history query ..
166+ ) AS "compositions" ON compositions.country_id = countries.id
153167
154168More methods are provided, see the
155169[ TimeMachine] ( https://github.com/ifad/chronomodel/blob/master/lib/chrono_model/time_machine.rb ) source
@@ -168,23 +182,30 @@ them in your output, use `rake VERBOSE=true`.
168182
169183 * ` .includes ` still doesn't work, but it'll fixed soon.
170184
171- * Some monkeypatching has been necessary both to ` ActiveRecord::Relation ` and
172- to ` Arel::Visitors::ToSql ` to fix a bug with ` WITH ` queries generation. This
173- will be reported to the upstream with a pull request after extensive testing.
185+ * The history queries are very verbose, they should be factored out in a
186+ per-table stored procedure.
174187
175188 * The migration statements extension is implemented using a Man-in-the-middle
176189 class that inherits from the PostgreSQL adapter, and that relies on some
177- private APIs. This should be made more maintainable, maybe by implementing
178- an extension framework for connection adapters. This library will (clearly)
179- never be merged into Rails, as it is against its principle of treating the
180- SQL database as a dummy data store.
190+ private APIs. This should be made more maintainable, maybe by requiring
191+ the use of ` adapter: chronomodel ` or ` adapter: chrono_postgresql ` .
181192
182193 * The schema dumper is WAY TOO hacky.
183194
184195 * Savepoints are disabled, because there is
185196 [ currently] ( http://archives.postgresql.org/pgsql-hackers/2012-08/msg01094.php )
186197 no way to identify a subtransaction belonging to the current transaction.
187198
199+ * The choice of using subqueries instead of [ Common Table Expressions] ( http://www.postgresql.org/docs/9.0/static/queries-with.html )
200+ was dictated by the fact that CTEs [ currently acts as an optimization
201+ fence] ( http://archives.postgresql.org/pgsql-hackers/2012-09/msg00700.php ) .
202+ If it will be possible [ to opt-out of the
203+ fence] ( http://archives.postgresql.org/pgsql-hackers/2012-10/msg00024.php )
204+ in the future, they will be probably be used again as they were [ in the
205+ past] ( https://github.com/ifad/chronomodel/commit/39ab32a ) , because the
206+ resulting queries are much more readable, and do not inhibit using .from()
207+ from ARel.
208+
188209
189210## Contributing
190211
0 commit comments