activefacts-compositions 1.9.14 → 1.9.15

Sign up to get free protection for your applications and to get access to all the features.
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