From c0402ba4c9761ac584d3cfaa3a7fa7e5f3070cf3 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 6 Apr 2026 12:54:10 +0530 Subject: [PATCH 01/21] HIVE-28265: Fix JDBC timeout message for hive.query.timeout.seconds Use server-side HiveSQLException with effective timeout seconds before cancel(TIMEDOUT) so GetOperationStatus exposes correct errorMessage. JDBC: prefer server message; ignore bogus 'after 0 seconds'; fall back to Statement queryTimeout or last SET hive.query.timeout.seconds tracked on HiveConnection. Parse SET assignments anywhere in SQL (last wins). SQLOperation async: do not overwrite operationException when already TIMEDOUT. Tests: TestJdbcDriver2 (session SET + sleep UDF; tighten testQueryTimeout). Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 35 ++++++++++++ .../org/apache/hive/jdbc/HiveConnection.java | 22 ++++++++ .../org/apache/hive/jdbc/HiveStatement.java | 53 ++++++++++++++++++- .../service/cli/operation/SQLOperation.java | 13 ++++- 4 files changed, 119 insertions(+), 4 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 6947b0b4b88b..85a477632288 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -2662,6 +2662,10 @@ public void testQueryTimeout() throws Exception { fail("Expecting SQLTimeoutException"); } catch (SQLTimeoutException e) { assertNotNull(e); + assertTrue("Message should reflect JDBC query timeout (1s): " + e.getMessage(), + e.getMessage().contains("1")); + assertFalse("Message should not claim 0 seconds: " + e.getMessage(), + e.getMessage().contains("after 0 seconds")); System.err.println(e.toString()); } catch (SQLException e) { fail("Expecting SQLTimeoutException, but got SQLException: " + e); @@ -2680,6 +2684,37 @@ public void testQueryTimeout() throws Exception { stmt.close(); } + /** + * HIVE-28265: hive.query.timeout.seconds drives the server-side timer, but the JDBC client + * must not report "0 seconds" when Statement#setQueryTimeout was not used. + */ + @Test + public void testQueryTimeoutMessageUsesHiveConf() throws Exception { + String udfName = SleepMsUDF.class.getName(); + Statement stmt1 = con.createStatement(); + stmt1.execute("create temporary function sleepMsUDF as '" + udfName + "'"); + stmt1.close(); + + Statement stmt = con.createStatement(); + stmt.execute("set hive.query.timeout.seconds=1s"); + + try { + stmt.executeQuery("select sleepMsUDF(t1.under_col, 5) as u0, t1.under_col as u1, " + + "t2.under_col as u2 from " + tableName + " t1 join " + tableName + + " t2 on t1.under_col = t2.under_col"); + fail("Expecting SQLTimeoutException"); + } catch (SQLTimeoutException e) { + assertNotNull(e); + assertTrue("Message should include session timeout (1s): " + e.getMessage(), + e.getMessage().contains("1")); + assertFalse("Message should not claim 0 seconds (HIVE-28265): " + e.getMessage(), + e.getMessage().contains("after 0 seconds")); + } catch (SQLException e) { + fail("Expecting SQLTimeoutException, but got SQLException: " + e); + } + stmt.close(); + } + /** * Test the non-null value of the Yarn ATS GUID. * We spawn 2 threads - one running the query and diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index 8f7c3ea8acd4..c5e2ee4f00ff 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -155,6 +155,7 @@ import org.apache.thrift.transport.TTransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; /** @@ -163,6 +164,12 @@ */ public class HiveConnection implements java.sql.Connection { private static final Logger LOG = LoggerFactory.getLogger(HiveConnection.class); + + /** + * Sentinel: no {@code SET hive.query.timeout.seconds} has been observed on this connection yet. + */ + static final long SESSION_QUERY_TIMEOUT_NOT_TRACKED = -1L; + private final AtomicLong sessionQueryTimeoutSeconds = new AtomicLong(SESSION_QUERY_TIMEOUT_NOT_TRACKED); private String jdbcUriString; private String host; private int port; @@ -190,6 +197,21 @@ public class HiveConnection implements java.sql.Connection { public TCLIService.Iface getClient() { return client; } + /** + * Records the effective {@code hive.query.timeout.seconds} (in seconds) after a successful + * {@code SET hive.query.timeout.seconds=...} on this connection. Used for JDBC timeout messages. + */ + void recordSessionQueryTimeoutFromSet(long seconds) { + sessionQueryTimeoutSeconds.set(seconds); + } + + /** + * @return seconds from the last client-tracked SET, or {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED} if none + */ + long getSessionQueryTimeoutSecondsTracked() { + return sessionQueryTimeoutSeconds.get(); + } + /** * Get all direct HiveServer2 URLs from a ZooKeeper based HiveServer2 URL * @param zookeeperBasedHS2Url diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index aba982670acb..a8ea00e314e5 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -19,6 +19,7 @@ package org.apache.hive.jdbc; import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.common.classification.InterfaceAudience.LimitedPrivate; import org.apache.hive.jdbc.logs.InPlaceUpdateStream; import org.apache.hive.service.cli.RowSet; @@ -57,6 +58,9 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.apache.hadoop.hive.ql.ErrorMsg.CLIENT_POLLING_OPSTATUS_INTERRUPTED; @@ -70,6 +74,10 @@ public class HiveStatement implements java.sql.Statement { public static final String QUERY_CANCELLED_MESSAGE = "Query was cancelled."; + /** Last assignment wins if multiple appear (e.g. multi-line script). Uses find(), not full-string match. */ + private static final Pattern SET_HIVE_QUERY_TIMEOUT_SECONDS = Pattern.compile( + "(?i)set\\s+hive\\.query\\.timeout\\.seconds\\s*=\\s*([^;\\n]+)"); + private final HiveConnection connection; private TCLIService.Iface client; private Optional stmtHandle; @@ -298,6 +306,7 @@ public void closeOnCompletion() throws SQLException { public boolean execute(String sql) throws SQLException { runAsyncOnServer(sql); TGetOperationStatusResp status = waitForOperationToComplete(); + trackSessionQueryTimeoutIfSet(sql); // The query should be completed by now if (!status.isHasResultSet() && stmtHandle.isPresent() && !stmtHandle.get().isHasResultSet()) { @@ -398,6 +407,32 @@ private TGetOperationStatusResp waitForResultSetStatus() throws SQLException { return statusResp; } + /** + * When {@code SET hive.query.timeout.seconds=...} succeeds, remember the effective value on the + * connection so {@code TIMEDOUT_STATE} can report it if the server omits {@code errorMessage} + * (HIVE-28265). + */ + private void trackSessionQueryTimeoutIfSet(String sql) { + if (sql == null) { + return; + } + Matcher m = SET_HIVE_QUERY_TIMEOUT_SECONDS.matcher(sql); + Long lastSec = null; + while (m.find()) { + try { + HiveConf conf = new HiveConf(); + conf.set(HiveConf.ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname, m.group(1).trim()); + long sec = HiveConf.getTimeVar(conf, HiveConf.ConfVars.HIVE_QUERY_TIMEOUT_SECONDS, TimeUnit.SECONDS); + lastSec = sec; + } catch (Exception e) { + LOG.debug("Could not parse session query timeout fragment: {}", m.group(0), e); + } + } + if (lastSec != null) { + connection.recordSessionQueryTimeoutFromSet(lastSec); + } + } + TGetOperationStatusResp waitForOperationToComplete() throws SQLException { TGetOperationStatusResp statusResp = null; @@ -441,8 +476,22 @@ TGetOperationStatusResp waitForOperationToComplete() throws SQLException { final String fullErrMsg = (errMsg == null || errMsg.isEmpty()) ? QUERY_CANCELLED_MESSAGE : QUERY_CANCELLED_MESSAGE + " " + errMsg; throw new SQLException(fullErrMsg, "01000"); - case TIMEDOUT_STATE: - throw new SQLTimeoutException("Query timed out after " + queryTimeout + " seconds"); + case TIMEDOUT_STATE: { + String timeoutMsg = statusResp.getErrorMessage(); + // HIVE-28265: ignore blank or known-broken "0 seconds" from mismatched/old peers; rebuild + // from JDBC timeout or last SET hive.query.timeout.seconds on this connection. + boolean needLocalMessage = StringUtils.isBlank(timeoutMsg) + || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); + if (needLocalMessage) { + long tracked = connection.getSessionQueryTimeoutSecondsTracked(); + long effectiveSec = queryTimeout > 0 ? queryTimeout + : (tracked > 0 ? tracked : 0); + timeoutMsg = effectiveSec > 0 + ? "Query timed out after " + effectiveSec + " seconds" + : "Query timed out"; + } + throw new SQLTimeoutException(timeoutMsg); + } case ERROR_STATE: // Get the error details from the underlying exception throw new SQLException(statusResp.getErrorMessage(), statusResp.getSqlState(), diff --git a/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java b/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java index 5f781a3bbb66..73e4637e48b2 100644 --- a/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java +++ b/service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java @@ -179,6 +179,11 @@ private void prepare(QueryState queryState) throws HiveSQLException { try { final String queryId = queryState.getQueryId(); log.info("Query timed out after: {} seconds. Cancelling the execution now: {}", queryTimeout, queryId); + setOperationException(new HiveSQLException( + "Query timed out after " + queryTimeout + " seconds", + "HYT00", + 0, + queryId)); SQLOperation.this.cancel(OperationState.TIMEDOUT); } catch (HiveSQLException e) { log.error("Error cancelling the query after timeout: {} seconds", queryTimeout, e); @@ -334,7 +339,9 @@ public Object run() throws HiveSQLException { runQuery(); } catch (HiveSQLException e) { // TODO: why do we invent our own error path op top of the one from Future.get? - setOperationException(e); + if (getState() != OperationState.TIMEDOUT) { + setOperationException(e); + } log.error("Error running hive query", e); } finally { if (!embedded) { @@ -353,7 +360,9 @@ public Object run() throws HiveSQLException { try { currentUGI.doAs(doAsAction); } catch (Exception e) { - setOperationException(new HiveSQLException(e)); + if (getState() != OperationState.TIMEDOUT) { + setOperationException(new HiveSQLException(e)); + } log.error("Error running hive query as user : {}", currentUGI.getShortUserName(), e); } finally { /** From dd4d51ace4a6e6421e1ceacdee9adcf5c8f5dbe5 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 6 Apr 2026 16:28:25 +0530 Subject: [PATCH 02/21] HIVE-28265: Refactor HiveStatement timeout handling; reset session timeout in tests - Extract TIMEDOUT/CANCELED handling into helpers to satisfy Sonar (complexity, nested blocks, nested ternary). - Add @After in TestJdbcDriver2 to run SET hive.query.timeout.seconds=0s on the shared connection so testQueryTimeoutMessageUsesHiveConf does not leave a 1s server-side limit for other tests (fixes Jenkins SQLTimeoutException cascades). Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 20 ++++++ .../org/apache/hive/jdbc/HiveStatement.java | 62 ++++++++++++------- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 85a477632288..7102422fc922 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -42,6 +42,7 @@ import org.apache.hive.service.cli.operation.ClassicTableTypeMapping.ClassicTableTypes; import org.apache.hive.service.cli.operation.HiveTableTypeMapping; import org.apache.hive.service.cli.operation.TableTypeMappingFactory.TableTypeMappings; +import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; @@ -131,6 +132,25 @@ public class TestJdbcDriver2 { @Rule public ExpectedException thrown = ExpectedException.none(); @Rule public final TestName testName = new TestName(); + /** + * {@code SET hive.query.timeout.seconds} applies to the whole HS2 session. Tests such as + * {@link #testQueryTimeoutMessageUsesHiveConf()} must not leave a short limit on the shared + * {@link #con}, or unrelated tests will see {@link SQLTimeoutException}. + */ + @After + public void resetHiveSessionQueryTimeout() { + try { + if (con == null || con.isClosed()) { + return; + } + try (Statement st = con.createStatement()) { + st.execute("set hive.query.timeout.seconds=0s"); + } + } catch (SQLException e) { + LOG.warn("Could not reset hive.query.timeout.seconds after {}", testName.getMethodName(), e); + } + } + private static Connection getConnection(String prefix, String postfix) throws SQLException { Connection con1; String connString = "jdbc:hive2:///" + prefix + "?" + conf.getOverlayOptionsAsQueryString() diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index a8ea00e314e5..7b81c2bd2885 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -433,6 +433,44 @@ private void trackSessionQueryTimeoutIfSet(String sql) { } } + /** + * HIVE-28265: Prefer server error text unless it is empty or the known-broken "0 seconds" case; + * otherwise derive seconds from JDBC {@link #setQueryTimeout(int)} or last session SET. + */ + private String sqlTimeoutMessageForTimedOutState(String serverMessage) { + if (!needsLocalTimeoutMessageForTimedOut(serverMessage)) { + return serverMessage; + } + long effectiveSec = resolveEffectiveTimeoutSecondsForMessage(); + if (effectiveSec > 0) { + return "Query timed out after " + effectiveSec + " seconds"; + } + return "Query timed out"; + } + + private boolean needsLocalTimeoutMessageForTimedOut(String timeoutMsg) { + return StringUtils.isBlank(timeoutMsg) + || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); + } + + private long resolveEffectiveTimeoutSecondsForMessage() { + if (queryTimeout > 0) { + return queryTimeout; + } + long tracked = connection.getSessionQueryTimeoutSecondsTracked(); + if (tracked > 0) { + return tracked; + } + return 0L; + } + + private SQLException sqlExceptionForCanceledState(TGetOperationStatusResp statusResp) { + final String errMsg = statusResp.getErrorMessage(); + final String fullErrMsg = + (errMsg == null || errMsg.isEmpty()) ? QUERY_CANCELLED_MESSAGE : QUERY_CANCELLED_MESSAGE + " " + errMsg; + return new SQLException(fullErrMsg, "01000"); + } + TGetOperationStatusResp waitForOperationToComplete() throws SQLException { TGetOperationStatusResp statusResp = null; @@ -471,27 +509,9 @@ TGetOperationStatusResp waitForOperationToComplete() throws SQLException { isLogBeingGenerated = false; break; case CANCELED_STATE: - // 01000 -> warning - final String errMsg = statusResp.getErrorMessage(); - final String fullErrMsg = - (errMsg == null || errMsg.isEmpty()) ? QUERY_CANCELLED_MESSAGE : QUERY_CANCELLED_MESSAGE + " " + errMsg; - throw new SQLException(fullErrMsg, "01000"); - case TIMEDOUT_STATE: { - String timeoutMsg = statusResp.getErrorMessage(); - // HIVE-28265: ignore blank or known-broken "0 seconds" from mismatched/old peers; rebuild - // from JDBC timeout or last SET hive.query.timeout.seconds on this connection. - boolean needLocalMessage = StringUtils.isBlank(timeoutMsg) - || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); - if (needLocalMessage) { - long tracked = connection.getSessionQueryTimeoutSecondsTracked(); - long effectiveSec = queryTimeout > 0 ? queryTimeout - : (tracked > 0 ? tracked : 0); - timeoutMsg = effectiveSec > 0 - ? "Query timed out after " + effectiveSec + " seconds" - : "Query timed out"; - } - throw new SQLTimeoutException(timeoutMsg); - } + throw sqlExceptionForCanceledState(statusResp); + case TIMEDOUT_STATE: + throw new SQLTimeoutException(sqlTimeoutMessageForTimedOutState(statusResp.getErrorMessage())); case ERROR_STATE: // Get the error details from the underlying exception throw new SQLException(statusResp.getErrorMessage(), statusResp.getSqlState(), From d675ed18e64dddba9c9d716f326a96c2ded87351 Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 7 Apr 2026 13:09:54 +0530 Subject: [PATCH 03/21] HIVE-28265: Tighten query timeout message assertions in TestJdbcDriver2 Use a regex for 'timed out after 1 seconds' instead of contains("1") to avoid false positives (e.g. 10-second timeouts). Made-with: Cursor --- .../java/org/apache/hive/jdbc/TestJdbcDriver2.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 7102422fc922..183ba661f1bf 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -151,6 +151,14 @@ public void resetHiveSessionQueryTimeout() { } } + /** + * HS2 / {@code HiveStatement} report timeouts as {@code ...timed out after N seconds...}; match + * {@code N == 1} with flexible whitespace so we do not treat {@code 10} or unrelated digits as {@code 1}. + */ + private static boolean isQueryTimedOutAfterOneSecondMessage(String msg) { + return msg != null && msg.matches("(?is).*timed out after\\s+1\\s+seconds.*"); + } + private static Connection getConnection(String prefix, String postfix) throws SQLException { Connection con1; String connString = "jdbc:hive2:///" + prefix + "?" + conf.getOverlayOptionsAsQueryString() @@ -2683,7 +2691,7 @@ public void testQueryTimeout() throws Exception { } catch (SQLTimeoutException e) { assertNotNull(e); assertTrue("Message should reflect JDBC query timeout (1s): " + e.getMessage(), - e.getMessage().contains("1")); + isQueryTimedOutAfterOneSecondMessage(e.getMessage())); assertFalse("Message should not claim 0 seconds: " + e.getMessage(), e.getMessage().contains("after 0 seconds")); System.err.println(e.toString()); @@ -2726,7 +2734,7 @@ public void testQueryTimeoutMessageUsesHiveConf() throws Exception { } catch (SQLTimeoutException e) { assertNotNull(e); assertTrue("Message should include session timeout (1s): " + e.getMessage(), - e.getMessage().contains("1")); + isQueryTimedOutAfterOneSecondMessage(e.getMessage())); assertFalse("Message should not claim 0 seconds (HIVE-28265): " + e.getMessage(), e.getMessage().contains("after 0 seconds")); } catch (SQLException e) { From 06edfccc0cae9c1cba716c591877116e01ecedd7 Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 7 Apr 2026 16:56:19 +0530 Subject: [PATCH 04/21] HIVE-28265: Address JDBC PR review (assertEquals, setter names, docs) - TestJdbcDriver2: assert exact 'Query timed out after 1 seconds'; drop redundant assertNotNull/assertFalse; keep HIVE-28265 context in Javadoc not assert text - HiveConnection: AtomicLong Javadoc for concurrent statements; rename to setSessionQueryTimeoutSeconds / getSessionQueryTimeoutSeconds - HiveStatement: update call sites Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 42 ++++++++++--------- .../org/apache/hive/jdbc/HiveConnection.java | 12 ++++-- .../org/apache/hive/jdbc/HiveStatement.java | 4 +- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 183ba661f1bf..57fe52c6e22d 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -129,6 +129,13 @@ public class TestJdbcDriver2 { private static Connection con; private static final float floatCompareDelta = 0.0001f; + /** + * Exact {@link SQLTimeoutException#getMessage()} for a 1s limit from HS2 / client; see + * {@code SQLOperation} and {@code HiveStatement#sqlTimeoutMessageForTimedOutState} (no query id + * or timestamp is appended to this text). + */ + private static final String QUERY_TIMED_OUT_AFTER_1_SECONDS = "Query timed out after 1 seconds"; + @Rule public ExpectedException thrown = ExpectedException.none(); @Rule public final TestName testName = new TestName(); @@ -151,14 +158,6 @@ public void resetHiveSessionQueryTimeout() { } } - /** - * HS2 / {@code HiveStatement} report timeouts as {@code ...timed out after N seconds...}; match - * {@code N == 1} with flexible whitespace so we do not treat {@code 10} or unrelated digits as {@code 1}. - */ - private static boolean isQueryTimedOutAfterOneSecondMessage(String msg) { - return msg != null && msg.matches("(?is).*timed out after\\s+1\\s+seconds.*"); - } - private static Connection getConnection(String prefix, String postfix) throws SQLException { Connection con1; String connString = "jdbc:hive2:///" + prefix + "?" + conf.getOverlayOptionsAsQueryString() @@ -2689,11 +2688,11 @@ public void testQueryTimeout() throws Exception { + " t2 on t1.under_col = t2.under_col"); fail("Expecting SQLTimeoutException"); } catch (SQLTimeoutException e) { - assertNotNull(e); - assertTrue("Message should reflect JDBC query timeout (1s): " + e.getMessage(), - isQueryTimedOutAfterOneSecondMessage(e.getMessage())); - assertFalse("Message should not claim 0 seconds: " + e.getMessage(), - e.getMessage().contains("after 0 seconds")); + assertEquals( + "JDBC query timeout (1s) should match HS2/HiveStatement text, e.g. " + + QUERY_TIMED_OUT_AFTER_1_SECONDS, + QUERY_TIMED_OUT_AFTER_1_SECONDS, + e.getMessage()); System.err.println(e.toString()); } catch (SQLException e) { fail("Expecting SQLTimeoutException, but got SQLException: " + e); @@ -2713,8 +2712,11 @@ public void testQueryTimeout() throws Exception { } /** - * HIVE-28265: hive.query.timeout.seconds drives the server-side timer, but the JDBC client - * must not report "0 seconds" when Statement#setQueryTimeout was not used. + * When only {@code hive.query.timeout.seconds} applies (no {@link Statement#setQueryTimeout(int)}), + * the client must still report the real limit in {@link SQLTimeoutException#getMessage()} (before + * HIVE-28265 some paths wrongly showed "after 0 seconds"). Expected full message: + * {@link #QUERY_TIMED_OUT_AFTER_1_SECONDS} — same string as HS2 uses, with no query id or host + * suffix. */ @Test public void testQueryTimeoutMessageUsesHiveConf() throws Exception { @@ -2732,11 +2734,11 @@ public void testQueryTimeoutMessageUsesHiveConf() throws Exception { + " t2 on t1.under_col = t2.under_col"); fail("Expecting SQLTimeoutException"); } catch (SQLTimeoutException e) { - assertNotNull(e); - assertTrue("Message should include session timeout (1s): " + e.getMessage(), - isQueryTimedOutAfterOneSecondMessage(e.getMessage())); - assertFalse("Message should not claim 0 seconds (HIVE-28265): " + e.getMessage(), - e.getMessage().contains("after 0 seconds")); + assertEquals( + "Session query timeout (1s) should match HS2/HiveStatement text, e.g. " + + QUERY_TIMED_OUT_AFTER_1_SECONDS, + QUERY_TIMED_OUT_AFTER_1_SECONDS, + e.getMessage()); } catch (SQLException e) { fail("Expecting SQLTimeoutException, but got SQLException: " + e); } diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index c5e2ee4f00ff..28000675c37a 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -169,6 +169,12 @@ public class HiveConnection implements java.sql.Connection { * Sentinel: no {@code SET hive.query.timeout.seconds} has been observed on this connection yet. */ static final long SESSION_QUERY_TIMEOUT_NOT_TRACKED = -1L; + /** + * Last effective {@code hive.query.timeout.seconds} from a client {@code SET} (seconds), or + * {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED}. A JDBC {@code Connection} may be shared across threads + * with concurrent {@link org.apache.hive.jdbc.HiveStatement}s on one HS2 session; this field uses an + * {@link AtomicLong} so updates remain well-defined (last SET wins). + */ private final AtomicLong sessionQueryTimeoutSeconds = new AtomicLong(SESSION_QUERY_TIMEOUT_NOT_TRACKED); private String jdbcUriString; private String host; @@ -198,17 +204,17 @@ public class HiveConnection implements java.sql.Connection { public TCLIService.Iface getClient() { return client; } /** - * Records the effective {@code hive.query.timeout.seconds} (in seconds) after a successful + * Sets the effective {@code hive.query.timeout.seconds} (in seconds) after a successful * {@code SET hive.query.timeout.seconds=...} on this connection. Used for JDBC timeout messages. */ - void recordSessionQueryTimeoutFromSet(long seconds) { + void setSessionQueryTimeoutSeconds(long seconds) { sessionQueryTimeoutSeconds.set(seconds); } /** * @return seconds from the last client-tracked SET, or {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED} if none */ - long getSessionQueryTimeoutSecondsTracked() { + long getSessionQueryTimeoutSeconds() { return sessionQueryTimeoutSeconds.get(); } diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index 7b81c2bd2885..af64ec495b83 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -429,7 +429,7 @@ private void trackSessionQueryTimeoutIfSet(String sql) { } } if (lastSec != null) { - connection.recordSessionQueryTimeoutFromSet(lastSec); + connection.setSessionQueryTimeoutSeconds(lastSec); } } @@ -457,7 +457,7 @@ private long resolveEffectiveTimeoutSecondsForMessage() { if (queryTimeout > 0) { return queryTimeout; } - long tracked = connection.getSessionQueryTimeoutSecondsTracked(); + long tracked = connection.getSessionQueryTimeoutSeconds(); if (tracked > 0) { return tracked; } From c8bb6bb86cc1d31c1d73b90a4b88c8ea4d154253 Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 7 Apr 2026 21:31:45 +0530 Subject: [PATCH 05/21] HIVE-28265: Strip Query ID suffix from JDBC SQLTimeoutException message HiveSQLException appends '; Query ID: ...' to getMessage(). When the client passes through the server timeout text (non-broken path), SQLTimeoutException included that suffix and TestJdbcDriver2 exact assertions failed on CI. Strip the same trailer the server uses before returning the message. Made-with: Cursor --- .../org/apache/hive/jdbc/HiveStatement.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index af64ec495b83..8065e017cf5b 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -22,6 +22,7 @@ import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.common.classification.InterfaceAudience.LimitedPrivate; import org.apache.hive.jdbc.logs.InPlaceUpdateStream; +import org.apache.hive.service.cli.HiveSQLException; import org.apache.hive.service.cli.RowSet; import org.apache.hive.service.cli.RowSetFactory; import org.apache.hive.service.rpc.thrift.TCLIService; @@ -439,7 +440,7 @@ private void trackSessionQueryTimeoutIfSet(String sql) { */ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { if (!needsLocalTimeoutMessageForTimedOut(serverMessage)) { - return serverMessage; + return stripTrailingQueryIdSuffix(serverMessage); } long effectiveSec = resolveEffectiveTimeoutSecondsForMessage(); if (effectiveSec > 0) { @@ -448,6 +449,20 @@ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { return "Query timed out"; } + /** + * HS2 wraps many errors via {@link HiveSQLException#getMessage()}, which appends + * {@code ; Query ID: ...}. Strip that trailer so JDBC timeout text stays stable and matches the + * base HS2 wording (HIVE-28265). + */ + private static String stripTrailingQueryIdSuffix(String msg) { + if (msg == null) { + return null; + } + String marker = "; " + HiveSQLException.QUERY_ID + ": "; + int idx = msg.indexOf(marker); + return idx >= 0 ? msg.substring(0, idx) : msg; + } + private boolean needsLocalTimeoutMessageForTimedOut(String timeoutMsg) { return StringUtils.isBlank(timeoutMsg) || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); From 1ab85073511d3382420370c5f62a2810d2154149 Mon Sep 17 00:00:00 2001 From: akpatra Date: Fri, 10 Apr 2026 14:43:09 +0530 Subject: [PATCH 06/21] HIVE-28265: Keep full HS2 timeout message; relax JDBC tests for Query ID suffix - HiveStatement: stop stripping '; Query ID;' from server timeout text; pass through when server message is usable (reverts strip-only follow-up). - TestJdbcDriver2: assert message starts with expected timeout text and does not contain 'after 0 seconds' (HIVE-28265), allowing HS2/HiveSQLException suffix. Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 36 ++++++++++--------- .../org/apache/hive/jdbc/HiveStatement.java | 17 +-------- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 57fe52c6e22d..749be4ffc713 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -130,9 +130,8 @@ public class TestJdbcDriver2 { private static final float floatCompareDelta = 0.0001f; /** - * Exact {@link SQLTimeoutException#getMessage()} for a 1s limit from HS2 / client; see - * {@code SQLOperation} and {@code HiveStatement#sqlTimeoutMessageForTimedOutState} (no query id - * or timestamp is appended to this text). + * Required prefix of {@link SQLTimeoutException#getMessage()} for a 1s limit. HS2 may append + * {@code ; Query ID: ...} after the base text from {@code HiveSQLException}. */ private static final String QUERY_TIMED_OUT_AFTER_1_SECONDS = "Query timed out after 1 seconds"; @@ -158,6 +157,18 @@ public void resetHiveSessionQueryTimeout() { } } + private static void assertTimeoutMessageShowsOneSecond(String context, SQLTimeoutException e) { + String msg = e.getMessage(); + assertNotNull(context + ": message should not be null", msg); + assertTrue( + context + ": should start with " + QUERY_TIMED_OUT_AFTER_1_SECONDS + + " (HS2 may append ; Query ID: ...); actual=" + msg, + msg.startsWith(QUERY_TIMED_OUT_AFTER_1_SECONDS)); + assertFalse( + "HIVE-28265: message should not claim 0 seconds: " + msg, + msg.contains("after 0 seconds")); + } + private static Connection getConnection(String prefix, String postfix) throws SQLException { Connection con1; String connString = "jdbc:hive2:///" + prefix + "?" + conf.getOverlayOptionsAsQueryString() @@ -2688,11 +2699,8 @@ public void testQueryTimeout() throws Exception { + " t2 on t1.under_col = t2.under_col"); fail("Expecting SQLTimeoutException"); } catch (SQLTimeoutException e) { - assertEquals( - "JDBC query timeout (1s) should match HS2/HiveStatement text, e.g. " - + QUERY_TIMED_OUT_AFTER_1_SECONDS, - QUERY_TIMED_OUT_AFTER_1_SECONDS, - e.getMessage()); + assertTimeoutMessageShowsOneSecond( + "JDBC query timeout (1s)", e); System.err.println(e.toString()); } catch (SQLException e) { fail("Expecting SQLTimeoutException, but got SQLException: " + e); @@ -2714,9 +2722,8 @@ public void testQueryTimeout() throws Exception { /** * When only {@code hive.query.timeout.seconds} applies (no {@link Statement#setQueryTimeout(int)}), * the client must still report the real limit in {@link SQLTimeoutException#getMessage()} (before - * HIVE-28265 some paths wrongly showed "after 0 seconds"). Expected full message: - * {@link #QUERY_TIMED_OUT_AFTER_1_SECONDS} — same string as HS2 uses, with no query id or host - * suffix. + * HIVE-28265 some paths wrongly showed "after 0 seconds"). Message must begin with + * {@link #QUERY_TIMED_OUT_AFTER_1_SECONDS}; HS2 may append {@code ; Query ID: ...}. */ @Test public void testQueryTimeoutMessageUsesHiveConf() throws Exception { @@ -2734,11 +2741,8 @@ public void testQueryTimeoutMessageUsesHiveConf() throws Exception { + " t2 on t1.under_col = t2.under_col"); fail("Expecting SQLTimeoutException"); } catch (SQLTimeoutException e) { - assertEquals( - "Session query timeout (1s) should match HS2/HiveStatement text, e.g. " - + QUERY_TIMED_OUT_AFTER_1_SECONDS, - QUERY_TIMED_OUT_AFTER_1_SECONDS, - e.getMessage()); + assertTimeoutMessageShowsOneSecond( + "Session query timeout (1s)", e); } catch (SQLException e) { fail("Expecting SQLTimeoutException, but got SQLException: " + e); } diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index 8065e017cf5b..af64ec495b83 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -22,7 +22,6 @@ import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.common.classification.InterfaceAudience.LimitedPrivate; import org.apache.hive.jdbc.logs.InPlaceUpdateStream; -import org.apache.hive.service.cli.HiveSQLException; import org.apache.hive.service.cli.RowSet; import org.apache.hive.service.cli.RowSetFactory; import org.apache.hive.service.rpc.thrift.TCLIService; @@ -440,7 +439,7 @@ private void trackSessionQueryTimeoutIfSet(String sql) { */ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { if (!needsLocalTimeoutMessageForTimedOut(serverMessage)) { - return stripTrailingQueryIdSuffix(serverMessage); + return serverMessage; } long effectiveSec = resolveEffectiveTimeoutSecondsForMessage(); if (effectiveSec > 0) { @@ -449,20 +448,6 @@ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { return "Query timed out"; } - /** - * HS2 wraps many errors via {@link HiveSQLException#getMessage()}, which appends - * {@code ; Query ID: ...}. Strip that trailer so JDBC timeout text stays stable and matches the - * base HS2 wording (HIVE-28265). - */ - private static String stripTrailingQueryIdSuffix(String msg) { - if (msg == null) { - return null; - } - String marker = "; " + HiveSQLException.QUERY_ID + ": "; - int idx = msg.indexOf(marker); - return idx >= 0 ? msg.substring(0, idx) : msg; - } - private boolean needsLocalTimeoutMessageForTimedOut(String timeoutMsg) { return StringUtils.isBlank(timeoutMsg) || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); From ad53984f22437289a8da18ab969c509d893175d7 Mon Sep 17 00:00:00 2001 From: akpatra Date: Fri, 10 Apr 2026 17:54:54 +0530 Subject: [PATCH 07/21] HIVE-28265: Refactor HiveStatement for Sonar (not all Sonar PR issues) - sqlExceptionForCanceledState: replace ternary with if/else (S3358). - Extract processOperationStatusResponse from waitForOperationToComplete (S6541). - Use local progressUpdates flag to simplify nested flow (AvoidNestedBlocks). Does not address hundreds of repo-wide Sonar findings outside this file. Made-with: Cursor --- .../org/apache/hive/jdbc/HiveStatement.java | 79 +++++++++++-------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index af64ec495b83..2ab21be0be87 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -466,25 +466,61 @@ private long resolveEffectiveTimeoutSecondsForMessage() { private SQLException sqlExceptionForCanceledState(TGetOperationStatusResp statusResp) { final String errMsg = statusResp.getErrorMessage(); - final String fullErrMsg = - (errMsg == null || errMsg.isEmpty()) ? QUERY_CANCELLED_MESSAGE : QUERY_CANCELLED_MESSAGE + " " + errMsg; + final String fullErrMsg; + if (errMsg == null || errMsg.isEmpty()) { + fullErrMsg = QUERY_CANCELLED_MESSAGE; + } else { + fullErrMsg = QUERY_CANCELLED_MESSAGE + " " + errMsg; + } return new SQLException(fullErrMsg, "01000"); } + /** + * One GetOperationStatus response: progress update, Thrift status check, then terminal states. + * Extracted to keep {@link #waitForOperationToComplete()} smaller for static analysis (Sonar). + */ + private void processOperationStatusResponse(TGetOperationStatusResp statusResp) throws SQLException { + if (!isOperationComplete && inPlaceUpdateStream.isPresent()) { + inPlaceUpdateStream.get().update(statusResp.getProgressUpdateResponse()); + } + Utils.verifySuccessWithInfo(statusResp.getStatus()); + if (!statusResp.isSetOperationState()) { + return; + } + switch (statusResp.getOperationState()) { + case CLOSED_STATE: + case FINISHED_STATE: + isOperationComplete = true; + isLogBeingGenerated = false; + break; + case CANCELED_STATE: + throw sqlExceptionForCanceledState(statusResp); + case TIMEDOUT_STATE: + throw new SQLTimeoutException(sqlTimeoutMessageForTimedOutState(statusResp.getErrorMessage())); + case ERROR_STATE: + throw new SQLException(statusResp.getErrorMessage(), statusResp.getSqlState(), statusResp.getErrorCode()); + case UKNOWN_STATE: + throw new SQLException("Unknown query", "HY000"); + case INITIALIZED_STATE: + case PENDING_STATE: + case RUNNING_STATE: + break; + } + } + TGetOperationStatusResp waitForOperationToComplete() throws SQLException { TGetOperationStatusResp statusResp = null; final TGetOperationStatusReq statusReq = new TGetOperationStatusReq(stmtHandle.get()); - statusReq.setGetProgressUpdate(inPlaceUpdateStream.isPresent()); + boolean progressUpdates = inPlaceUpdateStream.isPresent(); + statusReq.setGetProgressUpdate(progressUpdates); - // Progress bar is completed if there is nothing to request - if (inPlaceUpdateStream.isPresent()) { + if (progressUpdates) { inPlaceUpdateStream.get().getEventNotifier().progressBarCompleted(); } LOG.debug("Waiting on operation to complete: Polling operation status"); - // Poll on the operation status, till the operation is complete do { try { if (Thread.currentThread().isInterrupted()) { @@ -497,33 +533,7 @@ TGetOperationStatusResp waitForOperationToComplete() throws SQLException { */ statusResp = client.GetOperationStatus(statusReq); LOG.debug("Status response: {}", statusResp); - if (!isOperationComplete && inPlaceUpdateStream.isPresent()) { - inPlaceUpdateStream.get().update(statusResp.getProgressUpdateResponse()); - } - Utils.verifySuccessWithInfo(statusResp.getStatus()); - if (statusResp.isSetOperationState()) { - switch (statusResp.getOperationState()) { - case CLOSED_STATE: - case FINISHED_STATE: - isOperationComplete = true; - isLogBeingGenerated = false; - break; - case CANCELED_STATE: - throw sqlExceptionForCanceledState(statusResp); - case TIMEDOUT_STATE: - throw new SQLTimeoutException(sqlTimeoutMessageForTimedOutState(statusResp.getErrorMessage())); - case ERROR_STATE: - // Get the error details from the underlying exception - throw new SQLException(statusResp.getErrorMessage(), statusResp.getSqlState(), - statusResp.getErrorCode()); - case UKNOWN_STATE: - throw new SQLException("Unknown query", "HY000"); - case INITIALIZED_STATE: - case PENDING_STATE: - case RUNNING_STATE: - break; - } - } + processOperationStatusResponse(statusResp); } catch (SQLException e) { isLogBeingGenerated = false; throw e; @@ -533,8 +543,7 @@ TGetOperationStatusResp waitForOperationToComplete() throws SQLException { } } while (!isOperationComplete); - // set progress bar to be completed when hive query execution has completed - if (inPlaceUpdateStream.isPresent()) { + if (progressUpdates) { inPlaceUpdateStream.get().getEventNotifier().progressBarCompleted(); } return statusResp; From 126310b8d0dcfd5630c86eecad10a5417213370d Mon Sep 17 00:00:00 2001 From: akpatra Date: Sat, 11 Apr 2026 12:58:57 +0530 Subject: [PATCH 08/21] HIVE-28265: Checkstyle SKIPPED_ATTEMPTS; fix Beeline/llap precommit tests - Rename AtomicInteger counter to SKIPPED_ATTEMPTS (ConstantNameCheck). - Drop unused partitionDiscoveryEnabled again (revert commit restores history). - testQueryProgress: accept ELAPSED TIME, Beeline row timing, or Driver Time taken. - llap_io_cache: use 8MiB RPAD payload to avoid Parquet logging OOM on CI. Made-with: Cursor --- .../hive/beeline/TestBeeLineWithArgs.java | 5 +-- .../queries/clientpositive/llap_io_cache.q | 2 +- .../clientpositive/llap/llap_io_cache.q.out | 8 ++--- .../metastore/PartitionManagementTask.java | 32 +++++++++++++++---- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java index ffb9ab1224af..3a61ae5c4ef9 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java @@ -789,8 +789,9 @@ public void testQueryProgress() throws Throwable { "set hive.support.concurrency = false;\n" + "set hive.server2.logging.operation.level=execution;\n" + "select count(*) from " + tableName + ";\n"; - // Check for part of log message as well as part of progress information - final String EXPECTED_PATTERN = "ELAPSED TIME"; + // HS2 may log "ELAPSED TIME" (legacy); Beeline may print row timing or Driver logs "Time taken:" on stderr. + final String EXPECTED_PATTERN = + "(ELAPSED TIME|row selected \\([0-9]+\\.[0-9]+ seconds\\)|Time taken:)"; final String UNEXPECTED_PATTERN = "(?=Reducer 2\\:).*(?=Map 1\\:)"; testScriptFile(SCRIPT_TEXT, getBaseArgs(miniHS2.getBaseJdbcURL()), OutStream.ERR, Arrays.asList( diff --git a/ql/src/test/queries/clientpositive/llap_io_cache.q b/ql/src/test/queries/clientpositive/llap_io_cache.q index b5ab5b25bae9..170fddc7008d 100644 --- a/ql/src/test/queries/clientpositive/llap_io_cache.q +++ b/ql/src/test/queries/clientpositive/llap_io_cache.q @@ -19,7 +19,7 @@ TBLPROPERTIES ( INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 16777177, 'x') AS payload; + RPAD('x', 8388608, 'x') AS payload; SELECT LENGTH(payload) FROM tbl_parq; diff --git a/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out b/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out index 765ae2216310..7e264869f9d8 100644 --- a/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out +++ b/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out @@ -33,14 +33,14 @@ POSTHOOK: Output: default@tbl_parq PREHOOK: query: INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 16777177, 'x') AS payload + RPAD('x', 8388608, 'x') AS payload PREHOOK: type: QUERY PREHOOK: Input: _dummy_database@_dummy_table PREHOOK: Output: default@tbl_parq POSTHOOK: query: INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 16777177, 'x') AS payload + RPAD('x', 8388608, 'x') AS payload POSTHOOK: type: QUERY POSTHOOK: Input: _dummy_database@_dummy_table POSTHOOK: Output: default@tbl_parq @@ -54,7 +54,7 @@ POSTHOOK: query: SELECT LENGTH(payload) FROM tbl_parq POSTHOOK: type: QUERY POSTHOOK: Input: default@tbl_parq POSTHOOK: Output: hdfs://### HDFS PATH ### -16777177 +8388608 PREHOOK: query: SELECT SUM(LENGTH(payload)) FROM tbl_parq PREHOOK: type: QUERY PREHOOK: Input: default@tbl_parq @@ -63,4 +63,4 @@ POSTHOOK: query: SELECT SUM(LENGTH(payload)) FROM tbl_parq POSTHOOK: type: QUERY POSTHOOK: Input: default@tbl_parq POSTHOOK: Output: hdfs://### HDFS PATH ### -16777177 +8388608 diff --git a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java index fbece9c199a7..2f8aac00e4f5 100644 --- a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java +++ b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java @@ -19,11 +19,11 @@ package org.apache.hadoop.hive.metastore; import java.util.List; -import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -37,6 +37,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.ThreadFactoryBuilder; /** @@ -56,6 +57,10 @@ public class PartitionManagementTask implements MetastoreTaskThread { public static final String DISCOVER_PARTITIONS_TBLPROPERTY = "discover.partitions"; public static final String PARTITION_RETENTION_PERIOD_TBLPROPERTY = "partition.retention.period"; private static final Lock lock = new ReentrantLock(); + // these are just for testing + private static int completedAttempts; + /** Atomic: concurrent run() threads may take the tryLock() failure path together. */ + private static final AtomicInteger SKIPPED_ATTEMPTS = new AtomicInteger(0); private Configuration conf; @@ -75,11 +80,6 @@ public Configuration getConf() { return conf; } - private static boolean partitionDiscoveryEnabled(Map params) { - return params != null && params.containsKey(DISCOVER_PARTITIONS_TBLPROPERTY) && - params.get(DISCOVER_PARTITIONS_TBLPROPERTY).equalsIgnoreCase("true"); - } - @Override public void run() { if (lock.tryLock()) { @@ -131,8 +131,10 @@ public void run() { } lock.unlock(); } + completedAttempts++; } else { - LOG.info("Lock is held by some other partition discovery task. Skipping this attempt."); + int skipped = SKIPPED_ATTEMPTS.incrementAndGet(); + LOG.info("Lock is held by some other partition discovery task. Skipping this attempt..#{}", skipped); } } @@ -193,4 +195,20 @@ public void run() { } } + @VisibleForTesting + public static int getSkippedAttempts() { + return SKIPPED_ATTEMPTS.get(); + } + + /** Reset counters between tests; not for production use. */ + @VisibleForTesting + static void resetCountersForTesting() { + completedAttempts = 0; + SKIPPED_ATTEMPTS.set(0); + } + + @VisibleForTesting + public static int getCompletedAttempts() { + return completedAttempts; + } } From 081ac9c6c1dd1bb1d392818d387dad98f3561ac6 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 20 Apr 2026 10:09:32 +0530 Subject: [PATCH 09/21] HIVE-28265: Remove non-core files from PR (match master) Restore Beeline/llap_io_cache Q test, PartitionManagementTask, and TestPartitionManagement to upstream master. Core fix remains: SQLOperation, HiveStatement, HiveConnection, TestJdbcDriver2. Made-with: Cursor --- .../hive/beeline/TestBeeLineWithArgs.java | 5 ++-- .../queries/clientpositive/llap_io_cache.q | 2 +- .../clientpositive/llap/llap_io_cache.q.out | 8 +++---- .../metastore/PartitionManagementTask.java | 24 +++++++++---------- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java index 3a61ae5c4ef9..ffb9ab1224af 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeeLineWithArgs.java @@ -789,9 +789,8 @@ public void testQueryProgress() throws Throwable { "set hive.support.concurrency = false;\n" + "set hive.server2.logging.operation.level=execution;\n" + "select count(*) from " + tableName + ";\n"; - // HS2 may log "ELAPSED TIME" (legacy); Beeline may print row timing or Driver logs "Time taken:" on stderr. - final String EXPECTED_PATTERN = - "(ELAPSED TIME|row selected \\([0-9]+\\.[0-9]+ seconds\\)|Time taken:)"; + // Check for part of log message as well as part of progress information + final String EXPECTED_PATTERN = "ELAPSED TIME"; final String UNEXPECTED_PATTERN = "(?=Reducer 2\\:).*(?=Map 1\\:)"; testScriptFile(SCRIPT_TEXT, getBaseArgs(miniHS2.getBaseJdbcURL()), OutStream.ERR, Arrays.asList( diff --git a/ql/src/test/queries/clientpositive/llap_io_cache.q b/ql/src/test/queries/clientpositive/llap_io_cache.q index 170fddc7008d..b5ab5b25bae9 100644 --- a/ql/src/test/queries/clientpositive/llap_io_cache.q +++ b/ql/src/test/queries/clientpositive/llap_io_cache.q @@ -19,7 +19,7 @@ TBLPROPERTIES ( INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 8388608, 'x') AS payload; + RPAD('x', 16777177, 'x') AS payload; SELECT LENGTH(payload) FROM tbl_parq; diff --git a/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out b/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out index 7e264869f9d8..765ae2216310 100644 --- a/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out +++ b/ql/src/test/results/clientpositive/llap/llap_io_cache.q.out @@ -33,14 +33,14 @@ POSTHOOK: Output: default@tbl_parq PREHOOK: query: INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 8388608, 'x') AS payload + RPAD('x', 16777177, 'x') AS payload PREHOOK: type: QUERY PREHOOK: Input: _dummy_database@_dummy_table PREHOOK: Output: default@tbl_parq POSTHOOK: query: INSERT INTO TABLE tbl_parq SELECT 1 AS id, - RPAD('x', 8388608, 'x') AS payload + RPAD('x', 16777177, 'x') AS payload POSTHOOK: type: QUERY POSTHOOK: Input: _dummy_database@_dummy_table POSTHOOK: Output: default@tbl_parq @@ -54,7 +54,7 @@ POSTHOOK: query: SELECT LENGTH(payload) FROM tbl_parq POSTHOOK: type: QUERY POSTHOOK: Input: default@tbl_parq POSTHOOK: Output: hdfs://### HDFS PATH ### -8388608 +16777177 PREHOOK: query: SELECT SUM(LENGTH(payload)) FROM tbl_parq PREHOOK: type: QUERY PREHOOK: Input: default@tbl_parq @@ -63,4 +63,4 @@ POSTHOOK: query: SELECT SUM(LENGTH(payload)) FROM tbl_parq POSTHOOK: type: QUERY POSTHOOK: Input: default@tbl_parq POSTHOOK: Output: hdfs://### HDFS PATH ### -8388608 +16777177 diff --git a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java index 2f8aac00e4f5..fa9d5e2e9dd6 100644 --- a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java +++ b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PartitionManagementTask.java @@ -19,11 +19,11 @@ package org.apache.hadoop.hive.metastore; import java.util.List; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -59,8 +59,7 @@ public class PartitionManagementTask implements MetastoreTaskThread { private static final Lock lock = new ReentrantLock(); // these are just for testing private static int completedAttempts; - /** Atomic: concurrent run() threads may take the tryLock() failure path together. */ - private static final AtomicInteger SKIPPED_ATTEMPTS = new AtomicInteger(0); + private static int skippedAttempts; private Configuration conf; @@ -80,9 +79,15 @@ public Configuration getConf() { return conf; } + private static boolean partitionDiscoveryEnabled(Map params) { + return params != null && params.containsKey(DISCOVER_PARTITIONS_TBLPROPERTY) && + params.get(DISCOVER_PARTITIONS_TBLPROPERTY).equalsIgnoreCase("true"); + } + @Override public void run() { if (lock.tryLock()) { + skippedAttempts = 0; String qualifiedTableName = null; IMetaStoreClient msc = null; try { @@ -133,8 +138,8 @@ public void run() { } completedAttempts++; } else { - int skipped = SKIPPED_ATTEMPTS.incrementAndGet(); - LOG.info("Lock is held by some other partition discovery task. Skipping this attempt..#{}", skipped); + skippedAttempts++; + LOG.info("Lock is held by some other partition discovery task. Skipping this attempt..#{}", skippedAttempts); } } @@ -197,14 +202,7 @@ public void run() { @VisibleForTesting public static int getSkippedAttempts() { - return SKIPPED_ATTEMPTS.get(); - } - - /** Reset counters between tests; not for production use. */ - @VisibleForTesting - static void resetCountersForTesting() { - completedAttempts = 0; - SKIPPED_ATTEMPTS.set(0); + return skippedAttempts; } @VisibleForTesting From 3ec5d37dbcdcb6b4764fbce8e3aa26836ccba4da Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 20 Apr 2026 20:49:03 +0530 Subject: [PATCH 10/21] HIVE-28265: Add JDBC URL query timeout test (like testURLWithFetchSize) testURLWithHiveQueryTimeoutSeconds sets hive.query.timeout.seconds via the URL query string (getConnection postfix), matching the driver doc for db;sess?hive_conf. Asserts timeout message shows 1s (HIVE-28265). Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 749be4ffc713..c0a7219547ae 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -381,6 +381,41 @@ public void testURLWithFetchSize() throws SQLException { con.close(); } + /** + * Same idea as {@link #testURLWithFetchSize}: drive session behavior from the JDBC URL instead of + * only {@link Statement#setQueryTimeout(int)} or an explicit {@code SET}. The timeout is supplied + * in the URL query ({@code ?hive_conf_list}) per the driver format + * {@code jdbc:hive2://.../db;sess?hive_conf#hive_var}. + *

+ * HIVE-28265: {@link SQLTimeoutException#getMessage()} must reflect the configured limit (1s), + * not {@code after 0 seconds}. + */ + @Test + public void testURLWithHiveQueryTimeoutSeconds() throws Exception { + String udfName = SleepMsUDF.class.getName(); + // Postfix appends to the query string after test overlay / lock manager settings. + Connection con = getConnection(testDbName, "hive.query.timeout.seconds=1"); + try { + Statement stmt1 = con.createStatement(); + stmt1.execute("create temporary function sleepMsUDF as '" + udfName + "'"); + stmt1.close(); + Statement stmt = con.createStatement(); + try { + stmt.executeQuery("select sleepMsUDF(t1.under_col, 5) as u0, t1.under_col as u1, " + + "t2.under_col as u2 from " + tableName + " t1 join " + tableName + + " t2 on t1.under_col = t2.under_col"); + fail("Expecting SQLTimeoutException"); + } catch (SQLTimeoutException e) { + assertTimeoutMessageShowsOneSecond("JDBC URL hive.query.timeout.seconds=1 (query string)", e); + } catch (SQLException e) { + fail("Expecting SQLTimeoutException, but got SQLException: " + e); + } + stmt.close(); + } finally { + con.close(); + } + } + @Test /** * Test setting create external purge table by default in jdbc config From f9b858f12744de9b52fd3aea86b0ba0882391c1e Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 20 Apr 2026 23:32:25 +0530 Subject: [PATCH 11/21] HIVE-28265: Rename local Connection vars to avoid hiding TestJdbcDriver2.con Sonar: local name 'con' shadowed static field 'con' (HiddenField). Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index c0a7219547ae..18b7b8824138 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -374,11 +374,11 @@ private void checkBadUrl(String url) throws SQLException { * @throws SQLException */ public void testURLWithFetchSize() throws SQLException { - Connection con = getConnection(testDbName + ";fetchSize=1234", ""); - Statement stmt = con.createStatement(); + Connection connectionWithFetchSize = getConnection(testDbName + ";fetchSize=1234", ""); + Statement stmt = connectionWithFetchSize.createStatement(); assertEquals(stmt.getFetchSize(), 1234); stmt.close(); - con.close(); + connectionWithFetchSize.close(); } /** @@ -394,12 +394,12 @@ public void testURLWithFetchSize() throws SQLException { public void testURLWithHiveQueryTimeoutSeconds() throws Exception { String udfName = SleepMsUDF.class.getName(); // Postfix appends to the query string after test overlay / lock manager settings. - Connection con = getConnection(testDbName, "hive.query.timeout.seconds=1"); + Connection connectionWithUrlQueryTimeout = getConnection(testDbName, "hive.query.timeout.seconds=1"); try { - Statement stmt1 = con.createStatement(); + Statement stmt1 = connectionWithUrlQueryTimeout.createStatement(); stmt1.execute("create temporary function sleepMsUDF as '" + udfName + "'"); stmt1.close(); - Statement stmt = con.createStatement(); + Statement stmt = connectionWithUrlQueryTimeout.createStatement(); try { stmt.executeQuery("select sleepMsUDF(t1.under_col, 5) as u0, t1.under_col as u1, " + "t2.under_col as u2 from " + tableName + " t1 join " + tableName @@ -412,7 +412,7 @@ public void testURLWithHiveQueryTimeoutSeconds() throws Exception { } stmt.close(); } finally { - con.close(); + connectionWithUrlQueryTimeout.close(); } } @@ -422,13 +422,13 @@ public void testURLWithHiveQueryTimeoutSeconds() throws Exception { * @throws SQLException */ public void testCreateTableAsExternal() throws SQLException { - Connection con = getConnection(testDbName + ";hiveCreateAsExternalLegacy=true", ""); - Statement stmt = con.createStatement(); + Connection connectionWithExternalLegacy = getConnection(testDbName + ";hiveCreateAsExternalLegacy=true", ""); + Statement stmt = connectionWithExternalLegacy.createStatement(); ResultSet res = stmt.executeQuery("set hive.create.as.external.legacy"); assertTrue("ResultSet is empty", res.next()); assertEquals("hive.create.as.external.legacy=true", res.getObject(1)); stmt.close(); - con.close(); + connectionWithExternalLegacy.close(); } @Test From 7cad7ef55288ae0247ce3a27e33f96fbb038ba12 Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 21 Apr 2026 21:26:51 +0530 Subject: [PATCH 12/21] HIVE-28265: Seed session query timeout from JDBC URL hive conf Parse hive.query.timeout.seconds from connParams.getHiveConfs() at connect time using HiveConf.getTimeVar (same semantics as HiveStatement SET path) so JDBC timeout messages work when the timeout is set via URL only. Made-with: Cursor --- .../org/apache/hive/jdbc/HiveConnection.java | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index 28000675c37a..cc48ccb0b891 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -69,6 +69,7 @@ import java.util.Optional; import java.util.Properties; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Stream; @@ -82,6 +83,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.hive.common.auth.HiveAuthUtils; +import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.conf.HiveConf.ConfVars; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; @@ -166,14 +168,17 @@ public class HiveConnection implements java.sql.Connection { private static final Logger LOG = LoggerFactory.getLogger(HiveConnection.class); /** - * Sentinel: no {@code SET hive.query.timeout.seconds} has been observed on this connection yet. + * Sentinel: no {@code hive.query.timeout.seconds} has been applied from the JDBC URL or a client + * {@code SET} on this connection yet. */ static final long SESSION_QUERY_TIMEOUT_NOT_TRACKED = -1L; /** - * Last effective {@code hive.query.timeout.seconds} from a client {@code SET} (seconds), or + * Last effective {@code hive.query.timeout.seconds} in seconds: from the JDBC URL + * ({@code ?hive.query.timeout.seconds=...} / {@code hiveconf:} map) at connect time, and/or from a + * client {@code SET} (see {@link org.apache.hive.jdbc.HiveStatement}), or * {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED}. A JDBC {@code Connection} may be shared across threads * with concurrent {@link org.apache.hive.jdbc.HiveStatement}s on one HS2 session; this field uses an - * {@link AtomicLong} so updates remain well-defined (last SET wins). + * {@link AtomicLong} so updates remain well-defined (URL first, then last SET wins over prior value). */ private final AtomicLong sessionQueryTimeoutSeconds = new AtomicLong(SESSION_QUERY_TIMEOUT_NOT_TRACKED); private String jdbcUriString; @@ -204,15 +209,43 @@ public class HiveConnection implements java.sql.Connection { public TCLIService.Iface getClient() { return client; } /** - * Sets the effective {@code hive.query.timeout.seconds} (in seconds) after a successful - * {@code SET hive.query.timeout.seconds=...} on this connection. Used for JDBC timeout messages. + * Sets the effective {@code hive.query.timeout.seconds} (in seconds) after connect (URL) or a + * successful {@code SET hive.query.timeout.seconds=...}. Used for JDBC timeout messages (HIVE-28265). */ void setSessionQueryTimeoutSeconds(long seconds) { sessionQueryTimeoutSeconds.set(seconds); } /** - * @return seconds from the last client-tracked SET, or {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED} if none + * If the JDBC URL supplied {@code hive.query.timeout.seconds} (query string / {@code hiveconf:} map), + * parse and store it for {@link #getSessionQueryTimeoutSeconds()} so timeout error messages can use it + * without regex-parsing {@code SET} statements. Does not change HS2 behavior (already applied in + * {@link #openSession()}). + */ + private void applySessionQueryTimeoutFromJdbcUrl() { + Map hiveConfs = connParams.getHiveConfs(); + if (hiveConfs == null || hiveConfs.isEmpty()) { + return; + } + String raw = hiveConfs.get(ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname); + if (StringUtils.isBlank(raw)) { + return; + } + try { + HiveConf conf = new HiveConf(); + conf.set(ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname, raw.trim()); + long sec = HiveConf.getTimeVar(conf, ConfVars.HIVE_QUERY_TIMEOUT_SECONDS, TimeUnit.SECONDS); + if (sec > 0) { + setSessionQueryTimeoutSeconds(sec); + } + } catch (Exception e) { + LOG.debug("Could not parse {} from JDBC URL: {}", ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname, raw, e); + } + } + + /** + * @return seconds from the JDBC URL at connect and/or the last client-tracked SET, or + * {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED} if neither applied */ long getSessionQueryTimeoutSeconds() { return sessionQueryTimeoutSeconds.get(); @@ -360,6 +393,7 @@ protected HiveConnection(String uri, Properties info, // hive_conf_list -> hiveConfMap // hive_var_list -> hiveVarMap sessConfMap = connParams.getSessionVars(); + applySessionQueryTimeoutFromJdbcUrl(); setupLoginTimeout(); if (isKerberosAuthMode()) { // Ensure UserGroupInformation includes any authorized Kerberos principals. From dabb076acaa410a14373899c9a99946f4146d96d Mon Sep 17 00:00:00 2001 From: akpatra Date: Wed, 22 Apr 2026 07:26:57 +0530 Subject: [PATCH 13/21] HIVE-28265: Add test for session timeout message across multiple statements testQueryTimeoutMessagePersistedAcrossStatements verifies that when SET hive.query.timeout.seconds is issued on a separate closed statement, the tracked value on HiveConnection still drives the SQLTimeoutException message on a subsequent new statement (no setQueryTimeout call). Made-with: Cursor --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index 18b7b8824138..c6cc018b2a9d 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -2784,6 +2784,52 @@ public void testQueryTimeoutMessageUsesHiveConf() throws Exception { stmt.close(); } + /** + * Variant of {@link #testQueryTimeoutMessageUsesHiveConf}: the {@code SET} is issued on a + * separate, already-closed statement; the timed-out query runs on a brand-new statement with no + * {@link Statement#setQueryTimeout(int)} call. The tracked session timeout lives on the + * {@link HiveConnection}, so it persists across statement instances and must still drive the + * {@link SQLTimeoutException} message correctly. Covers the HIVE-28265 multi-statement scenario. + */ + @Test + public void testQueryTimeoutMessagePersistedAcrossStatements() throws Exception { + String udfName = SleepMsUDF.class.getName(); + Statement stmt1 = con.createStatement(); + stmt1.execute("create temporary function sleepMsUDF as '" + udfName + "'"); + stmt1.close(); + + // SET is issued on stmt2, which is then closed – timeout must survive on the connection + Statement stmt2 = con.createStatement(); + stmt2.execute("set hive.query.timeout.seconds=1s"); + stmt2.close(); + + // Brand-new statement, no setQueryTimeout() call – relies solely on the tracked session value + Statement stmt = con.createStatement(); + System.err.println("Executing query (expecting timeout): "); + try { + stmt.executeQuery("select sleepMsUDF(t1.under_col, 5) as u0, t1.under_col as u1, " + + "t2.under_col as u2 from " + tableName + " t1 join " + tableName + + " t2 on t1.under_col = t2.under_col"); + fail("Expecting SQLTimeoutException"); + } catch (SQLTimeoutException e) { + assertTimeoutMessageShowsOneSecond("SET on closed stmt2, executeQuery on new stmt", e); + System.err.println(e.toString()); + } catch (SQLException e) { + fail("Expecting SQLTimeoutException, but got SQLException: " + e); + e.printStackTrace(); + } + + // A fast query must still complete when the per-statement timeout overrides + stmt.setQueryTimeout(5); + try { + stmt.executeQuery("show tables"); + } catch (SQLException e) { + fail("Unexpected SQLException: " + e); + e.printStackTrace(); + } + stmt.close(); + } + /** * Test the non-null value of the Yarn ATS GUID. * We spawn 2 threads - one running the query and From c6362df30b436c57c7de893f0652a004ce68f4ac Mon Sep 17 00:00:00 2001 From: akpatra Date: Sun, 26 Apr 2026 13:42:16 +0530 Subject: [PATCH 14/21] HIVE-28265: Fix Sonar method-length warning in HiveConnection constructor Extract retry-config parsing from HiveConnection(uri,info,...,initSession) into readRetryIntervalMillis() so the constructor fits within Sonar's 150-line limit (was 151, now 142). Made-with: Cursor --- .../org/apache/hive/jdbc/HiveConnection.java | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index cc48ccb0b891..650adcff7d6a 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -458,18 +458,7 @@ protected HiveConnection(String uri, Properties info, executeInitSql(); } } else { - long retryInterval = 1000L; - try { - String strRetries = sessConfMap.get(JdbcConnectionParams.RETRIES); - if (StringUtils.isNotBlank(strRetries)) { - maxRetries = Integer.parseInt(strRetries); - } - String strRetryInterval = sessConfMap.get(JdbcConnectionParams.RETRY_INTERVAL); - if(StringUtils.isNotBlank(strRetryInterval)){ - retryInterval = Long.parseLong(strRetryInterval); - } - } catch(NumberFormatException e) { // Ignore the exception - } + long retryInterval = readRetryIntervalMillis(); for (int numRetries = 0;;) { try { @@ -531,6 +520,27 @@ protected HiveConnection(String uri, Properties info, client = newSynchronizedClient(client); } + /** + * Reads {@code retries} and {@code retryInterval} from the session configuration, updating + * {@link #maxRetries} as a side-effect, and returns the interval in milliseconds (default 1000). + * Extracted from the constructor to keep its length within Sonar's 150-line limit. + */ + private long readRetryIntervalMillis() { + long retryInterval = 1000L; + try { + String strRetries = sessConfMap.get(JdbcConnectionParams.RETRIES); + if (StringUtils.isNotBlank(strRetries)) { + maxRetries = Integer.parseInt(strRetries); + } + String strRetryInterval = sessConfMap.get(JdbcConnectionParams.RETRY_INTERVAL); + if (StringUtils.isNotBlank(strRetryInterval)) { + retryInterval = Long.parseLong(strRetryInterval); + } + } catch (NumberFormatException e) { // Ignore — bad values are silently skipped + } + return retryInterval; + } + private void executeInitSql() throws SQLException { if (initFile != null) { try (Statement st = createStatement()) { From b1f1d35b62734c194d770c93202123f3532a21f4 Mon Sep 17 00:00:00 2001 From: akpatra Date: Sun, 26 Apr 2026 14:26:21 +0530 Subject: [PATCH 15/21] HIVE-28265: Trigger CI Made-with: Cursor From 72dc8bf3af24168fc46b8ad37babbaa2137faee5 Mon Sep 17 00:00:00 2001 From: akpatra Date: Sun, 26 Apr 2026 21:06:33 +0530 Subject: [PATCH 16/21] HIVE-28265: Remove regex SET-tracking from HiveStatement The reviewer (InvisibleProgrammer) correctly pointed out that scanning every executed SQL string with a regex to detect SET hive.query.timeout.seconds is the wrong approach. The right source is TGetOperationStatusResp.errorMessage: SQLOperation already sets "Query timed out after N seconds" with the real value before cancel(TIMEDOUT), so the client-side regex fallback is unnecessary. Remove: - Pattern SET_HIVE_QUERY_TIMEOUT_SECONDS field - trackSessionQueryTimeoutIfSet(sql) method and its call site - Unused imports (HiveConf, TimeUnit, Matcher, Pattern) The timeout message resolution is now: 1. Server errorMessage from TGetOperationStatusResp (primary - correct approach) 2. Statement-level setQueryTimeout() (JDBC standard) 3. URL-seeded hive.query.timeout.seconds from applySessionQueryTimeoutFromJdbcUrl() All three paths avoid per-statement SQL parsing. Made-with: Cursor --- .../org/apache/hive/jdbc/HiveStatement.java | 40 ++----------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index 2ab21be0be87..c6a7a0649a62 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -19,7 +19,6 @@ package org.apache.hive.jdbc; import org.apache.commons.lang3.StringUtils; -import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.common.classification.InterfaceAudience.LimitedPrivate; import org.apache.hive.jdbc.logs.InPlaceUpdateStream; import org.apache.hive.service.cli.RowSet; @@ -58,9 +57,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import static org.apache.hadoop.hive.ql.ErrorMsg.CLIENT_POLLING_OPSTATUS_INTERRUPTED; @@ -74,10 +70,6 @@ public class HiveStatement implements java.sql.Statement { public static final String QUERY_CANCELLED_MESSAGE = "Query was cancelled."; - /** Last assignment wins if multiple appear (e.g. multi-line script). Uses find(), not full-string match. */ - private static final Pattern SET_HIVE_QUERY_TIMEOUT_SECONDS = Pattern.compile( - "(?i)set\\s+hive\\.query\\.timeout\\.seconds\\s*=\\s*([^;\\n]+)"); - private final HiveConnection connection; private TCLIService.Iface client; private Optional stmtHandle; @@ -306,7 +298,6 @@ public void closeOnCompletion() throws SQLException { public boolean execute(String sql) throws SQLException { runAsyncOnServer(sql); TGetOperationStatusResp status = waitForOperationToComplete(); - trackSessionQueryTimeoutIfSet(sql); // The query should be completed by now if (!status.isHasResultSet() && stmtHandle.isPresent() && !stmtHandle.get().isHasResultSet()) { @@ -408,34 +399,9 @@ private TGetOperationStatusResp waitForResultSetStatus() throws SQLException { } /** - * When {@code SET hive.query.timeout.seconds=...} succeeds, remember the effective value on the - * connection so {@code TIMEDOUT_STATE} can report it if the server omits {@code errorMessage} - * (HIVE-28265). - */ - private void trackSessionQueryTimeoutIfSet(String sql) { - if (sql == null) { - return; - } - Matcher m = SET_HIVE_QUERY_TIMEOUT_SECONDS.matcher(sql); - Long lastSec = null; - while (m.find()) { - try { - HiveConf conf = new HiveConf(); - conf.set(HiveConf.ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname, m.group(1).trim()); - long sec = HiveConf.getTimeVar(conf, HiveConf.ConfVars.HIVE_QUERY_TIMEOUT_SECONDS, TimeUnit.SECONDS); - lastSec = sec; - } catch (Exception e) { - LOG.debug("Could not parse session query timeout fragment: {}", m.group(0), e); - } - } - if (lastSec != null) { - connection.setSessionQueryTimeoutSeconds(lastSec); - } - } - - /** - * HIVE-28265: Prefer server error text unless it is empty or the known-broken "0 seconds" case; - * otherwise derive seconds from JDBC {@link #setQueryTimeout(int)} or last session SET. + * HIVE-28265: Prefer server error text (from {@code TGetOperationStatusResp.errorMessage}) unless + * it is empty or the legacy "0 seconds" value; fall back to JDBC {@link #setQueryTimeout(int)} + * or the URL-seeded {@code hive.query.timeout.seconds} on the connection. */ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { if (!needsLocalTimeoutMessageForTimedOut(serverMessage)) { From f7eb96061242a93752416bb508cedfff66ce78f9 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 27 Apr 2026 08:17:57 +0530 Subject: [PATCH 17/21] HIVE-28265: Trigger CI re-run Made-with: Cursor From cfefc2707611b3a1b2e1107839b64c72ccbbf969 Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 28 Apr 2026 10:37:28 +0530 Subject: [PATCH 18/21] HIVE-28265: Address reviewer comments (Javadoc, constant, test name) - Remove SESSION_QUERY_TIMEOUT_NOT_TRACKED constant; inline -1L - Rewrite Javadocs on setSessionQueryTimeoutSeconds, applySessionQueryTimeoutFromJdbcUrl, sqlTimeoutMessageForTimedOutState, and processOperationStatusResponse to drop ticket numbers and implementation-history notes - Rename testQueryTimeoutMessageUsesHiveConf to testQueryTimeoutFromSetStatement; update its Javadoc and cross-refs - Remove redundant assertFalse from assertTimeoutMessageShowsOneSecond --- .../org/apache/hive/jdbc/TestJdbcDriver2.java | 24 +++++++------- .../org/apache/hive/jdbc/HiveConnection.java | 32 +++++++------------ .../org/apache/hive/jdbc/HiveStatement.java | 10 +++--- 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java index c6cc018b2a9d..138fb8056b61 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcDriver2.java @@ -140,7 +140,7 @@ public class TestJdbcDriver2 { /** * {@code SET hive.query.timeout.seconds} applies to the whole HS2 session. Tests such as - * {@link #testQueryTimeoutMessageUsesHiveConf()} must not leave a short limit on the shared + * {@link #testQueryTimeoutFromSetStatement()} must not leave a short limit on the shared * {@link #con}, or unrelated tests will see {@link SQLTimeoutException}. */ @After @@ -164,9 +164,6 @@ private static void assertTimeoutMessageShowsOneSecond(String context, SQLTimeou context + ": should start with " + QUERY_TIMED_OUT_AFTER_1_SECONDS + " (HS2 may append ; Query ID: ...); actual=" + msg, msg.startsWith(QUERY_TIMED_OUT_AFTER_1_SECONDS)); - assertFalse( - "HIVE-28265: message should not claim 0 seconds: " + msg, - msg.contains("after 0 seconds")); } private static Connection getConnection(String prefix, String postfix) throws SQLException { @@ -387,7 +384,7 @@ public void testURLWithFetchSize() throws SQLException { * in the URL query ({@code ?hive_conf_list}) per the driver format * {@code jdbc:hive2://.../db;sess?hive_conf#hive_var}. *

- * HIVE-28265: {@link SQLTimeoutException#getMessage()} must reflect the configured limit (1s), + * {@link SQLTimeoutException#getMessage()} must reflect the configured limit (1s), * not {@code after 0 seconds}. */ @Test @@ -2755,13 +2752,14 @@ public void testQueryTimeout() throws Exception { } /** - * When only {@code hive.query.timeout.seconds} applies (no {@link Statement#setQueryTimeout(int)}), - * the client must still report the real limit in {@link SQLTimeoutException#getMessage()} (before - * HIVE-28265 some paths wrongly showed "after 0 seconds"). Message must begin with - * {@link #QUERY_TIMED_OUT_AFTER_1_SECONDS}; HS2 may append {@code ; Query ID: ...}. + * When the session timeout is configured via a {@code SET hive.query.timeout.seconds=...} + * statement and no {@link Statement#setQueryTimeout(int)} is called, the + * {@link SQLTimeoutException#getMessage()} must still reflect the real limit. + * Message must begin with {@link #QUERY_TIMED_OUT_AFTER_1_SECONDS}; + * HS2 may append {@code ; Query ID: ...}. */ @Test - public void testQueryTimeoutMessageUsesHiveConf() throws Exception { + public void testQueryTimeoutFromSetStatement() throws Exception { String udfName = SleepMsUDF.class.getName(); Statement stmt1 = con.createStatement(); stmt1.execute("create temporary function sleepMsUDF as '" + udfName + "'"); @@ -2785,11 +2783,11 @@ public void testQueryTimeoutMessageUsesHiveConf() throws Exception { } /** - * Variant of {@link #testQueryTimeoutMessageUsesHiveConf}: the {@code SET} is issued on a - * separate, already-closed statement; the timed-out query runs on a brand-new statement with no + * Variant of {@link #testQueryTimeoutFromSetStatement}: the {@code SET} is issued on a separate, + * already-closed statement; the timed-out query runs on a brand-new statement with no * {@link Statement#setQueryTimeout(int)} call. The tracked session timeout lives on the * {@link HiveConnection}, so it persists across statement instances and must still drive the - * {@link SQLTimeoutException} message correctly. Covers the HIVE-28265 multi-statement scenario. + * {@link SQLTimeoutException} message correctly. */ @Test public void testQueryTimeoutMessagePersistedAcrossStatements() throws Exception { diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index 650adcff7d6a..34643cd0532f 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -168,19 +168,12 @@ public class HiveConnection implements java.sql.Connection { private static final Logger LOG = LoggerFactory.getLogger(HiveConnection.class); /** - * Sentinel: no {@code hive.query.timeout.seconds} has been applied from the JDBC URL or a client - * {@code SET} on this connection yet. + * Last effective {@code hive.query.timeout.seconds} in seconds, or {@code -1} if not yet set. + * Seeded from the JDBC URL at connect time; a JDBC {@link java.sql.Connection} may be shared + * across threads with concurrent {@link org.apache.hive.jdbc.HiveStatement}s on one HS2 session, + * so this field uses an {@link AtomicLong} to keep updates well-defined. */ - static final long SESSION_QUERY_TIMEOUT_NOT_TRACKED = -1L; - /** - * Last effective {@code hive.query.timeout.seconds} in seconds: from the JDBC URL - * ({@code ?hive.query.timeout.seconds=...} / {@code hiveconf:} map) at connect time, and/or from a - * client {@code SET} (see {@link org.apache.hive.jdbc.HiveStatement}), or - * {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED}. A JDBC {@code Connection} may be shared across threads - * with concurrent {@link org.apache.hive.jdbc.HiveStatement}s on one HS2 session; this field uses an - * {@link AtomicLong} so updates remain well-defined (URL first, then last SET wins over prior value). - */ - private final AtomicLong sessionQueryTimeoutSeconds = new AtomicLong(SESSION_QUERY_TIMEOUT_NOT_TRACKED); + private final AtomicLong sessionQueryTimeoutSeconds = new AtomicLong(-1L); private String jdbcUriString; private String host; private int port; @@ -209,18 +202,18 @@ public class HiveConnection implements java.sql.Connection { public TCLIService.Iface getClient() { return client; } /** - * Sets the effective {@code hive.query.timeout.seconds} (in seconds) after connect (URL) or a - * successful {@code SET hive.query.timeout.seconds=...}. Used for JDBC timeout messages (HIVE-28265). + * Updates the tracked {@code hive.query.timeout.seconds} value (in seconds) on this connection. + * Called at connect time from the JDBC URL hive-conf map, and may be called again later if needed. */ void setSessionQueryTimeoutSeconds(long seconds) { sessionQueryTimeoutSeconds.set(seconds); } /** - * If the JDBC URL supplied {@code hive.query.timeout.seconds} (query string / {@code hiveconf:} map), - * parse and store it for {@link #getSessionQueryTimeoutSeconds()} so timeout error messages can use it - * without regex-parsing {@code SET} statements. Does not change HS2 behavior (already applied in - * {@link #openSession()}). + * If the JDBC URL supplied {@code hive.query.timeout.seconds} (via the {@code ?hive_conf_list} + * segment), parses and stores the value so that {@link #getSessionQueryTimeoutSeconds()} can + * return it for timeout error messages. This runs once at connect time and does not affect the + * server-side configuration, which is applied separately in {@link #openSession()}. */ private void applySessionQueryTimeoutFromJdbcUrl() { Map hiveConfs = connParams.getHiveConfs(); @@ -244,8 +237,7 @@ private void applySessionQueryTimeoutFromJdbcUrl() { } /** - * @return seconds from the JDBC URL at connect and/or the last client-tracked SET, or - * {@link #SESSION_QUERY_TIMEOUT_NOT_TRACKED} if neither applied + * @return the tracked {@code hive.query.timeout.seconds} in seconds, or {@code -1} if not set */ long getSessionQueryTimeoutSeconds() { return sessionQueryTimeoutSeconds.get(); diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index c6a7a0649a62..1282c36390fe 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -399,9 +399,9 @@ private TGetOperationStatusResp waitForResultSetStatus() throws SQLException { } /** - * HIVE-28265: Prefer server error text (from {@code TGetOperationStatusResp.errorMessage}) unless - * it is empty or the legacy "0 seconds" value; fall back to JDBC {@link #setQueryTimeout(int)} - * or the URL-seeded {@code hive.query.timeout.seconds} on the connection. + * Prefers the server error text from {@code TGetOperationStatusResp.errorMessage} when it is + * present and plausible; falls back to a locally derived message using + * {@link #setQueryTimeout(int)} or the URL-seeded {@code hive.query.timeout.seconds} value. */ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { if (!needsLocalTimeoutMessageForTimedOut(serverMessage)) { @@ -442,8 +442,8 @@ private SQLException sqlExceptionForCanceledState(TGetOperationStatusResp status } /** - * One GetOperationStatus response: progress update, Thrift status check, then terminal states. - * Extracted to keep {@link #waitForOperationToComplete()} smaller for static analysis (Sonar). + * Handles one {@code GetOperationStatus} response: applies a progress update if in-place updates + * are enabled, verifies the Thrift status, and dispatches on the operation state. */ private void processOperationStatusResponse(TGetOperationStatusResp statusResp) throws SQLException { if (!isOperationComplete && inPlaceUpdateStream.isPresent()) { From cd83d9ffc5a75b68aef31d0b02ea063863d21b39 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 4 May 2026 20:36:22 +0530 Subject: [PATCH 19/21] HIVE-28265: Use HYT00 SQL state to select timeout message; restore 01000 comment - sqlTimeoutMessageForTimedOutState: check statusResp.getSqlState() == "HYT00" instead of inspecting message text; remove needsLocalTimeoutMessageForTimedOut - sqlExceptionForCanceledState: restore // SQLSTATE 01000 = warning comment --- .../org/apache/hive/jdbc/HiveStatement.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java index 1282c36390fe..0a831e75cfbb 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveStatement.java @@ -399,12 +399,14 @@ private TGetOperationStatusResp waitForResultSetStatus() throws SQLException { } /** - * Prefers the server error text from {@code TGetOperationStatusResp.errorMessage} when it is - * present and plausible; falls back to a locally derived message using - * {@link #setQueryTimeout(int)} or the URL-seeded {@code hive.query.timeout.seconds} value. + * Returns the timeout message for a {@code TIMEDOUT_STATE} response. + * Uses the server error message when the SQL state is {@code HYT00} ("timeout expired"), + * which indicates that the server set a precise message. Otherwise falls back to a + * locally derived message from {@link #setQueryTimeout(int)} or the URL-seeded + * {@code hive.query.timeout.seconds} value on the connection. */ - private String sqlTimeoutMessageForTimedOutState(String serverMessage) { - if (!needsLocalTimeoutMessageForTimedOut(serverMessage)) { + private String sqlTimeoutMessageForTimedOutState(String serverMessage, String sqlState) { + if ("HYT00".equals(sqlState) && StringUtils.isNotBlank(serverMessage)) { return serverMessage; } long effectiveSec = resolveEffectiveTimeoutSecondsForMessage(); @@ -414,11 +416,6 @@ private String sqlTimeoutMessageForTimedOutState(String serverMessage) { return "Query timed out"; } - private boolean needsLocalTimeoutMessageForTimedOut(String timeoutMsg) { - return StringUtils.isBlank(timeoutMsg) - || StringUtils.containsIgnoreCase(timeoutMsg, "after 0 seconds"); - } - private long resolveEffectiveTimeoutSecondsForMessage() { if (queryTimeout > 0) { return queryTimeout; @@ -438,7 +435,7 @@ private SQLException sqlExceptionForCanceledState(TGetOperationStatusResp status } else { fullErrMsg = QUERY_CANCELLED_MESSAGE + " " + errMsg; } - return new SQLException(fullErrMsg, "01000"); + return new SQLException(fullErrMsg, "01000"); // SQLSTATE 01000 = warning } /** @@ -462,7 +459,8 @@ private void processOperationStatusResponse(TGetOperationStatusResp statusResp) case CANCELED_STATE: throw sqlExceptionForCanceledState(statusResp); case TIMEDOUT_STATE: - throw new SQLTimeoutException(sqlTimeoutMessageForTimedOutState(statusResp.getErrorMessage())); + throw new SQLTimeoutException( + sqlTimeoutMessageForTimedOutState(statusResp.getErrorMessage(), statusResp.getSqlState())); case ERROR_STATE: throw new SQLException(statusResp.getErrorMessage(), statusResp.getSqlState(), statusResp.getErrorCode()); case UKNOWN_STATE: From e84ee27db0547b5fe47767c8e5ba5774d671d670 Mon Sep 17 00:00:00 2001 From: akpatra Date: Thu, 18 Jun 2026 18:07:01 +0530 Subject: [PATCH 20/21] HIVE-28265: Use HiveConf.toTime to parse JDBC URL timeout (no full HiveConf) Replace new HiveConf() + getTimeVar with HiveConf.toTime(raw, SECONDS, SECONDS) in applySessionQueryTimeoutFromJdbcUrl. This parses the duration string directly without loading Hadoop/Hive configuration resources at connect time. --- jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index 34643cd0532f..3db9f29ee67a 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -225,9 +225,7 @@ private void applySessionQueryTimeoutFromJdbcUrl() { return; } try { - HiveConf conf = new HiveConf(); - conf.set(ConfVars.HIVE_QUERY_TIMEOUT_SECONDS.varname, raw.trim()); - long sec = HiveConf.getTimeVar(conf, ConfVars.HIVE_QUERY_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long sec = HiveConf.toTime(raw.trim(), TimeUnit.SECONDS, TimeUnit.SECONDS); if (sec > 0) { setSessionQueryTimeoutSeconds(sec); } From c3d2ccc31ca9f9f1cb6911b28d12e75e765f14ee Mon Sep 17 00:00:00 2001 From: akpatra Date: Fri, 19 Jun 2026 12:22:58 +0530 Subject: [PATCH 21/21] HIVE-28265: Trigger CI re-run