Skip to content

Conversation

@gertjanal
Copy link
Contributor

@gertjanal gertjanal commented Oct 28, 2025

Description

Some queries are quick and simple, like SELECT 1 and a query to list all tables. These queries should not have to wait in the queue to be executed, but should return quickly for a responsive UI.

With this PR, a resource selector queryText regex pattern can be added to match these simple types of queries and the query can be passed on to a priority resource group.

Additional context and related issues

This feature is also available in the Starburst version: https://docs.starburst.io/latest/admin/resource-groups.html#selector-rules

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:

## Resource groups
* Add `queryText` as a regular expression resource group selector. ({issue}`27129`)

Summary by Sourcery

Introduce queryText regex support for resource group selectors to route queries based on their SQL content, updating SPI, plugin, database mapping, dispatch logic, tests, and documentation.

New Features:

  • Add optional queryText regex in resource group selectors to match SQL query strings

Enhancements:

  • Extend SelectionCriteria to carry queryText and update StaticSelector, SelectorSpec, SelectorRecord, ResourceGroupsDao, and configuration managers to handle queryText patterns
  • Update DispatchManager to pass the SQL text into resource group selection logic
  • Add revapi exclusions for the changed SelectionCriteria constructor signature

Documentation:

  • Document the new queryText selector property in resource groups documentation

Tests:

  • Add unit tests for queryText matching and update existing selector tests to supply and verify the new queryText field

@cla-bot cla-bot bot added the cla-signed label Oct 28, 2025
@sourcery-ai
Copy link

sourcery-ai bot commented Oct 28, 2025

Reviewer's Guide

Introduce an optional queryText regex parameter to the resource group selection pipeline by extending the core SPI, dispatch manager, selector specs and records, DAOs, static and DB configuration managers, and updating matching logic, tests, and documentation to prioritize queries based on SQL text patterns.

Entity relationship diagram for updated selectors table

erDiagram
    SELECTORS {
        VARCHAR(512) resource_group_id
        INTEGER priority
        VARCHAR(512) user_regex
        VARCHAR(512) source_regex
        VARCHAR(512) original_user_regex
        VARCHAR(512) authenticated_user_regex
        VARCHAR(512) query_text
        VARCHAR(512) query_type
        VARCHAR(512) client_tags
        VARCHAR(1024) selector_resource_estimate
        VARCHAR(512) user_group_regex
    }
    RESOURCE_GROUPS {
        VARCHAR(512) resource_group_id
        VARCHAR(512) environment
    }
    SELECTORS ||--o{ RESOURCE_GROUPS : "resource_group_id"
Loading

Class diagram for SelectorSpec, SelectorRecord, and SelectionCriteria changes

classDiagram
    class SelectorSpec {
        +Optional<Pattern> originalUserRegex
        +Optional<Pattern> authenticatedUserRegex
        +Optional<Pattern> sourceRegex
        +Optional<Pattern> queryText
        +Optional<String> queryType
        +Optional<List<String>> clientTags
        +Optional<SelectorResourceEstimate> selectorResourceEstimate
        +ResourceGroupIdTemplate group
        +getQueryText()
    }
    class SelectorRecord {
        +Optional<Pattern> originalUserRegex
        +Optional<Pattern> authenticatedUserRegex
        +Optional<Pattern> sourceRegex
        +Optional<Pattern> queryText
        +Optional<String> queryType
        +Optional<List<String>> clientTags
        +Optional<SelectorResourceEstimate> selectorResourceEstimate
        +getQueryText()
    }
    class SelectionCriteria {
        +boolean authenticated
        +String user
        +Set<String> userGroups
        +Optional<String> source
        +Set<String> clientTags
        +ResourceEstimates resourceEstimates
        +String queryText
        +Optional<String> queryType
        +getQueryText()
    }
Loading

Flow diagram for query dispatch with queryText-based resource group selection

flowchart TD
    Q["Incoming SQL Query"] --> S["DispatchManager creates SelectionCriteria (includes queryText)"]
    S --> M["ResourceGroupSelector matches against queryText regex"]
    M --> G["Assigns to ResourceGroupIdTemplate (priority group if matched)"]
Loading

File-Level Changes

Change Details Files
Add queryText to SelectionCriteria and dispatch logic
  • declare queryText field
  • update constructors to accept queryText
  • add getter and include in toString
  • pass SQL text from DispatchManager into criteria
core/trino-spi/src/main/java/io/trino/spi/resourcegroups/SelectionCriteria.java
core/trino-main/src/main/java/io/trino/dispatcher/DispatchManager.java
Extend selector specs, records, and DAO to persist queryText
  • add queryText field and accessors in SelectorSpec
  • add queryText to SelectorRecord and row mapper
  • update SQL queries to include query_text column
  • adjust Revapi config for new constructor signature
plugin/trino-resource-group-managers/src/main/java/io/trino/plugin/resourcegroups/SelectorSpec.java
plugin/trino-resource-group-managers/src/main/java/io/trino/plugin/resourcegroups/db/SelectorRecord.java
plugin/trino-resource-group-managers/src/main/java/io/trino/plugin/resourcegroups/db/ResourceGroupsDao.java
core/trino-spi/pom.xml
Integrate queryText matching into StaticSelector and managers
  • add PatternMatcher for queryText in selector builder
  • incorporate queryText into AbstractResourceConfigurationManager build
  • propagate queryText into DbResourceGroupConfigurationManager
plugin/trino-resource-group-managers/src/main/java/io/trino/plugin/resourcegroups/StaticSelector.java
plugin/trino-resource-group-managers/src/main/java/io/trino/plugin/resourcegroups/AbstractResourceConfigurationManager.java
plugin/trino-resource-group-managers/src/main/java/io/trino/plugin/resourcegroups/db/DbResourceGroupConfigurationManager.java
Update and add tests for queryText behavior
  • introduce QUERY constant in tests
  • add TestStaticSelector.testQueryText
  • modify existing tests to supply queryText to SelectionCriteria
plugin/trino-resource-group-managers/src/test/java/io/trino/plugin/resourcegroups/TestStaticSelector.java
plugin/trino-resource-group-managers/src/test/java/io/trino/plugin/resourcegroups/db/TestDbSourceExactMatchSelector.java
plugin/trino-resource-group-managers/src/test/java/io/trino/plugin/resourcegroups/TestFileResourceGroupConfigurationManager.java
...multiple other test classes...
Document and configure new queryText option
  • add queryText description in admin docs
  • ensure Sphinx documentation includes new selector field
docs/src/main/sphinx/admin/resource-groups.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `plugin/trino-resource-group-managers/src/main/java/io/trino/plugin/resourcegroups/StaticSelector.java:67` </location>
<code_context>
         requireNonNull(sourceRegex, "sourceRegex is null");
         requireNonNull(clientTags, "clientTags is null");
         requireNonNull(selectorResourceEstimate, "selectorResourceEstimate is null");
+        requireNonNull(queryType, "queryText is null");
         requireNonNull(queryType, "queryType is null");
         this.group = requireNonNull(group, "group is null");
</code_context>

<issue_to_address>
**issue (bug_risk):** Incorrect requireNonNull parameter and error message for queryText.

The requireNonNull check for queryText should use queryText as the parameter and the error message should reference queryText, not queryType.
</issue_to_address>

### Comment 2
<location> `core/trino-spi/src/main/java/io/trino/spi/resourcegroups/SelectionCriteria.java:123` </location>
<code_context>
                 .add("source=" + source)
                 .add("clientTags=" + clientTags)
                 .add("resourceEstimates=" + resourceEstimates)
+                .add("queryText=queryText")
                 .add("queryType=" + queryType)
                 .toString();
</code_context>

<issue_to_address>
**issue (bug_risk):** Incorrect toString output for queryText field.

Replace .add("queryText=queryText") with .add("queryText=" + queryText) to show the actual field value.
</issue_to_address>

### Comment 3
<location> `plugin/trino-resource-group-managers/src/test/java/io/trino/plugin/resourcegroups/TestStaticSelector.java:332-341` </location>
<code_context>
     private static final long MEMORY_POOL_SIZE = 31415926535900L; // arbitrary uneven value for testing
+    private static final String QUERY = "select 1";

     @Test
     public void testInvalid()
</code_context>

<issue_to_address>
**suggestion (testing):** Missing edge case tests for queryText regex matching.

Add tests for null, empty, very long, partially matching, and special character queryText values to ensure robust regex handling.
</issue_to_address>

### Comment 4
<location> `plugin/trino-resource-group-managers/src/test/java/io/trino/plugin/resourcegroups/db/TestDbSourceExactMatchSelector.java:57-62` </location>
<code_context>
+        assertThat(selector.match(new SelectionCriteria(true, "testuser", ImmutableSet.of(), "testuser", Optional.empty(), Optional.of("@test@test_pipeline"), ImmutableSet.of("tag"), EMPTY_RESOURCE_ESTIMATES, QUERY, Optional.empty()))).isEqualTo(Optional.empty());
</code_context>

<issue_to_address>
**suggestion (testing):** No tests for queryText matching in DB selectors.

Please add tests to ensure DB selectors handle queryText regex matching correctly, including cases with null or empty queryText.
</issue_to_address>

### Comment 5
<location> `plugin/trino-resource-group-managers/src/test/java/io/trino/plugin/resourcegroups/db/TestDbResourceGroupConfigurationManager.java:102-105` </location>
<code_context>
         assertThat(manager.getSelectors()).hasSize(1);
         ResourceGroupSelector prodSelector = manager.getSelectors().get(0);
-        ResourceGroupId prodResourceGroupId = prodSelector.match(new SelectionCriteria(true, "prod_user", ImmutableSet.of(), "prod_user", Optional.empty(), Optional.empty(), ImmutableSet.of(), EMPTY_RESOURCE_ESTIMATES, Optional.empty())).get().getResourceGroupId();
+        ResourceGroupId prodResourceGroupId = prodSelector.match(new SelectionCriteria(true, "prod_user", ImmutableSet.of(), "prod_user", Optional.empty(), Optional.empty(), ImmutableSet.of(), EMPTY_RESOURCE_ESTIMATES, QUERY, Optional.empty())).get().getResourceGroupId();
         assertThat(prodResourceGroupId.toString()).isEqualTo("prod_global");

</code_context>

<issue_to_address>
**suggestion (testing):** No tests for queryText-based selector configuration in DB resource group manager.

Add tests to cover both matching and non-matching scenarios for selectors using queryText regex in the DB resource group manager.
</issue_to_address>

### Comment 6
<location> `plugin/trino-resource-group-managers/src/test/java/io/trino/plugin/resourcegroups/db/TestResourceGroupsDao.java:131-135` </location>
<code_context>
                         Optional.of(Pattern.compile(".*")),
                         Optional.empty(),
                         Optional.empty(),
+                        Optional.empty(),
</code_context>

<issue_to_address>
**suggestion (testing):** No test for queryText field persistence in selector DAO.

Add tests to ensure queryText is properly handled in the DAO, covering insert, update, delete, and edge cases like null and empty strings.

```suggestion
                        Optional.of(Pattern.compile(".*")),
                        Optional.empty(),
                        Optional.empty(),
                        Optional.empty(),
                        Optional.empty()));

    @Test
    public void testInsertSelectorWithQueryText() {
        SelectorRecord record = new SelectorRecord(
                Optional.of(Pattern.compile("user1")),
                Optional.of(Pattern.compile("role1")),
                Optional.of(Pattern.compile("source1")),
                Optional.of("SELECT * FROM test_table"),
                Optional.of(EXPLAIN.name())
        );
        long id = dao.insertSelector(record);
        SelectorRecord loaded = dao.getSelector(id);
        assertEquals(loaded.getQueryText(), Optional.of("SELECT * FROM test_table"));
    }

    @Test
    public void testUpdateSelectorQueryText() {
        SelectorRecord record = new SelectorRecord(
                Optional.of(Pattern.compile("user2")),
                Optional.of(Pattern.compile("role2")),
                Optional.of(Pattern.compile("source2")),
                Optional.of("SELECT 1"),
                Optional.of(EXPLAIN.name())
        );
        long id = dao.insertSelector(record);
        SelectorRecord updated = new SelectorRecord(
                record.getUserRegex(),
                record.getRoleRegex(),
                record.getSourceRegex(),
                Optional.of("SELECT 2"),
                record.getClientTags()
        );
        dao.updateSelector(id, updated);
        SelectorRecord loaded = dao.getSelector(id);
        assertEquals(loaded.getQueryText(), Optional.of("SELECT 2"));
    }

    @Test
    public void testDeleteSelectorWithQueryText() {
        SelectorRecord record = new SelectorRecord(
                Optional.of(Pattern.compile("user3")),
                Optional.of(Pattern.compile("role3")),
                Optional.of(Pattern.compile("source3")),
                Optional.of("DELETE FROM test_table"),
                Optional.of(EXPLAIN.name())
        );
        long id = dao.insertSelector(record);
        dao.deleteSelector(id);
        assertNull(dao.getSelector(id));
    }

    @Test
    public void testSelectorWithNullQueryText() {
        SelectorRecord record = new SelectorRecord(
                Optional.of(Pattern.compile("user4")),
                Optional.of(Pattern.compile("role4")),
                Optional.of(Pattern.compile("source4")),
                Optional.empty(),
                Optional.of(EXPLAIN.name())
        );
        long id = dao.insertSelector(record);
        SelectorRecord loaded = dao.getSelector(id);
        assertEquals(loaded.getQueryText(), Optional.empty());
    }

    @Test
    public void testSelectorWithEmptyQueryText() {
        SelectorRecord record = new SelectorRecord(
                Optional.of(Pattern.compile("user5")),
                Optional.of(Pattern.compile("role5")),
                Optional.of(Pattern.compile("source5")),
                Optional.of(""),
                Optional.of(EXPLAIN.name())
        );
        long id = dao.insertSelector(record);
        SelectorRecord loaded = dao.getSelector(id);
        assertEquals(loaded.getQueryText(), Optional.of(""));
    }
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@gertjanal gertjanal force-pushed the resource-selector-query-text branch from 44baab8 to 3df6a9b Compare October 28, 2025 09:00
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't have any tests that actually leverage your new functionality; it only updates existing tests. We should update H2ResourceGroupsDao to support the new regex field, and add corresponding test cases here as well as TestResourceGroupsDao

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can't really figure out how to add a test to TestResourceGroupsDao, there is no test for any other pattern in that class. Can you tell me how to fix this?

Copy link
Member

Choose a reason for hiding this comment

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

@gertjanal gertjanal force-pushed the resource-selector-query-text branch from 3df6a9b to 7b80187 Compare October 29, 2025 21:51
@gertjanal gertjanal force-pushed the resource-selector-query-text branch from 7b80187 to 4fcae56 Compare November 1, 2025 19:59
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.

2 participants