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    public void init(Object arg1, Object arg2){
671        init(arg1);
672        switch(createTableOptionType){
673            case etoClusterBy:
674                this.expressionList = (TExpressionList)arg2;
675                break;
676            case etoStageCopyOptions:
677                this.copyOptions = (TDummy)arg2;
678                break;
679            case etoStageFileFormat:
680                this.stageFileFormat = (TDummy)arg2;
681                break;
682            case etoComment:
683                //this.commentToken = (TSourceToken)arg2;
684                this.comment = (TObjectName) arg2;
685                break;
686            case etoBackfillFrom:
687                this.backfillFrom = (TObjectName) arg2;
688                break;
689            case etoDateRetentionTimeInDays:
690                this.dateRetentionInDays = (TSourceToken)arg2;
691                break;
692            case etoPartitionBy:
693                if (arg2 instanceof TObjectNameList){
694                    this.partitionColumnList = (TObjectNameList)arg2;
695                }else if (arg2 instanceof  TExpression){
696                    this.partitionByExpr = (TExpression)arg2;
697                }
698                break;
699            case etoAWSSnsTopic:
700                awsSnsTopic = ((TSourceToken)arg2).toString();
701                break;
702            case etoStageURL:
703                externalStageURL = ((TSourceToken)arg2).toString();
704                break;
705            case etoFiles:
706                this.expressionList = (TExpressionList)arg2;
707                break;
708            case etoDistributeOn:
709            case etoOrganizeOn:
710                this.columnNamelist =(TObjectNameList)arg2;
711                break;
712            case etoReloptions:
713                this.attributeOptions  = (ArrayList<TAttributeOption>)arg2;
714                break;
715            case etoPartitionSpec:
716                this.partitionSpec = (TBaseTablePartition)arg2;
717                break;
718            case etoDistributeBy:
719                this.distributeBy = (TDistributeBy)arg2;
720                break;
721            case etoDistributeByHash:
722                this.columnNamelist = (TObjectNameList)arg2;
723                break;
724            case etoPartitioningKey:
725                this.columnNamelist = (TObjectNameList)arg2;
726                break;
727            case etoOrganizeBy:
728            case etoOrganizeByDimensions:
729                this.valueRowItemList = (TMultiTargetList)arg2;
730                break;
731        }
732    }
733
734    private TMultiTargetList valueRowItemList;
735
736    public TMultiTargetList getValueRowItemList() {
737        return valueRowItemList;
738    }
739
740    public ArrayList<TAttributeOption> getAttributeOptions() {
741        return attributeOptions;
742    }
743
744    private ArrayList<TAttributeOption> attributeOptions ;
745
746    private void parseBigQueryTableOption() {
747        String options = this.toString().trim();
748        if (!options.toUpperCase().startsWith("OPTIONS"))
749            return;
750        options = options.replaceFirst("(?i)OPTIONS", "").trim();
751        options = options.substring(1, options.length() - 1).trim();
752        if (!options.endsWith(",")) {
753            options = options + ",";
754        }
755
756        this.allowJaggedRows = getPatternBoolean(options, "allow_jagged_rows");
757        this.allowQuotedNewlines = getPatternBoolean(options, "allow_quoted_newlines");
758        this.compression = getPatternString(options, "compression");
759        this.description = getPatternString(options, "description");
760        this.enableLogicalTypes = getPatternBoolean(options, "enable_logical_types");
761        this.encoding = getPatternString(options, "encoding");
762        this.expirationTimestamp = getTimestampString(options, "expiration_timestamp");
763        this.fieldDelimiter = getPatternString(options, "field_delimiter");
764        this.format = getPatternString(options, "format");
765        this.decimalTargetTypes = getPatternArray(options, "decimal_target_types");
766        this.hivePartitionUriPrefix = getPatternString(options, "hive_partition_uri_prefix");
767        this.ignoreUnknownValues = getPatternBoolean(options, "ignore_unknown_values");
768        this.maxBadRecords = getPatternLong(options, "max_bad_records");
769        this.nullMarker = getPatternString(options, "null_marker");
770        this.projectionFields = getPatternString(options, "projection_fields");
771        this.quote = getPatternString(options, "quote");
772        this.requireHivePartitionFilter = getPatternBoolean(options, "require_hive_partition_filter");
773        this.sheetRange = getPatternString(options, "sheet_range");
774        this.skipLeadingRows = getPatternLong(options, "skip_leading_rows");
775        this.uris = getPatternArray(options, "uris");
776    }
777
778    private String getTimestampString(String content, String field) {
779        content = content.replaceAll("(\\s*,)+", ",");
780        if (content.matches("(?is).*" + field + "\\s*=(.+?)=.+")) {
781            String patternExp = "(?is)" + field + "\\s*=(.+?)=";
782            Pattern pattern = Pattern.compile(patternExp);
783            Matcher matcher = pattern.matcher(content);
784            if (matcher.find()) {
785                String timestamp = matcher.group(1).trim();
786                timestamp = timestamp.substring(0, timestamp.lastIndexOf(",")).trim();
787                return timestamp;
788            }
789        }
790        else if(content.matches("(?is).*" + field + "\\s*=(.+),")){
791            String patternExp = "(?is)" + field + "\\s*=(.+),";
792            Pattern pattern = Pattern.compile(patternExp);
793            Matcher matcher = pattern.matcher(content);
794            if (matcher.find()) {
795                String timestamp = matcher.group(1).trim();
796                return timestamp;
797            }
798        } 
799        return null;
800    }
801
802    private String getPatternString(String content, String field) {
803        String patternExp = "(?is)" + field + "\\s*=\\s*(\".+?\"),";
804        Pattern pattern = Pattern.compile(patternExp);
805        Matcher matcher = pattern.matcher(content);
806        if (matcher.find()) {
807            return matcher.group(1).trim();
808        }
809        patternExp = "(?is)" + field + "\\s*=\\s*('.+?\'),";
810        pattern = Pattern.compile(patternExp);
811        matcher = pattern.matcher(content);
812        if (matcher.find()) {
813            return matcher.group(1).trim();
814        }
815        return null;
816    }
817
818    private Boolean getPatternBoolean(String content, String field) {
819        String patternExp = "(?is)" + field + "\\s*=(.+?),";
820        Pattern pattern = Pattern.compile(patternExp);
821        Matcher matcher = pattern.matcher(content);
822        if (matcher.find()) {
823            return Boolean.parseBoolean(matcher.group(1).trim());
824        }
825        return null;
826    }
827
828    private Long getPatternLong(String content, String field) {
829        String patternExp = "(?is)" + field + "\\s*=(.+?),";
830        Pattern pattern = Pattern.compile(patternExp);
831        Matcher matcher = pattern.matcher(content);
832        if (matcher.find()) {
833            return Long.parseLong(matcher.group(1).trim());
834        }
835        return null;
836    }
837
838    private List<String> getPatternArray(String content, String field) {
839        String patternExp = "(?is)" + field + "\\s*=\\s*\\[(.+?)\\]\\s*,";
840        Pattern pattern = Pattern.compile(patternExp);
841        Matcher matcher = pattern.matcher(content);
842        if (matcher.find()) {
843            String arrayItems = matcher.group(1).trim();
844            if (arrayItems.startsWith("(") && arrayItems.endsWith(")")) {
845                arrayItems = arrayItems.substring(1, arrayItems.length() - 1).trim();
846            }
847            return Arrays.asList(arrayItems.split("\\s*,\\s*"));
848        }
849        return null;
850    }
851
852    public void doParse(TCustomSqlStatement psql, ESqlClause plocation) {
853
854        if (psql instanceof TCreateTableSqlStatement) {
855            TCreateTableSqlStatement c = (TCreateTableSqlStatement) psql;
856
857            switch (this.createTableOptionType){
858                case etoPartitionBy:
859                    if (this.partitionColumnList != null){
860                        c.setPartitionColumnList(this.partitionColumnList);
861                    }else if (this.partitionByExpr != null){
862                        c.setPartitionByExpr(this.partitionByExpr);
863                        this.partitionByExpr.doParse(c,ESqlClause.createTable);
864                    }
865                    break;
866                case etoPattern:
867                    c.setRegex_pattern(this.getEndToken().toString());
868                    break;
869                case etoComment:
870                    c.setTableComment(TObjectName.createObjectName (psql.dbvendor, EDbObjectType.comment, this.commentToken));
871                    break;
872                case etoWithLocation:
873                    c.setStageLocation(this.getStageLocation());
874                    break;
875                case etoFileFormat:
876                    c.setFileFormatName(this.fileFormatName);
877                    c.setFileFormatType(this.fileFormatType);
878                    fileFormatProperties = parseFileFormatProperties(this.toString());
879                    break;
880                case etoAWSSnsTopic:
881                    c.setAwsSnsTopic(this.awsSnsTopic);
882                    break;
883                case etoBigQueryExternal:
884                    parseBigQueryTableOption();
885                    break;
886                case etoClusterBy:
887                    if (expressionList != null){
888                        expressionList.doParse(psql,plocation);
889                    }
890                    break;
891                case etoBackfillFrom:
892                    this.backfillFrom.setDbObjectType(
893                            psql.dbvendor, EDbObjectType.table);
894                    break;
895                default:
896                    break;
897
898            }
899        }else if(psql instanceof TCreateStageStmt){
900            TCreateStageStmt c = (TCreateStageStmt)psql;
901            switch (this.createTableOptionType){
902                case etoFileFormat:
903                    c.setFileFormatName(this.fileFormatName);
904                    c.setFileFormatType(this.fileFormatType);
905                    break;
906                case etoStageURL:
907                    c.setExternalStageURL(this.externalStageURL);
908                    break;
909                default:
910                    break;
911            }
912        }else if(psql instanceof TAlterStageStmt){
913            TAlterStageStmt c = (TAlterStageStmt)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 TSnowflakeCopyIntoStmt){
926            TSnowflakeCopyIntoStmt c = (TSnowflakeCopyIntoStmt)psql;
927            switch (this.createTableOptionType){
928                case etoFileFormat:
929                    c.setFileFormatName(this.fileFormatName);
930                    c.setFileFormatType(this.fileFormatType);
931                    break;
932                case etoStageURL:
933                    break;
934                case etoPattern:
935                    c.setRegex_pattern(this.getEndToken().toString());
936                    break;
937                case etoFiles:
938                    for(TExpression e:this.expressionList){
939                        c.getFileList().add(e.toString());
940                    }
941                    break;
942                default:
943                    break;
944            }
945        }
946    }
947
948    public void accept(TParseTreeVisitor v){
949        v.preVisit(this);
950        v.postVisit(this);
951    }
952    public void acceptChildren(TParseTreeVisitor v){
953        v.preVisit(this);
954        v.postVisit(this);
955    }
956}