@@ -56,7 +56,7 @@ public static bool TryExtractArray(
5656 Offset : null
5757 } select
5858 && ( ignorePredicate || select . Predicate is null )
59- // We can only apply the indexing if the JSON array is ordered by its natural ordered, i.e. by the "ordinality" column that
59+ // We can only apply the indexing if the array is ordered by its natural ordered, i.e. by the "ordinality" column that
6060 // we created in TranslatePrimitiveCollection. For example, if another ordering has been applied (e.g. by the array elements
6161 // themselves), we can no longer simply index into the original array.
6262 && ( ignoreOrderings
@@ -76,6 +76,78 @@ public static bool TryExtractArray(
7676 return false ;
7777 }
7878
79+ /// <summary>
80+ /// If the given <paramref name="source" /> wraps a JSON-array-returning expression without any additional clauses (e.g. filter,
81+ /// ordering...), returns that expression.
82+ /// </summary>
83+ /// <remarks>
84+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
85+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
86+ /// any release. You should only use it directly in your code with extreme caution and knowing that
87+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
88+ /// </remarks>
89+ public static bool TryExtractJsonArray (
90+ this ShapedQueryExpression source ,
91+ [ NotNullWhen ( true ) ] out SqlExpression ? jsonArray ,
92+ [ NotNullWhen ( true ) ] out SqlExpression ? projectedElement ,
93+ out bool isElementNullable ,
94+ bool ignoreOrderings = false ,
95+ bool ignorePredicate = false )
96+ {
97+ if ( source . QueryExpression is SelectExpression
98+ {
99+ Tables :
100+ [
101+ TableValuedFunctionExpression
102+ {
103+ Name : "jsonb_array_elements_text" or "json_array_elements_text" ,
104+ Arguments : [ var json ]
105+ } tvf
106+ ] ,
107+ GroupBy : [ ] ,
108+ Having : null ,
109+ IsDistinct : false ,
110+ Limit : null ,
111+ Offset : null
112+ } select
113+ && ( ignorePredicate || select . Predicate is null )
114+ // We can only apply the indexing if the array is ordered by its natural ordered, i.e. by the "ordinality" column that
115+ // we created in TranslatePrimitiveCollection. For example, if another ordering has been applied (e.g. by the array elements
116+ // themselves), we can no longer simply index into the original array.
117+ && ( ignoreOrderings
118+ || select . Orderings is [ ]
119+ || ( select . Orderings is [ { Expression : ColumnExpression { Name : "ordinality" , TableAlias : var orderingTableAlias } } ]
120+ && orderingTableAlias == tvf . Alias ) )
121+ && TryGetScalarProjection ( source , out var projectedScalar ) )
122+ {
123+ jsonArray = json ;
124+
125+ // The projected ColumnExpression is wrapped in a Convert to apply the element type mapping - unless it happens to be text.
126+ switch ( projectedScalar )
127+ {
128+ case SqlUnaryExpression
129+ {
130+ OperatorType : ExpressionType . Convert ,
131+ Operand : ColumnExpression { IsNullable : var isNullable }
132+ } convert :
133+ projectedElement = convert ;
134+ isElementNullable = isNullable ;
135+ return true ;
136+ case ColumnExpression { IsNullable : var isNullable } column :
137+ projectedElement = column ;
138+ isElementNullable = isNullable ;
139+ return true ;
140+ default :
141+ throw new UnreachableException ( ) ;
142+ }
143+ }
144+
145+ jsonArray = null ;
146+ projectedElement = null ;
147+ isElementNullable = false ;
148+ return false ;
149+ }
150+
79151 /// <summary>
80152 /// If the given <paramref name="source" /> wraps a <see cref="ValuesExpression" /> without any additional clauses (e.g. filter,
81153 /// ordering...), converts that to a <see cref="NewArrayExpression" /> and returns that.
@@ -86,7 +158,7 @@ public static bool TryExtractArray(
86158 /// any release. You should only use it directly in your code with extreme caution and knowing that
87159 /// doing so can result in application failures when updating to a new Entity Framework Core release.
88160 /// </remarks>
89- public static bool TryConvertValuesToArray (
161+ public static bool TryConvertToArray (
90162 this ShapedQueryExpression source ,
91163 [ NotNullWhen ( true ) ] out SqlExpression ? array ,
92164 bool ignoreOrderings = false ,
@@ -128,13 +200,28 @@ private static bool IsPostgresArray(SqlExpression expression)
128200 {
129201 { TypeMapping : NpgsqlArrayTypeMapping } => true ,
130202 { TypeMapping : NpgsqlMultirangeTypeMapping } => false ,
203+ { TypeMapping : NpgsqlJsonTypeMapping } => false ,
131204 { Type : var type } when type . IsMultirange ( ) => false ,
132205 _ => true
133206 } ;
134207
135208 private static bool TryGetProjectedColumn (
136209 ShapedQueryExpression shapedQueryExpression ,
137210 [ NotNullWhen ( true ) ] out ColumnExpression ? projectedColumn )
211+ {
212+ if ( TryGetScalarProjection ( shapedQueryExpression , out var scalar ) && scalar is ColumnExpression column )
213+ {
214+ projectedColumn = column ;
215+ return true ;
216+ }
217+
218+ projectedColumn = null ;
219+ return false ;
220+ }
221+
222+ private static bool TryGetScalarProjection (
223+ ShapedQueryExpression shapedQueryExpression ,
224+ [ NotNullWhen ( true ) ] out SqlExpression ? projectedScalar )
138225 {
139226 var shaperExpression = shapedQueryExpression . ShaperExpression ;
140227 if ( shaperExpression is UnaryExpression { NodeType : ExpressionType . Convert } unaryExpression
@@ -146,13 +233,13 @@ private static bool TryGetProjectedColumn(
146233
147234 if ( shaperExpression is ProjectionBindingExpression projectionBindingExpression
148235 && shapedQueryExpression . QueryExpression is SelectExpression selectExpression
149- && selectExpression . GetProjection ( projectionBindingExpression ) is ColumnExpression c )
236+ && selectExpression . GetProjection ( projectionBindingExpression ) is SqlExpression scalar )
150237 {
151- projectedColumn = c ;
238+ projectedScalar = scalar ;
152239 return true ;
153240 }
154241
155- projectedColumn = null ;
242+ projectedScalar = null ;
156243 return false ;
157244 }
158245}
0 commit comments