Repository: supabase/cli
Pull request #907 (2023-03-03 06:40:59)

feat: support self hosted urls with db commands
## What kind of change does this PR introduce?

feature

closes https://github.com/supabase/cli/issues/753

## What is the new behavior?

support migrations for self-hosted database via `--db-url` flag, including
- [x] `supabase migration list [--db-url]`
- [x] `supabase migration repair [--db-url]`
- [x] `supabase db dump [--db-url]`
- [x] `supabase db push [--db-url]`
- [x] `supabase db remote commit [--db-url]`
- [x] `supabase db diff [--linked | --db-url]`

## Additional context

Add any other context or screenshots.


coveralls: ## Pull Request Test Coverage Report for [Build 4320946297](https://coveralls.io/builds/57496676)

* **1** of **29**   **(3.45%)**  changed or added relevant lines in **3** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage decreased (**-0.004%**) to **62.658%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [internal/db/remote/changes/changes.go](https://coveralls.io/builds/57496676/source?filename=internal%2Fdb%2Fremote%2Fchanges%2Fchanges.go#L15) | 0 | 3 | 0.0%
| [internal/db/remote/commit/commit.go](https://coveralls.io/builds/57496676/source?filename=internal%2Fdb%2Fremote%2Fcommit%2Fcommit.go#L20) | 0 | 3 | 0.0%
| [cmd/db.go](https://coveralls.io/builds/57496676/source?filename=cmd%2Fdb.go#L111) | 1 | 23 | 4.35%
<!-- | **Total:** | **1** | **29** | **3.45%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/57496676/badge)](https://coveralls.io/builds/57496676) |
| :-- | --: |
| Change from base [Build 4320551854](https://coveralls.io/builds/57495333): |  -0.004% |
| Covered Lines: | 3762 |
| Relevant Lines: | 6004 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/cmd/db.go b/cmd/db.go
index 8f6693c7..f51ce53c 100644
--- a/cmd/db.go
+++ b/cmd/db.go
@@ -83,16 +83,16 @@ var (
 		Short: "Diffs the local database for schema changes",
 		RunE: func(cmd *cobra.Command, args []string) error {
 			fsys := afero.NewOsFs()
-			if linked {
-				if err := loadLinkedProject(fsys); err != nil {
+			if linked || len(dbUrl) > 0 {
+				if err := parseDatabaseConfig(fsys); err != nil {
 					return err
 				}
 			}
 			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
 			if usePgAdmin {
-				return diff.Run(ctx, schema, file, dbPassword, fsys)
+				return diff.Run(ctx, schema, file, dbConfig, fsys)
 			}
-			return diff.RunMigra(ctx, schema, file, dbPassword, fsys)
+			return diff.RunMigra(ctx, schema, file, dbConfig, fsys)
 		},
 	}
 
@@ -104,12 +104,11 @@ var (
 		Short: "Dumps data or schemas from the remote database",
 		RunE: func(cmd *cobra.Command, args []string) error {
 			fsys := afero.NewOsFs()
-			if err := loadLinkedProject(fsys); err != nil {
+			if err := parseDatabaseConfig(fsys); err != nil {
 				return err
 			}
-			host := utils.GetSupabaseDbHost(projectRef)
 			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
-			return dump.Run(ctx, file, username, dbPassword, database, host, dataOnly, roleOnly, fsys)
+			return dump.Run(ctx, file, dbConfig, dataOnly, roleOnly, fsys)
 		},
 	}
 
@@ -120,12 +119,11 @@ var (
 		Short: "Push new migrations to the remote database",
 		RunE: func(cmd *cobra.Command, args []string) error {
 			fsys := afero.NewOsFs()
-			if err := loadLinkedProject(fsys); err != nil {
+			if err := parseDatabaseConfig(fsys); err != nil {
 				return err
 			}
-			host := utils.GetSupabaseDbHost(projectRef)
 			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
-			return push.Run(ctx, dryRun, username, dbPassword, database, host, fsys)
+			return push.Run(ctx, dryRun, dbConfig, fsys)
 		},
 	}
 
@@ -137,7 +135,7 @@ var (
 				return err
 			}
 			fsys := afero.NewOsFs()
-			return loadLinkedProject(fsys)
+			return parseDatabaseConfig(fsys)
 		},
 	}
 
@@ -149,7 +147,7 @@ var (
 		RunE: func(cmd *cobra.Command, args []string) error {
 			fsys := afero.NewOsFs()
 			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
-			return changes.Run(ctx, schema, username, dbPassword, database, fsys)
+			return changes.Run(ctx, schema, dbConfig, fsys)
 		},
 	}
 
@@ -159,7 +157,7 @@ var (
 		RunE: func(cmd *cobra.Command, args []string) error {
 			fsys := afero.NewOsFs()
 			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
-			return commit.Run(ctx, schema, username, dbPassword, database, fsys)
+			return commit.Run(ctx, schema, dbConfig, fsys)
 		},
 	}
 
@@ -207,6 +205,7 @@ var (
 )
 
 func init() {
+	dbCmd.PersistentFlags().StringVar(&dbUrl, "db-url", "", "connect using the specified database url")
 	// Build branch command
 	dbBranchCmd.AddCommand(dbBranchCreateCmd)
 	dbBranchCmd.AddCommand(dbBranchDeleteCmd)
diff --git a/cmd/link.go b/cmd/link.go
index 59c9a635..f36facb4 100644
--- a/cmd/link.go
+++ b/cmd/link.go
@@ -11,10 +11,6 @@ import (
 )
 
 var (
-	// TODO: allow switching roles on backend
-	database = "postgres"
-	username = "postgres"
-
 	linkCmd = &cobra.Command{
 		GroupID: groupLocalDev,
 		Use:     "link",
@@ -29,7 +25,7 @@ var (
 				dbPassword = link.PromptPassword(os.Stdin)
 			}
 			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
-			return link.Run(ctx, projectRef, username, dbPassword, database, fsys)
+			return link.Run(ctx, projectRef, dbPassword, fsys)
 		},
 		PostRunE: func(cmd *cobra.Command, args []string) error {
 			return link.PostRun(projectRef, os.Stdout, afero.NewOsFs())
diff --git a/cmd/migration.go b/cmd/migration.go
index 04ac4312..e5db5514 100644
--- a/cmd/migration.go
+++ b/cmd/migration.go
@@ -4,6 +4,7 @@ import (
 	"os"
 	"os/signal"
 
+	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
 	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
@@ -22,17 +23,18 @@ var (
 		Short:   "Manage database migration scripts",
 	}
 
+	dbConfig pgconn.Config
+
 	migrationListCmd = &cobra.Command{
 		Use:   "list",
 		Short: "List local and remote migrations",
 		RunE: func(cmd *cobra.Command, args []string) error {
 			fsys := afero.NewOsFs()
-			if err := loadLinkedProject(fsys); err != nil {
+			if err := parseDatabaseConfig(fsys); err != nil {
 				return err
 			}
-			host := utils.GetSupabaseDbHost(projectRef)
 			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
-			return list.Run(ctx, username, dbPassword, database, host, fsys)
+			return list.Run(ctx, dbConfig, fsys)
 		},
 	}
 
@@ -58,17 +60,17 @@ var (
 		Args:  cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			fsys := afero.NewOsFs()
-			if err := loadLinkedProject(fsys); err != nil {
+			if err := parseDatabaseConfig(fsys); err != nil {
 				return err
 			}
-			host := utils.GetSupabaseDbHost(projectRef)
 			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
-			return repair.Run(ctx, username, dbPassword, database, host, args[0], targetStatus.Value)
+			return repair.Run(ctx, dbConfig, args[0], targetStatus.Value)
 		},
 	}
 )
 
 func init() {
+	migrationCmd.PersistentFlags().StringVar(&dbUrl, "db-url", "", "connect using the specified database url")
 	// Build list command
 	listFlags := migrationListCmd.Flags()
 	listFlags.StringVarP(&dbPassword, "password", "p", "", "Password to your remote Postgres database.")
@@ -104,3 +106,23 @@ func getPassword(projectRef string) string {
 	}
 	return link.PromptPassword(os.Stdin)
 }
+
+func parseDatabaseConfig(fsys afero.Fs) error {
+	if len(dbUrl) > 0 {
+		config, err := pgconn.ParseConfig(dbUrl)
+		if err == nil {
+			dbConfig = *config
+		}
+		return err
+	}
+	if err := loadLinkedProject(fsys); err != nil {
+		return err
+	}
+	// Initialise connection details for hosted project
+	dbConfig.Host = utils.GetSupabaseDbHost(projectRef)
+	dbConfig.Port = 6543
+	dbConfig.User = "postgres"
+	dbConfig.Password = dbPassword
+	dbConfig.Database = "postgres"
+	return nil
+}
diff --git a/internal/db/diff/diff.go b/internal/db/diff/diff.go
index 655f8033..791d4126 100644
--- a/internal/db/diff/diff.go
+++ b/internal/db/diff/diff.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"os"
 
+	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/migration/new"
 	"github.com/supabase/cli/internal/utils"
@@ -29,7 +30,7 @@ func SaveDiff(out, file string, fsys afero.Fs) error {
 	return nil
 }
 
-func Run(ctx context.Context, schema []string, file, password string, fsys afero.Fs) error {
+func Run(ctx context.Context, schema []string, file string, config pgconn.Config, fsys afero.Fs) error {
 	// Sanity checks.
 	{
 		if err := utils.LoadConfigFS(fsys); err != nil {
@@ -41,7 +42,7 @@ func Run(ctx context.Context, schema []string, file, password string, fsys afero
 	}
 
 	if err := utils.RunProgram(ctx, func(p utils.Program, ctx context.Context) error {
-		return run(p, ctx, schema, fsys)
+		return run(p, ctx, schema, config, fsys)
 	}); err != nil {
 		return err
 	}
@@ -51,7 +52,7 @@ func Run(ctx context.Context, schema []string, file, password string, fsys afero
 
 var output string
 
-func run(p utils.Program, ctx context.Context, schema []string, fsys afero.Fs) error {
+func run(p utils.Program, ctx context.Context, schema []string, config pgconn.Config, fsys afero.Fs) error {
 	p.Send(utils.StatusMsg("Creating shadow database..."))
 
 	// 1. Create shadow db and run migrations
@@ -68,7 +69,14 @@ func run(p utils.Program, ctx context.Context, schema []string, fsys afero.Fs) e
 
 	// 2. Diff local db (source) with shadow db (target), print it.
 	source := "postgresql://postgres:postgres@" + utils.DbId + ":5432/postgres"
-	target := "postgresql://postgres:postgres@" + shadow[:12] + ":5432/postgres"
+	if len(config.Password) == 0 {
+		config.Host = shadow[:12]
+		config.Port = 5432
+		config.User = "postgres"
+		config.Password = "postgres"
+		config.Database = "postgres"
+	}
+	target := utils.ToPostgresURL(config)
 	output, err = DiffSchema(ctx, source, target, schema, p)
 	return err
 }
diff --git a/internal/db/diff/migra.go b/internal/db/diff/migra.go
index 574a8589..6f7e8830 100644
--- a/internal/db/diff/migra.go
+++ b/internal/db/diff/migra.go
@@ -1,12 +1,12 @@
 package diff
 
 import (
+	"bytes"
 	"context"
 	_ "embed"
 	"errors"
 	"fmt"
 	"io"
-	"net/url"
 	"os"
 	"path/filepath"
 	"strconv"
@@ -30,38 +30,33 @@ var (
 	diffSchemaScript string
 )
 
-func RunMigra(ctx context.Context, schema []string, file, password string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
+func RunMigra(ctx context.Context, schema []string, file string, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) (err error) {
 	// Sanity checks.
 	if err := utils.LoadConfigFS(fsys); err != nil {
 		return err
 	}
-	// 1. Determine local or remote target
-	target, err := buildTargetUrl(password, fsys)
-	if err != nil {
-		return err
-	}
-	// 2. Load all user defined schemas
-	if len(schema) == 0 {
-		var conn *pgx.Conn
-		if len(password) > 0 {
-			options = append(options, func(cc *pgx.ConnConfig) {
-				cc.PreferSimpleProtocol = true
-			})
-			conn, err = utils.ConnectByUrl(ctx, target, options...)
-		} else {
-			conn, err = utils.ConnectLocalPostgres(ctx, "localhost", utils.Config.Db.Port, "postgres", options...)
-		}
-		if err != nil {
+	if len(config.Password) > 0 {
+		fmt.Fprintln(os.Stderr, "Connecting to remote database...")
+	} else {
+		fmt.Fprintln(os.Stderr, "Connecting to local database...")
+		if err := utils.AssertSupabaseDbIsRunning(); err != nil {
 			return err
 		}
-		defer conn.Close(context.Background())
-		schema, err = LoadUserSchemas(ctx, conn)
+		config.Host = "localhost"
+		config.Port = uint16(utils.Config.Db.Port)
+		config.User = "postgres"
+		config.Password = "postgres"
+		config.Database = "postgres"
+	}
+	// 1. Load all user defined schemas
+	if len(schema) == 0 {
+		schema, err = loadSchema(ctx, config, options...)
 		if err != nil {
 			return err
 		}
 	}
 	// 3. Run migra to diff schema
-	out, err := DiffDatabase(ctx, schema, target, os.Stderr, fsys, options...)
+	out, err := DiffDatabase(ctx, schema, config, os.Stderr, fsys, options...)
 	if err != nil {
 		return err
 	}
@@ -73,27 +68,18 @@ func RunMigra(ctx context.Context, schema []string, file, password string, fsys
 	return SaveDiff(out, file, fsys)
 }
 
-// Builds a postgres connection string for local or remote database
-func buildTargetUrl(password string, fsys afero.Fs) (target string, err error) {
-	if len(password) > 0 {
-		ref, err := utils.LoadProjectRef(fsys)
-		if err != nil {
-			return target, err
-		}
-		target = fmt.Sprintf(
-			"postgresql://%s@%s:6543/postgres",
-			url.UserPassword("postgres", password),
-			utils.GetSupabaseDbHost(ref),
-		)
-		fmt.Fprintln(os.Stderr, "Connecting to linked project...")
+func loadSchema(ctx context.Context, config pgconn.Config, options ...func(*pgx.ConnConfig)) (schema []string, err error) {
+	var conn *pgx.Conn
+	if config.Host == "localhost" && config.Port == uint16(utils.Config.Db.Port) {
+		conn, err = utils.ConnectLocalPostgres(ctx, config.Host, uint(config.Port), config.Database, options...)
 	} else {
-		if err := utils.AssertSupabaseDbIsRunning(); err != nil {
-			return target, err
-		}
-		target = "postgresql://postgres:postgres@" + utils.DbId + ":5432/postgres"
-		fmt.Fprintln(os.Stderr, "Connecting to local database...")
+		conn, err = utils.ConnectRemotePostgres(ctx, config, options...)
+	}
+	if err != nil {
+		return schema, err
 	}
-	return target, err
+	defer conn.Close(context.Background())
+	return LoadUserSchemas(ctx, conn)
 }
 
 func LoadUserSchemas(ctx context.Context, conn *pgx.Conn, exclude ...string) ([]string, error) {
@@ -255,14 +241,27 @@ func DiffSchemaMigra(ctx context.Context, source, target string, schema []string
 	// Passing in script string means command line args must be set manually, ie. "$@"
 	args := "set -- " + strings.Join(schema, " ") + ";"
 	cmd := []string{"/bin/sh", "-c", args + diffSchemaScript}
-	out, err := utils.DockerRunOnce(ctx, utils.MigraImage, env, cmd)
-	if err != nil {
-		return "", errors.New("error diffing schema: " + err.Error())
+	var out, stderr bytes.Buffer
+	if err := utils.DockerRunOnceWithConfig(
+		ctx,
+		container.Config{
+			Image: utils.MigraImage,
+			Env:   env,
+			Cmd:   cmd,
+		},
+		container.HostConfig{
+			NetworkMode: container.NetworkMode("host"),
+		},
+		"",
+		&out,
+		&stderr,
+	); err != nil {
+		return "", errors.New("error diffing schema:\n" + stderr.String())
 	}
-	return out, nil
+	return out.String(), nil
 }
 
-func DiffDatabase(ctx context.Context, schema []string, target string, w io.Writer, fsys afero.Fs, options ...func(*pgx.ConnConfig)) (string, error) {
+func DiffDatabase(ctx context.Context, schema []string, config pgconn.Config, w io.Writer, fsys afero.Fs, options ...func(*pgx.ConnConfig)) (string, error) {
 	fmt.Fprintln(w, "Creating shadow database...")
 	shadow, err := CreateShadowDatabase(ctx)
 	if err != nil {
@@ -273,6 +272,7 @@ func DiffDatabase(ctx context.Context, schema []string, target string, w io.Writ
 		return "", err
 	}
 	fmt.Fprintln(w, "Diffing schemas:", strings.Join(schema, ","))
-	source := "postgresql://postgres:postgres@" + shadow[:12] + ":5432/postgres"
+	source := fmt.Sprintf("postgresql://postgres:postgres@localhost:%d/postgres", utils.Config.Db.ShadowPort)
+	target := utils.ToPostgresURL(config)
 	return DiffSchemaMigra(ctx, source, target, schema)
 }
diff --git a/internal/db/dump/dump.go b/internal/db/dump/dump.go
index a6693fbe..adc007f8 100644
--- a/internal/db/dump/dump.go
+++ b/internal/db/dump/dump.go
@@ -8,6 +8,8 @@ import (
 	"os"
 	"strings"
 
+	"github.com/docker/docker/api/types/container"
+	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/utils"
 )
@@ -21,7 +23,7 @@ var (
 	dumpRoleScript string
 )
 
-func Run(ctx context.Context, path, username, password, database, host string, dataOnly, roleOnly bool, fsys afero.Fs) error {
+func Run(ctx context.Context, path string, config pgconn.Config, dataOnly, roleOnly bool, fsys afero.Fs) error {
 	// Initialise output stream
 	var outStream afero.File
 	if len(path) > 0 {
@@ -47,14 +49,27 @@ func Run(ctx context.Context, path, username, password, database, host string, d
 		script = dumpSchemaScript
 	}
 	// Run script in docker
-	if err := utils.DockerRunOnceWithStream(ctx, utils.Pg15Image, []string{
-		"PGHOST=" + host,
-		"PGUSER=" + username,
-		"PGPASSWORD=" + password,
-		"EXCLUDED_SCHEMAS=" + strings.Join(utils.InternalSchemas, "|"),
-		"RESERVED_ROLES=" + strings.Join(utils.ReservedRoles, "|"),
-		"DB_URL=" + database,
-	}, []string{"bash", "-c", script}, outStream, os.Stderr); err != nil {
+	if err := utils.DockerRunOnceWithConfig(
+		ctx,
+		container.Config{
+			Image: utils.Pg15Image,
+			Env: []string{
+				"PGHOST=" + config.Host,
+				"PGUSER=" + config.User,
+				"PGPASSWORD=" + config.Password,
+				"EXCLUDED_SCHEMAS=" + strings.Join(utils.InternalSchemas, "|"),
+				"RESERVED_ROLES=" + strings.Join(utils.ReservedRoles, "|"),
+				"DB_URL=" + config.Database,
+			},
+			Cmd: []string{"bash", "-c", script},
+		},
+		container.HostConfig{
+			NetworkMode: container.NetworkMode("host"),
+		},
+		"",
+		outStream,
+		os.Stderr,
+	); err != nil {
 		return errors.New("Error running pg_dump on remote database: " + err.Error())
 	}
 	if len(path) > 0 {
diff --git a/internal/db/push/push.go b/internal/db/push/push.go
index a6849b4d..03d033b1 100644
--- a/internal/db/push/push.go
+++ b/internal/db/push/push.go
@@ -20,11 +20,11 @@ var (
 	errConflict = errors.New("supabase_migrations.schema_migrations table conflicts with the contents of " + utils.Bold(utils.MigrationsDir) + ".")
 )
 
-func Run(ctx context.Context, dryRun bool, username, password, database, host string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
+func Run(ctx context.Context, dryRun bool, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
 	if dryRun {
 		fmt.Fprintln(os.Stderr, "DRY RUN: migrations will *not* be pushed to the database.")
 	}
-	conn, err := utils.ConnectRemotePostgres(ctx, username, password, database, host, options...)
+	conn, err := utils.ConnectRemotePostgres(ctx, config, options...)
 	if err != nil {
 		return err
 	}
diff --git a/internal/db/remote/changes/changes.go b/internal/db/remote/changes/changes.go
index f2195498..93b48911 100644
--- a/internal/db/remote/changes/changes.go
+++ b/internal/db/remote/changes/changes.go
@@ -2,9 +2,8 @@ package changes
 
 import (
 	"context"
-	"fmt"
-	"net/url"
 
+	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/diff"
 	"github.com/supabase/cli/internal/utils"
@@ -12,7 +11,7 @@ import (
 
 var output string
 
-func Run(ctx context.Context, schema []string, username, password, database string, fsys afero.Fs) error {
+func Run(ctx context.Context, schema []string, config pgconn.Config, fsys afero.Fs) error {
 	// Sanity checks.
 	{
 		if err := utils.AssertDockerIsRunning(ctx); err != nil {
@@ -24,7 +23,7 @@ func Run(ctx context.Context, schema []string, username, password, database stri
 	}
 
 	if err := utils.RunProgram(ctx, func(p utils.Program, ctx context.Context) error {
-		return run(p, ctx, schema, username, password, database, fsys)
+		return run(p, ctx, schema, config, fsys)
 	}); err != nil {
 		return err
 	}
@@ -32,17 +31,11 @@ func Run(ctx context.Context, schema []string, username, password, database stri
 	return diff.SaveDiff(output, "", fsys)
 }
 
-func run(p utils.Program, ctx context.Context, schema []string, username, password, database string, fsys afero.Fs) error {
-	projectRef, err := utils.LoadProjectRef(fsys)
-	if err != nil {
-		return err
-	}
-	host := utils.GetSupabaseDbHost(projectRef)
-
+func run(p utils.Program, ctx context.Context, schema []string, config pgconn.Config, fsys afero.Fs) (err error) {
 	// 1. Assert `supabase/migrations` and `schema_migrations` are in sync.
 	{
 		p.Send(utils.StatusMsg("Connecting to remote database..."))
-		conn, err := utils.ConnectRemotePostgres(ctx, username, password, database, host)
+		conn, err := utils.ConnectRemotePostgres(ctx, config)
 		if err != nil {
 			return err
 		}
@@ -57,7 +50,6 @@ func run(p utils.Program, ctx context.Context, schema []string, username, passwo
 
 	w := utils.StatusWriter{Program: p}
 	// 2. Diff remote db (source) & shadow db (target) and print it.
-	target := fmt.Sprintf("postgresql://%s@%s:6543/postgres", url.UserPassword(database, password), host)
-	output, err = diff.DiffDatabase(ctx, schema, target, w, fsys)
+	output, err = diff.DiffDatabase(ctx, schema, config, w, fsys)
 	return err
 }
diff --git a/internal/db/remote/commit/commit.go b/internal/db/remote/commit/commit.go
index ddb1b8b6..eb60bf9c 100644
--- a/internal/db/remote/commit/commit.go
+++ b/internal/db/remote/commit/commit.go
@@ -5,9 +5,10 @@ import (
 	_ "embed"
 	"errors"
 	"fmt"
-	"net/url"
 	"path/filepath"
 
+	"github.com/jackc/pgconn"
+	"github.com/jackc/pgerrcode"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/diff"
@@ -17,7 +18,7 @@ import (
 	"github.com/supabase/cli/internal/utils"
 )
 
-func Run(ctx context.Context, schema []string, username, password, database string, fsys afero.Fs) error {
+func Run(ctx context.Context, schema []string, config pgconn.Config, fsys afero.Fs) error {
 	// Sanity checks.
 	{
 		if err := utils.AssertDockerIsRunning(ctx); err != nil {
@@ -29,7 +30,7 @@ func Run(ctx context.Context, schema []string, username, password, database stri
 	}
 
 	if err := utils.RunProgram(ctx, func(p utils.Program, ctx context.Context) error {
-		return run(p, ctx, schema, username, password, database, fsys)
+		return run(p, ctx, schema, config, fsys)
 	}); err != nil {
 		return err
 	}
@@ -38,16 +39,10 @@ func Run(ctx context.Context, schema []string, username, password, database stri
 	return nil
 }
 
-func run(p utils.Program, ctx context.Context, schema []string, username, password, database string, fsys afero.Fs) error {
-	projectRef, err := utils.LoadProjectRef(fsys)
-	if err != nil {
-		return err
-	}
-	host := utils.GetSupabaseDbHost(projectRef)
-
+func run(p utils.Program, ctx context.Context, schema []string, config pgconn.Config, fsys afero.Fs) error {
 	// 1. Assert `supabase/migrations` and `schema_migrations` are in sync.
 	p.Send(utils.StatusMsg("Connecting to remote database..."))
-	conn, err := utils.ConnectRemotePostgres(ctx, username, password, database, host)
+	conn, err := utils.ConnectRemotePostgres(ctx, config)
 	if err != nil {
 		return err
 	}
@@ -64,7 +59,7 @@ func run(p utils.Program, ctx context.Context, schema []string, username, passwo
 		}
 	}
 	timestamp := utils.GetCurrentTimestamp()
-	if err := fetchRemote(p, ctx, schema, timestamp, username, password, database, host, fsys); err != nil {
+	if err := fetchRemote(p, ctx, schema, timestamp, config, fsys); err != nil {
 		return err
 	}
 
@@ -73,20 +68,19 @@ func run(p utils.Program, ctx context.Context, schema []string, username, passwo
 	return err
 }
 
-func fetchRemote(p utils.Program, ctx context.Context, schema []string, timestamp, username, password, database, host string, fsys afero.Fs) error {
+func fetchRemote(p utils.Program, ctx context.Context, schema []string, timestamp string, config pgconn.Config, fsys afero.Fs) error {
 	path := filepath.Join(utils.MigrationsDir, timestamp+"_remote_commit.sql")
 	// Special case if this is the first migration
 	if migrations, err := list.LoadLocalMigrations(fsys); err != nil {
 		return err
 	} else if len(migrations) == 0 {
 		p.Send(utils.StatusMsg("Committing initial migration on remote database..."))
-		return dump.Run(ctx, path, username, password, database, host, false, false, fsys)
+		return dump.Run(ctx, path, config, false, false, fsys)
 	}
 
 	w := utils.StatusWriter{Program: p}
 	// Diff remote db (source) & shadow db (target) and write it as a new migration.
-	target := fmt.Sprintf("postgresql://%s@%s:6543/postgres", url.UserPassword(database, password), host)
-	output, err := diff.DiffDatabase(ctx, schema, target, w, fsys)
+	output, err := diff.DiffDatabase(ctx, schema, config, w, fsys)
 	if err != nil {
 		return err
 	}
@@ -99,7 +93,13 @@ func fetchRemote(p utils.Program, ctx context.Context, schema []string, timestam
 func AssertRemoteInSync(ctx context.Context, conn *pgx.Conn, fsys afero.Fs) error {
 	remoteMigrations, err := list.LoadRemoteMigrations(ctx, conn)
 	if err != nil {
-		return err
+		var pgErr *pgconn.PgError
+		if !errors.As(err, &pgErr) || pgErr.Code != pgerrcode.UndefinedTable {
+			return err
+		}
+		if err := repair.CreateMigrationTable(ctx, conn); err != nil {
+			return err
+		}
 	}
 	localMigrations, err := list.LoadLocalMigrations(fsys)
 	if err != nil {
diff --git a/internal/link/link.go b/internal/link/link.go
index cc29cb6f..9a7e0086 100644
--- a/internal/link/link.go
+++ b/internal/link/link.go
@@ -31,7 +31,7 @@ func PreRun(projectRef string, fsys afero.Fs) error {
 	return utils.LoadConfigFS(fsys)
 }
 
-func Run(ctx context.Context, projectRef, username, password, database string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
+func Run(ctx context.Context, projectRef, password string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
 	// 1. Check postgrest config
 	if err := linkPostgrest(ctx, projectRef); err != nil {
 		return err
@@ -39,8 +39,13 @@ func Run(ctx context.Context, projectRef, username, password, database string, f
 
 	// 2. Check database connection
 	if len(password) > 0 {
-		host := utils.GetSupabaseDbHost(projectRef)
-		if err := linkDatabase(ctx, username, password, database, host, options...); err != nil {
+		if err := linkDatabase(ctx, pgconn.Config{
+			Host:     utils.GetSupabaseDbHost(projectRef),
+			Port:     6543,
+			User:     "postgres",
+			Password: password,
+			Database: "postgres",
+		}, options...); err != nil {
 			return err
 		}
 		// Save database password
@@ -119,8 +124,8 @@ func sliceEqual(a, b []string) bool {
 	return true
 }
 
-func linkDatabase(ctx context.Context, username, password, database, host string, options ...func(*pgx.ConnConfig)) error {
-	conn, err := utils.ConnectRemotePostgres(ctx, username, password, database, host, options...)
+func linkDatabase(ctx context.Context, config pgconn.Config, options ...func(*pgx.ConnConfig)) error {
+	conn, err := utils.ConnectRemotePostgres(ctx, config, options...)
 	if err != nil {
 		return err
 	}
diff --git a/internal/migration/list/list.go b/internal/migration/list/list.go
index 9b58f0ec..28f221cc 100644
--- a/internal/migration/list/list.go
+++ b/internal/migration/list/list.go
@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"github.com/charmbracelet/glamour"
+	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/utils"
@@ -19,8 +20,8 @@ const LIST_MIGRATION_VERSION = "SELECT version FROM supabase_migrations.schema_m
 
 var initSchemaPattern = regexp.MustCompile(`([0-9]{14})_init\.sql`)
 
-func Run(ctx context.Context, username, password, database, host string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
-	remoteVersions, err := loadRemoteVersions(ctx, username, password, database, host, options...)
+func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
+	remoteVersions, err := loadRemoteVersions(ctx, config, options...)
 	if err != nil {
 		return err
 	}
@@ -31,8 +32,8 @@ func Run(ctx context.Context, username, password, database, host string, fsys af
 	return RenderTable(remoteVersions, localVersions)
 }
 
-func loadRemoteVersions(ctx context.Context, username, password, database, host string, options ...func(*pgx.ConnConfig)) ([]string, error) {
-	conn, err := utils.ConnectRemotePostgres(ctx, username, password, database, host, options...)
+func loadRemoteVersions(ctx context.Context, config pgconn.Config, options ...func(*pgx.ConnConfig)) ([]string, error) {
+	conn, err := utils.ConnectRemotePostgres(ctx, config, options...)
 	if err != nil {
 		return nil, err
 	}
diff --git a/internal/migration/repair/repair.go b/internal/migration/repair/repair.go
index 52a34b2b..397af5a2 100644
--- a/internal/migration/repair/repair.go
+++ b/internal/migration/repair/repair.go
@@ -21,11 +21,10 @@ const (
 	CREATE_VERSION_TABLE     = "CREATE TABLE IF NOT EXISTS supabase_migrations.schema_migrations (version text NOT NULL PRIMARY KEY)"
 	INSERT_MIGRATION_VERSION = "INSERT INTO supabase_migrations.schema_migrations(version) VALUES($1)"
 	DELETE_MIGRATION_VERSION = "DELETE FROM supabase_migrations.schema_migrations WHERE version = $1"
-	CREATE_MIGRATION_TABLE   = CREATE_VERSION_SCHEMA + ";" + CREATE_VERSION_TABLE
 )
 
-func Run(ctx context.Context, username, password, database, host, version, status string, options ...func(*pgx.ConnConfig)) error {
-	conn, err := utils.ConnectRemotePostgres(ctx, username, password, database, host, options...)
+func Run(ctx context.Context, config pgconn.Config, version, status string, options ...func(*pgx.ConnConfig)) error {
+	conn, err := utils.ConnectRemotePostgres(ctx, config, options...)
 	if err != nil {
 		return err
 	}
@@ -48,6 +47,14 @@ func Run(ctx context.Context, username, password, database, host, version, statu
 	return nil
 }
 
+func CreateMigrationTable(ctx context.Context, conn *pgx.Conn) error {
+	batch := pgconn.Batch{}
+	batch.ExecParams(CREATE_VERSION_SCHEMA, nil, nil, nil, nil)
+	batch.ExecParams(CREATE_VERSION_TABLE, nil, nil, nil, nil)
+	_, err := conn.PgConn().ExecBatch(ctx, &batch).ReadAll()
+	return err
+}
+
 func InsertVersionSQL(batch *pgconn.Batch, version string) {
 	updateVersionSQL(batch, INSERT_MIGRATION_VERSION, version)
 }
diff --git a/internal/utils/connect.go b/internal/utils/connect.go
index 995b726c..93376f21 100644
--- a/internal/utils/connect.go
+++ b/internal/utils/connect.go
@@ -20,17 +20,23 @@ func isDialError(err error) bool {
 	return ok && opErr.Op == "dial"
 }
 
-// Connnect to remote Postgres with optimised settings. The caller is responsible for closing the connection returned.
-func ConnectRemotePostgres(ctx context.Context, username, password, database, host string, options ...func(*pgx.ConnConfig)) (*pgx.Conn, error) {
-	makeURL := func(port uint16) string {
-		return fmt.Sprintf(
-			"postgresql://%s@%s:%d/%s?connect_timeout=10",
-			url.UserPassword(username, password),
-			host,
-			port,
-			url.PathEscape(database),
-		)
+func ToPostgresURL(config pgconn.Config) string {
+	timeoutSecond := int64(config.ConnectTimeout.Seconds())
+	if timeoutSecond == 0 {
+		timeoutSecond = 10
 	}
+	return fmt.Sprintf(
+		"postgresql://%s@%s:%d/%s?connect_timeout=%d",
+		url.UserPassword(config.User, config.Password),
+		config.Host,
+		config.Port,
+		url.PathEscape(config.Database),
+		timeoutSecond,
+	)
+}
+
+// Connnect to remote Postgres with optimised settings. The caller is responsible for closing the connection returned.
+func ConnectRemotePostgres(ctx context.Context, config pgconn.Config, options ...func(*pgx.ConnConfig)) (*pgx.Conn, error) {
 	// Simple protocol is preferred over pgx default Parse -> Bind flow because
 	//   1. Using a single command for each query reduces RTT over an Internet connection.
 	//   2. Performance gains from using the alternate binary protocol is negligible because
@@ -45,13 +51,14 @@ func ConnectRemotePostgres(ctx context.Context, username, password, database, ho
 		}
 	})
 	// Use port 6543 for connection pooling
-	conn, err := ConnectByUrl(ctx, makeURL(6543), opts...)
-	if !pgconn.Timeout(err) && !isDialError(err) {
+	conn, err := ConnectByUrl(ctx, ToPostgresURL(config), opts...)
+	if !pgconn.Timeout(err) && !isDialError(err) && !ProjectHostPattern.MatchString(config.Host) {
 		return conn, err
 	}
 	// Fallback to 5432 when pgbouncer is unavailable
-	fmt.Fprintln(os.Stderr, "Retrying...", host+":5432")
-	return ConnectByUrl(ctx, makeURL(5432), opts...)
+	config.Port = 5432
+	fmt.Fprintln(os.Stderr, "Retrying...", config.Host, config.Port)
+	return ConnectByUrl(ctx, ToPostgresURL(config), opts...)
 }
 
 // Connnect to local Postgres with optimised settings. The caller is responsible for closing the connection returned.
diff --git a/internal/utils/misc.go b/internal/utils/misc.go
index 5c512a2a..4462f981 100644
--- a/internal/utils/misc.go
+++ b/internal/utils/misc.go
@@ -103,7 +103,7 @@ var (
 
 	AccessTokenPattern = regexp.MustCompile(`^sbp_[a-f0-9]{40}$`)
 	ProjectRefPattern  = regexp.MustCompile(`^[a-z]{20}$`)
-	PostgresUrlPattern = regexp.MustCompile(`^postgres(?:ql)?:\/\/postgres:(.*)@(.+)\/postgres$`)
+	ProjectHostPattern = regexp.MustCompile(`^(db\.)[a-z]{20}\.supabase\.(co|red)$`)
 	MigrateFilePattern = regexp.MustCompile(`^([0-9]+)_.*\.sql$`)
 	BranchNamePattern  = regexp.MustCompile(`[[:word:]-]+`)
 	FuncSlugPattern    = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_-]*$`)


Test patch:
diff --git a/internal/db/diff/migra_test.go b/internal/db/diff/migra_test.go
index 5fb431d2..fe9cc4eb 100644
--- a/internal/db/diff/migra_test.go
+++ b/internal/db/diff/migra_test.go
@@ -10,7 +10,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/docker/docker/api/types"
+	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
@@ -22,6 +22,14 @@ import (
 	"gopkg.in/h2non/gock.v1"
 )
 
+var dbConfig = pgconn.Config{
+	Host:     "localhost",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
 func TestRunMigra(t *testing.T) {
 	t.Run("runs migra diff", func(t *testing.T) {
 		utils.GlobalsSql = "create schema public"
@@ -49,7 +57,7 @@ func TestRunMigra(t *testing.T) {
 			Query(utils.InitialSchemaPg15Sql).
 			Reply("CREATE SCHEMA")
 		// Run test
-		err := RunMigra(context.Background(), []string{"public"}, "file", "password", fsys, conn.Intercept)
+		err := RunMigra(context.Background(), []string{"public"}, "file", dbConfig, fsys, conn.Intercept)
 		// Check error
 		assert.NoError(t, err)
 		assert.Empty(t, apitest.ListUnmatchedRequests())
@@ -67,21 +75,11 @@ func TestRunMigra(t *testing.T) {
 		// Setup in-memory fs
 		fsys := afero.NewMemMapFs()
 		// Run test
-		err := RunMigra(context.Background(), []string{"public"}, "", "", fsys)
+		err := RunMigra(context.Background(), []string{"public"}, "", pgconn.Config{}, fsys)
 		// Check error
 		assert.ErrorIs(t, err, os.ErrNotExist)
 	})
 
-	t.Run("throws error on missing project", func(t *testing.T) {
-		// Setup in-memory fs
-		fsys := afero.NewMemMapFs()
-		require.NoError(t, utils.WriteConfig(fsys, false))
-		// Run test
-		err := RunMigra(context.Background(), []string{"public"}, "", "password", fsys)
-		// Check error
-		assert.ErrorContains(t, err, "Cannot find project ref. Have you run supabase link?")
-	})
-
 	t.Run("throws error on missing database", func(t *testing.T) {
 		// Setup in-memory fs
 		fsys := afero.NewMemMapFs()
@@ -93,7 +91,7 @@ func TestRunMigra(t *testing.T) {
 			Get("/v" + utils.Docker.ClientVersion() + "/containers/supabase_db_").
 			ReplyError(errors.New("network error"))
 		// Run test
-		err := RunMigra(context.Background(), []string{"public"}, "", "", fsys)
+		err := RunMigra(context.Background(), []string{"public"}, "", pgconn.Config{}, fsys)
 		// Check error
 		assert.ErrorContains(t, err, "supabase start is not running.")
 		assert.Empty(t, apitest.ListUnmatchedRequests())
@@ -111,7 +109,7 @@ func TestRunMigra(t *testing.T) {
 		conn.Query(`SELECT schema_name FROM information_schema.schemata WHERE NOT schema_name LIKE ANY('{pgbouncer,realtime,"\\_realtime","supabase\\_functions","supabase\\_migrations","information\\_schema","pg\\_%",cron,graphql,"graphql\\_public",net,pgsodium,"pgsodium\\_masks",pgtle,repack,tiger,"tiger\\_data","timescaledb\\_%","\\_timescaledb\\_%",topology,vault}') ORDER BY schema_name`).
 			ReplyError(pgerrcode.DuplicateTable, `relation "test" already exists`)
 		// Run test
-		err := RunMigra(context.Background(), []string{}, "", "password", fsys, conn.Intercept)
+		err := RunMigra(context.Background(), []string{}, "", dbConfig, fsys, conn.Intercept)
 		// Check error
 		assert.ErrorContains(t, err, `ERROR: relation "test" already exists (SQLSTATE 42P07)`)
 	})
@@ -129,46 +127,13 @@ func TestRunMigra(t *testing.T) {
 			Get("/v" + utils.Docker.ClientVersion() + "/images/" + utils.GetRegistryImageUrl(utils.Pg15Image) + "/json").
 			ReplyError(errors.New("network error"))
 		// Run test
-		err := RunMigra(context.Background(), []string{"public"}, "file", "password", fsys)
+		err := RunMigra(context.Background(), []string{"public"}, "file", dbConfig, fsys)
 		// Check error
 		assert.ErrorContains(t, err, "network error")
 		assert.Empty(t, apitest.ListUnmatchedRequests())
 	})
 }
 
-func TestBuildTarget(t *testing.T) {
-	t.Run("builds remote url", func(t *testing.T) {
-		// Setup in-memory fs
-		fsys := afero.NewMemMapFs()
-		project := apitest.RandomProjectRef()
-		require.NoError(t, afero.WriteFile(fsys, utils.ProjectRefPath, []byte(project), 0644))
-		// Run test
-		url, err := buildTargetUrl("password", fsys)
-		// Check output
-		assert.NoError(t, err)
-		assert.Equal(t, "postgresql://postgres:password@db."+project+".supabase.co:6543/postgres", url)
-	})
-
-	t.Run("builds local url", func(t *testing.T) {
-		utils.DbId = "postgres"
-		// Setup in-memory fs
-		fsys := afero.NewMemMapFs()
-		// Setup mock docker
-		require.NoError(t, apitest.MockDocker(utils.Docker))
-		defer gock.OffAll()
-		gock.New(utils.Docker.DaemonHost()).
-			Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
-			Reply(http.StatusOK).
-			JSON(types.ContainerJSON{})
-		// Run test
-		url, err := buildTargetUrl("", fsys)
-		// Check output
-		assert.NoError(t, err)
-		assert.Equal(t, "postgresql://postgres:postgres@postgres:5432/postgres", url)
-		assert.Empty(t, apitest.ListUnmatchedRequests())
-	})
-}
-
 func TestApplyMigrations(t *testing.T) {
 	const postgresUrl = "postgresql://postgres:password@localhost:5432/postgres"
 
@@ -324,7 +289,7 @@ func TestDiffDatabase(t *testing.T) {
 			Get("/v" + utils.Docker.ClientVersion() + "/images/" + utils.GetRegistryImageUrl(utils.Pg15Image) + "/json").
 			ReplyError(errors.New("network error"))
 		// Run test
-		diff, err := DiffDatabase(context.Background(), []string{"public"}, "", io.Discard, fsys)
+		diff, err := DiffDatabase(context.Background(), []string{"public"}, dbConfig, io.Discard, fsys)
 		// Check error
 		assert.Empty(t, diff)
 		assert.ErrorContains(t, err, "network error")
@@ -347,7 +312,7 @@ func TestDiffDatabase(t *testing.T) {
 		conn.Query(utils.GlobalsSql).
 			ReplyError(pgerrcode.DuplicateSchema, `schema "public" already exists`)
 		// Run test
-		diff, err := DiffDatabase(context.Background(), []string{"public"}, "", io.Discard, fsys, conn.Intercept)
+		diff, err := DiffDatabase(context.Background(), []string{"public"}, dbConfig, io.Discard, fsys, conn.Intercept)
 		// Check error
 		assert.Empty(t, diff)
 		assert.ErrorContains(t, err, `ERROR: schema "public" already exists (SQLSTATE 42P06)
@@ -380,7 +345,7 @@ At statement 0: create schema public`)
 			Query(utils.InitialSchemaSql).
 			Reply("CREATE SCHEMA")
 		// Run test
-		diff, err := DiffDatabase(context.Background(), []string{"public"}, "", io.Discard, fsys, conn.Intercept)
+		diff, err := DiffDatabase(context.Background(), []string{"public"}, dbConfig, io.Discard, fsys, conn.Intercept)
 		// Check error
 		assert.Empty(t, diff)
 		assert.ErrorContains(t, err, "error diffing schema")
@@ -396,7 +361,7 @@ func TestUserSchema(t *testing.T) {
 		Reply("SELECT 1", []interface{}{"test"})
 	// Connect to mock
 	ctx := context.Background()
-	mock, err := utils.ConnectRemotePostgres(ctx, "admin", "pass", "postgres", "localhost", conn.Intercept)
+	mock, err := utils.ConnectRemotePostgres(ctx, dbConfig, conn.Intercept)
 	require.NoError(t, err)
 	defer mock.Close(ctx)
 	// Run test
diff --git a/internal/db/dump/dump_test.go b/internal/db/dump/dump_test.go
index 40691456..65c62190 100644
--- a/internal/db/dump/dump_test.go
+++ b/internal/db/dump/dump_test.go
@@ -5,6 +5,7 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -13,12 +14,13 @@ import (
 	"gopkg.in/h2non/gock.v1"
 )
 
-const (
-	username = "admin"
-	password = "password"
-	database = "postgres"
-	host     = "localhost"
-)
+var dbConfig = pgconn.Config{
+	Host:     "localhost",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
 
 func TestPullCommand(t *testing.T) {
 	imageUrl := utils.GetRegistryImageUrl(utils.Pg15Image)
@@ -33,7 +35,7 @@ func TestPullCommand(t *testing.T) {
 		apitest.MockDockerStart(utils.Docker, imageUrl, containerId)
 		require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerId, "hello world"))
 		// Run test
-		err := Run(context.Background(), "schema.sql", username, password, database, host, false, false, fsys)
+		err := Run(context.Background(), "schema.sql", dbConfig, false, false, fsys)
 		// Check error
 		assert.NoError(t, err)
 		assert.Empty(t, apitest.ListUnmatchedRequests())
@@ -52,7 +54,7 @@ func TestPullCommand(t *testing.T) {
 		apitest.MockDockerStart(utils.Docker, imageUrl, containerId)
 		require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerId, "hello world"))
 		// Run test
-		err := Run(context.Background(), "", username, password, database, host, false, false, fsys)
+		err := Run(context.Background(), "", dbConfig, false, false, fsys)
 		// Check error
 		assert.NoError(t, err)
 		assert.Empty(t, apitest.ListUnmatchedRequests())
@@ -68,7 +70,7 @@ func TestPullCommand(t *testing.T) {
 			Get("/v" + utils.Docker.ClientVersion() + "/images").
 			Reply(http.StatusServiceUnavailable)
 		// Run test
-		err := Run(context.Background(), "", username, password, database, host, false, false, fsys)
+		err := Run(context.Background(), "", dbConfig, false, false, fsys)
 		// Check error
 		assert.ErrorContains(t, err, "Error running pg_dump on remote database: request returned Service Unavailable")
 		assert.Empty(t, apitest.ListUnmatchedRequests())
@@ -83,7 +85,7 @@ func TestPullCommand(t *testing.T) {
 		apitest.MockDockerStart(utils.Docker, imageUrl, containerId)
 		require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerId, "hello world"))
 		// Run test
-		err := Run(context.Background(), "schema.sql", username, password, database, host, false, false, fsys)
+		err := Run(context.Background(), "schema.sql", dbConfig, false, false, fsys)
 		// Check error
 		assert.ErrorContains(t, err, "operation not permitted")
 		assert.Empty(t, apitest.ListUnmatchedRequests())
diff --git a/internal/db/push/push_test.go b/internal/db/push/push_test.go
index 1c727ef7..f0cca532 100644
--- a/internal/db/push/push_test.go
+++ b/internal/db/push/push_test.go
@@ -6,6 +6,7 @@ import (
 	"path/filepath"
 	"testing"
 
+	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
@@ -17,12 +18,13 @@ import (
 	"github.com/supabase/cli/internal/utils/parser"
 )
 
-const (
-	user     = "admin"
-	pass     = "password"
-	database = "postgres"
-	host     = "localhost"
-)
+var dbConfig = pgconn.Config{
+	Host:     "localhost",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
 
 func TestMigrationPush(t *testing.T) {
 	t.Run("dry run", func(t *testing.T) {
@@ -36,7 +38,7 @@ func TestMigrationPush(t *testing.T) {
 		conn.Query(list.LIST_MIGRATION_VERSION).
 			Reply("SELECT 0")
 		// Run test
-		err := Run(context.Background(), true, user, pass, database, host, fsys, conn.Intercept)
+		err := Run(context.Background(), true, dbConfig, fsys, conn.Intercept)
 		// Check error
 		assert.NoError(t, err)
 	})
@@ -50,7 +52,7 @@ func TestMigrationPush(t *testing.T) {
 		conn.Query(list.LIST_MIGRATION_VERSION).
 			Reply("SELECT 0")
 		// Run test
-		err := Run(context.Background(), false, user, pass, database, host, fsys, conn.Intercept)
+		err := Run(context.Background(), false, dbConfig, fsys, conn.Intercept)
 		// Check error
 		assert.NoError(t, err)
 	})
@@ -59,9 +61,9 @@ func TestMigrationPush(t *testing.T) {
 		// Setup in-memory fs
 		fsys := afero.NewMemMapFs()
 		// Run test
-		err := Run(context.Background(), false, user, pass, database, "0", fsys)
+		err := Run(context.Background(), false, pgconn.Config{}, fsys)
 		// Check error
-		assert.ErrorContains(t, err, "connect: connection refused")
+		assert.ErrorContains(t, err, "invalid port (outside range)")
 	})
 
 	t.Run("throws error on remote load failure", func(t *testing.T) {
@@ -73,7 +75,7 @@ func TestMigrationPush(t *testing.T) {
 		conn.Query(list.LIST_MIGRATION_VERSION).
 			ReplyError(pgerrcode.UndefinedTable, `relation "supabase_migrations.schema_migrations" does not exist`)
 		// Run test
-		err := Run(context.Background(), false, user, pass, database, host, fsys, conn.Intercept)
+		err := Run(context.Background(), false, dbConfig, fsys, conn.Intercept)
 		// Check error
 		assert.ErrorContains(t, err, `ERROR: relation "supabase_migrations.schema_migrations" does not exist (SQLSTATE 42P01)`)
 	})
@@ -91,7 +93,7 @@ func TestMigrationPush(t *testing.T) {
 			Query(repair.INSERT_MIGRATION_VERSION, "0").
 			ReplyError(pgerrcode.NotNullViolation, `null value in column "version" of relation "schema_migrations"`)
 		// Run test
-		err := Run(context.Background(), false, user, pass, database, host, fsys, conn.Intercept)
+		err := Run(context.Background(), false, dbConfig, fsys, conn.Intercept)
 		// Check error
 		assert.ErrorContains(t, err, `ERROR: null value in column "version" of relation "schema_migrations" (SQLSTATE 23502)`)
 		assert.ErrorContains(t, err, "At statement 0: "+repair.INSERT_MIGRATION_VERSION)
@@ -119,7 +121,7 @@ func TestPendingMigrations(t *testing.T) {
 			Reply("SELECT 2", []interface{}{"20221201000000"}, []interface{}{"20221201000001"})
 		// Connect to mock
 		ctx := context.Background()
-		mock, err := utils.ConnectRemotePostgres(ctx, user, pass, database, host, conn.Intercept)
+		mock, err := utils.ConnectRemotePostgres(ctx, dbConfig, conn.Intercept)
 		require.NoError(t, err)
 		defer mock.Close(ctx)
 		// Run test
@@ -139,7 +141,7 @@ func TestPendingMigrations(t *testing.T) {
 			Reply("SELECT 0")
 		// Connect to mock
 		ctx := context.Background()
-		mock, err := utils.ConnectRemotePostgres(ctx, user, pass, database, host, conn.Intercept)
+		mock, err := utils.ConnectRemotePostgres(ctx, dbConfig, conn.Intercept)
 		require.NoError(t, err)
 		defer mock.Close(ctx)
 		// Run test
@@ -158,7 +160,7 @@ func TestPendingMigrations(t *testing.T) {
 			Reply("SELECT 1", []interface{}{"0"})
 		// Connect to mock
 		ctx := context.Background()
-		mock, err := utils.ConnectRemotePostgres(ctx, user, pass, database, host, conn.Intercept)
+		mock, err := utils.ConnectRemotePostgres(ctx, dbConfig, conn.Intercept)
 		require.NoError(t, err)
 		defer mock.Close(ctx)
 		// Run test
@@ -179,7 +181,7 @@ func TestPendingMigrations(t *testing.T) {
 			Reply("SELECT 1", []interface{}{"0"})
 		// Connect to mock
 		ctx := context.Background()
-		mock, err := utils.ConnectRemotePostgres(ctx, user, pass, database, host, conn.Intercept)
+		mock, err := utils.ConnectRemotePostgres(ctx, dbConfig, conn.Intercept)
 		require.NoError(t, err)
 		defer mock.Close(ctx)
 		// Run test
@@ -205,7 +207,7 @@ func TestPushLocal(t *testing.T) {
 			Reply("INSERT 0 1")
 		// Connect to mock
 		ctx := context.Background()
-		mock, err := utils.ConnectRemotePostgres(ctx, user, pass, database, host, conn.Intercept)
+		mock, err := utils.ConnectRemotePostgres(ctx, dbConfig, conn.Intercept)
 		require.NoError(t, err)
 		defer mock.Close(ctx)
 		// Run test
@@ -247,7 +249,7 @@ func TestPushLocal(t *testing.T) {
 			ReplyError(pgerrcode.NotNullViolation, `null value in column "version" of relation "schema_migrations"`)
 		// Connect to mock
 		ctx := context.Background()
-		mock, err := utils.ConnectRemotePostgres(ctx, user, pass, database, host, conn.Intercept)
+		mock, err := utils.ConnectRemotePostgres(ctx, dbConfig, conn.Intercept)
 		require.NoError(t, err)
 		defer mock.Close(ctx)
 		// Run test
diff --git a/internal/link/link_test.go b/internal/link/link_test.go
index f7e80b93..c07348f6 100644
--- a/internal/link/link_test.go
+++ b/internal/link/link_test.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
@@ -21,12 +22,13 @@ import (
 	"gopkg.in/h2non/gock.v1"
 )
 
-const (
-	username = "admin"
-	password = "password"
-	database = "postgres"
-	host     = "localhost"
-)
+var dbConfig = pgconn.Config{
+	Host:     "localhost",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
 
 func TestPreRun(t *testing.T) {
 	// Reset global variable
@@ -125,7 +127,7 @@ func TestLinkCommand(t *testing.T) {
 			Reply(200).
 			JSON(api.PostgrestConfigResponse{})
 		// Run test
-		err := Run(context.Background(), project, username, password, database, fsys, conn.Intercept)
+		err := Run(context.Background(), project, dbConfig.Password, fsys, conn.Intercept)
 		// Check error
 		assert.NoError(t, err)
 		assert.Empty(t, apitest.ListUnmatchedRequests())
@@ -144,7 +146,7 @@ func TestLinkCommand(t *testing.T) {
 			Get("/v1/projects/" + project + "/postgrest").
 			ReplyError(errors.New("network error"))
 		// Run test
-		err := Run(context.Background(), project, username, password, database, fsys)
+		err := Run(context.Background(), project, dbConfig.Password, fsys)
 		// Check error
 		assert.ErrorContains(t, err, "network error")
 	})
@@ -159,7 +161,7 @@ func TestLinkCommand(t *testing.T) {
 			Reply(200).
 			JSON(api.PostgrestConfigResponse{})
 		// Run test
-		err := Run(context.Background(), project, username, password, database, fsys, func(cc *pgx.ConnConfig) {
+		err := Run(context.Background(), project, dbConfig.Password, fsys, func(cc *pgx.ConnConfig) {
 			cc.LookupFunc = func(ctx context.Context, host string) (addrs []string, err error) {
 				return nil, errors.New("hostname resolving error")
 			}
@@ -178,7 +180,7 @@ func TestLinkCommand(t *testing.T) {
 			Reply(200).
 			JSON(api.PostgrestConfigResponse{})
 		// Run test
-		err := Run(context.Background(), project, username, "", database, fsys)
+		err := Run(context.Background(), project, "", fsys)
 		// Check error
 		assert.ErrorContains(t, err, "operation not permitted")
 		assert.Empty(t, apitest.ListUnmatchedRequests())
@@ -283,9 +285,9 @@ func TestLinkDatabase(t *testing.T) {
 
 	t.Run("throws error on connect failure", func(t *testing.T) {
 		// Run test
-		err := linkDatabase(context.Background(), username, password, database, "0")
+		err := linkDatabase(context.Background(), pgconn.Config{})
 		// Check error
-		assert.ErrorContains(t, err, "connect: connection refused")
+		assert.ErrorContains(t, err, "invalid port (outside range)")
 		assert.Empty(t, updatedConfig)
 	})
 
@@ -300,7 +302,7 @@ func TestLinkDatabase(t *testing.T) {
 			Query(repair.CREATE_VERSION_TABLE).
 			Reply("CREATE TABLE")
 		// Run test
-		err := linkDatabase(context.Background(), username, password, database, host, conn.Intercept)
+		err := linkDatabase(context.Background(), dbConfig, conn.Intercept)
 		// Check error
 		assert.NoError(t, err)
 		assert.Empty(t, updatedConfig)
@@ -319,7 +321,7 @@ func TestLinkDatabase(t *testing.T) {
 			Query(repair.CREATE_VERSION_TABLE).
 			Reply("CREATE TABLE")
 		// Run test
-		err := linkDatabase(context.Background(), username, password, database, host, conn.Intercept)
+		err := linkDatabase(context.Background(), dbConfig, conn.Intercept)
 		// Check error
 		assert.NoError(t, err)
 		utils.Config.Db.MajorVersion = 15
@@ -338,7 +340,7 @@ func TestLinkDatabase(t *testing.T) {
 			Query(repair.CREATE_VERSION_TABLE).
 			ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation supabase_migrations")
 		// Run test
-		err := linkDatabase(context.Background(), username, password, database, host, conn.Intercept)
+		err := linkDatabase(context.Background(), dbConfig, conn.Intercept)
 		// Check error
 		assert.ErrorContains(t, err, "ERROR: permission denied for relation supabase_migrations (SQLSTATE 42501)")
 	})
diff --git a/internal/migration/list/list_test.go b/internal/migration/list/list_test.go
index acc8de25..17499d7b 100644
--- a/internal/migration/list/list_test.go
+++ b/internal/migration/list/list_test.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
@@ -15,12 +16,13 @@ import (
 	"github.com/supabase/cli/internal/utils"
 )
 
-const (
-	user = "admin"
-	pass = "password"
-	host = "localhost"
-	db   = "postgres"
-)
+var dbConfig = pgconn.Config{
+	Host:     "localhost",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
 
 func TestMigrationList(t *testing.T) {
 	t.Run("lists remote migrations", func(t *testing.T) {
@@ -32,7 +34,7 @@ func TestMigrationList(t *testing.T) {
 		conn.Query(LIST_MIGRATION_VERSION).
 			Reply("SELECT 0")
 		// Run test
-		err := Run(context.Background(), user, pass, db, host, fsys, conn.Intercept)
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
 		// Check error
 		assert.NoError(t, err)
 	})
@@ -41,9 +43,9 @@ func TestMigrationList(t *testing.T) {
 		// Setup in-memory fs
 		fsys := afero.NewMemMapFs()
 		// Run test
-		err := Run(context.Background(), user, pass, db, "0", fsys)
+		err := Run(context.Background(), pgconn.Config{}, fsys)
 		// Check error
-		assert.ErrorContains(t, err, "connect: connection refused")
+		assert.ErrorContains(t, err, "invalid port (outside range)")
 	})
 
 	t.Run("throws error on local failure", func(t *testing.T) {
@@ -55,7 +57,7 @@ func TestMigrationList(t *testing.T) {
 		conn.Query(LIST_MIGRATION_VERSION).
 			Reply("SELECT 0")
 		// Run test
-		err := Run(context.Background(), user, pass, db, host, afero.NewReadOnlyFs(fsys), conn.Intercept)
+		err := Run(context.Background(), dbConfig, afero.NewReadOnlyFs(fsys), conn.Intercept)
 		// Check error
 		assert.ErrorContains(t, err, "operation not permitted")
 	})
@@ -69,7 +71,7 @@ func TestRemoteMigrations(t *testing.T) {
 		conn.Query(LIST_MIGRATION_VERSION).
 			Reply("SELECT 1", []interface{}{"20220727064247"})
 		// Run test
-		versions, err := loadRemoteVersions(context.Background(), user, pass, db, host, conn.Intercept)
+		versions, err := loadRemoteVersions(context.Background(), dbConfig, conn.Intercept)
 		// Check error
 		assert.NoError(t, err)
 		assert.ElementsMatch(t, []string{"20220727064247"}, versions)
@@ -77,9 +79,9 @@ func TestRemoteMigrations(t *testing.T) {
 
 	t.Run("throws error on connect failure", func(t *testing.T) {
 		// Run test
-		_, err := loadRemoteVersions(context.Background(), user, pass, db, "0")
+		_, err := loadRemoteVersions(context.Background(), pgconn.Config{})
 		// Check error
-		assert.ErrorContains(t, err, "connect: connection refused")
+		assert.ErrorContains(t, err, "invalid port (outside range)")
 	})
 
 	t.Run("throws error on missing schema", func(t *testing.T) {
@@ -89,7 +91,7 @@ func TestRemoteMigrations(t *testing.T) {
 		conn.Query(LIST_MIGRATION_VERSION).
 			ReplyError(pgerrcode.UndefinedTable, "relation \"supabase_migrations.schema_migrations\" does not exist")
 		// Run test
-		_, err := loadRemoteVersions(context.Background(), user, pass, db, host, conn.Intercept)
+		_, err := loadRemoteVersions(context.Background(), dbConfig, conn.Intercept)
 		// Check error
 		assert.ErrorContains(t, err, `ERROR: relation "supabase_migrations.schema_migrations" does not exist (SQLSTATE 42P01)`)
 	})
@@ -101,7 +103,7 @@ func TestRemoteMigrations(t *testing.T) {
 		conn.Query(LIST_MIGRATION_VERSION).
 			Reply("SELECT 1", nil)
 		// Run test
-		_, err := loadRemoteVersions(context.Background(), user, pass, db, host, conn.Intercept)
+		_, err := loadRemoteVersions(context.Background(), dbConfig, conn.Intercept)
 		// Check error
 		assert.ErrorContains(t, err, "number of field descriptions must equal number of destinations, got 0 and 1")
 	})
diff --git a/internal/migration/repair/repair_test.go b/internal/migration/repair/repair_test.go
index 56759756..5c8849c3 100644
--- a/internal/migration/repair/repair_test.go
+++ b/internal/migration/repair/repair_test.go
@@ -4,17 +4,19 @@ import (
 	"context"
 	"testing"
 
+	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/pgtest"
 )
 
-const (
-	user     = "admin"
-	pass     = "password"
-	database = "postgres"
-	host     = "localhost"
-)
+var dbConfig = pgconn.Config{
+	Host:     "localhost",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
 
 func TestRepairCommand(t *testing.T) {
 	t.Run("applies new version", func(t *testing.T) {
@@ -28,7 +30,7 @@ func TestRepairCommand(t *testing.T) {
 			Query(INSERT_MIGRATION_VERSION, "0").
 			Reply("INSERT 0 1")
 		// Run test
-		err := Run(context.Background(), user, pass, database, host, "0", Applied, conn.Intercept)
+		err := Run(context.Background(), dbConfig, "0", Applied, conn.Intercept)
 		// Check error
 		assert.NoError(t, err)
 	})
@@ -44,16 +46,16 @@ func TestRepairCommand(t *testing.T) {
 			Query(DELETE_MIGRATION_VERSION, "0").
 			Reply("DELETE 1")
 		// Run test
-		err := Run(context.Background(), user, pass, database, host, "0", Reverted, conn.Intercept)
+		err := Run(context.Background(), dbConfig, "0", Reverted, conn.Intercept)
 		// Check error
 		assert.NoError(t, err)
 	})
 
 	t.Run("throws error on connect failure", func(t *testing.T) {
 		// Run test
-		err := Run(context.Background(), user, pass, database, "0", "0", Applied)
+		err := Run(context.Background(), pgconn.Config{}, "0", Applied)
 		// Check error
-		assert.ErrorContains(t, err, "connect: connection refused")
+		assert.ErrorContains(t, err, "invalid port (outside range)")
 	})
 
 	t.Run("throws error on insert failure", func(t *testing.T) {
@@ -67,7 +69,7 @@ func TestRepairCommand(t *testing.T) {
 			Query(INSERT_MIGRATION_VERSION, "0").
 			ReplyError(pgerrcode.DuplicateObject, `relation "supabase_migrations.schema_migrations" does not exist`)
 		// Run test
-		err := Run(context.Background(), user, pass, database, host, "0", Applied, conn.Intercept)
+		err := Run(context.Background(), dbConfig, "0", Applied, conn.Intercept)
 		// Check error
 		assert.ErrorContains(t, err, `ERROR: relation "supabase_migrations.schema_migrations" does not exist (SQLSTATE 42710)`)
 	})
diff --git a/internal/utils/connect_test.go b/internal/utils/connect_test.go
index aaa182b3..50d122ba 100644
--- a/internal/utils/connect_test.go
+++ b/internal/utils/connect_test.go
@@ -5,6 +5,7 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/jackc/pgconn"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -13,13 +14,21 @@ import (
 	"gopkg.in/h2non/gock.v1"
 )
 
+var dbConfig = pgconn.Config{
+	Host:     "localhost",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
 func TestConnectRemotePostgres(t *testing.T) {
 	t.Run("connects to remote postgres successfully", func(t *testing.T) {
 		// Setup mock postgres
 		conn := pgtest.NewConn()
 		defer conn.Close(t)
 		// Run test
-		c, err := ConnectRemotePostgres(context.Background(), "username", "password", "database", "localhost", conn.Intercept)
+		c, err := ConnectRemotePostgres(context.Background(), dbConfig, conn.Intercept)
 		require.NoError(t, err)
 		defer c.Close(context.Background())
 		assert.NoError(t, err)
@@ -30,11 +39,12 @@ func TestConnectRemotePostgres(t *testing.T) {
 		conn := pgtest.NewConn()
 		defer conn.Close(t)
 		// Run test
-		password := "pass word"
-		c, err := ConnectRemotePostgres(context.Background(), "username", password, "database", "localhost", conn.Intercept)
+		config := *dbConfig.Copy()
+		config.Password = "pass word"
+		c, err := ConnectRemotePostgres(context.Background(), config, conn.Intercept)
 		require.NoError(t, err)
 		defer c.Close(context.Background())
-		assert.Equal(t, password, c.Config().Password)
+		assert.Equal(t, config.Password, c.Config().Password)
 	})
 
 	t.Run("fallback to postgres port on timeout", func(t *testing.T) {
@@ -79,7 +89,7 @@ func TestConnectRemotePostgres(t *testing.T) {
 		conn := pgtest.NewConn()
 		defer conn.Close(t)
 		// Run test
-		c, err := ConnectRemotePostgres(context.Background(), "postgres", "postgres", "postgres", host, conn.Intercept)
+		c, err := ConnectRemotePostgres(context.Background(), dbConfig, conn.Intercept)
 		// Check error
 		require.NoError(t, err)
 		defer c.Close(context.Background())