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

Resolve issues in UPDATE and DELETE when using schemas #618

Merged
merged 4 commits into from
Feb 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,12 @@ jobs:
run: |
mkdir -p crdb/certs
pushd crdb
wget -qO- https://binaries.cockroachdb.com/cockroach-v2.1.0.linux-amd64.tgz | tar zxv
mv cockroach-v2.1.0.linux-amd64/cockroach .
wget -qO- https://binaries.cockroachdb.com/cockroach-v20.2.4.linux-amd64.tgz | tar zxv
mv cockroach-v20.2.4.linux-amd64/cockroach .
./cockroach cert create-ca --certs-dir certs --ca-key key
./cockroach cert create-client root --certs-dir certs --ca-key key
./cockroach cert create-node localhost 127.0.0.1 `hostname -s` `hostname -f` --certs-dir certs --ca-key key
./cockroach start --certs-dir certs --listen-addr localhost --port 26259 --http-port 8089 --background
./cockroach start-single-node --certs-dir certs --listen-addr localhost --port 26259 --http-port 8089 --background
popd
- name: Build and run soda
env:
Expand Down Expand Up @@ -152,9 +152,9 @@ jobs:
run: |
mkdir -p crdb
pushd crdb
wget -qO- https://binaries.cockroachdb.com/cockroach-v2.1.0.linux-amd64.tgz | tar zxv
mv cockroach-v2.1.0.linux-amd64/cockroach .
./cockroach start --insecure --background
wget -qO- https://binaries.cockroachdb.com/cockroach-v20.2.4.linux-amd64.tgz | tar zxv
mv cockroach-v20.2.4.linux-amd64/cockroach .
./cockroach start-single-node --insecure --background
popd
- name: Build and run soda
env:
Expand Down
2 changes: 1 addition & 1 deletion dialect_cockroach.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (p *cockroach) Update(s store, model *Model, cols columns.Columns) error {
}

func (p *cockroach) Destroy(s store, model *Model) error {
stmt := p.TranslateSQL(fmt.Sprintf("DELETE FROM %s WHERE %s", p.Quote(model.TableName()), model.whereID()))
stmt := p.TranslateSQL(fmt.Sprintf("DELETE FROM %s AS %s WHERE %s", p.Quote(model.TableName()), model.alias(), model.whereID()))
_, err := genericExec(s, stmt, model.ID())
return err
}
Expand Down
4 changes: 2 additions & 2 deletions dialect_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func genericCreate(s store, model *Model, cols columns.Columns, quoter quotable)
}

func genericUpdate(s store, model *Model, cols columns.Columns, quoter quotable) error {
stmt := fmt.Sprintf("UPDATE %s SET %s WHERE %s", quoter.Quote(model.TableName()), cols.Writeable().QuotedUpdateString(quoter), model.whereNamedID())
stmt := fmt.Sprintf("UPDATE %s AS %s SET %s WHERE %s", quoter.Quote(model.TableName()), model.alias(), cols.Writeable().QuotedUpdateString(quoter), model.whereNamedID())
log(logging.SQL, stmt, model.ID())
_, err := s.NamedExec(stmt, model.Value)
if err != nil {
Expand All @@ -109,7 +109,7 @@ func genericUpdate(s store, model *Model, cols columns.Columns, quoter quotable)
}

func genericDestroy(s store, model *Model, quoter quotable) error {
stmt := fmt.Sprintf("DELETE FROM %s WHERE %s", quoter.Quote(model.TableName()), model.whereID())
stmt := fmt.Sprintf("DELETE FROM %s AS %s WHERE %s", quoter.Quote(model.TableName()), model.alias(), model.whereID())
_, err := genericExec(s, stmt, model.ID())
if err != nil {
return err
Expand Down
4 changes: 3 additions & 1 deletion dialect_mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ func (m *mysql) Update(s store, model *Model, cols columns.Columns) error {
}

func (m *mysql) Destroy(s store, model *Model) error {
return errors.Wrap(genericDestroy(s, model, m), "mysql destroy")
stmt := fmt.Sprintf("DELETE FROM %s WHERE %s = ?", m.Quote(model.TableName()), model.IDField())
_, err := genericExec(s, stmt, model.ID())
return errors.Wrap(err, "mysql destroy")
}

func (m *mysql) SelectOne(s store, model *Model, query Query) error {
Expand Down
2 changes: 1 addition & 1 deletion dialect_postgresql.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (p *postgresql) Update(s store, model *Model, cols columns.Columns) error {
}

func (p *postgresql) Destroy(s store, model *Model) error {
stmt := p.TranslateSQL(fmt.Sprintf("DELETE FROM %s WHERE %s", p.Quote(model.TableName()), model.whereID()))
stmt := p.TranslateSQL(fmt.Sprintf("DELETE FROM %s AS %s WHERE %s", p.Quote(model.TableName()), model.alias(), model.whereID()))
_, err := genericExec(s, stmt, model.ID())
if err != nil {
return err
Expand Down
5 changes: 2 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ services:
volumes:
- ./sqldumps:/docker-entrypoint-initdb.d
cockroach:
image: cockroachdb/cockroach:v2.1.0
image: cockroachdb/cockroach:v20.2.4
ports:
- "26257:26257"
- "8080:8080"
volumes:
- "./cockroach-data/roach1:/cockroach/cockroach-data"
command: start --insecure
command: start-single-node --insecure
14 changes: 6 additions & 8 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,21 +223,19 @@ func (m *Model) touchUpdatedAt() {
}

func (m *Model) whereID() string {
as := m.As
if as == "" {
as = strings.ReplaceAll(m.TableName(), ".", "_")
}

return fmt.Sprintf("%s.%s = ?", as, m.IDField())
return fmt.Sprintf("%s.%s = ?", m.alias(), m.IDField())
}

func (m *Model) whereNamedID() string {
func (m *Model) alias() string {
as := m.As
if as == "" {
as = strings.ReplaceAll(m.TableName(), ".", "_")
}
return as
}

return fmt.Sprintf("%s.%s = :%s", as, m.IDField(), m.IDField())
func (m *Model) whereNamedID() string {
return fmt.Sprintf("%s.%s = :%s", m.alias(), m.IDField(), m.IDField())
}

func (m *Model) isSlice() bool {
Expand Down
22 changes: 19 additions & 3 deletions model_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,24 @@ type ContextTable struct {
}

func (t ContextTable) TableName(ctx context.Context) string {
// This is singular on purpose! It will checck if the TableName is properly
// This is singular on purpose! It will check if the TableName is properly
// Respected in slices as well.
return "context_prefix_" + ctx.Value("prefix").(string) + "_table"
prefix := ctx.Value("prefix").(string)

// PostgreSQL and CockroachDB support schemas which work like a prefix. For those cases, we use
// the schema to ensure that name normalization does not cause query problems.
//
// Since this works only for those two databases, we use underscore for the rest.
//
// While this schema is hardcoded, it would have been too difficult to add this special
// case to the migrations.
switch PDB.Dialect.Name() {
case nameCockroach:
fallthrough
case namePostgreSQL:
prefix = prefix + "." + prefix
}
return "context_prefix_" + prefix + "_table"
}

func Test_ModelContext(t *testing.T) {
Expand All @@ -41,6 +56,7 @@ func Test_ModelContext(t *testing.T) {

expected := ContextTable{ID: prefix, Value: prefix}
c := PDB.WithContext(context.WithValue(context.Background(), "prefix", prefix))

r.NoError(c.Create(&expected))

var actual ContextTable
Expand Down Expand Up @@ -79,7 +95,7 @@ func Test_ModelContext(t *testing.T) {
err := c.Create(&ContextTable{ID: "unknown"})
r.Error(err)

if !strings.Contains(err.Error(), "context_prefix_unknown_table") { // All other databases
if !strings.Contains(err.Error(), "context_prefix_unknown") { // All other databases
t.Fatalf("Expected error to contain indicator that table does not exist but got: %s", err.Error())
}
})
Expand Down
10 changes: 2 additions & 8 deletions sql_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,7 @@ func (sq *sqlBuilder) buildfromClauses() fromClauses {
fc := sq.Query.fromClauses
for _, m := range models {
tableName := m.TableName()
asName := m.As
if asName == "" {
asName = strings.Replace(tableName, ".", "_", -1)
}
asName := m.alias()
fc = append(fc, fromClause{
From: tableName,
As: asName,
Expand Down Expand Up @@ -216,10 +213,7 @@ var columnCacheMutex = sync.RWMutex{}

func (sq *sqlBuilder) buildColumns() columns.Columns {
tableName := sq.Model.TableName()
asName := sq.Model.As
if asName == "" {
asName = strings.Replace(tableName, ".", "_", -1)
}
asName := sq.Model.alias()
acl := len(sq.AddColumns)
if acl == 0 {
columnCacheMutex.RLock()
Expand Down
39 changes: 30 additions & 9 deletions testdata/migrations/20210104145901_context_tables.up.fizz
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
create_table("context_prefix_a_table") {
t.Column("id", "string", { primary: true })
t.Column("value", "string")
}

create_table("context_prefix_b_table") {
t.Column("id", "string", { primary: true })
t.Column("value", "string")
}
{{ if eq .Dialect "sqlite3" }}
create_table("context_prefix_a_table") {
t.Column("id", "string", { primary: true })
t.Column("value", "string")
}

create_table("context_prefix_b_table") {
t.Column("id", "string", { primary: true })
t.Column("value", "string")
}
{{ end }}

{{ if eq .Dialect "mysql" }}
create_table("context_prefix_a_table") {
t.Column("id", "string", { primary: true })
t.Column("value", "string")
}

create_table("context_prefix_b_table") {
t.Column("id", "string", { primary: true })
t.Column("value", "string")
}
{{ end }}

{{ if eq .Dialect "postgres" }}
sql("CREATE SCHEMA IF NOT EXISTS \"context_prefix_a\";COMMIT TRANSACTION;BEGIN TRANSACTION;")
sql("CREATE SCHEMA IF NOT EXISTS \"context_prefix_b\";COMMIT TRANSACTION;BEGIN TRANSACTION;")
sql("CREATE TABLE \"context_prefix_a\".\"a_table\" (id character varying(255) NOT NULL, value character varying(255) NOT NULL, created_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL);COMMIT TRANSACTION;BEGIN TRANSACTION;")
sql("CREATE TABLE \"context_prefix_b\".\"b_table\" (id character varying(255) NOT NULL, value character varying(255) NOT NULL, created_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL);COMMIT TRANSACTION;BEGIN TRANSACTION;")
{{ end }}