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
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.IncludesExpression;
import net.sf.jsqlparser.expression.operators.relational.Intersects;
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
Expand Down Expand Up @@ -95,7 +96,7 @@
}

default <S> T visitLimit(Limit limit, S context) {
if (limit != null && !limit.isLimitNull() && !limit.isLimitAll()) {

Check warning on line 99 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java

View workflow job for this annotation

GitHub Actions / Maven Verify (ubuntu-latest)

[deprecation] isLimitAll() in Limit has been deprecated

Check warning on line 99 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java

View workflow job for this annotation

GitHub Actions / Maven Verify (ubuntu-latest)

[deprecation] isLimitNull() in Limit has been deprecated

Check warning on line 99 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java

View workflow job for this annotation

GitHub Actions / Maven Verify (macos-latest)

[deprecation] isLimitAll() in Limit has been deprecated

Check warning on line 99 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java

View workflow job for this annotation

GitHub Actions / Maven Verify (macos-latest)

[deprecation] isLimitNull() in Limit has been deprecated
if (limit.getOffset() != null) {
limit.getOffset().accept(this, context);
}
Expand Down Expand Up @@ -725,6 +726,12 @@
this.visit(geometryDistance, null);
}

<S> T visit(Intersects intersects, S context);

default void visit(Intersects intersects) {
this.visit(intersects, null);
}

<S> T visit(Select select, S context);

<S> T visit(TranscodingFunction transcodingFunction, S context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.IncludesExpression;
import net.sf.jsqlparser.expression.operators.relational.Intersects;
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
Expand Down Expand Up @@ -776,12 +777,12 @@

@Override
public <S> T visit(ConnectByRootOperator connectByRootOperator, S context) {
return connectByRootOperator.getColumn().accept(this, context);

Check warning on line 780 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (ubuntu-latest)

[deprecation] getColumn() in ConnectByRootOperator has been deprecated

Check warning on line 780 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (macos-latest)

[deprecation] getColumn() in ConnectByRootOperator has been deprecated
}

@Override
public <S> T visit(ConnectByPriorOperator connectByPriorOperator, S context) {
return connectByPriorOperator.getColumn().accept(this, context);

Check warning on line 785 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (ubuntu-latest)

[deprecation] getColumn() in ConnectByPriorOperator has been deprecated

Check warning on line 785 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (macos-latest)

[deprecation] getColumn() in ConnectByPriorOperator has been deprecated
}

@Override
Expand All @@ -804,12 +805,17 @@
return visitBinaryExpression(geometryDistance, context);
}

@Override
public <S> T visit(Intersects intersects, S context) {
return visitBinaryExpression(intersects, context);
}

@Override
public <S> T visit(Select select, S context) {
if (selectVisitor != null) {
if (select.getWithItemsList() != null) {
for (WithItem<?> item : select.getWithItemsList()) {
item.accept(selectVisitor, context);

Check warning on line 818 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (ubuntu-latest)

[deprecation] <T,S>accept(SelectVisitor<T>,S) in WithItem has been deprecated

Check warning on line 818 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (macos-latest)

[deprecation] <T,S>accept(SelectVisitor<T>,S) in WithItem has been deprecated
}
}
select.accept(selectVisitor, context);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*-
* #%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.expression.operators.relational;

import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;

/**
* The PostgreSQL <code>#</code> binary operator: the geometric intersection of lseg / line / box
* (documentation Table 9.36) and the integer bitwise exclusive OR (Table 9.4).
*/
public class Intersects extends BinaryExpression {

@Override
public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
return expressionVisitor.visit(this, context);
}

@Override
public String getStringExpression() {
return "#";
}

@Override
public Intersects withLeftExpression(Expression expression) {
return (Intersects) super.withLeftExpression(expression);
}

@Override
public Intersects withRightExpression(Expression expression) {
return (Intersects) super.withRightExpression(expression);
}
}
7 changes: 7 additions & 0 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.IncludesExpression;
import net.sf.jsqlparser.expression.operators.relational.Intersects;
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
Expand Down Expand Up @@ -1127,6 +1128,12 @@ public <S> Void visit(JsonOperator jsonExpr, S context) {
return null;
}

@Override
public <S> Void visit(Intersects intersects, S context) {
visitBinaryExpression(intersects);
return null;
}

@Override
public <S> Void visit(AllColumns allColumns, S context) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.IncludesExpression;
import net.sf.jsqlparser.expression.operators.relational.Intersects;
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
Expand Down Expand Up @@ -1773,6 +1774,12 @@ public <S> StringBuilder visit(GeometryDistance geometryDistance, S context) {
return builder;
}

@Override
public <S> StringBuilder visit(Intersects intersects, S context) {
deparse(intersects, " # ", null);
return builder;
}

@Override
public <S> StringBuilder visit(TSQLLeftJoin tsqlLeftJoin, S context) {
this.deparse(tsqlLeftJoin, " *= ", null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.IncludesExpression;
import net.sf.jsqlparser.expression.operators.relational.Intersects;
import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression;
import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
Expand Down Expand Up @@ -815,6 +816,12 @@ public <S> Void visit(JsonOperator jsonExpr, S context) {
return null;
}

@Override
public <S> Void visit(Intersects intersects, S context) {
visitBinaryExpression(intersects, " # ");
return null;
}

@Override
public <S> Void visit(UserVariable var, S context) {
// nothing to validate
Expand Down Expand Up @@ -916,6 +923,10 @@ public void visit(JsonOperator jsonExpr) {
visit(jsonExpr, null);
}

public void visit(Intersects intersects) {
visit(intersects, null);
}

public void visit(UserVariable var) {
visit(var, null);
}
Expand Down
11 changes: 10 additions & 1 deletion src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -487,9 +487,11 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
int prec;

// Named tokens: OP_SLASH(/), OP_CARET(^), K_DIV, OP_CONCAT(||),
// OP_PIPE(|), OP_LSHIFT(<<), OP_RSHIFT(>>)
// OP_PIPE(|), OP_LSHIFT(<<), OP_RSHIFT(>>),
// S_HASH_OPERATOR(#)
// String-literal tokens: *, +, -, %, & (unnamed in JavaCC grammar)
if (op == OP_SLASH || op == OP_CARET || op == K_DIV) prec = 6;
else if (op == S_HASH_OPERATOR) prec = 5;
else if (op == OP_CONCAT || op == OP_PIPE
|| op == OP_LSHIFT || op == OP_RSHIFT) prec = 5;
else {
Expand All @@ -515,6 +517,7 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
Expression right = prattArithRest(PrimaryExpression(), prec + 1);

if (op == OP_SLASH) { Division r = new Division(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
else if (op == S_HASH_OPERATOR) { Intersects r = new Intersects(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
else if (op == OP_CARET) { net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor r = new net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
else if (op == K_DIV) { IntegerDivision r = new IntegerDivision(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
else if (op == OP_CONCAT) { Concat r = new Concat(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
Expand Down Expand Up @@ -1930,6 +1933,12 @@ TOKEN:
matchedToken.kind = charLiteralIndex;
}
|
// Bare `#` as a binary operator (PostgreSQL bitwise XOR / geometric
// intersection). Declared before <S_IDENTIFIER> to win the length tie on a
// lone `#`; `#temp`-style identifiers and `#>` / `#>>` keep their usual
// lexing by longest match (#1197, #1695).
<S_HASH_OPERATOR: "#">
|
<S_IDENTIFIER: (<LETTER> (<PART_LETTER>)*) | "$" | ("$" <PART_LETTER_NO_DOLLAR> (<PART_LETTER>)*)>
| <#LETTER: <UnicodeIdentifierStart>
| <Nd> | [ "#", "_" ] // Not SQL:2016 compliant!
Expand Down
41 changes: 41 additions & 0 deletions src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.JsonExpression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.Intersects;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
Expand Down Expand Up @@ -138,4 +139,44 @@ void testQuotedIdentifier() throws JSQLParserException {
Assertions.assertEquals("`This is a Test Table`", table.getName());

}

@Test
public void testPostgresHashBinaryOperator() throws JSQLParserException {
// PostgreSQL 18, Table 9.4 (Mathematical Operators):
// integral_type # integral_type -> bitwise exclusive OR
assertSqlCanBeParsedAndDeparsed("SELECT 17 # 5");
// PostgreSQL 18, Table 9.36 (Geometric Operators):
// geometric_type # geometric_type -> point of intersection
Select select = (Select) assertSqlCanBeParsedAndDeparsed("SELECT lseg1 # lseg2");
Intersects intersects = Assertions.assertInstanceOf(Intersects.class,
select.getPlainSelect().getSelectItem(0).getExpression());
Assertions.assertEquals("#", intersects.getStringExpression());
Assertions.assertInstanceOf(Column.class, intersects.getLeftExpression());
Assertions.assertInstanceOf(Column.class, intersects.getRightExpression());

assertSqlCanBeParsedAndDeparsed("SELECT a # (b + 1) FROM t");
assertSqlCanBeParsedAndDeparsed("SELECT a # b AS x FROM t");
// same tier as `&`: binds tighter than the comparison that follows
assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE a # b = 1");
}

@Test
public void testPostgresHashOperatorKeepsIdentifiersAndJsonOperators()
throws JSQLParserException {
// `#` stays an identifier character: SQL Server `#temp`, `##global`
// (#1197), Oracle `#$tab1#` and unquoted names containing `#`
assertSqlCanBeParsedAndDeparsed("SELECT #temp FROM t");
assertSqlCanBeParsedAndDeparsed("SELECT ##global FROM t");
assertSqlCanBeParsedAndDeparsed("SELECT #$tab1# FROM t");
assertSqlCanBeParsedAndDeparsed("SELECT a#b FROM t");
// `#>` / `#>>` keep their JSON lexing (#1695)
assertSqlCanBeParsedAndDeparsed("SELECT data #> '{a,b}' FROM t");
assertSqlCanBeParsedAndDeparsed("SELECT data #>> '{0,1}' FROM t");
// a lone `#` can no longer be an identifier (alias, column or table
// name), all such forms fail loudly now that `#` is an operator
Assertions.assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse("SELECT 1 #"));
Assertions.assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse("SELECT # FROM t"));
}
}
Loading