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
44package org .firebirdsql .jdbc .metadata ;
55
1010
1111import java .sql .ResultSet ;
1212import java .sql .SQLException ;
13+ import java .util .List ;
1314
1415import static org .firebirdsql .gds .ISCConstants .SQL_VARYING ;
1516import static org .firebirdsql .jdbc .metadata .FbMetadataConstants .OBJECT_NAME_LENGTH ;
2728 * @author Mark Rotteveel
2829 * @since 5
2930 */
30- public final class GetTablePrivileges extends AbstractMetadataMethod {
31+ public abstract sealed class GetTablePrivileges extends AbstractMetadataMethod {
3132
3233 private static final String TABLEPRIV = "TABLEPRIV" ;
3334
34- private static final RowDescriptor ROW_DESCRIPTOR = DbMetadataMediator .newRowDescriptorBuilder (8 )
35+ private static final RowDescriptor ROW_DESCRIPTOR = DbMetadataMediator .newRowDescriptorBuilder (9 )
3536 .at (0 ).simple (SQL_VARYING | 1 , OBJECT_NAME_LENGTH , "TABLE_CAT" , TABLEPRIV ).addField ()
3637 .at (1 ).simple (SQL_VARYING | 1 , OBJECT_NAME_LENGTH , "TABLE_SCHEM" , TABLEPRIV ).addField ()
3738 .at (2 ).simple (SQL_VARYING , OBJECT_NAME_LENGTH , "TABLE_NAME" , TABLEPRIV ).addField ()
@@ -40,63 +41,138 @@ public final class GetTablePrivileges extends AbstractMetadataMethod {
4041 .at (5 ).simple (SQL_VARYING , 31 , "PRIVILEGE" , TABLEPRIV ).addField ()
4142 .at (6 ).simple (SQL_VARYING , 3 , "IS_GRANTABLE" , TABLEPRIV ).addField ()
4243 .at (7 ).simple (SQL_VARYING , OBJECT_NAME_LENGTH , "JB_GRANTEE_TYPE" , TABLEPRIV ).addField ()
44+ .at (8 ).simple (SQL_VARYING , OBJECT_NAME_LENGTH , "JB_GRANTEE_SCHEMA" , TABLEPRIV ).addField ()
4345 .toRowDescriptor ();
4446
45- //@formatter:off
46- private static final String GET_TABLE_PRIVILEGES_START =
47- // Distinct is needed as we're selecting privileges for the table and columns of the table
48- "select distinct\n "
49- + " UP.RDB$RELATION_NAME as TABLE_NAME,\n "
50- + " UP.RDB$GRANTOR as GRANTOR,\n "
51- + " UP.RDB$USER as GRANTEE,\n "
52- + " UP.RDB$PRIVILEGE as PRIVILEGE,\n "
53- + " UP.RDB$GRANT_OPTION as IS_GRANTABLE,\n "
54- + " T.RDB$TYPE_NAME as JB_GRANTEE_TYPE\n "
55- + "from RDB$USER_PRIVILEGES UP\n "
56- + "left join RDB$TYPES T\n "
57- + " on T.RDB$FIELD_NAME = 'RDB$OBJECT_TYPE' and T.RDB$TYPE = UP.RDB$USER_TYPE \n "
58- // Other privileges don't make sense for table privileges
59- // TODO Consider including ALTER/DROP privileges
60- + "where UP.RDB$PRIVILEGE in ('A', 'D', 'I', 'R', 'S', 'U')\n "
61- // Only tables and views
62- + "and UP.RDB$OBJECT_TYPE in (0, 1)\n " ;
63-
64- // NOTE: Sort by user is not defined in JDBC, but we do this to ensure a consistent order for tests
65- private static final String GET_TABLE_PRIVILEGES_END = "order by RDB$RELATION_NAME, RDB$PRIVILEGE, RDB$USER" ;
66- //@formatter:on
67-
6847 private GetTablePrivileges (DbMetadataMediator mediator ) {
6948 super (ROW_DESCRIPTOR , mediator );
7049 }
7150
72- public ResultSet getTablePrivileges (String tableNamePattern ) throws SQLException {
51+ public final ResultSet getTablePrivileges (String schemaPattern , String tableNamePattern ) throws SQLException {
7352 if ("" .equals (tableNamePattern )) {
7453 return createEmpty ();
7554 }
76- Clause tableClause = new Clause ("RDB$RELATION_NAME" , tableNamePattern );
77-
78- String sql = GET_TABLE_PRIVILEGES_START
79- + tableClause .getCondition ("and " , "\n " )
80- + GET_TABLE_PRIVILEGES_END ;
81- MetadataQuery metadataQuery = new MetadataQuery (sql , Clause .parameters (tableClause ));
55+ MetadataQuery metadataQuery = createGetTablePrivilegesQuery (schemaPattern , tableNamePattern );
8256 return createMetaDataResultSet (metadataQuery );
8357 }
8458
59+ abstract MetadataQuery createGetTablePrivilegesQuery (String schemaPattern , String tableNamePattern );
60+
8561 @ Override
8662 RowValue createMetadataRow (ResultSet rs , RowValueBuilder valueBuilder ) throws SQLException {
8763 return valueBuilder
8864 .at (0 ).set (null )
89- .at (1 ).set ( null )
65+ .at (1 ).setString ( rs . getString ( "TABLE_SCHEM" ) )
9066 .at (2 ).setString (rs .getString ("TABLE_NAME" ))
9167 .at (3 ).setString (rs .getString ("GRANTOR" ))
9268 .at (4 ).setString (rs .getString ("GRANTEE" ))
9369 .at (5 ).setString (mapPrivilege (rs .getString ("PRIVILEGE" )))
9470 .at (6 ).setString (rs .getBoolean ("IS_GRANTABLE" ) ? "YES" : "NO" )
9571 .at (7 ).setString (rs .getString ("JB_GRANTEE_TYPE" ))
72+ .at (8 ).setString (rs .getString ("JB_GRANTEE_SCHEMA" ))
9673 .toRowValue (false );
9774 }
9875
9976 public static GetTablePrivileges create (DbMetadataMediator mediator ) {
100- return new GetTablePrivileges (mediator );
77+ // NOTE: Indirection through static method prevents unnecessary classloading
78+ if (mediator .getFirebirdSupportInfo ().isVersionEqualOrAbove (6 )) {
79+ return FB6 .createInstance (mediator );
80+ } else {
81+ return FB5 .createInstance (mediator );
82+ }
83+ }
84+
85+ /**
86+ * Implementation for Firebird 5.0 and older.
87+ */
88+ private static final class FB5 extends GetTablePrivileges {
89+
90+ // Distinct is needed as we're selecting privileges for the table and columns of the table
91+ private static final String GET_TABLE_PRIVILEGES_START_5 = """
92+ select distinct
93+ cast(null as char(1)) as TABLE_SCHEM,
94+ UP.RDB$RELATION_NAME as TABLE_NAME,
95+ UP.RDB$GRANTOR as GRANTOR,
96+ UP.RDB$USER as GRANTEE,
97+ UP.RDB$PRIVILEGE as PRIVILEGE,
98+ UP.RDB$GRANT_OPTION as IS_GRANTABLE,
99+ T.RDB$TYPE_NAME as JB_GRANTEE_TYPE,
100+ cast(null as char(1)) as JB_GRANTEE_SCHEMA
101+ from RDB$USER_PRIVILEGES UP
102+ left join RDB$TYPES T
103+ on T.RDB$FIELD_NAME = 'RDB$OBJECT_TYPE' and T.RDB$TYPE = UP.RDB$USER_TYPE
104+ where UP.RDB$PRIVILEGE in ('A', 'D', 'I', 'R', 'S', 'U') -- privileges relevant for tables
105+ and UP.RDB$OBJECT_TYPE in (0, 1) -- Only tables and views""" ;
106+
107+ // NOTE: Sort by user is not defined in JDBC, but we do this to ensure a consistent order for tests
108+ private static final String GET_TABLE_PRIVILEGES_END_5 =
109+ "\n order by UP.RDB$RELATION_NAME, UP.RDB$PRIVILEGE, UP.RDB$USER" ;
110+
111+ private FB5 (DbMetadataMediator mediator ) {
112+ super (mediator );
113+ }
114+
115+ private static GetTablePrivileges createInstance (DbMetadataMediator mediator ) {
116+ return new FB5 (mediator );
117+ }
118+
119+ @ Override
120+ MetadataQuery createGetTablePrivilegesQuery (String schemaPattern , String tableNamePattern ) {
121+ Clause tableClause = new Clause ("UP.RDB$RELATION_NAME" , tableNamePattern );
122+ String sql = GET_TABLE_PRIVILEGES_START_5
123+ + tableClause .getCondition ("\n and " , "" )
124+ + GET_TABLE_PRIVILEGES_END_5 ;
125+ return new MetadataQuery (sql , Clause .parameters (tableClause ));
126+ }
127+
128+ }
129+
130+ /**
131+ * Implementation for Firebird 6.0 and newer.
132+ */
133+ private static final class FB6 extends GetTablePrivileges {
134+
135+ // Distinct is needed as we're selecting privileges for the table and columns of the table
136+ private static final String GET_TABLE_PRIVILEGES_START_6 = """
137+ select distinct
138+ trim(trailing from UP.RDB$RELATION_SCHEMA_NAME) as TABLE_SCHEM,
139+ trim(trailing from UP.RDB$RELATION_NAME) as TABLE_NAME,
140+ trim(trailing from UP.RDB$GRANTOR) as GRANTOR,
141+ trim(trailing from UP.RDB$USER) as GRANTEE,
142+ UP.RDB$PRIVILEGE as PRIVILEGE,
143+ UP.RDB$GRANT_OPTION as IS_GRANTABLE,
144+ trim(trailing from T.RDB$TYPE_NAME) as JB_GRANTEE_TYPE,
145+ trim(trailing from UP.RDB$USER_SCHEMA_NAME) as JB_GRANTEE_SCHEMA
146+ from SYSTEM.RDB$USER_PRIVILEGES UP
147+ left join SYSTEM.RDB$TYPES T
148+ on T.RDB$FIELD_NAME = 'RDB$OBJECT_TYPE' and T.RDB$TYPE = UP.RDB$USER_TYPE
149+ where UP.RDB$PRIVILEGE in ('A', 'D', 'I', 'R', 'S', 'U') -- privileges relevant for tables
150+ and UP.RDB$OBJECT_TYPE in (0, 1) -- Only tables and views""" ;
151+
152+ // NOTE: Sort by user schema and user is not defined in JDBC, but we do this to ensure a consistent order for tests
153+ private static final String GET_TABLE_PRIVILEGES_END_6 =
154+ "\n order by UP.RDB$RELATION_SCHEMA_NAME, UP.RDB$RELATION_NAME, UP.RDB$PRIVILEGE, "
155+ + "UP.RDB$USER_SCHEMA_NAME nulls first, UP.RDB$USER" ;
156+
157+ private FB6 (DbMetadataMediator mediator ) {
158+ super (mediator );
159+ }
160+
161+ private static GetTablePrivileges createInstance (DbMetadataMediator mediator ) {
162+ return new FB6 (mediator );
163+ }
164+
165+ @ Override
166+ MetadataQuery createGetTablePrivilegesQuery (String schemaPattern , String tableNamePattern ) {
167+ var clauses = List .of (
168+ new Clause ("UP.RDB$RELATION_SCHEMA_NAME" , schemaPattern ),
169+ new Clause ("UP.RDB$RELATION_NAME" , tableNamePattern ));
170+ String sql = GET_TABLE_PRIVILEGES_START_6
171+ + (Clause .anyCondition (clauses ) ? "\n and " + Clause .conjunction (clauses ) : "" )
172+ + GET_TABLE_PRIVILEGES_END_6 ;
173+ return new MetadataQuery (sql , Clause .parameters (clauses ));
174+ }
175+
101176 }
177+
102178}
0 commit comments