Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

opt: check UDFs and UDTs by name when checking metadata dependencies #96045

Merged
merged 4 commits into from
Mar 20, 2023

Conversation

DrewKimball
Copy link
Collaborator

@DrewKimball DrewKimball commented Jan 27, 2023

tree: distinguish UDFs and builtins that use a SQL string body

This refactor changes the meaning of the Overload.IsUDF field to
be true only for user-defined functions - meaning those created using
CREATE FUNCTION. This is contrasted with builtin functions that are
defined with a SQL string set in Overload.Body. Logic that cares only
about user-defined functions can continue checking Overload.IsUDF,
while cases that deal with the SQL body should use Overload.HasSQLBody().
Note that it is insufficient to check whether Overload.Body is empty
because it is possible to define a UDF with an empty body.

opt: refactor metadata dependency tracking

This commit performs some refactoring for the way data source objects
are tracked in opt.Metadata in order to make future changes to UDT
and UDF dependency tracking easier. More particularly, UDTs and UDFs
will be able to re-resolve any references by name. This is necessary
in order to handle cases where changes to the search-path cause names
in the query to resolve to different objects.

opt: track UDT references by name in the Metadata

Previously, it was possible for invalid queries to be kept in the cache
after a schema change, since user-defined types were tracked only by OID.
This missed cases where the search path changed which object a name in
the query resolved to (or whether it resolved at all).

This patch fixes this behavior by tracking UDT references by name, similar
to what was already done for data sources. This ensures that the query
staleness check doesn't miss changes to the search path.

opt: track UDFs in the Metadata

This patch adds tracking for user-defined functions in opt.Metadata.
This ensures that the query cache will be correctly invalidated after
a schema change or search-path change that could change the query's
semantics, or cause it to error.

Fixes #95214
Fixes #93082
Fixes #93321
Fixes #96674

Release note (bug fix): Fixed a bug that could prevent a cached query from being invalidated when a UDF or UDT referenced by that query was altered or dropped, or when the schema or database of a UDF or UDT was altered or dropped.

@DrewKimball DrewKimball requested a review from a team January 27, 2023 01:56
@DrewKimball DrewKimball requested review from a team as code owners January 27, 2023 01:56
@cockroach-teamcity
Copy link
Member

This change is Reviewable

@DrewKimball DrewKimball requested review from chengxiong-ruan and removed request for a team January 27, 2023 01:57
@DrewKimball
Copy link
Collaborator Author

Hold off on reviewing this for a bit - it should be possible to fix #93321 as well.

@DrewKimball
Copy link
Collaborator Author

Alright, this is RFAL.

Copy link
Contributor

@ajwerner ajwerner left a comment

Choose a reason for hiding this comment

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

This looks good. My comment is nit-picky and more of a feeling than anything else. A lot of code in tree makes me feel uneasy with its unprincipled mutability etc, so there's nothing you could probably do that would make me feel good.

Feel free to wave me off if you like what's there.

@@ -63,6 +63,9 @@ type FunctionReferenceResolver interface {
// ResolvableFunctionReference implements the editable reference call of a
// FuncExpr.
type ResolvableFunctionReference struct {
// CallsiteName is the original UnresolvedName used at the function callsite.
// It may be unset if the function is unresolved.
CallsiteName *UnresolvedName
Copy link
Contributor

Choose a reason for hiding this comment

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

Something about this field name bothers me. I think your approach here is sound. Also, I wonder if we should make this field unexported and have the act of resolving which rewrites this in place to do the swapping out into the field. Then have a method which uses the unexported field if it's populated and otherwise delegates to the underlying function reference.

Copy link
Collaborator

@rharding6373 rharding6373 left a comment

Choose a reason for hiding this comment

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

This is great!

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @chengxiong-ruan and @DrewKimball)


pkg/sql/opt/metadata.go line 355 at r2 (raw file):

		}
	}
	// handleUndefined swallows "undefined" and "dropped object errors, since

nit: s/"dropped object/"dropped" object


pkg/sql/opt/metadata.go line 434 at r2 (raw file):

	}
	if name == nil {
		panic(errors.AssertionFailedf("attempted to add UDF with nil name"))

Is there a way to error gracefully instead of panic here? Better to not panic if we can.


pkg/sql/opt/metadata.go line 439 at r2 (raw file):

		if md.userDefinedFunctions[i].overload == fun && md.userDefinedFunctions[i].name == name {
			// This is a duplicate.
			break

Should this be a return? Right now it looks like we will always add the UDF to the metadata whether we found a duplicate or not.

Is there a test case that covers duplicates?

Copy link
Collaborator

@mgartner mgartner left a comment

Choose a reason for hiding this comment

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

Reviewed 3 of 6 files at r1, 6 of 6 files at r2, all commit messages.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner, @chengxiong-ruan, and @DrewKimball)


pkg/sql/opt/metadata.go line 377 at r2 (raw file):

	for i := range md.userDefinedFunctions {
		dep := &md.userDefinedFunctions[i]
		toCheck, err := optCatalog.ResolveFunction(ctx, dep.name, &evalCtx.SessionData().SearchPath)

Can we resolve the overload by ID rather than by name? I think that'd be more robust than using MatchOverload.


pkg/sql/opt/metadata.go line 430 at r2 (raw file):

// this query.
func (md *Metadata) AddUserDefinedFunc(fun *tree.Overload, name *tree.UnresolvedName) {
	if !fun.IsUDF {

We now set Overload.IsUDF to true for some builtins that use the UDF mechanisms. This is some tech debt that I think is time to address. I think we'll need two different booleans:

  • Overload.IsUDF can mean what it says - is the function user-defined.
  • Overload.HasBody (or some better name you come up with) can mean that an Overload's implementation is defined in the Body field.

We can't rely on an empty Body for the second point in order to be completely consistent with Postgres because UDFs with empty bodies are allowed.

Might be good to make this refactor in a separate PR/commit.


pkg/sql/opt/metadata.go line 437 at r2 (raw file):

	}
	for i := range md.userDefinedFunctions {
		if md.userDefinedFunctions[i].overload == fun && md.userDefinedFunctions[i].name == name {

Why do we need to check the name here?


pkg/sql/sem/tree/function_name.go line 68 at r2 (raw file):

Previously, ajwerner wrote…

Something about this field name bothers me. I think your approach here is sound. Also, I wonder if we should make this field unexported and have the act of resolving which rewrites this in place to do the swapping out into the field. Then have a method which uses the unexported field if it's populated and otherwise delegates to the underlying function reference.

If CheckDependencies resolves the function by ID/OID, then we shouldn't need this, right?

Copy link
Collaborator Author

@DrewKimball DrewKimball left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner, @chengxiong-ruan, @mgartner, and @rharding6373)


pkg/sql/opt/metadata.go line 355 at r2 (raw file):

Previously, rharding6373 (Rachael Harding) wrote…

nit: s/"dropped object/"dropped" object

Done.


pkg/sql/opt/metadata.go line 377 at r2 (raw file):

Previously, mgartner (Marcus Gartner) wrote…

Can we resolve the overload by ID rather than by name? I think that'd be more robust than using MatchOverload.

We have to use name in case the name now resolves to a different overload - for example, in logic_test/udf the re-created sequence will return 5 instead of 1 if we don't properly handle this. It also wouldn't handle the database being switched.


pkg/sql/opt/metadata.go line 430 at r2 (raw file):
I've refactored in a new commit. I'd appreciate some extra scrutiny for the cases where IsUDF is checked vs HasSQLBody - I left some as IsUDF because they seem to care only about whether the function was user-defined, not whether it uses the SQL string in its definition.

We can't rely on an empty Body for the second point in order to be completely consistent with Postgres because UDFs with empty bodies are allowed.

That case is only when IsUDF is true, right? So it should be enough to check IsUDF || Body != "". I've added a method to Overload to handle this. If you think it's better to add HasBody, I'm fine with that as well.


pkg/sql/opt/metadata.go line 434 at r2 (raw file):

Previously, rharding6373 (Rachael Harding) wrote…

Is there a way to error gracefully instead of panic here? Better to not panic if we can.

I don't think there's a way to propagate an error without plumbing it through the optbuilder. I think the panic should be caught be the optimizer, anyway.


pkg/sql/opt/metadata.go line 437 at r2 (raw file):

Previously, mgartner (Marcus Gartner) wrote…

Why do we need to check the name here?

We can't de-duplicate fully if a function is referred to in different ways (say, db.schema.func vs func), since that can change how a function is resolved in a different context (like if the database name is changed). I've changed the field from name to reference and added a comment to try and clarify this a bit. Also expanded the tests.


pkg/sql/opt/metadata.go line 439 at r2 (raw file):

Previously, rharding6373 (Rachael Harding) wrote…

Should this be a return? Right now it looks like we will always add the UDF to the metadata whether we found a duplicate or not.

Is there a test case that covers duplicates?

Good catch, it should be a return. It's difficult to test directly whether the de-duplication is working (and I'm not sure it's worth it since it's only a performance issue), but I'll expand the test cases a bit.


pkg/sql/sem/tree/function_name.go line 68 at r2 (raw file):

Something about this field name bothers me.

Changed it to Reference - does that make more sense to you?

Also, I wonder if we should make this field unexported and have the act of resolving which rewrites this in place to do the swapping out into the field. Then have a method which uses the unexported field if it's populated and otherwise delegates to the underlying function reference.

I like this idea. Done.

@DrewKimball DrewKimball requested review from jayshrivastava and removed request for a team and jayshrivastava February 2, 2023 23:22
Copy link
Collaborator

@mgartner mgartner left a comment

Choose a reason for hiding this comment

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

Don't forget to update the PR description with the new commit(s).

Reviewed 17 of 17 files at r3, 4 of 9 files at r4, all commit messages.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner, @chengxiong-ruan, @DrewKimball, and @rharding6373)


pkg/sql/delegate/show_function.go line 42 at r3 (raw file):

	var udfSchema string
	for _, o := range fn.Overloads {
		if o.HasSQLBody() {

nit: Can we leave this as IsUDF? It'd be odd to have SHOW CREATE FUNCTION work on just some built-ins.


pkg/sql/opt/metadata.go line 377 at r2 (raw file):

Previously, DrewKimball (Drew Kimball) wrote…

We have to use name in case the name now resolves to a different overload - for example, in logic_test/udf the re-created sequence will return 5 instead of 1 if we don't properly handle this. It also wouldn't handle the database being switched.

We talked IRL - there may be a way to use IDs instead of names.


pkg/sql/opt/metadata.go line 430 at r2 (raw file):

That case is only when IsUDF is true, right? So it should be enough to check IsUDF || Body != "". I've added a method to Overload to handle this. If you think it's better to add HasBody, I'm fine with that as well.

Great idea - I like it.


pkg/sql/opt/metadata.go line 240 at r4 (raw file):

	if len(from.userDefinedFunctions) > 0 {
		for i := range from.userDefinedFunctions {

nit: make a new slice with the exact capacity if md.userDefinedFunctions is nil.

Copy link
Collaborator Author

@DrewKimball DrewKimball left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @ajwerner, @chengxiong-ruan, @mgartner, and @rharding6373)


pkg/sql/delegate/show_function.go line 42 at r3 (raw file):

Previously, mgartner (Marcus Gartner) wrote…

nit: Can we leave this as IsUDF? It'd be odd to have SHOW CREATE FUNCTION work on just some built-ins.

Done.


pkg/sql/opt/metadata.go line 377 at r2 (raw file):

Previously, mgartner (Marcus Gartner) wrote…

We talked IRL - there may be a way to use IDs instead of names.

I've refactored the dependency checking to also check the schemas that are used to resolve each object, as well as the current database if it was used during resolution. This allows us to check only IDs for all schema objects.


pkg/sql/opt/metadata.go line 240 at r4 (raw file):

Previously, mgartner (Marcus Gartner) wrote…

nit: make a new slice with the exact capacity if md.userDefinedFunctions is nil.

Done.

@DrewKimball
Copy link
Collaborator Author

This should be ready for another look now.

@DrewKimball
Copy link
Collaborator Author

This should be ready for another look now.

Scratch that, looks like I have some more test failures to iron out.

@DrewKimball
Copy link
Collaborator Author

TFYRs!

bors r+

@craig
Copy link
Contributor

craig bot commented Mar 20, 2023

Build succeeded:

@craig craig bot merged commit ea389ff into cockroachdb:master Mar 20, 2023
mgartner added a commit to mgartner/cockroach that referenced this pull request Apr 20, 2023
This commit removes a TODO that was addressed by cockroachdb#96045.

Release note: None
craig bot pushed a commit that referenced this pull request Apr 20, 2023
101937: catalog: utilize scans for a large number of descriptors r=fqazi a=fqazi

Previously, the catalog descriptor would fetch descriptors via point lookups using Get when scanning large batches of descriptors. This was further extended to also look up ZoneConfigs and comments in a similar way. Recently, we we started seeing regression on the Django test suite involving the pg_catalog tables, which tend to do read large number of descriptors, likely linked to extra overhead linked to both comments and zone configs in 23.1. To address this, this patch ill now start using scans for runs of descriptor IDs for batch scans which reduces the overall cost of fetching a large number of descriptors hiding this cost.

Fixes: #100871

Release note: None

101943: build,release: provide way to inject build tag override, use in MAPB r=rail a=rickystewart

When we build nightly builds, we build them in "release" configuration. Because we do this, the build tag reported by these binaries from `cockroach version` has been identical to that of an actual release binary, which is very confusing. Here we update the script to build these nightlies (`make-and-publish-build-artifacts.sh`) to inject an appropriate, identifiable build tag.

It is probably wrong that we are building these nightlies as if they were "releases". We'll follow up with work to fix this and refine the build tags further.

Closes #100532.
Epic: None
Release note (build change): Update reported `Build Tag` for nightly (non-release) builds

101944: sem/tree: remove TODO r=mgartner a=mgartner

This commit removes a TODO that was addressed by #96045.

Epic: None

Release note: None

101952: ui: fix linegraph render for large clusters r=zachlite a=zachlite

We were feeding `Math.min` and `Math.max` very large arrays (length equal to ~10e7). These functions take variadic arguments, and the runtime can't handle that many arguments. As a result we blow the stack, causing uncaught range errors.

Instead, we'll use lodash min and max which are not variadic. Resolves #101377 Epic: None
Release note: None


Reproduction and Fix demonstrated on the command line:

<img width="722" alt="Screenshot 2023-04-20 at 4 56 41 PM" src="https://user-images.githubusercontent.com/5423191/233486016-fd740a98-9a0d-4fef-a6d8-67e9cb4e318e.png">


Co-authored-by: Faizan Qazi <faizan@cockroachlabs.com>
Co-authored-by: Ricky Stewart <rickybstewart@gmail.com>
Co-authored-by: Marcus Gartner <marcus@cockroachlabs.com>
Co-authored-by: Zach Lite <zach@cockroachlabs.com>
DrewKimball added a commit to DrewKimball/cockroach that referenced this pull request Apr 27, 2023
Previously, the `opt.Metadata` staleness check would check the access
privileges for a data source immediately after checking that the data
source still resolves as it originally did. This meant that the privilege
check for one data source could occur before checking staleness of other
data sources. Before cockroachdb#96045 this wasn't a problem, since these checks
would happen in the order in which the data sources were resolved. But
after cockroachdb#96045, the data sources were stored in a map instead of a slice,
which made the order in which the checks are performed nondeterministic.

This change in behavior can cause queries to return insufficient privilege
errors even when the current user has access privileges on all data sources
referenced by the query. This is because the query may *implicitly*
reference a table by ID if there is a foreign key relation from an explicitly
referenced data source. If the database is changed, this ID reference will
still resolve the same, but if the user is changed to one without privileges
on that table, the staleness check will result in an error. This wasn't a
problem before because the referencing table would always be checked first,
causing the staleness check to finish without an error.

This patch fixes the bug by moving the privilege checks until after all
data source resolution checks have succeeded.

Fixes cockroachdb#96045

Release note (bug fix): Fixed a bug introduced in versions 22.1.19,
22.2.8, and pre-release versions of 23.1 that could cause queries to
return spurious insufficient privilege errors. For the bug to occur,
two databases would need to have duplicate tables each with a foreign key
reference to another table. The error would then occur if the same SQL
string was executed against both databases concurrently by users that have
privileges over only one of the tables.
DrewKimball added a commit to DrewKimball/cockroach that referenced this pull request Apr 27, 2023
Previously, the `opt.Metadata` staleness check would check the access
privileges for a data source immediately after checking that the data
source still resolves as it originally did. This meant that the privilege
check for one data source could occur before checking staleness of other
data sources. Before cockroachdb#96045 this wasn't a problem, since these checks
would happen in the order in which the data sources were resolved. But
after cockroachdb#96045, the data sources were stored in a map instead of a slice,
which made the order in which the checks are performed nondeterministic.

This change in behavior can cause queries to return insufficient privilege
errors even when the current user has access privileges on all data sources
referenced by the query. This is because the query may *implicitly*
reference a table by ID if there is a foreign key relation from an explicitly
referenced data source. If the database is changed, this ID reference will
still resolve the same, but if the user is changed to one without privileges
on that table, the staleness check will result in an error. This wasn't a
problem before because the referencing table would always be checked first,
causing the staleness check to finish without an error.

This patch fixes the bug by moving the privilege checks until after all
data source resolution checks have succeeded.

Fixes cockroachdb#102375

Release note (bug fix): Fixed a bug introduced in versions 22.1.19,
22.2.8, and pre-release versions of 23.1 that could cause queries to
return spurious insufficient privilege errors. For the bug to occur,
two databases would need to have duplicate tables each with a foreign key
reference to another table. The error would then occur if the same SQL
string was executed against both databases concurrently by users that have
privileges over only one of the tables.
DrewKimball added a commit to DrewKimball/cockroach that referenced this pull request Apr 27, 2023
Previously, the `opt.Metadata` staleness check would check the access
privileges for a data source immediately after checking that the data
source still resolves as it originally did. This meant that the privilege
check for one data source could occur before checking staleness of other
data sources. Before cockroachdb#96045 this wasn't a problem, since these checks
would happen in the order in which the data sources were resolved. But
after cockroachdb#96045, the data sources were stored in a map instead of a slice,
which made the order in which the checks are performed nondeterministic.

This change in behavior can cause queries to return insufficient privilege
errors even when the current user has access privileges on all data sources
referenced by the query. This is because the query may *implicitly*
reference a table by ID if there is a foreign key relation from an explicitly
referenced data source. If the database is changed, this ID reference will
still resolve the same, but if the user is changed to one without privileges
on that table, the staleness check will result in an error. This wasn't a
problem before because the referencing table would always be checked first,
causing the staleness check to finish without an error.

This patch fixes the bug by moving the privilege checks until after all
data source resolution checks have succeeded.

Fixes cockroachdb#102375

Release note (bug fix): Fixed a bug introduced in versions 22.1.19,
22.2.8, and pre-release versions of 23.1 that could cause queries to
return spurious insufficient privilege errors. For the bug to occur,
two databases would need to have duplicate tables each with a foreign key
reference to another table. The error would then occur if the same SQL
string was executed against both databases concurrently by users that have
privileges over only one of the tables.
DrewKimball added a commit to DrewKimball/cockroach that referenced this pull request Apr 27, 2023
Previously, the `opt.Metadata` staleness check would check the access
privileges for a data source immediately after checking that the data
source still resolves as it originally did. This meant that the privilege
check for one data source could occur before checking staleness of other
data sources. Before cockroachdb#96045 this wasn't a problem, since these checks
would happen in the order in which the data sources were resolved. But
after cockroachdb#96045, the data sources were stored in a map instead of a slice,
which made the order in which the checks are performed nondeterministic.

This change in behavior can cause queries to return insufficient privilege
errors even when the current user has access privileges on all data sources
referenced by the query. This is because the query may *implicitly*
reference a table by ID if there is a foreign key relation from an explicitly
referenced data source. If the database is changed, this ID reference will
still resolve the same, but if the user is changed to one without privileges
on that table, the staleness check will result in an error. This wasn't a
problem before because the referencing table would always be checked first,
causing the staleness check to finish without an error.

This patch fixes the bug by moving the privilege checks until after all
data source resolution checks have succeeded.

Fixes cockroachdb#102375

Release note (bug fix): Fixed a bug introduced in versions 22.1.19,
22.2.8, and pre-release versions of 23.1 that could cause queries to
return spurious insufficient privilege errors. For the bug to occur,
two databases would need to have duplicate tables each with a foreign key
reference to another table. The error would then occur if the same SQL
string was executed against both databases concurrently by users that have
privileges over only one of the tables.
craig bot pushed a commit that referenced this pull request Apr 28, 2023
102405: opt: check privileges after data sources in memo staleness check r=DrewKimball a=DrewKimball

Previously, the `opt.Metadata` staleness check would check the access privileges for a data source immediately after checking that the data source still resolves as it originally did. This meant that the privilege check for one data source could occur before checking staleness of other data sources. Before #96045 this wasn't a problem, since these checks would happen in the order in which the data sources were resolved. But after #96045, the data sources were stored in a map instead of a slice, which made the order in which the checks are performed nondeterministic.

This change in behavior can cause queries to return insufficient privilege errors even when the current user has access privileges on all data sources referenced by the query. This is because the query may *implicitly* reference a table by ID if there is a foreign key relation from an explicitly referenced data source. If the database is changed, this ID reference will still resolve the same, but if the user is changed to one without privileges on that table, the staleness check will result in an error. This wasn't a problem before because the referencing table would always be checked first, causing the staleness check to finish without an error.

This patch fixes the bug by moving the privilege checks until after all data source resolution checks have succeeded.

Fixes #102375

Release note (bug fix): Fixed a bug introduced in versions 22.1.19, 22.2.8, and pre-release versions of 23.1 that could cause queries to return spurious insufficient privilege errors. For the bug to occur, two databases would need to have duplicate tables each with a foreign key reference to another table. The error would then occur if the same SQL string was executed against both databases concurrently by users that have privileges over only one of the tables.

Co-authored-by: Drew Kimball <drewk@cockroachlabs.com>
blathers-crl bot pushed a commit that referenced this pull request Apr 28, 2023
Previously, the `opt.Metadata` staleness check would check the access
privileges for a data source immediately after checking that the data
source still resolves as it originally did. This meant that the privilege
check for one data source could occur before checking staleness of other
data sources. Before #96045 this wasn't a problem, since these checks
would happen in the order in which the data sources were resolved. But
after #96045, the data sources were stored in a map instead of a slice,
which made the order in which the checks are performed nondeterministic.

This change in behavior can cause queries to return insufficient privilege
errors even when the current user has access privileges on all data sources
referenced by the query. This is because the query may *implicitly*
reference a table by ID if there is a foreign key relation from an explicitly
referenced data source. If the database is changed, this ID reference will
still resolve the same, but if the user is changed to one without privileges
on that table, the staleness check will result in an error. This wasn't a
problem before because the referencing table would always be checked first,
causing the staleness check to finish without an error.

This patch fixes the bug by moving the privilege checks until after all
data source resolution checks have succeeded.

Fixes #102375

Release note (bug fix): Fixed a bug introduced in versions 22.1.19,
22.2.8, and pre-release versions of 23.1 that could cause queries to
return spurious insufficient privilege errors. For the bug to occur,
two databases would need to have duplicate tables each with a foreign key
reference to another table. The error would then occur if the same SQL
string was executed against both databases concurrently by users that have
privileges over only one of the tables.
DrewKimball added a commit to DrewKimball/cockroach that referenced this pull request Apr 28, 2023
Previously, the `opt.Metadata` staleness check would check the access
privileges for a data source immediately after checking that the data
source still resolves as it originally did. This meant that the privilege
check for one data source could occur before checking staleness of other
data sources. Before cockroachdb#96045 this wasn't a problem, since these checks
would happen in the order in which the data sources were resolved. But
after cockroachdb#96045, the data sources were stored in a map instead of a slice,
which made the order in which the checks are performed nondeterministic.

This change in behavior can cause queries to return insufficient privilege
errors even when the current user has access privileges on all data sources
referenced by the query. This is because the query may *implicitly*
reference a table by ID if there is a foreign key relation from an explicitly
referenced data source. If the database is changed, this ID reference will
still resolve the same, but if the user is changed to one without privileges
on that table, the staleness check will result in an error. This wasn't a
problem before because the referencing table would always be checked first,
causing the staleness check to finish without an error.

This patch fixes the bug by moving the privilege checks until after all
data source resolution checks have succeeded.

Fixes cockroachdb#102375

Release note (bug fix): Fixed a bug introduced in versions 22.1.19,
22.2.8, and pre-release versions of 23.1 that could cause queries to
return spurious insufficient privilege errors. For the bug to occur,
two databases would need to have duplicate tables each with a foreign key
reference to another table. The error would then occur if the same SQL
string was executed against both databases concurrently by users that have
privileges over only one of the tables.
DrewKimball added a commit to DrewKimball/cockroach that referenced this pull request Apr 28, 2023
Previously, the `opt.Metadata` staleness check would check the access
privileges for a data source immediately after checking that the data
source still resolves as it originally did. This meant that the privilege
check for one data source could occur before checking staleness of other
data sources. Before cockroachdb#96045 this wasn't a problem, since these checks
would happen in the order in which the data sources were resolved. But
after cockroachdb#96045, the data sources were stored in a map instead of a slice,
which made the order in which the checks are performed nondeterministic.

This change in behavior can cause queries to return insufficient privilege
errors even when the current user has access privileges on all data sources
referenced by the query. This is because the query may *implicitly*
reference a table by ID if there is a foreign key relation from an explicitly
referenced data source. If the database is changed, this ID reference will
still resolve the same, but if the user is changed to one without privileges
on that table, the staleness check will result in an error. This wasn't a
problem before because the referencing table would always be checked first,
causing the staleness check to finish without an error.

This patch fixes the bug by moving the privilege checks until after all
data source resolution checks have succeeded.

Fixes cockroachdb#102375

Release note (bug fix): Fixed a bug introduced in versions 22.1.19,
22.2.8, and pre-release versions of 23.1 that could cause queries to
return spurious insufficient privilege errors. For the bug to occur,
two databases would need to have duplicate tables each with a foreign key
reference to another table. The error would then occur if the same SQL
string was executed against both databases concurrently by users that have
privileges over only one of the tables.
DrewKimball added a commit to DrewKimball/cockroach that referenced this pull request Apr 28, 2023
Previously, the `opt.Metadata` staleness check would check the access
privileges for a data source immediately after checking that the data
source still resolves as it originally did. This meant that the privilege
check for one data source could occur before checking staleness of other
data sources. Before cockroachdb#96045 this wasn't a problem, since these checks
would happen in the order in which the data sources were resolved. But
after cockroachdb#96045, the data sources were stored in a map instead of a slice,
which made the order in which the checks are performed nondeterministic.

This change in behavior can cause queries to return insufficient privilege
errors even when the current user has access privileges on all data sources
referenced by the query. This is because the query may *implicitly*
reference a table by ID if there is a foreign key relation from an explicitly
referenced data source. If the database is changed, this ID reference will
still resolve the same, but if the user is changed to one without privileges
on that table, the staleness check will result in an error. This wasn't a
problem before because the referencing table would always be checked first,
causing the staleness check to finish without an error.

This patch fixes the bug by moving the privilege checks until after all
data source resolution checks have succeeded.

Fixes cockroachdb#102375

Release note (bug fix): Fixed a bug introduced in versions 22.1.19,
22.2.8, and pre-release versions of 23.1 that could cause queries to
return spurious insufficient privilege errors. For the bug to occur,
two databases would need to have duplicate tables each with a foreign key
reference to another table. The error would then occur if the same SQL
string was executed against both databases concurrently by users that have
privileges over only one of the tables.
cameronnunez pushed a commit to cameronnunez/cockroach that referenced this pull request May 2, 2023
Previously, the `opt.Metadata` staleness check would check the access
privileges for a data source immediately after checking that the data
source still resolves as it originally did. This meant that the privilege
check for one data source could occur before checking staleness of other
data sources. Before cockroachdb#96045 this wasn't a problem, since these checks
would happen in the order in which the data sources were resolved. But
after cockroachdb#96045, the data sources were stored in a map instead of a slice,
which made the order in which the checks are performed nondeterministic.

This change in behavior can cause queries to return insufficient privilege
errors even when the current user has access privileges on all data sources
referenced by the query. This is because the query may *implicitly*
reference a table by ID if there is a foreign key relation from an explicitly
referenced data source. If the database is changed, this ID reference will
still resolve the same, but if the user is changed to one without privileges
on that table, the staleness check will result in an error. This wasn't a
problem before because the referencing table would always be checked first,
causing the staleness check to finish without an error.

This patch fixes the bug by moving the privilege checks until after all
data source resolution checks have succeeded.

Fixes cockroachdb#102375

Release note (bug fix): Fixed a bug introduced in versions 22.1.19,
22.2.8, and pre-release versions of 23.1 that could cause queries to
return spurious insufficient privilege errors. For the bug to occur,
two databases would need to have duplicate tables each with a foreign key
reference to another table. The error would then occur if the same SQL
string was executed against both databases concurrently by users that have
privileges over only one of the tables.
@DrewKimball DrewKimball deleted the drop-function branch June 28, 2024 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants