blob: 1e8dfdadebb73b85e3ca989a266e96209ada1be4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Apache.Ignite.Internal.Table.Serialization
{
using System;
using System.Collections.Generic;
using System.Linq;
using Common;
using Ignite.Table;
using Proto.BinaryTuple;
using Proto.MsgPack;
/// <summary>
/// Serializer handler for <see cref="IIgniteTuple"/>.
/// </summary>
internal sealed class TupleSerializerHandler : IRecordSerializerHandler<IIgniteTuple>
{
/// <summary>
/// Singleton instance.
/// </summary>
public static readonly IRecordSerializerHandler<IIgniteTuple> Instance = new TupleSerializerHandler();
/// <summary>
/// Initializes a new instance of the <see cref="TupleSerializerHandler"/> class.
/// </summary>
private TupleSerializerHandler()
{
// No-op.
}
/// <summary>
/// Reads tuple from the buffer.
/// </summary>
/// <param name="buf">Buffer.</param>
/// <param name="schema">Schema.</param>
/// <param name="keyOnly">Whether to read only the key columns.</param>
/// <returns>Tuple.</returns>
public static IgniteTuple ReadTuple(ReadOnlySpan<byte> buf, Schema schema, bool keyOnly)
{
var columns = schema.GetColumnsFor(keyOnly);
var tuple = new IgniteTuple(columns.Length);
var tupleReader = new BinaryTupleReader(buf, columns.Length);
foreach (var column in columns)
{
tuple[column.Name] = tupleReader.GetObject(column.GetBinaryTupleIndex(keyOnly), column.Type, column.Scale);
}
return tuple;
}
/// <summary>
/// Reads single column from the binary tuple.
/// </summary>
/// <param name="buf">Binary tuple buffer.</param>
/// <param name="schema">Schema.</param>
/// <param name="keyOnly">Whether <paramref name="buf"/> is a key-only binary tuple.</param>
/// <param name="index">Column index.</param>
/// <returns>Column value.</returns>
public static object? ReadObject(ReadOnlySpan<byte> buf, Schema schema, bool keyOnly, int index)
{
var columns = schema.GetColumnsFor(keyOnly);
var tupleReader = new BinaryTupleReader(buf, columns.Length);
var column = columns[index];
return tupleReader.GetObject(index, column.Type, column.Scale);
}
/// <inheritdoc/>
public IIgniteTuple Read(ref MsgPackReader reader, Schema schema, bool keyOnly = false) =>
new BinaryTupleIgniteTupleAdapter(
data: reader.ReadBinary().ToArray(),
schema: schema,
keyOnly);
/// <inheritdoc/>
public void Write(ref BinaryTupleBuilder tupleBuilder, IIgniteTuple record, Schema schema, bool keyOnly, Span<byte> noValueSet)
{
int written = 0;
var columns = keyOnly ? schema.KeyColumns : schema.Columns;
foreach (var col in columns)
{
var colIdx = record.GetOrdinal(col.Name);
if (colIdx >= 0)
{
tupleBuilder.AppendObject(record[colIdx], col.Type, col.Scale, col.Precision);
written++;
}
else
{
tupleBuilder.AppendNoValue(noValueSet);
}
}
ValidateMappedCount(record, schema, columns.Length, written);
}
private static void ValidateMappedCount(IIgniteTuple record, Schema schema, int columnCount, int written)
{
if (written == 0)
{
var columnStr = schema.Columns.Select(x => x.Type + " " + x.Name).StringJoin();
throw new ArgumentException($"Can't map '{record}' to columns '{columnStr}'. Matching fields not found.");
}
if (record.FieldCount > written)
{
var extraColumns = new HashSet<string>(record.FieldCount, StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < record.FieldCount; i++)
{
var name = record.GetName(i);
if (!extraColumns.Add(name))
{
throw new ArgumentException("Duplicate column in Tuple: " + name, nameof(record));
}
}
for (var i = 0; i < columnCount; i++)
{
extraColumns.Remove(schema.Columns[i].Name);
}
throw SerializerExceptionExtensions.GetUnmappedColumnsException("Tuple", schema, extraColumns);
}
}
}
}