activefacts-compositions 1.9.14 → 1.9.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5462e874311293e93fc6e63406876290e8dafbe6
4
- data.tar.gz: 7f9de00e45fe7e5798d919f7df3e3b185b544b3c
3
+ metadata.gz: f1bf118df915877ab33968a6777b33a62cf37324
4
+ data.tar.gz: bd5400584518e29d8bdefc7caf593379805a6e35
5
5
  SHA512:
6
- metadata.gz: 0c98e0fdca3f53b07bf453dbfdf7848a5f04a4d825f3f13ae90d0312286c5422a5ffc7e3c80d971b447c5ef770e058b5ecaa688c863561a28636f1c6e199fc3d
7
- data.tar.gz: 1b950d16fbc463a42f869328e3b8c7acaba1666c2c72f96f2e7a959987f51b4c5e1ad1e0f76a1f8f8e4da4da97a027ea8d25ebcf060fe9db63644761cdf5f06d
6
+ metadata.gz: 2012ef677cea2524ef6ff02df61e07af2822faad359ae4975f375ab374760db27d0ba981064969a55fabfcc13d2d24ec690b620f7c93281fded26764cf2f98f3
7
+ data.tar.gz: 30771fd537a8249d6f12f4c197eb1925e73cc2dc41222c6cc381591895216902bd7b479da6176309eae85eb8021ab7dd506701cd8429dfbcc82c7a08b905173d
@@ -29,6 +29,6 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_development_dependency "activefacts", "~> 1", ">= 1.8"
31
31
  spec.add_runtime_dependency "activefacts-api", "~> 1", ">= 1.9.11"
32
- spec.add_runtime_dependency "activefacts-metamodel", "~> 1", ">= 1.9.15"
32
+ spec.add_runtime_dependency "activefacts-metamodel", "~> 1", ">= 1.9.19"
33
33
  spec.add_runtime_dependency "activefacts-cql", "~> 1", ">= 1.9"
34
34
  end
@@ -154,11 +154,14 @@ class SchemaCompositor
154
154
  ' num'
155
155
  when Pathname
156
156
  ' file'
157
+ when Array
158
+ " { #{type.map(&:to_s)*' | '} }"
157
159
  else
158
160
  ' str'
159
161
  end
160
162
 
161
- stream.puts "\t#{tag}#{' '*(24-tag.size)}#{description}"
163
+ spaces = (s = 24-tag.size) < 2 ? 2 : s
164
+ stream.puts "\t#{tag}#{' '*spaces}#{description}"
162
165
  end
163
166
  end
164
167
  end
@@ -37,6 +37,9 @@ module ActiveFacts
37
37
  # Figure out how best to absorb things to reduce the number of tables
38
38
  optimise_absorption
39
39
 
40
+ # If we have partitioned subtypes, make that happen
41
+ enact_partitioning
42
+
40
43
  # Actually make a Composite object for each table:
41
44
  make_composites
42
45
 
@@ -228,6 +231,17 @@ module ActiveFacts
228
231
  end
229
232
  end
230
233
 
234
+ def enact_partitioning
235
+ @constellation.EntityType.each do |key, object_type|
236
+ sti = object_type.all_type_inheritance_as_supertype
237
+ if sti.size > 0 && sti.size == sti.select{|ti| ti.assimilation == 'partitioned'}.size
238
+ trace :relational_optimiser, "Supertype #{object_type.name} is fully partitioned so not a table"
239
+ candidate = @candidates[object_type]
240
+ candidate.definitely_not_table
241
+ end
242
+ end
243
+ end
244
+
231
245
  # Remove the unused reverse absorptions:
232
246
  def delete_reverse_absorptions
233
247
  @binary_mappings.each do |object_type, mapping|
@@ -561,11 +575,13 @@ module ActiveFacts
561
575
  # This member is an Absorption. Process it recursively, absorbing all its members or just a key
562
576
  # depending on whether the absorbed object is a Composite (or absorbed into one) or not.
563
577
  def absorb_nested mapping, member, paths
564
- # Should we absorb a foreign key or the whole contents?
578
+ # Is this where we absorb a partitioned supertype?
579
+ is_partitioned = (ft = member.child_role.fact_type).is_a?(MM::TypeInheritance) && ft.assimilation == 'partitioned'
565
580
 
581
+ # Should we absorb a foreign key or the whole contents?
566
582
  child_object_type = member.child_role.object_type
567
583
  child_mapping = @binary_mappings[child_object_type]
568
- if child_mapping.composite
584
+ if child_mapping.composite && !is_partitioned
569
585
  trace :relational_columns?, "Absorbing FK to #{member.child_role.name} in #{member.inspect_reading}" do
570
586
  paths[member] = @constellation.ForeignKey(:new, source_composite: mapping.root, composite: child_mapping.composite, absorption: member)
571
587
  absorb_key member, child_mapping, paths
@@ -594,6 +610,7 @@ module ActiveFacts
594
610
  return
595
611
  end
596
612
 
613
+ # REVISIT: if is_partitioned, don't absorb sibling subtypes!
597
614
  trace :relational_columns?, "Absorbing all of #{member.child_role.name} in #{member.inspect_reading}" do
598
615
  absorb_all member, child_mapping, paths
599
616
  end
@@ -651,7 +668,7 @@ module ActiveFacts
651
668
 
652
669
  pcs = []
653
670
  newpaths = {}
654
- if mapping.composite || mapping.full_absorption
671
+ if mapping.composite || mapping.full_absorption || mapping.parent_role.fact_type.is_a?(MM::TypeInheritance)
655
672
  pcs = find_uniqueness_constraints(mapping)
656
673
 
657
674
  # Don't build an index from the same PresenceConstraint twice on the same composite (e.g. for a subtype)
@@ -676,9 +693,18 @@ module ActiveFacts
676
693
  end
677
694
  end
678
695
 
696
+ # Delete indexes that accrued no fields:
679
697
  newpaths.values.select{|ix| ix.all_index_field.size == 0}.each(&:retract)
680
698
  end
681
699
 
700
+ # We include the subtype role of a TypeInheritance if
701
+ # it provides_identification or the subtype is not partitioned
702
+ def included_subtype role
703
+ role == role.fact_type.subtype_role &&
704
+ !role.object_type.is_partitioned &&
705
+ role.fact_type.provides_identification
706
+ end
707
+
682
708
  # Find all PresenceConstraints to index the object in this Mapping
683
709
  def find_uniqueness_constraints mapping
684
710
  return [] unless mapping.object_type.is_a?(MM::EntityType)
@@ -686,11 +712,11 @@ module ActiveFacts
686
712
  start_roles =
687
713
  mapping.
688
714
  object_type.
689
- all_role_transitive. # Includes objectification roles for objectified fact types
715
+ all_role. # Includes objectification roles for objectified fact types
690
716
  select do |role|
691
717
  (role.is_unique || # Must be unique on near role
692
718
  role.fact_type.is_unary) && # Or be a unary role
693
- !(role.fact_type.is_a?(MM::TypeInheritance) && role == role.fact_type.supertype_role) # allow roles as subtype
719
+ (!role.fact_type.is_a?(MM::TypeInheritance) || included_subtype(role))
694
720
  end.
695
721
  map(&:counterpart). # (Same role if it's a unary)
696
722
  compact. # Ignore nil counterpart of a role in an n-ary
@@ -750,8 +776,8 @@ module ActiveFacts
750
776
  trace :relational_paths?, "Adding #{new_pcs.size} new indices for presence constraints on #{mapping.inspect}" do
751
777
  new_pcs.each do |pc|
752
778
  newpaths[pc] = index = @constellation.Index(:new, composite: mapping.root, is_unique: true, presence_constraint: pc)
753
- if mapping.object_type.preferred_identifier == pc and
754
- !@composition.all_full_absorption[mapping.object_type] and
779
+ if mapping.root.mapping.object_type.preferred_identifier == pc and
780
+ !@composition.all_full_absorption[mapping.object_type] and # REVISIT: This clause might now be unnecessary
755
781
  !mapping.root.natural_index
756
782
  mapping.root.natural_index = index
757
783
  mapping.root.primary_index ||= index # Not if we have a surrogate already
@@ -1,5 +1,5 @@
1
1
  module ActiveFacts
2
2
  module Compositions
3
- VERSION = "1.9.14"
3
+ VERSION = "1.9.15"
4
4
  end
5
5
  end
@@ -88,6 +88,11 @@ module ActiveFacts
88
88
  def generate_composite composite
89
89
  ar_table_name = composite.rails.plural_name
90
90
 
91
+ pi = composite.primary_index
92
+ unless pi
93
+ warn "Warning: Cannot generate schema for #{composite.mapping.name} because it has no primary key"
94
+ return nil
95
+ end
91
96
  pk = composite.primary_index.all_index_field.to_a
92
97
  if pk[0].component.is_auto_assigned
93
98
  identity_column = pk[0].component
@@ -20,20 +20,34 @@ module ActiveFacts
20
20
  def self.options
21
21
  {
22
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"],
23
+ keywords: ['Boolean', "Quote all keywords, not just reserved words"],
24
+ restrict: ['String', "Restrict generation to tables in the specified group (e.g. bdv, rdv)"],
25
+ joiner: ['String', "Use 'str' instead of the default joiner between words in table and column names"],
24
26
  unicode: ['Boolean', "Use Unicode for all text fields by default"],
25
- # datavault: ['String', "Generate 'raw' or 'business' data vault tables"],
26
- restrict: ['String', "Restrict generation to tables in the specified group (e.g. bdv, rdv)"]
27
+ tables: [%w{cap title camel snake shout}, "Case to use for table names"],
28
+ columns: [%w{cap title camel snake shout}, "Case to use for table names"],
29
+ # Legacy: datavault: ['String', "Generate 'raw' or 'business' data vault tables"],
27
30
  }
28
31
  end
29
32
 
30
33
  def initialize composition, options = {}
31
34
  @composition = composition
32
35
  @options = options
36
+ @quote_keywords = {nil=>true, 't'=>true, 'f'=>false, 'y'=>true, 'n'=>false}[options.delete 'keywords']
37
+ @quote_keywords = false if @keywords == nil # Set default
33
38
  @delay_fks = options.delete "delay_fks"
34
- @underscore = options.has_key?("underscore") ? (options['underscore'] || '_') : ''
35
39
  @unicode = options.delete "unicode"
36
40
  @restrict = options.delete "restrict"
41
+
42
+ # Name configuration options:
43
+ @joiner = options.delete('joiner')
44
+ @table_joiner = options.has_key?('tables') ? @joiner : nil
45
+ @table_case = ((options.delete('tables') || 'cap') + 'words').to_sym
46
+ @table_joiner ||= [:snakewords, :shoutwords].include?(@table_case) ? '_' : ''
47
+ @column_joiner = options.has_key?('columns') ? @joiner : nil
48
+ @column_case = ((options.delete('columns') || 'cap') + 'words').to_sym
49
+ @column_joiner ||= [:snakewords, :shoutwords].include?(@column_case) ? '_' : ''
50
+
37
51
  # Legacy option. Use restrict=bdv/rdv instead
38
52
  @datavault = options.delete "datavault"
39
53
  case @datavault
@@ -129,7 +143,7 @@ module ActiveFacts
129
143
  "\t#{column_name}#{padding}#{column_type leaf, column_name}"
130
144
  end
131
145
 
132
- def auto_assign_type
146
+ def auto_assign_modifier
133
147
  ' GENERATED ALWAYS AS IDENTITY'
134
148
  end
135
149
 
@@ -138,7 +152,7 @@ module ActiveFacts
138
152
  options ||= {}
139
153
  length = options[:length]
140
154
  value_constraint = options[:value_constraint]
141
- type_name, length = normalise_type(type_name, length, value_constraint)
155
+ type_name, length = normalise_type(type_name, length, value_constraint, options)
142
156
 
143
157
  "#{
144
158
  type_name
@@ -147,7 +161,9 @@ module ActiveFacts
147
161
  }#{
148
162
  ((options[:mandatory] ? ' NOT' : '') + ' NULL') if options.has_key?(:mandatory)
149
163
  }#{
150
- auto_assign_type if a = options[:auto_assign] && a != 'assert'
164
+ options[:default] || ''
165
+ }#{
166
+ auto_assign_modifier if a = options[:auto_assign] && a != 'assert'
151
167
  }#{
152
168
  check_clause(column_name, value_constraint) if value_constraint
153
169
  }"
@@ -201,14 +217,20 @@ module ActiveFacts
201
217
  end
202
218
 
203
219
  # Return SQL type and (modified?) length for the passed base type
204
- def normalise_type(type_name, length, value_constraint)
220
+ def normalise_type(type_name, length, value_constraint, options)
205
221
  type = MM::DataType.normalise(type_name)
206
222
 
207
223
  case type
208
224
  when MM::DataType::TYPE_Boolean; data_type_context.boolean_type
209
225
  when MM::DataType::TYPE_Integer
210
- if type_name =~ /^Auto ?Counter$/i
211
- MM::DataType.normalise_int_length('int', data_type_context.default_surrogate_length, value_constraint, data_type_context)[0]
226
+ # The :auto_assign key is set for auto-assigned types, but with a nil value in foreign keys
227
+ if options.has_key?(:auto_assign)
228
+ MM::DataType.normalise_int_length(
229
+ 'int',
230
+ data_type_context.default_surrogate_length,
231
+ value_constraint,
232
+ data_type_context
233
+ )[0]
212
234
  else
213
235
  v, = MM::DataType.normalise_int_length(type_name, length, value_constraint, data_type_context)
214
236
  v # The typename here has the appropriate length, don't return a length
@@ -238,50 +260,123 @@ module ActiveFacts
238
260
 
239
261
  def reserved_words
240
262
  @reserved_words ||= %w{
241
- ABSOLUTE ACTION ADD AFTER ALL ALLOCATE ALTER AND ANY ARE
242
- ARRAY AS ASC ASSERTION AT AUTHORIZATION BEFORE BEGIN
243
- BETWEEN BINARY BIT BLOB BOOLEAN BOTH BREADTH BY CALL
244
- CASCADE CASCADED CASE CAST CATALOG CHAR CHARACTER CHECK
245
- CLOB CLOSE COLLATE COLLATION COLUMN COMMIT CONDITION
246
- CONNECT CONNECTION CONSTRAINT CONSTRAINTS CONSTRUCTOR
247
- CONTINUE CORRESPONDING CREATE CROSS CUBE CURRENT CURRENT_DATE
248
- CURRENT_DEFAULT_TRANSFORM_GROUP CURRENT_TRANSFORM_GROUP_FOR_TYPE
249
- CURRENT_PATH CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP
250
- CURRENT_USER CURSOR CYCLE DATA DATE DAY DEALLOCATE DEC
251
- DECIMAL DECLARE DEFAULT DEFERRABLE DEFERRED DELETE DEPTH
252
- DEREF DESC DESCRIBE DESCRIPTOR DETERMINISTIC DIAGNOSTICS
253
- DISCONNECT DISTINCT DO DOMAIN DOUBLE DROP DYNAMIC EACH
254
- ELSE ELSEIF END EQUALS ESCAPE EXCEPT EXCEPTION EXEC EXECUTE
255
- EXISTS EXIT EXTERNAL FALSE FETCH FIRST FLOAT FOR FOREIGN
256
- FOUND FROM FREE FULL FUNCTION GENERAL GET GLOBAL GO GOTO
257
- GRANT GROUP GROUPING HANDLE HAVING HOLD HOUR IDENTITY IF
258
- IMMEDIATE IN INDICATOR INITIALLY INNER INOUT INPUT INSERT
259
- INT INTEGER INTERSECT INTERVAL INTO IS ISOLATION JOIN KEY
260
- LANGUAGE LARGE LAST LATERAL LEADING LEAVE LEFT LEVEL LIKE
261
- LOCAL LOCALTIME LOCALTIMESTAMP LOCATOR LOOP MAP MATCH
262
- METHOD MINUTE MODIFIES MODULE MONTH NAMES NATIONAL NATURAL
263
- NCHAR NCLOB NESTING NEW NEXT NO NONE NOT NULL NUMERIC
264
- OBJECT OF OLD ON ONLY OPEN OPTION OR ORDER ORDINALITY OUT
265
- OUTER OUTPUT OVERLAPS PAD PARAMETER PARTIAL PATH PRECISION
266
- PREPARE PRESERVE PRIMARY PRIOR PRIVILEGES PROCEDURE PUBLIC
267
- READ READS REAL RECURSIVE REDO REF REFERENCES REFERENCING
268
- RELATIVE RELEASE REPEAT RESIGNAL RESTRICT RESULT RETURN
269
- RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROUTINE ROW
270
- ROWS SAVEPOINT SCHEMA SCROLL SEARCH SECOND SECTION SELECT
271
- SESSION SESSION_USER SET SETS SIGNAL SIMILAR SIZE SMALLINT
272
- SOME SPACE SPECIFIC SPECIFICTYPE SQL SQLEXCEPTION SQLSTATE
273
- SQLWARNING START STATE STATIC SYSTEM_USER TABLE TEMPORARY
274
- THEN TIME TIMESTAMP TIMEZONE_HOUR TIMEZONE_MINUTE TO
275
- TRAILING TRANSACTION TRANSLATION TREAT TRIGGER TRUE UNDER
276
- UNDO UNION UNIQUE UNKNOWN UNNEST UNTIL UPDATE USAGE USER
277
- USING VALUE VALUES VARCHAR VARYING VIEW WHEN WHENEVER
278
- WHERE WHILE WITH WITHOUT WORK WRITE YEAR ZONE
263
+ ABS ABSOLUTE ACTION ADD ALL ALLOCATE ALTER AND ANY ARE
264
+ ARRAY ARRAY_AGG ARRAY_MAX_CARDINALITY AS ASC ASENSITIVE
265
+ ASSERTION ASYMMETRIC AT ATOMIC AUTHORIZATION AVG BEGIN
266
+ BEGIN_FRAME BEGIN_PARTITION BETWEEN BIGINT BINARY BIT
267
+ BIT_LENGTH BLOB BOOLEAN BOTH BY CALL CALLED CARDINALITY
268
+ CASCADE CASCADED CASE CAST CATALOG CEIL CEILING CHAR
269
+ CHARACTER CHARACTER_LENGTH CHAR_LENGTH CHECK CLOB CLOSE
270
+ COALESCE COLLATE COLLATION COLLECT COLUMN COMMIT CONDITION
271
+ CONNECT CONNECTION CONSTRAINT CONSTRAINTS CONTAINS CONTINUE
272
+ CONVERT CORR CORRESPONDING COUNT COVAR_POP COVAR_SAMP
273
+ CREATE CROSS CUBE CUME_DIST CURRENT CURRENT_CATALOG
274
+ CURRENT_DATE CURRENT_DEFAULT_TRANSFORM_GROUP CURRENT_PATH
275
+ CURRENT_ROLE CURRENT_ROW CURRENT_SCHEMA CURRENT_TIME
276
+ CURRENT_TIMESTAMP CURRENT_TRANSFORM_GROUP_FOR_TYPE
277
+ CURRENT_USER CURSOR CYCLE DATALINK DATE DAY DEALLOCATE
278
+ DEC DECIMAL DECLARE DEFAULT DEFERRABLE DEFERRED DELETE
279
+ DENSE_RANK DEREF DESC DESCRIBE DESCRIPTOR DETERMINISTIC
280
+ DIAGNOSTICS DISCONNECT DISTINCT DLNEWCOPY DLPREVIOUSCOPY
281
+ DLURLCOMPLETE DLURLCOMPLETEONLY DLURLCOMPLETEWRITE DLURLPATH
282
+ DLURLPATHONLY DLURLPATHWRITE DLURLSCHEME DLURLSERVER
283
+ DLVALUE DO DOMAIN DOUBLE DROP DYNAMIC EACH ELEMENT ELSE
284
+ ELSEIF END END-EXEC END_FRAME END_PARTITION EQUALS ESCAPE
285
+ EVERY EXCEPT EXCEPTION EXEC EXECUTE EXISTS EXIT EXP
286
+ EXTERNAL EXTRACT FALSE FETCH FILTER FIRST FIRST_VALUE
287
+ FLOAT FLOOR FOR FOREIGN FOUND FRAME_ROW FREE FROM FULL
288
+ FUNCTION FUSION GET GLOBAL GO GOTO GRANT GROUP GROUPING
289
+ GROUPS HANDLER HAVING HOLD HOUR IDENTITY IF IMMEDIATE
290
+ IMPORT IN INDICATOR INITIALLY INNER INOUT INPUT INSENSITIVE
291
+ INSERT INT INTEGER INTERSECT INTERSECTION INTERVAL INTO
292
+ IS ISOLATION ITERATE JOIN KEY LAG LANGUAGE LARGE LAST
293
+ LAST_VALUE LATERAL LEAD LEADING LEAVE LEFT LEVEL LIKE
294
+ LIKE_REGEX LN LOCAL LOCALTIME LOCALTIMESTAMP LOOP LOWER
295
+ MATCH MAX MAX_CARDINALITY MEMBER MERGE METHOD MIN MINUTE
296
+ MOD MODIFIES MODULE MONTH MULTISET NAMES NATIONAL NATURAL
297
+ NCHAR NCLOB NEW NEXT NO NONE NORMALIZE NOT NTH_VALUE NTILE
298
+ NULL NULLIF NUMERIC OCCURRENCES_REGEX OCTET_LENGTH OF
299
+ OFFSET OLD ON ONLY OPEN OPTION OR ORDER OUT OUTER OUTPUT
300
+ OVER OVERLAPS OVERLAY PAD PARAMETER PARTIAL PARTITION
301
+ PERCENT PERCENTILE_CONT PERCENTILE_DISC PERCENT_RANK
302
+ PERIOD PORTION POSITION POSITION_REGEX POWER PRECEDES
303
+ PRECISION PREPARE PRESERVE PRIMARY PRIOR PRIVILEGES
304
+ PROCEDURE PUBLIC RANGE RANK READ READS REAL RECURSIVE REF
305
+ REFERENCES REFERENCING REGR_AVGX REGR_AVGY REGR_COUNT
306
+ REGR_INTERCEPT REGR_R2 REGR_SLOPE REGR_SXX REGR_SXY
307
+ REGR_SYY RELATIVE RELEASE REPEAT RESIGNAL RESTRICT RESULT
308
+ RETURN RETURNS REVOKE RIGHT ROLLBACK ROLLUP ROW ROWS
309
+ ROW_NUMBER SAVEPOINT SCHEMA SCOPE SCROLL SEARCH SECOND
310
+ SECTION SELECT SENSITIVE SESSION SESSION_USER SET SIGNAL
311
+ SIMILAR SIZE SMALLINT SOME SPACE SPECIFIC SPECIFICTYPE
312
+ SQL SQLCODE SQLERROR SQLEXCEPTION SQLSTATE SQLWARNING
313
+ SQRT START STATIC STDDEV_POP STDDEV_SAMP SUBMULTISET
314
+ SUBSTRING SUBSTRING_REGEX SUCCEEDS SUM SYMMETRIC SYSTEM
315
+ SYSTEM_TIME SYSTEM_USER TABLE TABLESAMPLE TEMPORARY THEN
316
+ TIME TIMESTAMP TIMEZONE_HOUR TIMEZONE_MINUTE TO TRAILING
317
+ TRANSACTION TRANSLATE TRANSLATE_REGEX TRANSLATION TREAT
318
+ TRIGGER TRIM TRIM_ARRAY TRUE TRUNCATE UESCAPE UNDO UNION
319
+ UNIQUE UNKNOWN UNNEST UNTIL UPDATE UPPER USAGE USER USING
320
+ VALUE VALUES VALUE_OF VARBINARY VARCHAR VARYING VAR_POP
321
+ VAR_SAMP VERSIONING VIEW WHEN WHENEVER WHERE WHILE
322
+ WIDTH_BUCKET WINDOW WITH WITHIN WITHOUT WORK WRITE XML
323
+ XMLAGG XMLATTRIBUTES XMLBINARY XMLCAST XMLCOMMENT XMLCONCAT
324
+ XMLDOCUMENT XMLELEMENT XMLEXISTS XMLFOREST XMLITERATE
325
+ XMLNAMESPACES XMLPARSE XMLPI XMLQUERY XMLSERIALIZE XMLTABLE
326
+ XMLTEXT XMLVALIDATE YEAR ZONE
327
+ }
328
+ end
329
+
330
+ def key_words
331
+ @key_words ||= %w{
332
+ A ABSENT ACCORDING ADA ADMIN AFTER ALWAYS ASSIGNMENT
333
+ ATTRIBUTE ATTRIBUTES BASE64 BEFORE BERNOULLI BLOCKED BOM
334
+ BREADTH C CATALOG_NAME CHAIN CHARACTERISTICS CHARACTERS
335
+ CHARACTER_SET_CATALOG CHARACTER_SET_NAME CHARACTER_SET_SCHEMA
336
+ CLASS_ORIGIN COBOL COLLATION_CATALOG COLLATION_NAME
337
+ COLLATION_SCHEMA COLUMNS COLUMN_NAME COMMAND_FUNCTION
338
+ COMMAND_FUNCTION_CODE COMMITTED CONDITION_NUMBER
339
+ CONNECTION_NAME CONSTRAINT_CATALOG CONSTRAINT_NAME
340
+ CONSTRAINT_SCHEMA CONSTRUCTOR CONTENT CONTROL CURSOR_NAME
341
+ DATA DATETIME_INTERVAL_CODE DATETIME_INTERVAL_PRECISION
342
+ DB DEFAULTS DEFINED DEFINER DEGREE DEPTH DERIVED DISPATCH
343
+ DOCUMENT DYNAMIC_FUNCTION DYNAMIC_FUNCTION_CODE EMPTY
344
+ ENCODING ENFORCED EXCLUDE EXCLUDING EXPRESSION FILE FINAL
345
+ FLAG FOLLOWING FORTRAN FS G GENERAL GENERATED GRANTED HEX
346
+ HIERARCHY ID IGNORE IMMEDIATELY IMPLEMENTATION INCLUDING
347
+ INCREMENT INDENT INSTANCE INSTANTIABLE INSTEAD INTEGRITY
348
+ INVOKER K KEY_MEMBER KEY_TYPE LENGTH LIBRARY LIMIT LINK
349
+ LOCATION LOCATOR M MAP MAPPING MATCHED MAXVALUE MESSAGE_LENGTH
350
+ MESSAGE_OCTET_LENGTH MESSAGE_TEXT MINVALUE MORE MUMPS
351
+ NAME NAMESPACE NESTING NFC NFD NFKC NFKD NIL NORMALIZED
352
+ NULLABLE NULLS NUMBER OBJECT OCTETS OFF OPTIONS ORDERING
353
+ ORDINALITY OTHERS OVERRIDING P PARAMETER_MODE PARAMETER_NAME
354
+ PARAMETER_ORDINAL_POSITION PARAMETER_SPECIFIC_CATALOG
355
+ PARAMETER_SPECIFIC_NAME PARAMETER_SPECIFIC_SCHEMA PASCAL
356
+ PASSING PASSTHROUGH PATH PERMISSION PLACING PLI PRECEDING
357
+ RECOVERY REPEATABLE REQUIRING RESPECT RESTART RESTORE
358
+ RETURNED_CARDINALITY RETURNED_LENGTH RETURNED_OCTET_LENGTH
359
+ RETURNED_SQLSTATE RETURNING ROLE ROUTINE ROUTINE_CATALOG
360
+ ROUTINE_NAME ROUTINE_SCHEMA ROW_COUNT SCALE SCHEMA_NAME
361
+ SCOPE_CATALOG SCOPE_NAME SCOPE_SCHEMA SECURITY SELECTIVE
362
+ SELF SEQUENCE SERIALIZABLE SERVER SERVER_NAME SETS SIMPLE
363
+ SOURCE SPECIFIC_NAME STANDALONE STATE STATEMENT STRIP
364
+ STRUCTURE STYLE SUBCLASS_ORIGIN T TABLE_NAME TIES TOKEN
365
+ TOP_LEVEL_COUNT TRANSACTIONS_COMMITTED TRANSACTIONS_ROLLED_BACK
366
+ TRANSACTION_ACTIVE TRANSFORM TRANSFORMS TRIGGER_CATALOG
367
+ TRIGGER_NAME TRIGGER_SCHEMA TYPE UNBOUNDED UNCOMMITTED
368
+ UNDER UNLINK UNNAMED UNTYPED URI USER_DEFINED_TYPE_CATALOG
369
+ USER_DEFINED_TYPE_CODE USER_DEFINED_TYPE_NAME
370
+ USER_DEFINED_TYPE_SCHEMA VALID VERSION WHITESPACE WRAPPER
371
+ XMLDECLARATION XMLSCHEMA YES
279
372
  }
280
373
  end
281
374
 
282
375
  def is_reserved_word w
283
376
  @reserved_word_hash ||=
284
- reserved_words.inject({}) do |h,w|
377
+ ( reserved_words +
378
+ (@quote_keywords ? key_words : [])).
379
+ inject({}) do |h,w|
285
380
  h[w] = true
286
381
  h
287
382
  end
@@ -425,11 +520,12 @@ module ActiveFacts
425
520
  end
426
521
 
427
522
  def table_name composite
428
- composite.mapping.name.gsub(' ', @underscore)
523
+ composite.mapping.name.words.send(@table_case)*@table_joiner
429
524
  end
430
525
 
431
526
  def column_name component
432
- component.column_name.capcase
527
+ words = component.column_name.send(@column_case)
528
+ words*@column_joiner
433
529
  end
434
530
 
435
531
  end
@@ -0,0 +1,164 @@
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/compositions'
9
+ require 'activefacts/generator/sql'
10
+
11
+ module ActiveFacts
12
+ module Generators
13
+ # Options are comma or space separated:
14
+ # * underscore
15
+ class SQL
16
+ class Postgres < SQL
17
+ def self.options
18
+ super.merge({
19
+ # no: [String, "no new options defined here"]
20
+ })
21
+ end
22
+
23
+ def initialize composition, options = {}
24
+ super(composition, {'tables' => 'snake', 'columns' => 'snake'}.merge(options))
25
+ end
26
+
27
+ def table_name_max
28
+ 63
29
+ end
30
+
31
+ def data_type_context
32
+ PostgresqlDataTypeContext.new
33
+ end
34
+
35
+ def auto_assign_modifier
36
+ ''
37
+ end
38
+
39
+ def generate_schema
40
+ go "CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public"
41
+ end
42
+
43
+ def normalise_type(type_name, length, value_constraint, options)
44
+ if type_name =~ /^(guid|uuid)$/i
45
+ if ![nil, ''].include?(options[:auto_assign])
46
+ options[:default] = " DEFAULT 'gen_random_uuid()'"
47
+ end
48
+ return ['UUID']
49
+ end
50
+
51
+ type = MM::DataType.normalise(type_name)
52
+ case type
53
+ when MM::DataType::TYPE_Integer
54
+ if aa = options[:auto_assign]
55
+ if aa.size > 0
56
+ 'BIGSERIAL'
57
+ else
58
+ 'BIGINT'
59
+ end
60
+ else
61
+ super
62
+ end
63
+ when MM::DataType::TYPE_Money; 'MONEY'
64
+ when MM::DataType::TYPE_DateTime; 'DATETIME'
65
+ when MM::DataType::TYPE_Timestamp;'DATETIME'
66
+ when MM::DataType::TYPE_Binary;
67
+ if length && length <= 8192
68
+ super
69
+ else
70
+ 'IMAGE'
71
+ end
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+ # Reserved words cannot be used anywhere without quoting.
78
+ # Keywords have existing definitions, so should not be used without quoting.
79
+ # Both lists here are added to the supertype's lists
80
+ def reserved_words
81
+ @postgres_reserved_words ||= %w{
82
+ ANALYSE ANALYZE LIMIT PLACING RETURNING VARIADIC
83
+ }
84
+ super + @postgres_reserved_words
85
+ end
86
+
87
+ def key_words
88
+ # These keywords should not be used for columns or tables:
89
+ @postgres_key_words ||= %w{
90
+ ABORT ACCESS AGGREGATE ALSO BACKWARD CACHE CHECKPOINT
91
+ CLASS CLUSTER COMMENT COMMENTS CONFIGURATION CONFLICT
92
+ CONVERSION COPY COST CSV DATABASE DELIMITER DELIMITERS
93
+ DICTIONARY DISABLE DISCARD ENABLE ENCRYPTED ENUM EVENT
94
+ EXCLUSIVE EXPLAIN EXTENSION FAMILY FORCE FORWARD FUNCTIONS
95
+ HEADER IMMUTABLE IMPLICIT INDEX INDEXES INHERIT INHERITS
96
+ INLINE LABEL LEAKPROOF LISTEN LOAD LOCK LOCKED LOGGED
97
+ MATERIALIZED MODE MOVE NOTHING NOTIFY NOWAIT OIDS
98
+ OPERATOR OWNED OWNER PARSER PASSWORD PLANS POLICY
99
+ PREPARED PROCEDURAL PROGRAM QUOTE REASSIGN RECHECK
100
+ REFRESH REINDEX RENAME REPLACE REPLICA RESET RULE
101
+ SEQUENCES SHARE SHOW SKIP SNAPSHOT STABLE STATISTICS
102
+ STDIN STDOUT STORAGE STRICT SYSID TABLES TABLESPACE
103
+ TEMP TEMPLATE TEXT TRUSTED TYPES UNENCRYPTED UNLISTEN
104
+ UNLOGGED VACUUM VALIDATE VALIDATOR VIEWS VOLATILE
105
+ }
106
+
107
+ # These keywords cannot be used for type or functions (and should not for columns or tables)
108
+ @postgres_key_words_func_type ||= %w{
109
+ GREATEST LEAST SETOF XMLROOT
110
+ }
111
+ super + @postgres_key_words + @postgres_key_words_func_type
112
+ end
113
+
114
+ def go s = ''
115
+ "#{s};\n\n"
116
+ end
117
+
118
+ def open_escape
119
+ '"'
120
+ end
121
+
122
+ def close_escape
123
+ '"'
124
+ end
125
+
126
+ def index_kind(index)
127
+ ''
128
+ end
129
+
130
+ class PostgresqlDataTypeContext < SQLDataTypeContext
131
+ def integer_ranges
132
+ super
133
+ end
134
+
135
+ def boolean_type
136
+ 'BOOLEAN'
137
+ end
138
+
139
+ def valid_from_type
140
+ 'TIMESTAMP'
141
+ end
142
+
143
+ # There is no performance benefit in using fixed-length CHAR fields,
144
+ # and an added burden of trimming the implicitly added white-space
145
+ def default_char_type
146
+ (@unicode ? 'N' : '') +
147
+ 'VARCHAR'
148
+ end
149
+
150
+ def default_varchar_type
151
+ (@unicode ? 'N' : '') +
152
+ 'VARCHAR'
153
+ end
154
+
155
+ def date_time_type
156
+ 'TIMESTAMP'
157
+ end
158
+ end
159
+ end
160
+
161
+ end
162
+ publish_generator SQL::Postgres
163
+ end
164
+ end
@@ -28,7 +28,7 @@ module ActiveFacts
28
28
  SQLServerDataTypeContext.new
29
29
  end
30
30
 
31
- def auto_assign_type
31
+ def auto_assign_modifier
32
32
  ' IDENTITY'
33
33
  end
34
34
 
@@ -51,32 +51,33 @@ module ActiveFacts
51
51
  end
52
52
  end
53
53
 
54
+ # Reserved words cannot be used anywhere without quoting.
55
+ # Keywords have existing definitions, so should not be used without quoting.
56
+ # Both lists here are added to the supertype's lists
54
57
  def reserved_words
55
- @reserved_words ||= %w{
56
- ADD ALL ALTER AND ANY AS ASC AUTHORIZATION BACKUP BEGIN
57
- BETWEEN BREAK BROWSE BULK BY CASCADE CASE CHECK CHECKPOINT
58
- CLOSE CLUSTERED COALESCE COLLATE COLUMN COMMIT COMPUTE
59
- CONSTRAINT CONTAINS CONTAINSTABLE CONTINUE CONVERT CREATE
60
- CROSS CURRENT CURRENT_DATE CURRENT_TIME CURRENT_TIMESTAMP
61
- CURRENT_USER CURSOR DATABASE DBCC DEALLOCATE DECLARE
62
- DEFAULT DELETE DENY DESC DISK DISTINCT DISTRIBUTED DOUBLE
63
- DROP DUMMY DUMP ELSE END ERRLVL ESCAPE EXCEPT EXEC EXECUTE
64
- EXISTS EXIT FETCH FILE FILLFACTOR FOR FOREIGN FREETEXT
65
- FREETEXTTABLE FROM FULL FUNCTION GOTO GRANT GROUP HAVING
66
- HOLDLOCK IDENTITY IDENTITYCOL IDENTITY_INSERT IF IN INDEX
67
- INNER INSERT INTERSECT INTO IS JOIN KEY KILL LEFT LIKE
68
- LINENO LOAD NATIONAL NOCHECK NONCLUSTERED NOT NULL NULLIF
69
- OF OFF OFFSETS ON OPEN OPENDATASOURCE OPENQUERY OPENROWSET
70
- OPENXML OPTION OR ORDER OUTER OVER PERCENT PLAN PRECISION
71
- PRIMARY PRINT PROC PROCEDURE PUBLIC RAISERROR READ READTEXT
72
- RECONFIGURE REFERENCES REPLICATION RESTORE RESTRICT RETURN
73
- REVOKE RIGHT ROLLBACK ROWCOUNT ROWGUIDCOL RULE SAVE SCHEMA
74
- SELECT SESSION_USER SET SETUSER SHUTDOWN SOME STATISTICS
75
- SYSTEM_USER TABLE TEXTSIZE THEN TO TOP TRAN TRANSACTION
76
- TRIGGER TRUNCATE TSEQUAL UNION UNIQUE UPDATE UPDATETEXT
77
- USE USER VALUES VARYING VIEW WAITFOR WHEN WHERE WHILE
78
- WITH WRITETEXT
58
+ @sqlserver_reserved_words ||= %w{
59
+ BACKUP BREAK BROWSE BULK CHECKPOINT CLUSTERED COMPUTE
60
+ CONTAINSTABLE DATABASE DBCC DENY DISK DISTRIBUTED DUMP
61
+ ERRLVL FILE FILLFACTOR FREETEXT FREETEXTTABLE HOLDLOCK
62
+ IDENTITYCOL IDENTITY_INSERT INDEX KILL LINENO LOAD
63
+ NOCHECK NONCLUSTERED OFF OFFSETS OPENDATASOURCE OPENQUERY
64
+ OPENROWSET OPENXML PIVOT PLAN PRINT PROC RAISERROR
65
+ READTEXT RECONFIGURE REPLICATION RESTORE REVERT ROWCOUNT
66
+ ROWGUIDCOL RULE SAVE SECURITYAUDIT SEMANTICKEYPHRASETABLE
67
+ SEMANTICSIMILARITYDETAILSTABLE SEMANTICSIMILARITYTABLE
68
+ SETUSER SHUTDOWN STATISTICS TEXTSIZE TOP TRAN TRY_CONVERT
69
+ TSEQUAL UNPIVOT UPDATETEXT USE WAITFOR WITHIN GROUP
70
+ WRITETEXT
79
71
  }
72
+ super + @sqlserver_reserved_words
73
+ end
74
+
75
+ def key_words
76
+ # These keywords should not be used for columns or tables:
77
+ @sqlserver_key_words ||= %w{
78
+ INCLUDE INDEX SQLCA
79
+ }
80
+ super + @sqlserver_key_words
80
81
  end
81
82
 
82
83
  def go s = ''
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activefacts-compositions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.14
4
+ version: 1.9.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clifford Heath
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-31 00:00:00.000000000 Z
11
+ date: 2017-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -149,7 +149,7 @@ dependencies:
149
149
  version: '1'
150
150
  - - ">="
151
151
  - !ruby/object:Gem::Version
152
- version: 1.9.15
152
+ version: 1.9.19
153
153
  type: :runtime
154
154
  prerelease: false
155
155
  version_requirements: !ruby/object:Gem::Requirement
@@ -159,7 +159,7 @@ dependencies:
159
159
  version: '1'
160
160
  - - ">="
161
161
  - !ruby/object:Gem::Version
162
- version: 1.9.15
162
+ version: 1.9.19
163
163
  - !ruby/object:Gem::Dependency
164
164
  name: activefacts-cql
165
165
  requirement: !ruby/object:Gem::Requirement
@@ -222,6 +222,7 @@ files:
222
222
  - lib/activefacts/generator/rails/schema.rb
223
223
  - lib/activefacts/generator/ruby.rb
224
224
  - lib/activefacts/generator/sql.rb
225
+ - lib/activefacts/generator/sql/postgres.rb
225
226
  - lib/activefacts/generator/sql/server.rb
226
227
  - lib/activefacts/generator/summary.rb
227
228
  - lib/activefacts/generator/transgen.rb