我正在使用Flatbuffers序列化sql表中的行。我有一个Statement.fbs,它将一条语句定义为Insert,Update,Delete等。该语句具有成员“ Row”,该成员是所有sql表类型的并集。但是,我有超过255个表,并且用flatc编译时出现此错误:
example.com/image-name:tag
我查看了Flatbuffers代码,发现为联合类型自动创建了一个枚举,并且该枚举的基础类型为uint8_t。
我看不到任何更改此行为的选项。
我可以通过将基本类型指定为Flatbuffer架构文件中的uint16来创建一个处理所有表的枚举。
语句模式:
$ ~/flatbuffers/flatc --cpp -o gen Statement.fbs
error: /home/jkl/fbtest/allobjects.fbs:773: 18: error: enum value does not fit [0; 255]
allobjects的行联合有点大,可以包含在这里。
include "allobjects.fbs";
namespace Database;
enum StatementKind : byte { Unknown = 0, Insert, Update, Delete, Truncate }
table Statement {
kind:StatementKind;
truncate:[TableKind];
row:Row;
}
root_type Statement;
我想这是针对平面缓冲区的设计决策,即联合类型只能使用一个字节。我可以接受,但是我真的很想解决方法。
答案 0 :(得分:0)
可悲的是,这是一个设计错误,目前还没有解决方法。可以将其修复为可配置的,但是鉴于依赖于该端口的语言端口数量(一个字节),这将是一个相当大的工作。参见例如此处:https://github.com/google/flatbuffers/issues/4209
是的,多个联合是一种笨拙的解决方法。
另一种选择是将类型定义为枚举。现在,您遇到的问题是,您没有类型安全的方式来存储表。这可以通过“嵌套平面缓冲区”来实现,即将联合值存储为字节向量,然后在检查枚举后即可以正确的类型廉价地调用GetRoot。
如果唯一类型的记录数小于256,则另一个选项可以是枚举+联合。例如,您可能具有多种行类型,即使它们具有不同的名称,其内容也只是一个字符串,因此它们可以合并为联合类型。
另一种可能是声明一个table RowBaseClass {}
或其他任何东西,这将是字段的类型,但是您实际上不会实例化该表。然后根据您使用的语言来回转换到该类型以存储实际表。
答案 1 :(得分:0)
嵌套到255个联合的限制的解决方案非常简单。
allobjects.fbs:
s3_resource.Bucket(bucket_name).put_object(
Key=s3_path,
Body=data,
ServerSideEncryption ="aws:kms"
)
Statement.fbs:
namespace Database;
table Garbage {
gid:ulong;
type:string;
weight:uint;
}
... many more ...
main.c:
include "allobjects.fbs";
namespace Database;
enum StatementKind : byte { Unknown = 0, Insert, Update, Delete, Truncate }
// suppose this enum holds the > 255 Row types
enum TableKind : uint16 { Unknown = 0, Garbage, Etc... }
// this is the "union", but with a type enum beyond ubyte size
table Row {
kind:TableKind;
// this payload will be the nested flatbuffer
payload:[ubyte];
}
table Statement {
kind:StatementKind;
truncate:[TableKind];
row:Row;
}
root_type Statement;
输出:
#include <iostream>
#include "Statement_generated.h"
void encodeInsertGarbage(unsigned long gid,
const std::string& type,
unsigned int weight,
std::vector<uint8_t>& retbuf)
{
flatbuffers::FlatBufferBuilder fbb;
// create Garbage flatbuffer
// I used the "Direct" version so I didn't have to create a flatbuffer string object
auto garbage = Database::CreateGarbageDirect(fbb, gid, type.c_str(), weight);
fbb.Finish(garbage);
// make [ubyte] from encoded "Garbage" object
auto payload = fbb.CreateVector(fbb.GetBufferPointer(), fbb.GetSize());
// make the generic Row homebrewed union
auto obj = Database::CreateRow(fbb, Database::TableKind_Garbage, payload);
fbb.Finish(obj);
// create the Statement - 0 for "truncate" since that is not used for Insert
auto statement = Database::CreateStatement(fbb, Database::StatementKind_Insert, 0, obj);
fbb.Finish(statement);
// copy the resulting flatbuffer to output vector
// just for this test program, typically you write to a file or socket.
retbuf.assign(fbb.GetBufferPointer(), fbb.GetBufferPointer() + fbb.GetSize());
}
void decodeInsertGarbage(std::vector<uint8_t>& retbuf)
{
auto statement = Database::GetStatement(retbuf.data());
auto tableType = statement->row()->kind();
auto payload = statement->row()->payload();
// just using a simple "if" statement here, but a full solution
// could use an array of getters, indexed by TableKind, then
// wrap it up nice with a template function to cast the return type
// like rowGet<Garbage>(payload);
if (tableType == Database::TableKind_Garbage)
{
auto garbage = Database::GetGarbage(payload->Data());
std::cout << " gid: " << garbage->gid() << std::endl;
std::cout << " type: " << garbage->type()->c_str() << std::endl;
std::cout << " weight: " << garbage->weight() << std::endl;
}
}
int main()
{
std::vector<uint8_t> iobuf;
encodeInsertGarbage(0, "solo cups", 12, iobuf);
decodeInsertGarbage(iobuf);
return 0;
}