| 976 | inline String getColumnStringValue(const ColumnWithTypeAndName & argument) const { return argument.column->getDataAt(0).toString(); } |
| 977 | |
| 978 | ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override |
| 979 | { |
| 980 | String db_name = getColumnStringValue(arguments[0]); |
| 981 | String table_name = getColumnStringValue(arguments[1]); |
| 982 | String column_name = getColumnStringValue(arguments[2]); |
| 983 | if (db_name.empty() || table_name.empty() || column_name.empty()) |
| 984 | throw Exception("Bad arguments: database/table/column should not be empty", ErrorCodes::BAD_ARGUMENTS); |
| 985 | |
| 986 | String pattern; |
| 987 | if (arguments.size() >= 4) |
| 988 | { |
| 989 | pattern = getColumnStringValue(arguments[3]); |
| 990 | } |
| 991 | |
| 992 | /// Check byte map type |
| 993 | auto table_id = context->resolveStorageID({db_name, table_name}); |
| 994 | auto table = DatabaseCatalog::instance().getTable(table_id, context); |
| 995 | auto metadata_snapshot = table->getInMemoryMetadataPtr(); |
| 996 | if (!metadata_snapshot || !metadata_snapshot->columns.hasPhysical(column_name)) |
| 997 | throw Exception(ErrorCodes::BAD_ARGUMENTS, "Table {}.{} doesn't contain column {}", db_name, table_name, column_name); |
| 998 | auto type = metadata_snapshot->columns.getPhysical(column_name).type; |
| 999 | if (!type->isMap() || type->isKVMap()) |
| 1000 | throw Exception( |
| 1001 | ErrorCodes::BAD_ARGUMENTS, "Function getMapKeys must apply to ByteMap but given {}", type->isKVMap() ? "KV map" : type->getName()); |
| 1002 | |
| 1003 | /** |
| 1004 | SELECT groupUniqArrayArray(ks) AS keys FROM ( |
| 1005 | SELECT arrayMap(t -> t.2, arrayFilter(t -> t.1 = 'some_map', _map_column_keys)) AS ks |
| 1006 | FROM some_db.some_table WHERE match(_partition_id, '.*2020.*10.*10.*') |
| 1007 | ) SETTINGS early_limit_for_map_virtual_columns = 1, max_threads = 1 |
| 1008 | */ |
| 1009 | String inner_query = "SELECT arrayMap(t -> t.2, arrayFilter(t -> t.1 = '" + column_name + "', _map_column_keys)) AS ks" // |
| 1010 | + " FROM `" + db_name + "`.`" + table_name + "`" // |
| 1011 | + (pattern.empty() ? "" : " WHERE match(_partition_id, '" + pattern + "')"); |
| 1012 | String query = "SELECT groupUniqArrayArray(ks) AS keys FROM ( " + inner_query |
| 1013 | + " ) SETTINGS early_limit_for_map_virtual_columns = 1, max_threads = 1"; |
| 1014 | |
| 1015 | auto query_context = createContextForSubQuery(context); |
| 1016 | auto res = executeSubQueryWithOneRow(query, query_context, true); |
| 1017 | if (res) |
| 1018 | { |
| 1019 | // TODO(shiyuze): maybe add a new function to get result in different rows, just like arrayJoin(getMapKeys(xxx)) |
| 1020 | |
| 1021 | /// Total map key number in result may exceed max_array_size_as_field (for example, |
| 1022 | /// each pratition or bucket has different key sets), so we need to avoid calling |
| 1023 | /// ColumnArray::[] or ColumnArray::get() to get array as a Field here. |
| 1024 | return ColumnConst::create(res.getByName("keys").column, input_rows_count)->convertToFullColumnIfConst(); |
| 1025 | } |
| 1026 | else |
| 1027 | { |
| 1028 | return result_type->createColumnConst(input_rows_count, Array{})->convertToFullColumnIfConst(); |
| 1029 | } |
| 1030 | } |
| 1031 | |
| 1032 | /// override executeImplDryRun to prevent calling getMapKeys multi times under |
| 1033 | /// same txn id, which may cause worker create table multi times |
nothing calls this directly
no test coverage detected