activefacts-compositions 1.9.6 → 1.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Rakefile +33 -0
- data/activefacts-compositions.gemspec +3 -3
- data/bin/schema_compositor +142 -85
- data/lib/activefacts/compositions/binary.rb +19 -15
- data/lib/activefacts/compositions/compositor.rb +126 -125
- data/lib/activefacts/compositions/constraints.rb +74 -54
- data/lib/activefacts/compositions/datavault.rb +545 -0
- data/lib/activefacts/compositions/names.rb +58 -58
- data/lib/activefacts/compositions/relational.rb +801 -692
- data/lib/activefacts/compositions/traits/rails.rb +180 -0
- data/lib/activefacts/compositions/version.rb +1 -1
- data/lib/activefacts/generator/doc/css/ldm.css +45 -0
- data/lib/activefacts/generator/doc/cwm.rb +764 -0
- data/lib/activefacts/generator/doc/glossary.rb +473 -0
- data/lib/activefacts/generator/doc/graphviz.rb +134 -0
- data/lib/activefacts/generator/doc/ldm.rb +698 -0
- data/lib/activefacts/generator/oo.rb +130 -124
- data/lib/activefacts/generator/rails/models.rb +237 -0
- data/lib/activefacts/generator/rails/schema.rb +273 -0
- data/lib/activefacts/generator/ruby.rb +75 -67
- data/lib/activefacts/generator/sql.rb +333 -351
- data/lib/activefacts/generator/sql/server.rb +100 -39
- data/lib/activefacts/generator/summary.rb +67 -59
- data/lib/activefacts/generator/validate.rb +19 -134
- metadata +18 -15
@@ -5,6 +5,7 @@
|
|
5
5
|
#
|
6
6
|
require 'digest/sha1'
|
7
7
|
require 'activefacts/metamodel'
|
8
|
+
require 'activefacts/metamodel/datatypes'
|
8
9
|
require 'activefacts/registry'
|
9
10
|
require 'activefacts/compositions'
|
10
11
|
require 'activefacts/generator'
|
@@ -15,416 +16,397 @@ module ActiveFacts
|
|
15
16
|
# * delay_fks Leave all foreign keys until the end, not just those that contain forward-references
|
16
17
|
# * underscore
|
17
18
|
class SQL
|
19
|
+
MM = ActiveFacts::Metamodel unless const_defined?(:MM)
|
20
|
+
def self.options
|
21
|
+
{
|
22
|
+
delay_fks: ['Boolean', "Delay emitting all foreign keys until the bottom of the file"],
|
23
|
+
underscore: [String, "Use 'str' instead of underscore between words in table names"],
|
24
|
+
unicode: ['Boolean', "Use Unicode for all text fields by default"],
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
18
28
|
def initialize composition, options = {}
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
29
|
+
@composition = composition
|
30
|
+
@options = options
|
31
|
+
@delay_fks = options.delete "delay_fks"
|
32
|
+
@underscore = options.has_key?("underscore") ? (options['underscore'] || '_') : ''
|
33
|
+
@unicode = options.delete "unicode"
|
23
34
|
end
|
24
35
|
|
25
36
|
def generate
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
37
|
+
@tables_emitted = {}
|
38
|
+
@delayed_foreign_keys = []
|
39
|
+
|
40
|
+
generate_schema +
|
41
|
+
@composition.
|
42
|
+
all_composite.
|
43
|
+
sort_by{|composite| composite.mapping.name}.
|
44
|
+
map{|composite| generate_table composite}*"\n" + "\n" +
|
45
|
+
@delayed_foreign_keys.sort*"\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
def data_type_context
|
49
|
+
@data_type_context ||= SQLDataTypeContext.new
|
35
50
|
end
|
36
51
|
|
37
52
|
def table_name_max
|
38
|
-
|
53
|
+
60
|
39
54
|
end
|
40
55
|
|
41
56
|
def column_name_max
|
42
|
-
|
57
|
+
40
|
43
58
|
end
|
44
59
|
|
45
60
|
def index_name_max
|
46
|
-
|
61
|
+
60
|
47
62
|
end
|
48
63
|
|
49
64
|
def schema_name_max
|
50
|
-
|
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
|
65
|
+
60
|
67
66
|
end
|
68
67
|
|
69
68
|
def generate_schema
|
70
|
-
|
71
|
-
|
69
|
+
#go "CREATE SCHEMA #{escape(@composition.name, schema_name_max)}" +
|
70
|
+
''
|
72
71
|
end
|
73
72
|
|
74
73
|
def generate_table composite
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
74
|
+
@tables_emitted[composite] = true
|
75
|
+
delayed_indices = []
|
76
|
+
|
77
|
+
"CREATE TABLE #{safe_table_name composite} (\n" +
|
78
|
+
(
|
79
|
+
composite.mapping.all_leaf.flat_map do |leaf|
|
80
|
+
# Absorbed empty subtypes appear as leaves
|
81
|
+
next if leaf.is_a?(MM::Absorption) && leaf.parent_role.fact_type.is_a?(MM::TypeInheritance)
|
82
|
+
|
83
|
+
generate_column leaf
|
84
|
+
end +
|
85
|
+
composite.all_index.map do |index|
|
86
|
+
generate_index index, delayed_indices
|
87
|
+
end.compact.sort +
|
88
|
+
composite.all_foreign_key_as_source_composite.map do |fk|
|
89
|
+
fk_text = generate_foreign_key fk
|
90
|
+
if !@delay_fks and @tables_emitted[fk.composite]
|
91
|
+
fk_text
|
92
|
+
else
|
93
|
+
@delayed_foreign_keys <<
|
94
|
+
go("ALTER TABLE #{safe_table_name fk.source_composite}\n\tADD " + fk_text)
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
end.compact.sort +
|
98
|
+
composite.all_local_constraint.map do |constraint|
|
99
|
+
'-- '+constraint.inspect # REVISIT: Emit local constraints
|
100
|
+
end
|
101
|
+
).compact.flat_map{|f| "\t#{f}" }*",\n"+"\n" +
|
102
|
+
go(")") +
|
103
|
+
delayed_indices.sort.map do |delayed_index|
|
104
|
+
go delayed_index
|
105
|
+
end*"\n"
|
107
106
|
end
|
108
107
|
|
109
108
|
def generate_column leaf
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
109
|
+
column_name = safe_column_name(leaf)
|
110
|
+
padding = " "*(column_name.size >= column_name_max ? 1 : column_name_max-column_name.size)
|
111
|
+
constraints = leaf.all_leaf_constraint
|
135
112
|
|
136
|
-
|
137
|
-
|
113
|
+
"-- #{leaf.comment}\n" +
|
114
|
+
"\t#{column_name}#{padding}#{column_type leaf, column_name}"
|
138
115
|
end
|
139
116
|
|
140
|
-
def
|
141
|
-
|
117
|
+
def auto_assign_type
|
118
|
+
' GENERATED ALWAYS AS IDENTITY'
|
142
119
|
end
|
143
120
|
|
144
|
-
def
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
121
|
+
def column_type component, column_name
|
122
|
+
type_name, options = component.data_type(data_type_context)
|
123
|
+
options ||= {}
|
124
|
+
length = options[:length]
|
125
|
+
value_constraint = options[:value_constraint]
|
126
|
+
type_name, length = normalise_type(type_name, length, value_constraint)
|
127
|
+
|
128
|
+
"#{
|
129
|
+
type_name
|
130
|
+
}#{
|
131
|
+
"(#{length}#{(s = options[:scale]) ? ", #{s}" : ''})" if length
|
132
|
+
}#{
|
133
|
+
((options[:mandatory] ? ' NOT' : '') + ' NULL') if options.has_key?(:mandatory)
|
134
|
+
}#{
|
135
|
+
auto_assign_type if a = options[:auto_assign] && a != 'assert'
|
136
|
+
}#{
|
137
|
+
check_clause(column_name, value_constraint) if value_constraint
|
138
|
+
}"
|
197
139
|
end
|
198
140
|
|
199
141
|
def generate_index index, delayed_indices
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
142
|
+
nullable_columns =
|
143
|
+
index.all_index_field.select do |ixf|
|
144
|
+
!ixf.component.path_mandatory
|
145
|
+
end
|
146
|
+
contains_nullable_columns = nullable_columns.size > 0
|
147
|
+
|
148
|
+
primary = index.composite_as_primary_index && !contains_nullable_columns
|
149
|
+
column_names =
|
150
|
+
index.all_index_field.map do |ixf|
|
151
|
+
column_name(ixf.component)
|
152
|
+
end
|
153
|
+
clustering =
|
154
|
+
(index.composite_as_primary_index ? ' CLUSTERED' : ' NONCLUSTERED')
|
155
|
+
|
156
|
+
if contains_nullable_columns
|
157
|
+
table_name = safe_table_name(index.composite)
|
158
|
+
delayed_indices <<
|
159
|
+
'CREATE UNIQUE'+clustering+' INDEX '+
|
160
|
+
escape("#{table_name(index.composite)}By#{column_names*''}", index_name_max) +
|
161
|
+
" ON #{table_name}("+column_names.map{|n| escape(n, column_name_max)}*', ' +
|
162
|
+
") WHERE #{
|
163
|
+
nullable_columns.
|
164
|
+
map{|ixf| safe_column_name ixf.component}.
|
165
|
+
map{|column_name| column_name + ' IS NOT NULL'} *
|
166
|
+
' AND '
|
167
|
+
}"
|
168
|
+
nil
|
169
|
+
else
|
170
|
+
'-- '+index.inspect + "\n\t" +
|
171
|
+
(primary ? 'PRIMARY KEY' : 'UNIQUE') +
|
172
|
+
clustering +
|
173
|
+
"(#{column_names.map{|n| escape(n, column_name_max)}*', '})"
|
174
|
+
end
|
232
175
|
end
|
233
176
|
|
234
177
|
def generate_foreign_key fk
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
178
|
+
'-- '+fk.inspect
|
179
|
+
"FOREIGN KEY (" +
|
180
|
+
fk.all_foreign_key_field.map{|fkf| safe_column_name fkf.component}*", " +
|
181
|
+
") REFERENCES #{safe_table_name fk.composite} (" +
|
182
|
+
fk.all_index_field.map{|ixf| safe_column_name ixf.component}*", " +
|
183
|
+
")"
|
184
|
+
end
|
185
|
+
|
186
|
+
# Return SQL type and (modified?) length for the passed base type
|
187
|
+
def normalise_type(type_name, length, value_constraint)
|
188
|
+
type = MM::DataType.normalise(type_name)
|
189
|
+
|
190
|
+
case type
|
191
|
+
when MM::DataType::TYPE_Boolean; data_type_context.boolean_type
|
192
|
+
when MM::DataType::TYPE_Integer
|
193
|
+
if type_name =~ /^Auto ?Counter$/i
|
194
|
+
MM::DataType.normalise_int_length('int', data_type_context.default_surrogate_length, value_constraint, data_type_context)[0]
|
195
|
+
else
|
196
|
+
v, = MM::DataType.normalise_int_length(type_name, length, value_constraint, data_type_context)
|
197
|
+
v
|
198
|
+
end
|
199
|
+
when MM::DataType::TYPE_Real;
|
200
|
+
["FLOAT", data_type_context.default_length(type, type_name)]
|
201
|
+
when MM::DataType::TYPE_Decimal; 'DECIMAL'
|
202
|
+
when MM::DataType::TYPE_Money; 'DECIMAL'
|
203
|
+
when MM::DataType::TYPE_Char; [data_type_context.default_char_type, length || data_type_context.char_default_length]
|
204
|
+
when MM::DataType::TYPE_String; [data_type_context.default_varchar_type, length || data_type_context.varchar_default_length]
|
205
|
+
when MM::DataType::TYPE_Text; [data_type_context.default_text_type, length || 'MAX']
|
206
|
+
when MM::DataType::TYPE_Date; 'DATE' # SQLSVR 2K5: 'date'
|
207
|
+
when MM::DataType::TYPE_Time; 'TIME' # SQLSVR 2K5: 'time'
|
208
|
+
when MM::DataType::TYPE_DateTime; 'TIMESTAMP'
|
209
|
+
when MM::DataType::TYPE_Timestamp;'TIMESTAMP'
|
210
|
+
when MM::DataType::TYPE_Binary;
|
211
|
+
length ||= 16 if type_name =~ /^(guid|uuid)$/i
|
212
|
+
if length
|
213
|
+
['BINARY', length]
|
214
|
+
else
|
215
|
+
'VARBINARY'
|
216
|
+
end
|
217
|
+
else
|
218
|
+
type_name
|
219
|
+
end
|
241
220
|
end
|
242
221
|
|
243
222
|
def reserved_words
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
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
|
-
}
|
223
|
+
@reserved_words ||= %w{
|
224
|
+
ABSOLUTE ACTION ADD AFTER ALL ALLOCATE ALTER AND ANY ARE
|
225
|
+
ARRAY AS ASC ASSERTION AT AUTHORIZATION BEFORE BEGIN
|
226
|
+
BETWEEN BINARY BIT BLOB BOOLEAN BOTH BREADTH BY CALL
|
227
|
+
CASCADE CASCADED CASE CAST CATALOG CHAR CHARACTER CHECK
|
228
|
+
CLOB CLOSE COLLATE COLLATION COLUMN COMMIT CONDITION
|
229
|
+
CONNECT CONNECTION CONSTRAINT CONSTRAINTS CONSTRUCTOR
|
230
|
+
CONTINUE CORRESPONDING CREATE CROSS CUBE CURRENT CURRENT_DATE
|
231
|
+
CURRENT_DEFAULT_TRANSFORM_GROUP CURRENT_TRANSFORM_GROUP_FOR_TYPE
|
232
|
+
CURRENT_PATH CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP
|
233
|
+
CURRENT_USER CURSOR CYCLE DATA DATE DAY DEALLOCATE DEC
|
234
|
+
DECIMAL DECLARE DEFAULT DEFERRABLE DEFERRED DELETE DEPTH
|
235
|
+
DEREF DESC DESCRIBE DESCRIPTOR DETERMINISTIC DIAGNOSTICS
|
236
|
+
DISCONNECT DISTINCT DO DOMAIN DOUBLE DROP DYNAMIC EACH
|
237
|
+
ELSE ELSEIF END EQUALS ESCAPE EXCEPT EXCEPTION EXEC EXECUTE
|
238
|
+
EXISTS EXIT EXTERNAL FALSE FETCH FIRST FLOAT FOR FOREIGN
|
239
|
+
FOUND FROM FREE FULL FUNCTION GENERAL GET GLOBAL GO GOTO
|
240
|
+
GRANT GROUP GROUPING HANDLE HAVING HOLD HOUR IDENTITY IF
|
241
|
+
IMMEDIATE IN INDICATOR INITIALLY INNER INOUT INPUT INSERT
|
242
|
+
INT INTEGER INTERSECT INTERVAL INTO IS ISOLATION JOIN KEY
|
243
|
+
LANGUAGE LARGE LAST LATERAL LEADING LEAVE LEFT LEVEL LIKE
|
244
|
+
LOCAL LOCALTIME LOCALTIMESTAMP LOCATOR LOOP MAP MATCH
|
245
|
+
METHOD MINUTE MODIFIES MODULE MONTH NAMES NATIONAL NATURAL
|
246
|
+
NCHAR NCLOB NESTING NEW NEXT NO NONE NOT NULL NUMERIC
|
247
|
+
OBJECT OF OLD ON ONLY OPEN OPTION OR ORDER ORDINALITY OUT
|
248
|
+
OUTER OUTPUT OVERLAPS PAD PARAMETER PARTIAL PATH PRECISION
|
249
|
+
PREPARE PRESERVE PRIMARY PRIOR PRIVILEGES PROCEDURE PUBLIC
|
250
|
+
READ READS REAL RECURSIVE REDO REF REFERENCES REFERENCING
|
251
|
+
RELATIVE RELEASE REPEAT RESIGNAL RESTRICT RESULT RETURN
|
252
|
+
RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROUTINE ROW
|
253
|
+
ROWS SAVEPOINT SCHEMA SCROLL SEARCH SECOND SECTION SELECT
|
254
|
+
SESSION SESSION_USER SET SETS SIGNAL SIMILAR SIZE SMALLINT
|
255
|
+
SOME SPACE SPECIFIC SPECIFICTYPE SQL SQLEXCEPTION SQLSTATE
|
256
|
+
SQLWARNING START STATE STATIC SYSTEM_USER TABLE TEMPORARY
|
257
|
+
THEN TIME TIMESTAMP TIMEZONE_HOUR TIMEZONE_MINUTE TO
|
258
|
+
TRAILING TRANSACTION TRANSLATION TREAT TRIGGER TRUE UNDER
|
259
|
+
UNDO UNION UNIQUE UNKNOWN UNNEST UNTIL UPDATE USAGE USER
|
260
|
+
USING VALUE VALUES VARCHAR VARYING VIEW WHEN WHENEVER
|
261
|
+
WHERE WHILE WITH WITHOUT WORK WRITE YEAR ZONE
|
262
|
+
}
|
310
263
|
end
|
311
264
|
|
312
265
|
def is_reserved_word w
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
266
|
+
@reserved_word_hash ||=
|
267
|
+
reserved_words.inject({}) do |h,w|
|
268
|
+
h[w] = true
|
269
|
+
h
|
270
|
+
end
|
271
|
+
@reserved_word_hash[w.upcase]
|
319
272
|
end
|
320
273
|
|
321
274
|
def go s = ''
|
322
|
-
|
275
|
+
"#{s};\n\n"
|
323
276
|
end
|
324
277
|
|
325
278
|
def escape s, max = table_name_max
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
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]
|
279
|
+
# Escape SQL keywords and non-identifiers
|
280
|
+
if s.size > max
|
281
|
+
excess = s[max..-1]
|
282
|
+
s = s[0...max-(excess.size/8)] +
|
283
|
+
Digest::SHA1.hexdigest(excess)[0...excess.size/8]
|
284
|
+
end
|
285
|
+
|
286
|
+
if s =~ /[^A-Za-z0-9_]/ || is_reserved_word(s)
|
287
|
+
"[#{s}]"
|
288
|
+
else
|
289
|
+
s
|
290
|
+
end
|
398
291
|
end
|
399
292
|
|
400
293
|
def sql_value(value)
|
401
|
-
|
294
|
+
value.is_literal_string ? sql_string(value.literal) : value.literal
|
402
295
|
end
|
403
296
|
|
404
297
|
def sql_string(str)
|
405
|
-
|
298
|
+
"'" + str.gsub(/'/,"''") + "'"
|
406
299
|
end
|
407
300
|
|
408
301
|
def check_clause column_name, value_constraint
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
302
|
+
" CHECK(" +
|
303
|
+
value_constraint.all_allowed_range_sorted.map do |ar|
|
304
|
+
vr = ar.value_range
|
305
|
+
min = vr.minimum_bound
|
306
|
+
max = vr.maximum_bound
|
307
|
+
if (min && max && max.value.literal == min.value.literal)
|
308
|
+
"#{column_name} = #{sql_value(min.value)}"
|
309
|
+
else
|
310
|
+
inequalities = [
|
311
|
+
min && "#{column_name} >#{min.is_inclusive ? "=" : ""} #{sql_value(min.value)}",
|
312
|
+
max && "#{column_name} <#{max.is_inclusive ? "=" : ""} #{sql_value(max.value)}"
|
313
|
+
].compact
|
314
|
+
inequalities.size > 1 ? "(" + inequalities*" AND " + ")" : inequalities[0]
|
315
|
+
end
|
316
|
+
end*" OR " +
|
317
|
+
")"
|
318
|
+
end
|
319
|
+
|
320
|
+
class SQLDataTypeContext < MM::DataType::Context
|
321
|
+
def integer_ranges
|
322
|
+
[
|
323
|
+
['SMALLINT', -2**15, 2**15-1], # The standard says -10^5..10^5 (less than 16 bits)
|
324
|
+
['INTEGER', -2**31, 2**31-1], # The standard says -10^10..10^10 (more than 32 bits!)
|
325
|
+
['BIGINT', -2**63, 2**63-1], # The standard says -10^19..10^19 (less than 64 bits)
|
326
|
+
]
|
327
|
+
end
|
328
|
+
|
329
|
+
def default_length data_type, type_name
|
330
|
+
case data_type
|
331
|
+
when MM::DataType::TYPE_Real
|
332
|
+
53 # IEEE Double precision floating point
|
333
|
+
when MM::DataType::TYPE_Integer
|
334
|
+
case type_name
|
335
|
+
when /([a-z ]|\b)Tiny([a-z ]|\b)/i
|
336
|
+
8
|
337
|
+
when /([a-z ]|\b)Small([a-z ]|\b)/i,
|
338
|
+
/([a-z ]|\b)Short([a-z ]|\b)/i
|
339
|
+
16
|
340
|
+
when /([a-z ]|\b)Big(INT)?([a-z ]|\b)/i
|
341
|
+
64
|
342
|
+
else
|
343
|
+
32
|
344
|
+
end
|
345
|
+
else
|
346
|
+
nil
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def boolean_type
|
351
|
+
'BOOLEAN'
|
352
|
+
end
|
353
|
+
|
354
|
+
def surrogate_type
|
355
|
+
type_name, = choose_integer_type(0, 2**(default_surrogate_length-1)-1)
|
356
|
+
type_name
|
357
|
+
end
|
358
|
+
|
359
|
+
def valid_from_type
|
360
|
+
'TIMESTAMP'
|
361
|
+
end
|
362
|
+
|
363
|
+
def date_time_type
|
364
|
+
'TIMESTAMP'
|
365
|
+
end
|
366
|
+
|
367
|
+
def default_char_type
|
368
|
+
(@unicode ? 'NATIONAL ' : '') +
|
369
|
+
'CHARACTER'
|
370
|
+
end
|
371
|
+
|
372
|
+
def default_varchar_type
|
373
|
+
(@unicode ? 'NATIONAL ' : '') +
|
374
|
+
'VARCHAR'
|
375
|
+
end
|
376
|
+
|
377
|
+
def char_default_length
|
378
|
+
nil
|
379
|
+
end
|
380
|
+
|
381
|
+
def varchar_default_length
|
382
|
+
nil
|
383
|
+
end
|
384
|
+
|
385
|
+
def default_surrogate_length
|
386
|
+
64
|
387
|
+
end
|
388
|
+
|
389
|
+
def default_text_type
|
390
|
+
default_varchar_type
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def safe_table_name composite
|
395
|
+
escape(table_name(composite), table_name_max)
|
396
|
+
end
|
397
|
+
|
398
|
+
def safe_column_name component
|
399
|
+
escape(column_name(component), column_name_max)
|
400
|
+
end
|
401
|
+
|
402
|
+
def table_name composite
|
403
|
+
composite.mapping.name.gsub(' ', @underscore)
|
404
|
+
end
|
405
|
+
|
406
|
+
def column_name component
|
407
|
+
component.column_name.capcase
|
425
408
|
end
|
426
409
|
|
427
|
-
MM = ActiveFacts::Metamodel
|
428
410
|
end
|
429
411
|
publish_generator SQL
|
430
412
|
end
|