From 532c05cb20ac08f95210b0331d57a747a3181d99 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 6 Apr 2026 12:54:10 +0530 Subject: [PATCH 01/26] 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 ad8020f163275832d36d595d2c386278c9f25766 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 6 Apr 2026 16:28:25 +0530 Subject: [PATCH 02/26] 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 7ee53b9df93e002835758fb15f053ae7ff6aebcf Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 7 Apr 2026 13:09:54 +0530 Subject: [PATCH 03/26] 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 e4f81dab13e053508a5eb00165118d37138cc55a Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 7 Apr 2026 16:56:19 +0530 Subject: [PATCH 04/26] 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 7abee0a809ab28e9aaf50de62131308440b9a207 Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 7 Apr 2026 21:31:45 +0530 Subject: [PATCH 05/26] 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 043bd2cbf5a34d478373752c1296e85716efcb2f Mon Sep 17 00:00:00 2001 From: akpatra Date: Fri, 10 Apr 2026 14:43:09 +0530 Subject: [PATCH 06/26] 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 bf1b402046ca5b5f4860cc1746e88892f1f1478e Mon Sep 17 00:00:00 2001 From: akpatra Date: Fri, 10 Apr 2026 17:54:54 +0530 Subject: [PATCH 07/26] 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 3d7ec33698e4def810dae46af3e8fe8d752971ff Mon Sep 17 00:00:00 2001 From: akpatra Date: Sat, 11 Apr 2026 12:58:57 +0530 Subject: [PATCH 08/26] 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 47f823b0419b2aed69852b3fdad74322d1da9b98 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 20 Apr 2026 10:09:32 +0530 Subject: [PATCH 09/26] 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 2d6e84cf470c2d47f94355ea3d1706766a548a89 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 20 Apr 2026 20:49:03 +0530 Subject: [PATCH 10/26] 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 d5f6ff6003bba315746addf287840f63c4dc2f92 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 20 Apr 2026 23:32:25 +0530 Subject: [PATCH 11/26] 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 b9c52d5544a8a4506bac942b604c1cde935f5b0c Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 21 Apr 2026 21:26:51 +0530 Subject: [PATCH 12/26] 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 2ba722c647ad805257595af975b6c27a77a0496c Mon Sep 17 00:00:00 2001 From: akpatra Date: Wed, 22 Apr 2026 07:26:57 +0530 Subject: [PATCH 13/26] 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 60b88a720169d096b2153f3cf16e7fa4eb446e7c Mon Sep 17 00:00:00 2001 From: akpatra Date: Sun, 26 Apr 2026 13:42:16 +0530 Subject: [PATCH 14/26] 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 e6b64fce2dbfcefaf4407d5a03f2dd40e4c79933 Mon Sep 17 00:00:00 2001 From: akpatra Date: Sun, 26 Apr 2026 14:26:21 +0530 Subject: [PATCH 15/26] HIVE-28265: Trigger CI Made-with: Cursor From 72cbdf9d8b65b2ad53316409e7e5584282ac861a Mon Sep 17 00:00:00 2001 From: akpatra Date: Sun, 26 Apr 2026 21:06:33 +0530 Subject: [PATCH 16/26] 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 23078850552404931f57216243d813132e5639a4 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 27 Apr 2026 08:17:57 +0530 Subject: [PATCH 17/26] HIVE-28265: Trigger CI re-run Made-with: Cursor From 4b8657f0dfc701d07445cd69a1905b602e3da3bb Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 28 Apr 2026 10:37:28 +0530 Subject: [PATCH 18/26] 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 81d050db081c6d120ccfca858f50eb587fe8049c Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 4 May 2026 20:36:22 +0530 Subject: [PATCH 19/26] 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 d5a6301192ed54ff7e4a67480d9918ec57cb2a56 Mon Sep 17 00:00:00 2001 From: akpatra Date: Thu, 18 Jun 2026 18:07:01 +0530 Subject: [PATCH 20/26] 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 26e551f0c9764e683c113e953f9f779706f37318 Mon Sep 17 00:00:00 2001 From: akpatra Date: Fri, 19 Jun 2026 12:22:58 +0530 Subject: [PATCH 21/26] HIVE-28265: Trigger CI re-run From 25dcdde0dadb140c0556d2ef2932fd2cdf18bb0a Mon Sep 17 00:00:00 2001 From: akpatra Date: Fri, 19 Jun 2026 19:31:53 +0530 Subject: [PATCH 22/26] HIVE-28265: Drop stray PartitionManagementTask changes from PR Restore PartitionManagementTask to upstream master. Test counters were re-introduced during rebase but upstream removed them in HIVE-29642; they are unrelated to the JDBC timeout fix and can fail CI. --- .../metastore/PartitionManagementTask.java | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) 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 fa9d5e2e9dd6..fbece9c199a7 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 @@ -37,7 +37,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.ThreadFactoryBuilder; /** @@ -57,9 +56,6 @@ 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; - private static int skippedAttempts; private Configuration conf; @@ -87,7 +83,6 @@ private static boolean partitionDiscoveryEnabled(Map params) { @Override public void run() { if (lock.tryLock()) { - skippedAttempts = 0; String qualifiedTableName = null; IMetaStoreClient msc = null; try { @@ -136,10 +131,8 @@ public void run() { } lock.unlock(); } - completedAttempts++; } else { - skippedAttempts++; - LOG.info("Lock is held by some other partition discovery task. Skipping this attempt..#{}", skippedAttempts); + LOG.info("Lock is held by some other partition discovery task. Skipping this attempt."); } } @@ -200,13 +193,4 @@ public void run() { } } - @VisibleForTesting - public static int getSkippedAttempts() { - return skippedAttempts; - } - - @VisibleForTesting - public static int getCompletedAttempts() { - return completedAttempts; - } } From d32a06cc7d73aab206efc0e548a1d711a277deac Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 22 Jun 2026 14:54:06 +0530 Subject: [PATCH 23/26] HIVE-28265: Skip JUnit Checks API publish on fork PR Jenkins builds Fork PRs cannot publish GitHub Checks from Jenkins, which marks the PostProcess junit step UNSTABLE with "No suitable checks publisher found" even when all tests pass. Use skipPublishingChecks so results are still recorded without failing the build on that infrastructure limitation. Co-authored-by: Cursor --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index c2da8e93e9a3..6e9e006a159f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -411,7 +411,7 @@ tar -xzf packaging/target/apache-hive-*-nightly-*-src.tar.gz sh """#!/bin/bash -e tar -czf ${fn} --files-from <(find . -path '*/surefire-reports/*')""" saveFile(fn) - junit '**/TEST-*.xml' + junit skipPublishingChecks: true, testResults: '**/TEST-*.xml' } } } From 7c85e0bbe8b37436f776d7ec950a22124b150da8 Mon Sep 17 00:00:00 2001 From: akpatra Date: Mon, 22 Jun 2026 17:57:20 +0530 Subject: [PATCH 24/26] HIVE-28265: Trigger CI re-run From ca1aff715719b5fc97c3721d6dd93cae557852ea Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 23 Jun 2026 13:43:25 +0530 Subject: [PATCH 25/26] HIVE-28265: Re-trigger CI From 93c40e17851b3c12beee9ef711705621b85ccf66 Mon Sep 17 00:00:00 2001 From: akpatra Date: Tue, 23 Jun 2026 14:23:31 +0530 Subject: [PATCH 26/26] HIVE-28265: Use volatile long for session query timeout tracking AtomicLong is unnecessary because we only perform simple get/set on this field; volatile provides the required cross-thread visibility. --- jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java | 10 ++++------ 1 file changed, 4 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 3db9f29ee67a..221bc09f54ee 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -157,7 +157,6 @@ 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; /** @@ -170,10 +169,9 @@ public class HiveConnection implements java.sql.Connection { /** * 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. + * across threads with concurrent {@link org.apache.hive.jdbc.HiveStatement}s on one HS2 session. */ - private final AtomicLong sessionQueryTimeoutSeconds = new AtomicLong(-1L); + private volatile long sessionQueryTimeoutSeconds = -1L; private String jdbcUriString; private String host; private int port; @@ -206,7 +204,7 @@ public class HiveConnection implements java.sql.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); + sessionQueryTimeoutSeconds = seconds; } /** @@ -238,7 +236,7 @@ private void applySessionQueryTimeoutFromJdbcUrl() { * @return the tracked {@code hive.query.timeout.seconds} in seconds, or {@code -1} if not set */ long getSessionQueryTimeoutSeconds() { - return sessionQueryTimeoutSeconds.get(); + return sessionQueryTimeoutSeconds; } /**