Skip to content

Commit 0b969c2

Browse files
committed
#882 Add JB_PROCEDURE_SOURCE column to getProcedures
Remove previously added getProcedureSourceCode(String, String) and deprecate getProcedureSourceCode(String) in favour of getProcedures
1 parent d28e557 commit 0b969c2

5 files changed

Lines changed: 90 additions & 38 deletions

File tree

src/docs/asciidoc/release_notes.adoc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,24 @@ Within the limitations and specification of the JDBC API, this method cannot be
532532
** `getColumnPrivileges` and `getTablePrivileges` received an additional column, `JB_GRANTEE_SCHEMA`, which is non-``null`` for grantees that are schema-bound (e.g. procedures).
533533
+
534534
As this is a non-standard column, we recommend to always retrieve it by name.
535-
** `getProcedureSourceCode`/`getTriggerSourceCode`/`getViewSourceCode` now also have an overload accepting the schema;
536-
the overloads without a `schema` parameter, or `schema` is `null` will return the source code of the first match found.
535+
** `getProcedures` received two additional columns.
536+
+
537+
As these are non-standard columns, we recommend to always retrieve them by name.
538+
+
539+
*** `JB_PROCEDURE_TYPE` -- value of column `RDB$PROCEDURE_TYPE`
540+
+
541+
For the possible values, the following constants were added to `FirebirdDatabaseMetaData`:
542+
+
543+
**** `jbProcedureTypeUnknown` (`0`)
544+
**** `jbProcedureTypeSelectable` (`1`)
545+
**** `jbProcedureTypeExecutable` (`2`).
546+
*** `JB_PROCEDURE_SOURCE` -- value of column `RDB$PROCEDURE_SOURCE`: the body of the stored procedure;
547+
this column is `null` for procedures in a package.
548+
** `getProcedureSourceCode` is deprecated, the recommended replacement is `DatabaseMetaData.getProcedures(String, String, String)`, column `JB_PROCEDURE_SOURCE`.
549+
** `getTriggerSourceCode`/`getViewSourceCode` now also have an overload accepting the schema;
550+
the overloads without a `schema` parameter, or `schema` is `null` will return the source code of the first match found (schema order is undefined).
551+
This behaviour also applies for -- now deprecated -- `getProcedureSourceCode(String)`.
552+
+
537553
The `schema` parameter is ignored on Firebird 5.0 and older.
538554
** `getSchemas()` returns all defined schemas
539555
** `getSchemas(String catalog, String schemaPattern)` returns all schemas matching the `LIKE` pattern `schemaPattern`, with the following caveats

src/main/org/firebirdsql/jdbc/FBDatabaseMetaData.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,8 @@ public boolean dataDefinitionIgnoredInTransactions() throws SQLException {
11951195
* <li>{@link #jbProcedureTypeExecutable} ({@code 2}) &mdash; executable</li>
11961196
* </ul>
11971197
* </li>
1198+
* <li><b>JB_PROCEDURE_SOURCE</b> String =&gt; source of the body of the stored procedure
1199+
* ({@code RDB$PROCEDURES.RDB$PROCEDURE_SOURCE}); {@code null} for procedures in a package</li>
11981200
* </ol>
11991201
* </p>
12001202
* <p>
@@ -1209,8 +1211,9 @@ public boolean dataDefinitionIgnoredInTransactions() throws SQLException {
12091211
* <li>Column {@code PROCEDURE_CAT} for normal procedures is empty string ({@code ""}) instead of {@code null},
12101212
* for packaged procedures it is the package name</li>
12111213
* <li>Column {@code SPECIFIC_NAME} for packaged procedures will report
1212-
* {@code <quoted-package-name>.<quoted-procedure-name>} (normal procedures will report the same as column
1213-
* {@code PROCEDURE_NAME}, the unquoted name)</li></li>
1214+
* {@code [<quoted-schema-name>.]<quoted-package-name>.<quoted-procedure-name>} (on Firebird 5.0 and older, normal
1215+
* procedures will report the same as column {@code PROCEDURE_NAME}, the unquoted name, on Firebird 6.0 and higher,
1216+
* {@code <quoted-schema-name>.<quoted-procedure-name>})</li>
12141217
* </ul>
12151218
*/
12161219
@Override
@@ -1870,12 +1873,7 @@ public boolean generatedKeyAlwaysReturned() throws SQLException {
18701873

18711874
@Override
18721875
public String getProcedureSourceCode(String procedureName) throws SQLException {
1873-
return getProcedureSourceCode(null, procedureName);
1874-
}
1875-
1876-
@Override
1877-
public String getProcedureSourceCode(String schema, String procedureName) throws SQLException {
1878-
return getSourceCode(schema, procedureName, SourceObjectType.PROCEDURE);
1876+
return getSourceCode(null, procedureName, SourceObjectType.PROCEDURE);
18791877
}
18801878

18811879
@Override

src/main/org/firebirdsql/jdbc/FirebirdDatabaseMetaData.java

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,59 +22,52 @@
2222
@SuppressWarnings("unused")
2323
public interface FirebirdDatabaseMetaData extends DatabaseMetaData {
2424

25-
// TODO Add schema support: remove/deprecate getProcedureSourceCode(String) and remove new alternative with schema,
26-
// and instead add a column to getProcedures(..)
27-
2825
/**
2926
* Firebird procedure type is unknown (value of column {@code JB_PROCEDURE_TYPE} of
3027
* {@link #getProcedures(String, String, String)})
28+
*
29+
* @since 7
3130
*/
3231
int jbProcedureTypeUnknown = 0;
3332
/**
3433
* Firebird procedure type is selectable (value of column {@code JB_PROCEDURE_TYPE} of
3534
* {@link #getProcedures(String, String, String)})
35+
*
36+
* @since 7
3637
*/
3738
int jbProcedureTypeSelectable = 1;
3839
/**
3940
* Firebird procedure type is executable (value of column {@code JB_PROCEDURE_TYPE} of
4041
* {@link #getProcedures(String, String, String)})
42+
*
43+
* @since 7
4144
*/
4245
int jbProcedureTypeExecutable = 2;
4346

4447
/**
4548
* Get the source of a stored procedure.
4649
* <p>
47-
* On Firebird 6.0 and higher, it is recommended to use {@link #getProcedureSourceCode(String, String)} instead.
50+
* <strong>WARNING</strong>: On Firebird 6.0 and higher, the sources returned are for the first procedure found
51+
* (with an undefined schema order!), use {@link DatabaseMetaData#getProcedures(String, String, String)} instead
52+
* (column {@code JB_PROCEDURE_SOURCE}).
4853
* </p>
4954
*
5055
* @param procedureName
5156
* name of the stored procedure
5257
* @return source of the stored procedure
5358
* @throws SQLException
5459
* if specified procedure cannot be found
55-
* @see #getProcedureSourceCode(String, String)
60+
* @deprecated use {@link DatabaseMetaData#getProcedures(String, String, String)}, column
61+
* {@code JB_PROCEDURE_SOURCE}; there are currently no plans to remove this method
5662
*/
63+
@Deprecated(forRemoval = false, since = "7")
5764
String getProcedureSourceCode(String procedureName) throws SQLException;
5865

59-
/**
60-
* Get the source of a stored procedure.
61-
*
62-
* @param schema
63-
* schema of the stored procedure ({@code null} drops the schema from the search; ignored on Firebird 5.0
64-
* and older)
65-
* @param procedureName
66-
* name of the stored procedure
67-
* @return source of the stored procedure
68-
* @throws SQLException
69-
* if specified procedure cannot be found
70-
* @since 7
71-
*/
72-
String getProcedureSourceCode(String schema, String procedureName) throws SQLException;
73-
7466
/**
7567
* Get the source of a trigger.
7668
* <p>
77-
* On Firebird 6.0 and higher, it is recommended to use {@link #getTriggerSourceCode(String, String)} instead.
69+
* <strong>WARNING</strong>: On Firebird 6.0 and higher, the sources returned are for the first trigger found
70+
* (with an undefined schema order!), use {@link #getTriggerSourceCode(String, String)} instead.
7871
* </p>
7972
*
8073
* @param triggerName
@@ -105,6 +98,10 @@ public interface FirebirdDatabaseMetaData extends DatabaseMetaData {
10598
* <p>
10699
* On Firebird 6.0 and higher, it is recommended to use {@link #getViewSourceCode(String, String)} instead.
107100
* </p>
101+
* <p>
102+
* <strong>WARNING</strong>: On Firebird 6.0 and higher, the sources returned are for the first view found
103+
* (with an undefined schema order!), use {@link #getViewSourceCode(String, String)} instead
104+
* </p>
108105
*
109106
* @param viewName
110107
* name of the view

src/main/org/firebirdsql/jdbc/metadata/GetProcedures.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public abstract sealed class GetProcedures extends AbstractMetadataMethod {
3636
private static final String COLUMN_SCHEMA_NAME = "RDB$SCHEMA_NAME";
3737
private static final String COLUMN_PACKAGE_NAME = "RDB$PACKAGE_NAME";
3838

39-
private static final RowDescriptor ROW_DESCRIPTOR = DbMetadataMediator.newRowDescriptorBuilder(10)
39+
private static final RowDescriptor ROW_DESCRIPTOR = DbMetadataMediator.newRowDescriptorBuilder(11)
4040
.at(0).simple(SQL_VARYING | 1, OBJECT_NAME_LENGTH, "PROCEDURE_CAT", PROCEDURES).addField()
4141
.at(1).simple(SQL_VARYING | 1, OBJECT_NAME_LENGTH, "PROCEDURE_SCHEM", PROCEDURES).addField()
4242
.at(2).simple(SQL_VARYING, OBJECT_NAME_LENGTH, "PROCEDURE_NAME", PROCEDURES).addField()
@@ -49,6 +49,8 @@ public abstract sealed class GetProcedures extends AbstractMetadataMethod {
4949
// space for quoted package name, ".", quoted procedure name (assuming no double quotes in name)
5050
.at(8).simple(SQL_VARYING, 2 * OBJECT_NAME_LENGTH + 5, "SPECIFIC_NAME", PROCEDURES).addField()
5151
.at(9).simple(SQL_SHORT, 0, "JB_PROCEDURE_TYPE", PROCEDURES).addField()
52+
// Field in Firebird is actually a blob, using Integer.MAX_VALUE for length
53+
.at(10).simple(SQL_VARYING, Integer.MAX_VALUE, "JB_PROCEDURE_SOURCE", PROCEDURES).addField()
5254
.toRowDescriptor();
5355

5456
private GetProcedures(DbMetadataMediator mediator) {
@@ -81,6 +83,7 @@ final RowValue createMetadataRow(ResultSet rs, RowValueBuilder valueBuilder) thr
8183
.at(7).setShort(rs.getShort("PROCEDURE_TYPE") == 0 ? procedureNoResult : procedureReturnsResult)
8284
.at(8).setString(toSpecificName(catalog, schema, procedureName))
8385
.at(9).setShort(rs.getShort("JB_PROCEDURE_TYPE"))
86+
.at(10).setString(rs.getString("JB_PROCEDURE_SOURCE"))
8487
.toRowValue(true);
8588
}
8689

@@ -114,7 +117,8 @@ private static final class FB2_5 extends GetProcedures {
114117
RDB$PROCEDURE_NAME as PROCEDURE_NAME,
115118
RDB$DESCRIPTION as REMARKS,
116119
RDB$PROCEDURE_OUTPUTS as PROCEDURE_TYPE,
117-
RDB$PROCEDURE_TYPE as JB_PROCEDURE_TYPE
120+
RDB$PROCEDURE_TYPE as JB_PROCEDURE_TYPE,
121+
RDB$PROCEDURE_SOURCE as JB_PROCEDURE_SOURCE
118122
from RDB$PROCEDURES""";
119123

120124
private static final String GET_PROCEDURES_ORDER_BY_2_5 = "\norder by RDB$PROCEDURE_NAME";
@@ -146,7 +150,8 @@ private static final class FB3 extends GetProcedures {
146150
trim(trailing from RDB$PROCEDURE_NAME) as PROCEDURE_NAME,
147151
RDB$DESCRIPTION as REMARKS,
148152
RDB$PROCEDURE_OUTPUTS as PROCEDURE_TYPE,
149-
RDB$PROCEDURE_TYPE as JB_PROCEDURE_TYPE
153+
RDB$PROCEDURE_TYPE as JB_PROCEDURE_TYPE,
154+
RDB$PROCEDURE_SOURCE as JB_PROCEDURE_SOURCE
150155
from RDB$PROCEDURES
151156
where RDB$PACKAGE_NAME is null""";
152157

@@ -180,7 +185,8 @@ private static final class FB3CatalogAsPackage extends GetProcedures {
180185
trim(trailing from RDB$PROCEDURE_NAME) as PROCEDURE_NAME,
181186
RDB$DESCRIPTION as REMARKS,
182187
RDB$PROCEDURE_OUTPUTS as PROCEDURE_TYPE,
183-
RDB$PROCEDURE_TYPE as JB_PROCEDURE_TYPE
188+
RDB$PROCEDURE_TYPE as JB_PROCEDURE_TYPE,
189+
RDB$PROCEDURE_SOURCE as JB_PROCEDURE_SOURCE
184190
from RDB$PROCEDURES""";
185191

186192
private static final String GET_PROCEDURES_ORDER_BY_3_W_PKG =
@@ -228,7 +234,8 @@ private static final class FB6 extends GetProcedures {
228234
trim(trailing from RDB$PROCEDURE_NAME) as PROCEDURE_NAME,
229235
RDB$DESCRIPTION as REMARKS,
230236
RDB$PROCEDURE_OUTPUTS as PROCEDURE_TYPE,
231-
RDB$PROCEDURE_TYPE as JB_PROCEDURE_TYPE
237+
RDB$PROCEDURE_TYPE as JB_PROCEDURE_TYPE,
238+
RDB$PROCEDURE_SOURCE as JB_PROCEDURE_SOURCE
232239
from SYSTEM.RDB$PROCEDURES
233240
where RDB$PACKAGE_NAME is null""";
234241

@@ -268,7 +275,8 @@ private static final class FB6CatalogAsPackage extends GetProcedures {
268275
trim(trailing from RDB$PROCEDURE_NAME) as PROCEDURE_NAME,
269276
RDB$DESCRIPTION as REMARKS,
270277
RDB$PROCEDURE_OUTPUTS as PROCEDURE_TYPE,
271-
RDB$PROCEDURE_TYPE as JB_PROCEDURE_TYPE
278+
RDB$PROCEDURE_TYPE as JB_PROCEDURE_TYPE,
279+
RDB$PROCEDURE_SOURCE as JB_PROCEDURE_SOURCE
272280
from SYSTEM.RDB$PROCEDURES""";
273281

274282
private static final String GET_PROCEDURES_ORDER_BY_6_W_PKG =

src/test/org/firebirdsql/jdbc/FBDatabaseMetaDataProceduresTest.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,9 @@ private enum ProcedureMetaData implements MetaDataInfo {
388388
FUTURE3(6, String.class),
389389
REMARKS(7, String.class),
390390
PROCEDURE_TYPE(8, Short.class),
391-
SPECIFIC_NAME(9, String.class)
391+
SPECIFIC_NAME(9, String.class),
392+
JB_PROCEDURE_TYPE(10, Short.class),
393+
JB_PROCEDURE_SOURCE(11, String.class),
392394
;
393395

394396
private final int position;
@@ -410,14 +412,36 @@ public Class<?> getColumnClass() {
410412
}
411413
}
412414

415+
/**
416+
* Returns the body of the stored procedure (after the first occurrence of {@code "\nAS\n"} or {@code "\nas\n"}).
417+
*
418+
* @param procedureSource
419+
* stored procedure source
420+
* @return body, or {@code null} if there was no occurrence of the expected token (see above)
421+
*/
422+
private static String extractBody(String procedureSource) {
423+
int index = procedureSource.indexOf("\nAS\n");
424+
int indexAs = procedureSource.indexOf("\nas\n");
425+
if (index == -1) {
426+
index = indexAs;
427+
} else if (indexAs < index && indexAs != -1) {
428+
index = indexAs;
429+
}
430+
431+
if (index == -1) return null;
432+
return procedureSource.substring(index + 4);
433+
}
434+
413435
private enum ProcedureTestData {
414436
NORMAL_PROC_NO_RETURN("NORMAL_PROC_NO_RETURN", List.of(CREATE_NORMAL_PROC_NO_RETURN)) {
415437
@Override
416438
Map<ProcedureMetaData, Object> getSpecificValidationRules(Map<ProcedureMetaData, Object> rules) {
417439
rules.put(ProcedureMetaData.PROCEDURE_NAME, "NORMAL_PROC_NO_RETURN");
418440
rules.put(ProcedureMetaData.PROCEDURE_TYPE, DatabaseMetaData.procedureNoResult);
441+
rules.put(ProcedureMetaData.JB_PROCEDURE_TYPE, FirebirdDatabaseMetaData.jbProcedureTypeExecutable);
419442
rules.put(ProcedureMetaData.SPECIFIC_NAME, ifSchemaElse(
420443
ObjectReference.of("PUBLIC", "NORMAL_PROC_NO_RETURN").toString(), "NORMAL_PROC_NO_RETURN"));
444+
rules.put(ProcedureMetaData.JB_PROCEDURE_SOURCE, extractBody(CREATE_NORMAL_PROC_NO_RETURN));
421445
return rules;
422446
}
423447
},
@@ -427,9 +451,11 @@ Map<ProcedureMetaData, Object> getSpecificValidationRules(Map<ProcedureMetaData,
427451
Map<ProcedureMetaData, Object> getSpecificValidationRules(Map<ProcedureMetaData, Object> rules) {
428452
rules.put(ProcedureMetaData.PROCEDURE_NAME, "NORMAL_PROC_WITH_RETURN");
429453
rules.put(ProcedureMetaData.PROCEDURE_TYPE, DatabaseMetaData.procedureReturnsResult);
454+
rules.put(ProcedureMetaData.JB_PROCEDURE_TYPE, FirebirdDatabaseMetaData.jbProcedureTypeExecutable);
430455
rules.put(ProcedureMetaData.REMARKS, "Some comment");
431456
rules.put(ProcedureMetaData.SPECIFIC_NAME, ifSchemaElse(
432457
ObjectReference.of("PUBLIC", "NORMAL_PROC_WITH_RETURN").toString(), "NORMAL_PROC_WITH_RETURN"));
458+
rules.put(ProcedureMetaData.JB_PROCEDURE_SOURCE, extractBody(CREATE_NORMAL_PROC_WITH_RETURN));
433459
return rules;
434460
}
435461

@@ -439,8 +465,10 @@ Map<ProcedureMetaData, Object> getSpecificValidationRules(Map<ProcedureMetaData,
439465
Map<ProcedureMetaData, Object> getSpecificValidationRules(Map<ProcedureMetaData, Object> rules) {
440466
rules.put(ProcedureMetaData.PROCEDURE_NAME, "quoted_proc_no_return");
441467
rules.put(ProcedureMetaData.PROCEDURE_TYPE, DatabaseMetaData.procedureNoResult);
468+
rules.put(ProcedureMetaData.JB_PROCEDURE_TYPE, FirebirdDatabaseMetaData.jbProcedureTypeExecutable);
442469
rules.put(ProcedureMetaData.SPECIFIC_NAME, ifSchemaElse(
443470
ObjectReference.of("PUBLIC", "quoted_proc_no_return").toString(), "quoted_proc_no_return"));
471+
rules.put(ProcedureMetaData.JB_PROCEDURE_SOURCE, extractBody(CREATE_QUOTED_PROC_NO_RETURN));
444472
return rules;
445473
}
446474
},
@@ -451,8 +479,11 @@ Map<ProcedureMetaData, Object> getSpecificValidationRules(Map<ProcedureMetaData,
451479
rules.put(ProcedureMetaData.PROCEDURE_CAT, "WITH$PROCEDURE");
452480
rules.put(ProcedureMetaData.PROCEDURE_NAME, "IN$PACKAGE");
453481
rules.put(ProcedureMetaData.PROCEDURE_TYPE, DatabaseMetaData.procedureReturnsResult);
482+
rules.put(ProcedureMetaData.JB_PROCEDURE_TYPE, FirebirdDatabaseMetaData.jbProcedureTypeExecutable);
454483
rules.put(ProcedureMetaData.SPECIFIC_NAME,
455484
ObjectReference.of(ifSchemaElse("PUBLIC", ""), "WITH$PROCEDURE", "IN$PACKAGE").toString());
485+
// No procedure body for packaged procedures
486+
rules.put(ProcedureMetaData.JB_PROCEDURE_SOURCE, null);
456487
return rules;
457488
}
458489

@@ -472,8 +503,10 @@ Map<ProcedureMetaData, Object> getSpecificValidationRules(Map<ProcedureMetaData,
472503
rules.put(ProcedureMetaData.PROCEDURE_SCHEM, "OTHER_SCHEMA");
473504
rules.put(ProcedureMetaData.PROCEDURE_NAME, "PROC_NO_RETURN");
474505
rules.put(ProcedureMetaData.PROCEDURE_TYPE, DatabaseMetaData.procedureNoResult);
506+
rules.put(ProcedureMetaData.JB_PROCEDURE_TYPE, FirebirdDatabaseMetaData.jbProcedureTypeExecutable);
475507
rules.put(ProcedureMetaData.SPECIFIC_NAME,
476508
ObjectReference.of("OTHER_SCHEMA", "PROC_NO_RETURN").toString());
509+
rules.put(ProcedureMetaData.JB_PROCEDURE_SOURCE, extractBody(CREATE_OTHER_SCHEMA_PROC_NO_RETURN));
477510
return rules;
478511
}
479512

0 commit comments

Comments
 (0)