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

Unary Complement execution has different results when the parameters are different #76943

Merged
merged 1 commit into from
Mar 2, 2022
Merged

Unary Complement execution has different results when the parameters are different #76943

merged 1 commit into from
Mar 2, 2022

Conversation

ecwall
Copy link
Contributor

@ecwall ecwall commented Feb 23, 2022

fixes #74493

Release note (sql change): Return ambiguous unary operator error for ambiguous input
like ~'1' which can be interpreted as an integer (resulting in -2) or a bit string
(resulting in 0).

Release justification: Improves a confusing error message saying that an operator is
invalid instead of ambiguous.

@ecwall ecwall requested a review from a team February 23, 2022 19:46
@cockroach-teamcity
Copy link
Member

This change is Reviewable

@otan
Copy link
Contributor

otan commented Feb 23, 2022

the tests that fail will be interesting o_o can you comment on why commenting that out fixed things?

Copy link
Contributor Author

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


pkg/sql/sem/tree/overload.go, line 746 at r1 (raw file):

				})
		}
		if ok, typedExprs, fn, err := checkReturn(ctx, semaCtx, &s); ok {

checkReturn was short circuiting for '2' because there was only 1 overload after this filtering above

		for _, i := range s.constIdxs {
			constExpr := exprs[i].(Constant)
			s.overloadIdxs = filterOverloads(s.overloads, s.overloadIdxs,
				func(o overloadImpl) bool {
					semaCtx := MakeSemaContext()
					_, err := constExpr.ResolveAsType(ctx, &semaCtx, o.params().GetAt(i))
					return err == nil
				})
		}

This appears to be due to a check to convert the string into a date, but I am not sure why '1' is a valid date but '2' is not so I am not sure if there are other issues.

This was preventing further filtering from happening below for '2' which resulted in 1 overload instead of 0.

1 level up in func (expr *UnaryExpr) TypeCheck(...) the 0 overloads are correctly returning an error, but 1 overload is resulting in a result being returned.

	if len(fns) != 1 {
		var desStr string
		if desired.Family() != types.AnyFamily {
			desStr = fmt.Sprintf(" (desired <%s>)", desired)
		}
		sig := fmt.Sprintf("%s <%s>%s", expr.Operator, exprReturn, desStr)
		if len(fns) == 0 {
			return nil,
				pgerror.Newf(pgcode.InvalidParameterValue, unsupportedUnaryOpErrFmt, sig)
		}
		fnsStr := formatCandidates(expr.Operator.String(), fns)
		err = pgerror.Newf(pgcode.AmbiguousFunction, ambiguousUnaryOpErrFmt, sig)
		err = errors.WithHintf(err, candidatesHintFmt, fnsStr)
		return nil, err
	}

Will this affect other code that calls typeCheckOverloadedExprs(...)? Should all short circuits be removed?




<!-- Sent from Reviewable.io -->

@otan
Copy link
Contributor

otan commented Feb 23, 2022


pkg/sql/sem/tree/overload.go, line 746 at r1 (raw file):

Will this affect other code that calls typeCheckOverloadedExprs(...)?

yeah. looks like the test failing here suggest that this check was necessary for certain checks.

This appears to be due to a check to convert the string into a date,

that doesn't seem right. otherwise, i'd expect this to work:

[email protected]:26257/defaultdb> select '1'::date;
ERROR: parsing as type date: missing required date fields
SQLSTATE: 22007
DETAIL: Wanted: [ Year Day Era Hour Minute Second Nanos Meridian TZHour TZMinute TZSecond ]
Already found in input: [ Month ]
[email protected]:26257/defaultdb> select '2'::date;
ERROR: parsing as type date: missing required date fields
SQLSTATE: 22007
DETAIL: Wanted: [ Year Day Era Hour Minute Second Nanos Meridian TZHour TZMinute TZSecond ]
Already found in input: [ Month ]

what does it think 2 is when we return early is, and why doesn't that result in an error message?

Copy link
Contributor Author

@ecwall ecwall 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 @otan)


pkg/sql/sem/tree/overload.go, line 746 at r1 (raw file):

Previously, otan (Oliver Tan) wrote…

Will this affect other code that calls typeCheckOverloadedExprs(...)?

yeah. looks like the test failing here suggest that this check was necessary for certain checks.

This appears to be due to a check to convert the string into a date,

that doesn't seem right. otherwise, i'd expect this to work:

[email protected]:26257/defaultdb> select '1'::date;
ERROR: parsing as type date: missing required date fields
SQLSTATE: 22007
DETAIL: Wanted: [ Year Day Era Hour Minute Second Nanos Meridian TZHour TZMinute TZSecond ]
Already found in input: [ Month ]
[email protected]:26257/defaultdb> select '2'::date;
ERROR: parsing as type date: missing required date fields
SQLSTATE: 22007
DETAIL: Wanted: [ Year Day Era Hour Minute Second Nanos Meridian TZHour TZMinute TZSecond ]
Already found in input: [ Month ]

what does it think 2 is when we return early is, and why doesn't that result in an error message?

Looking at it further, it is actually not related to a date/time because ptCtx is being ignored in the taken branch inside ParseAndRequireString

		ptCtx := simpleParseTimeContext{
			// We can return any time, but not the zero value - it causes an error when
			// parsing "yesterday".
			RelativeParseTime: time.Date(2000, time.January, 2, 3, 4, 5, 0, time.UTC),
		}
		if semaCtx != nil {
			ptCtx.DateStyle = semaCtx.DateStyle
			ptCtx.IntervalStyle = semaCtx.IntervalStyle
		}
		val, dependsOnContext, err := ParseAndRequireString(typ, expr.s, ptCtx)
func ParseAndRequireString(
	t *types.T, s string, ctx ParseTimeContext,
) (d Datum, dependsOnContext bool, err error) {
	switch t.Family() {
	case types.ArrayFamily:
		d, dependsOnContext, err = ParseDArrayFromString(ctx, s, t.ArrayContents())
	case types.BitFamily:
		r, err := ParseDBitArray(s)
		if err != nil {
			return nil, false, err
		}
		d = formatBitArrayToType(r, t)

The difference still stands though that the reason '1' and '2' are different is because of an implicit conversion to bit array (1 can be converted which prevents a short circuit thereby allowing later filtering to 0) in cockroach that is not in postgres combined with the early return logic for having 1 overload.

@otan
Copy link
Contributor

otan commented Feb 24, 2022


pkg/sql/sem/tree/overload.go, line 746 at r1 (raw file):

Previously, ecwall (Evan Wall) wrote…

Looking at it further, it is actually not related to a date/time because ptCtx is being ignored in the taken branch inside ParseAndRequireString

		ptCtx := simpleParseTimeContext{
			// We can return any time, but not the zero value - it causes an error when
			// parsing "yesterday".
			RelativeParseTime: time.Date(2000, time.January, 2, 3, 4, 5, 0, time.UTC),
		}
		if semaCtx != nil {
			ptCtx.DateStyle = semaCtx.DateStyle
			ptCtx.IntervalStyle = semaCtx.IntervalStyle
		}
		val, dependsOnContext, err := ParseAndRequireString(typ, expr.s, ptCtx)
func ParseAndRequireString(
	t *types.T, s string, ctx ParseTimeContext,
) (d Datum, dependsOnContext bool, err error) {
	switch t.Family() {
	case types.ArrayFamily:
		d, dependsOnContext, err = ParseDArrayFromString(ctx, s, t.ArrayContents())
	case types.BitFamily:
		r, err := ParseDBitArray(s)
		if err != nil {
			return nil, false, err
		}
		d = formatBitArrayToType(r, t)

The difference still stands though that the reason '1' and '2' are different is because of an implicit conversion to bit array (1 can be converted which prevents a short circuit thereby allowing later filtering to 0) in cockroach that is not in postgres combined with the early return logic for having 1 overload.

another case of our type inference system not being great :'(

@otan otan removed the request for review from a team February 28, 2022 20:23
@ecwall ecwall requested a review from a team March 1, 2022 20:02
Copy link
Contributor Author

@ecwall ecwall 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 @otan)


pkg/sql/sem/tree/overload.go, line 746 at r1 (raw file):

Previously, otan (Oliver Tan) wrote…

another case of our type inference system not being great :'(

I'm now trying a new strategy: instead of preventing all strings from being implicitly cast, it now will display an ambiguous unary operator error (which includes a list of the ambiguities) instead of an unsupported unary operator error (which gave no additional info).

Copy link
Contributor

@otan otan left a comment

Choose a reason for hiding this comment

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

nice

@rafiss
Copy link
Collaborator

rafiss commented Mar 1, 2022

thanks for digging though all this! i like this approach

different

fixes #74493

Release note (sql change): Return ambiguous unary operator error for ambiguous
input like ~'1' which can be interpreted as an integer (resulting in -2) or a
bit string (resulting in 0).

Release justification: Improves a confusing error message saying that an
operator is invalid instead of ambiguous.
@ecwall
Copy link
Contributor Author

ecwall commented Mar 2, 2022

bors r=otan

@craig
Copy link
Contributor

craig bot commented Mar 2, 2022

Build failed (retrying...):

@craig
Copy link
Contributor

craig bot commented Mar 2, 2022

Build succeeded:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unary Complement execution has different results when the parameters are different
4 participants