ClickHouse-Encoder
view release on metacpan or search on metacpan
);
my $bin = $enc->encode([[99.99]]);
my $off = skip_header($bin);
is(unpack('l<', substr($bin, $off, 4)), 9999, 'Decimal(9,2) 99.99 = 9999');
}
# Test Decimal128
{
my $enc = ClickHouse::Encoder->new(
columns => [['v', 'Decimal128(2)']],
);
my $bin = $enc->encode([[123.45]]);
my $off = skip_header($bin);
my $lo = unpack('q<', substr($bin, $off, 8));
my $hi = unpack('q<', substr($bin, $off+8, 8));
is($lo, 12345, 'Decimal128(2) low part');
is($hi, 0, 'Decimal128(2) high part');
}
# Test Decimal128 negative
{
my $enc = ClickHouse::Encoder->new(
columns => [['v', 'Decimal128(2)']],
);
my $bin = $enc->encode([[-123.45]]);
my $off = skip_header($bin);
my $lo = unpack('q<', substr($bin, $off, 8));
my $hi = unpack('q<', substr($bin, $off+8, 8));
is($lo, -12345, 'Decimal128(2) negative low part');
is($hi, -1, 'Decimal128(2) negative high part (sign extension)');
}
# Test Nullable(Decimal32)
{
my $enc = ClickHouse::Encoder->new(
columns => [['v', 'Nullable(Decimal32(2))']],
);
my $bin = $enc->encode([[123.45], [undef]]);
ok(defined $bin, 'Nullable(Decimal32) encodes');
}
# Test Array(Decimal64)
{
my $enc = ClickHouse::Encoder->new(
columns => [['v', 'Array(Decimal64(2))']],
);
my $bin = $enc->encode([[[1.23, 4.56]]]);
ok(defined $bin, 'Array(Decimal64) encodes');
}
# Test UTF-8 String
{
use utf8;
my $enc = ClickHouse::Encoder->new(columns => [['v', 'String']]);
my $utf8_str = "ÐÑÐ¸Ð²ÐµÑ Ð¼Ð¸Ñ æ¥æ¬èª ð";
my $bin = $enc->encode([[$utf8_str]]);
ok(defined $bin, 'UTF-8 string encodes');
my $off = skip_header($bin);
my ($len, $off2) = read_varint($bin, $off);
my $decoded = substr($bin, $off2, $len);
utf8::decode($decoded);
is($decoded, $utf8_str, 'UTF-8 string content preserved');
}
# Test UTF-8 FixedString (truncation on byte boundary)
{
use utf8;
my $enc = ClickHouse::Encoder->new(columns => [['v', 'FixedString(6)']]);
# "ÐÑивеÑ" is 12 bytes in UTF-8, should truncate to 6 bytes
my $bin = $enc->encode([["ÐÑивеÑ"]]);
my $off = skip_header($bin);
is(length(substr($bin, $off)), 6, 'UTF-8 FixedString truncates by bytes');
}
# Test empty rows
{
my $enc = ClickHouse::Encoder->new(columns => [['v', 'UInt32']]);
my $bin = $enc->encode([]);
ok(defined $bin, 'empty rows encodes');
my ($ncols, $off) = read_varint($bin, 0);
my ($nrows, $off2) = read_varint($bin, $off);
is($nrows, 0, 'empty rows count is 0');
}
# Test Tuple error handling
{
my $enc = ClickHouse::Encoder->new(columns => [['v', 'Tuple(UInt8, UInt8)']]);
eval { $enc->encode([['not a tuple']]) };
like($@, qr/Expected arrayref or hashref for Tuple/, 'Tuple with scalar croaks');
}
# Test Date with string
{
my $enc = ClickHouse::Encoder->new(columns => [['v', 'Date']]);
my $bin = $enc->encode([['1970-01-01'], ['2000-01-01'], ['2024-06-15']]);
my $off = skip_header($bin);
is(unpack('v', substr($bin, $off, 2)), 0, 'Date 1970-01-01 = 0');
is(unpack('v', substr($bin, $off+2, 2)), 10957, 'Date 2000-01-01 = 10957');
is(unpack('v', substr($bin, $off+4, 2)), 19889, 'Date 2024-06-15 = 19889');
}
# Test Date with integer (days since epoch)
{
my $enc = ClickHouse::Encoder->new(columns => [['v', 'Date']]);
my $bin = $enc->encode([[0], [10957]]);
my $off = skip_header($bin);
is(unpack('v', substr($bin, $off, 2)), 0, 'Date numeric 0');
is(unpack('v', substr($bin, $off+2, 2)), 10957, 'Date numeric 10957');
}
# Test Date32 with string (supports wider range including negative)
{
my $enc = ClickHouse::Encoder->new(columns => [['v', 'Date32']]);
my $bin = $enc->encode([['1970-01-01'], ['1900-01-01']]);
my $off = skip_header($bin);
is(unpack('l<', substr($bin, $off, 4)), 0, 'Date32 1970-01-01 = 0');
# 1900-01-01 is before epoch, should be negative
my $days_1900 = unpack('l<', substr($bin, $off+4, 4));
ok($days_1900 < 0, 'Date32 1900-01-01 is negative');
}
# Test DateTime with string
( run in 0.993 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )