Skip to content

Commit

Permalink
Allow escaping of '?' using '??' in SQL expression (#1935)
Browse files Browse the repository at this point in the history
Allow escaping of '?' using '??' in SQL expression and testEscapedQuestionMarkInSQLOperator to test ?? in SQL operator
  • Loading branch information
pvarga88 authored Nov 15, 2023
1 parent ed0e575 commit 63b0fb0
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -1981,6 +1981,11 @@ public Expression sql(String sql, List arguments) {
int start = 0;
int index = sql.indexOf('?');
while (index != -1) {
// ? can be escaped as ?? to support ? as a native SQL operator (e.g. on Postgres)
if (index < sql.length() - 1 && sql.charAt(index + 1) == '?') {
index = sql.indexOf('?', index + 2);
continue;
}
v.add(sql.substring(start, index));
start = index + 1;
index = sql.indexOf('?', start);
Expand All @@ -1989,6 +1994,14 @@ public Expression sql(String sql, List arguments) {
v.add(sql.substring(start, sql.length()));
}
anOperator.printsAs(v);
//Postgres expects '??' as an escape mechanism for '?' in parameterized queries
//https://jdbc.postgresql.org/documentation/query/#using-the-statement-or-preparedstatement-interface
//On other platforms, replace ?? with ? in code which is passed as a part of SQL into DB
if (getSession() == null || !getSession().getPlatform().isPostgreSQL()) {
for (int i = 0; i < anOperator.getDatabaseStrings().length; i++) {
anOperator.getDatabaseStrings()[i] = anOperator.getDatabaseStrings()[i].replace("??", "?");
}
}
anOperator.bePrefix();
anOperator.setNodeClass(ClassConstants.FunctionExpression_Class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,8 @@ public void visit(FunctionExpression expression) {
}

queryExpression = queryExpressions.remove(0);
// ensure the session is set for the 'SQL' operator
queryExpression.getBuilder().setSession(queryContext.getSession());

// SQL
if (identifier == org.eclipse.persistence.jpa.jpql.parser.Expression.SQL) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -30,6 +30,7 @@
import org.eclipse.persistence.jpa.test.framework.Emf;
import org.eclipse.persistence.jpa.test.framework.EmfRunner;
import org.eclipse.persistence.jpa.test.framework.Property;
import org.eclipse.persistence.sessions.Session;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -254,4 +255,32 @@ public void testSelectJsonInWhereCondition() {
}
}

@Test
public void testEscapedQuestionMarkInSQLOperator() {
EntityManager em = emf.createEntityManager();

if (emf.unwrap(Session.class).getPlatform().isOracle()) {
JsonValue value = Json.createObjectBuilder()
.add("id", "1007")
.build();
try {
em.getTransaction().begin();
JsonEntity e = new JsonEntity(1007, value);
em.persist(e);
em.flush();
em.getTransaction().commit();
em.clear();

JsonEntity dbValue = em.createQuery(
"SELECT v FROM JsonEntity v WHERE SQL('JSON_EXISTS(?, ''$??(@.id == 1007)'')', v.value)", JsonEntity.class)
.getSingleResult();
Assert.assertEquals(value, dbValue.getValue());
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
}
}

0 comments on commit 63b0fb0

Please sign in to comment.