001
002package gudusoft.gsqlparser.dlineage.dataflow.model;
003
004import gudusoft.gsqlparser.EDbVendor;
005import gudusoft.gsqlparser.TCustomSqlStatement;
006import gudusoft.gsqlparser.TSourceToken;
007import gudusoft.gsqlparser.dlineage.util.DlineageUtil;
008import gudusoft.gsqlparser.dlineage.util.Pair3;
009import gudusoft.gsqlparser.nodes.TParseTreeNode;
010import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
011import gudusoft.gsqlparser.sqlenv.TSQLEnv;
012import gudusoft.gsqlparser.util.Logger;
013import gudusoft.gsqlparser.util.LoggerFactory;
014import gudusoft.gsqlparser.util.SQLUtil;
015
016import java.io.ByteArrayInputStream;
017import java.io.IOException;
018import java.util.*;
019import java.util.concurrent.atomic.AtomicInteger;
020
021public class ResultSet {
022    private static final Logger logger = LoggerFactory.getLogger(ResultSet.class);
023    private long id;
024    protected String server;
025    protected String schema;
026    protected String database;
027    private Pair3<Long, Long, String> startPosition;
028    private Pair3<Long, Long, String> endPosition;
029    private List<ResultColumn> columns = new ArrayList<ResultColumn>();
030
031    private TParseTreeNode gspObject;
032    private boolean isTarget;
033    private boolean isDetermined;
034
035    private ResultSetRelationRows relationRows = new ResultSetRelationRows(this);
036
037    private Map<String, AtomicInteger> aliasMap = new HashMap<>();
038    
039    private String procedureId;
040    
041    public ResultSet(TParseTreeNode gspObject, boolean isTarget) {
042        if (gspObject == null) {
043            throw new IllegalArgumentException("ResultSet arguments can't be null.");
044        }
045
046        id = ++ModelBindingManager.get().TABLE_COLUMN_ID;
047
048        this.gspObject = gspObject;
049        this.isTarget = isTarget;
050
051        TSourceToken startToken = gspObject.getStartToken();
052        TSourceToken endToken = gspObject.getEndToken();
053        if (startToken != null) {
054            this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo,
055                    startToken.columnNo, ModelBindingManager.getGlobalHash());
056        }
057        
058        if (endToken != null) {
059            this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo,
060                    endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash());
061        }
062        
063        this.schema = ModelBindingManager.getGlobalSchema();
064        this.database = ModelBindingManager.getGlobalDatabase();
065
066        EDbVendor vendor = ModelBindingManager.getGlobalOption().getVendor();
067        boolean supportCatalog = TSQLEnv.supportCatalog(vendor);
068        boolean supportSchema = TSQLEnv.supportSchema(vendor);
069        
070        fillSchemaInfo();
071        
072        if (!supportCatalog) {
073            this.database = null;
074        } else if (this.database == null && !TSQLEnv.DEFAULT_DB_NAME.equals(getDefaultDatabase())) {
075            this.database = getDefaultDatabase();
076        }
077
078        if (!supportSchema) {
079            this.schema = null;
080        } else if (this.schema == null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equals(getDefaultSchema())) {
081            this.schema = getDefaultSchema();
082        }
083
084        if (this.server == null && !TSQLEnv.DEFAULT_SERVER_NAME.equals(getDefaultServer())) {
085            this.server = getDefaultServer();
086        }
087    }
088
089    private void fillSchemaInfo() {
090                TCustomSqlStatement stmt = DlineageUtil.getTopStmt(ModelBindingManager.getGlobalStmtStack().peek());
091                String sqlComment = null;
092                try {
093                        sqlComment = stmt.getCommentBeforeNode();
094                } catch (Exception e) {
095                }
096                if (!SQLUtil.isEmpty(sqlComment) && (sqlComment.indexOf("db") != -1 || sqlComment.indexOf("schema") != -1)) {
097                        Properties properties = new Properties();
098                        try {
099                                properties.load(
100                                                new ByteArrayInputStream(sqlComment.replace("--", "").trim().replace(",", "\n").getBytes()));
101                                if (SQLUtil.isEmpty(this.server) && properties.containsKey("db-instance")) {
102                                        this.server = properties.getProperty("db-instance");
103                                }
104                                if (SQLUtil.isEmpty(this.database) && properties.containsKey("db")) {
105                                        this.database = properties.getProperty("db");
106                                        if(this.database.indexOf(".")!=-1) {
107                                                this.database = SQLUtil.quoteDottedName(ModelBindingManager.getGlobalOption().getVendor(), ESQLDataObjectType.dotCatalog, this.database);
108                                        }
109                                }
110                                if (SQLUtil.isEmpty(this.schema) && properties.containsKey("schema")) {
111                                        this.schema = properties.getProperty("schema");
112                                        if(this.schema.indexOf(".")!=-1) {
113                                                this.schema = SQLUtil.quoteDottedName(ModelBindingManager.getGlobalOption().getVendor(), ESQLDataObjectType.dotSchema, this.schema);
114                                        }
115                                }
116                        } catch (IOException e) {
117                logger.error("load sql comment properties failed.", e);
118                        }
119                }
120        }
121    
122    protected String getDefaultServer() {
123        String defaultServer = null;
124        if (ModelBindingManager.getGlobalSQLEnv() != null) {
125            defaultServer = ModelBindingManager.getGlobalSQLEnv().getDefaultServerName();
126        }
127        if (!SQLUtil.isEmpty(defaultServer))
128            return defaultServer;
129        return TSQLEnv.DEFAULT_SERVER_NAME;
130    }
131
132    protected String getDefaultSchema() {
133        return ModelBindingManager.getEffectiveDefaultSchema();
134    }
135
136    protected String getDefaultDatabase() {
137        return ModelBindingManager.getEffectiveDefaultDatabase();
138    }
139
140    public Pair3<Long, Long, String> getStartPosition() {
141        return startPosition;
142    }
143
144    public Pair3<Long, Long, String> getEndPosition() {
145        return endPosition;
146    }
147
148    public List<ResultColumn> getColumns() {
149        return columns;
150    }
151
152    public void addColumn(ResultColumn column) {
153        if (column != null && !columns.contains(column)) {
154            this.columns.add(column);
155        }
156    }
157
158    /**
159     * STRUCT tree: the field nodes below this result set's columns ({@code customer.address.city} below
160     * {@code customer}), made for a field read through a CTE or derived table. They are not columns of their own, so
161     * they are kept out of {@link #getColumns()}, which star expansion and positional writes walk; the output lists
162     * them after their columns ({@link #getColumnsWithFieldPaths()}), as a table lists its field path nodes
163     * (Mantis 4836).
164     */
165    private final List<ResultColumn> fieldPathColumns = new ArrayList<ResultColumn>();
166    private final java.util.Map<ResultColumn, ResultColumn> fieldPathBases =
167            new java.util.IdentityHashMap<ResultColumn, ResultColumn>();
168
169    /** The field nodes by their structural identity ({@link FieldNodeKey}: base column and path), as made. */
170    private final java.util.Map<FieldNodeKey, ResultColumn> fieldPathIndex =
171            new java.util.LinkedHashMap<FieldNodeKey, ResultColumn>();
172
173    /** Makes {@code node}, a field node below {@code base}, one of the field path columns rather than a column. */
174    public void addFieldPathColumn(ResultColumn node, ResultColumn base) {
175        addFieldPathColumn(node, base, null);
176    }
177
178    /**
179     * Makes {@code node}, the field node at {@code segments} below {@code base}, one of the field path columns rather
180     * than a column, and indexes it by its identity when the path is given.
181     */
182    public void addFieldPathColumn(ResultColumn node, ResultColumn base,
183            java.util.List<gudusoft.gsqlparser.common.structured.StructuredPathSegment> segments) {
184        columns.remove(node);
185        if (!fieldPathColumns.contains(node)) {
186            fieldPathColumns.add(node);
187            fieldPathBases.put(node, base);
188            if (segments != null) {
189                FieldNodeKey key = FieldNodeKey.of(base, segments);
190                if (!fieldPathIndex.containsKey(key)) {
191                    fieldPathIndex.put(key, node);
192                }
193            }
194        }
195    }
196
197    /** The field node with identity {@code key}, or null. */
198    public ResultColumn getFieldPathNode(FieldNodeKey key) {
199        return fieldPathIndex.get(key);
200    }
201
202    /** The columns as the output lists them: each column followed by the field nodes below it. */
203    public List<ResultColumn> getColumnsWithFieldPaths() {
204        if (fieldPathColumns.isEmpty()) {
205            return columns;
206        }
207        List<ResultColumn> published = new ArrayList<ResultColumn>(columns.size() + fieldPathColumns.size());
208        for (ResultColumn column : columns) {
209            published.add(column);
210            for (ResultColumn node : fieldPathColumns) {
211                if (fieldPathBases.get(node) == column) {
212                    published.add(node);
213                }
214            }
215        }
216        for (ResultColumn node : fieldPathColumns) {
217            if (!published.contains(node)) {
218                published.add(node);
219            }
220        }
221        return published;
222    }
223
224    public TParseTreeNode getGspObject() {
225        return gspObject;
226    }
227
228    public long getId() {
229        return id;
230    }
231
232    public boolean isTarget() {
233        return isTarget;
234    }
235
236        public String getSchema() {
237                return schema;
238        }
239
240        public String getDatabase() {
241                return database;
242        }
243        
244        public ResultSetRelationRows getRelationRows(){
245                return relationRows;
246        }
247
248    public String getServer() {
249        return server;
250    }
251
252        public boolean isDetermined() {
253                return isDetermined;
254        }
255
256        public void setDetermined(boolean isDetermined) {
257                this.isDetermined = isDetermined;
258        }
259
260        public void setTarget(boolean isTarget) {
261                this.isTarget = isTarget;
262        }
263
264    Map<String, AtomicInteger> getAliasMap() {
265        return aliasMap;
266    }
267
268    public String getProcedureId() {
269        return procedureId;
270    }
271
272    public void setProcedureId(String procedureId) {
273        this.procedureId = procedureId;
274    }
275}