99 */
1010package net .sf .jsqlparser .statement ;
1111
12- import net .sf .jsqlparser .statement .select .SelectItem ;
13-
1412import java .util .ArrayList ;
13+ import java .util .LinkedHashMap ;
1514import java .util .List ;
15+ import java .util .Locale ;
16+ import java .util .Map ;
17+ import java .util .Objects ;
18+ import net .sf .jsqlparser .expression .ExpressionVisitorAdapter ;
19+ import net .sf .jsqlparser .schema .Column ;
20+ import net .sf .jsqlparser .schema .MultiPartName ;
21+ import net .sf .jsqlparser .schema .Table ;
22+ import net .sf .jsqlparser .statement .select .AllTableColumns ;
23+ import net .sf .jsqlparser .statement .select .SelectItem ;
1624
1725/**
1826 * RETURNING clause according to <a href=
@@ -25,26 +33,39 @@ public class ReturningClause extends ArrayList<SelectItem<?>> {
2533 * List of output targets like Table or UserVariable
2634 */
2735 private final List <Object > dataItems ;
36+ private final List <ReturningOutputAlias > outputAliases ;
2837 private Keyword keyword ;
2938
3039 public ReturningClause (Keyword keyword , List <SelectItem <?>> selectItems ,
3140 List <Object > dataItems ) {
41+ this (keyword , selectItems , null , dataItems );
42+ }
43+
44+ public ReturningClause (Keyword keyword , List <SelectItem <?>> selectItems ,
45+ List <ReturningOutputAlias > outputAliases , List <Object > dataItems ) {
3246 this .keyword = keyword ;
3347 this .addAll (selectItems );
48+ this .outputAliases = outputAliases ;
3449 this .dataItems = dataItems ;
50+ normalizeReturningReferences ();
3551 }
3652
3753 public ReturningClause (String keyword , List <SelectItem <?>> selectItems ,
3854 List <Object > dataItems ) {
3955 this (Keyword .from (keyword ), selectItems , dataItems );
4056 }
4157
58+ public ReturningClause (String keyword , List <SelectItem <?>> selectItems ,
59+ List <ReturningOutputAlias > outputAliases , List <Object > dataItems ) {
60+ this (Keyword .from (keyword ), selectItems , outputAliases , dataItems );
61+ }
62+
4263 public ReturningClause (Keyword keyword , List <SelectItem <?>> selectItems ) {
43- this (keyword , selectItems , null );
64+ this (keyword , selectItems , null , null );
4465 }
4566
4667 public ReturningClause (String keyword , List <SelectItem <?>> selectItems ) {
47- this (Keyword .valueOf (keyword ), selectItems , null );
68+ this (Keyword .from (keyword ), selectItems , null , null );
4869 }
4970
5071 public Keyword getKeyword () {
@@ -60,8 +81,22 @@ public List<?> getDataItems() {
6081 return dataItems ;
6182 }
6283
84+ public List <ReturningOutputAlias > getOutputAliases () {
85+ return outputAliases ;
86+ }
87+
6388 public StringBuilder appendTo (StringBuilder builder ) {
6489 builder .append (" " ).append (keyword ).append (" " );
90+ if (outputAliases != null && !outputAliases .isEmpty ()) {
91+ builder .append ("WITH (" );
92+ for (int i = 0 ; i < outputAliases .size (); i ++) {
93+ if (i > 0 ) {
94+ builder .append (", " );
95+ }
96+ builder .append (outputAliases .get (i ));
97+ }
98+ builder .append (") " );
99+ }
65100 for (int i = 0 ; i < size (); i ++) {
66101 if (i > 0 ) {
67102 builder .append (", " );
@@ -86,6 +121,126 @@ public String toString() {
86121 return appendTo (new StringBuilder ()).toString ();
87122 }
88123
124+ private void normalizeReturningReferences () {
125+ Map <QualifierKey , ReturningReferenceType > qualifierMap = buildQualifierMap ();
126+ if (qualifierMap .isEmpty ()) {
127+ return ;
128+ }
129+
130+ ReturningReferenceNormalizer normalizer = new ReturningReferenceNormalizer (qualifierMap );
131+ forEach (selectItem -> {
132+ if (selectItem != null && selectItem .getExpression () != null ) {
133+ selectItem .getExpression ().accept (normalizer , null );
134+ }
135+ });
136+ }
137+
138+ private Map <QualifierKey , ReturningReferenceType > buildQualifierMap () {
139+ LinkedHashMap <QualifierKey , ReturningReferenceType > qualifierMap = new LinkedHashMap <>();
140+
141+ if (outputAliases == null || outputAliases .isEmpty ()) {
142+ qualifierMap .put (QualifierKey .from ("OLD" ), ReturningReferenceType .OLD );
143+ qualifierMap .put (QualifierKey .from ("NEW" ), ReturningReferenceType .NEW );
144+ return qualifierMap ;
145+ }
146+
147+ for (ReturningOutputAlias outputAlias : outputAliases ) {
148+ if (outputAlias == null || outputAlias .getAlias () == null
149+ || outputAlias .getReferenceType () == null ) {
150+ continue ;
151+ }
152+ qualifierMap .put (QualifierKey .from (outputAlias .getAlias ()),
153+ outputAlias .getReferenceType ());
154+ }
155+ return qualifierMap ;
156+ }
157+
158+ private static class ReturningReferenceNormalizer extends ExpressionVisitorAdapter <Void > {
159+ private final Map <QualifierKey , ReturningReferenceType > qualifierMap ;
160+
161+ ReturningReferenceNormalizer (Map <QualifierKey , ReturningReferenceType > qualifierMap ) {
162+ this .qualifierMap = qualifierMap ;
163+ }
164+
165+ @ Override
166+ public <S > Void visit (Column column , S context ) {
167+ Table table = column .getTable ();
168+ String qualifier = extractSimpleQualifier (table );
169+ if (qualifier == null ) {
170+ return null ;
171+ }
172+ ReturningReferenceType referenceType = qualifierMap .get (QualifierKey .from (qualifier ));
173+ if (referenceType != null ) {
174+ column .withReturningReference (referenceType , qualifier );
175+ column .setTable (null );
176+ }
177+ return null ;
178+ }
179+
180+ @ Override
181+ public <S > Void visit (AllTableColumns allTableColumns , S context ) {
182+ Table table = allTableColumns .getTable ();
183+ String qualifier = extractSimpleQualifier (table );
184+ if (qualifier == null ) {
185+ return null ;
186+ }
187+ ReturningReferenceType referenceType = qualifierMap .get (QualifierKey .from (qualifier ));
188+ if (referenceType != null ) {
189+ allTableColumns .withReturningReference (referenceType , qualifier );
190+ allTableColumns .setTable (null );
191+ }
192+ return null ;
193+ }
194+
195+ private String extractSimpleQualifier (Table table ) {
196+ if (table == null || table .getSchemaName () != null || table .getDatabaseName () != null ) {
197+ return null ;
198+ }
199+ String qualifier = table .getName ();
200+ if (qualifier == null || qualifier .contains ("@" )) {
201+ return null ;
202+ }
203+ return qualifier ;
204+ }
205+ }
206+
207+ private static class QualifierKey {
208+ private final boolean quoted ;
209+ private final String normalizedIdentifier ;
210+
211+ private QualifierKey (boolean quoted , String normalizedIdentifier ) {
212+ this .quoted = quoted ;
213+ this .normalizedIdentifier = normalizedIdentifier ;
214+ }
215+
216+ static QualifierKey from (String identifier ) {
217+ boolean quoted = MultiPartName .isQuoted (identifier );
218+ String unquoted = MultiPartName .unquote (identifier );
219+ if (!quoted && unquoted != null ) {
220+ unquoted = unquoted .toUpperCase (Locale .ROOT );
221+ }
222+ return new QualifierKey (quoted , unquoted );
223+ }
224+
225+ @ Override
226+ public boolean equals (Object o ) {
227+ if (this == o ) {
228+ return true ;
229+ }
230+ if (!(o instanceof QualifierKey )) {
231+ return false ;
232+ }
233+ QualifierKey that = (QualifierKey ) o ;
234+ return quoted == that .quoted
235+ && Objects .equals (normalizedIdentifier , that .normalizedIdentifier );
236+ }
237+
238+ @ Override
239+ public int hashCode () {
240+ return Objects .hash (quoted , normalizedIdentifier );
241+ }
242+ }
243+
89244 public enum Keyword {
90245 RETURN , RETURNING ;
91246
0 commit comments