Skip to content

Conversation

@homar
Copy link
Member

@homar homar commented Jun 2, 2025

Description

Add tables with authorization information to system.metadata #25907

Trino supports ALTER (TABLE | FUNCTION | SCHEMA) SET AUTHORIZATION for quite some time.
However there is no way to retrieve this information. This commit fixes this by
introducing system.metadata.(tables | schemas | functions)_authorization tables.

Additional context and related issues

Release notes

( ) This is not user-visible or is docs only, and no release notes are required.
( ) Release notes are required. Please propose a release note for me.
(x) Release notes are required, with the following suggested text:

## Section
* Introduces system.metadata.(tables | schemas | functions)_authorization tables that expose the information about the authorization for given entities.

@cla-bot cla-bot bot added the cla-signed label Jun 2, 2025
@homar homar force-pushed the homar/add_authorizations_metadata branch 2 times, most recently from a7392e0 to 2ff0516 Compare June 2, 2025 22:03
@homar homar changed the title Homar/add authorizations metadata Add tables with authorization information to system.metadata Jun 3, 2025
@homar homar force-pushed the homar/add_authorizations_metadata branch from 2ff0516 to d6e48b8 Compare June 3, 2025 10:39
@homar homar marked this pull request as ready for review June 3, 2025 10:40
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: put schema first, then tables and functions last?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'd say All infix is redundant

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: just combine maps instead of extracting this method?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this method actually needed if all it does is delegating to listCatalogNames and listSchemas?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it only does delegation BUT i would need to reuse this code in 3 places so I decided it is a good idea to actually extract it to a separated method

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the only test using blackhole catalog instead of memory_test, why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because I was not able to create a function in blackhole catalog so functions are tested using memory (and memory_test to check that table returns functions from different catalogs).
I can change this to also use memory

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This javadocs does not say more that the method name. I would drop it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is odd to pass QualifiedTablePrefix as it is not a table. I mean if one passed schema_name parameter then you should return just that schema.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when schema name is passed then it is easy, but this is an optimization for situation when only catalog is known, instead of returning all schemas from all catalogs when can just get schemas for a specific catalog

@homar homar force-pushed the homar/add_authorizations_metadata branch from d6e48b8 to 4bdaae9 Compare June 4, 2025 21:30
@homar homar force-pushed the homar/add_authorizations_metadata branch from 4bdaae9 to 91ca0d7 Compare June 4, 2025 22:30
@homar homar force-pushed the homar/add_authorizations_metadata branch from 91ca0d7 to 21bc502 Compare June 5, 2025 20:35
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you know what is domain 0?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't, I saw it in other places like

Domain catalogDomain = constraint.getDomain(0, VARCHAR);
and from my testing it looks like it works this way. So 0 is catalog, 1 is schema etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nice to enumerate only functions that in constraint

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nice to enumerate only schemas that in constraint

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nice to enumerate only tables that in constraint

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not use QualifiedTablePrefix here as table has no sense

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I believe it is a naming problem, I can copy code of QualifiedTablePrefix and name it QualifiedObjectPrefix but would that really make sense?
I may rename QualifiedTablePrefix to QualifiedObjectPrefix but I don't believe it is a big deal actually

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I believe it is a naming problem, I can copy code of QualifiedTablePrefix and name it QualifiedObjectPrefix but would that really make sense?

Yes. Please. Also you may want them to become records. So then there is not that much copying :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not use QualifiedTablePrefix here as table has no sense

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add test for VIEW and MATERIALIZED VIEW

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VIEWS are covered in testViewColumnAccessControl, unfortunately there is no available connector in TestAccessControl that can create MATERIALIZED VIEWs.. So testing that is nor feasible right now

@homar homar force-pushed the homar/add_authorizations_metadata branch from 21bc502 to b78b694 Compare June 19, 2025 22:07
@homar homar force-pushed the homar/add_authorizations_metadata branch from b78b694 to e94721b Compare June 19, 2025 23:22
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extracting this as variable would make the code more readable. I need to go up and down to understand what 0 or 2 means.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm.... maybe I was wrong. Now when I see this code I think we don't need to do it as it will be filtered anyway. When I requested filtering I wanted to avoid metadata calls, but once the call is done filtering more is not useful.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is better this way

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it unnecessarly complicates the logic here. I wanted to try eliminate the number of metadata calls. Filtering the set here does not improve the performance much and makes code here complex.

Please apply the same comment in other places.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comments. It could be a set.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extract variable for it too

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not record? So much boilerplate..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw?

@homar homar force-pushed the homar/add_authorizations_metadata branch from e94721b to d44f577 Compare June 20, 2025 09:57
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it unnecessarly complicates the logic here. I wanted to try eliminate the number of metadata calls. Filtering the set here does not improve the performance much and makes code here complex.

Please apply the same comment in other places.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of passing QualifiedObjectPrefix, pass String catalog, Optional<String> schema, or some QualifiedSchemaPrefix

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry but I don't agree. I believe QualifiedObjectPrefix should be treated as generic and could be used in any scenario - can represent as short path as the one to catalog and as long as the one to table. Currently it is kind of hardcoded but I believe it can be reimplemented to support prefixes of any length if necessary

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need this if you use record. Same for getters.

@homar homar force-pushed the homar/add_authorizations_metadata branch from d44f577 to 56e8649 Compare June 22, 2025 20:07
@homar homar force-pushed the homar/add_authorizations_metadata branch from 56e8649 to 98e94f5 Compare June 23, 2025 14:08
@homar homar requested a review from lukasz-walkiewicz June 23, 2025 22:05
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong. See e9b931f

It is obvious that query on system catalog in Trino fails in this case. We care here about the catalog from constraint.getDomain(0, VARCHAR);.

However ownership is taken from SystemSecurityMetadata not a connector metadata so we could potentially skip that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to filter it. You already passed schema filter to getSchemasAuthorizationInfo

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schemaName is taken from the constraint, it can be empty, in that case getSchemasAuthorizationInfo will return all schemas so I still need to filter. In case it is provided there will be only 1 result so it is fine

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same with filtering. You already passed prefix to getTablesAuthorizationInfo

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same answer, that filtering may not happen as the source of that value is in constraint TupleDomain which may be empty

Comment on lines 2897 to 2882
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        requireNonNull(prefix, "prefix is null");

        Optional<CatalogMetadata> catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
        if (catalog.filter(SYSTEM::equals).present) {
                return systemSecurityMetadata.getSchemasAuthorizationInfo(session, prefix);
        }
        return ImmutableSet.of();
        

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no need to write getters for record

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ehh i used copilot and it failed miserably.. (i did fail to as I didn't check what it did..)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no need to assign things in record constructor.

You can call requireNonNull if you like

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, right, again copilot generated too much

@homar homar force-pushed the homar/add_authorizations_metadata branch from 98e94f5 to 00ee142 Compare June 26, 2025 22:00
@homar homar force-pushed the homar/add_authorizations_metadata branch from 00ee142 to 44971df Compare June 27, 2025 07:21
@homar homar force-pushed the homar/add_authorizations_metadata branch from 44971df to f3d1e5f Compare June 27, 2025 16:56
homar added a commit to homar/trino that referenced this pull request Jun 27, 2025
…25907

Trino supports ALTER (TABLE | FUNCTION | SCHEMA) SET AUTHORIZATION for quite some time.
However there is no way to retrieve this information. This commit fixes this by
introducing system.metadata.(tables | schemas | functions)_authorization tables.
@homar homar force-pushed the homar/add_authorizations_metadata branch from f3d1e5f to 2df7e83 Compare June 27, 2025 20:00
Copy link
Member

@kokosing kokosing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good, the last part is to make sure that we use access control properly and we do have tests for the access control filterring.



@Test
public void testSchemasAuthorization()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a test where user has no access to a schema so they cannot see it in schemas_authorization?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check last assertion in this test

}

@Test
public void testTablesAuthorization()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a test where user has no access to a table/view so they cannot see it in tables_authorization?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check last assertion in this test

}

@Test
public void testFunctionsAuthorization()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a test where user has no access to a schema so they cannot see it in function_authorization?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check last assertion in this test

try {
return doGetFunctionsAuthorization(session, constraint);
}
catch (RuntimeException exception) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if RuntimeException is already TrinoException?

Map<String, Set<CatalogSchemaName>> groupedByCatalog = availableSchemas.stream()
.collect(groupingBy(CatalogSchemaName::getCatalogName, Collectors.toSet()));
ImmutableList.Builder<CatalogSchemaAuthorization> result = ImmutableList.builder();
for (String catalog : groupedByCatalog.keySet()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see io.trino.security.AccessControl#filterSchemas called below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's good because it is not here :) It is called above inside io.trino.metadata.MetadataListing#listAllAvailableSchemas

{
Domain catalogDomain = constraint.getDomain(0, VARCHAR);
Domain schemaDomain = constraint.getDomain(1, VARCHAR);
Set<CatalogSchemaName> availableSchemas = listAllAvailableSchemas(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this using io.trino.security.AccessControl#filterCatalogs or io.trino.security.AccessControl#filterSchemas?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both

Session session,
TupleDomain<Integer> constraint)
{
Set<CatalogSchemaName> availableSchemas = listAllAvailableSchemas(session,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this using io.trino.security.AccessControl#filterCatalogs or io.trino.security.AccessControl#filterSchemas?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both

ImmutableList.Builder<CatalogTableAuthorization> result = ImmutableList.builder();
availableSchemas.forEach(catalogSchemaName -> {
QualifiedTablePrefix prefix = new QualifiedTablePrefix(catalogSchemaName.getCatalogName(), Optional.of(catalogSchemaName.getSchemaName()), tableName);
Set<SchemaTableName> accessibleNames = listTables(session, metadata, accessControl, prefix);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see io.trino.security.AccessControl#filterTables called here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good, it is called inside io.trino.metadata.MetadataListing#listTables

public QualifiedSchemaPrefix(String catalogName, Optional<String> schemaName)
{
this.catalogName = checkCatalogName(catalogName);
this.schemaName = schemaName;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no need to assign anything here :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping

…25907

Trino supports ALTER (TABLE | FUNCTION | SCHEMA) SET AUTHORIZATION for quite some time.
However there is no way to retrieve this information. This commit fixes this by
introducing system.metadata.(tables | schemas | functions)_authorization tables.
@homar homar force-pushed the homar/add_authorizations_metadata branch from 2df7e83 to 56f5b80 Compare June 30, 2025 20:29
@kokosing kokosing merged commit 59a96c1 into trinodb:master Jul 1, 2025
95 checks passed
@kokosing
Copy link
Member

kokosing commented Jul 1, 2025

Thank you!

@github-actions github-actions bot added this to the 477 milestone Jul 1, 2025
chiahangchang pushed a commit to chiahangchang/trino that referenced this pull request Jul 2, 2025
…25907

Trino supports ALTER (TABLE | FUNCTION | SCHEMA) SET AUTHORIZATION for quite some time.
However there is no way to retrieve this information. This commit fixes this by
introducing system.metadata.(tables | schemas | functions)_authorization tables.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants