activefacts-compositions 1.9.5 → 1.9.6

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.
@@ -13,8 +13,6 @@ require "activefacts/compositions"
13
13
  module ActiveFacts
14
14
  module Compositions
15
15
  class Relational < Compositor
16
- private
17
- MM = ActiveFacts::Metamodel
18
16
  public
19
17
  def initialize constellation, name, options = {}
20
18
  # Extract recognised options so our superclass doesn't complain:
@@ -802,5 +800,7 @@ module ActiveFacts
802
800
  end
803
801
 
804
802
  end
803
+
804
+ publish_compositor(Relational)
805
805
  end
806
806
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveFacts
2
2
  module Compositions
3
- VERSION = "1.9.5"
3
+ VERSION = "1.9.6"
4
4
  end
5
5
  end
@@ -0,0 +1,14 @@
1
+ require "activefacts/metamodel"
2
+ require "activefacts/compositions/version"
3
+
4
+ module ActiveFacts
5
+ module Generators
6
+ def self.generators
7
+ @@generators ||= {}
8
+ end
9
+
10
+ def self.publish_generator klass
11
+ generators[klass.name.sub(/^ActiveFacts::Generators::/,'').gsub(/::/, '/').downcase] = klass
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,190 @@
1
+ #
2
+ # ActiveFacts Object-Oriented API Generator
3
+ #
4
+ # Copyright (c) 2009-2016 Clifford Heath. Read the LICENSE file.
5
+ #
6
+ require 'digest/sha1'
7
+ require 'activefacts/metamodel'
8
+ require 'activefacts/registry'
9
+ require 'activefacts/compositions'
10
+ require 'activefacts/generator'
11
+
12
+ module ActiveFacts
13
+ module Generators
14
+ # Options are comma or space separated:
15
+ class ObjectOriented
16
+ def initialize composition, options = {}
17
+ @composition = composition
18
+ @options = options
19
+ @comments = @options.delete("comments")
20
+ end
21
+
22
+ def generate
23
+ @composites_emitted = {}
24
+
25
+ retract_intrinsic_types
26
+
27
+ composites =
28
+ @composition.
29
+ all_composite.
30
+ sort_by{|composite| composite.mapping.name}
31
+
32
+ prelude(@composition) +
33
+ generate_classes(composites) +
34
+ finale
35
+ end
36
+
37
+ def generate_classes composites
38
+ composites.
39
+ map do |composite|
40
+ generate_class(composite)
41
+ end.
42
+ compact.
43
+ join("\n")
44
+ end
45
+
46
+ def composite_for object_type
47
+ @composition.all_composite.detect{|c| c.mapping.object_type == object_type }
48
+ end
49
+
50
+ # We don't need Composites for object types that are built-in to the Ruby API.
51
+ def is_intrinsic_type composite
52
+ o = composite.mapping.object_type
53
+ return true if o.name == "_ImplicitBooleanValueType"
54
+ return false if o.supertype
55
+ # A value type with no supertype must be emitted if it is the child in any absorption:
56
+ return !composite.mapping.all_member.detect{|m| m.forward_absorption}
57
+ end
58
+
59
+ def retract_intrinsic_types
60
+ @composition.
61
+ all_composite.
62
+ sort_by{|composite| composite.mapping.name}.
63
+ each do |composite|
64
+ o = composite.mapping.object_type
65
+ next unless o.is_a?(MM::ValueType)
66
+ composite.retract and next if is_intrinsic_type(composite)
67
+ end
68
+ end
69
+
70
+ def inherited_identification
71
+ ''
72
+ end
73
+
74
+ def identified_by_roles identifying_roles
75
+ "REVISIT: override identified_by_roles\n"
76
+ end
77
+
78
+ def value_type_declaration object_type
79
+ "REVISIT: override value_type_declaration\n"
80
+ end
81
+
82
+ def class_prelude(object_type, supertype)
83
+ "REVISIT: override class_prelude\n"
84
+ end
85
+
86
+ def class_finale(object_type)
87
+ "REVISIT: override class_finale\n"
88
+ end
89
+
90
+ def generate_class composite, predefine_role_players = true
91
+ return nil if @composites_emitted[composite]
92
+
93
+ mapping = composite.mapping
94
+ object_type = mapping.object_type
95
+ is_entity_type = object_type.is_a?(MM::EntityType)
96
+ forward_declarations = []
97
+
98
+ # Emit supertypes before subtypes
99
+ supertype_composites =
100
+ object_type.all_supertype.map{|s| composite_for(s) }.compact
101
+ forward_declarations +=
102
+ supertype_composites.map{|c| generate_class(c, false)}.compact
103
+
104
+ @composites_emitted[composite] = true
105
+
106
+ # Select the members that will be declared as O-O roles:
107
+ mapping.re_rank
108
+ members = mapping.
109
+ all_member.
110
+ sort_by{|m| m.ordinal}.
111
+ reject do |m|
112
+ m.is_a?(MM::Absorption) and
113
+ m.forward_absorption || m.child_role.fact_type.is_a?(MM::TypeInheritance)
114
+ end
115
+
116
+ if predefine_role_players
117
+ # The idea was good, but we need to avoid triggering a forward reference problem.
118
+ # We only do it when we're not dumping a supertype dependency.
119
+ #
120
+ # For those roles that derive from Mappings, produce class definitions to avoid forward references:
121
+ forward_composites =
122
+ members.
123
+ select{ |m| m.is_a?(MM::Mapping) }.
124
+ map{ |m| composite_for m.object_type }.
125
+ compact.
126
+ sort_by{|c| c.mapping.name}
127
+ forward_declarations +=
128
+ forward_composites.map{|c| generate_class(c)}.compact
129
+ end
130
+
131
+ forward_declarations = forward_declarations.map{|f| "#{f}\n"}*''
132
+
133
+ primary_supertype =
134
+ if is_entity_type
135
+ object_type.identifying_supertype ||
136
+ object_type.supertypes[0] # Hopefully there's only one!
137
+ else
138
+ object_type.supertype || object_type
139
+ end
140
+
141
+ type_declaration =
142
+ if is_entity_type
143
+ if primary_supertype and object_type.identification_is_inherited
144
+ inherited_identification
145
+ else
146
+ identifying_roles =
147
+ if object_type.fact_type && object_type.fact_type.is_unary
148
+ # Objectified unary; find the absorption over the LinkFactType
149
+ members.
150
+ select{|m| m.is_a?(MM::Absorption) && m.child_role.base_role.fact_type.entity_type}.
151
+ map{|m| m.child_role}
152
+ else
153
+ object_type.preferred_identifier.role_sequence.all_role_ref.map(&:role).
154
+ map do |role|
155
+ members.detect{|m| m.all_role.include?(role)}
156
+ end
157
+ end
158
+ identified_by_roles identifying_roles
159
+ end
160
+ else
161
+ value_type_declaration object_type
162
+ end
163
+
164
+ forward_declarations +
165
+ class_prelude(object_type, primary_supertype) +
166
+ type_declaration +
167
+ members.
168
+ map do |component|
169
+ (@comments ? comment(component) + "\n" : '') +
170
+ role_definition(component)
171
+ end*'' +
172
+ class_finale(object_type)
173
+ end
174
+
175
+ def role_definition component
176
+ "REVISIT: override role_definition\n"
177
+ end
178
+
179
+ def comment component
180
+ if component.is_a?(MM::Absorption)
181
+ component.parent_role.fact_type.reading_preferably_starting_with_role(component.parent_role).expand([], false)
182
+ else
183
+ component.name
184
+ end
185
+ end
186
+
187
+ MM = ActiveFacts::Metamodel
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,123 @@
1
+ #
2
+ # ActiveFacts Ruby API Generator
3
+ #
4
+ # Copyright (c) 2009-2016 Clifford Heath. Read the LICENSE file.
5
+ #
6
+ require 'activefacts/metamodel'
7
+ require 'activefacts/compositions'
8
+ require 'activefacts/generator'
9
+ require 'activefacts/generator/oo'
10
+
11
+ module ActiveFacts
12
+ module Generators
13
+ # Options are comma or space separated:
14
+ class Ruby < ObjectOriented
15
+ def initialize composition, options = {}
16
+ super
17
+ @scope = options.delete('scope') || ''
18
+ @scope = @scope.split(/::/)
19
+ @scope_prefix = ' '*@scope.size
20
+ end
21
+
22
+ def prelude composition
23
+ "require 'activefacts/api'\n\n" +
24
+ (0...@scope.size).map{|i| ' '*i + "module #{@scope[i]}\n"}*'' +
25
+ "#{@scope_prefix}module #{composition.name}\n"
26
+ end
27
+
28
+ def finale
29
+ @scope.size.downto(0).map{|i| ' '*i+"end\n"}*''
30
+ end
31
+
32
+ def generate_classes composites
33
+ super(composites).
34
+ gsub(/^/, ' '*@scope.size)
35
+ end
36
+
37
+ def identified_by_roles identifying_roles
38
+ " identified_by #{ identifying_roles.map{|m| ':'+ruby_role_name(m) }*', ' }\n"
39
+ end
40
+
41
+ def value_type_declaration object_type
42
+ " value_type#{object_type.length ? " length: #{object_type.length}" : ''}\n" # REVISIT: Add other parameters and value restrictions
43
+ end
44
+
45
+ def class_prelude(object_type, supertype)
46
+ global_qualifier = object_type == supertype ? '::' :''
47
+ " class #{object_type.name.words.capcase}" + (supertype ? " < #{global_qualifier}#{supertype.name.words.capcase}" : '') + "\n"
48
+ end
49
+
50
+ def class_finale(object_type)
51
+ " end\n"
52
+ end
53
+
54
+ def role_definition component
55
+ role_name = ruby_role_name component
56
+
57
+ # Is the role mandatory?
58
+ mandatory = component.is_mandatory ? ', mandatory: true' : ''
59
+
60
+ # Does the role name imply the matching class name?
61
+ if component.is_a?(MM::Absorption) and
62
+ counterpart = component.object_type and
63
+ counterpart_composite = composite_for(counterpart)
64
+ counterpart_class_emitted = @composites_emitted[counterpart_composite]
65
+
66
+ counterpart_class_name = ruby_class_name counterpart_composite
67
+ counterpart_default_role = ruby_role_name counterpart_composite.mapping
68
+ rolename_implies_class = role_name.words.capcase == counterpart_class_name
69
+ class_ref = counterpart_class_emitted ? counterpart_class_name : counterpart_class_name.inspect
70
+ class_spec = rolename_implies_class ? '' : ", class: #{class_ref}"
71
+
72
+ # Does the reverse role need explicit specification?
73
+ implied_reverse_role_name = ruby_role_name(component.root.mapping)
74
+ actual_reverse_role_name = ruby_role_name component.reverse_absorption
75
+
76
+ if implied_reverse_role_name != actual_reverse_role_name
77
+ counterpart_spec = ", counterpart: :#{actual_reverse_role_name}"
78
+ elsif !rolename_implies_class
79
+ # _as_XYZ is added where the forward role does not imply the class, and :counterpart role name is not specified
80
+ actual_reverse_role_name += "_as_#{role_name}"
81
+ end
82
+ all = component.child_role.is_unique ? '' : 'all_'
83
+ see = ", see #{counterpart_class_name}\##{all+actual_reverse_role_name}"
84
+ end
85
+
86
+ counterpart_comment = "# #{comment component}#{see}"
87
+
88
+ definition =
89
+ " #{role_specifier component}"
90
+ definition += ' '*(20-definition.length) if definition.length < 20
91
+ definition += ":#{ruby_role_name component}#{mandatory}#{class_spec}#{counterpart_spec} "
92
+ definition += ' '*(56-definition.length) if definition.length < 56
93
+ definition += "#{counterpart_comment}"
94
+ definition += "\n"
95
+ end
96
+
97
+ def role_specifier component
98
+ if component.is_a?(MM::Indicator)
99
+ 'maybe'
100
+ else
101
+ if component.is_a?(MM::Absorption) and component.child_role.is_unique
102
+ 'one_to_one'
103
+ else
104
+ 'has_one'
105
+ end
106
+ end
107
+ end
108
+
109
+ def ruby_role_name component
110
+ component.name.words.snakecase
111
+ end
112
+
113
+ def ruby_class_name composite
114
+ composite.mapping.name.words.capcase
115
+ end
116
+
117
+ def comment component
118
+ super
119
+ end
120
+ end
121
+ publish_generator Ruby
122
+ end
123
+ end
@@ -0,0 +1,431 @@
1
+ #
2
+ # ActiveFacts Standard SQL Schema Generator
3
+ #
4
+ # Copyright (c) 2009-2016 Clifford Heath. Read the LICENSE file.
5
+ #
6
+ require 'digest/sha1'
7
+ require 'activefacts/metamodel'
8
+ require 'activefacts/registry'
9
+ require 'activefacts/compositions'
10
+ require 'activefacts/generator'
11
+
12
+ module ActiveFacts
13
+ module Generators
14
+ # Options are comma or space separated:
15
+ # * delay_fks Leave all foreign keys until the end, not just those that contain forward-references
16
+ # * underscore
17
+ class SQL
18
+ def initialize composition, options = {}
19
+ @composition = composition
20
+ @options = options
21
+ @delay_fks = options.include? "delay_fks"
22
+ @underscore = options.include?("underscore") ? "_" : ""
23
+ end
24
+
25
+ def generate
26
+ @tables_emitted = {}
27
+ @delayed_foreign_keys = []
28
+
29
+ generate_schema +
30
+ @composition.
31
+ all_composite.
32
+ sort_by{|composite| composite.mapping.name}.
33
+ map{|composite| generate_table composite}*"\n" + "\n" +
34
+ @delayed_foreign_keys.sort*"\n"
35
+ end
36
+
37
+ def table_name_max
38
+ 60
39
+ end
40
+
41
+ def column_name_max
42
+ 40
43
+ end
44
+
45
+ def index_name_max
46
+ 60
47
+ end
48
+
49
+ def schema_name_max
50
+ 60
51
+ end
52
+
53
+ def safe_table_name composite
54
+ escape(table_name(composite), table_name_max)
55
+ end
56
+
57
+ def safe_column_name component
58
+ escape(column_name(component), column_name_max)
59
+ end
60
+
61
+ def table_name composite
62
+ composite.mapping.name.gsub(' ', @underscore)
63
+ end
64
+
65
+ def column_name component
66
+ component.column_name.capcase
67
+ end
68
+
69
+ def generate_schema
70
+ #go "CREATE SCHEMA #{escape(@composition.name, schema_name_max)}" +
71
+ ''
72
+ end
73
+
74
+ def generate_table composite
75
+ @tables_emitted[composite] = true
76
+ delayed_indices = []
77
+
78
+ "CREATE TABLE #{safe_table_name composite} (\n" +
79
+ (
80
+ composite.mapping.leaves.flat_map do |leaf|
81
+ # Absorbed empty subtypes appear as leaves
82
+ next if leaf.is_a?(MM::Absorption) && leaf.parent_role.fact_type.is_a?(MM::TypeInheritance)
83
+
84
+ generate_column leaf
85
+ end +
86
+ composite.all_index.map do |index|
87
+ generate_index index, delayed_indices
88
+ end.compact.sort +
89
+ composite.all_foreign_key_as_source_composite.map do |fk|
90
+ fk_text = generate_foreign_key fk
91
+ if !@delay_fks and @tables_emitted[fk.composite]
92
+ fk_text
93
+ else
94
+ @delayed_foreign_keys <<
95
+ go("ALTER TABLE #{safe_table_name fk.composite}\n\tADD " + fk_text)
96
+ nil
97
+ end
98
+ end.compact.sort +
99
+ composite.all_local_constraint.map do |constraint|
100
+ '-- '+constraint.inspect # REVISIT: Emit local constraints
101
+ end
102
+ ).compact.flat_map{|f| "\t#{f}" }*",\n"+"\n" +
103
+ go(")") +
104
+ delayed_indices.sort.map do |delayed_index|
105
+ go delayed_index
106
+ end*"\n"
107
+ end
108
+
109
+ def generate_column leaf
110
+ column_name = safe_column_name(leaf)
111
+ padding = " "*(column_name.size >= column_name_max ? 1 : column_name_max-column_name.size)
112
+ constraints = leaf.all_leaf_constraint
113
+
114
+ identity = ''
115
+ "-- #{column_comment leaf}\n\t#{column_name}#{padding}#{component_type leaf, column_name}#{identity}"
116
+ end
117
+
118
+ def column_comment component
119
+ return '' unless cp = component.parent
120
+ prefix = column_comment(cp)
121
+ name = component.name
122
+ if component.is_a?(MM::Absorption)
123
+ reading = component.parent_role.fact_type.reading_preferably_starting_with_role(component.parent_role).expand([], false)
124
+ maybe = component.parent_role.is_mandatory ? '' : 'maybe '
125
+ cpname = cp.name
126
+ if prefix[(-cpname.size-1)..-1] == ' '+cpname && reading[0..cpname.size] == cpname+' '
127
+ prefix+' that ' + maybe + reading[cpname.size+1..-1]
128
+ else
129
+ (prefix.empty? ? '' : prefix+' and ') + maybe + reading
130
+ end
131
+ else
132
+ name
133
+ end
134
+ end
135
+
136
+ def boolean_type
137
+ 'BOOLEAN'
138
+ end
139
+
140
+ def surrogate_type
141
+ 'BIGINT IDENTITY NOT NULL'
142
+ end
143
+
144
+ def component_type component, column_name
145
+ case component
146
+ when MM::Indicator
147
+ boolean_type
148
+ when MM::SurrogateKey
149
+ surrogate_type
150
+ when MM::ValueField, MM::Absorption
151
+ object_type = component.object_type
152
+ while object_type.is_a?(MM::EntityType)
153
+ rr = object_type.preferred_identifier.role_sequence.all_role_ref.single
154
+ raise "Can't produce a column for composite #{component.inspect}" unless rr
155
+ object_type = rr.role.object_type
156
+ end
157
+ raise "A column can only be produced from a ValueType" unless object_type.is_a?(MM::ValueType)
158
+
159
+ if component.is_a?(MM::Absorption)
160
+ value_constraint ||= component.child_role.role_value_constraint
161
+ end
162
+
163
+ supertype = object_type
164
+ begin
165
+ object_type = supertype
166
+ length ||= object_type.length
167
+ scale ||= object_type.scale
168
+ unless component.parent.parent and component.parent.foreign_key
169
+ # No need to enforce value constraints that are already enforced by a foreign key
170
+ value_constraint ||= object_type.value_constraint
171
+ end
172
+ end while supertype = object_type.supertype
173
+ type, length = normalise_type(object_type.name, length)
174
+ sql_type = "#{type}#{
175
+ if !length
176
+ ''
177
+ else
178
+ '(' + length.to_s + (scale ? ", #{scale}" : '') + ')'
179
+ end
180
+ }#{
181
+ (component.path_mandatory ? '' : ' NOT') + ' NULL'
182
+ }#{
183
+ # REVISIT: This is an SQL Server-ism. Replace with a standard SQL SEQUENCE/
184
+ # Emit IDENTITY for columns auto-assigned on commit (except FKs)
185
+ if a = object_type.is_auto_assigned and a != 'assert' and
186
+ !component.all_foreign_key_field.detect{|fkf| fkf.foreign_key.source_composite == component.root}
187
+ ' IDENTITY'
188
+ else
189
+ ''
190
+ end
191
+ }#{
192
+ value_constraint ? check_clause(column_name, value_constraint) : ''
193
+ }"
194
+ else
195
+ raise "Can't make a column from #{component}"
196
+ end
197
+ end
198
+
199
+ def generate_index index, delayed_indices
200
+ nullable_columns =
201
+ index.all_index_field.select do |ixf|
202
+ !ixf.component.path_mandatory
203
+ end
204
+ contains_nullable_columns = nullable_columns.size > 0
205
+
206
+ primary = index.composite_as_primary_index && !contains_nullable_columns
207
+ column_names =
208
+ index.all_index_field.map do |ixf|
209
+ column_name(ixf.component)
210
+ end
211
+ clustering =
212
+ (index.composite_as_primary_index ? ' CLUSTERED' : ' NONCLUSTERED')
213
+
214
+ if contains_nullable_columns
215
+ delayed_indices <<
216
+ 'CREATE UNIQUE'+clustering+' INDEX '+
217
+ escape("#{safe_table_name(index.composite)}By#{column_names*''}", index_name_max) +
218
+ " ON ("+column_names.map{|n| escape(n, column_name_max)}*', ' +
219
+ ") WHERE #{
220
+ nullable_columns.
221
+ map{|ixf| safe_column_name ixf.component}.
222
+ map{|column_name| column_name + ' IS NOT NULL'} *
223
+ ' AND '
224
+ }"
225
+ nil
226
+ else
227
+ '-- '+index.inspect + "\n\t" +
228
+ (primary ? 'PRIMARY KEY' : 'UNIQUE') +
229
+ clustering +
230
+ "(#{column_names.map{|n| escape(n, column_name_max)}*', '})"
231
+ end
232
+ end
233
+
234
+ def generate_foreign_key fk
235
+ '-- '+fk.inspect
236
+ "FOREIGN KEY (" +
237
+ fk.all_foreign_key_field.map{|fkf| safe_column_name fkf.component}*", " +
238
+ ") REFERENCES #{safe_table_name fk.composite} (" +
239
+ fk.all_index_field.map{|ixf| safe_column_name ixf.component}*", " +
240
+ ")"
241
+ end
242
+
243
+ def reserved_words
244
+ @sql_server_reserved_words ||= %w{
245
+ ADD ALL ALTER AND ANY AS ASC AUTHORIZATION BACKUP BEGIN
246
+ BETWEEN BREAK BROWSE BULK BY CASCADE CASE CHECK CHECKPOINT
247
+ CLOSE CLUSTERED COALESCE COLLATE COLUMN COMMIT COMPUTE
248
+ CONSTRAINT CONTAINS CONTAINSTABLE CONTINUE CONVERT CREATE
249
+ CROSS CURRENT CURRENT_DATE CURRENT_TIME CURRENT_TIMESTAMP
250
+ CURRENT_USER CURSOR DATABASE DBCC DEALLOCATE DECLARE
251
+ DEFAULT DELETE DENY DESC DISK DISTINCT DISTRIBUTED DOUBLE
252
+ DROP DUMMY DUMP ELSE END ERRLVL ESCAPE EXCEPT EXEC EXECUTE
253
+ EXISTS EXIT FETCH FILE FILLFACTOR FOR FOREIGN FREETEXT
254
+ FREETEXTTABLE FROM FULL FUNCTION GOTO GRANT GROUP HAVING
255
+ HOLDLOCK IDENTITY IDENTITYCOL IDENTITY_INSERT IF IN INDEX
256
+ INNER INSERT INTERSECT INTO IS JOIN KEY KILL LEFT LIKE
257
+ LINENO LOAD NATIONAL NOCHECK NONCLUSTERED NOT NULL NULLIF
258
+ OF OFF OFFSETS ON OPEN OPENDATASOURCE OPENQUERY OPENROWSET
259
+ OPENXML OPTION OR ORDER OUTER OVER PERCENT PLAN PRECISION
260
+ PRIMARY PRINT PROC PROCEDURE PUBLIC RAISERROR READ READTEXT
261
+ RECONFIGURE REFERENCES REPLICATION RESTORE RESTRICT RETURN
262
+ REVOKE RIGHT ROLLBACK ROWCOUNT ROWGUIDCOL RULE SAVE SCHEMA
263
+ SELECT SESSION_USER SET SETUSER SHUTDOWN SOME STATISTICS
264
+ SYSTEM_USER TABLE TEXTSIZE THEN TO TOP TRAN TRANSACTION
265
+ TRIGGER TRUNCATE TSEQUAL UNION UNIQUE UPDATE UPDATETEXT
266
+ USE USER VALUES VARYING VIEW WAITFOR WHEN WHERE WHILE
267
+ WITH WRITETEXT
268
+ }
269
+
270
+ @reserved_words ||= %w{
271
+ ABSOLUTE ACTION ADD AFTER ALL ALLOCATE ALTER AND ANY ARE
272
+ ARRAY AS ASC ASSERTION AT AUTHORIZATION BEFORE BEGIN
273
+ BETWEEN BINARY BIT BLOB BOOLEAN BOTH BREADTH BY CALL
274
+ CASCADE CASCADED CASE CAST CATALOG CHAR CHARACTER CHECK
275
+ CLOB CLOSE COLLATE COLLATION COLUMN COMMIT CONDITION
276
+ CONNECT CONNECTION CONSTRAINT CONSTRAINTS CONSTRUCTOR
277
+ CONTINUE CORRESPONDING CREATE CROSS CUBE CURRENT CURRENT_DATE
278
+ CURRENT_DEFAULT_TRANSFORM_GROUP CURRENT_TRANSFORM_GROUP_FOR_TYPE
279
+ CURRENT_PATH CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP
280
+ CURRENT_USER CURSOR CYCLE DATA DATE DAY DEALLOCATE DEC
281
+ DECIMAL DECLARE DEFAULT DEFERRABLE DEFERRED DELETE DEPTH
282
+ DEREF DESC DESCRIBE DESCRIPTOR DETERMINISTIC DIAGNOSTICS
283
+ DISCONNECT DISTINCT DO DOMAIN DOUBLE DROP DYNAMIC EACH
284
+ ELSE ELSEIF END EQUALS ESCAPE EXCEPT EXCEPTION EXEC EXECUTE
285
+ EXISTS EXIT EXTERNAL FALSE FETCH FIRST FLOAT FOR FOREIGN
286
+ FOUND FROM FREE FULL FUNCTION GENERAL GET GLOBAL GO GOTO
287
+ GRANT GROUP GROUPING HANDLE HAVING HOLD HOUR IDENTITY IF
288
+ IMMEDIATE IN INDICATOR INITIALLY INNER INOUT INPUT INSERT
289
+ INT INTEGER INTERSECT INTERVAL INTO IS ISOLATION JOIN KEY
290
+ LANGUAGE LARGE LAST LATERAL LEADING LEAVE LEFT LEVEL LIKE
291
+ LOCAL LOCALTIME LOCALTIMESTAMP LOCATOR LOOP MAP MATCH
292
+ METHOD MINUTE MODIFIES MODULE MONTH NAMES NATIONAL NATURAL
293
+ NCHAR NCLOB NESTING NEW NEXT NO NONE NOT NULL NUMERIC
294
+ OBJECT OF OLD ON ONLY OPEN OPTION OR ORDER ORDINALITY OUT
295
+ OUTER OUTPUT OVERLAPS PAD PARAMETER PARTIAL PATH PRECISION
296
+ PREPARE PRESERVE PRIMARY PRIOR PRIVILEGES PROCEDURE PUBLIC
297
+ READ READS REAL RECURSIVE REDO REF REFERENCES REFERENCING
298
+ RELATIVE RELEASE REPEAT RESIGNAL RESTRICT RESULT RETURN
299
+ RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROUTINE ROW
300
+ ROWS SAVEPOINT SCHEMA SCROLL SEARCH SECOND SECTION SELECT
301
+ SESSION SESSION_USER SET SETS SIGNAL SIMILAR SIZE SMALLINT
302
+ SOME SPACE SPECIFIC SPECIFICTYPE SQL SQLEXCEPTION SQLSTATE
303
+ SQLWARNING START STATE STATIC SYSTEM_USER TABLE TEMPORARY
304
+ THEN TIME TIMESTAMP TIMEZONE_HOUR TIMEZONE_MINUTE TO
305
+ TRAILING TRANSACTION TRANSLATION TREAT TRIGGER TRUE UNDER
306
+ UNDO UNION UNIQUE UNKNOWN UNNEST UNTIL UPDATE USAGE USER
307
+ USING VALUE VALUES VARCHAR VARYING VIEW WHEN WHENEVER
308
+ WHERE WHILE WITH WITHOUT WORK WRITE YEAR ZONE
309
+ }
310
+ end
311
+
312
+ def is_reserved_word w
313
+ @reserved_word_hash ||=
314
+ reserved_words.inject({}) do |h,w|
315
+ h[w] = true
316
+ h
317
+ end
318
+ @reserved_word_hash[w.upcase]
319
+ end
320
+
321
+ def go s = ''
322
+ "#{s}\nGO\n" # REVISIT: This is an SQL-Serverism. Move it to a subclass.
323
+ end
324
+
325
+ def escape s, max = table_name_max
326
+ # Escape SQL keywords and non-identifiers
327
+ if s.size > max
328
+ excess = s[max..-1]
329
+ s = s[0...max-(excess.size/8)] +
330
+ Digest::SHA1.hexdigest(excess)[0...excess.size/8]
331
+ end
332
+
333
+ if s =~ /[^A-Za-z0-9_]/ || is_reserved_word(s)
334
+ "[#{s}]"
335
+ else
336
+ s
337
+ end
338
+ end
339
+
340
+ # Return SQL type and (modified?) length for the passed base type
341
+ def normalise_type(type, length)
342
+ sql_type = case type
343
+ when /^Auto ?Counter$/
344
+ 'int'
345
+
346
+ when /^Unsigned ?Integer$/,
347
+ /^Signed ?Integer$/,
348
+ /^Unsigned ?Small ?Integer$/,
349
+ /^Signed ?Small ?Integer$/,
350
+ /^Unsigned ?Tiny ?Integer$/
351
+ s = case
352
+ when length == nil
353
+ 'int'
354
+ when length <= 8
355
+ 'tinyint'
356
+ when length <= 16
357
+ 'smallint'
358
+ when length <= 32
359
+ 'int'
360
+ else
361
+ 'bigint'
362
+ end
363
+ length = nil
364
+ s
365
+
366
+ when /^Decimal$/
367
+ 'decimal'
368
+
369
+ when /^Fixed ?Length ?Text$/, /^Char$/
370
+ 'char'
371
+ when /^Variable ?Length ?Text$/, /^String$/
372
+ 'varchar'
373
+ when /^Large ?Length ?Text$/, /^Text$/
374
+ 'text'
375
+
376
+ when /^Date ?And ?Time$/, /^Date ?Time$/
377
+ 'datetime'
378
+ when /^Date$/
379
+ 'datetime' # SQLSVR 2K5: 'date'
380
+ when /^Time$/
381
+ 'datetime' # SQLSVR 2K5: 'time'
382
+ when /^Auto ?Time ?Stamp$/
383
+ 'timestamp'
384
+
385
+ when /^Guid$/
386
+ 'uniqueidentifier'
387
+ when /^Money$/
388
+ 'decimal'
389
+ when /^Picture ?Raw ?Data$/, /^Image$/
390
+ 'image'
391
+ when /^Variable ?Length ?Raw ?Data$/, /^Blob$/
392
+ 'varbinary'
393
+ when /^BIT$/
394
+ 'bit'
395
+ else type # raise "SQL type unknown for standard type #{type}"
396
+ end
397
+ [sql_type, length]
398
+ end
399
+
400
+ def sql_value(value)
401
+ value.is_literal_string ? sql_string(value.literal) : value.literal
402
+ end
403
+
404
+ def sql_string(str)
405
+ "'" + str.gsub(/'/,"''") + "'"
406
+ end
407
+
408
+ def check_clause column_name, value_constraint
409
+ " CHECK(" +
410
+ value_constraint.all_allowed_range_sorted.map do |ar|
411
+ vr = ar.value_range
412
+ min = vr.minimum_bound
413
+ max = vr.maximum_bound
414
+ if (min && max && max.value.literal == min.value.literal)
415
+ "#{column_name} = #{sql_value(min.value)}"
416
+ else
417
+ inequalities = [
418
+ min && "#{column_name} >#{min.is_inclusive ? "=" : ""} #{sql_value(min.value)}",
419
+ max && "#{column_name} <#{max.is_inclusive ? "=" : ""} #{sql_value(max.value)}"
420
+ ].compact
421
+ inequalities.size > 1 ? "(" + inequalities*" AND " + ")" : inequalities[0]
422
+ end
423
+ end*" OR " +
424
+ ")"
425
+ end
426
+
427
+ MM = ActiveFacts::Metamodel
428
+ end
429
+ publish_generator SQL
430
+ end
431
+ end