55 */
66package org .hibernate .reactive .provider .service ;
77
8- import java .util .Map ;
9- import java .util .concurrent .CompletionStage ;
10-
118import org .hibernate .boot .registry .StandardServiceInitiator ;
9+ import org .hibernate .dialect .DatabaseVersion ;
1210import org .hibernate .dialect .Dialect ;
11+ import org .hibernate .engine .jdbc .connections .spi .DatabaseConnectionInfo ;
1312import org .hibernate .engine .jdbc .dialect .spi .DialectFactory ;
1413import org .hibernate .engine .jdbc .dialect .spi .DialectResolutionInfo ;
1514import org .hibernate .engine .jdbc .env .internal .JdbcEnvironmentImpl ;
15+ import org .hibernate .engine .jdbc .env .internal .JdbcEnvironmentInitiator ;
1616import org .hibernate .engine .jdbc .env .spi .JdbcEnvironment ;
1717import org .hibernate .engine .jdbc .spi .SqlExceptionHelper ;
1818import org .hibernate .reactive .pool .ReactiveConnection ;
1919import org .hibernate .reactive .pool .ReactiveConnectionPool ;
20- import org .hibernate .reactive .provider .Settings ;
2120import org .hibernate .reactive .util .impl .CompletionStages ;
2221import org .hibernate .service .ServiceRegistry ;
2322import org .hibernate .service .spi .ServiceRegistryImplementor ;
2423
2524import io .vertx .sqlclient .spi .DatabaseMetadata ;
25+ import java .util .Map ;
26+ import java .util .concurrent .CompletionStage ;
27+ import java .util .function .Function ;
2628
29+ import static java .util .Objects .requireNonNullElse ;
2730import static java .util .function .Function .identity ;
2831import static org .hibernate .reactive .util .impl .CompletionStages .completedFuture ;
2932
3235 * that provides an implementation of {@link JdbcEnvironment} that infers
3336 * the Hibernate {@link org.hibernate.dialect.Dialect} from the JDBC URL.
3437 */
35- public class NoJdbcEnvironmentInitiator implements StandardServiceInitiator <JdbcEnvironment > {
38+ public class NoJdbcEnvironmentInitiator extends JdbcEnvironmentInitiator
39+ implements StandardServiceInitiator <JdbcEnvironment > {
3640
3741 public static final NoJdbcEnvironmentInitiator INSTANCE = new NoJdbcEnvironmentInitiator ();
3842
@@ -42,14 +46,59 @@ public Class<JdbcEnvironment> getServiceInitiated() {
4246 }
4347
4448 @ Override
45- public JdbcEnvironment initiateService (Map <String , Object > configurationValues , ServiceRegistryImplementor registry ) {
46- boolean explicitDialect = configurationValues .containsKey ( Settings .DIALECT );
47- if ( explicitDialect ) {
48- DialectFactory dialectFactory = registry .getService ( DialectFactory .class );
49- return new JdbcEnvironmentImpl ( registry , dialectFactory .buildDialect ( configurationValues , null ) );
50- }
49+ protected void logConnectionInfo (DatabaseConnectionInfo databaseConnectionInfo ) {
50+ // Nothing to do we log the connection info somewhere else
51+ }
5152
52- return new JdbcEnvironmentImpl ( registry , new DialectBuilder ( configurationValues , registry ).build () );
53+ @ Override
54+ protected JdbcEnvironmentImpl getJdbcEnvironmentWithExplicitConfiguration (
55+ Map <String , Object > configurationValues ,
56+ ServiceRegistryImplementor registry ,
57+ DialectFactory dialectFactory ,
58+ DialectResolutionInfo dialectResolutionInfo ) {
59+ return super .getJdbcEnvironmentWithExplicitConfiguration (
60+ configurationValues ,
61+ registry ,
62+ dialectFactory ,
63+ dialectResolutionInfo
64+ );
65+ }
66+
67+ @ Override
68+ protected JdbcEnvironmentImpl getJdbcEnvironmentWithDefaults (
69+ Map <String , Object > configurationValues ,
70+ ServiceRegistryImplementor registry ,
71+ DialectFactory dialectFactory ) {
72+ return new JdbcEnvironmentImpl ( registry , new DialectBuilder ( configurationValues , registry )
73+ .build ( dialectFactory )
74+ );
75+ }
76+
77+ @ Override
78+ protected JdbcEnvironmentImpl getJdbcEnvironmentUsingJdbcMetadata (
79+ Map <String , Object > configurationValues ,
80+ ServiceRegistryImplementor registry ,
81+ DialectFactory dialectFactory ,
82+ String explicitDatabaseName ,
83+ Integer explicitDatabaseMajorVersion ,
84+ Integer explicitDatabaseMinorVersion ,
85+ String explicitDatabaseVersion ) {
86+ try {
87+ final Dialect dialect = new DialectBuilder ( configurationValues , registry )
88+ .build (
89+ dialectFactory ,
90+ new ExplicitMetadata (
91+ explicitDatabaseName ,
92+ explicitDatabaseMajorVersion ,
93+ explicitDatabaseMinorVersion ,
94+ explicitDatabaseVersion
95+ )
96+ );
97+ return new JdbcEnvironmentImpl ( registry , dialect );
98+ }
99+ catch (RuntimeException e ) {
100+ return getJdbcEnvironmentWithDefaults ( configurationValues , registry , dialectFactory );
101+ }
53102 }
54103
55104 private static class DialectBuilder {
@@ -62,24 +111,40 @@ public DialectBuilder(Map<String, Object> configurationValues, ServiceRegistry r
62111 this .registry = registry ;
63112 }
64113
65- public Dialect build () {
66- DialectFactory dialectFactory = registry .getService ( DialectFactory .class );
114+ public Dialect build (DialectFactory dialectFactory ) {
67115 return dialectFactory .buildDialect ( configurationValues , this ::dialectResolutionInfo );
68116 }
69117
118+ public Dialect build (DialectFactory dialectFactory , ExplicitMetadata explicitMetadata ) {
119+ return dialectFactory .buildDialect ( configurationValues , () -> dialectResolutionInfo ( explicitMetadata ) );
120+ }
121+
70122 private DialectResolutionInfo dialectResolutionInfo () {
71- ReactiveConnectionPool connectionPool = registry .getService ( ReactiveConnectionPool .class );
72- return connectionPool
123+ return dialectResolutionInfo ( DialectBuilder ::buildResolutionInfo );
124+ }
125+
126+ private DialectResolutionInfo dialectResolutionInfo (ExplicitMetadata explicitMetadata ) {
127+ return dialectResolutionInfo ( reactiveConnection -> DialectBuilder
128+ .buildResolutionInfo ( reactiveConnection , explicitMetadata )
129+ );
130+ }
131+
132+ private DialectResolutionInfo dialectResolutionInfo (Function <ReactiveConnection , CompletionStage <ReactiveDialectResolutionInfo >> dialectResolutionFunction ) {
133+ return registry
134+ .getService ( ReactiveConnectionPool .class )
73135 // The default SqlExceptionHelper in ORM requires the dialect, but we haven't created a dialect yet,
74136 // so we need to override it at this stage, or we will have an exception.
75137 .getConnection ( new SqlExceptionHelper ( true ) )
76- .thenCompose ( DialectBuilder :: buildResolutionInfo )
138+ .thenCompose ( dialectResolutionFunction )
77139 .toCompletableFuture ().join ();
78140 }
79141
80142 private static CompletionStage <ReactiveDialectResolutionInfo > buildResolutionInfo (ReactiveConnection connection ) {
81- final DatabaseMetadata databaseMetadata = connection .getDatabaseMetadata ();
82- return resolutionInfoStage ( connection , databaseMetadata )
143+ return buildResolutionInfo ( connection , null );
144+ }
145+
146+ private static CompletionStage <ReactiveDialectResolutionInfo > buildResolutionInfo (ReactiveConnection connection , ExplicitMetadata explicitMetadata ) {
147+ return resolutionInfoStage ( connection , explicitMetadata )
83148 .handle ( CompletionStages ::handle )
84149 .thenCompose ( handled -> {
85150 if ( handled .hasFailed () ) {
@@ -96,19 +161,27 @@ private static CompletionStage<ReactiveDialectResolutionInfo> buildResolutionInf
96161 } );
97162 }
98163
99- private static CompletionStage <ReactiveDialectResolutionInfo > resolutionInfoStage (ReactiveConnection connection , DatabaseMetadata databaseMetadata ) {
100- if ( databaseMetadata .productName ().equalsIgnoreCase ( "PostgreSQL" ) ) {
101- // We need to check if the database is PostgreSQL or CockroachDB
102- // Hibernate ORM does it using a query, so we need to check in advance
164+ /**
165+ * @see org.hibernate.dialect.Database#POSTGRESQL for recognizing CockroachDB
166+ */
167+ private static CompletionStage <ReactiveDialectResolutionInfo > resolutionInfoStage (ReactiveConnection connection , ExplicitMetadata explicitMetadata ) {
168+ final DatabaseMetadata databaseMetadata = explicitMetadata != null
169+ ? new ReactiveDatabaseMetadata ( connection .getDatabaseMetadata (), explicitMetadata )
170+ : connection .getDatabaseMetadata ();
171+
172+ // If the product name is explicitly set to Postgres, we are not going to override it
173+ if ( ( explicitMetadata == null || explicitMetadata .productName == null )
174+ && databaseMetadata .productName ().equalsIgnoreCase ( "PostgreSQL" ) ) {
175+ // CockroachDB returns "PostgreSQL" as product name in the metadata.
176+ // So, we need to check if the database is PostgreSQL or CockroachDB
177+ // We follow the same approach used by ORM: run a new query and check the full version metadata
103178 // See org.hibernate.dialect.Database.POSTGRESQL#createDialect
104179 return connection .select ( "select version()" )
105180 .thenApply ( DialectBuilder ::readFullVersion )
106- .thenApply ( fullversion -> {
107- if ( fullversion .startsWith ( "Cockroach" ) ) {
108- return new CockroachDatabaseMetadata ( fullversion );
109- }
110- return databaseMetadata ;
111- } )
181+ .thenApply ( fullVersion -> fullVersion .startsWith ( "Cockroach" )
182+ ? new ReactiveDatabaseMetadata ( "Cockroach" , databaseMetadata )
183+ : databaseMetadata
184+ )
112185 .thenApply ( ReactiveDialectResolutionInfo ::new );
113186 }
114187
@@ -122,32 +195,62 @@ private static String readFullVersion(ReactiveConnection.Result result) {
122195 }
123196 }
124197
125- private static class CockroachDatabaseMetadata implements DatabaseMetadata {
198+ /**
199+ * Utility class to pass around explicit metadata properties.
200+ * It's different from {@link DatabaseMetadata} because values can be null.
201+ */
202+ private static class ExplicitMetadata {
203+ private final String productName ;
204+ private final String fullVersion ;
205+ private final Integer majorVersion ;
206+ private final Integer minorVersion ;
207+
208+ public ExplicitMetadata (String explicitDatabaseName , Integer explicitDatabaseMajorVersion , Integer explicitDatabaseMinorVersion , String explicitDatabaseVersion ) {
209+ this .productName = explicitDatabaseName ;
210+ this .fullVersion = explicitDatabaseVersion ;
211+ this .majorVersion = explicitDatabaseMajorVersion ;
212+ this .minorVersion = explicitDatabaseMinorVersion ;
213+ }
214+ }
126215
127- private final String fullversion ;
216+ private static class ReactiveDatabaseMetadata implements DatabaseMetadata {
217+ public final String productName ;
218+ public final String fullVersion ;
219+ public final int majorVersion ;
220+ public final int minorVersion ;
221+
222+ public ReactiveDatabaseMetadata (String productName , DatabaseMetadata databaseMetadata ) {
223+ this .productName = productName ;
224+ this .fullVersion = databaseMetadata .productName ();
225+ this .majorVersion = databaseMetadata .majorVersion ();
226+ this .minorVersion = databaseMetadata .minorVersion ();
227+ }
128228
129- public CockroachDatabaseMetadata (String fullversion ) {
130- this .fullversion = fullversion ;
229+ public ReactiveDatabaseMetadata (DatabaseMetadata metadata , ExplicitMetadata explicitMetadata ) {
230+ productName = requireNonNullElse ( explicitMetadata .productName , metadata .productName () );
231+ fullVersion = requireNonNullElse ( explicitMetadata .fullVersion , metadata .fullVersion () );
232+ majorVersion = requireNonNullElse ( explicitMetadata .majorVersion , metadata .majorVersion () );
233+ minorVersion = requireNonNullElse ( explicitMetadata .minorVersion , metadata .minorVersion () );
131234 }
132235
133236 @ Override
134237 public String productName () {
135- return "CockroachDb" ;
238+ return productName ;
136239 }
137240
138241 @ Override
139242 public String fullVersion () {
140- return fullversion ;
243+ return fullVersion ;
141244 }
142245
143246 @ Override
144247 public int majorVersion () {
145- return 0 ;
248+ return majorVersion ;
146249 }
147250
148251 @ Override
149252 public int minorVersion () {
150- return 0 ;
253+ return minorVersion ;
151254 }
152255 }
153256
@@ -179,6 +282,14 @@ public int getDatabaseMinorVersion() {
179282 return metadata .minorVersion ();
180283 }
181284
285+ @ Override
286+ public int getDatabaseMicroVersion () {
287+ // Hibernate ORM extracts the micro version from the product version using
288+ // databaseMicroVersion(DatabaseMetaData) in JdbcEnvironmentInitiator.
289+ // But, I don't think it's used at the moment, so I will just ignore it.
290+ return DatabaseVersion .NO_VERSION ;
291+ }
292+
182293 @ Override
183294 public String getDriverName () {
184295 return getDatabaseName ();
@@ -196,6 +307,7 @@ public int getDriverMinorVersion() {
196307
197308 @ Override
198309 public String getSQLKeywords () {
310+ // Vert.x metadata doesn't have this info
199311 return null ;
200312 }
201313
0 commit comments