Skip to content

Commit e95e0cc

Browse files
Part 1 of #5
enable/disable "Run utPLSQL test" context menu item in Connections navigator window
1 parent f98feec commit e95e0cc

File tree

9 files changed

+401
-16
lines changed

9 files changed

+401
-16
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright 2018 Philipp Salvisberg <[email protected]>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.utplsql.sqldev.dal
17+
18+
import java.sql.Connection
19+
import java.util.List
20+
import org.springframework.dao.EmptyResultDataAccessException
21+
import org.springframework.jdbc.core.BeanPropertyRowMapper
22+
import org.springframework.jdbc.core.JdbcTemplate
23+
import org.springframework.jdbc.datasource.SingleConnectionDataSource
24+
import org.utplsql.sqldev.model.ut.Annotation
25+
26+
class UtplsqlDao {
27+
public static val UTPLSQL_PACKAGE_NAME = "UT"
28+
private var Connection conn
29+
private var JdbcTemplate jdbcTemplate
30+
31+
new(Connection conn) {
32+
this.conn = conn
33+
this.jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, true))
34+
}
35+
36+
/**
37+
* Gets the schema name of the utPLSQL installation.
38+
*
39+
* @return utPLSQL schema or null if no utPLSQL is not installed
40+
* @throws DataAccessException if there is a problem
41+
*/
42+
def String getUtplsqlSchema() {
43+
val sql = '''
44+
SELECT table_owner
45+
FROM all_synonyms
46+
WHERE owner = 'PUBLIC'
47+
AND synonym_name = '«UTPLSQL_PACKAGE_NAME»'
48+
AND table_name = '«UTPLSQL_PACKAGE_NAME»'
49+
'''
50+
try {
51+
val schema = jdbcTemplate.queryForObject(sql, String)
52+
return schema
53+
} catch (EmptyResultDataAccessException e) {
54+
return null
55+
}
56+
}
57+
58+
/**
59+
* Checks if the package ut_annotation_manager is installed.
60+
* This package has been introduced with utPLSQL 3.0.4.
61+
* This version is a prerequisite to identify
62+
* utPLSQL unit test procedures.
63+
*
64+
* @return true if ut_annotation_manager package has been found
65+
* @throws DataAccessException if there is a problem
66+
*/
67+
def boolean isUtAnnotationManagerInstalled() {
68+
if (utplsqlSchema !== null) {
69+
val sql = '''
70+
SELECT count(*)
71+
FROM all_objects
72+
WHERE owner = '«utplsqlSchema»'
73+
AND object_type = 'PACKAGE'
74+
AND object_name = 'UT_ANNOTATION_MANAGER'
75+
'''
76+
val found = jdbcTemplate.queryForObject(sql, Integer)
77+
return found == 1
78+
}
79+
return false
80+
}
81+
82+
/**
83+
* Checks if utPLSQL tests exist
84+
*
85+
* @param owner schema name, mandatory, case-insensitive
86+
* @param objectName name of the package or package body, optional, case-insensitive
87+
* @param subobjectName name of the procedure, optional, case-insensitive
88+
* @return true if at least one test has been found
89+
* @throws DataAccessException if a utPLSQL version less than 3.0.4 is installed or if there are other problems
90+
*/
91+
def boolean containsUtplsqlTest(String owner, String objectName, String subobjectName) {
92+
try {
93+
val sql = '''
94+
SELECT count(
95+
CASE
96+
WHEN a.name = 'test'
97+
AND (upper(a.subobject_name) = upper(?) OR ? IS NULL)
98+
THEN
99+
1
100+
ELSE
101+
NULL
102+
END
103+
)
104+
FROM TABLE(«utplsqlSchema».ut_annotation_manager.get_annotated_objects(upper(?), 'PACKAGE')) o
105+
CROSS JOIN TABLE(o.annotations) a
106+
WHERE (o.object_name = upper(?) OR ? IS NULL)
107+
AND a.name IN ('test', 'suite')
108+
HAVING count(
109+
CASE
110+
WHEN a.name = 'suite' THEN
111+
1
112+
ELSE
113+
NULL
114+
END
115+
) > 0
116+
'''
117+
val found = jdbcTemplate.queryForObject(sql, Integer, #[subobjectName, subobjectName, owner, objectName, objectName])
118+
return found > 0
119+
} catch (EmptyResultDataAccessException e) {
120+
return false
121+
}
122+
}
123+
124+
def boolean containsUtplsqlTest(String owner) {
125+
return containsUtplsqlTest(owner, null, null)
126+
}
127+
128+
def boolean containsUtplsqlTest(String owner, String objectType) {
129+
return containsUtplsqlTest(owner, objectType, null)
130+
}
131+
132+
/**
133+
* Gets a list of utPLSQL annotations for a given PL/SQL package specification
134+
*
135+
* @param owner schema name, mandatory, case-insensitive
136+
* @param objectName name of the package or package body, optional, case-insensitive
137+
* @return list of Annotation with name 'suite' or 'test'
138+
* @throws DataAccessException if a utPLSQL version less than 3.0.4 is installed or if there are other problems
139+
*/
140+
def List<Annotation> annotations(String owner, String objectName) {
141+
val sql = '''
142+
SELECT o.object_owner, o.object_type, o.object_name, a.name, a.text, a.subobject_name
143+
FROM TABLE(«utplsqlSchema».ut_annotation_manager.get_annotated_objects(upper(?), 'PACKAGE')) o
144+
CROSS JOIN TABLE(o.annotations) a
145+
WHERE o.object_name = upper(?)
146+
'''
147+
val result = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Annotation>(Annotation), #[owner, objectName])
148+
return result
149+
}
150+
151+
}

sqldev/src/main/java/org/utplsql/sqldev/menu/UtplsqlContextMenuListener.xtend

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,15 @@ class UtplsqlContextMenuListener implements ContextMenuListener {
4343
if (element instanceof DatabaseConnection) {
4444
showMenu = true
4545
} else if (element instanceof ObjectFolder) {
46-
if (element.objectType == "PACKAGE" || element.objectType == "TYPE") {
46+
if (element.objectType == "PACKAGE") {
4747
showMenu = true
4848
}
4949
} else if (element instanceof PlSqlNode) {
50-
if (element.objectType == "PACKAGE" || element.objectType == "PACKAGE BODY" ||
51-
element.objectType == "TYPE" || element.objectType == "TYPE BODY") {
50+
if (element.objectType == "PACKAGE" || element.objectType == "PACKAGE BODY") {
5251
showMenu = true
5352
}
5453
} else if (element instanceof ChildObjectElement) {
55-
if (element.URL.objectType == "PACKAGE" || element.URL.objectType == "TYPE") {
54+
if (element.URL.objectType == "PACKAGE") {
5655
showMenu = true
5756
}
5857
}

sqldev/src/main/java/org/utplsql/sqldev/menu/UtplsqlController.xtend

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ import oracle.dbtools.raptor.navigator.impl.ChildObjectElement
2323
import oracle.dbtools.raptor.navigator.impl.DatabaseSourceNode
2424
import oracle.dbtools.raptor.navigator.impl.ObjectFolder
2525
import oracle.dbtools.raptor.navigator.plsql.PlSqlNode
26+
import oracle.dbtools.raptor.utils.Connections
2627
import oracle.dbtools.worksheet.editor.Worksheet
2728
import oracle.ide.Context
2829
import oracle.ide.Ide
2930
import oracle.ide.controller.Controller
3031
import oracle.ide.controller.IdeAction
3132
import oracle.ide.editor.Editor
3233
import org.utplsql.sqldev.UtplsqlWorksheet
34+
import org.utplsql.sqldev.dal.UtplsqlDao
3335
import org.utplsql.sqldev.model.URLTools
3436
import org.utplsql.sqldev.parser.UtplsqlParser
3537

@@ -62,7 +64,21 @@ class UtplsqlController implements Controller {
6264
}
6365
} else if (view instanceof DBNavigatorWindow) {
6466
if (context.selection.length == 1) {
65-
action.enabled = true
67+
val element = context.selection.get(0)
68+
val dao = new UtplsqlDao(Connections.instance.getConnection(context.URL.connectionName))
69+
if (dao.utAnnotationManagerInstalled) {
70+
if (element instanceof DatabaseConnection) {
71+
action.enabled = dao.containsUtplsqlTest(element.connection.schema)
72+
} else if (element instanceof ObjectFolder) {
73+
action.enabled = dao.containsUtplsqlTest(element.URL.schema)
74+
} else if (element instanceof PlSqlNode) {
75+
action.enabled = dao.containsUtplsqlTest(element.owner, element.objectName)
76+
} else if (element instanceof ChildObjectElement) {
77+
action.enabled = dao.containsUtplsqlTest(element.URL.schema, element.URL.memberObject, element.shortLabel)
78+
}
79+
} else {
80+
action.enabled = true
81+
}
6682
}
6783
}
6884
return true
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2018 Philipp Salvisberg <[email protected]>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.utplsql.sqldev.model.ut
17+
18+
import org.eclipse.xtend.lib.annotations.Accessors
19+
import org.utplsql.sqldev.model.AbstractModel
20+
21+
@Accessors
22+
class Annotation extends AbstractModel {
23+
String objectOwner
24+
String objectType
25+
String objectName
26+
String name
27+
String text
28+
String subobjectName
29+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class UtplsqlParser {
8484
}
8585

8686
private def populateObjects() {
87-
val p = Pattern.compile("(?i)(\\s*)(create(\\s+or\\s+replace)?\\s+(package|type)\\s+(body\\s+)?)([^\\s]+)(\\s+)")
87+
val p = Pattern.compile("(?i)(\\s*)(create(\\s+or\\s+replace)?\\s+(package)\\s+(body\\s+)?)([^\\s]+)(\\s+)")
8888
val m = p.matcher(plsqlReduced)
8989
while (m.find) {
9090
val o = new PlsqlObject
@@ -94,7 +94,7 @@ class UtplsqlParser {
9494
}
9595
}
9696
private def populateUnits() {
97-
val p = Pattern.compile("(?i)(\\s*)(function|procedure)(\\s+)([^\\s\\(;]+)")
97+
val p = Pattern.compile("(?i)(\\s*)(procedure)(\\s+)([^\\s\\(;]+)")
9898
val m = p.matcher(plsqlReduced)
9999
while (m.find) {
100100
val u = new Unit
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2018 Philipp Salvisberg <[email protected]>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.utplsql.sqldev.tests
17+
18+
import java.util.Properties
19+
import org.springframework.jdbc.core.JdbcTemplate
20+
import org.springframework.jdbc.datasource.SingleConnectionDataSource
21+
22+
abstract class AbstractJdbcTest {
23+
protected static var SingleConnectionDataSource dataSource
24+
protected static var JdbcTemplate jdbcTemplate
25+
protected static var SingleConnectionDataSource sysDataSource
26+
protected static var JdbcTemplate sysJdbcTemplate
27+
// static initializer not supported in Xtend, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=429141
28+
protected static val _staticInitializerForDataSourceAndJdbcTemplate = {
29+
val p = new Properties()
30+
p.load(AbstractJdbcTest.getClass().getResourceAsStream("/test.properties"))
31+
// create dataSource and jdbcTemplate
32+
dataSource = new SingleConnectionDataSource()
33+
dataSource.driverClassName = "oracle.jdbc.OracleDriver"
34+
dataSource.url = '''jdbc:oracle:thin:@«p.getProperty("host")»:«p.getProperty("port")»/«p.getProperty("service")»'''
35+
dataSource.username = p.getProperty("scott_username")
36+
dataSource.password = p.getProperty("scott_password")
37+
jdbcTemplate = new JdbcTemplate(dataSource)
38+
// create dbaDataSource and dbaJdbcTemplate
39+
sysDataSource = new SingleConnectionDataSource()
40+
sysDataSource.driverClassName = "oracle.jdbc.OracleDriver"
41+
sysDataSource.url = '''jdbc:oracle:thin:@«p.getProperty("host")»:«p.getProperty("port")»/«p.getProperty("service")»'''
42+
sysDataSource.username = p.getProperty("sys_username")
43+
sysDataSource.password = p.getProperty("sys_password")
44+
sysJdbcTemplate = new JdbcTemplate(AbstractJdbcTest.sysDataSource)
45+
}
46+
}

0 commit comments

Comments
 (0)