• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

starlake-ai / jsqltranspiler / #5

23 Jul 2024 08:43AM UTC coverage: 69.181% (+0.3%) from 68.891%
#5

push

github

manticore-projects
fix: aliased expressions in sub-query

- fixes #18

Signed-off-by: Andreas Reichel <andreas@manticore-projects.com>

43 of 47 new or added lines in 3 files covered. (91.49%)

2 existing lines in 1 file now uncovered.

3082 of 4455 relevant lines covered (69.18%)

0.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.91
/src/main/java/ai/starlake/transpiler/JSQLColumResolver.java
1
/**
2
 * Starlake.AI JSQLTranspiler is a SQL to DuckDB Transpiler.
3
 * Copyright (C) 2024 Starlake.AI
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
package ai.starlake.transpiler;
18

19
import ai.starlake.transpiler.schema.JdbcColumn;
20
import ai.starlake.transpiler.schema.JdbcMetaData;
21
import ai.starlake.transpiler.schema.JdbcResultSetMetaData;
22
import ai.starlake.transpiler.schema.JdbcTable;
23
import ai.starlake.transpiler.schema.treebuilder.TreeBuilder;
24
import net.sf.jsqlparser.JSQLParserException;
25
import net.sf.jsqlparser.expression.Alias;
26
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
27
import net.sf.jsqlparser.schema.Column;
28
import net.sf.jsqlparser.schema.Table;
29
import net.sf.jsqlparser.statement.Statement;
30
import net.sf.jsqlparser.statement.select.AllColumns;
31
import net.sf.jsqlparser.statement.select.AllTableColumns;
32
import net.sf.jsqlparser.statement.select.FromItem;
33
import net.sf.jsqlparser.statement.select.FromItemVisitor;
34
import net.sf.jsqlparser.statement.select.Join;
35
import net.sf.jsqlparser.statement.select.LateralSubSelect;
36
import net.sf.jsqlparser.statement.select.ParenthesedFromItem;
37
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
38
import net.sf.jsqlparser.statement.select.PlainSelect;
39
import net.sf.jsqlparser.statement.select.Select;
40
import net.sf.jsqlparser.statement.select.SelectItem;
41
import net.sf.jsqlparser.statement.select.SelectVisitor;
42
import net.sf.jsqlparser.statement.select.SetOperationList;
43
import net.sf.jsqlparser.statement.select.TableFunction;
44
import net.sf.jsqlparser.statement.select.TableStatement;
45
import net.sf.jsqlparser.statement.select.Values;
46
import net.sf.jsqlparser.statement.select.WithItem;
47
import net.sf.jsqlparser.util.deparser.StatementDeParser;
48

49
import java.lang.reflect.InvocationTargetException;
50
import java.sql.Connection;
51
import java.sql.SQLException;
52
import java.util.ArrayList;
53
import java.util.List;
54
import java.util.logging.Logger;
55

56
/**
57
 * A class for resolving the actual columns returned by a SELECT statement. Depends on virtual or
58
 * physical Database Metadata holding the schema and table information.
59
 */
60
@SuppressWarnings({"PMD.CyclomaticComplexity"})
61
public class JSQLColumResolver
62
    implements SelectVisitor<JdbcResultSetMetaData>, FromItemVisitor<JdbcResultSetMetaData> {
63
  public final static Logger LOGGER = Logger.getLogger(JSQLColumResolver.class.getName());
1✔
64

65

66
  private final JdbcMetaData metaData;
67
  private final JSQLExpressionColumnResolver expressionColumnResolver;
68

69

70
  /**
71
   * Instantiates a new JSQLColumnResolver for the provided Database Meta Data
72
   *
73
   * @param metaData the meta data
74
   */
75
  public JSQLColumResolver(JdbcMetaData metaData) {
1✔
76
    this.metaData = metaData;
1✔
77
    this.expressionColumnResolver = new JSQLExpressionColumnResolver(this);
1✔
78
  }
1✔
79

80

81
  /**
82
   * Instantiates a new JSQLColumnResolver for the provided simplified Meta Data, presented as an
83
   * Array of Tables and Column Names only.
84
   *
85
   * @param currentCatalogName the current catalog name
86
   * @param currentSchemaName the current schema name
87
   * @param metaDataDefinition the meta data definition as n Array of Tablename and Column Names
88
   */
89
  public JSQLColumResolver(String currentCatalogName, String currentSchemaName,
90
      String[][] metaDataDefinition) {
91
    this(new JdbcMetaData(currentCatalogName, currentSchemaName, metaDataDefinition));
1✔
92
  }
1✔
93

94
  /**
95
   * Instantiates a new JSQLColumnResolver for the provided simplified Meta Data with an empty
96
   * CURRENT_SCHEMA and CURRENT_CATALOG
97
   *
98
   * @param metaDataDefinition the meta data definition as n Array of Tablename and Column Names
99
   */
100
  public JSQLColumResolver(String[][] metaDataDefinition) {
101
    this("", "", metaDataDefinition);
1✔
102
  }
1✔
103

104

105
  /**
106
   * Resolves the actual columns returned by a SELECT statement for a given CURRENT_CATALOG and
107
   * CURRENT_SCHEMA and wraps this information into `ResultSetMetaData`.
108
   *
109
   * @param sqlStr the `SELECT` statement text
110
   * @param metaData the Database Meta Data
111
   * @return the ResultSetMetaData representing the actual columns returned by the `SELECT`
112
   *         statement
113
   * @throws JSQLParserException when the `SELECT` statement text can not be parsed
114
   */
115
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
116
  public static JdbcResultSetMetaData getResultSetMetaData(String sqlStr, JdbcMetaData metaData)
117
      throws JSQLParserException {
118

119
    if (sqlStr == null || sqlStr.trim().isEmpty()) {
1✔
120
      throw new JSQLParserException("The provided Statement must not be empty.");
×
121
    }
122

123
    JSQLColumResolver resolver = new JSQLColumResolver(metaData);
1✔
124

125
    Statement st = CCJSqlParserUtil.parse(sqlStr);
1✔
126
    if (st instanceof Select) {
1✔
127
      PlainSelect select = (PlainSelect) st;
1✔
128
      return select.accept(resolver, JdbcMetaData.copyOf(metaData));
1✔
129

130
    } else {
131
      throw new RuntimeException(
×
132
          st.getClass().getSimpleName() + " Statements are not supported yet.");
×
133
    }
134
  }
135

136
  /**
137
   * Resolves the actual columns returned by a SELECT statement for a given CURRENT_CATALOG and
138
   * CURRENT_SCHEMA and wraps this information into `ResultSetMetaData`.
139
   *
140
   * @param sqlStr the `SELECT` statement text
141
   * @param metaDataDefinition the meta data definition as an array of Tables with Columns e.g. {
142
   *        TABLE_NAME, COLUMN1, COLUMN2 ... COLUMN10 }
143
   * @param currentCatalogName the CURRENT_CATALOG name (which is the default catalog for accessing
144
   *        the schemas)
145
   * @param currentSchemaName the CURRENT_SCHEMA name (which is the default schema for accessing the
146
   *        tables)
147
   * @return the ResultSetMetaData representing the actual columns returned by the `SELECT`
148
   *         statement
149
   * @throws JSQLParserException when the `SELECT` statement text can not be parsed
150
   */
151
  public static JdbcResultSetMetaData getResultSetMetaData(String sqlStr,
152
      String[][] metaDataDefinition, String currentCatalogName, String currentSchemaName)
153
      throws JSQLParserException {
154
    JdbcMetaData metaData =
×
155
        new JdbcMetaData(currentCatalogName, currentSchemaName, metaDataDefinition);
156
    return getResultSetMetaData(sqlStr, metaData);
×
157
  }
158

159
  /**
160
   * Resolves the actual columns returned by a SELECT statement for an empty CURRENT_CATALOG and an
161
   * empty CURRENT_SCHEMA and wraps this information into `ResultSetMetaData`.
162
   *
163
   * @param sqlStr the `SELECT` statement text
164
   * @param metaDataDefinition the meta data definition as an array of Tables with Columns e.g. {
165
   *        TABLE_NAME, COLUMN1, COLUMN2 ... COLUMN10 }
166
   * @return the ResultSetMetaData representing the actual columns returned by the `SELECT`
167
   *         statement
168
   * @throws JSQLParserException when the `SELECT` statement text can not be parsed
169
   */
170
  public JdbcResultSetMetaData getResultSetMetaData(String sqlStr, String[][] metaDataDefinition)
171
      throws JSQLParserException {
172
    JdbcMetaData metaData = new JdbcMetaData("", "", metaDataDefinition);
×
173
    return getResultSetMetaData(sqlStr, metaData);
×
174
  }
175

176
  /**
177
   * Resolves the actual columns returned by a SELECT statement for an empty CURRENT_CATALOG and an
178
   * empty CURRENT_SCHEMA and wraps this information into `ResultSetMetaData`.
179
   *
180
   * @param sqlStr the `SELECT` statement text
181
   * @return the ResultSetMetaData representing the actual columns returned by the `SELECT`
182
   *         statement
183
   * @throws JSQLParserException when the `SELECT` statement text can not be parsed
184
   */
185
  public JdbcResultSetMetaData getResultSetMetaData(String sqlStr) throws JSQLParserException {
186

187
    Statement st = CCJSqlParserUtil.parse(sqlStr);
1✔
188
    if (st instanceof Select) {
1✔
189
      Select select = (Select) st;
1✔
190
      return select.accept(this, JdbcMetaData.copyOf(metaData));
1✔
191
    } else {
192
      throw new RuntimeException("Unsupported Statement");
×
193
    }
194
  }
195

196

197
  /**
198
   * Gets the rewritten statement text with any AllColumns "*" or AllTableColumns "t.*" expression
199
   * resolved into the actual columns
200
   *
201
   * @param sqlStr the query statement string (using any AllColumns "*" or AllTableColumns "t.*"
202
   *        expression)
203
   * @return rewritten statement text with any AllColumns "*" or AllTableColumns "t.*" expression
204
   *         resolved into the actual columns
205
   * @throws JSQLParserException the exception when parsing the query statement fails
206
   */
207
  public String getResolvedStatementText(String sqlStr) throws JSQLParserException {
208
    StringBuilder builder = new StringBuilder();
1✔
209
    StatementDeParser deParser = new StatementDeParser(builder);
1✔
210

211
    Statement st = CCJSqlParserUtil.parse(sqlStr);
1✔
212
    if (st instanceof Select) {
1✔
213
      Select select = (Select) st;
1✔
214
      select.accept(this, JdbcMetaData.copyOf(metaData));
1✔
215
    }
216
    st.accept(deParser);
1✔
217
    return builder.toString();
1✔
218
  }
219

220
  public static <T> T getLineage(Class<? extends TreeBuilder<T>> treeBuilderClass, String sqlStr,
221
      Connection connection) throws JSQLParserException, NoSuchMethodException,
222
      InvocationTargetException, InstantiationException, IllegalAccessException, SQLException {
223

224
    JdbcMetaData metaData = new JdbcMetaData(connection);
×
225
    JSQLColumResolver resolver = new JSQLColumResolver(metaData);
×
226
    JdbcResultSetMetaData resultSetMetaData = resolver.getResultSetMetaData(sqlStr);
×
227
    TreeBuilder<T> builder =
×
228
        treeBuilderClass.getConstructor(JdbcResultSetMetaData.class).newInstance(resultSetMetaData);
×
229
    return builder.getConvertedTree(resolver);
×
230
  }
231

232
  public static <T> T getLineage(Class<? extends TreeBuilder<T>> treeBuilderClass, String sqlStr,
233
      String[][] metaDataDefinition, String currentCatalogName, String currentSchemaName)
234
      throws JSQLParserException, NoSuchMethodException, InvocationTargetException,
235
      InstantiationException, IllegalAccessException, SQLException {
236

237
    JdbcMetaData metaData =
×
238
        new JdbcMetaData(currentCatalogName, currentSchemaName, metaDataDefinition);
239
    JSQLColumResolver resolver = new JSQLColumResolver(metaData);
×
240

241
    JdbcResultSetMetaData resultSetMetaData = resolver.getResultSetMetaData(sqlStr);
×
242
    TreeBuilder<T> builder =
×
243
        treeBuilderClass.getConstructor(JdbcResultSetMetaData.class).newInstance(resultSetMetaData);
×
244
    return builder.getConvertedTree(resolver);
×
245
  }
246

247
  // for visiting Column Sub-Selects
248
  public <T> T getLineage(Class<? extends TreeBuilder<T>> treeBuilderClass, Select select)
249
      throws NoSuchMethodException, InvocationTargetException, InstantiationException,
250
      IllegalAccessException, SQLException {
251
    JdbcResultSetMetaData resultSetMetaData = select.accept(this, JdbcMetaData.copyOf(metaData));
1✔
252
    TreeBuilder<T> builder =
1✔
253
        treeBuilderClass.getConstructor(JdbcResultSetMetaData.class).newInstance(resultSetMetaData);
1✔
254
    return builder.getConvertedTree(this);
1✔
255
  }
256

257

258
  public <T> T getLineage(Class<? extends TreeBuilder<T>> treeBuilderClass, String sqlStr)
259
      throws NoSuchMethodException, InvocationTargetException, InstantiationException,
260
      IllegalAccessException, SQLException, JSQLParserException {
261
    JdbcResultSetMetaData resultSetMetaData = getResultSetMetaData(sqlStr);
1✔
262
    TreeBuilder<T> builder =
1✔
263
        treeBuilderClass.getConstructor(JdbcResultSetMetaData.class).newInstance(resultSetMetaData);
1✔
264
    return builder.getConvertedTree(this);
1✔
265
  }
266

267
  public static String getQualifiedTableName(String catalogName, String schemaName,
268
      String tableName) {
269
    StringBuilder builder = new StringBuilder();
1✔
270
    if (catalogName != null && !catalogName.isEmpty()) {
1✔
271
      builder.append(catalogName).append(".");
×
NEW
272
      builder.append(schemaName != null ? schemaName : "").append(".");
×
273
    } else if (schemaName != null && !schemaName.isEmpty()) {
1✔
UNCOV
274
      builder.append(schemaName).append(".");
×
275
    }
276
    builder.append(tableName);
1✔
277
    return builder.toString();
1✔
278
  }
279

280
  public static String getQualifiedColumnName(String catalogName, String schemaName,
281
      String tableName, String columName) {
282
    StringBuilder builder = new StringBuilder();
1✔
283
    if (tableName == null || tableName.isEmpty()) {
1✔
284
      return columName;
×
285
    } else if (catalogName != null && !catalogName.isEmpty()) {
1✔
286
      builder.append(catalogName).append(".");
×
NEW
287
      builder.append(schemaName != null ? schemaName : "").append(".");
×
288
    } else if (schemaName != null && !schemaName.isEmpty()) {
1✔
UNCOV
289
      builder.append(schemaName).append(".");
×
290
    }
291
    builder.append(tableName).append(".").append(columName);
1✔
292

293
    return builder.toString();
1✔
294
  }
295

296
  @Override
297
  public <S> JdbcResultSetMetaData visit(Table table, S context) {
298
    JdbcResultSetMetaData rsMetaData = new JdbcResultSetMetaData();
1✔
299

300
    if (table.getSchemaName() == null || table.getSchemaName().isEmpty()) {
1✔
301
      table.setSchemaName(metaData.getCurrentSchemaName());
1✔
302
    }
303

304
    if (table.getDatabase() == null) {
1✔
305
      table.setDatabaseName(metaData.getCurrentCatalogName());
×
306
    } else if (table.getDatabase().getDatabaseName() == null
1✔
307
        || table.getDatabase().getDatabaseName().isEmpty()) {
×
308
      table.getDatabase().setDatabaseName(metaData.getCurrentCatalogName());
1✔
309
    }
310

311
    for (JdbcColumn jdbcColumn : metaData.getTableColumns(table.getDatabase().getDatabaseName(),
1✔
312
        table.getSchemaName(), table.getName(), null)) {
1✔
313

314
      rsMetaData.add(jdbcColumn, null);
1✔
315
    }
1✔
316

317
    return rsMetaData;
1✔
318
  }
319

320
  public JdbcResultSetMetaData visit(ParenthesedSelect parenthesedSelect, JdbcMetaData context) {
321
    JdbcResultSetMetaData rsMetaData = null;
1✔
322
    Alias alias = parenthesedSelect.getAlias();
1✔
323
    JdbcTable t = new JdbcTable(metaData.getCurrentCatalogName(), metaData.getCurrentSchemaName(),
1✔
324
        alias != null ? parenthesedSelect.getAlias().getName() : "");
1✔
325

326
    rsMetaData = parenthesedSelect.getSelect().accept((SelectVisitor<JdbcResultSetMetaData>) this,
1✔
327
        JdbcMetaData.copyOf(context));
1✔
328
    try {
329
      int columnCount = rsMetaData.getColumnCount();
1✔
330
      for (int i = 1; i <= columnCount; i++) {
1✔
331
        t.add(t.tableCatalog, t.tableSchema, t.tableName,
1✔
332
            rsMetaData.getColumnLabel(i) != null && !rsMetaData.getColumnLabel(i).isEmpty()
1✔
333
                ? rsMetaData.getColumnLabel(i)
1✔
334
                : rsMetaData.getColumnName(i),
1✔
335
            rsMetaData.getColumnType(i), rsMetaData.getColumnClassName(i),
1✔
336
            rsMetaData.getPrecision(i), rsMetaData.getScale(i), 10, rsMetaData.isNullable(i), "",
1✔
337
            "", rsMetaData.getColumnDisplaySize(i), i, "", rsMetaData.getCatalogName(i),
1✔
338
            rsMetaData.getSchemaName(i), rsMetaData.getTableName(i), null, "", "");
1✔
339
      }
340
      metaData.put(t);
1✔
341
    } catch (SQLException ex) {
×
342
      throw new RuntimeException("Error in WITH clause " + parenthesedSelect.toString(), ex);
×
343
    }
1✔
344
    return rsMetaData;
1✔
345
  }
346

347
  @Override
348
  public <S> JdbcResultSetMetaData visit(ParenthesedSelect parenthesedSelect, S context) {
349
    if (context instanceof JdbcMetaData) {
1✔
350
      JdbcMetaData metaData1 = (JdbcMetaData) context;
1✔
351
      return visit(parenthesedSelect, JdbcMetaData.copyOf(metaData1));
1✔
352
    }
353
    return null;
×
354
  }
355

356
  @Override
357
  public void visit(ParenthesedSelect parenthesedSelect) {
358
    SelectVisitor.super.visit(parenthesedSelect);
×
359
  }
×
360

361

362
  @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength"})
363
  public JdbcResultSetMetaData visit(PlainSelect select, JdbcMetaData metaData) {
364
    JdbcResultSetMetaData resultSetMetaData = new JdbcResultSetMetaData();
1✔
365

366
    FromItem fromItem = select.getFromItem();
1✔
367
    List<Join> joins = select.getJoins();
1✔
368

369
    if (select.getWithItemsList() != null) {
1✔
370
      for (WithItem withItem : select.getWithItemsList()) {
1✔
371
        withItem.accept((SelectVisitor<JdbcResultSetMetaData>) this, metaData);
1✔
372
      }
1✔
373
    }
374

375
    if (fromItem instanceof Table) {
1✔
376
      Alias alias = fromItem.getAlias();
1✔
377
      Table t = (Table) fromItem;
1✔
378

379
      if (alias != null) {
1✔
380
        metaData.getFromTables().put(alias.getName(), (Table) fromItem);
1✔
381
      } else {
382
        metaData.getFromTables().put(t.getName(), (Table) fromItem);
1✔
383
      }
384
    } else if (fromItem != null) {
1✔
385
      Alias alias = fromItem.getAlias();
1✔
386
      JdbcTable t = new JdbcTable(metaData.getCurrentCatalogName(), metaData.getCurrentSchemaName(),
1✔
387
          alias != null ? alias.getName() : "");
1✔
388

389
      JdbcResultSetMetaData rsMetaData = fromItem.accept(this, JdbcMetaData.copyOf(metaData));
1✔
390
      try {
391
        int columnCount = rsMetaData.getColumnCount();
1✔
392
        for (int i = 1; i <= columnCount; i++) {
1✔
393
          t.add(t.tableCatalog, t.tableSchema, t.tableName,
1✔
394
              rsMetaData.getColumnLabel(i) != null && !rsMetaData.getColumnLabel(i).isEmpty()
1✔
395
                  ? rsMetaData.getColumnLabel(i)
1✔
396
                  : rsMetaData.getColumnName(i),
1✔
397
              rsMetaData.getColumnType(i), rsMetaData.getColumnClassName(i),
1✔
398
              rsMetaData.getPrecision(i), rsMetaData.getScale(i), 10, rsMetaData.isNullable(i), "",
1✔
399
              "", rsMetaData.getColumnDisplaySize(i), i, "", rsMetaData.getCatalogName(i),
1✔
400
              rsMetaData.getSchemaName(i), rsMetaData.getTableName(i), null, "", "");
1✔
401
        }
402
        metaData.put(t);
1✔
403
        metaData.getFromTables().put(alias != null ? alias.getName() : t.tableName,
1✔
404
            new Table(alias != null ? alias.getName() : t.tableName));
1✔
405
      } catch (SQLException e) {
×
406
        throw new RuntimeException(e);
×
407
      }
1✔
408
    }
409

410
    if (joins != null) {
1✔
411
      for (Join join : joins) {
1✔
412
        if (join.isLeft() || join.isInner()) {
1✔
413
          metaData.addLeftUsingJoinColumns(join.getUsingColumns());
1✔
414
        } else if (join.isRight()) {
1✔
415
          metaData.addRightUsingJoinColumns(join.getUsingColumns());
1✔
416
        }
417

418
        if (join.getFromItem() instanceof Table) {
1✔
419
          Alias alias = join.getFromItem().getAlias();
1✔
420
          Table t = (Table) join.getFromItem();
1✔
421

422
          if (alias != null) {
1✔
423
            metaData.getFromTables().put(alias.getName(), t);
1✔
424
            if (join.isNatural()) {
1✔
425
              metaData.getNaturalJoinedTables().put(alias.getName(), t);
×
426
            }
427

428
          } else {
429
            metaData.getFromTables().put(t.getName(), t);
1✔
430
            if (join.isNatural()) {
1✔
431
              metaData.addNaturalJoinedTable(t);
1✔
432
            }
433
          }
434
        } else {
1✔
435
          Alias alias = join.getFromItem().getAlias();
1✔
436
          JdbcTable t = new JdbcTable(metaData.getCurrentCatalogName(),
1✔
437
              metaData.getCurrentSchemaName(), alias != null ? alias.getName() : "");
1✔
438

439
          JdbcResultSetMetaData rsMetaData =
1✔
440
              join.getFromItem().accept(this, JdbcMetaData.copyOf(metaData));
1✔
441
          try {
442
            int columnCount = rsMetaData.getColumnCount();
1✔
443
            for (int i = 1; i <= columnCount; i++) {
1✔
444
              t.add(t.tableCatalog, t.tableSchema, t.tableName, rsMetaData.getColumnName(i),
1✔
445
                  rsMetaData.getColumnType(i), rsMetaData.getColumnClassName(i),
1✔
446
                  rsMetaData.getPrecision(i), rsMetaData.getScale(i), 10, rsMetaData.isNullable(i),
1✔
447
                  "", "", rsMetaData.getColumnDisplaySize(i), i, "", rsMetaData.getCatalogName(i),
1✔
448
                  rsMetaData.getSchemaName(i), rsMetaData.getTableName(i), null, "", "");
1✔
449
            }
450
            metaData.put(t);
1✔
451
            metaData.getFromTables().put(alias != null ? alias.getName() : t.tableName,
1✔
452
                new Table(alias != null ? alias.getName() : t.tableName));
1✔
453

454
            if (join.isNatural()) {
1✔
455
              metaData.getNaturalJoinedTables().put(alias != null ? alias.getName() : t.tableName,
×
NEW
456
                  new Table(alias != null ? alias.getName() : t.tableName));
×
457
            }
458
          } catch (SQLException e) {
×
459
            throw new RuntimeException(e);
×
460
          }
1✔
461
        }
462
      }
1✔
463
    }
464

465
    for (Table t : metaData.getFromTables().values()) {
1✔
466
      if (t.getSchemaName() == null || t.getSchemaName().isEmpty()) {
1✔
467
        t.setSchemaName(metaData.getCurrentSchemaName());
1✔
468
      }
469

470
      if (t.getDatabase() == null) {
1✔
471
        t.setDatabaseName(metaData.getCurrentCatalogName());
×
472
      } else if (t.getDatabase().getDatabaseName() == null
1✔
473
          || t.getDatabase().getDatabaseName().isEmpty()) {
×
474
        t.getDatabase().setDatabaseName(metaData.getCurrentCatalogName());
1✔
475
      }
476
    }
1✔
477

478
    for (Table t : metaData.getNaturalJoinedTables().values()) {
1✔
479
      if (t.getSchemaName() == null || t.getSchemaName().isEmpty()) {
1✔
480
        t.setSchemaName(metaData.getCurrentSchemaName());
1✔
481
      }
482

483
      if (t.getDatabase() == null) {
1✔
484
        t.setDatabaseName(metaData.getCurrentCatalogName());
×
485
      } else if (t.getDatabase().getDatabaseName() == null
1✔
NEW
486
          || t.getDatabase().getDatabaseName().isEmpty()) {
×
487
        t.getDatabase().setDatabaseName(metaData.getCurrentCatalogName());
1✔
488
      }
489
    }
1✔
490

491
    /* this is valid SQL:
492
    
493
    SELECT
494
        main.sales.salesid
495
    FROM main.sales
496
     */
497

498
    // column positions in MetaData start at 1
499
    ArrayList<SelectItem<?>> newSelectItems = new ArrayList<>();
1✔
500
    for (SelectItem<?> selectItem : select.getSelectItems()) {
1✔
501
      Alias alias = selectItem.getAlias();
1✔
502
      List<JdbcColumn> jdbcColumns =
1✔
503
          selectItem.getExpression().accept(expressionColumnResolver, metaData);
1✔
504

505
      for (JdbcColumn col : jdbcColumns) {
1✔
506
        resultSetMetaData.add(col, alias != null ? alias.withUseAs(false).getName() : null);
1✔
507
        Table t = new Table(col.tableCatalog, col.tableSchema, col.tableName);
1✔
508
        if (selectItem.getExpression() instanceof AllColumns
1✔
509
            || selectItem.getExpression() instanceof AllTableColumns) {
1✔
510
          newSelectItems.add(new SelectItem<>(
1✔
511
              new Column(t, col.columnName).withCommentText("Resolved Column"), alias));
1✔
512
        } else {
513
          newSelectItems.add(selectItem);
1✔
514
        }
515
      }
1✔
516

517
    }
1✔
518
    select.setSelectItems(newSelectItems);
1✔
519

520
    return resultSetMetaData;
1✔
521
  }
522

523
  @Override
524
  public <S> JdbcResultSetMetaData visit(PlainSelect select, S context) {
525
    if (context instanceof JdbcMetaData) {
1✔
526
      return visit(select, (JdbcMetaData) context);
1✔
527
    } else {
528
      return null;
×
529
    }
530
  }
531

532
  @Override
533
  public void visit(PlainSelect plainSelect) {
534
    SelectVisitor.super.visit(plainSelect);
×
535
  }
×
536

537
  // for visiting Column Sub-Selects
538
  public JdbcResultSetMetaData visit(Select select) {
539
    return select.accept(this, JdbcMetaData.copyOf(metaData));
×
540
  }
541

542
  @Override
543
  public <S> JdbcResultSetMetaData visit(SetOperationList setOperationList, S context) {
544
    if (context instanceof JdbcMetaData) {
×
545
      return setOperationList.getSelect(0).accept(this, (JdbcMetaData) context);
×
546
    } else {
547
      return null;
×
548
    }
549
  }
550

551
  @Override
552
  public <S> JdbcResultSetMetaData visit(WithItem withItem, S context) {
553
    if (context instanceof JdbcMetaData) {
1✔
554
      JdbcMetaData metaData = (JdbcMetaData) context;
1✔
555

556
      JdbcTable t = new JdbcTable(metaData.getCurrentCatalogName(), metaData.getCurrentSchemaName(),
1✔
557
          withItem.getAlias().getName());
1✔
558

559
      JdbcResultSetMetaData rsMetaData = withItem.getSelect()
1✔
560
          .accept((SelectVisitor<JdbcResultSetMetaData>) this, JdbcMetaData.copyOf(metaData));
1✔
561
      try {
562
        int columnCount = rsMetaData.getColumnCount();
1✔
563
        for (int i = 1; i <= columnCount; i++) {
1✔
564
          t.add(t.tableCatalog, t.tableSchema, t.tableName,
1✔
565
              rsMetaData.getColumnLabel(i) != null && !rsMetaData.getColumnLabel(i).isEmpty()
1✔
566
                  ? rsMetaData.getColumnLabel(i)
1✔
567
                  : rsMetaData.getColumnName(i),
1✔
568
              rsMetaData.getColumnType(i), rsMetaData.getColumnClassName(i),
1✔
569
              rsMetaData.getPrecision(i), rsMetaData.getScale(i), 10, rsMetaData.isNullable(i), "",
1✔
570
              "", rsMetaData.getColumnDisplaySize(i), i, "", rsMetaData.getCatalogName(i),
1✔
571
              rsMetaData.getSchemaName(i), rsMetaData.getTableName(i), null, "", "");
1✔
572
        }
573
        metaData.put(t);
1✔
574
      } catch (SQLException ex) {
×
575
        throw new RuntimeException("Error in WITH clause " + withItem.toString(), ex);
×
576
      }
1✔
577
    }
578

579
    return withItem.getSelect().accept((SelectVisitor<JdbcResultSetMetaData>) this, metaData);
1✔
580
  }
581

582
  @Override
583
  public <S> JdbcResultSetMetaData visit(Values values, S context) {
584
    return null;
×
585
  }
586

587
  @Override
588
  public void visit(Values values) {
589
    SelectVisitor.super.visit(values);
×
590
  }
×
591

592
  @Override
593
  public <S> JdbcResultSetMetaData visit(LateralSubSelect lateralSubSelect, S context) {
594
    return null;
×
595
  }
596

597
  @Override
598
  public void visit(LateralSubSelect lateralSubSelect) {
599
    SelectVisitor.super.visit(lateralSubSelect);
×
600
  }
×
601

602
  @Override
603
  public <S> JdbcResultSetMetaData visit(TableFunction tableFunction, S context) {
604
    return null;
×
605
  }
606

607
  @Override
608
  public <S> JdbcResultSetMetaData visit(ParenthesedFromItem parenthesedFromItem, S context) {
609
    JdbcResultSetMetaData resultSetMetaData = new JdbcResultSetMetaData();
1✔
610

611
    FromItem fromItem = parenthesedFromItem.getFromItem();
1✔
612
    try {
613
      resultSetMetaData.add(fromItem.accept(this, context));
1✔
614
    } catch (SQLException ex) {
×
615
      throw new RuntimeException("Failed on ParenthesedFromItem " + fromItem.toString(), ex);
×
616
    }
1✔
617

618
    List<Join> joins = parenthesedFromItem.getJoins();
1✔
619
    if (joins != null && !joins.isEmpty()) {
1✔
620
      for (Join join : joins) {
1✔
621
        try {
622
          resultSetMetaData.add(join.getFromItem().accept(this, context));
1✔
623
        } catch (SQLException ex) {
×
624
          throw new RuntimeException("Failed on Join " + join.getFromItem().toString(), ex);
×
625
        }
1✔
626
      }
1✔
627
    }
628
    return resultSetMetaData;
1✔
629
  }
630

631
  @Override
632
  public <S> JdbcResultSetMetaData visit(TableStatement tableStatement, S context) {
633
    return null;
×
634
  }
635

636
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc