activefacts 1.6.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +60 -0
- data/Rakefile +3 -80
- data/activefacts.gemspec +36 -0
- data/bin/afgen +4 -2
- data/bin/cql +5 -1
- data/lib/activefacts.rb +3 -12
- data/lib/activefacts/{vocabulary/query_evaluator.rb → query/evaluator.rb} +0 -0
- data/lib/activefacts/version.rb +2 -2
- metadata +48 -296
- data/History.txt +0 -4
- data/LICENSE +0 -19
- data/Manifest.txt +0 -165
- data/README.rdoc +0 -81
- data/css/offline.css +0 -3
- data/css/orm2.css +0 -124
- data/css/print.css +0 -8
- data/css/style-print.css +0 -357
- data/css/style.css +0 -387
- data/download.html +0 -110
- data/examples/CQL/Address.cql +0 -44
- data/examples/CQL/Blog.cql +0 -54
- data/examples/CQL/CompanyDirectorEmployee.cql +0 -56
- data/examples/CQL/Death.cql +0 -17
- data/examples/CQL/Diplomacy.cql +0 -48
- data/examples/CQL/Genealogy.cql +0 -98
- data/examples/CQL/Insurance.cql +0 -320
- data/examples/CQL/Marriage.cql +0 -18
- data/examples/CQL/Metamodel.cql +0 -493
- data/examples/CQL/Monogamy.cql +0 -24
- data/examples/CQL/MultiInheritance.cql +0 -22
- data/examples/CQL/NonRoleId.cql +0 -14
- data/examples/CQL/OddIdentifier.cql +0 -18
- data/examples/CQL/OilSupply.cql +0 -53
- data/examples/CQL/OneToOnes.cql +0 -17
- data/examples/CQL/Orienteering.cql +0 -111
- data/examples/CQL/PersonPlaysGame.cql +0 -18
- data/examples/CQL/RedundantDependency.cql +0 -34
- data/examples/CQL/SchoolActivities.cql +0 -33
- data/examples/CQL/SeparateSubtype.cql +0 -30
- data/examples/CQL/ServiceDirector.cql +0 -276
- data/examples/CQL/SimplestUnary.cql +0 -12
- data/examples/CQL/Supervision.cql +0 -34
- data/examples/CQL/WaiterTips.cql +0 -33
- data/examples/CQL/Warehousing.cql +0 -101
- data/examples/CQL/WindowInRoomInBldg.cql +0 -28
- data/examples/CQL/unit.cql +0 -474
- data/examples/index.html +0 -420
- data/examples/intro.html +0 -327
- data/examples/local.css +0 -24
- data/index.html +0 -111
- data/lib/activefacts/cql.rb +0 -35
- data/lib/activefacts/cql/CQLParser.treetop +0 -158
- data/lib/activefacts/cql/Context.treetop +0 -48
- data/lib/activefacts/cql/Expressions.treetop +0 -67
- data/lib/activefacts/cql/FactTypes.treetop +0 -358
- data/lib/activefacts/cql/Language/English.treetop +0 -315
- data/lib/activefacts/cql/LexicalRules.treetop +0 -253
- data/lib/activefacts/cql/ObjectTypes.treetop +0 -210
- data/lib/activefacts/cql/Rakefile +0 -14
- data/lib/activefacts/cql/Terms.treetop +0 -183
- data/lib/activefacts/cql/ValueTypes.treetop +0 -202
- data/lib/activefacts/cql/compiler.rb +0 -156
- data/lib/activefacts/cql/compiler/clause.rb +0 -1137
- data/lib/activefacts/cql/compiler/constraint.rb +0 -581
- data/lib/activefacts/cql/compiler/entity_type.rb +0 -457
- data/lib/activefacts/cql/compiler/expression.rb +0 -443
- data/lib/activefacts/cql/compiler/fact.rb +0 -390
- data/lib/activefacts/cql/compiler/fact_type.rb +0 -421
- data/lib/activefacts/cql/compiler/query.rb +0 -106
- data/lib/activefacts/cql/compiler/shared.rb +0 -161
- data/lib/activefacts/cql/compiler/value_type.rb +0 -174
- data/lib/activefacts/cql/nodes.rb +0 -49
- data/lib/activefacts/cql/parser.rb +0 -241
- data/lib/activefacts/dependency_analyser.rb +0 -182
- data/lib/activefacts/generate/absorption.rb +0 -70
- data/lib/activefacts/generate/composition.rb +0 -118
- data/lib/activefacts/generate/cql.rb +0 -714
- data/lib/activefacts/generate/dm.rb +0 -279
- data/lib/activefacts/generate/help.rb +0 -64
- data/lib/activefacts/generate/helpers/inject.rb +0 -16
- data/lib/activefacts/generate/helpers/oo.rb +0 -162
- data/lib/activefacts/generate/helpers/ordered.rb +0 -605
- data/lib/activefacts/generate/helpers/rails.rb +0 -57
- data/lib/activefacts/generate/html/glossary.rb +0 -461
- data/lib/activefacts/generate/json.rb +0 -337
- data/lib/activefacts/generate/null.rb +0 -32
- data/lib/activefacts/generate/rails/models.rb +0 -246
- data/lib/activefacts/generate/rails/schema.rb +0 -216
- data/lib/activefacts/generate/records.rb +0 -46
- data/lib/activefacts/generate/ruby.rb +0 -133
- data/lib/activefacts/generate/sql/mysql.rb +0 -280
- data/lib/activefacts/generate/sql/server.rb +0 -273
- data/lib/activefacts/generate/stats.rb +0 -69
- data/lib/activefacts/generate/text.rb +0 -27
- data/lib/activefacts/generate/topics.rb +0 -265
- data/lib/activefacts/generate/traits/datavault.rb +0 -241
- data/lib/activefacts/generate/traits/oo.rb +0 -73
- data/lib/activefacts/generate/traits/ordered.rb +0 -33
- data/lib/activefacts/generate/traits/ruby.rb +0 -210
- data/lib/activefacts/generate/transform/datavault.rb +0 -266
- data/lib/activefacts/generate/transform/surrogate.rb +0 -214
- data/lib/activefacts/generate/version.rb +0 -26
- data/lib/activefacts/input/cql.rb +0 -43
- data/lib/activefacts/input/orm.rb +0 -1636
- data/lib/activefacts/mapping/rails.rb +0 -132
- data/lib/activefacts/persistence.rb +0 -15
- data/lib/activefacts/persistence/columns.rb +0 -446
- data/lib/activefacts/persistence/foreignkey.rb +0 -187
- data/lib/activefacts/persistence/index.rb +0 -240
- data/lib/activefacts/persistence/object_type.rb +0 -198
- data/lib/activefacts/persistence/reference.rb +0 -434
- data/lib/activefacts/persistence/tables.rb +0 -380
- data/lib/activefacts/registry.rb +0 -11
- data/lib/activefacts/support.rb +0 -132
- data/lib/activefacts/vocabulary.rb +0 -9
- data/lib/activefacts/vocabulary/extensions.rb +0 -1348
- data/lib/activefacts/vocabulary/metamodel.rb +0 -570
- data/lib/activefacts/vocabulary/verbaliser.rb +0 -804
- data/script/txt2html +0 -71
- data/spec/absorption_spec.rb +0 -95
- data/spec/cql/comparison_spec.rb +0 -89
- data/spec/cql/context_spec.rb +0 -94
- data/spec/cql/contractions_spec.rb +0 -224
- data/spec/cql/deontic_spec.rb +0 -88
- data/spec/cql/entity_type_spec.rb +0 -320
- data/spec/cql/expressions_spec.rb +0 -66
- data/spec/cql/fact_type_matching_spec.rb +0 -338
- data/spec/cql/french_spec.rb +0 -21
- data/spec/cql/parser/bad_literals_spec.rb +0 -86
- data/spec/cql/parser/constraints_spec.rb +0 -19
- data/spec/cql/parser/entity_types_spec.rb +0 -106
- data/spec/cql/parser/expressions_spec.rb +0 -199
- data/spec/cql/parser/fact_types_spec.rb +0 -44
- data/spec/cql/parser/literals_spec.rb +0 -312
- data/spec/cql/parser/pragmas_spec.rb +0 -89
- data/spec/cql/parser/value_types_spec.rb +0 -42
- data/spec/cql/role_matching_spec.rb +0 -148
- data/spec/cql/samples_spec.rb +0 -244
- data/spec/cql_cql_spec.rb +0 -73
- data/spec/cql_dm_spec.rb +0 -136
- data/spec/cql_mysql_spec.rb +0 -69
- data/spec/cql_parse_spec.rb +0 -34
- data/spec/cql_ruby_spec.rb +0 -73
- data/spec/cql_sql_spec.rb +0 -72
- data/spec/cql_symbol_tables_spec.rb +0 -261
- data/spec/cqldump_spec.rb +0 -170
- data/spec/helpers/array_matcher.rb +0 -23
- data/spec/helpers/ctrl_c_support.rb +0 -52
- data/spec/helpers/diff_matcher.rb +0 -39
- data/spec/helpers/file_matcher.rb +0 -34
- data/spec/helpers/parse_to_ast_matcher.rb +0 -80
- data/spec/helpers/string_matcher.rb +0 -30
- data/spec/helpers/test_parser.rb +0 -15
- data/spec/norma_cql_spec.rb +0 -66
- data/spec/norma_ruby_spec.rb +0 -62
- data/spec/norma_ruby_sql_spec.rb +0 -107
- data/spec/norma_sql_spec.rb +0 -57
- data/spec/norma_tables_spec.rb +0 -95
- data/spec/ruby_api_spec.rb +0 -23
- data/spec/spec_helper.rb +0 -35
- data/spec/transform_surrogate_spec.rb +0 -59
- data/status.html +0 -138
- data/why.html +0 -60
@@ -1,187 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# ActiveFacts Relational mapping and persistence.
|
3
|
-
# A ForeignKey exists for every Reference from a ObjectType to another ObjectType that's a table.
|
4
|
-
#
|
5
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
-
#
|
7
|
-
module ActiveFacts
|
8
|
-
module Persistence
|
9
|
-
class ForeignKey
|
10
|
-
# What table (ObjectType) is the FK from?
|
11
|
-
def from; @from; end
|
12
|
-
|
13
|
-
# What table (ObjectType) is the FK to?
|
14
|
-
def to; @to; end
|
15
|
-
|
16
|
-
# What reference created the FK?
|
17
|
-
def references; @references; end
|
18
|
-
|
19
|
-
# What columns in the *from* table form the FK
|
20
|
-
def from_columns; @from_columns; end
|
21
|
-
|
22
|
-
# What columns in the *to* table form the identifier
|
23
|
-
def to_columns; @to_columns; end
|
24
|
-
|
25
|
-
def initialize(from, to, references, from_columns, to_columns) #:nodoc:
|
26
|
-
@from, @to, @references, @from_columns, @to_columns =
|
27
|
-
from, to, references, from_columns, to_columns
|
28
|
-
end
|
29
|
-
|
30
|
-
def describe
|
31
|
-
"foreign key from #{from.name}(#{from_columns.map{|c| c.name}*', '}) to #{to.name}(#{to_columns.map{|c| c.name}*', '})"
|
32
|
-
end
|
33
|
-
|
34
|
-
def verbalised_path reverse = false
|
35
|
-
# REVISIT: This should be a proper join path verbalisation:
|
36
|
-
refs = reverse ? references.reverse : references
|
37
|
-
refs.map do |r|
|
38
|
-
r.verbalised_path reverse
|
39
|
-
end * ' and '
|
40
|
-
end
|
41
|
-
|
42
|
-
# Which references are absorbed into the "from" table?
|
43
|
-
def precursor_references
|
44
|
-
fk_jump = @references.detect(&:fk_jump)
|
45
|
-
jump_index = @references.index(fk_jump)
|
46
|
-
@references[0, jump_index]
|
47
|
-
end
|
48
|
-
|
49
|
-
# Which references are absorbed into the "to" table?
|
50
|
-
def following_references
|
51
|
-
fk_jump = @references.detect(&:fk_jump)
|
52
|
-
jump_index = @references.index(fk_jump)
|
53
|
-
fk_jump != @references.last ? @references[jump_index+1..-1] : []
|
54
|
-
end
|
55
|
-
|
56
|
-
def jump_reference
|
57
|
-
@references.detect(&:fk_jump)
|
58
|
-
end
|
59
|
-
|
60
|
-
def to_name
|
61
|
-
p = precursor_references
|
62
|
-
f = following_references
|
63
|
-
j = jump_reference
|
64
|
-
|
65
|
-
@references.last.to_names +
|
66
|
-
(p.empty? && f.empty? ? [] : ['via'] + p.map{|r| r.to_names}.flatten + f.map{|r| r.from_names}.flatten)
|
67
|
-
end
|
68
|
-
|
69
|
-
# The from_name is the role name of the table with the FK, viewed from the other end
|
70
|
-
# When there are no precursor_references or following_references, it's the jump_reference.from_names
|
71
|
-
# REVISIT: I'm still working out what to do with precursor_references and following_references
|
72
|
-
def from_name
|
73
|
-
p = precursor_references
|
74
|
-
f = following_references
|
75
|
-
j = jump_reference
|
76
|
-
|
77
|
-
# pluralise unless j.is_one_to_one
|
78
|
-
|
79
|
-
# REVISIT: references[0].from_names is where the FK lives; but the object of interest may be an absorbed subclass which we should use here instead:
|
80
|
-
# REVISIT: Should crunch superclasses in subtype traversals
|
81
|
-
# REVISIT: Need to add "_as_rolename" where rolename is not to.name
|
82
|
-
|
83
|
-
[
|
84
|
-
@references[0].from_names,
|
85
|
-
(p.empty? && f.empty? ? [] : ['via'] + p.map{|r| r.to_names}.flatten + f.map{|r| r.from_names}.flatten)
|
86
|
-
]
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
module Metamodel #:nodoc:
|
93
|
-
class ObjectType
|
94
|
-
# When an EntityType is fully absorbed, its foreign keys are too.
|
95
|
-
# Return an Array of Reference paths for such absorbed FKs
|
96
|
-
def all_absorbed_foreign_key_reference_path
|
97
|
-
references_from.inject([]) do |array, ref|
|
98
|
-
if ref.is_simple_reference
|
99
|
-
if TypeInheritance === ref.fact_type
|
100
|
-
# Ignore references to secondary supertypes, when absorption is through primary.
|
101
|
-
next array if absorbed_via && TypeInheritance === absorbed_via.fact_type
|
102
|
-
# Ignore the case where a subtype is absorbed elsewhere:
|
103
|
-
# REVISIT: Disabled, as this should never happen.
|
104
|
-
# next array if ref.to.absorbed_via != ref.fact_type
|
105
|
-
end
|
106
|
-
ref.fk_jump = true
|
107
|
-
array << [ref]
|
108
|
-
elsif ref.is_absorbing or (ref.to && !ref.to.is_table)
|
109
|
-
trace :fk, "getting fks absorbed into #{name} via #{ref}" do
|
110
|
-
ref.to.all_absorbed_foreign_key_reference_path.each do |aref|
|
111
|
-
array << aref.insert(0, ref)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
array
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def foreign_keys_to
|
120
|
-
@foreign_keys_to ||= []
|
121
|
-
end
|
122
|
-
|
123
|
-
# Return an array of all the foreign keys from this table
|
124
|
-
def foreign_keys
|
125
|
-
|
126
|
-
# Get the ForeignKey object for each absorbed reference path
|
127
|
-
@foreign_keys ||=
|
128
|
-
begin
|
129
|
-
fk_ref_paths = all_absorbed_foreign_key_reference_path
|
130
|
-
fk_ref_paths.map do |fk_ref_path|
|
131
|
-
trace :fk, "\nFK: " + fk_ref_path.map{|fk_ref| fk_ref.reading }*" and " do
|
132
|
-
|
133
|
-
from_columns = (columns||all_columns({})).select{|column|
|
134
|
-
column.references[0...fk_ref_path.size] == fk_ref_path
|
135
|
-
}
|
136
|
-
trace :fk, "from_columns = #{from_columns.map { |column| column.name }*", "}"
|
137
|
-
|
138
|
-
# Figure out absorption on the target end:
|
139
|
-
to = fk_ref_path.last.to
|
140
|
-
if to.absorbed_via
|
141
|
-
trace :fk, "Reference target #{fk_ref_path.last.to.name} is absorbed via:" do
|
142
|
-
while (r = to.absorbed_via)
|
143
|
-
m = r.reversed
|
144
|
-
trace :fk, "#{m.reading}"
|
145
|
-
fk_ref_path << m
|
146
|
-
to = m.from == to ? m.to : m.from
|
147
|
-
end
|
148
|
-
trace :fk, "Absorption ends at #{to.name}"
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# REVISIT: This test may no longer be necessary
|
153
|
-
raise "REVISIT: #{fk_ref_path.inspect} is bad" unless to and to.columns
|
154
|
-
|
155
|
-
# REVISIT: This fails for absorbed subtypes having their own identification.
|
156
|
-
# Check the CompanyDirectorEmployee model for example, EmployeeManagerNr -> Person (should reference EmployeeNr)
|
157
|
-
# Need to use the absorbed identifier_columns of the subtype,
|
158
|
-
# not the columns of the supertype that absorbs it.
|
159
|
-
# But in general, that isn't going to work because in most DBMS
|
160
|
-
# there's no suitable uniquen index on the subtype's identifier_columns
|
161
|
-
|
162
|
-
to_columns = fk_ref_path[-1].to.identifier_columns
|
163
|
-
|
164
|
-
# Put the column pairs in the correct order. They MUST be in the order they appear in the primary key
|
165
|
-
froms, tos = from_columns.zip(to_columns).sort_by { |pair|
|
166
|
-
to_columns.index(pair[1])
|
167
|
-
}.transpose
|
168
|
-
|
169
|
-
fk = ActiveFacts::Persistence::ForeignKey.new(self, to, fk_ref_path, froms, tos)
|
170
|
-
to.foreign_keys_to << fk
|
171
|
-
fk
|
172
|
-
end
|
173
|
-
end.
|
174
|
-
sort_by do |fk|
|
175
|
-
# Put the foreign keys in a defined order:
|
176
|
-
# debugger if !fk.to_columns || fk.to_columns.include?(nil) || !fk.from_columns || fk.from_columns.include?(nil)
|
177
|
-
[ fk.to.name,
|
178
|
-
fk.to_columns.map{|col| col.name(nil).sort},
|
179
|
-
fk.from_columns.map{|col| col.name(nil).sort}
|
180
|
-
]
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
@@ -1,240 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# ActiveFacts Relational mapping and persistence.
|
3
|
-
# An Index on a ObjectType is used to represent a unique constraint across roles absorbed
|
4
|
-
# into that object_type's table.
|
5
|
-
#
|
6
|
-
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
7
|
-
#
|
8
|
-
|
9
|
-
module ActiveFacts
|
10
|
-
module Persistence
|
11
|
-
class Index
|
12
|
-
# The UniquenessConstraint that created this index
|
13
|
-
def uniqueness_constraint; @uniqueness_constraint; end
|
14
|
-
|
15
|
-
# The table that the index is on
|
16
|
-
def on; @on; end
|
17
|
-
|
18
|
-
# If a non-mandatory reference was absorbed, only the non-nil instances are unique.
|
19
|
-
# Return the ObjectType that was absorbed, which might differ from this Index's table.
|
20
|
-
def over; @over; end
|
21
|
-
|
22
|
-
# Return the array of columns in this index
|
23
|
-
def columns; @columns; end
|
24
|
-
|
25
|
-
# Is this index the primary key for this table?
|
26
|
-
def is_primary; @is_primary; end
|
27
|
-
|
28
|
-
# Is this index unique?
|
29
|
-
def is_unique; @is_unique; end
|
30
|
-
|
31
|
-
# An Index arises from a uniqueness constraint and applies to a table,
|
32
|
-
# but because the UC may actually be over an object absorbed into the table,
|
33
|
-
# we must record that object also.
|
34
|
-
# We record the columns it's over, whether it's primary (for 'over'),
|
35
|
-
# and whether it's unique (always, at present)
|
36
|
-
def initialize(uc, on, over, columns, is_primary, is_unique = true) #:nodoc:
|
37
|
-
@uniqueness_constraint, @on, @over, @columns, @is_primary, @is_unique =
|
38
|
-
uc, on, over, columns, is_primary, is_unique
|
39
|
-
end
|
40
|
-
|
41
|
-
# The name that was assigned (perhaps implicitly by NORMA)
|
42
|
-
def real_name
|
43
|
-
@uniqueness_constraint.name && @uniqueness_constraint.name != '' ? @uniqueness_constraint.name.gsub(' ','') : nil
|
44
|
-
end
|
45
|
-
|
46
|
-
# This name is either the name explicitly assigned (if any) or is constructed to form a unique index name.
|
47
|
-
def name
|
48
|
-
uc = @uniqueness_constraint
|
49
|
-
r = real_name
|
50
|
-
return r if r && r !~ /^(Ex|In)ternalUniquenessConstraint[0-9]+$/
|
51
|
-
(uc.is_preferred_identifier ? "PK_" : "IX_") +
|
52
|
-
view_name +
|
53
|
-
(uc.is_preferred_identifier ? "" : "By"+column_names*"")
|
54
|
-
end
|
55
|
-
|
56
|
-
# An array of the names of the columns this index covers
|
57
|
-
def column_names(separator = "")
|
58
|
-
columns.map{|column| column.name(separator)}
|
59
|
-
end
|
60
|
-
|
61
|
-
# An array of the names of the columns this index covers, with some lexical truncations.
|
62
|
-
def abbreviated_column_names(separator = "")
|
63
|
-
columns.map{|column| column.name(separator).sub(/^#{over.name}/,'')}
|
64
|
-
end
|
65
|
-
|
66
|
-
# The name of a view that can be created to enforce uniqueness over non-null key values
|
67
|
-
def view_name
|
68
|
-
"#{over.name.gsub(' ','')}#{on == over ? "" : "In"+on.name.gsub(' ','')}"
|
69
|
-
end
|
70
|
-
|
71
|
-
def to_s #:nodoc:
|
72
|
-
if @uniqueness_constraint
|
73
|
-
name = @uniqueness_constraint.name
|
74
|
-
preferred = @uniqueness_constraint.is_preferred_identifier ? " (preferred)" : ""
|
75
|
-
else
|
76
|
-
name = "#{@on.name}IsUnique"
|
77
|
-
preferred = !@on.injected_surrogate_role ? " (preferred)" : ""
|
78
|
-
end
|
79
|
-
colnames = @columns.map(&:name)*", "
|
80
|
-
"Index #{name} on #{@on.name} over #{@over.name}(#{colnames})#{preferred}"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
module Metamodel #:nodoc:
|
86
|
-
class EntityType
|
87
|
-
def self_index
|
88
|
-
nil
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
class ValueType
|
93
|
-
def self_index
|
94
|
-
ActiveFacts::Persistence::Index.new(
|
95
|
-
nil, # The implied uniqueness constraint is not created
|
96
|
-
self, # ValueType being indexed
|
97
|
-
self, # Absorbed object being indexed
|
98
|
-
columns.select{|c| c.references[0].is_self_value},
|
99
|
-
injected_surrogate_role ? false : true
|
100
|
-
)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
class ObjectType
|
105
|
-
# An array of each Index for this table
|
106
|
-
def indices
|
107
|
-
@indices || populate_indices
|
108
|
-
end
|
109
|
-
|
110
|
-
def clear_indices #:nodoc:
|
111
|
-
# Clear any previous indices
|
112
|
-
@indices = nil
|
113
|
-
end
|
114
|
-
|
115
|
-
def populate_indices #:nodoc:
|
116
|
-
# The absorption path of a column indicates how it came to be in this table.
|
117
|
-
# It might be a direct many:one valuetype relationship, or it might be in such
|
118
|
-
# a relationship to an entity that was absorbed into this table (and so on).
|
119
|
-
# The reference path is the set of absorption references and one past it.
|
120
|
-
# Stopping here means we don't dig into the definitions of FK column counterparts.
|
121
|
-
# Note that many columns of an object may have the same ref_path.
|
122
|
-
#
|
123
|
-
# REVISIT:
|
124
|
-
# Note also that this produces columns ordered for each refpath the same as the
|
125
|
-
# order of the columns, not the same as the columns in the PK for which they might be an FK.
|
126
|
-
all_column_by_ref_path =
|
127
|
-
trace :index2, "Indexing columns by ref_path" do
|
128
|
-
columns.inject({}) do |hash, column|
|
129
|
-
trace :index2, "References in column #{name}.#{column.name}" do
|
130
|
-
ref_path = column.absorption_references
|
131
|
-
raise "No absorption_references for #{column.name} from #{column.references.map(&:to_s)*" and "}" if !ref_path || ref_path.empty?
|
132
|
-
(hash[ref_path] ||= []) << column
|
133
|
-
trace :index2, "#{column.name} involves #{ref_path.map(&:to_s)*" and "}"
|
134
|
-
end
|
135
|
-
hash
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
columns_by_unique_constraint = {}
|
140
|
-
all_column_by_role_ref =
|
141
|
-
all_column_by_ref_path.
|
142
|
-
keys. # Go through all refpaths and find uniqueness constraints
|
143
|
-
inject({}) do |hash, ref_path|
|
144
|
-
ref_path.each do |ref|
|
145
|
-
next unless ref.to_role
|
146
|
-
# trace :index2, "Considering #{ref_path.map(&:to_s)*" and "} yielding columns #{all_column_by_ref_path[ref_path].map{|c| c.name('.')}*", "}"
|
147
|
-
ref.to_role.all_role_ref.each do |role_ref|
|
148
|
-
all_pcs = role_ref.role_sequence.all_presence_constraint
|
149
|
-
# puts "pcs over #{ref_path.map{|r| r.to_names}.flatten*'.'}: #{role_ref.role_sequence.all_presence_constraint.map(&:describe)*"; "}" if all_pcs.size > 0
|
150
|
-
pcs = all_pcs.
|
151
|
-
reject do |pc|
|
152
|
-
!pc.max_frequency or # No maximum freq; cannot be a uniqueness constraint
|
153
|
-
pc.max_frequency != 1 or # maximum is not 1
|
154
|
-
# Constraint is not over a unary fact type role (NORMA does this)
|
155
|
-
pc.role_sequence.all_role_ref.size == 1 && ref_path[-1].to_role.fact_type.all_role.size == 1
|
156
|
-
end
|
157
|
-
next unless pcs.size > 0
|
158
|
-
# The columns for this ref_path support the UCs in "pcs".
|
159
|
-
pcs.each do |pc|
|
160
|
-
ref_columns = all_column_by_ref_path[ref_path]
|
161
|
-
ordinal = role_ref.ordinal # Position in priority order
|
162
|
-
ref_columns.each_with_index do |column, index|
|
163
|
-
#puts "Adding index column #{column.name} in rank[#{ordinal},#{index}]"
|
164
|
-
# REVISIT: the "index" here might be a duplicate in some cases: change sort_by below to just sort and run the SeparateSubtypes CQL model for example.
|
165
|
-
(columns_by_unique_constraint[pc] ||= []) << [ordinal, index, column]
|
166
|
-
end
|
167
|
-
end
|
168
|
-
hash[role_ref] = all_column_by_ref_path[ref_path]
|
169
|
-
end
|
170
|
-
end
|
171
|
-
hash
|
172
|
-
end
|
173
|
-
|
174
|
-
trace :index, "All Indices in #{name}:" do
|
175
|
-
@indices = columns_by_unique_constraint.map do |uc, columns_with_ordinal|
|
176
|
-
trace :index, "Index due to uc #{uc.concept.guid} on #{name} over (#{columns_with_ordinal.sort_by{|onc|onc[0]}.map{|ca| ca[2].name}.inspect})"
|
177
|
-
columns = columns_with_ordinal.sort_by{|ca| [ca[0,2], ca[2].name]}.map{|ca| ca[2]}
|
178
|
-
absorption_level = columns.map(&:absorption_level).min
|
179
|
-
over = columns[0].references[absorption_level].from
|
180
|
-
|
181
|
-
# Absorption through a one-to-one forms a UC that we don't need to enforce using an index:
|
182
|
-
if over != self and
|
183
|
-
over.absorbed_via == columns[0].references[absorption_level-1] and
|
184
|
-
(rr = uc.role_sequence.all_role_ref.single) and
|
185
|
-
over.absorbed_via.fact_type.all_role.include?(rr.role)
|
186
|
-
next nil
|
187
|
-
end
|
188
|
-
|
189
|
-
index = ActiveFacts::Persistence::Index.new(
|
190
|
-
uc,
|
191
|
-
self,
|
192
|
-
over,
|
193
|
-
columns,
|
194
|
-
uc.is_preferred_identifier
|
195
|
-
)
|
196
|
-
trace :index, index
|
197
|
-
index
|
198
|
-
end.
|
199
|
-
compact.
|
200
|
-
sort_by do |index|
|
201
|
-
# Put the indices in a defined order:
|
202
|
-
index.columns.map(&:name)+['', index.over.name]
|
203
|
-
end
|
204
|
-
end
|
205
|
-
si = self_index
|
206
|
-
@indices.unshift(si) if si
|
207
|
-
@indices
|
208
|
-
end
|
209
|
-
|
210
|
-
end
|
211
|
-
|
212
|
-
class Vocabulary
|
213
|
-
def populate_all_indices #:nodoc:
|
214
|
-
trace :index, "Populating all object_type indices" do
|
215
|
-
all_object_type.each do |object_type|
|
216
|
-
object_type.clear_indices
|
217
|
-
end
|
218
|
-
all_object_type.each do |object_type|
|
219
|
-
next unless object_type.is_table
|
220
|
-
trace :index, "Populating indices for #{object_type.name}" do
|
221
|
-
object_type.populate_indices
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
trace :index, "Finished object_type indices" do
|
226
|
-
all_object_type.each do |object_type|
|
227
|
-
next unless object_type.is_table
|
228
|
-
next unless object_type.indices.size > 0
|
229
|
-
trace :index, "#{object_type.name}:" do
|
230
|
-
object_type.indices.each do |index|
|
231
|
-
trace :index, index
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
end
|
240
|
-
end
|
@@ -1,198 +0,0 @@
|
|
1
|
-
require 'activefacts/support'
|
2
|
-
|
3
|
-
module ActiveFacts
|
4
|
-
module API
|
5
|
-
module ObjectType
|
6
|
-
def table
|
7
|
-
@is_table = true
|
8
|
-
end
|
9
|
-
|
10
|
-
def is_table
|
11
|
-
@is_table
|
12
|
-
end
|
13
|
-
|
14
|
-
def columns
|
15
|
-
raise "This method is no longer in use"
|
16
|
-
=begin
|
17
|
-
return @columns if @columns
|
18
|
-
trace :persistence, "Calculating columns for #{basename}" do
|
19
|
-
@columns = (
|
20
|
-
if superclass.is_entity_type
|
21
|
-
# REVISIT: Need keys to secondary supertypes as well, but no duplicates.
|
22
|
-
trace :persistence, "Separate subtype has a foreign key to its supertype" do
|
23
|
-
superclass.__absorb([[superclass.basename]], self)
|
24
|
-
end
|
25
|
-
else
|
26
|
-
[]
|
27
|
-
end +
|
28
|
-
# Then absorb all normal roles:
|
29
|
-
roles.values.select do |role|
|
30
|
-
role.unique && !role.counterpart_unary_has_precedence
|
31
|
-
end.inject([]) do |columns, role|
|
32
|
-
rn = role.name.to_s.split(/_/)
|
33
|
-
trace :persistence, "Role #{rn*'.'}" do
|
34
|
-
columns += role.counterpart_object_type.__absorb([rn], role.counterpart)
|
35
|
-
end
|
36
|
-
end +
|
37
|
-
# And finally all absorbed subtypes:
|
38
|
-
subtypes.
|
39
|
-
select{|subtype| !subtype.is_table}. # Don't absorb separate subtypes
|
40
|
-
inject([]) do |columns, subtype|
|
41
|
-
# Pass self as 2nd param here, not a role, standing for the supertype role
|
42
|
-
subtype_name = subtype.basename
|
43
|
-
trace :persistence, "Absorbing subtype #{subtype_name}" do
|
44
|
-
columns += subtype.__absorb([[subtype_name]], self)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
).map do |col_names|
|
48
|
-
last = nil
|
49
|
-
col_names.flatten.map do |name|
|
50
|
-
name.downcase.sub(/^[a-z]/){|c| c.upcase}
|
51
|
-
end.
|
52
|
-
reject do |n|
|
53
|
-
# Remove sequential duplicates:
|
54
|
-
dup = last == n
|
55
|
-
last = n
|
56
|
-
dup
|
57
|
-
end*"."
|
58
|
-
end
|
59
|
-
end
|
60
|
-
=end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Return an array of the absorbed columns, using prefix for name truncation
|
64
|
-
def __absorb(prefix, except_role = nil)
|
65
|
-
# also considered a table if the superclass isn't excluded and is (transitively) a table
|
66
|
-
if !@is_table && (except_role == superclass || !is_table_subtype)
|
67
|
-
if is_entity_type
|
68
|
-
if (role = fully_absorbed) && role != except_role
|
69
|
-
# If this non-table is fully absorbed into another table (not our caller!)
|
70
|
-
# (another table plays its single identifying role), then absorb that role only.
|
71
|
-
# counterpart_object_type = role.counterpart_object_type
|
72
|
-
# This omission matches the one in columns.rb, see EntityType#reference_columns
|
73
|
-
# new_prefix = prefix + [role.name.to_s.split(/_/)]
|
74
|
-
trace :persistence, "Reference to #{role.name} (absorbed elsewhere)" do
|
75
|
-
role.counterpart_object_type.__absorb(prefix, role.counterpart)
|
76
|
-
end
|
77
|
-
else
|
78
|
-
# Not a table -> all roles are absorbed
|
79
|
-
roles.
|
80
|
-
values.
|
81
|
-
select do |role|
|
82
|
-
role.unique && role != except_role && !role.counterpart_unary_has_precedence
|
83
|
-
end.
|
84
|
-
inject([]) do |columns, role|
|
85
|
-
columns += __absorb_role(prefix, role)
|
86
|
-
end +
|
87
|
-
subtypes. # Absorb subtype roles too!
|
88
|
-
select{|subtype| !subtype.is_table}. # Don't absorb separate subtypes
|
89
|
-
inject([]) do |columns, subtype|
|
90
|
-
# Pass self as 2nd param here, not a role, standing for the supertype role
|
91
|
-
new_prefix = prefix[0..-2] + [[subtype.basename]]
|
92
|
-
trace :persistence, "Absorbed subtype #{subtype.basename}" do
|
93
|
-
columns += subtype.__absorb(new_prefix, self)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
else
|
98
|
-
[prefix]
|
99
|
-
end
|
100
|
-
else
|
101
|
-
# Create a foreign key to the table
|
102
|
-
if is_entity_type
|
103
|
-
ir = identifying_role_names.map{|role_name| roles(role_name) }
|
104
|
-
trace :persistence, "Reference to #{basename} with #{prefix.inspect}" do
|
105
|
-
ic = identifying_role_names.map{|role_name| role_name.to_s.split(/_/)}
|
106
|
-
ir.inject([]) do |columns, role|
|
107
|
-
columns += __absorb_role(prefix, role)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
else
|
111
|
-
# Reference to value type which is a table
|
112
|
-
col = prefix.clone
|
113
|
-
trace :persistence, "Self-value #{col[-1]}.Value"
|
114
|
-
col[-1] += ["Value"]
|
115
|
-
col
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def __absorb_role(prefix, role)
|
121
|
-
if prefix.size > 0 and
|
122
|
-
(c = role.owner).is_entity_type and
|
123
|
-
c.identifying_roles == [role] and
|
124
|
-
(irn = c.identifying_role_names).size == 1 and
|
125
|
-
(n = irn[0].to_s.split(/_/)).size > 1 and
|
126
|
-
(owner = role.owner.basename.snakecase.split(/_/)) and
|
127
|
-
n[0...owner.size] == owner
|
128
|
-
trace :persistence, "truncating transitive identifying role #{n.inspect}"
|
129
|
-
owner.size.times { n.shift }
|
130
|
-
new_prefix = prefix + [n]
|
131
|
-
elsif (c = role.counterpart_object_type).is_entity_type and
|
132
|
-
(irn = c.identifying_role_names).size == 1 and
|
133
|
-
#irn[0].to_s.split(/_/)[0] == role.owner.basename.downcase
|
134
|
-
irn[0] == role.counterpart.name
|
135
|
-
#trace :persistence, "=== #{irn[0].to_s.split(/_/)[0]} elided ==="
|
136
|
-
new_prefix = prefix
|
137
|
-
elsif (fa_role = fully_absorbed) && fa_role == role
|
138
|
-
new_prefix = prefix
|
139
|
-
else
|
140
|
-
new_prefix = prefix + [role.name.to_s.split(/_/)]
|
141
|
-
end
|
142
|
-
#trace :persistence, "new_prefix is #{new_prefix*"."}"
|
143
|
-
|
144
|
-
trace :persistence, "Absorbing role #{role.name} as #{new_prefix[prefix.size..-1]*"."}" do
|
145
|
-
role.counterpart_object_type.__absorb(new_prefix, role.counterpart)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def is_table_subtype
|
150
|
-
return true if is_table
|
151
|
-
klass = superclass
|
152
|
-
while klass.is_entity_type
|
153
|
-
return true if klass.is_table
|
154
|
-
klass = klass.superclass
|
155
|
-
end
|
156
|
-
return false
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
module Entity
|
161
|
-
module ClassMethods
|
162
|
-
def fully_absorbed
|
163
|
-
return false unless (ir = identifying_role_names) && ir.size == 1
|
164
|
-
role = roles(ir[0])
|
165
|
-
return role if ((cp = role.counterpart_object_type).is_table ||
|
166
|
-
(cp.is_entity_type && cp.fully_absorbed))
|
167
|
-
return superclass if superclass.is_entity_type # Absorbed subtype
|
168
|
-
nil
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# A one-to-one can be absorbed into either table. We decide which by comparing
|
174
|
-
# the names, just as happens in ObjectType.populate_reference (see reference.rb)
|
175
|
-
class Role
|
176
|
-
def counterpart_unary_has_precedence
|
177
|
-
counterpart_object_type.is_table_subtype and
|
178
|
-
counterpart.unique and
|
179
|
-
owner.name.downcase < counterpart.owner.name.downcase
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
class TrueClass
|
187
|
-
def self.__absorb(prefix, except_role = nil)
|
188
|
-
[prefix]
|
189
|
-
end
|
190
|
-
|
191
|
-
def self.is_table
|
192
|
-
false
|
193
|
-
end
|
194
|
-
|
195
|
-
def self.is_table_subtype
|
196
|
-
false
|
197
|
-
end
|
198
|
-
end
|