Coverage Report

Created: 2026-11-03 17:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/format/jni/jni_data_bridge.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "jni_data_bridge.h"
19
20
#include <glog/logging.h>
21
22
#include <sstream>
23
#include <variant>
24
25
#include "core/block/block.h"
26
#include "core/column/column_array.h"
27
#include "core/column/column_map.h"
28
#include "core/column/column_nullable.h"
29
#include "core/column/column_string.h"
30
#include "core/column/column_struct.h"
31
#include "core/column/column_varbinary.h"
32
#include "core/column/variant_v2/column_variant_v2.h"
33
#include "core/data_type/data_type_array.h"
34
#include "core/data_type/data_type_map.h"
35
#include "core/data_type/data_type_nullable.h"
36
#include "core/data_type/data_type_struct.h"
37
#include "core/data_type/data_type_varbinary.h"
38
#include "core/data_type/define_primitive_type.h"
39
#include "core/data_type/primitive_type.h"
40
#include "core/types.h"
41
#include "core/value/decimalv2_value.h"
42
#include "core/value/jsonb_value.h"
43
#include "util/string_util.h"
44
#include "util/url_coding.h"
45
46
namespace doris {
47
48
namespace {
49
50
Status fill_jsonb_column(JniDataBridge::TableMetaAddress& address, MutableColumnPtr& column,
51
4
                         const bool* null_map, size_t num_rows) {
52
4
    const auto* offsets = static_cast<const int*>(address.next_meta_as_ptr());
53
4
    const auto* chars = static_cast<const char*>(address.next_meta_as_ptr());
54
4
    JsonBinaryValue value;
55
4
    size_t start = 0;
56
14
    for (size_t i = 0; i < num_rows; ++i) {
57
        // JNI carries JSON as UTF-8 text, not JSONB bytes. SQL NULL has no text to parse,
58
        // whereas the non-null text "null" must become a JSON null value.
59
11
        if (null_map[i]) {
60
2
            column->insert_default();
61
9
        } else {
62
9
            RETURN_IF_ERROR(value.from_json_string(chars + start, offsets[i] - start));
63
8
            column->insert_data(value.value(), value.size());
64
8
        }
65
10
        start = offsets[i];
66
10
    }
67
3
    return Status::OK();
68
4
}
69
70
} // namespace
71
72
#define FOR_FIXED_LENGTH_TYPES(M)                                  \
73
0
    M(PrimitiveType::TYPE_TINYINT, ColumnInt8, Int8)               \
74
0
    M(PrimitiveType::TYPE_BOOLEAN, ColumnUInt8, UInt8)             \
75
0
    M(PrimitiveType::TYPE_SMALLINT, ColumnInt16, Int16)            \
76
0
    M(PrimitiveType::TYPE_INT, ColumnInt32, Int32)                 \
77
0
    M(PrimitiveType::TYPE_BIGINT, ColumnInt64, Int64)              \
78
0
    M(PrimitiveType::TYPE_LARGEINT, ColumnInt128, Int128)          \
79
0
    M(PrimitiveType::TYPE_FLOAT, ColumnFloat32, Float32)           \
80
0
    M(PrimitiveType::TYPE_DOUBLE, ColumnFloat64, Float64)          \
81
0
    M(PrimitiveType::TYPE_DECIMALV2, ColumnDecimal128V2, Int128)   \
82
0
    M(PrimitiveType::TYPE_DECIMAL128I, ColumnDecimal128V3, Int128) \
83
0
    M(PrimitiveType::TYPE_DECIMAL32, ColumnDecimal32, Int32)       \
84
0
    M(PrimitiveType::TYPE_DECIMAL64, ColumnDecimal64, Int64)       \
85
0
    M(PrimitiveType::TYPE_DATE, ColumnDate, Int64)                 \
86
0
    M(PrimitiveType::TYPE_DATEV2, ColumnDateV2, UInt32)            \
87
0
    M(PrimitiveType::TYPE_DATETIME, ColumnDateTime, Int64)         \
88
0
    M(PrimitiveType::TYPE_DATETIMEV2, ColumnDateTimeV2, UInt64)    \
89
1
    M(PrimitiveType::TYPE_TIMESTAMP_NS, ColumnTimeStampNs, Int64)  \
90
1
    M(PrimitiveType::TYPE_TIMESTAMPTZ, ColumnTimeStampTz, UInt64)  \
91
0
    M(PrimitiveType::TYPE_IPV4, ColumnIPv4, IPv4)                  \
92
0
    M(PrimitiveType::TYPE_IPV6, ColumnIPv6, IPv6)                  \
93
0
    M(PrimitiveType::TYPE_UUID, ColumnUUID, UUIDValueType)
94
95
0
Status JniDataBridge::fill_block(Block* block, const ColumnNumbers& arguments, long table_address) {
96
0
    if (table_address == 0) {
97
0
        return Status::InternalError("table_address is 0");
98
0
    }
99
0
    TableMetaAddress table_meta(table_address);
100
0
    long num_rows = table_meta.next_meta_as_long();
101
0
    for (size_t i : arguments) {
102
0
        if (block->get_by_position(i).column.get() == nullptr) {
103
0
            auto return_type = block->get_data_type(i);
104
0
            bool result_nullable = return_type->is_nullable();
105
0
            ColumnUInt8::MutablePtr null_col = nullptr;
106
0
            if (result_nullable) {
107
0
                return_type = remove_nullable(return_type);
108
0
                null_col = ColumnUInt8::create();
109
0
            }
110
0
            auto res_col = return_type->create_column();
111
0
            if (result_nullable) {
112
0
                block->replace_by_position(
113
0
                        i, ColumnNullable::create(std::move(res_col), std::move(null_col)));
114
0
            } else {
115
0
                block->replace_by_position(i, std::move(res_col));
116
0
            }
117
0
        } else if (is_column_const(*(block->get_by_position(i).column))) {
118
0
            auto doris_column = block->get_by_position(i).column->convert_to_full_column_if_const();
119
0
            bool is_nullable = block->get_by_position(i).type->is_nullable();
120
0
            block->replace_by_position(i, is_nullable ? make_nullable(doris_column) : doris_column);
121
0
        }
122
0
        auto& column_with_type_and_name = block->get_by_position(i);
123
0
        auto& column_ptr = column_with_type_and_name.column;
124
0
        auto& column_type = column_with_type_and_name.type;
125
0
        RETURN_IF_ERROR(fill_column(table_meta, column_ptr, column_type, num_rows));
126
0
    }
127
0
    return Status::OK();
128
0
}
129
130
Status JniDataBridge::fill_column(TableMetaAddress& address, ColumnPtr& doris_column,
131
7
                                  const DataTypePtr& data_type, size_t num_rows) {
132
7
    auto logical_type = data_type->get_primitive_type();
133
7
    void* null_map_ptr = address.next_meta_as_ptr();
134
7
    if (null_map_ptr == nullptr) {
135
        // org.apache.doris.jni.spi.vec.ColumnType.Type#UNSUPPORTED will set column address as 0
136
0
        return Status::InternalError("Unsupported type {} in java side", data_type->get_name());
137
0
    }
138
7
    auto mutable_doris_column = IColumn::mutate(std::move(doris_column));
139
7
    MutableColumnPtr data_column;
140
7
    if (auto* nullable_column = check_and_get_column<ColumnNullable>(mutable_doris_column.get())) {
141
3
        data_column = nullable_column->get_nested_column_ptr();
142
3
        NullMap& null_map = nullable_column->get_null_map_data();
143
3
        size_t origin_size = null_map.size();
144
3
        null_map.resize(origin_size + num_rows);
145
3
        memcpy(null_map.data() + origin_size, static_cast<bool*>(null_map_ptr), num_rows);
146
4
    } else {
147
4
        data_column = mutable_doris_column->get_ptr();
148
4
    }
149
    // Date and DateTime are deprecated and not supported.
150
7
    Status status = Status::OK();
151
7
    switch (logical_type) {
152
0
#define DISPATCH(TYPE_INDEX, COLUMN_TYPE, CPP_TYPE)                                             \
153
1
    case TYPE_INDEX: {                                                                          \
154
1
        auto* data = reinterpret_cast<CPP_TYPE*>(address.next_meta_as_ptr());                   \
155
1
        status = _fill_fixed_length_column<COLUMN_TYPE, CPP_TYPE>(data_column, data, num_rows); \
156
1
        break;                                                                                  \
157
1
    }
158
1
        FOR_FIXED_LENGTH_TYPES(DISPATCH)
159
0
#undef DISPATCH
160
0
    case PrimitiveType::TYPE_STRING:
161
0
        [[fallthrough]];
162
0
    case PrimitiveType::TYPE_CHAR:
163
0
        [[fallthrough]];
164
0
    case PrimitiveType::TYPE_VARCHAR:
165
0
        status = _fill_string_column(address, data_column, num_rows);
166
0
        break;
167
4
    case PrimitiveType::TYPE_JSONB:
168
4
        status = fill_jsonb_column(address, data_column, static_cast<const bool*>(null_map_ptr),
169
4
                                   num_rows);
170
4
        break;
171
0
    case PrimitiveType::TYPE_ARRAY:
172
0
        status = _fill_array_column(address, data_column, data_type, num_rows);
173
0
        break;
174
0
    case PrimitiveType::TYPE_MAP:
175
0
        status = _fill_map_column(address, data_column, data_type, num_rows);
176
0
        break;
177
0
    case PrimitiveType::TYPE_STRUCT:
178
0
        status = _fill_struct_column(address, data_column, data_type, num_rows);
179
0
        break;
180
0
    case PrimitiveType::TYPE_VARBINARY:
181
0
        status = _fill_varbinary_column(address, data_column, num_rows);
182
0
        break;
183
2
    case PrimitiveType::TYPE_VARIANT:
184
2
        status = _fill_variant_v2_column(address, data_column, num_rows);
185
2
        break;
186
0
    default:
187
0
        status = Status::InvalidArgument("Unsupported type {} in jni scanner",
188
0
                                         data_type->get_name());
189
0
        break;
190
7
    }
191
7
    doris_column = std::move(mutable_doris_column);
192
7
    return status;
193
7
}
194
195
Status JniDataBridge::_fill_varbinary_column(TableMetaAddress& address,
196
0
                                             MutableColumnPtr& doris_column, size_t num_rows) {
197
0
    auto* meta_base = reinterpret_cast<char*>(address.next_meta_as_ptr());
198
0
    auto& varbinary_col = assert_cast<ColumnVarbinary&>(*doris_column);
199
    // Java side writes per-row metadata as 16 bytes: [len: long][addr: long]
200
0
    for (size_t i = 0; i < num_rows; ++i) {
201
        // Read length (first 8 bytes)
202
0
        int64_t len = 0;
203
0
        memcpy(&len, meta_base + 16 * i, sizeof(len));
204
0
        if (len <= 0) {
205
0
            varbinary_col.insert_default();
206
0
        } else {
207
            // Read address (next 8 bytes)
208
0
            uint64_t addr_u = 0;
209
0
            memcpy(&addr_u, meta_base + 16 * i + 8, sizeof(addr_u));
210
0
            const char* src = reinterpret_cast<const char*>(addr_u);
211
0
            varbinary_col.insert_data(src, static_cast<size_t>(len));
212
0
        }
213
0
    }
214
0
    return Status::OK();
215
0
}
216
217
Status JniDataBridge::_fill_variant_v2_column(TableMetaAddress& address,
218
2
                                              MutableColumnPtr& doris_column, size_t num_rows) {
219
2
    const auto metadata_count = static_cast<size_t>(address.next_meta_as_long());
220
2
    const auto* metadata_offsets = reinterpret_cast<const uint32_t*>(address.next_meta_as_ptr());
221
2
    const auto* metadata_bytes = reinterpret_cast<const char*>(address.next_meta_as_ptr());
222
2
    const auto* metadata_ids = reinterpret_cast<const uint32_t*>(address.next_meta_as_ptr());
223
2
    const auto* value_offsets = reinterpret_cast<const uint32_t*>(address.next_meta_as_ptr());
224
2
    const auto* value_bytes = reinterpret_cast<const char*>(address.next_meta_as_ptr());
225
226
2
    auto& variant_column = assert_cast<ColumnVariantV2&>(*doris_column);
227
2
    variant_column.insert_encoded_rows({
228
2
            .metadata_bytes = {metadata_bytes, metadata_offsets[metadata_count]},
229
2
            .metadata_offsets = {metadata_offsets, metadata_count + 1},
230
2
            .meta_ids = {metadata_ids, num_rows},
231
2
            .value_bytes = {value_bytes, value_offsets[num_rows]},
232
2
            .value_offsets = {value_offsets, num_rows + 1},
233
2
    });
234
2
    return Status::OK();
235
2
}
236
237
Status JniDataBridge::_fill_string_column(TableMetaAddress& address, MutableColumnPtr& doris_column,
238
0
                                          size_t num_rows) {
239
0
    auto& string_col = static_cast<ColumnString&>(*doris_column);
240
0
    ColumnString::Chars& string_chars = string_col.get_chars();
241
0
    ColumnString::Offsets& string_offsets = string_col.get_offsets();
242
0
    int* offsets = reinterpret_cast<int*>(address.next_meta_as_ptr());
243
0
    char* chars = reinterpret_cast<char*>(address.next_meta_as_ptr());
244
245
    // This judgment is necessary, otherwise the following statement `offsets[num_rows - 1]` out of bounds
246
    // What's more, This judgment must be placed after `address.next_meta_as_ptr()`
247
    // because `address.next_meta_as_ptr` will make `address._meta_index` plus 1
248
0
    if (num_rows == 0) {
249
0
        return Status::OK();
250
0
    }
251
252
0
    size_t origin_chars_size = string_chars.size();
253
0
    string_chars.resize(origin_chars_size + offsets[num_rows - 1]);
254
0
    memcpy(string_chars.data() + origin_chars_size, chars, offsets[num_rows - 1]);
255
256
0
    size_t origin_offsets_size = string_offsets.size();
257
0
    size_t start_offset = string_offsets[origin_offsets_size - 1];
258
0
    string_offsets.resize(origin_offsets_size + num_rows);
259
0
    for (size_t i = 0; i < num_rows; ++i) {
260
0
        string_offsets[origin_offsets_size + i] =
261
0
                static_cast<unsigned int>(offsets[i] + start_offset);
262
0
    }
263
0
    return Status::OK();
264
0
}
265
266
Status JniDataBridge::_fill_array_column(TableMetaAddress& address, MutableColumnPtr& doris_column,
267
0
                                         const DataTypePtr& data_type, size_t num_rows) {
268
0
    ColumnPtr& element_column = static_cast<ColumnArray&>(*doris_column).get_data_ptr();
269
0
    const DataTypePtr& element_type =
270
0
            (assert_cast<const DataTypeArray*>(remove_nullable(data_type).get()))
271
0
                    ->get_nested_type();
272
0
    ColumnArray::Offsets64& offsets_data = static_cast<ColumnArray&>(*doris_column).get_offsets();
273
274
0
    int64_t* offsets = reinterpret_cast<int64_t*>(address.next_meta_as_ptr());
275
0
    size_t origin_size = offsets_data.size();
276
0
    offsets_data.resize(origin_size + num_rows);
277
0
    size_t start_offset = offsets_data[origin_size - 1];
278
0
    for (size_t i = 0; i < num_rows; ++i) {
279
0
        offsets_data[origin_size + i] = offsets[i] + start_offset;
280
0
    }
281
282
0
    return fill_column(address, element_column, element_type,
283
0
                       offsets_data[origin_size + num_rows - 1] - start_offset);
284
0
}
285
286
Status JniDataBridge::_fill_map_column(TableMetaAddress& address, MutableColumnPtr& doris_column,
287
0
                                       const DataTypePtr& data_type, size_t num_rows) {
288
0
    auto& map = static_cast<ColumnMap&>(*doris_column);
289
0
    const DataTypePtr& key_type =
290
0
            reinterpret_cast<const DataTypeMap*>(remove_nullable(data_type).get())->get_key_type();
291
0
    const DataTypePtr& value_type =
292
0
            reinterpret_cast<const DataTypeMap*>(remove_nullable(data_type).get())
293
0
                    ->get_value_type();
294
0
    ColumnPtr& key_column = map.get_keys_ptr();
295
0
    ColumnPtr& value_column = map.get_values_ptr();
296
0
    ColumnArray::Offsets64& map_offsets = map.get_offsets();
297
298
0
    int64_t* offsets = reinterpret_cast<int64_t*>(address.next_meta_as_ptr());
299
0
    size_t origin_size = map_offsets.size();
300
0
    map_offsets.resize(origin_size + num_rows);
301
0
    size_t start_offset = map_offsets[origin_size - 1];
302
0
    for (size_t i = 0; i < num_rows; ++i) {
303
0
        map_offsets[origin_size + i] = offsets[i] + start_offset;
304
0
    }
305
306
0
    RETURN_IF_ERROR(fill_column(address, key_column, key_type,
307
0
                                map_offsets[origin_size + num_rows - 1] - start_offset));
308
0
    RETURN_IF_ERROR(fill_column(address, value_column, value_type,
309
0
                                map_offsets[origin_size + num_rows - 1] - start_offset));
310
0
    return Status::OK();
311
0
}
312
313
Status JniDataBridge::_fill_struct_column(TableMetaAddress& address, MutableColumnPtr& doris_column,
314
0
                                          const DataTypePtr& data_type, size_t num_rows) {
315
0
    auto& doris_struct = static_cast<ColumnStruct&>(*doris_column);
316
0
    const DataTypeStruct* doris_struct_type =
317
0
            reinterpret_cast<const DataTypeStruct*>(remove_nullable(data_type).get());
318
0
    for (int i = 0; i < doris_struct.tuple_size(); ++i) {
319
0
        ColumnPtr& struct_field = doris_struct.get_column_ptr(i);
320
0
        const DataTypePtr& field_type = doris_struct_type->get_element(i);
321
0
        RETURN_IF_ERROR(fill_column(address, struct_field, field_type, num_rows));
322
0
    }
323
0
    return Status::OK();
324
0
}
325
326
11
std::string JniDataBridge::get_jni_type(const DataTypePtr& data_type) {
327
11
    DataTypePtr type = remove_nullable(data_type);
328
11
    std::ostringstream buffer;
329
11
    switch (type->get_primitive_type()) {
330
0
    case TYPE_BOOLEAN:
331
0
        return "boolean";
332
0
    case TYPE_TINYINT:
333
0
        return "tinyint";
334
0
    case TYPE_SMALLINT:
335
0
        return "smallint";
336
0
    case TYPE_INT:
337
0
        return "int";
338
0
    case TYPE_BIGINT:
339
0
        return "bigint";
340
0
    case TYPE_LARGEINT:
341
0
        return "largeint";
342
0
    case TYPE_FLOAT:
343
0
        return "float";
344
0
    case TYPE_DOUBLE:
345
0
        return "double";
346
0
    case TYPE_IPV4:
347
0
        return "ipv4";
348
0
    case TYPE_IPV6:
349
0
        return "ipv6";
350
0
    case TYPE_UUID:
351
0
        return "uuid";
352
0
    case TYPE_VARCHAR:
353
0
        [[fallthrough]];
354
0
    case TYPE_CHAR:
355
0
        [[fallthrough]];
356
1
    case TYPE_STRING:
357
1
        return "string";
358
0
    case TYPE_DATE:
359
0
        return "datev1";
360
0
    case TYPE_DATEV2:
361
0
        return "datev2";
362
0
    case TYPE_DATETIME:
363
0
        return "datetimev1";
364
0
    case TYPE_DATETIMEV2:
365
0
        [[fallthrough]];
366
0
    case TYPE_TIMEV2: {
367
0
        buffer << "datetimev2(" << type->get_scale() << ")";
368
0
        return buffer.str();
369
0
    }
370
4
    case TYPE_TIMESTAMP_NS:
371
4
        return "timestamp_ns";
372
0
    case TYPE_TIMESTAMPTZ: {
373
0
        buffer << "timestamptz(" << type->get_scale() << ")";
374
0
        return buffer.str();
375
0
    }
376
0
    case TYPE_BINARY:
377
0
        return "binary";
378
0
    case TYPE_DECIMALV2: {
379
0
        buffer << "decimalv2(" << DecimalV2Value::PRECISION << "," << DecimalV2Value::SCALE << ")";
380
0
        return buffer.str();
381
0
    }
382
0
    case TYPE_DECIMAL32: {
383
0
        buffer << "decimal32(" << type->get_precision() << "," << type->get_scale() << ")";
384
0
        return buffer.str();
385
0
    }
386
0
    case TYPE_DECIMAL64: {
387
0
        buffer << "decimal64(" << type->get_precision() << "," << type->get_scale() << ")";
388
0
        return buffer.str();
389
0
    }
390
0
    case TYPE_DECIMAL128I: {
391
0
        buffer << "decimal128(" << type->get_precision() << "," << type->get_scale() << ")";
392
0
        return buffer.str();
393
0
    }
394
1
    case TYPE_STRUCT: {
395
1
        const DataTypeStruct* struct_type = reinterpret_cast<const DataTypeStruct*>(type.get());
396
1
        buffer << "struct<";
397
4
        for (int i = 0; i < struct_type->get_elements().size(); ++i) {
398
3
            if (i != 0) {
399
2
                buffer << ",";
400
2
            }
401
3
            buffer << struct_type->get_element_names()[i] << ":"
402
3
                   << get_jni_type(struct_type->get_element(i));
403
3
        }
404
1
        buffer << ">";
405
1
        return buffer.str();
406
0
    }
407
1
    case TYPE_ARRAY: {
408
1
        const DataTypeArray* array_type = reinterpret_cast<const DataTypeArray*>(type.get());
409
1
        buffer << "array<" << get_jni_type(array_type->get_nested_type()) << ">";
410
1
        return buffer.str();
411
0
    }
412
1
    case TYPE_MAP: {
413
1
        const DataTypeMap* map_type = reinterpret_cast<const DataTypeMap*>(type.get());
414
1
        buffer << "map<" << get_jni_type(map_type->get_key_type()) << ","
415
1
               << get_jni_type(map_type->get_value_type()) << ">";
416
1
        return buffer.str();
417
0
    }
418
0
    case TYPE_VARBINARY:
419
0
        return "varbinary";
420
3
    case TYPE_VARIANT:
421
3
        return "variant";
422
    // bitmap, hll, quantile_state, jsonb are transferred as strings via JNI
423
0
    case TYPE_BITMAP:
424
0
        [[fallthrough]];
425
0
    case TYPE_HLL:
426
0
        [[fallthrough]];
427
0
    case TYPE_QUANTILE_STATE:
428
0
        [[fallthrough]];
429
0
    case TYPE_JSONB:
430
0
        return "string";
431
0
    default:
432
0
        return "unsupported";
433
11
    }
434
11
}
435
436
18
std::string JniDataBridge::get_jni_type_with_different_string(const DataTypePtr& data_type) {
437
18
    DataTypePtr type = remove_nullable(data_type);
438
18
    std::ostringstream buffer;
439
18
    switch (data_type->get_primitive_type()) {
440
0
    case TYPE_BOOLEAN:
441
0
        return "boolean";
442
0
    case TYPE_TINYINT:
443
0
        return "tinyint";
444
0
    case TYPE_SMALLINT:
445
0
        return "smallint";
446
2
    case TYPE_INT:
447
2
        return "int";
448
2
    case TYPE_BIGINT:
449
2
        return "bigint";
450
0
    case TYPE_LARGEINT:
451
0
        return "largeint";
452
0
    case TYPE_FLOAT:
453
0
        return "float";
454
0
    case TYPE_DOUBLE:
455
0
        return "double";
456
0
    case TYPE_IPV4:
457
0
        return "ipv4";
458
0
    case TYPE_IPV6:
459
0
        return "ipv6";
460
0
    case TYPE_UUID:
461
0
        return "uuid";
462
0
    case TYPE_VARCHAR: {
463
0
        buffer << "varchar("
464
0
               << assert_cast<const DataTypeString*>(remove_nullable(data_type).get())->len()
465
0
               << ")";
466
0
        return buffer.str();
467
0
    }
468
0
    case TYPE_DATE:
469
0
        return "datev1";
470
0
    case TYPE_DATEV2:
471
0
        return "datev2";
472
0
    case TYPE_DATETIME:
473
0
        return "datetimev1";
474
0
    case TYPE_DATETIMEV2:
475
0
        [[fallthrough]];
476
0
    case TYPE_TIMEV2: {
477
0
        buffer << "datetimev2(" << data_type->get_scale() << ")";
478
0
        return buffer.str();
479
0
    }
480
0
    case TYPE_TIMESTAMP_NS:
481
0
        return "timestamp_ns";
482
0
    case TYPE_TIMESTAMPTZ: {
483
0
        buffer << "timestamptz(" << data_type->get_scale() << ")";
484
0
        return buffer.str();
485
0
    }
486
0
    case TYPE_BINARY:
487
0
        return "binary";
488
0
    case TYPE_CHAR: {
489
0
        buffer << "char("
490
0
               << assert_cast<const DataTypeString*>(remove_nullable(data_type).get())->len()
491
0
               << ")";
492
0
        return buffer.str();
493
0
    }
494
10
    case TYPE_STRING:
495
10
        return "string";
496
0
    case TYPE_VARBINARY:
497
0
        buffer << "varbinary("
498
0
               << assert_cast<const DataTypeVarbinary*>(remove_nullable(data_type).get())->len()
499
0
               << ")";
500
0
        return buffer.str();
501
3
    case TYPE_VARIANT:
502
3
        return "variant";
503
0
    case TYPE_DECIMALV2: {
504
0
        buffer << "decimalv2(" << DecimalV2Value::PRECISION << "," << DecimalV2Value::SCALE << ")";
505
0
        return buffer.str();
506
0
    }
507
0
    case TYPE_DECIMAL32: {
508
0
        buffer << "decimal32(" << data_type->get_precision() << "," << data_type->get_scale()
509
0
               << ")";
510
0
        return buffer.str();
511
0
    }
512
0
    case TYPE_DECIMAL64: {
513
0
        buffer << "decimal64(" << data_type->get_precision() << "," << data_type->get_scale()
514
0
               << ")";
515
0
        return buffer.str();
516
0
    }
517
0
    case TYPE_DECIMAL128I: {
518
0
        buffer << "decimal128(" << data_type->get_precision() << "," << data_type->get_scale()
519
0
               << ")";
520
0
        return buffer.str();
521
0
    }
522
1
    case TYPE_STRUCT: {
523
1
        const auto* type_struct =
524
1
                assert_cast<const DataTypeStruct*>(remove_nullable(data_type).get());
525
1
        buffer << "struct<";
526
2
        for (int i = 0; i < type_struct->get_elements().size(); ++i) {
527
1
            if (i != 0) {
528
0
                buffer << ",";
529
0
            }
530
1
            buffer << type_struct->get_element_name(i) << ":"
531
1
                   << get_jni_type_with_different_string(type_struct->get_element(i));
532
1
        }
533
1
        buffer << ">";
534
1
        return buffer.str();
535
0
    }
536
0
    case TYPE_ARRAY: {
537
0
        const auto* type_arr = assert_cast<const DataTypeArray*>(remove_nullable(data_type).get());
538
0
        buffer << "array<" << get_jni_type_with_different_string(type_arr->get_nested_type())
539
0
               << ">";
540
0
        return buffer.str();
541
0
    }
542
0
    case TYPE_MAP: {
543
0
        const auto* type_map = assert_cast<const DataTypeMap*>(remove_nullable(data_type).get());
544
0
        buffer << "map<" << get_jni_type_with_different_string(type_map->get_key_type()) << ","
545
0
               << get_jni_type_with_different_string(type_map->get_value_type()) << ">";
546
0
        return buffer.str();
547
0
    }
548
    // bitmap, hll, quantile_state, jsonb are transferred as strings via JNI
549
0
    case TYPE_BITMAP:
550
0
        [[fallthrough]];
551
0
    case TYPE_HLL:
552
0
        [[fallthrough]];
553
0
    case TYPE_QUANTILE_STATE:
554
0
        [[fallthrough]];
555
0
    case TYPE_JSONB:
556
0
        return "string";
557
0
    default:
558
0
        return "unsupported";
559
18
    }
560
18
}
561
562
11
std::string JniDataBridge::encode_schema_values(const std::vector<std::string>& values) {
563
11
    std::vector<std::string> encoded_values;
564
11
    encoded_values.reserve(values.size());
565
17
    for (const auto& value : values) {
566
17
        std::string encoded;
567
17
        base64_encode(value, &encoded);
568
        // Prefix every element so an empty Base64 token remains distinct from an empty list.
569
17
        encoded_values.emplace_back("$" + encoded);
570
17
    }
571
11
    return join(encoded_values, ",");
572
11
}
573
574
10
std::string JniDataBridge::get_jni_type_with_encoded_struct_fields(const DataTypePtr& data_type) {
575
10
    switch (data_type->get_primitive_type()) {
576
2
    case TYPE_STRUCT: {
577
2
        const auto* type_struct =
578
2
                assert_cast<const DataTypeStruct*>(remove_nullable(data_type).get());
579
2
        std::ostringstream buffer;
580
2
        buffer << "struct<";
581
6
        for (int i = 0; i < type_struct->get_elements().size(); ++i) {
582
4
            if (i != 0) {
583
2
                buffer << ",";
584
2
            }
585
4
            std::string encoded_name;
586
4
            base64_encode(type_struct->get_element_name(i), &encoded_name);
587
            // '$' versions the nested-name token and cannot collide with Base64 or grammar
588
            // delimiters; Java rejects an unversioned name in the encoded schema path.
589
4
            buffer << "$" << encoded_name << ":"
590
4
                   << get_jni_type_with_encoded_struct_fields(type_struct->get_element(i));
591
4
        }
592
2
        buffer << ">";
593
2
        return buffer.str();
594
0
    }
595
0
    case TYPE_ARRAY: {
596
0
        const auto* type_array =
597
0
                assert_cast<const DataTypeArray*>(remove_nullable(data_type).get());
598
0
        return "array<" + get_jni_type_with_encoded_struct_fields(type_array->get_nested_type()) +
599
0
               ">";
600
0
    }
601
0
    case TYPE_MAP: {
602
0
        const auto* type_map = assert_cast<const DataTypeMap*>(remove_nullable(data_type).get());
603
0
        return "map<" + get_jni_type_with_encoded_struct_fields(type_map->get_key_type()) + "," +
604
0
               get_jni_type_with_encoded_struct_fields(type_map->get_value_type()) + ">";
605
0
    }
606
8
    default:
607
8
        return get_jni_type_with_different_string(data_type);
608
10
    }
609
10
}
610
611
Status JniDataBridge::_fill_column_meta(const ColumnPtr& doris_column, const DataTypePtr& data_type,
612
0
                                        std::vector<long>& meta_data) {
613
0
    auto logical_type = data_type->get_primitive_type();
614
0
    const IColumn* column = nullptr;
615
    // insert const flag
616
0
    if (is_column_const(*doris_column)) {
617
0
        meta_data.emplace_back((long)1);
618
0
        const auto& const_column = assert_cast<const ColumnConst&>(*doris_column);
619
0
        column = &(const_column.get_data_column());
620
0
    } else {
621
0
        meta_data.emplace_back((long)0);
622
0
        column = &(*doris_column);
623
0
    }
624
625
    // insert null map address
626
0
    const IColumn* data_column = nullptr;
627
0
    if (const auto* nullable_column = check_and_get_column<ColumnNullable>(column)) {
628
0
        data_column = &(nullable_column->get_nested_column());
629
0
        const auto& null_map = nullable_column->get_null_map_data();
630
0
        meta_data.emplace_back((long)null_map.data());
631
0
    } else {
632
0
        meta_data.emplace_back(0);
633
0
        data_column = column;
634
0
    }
635
0
    switch (logical_type) {
636
0
#define DISPATCH(TYPE_INDEX, COLUMN_TYPE, CPP_TYPE)                                          \
637
0
    case TYPE_INDEX: {                                                                       \
638
0
        meta_data.emplace_back(_get_fixed_length_column_address<COLUMN_TYPE>(*data_column)); \
639
0
        break;                                                                               \
640
0
    }
641
0
        FOR_FIXED_LENGTH_TYPES(DISPATCH)
642
0
#undef DISPATCH
643
0
    case PrimitiveType::TYPE_STRING:
644
0
        [[fallthrough]];
645
0
    case PrimitiveType::TYPE_CHAR:
646
0
        [[fallthrough]];
647
0
    case PrimitiveType::TYPE_VARCHAR: {
648
0
        const auto& string_column = assert_cast<const ColumnString&>(*data_column);
649
        // insert offsets
650
0
        meta_data.emplace_back((long)string_column.get_offsets().data());
651
0
        meta_data.emplace_back((long)string_column.get_chars().data());
652
0
        break;
653
0
    }
654
0
    case PrimitiveType::TYPE_ARRAY: {
655
0
        const auto& element_column = assert_cast<const ColumnArray&>(*data_column).get_data_ptr();
656
0
        meta_data.emplace_back(
657
0
                (long)assert_cast<const ColumnArray&>(*data_column).get_offsets().data());
658
0
        const auto& element_type =
659
0
                (assert_cast<const DataTypeArray*>(remove_nullable(data_type).get()))
660
0
                        ->get_nested_type();
661
0
        RETURN_IF_ERROR(_fill_column_meta(element_column, element_type, meta_data));
662
0
        break;
663
0
    }
664
0
    case PrimitiveType::TYPE_STRUCT: {
665
0
        const auto& doris_struct = assert_cast<const ColumnStruct&>(*data_column);
666
0
        const auto* doris_struct_type =
667
0
                assert_cast<const DataTypeStruct*>(remove_nullable(data_type).get());
668
0
        for (int i = 0; i < doris_struct.tuple_size(); ++i) {
669
0
            const auto& struct_field = doris_struct.get_column_ptr(i);
670
0
            const auto& field_type = doris_struct_type->get_element(i);
671
0
            RETURN_IF_ERROR(_fill_column_meta(struct_field, field_type, meta_data));
672
0
        }
673
0
        break;
674
0
    }
675
0
    case PrimitiveType::TYPE_MAP: {
676
0
        const auto& map = assert_cast<const ColumnMap&>(*data_column);
677
0
        const auto& key_type =
678
0
                assert_cast<const DataTypeMap*>(remove_nullable(data_type).get())->get_key_type();
679
0
        const auto& value_type =
680
0
                assert_cast<const DataTypeMap*>(remove_nullable(data_type).get())->get_value_type();
681
0
        const auto& key_column = map.get_keys_ptr();
682
0
        const auto& value_column = map.get_values_ptr();
683
0
        meta_data.emplace_back((long)map.get_offsets().data());
684
0
        RETURN_IF_ERROR(_fill_column_meta(key_column, key_type, meta_data));
685
0
        RETURN_IF_ERROR(_fill_column_meta(value_column, value_type, meta_data));
686
0
        break;
687
0
    }
688
0
    case PrimitiveType::TYPE_VARBINARY: {
689
0
        const auto& varbinary_col = assert_cast<const ColumnVarbinary&>(*data_column);
690
0
        meta_data.emplace_back((long)varbinary_col.get_data().data());
691
0
        break;
692
0
    }
693
0
    default:
694
0
        return Status::InternalError("Unsupported type: {}", data_type->get_name());
695
0
    }
696
0
    return Status::OK();
697
0
}
698
699
0
Status JniDataBridge::to_java_table(Block* block, std::unique_ptr<long[]>& meta) {
700
0
    ColumnNumbers arguments;
701
0
    for (size_t i = 0; i < block->columns(); ++i) {
702
0
        arguments.emplace_back(i);
703
0
    }
704
0
    return to_java_table(block, block->rows(), arguments, meta);
705
0
}
706
707
Status JniDataBridge::to_java_table(Block* block, size_t num_rows, const ColumnNumbers& arguments,
708
0
                                    std::unique_ptr<long[]>& meta) {
709
0
    std::vector<long> meta_data;
710
    // insert number of rows
711
0
    meta_data.emplace_back(num_rows);
712
0
    for (size_t i : arguments) {
713
0
        auto& column_with_type_and_name = block->get_by_position(i);
714
0
        RETURN_IF_ERROR(_fill_column_meta(column_with_type_and_name.column,
715
0
                                          column_with_type_and_name.type, meta_data));
716
0
    }
717
718
0
    meta.reset(new long[meta_data.size()]);
719
0
    memcpy(meta.get(), &meta_data[0], meta_data.size() * 8);
720
0
    return Status::OK();
721
0
}
722
723
std::pair<std::string, std::string> JniDataBridge::parse_table_schema(
724
0
        Block* block, const ColumnNumbers& arguments, bool ignore_column_name) {
725
    // prepare table schema
726
0
    std::ostringstream required_fields;
727
0
    std::ostringstream columns_types;
728
0
    for (int i = 0; i < arguments.size(); ++i) {
729
0
        std::string type = JniDataBridge::get_jni_type(block->get_by_position(arguments[i]).type);
730
0
        if (i == 0) {
731
0
            if (ignore_column_name) {
732
0
                required_fields << "_col_" << arguments[i];
733
0
            } else {
734
0
                required_fields << block->get_by_position(arguments[i]).name;
735
0
            }
736
0
            columns_types << type;
737
0
        } else {
738
0
            if (ignore_column_name) {
739
0
                required_fields << ","
740
0
                                << "_col_" << arguments[i];
741
0
            } else {
742
0
                required_fields << "," << block->get_by_position(arguments[i]).name;
743
0
            }
744
0
            columns_types << "#" << type;
745
0
        }
746
0
    }
747
0
    return std::make_pair(required_fields.str(), columns_types.str());
748
0
}
749
750
0
std::pair<std::string, std::string> JniDataBridge::parse_table_schema(Block* block) {
751
0
    ColumnNumbers arguments;
752
0
    for (size_t i = 0; i < block->columns(); ++i) {
753
0
        arguments.emplace_back(i);
754
0
    }
755
0
    return parse_table_schema(block, arguments, true);
756
0
}
757
758
} // namespace doris