[jOOQ/jOOQ#18547] Parser cannot parse non-scalar subquery NULL predicate
This commit is contained in:
parent
8f2e2d3385
commit
c8c02f53ab
@ -631,6 +631,7 @@ import org.jooq.DropTableStep;
|
||||
import org.jooq.DropTypeStep;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.FieldOrRow;
|
||||
import org.jooq.FieldOrRowOrSelect;
|
||||
// ...
|
||||
// ...
|
||||
import org.jooq.Function1;
|
||||
@ -713,6 +714,7 @@ import org.jooq.SQLDialect;
|
||||
import org.jooq.SQLDialectCategory;
|
||||
import org.jooq.Schema;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.SelectCorrelatedSubqueryStep;
|
||||
import org.jooq.SelectField;
|
||||
import org.jooq.SelectFieldOrAsterisk;
|
||||
import org.jooq.Sequence;
|
||||
@ -1419,6 +1421,19 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
}
|
||||
|
||||
private final Field<?> parseScalarSubqueryIf() {
|
||||
FieldOrRowOrSelect r = parseSubqueryIf();
|
||||
|
||||
if (r instanceof Select<?> s) {
|
||||
if (Tools.degree(s) != 1)
|
||||
throw exception("Select list must contain exactly one column");
|
||||
|
||||
return field((Select) s);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private final FieldOrRowOrSelect parseSubqueryIf() {
|
||||
int p = position();
|
||||
|
||||
try {
|
||||
@ -1426,10 +1441,8 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
parse('(');
|
||||
SelectQueryImpl<Record> select = parseWithOrSelect();
|
||||
parse(')');
|
||||
if (Tools.degree(select) != 1)
|
||||
throw exception("Select list must contain exactly one column");
|
||||
|
||||
return field((Select) select);
|
||||
return select;
|
||||
}
|
||||
}
|
||||
catch (ParserException e) {
|
||||
@ -1782,7 +1795,11 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
return lhs;
|
||||
}
|
||||
|
||||
private final SelectQueryImpl<Record> degreeCheck(int expected, SelectQueryImpl<Record> s) {
|
||||
private final <S extends Select<?>> S degreeCheck(int expected, S s) {
|
||||
return degreeCheck(expected, s, true);
|
||||
}
|
||||
|
||||
private final <S extends Select<?>> S degreeCheck(int expected, S s, boolean throwIfNotMatched) {
|
||||
if (expected == 0)
|
||||
return s;
|
||||
|
||||
@ -1791,7 +1808,10 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
return s;
|
||||
|
||||
if (expected != actual)
|
||||
throw exception("Select list must contain " + expected + " columns. Got: " + actual);
|
||||
if (throwIfNotMatched)
|
||||
throw exception("Select list must contain " + expected + " columns. Got: " + actual);
|
||||
else
|
||||
return null;
|
||||
|
||||
return s;
|
||||
}
|
||||
@ -7098,10 +7118,11 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
}
|
||||
|
||||
boolean notOp = false;
|
||||
FieldOrRow left = parseConcat();
|
||||
FieldOrRowOrSelect left = parseConcat();
|
||||
Field leftScalar = toField(left, false);
|
||||
Select leftSelect = left instanceof Select s ? s : null;
|
||||
int p2 = position();
|
||||
boolean not = parseKeywordIf("NOT");
|
||||
boolean isField = left instanceof Field;
|
||||
Comparator comp;
|
||||
TSQLOuterJoinComparator outer;
|
||||
|
||||
@ -7131,25 +7152,25 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
// TODO equal degrees
|
||||
Condition result =
|
||||
all
|
||||
? isField
|
||||
? leftScalar != null
|
||||
? peekSelectOrWith(true)
|
||||
? ((Field) left).compare(comp, DSL.all(parseWithOrSelect(1)))
|
||||
: ((Field) left).compare(comp, DSL.all(parseList(',', c -> c.parseField()).toArray(EMPTY_FIELD)))
|
||||
? leftScalar.compare(comp, DSL.all(parseWithOrSelect(1)))
|
||||
: leftScalar.compare(comp, DSL.all(parseList(',', c -> c.parseField()).toArray(EMPTY_FIELD)))
|
||||
|
||||
// TODO: Support quantifiers also for rows
|
||||
: new RowSubqueryCondition((Row) left, DSL.all(parseWithOrSelect(((Row) left).size())), comp)
|
||||
|
||||
: any
|
||||
? isField
|
||||
? leftScalar != null
|
||||
? peekSelectOrWith(true)
|
||||
? ((Field) left).compare(comp, DSL.any(parseWithOrSelect(1)))
|
||||
: ((Field) left).compare(comp, DSL.any(parseList(',', c -> c.parseField()).toArray(EMPTY_FIELD)))
|
||||
? leftScalar.compare(comp, DSL.any(parseWithOrSelect(1)))
|
||||
: leftScalar.compare(comp, DSL.any(parseList(',', c -> c.parseField()).toArray(EMPTY_FIELD)))
|
||||
|
||||
// TODO: Support quantifiers also for rows
|
||||
: new RowSubqueryCondition((Row) left, DSL.any(parseWithOrSelect(((Row) left).size())), comp)
|
||||
|
||||
: isField
|
||||
? ((Field) left).compare(comp, toField(parseConcat()))
|
||||
: leftScalar != null
|
||||
? leftScalar.compare(comp, toField(parseConcat()))
|
||||
: AbstractRow.compare((Row) left, comp, parseRow(((Row) left).size(), true));
|
||||
|
||||
if (all || any)
|
||||
@ -7162,25 +7183,32 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
|
||||
if (parseKeywordIf("NULL"))
|
||||
return not
|
||||
? isField
|
||||
? ((Field) left).isNotNull()
|
||||
? leftScalar != null
|
||||
? leftScalar.isNotNull()
|
||||
: leftSelect != null
|
||||
? new SelectIsNotNull(leftSelect)
|
||||
: ((Row) left).isNotNull()
|
||||
: isField
|
||||
? ((Field) left).isNull()
|
||||
: leftScalar != null
|
||||
? leftScalar.isNull()
|
||||
: leftSelect != null
|
||||
? new SelectIsNull(leftSelect)
|
||||
: ((Row) left).isNull();
|
||||
else if (isField && parseKeywordIf("JSON"))
|
||||
else if (leftScalar != null && parseKeywordIf("JSON"))
|
||||
return not
|
||||
? ((Field) left).isNotJson()
|
||||
: ((Field) left).isJson();
|
||||
else if (isField && parseKeywordIf("DOCUMENT"))
|
||||
? leftScalar.isNotJson()
|
||||
: leftScalar.isJson();
|
||||
else if (leftScalar != null && parseKeywordIf("DOCUMENT"))
|
||||
return not
|
||||
? ((Field) left).isNotDocument()
|
||||
: ((Field) left).isDocument();
|
||||
? leftScalar.isNotDocument()
|
||||
: leftScalar.isDocument();
|
||||
|
||||
not = parseKeywordIf("DISTINCT FROM") == not;
|
||||
if (left instanceof Field f) {
|
||||
if (leftScalar != null) {
|
||||
Field right = toField(parseConcat());
|
||||
return not ? f.isNotDistinctFrom(right) : f.isDistinctFrom(right);
|
||||
return not ? leftScalar.isNotDistinctFrom(right) : leftScalar.isDistinctFrom(right);
|
||||
}
|
||||
else if (leftSelect != null) {
|
||||
throw notImplementedNonScalarSelectPredicate();
|
||||
}
|
||||
else {
|
||||
Row right = parseRow(((Row) left).size(), true);
|
||||
@ -7194,37 +7222,40 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
Condition result;
|
||||
|
||||
// [#12691] Some dialects support A IN B syntax without parentheses for single element in lists
|
||||
if (isField && !peek('(')) {
|
||||
if (leftScalar != null && !peek('(')) {
|
||||
result = not
|
||||
? ((Field) left).notIn(parseConcat())
|
||||
: ((Field) left).in(parseConcat());
|
||||
? leftScalar.notIn(parseConcat())
|
||||
: leftScalar.in(parseConcat());
|
||||
}
|
||||
else {
|
||||
parse('(');
|
||||
|
||||
if (leftScalar == null && leftSelect != null)
|
||||
throw notImplementedNonScalarSelectPredicate();
|
||||
|
||||
if (peek(')'))
|
||||
result = not
|
||||
? isField
|
||||
? ((Field) left).notIn(EMPTY_FIELD)
|
||||
? leftScalar != null
|
||||
? leftScalar.notIn(EMPTY_FIELD)
|
||||
: new RowInCondition((Row) left, new QueryPartList<>(), true)
|
||||
: isField
|
||||
? ((Field) left).in(EMPTY_FIELD)
|
||||
: leftScalar != null
|
||||
? leftScalar.in(EMPTY_FIELD)
|
||||
: new RowInCondition((Row) left, new QueryPartList<>(), false);
|
||||
else if (peekSelectOrWith(true))
|
||||
result = not
|
||||
? isField
|
||||
? ((Field) left).notIn(parseWithOrSelect(1))
|
||||
? leftScalar != null
|
||||
? leftScalar.notIn(parseWithOrSelect(1))
|
||||
: new RowSubqueryCondition((Row) left, parseWithOrSelect(((Row) left).size()), NOT_IN)
|
||||
: isField
|
||||
? ((Field) left).in(parseWithOrSelect(1))
|
||||
: leftScalar != null
|
||||
? leftScalar.in(parseWithOrSelect(1))
|
||||
: new RowSubqueryCondition((Row) left, parseWithOrSelect(((Row) left).size()), IN);
|
||||
else
|
||||
result = not
|
||||
? isField
|
||||
? ((Field) left).notIn(parseList(',', c -> c.parseField()))
|
||||
? leftScalar != null
|
||||
? leftScalar.notIn(parseList(',', c -> c.parseField()))
|
||||
: new RowInCondition((Row) left, new QueryPartList<>(parseList(',', c -> parseRow(((Row) left).size()))), true)
|
||||
: isField
|
||||
? ((Field) left).in(parseList(',', c -> c.parseField()))
|
||||
: leftScalar != null
|
||||
? leftScalar.in(parseList(',', c -> c.parseField()))
|
||||
: new RowInCondition((Row) left, new QueryPartList<>(parseList(',', c -> parseRow(((Row) left).size()))), false);
|
||||
|
||||
parse(')');
|
||||
@ -7234,40 +7265,44 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
}
|
||||
else if (parseKeywordIf("BETWEEN")) {
|
||||
boolean symmetric = !parseKeywordIf("ASYMMETRIC") && parseKeywordIf("SYMMETRIC");
|
||||
FieldOrRow r1 = isField
|
||||
|
||||
if (leftScalar == null && leftSelect != null)
|
||||
throw notImplementedNonScalarSelectPredicate();
|
||||
|
||||
FieldOrRowOrSelect r1 = leftScalar != null
|
||||
? parseConcat()
|
||||
: parseRow(((Row) left).size());
|
||||
parseKeyword("AND");
|
||||
FieldOrRow r2 = isField
|
||||
FieldOrRowOrSelect r2 = leftScalar != null
|
||||
? parseConcat()
|
||||
: parseRow(((Row) left).size());
|
||||
|
||||
return symmetric
|
||||
? not
|
||||
? isField
|
||||
? ((Field) left).notBetweenSymmetric((Field) r1, (Field) r2)
|
||||
? leftScalar != null
|
||||
? leftScalar.notBetweenSymmetric((Field) r1, (Field) r2)
|
||||
: new RowBetweenCondition((Row) left, (Row) r1, not, symmetric, (Row) r2)
|
||||
: isField
|
||||
? ((Field) left).betweenSymmetric((Field) r1, (Field) r2)
|
||||
: leftScalar != null
|
||||
? leftScalar.betweenSymmetric((Field) r1, (Field) r2)
|
||||
: new RowBetweenCondition((Row) left, (Row) r1, not, symmetric, (Row) r2)
|
||||
: not
|
||||
? isField
|
||||
? ((Field) left).notBetween((Field) r1, (Field) r2)
|
||||
? leftScalar != null
|
||||
? leftScalar.notBetween((Field) r1, (Field) r2)
|
||||
: new RowBetweenCondition((Row) left, (Row) r1, not, symmetric, (Row) r2)
|
||||
: isField
|
||||
? ((Field) left).between((Field) r1, (Field) r2)
|
||||
: leftScalar != null
|
||||
? leftScalar.between((Field) r1, (Field) r2)
|
||||
: new RowBetweenCondition((Row) left, (Row) r1, not, symmetric, (Row) r2);
|
||||
}
|
||||
else if (isField && (parseKeywordIf("LIKE") || parseOperatorIf("~~") || (notOp = parseOperatorIf("!~~")))) {
|
||||
else if (leftScalar != null && (parseKeywordIf("LIKE") || parseOperatorIf("~~") || (notOp = parseOperatorIf("!~~")))) {
|
||||
if (parseKeywordIf("ANY")) {
|
||||
parse('(');
|
||||
if (peekSelectOrWith(true)) {
|
||||
Select<?> select = parseWithOrSelect();
|
||||
parse(')');
|
||||
if (binary((Field) left))
|
||||
return (not ^ notOp) ? ((Field) left).notBinaryLike(any(select)) : ((Field) left).binaryLike(any(select));
|
||||
if (binary(leftScalar))
|
||||
return (not ^ notOp) ? leftScalar.notBinaryLike(any(select)) : leftScalar.binaryLike(any(select));
|
||||
else
|
||||
return parseEscapeClauseIf((not ^ notOp) ? ((Field) left).notLike(any(select)) : ((Field) left).like(any(select)));
|
||||
return parseEscapeClauseIf((not ^ notOp) ? leftScalar.notLike(any(select)) : leftScalar.like(any(select)));
|
||||
}
|
||||
else {
|
||||
List<Field<?>> fields;
|
||||
@ -7278,10 +7313,10 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
fields = parseList(',', c -> toField(parseConcat()));
|
||||
parse(')');
|
||||
}
|
||||
if (binary((Field) left))
|
||||
return (not ^ notOp) ? ((Field<String>) left).notBinaryLike(any((Field<byte[]>[]) fields.toArray(EMPTY_FIELD))) : ((Field<String>) left).binaryLike(any((Field<byte[]>[]) fields.toArray(EMPTY_FIELD)));
|
||||
if (binary(leftScalar))
|
||||
return (not ^ notOp) ? leftScalar.notBinaryLike(any((Field<byte[]>[]) fields.toArray(EMPTY_FIELD))) : leftScalar.binaryLike(any((Field<byte[]>[]) fields.toArray(EMPTY_FIELD)));
|
||||
else
|
||||
return parseEscapeClauseIf((not ^ notOp) ? ((Field<String>) left).notLike(any((Field<String>[]) fields.toArray(EMPTY_FIELD))) : ((Field<String>) left).like(any((Field<String>[]) fields.toArray(EMPTY_FIELD))));
|
||||
return parseEscapeClauseIf((not ^ notOp) ? leftScalar.notLike(any((Field<String>[]) fields.toArray(EMPTY_FIELD))) : leftScalar.like(any((Field<String>[]) fields.toArray(EMPTY_FIELD))));
|
||||
}
|
||||
}
|
||||
else if (parseKeywordIf("ALL")) {
|
||||
@ -7289,10 +7324,10 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
if (peekSelectOrWith(true)) {
|
||||
Select<?> select = parseWithOrSelect();
|
||||
parse(')');
|
||||
if (binary((Field) left))
|
||||
return (not ^ notOp) ? ((Field) left).notBinaryLike(all(select)) : ((Field) left).binaryLike(all(select));
|
||||
if (binary(leftScalar))
|
||||
return (not ^ notOp) ? leftScalar.notBinaryLike(all(select)) : leftScalar.binaryLike(all(select));
|
||||
else
|
||||
return parseEscapeClauseIf((not ^ notOp) ? ((Field) left).notLike(all(select)) : ((Field) left).like(all(select)));
|
||||
return parseEscapeClauseIf((not ^ notOp) ? leftScalar.notLike(all(select)) : leftScalar.like(all(select)));
|
||||
}
|
||||
else {
|
||||
List<Field<?>> fields;
|
||||
@ -7303,39 +7338,39 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
fields = parseList(',', c -> toField(parseConcat()));
|
||||
parse(')');
|
||||
}
|
||||
if (binary((Field) left))
|
||||
return (not ^ notOp) ? ((Field<String>) left).notBinaryLike(all((Field<byte[]>[]) fields.toArray(EMPTY_FIELD))) : ((Field<String>) left).binaryLike(all((Field<byte[]>[]) fields.toArray(EMPTY_FIELD)));
|
||||
if (binary(leftScalar))
|
||||
return (not ^ notOp) ? leftScalar.notBinaryLike(all((Field<byte[]>[]) fields.toArray(EMPTY_FIELD))) : leftScalar.binaryLike(all((Field<byte[]>[]) fields.toArray(EMPTY_FIELD)));
|
||||
else
|
||||
return parseEscapeClauseIf((not ^ notOp) ? ((Field<String>) left).notLike(all((Field<String>[]) fields.toArray(EMPTY_FIELD))) : ((Field<String>) left).like(all((Field<String>[]) fields.toArray(EMPTY_FIELD))));
|
||||
return parseEscapeClauseIf((not ^ notOp) ? leftScalar.notLike(all((Field<String>[]) fields.toArray(EMPTY_FIELD))) : leftScalar.like(all((Field<String>[]) fields.toArray(EMPTY_FIELD))));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Field right = toField(parseConcat());
|
||||
|
||||
if (binary((Field) left) || binary(right))
|
||||
return (not ^ notOp) ? ((Field) left).notBinaryLike(right) : ((Field) left).binaryLike(right);
|
||||
if (binary(leftScalar) || binary(right))
|
||||
return (not ^ notOp) ? leftScalar.notBinaryLike(right) : leftScalar.binaryLike(right);
|
||||
else
|
||||
return parseEscapeClauseIf((not ^ notOp) ? ((Field) left).notLike(right) : ((Field) left).like(right));
|
||||
return parseEscapeClauseIf((not ^ notOp) ? leftScalar.notLike(right) : leftScalar.like(right));
|
||||
}
|
||||
}
|
||||
else if (isField && (parseKeywordIf("ILIKE") || parseOperatorIf("~~*") || (notOp = parseOperatorIf("!~~*")))) {
|
||||
else if (leftScalar != null && (parseKeywordIf("ILIKE") || parseOperatorIf("~~*") || (notOp = parseOperatorIf("!~~*")))) {
|
||||
Field right = toField(parseConcat());
|
||||
LikeEscapeStep like = (not ^ notOp) ? ((Field) left).notLikeIgnoreCase(right) : ((Field) left).likeIgnoreCase(right);
|
||||
LikeEscapeStep like = (not ^ notOp) ? leftScalar.notLikeIgnoreCase(right) : leftScalar.likeIgnoreCase(right);
|
||||
return parseEscapeClauseIf(like);
|
||||
}
|
||||
else if (isField && (parseKeywordIf("REGEXP")
|
||||
else if (leftScalar != null && (parseKeywordIf("REGEXP")
|
||||
|| parseKeywordIf("RLIKE")
|
||||
|| parseKeywordIf("LIKE_REGEX")
|
||||
|| parseOperatorIf("~")
|
||||
|| (notOp = parseOperatorIf("!~")))) {
|
||||
Field right = toField(parseConcat());
|
||||
return (not ^ notOp)
|
||||
? ((Field) left).notLikeRegex(right)
|
||||
: ((Field) left).likeRegex(right);
|
||||
? leftScalar.notLikeRegex(right)
|
||||
: leftScalar.likeRegex(right);
|
||||
}
|
||||
else if (isField && parseKeywordIf("SIMILAR TO")) {
|
||||
else if (leftScalar != null && parseKeywordIf("SIMILAR TO")) {
|
||||
Field right = toField(parseConcat());
|
||||
LikeEscapeStep like = not ? ((Field) left).notSimilarTo(right) : ((Field) left).similarTo(right);
|
||||
LikeEscapeStep like = not ? leftScalar.notSimilarTo(right) : leftScalar.similarTo(right);
|
||||
return parseEscapeClauseIf(like);
|
||||
}
|
||||
else if (left instanceof Row && ((Row) left).size() == 2 && parseKeywordIf("OVERLAPS")) {
|
||||
@ -7357,11 +7392,12 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
Condition result;
|
||||
|
||||
parse('(');
|
||||
FieldOrRow left = parseConcat();
|
||||
FieldOrRowOrSelect left = parseConcat();
|
||||
parse(',');
|
||||
|
||||
if (left instanceof Field f)
|
||||
result = f.isNotDistinctFrom((Field) toField(parseConcat()));
|
||||
Field f = toField(left, false);
|
||||
if (f != null)
|
||||
result = f.isNotDistinctFrom(toField(parseConcat()));
|
||||
else
|
||||
result = new RowIsDistinctFrom((Row) left, parseRow(((Row) left).size(), true), true);
|
||||
|
||||
@ -8416,6 +8452,8 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
return null;
|
||||
else if (part instanceof Field<?> f)
|
||||
return f;
|
||||
else if (part instanceof Select<?> s)
|
||||
return DSL.field((Select) degreeCheck(1, s));
|
||||
else if (part instanceof Row r)
|
||||
return r;
|
||||
else
|
||||
@ -8423,20 +8461,32 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
}
|
||||
|
||||
private final Field<?> toField(QueryPart part) {
|
||||
return toField(part, true);
|
||||
}
|
||||
|
||||
private final Field<?> toField(QueryPart part, boolean throwIfNonScalar) {
|
||||
if (part == null)
|
||||
return null;
|
||||
else if (part instanceof Field<?> f)
|
||||
return f;
|
||||
else
|
||||
else if (part instanceof Select s)
|
||||
if (degreeCheck(1, s, throwIfNonScalar) != null)
|
||||
return DSL.field(s);
|
||||
else
|
||||
return null;
|
||||
else if (throwIfNonScalar)
|
||||
throw expected("Field");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private final FieldOrRow parseConcat() {
|
||||
FieldOrRow r = parseCollated();
|
||||
private final FieldOrRowOrSelect parseConcat() {
|
||||
FieldOrRowOrSelect r = parseCollated();
|
||||
|
||||
if (r instanceof Field)
|
||||
Field f = toField(r, false);
|
||||
if (f != null)
|
||||
while (parseIf("||"))
|
||||
r = concatOperator((Field) r, toField(parseCollated()));
|
||||
r = f = concatOperator(f, toField(parseCollated()));
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -8450,12 +8500,13 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
return DSL.concat(a1, a2);
|
||||
}
|
||||
|
||||
private final FieldOrRow parseCollated() {
|
||||
FieldOrRow r = parseOp();
|
||||
private final FieldOrRowOrSelect parseCollated() {
|
||||
FieldOrRowOrSelect r = parseOp();
|
||||
|
||||
if (r instanceof Field) {
|
||||
Field f = toField(r, false);
|
||||
if (f != null) {
|
||||
if (parseKeywordIf("COLLATE"))
|
||||
r = ((Field) r).collate(parseCollation());
|
||||
r = f = f.collate(parseCollation());
|
||||
|
||||
|
||||
|
||||
@ -8605,79 +8656,81 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
|
||||
// Any numeric operator of low precedence
|
||||
// See https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-PRECEDENCE
|
||||
private final FieldOrRow parseOp() {
|
||||
FieldOrRow l = parseSum();
|
||||
private final FieldOrRowOrSelect parseOp() {
|
||||
FieldOrRowOrSelect l = parseSum();
|
||||
|
||||
if (l instanceof Field)
|
||||
Field f = toField(l, false);
|
||||
if (f != null)
|
||||
for (;;)
|
||||
if (parseIf("<<"))
|
||||
l = ((Field) l).shl((Field) parseSum());
|
||||
l = f = f.shl(toField(parseSum()));
|
||||
else if (parseIf(">>"))
|
||||
l = ((Field) l).shr((Field) parseSum());
|
||||
l = f = f.shr(toField(parseSum()));
|
||||
else if (parseIf("->>")) {
|
||||
Field r = (Field) parseSum();
|
||||
Field r = toField(parseSum());
|
||||
|
||||
// [#10018] We cannot really know reliably whether this is a
|
||||
// index or attribute access. Let's default to the
|
||||
// more popular attribute access for now. Also,
|
||||
// JSONB is likely more popular than JSON.
|
||||
if (r.getDataType().isNumeric())
|
||||
if (((Field) l).getDataType().getFromType() == JSON.class)
|
||||
l = jsonGetElementAsText((Field) l, r);
|
||||
if (f.getDataType().getFromType() == JSON.class)
|
||||
l = f = jsonGetElementAsText(f, r);
|
||||
else
|
||||
l = jsonbGetElementAsText((Field) l, r);
|
||||
l = f = jsonbGetElementAsText(f, r);
|
||||
else
|
||||
if (((Field) l).getDataType().getFromType() == JSON.class)
|
||||
l = jsonGetAttributeAsText((Field) l, r);
|
||||
if (f.getDataType().getFromType() == JSON.class)
|
||||
l = f = jsonGetAttributeAsText(f, r);
|
||||
else
|
||||
l = jsonbGetAttributeAsText((Field) l, r);
|
||||
l = f = jsonbGetAttributeAsText(f, r);
|
||||
}
|
||||
else if (parseIf("->")) {
|
||||
Field r = (Field) parseSum();
|
||||
Field r = toField(parseSum());
|
||||
|
||||
// [#10018] We cannot really know reliably whether this is a
|
||||
// index or attribute access. Let's default to the
|
||||
// more popular attribute access for now. Also,
|
||||
// JSONB is likely more popular than JSON.
|
||||
if (r.getDataType().isNumeric())
|
||||
if (((Field) l).getDataType().getFromType() == JSON.class)
|
||||
l = jsonGetElement((Field) l, r);
|
||||
if (f.getDataType().getFromType() == JSON.class)
|
||||
l = f = jsonGetElement(f, r);
|
||||
else
|
||||
l = jsonbGetElement((Field) l, r);
|
||||
l = f = jsonbGetElement(f, r);
|
||||
else
|
||||
if (((Field) l).getDataType().getFromType() == JSON.class)
|
||||
l = jsonGetAttribute((Field) l, r);
|
||||
if (f.getDataType().getFromType() == JSON.class)
|
||||
l = f = jsonGetAttribute(f, r);
|
||||
else
|
||||
l = jsonbGetAttribute((Field) l, r);
|
||||
l = f = jsonbGetAttribute(f, r);
|
||||
}
|
||||
else if (parseIf("??") || parseIf("?"))
|
||||
if (((Field) l).getDataType().getFromType() == JSON.class)
|
||||
return jsonKeyExists((Field) l, (Field) parseSum());
|
||||
if (f.getDataType().getFromType() == JSON.class)
|
||||
return jsonKeyExists(f, (Field) toField(parseSum()));
|
||||
else
|
||||
return jsonbKeyExists((Field) l, (Field) parseSum());
|
||||
return jsonbKeyExists(f, (Field) toField(parseSum()));
|
||||
else
|
||||
break;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
private final FieldOrRow parseSum() {
|
||||
FieldOrRow r = parseFactor();
|
||||
private final FieldOrRowOrSelect parseSum() {
|
||||
FieldOrRowOrSelect r = parseFactor();
|
||||
|
||||
if (r instanceof Field)
|
||||
Field f = toField(r, false);
|
||||
if (f != null)
|
||||
for (;;)
|
||||
if (parseIf('+'))
|
||||
r = parseSumRightOperand(r, true);
|
||||
r = f = parseSumRightOperand(f, true);
|
||||
else if (!peek("->") && parseIf('-'))
|
||||
r = parseSumRightOperand(r, false);
|
||||
r = f = parseSumRightOperand(f, false);
|
||||
else
|
||||
break;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
private final Field parseSumRightOperand(FieldOrRow r, boolean add) {
|
||||
Field rhs = (Field) parseFactor();
|
||||
private final Field parseSumRightOperand(FieldOrRowOrSelect r, boolean add) {
|
||||
Field rhs = toField(parseFactor());
|
||||
DatePart part;
|
||||
|
||||
if (parseProKeywordIf("YEAR", "YEARS"))
|
||||
@ -8695,7 +8748,7 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
else
|
||||
part = null;
|
||||
|
||||
Field lhs = (Field) r;
|
||||
Field lhs = toField(r);
|
||||
|
||||
|
||||
|
||||
@ -8720,17 +8773,18 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
return lhs.sub(rhs);
|
||||
}
|
||||
|
||||
private final FieldOrRow parseFactor() {
|
||||
FieldOrRow r = parseExp();
|
||||
private final FieldOrRowOrSelect parseFactor() {
|
||||
FieldOrRowOrSelect r = parseExp();
|
||||
|
||||
if (r instanceof Field)
|
||||
Field f = toField(r, false);
|
||||
if (f != null)
|
||||
for (;;)
|
||||
if (!peek("*=") && parseIf('*'))
|
||||
r = ((Field) r).mul((Field) parseExp());
|
||||
r = f = f.mul(toField(parseExp()));
|
||||
else if (parseIf('/'))
|
||||
r = ((Field) r).div((Field) parseExp());
|
||||
r = f = f.div(toField(parseExp()));
|
||||
else if (parseIf('%'))
|
||||
r = ((Field) r).mod((Field) parseExp());
|
||||
r = f = f.mod(toField(parseExp()));
|
||||
|
||||
|
||||
|
||||
@ -8743,20 +8797,21 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
return r;
|
||||
}
|
||||
|
||||
private final FieldOrRow parseExp() {
|
||||
FieldOrRow r = parseUnaryOps();
|
||||
private final FieldOrRowOrSelect parseExp() {
|
||||
FieldOrRowOrSelect r = parseUnaryOps();
|
||||
|
||||
if (r instanceof Field)
|
||||
Field f = toField(r, false);
|
||||
if (f != null)
|
||||
for (;;)
|
||||
if (!peek("^=") && parseIf('^') || parseIf("**"))
|
||||
r = ((Field) r).pow(toField(parseUnaryOps()));
|
||||
r = f = f.pow(toField(parseUnaryOps()));
|
||||
else
|
||||
break;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
private final FieldOrRow parseUnaryOps() {
|
||||
private final FieldOrRowOrSelect parseUnaryOps() {
|
||||
if (parseProKeywordIf("CONNECT_BY_ROOT")) {
|
||||
|
||||
|
||||
@ -8766,7 +8821,7 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
if (parseIf('~'))
|
||||
return toField(parseUnaryOps()).bitNot();
|
||||
|
||||
FieldOrRow r;
|
||||
FieldOrRowOrSelect r;
|
||||
Sign sign = parseSign();
|
||||
|
||||
if (sign == Sign.NONE)
|
||||
@ -8809,7 +8864,7 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
return r;
|
||||
}
|
||||
|
||||
private final FieldOrRow parseMethodCallIf(FieldOrRow r) {
|
||||
private final FieldOrRowOrSelect parseMethodCallIf(FieldOrRowOrSelect r) {
|
||||
|
||||
|
||||
|
||||
@ -8821,7 +8876,8 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
return r;
|
||||
}
|
||||
|
||||
private final FieldOrRow parseMethodCallIf0(FieldOrRow r) {
|
||||
private final FieldOrRowOrSelect parseMethodCallIf0(FieldOrRowOrSelect r) {
|
||||
|
||||
|
||||
|
||||
|
||||
@ -8979,8 +9035,8 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
}
|
||||
}
|
||||
|
||||
private final FieldOrRow parseTerm() {
|
||||
FieldOrRow field;
|
||||
private final FieldOrRowOrSelect parseTerm() {
|
||||
FieldOrRowOrSelect field;
|
||||
Object value;
|
||||
|
||||
|
||||
@ -10152,12 +10208,15 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
if (!forbidden.isEmpty())
|
||||
forbidden = EnumSet.noneOf(FunctionKeyword.class);
|
||||
|
||||
FieldOrRow r = parseScalarSubqueryIf();
|
||||
FieldOrRowOrSelect r = parseSubqueryIf();
|
||||
if (r != null)
|
||||
return r;
|
||||
|
||||
parse('(');
|
||||
r = parseFieldOrRow();
|
||||
|
||||
// [#18548] This cast is safe in 3.20. In 3.21, it is unnecessary because
|
||||
// FieldOrRow <: FieldOrRowOrSelect
|
||||
r = (FieldOrRowOrSelect) parseFieldOrRow();
|
||||
List<Field<?>> list = null;
|
||||
|
||||
if (r instanceof Field<?> f) {
|
||||
@ -15950,6 +16009,10 @@ final class DefaultParseContext extends AbstractParseContext implements ParseCon
|
||||
return notImplemented(feature, "https://github.com/jOOQ/jOOQ/issues/16487");
|
||||
}
|
||||
|
||||
private final ParserException notImplementedNonScalarSelectPredicate() {
|
||||
return notImplemented("Non-scalar SELECT predicate", "https://github.com/jOOQ/jOOQ/issues/10176");
|
||||
}
|
||||
|
||||
private final ParserException notImplemented(String feature, String link) {
|
||||
return init(new ParserException(mark(), feature + " not yet implemented. If you're interested in this feature, please comment on " + link));
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user