1
2
3
4
5 package edu.internet2.middleware.grouperClient.jdbc.tableSync;
6
7 import java.sql.ResultSet;
8 import java.sql.ResultSetMetaData;
9 import java.sql.Types;
10 import java.util.ArrayList;
11 import java.util.Collections;
12 import java.util.Comparator;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import edu.internet2.middleware.grouperClient.collections.MultiKey;
20 import edu.internet2.middleware.grouperClient.jdbc.GcDbAccess;
21 import edu.internet2.middleware.grouperClient.jdbc.GcResultSetCallback;
22 import edu.internet2.middleware.grouperClient.jdbc.tableSync.GcTableSyncColumnMetadata.ColumnType;
23 import edu.internet2.middleware.grouperClient.util.ExpirableCache;
24 import edu.internet2.middleware.grouperClient.util.GrouperClientCommonUtils;
25 import edu.internet2.middleware.grouperClient.util.GrouperClientConfig;
26 import edu.internet2.middleware.grouperClient.util.GrouperClientUtils;
27 import org.apache.commons.lang3.StringUtils;
28 import org.apache.commons.logging.Log;
29
30
31
32
33
34 public class GcTableSyncTableMetadata {
35
36
37
38
39 private String metadataQuery = null;
40
41 private boolean quotesInitted = false;
42
43
44
45
46 private String quoteForColumnsInSql = "XXX";
47
48 public String quoteForColumnsInSql() {
49 if (!quotesInitted) {
50
51 quoteForColumnsInSql = GcDbAccess.quoteForColumnsInSql(this.connectionName);
52 quotesInitted = true;
53 }
54 return quoteForColumnsInSql;
55 }
56
57
58
59
60
61
62
63 public String quoteStrings(String columns) {
64 if (GrouperClientUtils.isBlank(columns)) {
65 return columns;
66 }
67 StringBuilder result = new StringBuilder();
68 boolean first = true;
69 for (String column : GrouperClientUtils.splitTrim(columns, ",")) {
70 if (!first) {
71 result.append(",");
72 }
73 if (column.startsWith("\"") && column.endsWith("\"")) {
74 result.append(column);
75 } else {
76 result.append(quoteForColumnsInSql()).append(column).append(quoteForColumnsInSql());
77 }
78
79 first = false;
80 }
81 return result.toString();
82 }
83
84
85
86
87 public String queryWherePrimaryKey() {
88
89 if (GrouperClientUtils.length(this.getPrimaryKey()) == 0) {
90 throw new RuntimeException("No primary key for '" + this.tableName + "'!");
91 }
92 boolean first = true;
93 StringBuilder result = new StringBuilder();
94
95 for (GcTableSyncColumnMetadata gcTableSyncColumnMetadata : this.getPrimaryKey()) {
96
97 if (!first) {
98 result.append(" and ");
99 }
100
101 result.append(" ").append(quoteForColumnsInSql()).append(gcTableSyncColumnMetadata.getColumnName()).append(quoteForColumnsInSql()).append(" = ? ");
102
103 first = false;
104 }
105
106 return result.toString();
107 }
108
109
110
111
112
113 public String queryUpdateNonPrimaryKey() {
114
115 if (GrouperClientUtils.length(this.getNonPrimaryKey()) == 0) {
116 throw new RuntimeException("No non-primary key for '" + this.tableName + "'!");
117 }
118 boolean first = true;
119 StringBuilder result = new StringBuilder();
120
121 for (GcTableSyncColumnMetadata gcTableSyncColumnMetadata : this.getNonPrimaryKey()) {
122
123 if (!first) {
124 result.append(" , ");
125 }
126
127 result.append(" ").append(quoteForColumnsInSql()).append(gcTableSyncColumnMetadata.getColumnName()).append(quoteForColumnsInSql()).append(" = ? ");
128
129 first = false;
130 }
131
132 return result.toString();
133 }
134
135
136
137
138 private List<GcTableSyncColumnMetadata> nonPrimaryKey;
139
140
141
142
143 private GcTableSyncColumnMetadata incrementalProgressColumn;
144
145
146
147
148
149 public GcTableSyncColumnMetadata getIncrementalProgressColumn() {
150 return this.incrementalProgressColumn;
151 }
152
153
154
155
156
157 public void setIncrementalProgressColumn(
158 GcTableSyncColumnMetadata incrementalProgressColumn1) {
159 this.incrementalProgressColumn = incrementalProgressColumn1;
160 }
161
162
163
164
165
166 public void assignIncrementalProgressColumn(String incrementalProgressColumnName) {
167 this.incrementalProgressColumn = this.lookupColumn(incrementalProgressColumnName, true);
168 }
169
170
171
172
173 private List<GcTableSyncColumnMetadata> primaryKeyColumns;
174
175
176
177
178 private Map<String, GcTableSyncColumnMetadata> columnUpperNameToGcColumnMetadata;
179
180
181
182
183
184
185 public List<GcTableSyncColumnMetadata> lookupColumns(String columnNames) {
186 if (GrouperClientUtils.isBlank(columnNames)) {
187 throw new RuntimeException("Pass in columns for " + this.getConnectionName() + " -> " + this.getTableName());
188 }
189
190 List<GcTableSyncColumnMetadata> result = new ArrayList<GcTableSyncColumnMetadata>();
191
192 if (GrouperClientUtils.equals(columnNames, "*")) {
193 result.addAll(this.getColumnMetadata());
194 } else {
195
196 for (String columnName : GrouperClientUtils.splitTrim(columnNames, ",")) {
197 GcTableSyncColumnMetadata gcTableSyncColumnMetadata = lookupColumn(columnName, true);
198 result.add(gcTableSyncColumnMetadata);
199 }
200 }
201
202 return result;
203 }
204
205
206
207
208
209
210
211 public GcTableSyncColumnMetadata lookupColumn(String columnName, boolean exceptionOnNotFound) {
212
213 if (this.columnUpperNameToGcColumnMetadata == null) {
214
215 if (GrouperClientUtils.length(this.columnMetadata) == 0) {
216 throw new RuntimeException("Cant find table metadata for " + this.connectionName + " -> " + this.tableName + "!");
217 }
218
219 Map<String, GcTableSyncColumnMetadata> tempColumnUpperNameToGcColumnMetadata = new HashMap<String, GcTableSyncColumnMetadata>();
220
221 for (GcTableSyncColumnMetadata gcTableSyncColumnMetadata : this.columnMetadata) {
222 tempColumnUpperNameToGcColumnMetadata.put(gcTableSyncColumnMetadata.getColumnName().toUpperCase(), gcTableSyncColumnMetadata);
223 }
224
225 this.columnUpperNameToGcColumnMetadata = tempColumnUpperNameToGcColumnMetadata;
226 }
227
228 GcTableSyncColumnMetadata gcTableSyncColumnMetadata = this.columnUpperNameToGcColumnMetadata.get(columnName.toUpperCase());
229
230 if (gcTableSyncColumnMetadata == null && exceptionOnNotFound) {
231 throw new RuntimeException("Cant find " + this.connectionName + " -> "
232 + (GrouperClientUtils.isBlank(this.tableName) ? ("(" + this.metadataQuery + ")") : this.tableName) + " -> " + columnName);
233 }
234
235 return gcTableSyncColumnMetadata;
236
237 }
238
239
240
241
242 private static ExpirableCache<MultiKey, GcTableSyncTableMetadata> metadataCache = null;
243
244
245
246
247
248 private static ExpirableCache<MultiKey, GcTableSyncTableMetadata> metadataCache() {
249 if (metadataCache == null) {
250 metadataCache = new ExpirableCache(GrouperClientConfig.retrieveConfig().propertyValueInt("tableSyncMetadataCacheMinutes", 10));
251 }
252 return metadataCache;
253 }
254
255
256
257
258
259
260
261 public static GcTableSyncTableMetadata retrieveTableMetadataFromCacheOrDatabase(String connectionName, String tableName) {
262
263 MultiKeyrouperClient/collections/MultiKey.html#MultiKey">MultiKey multiKey = new MultiKey(connectionName, tableName);
264 GcTableSyncTableMetadata gcTableSyncTableMetadata = metadataCache().get(multiKey);
265 if (gcTableSyncTableMetadata != null) {
266 return gcTableSyncTableMetadata;
267 }
268
269 gcTableSyncTableMetadata = retrieveTableMetadataFromDatabase(connectionName, tableName);
270 metadataCache().put(multiKey, gcTableSyncTableMetadata);
271 return gcTableSyncTableMetadata;
272 }
273
274
275
276
277
278
279
280 public static GcTableSyncTableMetadata retrieveQueryMetadataFromCacheOrDatabase(String connectionName, String query) {
281
282 MultiKeyrouperClient/collections/MultiKey.html#MultiKey">MultiKey multiKey = new MultiKey(connectionName, query);
283 GcTableSyncTableMetadata gcTableSyncTableMetadata = metadataCache().get(multiKey);
284 if (gcTableSyncTableMetadata != null) {
285 return gcTableSyncTableMetadata;
286 }
287
288 gcTableSyncTableMetadata = retrieveQueryMetadataFromDatabase(connectionName, query);
289 metadataCache().put(multiKey, gcTableSyncTableMetadata);
290 return gcTableSyncTableMetadata;
291 }
292
293
294
295
296
297
298
299 public static GcTableSyncTableMetadata retrieveTableMetadataFromDatabase(String theConnectionName, String tableName) {
300
301 if (GrouperClientUtils.isBlank(tableName)) {
302 throw new RuntimeException("tableName cannot be blank");
303 }
304
305 String sql = "select * from " + tableName;
306 GcTableSyncTableMetadata gcTableSyncTableMetadata = retrieveQueryMetadataFromDatabase(theConnectionName, sql);
307 if (GrouperClientUtils.isBlank(gcTableSyncTableMetadata.getTableName())) {
308 gcTableSyncTableMetadata.setTableName(tableName);
309 }
310 return gcTableSyncTableMetadata;
311 }
312
313
314
315
316
317
318
319 public static GcTableSyncTableMetadata retrieveQueryMetadataFromDatabase(String theConnectionName, String query) {
320 return retrieveQueryMetadataFromDatabase(theConnectionName, query, null);
321 }
322
323
324
325
326
327
328
329 public static GcTableSyncTableMetadata retrieveQueryMetadataFromDatabase(String theConnectionName, String query, List<Object> bindVars) {
330
331 if (GrouperClientUtils.isBlank(query)) {
332 throw new RuntimeException("query cannot be blank");
333 }
334 if (GrouperClientUtils.isBlank(theConnectionName)) {
335 throw new RuntimeException("connectionName cannot be blank");
336 }
337
338 if (StringUtils.endsWith(query, ";")) {
339 query = query.substring(0, query.length() - 1);
340 }
341
342
343
344
345 if (!query.contains("1!=1") && !query.contains("1 != 1")
346 && !query.contains("0 = 1") && !query.contains("0=1") && !query.contains("1 = 0") && !query.contains("1=0")) {
347
348 if (!query.toLowerCase().contains(" union ")) {
349
350 if (query.contains(" where ") || query.contains(" WHERE ")) {
351
352
353 query = GrouperClientUtils.replace(query, " where ", " where 1!=1 and ");
354 query = GrouperClientUtils.replace(query, " WHERE ", " where 1!=1 and ");
355 } else {
356 if (!query.toLowerCase().contains("where") && !query.toLowerCase().contains(" order ")
357 && !query.toLowerCase().contains(" group ")) {
358 query += " where 1!=1";
359 }
360 }
361 }
362 }
363
364 GcTableSyncTableMetadatableSyncTableMetadata.html#GcTableSyncTableMetadata">GcTableSyncTableMetadata gcTableSyncTableMetadata = new GcTableSyncTableMetadata();
365 gcTableSyncTableMetadata.metadataQuery = query;
366 gcTableSyncTableMetadata.setConnectionName(theConnectionName);
367
368 final ArrayList<GcTableSyncColumnMetadata> gcTableSyncColumnMetadatas = new ArrayList<GcTableSyncColumnMetadata>();
369 gcTableSyncTableMetadata.setColumnMetadata(gcTableSyncColumnMetadatas);
370
371 try {
372
373 GcDbAccesserClient/jdbc/GcDbAccess.html#GcDbAccess">GcDbAccess gcDbAccess = new GcDbAccess().connectionName(theConnectionName).sql(query);
374
375 if (bindVars != null) {
376 gcDbAccess.bindVars(bindVars);
377 }
378
379 gcDbAccess.callbackResultSet(new GcResultSetCallback() {
380
381 @Override
382 public Object callback(ResultSet resultSet) throws Exception {
383
384 ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
385
386
387
388
389
390 for (int i=0;i<resultSetMetaData.getColumnCount();i++) {
391 GcTableSyncColumnMetadataeSyncColumnMetadata.html#GcTableSyncColumnMetadata">GcTableSyncColumnMetadata gcTableSyncColumnMetadata = new GcTableSyncColumnMetadata();
392
393 String columnName = GrouperClientUtils.defaultIfBlank(resultSetMetaData.getColumnLabel(i+1), resultSetMetaData.getColumnName(i+1));
394 int dataType = resultSetMetaData.getColumnType(i+1);
395 String typeName = resultSetMetaData.getColumnTypeName(i+1);
396
397 gcTableSyncColumnMetadata.setColumnIndexZeroIndexed(i);
398
399 gcTableSyncColumnMetadata.setColumnName(columnName);
400
401 gcTableSyncColumnMetadatas.add(gcTableSyncColumnMetadata);
402
403 switch (dataType) {
404 case Types.BIGINT:
405 case Types.DECIMAL:
406 case Types.DOUBLE:
407 case Types.FLOAT:
408 case Types.INTEGER:
409 case Types.NUMERIC:
410 case Types.REAL:
411 case Types.SMALLINT:
412 case Types.TINYINT:
413
414 gcTableSyncColumnMetadata.setColumnType(ColumnType.NUMERIC);
415 {
416 int precision = resultSetMetaData.getPrecision(i+1);
417 gcTableSyncColumnMetadata.setPrecision(precision);
418 }
419
420 {
421 int scale = resultSetMetaData.getScale(i+1);
422 gcTableSyncColumnMetadata.setScale(scale);
423 }
424 break;
425
426 case Types.BIT:
427 case Types.BOOLEAN:
428
429 gcTableSyncColumnMetadata.setColumnType(ColumnType.BOOLEAN);
430 {
431 int columnDisplaySize = resultSetMetaData.getColumnDisplaySize(i+1);
432 gcTableSyncColumnMetadata.setColumnDisplaySize(columnDisplaySize);
433 }
434 break;
435 case Types.CHAR:
436 case Types.VARCHAR:
437 case Types.LONGVARCHAR:
438 case Types.NCHAR:
439 case Types.NVARCHAR:
440 case Types.LONGNVARCHAR:
441
442 gcTableSyncColumnMetadata.setColumnType(ColumnType.STRING);
443 {
444 int columnDisplaySize = resultSetMetaData.getColumnDisplaySize(i+1);
445 gcTableSyncColumnMetadata.setColumnDisplaySize(columnDisplaySize);
446 }
447 break;
448
449 case Types.DATE:
450 case Types.TIMESTAMP:
451
452 gcTableSyncColumnMetadata.setColumnType(ColumnType.TIMESTAMP);
453 break;
454
455 case Types.OTHER:
456
457 gcTableSyncColumnMetadata.setColumnType(ColumnType.UUID);
458 break;
459
460 default:
461 throw new RuntimeException("Type not supported: " + dataType + ", " + typeName);
462
463
464 }
465 }
466
467 Collections.sort(gcTableSyncColumnMetadatas, new Comparator<GcTableSyncColumnMetadata>() {
468
469 @Override
470 public int compare(GcTableSyncColumnMetadata./../edu/internet2/middleware/grouperClient/jdbc/tableSync/GcTableSyncColumnMetadata.html#GcTableSyncColumnMetadata">GcTableSyncColumnMetadata o1, GcTableSyncColumnMetadata o2) {
471
472 if (o1 == o2) {
473 return 0;
474 }
475 if (o1 == null) {
476 return -1;
477 }
478 if (o2 == null) {
479 return 1;
480 }
481 return o1.getColumnName().toLowerCase().compareTo(o2.getColumnName().toLowerCase());
482 }
483 });
484
485 return null;
486 }
487
488 });
489 } catch (RuntimeException e) {
490 RuntimeException e2 = GrouperClientCommonUtils.createRuntimeExceptionWithMessage(e, "Error finding metadata for '" + query + "' in database: '" + theConnectionName + "'");
491 throw e2;
492 }
493
494 if (gcTableSyncColumnMetadatas.size() == 0) {
495 throw new RuntimeException("Cant find table metadata for '" + query + "' in database: '" + theConnectionName + "'");
496 }
497
498 return gcTableSyncTableMetadata;
499 }
500
501
502
503 public String getMetadataQuery() {
504 return metadataQuery;
505 }
506
507
508
509
510 public String getConnectionName() {
511 return this.connectionName;
512 }
513
514
515
516
517
518 public void setConnectionName(String connectionName) {
519 this.connectionName = connectionName;
520 }
521
522
523
524
525
526 public String getTableName() {
527 return this.tableName;
528 }
529
530
531
532
533
534 public void setTableName(String tableName) {
535 this.tableName = tableName;
536 }
537
538
539
540
541 private String connectionName;
542
543
544
545
546 private String connectionNameOrReadonly;
547
548
549
550
551
552 public String getConnectionNameOrReadonly() {
553 return this.connectionNameOrReadonly;
554 }
555
556
557
558
559
560 public void setConnectionNameOrReadonly(String connectionNameOrReadonly1) {
561 this.connectionNameOrReadonly = connectionNameOrReadonly1;
562 }
563
564
565
566
567 private String tableName;
568
569
570
571
572 public GcTableSyncTableMetadata() {
573 }
574
575
576
577
578 private List<GcTableSyncColumnMetadata> columnMetadata;
579
580
581
582
583 private List<GcTableSyncColumnMetadata> columns;
584
585
586
587
588 private static Log LOG = GrouperClientUtils.retrieveLog(GcTableSyncTableMetadata.class);
589
590
591
592
593 private GcTableSyncColumnMetadata groupColumn;
594
595
596
597
598 private GcTableSyncColumnMetadata changeFlagColumn;
599
600
601
602
603 private GcTableSyncColumnMetadata incrementalAllCoumnsColumn;
604
605
606
607
608
609 public GcTableSyncColumnMetadata getIncrementalAllCoumnsColumn() {
610 return this.incrementalAllCoumnsColumn;
611 }
612
613
614
615
616
617 public void setIncrementalAllCoumnsColumn(
618 GcTableSyncColumnMetadata incrementalAllCoumnsColumn1) {
619 this.incrementalAllCoumnsColumn = incrementalAllCoumnsColumn1;
620 }
621
622
623
624
625
626 public GcTableSyncColumnMetadata getChangeFlagColumn() {
627 return this.changeFlagColumn;
628 }
629
630
631
632
633
634 public void setChangeFlagColumn(GcTableSyncColumnMetadata changeFlagColumn1) {
635 this.changeFlagColumn = changeFlagColumn1;
636 }
637
638
639
640
641
642 public List<GcTableSyncColumnMetadata> getColumnMetadata() {
643 return this.columnMetadata;
644 }
645
646
647
648
649
650 public List<GcTableSyncColumnMetadata> retrieveColumnMetadataOrdered() {
651 if (this.columnMetadata == null) {
652 return null;
653 }
654 List<GcTableSyncColumnMetadata> result = new ArrayList<>(this.columnMetadata);
655 for (GcTableSyncColumnMetadata gcTableSyncColumnMetadata : this.columnMetadata) {
656 result.set(gcTableSyncColumnMetadata.getColumnIndexZeroIndexed(), gcTableSyncColumnMetadata);
657 }
658 return result;
659 }
660
661
662
663
664
665
666 public void setColumnMetadata(List<GcTableSyncColumnMetadata> columnMetadata1) {
667 this.columnMetadata = columnMetadata1;
668 }
669
670
671
672
673 public void assignPrimaryKeyColumns(String theColumns) {
674 this.primaryKeyColumns = this.lookupColumns(theColumns);
675 }
676
677
678
679
680
681 public List<GcTableSyncColumnMetadata> getColumns() {
682 return this.columns;
683 }
684
685
686
687
688
689 public List<GcTableSyncColumnMetadata> getNonPrimaryKey() {
690 if (this.nonPrimaryKey == null) {
691
692 List<GcTableSyncColumnMetadata> result = new ArrayList();
693 result.addAll(this.getColumns());
694 result.removeAll(this.getPrimaryKey());
695 this.nonPrimaryKey = result;
696 }
697
698 return this.nonPrimaryKey;
699 }
700
701
702
703
704
705 public List<GcTableSyncColumnMetadata> getPrimaryKey() {
706 return this.primaryKeyColumns;
707 }
708
709
710
711
712 public void assignColumns(String theColumns) {
713 this.columns = this.lookupColumns(theColumns);
714 }
715
716
717
718
719
720 public String columnListAll() {
721 StringBuilder result = new StringBuilder();
722 boolean first = true;
723 for (GcTableSyncColumnMetadata gcTableSyncColumnMetadata : GrouperClientUtils.nonNull(this.columns)) {
724 if (!first) {
725 result.append(", ");
726 }
727 result.append(gcTableSyncColumnMetadata.getColumnName());
728
729 first = false;
730 }
731 return result.toString();
732 }
733
734
735
736
737
738 public String columnListAllQuoted() {
739 StringBuilder result = new StringBuilder();
740 boolean first = true;
741 for (GcTableSyncColumnMetadata gcTableSyncColumnMetadata : GrouperClientUtils.nonNull(this.columns)) {
742 if (!first) {
743 result.append(", ");
744 }
745 result.append(quoteForColumnsInSql()).append(gcTableSyncColumnMetadata.getColumnName()).append(quoteForColumnsInSql());
746
747 first = false;
748 }
749 return result.toString();
750 }
751
752
753
754
755
756 public String columnListPrimaryKeyAndChangeFlagAndOptionalIncrementalProgress() {
757 StringBuilder result = new StringBuilder();
758
759 Set<String> columnNames = new HashSet<String>();
760 boolean first = true;
761 for (GcTableSyncColumnMetadata gcTableSyncColumnMetadata : GrouperClientUtils.nonNull(this.columns)) {
762
763 if (!first) {
764 result.append(", ");
765 }
766
767 result.append(gcTableSyncColumnMetadata.getColumnName());
768 columnNames.add(gcTableSyncColumnMetadata.getColumnName());
769 first = false;
770 }
771 if (!columnNames.contains(this.getChangeFlagColumn().getColumnName())) {
772 result.append(", ");
773 result.append(this.getChangeFlagColumn().getColumnName());
774 columnNames.add(this.getChangeFlagColumn().getColumnName());
775 }
776 if (this.getIncrementalAllCoumnsColumn() != null) {
777 if (!columnNames.contains(this.getIncrementalAllCoumnsColumn().getColumnName())) {
778 result.append(", ");
779 result.append(this.getIncrementalAllCoumnsColumn().getColumnName());
780 columnNames.add(this.getChangeFlagColumn().getColumnName());
781 }
782 }
783 return result.toString();
784 }
785
786
787
788
789
790 public String columnListInputtedColumnsAndIncrementalProgressColumn(List<GcTableSyncColumnMetadata> otherTablePrimaryKey) {
791 StringBuilder result = new StringBuilder();
792
793 for (GcTableSyncColumnMetadata gcTableSyncColumnMetadataOther : otherTablePrimaryKey) {
794
795 GcTableSyncColumnMetadata gcTableSyncColumnMetadataThis = this.lookupColumn(gcTableSyncColumnMetadataOther.getColumnName(), true);
796 result.append(gcTableSyncColumnMetadataThis.getColumnName());
797
798 result.append(", ");
799 }
800 result.append(this.getIncrementalProgressColumn().getColumnName());
801 return result.toString();
802 }
803
804
805
806
807
808 public void setColumns(List<GcTableSyncColumnMetadata> columns1) {
809 this.columns = columns1;
810 }
811
812
813
814
815
816 public void assignGroupColumn(String groupColumnName) {
817 this.groupColumn = this.lookupColumn(groupColumnName, true);
818 }
819
820
821
822
823
824 public void assignChangeFlagColumn(String changeFlagColumnName) {
825 this.changeFlagColumn = this.lookupColumn(changeFlagColumnName, true);
826 }
827
828
829
830
831
832 public GcTableSyncColumnMetadata getGroupColumnMetadata() {
833 return this.groupColumn;
834 }
835
836
837
838
839
840 public void assignIncrementalAllCoumnsColumn(String incrementalAllColumnsColumnName) {
841 this.incrementalAllCoumnsColumn = this.lookupColumn(incrementalAllColumnsColumnName, true);
842 }
843
844 }