001package gudusoft.gsqlparser;
002
003import gudusoft.gsqlparser.compiler.TSymbol;
004import gudusoft.gsqlparser.nodes.TColumnDefinition;
005import gudusoft.gsqlparser.nodes.TObjectName;
006import gudusoft.gsqlparser.nodes.TResultColumn;
007import gudusoft.gsqlparser.nodes.TTable;
008import gudusoft.gsqlparser.sqlenv.TSQLColumn;
009
010import java.util.ArrayList;
011import java.util.Objects;
012
013/**
014 * TAttributeNode 类似 column,但含义更广泛。用来表示 SQL 语句中 table 包含的字段(table 或许用 relation 更准确)。
015 * 这里的 table 可以是数据库的基本表,也可以是 from clause 中出现的子查询,或者是 CTE。
016 *
017 * GSP 会把 SQL 语句中出现的每个 table 都生成对应的 TAttributeNode 列表,表示该 table 可以在 SQL 中被使用的字段。
018 * 例如:一般 table
019 * select column1 from table1
020 *
021 * GSP 会在  SQLEnv 中查找 table1 的定义,如果找到(column1,column2,column3), 则 table 会创建三个对应的 TAttributeNode。
022 * 用户可以通过 {@link TTable#getAttributes()} 来获取。
023 *
024 * 如果SQLEnv 中没有查找 table1 的定义,则创建一个 star column TAttributeNode, 形如 table1.*
025 *
026 * 例如:子查询
027 * select column1 from (select c1,c2 from table1) t1
028 * 针对上例中的 t1 子查询, GSP 会创建两个 TAttributeNode,对应 t1.c1, t1.c2
029 *
030 * 针对其他类型的 table, 也做类似处理。为 SQL 中的每个 table 准备好 TAttributeNode 列表,
031 * 在接下来的分析中,SQL 语句中出现的 column 引用都应该能找到对应的来源表和 TAttributeNode
032 *
033 * 相关的属性
034 * {@link TAttributeNode#getName()}, 字段名,不带前缀。
035 * {@link TAttributeNode#getTable_ref()}, 该字段对应的 table,每个在 SQL 语句中出现的 table 都有对应的 {@link TTable} 对象。
036 * {@link TAttributeNode#getSqlColumn()}, 如果在 SQLEnv 中找到该 table 的定义,这个方法返回数据库中定义的字段、
037 * {@link TAttributeNode#getSubLevelResultColumn()}, 如果 table 类型为子查询,该方法返回子查询 select list 中的 {@link TResultColumn} 对象,为该字段的数据来源。
038 *  或者是 values() 中的 result column。
039 *
040 */
041public class TAttributeNode extends TSymbol {
042
043    // USING (empno INTEGER,
044    //name VARCHAR(50),
045    //salary INTEGER)
046    //MERGE INTO employee AS t
047    //USING VALUES (:empno, :name, :salary) AS s(empno, name, salary)
048    //ON t.empno=s.empno
049    //WHEN MATCHED THEN UPDATE
050    //SET salary=s.salary
051    //WHEN NOT MATCHED THEN INSERT (empno, name, salary)
052    //VALUES (s.empno, s.name, s.salary);
053
054    // 如果 TAttributeNode 来自 VALUES (:empno, :name, :salary) AS s(empno, name, salary) 中的 s(empno, name, salary)
055    // 则 attributeCreatedFromAliasColumn = true,
056    // 当 target column 关联上该 TAttributeNode 后, target column 的 {@link TObjectName#setSourceColumn(TResultColumn)} 需要关联到
057    // 该 node 的 getSubLevelResultColumn() 上。
058
059//    public void setAttributeCreatedFromAliasColumn(boolean attributeCreatedFromAliasColumn) {
060//        this.attributeCreatedFromAliasColumn = attributeCreatedFromAliasColumn;
061//    }
062//
063//    public boolean isAttributeCreatedFromAliasColumn() {
064//        return attributeCreatedFromAliasColumn;
065//    }
066//
067//    private boolean attributeCreatedFromAliasColumn;
068
069
070    public static boolean addNodeToList(TAttributeNode node, ArrayList<TAttributeNode> list){
071        if ((node == null)||(list == null)) return false;
072
073        // 检查是否已存在同名节点
074        for (TAttributeNode existing : list) {
075            if (existing.getName().equals(node.getName())) {
076                return false;
077            }
078        }
079
080        list.add(node);
081        return true;
082    }
083
084    public static boolean addAllNodesToList(ArrayList<TAttributeNode> nodes, ArrayList<TAttributeNode> list){
085        if ((nodes == null)||(list == null)) return false;
086
087        boolean b = true;
088        for (TAttributeNode node : nodes) {
089            b = addNodeToList(node, list);
090        }
091        return b;
092    }
093
094    // 当一个 attribute node 从 subQuery 产生,然后这个 subQuery 又是 combined query 时,
095    // 这个 attribute node 会有一组同伴 attribute node, 他们是从 combined query 的 不同 subQuery 的 select list 中产生的
096    private ArrayList<TAttributeNode> accompaniedAttributeNodes = new ArrayList<>();
097
098    public ArrayList<TAttributeNode> getAccompaniedAttributeNodes() {
099        return accompaniedAttributeNodes;
100    }
101
102    private String name;
103
104    // https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type
105    // Typed struct syntax
106    // STRUCT<[field_name] field_type, ...>( expr1 [, ... ])
107    private TResultColumn structResultColumn;
108
109    public TResultColumn getStructResultColumn() {
110        return structResultColumn;
111    }
112
113    private TColumnDefinition columnDefinition; // this attribute is created from field definition struct<>
114    public TAttributeNode(String n, TTable table,TColumnDefinition columnDef, TResultColumn resultColumn){
115        this(n,table);
116        this.columnDefinition = columnDef;
117        this.structResultColumn = resultColumn;
118        // System.out.println("Attribute node hashcode"+this.hashCode()+"Add "+this.subLevelResultColumn.toString()+" for table: "+table_ref.toString()+" hashcode:"+ table_ref.hashCode());
119    }
120    public TColumnDefinition getColumnDefinition() {
121        return columnDefinition;
122    }
123
124    public TResultColumn getSubLevelResultColumn() {
125        return subLevelResultColumn;
126    }
127
128    public TSQLColumn getSqlColumn() {
129        return sqlColumn;
130    }
131
132    private TResultColumn subLevelResultColumn;
133    private TTable table_ref;// table reference in the script that derived this attribute
134    // SELECT col_1, col_2
135    //FROM (
136    //    select *
137    //    FROM table_a
138    //  ) a
139    private int index;// a.* 在 select * from table_a 的 result list 中的位置
140
141    /**
142     * 把关联上的 column 加入到 attributeNode 中来。
143     *
144     * 另外,如果这个 attributeNode 的源来自 sub-query 中的 select list 的 star column,
145     * 那么把这个 column 加到 star column 中来,完成自顶向下的数据传递,
146     * @param objectName
147     */
148    public void addAttributeRefToThisNode(TObjectName objectName){
149        attributesRefToThisNode.add(objectName);
150        objectName.setSourceAttributeNode(this);
151        if ((subLevelResultColumn != null) && (subLevelResultColumn.toString().endsWith("*"))){
152            if(!subLevelResultColumn.getAttributesFromUpLevelReferenceToStarColumn().contains(objectName)){
153                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
154                    TBaseType.log(String.format("Add reference %s to star column %s in attribute node: %s",objectName.toString(),subLevelResultColumn.toString(),this.getName()),TLog.DEBUG);
155                }
156                subLevelResultColumn.getAttributesFromUpLevelReferenceToStarColumn().add(objectName);
157            }
158        }
159    }
160    private ArrayList<TObjectName> attributesRefToThisNode = new ArrayList<>();
161
162//    public ArrayList<TObjectName> getAttributesRefToThisNode() {
163//        return attributesRefToThisNode;
164//    }
165
166    public int getIndex() {
167        return index;
168    }
169
170    public String getName() {
171        return name;
172    }
173
174    public String getLastPartOfName() {
175
176        String last_part = name;
177        for(int i=name.length()-1;i>=0;i--){
178            if (name.charAt(i) == '.'){
179                last_part = name.substring(i+1,name.length());
180                break;
181            }
182        }
183        return last_part;
184    }
185    public TTable getTable_ref() {
186        return table_ref;
187    }
188
189    public TAttributeNode(String n, TTable table){
190        this.name = n;
191        this.table_ref = table;
192    }
193
194    private TSQLColumn sqlColumn; // 如果这个attribute 来自 DDL 或数据库中,那么关联上 column definition
195    public TAttributeNode(String n, TTable table,TSQLColumn columnDef){
196        this(n,table);
197        this.sqlColumn = columnDef;
198        // System.out.println("Attribute node hashcode"+this.hashCode()+"Add "+this.subLevelResultColumn.toString()+" for table: "+table_ref.toString()+" hashcode:"+ table_ref.hashCode());
199    }
200
201
202    public TAttributeNode(String n, TTable table,TResultColumn attribute){
203        this(n,table);
204        this.subLevelResultColumn = attribute;
205       // System.out.println("Attribute node hashcode"+this.hashCode()+"Add "+this.subLevelResultColumn.toString()+" for table: "+table_ref.toString()+" hashcode:"+ table_ref.hashCode());
206    }
207
208    public TAttributeNode(String n, TTable table,TSQLColumn columnDef,TResultColumn attribute){
209        this(n,table,columnDef);
210        this.subLevelResultColumn = attribute;
211        // System.out.println("Attribute node hashcode"+this.hashCode()+"Add "+this.subLevelResultColumn.toString()+" for table: "+table_ref.toString()+" hashcode:"+ table_ref.hashCode());
212    }
213
214    public TAttributeNode(String n, TTable table,TResultColumn attribute,int index){
215        this(n,table,attribute);
216        //this.subLevelResultColumn = attribute;
217        this.index = index;
218    }
219
220    public TAttributeNode(String n, TTable table,TSQLColumn columnDef,TResultColumn attribute,int index){
221        this(n,table,columnDef, attribute);
222        //this.subLevelResultColumn = attribute;
223        this.index = index;
224    }
225
226    @Override
227    public String toString(){
228        return name;//+"( table ref: "+table_ref.toString()+")";
229    }
230
231    @Override
232    public boolean equals(Object o) {
233        if (this == o) return true;
234        if (o == null || getClass() != o.getClass()) return false;
235        TAttributeNode that = (TAttributeNode) o;
236        return Objects.equals(getName(), that.getName());
237    }
238
239    @Override
240    public int hashCode() {
241        return Objects.hash(getName());
242    }
243
244}