diff --git a/pkg/codegen/assets/store_rdbms.gen.go.tpl b/pkg/codegen/assets/store_rdbms.gen.go.tpl index 4ab48704d..d07980eea 100644 --- a/pkg/codegen/assets/store_rdbms.gen.go.tpl +++ b/pkg/codegen/assets/store_rdbms.gen.go.tpl @@ -281,7 +281,7 @@ func (s Store) Search{{ pubIdent $.Types.Plural }}(ctx context.Context, f {{ $.T {{ end -}} ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } {{ end }} @@ -309,7 +309,7 @@ func (s Store) Create{{ pubIdent $.Types.Singular }}(ctx context.Context, rr ... for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.{{ $.Types.Singular }}Table()).SetMap(s.internal{{ pubIdent $.Types.Singular }}Encoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -319,7 +319,7 @@ func (s Store) Create{{ pubIdent $.Types.Singular }}(ctx context.Context, rr ... // Update{{ pubIdent $.Types.Singular }} updates one or more existing rows in {{ $.RDBMS.Table }} func (s Store) Update{{ pubIdent $.Types.Singular }}(ctx context.Context, rr ... *{{ $.Types.GoType }}) error { - return s.PartialUpdate{{ pubIdent $.Types.Singular }}(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdate{{ pubIdent $.Types.Singular }}(ctx, nil, rr...)) } // PartialUpdate{{ pubIdent $.Types.Singular }} updates one or more existing rows in {{ $.RDBMS.Table }} @@ -343,7 +343,7 @@ func (s Store) PartialUpdate{{ pubIdent $.Types.Singular }}(ctx context.Context, {{- end -}} ).Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -361,7 +361,7 @@ func (s Store) Remove{{ pubIdent $.Types.Singular }}(ctx context.Context, rr ... for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.{{ $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }})).Where({{ template "filterByPrimaryKeys" $.Fields }},)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -372,19 +372,19 @@ func (s Store) Remove{{ pubIdent $.Types.Singular }}(ctx context.Context, rr ... // Remove{{ pubIdent $.Types.Singular }}By{{ template "primaryKeySuffix" $.Fields }} removes row from the {{ $.RDBMS.Table }} table func (s Store) Remove{{ pubIdent $.Types.Singular }}By{{ template "primaryKeySuffix" $.Fields }}(ctx context.Context {{ template "primaryKeyArgs" $.Fields }}) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.{{ $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }})).Where({{ template "filterByPrimaryKeysWithArgs" $.Fields }},)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.{{ $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }})).Where({{ template "filterByPrimaryKeysWithArgs" $.Fields }},))) } // Truncate{{ pubIdent $.Types.Plural }} removes all rows from the {{ $.RDBMS.Table }} table func (s Store) Truncate{{ pubIdent $.Types.Plural }}(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.{{ $.Types.Singular }}Table()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.{{ $.Types.Singular }}Table())) } // ExecUpdate{{ pubIdent $.Types.Plural }} updates all matched (by cnd) rows in {{ $.RDBMS.Table }} with given data func (s Store) ExecUpdate{{ pubIdent $.Types.Plural }}(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.{{ $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }})).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.{{ $.Types.Singular }}Table({{ printf "%q" .RDBMS.Alias }})).Where(cnd).SetMap(set))) } // {{ $.Types.Singular }}Lookup prepares {{ $.Types.Singular }} query and executes it, diff --git a/store/errors.go b/store/errors.go index 688f87389..c6fc349e9 100644 --- a/store/errors.go +++ b/store/errors.go @@ -15,10 +15,16 @@ func (e *storeError) Unwrap() error { return e.err } +func (e *storeError) Wrap(err error) error { + e.err = err + return e +} + var ( - ErrNotFound = &storeError{msg: "not found"} + ErrNotFound = &storeError{msg: "not found"} + ErrNotUnique = &storeError{msg: "not unique"} ) -func Error(msg string, err error) error { +func WrappedError(err error, msg string, a ...interface{}) error { return &storeError{msg, err} } diff --git a/store/mysql/mysql.go b/store/mysql/mysql.go index 2a90bf62a..4ecffb3f5 100644 --- a/store/mysql/mysql.go +++ b/store/mysql/mysql.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/Masterminds/squirrel" + "github.com/cortezaproject/corteza-server/store" "github.com/cortezaproject/corteza-server/store/rdbms" "github.com/go-sql-driver/mysql" "go.uber.org/zap" @@ -26,6 +27,7 @@ func New(ctx context.Context, dsn string) (s *Store, err error) { cfg.PlaceholderFormat = squirrel.Question cfg.TxRetryErrHandler = txRetryErrHandler + cfg.ErrorHandler = errorHandler s = new(Store) if s.Store, err = rdbms.New(ctx, cfg); err != nil { @@ -105,3 +107,18 @@ func txRetryErrHandler(try int, err error) bool { return false } + +func errorHandler(err error) error { + if err != nil { + + if implErr, ok := err.(*mysql.MySQLError); ok { + // https://www.fromdual.com/de/mysql-error-codes-and-messages + switch implErr.Number { + case 1062: // Can't write, because of unique constraint, to table '%s' + return store.ErrNotUnique.Wrap(implErr) + } + } + } + + return err +} diff --git a/store/pgsql/pgsql.go b/store/pgsql/pgsql.go index dbec8969a..7acfc216d 100644 --- a/store/pgsql/pgsql.go +++ b/store/pgsql/pgsql.go @@ -4,8 +4,9 @@ import ( "context" "fmt" "github.com/Masterminds/squirrel" + "github.com/cortezaproject/corteza-server/store" "github.com/cortezaproject/corteza-server/store/rdbms" - _ "github.com/lib/pq" + "github.com/lib/pq" "go.uber.org/zap" "net/url" "strings" @@ -25,6 +26,7 @@ func New(ctx context.Context, dsn string) (s *Store, err error) { } cfg.PlaceholderFormat = squirrel.Dollar + cfg.ErrorHandler = errorHandler s = new(Store) if s.Store, err = rdbms.New(ctx, cfg); err != nil { @@ -68,3 +70,16 @@ func ProcDataSourceName(dsn string) (c *rdbms.Config, err error) { DBName: strings.Trim(u.Path, "/"), }, nil } + +func errorHandler(err error) error { + if err != nil { + if implErr, ok := err.(*pq.Error); ok { + switch implErr.Code.Name() { + case "unique_violation": + return store.ErrNotUnique.Wrap(implErr) + } + } + } + + return err +} diff --git a/store/rdbms/actionlog.gen.go b/store/rdbms/actionlog.gen.go index a08b4ed91..a10c4e6db 100644 --- a/store/rdbms/actionlog.gen.go +++ b/store/rdbms/actionlog.gen.go @@ -188,7 +188,7 @@ func (s Store) SearchActionlogs(ctx context.Context, f actionlog.Filter) (action } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // CreateActionlog creates one or more rows in actionlog table @@ -201,7 +201,7 @@ func (s Store) CreateActionlog(ctx context.Context, rr ...*actionlog.Action) err for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.ActionlogTable()).SetMap(s.internalActionlogEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -211,7 +211,7 @@ func (s Store) CreateActionlog(ctx context.Context, rr ...*actionlog.Action) err // UpdateActionlog updates one or more existing rows in actionlog func (s Store) UpdateActionlog(ctx context.Context, rr ...*actionlog.Action) error { - return s.PartialUpdateActionlog(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateActionlog(ctx, nil, rr...)) } // PartialUpdateActionlog updates one or more existing rows in actionlog @@ -229,7 +229,7 @@ func (s Store) PartialUpdateActionlog(ctx context.Context, onlyColumns []string, squirrel.Eq{s.preprocessColumn("alg.id", ""): s.preprocessValue(res.ID, "")}, s.internalActionlogEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -247,7 +247,7 @@ func (s Store) RemoveActionlog(ctx context.Context, rr ...*actionlog.Action) err for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ActionlogTable("alg")).Where(squirrel.Eq{s.preprocessColumn("alg.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -257,17 +257,17 @@ func (s Store) RemoveActionlog(ctx context.Context, rr ...*actionlog.Action) err // RemoveActionlogByID removes row from the actionlog table func (s Store) RemoveActionlogByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ActionlogTable("alg")).Where(squirrel.Eq{s.preprocessColumn("alg.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ActionlogTable("alg")).Where(squirrel.Eq{s.preprocessColumn("alg.id", ""): s.preprocessValue(ID, "")}))) } // TruncateActionlogs removes all rows from the actionlog table func (s Store) TruncateActionlogs(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.ActionlogTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.ActionlogTable())) } // ExecUpdateActionlogs updates all matched (by cnd) rows in actionlog with given data func (s Store) ExecUpdateActionlogs(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.ActionlogTable("alg")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.ActionlogTable("alg")).Where(cnd).SetMap(set))) } // ActionlogLookup prepares Actionlog query and executes it, diff --git a/store/rdbms/applications.gen.go b/store/rdbms/applications.gen.go index 7807d9f5e..02f8a8985 100644 --- a/store/rdbms/applications.gen.go +++ b/store/rdbms/applications.gen.go @@ -219,7 +219,7 @@ func (s Store) SearchApplications(ctx context.Context, f types.ApplicationFilter } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupApplicationByID searches for application by ID @@ -241,7 +241,7 @@ func (s Store) CreateApplication(ctx context.Context, rr ...*types.Application) for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.ApplicationTable()).SetMap(s.internalApplicationEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -251,7 +251,7 @@ func (s Store) CreateApplication(ctx context.Context, rr ...*types.Application) // UpdateApplication updates one or more existing rows in applications func (s Store) UpdateApplication(ctx context.Context, rr ...*types.Application) error { - return s.PartialUpdateApplication(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateApplication(ctx, nil, rr...)) } // PartialUpdateApplication updates one or more existing rows in applications @@ -269,7 +269,7 @@ func (s Store) PartialUpdateApplication(ctx context.Context, onlyColumns []strin squirrel.Eq{s.preprocessColumn("app.id", ""): s.preprocessValue(res.ID, "")}, s.internalApplicationEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -287,7 +287,7 @@ func (s Store) RemoveApplication(ctx context.Context, rr ...*types.Application) for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ApplicationTable("app")).Where(squirrel.Eq{s.preprocessColumn("app.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -297,17 +297,17 @@ func (s Store) RemoveApplication(ctx context.Context, rr ...*types.Application) // RemoveApplicationByID removes row from the applications table func (s Store) RemoveApplicationByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ApplicationTable("app")).Where(squirrel.Eq{s.preprocessColumn("app.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ApplicationTable("app")).Where(squirrel.Eq{s.preprocessColumn("app.id", ""): s.preprocessValue(ID, "")}))) } // TruncateApplications removes all rows from the applications table func (s Store) TruncateApplications(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.ApplicationTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.ApplicationTable())) } // ExecUpdateApplications updates all matched (by cnd) rows in applications with given data func (s Store) ExecUpdateApplications(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.ApplicationTable("app")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.ApplicationTable("app")).Where(cnd).SetMap(set))) } // ApplicationLookup prepares Application query and executes it, diff --git a/store/rdbms/attachments.gen.go b/store/rdbms/attachments.gen.go index be8c3ef41..ca9b71dd4 100644 --- a/store/rdbms/attachments.gen.go +++ b/store/rdbms/attachments.gen.go @@ -64,7 +64,7 @@ func (s Store) SearchAttachments(ctx context.Context, f types.AttachmentFilter) } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupAttachmentByID searches for attachment by its ID @@ -86,7 +86,7 @@ func (s Store) CreateAttachment(ctx context.Context, rr ...*types.Attachment) er for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.AttachmentTable()).SetMap(s.internalAttachmentEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -96,7 +96,7 @@ func (s Store) CreateAttachment(ctx context.Context, rr ...*types.Attachment) er // UpdateAttachment updates one or more existing rows in attachments func (s Store) UpdateAttachment(ctx context.Context, rr ...*types.Attachment) error { - return s.PartialUpdateAttachment(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateAttachment(ctx, nil, rr...)) } // PartialUpdateAttachment updates one or more existing rows in attachments @@ -114,7 +114,7 @@ func (s Store) PartialUpdateAttachment(ctx context.Context, onlyColumns []string squirrel.Eq{s.preprocessColumn("att.id", ""): s.preprocessValue(res.ID, "")}, s.internalAttachmentEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -132,7 +132,7 @@ func (s Store) RemoveAttachment(ctx context.Context, rr ...*types.Attachment) er for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.AttachmentTable("att")).Where(squirrel.Eq{s.preprocessColumn("att.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -142,17 +142,17 @@ func (s Store) RemoveAttachment(ctx context.Context, rr ...*types.Attachment) er // RemoveAttachmentByID removes row from the attachments table func (s Store) RemoveAttachmentByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.AttachmentTable("att")).Where(squirrel.Eq{s.preprocessColumn("att.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.AttachmentTable("att")).Where(squirrel.Eq{s.preprocessColumn("att.id", ""): s.preprocessValue(ID, "")}))) } // TruncateAttachments removes all rows from the attachments table func (s Store) TruncateAttachments(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.AttachmentTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.AttachmentTable())) } // ExecUpdateAttachments updates all matched (by cnd) rows in attachments with given data func (s Store) ExecUpdateAttachments(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.AttachmentTable("att")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.AttachmentTable("att")).Where(cnd).SetMap(set))) } // AttachmentLookup prepares Attachment query and executes it, diff --git a/store/rdbms/compose_charts.gen.go b/store/rdbms/compose_charts.gen.go index 9f4e0dacb..c5164d377 100644 --- a/store/rdbms/compose_charts.gen.go +++ b/store/rdbms/compose_charts.gen.go @@ -219,7 +219,7 @@ func (s Store) SearchComposeCharts(ctx context.Context, f types.ChartFilter) (ty } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupComposeChartByID searches for compose chart by ID @@ -248,7 +248,7 @@ func (s Store) CreateComposeChart(ctx context.Context, rr ...*types.Chart) error for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.ComposeChartTable()).SetMap(s.internalComposeChartEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -258,7 +258,7 @@ func (s Store) CreateComposeChart(ctx context.Context, rr ...*types.Chart) error // UpdateComposeChart updates one or more existing rows in compose_chart func (s Store) UpdateComposeChart(ctx context.Context, rr ...*types.Chart) error { - return s.PartialUpdateComposeChart(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateComposeChart(ctx, nil, rr...)) } // PartialUpdateComposeChart updates one or more existing rows in compose_chart @@ -276,7 +276,7 @@ func (s Store) PartialUpdateComposeChart(ctx context.Context, onlyColumns []stri squirrel.Eq{s.preprocessColumn("cch.id", ""): s.preprocessValue(res.ID, "")}, s.internalComposeChartEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -294,7 +294,7 @@ func (s Store) RemoveComposeChart(ctx context.Context, rr ...*types.Chart) error for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeChartTable("cch")).Where(squirrel.Eq{s.preprocessColumn("cch.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -304,17 +304,17 @@ func (s Store) RemoveComposeChart(ctx context.Context, rr ...*types.Chart) error // RemoveComposeChartByID removes row from the compose_chart table func (s Store) RemoveComposeChartByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeChartTable("cch")).Where(squirrel.Eq{s.preprocessColumn("cch.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeChartTable("cch")).Where(squirrel.Eq{s.preprocessColumn("cch.id", ""): s.preprocessValue(ID, "")}))) } // TruncateComposeCharts removes all rows from the compose_chart table func (s Store) TruncateComposeCharts(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.ComposeChartTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.ComposeChartTable())) } // ExecUpdateComposeCharts updates all matched (by cnd) rows in compose_chart with given data func (s Store) ExecUpdateComposeCharts(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.ComposeChartTable("cch")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.ComposeChartTable("cch")).Where(cnd).SetMap(set))) } // ComposeChartLookup prepares ComposeChart query and executes it, diff --git a/store/rdbms/compose_module_fields.gen.go b/store/rdbms/compose_module_fields.gen.go index 4477d4635..0ab9c0ebc 100644 --- a/store/rdbms/compose_module_fields.gen.go +++ b/store/rdbms/compose_module_fields.gen.go @@ -28,7 +28,7 @@ func (s Store) CreateComposeModuleField(ctx context.Context, rr ...*types.Module for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.ComposeModuleFieldTable()).SetMap(s.internalComposeModuleFieldEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -38,7 +38,7 @@ func (s Store) CreateComposeModuleField(ctx context.Context, rr ...*types.Module // UpdateComposeModuleField updates one or more existing rows in compose_module_field func (s Store) UpdateComposeModuleField(ctx context.Context, rr ...*types.ModuleField) error { - return s.PartialUpdateComposeModuleField(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateComposeModuleField(ctx, nil, rr...)) } // PartialUpdateComposeModuleField updates one or more existing rows in compose_module_field @@ -56,7 +56,7 @@ func (s Store) PartialUpdateComposeModuleField(ctx context.Context, onlyColumns squirrel.Eq{s.preprocessColumn("cmf.id", ""): s.preprocessValue(res.ID, "")}, s.internalComposeModuleFieldEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -74,7 +74,7 @@ func (s Store) RemoveComposeModuleField(ctx context.Context, rr ...*types.Module for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeModuleFieldTable("cmf")).Where(squirrel.Eq{s.preprocessColumn("cmf.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -84,17 +84,17 @@ func (s Store) RemoveComposeModuleField(ctx context.Context, rr ...*types.Module // RemoveComposeModuleFieldByID removes row from the compose_module_field table func (s Store) RemoveComposeModuleFieldByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeModuleFieldTable("cmf")).Where(squirrel.Eq{s.preprocessColumn("cmf.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeModuleFieldTable("cmf")).Where(squirrel.Eq{s.preprocessColumn("cmf.id", ""): s.preprocessValue(ID, "")}))) } // TruncateComposeModuleFields removes all rows from the compose_module_field table func (s Store) TruncateComposeModuleFields(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.ComposeModuleFieldTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.ComposeModuleFieldTable())) } // ExecUpdateComposeModuleFields updates all matched (by cnd) rows in compose_module_field with given data func (s Store) ExecUpdateComposeModuleFields(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.ComposeModuleFieldTable("cmf")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.ComposeModuleFieldTable("cmf")).Where(cnd).SetMap(set))) } // ComposeModuleFieldLookup prepares ComposeModuleField query and executes it, diff --git a/store/rdbms/compose_modules.gen.go b/store/rdbms/compose_modules.gen.go index abfff0967..dd0d495d3 100644 --- a/store/rdbms/compose_modules.gen.go +++ b/store/rdbms/compose_modules.gen.go @@ -219,7 +219,7 @@ func (s Store) SearchComposeModules(ctx context.Context, f types.ModuleFilter) ( } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupComposeModuleByHandle searches for compose module by handle (case-insensitive) @@ -248,7 +248,7 @@ func (s Store) CreateComposeModule(ctx context.Context, rr ...*types.Module) err for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.ComposeModuleTable()).SetMap(s.internalComposeModuleEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -258,7 +258,7 @@ func (s Store) CreateComposeModule(ctx context.Context, rr ...*types.Module) err // UpdateComposeModule updates one or more existing rows in compose_module func (s Store) UpdateComposeModule(ctx context.Context, rr ...*types.Module) error { - return s.PartialUpdateComposeModule(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateComposeModule(ctx, nil, rr...)) } // PartialUpdateComposeModule updates one or more existing rows in compose_module @@ -276,7 +276,7 @@ func (s Store) PartialUpdateComposeModule(ctx context.Context, onlyColumns []str squirrel.Eq{s.preprocessColumn("cmd.id", ""): s.preprocessValue(res.ID, "")}, s.internalComposeModuleEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -294,7 +294,7 @@ func (s Store) RemoveComposeModule(ctx context.Context, rr ...*types.Module) err for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeModuleTable("cmd")).Where(squirrel.Eq{s.preprocessColumn("cmd.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -304,17 +304,17 @@ func (s Store) RemoveComposeModule(ctx context.Context, rr ...*types.Module) err // RemoveComposeModuleByID removes row from the compose_module table func (s Store) RemoveComposeModuleByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeModuleTable("cmd")).Where(squirrel.Eq{s.preprocessColumn("cmd.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeModuleTable("cmd")).Where(squirrel.Eq{s.preprocessColumn("cmd.id", ""): s.preprocessValue(ID, "")}))) } // TruncateComposeModules removes all rows from the compose_module table func (s Store) TruncateComposeModules(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.ComposeModuleTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.ComposeModuleTable())) } // ExecUpdateComposeModules updates all matched (by cnd) rows in compose_module with given data func (s Store) ExecUpdateComposeModules(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.ComposeModuleTable("cmd")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.ComposeModuleTable("cmd")).Where(cnd).SetMap(set))) } // ComposeModuleLookup prepares ComposeModule query and executes it, diff --git a/store/rdbms/compose_namespaces.gen.go b/store/rdbms/compose_namespaces.gen.go index 5d12a6636..8d47bee31 100644 --- a/store/rdbms/compose_namespaces.gen.go +++ b/store/rdbms/compose_namespaces.gen.go @@ -219,7 +219,7 @@ func (s Store) SearchComposeNamespaces(ctx context.Context, f types.NamespaceFil } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupComposeNamespaceBySlug searches for namespace by slug (case-insensitive) @@ -248,7 +248,7 @@ func (s Store) CreateComposeNamespace(ctx context.Context, rr ...*types.Namespac for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.ComposeNamespaceTable()).SetMap(s.internalComposeNamespaceEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -258,7 +258,7 @@ func (s Store) CreateComposeNamespace(ctx context.Context, rr ...*types.Namespac // UpdateComposeNamespace updates one or more existing rows in compose_namespace func (s Store) UpdateComposeNamespace(ctx context.Context, rr ...*types.Namespace) error { - return s.PartialUpdateComposeNamespace(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateComposeNamespace(ctx, nil, rr...)) } // PartialUpdateComposeNamespace updates one or more existing rows in compose_namespace @@ -276,7 +276,7 @@ func (s Store) PartialUpdateComposeNamespace(ctx context.Context, onlyColumns [] squirrel.Eq{s.preprocessColumn("cns.id", ""): s.preprocessValue(res.ID, "")}, s.internalComposeNamespaceEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -294,7 +294,7 @@ func (s Store) RemoveComposeNamespace(ctx context.Context, rr ...*types.Namespac for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeNamespaceTable("cns")).Where(squirrel.Eq{s.preprocessColumn("cns.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -304,17 +304,17 @@ func (s Store) RemoveComposeNamespace(ctx context.Context, rr ...*types.Namespac // RemoveComposeNamespaceByID removes row from the compose_namespace table func (s Store) RemoveComposeNamespaceByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeNamespaceTable("cns")).Where(squirrel.Eq{s.preprocessColumn("cns.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposeNamespaceTable("cns")).Where(squirrel.Eq{s.preprocessColumn("cns.id", ""): s.preprocessValue(ID, "")}))) } // TruncateComposeNamespaces removes all rows from the compose_namespace table func (s Store) TruncateComposeNamespaces(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.ComposeNamespaceTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.ComposeNamespaceTable())) } // ExecUpdateComposeNamespaces updates all matched (by cnd) rows in compose_namespace with given data func (s Store) ExecUpdateComposeNamespaces(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.ComposeNamespaceTable("cns")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.ComposeNamespaceTable("cns")).Where(cnd).SetMap(set))) } // ComposeNamespaceLookup prepares ComposeNamespace query and executes it, diff --git a/store/rdbms/compose_pages.gen.go b/store/rdbms/compose_pages.gen.go index d45df55e0..834c46daa 100644 --- a/store/rdbms/compose_pages.gen.go +++ b/store/rdbms/compose_pages.gen.go @@ -219,7 +219,7 @@ func (s Store) SearchComposePages(ctx context.Context, f types.PageFilter) (type } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupComposePageByHandle searches for page chart by handle (case-insensitive) @@ -248,7 +248,7 @@ func (s Store) CreateComposePage(ctx context.Context, rr ...*types.Page) error { for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.ComposePageTable()).SetMap(s.internalComposePageEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -258,7 +258,7 @@ func (s Store) CreateComposePage(ctx context.Context, rr ...*types.Page) error { // UpdateComposePage updates one or more existing rows in compose_page func (s Store) UpdateComposePage(ctx context.Context, rr ...*types.Page) error { - return s.PartialUpdateComposePage(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateComposePage(ctx, nil, rr...)) } // PartialUpdateComposePage updates one or more existing rows in compose_page @@ -276,7 +276,7 @@ func (s Store) PartialUpdateComposePage(ctx context.Context, onlyColumns []strin squirrel.Eq{s.preprocessColumn("cpg.id", ""): s.preprocessValue(res.ID, "")}, s.internalComposePageEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -294,7 +294,7 @@ func (s Store) RemoveComposePage(ctx context.Context, rr ...*types.Page) error { for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposePageTable("cpg")).Where(squirrel.Eq{s.preprocessColumn("cpg.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -304,17 +304,17 @@ func (s Store) RemoveComposePage(ctx context.Context, rr ...*types.Page) error { // RemoveComposePageByID removes row from the compose_page table func (s Store) RemoveComposePageByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposePageTable("cpg")).Where(squirrel.Eq{s.preprocessColumn("cpg.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ComposePageTable("cpg")).Where(squirrel.Eq{s.preprocessColumn("cpg.id", ""): s.preprocessValue(ID, "")}))) } // TruncateComposePages removes all rows from the compose_page table func (s Store) TruncateComposePages(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.ComposePageTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.ComposePageTable())) } // ExecUpdateComposePages updates all matched (by cnd) rows in compose_page with given data func (s Store) ExecUpdateComposePages(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.ComposePageTable("cpg")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.ComposePageTable("cpg")).Where(cnd).SetMap(set))) } // ComposePageLookup prepares ComposePage query and executes it, diff --git a/store/rdbms/credentials.gen.go b/store/rdbms/credentials.gen.go index ff33ea666..ad054308d 100644 --- a/store/rdbms/credentials.gen.go +++ b/store/rdbms/credentials.gen.go @@ -64,7 +64,7 @@ func (s Store) SearchCredentials(ctx context.Context, f types.CredentialsFilter) } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupCredentialsByID searches for credentials by ID @@ -86,7 +86,7 @@ func (s Store) CreateCredentials(ctx context.Context, rr ...*types.Credentials) for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.CredentialsTable()).SetMap(s.internalCredentialsEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -96,7 +96,7 @@ func (s Store) CreateCredentials(ctx context.Context, rr ...*types.Credentials) // UpdateCredentials updates one or more existing rows in credentials func (s Store) UpdateCredentials(ctx context.Context, rr ...*types.Credentials) error { - return s.PartialUpdateCredentials(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateCredentials(ctx, nil, rr...)) } // PartialUpdateCredentials updates one or more existing rows in credentials @@ -114,7 +114,7 @@ func (s Store) PartialUpdateCredentials(ctx context.Context, onlyColumns []strin squirrel.Eq{s.preprocessColumn("crd.id", ""): s.preprocessValue(res.ID, "")}, s.internalCredentialsEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -132,7 +132,7 @@ func (s Store) RemoveCredentials(ctx context.Context, rr ...*types.Credentials) for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.CredentialsTable("crd")).Where(squirrel.Eq{s.preprocessColumn("crd.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -142,17 +142,17 @@ func (s Store) RemoveCredentials(ctx context.Context, rr ...*types.Credentials) // RemoveCredentialsByID removes row from the credentials table func (s Store) RemoveCredentialsByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.CredentialsTable("crd")).Where(squirrel.Eq{s.preprocessColumn("crd.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.CredentialsTable("crd")).Where(squirrel.Eq{s.preprocessColumn("crd.id", ""): s.preprocessValue(ID, "")}))) } // TruncateCredentials removes all rows from the credentials table func (s Store) TruncateCredentials(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.CredentialsTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.CredentialsTable())) } // ExecUpdateCredentials updates all matched (by cnd) rows in credentials with given data func (s Store) ExecUpdateCredentials(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.CredentialsTable("crd")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.CredentialsTable("crd")).Where(cnd).SetMap(set))) } // CredentialsLookup prepares Credentials query and executes it, diff --git a/store/rdbms/rbac_rules.gen.go b/store/rdbms/rbac_rules.gen.go index b030884ca..a8f83592c 100644 --- a/store/rdbms/rbac_rules.gen.go +++ b/store/rdbms/rbac_rules.gen.go @@ -61,7 +61,7 @@ func (s Store) SearchRbacRules(ctx context.Context, f permissions.RuleFilter) (p } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // CreateRbacRule creates one or more rows in rbac_rules table @@ -74,7 +74,7 @@ func (s Store) CreateRbacRule(ctx context.Context, rr ...*permissions.Rule) erro for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.RbacRuleTable()).SetMap(s.internalRbacRuleEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -84,7 +84,7 @@ func (s Store) CreateRbacRule(ctx context.Context, rr ...*permissions.Rule) erro // UpdateRbacRule updates one or more existing rows in rbac_rules func (s Store) UpdateRbacRule(ctx context.Context, rr ...*permissions.Rule) error { - return s.PartialUpdateRbacRule(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateRbacRule(ctx, nil, rr...)) } // PartialUpdateRbacRule updates one or more existing rows in rbac_rules @@ -105,7 +105,7 @@ func (s Store) PartialUpdateRbacRule(ctx context.Context, onlyColumns []string, }, s.internalRbacRuleEncoder(res).Skip("rel_role", "resource", "operation").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -126,7 +126,7 @@ func (s Store) RemoveRbacRule(ctx context.Context, rr ...*permissions.Rule) erro s.preprocessColumn("rls.operation", ""): s.preprocessValue(res.Operation, ""), })) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -136,22 +136,22 @@ func (s Store) RemoveRbacRule(ctx context.Context, rr ...*permissions.Rule) erro // RemoveRbacRuleByRoleIDResourceOperation removes row from the rbac_rules table func (s Store) RemoveRbacRuleByRoleIDResourceOperation(ctx context.Context, roleID uint64, resource string, operation string) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.RbacRuleTable("rls")).Where(squirrel.Eq{s.preprocessColumn("rls.rel_role", ""): s.preprocessValue(roleID, ""), + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.RbacRuleTable("rls")).Where(squirrel.Eq{s.preprocessColumn("rls.rel_role", ""): s.preprocessValue(roleID, ""), s.preprocessColumn("rls.resource", ""): s.preprocessValue(resource, ""), s.preprocessColumn("rls.operation", ""): s.preprocessValue(operation, ""), - })) + }))) } // TruncateRbacRules removes all rows from the rbac_rules table func (s Store) TruncateRbacRules(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.RbacRuleTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.RbacRuleTable())) } // ExecUpdateRbacRules updates all matched (by cnd) rows in rbac_rules with given data func (s Store) ExecUpdateRbacRules(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.RbacRuleTable("rls")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.RbacRuleTable("rls")).Where(cnd).SetMap(set))) } // RbacRuleLookup prepares RbacRule query and executes it, diff --git a/store/rdbms/rdbms.go b/store/rdbms/rdbms.go index e8378b57c..b97d88b22 100644 --- a/store/rdbms/rdbms.go +++ b/store/rdbms/rdbms.go @@ -26,6 +26,7 @@ type ( txRetryOnErrHandler func(int, error) bool columnPreprocFn func(string, string) string valuePreprocFn func(interface{}, string) interface{} + errorHandler func(error) error schemaUpgradeGenerator interface { TableExists(string) bool @@ -58,6 +59,8 @@ type ( ColumnPreprocessors map[string]columnPreprocFn ValuePreprocessors map[string]valuePreprocFn + ErrorHandler errorHandler + // Implementations can override internal RDBMS row scanners RowScanners map[string]interface{} } @@ -122,6 +125,10 @@ func New(ctx context.Context, cfg *Config) (*Store, error) { s.config.TxRetryErrHandler = TxNoRetry } + if s.config.ErrorHandler == nil { + s.config.ErrorHandler = ErrHandlerFallthrough + } + if err := s.Connect(ctx); err != nil { return nil, err } @@ -270,7 +277,6 @@ func ExecuteSqlizer(ctx context.Context, db sqlx.ExecerContext, sqlizer squirrel } _, err = db.ExecContext(ctx, query, args...) - return err } @@ -335,4 +341,5 @@ func Tx(ctx context.Context, db *sqlx.DB, cfg *Config, txOpt *sql.TxOptions, tas // TxNoRetry - Transaction retry handler // // Only returns false so transactions will never retry -func TxNoRetry(int, error) bool { return false } +func TxNoRetry(int, error) bool { return false } +func ErrHandlerFallthrough(err error) error { return err } diff --git a/store/rdbms/reminders.gen.go b/store/rdbms/reminders.gen.go index 860e49045..a6e7fbb87 100644 --- a/store/rdbms/reminders.gen.go +++ b/store/rdbms/reminders.gen.go @@ -219,7 +219,7 @@ func (s Store) SearchReminders(ctx context.Context, f types.ReminderFilter) (typ } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupReminderByID searches for reminder by its ID @@ -241,7 +241,7 @@ func (s Store) CreateReminder(ctx context.Context, rr ...*types.Reminder) error for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.ReminderTable()).SetMap(s.internalReminderEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -251,7 +251,7 @@ func (s Store) CreateReminder(ctx context.Context, rr ...*types.Reminder) error // UpdateReminder updates one or more existing rows in reminders func (s Store) UpdateReminder(ctx context.Context, rr ...*types.Reminder) error { - return s.PartialUpdateReminder(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateReminder(ctx, nil, rr...)) } // PartialUpdateReminder updates one or more existing rows in reminders @@ -269,7 +269,7 @@ func (s Store) PartialUpdateReminder(ctx context.Context, onlyColumns []string, squirrel.Eq{s.preprocessColumn("rmd.id", ""): s.preprocessValue(res.ID, "")}, s.internalReminderEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -287,7 +287,7 @@ func (s Store) RemoveReminder(ctx context.Context, rr ...*types.Reminder) error for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ReminderTable("rmd")).Where(squirrel.Eq{s.preprocessColumn("rmd.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -297,17 +297,17 @@ func (s Store) RemoveReminder(ctx context.Context, rr ...*types.Reminder) error // RemoveReminderByID removes row from the reminders table func (s Store) RemoveReminderByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ReminderTable("rmd")).Where(squirrel.Eq{s.preprocessColumn("rmd.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.ReminderTable("rmd")).Where(squirrel.Eq{s.preprocessColumn("rmd.id", ""): s.preprocessValue(ID, "")}))) } // TruncateReminders removes all rows from the reminders table func (s Store) TruncateReminders(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.ReminderTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.ReminderTable())) } // ExecUpdateReminders updates all matched (by cnd) rows in reminders with given data func (s Store) ExecUpdateReminders(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.ReminderTable("rmd")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.ReminderTable("rmd")).Where(cnd).SetMap(set))) } // ReminderLookup prepares Reminder query and executes it, diff --git a/store/rdbms/role_members.gen.go b/store/rdbms/role_members.gen.go index 4621de30c..d245b9496 100644 --- a/store/rdbms/role_members.gen.go +++ b/store/rdbms/role_members.gen.go @@ -61,7 +61,7 @@ func (s Store) SearchRoleMembers(ctx context.Context, f types.RoleMemberFilter) } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // CreateRoleMember creates one or more rows in role_members table @@ -74,7 +74,7 @@ func (s Store) CreateRoleMember(ctx context.Context, rr ...*types.RoleMember) er for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.RoleMemberTable()).SetMap(s.internalRoleMemberEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -84,7 +84,7 @@ func (s Store) CreateRoleMember(ctx context.Context, rr ...*types.RoleMember) er // UpdateRoleMember updates one or more existing rows in role_members func (s Store) UpdateRoleMember(ctx context.Context, rr ...*types.RoleMember) error { - return s.PartialUpdateRoleMember(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateRoleMember(ctx, nil, rr...)) } // PartialUpdateRoleMember updates one or more existing rows in role_members @@ -104,7 +104,7 @@ func (s Store) PartialUpdateRoleMember(ctx context.Context, onlyColumns []string }, s.internalRoleMemberEncoder(res).Skip("rel_user", "rel_role").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -124,7 +124,7 @@ func (s Store) RemoveRoleMember(ctx context.Context, rr ...*types.RoleMember) er s.preprocessColumn("rm.rel_role", ""): s.preprocessValue(res.RoleID, ""), })) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -134,20 +134,20 @@ func (s Store) RemoveRoleMember(ctx context.Context, rr ...*types.RoleMember) er // RemoveRoleMemberByUserIDRoleID removes row from the role_members table func (s Store) RemoveRoleMemberByUserIDRoleID(ctx context.Context, userID uint64, roleID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.RoleMemberTable("rm")).Where(squirrel.Eq{s.preprocessColumn("rm.rel_user", ""): s.preprocessValue(userID, ""), + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.RoleMemberTable("rm")).Where(squirrel.Eq{s.preprocessColumn("rm.rel_user", ""): s.preprocessValue(userID, ""), s.preprocessColumn("rm.rel_role", ""): s.preprocessValue(roleID, ""), - })) + }))) } // TruncateRoleMembers removes all rows from the role_members table func (s Store) TruncateRoleMembers(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.RoleMemberTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.RoleMemberTable())) } // ExecUpdateRoleMembers updates all matched (by cnd) rows in role_members with given data func (s Store) ExecUpdateRoleMembers(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.RoleMemberTable("rm")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.RoleMemberTable("rm")).Where(cnd).SetMap(set))) } // RoleMemberLookup prepares RoleMember query and executes it, diff --git a/store/rdbms/roles.gen.go b/store/rdbms/roles.gen.go index f88f7888d..6c9718630 100644 --- a/store/rdbms/roles.gen.go +++ b/store/rdbms/roles.gen.go @@ -219,7 +219,7 @@ func (s Store) SearchRoles(ctx context.Context, f types.RoleFilter) (types.RoleS } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupRoleByID searches for role by ID @@ -263,7 +263,7 @@ func (s Store) CreateRole(ctx context.Context, rr ...*types.Role) error { for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.RoleTable()).SetMap(s.internalRoleEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -273,7 +273,7 @@ func (s Store) CreateRole(ctx context.Context, rr ...*types.Role) error { // UpdateRole updates one or more existing rows in roles func (s Store) UpdateRole(ctx context.Context, rr ...*types.Role) error { - return s.PartialUpdateRole(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateRole(ctx, nil, rr...)) } // PartialUpdateRole updates one or more existing rows in roles @@ -291,7 +291,7 @@ func (s Store) PartialUpdateRole(ctx context.Context, onlyColumns []string, rr . squirrel.Eq{s.preprocessColumn("rl.id", ""): s.preprocessValue(res.ID, "")}, s.internalRoleEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -309,7 +309,7 @@ func (s Store) RemoveRole(ctx context.Context, rr ...*types.Role) error { for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.RoleTable("rl")).Where(squirrel.Eq{s.preprocessColumn("rl.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -319,17 +319,17 @@ func (s Store) RemoveRole(ctx context.Context, rr ...*types.Role) error { // RemoveRoleByID removes row from the roles table func (s Store) RemoveRoleByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.RoleTable("rl")).Where(squirrel.Eq{s.preprocessColumn("rl.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.RoleTable("rl")).Where(squirrel.Eq{s.preprocessColumn("rl.id", ""): s.preprocessValue(ID, "")}))) } // TruncateRoles removes all rows from the roles table func (s Store) TruncateRoles(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.RoleTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.RoleTable())) } // ExecUpdateRoles updates all matched (by cnd) rows in roles with given data func (s Store) ExecUpdateRoles(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.RoleTable("rl")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.RoleTable("rl")).Where(cnd).SetMap(set))) } // RoleLookup prepares Role query and executes it, diff --git a/store/rdbms/settings.gen.go b/store/rdbms/settings.gen.go index 5da5f904d..c17084fe5 100644 --- a/store/rdbms/settings.gen.go +++ b/store/rdbms/settings.gen.go @@ -64,7 +64,7 @@ func (s Store) SearchSettings(ctx context.Context, f types.SettingsFilter) (type } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupSettingByNameOwnedBy searches for settings by name and owner @@ -85,7 +85,7 @@ func (s Store) CreateSetting(ctx context.Context, rr ...*types.SettingValue) err for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.SettingTable()).SetMap(s.internalSettingEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -95,7 +95,7 @@ func (s Store) CreateSetting(ctx context.Context, rr ...*types.SettingValue) err // UpdateSetting updates one or more existing rows in settings func (s Store) UpdateSetting(ctx context.Context, rr ...*types.SettingValue) error { - return s.PartialUpdateSetting(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateSetting(ctx, nil, rr...)) } // PartialUpdateSetting updates one or more existing rows in settings @@ -115,7 +115,7 @@ func (s Store) PartialUpdateSetting(ctx context.Context, onlyColumns []string, r }, s.internalSettingEncoder(res).Skip("name", "rel_owner").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -135,7 +135,7 @@ func (s Store) RemoveSetting(ctx context.Context, rr ...*types.SettingValue) err s.preprocessColumn("st.rel_owner", ""): s.preprocessValue(res.OwnedBy, ""), })) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -145,20 +145,20 @@ func (s Store) RemoveSetting(ctx context.Context, rr ...*types.SettingValue) err // RemoveSettingByNameOwnedBy removes row from the settings table func (s Store) RemoveSettingByNameOwnedBy(ctx context.Context, name string, ownedBy uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.SettingTable("st")).Where(squirrel.Eq{s.preprocessColumn("st.name", ""): s.preprocessValue(name, ""), + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.SettingTable("st")).Where(squirrel.Eq{s.preprocessColumn("st.name", ""): s.preprocessValue(name, ""), s.preprocessColumn("st.rel_owner", ""): s.preprocessValue(ownedBy, ""), - })) + }))) } // TruncateSettings removes all rows from the settings table func (s Store) TruncateSettings(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.SettingTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.SettingTable())) } // ExecUpdateSettings updates all matched (by cnd) rows in settings with given data func (s Store) ExecUpdateSettings(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.SettingTable("st")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.SettingTable("st")).Where(cnd).SetMap(set))) } // SettingLookup prepares Setting query and executes it, diff --git a/store/rdbms/users.gen.go b/store/rdbms/users.gen.go index 93de4888b..f8394b6df 100644 --- a/store/rdbms/users.gen.go +++ b/store/rdbms/users.gen.go @@ -219,7 +219,7 @@ func (s Store) SearchUsers(ctx context.Context, f types.UserFilter) (types.UserS } ) - return set, f, fetch() + return set, f, s.config.ErrorHandler(fetch()) } // LookupUserByID searches for user by ID @@ -274,7 +274,7 @@ func (s Store) CreateUser(ctx context.Context, rr ...*types.User) error { for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Insert(s.UserTable()).SetMap(s.internalUserEncoder(res))) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -284,7 +284,7 @@ func (s Store) CreateUser(ctx context.Context, rr ...*types.User) error { // UpdateUser updates one or more existing rows in users func (s Store) UpdateUser(ctx context.Context, rr ...*types.User) error { - return s.PartialUpdateUser(ctx, nil, rr...) + return s.config.ErrorHandler(s.PartialUpdateUser(ctx, nil, rr...)) } // PartialUpdateUser updates one or more existing rows in users @@ -302,7 +302,7 @@ func (s Store) PartialUpdateUser(ctx context.Context, onlyColumns []string, rr . squirrel.Eq{s.preprocessColumn("usr.id", ""): s.preprocessValue(res.ID, "")}, s.internalUserEncoder(res).Skip("id").Only(onlyColumns...)) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -320,7 +320,7 @@ func (s Store) RemoveUser(ctx context.Context, rr ...*types.User) error { for _, res := range rr { err = ExecuteSqlizer(ctx, s.DB(), s.Delete(s.UserTable("usr")).Where(squirrel.Eq{s.preprocessColumn("usr.id", ""): s.preprocessValue(res.ID, "")})) if err != nil { - return err + return s.config.ErrorHandler(err) } } @@ -330,17 +330,17 @@ func (s Store) RemoveUser(ctx context.Context, rr ...*types.User) error { // RemoveUserByID removes row from the users table func (s Store) RemoveUserByID(ctx context.Context, ID uint64) error { - return ExecuteSqlizer(ctx, s.DB(), s.Delete(s.UserTable("usr")).Where(squirrel.Eq{s.preprocessColumn("usr.id", ""): s.preprocessValue(ID, "")})) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Delete(s.UserTable("usr")).Where(squirrel.Eq{s.preprocessColumn("usr.id", ""): s.preprocessValue(ID, "")}))) } // TruncateUsers removes all rows from the users table func (s Store) TruncateUsers(ctx context.Context) error { - return Truncate(ctx, s.DB(), s.UserTable()) + return s.config.ErrorHandler(Truncate(ctx, s.DB(), s.UserTable())) } // ExecUpdateUsers updates all matched (by cnd) rows in users with given data func (s Store) ExecUpdateUsers(ctx context.Context, cnd squirrel.Sqlizer, set store.Payload) error { - return ExecuteSqlizer(ctx, s.DB(), s.Update(s.UserTable("usr")).Where(cnd).SetMap(set)) + return s.config.ErrorHandler(ExecuteSqlizer(ctx, s.DB(), s.Update(s.UserTable("usr")).Where(cnd).SetMap(set))) } // UserLookup prepares User query and executes it, diff --git a/store/sqlite/sqlite.go b/store/sqlite/sqlite.go index 5beecaa1b..771641490 100644 --- a/store/sqlite/sqlite.go +++ b/store/sqlite/sqlite.go @@ -4,8 +4,9 @@ import ( "context" "fmt" "github.com/Masterminds/squirrel" + "github.com/cortezaproject/corteza-server/store" "github.com/cortezaproject/corteza-server/store/rdbms" - _ "github.com/mattn/go-sqlite3" + "github.com/mattn/go-sqlite3" "go.uber.org/zap" "strings" ) @@ -24,6 +25,7 @@ func New(ctx context.Context, dsn string) (s *Store, err error) { } cfg.PlaceholderFormat = squirrel.Dollar + cfg.ErrorHandler = errorHandler s = new(Store) if s.Store, err = rdbms.New(ctx, cfg); err != nil { @@ -83,3 +85,16 @@ func ProcDataSourceName(in string) (*rdbms.Config, error) { return c, nil } + +func errorHandler(err error) error { + if err != nil { + if implErr, ok := err.(sqlite3.Error); ok { + switch implErr.ExtendedCode { + case sqlite3.ErrConstraintUnique: + return store.ErrNotUnique.Wrap(implErr) + } + } + } + + return err +} diff --git a/store/tests/users_test.go b/store/tests/users_test.go index 715be6c7d..5a265d313 100644 --- a/store/tests/users_test.go +++ b/store/tests/users_test.go @@ -74,6 +74,22 @@ func testUsers(t *testing.T, tmp interface{}) { req.NoError(s.CreateUser(ctx, makeNew())) }) + t.Run("create with duplicate email", func(t *testing.T) { + req := require.New(t) + req.NoError(s.TruncateUsers(ctx)) + + req.NoError(s.CreateUser(ctx, makeNew("a"))) + req.EqualError(s.CreateUser(ctx, makeNew("a")), store.ErrNotUnique.Error()) + }) + + t.Run("create with duplicate ID", func(t *testing.T) { + req := require.New(t) + req.NoError(s.TruncateUsers(ctx)) + user := makeNew("a") + req.NoError(s.CreateUser(ctx, user)) + req.EqualError(s.CreateUser(ctx, user), store.ErrNotUnique.Error()) + }) + t.Run("lookup by ID", func(t *testing.T) { req, user := truncAndCreate(t)