Skip to content

Commit f9f3452

Browse files
dcherednikuzhastik
authored andcommitted
Allow read table returns and use not null types via public API (#5219) (#6318)
1 parent eaea57b commit f9f3452

File tree

21 files changed

+363
-42
lines changed

21 files changed

+363
-42
lines changed

ydb/core/engine/mkql_proto.cpp

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
namespace NKikimr::NMiniKQL {
1414

1515
// NOTE: TCell's can reference memomry from tupleValue
16+
// TODO: Place notNull flag in to the NScheme::TTypeInfo?
1617
bool CellsFromTuple(const NKikimrMiniKQL::TType* tupleType,
1718
const NKikimrMiniKQL::TValue& tupleValue,
1819
const TConstArrayRef<NScheme::TTypeInfo>& types,
20+
TVector<bool> notNullTypes,
1921
bool allowCastFromString,
2022
TVector<TCell>& key,
2123
TString& errStr,
@@ -28,6 +30,18 @@ bool CellsFromTuple(const NKikimrMiniKQL::TType* tupleType,
2830
return false; \
2931
}
3032

33+
// Please note we modify notNullTypes during tuplyType verification to allow cast nullable to non nullable value
34+
if (notNullTypes) {
35+
CHECK_OR_RETURN_ERROR(notNullTypes.size() == types.size(),
36+
"The size of type array and given not null markers must be equial");
37+
if (tupleType) {
38+
CHECK_OR_RETURN_ERROR(notNullTypes.size() >= tupleType->GetTuple().ElementSize(),
39+
"The size of tuple type and given not null markers must be equal");
40+
}
41+
} else {
42+
notNullTypes.resize(types.size());
43+
}
44+
3145
if (tupleType) {
3246
CHECK_OR_RETURN_ERROR(tupleType->GetKind() == NKikimrMiniKQL::Tuple ||
3347
(tupleType->GetKind() == NKikimrMiniKQL::Unknown && tupleType->GetTuple().ElementSize() == 0), "Must be a tuple");
@@ -36,8 +50,14 @@ bool CellsFromTuple(const NKikimrMiniKQL::TType* tupleType,
3650

3751
for (size_t i = 0; i < tupleType->GetTuple().ElementSize(); ++i) {
3852
const auto& ti = tupleType->GetTuple().GetElement(i);
39-
CHECK_OR_RETURN_ERROR(ti.GetKind() == NKikimrMiniKQL::Optional, "Element at index " + ToString(i) + " in not an Optional");
40-
const auto& item = ti.GetOptional().GetItem();
53+
if (notNullTypes[i]) {
54+
// For not null column type we allow to build cell from nullable mkql type for compatibility reason.
55+
notNullTypes[i] = ti.GetKind() != NKikimrMiniKQL::Optional;
56+
} else {
57+
// But we do not allow to build cell for nullable column from not nullable type
58+
CHECK_OR_RETURN_ERROR(ti.GetKind() == NKikimrMiniKQL::Optional, "Element at index " + ToString(i) + " in not an Optional");
59+
}
60+
const auto& item = notNullTypes[i] ? ti : ti.GetOptional().GetItem();
4161
CHECK_OR_RETURN_ERROR(item.GetKind() == NKikimrMiniKQL::Data, "Element at index " + ToString(i) + " Item kind is not Data");
4262
const auto& typeId = item.GetData().GetScheme();
4363
CHECK_OR_RETURN_ERROR(typeId == types[i].GetTypeId() ||
@@ -53,26 +73,36 @@ bool CellsFromTuple(const NKikimrMiniKQL::TType* tupleType,
5373
}
5474

5575
for (ui32 i = 0; i < tupleValue.TupleSize(); ++i) {
56-
auto& o = tupleValue.GetTuple(i);
57-
58-
auto element_case = o.value_value_case();
59-
60-
CHECK_OR_RETURN_ERROR(element_case == NKikimrMiniKQL::TValue::kOptional ||
61-
element_case == NKikimrMiniKQL::TValue::VALUE_VALUE_NOT_SET,
62-
Sprintf("Optional type is expected in tuple at position %" PRIu32, i));
6376

64-
CHECK_OR_RETURN_ERROR(o.ListSize() == 0 &&
65-
o.StructSize() == 0 &&
66-
o.TupleSize() == 0 &&
67-
o.DictSize() == 0,
68-
Sprintf("Optional type is expected in tuple at position %" PRIu32, i));
77+
auto& o = tupleValue.GetTuple(i);
6978

70-
if (!o.HasOptional()) {
71-
key.push_back(TCell());
72-
continue;
79+
if (notNullTypes[i]) {
80+
CHECK_OR_RETURN_ERROR(o.ListSize() == 0 &&
81+
o.StructSize() == 0 &&
82+
o.TupleSize() == 0 &&
83+
o.DictSize() == 0 &&
84+
!o.HasOptional(),
85+
Sprintf("Primitive type is expected in tuple at position %" PRIu32, i));
86+
} else {
87+
auto element_case = o.value_value_case();
88+
89+
CHECK_OR_RETURN_ERROR(element_case == NKikimrMiniKQL::TValue::kOptional ||
90+
element_case == NKikimrMiniKQL::TValue::VALUE_VALUE_NOT_SET,
91+
Sprintf("Optional type is expected in tuple at position %" PRIu32, i));
92+
93+
CHECK_OR_RETURN_ERROR(o.ListSize() == 0 &&
94+
o.StructSize() == 0 &&
95+
o.TupleSize() == 0 &&
96+
o.DictSize() == 0,
97+
Sprintf("Optional type is expected in tuple at position %" PRIu32, i));
98+
99+
if (!o.HasOptional()) {
100+
key.push_back(TCell());
101+
continue;
102+
}
73103
}
74104

75-
auto& v = o.GetOptional();
105+
auto& v = notNullTypes[i] ? o : o.GetOptional();
76106

77107
auto value_case = v.value_value_case();
78108

ydb/core/engine/mkql_proto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class THolderFactory;
1616
bool CellsFromTuple(const NKikimrMiniKQL::TType* tupleType,
1717
const NKikimrMiniKQL::TValue& tupleValue,
1818
const TConstArrayRef<NScheme::TTypeInfo>& expectedTypes,
19+
TVector<bool> notNullTypes,
1920
bool allowCastFromString,
2021
TVector<TCell>& key,
2122
TString& errStr,

ydb/core/engine/mkql_proto_ut.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ Y_UNIT_TEST(TestExportVariantStructTypeYdb) {
511511
TVector<TCell> cells;
512512
TVector<TString> memoryOwner;
513513
TString errStr;
514-
bool res = CellsFromTuple(&params.GetType(), params.GetValue(), types, true, cells, errStr, memoryOwner);
514+
bool res = CellsFromTuple(&params.GetType(), params.GetValue(), types, {}, true, cells, errStr, memoryOwner);
515515
UNIT_ASSERT_VALUES_EQUAL_C(res, errStr.empty(), paramsProto);
516516

517517
return errStr;

ydb/core/grpc_services/resolve_local_db_table.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ namespace NGRpcService {
4545
const NTable::TScheme::TTableInfo* tableInfo = scheme.Tables.FindPtr(*ti);
4646

4747
for (const auto& col : tableInfo->Columns) {
48-
entry.Columns[col.first] = TSysTables::TTableColumnInfo(col.second.Name, col.first, col.second.PType, col.second.PTypeMod, col.second.KeyOrder);
48+
entry.Columns[col.first] = TSysTables::TTableColumnInfo(
49+
col.second.Name, col.first, col.second.PType, col.second.PTypeMod, col.second.KeyOrder,
50+
{}, TSysTables::TTableColumnInfo::EDefaultKind::DEFAULT_UNDEFINED, {}, false, col.second.NotNull);
4951
}
5052
}
5153

ydb/core/grpc_services/rpc_read_table.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ class TReadTableRPC : public TActorBootstrapped<TReadTableRPC> {
214214
} else {
215215
TStringStream str;
216216
str << "Response version missmatched";
217+
if (msg->Record.HasReadTableResponseVersion()) {
218+
str << " , got: " << msg->Record.GetReadTableResponseVersion();
219+
}
217220
LOG_ERROR(ctx, NKikimrServices::READ_TABLE_API,
218221
"%s", str.Str().data());
219222
const NYql::TIssue& issue = MakeIssue(NKikimrIssues::TIssuesIds::DEFAULT_ERROR, str.Str());
@@ -243,8 +246,11 @@ class TReadTableRPC : public TActorBootstrapped<TReadTableRPC> {
243246
return ReplyFinishStream(Ydb::StatusIds::UNAUTHORIZED, issueMessage, ctx);
244247
}
245248
case TEvTxUserProxy::TResultStatus::ResolveError: {
246-
const NYql::TIssue& issue = MakeIssue(NKikimrIssues::TIssuesIds::DEFAULT_ERROR, "Got ResolveError response from TxProxy");
249+
NYql::TIssue issue = MakeIssue(NKikimrIssues::TIssuesIds::DEFAULT_ERROR, "Got ResolveError response from TxProxy");
247250
auto tmp = issueMessage.Add();
251+
for (const auto& unresolved : msg->Record.GetUnresolvedKeys()) {
252+
issue.AddSubIssue(MakeIntrusive<NYql::TIssue>(unresolved));
253+
}
248254
NYql::IssueToMessage(issue, tmp);
249255
return ReplyFinishStream(Ydb::StatusIds::SCHEME_ERROR, issueMessage, ctx);
250256
}
@@ -525,6 +531,20 @@ class TReadTableRPC : public TActorBootstrapped<TReadTableRPC> {
525531
settings.TablePath = req->path();
526532
settings.Ordered = req->ordered();
527533
settings.RequireResultSet = true;
534+
535+
// Right now assume return_not_null_data_as_optional is true by default
536+
// Sometimes we well change this default
537+
switch (req->return_not_null_data_as_optional()) {
538+
case Ydb::FeatureFlag::DISABLED:
539+
settings.DataFormat = NTxProxy::EReadTableFormat::YdbResultSetWithNotNullSupport;
540+
break;
541+
case Ydb::FeatureFlag::STATUS_UNSPECIFIED:
542+
case Ydb::FeatureFlag::ENABLED:
543+
default:
544+
settings.DataFormat = NTxProxy::EReadTableFormat::YdbResultSet;
545+
break;
546+
}
547+
528548
if (req->row_limit()) {
529549
settings.MaxRows = req->row_limit();
530550
}

ydb/core/protos/tx_datashard.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ message TReadTableTransaction {
101101
optional string Name = 2;
102102
optional uint32 TypeId = 3;
103103
optional NKikimrProto.TTypeInfo TypeInfo = 4;
104+
optional bool NotNull = 5 [default = false]; //If not set datashard will treat not null type as nullable (for compatibility)
104105
}
105106

106107
optional NKikimrDataEvents.TTableId TableId = 1;

ydb/core/protos/tx_proxy.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ message TReadTableTransaction {
6060
enum EVersion {
6161
UNSPECIFIED = 0;
6262
YDB_V1 = 1;
63+
YDB_V2 = 2; // Like V1 but allows NotNull types in result set
6364
}
6465
optional string Path = 1;
6566
optional bool Ordered = 2 [default = false];

ydb/core/tx/datashard/read_table_scan.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,9 @@ class TRowsToOldResult : public TRowsToResult {
301301

302302
class TRowsToYdbResult : public TRowsToResult {
303303
public:
304-
TRowsToYdbResult(const NKikimrTxDataShard::TReadTableTransaction& request)
304+
TRowsToYdbResult(const NKikimrTxDataShard::TReadTableTransaction& request, bool allowNotNull)
305305
: TRowsToResult(request)
306+
, AllowNotNull(allowNotNull)
306307
{
307308
BuildResultCommonPart(request);
308309
StartNewMessage();
@@ -345,16 +346,16 @@ class TRowsToYdbResult : public TRowsToResult {
345346
pg->set_typlen(0);
346347
pg->set_typmod(0);
347348
} else {
349+
bool notNullResp = AllowNotNull && col.GetNotNull();
348350
auto id = static_cast<NYql::NProto::TypeIds>(col.GetTypeId());
351+
auto xType = notNullResp ? meta->mutable_type() : meta->mutable_type()->mutable_optional_type()->mutable_item();
349352
if (id == NYql::NProto::Decimal) {
350-
auto decimalType = meta->mutable_type()->mutable_optional_type()->mutable_item()
351-
->mutable_decimal_type();
353+
auto decimalType = xType->mutable_decimal_type();
352354
//TODO: Pass decimal params here
353355
decimalType->set_precision(22);
354356
decimalType->set_scale(9);
355357
} else {
356-
meta->mutable_type()->mutable_optional_type()->mutable_item()
357-
->set_type_id(static_cast<Ydb::Type::PrimitiveTypeId>(id));
358+
xType->set_type_id(static_cast<Ydb::Type::PrimitiveTypeId>(id));
358359
}
359360
}
360361
}
@@ -363,6 +364,7 @@ class TRowsToYdbResult : public TRowsToResult {
363364
}
364365

365366
Ydb::ResultSet YdbResultSet;
367+
const bool AllowNotNull;
366368
};
367369

368370
class TReadTableScan : public TActor<TReadTableScan>, public NTable::IScan {
@@ -390,8 +392,14 @@ class TReadTableScan : public TActor<TReadTableScan>, public NTable::IScan {
390392
, PendingAcks(0)
391393
, Finished(false)
392394
{
393-
if (tx.HasApiVersion() && tx.GetApiVersion() == NKikimrTxUserProxy::TReadTableTransaction::YDB_V1) {
394-
Writer = MakeHolder<TRowsToYdbResult>(tx);
395+
if (tx.HasApiVersion()) {
396+
if (tx.GetApiVersion() == NKikimrTxUserProxy::TReadTableTransaction::YDB_V1) {
397+
Writer = MakeHolder<TRowsToYdbResult>(tx, false);
398+
} else if (tx.GetApiVersion() == NKikimrTxUserProxy::TReadTableTransaction::YDB_V2) {
399+
Writer = MakeHolder<TRowsToYdbResult>(tx, true);
400+
} else {
401+
Writer = MakeHolder<TRowsToOldResult>(tx);
402+
}
395403
} else {
396404
Writer = MakeHolder<TRowsToOldResult>(tx);
397405
}

ydb/core/tx/datashard/sys_tables.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct TSysTables {
3131
EDefaultKind DefaultKind;
3232
Ydb::TypedValue DefaultFromLiteral;
3333
bool IsBuildInProgress = false;
34+
bool IsNotNullColumn = false; //maybe move into TTypeInfo?
3435

3536
TTableColumnInfo() = default;
3637

@@ -54,7 +55,7 @@ struct TSysTables {
5455
const TString& typeMod = {}, i32 keyOrder = -1,
5556
const TString& defaultFromSequence = {},
5657
EDefaultKind defaultKind = EDefaultKind::DEFAULT_UNDEFINED,
57-
const Ydb::TypedValue& defaultFromLiteral = {}, bool isBuildInProgress = false)
58+
const Ydb::TypedValue& defaultFromLiteral = {}, bool isBuildInProgress = false, bool isNotNullColumn = false)
5859
: Name(name)
5960
, Id(colId)
6061
, PType(type)
@@ -64,6 +65,7 @@ struct TSysTables {
6465
, DefaultKind(defaultKind)
6566
, DefaultFromLiteral(defaultFromLiteral)
6667
, IsBuildInProgress(isBuildInProgress)
68+
, IsNotNullColumn(isNotNullColumn)
6769
{}
6870
};
6971

ydb/core/tx/scheme_board/cache.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ class TSchemeCache: public TMonitorableActor<TSchemeCache> {
785785
}
786786

787787
if (columnDesc.GetNotNull()) {
788+
column.IsNotNullColumn = true;
788789
NotNullColumns.insert(columnDesc.GetName());
789790
}
790791
}

0 commit comments

Comments
 (0)