Skip to content

Commit f7f8d03

Browse files
committed
Added OracleHint class, grammar and model support, tests
1 parent b6f71e6 commit f7f8d03

File tree

9 files changed

+216
-0
lines changed

9 files changed

+216
-0
lines changed

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,7 @@ public interface ExpressionVisitor {
162162
void visit(MySQLGroupConcat groupConcat);
163163

164164
void visit(RowConstructor rowConstructor);
165+
166+
void visit(OracleHint hint);
167+
165168
}

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,4 +452,10 @@ public void visit(RowConstructor rowConstructor) {
452452
public void visit(HexValue hexValue) {
453453

454454
}
455+
456+
@Override
457+
public void visit(OracleHint hint) {
458+
459+
}
460+
455461
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2013 JSQLParser
6+
* %%
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Lesser General Public License as
9+
* published by the Free Software Foundation, either version 2.1 of the
10+
* License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Lesser Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Lesser Public
18+
* License along with this program. If not, see
19+
* <http://www.gnu.org/licenses/lgpl-2.1.html>.
20+
* #L%
21+
*/
22+
package net.sf.jsqlparser.expression;
23+
24+
import java.util.regex.Matcher;
25+
import java.util.regex.Pattern;
26+
27+
/**
28+
* Oracle Hint Expression
29+
* @author valdo
30+
*/
31+
public class OracleHint implements Expression {
32+
33+
private static final Pattern SINGLE_LINE = Pattern.compile("--\\+ *([^ ].*[^ ])");
34+
private static final Pattern MULTI_LINE = Pattern.compile("\\/\\*\\+ *([^ ].*[^ ]) *\\*+\\/", Pattern.MULTILINE | Pattern.DOTALL);
35+
36+
public static boolean isHintMatch(String comment) {
37+
return SINGLE_LINE.matcher(comment).find() ||
38+
MULTI_LINE.matcher(comment).find();
39+
}
40+
41+
private String value;
42+
private boolean singleLine = false;
43+
44+
public final void setComment(String comment) {
45+
Matcher m;
46+
{
47+
m = SINGLE_LINE.matcher(comment);
48+
if (m.find()) {
49+
this.value = m.group(1);
50+
this.singleLine = true;
51+
return;
52+
}
53+
}
54+
{
55+
m = MULTI_LINE.matcher(comment);
56+
if (m.find()) {
57+
this.value = m.group(1);
58+
this.singleLine = false;
59+
}
60+
}
61+
}
62+
63+
public String getValue() {
64+
return value;
65+
}
66+
67+
public void setValue(String value) {
68+
this.value = value;
69+
}
70+
71+
public boolean isSingleLine() {
72+
return singleLine;
73+
}
74+
75+
public void setSingleLine(boolean singleLine) {
76+
this.singleLine = singleLine;
77+
}
78+
79+
@Override
80+
public void accept(ExpressionVisitor visitor) {
81+
visitor.visit(this);
82+
}
83+
84+
@Override
85+
public String toString() {
86+
if (singleLine) {
87+
return "--+ " + value + "\n";
88+
} else {
89+
return "/*+ " + value + " */";
90+
}
91+
}
92+
93+
}

src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Iterator;
3030
import java.util.List;
3131
import net.sf.jsqlparser.expression.OracleHierarchicalExpression;
32+
import net.sf.jsqlparser.expression.OracleHint;
3233

3334
/**
3435
* The core of a "SELECT" statement (no UNION, no ORDER BY)
@@ -51,6 +52,7 @@ public class PlainSelect implements SelectBody {
5152
private First first;
5253
private Top top;
5354
private OracleHierarchicalExpression oracleHierarchical = null;
55+
private OracleHint oracleHint = null;
5456
private boolean oracleSiblings = false;
5557
private boolean forUpdate = false;
5658
private Table forUpdateTable = null;
@@ -257,6 +259,14 @@ public void setForUpdateTable(Table forUpdateTable) {
257259
this.forUpdateTable = forUpdateTable;
258260
}
259261

262+
public OracleHint getOracleHint() {
263+
return oracleHint;
264+
}
265+
266+
public void setOracleHint(OracleHint oracleHint) {
267+
this.oracleHint = oracleHint;
268+
}
269+
260270
@Override
261271
public String toString() {
262272
StringBuilder sql = new StringBuilder();
@@ -265,6 +275,10 @@ public String toString() {
265275
}
266276
sql.append("SELECT ");
267277

278+
if (oracleHint != null) {
279+
sql.append(oracleHint).append(" ");
280+
}
281+
268282
if (skip != null) {
269283
sql.append(skip).append(" ");
270284
}

src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,4 +633,9 @@ public void visit(HexValue hexValue) {
633633
public void visit(Merge merge) {
634634
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
635635
}
636+
637+
@Override
638+
public void visit(OracleHint hint) {
639+
}
640+
636641
}

src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,4 +569,10 @@ public void visit(RowConstructor rowConstructor) {
569569
}
570570
buffer.append(")");
571571
}
572+
573+
@Override
574+
public void visit(OracleHint hint) {
575+
buffer.append(hint.toString());
576+
}
577+
572578
}

src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public void visit(PlainSelect plainSelect) {
5757
}
5858
buffer.append("SELECT ");
5959

60+
OracleHint hint = plainSelect.getOracleHint();
61+
if (hint != null) {
62+
buffer.append(hint).append(" ");
63+
}
64+
6065
Skip skip = plainSelect.getSkip();
6166
if (skip != null) {
6267
buffer.append(skip).append(" ");

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,8 @@ PlainSelect PlainSelect():
722722
{
723723
<K_SELECT>
724724

725+
{ plainSelect.setOracleHint(getOracleHint()); }
726+
725727
[skip = Skip() { plainSelect.setSkip(skip); } ]
726728

727729
[LOOKAHEAD(2) first = First() { plainSelect.setFirst(first); } ]
@@ -1397,6 +1399,23 @@ Skip Skip():
13971399
}
13981400
}
13991401

1402+
JAVACODE
1403+
OracleHint getOracleHint() {
1404+
OracleHint hint = null;
1405+
Token tok = getToken(1);
1406+
// Retrieve first comment (if any) prior next token
1407+
if (tok.specialToken != null) {
1408+
tok = tok.specialToken;
1409+
while (tok.specialToken != null) tok = tok.specialToken;
1410+
// Check if it matches Hint pattern?
1411+
if (OracleHint.isHintMatch(tok.image)) {
1412+
hint = new OracleHint();
1413+
hint.setComment(tok.image);
1414+
}
1415+
}
1416+
return hint;
1417+
}
1418+
14001419
First First():
14011420
{
14021421
First first = new First();

src/test/java/net/sf/jsqlparser/test/select/SelectTest.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,4 +1958,69 @@ public void testIssue167_singleQuoteEscape2() throws JSQLParserException {
19581958
public void testIssue77_singleQuoteEscape2() throws JSQLParserException {
19591959
assertSqlCanBeParsedAndDeparsed("SELECT 'test\\'' FROM dual");
19601960
}
1961+
1962+
public void testOracleHint() throws JSQLParserException {
1963+
String[][] sqls = new String[][] {
1964+
{"SOMEHINT", "SELECT /*+ SOMEHINT */ * FROM mytable",
1965+
"true"},
1966+
{"MORE HINTS POSSIBLE", "SELECT /*+ MORE HINTS POSSIBLE */ * FROM mytable",
1967+
"true"},
1968+
{"MORE\nHINTS\t\nPOSSIBLE", "SELECT /*+ MORE\nHINTS\t\nPOSSIBLE */ * FROM mytable",
1969+
"true"},
1970+
{"leading(sn di md sh ot) cardinality(ot 1000)", "SELECT /*+ leading(sn di md sh ot) cardinality(ot 1000) */ c, b FROM mytable",
1971+
"true"},
1972+
{"ORDERED INDEX (b, jl_br_balances_n1) USE_NL (j b) \n" +
1973+
" USE_NL (glcc glf) USE_MERGE (gp gsb)",
1974+
"SELECT /*+ ORDERED INDEX (b, jl_br_balances_n1) USE_NL (j b) \n" +
1975+
" USE_NL (glcc glf) USE_MERGE (gp gsb) */\n" +
1976+
" b.application_id ,\n" +
1977+
" b.set_of_books_id ,\n" +
1978+
" b.personnel_id,\n" +
1979+
" p.vendor_id Personnel,\n" +
1980+
" p.segment1 PersonnelNumber,\n" +
1981+
" p.vendor_name Name\n" +
1982+
"FROM jl_br_journals j,\n" +
1983+
" jl_br_balances b,\n" +
1984+
" gl_code_combinations glcc,\n" +
1985+
" fnd_flex_values_vl glf,\n" +
1986+
" gl_periods gp,\n" +
1987+
" gl_sets_of_books gsb,\n" +
1988+
" po_vendors p",
1989+
"true" },
1990+
{"ROWID(emp)", "SELECT /*+ROWID(emp)*/ /*+ THIS IS NOT HINT! ***/ * \n" +
1991+
"FROM emp \n" +
1992+
"WHERE rowid > 'AAAAtkAABAAAFNTAAA' AND empno = 155",
1993+
"false"},
1994+
{"INDEX(patients sex_index) use sex_index because there are few\n" +
1995+
" male patients", "SELECT /*+ INDEX(patients sex_index) use sex_index because there are few\n" +
1996+
" male patients */ name, height, weight\n" +
1997+
"FROM patients\n" +
1998+
"WHERE sex = 'm'",
1999+
"true"},
2000+
{"INDEX_COMBINE(emp sal_bmi hiredate_bmi)", "SELECT /*+INDEX_COMBINE(emp sal_bmi hiredate_bmi)*/ * \n" +
2001+
"FROM emp \n" +
2002+
"WHERE sal < 50000 AND hiredate < '01-JAN-1990'",
2003+
"true"},
2004+
{"CLUSTER", "SELECT --+ CLUSTER \n" +
2005+
"emp.ename, deptno\n" +
2006+
"FROM emp, dept\n" +
2007+
"WHERE deptno = 10 \n" +
2008+
"AND emp.deptno = dept.deptno",
2009+
"true"},
2010+
{"CLUSTER", "SELECT --+ CLUSTER \n --+ some other comment, not hint\n /* even more comments */ * from dual",
2011+
"false"}
2012+
};
2013+
2014+
for (String[] sql: sqls) {
2015+
if (Boolean.parseBoolean(sql[2])) {
2016+
assertSqlCanBeParsedAndDeparsed(sql[1], true);
2017+
}
2018+
Select select = (Select) CCJSqlParserUtil.parse(sql[1]);
2019+
PlainSelect pselect = (PlainSelect) select.getSelectBody();
2020+
OracleHint hint = pselect.getOracleHint();
2021+
assertNotNull(hint);
2022+
assertEquals(sql[0], hint.getValue());
2023+
}
2024+
}
2025+
19612026
}

0 commit comments

Comments
 (0)