001/*- 002 * #%L 003 * HAPI FHIR Server - SQL Migration 004 * %% 005 * Copyright (C) 2014 - 2026 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.migrate; 021 022import ca.uhn.fhir.i18n.Msg; 023import ca.uhn.fhir.jpa.migrate.taskdef.ColumnTypeEnum; 024import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 025import jakarta.annotation.Nonnull; 026import jakarta.annotation.Nullable; 027import org.apache.commons.lang3.builder.EqualsBuilder; 028import org.apache.commons.lang3.builder.HashCodeBuilder; 029import org.apache.commons.lang3.builder.ToStringBuilder; 030import org.hibernate.boot.model.naming.Identifier; 031import org.hibernate.boot.model.relational.QualifiedSequenceName; 032import org.hibernate.dialect.Dialect; 033import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver; 034import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter; 035import org.hibernate.engine.jdbc.dialect.spi.DialectResolver; 036import org.hibernate.engine.jdbc.env.internal.NormalizingIdentifierHelperImpl; 037import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData; 038import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; 039import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; 040import org.hibernate.engine.jdbc.env.spi.LobCreatorBuilder; 041import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; 042import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter; 043import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; 044import org.hibernate.service.ServiceRegistry; 045import org.hibernate.sql.ast.SqlAstTranslatorFactory; 046import org.hibernate.tool.schema.extract.spi.ExtractionContext; 047import org.hibernate.tool.schema.extract.spi.SequenceInformation; 048import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; 049import org.intellij.lang.annotations.Language; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052import org.springframework.jdbc.core.ColumnMapRowMapper; 053import org.springframework.transaction.support.TransactionTemplate; 054import org.springframework.util.LinkedCaseInsensitiveMap; 055 056import java.sql.Connection; 057import java.sql.DatabaseMetaData; 058import java.sql.ResultSet; 059import java.sql.SQLException; 060import java.sql.Types; 061import java.util.ArrayList; 062import java.util.Collections; 063import java.util.HashSet; 064import java.util.List; 065import java.util.Locale; 066import java.util.Map; 067import java.util.Objects; 068import java.util.Set; 069import java.util.stream.Collectors; 070import java.util.stream.StreamSupport; 071import javax.sql.DataSource; 072 073public class JdbcUtils { 074 private static final Logger ourLog = LoggerFactory.getLogger(JdbcUtils.class); 075 076 /** 077 * Retrieve all index names 078 */ 079 public static Set<String> getPrimaryKeyColumns( 080 DriverTypeEnum.ConnectionProperties theConnectionProperties, String theTableName) throws SQLException { 081 082 DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); 083 try (Connection connection = dataSource.getConnection()) { 084 return theConnectionProperties.getTxTemplate().execute(t -> { 085 DatabaseMetaData metadata; 086 Set<String> retVal = new HashSet<>(); 087 try { 088 metadata = connection.getMetaData(); 089 090 try (ResultSet results = 091 metadata.getPrimaryKeys(connection.getCatalog(), connection.getSchema(), theTableName)) { 092 while (results.next()) { 093 String columnName = results.getString("COLUMN_NAME"); 094 retVal.add(columnName.toUpperCase(Locale.US)); 095 } 096 } 097 098 } catch (SQLException e) { 099 throw new InternalErrorException(Msg.code(2562) + e); 100 } 101 return retVal; 102 }); 103 } 104 } 105 106 /** 107 * Retrieve all index names 108 */ 109 public static Set<String> getIndexNames( 110 DriverTypeEnum.ConnectionProperties theConnectionProperties, String theTableName) throws SQLException { 111 112 if (!getTableNames(theConnectionProperties).contains(theTableName)) { 113 return Collections.emptySet(); 114 } 115 116 DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); 117 try (Connection connection = dataSource.getConnection()) { 118 return theConnectionProperties.getTxTemplate().execute(t -> { 119 DatabaseMetaData metadata; 120 try { 121 metadata = connection.getMetaData(); 122 123 Set<String> indexNames = new HashSet<>(); 124 125 for (boolean unique : Set.of(false, true)) { 126 try (ResultSet indexes = getIndexInfo(theTableName, connection, metadata, unique)) { 127 while (indexes.next()) { 128 ourLog.debug("*** Next index: {}", new ColumnMapRowMapper().mapRow(indexes, 0)); 129 String indexName = indexes.getString("INDEX_NAME"); 130 indexNames.add(indexName); 131 } 132 } 133 } 134 135 indexNames = indexNames.stream() 136 .filter(Objects::nonNull) // filter out the nulls first 137 .map(s -> s.toUpperCase(Locale.US)) // then convert the non-null entries to upper case 138 .collect(Collectors.toSet()); 139 140 return indexNames; 141 142 } catch (SQLException e) { 143 throw new InternalErrorException(Msg.code(29) + e); 144 } 145 }); 146 } 147 } 148 149 @SuppressWarnings("ConstantConditions") 150 public static boolean isIndexUnique( 151 DriverTypeEnum.ConnectionProperties theConnectionProperties, String theTableName, String theIndexName) 152 throws SQLException { 153 DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); 154 try (Connection connection = dataSource.getConnection()) { 155 return theConnectionProperties.getTxTemplate().execute(t -> { 156 DatabaseMetaData metadata; 157 try { 158 metadata = connection.getMetaData(); 159 try (ResultSet indexes = getIndexInfo(theTableName, connection, metadata, false)) { 160 161 while (indexes.next()) { 162 String indexName = indexes.getString("INDEX_NAME"); 163 if (theIndexName.equalsIgnoreCase(indexName)) { 164 boolean nonUnique = indexes.getBoolean("NON_UNIQUE"); 165 return !nonUnique; 166 } 167 } 168 } 169 170 } catch (SQLException e) { 171 throw new InternalErrorException(Msg.code(30) + e); 172 } 173 174 throw new InternalErrorException( 175 Msg.code(31) + "Can't find index: " + theIndexName + " on table " + theTableName); 176 }); 177 } 178 } 179 180 private static ResultSet getIndexInfo( 181 String theTableName, Connection theConnection, DatabaseMetaData theMetadata, boolean theUnique) 182 throws SQLException { 183 // FYI Using approximate=false causes a very slow table scan on Oracle 184 boolean approximate = true; 185 return theMetadata.getIndexInfo( 186 theConnection.getCatalog(), 187 theConnection.getSchema(), 188 massageIdentifier(theMetadata, theTableName), 189 theUnique, 190 approximate); 191 } 192 193 /** 194 * Retrieve all index names 195 */ 196 public static ColumnType getColumnType( 197 DriverTypeEnum.ConnectionProperties theConnectionProperties, String theTableName, String theColumnName) 198 throws SQLException { 199 DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); 200 try (Connection connection = dataSource.getConnection()) { 201 return theConnectionProperties.getTxTemplate().execute(t -> { 202 DatabaseMetaData metadata; 203 try { 204 metadata = connection.getMetaData(); 205 String catalog = connection.getCatalog(); 206 String schema = connection.getSchema(); 207 try (ResultSet indexes = 208 metadata.getColumns(catalog, schema, massageIdentifier(metadata, theTableName), null)) { 209 210 while (indexes.next()) { 211 212 String tableName = indexes.getString("TABLE_NAME").toUpperCase(Locale.US); 213 if (!theTableName.equalsIgnoreCase(tableName)) { 214 continue; 215 } 216 String columnName = indexes.getString("COLUMN_NAME").toUpperCase(Locale.US); 217 if (!theColumnName.equalsIgnoreCase(columnName)) { 218 continue; 219 } 220 221 int dataType = indexes.getInt("DATA_TYPE"); 222 Long length = indexes.getLong("COLUMN_SIZE"); 223 switch (dataType) { 224 case Types.LONGVARCHAR: 225 return new ColumnType(ColumnTypeEnum.TEXT, length); 226 case Types.BIT: 227 case Types.BOOLEAN: 228 return new ColumnType(ColumnTypeEnum.BOOLEAN, length); 229 case Types.VARCHAR: 230 return new ColumnType(ColumnTypeEnum.STRING, length); 231 case Types.NUMERIC: 232 case Types.BIGINT: 233 case Types.DECIMAL: 234 return new ColumnType(ColumnTypeEnum.LONG, length); 235 case Types.INTEGER: 236 return new ColumnType(ColumnTypeEnum.INT, length); 237 case Types.TIMESTAMP: 238 case Types.TIMESTAMP_WITH_TIMEZONE: 239 return new ColumnType(ColumnTypeEnum.DATE_TIMESTAMP, length); 240 case Types.BLOB: 241 return new ColumnType(ColumnTypeEnum.BLOB, length); 242 case Types.LONGVARBINARY: 243 return new ColumnType(ColumnTypeEnum.BINARY, length); 244 case Types.VARBINARY: 245 if (DriverTypeEnum.MSSQL_2012.equals(theConnectionProperties.getDriverType())) { 246 // MS SQLServer seems to be mapping BLOB to VARBINARY under the covers, 247 // so we need to reverse that mapping 248 return new ColumnType(ColumnTypeEnum.BLOB, length); 249 250 } else { 251 throw new IllegalArgumentException( 252 Msg.code(33) + "Don't know how to handle datatype " + dataType 253 + " for column " + theColumnName 254 + " on table " + theTableName); 255 } 256 case Types.CLOB: 257 return new ColumnType(ColumnTypeEnum.CLOB, length); 258 case Types.DOUBLE: 259 return new ColumnType(ColumnTypeEnum.DOUBLE, length); 260 case Types.FLOAT: 261 return new ColumnType(ColumnTypeEnum.FLOAT, length); 262 case Types.TINYINT: 263 return new ColumnType(ColumnTypeEnum.TINYINT, length); 264 case Types.SMALLINT: 265 return new ColumnType(ColumnTypeEnum.SMALLINT, length); 266 default: 267 throw new IllegalArgumentException( 268 Msg.code(34) + "Don't know how to handle datatype " + dataType 269 + " for column " + theColumnName 270 + " on table " + theTableName); 271 } 272 } 273 } 274 275 ourLog.debug("Unable to find column {} in table {}.", theColumnName, theTableName); 276 return null; 277 278 } catch (SQLException e) { 279 throw new InternalErrorException(Msg.code(35) + e); 280 } 281 }); 282 } 283 } 284 285 /** 286 * Retrieve all index names. The returned names will be in upper case 287 * always. 288 */ 289 public static Set<String> getForeignKeys( 290 DriverTypeEnum.ConnectionProperties theConnectionProperties, 291 String theTableName, 292 @Nullable String theForeignTable) 293 throws SQLException { 294 DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); 295 296 try (Connection connection = dataSource.getConnection()) { 297 TransactionTemplate txTemplate = theConnectionProperties.getTxTemplate(); 298 return txTemplate.execute(t -> { 299 DatabaseMetaData metadata; 300 try { 301 metadata = connection.getMetaData(); 302 String catalog = connection.getCatalog(); 303 String schema = connection.getSchema(); 304 305 List<String> parentTables = new ArrayList<>(); 306 if (theTableName != null) { 307 parentTables.add(massageIdentifier(metadata, theTableName)); 308 } else { 309 // If no foreign table is specified, we'll try all of them 310 parentTables.addAll(JdbcUtils.getTableNames(theConnectionProperties)); 311 } 312 313 String foreignTable = massageIdentifier(metadata, theForeignTable); 314 315 Set<String> fkNames = new HashSet<>(); 316 for (String nextParentTable : parentTables) { 317 try (ResultSet indexes = metadata.getCrossReference( 318 catalog, schema, nextParentTable, catalog, schema, foreignTable)) { 319 while (indexes.next()) { 320 String fkName = indexes.getString("FK_NAME"); 321 fkName = fkName.toUpperCase(Locale.US); 322 fkNames.add(fkName); 323 } 324 } 325 } 326 327 return fkNames; 328 } catch (SQLException e) { 329 throw new InternalErrorException(Msg.code(36) + e); 330 } 331 }); 332 } 333 } 334 335 /** 336 * Retrieve names of foreign keys that reference a specified foreign key column. 337 */ 338 public static Set<String> getForeignKeysForColumn( 339 DriverTypeEnum.ConnectionProperties theConnectionProperties, 340 String theForeignKeyColumn, 341 String theForeignTable) 342 throws SQLException { 343 DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); 344 345 try (Connection connection = dataSource.getConnection()) { 346 return theConnectionProperties.getTxTemplate().execute(t -> { 347 DatabaseMetaData metadata; 348 try { 349 metadata = connection.getMetaData(); 350 String catalog = connection.getCatalog(); 351 String schema = connection.getSchema(); 352 353 List<String> parentTables = new ArrayList<>(); 354 parentTables.addAll(JdbcUtils.getTableNames(theConnectionProperties)); 355 356 String foreignTable = massageIdentifier(metadata, theForeignTable); 357 358 Set<String> fkNames = new HashSet<>(); 359 for (String nextParentTable : parentTables) { 360 try (ResultSet indexes = metadata.getCrossReference( 361 catalog, schema, nextParentTable, catalog, schema, foreignTable)) { 362 while (indexes.next()) { 363 if (theForeignKeyColumn.equals(indexes.getString("FKCOLUMN_NAME"))) { 364 String fkName = indexes.getString("FK_NAME"); 365 fkName = fkName.toUpperCase(Locale.US); 366 fkNames.add(fkName); 367 } 368 } 369 } 370 } 371 372 return fkNames; 373 } catch (SQLException e) { 374 throw new InternalErrorException(Msg.code(37) + e); 375 } 376 }); 377 } 378 } 379 380 /** 381 * Retrieve all index names 382 */ 383 public static Set<String> getColumnNames( 384 DriverTypeEnum.ConnectionProperties theConnectionProperties, String theTableName) throws SQLException { 385 DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); 386 try (Connection connection = dataSource.getConnection()) { 387 return theConnectionProperties.getTxTemplate().execute(t -> { 388 DatabaseMetaData metadata; 389 try { 390 metadata = connection.getMetaData(); 391 LinkedCaseInsensitiveMap<String> columnNames = new LinkedCaseInsensitiveMap<>(); 392 393 try (ResultSet indexes = metadata.getColumns( 394 connection.getCatalog(), 395 connection.getSchema(), 396 massageIdentifier(metadata, theTableName), 397 null)) { 398 399 while (indexes.next()) { 400 String tableName = indexes.getString("TABLE_NAME").toUpperCase(Locale.US); 401 if (!theTableName.equalsIgnoreCase(tableName)) { 402 continue; 403 } 404 405 String columnName = indexes.getString("COLUMN_NAME"); 406 columnName = columnName.toUpperCase(Locale.US); 407 columnNames.put(columnName, columnName); 408 } 409 } 410 411 return columnNames.keySet(); 412 } catch (SQLException e) { 413 throw new InternalErrorException(Msg.code(38) + e); 414 } 415 }); 416 } 417 } 418 419 public static Set<String> getSequenceNames(DriverTypeEnum.ConnectionProperties theConnectionProperties) 420 throws SQLException { 421 List<SequenceInformation> sequenceInformation = getSequenceInformation(theConnectionProperties); 422 423 return sequenceInformation.stream() 424 .map(SequenceInformation::getSequenceName) 425 .map(QualifiedSequenceName::getSequenceName) 426 .map(Identifier::getText) 427 .collect(Collectors.toSet()); 428 } 429 430 @Nonnull 431 public static List<SequenceInformation> getSequenceInformation( 432 DriverTypeEnum.ConnectionProperties theConnectionProperties) throws SQLException { 433 DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); 434 try (Connection connection = dataSource.getConnection()) { 435 return Objects.requireNonNull( 436 theConnectionProperties.getTxTemplate().execute(t -> { 437 try { 438 DialectResolver dialectResolver = new StandardDialectResolver(); 439 Dialect dialect = dialectResolver.resolveDialect( 440 new DatabaseMetaDataDialectResolutionInfoAdapter(connection.getMetaData())); 441 442 List<SequenceInformation> sequenceInformation = new ArrayList<>(); 443 if (dialect.getSequenceSupport().supportsSequences()) { 444 445 // Use Hibernate to get a list of current sequences 446 SequenceInformationExtractor sequenceInformationExtractor = 447 dialect.getSequenceInformationExtractor(); 448 ExtractionContext extractionContext = new EmptyExtractionContext(connection, dialect); 449 Iterable<SequenceInformation> sequenceInformationIterator = 450 sequenceInformationExtractor.extractMetadata(extractionContext); 451 452 return StreamSupport.stream(sequenceInformationIterator.spliterator(), false) 453 .collect(Collectors.toList()); 454 } 455 return sequenceInformation; 456 } catch (SQLException e) { 457 throw new InternalErrorException(Msg.code(39) + e); 458 } 459 })); 460 } 461 } 462 463 public static Set<String> getTableNames(DriverTypeEnum.ConnectionProperties theConnectionProperties) 464 throws SQLException { 465 DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); 466 try (Connection connection = dataSource.getConnection()) { 467 return theConnectionProperties.getTxTemplate().execute(t -> { 468 DatabaseMetaData metadata; 469 try { 470 metadata = connection.getMetaData(); 471 Set<String> columnNames = new HashSet<>(); 472 473 try (ResultSet tables = 474 metadata.getTables(connection.getCatalog(), connection.getSchema(), null, null)) { 475 476 while (tables.next()) { 477 String tableName = tables.getString("TABLE_NAME"); 478 tableName = tableName.toUpperCase(Locale.US); 479 480 String tableType = tables.getString("TABLE_TYPE"); 481 if ("SYSTEM TABLE".equalsIgnoreCase(tableType)) { 482 continue; 483 } 484 if (SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME.equalsIgnoreCase(tableName)) { 485 continue; 486 } 487 488 columnNames.add(tableName); 489 } 490 } 491 492 return columnNames; 493 } catch (SQLException e) { 494 throw new InternalErrorException(Msg.code(40) + e); 495 } 496 }); 497 } 498 } 499 500 public static boolean isColumnNullable( 501 DriverTypeEnum.ConnectionProperties theConnectionProperties, String theTableName, String theColumnName) 502 throws SQLException { 503 DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); 504 try (Connection connection = dataSource.getConnection()) { 505 //noinspection ConstantConditions 506 return theConnectionProperties.getTxTemplate().execute(t -> { 507 DatabaseMetaData metadata; 508 try { 509 metadata = connection.getMetaData(); 510 try (ResultSet tables = metadata.getColumns( 511 connection.getCatalog(), 512 connection.getSchema(), 513 massageIdentifier(metadata, theTableName), 514 null)) { 515 516 while (tables.next()) { 517 String tableName = tables.getString("TABLE_NAME").toUpperCase(Locale.US); 518 if (!theTableName.equalsIgnoreCase(tableName)) { 519 continue; 520 } 521 522 if (theColumnName.equalsIgnoreCase(tables.getString("COLUMN_NAME"))) { 523 String nullable = tables.getString("IS_NULLABLE"); 524 if ("YES".equalsIgnoreCase(nullable)) { 525 return true; 526 } else if ("NO".equalsIgnoreCase(nullable)) { 527 return false; 528 } else { 529 throw new IllegalStateException(Msg.code(41) + "Unknown nullable: " + nullable); 530 } 531 } 532 } 533 } 534 535 throw new IllegalStateException(Msg.code(42) + "Did not find column " + theColumnName); 536 } catch (SQLException e) { 537 throw new InternalErrorException(Msg.code(43) + e); 538 } 539 }); 540 } 541 } 542 543 public static void executeSql( 544 DriverTypeEnum.ConnectionProperties theConnectionProperties, 545 @Language("SQL") String theSql, 546 Object... theArgs) { 547 theConnectionProperties.getTxTemplate().execute(t -> { 548 theConnectionProperties.newJdbcTemplate().update(theSql, theArgs); 549 return null; 550 }); 551 } 552 553 public static List<Map<String, Object>> executeQuery( 554 DriverTypeEnum.ConnectionProperties theConnectionProperties, 555 @Language("SQL") String theSql, 556 Object... theArgs) { 557 return theConnectionProperties.getTxTemplate().execute(t -> theConnectionProperties 558 .newJdbcTemplate() 559 .query(theSql, theArgs, new ColumnMapRowMapper())); 560 } 561 562 public static String massageIdentifier(DatabaseMetaData theMetadata, String theIdentifier) throws SQLException { 563 String retVal = theIdentifier; 564 if (theIdentifier == null) { 565 return null; 566 } else if (theMetadata.storesLowerCaseIdentifiers()) { 567 retVal = retVal.toLowerCase(); 568 } else { 569 retVal = retVal.toUpperCase(); 570 } 571 return retVal; 572 } 573 574 public static class ColumnType { 575 private final ColumnTypeEnum myColumnTypeEnum; 576 private final Long myLength; 577 578 public ColumnType(ColumnTypeEnum theColumnType, Long theLength) { 579 myColumnTypeEnum = theColumnType; 580 myLength = theLength; 581 } 582 583 public ColumnType(ColumnTypeEnum theColumnType, int theLength) { 584 this(theColumnType, (long) theLength); 585 } 586 587 public ColumnType(ColumnTypeEnum theColumnType) { 588 this(theColumnType, null); 589 } 590 591 @Override 592 public boolean equals(Object theO) { 593 if (this == theO) { 594 return true; 595 } 596 597 if (theO == null || getClass() != theO.getClass()) { 598 return false; 599 } 600 601 ColumnType that = (ColumnType) theO; 602 603 return new EqualsBuilder() 604 .append(myColumnTypeEnum, that.myColumnTypeEnum) 605 .append(myLength, that.myLength) 606 .isEquals(); 607 } 608 609 @Override 610 public int hashCode() { 611 return new HashCodeBuilder(17, 37) 612 .append(myColumnTypeEnum) 613 .append(myLength) 614 .toHashCode(); 615 } 616 617 @Override 618 public String toString() { 619 ToStringBuilder b = new ToStringBuilder(this); 620 b.append("type", myColumnTypeEnum); 621 if (myLength != null) { 622 b.append("length", myLength); 623 } 624 return b.toString(); 625 } 626 627 public ColumnTypeEnum getColumnTypeEnum() { 628 return myColumnTypeEnum; 629 } 630 631 public Long getLength() { 632 return myLength; 633 } 634 635 public boolean equals(ColumnTypeEnum theTaskColumnType, Long theTaskColumnLength) { 636 ourLog.debug( 637 "Comparing existing {} {} to new {} {}", 638 myColumnTypeEnum, 639 myLength, 640 theTaskColumnType, 641 theTaskColumnLength); 642 return myColumnTypeEnum == theTaskColumnType 643 && (theTaskColumnLength == null || theTaskColumnLength.equals(myLength)); 644 } 645 } 646 647 private static class EmptyExtractionContext extends ExtractionContext.EmptyExtractionContext { 648 649 private final Connection myConnection; 650 private final Dialect myDialect; 651 652 public EmptyExtractionContext(Connection theConnection, Dialect theDialect) { 653 this.myConnection = theConnection; 654 this.myDialect = theDialect; 655 } 656 657 @Override 658 public Connection getJdbcConnection() { 659 return myConnection; 660 } 661 662 @Override 663 public ServiceRegistry getServiceRegistry() { 664 return super.getServiceRegistry(); 665 } 666 667 @Override 668 public JdbcEnvironment getJdbcEnvironment() { 669 return new JdbcEnvironment() { 670 671 @Override 672 public Dialect getDialect() { 673 return myDialect; 674 } 675 676 @Override 677 public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { 678 return null; 679 } 680 681 @Override 682 public ExtractedDatabaseMetaData getExtractedDatabaseMetaData() { 683 return null; 684 } 685 686 @Override 687 public Identifier getCurrentCatalog() { 688 return null; 689 } 690 691 @Override 692 public Identifier getCurrentSchema() { 693 return null; 694 } 695 696 @Override 697 public QualifiedObjectNameFormatter getQualifiedObjectNameFormatter() { 698 return null; 699 } 700 701 @Override 702 public IdentifierHelper getIdentifierHelper() { 703 return new NormalizingIdentifierHelperImpl(this, null, true, true, true, true, null, null, null); 704 } 705 706 @Override 707 public NameQualifierSupport getNameQualifierSupport() { 708 return null; 709 } 710 711 @Override 712 public SqlExceptionHelper getSqlExceptionHelper() { 713 return null; 714 } 715 716 @Override 717 public LobCreatorBuilder getLobCreatorBuilder() { 718 return null; 719 } 720 }; 721 } 722 } 723}