diff --git a/.env.mysql b/.env.mysql index 96110c8..fef270e 100644 --- a/.env.mysql +++ b/.env.mysql @@ -1,7 +1,6 @@ -DB_PORT=4002 - BOLT_DB_CONN_DBNAME=test_db -BOLT_DB_CONN_HOST=localhost:4002 +BOLT_DB_CONN_HOST=localhost +BOLT_DB_CONN_PORT=4002 BOLT_DB_CONN_USER=test_user BOLT_DB_CONN_PASSWORD=testpassword BOLT_DB_CONN_DRIVER=mysql diff --git a/.env.postgresql b/.env.postgresql index 6735230..65082c9 100644 --- a/.env.postgresql +++ b/.env.postgresql @@ -1,7 +1,6 @@ -DB_PORT=4001 - BOLT_DB_CONN_DBNAME=test_db -BOLT_DB_CONN_HOST=localhost:4001 +BOLT_DB_CONN_HOST=localhost +BOLT_DB_CONN_PORT=4001 BOLT_DB_CONN_USER=test_user BOLT_DB_CONN_PASSWORD=testpassword BOLT_DB_CONN_DRIVER=postgresql diff --git a/README.md b/README.md index 4f940b0..45b5724 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ $ touch bolt.toml ```toml [connection] host = "localhost" +port = 5432 user = "bolt_user" password = "bolt_password" dbname = "bolt_tutorial_db" @@ -275,10 +276,10 @@ version_style = "timestamp" # required. [connection] # The host to use to connect to your database. -# If you need to use a particular port, use :. -# For example: localhost:5432. Otherwise, whatever port is -# the default for the database driver will be used. host = +# The port to use to connect to your database. +# Note: This should be an integer. +port = # The user to use to connect to your database. user = # The password to use to connect to your database. @@ -297,6 +298,7 @@ All configuration file settings have corresponding environment variables. - `BOLT_MIGRATIONS_DIR_PATH` - `BOLT_MIGRATIONS_VERSION_STYLE` - `BOLT_DB_CONN_HOST` +- `BOLT_DB_CONN_PORT` - `BOLT_DB_CONN_USER` - `BOLT_DB_CONN_PASSWORD` - `BOLT_DB_CONN_DBNAME` diff --git a/docker-compose.mysql.yml b/docker-compose.mysql.yml index 8e70946..e88d0f5 100644 --- a/docker-compose.mysql.yml +++ b/docker-compose.mysql.yml @@ -5,7 +5,7 @@ services: image: mysql:8.3 container_name: bolt_mysql_db ports: - - "${DB_PORT:?err}:3306" + - "${BOLT_DB_CONN_PORT:?err}:3306" environment: - MYSQL_DATABASE=${BOLT_DB_CONN_DBNAME:?err} - MYSQL_USER=${BOLT_DB_CONN_USER:?err} diff --git a/docker-compose.postgresql.yml b/docker-compose.postgresql.yml index a3b79d4..f3052fd 100644 --- a/docker-compose.postgresql.yml +++ b/docker-compose.postgresql.yml @@ -5,7 +5,7 @@ services: image: postgres:16.1-alpine3.19 container_name: bolt_postgres_db ports: - - "${DB_PORT:?err}:5432" + - "${BOLT_DB_CONN_PORT:?err}:5432" environment: - POSTGRES_DB=${BOLT_DB_CONN_DBNAME:?err} - POSTGRES_USER=${BOLT_DB_CONN_USER:?err} diff --git a/go.mod b/go.mod index 1b9282c..6377fa3 100644 --- a/go.mod +++ b/go.mod @@ -5,25 +5,22 @@ go 1.21.6 require ( github.com/BurntSushi/toml v1.3.2 github.com/eugenetriguba/checkmate v0.3.2 + github.com/go-sql-driver/mysql v1.8.0 github.com/google/subcommands v1.2.0 + github.com/jackc/pgx/v4 v4.18.3 github.com/kelseyhightower/envconfig v1.4.0 - github.com/upper/db/v4 v4.7.0 ) require ( filippo.io/edwards25519 v1.1.0 // indirect - github.com/go-sql-driver/mysql v1.8.0 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.14.3 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect - github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect - github.com/jackc/pgtype v1.14.2 // indirect - github.com/jackc/pgx/v4 v4.18.3 // indirect - github.com/lib/pq v1.10.9 // indirect - github.com/segmentio/fasthash v1.0.3 // indirect - golang.org/x/crypto v0.21.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgtype v1.14.0 // indirect + golang.org/x/crypto v0.20.0 // indirect golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index b8fe81e..0e3ce49 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,5 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -16,32 +13,20 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= github.com/eugenetriguba/checkmate v0.3.2 h1:vcALwpLLpt0FBxckjuobNFYtBLtlCT2p55Xk4p0SAhk= github.com/eugenetriguba/checkmate v0.3.2/go.mod h1:IY38Cl4zlNI1dg+wcDYwrbWZfJ9i2/ouWf8C0N8c/do= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4= github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= -github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -52,8 +37,6 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= -github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= @@ -71,31 +54,26 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 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-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/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= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgtype v1.14.2 h1:QBdZQTKpPdBlw2AdKwHEyqUcm/lrl2cwWAHjCMyln/o= -github.com/jackc/pgtype v1.14.2/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -108,56 +86,37 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= -github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/upper/db/v4 v4.7.0 h1:GNOxFAR8S3r0ITTWUq1LbTvvxipmwgSP4yxSCyJdim4= -github.com/upper/db/v4 v4.7.0/go.mod h1:EO/sQ5p41YroLxv2Z2CIxRBAtEeSG4ZOTksc+KA9VfY= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 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= @@ -176,35 +135,20 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -216,34 +160,14 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -253,10 +177,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -266,50 +187,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= -gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -modernc.org/b v1.0.4/go.mod h1:Oqc2xtmGT0tvBUsPZIanirLhxBCQZhM7Lu3TlzBj9w8= -modernc.org/b v1.1.0/go.mod h1:yF+wmBAFjebNdVqZNTeNfmnLaLqq91wozvDLcuXz+ck= -modernc.org/db v1.0.8/go.mod h1:L8Az96H46DF2+BGeaS6+WiEqLORR2sjp0yBn6LA/lAQ= -modernc.org/db v1.0.10/go.mod h1:P4R9V+DHFTxL0JYYdGhXkVCxEFS2mA4d7cWzl6Zy7Cs= -modernc.org/file v1.0.6/go.mod h1:obrJncmei0Gqizt7w2+/jVB+IQ62GFXO4ZxJ36irXwM= -modernc.org/file v1.0.7/go.mod h1:5Mjdj8xZ/eaNok4AIU6yDeFzVJycCzCNRY4tAFDGOpI= -modernc.org/file v1.0.8/go.mod h1:v0ae8/gLTK8QR+eyuV504kYZ/nsoe5JRDcHZFZecfxM= -modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= -modernc.org/fileutil v1.1.1/go.mod h1:HdjlliqRHrMAI4nVOvvpYVzVgvRSK7WnoCiG0GUWJNo= -modernc.org/fileutil v1.1.2/go.mod h1:HdjlliqRHrMAI4nVOvvpYVzVgvRSK7WnoCiG0GUWJNo= -modernc.org/fileutil v1.2.0/go.mod h1:0rLMFc17WSz6Bm/GtHeme7TOX8pNRhFN2NkfBlOZhrQ= -modernc.org/golex v1.0.5/go.mod h1:pTY7KKjdvZbv2ROjfp6FFX5BXMM9QWZEnmCsl60aCfI= -modernc.org/golex v1.1.0/go.mod h1:2pVlfqApurXhR1m0N+WDYu6Twnc4QuvO4+U8HnwoiRA= -modernc.org/internal v1.0.3/go.mod h1:dvHFQEGEd33HZar0OdSYIm6yen/77eukCqffWSAwQUc= -modernc.org/internal v1.0.4/go.mod h1:dvHFQEGEd33HZar0OdSYIm6yen/77eukCqffWSAwQUc= -modernc.org/internal v1.0.6/go.mod h1:9cJQ3k3JLgx7shbFOm7zJvMqaqCbV7+FD2GunADpCWU= -modernc.org/internal v1.0.7/go.mod h1:CUocFmERLLCOQogxq8ZQl5MSO97yGh3qb4QvVg6ltkM= -modernc.org/internal v1.0.8/go.mod h1:km71QBJPWkc1+LUldg2U9TJsKT6Q2QKHIykdEeCy/jw= -modernc.org/internal v1.1.0/go.mod h1:IFhfxUE81NbN8Riy+oHylA3PIYgAvIQ5eMufNzg7/QU= -modernc.org/lex v1.1.1/go.mod h1:6r8o8DLJkAnOsQaGi8fMoi+Vt6LTbDaCrkUK729D8xM= -modernc.org/lexer v1.0.4/go.mod h1:tOajb8S4sdfOYitzCgXDFmbVJ/LE0v1fNJ7annTw36U= -modernc.org/lexer v1.0.5/go.mod h1:8npHn3u/NxCEtlC/tRSY77x5+WB3HvHMzMVElQ76ayI= -modernc.org/lldb v1.0.4/go.mod h1:AKDI6wUJk7iJS8nRX54St8rq9wUIi3o5YGN3rlejR5o= -modernc.org/lldb v1.0.8/go.mod h1:ybOcsZ/RNZo3q8fiGadQFRnD+1Jc+RWGcTPdeilCnUk= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/ql v1.4.7/go.mod h1:I900l6z8ckpPy1y9VR0gu4pZ9hl9AhmQla4F8KERzdc= -modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= -modernc.org/sortutil v1.1.1/go.mod h1:DTj/8BqjEBLZFVPYvEGDfFFg94SsfPxQ70R+SQJ98qA= -modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= -modernc.org/zappy v1.0.5/go.mod h1:Q5T4ra3/JJNORGK16oe8rRAti7kWtRW4Z93fzin2gBc= -modernc.org/zappy v1.0.9/go.mod h1:y2c4Hv5jzyBP179SxNmx5H/BM6cVgNIXPQv2bCeR6IM= -modernc.org/zappy v1.1.0/go.mod h1:cxC0dWAgZuyMsJ+KL3ZBgo3twyKGBB/0By/umSZE2bQ= diff --git a/internal/bolttest/db.go b/internal/bolttest/db.go index a4e1115..639adb8 100644 --- a/internal/bolttest/db.go +++ b/internal/bolttest/db.go @@ -12,11 +12,11 @@ import ( func NewTestDB(t *testing.T) storage.DB { connectionConfig := NewTestConnectionConfig() - testdb, err := storage.DBConnect(connectionConfig) + testdb, err := storage.NewDB(connectionConfig) assert.Nil(t, err) t.Cleanup(func() { DropTable(t, testdb, "bolt_migrations") - assert.Nil(t, testdb.Session.Close()) + assert.Nil(t, testdb.Close()) }) return testdb } @@ -26,12 +26,13 @@ func NewTestConnectionConfig() configloader.ConnectionConfig { Driver: os.Getenv("BOLT_DB_CONN_DRIVER"), DBName: os.Getenv("BOLT_DB_CONN_DBNAME"), Host: os.Getenv("BOLT_DB_CONN_HOST"), + Port: os.Getenv("BOLT_DB_CONN_PORT"), User: os.Getenv("BOLT_DB_CONN_USER"), Password: os.Getenv("BOLT_DB_CONN_PASSWORD"), } } func DropTable(t *testing.T, testdb storage.DB, tableName string) { - _, err := testdb.Session.SQL().Exec(fmt.Sprintf(`DROP TABLE IF EXISTS %s;`, tableName)) + _, err := testdb.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s;", tableName)) assert.Nil(t, err) } diff --git a/internal/commands/down.go b/internal/commands/down.go index 0ba3abb..f991c69 100644 --- a/internal/commands/down.go +++ b/internal/commands/down.go @@ -54,12 +54,12 @@ func (cmd *DownCmd) Execute( return subcommands.ExitFailure } - db, err := storage.DBConnect(cfg.Connection) + db, err := storage.NewDB(cfg.Connection) if err != nil { consoleOutputter.Error(fmt.Errorf("unable to connect to database: %w", err)) return subcommands.ExitFailure } - defer db.Session.Close() + defer db.Close() migrationDBRepo, err := repositories.NewMigrationDBRepo(db) if err != nil { diff --git a/internal/commands/status.go b/internal/commands/status.go index d44a132..1e176b9 100644 --- a/internal/commands/status.go +++ b/internal/commands/status.go @@ -44,12 +44,12 @@ func (m *StatusCmd) Execute( return subcommands.ExitFailure } - db, err := storage.DBConnect(cfg.Connection) + db, err := storage.NewDB(cfg.Connection) if err != nil { consoleOutputter.Error(fmt.Errorf("unable to connect to database: %w", err)) return subcommands.ExitFailure } - defer db.Session.Close() + defer db.Close() migrationDBRepo, err := repositories.NewMigrationDBRepo(db) if err != nil { diff --git a/internal/commands/up.go b/internal/commands/up.go index a203d2e..77e0b8d 100644 --- a/internal/commands/up.go +++ b/internal/commands/up.go @@ -54,12 +54,12 @@ func (cmd *UpCmd) Execute( return subcommands.ExitFailure } - db, err := storage.DBConnect(cfg.Connection) + db, err := storage.NewDB(cfg.Connection) if err != nil { consoleOutputter.Error(fmt.Errorf("unable to connect to database: %w", err)) return subcommands.ExitFailure } - defer db.Session.Close() + defer db.Close() migrationDBRepo, err := repositories.NewMigrationDBRepo(db) if err != nil { diff --git a/internal/configloader/configloader.go b/internal/configloader/configloader.go index 96bab00..8057ee2 100644 --- a/internal/configloader/configloader.go +++ b/internal/configloader/configloader.go @@ -55,6 +55,7 @@ type MigrationsConfig struct { type ConnectionConfig struct { Host string `toml:"host" envconfig:"BOLT_DB_CONN_HOST"` + Port string `toml:"port" envconfig:"BOLT_DB_CONN_PORT"` User string `toml:"user" envconfig:"BOLT_DB_CONN_USER"` Password string `toml:"password" envconfig:"BOLT_DB_CONN_PASSWORD"` DBName string `toml:"dbname" envconfig:"BOLT_DB_CONN_DBNAME"` diff --git a/internal/configloader/configloader_test.go b/internal/configloader/configloader_test.go index 0c89672..6a27d86 100644 --- a/internal/configloader/configloader_test.go +++ b/internal/configloader/configloader_test.go @@ -36,6 +36,7 @@ func TestNewConfigWithInvalidVersionStyle(t *testing.T) { func TestNewConfigFindsFileAndPopulatesConfigStruct(t *testing.T) { bolttest.UnsetEnv(t, "BOLT_DB_CONN_HOST") + bolttest.UnsetEnv(t, "BOLT_DB_CONN_PORT") bolttest.UnsetEnv(t, "BOLT_DB_CONN_USER") bolttest.UnsetEnv(t, "BOLT_DB_CONN_PASSWORD") bolttest.UnsetEnv(t, "BOLT_DB_CONN_DBNAME") @@ -49,6 +50,7 @@ func TestNewConfigFindsFileAndPopulatesConfigStruct(t *testing.T) { }, Connection: configloader.ConnectionConfig{ Host: "testhost", + Port: "1234", User: "testuser", Password: "testpassword", DBName: "testdb", @@ -73,6 +75,7 @@ func TestNewConfigCanBeOverridenByEnvVars(t *testing.T) { }, Connection: configloader.ConnectionConfig{ Host: "testhost", + Port: "1234", User: "testuser", Password: "testpassword", DBName: "testdb", @@ -88,6 +91,7 @@ func TestNewConfigCanBeOverridenByEnvVars(t *testing.T) { }, Connection: configloader.ConnectionConfig{ Host: "envtesthost", + Port: "4321", User: "envtestuser", Password: "envtestpassword", DBName: "envtestdb", @@ -97,6 +101,7 @@ func TestNewConfigCanBeOverridenByEnvVars(t *testing.T) { t.Setenv("BOLT_MIGRATIONS_VERSION_STYLE", string(envCfg.Migrations.VersionStyle)) t.Setenv("BOLT_MIGRATIONS_DIR_PATH", envCfg.Migrations.DirectoryPath) t.Setenv("BOLT_DB_CONN_HOST", envCfg.Connection.Host) + t.Setenv("BOLT_DB_CONN_PORT", envCfg.Connection.Port) t.Setenv("BOLT_DB_CONN_USER", envCfg.Connection.User) t.Setenv("BOLT_DB_CONN_PASSWORD", envCfg.Connection.Password) t.Setenv("BOLT_DB_CONN_DBNAME", envCfg.Connection.DBName) @@ -109,6 +114,7 @@ func TestNewConfigCanBeOverridenByEnvVars(t *testing.T) { func TestNewConfigSearchesParentDirectories(t *testing.T) { bolttest.UnsetEnv(t, "BOLT_DB_CONN_HOST") + bolttest.UnsetEnv(t, "BOLT_DB_CONN_PORT") bolttest.UnsetEnv(t, "BOLT_DB_CONN_USER") bolttest.UnsetEnv(t, "BOLT_DB_CONN_PASSWORD") bolttest.UnsetEnv(t, "BOLT_DB_CONN_DBNAME") diff --git a/internal/repositories/migration_db_repo.go b/internal/repositories/migration_db_repo.go index 56bff0c..27644ae 100644 --- a/internal/repositories/migration_db_repo.go +++ b/internal/repositories/migration_db_repo.go @@ -8,7 +8,6 @@ import ( "github.com/eugenetriguba/bolt/internal/models" "github.com/eugenetriguba/bolt/internal/storage" - "github.com/upper/db/v4" ) type MigrationDBRepo interface { @@ -29,7 +28,7 @@ type migrationDBRepo struct { // operates on exists. If it is unable to create or confirm // the table exists, an error is returned. func NewMigrationDBRepo(db storage.DB) (MigrationDBRepo, error) { - _, err := db.Session.SQL().Exec(` + _, err := db.Exec(` CREATE TABLE IF NOT EXISTS bolt_migrations( version CHARACTER(14) PRIMARY KEY NOT NULL ); @@ -49,7 +48,7 @@ func NewMigrationDBRepo(db storage.DB) (MigrationDBRepo, error) { // will be ones that have been applied, and their message // will always be an empty string. func (mr migrationDBRepo) List() (map[string]*models.Migration, error) { - rows, err := mr.db.Session.SQL().Select("version").From("bolt_migrations").Query() + rows, err := mr.db.Query("SELECT version FROM bolt_migrations;") if err != nil { return nil, fmt.Errorf( "unable to execute query to select versions from "+ @@ -89,12 +88,9 @@ func (mr migrationDBRepo) List() (map[string]*models.Migration, error) { // when the version might be applied, but there was an error. // Check err first before looking at whether the version is applied. func (mr migrationDBRepo) IsApplied(version string) (bool, error) { - row, err := mr.db.Session.SQL().Select(1).From("bolt_migrations").Where("version = ?", version).QueryRow() - if err != nil { - return false, fmt.Errorf("unable to execute query to check if version exists in bolt_migrations: %w", err) - } var scanResult int - err = row.Scan(&scanResult) + err := mr.db.QueryRow("SELECT 1 FROM bolt_migrations WHERE version = ?", version). + Scan(&scanResult) if errors.Is(err, sql.ErrNoRows) { return false, nil } @@ -116,7 +112,7 @@ func (mr migrationDBRepo) Apply( upgradeScript string, migration *models.Migration, ) error { - err := applyMigration(mr.db.Session, upgradeScript, *migration) + err := applyMigration(mr.db, upgradeScript, *migration) if err != nil { return err } @@ -131,8 +127,8 @@ func (mr migrationDBRepo) ApplyWithTx( upgradeScript string, migration *models.Migration, ) error { - err := mr.db.Session.Tx(func(sess db.Session) error { - err := applyMigration(sess, upgradeScript, *migration) + err := mr.db.Tx(func(db storage.DB) error { + err := applyMigration(db, upgradeScript, *migration) if err != nil { return err } @@ -147,16 +143,16 @@ func (mr migrationDBRepo) ApplyWithTx( } func applyMigration( - db db.Session, + db storage.DB, upgradeScript string, migration models.Migration, ) error { - _, err := db.SQL().Exec(upgradeScript) + _, err := db.Exec(upgradeScript) if err != nil { return fmt.Errorf("unable to execute upgrade script: %w", err) } - _, err = db.SQL().InsertInto("bolt_migrations").Columns("version").Values(migration.Version).Exec() + _, err = db.Exec("INSERT INTO bolt_migrations(version) VALUES(?)", migration.Version) if err != nil { return fmt.Errorf( "unable to insert migration: %w", @@ -174,7 +170,7 @@ func (mr migrationDBRepo) Revert( downgradeScript string, migration *models.Migration, ) error { - err := revertMigration(mr.db.Session, downgradeScript, *migration) + err := revertMigration(mr.db, downgradeScript, *migration) if err != nil { return err } @@ -189,8 +185,8 @@ func (mr migrationDBRepo) RevertWithTx( downgradeScript string, migration *models.Migration, ) error { - err := mr.db.Session.Tx(func(sess db.Session) error { - err := revertMigration(sess, downgradeScript, *migration) + err := mr.db.Tx(func(db storage.DB) error { + err := revertMigration(db, downgradeScript, *migration) if err != nil { return err } @@ -205,16 +201,16 @@ func (mr migrationDBRepo) RevertWithTx( } func revertMigration( - db db.Session, + db storage.DB, downgradeScript string, migration models.Migration, ) error { - _, err := db.SQL().Exec(downgradeScript) + _, err := db.Exec(downgradeScript) if err != nil { return fmt.Errorf("unable to execute downgrade script: %w", err) } - _, err = db.SQL().DeleteFrom("bolt_migrations").Where("version = ?", migration.Version).Exec() + _, err = db.Exec("DELETE FROM bolt_migrations WHERE version = ?", migration.Version) if err != nil { return fmt.Errorf( "unable to remove reverted migration from bolt_migrations table: %w", diff --git a/internal/repositories/migration_db_repo_test.go b/internal/repositories/migration_db_repo_test.go index b9fb16e..d54fb24 100644 --- a/internal/repositories/migration_db_repo_test.go +++ b/internal/repositories/migration_db_repo_test.go @@ -8,40 +8,38 @@ import ( "github.com/eugenetriguba/bolt/internal/models" "github.com/eugenetriguba/bolt/internal/repositories" "github.com/eugenetriguba/checkmate/assert" - "github.com/upper/db/v4" ) func TestNewMigrationDBRepo_CreatesTable(t *testing.T) { testdb := bolttest.NewTestDB(t) - exists, err := testdb.Session.Collection("bolt_migrations").Exists() - assert.ErrorIs(t, err, db.ErrCollectionDoesNotExist) + exists, err := testdb.TableExists("bolt_migrations") + assert.Nil(t, err) assert.False(t, exists) _, err = repositories.NewMigrationDBRepo(testdb) assert.Nil(t, err) - exists, err = testdb.Session.Collection("bolt_migrations").Exists() + exists, err = testdb.TableExists("bolt_migrations") assert.Nil(t, err) assert.True(t, exists) } func TestNewMigrationDBRepo_TableAlreadyExists(t *testing.T) { testdb := bolttest.NewTestDB(t) - _, err := testdb.Session.SQL().Exec(`CREATE TABLE bolt_migrations(id INT NOT NULL PRIMARY KEY)`) + _, err := testdb.Exec(`CREATE TABLE bolt_migrations(id INT NOT NULL PRIMARY KEY)`) assert.Nil(t, err) - _, err = testdb.Session.SQL().Exec(`INSERT INTO bolt_migrations(id) VALUES (1);`) + _, err = testdb.Exec(`INSERT INTO bolt_migrations(id) VALUES (1);`) assert.Nil(t, err) _, err = repositories.NewMigrationDBRepo(testdb) assert.Nil(t, err) - count, err := testdb.Session.Collection("bolt_migrations").Find().Count() - assert.Nil(t, err) - assert.Equal(t, count, uint64(1)) - row, err := testdb.Session.SQL().Select("id").From("bolt_migrations").QueryRow() + var count int + err = testdb.QueryRow("SELECT count(*) FROM bolt_migrations;").Scan(&count) assert.Nil(t, err) + assert.Equal(t, count, 1) var scanResult int - err = row.Scan(&scanResult) + err = testdb.QueryRow("SELECT id FROM bolt_migrations;").Scan(&scanResult) assert.Nil(t, err) assert.Equal(t, scanResult, 1) } @@ -62,7 +60,7 @@ func TestList_SingleResult(t *testing.T) { assert.Nil(t, err) version := "20230101000000" - _, err = db.Session.SQL().InsertInto("bolt_migrations").Columns("version").Values(version).Exec() + _, err = db.Exec("INSERT INTO bolt_migrations(version) VALUES(?)", version) assert.Nil(t, err) migrations, err := repo.List() @@ -81,7 +79,7 @@ func TestList_ShortVersion(t *testing.T) { assert.Nil(t, err) version := "20230101" - _, err = db.Session.SQL().InsertInto("bolt_migrations").Columns("version").Values(version).Exec() + _, err = db.Exec("INSERT INTO bolt_migrations(version) VALUES(?)", version) assert.Nil(t, err) migrations, err := repo.List() @@ -111,7 +109,7 @@ func TestIsApplied_WithApplied(t *testing.T) { assert.Nil(t, err) version := "20230101010101" - _, err = db.Session.SQL().InsertInto("bolt_migrations").Columns("version").Values(version).Exec() + _, err = db.Exec("INSERT INTO bolt_migrations(version) VALUES(?)", version) assert.Nil(t, err) applied, err := repo.IsApplied(version) @@ -132,7 +130,7 @@ func TestApply(t *testing.T) { assert.Nil(t, err) assert.Equal(t, migration.Applied, true) - exists, err := testdb.Session.Collection("tmp").Exists() + exists, err := testdb.TableExists("tmp") assert.Nil(t, err) assert.True(t, exists) applied, err := repo.IsApplied(migration.Version) @@ -176,7 +174,7 @@ func TestApplyWithTx_SuccessfullyApplied(t *testing.T) { assert.Nil(t, err) assert.Equal(t, migration.Applied, true) - exists, err := testdb.Session.Collection("tmp").Exists() + exists, err := testdb.TableExists("tmp") assert.Nil(t, err) assert.True(t, exists) applied, err := repo.IsApplied(migration.Version) @@ -192,11 +190,14 @@ func TestRevert(t *testing.T) { bolttest.DropTable(t, testdb, "tmp") }) - _, err = testdb.Session.SQL().Exec(`CREATE TABLE tmp(id INT NOT NULL PRIMARY KEY)`) + _, err = testdb.Exec(`CREATE TABLE tmp(id INT NOT NULL PRIMARY KEY)`) assert.Nil(t, err) migration := models.NewTimestampMigration(time.Now(), "test") - _, err = testdb.Session.SQL().InsertInto("bolt_migrations").Columns("version").Values(migration.Version).Exec() + _, err = testdb.Exec( + "INSERT INTO bolt_migrations(version) VALUES(?)", + migration.Version, + ) assert.Nil(t, err) migration.Applied = true @@ -204,12 +205,13 @@ func TestRevert(t *testing.T) { assert.Nil(t, err) assert.Equal(t, migration.Applied, false) - exists, err := testdb.Session.Collection("tmp").Exists() - assert.ErrorIs(t, err, db.ErrCollectionDoesNotExist) + exists, err := testdb.TableExists("tmp") + assert.Nil(t, err) assert.False(t, exists) - count, err := testdb.Session.Collection("bolt_migrations").Count() + var count int + err = testdb.QueryRow("SELECT count(*) FROM bolt_migrations;").Scan(&count) assert.Nil(t, err) - assert.Equal(t, count, uint64(0)) + assert.Equal(t, count, 0) } func TestRevert_MalformedSql(t *testing.T) { @@ -243,11 +245,14 @@ func TestRevertWithTx_SuccessfullyReverted(t *testing.T) { bolttest.DropTable(t, testdb, "tmp") }) - _, err = testdb.Session.SQL().Exec(`CREATE TABLE tmp(id INT NOT NULL PRIMARY KEY)`) + _, err = testdb.Exec(`CREATE TABLE tmp(id INT NOT NULL PRIMARY KEY)`) assert.Nil(t, err) migration := models.NewTimestampMigration(time.Now(), "test") - _, err = testdb.Session.SQL().InsertInto("bolt_migrations").Columns("version").Values(migration.Version).Exec() + _, err = testdb.Exec( + "INSERT INTO bolt_migrations(version) VALUES(?)", + migration.Version, + ) assert.Nil(t, err) migration.Applied = true @@ -255,10 +260,11 @@ func TestRevertWithTx_SuccessfullyReverted(t *testing.T) { assert.Nil(t, err) assert.Equal(t, migration.Applied, false) - exists, err := testdb.Session.Collection("tmp").Exists() - assert.ErrorIs(t, err, db.ErrCollectionDoesNotExist) + exists, err := testdb.TableExists("tmp") + assert.Nil(t, err) assert.False(t, exists) - count, err := testdb.Session.Collection("bolt_migrations").Count() + var count int + err = testdb.QueryRow("SELECT count(*) FROM bolt_migrations;").Scan(&count) assert.Nil(t, err) - assert.Equal(t, count, uint64(0)) + assert.Equal(t, count, 0) } diff --git a/internal/storage/adapter.go b/internal/storage/adapter.go new file mode 100644 index 0000000..ce01989 --- /dev/null +++ b/internal/storage/adapter.go @@ -0,0 +1,10 @@ +package storage + +import "github.com/eugenetriguba/bolt/internal/configloader" + +type DBAdapter interface { + ConvertGenericPlaceholders(query string, argsCount int) string + TableExists(executor sqlExecutor, tableName string) (bool, error) + DatabaseName(executor sqlExecutor) (string, error) + CreateDSN(cfg configloader.ConnectionConfig) string +} diff --git a/internal/storage/db.go b/internal/storage/db.go index 7f1d93d..6a7650e 100644 --- a/internal/storage/db.go +++ b/internal/storage/db.go @@ -1,64 +1,139 @@ package storage import ( + "database/sql" "errors" "fmt" "github.com/eugenetriguba/bolt/internal/configloader" - "github.com/upper/db/v4" - "github.com/upper/db/v4/adapter/mysql" - "github.com/upper/db/v4/adapter/postgresql" + _ "github.com/go-sql-driver/mysql" + _ "github.com/jackc/pgx/v4/stdlib" ) var postgresqlDriverName = "postgresql" var mysqlDriverName = "mysql" -var supportedDrivers = []string{postgresqlDriverName, mysqlDriverName} + +var supportedDrivers = map[string]string{ + postgresqlDriverName: "pgx", + mysqlDriverName: "mysql", +} + +var driverAdapters = map[string]DBAdapter{ + postgresqlDriverName: PostgresqlAdapter{}, + mysqlDriverName: MySQLAdapter{}, +} var ( ErrMalformedConnectionString = errors.New( "malformed database connection parameters provided", ) ErrUnableToConnect = errors.New("unable to open connection to database") - ErrUnsupportedDriver = fmt.Errorf("unsupported driver, supported drivers are %s", supportedDrivers) + ErrUnsupportedDriver = fmt.Errorf( + "unsupported driver, supported drivers are %s", + []string{postgresqlDriverName, mysqlDriverName}, + ) ) +type sqlExecutor interface { + Exec(query string, args ...interface{}) (sql.Result, error) + Query(query string, args ...interface{}) (*sql.Rows, error) + QueryRow(query string, args ...interface{}) *sql.Row +} + type DB struct { - Session db.Session + executor sqlExecutor + sqlDB *sql.DB + adapter DBAdapter } -// DBConnect establishes a connection to the database using the +// NewDB establishes a connection to the database using the // connection configuration. // // The following errors may be returned: +// - ErrMalformedConnectionString: The provided connection parameters are +// not in a valid format. // - ErrUnableToConnect: Unable to make a connection to the database with // the provided connection parameters. // - ErrUnsupportedDriver: The provided driver is not supported. -func DBConnect(cfg configloader.ConnectionConfig) (DB, error) { - var db db.Session - var err error = nil - - switch cfg.Driver { - case postgresqlDriverName: - db, err = postgresql.Open(postgresql.ConnectionURL{ - User: cfg.User, - Password: cfg.Password, - Host: cfg.Host, - Database: cfg.DBName, - }) - case mysqlDriverName: - db, err = mysql.Open(mysql.ConnectionURL{ - User: cfg.User, - Password: cfg.Password, - Host: cfg.Host, - Database: cfg.DBName, - }) - default: +func NewDB(cfg configloader.ConnectionConfig) (DB, error) { + driverName, exists := supportedDrivers[cfg.Driver] + if !exists { + return DB{}, ErrUnsupportedDriver + } + + adapter, exists := driverAdapters[cfg.Driver] + if !exists { return DB{}, ErrUnsupportedDriver } + db, err := sql.Open(driverName, adapter.CreateDSN(cfg)) + if err != nil { + return DB{}, fmt.Errorf("%w: %v", ErrMalformedConnectionString, err) + } + + // Note: `sql.Open` only validates the connection string we provided is sane. + // It doesn't open up a connection to the database. For that, we ping + // the database to ensure the connection string is fully valid. + err = db.Ping() if err != nil { return DB{}, fmt.Errorf("%w: %v", ErrUnableToConnect, err) } - return DB{Session: db}, nil + return DB{executor: db, sqlDB: db, adapter: adapter}, nil +} + +// Close closes the database connection. Any further +// queries will result in errors, and you should call +// NewDB again after if you'd like to run more. +func (db DB) Close() error { + return db.sqlDB.Close() +} + +func (db DB) Exec(query string, args ...any) (sql.Result, error) { + newQuery := db.adapter.ConvertGenericPlaceholders(query, len(args)) + return db.executor.Exec(newQuery, args...) +} + +func (db DB) Query(query string, args ...any) (*sql.Rows, error) { + newQuery := db.adapter.ConvertGenericPlaceholders(query, len(args)) + return db.executor.Query(newQuery, args...) +} + +func (db DB) QueryRow(query string, args ...any) *sql.Row { + newQuery := db.adapter.ConvertGenericPlaceholders(query, len(args)) + return db.executor.QueryRow(newQuery, args...) +} + +func (db DB) TableExists(tableName string) (bool, error) { + return db.adapter.TableExists(db.executor, tableName) +} + +type txFunc func(db DB) error + +func (db *DB) Tx(fn txFunc) error { + tx, err := db.sqlDB.Begin() + if err != nil { + return fmt.Errorf( + "unable to start transaction: %w", + err, + ) + } + defer tx.Rollback() + + // Create a shallow clone of the DB instance for + // the transaction scope with a tx executor. + txDB := *db + txDB.executor = tx + + err = fn(txDB) + if err != nil { + return err + } + + err = tx.Commit() + if err != nil { + return err + } + + return nil } diff --git a/internal/storage/db_test.go b/internal/storage/db_test.go index 8162898..0eacd23 100644 --- a/internal/storage/db_test.go +++ b/internal/storage/db_test.go @@ -8,31 +8,22 @@ import ( "github.com/eugenetriguba/checkmate/assert" ) -func TestDBConnect_Success(t *testing.T) { +func TestNewDB_Success(t *testing.T) { cfg := bolttest.NewTestConnectionConfig() - conn, err := storage.DBConnect(cfg) - assert.Nil(t, err) - defer conn.Session.Close() - _, err = conn.Session.SQL().Exec("SELECT 1;") + db, err := storage.NewDB(cfg) + + assert.Nil(t, err) + defer db.Close() + _, err = db.Exec("SELECT 1;") assert.Nil(t, err) } -func TestDBConnect_UnsupportedDriver(t *testing.T) { +func TestNewDB_UnsupportedDriver(t *testing.T) { t.Setenv("BOLT_DB_CONN_DRIVER", "abc123") cfg := bolttest.NewTestConnectionConfig() - _, err := storage.DBConnect(cfg) - assert.ErrorIs(t, err, storage.ErrUnsupportedDriver) -} + _, err := storage.NewDB(cfg) -func TestDBConnect_BadConnectionString(t *testing.T) { - t.Setenv("BOLT_DB_CONN_DRIVER", "mysql") - t.Setenv("BOLT_DB_CONN_HOST", "abc123") - cfg := bolttest.NewTestConnectionConfig() - - _, err := storage.DBConnect(cfg) - - assert.ErrorIs(t, err, storage.ErrUnableToConnect) - assert.ErrorContains(t, err, "unable to open connection to database") + assert.ErrorIs(t, err, storage.ErrUnsupportedDriver) } diff --git a/internal/storage/mysql.go b/internal/storage/mysql.go new file mode 100644 index 0000000..19514fb --- /dev/null +++ b/internal/storage/mysql.go @@ -0,0 +1,77 @@ +package storage + +import ( + "fmt" + + "github.com/eugenetriguba/bolt/internal/configloader" + "github.com/go-sql-driver/mysql" +) + +type MySQLAdapter struct{} + +func (m MySQLAdapter) ConvertGenericPlaceholders(query string, argsCount int) string { + // MySQL uses ? as its query argument placeholder, which we're using + // as our generic placeholder so no transformation is necessary. + return query +} + +func (m MySQLAdapter) TableExists( + executor sqlExecutor, + tableName string, +) (bool, error) { + // Note: MySQL doesn't have schemas. So the "table_schema" + // should be the currently selected database. + databaseName, err := m.DatabaseName(executor) + if err != nil { + return false, fmt.Errorf("unable to retrieve database name: %w", err) + } + + var exists bool + err = executor.QueryRow(` + SELECT EXISTS ( + SELECT 1 + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = ? + AND TABLE_NAME = ? + ); + `, databaseName, tableName).Scan(&exists) + if err != nil { + return false, fmt.Errorf( + "unable to check if %s exists: %w", + tableName, + err, + ) + } + + return exists, nil +} + +func (m MySQLAdapter) DatabaseName(executor sqlExecutor) (string, error) { + var name string + err := executor.QueryRow("SELECT DATABASE();").Scan(&name) + if err != nil { + return "", fmt.Errorf("unable to retrieve database name: %w", err) + } + + // MySQL allows connecting without a database selected. + if name == "" { + return "", fmt.Errorf("no database is currently selected") + } + + return name, nil +} + +func (m MySQLAdapter) CreateDSN(cfg configloader.ConnectionConfig) string { + addr := cfg.Host + if cfg.Port != "" { + addr += fmt.Sprintf(":%s", cfg.Port) + } + dsnCfg := mysql.Config{ + Net: "tcp", + Addr: addr, + User: cfg.User, + Passwd: cfg.Password, + DBName: cfg.DBName, + } + return dsnCfg.FormatDSN() +} diff --git a/internal/storage/postgresql.go b/internal/storage/postgresql.go new file mode 100644 index 0000000..f728514 --- /dev/null +++ b/internal/storage/postgresql.go @@ -0,0 +1,71 @@ +package storage + +import ( + "fmt" + "strings" + + "github.com/eugenetriguba/bolt/internal/configloader" +) + +type PostgresqlAdapter struct{} + +func (p PostgresqlAdapter) ConvertGenericPlaceholders( + query string, + argsCount int, +) string { + for i := 0; i <= argsCount; i++ { + placeholder := p.createPlaceholder(i + 1) + query = strings.Replace(query, "?", placeholder, 1) + } + return query +} + +func (p PostgresqlAdapter) createPlaceholder(index int) string { + return fmt.Sprintf("$%d", index) +} + +func (p PostgresqlAdapter) TableExists( + executor sqlExecutor, + tableName string, +) (bool, error) { + var exists bool + // Assumption: Anytime we check if a table exists, it will be + // for the 'public' schema. If someone wants to have bolt_migrations + // table outside of the 'public' schema on postgresql, this would be + // an issue. + err := executor.QueryRow(` + SELECT EXISTS ( + SELECT FROM pg_catalog.pg_class c + JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = 'public' + AND c.relname = $1 + AND c.relkind = 'r' -- Only tables + ); + `, tableName).Scan(&exists) + if err != nil { + return false, fmt.Errorf( + "unable to check if %s exists: %w", + tableName, + err, + ) + } + + return exists, nil +} + +func (p PostgresqlAdapter) DatabaseName(executor sqlExecutor) (string, error) { + var name string + err := executor.QueryRow("SELECT database_name();").Scan(&name) + if err != nil { + return "", fmt.Errorf("unable to retrieve database name: %w", err) + } + + return name, nil +} + +func (p PostgresqlAdapter) CreateDSN(cfg configloader.ConnectionConfig) string { + return fmt.Sprintf( + "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", + cfg.Host, cfg.Port, cfg.User, cfg.Password, cfg.DBName, + ) +}