Skip to content

sql: add user-defined composite types#90491

Merged
craig[bot] merged 2 commits into
cockroachdb:masterfrom
jordanlewis:udct
Dec 12, 2022
Merged

sql: add user-defined composite types#90491
craig[bot] merged 2 commits into
cockroachdb:masterfrom
jordanlewis:udct

Conversation

@jordanlewis

@jordanlewis jordanlewis commented Oct 21, 2022

Copy link
Copy Markdown
Contributor

Closes #27792

This commit adds user-defined composite types, which are created with
the CREATE TYPE t AS (field1 type1, field2 type2, ...) syntax.

See documentation here:
https://www.postgresql.org/docs/current/rowtypes.html

It permits this kind of thing:

CREATE TYPE t AS (a INT, b INT);
CREATE TABLE a (a t);
INSERT INTO a VALUES((1, 2))
SELECT (a).b FROM a

Release note (sql change): add user-defined composite column types

@cockroach-teamcity

Copy link
Copy Markdown
Member

This change is Reviewable

@rafiss

rafiss commented Oct 21, 2022

Copy link
Copy Markdown
Collaborator

super cool!

two idle comments before a full review:

  • let's make sure to add a new cluster version and version gate this.
  • do you know how exactly these differ from the implicit table record types in postgres? i was wondering if we could implement them in the exact same way we've already implemented implicit table types, with the addition of marking the table descriptor as unusable. maybe there would be an issue with privileges?

@jordanlewis

Copy link
Copy Markdown
Contributor Author

let's make sure to add a new cluster version and version gate this.

Definitely.

do you know how exactly these differ from the implicit table record types in postgres?

There are a couple of differences, but they're pretty minor. It's telling that in Postgres, a UDT appears in pg_class. It does strike me that maybe we could do it by just creating a table that you can't insert to, and proxying all the alter statements into alter table statements, but I'm not sure it's a good idea - it seems like it might make existing code more complicated rather than adding new code that's separate.

I'm curious what @ajwerner thinks as the majority of the code here is in schema world but my instinct is that we should model it as a new type kind just like enum.

@ajwerner

Copy link
Copy Markdown
Contributor

I'm curious what @ajwerner thinks as the majority of the code here is in schema world but my instinct is that we should model it as a new type kind just like enum.

That matches my expectation. I can imagine a future migration to add such types for tables too.

@jordanlewis jordanlewis force-pushed the udct branch 4 times, most recently from a19be20 to acf0bc6 Compare October 25, 2022 03:34
@jordanlewis jordanlewis changed the title [WIP] sql: add user-defined composite types sql: add user-defined composite types Oct 25, 2022
@jordanlewis jordanlewis marked this pull request as ready for review October 25, 2022 14:09
@jordanlewis jordanlewis requested a review from a team as a code owner October 25, 2022 14:09
@jordanlewis jordanlewis requested review from a team and benbardin and removed request for a team October 25, 2022 14:09
@jordanlewis

Copy link
Copy Markdown
Contributor Author

I'm feeling relatively good about contents of this PR, if someone has a chance to review I'd appreciate it. It was nice to learn a little bit about the new schema changer - once you get the hang of it, it seems to do an impressive job of abstracting away the underlying complexity. Or, perhaps I just had blinders on :)

I'm not sure whether I did the dependency tracking right, and I'm sure it's missing a bunch of other stuff as well.

@jordanlewis

Copy link
Copy Markdown
Contributor Author

Also the PR is fairly large, say the word and I'll try to chunk it up a bit.

@jordanlewis

Copy link
Copy Markdown
Contributor Author

Actually, I don't think I have done anything about backup and restore in this patch, so it is probably not complete :)

@chengxiong-ruan

Copy link
Copy Markdown
Contributor

Nice work. Maybe it's just me but I found myself had to do quite a bit of context switching while going through the PR briefly. I'd appreciate if you could at least split the PR into several commits : 1. front-end change (parser), 2. legacy schema changer support, 3. fix hydration logic, 4. declarative schema changer support (this worth a separate PR, I think).

@jordanlewis

Copy link
Copy Markdown
Contributor Author

Thanks @chengxiong-ruan I will split it up. Question though, how do I avoid seeing failing tests on the intermediate commits? In particular maybe I need to find a way to tell the declarative schema changer to skip handling DROP TYPE in the case of composite types? I usually like each commit to pass all the tests but maybe that's not a realistic hope at this point.

@chengxiong-ruan

Copy link
Copy Markdown
Contributor

Thanks @chengxiong-ruan I will split it up. Question though, how do I avoid seeing failing tests on the intermediate commits? In particular maybe I need to find a way to tell the declarative schema changer to skip handling DROP TYPE in the case of composite types? I usually like each commit to pass all the tests but maybe that's not a realistic hope at this point.

Ah, yeah, making CI passed on each commit is hard heh. I think in this case for the declarative schema changer, you could raise an unimplemented error like this so that it can fall back to the legacy schema changer.

@jordanlewis

Copy link
Copy Markdown
Contributor Author

@chengxiong-ruan I have removed the declarative schema changer implementation from this PR and split into 2 commits, one for the parser and one for the rest. I wasn't sure what you meant by fixing the hydration so please let me know if this is helpful, or if you have additional split requests.

@chengxiong-ruan chengxiong-ruan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

Comment thread pkg/sql/parser/sql.y Outdated
Comment thread pkg/sql/catalog/typedesc/type_desc.go Outdated
@jordanlewis jordanlewis force-pushed the udct branch 3 times, most recently from 7385453 to 68fa7b4 Compare October 26, 2022 22:24
@jordanlewis

jordanlewis commented Nov 16, 2022

Copy link
Copy Markdown
Contributor Author

@postamar PTAL, I have:

  1. Banned composite types that reference other UDTs (sql: support user-defined types within user-defined composite types #91779)
  2. Added the indicated missing tests
  3. Added commented-out tests for the bug sql: old schema changer doesn't handle type back references properly in alter column #91972

@jordanlewis jordanlewis requested a review from postamar November 16, 2022 17:29

@rafiss rafiss left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

does this support arrays of composite types?

@jordanlewis

Copy link
Copy Markdown
Contributor Author

does this support arrays of composite types?

Yes.

@rafiss rafiss left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can there be tests for arrays of composite types?

Comment thread pkg/sql/catalog/descpb/structured.proto Outdated
Comment thread pkg/sql/types/types.go Outdated

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do we want this to be a table ID instead? Zero meaning it's not an implicit record type, and vice-versa.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't see a use case for recording that table ID, so I guess not? :) We can easily add it if need be.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is the schema changer support PR ready for review? I'm just being mindful of accruing technical debt so to be honest I'd rather this change were part of this PR, but won't insist on it. Hopefully removing this scerrors.NotImplementedErrorf error should be quite easy at this point.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, it's ready for review. I moved it out of the PR at @chengxiong-ruan suggestion to keep things small. At this point I do not want to re-add it - can we get this one merged and then I'll work on the other one immediately?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That's fine by me, thanks! This need to change a zillion things everytime one touches anything in schema-land is sadly a fairly typical feature of our development experience.

Comment thread pkg/sql/logictest/testdata/logic_test/composite_types Outdated
Comment thread pkg/sql/logictest/testdata/logic_test/composite_types Outdated
Comment thread pkg/sql/drop_type.go Outdated

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: consider using catalog.DescriptorIDSet here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done, but for my edification, why? Doesn't IDClosure already dedupe the IDs here? I don't see what it's adding.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry, I overlooked the fact that .GetIDClosure() returned a map and not a slice (or even better, a catalog.DescriptorIDSet). My bad!

Comment thread pkg/sql/create_type.go Outdated

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should we wrap the error in something which references the label? Seems like things could get confusing otherwise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We don't seem to ever wrap the result error from ResolveType in other spots, even in places like CREATE TABLE, so I guess I don't see why this one is special - I'll leave it alone.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I didn't realize that. Fair enough. Let's stay consistent then.

Comment thread pkg/sql/create_type.go Outdated
Comment thread pkg/sql/crdb_internal.go Outdated

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Just curious: is this hydration logic exercised at all now that we can't reference user-defined types within composite types?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it'll no-op. But, there's no harm in leaving it, right? We should always run hydration on types that are pulled from disk, everywhere - better to be consistent about that than have to remember to re-add it later when we resolve #91779.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Agreed in this case.

Comment thread pkg/sql/catalog/typedesc/type_desc.go Outdated

@jordanlewis jordanlewis left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review! PTAL.

Comment thread pkg/sql/catalog/descpb/structured.proto Outdated
Comment thread pkg/sql/catalog/typedesc/type_desc.go Outdated
Comment thread pkg/sql/crdb_internal.go Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it'll no-op. But, there's no harm in leaving it, right? We should always run hydration on types that are pulled from disk, everywhere - better to be consistent about that than have to remember to re-add it later when we resolve #91779.

Comment thread pkg/sql/create_type.go Outdated
Comment thread pkg/sql/create_type.go Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We don't seem to ever wrap the result error from ResolveType in other spots, even in places like CREATE TABLE, so I guess I don't see why this one is special - I'll leave it alone.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's not a correctness bug the way we define it - errors where there shouldn't be errors are not correctness bugs.

The reason this is broken is a type system issue that's unrelated to composite types. I filed #93349 for it but I don't agree that it's a showstopper - it's a classic example of CockroachDB's type system differences from Postgres, and is correctable by adding casts. I've improved the comments here.

Here's an example of the existing broken-ness:

demo@127.0.0.1:26257/defaultdb> values((1, 2)), ((1, null));

ERROR: VALUES types tuple{int, unknown} and tuple{int, int} cannot be matched
SQLSTATE: 42804

Comment thread pkg/sql/logictest/testdata/logic_test/composite_types Outdated
Comment thread pkg/sql/logictest/testdata/logic_test/composite_types Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, it's ready for review. I moved it out of the PR at @chengxiong-ruan suggestion to keep things small. At this point I do not want to re-add it - can we get this one merged and then I'll work on the other one immediately?

Comment thread pkg/sql/types/types.go Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't see a use case for recording that table ID, so I guess not? :) We can easily add it if need be.

This commit adds user-defined composite types, which are created with
the `CREATE TYPE t AS (field1 type1, field2 type2, ...)` syntax.

See documentation here:
https://www.postgresql.org/docs/current/rowtypes.html

It permits this kind of thing:

```
CREATE TYPE t AS (a INT, b INT);
CREATE TABLE a (a t);
INSERT INTO a VALUES((1, 2))
SELECT (a).b FROM a
```

Release note (sql change): add user-defined composite column types

@postamar postamar left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM. Thanks for doing all of this work!

@rafiss arrays of composite types get exercised in this PR in the new composite_types logic test suite, but did you have something else in mind?

@jordanlewis

Copy link
Copy Markdown
Contributor Author

TFTR!

I added the test suite at @rafiss suggestion 😅

@jordanlewis

Copy link
Copy Markdown
Contributor Author

Woohoo! Thanks for the reviews 😁

bors r+

@craig

craig Bot commented Dec 12, 2022

Copy link
Copy Markdown
Contributor

Build succeeded:

@craig craig Bot merged commit aae18c7 into cockroachdb:master Dec 12, 2022
@jordanlewis jordanlewis deleted the udct branch December 12, 2022 17:33
@dvarrazzo

Copy link
Copy Markdown

Hello, just stumbled on thia. Psycopg 2 and 3's test suites have an amount of tests involving composite types. Did you try to un-skip them for the crdb version supporting this feature and give them a go? 🙂

@jordanlewis

Copy link
Copy Markdown
Contributor Author

Hey @dvarrazzo, I didn't yet - if I understand our test infrastructure properly, the nightly tests will automatically exercise this tonight and show us which ones are now passing that should be marked as failed. I'll make sure I have the right idea with @rafiss though :-)

@rafiss

rafiss commented Dec 12, 2022

Copy link
Copy Markdown
Collaborator

We previously worked with @dvarrazzo to test CRDB in the psycopg CI. Example:
image

There's a helper in the test suite that skips certain tests that don't work on CRDB: https://github.com/psycopg/psycopg2/blob/e8d92b74fd4b219cb4725cb906c526f3f2dce31d/tests/testutils.py#L426

So we can try it out and make a commit to psycopg if more tests are passing (conditional on using CRDB >= 23.1)

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.

sql: support user-defined composite/record types

7 participants