@@ -75,10 +75,11 @@ Writing BLOB Columns
7575~~~~~~~~~~~~~~~~~~~~
7676
7777A ``BLOB `` column is a ``LargeBinary `` field carrying Paimon's BLOB field
78- metadata, and an ``ARRAY<BLOB> `` column is a top-level ``List `` field whose
79- element field carries it. Build the BLOB field with ``paimon::Blob::ArrowField ``
80- and import it into Arrow; the element field of an ``ARRAY<BLOB> `` column must
81- keep that metadata:
78+ metadata, an ``ARRAY<BLOB> `` column is a top-level ``List `` field whose element
79+ field carries it, and a ``MAP<kt, BLOB> `` column is a top-level ``Map `` field
80+ whose values are ``LargeBinary ``. Build the BLOB field with
81+ ``paimon::Blob::ArrowField `` and import it into Arrow; the element field of an
82+ ``ARRAY<BLOB> `` column must keep that metadata:
8283
8384.. code-block :: cpp
8485
@@ -87,26 +88,31 @@ keep that metadata:
8788 PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Field> element,
8889 arrow::ImportField(c_element.get()));
8990 std::shared_ptr<arrow::Schema> schema = arrow::schema(
90- {arrow::field("id", arrow::int32()), arrow::field("frames", arrow::list(element))});
91+ {arrow::field("id", arrow::int32()), arrow::field("frames", arrow::list(element)),
92+ arrow::field("views", arrow::map(arrow::utf8(), element))});
9193
92- For a column stored in blob files, which includes every ``ARRAY<BLOB> `` column,
93- each value or element holds either the raw bytes or a serialized
94- ``paimon::BlobDescriptor `` produced by ``paimon::Blob::ToDescriptor ``; the writer
95- copies the referenced data into the blob file. A BLOB column listed in
96- ``blob-descriptor-field `` or ``blob-view-field `` keeps the reference in the data
97- file instead, so the referenced data must remain available.
94+ For a column stored in blob files, which includes every ``ARRAY<BLOB> `` and
95+ ``MAP<kt, BLOB> `` column, each value, element or map value holds either the raw
96+ bytes or a serialized ``paimon::BlobDescriptor `` produced by
97+ ``paimon::Blob::ToDescriptor ``; the writer copies the referenced data into the
98+ blob file. A BLOB column listed in ``blob-descriptor-field `` or
99+ ``blob-view-field `` keeps the reference in the data file instead, so the
100+ referenced data must remain available.
98101
99102If the referenced data cannot be reached, the write fails unless a write-null
100103option covers the failure: ``blob-write-null-on-missing-file `` covers a
101104referenced file that does not exist, and ``blob-write-null-on-fetch-failure ``
102105covers any other failure to resolve the descriptor or open the data, including
103106a missing file when the former is disabled and an offset past the end of the
104107file for a descriptor with a dynamic length (``-1 ``). A covered value is written
105- as NULL; in an ``ARRAY<BLOB> `` only that element becomes NULL, not the array.
108+ as NULL; in an ``ARRAY<BLOB> `` or a ``MAP<kt, BLOB> `` only that element or map
109+ value becomes NULL, not the array or map.
106110Any other failure fails the write, such as a failure to write the blob file or
107111to close a referenced file. As in Paimon Java, this includes a file too short
108112for the range of a descriptor with a known length, which is only detected while
109113the data is copied.
114+ Except for the placeholder marker defined below, a ``MAP<kt, BLOB> `` value with
115+ a repeated or null key also fails the write, before any of its data is written.
110116See :doc: `data_types ` for the table requirements and restrictions.
111117
112118A data-evolution write can update columns of existing rows. The write does not
@@ -125,20 +131,40 @@ payload was serialized with.
125131In such an update, a row whose BLOB or ``ARRAY<BLOB> `` value stays unchanged is
126132marked with the reserved bytes ``_PAIMON_BLOB_PLACEHOLDER ``: as the value itself
127133for a BLOB column, or as the only element of the array for an ``ARRAY<BLOB> ``
128- column. As in Paimon Java, the marker works whatever other columns the update
129- carries. Such a row keeps its value from the older files when read. Every write
130- stores a value equal to the reserved bytes as such a marker, so that value is
131- not supported.
134+ column. Every write stores a value equal to the reserved bytes as such a marker,
135+ so that value is not supported. A row whose ``MAP<kt, BLOB> `` value stays
136+ unchanged is marked with a map of exactly two entries with equal keys and null
137+ values, which no other map can hold since its keys must be unique. As in Paimon
138+ Java, a marker works whatever other columns the update carries, and its row
139+ keeps its value from the older files when read.
140+
141+ Append the two entries of a ``MAP<kt, BLOB> `` marker one by one, for example
142+ with ``arrow::MapBuilder ``, since a JSON object or a dictionary merges equal
143+ keys into one entry:
144+
145+ .. code-block :: cpp
146+
147+ // map_builder builds the MAP<STRING, BLOB> column of the update.
148+ auto* keys = static_cast<arrow::StringBuilder*>(map_builder->key_builder());
149+ auto* values = static_cast<arrow::LargeBinaryBuilder*>(map_builder->item_builder());
150+ ARROW_RETURN_NOT_OK(map_builder->Append());
151+ for (int32_t i = 0; i < 2; ++i) {
152+ ARROW_RETURN_NOT_OK(keys->Append("k"));
153+ ARROW_RETURN_NOT_OK(values->AppendNull());
154+ }
132155
133156 .. note ::
134157 The C++ writer differs from Paimon Java in these respects:
135158
136- - A placeholder is identified by the reserved bytes; Java uses a dedicated
137- placeholder object, which cannot collide with a user value.
159+ - A placeholder is identified by the reserved bytes, which can collide with a
160+ user value, or by the two-entry map marker above; Java uses a dedicated
161+ placeholder object.
162+ - A ``MAP<kt, BLOB> `` key cannot be null, as Arrow map keys are not
163+ nullable, and cannot be TIME; Java allows one null key and TIME keys.
138164 - A missing file is detected with ``FileSystem::Exists ``. Java detects a
139- missing file for an ``ARRAY<BLOB> `` element only from an HTTP 404, and
140- does not write NULL for a 404 under `` blob-write-null-on-fetch-failure ``
141- alone.
165+ missing file for an ``ARRAY<BLOB> `` element or a `` MAP<kt, BLOB> `` value
166+ only from an HTTP 404, and does not write NULL for a 404 under
167+ `` blob-write-null-on-fetch-failure `` alone.
142168 - A descriptor with a dynamic length is copied up to the file length read
143169 when it is opened, so data appended to the file during the copy is left
144170 out, and a file truncated during the copy fails the write. Java reads it
0 commit comments