diff options
| author | vitya-smirnov <[email protected]> | 2025-09-03 16:23:37 +0300 |
|---|---|---|
| committer | vitya-smirnov <[email protected]> | 2025-09-03 16:53:40 +0300 |
| commit | 1a6fddffd17953f4ed4d20de9fdd5c010166acd1 (patch) | |
| tree | 1f90d4097dccbfa041bc9ef3ce695e75f51a3ed3 /yql/essentials/sql | |
| parent | db0b30ed142b14352bba1fb6e67768928da20d49 (diff) | |
YQL-20189: Track warnings as errors
There was an issue that a query with warnings
as errors passed translation.
commit_hash:890d18853380b5ad669e9684553cdb6991827cff
Diffstat (limited to 'yql/essentials/sql')
| -rw-r--r-- | yql/essentials/sql/v1/builtin.cpp | 49 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/context.cpp | 72 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/context.h | 15 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/insert.cpp | 6 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/node.cpp | 57 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/node.h | 2 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/query.cpp | 15 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/select.cpp | 32 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/sql_expression.cpp | 28 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/sql_query.cpp | 48 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/sql_select.cpp | 31 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/sql_translation.cpp | 38 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/sql_ut_common.h | 50 | ||||
| -rw-r--r-- | yql/essentials/sql/v1/sql_values.cpp | 8 |
14 files changed, 346 insertions, 105 deletions
diff --git a/yql/essentials/sql/v1/builtin.cpp b/yql/essentials/sql/v1/builtin.cpp index c660d35817c..f8d84095259 100644 --- a/yql/essentials/sql/v1/builtin.cpp +++ b/yql/essentials/sql/v1/builtin.cpp @@ -487,14 +487,20 @@ public: // TODO: TablePath() and TableRecordIndex() have more strict limitations if (src->GetJoin()) { - ctx.Warning(Pos_, - TIssuesIds::YQL_EMPTY_TABLENAME_RESULT) << "TableName() may produce empty result when used in ambiguous context (with JOIN)"; + if (!ctx.Warning(Pos_, TIssuesIds::YQL_EMPTY_TABLENAME_RESULT, [](auto& out) { + out << "TableName() may produce empty result when used in ambiguous context (with JOIN)"; + })) { + return false; + } } if (src->HasAggregations()) { - ctx.Warning(Pos_, - TIssuesIds::YQL_EMPTY_TABLENAME_RESULT) << "TableName() will produce empty result when used with aggregation.\n" - "Please consult documentation for possible workaround"; + if (!ctx.Warning(Pos_, TIssuesIds::YQL_EMPTY_TABLENAME_RESULT, [](auto& out) { + out << "TableName() will produce empty result when used with aggregation.\n" + "Please consult documentation for possible workaround"; + })) { + return false; + } } Args_.push_back(Y("TablePath", Y("DependsOn", "row"))); @@ -1615,10 +1621,13 @@ private: // TODO: 'IN ((select ...))' is parsed exactly like 'IN (select ...)' instead of a single element tuple if (singleElement->GetSource() || singleElement->IsSelect()) { TStringBuf parenKind = singleElement->GetSource() ? "" : "external "; - ctx.Warning(pos, - TIssuesIds::YQL_CONST_SUBREQUEST_IN_LIST) << "Using subrequest in scalar context after IN, " - << "perhaps you should remove " - << parenKind << "parenthesis here"; + if (!ctx.Warning(pos, TIssuesIds::YQL_CONST_SUBREQUEST_IN_LIST, [&](auto& out) { + out << "Using subrequest in scalar context after IN, " + << "perhaps you should remove " + << parenKind << "parenthesis here"; + })) { + return false; + } } } @@ -3448,7 +3457,12 @@ TNodePtr BuildBuiltinFunc(TContext& ctx, TPosition pos, TString name, const TVec } if (ns == "datetime2") { - ctx.Warning(pos, TIssuesIds::YQL_DEPRECATED_DATETIME2) << "DateTime2:: is a temporary alias for DateTime:: which will be removed in the future, use DateTime:: instead"; + if (!ctx.Warning(pos, TIssuesIds::YQL_DEPRECATED_DATETIME2, [](auto& out) { + out << "DateTime2:: is a temporary alias for DateTime:: which will be " + << "removed in the future, use DateTime:: instead"; + })) { + return nullptr; + } } if (ns == "datetime") { @@ -3475,9 +3489,12 @@ TNodePtr BuildBuiltinFunc(TContext& ctx, TPosition pos, TString name, const TVec if (ns == "yql" || ns == "@yql") { if (warnOnYqlNameSpace && GetEnv("YQL_DETERMINISTIC_MODE").empty()) { - ctx.Warning(pos, TIssuesIds::YQL_S_EXPRESSIONS_CALL) - << "It is not recommended to directly access s-expressions functions via YQL::" << Endl - << "This mechanism is mostly intended for temporary workarounds or internal testing purposes"; + if (!ctx.Warning(pos, TIssuesIds::YQL_S_EXPRESSIONS_CALL, [](auto& out) { + out << "It is not recommended to directly access s-expressions functions via YQL::" << Endl + << "This mechanism is mostly intended for temporary workarounds or internal testing purposes"; + })) { + return nullptr; + } } if (ns == "yql") { @@ -3890,7 +3907,11 @@ TNodePtr BuildBuiltinFunc(TContext& ctx, TPosition pos, TString name, const TVec TNodePtr customUserType = nullptr; if (ns == "json") { - ctx.Warning(pos, TIssuesIds::YQL_DEPRECATED_JSON_UDF) << "Json UDF is deprecated. Please use JSON API instead"; + if (!ctx.Warning(pos, TIssuesIds::YQL_DEPRECATED_JSON_UDF, [](auto& out) { + out << "Json UDF is deprecated. Please use JSON API instead"; + })) { + return nullptr; + } ns = "yson"; nameSpace = "Yson"; diff --git a/yql/essentials/sql/v1/context.cpp b/yql/essentials/sql/v1/context.cpp index cdf2a60824d..440f7ca11df 100644 --- a/yql/essentials/sql/v1/context.cpp +++ b/yql/essentials/sql/v1/context.cpp @@ -7,6 +7,7 @@ #include <util/folder/pathsplit.h> #include <util/string/join.h> #include <util/stream/null.h> +#include <util/generic/scope.h> #ifdef GetMessage #undef GetMessage @@ -108,6 +109,7 @@ TContext::TContext(const TLexers& lexers, const TParsers& parsers, , AnsiQuotedIdentifiers(settings.AnsiLexer) , WarningPolicy(settings.IsReplay) , BlockEngineEnable(Settings.BlockDefaultAuto->Allow()) + , StrictWarningAsError(Settings.Flags.contains("StrictWarningAsError")) { if (settings.LangVer >= MakeLangVersion(2025, 2)) { GroupByExprAfterWhere = true; @@ -188,15 +190,20 @@ IOutputStream& TContext::Error(NYql::TIssueCode code) { IOutputStream& TContext::Error(NYql::TPosition pos, NYql::TIssueCode code) { HasPendingErrors = true; - return MakeIssue(TSeverityIds::S_ERROR, code, pos); + bool isError; + return MakeIssue(TSeverityIds::S_ERROR, code, pos, isError); } -IOutputStream& TContext::Warning(NYql::TPosition pos, NYql::TIssueCode code) { - return MakeIssue(TSeverityIds::S_WARNING, code, pos); +bool TContext::Warning(NYql::TPosition pos, NYql::TIssueCode code, std::function<void(IOutputStream&)> message) { + bool isError; + IOutputStream& out = MakeIssue(TSeverityIds::S_WARNING, code, pos, isError); + message(out); + return !StrictWarningAsError || !isError; } IOutputStream& TContext::Info(NYql::TPosition pos) { - return MakeIssue(TSeverityIds::S_INFO, TIssuesIds::INFO, pos); + bool isError; + return MakeIssue(TSeverityIds::S_INFO, TIssuesIds::INFO, pos, isError); } void TContext::SetWarningPolicyFor(NYql::TIssueCode code, NYql::EWarningAction action) { @@ -221,22 +228,28 @@ TVector<NSQLTranslation::TSQLHint> TContext::PullHintForToken(NYql::TPosition to return result; } -void TContext::WarnUnusedHints() { - if (!SqlHints_.empty()) { - // warn about first unused hint - auto firstUnused = SqlHints_.begin(); - YQL_ENSURE(!firstUnused->second.empty()); - const NSQLTranslation::TSQLHint& hint = firstUnused->second.front(); - Warning(hint.Pos, TIssuesIds::YQL_UNUSED_HINT) << "Hint " << hint.Name << " will not be used"; +bool TContext::WarnUnusedHints() { + if (SqlHints_.empty()) { + return true; } + + auto firstUnused = SqlHints_.begin(); + YQL_ENSURE(!firstUnused->second.empty()); + const NSQLTranslation::TSQLHint& hint = firstUnused->second.front(); + return Warning(hint.Pos, TIssuesIds::YQL_UNUSED_HINT, [&](auto& out) { + out << "Hint " << hint.Name << " will not be used"; + }); } -IOutputStream& TContext::MakeIssue(ESeverity severity, TIssueCode code, NYql::TPosition pos) { +IOutputStream& TContext::MakeIssue(ESeverity severity, TIssueCode code, NYql::TPosition pos, bool& isError) { + isError = (severity == TSeverityIds::S_ERROR); + if (severity == TSeverityIds::S_WARNING) { auto action = WarningPolicy.GetAction(code); if (action == EWarningAction::ERROR) { severity = TSeverityIds::S_ERROR; HasPendingErrors = true; + isError = true; } else if (action == EWarningAction::DISABLE) { return Cnull; } @@ -635,35 +648,52 @@ TString TTranslation::PushNamedAtom(TPosition namePos, const TString& name) { return PushNamedNode(namePos, name, buildAtom); } -void TTranslation::PopNamedNode(const TString& name) { +bool TTranslation::PopNamedNode(const TString& name) { auto mapIt = Ctx_.Scoped->NamedNodes.find(name); Y_DEBUG_ABORT_UNLESS(mapIt != Ctx_.Scoped->NamedNodes.end()); Y_DEBUG_ABORT_UNLESS(mapIt->second.size() > 0); + + Y_DEFER { + mapIt->second.pop_front(); + if (mapIt->second.empty()) { + Ctx_.Scoped->NamedNodes.erase(mapIt); + } + }; + auto& top = mapIt->second.front(); if (!top->IsUsed && !Ctx_.HasPendingErrors && !name.StartsWith("$_")) { - Ctx_.Warning(top->NamePos, TIssuesIds::YQL_UNUSED_SYMBOL) << "Symbol " << name << " is not used"; - } - mapIt->second.pop_front(); - if (mapIt->second.empty()) { - Ctx_.Scoped->NamedNodes.erase(mapIt); + if (!Ctx_.Warning(top->NamePos, TIssuesIds::YQL_UNUSED_SYMBOL, [&](auto& out) { + out << "Symbol " << name << " is not used"; + })) { + return false; + } } + return true; } -void TTranslation::WarnUnusedNodes() const { +bool TTranslation::WarnUnusedNodes() const { if (Ctx_.HasPendingErrors) { // result is not reliable in this case - return; + return true; } + for (const auto& [name, items]: Ctx_.Scoped->NamedNodes) { if (name.StartsWith("$_")) { continue; } + for (const auto& item : items) { if (!item->IsUsed && item->Level == Ctx_.ScopeLevel) { - Ctx_.Warning(item->NamePos, TIssuesIds::YQL_UNUSED_SYMBOL) << "Symbol " << name << " is not used"; + if (!Ctx_.Warning(item->NamePos, TIssuesIds::YQL_UNUSED_SYMBOL, [&](auto& out) { + out << "Symbol " << name << " is not used"; + })) { + return false; + } } } } + + return true; } TString GetDescription(const google::protobuf::Message& node, const google::protobuf::FieldDescriptor* d) { diff --git a/yql/essentials/sql/v1/context.h b/yql/essentials/sql/v1/context.h index 7ae995059a8..5289b9e213d 100644 --- a/yql/essentials/sql/v1/context.h +++ b/yql/essentials/sql/v1/context.h @@ -107,7 +107,7 @@ namespace NSQLTranslationV1 { IOutputStream& Error(NYql::TIssueCode code = NYql::TIssuesIds::DEFAULT_ERROR); IOutputStream& Error(NYql::TPosition pos, NYql::TIssueCode code = NYql::TIssuesIds::DEFAULT_ERROR); - IOutputStream& Warning(NYql::TPosition pos, NYql::TIssueCode code); + bool Warning(NYql::TPosition pos, NYql::TIssueCode code, std::function<void(IOutputStream&)> message); IOutputStream& Info(NYql::TPosition pos); void SetWarningPolicyFor(NYql::TIssueCode code, NYql::EWarningAction action); @@ -243,12 +243,16 @@ namespace NSQLTranslationV1 { } TVector<NSQLTranslation::TSQLHint> PullHintForToken(NYql::TPosition tokenPos); - void WarnUnusedHints(); + bool WarnUnusedHints(); TScopedStatePtr CreateScopedState() const; private: - IOutputStream& MakeIssue(NYql::ESeverity severity, NYql::TIssueCode code, NYql::TPosition pos); + IOutputStream& MakeIssue( + NYql::ESeverity severity, + NYql::TIssueCode code, + NYql::TPosition pos, + bool& isError); public: const TLexers Lexers; @@ -382,6 +386,7 @@ namespace NSQLTranslationV1 { bool OptimizeSimpleIlike = false; bool PersistableFlattenAndAggrExprs = false; bool DebugPositions = false; + bool StrictWarningAsError = false; TVector<size_t> ForAllStatementsParts; TMaybe<TString> Engine; @@ -460,8 +465,8 @@ namespace NSQLTranslationV1 { TString PushNamedNode(TPosition namePos, const TString& name, const TNodeBuilderByName& builder); TString PushNamedNode(TPosition namePos, const TString& name, TNodePtr node); TString PushNamedAtom(TPosition namePos, const TString& name); - void PopNamedNode(const TString& name); - void WarnUnusedNodes() const; + bool PopNamedNode(const TString& name); + bool WarnUnusedNodes() const; template <typename TNode> void AltNotImplemented(const TString& ruleName, const TNode& node) { diff --git a/yql/essentials/sql/v1/insert.cpp b/yql/essentials/sql/v1/insert.cpp index 9590b7642fb..42bddd74ca0 100644 --- a/yql/essentials/sql/v1/insert.cpp +++ b/yql/essentials/sql/v1/insert.cpp @@ -220,7 +220,11 @@ public: } } if (mismatchFound) { - ctx.Warning(Pos_, TIssuesIds::YQL_SOURCE_SELECT_COLUMN_MISMATCH) << str.Str(); + if (!ctx.Warning(Pos_, TIssuesIds::YQL_SOURCE_SELECT_COLUMN_MISMATCH, [&](auto& out) { + out << str.Str(); + })) { + return false; + } } } return true; diff --git a/yql/essentials/sql/v1/node.cpp b/yql/essentials/sql/v1/node.cpp index 6075eb195fd..1c87734edad 100644 --- a/yql/essentials/sql/v1/node.cpp +++ b/yql/essentials/sql/v1/node.cpp @@ -1204,11 +1204,17 @@ bool TWinRank::DoInit(TContext& ctx, ISource* src) { const auto& orderSpec = winSpecPtr->OrderBy; if (orderSpec.empty()) { if (Args_.empty()) { - ctx.Warning(GetPos(), TIssuesIds::YQL_RANK_WITHOUT_ORDER_BY) << - OpName_ << "() is used with unordered window - all rows will be considered equal to each other"; + if (!ctx.Warning(GetPos(), TIssuesIds::YQL_RANK_WITHOUT_ORDER_BY, [&](auto& out) { + out << OpName_ << "() is used with unordered window - all rows will be considered equal to each other"; + })) { + return false; + } } else { - ctx.Warning(GetPos(), TIssuesIds::YQL_RANK_WITHOUT_ORDER_BY) << - OpName_ << "(<expression>) is used with unordered window - the result is likely to be undefined"; + if (!ctx.Warning(GetPos(), TIssuesIds::YQL_RANK_WITHOUT_ORDER_BY, [&](auto& out) { + out << OpName_ << "(<expression>) is used with unordered window - the result is likely to be undefined"; + })) { + return false; + } } } @@ -1873,8 +1879,11 @@ StringContentInternal(TContext& ctx, TPosition pos, const TString& input, EStrin result.Type = NKikimr::NUdf::EDataSlot::Utf8; } else { if (ctx.Scoped->WarnUntypedStringLiterals) { - ctx.Warning(pos, TIssuesIds::YQL_UNTYPED_STRING_LITERALS) - << "Please add suffix u for Utf8 strings or s for arbitrary binary strings"; + if (!ctx.Warning(pos, TIssuesIds::YQL_UNTYPED_STRING_LITERALS, [](auto& out) { + out << "Please add suffix u for Utf8 strings or s for arbitrary binary strings"; + })) { + return {}; + } } if (ctx.Scoped->UnicodeLiterals) { @@ -2687,7 +2696,7 @@ TNodePtr BuildAccess(TPosition pos, const TVector<INode::TIdPart>& ids, bool isL return new TAccessNode(pos, ids, isLookup); } -void WarnIfAliasFromSelectIsUsedInGroupBy(TContext& ctx, const TVector<TNodePtr>& selectTerms, const TVector<TNodePtr>& groupByTerms, +bool WarnIfAliasFromSelectIsUsedInGroupBy(TContext& ctx, const TVector<TNodePtr>& selectTerms, const TVector<TNodePtr>& groupByTerms, const TVector<TNodePtr>& groupByExprTerms) { THashMap<TString, TNodePtr> termsByLabel; @@ -2718,9 +2727,10 @@ void WarnIfAliasFromSelectIsUsedInGroupBy(TContext& ctx, const TVector<TNodePtr> } if (termsByLabel.empty()) { - return; + return true; } + bool isError = false; bool found = false; auto visitor = [&](const INode& current) { if (found) { @@ -2736,10 +2746,17 @@ void WarnIfAliasFromSelectIsUsedInGroupBy(TContext& ctx, const TVector<TNodePtr> auto it = termsByLabel.find(*columnName); if (it != termsByLabel.end()) { found = true; - ctx.Warning(current.GetPos(), TIssuesIds::YQL_PROJECTION_ALIAS_IS_REFERENCED_IN_GROUP_BY) - << "GROUP BY will aggregate by column `" << *columnName << "` instead of aggregating by SELECT expression with same alias"; - ctx.Warning(it->second->GetPos(), TIssuesIds::YQL_PROJECTION_ALIAS_IS_REFERENCED_IN_GROUP_BY) - << "You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details"; + + ctx.Warning(current.GetPos(), TIssuesIds::YQL_PROJECTION_ALIAS_IS_REFERENCED_IN_GROUP_BY, [&](auto& out) { + out << "GROUP BY will aggregate by column `" << *columnName + << "` instead of aggregating by SELECT expression with same alias"; + }); + + isError = isError || !ctx.Warning(it->second->GetPos(), TIssuesIds::YQL_PROJECTION_ALIAS_IS_REFERENCED_IN_GROUP_BY, [](auto& out) { + out << "You should probably use alias in GROUP BY instead of using it here. " + << "Please consult documentation for more details"; + }); + return false; } } @@ -2769,10 +2786,14 @@ void WarnIfAliasFromSelectIsUsedInGroupBy(TContext& ctx, const TVector<TNodePtr> for (auto& groupByTerm : originalGroupBy) { groupByTerm->VisitTree(visitor); + if (isError) { + return false; + } if (found) { - return; + return true; } } + return true; } bool ValidateAllNodesForAggregation(TContext& ctx, const TVector<TNodePtr>& nodes) { @@ -3201,9 +3222,13 @@ TNodePtr BuildBinaryOp(TContext& ctx, TPosition pos, const TString& opName, TNod const bool oneArgNull = a->IsNull() || b->IsNull(); if (bothArgNull || (oneArgNull && opName != "Or" && opName != "And")) { - ctx.Warning(pos, TIssuesIds::YQL_OPERATION_WILL_RETURN_NULL) << "Binary operation " - << opName.substr(0, opName.size() - 7 * opName.EndsWith("MayWarn")) - << " will return NULL here"; + if (!ctx.Warning(pos, TIssuesIds::YQL_OPERATION_WILL_RETURN_NULL, [&](auto& out) { + out << "Binary operation " + << opName.substr(0, opName.size() - 7 * opName.EndsWith("MayWarn")) + << " will return NULL here"; + })) { + return nullptr; + } } } diff --git a/yql/essentials/sql/v1/node.h b/yql/essentials/sql/v1/node.h index 3d0b79f18aa..7ebc7c1e250 100644 --- a/yql/essentials/sql/v1/node.h +++ b/yql/essentials/sql/v1/node.h @@ -820,7 +820,7 @@ namespace NSQLTranslationV1 { TWinSpecs CloneContainer(const TWinSpecs& specs); - void WarnIfAliasFromSelectIsUsedInGroupBy(TContext& ctx, const TVector<TNodePtr>& selectTerms, const TVector<TNodePtr>& groupByTerms, + bool WarnIfAliasFromSelectIsUsedInGroupBy(TContext& ctx, const TVector<TNodePtr>& selectTerms, const TVector<TNodePtr>& groupByTerms, const TVector<TNodePtr>& groupByExprTerms); bool ValidateAllNodesForAggregation(TContext& ctx, const TVector<TNodePtr>& nodes); diff --git a/yql/essentials/sql/v1/query.cpp b/yql/essentials/sql/v1/query.cpp index ab863cc32ff..6d4b7617454 100644 --- a/yql/essentials/sql/v1/query.cpp +++ b/yql/essentials/sql/v1/query.cpp @@ -584,7 +584,12 @@ public: if (func.StartsWith("regexp")) { if (!ctx.PragmaRegexUseRe2) { - ctx.Warning(Pos_, TIssuesIds::CORE_LEGACY_REGEX_ENGINE) << "Legacy regex engine works incorrectly with unicode. Use PRAGMA RegexUseRe2='true';"; + if (!ctx.Warning(Pos_, TIssuesIds::CORE_LEGACY_REGEX_ENGINE, [&](auto& out) { + out << "Legacy regex engine works incorrectly with unicode. " + << "Use PRAGMA RegexUseRe2='true';"; + })) { + return nullptr; + } } auto pattern = Args_[1].Id; @@ -742,8 +747,12 @@ public: postHandler = arg.Expr; } else { - ctx.Warning(Pos_, DEFAULT_ERROR) << "Unsupported named argument: " - << label << " in " << Func_; + if (!ctx.Warning(Pos_, DEFAULT_ERROR, [&](auto& out) { + out << "Unsupported named argument: " + << label << " in " << Func_; + })) { + return nullptr; + } } } if (rootAttributes == nullptr) { diff --git a/yql/essentials/sql/v1/select.cpp b/yql/essentials/sql/v1/select.cpp index c75f18df30a..f20668ddbc6 100644 --- a/yql/essentials/sql/v1/select.cpp +++ b/yql/essentials/sql/v1/select.cpp @@ -1689,8 +1689,12 @@ public: "core", BuildLambda(Pos_, Y("row"), Y("Coalesce", Having_, Y("Bool", Q("false")))) ); - ctx.Warning(Having_->GetPos(), TIssuesIds::YQL_HAVING_WITHOUT_AGGREGATION_IN_SELECT_DISTINCT) - << "The usage of HAVING without aggregations with SELECT DISTINCT is non-standard and will stop working soon. Please use WHERE instead."; + if (!ctx.Warning(Having_->GetPos(), TIssuesIds::YQL_HAVING_WITHOUT_AGGREGATION_IN_SELECT_DISTINCT, [](auto& out) { + out << "The usage of HAVING without aggregations with SELECT DISTINCT is " + << "non-standard and will stop working soon. Please use WHERE instead."; + })) { + return false; + } } else { ctx.Error(Having_->GetPos()) << "HAVING with meaning GROUP BY () should be with aggregation function."; return false; @@ -2009,8 +2013,11 @@ private: label = Columns_.AddUnnamed(); hasName = false; if (ctx.WarnUnnamedColumns) { - ctx.Warning(term->GetPos(), TIssuesIds::YQL_UNNAMED_COLUMN) - << "Autogenerated column name " << label << " will be used for expression"; + if (!ctx.Warning(term->GetPos(), TIssuesIds::YQL_UNNAMED_COLUMN, [&](auto& out) { + out << "Autogenerated column name " << label << " will be used for expression"; + })) { + return false; + } } } } @@ -2063,7 +2070,9 @@ private: hasError = true; } if (!src->IsCompositeSource() && !Columns_.All && src->HasAggregations()) { - WarnIfAliasFromSelectIsUsedInGroupBy(ctx, Terms_, GroupBy_, GroupByExpr_); + if (!WarnIfAliasFromSelectIsUsedInGroupBy(ctx, Terms_, GroupBy_, GroupByExpr_)) { + hasError = true; + } /// verify select aggregation compatibility TVector<TNodePtr> exprs(Terms_); @@ -3039,7 +3048,11 @@ public: if (IgnoreSort()) { Source_->DisableSort(); - ctx.Warning(Source_->GetPos(), TIssuesIds::YQL_ORDER_BY_WITHOUT_LIMIT_IN_SUBQUERY) << "ORDER BY without LIMIT in subquery will be ignored"; + if (!ctx.Warning(Source_->GetPos(), TIssuesIds::YQL_ORDER_BY_WITHOUT_LIMIT_IN_SUBQUERY, [](auto& out) { + out << "ORDER BY without LIMIT in subquery will be ignored"; + })) { + return false; + } } if (!Source_->Init(ctx, src)) { @@ -3052,7 +3065,12 @@ public: return false; } if (SkipTake_->HasSkip() && Source_->GetOrderKind() != EOrderKind::Sort && Source_->GetOrderKind() != EOrderKind::Assume) { - ctx.Warning(Source_->GetPos(), TIssuesIds::YQL_OFFSET_WITHOUT_SORT) << "LIMIT with OFFSET without [ASSUME] ORDER BY may provide different results from run to run"; + if (!ctx.Warning(Source_->GetPos(), TIssuesIds::YQL_OFFSET_WITHOUT_SORT, [](auto& out) { + out << "LIMIT with OFFSET without [ASSUME] ORDER BY may provide " + << "different results from run to run"; + })) { + return false; + } } } diff --git a/yql/essentials/sql/v1/sql_expression.cpp b/yql/essentials/sql/v1/sql_expression.cpp index bceda17f2eb..9876371f27d 100644 --- a/yql/essentials/sql/v1/sql_expression.cpp +++ b/yql/essentials/sql/v1/sql_expression.cpp @@ -1233,7 +1233,9 @@ TNodePtr TSqlExpression::LambdaRule(const TRule_lambda& rule) { TVector<TString> argNames; for (const auto& arg : args) { argNames.push_back(arg.Name); - PopNamedNode(arg.Name); + if (!PopNamedNode(arg.Name)) { + return {}; + } } if (!ret) { return {}; @@ -1659,7 +1661,9 @@ bool TSqlExpression::SqlLambdaExprBody(TContext& ctx, const TRule_lambda_body& n } for (const auto& name : localNames) { - PopNamedNode(name); + if (!PopNamedNode(name)) { + return false; + } } if (!nodeExpr) { @@ -1903,7 +1907,12 @@ TNodePtr TSqlExpression::SubExpr(const TRule_xor_subexpr& node, const TTrailingQ } if (!Ctx_.PragmaRegexUseRe2) { - Ctx_.Warning(pos, TIssuesIds::CORE_LEGACY_REGEX_ENGINE) << "Legacy regex engine works incorrectly with unicode. Use PRAGMA RegexUseRe2='true';"; + if (!Ctx_.Warning(pos, TIssuesIds::CORE_LEGACY_REGEX_ENGINE, [](auto& out) { + out << "Legacy regex engine works incorrectly with unicode. " + << "Use PRAGMA RegexUseRe2='true';"; + })) { + return nullptr; + } } const auto& matcher = Ctx_.PragmaRegexUseRe2 ? @@ -1957,7 +1966,11 @@ TNodePtr TSqlExpression::SubExpr(const TRule_xor_subexpr& node, const TTrailingQ if (altCase == TRule_cond_expr::TAlt3::TBlock1::kAlt4 && !cond.GetAlt_cond_expr3().GetBlock1().GetAlt4().HasBlock1()) { - Ctx_.Warning(Ctx_.Pos(), TIssuesIds::YQL_MISSING_IS_BEFORE_NOT_NULL) << "Missing IS keyword before NOT NULL"; + if (!Ctx_.Warning(Ctx_.Pos(), TIssuesIds::YQL_MISSING_IS_BEFORE_NOT_NULL, [](auto& out) { + out << "Missing IS keyword before NOT NULL"; + })) { + return {}; + } } auto isNull = BuildIsNullOp(pos, res); @@ -1978,8 +1991,11 @@ TNodePtr TSqlExpression::SubExpr(const TRule_xor_subexpr& node, const TTrailingQ const bool oneArgNull = left->IsNull() || right->IsNull(); if (res->IsNull() || bothArgNull || (symmetric && oneArgNull)) { - Ctx_.Warning(pos, TIssuesIds::YQL_OPERATION_WILL_RETURN_NULL) - << "BETWEEN operation will return NULL here"; + if (!Ctx_.Warning(pos, TIssuesIds::YQL_OPERATION_WILL_RETURN_NULL, [](auto& out) { + out << "BETWEEN operation will return NULL here"; + })) { + return {}; + } } auto buildSubexpr = [&](const TNodePtr& left, const TNodePtr& right) { diff --git a/yql/essentials/sql/v1/sql_query.cpp b/yql/essentials/sql/v1/sql_query.cpp index b84e2df0add..5f29be58d4d 100644 --- a/yql/essentials/sql/v1/sql_query.cpp +++ b/yql/essentials/sql/v1/sql_query.cpp @@ -2157,7 +2157,11 @@ bool TSqlQuery::DeclareStatement(const TRule_declare_stmt& stmt) { } if (Ctx_.IsAlreadyDeclared(varName)) { - Ctx_.Warning(varPos, TIssuesIds::YQL_DUPLICATE_DECLARE) << "Duplicate declaration of '" << varName << "' will be ignored"; + if (!Ctx_.Warning(varPos, TIssuesIds::YQL_DUPLICATE_DECLARE, [&](auto& out) { + out << "Duplicate declaration of '" << varName << "' will be ignored"; + })) { + return false; + } } else { PushNamedAtom(varPos, varName); Ctx_.DeclareVariable(varName, varPos, typeNode); @@ -3111,7 +3115,11 @@ THashMap<TString, TPragmaDescr> PragmaDescrs{ query.Error() << "Expected string literal as a single argument for: " << pragma; return {}; } - ctx.Warning(ctx.Pos(), TIssuesIds::YQL_PRAGMA_WARNING_MSG) << *values[0].GetLiteral(); + if (!ctx.Warning(ctx.Pos(), TIssuesIds::YQL_PRAGMA_WARNING_MSG, [&](auto& out) { + out << *values[0].GetLiteral(); + })) { + return {}; + } return TNodePtr{}; }), TableElemExt("ErrorMsg", [](CB_SIG) -> TMaybe<TNodePtr> { @@ -3141,8 +3149,11 @@ THashMap<TString, TPragmaDescr> PragmaDescrs{ }), TableElemExt("DisableUnordered", [](CB_SIG) -> TMaybe<TNodePtr> { auto& ctx = query.Context(); - ctx.Warning(ctx.Pos(), TIssuesIds::YQL_DEPRECATED_PRAGMA) - << "Use of deprecated DisableUnordered pragma. It will be dropped soon"; + if (!ctx.Warning(ctx.Pos(), TIssuesIds::YQL_DEPRECATED_PRAGMA, [](auto& out) { + out << "Use of deprecated DisableUnordered pragma. It will be dropped soon"; + })) { + return {}; + } return TNodePtr{}; }), TableElemExt("RotateJoinTree", [](CB_SIG) -> TMaybe<TNodePtr> { @@ -3253,9 +3264,12 @@ THashMap<TString, TPragmaDescr> PragmaDescrs{ }), TableElemExt("DisableFlexibleTypes", [](CB_SIG) -> TMaybe<TNodePtr> { auto& ctx = query.Context(); - ctx.Warning(ctx.Pos(), TIssuesIds::YQL_DEPRECATED_PRAGMA) - << "Deprecated pragma DisableFlexibleTypes - it will be removed soon. " - "Consider submitting bug report if FlexibleTypes doesn't work for you"; + if (!ctx.Warning(ctx.Pos(), TIssuesIds::YQL_DEPRECATED_PRAGMA, [](auto& out) { + out << "Deprecated pragma DisableFlexibleTypes - it will be removed soon. " + << "Consider submitting bug report if FlexibleTypes doesn't work for you"; + })) { + return {}; + } ctx.FlexibleTypes = false; return TNodePtr{}; }), @@ -3287,9 +3301,12 @@ THashMap<TString, TPragmaDescr> PragmaDescrs{ }), TableElemExt("DisableCompactNamedExprs", [](CB_SIG) -> TMaybe<TNodePtr> { auto& ctx = query.Context(); - ctx.Warning(ctx.Pos(), TIssuesIds::YQL_DEPRECATED_PRAGMA) - << "Deprecated pragma DisableCompactNamedExprs - it will be removed soon. " - "Consider submitting bug report if CompactNamedExprs doesn't work for you"; + if (!ctx.Warning(ctx.Pos(), TIssuesIds::YQL_DEPRECATED_PRAGMA, [](auto& out) { + out << "Deprecated pragma DisableCompactNamedExprs - it will be removed soon. " + << "Consider submitting bug report if CompactNamedExprs doesn't work for you"; + })) { + return {}; + } ctx.CompactNamedExprs = false; return TNodePtr{}; }), @@ -3536,8 +3553,11 @@ TMaybe<TNodePtr> TSqlQuery::PragmaStatement(const TRule_pragma_stmt& stmt) { return {}; } if (normalizedPragma == "fast") { - Ctx_.Warning(Ctx_.Pos(), TIssuesIds::YQL_DEPRECATED_PRAGMA) - << "Use of deprecated yson.Fast pragma. It will be dropped soon"; + if (!Ctx_.Warning(Ctx_.Pos(), TIssuesIds::YQL_DEPRECATED_PRAGMA, [](auto& out) { + out << "Use of deprecated yson.Fast pragma. It will be dropped soon"; + })) { + return {}; + } return TNodePtr{}; } else if (normalizedPragma == "autoconvert") { Ctx_.PragmaYsonAutoConvert = true; @@ -3899,7 +3919,9 @@ TNodePtr TSqlQuery::Build(const TSQLv1ParserAST& ast) { } auto result = BuildQuery(Ctx_.Pos(), blocks, true, Ctx_.Scoped, Ctx_.SeqMode); - WarnUnusedNodes(); + if (!WarnUnusedNodes()) { + return nullptr; + } return result; } diff --git a/yql/essentials/sql/v1/sql_select.cpp b/yql/essentials/sql/v1/sql_select.cpp index 3dd5043438d..acfc83ae293 100644 --- a/yql/essentials/sql/v1/sql_select.cpp +++ b/yql/essentials/sql/v1/sql_select.cpp @@ -38,7 +38,11 @@ bool CollectJoinLinkSettings(TPosition pos, TJoinLinkSettings& linkSettings, TCo linkSettings.Compact = true; continue; } else { - ctx.Warning(hint.Pos, TIssuesIds::YQL_UNUSED_HINT) << "Unsupported join hint: " << hint.Name; + if (!ctx.Warning(hint.Pos, TIssuesIds::YQL_UNUSED_HINT, [&](auto& out) { + out << "Unsupported join hint: " << hint.Name; + })) { + return false; + } } if (TJoinLinkSettings::EStrategy::Default == linkSettings.Strategy) { @@ -159,7 +163,11 @@ bool TSqlSelect::JoinOp(ISource* join, const TRule_join_source::TBlock3& block, } joinOp = NormalizeJoinOp(joinOp); if (linkSettings.Strategy != TJoinLinkSettings::EStrategy::Default && joinOp == "Cross") { - Ctx_.Warning(Ctx_.Pos(), TIssuesIds::YQL_UNUSED_HINT) << "Non-default join strategy will not be used for CROSS JOIN"; + if (!Ctx_.Warning(Ctx_.Pos(), TIssuesIds::YQL_UNUSED_HINT, [](auto& out) { + out << "Non-default join strategy will not be used for CROSS JOIN"; + })) { + return false; + } linkSettings.Strategy = TJoinLinkSettings::EStrategy::Default; } @@ -999,7 +1007,11 @@ TSourcePtr TSqlSelect::SelectCore(const TRule_select_core& node, const TWriteSet uniqueSets.insert_unique(NSorted::TSimpleSet<TString>(hint.Values.cbegin(), hint.Values.cend())); distinctSets.insert_unique(NSorted::TSimpleSet<TString>(hint.Values.cbegin(), hint.Values.cend())); } else { - Ctx_.Warning(hint.Pos, TIssuesIds::YQL_UNUSED_HINT) << "Hint " << hint.Name << " will not be used"; + if (!Ctx_.Warning(hint.Pos, TIssuesIds::YQL_UNUSED_HINT, [&](auto& out) { + out << "Hint " << hint.Name << " will not be used"; + })) { + return nullptr; + } } } @@ -1395,11 +1407,14 @@ TSourcePtr TSqlSelect::BuildStmt(const TRule& node, TPosition& pos) { if (assumeOrderBy) { YQL_ENSURE(!orderBy.empty()); - Ctx_.Warning(orderBy[0]->OrderExpr->GetPos(), TIssuesIds::WARNING) - << "ASSUME ORDER BY is used, " - << "but UNION, INTERSECT and EXCEPT " - << "operators have no ordering guarantees, " - << "therefore consider using ORDER BY"; + if (!Ctx_.Warning(orderBy[0]->OrderExpr->GetPos(), TIssuesIds::WARNING, [](auto& out) { + out << "ASSUME ORDER BY is used, " + << "but UNION, INTERSECT and EXCEPT " + << "operators have no ordering guarantees, " + << "therefore consider using ORDER BY"; + })) { + return nullptr; + } } if (orderBy) { diff --git a/yql/essentials/sql/v1/sql_translation.cpp b/yql/essentials/sql/v1/sql_translation.cpp index 7d1dc51c20f..e3ff7464795 100644 --- a/yql/essentials/sql/v1/sql_translation.cpp +++ b/yql/essentials/sql/v1/sql_translation.cpp @@ -1216,7 +1216,12 @@ bool TSqlTranslation::ClusterExpr(const TRule_cluster_expr& node, bool allowWild isBinding = true; break; case NSQLTranslation::EBindingsMode::DROP_WITH_WARNING: - Ctx_.Warning(Ctx_.Pos(), TIssuesIds::YQL_DEPRECATED_BINDINGS) << "Please remove 'bindings.' from your query, the support for this syntax will be dropped soon"; + if (!Ctx_.Warning(Ctx_.Pos(), TIssuesIds::YQL_DEPRECATED_BINDINGS, [](auto& out) { + out << "Please remove 'bindings.' from your query, " + << "the support for this syntax will be dropped soon"; + })) { + return false; + } Ctx_.IncrementMonCounter("sql_errors", "DeprecatedBinding"); [[fallthrough]]; case NSQLTranslation::EBindingsMode::DROP: @@ -3537,8 +3542,12 @@ bool TSqlTranslation::TableHintImpl(const TRule_table_hint& rule, TTableHints& h auto pos = Ctx_.Pos(); if (!altCurrent && !warn) { - Ctx_.Warning(pos, TIssuesIds::YQL_DEPRECATED_POSITIONAL_SCHEMA) - << "Deprecated syntax for positional schema: please use 'column type' instead of 'type AS column'"; + if (!Ctx_.Warning(pos, TIssuesIds::YQL_DEPRECATED_POSITIONAL_SCHEMA, [](auto& out) { + out << "Deprecated syntax for positional schema: " + << "please use 'column type' instead of 'type AS column'"; + })) { + return false; + } warn = true; } @@ -4472,7 +4481,11 @@ bool TSqlTranslation::IsValidFrameSettings(TContext& ctx, const TFrameSpecificat if (*beginValue > *endValue) { YQL_ENSURE(begin.Bound); - ctx.Warning(begin.Bound->GetPos(), TIssuesIds::YQL_EMPTY_WINDOW_FRAME) << "Used frame specification implies empty window frame"; + if (!ctx.Warning(begin.Bound->GetPos(), TIssuesIds::YQL_EMPTY_WINDOW_FRAME, [](auto& out) { + out << "Used frame specification implies empty window frame"; + })) { + return false; + } } } @@ -4779,7 +4792,9 @@ TNodePtr TSqlTranslation::DoStatement(const TRule_do_stmt& stmt, bool makeLambda const bool hasValidBody = DefineActionOrSubqueryBody(query, innerBlocks, body); auto ret = hasValidBody ? BuildQuery(Ctx_.Pos(), innerBlocks, false, Ctx_.Scoped, Ctx_.SeqMode) : nullptr; - WarnUnusedNodes(); + if (!WarnUnusedNodes()) { + return nullptr; + } Ctx_.ScopeLevel--; Ctx_.Scoped = saveScoped; @@ -4894,7 +4909,9 @@ bool TSqlTranslation::DefineActionOrSubqueryStatement(const TRule_define_action_ } auto ret = hasValidBody ? BuildQuery(Ctx_.Pos(), innerBlocks, false, Ctx_.Scoped, Ctx_.SeqMode) : nullptr; - WarnUnusedNodes(); + if (!WarnUnusedNodes()) { + return false; + } Ctx_.Scoped = saveScoped; Ctx_.ScopeLevel--; Ctx_.Settings.Mode = saveMode; @@ -4975,7 +4992,10 @@ TNodePtr TSqlTranslation::ForStatement(const TRule_for_stmt& stmt) { --Ctx_.ParallelModeCount; } - PopNamedNode(itemArgName); + if (!PopNamedNode(itemArgName)) { + return {}; + } + if (!bodyNode) { return{}; } @@ -5646,7 +5666,9 @@ bool TSqlTranslation::ParseStreamingQueryDefinition(const TRule_streaming_query_ const auto& inlineAction = node.GetRule_inline_action3(); const bool hasValidBody = DefineActionOrSubqueryBody(query, innerBlocks, inlineAction.GetRule_define_action_or_subquery_body2()); auto queryNode = hasValidBody ? BuildQuery(Ctx_.Pos(), innerBlocks, false, Ctx_.Scoped, Ctx_.SeqMode) : nullptr; - WarnUnusedNodes(); + if (!WarnUnusedNodes()) { + return false; + } Ctx_.ScopeLevel--; Ctx_.Scoped = saveScoped; diff --git a/yql/essentials/sql/v1/sql_ut_common.h b/yql/essentials/sql/v1/sql_ut_common.h index a47b22aa0bc..d961a33ea6c 100644 --- a/yql/essentials/sql/v1/sql_ut_common.h +++ b/yql/essentials/sql/v1/sql_ut_common.h @@ -4328,6 +4328,39 @@ Y_UNIT_TEST_SUITE(SqlToYQLErrors) { "<main>:1:10: Warning: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n"); } + Y_UNIT_TEST(WarnForAggregationBySelectAliasAsError) { + NSQLTranslation::TTranslationSettings settings; + + NYql::TAstParseResult res = SqlToYqlWithSettings(R"sql( + PRAGMA Warning("error", "*"); + SELECT c + 1 AS c + FROM plato.Input + GROUP BY c; + )sql", settings); + + UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString()); + UNIT_ASSERT_NO_DIFF(Err2Str(res), + "<main>:5:22: Error: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n" + "<main>:3:22: Error: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n"); + } + + Y_UNIT_TEST(WarnForAggregationBySelectAliasAsErrorStrict) { + NSQLTranslation::TTranslationSettings settings; + settings.Flags.emplace("StrictWarningAsError"); + + NYql::TAstParseResult res = SqlToYqlWithSettings(R"sql( + PRAGMA Warning("error", "*"); + SELECT c + 1 AS c + FROM plato.Input + GROUP BY c; + )sql", settings); + + UNIT_ASSERT_C(!res.IsOk(), res.Issues.ToString()); + UNIT_ASSERT_NO_DIFF(Err2Str(res), + "<main>:5:22: Error: GROUP BY will aggregate by column `c` instead of aggregating by SELECT expression with same alias, code: 4532\n" + "<main>:3:22: Error: You should probably use alias in GROUP BY instead of using it here. Please consult documentation for more details, code: 4532\n"); + } + Y_UNIT_TEST(NoWarnForAggregationBySelectAliasWhenAggrFunctionsAreUsedInAlias) { NYql::TAstParseResult res = SqlToYql("select\n" " cast(avg(val) as int) as value,\n" @@ -5873,6 +5906,23 @@ select FormatType($f()); UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:2:23: Warning: Hint foo will not be used, code: 4534\n"); } + Y_UNIT_TEST(WarnForUnusedSqlHintAsError) { + NSQLTranslation::TTranslationSettings settings; + + TString query = R"sql( + pragma warning("error", "*"); + + select * from plato.Input1 as a + join /*+ merge() */ plato.Input2 as b using(key); + select --+ foo(bar) + 1; + )sql"; + + NYql::TAstParseResult res = SqlToYqlWithSettings(query, settings); + UNIT_ASSERT_C(!res.IsOk(), res.Issues.ToString()); + UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:6:35: Error: Hint foo will not be used, code: 4534\n"); + } + Y_UNIT_TEST(WarnForDeprecatedSchema) { NSQLTranslation::TTranslationSettings settings; settings.ClusterMapping["s3bucket"] = NYql::S3ProviderName; diff --git a/yql/essentials/sql/v1/sql_values.cpp b/yql/essentials/sql/v1/sql_values.cpp index 2375fa6ec91..83608995c50 100644 --- a/yql/essentials/sql/v1/sql_values.cpp +++ b/yql/essentials/sql/v1/sql_values.cpp @@ -28,8 +28,12 @@ TSourcePtr TSqlValues::Build(const TRule_values_stmt& node, TPosition& valuesPos auto columns = derivedColumns; if (Ctx_.WarnUnnamedColumns && columns.size() < columnsCount) { - Ctx_.Warning(valuesPos, TIssuesIds::YQL_UNNAMED_COLUMN) - << "Autogenerated column names column" << columns.size() << "...column" << columnsCount - 1 << " will be used here"; + if (!Ctx_.Warning(valuesPos, TIssuesIds::YQL_UNNAMED_COLUMN, [&](auto& out) { + out << "Autogenerated column names column" << columns.size() + << "...column" << columnsCount - 1 << " will be used here"; + })) { + return nullptr; + } } while (columns.size() < columnsCount) { |
