11using YesSql ;
2+ using YesSql . Provider . MySql ;
3+ using YesSql . Provider . PostgreSql ;
4+ using YesSql . Provider . Sqlite ;
5+ using YesSql . Provider . SqlServer ;
26
37namespace OrchardCore . ContentManagement . GraphQL . Queries . Predicates ;
48
@@ -22,7 +26,6 @@ public PredicateQuery(
2226
2327 public IDictionary < string , object > Parameters { get ; } = new Dictionary < string , object > ( ) ;
2428
25-
2629 public string NewQueryParameter ( object value )
2730 {
2831 var count = Parameters . Count ;
@@ -54,7 +57,6 @@ public void CreateTableAlias(string path, string tableAlias)
5457 _tableAliases [ path ] = tableAlias ;
5558 }
5659
57-
5860 public void SearchUsedAlias ( string propertyPath )
5961 {
6062 ArgumentNullException . ThrowIfNull ( propertyPath ) ;
@@ -67,10 +69,10 @@ public void SearchUsedAlias(string propertyPath)
6769 return ;
6870 }
6971
70- var values = propertyPath . Split ( '.' , 2 ) ;
72+ var index = IndexOfUnquoted ( propertyPath , '.' ) ;
7173
7274 // if empty prefix, use default (empty alias)
73- var aliasPath = values . Length == 1 ? string . Empty : values [ 0 ] ;
75+ var aliasPath = index == - 1 ? string . Empty : propertyPath [ .. index ] ;
7476
7577 // get the actual index from the alias
7678 if ( _aliases . TryGetValue ( aliasPath , out alias ) )
@@ -80,21 +82,17 @@ public void SearchUsedAlias(string propertyPath)
8082
8183 if ( propertyProvider != null )
8284 {
83- if ( propertyProvider . TryGetValue ( values . Last ( ) , out var columnName ) )
85+ if ( propertyProvider . TryGetValue ( propertyPath [ ( index + 1 ) .. ] , out var columnName ) )
8486 {
8587 _usedAliases . Add ( alias . alias ) ;
86- return ;
8788 }
8889 }
8990 else
9091 {
9192 _usedAliases . Add ( alias . alias ) ;
92- return ;
9393 }
9494 }
95-
96- // No aliases registered for this path, return the formatted path.
97- return ;
95+ // else: No aliases registered for this path, return the formatted path.
9896 }
9997
10098 public string GetColumnName ( string propertyPath )
@@ -116,52 +114,137 @@ public string GetColumnName(string propertyPath)
116114 {
117115 if ( propertyProvider . TryGetValue ( propertyPath , out var columnName ) )
118116 {
119- return $ " { Dialect . QuoteForAliasName ( tableAlias ) } . { Dialect . QuoteForColumnName ( columnName ) } " ;
117+ return EnsureQuotes ( tableAlias , columnName ) ;
120118 }
121119 }
122120
123- return $ " { Dialect . QuoteForAliasName ( tableAlias ) } . { Dialect . QuoteForColumnName ( alias . alias ) } " ;
121+ return EnsureQuotes ( tableAlias , alias . alias ) ;
124122 }
125123
126- return Dialect . QuoteForColumnName ( alias . alias ) ;
124+ return EnsureQuotes ( alias . alias ) ;
127125 }
128126
129- var values = propertyPath . Split ( '.' , 2 ) ;
127+ var index = IndexOfUnquoted ( propertyPath , '.' ) ;
130128
131129 // if empty prefix, use default (empty alias)
132- var aliasPath = values . Length == 1 ? string . Empty : values [ 0 ] ;
130+ var aliasPath = index == - 1 ? string . Empty : propertyPath [ .. index ] ;
133131
134132 // get the actual index from the alias
135133 if ( _aliases . TryGetValue ( aliasPath , out alias ) )
136134 {
137- var tableAlias = _tableAliases [ alias . alias ] ;
135+ if ( ! _tableAliases . TryGetValue ( alias . alias , out var tableAlias ) )
136+ {
137+ throw new InvalidOperationException ( $ "Missing table alias for path { alias . alias } .") ;
138+ }
139+
138140 // get the index property provider fore the alias
139141 var propertyProvider = _propertyProviders . FirstOrDefault ( x => x . IndexName . Equals ( alias . alias , StringComparison . OrdinalIgnoreCase ) ) ;
140142
141143 if ( propertyProvider != null )
142144 {
143- if ( propertyProvider . TryGetValue ( values . Last ( ) , out var columnName ) )
145+ if ( propertyProvider . TryGetValue ( propertyPath [ ( index + 1 ) .. ] , out var columnName ) )
144146 {
145147 // Switch the given alias in the path with the mapped alias.
146148 // aliasPart.alias -> AliasPartIndex.Alias
147- return $ " { Dialect . QuoteForAliasName ( tableAlias ) } . { Dialect . QuoteForColumnName ( columnName ) } " ;
149+ return EnsureQuotes ( tableAlias , columnName ) ;
148150 }
149151 }
150152 else
151153 {
152154 // no property provider exists; hope sql is case-insensitive (will break postgres; property providers must be supplied for postgres)
153155 // Switch the given alias in the path with the mapped alias.
154156 // aliasPart.Alias -> AliasPartIndex.alias
155- return $ " { Dialect . QuoteForAliasName ( tableAlias ) } . { Dialect . QuoteForColumnName ( values . Last ( ) ) } " ;
157+ return EnsureQuotes ( tableAlias , propertyPath [ ( index + 1 ) .. ] ) ;
156158 }
157159 }
158160
159161 // No aliases registered for this path, return the formatted path.
160- return Dialect . QuoteForColumnName ( propertyPath ) ;
162+ return EnsureQuotes ( propertyPath ) ;
161163 }
162164
163165 public IEnumerable < string > GetUsedAliases ( )
164166 {
165167 return _usedAliases ;
166168 }
169+
170+ private string EnsureQuotes ( string alias )
171+ {
172+ var index = IndexOfUnquoted ( alias , '.' ) ;
173+ return index == - 1
174+ ? ( IsQuoted ( alias ) ? alias : Dialect . QuoteForColumnName ( alias ) )
175+ : EnsureQuotes ( alias [ ..index ] , alias [ ( index + 1 ) ..] ) ;
176+ }
177+
178+ private string EnsureQuotes ( string tableAlias , string columnName )
179+ {
180+ if ( ! IsQuoted ( tableAlias ) )
181+ {
182+ tableAlias = Dialect . QuoteForAliasName ( tableAlias ) ;
183+ }
184+
185+ if ( ! IsQuoted ( columnName ) )
186+ {
187+ columnName = Dialect . QuoteForColumnName ( columnName ) ;
188+ }
189+
190+ return $ "{ tableAlias } .{ columnName } ";
191+ }
192+
193+ private bool IsQuoted ( string value )
194+ {
195+ if ( value . Length >= 2 )
196+ {
197+ var ( startQuote , endQuote ) = GetQuoteChars ( Dialect ) ;
198+ return value [ 0 ] == startQuote && value [ ^ 1 ] == endQuote ;
199+ }
200+
201+ return false ;
202+ }
203+
204+ private int IndexOfUnquoted ( string value , char c )
205+ {
206+ var startIndex = 0 ;
207+
208+ while ( true )
209+ {
210+ var index = value . IndexOf ( c , startIndex ) ;
211+
212+ if ( index < 0 )
213+ {
214+ return - 1 ;
215+ }
216+
217+ var ( startQuote , endQuote ) = GetQuoteChars ( Dialect ) ;
218+ var startQuoteIndex = value . IndexOf ( startQuote , startIndex ) ;
219+
220+ if ( startQuoteIndex >= 0 && startQuoteIndex < index )
221+ {
222+ var endQuoteIndex = value . IndexOf ( endQuote , startQuoteIndex + 1 ) ;
223+
224+ if ( endQuoteIndex >= index )
225+ {
226+ startIndex = endQuoteIndex + 1 ;
227+ continue ;
228+ }
229+ }
230+
231+ return index ;
232+ }
233+ }
234+
235+ private static ( char startQuote , char endQuote ) GetQuoteChars ( ISqlDialect dialect )
236+ => dialect switch
237+ {
238+ MySqlDialect => ( '`' , '`' ) ,
239+ PostgreSqlDialect => ( '"' , '"' ) ,
240+ SqliteDialect or
241+ SqlServerDialect => ( '[' , ']' ) ,
242+ _ => ExtractQuoteChars ( dialect )
243+ } ;
244+
245+ private static ( char startQuote , char endQuote ) ExtractQuoteChars ( ISqlDialect dialect )
246+ {
247+ var quoted = dialect . QuoteForColumnName ( "alias" ) ;
248+ return ( quoted [ 0 ] , quoted [ ^ 1 ] ) ;
249+ }
167250}
0 commit comments