Skip to content

Commit 93f7a16

Browse files
committed
#882 Add schema support for FBCallableStatement
1 parent f6033b9 commit 93f7a16

63 files changed

Lines changed: 2324 additions & 460 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

devdoc/jdp/jdp-2025-06-schema-support.adoc

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ This is done -- with some exceptions -- at prepare time.
4242
* Gbak has additional options to include/exclude (skip) schema data in backup or restore, similar to existing options to include/exclude tables
4343
* Gstat has additional options to specify a schema for operations involving tables
4444
* For validation, `val_sch_incl` and `val_sch_excl` (I don't think we use the equivalent,`val_tab_incl`/`val_tab_excl` in Jaybird, so might not be relevant)
45+
* Stored procedure resolution:
46+
** Unqualified stored procedures (`<name>`) are searched on the search path
47+
** Qualified stored procedures (`<schema-or-package>.<name>`) are first located by schema and name, and if not found, searched on the search path by package and name.
48+
** Scope-specified stored procedures are either only located by schema (`<schema>%SCHEMA.<name>`) or only searched on the search path by package and name (`<package>%PACKAGE.<name>`), not both.
49+
** Fully-qualified packaged stored procedures (`<schema>.<package>.<name>`) are located by schema, package and name
4550

4651
JDBC defines various methods, parameters, and return values or result set columns that are related to schemas.
4752

@@ -72,14 +77,14 @@ On Firebird 5.0 and older, this will be silently ignored.
7277
* In internal queries in Jaybird, and fully qualified object names, we'll use the regular -- unquoted -- identifier `SYSTEM`, even though `SYSTEM` is a SQL:2023 reserved word, to preserve dialect 1 compatibility.
7378
* `Connection.getSchema()` will return the result of `select CURRENT_SCHEMA from SYSTEM.RDB$DATABASE`;
7479
the connection will not store this value
75-
* `Connection.setSchema(String)` will query the current search path, if not previously called, it will prepend the schema name to the search path, otherwise it will _replace_ the previously prepended schema name.
80+
* `Connection.setSchema(String)` will query the current search path, and if not previously called, it will prepend the schema name to the search path, otherwise it will _replace_ the previously prepended schema name.
7681
The schema name is stored _only_ for this replacement operation (i.e. it will not be returned by `getSchema`!)
7782
+
7883
** The name must match exactly as is stored in the metadata (it is always case-sensitive!)
7984
** Jaybird will take care of quoting, and will always quote on dialect 3
8085
** Existence of the schema is **not** checked, so it is possible the current schema does not change with this operation, as `CURRENT_SCHEMA` reports the first _valid_ schema
8186
** JDBC specifies that "`__Calling ``setSchema`` has no effect on previously created or prepared Statement objects.__`";
82-
Jaybird cannot honour this requirement for plain `Statement`, as schema resolution is on prepare time (which for plain `Statement` is on execute), and not always for `CallableStatement` (as the implementation may delay actual prepare until execution).
87+
Jaybird cannot honour this requirement for plain `Statement`, as schema resolution is on prepare time (which for plain `Statement` is on execute), and not always for `CallableStatement` (as the implementation may delay actual prepare until execution, though we do try to identify the procedure when the callable statement is created and use that to fully-qualify the procedure).
8388
* Request `isc_info_sql_relation_schema` after preparing a query, record it in `FieldDescriptor`, and return it were relevant for JDBC (e.g. `ResultSetMetaData.getSchemaName(int)`)
8489
** For Firebird 5.0 and older, we need to ensure that JDBC methods continue to report the correct value (i.e. `""` for schema-less objects)
8590
* A Firebird 6.0 variant of the `DatabaseMetaData` and other internal metadata queries needs to be written to address at least the following things:
@@ -91,8 +96,15 @@ Jaybird cannot honour this requirement for plain `Statement`, as schema resoluti
9196
* `FirebirdConnection`
9297
** Added method `String getSearchPath()` to obtain the search path as reported by `RBB$GET_CONTEXT('SYSTEM', 'SEARCH_PATH')`, or `null` if schemas are not supported
9398
** Added method `List<String> getSearchPatList()` to obtain the search path as a list of unquoted object names, or empty list if schemas are not supported
99+
* `FBCallableStatement`
100+
** On creating the instance, the stored procedure is parsed and identified in the database metadata, including selectability, unless `ignoreProcedureType` is `true`
101+
*** Parsing of callable statements is changed to be able to identify schema, package and procedure, including scope specifiers
102+
** Jaybird emulates the lookup rules as used by Firebird, and -- if found -- records the identified procedure so subsequent internal prepares refer to the same procedure, even if the search path changes;
103+
this fulfills the JDBC requirements that a `CallableStatement` is not sensitive to current schema changes *if* Jaybird is able to identify the procedure, behaviour is undefined if the procedure was not found.
104+
** The API of `StoredProcedureMetaData` (internal API) is changed to not report selectability, but to update the `FBProcedureCall` instance with selectability and other information, like identified schema and/or package.
105+
** For qualified *and* unambiguous procedure reference, the selectability is cached *per connection*, for unqualified or ambiguous procedure reference, the lookup is performed on each `Connection.prepareCall`, to account for search path changes
106+
** Support for packages was missing in the handling of callable statements, and is added, also for older versions
94107
* TODO: Define effects for management API
95-
* TODO: Redesign retrieval of selectable procedure information (`StoredProcedureMetaDataFactory`) to be able to find stored procedures by schema
96108
* TODO: Add information to Jaybird manual
97109

98110
Note to self: use `// TODO Add schema support` in places that you identify need to get/improve schema support, while working on schema support elsewhere

src/docs/asciidoc/release_notes.adoc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,6 @@ If you are confronted with such a change, let us know on {firebird-java}[firebir
685685
* `FbWireOperations`
686686
** The `ProcessAttachCallback` parameter of `authReceiveResponse` was removed, as all implementations did nothing, and since protocol 13, it wasn't only called for the attach response
687687
** Interface `ProcessAttachCallback` was removed
688-
* Interface `StoredProcedureMetaData` was made package-private
689688
690689
[#breaking-changes-unlikely]
691690
=== Unlikely breaking changes
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// SPDX-FileCopyrightText: Copyright 2025 Mark Rotteveel
2+
// SPDX-License-Identifier: LGPL-2.1-or-later
3+
package org.firebirdsql.gds;
4+
5+
import org.firebirdsql.jaybird.util.BasicVersion;
6+
import org.jspecify.annotations.NonNull;
7+
8+
import java.io.Serial;
9+
import java.io.Serializable;
10+
import java.util.Objects;
11+
12+
/**
13+
* Abstract version for {@code major.minor} version information.
14+
*
15+
* @since 7
16+
*/
17+
public abstract class AbstractVersion implements Comparable<AbstractVersion>, Serializable {
18+
19+
@Serial
20+
private static final long serialVersionUID = 909074721396393952L;
21+
22+
private final int major;
23+
private final int minor;
24+
25+
protected AbstractVersion(int major, int minor) {
26+
this.major = major;
27+
this.minor = minor;
28+
}
29+
30+
/**
31+
* @return major version
32+
*/
33+
public final int major() {
34+
return major;
35+
}
36+
37+
/**
38+
* @return minor version
39+
*/
40+
public final int minor() {
41+
return minor;
42+
}
43+
44+
/**
45+
* Convenience method to check if the <em>major</em> of this version is equal to or larger than the specified
46+
* required version.
47+
*
48+
* @param requiredMajorVersion
49+
* required major version
50+
* @return {@code true} when current major is equal to or larger than required
51+
*/
52+
public final boolean isEqualOrAbove(int requiredMajorVersion) {
53+
return major >= requiredMajorVersion;
54+
}
55+
56+
/**
57+
* Convenience method to check if the <em>major.minor</em> of this version is equal to or larger than the specified
58+
* required version.
59+
*
60+
* @param requiredMajorVersion
61+
* required major version
62+
* @param requiredMinorVersion
63+
* required minor version
64+
* @return {@code true} when current major is larger than required, or major is same and minor is equal to or
65+
* larger than required
66+
*/
67+
public final boolean isEqualOrAbove(int requiredMajorVersion, int requiredMinorVersion) {
68+
return major > requiredMajorVersion
69+
|| (major == requiredMajorVersion && minor >= requiredMinorVersion);
70+
}
71+
72+
@Override
73+
public boolean equals(Object o) {
74+
if (o == null || getClass() != o.getClass()) return false;
75+
AbstractVersion that = (AbstractVersion) o;
76+
return major == that.major && minor == that.minor;
77+
}
78+
79+
@Override
80+
public int hashCode() {
81+
return Objects.hash(major, minor);
82+
}
83+
84+
@Override
85+
public String toString() {
86+
return major + "." + minor;
87+
}
88+
89+
/**
90+
* @return a - possibly cached - basic version ({@code major.minor} only) from the major and minor of this object
91+
*/
92+
public BasicVersion toBasicVersion() {
93+
return BasicVersion.of(this);
94+
}
95+
96+
/**
97+
* {@inheritDoc}
98+
* <p>
99+
* The default implementation compares major and minor; subclasses with more version fields may compare those
100+
* additional fields for instances of their own type and its subclasses. This can result in an unstable order, but
101+
* we accept that as we expect only to compare two versions of potentially differing types, or collections of
102+
* versions of the same type.
103+
* </p>
104+
* <p>
105+
* If a stable order is required, use a custom comparator that only compares major and minor.
106+
* </p>
107+
*/
108+
@Override
109+
public int compareTo(@NonNull AbstractVersion other) {
110+
int majorDiff = Integer.compare(this.major, other.major);
111+
if (majorDiff != 0) return majorDiff;
112+
return Integer.compare(this.minor, other.minor);
113+
}
114+
115+
}

src/main/org/firebirdsql/gds/impl/GDSHelper.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,15 @@ public void cancelOperation() throws SQLException {
142142

143143
// for DatabaseMetaData.
144144

145+
// TODO Consider removing some of these methods by pushing them down into FBDatabaseMetaData
146+
145147
/**
146148
* Get the name of the database product that we're connected to.
147149
*
148150
* @return The database product name (i.e. Firebird or Interbase)
149151
*/
150152
public String getDatabaseProductName() {
151-
return database.getServerVersion().getServerName();
153+
return getServerVersion().getServerName();
152154
}
153155

154156
/**
@@ -157,7 +159,7 @@ public String getDatabaseProductName() {
157159
* @return the database product version
158160
*/
159161
public String getDatabaseProductVersion() {
160-
return database.getServerVersion().getFullVersion();
162+
return getServerVersion().getFullVersion();
161163
}
162164

163165
/**
@@ -166,7 +168,7 @@ public String getDatabaseProductVersion() {
166168
* @return The major version number of the database
167169
*/
168170
public int getDatabaseProductMajorVersion() {
169-
return database.getServerVersion().getMajorVersion();
171+
return getServerVersion().getMajorVersion();
170172
}
171173

172174
/**
@@ -175,7 +177,7 @@ public int getDatabaseProductMajorVersion() {
175177
* @return The minor version number of the database
176178
*/
177179
public int getDatabaseProductMinorVersion() {
178-
return database.getServerVersion().getMinorVersion();
180+
return getServerVersion().getMinorVersion();
179181
}
180182

181183
/**
@@ -201,6 +203,15 @@ public int compareToVersion(int major, int minor) {
201203
return differenceMajor;
202204
}
203205

206+
/**
207+
* Get the server version.
208+
*
209+
* @return server version
210+
*/
211+
public GDSServerVersion getServerVersion() {
212+
return database.getServerVersion();
213+
}
214+
204215
/**
205216
* Compares the version of this database to the specified major version.
206217
* <p>

0 commit comments

Comments
 (0)