Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ public ClientSession toClientSession(TrinoUri uri)
.clientInfo(clientInfo.orElse(null))
.catalog(uri.getCatalog().orElse(catalog.orElse(null)))
.schema(uri.getSchema().orElse(schema.orElse(null)))
.timeZone(timeZone)
.timeZone(uri.getTimeZone())
.locale(Locale.getDefault())
.resourceEstimates(toResourceEstimates(resourceEstimates))
.properties(toProperties(sessionProperties))
Expand Down Expand Up @@ -409,6 +409,7 @@ public TrinoUri getTrinoUri(Map<PropertyName, String> restrictedProperties)
traceToken.ifPresent(builder::setTraceToken);
socksProxy.ifPresent(builder::setSocksProxy);
httpProxy.ifPresent(builder::setHttpProxy);
builder.setTimeZone(timeZone);
builder.setDisableCompression(disableCompression);
TrinoUri trinoUri;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.ietf.jgss.GSSCredential;

import java.io.File;
import java.time.ZoneId;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -97,6 +98,7 @@ enum SslVerificationMode
public static final ConnectionProperty<String, Class<? extends DnsResolver>> DNS_RESOLVER = new Resolver();
public static final ConnectionProperty<String, String> DNS_RESOLVER_CONTEXT = new ResolverContext();
public static final ConnectionProperty<String, String> HOSTNAME_IN_CERTIFICATE = new HostnameInCertificate();
public static final ConnectionProperty<String, ZoneId> TIMEZONE = new TimeZone();

private static final Set<ConnectionProperty<?, ?>> ALL_PROPERTIES = ImmutableSet.<ConnectionProperty<?, ?>>builder()
.add(USER)
Expand Down Expand Up @@ -141,6 +143,7 @@ enum SslVerificationMode
.add(DNS_RESOLVER)
.add(DNS_RESOLVER_CONTEXT)
.add(HOSTNAME_IN_CERTIFICATE)
.add(TIMEZONE)
.build();

private static final Map<String, ConnectionProperty<?, ?>> KEY_LOOKUP = unmodifiableMap(ALL_PROPERTIES.stream()
Expand Down Expand Up @@ -704,6 +707,15 @@ public HostnameInCertificate()
}
}

private static class TimeZone
extends AbstractConnectionProperty<String, ZoneId>
{
public TimeZone()
{
super(PropertyName.TIMEZONE, NOT_REQUIRED, ALLOWED, ZoneId::of);
}
}

private static class MapPropertyParser
{
private static final CharMatcher PRINTABLE_ASCII = CharMatcher.inRange((char) 0x21, (char) 0x7E);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public enum PropertyName
DNS_RESOLVER("dnsResolver"),
DNS_RESOLVER_CONTEXT("dnsResolverContext"),
HOSTNAME_IN_CERTIFICATE("hostnameInCertificate"),
TIMEZONE("timezone"),
// these two are not actual properties but parts of the path
CATALOG("catalog"),
SCHEMA("schema");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.time.Duration;
import java.time.ZoneId;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -104,6 +105,7 @@
import static io.trino.client.uri.ConnectionProperties.SslVerificationMode.CA;
import static io.trino.client.uri.ConnectionProperties.SslVerificationMode.FULL;
import static io.trino.client.uri.ConnectionProperties.SslVerificationMode.NONE;
import static io.trino.client.uri.ConnectionProperties.TIMEZONE;
import static io.trino.client.uri.ConnectionProperties.TRACE_TOKEN;
import static io.trino.client.uri.ConnectionProperties.USER;
import static java.lang.String.format;
Expand Down Expand Up @@ -159,6 +161,7 @@ public class TrinoUri
private Optional<KnownTokenCache> externalAuthenticationTokenCache;
private Optional<Map<String, String>> extraCredentials;
private Optional<String> hostnameInCertificate;
private Optional<ZoneId> timeZone;
private Optional<String> clientInfo;
private Optional<String> clientTags;
private Optional<String> traceToken;
Expand Down Expand Up @@ -211,6 +214,7 @@ private TrinoUri(
Optional<KnownTokenCache> externalAuthenticationTokenCache,
Optional<Map<String, String>> extraCredentials,
Optional<String> hostnameInCertificate,
Optional<ZoneId> timeZone,
Optional<String> clientInfo,
Optional<String> clientTags,
Optional<String> traceToken,
Expand Down Expand Up @@ -262,6 +266,7 @@ private TrinoUri(
this.externalAuthenticationTokenCache = EXTERNAL_AUTHENTICATION_TOKEN_CACHE.getValueOrDefault(urlProperties, externalAuthenticationTokenCache);
this.extraCredentials = EXTRA_CREDENTIALS.getValueOrDefault(urlProperties, extraCredentials);
this.hostnameInCertificate = HOSTNAME_IN_CERTIFICATE.getValueOrDefault(urlProperties, hostnameInCertificate);
this.timeZone = TIMEZONE.getValueOrDefault(urlProperties, timeZone);
this.clientInfo = CLIENT_INFO.getValueOrDefault(urlProperties, clientInfo);
this.clientTags = CLIENT_TAGS.getValueOrDefault(urlProperties, clientTags);
this.traceToken = TRACE_TOKEN.getValueOrDefault(urlProperties, traceToken);
Expand Down Expand Up @@ -347,6 +352,7 @@ private Properties buildProperties()
.map(entry -> entry.getKey() + ":" + entry.getValue())
.collect(Collectors.joining(";"))));
hostnameInCertificate.ifPresent(value -> properties.setProperty(PropertyName.HOSTNAME_IN_CERTIFICATE.toString(), value));
timeZone.ifPresent(value -> properties.setProperty(PropertyName.TIMEZONE.toString(), value.getId()));
clientInfo.ifPresent(value -> properties.setProperty(PropertyName.CLIENT_INFO.toString(), value));
clientTags.ifPresent(value -> properties.setProperty(PropertyName.CLIENT_TAGS.toString(), value));
traceToken.ifPresent(value -> properties.setProperty(PropertyName.TRACE_TOKEN.toString(), value));
Expand Down Expand Up @@ -404,6 +410,7 @@ protected TrinoUri(URI uri, Properties driverProperties)
this.externalAuthenticationTokenCache = EXTERNAL_AUTHENTICATION_TOKEN_CACHE.getValue(properties);
this.extraCredentials = EXTRA_CREDENTIALS.getValue(properties);
this.hostnameInCertificate = HOSTNAME_IN_CERTIFICATE.getValue(properties);
this.timeZone = TIMEZONE.getValue(properties);
this.clientInfo = CLIENT_INFO.getValue(properties);
this.clientTags = CLIENT_TAGS.getValue(properties);
this.traceToken = TRACE_TOKEN.getValue(properties);
Expand Down Expand Up @@ -531,6 +538,11 @@ public boolean isAssumeLiteralUnderscoreInMetadataCallsForNonConformingClients()
return assumeLiteralUnderscoreInMetadataCallsForNonConformingClients.orElse(false);
}

public ZoneId getTimeZone()
{
return timeZone.orElseGet(ZoneId::systemDefault);
}

public Properties getProperties()
{
return properties;
Expand Down Expand Up @@ -909,6 +921,7 @@ public static final class Builder
private KnownTokenCache externalAuthenticationTokenCache;
private Map<String, String> extraCredentials;
private String hostnameInCertificate;
private ZoneId timeZone;
private String clientInfo;
private String clientTags;
private String traceToken;
Expand Down Expand Up @@ -1166,6 +1179,12 @@ public Builder setHostnameInCertificate(String hostnameInCertificate)
return this;
}

public Builder setTimeZone(ZoneId timeZone)
{
this.timeZone = requireNonNull(timeZone, "timeZone is null");
return this;
}

public Builder setClientInfo(String clientInfo)
{
this.clientInfo = requireNonNull(clientInfo, "clientInfo is null");
Expand Down Expand Up @@ -1239,6 +1258,7 @@ public TrinoUri build()
Optional.ofNullable(externalAuthenticationTokenCache),
Optional.ofNullable(extraCredentials),
Optional.ofNullable(hostnameInCertificate),
Optional.ofNullable(timeZone),
Optional.ofNullable(clientInfo),
Optional.ofNullable(clientTags),
Optional.ofNullable(traceToken),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public class TrinoConnection
uri.getTraceToken().ifPresent(tags -> clientInfo.put(TRACE_TOKEN, tags));

roles.putAll(uri.getRoles());
timeZoneId.set(ZoneId.systemDefault());
timeZoneId.set(uri.getTimeZone());
locale.set(Locale.getDefault());
sessionProperties.putAll(uri.getSessionProperties());
}
Expand Down
34 changes: 34 additions & 0 deletions client/trino-jdbc/src/test/java/io/trino/jdbc/TestTrinoDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.zone.ZoneRulesException;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;
Expand Down Expand Up @@ -666,6 +667,32 @@ public void testSetTimeZoneId()
assertEquals(rs.getTimestamp("ts"), new Timestamp(new DateTime(2001, 2, 3, 3, 4, 5, defaultZone).getMillis()));
}
}

try (Connection connection = createConnectionWithParameter("timezone=Asia/Kolkata")) {
try (Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql)) {
assertTrue(rs.next());
assertEquals(rs.getString("zone"), "Asia/Kolkata");
// setting the session timezone has no effect on the interpretation of timestamps in the JDBC driver
assertEquals(rs.getTimestamp("ts"), new Timestamp(new DateTime(2001, 2, 3, 3, 4, 5, defaultZone).getMillis()));
}
}

try (Connection connection = createConnectionWithParameter("timezone=UTC+05:30")) {
try (Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql)) {
assertTrue(rs.next());
assertEquals(rs.getString("zone"), "+05:30");
// setting the session timezone has no effect on the interpretation of timestamps in the JDBC driver
assertEquals(rs.getTimestamp("ts"), new Timestamp(new DateTime(2001, 2, 3, 3, 4, 5, defaultZone).getMillis()));
}
}

assertThatThrownBy(() -> createConnectionWithParameter("timezone=Asia/NOT_FOUND"))
.isInstanceOf(SQLException.class)
.hasMessage("Connection property timezone value is invalid: Asia/NOT_FOUND")
.hasRootCauseInstanceOf(ZoneRulesException.class)
.hasRootCauseMessage("Unknown time-zone ID: Asia/NOT_FOUND");
}

@Test
Expand Down Expand Up @@ -1200,6 +1227,13 @@ private Connection createConnection(String catalog, String schema)
return DriverManager.getConnection(url, "test", null);
}

private Connection createConnectionWithParameter(String parameter)
throws SQLException
{
String url = format("jdbc:trino://%s?%s", server.getAddress(), parameter);
return DriverManager.getConnection(url, "test", null);
}

private static Properties toProperties(Map<String, String> map)
{
Properties properties = new Properties();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import java.net.URI;
import java.sql.SQLException;
import java.time.ZoneId;
import java.time.zone.ZoneRulesException;
import java.util.Properties;

import static io.trino.client.uri.PropertyName.CLIENT_TAGS;
Expand Down Expand Up @@ -423,6 +425,26 @@ public void testAssumeLiteralUnderscoreInMetadataCallsForNonConformingClients()
assertThat(parameters.isAssumeLiteralNamesInMetadataCallsForNonConformingClients()).isFalse();
}

@Test
public void testTimezone()
throws SQLException
{
TrinoDriverUri defaultParameters = createDriverUri("jdbc:trino://localhost:8080");
assertThat(defaultParameters.getTimeZone()).isEqualTo(ZoneId.systemDefault());

TrinoDriverUri parameters = createDriverUri("jdbc:trino://localhost:8080?timezone=Asia/Kolkata");
assertThat(parameters.getTimeZone()).isEqualTo(ZoneId.of("Asia/Kolkata"));

TrinoDriverUri offsetParameters = createDriverUri("jdbc:trino://localhost:8080?timezone=UTC+05:30");
assertThat(offsetParameters.getTimeZone()).isEqualTo(ZoneId.of("UTC+05:30"));

assertThatThrownBy(() -> createDriverUri("jdbc:trino://localhost:8080?timezone=Asia/NOT_FOUND"))
.isInstanceOf(SQLException.class)
.hasMessage("Connection property timezone value is invalid: Asia/NOT_FOUND")
.hasRootCauseInstanceOf(ZoneRulesException.class)
.hasRootCauseMessage("Unknown time-zone ID: Asia/NOT_FOUND");
}

private static void assertUriPortScheme(TrinoDriverUri parameters, int port, String scheme)
{
URI uri = parameters.getHttpUri();
Expand Down
4 changes: 4 additions & 0 deletions docs/src/main/sphinx/client/jdbc.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,4 +248,8 @@ may not be specified using both methods.
treated as underscores. You can use this as a workaround for
applications that do not escape schema or table names when passing them
to ``DatabaseMetaData`` methods as schema or table name patterns.
* - ``timezone``
- Sets the time zone for the session using the `time zone passed
<https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/ZoneId.html#of(java.lang.String)>`_. Defaults
to the timezone of the JVM running the JDBC driver.
```