-
-
Notifications
You must be signed in to change notification settings - Fork 597
Allow foreign keys on nonlocal tables #9989
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
Conversation
…g table names on roots. This is used for code in doltdb that needs to account for dolt_nonlocal_tables.
|
@nicktobey DOLT
|
|
@coffeegoddd DOLT
|
|
@nicktobey DOLT
|
…d add docstrings.
|
@nicktobey DOLT
|
zachmu
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall seems fine, the table resolution logic is getting away from us but I don't know if you need to address that right now.
Testing should be shored up a bit.
I can't see any obvious reason this is breaking Doltgres. I would expect that some place resolve.Table() was getting called is now not being called, but I didn't see any place that was happening. If you have a link to specific failures I might have better advice.
| // This is useful because the user-backed system table dolt_nonlocal_tables allows table names to resolve to | ||
| // tables on other refs, but sqle.Database is necessary to resolve those refs. | ||
| type TableResolver interface { | ||
| GetDoltDBTableInsensitiveWithRoot(ctx *sql.Context, root RootValue, tblName TableName) (trueTableName TableName, table *Table, found bool, err error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kind of a mouthful, what about just Resolve or ResolveTable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because doltdb.Database implements it and I needed the method name to distinguish itself from all of the other table name resolution methods. And it's not infeasible that we might need to add other methods to this interface that differ in subtle ways (although we should make attempts not to do so.)
But you're right that this interface shouldn't pay for the Database type's sins. I made a wrapper type in doltdb to implement the interface instead, and renamed the interface.
| } | ||
| trueTableName = TableName{ | ||
| Name: trueTableNameString, | ||
| Schema: tblName.Schema, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably should be trueTableName.Schema. I don't know if ResolveTableName actually case-corrects the schema name but it probably should.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ResolveTableName only returns the table name string, and looking at the current implementation, having it case-correct the schema and return the normalized schema may require additional changes to the rootValueStorage interface. I agree it should probably both case-correct the schema name and return a TableName, and I think migrating to using TableName everywhere is important, but that seems like it would be a larger refactor that's probably better saved for a separate PR.
| } | ||
| } | ||
|
|
||
| func (db Database) getNonlocalTableNames(ctx *sql.Context, root doltdb.RootValue) (nonlocalTableNames []string, error error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
| } | ||
|
|
||
| func (db Database) getAllTableNames(ctx *sql.Context, root doltdb.RootValue, includeGeneratedSystemTables bool, includeRootObjects bool) ([]string, error) { | ||
| func (db Database) getAllTableNames(ctx *sql.Context, root doltdb.RootValue, includeGeneratedSystemTables bool, includeRootObjects bool, includeNonlocalTables bool) ([]string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use use a type alias for includNonLocalTables elsewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
| return d.provider | ||
| } | ||
|
|
||
| func (d *DoltSession) GetTableResolver(ctx *sql.Context, dbName string) (doltdb.TableResolver, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not clear why this needs to live here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It requires the session provider?
I inlined it into the GetTableResolver helper function in the same file and got rid of the explicit cast.
| } | ||
|
|
||
| func (t *WritableDoltTable) getDoltTableForFK(ctx *sql.Context, root doltdb.RootValue, lwrTableName string, sqlFk sql.ForeignKeyConstraint) (refTbl *doltdb.Table, err error) { | ||
| _, refTbl, nonlocalTableExists, err := t.db.getNonlocalDoltDBTable(ctx, root, doltdb.TableName{Name: lwrTableName, Schema: sqlFk.ParentSchema}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is the right time for this but food for thought:
The logic for resolving tables is getting more and more complex and special-cased. It would good to encapsulate it better, and to return some kind of TableMetadata object to inform interested callers about things like "this is a system table", "this table was resolved with a non-local override", etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
100% agree. This is only going to get messier and would benefit from some kind of general interface.
I don't think this PR is the right place for that, but it might make sense to do before the next time we have to make things even more complicated. I can ruminate on approaches.
| [[ "$output" =~ "Cannot commit changes on more than one branch / database" ]] || false | ||
| } | ||
|
|
||
| @test "nonlocal: test foreign keys" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are good but it would be nice to have some engine tests for this capability as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For engine tests, I would expect to see e.g. tests that foreign key constraints are correctly enforced and that they resolve to the correct root, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
|
@nicktobey DOLT
|
|
@nicktobey DOLT
|
21158f5 to
0e2ddb4
Compare
|
@nicktobey DOLT
|
|
@coffeegoddd DOLT
|
|
@coffeegoddd DOLT
|
|
@coffeegoddd DOLT
|
This change allows users to add foreign key relations on nonlocal tables (table names that match entries in
dolt_nonlocal_tablesand resolve to tables on other branches).The biggest obstacle was that the foreign key verification logic operates on the DB directly, but resolving the references in the
dolt_nonlocal_tablestable requires a Dolt SQL engine. Because we now use the engine for most operations, the engine is guaranteed to exist, but it wasn't obvious how to allow the storage code to access the engine in a way that didn't break encapsulation or create dependency cycles.The way this PR accomplishes this is by creating a new interface called
doltdb.TableResolver, which has the methodGetDoltTableInsensitiveWithRoot, which can resolve table names at a supplied root value. This object can be instantiated by the Dolt Session and passed into the DB layer.I'm not thrilled about adding the extra confusingly similar methods to
doltdb.Database, but hopefully the differences between them are clear.