[chore] update dependencies (#4422)

- github.com/jackc/pgx/v5 v5.7.5 -> v5.7.6
- github.com/ncruces/go-sqlite3 v0.28.0 -> v0.29.0
- github.com/tdewolff/minify/v2 v2.24.2 -> v2.24.3
- golang.org/x/oauth2 v0.30.0 -> v0.31.0
- golang.org/x/sys v0.35.0 -> v0.36.0
- golang.org/x/text v0.28.0 -> v0.29.0

Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4422
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
This commit is contained in:
kim 2025-09-08 20:53:25 +02:00 committed by kim
commit a6429b5410
78 changed files with 1439 additions and 1189 deletions

18
go.mod
View file

@ -48,13 +48,13 @@ require (
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0 github.com/gorilla/feeds v1.2.0
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/jackc/pgx/v5 v5.7.5 github.com/jackc/pgx/v5 v5.7.6
github.com/k3a/html2text v1.2.1 github.com/k3a/html2text v1.2.1
github.com/microcosm-cc/bluemonday v1.0.27 github.com/microcosm-cc/bluemonday v1.0.27
github.com/miekg/dns v1.1.68 github.com/miekg/dns v1.1.68
github.com/minio/minio-go/v7 v7.0.95 github.com/minio/minio-go/v7 v7.0.95
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/ncruces/go-sqlite3 v0.28.0 github.com/ncruces/go-sqlite3 v0.29.0
github.com/oklog/ulid v1.3.1 github.com/oklog/ulid v1.3.1
github.com/pquerna/otp v1.5.0 github.com/pquerna/otp v1.5.0
github.com/rivo/uniseg v0.4.7 github.com/rivo/uniseg v0.4.7
@ -63,7 +63,7 @@ require (
github.com/spf13/pflag v1.0.10 github.com/spf13/pflag v1.0.10
github.com/spf13/viper v1.20.1 github.com/spf13/viper v1.20.1
github.com/stretchr/testify v1.11.1 github.com/stretchr/testify v1.11.1
github.com/tdewolff/minify/v2 v2.24.2 github.com/tdewolff/minify/v2 v2.24.3
github.com/technologize/otel-go-contrib v1.1.1 github.com/technologize/otel-go-contrib v1.1.1
github.com/temoto/robotstxt v1.1.2 github.com/temoto/robotstxt v1.1.2
github.com/tetratelabs/wazero v1.9.0 github.com/tetratelabs/wazero v1.9.0
@ -86,9 +86,9 @@ require (
golang.org/x/crypto v0.41.0 golang.org/x/crypto v0.41.0
golang.org/x/image v0.30.0 golang.org/x/image v0.30.0
golang.org/x/net v0.43.0 golang.org/x/net v0.43.0
golang.org/x/oauth2 v0.30.0 golang.org/x/oauth2 v0.31.0
golang.org/x/sys v0.35.0 golang.org/x/sys v0.36.0
golang.org/x/text v0.28.0 golang.org/x/text v0.29.0
gopkg.in/mcuadros/go-syslog.v2 v2.3.0 gopkg.in/mcuadros/go-syslog.v2 v2.3.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.38.2 modernc.org/sqlite v1.38.2
@ -231,9 +231,9 @@ require (
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.18.0 // indirect golang.org/x/arch v0.18.0 // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/mod v0.26.0 // indirect golang.org/x/mod v0.27.0 // indirect
golang.org/x/sync v0.16.0 // indirect golang.org/x/sync v0.17.0 // indirect
golang.org/x/tools v0.35.0 // indirect golang.org/x/tools v0.36.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.0 // indirect google.golang.org/grpc v1.75.0 // indirect

36
go.sum generated
View file

@ -273,8 +273,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@ -340,8 +340,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/ncruces/go-sqlite3 v0.28.0 h1:AQVTUPgfamONl09LS+4rGFbHmLKM8/QrJJJi1UukjEQ= github.com/ncruces/go-sqlite3 v0.29.0 h1:1tsLiagCoqZEfcHDeKsNSv5jvrY/Iu393pAnw2wLNJU=
github.com/ncruces/go-sqlite3 v0.28.0/go.mod h1:WqvLhYwtEiZzg1H8BIeahUv/DxbmR+3xG5jDHDiBAGk= github.com/ncruces/go-sqlite3 v0.29.0/go.mod h1:r1hSvYKPNJ+OlUA1O3r8o9LAawzPAlqeZiIdxTBBBJ0=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M= github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
@ -431,8 +431,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tdewolff/minify/v2 v2.24.2 h1:vnY3nTulEAbCAAlxTxPPDkzG24rsq31SOzp63yT+7mo= github.com/tdewolff/minify/v2 v2.24.3 h1:BaKgWSFLKbKDiUskbeRgbe2n5d1Ci1x3cN/eXna8zOA=
github.com/tdewolff/minify/v2 v2.24.2/go.mod h1:1JrCtoZXaDbqioQZfk3Jdmr0GPJKiU7c1Apmb+7tCeE= github.com/tdewolff/minify/v2 v2.24.3/go.mod h1:1JrCtoZXaDbqioQZfk3Jdmr0GPJKiU7c1Apmb+7tCeE=
github.com/tdewolff/parse/v2 v2.8.3 h1:5VbvtJ83cfb289A1HzRA9sf02iT8YyUwN84ezjkdY1I= github.com/tdewolff/parse/v2 v2.8.3 h1:5VbvtJ83cfb289A1HzRA9sf02iT8YyUwN84ezjkdY1I=
github.com/tdewolff/parse/v2 v2.8.3/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= github.com/tdewolff/parse/v2 v2.8.3/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE= github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=
@ -588,8 +588,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -607,8 +607,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -616,8 +616,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -633,8 +633,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -657,8 +657,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@ -666,8 +666,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=

21
vendor/github.com/jackc/pgx/v5/.golangci.yml generated vendored Normal file
View file

@ -0,0 +1,21 @@
# See for configurations: https://golangci-lint.run/usage/configuration/
version: 2
# See: https://golangci-lint.run/usage/formatters/
formatters:
default: none
enable:
- gofmt # https://pkg.go.dev/cmd/gofmt
- gofumpt # https://github.com/mvdan/gofumpt
settings:
gofmt:
simplify: true # Simplify code: gofmt with `-s` option.
gofumpt:
# Module path which contains the source code being formatted.
# Default: ""
module-path: github.com/jackc/pgx/v5 # Should match with module in go.mod
# Choose whether to use the extra rules.
# Default: false
extra-rules: true

View file

@ -1,3 +1,14 @@
# 5.7.6 (September 8, 2025)
* Use ParseConfigError in pgx.ParseConfig and pgxpool.ParseConfig (Yurasov Ilia)
* Add PrepareConn hook to pgxpool (Jonathan Hall)
* Reduce allocations in QueryContext (Dominique Lefevre)
* Add MarshalJSON and UnmarshalJSON for pgtype.Uint32 (Panos Koutsovasilis)
* Configure ping behavior on pgxpool with ShouldPing (Christian Kiely)
* zeronull int types implement Int64Valuer and Int64Scanner (Li Zeghong)
* Fix panic when receiving terminate connection message during CopyFrom (Michal Drausowski)
* Fix statement cache not being invalidated on error during batch (Muhammadali Nazarov)
# 5.7.5 (May 17, 2025) # 5.7.5 (May 17, 2025)
* Support sslnegotiation connection option (divyam234) * Support sslnegotiation connection option (divyam234)

View file

@ -127,6 +127,7 @@ pgerrcode contains constants for the PostgreSQL error codes.
## Adapters for 3rd Party Tracers ## Adapters for 3rd Party Tracers
* [github.com/jackhopner/pgx-xray-tracer](https://github.com/jackhopner/pgx-xray-tracer) * [github.com/jackhopner/pgx-xray-tracer](https://github.com/jackhopner/pgx-xray-tracer)
* [github.com/exaring/otelpgx](https://github.com/exaring/otelpgx)
## Adapters for 3rd Party Loggers ## Adapters for 3rd Party Loggers
@ -184,3 +185,7 @@ Simple Golang implementation for transactional outbox pattern for PostgreSQL usi
### [https://github.com/Arlandaren/pgxWrappy](https://github.com/Arlandaren/pgxWrappy) ### [https://github.com/Arlandaren/pgxWrappy](https://github.com/Arlandaren/pgxWrappy)
Simplifies working with the pgx library, providing convenient scanning of nested structures. Simplifies working with the pgx library, providing convenient scanning of nested structures.
## [https://github.com/KoNekoD/pgx-colon-query-rewriter](https://github.com/KoNekoD/pgx-colon-query-rewriter)
Implementation of the pgx query rewriter to use ':' instead of '@' in named query parameters.

View file

@ -43,6 +43,10 @@ func (qq *QueuedQuery) QueryRow(fn func(row Row) error) {
} }
// Exec sets fn to be called when the response to qq is received. // Exec sets fn to be called when the response to qq is received.
//
// Note: for simple batch insert uses where it is not required to handle
// each potential error individually, it's sufficient to not set any callbacks,
// and just handle the return value of BatchResults.Close.
func (qq *QueuedQuery) Exec(fn func(ct pgconn.CommandTag) error) { func (qq *QueuedQuery) Exec(fn func(ct pgconn.CommandTag) error) {
qq.Fn = func(br BatchResults) error { qq.Fn = func(br BatchResults) error {
ct, err := br.Exec() ct, err := br.Exec()
@ -83,7 +87,7 @@ func (b *Batch) Len() int {
type BatchResults interface { type BatchResults interface {
// Exec reads the results from the next query in the batch as if the query has been sent with Conn.Exec. Prefer // Exec reads the results from the next query in the batch as if the query has been sent with Conn.Exec. Prefer
// calling Exec on the QueuedQuery. // calling Exec on the QueuedQuery, or just calling Close.
Exec() (pgconn.CommandTag, error) Exec() (pgconn.CommandTag, error)
// Query reads the results from the next query in the batch as if the query has been sent with Conn.Query. Prefer // Query reads the results from the next query in the batch as if the query has been sent with Conn.Query. Prefer
@ -98,6 +102,9 @@ type BatchResults interface {
// QueuedQuery.Query, QueuedQuery.QueryRow, or QueuedQuery.Exec will be called. If a callback function returns an // QueuedQuery.Query, QueuedQuery.QueryRow, or QueuedQuery.Exec will be called. If a callback function returns an
// error or the batch encounters an error subsequent callback functions will not be called. // error or the batch encounters an error subsequent callback functions will not be called.
// //
// For simple batch inserts inside a transaction or similar queries, it's sufficient to not set any callbacks,
// and just handle the return value of Close.
//
// Close must be called before the underlying connection can be used again. Any error that occurred during a batch // Close must be called before the underlying connection can be used again. Any error that occurred during a batch
// operation may have made it impossible to resyncronize the connection with the server. In this case the underlying // operation may have made it impossible to resyncronize the connection with the server. In this case the underlying
// connection will have been closed. // connection will have been closed.
@ -207,7 +214,6 @@ func (br *batchResults) Query() (Rows, error) {
func (br *batchResults) QueryRow() Row { func (br *batchResults) QueryRow() Row {
rows, _ := br.Query() rows, _ := br.Query()
return (*connRow)(rows.(*baseRows)) return (*connRow)(rows.(*baseRows))
} }
// Close closes the batch operation. Any error that occurred during a batch operation may have made it impossible to // Close closes the batch operation. Any error that occurred during a batch operation may have made it impossible to
@ -220,6 +226,8 @@ func (br *batchResults) Close() error {
} }
br.endTraced = true br.endTraced = true
} }
invalidateCachesOnBatchResultsError(br.conn, br.b, br.err)
}() }()
if br.err != nil { if br.err != nil {
@ -378,7 +386,6 @@ func (br *pipelineBatchResults) Query() (Rows, error) {
func (br *pipelineBatchResults) QueryRow() Row { func (br *pipelineBatchResults) QueryRow() Row {
rows, _ := br.Query() rows, _ := br.Query()
return (*connRow)(rows.(*baseRows)) return (*connRow)(rows.(*baseRows))
} }
// Close closes the batch operation. Any error that occurred during a batch operation may have made it impossible to // Close closes the batch operation. Any error that occurred during a batch operation may have made it impossible to
@ -391,6 +398,8 @@ func (br *pipelineBatchResults) Close() error {
} }
br.endTraced = true br.endTraced = true
} }
invalidateCachesOnBatchResultsError(br.conn, br.b, br.err)
}() }()
if br.err == nil && br.lastRows != nil && br.lastRows.err != nil { if br.err == nil && br.lastRows != nil && br.lastRows.err != nil {
@ -441,3 +450,20 @@ func (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, er
br.qqIdx++ br.qqIdx++
return bi.SQL, bi.Arguments, nil return bi.SQL, bi.Arguments, nil
} }
// invalidates statement and description caches on batch results error
func invalidateCachesOnBatchResultsError(conn *Conn, b *Batch, err error) {
if err != nil && conn != nil && b != nil {
if sc := conn.statementCache; sc != nil {
for _, bi := range b.QueuedQueries {
sc.Invalidate(bi.SQL)
}
}
if sc := conn.descriptionCache; sc != nil {
for _, bi := range b.QueuedQueries {
sc.Invalidate(bi.SQL)
}
}
}
}

View file

@ -172,7 +172,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
delete(config.RuntimeParams, "statement_cache_capacity") delete(config.RuntimeParams, "statement_cache_capacity")
n, err := strconv.ParseInt(s, 10, 32) n, err := strconv.ParseInt(s, 10, 32)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot parse statement_cache_capacity: %w", err) return nil, pgconn.NewParseConfigError(connString, "cannot parse statement_cache_capacity", err)
} }
statementCacheCapacity = int(n) statementCacheCapacity = int(n)
} }
@ -182,7 +182,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
delete(config.RuntimeParams, "description_cache_capacity") delete(config.RuntimeParams, "description_cache_capacity")
n, err := strconv.ParseInt(s, 10, 32) n, err := strconv.ParseInt(s, 10, 32)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot parse description_cache_capacity: %w", err) return nil, pgconn.NewParseConfigError(connString, "cannot parse description_cache_capacity", err)
} }
descriptionCacheCapacity = int(n) descriptionCacheCapacity = int(n)
} }
@ -202,7 +202,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
case "simple_protocol": case "simple_protocol":
defaultQueryExecMode = QueryExecModeSimpleProtocol defaultQueryExecMode = QueryExecModeSimpleProtocol
default: default:
return nil, fmt.Errorf("invalid default_query_exec_mode: %s", s) return nil, pgconn.NewParseConfigError(connString, "invalid default_query_exec_mode", err)
} }
} }

View file

@ -31,7 +31,6 @@ func (c *LRUCache) Get(key string) *pgconn.StatementDescription {
} }
return nil return nil
} }
// Put stores sd in the cache. Put panics if sd.SQL is "". Put does nothing if sd.SQL already exists in the cache or // Put stores sd in the cache. Put panics if sd.SQL is "". Put does nothing if sd.SQL already exists in the cache or

View file

@ -263,7 +263,7 @@ func computeClientProof(saltedPassword, authMessage []byte) []byte {
return buf return buf
} }
func computeServerSignature(saltedPassword []byte, authMessage []byte) []byte { func computeServerSignature(saltedPassword, authMessage []byte) []byte {
serverKey := computeHMAC(saltedPassword, []byte("Server Key")) serverKey := computeHMAC(saltedPassword, []byte("Server Key"))
serverSignature := computeHMAC(serverKey, authMessage) serverSignature := computeHMAC(serverKey, authMessage)
buf := make([]byte, base64.StdEncoding.EncodedLen(len(serverSignature))) buf := make([]byte, base64.StdEncoding.EncodedLen(len(serverSignature)))

View file

@ -23,9 +23,11 @@ import (
"github.com/jackc/pgx/v5/pgproto3" "github.com/jackc/pgx/v5/pgproto3"
) )
type AfterConnectFunc func(ctx context.Context, pgconn *PgConn) error type (
type ValidateConnectFunc func(ctx context.Context, pgconn *PgConn) error AfterConnectFunc func(ctx context.Context, pgconn *PgConn) error
type GetSSLPasswordFunc func(ctx context.Context) string ValidateConnectFunc func(ctx context.Context, pgconn *PgConn) error
GetSSLPasswordFunc func(ctx context.Context) string
)
// Config is the settings used to establish a connection to a PostgreSQL server. It must be created by [ParseConfig]. A // Config is the settings used to establish a connection to a PostgreSQL server. It must be created by [ParseConfig]. A
// manually initialized Config will cause ConnectConfig to panic. // manually initialized Config will cause ConnectConfig to panic.
@ -179,7 +181,7 @@ func NetworkAddress(host string, port uint16) (network, address string) {
// //
// ParseConfig supports specifying multiple hosts in similar manner to libpq. Host and port may include comma separated // ParseConfig supports specifying multiple hosts in similar manner to libpq. Host and port may include comma separated
// values that will be tried in order. This can be used as part of a high availability system. See // values that will be tried in order. This can be used as part of a high availability system. See
// https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS for more information. // https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS for more information.
// //
// # Example URL // # Example URL
// postgres://jack:secret@foo.example.com:5432,bar.example.com:5432/mydb // postgres://jack:secret@foo.example.com:5432,bar.example.com:5432/mydb
@ -206,9 +208,9 @@ func NetworkAddress(host string, port uint16) (network, address string) {
// PGTARGETSESSIONATTRS // PGTARGETSESSIONATTRS
// PGTZ // PGTZ
// //
// See http://www.postgresql.org/docs/11/static/libpq-envars.html for details on the meaning of environment variables. // See http://www.postgresql.org/docs/current/static/libpq-envars.html for details on the meaning of environment variables.
// //
// See https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-PARAMKEYWORDS for parameter key word names. They are // See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS for parameter key word names. They are
// usually but not always the environment variable name downcased and without the "PG" prefix. // usually but not always the environment variable name downcased and without the "PG" prefix.
// //
// Important Security Notes: // Important Security Notes:
@ -216,7 +218,7 @@ func NetworkAddress(host string, port uint16) (network, address string) {
// ParseConfig tries to match libpq behavior with regard to PGSSLMODE. This includes defaulting to "prefer" behavior if // ParseConfig tries to match libpq behavior with regard to PGSSLMODE. This includes defaulting to "prefer" behavior if
// not set. // not set.
// //
// See http://www.postgresql.org/docs/11/static/libpq-ssl.html#LIBPQ-SSL-PROTECTION for details on what level of // See http://www.postgresql.org/docs/current/static/libpq-ssl.html#LIBPQ-SSL-PROTECTION for details on what level of
// security each sslmode provides. // security each sslmode provides.
// //
// The sslmode "prefer" (the default), sslmode "allow", and multiple hosts are implemented via the Fallbacks field of // The sslmode "prefer" (the default), sslmode "allow", and multiple hosts are implemented via the Fallbacks field of
@ -713,7 +715,7 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
// According to PostgreSQL documentation, if a root CA file exists, // According to PostgreSQL documentation, if a root CA file exists,
// the behavior of sslmode=require should be the same as that of verify-ca // the behavior of sslmode=require should be the same as that of verify-ca
// //
// See https://www.postgresql.org/docs/12/libpq-ssl.html // See https://www.postgresql.org/docs/current/libpq-ssl.html
if sslrootcert != "" { if sslrootcert != "" {
goto nextCase goto nextCase
} }

View file

@ -27,7 +27,7 @@ func Timeout(err error) bool {
} }
// PgError represents an error reported by the PostgreSQL server. See // PgError represents an error reported by the PostgreSQL server. See
// http://www.postgresql.org/docs/11/static/protocol-error-fields.html for // http://www.postgresql.org/docs/current/static/protocol-error-fields.html for
// detailed field description. // detailed field description.
type PgError struct { type PgError struct {
Severity string Severity string
@ -112,6 +112,14 @@ type ParseConfigError struct {
err error err error
} }
func NewParseConfigError(conn, msg string, err error) error {
return &ParseConfigError{
ConnString: conn,
msg: msg,
err: err,
}
}
func (e *ParseConfigError) Error() string { func (e *ParseConfigError) Error() string {
// Now that ParseConfigError is public and ConnString is available to the developer, perhaps it would be better only // Now that ParseConfigError is public and ConnString is available to the developer, perhaps it would be better only
// return a static string. That would ensure that the error message cannot leak a password. The ConnString field would // return a static string. That would ensure that the error message cannot leak a password. The ConnString field would

View file

@ -28,7 +28,7 @@ func RegisterGSSProvider(newGSSArg NewGSSFunc) {
// GSS provides GSSAPI authentication (e.g., Kerberos). // GSS provides GSSAPI authentication (e.g., Kerberos).
type GSS interface { type GSS interface {
GetInitToken(host string, service string) ([]byte, error) GetInitToken(host, service string) ([]byte, error)
GetInitTokenFromSPN(spn string) ([]byte, error) GetInitTokenFromSPN(spn string) ([]byte, error)
Continue(inToken []byte) (done bool, outToken []byte, err error) Continue(inToken []byte) (done bool, outToken []byte, err error)
} }

View file

@ -135,7 +135,7 @@ func ConnectWithOptions(ctx context.Context, connString string, parseConfigOptio
// //
// If config.Fallbacks are present they will sequentially be tried in case of error establishing network connection. An // If config.Fallbacks are present they will sequentially be tried in case of error establishing network connection. An
// authentication error will terminate the chain of attempts (like libpq: // authentication error will terminate the chain of attempts (like libpq:
// https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS) and be returned as the error. // https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS) and be returned as the error.
func ConnectConfig(ctx context.Context, config *Config) (*PgConn, error) { func ConnectConfig(ctx context.Context, config *Config) (*PgConn, error) {
// Default values are set in ParseConfig. Enforce initial creation by ParseConfig rather than setting defaults from // Default values are set in ParseConfig. Enforce initial creation by ParseConfig rather than setting defaults from
// zero values. // zero values.
@ -991,7 +991,8 @@ func noticeResponseToNotice(msg *pgproto3.NoticeResponse) *Notice {
// CancelRequest sends a cancel request to the PostgreSQL server. It returns an error if unable to deliver the cancel // CancelRequest sends a cancel request to the PostgreSQL server. It returns an error if unable to deliver the cancel
// request, but lack of an error does not ensure that the query was canceled. As specified in the documentation, there // request, but lack of an error does not ensure that the query was canceled. As specified in the documentation, there
// is no way to be sure a query was canceled. See https://www.postgresql.org/docs/11/protocol-flow.html#id-1.10.5.7.9 // is no way to be sure a query was canceled.
// See https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-CANCELING-REQUESTS
func (pgConn *PgConn) CancelRequest(ctx context.Context) error { func (pgConn *PgConn) CancelRequest(ctx context.Context) error {
// Open a cancellation request to the same server. The address is taken from the net.Conn directly instead of reusing // Open a cancellation request to the same server. The address is taken from the net.Conn directly instead of reusing
// the connection config. This is important in high availability configurations where fallback connections may be // the connection config. This is important in high availability configurations where fallback connections may be
@ -1140,7 +1141,7 @@ func (pgConn *PgConn) Exec(ctx context.Context, sql string) *MultiResultReader {
// binary format. If resultFormats is nil all results will be in text format. // binary format. If resultFormats is nil all results will be in text format.
// //
// ResultReader must be closed before PgConn can be used again. // ResultReader must be closed before PgConn can be used again.
func (pgConn *PgConn) ExecParams(ctx context.Context, sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats []int16, resultFormats []int16) *ResultReader { func (pgConn *PgConn) ExecParams(ctx context.Context, sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats, resultFormats []int16) *ResultReader {
result := pgConn.execExtendedPrefix(ctx, paramValues) result := pgConn.execExtendedPrefix(ctx, paramValues)
if result.closed { if result.closed {
return result return result
@ -1166,7 +1167,7 @@ func (pgConn *PgConn) ExecParams(ctx context.Context, sql string, paramValues []
// binary format. If resultFormats is nil all results will be in text format. // binary format. If resultFormats is nil all results will be in text format.
// //
// ResultReader must be closed before PgConn can be used again. // ResultReader must be closed before PgConn can be used again.
func (pgConn *PgConn) ExecPrepared(ctx context.Context, stmtName string, paramValues [][]byte, paramFormats []int16, resultFormats []int16) *ResultReader { func (pgConn *PgConn) ExecPrepared(ctx context.Context, stmtName string, paramValues [][]byte, paramFormats, resultFormats []int16) *ResultReader {
result := pgConn.execExtendedPrefix(ctx, paramValues) result := pgConn.execExtendedPrefix(ctx, paramValues)
if result.closed { if result.closed {
return result return result
@ -1373,7 +1374,14 @@ func (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (Co
close(pgConn.cleanupDone) close(pgConn.cleanupDone)
return CommandTag{}, normalizeTimeoutError(ctx, err) return CommandTag{}, normalizeTimeoutError(ctx, err)
} }
msg, _ := pgConn.receiveMessage() // peekMessage never returns err in the bufferingReceive mode - it only forwards the bufferingReceive variables.
// Therefore, the only case for receiveMessage to return err is during handling of the ErrorResponse message type
// and using pgOnError handler to determine the connection is no longer valid (and thus closing the conn).
msg, serverError := pgConn.receiveMessage()
if serverError != nil {
close(abortCopyChan)
return CommandTag{}, serverError
}
switch msg := msg.(type) { switch msg := msg.(type) {
case *pgproto3.ErrorResponse: case *pgproto3.ErrorResponse:
@ -1712,7 +1720,7 @@ type Batch struct {
} }
// ExecParams appends an ExecParams command to the batch. See PgConn.ExecParams for parameter descriptions. // ExecParams appends an ExecParams command to the batch. See PgConn.ExecParams for parameter descriptions.
func (batch *Batch) ExecParams(sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats []int16, resultFormats []int16) { func (batch *Batch) ExecParams(sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats, resultFormats []int16) {
if batch.err != nil { if batch.err != nil {
return return
} }
@ -1725,7 +1733,7 @@ func (batch *Batch) ExecParams(sql string, paramValues [][]byte, paramOIDs []uin
} }
// ExecPrepared appends an ExecPrepared e command to the batch. See PgConn.ExecPrepared for parameter descriptions. // ExecPrepared appends an ExecPrepared e command to the batch. See PgConn.ExecPrepared for parameter descriptions.
func (batch *Batch) ExecPrepared(stmtName string, paramValues [][]byte, paramFormats []int16, resultFormats []int16) { func (batch *Batch) ExecPrepared(stmtName string, paramValues [][]byte, paramFormats, resultFormats []int16) {
if batch.err != nil { if batch.err != nil {
return return
} }
@ -2201,7 +2209,7 @@ func (p *Pipeline) SendDeallocate(name string) {
} }
// SendQueryParams is the pipeline version of *PgConn.QueryParams. // SendQueryParams is the pipeline version of *PgConn.QueryParams.
func (p *Pipeline) SendQueryParams(sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats []int16, resultFormats []int16) { func (p *Pipeline) SendQueryParams(sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats, resultFormats []int16) {
if p.closed { if p.closed {
return return
} }
@ -2214,7 +2222,7 @@ func (p *Pipeline) SendQueryParams(sql string, paramValues [][]byte, paramOIDs [
} }
// SendQueryPrepared is the pipeline version of *PgConn.QueryPrepared. // SendQueryPrepared is the pipeline version of *PgConn.QueryPrepared.
func (p *Pipeline) SendQueryPrepared(stmtName string, paramValues [][]byte, paramFormats []int16, resultFormats []int16) { func (p *Pipeline) SendQueryPrepared(stmtName string, paramValues [][]byte, paramFormats, resultFormats []int16) {
if p.closed { if p.closed {
return return
} }

View file

@ -9,8 +9,7 @@ import (
) )
// AuthenticationCleartextPassword is a message sent from the backend indicating that a clear-text password is required. // AuthenticationCleartextPassword is a message sent from the backend indicating that a clear-text password is required.
type AuthenticationCleartextPassword struct { type AuthenticationCleartextPassword struct{}
}
// Backend identifies this message as sendable by the PostgreSQL backend. // Backend identifies this message as sendable by the PostgreSQL backend.
func (*AuthenticationCleartextPassword) Backend() {} func (*AuthenticationCleartextPassword) Backend() {}

View file

@ -9,8 +9,7 @@ import (
) )
// AuthenticationOk is a message sent from the backend indicating that authentication was successful. // AuthenticationOk is a message sent from the backend indicating that authentication was successful.
type AuthenticationOk struct { type AuthenticationOk struct{}
}
// Backend identifies this message as sendable by the PostgreSQL backend. // Backend identifies this message as sendable by the PostgreSQL backend.
func (*AuthenticationOk) Backend() {} func (*AuthenticationOk) Backend() {}

View file

@ -4,8 +4,7 @@ import (
"encoding/json" "encoding/json"
) )
type CopyDone struct { type CopyDone struct{}
}
// Backend identifies this message as sendable by the PostgreSQL backend. // Backend identifies this message as sendable by the PostgreSQL backend.
func (*CopyDone) Backend() {} func (*CopyDone) Backend() {}

View file

@ -10,8 +10,7 @@ import (
const gssEncReqNumber = 80877104 const gssEncReqNumber = 80877104
type GSSEncRequest struct { type GSSEncRequest struct{}
}
// Frontend identifies this message as sendable by a PostgreSQL frontend. // Frontend identifies this message as sendable by a PostgreSQL frontend.
func (*GSSEncRequest) Frontend() {} func (*GSSEncRequest) Frontend() {}

View file

@ -56,7 +56,6 @@ func (*RowDescription) Backend() {}
// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
// type identifier and 4 byte message length. // type identifier and 4 byte message length.
func (dst *RowDescription) Decode(src []byte) error { func (dst *RowDescription) Decode(src []byte) error {
if len(src) < 2 { if len(src) < 2 {
return &invalidMessageFormatErr{messageType: "RowDescription"} return &invalidMessageFormatErr{messageType: "RowDescription"}
} }

View file

@ -10,8 +10,7 @@ import (
const sslRequestNumber = 80877103 const sslRequestNumber = 80877103
type SSLRequest struct { type SSLRequest struct{}
}
// Frontend identifies this message as sendable by a PostgreSQL frontend. // Frontend identifies this message as sendable by a PostgreSQL frontend.
func (*SSLRequest) Frontend() {} func (*SSLRequest) Frontend() {}

View file

@ -374,8 +374,8 @@ func quoteArrayElementIfNeeded(src string) string {
return src return src
} }
// Array represents a PostgreSQL array for T. It implements the ArrayGetter and ArraySetter interfaces. It preserves // Array represents a PostgreSQL array for T. It implements the [ArrayGetter] and [ArraySetter] interfaces. It preserves
// PostgreSQL dimensions and custom lower bounds. Use FlatArray if these are not needed. // PostgreSQL dimensions and custom lower bounds. Use [FlatArray] if these are not needed.
type Array[T any] struct { type Array[T any] struct {
Elements []T Elements []T
Dims []ArrayDimension Dims []ArrayDimension
@ -419,8 +419,8 @@ func (a Array[T]) ScanIndexType() any {
return new(T) return new(T)
} }
// FlatArray implements the ArrayGetter and ArraySetter interfaces for any slice of T. It ignores PostgreSQL dimensions // FlatArray implements the [ArrayGetter] and [ArraySetter] interfaces for any slice of T. It ignores PostgreSQL dimensions
// and custom lower bounds. Use Array to preserve these. // and custom lower bounds. Use [Array] to preserve these.
type FlatArray[T any] []T type FlatArray[T any] []T
func (a FlatArray[T]) Dimensions() []ArrayDimension { func (a FlatArray[T]) Dimensions() []ArrayDimension {

View file

@ -23,16 +23,18 @@ type Bits struct {
Valid bool Valid bool
} }
// ScanBits implements the [BitsScanner] interface.
func (b *Bits) ScanBits(v Bits) error { func (b *Bits) ScanBits(v Bits) error {
*b = v *b = v
return nil return nil
} }
// BitsValue implements the [BitsValuer] interface.
func (b Bits) BitsValue() (Bits, error) { func (b Bits) BitsValue() (Bits, error) {
return b, nil return b, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Bits) Scan(src any) error { func (dst *Bits) Scan(src any) error {
if src == nil { if src == nil {
*dst = Bits{} *dst = Bits{}
@ -47,7 +49,7 @@ func (dst *Bits) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Bits) Value() (driver.Value, error) { func (src Bits) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -127,7 +129,6 @@ func (encodePlanBitsCodecText) Encode(value any, buf []byte) (newBuf []byte, err
} }
func (BitsCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (BitsCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -22,16 +22,18 @@ type Bool struct {
Valid bool Valid bool
} }
// ScanBool implements the [BoolScanner] interface.
func (b *Bool) ScanBool(v Bool) error { func (b *Bool) ScanBool(v Bool) error {
*b = v *b = v
return nil return nil
} }
// BoolValue implements the [BoolValuer] interface.
func (b Bool) BoolValue() (Bool, error) { func (b Bool) BoolValue() (Bool, error) {
return b, nil return b, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Bool) Scan(src any) error { func (dst *Bool) Scan(src any) error {
if src == nil { if src == nil {
*dst = Bool{} *dst = Bool{}
@ -61,7 +63,7 @@ func (dst *Bool) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Bool) Value() (driver.Value, error) { func (src Bool) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -70,6 +72,7 @@ func (src Bool) Value() (driver.Value, error) {
return src.Bool, nil return src.Bool, nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (src Bool) MarshalJSON() ([]byte, error) { func (src Bool) MarshalJSON() ([]byte, error) {
if !src.Valid { if !src.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -82,6 +85,7 @@ func (src Bool) MarshalJSON() ([]byte, error) {
} }
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (dst *Bool) UnmarshalJSON(b []byte) error { func (dst *Bool) UnmarshalJSON(b []byte) error {
var v *bool var v *bool
err := json.Unmarshal(b, &v) err := json.Unmarshal(b, &v)
@ -200,7 +204,6 @@ func (encodePlanBoolCodecTextBool) Encode(value any, buf []byte) (newBuf []byte,
} }
func (BoolCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (BoolCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {
@ -328,7 +331,7 @@ func (scanPlanTextAnyToBoolScanner) Scan(src []byte, dst any) error {
return s.ScanBool(Bool{Bool: v, Valid: true}) return s.ScanBool(Bool{Bool: v, Valid: true})
} }
// https://www.postgresql.org/docs/11/datatype-boolean.html // https://www.postgresql.org/docs/current/datatype-boolean.html
func planTextToBool(src []byte) (bool, error) { func planTextToBool(src []byte) (bool, error) {
s := string(bytes.ToLower(bytes.TrimSpace(src))) s := string(bytes.ToLower(bytes.TrimSpace(src)))

View file

@ -24,16 +24,18 @@ type Box struct {
Valid bool Valid bool
} }
// ScanBox implements the [BoxScanner] interface.
func (b *Box) ScanBox(v Box) error { func (b *Box) ScanBox(v Box) error {
*b = v *b = v
return nil return nil
} }
// BoxValue implements the [BoxValuer] interface.
func (b Box) BoxValue() (Box, error) { func (b Box) BoxValue() (Box, error) {
return b, nil return b, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Box) Scan(src any) error { func (dst *Box) Scan(src any) error {
if src == nil { if src == nil {
*dst = Box{} *dst = Box{}
@ -48,7 +50,7 @@ func (dst *Box) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Box) Value() (driver.Value, error) { func (src Box) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -127,7 +129,6 @@ func (encodePlanBoxCodecText) Encode(value any, buf []byte) (newBuf []byte, err
} }
func (BoxCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (BoxCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -527,6 +527,7 @@ func (w *netIPNetWrapper) ScanNetipPrefix(v netip.Prefix) error {
return nil return nil
} }
func (w netIPNetWrapper) NetipPrefixValue() (netip.Prefix, error) { func (w netIPNetWrapper) NetipPrefixValue() (netip.Prefix, error) {
ip, ok := netip.AddrFromSlice(w.IP) ip, ok := netip.AddrFromSlice(w.IP)
if !ok { if !ok {
@ -881,7 +882,6 @@ func (a *anyMultiDimSliceArray) SetDimensions(dimensions []ArrayDimension) error
return nil return nil
} }
} }
func (a *anyMultiDimSliceArray) makeMultidimensionalSlice(sliceType reflect.Type, dimensions []ArrayDimension, flatSlice reflect.Value, flatSliceIdx int) reflect.Value { func (a *anyMultiDimSliceArray) makeMultidimensionalSlice(sliceType reflect.Type, dimensions []ArrayDimension, flatSlice reflect.Value, flatSliceIdx int) reflect.Value {

View file

@ -148,7 +148,6 @@ func (encodePlanBytesCodecTextBytesValuer) Encode(value any, buf []byte) (newBuf
} }
func (ByteaCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (ByteaCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -25,16 +25,18 @@ type Circle struct {
Valid bool Valid bool
} }
// ScanCircle implements the [CircleScanner] interface.
func (c *Circle) ScanCircle(v Circle) error { func (c *Circle) ScanCircle(v Circle) error {
*c = v *c = v
return nil return nil
} }
// CircleValue implements the [CircleValuer] interface.
func (c Circle) CircleValue() (Circle, error) { func (c Circle) CircleValue() (Circle, error) {
return c, nil return c, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Circle) Scan(src any) error { func (dst *Circle) Scan(src any) error {
if src == nil { if src == nil {
*dst = Circle{} *dst = Circle{}
@ -49,7 +51,7 @@ func (dst *Circle) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Circle) Value() (driver.Value, error) { func (src Circle) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil

View file

@ -276,7 +276,6 @@ func (c *CompositeCodec) DecodeValue(m *Map, oid uint32, format int16, src []byt
default: default:
return nil, fmt.Errorf("unknown format code %d", format) return nil, fmt.Errorf("unknown format code %d", format)
} }
} }
type CompositeBinaryScanner struct { type CompositeBinaryScanner struct {

View file

@ -26,11 +26,13 @@ type Date struct {
Valid bool Valid bool
} }
// ScanDate implements the [DateScanner] interface.
func (d *Date) ScanDate(v Date) error { func (d *Date) ScanDate(v Date) error {
*d = v *d = v
return nil return nil
} }
// DateValue implements the [DateValuer] interface.
func (d Date) DateValue() (Date, error) { func (d Date) DateValue() (Date, error) {
return d, nil return d, nil
} }
@ -40,7 +42,7 @@ const (
infinityDayOffset = 2147483647 infinityDayOffset = 2147483647
) )
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Date) Scan(src any) error { func (dst *Date) Scan(src any) error {
if src == nil { if src == nil {
*dst = Date{} *dst = Date{}
@ -58,7 +60,7 @@ func (dst *Date) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Date) Value() (driver.Value, error) { func (src Date) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -70,6 +72,7 @@ func (src Date) Value() (driver.Value, error) {
return src.Time, nil return src.Time, nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (src Date) MarshalJSON() ([]byte, error) { func (src Date) MarshalJSON() ([]byte, error) {
if !src.Valid { if !src.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -89,6 +92,7 @@ func (src Date) MarshalJSON() ([]byte, error) {
return json.Marshal(s) return json.Marshal(s)
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (dst *Date) UnmarshalJSON(b []byte) error { func (dst *Date) UnmarshalJSON(b []byte) error {
var s *string var s *string
err := json.Unmarshal(b, &s) err := json.Unmarshal(b, &s)
@ -223,7 +227,6 @@ func (encodePlanDateCodecText) Encode(value any, buf []byte) (newBuf []byte, err
} }
func (DateCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (DateCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -16,26 +16,29 @@ type Float4 struct {
Valid bool Valid bool
} }
// ScanFloat64 implements the Float64Scanner interface. // ScanFloat64 implements the [Float64Scanner] interface.
func (f *Float4) ScanFloat64(n Float8) error { func (f *Float4) ScanFloat64(n Float8) error {
*f = Float4{Float32: float32(n.Float64), Valid: n.Valid} *f = Float4{Float32: float32(n.Float64), Valid: n.Valid}
return nil return nil
} }
// Float64Value implements the [Float64Valuer] interface.
func (f Float4) Float64Value() (Float8, error) { func (f Float4) Float64Value() (Float8, error) {
return Float8{Float64: float64(f.Float32), Valid: f.Valid}, nil return Float8{Float64: float64(f.Float32), Valid: f.Valid}, nil
} }
// ScanInt64 implements the [Int64Scanner] interface.
func (f *Float4) ScanInt64(n Int8) error { func (f *Float4) ScanInt64(n Int8) error {
*f = Float4{Float32: float32(n.Int64), Valid: n.Valid} *f = Float4{Float32: float32(n.Int64), Valid: n.Valid}
return nil return nil
} }
// Int64Value implements the [Int64Valuer] interface.
func (f Float4) Int64Value() (Int8, error) { func (f Float4) Int64Value() (Int8, error) {
return Int8{Int64: int64(f.Float32), Valid: f.Valid}, nil return Int8{Int64: int64(f.Float32), Valid: f.Valid}, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (f *Float4) Scan(src any) error { func (f *Float4) Scan(src any) error {
if src == nil { if src == nil {
*f = Float4{} *f = Float4{}
@ -58,7 +61,7 @@ func (f *Float4) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (f Float4) Value() (driver.Value, error) { func (f Float4) Value() (driver.Value, error) {
if !f.Valid { if !f.Valid {
return nil, nil return nil, nil
@ -66,6 +69,7 @@ func (f Float4) Value() (driver.Value, error) {
return float64(f.Float32), nil return float64(f.Float32), nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (f Float4) MarshalJSON() ([]byte, error) { func (f Float4) MarshalJSON() ([]byte, error) {
if !f.Valid { if !f.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -73,6 +77,7 @@ func (f Float4) MarshalJSON() ([]byte, error) {
return json.Marshal(f.Float32) return json.Marshal(f.Float32)
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (f *Float4) UnmarshalJSON(b []byte) error { func (f *Float4) UnmarshalJSON(b []byte) error {
var n *float32 var n *float32
err := json.Unmarshal(b, &n) err := json.Unmarshal(b, &n)
@ -170,7 +175,6 @@ func (encodePlanFloat4CodecBinaryInt64Valuer) Encode(value any, buf []byte) (new
} }
func (Float4Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (Float4Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -24,26 +24,29 @@ type Float8 struct {
Valid bool Valid bool
} }
// ScanFloat64 implements the Float64Scanner interface. // ScanFloat64 implements the [Float64Scanner] interface.
func (f *Float8) ScanFloat64(n Float8) error { func (f *Float8) ScanFloat64(n Float8) error {
*f = n *f = n
return nil return nil
} }
// Float64Value implements the [Float64Valuer] interface.
func (f Float8) Float64Value() (Float8, error) { func (f Float8) Float64Value() (Float8, error) {
return f, nil return f, nil
} }
// ScanInt64 implements the [Int64Scanner] interface.
func (f *Float8) ScanInt64(n Int8) error { func (f *Float8) ScanInt64(n Int8) error {
*f = Float8{Float64: float64(n.Int64), Valid: n.Valid} *f = Float8{Float64: float64(n.Int64), Valid: n.Valid}
return nil return nil
} }
// Int64Value implements the [Int64Valuer] interface.
func (f Float8) Int64Value() (Int8, error) { func (f Float8) Int64Value() (Int8, error) {
return Int8{Int64: int64(f.Float64), Valid: f.Valid}, nil return Int8{Int64: int64(f.Float64), Valid: f.Valid}, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (f *Float8) Scan(src any) error { func (f *Float8) Scan(src any) error {
if src == nil { if src == nil {
*f = Float8{} *f = Float8{}
@ -66,7 +69,7 @@ func (f *Float8) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (f Float8) Value() (driver.Value, error) { func (f Float8) Value() (driver.Value, error) {
if !f.Valid { if !f.Valid {
return nil, nil return nil, nil
@ -74,6 +77,7 @@ func (f Float8) Value() (driver.Value, error) {
return f.Float64, nil return f.Float64, nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (f Float8) MarshalJSON() ([]byte, error) { func (f Float8) MarshalJSON() ([]byte, error) {
if !f.Valid { if !f.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -81,6 +85,7 @@ func (f Float8) MarshalJSON() ([]byte, error) {
return json.Marshal(f.Float64) return json.Marshal(f.Float64)
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (f *Float8) UnmarshalJSON(b []byte) error { func (f *Float8) UnmarshalJSON(b []byte) error {
var n *float64 var n *float64
err := json.Unmarshal(b, &n) err := json.Unmarshal(b, &n)
@ -208,7 +213,6 @@ func (encodePlanTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, e
} }
func (Float8Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (Float8Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -22,16 +22,18 @@ type HstoreValuer interface {
// associated with its keys. // associated with its keys.
type Hstore map[string]*string type Hstore map[string]*string
// ScanHstore implements the [HstoreScanner] interface.
func (h *Hstore) ScanHstore(v Hstore) error { func (h *Hstore) ScanHstore(v Hstore) error {
*h = v *h = v
return nil return nil
} }
// HstoreValue implements the [HstoreValuer] interface.
func (h Hstore) HstoreValue() (Hstore, error) { func (h Hstore) HstoreValue() (Hstore, error) {
return h, nil return h, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (h *Hstore) Scan(src any) error { func (h *Hstore) Scan(src any) error {
if src == nil { if src == nil {
*h = nil *h = nil
@ -46,7 +48,7 @@ func (h *Hstore) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (h Hstore) Value() (driver.Value, error) { func (h Hstore) Value() (driver.Value, error) {
if h == nil { if h == nil {
return nil, nil return nil, nil
@ -162,7 +164,6 @@ func (encodePlanHstoreCodecText) Encode(value any, buf []byte) (newBuf []byte, e
} }
func (HstoreCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (HstoreCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {
@ -298,7 +299,7 @@ func (p *hstoreParser) consume() (b byte, end bool) {
return b, false return b, false
} }
func unexpectedByteErr(actualB byte, expectedB byte) error { func unexpectedByteErr(actualB, expectedB byte) error {
return fmt.Errorf("expected '%c' ('%#v'); found '%c' ('%#v')", expectedB, expectedB, actualB, actualB) return fmt.Errorf("expected '%c' ('%#v'); found '%c' ('%#v')", expectedB, expectedB, actualB, actualB)
} }
@ -316,7 +317,7 @@ func (p *hstoreParser) consumeExpectedByte(expectedB byte) error {
// consumeExpected2 consumes two expected bytes or returns an error. // consumeExpected2 consumes two expected bytes or returns an error.
// This was a bit faster than using a string argument (better inlining? Not sure). // This was a bit faster than using a string argument (better inlining? Not sure).
func (p *hstoreParser) consumeExpected2(one byte, two byte) error { func (p *hstoreParser) consumeExpected2(one, two byte) error {
if p.pos+2 > len(p.str) { if p.pos+2 > len(p.str) {
return errors.New("unexpected end of string") return errors.New("unexpected end of string")
} }

View file

@ -24,7 +24,7 @@ type NetipPrefixValuer interface {
NetipPrefixValue() (netip.Prefix, error) NetipPrefixValue() (netip.Prefix, error)
} }
// InetCodec handles both inet and cidr PostgreSQL types. The preferred Go types are netip.Prefix and netip.Addr. If // InetCodec handles both inet and cidr PostgreSQL types. The preferred Go types are [netip.Prefix] and [netip.Addr]. If
// IsValid() is false then they are treated as SQL NULL. // IsValid() is false then they are treated as SQL NULL.
type InetCodec struct{} type InetCodec struct{}
@ -107,7 +107,6 @@ func (encodePlanInetCodecText) Encode(value any, buf []byte) (newBuf []byte, err
} }
func (InetCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (InetCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -26,7 +26,7 @@ type Int2 struct {
Valid bool Valid bool
} }
// ScanInt64 implements the Int64Scanner interface. // ScanInt64 implements the [Int64Scanner] interface.
func (dst *Int2) ScanInt64(n Int8) error { func (dst *Int2) ScanInt64(n Int8) error {
if !n.Valid { if !n.Valid {
*dst = Int2{} *dst = Int2{}
@ -44,11 +44,12 @@ func (dst *Int2) ScanInt64(n Int8) error {
return nil return nil
} }
// Int64Value implements the [Int64Valuer] interface.
func (n Int2) Int64Value() (Int8, error) { func (n Int2) Int64Value() (Int8, error) {
return Int8{Int64: int64(n.Int16), Valid: n.Valid}, nil return Int8{Int64: int64(n.Int16), Valid: n.Valid}, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Int2) Scan(src any) error { func (dst *Int2) Scan(src any) error {
if src == nil { if src == nil {
*dst = Int2{} *dst = Int2{}
@ -87,7 +88,7 @@ func (dst *Int2) Scan(src any) error {
return nil return nil
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Int2) Value() (driver.Value, error) { func (src Int2) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -95,6 +96,7 @@ func (src Int2) Value() (driver.Value, error) {
return int64(src.Int16), nil return int64(src.Int16), nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (src Int2) MarshalJSON() ([]byte, error) { func (src Int2) MarshalJSON() ([]byte, error) {
if !src.Valid { if !src.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -102,6 +104,7 @@ func (src Int2) MarshalJSON() ([]byte, error) {
return []byte(strconv.FormatInt(int64(src.Int16), 10)), nil return []byte(strconv.FormatInt(int64(src.Int16), 10)), nil
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (dst *Int2) UnmarshalJSON(b []byte) error { func (dst *Int2) UnmarshalJSON(b []byte) error {
var n *int16 var n *int16
err := json.Unmarshal(b, &n) err := json.Unmarshal(b, &n)
@ -586,7 +589,7 @@ type Int4 struct {
Valid bool Valid bool
} }
// ScanInt64 implements the Int64Scanner interface. // ScanInt64 implements the [Int64Scanner] interface.
func (dst *Int4) ScanInt64(n Int8) error { func (dst *Int4) ScanInt64(n Int8) error {
if !n.Valid { if !n.Valid {
*dst = Int4{} *dst = Int4{}
@ -604,11 +607,12 @@ func (dst *Int4) ScanInt64(n Int8) error {
return nil return nil
} }
// Int64Value implements the [Int64Valuer] interface.
func (n Int4) Int64Value() (Int8, error) { func (n Int4) Int64Value() (Int8, error) {
return Int8{Int64: int64(n.Int32), Valid: n.Valid}, nil return Int8{Int64: int64(n.Int32), Valid: n.Valid}, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Int4) Scan(src any) error { func (dst *Int4) Scan(src any) error {
if src == nil { if src == nil {
*dst = Int4{} *dst = Int4{}
@ -647,7 +651,7 @@ func (dst *Int4) Scan(src any) error {
return nil return nil
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Int4) Value() (driver.Value, error) { func (src Int4) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -655,6 +659,7 @@ func (src Int4) Value() (driver.Value, error) {
return int64(src.Int32), nil return int64(src.Int32), nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (src Int4) MarshalJSON() ([]byte, error) { func (src Int4) MarshalJSON() ([]byte, error) {
if !src.Valid { if !src.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -662,6 +667,7 @@ func (src Int4) MarshalJSON() ([]byte, error) {
return []byte(strconv.FormatInt(int64(src.Int32), 10)), nil return []byte(strconv.FormatInt(int64(src.Int32), 10)), nil
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (dst *Int4) UnmarshalJSON(b []byte) error { func (dst *Int4) UnmarshalJSON(b []byte) error {
var n *int32 var n *int32
err := json.Unmarshal(b, &n) err := json.Unmarshal(b, &n)
@ -1157,7 +1163,7 @@ type Int8 struct {
Valid bool Valid bool
} }
// ScanInt64 implements the Int64Scanner interface. // ScanInt64 implements the [Int64Scanner] interface.
func (dst *Int8) ScanInt64(n Int8) error { func (dst *Int8) ScanInt64(n Int8) error {
if !n.Valid { if !n.Valid {
*dst = Int8{} *dst = Int8{}
@ -1175,11 +1181,12 @@ func (dst *Int8) ScanInt64(n Int8) error {
return nil return nil
} }
// Int64Value implements the [Int64Valuer] interface.
func (n Int8) Int64Value() (Int8, error) { func (n Int8) Int64Value() (Int8, error) {
return Int8{Int64: int64(n.Int64), Valid: n.Valid}, nil return Int8{Int64: int64(n.Int64), Valid: n.Valid}, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Int8) Scan(src any) error { func (dst *Int8) Scan(src any) error {
if src == nil { if src == nil {
*dst = Int8{} *dst = Int8{}
@ -1218,7 +1225,7 @@ func (dst *Int8) Scan(src any) error {
return nil return nil
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Int8) Value() (driver.Value, error) { func (src Int8) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -1226,6 +1233,7 @@ func (src Int8) Value() (driver.Value, error) {
return int64(src.Int64), nil return int64(src.Int64), nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (src Int8) MarshalJSON() ([]byte, error) { func (src Int8) MarshalJSON() ([]byte, error) {
if !src.Valid { if !src.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -1233,6 +1241,7 @@ func (src Int8) MarshalJSON() ([]byte, error) {
return []byte(strconv.FormatInt(int64(src.Int64), 10)), nil return []byte(strconv.FormatInt(int64(src.Int64), 10)), nil
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (dst *Int8) UnmarshalJSON(b []byte) error { func (dst *Int8) UnmarshalJSON(b []byte) error {
var n *int64 var n *int64
err := json.Unmarshal(b, &n) err := json.Unmarshal(b, &n)

View file

@ -27,7 +27,7 @@ type Int<%= pg_byte_size %> struct {
Valid bool Valid bool
} }
// ScanInt64 implements the Int64Scanner interface. // ScanInt64 implements the [Int64Scanner] interface.
func (dst *Int<%= pg_byte_size %>) ScanInt64(n Int8) error { func (dst *Int<%= pg_byte_size %>) ScanInt64(n Int8) error {
if !n.Valid { if !n.Valid {
*dst = Int<%= pg_byte_size %>{} *dst = Int<%= pg_byte_size %>{}
@ -45,11 +45,12 @@ func (dst *Int<%= pg_byte_size %>) ScanInt64(n Int8) error {
return nil return nil
} }
// Int64Value implements the [Int64Valuer] interface.
func (n Int<%= pg_byte_size %>) Int64Value() (Int8, error) { func (n Int<%= pg_byte_size %>) Int64Value() (Int8, error) {
return Int8{Int64: int64(n.Int<%= pg_bit_size %>), Valid: n.Valid}, nil return Int8{Int64: int64(n.Int<%= pg_bit_size %>), Valid: n.Valid}, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Int<%= pg_byte_size %>) Scan(src any) error { func (dst *Int<%= pg_byte_size %>) Scan(src any) error {
if src == nil { if src == nil {
*dst = Int<%= pg_byte_size %>{} *dst = Int<%= pg_byte_size %>{}
@ -88,7 +89,7 @@ func (dst *Int<%= pg_byte_size %>) Scan(src any) error {
return nil return nil
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Int<%= pg_byte_size %>) Value() (driver.Value, error) { func (src Int<%= pg_byte_size %>) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -96,6 +97,7 @@ func (src Int<%= pg_byte_size %>) Value() (driver.Value, error) {
return int64(src.Int<%= pg_bit_size %>), nil return int64(src.Int<%= pg_bit_size %>), nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (src Int<%= pg_byte_size %>) MarshalJSON() ([]byte, error) { func (src Int<%= pg_byte_size %>) MarshalJSON() ([]byte, error) {
if !src.Valid { if !src.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -103,6 +105,7 @@ func (src Int<%= pg_byte_size %>) MarshalJSON() ([]byte, error) {
return []byte(strconv.FormatInt(int64(src.Int<%= pg_bit_size %>), 10)), nil return []byte(strconv.FormatInt(int64(src.Int<%= pg_bit_size %>), 10)), nil
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (dst *Int<%= pg_byte_size %>) UnmarshalJSON(b []byte) error { func (dst *Int<%= pg_byte_size %>) UnmarshalJSON(b []byte) error {
var n *int<%= pg_bit_size %> var n *int<%= pg_bit_size %>
err := json.Unmarshal(b, &n) err := json.Unmarshal(b, &n)

View file

@ -33,16 +33,18 @@ type Interval struct {
Valid bool Valid bool
} }
// ScanInterval implements the [IntervalScanner] interface.
func (interval *Interval) ScanInterval(v Interval) error { func (interval *Interval) ScanInterval(v Interval) error {
*interval = v *interval = v
return nil return nil
} }
// IntervalValue implements the [IntervalValuer] interface.
func (interval Interval) IntervalValue() (Interval, error) { func (interval Interval) IntervalValue() (Interval, error) {
return interval, nil return interval, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (interval *Interval) Scan(src any) error { func (interval *Interval) Scan(src any) error {
if src == nil { if src == nil {
*interval = Interval{} *interval = Interval{}
@ -57,7 +59,7 @@ func (interval *Interval) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (interval Interval) Value() (driver.Value, error) { func (interval Interval) Value() (driver.Value, error) {
if !interval.Valid { if !interval.Valid {
return nil, nil return nil, nil
@ -157,7 +159,6 @@ func (encodePlanIntervalCodecText) Encode(value any, buf []byte) (newBuf []byte,
} }
func (IntervalCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (IntervalCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -24,11 +24,13 @@ type Line struct {
Valid bool Valid bool
} }
// ScanLine implements the [LineScanner] interface.
func (line *Line) ScanLine(v Line) error { func (line *Line) ScanLine(v Line) error {
*line = v *line = v
return nil return nil
} }
// LineValue implements the [LineValuer] interface.
func (line Line) LineValue() (Line, error) { func (line Line) LineValue() (Line, error) {
return line, nil return line, nil
} }
@ -37,7 +39,7 @@ func (line *Line) Set(src any) error {
return fmt.Errorf("cannot convert %v to Line", src) return fmt.Errorf("cannot convert %v to Line", src)
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (line *Line) Scan(src any) error { func (line *Line) Scan(src any) error {
if src == nil { if src == nil {
*line = Line{} *line = Line{}
@ -52,7 +54,7 @@ func (line *Line) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (line Line) Value() (driver.Value, error) { func (line Line) Value() (driver.Value, error) {
if !line.Valid { if !line.Valid {
return nil, nil return nil, nil
@ -129,7 +131,6 @@ func (encodePlanLineCodecText) Encode(value any, buf []byte) (newBuf []byte, err
} }
func (LineCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (LineCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -24,16 +24,18 @@ type Lseg struct {
Valid bool Valid bool
} }
// ScanLseg implements the [LsegScanner] interface.
func (lseg *Lseg) ScanLseg(v Lseg) error { func (lseg *Lseg) ScanLseg(v Lseg) error {
*lseg = v *lseg = v
return nil return nil
} }
// LsegValue implements the [LsegValuer] interface.
func (lseg Lseg) LsegValue() (Lseg, error) { func (lseg Lseg) LsegValue() (Lseg, error) {
return lseg, nil return lseg, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (lseg *Lseg) Scan(src any) error { func (lseg *Lseg) Scan(src any) error {
if src == nil { if src == nil {
*lseg = Lseg{} *lseg = Lseg{}
@ -48,7 +50,7 @@ func (lseg *Lseg) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (lseg Lseg) Value() (driver.Value, error) { func (lseg Lseg) Value() (driver.Value, error) {
if !lseg.Valid { if !lseg.Valid {
return nil, nil return nil, nil
@ -127,7 +129,6 @@ func (encodePlanLsegCodecText) Encode(value any, buf []byte) (newBuf []byte, err
} }
func (LsegCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (LsegCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -374,7 +374,6 @@ parseValueLoop:
} }
return elements, nil return elements, nil
} }
func parseRange(buf *bytes.Buffer) (string, error) { func parseRange(buf *bytes.Buffer) (string, error) {
@ -403,8 +402,8 @@ func parseRange(buf *bytes.Buffer) (string, error) {
// Multirange is a generic multirange type. // Multirange is a generic multirange type.
// //
// T should implement RangeValuer and *T should implement RangeScanner. However, there does not appear to be a way to // T should implement [RangeValuer] and *T should implement [RangeScanner]. However, there does not appear to be a way to
// enforce the RangeScanner constraint. // enforce the [RangeScanner] constraint.
type Multirange[T RangeValuer] []T type Multirange[T RangeValuer] []T
func (r Multirange[T]) IsNull() bool { func (r Multirange[T]) IsNull() bool {

View file

@ -27,16 +27,20 @@ const (
pgNumericNegInfSign = 0xf000 pgNumericNegInfSign = 0xf000
) )
var big0 *big.Int = big.NewInt(0) var (
var big1 *big.Int = big.NewInt(1) big0 *big.Int = big.NewInt(0)
var big10 *big.Int = big.NewInt(10) big1 *big.Int = big.NewInt(1)
var big100 *big.Int = big.NewInt(100) big10 *big.Int = big.NewInt(10)
var big1000 *big.Int = big.NewInt(1000) big100 *big.Int = big.NewInt(100)
big1000 *big.Int = big.NewInt(1000)
)
var bigNBase *big.Int = big.NewInt(nbase) var (
var bigNBaseX2 *big.Int = big.NewInt(nbase * nbase) bigNBase *big.Int = big.NewInt(nbase)
var bigNBaseX3 *big.Int = big.NewInt(nbase * nbase * nbase) bigNBaseX2 *big.Int = big.NewInt(nbase * nbase)
var bigNBaseX4 *big.Int = big.NewInt(nbase * nbase * nbase * nbase) bigNBaseX3 *big.Int = big.NewInt(nbase * nbase * nbase)
bigNBaseX4 *big.Int = big.NewInt(nbase * nbase * nbase * nbase)
)
type NumericScanner interface { type NumericScanner interface {
ScanNumeric(v Numeric) error ScanNumeric(v Numeric) error
@ -54,15 +58,18 @@ type Numeric struct {
Valid bool Valid bool
} }
// ScanNumeric implements the [NumericScanner] interface.
func (n *Numeric) ScanNumeric(v Numeric) error { func (n *Numeric) ScanNumeric(v Numeric) error {
*n = v *n = v
return nil return nil
} }
// NumericValue implements the [NumericValuer] interface.
func (n Numeric) NumericValue() (Numeric, error) { func (n Numeric) NumericValue() (Numeric, error) {
return n, nil return n, nil
} }
// Float64Value implements the [Float64Valuer] interface.
func (n Numeric) Float64Value() (Float8, error) { func (n Numeric) Float64Value() (Float8, error) {
if !n.Valid { if !n.Valid {
return Float8{}, nil return Float8{}, nil
@ -92,6 +99,7 @@ func (n Numeric) Float64Value() (Float8, error) {
return Float8{Float64: f, Valid: true}, nil return Float8{Float64: f, Valid: true}, nil
} }
// ScanInt64 implements the [Int64Scanner] interface.
func (n *Numeric) ScanInt64(v Int8) error { func (n *Numeric) ScanInt64(v Int8) error {
if !v.Valid { if !v.Valid {
*n = Numeric{} *n = Numeric{}
@ -102,6 +110,7 @@ func (n *Numeric) ScanInt64(v Int8) error {
return nil return nil
} }
// Int64Value implements the [Int64Valuer] interface.
func (n Numeric) Int64Value() (Int8, error) { func (n Numeric) Int64Value() (Int8, error) {
if !n.Valid { if !n.Valid {
return Int8{}, nil return Int8{}, nil
@ -203,7 +212,7 @@ func nbaseDigitsToInt64(src []byte) (accum int64, bytesRead, digitsRead int) {
return accum, rp, digits return accum, rp, digits
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (n *Numeric) Scan(src any) error { func (n *Numeric) Scan(src any) error {
if src == nil { if src == nil {
*n = Numeric{} *n = Numeric{}
@ -218,7 +227,7 @@ func (n *Numeric) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (n Numeric) Value() (driver.Value, error) { func (n Numeric) Value() (driver.Value, error) {
if !n.Valid { if !n.Valid {
return nil, nil return nil, nil
@ -231,6 +240,7 @@ func (n Numeric) Value() (driver.Value, error) {
return string(buf), err return string(buf), err
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (n Numeric) MarshalJSON() ([]byte, error) { func (n Numeric) MarshalJSON() ([]byte, error) {
if !n.Valid { if !n.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -243,6 +253,7 @@ func (n Numeric) MarshalJSON() ([]byte, error) {
return n.numberTextBytes(), nil return n.numberTextBytes(), nil
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (n *Numeric) UnmarshalJSON(src []byte) error { func (n *Numeric) UnmarshalJSON(src []byte) error {
if bytes.Equal(src, []byte(`null`)) { if bytes.Equal(src, []byte(`null`)) {
*n = Numeric{} *n = Numeric{}
@ -553,7 +564,6 @@ func encodeNumericText(n Numeric, buf []byte) (newBuf []byte, err error) {
} }
func (NumericCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (NumericCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -25,16 +25,18 @@ type Path struct {
Valid bool Valid bool
} }
// ScanPath implements the [PathScanner] interface.
func (path *Path) ScanPath(v Path) error { func (path *Path) ScanPath(v Path) error {
*path = v *path = v
return nil return nil
} }
// PathValue implements the [PathValuer] interface.
func (path Path) PathValue() (Path, error) { func (path Path) PathValue() (Path, error) {
return path, nil return path, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (path *Path) Scan(src any) error { func (path *Path) Scan(src any) error {
if src == nil { if src == nil {
*path = Path{} *path = Path{}
@ -49,7 +51,7 @@ func (path *Path) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (path Path) Value() (driver.Value, error) { func (path Path) Value() (driver.Value, error) {
if !path.Valid { if !path.Valid {
return nil, nil return nil, nil
@ -154,7 +156,6 @@ func (encodePlanPathCodecText) Encode(value any, buf []byte) (newBuf []byte, err
} }
func (PathCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (PathCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -2010,7 +2010,7 @@ var valuerReflectType = reflect.TypeFor[driver.Valuer]()
// isNilDriverValuer returns true if value is any type of nil unless it implements driver.Valuer. *T is not considered to implement // isNilDriverValuer returns true if value is any type of nil unless it implements driver.Valuer. *T is not considered to implement
// driver.Valuer if it is only implemented by T. // driver.Valuer if it is only implemented by T.
func isNilDriverValuer(value any) (isNil bool, callNilDriverValuer bool) { func isNilDriverValuer(value any) (isNil, callNilDriverValuer bool) {
if value == nil { if value == nil {
return true, false return true, false
} }

View file

@ -30,11 +30,13 @@ type Point struct {
Valid bool Valid bool
} }
// ScanPoint implements the [PointScanner] interface.
func (p *Point) ScanPoint(v Point) error { func (p *Point) ScanPoint(v Point) error {
*p = v *p = v
return nil return nil
} }
// PointValue implements the [PointValuer] interface.
func (p Point) PointValue() (Point, error) { func (p Point) PointValue() (Point, error) {
return p, nil return p, nil
} }
@ -68,7 +70,7 @@ func parsePoint(src []byte) (*Point, error) {
return &Point{P: Vec2{x, y}, Valid: true}, nil return &Point{P: Vec2{x, y}, Valid: true}, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Point) Scan(src any) error { func (dst *Point) Scan(src any) error {
if src == nil { if src == nil {
*dst = Point{} *dst = Point{}
@ -83,7 +85,7 @@ func (dst *Point) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Point) Value() (driver.Value, error) { func (src Point) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -96,6 +98,7 @@ func (src Point) Value() (driver.Value, error) {
return string(buf), err return string(buf), err
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (src Point) MarshalJSON() ([]byte, error) { func (src Point) MarshalJSON() ([]byte, error) {
if !src.Valid { if !src.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -108,6 +111,7 @@ func (src Point) MarshalJSON() ([]byte, error) {
return buff.Bytes(), nil return buff.Bytes(), nil
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (dst *Point) UnmarshalJSON(point []byte) error { func (dst *Point) UnmarshalJSON(point []byte) error {
p, err := parsePoint(point) p, err := parsePoint(point)
if err != nil { if err != nil {
@ -178,7 +182,6 @@ func (encodePlanPointCodecText) Encode(value any, buf []byte) (newBuf []byte, er
} }
func (PointCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (PointCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -24,16 +24,18 @@ type Polygon struct {
Valid bool Valid bool
} }
// ScanPolygon implements the [PolygonScanner] interface.
func (p *Polygon) ScanPolygon(v Polygon) error { func (p *Polygon) ScanPolygon(v Polygon) error {
*p = v *p = v
return nil return nil
} }
// PolygonValue implements the [PolygonValuer] interface.
func (p Polygon) PolygonValue() (Polygon, error) { func (p Polygon) PolygonValue() (Polygon, error) {
return p, nil return p, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (p *Polygon) Scan(src any) error { func (p *Polygon) Scan(src any) error {
if src == nil { if src == nil {
*p = Polygon{} *p = Polygon{}
@ -48,7 +50,7 @@ func (p *Polygon) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (p Polygon) Value() (driver.Value, error) { func (p Polygon) Value() (driver.Value, error) {
if !p.Valid { if !p.Valid {
return nil, nil return nil, nil
@ -139,7 +141,6 @@ func (encodePlanPolygonCodecText) Encode(value any, buf []byte) (newBuf []byte,
} }
func (PolygonCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (PolygonCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -191,11 +191,13 @@ type untypedBinaryRange struct {
// 18 = [ = 10010 // 18 = [ = 10010
// 24 = = 11000 // 24 = = 11000
const emptyMask = 1 const (
const lowerInclusiveMask = 2 emptyMask = 1
const upperInclusiveMask = 4 lowerInclusiveMask = 2
const lowerUnboundedMask = 8 upperInclusiveMask = 4
const upperUnboundedMask = 16 lowerUnboundedMask = 8
upperUnboundedMask = 16
)
func parseUntypedBinaryRange(src []byte) (*untypedBinaryRange, error) { func parseUntypedBinaryRange(src []byte) (*untypedBinaryRange, error) {
ubr := &untypedBinaryRange{} ubr := &untypedBinaryRange{}
@ -273,7 +275,6 @@ func parseUntypedBinaryRange(src []byte) (*untypedBinaryRange, error) {
} }
return ubr, nil return ubr, nil
} }
// Range is a generic range type. // Range is a generic range type.

View file

@ -121,5 +121,4 @@ func (RecordCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (an
default: default:
return nil, fmt.Errorf("unknown format code %d", format) return nil, fmt.Errorf("unknown format code %d", format)
} }
} }

View file

@ -19,16 +19,18 @@ type Text struct {
Valid bool Valid bool
} }
// ScanText implements the [TextScanner] interface.
func (t *Text) ScanText(v Text) error { func (t *Text) ScanText(v Text) error {
*t = v *t = v
return nil return nil
} }
// TextValue implements the [TextValuer] interface.
func (t Text) TextValue() (Text, error) { func (t Text) TextValue() (Text, error) {
return t, nil return t, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Text) Scan(src any) error { func (dst *Text) Scan(src any) error {
if src == nil { if src == nil {
*dst = Text{} *dst = Text{}
@ -47,7 +49,7 @@ func (dst *Text) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Text) Value() (driver.Value, error) { func (src Text) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -55,6 +57,7 @@ func (src Text) Value() (driver.Value, error) {
return src.String, nil return src.String, nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (src Text) MarshalJSON() ([]byte, error) { func (src Text) MarshalJSON() ([]byte, error) {
if !src.Valid { if !src.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -63,6 +66,7 @@ func (src Text) MarshalJSON() ([]byte, error) {
return json.Marshal(src.String) return json.Marshal(src.String)
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (dst *Text) UnmarshalJSON(b []byte) error { func (dst *Text) UnmarshalJSON(b []byte) error {
var s *string var s *string
err := json.Unmarshal(b, &s) err := json.Unmarshal(b, &s)
@ -146,7 +150,6 @@ func (encodePlanTextCodecTextValuer) Encode(value any, buf []byte) (newBuf []byt
} }
func (TextCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (TextCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case TextFormatCode, BinaryFormatCode: case TextFormatCode, BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -35,16 +35,18 @@ type TID struct {
Valid bool Valid bool
} }
// ScanTID implements the [TIDScanner] interface.
func (b *TID) ScanTID(v TID) error { func (b *TID) ScanTID(v TID) error {
*b = v *b = v
return nil return nil
} }
// TIDValue implements the [TIDValuer] interface.
func (b TID) TIDValue() (TID, error) { func (b TID) TIDValue() (TID, error) {
return b, nil return b, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *TID) Scan(src any) error { func (dst *TID) Scan(src any) error {
if src == nil { if src == nil {
*dst = TID{} *dst = TID{}
@ -59,7 +61,7 @@ func (dst *TID) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src TID) Value() (driver.Value, error) { func (src TID) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -131,7 +133,6 @@ func (encodePlanTIDCodecText) Encode(value any, buf []byte) (newBuf []byte, err
} }
func (TIDCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (TIDCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -29,16 +29,18 @@ type Time struct {
Valid bool Valid bool
} }
// ScanTime implements the [TimeScanner] interface.
func (t *Time) ScanTime(v Time) error { func (t *Time) ScanTime(v Time) error {
*t = v *t = v
return nil return nil
} }
// TimeValue implements the [TimeValuer] interface.
func (t Time) TimeValue() (Time, error) { func (t Time) TimeValue() (Time, error) {
return t, nil return t, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (t *Time) Scan(src any) error { func (t *Time) Scan(src any) error {
if src == nil { if src == nil {
*t = Time{} *t = Time{}
@ -58,7 +60,7 @@ func (t *Time) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (t Time) Value() (driver.Value, error) { func (t Time) Value() (driver.Value, error) {
if !t.Valid { if !t.Valid {
return nil, nil return nil, nil
@ -137,7 +139,6 @@ func (encodePlanTimeCodecText) Encode(value any, buf []byte) (newBuf []byte, err
} }
func (TimeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (TimeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -11,8 +11,10 @@ import (
"github.com/jackc/pgx/v5/internal/pgio" "github.com/jackc/pgx/v5/internal/pgio"
) )
const pgTimestampFormat = "2006-01-02 15:04:05.999999999" const (
const jsonISO8601 = "2006-01-02T15:04:05.999999999" pgTimestampFormat = "2006-01-02 15:04:05.999999999"
jsonISO8601 = "2006-01-02T15:04:05.999999999"
)
type TimestampScanner interface { type TimestampScanner interface {
ScanTimestamp(v Timestamp) error ScanTimestamp(v Timestamp) error
@ -29,16 +31,18 @@ type Timestamp struct {
Valid bool Valid bool
} }
// ScanTimestamp implements the [TimestampScanner] interface.
func (ts *Timestamp) ScanTimestamp(v Timestamp) error { func (ts *Timestamp) ScanTimestamp(v Timestamp) error {
*ts = v *ts = v
return nil return nil
} }
// TimestampValue implements the [TimestampValuer] interface.
func (ts Timestamp) TimestampValue() (Timestamp, error) { func (ts Timestamp) TimestampValue() (Timestamp, error) {
return ts, nil return ts, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (ts *Timestamp) Scan(src any) error { func (ts *Timestamp) Scan(src any) error {
if src == nil { if src == nil {
*ts = Timestamp{} *ts = Timestamp{}
@ -56,7 +60,7 @@ func (ts *Timestamp) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (ts Timestamp) Value() (driver.Value, error) { func (ts Timestamp) Value() (driver.Value, error) {
if !ts.Valid { if !ts.Valid {
return nil, nil return nil, nil
@ -68,6 +72,7 @@ func (ts Timestamp) Value() (driver.Value, error) {
return ts.Time, nil return ts.Time, nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (ts Timestamp) MarshalJSON() ([]byte, error) { func (ts Timestamp) MarshalJSON() ([]byte, error) {
if !ts.Valid { if !ts.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -87,6 +92,7 @@ func (ts Timestamp) MarshalJSON() ([]byte, error) {
return json.Marshal(s) return json.Marshal(s)
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (ts *Timestamp) UnmarshalJSON(b []byte) error { func (ts *Timestamp) UnmarshalJSON(b []byte) error {
var s *string var s *string
err := json.Unmarshal(b, &s) err := json.Unmarshal(b, &s)

View file

@ -11,10 +11,12 @@ import (
"github.com/jackc/pgx/v5/internal/pgio" "github.com/jackc/pgx/v5/internal/pgio"
) )
const pgTimestamptzHourFormat = "2006-01-02 15:04:05.999999999Z07" const (
const pgTimestamptzMinuteFormat = "2006-01-02 15:04:05.999999999Z07:00" pgTimestamptzHourFormat = "2006-01-02 15:04:05.999999999Z07"
const pgTimestamptzSecondFormat = "2006-01-02 15:04:05.999999999Z07:00:00" pgTimestamptzMinuteFormat = "2006-01-02 15:04:05.999999999Z07:00"
const microsecFromUnixEpochToY2K = 946684800 * 1000000 pgTimestamptzSecondFormat = "2006-01-02 15:04:05.999999999Z07:00:00"
microsecFromUnixEpochToY2K = 946684800 * 1000000
)
const ( const (
negativeInfinityMicrosecondOffset = -9223372036854775808 negativeInfinityMicrosecondOffset = -9223372036854775808
@ -36,16 +38,18 @@ type Timestamptz struct {
Valid bool Valid bool
} }
// ScanTimestamptz implements the [TimestamptzScanner] interface.
func (tstz *Timestamptz) ScanTimestamptz(v Timestamptz) error { func (tstz *Timestamptz) ScanTimestamptz(v Timestamptz) error {
*tstz = v *tstz = v
return nil return nil
} }
// TimestamptzValue implements the [TimestamptzValuer] interface.
func (tstz Timestamptz) TimestamptzValue() (Timestamptz, error) { func (tstz Timestamptz) TimestamptzValue() (Timestamptz, error) {
return tstz, nil return tstz, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (tstz *Timestamptz) Scan(src any) error { func (tstz *Timestamptz) Scan(src any) error {
if src == nil { if src == nil {
*tstz = Timestamptz{} *tstz = Timestamptz{}
@ -63,7 +67,7 @@ func (tstz *Timestamptz) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (tstz Timestamptz) Value() (driver.Value, error) { func (tstz Timestamptz) Value() (driver.Value, error) {
if !tstz.Valid { if !tstz.Valid {
return nil, nil return nil, nil
@ -75,6 +79,7 @@ func (tstz Timestamptz) Value() (driver.Value, error) {
return tstz.Time, nil return tstz.Time, nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (tstz Timestamptz) MarshalJSON() ([]byte, error) { func (tstz Timestamptz) MarshalJSON() ([]byte, error) {
if !tstz.Valid { if !tstz.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -94,6 +99,7 @@ func (tstz Timestamptz) MarshalJSON() ([]byte, error) {
return json.Marshal(s) return json.Marshal(s)
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (tstz *Timestamptz) UnmarshalJSON(b []byte) error { func (tstz *Timestamptz) UnmarshalJSON(b []byte) error {
var s *string var s *string
err := json.Unmarshal(b, &s) err := json.Unmarshal(b, &s)
@ -225,7 +231,6 @@ func (encodePlanTimestamptzCodecText) Encode(value any, buf []byte) (newBuf []by
} }
func (c *TimestamptzCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (c *TimestamptzCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -3,6 +3,7 @@ package pgtype
import ( import (
"database/sql/driver" "database/sql/driver"
"encoding/binary" "encoding/binary"
"encoding/json"
"fmt" "fmt"
"math" "math"
"strconv" "strconv"
@ -24,16 +25,18 @@ type Uint32 struct {
Valid bool Valid bool
} }
// ScanUint32 implements the [Uint32Scanner] interface.
func (n *Uint32) ScanUint32(v Uint32) error { func (n *Uint32) ScanUint32(v Uint32) error {
*n = v *n = v
return nil return nil
} }
// Uint32Value implements the [Uint32Valuer] interface.
func (n Uint32) Uint32Value() (Uint32, error) { func (n Uint32) Uint32Value() (Uint32, error) {
return n, nil return n, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Uint32) Scan(src any) error { func (dst *Uint32) Scan(src any) error {
if src == nil { if src == nil {
*dst = Uint32{} *dst = Uint32{}
@ -67,7 +70,7 @@ func (dst *Uint32) Scan(src any) error {
return nil return nil
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Uint32) Value() (driver.Value, error) { func (src Uint32) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -75,6 +78,31 @@ func (src Uint32) Value() (driver.Value, error) {
return int64(src.Uint32), nil return int64(src.Uint32), nil
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (src Uint32) MarshalJSON() ([]byte, error) {
if !src.Valid {
return []byte("null"), nil
}
return json.Marshal(src.Uint32)
}
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (dst *Uint32) UnmarshalJSON(b []byte) error {
var n *uint32
err := json.Unmarshal(b, &n)
if err != nil {
return err
}
if n == nil {
*dst = Uint32{}
} else {
*dst = Uint32{Uint32: *n, Valid: true}
}
return nil
}
type Uint32Codec struct{} type Uint32Codec struct{}
func (Uint32Codec) FormatSupported(format int16) bool { func (Uint32Codec) FormatSupported(format int16) bool {
@ -197,7 +225,6 @@ func (encodePlanUint32CodecTextInt64Valuer) Encode(value any, buf []byte) (newBu
} }
func (Uint32Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (Uint32Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -24,16 +24,18 @@ type Uint64 struct {
Valid bool Valid bool
} }
// ScanUint64 implements the [Uint64Scanner] interface.
func (n *Uint64) ScanUint64(v Uint64) error { func (n *Uint64) ScanUint64(v Uint64) error {
*n = v *n = v
return nil return nil
} }
// Uint64Value implements the [Uint64Valuer] interface.
func (n Uint64) Uint64Value() (Uint64, error) { func (n Uint64) Uint64Value() (Uint64, error) {
return n, nil return n, nil
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *Uint64) Scan(src any) error { func (dst *Uint64) Scan(src any) error {
if src == nil { if src == nil {
*dst = Uint64{} *dst = Uint64{}
@ -63,7 +65,7 @@ func (dst *Uint64) Scan(src any) error {
return nil return nil
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src Uint64) Value() (driver.Value, error) { func (src Uint64) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -194,7 +196,6 @@ func (encodePlanUint64CodecTextInt64Valuer) Encode(value any, buf []byte) (newBu
} }
func (Uint64Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { func (Uint64Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format { switch format {
case BinaryFormatCode: case BinaryFormatCode:
switch target.(type) { switch target.(type) {

View file

@ -20,11 +20,13 @@ type UUID struct {
Valid bool Valid bool
} }
// ScanUUID implements the [UUIDScanner] interface.
func (b *UUID) ScanUUID(v UUID) error { func (b *UUID) ScanUUID(v UUID) error {
*b = v *b = v
return nil return nil
} }
// UUIDValue implements the [UUIDValuer] interface.
func (b UUID) UUIDValue() (UUID, error) { func (b UUID) UUIDValue() (UUID, error) {
return b, nil return b, nil
} }
@ -67,7 +69,7 @@ func encodeUUID(src [16]byte) string {
return string(buf[:]) return string(buf[:])
} }
// Scan implements the database/sql Scanner interface. // Scan implements the [database/sql.Scanner] interface.
func (dst *UUID) Scan(src any) error { func (dst *UUID) Scan(src any) error {
if src == nil { if src == nil {
*dst = UUID{} *dst = UUID{}
@ -87,7 +89,7 @@ func (dst *UUID) Scan(src any) error {
return fmt.Errorf("cannot scan %T", src) return fmt.Errorf("cannot scan %T", src)
} }
// Value implements the database/sql/driver Valuer interface. // Value implements the [database/sql/driver.Valuer] interface.
func (src UUID) Value() (driver.Value, error) { func (src UUID) Value() (driver.Value, error) {
if !src.Valid { if !src.Valid {
return nil, nil return nil, nil
@ -104,6 +106,7 @@ func (src UUID) String() string {
return encodeUUID(src.Bytes) return encodeUUID(src.Bytes)
} }
// MarshalJSON implements the [encoding/json.Marshaler] interface.
func (src UUID) MarshalJSON() ([]byte, error) { func (src UUID) MarshalJSON() ([]byte, error) {
if !src.Valid { if !src.Valid {
return []byte("null"), nil return []byte("null"), nil
@ -116,6 +119,7 @@ func (src UUID) MarshalJSON() ([]byte, error) {
return buff.Bytes(), nil return buff.Bytes(), nil
} }
// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func (dst *UUID) UnmarshalJSON(src []byte) error { func (dst *UUID) UnmarshalJSON(src []byte) error {
if bytes.Equal(src, []byte("null")) { if bytes.Equal(src, []byte("null")) {
*dst = UUID{} *dst = UUID{}

View file

@ -2,7 +2,7 @@ package pgxpool
import ( import (
"context" "context"
"fmt" "errors"
"math/rand" "math/rand"
"runtime" "runtime"
"strconv" "strconv"
@ -15,12 +15,14 @@ import (
"github.com/jackc/puddle/v2" "github.com/jackc/puddle/v2"
) )
var defaultMaxConns = int32(4) var (
var defaultMinConns = int32(0) defaultMaxConns = int32(4)
var defaultMinIdleConns = int32(0) defaultMinConns = int32(0)
var defaultMaxConnLifetime = time.Hour defaultMinIdleConns = int32(0)
var defaultMaxConnIdleTime = time.Minute * 30 defaultMaxConnLifetime = time.Hour
var defaultHealthCheckPeriod = time.Minute defaultMaxConnIdleTime = time.Minute * 30
defaultHealthCheckPeriod = time.Minute
)
type connResource struct { type connResource struct {
conn *pgx.Conn conn *pgx.Conn
@ -84,9 +86,10 @@ type Pool struct {
config *Config config *Config
beforeConnect func(context.Context, *pgx.ConnConfig) error beforeConnect func(context.Context, *pgx.ConnConfig) error
afterConnect func(context.Context, *pgx.Conn) error afterConnect func(context.Context, *pgx.Conn) error
beforeAcquire func(context.Context, *pgx.Conn) bool prepareConn func(context.Context, *pgx.Conn) (bool, error)
afterRelease func(*pgx.Conn) bool afterRelease func(*pgx.Conn) bool
beforeClose func(*pgx.Conn) beforeClose func(*pgx.Conn)
shouldPing func(context.Context, ShouldPingParams) bool
minConns int32 minConns int32
minIdleConns int32 minIdleConns int32
maxConns int32 maxConns int32
@ -104,6 +107,12 @@ type Pool struct {
closeChan chan struct{} closeChan chan struct{}
} }
// ShouldPingParams are the parameters passed to ShouldPing.
type ShouldPingParams struct {
Conn *pgx.Conn
IdleDuration time.Duration
}
// Config is the configuration struct for creating a pool. It must be created by [ParseConfig] and then it can be // Config is the configuration struct for creating a pool. It must be created by [ParseConfig] and then it can be
// modified. // modified.
type Config struct { type Config struct {
@ -119,8 +128,23 @@ type Config struct {
// BeforeAcquire is called before a connection is acquired from the pool. It must return true to allow the // BeforeAcquire is called before a connection is acquired from the pool. It must return true to allow the
// acquisition or false to indicate that the connection should be destroyed and a different connection should be // acquisition or false to indicate that the connection should be destroyed and a different connection should be
// acquired. // acquired.
//
// Deprecated: Use PrepareConn instead. If both PrepareConn and BeforeAcquire are set, PrepareConn will take
// precedence, ignoring BeforeAcquire.
BeforeAcquire func(context.Context, *pgx.Conn) bool BeforeAcquire func(context.Context, *pgx.Conn) bool
// PrepareConn is called before a connection is acquired from the pool. If this function returns true, the connection
// is considered valid, otherwise the connection is destroyed. If the function returns a non-nil error, the instigating
// query will fail with the returned error.
//
// Specifically, this means that:
//
// - If it returns true and a nil error, the query proceeds as normal.
// - If it returns true and an error, the connection will be returned to the pool, and the instigating query will fail with the returned error.
// - If it returns false, and an error, the connection will be destroyed, and the query will fail with the returned error.
// - If it returns false and a nil error, the connection will be destroyed, and the instigating query will be retried on a new connection.
PrepareConn func(context.Context, *pgx.Conn) (bool, error)
// AfterRelease is called after a connection is released, but before it is returned to the pool. It must return true to // AfterRelease is called after a connection is released, but before it is returned to the pool. It must return true to
// return the connection to the pool or false to destroy the connection. // return the connection to the pool or false to destroy the connection.
AfterRelease func(*pgx.Conn) bool AfterRelease func(*pgx.Conn) bool
@ -128,6 +152,10 @@ type Config struct {
// BeforeClose is called right before a connection is closed and removed from the pool. // BeforeClose is called right before a connection is closed and removed from the pool.
BeforeClose func(*pgx.Conn) BeforeClose func(*pgx.Conn)
// ShouldPing is called after a connection is acquired from the pool. If it returns true, the connection is pinged to check for liveness.
// If this func is not set, the default behavior is to ping connections that have been idle for at least 1 second.
ShouldPing func(context.Context, ShouldPingParams) bool
// MaxConnLifetime is the duration since creation after which a connection will be automatically closed. // MaxConnLifetime is the duration since creation after which a connection will be automatically closed.
MaxConnLifetime time.Duration MaxConnLifetime time.Duration
@ -190,11 +218,18 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
panic("config must be created by ParseConfig") panic("config must be created by ParseConfig")
} }
prepareConn := config.PrepareConn
if prepareConn == nil && config.BeforeAcquire != nil {
prepareConn = func(ctx context.Context, conn *pgx.Conn) (bool, error) {
return config.BeforeAcquire(ctx, conn), nil
}
}
p := &Pool{ p := &Pool{
config: config, config: config,
beforeConnect: config.BeforeConnect, beforeConnect: config.BeforeConnect,
afterConnect: config.AfterConnect, afterConnect: config.AfterConnect,
beforeAcquire: config.BeforeAcquire, prepareConn: prepareConn,
afterRelease: config.AfterRelease, afterRelease: config.AfterRelease,
beforeClose: config.BeforeClose, beforeClose: config.BeforeClose,
minConns: config.MinConns, minConns: config.MinConns,
@ -216,6 +251,14 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
p.releaseTracer = t p.releaseTracer = t
} }
if config.ShouldPing != nil {
p.shouldPing = config.ShouldPing
} else {
p.shouldPing = func(ctx context.Context, params ShouldPingParams) bool {
return params.IdleDuration > time.Second
}
}
var err error var err error
p.p, err = puddle.NewPool( p.p, err = puddle.NewPool(
&puddle.Config[*connResource]{ &puddle.Config[*connResource]{
@ -321,10 +364,10 @@ func ParseConfig(connString string) (*Config, error) {
delete(connConfig.Config.RuntimeParams, "pool_max_conns") delete(connConfig.Config.RuntimeParams, "pool_max_conns")
n, err := strconv.ParseInt(s, 10, 32) n, err := strconv.ParseInt(s, 10, 32)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot parse pool_max_conns: %w", err) return nil, pgconn.NewParseConfigError(connString, "cannot parse pool_max_conns", err)
} }
if n < 1 { if n < 1 {
return nil, fmt.Errorf("pool_max_conns too small: %d", n) return nil, pgconn.NewParseConfigError(connString, "pool_max_conns too small", err)
} }
config.MaxConns = int32(n) config.MaxConns = int32(n)
} else { } else {
@ -338,7 +381,7 @@ func ParseConfig(connString string) (*Config, error) {
delete(connConfig.Config.RuntimeParams, "pool_min_conns") delete(connConfig.Config.RuntimeParams, "pool_min_conns")
n, err := strconv.ParseInt(s, 10, 32) n, err := strconv.ParseInt(s, 10, 32)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot parse pool_min_conns: %w", err) return nil, pgconn.NewParseConfigError(connString, "cannot parse pool_min_conns", err)
} }
config.MinConns = int32(n) config.MinConns = int32(n)
} else { } else {
@ -349,7 +392,7 @@ func ParseConfig(connString string) (*Config, error) {
delete(connConfig.Config.RuntimeParams, "pool_min_idle_conns") delete(connConfig.Config.RuntimeParams, "pool_min_idle_conns")
n, err := strconv.ParseInt(s, 10, 32) n, err := strconv.ParseInt(s, 10, 32)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot parse pool_min_idle_conns: %w", err) return nil, pgconn.NewParseConfigError(connString, "cannot parse pool_min_idle_conns", err)
} }
config.MinIdleConns = int32(n) config.MinIdleConns = int32(n)
} else { } else {
@ -360,7 +403,7 @@ func ParseConfig(connString string) (*Config, error) {
delete(connConfig.Config.RuntimeParams, "pool_max_conn_lifetime") delete(connConfig.Config.RuntimeParams, "pool_max_conn_lifetime")
d, err := time.ParseDuration(s) d, err := time.ParseDuration(s)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid pool_max_conn_lifetime: %w", err) return nil, pgconn.NewParseConfigError(connString, "cannot parse pool_max_conn_lifetime", err)
} }
config.MaxConnLifetime = d config.MaxConnLifetime = d
} else { } else {
@ -371,7 +414,7 @@ func ParseConfig(connString string) (*Config, error) {
delete(connConfig.Config.RuntimeParams, "pool_max_conn_idle_time") delete(connConfig.Config.RuntimeParams, "pool_max_conn_idle_time")
d, err := time.ParseDuration(s) d, err := time.ParseDuration(s)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid pool_max_conn_idle_time: %w", err) return nil, pgconn.NewParseConfigError(connString, "cannot parse pool_max_conn_idle_time", err)
} }
config.MaxConnIdleTime = d config.MaxConnIdleTime = d
} else { } else {
@ -382,7 +425,7 @@ func ParseConfig(connString string) (*Config, error) {
delete(connConfig.Config.RuntimeParams, "pool_health_check_period") delete(connConfig.Config.RuntimeParams, "pool_health_check_period")
d, err := time.ParseDuration(s) d, err := time.ParseDuration(s)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid pool_health_check_period: %w", err) return nil, pgconn.NewParseConfigError(connString, "cannot parse pool_health_check_period", err)
} }
config.HealthCheckPeriod = d config.HealthCheckPeriod = d
} else { } else {
@ -393,7 +436,7 @@ func ParseConfig(connString string) (*Config, error) {
delete(connConfig.Config.RuntimeParams, "pool_max_conn_lifetime_jitter") delete(connConfig.Config.RuntimeParams, "pool_max_conn_lifetime_jitter")
d, err := time.ParseDuration(s) d, err := time.ParseDuration(s)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid pool_max_conn_lifetime_jitter: %w", err) return nil, pgconn.NewParseConfigError(connString, "cannot parse pool_max_conn_lifetime_jitter", err)
} }
config.MaxConnLifetimeJitter = d config.MaxConnLifetimeJitter = d
} }
@ -545,7 +588,10 @@ func (p *Pool) Acquire(ctx context.Context) (c *Conn, err error) {
}() }()
} }
for { // Try to acquire from the connection pool up to maxConns + 1 times, so that
// any that fatal errors would empty the pool and still at least try 1 fresh
// connection.
for range p.maxConns + 1 {
res, err := p.p.Acquire(ctx) res, err := p.p.Acquire(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
@ -553,7 +599,8 @@ func (p *Pool) Acquire(ctx context.Context) (c *Conn, err error) {
cr := res.Value() cr := res.Value()
if res.IdleDuration() > time.Second { shouldPingParams := ShouldPingParams{Conn: cr.conn, IdleDuration: res.IdleDuration()}
if p.shouldPing(ctx, shouldPingParams) {
err := cr.conn.Ping(ctx) err := cr.conn.Ping(ctx)
if err != nil { if err != nil {
res.Destroy() res.Destroy()
@ -561,12 +608,25 @@ func (p *Pool) Acquire(ctx context.Context) (c *Conn, err error) {
} }
} }
if p.beforeAcquire == nil || p.beforeAcquire(ctx, cr.conn) { if p.prepareConn != nil {
return cr.getConn(p, res), nil ok, err := p.prepareConn(ctx, cr.conn)
} if !ok {
res.Destroy() res.Destroy()
} }
if err != nil {
if ok {
res.Release()
}
return nil, err
}
if !ok {
continue
}
}
return cr.getConn(p, res), nil
}
return nil, errors.New("pgxpool: detected infinite loop acquiring connection; likely bug in PrepareConn or BeforeAcquire hook")
} }
// AcquireFunc acquires a *Conn and calls f with that *Conn. ctx will only affect the Acquire. It has no effect on the // AcquireFunc acquires a *Conn and calls f with that *Conn. ctx will only affect the Acquire. It has no effect on the
@ -589,12 +649,15 @@ func (p *Pool) AcquireAllIdle(ctx context.Context) []*Conn {
conns := make([]*Conn, 0, len(resources)) conns := make([]*Conn, 0, len(resources))
for _, res := range resources { for _, res := range resources {
cr := res.Value() cr := res.Value()
if p.beforeAcquire == nil || p.beforeAcquire(ctx, cr.conn) { if p.prepareConn != nil {
conns = append(conns, cr.getConn(p, res)) ok, err := p.prepareConn(ctx, cr.conn)
} else { if !ok || err != nil {
res.Destroy() res.Destroy()
continue
} }
} }
conns = append(conns, cr.getConn(p, res))
}
return conns return conns
} }

View file

@ -41,22 +41,19 @@ type Rows interface {
// when there was an error executing the query. // when there was an error executing the query.
FieldDescriptions() []pgconn.FieldDescription FieldDescriptions() []pgconn.FieldDescription
// Next prepares the next row for reading. It returns true if there is another // Next prepares the next row for reading. It returns true if there is another row and false if no more rows are
// row and false if no more rows are available or a fatal error has occurred. // available or a fatal error has occurred. It automatically closes rows upon returning false (whether due to all rows
// It automatically closes rows when all rows are read. // having been read or due to an error).
// //
// Callers should check rows.Err() after rows.Next() returns false to detect // Callers should check rows.Err() after rows.Next() returns false to detect whether result-set reading ended
// whether result-set reading ended prematurely due to an error. See // prematurely due to an error. See Conn.Query for details.
// Conn.Query for details.
// //
// For simpler error handling, consider using the higher-level pgx v5 // For simpler error handling, consider using the higher-level pgx v5 CollectRows() and ForEachRow() helpers instead.
// CollectRows() and ForEachRow() helpers instead.
Next() bool Next() bool
// Scan reads the values from the current row into dest values positionally. // Scan reads the values from the current row into dest values positionally. dest can include pointers to core types,
// dest can include pointers to core types, values implementing the Scanner // values implementing the Scanner interface, and nil. nil will skip the value entirely. It is an error to call Scan
// interface, and nil. nil will skip the value entirely. It is an error to // without first calling Next() and checking that it returned true. Rows is automatically closed upon error.
// call Scan without first calling Next() and checking that it returned true.
Scan(dest ...any) error Scan(dest ...any) error
// Values returns the decoded row values. As with Scan(), it is an error to // Values returns the decoded row values. As with Scan(), it is an error to
@ -563,7 +560,7 @@ func (rs *mapRowScanner) ScanRow(rows Rows) error {
return nil return nil
} }
// RowToStructByPos returns a T scanned from row. T must be a struct. T must have the same number a public fields as row // RowToStructByPos returns a T scanned from row. T must be a struct. T must have the same number of public fields as row
// has fields. The row and T fields will be matched by position. If the "db" struct tag is "-" then the field will be // has fields. The row and T fields will be matched by position. If the "db" struct tag is "-" then the field will be
// ignored. // ignored.
func RowToStructByPos[T any](row CollectableRow) (T, error) { func RowToStructByPos[T any](row CollectableRow) (T, error) {

View file

@ -471,7 +471,8 @@ func (c *Conn) ExecContext(ctx context.Context, query string, argsV []driver.Nam
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
args := namedValueToInterface(argsV) args := make([]any, len(argsV))
convertNamedArguments(args, argsV)
commandTag, err := c.conn.Exec(ctx, query, args...) commandTag, err := c.conn.Exec(ctx, query, args...)
// if we got a network error before we had a chance to send the query, retry // if we got a network error before we had a chance to send the query, retry
@ -488,8 +489,9 @@ func (c *Conn) QueryContext(ctx context.Context, query string, argsV []driver.Na
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
args := []any{databaseSQLResultFormats} args := make([]any, 1+len(argsV))
args = append(args, namedValueToInterface(argsV)...) args[0] = databaseSQLResultFormats
convertNamedArguments(args[1:], argsV)
rows, err := c.conn.Query(ctx, query, args...) rows, err := c.conn.Query(ctx, query, args...)
if err != nil { if err != nil {
@ -848,28 +850,14 @@ func (r *Rows) Next(dest []driver.Value) error {
return nil return nil
} }
func valueToInterface(argsV []driver.Value) []any { func convertNamedArguments(args []any, argsV []driver.NamedValue) {
args := make([]any, 0, len(argsV)) for i, v := range argsV {
for _, v := range argsV {
if v != nil {
args = append(args, v.(any))
} else {
args = append(args, nil)
}
}
return args
}
func namedValueToInterface(argsV []driver.NamedValue) []any {
args := make([]any, 0, len(argsV))
for _, v := range argsV {
if v.Value != nil { if v.Value != nil {
args = append(args, v.Value.(any)) args[i] = v.Value.(any)
} else { } else {
args = append(args, nil) args[i] = nil
} }
} }
return args
} }
type wrapTx struct { type wrapTx struct {

View file

@ -27,8 +27,9 @@ trap 'rm -f sqlite3.tmp' EXIT
$(awk '{print "-Wl,--export="$0}' exports.txt) $(awk '{print "-Wl,--export="$0}' exports.txt)
"$BINARYEN/wasm-ctor-eval" -g -c _initialize sqlite3.wasm -o sqlite3.tmp "$BINARYEN/wasm-ctor-eval" -g -c _initialize sqlite3.wasm -o sqlite3.tmp
"$BINARYEN/wasm-opt" -g --strip --strip-producers -c -O3 \ "$BINARYEN/wasm-opt" -g sqlite3.tmp -o sqlite3.wasm \
sqlite3.tmp -o sqlite3.wasm --low-memory-unused \ --low-memory-unused --gufa --generate-global-effects --converge -O3 \
--enable-mutable-globals --enable-nontrapping-float-to-int \ --enable-mutable-globals --enable-nontrapping-float-to-int \
--enable-simd --enable-bulk-memory --enable-sign-ext \ --enable-simd --enable-bulk-memory --enable-sign-ext \
--enable-reference-types --enable-multivalue --enable-reference-types --enable-multivalue \
--strip --strip-producers

Binary file not shown.

View file

@ -9,24 +9,31 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
func NewMemory(_, max uint64) experimental.LinearMemory { func NewMemory(cap, max uint64) experimental.LinearMemory {
// Round up to the page size. // Round up to the page size.
rnd := uint64(unix.Getpagesize() - 1) rnd := uint64(unix.Getpagesize() - 1)
max = (max + rnd) &^ rnd res := (max + rnd) &^ rnd
if max > math.MaxInt { if res > math.MaxInt {
// This ensures int(max) overflows to a negative value, // This ensures int(res) overflows to a negative value,
// and unix.Mmap returns EINVAL. // and unix.Mmap returns EINVAL.
max = math.MaxUint64 res = math.MaxUint64
} }
// Reserve max bytes of address space, to ensure we won't need to move it. com := res
prot := unix.PROT_READ | unix.PROT_WRITE
if cap < max { // Commit memory only if cap=max.
com = 0
prot = unix.PROT_NONE
}
// Reserve res bytes of address space, to ensure we won't need to move it.
// A protected, private, anonymous mapping should not commit memory. // A protected, private, anonymous mapping should not commit memory.
b, err := unix.Mmap(-1, 0, int(max), unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANON) b, err := unix.Mmap(-1, 0, int(res), prot, unix.MAP_PRIVATE|unix.MAP_ANON)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return &mmappedMemory{buf: b[:0]} return &mmappedMemory{buf: b[:com]}
} }
// The slice covers the entire mmapped memory: // The slice covers the entire mmapped memory:
@ -40,9 +47,11 @@ func (m *mmappedMemory) Reallocate(size uint64) []byte {
com := uint64(len(m.buf)) com := uint64(len(m.buf))
res := uint64(cap(m.buf)) res := uint64(cap(m.buf))
if com < size && size <= res { if com < size && size <= res {
// Round up to the page size. // Grow geometrically, round up to the page size.
rnd := uint64(unix.Getpagesize() - 1) rnd := uint64(unix.Getpagesize() - 1)
new := (size + rnd) &^ rnd new := com + com>>3
new = min(max(size, new), res)
new = (new + rnd) &^ rnd
// Commit additional memory up to new bytes. // Commit additional memory up to new bytes.
err := unix.Mprotect(m.buf[com:new], unix.PROT_READ|unix.PROT_WRITE) err := unix.Mprotect(m.buf[com:new], unix.PROT_READ|unix.PROT_WRITE)
@ -50,8 +59,7 @@ func (m *mmappedMemory) Reallocate(size uint64) []byte {
return nil return nil
} }
// Update committed memory. m.buf = m.buf[:new] // Update committed memory.
m.buf = m.buf[:new]
} }
// Limit returned capacity because bytes beyond // Limit returned capacity because bytes beyond
// len(m.buf) have not yet been committed. // len(m.buf) have not yet been committed.

View file

@ -9,20 +9,26 @@ import (
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
func NewMemory(_, max uint64) experimental.LinearMemory { func NewMemory(cap, max uint64) experimental.LinearMemory {
// Round up to the page size. // Round up to the page size.
rnd := uint64(windows.Getpagesize() - 1) rnd := uint64(windows.Getpagesize() - 1)
max = (max + rnd) &^ rnd res := (max + rnd) &^ rnd
if max > math.MaxInt { if res > math.MaxInt {
// This ensures uintptr(max) overflows to a large value, // This ensures uintptr(res) overflows to a large value,
// and windows.VirtualAlloc returns an error. // and windows.VirtualAlloc returns an error.
max = math.MaxUint64 res = math.MaxUint64
} }
// Reserve max bytes of address space, to ensure we won't need to move it. com := res
// This does not commit memory. kind := windows.MEM_COMMIT
r, err := windows.VirtualAlloc(0, uintptr(max), windows.MEM_RESERVE, windows.PAGE_READWRITE) if cap < max { // Commit memory only if cap=max.
com = 0
kind = windows.MEM_RESERVE
}
// Reserve res bytes of address space, to ensure we won't need to move it.
r, err := windows.VirtualAlloc(0, uintptr(res), uint32(kind), windows.PAGE_READWRITE)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -30,8 +36,9 @@ func NewMemory(_, max uint64) experimental.LinearMemory {
mem := virtualMemory{addr: r} mem := virtualMemory{addr: r}
// SliceHeader, although deprecated, avoids a go vet warning. // SliceHeader, although deprecated, avoids a go vet warning.
sh := (*reflect.SliceHeader)(unsafe.Pointer(&mem.buf)) sh := (*reflect.SliceHeader)(unsafe.Pointer(&mem.buf))
sh.Cap = int(max)
sh.Data = r sh.Data = r
sh.Len = int(com)
sh.Cap = int(res)
return &mem return &mem
} }
@ -47,9 +54,11 @@ func (m *virtualMemory) Reallocate(size uint64) []byte {
com := uint64(len(m.buf)) com := uint64(len(m.buf))
res := uint64(cap(m.buf)) res := uint64(cap(m.buf))
if com < size && size <= res { if com < size && size <= res {
// Round up to the page size. // Grow geometrically, round up to the page size.
rnd := uint64(windows.Getpagesize() - 1) rnd := uint64(windows.Getpagesize() - 1)
new := (size + rnd) &^ rnd new := com + com>>3
new = min(max(size, new), res)
new = (new + rnd) &^ rnd
// Commit additional memory up to new bytes. // Commit additional memory up to new bytes.
_, err := windows.VirtualAlloc(m.addr, uintptr(new), windows.MEM_COMMIT, windows.PAGE_READWRITE) _, err := windows.VirtualAlloc(m.addr, uintptr(new), windows.MEM_COMMIT, windows.PAGE_READWRITE)
@ -57,8 +66,7 @@ func (m *virtualMemory) Reallocate(size uint64) []byte {
return nil return nil
} }
// Update committed memory. m.buf = m.buf[:new] // Update committed memory.
m.buf = m.buf[:new]
} }
// Limit returned capacity because bytes beyond // Limit returned capacity because bytes beyond
// len(m.buf) have not yet been committed. // len(m.buf) have not yet been committed.

View file

@ -255,8 +255,8 @@ There are a couple of comparison tables online, such as [CSS Minifier Comparison
Options: Options:
- `KeepCSS2` prohibits using CSS3 syntax (such as exponents in numbers, or `rgba(` &#8594; `rgb(`), might be incomplete
- `Precision` number of significant digits to preserve for numbers, `0` means no trimming - `Precision` number of significant digits to preserve for numbers, `0` means no trimming
- `Version` CSS version to use for output, `0` is the latest
## JS ## JS

View file

@ -38,9 +38,7 @@ func SchedSetaffinity(pid int, set *CPUSet) error {
// Zero clears the set s, so that it contains no CPUs. // Zero clears the set s, so that it contains no CPUs.
func (s *CPUSet) Zero() { func (s *CPUSet) Zero() {
for i := range s { clear(s[:])
s[i] = 0
}
} }
func cpuBitsIndex(cpu int) int { func cpuBitsIndex(cpu int) int {

View file

@ -629,7 +629,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Kill(pid int, signum syscall.Signal) (err error) //sys Kill(pid int, signum syscall.Signal) (err error)
//sys Lchown(path string, uid int, gid int) (err error) //sys Lchown(path string, uid int, gid int) (err error)
//sys Link(path string, link string) (err error) //sys Link(path string, link string) (err error)
//sys Listen(s int, backlog int) (err error) = libsocket.__xnet_llisten //sys Listen(s int, backlog int) (err error) = libsocket.__xnet_listen
//sys Lstat(path string, stat *Stat_t) (err error) //sys Lstat(path string, stat *Stat_t) (err error)
//sys Madvise(b []byte, advice int) (err error) //sys Madvise(b []byte, advice int) (err error)
//sys Mkdir(path string, mode uint32) (err error) //sys Mkdir(path string, mode uint32) (err error)

View file

@ -72,7 +72,7 @@ import (
//go:cgo_import_dynamic libc_kill kill "libc.so" //go:cgo_import_dynamic libc_kill kill "libc.so"
//go:cgo_import_dynamic libc_lchown lchown "libc.so" //go:cgo_import_dynamic libc_lchown lchown "libc.so"
//go:cgo_import_dynamic libc_link link "libc.so" //go:cgo_import_dynamic libc_link link "libc.so"
//go:cgo_import_dynamic libc___xnet_llisten __xnet_llisten "libsocket.so" //go:cgo_import_dynamic libc___xnet_listen __xnet_listen "libsocket.so"
//go:cgo_import_dynamic libc_lstat lstat "libc.so" //go:cgo_import_dynamic libc_lstat lstat "libc.so"
//go:cgo_import_dynamic libc_madvise madvise "libc.so" //go:cgo_import_dynamic libc_madvise madvise "libc.so"
//go:cgo_import_dynamic libc_mkdir mkdir "libc.so" //go:cgo_import_dynamic libc_mkdir mkdir "libc.so"
@ -221,7 +221,7 @@ import (
//go:linkname procKill libc_kill //go:linkname procKill libc_kill
//go:linkname procLchown libc_lchown //go:linkname procLchown libc_lchown
//go:linkname procLink libc_link //go:linkname procLink libc_link
//go:linkname proc__xnet_llisten libc___xnet_llisten //go:linkname proc__xnet_listen libc___xnet_listen
//go:linkname procLstat libc_lstat //go:linkname procLstat libc_lstat
//go:linkname procMadvise libc_madvise //go:linkname procMadvise libc_madvise
//go:linkname procMkdir libc_mkdir //go:linkname procMkdir libc_mkdir
@ -371,7 +371,7 @@ var (
procKill, procKill,
procLchown, procLchown,
procLink, procLink,
proc__xnet_llisten, proc__xnet_listen,
procLstat, procLstat,
procMadvise, procMadvise,
procMkdir, procMkdir,
@ -1178,7 +1178,7 @@ func Link(path string, link string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Listen(s int, backlog int) (err error) { func Listen(s int, backlog int) (err error) {
_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&proc__xnet_llisten)), 2, uintptr(s), uintptr(backlog), 0, 0, 0, 0) _, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&proc__xnet_listen)), 2, uintptr(s), uintptr(backlog), 0, 0, 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View file

@ -632,6 +632,8 @@ const (
IFA_FLAGS = 0x8 IFA_FLAGS = 0x8
IFA_RT_PRIORITY = 0x9 IFA_RT_PRIORITY = 0x9
IFA_TARGET_NETNSID = 0xa IFA_TARGET_NETNSID = 0xa
IFAL_LABEL = 0x2
IFAL_ADDRESS = 0x1
RT_SCOPE_UNIVERSE = 0x0 RT_SCOPE_UNIVERSE = 0x0
RT_SCOPE_SITE = 0xc8 RT_SCOPE_SITE = 0xc8
RT_SCOPE_LINK = 0xfd RT_SCOPE_LINK = 0xfd
@ -689,6 +691,7 @@ const (
SizeofRtAttr = 0x4 SizeofRtAttr = 0x4
SizeofIfInfomsg = 0x10 SizeofIfInfomsg = 0x10
SizeofIfAddrmsg = 0x8 SizeofIfAddrmsg = 0x8
SizeofIfAddrlblmsg = 0xc
SizeofIfaCacheinfo = 0x10 SizeofIfaCacheinfo = 0x10
SizeofRtMsg = 0xc SizeofRtMsg = 0xc
SizeofRtNexthop = 0x8 SizeofRtNexthop = 0x8
@ -740,6 +743,15 @@ type IfAddrmsg struct {
Index uint32 Index uint32
} }
type IfAddrlblmsg struct {
Family uint8
_ uint8
Prefixlen uint8
Flags uint8
Index uint32
Seq uint32
}
type IfaCacheinfo struct { type IfaCacheinfo struct {
Prefered uint32 Prefered uint32
Valid uint32 Valid uint32
@ -3052,6 +3064,23 @@ const (
) )
const ( const (
TCA_UNSPEC = 0x0
TCA_KIND = 0x1
TCA_OPTIONS = 0x2
TCA_STATS = 0x3
TCA_XSTATS = 0x4
TCA_RATE = 0x5
TCA_FCNT = 0x6
TCA_STATS2 = 0x7
TCA_STAB = 0x8
TCA_PAD = 0x9
TCA_DUMP_INVISIBLE = 0xa
TCA_CHAIN = 0xb
TCA_HW_OFFLOAD = 0xc
TCA_INGRESS_BLOCK = 0xd
TCA_EGRESS_BLOCK = 0xe
TCA_DUMP_FLAGS = 0xf
TCA_EXT_WARN_MSG = 0x10
RTNLGRP_NONE = 0x0 RTNLGRP_NONE = 0x0
RTNLGRP_LINK = 0x1 RTNLGRP_LINK = 0x1
RTNLGRP_NOTIFY = 0x2 RTNLGRP_NOTIFY = 0x2
@ -3086,6 +3115,18 @@ const (
RTNLGRP_IPV6_MROUTE_R = 0x1f RTNLGRP_IPV6_MROUTE_R = 0x1f
RTNLGRP_NEXTHOP = 0x20 RTNLGRP_NEXTHOP = 0x20
RTNLGRP_BRVLAN = 0x21 RTNLGRP_BRVLAN = 0x21
RTNLGRP_MCTP_IFADDR = 0x22
RTNLGRP_TUNNEL = 0x23
RTNLGRP_STATS = 0x24
RTNLGRP_IPV4_MCADDR = 0x25
RTNLGRP_IPV6_MCADDR = 0x26
RTNLGRP_IPV6_ACADDR = 0x27
TCA_ROOT_UNSPEC = 0x0
TCA_ROOT_TAB = 0x1
TCA_ROOT_FLAGS = 0x2
TCA_ROOT_COUNT = 0x3
TCA_ROOT_TIME_DELTA = 0x4
TCA_ROOT_EXT_WARN_MSG = 0x5
) )
type CapUserHeader struct { type CapUserHeader struct {

View file

@ -52,7 +52,7 @@ var (
) )
func regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) { func regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) {
r0, _, _ := syscall.Syscall(procRegConnectRegistryW.Addr(), 3, uintptr(unsafe.Pointer(machinename)), uintptr(key), uintptr(unsafe.Pointer(result))) r0, _, _ := syscall.SyscallN(procRegConnectRegistryW.Addr(), uintptr(unsafe.Pointer(machinename)), uintptr(key), uintptr(unsafe.Pointer(result)))
if r0 != 0 { if r0 != 0 {
regerrno = syscall.Errno(r0) regerrno = syscall.Errno(r0)
} }
@ -60,7 +60,7 @@ func regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall
} }
func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) { func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) {
r0, _, _ := syscall.Syscall9(procRegCreateKeyExW.Addr(), 9, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition))) r0, _, _ := syscall.SyscallN(procRegCreateKeyExW.Addr(), uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition)))
if r0 != 0 { if r0 != 0 {
regerrno = syscall.Errno(r0) regerrno = syscall.Errno(r0)
} }
@ -68,7 +68,7 @@ func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *
} }
func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) { func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) {
r0, _, _ := syscall.Syscall(procRegDeleteKeyW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(subkey)), 0) r0, _, _ := syscall.SyscallN(procRegDeleteKeyW.Addr(), uintptr(key), uintptr(unsafe.Pointer(subkey)))
if r0 != 0 { if r0 != 0 {
regerrno = syscall.Errno(r0) regerrno = syscall.Errno(r0)
} }
@ -76,7 +76,7 @@ func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) {
} }
func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) { func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) {
r0, _, _ := syscall.Syscall(procRegDeleteValueW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(name)), 0) r0, _, _ := syscall.SyscallN(procRegDeleteValueW.Addr(), uintptr(key), uintptr(unsafe.Pointer(name)))
if r0 != 0 { if r0 != 0 {
regerrno = syscall.Errno(r0) regerrno = syscall.Errno(r0)
} }
@ -84,7 +84,7 @@ func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) {
} }
func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) { func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) {
r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)), 0) r0, _, _ := syscall.SyscallN(procRegEnumValueW.Addr(), uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)))
if r0 != 0 { if r0 != 0 {
regerrno = syscall.Errno(r0) regerrno = syscall.Errno(r0)
} }
@ -92,7 +92,7 @@ func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint3
} }
func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) { func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) {
r0, _, _ := syscall.Syscall9(procRegLoadMUIStringW.Addr(), 7, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(unsafe.Pointer(buflenCopied)), uintptr(flags), uintptr(unsafe.Pointer(dir)), 0, 0) r0, _, _ := syscall.SyscallN(procRegLoadMUIStringW.Addr(), uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(unsafe.Pointer(buflenCopied)), uintptr(flags), uintptr(unsafe.Pointer(dir)))
if r0 != 0 { if r0 != 0 {
regerrno = syscall.Errno(r0) regerrno = syscall.Errno(r0)
} }
@ -100,7 +100,7 @@ func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint
} }
func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) { func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) {
r0, _, _ := syscall.Syscall6(procRegSetValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize)) r0, _, _ := syscall.SyscallN(procRegSetValueExW.Addr(), uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize))
if r0 != 0 { if r0 != 0 {
regerrno = syscall.Errno(r0) regerrno = syscall.Errno(r0)
} }
@ -108,7 +108,7 @@ func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype
} }
func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) { func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) {
r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size)) r0, _, e1 := syscall.SyscallN(procExpandEnvironmentStringsW.Addr(), uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size))
n = uint32(r0) n = uint32(r0)
if n == 0 { if n == 0 {
err = errnoErr(e1) err = errnoErr(e1)

View file

@ -1976,6 +1976,12 @@ const (
SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1
) )
// FILE_ZERO_DATA_INFORMATION from winioctl.h
type FileZeroDataInformation struct {
FileOffset int64
BeyondFinalZero int64
}
const ( const (
ComputerNameNetBIOS = 0 ComputerNameNetBIOS = 0
ComputerNameDnsHostname = 1 ComputerNameDnsHostname = 1

File diff suppressed because it is too large Load diff

View file

@ -113,7 +113,7 @@ func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Nod
// childrenOf elides the FuncType node beneath FuncDecl. // childrenOf elides the FuncType node beneath FuncDecl.
// Add it back here for TypeParams, Params, Results, // Add it back here for TypeParams, Params, Results,
// all FieldLists). But we don't add it back for the "func" token // all FieldLists). But we don't add it back for the "func" token
// even though it is is the tree at FuncDecl.Type.Func. // even though it is the tree at FuncDecl.Type.Func.
if decl, ok := node.(*ast.FuncDecl); ok { if decl, ok := node.(*ast.FuncDecl); ok {
if fields, ok := child.(*ast.FieldList); ok && fields != decl.Recv { if fields, ok := child.(*ast.FieldList); ok && fields != decl.Recv {
path = append(path, decl.Type) path = append(path, decl.Type)

View file

@ -76,6 +76,8 @@ uninterpreted to Load, so that it can interpret them
according to the conventions of the underlying build system. according to the conventions of the underlying build system.
See the Example function for typical usage. See the Example function for typical usage.
See also [golang.org/x/tools/go/packages/internal/linecount]
for an example application.
# The driver protocol # The driver protocol

View file

@ -15,6 +15,10 @@ import (
// This code is here rather than in the modindex package // This code is here rather than in the modindex package
// to avoid import loops // to avoid import loops
// TODO(adonovan): this code is only used by a test in this package.
// Can we delete it? Or is there a plan to call NewIndexSource from
// cmd/goimports?
// implements Source using modindex, so only for module cache. // implements Source using modindex, so only for module cache.
// //
// this is perhaps over-engineered. A new Index is read at first use. // this is perhaps over-engineered. A new Index is read at first use.
@ -22,8 +26,8 @@ import (
// is read if the index changed. It is not clear the Mutex is needed. // is read if the index changed. It is not clear the Mutex is needed.
type IndexSource struct { type IndexSource struct {
modcachedir string modcachedir string
mutex sync.Mutex mu sync.Mutex
ix *modindex.Index index *modindex.Index // (access via getIndex)
expires time.Time expires time.Time
} }
@ -39,13 +43,14 @@ func (s *IndexSource) LoadPackageNames(ctx context.Context, srcDir string, paths
} }
func (s *IndexSource) ResolveReferences(ctx context.Context, filename string, missing References) ([]*Result, error) { func (s *IndexSource) ResolveReferences(ctx context.Context, filename string, missing References) ([]*Result, error) {
if err := s.maybeReadIndex(); err != nil { index, err := s.getIndex()
if err != nil {
return nil, err return nil, err
} }
var cs []modindex.Candidate var cs []modindex.Candidate
for pkg, nms := range missing { for pkg, nms := range missing {
for nm := range nms { for nm := range nms {
x := s.ix.Lookup(pkg, nm, false) x := index.Lookup(pkg, nm, false)
cs = append(cs, x...) cs = append(cs, x...)
} }
} }
@ -74,30 +79,22 @@ func (s *IndexSource) ResolveReferences(ctx context.Context, filename string, mi
return ans, nil return ans, nil
} }
func (s *IndexSource) maybeReadIndex() error { func (s *IndexSource) getIndex() (*modindex.Index, error) {
s.mutex.Lock() s.mu.Lock()
defer s.mutex.Unlock() defer s.mu.Unlock()
var readIndex bool // (s.index = nil => s.expires is zero,
if time.Now().After(s.expires) { // so the first condition is strictly redundant.
ok, err := modindex.Update(s.modcachedir) // But it makes the postcondition very clear.)
if s.index == nil || time.Now().After(s.expires) {
index, err := modindex.Update(s.modcachedir)
if err != nil { if err != nil {
return err return nil, err
}
if ok {
readIndex = true
} }
s.index = index
s.expires = index.ValidAt.Add(15 * time.Minute) // (refresh period)
} }
// Inv: s.index != nil
if readIndex || s.ix == nil { return s.index, nil
ix, err := modindex.ReadIndex(s.modcachedir)
if err != nil {
return err
}
s.ix = ix
// for now refresh every 15 minutes
s.expires = time.Now().Add(time.Minute * 15)
}
return nil
} }

View file

@ -10,7 +10,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"slices"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -20,50 +19,48 @@ import (
) )
type directory struct { type directory struct {
path Relpath path string // relative to GOMODCACHE
importPath string importPath string
version string // semantic version version string // semantic version
syms []symbol
} }
// byImportPath groups the directories by import path, // bestDirByImportPath returns the best directory for each import
// sorting the ones with the same import path by semantic version, // path, where "best" means most recent semantic version. These import
// most recent first. // paths are inferred from the GOMODCACHE-relative dir names in dirs.
func byImportPath(dirs []Relpath) (map[string][]*directory, error) { func bestDirByImportPath(dirs []string) (map[string]directory, error) {
ans := make(map[string][]*directory) // key is import path dirsByPath := make(map[string]directory)
for _, d := range dirs { for _, dir := range dirs {
ip, sv, err := DirToImportPathVersion(d) importPath, version, err := dirToImportPathVersion(dir)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ans[ip] = append(ans[ip], &directory{ new := directory{
path: d, path: dir,
importPath: ip, importPath: importPath,
version: sv, version: version,
})
} }
for k, v := range ans { if old, ok := dirsByPath[importPath]; !ok || compareDirectory(new, old) < 0 {
semanticSort(v) dirsByPath[importPath] = new
ans[k] = v
} }
return ans, nil }
return dirsByPath, nil
} }
// sort the directories by semantic version, latest first // compareDirectory defines an ordering of path@version directories,
func semanticSort(v []*directory) { // by descending version, then by ascending path.
slices.SortFunc(v, func(l, r *directory) int { func compareDirectory(x, y directory) int {
if n := semver.Compare(l.version, r.version); n != 0 { if sign := -semver.Compare(x.version, y.version); sign != 0 {
return -n // latest first return sign // latest first
} }
return strings.Compare(string(l.path), string(r.path)) return strings.Compare(string(x.path), string(y.path))
})
} }
// modCacheRegexp splits a relpathpath into module, module version, and package. // modCacheRegexp splits a relpathpath into module, module version, and package.
var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
// DirToImportPathVersion computes import path and semantic version // dirToImportPathVersion computes import path and semantic version
func DirToImportPathVersion(dir Relpath) (string, string, error) { // from a GOMODCACHE-relative directory name.
func dirToImportPathVersion(dir string) (string, string, error) {
m := modCacheRegexp.FindStringSubmatch(string(dir)) m := modCacheRegexp.FindStringSubmatch(string(dir))
// m[1] is the module path // m[1] is the module path
// m[2] is the version major.minor.patch(-<pre release identifier) // m[2] is the version major.minor.patch(-<pre release identifier)
@ -74,62 +71,61 @@ func DirToImportPathVersion(dir Relpath) (string, string, error) {
if !semver.IsValid(m[2]) { if !semver.IsValid(m[2]) {
return "", "", fmt.Errorf("bad semantic version %s", m[2]) return "", "", fmt.Errorf("bad semantic version %s", m[2])
} }
// ToSlash is required for Windows. // ToSlash is required to convert Windows file paths
// into Go package import paths.
return filepath.ToSlash(m[1] + m[3]), m[2], nil return filepath.ToSlash(m[1] + m[3]), m[2], nil
} }
// a region controls what directories to look at, for // findDirs returns an unordered list of relevant package directories,
// updating the index incrementally, and for testing that. // relative to the specified module cache root. The result includes only
// (for testing one builds an index as of A, incrementally // module dirs whose mtime is within (start, end).
// updates it to B, and compares the result to an index build func findDirs(root string, start, end time.Time) []string {
// as of B.) var (
type region struct { resMu sync.Mutex
onlyAfter, onlyBefore time.Time res []string
sync.Mutex )
ans []Relpath
addDir := func(root gopathwalk.Root, dir string) {
// TODO(pjw): do we need to check times?
resMu.Lock()
defer resMu.Unlock()
res = append(res, relative(root.Path, dir))
} }
func findDirs(root string, onlyAfter, onlyBefore time.Time) []Relpath { skipDir := func(_ gopathwalk.Root, dir string) bool {
roots := []gopathwalk.Root{{Path: root, Type: gopathwalk.RootModuleCache}} // The cache directory is already ignored in gopathwalk.
// TODO(PJW): adjust concurrency
opts := gopathwalk.Options{ModulesEnabled: true, Concurrency: 1 /* ,Logf: log.Printf*/}
betw := &region{
onlyAfter: onlyAfter,
onlyBefore: onlyBefore,
}
gopathwalk.WalkSkip(roots, betw.addDir, betw.skipDir, opts)
return betw.ans
}
func (r *region) addDir(rt gopathwalk.Root, dir string) {
// do we need to check times?
r.Lock()
defer r.Unlock()
x := filepath.ToSlash(string(toRelpath(Abspath(rt.Path), dir)))
r.ans = append(r.ans, toRelpath(Abspath(rt.Path), x))
}
func (r *region) skipDir(_ gopathwalk.Root, dir string) bool {
// The cache directory is already ignored in gopathwalk\
if filepath.Base(dir) == "internal" { if filepath.Base(dir) == "internal" {
return true return true
} }
// Skip toolchains.
if strings.Contains(dir, "toolchain@") { if strings.Contains(dir, "toolchain@") {
return true return true
} }
// don't look inside @ directories that are too old
// Don't look inside @ directories that are too old/new.
if strings.Contains(filepath.Base(dir), "@") { if strings.Contains(filepath.Base(dir), "@") {
st, err := os.Stat(dir) st, err := os.Stat(dir)
if err != nil { if err != nil {
log.Printf("can't stat dir %s %v", dir, err) log.Printf("can't stat dir %s %v", dir, err)
return true return true
} }
if st.ModTime().Before(r.onlyAfter) { mtime := st.ModTime()
return true return mtime.Before(start) || mtime.After(end)
}
if st.ModTime().After(r.onlyBefore) {
return true
}
} }
return false return false
} }
// TODO(adonovan): parallelize this. Even with a hot buffer cache,
// find $(go env GOMODCACHE) -type d
// can easily take up a minute.
roots := []gopathwalk.Root{{Path: root, Type: gopathwalk.RootModuleCache}}
gopathwalk.WalkSkip(roots, addDir, skipDir, gopathwalk.Options{
ModulesEnabled: true,
Concurrency: 1, // TODO(pjw): adjust concurrency
// Logf: log.Printf,
})
return res
}

View file

@ -6,12 +6,10 @@ package modindex
import ( import (
"bufio" "bufio"
"crypto/sha256"
"encoding/csv" "encoding/csv"
"errors"
"fmt" "fmt"
"hash/crc64"
"io" "io"
"io/fs"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -22,7 +20,7 @@ import (
) )
/* /*
The on-disk index is a text file. The on-disk index ("payload") is a text file.
The first 3 lines are header information containing CurrentVersion, The first 3 lines are header information containing CurrentVersion,
the value of GOMODCACHE, and the validity date of the index. the value of GOMODCACHE, and the validity date of the index.
(This is when the code started building the index.) (This is when the code started building the index.)
@ -68,34 +66,45 @@ whose types are []byte and interface{}.
// CurrentVersion tells readers about the format of the index. // CurrentVersion tells readers about the format of the index.
const CurrentVersion int = 0 const CurrentVersion int = 0
// Index is returned by ReadIndex(). // Index is returned by [Read].
type Index struct { type Index struct {
Version int Version int
Cachedir Abspath // The directory containing the module cache GOMODCACHE string // absolute path of Go module cache dir
Changed time.Time // The index is up to date as of Changed ValidAt time.Time // moment at which the index was up to date
Entries []Entry Entries []Entry
} }
func (ix *Index) String() string {
return fmt.Sprintf("Index(%s v%d has %d entries at %v)",
ix.GOMODCACHE, ix.Version, len(ix.Entries), ix.ValidAt)
}
// An Entry contains information for an import path. // An Entry contains information for an import path.
type Entry struct { type Entry struct {
Dir Relpath // directory in modcache Dir string // package directory relative to GOMODCACHE; uses OS path separator
ImportPath string ImportPath string
PkgName string PkgName string
Version string Version string
//ModTime STime // is this useful?
Names []string // exported names and information Names []string // exported names and information
} }
// IndexDir is where the module index is stored. // IndexDir is where the module index is stored.
var IndexDir string // Each logical index entry consists of a pair of files:
//
// Set IndexDir // - the "payload" (index-VERSION-XXX), whose name is
func init() { // randomized, holds the actual index; and
// - the "link" (index-name-VERSION-HASH),
// whose name is predictable, contains the
// name of the payload file.
//
// Since the link file is small (<512B),
// reads and writes to it may be assumed atomic.
var IndexDir string = func() string {
var dir string var dir string
var err error
if testing.Testing() { if testing.Testing() {
dir = os.TempDir() dir = os.TempDir()
} else { } else {
var err error
dir, err = os.UserCacheDir() dir, err = os.UserCacheDir()
// shouldn't happen, but TempDir is better than // shouldn't happen, but TempDir is better than
// creating ./go/imports // creating ./go/imports
@ -103,81 +112,83 @@ func init() {
dir = os.TempDir() dir = os.TempDir()
} }
} }
dir = filepath.Join(dir, "go", "imports") dir = filepath.Join(dir, "goimports")
os.MkdirAll(dir, 0777) if err := os.MkdirAll(dir, 0777); err != nil {
IndexDir = dir log.Printf("failed to create modcache index dir: %v", err)
}
return dir
}()
// Read reads the latest version of the on-disk index
// for the specified Go module cache directory.
// If there is no index, it returns a nil Index and an fs.ErrNotExist error.
func Read(gomodcache string) (*Index, error) {
gomodcache, err := filepath.Abs(gomodcache)
if err != nil {
return nil, err
} }
// ReadIndex reads the latest version of the on-disk index // Read the "link" file for the specified gomodcache directory.
// for the cache directory cd. // It names the payload file.
// It returns (nil, nil) if there is no index, but returns content, err := os.ReadFile(filepath.Join(IndexDir, linkFileBasename(gomodcache)))
// a non-nil error if the index exists but could not be read.
func ReadIndex(cachedir string) (*Index, error) {
cachedir, err := filepath.Abs(cachedir)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cd := Abspath(cachedir) payloadFile := filepath.Join(IndexDir, string(content))
dir := IndexDir
base := indexNameBase(cd) // Read the index out of the payload file.
iname := filepath.Join(dir, base) f, err := os.Open(payloadFile)
buf, err := os.ReadFile(iname)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return nil, nil
}
return nil, fmt.Errorf("cannot read %s: %w", iname, err)
}
fname := filepath.Join(dir, string(buf))
fd, err := os.Open(fname)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer fd.Close() defer f.Close()
r := bufio.NewReader(fd) return readIndexFrom(gomodcache, bufio.NewReader(f))
ix, err := readIndexFrom(cd, r)
if err != nil {
return nil, err
}
return ix, nil
} }
func readIndexFrom(cd Abspath, bx io.Reader) (*Index, error) { func readIndexFrom(gomodcache string, r io.Reader) (*Index, error) {
b := bufio.NewScanner(bx) scan := bufio.NewScanner(r)
var ans Index
// header // version
ok := b.Scan() if !scan.Scan() {
if !ok { return nil, fmt.Errorf("unexpected scan error: %v", scan.Err())
return nil, fmt.Errorf("unexpected scan error")
} }
l := b.Text() version, err := strconv.Atoi(scan.Text())
var err error
ans.Version, err = strconv.Atoi(l)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ans.Version != CurrentVersion { if version != CurrentVersion {
return nil, fmt.Errorf("got version %d, expected %d", ans.Version, CurrentVersion) return nil, fmt.Errorf("got version %d, expected %d", version, CurrentVersion)
} }
if ok := b.Scan(); !ok {
return nil, fmt.Errorf("scanner error reading cachedir") // gomodcache
} if !scan.Scan() {
ans.Cachedir = Abspath(b.Text()) return nil, fmt.Errorf("scanner error reading module cache dir: %v", scan.Err())
if ok := b.Scan(); !ok {
return nil, fmt.Errorf("scanner error reading index creation time")
} }
// TODO(pjw): need to check that this is the expected cache dir // TODO(pjw): need to check that this is the expected cache dir
// so the tag should be passed in to this function // so the tag should be passed in to this function
ans.Changed, err = time.ParseInLocation(time.DateTime, b.Text(), time.Local) if dir := string(scan.Text()); dir != gomodcache {
return nil, fmt.Errorf("index file GOMODCACHE mismatch: got %q, want %q", dir, gomodcache)
}
// changed
if !scan.Scan() {
return nil, fmt.Errorf("scanner error reading index creation time: %v", scan.Err())
}
changed, err := time.ParseInLocation(time.DateTime, scan.Text(), time.Local)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var curEntry *Entry
for b.Scan() { // entries
v := b.Text() var (
curEntry *Entry
entries []Entry
)
for scan.Scan() {
v := scan.Text()
if v[0] == ':' { if v[0] == ':' {
if curEntry != nil { if curEntry != nil {
ans.Entries = append(ans.Entries, *curEntry) entries = append(entries, *curEntry)
} }
// as directories may contain commas and quotes, they need to be read as csv. // as directories may contain commas and quotes, they need to be read as csv.
rdr := strings.NewReader(v[1:]) rdr := strings.NewReader(v[1:])
@ -189,49 +200,56 @@ func readIndexFrom(cd Abspath, bx io.Reader) (*Index, error) {
if len(flds) != 4 { if len(flds) != 4 {
return nil, fmt.Errorf("header contains %d fields, not 4: %q", len(v), v) return nil, fmt.Errorf("header contains %d fields, not 4: %q", len(v), v)
} }
curEntry = &Entry{PkgName: flds[0], ImportPath: flds[1], Dir: toRelpath(cd, flds[2]), Version: flds[3]} curEntry = &Entry{
PkgName: flds[0],
ImportPath: flds[1],
Dir: relative(gomodcache, flds[2]),
Version: flds[3],
}
continue continue
} }
curEntry.Names = append(curEntry.Names, v) curEntry.Names = append(curEntry.Names, v)
} }
if err := scan.Err(); err != nil {
return nil, fmt.Errorf("scanner failed while reading modindex entry: %v", err)
}
if curEntry != nil { if curEntry != nil {
ans.Entries = append(ans.Entries, *curEntry) entries = append(entries, *curEntry)
}
if err := b.Err(); err != nil {
return nil, fmt.Errorf("scanner failed %v", err)
}
return &ans, nil
} }
// write the index as a text file return &Index{
func writeIndex(cachedir Abspath, ix *Index) error { Version: version,
ipat := fmt.Sprintf("index-%d-*", CurrentVersion) GOMODCACHE: gomodcache,
fd, err := os.CreateTemp(IndexDir, ipat) ValidAt: changed,
if err != nil { Entries: entries,
return err // can this happen? }, nil
}
defer fd.Close()
if err := writeIndexToFile(ix, fd); err != nil {
return err
}
content := fd.Name()
content = filepath.Base(content)
base := indexNameBase(cachedir)
nm := filepath.Join(IndexDir, base)
err = os.WriteFile(nm, []byte(content), 0666)
if err != nil {
return err
}
return nil
} }
func writeIndexToFile(x *Index, fd *os.File) error { // write writes the index file and updates the index directory to refer to it.
cnt := 0 func write(gomodcache string, ix *Index) error {
w := bufio.NewWriter(fd) // Write the index into a payload file with a fresh name.
f, err := os.CreateTemp(IndexDir, fmt.Sprintf("index-%d-*", CurrentVersion))
if err != nil {
return err // e.g. disk full, or index dir deleted
}
if err := writeIndexToFile(ix, bufio.NewWriter(f)); err != nil {
_ = f.Close() // ignore error
return err
}
if err := f.Close(); err != nil {
return err
}
// Write the name of the payload file into a link file.
indexDirFile := filepath.Join(IndexDir, linkFileBasename(gomodcache))
content := []byte(filepath.Base(f.Name()))
return os.WriteFile(indexDirFile, content, 0666)
}
func writeIndexToFile(x *Index, w *bufio.Writer) error {
fmt.Fprintf(w, "%d\n", x.Version) fmt.Fprintf(w, "%d\n", x.Version)
fmt.Fprintf(w, "%s\n", x.Cachedir) fmt.Fprintf(w, "%s\n", x.GOMODCACHE)
// round the time down tm := x.ValidAt.Truncate(time.Second) // round the time down
tm := x.Changed.Add(-time.Second / 2)
fmt.Fprintf(w, "%s\n", tm.Format(time.DateTime)) fmt.Fprintf(w, "%s\n", tm.Format(time.DateTime))
for _, e := range x.Entries { for _, e := range x.Entries {
if e.ImportPath == "" { if e.ImportPath == "" {
@ -239,7 +257,6 @@ func writeIndexToFile(x *Index, fd *os.File) error {
} }
// PJW: maybe always write these headers as csv? // PJW: maybe always write these headers as csv?
if strings.ContainsAny(string(e.Dir), ",\"") { if strings.ContainsAny(string(e.Dir), ",\"") {
log.Printf("DIR: %s", e.Dir)
cw := csv.NewWriter(w) cw := csv.NewWriter(w)
cw.Write([]string{":" + e.PkgName, e.ImportPath, string(e.Dir), e.Version}) cw.Write([]string{":" + e.PkgName, e.ImportPath, string(e.Dir), e.Version})
cw.Flush() cw.Flush()
@ -248,19 +265,23 @@ func writeIndexToFile(x *Index, fd *os.File) error {
} }
for _, x := range e.Names { for _, x := range e.Names {
fmt.Fprintf(w, "%s\n", x) fmt.Fprintf(w, "%s\n", x)
cnt++
} }
} }
if err := w.Flush(); err != nil { return w.Flush()
return err
}
return nil
} }
// return the base name of the file containing the name of the current index // linkFileBasename returns the base name of the link file in the
func indexNameBase(cachedir Abspath) string { // index directory that holds the name of the payload file for the
// crc64 is a way to convert path names into 16 hex digits. // specified (absolute) Go module cache dir.
h := crc64.Checksum([]byte(cachedir), crc64.MakeTable(crc64.ECMA)) func linkFileBasename(gomodcache string) string {
fname := fmt.Sprintf("index-name-%d-%016x", CurrentVersion, h) // Note: coupled to logic in ./gomodindex/cmd.go. TODO: factor.
return fname h := sha256.Sum256([]byte(gomodcache)) // collision-resistant hash
return fmt.Sprintf("index-name-%d-%032x", CurrentVersion, h)
}
func relative(base, file string) string {
if rel, err := filepath.Rel(base, file); err == nil {
return rel
}
return file
} }

View file

@ -2,17 +2,21 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package modindex contains code for building and searching an index to // Package modindex contains code for building and searching an
// the Go module cache. The directory containing the index, returned by // [Index] of the Go module cache.
// IndexDir(), contains a file index-name-<ver> that contains the name package modindex
// The directory containing the index, returned by
// [IndexDir], contains a file index-name-<ver> that contains the name
// of the current index. We believe writing that short file is atomic. // of the current index. We believe writing that short file is atomic.
// ReadIndex reads that file to get the file name of the index. // [Read] reads that file to get the file name of the index.
// WriteIndex writes an index with a unique name and then // WriteIndex writes an index with a unique name and then
// writes that name into a new version of index-name-<ver>. // writes that name into a new version of index-name-<ver>.
// (<ver> stands for the CurrentVersion of the index format.) // (<ver> stands for the CurrentVersion of the index format.)
package modindex
import ( import (
"maps"
"os"
"path/filepath" "path/filepath"
"slices" "slices"
"strings" "strings"
@ -21,144 +25,95 @@ import (
"golang.org/x/mod/semver" "golang.org/x/mod/semver"
) )
// Create always creates a new index for the go module cache that is in cachedir. // Update updates the index for the specified Go
func Create(cachedir string) error { // module cache directory, creating it as needed.
_, err := indexModCache(cachedir, true) // On success it returns the current index.
return err func Update(gomodcache string) (*Index, error) {
} prev, err := Read(gomodcache)
// Update the index for the go module cache that is in cachedir,
// If there is no existing index it will build one.
// If there are changed directories since the last index, it will
// write a new one and return true. Otherwise it returns false.
func Update(cachedir string) (bool, error) {
return indexModCache(cachedir, false)
}
// indexModCache writes an index current as of when it is called.
// If clear is true the index is constructed from all of GOMODCACHE
// otherwise the index is constructed from the last previous index
// and the updates to the cache. It returns true if it wrote an index,
// false otherwise.
func indexModCache(cachedir string, clear bool) (bool, error) {
cachedir, err := filepath.Abs(cachedir)
if err != nil { if err != nil {
return false, err if !os.IsNotExist(err) {
return nil, err
} }
cd := Abspath(cachedir) prev = nil
future := time.Now().Add(24 * time.Hour) // safely in the future }
ok, err := modindexTimed(future, cd, clear) return update(gomodcache, prev)
}
// update builds, writes, and returns the current index.
//
// If old is nil, the new index is built from all of GOMODCACHE;
// otherwise it is built from the old index plus cache updates
// since the previous index's time.
func update(gomodcache string, old *Index) (*Index, error) {
gomodcache, err := filepath.Abs(gomodcache)
if err != nil { if err != nil {
return false, err return nil, err
} }
return ok, nil new, changed, err := build(gomodcache, old)
}
// modindexTimed writes an index current as of onlyBefore.
// If clear is true the index is constructed from all of GOMODCACHE
// otherwise the index is constructed from the last previous index
// and all the updates to the cache before onlyBefore.
// It returns true if it wrote a new index, false if it wrote nothing.
func modindexTimed(onlyBefore time.Time, cachedir Abspath, clear bool) (bool, error) {
var curIndex *Index
if !clear {
var err error
curIndex, err = ReadIndex(string(cachedir))
if clear && err != nil {
return false, err
}
// TODO(pjw): check that most of those directories still exist
}
cfg := &work{
onlyBefore: onlyBefore,
oldIndex: curIndex,
cacheDir: cachedir,
}
if curIndex != nil {
cfg.onlyAfter = curIndex.Changed
}
if err := cfg.buildIndex(); err != nil {
return false, err
}
if len(cfg.newIndex.Entries) == 0 && curIndex != nil {
// no changes from existing curIndex, don't write a new index
return false, nil
}
if err := cfg.writeIndex(); err != nil {
return false, err
}
return true, nil
}
type work struct {
onlyBefore time.Time // do not use directories later than this
onlyAfter time.Time // only interested in directories after this
// directories from before onlyAfter come from oldIndex
oldIndex *Index
newIndex *Index
cacheDir Abspath
}
func (w *work) buildIndex() error {
// The effective date of the new index should be at least
// slightly earlier than when the directories are scanned
// so set it now.
w.newIndex = &Index{Changed: time.Now(), Cachedir: w.cacheDir}
dirs := findDirs(string(w.cacheDir), w.onlyAfter, w.onlyBefore)
if len(dirs) == 0 {
return nil
}
newdirs, err := byImportPath(dirs)
if err != nil { if err != nil {
return err return nil, err
} }
// for each import path it might occur only in newdirs, if old == nil || changed {
// only in w.oldIndex, or in both. if err := write(gomodcache, new); err != nil {
// If it occurs in both, use the semantically later one return nil, err
if w.oldIndex != nil {
for _, e := range w.oldIndex.Entries {
found, ok := newdirs[e.ImportPath]
if !ok {
w.newIndex.Entries = append(w.newIndex.Entries, e)
continue // use this one, there is no new one
} }
if semver.Compare(found[0].version, e.Version) > 0 { }
// use the new one return new, nil
} else { }
// use the old one, forget the new one
w.newIndex.Entries = append(w.newIndex.Entries, e) // build returns a new index for the specified Go module cache (an
delete(newdirs, e.ImportPath) // absolute path).
//
// If an old index is provided, only directories more recent than it
// that it are scanned; older directories are provided by the old
// Index.
//
// The boolean result indicates whether new entries were found.
func build(gomodcache string, old *Index) (*Index, bool, error) {
// Set the time window.
var start time.Time // = dawn of time
if old != nil {
start = old.ValidAt
}
now := time.Now()
end := now.Add(24 * time.Hour) // safely in the future
// Enumerate GOMODCACHE package directories.
// Choose the best (latest) package for each import path.
pkgDirs := findDirs(gomodcache, start, end)
dirByPath, err := bestDirByImportPath(pkgDirs)
if err != nil {
return nil, false, err
}
// For each import path it might occur only in
// dirByPath, only in old, or in both.
// If both, use the semantically later one.
var entries []Entry
if old != nil {
for _, entry := range old.Entries {
dir, ok := dirByPath[entry.ImportPath]
if !ok || semver.Compare(dir.version, entry.Version) <= 0 {
// New dir is missing or not more recent; use old entry.
entries = append(entries, entry)
delete(dirByPath, entry.ImportPath)
} }
} }
} }
// get symbol information for all the new diredtories
getSymbols(w.cacheDir, newdirs) // Extract symbol information for all the new directories.
// assemble the new index entries newEntries := extractSymbols(gomodcache, maps.Values(dirByPath))
for k, v := range newdirs { entries = append(entries, newEntries...)
d := v[0] slices.SortFunc(entries, func(x, y Entry) int {
pkg, names := processSyms(d.syms) if n := strings.Compare(x.PkgName, y.PkgName); n != 0 {
if pkg == "" {
continue // PJW: does this ever happen?
}
entry := Entry{
PkgName: pkg,
Dir: d.path,
ImportPath: k,
Version: d.version,
Names: names,
}
w.newIndex.Entries = append(w.newIndex.Entries, entry)
}
// sort the entries in the new index
slices.SortFunc(w.newIndex.Entries, func(l, r Entry) int {
if n := strings.Compare(l.PkgName, r.PkgName); n != 0 {
return n return n
} }
return strings.Compare(l.ImportPath, r.ImportPath) return strings.Compare(x.ImportPath, y.ImportPath)
}) })
return nil
}
func (w *work) writeIndex() error { return &Index{
return writeIndex(w.cacheDir, w.newIndex) GOMODCACHE: gomodcache,
ValidAt: now, // time before the directories were scanned
Entries: entries,
}, len(newEntries) > 0, nil
} }

View file

@ -10,11 +10,13 @@ import (
"go/parser" "go/parser"
"go/token" "go/token"
"go/types" "go/types"
"iter"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"slices" "slices"
"strings" "strings"
"sync"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@ -34,41 +36,65 @@ type symbol struct {
sig string // signature information, for F sig string // signature information, for F
} }
// find the symbols for the best directories // extractSymbols returns a (new, unordered) array of Entries, one for
func getSymbols(cd Abspath, dirs map[string][]*directory) { // each provided package directory, describing its exported symbols.
func extractSymbols(cwd string, dirs iter.Seq[directory]) []Entry {
var (
mu sync.Mutex
entries []Entry
)
var g errgroup.Group var g errgroup.Group
g.SetLimit(max(2, runtime.GOMAXPROCS(0)/2)) g.SetLimit(max(2, runtime.GOMAXPROCS(0)/2))
for _, vv := range dirs { for dir := range dirs {
// throttling some day?
d := vv[0]
g.Go(func() error { g.Go(func() error {
thedir := filepath.Join(string(cd), string(d.path)) thedir := filepath.Join(cwd, string(dir.path))
mode := parser.SkipObjectResolution | parser.ParseComments mode := parser.SkipObjectResolution | parser.ParseComments
fi, err := os.ReadDir(thedir) // Parse all Go files in dir and extract symbols.
dirents, err := os.ReadDir(thedir)
if err != nil { if err != nil {
return nil // log this someday? return nil // log this someday?
} }
for _, fx := range fi { var syms []symbol
if !strings.HasSuffix(fx.Name(), ".go") || strings.HasSuffix(fx.Name(), "_test.go") { for _, dirent := range dirents {
if !strings.HasSuffix(dirent.Name(), ".go") ||
strings.HasSuffix(dirent.Name(), "_test.go") {
continue continue
} }
fname := filepath.Join(thedir, fx.Name()) fname := filepath.Join(thedir, dirent.Name())
tr, err := parser.ParseFile(token.NewFileSet(), fname, nil, mode) tr, err := parser.ParseFile(token.NewFileSet(), fname, nil, mode)
if err != nil { if err != nil {
continue // ignore errors, someday log them? continue // ignore errors, someday log them?
} }
d.syms = append(d.syms, getFileExports(tr)...) syms = append(syms, getFileExports(tr)...)
} }
// Create an entry for the package.
pkg, names := processSyms(syms)
if pkg != "" {
mu.Lock()
defer mu.Unlock()
entries = append(entries, Entry{
PkgName: pkg,
Dir: dir.path,
ImportPath: dir.importPath,
Version: dir.version,
Names: names,
})
}
return nil return nil
}) })
} }
g.Wait() g.Wait() // ignore error
return entries
} }
func getFileExports(f *ast.File) []symbol { func getFileExports(f *ast.File) []symbol {
pkg := f.Name.Name pkg := f.Name.Name
if pkg == "main" { if pkg == "main" || pkg == "" {
return nil return nil
} }
var ans []symbol var ans []symbol
@ -202,17 +228,18 @@ func processSyms(syms []symbol) (string, []string) {
pkg := syms[0].pkg pkg := syms[0].pkg
var names []string var names []string
for _, s := range syms { for _, s := range syms {
if s.pkg != pkg {
// Symbols came from two files in same dir
// with different package declarations.
continue
}
var nx string var nx string
if s.pkg == pkg {
if s.sig != "" { if s.sig != "" {
nx = fmt.Sprintf("%s %s %s", s.name, s.kind, s.sig) nx = fmt.Sprintf("%s %s %s", s.name, s.kind, s.sig)
} else { } else {
nx = fmt.Sprintf("%s %s", s.name, s.kind) nx = fmt.Sprintf("%s %s", s.name, s.kind)
} }
names = append(names, nx) names = append(names, nx)
} else {
continue // PJW: do we want to keep track of these?
}
} }
return pkg, names return pkg, names
} }

View file

@ -1,25 +0,0 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package modindex
import (
"strings"
)
// some special types to avoid confusions
// distinguish various types of directory names. It's easy to get confused.
type Abspath string // absolute paths
type Relpath string // paths with GOMODCACHE prefix removed
func toRelpath(cachedir Abspath, s string) Relpath {
if strings.HasPrefix(s, string(cachedir)) {
if s == string(cachedir) {
return Relpath("")
}
return Relpath(s[len(cachedir)+1:])
}
return Relpath(s)
}

28
vendor/modules.txt vendored
View file

@ -619,7 +619,7 @@ github.com/jackc/pgpassfile
# github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 # github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761
## explicit; go 1.14 ## explicit; go 1.14
github.com/jackc/pgservicefile github.com/jackc/pgservicefile
# github.com/jackc/pgx/v5 v5.7.5 # github.com/jackc/pgx/v5 v5.7.6
## explicit; go 1.23.0 ## explicit; go 1.23.0
github.com/jackc/pgx/v5 github.com/jackc/pgx/v5
github.com/jackc/pgx/v5/internal/iobufpool github.com/jackc/pgx/v5/internal/iobufpool
@ -727,8 +727,8 @@ github.com/modern-go/reflect2
# github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
## explicit ## explicit
github.com/munnerz/goautoneg github.com/munnerz/goautoneg
# github.com/ncruces/go-sqlite3 v0.28.0 # github.com/ncruces/go-sqlite3 v0.29.0
## explicit; go 1.23.0 ## explicit; go 1.24.0
github.com/ncruces/go-sqlite3 github.com/ncruces/go-sqlite3
github.com/ncruces/go-sqlite3/driver github.com/ncruces/go-sqlite3/driver
github.com/ncruces/go-sqlite3/embed github.com/ncruces/go-sqlite3/embed
@ -858,7 +858,7 @@ github.com/stretchr/testify/suite
# github.com/subosito/gotenv v1.6.0 # github.com/subosito/gotenv v1.6.0
## explicit; go 1.18 ## explicit; go 1.18
github.com/subosito/gotenv github.com/subosito/gotenv
# github.com/tdewolff/minify/v2 v2.24.2 # github.com/tdewolff/minify/v2 v2.24.3
## explicit; go 1.17 ## explicit; go 1.17
github.com/tdewolff/minify/v2 github.com/tdewolff/minify/v2
github.com/tdewolff/minify/v2/html github.com/tdewolff/minify/v2/html
@ -1190,7 +1190,7 @@ golang.org/x/image/riff
golang.org/x/image/vp8 golang.org/x/image/vp8
golang.org/x/image/vp8l golang.org/x/image/vp8l
golang.org/x/image/webp golang.org/x/image/webp
# golang.org/x/mod v0.26.0 # golang.org/x/mod v0.27.0
## explicit; go 1.23.0 ## explicit; go 1.23.0
golang.org/x/mod/internal/lazyregexp golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/module golang.org/x/mod/module
@ -1214,22 +1214,22 @@ golang.org/x/net/ipv4
golang.org/x/net/ipv6 golang.org/x/net/ipv6
golang.org/x/net/publicsuffix golang.org/x/net/publicsuffix
golang.org/x/net/trace golang.org/x/net/trace
# golang.org/x/oauth2 v0.30.0 # golang.org/x/oauth2 v0.31.0
## explicit; go 1.23.0 ## explicit; go 1.24.0
golang.org/x/oauth2 golang.org/x/oauth2
golang.org/x/oauth2/internal golang.org/x/oauth2/internal
# golang.org/x/sync v0.16.0 # golang.org/x/sync v0.17.0
## explicit; go 1.23.0 ## explicit; go 1.24.0
golang.org/x/sync/errgroup golang.org/x/sync/errgroup
golang.org/x/sync/semaphore golang.org/x/sync/semaphore
# golang.org/x/sys v0.35.0 # golang.org/x/sys v0.36.0
## explicit; go 1.23.0 ## explicit; go 1.24.0
golang.org/x/sys/cpu golang.org/x/sys/cpu
golang.org/x/sys/unix golang.org/x/sys/unix
golang.org/x/sys/windows golang.org/x/sys/windows
golang.org/x/sys/windows/registry golang.org/x/sys/windows/registry
# golang.org/x/text v0.28.0 # golang.org/x/text v0.29.0
## explicit; go 1.23.0 ## explicit; go 1.24.0
golang.org/x/text/cases golang.org/x/text/cases
golang.org/x/text/encoding golang.org/x/text/encoding
golang.org/x/text/encoding/internal golang.org/x/text/encoding/internal
@ -1250,7 +1250,7 @@ golang.org/x/text/transform
golang.org/x/text/unicode/bidi golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm golang.org/x/text/unicode/norm
golang.org/x/text/width golang.org/x/text/width
# golang.org/x/tools v0.35.0 # golang.org/x/tools v0.36.0
## explicit; go 1.23.0 ## explicit; go 1.23.0
golang.org/x/tools/go/ast/astutil golang.org/x/tools/go/ast/astutil
golang.org/x/tools/go/gcexportdata golang.org/x/tools/go/gcexportdata