11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4- using System . Diagnostics . CodeAnalysis ;
54using EntityFrameworkCore . Jet . Internal ;
5+ using Microsoft . EntityFrameworkCore . Query ;
66using Microsoft . EntityFrameworkCore . Query . SqlExpressions ;
7+ using System . Diagnostics . CodeAnalysis ;
78
89namespace EntityFrameworkCore . Jet . Query . Internal ;
910
@@ -15,6 +16,10 @@ namespace EntityFrameworkCore.Jet.Query.Internal;
1516/// </summary>
1617public class JetQueryableMethodTranslatingExpressionVisitor : RelationalQueryableMethodTranslatingExpressionVisitor
1718{
19+ protected readonly RelationalQueryCompilationContext queryCompilationContext ;
20+
21+ private readonly bool _subquery ;
22+
1823 /// <summary>
1924 /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
2025 /// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -27,6 +32,8 @@ public JetQueryableMethodTranslatingExpressionVisitor(
2732 RelationalQueryCompilationContext queryCompilationContext )
2833 : base ( dependencies , relationalDependencies , queryCompilationContext )
2934 {
35+ this . queryCompilationContext = queryCompilationContext ;
36+ _subquery = false ;
3037 }
3138
3239 /// <summary>
@@ -39,6 +46,8 @@ protected JetQueryableMethodTranslatingExpressionVisitor(
3946 JetQueryableMethodTranslatingExpressionVisitor parentVisitor )
4047 : base ( parentVisitor )
4148 {
49+ this . queryCompilationContext = parentVisitor . queryCompilationContext ;
50+ _subquery = true ;
4251 }
4352
4453 /// <summary>
@@ -140,9 +149,164 @@ protected override bool IsValidSelectExpressionForExecuteUpdate(
140149 return false ;
141150 }
142151
143- protected override ShapedQueryExpression ? TranslateFirstOrDefault ( ShapedQueryExpression source , LambdaExpression ? predicate ,
144- Type returnType , bool returnDefault )
152+ protected override ShapedQueryExpression ? TranslateElementAtOrDefault (
153+ ShapedQueryExpression source ,
154+ Expression index ,
155+ bool returnDefault )
156+ {
157+ var selectExpression = ( SelectExpression ) source . QueryExpression ;
158+ var translation = TranslateExpression ( index ) ;
159+ if ( translation == null )
160+ {
161+ return null ;
162+ }
163+
164+ if ( ! IsOrdered ( selectExpression ) )
165+ {
166+ queryCompilationContext . Logger . RowLimitingOperationWithoutOrderByWarning ( ) ;
167+ }
168+
169+ selectExpression . ApplyOffset ( translation ) ;
170+ JetApplyLimit ( selectExpression , TranslateExpression ( Expression . Constant ( 1 ) ) ! ) ;
171+
172+ return source ;
173+ }
174+
175+ protected override ShapedQueryExpression ? TranslateFirstOrDefault (
176+ ShapedQueryExpression source ,
177+ LambdaExpression ? predicate ,
178+ Type returnType ,
179+ bool returnDefault )
180+ {
181+ if ( predicate != null )
182+ {
183+ var translatedSource = TranslateWhere ( source , predicate ) ;
184+ if ( translatedSource == null )
185+ {
186+ return null ;
187+ }
188+
189+ source = translatedSource ;
190+ }
191+
192+ var selectExpression = ( SelectExpression ) source . QueryExpression ;
193+ if ( selectExpression . Predicate == null
194+ && selectExpression . Orderings . Count == 0 )
195+ {
196+ queryCompilationContext . Logger . FirstWithoutOrderByAndFilterWarning ( ) ;
197+ }
198+
199+ JetApplyLimit ( selectExpression , TranslateExpression ( Expression . Constant ( 1 ) ) ! ) ;
200+
201+ return source . ShaperExpression . Type != returnType
202+ ? source . UpdateShaperExpression ( Expression . Convert ( source . ShaperExpression , returnType ) )
203+ : source ;
204+ }
205+
206+ protected override ShapedQueryExpression ? TranslateLastOrDefault (
207+ ShapedQueryExpression source ,
208+ LambdaExpression ? predicate ,
209+ Type returnType ,
210+ bool returnDefault )
211+ {
212+ var selectExpression = ( SelectExpression ) source . QueryExpression ;
213+ if ( selectExpression . Orderings . Count == 0 )
214+ {
215+ throw new InvalidOperationException (
216+ RelationalStrings . LastUsedWithoutOrderBy ( returnDefault ? nameof ( Queryable . LastOrDefault ) : nameof ( Queryable . Last ) ) ) ;
217+ }
218+
219+ if ( predicate != null )
220+ {
221+ var translatedSource = TranslateWhere ( source , predicate ) ;
222+ if ( translatedSource == null )
223+ {
224+ return null ;
225+ }
226+
227+ source = translatedSource ;
228+ }
229+
230+ selectExpression . ReverseOrderings ( ) ;
231+ JetApplyLimit ( selectExpression , TranslateExpression ( Expression . Constant ( 1 ) ) ! ) ;
232+
233+ return source . ShaperExpression . Type != returnType
234+ ? source . UpdateShaperExpression ( Expression . Convert ( source . ShaperExpression , returnType ) )
235+ : source ;
236+ }
237+
238+ protected override ShapedQueryExpression ? TranslateSingleOrDefault (
239+ ShapedQueryExpression source ,
240+ LambdaExpression ? predicate ,
241+ Type returnType ,
242+ bool returnDefault )
145243 {
146- return base . TranslateFirstOrDefault ( source , predicate , returnType , returnDefault ) ;
244+ if ( predicate != null )
245+ {
246+ var translatedSource = TranslateWhere ( source , predicate ) ;
247+ if ( translatedSource == null )
248+ {
249+ return null ;
250+ }
251+
252+ source = translatedSource ;
253+ }
254+
255+ var selectExpression = ( SelectExpression ) source . QueryExpression ;
256+ JetApplyLimit ( selectExpression , TranslateExpression ( Expression . Constant ( _subquery ? 1 : 2 ) ) ! ) ;
257+
258+ return source . ShaperExpression . Type != returnType
259+ ? source . UpdateShaperExpression ( Expression . Convert ( source . ShaperExpression , returnType ) )
260+ : source ;
147261 }
262+
263+ protected override ShapedQueryExpression ? TranslateTake ( ShapedQueryExpression source , Expression count )
264+ {
265+ var selectExpression = ( SelectExpression ) source . QueryExpression ;
266+ var translation = TranslateExpression ( count ) ;
267+ if ( translation == null )
268+ {
269+ return null ;
270+ }
271+
272+ if ( ! IsOrdered ( selectExpression ) )
273+ {
274+ queryCompilationContext . Logger . RowLimitingOperationWithoutOrderByWarning ( ) ;
275+ }
276+
277+ JetApplyLimit ( selectExpression , translation ) ;
278+
279+ return source ;
280+ }
281+
282+ private void JetApplyLimit ( SelectExpression selectExpression , SqlExpression limit )
283+ {
284+ var oldLimit = selectExpression . Limit ;
285+
286+ if ( oldLimit is null )
287+ {
288+ selectExpression . SetLimit ( limit ) ;
289+ return ;
290+ }
291+
292+ if ( oldLimit is SqlConstantExpression { Value : int oldConst } && limit is SqlConstantExpression { Value : int newConst } )
293+ {
294+ // if both the old and new limit are constants, use the smaller one
295+ // (aka constant-fold LEAST(constA, constB))
296+ if ( oldConst > newConst )
297+ {
298+ selectExpression . SetLimit ( limit ) ;
299+ }
300+
301+ return ;
302+ }
303+
304+ if ( oldLimit . Equals ( limit ) )
305+ {
306+ return ;
307+ }
308+
309+ selectExpression . ApplyLimit ( limit ) ;
310+ }
311+
148312}
0 commit comments