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
@@ -13,6 +13,7 @@ require 'activefacts/metamodel/datatypes'
|
|
13
13
|
require 'activefacts/compositions'
|
14
14
|
require 'activefacts/compositions/names'
|
15
15
|
require 'activefacts/generator'
|
16
|
+
require 'activefacts/generator/traits/sql'
|
16
17
|
|
17
18
|
module ActiveFacts
|
18
19
|
module Generators
|
@@ -20,52 +21,12 @@ module ActiveFacts
|
|
20
21
|
# * delay_fks Leave all foreign keys until the end, not just those that contain forward-references
|
21
22
|
# * underscore
|
22
23
|
class SQL
|
23
|
-
|
24
|
-
|
25
|
-
{
|
26
|
-
delay_fks: ['Boolean', "Delay emitting all foreign keys until the bottom of the file"],
|
27
|
-
keywords: ['Boolean', "Quote all keywords, not just reserved words"],
|
28
|
-
restrict: ['String', "Restrict generation to tables in the specified group (e.g. bdv, rdv)"],
|
29
|
-
joiner: ['String', "Use 'str' instead of the default joiner between words in table and column names"],
|
30
|
-
unicode: ['Boolean', "Use Unicode for all text fields by default"],
|
31
|
-
tables: [%w{cap title camel snake shout}, "Case to use for table names"],
|
32
|
-
columns: [%w{cap title camel snake shout}, "Case to use for table names"],
|
33
|
-
# Legacy: datavault: ['String', "Generate 'raw' or 'business' data vault tables"],
|
34
|
-
}
|
35
|
-
end
|
24
|
+
include Traits::SQL
|
25
|
+
extend Traits::SQL
|
36
26
|
|
37
27
|
def initialize composition, options = {}
|
38
28
|
@composition = composition
|
39
|
-
|
40
|
-
@quote_keywords = {nil=>true, 't'=>true, 'f'=>false, 'y'=>true, 'n'=>false}[options.delete 'keywords']
|
41
|
-
@quote_keywords = false if @keywords == nil # Set default
|
42
|
-
@delay_fks = options.delete "delay_fks"
|
43
|
-
@unicode = options.delete "unicode"
|
44
|
-
@restrict = options.delete "restrict"
|
45
|
-
|
46
|
-
# Name configuration options:
|
47
|
-
@joiner = options.delete('joiner')
|
48
|
-
@table_joiner = options.has_key?('tables') ? @joiner : nil
|
49
|
-
@table_case = ((options.delete('tables') || 'cap') + 'words').to_sym
|
50
|
-
@table_joiner ||= [:snakewords, :shoutwords].include?(@table_case) ? '_' : ''
|
51
|
-
@column_joiner = options.has_key?('columns') ? @joiner : nil
|
52
|
-
@column_case = ((options.delete('columns') || 'cap') + 'words').to_sym
|
53
|
-
@column_joiner ||= [:snakewords, :shoutwords].include?(@column_case) ? '_' : ''
|
54
|
-
|
55
|
-
# Legacy option. Use restrict=bdv/rdv instead
|
56
|
-
@datavault = options.delete "datavault"
|
57
|
-
case @datavault
|
58
|
-
when "business"
|
59
|
-
@restrict = "bdv"
|
60
|
-
when "raw"
|
61
|
-
@restrict = "rdv"
|
62
|
-
end
|
63
|
-
|
64
|
-
# Do not (yet) expose the closed-world vs open world problem.
|
65
|
-
# Closed World vs Open World uniqueness is a semantic issue,
|
66
|
-
# and so is OW, CW or CW with negation for unary fact types.
|
67
|
-
# We need an overall strategy for handling it.
|
68
|
-
@closed_world_indices = false # Allow for SQL Server's non-standard NULL indexing
|
29
|
+
process_options options
|
69
30
|
end
|
70
31
|
|
71
32
|
def generate
|
@@ -82,34 +43,13 @@ module ActiveFacts
|
|
82
43
|
@delayed_foreign_keys.sort*"\n"
|
83
44
|
end
|
84
45
|
|
85
|
-
def data_type_context
|
86
|
-
@data_type_context ||= SQLDataTypeContext.new
|
87
|
-
end
|
88
|
-
|
89
|
-
def table_name_max
|
90
|
-
60
|
91
|
-
end
|
92
|
-
|
93
|
-
def column_name_max
|
94
|
-
40
|
95
|
-
end
|
96
|
-
|
97
|
-
def index_name_max
|
98
|
-
60
|
99
|
-
end
|
100
|
-
|
101
|
-
def schema_name_max
|
102
|
-
60
|
103
|
-
end
|
104
|
-
|
105
46
|
def generate_schema
|
106
|
-
|
107
|
-
''
|
47
|
+
schema_prefix
|
108
48
|
end
|
109
49
|
|
110
50
|
def generate_table composite
|
111
51
|
@tables_emitted[composite] = true
|
112
|
-
|
52
|
+
@delayed_statements = []
|
113
53
|
|
114
54
|
"CREATE TABLE #{safe_table_name composite} (\n" +
|
115
55
|
(
|
@@ -120,9 +60,10 @@ module ActiveFacts
|
|
120
60
|
generate_column leaf
|
121
61
|
end +
|
122
62
|
composite.all_index.map do |index|
|
123
|
-
generate_index index
|
63
|
+
generate_index index
|
124
64
|
end.compact.sort +
|
125
65
|
composite.all_foreign_key_as_source_composite.map do |fk|
|
66
|
+
next nil if @fks == false
|
126
67
|
fk_text = generate_foreign_key fk
|
127
68
|
if !@delay_fks and # We're not delaying foreign keys unnecessarily
|
128
69
|
@tables_emitted[fk.composite] || # Already done
|
@@ -138,31 +79,30 @@ module ActiveFacts
|
|
138
79
|
'-- '+constraint.inspect # REVISIT: Emit local constraints
|
139
80
|
end
|
140
81
|
).compact.flat_map{|f| "\t#{f}" }*",\n"+"\n" +
|
141
|
-
go(")") +
|
142
|
-
|
143
|
-
go
|
144
|
-
end*
|
82
|
+
go(")") + "\n" +
|
83
|
+
@delayed_statements.sort.map do |delayed_statement|
|
84
|
+
go delayed_statement
|
85
|
+
end*''
|
145
86
|
end
|
146
87
|
|
147
88
|
def generate_column leaf
|
148
89
|
column_name = safe_column_name(leaf)
|
149
|
-
padding = " "*(column_name.size >=
|
90
|
+
padding = " "*(column_name.size >= 40 ? 1 : 40-column_name.size)
|
150
91
|
constraints = leaf.all_leaf_constraint
|
151
92
|
|
152
93
|
"-- #{leaf.comment}\n" +
|
153
|
-
"\t#{column_name}#{padding}#{column_type
|
154
|
-
end
|
155
|
-
|
156
|
-
def auto_assign_modifier
|
157
|
-
' GENERATED ALWAYS AS IDENTITY'
|
94
|
+
"\t#{column_name}#{padding}#{column_type(leaf, column_name)}"
|
158
95
|
end
|
159
96
|
|
160
97
|
def column_type component, column_name
|
98
|
+
# Get the base data type name and options:
|
161
99
|
type_name, options = component.data_type(data_type_context)
|
162
100
|
options ||= {}
|
163
|
-
length = options[:length]
|
164
101
|
value_constraint = options[:value_constraint]
|
165
|
-
type_name
|
102
|
+
type_name = choose_sql_type(type_name, value_constraint, component, options)
|
103
|
+
@delayed_statements += options.delete(:delayed) if options[:delayed]
|
104
|
+
length = options[:length]
|
105
|
+
return options[:computed] if options[:computed]
|
166
106
|
|
167
107
|
"#{
|
168
108
|
type_name
|
@@ -172,18 +112,12 @@ module ActiveFacts
|
|
172
112
|
((options[:mandatory] ? ' NOT' : '') + ' NULL') if options.has_key?(:mandatory)
|
173
113
|
}#{
|
174
114
|
options[:default] || ''
|
175
|
-
}#{
|
176
|
-
auto_assign_modifier if a = options[:auto_assign] && a != 'assert'
|
177
115
|
}#{
|
178
116
|
check_clause(column_name, value_constraint) if value_constraint
|
179
117
|
}"
|
180
118
|
end
|
181
119
|
|
182
|
-
def
|
183
|
-
''
|
184
|
-
end
|
185
|
-
|
186
|
-
def generate_index index, delayed_indices
|
120
|
+
def generate_index index
|
187
121
|
nullable_columns =
|
188
122
|
index.all_index_field.select do |ixf|
|
189
123
|
!ixf.component.path_mandatory
|
@@ -198,25 +132,40 @@ module ActiveFacts
|
|
198
132
|
column_name(ixf.component)
|
199
133
|
end
|
200
134
|
|
201
|
-
if
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
135
|
+
if index.is_unique
|
136
|
+
if contains_nullable_columns and @closed_world_indices
|
137
|
+
# Implement open-world uniqueness using a filtered index:
|
138
|
+
table_name = safe_table_name(index.composite)
|
139
|
+
@delayed_statements <<
|
140
|
+
'CREATE UNIQUE'+index_kind(index)+' INDEX '+
|
141
|
+
escape("#{table_name(index.composite)}By#{column_names*''}", index_name_max) +
|
142
|
+
" ON #{table_name}("+column_names.map{|n| escape(n, column_name_max)}*', ' +
|
143
|
+
") WHERE #{
|
144
|
+
nullable_columns.
|
145
|
+
map{|ixf| safe_column_name ixf.component}.
|
146
|
+
map{|column_name| column_name + ' IS NOT NULL'} *
|
147
|
+
' AND '
|
148
|
+
}"
|
149
|
+
nil # Nothing inline
|
150
|
+
else
|
151
|
+
'-- '+index.inspect + "\n\t" +
|
152
|
+
(primary ? 'PRIMARY KEY' : 'UNIQUE') +
|
153
|
+
index_kind(index) +
|
154
|
+
"(#{column_names.map{|n| escape(n, column_name_max)}*', '})"
|
155
|
+
end
|
215
156
|
else
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
157
|
+
# REVISIT: If the fields of this index is a prefix of another index, it can be omitted
|
158
|
+
tn = table_name(index.composite)
|
159
|
+
create_index =
|
160
|
+
'CREATE'+index_kind(index)+' INDEX '+
|
161
|
+
escape("#{tn}By#{column_names*''}", index_name_max) +
|
162
|
+
" ON #{tn}(" +
|
163
|
+
column_names.map{|n|
|
164
|
+
escape(n, column_name_max)
|
165
|
+
}*', ' +
|
166
|
+
')'
|
167
|
+
@delayed_statements << create_index
|
168
|
+
nil # Nothing inline
|
220
169
|
end
|
221
170
|
end
|
222
171
|
|
@@ -228,325 +177,8 @@ module ActiveFacts
|
|
228
177
|
fk.all_index_field.map{|ixf| safe_column_name ixf.component}*", " +
|
229
178
|
")"
|
230
179
|
end
|
231
|
-
|
232
|
-
# Return SQL type and (modified?) length for the passed base type
|
233
|
-
def normalise_type(type_name, length, value_constraint, options)
|
234
|
-
type = MM::DataType.normalise(type_name)
|
235
|
-
|
236
|
-
case type
|
237
|
-
when MM::DataType::TYPE_Boolean; data_type_context.boolean_type
|
238
|
-
when MM::DataType::TYPE_Integer
|
239
|
-
# The :auto_assign key is set for auto-assigned types, but with a nil value in foreign keys
|
240
|
-
if options.has_key?(:auto_assign)
|
241
|
-
MM::DataType.normalise_int_length(
|
242
|
-
'int',
|
243
|
-
data_type_context.default_surrogate_length,
|
244
|
-
value_constraint,
|
245
|
-
data_type_context
|
246
|
-
)[0]
|
247
|
-
else
|
248
|
-
v, = MM::DataType.normalise_int_length(type_name, length, value_constraint, data_type_context)
|
249
|
-
v # The typename here has the appropriate length, don't return a length
|
250
|
-
end
|
251
|
-
when MM::DataType::TYPE_Real;
|
252
|
-
["FLOAT", data_type_context.default_length(type, type_name)]
|
253
|
-
when MM::DataType::TYPE_Decimal; ['DECIMAL', length]
|
254
|
-
when MM::DataType::TYPE_Money; ['DECIMAL', length]
|
255
|
-
when MM::DataType::TYPE_Char; [data_type_context.default_char_type, length || data_type_context.char_default_length]
|
256
|
-
when MM::DataType::TYPE_String; [data_type_context.default_varchar_type, length || data_type_context.varchar_default_length]
|
257
|
-
when MM::DataType::TYPE_Text; [data_type_context.default_text_type, length || 'MAX']
|
258
|
-
when MM::DataType::TYPE_Date; 'DATE' # SQLSVR 2K5: 'date'
|
259
|
-
when MM::DataType::TYPE_Time; 'TIME' # SQLSVR 2K5: 'time'
|
260
|
-
when MM::DataType::TYPE_DateTime; 'TIMESTAMP'
|
261
|
-
when MM::DataType::TYPE_Timestamp;'TIMESTAMP'
|
262
|
-
when MM::DataType::TYPE_Binary;
|
263
|
-
if type_name =~ /^(guid|uuid)$/i && (!length || length == 16)
|
264
|
-
length ||= 16
|
265
|
-
if ![nil, ''].include?(options[:auto_assign])
|
266
|
-
options.delete(:auto_assign) # Don't auto-assign foreign keys
|
267
|
-
end
|
268
|
-
end
|
269
|
-
if length
|
270
|
-
['BINARY', length]
|
271
|
-
else
|
272
|
-
['VARBINARY', length]
|
273
|
-
end
|
274
|
-
else
|
275
|
-
[type_name, length]
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
def reserved_words
|
280
|
-
@reserved_words ||= %w{
|
281
|
-
ABS ABSOLUTE ACTION ADD ALL ALLOCATE ALTER AND ANY ARE
|
282
|
-
ARRAY ARRAY_AGG ARRAY_MAX_CARDINALITY AS ASC ASENSITIVE
|
283
|
-
ASSERTION ASYMMETRIC AT ATOMIC AUTHORIZATION AVG BEGIN
|
284
|
-
BEGIN_FRAME BEGIN_PARTITION BETWEEN BIGINT BINARY BIT
|
285
|
-
BIT_LENGTH BLOB BOOLEAN BOTH BY CALL CALLED CARDINALITY
|
286
|
-
CASCADE CASCADED CASE CAST CATALOG CEIL CEILING CHAR
|
287
|
-
CHARACTER CHARACTER_LENGTH CHAR_LENGTH CHECK CLOB CLOSE
|
288
|
-
COALESCE COLLATE COLLATION COLLECT COLUMN COMMIT CONDITION
|
289
|
-
CONNECT CONNECTION CONSTRAINT CONSTRAINTS CONTAINS CONTINUE
|
290
|
-
CONVERT CORR CORRESPONDING COUNT COVAR_POP COVAR_SAMP
|
291
|
-
CREATE CROSS CUBE CUME_DIST CURRENT CURRENT_CATALOG
|
292
|
-
CURRENT_DATE CURRENT_DEFAULT_TRANSFORM_GROUP CURRENT_PATH
|
293
|
-
CURRENT_ROLE CURRENT_ROW CURRENT_SCHEMA CURRENT_TIME
|
294
|
-
CURRENT_TIMESTAMP CURRENT_TRANSFORM_GROUP_FOR_TYPE
|
295
|
-
CURRENT_USER CURSOR CYCLE DATALINK DATE DAY DEALLOCATE
|
296
|
-
DEC DECIMAL DECLARE DEFAULT DEFERRABLE DEFERRED DELETE
|
297
|
-
DENSE_RANK DEREF DESC DESCRIBE DESCRIPTOR DETERMINISTIC
|
298
|
-
DIAGNOSTICS DISCONNECT DISTINCT DLNEWCOPY DLPREVIOUSCOPY
|
299
|
-
DLURLCOMPLETE DLURLCOMPLETEONLY DLURLCOMPLETEWRITE DLURLPATH
|
300
|
-
DLURLPATHONLY DLURLPATHWRITE DLURLSCHEME DLURLSERVER
|
301
|
-
DLVALUE DO DOMAIN DOUBLE DROP DYNAMIC EACH ELEMENT ELSE
|
302
|
-
ELSEIF END END-EXEC END_FRAME END_PARTITION EQUALS ESCAPE
|
303
|
-
EVERY EXCEPT EXCEPTION EXEC EXECUTE EXISTS EXIT EXP
|
304
|
-
EXTERNAL EXTRACT FALSE FETCH FILTER FIRST FIRST_VALUE
|
305
|
-
FLOAT FLOOR FOR FOREIGN FOUND FRAME_ROW FREE FROM FULL
|
306
|
-
FUNCTION FUSION GET GLOBAL GO GOTO GRANT GROUP GROUPING
|
307
|
-
GROUPS HANDLER HAVING HOLD HOUR IDENTITY IF IMMEDIATE
|
308
|
-
IMPORT IN INDICATOR INITIALLY INNER INOUT INPUT INSENSITIVE
|
309
|
-
INSERT INT INTEGER INTERSECT INTERSECTION INTERVAL INTO
|
310
|
-
IS ISOLATION ITERATE JOIN KEY LAG LANGUAGE LARGE LAST
|
311
|
-
LAST_VALUE LATERAL LEAD LEADING LEAVE LEFT LEVEL LIKE
|
312
|
-
LIKE_REGEX LN LOCAL LOCALTIME LOCALTIMESTAMP LOOP LOWER
|
313
|
-
MATCH MAX MAX_CARDINALITY MEMBER MERGE METHOD MIN MINUTE
|
314
|
-
MOD MODIFIES MODULE MONTH MULTISET NAMES NATIONAL NATURAL
|
315
|
-
NCHAR NCLOB NEW NEXT NO NONE NORMALIZE NOT NTH_VALUE NTILE
|
316
|
-
NULL NULLIF NUMERIC OCCURRENCES_REGEX OCTET_LENGTH OF
|
317
|
-
OFFSET OLD ON ONLY OPEN OPTION OR ORDER OUT OUTER OUTPUT
|
318
|
-
OVER OVERLAPS OVERLAY PAD PARAMETER PARTIAL PARTITION
|
319
|
-
PERCENT PERCENTILE_CONT PERCENTILE_DISC PERCENT_RANK
|
320
|
-
PERIOD PORTION POSITION POSITION_REGEX POWER PRECEDES
|
321
|
-
PRECISION PREPARE PRESERVE PRIMARY PRIOR PRIVILEGES
|
322
|
-
PROCEDURE PUBLIC RANGE RANK READ READS REAL RECURSIVE REF
|
323
|
-
REFERENCES REFERENCING REGR_AVGX REGR_AVGY REGR_COUNT
|
324
|
-
REGR_INTERCEPT REGR_R2 REGR_SLOPE REGR_SXX REGR_SXY
|
325
|
-
REGR_SYY RELATIVE RELEASE REPEAT RESIGNAL RESTRICT RESULT
|
326
|
-
RETURN RETURNS REVOKE RIGHT ROLLBACK ROLLUP ROW ROWS
|
327
|
-
ROW_NUMBER SAVEPOINT SCHEMA SCOPE SCROLL SEARCH SECOND
|
328
|
-
SECTION SELECT SENSITIVE SESSION SESSION_USER SET SIGNAL
|
329
|
-
SIMILAR SIZE SMALLINT SOME SPACE SPECIFIC SPECIFICTYPE
|
330
|
-
SQL SQLCODE SQLERROR SQLEXCEPTION SQLSTATE SQLWARNING
|
331
|
-
SQRT START STATIC STDDEV_POP STDDEV_SAMP SUBMULTISET
|
332
|
-
SUBSTRING SUBSTRING_REGEX SUCCEEDS SUM SYMMETRIC SYSTEM
|
333
|
-
SYSTEM_TIME SYSTEM_USER TABLE TABLESAMPLE TEMPORARY THEN
|
334
|
-
TIME TIMESTAMP TIMEZONE_HOUR TIMEZONE_MINUTE TO TRAILING
|
335
|
-
TRANSACTION TRANSLATE TRANSLATE_REGEX TRANSLATION TREAT
|
336
|
-
TRIGGER TRIM TRIM_ARRAY TRUE TRUNCATE UESCAPE UNDO UNION
|
337
|
-
UNIQUE UNKNOWN UNNEST UNTIL UPDATE UPPER USAGE USER USING
|
338
|
-
VALUE VALUES VALUE_OF VARBINARY VARCHAR VARYING VAR_POP
|
339
|
-
VAR_SAMP VERSIONING VIEW WHEN WHENEVER WHERE WHILE
|
340
|
-
WIDTH_BUCKET WINDOW WITH WITHIN WITHOUT WORK WRITE XML
|
341
|
-
XMLAGG XMLATTRIBUTES XMLBINARY XMLCAST XMLCOMMENT XMLCONCAT
|
342
|
-
XMLDOCUMENT XMLELEMENT XMLEXISTS XMLFOREST XMLITERATE
|
343
|
-
XMLNAMESPACES XMLPARSE XMLPI XMLQUERY XMLSERIALIZE XMLTABLE
|
344
|
-
XMLTEXT XMLVALIDATE YEAR ZONE
|
345
|
-
}
|
346
|
-
end
|
347
|
-
|
348
|
-
def key_words
|
349
|
-
@key_words ||= %w{
|
350
|
-
A ABSENT ACCORDING ADA ADMIN AFTER ALWAYS ASSIGNMENT
|
351
|
-
ATTRIBUTE ATTRIBUTES BASE64 BEFORE BERNOULLI BLOCKED BOM
|
352
|
-
BREADTH C CATALOG_NAME CHAIN CHARACTERISTICS CHARACTERS
|
353
|
-
CHARACTER_SET_CATALOG CHARACTER_SET_NAME CHARACTER_SET_SCHEMA
|
354
|
-
CLASS_ORIGIN COBOL COLLATION_CATALOG COLLATION_NAME
|
355
|
-
COLLATION_SCHEMA COLUMNS COLUMN_NAME COMMAND_FUNCTION
|
356
|
-
COMMAND_FUNCTION_CODE COMMITTED CONDITION_NUMBER
|
357
|
-
CONNECTION_NAME CONSTRAINT_CATALOG CONSTRAINT_NAME
|
358
|
-
CONSTRAINT_SCHEMA CONSTRUCTOR CONTENT CONTROL CURSOR_NAME
|
359
|
-
DATA DATETIME_INTERVAL_CODE DATETIME_INTERVAL_PRECISION
|
360
|
-
DB DEFAULTS DEFINED DEFINER DEGREE DEPTH DERIVED DISPATCH
|
361
|
-
DOCUMENT DYNAMIC_FUNCTION DYNAMIC_FUNCTION_CODE EMPTY
|
362
|
-
ENCODING ENFORCED EXCLUDE EXCLUDING EXPRESSION FILE FINAL
|
363
|
-
FLAG FOLLOWING FORTRAN FS G GENERAL GENERATED GRANTED HEX
|
364
|
-
HIERARCHY ID IGNORE IMMEDIATELY IMPLEMENTATION INCLUDING
|
365
|
-
INCREMENT INDENT INSTANCE INSTANTIABLE INSTEAD INTEGRITY
|
366
|
-
INVOKER K KEY_MEMBER KEY_TYPE LENGTH LIBRARY LIMIT LINK
|
367
|
-
LOCATION LOCATOR M MAP MAPPING MATCHED MAXVALUE MESSAGE_LENGTH
|
368
|
-
MESSAGE_OCTET_LENGTH MESSAGE_TEXT MINVALUE MORE MUMPS
|
369
|
-
NAME NAMESPACE NESTING NFC NFD NFKC NFKD NIL NORMALIZED
|
370
|
-
NULLABLE NULLS NUMBER OBJECT OCTETS OFF OPTIONS ORDERING
|
371
|
-
ORDINALITY OTHERS OVERRIDING P PARAMETER_MODE PARAMETER_NAME
|
372
|
-
PARAMETER_ORDINAL_POSITION PARAMETER_SPECIFIC_CATALOG
|
373
|
-
PARAMETER_SPECIFIC_NAME PARAMETER_SPECIFIC_SCHEMA PASCAL
|
374
|
-
PASSING PASSTHROUGH PATH PERMISSION PLACING PLI PRECEDING
|
375
|
-
RECOVERY REPEATABLE REQUIRING RESPECT RESTART RESTORE
|
376
|
-
RETURNED_CARDINALITY RETURNED_LENGTH RETURNED_OCTET_LENGTH
|
377
|
-
RETURNED_SQLSTATE RETURNING ROLE ROUTINE ROUTINE_CATALOG
|
378
|
-
ROUTINE_NAME ROUTINE_SCHEMA ROW_COUNT SCALE SCHEMA_NAME
|
379
|
-
SCOPE_CATALOG SCOPE_NAME SCOPE_SCHEMA SECURITY SELECTIVE
|
380
|
-
SELF SEQUENCE SERIALIZABLE SERVER SERVER_NAME SETS SIMPLE
|
381
|
-
SOURCE SPECIFIC_NAME STANDALONE STATE STATEMENT STRIP
|
382
|
-
STRUCTURE STYLE SUBCLASS_ORIGIN T TABLE_NAME TIES TOKEN
|
383
|
-
TOP_LEVEL_COUNT TRANSACTIONS_COMMITTED TRANSACTIONS_ROLLED_BACK
|
384
|
-
TRANSACTION_ACTIVE TRANSFORM TRANSFORMS TRIGGER_CATALOG
|
385
|
-
TRIGGER_NAME TRIGGER_SCHEMA TYPE UNBOUNDED UNCOMMITTED
|
386
|
-
UNDER UNLINK UNNAMED UNTYPED URI USER_DEFINED_TYPE_CATALOG
|
387
|
-
USER_DEFINED_TYPE_CODE USER_DEFINED_TYPE_NAME
|
388
|
-
USER_DEFINED_TYPE_SCHEMA VALID VERSION WHITESPACE WRAPPER
|
389
|
-
XMLDECLARATION XMLSCHEMA YES
|
390
|
-
}
|
391
|
-
end
|
392
|
-
|
393
|
-
def is_reserved_word w
|
394
|
-
@reserved_word_hash ||=
|
395
|
-
( reserved_words +
|
396
|
-
(@quote_keywords ? key_words : [])).
|
397
|
-
inject({}) do |h,w|
|
398
|
-
h[w] = true
|
399
|
-
h
|
400
|
-
end
|
401
|
-
@reserved_word_hash[w.upcase]
|
402
|
-
end
|
403
|
-
|
404
|
-
def go s = ''
|
405
|
-
"#{s};\n\n"
|
406
|
-
end
|
407
|
-
|
408
|
-
def open_escape
|
409
|
-
'"'
|
410
|
-
end
|
411
|
-
|
412
|
-
def close_escape
|
413
|
-
'"'
|
414
|
-
end
|
415
|
-
|
416
|
-
def escape s, max = table_name_max
|
417
|
-
# Escape SQL keywords and non-identifiers
|
418
|
-
if s.size > max
|
419
|
-
excess = s[max..-1]
|
420
|
-
s = s[0...max-(excess.size/8)] +
|
421
|
-
Digest::SHA1.hexdigest(excess)[0...excess.size/8]
|
422
|
-
end
|
423
|
-
|
424
|
-
if s =~ /[^A-Za-z0-9_]/ || is_reserved_word(s)
|
425
|
-
"#{open_escape}#{s}#{close_escape}"
|
426
|
-
else
|
427
|
-
s
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
def sql_value(value)
|
432
|
-
value.is_literal_string ? sql_string(value.literal) : value.literal
|
433
|
-
end
|
434
|
-
|
435
|
-
def sql_string(str)
|
436
|
-
"'" + str.gsub(/'/,"''") + "'"
|
437
|
-
end
|
438
|
-
|
439
|
-
def check_clause column_name, value_constraint
|
440
|
-
" CHECK(" +
|
441
|
-
value_constraint.all_allowed_range_sorted.map do |ar|
|
442
|
-
vr = ar.value_range
|
443
|
-
min = vr.minimum_bound
|
444
|
-
max = vr.maximum_bound
|
445
|
-
if (min && max && max.value.literal == min.value.literal)
|
446
|
-
"#{column_name} = #{sql_value(min.value)}"
|
447
|
-
else
|
448
|
-
inequalities = [
|
449
|
-
min && "#{column_name} >#{min.is_inclusive ? "=" : ""} #{sql_value(min.value)}",
|
450
|
-
max && "#{column_name} <#{max.is_inclusive ? "=" : ""} #{sql_value(max.value)}"
|
451
|
-
].compact
|
452
|
-
inequalities.size > 1 ? "(" + inequalities*" AND " + ")" : inequalities[0]
|
453
|
-
end
|
454
|
-
end*" OR " +
|
455
|
-
")"
|
456
|
-
end
|
457
|
-
|
458
|
-
class SQLDataTypeContext < MM::DataType::Context
|
459
|
-
def integer_ranges
|
460
|
-
[
|
461
|
-
['SMALLINT', -2**15, 2**15-1], # The standard says -10^5..10^5 (less than 16 bits)
|
462
|
-
['INTEGER', -2**31, 2**31-1], # The standard says -10^10..10^10 (more than 32 bits!)
|
463
|
-
['BIGINT', -2**63, 2**63-1], # The standard says -10^19..10^19 (less than 64 bits)
|
464
|
-
]
|
465
|
-
end
|
466
|
-
|
467
|
-
def default_length data_type, type_name
|
468
|
-
case data_type
|
469
|
-
when MM::DataType::TYPE_Real
|
470
|
-
53 # IEEE Double precision floating point
|
471
|
-
when MM::DataType::TYPE_Integer
|
472
|
-
case type_name
|
473
|
-
when /([a-z ]|\b)Tiny([a-z ]|\b)/i
|
474
|
-
8
|
475
|
-
when /([a-z ]|\b)Small([a-z ]|\b)/i,
|
476
|
-
/([a-z ]|\b)Short([a-z ]|\b)/i
|
477
|
-
16
|
478
|
-
when /([a-z ]|\b)Big(INT)?([a-z ]|\b)/i
|
479
|
-
64
|
480
|
-
else
|
481
|
-
32
|
482
|
-
end
|
483
|
-
else
|
484
|
-
nil
|
485
|
-
end
|
486
|
-
end
|
487
|
-
|
488
|
-
def boolean_type
|
489
|
-
'BOOLEAN'
|
490
|
-
end
|
491
|
-
|
492
|
-
def surrogate_type
|
493
|
-
type_name, = choose_integer_type(0, 2**(default_surrogate_length-1)-1)
|
494
|
-
type_name
|
495
|
-
end
|
496
|
-
|
497
|
-
def valid_from_type
|
498
|
-
'TIMESTAMP'
|
499
|
-
end
|
500
|
-
|
501
|
-
def date_time_type
|
502
|
-
'TIMESTAMP'
|
503
|
-
end
|
504
|
-
|
505
|
-
def default_char_type
|
506
|
-
(@unicode ? 'NATIONAL ' : '') +
|
507
|
-
'CHARACTER'
|
508
|
-
end
|
509
|
-
|
510
|
-
def default_varchar_type
|
511
|
-
(@unicode ? 'NATIONAL ' : '') +
|
512
|
-
'VARCHAR'
|
513
|
-
end
|
514
|
-
|
515
|
-
def char_default_length
|
516
|
-
nil
|
517
|
-
end
|
518
|
-
|
519
|
-
def varchar_default_length
|
520
|
-
nil
|
521
|
-
end
|
522
|
-
|
523
|
-
def default_surrogate_length
|
524
|
-
64
|
525
|
-
end
|
526
|
-
|
527
|
-
def default_text_type
|
528
|
-
default_varchar_type
|
529
|
-
end
|
530
|
-
end
|
531
|
-
|
532
|
-
def safe_table_name composite
|
533
|
-
escape(table_name(composite), table_name_max)
|
534
|
-
end
|
535
|
-
|
536
|
-
def safe_column_name component
|
537
|
-
escape(column_name(component), column_name_max)
|
538
|
-
end
|
539
|
-
|
540
|
-
def table_name composite
|
541
|
-
composite.mapping.name.words.send(@table_case)*@table_joiner
|
542
|
-
end
|
543
|
-
|
544
|
-
def column_name component
|
545
|
-
words = component.column_name.send(@column_case)
|
546
|
-
words*@column_joiner
|
547
|
-
end
|
548
|
-
|
549
180
|
end
|
181
|
+
|
550
182
|
publish_generator SQL
|
551
183
|
end
|
552
184
|
end
|
@@ -29,12 +29,14 @@ module ActiveFacts
|
|
29
29
|
class Composite
|
30
30
|
def summary
|
31
31
|
indices = self.all_indices_by_rank
|
32
|
+
fks = {}
|
33
|
+
fk_count = 0
|
32
34
|
|
33
35
|
(
|
34
36
|
[mapping.name+"\n"] +
|
35
37
|
mapping.
|
36
38
|
all_leaf.
|
37
|
-
reject{|leaf| leaf.is_a?(Absorption) && leaf.
|
39
|
+
reject{|leaf| leaf.is_a?(Absorption) && leaf.forward_mapping}.
|
38
40
|
flat_map do |leaf|
|
39
41
|
|
40
42
|
# Build a display of the names in this absorption path, with FK and optional indicators
|
@@ -49,10 +51,12 @@ module ActiveFacts
|
|
49
51
|
is_mandatory = false
|
50
52
|
end
|
51
53
|
|
52
|
-
if
|
53
|
-
|
54
|
-
|
55
|
-
"
|
54
|
+
# if all_foreign_key.detect{|fk| fk.all_foreign_key_field.detect{|fkf| fkf.component == leaf}}
|
55
|
+
if component.is_a?(Mapping) && component.foreign_key && leaf.all_foreign_key_field.size > 0
|
56
|
+
fk_number = (fks[component.foreign_key] ||= (fk_count += 1))
|
57
|
+
"[F#{fk_number}:#{component.name}"
|
58
|
+
elsif component == leaf && leaf.all_foreign_key_field.size > 0
|
59
|
+
"#{component.name}]"
|
56
60
|
else
|
57
61
|
component.name
|
58
62
|
end +
|
@@ -62,9 +66,14 @@ module ActiveFacts
|
|
62
66
|
# Build a symbolic representation of the index participation of this leaf
|
63
67
|
pos = 0
|
64
68
|
indexing = indices.inject([]) do |a, index|
|
69
|
+
# An index can be both Primary and Natural. Otherwise we show if it's Unique
|
70
|
+
type_str = ''
|
71
|
+
type_str << 'P' if index == primary_index
|
72
|
+
type_str << 'N' if index == natural_index
|
73
|
+
type_str = 'U' if type_str == '' && index.is_unique
|
65
74
|
pos += 1
|
66
75
|
if part = index.position_in_index(leaf)
|
67
|
-
a << "#{pos}.#{part}"
|
76
|
+
a << "#{type_str}#{pos}" + (index.all_index_field.size > 1 ? ".#{part}" : "")
|
68
77
|
end
|
69
78
|
a
|
70
79
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Generator Expression List Traits
|
3
|
+
#
|
4
|
+
# Each expression is a string which, evaluated in some context (usu. SQL),
|
5
|
+
# yields a single value of the specified type (perhaps an array value!)
|
6
|
+
#
|
7
|
+
# Copyright (c) 2017 Clifford Heath. Read the LICENSE file.
|
8
|
+
#
|
9
|
+
require 'activefacts/metamodel'
|
10
|
+
require 'activefacts/metamodel/datatypes'
|
11
|
+
require 'activefacts/generator'
|
12
|
+
|
13
|
+
module ActiveFacts
|
14
|
+
module Generators
|
15
|
+
class Expression
|
16
|
+
private
|
17
|
+
MM = ActiveFacts::Metamodel unless const_defined?(:MM)
|
18
|
+
public
|
19
|
+
attr_reader :type_num # ActiveFacts::Metamodel::DataType number
|
20
|
+
attr_reader :value # String representation of the expression
|
21
|
+
attr_reader :is_mandatory # false if nullable
|
22
|
+
attr_reader :is_array # the expression returns an array of the specified type
|
23
|
+
|
24
|
+
# Construct an expression that addresses a field from a Metamodel::Component
|
25
|
+
def initialize value, type_num, is_mandatory, is_array = false
|
26
|
+
@type_num = type_num
|
27
|
+
@value = value
|
28
|
+
@is_mandatory = is_mandatory
|
29
|
+
@is_array = is_array
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
value
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect
|
37
|
+
"Expression(#{value.inspect}, #{@type_num ? ActiveFacts::Metamodel::DataType::TypeNames[@type_num] : 'unknown'}, #{@is_mandatory ? 'mandatory' : 'nullable'}#{@is_array ? ', array' : ''})"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|