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

sql: allow string concat with any non-array type #55611

Merged
merged 1 commit into from
Nov 6, 2020

Conversation

rafiss
Copy link
Collaborator

@rafiss rafiss commented Oct 15, 2020

Many tools and ORMs that work with Postgres expect this behavior.

this is retrying the idea from #43190
fixes #41872
fixes #34404

Release note (sql change): the concatenation operator || is now usable
between strings and any other non-array type.

Release note (backward-incompatible change): Concatenation between a
non-null argument and a null argument is now typed as string
concatenation. (It previously used to be typed as array concatentation.)
This means that the result of NULL || 1 will now be NULL.
Previously, the result would be {1}. To preserve the old behavior, the NULL
argument can be casted to an explicit type.

@cockroach-teamcity
Copy link
Member

This change is Reviewable

@rafiss rafiss force-pushed the string-concat branch 2 times, most recently from a7b8529 to 018b753 Compare October 16, 2020 22:13
@rafiss rafiss requested a review from a team as a code owner October 16, 2020 22:13
Copy link
Member

@RaduBerinde RaduBerinde 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 @rafiss)


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

				return nil, errors.New("neither LHS or RHS matched DString")
			},
			Volatility: VolatilityImmutable,

This is not correct. E.g. TIMESTAMPTZ to string is not an immutable cast. Look at the cast volatilities in tree/casts.go (validCasts). Perhaps looking up the volatility of the cast from the given type to string is the right way to initialize this.

@yuzefovich
Copy link
Member

Before this PR, we fully supported concat binary operator in the vectorized engine, so it'd be good to either confirm that it is still the case or open up an issue to add the support (if the latter is the case, the new issue should be referenced in #49463).

@rafiss rafiss force-pushed the string-concat branch 4 times, most recently from f79b917 to 316a312 Compare October 21, 2020 17:48
@rafiss rafiss force-pushed the string-concat branch 2 times, most recently from 0eaca7e to 3705e3c Compare October 22, 2020 17:53
@rafiss rafiss requested review from otan and a team October 22, 2020 18:45
@rafiss
Copy link
Collaborator Author

rafiss commented Oct 22, 2020

@yuzefovich I created #55841 (the new overloads do not work with colexec)

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.

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @otan and @RaduBerinde)


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

Previously, RaduBerinde wrote…

This is not correct. E.g. TIMESTAMPTZ to string is not an immutable cast. Look at the cast volatilities in tree/casts.go (validCasts). Perhaps looking up the volatility of the cast from the given type to string is the right way to initialize this.

thanks for this tip. updated

@otan
Copy link
Contributor

otan commented Oct 22, 2020

sooooo how hard is it to get this to work with anynonarray, to be more postgres-esque?

@rafiss
Copy link
Collaborator Author

rafiss commented Oct 23, 2020

I had a local branch with the anynonarray approach. but i didn't have an idea for dealing with the thing you got stuck with: #43143 (comment). but i do think i can dive into that problem more.

@rafiss
Copy link
Collaborator Author

rafiss commented Oct 23, 2020

hmm although to Radu's point above, we can't treat all the anynonarray overloads the same, since they have different volatility. unless we are allowed to be pessimistic and make them all VolatilityStable

@rafiss
Copy link
Collaborator Author

rafiss commented Oct 23, 2020

so i did get past the issue of #43143 (comment) just by adding explicit string-byte and byte-string concat overloads.

but after that, these cases failed:

    --- FAIL: TestEval/opt (0.07s)
        --- FAIL: TestEval/opt/concat (0.00s)
            eval_test.go:49:
                testdata/eval/concat:80: 1 || NULL
                expected:
                NULL

                found:
                ARRAY[1]
    --- FAIL: TestEval/no-opt (0.05s)
        --- FAIL: TestEval/no-opt/concat (0.00s)
            eval_test.go:49:
                testdata/eval/concat:80: 1 || NULL
                expected:
                NULL

                found:
                ARRAY[1]

with the anynonarray approach i haven't been able to think of a way to make the type checker pick string concat over array concat in the presence of nulls.

@otan
Copy link
Contributor

otan commented Oct 26, 2020

unless we are allowed to be pessimistic and make them all VolatilityStable

this is what PostgreSQL does

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.

lgtm if you can't get anynonarray to work.

@@ -417,9 +417,48 @@ func initArrayToArrayConcatenation() {
}
}

// initNonArrayToNonArrayConcatenation initializes string + nonarrayelement
// and nonarrayelement + string concatenation.
func initNonArrayToNonArrayConcatenation() {
Copy link
Contributor

Choose a reason for hiding this comment

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

can you add a test that checks <anyNonArrayElem> || NULL = anyNonArrayElem.String() for each non array elem type?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i may not be following, but in the operation you posted, the result should be NULL.

i added a test to pkg/sql/sem/tree/testdata/eval/concat. See https://github.com/rafiss/cockroach/blob/3705e3c7f37effaa644d45344b5c89a461877401/pkg/sql/sem/tree/testdata/eval/concat#L73

i can add more tests for the other non array types.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah woops, i meant anyNonArrayElem || string, as a loop over all current types to make sure it doesn't get resolved unexpectedly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it didn't seem very nice to loop over types and generate values for each type, so instead i just added more cases to concat eval test. it turned up an issue so i updated eval.go to use the FmtBareStrings flag

Copy link
Contributor

Choose a reason for hiding this comment

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

it didn't seem very nice to loop over types and generate values for each type

(i'm not opposed to this landing this as is, but curious)
does RandDatum not give you something nice?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i didn't know about RandDatum hehehe. thank you for the pointer!

will use that, and anyway looks like i need to fix tuple-string concat:

 encountered internal error:
no volatility for cast tuple{int, int}::tuple
(1) assertion failure
Wraps: (2) attached stack trace
  -- stack trace:
  | github.com/cockroachdb/cockroach/pkg/sql/opt/memo.BuildSharedProps
  | 	/go/src/github.com/cockroachdb/cockroach/pkg/sql/opt/memo/logical_props_builder.go:1512

@rafiss rafiss force-pushed the string-concat branch 2 times, most recently from 09ffe25 to eae45c4 Compare October 30, 2020 19:45
@rafiss rafiss requested a review from otan October 30, 2020 19:46
@rafiss
Copy link
Collaborator Author

rafiss commented Oct 30, 2020

I've had to update tree.ReType to get things working, so PTAL. I don't think that needs to handle recursive tuples, but let me know if it should.

}
// Casting to an "any" tuple is not allowed because there is no way to define
// the volatility in LookupCastVolatility.
if wantedType.Family() == types.TupleFamily &&
Copy link
Contributor

Choose a reason for hiding this comment

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

can we not always use stable as a volatility? i think that's what Postgres does. in these circumstances.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

that sounds cool! will try.

@rafiss rafiss force-pushed the string-concat branch 3 times, most recently from d966b95 to 11dc8bb Compare November 6, 2020 00:19
Many tools and ORMs that work with Postgres expect this behavior.

Release note (sql change): The concatenation operator || is now usable
between strings and any other non-array type.

Release note (backward-incompatible change): Concatenation between a
non-null argument and a null argument is now typed as string
concatenation. (It previously used to be typed as array concatentation.)
This means that the result of `NULL || 1` will now be `NULL`.
Previously, the result would be `{1}`. To preserve the old behavior, the NULL
argument can be casted to an explicit type.
@rafiss
Copy link
Collaborator Author

rafiss commented Nov 6, 2020

thanks for reviewing :)

bors r=otan

@craig
Copy link
Contributor

craig bot commented Nov 6, 2020

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
5 participants