Copilot commented on code in PR #6662:
URL: https://github.com/apache/hive/pull/6662#discussion_r3690445177


##########
ql/src/java/org/apache/hadoop/hive/ql/stats/StatsUtils.java:
##########
@@ -207,38 +197,75 @@ private static Statistics collectStatistics(HiveConf 
conf, PrunedPartitionList p
   public static long getNumRows(HiveConf conf, List<ColumnInfo> schema, Table 
table, PrunedPartitionList partitionList, 
       AtomicInteger noColsMissingStats) {
 
-    List<Partish> inputs = new ArrayList<>();
-    if (table.isPartitioned()) {
-      for (Partition part : partitionList.getNotDeniedPartns()) {
-        inputs.add(Partish.buildFor(table, part));
-      }
-    } else {
-      inputs.add(Partish.buildFor(table));
-    }
-
     Factory basicStatsFactory = new BasicStats.Factory();
 
     if (HiveConf.getBoolVar(conf, ConfVars.HIVE_STATS_ESTIMATE_STATS)) {
       basicStatsFactory.addEnhancer(new BasicStats.DataSizeEstimator(conf));
       basicStatsFactory.addEnhancer(new 
BasicStats.RowNumEstimator(estimateRowSizeFromSchema(conf, schema)));
     }
     
-    for (Partish pi : inputs) {
-      BasicStats bStats = new BasicStats(pi);
-      long nr = bStats.getNumRows();
-      // FIXME: this point will be lost after the factory; check that it's 
really a warning....cleanup/etc
-      if (nr <= 0) {
-        // log warning if row count is missing
-        noColsMissingStats.getAndIncrement();
-      }
-    }
-    List<BasicStats> results = basicStatsFactory.buildAll(conf, inputs);
+    List<BasicStats> results;
+    if (table.isPartitioned() && checkCanProvidePartitionStats(table)) {
+      List<Partish> inputs = partitionList.getNotDeniedPartns().stream()
+          .map(part -> Partish.buildFor(table, part))
+          .toList();
+      results = buildBasicStats(conf, table, partitionList, inputs, 
basicStatsFactory);
+    } else {
+      // partition-level statistics are unavailable (e.g. non-native table 
with an external stats source):
+      // fall back to the table-level statistics rather than per-partition 
minimums
+      results = List.of(buildBasicStats(table, basicStatsFactory));
+    }
+    // count the partishes with missing row counts (estimated rows do not 
count as provided)
+    noColsMissingStats.addAndGet((int) results.stream()
+        .filter(bStats -> bStats.getRawNumRows() <= 0)
+        .count());
     BasicStats aggregateStat = BasicStats.buildFrom(results);
 
     aggregateStat.apply(new BasicStats.SetMinRowNumber());
     return aggregateStat.getNumRows();
   }
 
+  /**
+   * Builds the per-partish basic stats. For non-native tables the stats are 
sourced from the storage handler in a
+   * single batched read, mirroring the aggregate column statistics retrieval 
(see
+   * {@link HiveStorageHandler#getAggrBasicStatsFor}): one read serves the 
whole partition list. Otherwise falls
+   * back to per-partish collection (see {@link BasicStats.Factory#buildAll}).
+   */
+  private static List<BasicStats> buildBasicStats(HiveConf conf, Table table, 
PrunedPartitionList partList,
+      List<Partish> inputs, BasicStats.Factory factory) {
+    HiveStorageHandler storageHandler = table.isNonNative() ? 
table.getStorageHandler() : null;
+    if (storageHandler != null && storageHandler.canProvideBasicStatistics()) {
+      if (partList != null && partList.getReferredPartCols().isEmpty()) {

Review Comment:
   `PrunedPartitionList#getReferredPartCols()` can be empty both when there is 
no partition predicate *and* when the pruner expression is constant-false (see 
the class-level comment). In the constant-false case, `inputs` will be empty, 
but this branch currently short-circuits to table-level statistics, producing a 
large row estimate for a scan that actually reads no partitions. Add a guard so 
the shortcut only applies when there are partitions in scope.



##########
ql/src/java/org/apache/hadoop/hive/ql/optimizer/StatsOptimizer.java:
##########
@@ -933,29 +934,60 @@ private Collection<List<ColumnStatisticsObj>> 
verifyAndGetPartColumnStats(
     }
 
     private Long getRowCnt(TableScanOperator tsOp, Table tbl) throws 
HiveException {
-      long rowCnt = 0L;
+      if (tbl.isNonNative()) {
+        return getRowCntFromStorageHandler(tsOp, tbl);
+      }
       final List<Partish> partishList;
-      if (tbl.isPartitioned() && 
StatsUtils.checkCanProvidePartitionStats(tbl)) {
+      if (tbl.isPartitioned()) {
         partishList = pctx.getPrunedPartitions(tsOp.getConf().getAlias(), 
tsOp).getPartitions().stream()
           .map(Partish::buildFor)
-          .collect(Collectors.toList());
+          .toList();
       } else {
         partishList = Lists.newArrayList(Partish.buildFor(tbl));
       }
+      long rowCnt = 0L;
       for (Partish partish : partishList) {
         Map<String, String> basicStats = partish.getPartParameters();
-        if (tbl.isNonNative()) {
-          if (!tbl.getStorageHandler().canComputeQueryUsingStats(partish)) {
-            return null;
-          }
-          basicStats = tbl.getStorageHandler().getBasicStatistics(partish);
-        } else if 
(!StatsUtils.areBasicStatsUptoDateForQueryAnswering(partish.getTable(), 
partish.getPartParameters())) {
+        if 
(!StatsUtils.areBasicStatsUptoDateForQueryAnswering(partish.getTable(), 
basicStats)) {
           return null;
         }
-        long partRowCnt = 
Long.parseLong(basicStats.get(StatsSetupConst.ROW_COUNT));
-        rowCnt += partRowCnt;
+        rowCnt += Long.parseLong(basicStats.get(StatsSetupConst.ROW_COUNT));
       }
       return rowCnt;
     }
+
+    private Long getRowCntFromStorageHandler(TableScanOperator tsOp, Table 
tbl) throws HiveException {
+      if (tbl.getMetaTable() != null) {
+        // metadata table scans cannot be answered from the data table's 
statistics
+        return null;
+      }
+      HiveStorageHandler storageHandler = tbl.getStorageHandler();
+      if (tbl.isPartitioned()) {
+        PrunedPartitionList prunedList = 
pctx.getPrunedPartitions(tsOp.getConf().getAlias(), tsOp);
+        if (!prunedList.getReferredPartCols().isEmpty()) {

Review Comment:
   When `PrunedPartitionList#getReferredPartCols()` is empty, that can also 
mean the pruner expression is constant-false (and the partition set is empty). 
In that case this method currently falls through to `getRowCount(tbl)` and may 
rewrite `count(*)` using the table-level row count, yielding an incorrect 
non-zero result for a scan that reads no partitions. Handle the 
empty-partition-list case explicitly before using table-level stats.



##########
common/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java:
##########
@@ -313,6 +313,7 @@ public enum ErrorMsg {
     "Not all clauses are supported with mapjoin hint. Please remove mapjoin 
hint."),
 
   ANALYZE_TABLE_NOSCAN_NON_NATIVE(10228, "ANALYZE TABLE NOSCAN cannot be used 
for " + "a non-native table"),
+  ANALYZE_PARTITION_NON_NATIVE(10449, "ANALYZE TABLE with a PARTITION clause 
cannot be used for a non-native table"),

Review Comment:
   This error is thrown only when `tbl.hasNonNativePartitionSupport()` is true, 
not for every non-native table. The current wording (“non-native table”) is 
broader than the actual condition and can mislead users into thinking 
partition-scoped ANALYZE is universally unsupported for all non-native tables. 
Consider rewording to match the real restriction.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to