This is an automated email from the ASF dual-hosted git repository.

yuxia pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new b47ef7e  chore: update docs after opaque types CPP rework (#349)
b47ef7e is described below

commit b47ef7e5d6d53c87fb41b63a9969b71c1910eb1f
Author: Anton Borisov <[email protected]>
AuthorDate: Wed Feb 18 02:11:18 2026 +0000

    chore: update docs after opaque types CPP rework (#349)
---
 bindings/cpp/include/fluss.hpp                     |   3 +-
 website/docs/user-guide/cpp/api-reference.md       | 179 ++++++++++++++-------
 website/docs/user-guide/cpp/data-types.md          |  98 ++++++++---
 .../user-guide/cpp/example/partitioned-tables.md   |   7 +-
 .../user-guide/cpp/example/primary-key-tables.md   |  11 +-
 5 files changed, 202 insertions(+), 96 deletions(-)

diff --git a/bindings/cpp/include/fluss.hpp b/bindings/cpp/include/fluss.hpp
index 00b5000..a1a6b1f 100644
--- a/bindings/cpp/include/fluss.hpp
+++ b/bindings/cpp/include/fluss.hpp
@@ -716,10 +716,9 @@ class ScanRecords {
     Iterator begin() const { return Iterator(this, 0); }
     Iterator end() const { return Iterator(this, Size()); }
 
+   private:
     /// Returns the column name-to-index map (lazy-built, cached).
     const std::shared_ptr<detail::ColumnMap>& GetColumnMap() const;
-
-   private:
     friend class LogScanner;
     void Destroy() noexcept;
     void BuildColumnMap() const;
diff --git a/website/docs/user-guide/cpp/api-reference.md 
b/website/docs/user-guide/cpp/api-reference.md
index 3b93916..a20739b 100644
--- a/website/docs/user-guide/cpp/api-reference.md
+++ b/website/docs/user-guide/cpp/api-reference.md
@@ -145,9 +145,9 @@ Complete API reference for the Fluss C++ client.
 
 ## `Lookuper`
 
-| Method                                                                     | 
 Description                |
-|----------------------------------------------------------------------------|-----------------------------|
-| `Lookup(const GenericRow& pk_row, bool& found, GenericRow& out) -> Result` | 
Lookup a row by primary key |
+| Method                                                        |  Description 
               |
+|---------------------------------------------------------------|-----------------------------|
+| `Lookup(const GenericRow& pk_row, LookupResult& out) -> Result` | Lookup a 
row by primary key |
 
 ## `LogScanner`
 
@@ -164,21 +164,7 @@ Complete API reference for the Fluss C++ client.
 
 ## `GenericRow`
 
-### Index-Based Getters
-
-| Method                                         |  Description                
   |
-|------------------------------------------------|--------------------------------|
-| `GetBool(size_t idx) -> bool`                  | Get boolean value at index  
   |
-| `GetInt32(size_t idx) -> int32_t`              | Get 32-bit integer at index 
   |
-| `GetInt64(size_t idx) -> int64_t`              | Get 64-bit integer at index 
   |
-| `GetFloat32(size_t idx) -> float`              | Get 32-bit float at index   
   |
-| `GetFloat64(size_t idx) -> double`             | Get 64-bit float at index   
   |
-| `GetString(size_t idx) -> std::string`         | Get string at index         
   |
-| `GetBytes(size_t idx) -> std::vector<uint8_t>` | Get binary data at index    
   |
-| `GetDate(size_t idx) -> Date`                  | Get date at index           
   |
-| `GetTime(size_t idx) -> Time`                  | Get time at index           
   |
-| `GetTimestamp(size_t idx) -> Timestamp`        | Get timestamp at index      
   |
-| `DecimalToString(size_t idx) -> std::string`   | Get decimal as string at 
index |
+`GenericRow` is a **write-only** row used for append, upsert, delete, and 
lookup key construction. For reading field values from scan or lookup results, 
see [`RowView`](#rowview) and [`LookupResult`](#lookupresult).
 
 ### Index-Based Setters
 
@@ -215,34 +201,121 @@ When using `table.NewRow()`, the `Set()` method 
auto-routes to the correct type
 | `Set(const std::string& name, const Time& value)`        | Set time by 
column name           |
 | `Set(const std::string& name, const Timestamp& value)`   | Set timestamp by 
column name      |
 
-### Row Inspection
+## `RowView`
+
+Read-only row view for scan results. Provides zero-copy access to string and 
bytes data.
+
+:::warning Lifetime
+`RowView` borrows from `ScanRecords`. It must not outlive the `ScanRecords` 
that produced it (similar to `std::string_view` borrowing from `std::string`).
+:::
 
-| Method                             |  Description                     |
-|------------------------------------|----------------------------------|
-| `FieldCount() -> size_t`           | Get the number of fields         |
-| `GetType(size_t idx) -> DatumType` | Get the datum type at index      |
-| `IsNull(size_t idx) -> bool`       | Check if field is null           |
-| `IsDecimal(size_t idx) -> bool`    | Check if field is a decimal type |
+### Index-Based Getters
+
+| Method                                                     |  Description    
               |
+|------------------------------------------------------------|--------------------------------|
+| `FieldCount() -> size_t`                                   | Get the number 
of fields       |
+| `GetType(size_t idx) -> TypeId`                            | Get the type at 
index          |
+| `IsNull(size_t idx) -> bool`                               | Check if field 
is null         |
+| `GetBool(size_t idx) -> bool`                              | Get boolean 
value at index     |
+| `GetInt32(size_t idx) -> int32_t`                          | Get 32-bit 
integer at index    |
+| `GetInt64(size_t idx) -> int64_t`                          | Get 64-bit 
integer at index    |
+| `GetFloat32(size_t idx) -> float`                          | Get 32-bit 
float at index      |
+| `GetFloat64(size_t idx) -> double`                         | Get 64-bit 
float at index      |
+| `GetString(size_t idx) -> std::string_view`                | Get string at 
index (zero-copy)|
+| `GetBytes(size_t idx) -> std::pair<const uint8_t*, size_t>`| Get binary data 
at index (zero-copy)|
+| `GetDate(size_t idx) -> Date`                              | Get date at 
index              |
+| `GetTime(size_t idx) -> Time`                              | Get time at 
index              |
+| `GetTimestamp(size_t idx) -> Timestamp`                    | Get timestamp 
at index         |
+| `IsDecimal(size_t idx) -> bool`                            | Check if field 
is a decimal type|
+| `GetDecimalString(size_t idx) -> std::string`              | Get decimal as 
string at index |
+
+### Name-Based Getters
+
+| Method                                                  |  Description       
                |
+|---------------------------------------------------------|------------------------------------|
+| `IsNull(const std::string& name) -> bool`               | Check if field is 
null by name     |
+| `GetBool(const std::string& name) -> bool`              | Get boolean by 
column name         |
+| `GetInt32(const std::string& name) -> int32_t`          | Get 32-bit integer 
by column name  |
+| `GetInt64(const std::string& name) -> int64_t`          | Get 64-bit integer 
by column name  |
+| `GetFloat32(const std::string& name) -> float`          | Get 32-bit float 
by column name    |
+| `GetFloat64(const std::string& name) -> double`         | Get 64-bit float 
by column name    |
+| `GetString(const std::string& name) -> std::string_view`| Get string by 
column name          |
+| `GetBytes(const std::string& name) -> std::pair<const uint8_t*, size_t>` | 
Get binary data by column name |
+| `GetDate(const std::string& name) -> Date`              | Get date by column 
name            |
+| `GetTime(const std::string& name) -> Time`              | Get time by column 
name            |
+| `GetTimestamp(const std::string& name) -> Timestamp`    | Get timestamp by 
column name       |
+| `GetDecimalString(const std::string& name) -> std::string` | Get decimal as 
string by column name |
 
 ## `ScanRecord`
 
-| Field          | Type                     | Description                      
                                   |
-|----------------|--------------------------|---------------------------------------------------------------------|
-| `bucket_id`    | `int32_t`                | Bucket this record belongs to    
                                   |
-| `partition_id` | `std::optional<int64_t>` | Partition ID (present only for 
partitioned tables)                  |
-| `offset`       | `int64_t`                | Record offset in the log         
                                   |
-| `timestamp`    | `int64_t`                | Record timestamp                 
                                   |
-| `change_type`  | `ChangeType`             | Change type (AppendOnly, Insert, 
UpdateBefore, UpdateAfter, Delete) |
-| `row`          | `RowView`                | Row data                         
                                   |
+:::warning Lifetime
+`ScanRecord` contains a `RowView` that borrows from `ScanRecords`. It must not 
outlive the `ScanRecords` that produced it.
+:::
+
+| Field          | Type                    |  Description                     |
+|----------------|-------------------------|----------------------------------|
+| `bucket_id`    | `int32_t`               | Bucket this record belongs to    |
+| `partition_id` | `std::optional<int64_t>`| Partition ID (if partitioned)    |
+| `offset`       | `int64_t`               | Record offset in the log         |
+| `timestamp`    | `int64_t`               | Record timestamp                 |
+| `change_type`  | `ChangeType`            | Type of change (see `ChangeType`)|
+| `row`          | `RowView`               | Read-only row view for field 
access |
 
 ## `ScanRecords`
 
-| Method                                        |  Description                 
              |
-|-----------------------------------------------|--------------------------------------------|
-| `Size() -> size_t`                            | Number of records            
              |
-| `Empty() -> bool`                             | Check if empty               
              |
-| `operator[](size_t idx) -> const ScanRecord&` | Access record by index       
              |
-| `begin() / end()`                             | Iterator support for 
range-based for loops |
+| Method                                 |  Description                        
       |
+|----------------------------------------|--------------------------------------------|
+| `Size() -> size_t`                     | Number of records                   
       |
+| `Empty() -> bool`                      | Check if empty                      
       |
+| `operator[](size_t idx) -> ScanRecord` | Access record by index              
       |
+| `begin() / end()`                      | Iterator support for range-based 
for loops |
+
+## `LookupResult`
+
+Read-only result for lookup operations. Provides zero-copy access to field 
values.
+
+### Metadata
+
+| Method                      |  Description                   |
+|-----------------------------|--------------------------------|
+| `Found() -> bool`           | Whether a matching row was found |
+| `FieldCount() -> size_t`    | Get the number of fields       |
+
+### Index-Based Getters
+
+| Method                                                     |  Description    
               |
+|------------------------------------------------------------|--------------------------------|
+| `GetType(size_t idx) -> TypeId`                            | Get the type at 
index          |
+| `IsNull(size_t idx) -> bool`                               | Check if field 
is null         |
+| `GetBool(size_t idx) -> bool`                              | Get boolean 
value at index     |
+| `GetInt32(size_t idx) -> int32_t`                          | Get 32-bit 
integer at index    |
+| `GetInt64(size_t idx) -> int64_t`                          | Get 64-bit 
integer at index    |
+| `GetFloat32(size_t idx) -> float`                          | Get 32-bit 
float at index      |
+| `GetFloat64(size_t idx) -> double`                         | Get 64-bit 
float at index      |
+| `GetString(size_t idx) -> std::string_view`                | Get string at 
index (zero-copy)|
+| `GetBytes(size_t idx) -> std::pair<const uint8_t*, size_t>`| Get binary data 
at index (zero-copy)|
+| `GetDate(size_t idx) -> Date`                              | Get date at 
index              |
+| `GetTime(size_t idx) -> Time`                              | Get time at 
index              |
+| `GetTimestamp(size_t idx) -> Timestamp`                    | Get timestamp 
at index         |
+| `IsDecimal(size_t idx) -> bool`                            | Check if field 
is a decimal type|
+| `GetDecimalString(size_t idx) -> std::string`              | Get decimal as 
string at index |
+
+### Name-Based Getters
+
+| Method                                                  |  Description       
                |
+|---------------------------------------------------------|------------------------------------|
+| `IsNull(const std::string& name) -> bool`               | Check if field is 
null by name     |
+| `GetBool(const std::string& name) -> bool`              | Get boolean by 
column name         |
+| `GetInt32(const std::string& name) -> int32_t`          | Get 32-bit integer 
by column name  |
+| `GetInt64(const std::string& name) -> int64_t`          | Get 64-bit integer 
by column name  |
+| `GetFloat32(const std::string& name) -> float`          | Get 32-bit float 
by column name    |
+| `GetFloat64(const std::string& name) -> double`         | Get 64-bit float 
by column name    |
+| `GetString(const std::string& name) -> std::string_view`| Get string by 
column name          |
+| `GetBytes(const std::string& name) -> std::pair<const uint8_t*, size_t>` | 
Get binary data by column name |
+| `GetDate(const std::string& name) -> Date`              | Get date by column 
name            |
+| `GetTime(const std::string& name) -> Time`              | Get time by column 
name            |
+| `GetTimestamp(const std::string& name) -> Timestamp`    | Get timestamp by 
column name       |
+| `GetDecimalString(const std::string& name) -> std::string` | Get decimal as 
string by column name |
 
 ## `ArrowRecordBatch`
 
@@ -496,25 +569,15 @@ inline const char* ChangeTypeShortString(ChangeType ct) {
 | `TimestampLtz` | Timestamp with timezone    |
 | `Decimal`      | Decimal                    |
 
-### `DatumType`
-
-| Value           | C++ Type               |  Description                    |
-|-----------------|------------------------|---------------------------------|
-| `Null`          | --                     | Null value                      |
-| `Bool`          | `bool`                 | Boolean                         |
-| `Int32`         | `int32_t`              | 32-bit integer                  |
-| `Int64`         | `int64_t`              | 64-bit integer                  |
-| `Float32`       | `float`                | 32-bit float                    |
-| `Float64`       | `double`               | 64-bit float                    |
-| `String`        | `std::string`          | String                          |
-| `Bytes`         | `std::vector<uint8_t>` | Binary data                     |
-| `DecimalI64`    | `int64_t`              | Decimal (64-bit internal)       |
-| `DecimalI128`   | `__int128`             | Decimal (128-bit internal)      |
-| `DecimalString` | `std::string`          | Decimal (string representation) |
-| `Date`          | `Date`                 | Date                            |
-| `Time`          | `Time`                 | Time                            |
-| `TimestampNtz`  | `Timestamp`            | Timestamp without timezone      |
-| `TimestampLtz`  | `Timestamp`            | Timestamp with timezone         |
+### `ChangeType`
+
+| Value          |  Description                                |
+|----------------|---------------------------------------------|
+| `AppendOnly`   | Append-only record (log tables)             |
+| `Insert`       | Inserted row (PK tables)                    |
+| `UpdateBefore` | Row value before an update (PK tables)      |
+| `UpdateAfter`  | Row value after an update (PK tables)       |
+| `Delete`       | Deleted row (PK tables)                     |
 
 ### `OffsetSpec`
 
diff --git a/website/docs/user-guide/cpp/data-types.md 
b/website/docs/user-guide/cpp/data-types.md
index fb01ac2..18121d3 100644
--- a/website/docs/user-guide/cpp/data-types.md
+++ b/website/docs/user-guide/cpp/data-types.md
@@ -24,6 +24,8 @@ sidebar_position: 3
 
 ## GenericRow Setters
 
+`SetInt32` is used for `TinyInt`, `SmallInt`, and `Int` columns. For `TinyInt` 
and `SmallInt`, the value is validated at write time — an error is returned if 
it overflows the column's range (e.g., \[-128, 127\] for `TinyInt`, \[-32768, 
32767\] for `SmallInt`).
+
 ```cpp
 fluss::GenericRow row;
 row.SetNull(0);
@@ -52,46 +54,90 @@ row.Set("created_at", 
fluss::Timestamp::FromMillis(1700000000000));
 row.Set("nickname", nullptr);    // set to null
 ```
 
-## GenericRow Getters
+## Reading Field Values
+
+Field values are read through `RowView` (from scan results) and `LookupResult` 
(from lookups), not through `GenericRow`. Both provide the same getter 
interface with zero-copy access to string and bytes data.
+
+:::warning Lifetime
+`RowView` borrows from `ScanRecords`. It must not outlive the `ScanRecords` 
that produced it (similar to `std::string_view` borrowing from `std::string`).
+:::
 
 ```cpp
-std::string name = result_row.GetString(1);
-float score = result_row.GetFloat32(3);
-std::string balance = result_row.DecimalToString(4);
-fluss::Date date = result_row.GetDate(5);
-fluss::Time time = result_row.GetTime(6);
-fluss::Timestamp ts = result_row.GetTimestamp(7);
+// DON'T — string_view dangles after ScanRecords is destroyed:
+std::string_view dangling;
+{
+    fluss::ScanRecords records;
+    scanner.Poll(5000, records);
+    dangling = records[0].row.GetString(0); // points into ScanRecords memory
+}
+// dangling is undefined behavior here — ScanRecords is gone!
+
+// DO — use values within ScanRecords lifetime, or copy when you need 
ownership:
+fluss::ScanRecords records;
+scanner.Poll(5000, records);
+for (const auto& rec : records) {
+    auto name = rec.row.GetString(0);              // zero-copy string_view
+    auto owned = std::string(rec.row.GetString(0)); // explicit copy when 
needed
+    process(owned);
+}
+```
+
+### From Scan Results (RowView)
+
+```cpp
+for (const auto& rec : records) {
+    auto name = rec.row.GetString(1);          // zero-copy string_view
+    float score = rec.row.GetFloat32(3);
+    auto balance = rec.row.GetDecimalString(4); // std::string (already owned)
+    fluss::Date date = rec.row.GetDate(5);
+    fluss::Time time = rec.row.GetTime(6);
+    fluss::Timestamp ts = rec.row.GetTimestamp(7);
+}
+```
+
+### From Lookup Results (LookupResult)
+
+```cpp
+fluss::LookupResult result;
+lookuper.Lookup(pk_row, result);
+if (result.Found()) {
+    auto name = result.GetString(1);  // zero-copy string_view
+    int64_t age = result.GetInt64(2);
+}
 ```
 
-## DatumType Enum
-
-| DatumType       | C++ Type               | Getter                 |
-|-----------------|------------------------|------------------------|
-| `Null`          | --                     | `IsNull(idx)`          |
-| `Bool`          | `bool`                 | `GetBool(idx)`         |
-| `Int32`         | `int32_t`              | `GetInt32(idx)`        |
-| `Int64`         | `int64_t`              | `GetInt64(idx)`        |
-| `Float32`       | `float`                | `GetFloat32(idx)`      |
-| `Float64`       | `double`               | `GetFloat64(idx)`      |
-| `String`        | `std::string`          | `GetString(idx)`       |
-| `Bytes`         | `std::vector<uint8_t>` | `GetBytes(idx)`        |
-| `Date`          | `Date`                 | `GetDate(idx)`         |
-| `Time`          | `Time`                 | `GetTime(idx)`         |
-| `TimestampNtz`  | `Timestamp`            | `GetTimestamp(idx)`    |
-| `TimestampLtz`  | `Timestamp`            | `GetTimestamp(idx)`    |
-| `DecimalString` | `std::string`          | `DecimalToString(idx)` |
+## TypeId Enum
+
+`TinyInt` and `SmallInt` values are widened to `int32_t` on read.
+
+| TypeId          | C++ Type                                    | Getter       
             |
+|-----------------|---------------------------------------------|---------------------------|
+| `Boolean`       | `bool`                                      | 
`GetBool(idx)`            |
+| `TinyInt`       | `int32_t`                                   | 
`GetInt32(idx)`           |
+| `SmallInt`      | `int32_t`                                   | 
`GetInt32(idx)`           |
+| `Int`           | `int32_t`                                   | 
`GetInt32(idx)`           |
+| `BigInt`        | `int64_t`                                   | 
`GetInt64(idx)`           |
+| `Float`         | `float`                                     | 
`GetFloat32(idx)`         |
+| `Double`        | `double`                                    | 
`GetFloat64(idx)`         |
+| `String`        | `std::string_view`                          | 
`GetString(idx)`          |
+| `Bytes`         | `std::pair<const uint8_t*, size_t>`         | 
`GetBytes(idx)`           |
+| `Date`          | `Date`                                      | 
`GetDate(idx)`            |
+| `Time`          | `Time`                                      | 
`GetTime(idx)`            |
+| `Timestamp`     | `Timestamp`                                 | 
`GetTimestamp(idx)`       |
+| `TimestampLtz`  | `Timestamp`                                 | 
`GetTimestamp(idx)`       |
+| `Decimal`       | `std::string`                               | 
`GetDecimalString(idx)`   |
 
 ## Type Checking
 
 ```cpp
-if (rec.row.GetType(0) == fluss::DatumType::Int32) {
+if (rec.row.GetType(0) == fluss::TypeId::Int) {
     int32_t value = rec.row.GetInt32(0);
 }
 if (rec.row.IsNull(1)) {
     // field is null
 }
 if (rec.row.IsDecimal(2)) {
-    std::string decimal_str = rec.row.DecimalToString(2);
+    std::string decimal_str = rec.row.GetDecimalString(2);
 }
 ```
 
diff --git a/website/docs/user-guide/cpp/example/partitioned-tables.md 
b/website/docs/user-guide/cpp/example/partitioned-tables.md
index 371ee3e..17c1c20 100644
--- a/website/docs/user-guide/cpp/example/partitioned-tables.md
+++ b/website/docs/user-guide/cpp/example/partitioned-tables.md
@@ -171,10 +171,9 @@ pk.Set("user_id", 1001);
 pk.Set("region", "APAC");
 pk.Set("zone", static_cast<int64_t>(1));
 
-bool found = false;
-fluss::GenericRow result;
-lookuper.Lookup(pk, found, result);
-if (found) {
+fluss::LookupResult result;
+lookuper.Lookup(pk, result);
+if (result.Found()) {
     std::cout << "score=" << result.GetInt64(3) << std::endl;
 }
 ```
diff --git a/website/docs/user-guide/cpp/example/primary-key-tables.md 
b/website/docs/user-guide/cpp/example/primary-key-tables.md
index 7aa87e3..f26b547 100644
--- a/website/docs/user-guide/cpp/example/primary-key-tables.md
+++ b/website/docs/user-guide/cpp/example/primary-key-tables.md
@@ -120,13 +120,12 @@ table.NewLookup().CreateLookuper(lookuper);
 auto pk_row = table.NewRow();
 pk_row.Set("id", 1);
 
-bool found = false;
-fluss::GenericRow result_row;
-lookuper.Lookup(pk_row, found, result_row);
+fluss::LookupResult result;
+lookuper.Lookup(pk_row, result);
 
-if (found) {
-    std::cout << "Found: name=" << result_row.GetString(1)
-              << ", age=" << result_row.GetInt64(2) << std::endl;
+if (result.Found()) {
+    std::cout << "Found: name=" << result.GetString(1)
+              << ", age=" << result.GetInt64(2) << std::endl;
 } else {
     std::cout << "Not found" << std::endl;
 }

Reply via email to