activefacts-compositions 1.9.17 → 1.9.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/activefacts-compositions.gemspec +2 -2
- data/lib/activefacts/compositions/binary.rb +1 -1
- data/lib/activefacts/compositions/compositor.rb +16 -12
- data/lib/activefacts/compositions/datavault.rb +110 -115
- data/lib/activefacts/compositions/relational.rb +137 -94
- data/lib/activefacts/compositions/staging.rb +89 -27
- data/lib/activefacts/compositions/traits/datavault.rb +116 -49
- data/lib/activefacts/compositions/traits/rails.rb +2 -2
- data/lib/activefacts/compositions/version.rb +1 -1
- data/lib/activefacts/generator/doc/cwm.rb +6 -18
- data/lib/activefacts/generator/doc/ldm.rb +1 -1
- data/lib/activefacts/generator/etl/unidex.rb +341 -0
- data/lib/activefacts/generator/oo.rb +31 -14
- data/lib/activefacts/generator/rails/models.rb +6 -5
- data/lib/activefacts/generator/rails/schema.rb +5 -9
- data/lib/activefacts/generator/ruby.rb +2 -2
- data/lib/activefacts/generator/sql/mysql.rb +3 -184
- data/lib/activefacts/generator/sql/oracle.rb +3 -152
- data/lib/activefacts/generator/sql/postgres.rb +3 -145
- data/lib/activefacts/generator/sql/server.rb +3 -126
- data/lib/activefacts/generator/sql.rb +54 -422
- data/lib/activefacts/generator/summary.rb +15 -6
- data/lib/activefacts/generator/traits/expr.rb +41 -0
- data/lib/activefacts/generator/traits/sql/mysql.rb +280 -0
- data/lib/activefacts/generator/traits/sql/oracle.rb +265 -0
- data/lib/activefacts/generator/traits/sql/postgres.rb +287 -0
- data/lib/activefacts/generator/traits/sql/server.rb +262 -0
- data/lib/activefacts/generator/traits/sql.rb +538 -0
- metadata +13 -8
- data/lib/activefacts/compositions/docgraph.rb +0 -798
- data/lib/activefacts/compositions/staging/persistent.rb +0 -107
@@ -14,13 +14,31 @@ module ActiveFacts
|
|
14
14
|
|
15
15
|
def self.options
|
16
16
|
{
|
17
|
-
surrogates: [
|
17
|
+
surrogates: [%w{true field_name_pattern}, "Inject a surrogate key into each table that needs it"],
|
18
|
+
fk: [%w{primary natural hash}, "Enforce foreign keys using the primary (surrogate), natural keys, or a hash of the natural keys"],
|
18
19
|
}.merge(Compositor.options)
|
19
20
|
end
|
20
21
|
|
21
22
|
def initialize constellation, name, options = {}, compositor_name = 'Relational'
|
22
|
-
# Extract recognised options:
|
23
23
|
@option_surrogates = options.delete('surrogates')
|
24
|
+
|
25
|
+
# Extract recognised options:
|
26
|
+
fk = options.delete('fk')
|
27
|
+
@option_fk = :primary unless @option_fk != nil # Don't override subclass default
|
28
|
+
case fk
|
29
|
+
when 'primary', '', nil
|
30
|
+
@option_fk = :primary
|
31
|
+
when 'natural'
|
32
|
+
@option_fk = :natural
|
33
|
+
when 'hash'
|
34
|
+
@option_fk = :hash
|
35
|
+
@option_surrogates = true
|
36
|
+
else
|
37
|
+
raise "Value #{fk.inspect} for fk option is not supported"
|
38
|
+
end
|
39
|
+
|
40
|
+
@surrogate_name_pattern ||= [true, '', 'true', 'yes', nil].include?(t = @option_surrogates) ? '+ ID' : t
|
41
|
+
|
24
42
|
super constellation, name, options, compositor_name
|
25
43
|
end
|
26
44
|
|
@@ -46,11 +64,14 @@ module ActiveFacts
|
|
46
64
|
# If a value type has been mapped to a table, add a column to hold its value
|
47
65
|
inject_value_fields
|
48
66
|
|
67
|
+
# If the compositor calls for audit fields, add them here
|
68
|
+
inject_all_audit_fields
|
69
|
+
|
49
70
|
# Inject surrogate keys if the options ask for that
|
50
71
|
inject_surrogates if @option_surrogates
|
51
72
|
|
52
73
|
# Remove the un-used absorption paths
|
53
|
-
|
74
|
+
retract_reverse_mappings
|
54
75
|
|
55
76
|
# Traverse the absorbed objects to build the path to each required column, including foreign keys:
|
56
77
|
absorb_all_columns
|
@@ -114,7 +135,7 @@ module ActiveFacts
|
|
114
135
|
raise "REVISIT: Internal error" unless absorbing_ref.parent_role.object_type == object_type
|
115
136
|
absorbing_ref = absorbing_ref.flip!
|
116
137
|
candidate.full_absorption =
|
117
|
-
@constellation.FullAbsorption(composition: @composition,
|
138
|
+
@constellation.FullAbsorption(composition: @composition, mapping: absorbing_ref, object_type: object_type)
|
118
139
|
trace :relational_optimiser, "Fully absorb objectified unary #{object_type.name} into #{f.all_role.single.object_type.name}"
|
119
140
|
candidate.definitely_not_table
|
120
141
|
next object_type
|
@@ -134,9 +155,9 @@ module ActiveFacts
|
|
134
155
|
absorption.is_a?(MM::Absorption) && absorption.child_role.base_role == single_pi_role
|
135
156
|
end
|
136
157
|
|
137
|
-
absorbing_ref = absorbing_ref.
|
158
|
+
absorbing_ref = absorbing_ref.forward_mapping || absorbing_ref.flip!
|
138
159
|
candidate.full_absorption =
|
139
|
-
@constellation.FullAbsorption(composition: @composition,
|
160
|
+
@constellation.FullAbsorption(composition: @composition, mapping: absorbing_ref, object_type: object_type)
|
140
161
|
trace :relational_optimiser, "EntityType #{single_pi_role.object_type.name} identifies EntityType #{object_type.name}, so fully absorbs it via #{absorbing_ref.inspect}"
|
141
162
|
candidate.definitely_not_table
|
142
163
|
next object_type
|
@@ -184,10 +205,10 @@ module ActiveFacts
|
|
184
205
|
child_candidate = @candidates[a.child_role.object_type]
|
185
206
|
|
186
207
|
# It's ok if we absorbed them already
|
187
|
-
next true if a.full_absorption && child_candidate.full_absorption.
|
208
|
+
next true if a.full_absorption && child_candidate.full_absorption.mapping != a
|
188
209
|
|
189
210
|
# If our counterpart is a full absorption, don't try to reverse that!
|
190
|
-
next false if (aa = (a.
|
211
|
+
next false if (aa = (a.forward_mapping || a.reverse_mapping)) && aa.full_absorption
|
191
212
|
|
192
213
|
# Otherwise the other end must already be a table or fully absorbed into one
|
193
214
|
next false unless child_candidate.nil? || child_candidate.is_table || child_candidate.full_absorption
|
@@ -205,7 +226,7 @@ module ActiveFacts
|
|
205
226
|
if absorption_paths.size > 0
|
206
227
|
trace :relational_optimiser, "#{object_type.name} is fully absorbed in #{absorption_paths.size} places" do
|
207
228
|
absorption_paths.each do |a|
|
208
|
-
a = a.flip! if a.
|
229
|
+
a = a.flip! if a.forward_mapping
|
209
230
|
trace :relational_optimiser, "#{object_type.name} is fully absorbed via #{a.inspect}"
|
210
231
|
end
|
211
232
|
end
|
@@ -219,8 +240,8 @@ module ActiveFacts
|
|
219
240
|
refs_to = candidate.references_to.reject{|a|a.parent_role.base_role.is_identifying}
|
220
241
|
if !refs_to.empty? and non_identifying_refs_from.size == 0
|
221
242
|
refs_to.map! do |a|
|
222
|
-
a = a.flip! if a.
|
223
|
-
a.
|
243
|
+
a = a.flip! if a.reverse_mapping # We were forward, but the other end must be
|
244
|
+
a.forward_mapping
|
224
245
|
end
|
225
246
|
trace :relational_optimiser, "#{object_type.name} is fully absorbed in #{refs_to.size} places: #{refs_to.map{|ref| ref.inspect}*", "}"
|
226
247
|
candidate.definitely_not_table
|
@@ -244,12 +265,12 @@ module ActiveFacts
|
|
244
265
|
end
|
245
266
|
|
246
267
|
# Remove the unused reverse absorptions:
|
247
|
-
def
|
268
|
+
def retract_reverse_mappings
|
248
269
|
@binary_mappings.each do |object_type, mapping|
|
249
270
|
mapping.all_member.to_a. # Avoid problems with deletion from all_member
|
250
271
|
each do |member|
|
251
|
-
next unless member.is_a?(MM::
|
252
|
-
member.retract if member.
|
272
|
+
next unless member.is_a?(MM::Mapping)
|
273
|
+
member.retract if member.forward_mapping # This is the reverse of some mapping
|
253
274
|
end
|
254
275
|
mapping.re_rank
|
255
276
|
end
|
@@ -280,7 +301,7 @@ module ActiveFacts
|
|
280
301
|
@composition.all_composite.each do |composite|
|
281
302
|
mapping = composite.mapping
|
282
303
|
if mapping.object_type.is_a?(MM::ValueType) and # Composite needs a ValueField
|
283
|
-
!mapping.all_member.detect{|m| m.is_a?(MM::ValueField)} # And
|
304
|
+
!mapping.all_member.detect{|m| m.is_a?(MM::ValueField)} # And doesn't already have one
|
284
305
|
trace :relational_columns, "Adding value field for #{mapping.object_type.name}"
|
285
306
|
@constellation.ValueField(
|
286
307
|
:new,
|
@@ -293,6 +314,13 @@ module ActiveFacts
|
|
293
314
|
end
|
294
315
|
end
|
295
316
|
|
317
|
+
def inject_all_audit_fields
|
318
|
+
end
|
319
|
+
|
320
|
+
def patterned_name pattern, name
|
321
|
+
pattern.sub(/\+/, name)
|
322
|
+
end
|
323
|
+
|
296
324
|
def inject_surrogates
|
297
325
|
composites = @composition.all_composite.to_a
|
298
326
|
return if composites.empty?
|
@@ -305,31 +333,18 @@ module ActiveFacts
|
|
305
333
|
end
|
306
334
|
end
|
307
335
|
|
308
|
-
def
|
309
|
-
@surrogate_type ||= begin
|
310
|
-
surrogate_type_name = [true, '', 'true', 'yes', nil].include?(t = @option_surrogates) ? 'Auto Counter' : t
|
311
|
-
# REVISIT: Crappy: choose the first (currently always single)
|
312
|
-
vocabulary = @composition.all_composite.to_a[0].mapping.object_type.vocabulary
|
313
|
-
@constellation.ValueType(
|
314
|
-
vocabulary: vocabulary,
|
315
|
-
name: surrogate_type_name,
|
316
|
-
concept: [:new, :implication_rule => "surrogate injection"]
|
317
|
-
)
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
def inject_surrogate composite, extension = ' ID'
|
336
|
+
def inject_surrogate composite, name_pattern = @surrogate_name_pattern
|
322
337
|
trace :surrogates, "Injecting surrogate for #{composite.inspect}" do
|
323
338
|
surrogate_component =
|
324
339
|
@constellation.SurrogateKey(
|
325
340
|
:new,
|
326
341
|
parent: composite.mapping,
|
327
|
-
name: composite.mapping.name
|
328
|
-
|
342
|
+
name: patterned_name(name_pattern, composite.mapping.name),
|
343
|
+
injection_annotation: "surrogate"
|
329
344
|
)
|
330
345
|
index =
|
331
346
|
@constellation.Index(:new, composite: composite, is_unique: true,
|
332
|
-
|
347
|
+
composite_as_primary_index: composite)
|
333
348
|
@constellation.IndexField(access_path: index, ordinal: 0, component: surrogate_component)
|
334
349
|
composite.mapping.re_rank
|
335
350
|
surrogate_component
|
@@ -337,6 +352,8 @@ module ActiveFacts
|
|
337
352
|
end
|
338
353
|
|
339
354
|
def needs_surrogate(composite)
|
355
|
+
return true if @option_fk == :hash
|
356
|
+
|
340
357
|
object_type = composite.mapping.object_type
|
341
358
|
if MM::ValueType === object_type
|
342
359
|
trace :surrogates, "#{composite.inspect} is a ValueType that #{object_type.is_auto_assigned ? "is auto-assigned already" : "requires a surrogate" }"
|
@@ -344,7 +361,7 @@ module ActiveFacts
|
|
344
361
|
end
|
345
362
|
|
346
363
|
non_key_members, key_members = composite.mapping.all_member.reject do |member|
|
347
|
-
member.is_a?(MM::Absorption) and member.
|
364
|
+
member.is_a?(MM::Absorption) and member.forward_mapping
|
348
365
|
end.partition do |member|
|
349
366
|
member.rank_key[0] > MM::Component::RANK_IDENT
|
350
367
|
end
|
@@ -470,50 +487,53 @@ module ActiveFacts
|
|
470
487
|
ordered.each do |member|
|
471
488
|
# Only consider forward Absorptions:
|
472
489
|
next if !member.is_a?(MM::Absorption)
|
473
|
-
next if member.
|
490
|
+
next if member.forward_mapping
|
474
491
|
|
475
492
|
child_object_type = member.child_role.object_type
|
476
493
|
child_mapping = @binary_mappings[child_object_type]
|
477
494
|
if child_mapping.composite
|
478
|
-
trace :fks, "FK to #{member.child_role.name} in #{member.
|
495
|
+
trace :fks, "FK to #{member.child_role.name} in #{member.inspect_reason}"
|
479
496
|
accumulator << child_mapping.composite
|
480
497
|
next
|
481
498
|
end
|
482
499
|
|
483
500
|
full_absorption = child_object_type.all_full_absorption[@composition]
|
484
|
-
if full_absorption &&
|
501
|
+
if full_absorption &&
|
502
|
+
MM::Absorption === full_absorption.mapping &&
|
503
|
+
full_absorption.mapping.parent_role.fact_type != member.parent_role.fact_type
|
485
504
|
begin # Follow transitive target absorption
|
486
|
-
child_object_type = full_absorption.
|
505
|
+
child_object_type = full_absorption.mapping.parent_role.object_type
|
487
506
|
end while full_absorption = child_object_type.all_full_absorption[@composition]
|
488
507
|
child_mapping = @binary_mappings[child_object_type]
|
489
|
-
trace :fks, "FK to #{child_mapping.name} in #{member.
|
508
|
+
trace :fks, "FK to #{child_mapping.name} in #{member.inspect_reason} (for fully-absorbed #{member.child_role.name})"
|
490
509
|
accumulator << child_mapping.composite
|
491
510
|
next
|
492
511
|
end
|
493
512
|
|
494
|
-
trace :fks, "Descending all of #{member.child_role.name} in #{member.
|
513
|
+
trace :fks, "Descending all of #{member.child_role.name} in #{member.inspect_reason}" do
|
495
514
|
enumerate_foreign_keys member, child_mapping, accumulator, path
|
496
515
|
end
|
497
516
|
end
|
498
517
|
accumulator
|
499
518
|
end
|
500
519
|
|
501
|
-
# Overwritten by
|
520
|
+
# Overwritten by subclasses to modify a structurally-complete schema
|
502
521
|
def apply_schema_transformations
|
522
|
+
# replace_exclusive_indicators_by_discriminators
|
503
523
|
end
|
504
524
|
|
505
525
|
# This member is an Absorption. Process it recursively, absorbing all its members or just a key
|
506
|
-
# depending on whether the absorbed object is a Composite (or absorbed into one) or not.
|
526
|
+
# depending on whether the absorbed object is a Composite (or fully absorbed into one) or not.
|
507
527
|
def absorb_nested mapping, member, paths
|
508
|
-
|
509
|
-
is_partitioned = (ft = member.child_role.fact_type).is_a?(MM::TypeInheritance) && ft.assimilation == 'partitioned'
|
510
|
-
|
528
|
+
return if MM::ValueField === member
|
511
529
|
# Should we absorb a foreign key or the whole contents?
|
512
|
-
child_object_type = member.
|
530
|
+
child_object_type = member.object_type
|
513
531
|
child_mapping = @binary_mappings[child_object_type]
|
514
|
-
|
515
|
-
|
516
|
-
|
532
|
+
|
533
|
+
if child_mapping.composite && # The child is a separate table
|
534
|
+
!member.is_partitioned_here # We are not absorbing a partitioned supertype
|
535
|
+
trace :relational_columns?, "Absorbing FK to #{child_mapping.composite.mapping.name} in #{member.inspect_reason}" do
|
536
|
+
paths[member] = @constellation.ForeignKey(:new, source_composite: mapping.root, composite: child_mapping.composite, mapping: member)
|
517
537
|
absorb_key member, child_mapping, paths
|
518
538
|
return
|
519
539
|
end
|
@@ -523,68 +543,74 @@ module ActiveFacts
|
|
523
543
|
full_absorption = child_object_type.all_full_absorption[@composition]
|
524
544
|
# We can't use member.full_absorption here, as it's not populated on forked copies
|
525
545
|
# if full_absorption && full_absorption != member.full_absorption
|
526
|
-
if full_absorption &&
|
527
|
-
|
546
|
+
if full_absorption &&
|
547
|
+
MM::Absorption === full_absorption.mapping &&
|
548
|
+
full_absorption.mapping.parent_role.fact_type != member.parent_role.fact_type
|
528
549
|
# REVISIT: This should be done by recursing to absorb_key, not using a loop
|
529
|
-
|
550
|
+
top_mapping = member # Retain this for the ForeignKey
|
530
551
|
begin # Follow transitive target absorption
|
531
|
-
member = mirror(full_absorption.
|
532
|
-
child_object_type = full_absorption.
|
552
|
+
member = mirror(full_absorption.mapping, member)
|
553
|
+
child_object_type = full_absorption.mapping.parent_role.object_type
|
533
554
|
end while full_absorption = child_object_type.all_full_absorption[@composition]
|
534
555
|
child_mapping = @binary_mappings[child_object_type]
|
535
556
|
|
536
|
-
trace :relational_columns?, "Absorbing FK to #{
|
537
|
-
paths[
|
557
|
+
trace :relational_columns?, "Absorbing FK to #{top_mapping.child_role.name} (fully absorbed into #{child_object_type.name}) in #{member.inspect_reason}" do
|
558
|
+
paths[top_mapping] = @constellation.ForeignKey(:new, source_composite: mapping.root, composite: child_mapping.composite, mapping: top_mapping)
|
538
559
|
absorb_key member, child_mapping, paths
|
539
560
|
end
|
540
561
|
return
|
541
562
|
end
|
542
563
|
|
543
|
-
# REVISIT: if
|
544
|
-
trace :relational_columns?, "Absorbing all of #{member.
|
564
|
+
# REVISIT: if member.is_partitioned_here, don't absorb sibling subtypes!
|
565
|
+
trace :relational_columns?, "Absorbing all of #{member.name} in #{member.inspect_reason}" do
|
566
|
+
if MM::Absorption === member && !member.parent_role.is_mandatory && MM::EntityType === member.object_type
|
567
|
+
# REVISIT: If this is an absorbed subtype or full_absorption, add an indicator to that effect
|
568
|
+
# Perhaps only if the member contains any non-mandatory leaves?
|
569
|
+
trace :relational_columns, "REVISIT: Absorb an indicator for absorbed #{member.inspect}"
|
570
|
+
end
|
545
571
|
absorb_all member, child_mapping, paths
|
546
572
|
end
|
547
573
|
end
|
548
574
|
|
549
575
|
# May be overridden in subclasses
|
550
|
-
def
|
551
|
-
|
576
|
+
def preferred_fk_type building_natural_key, source_composite, target_composite
|
577
|
+
@option_fk
|
552
578
|
end
|
553
579
|
|
554
580
|
# Recursively add members to this component for the existential roles of
|
555
581
|
# the composite mapping for the absorbed (child_role) object:
|
556
582
|
def absorb_key mapping, target, paths
|
557
583
|
building_natural_key = paths.detect{|k,i| i.is_a?(MM::Index) && i.composite_as_natural_index}
|
558
|
-
|
559
|
-
prefer_natural = false unless !target.composite || target.composite.primary_index != target.composite.natural_index
|
584
|
+
fk_type = preferred_fk_type(building_natural_key, mapping.root, target.composite)
|
560
585
|
target.re_rank
|
561
586
|
target.all_member.sort_by(&:ordinal).each do |member|
|
562
587
|
rank = member.rank_key[0]
|
563
|
-
|
564
|
-
if rank == MM::Component::RANK_SURROGATE &&
|
588
|
+
break unless rank <= MM::Component::RANK_IDENT
|
589
|
+
if rank == MM::Component::RANK_SURROGATE && fk_type == :natural
|
565
590
|
next
|
566
591
|
end
|
592
|
+
is_primary = member.is_in_primary && mapping.depth == 1
|
567
593
|
member = member.fork_to_new_parent mapping
|
568
|
-
augment_paths paths, member
|
569
|
-
if rank == MM::Component::RANK_SURROGATE &&
|
594
|
+
augment_paths is_primary, paths, member
|
595
|
+
if rank == MM::Component::RANK_SURROGATE && fk_type == :primary
|
570
596
|
break # Will always be first (higher rank), and usurps others
|
571
|
-
elsif member.is_a?(MM::
|
572
|
-
object_type = member.
|
573
|
-
full_absorption = @composition.all_full_absorption[
|
597
|
+
elsif member.is_a?(MM::Mapping) && !member.is_a?(MM::ValueField)
|
598
|
+
object_type = member.object_type
|
599
|
+
full_absorption = @composition.all_full_absorption[object_type]
|
574
600
|
if full_absorption
|
575
601
|
# The target object is fully absorbed. Absorb a key to where it was absorbed
|
576
602
|
# We can't recurse here, because we must descend supertype absorptions
|
577
|
-
while full_absorption
|
578
|
-
trace :relational_columns?, "Absorbing key of fully absorbed #{
|
579
|
-
member = mirror full_absorption.
|
580
|
-
augment_paths paths, member
|
603
|
+
while full_absorption && MM::Absorption === full_absorption.mapping
|
604
|
+
trace :relational_columns?, "Absorbing key of fully absorbed #{object_type.name}" do
|
605
|
+
member = mirror(cloned = full_absorption.mapping, member)
|
606
|
+
augment_paths cloned.is_in_primary, paths, member
|
581
607
|
# Descend so the key fields get fully populated
|
582
|
-
absorb_key member, full_absorption.
|
583
|
-
full_absorption = @composition.all_full_absorption[member.
|
608
|
+
absorb_key member, full_absorption.mapping.parent, paths
|
609
|
+
full_absorption = @composition.all_full_absorption[member.object_type]
|
584
610
|
end
|
585
611
|
end
|
586
612
|
else
|
587
|
-
absorb_key member, @binary_mappings[
|
613
|
+
absorb_key member, @binary_mappings[object_type], paths
|
588
614
|
end
|
589
615
|
end
|
590
616
|
end
|
@@ -598,11 +624,12 @@ module ActiveFacts
|
|
598
624
|
|
599
625
|
pcs = []
|
600
626
|
newpaths = {}
|
601
|
-
if mapping.composite || mapping.full_absorption || mapping.
|
627
|
+
if mapping.composite || mapping.full_absorption || mapping.is_type_inheritance
|
602
628
|
pcs = find_uniqueness_constraints(mapping)
|
603
629
|
|
604
630
|
# Don't build an index from the same PresenceConstraint twice on the same composite (e.g. for a subtype)
|
605
631
|
existing_pcs = mapping.root.all_access_path.select{|ap| MM::Index === ap}.map(&:presence_constraint)
|
632
|
+
# REVISIT: Some of paths.keys are not PresenceConstraints here!
|
606
633
|
newpaths = make_new_paths mapping, paths.keys+existing_pcs, pcs
|
607
634
|
end
|
608
635
|
|
@@ -610,14 +637,15 @@ module ActiveFacts
|
|
610
637
|
ordered = from.all_member.sort_by(&:ordinal)
|
611
638
|
ordered.each do |member|
|
612
639
|
trace :relational_columns, proc {"#{top_level ? 'Existing' : 'Absorbing'} #{member.inspect}"} do
|
640
|
+
is_primary = member.is_in_primary
|
613
641
|
unless top_level # Top-level members are already instantiated
|
614
642
|
member = member.fork_to_new_parent mapping
|
615
643
|
end
|
616
644
|
rel = paths.merge(relevant_paths(newpaths, member))
|
617
|
-
augment_paths rel, member
|
645
|
+
augment_paths is_primary, rel, member
|
618
646
|
|
619
|
-
if member.is_a?(MM::
|
620
|
-
#
|
647
|
+
if member.is_a?(MM::Mapping) && !member.forward_mapping
|
648
|
+
# Process forward absorptions recursively
|
621
649
|
absorb_nested mapping, member, rel
|
622
650
|
end
|
623
651
|
end
|
@@ -670,7 +698,8 @@ module ActiveFacts
|
|
670
698
|
pc.role_sequence.all_role_ref.size == 1 and
|
671
699
|
mapping.is_a?(MM::Absorption) and
|
672
700
|
fa = mapping.full_absorption and
|
673
|
-
|
701
|
+
fa.mapping.is_a?(MM::Absorption) and
|
702
|
+
pc.role_sequence.all_role_ref.single.role.base_role == fa.mapping.parent_role.base_role
|
674
703
|
)
|
675
704
|
end # Alethic uniqueness constraint on far end
|
676
705
|
|
@@ -691,7 +720,7 @@ module ActiveFacts
|
|
691
720
|
end
|
692
721
|
pcs = non_absorption_pcs
|
693
722
|
|
694
|
-
trace :relational_paths, "Uniqueness Constraints for #{mapping.
|
723
|
+
trace :relational_paths, "Uniqueness Constraints for #{mapping.name}" do
|
695
724
|
pcs.each do |pc|
|
696
725
|
trace :relational_paths, "#{pc.describe.inspect}#{pc.is_preferred_identifier ? ' (PI)' : ''}"
|
697
726
|
end
|
@@ -706,7 +735,8 @@ module ActiveFacts
|
|
706
735
|
trace :relational_paths?, "Adding #{new_pcs.size} new indices for presence constraints on #{mapping.inspect}" do
|
707
736
|
new_pcs.each do |pc|
|
708
737
|
newpaths[pc] = index = @constellation.Index(:new, composite: mapping.root, is_unique: true, presence_constraint: pc)
|
709
|
-
|
738
|
+
identified_object = mapping.is_partitioned_here ? mapping.child_role.object_type : mapping.root.mapping.object_type
|
739
|
+
if identified_object.preferred_identifier == pc and
|
710
740
|
!@composition.all_full_absorption[mapping.object_type] and # REVISIT: This clause might now be unnecessary
|
711
741
|
!mapping.root.natural_index
|
712
742
|
mapping.root.natural_index = index
|
@@ -736,14 +766,14 @@ module ActiveFacts
|
|
736
766
|
rel
|
737
767
|
end
|
738
768
|
|
739
|
-
def augment_paths paths, mapping
|
740
|
-
return unless MM::
|
769
|
+
def augment_paths is_primary, paths, mapping
|
770
|
+
return unless !(MM::Mapping === mapping) || MM::ValueType === mapping.object_type
|
741
771
|
|
742
772
|
if MM::ValueField === mapping && mapping.parent.composite # ValueType that's a composite (table) by itself
|
743
773
|
# This AccessPath has exactly one field and no presence constraint, so just make the index.
|
744
774
|
composite = mapping.parent.composite
|
745
775
|
paths[nil] =
|
746
|
-
index = @constellation.Index(:new, composite: mapping.root, is_unique: true,
|
776
|
+
index = @constellation.Index(:new, composite: mapping.root, is_unique: true, composite_as_natural_index: composite)
|
747
777
|
composite.primary_index ||= index
|
748
778
|
end
|
749
779
|
|
@@ -751,8 +781,13 @@ module ActiveFacts
|
|
751
781
|
trace :relational_paths, "Adding #{mapping.inspect} to #{path.inspect}" do
|
752
782
|
case path
|
753
783
|
when MM::Index
|
784
|
+
# If we're using hash surrogates, refuse to include a surrogate in the natural index:
|
785
|
+
next if @option_fk == :hash && (!!is_primary == (mapping.root.natural_index == path))
|
754
786
|
@constellation.IndexField(access_path: path, ordinal: path.all_index_field.size, component: mapping)
|
755
787
|
when MM::ForeignKey
|
788
|
+
# If we're using hash surrogates, foreign keys only contain surrogates.
|
789
|
+
# REVISIT: What if not all FK target tables have a surrogate? Answer: they do because they must.
|
790
|
+
next if @option_fk == :hash && !is_primary
|
756
791
|
@constellation.ForeignKeyField(foreign_key: path, ordinal: path.all_foreign_key_field.size, component: mapping)
|
757
792
|
end
|
758
793
|
end
|
@@ -766,14 +801,14 @@ module ActiveFacts
|
|
766
801
|
next if MM::Index === path
|
767
802
|
|
768
803
|
next if path.all_foreign_key_field.size == path.all_index_field.size
|
769
|
-
target_object_type = path.
|
804
|
+
target_object_type = path.mapping.object_type
|
770
805
|
while fa = target_object_type.all_full_absorption[@composition]
|
771
|
-
target_object_type = fa.
|
806
|
+
target_object_type = fa.mapping.parent.object_type
|
772
807
|
end
|
773
808
|
target = @composites[target_object_type]
|
774
|
-
|
809
|
+
fk_type = preferred_fk_type(false, composite, target)
|
775
810
|
trace :relational_paths, "Completing #{path.inspect} to #{target.mapping.inspect}"
|
776
|
-
index = (
|
811
|
+
index = (fk_type == :natural && target.natural_index) || target.primary_index
|
777
812
|
if index
|
778
813
|
index.all_index_field.each do |index_field|
|
779
814
|
@constellation.IndexField access_path: path, ordinal: index_field.ordinal, component: index_field.component
|
@@ -816,13 +851,21 @@ module ActiveFacts
|
|
816
851
|
|
817
852
|
# References from us are things we can own (non-Mappings) or have a unique forward absorption for
|
818
853
|
def references_from
|
819
|
-
@mapping.all_member.select
|
854
|
+
@mapping.all_member.select do |m|
|
855
|
+
!m.is_a?(MM::Absorption) or
|
856
|
+
!m.forward_mapping && m.parent_role.is_unique # A forward absorption has no forward absorption
|
857
|
+
end
|
820
858
|
end
|
821
859
|
alias_method :rf, :references_from
|
822
860
|
|
823
861
|
# References to us are reverse absorptions where the forward absorption can absorb us
|
824
862
|
def references_to
|
825
|
-
|
863
|
+
# REVISIT: If some other object has a Mapping to us, that should be in this list
|
864
|
+
@mapping.all_member.select do |m|
|
865
|
+
m.is_a?(MM::Absorption) and
|
866
|
+
f = m.forward_mapping and # This Absorption has a forward counterpart, so must be reverse
|
867
|
+
f.parent_role.is_unique
|
868
|
+
end
|
826
869
|
end
|
827
870
|
alias_method :rt, :references_to
|
828
871
|
|
@@ -892,10 +935,10 @@ module ActiveFacts
|
|
892
935
|
|
893
936
|
absorbing_ref = mapping.all_member.detect{|m| m.is_a?(MM::Absorption) && m.child_role.fact_type == fact_type}
|
894
937
|
|
895
|
-
absorbing_ref = absorbing_ref.flip! if absorbing_ref.
|
896
|
-
absorbing_ref = absorbing_ref.
|
938
|
+
absorbing_ref = absorbing_ref.flip! if absorbing_ref.reverse_mapping # We were forward, but the other end must be
|
939
|
+
absorbing_ref = absorbing_ref.forward_mapping
|
897
940
|
self.full_absorption =
|
898
|
-
o.constellation.FullAbsorption(composition: composition,
|
941
|
+
o.constellation.FullAbsorption(composition: composition, mapping: absorbing_ref, object_type: o)
|
899
942
|
trace :relational_defaults, "Supertype #{fact_type.supertype_role.name} fully absorbs subtype #{o.name} via #{absorbing_ref.inspect}"
|
900
943
|
definitely_not_table
|
901
944
|
return
|