001package gudusoft.gsqlparser.nodes;
002
003import java.util.ArrayList;
004import java.util.Arrays;
005import java.util.List;
006import java.util.regex.Matcher;
007import java.util.regex.Pattern;
008
009import gudusoft.gsqlparser.*;
010import gudusoft.gsqlparser.stmt.TCreateTableSqlStatement;
011import gudusoft.gsqlparser.stmt.snowflake.TSnowflakeCopyIntoStmt;
012import gudusoft.gsqlparser.stmt.snowflake.TAlterStageStmt;
013import gudusoft.gsqlparser.stmt.snowflake.TCreateStageStmt;
014
015public class TCreateTableOption extends TParseTreeNode {
016
017    public enum ESecondaryRolesMode {
018        unspecified,
019        all,
020        none,
021        roleList
022    }
023
024    private TObjectName executeAsUser;
025    private TObjectName backfillFrom;
026    private ESecondaryRolesMode secondaryRolesMode = ESecondaryRolesMode.unspecified;
027    private TObjectNameList secondaryRoleList;
028
029    /**
030     * Returns the Snowflake user whose privileges are used to refresh a
031     * dynamic table.
032     */
033    public TObjectName getExecuteAsUser() {
034        return executeAsUser;
035    }
036
037    public void setExecuteAsUser(TObjectName executeAsUser) {
038        this.executeAsUser = executeAsUser;
039    }
040
041    /**
042     * Returns the Snowflake table used to seed a dynamic table.
043     */
044    public TObjectName getBackfillFrom() {
045        return backfillFrom;
046    }
047
048    public void setBackfillFrom(TObjectName backfillFrom) {
049        this.backfillFrom = backfillFrom;
050    }
051
052    /**
053     * Returns how USE SECONDARY ROLES is configured for EXECUTE AS USER.
054     */
055    public ESecondaryRolesMode getSecondaryRolesMode() {
056        return secondaryRolesMode;
057    }
058
059    public void setSecondaryRolesMode(ESecondaryRolesMode secondaryRolesMode) {
060        this.secondaryRolesMode = secondaryRolesMode;
061    }
062
063    /**
064     * Returns the explicit role list when {@link #getSecondaryRolesMode()}
065     * is {@link ESecondaryRolesMode#roleList}.
066     */
067    public TObjectNameList getSecondaryRoleList() {
068        return secondaryRoleList;
069    }
070
071    public void setSecondaryRoleList(TObjectNameList secondaryRoleList) {
072        this.secondaryRoleList = secondaryRoleList;
073    }
074
075    private TDistributeBy distributeBy;
076
077    public TDistributeBy getDistributeBy() {
078        return distributeBy;
079    }
080
081    private TBaseTablePartition partitionSpec;
082
083    public TBaseTablePartition getPartitionSpec() {
084        return partitionSpec;
085    }
086
087    public TObjectName getComment() {
088        return comment;
089    }
090
091    private TObjectName comment;
092
093    /*
094     * BigQuery external_table_option_list
095     *
096     * @see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-
097     * definition-language#external_table_option_list
098     */
099    private Boolean allowJaggedRows;
100    private Boolean allowQuotedNewlines;
101    private String compression;
102    private String description;
103    private Boolean enableLogicalTypes;
104    private String encoding;
105    private String expirationTimestamp;
106    private String fieldDelimiter;
107    private String format;
108    private List<String> decimalTargetTypes;
109    private String hivePartitionUriPrefix;
110    private Boolean ignoreUnknownValues;
111    private Long maxBadRecords;
112    private String nullMarker;
113    private String projectionFields;
114    private String quote;
115    private Boolean requireHivePartitionFilter;
116    private String sheetRange;
117    private Long skipLeadingRows;
118    private List<String> uris;
119
120    private  TObjectNameList columnNamelist;
121
122    public TObjectNameList getColumnNamelist() {
123        return columnNamelist;
124    }
125
126    private String awsSnsTopic = null;
127    private TObjectNameList partitionColumnList;
128    private TExpression partitionByExpr;
129
130    /**
131     * Bigquery partition by expr
132     * @return expr
133     */
134    public TExpression getPartitionByExpr() {
135        return partitionByExpr;
136    }
137
138    private String fileFormatName = null;
139    private String fileFormatType = null;
140    private ArrayList<TNameValuePair> fileFormatProperties = null;
141
142    public void setFileFormatName(TSourceToken fileFormatName) {
143        this.fileFormatName = fileFormatName.toString();
144    }
145    public void setFileFormatName(TObjectName fileFormatName) {
146        this.fileFormatName = fileFormatName.toString();
147    }
148
149    public void setFileFormatType(TSourceToken fileFormatType) {
150        this.fileFormatType = fileFormatType.toString();
151    }
152
153    public String getFileFormatName() {
154        return fileFormatName;
155    }
156
157    public String getFileFormatType() {
158        return fileFormatType;
159    }
160
161    /**
162     * Get file format properties list (e.g., TYPE=CSV, FIELD_DELIMITER='|', SKIP_HEADER=1)
163     * Used in Snowflake external table file_format option
164     * @return list of name-value pairs representing file format properties
165     */
166    public ArrayList<TNameValuePair> getFileFormatProperties() {
167        return fileFormatProperties;
168    }
169
170    /**
171     * Set file format properties list
172     * @param fileFormatProperties list of name-value pairs for file format options
173     */
174    public void setFileFormatProperties(ArrayList<TNameValuePair> fileFormatProperties) {
175        this.fileFormatProperties = fileFormatProperties;
176    }
177
178    /**
179     * Parse file format string and create name-value pair list
180     *
181     * <p>Parses file format options in formats like:</p>
182     * <ul>
183     *   <li>Single line: file_format=(TYPE=CSV FIELD_DELIMITER='|' SKIP_HEADER=1)</li>
184     *   <li>Multi-line: FILE_FORMAT = (TYPE = CSV, FIELD_DELIMITER = ',', SKIP_HEADER = 1)</li>
185     * </ul>
186     *
187     * @param fileFormatString the file_format string to parse
188     * @return ArrayList of TNameValuePair objects representing the properties
189     */
190    public ArrayList<TNameValuePair> parseFileFormatProperties(String fileFormatString) {
191        if (fileFormatString == null || fileFormatString.trim().isEmpty()) {
192            return null;
193        }
194
195        ArrayList<TNameValuePair> properties = new ArrayList<TNameValuePair>();
196
197        // Normalize the input: remove line breaks and extra spaces
198        String normalized = fileFormatString.replaceAll("\\s+", " ").trim();
199
200        // Extract content within parentheses
201        // Match pattern: file_format=(...)  or FILE_FORMAT = (...)
202        Pattern outerPattern = Pattern.compile("(?i)file_format\\s*=\\s*\\((.+)\\)", Pattern.DOTALL);
203        Matcher outerMatcher = outerPattern.matcher(normalized);
204
205        String content;
206        if (outerMatcher.find()) {
207            content = outerMatcher.group(1).trim();
208        } else {
209            // Try to match just the content in parentheses (...)
210            Pattern parenthesesPattern = Pattern.compile("\\((.+)\\)", Pattern.DOTALL);
211            Matcher parenthesesMatcher = parenthesesPattern.matcher(normalized);
212            if (parenthesesMatcher.find()) {
213                content = parenthesesMatcher.group(1).trim();
214            } else {
215                // No parentheses found, assume the whole string is the content
216                content = normalized;
217            }
218        }
219
220        // Split by comma or space, but respect quoted strings and parentheses
221        ArrayList<String> pairs;
222        if (hasUnquotedComma(content)) {
223            // Comma-separated format
224            pairs = splitByComma(content);
225        } else {
226            // Space-separated format
227            pairs = splitBySpace(content);
228        }
229
230        // Parse each name=value pair
231        for (String pair : pairs) {
232            pair = pair.trim();
233            if (pair.isEmpty()) {
234                continue;
235            }
236
237            // Split by '=' to get name and value
238            int equalsIndex = findEqualsIndex(pair);
239            if (equalsIndex > 0) {
240                String name = pair.substring(0, equalsIndex).trim();
241                String value = pair.substring(equalsIndex + 1).trim();
242
243                TNameValuePair nvPair = new TNameValuePair();
244                nvPair.init(name, value);
245                properties.add(nvPair);
246            }
247        }
248
249        // Store in the field and return
250        this.fileFormatProperties = properties;
251        return properties;
252    }
253
254    /**
255     * Check if the content has any unquoted comma
256     * Helper method for parseFileFormatProperties
257     */
258    private boolean hasUnquotedComma(String content) {
259        boolean inSingleQuote = false;
260        boolean inDoubleQuote = false;
261
262        for (int i = 0; i < content.length(); i++) {
263            char ch = content.charAt(i);
264
265            if (ch == '\'' && !inDoubleQuote) {
266                inSingleQuote = !inSingleQuote;
267            } else if (ch == '"' && !inSingleQuote) {
268                inDoubleQuote = !inDoubleQuote;
269            } else if (ch == ',' && !inSingleQuote && !inDoubleQuote) {
270                return true; // Found an unquoted comma
271            }
272        }
273
274        return false;
275    }
276
277    /**
278     * Split string by comma, but respect quoted strings and parentheses
279     * Helper method for parseFileFormatProperties
280     */
281    private ArrayList<String> splitByComma(String content) {
282        ArrayList<String> parts = new ArrayList<String>();
283        StringBuilder current = new StringBuilder();
284        boolean inSingleQuote = false;
285        boolean inDoubleQuote = false;
286        int parenthesesDepth = 0;
287
288        for (int i = 0; i < content.length(); i++) {
289            char ch = content.charAt(i);
290
291            if (ch == '\'' && !inDoubleQuote) {
292                inSingleQuote = !inSingleQuote;
293                current.append(ch);
294            } else if (ch == '"' && !inSingleQuote) {
295                inDoubleQuote = !inDoubleQuote;
296                current.append(ch);
297            } else if (ch == '(' && !inSingleQuote && !inDoubleQuote) {
298                parenthesesDepth++;
299                current.append(ch);
300            } else if (ch == ')' && !inSingleQuote && !inDoubleQuote) {
301                parenthesesDepth--;
302                current.append(ch);
303            } else if (ch == ',' && !inSingleQuote && !inDoubleQuote && parenthesesDepth == 0) {
304                // Found a separator comma
305                parts.add(current.toString());
306                current.setLength(0); // Clear StringBuilder
307            } else {
308                current.append(ch);
309            }
310        }
311
312        // Add the last part
313        if (current.length() > 0) {
314            parts.add(current.toString());
315        }
316
317        return parts;
318    }
319
320    /**
321     * Split string by space, but respect quoted strings
322     * Helper method for parseFileFormatProperties (for space-separated format)
323     */
324    private ArrayList<String> splitBySpace(String content) {
325        ArrayList<String> parts = new ArrayList<String>();
326        StringBuilder current = new StringBuilder();
327        boolean inSingleQuote = false;
328        boolean inDoubleQuote = false;
329        boolean inValue = false; // Track if we're currently building a value after '='
330
331        for (int i = 0; i < content.length(); i++) {
332            char ch = content.charAt(i);
333
334            if (ch == '\'' && !inDoubleQuote) {
335                inSingleQuote = !inSingleQuote;
336                current.append(ch);
337            } else if (ch == '"' && !inSingleQuote) {
338                inDoubleQuote = !inDoubleQuote;
339                current.append(ch);
340            } else if (ch == '=' && !inSingleQuote && !inDoubleQuote) {
341                current.append(ch);
342                inValue = true; // After '=', we're in the value part
343            } else if (ch == ' ' && !inSingleQuote && !inDoubleQuote && inValue) {
344                // Space after a value - end current pair
345                parts.add(current.toString());
346                current.setLength(0);
347                inValue = false;
348            } else if (ch == ' ' && !inSingleQuote && !inDoubleQuote && !inValue) {
349                // Space before '=' - just skip it
350                if (current.length() > 0) {
351                    current.append(ch);
352                }
353            } else {
354                current.append(ch);
355            }
356        }
357
358        // Add the last part
359        if (current.length() > 0) {
360            parts.add(current.toString());
361        }
362
363        return parts;
364    }
365
366    /**
367     * Find the index of the first '=' that is not inside quotes
368     * Helper method for parseFileFormatProperties
369     */
370    private int findEqualsIndex(String pair) {
371        boolean inSingleQuote = false;
372        boolean inDoubleQuote = false;
373
374        for (int i = 0; i < pair.length(); i++) {
375            char ch = pair.charAt(i);
376
377            if (ch == '\'' && !inDoubleQuote) {
378                inSingleQuote = !inSingleQuote;
379            } else if (ch == '"' && !inSingleQuote) {
380                inDoubleQuote = !inDoubleQuote;
381            } else if (ch == '=' && !inSingleQuote && !inDoubleQuote) {
382                return i;
383            }
384        }
385
386        return -1; // No equals sign found
387    }
388
389    private TStageLocation stageLocation;
390
391    public void setStageLocation(TStageLocation stageLocation) {
392        this.stageLocation = stageLocation;
393    }
394
395    public TStageLocation getStageLocation() {
396        return stageLocation;
397    }
398
399
400
401    public String getAwsSnsTopic() {
402        return awsSnsTopic;
403    }
404
405    public void setAwsSnsTopic(String awsSnsTopic) {
406        this.awsSnsTopic = awsSnsTopic;
407    }
408
409    public TObjectNameList getPartitionColumnList() {
410        return partitionColumnList;
411    }
412
413    public void setPartitionColumnList(TObjectNameList partitionColumnList) {
414        this.partitionColumnList = partitionColumnList;
415    }
416
417    public String getCompression() {
418        return compression;
419    }
420
421    public void setCompression(String compression) {
422        this.compression = compression;
423    }
424
425    public String getDescription() {
426        return description;
427    }
428
429    public void setDescription(String description) {
430        this.description = description;
431    }
432
433    public String getExpirationTimestamp() {
434        return expirationTimestamp;
435    }
436
437    public void setExpirationTimestamp(String expirationTimestamp) {
438        this.expirationTimestamp = expirationTimestamp;
439    }
440
441    public String getFieldDelimiter() {
442        return fieldDelimiter;
443    }
444
445    public void setFieldDelimiter(String fieldDelimiter) {
446        this.fieldDelimiter = fieldDelimiter;
447    }
448
449    public String getFormat() {
450        return format;
451    }
452
453    public void setFormat(String format) {
454        this.format = format;
455    }
456
457    public List<String> getDecimalTargetTypes() {
458        return decimalTargetTypes;
459    }
460
461    public void setDecimalTargetTypes(List<String> decimalTargetTypes) {
462        this.decimalTargetTypes = decimalTargetTypes;
463    }
464
465    public String getHivePartitionUriPrefix() {
466        return hivePartitionUriPrefix;
467    }
468
469    public void setHivePartitionUriPrefix(String hivePartitionUriPrefix) {
470        this.hivePartitionUriPrefix = hivePartitionUriPrefix;
471    }
472
473    public Boolean getIgnoreUnknownValues() {
474        return ignoreUnknownValues;
475    }
476
477    public void setIgnoreUnknownValues(Boolean ignoreUnknownValues) {
478        this.ignoreUnknownValues = ignoreUnknownValues;
479    }
480
481    public Long getMaxBadRecords() {
482        return maxBadRecords;
483    }
484
485    public void setMaxBadRecords(Long maxBadRecords) {
486        this.maxBadRecords = maxBadRecords;
487    }
488
489    public void setRequireHivePartitionFilter(Boolean requireHivePartitionFilter) {
490        this.requireHivePartitionFilter = requireHivePartitionFilter;
491    }
492
493    public String getNullMarker() {
494        return nullMarker;
495    }
496
497    public void setNullMarker(String nullMarker) {
498        this.nullMarker = nullMarker;
499    }
500
501    public String getProjectionFields() {
502        return projectionFields;
503    }
504
505    public void setProjectionFields(String projectionFields) {
506        this.projectionFields = projectionFields;
507    }
508
509    public String getQuote() {
510        return quote;
511    }
512
513    public void setQuote(String quote) {
514        this.quote = quote;
515    }
516
517    public Boolean getRequireHivePartitionFilter() {
518        return requireHivePartitionFilter;
519    }
520
521    public String getSheetRange() {
522        return sheetRange;
523    }
524
525    public void setSheetRange(String sheetRange) {
526        this.sheetRange = sheetRange;
527    }
528
529    public Long getSkipLeadingRows() {
530        return skipLeadingRows;
531    }
532
533    public void setSkipLeadingRows(Long skipLeadingRows) {
534        this.skipLeadingRows = skipLeadingRows;
535    }
536
537    public List<String> getUris() {
538        return uris;
539    }
540
541    public void setUris(List<String> uris) {
542        this.uris = uris;
543    }
544
545    public Boolean getAllowJaggedRows() {
546        return allowJaggedRows;
547    }
548
549    public void setAllowJaggedRows(Boolean allowJaggedRows) {
550        this.allowJaggedRows = allowJaggedRows;
551    }
552
553    public Boolean getAllowQuotedNewlines() {
554        return allowQuotedNewlines;
555    }
556
557    public void setAllowQuotedNewlines(Boolean allowQuotedNewlines) {
558        this.allowQuotedNewlines = allowQuotedNewlines;
559    }
560
561    public Boolean getEnableLogicalTypes() {
562        return enableLogicalTypes;
563    }
564
565    public void setEnableLogicalTypes(Boolean enableLogicalTypes) {
566        this.enableLogicalTypes = enableLogicalTypes;
567    }
568
569    public String getEncoding() {
570        return encoding;
571    }
572
573    public void setEncoding(String encoding) {
574        this.encoding = encoding;
575    }
576    public void setEncoding(TSourceToken encoding) {
577                this.encoding = encoding.toString();
578    } // added by baffle
579
580    public String getExternalStageURL() {
581        return externalStageURL;
582    }
583
584    public void setExternalStageURL(String externalStageURL) {
585        this.externalStageURL = externalStageURL;
586    }
587
588    public void setPartitionByExpr(TExpression partitionByExpr) {
589        this.partitionByExpr = partitionByExpr;
590    }
591
592    public void setFileFormatName(String fileFormatName) {
593        this.fileFormatName = fileFormatName;
594    }
595
596    public void setFileFormatType(String fileFormatType) {
597        this.fileFormatType = fileFormatType;
598    }
599
600    public void setDateRetentionInDays(TSourceToken dateRetentionInDays) {
601        this.dateRetentionInDays = dateRetentionInDays;
602    }
603
604    public void setCommentToken(TSourceToken commentToken) {
605        this.commentToken = commentToken;
606    }
607
608    public void setStageFileFormat(TDummy stageFileFormat) {
609        this.stageFileFormat = stageFileFormat;
610    }
611
612    public void setCopyOptions(TDummy copyOptions) {
613        this.copyOptions = copyOptions;
614    }
615
616    public void setExpressionList(TExpressionList expressionList) {
617        this.expressionList = expressionList;
618    }
619
620    private TSourceToken dateRetentionInDays;
621
622    public TSourceToken getDateRetentionInDays() {
623        return dateRetentionInDays;
624    }
625
626    private TSourceToken commentToken;
627
628    /**
629     * @deprecated since v 2.8.1.1, please use {@link #getComment()} instead
630     * @return
631     */
632    public TSourceToken getCommentToken() {
633        return commentToken;
634    }
635
636    private TDummy stageFileFormat;
637
638    private TDummy copyOptions;
639
640    public TDummy getCopyOptions() {
641        return copyOptions;
642    }
643
644    protected ECreateTableOption createTableOptionType;
645
646    public void setCreateTableOptionType(ECreateTableOption createTableOptionType) {
647        this.createTableOptionType = createTableOptionType;
648    }
649
650    public ECreateTableOption getCreateTableOptionType() {
651        return createTableOptionType;
652    }
653
654    public void  init(Object arg1){
655        createTableOptionType = (ECreateTableOption)arg1;
656    }
657
658    private TExpressionList expressionList;
659
660    public TExpressionList getExpressionList() {
661        return expressionList;
662    }
663
664    public TDummy getStageFileFormat() {
665        return stageFileFormat;
666    }
667
668    private String externalStageURL=null;
669
670    private TObjectName tableLocation;
671
672    /**
673     * The path of a {@link ECreateTableOption#etoLocation} option, {@code 's3://bucket/path/'} in
674     * {@code LOCATION 's3://bucket/path/'}: where the files of the table are. Null for any other option.
675     */
676    public TObjectName getTableLocation() {
677        return tableLocation;
678    }
679
680    public void init(Object arg1, Object arg2){
681        init(arg1);
682        switch(createTableOptionType){
683            case etoLocation:
684                this.tableLocation = (TObjectName)arg2;
685                break;
686            case etoClusterBy:
687                this.expressionList = (TExpressionList)arg2;
688                break;
689            case etoStageCopyOptions:
690                this.copyOptions = (TDummy)arg2;
691                break;
692            case etoStageFileFormat:
693                this.stageFileFormat = (TDummy)arg2;
694                break;
695            case etoComment:
696                //this.commentToken = (TSourceToken)arg2;
697                this.comment = (TObjectName) arg2;
698                break;
699            case etoBackfillFrom:
700                this.backfillFrom = (TObjectName) arg2;
701                break;
702            case etoDateRetentionTimeInDays:
703                this.dateRetentionInDays = (TSourceToken)arg2;
704                break;
705            case etoPartitionBy:
706                if (arg2 instanceof TObjectNameList){
707                    this.partitionColumnList = (TObjectNameList)arg2;
708                }else if (arg2 instanceof  TExpression){
709                    this.partitionByExpr = (TExpression)arg2;
710                }
711                break;
712            case etoAWSSnsTopic:
713                awsSnsTopic = ((TSourceToken)arg2).toString();
714                break;
715            case etoStageURL:
716                externalStageURL = ((TSourceToken)arg2).toString();
717                break;
718            case etoFiles:
719                this.expressionList = (TExpressionList)arg2;
720                break;
721            case etoDistributeOn:
722            case etoOrganizeOn:
723                this.columnNamelist =(TObjectNameList)arg2;
724                break;
725            case etoReloptions:
726                this.attributeOptions  = (ArrayList<TAttributeOption>)arg2;
727                break;
728            case etoPartitionSpec:
729                this.partitionSpec = (TBaseTablePartition)arg2;
730                break;
731            case etoDistributeBy:
732                this.distributeBy = (TDistributeBy)arg2;
733                break;
734            case etoDistributeByHash:
735                this.columnNamelist = (TObjectNameList)arg2;
736                break;
737            case etoPartitioningKey:
738                this.columnNamelist = (TObjectNameList)arg2;
739                break;
740            case etoOrganizeBy:
741            case etoOrganizeByDimensions:
742                this.valueRowItemList = (TMultiTargetList)arg2;
743                break;
744        }
745    }
746
747    private TMultiTargetList valueRowItemList;
748
749    public TMultiTargetList getValueRowItemList() {
750        return valueRowItemList;
751    }
752
753    public ArrayList<TAttributeOption> getAttributeOptions() {
754        return attributeOptions;
755    }
756
757    private ArrayList<TAttributeOption> attributeOptions ;
758
759    private void parseBigQueryTableOption() {
760        String options = this.toString().trim();
761        if (!options.toUpperCase(java.util.Locale.ROOT).startsWith("OPTIONS"))
762            return;
763        options = options.replaceFirst("(?i)OPTIONS", "").trim();
764        options = options.substring(1, options.length() - 1).trim();
765        if (!options.endsWith(",")) {
766            options = options + ",";
767        }
768
769        this.allowJaggedRows = getPatternBoolean(options, "allow_jagged_rows");
770        this.allowQuotedNewlines = getPatternBoolean(options, "allow_quoted_newlines");
771        this.compression = getPatternString(options, "compression");
772        this.description = getPatternString(options, "description");
773        this.enableLogicalTypes = getPatternBoolean(options, "enable_logical_types");
774        this.encoding = getPatternString(options, "encoding");
775        this.expirationTimestamp = getTimestampString(options, "expiration_timestamp");
776        this.fieldDelimiter = getPatternString(options, "field_delimiter");
777        this.format = getPatternString(options, "format");
778        this.decimalTargetTypes = getPatternArray(options, "decimal_target_types");
779        this.hivePartitionUriPrefix = getPatternString(options, "hive_partition_uri_prefix");
780        this.ignoreUnknownValues = getPatternBoolean(options, "ignore_unknown_values");
781        this.maxBadRecords = getPatternLong(options, "max_bad_records");
782        this.nullMarker = getPatternString(options, "null_marker");
783        this.projectionFields = getPatternString(options, "projection_fields");
784        this.quote = getPatternString(options, "quote");
785        this.requireHivePartitionFilter = getPatternBoolean(options, "require_hive_partition_filter");
786        this.sheetRange = getPatternString(options, "sheet_range");
787        this.skipLeadingRows = getPatternLong(options, "skip_leading_rows");
788        this.uris = getPatternArray(options, "uris");
789    }
790
791    private String getTimestampString(String content, String field) {
792        content = content.replaceAll("(\\s*,)+", ",");
793        if (content.matches("(?is).*" + field + "\\s*=(.+?)=.+")) {
794            String patternExp = "(?is)" + field + "\\s*=(.+?)=";
795            Pattern pattern = Pattern.compile(patternExp);
796            Matcher matcher = pattern.matcher(content);
797            if (matcher.find()) {
798                String timestamp = matcher.group(1).trim();
799                timestamp = timestamp.substring(0, timestamp.lastIndexOf(",")).trim();
800                return timestamp;
801            }
802        }
803        else if(content.matches("(?is).*" + field + "\\s*=(.+),")){
804            String patternExp = "(?is)" + field + "\\s*=(.+),";
805            Pattern pattern = Pattern.compile(patternExp);
806            Matcher matcher = pattern.matcher(content);
807            if (matcher.find()) {
808                String timestamp = matcher.group(1).trim();
809                return timestamp;
810            }
811        } 
812        return null;
813    }
814
815    private String getPatternString(String content, String field) {
816        String patternExp = "(?is)" + field + "\\s*=\\s*(\".+?\"),";
817        Pattern pattern = Pattern.compile(patternExp);
818        Matcher matcher = pattern.matcher(content);
819        if (matcher.find()) {
820            return matcher.group(1).trim();
821        }
822        patternExp = "(?is)" + field + "\\s*=\\s*('.+?\'),";
823        pattern = Pattern.compile(patternExp);
824        matcher = pattern.matcher(content);
825        if (matcher.find()) {
826            return matcher.group(1).trim();
827        }
828        return null;
829    }
830
831    private Boolean getPatternBoolean(String content, String field) {
832        String patternExp = "(?is)" + field + "\\s*=(.+?),";
833        Pattern pattern = Pattern.compile(patternExp);
834        Matcher matcher = pattern.matcher(content);
835        if (matcher.find()) {
836            return Boolean.parseBoolean(matcher.group(1).trim());
837        }
838        return null;
839    }
840
841    private Long getPatternLong(String content, String field) {
842        String patternExp = "(?is)" + field + "\\s*=(.+?),";
843        Pattern pattern = Pattern.compile(patternExp);
844        Matcher matcher = pattern.matcher(content);
845        if (matcher.find()) {
846            return Long.parseLong(matcher.group(1).trim());
847        }
848        return null;
849    }
850
851    private List<String> getPatternArray(String content, String field) {
852        String patternExp = "(?is)" + field + "\\s*=\\s*\\[(.+?)\\]\\s*,";
853        Pattern pattern = Pattern.compile(patternExp);
854        Matcher matcher = pattern.matcher(content);
855        if (matcher.find()) {
856            String arrayItems = matcher.group(1).trim();
857            if (arrayItems.startsWith("(") && arrayItems.endsWith(")")) {
858                arrayItems = arrayItems.substring(1, arrayItems.length() - 1).trim();
859            }
860            return Arrays.asList(arrayItems.split("\\s*,\\s*"));
861        }
862        return null;
863    }
864
865    public void doParse(TCustomSqlStatement psql, ESqlClause plocation) {
866
867        if (psql instanceof TCreateTableSqlStatement) {
868            TCreateTableSqlStatement c = (TCreateTableSqlStatement) psql;
869
870            switch (this.createTableOptionType){
871                case etoPartitionBy:
872                    if (this.partitionColumnList != null){
873                        c.setPartitionColumnList(this.partitionColumnList);
874                    }else if (this.partitionByExpr != null){
875                        c.setPartitionByExpr(this.partitionByExpr);
876                        this.partitionByExpr.doParse(c,ESqlClause.createTable);
877                    }
878                    break;
879                case etoPattern:
880                    c.setRegex_pattern(this.getEndToken().toString());
881                    break;
882                case etoComment:
883                    c.setTableComment(TObjectName.createObjectName (psql.dbvendor, EDbObjectType.comment, this.commentToken));
884                    break;
885                case etoWithLocation:
886                    c.setStageLocation(this.getStageLocation());
887                    break;
888                case etoFileFormat:
889                    c.setFileFormatName(this.fileFormatName);
890                    c.setFileFormatType(this.fileFormatType);
891                    fileFormatProperties = parseFileFormatProperties(this.toString());
892                    break;
893                case etoAWSSnsTopic:
894                    c.setAwsSnsTopic(this.awsSnsTopic);
895                    break;
896                case etoBigQueryExternal:
897                    parseBigQueryTableOption();
898                    break;
899                case etoClusterBy:
900                    if (expressionList != null){
901                        expressionList.doParse(psql,plocation);
902                    }
903                    break;
904                case etoBackfillFrom:
905                    this.backfillFrom.setDbObjectType(
906                            psql.dbvendor, EDbObjectType.table);
907                    break;
908                default:
909                    break;
910
911            }
912        }else if(psql instanceof TCreateStageStmt){
913            TCreateStageStmt c = (TCreateStageStmt)psql;
914            switch (this.createTableOptionType){
915                case etoFileFormat:
916                    c.setFileFormatName(this.fileFormatName);
917                    c.setFileFormatType(this.fileFormatType);
918                    break;
919                case etoStageURL:
920                    c.setExternalStageURL(this.externalStageURL);
921                    break;
922                default:
923                    break;
924            }
925        }else if(psql instanceof TAlterStageStmt){
926            TAlterStageStmt c = (TAlterStageStmt)psql;
927            switch (this.createTableOptionType){
928                case etoFileFormat:
929                    c.setFileFormatName(this.fileFormatName);
930                    c.setFileFormatType(this.fileFormatType);
931                    break;
932                case etoStageURL:
933                    c.setExternalStageURL(this.externalStageURL);
934                    break;
935                default:
936                    break;
937            }
938        }else if(psql instanceof TSnowflakeCopyIntoStmt){
939            TSnowflakeCopyIntoStmt c = (TSnowflakeCopyIntoStmt)psql;
940            switch (this.createTableOptionType){
941                case etoFileFormat:
942                    c.setFileFormatName(this.fileFormatName);
943                    c.setFileFormatType(this.fileFormatType);
944                    break;
945                case etoStageURL:
946                    break;
947                case etoPattern:
948                    c.setRegex_pattern(this.getEndToken().toString());
949                    break;
950                case etoFiles:
951                    for(TExpression e:this.expressionList){
952                        c.getFileList().add(e.toString());
953                    }
954                    break;
955                default:
956                    break;
957            }
958        }
959    }
960
961    public void accept(TParseTreeVisitor v){
962        v.preVisit(this);
963        v.postVisit(this);
964    }
965    public void acceptChildren(TParseTreeVisitor v){
966        v.preVisit(this);
967        v.postVisit(this);
968    }
969}