activefacts-compositions 1.9.16 → 1.9.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/activefacts/compositions/staging.rb +27 -1
- data/lib/activefacts/compositions/staging/persistent.rb +107 -0
- data/lib/activefacts/compositions/traits/datavault.rb +3 -1
- data/lib/activefacts/compositions/version.rb +1 -1
- data/lib/activefacts/generator/sql.rb +10 -1
- data/lib/activefacts/generator/sql/server.rb +5 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 629d91efc99ba1f54872c6c668a1ee0f86bf7d04
|
4
|
+
data.tar.gz: b2e8c929798553e24d7bf1e8fe0e81718c0b811b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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, '
|
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};")
|
@@ -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 '+
|
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.
|
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-
|
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
|