001package gudusoft.gsqlparser.nodes.starrocks;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.nodes.*;
005import gudusoft.gsqlparser.nodes.flink.TFlinkTableProperty;
006
007/**
008 * Represents the StarRocks FILES() table function for reading data from external storage.
009 *
010 * <p>The FILES() function allows querying data files directly from cloud storage
011 * (S3, HDFS, GCS, Azure Blob, etc.) without creating external tables.</p>
012 *
013 * <p>Syntax:</p>
014 * <pre>
015 * FILES(
016 *     "path" = "s3://bucket/path/*.parquet",
017 *     "format" = "parquet",
018 *     "aws.s3.access_key" = "xxx",
019 *     "aws.s3.secret_key" = "xxx",
020 *     "aws.s3.region" = "us-west-2"
021 * )
022 * </pre>
023 *
024 * <p>Example usage:</p>
025 * <pre>
026 * SELECT * FROM FILES(
027 *     "path" = "s3://bucket/data.parquet",
028 *     "format" = "parquet"
029 * );
030 *
031 * INSERT INTO my_table SELECT * FROM FILES(...);
032 * </pre>
033 *
034 * @see <a href="https://docs.starrocks.io/docs/sql-reference/sql-functions/table-functions/files/">StarRocks FILES Documentation</a>
035 */
036public class TFilesTableFunction extends TTableFunction {
037
038    /**
039     * The list of key-value properties passed to FILES().
040     * Common properties include:
041     * - path: The file path (S3, HDFS, local, etc.)
042     * - format: File format (parquet, orc, csv, json, avro)
043     * - aws.s3.access_key, aws.s3.secret_key, aws.s3.region: S3 credentials
044     * - columns_from_path: Extract columns from file path
045     * - list_files_only: Only list files without reading data
046     * - list_recursively: Recursively list directories
047     */
048    private TPTNodeList<TFlinkTableProperty> properties;
049
050    /**
051     * Creates a new TFilesTableFunction.
052     */
053    public TFilesTableFunction() {
054        this.functionType = EFunctionType.files_t;
055    }
056
057    /**
058     * Initialize with properties list.
059     * @param arg1 the properties list (TPTNodeList&lt;TFlinkTableProperty&gt;)
060     */
061    @Override
062    public void init(Object arg1) {
063        if (arg1 instanceof TPTNodeList) {
064            @SuppressWarnings("unchecked")
065            TPTNodeList<TFlinkTableProperty> props = (TPTNodeList<TFlinkTableProperty>) arg1;
066            this.properties = props;
067        }
068    }
069
070    /**
071     * Initialize with function name and properties list.
072     * @param arg1 the function name (TObjectName)
073     * @param arg2 the properties list (TPTNodeList&lt;TFlinkTableProperty&gt;)
074     */
075    @Override
076    public void init(Object arg1, Object arg2) {
077        if (arg1 instanceof TObjectName) {
078            this.functionName = (TObjectName) arg1;
079        }
080        if (arg2 instanceof TPTNodeList) {
081            @SuppressWarnings("unchecked")
082            TPTNodeList<TFlinkTableProperty> props = (TPTNodeList<TFlinkTableProperty>) arg2;
083            this.properties = props;
084        }
085    }
086
087    /**
088     * Gets the properties list.
089     * @return the list of key-value properties
090     */
091    public TPTNodeList<TFlinkTableProperty> getProperties() {
092        return properties;
093    }
094
095    /**
096     * Sets the properties list.
097     * @param properties the list of key-value properties
098     */
099    public void setProperties(TPTNodeList<TFlinkTableProperty> properties) {
100        this.properties = properties;
101    }
102
103    /**
104     * Gets a property value by key name.
105     * @param key the property key (without quotes)
106     * @return the property value (without quotes), or null if not found
107     */
108    public String getPropertyValue(String key) {
109        if (properties == null || key == null) return null;
110        for (int i = 0; i < properties.size(); i++) {
111            TFlinkTableProperty prop = properties.getElement(i);
112            if (key.equals(prop.getKeyString())) {
113                return prop.getValueString();
114            }
115        }
116        return null;
117    }
118
119    /**
120     * Gets the file path from the "path" property.
121     * @return the file path, or null if not specified
122     */
123    public String getPath() {
124        return getPropertyValue("path");
125    }
126
127    /**
128     * Gets the file format from the "format" property.
129     * @return the format (parquet, orc, csv, json, avro), or null if not specified
130     */
131    public String getFormat() {
132        return getPropertyValue("format");
133    }
134
135    /**
136     * Checks if this is a list-files-only operation.
137     * @return true if list_files_only is "true", false otherwise
138     */
139    public boolean isListFilesOnly() {
140        String value = getPropertyValue("list_files_only");
141        return "true".equalsIgnoreCase(value);
142    }
143
144    /**
145     * Checks if recursive listing is enabled.
146     * @return true if list_recursively is "true", false otherwise
147     */
148    public boolean isListRecursively() {
149        String value = getPropertyValue("list_recursively");
150        return "true".equalsIgnoreCase(value);
151    }
152
153    /**
154     * Gets the columns_from_path property value.
155     * @return the columns_from_path value, or null if not specified
156     */
157    public String getColumnsFromPath() {
158        return getPropertyValue("columns_from_path");
159    }
160
161    @Override
162    public void doParse(TCustomSqlStatement psql, ESqlClause plocation) {
163        this.dbvendor = psql.dbvendor;
164        // FILES() properties don't need special parsing - they are literal string values
165    }
166
167    @Override
168    public void accept(TParseTreeVisitor v) {
169        v.preVisit(this);
170        v.postVisit(this);
171    }
172
173    @Override
174    public void acceptChildren(TParseTreeVisitor v) {
175        v.preVisit(this);
176        if (functionName != null) {
177            functionName.acceptChildren(v);
178        }
179        if (properties != null) {
180            properties.acceptChildren(v);
181        }
182        v.postVisit(this);
183    }
184}