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 ;
@@ -41,6 +44,7 @@ public void CreateAlias(string path, string alias)
4144
4245 _aliases [ path ] = alias ;
4346 }
47+
4448 public void CreateTableAlias ( string path , string tableAlias )
4549 {
4650 ArgumentNullException . ThrowIfNull ( path ) ;
@@ -50,7 +54,6 @@ public void CreateTableAlias(string path, string tableAlias)
5054 _tableAliases [ path ] = tableAlias ;
5155 }
5256
53-
5457 public void SearchUsedAlias ( string propertyPath )
5558 {
5659 ArgumentNullException . ThrowIfNull ( propertyPath ) ;
@@ -63,10 +66,10 @@ public void SearchUsedAlias(string propertyPath)
6366 return ;
6467 }
6568
66- var values = propertyPath . Split ( '.' , 2 ) ;
69+ var index = IndexOfUnquoted ( propertyPath , '.' ) ;
6770
6871 // if empty prefix, use default (empty alias)
69- var aliasPath = values . Length == 1 ? string . Empty : values [ 0 ] ;
72+ var aliasPath = index == - 1 ? string . Empty : propertyPath [ .. index ] ;
7073
7174 // get the actual index from the alias
7275 if ( _aliases . TryGetValue ( aliasPath , out alias ) )
@@ -76,21 +79,17 @@ public void SearchUsedAlias(string propertyPath)
7679
7780 if ( propertyProvider != null )
7881 {
79- if ( propertyProvider . TryGetValue ( values . Last ( ) , out var columnName ) )
82+ if ( propertyProvider . TryGetValue ( propertyPath [ ( index + 1 ) .. ] , out var columnName ) )
8083 {
8184 _usedAliases . Add ( alias ) ;
82- return ;
8385 }
8486 }
8587 else
8688 {
8789 _usedAliases . Add ( alias ) ;
88- return ;
8990 }
9091 }
91-
92- // No aliases registered for this path, return the formatted path.
93- return ;
92+ // else: No aliases registered for this path, return the formatted path.
9493 }
9594
9695 public string GetColumnName ( string propertyPath )
@@ -101,45 +100,130 @@ public string GetColumnName(string propertyPath)
101100 // aliasPart.Alias -> AliasFieldIndex.Alias
102101 if ( _aliases . TryGetValue ( propertyPath , out var alias ) )
103102 {
104- return Dialect . QuoteForColumnName ( alias ) ;
103+ return EnsureQuotes ( alias ) ;
105104 }
106105
107- var values = propertyPath . Split ( '.' , 2 ) ;
106+ var index = IndexOfUnquoted ( propertyPath , '.' ) ;
108107
109108 // if empty prefix, use default (empty alias)
110- var aliasPath = values . Length == 1 ? string . Empty : values [ 0 ] ;
109+ var aliasPath = index == - 1 ? string . Empty : propertyPath [ .. index ] ;
111110
112111 // get the actual index from the alias
113112 if ( _aliases . TryGetValue ( aliasPath , out alias ) )
114113 {
115- var tableAlias = _tableAliases [ alias ] ;
114+ if ( ! _tableAliases . TryGetValue ( alias , out var tableAlias ) )
115+ {
116+ throw new InvalidOperationException ( $ "Missing table alias for path { alias } .") ;
117+ }
118+
116119 // get the index property provider fore the alias
117120 var propertyProvider = _propertyProviders . FirstOrDefault ( x => x . IndexName . Equals ( alias , StringComparison . OrdinalIgnoreCase ) ) ;
118121
119122 if ( propertyProvider != null )
120123 {
121- if ( propertyProvider . TryGetValue ( values . Last ( ) , out var columnName ) )
124+ if ( propertyProvider . TryGetValue ( propertyPath [ ( index + 1 ) .. ] , out var columnName ) )
122125 {
123126 // Switch the given alias in the path with the mapped alias.
124127 // aliasPart.alias -> AliasPartIndex.Alias
125- return $ " { Dialect . QuoteForAliasName ( tableAlias ) } . { Dialect . QuoteForColumnName ( columnName ) } " ;
128+ return EnsureQuotes ( tableAlias , columnName ) ;
126129 }
127130 }
128131 else
129132 {
130133 // no property provider exists; hope sql is case-insensitive (will break postgres; property providers must be supplied for postgres)
131134 // Switch the given alias in the path with the mapped alias.
132135 // aliasPart.Alias -> AliasPartIndex.alias
133- return $ " { Dialect . QuoteForAliasName ( tableAlias ) } . { Dialect . QuoteForColumnName ( values . Last ( ) ) } " ;
136+ return EnsureQuotes ( tableAlias , propertyPath [ ( index + 1 ) .. ] ) ;
134137 }
135138 }
136139
137140 // No aliases registered for this path, return the formatted path.
138- return Dialect . QuoteForColumnName ( propertyPath ) ;
141+ return EnsureQuotes ( propertyPath ) ;
139142 }
140143
141144 public IEnumerable < string > GetUsedAliases ( )
142145 {
143146 return _usedAliases ;
144147 }
148+
149+ private string EnsureQuotes ( string alias )
150+ {
151+ var index = IndexOfUnquoted ( alias , '.' ) ;
152+ return index == - 1
153+ ? ( IsQuoted ( alias ) ? alias : Dialect . QuoteForColumnName ( alias ) )
154+ : EnsureQuotes ( alias [ ..index ] , alias [ ( index + 1 ) ..] ) ;
155+ }
156+
157+ private string EnsureQuotes ( string tableAlias , string columnName )
158+ {
159+ if ( ! IsQuoted ( tableAlias ) )
160+ {
161+ tableAlias = Dialect . QuoteForAliasName ( tableAlias ) ;
162+ }
163+
164+ if ( ! IsQuoted ( columnName ) )
165+ {
166+ columnName = Dialect . QuoteForColumnName ( columnName ) ;
167+ }
168+
169+ return $ "{ tableAlias } .{ columnName } ";
170+ }
171+
172+ private bool IsQuoted ( string value )
173+ {
174+ if ( value . Length >= 2 )
175+ {
176+ var ( startQuote , endQuote ) = GetQuoteChars ( Dialect ) ;
177+ return value [ 0 ] == startQuote && value [ ^ 1 ] == endQuote ;
178+ }
179+
180+ return false ;
181+ }
182+
183+ private int IndexOfUnquoted ( string value , char c )
184+ {
185+ var startIndex = 0 ;
186+
187+ while ( true )
188+ {
189+ var index = value . IndexOf ( c , startIndex ) ;
190+
191+ if ( index < 0 )
192+ {
193+ return - 1 ;
194+ }
195+
196+ var ( startQuote , endQuote ) = GetQuoteChars ( Dialect ) ;
197+ var startQuoteIndex = value . IndexOf ( startQuote , startIndex ) ;
198+
199+ if ( startQuoteIndex >= 0 && startQuoteIndex < index )
200+ {
201+ var endQuoteIndex = value . IndexOf ( endQuote , startQuoteIndex + 1 ) ;
202+
203+ if ( endQuoteIndex >= index )
204+ {
205+ startIndex = endQuoteIndex + 1 ;
206+ continue ;
207+ }
208+ }
209+
210+ return index ;
211+ }
212+ }
213+
214+ private static ( char startQuote , char endQuote ) GetQuoteChars ( ISqlDialect dialect )
215+ => dialect switch
216+ {
217+ MySqlDialect => ( '`' , '`' ) ,
218+ PostgreSqlDialect => ( '"' , '"' ) ,
219+ SqliteDialect or
220+ SqlServerDialect => ( '[' , ']' ) ,
221+ _ => ExtractQuoteChars ( dialect )
222+ } ;
223+
224+ private static ( char startQuote , char endQuote ) ExtractQuoteChars ( ISqlDialect dialect )
225+ {
226+ var quoted = dialect . QuoteForColumnName ( "alias" ) ;
227+ return ( quoted [ 0 ] , quoted [ ^ 1 ] ) ;
228+ }
145229}
0 commit comments