-
-
Notifications
You must be signed in to change notification settings - Fork 233
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
Introduce ALTER TABLE query #726
Conversation
… in InsertQuery.Ignore()
Unlike most of the other SQL queries, ALTER TABLE has a number of statements it can branch out to. Handling all of them in one object may become overwhelming very soon. + introduce ifExists type alias for boolean `ifExists`
ALTER COLUMN statements can be chained within the same ALTER TABLE query
…emented interfaces in structs
acbf516
to
533383a
Compare
Something I can't make my up about is this: // Similarly to Column in SelectQuery, but we pass `typ` as `schema.Safe(typ)` rather than `schema.UnsafeIdent(typ)`
func (q *AlterColumnSubquery) Type(typ string) *AlterColumnSubquery {
q.modification = schema.QueryWithArgs{
Query: "SET DATA TYPE ?",
Args: []interface{}{schema.Safe(typ)},
}
}
// Similarly to ColumnExpr, but we append our SET DATA TYPE in front of the user's `query`
func (q *AlterColumnSubquery) TypeExpr(query string, args ...interface{}) *AlterColumnSubquery {
query = fmt.Sprintf("SET DATA TYPE %s", query)
q.modification = schema.SafeQuery(query, args)
} It is currently done in the 1st way, but I dislike it, because it is not being explicit (in the method's name) about the fact that the input is treated as a "safe" expression. What do you think? I'm using |
I am not sure, but we can start with collecting examples for different RDBMS. PostgreSQL: ALTER TABLE table_name
ALTER COLUMN column_name1 [SET DATA] TYPE new_data_type,
ALTER COLUMN column_name2 [SET DATA] TYPE new_data_type, MySQL: ALTER TABLE table_name
MODIFY COLUMN column_name1 data_type,
MODIFY COLUMN column_name2 data_type ... ; MSSQL: ALTER TABLE table_name
ALTER COLUMN column_name datatype; SQLite does not support it and requires creating a new table and copying existing data into it. So to work on all supported RDBMS it should be something like: db.NewAlterTable().AlterColumnType(columnName, columnType) At least that is my line of thinking without looking at the PR. |
@@ -224,6 +224,13 @@ func funcName(x interface{}) string { | |||
return s | |||
} | |||
|
|||
func skipIfNotHasFeature(tb testing.TB, db *bun.DB, feat feature.Feature, featName string) { |
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.
nitpick: skipIfNoFeature
"rename column", needsAlterTable, | ||
func(db *bun.DB) schema.QueryAppender { | ||
return db.NewAlterTable().Model((*Model)(nil)). | ||
RenameColumn().Column("old").To("new") |
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.
Any reasons why not simply RenameColumn("old_name", "new_name")
?
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.
Ditto: #726 (comment)
"rename table", needsAlterTable, | ||
func(db *bun.DB) schema.QueryAppender { | ||
return db.NewAlterTable().Model((*Model)(nil)). | ||
Rename().To("new_models") |
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.
Why not RenameTable("new_name")
?
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.
You're right, that's actually what I started with, but I wasn't sure if it wasn't more consistent with the rest of the API to mirror actual the SQL keywords.
I agree that that what you've suggested is much more readable and explicit though.
func(db *bun.DB) schema.QueryAppender { | ||
// Change column type with common SQL data type | ||
return db.NewAlterTable().Model((*Model)(nil)). | ||
AlterColumn().Column("old").Type(sqltype.Blob) |
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.
Same suggestion AlterColumnType(colName, colType)
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.
Ditto: #726 (comment)
type ChainableSubquery interface { | ||
SubqueryAppender | ||
chain() | ||
} |
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.
Looks like you've come with some new design/idea for Bun query builder. It could be a nice idea, but let's describe it first and come to understanding how we will use it with other queries like SELECT/UPDATE/INSERT.
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.
...but I'd rather not design something new just for ALTER TABLE
and instead try to solve the task with existing APIs.
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.
Agree.
I've added some more thoughts about that below.
Going to refactor ChainableSubquery
away in favor of the existing API 👌.
It can stay in Git history, or I could describe it in a proposal, if you think it could be used in the rest of the lib.
return b, nil | ||
} | ||
return append(b, "IF NOT EXISTS "...), nil | ||
} |
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.
Looks like over-engineering to me, but if you insist... :)
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.
Perhaps you're right; I was toying with the concept of custom types here.
I'm rather new to Go, still trying to discover what is and isn't idiomatic.
After looking at the PR it looks like it makes sense to split db.NewRenameTable().Table("old_table_name").To("new_table_name")
db.NewRenameTable().Model(&model).To("new_table_name")
db.NewAlterColumnType().Model(&model).Column("name1", "type1").Column("name2", "type2")
db.NewDropColumn().Model(&model).Column("col1").Column("col2") That would make API cleaner and we won't need chainable/parent queries any more. WDYT? |
tldr; Syntax and feature support
Following your suggestion, I've put up the examples for most of the queries from this list in the dialects Bun supports. RENAME TABLE-- PostgresSQL
ALTER TABLE t RENAME TO new;
-- MySQL
ALTER TABLE t RENAME TO new;
-- or, for multiple renamed tables:
RENAME TABLE
t TO new,
d TO dew;
-- MSSQL
EXEC sp_rename 't', 'new', 'TABLE';
-- SQLite
ALTER TABLE t RENAME TO new; RENAME COLUMN-- PostgresSQL
ALTER TABLE t RENAME COLUMN a TO new_a;
-- MySQL
ALTER TABLE t RENAME COLUMN a TO new_a;
-- MSSQL
EXEC sp_rename 't.a', 'col_a', 'COLUMN'
-- SQLite
ALTER TABLE t RENAME COLUMN a TO new_a; CHANGE COLUMN TYPE-- PostgresSQL
ALTER TABLE t
ALTER COLUMN a SET DATA TYPE new_data_type,
ALTER COLUMN b SET DATA TYPE new_data_type;
-- MySQL
ALTER TABLE t
MODIFY a new_data_type,
MODIFY b new_data_type;
-- MSSQL
ALTER TABLE table_name ALTER COLUMN a new_data_type;
ALTER TABLE table_name ALTER COLUMN b new_data_type;
-- SQLite
Not supported in the standard API, probably something like this:
BEGIN TRANSACTION
ALTER TABLE t ADD COLUMN __temp_a new_data_type_a;
UPDATE t SET __temp_a = a;
ALTER TABLE t DROP COLUMN a;
ALTER TABLE t RENAME COLUMN __temp_a TO a;
COMMIT; ADD COLUMN-- PostgresSQL
ALTER TABLE t
ADD COLUMN a data_type,
ADD COLUMN b data_type;
-- MySQL
ALTER TABLE t
ADD COLUMN a data_type,
ADD COLUMN b data_type;
-- MSSQL
ALTER TABLE t
ADD
a data_type,
b data_type;
-- SQLite
ALTER TABLE t ADD COLUMN a data_type;
ALTER TABLE t ADD COLUMN b data_type; SET DEFAULT-- PostgresSQL
ALTER TABLE t
ALTER COLUMN a SET DEFAULT 0,
ALTER COLUMN b SET DEFAULT 0;
-- MySQL
ALTER TABLE t
ALTER COLUMN a SET DEFAULT 0,
ALTER COLUMN b SET DEFAULT 0;
-- MSSQL
ALTER TABLE t ADD
CONSTRAINT my_default_value_a DEFAULT 0 FOR a,
CONSTRAINT my_default_value_a DEFAULT 0 FOR b;
-- SQLite
Not supported in the standard API:
BEGIN TRANSACTION
ALTER TABLE t ADD COLUMN __temp_a data_type_a DEFAULT 0;
UPDATE t SET __temp_a = a;
ALTER TABLE t DROP COLUMN a;
ALTER TABLE t RENAME COLUMN __temp_a TO a;
COMMIT; There are 2 things to notice here:
In conclusion, I think these APIs for altering DB schema have less commonalities than I have initially hoped for, and in this case it really makes more sense to design Bun's API around specific operations rather than SQL syntactic constructs. |
Fully agree. Perhaps even ignore RenameTable(context.Context, oldName, newName string) error
AddColumn(context.Context, *sqlschema.Column) error
DropColumn(context.Context, colName string) error
ChangeColumnType(context.Context, colName, colType string) error This way you can do whatever you want in dialects, .e.g. start transactions, create temp tables etc. |
After giving it some thought here's roughly the solution I came up with:
Edit: Provide minimal API, as suggested here. Although
type Dialect interface {
RenameTable(...) []QueryWithArgs
AddColumn(...) []QueryWithArgs
} These methods return a slice of queries, so that, for instance, SQLite could build several queries for something like
package bun
func DropDefault(ctx context.Context, db *DB, colName string) {
// Build query
...
// Do
if len(q.mod) > 1 {
// execute raw query at q.mod[0]
}
// start a transaction and execute each one of the queries in q.mod
} Will add implementation for that tomorrow, let's see how that works out. |
The idea sounds right, but returning I guess you are worried about not having access to |
You're right, I too thought that returning
The only alternative that comes to my mind atm is to have the dialects return
Yes, if we put |
I've missed that, but it would be nice to have. It is not mandatory though and we could start with Go-only migrations. Perhaps we could achieve that by having some special |
This pull request has been automatically marked as stale because it has not had activity in the last 30 days. If there is no update within the next 7 days, this pr will be closed. Please feel free to give a status update now, ping for review, when it's ready. Thank you for your contributions! |
This PR introduces
ALTER TABLE
query as part of the effort to bring auto-migration tobun
, see #456.Therefore, only a limited scope of the functionality related to modifying a database schema is supported in this step.
Changes also include: