Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions src/main/java/net/sf/jsqlparser/schema/Column.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import net.sf.jsqlparser.expression.ArrayConstructor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
import net.sf.jsqlparser.statement.ReturningReferenceType;

/**
* A column. It can have the table name it belongs to.
Expand All @@ -30,6 +30,8 @@ public class Column extends ASTNodeAccessImpl implements Expression, MultiPartNa
private ArrayConstructor arrayConstructor;
private String tableDelimiter = ".";
private int oldOracleJoinSyntax = SupportsOldOracleJoinSyntax.NO_ORACLE_JOIN;
private ReturningReferenceType returningReferenceType = null;
private String returningQualifier = null;

// holds the physical table when resolved against an actual schema information
private Table resolvedTable = null;
Expand Down Expand Up @@ -215,7 +217,9 @@ public String getUnquotedName() {
public String getFullyQualifiedName(boolean aliases) {
StringBuilder fqn = new StringBuilder();

if (table != null) {
if (returningQualifier != null) {
fqn.append(returningQualifier);
} else if (table != null) {
if (table.getAlias() != null && aliases) {
fqn.append(table.getAlias().getName());
} else {
Expand Down Expand Up @@ -284,6 +288,31 @@ public Column withOldOracleJoinSyntax(int oldOracleJoinSyntax) {
return this;
}

public ReturningReferenceType getReturningReferenceType() {
return returningReferenceType;
}

public Column setReturningReferenceType(ReturningReferenceType returningReferenceType) {
this.returningReferenceType = returningReferenceType;
return this;
}

public String getReturningQualifier() {
return returningQualifier;
}

public Column setReturningQualifier(String returningQualifier) {
this.returningQualifier = returningQualifier;
return this;
}

public Column withReturningReference(ReturningReferenceType returningReferenceType,
String returningQualifier) {
this.returningReferenceType = returningReferenceType;
this.returningQualifier = returningQualifier;
return this;
}

public String getCommentText() {
return commentText;
}
Expand Down
163 changes: 159 additions & 4 deletions src/main/java/net/sf/jsqlparser/statement/ReturningClause.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@
*/
package net.sf.jsqlparser.statement;

import net.sf.jsqlparser.statement.select.SelectItem;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.MultiPartName;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.AllTableColumns;
import net.sf.jsqlparser.statement.select.SelectItem;

/**
* RETURNING clause according to <a href=
Expand All @@ -25,26 +33,39 @@ public class ReturningClause extends ArrayList<SelectItem<?>> {
* List of output targets like Table or UserVariable
*/
private final List<Object> dataItems;
private final List<ReturningOutputAlias> outputAliases;
private Keyword keyword;

public ReturningClause(Keyword keyword, List<SelectItem<?>> selectItems,
List<Object> dataItems) {
this(keyword, selectItems, null, dataItems);
}

public ReturningClause(Keyword keyword, List<SelectItem<?>> selectItems,
List<ReturningOutputAlias> outputAliases, List<Object> dataItems) {
this.keyword = keyword;
this.addAll(selectItems);
this.outputAliases = outputAliases;
this.dataItems = dataItems;
normalizeReturningReferences();
}

public ReturningClause(String keyword, List<SelectItem<?>> selectItems,
List<Object> dataItems) {
this(Keyword.from(keyword), selectItems, dataItems);
}

public ReturningClause(String keyword, List<SelectItem<?>> selectItems,
List<ReturningOutputAlias> outputAliases, List<Object> dataItems) {
this(Keyword.from(keyword), selectItems, outputAliases, dataItems);
}

public ReturningClause(Keyword keyword, List<SelectItem<?>> selectItems) {
this(keyword, selectItems, null);
this(keyword, selectItems, null, null);
}

public ReturningClause(String keyword, List<SelectItem<?>> selectItems) {
this(Keyword.valueOf(keyword), selectItems, null);
this(Keyword.from(keyword), selectItems, null, null);
}

public Keyword getKeyword() {
Expand All @@ -60,8 +81,22 @@ public List<?> getDataItems() {
return dataItems;
}

public List<ReturningOutputAlias> getOutputAliases() {
return outputAliases;
}

public StringBuilder appendTo(StringBuilder builder) {
builder.append(" ").append(keyword).append(" ");
if (outputAliases != null && !outputAliases.isEmpty()) {
builder.append("WITH (");
for (int i = 0; i < outputAliases.size(); i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(outputAliases.get(i));
}
builder.append(") ");
}
for (int i = 0; i < size(); i++) {
if (i > 0) {
builder.append(", ");
Expand All @@ -86,6 +121,126 @@ public String toString() {
return appendTo(new StringBuilder()).toString();
}

private void normalizeReturningReferences() {
Map<QualifierKey, ReturningReferenceType> qualifierMap = buildQualifierMap();
if (qualifierMap.isEmpty()) {
return;
}

ReturningReferenceNormalizer normalizer = new ReturningReferenceNormalizer(qualifierMap);
forEach(selectItem -> {
if (selectItem != null && selectItem.getExpression() != null) {
selectItem.getExpression().accept(normalizer, null);
}
});
}

private Map<QualifierKey, ReturningReferenceType> buildQualifierMap() {
LinkedHashMap<QualifierKey, ReturningReferenceType> qualifierMap = new LinkedHashMap<>();

if (outputAliases == null || outputAliases.isEmpty()) {
qualifierMap.put(QualifierKey.from("OLD"), ReturningReferenceType.OLD);
qualifierMap.put(QualifierKey.from("NEW"), ReturningReferenceType.NEW);
return qualifierMap;
}

for (ReturningOutputAlias outputAlias : outputAliases) {
if (outputAlias == null || outputAlias.getAlias() == null
|| outputAlias.getReferenceType() == null) {
continue;
}
qualifierMap.put(QualifierKey.from(outputAlias.getAlias()),
outputAlias.getReferenceType());
}
return qualifierMap;
}

private static class ReturningReferenceNormalizer extends ExpressionVisitorAdapter<Void> {
private final Map<QualifierKey, ReturningReferenceType> qualifierMap;

ReturningReferenceNormalizer(Map<QualifierKey, ReturningReferenceType> qualifierMap) {
this.qualifierMap = qualifierMap;
}

@Override
public <S> Void visit(Column column, S context) {
Table table = column.getTable();
String qualifier = extractSimpleQualifier(table);
if (qualifier == null) {
return null;
}
ReturningReferenceType referenceType = qualifierMap.get(QualifierKey.from(qualifier));
if (referenceType != null) {
column.withReturningReference(referenceType, qualifier);
column.setTable(null);
}
return null;
}

@Override
public <S> Void visit(AllTableColumns allTableColumns, S context) {
Table table = allTableColumns.getTable();
String qualifier = extractSimpleQualifier(table);
if (qualifier == null) {
return null;
}
ReturningReferenceType referenceType = qualifierMap.get(QualifierKey.from(qualifier));
if (referenceType != null) {
allTableColumns.withReturningReference(referenceType, qualifier);
allTableColumns.setTable(null);
}
return null;
}

private String extractSimpleQualifier(Table table) {
if (table == null || table.getSchemaName() != null || table.getDatabaseName() != null) {
return null;
}
String qualifier = table.getName();
if (qualifier == null || qualifier.contains("@")) {
return null;
}
return qualifier;
}
}

private static class QualifierKey {
private final boolean quoted;
private final String normalizedIdentifier;

private QualifierKey(boolean quoted, String normalizedIdentifier) {
this.quoted = quoted;
this.normalizedIdentifier = normalizedIdentifier;
}

static QualifierKey from(String identifier) {
boolean quoted = MultiPartName.isQuoted(identifier);
String unquoted = MultiPartName.unquote(identifier);
if (!quoted && unquoted != null) {
unquoted = unquoted.toUpperCase(Locale.ROOT);
}
return new QualifierKey(quoted, unquoted);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof QualifierKey)) {
return false;
}
QualifierKey that = (QualifierKey) o;
return quoted == that.quoted
&& Objects.equals(normalizedIdentifier, that.normalizedIdentifier);
}

@Override
public int hashCode() {
return Objects.hash(quoted, normalizedIdentifier);
}
}

public enum Keyword {
RETURN, RETURNING;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement;

import java.util.Objects;

public class ReturningOutputAlias {
private ReturningReferenceType referenceType;
private String alias;

public ReturningOutputAlias(ReturningReferenceType referenceType, String alias) {
this.referenceType = referenceType;
this.alias = alias;
}

public ReturningReferenceType getReferenceType() {
return referenceType;
}

public ReturningOutputAlias setReferenceType(ReturningReferenceType referenceType) {
this.referenceType = referenceType;
return this;
}

public String getAlias() {
return alias;
}

public ReturningOutputAlias setAlias(String alias) {
this.alias = alias;
return this;
}

public StringBuilder appendTo(StringBuilder builder) {
return builder.append(referenceType).append(" AS ").append(alias);
}

@Override
public String toString() {
return appendTo(new StringBuilder()).toString();
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ReturningOutputAlias)) {
return false;
}
ReturningOutputAlias that = (ReturningOutputAlias) o;
return referenceType == that.referenceType && Objects.equals(alias, that.alias);
}

@Override
public int hashCode() {
return Objects.hash(referenceType, alias);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement;

import net.sf.jsqlparser.schema.MultiPartName;

public enum ReturningReferenceType {
OLD, NEW;

public static ReturningReferenceType from(String name) {
String unquoted = MultiPartName.unquote(name);
if (unquoted == null) {
return null;
}
if ("OLD".equalsIgnoreCase(unquoted)) {
return OLD;
}
if ("NEW".equalsIgnoreCase(unquoted)) {
return NEW;
}
return null;
}
}
Loading