From c5d2fbfc8b0bbc005a8c8488e12bc567b960ec87 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Thu, 15 Jan 2026 15:05:31 +0700 Subject: [PATCH 1/7] feat: squash migrations to declarative schemas --- cmd/migration.go | 6 ++ go.mod | 58 ++++++++++---------- go.sum | 121 ++++++++++++++++++++--------------------- internal/utils/misc.go | 26 +++++++++ 4 files changed, 121 insertions(+), 90 deletions(-) diff --git a/cmd/migration.go b/cmd/migration.go index 697f3651d..5ab9c422f 100644 --- a/cmd/migration.go +++ b/cmd/migration.go @@ -9,6 +9,7 @@ import ( "github.com/spf13/viper" "github.com/supabase/cli/internal/migration/down" "github.com/supabase/cli/internal/migration/fetch" + "github.com/supabase/cli/internal/migration/format" "github.com/supabase/cli/internal/migration/list" "github.com/supabase/cli/internal/migration/new" "github.com/supabase/cli/internal/migration/repair" @@ -61,12 +62,16 @@ var ( }, } + useDeclarative bool migrationVersion string migrationSquashCmd = &cobra.Command{ Use: "squash", Short: "Squash migrations to a single file", RunE: func(cmd *cobra.Command, args []string) error { + if useDeclarative { + return format.Run(cmd.Context(), afero.NewOsFs()) + } return squash.Run(cmd.Context(), migrationVersion, flags.DbConfig, afero.NewOsFs()) }, PostRun: func(cmd *cobra.Command, args []string) { @@ -130,6 +135,7 @@ func init() { migrationCmd.AddCommand(migrationRepairCmd) // Build squash command squashFlags := migrationSquashCmd.Flags() + squashFlags.BoolVar(&useDeclarative, "declarative", false, "Squash migrations to declarative schemas") squashFlags.StringVar(&migrationVersion, "version", "", "Squash up to the specified version.") squashFlags.String("db-url", "", "Squashes migrations of the database specified by the connection string (must be percent-encoded).") squashFlags.Bool("linked", false, "Squashes the migration history of the linked project.") diff --git a/go.mod b/go.mod index b46770609..18589def0 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/joho/godotenv v1.5.1 github.com/mithrandie/csvq-driver v1.7.0 github.com/muesli/reflow v0.3.0 + github.com/multigres/multigres v0.0.0-20260114202706-8cdfa4159401 github.com/oapi-codegen/nullable v1.1.0 github.com/olekukonko/tablewriter v1.1.2 github.com/slack-go/slack v0.17.3 @@ -117,9 +118,10 @@ require ( github.com/butuzov/mirror v1.3.0 // indirect github.com/catenacyber/perfsprint v0.9.1 // indirect github.com/ccojocar/zxcvbn-go v1.0.2 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.10 // indirect - github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect + github.com/charmbracelet/colorprofile v0.3.1 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/charmbracelet/x/ansi v0.10.1 // indirect github.com/charmbracelet/x/cellbuf v0.0.13 // indirect @@ -221,7 +223,7 @@ require ( github.com/gostaticanalysis/comment v1.5.0 // indirect github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect github.com/gostaticanalysis/nilerr v0.1.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -237,7 +239,7 @@ require ( github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgtype v1.14.4 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jgautheron/goconst v1.8.1 // indirect @@ -332,12 +334,12 @@ require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polyfloyd/go-errorlint v1.8.0 // indirect - github.com/prometheus/client_golang v1.22.0 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/quasilyte/go-ruleguard v0.4.4 // indirect - github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.4 // indirect + github.com/prometheus/procfs v0.19.2 // indirect + github.com/quasilyte/go-ruleguard v0.4.5 // indirect + github.com/quasilyte/go-ruleguard/dsl v0.3.23 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect @@ -346,7 +348,7 @@ require ( github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/ryancurrah/gomodguard v1.4.1 // indirect github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect - github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/sahilm/fuzzy v0.1.1 // indirect github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect @@ -362,7 +364,6 @@ require ( github.com/skeema/knownhosts v1.3.1 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/sonatard/noctx v0.1.0 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -407,24 +408,23 @@ require ( go-simpler.org/sloglint v0.11.0 // indirect go.augendre.info/fatcontext v0.8.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 // indirect go.opentelemetry.io/otel/metric v1.39.0 // indirect - go.opentelemetry.io/otel/sdk v1.38.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.39.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect go.opentelemetry.io/otel/trace v1.39.0 // indirect - go.opentelemetry.io/proto/otlp v1.5.0 // indirect - go.uber.org/atomic v1.9.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect - go.uber.org/multierr v1.9.0 // indirect - go.uber.org/zap v1.24.0 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.47.0 // indirect golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect @@ -435,9 +435,9 @@ require ( golang.org/x/tools v0.40.0 // indirect golang.org/x/tools/go/expect v0.1.1-deprecated // indirect golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect - google.golang.org/protobuf v1.36.10 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect @@ -451,7 +451,7 @@ require ( k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect - mvdan.cc/gofumpt v0.8.0 // indirect + mvdan.cc/gofumpt v0.9.1 // indirect mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect diff --git a/go.sum b/go.sum index 1a351cc62..ce56d2c3b 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,6 @@ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWp github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -157,6 +155,8 @@ github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= @@ -165,8 +165,8 @@ github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40= +github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V6kXldcY= github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= @@ -529,8 +529,8 @@ github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8= github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 h1:kEISI/Gx67NzH3nJxAmY/dGac80kKZgZt134u7Y/k1s= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4/go.mod h1:6Nz966r3vQYCqIzWsuEl9d7cf7mRhtDmm++sOxlnfxI= github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= @@ -596,8 +596,9 @@ github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +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/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= @@ -806,6 +807,8 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/multigres/multigres v0.0.0-20260114202706-8cdfa4159401 h1:VPySOSQmtjlhgv0R1z2JiDF0CG8z9dilYW2s68B6Mao= +github.com/multigres/multigres v0.0.0-20260114202706-8cdfa4159401/go.mod h1:UvLRTBJXqpyyXOtyEYH2NRPyklWWdzM7cNzcrEXiyRM= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -909,28 +912,28 @@ github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= +github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -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/quasilyte/go-ruleguard v0.4.4 h1:53DncefIeLX3qEpjzlS1lyUmQoUEeOWPFWqaTJq9eAQ= -github.com/quasilyte/go-ruleguard v0.4.4/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE= -github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= -github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/quasilyte/go-ruleguard v0.4.5 h1:AGY0tiOT5hJX9BTdx/xBdoCubQUAE2grkqY2lSwvZcA= +github.com/quasilyte/go-ruleguard v0.4.5/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE= +github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY= +github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU= @@ -955,8 +958,8 @@ github.com/ryancurrah/gomodguard v1.4.1 h1:eWC8eUMNZ/wM/PWuZBv7JxxqT5fiIKSIyTvjb github.com/ryancurrah/gomodguard v1.4.1/go.mod h1:qnMJwV1hX9m+YJseXEBhd2s90+1Xn6x9dLz11ualI1I= github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= -github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= -github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= @@ -1001,8 +1004,6 @@ github.com/slack-go/slack v0.17.3 h1:zV5qO3Q+WJAQ/XwbGfNFrRMaJ5T/naqaonyPV/1TP4g github.com/slack-go/slack v0.17.3/go.mod h1:X+UqOufi3LYQHDnMG1vxf0J8asC6+WllXrVrhl8/Prk= github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM= github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk= @@ -1156,40 +1157,38 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 h1:0NIXxOCFx+SKbhCVxwl3ETG8ClLPAa0KuKV6p3yhxP8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0/go.mod h1:ChZSJbbfbl/DcRZNc9Gqh6DYGlfjw4PvO1pEOZH1ZsE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 h1:cEf8jF6WbuGQWUVcqgyWtTR0kOOAWY1DYZ+UhvdmQPw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0/go.mod h1:k1lzV5n5U3HkGvTCJHraTAGJ7MqsgL1wrGwTj1Isfiw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0 h1:nKP4Z2ejtHn3yShBb+2KawiXgpn8In5cT7aO2wXuOTE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0/go.mod h1:NwjeBbNigsO4Aj9WgM0C+cKIrxsZUaRmZUO7A8I7u8o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -1199,16 +1198,16 @@ go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1232,8 +1231,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU= +golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4= @@ -1413,10 +1412,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda h1:+2XxjfsAu6vqFxwGBRcHiMaDCuZiqXGDUDVWVtrFAnE= -google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E= +google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:Xa7le7qx2vmqB/SzWUBa7KdMjpdpAHlh5QCSnjessQk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= @@ -1428,8 +1427,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= @@ -1486,8 +1485,8 @@ k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJ k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -mvdan.cc/gofumpt v0.8.0 h1:nZUCeC2ViFaerTcYKstMmfysj6uhQrA2vJe+2vwGU6k= -mvdan.cc/gofumpt v0.8.0/go.mod h1:vEYnSzyGPmjvFkqJWtXkh79UwPWP9/HMxQdGEXZHjpg= +mvdan.cc/gofumpt v0.9.1 h1:p5YT2NfFWsYyTieYgwcQ8aKV3xRvFH4uuN/zB2gBbMQ= +mvdan.cc/gofumpt v0.9.1/go.mod h1:3xYtNemnKiXaTh6R4VtlqDATFwBbdXI8lJvH/4qk7mw= mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8= mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= diff --git a/internal/utils/misc.go b/internal/utils/misc.go index b47a8a3eb..d301682bc 100644 --- a/internal/utils/misc.go +++ b/internal/utils/misc.go @@ -77,7 +77,33 @@ var ( CliVersionPath = filepath.Join(TempDir, "cli-latest") ProfilePath = filepath.Join(TempDir, "profile") CurrBranchPath = filepath.Join(SupabaseDirPath, ".branches", "_current_branch") + ClusterDir = filepath.Join(SupabaseDirPath, "cluster") + RolesPath = filepath.Join(ClusterDir, "roles.sql") + ExtensionsPath = filepath.Join(ClusterDir, "extensions.sql") + ForeignDWPath = filepath.Join(ClusterDir, "foreign_data_wrappers.sql") + PublicationsPath = filepath.Join(ClusterDir, "publications.sql") + SubscriptionsPath = filepath.Join(ClusterDir, "subscriptions.sql") + EventTriggersPath = filepath.Join(ClusterDir, "event_triggers.sql") + VariablesPath = filepath.Join(ClusterDir, "variables.sql") SchemasDir = filepath.Join(SupabaseDirPath, "schemas") + AggregatePath = filepath.Join(SchemasDir, "aggregate.sql") + CollationPath = filepath.Join(SchemasDir, "collation.sql") + DomainPath = filepath.Join(SchemasDir, "domain.sql") + IndexPath = filepath.Join(SchemasDir, "index.sql") + LanguagePath = filepath.Join(SchemasDir, "language.sql") + MaterializedViewPath = filepath.Join(SchemasDir, "materialized-view.sql") + ProcedurePath = filepath.Join(SchemasDir, "procedure.sql") + RlsPolicyPath = filepath.Join(SchemasDir, "rls-policy.sql") + RulePath = filepath.Join(SchemasDir, "rule.sql") + SchemaPath = filepath.Join(SchemasDir, "schema.sql") + SequencePath = filepath.Join(SchemasDir, "sequence.sql") + TablePath = filepath.Join(SchemasDir, "table.sql") + TriggerPath = filepath.Join(SchemasDir, "trigger.sql") + TypePath = filepath.Join(SchemasDir, "type.sql") + ViewPath = filepath.Join(SchemasDir, "view.sql") + PrivilegesPath = filepath.Join(SchemasDir, "privileges.sql") + DataDir = filepath.Join(SupabaseDirPath, "data") + CronPath = filepath.Join(DataDir, "cron.sql") MigrationsDir = filepath.Join(SupabaseDirPath, "migrations") FunctionsDir = filepath.Join(SupabaseDirPath, "functions") SnippetsDir = filepath.Join(SupabaseDirPath, "snippets") From 5e276537e89f56ca7381edc539b4fda636fb48d8 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Thu, 15 Jan 2026 17:23:41 +0700 Subject: [PATCH 2/7] chore: add auto formatting --- internal/migration/format/format.go | 104 ++++++++++++++++++ .../migration/format/templates/order.toml | 35 ++++++ 2 files changed, 139 insertions(+) create mode 100644 internal/migration/format/format.go create mode 100644 internal/migration/format/templates/order.toml diff --git a/internal/migration/format/format.go b/internal/migration/format/format.go new file mode 100644 index 000000000..c29cde4ea --- /dev/null +++ b/internal/migration/format/format.go @@ -0,0 +1,104 @@ +package format + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/go-errors/errors" + "github.com/multigres/multigres/go/parser" + "github.com/spf13/afero" + "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/pkg/migration" +) + +//go:embed templates/order.toml +var order string + +func Run(ctx context.Context, fsys afero.Fs) error { + files, err := migration.ListLocalMigrations(utils.MigrationsDir, afero.NewIOFS(fsys)) + if err != nil { + return err + } + var buf bytes.Buffer + for _, name := range files { + if err := readFile(name, &buf, fsys); err != nil { + return err + } + } + stat, err := parser.ParseSQL(buf.String()) + if err != nil { + return errors.Errorf("failed to parse SQL: %w", err) + } + for _, d := range []string{utils.ClusterDir, utils.SchemasDir, utils.DataDir} { + if err := fsys.RemoveAll(d); err != nil { + return errors.Errorf("failed to remove directory: %w", err) + } + } + for _, s := range stat { + var name string + switch s.StatementType() { + case "SET": // T_VariableSetStmt + name = utils.VariablesPath + case "SELECT": // T_SelectStmt + // TODO: differentiate function calls to create cron / pgmq / etc + name = utils.CronPath + case "CreateExtensionStmt": // T_CreateExtensionStmt + name = utils.ExtensionsPath + case "CommentStmt": // T_CommentStmt + // TODO: differentiate comment by entity type + name = utils.ExtensionsPath + case "CreateEnumStmt": // T_CreateEnumStmt + name = utils.TypePath + case "ALTER": // T_AlterOwnerStmt + // TODO: different owner by entitye type + name = utils.PrivilegesPath + case "CREATE": // T_CreateStmt + name = utils.TablePath + case "AlterTableStmt": // T_AlterTableStmt + name = utils.TablePath + case "GRANT": // T_GrantStmt + name = utils.PrivilegesPath + case "ALTER_DEFAULT_PRIVILEGES": // T_AlterDefaultPrivilegesStmt + name = utils.PrivilegesPath + default: + fmt.Fprintln(os.Stderr, "Unsupported:", s.NodeTag(), s.StatementType()) + fmt.Fprintln(os.Stderr, s.SqlString()) + continue + } + if err := appendFile(name, s.SqlString()+";\n", fsys); err != nil { + return err + } + } + return appendFile(utils.ConfigPath, order, fsys) +} + +func readFile(name string, w io.Writer, fsys afero.Fs) error { + f, err := fsys.Open(name) + if err != nil { + return errors.Errorf("failed to open migration: %w", err) + } + defer f.Close() + if _, err := io.Copy(w, f); err != nil { + return errors.Errorf("failed to read migration: %w", err) + } + return nil +} + +func appendFile(name, data string, fsys afero.Fs) error { + if err := utils.MkdirIfNotExistFS(fsys, filepath.Dir(name)); err != nil { + return err + } + f, err := fsys.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return errors.Errorf("failed to open migration file: %w", err) + } + defer f.Close() + if _, err := f.WriteString(data); err != nil { + return errors.Errorf("failed to write migration file: %w", err) + } + return nil +} diff --git a/internal/migration/format/templates/order.toml b/internal/migration/format/templates/order.toml new file mode 100644 index 000000000..a0f3e9f81 --- /dev/null +++ b/internal/migration/format/templates/order.toml @@ -0,0 +1,35 @@ +[db.migrations] +schema_paths = [ + # 1. Cluster-level objects + "./cluster/roles.sql", + "./cluster/extensions.sql", + "./cluster/foreign_data_wrappers.sql", + + # 2. Schema definitions + "./schemas/*/schema.sql", + + # 3. Types and sequences (before tables) + "./schemas/*/types.sql", + "./schemas/*/sequences.sql", + + # 4. Tables (with indexes, constraints) + "./schemas/*/tables/*.sql", + + # 5. Foreign tables + "./schemas/*/foreign_tables/*.sql", + + # 6. Views and materialized views (depend on tables) + "./schemas/*/views/*.sql", + "./schemas/*/materialized_views/*.sql", + + # 7. Functions and procedures (scalar/table-valued only; trigger functions are with their tables/views) + "./schemas/*/functions/*.sql", + "./schemas/*/procedures/*.sql", + + # 8. Replication (depends on tables) + "./cluster/publications.sql", + "./cluster/subscriptions.sql", + + # 9. Event triggers (last) + "./cluster/event_triggers.sql", +] From b3a0b445389ca28c97e2fe263e6391c7e9bcde88 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Sat, 17 Jan 2026 14:11:10 +0700 Subject: [PATCH 3/7] fix: switch on ast types --- internal/migration/format/format.go | 53 +++++++++++++++++++---------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/internal/migration/format/format.go b/internal/migration/format/format.go index c29cde4ea..2feaf116c 100644 --- a/internal/migration/format/format.go +++ b/internal/migration/format/format.go @@ -3,6 +3,7 @@ package format import ( "bytes" "context" + _ "embed" "fmt" "io" "os" @@ -10,6 +11,7 @@ import ( "github.com/go-errors/errors" "github.com/multigres/multigres/go/parser" + "github.com/multigres/multigres/go/parser/ast" "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" "github.com/supabase/cli/pkg/migration" @@ -40,40 +42,55 @@ func Run(ctx context.Context, fsys afero.Fs) error { } for _, s := range stat { var name string - switch s.StatementType() { - case "SET": // T_VariableSetStmt + switch v := s.(type) { + case *ast.VariableSetStmt: name = utils.VariablesPath - case "SELECT": // T_SelectStmt + case *ast.SelectStmt: + fmt.Fprintln(os.Stderr, v.TargetList) // TODO: differentiate function calls to create cron / pgmq / etc name = utils.CronPath - case "CreateExtensionStmt": // T_CreateExtensionStmt + case *ast.CreateExtensionStmt: name = utils.ExtensionsPath - case "CommentStmt": // T_CommentStmt - // TODO: differentiate comment by entity type - name = utils.ExtensionsPath - case "CreateEnumStmt": // T_CreateEnumStmt + case *ast.CommentStmt: + switch v.Objtype { + case ast.OBJECT_SCHEMA: + name = utils.SchemaPath + default: + name = utils.ExtensionsPath + } + case *ast.CreateEnumStmt: name = utils.TypePath - case "ALTER": // T_AlterOwnerStmt - // TODO: different owner by entitye type - name = utils.PrivilegesPath - case "CREATE": // T_CreateStmt + case *ast.AlterOwnerStmt: + switch v.ObjectType { + case ast.OBJECT_TYPE: + name = utils.TypePath + case ast.OBJECT_TABLE: + name = utils.TablePath + case ast.OBJECT_PUBLICATION: + name = utils.PublicationsPath + default: + name = utils.PrivilegesPath + } + case *ast.CreateStmt: name = utils.TablePath - case "AlterTableStmt": // T_AlterTableStmt + case *ast.AlterTableStmt: name = utils.TablePath - case "GRANT": // T_GrantStmt + case *ast.GrantStmt: name = utils.PrivilegesPath - case "ALTER_DEFAULT_PRIVILEGES": // T_AlterDefaultPrivilegesStmt + case *ast.AlterDefaultPrivilegesStmt: name = utils.PrivilegesPath default: - fmt.Fprintln(os.Stderr, "Unsupported:", s.NodeTag(), s.StatementType()) - fmt.Fprintln(os.Stderr, s.SqlString()) + fmt.Fprintln(os.Stderr, "Unsupported:", s.SqlString()) continue } if err := appendFile(name, s.SqlString()+";\n", fsys); err != nil { return err } } - return appendFile(utils.ConfigPath, order, fsys) + if len(utils.Config.Db.Migrations.SchemaPaths) == 0 { + return appendFile(utils.ConfigPath, order, fsys) + } + return nil } func readFile(name string, w io.Writer, fsys afero.Fs) error { From 2bdb01d7049cc8c75aa324d9d3305ff9603607b1 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Sat, 17 Jan 2026 18:23:27 +0700 Subject: [PATCH 4/7] fix: flag for experimental database pull --- internal/db/pull/pull.go | 11 ++++ internal/migration/format/format.go | 90 ++++++++++++++++++++++++----- internal/utils/misc.go | 1 + 3 files changed, 89 insertions(+), 13 deletions(-) diff --git a/internal/db/pull/pull.go b/internal/db/pull/pull.go index b55b6935c..93ef5450e 100644 --- a/internal/db/pull/pull.go +++ b/internal/db/pull/pull.go @@ -1,6 +1,7 @@ package pull import ( + "bytes" "context" _ "embed" "fmt" @@ -14,8 +15,10 @@ import ( "github.com/jackc/pgconn" "github.com/jackc/pgx/v4" "github.com/spf13/afero" + "github.com/spf13/viper" "github.com/supabase/cli/internal/db/diff" "github.com/supabase/cli/internal/db/dump" + "github.com/supabase/cli/internal/migration/format" "github.com/supabase/cli/internal/migration/list" "github.com/supabase/cli/internal/migration/new" "github.com/supabase/cli/internal/migration/repair" @@ -36,6 +39,14 @@ func Run(ctx context.Context, schema []string, config pgconn.Config, name string return err } defer conn.Close(context.Background()) + if viper.GetBool("EXPERIMENTAL") { + var buf bytes.Buffer + if err := migration.DumpSchema(ctx, config, &buf, dump.DockerExec); err != nil { + return err + } + // TODO: handle managed schemas + return format.WriteStructuredSchemas(ctx, buf.String(), fsys) + } // 2. Pull schema timestamp := utils.GetCurrentTimestamp() path := new.GetMigrationPath(timestamp, name) diff --git a/internal/migration/format/format.go b/internal/migration/format/format.go index 2feaf116c..ba5a815fe 100644 --- a/internal/migration/format/format.go +++ b/internal/migration/format/format.go @@ -31,7 +31,11 @@ func Run(ctx context.Context, fsys afero.Fs) error { return err } } - stat, err := parser.ParseSQL(buf.String()) + return WriteStructuredSchemas(ctx, buf.String(), fsys) +} + +func WriteStructuredSchemas(ctx context.Context, sql string, fsys afero.Fs) error { + stat, err := parser.ParseSQL(sql) if err != nil { return errors.Errorf("failed to parse SQL: %w", err) } @@ -41,42 +45,95 @@ func Run(ctx context.Context, fsys afero.Fs) error { } } for _, s := range stat { - var name string + name := utils.UnqualifiedPath switch v := s.(type) { case *ast.VariableSetStmt: name = utils.VariablesPath case *ast.SelectStmt: - fmt.Fprintln(os.Stderr, v.TargetList) + if n := v.TargetList; n != nil && len(n.Items) == 1 { + fmt.Fprintln(os.Stderr, n.Items[0].String()) + } // TODO: differentiate function calls to create cron / pgmq / etc name = utils.CronPath case *ast.CreateExtensionStmt: name = utils.ExtensionsPath + case *ast.CreateSchemaStmt: + name = filepath.Join(utils.SchemasDir, v.Schemaname, "schema.sql") case *ast.CommentStmt: switch v.Objtype { case ast.OBJECT_SCHEMA: - name = utils.SchemaPath + if s, ok := v.Object.(*ast.String); ok { + name = filepath.Join(utils.SchemasDir, s.SVal, "schema.sql") + } + case ast.OBJECT_TABLE: + if n, ok := v.Object.(*ast.NodeList); ok && len(n.Items) == 2 { + if s0, ok := n.Items[0].(*ast.String); !ok { + break + } else if s1, ok := n.Items[1].(*ast.String); !ok { + break + } else { + name = filepath.Join(utils.SchemasDir, s0.SVal, "tables", s1.SVal+".sql") + } + } default: - name = utils.ExtensionsPath + fmt.Fprintln(os.Stderr, "Unsupported:", s.SqlString()) + } + case *ast.CompositeTypeStmt: + if r := v.Typevar; r != nil && len(r.SchemaName) > 0 { + name = filepath.Join(utils.SchemasDir, r.SchemaName, "types.sql") } case *ast.CreateEnumStmt: - name = utils.TypePath + if n := v.TypeName; n != nil && len(n.Items) == 2 { + if s, ok := n.Items[0].(*ast.String); ok { + name = filepath.Join(utils.SchemasDir, s.SVal, "types.sql") + } + } case *ast.AlterOwnerStmt: switch v.ObjectType { case ast.OBJECT_TYPE: - name = utils.TypePath + if n, ok := v.Object.(*ast.NodeList); ok && len(n.Items) == 2 { + if s, ok := n.Items[0].(*ast.String); ok { + name = filepath.Join(utils.SchemasDir, s.SVal, "types.sql") + } + } case ast.OBJECT_TABLE: name = utils.TablePath case ast.OBJECT_PUBLICATION: name = utils.PublicationsPath + case ast.OBJECT_SCHEMA: + if s, ok := v.Object.(*ast.String); ok { + name = filepath.Join(utils.SchemasDir, s.SVal, "schema.sql") + } default: - name = utils.PrivilegesPath + fmt.Fprintln(os.Stderr, "Unsupported:", s.SqlString()) } case *ast.CreateStmt: - name = utils.TablePath + name = getTablePath(v.Relation) case *ast.AlterTableStmt: - name = utils.TablePath + name = getTablePath(v.Relation) case *ast.GrantStmt: - name = utils.PrivilegesPath + switch v.Objtype { + case ast.OBJECT_SCHEMA: + if n := v.Objects; n != nil && len(n.Items) == 1 { + if s, ok := n.Items[0].(*ast.String); ok { + name = filepath.Join(utils.SchemasDir, s.SVal, "schema.sql") + } + } + case ast.OBJECT_TABLE: + if n := v.Objects; n != nil && len(n.Items) == 1 { + if s, ok := n.Items[0].(*ast.RangeVar); ok { + name = getTablePath(s) + } + } + case ast.OBJECT_SEQUENCE: + if n := v.Objects; n != nil && len(n.Items) == 1 { + if s, ok := n.Items[0].(*ast.RangeVar); ok { + name = filepath.Join(utils.SchemasDir, s.SchemaName, "sequences.sql") + } + } + default: + fmt.Fprintln(os.Stderr, "Unsupported:", s.SqlString()) + } case *ast.AlterDefaultPrivilegesStmt: name = utils.PrivilegesPath default: @@ -93,6 +150,13 @@ func Run(ctx context.Context, fsys afero.Fs) error { return nil } +func getTablePath(r *ast.RangeVar) string { + if r != nil && len(r.SchemaName) > 0 { + return filepath.Join(utils.SchemasDir, r.SchemaName, "tables", r.RelName+".sql") + } + return utils.TablePath +} + func readFile(name string, w io.Writer, fsys afero.Fs) error { f, err := fsys.Open(name) if err != nil { @@ -111,11 +175,11 @@ func appendFile(name, data string, fsys afero.Fs) error { } f, err := fsys.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if err != nil { - return errors.Errorf("failed to open migration file: %w", err) + return errors.Errorf("failed to open file: %w", err) } defer f.Close() if _, err := f.WriteString(data); err != nil { - return errors.Errorf("failed to write migration file: %w", err) + return errors.Errorf("failed to write file: %w", err) } return nil } diff --git a/internal/utils/misc.go b/internal/utils/misc.go index d301682bc..e9c03f743 100644 --- a/internal/utils/misc.go +++ b/internal/utils/misc.go @@ -102,6 +102,7 @@ var ( TypePath = filepath.Join(SchemasDir, "type.sql") ViewPath = filepath.Join(SchemasDir, "view.sql") PrivilegesPath = filepath.Join(SchemasDir, "privileges.sql") + UnqualifiedPath = filepath.Join(SchemasDir, "unqualified.sql") DataDir = filepath.Join(SupabaseDirPath, "data") CronPath = filepath.Join(DataDir, "cron.sql") MigrationsDir = filepath.Join(SupabaseDirPath, "migrations") From ee5dd4749d0140cf0d380a5957b38988b0713ae9 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Sun, 18 Jan 2026 23:12:36 +0700 Subject: [PATCH 5/7] fix: unit test sql formatter --- cmd/migration.go | 6 -- internal/migration/format/format.go | 39 +--------- internal/migration/format/format_test.go | 60 +++++++++++++++ .../testdata/simple/cluster/extensions.sql | 8 ++ .../testdata/simple/cluster/publications.sql | 1 + .../testdata/simple/cluster/variables.sql | 11 +++ .../format/testdata/simple/data/cron.sql | 1 + .../migration/format/testdata/simple/dump.sql | 73 +++++++++++++++++++ .../testdata/simple/schemas/privileges.sql | 12 +++ .../testdata/simple/schemas/public/schema.sql | 5 ++ .../simple/schemas/public/sequences.sql | 3 + .../schemas/public/tables/countries.sql | 7 ++ .../testdata/simple/schemas/public/types.sql | 2 + 13 files changed, 185 insertions(+), 43 deletions(-) create mode 100644 internal/migration/format/format_test.go create mode 100644 internal/migration/format/testdata/simple/cluster/extensions.sql create mode 100644 internal/migration/format/testdata/simple/cluster/publications.sql create mode 100644 internal/migration/format/testdata/simple/cluster/variables.sql create mode 100644 internal/migration/format/testdata/simple/data/cron.sql create mode 100644 internal/migration/format/testdata/simple/dump.sql create mode 100644 internal/migration/format/testdata/simple/schemas/privileges.sql create mode 100644 internal/migration/format/testdata/simple/schemas/public/schema.sql create mode 100644 internal/migration/format/testdata/simple/schemas/public/sequences.sql create mode 100644 internal/migration/format/testdata/simple/schemas/public/tables/countries.sql create mode 100644 internal/migration/format/testdata/simple/schemas/public/types.sql diff --git a/cmd/migration.go b/cmd/migration.go index 5ab9c422f..697f3651d 100644 --- a/cmd/migration.go +++ b/cmd/migration.go @@ -9,7 +9,6 @@ import ( "github.com/spf13/viper" "github.com/supabase/cli/internal/migration/down" "github.com/supabase/cli/internal/migration/fetch" - "github.com/supabase/cli/internal/migration/format" "github.com/supabase/cli/internal/migration/list" "github.com/supabase/cli/internal/migration/new" "github.com/supabase/cli/internal/migration/repair" @@ -62,16 +61,12 @@ var ( }, } - useDeclarative bool migrationVersion string migrationSquashCmd = &cobra.Command{ Use: "squash", Short: "Squash migrations to a single file", RunE: func(cmd *cobra.Command, args []string) error { - if useDeclarative { - return format.Run(cmd.Context(), afero.NewOsFs()) - } return squash.Run(cmd.Context(), migrationVersion, flags.DbConfig, afero.NewOsFs()) }, PostRun: func(cmd *cobra.Command, args []string) { @@ -135,7 +130,6 @@ func init() { migrationCmd.AddCommand(migrationRepairCmd) // Build squash command squashFlags := migrationSquashCmd.Flags() - squashFlags.BoolVar(&useDeclarative, "declarative", false, "Squash migrations to declarative schemas") squashFlags.StringVar(&migrationVersion, "version", "", "Squash up to the specified version.") squashFlags.String("db-url", "", "Squashes migrations of the database specified by the connection string (must be percent-encoded).") squashFlags.Bool("linked", false, "Squashes the migration history of the linked project.") diff --git a/internal/migration/format/format.go b/internal/migration/format/format.go index ba5a815fe..cc1c84b35 100644 --- a/internal/migration/format/format.go +++ b/internal/migration/format/format.go @@ -1,11 +1,9 @@ package format import ( - "bytes" "context" _ "embed" "fmt" - "io" "os" "path/filepath" @@ -14,26 +12,11 @@ import ( "github.com/multigres/multigres/go/parser/ast" "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/migration" ) //go:embed templates/order.toml var order string -func Run(ctx context.Context, fsys afero.Fs) error { - files, err := migration.ListLocalMigrations(utils.MigrationsDir, afero.NewIOFS(fsys)) - if err != nil { - return err - } - var buf bytes.Buffer - for _, name := range files { - if err := readFile(name, &buf, fsys); err != nil { - return err - } - } - return WriteStructuredSchemas(ctx, buf.String(), fsys) -} - func WriteStructuredSchemas(ctx context.Context, sql string, fsys afero.Fs) error { stat, err := parser.ParseSQL(sql) if err != nil { @@ -75,8 +58,6 @@ func WriteStructuredSchemas(ctx context.Context, sql string, fsys afero.Fs) erro name = filepath.Join(utils.SchemasDir, s0.SVal, "tables", s1.SVal+".sql") } } - default: - fmt.Fprintln(os.Stderr, "Unsupported:", s.SqlString()) } case *ast.CompositeTypeStmt: if r := v.Typevar; r != nil && len(r.SchemaName) > 0 { @@ -104,8 +85,6 @@ func WriteStructuredSchemas(ctx context.Context, sql string, fsys afero.Fs) erro if s, ok := v.Object.(*ast.String); ok { name = filepath.Join(utils.SchemasDir, s.SVal, "schema.sql") } - default: - fmt.Fprintln(os.Stderr, "Unsupported:", s.SqlString()) } case *ast.CreateStmt: name = getTablePath(v.Relation) @@ -131,14 +110,12 @@ func WriteStructuredSchemas(ctx context.Context, sql string, fsys afero.Fs) erro name = filepath.Join(utils.SchemasDir, s.SchemaName, "sequences.sql") } } - default: - fmt.Fprintln(os.Stderr, "Unsupported:", s.SqlString()) } case *ast.AlterDefaultPrivilegesStmt: name = utils.PrivilegesPath - default: + } + if name == utils.UnqualifiedPath { fmt.Fprintln(os.Stderr, "Unsupported:", s.SqlString()) - continue } if err := appendFile(name, s.SqlString()+";\n", fsys); err != nil { return err @@ -157,18 +134,6 @@ func getTablePath(r *ast.RangeVar) string { return utils.TablePath } -func readFile(name string, w io.Writer, fsys afero.Fs) error { - f, err := fsys.Open(name) - if err != nil { - return errors.Errorf("failed to open migration: %w", err) - } - defer f.Close() - if _, err := io.Copy(w, f); err != nil { - return errors.Errorf("failed to read migration: %w", err) - } - return nil -} - func appendFile(name, data string, fsys afero.Fs) error { if err := utils.MkdirIfNotExistFS(fsys, filepath.Dir(name)); err != nil { return err diff --git a/internal/migration/format/format_test.go b/internal/migration/format/format_test.go new file mode 100644 index 000000000..cbccc294f --- /dev/null +++ b/internal/migration/format/format_test.go @@ -0,0 +1,60 @@ +package format + +import ( + "context" + "embed" + _ "embed" + "fmt" + "io/fs" + "path" + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/supabase/cli/internal/utils" +) + +//go:embed testdata +var testdata embed.FS + +func TestWriteStructured(t *testing.T) { + testCases, err := testdata.ReadDir("testdata") + require.NoError(t, err) + + for _, tc := range testCases { + testName := fmt.Sprintf("formats %s statements", tc.Name()) + testFs := afero.NewBasePathFs( + afero.FromIOFS{FS: testdata}, + path.Join("testdata", tc.Name()), + ) + const dumpPath = "dump.sql" + + t.Run(testName, func(t *testing.T) { + sql, err := afero.ReadFile(testFs, dumpPath) + assert.NoError(t, err) + // Setup in-memory fs + fsys := afero.NewMemMapFs() + // Run test + err = WriteStructuredSchemas(context.Background(), string(sql), fsys) + // Check error + assert.NoError(t, err) + err = afero.Walk(testFs, ".", func(fp string, info fs.FileInfo, err error) error { + if err != nil || info.IsDir() || info.Name() == dumpPath { + return err + } + expected, err := afero.ReadFile(testFs, fp) + if err != nil { + return err + } + actual, err := afero.ReadFile(fsys, path.Join(utils.SupabaseDirPath, fp)) + if err != nil { + return err + } + assert.Equal(t, string(expected), string(actual), fp) + return nil + }) + assert.NoError(t, err) + }) + } +} diff --git a/internal/migration/format/testdata/simple/cluster/extensions.sql b/internal/migration/format/testdata/simple/cluster/extensions.sql new file mode 100644 index 000000000..120fecd6d --- /dev/null +++ b/internal/migration/format/testdata/simple/cluster/extensions.sql @@ -0,0 +1,8 @@ +CREATE EXTENSION IF NOT EXISTS pgsodium; +CREATE EXTENSION IF NOT EXISTS pg_graphql SCHEMA graphql; +CREATE EXTENSION IF NOT EXISTS pg_stat_statements SCHEMA extensions; +CREATE EXTENSION IF NOT EXISTS pgcrypto SCHEMA extensions; +CREATE EXTENSION IF NOT EXISTS pgjwt SCHEMA extensions; +CREATE EXTENSION IF NOT EXISTS postgis SCHEMA extensions; +CREATE EXTENSION IF NOT EXISTS supabase_vault SCHEMA vault; +CREATE EXTENSION IF NOT EXISTS uuid-ossp SCHEMA extensions; diff --git a/internal/migration/format/testdata/simple/cluster/publications.sql b/internal/migration/format/testdata/simple/cluster/publications.sql new file mode 100644 index 000000000..57d298ec0 --- /dev/null +++ b/internal/migration/format/testdata/simple/cluster/publications.sql @@ -0,0 +1 @@ +ALTER PUBLICATION supabase_realtime OWNER TO postgres; diff --git a/internal/migration/format/testdata/simple/cluster/variables.sql b/internal/migration/format/testdata/simple/cluster/variables.sql new file mode 100644 index 000000000..a475757af --- /dev/null +++ b/internal/migration/format/testdata/simple/cluster/variables.sql @@ -0,0 +1,11 @@ +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET NAMES 'UTF8'; +SET standard_conforming_strings = on; +SET check_function_bodies = false; +SET XML OPTION CONTENT; +SET client_min_messages = 'warning'; +SET row_security = off; +SET default_tablespace = ''; +SET default_table_access_method = 'heap'; diff --git a/internal/migration/format/testdata/simple/data/cron.sql b/internal/migration/format/testdata/simple/data/cron.sql new file mode 100644 index 000000000..fdb1ccb14 --- /dev/null +++ b/internal/migration/format/testdata/simple/data/cron.sql @@ -0,0 +1 @@ +SELECT set_config('search_path', '', FALSE); diff --git a/internal/migration/format/testdata/simple/dump.sql b/internal/migration/format/testdata/simple/dump.sql new file mode 100644 index 000000000..465653e43 --- /dev/null +++ b/internal/migration/format/testdata/simple/dump.sql @@ -0,0 +1,73 @@ +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; +CREATE EXTENSION IF NOT EXISTS "pgsodium"; +COMMENT ON SCHEMA "public" IS 'standard public schema'; +CREATE EXTENSION IF NOT EXISTS "pg_graphql" WITH SCHEMA "graphql"; +CREATE EXTENSION IF NOT EXISTS "pg_stat_statements" WITH SCHEMA "extensions"; +CREATE EXTENSION IF NOT EXISTS "pgcrypto" WITH SCHEMA "extensions"; +CREATE EXTENSION IF NOT EXISTS "pgjwt" WITH SCHEMA "extensions"; +CREATE EXTENSION IF NOT EXISTS "postgis" WITH SCHEMA "extensions"; +CREATE EXTENSION IF NOT EXISTS "supabase_vault" WITH SCHEMA "vault"; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA "extensions"; +CREATE TYPE "public"."continents" AS ENUM ( + 'Africa', + 'Antarctica', + 'Asia', + 'Europe', + 'Oceania', + 'North America', + 'South America' +); +ALTER TYPE "public"."continents" OWNER TO "postgres"; +SET default_tablespace = ''; +SET default_table_access_method = "heap"; +CREATE TABLE IF NOT EXISTS "public"."countries" ( + "id" bigint NOT NULL, + "name" "text", + "iso2" "text" NOT NULL, + "iso3" "text", + "local_name" "text", + "continent" "public"."continents" +); +ALTER TABLE "public"."countries" OWNER TO "postgres"; +ALTER TABLE "public"."countries" ALTER COLUMN "id" ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME "public"."countries_id_seq" + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); +ALTER TABLE ONLY "public"."countries" + ADD CONSTRAINT "countries_pkey" PRIMARY KEY ("id"); +ALTER PUBLICATION "supabase_realtime" OWNER TO "postgres"; +GRANT USAGE ON SCHEMA "public" TO "postgres"; +GRANT USAGE ON SCHEMA "public" TO "anon"; +GRANT USAGE ON SCHEMA "public" TO "authenticated"; +GRANT USAGE ON SCHEMA "public" TO "service_role"; +GRANT ALL ON TABLE "public"."countries" TO "anon"; +GRANT ALL ON TABLE "public"."countries" TO "authenticated"; +GRANT ALL ON TABLE "public"."countries" TO "service_role"; +GRANT ALL ON SEQUENCE "public"."countries_id_seq" TO "anon"; +GRANT ALL ON SEQUENCE "public"."countries_id_seq" TO "authenticated"; +GRANT ALL ON SEQUENCE "public"."countries_id_seq" TO "service_role"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "service_role"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "service_role"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "service_role"; diff --git a/internal/migration/format/testdata/simple/schemas/privileges.sql b/internal/migration/format/testdata/simple/schemas/privileges.sql new file mode 100644 index 000000000..acd9f926c --- /dev/null +++ b/internal/migration/format/testdata/simple/schemas/privileges.sql @@ -0,0 +1,12 @@ +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO postgres; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO anon; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO authenticated; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO service_role; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO postgres; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO anon; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO authenticated; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO service_role; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO postgres; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO anon; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO authenticated; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO service_role; diff --git a/internal/migration/format/testdata/simple/schemas/public/schema.sql b/internal/migration/format/testdata/simple/schemas/public/schema.sql new file mode 100644 index 000000000..dab5a9436 --- /dev/null +++ b/internal/migration/format/testdata/simple/schemas/public/schema.sql @@ -0,0 +1,5 @@ +COMMENT ON SCHEMA public IS 'standard public schema'; +GRANT USAGE ON SCHEMA public TO postgres; +GRANT USAGE ON SCHEMA public TO anon; +GRANT USAGE ON SCHEMA public TO authenticated; +GRANT USAGE ON SCHEMA public TO service_role; diff --git a/internal/migration/format/testdata/simple/schemas/public/sequences.sql b/internal/migration/format/testdata/simple/schemas/public/sequences.sql new file mode 100644 index 000000000..8123ca0fd --- /dev/null +++ b/internal/migration/format/testdata/simple/schemas/public/sequences.sql @@ -0,0 +1,3 @@ +GRANT ALL ON SEQUENCE public.countries_id_seq TO anon; +GRANT ALL ON SEQUENCE public.countries_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.countries_id_seq TO service_role; diff --git a/internal/migration/format/testdata/simple/schemas/public/tables/countries.sql b/internal/migration/format/testdata/simple/schemas/public/tables/countries.sql new file mode 100644 index 000000000..c64ea7ba7 --- /dev/null +++ b/internal/migration/format/testdata/simple/schemas/public/tables/countries.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS public.countries (id BIGINT NOT NULL, name TEXT, iso2 TEXT NOT NULL, iso3 TEXT, local_name TEXT, continent continents); +ALTER TABLE public.countries OWNER TO postgres; +ALTER TABLE public.countries ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (START WITH 1 INCREMENT BY 1 CACHE 1); +ALTER TABLE ONLY public.countries ADD CONSTRAINT countries_pkey PRIMARY KEY (id); +GRANT ALL ON public.countries TO anon; +GRANT ALL ON public.countries TO authenticated; +GRANT ALL ON public.countries TO service_role; diff --git a/internal/migration/format/testdata/simple/schemas/public/types.sql b/internal/migration/format/testdata/simple/schemas/public/types.sql new file mode 100644 index 000000000..b9020d1ce --- /dev/null +++ b/internal/migration/format/testdata/simple/schemas/public/types.sql @@ -0,0 +1,2 @@ +CREATE TYPE public.continents AS ENUM ('Africa', 'Antarctica', 'Asia', 'Europe', 'Oceania', 'North America', 'South America'); +ALTER TYPE public.continents OWNER TO postgres; From a1f6dbd7458d88aeba4d85a97a2e639f4cf20611 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Mon, 19 Jan 2026 03:35:00 +0700 Subject: [PATCH 6/7] fix: handle common statement types --- internal/migration/format/format.go | 382 ++++++++++++++---- .../format/testdata/simple/data/cron.sql | 1 - .../schemas/{ => public}/privileges.sql | 0 internal/utils/misc.go | 18 +- 4 files changed, 314 insertions(+), 87 deletions(-) delete mode 100644 internal/migration/format/testdata/simple/data/cron.sql rename internal/migration/format/testdata/simple/schemas/{ => public}/privileges.sql (100%) diff --git a/internal/migration/format/format.go b/internal/migration/format/format.go index cc1c84b35..1df5e7331 100644 --- a/internal/migration/format/format.go +++ b/internal/migration/format/format.go @@ -30,89 +30,196 @@ func WriteStructuredSchemas(ctx context.Context, sql string, fsys afero.Fs) erro for _, s := range stat { name := utils.UnqualifiedPath switch v := s.(type) { - case *ast.VariableSetStmt: - name = utils.VariablesPath - case *ast.SelectStmt: - if n := v.TargetList; n != nil && len(n.Items) == 1 { - fmt.Fprintln(os.Stderr, n.Items[0].String()) - } - // TODO: differentiate function calls to create cron / pgmq / etc - name = utils.CronPath - case *ast.CreateExtensionStmt: + // Cluster level entities + case *ast.CreateRoleStmt, *ast.AlterRoleStmt, *ast.AlterRoleSetStmt, *ast.GrantRoleStmt: + name = utils.RolesPath + case *ast.CreateExtensionStmt, *ast.AlterExtensionStmt, *ast.AlterExtensionContentsStmt: name = utils.ExtensionsPath + case *ast.CreateFdwStmt, *ast.AlterFdwStmt, *ast.CreateForeignServerStmt, *ast.AlterForeignServerStmt, *ast.CreateUserMappingStmt, *ast.AlterUserMappingStmt: + name = utils.ForeignDWPath + case *ast.CreatePublicationStmt, *ast.AlterPublicationStmt: + name = utils.PublicationsPath + case *ast.CreateSubscriptionStmt, *ast.AlterSubscriptionStmt: + name = utils.SubscriptionsPath + case *ast.CreateEventTrigStmt, *ast.AlterEventTrigStmt: + name = utils.EventTriggersPath + case *ast.CreateTableSpaceStmt, *ast.AlterTableSpaceStmt: + name = utils.TablespacesPath + case *ast.AlterDatabaseStmt, *ast.AlterDatabaseSetStmt, *ast.AlterSystemStmt, *ast.VariableSetStmt: + name = utils.VariablesPath + // Schema level entities case *ast.CreateSchemaStmt: - name = filepath.Join(utils.SchemasDir, v.Schemaname, "schema.sql") - case *ast.CommentStmt: - switch v.Objtype { - case ast.OBJECT_SCHEMA: - if s, ok := v.Object.(*ast.String); ok { - name = filepath.Join(utils.SchemasDir, s.SVal, "schema.sql") - } - case ast.OBJECT_TABLE: - if n, ok := v.Object.(*ast.NodeList); ok && len(n.Items) == 2 { - if s0, ok := n.Items[0].(*ast.String); !ok { - break - } else if s1, ok := n.Items[1].(*ast.String); !ok { - break - } else { - name = filepath.Join(utils.SchemasDir, s0.SVal, "tables", s1.SVal+".sql") - } - } + name = getSchemaPath(v.Schemaname) + case *ast.CreateOpFamilyStmt: + if s := getQualifiedSchema(v.OpFamilyName); s != nil { + name = getSchemaPath(s.SVal) + } + case *ast.AlterOpFamilyStmt: + if s := getQualifiedSchema(v.OpFamilyName); s != nil { + name = getSchemaPath(s.SVal) + } + case *ast.AlterCollationStmt: + if s := getQualifiedSchema(v.Collname); s != nil { + name = getSchemaPath(s.SVal) + } + case *ast.AlterTSDictionaryStmt: + if s := getQualifiedSchema(v.Dictname); s != nil { + name = getSchemaPath(s.SVal) + } + case *ast.AlterTSConfigurationStmt: + if s := getQualifiedSchema(v.Cfgname); s != nil { + name = getSchemaPath(s.SVal) } + // Schema level entities - types case *ast.CompositeTypeStmt: if r := v.Typevar; r != nil && len(r.SchemaName) > 0 { - name = filepath.Join(utils.SchemasDir, r.SchemaName, "types.sql") + name = getTypesPath(r.SchemaName) + } + case *ast.AlterCompositeTypeStmt: + if s := getQualifiedSchema(v.TypeName); s != nil { + name = getTypesPath(s.SVal) + } + case *ast.AlterTypeStmt: + if s := getQualifiedSchema(v.TypeName); s != nil { + name = getTypesPath(s.SVal) } case *ast.CreateEnumStmt: - if n := v.TypeName; n != nil && len(n.Items) == 2 { - if s, ok := n.Items[0].(*ast.String); ok { - name = filepath.Join(utils.SchemasDir, s.SVal, "types.sql") - } + if s := getQualifiedSchema(v.TypeName); s != nil { + name = getTypesPath(s.SVal) } - case *ast.AlterOwnerStmt: - switch v.ObjectType { - case ast.OBJECT_TYPE: - if n, ok := v.Object.(*ast.NodeList); ok && len(n.Items) == 2 { - if s, ok := n.Items[0].(*ast.String); ok { - name = filepath.Join(utils.SchemasDir, s.SVal, "types.sql") - } + case *ast.AlterEnumStmt: + if s := getQualifiedSchema(v.TypeName); s != nil { + name = getTypesPath(s.SVal) + } + case *ast.CreateRangeStmt: + if s := getQualifiedSchema(v.TypeName); s != nil { + name = getTypesPath(s.SVal) + } + case *ast.CreateTransformStmt: + if t := v.TypeName; t != nil { + if s := getQualifiedSchema(t.Names); s != nil { + name = getTypesPath(s.SVal) } - case ast.OBJECT_TABLE: - name = utils.TablePath - case ast.OBJECT_PUBLICATION: - name = utils.PublicationsPath - case ast.OBJECT_SCHEMA: - if s, ok := v.Object.(*ast.String); ok { - name = filepath.Join(utils.SchemasDir, s.SVal, "schema.sql") + } + case *ast.CreateDomainStmt: + if s := getQualifiedSchema(v.Domainname); s != nil { + name = getTypesPath(s.SVal) + } + case *ast.AlterDomainStmt: + if s := getQualifiedSchema(v.TypeName); s != nil { + name = getTypesPath(s.SVal) + } + case *ast.CreateOpClassStmt: + if t := v.DataType; t != nil { + if s := getQualifiedSchema(t.Names); s != nil { + name = getTypesPath(s.SVal) } } + case *ast.DefineStmt: + if s := getQualifiedSchema(v.DefNames); s != nil { + name = getTypesPath(s.SVal) + } + // Schema level entities - relations case *ast.CreateStmt: - name = getTablePath(v.Relation) + if r := v.Relation; r != nil && len(r.SchemaName) > 0 { + name = getTablePath(r.SchemaName, r.RelName) + } case *ast.AlterTableStmt: - name = getTablePath(v.Relation) - case *ast.GrantStmt: - switch v.Objtype { - case ast.OBJECT_SCHEMA: - if n := v.Objects; n != nil && len(n.Items) == 1 { - if s, ok := n.Items[0].(*ast.String); ok { - name = filepath.Join(utils.SchemasDir, s.SVal, "schema.sql") - } + if r := v.Relation; r != nil && len(r.SchemaName) > 0 { + name = getTablePath(r.SchemaName, r.RelName) + } + case *ast.CreateForeignTableStmt: + if t := v.Base; t != nil { + if r := t.Relation; r != nil && len(r.SchemaName) > 0 { + name = getForeignTablePath(r.SchemaName, r.RelName) } - case ast.OBJECT_TABLE: - if n := v.Objects; n != nil && len(n.Items) == 1 { - if s, ok := n.Items[0].(*ast.RangeVar); ok { - name = getTablePath(s) - } + } + case *ast.CreateTableAsStmt: + if t := v.Into; t != nil { + if r := t.Rel; r != nil && len(r.SchemaName) > 0 { + name = getMaterializedViewPath(r.SchemaName, r.RelName) } - case ast.OBJECT_SEQUENCE: - if n := v.Objects; n != nil && len(n.Items) == 1 { - if s, ok := n.Items[0].(*ast.RangeVar); ok { - name = filepath.Join(utils.SchemasDir, s.SchemaName, "sequences.sql") - } + } + case *ast.ViewStmt: + if r := v.View; r != nil && len(r.SchemaName) > 0 { + name = getViewPath(r.SchemaName, r.RelName) + } + case *ast.CreateSeqStmt: + if r := v.Sequence; r != nil && len(r.SchemaName) > 0 { + name = getSequencesPath(r.SchemaName) + } + case *ast.AlterSeqStmt: + if r := v.Sequence; r != nil && len(r.SchemaName) > 0 { + name = getSequencesPath(r.SchemaName) + } + case *ast.IndexStmt: + if r := v.Relation; r != nil && len(r.SchemaName) > 0 { + name = getTablePath(r.SchemaName, r.RelName) + } + case *ast.CreatePolicyStmt: + if r := v.Table; r != nil && len(r.SchemaName) > 0 { + name = getTablePath(r.SchemaName, r.RelName) + } + case *ast.AlterPolicyStmt: + if r := v.Table; r != nil && len(r.SchemaName) > 0 { + name = getTablePath(r.SchemaName, r.RelName) + } + case *ast.RuleStmt: + if r := v.Relation; r != nil && len(r.SchemaName) > 0 { + name = getTablePath(r.SchemaName, r.RelName) + } + // Schema level entities - functions + case *ast.CreateFunctionStmt: + if s := toQualifiedName(v.FuncName); len(s) == 2 { + name = getFunctionPath(s[0], s[1]) + } + case *ast.AlterFunctionStmt: + if f := v.Func; f != nil { + if s := toQualifiedName(f.Objname); len(s) == 2 { + name = getFunctionPath(s[0], s[1]) + } + } + case *ast.CreateTriggerStmt: + if s := toQualifiedName(v.Funcname); len(s) == 2 { + name = getFunctionPath(s[0], s[1]) + } + case *ast.CreatePLangStmt: + if s := toQualifiedName(v.PLHandler); len(s) == 2 { + name = getFunctionPath(s[0], s[1]) + } + case *ast.CreateAmStmt: + if s := toQualifiedName(v.HandlerName); len(s) == 2 { + name = getFunctionPath(s[0], s[1]) + } + // Schema level entities - others + case *ast.CommentStmt: + if s := getNodePath(v.Objtype, v.Object); len(s) > 0 { + name = s + } + case *ast.AlterOwnerStmt: + if s := getNodePath(v.ObjectType, v.Object); len(s) > 0 { + name = s + } + case *ast.GrantStmt: + if n := v.Objects; n != nil && len(n.Items) == 1 { + if s := getNodePath(v.Objtype, n.Items[0]); len(s) > 0 { + name = s } } case *ast.AlterDefaultPrivilegesStmt: - name = utils.PrivilegesPath + if n := v.Options; n != nil { + for _, s := range n.Items { + if e, ok := s.(*ast.DefElem); ok && e.Defname == "schemas" { + if n, ok := e.Arg.(*ast.NodeList); ok && len(n.Items) == 1 { + if p, ok := n.Items[0].(*ast.String); ok { + name = getPrivilegesPath(p.SVal) + } + } + } + } + } + // TODO: Data level entities, ie. pg_cron, pgmq, etc. + case *ast.InsertStmt, *ast.UpdateStmt, *ast.DeleteStmt, *ast.CopyStmt, *ast.CallStmt, *ast.SelectStmt: } if name == utils.UnqualifiedPath { fmt.Fprintln(os.Stderr, "Unsupported:", s.SqlString()) @@ -127,11 +234,148 @@ func WriteStructuredSchemas(ctx context.Context, sql string, fsys afero.Fs) erro return nil } -func getTablePath(r *ast.RangeVar) string { - if r != nil && len(r.SchemaName) > 0 { - return filepath.Join(utils.SchemasDir, r.SchemaName, "tables", r.RelName+".sql") +func getNodePath(obj ast.ObjectType, n ast.Node) string { + switch obj { + case ast.OBJECT_ACCESS_METHOD: + case ast.OBJECT_AGGREGATE: + // case ast.OBJECT_AMOP: + // case ast.OBJECT_AMPROC: + // case ast.OBJECT_ATTRIBUTE: + // case ast.OBJECT_CAST: + case ast.OBJECT_COLUMN: + case ast.OBJECT_COLLATION: + // case ast.OBJECT_CONVERSION: + // case ast.OBJECT_DATABASE: + // case ast.OBJECT_DEFAULT: + // case ast.OBJECT_DEFACL: + case ast.OBJECT_DOMAIN: + // case ast.OBJECT_DOMCONSTRAINT: + case ast.OBJECT_EVENT_TRIGGER: + return utils.EventTriggersPath + case ast.OBJECT_EXTENSION: + return utils.ExtensionsPath + case ast.OBJECT_FDW: + return utils.ForeignDWPath + case ast.OBJECT_FOREIGN_SERVER: + return utils.ForeignDWPath + case ast.OBJECT_FOREIGN_TABLE: + case ast.OBJECT_FUNCTION: + case ast.OBJECT_INDEX: + // case ast.OBJECT_LANGUAGE: + // case ast.OBJECT_LARGEOBJECT: + case ast.OBJECT_MATVIEW: + // case ast.OBJECT_OPCLASS: + // case ast.OBJECT_OPERATOR: + // case ast.OBJECT_OPFAMILY: + // case ast.OBJECT_PARAMETER_ACL: + case ast.OBJECT_POLICY: + case ast.OBJECT_PROCEDURE: + case ast.OBJECT_PUBLICATION: + return utils.PublicationsPath + case ast.OBJECT_PUBLICATION_NAMESPACE: + case ast.OBJECT_PUBLICATION_REL: + case ast.OBJECT_ROLE: + case ast.OBJECT_ROUTINE: + case ast.OBJECT_RULE: + case ast.OBJECT_SCHEMA: + if s, ok := n.(*ast.String); ok { + return getSchemaPath(s.SVal) + } + case ast.OBJECT_SEQUENCE: + if nl, ok := n.(*ast.NodeList); ok { + if s := toQualifiedName(nl); len(s) == 2 { + return getTablePath(s[0], s[1]) + } + } else if s, ok := n.(*ast.RangeVar); ok { + return getSequencesPath(s.SchemaName) + } + case ast.OBJECT_SUBSCRIPTION: + return utils.SubscriptionsPath + // case ast.OBJECT_STATISTIC_EXT: + // case ast.OBJECT_TABCONSTRAINT: + case ast.OBJECT_TABLE: + if nl, ok := n.(*ast.NodeList); ok { + if s := toQualifiedName(nl); len(s) == 2 { + return getTablePath(s[0], s[1]) + } + } else if r, ok := n.(*ast.RangeVar); ok && len(r.SchemaName) > 0 { + return getTablePath(r.SchemaName, r.RelName) + } + case ast.OBJECT_TABLESPACE: + case ast.OBJECT_TRANSFORM: + case ast.OBJECT_TRIGGER: + case ast.OBJECT_TSCONFIGURATION: + case ast.OBJECT_TSDICTIONARY: + // case ast.OBJECT_TSPARSER: + // case ast.OBJECT_TSTEMPLATE: + case ast.OBJECT_TYPE: + if nl, ok := n.(*ast.NodeList); ok && len(nl.Items) == 2 { + if s, ok := nl.Items[0].(*ast.String); ok { + return getTypesPath(s.SVal) + } + } + case ast.OBJECT_USER_MAPPING: + case ast.OBJECT_VIEW: + } + return "" +} + +func getQualifiedSchema(n *ast.NodeList) *ast.String { + if n != nil && len(n.Items) == 2 { + if s, ok := n.Items[0].(*ast.String); ok { + return s + } } - return utils.TablePath + return nil +} + +func toQualifiedName(n *ast.NodeList) []string { + if n == nil { + return nil + } + var r []string + for _, v := range n.Items { + if s, ok := v.(*ast.String); ok { + r = append(r, s.SVal) + } + } + return r +} + +func getSchemaPath(name string) string { + return filepath.Join(utils.SchemasDir, name, "schema.sql") +} + +func getTypesPath(schema string) string { + return filepath.Join(utils.SchemasDir, schema, "types.sql") +} + +func getSequencesPath(schema string) string { + return filepath.Join(utils.SchemasDir, schema, "sequences.sql") +} + +func getPrivilegesPath(schema string) string { + return filepath.Join(utils.SchemasDir, schema, "privileges.sql") +} + +func getTablePath(schema, name string) string { + return filepath.Join(utils.SchemasDir, schema, "tables", name+".sql") +} + +func getForeignTablePath(schema, name string) string { + return filepath.Join(utils.SchemasDir, schema, "foreign_tables", name+".sql") +} + +func getFunctionPath(schema, name string) string { + return filepath.Join(utils.SchemasDir, schema, "functions", name+".sql") +} + +func getMaterializedViewPath(schema, name string) string { + return filepath.Join(utils.SchemasDir, schema, "materialized_views", name+".sql") +} + +func getViewPath(schema, name string) string { + return filepath.Join(utils.SchemasDir, schema, "views", name+".sql") } func appendFile(name, data string, fsys afero.Fs) error { diff --git a/internal/migration/format/testdata/simple/data/cron.sql b/internal/migration/format/testdata/simple/data/cron.sql deleted file mode 100644 index fdb1ccb14..000000000 --- a/internal/migration/format/testdata/simple/data/cron.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT set_config('search_path', '', FALSE); diff --git a/internal/migration/format/testdata/simple/schemas/privileges.sql b/internal/migration/format/testdata/simple/schemas/public/privileges.sql similarity index 100% rename from internal/migration/format/testdata/simple/schemas/privileges.sql rename to internal/migration/format/testdata/simple/schemas/public/privileges.sql diff --git a/internal/utils/misc.go b/internal/utils/misc.go index e9c03f743..9fb6ed265 100644 --- a/internal/utils/misc.go +++ b/internal/utils/misc.go @@ -84,27 +84,11 @@ var ( PublicationsPath = filepath.Join(ClusterDir, "publications.sql") SubscriptionsPath = filepath.Join(ClusterDir, "subscriptions.sql") EventTriggersPath = filepath.Join(ClusterDir, "event_triggers.sql") + TablespacesPath = filepath.Join(ClusterDir, "tablespaces.sql") VariablesPath = filepath.Join(ClusterDir, "variables.sql") SchemasDir = filepath.Join(SupabaseDirPath, "schemas") - AggregatePath = filepath.Join(SchemasDir, "aggregate.sql") - CollationPath = filepath.Join(SchemasDir, "collation.sql") - DomainPath = filepath.Join(SchemasDir, "domain.sql") - IndexPath = filepath.Join(SchemasDir, "index.sql") - LanguagePath = filepath.Join(SchemasDir, "language.sql") - MaterializedViewPath = filepath.Join(SchemasDir, "materialized-view.sql") - ProcedurePath = filepath.Join(SchemasDir, "procedure.sql") - RlsPolicyPath = filepath.Join(SchemasDir, "rls-policy.sql") - RulePath = filepath.Join(SchemasDir, "rule.sql") - SchemaPath = filepath.Join(SchemasDir, "schema.sql") - SequencePath = filepath.Join(SchemasDir, "sequence.sql") - TablePath = filepath.Join(SchemasDir, "table.sql") - TriggerPath = filepath.Join(SchemasDir, "trigger.sql") - TypePath = filepath.Join(SchemasDir, "type.sql") - ViewPath = filepath.Join(SchemasDir, "view.sql") - PrivilegesPath = filepath.Join(SchemasDir, "privileges.sql") UnqualifiedPath = filepath.Join(SchemasDir, "unqualified.sql") DataDir = filepath.Join(SupabaseDirPath, "data") - CronPath = filepath.Join(DataDir, "cron.sql") MigrationsDir = filepath.Join(SupabaseDirPath, "migrations") FunctionsDir = filepath.Join(SupabaseDirPath, "functions") SnippetsDir = filepath.Join(SupabaseDirPath, "snippets") From 8bfc53dbd0f0737d401b48190569034533e7a522 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Mon, 19 Jan 2026 03:40:26 +0700 Subject: [PATCH 7/7] chore: lint errors --- go.sum | 2 ++ internal/migration/format/format_test.go | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/go.sum b/go.sum index ce56d2c3b..ed2621c05 100644 --- a/go.sum +++ b/go.sum @@ -890,6 +890,8 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0 github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pganalyze/pg_query_go/v6 v6.1.0 h1:jG5ZLhcVgL1FAw4C/0VNQaVmX1SUJx71wBGdtTtBvls= +github.com/pganalyze/pg_query_go/v6 v6.1.0/go.mod h1:nvTHIuoud6e1SfrUaFwHqT0i4b5Nr+1rPWVds3B5+50= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= diff --git a/internal/migration/format/format_test.go b/internal/migration/format/format_test.go index cbccc294f..a2feb856c 100644 --- a/internal/migration/format/format_test.go +++ b/internal/migration/format/format_test.go @@ -3,7 +3,6 @@ package format import ( "context" "embed" - _ "embed" "fmt" "io/fs" "path"