Skip to content

Commit f66bf77

Browse files
fix: allow refresh-token auth without userName
- DataCloudConnection.of() accepts null userName (optional for refresh-token auth) - DataCloudDatabaseMetadata.getUserName() throws SQLException with state 28000 when userName is null - DataCloudDatasource normalizes null userName to empty string for connect path Fixes regression where driver required userName for refresh-token auth (e.g. 'userName is marked non-null but is null').
1 parent 10153b2 commit f66bf77

5 files changed

Lines changed: 51 additions & 4 deletions

File tree

jdbc-core/src/main/java/com/salesforce/datacloud/jdbc/core/DataCloudConnection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public static DataCloudConnection of(
110110
@NonNull HyperGrpcStubProvider stubProvider,
111111
@NonNull ConnectionProperties properties,
112112
JdbcURL jdbcUrl,
113-
@NonNull String userName,
113+
String userName,
114114
@NonNull ThrowingJdbcSupplier<String> lakehouseSupplier,
115115
@NonNull ThrowingJdbcSupplier<List<String>> dataspacesSupplier)
116116
throws SQLException {

jdbc-core/src/main/java/com/salesforce/datacloud/jdbc/core/DataCloudDatabaseMetadata.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,16 @@ public class DataCloudDatabaseMetadata implements DatabaseMetaData {
4242

4343
private final ThrowingJdbcSupplier<List<String>> dataspacesSupplier;
4444

45-
@Getter
4645
private final String userName;
4746

47+
@Override
48+
public String getUserName() throws SQLException {
49+
if (userName == null) {
50+
throw new SQLException("userName is not available for this connection", "28000");
51+
}
52+
return userName;
53+
}
54+
4855
@Override
4956
public boolean allProceduresAreCallable() {
5057
return false;

jdbc-core/src/test/java/com/salesforce/datacloud/jdbc/core/DataCloudConnectionTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package com.salesforce.datacloud.jdbc.core;
66

7+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
78
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
89
import static org.junit.jupiter.api.Assertions.assertThrows;
910
import static org.mockito.ArgumentMatchers.any;
@@ -13,6 +14,7 @@
1314
import io.grpc.ClientInterceptor;
1415
import java.sql.Connection;
1516
import java.sql.SQLException;
17+
import java.util.Collections;
1618
import lombok.Getter;
1719
import lombok.SneakyThrows;
1820
import lombok.val;
@@ -105,4 +107,28 @@ void testDriverInterceptorsAreAddedWhenStubProviderIsUsed() {
105107
verify(stubProvider.stub).withInterceptors(any(ClientInterceptor[].class));
106108
connection.close();
107109
}
110+
111+
/**
112+
* The driver must not require userName for refresh-token auth; connection creation succeeds with null userName,
113+
* and getMetaData().getUserName() throws SQLException when userName was not provided.
114+
*/
115+
@Test
116+
void connectionWithNullUserName_succeeds_refreshTokenAuth() throws SQLException {
117+
val stubProvider = new TestStubProvider();
118+
try (DataCloudConnection conn = DataCloudConnection.of(
119+
stubProvider,
120+
ConnectionProperties.defaultProperties(),
121+
null,
122+
null, // userName optional for refresh-token auth
123+
() -> "",
124+
Collections::emptyList)) {
125+
assertThat(conn).isNotNull();
126+
assertThat(conn.isClosed()).isFalse();
127+
assertThat(conn.getMetaData()).isNotNull();
128+
assertThatThrownBy(() -> conn.getMetaData().getUserName())
129+
.isInstanceOf(SQLException.class)
130+
.hasMessageContaining("userName is not available for this connection")
131+
.satisfies(e -> assertThat(((SQLException) e).getSQLState()).isEqualTo("28000"));
132+
}
133+
}
108134
}

jdbc-core/src/test/java/com/salesforce/datacloud/jdbc/core/DataCloudDatabaseMetadataTest.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package com.salesforce.datacloud.jdbc.core;
66

77
import static org.assertj.core.api.Assertions.assertThat;
8+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
89
import static org.assertj.core.api.Assertions.fail;
910
import static org.junit.jupiter.api.Assertions.assertFalse;
1011
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -71,10 +72,23 @@ public void testAllTablesAreSelectable() {
7172
}
7273

7374
@Test
74-
public void testGetUserName() {
75+
public void testGetUserName() throws SQLException {
7576
assertThat(dataCloudDatabaseMetadata.getUserName()).isEqualTo("userName");
7677
}
7778

79+
/**
80+
* When userName is null (e.g. refresh-token auth without userName), getUserName() throws SQLException.
81+
*/
82+
@Test
83+
public void testGetUserName_throwsWhenNull() throws SQLException {
84+
DataCloudDatabaseMetadata metadataWithNullUser = new DataCloudDatabaseMetadata(
85+
connection, JdbcURL.of("jdbc:salesforce-datacloud://login.salesforce.com"), null, null, null);
86+
assertThatThrownBy(metadataWithNullUser::getUserName)
87+
.isInstanceOf(SQLException.class)
88+
.hasMessageContaining("userName is not available for this connection")
89+
.satisfies(e -> assertThat(((SQLException) e).getSQLState()).isEqualTo("28000"));
90+
}
91+
7892
@Test
7993
public void testIsReadOnly() {
8094
assertThat(dataCloudDatabaseMetadata.isReadOnly()).isTrue();

jdbc/src/main/java/com/salesforce/datacloud/jdbc/DataCloudDatasource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ private static DataCloudConnection createConnection(
156156
stubProvider,
157157
connectionProperties,
158158
jdbcUrl,
159-
authProperties.getUserName(),
159+
authProperties.getUserName() != null ? authProperties.getUserName() : "",
160160
tokenProvider::getLakehouseName,
161161
dataspaceClient);
162162
}

0 commit comments

Comments
 (0)