activefacts-compositions 1.9.16 → 1.9.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1f365db9787609b86e8184f61d9fc4c2d0b6e76e
4
- data.tar.gz: 66f3cd28bbd6eeadb7a9ee48fb1afd2774697193
3
+ metadata.gz: 629d91efc99ba1f54872c6c668a1ee0f86bf7d04
4
+ data.tar.gz: b2e8c929798553e24d7bf1e8fe0e81718c0b811b
5
5
  SHA512:
6
- metadata.gz: 1b8237ff0549bd7d36c8c8bcae1ad5272a3d13357c61ae4b7bcd4f9c1cd6c88bc46525844128d472231034c4df2c999230c7e964eee40a730515549ab94d2fcc
7
- data.tar.gz: 8057016adea612072f44aa65a84496d6e80660394ae9199157868475dca6e9a39ed96679c0b71cc62408f3a964bca2ebe9fbc1511d4fbb1b8fda9aeeeed81143
6
+ metadata.gz: a5ebe59af26c890d1f5771400d3dced9c123c96b252b10ee0805c04612fed3f7eac0ef4018ecfbdfe09882b43605fa6109307b831c2fce15541c0a5ff43311be
7
+ data.tar.gz: bd9f2f20c79718881cf6a8f7c42b21cb37f6b0d5346302f77d9453341f6aff7768acc9f0e495c7b4e1850879935bddecf72d14e5fb98ce0740b62fa23e7973d1
@@ -18,6 +18,7 @@ module ActiveFacts
18
18
  datavault_options.
19
19
  merge({
20
20
  stgname: ['String', "Suffix or pattern for naming staging tables. Include a + to insert the name. Default 'STG'"],
21
+ fk: ['Boolean', "Retain foreign keys in the output (by default they are deleted)"]
21
22
  }).
22
23
  merge(Relational.options).
23
24
  reject{|k,v| [:surrogates].include?(k) }
@@ -29,6 +30,9 @@ module ActiveFacts
29
30
  @option_stg_name = options.delete('stgname') || 'STG'
30
31
  @option_stg_name.sub!(/^/,'+ ') unless @option_stg_name =~ /\+/
31
32
 
33
+ @option_keep_fks = options.delete('fk') || false
34
+ @option_keep_fks = ['', true, 'true', 'yes'].include?(@option_keep_fks)
35
+
32
36
  super constellation, name, options, 'Staging'
33
37
  end
34
38
 
@@ -39,7 +43,7 @@ module ActiveFacts
39
43
 
40
44
  def inject_value_fields
41
45
  super
42
- inject_loadbatch_relationships
46
+ inject_loadbatch_relationships if @option_loadbatch
43
47
  end
44
48
 
45
49
  def inject_all_datetime_recordsource
@@ -61,6 +65,28 @@ module ActiveFacts
61
65
 
62
66
  inject_all_datetime_recordsource
63
67
  end
68
+
69
+ def complete_foreign_keys
70
+ if @option_keep_fks
71
+ super
72
+ else
73
+ retract_foreign_keys
74
+ end
75
+ end
76
+
77
+ def retract_foreign_keys
78
+ trace :relational_paths, "Retracting foreign keys" do
79
+ @composition.all_composite.each do |composite|
80
+ composite.all_access_path.each do |path|
81
+ next if MM::Index === path
82
+ trace :relational_paths, "Retracting #{path.inspect}" do
83
+ path.retract
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
64
90
  end
65
91
 
66
92
  publish_compositor(Staging)
@@ -0,0 +1,107 @@
1
+ #
2
+ # ActiveFacts Compositions, Staging Compositor.
3
+ #
4
+ # Computes a Persistent Staging schema for Data Vault.
5
+ #
6
+ # Copyright (c) 2017 Clifford Heath. Read the LICENSE file.
7
+ #
8
+ # This style of staging area contains a table for every source table,
9
+ # with injected foreign keys to a LoadBatch table, as for transient
10
+ # staging.
11
+ #
12
+ # Where it differs is that all unique constraints are disabled, and
13
+ # replaced by a primary key that is the source schema's PK appended
14
+ # with the LoadBatchID. Each record also has a ValidUntil timestamp,
15
+ # which is NULL for the most recent record. This allows a load batch
16
+ # to load a new version of any record, and the key value will be
17
+ # adjacent to the previous versions of that record.
18
+ #
19
+ # After a batch is complete, any updated record keys will address
20
+ # more than one record with a NULL ValidUntil timestamp, and this
21
+ # allows delta detection. As a delta is processed, the older record
22
+ # can be marked as valid only until the current LoadBatch time, or
23
+ # it can be deleted. If retained, a purge process can remove records
24
+ # that have exceeded their useful lifetime.
25
+ #
26
+ # Note that this approach doesn't directly detect deleted records.
27
+ # If the source system uses soft deletion, this generator can be
28
+ # configured with the name and value of the deleted flag field(s).
29
+ # This approach also works to manage CDC systems, which provide a
30
+ # record-deleted flag.
31
+ #
32
+
33
+ require "activefacts/compositions/staging"
34
+
35
+ module ActiveFacts
36
+ module Compositions
37
+ class Staging
38
+ class Persistent < Staging
39
+ public
40
+ def self.options
41
+ super.
42
+ merge({
43
+ }).
44
+ merge(Relational.options).
45
+ reject{|k,v| [:surrogates].include?(k) }
46
+ end
47
+
48
+ def initialize constellation, name, options = {}
49
+ # Extract recognised options:
50
+ super(constellation, name, {"loadbatch"=>true}.merge(options))
51
+
52
+ raise "--staging/persistent requires the loadbatch option (you can't disable it)" unless @option_loadbatch
53
+ end
54
+
55
+ def complete_foreign_keys
56
+ super
57
+
58
+ remake_primary_keys
59
+ end
60
+
61
+ def remake_primary_keys
62
+ trace :relational_paths, "Remaking primary keys" do
63
+ @composition.all_composite.each do |composite|
64
+ next if composite.mapping.object_type == @loadbatch_entity_type
65
+ composite.all_access_path.each do |path|
66
+ # Ignore foreign keys:
67
+ next unless MM::Index === path
68
+
69
+ # Don't meddle with the LoadBatch table:
70
+ next if composite.mapping.object_type == @loadbatch_entity_type
71
+ if composite.natural_index == path
72
+ # Add LoadBatchID to the natural index:
73
+ trace :relational_paths, "Appending LoadBatch to primary key #{path.inspect}" do
74
+ load_batch_role =
75
+ composite.mapping.object_type.all_role.detect do |role|
76
+ c = role.counterpart and c.object_type == @loadbatch_entity_type
77
+ end
78
+ trace :relational_paths, "Found LoadBatch role in #{load_batch_role.fact_type.default_reading}" if load_batch_role
79
+ # There can only be one absorption of LoadBatch, because we added it,
80
+ # but if you have separate subtypes, we need to select the one for the right composite:
81
+ absorptions = load_batch_role.
82
+ counterpart.
83
+ all_absorption_as_child_role.
84
+ select{|a| a.root == composite}
85
+ # There should now always be exactly one.
86
+ raise "Missing or ambiguous FK to LoadBatch from #{composite.inspect}" if absorptions.size != 1
87
+ absorption = absorptions[0]
88
+ absorption.all_leaf.each do |leaf|
89
+ @constellation.IndexField(access_path: path, ordinal: path.all_index_field.size, component: leaf)
90
+ end
91
+ end
92
+ elsif path.is_unique
93
+ # Retract other unique keys:
94
+ trace :relational_paths, "Retracting unique secondary index #{path.inspect}" do
95
+ # REVISIT: Or just make it non-unique?
96
+ path.retract
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end # Persistent
104
+ end # Staging
105
+ publish_compositor(Staging::Persistent)
106
+ end
107
+ end
@@ -16,7 +16,8 @@ module ActiveFacts
16
16
  @option_recordsource = options.delete('recordsource')
17
17
  @option_recordsource = 'String' if [true, '', 'true', 'yes', nil].include?(@option_recordsource)
18
18
  @option_loadbatch = options.delete('loadbatch')
19
- @option_loadbatch = 'LoadBatch' if [true, '', 'true', 'yes'].include?(@option_loadbatch)
19
+ @option_loadbatch = 'LoadBatch' if [true, 'true', 'yes'].include?(@option_loadbatch)
20
+ @option_loadbatch = nil if [false, 'false', ''].include?(@option_loadbatch)
20
21
  end
21
22
 
22
23
  def create_loadbatch
@@ -36,6 +37,7 @@ module ActiveFacts
36
37
  end
37
38
 
38
39
  def inject_loadbatch_relationships
40
+ return unless @option_loadbatch
39
41
  @composition.all_composite.each do |composite|
40
42
  next if composite.mapping.object_type.name == @option_loadbatch
41
43
  @compiler.compile("#{composite.mapping.name} was loaded in one #{@option_loadbatch};")
@@ -1,5 +1,5 @@
1
1
  module ActiveFacts
2
2
  module Compositions
3
- VERSION = "1.9.16"
3
+ VERSION = "1.9.17"
4
4
  end
5
5
  end
@@ -60,6 +60,12 @@ module ActiveFacts
60
60
  when "raw"
61
61
  @restrict = "rdv"
62
62
  end
63
+
64
+ # Do not (yet) expose the closed-world vs open world problem.
65
+ # Closed World vs Open World uniqueness is a semantic issue,
66
+ # and so is OW, CW or CW with negation for unary fact types.
67
+ # We need an overall strategy for handling it.
68
+ @closed_world_indices = false # Allow for SQL Server's non-standard NULL indexing
63
69
  end
64
70
 
65
71
  def generate
@@ -184,13 +190,16 @@ module ActiveFacts
184
190
  end
185
191
  contains_nullable_columns = nullable_columns.size > 0
186
192
 
193
+ # The index can only be emitted as PRIMARY if it has no nullable columns:
187
194
  primary = index.composite_as_primary_index && !contains_nullable_columns
195
+
188
196
  column_names =
189
197
  index.all_index_field.map do |ixf|
190
198
  column_name(ixf.component)
191
199
  end
192
200
 
193
- if contains_nullable_columns
201
+ if contains_nullable_columns and @closed_world_indices
202
+ # Implement open-world uniqueness using a filtered index:
194
203
  table_name = safe_table_name(index.composite)
195
204
  delayed_indices <<
196
205
  'CREATE UNIQUE'+index_kind(index)+' INDEX '+
@@ -23,6 +23,11 @@ module ActiveFacts
23
23
  })
24
24
  end
25
25
 
26
+ def initialize composition, options = {}
27
+ super
28
+ @closed_world_indices = true
29
+ end
30
+
26
31
  def table_name_max
27
32
  128
28
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activefacts-compositions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.16
4
+ version: 1.9.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clifford Heath
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-10 00:00:00.000000000 Z
11
+ date: 2017-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -209,6 +209,7 @@ files:
209
209
  - lib/activefacts/compositions/names.rb
210
210
  - lib/activefacts/compositions/relational.rb
211
211
  - lib/activefacts/compositions/staging.rb
212
+ - lib/activefacts/compositions/staging/persistent.rb
212
213
  - lib/activefacts/compositions/traits/datavault.rb
213
214
  - lib/activefacts/compositions/traits/rails.rb
214
215
  - lib/activefacts/compositions/version.rb