• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmd / pmd / 121

16 Aug 2025 06:03PM UTC coverage: 78.488% (+0.001%) from 78.487%
121

push

github

adangel
chore: Fix typos (#5980)

Merge pull request #5980 from zbynek:typos

17914 of 23665 branches covered (75.7%)

Branch coverage included in aggregate %.

6 of 8 new or added lines in 4 files covered. (75.0%)

39216 of 49123 relevant lines covered (79.83%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/pmd-core/src/main/java/net/sourceforge/pmd/util/database/DBMSMetadata.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.util.database;
6

7
import java.net.MalformedURLException;
8
import java.sql.CallableStatement;
9
import java.sql.Clob;
10
import java.sql.Connection;
11
import java.sql.DatabaseMetaData;
12
import java.sql.DriverManager;
13
import java.sql.PreparedStatement;
14
import java.sql.ResultSet;
15
import java.sql.SQLException;
16
import java.util.ArrayList;
17
import java.util.Arrays;
18
import java.util.Collections;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Properties;
22

23
import org.slf4j.Logger;
24
import org.slf4j.LoggerFactory;
25

26
/**
27
 * Wrap JDBC connection for use by PMD: {@link DBURI} parameters specify the
28
 * source code to be passed to PMD.
29
 *
30
 * @author sturton
31
 */
32
public class DBMSMetadata {
33
    /**
34
     * Local logger.
35
     */
36
    private static final Logger LOG = LoggerFactory.getLogger(DBMSMetadata.class);
×
37

38
    /**
39
     * Optional DBType property specifying a query to fetch the Source Objects
40
     * from the database.
41
     *
42
     * <p>
43
     * If the DBType lacks this property, then the standard
44
     * DatabaseMetaData.getProcedures method is used.
45
     * </p>
46
     */
47
    private static final String GET_SOURCE_OBJECTS_STATEMENT = "getSourceObjectsStatement";
48

49
    /**
50
     * Essential DBType property specifying a CallableStatement to retrieve the
51
     * Source Object's code from the database.
52
     *
53
     * <p>
54
     * <b>If the DBType lacks this property, there is no DatabaseMetaData method
55
     * to fallback to</b>.
56
     * </p>
57
     */
58
    private static final String GET_SOURCE_CODE_STATEMENT = "getSourceCodeStatement";
59

60
    /**
61
     * DBURI
62
     */
63
    protected DBURI dburi = null;
×
64

65
    /**
66
     * Connection management
67
     */
68
    protected Connection connection = null;
×
69

70
    /**
71
     * Procedural statement to return list of source code objects.
72
     */
73
    protected String returnSourceCodeObjectsStatement = null;
×
74

75
    /**
76
     * Procedural statement to return source code.
77
     */
78
    protected String returnSourceCodeStatement = null;
×
79

80
    /**
81
     * CallableStatement to return source code.
82
     */
83
    protected CallableStatement callableStatement = null;
×
84

85
    /**
86
     * {@link java.sql.Types} value representing the type returned by
87
     * {@link #callableStatement}
88
     *
89
     * <b>Currently only java.sql.Types.String and java.sql.Types.Clob are
90
     * supported</b>
91
     */
92
    protected int returnType = java.sql.Types.CLOB;
×
93

94
    /* constructors */
95
    /**
96
     * Minimal constructor
97
     *
98
     * @param c
99
     *            JDBC Connection
100
     * @throws SQLException
101
     */
102
    public DBMSMetadata(Connection c) throws SQLException {
×
103
        connection = c;
×
104
    }
×
105

106
    /**
107
     * Define database connection and source code to retrieve with explicit
108
     * database username and password.
109
     *
110
     * @param user
111
     *            Database username
112
     * @param password
113
     *            Database password
114
     * @param dbURI
115
     *            {@link DBURI } containing JDBC connection plus parameters to
116
     *            specify source code.
117
     * @throws SQLException
118
     *             on failing to create JDBC connection
119
     * @throws MalformedURLException
120
     *             on attempting to connect with malformed JDBC URL
121
     * @throws ClassNotFoundException
122
     *             on failing to locate the JDBC driver class.
123
     */
124
    public DBMSMetadata(String user, String password, DBURI dbURI)
125
            throws SQLException, MalformedURLException, ClassNotFoundException {
×
126
        String urlString = init(dbURI);
×
127

128
        Properties mergedProperties = dbURI.getDbType().getProperties();
×
129
        Map<String, String> dbURIParameters = dbURI.getParameters();
×
130
        mergedProperties.putAll(dbURIParameters);
×
131
        mergedProperties.put("user", user);
×
132
        mergedProperties.put("password", password);
×
133

134
        connection = DriverManager.getConnection(urlString, mergedProperties);
×
135
        LOG.debug("we have a connection={}", connection);
×
136
    }
×
137

138
    /**
139
     * Define database connection and source code to retrieve with database
140
     * properties.
141
     *
142
     * @param properties
143
     *            database settings such as database username, password
144
     * @param dbURI
145
     *            {@link DBURI } containing JDBC connection plus parameters to
146
     *            specify source code.
147
     * @throws SQLException
148
     *             on failing to create JDBC connection
149
     * @throws MalformedURLException
150
     *             on attempting to connect with malformed JDBC URL
151
     * @throws ClassNotFoundException
152
     *             on failing to locate the JDBC driver class.
153
     */
154
    public DBMSMetadata(Properties properties, DBURI dbURI)
155
            throws SQLException, MalformedURLException, ClassNotFoundException {
×
156
        String urlString = init(dbURI);
×
157

158
        Properties mergedProperties = dbURI.getDbType().getProperties();
×
159
        Map<String, String> dbURIParameters = dbURI.getParameters();
×
160
        mergedProperties.putAll(dbURIParameters);
×
161
        mergedProperties.putAll(properties);
×
162

163
        LOG.debug("Retrieving connection for urlString={}", urlString);
×
164
        connection = DriverManager.getConnection(urlString, mergedProperties);
×
165
        LOG.debug("Secured Connection for DBURI={}", dbURI);
×
166
    }
×
167

168
    /**
169
     * Define database connection and source code to retrieve.
170
     *
171
     * <p>
172
     * This constructor is reliant on database username and password embedded in
173
     * the JDBC URL or defaulted from the {@link DBURI}'s <code>DriverType</code>.
174
     * </p>
175
     *
176
     * @param dbURI
177
     *            {@link DBURI } containing JDBC connection plus parameters to
178
     *            specify source code.
179
     * @throws SQLException
180
     *             on failing to create JDBC connection
181
     * @throws ClassNotFoundException
182
     *             on failing to locate the JDBC driver class.
183
     */
184
    public DBMSMetadata(DBURI dbURI) throws SQLException, ClassNotFoundException {
×
185
        String urlString = init(dbURI);
×
186

187
        Properties dbURIProperties = dbURI.getDbType().getProperties();
×
188
        Map<String, String> dbURIParameters = dbURI.getParameters();
×
189

190
        /*
191
         * Overwrite any DBType properties with DBURI parameters allowing JDBC
192
         * connection properties to be inherited from DBType or passed as DBURI
193
         * parameters
194
         */
195
        dbURIProperties.putAll(dbURIParameters);
×
196

197
        connection = DriverManager.getConnection(urlString, dbURIProperties);
×
198
    }
×
199

200
    /**
201
     * Return JDBC Connection for direct JDBC access to the specified database.
202
     *
203
     * @return I=JDBC Connection
204
     * @throws SQLException
205
     */
206
    public Connection getConnection() throws SQLException {
207
        return connection;
×
208
    }
209

210
    private String init(DBURI dbURI) throws ClassNotFoundException {
211
        this.dburi = dbURI;
×
212
        this.returnSourceCodeObjectsStatement = dbURI.getDbType().getProperties()
×
213
                .getProperty(GET_SOURCE_OBJECTS_STATEMENT);
×
214
        this.returnSourceCodeStatement = dbURI.getDbType().getProperties().getProperty(GET_SOURCE_CODE_STATEMENT);
×
215
        this.returnType = dbURI.getSourceCodeType();
×
216
        LOG.debug("returnSourceCodeStatement={}, returnType={}", returnSourceCodeStatement, returnType);
×
217

218
        String driverClass = dbURI.getDriverClass();
×
219
        String urlString = dbURI.getURL().toString();
×
220
        LOG.debug("driverClass={}, urlString={}", driverClass, urlString);
×
221
        Class.forName(driverClass);
×
222
        LOG.debug("Located class for driverClass={}", driverClass);
×
223
        return urlString;
×
224
    }
225

226
    /**
227
     * Return source code text from the database.
228
     *
229
     * @param sourceObject object
230
     * @return source code
231
     * @throws SQLException
232
     */
233
    public java.io.Reader getSourceCode(SourceObject sourceObject) throws SQLException {
234
        return getSourceCode(sourceObject.getType(), sourceObject.getName(), sourceObject.getSchema());
×
235

236
    }
237

238
    /**
239
     * return source code text
240
     *
241
     * @param objectType
242
     * @param name
243
     *            Source Code name
244
     * @param schema
245
     *            Owner of the code
246
     * @return Source code text.
247
     * @throws SQLException
248
     *             on failing to retrieve the source Code text
249
     */
250
    public java.io.Reader getSourceCode(String objectType, String name, String schema) throws SQLException {
251
        Object result;
252

253
        /*
254
         * Only define callableStatement once and reuse it for subsequent calls
255
         * to getSourceCode()
256
         */
257
        if (null == callableStatement) {
×
258
            LOG.trace("getSourceCode: returnSourceCodeStatement=\"{}\"", returnSourceCodeStatement);
×
259
            LOG.trace("getSourceCode: returnType=\"{}\"", returnType);
×
260
            callableStatement = getConnection().prepareCall(returnSourceCodeStatement);
×
261
            callableStatement.registerOutParameter(1, returnType);
×
262
        }
263

264
        // set IN parameters
265
        callableStatement.setString(2, objectType);
×
266
        callableStatement.setString(3, name);
×
267
        callableStatement.setString(4, schema);
×
268
        //
269
        // execute statement
270
        callableStatement.executeUpdate();
×
271
        // retrieve OUT parameters
272
        result = callableStatement.getObject(1);
×
273

274
        return (java.sql.Types.CLOB == returnType) ? ((Clob) result).getCharacterStream()
×
275
                : new java.io.StringReader(result.toString());
×
276
    }
277

278
    /**
279
     * Return all source code objects associated with any associated DBURI.
280
     *
281
     * @return
282
     */
283
    public List<SourceObject> getSourceObjectList() {
284

285
        if (null == dburi) {
×
286
            LOG.warn("No dbUri defined - no further action possible");
×
287
            return Collections.emptyList();
×
288
        } else {
289
            return getSourceObjectList(dburi.getLanguagesList(), dburi.getSchemasList(), dburi.getSourceCodeTypesList(),
×
290
                    dburi.getSourceCodeNamesList());
×
291
        }
292

293
    }
294

295
    /**
296
     * Return all source code objects associated with the specified languages,
297
     * schemas, source code types and source code names.
298
     *
299
     * <p>
300
     * Each parameter may be null and the appropriate field from any related
301
     * DBURI is assigned, defaulting to the normal SQL wildcard expression
302
     * ("%").
303
     * </p>
304
     *
305
     * @param languages
306
     *            Optional list of languages to search for
307
     * @param schemas
308
     *            Optional list of schemas to search for
309
     * @param sourceCodeTypes
310
     *            Optional list of source code types to search for
311
     * @param sourceCodeNames
312
     *            Optional list of source code names to search for
313
     */
314
    public List<SourceObject> getSourceObjectList(List<String> languages, List<String> schemas,
315
            List<String> sourceCodeTypes, List<String> sourceCodeNames) {
316

317
        List<SourceObject> sourceObjectsList = new ArrayList<>();
×
318

319
        List<String> searchLanguages = languages;
×
320
        List<String> searchSchemas = schemas;
×
321
        List<String> searchSourceCodeTypes = sourceCodeTypes;
×
322
        List<String> searchSourceCodeNames = sourceCodeNames;
×
323
        List<String> wildcardList = Arrays.asList("%");
×
324

325
        /*
326
         * Assign each search list to the first
327
         *
328
         * explicit parameter dburi field wildcard list
329
         *
330
         */
331
        if (null == searchLanguages) {
×
332
            List<String> dbURIList = (null == dburi) ? null : dburi.getLanguagesList();
×
333
            if (null == dbURIList || dbURIList.isEmpty()) {
×
334
                searchLanguages = wildcardList;
×
335
            } else {
336
                searchLanguages = dbURIList;
×
337
            }
338
        }
339

340
        if (null == searchSchemas) {
×
341
            List<String> dbURIList = (null == dburi) ? null : dburi.getSchemasList();
×
342
            if (null == dbURIList || dbURIList.isEmpty()) {
×
343
                searchSchemas = wildcardList;
×
344
            } else {
345
                searchSchemas = dbURIList;
×
346
            }
347
        }
348

349
        if (null == searchSourceCodeTypes) {
×
350
            List<String> dbURIList = (null == dburi) ? null : dburi.getSourceCodeTypesList();
×
351
            if (null == dbURIList || dbURIList.isEmpty()) {
×
352
                searchSourceCodeTypes = wildcardList;
×
353
            } else {
354
                searchSourceCodeTypes = dbURIList;
×
355
            }
356
        }
357

358
        if (null == searchSourceCodeNames) {
×
359
            List<String> dbURIList = (null == dburi) ? null : dburi.getSourceCodeNamesList();
×
360
            if (null == dbURIList || dbURIList.isEmpty()) {
×
361
                searchSourceCodeNames = wildcardList;
×
362
            } else {
363
                searchSourceCodeNames = dbURIList;
×
364
            }
365
        }
366

367
        try {
368

369
            if (null != returnSourceCodeObjectsStatement) {
×
370
                LOG.debug("Have bespoke returnSourceCodeObjectsStatement from DBURI: \"{}\"",
×
371
                        returnSourceCodeObjectsStatement);
372
                try (PreparedStatement sourceCodeObjectsStatement = getConnection()
×
373
                        .prepareStatement(returnSourceCodeObjectsStatement)) {
×
374
                    for (String language : searchLanguages) {
×
375
                        for (String schema : searchSchemas) {
×
376
                            for (String sourceCodeType : searchSourceCodeTypes) {
×
377
                                for (String sourceCodeName : searchSourceCodeNames) {
×
378
                                    sourceObjectsList.addAll(findSourceObjects(sourceCodeObjectsStatement, language, schema,
×
379
                                            sourceCodeType, sourceCodeName));
380
                                }
×
381
                            }
×
382
                        }
×
383
                    }
×
384
                }
385
            } else {
386
                // Use standard DatabaseMetaData interface
387
                LOG.debug(
×
388
                        "Have dbUri - no returnSourceCodeObjectsStatement, reverting to DatabaseMetaData.getProcedures(...)");
389

390
                DatabaseMetaData metadata = connection.getMetaData();
×
391
                List<String> schemasList = dburi.getSchemasList();
×
392
                for (String schema : schemasList) {
×
393
                    for (String sourceCodeName : dburi.getSourceCodeNamesList()) {
×
394
                        sourceObjectsList.addAll(findSourceObjectFromMetaData(metadata, schema, sourceCodeName));
×
395
                    }
×
396
                }
×
397
            }
398

NEW
399
            LOG.trace("Identified={} sourceObjects", sourceObjectsList.size());
×
400

401
            return sourceObjectsList;
×
402
        } catch (SQLException sqle) {
×
403
            throw new RuntimeException("Problem collecting list of source code objects", sqle);
×
404
        }
405
    }
406

407
    private List<SourceObject> findSourceObjectFromMetaData(DatabaseMetaData metadata,
408
            String schema, String sourceCodeName) throws SQLException {
409
        List<SourceObject> sourceObjectsList = new ArrayList<>();
×
410
        /*
411
         * public ResultSet getProcedures(String catalog ,
412
         * String schemaPattern , String procedureNamePattern)
413
         * throws SQLException
414
         */
415
        try (ResultSet sourceCodeObjects = metadata.getProcedures(null, schema, sourceCodeName)) {
×
416
            /*
417
             * From Javadoc .... Each procedure description has the
418
             * the following columns: PROCEDURE_CAT String =>
419
             * procedure catalog (may be null) PROCEDURE_SCHEM
420
             * String => procedure schema (may be null)
421
             * PROCEDURE_NAME String => procedure name reserved for
422
             * future use reserved for future use reserved for
423
             * future use REMARKS String => explanatory comment on
424
             * the procedure PROCEDURE_TYPE short => kind of
425
             * procedure: procedureResultUnknown - Cannot determine
426
             * if a return value will be returned procedureNoResult
427
             * - Does not return a return value
428
             * procedureReturnsResult - Returns a return value
429
             * SPECIFIC_NAME String => The name which uniquely
430
             * identifies this procedure within its schema.
431
             *
432
             * Oracle getProcedures actually returns these 8
433
             * columns:- ResultSet "Matched Procedures" has 8
434
             * columns and contains ...
435
             * [PROCEDURE_CAT,PROCEDURE_SCHEM,PROCEDURE_NAME,NULL,
436
             * NULL,NULL,REMARKS,PROCEDURE_TYPE
437
             * ,null,PHPDEMO,ADD_JOB_HISTORY,null,null,null,
438
             * Standalone procedure or function,1
439
             * ,FETCHPERFPKG,PHPDEMO,BULKSELECTPRC,null,null,null,
440
             * Packaged function,2
441
             * ,FETCHPERFPKG,PHPDEMO,BULKSELECTPRC,null,null,null,
442
             * Packaged procedure,1
443
             * ,null,PHPDEMO,CITY_LIST,null,null,null,Standalone
444
             * procedure or function,1
445
             * ,null,PHPDEMO,EDDISCOUNT,null,null,null,Standalone
446
             * procedure or function,2
447
             * ,SELPKG_BA,PHPDEMO,EMPSELBULK,null,null,null,Packaged
448
             * function,2
449
             * ,SELPKG_BA,PHPDEMO,EMPSELBULK,null,null,null,Packaged
450
             * procedure,1
451
             * ,INSPKG,PHPDEMO,INSFORALL,null,null,null,Packaged
452
             * procedure,1
453
             * ,null,PHPDEMO,MYDOFETCH,null,null,null,Standalone
454
             * procedure or function,2
455
             * ,null,PHPDEMO,MYPROC1,null,null,null,Standalone
456
             * procedure or function,1
457
             * ,null,PHPDEMO,MYPROC2,null,null,null,Standalone
458
             * procedure or function,1
459
             * ,null,PHPDEMO,MYXAQUERY,null,null,null,Standalone
460
             * procedure or function,1
461
             * ,null,PHPDEMO,POLICY_VPDPARTS,null,null,null,
462
             * Standalone procedure or function,2
463
             * ,FETCHPERFPKG,PHPDEMO,REFCURPRC,null,null,null,
464
             * Packaged procedure,1
465
             * ,null,PHPDEMO,SECURE_DML,null,null,null,Standalone
466
             * procedure or function,1 ... ]
467
             */
468
            while (sourceCodeObjects.next()) {
×
469
                LOG.trace("Located schema={},object_type={},object_name={}",
×
470
                        sourceCodeObjects.getString("PROCEDURE_SCHEM"),
×
471
                        sourceCodeObjects.getString("PROCEDURE_TYPE"),
×
472
                        sourceCodeObjects.getString("PROCEDURE_NAME"));
×
473

474
                sourceObjectsList.add(new SourceObject(sourceCodeObjects.getString("PROCEDURE_SCHEM"),
×
475
                        sourceCodeObjects.getString("PROCEDURE_TYPE"),
×
476
                        sourceCodeObjects.getString("PROCEDURE_NAME"), null));
×
477
            }
478
        }
479
        return sourceObjectsList;
×
480
    }
481

482
    private List<SourceObject> findSourceObjects(PreparedStatement sourceCodeObjectsStatement,
483
            String language, String schema, String sourceCodeType, String sourceCodeName) throws SQLException {
484
        List<SourceObject> sourceObjectsList = new ArrayList<>();
×
485
        sourceCodeObjectsStatement.setString(1, language);
×
486
        sourceCodeObjectsStatement.setString(2, schema);
×
487
        sourceCodeObjectsStatement.setString(3, sourceCodeType);
×
488
        sourceCodeObjectsStatement.setString(4, sourceCodeName);
×
489
        LOG.debug(
×
490
                "searching for language=\"{}\", schema=\"{}\", sourceCodeType=\"{}\", sourceCodeNames=\"{}\" ",
491
                language, schema, sourceCodeType, sourceCodeName);
492

493
        /*
494
         * public ResultSet getProcedures(String catalog
495
         * , String schemaPattern , String
496
         * procedureNamePattern) throws SQLException
497
         */
498
        try (ResultSet sourceCodeObjects = sourceCodeObjectsStatement.executeQuery()) {
×
499

500
            /*
501
             * From Javadoc .... Each procedure description
502
             * has the the following columns: PROCEDURE_CAT
503
             * String => procedure catalog (may be null)
504
             * PROCEDURE_SCHEM String => procedure schema
505
             * (may be null) PROCEDURE_NAME String =>
506
             * procedure name reserved for future use
507
             * reserved for future use reserved for future
508
             * use REMARKS String => explanatory comment on
509
             * the procedure PROCEDURE_TYPE short => kind of
510
             * procedure: procedureResultUnknown - Cannot
511
             * determine if a return value will be returned
512
             * procedureNoResult - Does not return a return
513
             * value procedureReturnsResult - Returns a
514
             * return value SPECIFIC_NAME String => The name
515
             * which uniquely identifies this procedure
516
             * within its schema.
517
             */
518
            while (sourceCodeObjects.next()) {
×
519
                LOG.trace("Found schema={},object_type={},object_name={}",
×
520
                        sourceCodeObjects.getString("PROCEDURE_SCHEM"),
×
521
                        sourceCodeObjects.getString("PROCEDURE_TYPE"),
×
522
                        sourceCodeObjects.getString("PROCEDURE_NAME"));
×
523

524
                sourceObjectsList
×
525
                        .add(new SourceObject(sourceCodeObjects.getString("PROCEDURE_SCHEM"),
×
526
                                sourceCodeObjects.getString("PROCEDURE_TYPE"),
×
527
                                sourceCodeObjects.getString("PROCEDURE_NAME"), null));
×
528
            }
529
        }
530
        return sourceObjectsList;
×
531
    }
532
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc