storage/{cockroachkv,postgreskv}: delete unused code
Change-Id: I11da924508d207d8816bda206d28261931d9cd4e
This commit is contained in:
parent
8c62788b24
commit
4d37378129
2
go.mod
2
go.mod
@ -5,13 +5,13 @@ go 1.13
|
||||
require (
|
||||
github.com/alessio/shellescape v1.2.2
|
||||
github.com/alicebob/miniredis/v2 v2.13.3
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
|
||||
github.com/calebcase/tmpfile v1.0.2
|
||||
github.com/cheggaaa/pb/v3 v3.0.5
|
||||
github.com/fatih/color v1.9.0
|
||||
github.com/go-redis/redis/v8 v8.7.1
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang-migrate/migrate/v4 v4.7.0
|
||||
github.com/google/go-cmp v0.5.4
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3 // indirect
|
||||
github.com/gorilla/mux v1.8.0
|
||||
|
100
go.sum
100
go.sum
@ -2,7 +2,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
@ -25,17 +24,10 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
|
||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
@ -53,11 +45,9 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
@ -97,9 +87,6 @@ github.com/cloudfoundry/gosigar v1.1.0 h1:V/dVCzhKOdIU3WRB5inQU20s4yIgL9Dxx/Mhi0
|
||||
github.com/cloudfoundry/gosigar v1.1.0/go.mod h1:3qLfc2GlfmwOx2+ZDaRGH3Y9fwQ0sQeaAleo2GV5pH0=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
|
||||
github.com/containerd/containerd v1.2.7 h1:8lqLbl7u1j3MmiL9cJ/O275crSq7bfwUayvvatEupQk=
|
||||
github.com/containerd/containerd v1.2.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@ -109,41 +96,15 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
|
||||
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=
|
||||
github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00/go.mod h1:olo7eAdKwJdXxb55TKGLiJ6xt1H0/tiiRCWKVLmtjY4=
|
||||
github.com/cznic/lldb v1.1.0/go.mod h1:FIZVUmYUVhPwRiPzL8nD/mpFcJ/G7SSXjjXYG4uRI3A=
|
||||
github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||
github.com/cznic/ql v1.2.0/go.mod h1:FbpzhyZrqr0PVlK6ury+PoW3T0ODUV22OeWIxcaOrSE=
|
||||
github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
|
||||
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
|
||||
github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc/go.mod h1:Y1SNZ4dRUOKXshKUbwUapqNncRrho4mkjQebgEHZLj8=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dhui/dktest v0.3.0 h1:kwX5a7EkLcjo7VpsPQSYJcKGbXBXdjI9FGjuUj1jn6I=
|
||||
github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc=
|
||||
github.com/docker/distribution v2.7.0+incompatible h1:neUDAlf3wX6Ml4HdqTrbcOHXtfRN0TFIwt6YFL7N9RU=
|
||||
github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v0.7.3-0.20190817195342-4760db040282 h1:mzrx39dGtGq0VEnTHjnakmczd4uFbhx2cZU3BJDsLdc=
|
||||
github.com/docker/docker v0.7.3-0.20190817195342-4760db040282/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
@ -154,7 +115,6 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
@ -165,18 +125,13 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-redis/redis/v8 v8.7.1 h1:8IYi6RO83fNcG5amcUUYTN/qH2h4OjZHlim3KWGFSsA=
|
||||
github.com/go-redis/redis/v8 v8.7.1/go.mod h1:BRxHBWn3pO3CfjyX6vAoyeRmCquvxr6QG+2onGV2gYs=
|
||||
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/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-migrate/migrate/v4 v4.7.0 h1:gONcHxHApDTKXDyLH/H97gEHmpu1zcnnbAaq2zgrPrs=
|
||||
github.com/golang-migrate/migrate/v4 v4.7.0/go.mod h1:Qvut3N4xKWjoH3sokBccML6WyHSnggXm/DvMMnTsQIc=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@ -201,9 +156,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@ -231,9 +183,6 @@ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||
@ -246,15 +195,12 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
@ -279,7 +225,6 @@ github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9
|
||||
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/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=
|
||||
@ -316,8 +261,6 @@ github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkAL
|
||||
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
|
||||
github.com/jackc/pgtype v1.6.2 h1:b3pDeuhbbzBYcg5kwNmNDun4pFUD/0AAr1kLXZLeNt8=
|
||||
github.com/jackc/pgtype v1.6.2/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=
|
||||
@ -333,7 +276,6 @@ github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
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=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
@ -347,13 +289,11 @@ github.com/jtolds/monkit-hw/v2 v2.0.0-20191108235325-141a0da276b3/go.mod h1:eo5p
|
||||
github.com/jtolds/tracetagger/v2 v2.0.0-rc5 h1:SriMFVtftPsQmG+0xaABotz9HnoKoo1QM/oggqfpGh8=
|
||||
github.com/jtolds/tracetagger/v2 v2.0.0-rc5/go.mod h1:61Fh+XhbBONy+RsqkA+xTtmaFbEVL040m9FAF/hTrjQ=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
@ -364,7 +304,6 @@ github.com/kr/pty v1.1.3/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=
|
||||
@ -400,7 +339,6 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
@ -420,10 +358,7 @@ github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxd
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758=
|
||||
@ -445,17 +380,11 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
|
||||
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.9.0 h1:NOd0BRdOKpPf0SxkL3HxSQOG7rNh+4kl6PHcBPFs7Q0=
|
||||
github.com/pelletier/go-toml v1.9.0/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@ -466,22 +395,17 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
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=
|
||||
@ -524,7 +448,6 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
@ -576,15 +499,11 @@ github.com/stripe/stripe-go v70.15.0+incompatible/go.mod h1:A1dQZmO/QypXmsL0T8ax
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3 h1:zMsHhfK9+Wdl1F7sIKLyx3wrOFofpb3rWFbA4HgcK5k=
|
||||
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3/go.mod h1:R0Gbuw7ElaGSLOZUSwBm/GgVwMd30jWxBDdAyMOeTuc=
|
||||
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
|
||||
@ -610,13 +529,10 @@ github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54/go.mod h1:EI8LcOBDlSL
|
||||
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.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
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=
|
||||
@ -652,9 +568,7 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/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-20190313024323-a1f597ede03a/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=
|
||||
@ -701,17 +615,14 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
@ -727,10 +638,8 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/Lt
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -753,8 +662,6 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/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=
|
||||
@ -763,7 +670,6 @@ golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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=
|
||||
@ -815,7 +721,6 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/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=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
@ -845,8 +750,6 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@ -869,7 +772,6 @@ google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoA
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@ -926,8 +828,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -1,373 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package cockroachkv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
"github.com/spacemonkeygo/monkit/v3"
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/private/dbutil"
|
||||
"storj.io/private/dbutil/cockroachutil"
|
||||
"storj.io/private/dbutil/pgutil"
|
||||
"storj.io/private/dbutil/txutil"
|
||||
"storj.io/private/tagsql"
|
||||
"storj.io/storj/storage"
|
||||
"storj.io/storj/storage/cockroachkv/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
mon = monkit.Package()
|
||||
)
|
||||
|
||||
// Client is the entrypoint into a cockroachkv data store.
|
||||
type Client struct {
|
||||
db tagsql.DB
|
||||
dbURL string
|
||||
lookupLimit int
|
||||
}
|
||||
|
||||
// Open connects a new cockroachkv client given db URL.
|
||||
func Open(ctx context.Context, dbURL string, app string) (*Client, error) {
|
||||
dbURL, err := pgutil.CheckApplicationName(dbURL, app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db, err := tagsql.Open(ctx, "cockroach", dbURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbutil.Configure(ctx, db, "cockroachkv", mon)
|
||||
|
||||
return NewWith(db, dbURL), nil
|
||||
}
|
||||
|
||||
// NewWith instantiates a new cockroachkv client given db.
|
||||
func NewWith(db tagsql.DB, dbURL string) *Client {
|
||||
return &Client{db: db, dbURL: dbURL, lookupLimit: storage.DefaultLookupLimit}
|
||||
}
|
||||
|
||||
// MigrateToLatest migrates to latest schema version.
|
||||
func (client *Client) MigrateToLatest(ctx context.Context) error {
|
||||
return schema.PrepareDB(ctx, client.db)
|
||||
}
|
||||
|
||||
// SetLookupLimit sets the lookup limit.
|
||||
func (client *Client) SetLookupLimit(v int) { client.lookupLimit = v }
|
||||
|
||||
// LookupLimit returns the maximum limit that is allowed.
|
||||
func (client *Client) LookupLimit() int { return client.lookupLimit }
|
||||
|
||||
// Close closes the client.
|
||||
func (client *Client) Close() error {
|
||||
return client.db.Close()
|
||||
}
|
||||
|
||||
// Put sets the value for the provided key.
|
||||
func (client *Client) Put(ctx context.Context, key storage.Key, value storage.Value) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if key.IsZero() {
|
||||
return storage.ErrEmptyKey.New("")
|
||||
}
|
||||
|
||||
q := `
|
||||
INSERT INTO pathdata (fullpath, metadata)
|
||||
VALUES ($1:::BYTEA, $2:::BYTEA)
|
||||
ON CONFLICT (fullpath) DO UPDATE SET metadata = EXCLUDED.metadata
|
||||
`
|
||||
|
||||
_, err = client.db.ExecContext(ctx, q, []byte(key), []byte(value))
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
// Get looks up the provided key and returns its value (or an error).
|
||||
func (client *Client) Get(ctx context.Context, key storage.Key) (_ storage.Value, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if key.IsZero() {
|
||||
return nil, storage.ErrEmptyKey.New("")
|
||||
}
|
||||
|
||||
q := "SELECT metadata FROM pathdata WHERE fullpath = $1:::BYTEA"
|
||||
row := client.db.QueryRowContext(ctx, q, []byte(key))
|
||||
|
||||
var val []byte
|
||||
err = row.Scan(&val)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, storage.ErrKeyNotFound.New("%q", key)
|
||||
}
|
||||
|
||||
return val, Error.Wrap(err)
|
||||
}
|
||||
|
||||
// GetAll finds all values for the provided keys (up to LookupLimit).
|
||||
// If more keys are provided than the maximum, an error will be returned.
|
||||
func (client *Client) GetAll(ctx context.Context, keys storage.Keys) (values storage.Values, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if len(keys) > client.lookupLimit {
|
||||
return nil, storage.ErrLimitExceeded.New("lookup limit exceeded")
|
||||
}
|
||||
|
||||
for {
|
||||
values, err = client.getAllOnce(ctx, keys)
|
||||
if err != nil {
|
||||
if cockroachutil.NeedsRetry(err) {
|
||||
continue
|
||||
}
|
||||
return nil, Error.Wrap(err)
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (client *Client) getAllOnce(ctx context.Context, keys storage.Keys) (values storage.Values, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
q := `
|
||||
SELECT metadata
|
||||
FROM pathdata pd
|
||||
RIGHT JOIN
|
||||
unnest($1:::BYTEA[]) WITH ORDINALITY pk(request, ord)
|
||||
ON (pd.fullpath = pk.request)
|
||||
ORDER BY pk.ord
|
||||
`
|
||||
rows, err := client.db.QueryContext(ctx, q, pgutil.ByteaArray(keys.ByteSlices()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
closeErr := rows.Close()
|
||||
if closeErr != nil {
|
||||
err = errs.Combine(err, closeErr)
|
||||
}
|
||||
}()
|
||||
values = make([]storage.Value, 0, len(keys))
|
||||
for rows.Next() {
|
||||
var value []byte
|
||||
if err := rows.Scan(&value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values = append(values, storage.Value(value))
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// Delete deletes the given key and its associated value.
|
||||
func (client *Client) Delete(ctx context.Context, key storage.Key) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if key.IsZero() {
|
||||
return storage.ErrEmptyKey.New("")
|
||||
}
|
||||
|
||||
q := "DELETE FROM pathdata WHERE fullpath = $1:::BYTEA"
|
||||
result, err := client.db.ExecContext(ctx, q, []byte(key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
numRows, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if numRows == 0 {
|
||||
return storage.ErrKeyNotFound.New("%q", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteMultiple deletes keys ignoring missing keys.
|
||||
func (client *Client) DeleteMultiple(ctx context.Context, keys []storage.Key) (items storage.Items, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
// make sure delete always happen in the same order
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i].Less(keys[j])
|
||||
})
|
||||
|
||||
for {
|
||||
items, err = client.deleteMultipleOnce(ctx, keys)
|
||||
if err != nil {
|
||||
if cockroachutil.NeedsRetry(err) {
|
||||
continue
|
||||
}
|
||||
return nil, Error.Wrap(err)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (client *Client) deleteMultipleOnce(ctx context.Context, keys storage.Keys) (items storage.Items, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
rows, err := client.db.QueryContext(ctx, `
|
||||
DELETE FROM pathdata
|
||||
WHERE fullpath = any($1::BYTEA[])
|
||||
RETURNING fullpath, metadata`,
|
||||
pgutil.ByteaArray(keys.ByteSlices()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
closeErr := rows.Close()
|
||||
if closeErr != nil {
|
||||
err = errs.Combine(err, closeErr)
|
||||
}
|
||||
}()
|
||||
items = make([]storage.ListItem, 0, len(keys))
|
||||
for rows.Next() {
|
||||
var key, value []byte
|
||||
err := rows.Scan(&key, &value)
|
||||
if err != nil {
|
||||
return items, err
|
||||
}
|
||||
items = append(items, storage.ListItem{
|
||||
Key: key,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// List returns either a list of known keys, in order, or an error.
|
||||
func (client *Client) List(ctx context.Context, first storage.Key, limit int) (_ storage.Keys, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
return storage.ListKeys(ctx, client, first, limit)
|
||||
}
|
||||
|
||||
// Iterate calls the callback with an iterator over the keys.
|
||||
func (client *Client) Iterate(ctx context.Context, opts storage.IterateOptions, fn func(context.Context, storage.Iterator) error) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if opts.Limit <= 0 || opts.Limit > client.lookupLimit {
|
||||
opts.Limit = client.lookupLimit
|
||||
}
|
||||
|
||||
return client.IterateWithoutLookupLimit(ctx, opts, fn)
|
||||
}
|
||||
|
||||
// IterateWithoutLookupLimit calls the callback with an iterator over the keys, but doesn't enforce default limit on opts.
|
||||
func (client *Client) IterateWithoutLookupLimit(ctx context.Context, opts storage.IterateOptions, fn func(context.Context, storage.Iterator) error) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
opi, err := newOrderedCockroachIterator(ctx, client, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errs.Combine(err, opi.Close())
|
||||
}()
|
||||
|
||||
return fn(ctx, opi)
|
||||
}
|
||||
|
||||
// CompareAndSwap atomically compares and swaps oldValue with newValue.
|
||||
func (client *Client) CompareAndSwap(ctx context.Context, key storage.Key, oldValue, newValue storage.Value) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if key.IsZero() {
|
||||
return storage.ErrEmptyKey.New("")
|
||||
}
|
||||
|
||||
if oldValue == nil && newValue == nil {
|
||||
q := "SELECT metadata FROM pathdata WHERE fullpath = $1:::BYTEA"
|
||||
row := client.db.QueryRowContext(ctx, q, []byte(key))
|
||||
|
||||
var val []byte
|
||||
err = row.Scan(&val)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
return storage.ErrValueChanged.New("%q", key)
|
||||
}
|
||||
|
||||
if oldValue == nil {
|
||||
q := `
|
||||
INSERT INTO pathdata (fullpath, metadata) VALUES ($1:::BYTEA, $2:::BYTEA)
|
||||
ON CONFLICT DO NOTHING
|
||||
RETURNING 1
|
||||
`
|
||||
row := client.db.QueryRowContext(ctx, q, []byte(key), []byte(newValue))
|
||||
|
||||
var val []byte
|
||||
err = row.Scan(&val)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return storage.ErrValueChanged.New("%q", key)
|
||||
}
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
return txutil.WithTx(ctx, client.db, nil, func(ctx context.Context, txn tagsql.Tx) error {
|
||||
q := "SELECT metadata FROM pathdata WHERE fullpath = $1:::BYTEA;"
|
||||
row := txn.QueryRowContext(ctx, q, []byte(key))
|
||||
|
||||
var metadata []byte
|
||||
err = row.Scan(&metadata)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// Row not found for this fullpath.
|
||||
// Potentially because another concurrent transaction changed the row.
|
||||
return storage.ErrKeyNotFound.New("%q", key)
|
||||
}
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
if equal := bytes.Compare(metadata, oldValue); equal != 0 {
|
||||
// If the row is found but the metadata has been already changed
|
||||
// we can't continue to delete it.
|
||||
return storage.ErrValueChanged.New("%q", key)
|
||||
}
|
||||
|
||||
var res sql.Result
|
||||
if newValue == nil {
|
||||
q = `
|
||||
DELETE FROM pathdata
|
||||
WHERE pathdata.fullpath = $1:::BYTEA
|
||||
AND pathdata.metadata = $2:::BYTEA
|
||||
`
|
||||
|
||||
res, err = txn.ExecContext(ctx, q, []byte(key), []byte(oldValue))
|
||||
} else {
|
||||
q = `
|
||||
UPDATE pathdata
|
||||
SET metadata = $3:::BYTEA
|
||||
WHERE pathdata.fullpath = $1:::BYTEA
|
||||
AND pathdata.metadata = $2:::BYTEA
|
||||
`
|
||||
res, err = txn.ExecContext(ctx, q, []byte(key), []byte(oldValue), []byte(newValue))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
affected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
return storage.ErrValueChanged.New("%q", key)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
package cockroachkv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"storj.io/common/testcontext"
|
||||
"storj.io/private/dbutil/cockroachutil"
|
||||
"storj.io/private/dbutil/pgtest"
|
||||
"storj.io/storj/storage/testsuite"
|
||||
)
|
||||
|
||||
func newTestCockroachDB(ctx context.Context, t testing.TB) (store *Client, cleanup func()) {
|
||||
connstr := pgtest.PickCockroach(t)
|
||||
|
||||
tdb, err := cockroachutil.OpenUnique(ctx, connstr, "test-schema")
|
||||
if err != nil {
|
||||
t.Fatalf("init: %+v", err)
|
||||
}
|
||||
|
||||
return NewWith(tdb.DB, connstr), func() {
|
||||
if err := tdb.Close(); err != nil {
|
||||
t.Fatalf("failed to close db: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuite(t *testing.T) {
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
|
||||
store, cleanup := newTestCockroachDB(ctx, t)
|
||||
defer cleanup()
|
||||
|
||||
err := store.MigrateToLatest(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
store.SetLookupLimit(500)
|
||||
testsuite.RunTests(t, store)
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package cockroachkv
|
||||
|
||||
import (
|
||||
"github.com/zeebo/errs"
|
||||
)
|
||||
|
||||
// Error is the default postgreskv errs class.
|
||||
var Error = errs.Class("cockroachkv error")
|
@ -1,190 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package cockroachkv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/private/dbutil/cockroachutil"
|
||||
"storj.io/private/tagsql"
|
||||
"storj.io/storj/storage"
|
||||
)
|
||||
|
||||
type orderedCockroachIterator struct {
|
||||
client *Client
|
||||
opts *storage.IterateOptions
|
||||
delimiter byte
|
||||
batchSize int
|
||||
curIndex int
|
||||
curRows tagsql.Rows
|
||||
skipPrefix bool
|
||||
lastKeySeen storage.Key
|
||||
largestKey storage.Key
|
||||
errEncountered error
|
||||
}
|
||||
|
||||
func newOrderedCockroachIterator(ctx context.Context, cli *Client, opts storage.IterateOptions) (_ *orderedCockroachIterator, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
if opts.Prefix == nil {
|
||||
opts.Prefix = storage.Key("")
|
||||
}
|
||||
if opts.First == nil {
|
||||
opts.First = storage.Key("")
|
||||
}
|
||||
if opts.First.Less(opts.Prefix) {
|
||||
opts.First = opts.Prefix
|
||||
}
|
||||
|
||||
oci := &orderedCockroachIterator{
|
||||
client: cli,
|
||||
opts: &opts,
|
||||
delimiter: byte('/'),
|
||||
batchSize: opts.Limit,
|
||||
curIndex: 0,
|
||||
}
|
||||
|
||||
if len(opts.Prefix) > 0 {
|
||||
oci.largestKey = storage.AfterPrefix(opts.Prefix)
|
||||
}
|
||||
|
||||
newRows, err := oci.doNextQuery(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oci.curRows = newRows
|
||||
|
||||
return oci, nil
|
||||
}
|
||||
|
||||
func (oci *orderedCockroachIterator) Close() error {
|
||||
defer mon.Task()(nil)(nil)
|
||||
|
||||
return errs.Combine(oci.curRows.Err(), oci.errEncountered, oci.curRows.Close())
|
||||
}
|
||||
|
||||
// Next fills in info for the next item in an ongoing listing.
|
||||
func (oci *orderedCockroachIterator) Next(ctx context.Context, item *storage.ListItem) bool {
|
||||
defer mon.Task()(&ctx)(nil)
|
||||
|
||||
for {
|
||||
for {
|
||||
nextTask := mon.TaskNamed("check_next_row")(nil)
|
||||
next := oci.curRows.Next()
|
||||
nextTask(nil)
|
||||
if next {
|
||||
break
|
||||
}
|
||||
|
||||
result := func() bool {
|
||||
defer mon.TaskNamed("acquire_new_query")(nil)(nil)
|
||||
|
||||
retry := false
|
||||
if err := oci.curRows.Err(); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
// This NeedsRetry needs to be exported here because it is
|
||||
// expected behavior for cockroach to return retryable errors
|
||||
// that will be captured in this Rows object.
|
||||
if cockroachutil.NeedsRetry(err) {
|
||||
mon.Event("needed_retry")
|
||||
retry = true
|
||||
} else {
|
||||
oci.errEncountered = errs.Wrap(err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if err := oci.curRows.Close(); err != nil {
|
||||
if cockroachutil.NeedsRetry(err) {
|
||||
mon.Event("needed_retry")
|
||||
retry = true
|
||||
} else {
|
||||
oci.errEncountered = errs.Wrap(err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if oci.curIndex < oci.batchSize && !retry {
|
||||
return false
|
||||
}
|
||||
newRows, err := oci.doNextQuery(ctx)
|
||||
if err != nil {
|
||||
oci.errEncountered = errs.Wrap(err)
|
||||
return false
|
||||
}
|
||||
oci.curRows = newRows
|
||||
oci.curIndex = 0
|
||||
return true
|
||||
}()
|
||||
if !result {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
var k, v []byte
|
||||
scanTask := mon.TaskNamed("scan_next_row")(nil)
|
||||
err := oci.curRows.Scan(&k, &v)
|
||||
scanTask(&err)
|
||||
if err != nil {
|
||||
oci.errEncountered = errs.Wrap(err)
|
||||
return false
|
||||
}
|
||||
oci.curIndex++
|
||||
|
||||
if !bytes.HasPrefix(k, []byte(oci.opts.Prefix)) {
|
||||
return false
|
||||
}
|
||||
|
||||
item.Key = storage.Key(k)
|
||||
item.Value = storage.Value(v)
|
||||
item.IsPrefix = false
|
||||
|
||||
if !oci.opts.Recurse {
|
||||
if idx := bytes.IndexByte(item.Key[len(oci.opts.Prefix):], oci.delimiter); idx >= 0 {
|
||||
item.Key = item.Key[:len(oci.opts.Prefix)+idx+1]
|
||||
item.Value = nil
|
||||
item.IsPrefix = true
|
||||
}
|
||||
}
|
||||
if oci.lastKeySeen.Equal(item.Key) {
|
||||
continue
|
||||
}
|
||||
|
||||
oci.skipPrefix = item.IsPrefix
|
||||
oci.lastKeySeen = item.Key
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (oci *orderedCockroachIterator) doNextQuery(ctx context.Context) (_ tagsql.Rows, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
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 = ">="
|
||||
} else if oci.skipPrefix {
|
||||
start = storage.AfterPrefix(start)
|
||||
gt = ">="
|
||||
}
|
||||
|
||||
return oci.client.db.QueryContext(ctx, fmt.Sprintf(`
|
||||
SELECT pd.fullpath, pd.metadata
|
||||
FROM pathdata pd
|
||||
WHERE pd.fullpath %s $1:::BYTEA
|
||||
AND ($2:::BYTEA = '':::BYTEA OR pd.fullpath < $2:::BYTEA)
|
||||
LIMIT $3
|
||||
`, gt), start, largestKey, oci.batchSize)
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/private/dbutil/pgutil"
|
||||
"storj.io/private/tagsql"
|
||||
)
|
||||
|
||||
// PrepareDB creates the pathdata tables if they don't already exist.
|
||||
func PrepareDB(ctx context.Context, db tagsql.DB) (err error) {
|
||||
var dbName string
|
||||
if err := db.QueryRow(ctx, `SELECT current_database();`).Scan(&dbName); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
// Note: the buckets table is unused. It exists here to ease importing
|
||||
// backups from postgres. Similarly, the bucket column in pathdata is
|
||||
// also unused and exists to ease imports.
|
||||
_, err = db.Exec(ctx, fmt.Sprintf(`
|
||||
CREATE DATABASE IF NOT EXISTS %s;
|
||||
CREATE TABLE IF NOT EXISTS buckets (
|
||||
bucketname BYTES PRIMARY KEY,
|
||||
delim INT8 NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS pathdata (
|
||||
fullpath BYTEA PRIMARY KEY,
|
||||
metadata BYTEA NOT NULL,
|
||||
bucket BYTEA
|
||||
);
|
||||
`, pgutil.QuoteIdentifier(dbName)))
|
||||
return errs.Wrap(err)
|
||||
}
|
@ -1,325 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package postgreskv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
"github.com/spacemonkeygo/monkit/v3"
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/private/dbutil"
|
||||
"storj.io/private/dbutil/pgutil"
|
||||
"storj.io/private/tagsql"
|
||||
"storj.io/storj/storage"
|
||||
"storj.io/storj/storage/postgreskv/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
mon = monkit.Package()
|
||||
)
|
||||
|
||||
// Client is the entrypoint into a postgreskv data store.
|
||||
type Client struct {
|
||||
db tagsql.DB
|
||||
dbURL string
|
||||
lookupLimit int
|
||||
}
|
||||
|
||||
// Open connects a new postgreskv client given db URL.
|
||||
func Open(ctx context.Context, dbURL string, app string) (*Client, error) {
|
||||
dbURL, err := pgutil.CheckApplicationName(dbURL, app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db, err := tagsql.Open(ctx, "pgx", dbURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbutil.Configure(ctx, db, "postgreskv", mon)
|
||||
return NewWith(db, dbURL), nil
|
||||
}
|
||||
|
||||
// NewWith instantiates a new postgreskv client given db.
|
||||
func NewWith(db tagsql.DB, dbURL string) *Client {
|
||||
return &Client{db: db, lookupLimit: storage.DefaultLookupLimit, dbURL: dbURL}
|
||||
}
|
||||
|
||||
// MigrateToLatest migrates to latest schema version.
|
||||
func (client *Client) MigrateToLatest(ctx context.Context) error {
|
||||
return schema.PrepareDB(ctx, client.db, client.dbURL)
|
||||
}
|
||||
|
||||
// SetLookupLimit sets the lookup limit.
|
||||
func (client *Client) SetLookupLimit(v int) { client.lookupLimit = v }
|
||||
|
||||
// LookupLimit returns the maximum limit that is allowed.
|
||||
func (client *Client) LookupLimit() int { return client.lookupLimit }
|
||||
|
||||
// Close closes the client.
|
||||
func (client *Client) Close() error {
|
||||
return client.db.Close()
|
||||
}
|
||||
|
||||
// Put sets the value for the provided key.
|
||||
func (client *Client) Put(ctx context.Context, key storage.Key, value storage.Value) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if key.IsZero() {
|
||||
return storage.ErrEmptyKey.New("")
|
||||
}
|
||||
|
||||
q := `
|
||||
INSERT INTO pathdata (fullpath, metadata)
|
||||
VALUES ($1::BYTEA, $2::BYTEA)
|
||||
ON CONFLICT (fullpath) DO UPDATE SET metadata = EXCLUDED.metadata
|
||||
`
|
||||
|
||||
_, err = client.db.Exec(ctx, q, []byte(key), []byte(value))
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
// Get looks up the provided key and returns its value (or an error).
|
||||
func (client *Client) Get(ctx context.Context, key storage.Key) (_ storage.Value, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if key.IsZero() {
|
||||
return nil, storage.ErrEmptyKey.New("")
|
||||
}
|
||||
|
||||
q := "SELECT metadata FROM pathdata WHERE fullpath = $1::BYTEA"
|
||||
row := client.db.QueryRow(ctx, q, []byte(key))
|
||||
|
||||
var val []byte
|
||||
err = row.Scan(&val)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, storage.ErrKeyNotFound.New("%q", key)
|
||||
}
|
||||
|
||||
return val, Error.Wrap(err)
|
||||
}
|
||||
|
||||
// GetAll finds all values for the provided keys (up to LookupLimit).
|
||||
// If more keys are provided than the maximum, an error will be returned.
|
||||
func (client *Client) GetAll(ctx context.Context, keys storage.Keys) (_ storage.Values, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if len(keys) > client.lookupLimit {
|
||||
return nil, storage.ErrLimitExceeded.New("lookup limit exceeded")
|
||||
}
|
||||
|
||||
q := `
|
||||
SELECT metadata
|
||||
FROM pathdata pd
|
||||
RIGHT JOIN
|
||||
unnest($1::BYTEA[]) WITH ORDINALITY pk(request, ord)
|
||||
ON (pd.fullpath = pk.request)
|
||||
ORDER BY pk.ord
|
||||
`
|
||||
rows, err := client.db.Query(ctx, q, pgutil.ByteaArray(keys.ByteSlices()))
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
defer func() { err = errs.Combine(err, Error.Wrap(rows.Close())) }()
|
||||
|
||||
values := make([]storage.Value, 0, len(keys))
|
||||
for rows.Next() {
|
||||
var value []byte
|
||||
if err := rows.Scan(&value); err != nil {
|
||||
return nil, Error.Wrap(err)
|
||||
}
|
||||
values = append(values, storage.Value(value))
|
||||
}
|
||||
|
||||
return values, Error.Wrap(rows.Err())
|
||||
}
|
||||
|
||||
// Delete deletes the given key and its associated value.
|
||||
func (client *Client) Delete(ctx context.Context, key storage.Key) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if key.IsZero() {
|
||||
return storage.ErrEmptyKey.New("")
|
||||
}
|
||||
|
||||
q := "DELETE FROM pathdata WHERE fullpath = $1::BYTEA"
|
||||
result, err := client.db.Exec(ctx, q, []byte(key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
numRows, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if numRows == 0 {
|
||||
return storage.ErrKeyNotFound.New("%q", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteMultiple deletes keys ignoring missing keys.
|
||||
func (client *Client) DeleteMultiple(ctx context.Context, keys []storage.Key) (_ storage.Items, err error) {
|
||||
defer mon.Task()(&ctx, len(keys))(&err)
|
||||
|
||||
// make sure deletes always happen in the same order
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i].Less(keys[j])
|
||||
})
|
||||
|
||||
rows, err := client.db.QueryContext(ctx, `
|
||||
DELETE FROM pathdata
|
||||
WHERE fullpath = any($1::BYTEA[])
|
||||
RETURNING fullpath, metadata`,
|
||||
pgutil.ByteaArray(storage.Keys(keys).ByteSlices()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
err = errs.Combine(err, rows.Close())
|
||||
}()
|
||||
|
||||
var items storage.Items
|
||||
for rows.Next() {
|
||||
var key, value []byte
|
||||
err := rows.Scan(&key, &value)
|
||||
if err != nil {
|
||||
return items, err
|
||||
}
|
||||
items = append(items, storage.ListItem{
|
||||
Key: key,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
return items, rows.Err()
|
||||
}
|
||||
|
||||
// List returns either a list of known keys, in order, or an error.
|
||||
func (client *Client) List(ctx context.Context, first storage.Key, limit int) (_ storage.Keys, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
return storage.ListKeys(ctx, client, first, limit)
|
||||
}
|
||||
|
||||
// Iterate calls the callback with an iterator over the keys.
|
||||
func (client *Client) Iterate(ctx context.Context, opts storage.IterateOptions, fn func(context.Context, storage.Iterator) error) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if opts.Limit <= 0 || opts.Limit > client.lookupLimit {
|
||||
opts.Limit = client.lookupLimit
|
||||
}
|
||||
|
||||
return client.IterateWithoutLookupLimit(ctx, opts, fn)
|
||||
}
|
||||
|
||||
// IterateWithoutLookupLimit calls the callback with an iterator over the keys, but doesn't enforce default limit on opts.
|
||||
func (client *Client) IterateWithoutLookupLimit(ctx context.Context, opts storage.IterateOptions, fn func(context.Context, storage.Iterator) error) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
opi, err := newOrderedPostgresIterator(ctx, client, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errs.Combine(err, opi.Close())
|
||||
}()
|
||||
|
||||
return fn(ctx, opi)
|
||||
}
|
||||
|
||||
// CompareAndSwap atomically compares and swaps oldValue with newValue.
|
||||
func (client *Client) CompareAndSwap(ctx context.Context, key storage.Key, oldValue, newValue storage.Value) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
if key.IsZero() {
|
||||
return storage.ErrEmptyKey.New("")
|
||||
}
|
||||
|
||||
if oldValue == nil && newValue == nil {
|
||||
q := "SELECT metadata FROM pathdata WHERE fullpath = $1::BYTEA"
|
||||
row := client.db.QueryRow(ctx, q, []byte(key))
|
||||
|
||||
var val []byte
|
||||
err = row.Scan(&val)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
return storage.ErrValueChanged.New("%q", key)
|
||||
}
|
||||
|
||||
if oldValue == nil {
|
||||
q := `
|
||||
INSERT INTO pathdata (fullpath, metadata) VALUES ($1::BYTEA, $2::BYTEA)
|
||||
ON CONFLICT DO NOTHING
|
||||
RETURNING 1
|
||||
`
|
||||
row := client.db.QueryRow(ctx, q, []byte(key), []byte(newValue))
|
||||
|
||||
var val []byte
|
||||
err = row.Scan(&val)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return storage.ErrValueChanged.New("%q", key)
|
||||
}
|
||||
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
var row *sql.Row
|
||||
if newValue == nil {
|
||||
q := `
|
||||
WITH matching_key AS (
|
||||
SELECT * FROM pathdata WHERE fullpath = $1::BYTEA
|
||||
), updated AS (
|
||||
DELETE FROM pathdata
|
||||
USING matching_key mk
|
||||
WHERE pathdata.metadata = $2::BYTEA
|
||||
AND pathdata.fullpath = mk.fullpath
|
||||
RETURNING 1
|
||||
)
|
||||
SELECT EXISTS(SELECT 1 FROM matching_key) AS key_present, EXISTS(SELECT 1 FROM updated) AS value_updated
|
||||
`
|
||||
row = client.db.QueryRow(ctx, q, []byte(key), []byte(oldValue))
|
||||
} else {
|
||||
q := `
|
||||
WITH matching_key AS (
|
||||
SELECT * FROM pathdata WHERE fullpath = $1::BYTEA
|
||||
), updated AS (
|
||||
UPDATE pathdata
|
||||
SET metadata = $3::BYTEA
|
||||
FROM matching_key mk
|
||||
WHERE pathdata.metadata = $2::BYTEA
|
||||
AND pathdata.fullpath = mk.fullpath
|
||||
RETURNING 1
|
||||
)
|
||||
SELECT EXISTS(SELECT 1 FROM matching_key) AS key_present, EXISTS(SELECT 1 FROM updated) AS value_updated;
|
||||
`
|
||||
row = client.db.QueryRow(ctx, q, []byte(key), []byte(oldValue), []byte(newValue))
|
||||
}
|
||||
|
||||
var keyPresent, valueUpdated bool
|
||||
err = row.Scan(&keyPresent, &valueUpdated)
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
if !keyPresent {
|
||||
return storage.ErrKeyNotFound.New("%q", key)
|
||||
}
|
||||
|
||||
if !valueUpdated {
|
||||
return storage.ErrValueChanged.New("%q", key)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package postgreskv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"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/private/dbutil/pgtest"
|
||||
"storj.io/private/tagsql"
|
||||
"storj.io/storj/storage"
|
||||
"storj.io/storj/storage/testsuite"
|
||||
)
|
||||
|
||||
func openTestPostgres(ctx context.Context, t testing.TB) (store *Client, cleanup func()) {
|
||||
connstr := pgtest.PickPostgres(t)
|
||||
|
||||
pgdb, err := Open(ctx, connstr, "satellite-test-postgres")
|
||||
if err != nil {
|
||||
t.Fatalf("init: %v", err)
|
||||
}
|
||||
|
||||
return pgdb, func() {
|
||||
if err := pgdb.Close(); err != nil {
|
||||
t.Fatalf("failed to close db: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuite(t *testing.T) {
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
|
||||
store, cleanup := openTestPostgres(ctx, t)
|
||||
defer cleanup()
|
||||
|
||||
err := store.MigrateToLatest(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// zap := zaptest.NewLogger(t)
|
||||
// loggedStore := storelogger.New(zap, store)
|
||||
store.SetLookupLimit(500)
|
||||
testsuite.RunTests(t, store)
|
||||
}
|
||||
|
||||
func TestThatMigrationActuallyHappened(t *testing.T) {
|
||||
t.Skip()
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
|
||||
store, cleanup := openTestPostgres(ctx, t)
|
||||
defer cleanup()
|
||||
|
||||
rows, err := store.db.Query(ctx, `
|
||||
SELECT prosrc
|
||||
FROM pg_catalog.pg_proc p,
|
||||
pg_catalog.pg_namespace n
|
||||
WHERE p.pronamespace = n.oid
|
||||
AND p.proname = 'list_directory'
|
||||
AND n.nspname = ANY(current_schemas(true))
|
||||
AND p.pronargs = 4
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get list_directory source: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := rows.Close(); err != nil {
|
||||
t.Fatalf("failed to close rows: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
numFound := 0
|
||||
for rows.Next() {
|
||||
numFound++
|
||||
if numFound > 1 {
|
||||
t.Fatal("there are multiple eligible list_directory() functions??")
|
||||
}
|
||||
var source string
|
||||
if err := rows.Scan(&source); err != nil {
|
||||
t.Fatalf("failed to read list_directory source: %v", err)
|
||||
}
|
||||
if strings.Contains(source, "distinct_prefix (truncatedpath)") {
|
||||
t.Fatal("list_directory() function in pg appears to be the oldnbusted one")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSuite(b *testing.B) {
|
||||
b.Skip("broken")
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
store, cleanup := openTestPostgres(ctx, b)
|
||||
defer cleanup()
|
||||
|
||||
testsuite.RunBenchmarks(b, store)
|
||||
}
|
||||
|
||||
type bulkImportCopyFromSource struct {
|
||||
ctx context.Context
|
||||
iter storage.Iterator
|
||||
item storage.ListItem
|
||||
}
|
||||
|
||||
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 {
|
||||
_, err := db.Exec(ctx, "TRUNCATE pathdata")
|
||||
if err != nil {
|
||||
return errs.New("Failed to TRUNCATE pathdata table: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type pgLongBenchmarkStore struct {
|
||||
*Client
|
||||
}
|
||||
|
||||
func (store *pgLongBenchmarkStore) BulkImport(ctx context.Context, iter storage.Iterator) error {
|
||||
return bulkImport(ctx, store.db, iter)
|
||||
}
|
||||
|
||||
func (store *pgLongBenchmarkStore) BulkDeleteAll(ctx context.Context) error {
|
||||
return bulkDeleteAll(ctx, store.db)
|
||||
}
|
||||
|
||||
func BenchmarkSuiteLong(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
|
||||
store, cleanup := openTestPostgres(ctx, b)
|
||||
defer cleanup()
|
||||
|
||||
testsuite.BenchmarkPathOperationsInLargeDb(b, &pgLongBenchmarkStore{store})
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package postgreskv
|
||||
|
||||
import (
|
||||
"github.com/zeebo/errs"
|
||||
)
|
||||
|
||||
// Error is the default postgreskv errs class.
|
||||
var Error = errs.Class("postgreskv error")
|
@ -1,176 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package postgreskv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/private/tagsql"
|
||||
"storj.io/storj/storage"
|
||||
)
|
||||
|
||||
type orderedPostgresIterator struct {
|
||||
client *Client
|
||||
opts *storage.IterateOptions
|
||||
delimiter byte
|
||||
batchSize int
|
||||
curIndex int
|
||||
curRows tagsql.Rows
|
||||
skipPrefix bool
|
||||
lastKeySeen storage.Key
|
||||
largestKey storage.Key
|
||||
errEncountered error
|
||||
}
|
||||
|
||||
func newOrderedPostgresIterator(ctx context.Context, cli *Client, opts storage.IterateOptions) (_ *orderedPostgresIterator, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
if opts.Prefix == nil {
|
||||
opts.Prefix = storage.Key("")
|
||||
}
|
||||
if opts.First == nil {
|
||||
opts.First = storage.Key("")
|
||||
}
|
||||
if opts.First.Less(opts.Prefix) {
|
||||
opts.First = opts.Prefix
|
||||
}
|
||||
|
||||
opi := &orderedPostgresIterator{
|
||||
client: cli,
|
||||
opts: &opts,
|
||||
delimiter: byte('/'),
|
||||
batchSize: opts.Limit,
|
||||
curIndex: 0,
|
||||
}
|
||||
|
||||
if len(opts.Prefix) > 0 {
|
||||
opi.largestKey = storage.AfterPrefix(opts.Prefix)
|
||||
}
|
||||
|
||||
newRows, err := opi.doNextQuery(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opi.curRows = newRows
|
||||
|
||||
return opi, nil
|
||||
}
|
||||
|
||||
func (opi *orderedPostgresIterator) Close() error {
|
||||
defer mon.Task()(nil)(nil)
|
||||
|
||||
return errs.Combine(opi.curRows.Err(), opi.errEncountered, opi.curRows.Close())
|
||||
}
|
||||
|
||||
// Next fills in info for the next item in an ongoing listing.
|
||||
func (opi *orderedPostgresIterator) Next(ctx context.Context, item *storage.ListItem) bool {
|
||||
defer mon.Task()(&ctx)(nil)
|
||||
|
||||
for {
|
||||
for {
|
||||
nextTask := mon.TaskNamed("check_next_row")(nil)
|
||||
next := opi.curRows.Next()
|
||||
nextTask(nil)
|
||||
if next {
|
||||
break
|
||||
}
|
||||
|
||||
result := func() bool {
|
||||
defer mon.TaskNamed("acquire_new_query")(nil)(nil)
|
||||
|
||||
if err := opi.curRows.Err(); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
opi.errEncountered = errs.Wrap(err)
|
||||
return false
|
||||
}
|
||||
if err := opi.curRows.Close(); err != nil {
|
||||
opi.errEncountered = errs.Wrap(err)
|
||||
return false
|
||||
}
|
||||
if opi.curIndex < opi.batchSize {
|
||||
return false
|
||||
}
|
||||
newRows, err := opi.doNextQuery(ctx)
|
||||
if err != nil {
|
||||
opi.errEncountered = errs.Wrap(err)
|
||||
return false
|
||||
}
|
||||
opi.curRows = newRows
|
||||
opi.curIndex = 0
|
||||
return true
|
||||
}()
|
||||
if !result {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
var k, v []byte
|
||||
scanTask := mon.TaskNamed("scan_next_row")(nil)
|
||||
err := opi.curRows.Scan(&k, &v)
|
||||
scanTask(&err)
|
||||
if err != nil {
|
||||
opi.errEncountered = errs.Wrap(err)
|
||||
return false
|
||||
}
|
||||
opi.curIndex++
|
||||
|
||||
if !bytes.HasPrefix(k, []byte(opi.opts.Prefix)) {
|
||||
return false
|
||||
}
|
||||
|
||||
item.Key = storage.Key(k)
|
||||
item.Value = storage.Value(v)
|
||||
item.IsPrefix = false
|
||||
|
||||
if !opi.opts.Recurse {
|
||||
if idx := bytes.IndexByte(item.Key[len(opi.opts.Prefix):], opi.delimiter); idx >= 0 {
|
||||
item.Key = item.Key[:len(opi.opts.Prefix)+idx+1]
|
||||
item.Value = nil
|
||||
item.IsPrefix = true
|
||||
}
|
||||
}
|
||||
if opi.lastKeySeen.Equal(item.Key) {
|
||||
continue
|
||||
}
|
||||
|
||||
opi.skipPrefix = item.IsPrefix
|
||||
opi.lastKeySeen = item.Key
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (opi *orderedPostgresIterator) doNextQuery(ctx context.Context) (_ tagsql.Rows, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
gt := ">"
|
||||
start := opi.lastKeySeen
|
||||
|
||||
if len(start) == 0 {
|
||||
start = opi.opts.First
|
||||
gt = ">="
|
||||
} else if opi.skipPrefix {
|
||||
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
|
||||
FROM pathdata pd
|
||||
WHERE pd.fullpath %s $1::BYTEA
|
||||
AND ($2::BYTEA = ''::BYTEA OR pd.fullpath < $2::BYTEA)
|
||||
ORDER BY pd.fullpath
|
||||
LIMIT $3
|
||||
`, gt), start, endLimitKey, opi.batchSize)
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
DROP FUNCTION list_directory_reverse(BYTEA, BYTEA, BYTEA, INTEGER);
|
||||
DROP FUNCTION list_directory(BYTEA, BYTEA, BYTEA, INTEGER);
|
||||
DROP TYPE path_and_meta;
|
||||
DROP FUNCTION component_increment(BYTEA, INTEGER);
|
||||
DROP FUNCTION bytea_increment(BYTEA);
|
||||
DROP FUNCTION truncate_after(BYTEA, INTEGER, INTEGER);
|
||||
DROP VIEW pathdata_pretty;
|
||||
DROP TABLE pathdata;
|
||||
DROP TABLE buckets;
|
@ -1,194 +0,0 @@
|
||||
CREATE TABLE buckets (
|
||||
bucketname BYTEA
|
||||
PRIMARY KEY,
|
||||
delim INT
|
||||
NOT NULL
|
||||
CHECK (delim > 0 AND delim < 255)
|
||||
);
|
||||
|
||||
-- until the KeyValueStore interface supports passing the bucket separately, or
|
||||
-- until storj actually supports changing the delimiter character per bucket, this
|
||||
-- dummy row should suffice for everything.
|
||||
INSERT INTO buckets (bucketname, delim) VALUES (''::BYTEA, ascii('/'));
|
||||
|
||||
|
||||
CREATE TABLE pathdata (
|
||||
bucket BYTEA
|
||||
NOT NULL
|
||||
REFERENCES buckets (bucketname),
|
||||
fullpath BYTEA
|
||||
NOT NULL
|
||||
CHECK (fullpath <> ''),
|
||||
metadata BYTEA
|
||||
NOT NULL,
|
||||
|
||||
PRIMARY KEY (bucket, fullpath)
|
||||
);
|
||||
|
||||
CREATE VIEW pathdata_pretty AS
|
||||
SELECT encode(bucket, 'escape') AS bucket,
|
||||
encode(fullpath, 'escape') AS fullpath,
|
||||
encode(metadata, 'escape') AS metadata
|
||||
FROM pathdata;
|
||||
|
||||
|
||||
-- given a path as might be found in the pathdata table, truncate it after the next delimiter to be
|
||||
-- found at or after 'afterpos', if any.
|
||||
--
|
||||
-- Examples:
|
||||
--
|
||||
-- truncate_after(''::BYTEA, ascii('/'), 1) -> ''::BYTEA
|
||||
-- truncate_after('foo'::BYTEA, ascii('/'), 1) -> 'foo'::BYTEA
|
||||
-- truncate_after('foo/'::BYTEA, ascii('/'), 1) -> 'foo/'::BYTEA
|
||||
-- truncate_after('foo/bar/baz'::BYTEA, ascii('/'), 4) -> 'foo/'::BYTEA
|
||||
-- truncate_after('foo/bar/baz'::BYTEA, ascii('/'), 5) -> 'foo/bar/'::BYTEA
|
||||
-- truncate_after('foo/bar/baz'::BYTEA, ascii('/'), 8) -> 'foo/bar/'::BYTEA
|
||||
-- truncate_after('foo/bar/baz'::BYTEA, ascii('/'), 9) -> 'foo/bar/baz'::BYTEA
|
||||
-- truncate_after('foo//bar/bz'::BYTEA, ascii('/'), 4) -> 'foo/'::BYTEA
|
||||
-- truncate_after('foo//bar/bz'::BYTEA, ascii('/'), 5) -> 'foo//'::BYTEA
|
||||
--
|
||||
CREATE FUNCTION truncate_after(bpath BYTEA, delim INTEGER, afterpos INTEGER) RETURNS BYTEA AS $$
|
||||
DECLARE
|
||||
suff BYTEA;
|
||||
delimpos INTEGER;
|
||||
BEGIN
|
||||
suff := substring(bpath FROM afterpos);
|
||||
delimpos := position(set_byte(' '::BYTEA, 0, delim) IN suff);
|
||||
IF delimpos > 0 THEN
|
||||
RETURN substring(bpath FROM 1 FOR (afterpos + delimpos - 1));
|
||||
END IF;
|
||||
RETURN bpath;
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
||||
|
||||
|
||||
CREATE FUNCTION bytea_increment(b BYTEA) RETURNS BYTEA AS $$
|
||||
BEGIN
|
||||
WHILE b <> ''::BYTEA AND get_byte(b, octet_length(b) - 1) = 255 LOOP
|
||||
b := substring(b FROM 1 FOR octet_length(b) - 1);
|
||||
END LOOP;
|
||||
IF b = ''::BYTEA THEN
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
RETURN set_byte(b, octet_length(b) - 1, get_byte(b, octet_length(b) - 1) + 1);
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
||||
|
||||
|
||||
-- Given a path as might be found in the pathdata table, with a delimeter appended if that path
|
||||
-- has any sub-elements, return the next possible path that _could_ be in the table (skipping over
|
||||
-- any potential sub-elements).
|
||||
--
|
||||
-- Examples:
|
||||
--
|
||||
-- component_increment('/'::BYTEA, ascii('/')) -> '0'::BYTEA
|
||||
-- (nothing can be between '/' and '0' other than subpaths under '/')
|
||||
--
|
||||
-- component_increment('/foo/bar/'::BYTEA, ascii('/')) -> '/foo/bar0'::BYTEA
|
||||
--
|
||||
-- component_increment('/foo/barboom'::BYTEA, ascii('/')) -> ('/foo/barboom' || E'\\x00')
|
||||
-- (nothing can be between '/foo/barboom' and '/foo/barboom\x00' in normal BYTEA ordering)
|
||||
--
|
||||
-- component_increment(E'\\xFEFFFF'::BYTEA, 255) -> E'\\xFF'::BYTEA
|
||||
--
|
||||
CREATE FUNCTION component_increment(bpath BYTEA, delim INTEGER) RETURNS BYTEA AS $$
|
||||
SELECT CASE WHEN get_byte(bpath, octet_length(bpath) - 1) = delim
|
||||
THEN CASE WHEN delim = 255
|
||||
THEN bytea_increment(bpath)
|
||||
ELSE set_byte(bpath, octet_length(bpath) - 1, delim + 1)
|
||||
END
|
||||
ELSE bpath || E'\\x00'::BYTEA
|
||||
END;
|
||||
$$ LANGUAGE 'sql' IMMUTABLE STRICT;
|
||||
|
||||
|
||||
CREATE TYPE path_and_meta AS (
|
||||
fullpath BYTEA,
|
||||
metadata BYTEA
|
||||
);
|
||||
|
||||
CREATE FUNCTION list_directory(bucket BYTEA, dirpath BYTEA, start_at BYTEA = ''::BYTEA, limit_to INTEGER = NULL)
|
||||
RETURNS SETOF path_and_meta AS $$
|
||||
WITH RECURSIVE
|
||||
inputs AS (
|
||||
SELECT CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE dirpath END AS range_low,
|
||||
CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE bytea_increment(dirpath) END AS range_high,
|
||||
octet_length(dirpath) + 1 AS component_start,
|
||||
b.delim AS delim,
|
||||
b.bucketname AS bucket
|
||||
FROM buckets b
|
||||
WHERE bucketname = bucket
|
||||
),
|
||||
distinct_prefix (truncatedpath) AS (
|
||||
SELECT (SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE (i.range_low IS NULL OR pd.fullpath > i.range_low)
|
||||
AND (i.range_high IS NULL OR pd.fullpath < i.range_high)
|
||||
AND (start_at = '' OR pd.fullpath >= start_at)
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath
|
||||
LIMIT 1)
|
||||
FROM inputs i
|
||||
UNION ALL
|
||||
SELECT (SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE pd.fullpath >= component_increment(pfx.truncatedpath, i.delim)
|
||||
AND (i.range_high IS NULL OR pd.fullpath < i.range_high)
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath
|
||||
LIMIT 1)
|
||||
FROM distinct_prefix pfx, inputs i
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
)
|
||||
SELECT pfx.truncatedpath AS fullpath,
|
||||
pd.metadata
|
||||
FROM distinct_prefix pfx LEFT OUTER JOIN pathdata pd ON pfx.truncatedpath = pd.fullpath
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
UNION ALL
|
||||
-- this one, if it exists, can't be part of distinct_prefix (or it would cause us to skip over all
|
||||
-- subcontents of the prefix we're looking for), so we tack it on here
|
||||
SELECT pd.fullpath, pd.metadata FROM pathdata pd, inputs i WHERE pd.fullpath = i.range_low
|
||||
ORDER BY fullpath
|
||||
LIMIT limit_to;
|
||||
$$ LANGUAGE 'sql' STABLE;
|
||||
|
||||
CREATE FUNCTION list_directory_reverse(bucket BYTEA, dirpath BYTEA, start_at BYTEA = ''::BYTEA, limit_to INTEGER = NULL)
|
||||
RETURNS SETOF path_and_meta AS $$
|
||||
WITH RECURSIVE
|
||||
inputs AS (
|
||||
SELECT CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE dirpath END AS range_low,
|
||||
CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE bytea_increment(dirpath) END AS range_high,
|
||||
octet_length(dirpath) + 1 AS component_start,
|
||||
b.delim AS delim,
|
||||
b.bucketname AS bucket
|
||||
FROM buckets b
|
||||
WHERE bucketname = bucket
|
||||
),
|
||||
distinct_prefix (truncatedpath) AS (
|
||||
SELECT (SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE (i.range_low IS NULL OR pd.fullpath >= i.range_low)
|
||||
AND (i.range_high IS NULL OR pd.fullpath < i.range_high)
|
||||
AND (start_at = '' OR pd.fullpath <= start_at)
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath DESC
|
||||
LIMIT 1)
|
||||
FROM inputs i
|
||||
UNION ALL
|
||||
SELECT (SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE (i.range_low IS NULL OR pd.fullpath >= i.range_low)
|
||||
AND pd.fullpath < pfx.truncatedpath
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath DESC
|
||||
LIMIT 1)
|
||||
FROM distinct_prefix pfx, inputs i
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
)
|
||||
SELECT pfx.truncatedpath AS fullpath,
|
||||
pd.metadata
|
||||
FROM distinct_prefix pfx LEFT OUTER JOIN pathdata pd ON pfx.truncatedpath = pd.fullpath
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
ORDER BY fullpath DESC
|
||||
LIMIT limit_to;
|
||||
$$ LANGUAGE 'sql' STABLE;
|
@ -1,44 +0,0 @@
|
||||
CREATE OR REPLACE FUNCTION list_directory(bucket BYTEA, dirpath BYTEA, start_at BYTEA = ''::BYTEA, limit_to INTEGER = NULL)
|
||||
RETURNS SETOF path_and_meta AS $$
|
||||
WITH RECURSIVE
|
||||
inputs AS (
|
||||
SELECT CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE dirpath END AS range_low,
|
||||
CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE bytea_increment(dirpath) END AS range_high,
|
||||
octet_length(dirpath) + 1 AS component_start,
|
||||
b.delim AS delim,
|
||||
b.bucketname AS bucket
|
||||
FROM buckets b
|
||||
WHERE bucketname = bucket
|
||||
),
|
||||
distinct_prefix (truncatedpath) AS (
|
||||
SELECT (SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE (i.range_low IS NULL OR pd.fullpath > i.range_low)
|
||||
AND (i.range_high IS NULL OR pd.fullpath < i.range_high)
|
||||
AND (start_at = '' OR pd.fullpath >= start_at)
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath
|
||||
LIMIT 1)
|
||||
FROM inputs i
|
||||
UNION ALL
|
||||
SELECT (SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE pd.fullpath >= component_increment(pfx.truncatedpath, i.delim)
|
||||
AND (i.range_high IS NULL OR pd.fullpath < i.range_high)
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath
|
||||
LIMIT 1)
|
||||
FROM distinct_prefix pfx, inputs i
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
)
|
||||
SELECT pfx.truncatedpath AS fullpath,
|
||||
pd.metadata
|
||||
FROM distinct_prefix pfx LEFT OUTER JOIN pathdata pd ON pfx.truncatedpath = pd.fullpath
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
UNION ALL
|
||||
-- this one, if it exists, can't be part of distinct_prefix (or it would cause us to skip over all
|
||||
-- subcontents of the prefix we're looking for), so we tack it on here
|
||||
SELECT pd.fullpath, pd.metadata FROM pathdata pd, inputs i WHERE pd.fullpath = i.range_low
|
||||
ORDER BY fullpath
|
||||
LIMIT limit_to;
|
||||
$$ LANGUAGE 'sql' STABLE;
|
@ -1,46 +0,0 @@
|
||||
CREATE OR REPLACE FUNCTION list_directory(bucket BYTEA, dirpath BYTEA, start_at BYTEA = ''::BYTEA, limit_to INTEGER = NULL)
|
||||
RETURNS SETOF path_and_meta AS $$
|
||||
WITH RECURSIVE
|
||||
inputs AS (
|
||||
SELECT CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE dirpath END AS range_low,
|
||||
CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE bytea_increment(dirpath) END AS range_high,
|
||||
octet_length(dirpath) + 1 AS component_start,
|
||||
b.delim AS delim,
|
||||
b.bucketname AS bucket
|
||||
FROM buckets b
|
||||
WHERE bucketname = bucket
|
||||
),
|
||||
distinct_prefix (bucket, truncatedpath) AS (
|
||||
SELECT i.bucket,
|
||||
(SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE (i.range_low IS NULL OR pd.fullpath > i.range_low)
|
||||
AND (i.range_high IS NULL OR pd.fullpath < i.range_high)
|
||||
AND (start_at = '' OR pd.fullpath >= start_at)
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath
|
||||
LIMIT 1)
|
||||
FROM inputs i
|
||||
UNION ALL
|
||||
SELECT i.bucket,
|
||||
(SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE pd.fullpath >= component_increment(pfx.truncatedpath, i.delim)
|
||||
AND (i.range_high IS NULL OR pd.fullpath < i.range_high)
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath
|
||||
LIMIT 1)
|
||||
FROM distinct_prefix pfx, inputs i
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
)
|
||||
SELECT pfx.truncatedpath AS fullpath,
|
||||
pd.metadata
|
||||
FROM distinct_prefix pfx LEFT OUTER JOIN pathdata pd ON pfx.truncatedpath = pd.fullpath AND pd.bucket = pfx.bucket
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
UNION ALL
|
||||
-- this one, if it exists, can't be part of distinct_prefix (or it would cause us to skip over all
|
||||
-- subcontents of the prefix we're looking for), so we tack it on here
|
||||
SELECT pd.fullpath, pd.metadata FROM pathdata pd, inputs i WHERE pd.fullpath = i.range_low AND pd.bucket = i.bucket
|
||||
ORDER BY fullpath
|
||||
LIMIT limit_to;
|
||||
$$ LANGUAGE 'sql' STABLE;
|
@ -1,2 +0,0 @@
|
||||
alter table pathdata alter column bucket drop default;
|
||||
alter table pathdata add constraint "pathdata_bucket_fkey" FOREIGN KEY (bucket) REFERENCES buckets(bucketname);
|
@ -1,2 +0,0 @@
|
||||
alter table pathdata alter column bucket set default '';
|
||||
alter table pathdata drop constraint pathdata_bucket_fkey;
|
@ -1,195 +0,0 @@
|
||||
CREATE TABLE buckets (
|
||||
bucketname BYTEA
|
||||
PRIMARY KEY,
|
||||
delim INT
|
||||
NOT NULL
|
||||
CHECK (delim > 0 AND delim < 255)
|
||||
);
|
||||
|
||||
-- until the KeyValueStore interface supports passing the bucket separately, or
|
||||
-- until storj actually supports changing the delimiter character per bucket, this
|
||||
-- dummy row should suffice for everything.
|
||||
INSERT INTO buckets (bucketname, delim) VALUES (''::BYTEA, ascii('/'));
|
||||
|
||||
|
||||
CREATE TABLE pathdata (
|
||||
bucket BYTEA
|
||||
NOT NULL
|
||||
REFERENCES buckets (bucketname),
|
||||
fullpath BYTEA
|
||||
NOT NULL
|
||||
CHECK (fullpath <> ''),
|
||||
metadata BYTEA
|
||||
NOT NULL,
|
||||
|
||||
PRIMARY KEY (bucket, fullpath)
|
||||
);
|
||||
|
||||
CREATE VIEW pathdata_pretty AS
|
||||
SELECT encode(bucket, 'escape') AS bucket,
|
||||
encode(fullpath, 'escape') AS fullpath,
|
||||
encode(metadata, 'escape') AS metadata
|
||||
FROM pathdata;
|
||||
|
||||
|
||||
-- given a path as might be found in the pathdata table, truncate it after the next delimiter to be
|
||||
-- found at or after 'afterpos', if any.
|
||||
--
|
||||
-- Examples:
|
||||
--
|
||||
-- truncate_after(''::BYTEA, ascii('/'), 1) -> ''::BYTEA
|
||||
-- truncate_after('foo'::BYTEA, ascii('/'), 1) -> 'foo'::BYTEA
|
||||
-- truncate_after('foo/'::BYTEA, ascii('/'), 1) -> 'foo/'::BYTEA
|
||||
-- truncate_after('foo/bar/baz'::BYTEA, ascii('/'), 4) -> 'foo/'::BYTEA
|
||||
-- truncate_after('foo/bar/baz'::BYTEA, ascii('/'), 5) -> 'foo/bar/'::BYTEA
|
||||
-- truncate_after('foo/bar/baz'::BYTEA, ascii('/'), 8) -> 'foo/bar/'::BYTEA
|
||||
-- truncate_after('foo/bar/baz'::BYTEA, ascii('/'), 9) -> 'foo/bar/baz'::BYTEA
|
||||
-- truncate_after('foo//bar/bz'::BYTEA, ascii('/'), 4) -> 'foo/'::BYTEA
|
||||
-- truncate_after('foo//bar/bz'::BYTEA, ascii('/'), 5) -> 'foo//'::BYTEA
|
||||
--
|
||||
CREATE FUNCTION truncate_after(bpath BYTEA, delim INTEGER, afterpos INTEGER) RETURNS BYTEA AS $$
|
||||
DECLARE
|
||||
suff BYTEA;
|
||||
delimpos INTEGER;
|
||||
BEGIN
|
||||
suff := substring(bpath FROM afterpos);
|
||||
delimpos := position(set_byte(' '::BYTEA, 0, delim) IN suff);
|
||||
IF delimpos > 0 THEN
|
||||
RETURN substring(bpath FROM 1 FOR (afterpos + delimpos - 1));
|
||||
END IF;
|
||||
RETURN bpath;
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
||||
|
||||
|
||||
CREATE FUNCTION bytea_increment(b BYTEA) RETURNS BYTEA AS $$
|
||||
BEGIN
|
||||
WHILE b <> ''::BYTEA AND get_byte(b, octet_length(b) - 1) = 255 LOOP
|
||||
b := substring(b FROM 1 FOR octet_length(b) - 1);
|
||||
END LOOP;
|
||||
IF b = ''::BYTEA THEN
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
RETURN set_byte(b, octet_length(b) - 1, get_byte(b, octet_length(b) - 1) + 1);
|
||||
END;
|
||||
$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
|
||||
|
||||
|
||||
-- Given a path as might be found in the pathdata table, with a delimeter appended if that path
|
||||
-- has any sub-elements, return the next possible path that _could_ be in the table (skipping over
|
||||
-- any potential sub-elements).
|
||||
--
|
||||
-- Examples:
|
||||
--
|
||||
-- component_increment('/'::BYTEA, ascii('/')) -> '0'::BYTEA
|
||||
-- (nothing can be between '/' and '0' other than subpaths under '/')
|
||||
--
|
||||
-- component_increment('/foo/bar/'::BYTEA, ascii('/')) -> '/foo/bar0'::BYTEA
|
||||
--
|
||||
-- component_increment('/foo/barboom'::BYTEA, ascii('/')) -> ('/foo/barboom' || E'\\x00')
|
||||
-- (nothing can be between '/foo/barboom' and '/foo/barboom\x00' in normal BYTEA ordering)
|
||||
--
|
||||
-- component_increment(E'\\xFEFFFF'::BYTEA, 255) -> E'\\xFF'::BYTEA
|
||||
--
|
||||
CREATE FUNCTION component_increment(bpath BYTEA, delim INTEGER) RETURNS BYTEA AS $$
|
||||
SELECT CASE WHEN get_byte(bpath, octet_length(bpath) - 1) = delim
|
||||
THEN CASE WHEN delim = 255
|
||||
THEN bytea_increment(bpath)
|
||||
ELSE set_byte(bpath, octet_length(bpath) - 1, delim + 1)
|
||||
END
|
||||
ELSE bpath || E'\\x00'::BYTEA
|
||||
END;
|
||||
$$ LANGUAGE 'sql' IMMUTABLE STRICT;
|
||||
|
||||
|
||||
CREATE TYPE path_and_meta AS (
|
||||
fullpath BYTEA,
|
||||
metadata BYTEA
|
||||
);
|
||||
|
||||
CREATE OR REPLACE FUNCTION list_directory(bucket BYTEA, dirpath BYTEA, start_at BYTEA = ''::BYTEA, limit_to INTEGER = NULL)
|
||||
RETURNS SETOF path_and_meta AS $$
|
||||
WITH RECURSIVE
|
||||
inputs AS (
|
||||
SELECT CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE dirpath END AS range_low,
|
||||
CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE bytea_increment(dirpath) END AS range_high,
|
||||
octet_length(dirpath) + 1 AS component_start,
|
||||
b.delim AS delim,
|
||||
b.bucketname AS bucket
|
||||
FROM buckets b
|
||||
WHERE bucketname = bucket
|
||||
),
|
||||
distinct_prefix (bucket, truncatedpath) AS (
|
||||
SELECT i.bucket,
|
||||
(SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE (i.range_low IS NULL OR pd.fullpath > i.range_low)
|
||||
AND (i.range_high IS NULL OR pd.fullpath < i.range_high)
|
||||
AND (start_at = '' OR pd.fullpath >= start_at)
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath
|
||||
LIMIT 1)
|
||||
FROM inputs i
|
||||
UNION ALL
|
||||
SELECT i.bucket,
|
||||
(SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE pd.fullpath >= component_increment(pfx.truncatedpath, i.delim)
|
||||
AND (i.range_high IS NULL OR pd.fullpath < i.range_high)
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath
|
||||
LIMIT 1)
|
||||
FROM distinct_prefix pfx, inputs i
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
)
|
||||
SELECT pfx.truncatedpath AS fullpath,
|
||||
pd.metadata
|
||||
FROM distinct_prefix pfx LEFT OUTER JOIN pathdata pd ON pfx.truncatedpath = pd.fullpath AND pd.bucket = pfx.bucket
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
UNION ALL
|
||||
-- this one, if it exists, can't be part of distinct_prefix (or it would cause us to skip over all
|
||||
-- subcontents of the prefix we're looking for), so we tack it on here
|
||||
SELECT pd.fullpath, pd.metadata FROM pathdata pd, inputs i WHERE pd.fullpath = i.range_low AND pd.bucket = i.bucket
|
||||
ORDER BY fullpath
|
||||
LIMIT limit_to;
|
||||
$$ LANGUAGE 'sql' STABLE;
|
||||
CREATE FUNCTION list_directory_reverse(bucket BYTEA, dirpath BYTEA, start_at BYTEA = ''::BYTEA, limit_to INTEGER = NULL)
|
||||
RETURNS SETOF path_and_meta AS $$
|
||||
WITH RECURSIVE
|
||||
inputs AS (
|
||||
SELECT CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE dirpath END AS range_low,
|
||||
CASE WHEN dirpath = ''::BYTEA THEN NULL ELSE bytea_increment(dirpath) END AS range_high,
|
||||
octet_length(dirpath) + 1 AS component_start,
|
||||
b.delim AS delim,
|
||||
b.bucketname AS bucket
|
||||
FROM buckets b
|
||||
WHERE bucketname = bucket
|
||||
),
|
||||
distinct_prefix (truncatedpath) AS (
|
||||
SELECT (SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE (i.range_low IS NULL OR pd.fullpath >= i.range_low)
|
||||
AND (i.range_high IS NULL OR pd.fullpath < i.range_high)
|
||||
AND (start_at = '' OR pd.fullpath <= start_at)
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath DESC
|
||||
LIMIT 1)
|
||||
FROM inputs i
|
||||
UNION ALL
|
||||
SELECT (SELECT truncate_after(pd.fullpath, i.delim, i.component_start)
|
||||
FROM pathdata pd
|
||||
WHERE (i.range_low IS NULL OR pd.fullpath >= i.range_low)
|
||||
AND pd.fullpath < pfx.truncatedpath
|
||||
AND pd.bucket = i.bucket
|
||||
ORDER BY pd.fullpath DESC
|
||||
LIMIT 1)
|
||||
FROM distinct_prefix pfx, inputs i
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
)
|
||||
SELECT pfx.truncatedpath AS fullpath,
|
||||
pd.metadata
|
||||
FROM distinct_prefix pfx LEFT OUTER JOIN pathdata pd ON pfx.truncatedpath = pd.fullpath
|
||||
WHERE pfx.truncatedpath IS NOT NULL
|
||||
ORDER BY fullpath DESC
|
||||
LIMIT limit_to;
|
||||
$$ LANGUAGE 'sql' STABLE;
|
@ -1,17 +0,0 @@
|
||||
DROP VIEW pathdata_pretty;
|
||||
DROP FUNCTION list_directory_reverse(BYTEA, BYTEA, BYTEA, INTEGER);
|
||||
DROP FUNCTION list_directory(BYTEA, BYTEA, BYTEA, INTEGER);
|
||||
DROP FUNCTION component_increment(BYTEA, INTEGER);
|
||||
DROP FUNCTION bytea_increment(BYTEA);
|
||||
DROP FUNCTION truncate_after(BYTEA, INTEGER, INTEGER);
|
||||
DROP TYPE path_and_meta;
|
||||
CREATE TABLE IF NOT EXISTS pathdata_v2 (
|
||||
fullpath BYTEA PRIMARY KEY,
|
||||
metadata BYTEA NOT NULL
|
||||
);
|
||||
ALTER TABLE pathdata_v2 ALTER COLUMN fullpath SET STORAGE MAIN;
|
||||
ALTER TABLE pathdata_v2 ALTER COLUMN metadata SET STORAGE MAIN;
|
||||
INSERT INTO pathdata_v2 (fullpath, metadata) SELECT fullpath, metadata FROM pathdata;
|
||||
DROP TABLE pathdata;
|
||||
DROP TABLE buckets;
|
||||
ALTER TABLE pathdata_v2 RENAME TO pathdata;
|
File diff suppressed because one or more lines are too long
@ -1,54 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
//go:generate go-bindata -o data.go -pkg schema -ignore ".*go" .
|
||||
//go:generate bash -c "sed -i'' '1i //lint:file-ignore * generated file\n' data.go"
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database/postgres"
|
||||
bindata "github.com/golang-migrate/migrate/v4/source/go_bindata"
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/private/dbutil/pgutil"
|
||||
"storj.io/private/tagsql"
|
||||
)
|
||||
|
||||
// PrepareDB applies schema migrations as necessary to the given database to
|
||||
// get it up to date.
|
||||
func PrepareDB(ctx context.Context, db tagsql.DB, dbURL string) error {
|
||||
srcDriver, err := bindata.WithInstance(bindata.Resource(AssetNames(), Asset))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
schema, err := pgutil.ParseSchemaFromConnstr(dbURL)
|
||||
if err != nil {
|
||||
return errs.New("error parsing schema: %+v", err)
|
||||
}
|
||||
if schema != "" {
|
||||
err := pgutil.CreateSchema(ctx, db, schema)
|
||||
if err != nil {
|
||||
return errs.New("error creating schema: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
dbDriver, err := postgres.WithInstance(db.Internal(), &postgres.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := migrate.NewWithInstance("go-bindata migrations", srcDriver, "postgreskv db", dbDriver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = m.Up()
|
||||
if errors.Is(err, migrate.ErrNoChange) {
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
Loading…
Reference in New Issue
Block a user