Skip to content

Commit 5329dad

Browse files
committed
#882 Schema support for getPrimaryKeys
1 parent 286f725 commit 5329dad

File tree

4 files changed

+110
-29
lines changed

4 files changed

+110
-29
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1428,7 +1428,7 @@ public ResultSet getVersionColumns(String catalog, String schema, String table)
14281428
*/
14291429
@Override
14301430
public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException {
1431-
return GetPrimaryKeys.create(getDbMetadataMediator()).getPrimaryKeys(table);
1431+
return GetPrimaryKeys.create(getDbMetadataMediator()).getPrimaryKeys(schema, table);
14321432
}
14331433

14341434
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ private static GetBestRowIdentifier createInstance(DbMetadataMediator mediator)
251251

252252
@Override
253253
MetadataQuery createGetPrimaryKeyIdentifierQuery(String schema, String table) {
254-
List<Clause> clauses = new ArrayList<>(2);
254+
var clauses = new ArrayList<Clause>(2);
255255
if (schema != null) {
256256
// NOTE: empty string will return no rows as required ("" retrieves those without a schema)
257257
clauses.add(Clause.equalsClause("RC.RDB$SCHEMA_NAME", schema));
Lines changed: 103 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// SPDX-FileCopyrightText: Copyright 2001-2024 Firebird development team and individual contributors
2-
// SPDX-FileCopyrightText: Copyright 2022-2024 Mark Rotteveel
1+
// SPDX-FileCopyrightText: Copyright 2001-2025 Firebird development team and individual contributors
2+
// SPDX-FileCopyrightText: Copyright 2022-2025 Mark Rotteveel
33
// SPDX-License-Identifier: LGPL-2.1-or-later
44
package org.firebirdsql.jdbc.metadata;
55

@@ -10,6 +10,7 @@
1010

1111
import java.sql.ResultSet;
1212
import java.sql.SQLException;
13+
import java.util.ArrayList;
1314

1415
import static org.firebirdsql.gds.ISCConstants.SQL_SHORT;
1516
import static org.firebirdsql.gds.ISCConstants.SQL_VARYING;
@@ -22,7 +23,7 @@
2223
* @author Mark Rotteveel
2324
* @since 5
2425
*/
25-
public final class GetPrimaryKeys extends AbstractMetadataMethod {
26+
public abstract class GetPrimaryKeys extends AbstractMetadataMethod {
2627

2728
private static final String COLUMNINFO = "COLUMNINFO";
2829

@@ -36,42 +37,25 @@ public final class GetPrimaryKeys extends AbstractMetadataMethod {
3637
.at(6).simple(SQL_VARYING, OBJECT_NAME_LENGTH, "JB_PK_INDEX_NAME", COLUMNINFO).addField()
3738
.toRowDescriptor();
3839

39-
private static final String GET_PRIMARY_KEYS_START = """
40-
select
41-
RC.RDB$RELATION_NAME as TABLE_NAME,
42-
ISGMT.RDB$FIELD_NAME as COLUMN_NAME,
43-
ISGMT.RDB$FIELD_POSITION + 1 as KEY_SEQ,
44-
RC.RDB$CONSTRAINT_NAME as PK_NAME,
45-
RC.RDB$INDEX_NAME as JB_PK_INDEX_NAME
46-
from RDB$RELATION_CONSTRAINTS RC
47-
inner join RDB$INDEX_SEGMENTS ISGMT
48-
on RC.RDB$INDEX_NAME = ISGMT.RDB$INDEX_NAME
49-
where RC.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'
50-
and\s""";
51-
52-
private static final String GET_PRIMARY_KEYS_END = "\norder by ISGMT.RDB$FIELD_NAME ";
53-
5440
private GetPrimaryKeys(DbMetadataMediator mediator) {
5541
super(ROW_DESCRIPTOR, mediator);
5642
}
5743

58-
public ResultSet getPrimaryKeys(String table) throws SQLException {
44+
public final ResultSet getPrimaryKeys(String schema, String table) throws SQLException {
5945
if (isNullOrEmpty(table)) {
6046
return createEmpty();
6147
}
62-
Clause tableClause = Clause.equalsClause("RC.RDB$RELATION_NAME", table);
63-
String sql = GET_PRIMARY_KEYS_START
64-
+ tableClause.getCondition(false)
65-
+ GET_PRIMARY_KEYS_END;
66-
MetadataQuery metadataQuery = new MetadataQuery(sql, Clause.parameters(tableClause));
48+
MetadataQuery metadataQuery = createGetPrimaryKeysQuery(schema, table);
6749
return createMetaDataResultSet(metadataQuery);
6850
}
6951

52+
abstract MetadataQuery createGetPrimaryKeysQuery(String schema, String table);
53+
7054
@Override
7155
RowValue createMetadataRow(ResultSet rs, RowValueBuilder valueBuilder) throws SQLException {
7256
return valueBuilder
7357
.at(0).set(null)
74-
.at(1).set(null)
58+
.at(1).setString(rs.getString("TABLE_SCHEM"))
7559
.at(2).setString(rs.getString("TABLE_NAME"))
7660
.at(3).setString(rs.getString("COLUMN_NAME"))
7761
.at(4).setShort(rs.getShort("KEY_SEQ"))
@@ -81,6 +65,99 @@ RowValue createMetadataRow(ResultSet rs, RowValueBuilder valueBuilder) throws SQ
8165
}
8266

8367
public static GetPrimaryKeys create(DbMetadataMediator mediator) {
84-
return new GetPrimaryKeys(mediator);
68+
// NOTE: Indirection through static method prevents unnecessary classloading
69+
if (mediator.getFirebirdSupportInfo().isVersionEqualOrAbove(6)) {
70+
return FB6.createInstance(mediator);
71+
} else {
72+
return FB5.createInstance(mediator);
73+
}
8574
}
75+
76+
/**
77+
* Implementation for Firebird 5.0 and older.
78+
*/
79+
private static final class FB5 extends GetPrimaryKeys {
80+
81+
private static final String GET_PRIMARY_KEYS_START_5 = """
82+
select
83+
cast(null as char(1)) as TABLE_SCHEM,
84+
RC.RDB$RELATION_NAME as TABLE_NAME,
85+
ISGMT.RDB$FIELD_NAME as COLUMN_NAME,
86+
ISGMT.RDB$FIELD_POSITION + 1 as KEY_SEQ,
87+
RC.RDB$CONSTRAINT_NAME as PK_NAME,
88+
RC.RDB$INDEX_NAME as JB_PK_INDEX_NAME
89+
from RDB$RELATION_CONSTRAINTS RC
90+
inner join RDB$INDEX_SEGMENTS ISGMT
91+
on RC.RDB$INDEX_NAME = ISGMT.RDB$INDEX_NAME
92+
where RC.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'
93+
and\s""";
94+
95+
private static final String GET_PRIMARY_KEYS_END_5 = "\norder by ISGMT.RDB$FIELD_NAME";
96+
97+
private FB5(DbMetadataMediator mediator) {
98+
super(mediator);
99+
}
100+
101+
private static GetPrimaryKeys createInstance(DbMetadataMediator mediator) {
102+
return new FB5(mediator);
103+
}
104+
105+
@Override
106+
MetadataQuery createGetPrimaryKeysQuery(String schema, String table) {
107+
Clause tableClause = Clause.equalsClause("RC.RDB$RELATION_NAME", table);
108+
String sql = GET_PRIMARY_KEYS_START_5
109+
+ tableClause.getCondition(false)
110+
+ GET_PRIMARY_KEYS_END_5;
111+
return new MetadataQuery(sql, Clause.parameters(tableClause));
112+
}
113+
114+
}
115+
116+
/**
117+
* Implementation for Firebird 6.0 and higher.
118+
*/
119+
private static final class FB6 extends GetPrimaryKeys {
120+
121+
private static final String GET_PRIMARY_KEYS_START_6 = """
122+
select
123+
trim(trailing from RC.RDB$SCHEMA_NAME) as TABLE_SCHEM,
124+
trim(trailing from RC.RDB$RELATION_NAME) as TABLE_NAME,
125+
trim(trailing from ISGMT.RDB$FIELD_NAME) as COLUMN_NAME,
126+
ISGMT.RDB$FIELD_POSITION + 1 as KEY_SEQ,
127+
trim(trailing from RC.RDB$CONSTRAINT_NAME) as PK_NAME,
128+
trim(trailing from RC.RDB$INDEX_NAME) as JB_PK_INDEX_NAME
129+
from SYSTEM.RDB$RELATION_CONSTRAINTS RC
130+
inner join SYSTEM.RDB$INDEX_SEGMENTS ISGMT
131+
on RC.RDB$SCHEMA_NAME = ISGMT.RDB$SCHEMA_NAME and RC.RDB$INDEX_NAME = ISGMT.RDB$INDEX_NAME
132+
where RC.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'
133+
and\s""";
134+
135+
// For consistent order (e.g. for tests), we're also sorting on schema name.
136+
// JDBC specifies that the result set is sorted on COLUMN_NAME, so we can't sort on schema first
137+
private static final String GET_PRIMARY_KEYS_END_6 = "\norder by ISGMT.RDB$FIELD_NAME, ISGMT.RDB$SCHEMA_NAME";
138+
139+
private FB6(DbMetadataMediator mediator) {
140+
super(mediator);
141+
}
142+
143+
private static GetPrimaryKeys createInstance(DbMetadataMediator mediator) {
144+
return new FB6(mediator);
145+
}
146+
147+
@Override
148+
MetadataQuery createGetPrimaryKeysQuery(String schema, String table) {
149+
var clauses = new ArrayList<Clause>(2);
150+
if (schema != null) {
151+
// NOTE: empty string will return no rows as required ("" retrieves those without a schema)
152+
clauses.add(Clause.equalsClause("RC.RDB$SCHEMA_NAME", schema));
153+
}
154+
clauses.add(Clause.equalsClause("RC.RDB$RELATION_NAME", table));
155+
String sql = GET_PRIMARY_KEYS_START_6
156+
+ Clause.conjunction(clauses)
157+
+ GET_PRIMARY_KEYS_END_6;
158+
return new MetadataQuery(sql, Clause.parameters(clauses));
159+
}
160+
161+
}
162+
86163
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: Copyright 2024 Mark Rotteveel
1+
// SPDX-FileCopyrightText: Copyright 2024-2025 Mark Rotteveel
22
// SPDX-License-Identifier: LGPL-2.1-or-later
33
package org.firebirdsql.jdbc;
44

@@ -20,6 +20,7 @@
2020

2121
import static java.util.Collections.unmodifiableMap;
2222
import static org.firebirdsql.common.FBTestProperties.getConnectionViaDriverManager;
23+
import static org.firebirdsql.common.FBTestProperties.ifSchemaElse;
2324
import static org.firebirdsql.common.assertions.ResultSetAssertions.assertNextRow;
2425
import static org.firebirdsql.common.assertions.ResultSetAssertions.assertNoNextRow;
2526

@@ -30,6 +31,8 @@
3031
*/
3132
class FBDatabaseMetaDataPrimaryKeysTest {
3233

34+
// TODO Add schema support: tests involving other schema
35+
3336
private static final String UNNAMED_CONSTRAINT_PREFIX = "INTEG_";
3437
private static final String UNNAMED_PK_INDEX_PREFIX = "RDB$PRIMARY";
3538

@@ -170,6 +173,7 @@ private void validateExpectedPrimaryKeys(String tableName, List<Map<PrimaryKeysM
170173
static {
171174
var defaults = new EnumMap<>(PrimaryKeysMetaData.class);
172175
Arrays.stream(PrimaryKeysMetaData.values()).forEach(key -> defaults.put(key, null));
176+
defaults.put(PrimaryKeysMetaData.TABLE_SCHEM, ifSchemaElse("PUBLIC", null));
173177
DEFAULT_COLUMN_VALUES = unmodifiableMap(defaults);
174178
}
175179

0 commit comments

Comments
 (0)