Skip to content

Commit fd92352

Browse files
Fixes #1 and fixes #7
1 parent 2ffa921 commit fd92352

File tree

2 files changed

+126
-14
lines changed

2 files changed

+126
-14
lines changed

sqldev/src/main/java/org/utplsql/sqldev/parser/UtplsqlParser.xtend

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,45 @@ import org.utplsql.sqldev.model.parser.Unit
2222

2323
class UtplsqlParser {
2424
private String plsql
25-
private String plsqlWithoutComments
25+
private String plsqlReduced
2626
private ArrayList<PlsqlObject> objects = new ArrayList<PlsqlObject>
2727
private ArrayList<Unit> units = new ArrayList<Unit>
2828

2929
new(String plsql) {
30-
this.plsql = plsql
31-
setPlsqlWithoutComments
30+
setPlsql(plsql)
31+
setPlsqlReduced
3232
populateObjects
3333
populateUnits
3434
}
3535

3636
/**
37-
* replace multi-line and single-line PL/SQL comments with space
38-
* to simplify and improve performance of subsequent regex expressions
37+
* JTextComponents uses one position for EOL (end-of-line),
38+
* even on Windows platforms were it is two characters (CR/LF).
39+
* To simplify position calculations and subsequent regular expressions
40+
* all new lines are replaced with LF on Windows platforms.
3941
*/
40-
private def setPlsqlWithoutComments() {
42+
private def setPlsql(String plsql) {
43+
val lineSep = System.getProperty("line.separator")
44+
if (lineSep.length > 0) {
45+
// replace CR/LF with LF on Windows platforms
46+
this.plsql = plsql.replace(lineSep, "\n")
47+
} else {
48+
this.plsql = plsql
49+
}
50+
}
51+
52+
/**
53+
* replace the following expressions with space to simplify
54+
* and improve performance of subsequent regular expressions:
55+
* - multi-line PL/SQL comments
56+
* - single-line PL/SQL comments
57+
* - string literals
58+
* the result is not valid PL/SQL anymore, but good enough
59+
* to find PL/SQL objects and units
60+
*/
61+
private def setPlsqlReduced() {
4162
val sb = new StringBuffer
42-
val p = Pattern.compile("(/\\*(.|[\\r\\n])*?\\*/)|(--.*\\r?\\n)")
63+
val p = Pattern.compile("(/\\*(.|[\\n])*?\\*/)|(--[^\\n]*\\n)|('([^']|[\\n])*?')")
4364
val m = p.matcher(plsql)
4465
var pos = 0
4566
while (m.find) {
@@ -59,12 +80,12 @@ class UtplsqlParser {
5980
if (plsql.length > pos) {
6081
sb.append(plsql.substring(pos, plsql.length))
6182
}
62-
plsqlWithoutComments=sb.toString
83+
plsqlReduced=sb.toString
6384
}
6485

6586
private def populateObjects() {
66-
val p = Pattern.compile("(?i)(\\s*)(create(\\s+or\\s+replace)?\\s+(package|type)\\s+(body\\s+)?)(.+?)(\\s+)")
67-
val m = p.matcher(plsqlWithoutComments)
87+
val p = Pattern.compile("(?i)(\\s*)(create(\\s+or\\s+replace)?\\s+(package|type)\\s+(body\\s+)?)([^\\s]+)(\\s+)")
88+
val m = p.matcher(plsqlReduced)
6889
while (m.find) {
6990
val o = new PlsqlObject
7091
o.name = m.group(6)
@@ -73,8 +94,8 @@ class UtplsqlParser {
7394
}
7495
}
7596
private def populateUnits() {
76-
val p = Pattern.compile("(?i)(\\s*)(function|procedure)(\\s+)(.+?)(\\s+)")
77-
val m = p.matcher(plsqlWithoutComments)
97+
val p = Pattern.compile("(?i)(\\s*)(function|procedure)(\\s+)([^\\s\\(;]+)")
98+
val m = p.matcher(plsqlReduced)
7899
while (m.find) {
79100
val u = new Unit
80101
u.name = m.group(4)
@@ -104,7 +125,7 @@ class UtplsqlParser {
104125
}
105126

106127
private def fixName(String name) {
107-
return name.replace("\"", "").replace(";", "")
128+
return name.replace("\"", "")
108129
}
109130

110131
def getObjects() {

sqldev/src/test/java/org/utplsql/sqldev/tests/UtplsqlParserTest.xtend

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,96 @@ class UtplsqlParserTest {
8484
Assert.assertEquals("SCOTT.PKG.P", parser.getPathAt(22,9))
8585
Assert.assertEquals("SCOTT.PKG.F", parser.getPathAt(22,10))
8686
Assert.assertEquals("SCOTT.PKG.F", parser.getPathAt(29,1))
87-
}
87+
}
88+
89+
@Test
90+
def issue_1() {
91+
val plsql = '''
92+
create or replace package body test_expect_not_to_be_null
93+
is
94+
gc_object_name constant varchar2(30) := 't_not_to_be_null_test';
95+
gc_nested_table_name constant varchar2(30) := 'tt_not_to_be_null_test';
96+
gc_varray_name constant varchar2(30) := 'tv_not_to_be_null_test';
97+
98+
procedure cleanup_expectations
99+
is
100+
begin
101+
ut3.ut_expectation_processor.clear_expectations();
102+
end;
103+
104+
procedure create_types
105+
is
106+
pragma autonomous_transaction;
107+
begin
108+
execute immediate 'create type '||gc_object_name||' is object (dummy number)';
109+
execute immediate ' create type '||gc_nested_table_name||' is table of number';
110+
execute immediate '
111+
create type '||gc_varray_name||' is varray(1) of number';
112+
end;
113+
114+
procedure drop_types
115+
is
116+
pragma autonomous_transaction;
117+
begin
118+
execute immediate 'drop type '||gc_object_name;
119+
execute immediate ' drop type '||gc_nested_table_name;
120+
execute immediate '
121+
drop type '||gc_varray_name;
122+
end;
123+
124+
procedure blob_not_null
125+
is
126+
begin
127+
--Act
128+
execute immediate expectations_helpers.unary_expectation_block('not_to_be_null', 'blob', 'to_blob(''abc'')');
129+
--Assert
130+
ut.expect(anydata.convertCollection(ut3.ut_expectation_processor.get_failed_expectations())).to_be_empty();
131+
end;
132+
133+
--and so on...
134+
135+
end;
136+
'''
137+
val parser = new UtplsqlParser(plsql)
138+
Assert.assertEquals("test_expect_not_to_be_null.cleanup_expectations", parser.getPathAt(7,1))
139+
Assert.assertEquals("test_expect_not_to_be_null.create_types", parser.getPathAt(13,1))
140+
// was: '||gc_varray_name||'.drop_types
141+
Assert.assertEquals("test_expect_not_to_be_null.drop_types", parser.getPathAt(23,1))
142+
// was: '||gc_varray_name||'.blob_not_null
143+
Assert.assertEquals("test_expect_not_to_be_null.blob_not_null", parser.getPathAt(33,1))
144+
}
145+
146+
@Test
147+
def issue_7() {
148+
val plsql = '''
149+
create or replace package test_expect_not_to_be_null
150+
is
151+
--%suite(expectations - not_to_be_null)
152+
--%suitepath(utplsql.core.expectations.unary)
153+
154+
--%aftereach
155+
procedure cleanup_expectations;
156+
157+
--%beforeall
158+
procedure create_types;
159+
160+
--%afterall
161+
procedure drop_types;
162+
163+
--%test(Gives success for not null blob)
164+
procedure blob_not_null;
165+
166+
--%test(Gives success for blob with length 0)
167+
procedure blob_0_length;
168+
169+
-- ...
170+
end test_expect_not_to_be_null;
171+
/
172+
'''
173+
val parser = new UtplsqlParser(plsql)
174+
// was: test_expect_not_to_be_null.create_types
175+
Assert.assertEquals("test_expect_not_to_be_null.blob_not_null", parser.getPathAt(13,26))
176+
}
177+
178+
88179
}

0 commit comments

Comments
 (0)