1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 13:19:43 +02:00

Bumped database driver dependencies

Latest used for MySQL, SQL Server and PostgreSQL
This commit is contained in:
sauls8t 2019-08-15 14:51:40 +01:00
parent ffacf17c5f
commit 32dbab826d
31 changed files with 2431 additions and 1611 deletions

18
Gopkg.lock generated
View file

@ -27,14 +27,15 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:cf0505eb7d4704d5ab445353dc64122acf56d13937e20dac5570b995cb21232c" digest = "1:159f2147aa2b2e5fa020480ab46ab26f3e6c3ebe5d0191632622cc9aa81bcffe"
name = "github.com/denisenkom/go-mssqldb" name = "github.com/denisenkom/go-mssqldb"
packages = [ packages = [
".", ".",
"internal/cp", "internal/cp",
"internal/querytext",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "a8ed825ac8537383ef814eaf5a16174751796a2b" revision = "db2462fef53bd29c619fe40f73cbfcc12be79d9f"
[[projects]] [[projects]]
digest = "1:217f778e19b8d206112c21d21a7cc72ca3cb493b67631680a2324bc50335d432" digest = "1:217f778e19b8d206112c21d21a7cc72ca3cb493b67631680a2324bc50335d432"
@ -93,12 +94,12 @@
version = "v1.0" version = "v1.0"
[[projects]] [[projects]]
digest = "1:adea5a94903eb4384abef30f3d878dc9ff6b6b5b0722da25b82e5169216dfb61" digest = "1:ec6f9bf5e274c833c911923c9193867f3f18788c461f76f05f62bb1510e0ae65"
name = "github.com/go-sql-driver/mysql" name = "github.com/go-sql-driver/mysql"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "d523deb1b23d913de5bdada721a6071e71283618" revision = "72cd26f257d44c1114970e19afddcd812016007e"
version = "v1.4.0" version = "v1.4.1"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -168,15 +169,16 @@
revision = "d161d7a76b5661016ad0b085869f77fd410f3e6a" revision = "d161d7a76b5661016ad0b085869f77fd410f3e6a"
[[projects]] [[projects]]
digest = "1:8ef506fc2bb9ced9b151dafa592d4046063d744c646c1bbe801982ce87e4bc24" digest = "1:12cb143f2148bf54bcd9fe622abac17325e85eeb1d84b8ec6caf1c80232108fd"
name = "github.com/lib/pq" name = "github.com/lib/pq"
packages = [ packages = [
".", ".",
"oid", "oid",
"scram",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "4ded0e9383f75c197b3a2aaa6d590ac52df6fd79" revision = "3427c32cb71afc948325f299f040e53c1dd78979"
version = "v1.0.0" version = "v1.2.0"
[[projects]] [[projects]]
branch = "master" branch = "master"

File diff suppressed because one or more lines are too long

View file

@ -117,6 +117,27 @@ _, err := db.ExecContext(ctx, "sp_RunMe",
) )
``` ```
## Reading Output Parameters from a Stored Procedure with Resultset
To read output parameters from a stored procedure with resultset, make sure you read all the rows before reading the output parameters:
```go
sqltextcreate := `
CREATE PROCEDURE spwithoutputandrows
@bitparam BIT OUTPUT
AS BEGIN
SET @bitparam = 1
SELECT 'Row 1'
END
`
var bitout int64
rows, err := db.QueryContext(ctx, "spwithoutputandrows", sql.Named("bitparam", sql.Out{Dest: &bitout}))
var strrow string
for rows.Next() {
err = rows.Scan(&strrow)
}
fmt.Printf("bitparam is %d", bitout)
```
## Caveat for local temporary tables ## Caveat for local temporary tables
Due to protocol limitations, temporary tables will only be allocated on the connection Due to protocol limitations, temporary tables will only be allocated on the connection
@ -166,6 +187,19 @@ _, err := db.ExecContext(ctx, "theproc", &rs)
log.Printf("status=%d", rs) log.Printf("status=%d", rs)
``` ```
or
```
var rs mssql.ReturnStatus
_, err := db.QueryContext(ctx, "theproc", &rs)
for rows.Next() {
err = rows.Scan(&val)
}
log.Printf("status=%d", rs)
```
Limitation: ReturnStatus cannot be retrieved using `QueryRow`.
## Parameters ## Parameters
The `sqlserver` driver uses normal MS SQL Server syntax and expects parameters in The `sqlserver` driver uses normal MS SQL Server syntax and expects parameters in
@ -189,7 +223,7 @@ are supported:
* "cloud.google.com/go/civil".Date -> date * "cloud.google.com/go/civil".Date -> date
* "cloud.google.com/go/civil".DateTime -> datetime2 * "cloud.google.com/go/civil".DateTime -> datetime2
* "cloud.google.com/go/civil".Time -> time * "cloud.google.com/go/civil".Time -> time
* mssql.TVPType -> Table Value Parameter (TDS version dependent) * mssql.TVP -> Table Value Parameter (TDS version dependent)
## Important Notes ## Important Notes

10
vendor/github.com/denisenkom/go-mssqldb/go.mod generated vendored Normal file
View file

@ -0,0 +1,10 @@
module github.com/denisenkom/go-mssqldb
go 1.11
require (
cloud.google.com/go v0.37.4
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
)

168
vendor/github.com/denisenkom/go-mssqldb/go.sum generated vendored Normal file
View file

@ -0,0 +1,168 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
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.2 h1:4y4L7BdHenTfZL0HervofNTHh9Ad6mNX72cQvl+5eH0=
cloud.google.com/go v0.37.2/go.mod h1:H8IAquKe2L30IxoupDgqTaQvKSwF/c8prYHynGIWQbA=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
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/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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_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/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.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/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A=
go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/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-20181114220301-adae6a3d119a/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/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-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/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-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
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.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU=
google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View file

@ -1,4 +1,8 @@
package mssql // Package querytext is the old query parser and parameter substitute process.
// Do not use on new code.
//
// This package is not subject to any API compatibility guarantee.
package querytext
import ( import (
"bytes" "bytes"
@ -40,7 +44,11 @@ func (p *parser) write(ch rune) {
type stateFunc func(*parser) stateFunc type stateFunc func(*parser) stateFunc
func parseParams(query string) (string, int) { // ParseParams rewrites the query from using "?" placeholders
// to using "@pN" parameter names that SQL Server will accept.
//
// This function and package is not subject to any API compatibility guarantee.
func ParseParams(query string) (string, int) {
p := &parser{ p := &parser{
r: bytes.NewReader([]byte(query)), r: bytes.NewReader([]byte(query)),
namedParams: map[string]bool{}, namedParams: map[string]bool{},

View file

@ -14,6 +14,8 @@ import (
"strings" "strings"
"time" "time"
"unicode" "unicode"
"github.com/denisenkom/go-mssqldb/internal/querytext"
) )
// ReturnStatus may be used to return the return value from a proc. // ReturnStatus may be used to return the return value from a proc.
@ -351,7 +353,6 @@ func (d *Driver) connect(ctx context.Context, c *Connector, params connectParams
processQueryText: d.processQueryText, processQueryText: d.processQueryText,
connectionGood: true, connectionGood: true,
} }
conn.sess.log = d.log
return conn, nil return conn, nil
} }
@ -386,7 +387,7 @@ func (c *Conn) Prepare(query string) (driver.Stmt, error) {
func (c *Conn) prepareContext(ctx context.Context, query string) (*Stmt, error) { func (c *Conn) prepareContext(ctx context.Context, query string) (*Stmt, error) {
paramCount := -1 paramCount := -1
if c.processQueryText { if c.processQueryText {
query, paramCount = parseParams(query) query, paramCount = querytext.ParseParams(query)
} }
return &Stmt{c, query, paramCount, nil}, nil return &Stmt{c, query, paramCount, nil}, nil
} }
@ -456,13 +457,13 @@ func (s *Stmt) sendQuery(args []namedValue) (err error) {
var params []param var params []param
if isProc(s.query) { if isProc(s.query) {
proc.name = s.query proc.name = s.query
params, _, err = s.makeRPCParams(args, 0) params, _, err = s.makeRPCParams(args, true)
if err != nil { if err != nil {
return return
} }
} else { } else {
var decls []string var decls []string
params, decls, err = s.makeRPCParams(args, 2) params, decls, err = s.makeRPCParams(args, false)
if err != nil { if err != nil {
return return
} }
@ -534,8 +535,12 @@ func isProc(s string) bool {
return true return true
} }
func (s *Stmt) makeRPCParams(args []namedValue, offset int) ([]param, []string, error) { func (s *Stmt) makeRPCParams(args []namedValue, isProc bool) ([]param, []string, error) {
var err error var err error
var offset int
if !isProc {
offset = 2
}
params := make([]param, len(args)+offset) params := make([]param, len(args)+offset)
decls := make([]string, len(args)) decls := make([]string, len(args))
for i, val := range args { for i, val := range args {
@ -546,7 +551,7 @@ func (s *Stmt) makeRPCParams(args []namedValue, offset int) ([]param, []string,
var name string var name string
if len(val.Name) > 0 { if len(val.Name) > 0 {
name = "@" + val.Name name = "@" + val.Name
} else { } else if !isProc {
name = fmt.Sprintf("@p%d", val.Ordinal) name = fmt.Sprintf("@p%d", val.Ordinal)
} }
params[i+offset].Name = name params[i+offset].Name = name
@ -711,6 +716,8 @@ func (rc *Rows) Next(dest []driver.Value) error {
if tokdata.isError() { if tokdata.isError() {
return rc.stmt.c.checkBadConn(tokdata.getError()) return rc.stmt.c.checkBadConn(tokdata.getError())
} }
case ReturnStatus:
rc.stmt.c.setReturnStatus(tokdata)
case error: case error:
return rc.stmt.c.checkBadConn(tokdata) return rc.stmt.c.checkBadConn(tokdata)
} }

View file

@ -112,7 +112,7 @@ func (c *Conn) CheckNamedValue(nv *driver.NamedValue) error {
*v = 0 // By default the return value should be zero. *v = 0 // By default the return value should be zero.
c.returnStatus = v c.returnStatus = v
return driver.ErrRemoveArgument return driver.ErrRemoveArgument
case TVPType: case TVP:
return nil return nil
default: default:
var err error var err error
@ -162,15 +162,27 @@ func (s *Stmt) makeParamExtra(val driver.Value) (res param, err error) {
case sql.Out: case sql.Out:
res, err = s.makeParam(val.Dest) res, err = s.makeParam(val.Dest)
res.Flags = fByRevValue res.Flags = fByRevValue
case TVPType: case TVP:
err = val.check() err = val.check()
if err != nil { if err != nil {
return return
} }
res.ti.UdtInfo.TypeName = val.TVPTypeName schema, name, errGetName := getSchemeAndName(val.TypeName)
res.ti.UdtInfo.SchemaName = val.TVPScheme if errGetName != nil {
return
}
res.ti.UdtInfo.TypeName = name
res.ti.UdtInfo.SchemaName = schema
res.ti.TypeId = typeTvp res.ti.TypeId = typeTvp
res.buffer, err = val.encode() columnStr, tvpFieldIndexes, errCalTypes := val.columnTypes()
if errCalTypes != nil {
err = errCalTypes
return
}
res.buffer, err = val.encode(schema, name, columnStr, tvpFieldIndexes)
if err != nil {
return
}
res.ti.Size = len(res.buffer) res.ti.Size = len(res.buffer)
default: default:

View file

@ -655,28 +655,29 @@ func sendAttention(buf *tdsBuffer) error {
} }
type connectParams struct { type connectParams struct {
logFlags uint64 logFlags uint64
port uint64 port uint64
host string host string
instance string instance string
database string database string
user string user string
password string password string
dial_timeout time.Duration dial_timeout time.Duration
conn_timeout time.Duration conn_timeout time.Duration
keepAlive time.Duration keepAlive time.Duration
encrypt bool encrypt bool
disableEncryption bool disableEncryption bool
trustServerCertificate bool trustServerCertificate bool
certificate string certificate string
hostInCertificate string hostInCertificate string
serverSPN string hostInCertificateProvided bool
workstation string serverSPN string
appname string workstation string
typeFlags uint8 appname string
failOverPartner string typeFlags uint8
failOverPort uint64 failOverPartner string
packetSize uint16 failOverPort uint64
packetSize uint16
} }
func splitConnectionString(dsn string) (res map[string]string) { func splitConnectionString(dsn string) (res map[string]string) {
@ -1050,8 +1051,11 @@ func parseConnectParams(dsn string) (connectParams, error) {
} }
p.certificate = params["certificate"] p.certificate = params["certificate"]
p.hostInCertificate, ok = params["hostnameincertificate"] p.hostInCertificate, ok = params["hostnameincertificate"]
if !ok { if ok {
p.hostInCertificateProvided = true
} else {
p.hostInCertificate = p.host p.hostInCertificate = p.host
p.hostInCertificateProvided = false
} }
serverSPN, ok := params["serverspn"] serverSPN, ok := params["serverspn"]
@ -1318,42 +1322,43 @@ initiate_connection:
} }
// processing login response // processing login response
var sspi_msg []byte
continue_login:
tokchan := make(chan tokenStruct, 5)
go processResponse(context.Background(), &sess, tokchan, nil)
success := false success := false
for tok := range tokchan { for {
switch token := tok.(type) { tokchan := make(chan tokenStruct, 5)
case sspiMsg: go processResponse(context.Background(), &sess, tokchan, nil)
sspi_msg, err = auth.NextBytes(token) for tok := range tokchan {
if err != nil { switch token := tok.(type) {
return nil, err case sspiMsg:
} sspi_msg, err := auth.NextBytes(token)
case loginAckStruct: if err != nil {
success = true return nil, err
sess.loginAck = token }
case error: if sspi_msg != nil && len(sspi_msg) > 0 {
return nil, fmt.Errorf("Login error: %s", token.Error()) outbuf.BeginPacket(packSSPIMessage, false)
case doneStruct: _, err = outbuf.Write(sspi_msg)
if token.isError() { if err != nil {
return nil, fmt.Errorf("Login error: %s", token.getError()) return nil, err
}
err = outbuf.FinishPacket()
if err != nil {
return nil, err
}
sspi_msg = nil
}
case loginAckStruct:
success = true
sess.loginAck = token
case error:
return nil, fmt.Errorf("Login error: %s", token.Error())
case doneStruct:
if token.isError() {
return nil, fmt.Errorf("Login error: %s", token.getError())
}
goto loginEnd
} }
} }
} }
if sspi_msg != nil { loginEnd:
outbuf.BeginPacket(packSSPIMessage, false)
_, err = outbuf.Write(sspi_msg)
if err != nil {
return nil, err
}
err = outbuf.FinishPacket()
if err != nil {
return nil, err
}
sspi_msg = nil
goto continue_login
}
if !success { if !success {
return nil, fmt.Errorf("Login failed") return nil, fmt.Errorf("Login failed")
} }
@ -1361,6 +1366,9 @@ continue_login:
toconn.Close() toconn.Close()
p.host = sess.routedServer p.host = sess.routedServer
p.port = uint64(sess.routedPort) p.port = uint64(sess.routedPort)
if !p.hostInCertificateProvided {
p.hostInCertificate = sess.routedServer
}
goto initiate_connection goto initiate_connection
} }
return &sess, nil return &sess, nil

View file

@ -8,56 +8,70 @@ import (
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"strings"
"time" "time"
) )
var ( const (
ErrorEmptyTVPName = errors.New("TVPTypeName must not be empty") jsonTag = "json"
ErrorTVPTypeSlice = errors.New("TVPType must be slice type") tvpTag = "tvp"
ErrorTVPTypeSliceIsEmpty = errors.New("TVPType mustn't be null value") skipTagValue = "-"
sqlSeparator = "."
) )
//TVPType is driver type, which allows supporting Table Valued Parameters (TVP) in SQL Server var (
type TVPType struct { ErrorEmptyTVPTypeName = errors.New("TypeName must not be empty")
//TVP param name, mustn't be default value ErrorTypeSlice = errors.New("TVP must be slice type")
TVPTypeName string ErrorTypeSliceIsEmpty = errors.New("TVP mustn't be null value")
//TVP scheme name ErrorSkip = errors.New("all fields mustn't skip")
TVPScheme string ErrorObjectName = errors.New("wrong tvp name")
//TVP Value. Param must be the slice, mustn't be nil ErrorWrongTyping = errors.New("the number of elements in columnStr and tvpFieldIndexes do not align")
TVPValue interface{} )
//TVP is driver type, which allows supporting Table Valued Parameters (TVP) in SQL Server
type TVP struct {
//TypeName mustn't be default value
TypeName string
//Value must be the slice, mustn't be nil
Value interface{}
} }
func (tvp TVPType) check() error { func (tvp TVP) check() error {
if len(tvp.TVPTypeName) == 0 { if len(tvp.TypeName) == 0 {
return ErrorEmptyTVPName return ErrorEmptyTVPTypeName
} }
valueOf := reflect.ValueOf(tvp.TVPValue) if !isProc(tvp.TypeName) {
return ErrorEmptyTVPTypeName
}
if sepCount := getCountSQLSeparators(tvp.TypeName); sepCount > 1 {
return ErrorObjectName
}
valueOf := reflect.ValueOf(tvp.Value)
if valueOf.Kind() != reflect.Slice { if valueOf.Kind() != reflect.Slice {
return ErrorTVPTypeSlice return ErrorTypeSlice
} }
if valueOf.IsNil() { if valueOf.IsNil() {
return ErrorTVPTypeSliceIsEmpty return ErrorTypeSliceIsEmpty
} }
if reflect.TypeOf(tvp.TVPValue).Elem().Kind() != reflect.Struct { if reflect.TypeOf(tvp.Value).Elem().Kind() != reflect.Struct {
return ErrorTVPTypeSlice return ErrorTypeSlice
} }
return nil return nil
} }
func (tvp TVPType) encode() ([]byte, error) { func (tvp TVP) encode(schema, name string, columnStr []columnStruct, tvpFieldIndexes []int) ([]byte, error) {
columnStr, err := tvp.columnTypes() if len(columnStr) != len(tvpFieldIndexes) {
if err != nil { return nil, ErrorWrongTyping
return nil, err
} }
preparedBuffer := make([]byte, 0, 20+(10*len(columnStr))) preparedBuffer := make([]byte, 0, 20+(10*len(columnStr)))
buf := bytes.NewBuffer(preparedBuffer) buf := bytes.NewBuffer(preparedBuffer)
err = writeBVarChar(buf, "") err := writeBVarChar(buf, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
writeBVarChar(buf, tvp.TVPScheme)
writeBVarChar(buf, tvp.TVPTypeName)
writeBVarChar(buf, schema)
writeBVarChar(buf, name)
binary.Write(buf, binary.LittleEndian, uint16(len(columnStr))) binary.Write(buf, binary.LittleEndian, uint16(len(columnStr)))
for i, column := range columnStr { for i, column := range columnStr {
@ -66,7 +80,9 @@ func (tvp TVPType) encode() ([]byte, error) {
writeTypeInfo(buf, &columnStr[i].ti) writeTypeInfo(buf, &columnStr[i].ti)
writeBVarChar(buf, "") writeBVarChar(buf, "")
} }
// The returned error is always nil
buf.WriteByte(_TVP_END_TOKEN) buf.WriteByte(_TVP_END_TOKEN)
conn := new(Conn) conn := new(Conn)
conn.sess = new(tdsSession) conn.sess = new(tdsSession)
conn.sess.loginAck = loginAckStruct{TDSVersion: verTDS73} conn.sess.loginAck = loginAckStruct{TDSVersion: verTDS73}
@ -74,18 +90,18 @@ func (tvp TVPType) encode() ([]byte, error) {
c: conn, c: conn,
} }
val := reflect.ValueOf(tvp.TVPValue) val := reflect.ValueOf(tvp.Value)
for i := 0; i < val.Len(); i++ { for i := 0; i < val.Len(); i++ {
refStr := reflect.ValueOf(val.Index(i).Interface()) refStr := reflect.ValueOf(val.Index(i).Interface())
buf.WriteByte(_TVP_ROW_TOKEN) buf.WriteByte(_TVP_ROW_TOKEN)
for j := 0; j < refStr.NumField(); j++ { for columnStrIdx, fieldIdx := range tvpFieldIndexes {
field := refStr.Field(j) field := refStr.Field(fieldIdx)
tvpVal := field.Interface() tvpVal := field.Interface()
valOf := reflect.ValueOf(tvpVal) valOf := reflect.ValueOf(tvpVal)
elemKind := field.Kind() elemKind := field.Kind()
if elemKind == reflect.Ptr && valOf.IsNil() { if elemKind == reflect.Ptr && valOf.IsNil() {
switch tvpVal.(type) { switch tvpVal.(type) {
case *bool, *time.Time, *int8, *int16, *int32, *int64, *float32, *float64: case *bool, *time.Time, *int8, *int16, *int32, *int64, *float32, *float64, *int:
binary.Write(buf, binary.LittleEndian, uint8(0)) binary.Write(buf, binary.LittleEndian, uint8(0))
continue continue
default: default:
@ -106,34 +122,44 @@ func (tvp TVPType) encode() ([]byte, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to make tvp parameter row col: %s", err) return nil, fmt.Errorf("failed to make tvp parameter row col: %s", err)
} }
columnStr[j].ti.Writer(buf, param.ti, param.buffer) columnStr[columnStrIdx].ti.Writer(buf, param.ti, param.buffer)
} }
} }
buf.WriteByte(_TVP_END_TOKEN) buf.WriteByte(_TVP_END_TOKEN)
return buf.Bytes(), nil return buf.Bytes(), nil
} }
func (tvp TVPType) columnTypes() ([]columnStruct, error) { func (tvp TVP) columnTypes() ([]columnStruct, []int, error) {
val := reflect.ValueOf(tvp.TVPValue) val := reflect.ValueOf(tvp.Value)
var firstRow interface{} var firstRow interface{}
if val.Len() != 0 { if val.Len() != 0 {
firstRow = val.Index(0).Interface() firstRow = val.Index(0).Interface()
} else { } else {
firstRow = reflect.New(reflect.TypeOf(tvp.TVPValue).Elem()).Elem().Interface() firstRow = reflect.New(reflect.TypeOf(tvp.Value).Elem()).Elem().Interface()
} }
tvpRow := reflect.TypeOf(firstRow) tvpRow := reflect.TypeOf(firstRow)
columnCount := tvpRow.NumField() columnCount := tvpRow.NumField()
defaultValues := make([]interface{}, 0, columnCount) defaultValues := make([]interface{}, 0, columnCount)
tvpFieldIndexes := make([]int, 0, columnCount)
for i := 0; i < columnCount; i++ { for i := 0; i < columnCount; i++ {
typeField := tvpRow.Field(i).Type field := tvpRow.Field(i)
if typeField.Kind() == reflect.Ptr { tvpTagValue, isTvpTag := field.Tag.Lookup(tvpTag)
v := reflect.New(typeField.Elem()) jsonTagValue, isJsonTag := field.Tag.Lookup(jsonTag)
if IsSkipField(tvpTagValue, isTvpTag, jsonTagValue, isJsonTag) {
continue
}
tvpFieldIndexes = append(tvpFieldIndexes, i)
if field.Type.Kind() == reflect.Ptr {
v := reflect.New(field.Type.Elem())
defaultValues = append(defaultValues, v.Interface()) defaultValues = append(defaultValues, v.Interface())
continue continue
} }
defaultValues = append(defaultValues, reflect.Zero(typeField).Interface()) defaultValues = append(defaultValues, reflect.Zero(field.Type).Interface())
}
if columnCount-len(tvpFieldIndexes) == columnCount {
return nil, nil, ErrorSkip
} }
conn := new(Conn) conn := new(Conn)
@ -147,11 +173,11 @@ func (tvp TVPType) columnTypes() ([]columnStruct, error) {
for index, val := range defaultValues { for index, val := range defaultValues {
cval, err := convertInputParameter(val) cval, err := convertInputParameter(val)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to convert tvp parameter row %d col %d: %s", index, val, err) return nil, nil, fmt.Errorf("failed to convert tvp parameter row %d col %d: %s", index, val, err)
} }
param, err := stmt.makeParam(cval) param, err := stmt.makeParam(cval)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
column := columnStruct{ column := columnStruct{
ti: param.ti, ti: param.ti,
@ -163,5 +189,43 @@ func (tvp TVPType) columnTypes() ([]columnStruct, error) {
columnConfiguration = append(columnConfiguration, column) columnConfiguration = append(columnConfiguration, column)
} }
return columnConfiguration, nil return columnConfiguration, tvpFieldIndexes, nil
}
func IsSkipField(tvpTagValue string, isTvpValue bool, jsonTagValue string, isJsonTagValue bool) bool {
if !isTvpValue && !isJsonTagValue {
return false
} else if isTvpValue && tvpTagValue != skipTagValue {
return false
} else if !isTvpValue && isJsonTagValue && jsonTagValue != skipTagValue {
return false
}
return true
}
func getSchemeAndName(tvpName string) (string, string, error) {
if len(tvpName) == 0 {
return "", "", ErrorEmptyTVPTypeName
}
splitVal := strings.Split(tvpName, ".")
if len(splitVal) > 2 {
return "", "", errors.New("wrong tvp name")
}
if len(splitVal) == 2 {
res := make([]string, 2)
for key, value := range splitVal {
tmp := strings.Replace(value, "[", "", -1)
tmp = strings.Replace(tmp, "]", "", -1)
res[key] = tmp
}
return res[0], res[1], nil
}
tmp := strings.Replace(splitVal[0], "[", "", -1)
tmp = strings.Replace(tmp, "]", "", -1)
return "", tmp, nil
}
func getCountSQLSeparators(str string) int {
return strings.Count(str, sqlSeparator)
} }

View file

@ -73,14 +73,10 @@ const (
const _PLP_NULL = 0xFFFFFFFFFFFFFFFF const _PLP_NULL = 0xFFFFFFFFFFFFFFFF
const _UNKNOWN_PLP_LEN = 0xFFFFFFFFFFFFFFFE const _UNKNOWN_PLP_LEN = 0xFFFFFFFFFFFFFFFE
const _PLP_TERMINATOR = 0x00000000 const _PLP_TERMINATOR = 0x00000000
const _TVP_NULL_TOKEN = 0xffff
// TVP COLUMN FLAGS // TVP COLUMN FLAGS
const _TVP_COLUMN_DEFAULT_FLAG = 0x200
const _TVP_END_TOKEN = 0x00 const _TVP_END_TOKEN = 0x00
const _TVP_ROW_TOKEN = 0x01 const _TVP_ROW_TOKEN = 0x01
const _TVP_ORDER_UNIQUE_TOKEN = 0x10
const _TVP_COLUMN_ORDERING_TOKEN = 0x11
// TYPE_INFO rule // TYPE_INFO rule
// http://msdn.microsoft.com/en-us/library/dd358284.aspx // http://msdn.microsoft.com/en-us/library/dd358284.aspx

View file

@ -1,3 +1,14 @@
## Version 1.4.1 (2018-11-14)
Bugfixes:
- Fix TIME format for binary columns (#818)
- Fix handling of empty auth plugin names (#835)
- Fix caching_sha2_password with empty password (#826)
- Fix canceled context broke mysqlConn (#862)
- Fix OldAuthSwitchRequest support (#870)
- Fix Auth Response packet for cleartext password (#887)
## Version 1.4 (2018-06-03) ## Version 1.4 (2018-06-03)
Changes: Changes:

View file

@ -234,64 +234,64 @@ func (mc *mysqlConn) sendEncryptedPassword(seed []byte, pub *rsa.PublicKey) erro
if err != nil { if err != nil {
return err return err
} }
return mc.writeAuthSwitchPacket(enc, false) return mc.writeAuthSwitchPacket(enc)
} }
func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, bool, error) { func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
switch plugin { switch plugin {
case "caching_sha2_password": case "caching_sha2_password":
authResp := scrambleSHA256Password(authData, mc.cfg.Passwd) authResp := scrambleSHA256Password(authData, mc.cfg.Passwd)
return authResp, (authResp == nil), nil return authResp, nil
case "mysql_old_password": case "mysql_old_password":
if !mc.cfg.AllowOldPasswords { if !mc.cfg.AllowOldPasswords {
return nil, false, ErrOldPassword return nil, ErrOldPassword
} }
// Note: there are edge cases where this should work but doesn't; // Note: there are edge cases where this should work but doesn't;
// this is currently "wontfix": // this is currently "wontfix":
// https://github.com/go-sql-driver/mysql/issues/184 // https://github.com/go-sql-driver/mysql/issues/184
authResp := scrambleOldPassword(authData[:8], mc.cfg.Passwd) authResp := append(scrambleOldPassword(authData[:8], mc.cfg.Passwd), 0)
return authResp, true, nil return authResp, nil
case "mysql_clear_password": case "mysql_clear_password":
if !mc.cfg.AllowCleartextPasswords { if !mc.cfg.AllowCleartextPasswords {
return nil, false, ErrCleartextPassword return nil, ErrCleartextPassword
} }
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html // http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html // http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
return []byte(mc.cfg.Passwd), true, nil return append([]byte(mc.cfg.Passwd), 0), nil
case "mysql_native_password": case "mysql_native_password":
if !mc.cfg.AllowNativePasswords { if !mc.cfg.AllowNativePasswords {
return nil, false, ErrNativePassword return nil, ErrNativePassword
} }
// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html // https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
// Native password authentication only need and will need 20-byte challenge. // Native password authentication only need and will need 20-byte challenge.
authResp := scramblePassword(authData[:20], mc.cfg.Passwd) authResp := scramblePassword(authData[:20], mc.cfg.Passwd)
return authResp, false, nil return authResp, nil
case "sha256_password": case "sha256_password":
if len(mc.cfg.Passwd) == 0 { if len(mc.cfg.Passwd) == 0 {
return nil, true, nil return []byte{0}, nil
} }
if mc.cfg.tls != nil || mc.cfg.Net == "unix" { if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
// write cleartext auth packet // write cleartext auth packet
return []byte(mc.cfg.Passwd), true, nil return append([]byte(mc.cfg.Passwd), 0), nil
} }
pubKey := mc.cfg.pubKey pubKey := mc.cfg.pubKey
if pubKey == nil { if pubKey == nil {
// request public key from server // request public key from server
return []byte{1}, false, nil return []byte{1}, nil
} }
// encrypted password // encrypted password
enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey) enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey)
return enc, false, err return enc, err
default: default:
errLog.Print("unknown auth plugin:", plugin) errLog.Print("unknown auth plugin:", plugin)
return nil, false, ErrUnknownPlugin return nil, ErrUnknownPlugin
} }
} }
@ -315,11 +315,11 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
plugin = newPlugin plugin = newPlugin
authResp, addNUL, err := mc.auth(authData, plugin) authResp, err := mc.auth(authData, plugin)
if err != nil { if err != nil {
return err return err
} }
if err = mc.writeAuthSwitchPacket(authResp, addNUL); err != nil { if err = mc.writeAuthSwitchPacket(authResp); err != nil {
return err return err
} }
@ -352,7 +352,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
case cachingSha2PasswordPerformFullAuthentication: case cachingSha2PasswordPerformFullAuthentication:
if mc.cfg.tls != nil || mc.cfg.Net == "unix" { if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
// write cleartext auth packet // write cleartext auth packet
err = mc.writeAuthSwitchPacket([]byte(mc.cfg.Passwd), true) err = mc.writeAuthSwitchPacket(append([]byte(mc.cfg.Passwd), 0))
if err != nil { if err != nil {
return err return err
} }

View file

@ -149,22 +149,21 @@ func (mc *mysqlConn) watchCancel(ctx context.Context) error {
mc.cleanup() mc.cleanup()
return nil return nil
} }
// When ctx is already cancelled, don't watch it.
if err := ctx.Err(); err != nil {
return err
}
// When ctx is not cancellable, don't watch it.
if ctx.Done() == nil { if ctx.Done() == nil {
return nil return nil
} }
// When watcher is not alive, can't watch it.
mc.watching = true
select {
default:
case <-ctx.Done():
return ctx.Err()
}
if mc.watcher == nil { if mc.watcher == nil {
return nil return nil
} }
mc.watching = true
mc.watcher <- ctx mc.watcher <- ctx
return nil return nil
} }

View file

@ -112,20 +112,23 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
mc.cleanup() mc.cleanup()
return nil, err return nil, err
} }
if plugin == "" {
plugin = defaultAuthPlugin
}
// Send Client Authentication Packet // Send Client Authentication Packet
authResp, addNUL, err := mc.auth(authData, plugin) authResp, err := mc.auth(authData, plugin)
if err != nil { if err != nil {
// try the default auth plugin, if using the requested plugin failed // try the default auth plugin, if using the requested plugin failed
errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error()) errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error())
plugin = defaultAuthPlugin plugin = defaultAuthPlugin
authResp, addNUL, err = mc.auth(authData, plugin) authResp, err = mc.auth(authData, plugin)
if err != nil { if err != nil {
mc.cleanup() mc.cleanup()
return nil, err return nil, err
} }
} }
if err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin); err != nil { if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
mc.cleanup() mc.cleanup()
return nil, err return nil, err
} }

View file

@ -154,15 +154,15 @@ func (mc *mysqlConn) writePacket(data []byte) error {
// Handshake Initialization Packet // Handshake Initialization Packet
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake
func (mc *mysqlConn) readHandshakePacket() ([]byte, string, error) { func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err error) {
data, err := mc.readPacket() data, err = mc.readPacket()
if err != nil { if err != nil {
// for init we can rewrite this to ErrBadConn for sql.Driver to retry, since // for init we can rewrite this to ErrBadConn for sql.Driver to retry, since
// in connection initialization we don't risk retrying non-idempotent actions. // in connection initialization we don't risk retrying non-idempotent actions.
if err == ErrInvalidConn { if err == ErrInvalidConn {
return nil, "", driver.ErrBadConn return nil, "", driver.ErrBadConn
} }
return nil, "", err return
} }
if data[0] == iERR { if data[0] == iERR {
@ -198,7 +198,6 @@ func (mc *mysqlConn) readHandshakePacket() ([]byte, string, error) {
} }
pos += 2 pos += 2
plugin := ""
if len(data) > pos { if len(data) > pos {
// character set [1 byte] // character set [1 byte]
// status flags [2 bytes] // status flags [2 bytes]
@ -236,8 +235,6 @@ func (mc *mysqlConn) readHandshakePacket() ([]byte, string, error) {
return b[:], plugin, nil return b[:], plugin, nil
} }
plugin = defaultAuthPlugin
// make a memory safe copy of the cipher slice // make a memory safe copy of the cipher slice
var b [8]byte var b [8]byte
copy(b[:], authData) copy(b[:], authData)
@ -246,7 +243,7 @@ func (mc *mysqlConn) readHandshakePacket() ([]byte, string, error) {
// Client Authentication Packet // Client Authentication Packet
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool, plugin string) error { func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string) error {
// Adjust client flags based on server support // Adjust client flags based on server support
clientFlags := clientProtocol41 | clientFlags := clientProtocol41 |
clientSecureConn | clientSecureConn |
@ -272,7 +269,8 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool,
// encode length of the auth plugin data // encode length of the auth plugin data
var authRespLEIBuf [9]byte var authRespLEIBuf [9]byte
authRespLEI := appendLengthEncodedInteger(authRespLEIBuf[:0], uint64(len(authResp))) authRespLen := len(authResp)
authRespLEI := appendLengthEncodedInteger(authRespLEIBuf[:0], uint64(authRespLen))
if len(authRespLEI) > 1 { if len(authRespLEI) > 1 {
// if the length can not be written in 1 byte, it must be written as a // if the length can not be written in 1 byte, it must be written as a
// length encoded integer // length encoded integer
@ -280,9 +278,6 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool,
} }
pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + len(authRespLEI) + len(authResp) + 21 + 1 pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + len(authRespLEI) + len(authResp) + 21 + 1
if addNUL {
pktLen++
}
// To specify a db name // To specify a db name
if n := len(mc.cfg.DBName); n > 0 { if n := len(mc.cfg.DBName); n > 0 {
@ -353,10 +348,6 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool,
// Auth Data [length encoded integer] // Auth Data [length encoded integer]
pos += copy(data[pos:], authRespLEI) pos += copy(data[pos:], authRespLEI)
pos += copy(data[pos:], authResp) pos += copy(data[pos:], authResp)
if addNUL {
data[pos] = 0x00
pos++
}
// Databasename [null terminated string] // Databasename [null terminated string]
if len(mc.cfg.DBName) > 0 { if len(mc.cfg.DBName) > 0 {
@ -367,17 +358,15 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool,
pos += copy(data[pos:], plugin) pos += copy(data[pos:], plugin)
data[pos] = 0x00 data[pos] = 0x00
pos++
// Send Auth packet // Send Auth packet
return mc.writePacket(data) return mc.writePacket(data[:pos])
} }
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte, addNUL bool) error { func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error {
pktLen := 4 + len(authData) pktLen := 4 + len(authData)
if addNUL {
pktLen++
}
data := mc.buf.takeSmallBuffer(pktLen) data := mc.buf.takeSmallBuffer(pktLen)
if data == nil { if data == nil {
// cannot take the buffer. Something must be wrong with the connection // cannot take the buffer. Something must be wrong with the connection
@ -387,10 +376,6 @@ func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte, addNUL bool) error {
// Add the auth data [EOF] // Add the auth data [EOF]
copy(data[4:], authData) copy(data[4:], authData)
if addNUL {
data[pktLen-1] = 0x00
}
return mc.writePacket(data) return mc.writePacket(data)
} }
@ -482,7 +467,7 @@ func (mc *mysqlConn) readAuthResult() ([]byte, string, error) {
return data[1:], "", err return data[1:], "", err
case iEOF: case iEOF:
if len(data) < 1 { if len(data) == 1 {
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest // https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
return nil, "mysql_old_password", nil return nil, "mysql_old_password", nil
} }
@ -1261,7 +1246,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
rows.rs.columns[i].decimals, rows.rs.columns[i].decimals,
) )
} }
dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, true) dest[i], err = formatBinaryTime(data[pos:pos+int(num)], dstlen)
case rows.mc.parseTime: case rows.mc.parseTime:
dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc) dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
default: default:
@ -1281,7 +1266,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
) )
} }
} }
dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, false) dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen)
} }
if err == nil { if err == nil {

View file

@ -14,6 +14,7 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -227,87 +228,104 @@ var zeroDateTime = []byte("0000-00-00 00:00:00.000000")
const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999" const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value, error) { func appendMicrosecs(dst, src []byte, decimals int) []byte {
if decimals <= 0 {
return dst
}
if len(src) == 0 {
return append(dst, ".000000"[:decimals+1]...)
}
microsecs := binary.LittleEndian.Uint32(src[:4])
p1 := byte(microsecs / 10000)
microsecs -= 10000 * uint32(p1)
p2 := byte(microsecs / 100)
microsecs -= 100 * uint32(p2)
p3 := byte(microsecs)
switch decimals {
default:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2], digits01[p2],
digits10[p3], digits01[p3],
)
case 1:
return append(dst, '.',
digits10[p1],
)
case 2:
return append(dst, '.',
digits10[p1], digits01[p1],
)
case 3:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2],
)
case 4:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2], digits01[p2],
)
case 5:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2], digits01[p2],
digits10[p3],
)
}
}
func formatBinaryDateTime(src []byte, length uint8) (driver.Value, error) {
// length expects the deterministic length of the zero value, // length expects the deterministic length of the zero value,
// negative time and 100+ hours are automatically added if needed // negative time and 100+ hours are automatically added if needed
if len(src) == 0 { if len(src) == 0 {
if justTime {
return zeroDateTime[11 : 11+length], nil
}
return zeroDateTime[:length], nil return zeroDateTime[:length], nil
} }
var dst []byte // return value var dst []byte // return value
var pt, p1, p2, p3 byte // current digit pair var p1, p2, p3 byte // current digit pair
var zOffs byte // offset of value in zeroDateTime
if justTime { switch length {
switch length { case 10, 19, 21, 22, 23, 24, 25, 26:
case default:
8, // time (can be up to 10 when negative and 100+ hours) t := "DATE"
10, 11, 12, 13, 14, 15: // time with fractional seconds if length > 10 {
default: t += "TIME"
return nil, fmt.Errorf("illegal TIME length %d", length)
} }
switch len(src) { return nil, fmt.Errorf("illegal %s length %d", t, length)
case 8, 12:
default:
return nil, fmt.Errorf("invalid TIME packet length %d", len(src))
}
// +2 to enable negative time and 100+ hours
dst = make([]byte, 0, length+2)
if src[0] == 1 {
dst = append(dst, '-')
}
if src[1] != 0 {
hour := uint16(src[1])*24 + uint16(src[5])
pt = byte(hour / 100)
p1 = byte(hour - 100*uint16(pt))
dst = append(dst, digits01[pt])
} else {
p1 = src[5]
}
zOffs = 11
src = src[6:]
} else {
switch length {
case 10, 19, 21, 22, 23, 24, 25, 26:
default:
t := "DATE"
if length > 10 {
t += "TIME"
}
return nil, fmt.Errorf("illegal %s length %d", t, length)
}
switch len(src) {
case 4, 7, 11:
default:
t := "DATE"
if length > 10 {
t += "TIME"
}
return nil, fmt.Errorf("illegal %s packet length %d", t, len(src))
}
dst = make([]byte, 0, length)
// start with the date
year := binary.LittleEndian.Uint16(src[:2])
pt = byte(year / 100)
p1 = byte(year - 100*uint16(pt))
p2, p3 = src[2], src[3]
dst = append(dst,
digits10[pt], digits01[pt],
digits10[p1], digits01[p1], '-',
digits10[p2], digits01[p2], '-',
digits10[p3], digits01[p3],
)
if length == 10 {
return dst, nil
}
if len(src) == 4 {
return append(dst, zeroDateTime[10:length]...), nil
}
dst = append(dst, ' ')
p1 = src[4] // hour
src = src[5:]
} }
switch len(src) {
case 4, 7, 11:
default:
t := "DATE"
if length > 10 {
t += "TIME"
}
return nil, fmt.Errorf("illegal %s packet length %d", t, len(src))
}
dst = make([]byte, 0, length)
// start with the date
year := binary.LittleEndian.Uint16(src[:2])
pt := year / 100
p1 = byte(year - 100*uint16(pt))
p2, p3 = src[2], src[3]
dst = append(dst,
digits10[pt], digits01[pt],
digits10[p1], digits01[p1], '-',
digits10[p2], digits01[p2], '-',
digits10[p3], digits01[p3],
)
if length == 10 {
return dst, nil
}
if len(src) == 4 {
return append(dst, zeroDateTime[10:length]...), nil
}
dst = append(dst, ' ')
p1 = src[4] // hour
src = src[5:]
// p1 is 2-digit hour, src is after hour // p1 is 2-digit hour, src is after hour
p2, p3 = src[0], src[1] p2, p3 = src[0], src[1]
dst = append(dst, dst = append(dst,
@ -315,51 +333,49 @@ func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value
digits10[p2], digits01[p2], ':', digits10[p2], digits01[p2], ':',
digits10[p3], digits01[p3], digits10[p3], digits01[p3],
) )
if length <= byte(len(dst)) { return appendMicrosecs(dst, src[2:], int(length)-20), nil
return dst, nil }
}
src = src[2:] func formatBinaryTime(src []byte, length uint8) (driver.Value, error) {
// length expects the deterministic length of the zero value,
// negative time and 100+ hours are automatically added if needed
if len(src) == 0 { if len(src) == 0 {
return append(dst, zeroDateTime[19:zOffs+length]...), nil return zeroDateTime[11 : 11+length], nil
} }
microsecs := binary.LittleEndian.Uint32(src[:4]) var dst []byte // return value
p1 = byte(microsecs / 10000)
microsecs -= 10000 * uint32(p1) switch length {
p2 = byte(microsecs / 100) case
microsecs -= 100 * uint32(p2) 8, // time (can be up to 10 when negative and 100+ hours)
p3 = byte(microsecs) 10, 11, 12, 13, 14, 15: // time with fractional seconds
switch decimals := zOffs + length - 20; decimals {
default: default:
return append(dst, '.', return nil, fmt.Errorf("illegal TIME length %d", length)
digits10[p1], digits01[p1],
digits10[p2], digits01[p2],
digits10[p3], digits01[p3],
), nil
case 1:
return append(dst, '.',
digits10[p1],
), nil
case 2:
return append(dst, '.',
digits10[p1], digits01[p1],
), nil
case 3:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2],
), nil
case 4:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2], digits01[p2],
), nil
case 5:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2], digits01[p2],
digits10[p3],
), nil
} }
switch len(src) {
case 8, 12:
default:
return nil, fmt.Errorf("invalid TIME packet length %d", len(src))
}
// +2 to enable negative time and 100+ hours
dst = make([]byte, 0, length+2)
if src[0] == 1 {
dst = append(dst, '-')
}
days := binary.LittleEndian.Uint32(src[1:5])
hours := int64(days)*24 + int64(src[5])
if hours >= 100 {
dst = strconv.AppendInt(dst, hours, 10)
} else {
dst = append(dst, digits10[hours], digits01[hours])
}
min, sec := src[6], src[7]
dst = append(dst, ':',
digits10[min], digits01[min], ':',
digits10[sec], digits01[sec],
)
return appendMicrosecs(dst, src[8:], int(length)-9), nil
} }
/****************************************************************************** /******************************************************************************

13
vendor/github.com/lib/pq/.travis.sh generated vendored
View file

@ -70,17 +70,4 @@ postgresql_uninstall() {
sudo rm -rf /var/lib/postgresql sudo rm -rf /var/lib/postgresql
} }
megacheck_install() {
# Lock megacheck version at $MEGACHECK_VERSION to prevent spontaneous
# new error messages in old code.
go get -d honnef.co/go/tools/...
git -C $GOPATH/src/honnef.co/go/tools/ checkout $MEGACHECK_VERSION
go install honnef.co/go/tools/cmd/megacheck
megacheck --version
}
golint_install() {
go get github.com/golang/lint/golint
}
$1 $1

16
vendor/github.com/lib/pq/.travis.yml generated vendored
View file

@ -1,9 +1,8 @@
language: go language: go
go: go:
- 1.8.x - 1.11.x
- 1.9.x - 1.12.x
- 1.10.x
- master - master
sudo: true sudo: true
@ -14,16 +13,11 @@ env:
- PQGOSSLTESTS=1 - PQGOSSLTESTS=1
- PQSSLCERTTEST_PATH=$PWD/certs - PQSSLCERTTEST_PATH=$PWD/certs
- PGHOST=127.0.0.1 - PGHOST=127.0.0.1
- MEGACHECK_VERSION=2017.2.2
matrix: matrix:
- PGVERSION=10 - PGVERSION=10
- PGVERSION=9.6 - PGVERSION=9.6
- PGVERSION=9.5 - PGVERSION=9.5
- PGVERSION=9.4 - PGVERSION=9.4
- PGVERSION=9.3
- PGVERSION=9.2
- PGVERSION=9.1
- PGVERSION=9.0
before_install: before_install:
- ./.travis.sh postgresql_uninstall - ./.travis.sh postgresql_uninstall
@ -31,9 +25,9 @@ before_install:
- ./.travis.sh postgresql_install - ./.travis.sh postgresql_install
- ./.travis.sh postgresql_configure - ./.travis.sh postgresql_configure
- ./.travis.sh client_configure - ./.travis.sh client_configure
- ./.travis.sh megacheck_install
- ./.travis.sh golint_install
- go get golang.org/x/tools/cmd/goimports - go get golang.org/x/tools/cmd/goimports
- go get golang.org/x/lint/golint
- GO111MODULE=on go get honnef.co/go/tools/cmd/staticcheck@2019.2.1
before_script: before_script:
- createdb pqgotest - createdb pqgotest
@ -44,7 +38,7 @@ script:
- > - >
goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }' goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
- go vet ./... - go vet ./...
- megacheck -go 1.8 ./... - staticcheck -go 1.11 ./...
- golint ./... - golint ./...
- PQTEST_BINARY_PARAMETERS=no go test -race -v ./... - PQTEST_BINARY_PARAMETERS=no go test -race -v ./...
- PQTEST_BINARY_PARAMETERS=yes go test -race -v ./... - PQTEST_BINARY_PARAMETERS=yes go test -race -v ./...

2
vendor/github.com/lib/pq/README.md generated vendored
View file

@ -10,7 +10,7 @@
## Docs ## Docs
For detailed documentation and basic usage examples, please see the package For detailed documentation and basic usage examples, please see the package
documentation at <http://godoc.org/github.com/lib/pq>. documentation at <https://godoc.org/github.com/lib/pq>.
## Tests ## Tests

2
vendor/github.com/lib/pq/buf.go generated vendored
View file

@ -66,7 +66,7 @@ func (b *writeBuf) int16(n int) {
} }
func (b *writeBuf) string(s string) { func (b *writeBuf) string(s string) {
b.buf = append(b.buf, (s + "\000")...) b.buf = append(append(b.buf, s...), '\000')
} }
func (b *writeBuf) byte(c byte) { func (b *writeBuf) byte(c byte) {

301
vendor/github.com/lib/pq/conn.go generated vendored
View file

@ -2,7 +2,9 @@ package pq
import ( import (
"bufio" "bufio"
"context"
"crypto/md5" "crypto/md5"
"crypto/sha256"
"database/sql" "database/sql"
"database/sql/driver" "database/sql/driver"
"encoding/binary" "encoding/binary"
@ -20,6 +22,7 @@ import (
"unicode" "unicode"
"github.com/lib/pq/oid" "github.com/lib/pq/oid"
"github.com/lib/pq/scram"
) )
// Common error types // Common error types
@ -89,13 +92,25 @@ type Dialer interface {
DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error)
} }
type defaultDialer struct{} // DialerContext is the context-aware dialer interface.
type DialerContext interface {
func (d defaultDialer) Dial(ntw, addr string) (net.Conn, error) { DialContext(ctx context.Context, network, address string) (net.Conn, error)
return net.Dial(ntw, addr)
} }
func (d defaultDialer) DialTimeout(ntw, addr string, timeout time.Duration) (net.Conn, error) {
return net.DialTimeout(ntw, addr, timeout) type defaultDialer struct {
d net.Dialer
}
func (d defaultDialer) Dial(network, address string) (net.Conn, error) {
return d.d.Dial(network, address)
}
func (d defaultDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return d.DialContext(ctx, network, address)
}
func (d defaultDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
return d.d.DialContext(ctx, network, address)
} }
type conn struct { type conn struct {
@ -244,90 +259,35 @@ func (cn *conn) writeBuf(b byte) *writeBuf {
} }
} }
// Open opens a new connection to the database. name is a connection string. // Open opens a new connection to the database. dsn is a connection string.
// Most users should only use it through database/sql package from the standard // Most users should only use it through database/sql package from the standard
// library. // library.
func Open(name string) (_ driver.Conn, err error) { func Open(dsn string) (_ driver.Conn, err error) {
return DialOpen(defaultDialer{}, name) return DialOpen(defaultDialer{}, dsn)
} }
// DialOpen opens a new connection to the database using a dialer. // DialOpen opens a new connection to the database using a dialer.
func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { func DialOpen(d Dialer, dsn string) (_ driver.Conn, err error) {
c, err := NewConnector(dsn)
if err != nil {
return nil, err
}
c.dialer = d
return c.open(context.Background())
}
func (c *Connector) open(ctx context.Context) (cn *conn, err error) {
// Handle any panics during connection initialization. Note that we // Handle any panics during connection initialization. Note that we
// specifically do *not* want to use errRecover(), as that would turn any // specifically do *not* want to use errRecover(), as that would turn any
// connection errors into ErrBadConns, hiding the real error message from // connection errors into ErrBadConns, hiding the real error message from
// the user. // the user.
defer errRecoverNoErrBadConn(&err) defer errRecoverNoErrBadConn(&err)
o := make(values) o := c.opts
// A number of defaults are applied here, in this order: cn = &conn{
//
// * Very low precedence defaults applied in every situation
// * Environment variables
// * Explicitly passed connection information
o["host"] = "localhost"
o["port"] = "5432"
// N.B.: Extra float digits should be set to 3, but that breaks
// Postgres 8.4 and older, where the max is 2.
o["extra_float_digits"] = "2"
for k, v := range parseEnviron(os.Environ()) {
o[k] = v
}
if strings.HasPrefix(name, "postgres://") || strings.HasPrefix(name, "postgresql://") {
name, err = ParseURL(name)
if err != nil {
return nil, err
}
}
if err := parseOpts(name, o); err != nil {
return nil, err
}
// Use the "fallback" application name if necessary
if fallback, ok := o["fallback_application_name"]; ok {
if _, ok := o["application_name"]; !ok {
o["application_name"] = fallback
}
}
// We can't work with any client_encoding other than UTF-8 currently.
// However, we have historically allowed the user to set it to UTF-8
// explicitly, and there's no reason to break such programs, so allow that.
// Note that the "options" setting could also set client_encoding, but
// parsing its value is not worth it. Instead, we always explicitly send
// client_encoding as a separate run-time parameter, which should override
// anything set in options.
if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) {
return nil, errors.New("client_encoding must be absent or 'UTF8'")
}
o["client_encoding"] = "UTF8"
// DateStyle needs a similar treatment.
if datestyle, ok := o["datestyle"]; ok {
if datestyle != "ISO, MDY" {
panic(fmt.Sprintf("setting datestyle must be absent or %v; got %v",
"ISO, MDY", datestyle))
}
} else {
o["datestyle"] = "ISO, MDY"
}
// If a user is not provided by any other means, the last
// resort is to use the current operating system provided user
// name.
if _, ok := o["user"]; !ok {
u, err := userCurrent()
if err != nil {
return nil, err
}
o["user"] = u
}
cn := &conn{
opts: o, opts: o,
dialer: d, dialer: c.dialer,
} }
err = cn.handleDriverSettings(o) err = cn.handleDriverSettings(o)
if err != nil { if err != nil {
@ -335,13 +295,16 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
} }
cn.handlePgpass(o) cn.handlePgpass(o)
cn.c, err = dial(d, o) cn.c, err = dial(ctx, c.dialer, o)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = cn.ssl(o) err = cn.ssl(o)
if err != nil { if err != nil {
if cn.c != nil {
cn.c.Close()
}
return nil, err return nil, err
} }
@ -364,10 +327,10 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
return cn, err return cn, err
} }
func dial(d Dialer, o values) (net.Conn, error) { func dial(ctx context.Context, d Dialer, o values) (net.Conn, error) {
ntw, addr := network(o) network, address := network(o)
// SSL is not necessary or supported over UNIX domain sockets // SSL is not necessary or supported over UNIX domain sockets
if ntw == "unix" { if network == "unix" {
o["sslmode"] = "disable" o["sslmode"] = "disable"
} }
@ -378,19 +341,30 @@ func dial(d Dialer, o values) (net.Conn, error) {
return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err) return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err)
} }
duration := time.Duration(seconds) * time.Second duration := time.Duration(seconds) * time.Second
// connect_timeout should apply to the entire connection establishment // connect_timeout should apply to the entire connection establishment
// procedure, so we both use a timeout for the TCP connection // procedure, so we both use a timeout for the TCP connection
// establishment and set a deadline for doing the initial handshake. // establishment and set a deadline for doing the initial handshake.
// The deadline is then reset after startup() is done. // The deadline is then reset after startup() is done.
deadline := time.Now().Add(duration) deadline := time.Now().Add(duration)
conn, err := d.DialTimeout(ntw, addr, duration) var conn net.Conn
if dctx, ok := d.(DialerContext); ok {
ctx, cancel := context.WithTimeout(ctx, duration)
defer cancel()
conn, err = dctx.DialContext(ctx, network, address)
} else {
conn, err = d.DialTimeout(network, address, duration)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = conn.SetDeadline(deadline) err = conn.SetDeadline(deadline)
return conn, err return conn, err
} }
return d.Dial(ntw, addr) if dctx, ok := d.(DialerContext); ok {
return dctx.DialContext(ctx, network, address)
}
return d.Dial(network, address)
} }
func network(o values) (string, string) { func network(o values) (string, string) {
@ -576,7 +550,7 @@ func (cn *conn) Commit() (err error) {
// would get the same behaviour if you issued a COMMIT in a failed // would get the same behaviour if you issued a COMMIT in a failed
// transaction, so it's also the least surprising thing to do here. // transaction, so it's also the least surprising thing to do here.
if cn.txnStatus == txnStatusInFailedTransaction { if cn.txnStatus == txnStatusInFailedTransaction {
if err := cn.Rollback(); err != nil { if err := cn.rollback(); err != nil {
return err return err
} }
return ErrInFailedTransaction return ErrInFailedTransaction
@ -603,7 +577,10 @@ func (cn *conn) Rollback() (err error) {
return driver.ErrBadConn return driver.ErrBadConn
} }
defer cn.errRecover(&err) defer cn.errRecover(&err)
return cn.rollback()
}
func (cn *conn) rollback() (err error) {
cn.checkIsInTransaction(true) cn.checkIsInTransaction(true)
_, commandTag, err := cn.simpleExec("ROLLBACK") _, commandTag, err := cn.simpleExec("ROLLBACK")
if err != nil { if err != nil {
@ -704,7 +681,7 @@ func (cn *conn) simpleQuery(q string) (res *rows, err error) {
// res might be non-nil here if we received a previous // res might be non-nil here if we received a previous
// CommandComplete, but that's fine; just overwrite it // CommandComplete, but that's fine; just overwrite it
res = &rows{cn: cn} res = &rows{cn: cn}
res.colNames, res.colFmts, res.colTyps = parsePortalRowDescribe(r) res.rowsHeader = parsePortalRowDescribe(r)
// To work around a bug in QueryRow in Go 1.2 and earlier, wait // To work around a bug in QueryRow in Go 1.2 and earlier, wait
// until the first DataRow has been received. // until the first DataRow has been received.
@ -861,17 +838,15 @@ func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
cn.readParseResponse() cn.readParseResponse()
cn.readBindResponse() cn.readBindResponse()
rows := &rows{cn: cn} rows := &rows{cn: cn}
rows.colNames, rows.colFmts, rows.colTyps = cn.readPortalDescribeResponse() rows.rowsHeader = cn.readPortalDescribeResponse()
cn.postExecuteWorkaround() cn.postExecuteWorkaround()
return rows, nil return rows, nil
} }
st := cn.prepareTo(query, "") st := cn.prepareTo(query, "")
st.exec(args) st.exec(args)
return &rows{ return &rows{
cn: cn, cn: cn,
colNames: st.colNames, rowsHeader: st.rowsHeader,
colTyps: st.colTyps,
colFmts: st.colFmts,
}, nil }, nil
} }
@ -992,7 +967,6 @@ func (cn *conn) recv() (t byte, r *readBuf) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
switch t { switch t {
case 'E': case 'E':
panic(parseError(r)) panic(parseError(r))
@ -1163,6 +1137,55 @@ func (cn *conn) auth(r *readBuf, o values) {
if r.int32() != 0 { if r.int32() != 0 {
errorf("unexpected authentication response: %q", t) errorf("unexpected authentication response: %q", t)
} }
case 10:
sc := scram.NewClient(sha256.New, o["user"], o["password"])
sc.Step(nil)
if sc.Err() != nil {
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
}
scOut := sc.Out()
w := cn.writeBuf('p')
w.string("SCRAM-SHA-256")
w.int32(len(scOut))
w.bytes(scOut)
cn.send(w)
t, r := cn.recv()
if t != 'R' {
errorf("unexpected password response: %q", t)
}
if r.int32() != 11 {
errorf("unexpected authentication response: %q", t)
}
nextStep := r.next(len(*r))
sc.Step(nextStep)
if sc.Err() != nil {
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
}
scOut = sc.Out()
w = cn.writeBuf('p')
w.bytes(scOut)
cn.send(w)
t, r = cn.recv()
if t != 'R' {
errorf("unexpected password response: %q", t)
}
if r.int32() != 12 {
errorf("unexpected authentication response: %q", t)
}
nextStep = r.next(len(*r))
sc.Step(nextStep)
if sc.Err() != nil {
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
}
default: default:
errorf("unknown authentication response: %d", code) errorf("unknown authentication response: %d", code)
} }
@ -1180,12 +1203,10 @@ var colFmtDataAllBinary = []byte{0, 1, 0, 1}
var colFmtDataAllText = []byte{0, 0} var colFmtDataAllText = []byte{0, 0}
type stmt struct { type stmt struct {
cn *conn cn *conn
name string name string
colNames []string rowsHeader
colFmts []format
colFmtData []byte colFmtData []byte
colTyps []fieldDesc
paramTyps []oid.Oid paramTyps []oid.Oid
closed bool closed bool
} }
@ -1231,10 +1252,8 @@ func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) {
st.exec(v) st.exec(v)
return &rows{ return &rows{
cn: st.cn, cn: st.cn,
colNames: st.colNames, rowsHeader: st.rowsHeader,
colTyps: st.colTyps,
colFmts: st.colFmts,
}, nil }, nil
} }
@ -1344,16 +1363,22 @@ func (cn *conn) parseComplete(commandTag string) (driver.Result, string) {
return driver.RowsAffected(n), commandTag return driver.RowsAffected(n), commandTag
} }
type rows struct { type rowsHeader struct {
cn *conn
finish func()
colNames []string colNames []string
colTyps []fieldDesc colTyps []fieldDesc
colFmts []format colFmts []format
done bool }
rb readBuf
result driver.Result type rows struct {
tag string cn *conn
finish func()
rowsHeader
done bool
rb readBuf
result driver.Result
tag string
next *rowsHeader
} }
func (rs *rows) Close() error { func (rs *rows) Close() error {
@ -1440,7 +1465,8 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
} }
return return
case 'T': case 'T':
rs.colNames, rs.colFmts, rs.colTyps = parsePortalRowDescribe(&rs.rb) next := parsePortalRowDescribe(&rs.rb)
rs.next = &next
return io.EOF return io.EOF
default: default:
errorf("unexpected message after execute: %q", t) errorf("unexpected message after execute: %q", t)
@ -1449,10 +1475,16 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
} }
func (rs *rows) HasNextResultSet() bool { func (rs *rows) HasNextResultSet() bool {
return !rs.done hasNext := rs.next != nil && !rs.done
return hasNext
} }
func (rs *rows) NextResultSet() error { func (rs *rows) NextResultSet() error {
if rs.next == nil {
return io.EOF
}
rs.rowsHeader = *rs.next
rs.next = nil
return nil return nil
} }
@ -1475,6 +1507,39 @@ func QuoteIdentifier(name string) string {
return `"` + strings.Replace(name, `"`, `""`, -1) + `"` return `"` + strings.Replace(name, `"`, `""`, -1) + `"`
} }
// QuoteLiteral quotes a 'literal' (e.g. a parameter, often used to pass literal
// to DDL and other statements that do not accept parameters) to be used as part
// of an SQL statement. For example:
//
// exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z")
// err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date))
//
// Any single quotes in name will be escaped. Any backslashes (i.e. "\") will be
// replaced by two backslashes (i.e. "\\") and the C-style escape identifier
// that PostgreSQL provides ('E') will be prepended to the string.
func QuoteLiteral(literal string) string {
// This follows the PostgreSQL internal algorithm for handling quoted literals
// from libpq, which can be found in the "PQEscapeStringInternal" function,
// which is found in the libpq/fe-exec.c source file:
// https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/interfaces/libpq/fe-exec.c
//
// substitute any single-quotes (') with two single-quotes ('')
literal = strings.Replace(literal, `'`, `''`, -1)
// determine if the string has any backslashes (\) in it.
// if it does, replace any backslashes (\) with two backslashes (\\)
// then, we need to wrap the entire string with a PostgreSQL
// C-style escape. Per how "PQEscapeStringInternal" handles this case, we
// also add a space before the "E"
if strings.Contains(literal, `\`) {
literal = strings.Replace(literal, `\`, `\\`, -1)
literal = ` E'` + literal + `'`
} else {
// otherwise, we can just wrap the literal with a pair of single quotes
literal = `'` + literal + `'`
}
return literal
}
func md5s(s string) string { func md5s(s string) string {
h := md5.New() h := md5.New()
h.Write([]byte(s)) h.Write([]byte(s))
@ -1630,13 +1695,13 @@ func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames [
} }
} }
func (cn *conn) readPortalDescribeResponse() (colNames []string, colFmts []format, colTyps []fieldDesc) { func (cn *conn) readPortalDescribeResponse() rowsHeader {
t, r := cn.recv1() t, r := cn.recv1()
switch t { switch t {
case 'T': case 'T':
return parsePortalRowDescribe(r) return parsePortalRowDescribe(r)
case 'n': case 'n':
return nil, nil, nil return rowsHeader{}
case 'E': case 'E':
err := parseError(r) err := parseError(r)
cn.readReadyForQuery() cn.readReadyForQuery()
@ -1742,11 +1807,11 @@ func parseStatementRowDescribe(r *readBuf) (colNames []string, colTyps []fieldDe
return return
} }
func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, colTyps []fieldDesc) { func parsePortalRowDescribe(r *readBuf) rowsHeader {
n := r.int16() n := r.int16()
colNames = make([]string, n) colNames := make([]string, n)
colFmts = make([]format, n) colFmts := make([]format, n)
colTyps = make([]fieldDesc, n) colTyps := make([]fieldDesc, n)
for i := range colNames { for i := range colNames {
colNames[i] = r.string() colNames[i] = r.string()
r.next(6) r.next(6)
@ -1755,7 +1820,11 @@ func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, co
colTyps[i].Mod = r.int32() colTyps[i].Mod = r.int32()
colFmts[i] = format(r.int16()) colFmts[i] = format(r.int16())
} }
return return rowsHeader{
colNames: colNames,
colFmts: colFmts,
colTyps: colTyps,
}
} }
// parseEnviron tries to mimic some of libpq's environment handling // parseEnviron tries to mimic some of libpq's environment handling

View file

@ -1,5 +1,3 @@
// +build go1.8
package pq package pq
import ( import (
@ -9,6 +7,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"time"
) )
// Implement the "QueryerContext" interface // Implement the "QueryerContext" interface
@ -76,13 +75,32 @@ func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx,
return tx, nil return tx, nil
} }
func (cn *conn) Ping(ctx context.Context) error {
if finish := cn.watchCancel(ctx); finish != nil {
defer finish()
}
rows, err := cn.simpleQuery("SELECT 'lib/pq ping test';")
if err != nil {
return driver.ErrBadConn // https://golang.org/pkg/database/sql/driver/#Pinger
}
rows.Close()
return nil
}
func (cn *conn) watchCancel(ctx context.Context) func() { func (cn *conn) watchCancel(ctx context.Context) func() {
if done := ctx.Done(); done != nil { if done := ctx.Done(); done != nil {
finished := make(chan struct{}) finished := make(chan struct{})
go func() { go func() {
select { select {
case <-done: case <-done:
_ = cn.cancel() // At this point the function level context is canceled,
// so it must not be used for the additional network
// request to cancel the query.
// Create a new context to pass into the dial.
ctxCancel, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
_ = cn.cancel(ctxCancel)
finished <- struct{}{} finished <- struct{}{}
case <-finished: case <-finished:
} }
@ -97,8 +115,8 @@ func (cn *conn) watchCancel(ctx context.Context) func() {
return nil return nil
} }
func (cn *conn) cancel() error { func (cn *conn) cancel(ctx context.Context) error {
c, err := dial(cn.dialer, cn.opts) c, err := dial(ctx, cn.dialer, cn.opts)
if err != nil { if err != nil {
return err return err
} }

View file

@ -1,10 +1,12 @@
// +build go1.10
package pq package pq
import ( import (
"context" "context"
"database/sql/driver" "database/sql/driver"
"errors"
"fmt"
"os"
"strings"
) )
// Connector represents a fixed configuration for the pq driver with a given // Connector represents a fixed configuration for the pq driver with a given
@ -14,30 +16,95 @@ import (
// //
// See https://golang.org/pkg/database/sql/driver/#Connector. // See https://golang.org/pkg/database/sql/driver/#Connector.
// See https://golang.org/pkg/database/sql/#OpenDB. // See https://golang.org/pkg/database/sql/#OpenDB.
type connector struct { type Connector struct {
name string opts values
dialer Dialer
} }
// Connect returns a connection to the database using the fixed configuration // Connect returns a connection to the database using the fixed configuration
// of this Connector. Context is not used. // of this Connector. Context is not used.
func (c *connector) Connect(_ context.Context) (driver.Conn, error) { func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
return (&Driver{}).Open(c.name) return c.open(ctx)
} }
// Driver returnst the underlying driver of this Connector. // Driver returnst the underlying driver of this Connector.
func (c *connector) Driver() driver.Driver { func (c *Connector) Driver() driver.Driver {
return &Driver{} return &Driver{}
} }
var _ driver.Connector = &connector{}
// NewConnector returns a connector for the pq driver in a fixed configuration // NewConnector returns a connector for the pq driver in a fixed configuration
// with the given name. The returned connector can be used to create any number // with the given dsn. The returned connector can be used to create any number
// of equivalent Conn's. The returned connector is intended to be used with // of equivalent Conn's. The returned connector is intended to be used with
// database/sql.OpenDB. // database/sql.OpenDB.
// //
// See https://golang.org/pkg/database/sql/driver/#Connector. // See https://golang.org/pkg/database/sql/driver/#Connector.
// See https://golang.org/pkg/database/sql/#OpenDB. // See https://golang.org/pkg/database/sql/#OpenDB.
func NewConnector(name string) (driver.Connector, error) { func NewConnector(dsn string) (*Connector, error) {
return &connector{name: name}, nil var err error
o := make(values)
// A number of defaults are applied here, in this order:
//
// * Very low precedence defaults applied in every situation
// * Environment variables
// * Explicitly passed connection information
o["host"] = "localhost"
o["port"] = "5432"
// N.B.: Extra float digits should be set to 3, but that breaks
// Postgres 8.4 and older, where the max is 2.
o["extra_float_digits"] = "2"
for k, v := range parseEnviron(os.Environ()) {
o[k] = v
}
if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
dsn, err = ParseURL(dsn)
if err != nil {
return nil, err
}
}
if err := parseOpts(dsn, o); err != nil {
return nil, err
}
// Use the "fallback" application name if necessary
if fallback, ok := o["fallback_application_name"]; ok {
if _, ok := o["application_name"]; !ok {
o["application_name"] = fallback
}
}
// We can't work with any client_encoding other than UTF-8 currently.
// However, we have historically allowed the user to set it to UTF-8
// explicitly, and there's no reason to break such programs, so allow that.
// Note that the "options" setting could also set client_encoding, but
// parsing its value is not worth it. Instead, we always explicitly send
// client_encoding as a separate run-time parameter, which should override
// anything set in options.
if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) {
return nil, errors.New("client_encoding must be absent or 'UTF8'")
}
o["client_encoding"] = "UTF8"
// DateStyle needs a similar treatment.
if datestyle, ok := o["datestyle"]; ok {
if datestyle != "ISO, MDY" {
return nil, fmt.Errorf("setting datestyle must be absent or %v; got %v", "ISO, MDY", datestyle)
}
} else {
o["datestyle"] = "ISO, MDY"
}
// If a user is not provided by any other means, the last
// resort is to use the current operating system provided user
// name.
if _, ok := o["user"]; !ok {
u, err := userCurrent()
if err != nil {
return nil, err
}
o["user"] = u
}
return &Connector{opts: o, dialer: defaultDialer{}}, nil
} }

2
vendor/github.com/lib/pq/doc.go generated vendored
View file

@ -239,7 +239,7 @@ for more information). Note that the channel name will be truncated to 63
bytes by the PostgreSQL server. bytes by the PostgreSQL server.
You can find a complete, working example of Listener usage at You can find a complete, working example of Listener usage at
http://godoc.org/github.com/lib/pq/example/listen. https://godoc.org/github.com/lib/pq/example/listen.
*/ */
package pq package pq

9
vendor/github.com/lib/pq/encode.go generated vendored
View file

@ -117,11 +117,10 @@ func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interfa
} }
return i return i
case oid.T_float4, oid.T_float8: case oid.T_float4, oid.T_float8:
bits := 64 // We always use 64 bit parsing, regardless of whether the input text is for
if typ == oid.T_float4 { // a float4 or float8, because clients expect float64s for all float datatypes
bits = 32 // and returning a 32-bit parsed float64 produces lossy results.
} f, err := strconv.ParseFloat(string(s), 64)
f, err := strconv.ParseFloat(string(s), bits)
if err != nil { if err != nil {
errorf("%s", err) errorf("%s", err)
} }

10
vendor/github.com/lib/pq/error.go generated vendored
View file

@ -478,13 +478,13 @@ func errRecoverNoErrBadConn(err *error) {
} }
} }
func (c *conn) errRecover(err *error) { func (cn *conn) errRecover(err *error) {
e := recover() e := recover()
switch v := e.(type) { switch v := e.(type) {
case nil: case nil:
// Do nothing // Do nothing
case runtime.Error: case runtime.Error:
c.bad = true cn.bad = true
panic(v) panic(v)
case *Error: case *Error:
if v.Fatal() { if v.Fatal() {
@ -493,7 +493,7 @@ func (c *conn) errRecover(err *error) {
*err = v *err = v
} }
case *net.OpError: case *net.OpError:
c.bad = true cn.bad = true
*err = v *err = v
case error: case error:
if v == io.EOF || v.(error).Error() == "remote error: handshake failure" { if v == io.EOF || v.(error).Error() == "remote error: handshake failure" {
@ -503,13 +503,13 @@ func (c *conn) errRecover(err *error) {
} }
default: default:
c.bad = true cn.bad = true
panic(fmt.Sprintf("unknown error: %#v", e)) panic(fmt.Sprintf("unknown error: %#v", e))
} }
// Any time we return ErrBadConn, we need to remember it since *Tx doesn't // Any time we return ErrBadConn, we need to remember it since *Tx doesn't
// mark the connection bad in database/sql. // mark the connection bad in database/sql.
if *err == driver.ErrBadConn { if *err == driver.ErrBadConn {
c.bad = true cn.bad = true
} }
} }

264
vendor/github.com/lib/pq/scram/scram.go generated vendored Normal file
View file

@ -0,0 +1,264 @@
// Copyright (c) 2014 - Gustavo Niemeyer <gustavo@niemeyer.net>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Package scram implements a SCRAM-{SHA-1,etc} client per RFC5802.
//
// http://tools.ietf.org/html/rfc5802
//
package scram
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"encoding/base64"
"fmt"
"hash"
"strconv"
"strings"
)
// Client implements a SCRAM-* client (SCRAM-SHA-1, SCRAM-SHA-256, etc).
//
// A Client may be used within a SASL conversation with logic resembling:
//
// var in []byte
// var client = scram.NewClient(sha1.New, user, pass)
// for client.Step(in) {
// out := client.Out()
// // send out to server
// in := serverOut
// }
// if client.Err() != nil {
// // auth failed
// }
//
type Client struct {
newHash func() hash.Hash
user string
pass string
step int
out bytes.Buffer
err error
clientNonce []byte
serverNonce []byte
saltedPass []byte
authMsg bytes.Buffer
}
// NewClient returns a new SCRAM-* client with the provided hash algorithm.
//
// For SCRAM-SHA-256, for example, use:
//
// client := scram.NewClient(sha256.New, user, pass)
//
func NewClient(newHash func() hash.Hash, user, pass string) *Client {
c := &Client{
newHash: newHash,
user: user,
pass: pass,
}
c.out.Grow(256)
c.authMsg.Grow(256)
return c
}
// Out returns the data to be sent to the server in the current step.
func (c *Client) Out() []byte {
if c.out.Len() == 0 {
return nil
}
return c.out.Bytes()
}
// Err returns the error that ocurred, or nil if there were no errors.
func (c *Client) Err() error {
return c.err
}
// SetNonce sets the client nonce to the provided value.
// If not set, the nonce is generated automatically out of crypto/rand on the first step.
func (c *Client) SetNonce(nonce []byte) {
c.clientNonce = nonce
}
var escaper = strings.NewReplacer("=", "=3D", ",", "=2C")
// Step processes the incoming data from the server and makes the
// next round of data for the server available via Client.Out.
// Step returns false if there are no errors and more data is
// still expected.
func (c *Client) Step(in []byte) bool {
c.out.Reset()
if c.step > 2 || c.err != nil {
return false
}
c.step++
switch c.step {
case 1:
c.err = c.step1(in)
case 2:
c.err = c.step2(in)
case 3:
c.err = c.step3(in)
}
return c.step > 2 || c.err != nil
}
func (c *Client) step1(in []byte) error {
if len(c.clientNonce) == 0 {
const nonceLen = 16
buf := make([]byte, nonceLen+b64.EncodedLen(nonceLen))
if _, err := rand.Read(buf[:nonceLen]); err != nil {
return fmt.Errorf("cannot read random SCRAM-SHA-256 nonce from operating system: %v", err)
}
c.clientNonce = buf[nonceLen:]
b64.Encode(c.clientNonce, buf[:nonceLen])
}
c.authMsg.WriteString("n=")
escaper.WriteString(&c.authMsg, c.user)
c.authMsg.WriteString(",r=")
c.authMsg.Write(c.clientNonce)
c.out.WriteString("n,,")
c.out.Write(c.authMsg.Bytes())
return nil
}
var b64 = base64.StdEncoding
func (c *Client) step2(in []byte) error {
c.authMsg.WriteByte(',')
c.authMsg.Write(in)
fields := bytes.Split(in, []byte(","))
if len(fields) != 3 {
return fmt.Errorf("expected 3 fields in first SCRAM-SHA-256 server message, got %d: %q", len(fields), in)
}
if !bytes.HasPrefix(fields[0], []byte("r=")) || len(fields[0]) < 2 {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 nonce: %q", fields[0])
}
if !bytes.HasPrefix(fields[1], []byte("s=")) || len(fields[1]) < 6 {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 salt: %q", fields[1])
}
if !bytes.HasPrefix(fields[2], []byte("i=")) || len(fields[2]) < 6 {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2])
}
c.serverNonce = fields[0][2:]
if !bytes.HasPrefix(c.serverNonce, c.clientNonce) {
return fmt.Errorf("server SCRAM-SHA-256 nonce is not prefixed by client nonce: got %q, want %q+\"...\"", c.serverNonce, c.clientNonce)
}
salt := make([]byte, b64.DecodedLen(len(fields[1][2:])))
n, err := b64.Decode(salt, fields[1][2:])
if err != nil {
return fmt.Errorf("cannot decode SCRAM-SHA-256 salt sent by server: %q", fields[1])
}
salt = salt[:n]
iterCount, err := strconv.Atoi(string(fields[2][2:]))
if err != nil {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2])
}
c.saltPassword(salt, iterCount)
c.authMsg.WriteString(",c=biws,r=")
c.authMsg.Write(c.serverNonce)
c.out.WriteString("c=biws,r=")
c.out.Write(c.serverNonce)
c.out.WriteString(",p=")
c.out.Write(c.clientProof())
return nil
}
func (c *Client) step3(in []byte) error {
var isv, ise bool
var fields = bytes.Split(in, []byte(","))
if len(fields) == 1 {
isv = bytes.HasPrefix(fields[0], []byte("v="))
ise = bytes.HasPrefix(fields[0], []byte("e="))
}
if ise {
return fmt.Errorf("SCRAM-SHA-256 authentication error: %s", fields[0][2:])
} else if !isv {
return fmt.Errorf("unsupported SCRAM-SHA-256 final message from server: %q", in)
}
if !bytes.Equal(c.serverSignature(), fields[0][2:]) {
return fmt.Errorf("cannot authenticate SCRAM-SHA-256 server signature: %q", fields[0][2:])
}
return nil
}
func (c *Client) saltPassword(salt []byte, iterCount int) {
mac := hmac.New(c.newHash, []byte(c.pass))
mac.Write(salt)
mac.Write([]byte{0, 0, 0, 1})
ui := mac.Sum(nil)
hi := make([]byte, len(ui))
copy(hi, ui)
for i := 1; i < iterCount; i++ {
mac.Reset()
mac.Write(ui)
mac.Sum(ui[:0])
for j, b := range ui {
hi[j] ^= b
}
}
c.saltedPass = hi
}
func (c *Client) clientProof() []byte {
mac := hmac.New(c.newHash, c.saltedPass)
mac.Write([]byte("Client Key"))
clientKey := mac.Sum(nil)
hash := c.newHash()
hash.Write(clientKey)
storedKey := hash.Sum(nil)
mac = hmac.New(c.newHash, storedKey)
mac.Write(c.authMsg.Bytes())
clientProof := mac.Sum(nil)
for i, b := range clientKey {
clientProof[i] ^= b
}
clientProof64 := make([]byte, b64.EncodedLen(len(clientProof)))
b64.Encode(clientProof64, clientProof)
return clientProof64
}
func (c *Client) serverSignature() []byte {
mac := hmac.New(c.newHash, c.saltedPass)
mac.Write([]byte("Server Key"))
serverKey := mac.Sum(nil)
mac = hmac.New(c.newHash, serverKey)
mac.Write(c.authMsg.Bytes())
serverSignature := mac.Sum(nil)
encoded := make([]byte, b64.EncodedLen(len(serverSignature)))
b64.Encode(encoded, serverSignature)
return encoded
}

8
vendor/github.com/lib/pq/ssl.go generated vendored
View file

@ -58,7 +58,13 @@ func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
sslRenegotiation(&tlsConf)
// Accept renegotiation requests initiated by the backend.
//
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
// the default configuration of older versions has it enabled. Redshift
// also initiates renegotiations and cannot be reconfigured.
tlsConf.Renegotiation = tls.RenegotiateFreelyAsClient
return func(conn net.Conn) (net.Conn, error) { return func(conn net.Conn) (net.Conn, error) {
client := tls.Client(conn, &tlsConf) client := tls.Client(conn, &tlsConf)

View file

@ -1,14 +0,0 @@
// +build go1.7
package pq
import "crypto/tls"
// Accept renegotiation requests initiated by the backend.
//
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
// the default configuration of older versions has it enabled. Redshift
// also initiates renegotiations and cannot be reconfigured.
func sslRenegotiation(conf *tls.Config) {
conf.Renegotiation = tls.RenegotiateFreelyAsClient
}

View file

@ -1,8 +0,0 @@
// +build !go1.7
package pq
import "crypto/tls"
// Renegotiation is not supported by crypto/tls until Go 1.7.
func sslRenegotiation(*tls.Config) {}