diff --git a/go.mod b/go.mod index aff77ba6a7..cad997cf22 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.0 github.com/gosimple/slug v1.10.0 + github.com/jackc/pgx/v5 v5.7.2 github.com/jmoiron/sqlx v1.3.4 github.com/json-iterator/go v1.1.12 github.com/knadh/koanf v1.5.0 @@ -51,8 +52,9 @@ require ( github.com/soheilhy/cmux v0.1.5 github.com/srikanthccv/ClickHouse-go-mock v0.9.0 github.com/stretchr/testify v1.10.0 - github.com/uptrace/bun v1.2.8 - github.com/uptrace/bun/dialect/sqlitedialect v1.2.8 + github.com/uptrace/bun v1.2.9 + github.com/uptrace/bun/dialect/pgdialect v1.2.9 + github.com/uptrace/bun/dialect/sqlitedialect v1.2.9 go.opentelemetry.io/collector/confmap v1.17.0 go.opentelemetry.io/collector/pdata v1.17.0 go.opentelemetry.io/collector/processor v0.111.0 @@ -123,6 +125,9 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.4.0 // indirect @@ -153,7 +158,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect + github.com/puzpuzpuz/xsync/v3 v3.5.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/segmentio/backo-go v1.0.1 // indirect @@ -220,6 +225,7 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.30.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.11.0 // indirect + golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/time v0.6.0 // indirect gonum.org/v1/gonum v0.15.1 // indirect diff --git a/go.sum b/go.sum index 05b0a0de43..17b874c650 100644 --- a/go.sum +++ b/go.sum @@ -436,6 +436,14 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ionos-cloud/sdk-go/v6 v6.2.1 h1:mxxN+frNVmbFrmmFfXnBC3g2USYJrl6mc1LW2iNYbFY= github.com/ionos-cloud/sdk-go/v6 v6.2.1/go.mod h1:SXrO9OGyWjd2rZhAhEpdYN6VUAODzzqRdqA9BCviQtI= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -511,8 +519,8 @@ github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxq github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= @@ -663,8 +671,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4= -github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/puzpuzpuz/xsync/v3 v3.5.0 h1:i+cMcpEDY1BkNm7lPDkCtE4oElsYLn+EKF8kAu2vXT4= +github.com/puzpuzpuz/xsync/v3 v3.5.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= @@ -747,10 +755,12 @@ github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/uptrace/bun v1.2.8 h1:HEiLvy9wc7ehU5S02+O6NdV5BLz48lL4REPhTkMX3Dg= -github.com/uptrace/bun v1.2.8/go.mod h1:JBq0uBKsKqNT0Ccce1IAFZY337Wkf08c6F6qlmfOHE8= -github.com/uptrace/bun/dialect/sqlitedialect v1.2.8 h1:Huqw7YhLFTbocbSv8NETYYXqKtwLa6XsciCWtjzWSWU= -github.com/uptrace/bun/dialect/sqlitedialect v1.2.8/go.mod h1:ni7h2uwIc5zPhxgmCMTEbefONc4XsVr/ATfz1Q7d3CE= +github.com/uptrace/bun v1.2.9 h1:OOt2DlIcRUMSZPr6iXDFg/LaQd59kOxbAjpIVHddKRs= +github.com/uptrace/bun v1.2.9/go.mod h1:r2ZaaGs9Ru5bpGTr8GQfp8jp+TlCav9grYCPOu2CJSg= +github.com/uptrace/bun/dialect/pgdialect v1.2.9 h1:caf5uFbOGiXvadV6pA5gn87k0awFFxL1kuuY3SpxnWk= +github.com/uptrace/bun/dialect/pgdialect v1.2.9/go.mod h1:m7L9JtOp/Lt8HccET70ULxplMweE/u0S9lNUSxz2duo= +github.com/uptrace/bun/dialect/sqlitedialect v1.2.9 h1:HLzGWXBh07sT8zhVPy6veYbbGrAtYq0KzyRHXBj+GjA= +github.com/uptrace/bun/dialect/sqlitedialect v1.2.9/go.mod h1:dUR+ecoCWA0FIa9vhQVRnGtYYPpuCLJoEEtX9E1aiBU= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= diff --git a/pkg/signoz/provider.go b/pkg/signoz/provider.go index 41c468de8a..369c3caec2 100644 --- a/pkg/signoz/provider.go +++ b/pkg/signoz/provider.go @@ -46,6 +46,7 @@ func NewProviderConfig() ProviderConfig { ), SQLStoreProviderFactories: factory.MustNewNamedMap( sqlitesqlstore.NewFactory(), + // postgressqlstore.NewFactory(), ), SQLMigrationProviderFactories: factory.MustNewNamedMap( sqlmigration.NewAddDataMigrationsFactory(), @@ -58,6 +59,7 @@ func NewProviderConfig() ProviderConfig { sqlmigration.NewAddIntegrationsFactory(), sqlmigration.NewAddLicensesFactory(), sqlmigration.NewAddPatsFactory(), + sqlmigration.NewModifyDatetimeFactory(), ), TelemetryStoreProviderFactories: factory.MustNewNamedMap( clickhousetelemetrystore.NewFactory(hook), diff --git a/pkg/sqlmigration/003_add_dashboards.go b/pkg/sqlmigration/003_add_dashboards.go index c3f1d4e613..6e71f72f1f 100644 --- a/pkg/sqlmigration/003_add_dashboards.go +++ b/pkg/sqlmigration/003_add_dashboards.go @@ -34,9 +34,9 @@ func (migration *addDashboards) Up(ctx context.Context, db *bun.DB) error { bun.BaseModel `bun:"table:dashboards"` ID int `bun:"id,pk,autoincrement"` UUID string `bun:"uuid,type:text,notnull,unique"` - CreatedAt time.Time `bun:"created_at,type:datetime,notnull"` + CreatedAt time.Time `bun:"created_at,notnull"` CreatedBy string `bun:"created_by,type:text,notnull"` - UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"` + UpdatedAt time.Time `bun:"updated_at,notnull"` UpdatedBy string `bun:"updated_by,type:text,notnull"` Data string `bun:"data,type:text,notnull"` Locked int `bun:"locked,notnull,default:0"` @@ -51,9 +51,9 @@ func (migration *addDashboards) Up(ctx context.Context, db *bun.DB) error { Model(&struct { bun.BaseModel `bun:"table:rules"` ID int `bun:"id,pk,autoincrement"` - CreatedAt time.Time `bun:"created_at,type:datetime,notnull"` + CreatedAt time.Time `bun:"created_at,notnull"` CreatedBy string `bun:"created_by,type:text,notnull"` - UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"` + UpdatedAt time.Time `bun:"updated_at,notnull"` UpdatedBy string `bun:"updated_by,type:text,notnull"` Deleted int `bun:"deleted,notnull,default:0"` Data string `bun:"data,type:text,notnull"` @@ -68,8 +68,8 @@ func (migration *addDashboards) Up(ctx context.Context, db *bun.DB) error { Model(&struct { bun.BaseModel `bun:"table:notification_channels"` ID int `bun:"id,pk,autoincrement"` - CreatedAt time.Time `bun:"created_at,type:datetime,notnull"` - UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"` + CreatedAt time.Time `bun:"created_at,notnull"` + UpdatedAt time.Time `bun:"updated_at,notnull"` Name string `bun:"name,type:text,notnull,unique"` Type string `bun:"type,type:text,notnull"` Deleted int `bun:"deleted,notnull,default:0"` @@ -89,9 +89,9 @@ func (migration *addDashboards) Up(ctx context.Context, db *bun.DB) error { Description string `bun:"description,type:text"` AlertIDs string `bun:"alert_ids,type:text"` Schedule string `bun:"schedule,type:text,notnull"` - CreatedAt time.Time `bun:"created_at,type:datetime,notnull"` + CreatedAt time.Time `bun:"created_at,notnull"` CreatedBy string `bun:"created_by,type:text,notnull"` - UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"` + UpdatedAt time.Time `bun:"updated_at,notnull"` UpdatedBy string `bun:"updated_by,type:text,notnull"` }{}). IfNotExists(). @@ -105,8 +105,8 @@ func (migration *addDashboards) Up(ctx context.Context, db *bun.DB) error { bun.BaseModel `bun:"table:ttl_status"` ID int `bun:"id,pk,autoincrement"` TransactionID string `bun:"transaction_id,type:text,notnull"` - CreatedAt time.Time `bun:"created_at,type:datetime,notnull"` - UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"` + CreatedAt time.Time `bun:"created_at,notnull"` + UpdatedAt time.Time `bun:"updated_at,notnull"` TableName string `bun:"table_name,type:text,notnull"` TTL int `bun:"ttl,notnull,default:0"` ColdStorageTTL int `bun:"cold_storage_ttl,notnull,default:0"` diff --git a/pkg/sqlmigration/004_add_saved_views.go b/pkg/sqlmigration/004_add_saved_views.go index 20f817bc55..fb9d9b0041 100644 --- a/pkg/sqlmigration/004_add_saved_views.go +++ b/pkg/sqlmigration/004_add_saved_views.go @@ -35,9 +35,9 @@ func (migration *addSavedViews) Up(ctx context.Context, db *bun.DB) error { UUID string `bun:"uuid,pk,type:text"` Name string `bun:"name,type:text,notnull"` Category string `bun:"category,type:text,notnull"` - CreatedAt time.Time `bun:"created_at,type:datetime,notnull"` + CreatedAt time.Time `bun:"created_at,notnull"` CreatedBy string `bun:"created_by,type:text"` - UpdatedAt time.Time `bun:"updated_at,type:datetime,notnull"` + UpdatedAt time.Time `bun:"updated_at,notnull"` UpdatedBy string `bun:"updated_by,type:text"` SourcePage string `bun:"source_page,type:text,notnull"` Tags string `bun:"tags,type:text"` diff --git a/pkg/sqlmigration/005_add_agents.go b/pkg/sqlmigration/005_add_agents.go index 20ac7acbdc..64887f1870 100644 --- a/pkg/sqlmigration/005_add_agents.go +++ b/pkg/sqlmigration/005_add_agents.go @@ -32,8 +32,8 @@ func (migration *addAgents) Up(ctx context.Context, db *bun.DB) error { Model(&struct { bun.BaseModel `bun:"table:agents"` AgentID string `bun:"agent_id,pk,type:text,unique"` - StartedAt time.Time `bun:"started_at,type:datetime,notnull"` - TerminatedAt time.Time `bun:"terminated_at,type:datetime"` + StartedAt time.Time `bun:"started_at,notnull"` + TerminatedAt time.Time `bun:"terminated_at"` CurrentStatus string `bun:"current_status,type:text,notnull"` EffectiveConfig string `bun:"effective_config,type:text,notnull"` }{}). diff --git a/pkg/sqlmigration/011_modify_datetime.go b/pkg/sqlmigration/011_modify_datetime.go new file mode 100644 index 0000000000..977b466494 --- /dev/null +++ b/pkg/sqlmigration/011_modify_datetime.go @@ -0,0 +1,90 @@ +package sqlmigration + +import ( + "context" + + "github.com/uptrace/bun" + "github.com/uptrace/bun/migrate" + "go.signoz.io/signoz/pkg/factory" +) + +type modifyDatetime struct{} + +func NewModifyDatetimeFactory() factory.ProviderFactory[SQLMigration, Config] { + return factory.NewProviderFactory(factory.MustNewName("modify_datetime"), newModifyDatetime) +} + +func newModifyDatetime(_ context.Context, _ factory.ProviderSettings, _ Config) (SQLMigration, error) { + return &modifyDatetime{}, nil +} + +func (migration *modifyDatetime) Register(migrations *migrate.Migrations) error { + if err := migrations.Register(migration.Up, migration.Down); err != nil { + return err + } + + return nil +} + +func (migration *modifyDatetime) Up(ctx context.Context, db *bun.DB) error { + // only run this for old sqlite db + if db.Dialect().Name().String() != "sqlite" { + return nil + } + + // begin transaction + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() + + tables := []string{"dashboards", "rules", "planned_maintenance", "ttl_status", "saved_views"} + columns := []string{"created_at", "updated_at"} + for _, table := range tables { + for _, column := range columns { + if err := modifyColumn(ctx, tx, table, column); err != nil { + return err + } + } + } + + for _, column := range []string{"started_at", "terminated_at"} { + if err := modifyColumn(ctx, tx, "agents", column); err != nil { + return err + } + } + + if err := tx.Commit(); err != nil { + return err + } + + return nil +} + +func modifyColumn(ctx context.Context, tx bun.Tx, table string, column string) error { + // rename old column + if _, err := tx.ExecContext(ctx, `ALTER TABLE `+table+` RENAME COLUMN `+column+` TO `+column+`_old`); err != nil { + return err + } + + // cannot add not null constraint to the column + if _, err := tx.ExecContext(ctx, `ALTER TABLE `+table+` ADD COLUMN `+column+` TIMESTAMP`); err != nil { + return err + } + + // update the new column with the value of the old column + if _, err := tx.ExecContext(ctx, `UPDATE `+table+` SET `+column+` = `+column+`_old`); err != nil { + return err + } + + // drop the old column + if _, err := tx.ExecContext(ctx, `ALTER TABLE `+table+` DROP COLUMN `+column+`_old`); err != nil { + return err + } + return nil +} + +func (migration *modifyDatetime) Down(ctx context.Context, db *bun.DB) error { + return nil +} diff --git a/pkg/sqlstore/config.go b/pkg/sqlstore/config.go index f3f45d9553..bd357f6d2d 100644 --- a/pkg/sqlstore/config.go +++ b/pkg/sqlstore/config.go @@ -11,6 +11,13 @@ type Config struct { Connection ConnectionConfig `mapstructure:",squash"` // Sqlite is the sqlite configuration. Sqlite SqliteConfig `mapstructure:"sqlite"` + // Postgres is the postgres configuration. + Postgres PostgresConfig `mapstructure:"postgres"` +} + +type PostgresConfig struct { + // DSN is the database source name. + DSN string `mapstructure:"dsn"` } type SqliteConfig struct { diff --git a/pkg/sqlstore/postgressqlstore/provider.go b/pkg/sqlstore/postgressqlstore/provider.go new file mode 100644 index 0000000000..f18f4b3d3c --- /dev/null +++ b/pkg/sqlstore/postgressqlstore/provider.go @@ -0,0 +1,64 @@ +package postgressqlstore + +import ( + "context" + "database/sql" + + "github.com/jackc/pgx/v5/pgxpool" + "github.com/jackc/pgx/v5/stdlib" + "github.com/jmoiron/sqlx" + "github.com/uptrace/bun" + "github.com/uptrace/bun/dialect/pgdialect" + "go.signoz.io/signoz/pkg/factory" + "go.signoz.io/signoz/pkg/sqlstore" +) + +type provider struct { + settings factory.ScopedProviderSettings + sqldb *sql.DB + bundb *bun.DB + sqlxdb *sqlx.DB +} + +func NewFactory() factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] { + return factory.NewProviderFactory(factory.MustNewName("postgres"), New) +} + +func New(ctx context.Context, providerSettings factory.ProviderSettings, config sqlstore.Config) (sqlstore.SQLStore, error) { + settings := factory.NewScopedProviderSettings(providerSettings, "go.signoz.io/signoz/pkg/sqlstore/postgressqlstore") + + pgConfig, err := pgxpool.ParseConfig(config.Postgres.DSN) + if err != nil { + return nil, err + } + + // Set the maximum number of open connections + pgConfig.MaxConns = int32(config.Connection.MaxOpenConns) + + // Use pgxpool to create a connection pool + pool, err := pgxpool.NewWithConfig(ctx, pgConfig) + if err != nil { + return nil, err + } + + sqldb := stdlib.OpenDBFromPool(pool) + + return &provider{ + settings: settings, + sqldb: sqldb, + bundb: bun.NewDB(sqldb, pgdialect.New()), + sqlxdb: sqlx.NewDb(sqldb, "postgres"), + }, nil +} + +func (provider *provider) BunDB() *bun.DB { + return provider.bundb +} + +func (provider *provider) SQLDB() *sql.DB { + return provider.sqldb +} + +func (provider *provider) SQLxDB() *sqlx.DB { + return provider.sqlxdb +}