ClickHouse
724 строки · 24.8 Кб
1#include <Columns/ColumnDecimal.h>
2#include <Columns/ColumnFixedString.h>
3#include <Columns/ColumnString.h>
4#include <Columns/ColumnVector.h>
5#include <Columns/ColumnsNumber.h>
6#include <Common/BitHelpers.h>
7#include <Common/BinStringDecodeHelper.h>
8#include <DataTypes/DataTypeString.h>
9#include <Functions/FunctionFactory.h>
10#include <Functions/IFunction.h>
11#include <IO/WriteHelpers.h>
12#include <Interpreters/Context_fwd.h>
13#include <Interpreters/castColumn.h>
14
15namespace DB
16{
17
18namespace ErrorCodes
19{
20extern const int ILLEGAL_TYPE_OF_ARGUMENT;
21extern const int LOGICAL_ERROR;
22extern const int ILLEGAL_COLUMN;
23}
24
25/*
26* hex(x) - Returns hexadecimal representation; capital letters; there are no prefixes 0x or suffixes h.
27* For numbers, returns a variable-length string - hex in the "human" (big endian) format, with the leading zeros being cut,
28* but only by whole bytes. For dates and datetimes - the same as for numbers.
29* For example, hex(257) = '0101'.
30*
31* unhex(string) - Returns a string, hex of which is equal to `string` with regard of case and discarding one leading zero.
32* If such a string does not exist, could return arbitrary implementation specific value.
33*
34* bin(x) - Returns binary representation.
35*
36* unbin(x) - Returns a string, opposite to `bin`.
37*
38*/
39
40struct HexImpl
41{
42static constexpr auto name = "hex";
43static constexpr size_t word_size = 2;
44
45template <typename T>
46static void executeOneUIntOrInt(T x, char *& out, bool skip_leading_zero = true, bool auto_close = true)
47{
48bool was_nonzero = false;
49for (int offset = (sizeof(T) - 1) * 8; offset >= 0; offset -= 8)
50{
51UInt8 byte = x >> offset;
52
53/// Skip leading zeros
54if (byte == 0 && !was_nonzero && offset && skip_leading_zero)
55continue;
56
57was_nonzero = true;
58writeHexByteUppercase(byte, out);
59out += word_size;
60}
61if (auto_close)
62{
63*out = '\0';
64++out;
65}
66}
67
68static void executeOneString(const UInt8 * pos, const UInt8 * end, char *& out, bool reverse_order = false)
69{
70if (!reverse_order)
71{
72while (pos < end)
73{
74writeHexByteUppercase(*pos, out);
75++pos;
76out += word_size;
77}
78}
79else
80{
81const auto * start_pos = pos;
82pos = end - 1;
83while (pos >= start_pos)
84{
85writeHexByteUppercase(*pos, out);
86--pos;
87out += word_size;
88}
89}
90*out = '\0';
91++out;
92}
93
94template <typename T>
95static void executeFloatAndDecimal(const T & in_vec, ColumnPtr & col_res, const size_t type_size_in_bytes)
96{
97const size_t hex_length = type_size_in_bytes * word_size + 1; /// Including trailing zero byte.
98auto col_str = ColumnString::create();
99
100ColumnString::Chars & out_vec = col_str->getChars();
101ColumnString::Offsets & out_offsets = col_str->getOffsets();
102
103size_t size = in_vec.size();
104out_offsets.resize(size);
105out_vec.resize(size * hex_length);
106
107size_t pos = 0;
108char * out = reinterpret_cast<char *>(out_vec.data());
109for (size_t i = 0; i < size; ++i)
110{
111const UInt8 * in_pos = reinterpret_cast<const UInt8 *>(&in_vec[i]);
112bool reverse_order = (std::endian::native == std::endian::big);
113executeOneString(in_pos, in_pos + type_size_in_bytes, out, reverse_order);
114
115pos += hex_length;
116out_offsets[i] = pos;
117}
118col_res = std::move(col_str);
119}
120};
121
122struct UnhexImpl
123{
124static constexpr auto name = "unhex";
125static constexpr size_t word_size = 2;
126
127static void decode(const char * pos, const char * end, char *& out)
128{
129hexStringDecode(pos, end, out, word_size);
130}
131};
132
133struct BinImpl
134{
135static constexpr auto name = "bin";
136static constexpr size_t word_size = 8;
137
138template <typename T>
139static void executeOneUIntOrInt(T x, char *& out, bool skip_leading_zero = true, bool auto_close = true)
140{
141bool was_nonzero = false;
142for (int offset = (sizeof(T) - 1) * 8; offset >= 0; offset -= 8)
143{
144UInt8 byte = x >> offset;
145
146/// Skip leading zeros
147if (byte == 0 && !was_nonzero && offset && skip_leading_zero)
148continue;
149
150was_nonzero = true;
151writeBinByte(byte, out);
152out += word_size;
153}
154if (auto_close)
155{
156*out = '\0';
157++out;
158}
159}
160
161template <typename T>
162static void executeFloatAndDecimal(const T & in_vec, ColumnPtr & col_res, const size_t type_size_in_bytes)
163{
164const size_t hex_length = type_size_in_bytes * word_size + 1; /// Including trailing zero byte.
165auto col_str = ColumnString::create();
166
167ColumnString::Chars & out_vec = col_str->getChars();
168ColumnString::Offsets & out_offsets = col_str->getOffsets();
169
170size_t size = in_vec.size();
171out_offsets.resize(size);
172out_vec.resize(size * hex_length);
173
174size_t pos = 0;
175char * out = reinterpret_cast<char *>(out_vec.data());
176for (size_t i = 0; i < size; ++i)
177{
178const UInt8 * in_pos = reinterpret_cast<const UInt8 *>(&in_vec[i]);
179
180bool reverse_order = (std::endian::native == std::endian::big);
181executeOneString(in_pos, in_pos + type_size_in_bytes, out, reverse_order);
182
183pos += hex_length;
184out_offsets[i] = pos;
185}
186col_res = std::move(col_str);
187}
188
189static void executeOneString(const UInt8 * pos, const UInt8 * end, char *& out, bool reverse_order = false)
190{
191if (!reverse_order)
192{
193while (pos < end)
194{
195writeBinByte(*pos, out);
196++pos;
197out += word_size;
198}
199}
200else
201{
202const auto * start_pos = pos;
203pos = end - 1;
204while (pos >= start_pos)
205{
206writeBinByte(*pos, out);
207--pos;
208out += word_size;
209}
210}
211*out = '\0';
212++out;
213}
214};
215
216struct UnbinImpl
217{
218static constexpr auto name = "unbin";
219static constexpr size_t word_size = 8;
220
221static void decode(const char * pos, const char * end, char *& out)
222{
223binStringDecode(pos, end, out);
224}
225};
226
227/// Encode number or string to string with binary or hexadecimal representation
228template <typename Impl>
229class EncodeToBinaryRepresentation : public IFunction
230{
231public:
232static constexpr auto name = Impl::name;
233static constexpr size_t word_size = Impl::word_size;
234
235static FunctionPtr create(ContextPtr) { return std::make_shared<EncodeToBinaryRepresentation>(); }
236
237String getName() const override { return name; }
238
239size_t getNumberOfArguments() const override { return 1; }
240
241bool useDefaultImplementationForConstants() const override { return true; }
242
243bool isInjective(const ColumnsWithTypeAndName &) const override { return true; }
244
245bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
246
247DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
248{
249WhichDataType which(arguments[0]);
250
251if (!which.isStringOrFixedString() &&
252!which.isDate() &&
253!which.isDateTime() &&
254!which.isDateTime64() &&
255!which.isUInt() &&
256!which.isInt() &&
257!which.isFloat() &&
258!which.isDecimal() &&
259!which.isUUID() &&
260!which.isIPv4() &&
261!which.isIPv6() &&
262!which.isAggregateFunction())
263throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}",
264arguments[0]->getName(), getName());
265
266return std::make_shared<DataTypeString>();
267}
268
269ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override
270{
271const IColumn * column = arguments[0].column.get();
272ColumnPtr res_column;
273
274WhichDataType which(column->getDataType());
275if (which.isAggregateFunction())
276{
277const ColumnPtr to_string = castColumn(arguments[0], std::make_shared<DataTypeString>());
278const auto * str_column = checkAndGetColumn<ColumnString>(to_string.get());
279tryExecuteString(str_column, res_column);
280return res_column;
281}
282
283if (tryExecuteUIntOrInt<UInt8>(column, res_column) ||
284tryExecuteUIntOrInt<UInt16>(column, res_column) ||
285tryExecuteUIntOrInt<UInt32>(column, res_column) ||
286tryExecuteUIntOrInt<UInt64>(column, res_column) ||
287tryExecuteUIntOrInt<UInt128>(column, res_column) ||
288tryExecuteUIntOrInt<UInt256>(column, res_column) ||
289tryExecuteUIntOrInt<Int8>(column, res_column) ||
290tryExecuteUIntOrInt<Int16>(column, res_column) ||
291tryExecuteUIntOrInt<Int32>(column, res_column) ||
292tryExecuteUIntOrInt<Int64>(column, res_column) ||
293tryExecuteUIntOrInt<Int128>(column, res_column) ||
294tryExecuteUIntOrInt<Int256>(column, res_column) ||
295tryExecuteString(column, res_column) ||
296tryExecuteFixedString(column, res_column) ||
297tryExecuteFloat<Float32>(column, res_column) ||
298tryExecuteFloat<Float64>(column, res_column) ||
299tryExecuteDecimal<Decimal32>(column, res_column) ||
300tryExecuteDecimal<Decimal64>(column, res_column) ||
301tryExecuteDecimal<Decimal128>(column, res_column) ||
302tryExecuteDecimal<Decimal256>(column, res_column) ||
303tryExecuteUUID(column, res_column) ||
304tryExecuteIPv4(column, res_column) ||
305tryExecuteIPv6(column, res_column))
306return res_column;
307
308throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}",
309arguments[0].column->getName(), getName());
310}
311
312template <typename T>
313bool tryExecuteUIntOrInt(const IColumn * col, ColumnPtr & col_res) const
314{
315const ColumnVector<T> * col_vec = checkAndGetColumn<ColumnVector<T>>(col);
316
317static constexpr size_t MAX_LENGTH = sizeof(T) * word_size + 1; /// Including trailing zero byte.
318
319if (col_vec)
320{
321auto col_str = ColumnString::create();
322ColumnString::Chars & out_vec = col_str->getChars();
323ColumnString::Offsets & out_offsets = col_str->getOffsets();
324
325const typename ColumnVector<T>::Container & in_vec = col_vec->getData();
326
327size_t size = in_vec.size();
328out_offsets.resize(size);
329out_vec.resize(size * (word_size+1) + MAX_LENGTH); /// word_size+1 is length of one byte in hex/bin plus zero byte.
330
331size_t pos = 0;
332for (size_t i = 0; i < size; ++i)
333{
334/// Manual exponential growth, so as not to rely on the linear amortized work time of `resize` (no one guarantees it).
335if (pos + MAX_LENGTH > out_vec.size())
336out_vec.resize(out_vec.size() * word_size + MAX_LENGTH);
337
338char * begin = reinterpret_cast<char *>(&out_vec[pos]);
339char * end = begin;
340Impl::executeOneUIntOrInt(in_vec[i], end);
341
342pos += end - begin;
343out_offsets[i] = pos;
344}
345out_vec.resize(pos);
346
347col_res = std::move(col_str);
348return true;
349}
350else
351{
352return false;
353}
354}
355
356bool tryExecuteString(const IColumn *col, ColumnPtr &col_res) const
357{
358const ColumnString * col_str_in = checkAndGetColumn<ColumnString>(col);
359
360if (col_str_in)
361{
362auto col_str = ColumnString::create();
363ColumnString::Chars & out_vec = col_str->getChars();
364ColumnString::Offsets & out_offsets = col_str->getOffsets();
365
366const ColumnString::Chars & in_vec = col_str_in->getChars();
367const ColumnString::Offsets & in_offsets = col_str_in->getOffsets();
368
369size_t size = in_offsets.size();
370
371out_offsets.resize(size);
372/// reserve `word_size` bytes for each non trailing zero byte from input + `size` bytes for trailing zeros
373out_vec.resize((in_vec.size() - size) * word_size + size);
374
375char * begin = reinterpret_cast<char *>(out_vec.data());
376char * pos = begin;
377size_t prev_offset = 0;
378
379for (size_t i = 0; i < size; ++i)
380{
381size_t new_offset = in_offsets[i];
382
383Impl::executeOneString(&in_vec[prev_offset], &in_vec[new_offset - 1], pos);
384
385out_offsets[i] = pos - begin;
386
387prev_offset = new_offset;
388}
389if (!out_offsets.empty() && out_offsets.back() != out_vec.size())
390throw Exception(ErrorCodes::LOGICAL_ERROR, "Column size mismatch (internal logical error)");
391
392col_res = std::move(col_str);
393return true;
394}
395else
396{
397return false;
398}
399}
400
401template <typename T>
402bool tryExecuteDecimal(const IColumn * col, ColumnPtr & col_res) const
403{
404const ColumnDecimal<T> * col_dec = checkAndGetColumn<ColumnDecimal<T>>(col);
405if (col_dec)
406{
407const typename ColumnDecimal<T>::Container & in_vec = col_dec->getData();
408Impl::executeFloatAndDecimal(in_vec, col_res, sizeof(T));
409return true;
410}
411else
412{
413return false;
414}
415}
416
417static bool tryExecuteFixedString(const IColumn * col, ColumnPtr & col_res)
418{
419const ColumnFixedString * col_fstr_in = checkAndGetColumn<ColumnFixedString>(col);
420
421if (col_fstr_in)
422{
423auto col_str = ColumnString::create();
424ColumnString::Chars & out_vec = col_str->getChars();
425ColumnString::Offsets & out_offsets = col_str->getOffsets();
426
427const ColumnString::Chars & in_vec = col_fstr_in->getChars();
428
429size_t size = col_fstr_in->size();
430
431out_offsets.resize(size);
432out_vec.resize(in_vec.size() * word_size + size);
433
434char * begin = reinterpret_cast<char *>(out_vec.data());
435char * pos = begin;
436
437size_t n = col_fstr_in->getN();
438
439size_t prev_offset = 0;
440
441for (size_t i = 0; i < size; ++i)
442{
443size_t new_offset = prev_offset + n;
444
445Impl::executeOneString(&in_vec[prev_offset], &in_vec[new_offset], pos);
446
447out_offsets[i] = pos - begin;
448prev_offset = new_offset;
449}
450
451if (!out_offsets.empty() && out_offsets.back() != out_vec.size())
452throw Exception(ErrorCodes::LOGICAL_ERROR, "Column size mismatch (internal logical error)");
453
454col_res = std::move(col_str);
455return true;
456}
457else
458{
459return false;
460}
461}
462
463template <typename T>
464bool tryExecuteFloat(const IColumn * col, ColumnPtr & col_res) const
465{
466const ColumnVector<T> * col_vec = checkAndGetColumn<ColumnVector<T>>(col);
467if (col_vec)
468{
469const typename ColumnVector<T>::Container & in_vec = col_vec->getData();
470Impl::executeFloatAndDecimal(in_vec, col_res, sizeof(T));
471return true;
472}
473else
474{
475return false;
476}
477}
478
479bool tryExecuteUUID(const IColumn * col, ColumnPtr & col_res) const
480{
481const ColumnUUID * col_vec = checkAndGetColumn<ColumnUUID>(col);
482
483static constexpr size_t MAX_LENGTH = sizeof(UUID) * word_size + 1; /// Including trailing zero byte.
484
485if (col_vec)
486{
487auto col_str = ColumnString::create();
488ColumnString::Chars & out_vec = col_str->getChars();
489ColumnString::Offsets & out_offsets = col_str->getOffsets();
490
491const typename ColumnUUID::Container & in_vec = col_vec->getData();
492const UUID* uuid = in_vec.data();
493
494size_t size = in_vec.size();
495out_offsets.resize(size);
496out_vec.resize(size * (word_size+1) + MAX_LENGTH); /// word_size+1 is length of one byte in hex/bin plus zero byte.
497
498size_t pos = 0;
499for (size_t i = 0; i < size; ++i)
500{
501/// Manual exponential growth, so as not to rely on the linear amortized work time of `resize` (no one guarantees it).
502if (pos + MAX_LENGTH > out_vec.size())
503out_vec.resize(out_vec.size() * word_size + MAX_LENGTH);
504
505char * begin = reinterpret_cast<char *>(&out_vec[pos]);
506char * end = begin;
507
508// use executeOnUInt instead of using executeOneString
509// because the latter one outputs the string in the memory order
510Impl::executeOneUIntOrInt(UUIDHelpers::getHighBytes(uuid[i]), end, false, false);
511Impl::executeOneUIntOrInt(UUIDHelpers::getLowBytes(uuid[i]), end, false, true);
512
513pos += end - begin;
514out_offsets[i] = pos;
515}
516out_vec.resize(pos);
517
518col_res = std::move(col_str);
519return true;
520}
521else
522{
523return false;
524}
525}
526
527bool tryExecuteIPv6(const IColumn * col, ColumnPtr & col_res) const
528{
529const ColumnIPv6 * col_vec = checkAndGetColumn<ColumnIPv6>(col);
530
531static constexpr size_t MAX_LENGTH = sizeof(IPv6) * word_size + 1; /// Including trailing zero byte.
532
533if (!col_vec)
534return false;
535
536auto col_str = ColumnString::create();
537ColumnString::Chars & out_vec = col_str->getChars();
538ColumnString::Offsets & out_offsets = col_str->getOffsets();
539
540const typename ColumnIPv6::Container & in_vec = col_vec->getData();
541const IPv6* ip = in_vec.data();
542
543size_t size = in_vec.size();
544out_offsets.resize(size);
545out_vec.resize(size * (word_size+1) + MAX_LENGTH); /// word_size+1 is length of one byte in hex/bin plus zero byte.
546
547size_t pos = 0;
548for (size_t i = 0; i < size; ++i)
549{
550/// Manual exponential growth, so as not to rely on the linear amortized work time of `resize` (no one guarantees it).
551if (pos + MAX_LENGTH > out_vec.size())
552out_vec.resize(out_vec.size() * word_size + MAX_LENGTH);
553
554char * begin = reinterpret_cast<char *>(&out_vec[pos]);
555char * end = begin;
556
557Impl::executeOneString(reinterpret_cast<const UInt8 *>(&ip[i].toUnderType().items[0]), reinterpret_cast<const UInt8 *>(&ip[i].toUnderType().items[2]), end);
558
559pos += end - begin;
560out_offsets[i] = pos;
561}
562out_vec.resize(pos);
563
564col_res = std::move(col_str);
565return true;
566}
567
568bool tryExecuteIPv4(const IColumn * col, ColumnPtr & col_res) const
569{
570const ColumnIPv4 * col_vec = checkAndGetColumn<ColumnIPv4>(col);
571
572static constexpr size_t MAX_LENGTH = sizeof(IPv4) * word_size + 1; /// Including trailing zero byte.
573
574if (!col_vec)
575return false;
576
577auto col_str = ColumnString::create();
578ColumnString::Chars & out_vec = col_str->getChars();
579ColumnString::Offsets & out_offsets = col_str->getOffsets();
580
581const typename ColumnIPv4::Container & in_vec = col_vec->getData();
582const IPv4* ip = in_vec.data();
583
584size_t size = in_vec.size();
585out_offsets.resize(size);
586out_vec.resize(size * (word_size+1) + MAX_LENGTH); /// word_size+1 is length of one byte in hex/bin plus zero byte.
587
588size_t pos = 0;
589for (size_t i = 0; i < size; ++i)
590{
591/// Manual exponential growth, so as not to rely on the linear amortized work time of `resize` (no one guarantees it).
592if (pos + MAX_LENGTH > out_vec.size())
593out_vec.resize(out_vec.size() * word_size + MAX_LENGTH);
594
595char * begin = reinterpret_cast<char *>(&out_vec[pos]);
596char * end = begin;
597
598Impl::executeOneUIntOrInt(ip[i].toUnderType(), end);
599
600pos += end - begin;
601out_offsets[i] = pos;
602}
603out_vec.resize(pos);
604
605col_res = std::move(col_str);
606return true;
607}
608};
609
610/// Decode number or string from string with binary or hexadecimal representation
611template <typename Impl>
612class DecodeFromBinaryRepresentation : public IFunction
613{
614public:
615static constexpr auto name = Impl::name;
616static constexpr size_t word_size = Impl::word_size;
617static FunctionPtr create(ContextPtr) { return std::make_shared<DecodeFromBinaryRepresentation>(); }
618
619String getName() const override { return name; }
620
621size_t getNumberOfArguments() const override { return 1; }
622bool isInjective(const ColumnsWithTypeAndName &) const override { return true; }
623
624bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
625
626DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
627{
628WhichDataType which(arguments[0]);
629if (!which.isStringOrFixedString())
630throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}",
631arguments[0]->getName(), getName());
632
633return std::make_shared<DataTypeString>();
634}
635
636bool useDefaultImplementationForConstants() const override { return true; }
637
638ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override
639{
640const ColumnPtr & column = arguments[0].column;
641
642if (const ColumnString * col = checkAndGetColumn<ColumnString>(column.get()))
643{
644auto col_res = ColumnString::create();
645
646ColumnString::Chars & out_vec = col_res->getChars();
647ColumnString::Offsets & out_offsets = col_res->getOffsets();
648
649const ColumnString::Chars & in_vec = col->getChars();
650const ColumnString::Offsets & in_offsets = col->getOffsets();
651
652size_t size = in_offsets.size();
653out_offsets.resize(size);
654out_vec.resize(in_vec.size() / word_size + size);
655
656char * begin = reinterpret_cast<char *>(out_vec.data());
657char * pos = begin;
658size_t prev_offset = 0;
659
660for (size_t i = 0; i < size; ++i)
661{
662size_t new_offset = in_offsets[i];
663
664Impl::decode(reinterpret_cast<const char *>(&in_vec[prev_offset]), reinterpret_cast<const char *>(&in_vec[new_offset - 1]), pos);
665
666out_offsets[i] = pos - begin;
667
668prev_offset = new_offset;
669}
670
671out_vec.resize(pos - begin);
672
673return col_res;
674}
675else if (const ColumnFixedString * col_fix_string = checkAndGetColumn<ColumnFixedString>(column.get()))
676{
677auto col_res = ColumnString::create();
678
679ColumnString::Chars & out_vec = col_res->getChars();
680ColumnString::Offsets & out_offsets = col_res->getOffsets();
681
682const ColumnString::Chars & in_vec = col_fix_string->getChars();
683size_t n = col_fix_string->getN();
684
685size_t size = col_fix_string->size();
686out_offsets.resize(size);
687out_vec.resize(in_vec.size() / word_size + size);
688
689char * begin = reinterpret_cast<char *>(out_vec.data());
690char * pos = begin;
691size_t prev_offset = 0;
692
693for (size_t i = 0; i < size; ++i)
694{
695size_t new_offset = prev_offset + n;
696
697Impl::decode(reinterpret_cast<const char *>(&in_vec[prev_offset]), reinterpret_cast<const char *>(&in_vec[new_offset]), pos);
698
699out_offsets[i] = pos - begin;
700
701prev_offset = new_offset;
702}
703
704out_vec.resize(pos - begin);
705
706return col_res;
707}
708else
709{
710throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}",
711arguments[0].column->getName(), getName());
712}
713}
714};
715
716REGISTER_FUNCTION(BinaryRepr)
717{
718factory.registerFunction<EncodeToBinaryRepresentation<HexImpl>>({}, FunctionFactory::CaseInsensitive);
719factory.registerFunction<DecodeFromBinaryRepresentation<UnhexImpl>>({}, FunctionFactory::CaseInsensitive);
720factory.registerFunction<EncodeToBinaryRepresentation<BinImpl>>({}, FunctionFactory::CaseInsensitive);
721factory.registerFunction<DecodeFromBinaryRepresentation<UnbinImpl>>({}, FunctionFactory::CaseInsensitive);
722}
723
724}
725