[jOOQ/jOOQ#11850] Get SQLDialect.TERADATA up to date

This commit is contained in:
Lukas Eder 2021-05-06 13:38:04 +02:00
parent 9ccbf454f5
commit fbda63cae6
19 changed files with 450 additions and 78 deletions

View File

@ -43,6 +43,7 @@ import org.jetbrains.annotations.*;
import static org.jooq.SQLDialect.H2;
// ...
// ...
// ...
/**
* This type is used for the {@link Select}'s DSL API when selecting generic

View File

@ -43,6 +43,7 @@ import org.jetbrains.annotations.*;
import static org.jooq.SQLDialect.H2;
// ...
// ...
// ...
/**
* This type is used for the {@link Select}'s DSL API when selecting generic

View File

@ -0,0 +1,43 @@
package org.jooq.conf;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for RenderImplicitWindowRange.
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <pre>
* &lt;simpleType name="RenderImplicitWindowRange"&gt;
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string"&gt;
* &lt;enumeration value="OFF"/&gt;
* &lt;enumeration value="ROWS_UNBOUNDED_PRECEDING"/&gt;
* &lt;enumeration value="ROWS_ALL"/&gt;
* &lt;enumeration value="RANGE_UNBOUNDED_PRECEDING"/&gt;
* &lt;enumeration value="RANGE_ALL"/&gt;
* &lt;/restriction&gt;
* &lt;/simpleType&gt;
* </pre>
*
*/
@XmlType(name = "RenderImplicitWindowRange")
@XmlEnum
public enum RenderImplicitWindowRange {
OFF,
ROWS_UNBOUNDED_PRECEDING,
ROWS_ALL,
RANGE_UNBOUNDED_PRECEDING,
RANGE_ALL;
public String value() {
return name();
}
public static RenderImplicitWindowRange fromValue(String v) {
return valueOf(v);
}
}

View File

@ -79,6 +79,9 @@ public class Settings
@XmlElement(defaultValue = "DEFAULT")
@XmlSchemaType(name = "string")
protected RenderOptionalKeyword renderOptionalOuterKeyword = RenderOptionalKeyword.DEFAULT;
@XmlElement(defaultValue = "OFF")
@XmlSchemaType(name = "string")
protected RenderImplicitWindowRange renderImplicitWindowRange = RenderImplicitWindowRange.OFF;
@XmlElement(defaultValue = "false")
protected Boolean renderScalarSubqueriesForStoredFunctions = false;
@XmlElement(defaultValue = "DEFAULT")
@ -288,6 +291,9 @@ public class Settings
@XmlElement(defaultValue = "OFF")
@XmlSchemaType(name = "string")
protected ParseWithMetaLookups parseWithMetaLookups = ParseWithMetaLookups.OFF;
@XmlElement(defaultValue = "WHEN_NEEDED")
@XmlSchemaType(name = "string")
protected Transformation parseAppendMissingTableReferences = Transformation.WHEN_NEEDED;
@XmlElement(defaultValue = "false")
protected Boolean parseSetCommands = false;
@XmlElement(defaultValue = "IGNORE")
@ -680,6 +686,22 @@ public class Settings
this.renderOptionalOuterKeyword = value;
}
/**
* Whether to render an explicit window <code>RANGE</code> clause when an implicit clause is applied.
*
*/
public RenderImplicitWindowRange getRenderImplicitWindowRange() {
return renderImplicitWindowRange;
}
/**
* Whether to render an explicit window <code>RANGE</code> clause when an implicit clause is applied.
*
*/
public void setRenderImplicitWindowRange(RenderImplicitWindowRange value) {
this.renderImplicitWindowRange = value;
}
/**
* Whether stored function calls should be wrapped in scalar subqueries.
* <p>
@ -2628,6 +2650,36 @@ public class Settings
this.parseWithMetaLookups = value;
}
/**
* Transform the parsed SQL to append missing table references to the query's <code>FROM</code> or <code>USING</code> clause, if applicable.
* <p>
* Teradata (and possibly others) allow for referencing tables that are not listed in the <code>FROM</code>
* clause, such as <code>SELECT t.* FROM t WHERE t.i = u.i</code>. This transformation is executed in the
* parser, to produce <code>SELECT t.* FROM t, u WHERE t.i = u.i</code>, instead. By default, it is active
* when the input dialect supports this syntax.
* <p>
* This feature is available in the commercial distribution only.
*
*/
public Transformation getParseAppendMissingTableReferences() {
return parseAppendMissingTableReferences;
}
/**
* Transform the parsed SQL to append missing table references to the query's <code>FROM</code> or <code>USING</code> clause, if applicable.
* <p>
* Teradata (and possibly others) allow for referencing tables that are not listed in the <code>FROM</code>
* clause, such as <code>SELECT t.* FROM t WHERE t.i = u.i</code>. This transformation is executed in the
* parser, to produce <code>SELECT t.* FROM t, u WHERE t.i = u.i</code>, instead. By default, it is active
* when the input dialect supports this syntax.
* <p>
* This feature is available in the commercial distribution only.
*
*/
public void setParseAppendMissingTableReferences(Transformation value) {
this.parseAppendMissingTableReferences = value;
}
/**
* [#9780] Whether commands of the type <code>SET key = value</code> should be parsed rather than ignored.
*
@ -2984,6 +3036,15 @@ public class Settings
return this;
}
/**
* Whether to render an explicit window <code>RANGE</code> clause when an implicit clause is applied.
*
*/
public Settings withRenderImplicitWindowRange(RenderImplicitWindowRange value) {
setRenderImplicitWindowRange(value);
return this;
}
public Settings withRenderScalarSubqueriesForStoredFunctions(Boolean value) {
setRenderScalarSubqueriesForStoredFunctions(value);
return this;
@ -3667,6 +3728,22 @@ public class Settings
return this;
}
/**
* Transform the parsed SQL to append missing table references to the query's <code>FROM</code> or <code>USING</code> clause, if applicable.
* <p>
* Teradata (and possibly others) allow for referencing tables that are not listed in the <code>FROM</code>
* clause, such as <code>SELECT t.* FROM t WHERE t.i = u.i</code>. This transformation is executed in the
* parser, to produce <code>SELECT t.* FROM t, u WHERE t.i = u.i</code>, instead. By default, it is active
* when the input dialect supports this syntax.
* <p>
* This feature is available in the commercial distribution only.
*
*/
public Settings withParseAppendMissingTableReferences(Transformation value) {
setParseAppendMissingTableReferences(value);
return this;
}
public Settings withParseSetCommands(Boolean value) {
setParseSetCommands(value);
return this;
@ -3805,6 +3882,7 @@ public class Settings
builder.append("renderOptionalAsKeywordForFieldAliases", renderOptionalAsKeywordForFieldAliases);
builder.append("renderOptionalInnerKeyword", renderOptionalInnerKeyword);
builder.append("renderOptionalOuterKeyword", renderOptionalOuterKeyword);
builder.append("renderImplicitWindowRange", renderImplicitWindowRange);
builder.append("renderScalarSubqueriesForStoredFunctions", renderScalarSubqueriesForStoredFunctions);
builder.append("renderImplicitJoinType", renderImplicitJoinType);
builder.append("renderDefaultNullability", renderDefaultNullability);
@ -3893,6 +3971,7 @@ public class Settings
builder.append("parseNamedParamPrefix", parseNamedParamPrefix);
builder.append("parseNameCase", parseNameCase);
builder.append("parseWithMetaLookups", parseWithMetaLookups);
builder.append("parseAppendMissingTableReferences", parseAppendMissingTableReferences);
builder.append("parseSetCommands", parseSetCommands);
builder.append("parseUnsupportedSyntax", parseUnsupportedSyntax);
builder.append("parseUnknownFunctions", parseUnknownFunctions);
@ -4078,6 +4157,15 @@ public class Settings
return false;
}
}
if (renderImplicitWindowRange == null) {
if (other.renderImplicitWindowRange!= null) {
return false;
}
} else {
if (!renderImplicitWindowRange.equals(other.renderImplicitWindowRange)) {
return false;
}
}
if (renderScalarSubqueriesForStoredFunctions == null) {
if (other.renderScalarSubqueriesForStoredFunctions!= null) {
return false;
@ -4870,6 +4958,15 @@ public class Settings
return false;
}
}
if (parseAppendMissingTableReferences == null) {
if (other.parseAppendMissingTableReferences!= null) {
return false;
}
} else {
if (!parseAppendMissingTableReferences.equals(other.parseAppendMissingTableReferences)) {
return false;
}
}
if (parseSetCommands == null) {
if (other.parseSetCommands!= null) {
return false;
@ -4993,6 +5090,7 @@ public class Settings
result = ((prime*result)+((renderOptionalAsKeywordForFieldAliases == null)? 0 :renderOptionalAsKeywordForFieldAliases.hashCode()));
result = ((prime*result)+((renderOptionalInnerKeyword == null)? 0 :renderOptionalInnerKeyword.hashCode()));
result = ((prime*result)+((renderOptionalOuterKeyword == null)? 0 :renderOptionalOuterKeyword.hashCode()));
result = ((prime*result)+((renderImplicitWindowRange == null)? 0 :renderImplicitWindowRange.hashCode()));
result = ((prime*result)+((renderScalarSubqueriesForStoredFunctions == null)? 0 :renderScalarSubqueriesForStoredFunctions.hashCode()));
result = ((prime*result)+((renderImplicitJoinType == null)? 0 :renderImplicitJoinType.hashCode()));
result = ((prime*result)+((renderDefaultNullability == null)? 0 :renderDefaultNullability.hashCode()));
@ -5081,6 +5179,7 @@ public class Settings
result = ((prime*result)+((parseNamedParamPrefix == null)? 0 :parseNamedParamPrefix.hashCode()));
result = ((prime*result)+((parseNameCase == null)? 0 :parseNameCase.hashCode()));
result = ((prime*result)+((parseWithMetaLookups == null)? 0 :parseWithMetaLookups.hashCode()));
result = ((prime*result)+((parseAppendMissingTableReferences == null)? 0 :parseAppendMissingTableReferences.hashCode()));
result = ((prime*result)+((parseSetCommands == null)? 0 :parseSetCommands.hashCode()));
result = ((prime*result)+((parseUnsupportedSyntax == null)? 0 :parseUnsupportedSyntax.hashCode()));
result = ((prime*result)+((parseUnknownFunctions == null)? 0 :parseUnknownFunctions.hashCode()));

View File

@ -143,6 +143,10 @@ final class DeleteQueryImpl<R extends Record> extends AbstractDMLQuery<R> implem
return condition.hasWhere();
}
final TableList getUsing() {
return using;
}
@Override
public final void addUsing(Collection<? extends TableLike<?>> f) {
for (TableLike<?> provider : f)

View File

@ -41,6 +41,7 @@ import org.jooq.Clause;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.Name;
import org.jooq.Query;
import org.jooq.Record;
import org.jooq.Table;
import org.jooq.TableField;
@ -54,8 +55,26 @@ import org.jooq.TableField;
@SuppressWarnings("unchecked")
final class FieldProxy<T> extends AbstractField<T> implements TableField<Record, T> {
private AbstractField<T> delegate;
private int position;
/**
* The resolved field after a successful meta lookup.
*/
private AbstractField<T> delegate;
/**
* The position in the parsed SQL string where this field proxy was
* encountered.
*/
private int position;
/**
* The scope owner that produced this field proxy.
*/
Query scopeOwner;
/**
* Whether this FieldProxy could be resolved at some scope level.
*/
boolean resolved;
FieldProxy(AbstractField<T> delegate, int position) {
super(
@ -69,16 +88,29 @@ final class FieldProxy<T> extends AbstractField<T> implements TableField<Record,
this.position = position;
}
int position() {
final int position() {
return position;
}
void delegate(AbstractField<T> newDelegate) {
final void delegate(AbstractField<T> newDelegate) {
resolve();
this.delegate = newDelegate;
((DataTypeProxy<T>) getDataType()).type((AbstractDataType<T>) newDelegate.getDataType());
}
final FieldProxy<T> resolve() {
this.resolved = true;
this.scopeOwner = null;
return this;
}
final void scopeOwner(Query query) {
if (!resolved && scopeOwner == null)
scopeOwner = query;
}
@Override
public final Name getQualifiedName() {
return delegate.getQualifiedName();

View File

@ -81,8 +81,12 @@ import static org.jooq.impl.Tools.EMPTY_SORTFIELD;
import static org.jooq.impl.Tools.EMPTY_TABLE;
import static org.jooq.impl.Tools.aliased;
import static org.jooq.impl.Tools.anyMatch;
import static org.jooq.impl.Tools.deleteQueryImpl;
import static org.jooq.impl.Tools.findAny;
import static org.jooq.impl.Tools.normaliseNameCase;
import static org.jooq.impl.Tools.selectQueryImpl;
import static org.jooq.impl.Tools.updateQueryImpl;
import static org.jooq.impl.Transformations.transformAppendMissingTableReferences;
import static org.jooq.impl.XMLPassingMechanism.BY_REF;
import static org.jooq.impl.XMLPassingMechanism.BY_VALUE;
import static org.jooq.tools.StringUtils.defaultIfNull;
@ -666,25 +670,26 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
scopeStart();
boolean previousMetaLookupsForceIgnore = metaLookupsForceIgnore();
Query result = null;
try {
switch (characterUpper()) {
case 'A':
if (!parseResultQuery && peekKeyword("ALTER"))
return metaLookupsForceIgnore(true).parseAlter();
return result = metaLookupsForceIgnore(true).parseAlter();
break;
case 'B':
if (!parseResultQuery && peekKeyword("BEGIN"))
return parseBlock(false);
return result = parseBlock(false);
break;
case 'C':
if (!parseResultQuery && peekKeyword("CREATE"))
return metaLookupsForceIgnore(true).parseCreate();
return result = metaLookupsForceIgnore(true).parseCreate();
else if (!parseResultQuery && peekKeyword("COMMENT ON"))
return metaLookupsForceIgnore(true).parseCommentOn();
return result = metaLookupsForceIgnore(true).parseCommentOn();
else if (peekKeyword("CALL") && requireProEdition())
@ -699,21 +704,21 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
case 'D':
if (!parseResultQuery && peekKeyword("DECLARE") && requireProEdition())
return parseBlock(true);
return result = parseBlock(true);
else if (!parseResultQuery && (peekKeyword("DELETE") || peekKeyword("DEL")))
return parseDelete(null);
return result = parseDelete(null);
else if (!parseResultQuery && peekKeyword("DROP"))
return metaLookupsForceIgnore(true).parseDrop();
return result = metaLookupsForceIgnore(true).parseDrop();
else if (!parseResultQuery && peekKeyword("DO"))
return parseDo();
return result = parseDo();
break;
case 'E':
if (!parseResultQuery && peekKeyword("EXECUTE BLOCK AS"))
return parseBlock(true);
return result = parseBlock(true);
else if (!parseResultQuery && peekKeyword("EXEC"))
return parseExec();
return result = parseExec();
else if (peekKeyword("EXECUTE PROCEDURE") && requireProEdition())
@ -724,13 +729,13 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
case 'G':
if (!parseResultQuery && peekKeyword("GRANT"))
return metaLookupsForceIgnore(true).parseGrant();
return result = metaLookupsForceIgnore(true).parseGrant();
break;
case 'I':
if (!parseResultQuery && (peekKeyword("INSERT") || peekKeyword("INS")))
return parseInsert(null);
return result = parseInsert(null);
break;
@ -742,21 +747,21 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
case 'M':
if (!parseResultQuery && peekKeyword("MERGE"))
return parseMerge(null);
return result = parseMerge(null);
break;
case 'O':
if (!parseResultQuery && peekKeyword("OPEN"))
return parseOpen();
return result = parseOpen();
break;
case 'R':
if (!parseResultQuery && peekKeyword("RENAME"))
return metaLookupsForceIgnore(true).parseRename();
return result = metaLookupsForceIgnore(true).parseRename();
else if (!parseResultQuery && peekKeyword("REVOKE"))
return metaLookupsForceIgnore(true).parseRevoke();
return result = metaLookupsForceIgnore(true).parseRevoke();
else if (parseKeywordIf("REPLACE"))
throw notImplemented("REPLACE");
else if (parseKeywordIf("ROLLBACK"))
@ -766,9 +771,9 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
case 'S':
if (peekSelect(false))
return parseSelect();
return result = parseSelect();
else if (!parseResultQuery && peekKeyword("SET"))
return parseSet();
return result = parseSet();
else if (parseKeywordIf("SAVEPOINT"))
throw notImplemented("SAVEPOINT");
@ -776,17 +781,17 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
case 'T':
if (!parseSelect && peekKeyword("TABLE"))
return parseSelect();
return result = parseSelect();
else if (!parseResultQuery && peekKeyword("TRUNCATE"))
return parseTruncate();
return result = parseTruncate();
break;
case 'U':
if (!parseResultQuery && (peekKeyword("UPDATE") || peekKeyword("UPD")))
return parseUpdate(null);
return result = parseUpdate(null);
else if (!parseResultQuery && peekKeyword("USE"))
return parseUse();
return result = parseUse();
else if (parseKeywordIf("UPSERT"))
throw notImplemented("UPSERT");
@ -794,11 +799,11 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
case 'V':
if (!parseSelect && peekKeyword("VALUES"))
return parseSelect();
return result = parseSelect();
case 'W':
if (peekKeyword("WITH"))
return parseWith(parseSelect);
return result = parseWith(parseSelect);
break;
@ -806,9 +811,9 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
// TODO are there other possible statement types?
if (peekKeyword("WITH", false, true, false))
return parseWith(true);
return result = parseWith(true);
else
return parseSelect();
return result = parseSelect();
case '{':
if (peekKeyword("{ CALL") && requireProEdition())
@ -832,7 +837,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
throw e;
}
finally {
scopeEnd();
scopeEnd(result);
scopeResolve();
metaLookupsForceIgnore(previousMetaLookupsForceIgnore);
}
@ -1065,7 +1070,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
result.setForUpdateSkipLocked();
}
scopeEnd();
scopeEnd(result);
return result;
}
@ -1150,16 +1155,17 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
private final SelectQueryImpl<Record> parseQueryExpressionBody(Integer degree, WithImpl with, SelectQueryImpl<Record> prefix) {
SelectQueryImpl<Record> lhs = parseQueryTerm(degree, with, prefix);
SelectQueryImpl<Record> local = lhs;
CombineOperator combine;
while ((combine = parseCombineOperatorIf(false)) != null) {
scopeEnd();
scopeEnd(local);
scopeStart();
if (degree == null)
degree = Tools.degree(lhs);
SelectQueryImpl<Record> rhs = degreeCheck(degree, parseQueryTerm(degree, null, null));
SelectQueryImpl<Record> rhs = local = degreeCheck(degree, parseQueryTerm(degree, null, null));
switch (combine) {
case UNION:
lhs = lhs.union(rhs);
@ -1183,16 +1189,17 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
private final SelectQueryImpl<Record> parseQueryTerm(Integer degree, WithImpl with, SelectQueryImpl<Record> prefix) {
SelectQueryImpl<Record> lhs = prefix != null ? prefix : parseQueryPrimary(degree, with);
SelectQueryImpl<Record> local = lhs;
CombineOperator combine;
while ((combine = parseCombineOperatorIf(true)) != null) {
scopeEnd();
scopeEnd(local);
scopeStart();
if (degree == null)
degree = Tools.degree(lhs);
SelectQueryImpl<Record> rhs = degreeCheck(degree, parseQueryPrimary(degree, null));
SelectQueryImpl<Record> rhs = local = degreeCheck(degree, parseQueryPrimary(degree, null));
switch (combine) {
case INTERSECT:
lhs = lhs.intersect(rhs);
@ -1833,7 +1840,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
// [#10954] These are moved into the INSERT .. SELECT clause handling. They should not be necessary here
// either, but it seems we currently don't correctly implement nesting scopes?
scopeEnd();
scopeEnd(null);
scopeStart();
Select<?> select = parseWithOrSelect();
@ -1908,7 +1915,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
return returning;
}
finally {
scopeEnd();
scopeEnd(((InsertImpl) s1).getDelegate());
}
}
@ -13155,6 +13162,19 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
return parseDialect().family();
}
private final ParseWithMetaLookups metaLookups() {
if (metaLookupsForceIgnore())
return ParseWithMetaLookups.OFF;
else
return this.metaLookups;
}
private final boolean metaLookupsForceIgnore() {
return this.metaLookupsForceIgnore;
}
@ -13436,7 +13456,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
lookupFields.setAll(null);
}
private final void scopeEnd() {
private final void scopeEnd(Query scopeOwner) {
List<FieldProxy<?>> retain = new ArrayList<>();
for (FieldProxy<?> lookup : lookupFields) {
@ -13455,10 +13475,14 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
}
found = resolveInTableScope(tableScope.valueIterable(), lookup.getQualifiedName(), lookup, found);
if (found != null)
if (found != null && !(found.value() instanceof FieldProxy)) {
lookup.delegate((AbstractField) found.value());
else
}
else {
lookup.scopeOwner(scopeOwner);
retain.add(lookup);
}
}
lookupFields.scopeEnd();
@ -13499,6 +13523,14 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
if (x && q.equals(t.value().getQualifiedName()) || !x && q.last().equals(t.value().getName()))
if ((found = Value.of(t.scopeLevel(), t.value().field(lookup.getName()))) != null)
break tableScopeLoop;
}
else if ((f = Value.of(t.scopeLevel(), t.value().field(lookup.getName()))) != null) {
if (found == null || found.scopeLevel() < f.scopeLevel()) {
@ -13524,9 +13556,41 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
}
private final void unknownField(FieldProxy<?> field) {
if (!scopeClear && !metaLookupsForceIgnore && metaLookups == THROW_ON_FAILURE) {
position(field.position());
throw exception("Unknown field identifier");
if (!scopeClear) {
if (metaLookups() == THROW_ON_FAILURE) {
position(field.position());
throw exception("Unknown field identifier");
}
}
}
@ -13548,14 +13612,14 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
return tables.get(0);
}
if (!metaLookupsForceIgnore && metaLookups == THROW_ON_FAILURE)
if (metaLookups() == THROW_ON_FAILURE)
throw exception("Unknown table identifier");
return table(name);
}
private final Field<?> lookupField(Name name) {
if (metaLookups == ParseWithMetaLookups.OFF)
if (metaLookups() == ParseWithMetaLookups.OFF || lookupFields.scopeLevel() < 0)
return field(name);
FieldProxy<?> field = lookupFields.get(name);

View File

@ -63,6 +63,7 @@ import static org.jooq.SQLDialect.FIREBIRD;
// ...
// ...
// ...
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.Keywords.K_NOT;
import static org.jooq.impl.Tools.map;
@ -76,6 +77,7 @@ import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.Field;
// ...
import org.jooq.QueryPartInternal;
import org.jooq.Row;
import org.jooq.SQLDialect;
@ -85,9 +87,14 @@ import org.jooq.SQLDialect;
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
final class RowCondition extends AbstractCondition {
private static final Clause[] CLAUSES = { CONDITION, CONDITION_COMPARISON };
private static final Set<SQLDialect> EMULATE_EQ_AND_NE = SQLDialect.supportedBy(DERBY, FIREBIRD);
private static final Set<SQLDialect> EMULATE_RANGES = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD);
private static final Clause[] CLAUSES = { CONDITION, CONDITION_COMPARISON };
private static final Set<SQLDialect> EMULATE_EQ_AND_NE = SQLDialect.supportedBy(DERBY, FIREBIRD);
private static final Set<SQLDialect> EMULATE_RANGES = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD);
private final Row left;
private final Row right;
@ -116,6 +123,12 @@ final class RowCondition extends AbstractCondition {
}
private final QueryPartInternal delegate(Configuration configuration) {
// Regular comparison predicate emulation
if ((comparator == EQUALS || comparator == NOT_EQUALS) &&
(forceEmulation || EMULATE_EQ_AND_NE.contains(configuration.dialect()))) {

View File

@ -61,6 +61,7 @@ import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
// ...
// ...
import static org.jooq.impl.DSL.exists;
import static org.jooq.impl.DSL.notExists;
import static org.jooq.impl.DSL.select;

View File

@ -64,6 +64,7 @@ import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
// ...
// ...
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.selectCount;
import static org.jooq.impl.Keywords.K_IS_NOT_NULL;
@ -71,8 +72,6 @@ import static org.jooq.impl.Keywords.K_IS_NULL;
import static org.jooq.impl.Tools.map;
import static org.jooq.impl.Tools.visitSubquery;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.jooq.Clause;

View File

@ -68,6 +68,7 @@ import static org.jooq.SQLDialect.POSTGRES;
// ...
import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
import static org.jooq.impl.DSL.asterisk;
import static org.jooq.impl.DSL.exists;
import static org.jooq.impl.DSL.name;

View File

@ -41,15 +41,14 @@ package org.jooq.impl;
// ...
// ...
import static org.jooq.impl.Keywords.K_DECLARE;
import static org.jooq.impl.Keywords.K_WITH;
import static org.jooq.impl.Tools.increment;
import static org.jooq.impl.Tools.DataKey.DATA_TOP_LEVEL_CTE;
import static org.jooq.impl.WithImpl.acceptWithRecursive;
import org.jooq.Clause;
import org.jooq.Context;
// ...
import org.jooq.QueryPartInternal;
import org.jooq.SQLDialect;
import org.jooq.Statement;
import org.jooq.impl.AbstractContext.ScopeStackElement;
import org.jooq.impl.Tools.DataExtendedKey;
@ -124,7 +123,7 @@ enum ScopeMarker {
boolean noWith = afterLast != null && beforeFirst.positions[0] == afterLast.positions[0];
if (noWith) {
ctx.visit(K_WITH);
acceptWithRecursive(ctx, cte.recursive);
if (single)
ctx.formatIndentStart()

View File

@ -3211,12 +3211,12 @@ final class Tools {
@SuppressWarnings("unchecked")
@SuppressWarnings({ "unchecked", "rawtypes" })
static final <R extends Record> SelectQueryImpl<R> selectQueryImpl(QueryPart part) {
if (part instanceof SelectQueryImpl)
return (SelectQueryImpl<R>) part;
else if (part instanceof AbstractDelegatingQuery)
return ((AbstractDelegatingQuery<R, SelectQueryImpl<R>>) part).getDelegate();
else if (part instanceof SelectImpl)
return (SelectQueryImpl<R>) ((SelectImpl) part).getDelegate();
else if (part instanceof ScalarSubquery)
return selectQueryImpl(((ScalarSubquery<?>) part).query);
else if (part instanceof QuantifiedSelectImpl)
@ -3234,11 +3234,29 @@ final class Tools {
return null;
}
static final UpdateQueryImpl<?> updateQueryImpl(Query query) {
AbstractDMLQuery<?> result = abstractDMLQuery(query);
if (result instanceof UpdateQueryImpl)
return (UpdateQueryImpl<?>) result;
else
return null;
}
static final DeleteQueryImpl<?> deleteQueryImpl(Query query) {
AbstractDMLQuery<?> result = abstractDMLQuery(query);
if (result instanceof DeleteQueryImpl)
return (DeleteQueryImpl<?>) result;
else
return null;
}
static final AbstractDMLQuery<?> abstractDMLQuery(Query query) {
if (query instanceof AbstractDMLQuery)
return (AbstractDMLQuery<?>) query;
else if (query instanceof AbstractDelegatingQuery)
return abstractDMLQuery(((AbstractDelegatingQuery<?, ?>) query).getDelegate());
else if (query instanceof AbstractDelegatingDMLQuery)
return abstractDMLQuery(((AbstractDelegatingDMLQuery<?, ?>) query).getDelegate());
else if (query instanceof DMLQueryAsResultQuery)
return ((DMLQueryAsResultQuery<?, ?>) query).getDelegate();
else

View File

@ -48,6 +48,8 @@ import org.jooq.impl.ScopeMarker.ScopeContent;
*/
final class TopLevelCte extends QueryPartList<QueryPart> implements ScopeContent {
boolean recursive;
@Override
public void accept(Context<?> ctx) {
markTopLevelCteAndAccept(ctx, c -> super.accept(c));

View File

@ -48,6 +48,7 @@ import static org.jooq.SQLDialect.CUBRID;
import static org.jooq.SQLDialect.DERBY;
// ...
import static org.jooq.SQLDialect.FIREBIRD;
import static org.jooq.SQLDialect.H2;
// ...
import static org.jooq.SQLDialect.HSQLDB;
import static org.jooq.SQLDialect.IGNITE;
@ -74,10 +75,8 @@ import java.util.Set;
import java.util.function.Predicate;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.QueryPart;
import org.jooq.SQLDialect;
import org.jooq.Scope;
import org.jooq.conf.Transformation;
/**
@ -87,9 +86,10 @@ import org.jooq.conf.Transformation;
*/
final class Transformations {
private static final Set<SQLDialect> NO_SUPPORT_IN_LIMIT = SQLDialect.supportedBy(MARIADB, MYSQL);
private static final Set<SQLDialect> EMULATE_QUALIFY = SQLDialect.supportedBy(CUBRID, FIREBIRD, MARIADB, MYSQL, POSTGRES, SQLITE);
private static final Set<SQLDialect> EMULATE_ROWNUM = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, HSQLDB, IGNITE, MARIADB, MYSQL, POSTGRES, SQLITE);
private static final Set<SQLDialect> NO_SUPPORT_IN_LIMIT = SQLDialect.supportedBy(MARIADB, MYSQL);
private static final Set<SQLDialect> SUPPORT_MISSING_TABLE_REFERENCES = SQLDialect.supportedBy();
private static final Set<SQLDialect> EMULATE_QUALIFY = SQLDialect.supportedBy(CUBRID, FIREBIRD, MARIADB, MYSQL, POSTGRES, SQLITE);
private static final Set<SQLDialect> EMULATE_ROWNUM = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, HSQLDB, IGNITE, MARIADB, MYSQL, POSTGRES, SQLITE);
static final SelectQueryImpl<?> subqueryWithLimit(QueryPart source) {
SelectQueryImpl<?> s;
@ -123,6 +123,15 @@ final class Transformations {
);
}
static final boolean transformAppendMissingTableReferences(Configuration configuration) {
return transform(
configuration,
"Settings.transformAppendMissingTableReferences",
configuration.settings().getParseAppendMissingTableReferences(),
c -> SUPPORT_MISSING_TABLE_REFERENCES.contains(c.settings().getParseDialect())
);
}
/**
* Check whether a given SQL transformation needs to be applied.
*/

View File

@ -521,6 +521,10 @@ final class UpdateQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
return condition.hasWhere();
}
final TableList getFrom() {
return from;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
final void accept0(Context<?> ctx) {

View File

@ -66,6 +66,7 @@ import static org.jooq.impl.WindowSpecificationImpl.Exclude.TIES;
import static org.jooq.impl.WindowSpecificationImpl.FrameUnits.GROUPS;
import static org.jooq.impl.WindowSpecificationImpl.FrameUnits.RANGE;
import static org.jooq.impl.WindowSpecificationImpl.FrameUnits.ROWS;
import static org.jooq.tools.StringUtils.defaultIfNull;
import java.util.Arrays;
import java.util.Collection;
@ -81,6 +82,7 @@ import org.jooq.WindowSpecificationFinalStep;
import org.jooq.WindowSpecificationOrderByStep;
import org.jooq.WindowSpecificationPartitionByStep;
import org.jooq.WindowSpecificationRowsAndStep;
import org.jooq.conf.RenderImplicitWindowRange;
import org.jooq.impl.Tools.BooleanDataKey;
/**
@ -149,7 +151,11 @@ final class WindowSpecificationImpl extends AbstractQueryPart implements
boolean hasWindowDefinitions = windowDefinition != null;
boolean hasPartitionBy = !partitionBy.isEmpty();
boolean hasOrderBy = !o.isEmpty();
boolean hasFrame = frameStart != null;
boolean hasFrame = frameStart != null
;
int clauses = 0;
@ -199,17 +205,53 @@ final class WindowSpecificationImpl extends AbstractQueryPart implements
if (hasWindowDefinitions || hasPartitionBy || hasOrderBy)
ctx.formatSeparator();
ctx.visit(frameUnits.keyword).sql(' ');
FrameUnits u = frameUnits;
Integer s = frameStart;
Integer e = frameEnd;
if (frameEnd != null) {
ctx.visit(u.keyword).sql(' ');
if (e != null) {
ctx.visit(K_BETWEEN).sql(' ');
toSQLRows(ctx, frameStart);
toSQLRows(ctx, s);
ctx.sql(' ').visit(K_AND).sql(' ');
toSQLRows(ctx, frameEnd);
toSQLRows(ctx, e);
}
else {
toSQLRows(ctx, frameStart);
toSQLRows(ctx, s);
}
if (exclude != null)

View File

@ -43,6 +43,7 @@ import static org.jooq.Clause.WITH;
// ...
// ...
// ...
// ...
import static org.jooq.impl.DSL.count;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.one;
@ -125,7 +126,6 @@ import org.jooq.WithAsStep7;
import org.jooq.WithAsStep8;
import org.jooq.WithAsStep9;
import org.jooq.WithStep;
import org.jooq.impl.Tools.BooleanDataKey;
/**
* This type models an intermediary DSL construction step, which leads towards
@ -219,14 +219,8 @@ implements
ctx.visit(K_WITH);
if (recursive
)
ctx.sql(' ').visit(K_RECURSIVE);
acceptWithRecursive(ctx, recursive);
ctx.data(DATA_LIST_ALREADY_INDENTED, true, c1 ->
c1.formatIndentStart()
@ -238,6 +232,17 @@ implements
}
}
static final void acceptWithRecursive(Context<?> ctx, boolean recursive) {
ctx.visit(K_WITH);
if (recursive
)
ctx.sql(' ').visit(K_RECURSIVE);
}
@Override
public final Clause[] clauses(Context<?> ctx) {
return CLAUSES;

View File

@ -129,6 +129,10 @@ providing a name to parameters, resulting in <code>:1</code> or <code>@1</code>
<element name="renderOptionalOuterKeyword" type="jooq-runtime:RenderOptionalKeyword" minOccurs="0" maxOccurs="1" default="DEFAULT">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether to render the optional <code>OUTER</code> keyword in <code>OUTER JOIN</code>, if it is optional in the output dialect.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="renderImplicitWindowRange" type="jooq-runtime:RenderImplicitWindowRange" minOccurs="0" maxOccurs="1" default="OFF">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether to render an explicit window <code>RANGE</code> clause when an implicit clause is applied.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="renderScalarSubqueriesForStoredFunctions" type="boolean" minOccurs="0" maxOccurs="1" default="false">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether stored function calls should be wrapped in scalar subqueries.
@ -635,6 +639,17 @@ providing a name to parameters, resulting in <code>:1</code> or <code>@1</code>
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[[#7163] Whether the parser should perform meta lookups in the Configuration's MetaProvider.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="parseAppendMissingTableReferences" type="jooq-runtime:Transformation" minOccurs="0" maxOccurs="1" default="WHEN_NEEDED">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Transform the parsed SQL to append missing table references to the query's <code>FROM</code> or <code>USING</code> clause, if applicable.
<p>
Teradata (and possibly others) allow for referencing tables that are not listed in the <code>FROM</code>
clause, such as <code>SELECT t.* FROM t WHERE t.i = u.i</code>. This transformation is executed in the
parser, to produce <code>SELECT t.* FROM t, u WHERE t.i = u.i</code>, instead. By default, it is active
when the input dialect supports this syntax.
<p>
This feature is available in the commercial distribution only.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="parseSearchPath" type="jooq-runtime:ParseSearchSchemata" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[[#8616] The search path to be used for unqualified table lookups by the parser.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
@ -1171,6 +1186,26 @@ Either &lt;input/&gt; or &lt;inputExpression/&gt; must be provided]]></jxb:javad
</restriction>
</simpleType>
<simpleType name="RenderImplicitWindowRange">
<restriction base="string">
<!-- Implicit RANGE clause will not be generated explicitly. The RDBMS's implicit behaviour is used -->
<enumeration value="OFF"/>
<!-- Implicit RANGE clause is generated as ROWS UNBOUNDED PRECEDING -->
<enumeration value="ROWS_UNBOUNDED_PRECEDING"/>
<!-- Implicit RANGE clause is generated as ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING -->
<enumeration value="ROWS_ALL"/>
<!-- Implicit RANGE clause is generated as RANGE UNBOUNDED PRECEDING -->
<enumeration value="RANGE_UNBOUNDED_PRECEDING"/>
<!-- Implicit RANGE clause is generated as RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING -->
<enumeration value="RANGE_ALL"/>
</restriction>
</simpleType>
<simpleType name="InterpreterNameLookupCaseSensitivity">
<restriction base="string">