Skip to content

Commit 77a84ba

Browse files
experimental intergration of SQL Developer's PL/SQL parser
It does not work from a 3rt party extension since Oracle does not expose this feature via their OSGi bundle. It's possible to include the relevant JAR file directly, but this would violate the license agreement. Hence, the utPLSQL's parser is still used.
1 parent 763563f commit 77a84ba

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* Copyright 2018 Philipp Salvisberg <philipp.salvisberg@trivadis.com>
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
package org.utplsql.sqldev.parser
16+
17+
import oracle.dbtools.parser.LexerToken
18+
import oracle.dbtools.raptor.navigator.plsql.PlSqlArguments
19+
import oracle.dbtools.raptor.navigator.plsql.PlsqlStructureParser
20+
21+
/*
22+
* Cannot use this class within SQL Developer because the
23+
* package oracle.dbtools.parser is not exported in sqldeveloper OSGI bundle (extension)
24+
* (throws ClassNotFoundException at runtime).
25+
*
26+
* The dbtools-common.jar contains the necessary packages,
27+
* but it cannot be distributed with the utPLSQL extension
28+
* without violating the Oracle license agreement.
29+
*/
30+
class SqlDevParser {
31+
def getMembers(String plsql) {
32+
val tokens = LexerToken.parse(plsql)
33+
val parser = new PlsqlStructureParser
34+
parser.parse(tokens, PlSqlArguments.sort)
35+
return parser.children
36+
}
37+
38+
private def getStartLine(String plsql, int offset) {
39+
var int line = 1
40+
for (var i = 0; i < plsql.length; i++) {
41+
val c = plsql.substring(i, i+1)
42+
if (i > offset) {
43+
return line
44+
} else if (c == '\n') {
45+
line = line + 1
46+
}
47+
}
48+
return line
49+
}
50+
51+
def getMemberStartLine(String plsql, String memberName) {
52+
val members = plsql.members
53+
val member = members.findFirst[it.name.equalsIgnoreCase(memberName)]
54+
if (member !== null) {
55+
return getStartLine(plsql, member.codeOffset)
56+
} else {
57+
1
58+
}
59+
}
60+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package org.utplsql.sqldev.test.parser
2+
3+
import org.junit.Assert
4+
import org.junit.Test
5+
import org.utplsql.sqldev.parser.SqlDevParser
6+
7+
class SqlDevParserTest {
8+
val packageSpec = '''
9+
CREATE OR REPLACE PACKAGE junit_utplsql_test1_pkg is
10+
--%suite(JUnit testing)
11+
--%suitepath(a)
12+
13+
--%context(test context)
14+
15+
--%test(test 1 - OK)
16+
PRoCeDURE test_1_ok;
17+
18+
--%test(test 2 - NOK)
19+
PROCEDURE test_2_nok;
20+
21+
--%test(test 3 - disabled)
22+
--%disabled
23+
PROCEDURE test_3_disabled;
24+
25+
--%test(test 4 - errored)
26+
PROCEDURE test_4_errored;
27+
28+
--%test(test 5 - warnings)
29+
PROCEDURE test_5_warnings;
30+
--%endcontext
31+
32+
function my_Func (p IN number) RETURN BOOLEAN;
33+
END;
34+
'''
35+
36+
val packageBody = '''
37+
CREATE OR REPLACE PACKAGE BODY junit_utplsql_test1_pkg IS
38+
PROCEDURE test_1_ok IS
39+
BEGIN
40+
dbms_output.put_line('start test 1');
41+
dbms_session.sleep(1);
42+
ut.expect(1).to_equal(1);
43+
dbms_output.put_line('end test 1');
44+
END;
45+
46+
PROCEDURE test_2_nok IS
47+
BEGIN
48+
dbms_output.put_line('start test 2');
49+
dbms_session.sleep(2);
50+
ut.expect(1, 'first assert.').to_equal(2);
51+
ut.expect(1, 'second assert.').to_equal(2);
52+
dbms_output.put_line('end test 2');
53+
END;
54+
55+
PROCEDURE test_3_disabled IS
56+
BEGIN
57+
NULL;
58+
END;
59+
60+
PROCEDURE test_4_errored IS
61+
BEGIN
62+
EXECUTE IMMEDIATE 'bla bla';
63+
END;
64+
65+
PROCEDURE test_5_warnings IS
66+
BEGIN
67+
COMMIT; -- will raise a warning
68+
ut.expect(1).to_equal(1);
69+
END;
70+
71+
FUNCTION my_Func (p IN number) RETURN BOOLEAN IS
72+
RETURN TRUE;
73+
END;
74+
END;
75+
'''
76+
77+
@Test
78+
def void packageSpecMembers() {
79+
val parser = new SqlDevParser
80+
val actual = parser.getMembers(packageSpec)
81+
Assert.assertEquals(6, actual.length)
82+
val first = actual.get(0)
83+
Assert.assertEquals("PROCEDURE", first.type)
84+
Assert.assertEquals("test_1_ok", first.name)
85+
val last = actual.get(5)
86+
Assert.assertEquals("FUNCTION", last.type)
87+
Assert.assertEquals("my_Func", last.name)
88+
}
89+
90+
@Test
91+
def void packageBodyMembers() {
92+
val parser = new SqlDevParser
93+
val actual = parser.getMembers(packageBody)
94+
Assert.assertEquals(6, actual.length)
95+
val first = actual.get(0)
96+
Assert.assertEquals("PROCEDURE", first.type)
97+
Assert.assertEquals("test_1_ok", first.name)
98+
val last = actual.get(5)
99+
Assert.assertEquals("FUNCTION", last.type)
100+
Assert.assertEquals("my_Func", last.name)
101+
}
102+
103+
@Test
104+
def void StartLineSpec() {
105+
val parser = new SqlDevParser
106+
val first = parser.getMemberStartLine(packageSpec, 'test_1_ok')
107+
Assert.assertEquals(8, first)
108+
val last = parser.getMemberStartLine(packageSpec, 'my_func')
109+
Assert.assertEquals(24, last)
110+
}
111+
112+
@Test
113+
def void StartLineBody() {
114+
val parser = new SqlDevParser
115+
val first = parser.getMemberStartLine(packageBody, 'test_1_ok')
116+
Assert.assertEquals(2, first)
117+
val last = parser.getMemberStartLine(packageBody, 'my_func')
118+
Assert.assertEquals(35, last)
119+
}
120+
121+
122+
123+
}

0 commit comments

Comments
 (0)