Skip to content

sql: add aclitem type, implement acldefault and makeaclitem, enhance aclexplode#165744

Merged
trunk-io[bot] merged 2 commits intocockroachdb:masterfrom
rafiss:aclitem
Mar 17, 2026
Merged

sql: add aclitem type, implement acldefault and makeaclitem, enhance aclexplode#165744
trunk-io[bot] merged 2 commits intocockroachdb:masterfrom
rafiss:aclitem

Conversation

@rafiss
Copy link
Collaborator

@rafiss rafiss commented Mar 13, 2026

Add a proper aclitem type (StringFamily with OID 1033, following the same pattern as name) and implement three PostgreSQL-compatible ACL functions:

  • makeaclitem(grantee oid, grantor oid, privileges text, is_grantable bool) → aclitem: constructs an aclitem string from its components.
  • acldefault(type text, ownerId oid) → aclitem[]: returns the hard-wired default ACL for a given object type, matching PostgreSQL's defaults for all 13 object type characters.
  • aclexplode(aclitems) → setof record: previously a stub that always returned no rows. Now parses ACL strings and emits (grantor, grantee, privilege_type, is_grantable) rows. Supports both text[] and aclitem[] input.

These functions are needed for pg-compatibility with ORMs and tools that inspect PostgreSQL access control lists.

Resolves: #91070
informs: #20296

Release note (sql change): Added support for the aclitem type and the makeaclitem and acldefault built-in functions for PostgreSQL compatibility. The existing aclexplode function, which previously always returned no rows, now correctly parses ACL strings and returns the individual privilege grants they contain.

@rafiss rafiss requested review from a team as code owners March 13, 2026 22:11
@rafiss rafiss requested review from yuzefovich and removed request for a team March 13, 2026 22:11
@trunk-io
Copy link
Contributor

trunk-io bot commented Mar 13, 2026

😎 Merged successfully - details.

@cockroach-teamcity
Copy link
Member

This change is Reviewable

@github-actions

This comment was marked as resolved.

@github-actions github-actions bot added the o-AI-Review-Potential-Issue-Detected AI reviewer found potential issue. Never assign manually—auto-applied by GH action only. label Mar 15, 2026
@rafiss rafiss added the O-AI-Review-Not-Helpful AI reviewer produced result which was incorrect or unhelpful label Mar 15, 2026
@rafiss rafiss force-pushed the aclitem branch 2 times, most recently from b49eb0b to 2647f0a Compare March 16, 2026 16:40
@rafiss rafiss requested a review from fqazi March 16, 2026 16:40
Copy link
Collaborator

@fqazi fqazi left a comment

Choose a reason for hiding this comment

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

@rafiss Great work, a couple of issues

@fqazi reviewed 4 files and all commit messages, and made 4 comments.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss and yuzefovich).


pkg/sql/privilege/privilege.go line 480 at r2 (raw file):

		}
		// Unterminated quote — caller will detect trailing content error.
		return i

I think the validation here is a bad and we need to return a special value. The following will pass incorrectly:

s="user=rw/"root"


pkg/sql/sem/builtins/generator_builtins.go line 188 at r2 (raw file):

		// Resolve role names to OIDs.
		granteeOid, err := resolveRoleOid(ctx, evalCtx, granteeStr)

This could be really slow for large arrays, can we may be do a batched resolution / have caching? I don't know the scale here, so just something to think about.


pkg/sql/sem/builtins/pg_builtins.go line 2519 at r2 (raw file):

				// Build the aclitem string: "grantee=privchars/grantor".
				var result strings.Builder
				result.WriteString(granteeName)

Think we need to escape the string, if for example it contains "user-1"

Copy link
Collaborator

@fqazi fqazi left a comment

Choose a reason for hiding this comment

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

@fqazi made 1 comment.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss and yuzefovich).


pkg/sql/privilege/privilege.go line 480 at r2 (raw file):

Previously, fqazi (Faizan Qazi) wrote…

I think the validation here is a bad and we need to return a special value. The following will pass incorrectly:

s="user=rw/"root"

*we need to also return a special value for missing quotes

Copy link
Collaborator

@fqazi fqazi left a comment

Choose a reason for hiding this comment

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

@fqazi made 1 comment.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on rafiss and yuzefovich).


pkg/sql/privilege/privilege.go line 480 at r2 (raw file):

Previously, fqazi (Faizan Qazi) wrote…

*we need to also return a special value for missing quotes

Ugh the example above should be user=rw/"root

Copy link
Member

@yuzefovich yuzefovich 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! I think there are a few spots where we missed the new type. Some are mentioned inline, additionaly probably strVal.ResolveAsType needs an adjustment.

@yuzefovich reviewed 18 files and all commit messages, and made 8 comments.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on fqazi and rafiss).


-- commits line 21 at r2:
Should we add support for array containment @> before closing the issue? (I.e. either add it in this PR, or leave a TODO and not close the issue, or file a new issue.)


pkg/sql/pgwire/pgwirebase/encoding.go line 979 at r2 (raw file):

		}
		return da.NewDName(tree.DString(bs)), nil
	case oidext.T_aclitem:

We also probably need to update the decoding logic in rowenc/keyside (1 spot) and rowenc/valueside (2 spots) packages.


pkg/sql/pgwire/pgwirebase/encoding.go line 983 at r2 (raw file):

			return nil, err
		}
		return tree.NewDACLItem(bs), nil

nit: we should extend tree.DatumAlloc for the new type.


pkg/sql/randgen/datum.go line 282 at r2 (raw file):

			// characters (=, /, ") that break the aclitem format.
			const alphanums = "abcdefghijklmnopqrstuvwxyz0123456789"
			granteeLen := rng.Intn(6)

nit: is 0 length ok?


pkg/sql/sem/eval/cast.go line 519 at r2 (raw file):

				return tree.NewDName(s), nil
			}
			if t.Oid() == oidext.T_aclitem {

We also need to add a similar block to vec_to_datum_tmpl.go. We also need to adjust toString method in cast_gen_util.go.


pkg/sql/sem/tree/datum.go line 5837 at r2 (raw file):

// initialized from a string in the format "grantee=privchars/grantor".
func NewDACLItem(d string) Datum {
	return wrapWithOid(NewDString(d), oidext.T_aclitem)

nit: update the last paragraph of the comment on DOidWrapper to include new type.


pkg/sql/logictest/testdata/logic_test/pg_builtins line 12 at r2 (raw file):


# aclexplode with an invalid ACL string produces no rows (graceful skip).
query T

nit: there is query empty 😉

Copy link
Collaborator Author

@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.

i looked into adding an aclitem case to ResolveAsType. that would bypass ValidateACLItemString (which is in the privilege package and can't be imported from tree). The cast path in eval/cast.go already handles validation correctly, and string literals like '...'::aclitem go through that path. So it doesn't seem that any change needed here.

@rafiss made 11 comments and resolved 4 discussions.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on fqazi and yuzefovich).


-- commits line 21 at r2:

Previously, yuzefovich (Yahor Yuzefovich) wrote…

Should we add support for array containment @> before closing the issue? (I.e. either add it in this PR, or leave a TODO and not close the issue, or file a new issue.)

this actually worked already since aclitem inherits DString comparison via DOidWrapper, so the generic ArrayContains logic handles it. i added logic tests covering @>


pkg/sql/pgwire/pgwirebase/encoding.go line 979 at r2 (raw file):

Previously, yuzefovich (Yahor Yuzefovich) wrote…

We also probably need to update the decoding logic in rowenc/keyside (1 spot) and rowenc/valueside (2 spots) packages.

Done.


pkg/sql/pgwire/pgwirebase/encoding.go line 983 at r2 (raw file):

Previously, yuzefovich (Yahor Yuzefovich) wrote…

nit: we should extend tree.DatumAlloc for the new type.

Done


pkg/sql/privilege/privilege.go line 480 at r2 (raw file):

Previously, fqazi (Faizan Qazi) wrote…

Ugh the example above should be user=rw/"root

Done. skipACLIdentifier now returns -1 for unterminated quotes, and both call sites in ValidateACLItemString check for it and return a proper error. Added unit tests in TestValidateACLItemString and logic tests for both grantee and grantor positions.


pkg/sql/randgen/datum.go line 282 at r2 (raw file):

Previously, yuzefovich (Yahor Yuzefovich) wrote…

nit: is 0 length ok?

Done; i added 1 +


pkg/sql/sem/builtins/generator_builtins.go line 188 at r2 (raw file):

Previously, fqazi (Faizan Qazi) wrote…

This could be really slow for large arrays, can we may be do a batched resolution / have caching? I don't know the scale here, so just something to think about.

Done; great point. I don't think this is function would ever be used in a high throughput situation, but it could be used in a big query. I added a roleOidCache map local to the newAclexplodeGenerator call so repeated role names across aclitem entries don't issue duplicate queries.


pkg/sql/sem/builtins/pg_builtins.go line 2519 at r2 (raw file):

Previously, fqazi (Faizan Qazi) wrote…

Think we need to escape the string, if for example it contains "user-1"

Done. Added QuoteACLIdentifier in the privilege package and use it in both makeaclitem and acldefault output. Also updated aclexplode to use ExtractACLIdentifier for proper unquoting when parsing. I added tests for it.


pkg/sql/sem/eval/cast.go line 519 at r2 (raw file):

Previously, yuzefovich (Yahor Yuzefovich) wrote…

We also need to add a similar block to vec_to_datum_tmpl.go. We also need to adjust toString method in cast_gen_util.go.

Done.


pkg/sql/sem/tree/datum.go line 5837 at r2 (raw file):

Previously, yuzefovich (Yahor Yuzefovich) wrote…

nit: update the last paragraph of the comment on DOidWrapper to include new type.

Done


pkg/sql/logictest/testdata/logic_test/pg_builtins line 12 at r2 (raw file):

Previously, yuzefovich (Yahor Yuzefovich) wrote…

nit: there is query empty 😉

done; TIL

@blathers-crl
Copy link

blathers-crl bot commented Mar 17, 2026

Your pull request contains more than 1000 changes. It is strongly encouraged to split big PRs into smaller chunks.

🦉 Hoot! I am a Blathers, a bot for CockroachDB. My owner is dev-inf.

Copy link
Member

@yuzefovich yuzefovich left a comment

Choose a reason for hiding this comment

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

Ack. Execution parts LGTM, will defer to Faizan for approval.

@yuzefovich reviewed 11 files and all commit messages, made 4 comments, and resolved 1 discussion.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on fqazi and rafiss).


-- commits line 21 at r2:

Previously, rafiss (Rafi Shamim) wrote…

this actually worked already since aclitem inherits DString comparison via DOidWrapper, so the generic ArrayContains logic handles it. i added logic tests covering @>

Nice!


pkg/sql/pgwire/pgwirebase/encoding.go line 979 at r2 (raw file):

Previously, rafiss (Rafi Shamim) wrote…

Done.

One more spot in valueside.UnmarshalLegacy.


pkg/sql/colexec/colexecbase/BUILD.bazel line 43 at r3 (raw file):

        "@com_github_cockroachdb_errors//:errors",
        "@com_github_cockroachdb_redact//:redact",  # keep
        "//pkg/sql/oidext",  # keep

nit: out of order.

…aclexplode

Add a proper `aclitem` type (StringFamily with OID 1033, following the
same pattern as `name`) and implement three PostgreSQL-compatible ACL
functions:

- `makeaclitem(grantee oid, grantor oid, privileges text, is_grantable
  bool) → aclitem`: constructs an aclitem string from its components.
- `acldefault(type text, ownerId oid) → aclitem[]`: returns the
  hard-wired default ACL for a given object type, matching PostgreSQL's
  defaults for all 13 object type characters.
- `aclexplode(aclitems) → setof record`: previously a stub that always
  returned no rows. Now parses ACL strings and emits (grantor, grantee,
  privilege_type, is_grantable) rows. Supports both `text[]` and
  `aclitem[]` input.

These functions are needed for pg-compatibility with ORMs and tools that
inspect PostgreSQL access control lists.

Role names containing special characters (hyphens, spaces, etc.) are
properly quoted with double quotes in aclitem output via
QuoteACLIdentifier, matching PostgreSQL's putid() behavior. The
aclexplode function correctly unquotes these identifiers when parsing,
and caches role OID lookups to avoid repeated queries.

The aclitem type is integrated into the row encoding (keyside/valueside),
vectorized execution (vec_to_datum, cast), and DatumAlloc infrastructure.
Array containment (@>) works for aclitem arrays. Unterminated quoted
identifiers in aclitem strings are now properly rejected during
validation.

Resolves: cockroachdb#91070

Release note (sql change): Added support for the `aclitem` type and the
`makeaclitem` and `acldefault` built-in functions for PostgreSQL
compatibility. The existing `aclexplode` function, which previously
always returned no rows, now correctly parses ACL strings and returns
the individual privilege grants they contain.

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
Copy link
Collaborator Author

@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 2 comments and resolved 1 discussion.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on fqazi and yuzefovich).


pkg/sql/colexec/colexecbase/BUILD.bazel line 43 at r3 (raw file):

Previously, yuzefovich (Yahor Yuzefovich) wrote…

nit: out of order.

done


pkg/sql/pgwire/pgwirebase/encoding.go line 979 at r2 (raw file):

Previously, yuzefovich (Yahor Yuzefovich) wrote…

One more spot in valueside.UnmarshalLegacy.

done

@github-actions
Copy link
Contributor

AI Review: Potential Issue(s) Detected

Missing privilege character mappings for 's' (SET) and 'A' (ALTER SYSTEM) in ACLCharToPrivName and PrivNameToACLChar maps (pkg/sql/privilege/privilege.go:371-403) cause:

  1. Silent data loss in aclexplode: Characters not found in ACLCharToPrivName are silently skipped (generator_builtins.go:232), so aclexplode(acldefault('p', oid)) returns zero privilege rows despite the aclitem containing valid 's' and 'A' characters.
  2. makeaclitem rejects SET/ALTER SYSTEM: PrivNameToACLChar lacks the reverse mappings, so makeaclitem(..., 'SET', ...) returns an "unrecognized privilege type" error.
  3. Broken round-trip: The acldefaultaclexplode contract is violated for PARAMETER ('p') objects.

Fix: Add 's': "SET" and 'A': "ALTER SYSTEM" to ACLCharToPrivName, and "SET": 's' and "ALTER SYSTEM": 'A' to PrivNameToACLChar.

Inline comments have been added to the relevant lines.

View full analysis

If helpful: add O-AI-Review-Real-Issue-Found label.
If not helpful: add O-AI-Review-Not-Helpful label.

@rafiss rafiss added O-AI-Review-Real-Issue-Found AI reviewer found real issue and removed O-AI-Review-Not-Helpful AI reviewer produced result which was incorrect or unhelpful labels Mar 17, 2026
Copy link
Collaborator

@fqazi fqazi left a comment

Choose a reason for hiding this comment

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

:lgtm:

@fqazi reviewed 34 files and all commit messages, made 1 comment, and resolved 2 discussions.
Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on rafiss and yuzefovich).

@rafiss
Copy link
Collaborator Author

rafiss commented Mar 17, 2026

/trunk merge

TFTR!

…item validation

The ACLCharToPrivName and PrivNameToACLChar maps were missing entries
for 's' (SET) and 'A' (ALTER SYSTEM). This caused two bugs:

1. aclexplode silently dropped SET and ALTER SYSTEM privileges, so
   aclexplode(acldefault('p', oid)) returned 0 rows instead of 2.
2. makeaclitem rejected SET and ALTER SYSTEM as unrecognized privilege
   types.

Fix by adding the missing entries. Also consolidate the three redundant
ACL character mapping tables (privToACL, ACLCharToPrivName,
PrivNameToACLChar) so that ACLCharToPrivName is the single source of
truth and the other two are derived from it in init().

Additionally, add validation to NewDACLItemFromDString so that malformed
aclitem strings are rejected at construction time rather than silently
accepted. Add an aclitem case to StrVal.ResolveAsType for string literal
resolution. Remove the now-redundant validation in eval/cast.go.

Release note: None

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
@trunk-io trunk-io bot merged commit c7212dc into cockroachdb:master Mar 17, 2026
24 of 26 checks passed
@rafiss rafiss deleted the aclitem branch March 18, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

o-AI-Review-Potential-Issue-Detected AI reviewer found potential issue. Never assign manually—auto-applied by GH action only. O-AI-Review-Real-Issue-Found AI reviewer found real issue target-release-26.2.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pgbuiltins: support aclitem functions (acldefault)

4 participants