Skip to content

Commit 5d1813f

Browse files
Merge a03f6c6 into 31e0e46
2 parents 31e0e46 + a03f6c6 commit 5d1813f

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

ydb/library/yql/providers/s3/provider/yql_s3_datasink_type_ann.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ TExprNode::TListType GetPartitionKeys(const TExprNode::TPtr& partBy) {
2626

2727
return {};
2828
}
29+
30+
bool ValidateSchemaForOutput(const TStructExprType* schemaStructRowType, TExprContext& ctx) {
31+
for (const TItemExprType* item : schemaStructRowType->GetItems()) {
32+
const TTypeAnnotationNode* rowType = item->GetItemType();
33+
if (rowType->GetKind() == ETypeAnnotationKind::Optional) {
34+
rowType = rowType->Cast<TOptionalExprType>()->GetItemType();
35+
}
36+
37+
if (rowType->GetKind() == ETypeAnnotationKind::Optional) {
38+
ctx.AddError(TIssue(TStringBuilder() << "Double optional types are not supported for output (you have '"
39+
<< item->GetName() << " " << FormatType(item->GetItemType()) << "' field)"));
40+
return false;
41+
}
42+
}
43+
return true;
44+
}
45+
2946
}
3047

3148
namespace {
@@ -74,6 +91,10 @@ class TS3DataSinkTypeAnnotationTransformer : public TVisitorTransformerBase {
7491
return TStatus::Error;
7592
}
7693

94+
if (!ValidateSchemaForOutput(sourceType->Cast<TStructExprType>(), ctx)) {
95+
return TStatus::Error;
96+
}
97+
7798
auto target = input->Child(TS3WriteObject::idx_Target);
7899
if (!TS3Target::Match(target)) {
79100
ctx.AddError(TIssue(ctx.GetPosition(target->Pos()), "Expected S3 target."));

ydb/tests/fq/s3/test_insert.py

+32
Original file line numberDiff line numberDiff line change
@@ -535,3 +535,35 @@ def test_insert_without_format_error(self, kikimr, s3, client, unique_prefix):
535535
issues = str(client.describe_query(query_id).result.query.issue)
536536

537537
assert "Missing format - please use WITH FORMAT when writing into S3" in issues, "Incorrect Issues: " + issues
538+
539+
@yq_all
540+
@pytest.mark.parametrize("client", [{"folder_id": "my_folder"}], indirect=True)
541+
def test_insert_type_validation(self, kikimr, s3, client, unique_prefix):
542+
resource = boto3.resource(
543+
"s3", endpoint_url=s3.s3_url, aws_access_key_id="key", aws_secret_access_key="secret_key"
544+
)
545+
546+
bucket = resource.Bucket("fbucket")
547+
bucket.create(ACL='public-read')
548+
bucket.objects.all().delete()
549+
550+
s3_client = boto3.client(
551+
"s3", endpoint_url=s3.s3_url, aws_access_key_id="key", aws_secret_access_key="secret_key"
552+
)
553+
554+
kikimr.control_plane.wait_bootstrap(1)
555+
storage_connection_name = unique_prefix + "fruitbucket"
556+
client.create_storage_connection(storage_connection_name, "fbucket")
557+
558+
sql = f'''
559+
INSERT INTO `{storage_connection_name}`.`insert/`
560+
WITH
561+
(
562+
FORMAT="csv_with_names"
563+
)
564+
SELECT CAST(42 AS Int32??) as Weight;'''
565+
566+
query_id = client.create_query("simple", sql, type=fq.QueryContent.QueryType.ANALYTICS).result.query_id
567+
client.wait_query_status(query_id, fq.QueryMeta.FAILED)
568+
569+
assert "Double optional types are not supported for output" in str(client.describe_query(query_id).result)

0 commit comments

Comments
 (0)