activefacts-compositions 1.9.8 → 1.9.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,49 @@
1
+ #
2
+ # ActiveFacts Compositions, Staging Compositor.
3
+ #
4
+ # Computes a Staging schema for Data Vault.
5
+ #
6
+ # Copyright (c) 2016 Graeme Port. Read the LICENSE file.
7
+ #
8
+ require "activefacts/compositions/relational"
9
+
10
+ module ActiveFacts
11
+ module Compositions
12
+ class Staging < Relational
13
+ public
14
+ def self.options
15
+ {
16
+ stgname: ['String', "Suffix or pattern for naming staging tables. Include a + to insert the name. Default 'STG'"],
17
+ }
18
+ end
19
+
20
+ def initialize constellation, name, options = {}
21
+ # Extract recognised options:
22
+ @option_stg_name = options.delete('stgname') || 'STG'
23
+ @option_stg_name.sub!(/^/,'+ ') unless @option_stg_name =~ /\+/
24
+
25
+ super constellation, name, options
26
+
27
+ end
28
+
29
+ def inject_all_datetime_recordsource
30
+ composites = @composition.all_composite.to_a
31
+ return if composites.empty?
32
+
33
+ trace :staging, "Injecting load datetime and record source" do
34
+ @composition.all_composite.each do |composite|
35
+ inject_datetime_recordsource composite.mapping
36
+ composite.mapping.re_rank
37
+ end
38
+ end
39
+ end
40
+
41
+ def devolve_all
42
+ # Rename composites with STG prefix
43
+ rename_parents
44
+ end
45
+ end
46
+
47
+ publish_compositor(Staging)
48
+ end
49
+ end
@@ -1,5 +1,5 @@
1
1
  module ActiveFacts
2
2
  module Compositions
3
- VERSION = "1.9.8"
3
+ VERSION = "1.9.9"
4
4
  end
5
5
  end
@@ -8,7 +8,6 @@
8
8
  require 'digest/sha1'
9
9
  require 'activefacts/metamodel'
10
10
  require 'activefacts/metamodel/datatypes'
11
- require 'activefacts/registry'
12
11
  require 'activefacts/compositions'
13
12
  require 'activefacts/generator'
14
13
  require 'activefacts/support'
@@ -39,6 +38,11 @@ module ActiveFacts
39
38
  attr_accessor :xmiid
40
39
  end
41
40
 
41
+ class ActiveFacts::Metamodel::ValueField
42
+ attr_accessor :xmiid
43
+ attr_accessor :index_xmiid
44
+ end
45
+
42
46
  class CWM
43
47
  MM = ActiveFacts::Metamodel unless const_defined?(:MM)
44
48
  def self.options
@@ -47,25 +51,26 @@ module ActiveFacts
47
51
  }
48
52
  end
49
53
 
50
- def initialize composition, options = {}
51
- @composition = composition
54
+ def initialize compositions, options = {}
55
+ raise "--cwm only processes a single composition" if compositions.size > 1
56
+ @composition = compositions[0]
52
57
  @options = options
53
58
  @underscore = options.has_key?("underscore") ? (options['underscore'] || '_') : ''
54
-
59
+
55
60
  @vocabulary = composition.constellation.Vocabulary.values[0] # REVISIT when importing from other vocabularies
56
61
  end
57
62
 
58
63
  def data_type_context
59
64
  @data_type_context ||= CWMDataTypeContext.new
60
65
  end
61
-
66
+
62
67
  def generate
63
68
  # @tables_emitted = {}
64
- @namespace = Array.new
69
+ @ns = 0
65
70
  @datatypes = Array.new
66
-
71
+
67
72
  trace.enable 'cwm'
68
-
73
+
69
74
  model_ns, schema_ns = populate_namespace_ids
70
75
 
71
76
  generate_header +
@@ -108,16 +113,22 @@ module ActiveFacts
108
113
  def indent depth, str
109
114
  " " * depth + str + "\n"
110
115
  end
111
-
112
- def nsdef name
113
- ns = "_#{@namespace.size + 1}"
114
- @namespace << [ns, name]
115
- ns
116
+
117
+ def rawnsdef
118
+ @ns += 1
119
+ "_#{@ns}"
116
120
  end
117
-
121
+
122
+ def nsdef obj, pref = nil
123
+ if obj.xmiid == nil
124
+ obj.xmiid = "#{pref}#{rawnsdef}"
125
+ end
126
+ obj.xmiid
127
+ end
128
+
118
129
  def populate_namespace_ids
119
- model_ns = nsdef("Model")
120
- schema_ns = nsdef("Schema")
130
+ model_ns = rawnsdef
131
+ schema_ns = rawnsdef
121
132
 
122
133
  @composition.
123
134
  all_composite.
@@ -129,21 +140,18 @@ module ActiveFacts
129
140
 
130
141
  def populate_table_ids(table)
131
142
  tname = table_name(table)
132
- table.xmiid = nsdef(tname)
143
+ nsdef(table)
133
144
  table.mapping.all_leaf.flat_map do |leaf|
134
145
  # Absorbed empty subtypes appear as leaves
135
146
  next if leaf.is_a?(MM::Absorption) && leaf.parent_role.fact_type.is_a?(MM::TypeInheritance)
136
- leaf.xmiid = nsdef(safe_column_name(leaf))
147
+ nsdef(leaf)
137
148
  end
138
149
  table.all_index.map do |index|
139
- index.xmiid = nsdef("PK#{tname}")
140
- # for index to single column, save the index id with the column
141
- if index.all_index_field.size == 1
142
- index.all_index_field[0].component.index_xmiid = index.xmiid
143
- end
150
+ nsdef(index)
151
+ index.all_index_field.map{|idf| idf.component.index_xmiid = index.xmiid}
144
152
  end
145
- table.all_foreign_key_as_source_composite.map do |fk|
146
- fk.xmiid = nsdef("R_#{@namespace.size+1}")
153
+ table.all_foreign_key_as_source_composite.sort_by{|fk| [fk.source_composite.mapping.name, fk.absorption.inspect] }.map do |fk|
154
+ nsdef(fk)
147
155
  end
148
156
  end
149
157
 
@@ -209,7 +217,7 @@ module ActiveFacts
209
217
 
210
218
  table_columns =
211
219
  indent(depth, " <CWM:Classifier.feature>") +
212
- (table.mapping.all_leaf.flat_map do |leaf|
220
+ (table.mapping.all_leaf.flat_map.sort_by{|c| column_name(c)}.map do |leaf|
213
221
  # Absorbed empty subtypes appear as leaves
214
222
  next if leaf.is_a?(MM::Absorption) && leaf.parent_role.fact_type.is_a?(MM::TypeInheritance)
215
223
 
@@ -221,10 +229,10 @@ module ActiveFacts
221
229
  table_keys =
222
230
  indent(depth, " <CWM:Namespace.ownedElement>") +
223
231
  (table.all_index.map do |index|
224
- generate_index(depth+2, table.xmiid, index, name)
232
+ generate_index(depth+2, table.xmiid, index, name, table.all_foreign_key_as_target_composite)
225
233
  end
226
234
  ) * "" +
227
- (table.all_foreign_key_as_source_composite.map do |fk|
235
+ (table.all_foreign_key_as_source_composite.sort_by{|fk| [fk.source_composite.mapping.name, fk.absorption.inspect] }.map do |fk|
228
236
  generate_foreign_key(depth+2, table.xmiid, fk)
229
237
  end
230
238
  ) * "" +
@@ -256,7 +264,7 @@ module ActiveFacts
256
264
  end
257
265
 
258
266
  def create_data_type(type_name, type_num, type_params)
259
- type_ns = nsdef(type_name)
267
+ type_ns = rawnsdef
260
268
 
261
269
  cwm_data_type =
262
270
  "<CWMRDB:SQLSimpleType xmi.id=\"#{type_ns}\" name=\"#{type_name}\" visibility=\"public\" typeNumber=\"#{type_num}\" #{type_params}/>"
@@ -265,7 +273,7 @@ module ActiveFacts
265
273
  type_ns
266
274
  end
267
275
 
268
- def generate_index(depth, table_ns, index, table_name)
276
+ def generate_index(depth, table_ns, index, table_name, all_fks_as_target)
269
277
  key_ns = index.xmiid
270
278
 
271
279
  nullable_columns =
@@ -276,25 +284,42 @@ module ActiveFacts
276
284
 
277
285
  primary = index.composite_as_primary_index && !contains_nullable_columns
278
286
  column_ids =
279
- index.all_index_field.map do |ixf|
280
- ixf.component.xmiid
281
- end
282
- clustering =
283
- (index.composite_as_primary_index ? ' CLUSTERED' : ' NONCLUSTERED')
287
+ index.all_index_field.map do |ixf|
288
+ ixf.component.xmiid
289
+ end
290
+ # clustering =
291
+ # (index.composite_as_primary_index ? ' CLUSTERED' : ' NONCLUSTERED')
284
292
 
285
- key_type = primary ? 'PrimaryKey' : 'UniqueKey'
293
+ key_type = primary ? 'CWMRDB:PrimaryKey' : 'CWM:UniqueKey'
294
+
295
+ # find target foreign keys for this index
296
+ fks_as_target = all_fks_as_target
286
297
 
287
- if column_ids.count == 1
298
+ if column_ids.count == 1 && fks_as_target.count == 0
288
299
  colid = column_ids[0]
289
- indent(depth, "<CWMRDB:#{key_type} xmi.id=\"#{key_ns}\" name=\"XPK#{table_name}\" visibility=\"public\" namespace=\"#{table_ns}\" feature=\"#{colid}\"/>")
300
+ indent(depth, "<#{key_type} xmi.id=\"#{key_ns}\" name=\"XPK#{table_name}\" visibility=\"public\" namespace=\"#{table_ns}\" feature=\"#{colid}\"/>")
290
301
  else
291
- indent(depth, "<CWMRDB:#{key_type} xmi.id=\"#{key_ns}\" name=\"XPK#{table_name}\" visibility=\"public\" namespace=\"#{table_ns}\">") +
292
- indent(depth, " <CWM:UniqueKey.feature>") +
293
- column_ids.map do |id|
294
- indent(depth, " <CWM:StructuralFeature xmi.idref=\"#{id}\"/>")
295
- end * "" +
296
- indent(depth, " </CWM:UniqueKey.feature>") +
297
- indent(depth, "</CWMRDB:#{key_type}>")
302
+ if column_ids.count == 1
303
+ colid = column_ids[0]
304
+ indent(depth, "<#{key_type} xmi.id=\"#{key_ns}\" name=\"XPK#{table_name}\" visibility=\"public\" namespace=\"#{table_ns}\" feature=\"#{colid}\">")
305
+ else
306
+ indent(depth, "<#{key_type} xmi.id=\"#{key_ns}\" name=\"XPK#{table_name}\" visibility=\"public\" namespace=\"#{table_ns}\">") +
307
+ indent(depth, " <CWM:UniqueKey.feature>") +
308
+ column_ids.map do |id|
309
+ indent(depth, " <CWM:StructuralFeature xmi.idref=\"#{id}\"/>")
310
+ end * "" +
311
+ indent(depth, " </CWM:UniqueKey.feature>")
312
+ end +
313
+ if fks_as_target.count > 0
314
+ indent(depth, "<CWM:UniqueKey.keyRelationship>") +
315
+ fks_as_target.map do |fk|
316
+ indent(depth, " <CWM:KeyRelationship xmi.idref=\"#{fk.xmiid}\"/>")
317
+ end * "" +
318
+ indent(depth, "</CWM:UniqueKey.keyRelationship>")
319
+ else
320
+ ""
321
+ end +
322
+ indent(depth, "</#{key_type}>")
298
323
  end
299
324
  end
300
325
 
@@ -304,13 +329,19 @@ module ActiveFacts
304
329
  if fk.all_foreign_key_field.size == 1
305
330
  fkf = fk.all_foreign_key_field[0]
306
331
  ixf = fk.all_index_field[0]
307
- indent(depth, "<CWMRDB:ForeignKey xmi.id=\"#{key_ns}\" name=\"R#{key_ns}\" visibility=\"public\" namespace=\"#{table_ns}\" feature=\"#{fkf.component.xmiid}\" uniqueKey=\"#{ixf.component.index_xmiid}\"/>")
332
+ indent(depth, "<CWMRDB:ForeignKey xmi.id=\"#{key_ns}\" name=\"R#{key_ns}\" visibility=\"public\" namespace=\"#{table_ns}\" feature=\"#{fkf.component.xmiid}\" uniqueKey=\"#{ixf.component.index_xmiid}\" />")
308
333
  else
309
334
  indent(depth, "<CWMRDB:ForeignKey xmi.id=\"#{key_ns}\" name=\"R#{key_ns}\" visibility=\"public\" namespace=\"#{table_ns}\">") +
310
335
  indent(depth, " <CWM:KeyRelationship.feature>") +
311
- fk.all_foreign_key_field.map do |fkf|
312
- indent(depth, " <CWM:StructuralFeature xmi.idref=\"#{fkf.component.xmiid}\"/>")
313
- end * "" +
336
+ begin
337
+ out = ""
338
+ for i in 0..(fk.all_foreign_key_field.size - 1)
339
+ fkf = fk.all_foreign_key_field[i]
340
+ ixf = fk.all_index_field[i]
341
+ out += indent(depth, " <CWM:StructuralFeature xmi.idref=\"#{fkf.component.xmiid}\" uniqueKey=\"#{ixf.component.index_xmiid}\" />")
342
+ end
343
+ out
344
+ end +
314
345
  indent(depth, " </CWM:KeyRelationship.feature>") +
315
346
  indent(depth, "</CWMRDB:ForeignKey>")
316
347
  end
@@ -322,154 +353,6 @@ module ActiveFacts
322
353
 
323
354
  end
324
355
 
325
-
326
-
327
-
328
- ########################
329
-
330
-
331
-
332
-
333
- #
334
- # Dump functions
335
- #
336
- # def entity_type_dump(o, level)
337
- # pi = o.preferred_identifier
338
- # supers = o.supertypes
339
- # if (supers.size > 0) # Ignore identification by a supertype:
340
- # pi = nil if pi && pi.role_sequence.all_role_ref.detect{ |rr|
341
- # rr.role.fact_type.is_a?(ActiveFacts::Metamodel::TypeInheritance)
342
- # }
343
- # end
344
- #
345
- # cn_array = o.concept.all_context_note_as_relevant_concept.map{|cn| [cn.context_note_kind, cn.discussion] }
346
- # cn_hash = cn_array.inject({}) do |hash, value|
347
- # hash[value.first] = value.last
348
- # hash
349
- # end
350
- #
351
- # informal_defn = cn_hash["because"]
352
- # defn_term =
353
- # " <div class=\"row\">\n" +
354
- # " <div class=\"col-md-12 definition\">\n" +
355
- # " A #{termdef(o.name)} #{informal_defn ? 'is ' + informal_defn : ''}\n" +
356
- # " </div>\n" +
357
- # " </div>\n"
358
- #
359
- # defn_detail =
360
- # " <div class=\"row\">\n" +
361
- # " <div class=\"col-md-12 details\">\n" +
362
- # (supers.size > 0 ?
363
- # "#{span('Each', 'keyword')} #{termref(o.name, nil, o)} #{span('is a kind of', 'keyword')} #{supers.map{|s| termref(s.name, nil, s)}*', '}\n" :
364
- # ''
365
- # ) +
366
- # if pi
367
- # "#{span('Each', 'keyword')} #{termref(o.name, nil, o)} #{span('is identified by', 'keyword')} " +
368
- # pi.role_sequence.all_role_ref_in_order.map do |rr|
369
- # termref(
370
- # rr.role.object_type.name,
371
- # [ rr.leading_adjective,
372
- # rr.role.role_name || rr.role.object_type.name,
373
- # rr.trailing_adjective
374
- # ].compact * '-',
375
- # rr.role.object_type
376
- # )
377
- # end * ", " + "\n"
378
- # else
379
- # ''
380
- # end +
381
- # fact_types_dump(o, relevant_fact_types(o)) + "\n" +
382
- # " </div>\n" +
383
- # " </div>\n"
384
- #
385
- # defn_term + defn_detail
386
- # end
387
- #
388
- # def relevant_fact_types(o)
389
- # o.
390
- # all_role.
391
- # map{|r| [r, r.fact_type]}.
392
- # reject { |r, ft| ft.is_a?(ActiveFacts::Metamodel::LinkFactType) }.
393
- # select { |r, ft| ft.entity_type || has_another_nonstatic_role(ft, r) }
394
- # end
395
- #
396
- # def has_another_nonstatic_role(ft, r)
397
- # ft.all_role.detect do |rr|
398
- # rr != r &&
399
- # rr.object_type.is_a?(ActiveFacts::Metamodel::EntityType) &&
400
- # !rr.object_type.is_static
401
- # end
402
- # end
403
- #
404
- # def fact_types_dump(o, ftm)
405
- # ftm.
406
- # map { |r, ft| [ft, " #{fact_type_dump(ft, o)}"] }.
407
- # sort_by{|ft, text| [ ft.is_a?(ActiveFacts::Metamodel::TypeInheritance) ? 0 : 1, text]}.
408
- # map{|ft, text| text} * "\n"
409
- # end
410
- #
411
- # def fact_type_dump(ft, wrt = nil)
412
- # if ft.entity_type
413
- # div(
414
- # div(span('Each ', 'keyword') + termref(ft.entity_type.name, nil, ft.entity_type) + span(' is where ', 'keyword')) +
415
- # div(expand_fact_type(ft, wrt, true, 'some')),
416
- # 'glossary-objectification'
417
- # )
418
- # else
419
- # fact_type_block(ft, wrt)
420
- # end
421
- # end
422
- #
423
- # def fact_type_block(ft, wrt = nil, include_rolenames = true)
424
- # div(expand_fact_type(ft, wrt, include_rolenames, ''), 'glossary-facttype')
425
- # end
426
- #
427
- # def expand_fact_type(ft, wrt = nil, include_rolenames = true, wrt_qualifier = '')
428
- # role = ft.all_role.detect{|r| r.object_type == wrt}
429
- # preferred_reading = ft.reading_preferably_starting_with_role(role)
430
- # alternate_readings = ft.all_reading.reject{|r| r == preferred_reading}
431
- #
432
- # div(
433
- # expand_reading(preferred_reading, include_rolenames, wrt, wrt_qualifier),
434
- # 'glossary-reading'
435
- # )
436
- # end
437
- #
438
- # def role_ref(rr, freq_con, l_adj, name, t_adj, role_name_def, literal)
439
- # term_parts = [l_adj, termref(name, nil, rr.role.object_type), t_adj].compact
440
- # [
441
- # freq_con ? element(freq_con, :class=>:keyword) : nil,
442
- # term_parts.size > 1 ? term([l_adj, termref(name, nil, rr.role.object_type), t_adj].compact*' ') : term_parts[0],
443
- # role_name_def,
444
- # literal
445
- # ]
446
- # end
447
- #
448
- # def expand_reading(reading, include_rolenames = true, wrt = nil, wrt_qualifier = '')
449
- # role_refs = reading.role_sequence.all_role_ref.sort_by{|role_ref| role_ref.ordinal}
450
- # lrr = role_refs[role_refs.size - 1]
451
- # element(
452
- # # element(rr.role.is_unique ? "one" : "some", :class=>:keyword) +
453
- # reading.expand([], include_rolenames) do |rr, freq_con, l_adj, name, t_adj, role_name_def, literal|
454
- # if role_name_def
455
- # role_name_def = role_name_def.gsub(/\(as ([^)]+)\)/) {
456
- # span("(as #{ termref(rr.role.object_type.name, $1, rr.role.object_type) })", 'keyword')
457
- # }
458
- # end
459
- # # qualify the last role of the reading
460
- # quantifier = ''
461
- # if rr == lrr
462
- # uniq = true
463
- # (0 ... role_refs.size - 2).each{|i| uniq = uniq && role_refs[i].role.is_unique }
464
- # quantifier = uniq ? "one" : "at least one"
465
- # end
466
- # role_ref(rr, quantifier, l_adj, name, t_adj, role_name_def, literal)
467
- # end,
468
- # {:class => 'reading'}
469
- # )
470
- # end
471
-
472
-
473
356
  def boolean_type
474
357
  'boolean'
475
358
  end
@@ -478,107 +361,6 @@ module ActiveFacts
478
361
  'bigint'
479
362
  end
480
363
 
481
- # def component_type component, column_name
482
- # case component
483
- # when MM::Indicator
484
- # boolean_type
485
- # when MM::SurrogateKey
486
- # surrogate_type
487
- # when MM::ValueField, MM::Absorption
488
- # object_type = component.object_type
489
- # while object_type.is_a?(MM::EntityType)
490
- # rr = object_type.preferred_identifier.role_sequence.all_role_ref.single
491
- # raise "Can't produce a column for composite #{component.inspect}" unless rr
492
- # object_type = rr.role.object_type
493
- # end
494
- # raise "A column can only be produced from a ValueType" unless object_type.is_a?(MM::ValueType)
495
- #
496
- # if component.is_a?(MM::Absorption)
497
- # value_constraint ||= component.child_role.role_value_constraint
498
- # end
499
- #
500
- # supertype = object_type
501
- # begin
502
- # object_type = supertype
503
- # length ||= object_type.length
504
- # scale ||= object_type.scale
505
- # unless component.parent.parent and component.parent.foreign_key
506
- # # No need to enforce value constraints that are already enforced by a foreign key
507
- # value_constraint ||= object_type.value_constraint
508
- # end
509
- # end while supertype = object_type.supertype
510
- # type, length = normalise_type(object_type.name, length)
511
- # sql_type = "#{type}#{
512
- # if !length
513
- # ''
514
- # else
515
- # '(' + length.to_s + (scale ? ", #{scale}" : '') + ')'
516
- # end
517
- # # }#{
518
- # # (component.path_mandatory ? '' : ' NOT') + ' NULL'
519
- # # }#{
520
- # # # REVISIT: This is an SQL Server-ism. Replace with a standard SQL SEQUENCE/
521
- # # # Emit IDENTITY for columns auto-assigned on commit (except FKs)
522
- # # if a = object_type.is_auto_assigned and a != 'assert' and
523
- # # !component.all_foreign_key_field.detect{|fkf| fkf.foreign_key.source_composite == component.root}
524
- # # ' IDENTITY'
525
- # # else
526
- # # ''
527
- # # end
528
- # }#{
529
- # value_constraint ? check_clause(column_name, value_constraint) : ''
530
- # }"
531
- # when MM::Injection
532
- # component.object_type.name
533
- # else
534
- # raise "Can't make a column from #{component}"
535
- # end
536
- # end
537
-
538
- # def generate_index index, delayed_indices, indent
539
- # nullable_columns =
540
- # index.all_index_field.select do |ixf|
541
- # !ixf.component.path_mandatory
542
- # end
543
- # contains_nullable_columns = nullable_columns.size > 0
544
- #
545
- # primary = index.composite_as_primary_index && !contains_nullable_columns
546
- # column_names =
547
- # index.all_index_field.map do |ixf|
548
- # column_name(ixf.component)
549
- # end
550
- # clustering =
551
- # (index.composite_as_primary_index ? ' CLUSTERED' : ' NONCLUSTERED')
552
- #
553
- # if contains_nullable_columns
554
- # table_name = safe_table_name(index.composite)
555
- # delayed_indices <<
556
- # 'CREATE UNIQUE'+clustering+' INDEX '+
557
- # escape("#{table_name(index.composite)}By#{column_names*''}", index_name_max) +
558
- # " ON #{table_name}("+column_names.map{|n| escape(n, column_name_max)}*', ' +
559
- # ") WHERE #{
560
- # nullable_columns.
561
- # map{|ixf| safe_column_name ixf.component}.
562
- # map{|column_name| column_name + ' IS NOT NULL'} *
563
- # ' AND '
564
- # }"
565
- # nil
566
- # else
567
- # # '-- '+index.inspect
568
- # " " * indent + (primary ? 'PRIMARY KEY' : 'UNIQUE') +
569
- # clustering +
570
- # "(#{column_names.map{|n| escape(n, column_name_max)}*', '})"
571
- # end
572
- # end
573
-
574
- # def generate_foreign_key fk, indent
575
- # # '-- '+fk.inspect
576
- # " " * indent + "FOREIGN KEY (" +
577
- # fk.all_foreign_key_field.map{|fkf| safe_column_name fkf.component}*", " +
578
- # ") REFERENCES <a href=\"#LDMD_#{table_name fk.composite}\">#{table_name fk.composite}</a> (" +
579
- # fk.all_index_field.map{|ixf| safe_column_name ixf.component}*", " +
580
- # ")"
581
- # end
582
364
 
583
365
  def reserved_words
584
366
  @reserved_words ||= %w{ }
@@ -593,10 +375,6 @@ module ActiveFacts
593
375
  @reserved_word_hash[w.upcase]
594
376
  end
595
377
 
596
- # def go s = ''
597
- # "#{s}\nGO\n" # REVISIT: This is an SQL-Serverism. Move it to a subclass.
598
- # end
599
-
600
378
  def escape s, max = table_name_max
601
379
  # Escape SQL keywords and non-identifiers
602
380
  if s.size > max