wangyong9999 commented on code in PR #245:
URL: https://github.com/apache/paimon-cpp/pull/245#discussion_r3851792863


##########
src/paimon/common/utils/fields_comparator.cpp:
##########
@@ -128,21 +144,24 @@ Result<FieldsComparator::FieldComparatorFunc> 
FieldsComparator::CompareField(
                     return lvalue == rvalue ? 0 : (lvalue < rvalue ? -1 : 1);
                 });
         case arrow::Type::type::FLOAT:
-            // TODO(xinyu.lxy):
-            // currently in java KeyComparatorSupplier: -inf < -0.0 == +0.0 < 
+inf = nan
-            // paimon-cpp: -inf < -0.0 == +0.0 < +inf and nan cannot be 
compared
             return FieldsComparator::FieldComparatorFunc(
-                [field_idx](const InternalRow& lhs, const InternalRow& rhs) -> 
int32_t {
+                [field_idx, use_java_floating_point_order](const InternalRow& 
lhs,
+                                                           const InternalRow& 
rhs) -> int32_t {
                     float lvalue = lhs.GetFloat(field_idx);
                     float rvalue = rhs.GetFloat(field_idx);
-                    return lvalue == rvalue ? 0 : (lvalue < rvalue ? -1 : 1);
+                    return use_java_floating_point_order
+                               ? CompareFloatingPoint(lvalue, rvalue)
+                               : (lvalue == rvalue ? 0 : (lvalue < rvalue ? -1 
: 1));

Review Comment:
   Scoping the Java order to the index build is the right call, but note what 
the `false` branch is: `compare(NaN, x)` and `compare(x, NaN)` both return 1, 
so it is not a strict weak ordering, and every `std::sort` / `std::stable_sort` 
/ heap comparator that consumes a default `FieldsComparator` is UB the moment a 
NaN shows up.
   
   Leaving that behavior alone in this PR is fine, but the TODO that documented 
it ("nan cannot be compared") was deleted in the previous revision, so the 
branch now reads as deliberate and correct. Worth restoring a note here.



##########
src/paimon/core/operation/expire_snapshots.cpp:
##########
@@ -144,7 +156,33 @@ Result<int32_t> ExpireSnapshots::ExpireUntil(int64_t 
earliest_snapshot_id,
             continue;
         }
         PAIMON_ASSIGN_OR_RAISE(Snapshot snapshot, 
snapshot_manager_->LoadSnapshot(id));
-        
PAIMON_RETURN_NOT_OK(CleanUnusedDataFiles(snapshot.DeltaManifestList()));
+        bool tag_changed = false;
+        while (next_tag != tagged_snapshots.end() && next_tag->Id() < id) {
+            previous_tag = &*next_tag;
+            ++next_tag;
+            tag_changed = true;
+        }
+        if (tag_changed) {
+            Result<std::set<std::string>> tagged_data_files_result =
+                GetTaggedDataFiles(*previous_tag);
+            if (!tagged_data_files_result.ok()) {
+                PAIMON_LOG_WARN(logger_,
+                                "Skip cleaning data files of snapshot #%ld 
because the data files "
+                                "referenced by tag snapshot #%ld could not be 
loaded. %s",
+                                id, previous_tag->Id(),
+                                
tagged_data_files_result.status().ToString().c_str());
+                tagged_data_files.reset();
+            } else {
+                tagged_data_files = 
std::move(tagged_data_files_result).value();
+            }
+        }
+        if (previous_tag != nullptr && !tagged_data_files) {

Review Comment:
   Two notes on this skip:
   
   - `tagged_data_files` is only recomputed when the tag changes, so one failed 
read is sticky for every remaining snapshot under the same tag, not just this 
one. Java retries `tryReadDataFiles` per expiring snapshot and only caches 
successes.
   - The skip covers data-file cleanup only. The second loop still deletes this 
snapshot's manifests and the snapshot file itself, so the data files that were 
not cleaned end up unreachable — no snapshot references them and no delta 
manifest lists them any more. They are recoverable only through orphan cleanup.
   
   The conservative direction is right (better an orphan than a deleted tagged 
file); worth saying the second part in the warning so the leak is diagnosable.



##########
src/paimon/core/schema/schema_validation.cpp:
##########
@@ -341,6 +402,64 @@ Status SchemaValidation::ValidateForDeletionVectors(const 
CoreOptions& options)
         "no deletion of old data in this merge engine.");
 }
 
+Status SchemaValidation::ValidatePrimaryKeyBTreeIndexes(const TableSchema& 
schema,
+                                                        const CoreOptions& 
options) {
+    std::vector<std::string> index_columns = 
PrimaryKeyBTreeIndexColumns(schema.Options());
+    if (index_columns.empty()) {
+        return Status::OK();
+    }
+
+    PAIMON_ASSIGN_OR_RAISE(PrimaryKeyIndexDefinitions definitions,
+                           PrimaryKeyIndexDefinitions::Create(schema));
+    if (!options.DeletionVectorsEnabled()) {
+        return Status::Invalid(
+            "Primary-key BTree indexes require deletion-vectors.enabled = 
true.");
+    }
+    if (schema.PrimaryKeys().empty()) {
+        return Status::Invalid("Primary-key BTree indexes require a 
primary-key table.");
+    }
+    if (options.GetBucket() <= 0 && !IsPostponeBucketTable(schema, 
options.GetBucket())) {
+        return Status::Invalid(
+            fmt::format("Primary-key BTree indexes require fixed or postpone 
bucket mode "
+                        "(bucket > 0 or bucket = -2), but bucket is {}.",
+                        options.GetBucket()));
+    }
+    PAIMON_ASSIGN_OR_RAISE(
+        bool deletion_vectors_merge_on_read,
+        OptionsUtils::GetValueFromMap<bool>(schema.Options(), 
kDeletionVectorsMergeOnRead, false));
+    if (deletion_vectors_merge_on_read) {
+        return Status::Invalid(
+            "Primary-key BTree indexes require deletion-vectors.merge-on-read 
= false.");
+    }
+    PAIMON_ASSIGN_OR_RAISE(bool pk_clustering_override,
+                           OptionsUtils::GetValueFromMap<bool>(
+                               schema.Options(), 
Options::PK_CLUSTERING_OVERRIDE, false));
+    if (pk_clustering_override) {

Review Comment:
   `FileStoreCommitImpl::ValidateCommitOptions` already rejects 
`pk-clustering-override` for every table (`file_store_commit.cpp:129` runs it 
on every commit path), so this branch only fires for a schema whose first 
commit would fail anyway, and the message reads as if the option works when 
BTree indexes are off. Either drop it or point it at the global 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]

Reply via email to