Skip to content

sql: add parser grammar and wire up for SHOW USERS clauses#166416

Merged
trunk-io[bot] merged 2 commits into
cockroachdb:masterfrom
souravcrl:show-users-enhance
Apr 21, 2026
Merged

sql: add parser grammar and wire up for SHOW USERS clauses#166416
trunk-io[bot] merged 2 commits into
cockroachdb:masterfrom
souravcrl:show-users-enhance

Conversation

@souravcrl
Copy link
Copy Markdown
Contributor

@souravcrl souravcrl commented Mar 23, 2026

sql: add parser grammar for SHOW USERS provisioning clauses

Extend the SQL parser grammar to support optional WITH clauses
and LIMIT on the SHOW USERS statement:

SHOW USERS [WITH ] [LIMIT ]

Options:
SOURCE =
LAST LOGIN BEFORE

Options are comma-separated and parsed into ShowUsersOptions AST
nodes defined in a prior commit. The grammar follows the same
pattern as opt_with_show_backup_options for consistency.

sql: wire up SHOW USERS delegation and add e2e tests

Wire up the TryDelegate dispatch to route ShowUsers AST nodes
through delegateShowRolesExtended, which applies provisioning
filters when WITH options are present.

Add end-to-end logic tests that exercise the full SHOW USERS
WITH clause pipeline from parsing through delegation to execution:

  • SOURCE filter matching users with specific PROVISIONSRC values
  • SOURCE filter with non-matching value returns empty results
  • LAST LOGIN BEFORE filter with threshold above/below login time
  • LIMIT clause restricting result count
  • LIMIT without WITH clause
  • Notice emission with extended form

Informs #150604

Release note (sql change): SHOW USERS now supports optional
filtering clauses for user provisioning workflows:

SHOW USERS [WITH ] [LIMIT ]

Options (comma-separated):

  • SOURCE = : filter users by their provisioning source
    (PROVISIONSRC role option value), e.g. 'ldap:ldap.example.com'.
  • LAST LOGIN BEFORE : filter users whose estimated
    last login time is before the given timestamp. Users who have
    never logged in (NULL estimated_last_login_time) are excluded.

Examples:

-- Find all LDAP-provisioned users
SHOW USERS WITH SOURCE = 'ldap:ldap.example.com'

-- Find dormant users who haven't logged in since Jan 2024
SHOW USERS WITH LAST LOGIN BEFORE '2024-01-01'

-- Combine filters with a limit
SHOW USERS WITH SOURCE = 'ldap:ldap.example.com',
LAST LOGIN BEFORE '2024-06-01' LIMIT 100

The original SHOW USERS behavior (no clauses) is unchanged.

@trunk-io
Copy link
Copy Markdown
Contributor

trunk-io Bot commented Mar 23, 2026

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

@souravcrl souravcrl requested a review from sanchit-CRL March 23, 2026 10:58
@cockroach-teamcity
Copy link
Copy Markdown
Member

This change is Reviewable

@souravcrl souravcrl marked this pull request as ready for review March 23, 2026 10:59
@souravcrl souravcrl requested a review from a team as a code owner March 23, 2026 10:59
@souravcrl souravcrl requested review from DrewKimball and removed request for a team March 23, 2026 10:59
@yuzefovich yuzefovich requested review from a team and removed request for DrewKimball March 23, 2026 23:01
@rafiss rafiss self-requested a review March 25, 2026 22:05
Copy link
Copy Markdown
Collaborator

@rafiss rafiss left a comment

Choose a reason for hiding this comment

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

@rafiss made 6 comments.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on sanchit-CRL and souravcrl).


-- commits line 23 at r4:
nit: the PR body says "informs" -- ideally these would be consistent


pkg/sql/delegate/show_roles.go line 67 at r4 (raw file):

			tsStr := tree.AsStringWithFlags(n.Options.LastAccessOlderThan, tree.FmtBareStrings)
			whereExprs = append(whereExprs, fmt.Sprintf(
				"u.estimated_last_login_time < %s::TIMESTAMPTZ",

nit: it could be helpful to add a comment saying that users with a NULL estimated_last_login_time will be excluded from the result.


pkg/sql/sem/tree/show.go line 1013 at r4 (raw file):

// ShowUsersOptions describes options for the SHOW USERS statement.
type ShowUsersOptions struct {
	Source              Expr // SOURCE = 'ldap:...'

nit: this just echoes the syntax rather than explaining semantics. Consider adding what the field represents (e.g., "filters users by their PROVISIONSRC role option value").


pkg/sql/logictest/testdata/logic_test/user line 256 at r4 (raw file):

subtest end

subtest show_users_provisioning_filters

please add an additional test case that verifies that the "last access before" syntax actually works.

you can use the let syntax in the logic test framework to capture the login time. for example:

# The estimated_last_login_time is not guaranteed to be populated synchronously,
# so we poll until testuser's entry was updated with the last login time.
query I retry
SELECT count(*) FROM system.users WHERE estimated_last_login_time IS NOT NULL AND username = 'testuser2'
----
1

let $login_time
SELECT estimated_last_login_time FROM system.users WHERE username = 'testuser2'

query TTT
SELECT username, options, member_of FROM [SHOW USERS WITH LAST LOGIN BEFORE $login_time::timestamptz + '1 minute'::interval]

pkg/sql/delegate/show_roles_test.go line 175 at r4 (raw file):

	// escaped via lexbase.EscapeSQLString.
	n := &tree.ShowUsers{
		Options: &tree.ShowUsersOptions{

nit: the SQL injection test only covers Source, not LastAccessOlderThan. Add a case with a malicious timestamp value like "2024-01-01' OR 1=1 --" to TestDelegateShowRolesExtendedSQLInjection.


pkg/sql/parser/sql.y line 10258 at r4 (raw file):

    $$.val = &tree.ShowUsersOptions{Source: $3.expr()}
  }
| LAST ACCESS TIME OLDER THAN a_expr

nit: this syntax is a little verbose. how about SHOW USERS WITH LAST LOGIN BEFORE a_expr

(consider updating the go struct names if you change this syntax)

@souravcrl souravcrl force-pushed the show-users-enhance branch from 4f37fb6 to 16308ac Compare March 27, 2026 07:20
@souravcrl souravcrl requested a review from rafiss March 27, 2026 07:45
Copy link
Copy Markdown
Contributor Author

@souravcrl souravcrl left a comment

Choose a reason for hiding this comment

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

@souravcrl made 6 comments and resolved 4 discussions.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss and sanchit-CRL).


pkg/sql/delegate/show_roles.go line 67 at r4 (raw file):

Previously, rafiss (Rafi Shamim) wrote…

nit: it could be helpful to add a comment saying that users with a NULL estimated_last_login_time will be excluded from the result.

done


pkg/sql/parser/sql.y line 10258 at r4 (raw file):

Previously, rafiss (Rafi Shamim) wrote…

nit: this syntax is a little verbose. how about SHOW USERS WITH LAST LOGIN BEFORE a_expr

(consider updating the go struct names if you change this syntax)

done


pkg/sql/sem/tree/show.go line 1013 at r4 (raw file):

Previously, rafiss (Rafi Shamim) wrote…

nit: this just echoes the syntax rather than explaining semantics. Consider adding what the field represents (e.g., "filters users by their PROVISIONSRC role option value").

done


pkg/sql/logictest/testdata/logic_test/user line 256 at r4 (raw file):

Previously, rafiss (Rafi Shamim) wrote…

please add an additional test case that verifies that the "last access before" syntax actually works.

you can use the let syntax in the logic test framework to capture the login time. for example:

# The estimated_last_login_time is not guaranteed to be populated synchronously,
# so we poll until testuser's entry was updated with the last login time.
query I retry
SELECT count(*) FROM system.users WHERE estimated_last_login_time IS NOT NULL AND username = 'testuser2'
----
1

let $login_time
SELECT estimated_last_login_time FROM system.users WHERE username = 'testuser2'

query TTT
SELECT username, options, member_of FROM [SHOW USERS WITH LAST LOGIN BEFORE $login_time::timestamptz + '1 minute'::interval]

done


pkg/sql/delegate/show_roles_test.go line 175 at r4 (raw file):

Previously, rafiss (Rafi Shamim) wrote…

nit: the SQL injection test only covers Source, not LastAccessOlderThan. Add a case with a malicious timestamp value like "2024-01-01' OR 1=1 --" to TestDelegateShowRolesExtendedSQLInjection.

done


pkg/sql/delegate/show_roles.go line 84 at r8 (raw file):

	var limitClause string
	if n.Limit != nil && n.Limit.Count != nil {

@rafiss I had a question if we should also validate n.Limit.Offset here? cc: @sanchit-CRL

Copy link
Copy Markdown
Contributor Author

@souravcrl souravcrl left a comment

Choose a reason for hiding this comment

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

@souravcrl made 1 comment.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss and sanchit-CRL).


-- commits line 23 at r4:

Previously, rafiss (Rafi Shamim) wrote…

nit: the PR body says "informs" -- ideally these would be consistent

Apologies, this has already merged. I will take care from next time.

@souravcrl souravcrl force-pushed the show-users-enhance branch from 16308ac to 6cdc8ca Compare March 27, 2026 09:39
Copy link
Copy Markdown
Collaborator

@rafiss rafiss left a comment

Choose a reason for hiding this comment

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

nice work!

@rafiss made 2 comments.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on sanchit-CRL).


pkg/sql/delegate/show_roles.go line 84 at r8 (raw file):

Previously, souravcrl wrote…

@rafiss I had a question if we should also validate n.Limit.Offset here? cc: @sanchit-CRL

validation seems reasonable to add. what did you have in mind?

@souravcrl souravcrl requested a review from rafiss April 7, 2026 10:55
Copy link
Copy Markdown
Contributor Author

@souravcrl souravcrl left a comment

Choose a reason for hiding this comment

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

@souravcrl made 1 comment.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss and sanchit-CRL).


pkg/sql/delegate/show_roles.go line 84 at r8 (raw file):

Previously, rafiss (Rafi Shamim) wrote…

validation seems reasonable to add. what did you have in mind?

I am not sure exactly how to achieve this. Is there an existing sql statement leveraging this and should we try to clone that here?

@souravcrl souravcrl force-pushed the show-users-enhance branch 2 times, most recently from cbfcdca to 762c2ff Compare April 7, 2026 11:58
@cockroach-teamcity cockroach-teamcity added the X-perf-gain Microbenchmarks CI: Added if a performance gain is detected label Apr 7, 2026
Copy link
Copy Markdown
Collaborator

@rafiss rafiss left a comment

Choose a reason for hiding this comment

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

@rafiss made 1 comment.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on sanchit-CRL).


pkg/sql/delegate/show_roles.go line 84 at r8 (raw file):
I'm still not sure what your idea is -- what are you thinking of validating?

I am not sure exactly how to achieve this. Is there an existing sql statement leveraging this

What does "this" refer to?

@rafiss rafiss requested a review from spilchen April 7, 2026 15:23
@souravcrl souravcrl requested a review from rafiss April 7, 2026 18:26
Copy link
Copy Markdown
Contributor Author

@souravcrl souravcrl left a comment

Choose a reason for hiding this comment

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

@souravcrl made 1 comment.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss, sanchit-CRL, and spilchen).


pkg/sql/delegate/show_roles.go line 84 at r8 (raw file):
Previously you mentioned

validation seems reasonable to add

So, I wanted to see if we are doing this elsewhere in the code and how this could improve the implementation. This was originally @sanchit-CRL 's idea that we should have this, so adding him here to chime in his thoughts.

Copy link
Copy Markdown
Contributor

@spilchen spilchen left a comment

Choose a reason for hiding this comment

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

@spilchen partially reviewed 3 files and made 4 comments.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss, sanchit-CRL, and souravcrl).


pkg/sql/delegate/delegate.go line 156 at r13 (raw file):

	case *tree.ShowUsers:
		return d.delegateShowRolesExtended(t)

one thing I found confusing was before SHOW ROLES and SHOW USERS were interchangeable. But now the extended syntax only applies to SHOW USERS, not SHOW ROLES. Perhaps that is intentional and by design. But the extended support was added to the function name delegateShowRolesExtended. So, I am a little confused.

I think one of two things has to happen:

  • extend the SHOW ROLES syntax to have the extended options
  • rename delegateShowRolesExtended to delegateShowUsersExtended so that it's clear this extended syntax only applies to SHOW USERS

pkg/sql/logictest/testdata/logic_test/user line 256 at r13 (raw file):

subtest end

subtest show_users_provisioning_filters

do we have a test that has multiple options with SHOW USER? e.g. SOURCE = ..., LAST LOGIN BEFORE ...


pkg/sql/parser/testdata/show line 669 at r13 (raw file):


parse
SHOW USERS WITH SOURCE = 'ldap:ldap.example.com', LAST LOGIN BEFORE '2024-01-01'

can we have a test for duplicate options? I believe it should error out


pkg/sql/delegate/show_roles.go line 0 at r13 (raw file):
the changes in this file was reverted. But I see a TODO in there that should probably removed now:

// TODO(sourav): Wire this into the delegate dispatch for ShowUsers;
// currently only exercised by unit tests.
func (d *delegator) delegateShowRolesExtended(n *tree.ShowUsers) (tree.Statement, error) {

@rafiss
Copy link
Copy Markdown
Collaborator

rafiss commented Apr 8, 2026

Nice catch -- we should continue to treat user and role as identical in all our syntax. So let's go with the first option: extend the SHOW ROLES syntax to have the extended options.

@souravcrl souravcrl requested a review from spilchen April 9, 2026 11:13
Copy link
Copy Markdown
Contributor Author

@souravcrl souravcrl left a comment

Choose a reason for hiding this comment

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

@souravcrl made 1 comment.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss, sanchit-CRL, and spilchen).


pkg/sql/delegate/delegate.go line 156 at r13 (raw file):

Previously, spilchen wrote…

one thing I found confusing was before SHOW ROLES and SHOW USERS were interchangeable. But now the extended syntax only applies to SHOW USERS, not SHOW ROLES. Perhaps that is intentional and by design. But the extended support was added to the function name delegateShowRolesExtended. So, I am a little confused.

I think one of two things has to happen:

  • extend the SHOW ROLES syntax to have the extended options
  • rename delegateShowRolesExtended to delegateShowUsersExtended so that it's clear this extended syntax only applies to SHOW USERS

Thanks for the note! You're right — SHOW ROLES and SHOW USERS have always been interchangeable, so it doesn't make sense for the extended syntax to only apply to SHOW USERS. I went with option 1: extending
SHOW ROLES to support the same provisioning clauses.

▎ The changes are split across 3 PRs (stacked on top of this one), mirroring the structure of the original SHOW USERS PRs:

▎ - #168022 — Extends the ShowRoles AST node with Options and Limit fields
▎ - #168023 — Refactors delegateShowRolesExtended to accept (options, limit) params so it serves both SHOW USERS and SHOW ROLES, and wires up the SHOW ROLES dispatch path
▎ - #168024 — Adds parser grammar for SHOW ROLES [WITH ] [LIMIT ], parser tests (including duplicate option error cases), and e2e logic tests covering SHOW ROLES with SOURCE, LAST LOGIN
BEFORE, and combined options

Copy link
Copy Markdown
Collaborator

@sanchit-CRL sanchit-CRL left a comment

Choose a reason for hiding this comment

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

@sanchit-CRL made 1 comment.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss, souravcrl, and spilchen).


pkg/sql/delegate/show_roles.go line 84 at r8 (raw file):

Previously, souravcrl wrote…

Previously you mentioned

validation seems reasonable to add

So, I wanted to see if we are doing this elsewhere in the code and how this could improve the implementation. This was originally @sanchit-CRL 's idea that we should have this, so adding him here to chime in his thoughts.

My question in the other PR was whether we should reject n.Limit.Offset if that has been provided in the query explicitly. So should that validation be added here.

@souravcrl souravcrl force-pushed the show-users-enhance branch 2 times, most recently from bdea500 to d4f3a9d Compare April 13, 2026 09:11
Copy link
Copy Markdown
Contributor Author

@souravcrl souravcrl left a comment

Choose a reason for hiding this comment

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

@souravcrl made 3 comments.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss and spilchen).


pkg/sql/delegate/show_roles.go line at r13 (raw file):

Previously, spilchen wrote…

the changes in this file was reverted. But I see a TODO in there that should probably removed now:

// TODO(sourav): Wire this into the delegate dispatch for ShowUsers;
// currently only exercised by unit tests.
func (d *delegator) delegateShowRolesExtended(n *tree.ShowUsers) (tree.Statement, error) {

done


pkg/sql/logictest/testdata/logic_test/user line 256 at r13 (raw file):

Previously, spilchen wrote…

do we have a test that has multiple options with SHOW USER? e.g. SOURCE = ..., LAST LOGIN BEFORE ...

have added.


pkg/sql/parser/testdata/show line 669 at r13 (raw file):

Previously, spilchen wrote…

can we have a test for duplicate options? I believe it should error out

done

@blathers-crl
Copy link
Copy Markdown

blathers-crl Bot commented Apr 20, 2026

Detected infrastructure failure (matched: self-hosted runner lost communication with the server). Automatically rerunning failed jobs. (run link)

Add grammar rules to sql.y for the SHOW USERS WITH ... syntax:

  SHOW USERS WITH SOURCE = <expr>, LAST LOGIN BEFORE <expr> LIMIT <n>

Options are parsed into ShowUsersOptions via the show_users_option
production rule. Multiple options are comma-separated and validated
for duplicates via CombineWith.

Update parser testdata with parse round-trip tests for all clause
combinations.

Epic: CRDB-52460

Release note (sql change): SHOW USERS now supports optional
filtering clauses for user provisioning workflows:

  SHOW USERS [WITH <options>] [LIMIT <n>]

Options (comma-separated):
- SOURCE = <string>: filter users by their provisioning source
  (PROVISIONSRC role option value), e.g. 'ldap:ldap.example.com'.
- LAST LOGIN BEFORE <timestamp>: filter users whose estimated
  last login time is before the given timestamp. Users who have
  never logged in (NULL estimated_last_login_time) are excluded.

Examples:

  -- Find all LDAP-provisioned users
  SHOW USERS WITH SOURCE = 'ldap:ldap.example.com'

  -- Find dormant users who haven't logged in since Jan 2024
  SHOW USERS WITH LAST LOGIN BEFORE '2024-01-01'

  -- Combine filters with a limit
  SHOW USERS WITH SOURCE = 'ldap:ldap.example.com',
    LAST LOGIN BEFORE '2024-06-01' LIMIT 100

The original SHOW USERS behavior (no clauses) is unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@souravcrl souravcrl force-pushed the show-users-enhance branch from ff93c78 to c3bb649 Compare April 20, 2026 08:55
This commit connects the SHOW USERS extended syntax (WITH SOURCE,
WITH LAST LOGIN BEFORE, LIMIT) to the delegation layer that generates
the underlying SQL query. It also adds end-to-end logic tests covering
the new filter clauses.

Epic: CRDB-45081
Release note: None
@souravcrl souravcrl force-pushed the show-users-enhance branch from c3bb649 to 80b98d5 Compare April 20, 2026 10:06
@trunk-io trunk-io Bot merged commit 80b98d5 into cockroachdb:master Apr 21, 2026
23 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

target-release-26.3.0 X-perf-gain Microbenchmarks CI: Added if a performance gain is detected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants