all: use jackc/pgx in place of lib/pq

What:

Use the github.com/jackc/pgx postgresql driver in place of
github.com/lib/pq.

Why:

github.com/lib/pq has some problems with error handling and context
cancellations (i.e. it might even issue queries or DML statements more
than once! see https://github.com/lib/pq/issues/939). The
github.com/jackx/pgx library appears not to have these problems, and
also appears to be better engineered and implemented (in particular, it
doesn't use "exceptions by panic"). It should also give us some
performance improvements in some cases, and even more so if we can use
it directly instead of going through the database/sql layer.

Change-Id: Ia696d220f340a097dee9550a312d37de14ed2044
This commit is contained in:
paul cannon 2020-06-27 22:56:29 -05:00 committed by Egon Elbre
parent 262da14359
commit bbdb351e5e
42 changed files with 864 additions and 664 deletions

8
go.mod
View File

@ -16,12 +16,14 @@ require (
github.com/gorilla/mux v1.7.1
github.com/gorilla/schema v1.1.0
github.com/graphql-go/graphql v0.7.9
github.com/jackc/pgconn v1.6.1
github.com/jackc/pgtype v1.4.0
github.com/jackc/pgx/v4 v4.7.1
github.com/jtolds/monkit-hw/v2 v2.0.0-20191108235325-141a0da276b3
github.com/lib/pq v1.3.0
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/nsf/jsondiff v0.0.0-20160203110537-7de28ed2b6e3
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d
github.com/shopspring/decimal v0.0.0-20200105231215-408a2507e114
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc
github.com/sirupsen/logrus v1.5.0 // indirect
github.com/spacemonkeygo/monkit/v3 v3.0.7-0.20200515175308-072401d8c752
github.com/spf13/cobra v0.0.6
@ -33,7 +35,7 @@ require (
github.com/zeebo/errs v1.2.2
go.etcd.io/bbolt v1.3.4
go.uber.org/zap v1.15.0
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
golang.org/x/sys v0.0.0-20200610111108-226ff32320da

96
go.sum
View File

@ -83,8 +83,10 @@ github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg=
github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
@ -138,6 +140,8 @@ github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZp
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
@ -212,10 +216,60 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
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=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.6.1 h1:lwofaXKPbIx6qEaK8mNm7uZuOwxHw+PnAFGDsDFpkRI=
github.com/jackc/pgconn v1.6.1/go.mod h1:g8mKMqmSUO6AzAvha7vy07g1rbGOlc7iF0nU0ei83hc=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.0.2 h1:q1Hsy66zh4vuNsajBUF2PNqfAMMfxU5mk594lPE9vjY=
github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8 h1:Q3tB+ExeflWUW7AFcAhXqk40s9mnNYLk1nOkKNZ5GnU=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
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.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
github.com/jackc/pgtype v1.4.0 h1:pHQfb4jh9iKqHyxPthq1fr+0HwSNIl3btYPbw2m2lbM=
github.com/jackc/pgtype v1.4.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY=
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
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.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
github.com/jackc/pgx/v4 v4.7.1 h1:aqUSOcStk6fik+lSE+tqfFhvt/EwT8q/oMtJbP9CjXI=
github.com/jackc/pgx/v4 v4.7.1/go.mod h1:nu42q3aPjuC1M0Nak4bnoprKlXPINqopEKqbq5AZSC4=
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.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@ -239,18 +293,29 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE=
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.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
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.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@ -318,16 +383,20 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
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/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200105231215-408a2507e114 h1:Pm6R878vxWWWR+Sa3ppsLce/Zq+JNTs6aVvRu13jv9A=
github.com/shopspring/decimal v0.0.0-20200105231215-408a2507e114/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
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.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@ -360,6 +429,7 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
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/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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
@ -396,6 +466,7 @@ github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 h1:+cwNE5KJ3pika4Huzm
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54/go.mod h1:EI8LcOBDlSL3POyqwC1eJhOYlMBMidES+613EtmmT5w=
github.com/zeebo/structs v1.0.2 h1:kvcd7s2LqXuO9cdV5LqrGHCOAfCBXaZpKCA3jD9SJIc=
github.com/zeebo/structs v1.0.2/go.mod h1:LphfpprlqJQcbCq+eA3iIK/NsejMwk9mlfH/tM1XuKQ=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
@ -406,6 +477,7 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@ -414,6 +486,7 @@ go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
@ -422,14 +495,20 @@ golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -471,6 +550,7 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -502,6 +582,7 @@ golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5h
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=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -509,11 +590,15 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200107144601-ef85f5a75ddf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200610111108-226ff32320da h1:bGb80FudwxpeucJUjPYJXuJ8Hk91vNtfvrymzwiei38=
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -521,6 +606,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
@ -534,6 +621,7 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@ -541,6 +629,7 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -550,6 +639,8 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200428211428-0c9eba77bc32 h1:Xvf3ZQTm5bjXPxhI7g+dwqsCqadK1rcNtwtszuatetk=
golang.org/x/tools v0.0.0-20200428211428-0c9eba77bc32/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
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=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
@ -598,6 +689,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=

View File

@ -10,11 +10,11 @@ import (
"net/url"
"strings"
"github.com/lib/pq"
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
"storj.io/storj/private/dbutil"
"storj.io/storj/private/dbutil/pgutil"
"storj.io/storj/private/tagsql"
)
@ -50,13 +50,13 @@ func OpenUnique(ctx context.Context, connStr string, schemaPrefix string) (db *d
return nil, errs.New("Could not open masterDB at conn %q: %w", connStr, err)
}
_, err = masterDB.Exec(ctx, "CREATE DATABASE "+pq.QuoteIdentifier(schemaName))
_, err = masterDB.Exec(ctx, "CREATE DATABASE "+pgutil.QuoteIdentifier(schemaName))
if err != nil {
return nil, errs.Wrap(err)
}
cleanup := func(cleanupDB tagsql.DB) error {
_, err := cleanupDB.Exec(context.TODO(), "DROP DATABASE "+pq.QuoteIdentifier(schemaName))
_, err := cleanupDB.Exec(context.TODO(), "DROP DATABASE "+pgutil.QuoteIdentifier(schemaName))
return errs.Wrap(err)
}

View File

@ -7,61 +7,71 @@ import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"io"
"reflect"
"strings"
"github.com/lib/pq"
"github.com/jackc/pgx/v4/stdlib"
"github.com/zeebo/errs"
"storj.io/storj/private/dbutil/pgutil"
)
// Driver is the type for the "cockroach" sql/database driver.
// It uses github.com/lib/pq under the covers because of Cockroach's
// PostgreSQL compatibility, but allows differentiation between pg and
// crdb connections.
// Driver is the type for the "cockroach" sql/database driver. It uses
// github.com/jackc/pgx/v4/stdlib under the covers because of Cockroach's
// PostgreSQL compatibility, but allows differentiation between pg and crdb
// connections.
type Driver struct {
pq.Driver
pgxDriver stdlib.Driver
}
// Open opens a new cockroachDB connection.
func (cd *Driver) Open(name string) (driver.Conn, error) {
name = translateName(name)
return pq.Open(name)
conn, err := cd.pgxDriver.Open(name)
if err != nil {
return nil, err
}
pgxStdlibConn, ok := conn.(*stdlib.Conn)
if !ok {
return nil, errs.New("Conn from pgx is not a *stdlib.Conn??? T: %T", conn)
}
return &cockroachConn{pgxStdlibConn}, nil
}
// OpenConnector obtains a new db Connector, which sql.DB can use to
// obtain each needed connection at the appropriate time.
func (cd *Driver) OpenConnector(name string) (driver.Connector, error) {
name = translateName(name)
pgConnector, err := pq.NewConnector(name)
pgxConnector, err := cd.pgxDriver.OpenConnector(name)
if err != nil {
return nil, err
}
return &cockroachConnector{pgConnector}, nil
return &cockroachConnector{driver: cd, pgxConnector: pgxConnector}, nil
}
// cockroachConnector is a thin wrapper around a pq-based connector. This allows
// Driver to supply our custom cockroachConn type for connections.
type cockroachConnector struct {
pgConnector driver.Connector
driver *Driver
pgxConnector driver.Connector
}
// Driver returns the driver being used for this connector.
func (c *cockroachConnector) Driver() driver.Driver {
return &Driver{}
return c.driver
}
// Connect creates a new connection using the connector.
func (c *cockroachConnector) Connect(ctx context.Context) (driver.Conn, error) {
pgConn, err := c.pgConnector.Connect(ctx)
pgxConn, err := c.pgxConnector.Connect(ctx)
if err != nil {
return nil, err
}
if pgConnAll, ok := pgConn.(connAll); ok {
return &cockroachConn{pgConnAll}, nil
pgxStdlibConn, ok := pgxConn.(*stdlib.Conn)
if !ok {
return nil, errs.New("Conn from pgx is not a *stdlib.Conn??? T: %T", pgxConn)
}
return nil, errs.New("Underlying connector type %T does not implement connAll?!", pgConn)
return &cockroachConn{pgxStdlibConn}, nil
}
type connAll interface {
@ -73,7 +83,7 @@ type connAll interface {
// cockroachConn is a connection to a database. It is not used concurrently by multiple goroutines.
type cockroachConn struct {
underlying connAll
underlying *stdlib.Conn
}
// Assert that cockroachConn fulfills connAll.
@ -205,11 +215,8 @@ const (
)
func (c *cockroachConn) txnStatus() transactionStatus {
// access c.underlying -> c.underlying.(*pq.conn) -> (*c.underlying.(*pq.conn)).txnStatus
//
// this is of course brittle if lib/pq internals change, so a test is necessary to make
// sure we stay on the same page.
return transactionStatus(reflect.ValueOf(c.underlying).Elem().Field(4).Uint())
pgConn := c.underlying.Conn().PgConn()
return transactionStatus(pgConn.TxStatus())
}
func (c *cockroachConn) isInTransaction() bool {
@ -312,7 +319,7 @@ func (stmt *cockroachStmt) QueryContext(ctx context.Context, args []driver.Named
}
// translateName changes the scheme name in a `cockroach://` URL to
// `postgres://`, as that is what lib/pq will expect.
// `postgres://`, as that is what jackc/pgx will expect.
func translateName(name string) string {
if strings.HasPrefix(name, "cockroach://") {
name = "postgres://" + name[12:]
@ -323,7 +330,7 @@ func translateName(name string) string {
// NeedsRetry checks if the error code means a retry is needed,
// borrowed from code in crdb.
func NeedsRetry(err error) bool {
code := errCode(err)
code := pgutil.ErrorCode(err)
// 57P01 occurs when a CRDB node rejoins the cluster but is not ready to accept connections
// CRDB support recommended a retry at this point
@ -332,30 +339,8 @@ func NeedsRetry(err error) bool {
return code == "40001" || code == "CR000" || code == "57P01"
}
// borrowed from crdb
func errCode(err error) string {
switch t := errorCause(err).(type) {
case *pq.Error:
return string(t.Code)
default:
return ""
}
}
func errorCause(err error) error {
for err != nil {
cause := errors.Unwrap(err)
if cause == nil {
break
}
err = cause
}
return err
}
// Assert that Driver satisfies DriverContext.
var _ driver.DriverContext = &Driver{}
var defaultDriver = &Driver{}
func init() {
sql.Register("cockroach", &Driver{})
sql.Register("cockroach", defaultDriver)
}

View File

@ -25,7 +25,7 @@ const (
// the url with the provided scheme.
func ImplementationForScheme(scheme string) Implementation {
switch scheme {
case "postgres", "postgresql":
case "pgx", "postgres", "postgresql":
return Postgres
case "cockroach":
return Cockroach

View File

@ -5,12 +5,16 @@ package pgutil
import (
"context"
"errors"
"strings"
"time"
"github.com/lib/pq"
"github.com/jackc/pgtype"
"github.com/jackc/pgx/v4"
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
"storj.io/common/storj"
"storj.io/storj/private/dbutil"
"storj.io/storj/private/dbutil/dbschema"
"storj.io/storj/private/tagsql"
@ -20,6 +24,12 @@ var (
mon = monkit.Package()
)
const (
// pgErrorClassConstraintViolation is the class of PostgreSQL errors indicating
// integrity constraint violations.
pgErrorClassConstraintViolation = "23"
)
// OpenUnique opens a postgres database with a temporary unique schema, which will be cleaned up
// when closed. It is expected that this should normally be used by way of
// "storj.io/storj/private/dbutil/tempdb".OpenUnique() instead of calling it directly.
@ -32,14 +42,14 @@ func OpenUnique(ctx context.Context, connstr string, schemaPrefix string) (*dbut
schemaName := schemaPrefix + "-" + CreateRandomTestingSchemaName(8)
connStrWithSchema := ConnstrWithSchema(connstr, schemaName)
db, err := tagsql.Open("postgres", connStrWithSchema)
db, err := tagsql.Open("pgx", connStrWithSchema)
if err == nil {
// check that connection actually worked before trying CreateSchema, to make
// troubleshooting (lots) easier
err = db.PingContext(ctx)
}
if err != nil {
return nil, errs.New("failed to connect to %q with driver postgres: %w", connStrWithSchema, err)
return nil, errs.New("failed to connect to %q with driver pgx: %w", connStrWithSchema, err)
}
err = CreateSchema(ctx, db, schemaName)
@ -56,7 +66,7 @@ func OpenUnique(ctx context.Context, connstr string, schemaPrefix string) (*dbut
DB: db,
ConnStr: connStrWithSchema,
Schema: schemaName,
Driver: "postgres",
Driver: "pgx",
Implementation: dbutil.Postgres,
Cleanup: cleanup,
}, nil
@ -97,12 +107,161 @@ func CheckApplicationName(s string) (r string) {
// IsConstraintError checks if given error is about constraint violation
func IsConstraintError(err error) bool {
return errs.IsFunc(err, func(err error) bool {
if e, ok := err.(*pq.Error); ok {
if e.Code.Class() == "23" {
return true
}
}
return false
})
errCode := ErrorCode(err)
return strings.HasPrefix(errCode, pgErrorClassConstraintViolation)
}
// ErrorCode returns the 5-character PostgreSQL error code string associated
// with the given error, if any.
func ErrorCode(err error) string {
var sqlStateErr errWithSQLState
if errors.As(err, &sqlStateErr) {
return sqlStateErr.SQLState()
}
return ""
}
// errWithSQLState is an interface supported by error classes corresponding
// to PostgreSQL errors from certain drivers. This is satisfied, in particular,
// by pgx (*pgconn.PgError) and may be adopted by other types. An effort is
// apparently underway to get lib/pq to add this interface.
type errWithSQLState interface {
SQLState() string
}
// The following XArray() helper methods exist alongside similar methods in the
// jackc/pgtype library. The difference with the methods in pgtype is that they
// will accept any of a wide range of types. That is nice, but it comes with
// the potential that someone might pass in an invalid type; thus, those
// methods have to return (*pgtype.XArray, error).
//
// The methods here do not need to return an error because they require passing
// in the correct type to begin with.
//
// An alternative implementation for the following methods might look like
// calls to pgtype.ByteaArray() followed by `if err != nil { panic }` blocks.
// That would probably be ok, but we decided on this approach, as it ought to
// require fewer allocations and less time, in addition to having no error
// return.
// ByteaArray returns an object usable by pg drivers for passing a [][]byte slice
// into a database as type BYTEA[].
func ByteaArray(bytesArray [][]byte) *pgtype.ByteaArray {
pgtypeByteaArray := make([]pgtype.Bytea, len(bytesArray))
for i, byteSlice := range bytesArray {
pgtypeByteaArray[i].Bytes = byteSlice
pgtypeByteaArray[i].Status = pgtype.Present
}
return &pgtype.ByteaArray{
Elements: pgtypeByteaArray,
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(bytesArray)), LowerBound: 1}},
Status: pgtype.Present,
}
}
// StringArray returns an object usable by pg drivers for passing a []string slice
// into a database as type VARCHAR[].
func StringArray(stringSlice []string) *pgtype.VarcharArray {
pgtypeVarcharArray := make([]pgtype.Varchar, len(stringSlice))
for i, s := range stringSlice {
pgtypeVarcharArray[i].String = s
pgtypeVarcharArray[i].Status = pgtype.Present
}
return &pgtype.VarcharArray{
Elements: pgtypeVarcharArray,
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(stringSlice)), LowerBound: 1}},
Status: pgtype.Present,
}
}
// TimestampTZArray returns an object usable by pg drivers for passing a []time.Time
// slice into a database as type TIMESTAMPTZ[].
func TimestampTZArray(timeSlice []time.Time) *pgtype.TimestamptzArray {
pgtypeTimestamptzArray := make([]pgtype.Timestamptz, len(timeSlice))
for i, t := range timeSlice {
pgtypeTimestamptzArray[i].Time = t
pgtypeTimestamptzArray[i].Status = pgtype.Present
}
return &pgtype.TimestamptzArray{
Elements: pgtypeTimestamptzArray,
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(timeSlice)), LowerBound: 1}},
Status: pgtype.Present,
}
}
// Int4Array returns an object usable by pg drivers for passing a []int32 slice
// into a database as type INT4[].
func Int4Array(ints []int32) *pgtype.Int4Array {
pgtypeInt4Array := make([]pgtype.Int4, len(ints))
for i, someInt := range ints {
pgtypeInt4Array[i].Int = someInt
pgtypeInt4Array[i].Status = pgtype.Present
}
return &pgtype.Int4Array{
Elements: pgtypeInt4Array,
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(ints)), LowerBound: 1}},
Status: pgtype.Present,
}
}
// Int8Array returns an object usable by pg drivers for passing a []int64 slice
// into a database as type INT8[].
func Int8Array(bigInts []int64) *pgtype.Int8Array {
pgtypeInt8Array := make([]pgtype.Int8, len(bigInts))
for i, bigInt := range bigInts {
pgtypeInt8Array[i].Int = bigInt
pgtypeInt8Array[i].Status = pgtype.Present
}
return &pgtype.Int8Array{
Elements: pgtypeInt8Array,
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(bigInts)), LowerBound: 1}},
Status: pgtype.Present,
}
}
// Float8Array returns an object usable by pg drivers for passing a []float64 slice
// into a database as type FLOAT8[].
func Float8Array(floats []float64) *pgtype.Float8Array {
pgtypeFloat8Array := make([]pgtype.Float8, len(floats))
for i, someFloat := range floats {
pgtypeFloat8Array[i].Float = someFloat
pgtypeFloat8Array[i].Status = pgtype.Present
}
return &pgtype.Float8Array{
Elements: pgtypeFloat8Array,
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(floats)), LowerBound: 1}},
Status: pgtype.Present,
}
}
// NodeIDArray returns an object usable by pg drivers for passing a []storj.NodeID
// slice into a database as type BYTEA[].
func NodeIDArray(nodeIDs []storj.NodeID) *pgtype.ByteaArray {
if nodeIDs == nil {
return &pgtype.ByteaArray{Status: pgtype.Null}
}
pgtypeByteaArray := make([]pgtype.Bytea, len(nodeIDs))
for i, nodeID := range nodeIDs {
nodeIDCopy := nodeID
pgtypeByteaArray[i].Bytes = nodeIDCopy[:]
pgtypeByteaArray[i].Status = pgtype.Present
}
return &pgtype.ByteaArray{
Elements: pgtypeByteaArray,
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(nodeIDs)), LowerBound: 1}},
Status: pgtype.Present,
}
}
// QuoteIdentifier quotes an identifier for use in an interpolated SQL string.
func QuoteIdentifier(ident string) string {
return pgx.Identifier{ident}.Sanitize()
}
// UnquoteIdentifier is the analog of QuoteIdentifier.
func UnquoteIdentifier(quotedIdent string) string {
if len(quotedIdent) >= 2 && quotedIdent[0] == '"' && quotedIdent[len(quotedIdent)-1] == '"' {
quotedIdent = strings.ReplaceAll(quotedIdent[1:len(quotedIdent)-1], "\"\"", "\"")
}
return quotedIdent
}

View File

@ -9,7 +9,7 @@ import (
"regexp"
"strings"
"github.com/lib/pq"
"github.com/jackc/pgtype"
"github.com/zeebo/errs"
"storj.io/storj/private/dbutil/dbschema"
@ -97,10 +97,15 @@ func QuerySchema(ctx context.Context, db dbschema.Queryer) (*dbschema.Schema, er
for rows.Next() {
var tableName, constraintName, constraintType string
var columns pq.StringArray
var columnsArray pgtype.VarcharArray
var columns []string
var definition string
err := rows.Scan(&tableName, &constraintName, &constraintType, &columns, &definition)
err := rows.Scan(&tableName, &constraintName, &constraintType, &columnsArray, &definition)
if err != nil {
return err
}
err = columnsArray.AssignTo(&columns)
if err != nil {
return err
}
@ -108,7 +113,7 @@ func QuerySchema(ctx context.Context, db dbschema.Queryer) (*dbschema.Schema, er
switch constraintType {
case "p": // primary key
table := schema.EnsureTable(tableName)
table.PrimaryKey = ([]string)(columns)
table.PrimaryKey = columns
case "f": // foreign key
if len(columns) != 1 {
return fmt.Errorf("expected one column, got: %q", columns)
@ -184,14 +189,6 @@ var rxPostgresForeignKey = regexp.MustCompile(
`(?:\s*ON DELETE (CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION))?$`,
)
// UnquoteIdentifier is the analog of pq.QuoteIdentifier.
func UnquoteIdentifier(quotedIdent string) string {
if len(quotedIdent) >= 2 && quotedIdent[0] == '"' && quotedIdent[len(quotedIdent)-1] == '"' {
quotedIdent = strings.ReplaceAll(quotedIdent[1:len(quotedIdent)-1], "\"\"", "\"")
}
return quotedIdent
}
var (
rxIndex = regexp.MustCompile(`^CREATE( UNIQUE)? INDEX (.*) ON .*\.(.*) USING btree \((.*)\)$`)
indexDirRemove = strings.NewReplacer(" ASC", "", " DESC", "")

View File

@ -6,7 +6,7 @@ package pgutil_test
import (
"testing"
_ "github.com/lib/pq"
_ "github.com/jackc/pgx/v4/stdlib"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -11,8 +11,6 @@ import (
"encoding/hex"
"net/url"
"strings"
"github.com/lib/pq"
)
// CreateRandomTestingSchemaName creates a random schema name string.
@ -32,7 +30,7 @@ func ConnstrWithSchema(connstr, schema string) string {
} else {
connstr += "?options="
}
return connstr + url.QueryEscape("--search_path="+pq.QuoteIdentifier(schema))
return connstr + url.QueryEscape("--search_path="+QuoteIdentifier(schema))
}
// ParseSchemaFromConnstr returns the name of the schema parsed from the
@ -43,7 +41,7 @@ func ParseSchemaFromConnstr(connstr string) (string, error) {
return "", err
}
queryValues := url.Query()
// this is the Proper™ way to encode search_path in a pq connection string
// this is the Proper™ way to encode search_path in a pg connection string
options := queryValues["options"]
for _, option := range options {
if strings.HasPrefix(option, "--search_path=") {
@ -60,7 +58,7 @@ func ParseSchemaFromConnstr(connstr string) (string, error) {
// QuoteSchema quotes schema name for
func QuoteSchema(schema string) string {
return pq.QuoteIdentifier(schema)
return QuoteIdentifier(schema)
}
// Execer is for executing sql

View File

@ -1,12 +1,11 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package satellitedb
package pgutil
import (
"testing"
"github.com/lib/pq"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -20,11 +19,11 @@ func TestPostgresNodeIDsArray(t *testing.T) {
ids[i] = testrand.NodeID()
}
got, err := postgresNodeIDList(ids).Value() // returns a []byte
got, err := NodeIDArray(ids).Value() // returns a string
require.NoError(t, err)
expected, err := pq.ByteaArray(ids.Bytes()).Value() // returns a string
expected, err := ByteaArray(ids.Bytes()).Value() // returns a string
require.NoError(t, err)
assert.Equal(t, expected.(string), string(got.([]byte)))
assert.Equal(t, expected.(string), got.(string))
}

View File

@ -19,8 +19,13 @@ func SplitConnStr(s string) (driver string, source string, implementation Implem
source = parts[1]
implementation = ImplementationForScheme(parts[0])
if implementation == Postgres || implementation == Cockroach {
source = s // postgres and cockroach want full URLS for their DSNs
switch implementation {
case Postgres:
source = s // postgres wants full URLS for its DSN
driver = "pgx"
case Cockroach:
source = s // cockroach wants full URLS for its DSN
driver = "pgxcockroach"
}
return driver, source, implementation, nil
}

View File

@ -6,7 +6,7 @@ package sqliteutil_test
import (
"testing"
_ "github.com/lib/pq"
_ "github.com/jackc/pgx/v4/stdlib"
_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -10,10 +10,10 @@ import (
"database/sql"
"time"
"github.com/lib/pq"
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
"storj.io/storj/private/dbutil/pgutil"
"storj.io/storj/private/tagsql"
)
@ -40,7 +40,7 @@ func WithTx(ctx context.Context, db tagsql.DB, txOpts *sql.TxOptions, fn func(co
if dur := time.Since(start); dur < 5*time.Minute && i < 10 {
// even though the resources (duration and count) allow us to issue a retry,
// we only should if the error claims we should.
if code := errCode(err); code == "CR000" || code == "40001" {
if code := pgutil.ErrorCode(err); code == "CR000" || code == "40001" {
continue
}
} else {
@ -74,16 +74,3 @@ func withTxOnce(ctx context.Context, db tagsql.DB, txOpts *sql.TxOptions, fn fun
return fn(ctx, tx), nil
}
// errCode returns the error code associated with any postgres error in the chain of
// errors walked by unwrapping.
func errCode(err error) (code string) {
errs.IsFunc(err, func(err error) bool {
if pgerr, ok := err.(*pq.Error); ok {
code = string(pgerr.Code)
return true
}
return false
})
return code
}

View File

@ -7,7 +7,7 @@ import (
"strconv"
"testing"
_ "github.com/lib/pq"
_ "github.com/jackc/pgx/v4/stdlib"
_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -147,7 +147,7 @@ func TestMultipleMigrationSqlite(t *testing.T) {
func TestMultipleMigrationPostgres(t *testing.T) {
connstr := pgtest.PickPostgres(t)
db, err := tagsql.Open("postgres", connstr)
db, err := tagsql.Open("pgx", connstr)
require.NoError(t, err)
defer func() { assert.NoError(t, db.Close()) }()
@ -223,7 +223,7 @@ func TestFailedMigrationSqlite(t *testing.T) {
func TestFailedMigrationPostgres(t *testing.T) {
connstr := pgtest.PickPostgres(t)
db, err := tagsql.Open("postgres", connstr)
db, err := tagsql.Open("pgx", connstr)
require.NoError(t, err)
defer func() { assert.NoError(t, db.Close()) }()

View File

@ -6,7 +6,7 @@ package tagsql_test
import (
"testing"
_ "github.com/lib/pq"
_ "github.com/jackc/pgx/v4/stdlib"
_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/require"
@ -33,7 +33,7 @@ func run(t *testing.T, fn func(*testcontext.Context, *testing.T, tagsql.DB, tags
fn(ctx, t, db, tagsql.SupportBasic)
})
t.Run("lib-pq-postgres", func(t *testing.T) {
t.Run("jackc-pgx-postgres", func(t *testing.T) {
connstr := pgtest.PickPostgres(t)
ctx := testcontext.New(t)
@ -49,7 +49,7 @@ func run(t *testing.T, fn func(*testcontext.Context, *testing.T, tagsql.DB, tags
fn(ctx, t, db.DB, tagsql.SupportNone)
})
t.Run("lib-pq-cockroach", func(t *testing.T) {
t.Run("jackc-pgx-cockroach", func(t *testing.T) {
connstr := pgtest.PickCockroach(t)
ctx := testcontext.New(t)

View File

@ -22,7 +22,8 @@ import (
// except in transactions. For them, we need to disable.
// https://github.com/mattn/go-sqlite3/issues/769
//
// Currently we don't have data on whether github.com/jackc/pgx supports them properly.
// So far, we believe that github.com/jackc/pgx supports contexts
// and cancellations properly.
// ContextSupport returns the level of context support a driver has.
type ContextSupport byte
@ -64,6 +65,8 @@ func DetectContextSupport(db *sql.DB) (ContextSupport, error) {
// internally uses lib/pq
typ.PkgPath() == "storj.io/storj/private/dbutil/cockroachutil" && typ.Name() == "Driver":
return SupportNone, nil
case typ.PkgPath() == "github.com/jackc/pgx/v4/stdlib" && typ.Name() == "Driver":
return SupportTransactions, nil
default:
return SupportNone, errs.New("sql driver %q %q unsupported", typ.PkgPath(), typ.Name())
}

View File

@ -75,7 +75,7 @@ func TestCouponRepository(t *testing.T) {
date, err := couponsRepo.GetLatest(ctx, coupon.ID)
require.NoError(t, err)
// go and postgres has different precision. go - nanoseconds, postgres micro
require.Equal(t, date.UTC(), now.Round(time.Microsecond))
require.Equal(t, date.UTC(), now.Truncate(time.Microsecond))
})
t.Run("total usage", func(t *testing.T) {

View File

@ -1289,6 +1289,7 @@ func testRepairGracefullyExited(t *testing.T, inMemoryRepair bool) {
metainfo := satellite.Metainfo.Service
listResponse, _, err := metainfo.List(ctx, "", "", true, 0, 0)
require.NoError(t, err)
require.NotNil(t, listResponse)
var path string
var pointer *pb.Pointer

View File

@ -63,8 +63,8 @@ func TestOffer_Database(t *testing.T) {
for i := range validOffers {
new, err := planet.Satellites[0].DB.Rewards().Create(ctx, &validOffers[i])
require.NoError(t, err)
new.ExpiresAt = new.ExpiresAt.Round(time.Microsecond)
new.CreatedAt = new.CreatedAt.Round(time.Microsecond)
new.ExpiresAt = new.ExpiresAt.Truncate(time.Microsecond)
new.CreatedAt = new.CreatedAt.Truncate(time.Microsecond)
all, err := planet.Satellites[0].DB.Rewards().ListAll(ctx)
require.NoError(t, err)

View File

@ -10,11 +10,11 @@ import (
"sort"
"time"
"github.com/lib/pq"
"github.com/zeebo/errs"
"storj.io/common/memory"
"storj.io/common/uuid"
"storj.io/storj/private/dbutil/pgutil"
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/payments"
"storj.io/storj/satellite/payments/coinpayments"
@ -435,7 +435,7 @@ func (coupons *coupons) activeUserWithProjectAndWithoutCoupon(ctx context.Contex
WHERE users_with_projects.id NOT IN (
SELECT user_id FROM coupons WHERE type = ?
)
`), pq.ByteaArray(userIDs), console.Active, payments.CouponTypePromotional)
`), pgutil.ByteaArray(userIDs), console.Active, payments.CouponTypePromotional)
if err != nil {
return nil, err
}

View File

@ -1,12 +1,12 @@
#!/bin/sh
dbx schema -d postgres -d cockroach satellitedb.dbx .
dbx golang -d postgres -d cockroach -p dbx -t templates satellitedb.dbx .
dbx schema -d pgx -d pgxcockroach satellitedb.dbx .
dbx golang -d pgx -d pgxcockroach -p dbx -t templates satellitedb.dbx .
( echo '//lint:file-ignore * generated file'; cat satellitedb.dbx.go ) > satellitedb.dbx.go.tmp && mv satellitedb.dbx.go.tmp satellitedb.dbx.go
gofmt -r "*sql.Tx -> tagsql.Tx" -w satellitedb.dbx.go
gofmt -r "*sql.Rows -> tagsql.Rows" -w satellitedb.dbx.go
perl -0777 -pi \
-e 's,\t"github.com/lib/pq"\n\),\t"github.com/lib/pq"\n\n\t"storj.io/storj/private/tagsql"\n\),' \
-e 's,\t_ "github.com/jackc/pgx/v4/stdlib"\n\),\t_ "github.com/jackc/pgx/v4/stdlib"\n\n\t"storj.io/storj/private/tagsql"\n\),' \
satellitedb.dbx.go
perl -0777 -pi \
-e 's/type DB struct \{\n\t\*sql\.DB/type DB struct \{\n\ttagsql.DB/' \

File diff suppressed because it is too large Load Diff

View File

@ -10,10 +10,10 @@ import (
"sort"
"time"
"github.com/lib/pq"
"github.com/zeebo/errs"
"storj.io/common/storj"
"storj.io/storj/private/dbutil/pgutil"
"storj.io/storj/private/tagsql"
"storj.io/storj/satellite/gracefulexit"
"storj.io/storj/satellite/satellitedb/dbx"
@ -98,7 +98,7 @@ func (db *gracefulexitDB) Enqueue(ctx context.Context, items []gracefulexit.Tran
_, err = db.db.ExecContext(ctx, db.db.Rebind(`
INSERT INTO graceful_exit_transfer_queue(node_id, path, piece_num, root_piece_id, durability_ratio, queued_at)
SELECT unnest($1::bytea[]), unnest($2::bytea[]), unnest($3::integer[]), unnest($4::bytea[]), unnest($5::float8[]), $6
ON CONFLICT DO NOTHING;`), postgresNodeIDList(nodeIDs), pq.ByteaArray(paths), pq.Array(pieceNums), pq.ByteaArray(rootPieceIDs), pq.Array(durabilities), time.Now().UTC())
ON CONFLICT DO NOTHING;`), pgutil.NodeIDArray(nodeIDs), pgutil.ByteaArray(paths), pgutil.Int4Array(pieceNums), pgutil.ByteaArray(rootPieceIDs), pgutil.Float8Array(durabilities), time.Now().UTC())
return Error.Wrap(err)
}

View File

@ -8,7 +8,6 @@ import (
"fmt"
"strings"
"github.com/lib/pq"
"github.com/zeebo/errs"
"go.uber.org/zap"
@ -54,7 +53,7 @@ func (db *satelliteDB) MigrateToLatest(ctx context.Context) error {
}
_, err := db.Exec(ctx, fmt.Sprintf(`CREATE DATABASE IF NOT EXISTS %s;`,
pq.QuoteIdentifier(dbName)))
pgutil.QuoteIdentifier(dbName)))
if err != nil {
return errs.Wrap(err)
}
@ -104,7 +103,7 @@ func (db *satelliteDB) TestingMigrateToLatest(ctx context.Context) error {
return ErrMigrateMinVersion.New("error querying current database: %+v", err)
}
_, err := db.Exec(ctx, fmt.Sprintf(`CREATE DATABASE IF NOT EXISTS %s;`, pq.QuoteIdentifier(dbName)))
_, err := db.Exec(ctx, fmt.Sprintf(`CREATE DATABASE IF NOT EXISTS %s;`, pgutil.QuoteIdentifier(dbName)))
if err != nil {
return ErrMigrateMinVersion.Wrap(err)
}

View File

@ -5,6 +5,7 @@ package satellitedb_test
import (
"context"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
@ -12,7 +13,7 @@ import (
"testing"
"time"
"github.com/lib/pq"
"github.com/jackc/pgconn"
"github.com/stretchr/testify/require"
"github.com/zeebo/errs"
"go.uber.org/zap"
@ -57,8 +58,9 @@ func loadSnapshots(ctx context.Context, connstr, dbxscript string) (*dbschema.Sn
snapshot, err := loadSnapshotFromSQL(ctx, connstr, string(scriptData))
if err != nil {
if pqErr, ok := err.(*pq.Error); ok && pqErr.Detail != "" {
return fmt.Errorf("Version %d error: %v\nDetail: %s\nHint: %s", version, pqErr, pqErr.Detail, pqErr.Hint)
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
return fmt.Errorf("Version %d error: %v\nDetail: %s\nHint: %s", version, pgErr, pgErr.Detail, pgErr.Hint)
}
return fmt.Errorf("Version %d error: %+v", version, err)
}

View File

@ -22,7 +22,7 @@ type nodeAPIVersionDB struct {
func (db *nodeAPIVersionDB) UpdateVersionAtLeast(ctx context.Context, id storj.NodeID, version nodeapiversion.Version) (err error) {
defer mon.Task()(&ctx)(&err)
// try to create a row at the version
err = db.db.CreateNoReturn_NodeApiVersion(ctx,
err = db.db.ReplaceNoReturn_NodeApiVersion(ctx,
dbx.NodeApiVersion_Id(id.Bytes()),
dbx.NodeApiVersion_ApiVersion(int(version)))
if errs.IsFunc(err, dbx.IsConstraintError) {

View File

@ -10,12 +10,12 @@ import (
"strings"
"time"
"github.com/lib/pq"
"github.com/zeebo/errs"
"storj.io/common/pb"
"storj.io/common/storj"
"storj.io/private/version"
"storj.io/storj/private/dbutil/pgutil"
"storj.io/storj/satellite/overlay"
)
@ -207,14 +207,14 @@ func nodeSelectionCondition(ctx context.Context, criteria *overlay.NodeCriteria,
if len(excludedIDs) > 0 {
conds.add(
`not (id = any(?::bytea[]))`,
postgresNodeIDList(excludedIDs),
pgutil.NodeIDArray(excludedIDs),
)
}
if criteria.DistinctIP {
if len(excludedNetworks) > 0 {
conds.add(
`not (last_net = any(?::text[]))`,
pq.Array(excludedNetworks),
pgutil.StringArray(excludedNetworks),
)
}
conds.add(`last_net <> ''`)

View File

@ -8,7 +8,6 @@ import (
"database/sql"
"time"
"github.com/lib/pq"
"github.com/zeebo/errs"
"go.uber.org/zap"
@ -270,7 +269,7 @@ func (db *ordersDB) ProcessOrders(ctx context.Context, requests []*orders.Proces
serial_numbers sn,
unnest($1::bytea[]) WITH ORDINALITY AS request(serial_number, i)
WHERE request.serial_number = sn.serial_number
`, pq.ByteaArray(serialNums))
`, pgutil.ByteaArray(serialNums))
if err != nil {
return nil, Error.Wrap(err)
}
@ -378,13 +377,18 @@ func (db *ordersDB) ProcessOrders(ctx context.Context, requests []*orders.Proces
return nil, Error.New("invalid dbType: %v", db.db.driver)
}
actionNumArray := make([]int32, len(actionArray))
for i, num := range actionArray {
actionNumArray[i] = int32(num)
}
_, err = db.db.ExecContext(ctx, stmt,
storageNodeID.Bytes(),
pq.ByteaArray(bucketIDArray),
pq.ByteaArray(serialNumArray),
pq.Array(actionArray),
pq.Array(settledArray),
pq.Array(expiresAtArray),
pgutil.ByteaArray(bucketIDArray),
pgutil.ByteaArray(serialNumArray),
pgutil.Int4Array(actionNumArray),
pgutil.Int8Array(settledArray),
pgutil.TimestampTZArray(expiresAtArray),
)
if err != nil {
return nil, Error.Wrap(err)
@ -458,9 +462,9 @@ func (tx *ordersDBTx) UpdateBucketBandwidthBatch(ctx context.Context, intervalSt
allocated = bucket_bandwidth_rollups.allocated + EXCLUDED.allocated,
inline = bucket_bandwidth_rollups.inline + EXCLUDED.inline,
settled = bucket_bandwidth_rollups.settled + EXCLUDED.settled`,
pq.ByteaArray(bucketNames), pq.ByteaArray(projectIDs),
pgutil.ByteaArray(bucketNames), pgutil.ByteaArray(projectIDs),
intervalStart, defaultIntervalSeconds,
pq.Array(actionSlice), pq.Array(inlineSlice), pq.Array(allocatedSlice), pq.Array(settledSlice))
pgutil.Int4Array(actionSlice), pgutil.Int8Array(inlineSlice), pgutil.Int8Array(allocatedSlice), pgutil.Int8Array(settledSlice))
if err != nil {
tx.log.Error("Bucket bandwidth rollup batch flush failed.", zap.Error(err))
}
@ -486,7 +490,7 @@ func (tx *ordersDBTx) UpdateBucketBandwidthBatch(ctx context.Context, intervalSt
ON CONFLICT(project_id, interval_month)
DO UPDATE SET egress_allocated = project_bandwidth_rollups.egress_allocated + EXCLUDED.egress_allocated::bigint;
`,
pq.ByteaArray(projectRUIDs), projectInterval, pq.Array(projectRUAllocated))
pgutil.ByteaArray(projectRUIDs), projectInterval, pgutil.Int8Array(projectRUAllocated))
if err != nil {
tx.log.Error("Project bandwidth rollup batch flush failed.", zap.Error(err))
}
@ -532,9 +536,9 @@ func (tx *ordersDBTx) UpdateStoragenodeBandwidthBatch(ctx context.Context, inter
DO UPDATE SET
allocated = storagenode_bandwidth_rollups.allocated + EXCLUDED.allocated,
settled = storagenode_bandwidth_rollups.settled + EXCLUDED.settled`,
postgresNodeIDList(storageNodeIDs),
pgutil.NodeIDArray(storageNodeIDs),
intervalStart, defaultIntervalSeconds,
pq.Array(actionSlice), pq.Array(allocatedSlice), pq.Array(settledSlice))
pgutil.Int4Array(actionSlice), pgutil.Int8Array(allocatedSlice), pgutil.Int8Array(settledSlice))
if err != nil {
tx.log.Error("Storagenode bandwidth rollup batch flush failed.", zap.Error(err))
}
@ -583,9 +587,9 @@ func (tx *ordersDBTx) CreateConsumedSerialsBatch(ctx context.Context, consumedSe
}
_, err = tx.tx.Tx.ExecContext(ctx, stmt,
pq.ByteaArray(storageNodeIDSlice),
pq.ByteaArray(serialNumberSlice),
pq.Array(expiresAtSlice),
pgutil.ByteaArray(storageNodeIDSlice),
pgutil.ByteaArray(serialNumberSlice),
pgutil.TimestampTZArray(expiresAtSlice),
)
return Error.Wrap(err)
}

View File

@ -11,7 +11,6 @@ import (
"sort"
"time"
"github.com/lib/pq"
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
"go.uber.org/zap"
@ -19,6 +18,7 @@ import (
"storj.io/common/pb"
"storj.io/common/storj"
"storj.io/private/version"
"storj.io/storj/private/dbutil/pgutil"
"storj.io/storj/private/tagsql"
"storj.io/storj/satellite/overlay"
"storj.io/storj/satellite/satellitedb/dbx"
@ -109,7 +109,7 @@ func (cache *overlaycache) GetNodesNetwork(ctx context.Context, nodeIDs []storj.
rows, err = cache.db.Query(ctx, cache.db.Rebind(`
SELECT last_net FROM nodes
WHERE id = any($1::bytea[])
`), postgresNodeIDList(nodeIDs),
`), pgutil.NodeIDArray(nodeIDs),
)
if err != nil {
return nil, err
@ -158,7 +158,7 @@ func (cache *overlaycache) GetOnlineNodesForGetDelete(ctx context.Context, nodeI
AND disqualified IS NULL
AND exit_finished_at IS NULL
AND last_contact_success > $2
`), postgresNodeIDList(nodeIDs), time.Now().Add(-onlineWindow))
`), pgutil.NodeIDArray(nodeIDs), time.Now().Add(-onlineWindow))
if err != nil {
return nil, err
}
@ -198,7 +198,7 @@ func (cache *overlaycache) KnownOffline(ctx context.Context, criteria *overlay.N
SELECT id FROM nodes
WHERE id = any($1::bytea[])
AND last_contact_success < $2
`), postgresNodeIDList(nodeIds), time.Now().Add(-criteria.OnlineWindow),
`), pgutil.NodeIDArray(nodeIds), time.Now().Add(-criteria.OnlineWindow),
)
if err != nil {
return nil, err
@ -233,7 +233,7 @@ func (cache *overlaycache) KnownUnreliableOrOffline(ctx context.Context, criteri
AND unknown_audit_suspended IS NULL
AND exit_finished_at IS NULL
AND last_contact_success > $2
`), postgresNodeIDList(nodeIds), time.Now().Add(-criteria.OnlineWindow),
`), pgutil.NodeIDArray(nodeIds), time.Now().Add(-criteria.OnlineWindow),
)
if err != nil {
return nil, err
@ -274,7 +274,7 @@ func (cache *overlaycache) KnownReliable(ctx context.Context, onlineWindow time.
AND unknown_audit_suspended IS NULL
AND exit_finished_at IS NULL
AND last_contact_success > $2
`), postgresNodeIDList(nodeIDs), time.Now().Add(-onlineWindow),
`), pgutil.NodeIDArray(nodeIDs), time.Now().Add(-onlineWindow),
)
if err != nil {
return nil, err
@ -682,7 +682,7 @@ func (cache *overlaycache) UpdatePieceCounts(ctx context.Context, pieceCounts ma
SELECT unnest($1::bytea[]) as id, unnest($2::bigint[]) as count
) as update
WHERE nodes.id = update.id
`, postgresNodeIDList(nodeIDs), pq.Array(countNumbers))
`, pgutil.NodeIDArray(nodeIDs), pgutil.Int8Array(countNumbers))
return Error.Wrap(err)
}

View File

@ -9,13 +9,13 @@ import (
"fmt"
"time"
"github.com/lib/pq"
"github.com/zeebo/errs"
"storj.io/common/memory"
"storj.io/common/pb"
"storj.io/common/uuid"
"storj.io/storj/private/dbutil"
"storj.io/storj/private/dbutil/pgutil"
"storj.io/storj/satellite/accounting"
"storj.io/storj/satellite/satellitedb/dbx"
)
@ -35,17 +35,17 @@ func (db *ProjectAccounting) SaveTallies(ctx context.Context, intervalStart time
return nil
}
var bucketNames, projectIDs [][]byte
var inlineBytes, remoteBytes, metadataSizes []uint64
var remoteSegments, inlineSegments, objectCounts []uint
var inlineBytes, remoteBytes, metadataSizes []int64
var remoteSegments, inlineSegments, objectCounts []int64
for _, info := range bucketTallies {
bucketNames = append(bucketNames, info.BucketName)
projectIDs = append(projectIDs, info.ProjectID[:])
inlineBytes = append(inlineBytes, uint64(info.InlineBytes))
remoteBytes = append(remoteBytes, uint64(info.RemoteBytes))
remoteSegments = append(remoteSegments, uint(info.RemoteSegments))
inlineSegments = append(inlineSegments, uint(info.InlineSegments))
objectCounts = append(objectCounts, uint(info.ObjectCount))
metadataSizes = append(metadataSizes, uint64(info.MetadataSize))
inlineBytes = append(inlineBytes, info.InlineBytes)
remoteBytes = append(remoteBytes, info.RemoteBytes)
remoteSegments = append(remoteSegments, info.RemoteSegments)
inlineSegments = append(inlineSegments, info.InlineSegments)
objectCounts = append(objectCounts, info.ObjectCount)
metadataSizes = append(metadataSizes, info.MetadataSize)
}
_, err = db.db.DB.ExecContext(ctx, db.db.Rebind(`
INSERT INTO bucket_storage_tallies (
@ -61,10 +61,10 @@ func (db *ProjectAccounting) SaveTallies(ctx context.Context, intervalStart time
unnest($6::int[]), unnest($7::int[]),
unnest($8::int[]), unnest($9::int8[])`),
intervalStart,
pq.ByteaArray(bucketNames), pq.ByteaArray(projectIDs),
pq.Array(inlineBytes), pq.Array(remoteBytes),
pq.Array(remoteSegments), pq.Array(inlineSegments),
pq.Array(objectCounts), pq.Array(metadataSizes))
pgutil.ByteaArray(bucketNames), pgutil.ByteaArray(projectIDs),
pgutil.Int8Array(inlineBytes), pgutil.Int8Array(remoteBytes),
pgutil.Int8Array(remoteSegments), pgutil.Int8Array(inlineSegments),
pgutil.Int8Array(objectCounts), pgutil.Int8Array(metadataSizes))
return Error.Wrap(err)
}

View File

@ -8,11 +8,11 @@ import (
"database/sql"
"time"
"github.com/lib/pq"
"github.com/zeebo/errs"
"storj.io/common/storj"
"storj.io/storj/private/dbutil"
"storj.io/storj/private/dbutil/pgutil"
"storj.io/storj/satellite/accounting"
"storj.io/storj/satellite/compensation"
"storj.io/storj/satellite/satellitedb/dbx"
@ -45,7 +45,7 @@ func (db *StoragenodeAccounting) SaveTallies(ctx context.Context, latestTally ti
$1,
unnest($2::bytea[]), unnest($3::float8[])`),
latestTally,
postgresNodeIDList(nodeIDs), pq.Array(totals))
pgutil.NodeIDArray(nodeIDs), pgutil.Float8Array(totals))
if err != nil {
return err
}

View File

@ -1,52 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package satellitedb
import (
"database/sql/driver"
"storj.io/common/storj"
)
type postgresNodeIDList storj.NodeIDList
// Value converts a NodeIDList to a postgres array
func (nodes postgresNodeIDList) Value() (driver.Value, error) {
const hextable = "0123456789abcdef"
if nodes == nil {
return nil, nil
}
if len(nodes) == 0 {
return []byte("{}"), nil
}
var wp, x int
out := make([]byte, 2+len(nodes)*(6+storj.NodeIDSize*2)-1)
x = copy(out[wp:], []byte(`{"\\x`))
wp += x
for i := range nodes {
for _, v := range nodes[i] {
out[wp] = hextable[v>>4]
out[wp+1] = hextable[v&0xf]
wp += 2
}
if i+1 < len(nodes) {
x = copy(out[wp:], []byte(`","\\x`))
wp += x
}
}
x = copy(out[wp:], `"}`)
wp += x
if wp != len(out) {
panic("unreachable")
}
return out, nil
}

View File

@ -9,7 +9,6 @@ import (
"database/sql"
"sort"
"github.com/lib/pq"
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
@ -137,7 +136,7 @@ func (client *Client) getAllOnce(ctx context.Context, keys storage.Keys) (values
ON (pd.fullpath = pk.request)
ORDER BY pk.ord
`
rows, err := client.db.QueryContext(ctx, q, pq.ByteaArray(keys.ByteSlices()))
rows, err := client.db.QueryContext(ctx, q, pgutil.ByteaArray(keys.ByteSlices()))
if err != nil {
return nil, err
}
@ -212,7 +211,7 @@ func (client *Client) deleteMultipleOnce(ctx context.Context, keys storage.Keys)
DELETE FROM pathdata
WHERE fullpath = any($1::BYTEA[])
RETURNING fullpath, metadata`,
pq.ByteaArray(keys.ByteSlices()))
pgutil.ByteaArray(keys.ByteSlices()))
if err != nil {
return nil, err
}

View File

@ -6,7 +6,6 @@ import (
"context"
"testing"
_ "github.com/lib/pq"
"github.com/stretchr/testify/require"
"storj.io/common/testcontext"

View File

@ -164,6 +164,13 @@ func (oci *orderedCockroachIterator) doNextQuery(ctx context.Context) (_ tagsql.
gt := ">"
start := oci.lastKeySeen
largestKey := []byte(oci.largestKey)
if largestKey == nil {
// github.com/lib/pq would treat nil as an empty bytea array, while
// github.com/jackc/pgx will treat nil as NULL. Make an explicit empty
// byte array so that they'll work the same.
largestKey = []byte{}
}
if len(start) == 0 {
start = oci.opts.First
gt = ">="
@ -178,5 +185,5 @@ func (oci *orderedCockroachIterator) doNextQuery(ctx context.Context) (_ tagsql.
WHERE pd.fullpath %s $1:::BYTEA
AND ($2:::BYTEA = '':::BYTEA OR pd.fullpath < $2:::BYTEA)
LIMIT $3
`, gt), start, []byte(oci.largestKey), oci.batchSize)
`, gt), start, largestKey, oci.batchSize)
}

View File

@ -7,9 +7,9 @@ import (
"context"
"fmt"
"github.com/lib/pq"
"github.com/zeebo/errs"
"storj.io/storj/private/dbutil/pgutil"
"storj.io/storj/private/tagsql"
)
@ -34,6 +34,6 @@ func PrepareDB(ctx context.Context, db tagsql.DB) (err error) {
metadata BYTEA NOT NULL,
bucket BYTEA
);
`, pq.QuoteIdentifier(dbName)))
`, pgutil.QuoteIdentifier(dbName)))
return errs.Wrap(err)
}

View File

@ -8,7 +8,6 @@ import (
"database/sql"
"sort"
"github.com/lib/pq"
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
@ -34,7 +33,7 @@ type Client struct {
func New(dbURL string) (*Client, error) {
dbURL = pgutil.CheckApplicationName(dbURL)
db, err := tagsql.Open("postgres", dbURL)
db, err := tagsql.Open("pgx", dbURL)
if err != nil {
return nil, err
}
@ -119,7 +118,7 @@ func (client *Client) GetAll(ctx context.Context, keys storage.Keys) (_ storage.
ON (pd.fullpath = pk.request)
ORDER BY pk.ord
`
rows, err := client.db.Query(ctx, q, pq.ByteaArray(keys.ByteSlices()))
rows, err := client.db.Query(ctx, q, pgutil.ByteaArray(keys.ByteSlices()))
if err != nil {
return nil, errs.Wrap(err)
}
@ -174,7 +173,7 @@ func (client *Client) DeleteMultiple(ctx context.Context, keys []storage.Key) (_
DELETE FROM pathdata
WHERE fullpath = any($1::BYTEA[])
RETURNING fullpath, metadata`,
pq.ByteaArray(storage.Keys(keys).ByteSlices()))
pgutil.ByteaArray(storage.Keys(keys).ByteSlices()))
if err != nil {
return nil, err
}

View File

@ -8,13 +8,13 @@ import (
"strings"
"testing"
"github.com/lib/pq"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/stdlib"
"github.com/stretchr/testify/require"
"github.com/zeebo/errs"
"storj.io/common/testcontext"
"storj.io/storj/private/dbutil/pgtest"
"storj.io/storj/private/dbutil/txutil"
"storj.io/storj/private/tagsql"
"storj.io/storj/storage"
"storj.io/storj/storage/testsuite"
@ -102,30 +102,38 @@ func BenchmarkSuite(b *testing.B) {
testsuite.RunBenchmarks(b, store)
}
func bulkImport(ctx context.Context, db tagsql.DB, iter storage.Iterator) error {
return txutil.WithTx(ctx, db, nil, func(ctx context.Context, txn tagsql.Tx) (err error) {
stmt, err := txn.Prepare(ctx, pq.CopyIn("pathdata", "fullpath", "metadata"))
if err != nil {
return errs.New("Failed to initialize COPY FROM: %v", err)
}
defer func() {
err2 := stmt.Close()
if err2 != nil {
err = errs.Combine(err, errs.New("Failed to close COPY FROM statement: %v", err2))
}
}()
type bulkImportCopyFromSource struct {
ctx context.Context
iter storage.Iterator
item storage.ListItem
}
var item storage.ListItem
for iter.Next(ctx, &item) {
if _, err := stmt.Exec(ctx, []byte(item.Key), []byte(item.Value)); err != nil {
return err
}
}
if _, err = stmt.Exec(ctx); err != nil {
return errs.New("Failed to complete COPY FROM: %v", err)
}
return nil
})
func (bs *bulkImportCopyFromSource) Next() bool {
return bs.iter.Next(bs.ctx, &bs.item)
}
func (bs *bulkImportCopyFromSource) Values() ([]interface{}, error) {
return []interface{}{bs.item.Key, bs.item.Value}, nil
}
func (bs *bulkImportCopyFromSource) Err() error {
// we can't determine this from storage.Iterator, I guess
return nil
}
func bulkImport(ctx context.Context, db tagsql.DB, iter storage.Iterator) (err error) {
defer mon.Task()(&ctx)(&err)
pgxConn, err := stdlib.AcquireConn(db.Internal())
if err != nil {
return err
}
defer func() {
err = errs.Combine(err, stdlib.ReleaseConn(db.Internal(), pgxConn))
}()
importSource := &bulkImportCopyFromSource{iter: iter}
_, err = pgxConn.CopyFrom(ctx, pgx.Identifier{"pathdata"}, []string{"fullpath", "metadata"}, importSource)
return err
}
func bulkDeleteAll(ctx context.Context, db tagsql.DB) error {

View File

@ -156,6 +156,13 @@ func (opi *orderedPostgresIterator) doNextQuery(ctx context.Context) (_ tagsql.R
start = storage.AfterPrefix(start)
gt = ">="
}
var endLimitKey = opi.largestKey
if endLimitKey == nil {
// jackc/pgx will treat nil as a NULL value, while lib/pq treats it as
// an empty string. We'll remove the ambiguity by making it a zero-length
// byte slice instead
endLimitKey = []byte{}
}
return opi.client.db.Query(ctx, fmt.Sprintf(`
SELECT pd.fullpath, pd.metadata
@ -164,5 +171,5 @@ func (opi *orderedPostgresIterator) doNextQuery(ctx context.Context) (_ tagsql.R
AND ($2::BYTEA = ''::BYTEA OR pd.fullpath < $2::BYTEA)
ORDER BY pd.fullpath
LIMIT $3
`, gt), start, []byte(opi.largestKey), opi.batchSize)
`, gt), start, endLimitKey, opi.batchSize)
}