6464#include < util/stream/str.h>
6565#include < util/stream/input.h>
6666#include < util/stream/file.h>
67+ #include < util/string/type.h>
6768#include < util/system/execpath.h>
6869#include < util/system/guard.h>
6970#include < util/system/shellcommand.h>
@@ -214,46 +215,50 @@ TString DebugPath(NYT::TRichYPath path) {
214215 return NYT::NodeToCanonicalYsonString (NYT::PathToNode (path), NYT::NYson::EYsonFormat::Text) + " (" + std::to_string (numColumns) + " columns)" ;
215216}
216217
217- void GetIntegerConstraints (const TExprNode::TPtr& column, bool & isSigned, ui64& minValueAbs, ui64& maxValueAbs) {
218- EDataSlot toType = column->GetTypeAnn ()->Cast <TDataExprType>()->GetSlot ();
218+ void GetIntegerConstraints (const TExprNode::TPtr& column, bool & isSigned, ui64& minValueAbs, ui64& maxValueAbs, bool & isOptional) {
219+ const TDataExprType* dataType = nullptr ;
220+ const bool columnHasDataType = IsDataOrOptionalOfData (column->GetTypeAnn (), isOptional, dataType);
221+ YQL_ENSURE (columnHasDataType, " YtQLFilter: unsupported type of column " << column->Dump ());
222+ YQL_ENSURE (dataType);
223+ const EDataSlot dataSlot = dataType->Cast <TDataExprType>()->GetSlot ();
219224
220- // AllowIntegralConversion (may consider some refactoring)
221- if (toType == EDataSlot::Uint8) {
225+ // looks like AllowIntegralConversion (may consider some refactoring)
226+ if (dataSlot == EDataSlot::Uint8) {
222227 isSigned = false ;
223228 minValueAbs = 0 ;
224229 maxValueAbs = Max<ui8>();
225230 }
226- else if (toType == EDataSlot::Uint16) {
231+ else if (dataSlot == EDataSlot::Uint16) {
227232 isSigned = false ;
228233 minValueAbs = 0 ;
229234 maxValueAbs = Max<ui16>();
230235 }
231- else if (toType == EDataSlot::Uint32) {
236+ else if (dataSlot == EDataSlot::Uint32) {
232237 isSigned = false ;
233238 minValueAbs = 0 ;
234239 maxValueAbs = Max<ui32>();
235240 }
236- else if (toType == EDataSlot::Uint64) {
241+ else if (dataSlot == EDataSlot::Uint64) {
237242 isSigned = false ;
238243 minValueAbs = 0 ;
239244 maxValueAbs = Max<ui64>();
240245 }
241- else if (toType == EDataSlot::Int8) {
246+ else if (dataSlot == EDataSlot::Int8) {
242247 isSigned = true ;
243248 minValueAbs = (ui64)Max<i8 >() + 1 ;
244249 maxValueAbs = (ui64)Max<i8 >();
245250 }
246- else if (toType == EDataSlot::Int16) {
251+ else if (dataSlot == EDataSlot::Int16) {
247252 isSigned = true ;
248253 minValueAbs = (ui64)Max<i16 >() + 1 ;
249254 maxValueAbs = (ui64)Max<i16 >();
250255 }
251- else if (toType == EDataSlot::Int32) {
256+ else if (dataSlot == EDataSlot::Int32) {
252257 isSigned = true ;
253258 minValueAbs = (ui64)Max<i32 >() + 1 ;
254259 maxValueAbs = (ui64)Max<i32 >();
255260 }
256- else if (toType == EDataSlot::Int64) {
261+ else if (dataSlot == EDataSlot::Int64) {
257262 isSigned = true ;
258263 minValueAbs = (ui64)Max<i64 >() + 1 ;
259264 maxValueAbs = (ui64)Max<i64 >();
@@ -286,51 +291,102 @@ void ConvertComparisonForQL(const TStringBuf& opName, TStringBuilder& result) {
286291 }
287292}
288293
289- void GenerateInputQueryIntegerComparison (const TStringBuf& opName, const TExprNode::TPtr& intColumn, const TExprNode::TPtr& intValue, TStringBuilder& result) {
294+ void GenerateInputQueryIntegerComparison (const TStringBuf& opName, const TExprNode::TPtr& intColumn, const TExprNode::TPtr& intValue, const std::optional<bool >& nullValue, TStringBuilder& result) {
295+ if (TMaybeNode<TCoNull>(intValue) || TMaybeNode<TCoNothing>(intValue)) {
296+ YQL_ENSURE (nullValue.has_value (), " YtQLFilter: optional type without coalesce is not supported" );
297+ if (nullValue.value ()) {
298+ result << " TRUE" ;
299+ } else {
300+ result << " FALSE" ;
301+ }
302+ return ;
303+ }
304+
305+ TMaybeNode<TCoIntegralCtor> maybeIntValue;
306+ if (auto maybeJustValue = TMaybeNode<TCoJust>(intValue)) {
307+ maybeIntValue = TMaybeNode<TCoIntegralCtor>(maybeJustValue.Cast ().Input ().Ptr ());
308+ } else {
309+ maybeIntValue = TMaybeNode<TCoIntegralCtor>(intValue);
310+ }
311+ YQL_ENSURE (maybeIntValue);
312+
290313 bool columnsIsSigned;
291314 ui64 minValueAbs;
292315 ui64 maxValueAbs;
293- GetIntegerConstraints (intColumn, columnsIsSigned, minValueAbs, maxValueAbs);
316+ bool columnIsOptional;
317+ GetIntegerConstraints (intColumn, columnsIsSigned, minValueAbs, maxValueAbs, columnIsOptional);
318+ YQL_ENSURE (!columnIsOptional || columnIsOptional && nullValue.has_value (), " YtQLFilter: optional type without coalesce is not supported" );
294319
295- const auto maybeInt = TMaybeNode<TCoIntegralCtor>(intValue);
296- YQL_ENSURE (maybeInt);
297320 bool hasSign;
298321 bool isSigned;
299322 ui64 valueAbs;
300- ExtractIntegralValue (maybeInt .Ref (), false , hasSign, isSigned, valueAbs);
323+ ExtractIntegralValue (maybeIntValue .Ref (), false , hasSign, isSigned, valueAbs);
301324
325+ std::optional<bool > constantFilter;
302326 if (!hasSign && valueAbs > maxValueAbs) {
303- // value is greater than maximum
327+ // Value is greater than maximum.
304328 if (opName == " >" || opName == " >=" || opName == " ==" ) {
305- result << " FALSE " ;
329+ constantFilter = false ;
306330 } else {
307- result << " TRUE " ;
331+ constantFilter = true ;
308332 }
309333 } else if (hasSign && valueAbs > minValueAbs) {
310- // value is less than minimum
334+ // Value is less than minimum.
311335 if (opName == " <" || opName == " <=" || opName == " ==" ) {
312- result << " FALSE " ;
336+ constantFilter = false ;
313337 } else {
314- result << " TRUE" ;
338+ constantFilter = true ;
339+ }
340+ }
341+
342+ const auto columnName = intColumn->ChildPtr (1 )->Content ();
343+ if (!constantFilter.has_value ()) {
344+ // Value is in the range, comparison is not constant.
345+ if (columnIsOptional) {
346+ const bool isLess = opName == " <" || opName == " <=" ;
347+ if (isLess && !nullValue.value ()) {
348+ // QL will handle 'x [operation] NULL' as TRUE here, but we need FALSE.
349+ QuoteColumnForQL (columnName, result);
350+ result << " != NULL AND " ;
351+ } else if (!isLess && nullValue.value ()) {
352+ // QL will handle 'x [operation] NULL' as FALSE here, but we need TRUE.
353+ QuoteColumnForQL (columnName, result);
354+ result << " = NULL OR " ;
355+ }
315356 }
316- } else {
317- // value is in the range
318- const auto columnName = intColumn->ChildPtr (1 )->Content ();
319- const auto valueStr = maybeInt.Cast ().Literal ().Value ();
320357 QuoteColumnForQL (columnName, result);
321358 result << " " ;
322359 ConvertComparisonForQL (opName, result);
360+ const auto valueStr = maybeIntValue.Cast ().Literal ().Value ();
323361 result << " " << valueStr;
362+ } else if (constantFilter.value ()) {
363+ // Value is out of the range, comparison is always TRUE.
364+ if (columnIsOptional && !nullValue.value ()) {
365+ // Handle comparison with NULL as FALSE.
366+ QuoteColumnForQL (columnName, result);
367+ result << " IS NOT NULL" ;
368+ } else {
369+ result << " TRUE" ;
370+ }
371+ } else {
372+ // Value is out of the range, comparison is always FALSE.
373+ if (columnIsOptional && nullValue.value ()) {
374+ // Handle comparison with NULL as TRUE.
375+ QuoteColumnForQL (columnName, result);
376+ result << " IS NULL" ;
377+ } else {
378+ result << " FALSE" ;
379+ }
324380 }
325381}
326382
327- void GenerateInputQueryComparison (const TCoCompare& op, TStringBuilder& result) {
383+ void GenerateInputQueryComparison (const TCoCompare& op, const std::optional< bool >& nullValue, TStringBuilder& result) {
328384 YQL_ENSURE (op.Ref ().IsCallable ({" <" , " <=" , " >" , " >=" , " ==" , " !=" }));
329385 const auto left = op.Left ().Ptr ();
330386 const auto right = op.Right ().Ptr ();
331387
332388 if (left->IsCallable (" Member" )) {
333- GenerateInputQueryIntegerComparison (op.CallableName (), left, right, result);
389+ GenerateInputQueryIntegerComparison (op.CallableName (), left, right, nullValue, result);
334390 } else {
335391 YQL_ENSURE (right->IsCallable (" Member" ));
336392 auto invertedOp = op.CallableName ();
@@ -343,17 +399,29 @@ void GenerateInputQueryComparison(const TCoCompare& op, TStringBuilder& result)
343399 } else if (invertedOp == " >=" ) {
344400 invertedOp = " <=" ;
345401 }
346- GenerateInputQueryIntegerComparison (invertedOp, right, left, result);
402+ GenerateInputQueryIntegerComparison (invertedOp, right, left, nullValue, result);
347403 }
348404}
349405
350406void GenerateInputQueryWhereExpression (const TExprNode::TPtr& node, TStringBuilder& result) {
351407 if (const auto maybeCompare = TMaybeNode<TCoCompare>(node)) {
352- GenerateInputQueryComparison (maybeCompare.Cast (), result);
408+ GenerateInputQueryComparison (maybeCompare.Cast (), {}, result);
353409 } else if (node->IsCallable (" Not" )) {
354- result << " NOT (" ;
410+ const auto child = node->ChildPtr (0 );
411+ if (child->IsCallable (" Exists" )) {
412+ // Do not generate NOT (x IS NOT NULL).
413+ result << " (" ;
414+ GenerateInputQueryWhereExpression (child->ChildPtr (0 ), result);
415+ result << " ) IS NULL" ;
416+ } else {
417+ result << " NOT (" ;
418+ GenerateInputQueryWhereExpression (child, result);
419+ result << " )" ;
420+ }
421+ } else if (node->IsCallable (" Exists" )) {
422+ result << " (" ;
355423 GenerateInputQueryWhereExpression (node->ChildPtr (0 ), result);
356- result << " )" ;
424+ result << " ) IS NOT NULL " ;
357425 } else if (node->IsCallable ({" And" , " Or" })) {
358426 const TStringBuf op = node->IsCallable (" And" ) ? " AND" : " OR" ;
359427
@@ -367,6 +435,17 @@ void GenerateInputQueryWhereExpression(const TExprNode::TPtr& node, TStringBuild
367435 GenerateInputQueryWhereExpression (node->Child (i), result);
368436 result << " )" ;
369437 };
438+ } else if (node->IsCallable (" Coalesce" )) {
439+ YQL_ENSURE (node->ChildrenSize () == 2 );
440+ const auto op = TMaybeNode<TCoCompare>(node->Child (0 )).Cast ();
441+ const auto nullValueStr = TMaybeNode<TCoBool>(node->Child (1 )).Cast ().Literal ().Value ();
442+ const std::optional<bool > nullValue (IsTrue (nullValueStr));
443+ GenerateInputQueryComparison (op, nullValue, result);
444+ } else if (const auto maybeBool = TMaybeNode<TCoBool>(node)) {
445+ result << maybeBool.Cast ().Literal ().Value ();
446+ } else if (node->IsCallable (" Member" )) {
447+ const auto columnName = node->ChildPtr (1 )->Content ();
448+ QuoteColumnForQL (columnName, result);
370449 } else {
371450 YQL_ENSURE (false , " unexpected node type" );
372451 }
0 commit comments