001package gudusoft.gsqlparser.resolver;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.compiler.TContext;
005import gudusoft.gsqlparser.compiler.TScope;
006import gudusoft.gsqlparser.nodes.*;
007import gudusoft.gsqlparser.sqlenv.TSQLEnv;
008import gudusoft.gsqlparser.sqlenv.TSQLFunction;
009import gudusoft.gsqlparser.sqlenv.TSQLTable;
010import gudusoft.gsqlparser.stmt.TCreateFunctionStmt;
011import gudusoft.gsqlparser.stmt.TCreateTableSqlStatement;
012import gudusoft.gsqlparser.stmt.TSelectSqlStatement;
013
014import java.util.Stack;
015
016/**
017 * TMetadataCollector is responsible for collecting metadata information from SQL statements,
018 * particularly focusing on table and function definitions. It traverses the parse tree
019 * and builds up metadata in the global scope.
020 */
021public class TMetadataCollector extends TParseTreeVisitor {
022    private TContext globalScope;        // Stores global context including SQL environment
023    private TStatementList sqlStatements; // List of SQL statements to analyze
024    private Stack<TScope> scopeStack = new Stack<>();  // Maintains scope hierarchy
025    
026    /**
027     * Constructor for TMetadataCollector.
028     * Initializes the collector with a list of SQL statements and a global context.
029     *
030     * @param sqls List of SQL statements to analyze
031     * @param context Global context that will store collected metadata
032     */
033    public TMetadataCollector(TStatementList sqls, TContext context) {
034        this.globalScope = context;
035        sqlStatements = sqls;
036        scopeStack.push(globalScope);
037    }
038
039    /**
040     * Main method to start metadata collection process.
041     * Iterates through all SQL statements and collects metadata from each one.
042     * This includes:
043     * - Table definitions from CREATE TABLE statements
044     * - Function definitions from CREATE FUNCTION statements
045     * - Other metadata from various SQL statements
046     */
047    public void collect() {
048        for (int i = 0; i < sqlStatements.size(); i++) {
049            sqlStatements.get(i).acceptChildren(this);
050        }
051    }
052
053    /**
054     * Processes CREATE TABLE statements to collect table metadata.
055     * Handles both regular table creation and CREATE TABLE AS SELECT.
056     *
057     * @param stmt The CREATE TABLE statement to process
058     *
059     * Actions performed:
060     * 1. Creates new table entry in SQL environment
061     * 2. Sets table name and resolves it
062     * 3. Processes column definitions
063     * 4. Handles subquery-based table creation
064     */
065    public void preVisit(TCreateTableSqlStatement stmt) {
066        TSQLTable sqlTable = null;
067        TTable createTable = stmt.getTargetTable();
068        if (globalScope.getSqlEnv() == null) return;
069
070        // Create new table entry
071        if (stmt.getTargetTable() != null) {
072            sqlTable = globalScope.getSqlEnv().addTable(createTable.getFullName(), true);
073        }
074        if (sqlTable == null) return;
075        createTable.setResolvedTable(sqlTable);
076
077        // Set SQL environment for table name
078        createTable.getTableName().setSqlEnv(globalScope.getSqlEnv());
079
080        // Process columns
081        if (stmt.getColumnList().size() != 0) {
082            // Handle explicit column definitions
083            for(TColumnDefinition cd : stmt.getColumnList()) {
084                sqlTable.addColumn(cd.getColumnName().toString(), cd.getDatatype());
085            }
086        } else if (stmt.getSubQuery() != null) {
087            // Handle CREATE TABLE AS SELECT
088            TSelectSqlStatement viewQuery = stmt.getSubQuery();
089            if (viewQuery.isCombinedQuery()) {
090                viewQuery = viewQuery.getRightStmt();
091            }
092
093            TResultColumnList resultColumnList = stmt.getSubQuery().getResultColumnList();
094            if (resultColumnList != null) {
095                for (TResultColumn resultColumn : resultColumnList) {
096                    if (resultColumn.getAliasNameList().size() > 0){
097                        // select t.a1, t.a2, stack(2, 'T', t.a1, t.a2, t.a3/t.a4, t.a5/t.a6,   'T+0', t.a7, t.a8, t.a9/t.a10, t.a11/t.a12) as (b1, b2, b3, b4, b5)  from db1.table1 t
098                        for(int j = 0; j < resultColumn.getAliasNameList().size(); j++){
099                            TObjectName column = resultColumn.getAliasNameList().get(j);
100                            sqlTable.addColumn(TSQLEnv.getObjectName( TBaseType.removeQuoteChar(column.toString())));
101                            createTable.getLinkedColumns().addObjectName(column); // for getTableColumn work correctly
102                        }
103
104                    }else{
105                        sqlTable.addColumn(TSQLEnv.getObjectName(resultColumn.getDisplayName()));
106                    }
107
108                }
109            }
110        }
111    }
112
113    /**
114     * Processes CREATE FUNCTION statements to collect function metadata.
115     * Captures function name, parameters, and return type information.
116     *
117     * @param stmt The CREATE FUNCTION statement to process
118     *
119     * Actions performed:
120     * 1. Creates new function entry in SQL environment
121     * 2. Processes function parameters
122     * 3. Sets function return type
123     */
124    public void preVisit(TCreateFunctionStmt stmt) {
125        if (globalScope.getSqlEnv() == null) return;
126        
127        // Create new function entry
128        TSQLFunction sqlFunction = globalScope.getSqlEnv().addFunction(stmt.getFunctionName().toString(), true);
129        if (sqlFunction == null) return;
130        if (stmt.getParameterDeclarations() == null) return;
131
132        // Process function parameters
133        int c = 0;
134        for(TParameterDeclaration p : stmt.getParameterDeclarations()) {
135            if (p.getParameterName() != null) {
136                sqlFunction.addParameter(c, p.getDataType(), p.getParameterName().toString());
137            } else {
138                sqlFunction.addParameter(c, p.getDataType());
139            }
140        }
141
142        // Set function return type
143        sqlFunction.setReturnType(stmt.getReturnDataType());
144    }
145}