@@ -614,11 +614,29 @@ protected override Expression VisitSqlUnary(SqlUnaryExpression sqlUnaryExpressio
614614 Visit ( sqlUnaryExpression . Operand ) ;
615615 return sqlUnaryExpression ;
616616
617- // Not operation on full-text queries
617+ // NOT operation on full-text queries
618618 case ExpressionType . Not when sqlUnaryExpression . Operand . TypeMapping . ClrType == typeof ( NpgsqlTsQuery ) :
619619 Sql . Append ( "!!" ) ;
620620 Visit ( sqlUnaryExpression . Operand ) ;
621621 return sqlUnaryExpression ;
622+
623+ // NOT over expression types which have fancy embedded negation
624+ case ExpressionType . Not
625+ when sqlUnaryExpression . Type == typeof ( bool ) :
626+ {
627+ switch ( sqlUnaryExpression . Operand )
628+ {
629+ case PgRegexMatchExpression regexMatch :
630+ VisitRegexMatch ( regexMatch , negated : true ) ;
631+ return sqlUnaryExpression ;
632+
633+ case PgILikeExpression iLike :
634+ VisitILike ( iLike , negated : true ) ;
635+ return sqlUnaryExpression ;
636+ }
637+
638+ break ;
639+ }
622640 }
623641
624642 return base . VisitSqlUnary ( sqlUnaryExpression ) ;
@@ -907,29 +925,25 @@ protected virtual Expression VisitArraySlice(PgArraySliceExpression expression)
907925 }
908926
909927 /// <summary>
910- /// Visits the children of a <see cref="PgRegexMatchExpression" /> .
928+ /// Produces SQL for PostgreSQL regex matching .
911929 /// </summary>
912- /// <param name="expression">The expression.</param>
913- /// <returns>
914- /// An <see cref="Expression" />.
915- /// </returns>
916930 /// <remarks>
917931 /// See: http://www.postgresql.org/docs/current/static/functions-matching.html
918932 /// </remarks>
919- protected virtual Expression VisitRegexMatch ( PgRegexMatchExpression expression )
933+ protected virtual Expression VisitRegexMatch ( PgRegexMatchExpression expression , bool negated = false )
920934 {
921935 var options = expression . Options ;
922936
923937 Visit ( expression . Match ) ;
924938
925939 if ( options . HasFlag ( RegexOptions . IgnoreCase ) )
926940 {
927- Sql . Append ( " ~* " ) ;
941+ Sql . Append ( negated ? " !~* " : " ~* " ) ;
928942 options &= ~ RegexOptions . IgnoreCase ;
929943 }
930944 else
931945 {
932- Sql . Append ( " ~ " ) ;
946+ Sql . Append ( negated ? " !~ " : " ~ " ) ;
933947 }
934948
935949 // PG regexps are single-line by default
@@ -1012,16 +1026,22 @@ protected virtual Expression VisitRowValue(PgRowValueExpression rowValueExpressi
10121026 }
10131027
10141028 /// <summary>
1015- /// Visits the children of an <see cref="PgILikeExpression" />.
1029+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
1030+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
1031+ /// any release. You should only use it directly in your code with extreme caution and knowing that
1032+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
10161033 /// </summary>
1017- /// <param name="likeExpression">The expression.</param>
1018- /// <returns>
1019- /// An <see cref="Expression" />.
1020- /// </returns>
1021- protected virtual Expression VisitILike ( PgILikeExpression likeExpression )
1034+ protected virtual Expression VisitILike ( PgILikeExpression likeExpression , bool negated = false )
10221035 {
10231036 Visit ( likeExpression . Match ) ;
1037+
1038+ if ( negated )
1039+ {
1040+ Sql . Append ( " NOT" ) ;
1041+ }
1042+
10241043 Sql . Append ( " ILIKE " ) ;
1044+
10251045 Visit ( likeExpression . Pattern ) ;
10261046
10271047 if ( likeExpression . EscapeChar is not null )
0 commit comments