From dd4db8f5576bd9f0af89f58935323c2716750b14 Mon Sep 17 00:00:00 2001 From: go-jet Date: Mon, 9 Dec 2024 12:26:14 +0100 Subject: [PATCH 1/8] Support pgx scan. Initial commit. --- go.mod | 12 ++- go.sum | 33 +++++--- internal/testutils/test_utils.go | 2 +- qrm/qrm_pgx.go | 108 +++++++++++++++++++++++++ qrm/scan_context.go | 22 +++-- tests/init/init.go | 2 +- tests/postgres/main_test.go | 8 +- tests/postgres/northwind_test.go | 135 +++++++++++++++++++------------ 8 files changed, 241 insertions(+), 81 deletions(-) create mode 100644 qrm/qrm_pgx.go diff --git a/go.mod b/go.mod index 15da241f..bd1ddb89 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/google/uuid v1.6.0 github.com/jackc/pgconn v1.14.3 github.com/jackc/pgtype v1.14.4 - github.com/jackc/pgx/v4 v4.18.3 github.com/lib/pq v1.10.9 github.com/mattn/go-sqlite3 v1.14.24 ) @@ -16,6 +15,7 @@ require ( // used in tests require ( github.com/google/go-cmp v0.6.0 + github.com/jackc/pgx/v5 v5.7.1 github.com/pkg/profile v1.7.0 github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.10.0 @@ -34,13 +34,17 @@ require ( github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.6.1 // indirect github.com/volatiletech/inflect v0.0.1 // indirect github.com/volatiletech/randomize v0.0.1 // indirect github.com/volatiletech/strmangle v0.0.1 // indirect - golang.org/x/crypto v0.20.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index cd810781..9313aca3 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,15 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -64,8 +63,9 @@ github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= @@ -77,22 +77,27 @@ github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08 github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= -github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= -github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= +github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -106,13 +111,14 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= @@ -170,8 +176,9 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -189,6 +196,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -221,8 +230,9 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -241,8 +251,9 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg= gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= diff --git a/internal/testutils/test_utils.go b/internal/testutils/test_utils.go index 817de32e..12f6f33f 100644 --- a/internal/testutils/test_utils.go +++ b/internal/testutils/test_utils.go @@ -127,7 +127,7 @@ func SaveJSONFile(v interface{}, testRelativePath string) { } // AssertJSONFile check if data json representation is the same as json at testRelativePath -func AssertJSONFile(t *testing.T, data interface{}, testRelativePath string) { +func AssertJSONFile(t require.TestingT, data interface{}, testRelativePath string) { filePath := getFullPath(testRelativePath) fileJSONData, err := os.ReadFile(filePath) // #nosec G304 diff --git a/qrm/qrm_pgx.go b/qrm/qrm_pgx.go new file mode 100644 index 00000000..1e0785d9 --- /dev/null +++ b/qrm/qrm_pgx.go @@ -0,0 +1,108 @@ +package qrm + +import ( + "context" + "fmt" + "github.com/go-jet/jet/v2/internal/utils/must" + "github.com/jackc/pgx/v5" + "reflect" +) + +// QueryablePGX interface for pgx Query method +type QueryablePGX interface { + Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) +} + +// QueryPGX executes Query Result Mapping (QRM) of `query` with list of parametrized arguments `arg` over database connection `db` +// using context `ctx` into destination `destPtr`. +// Destination can be either pointer to struct or pointer to slice of structs. +// If destination is pointer to struct and query result set is empty, method returns qrm.ErrNoRows. +func QueryPGX(ctx context.Context, db QueryablePGX, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { + + must.BeInitializedPtr(db, "jet: db is nil") + must.BeInitializedPtr(destPtr, "jet: destination is nil") + must.BeTypeKind(destPtr, reflect.Ptr, "jet: destination has to be a pointer to slice or pointer to struct") + + destinationPtrType := reflect.TypeOf(destPtr) + + if destinationPtrType.Elem().Kind() == reflect.Slice { + rowsProcessed, err := queryToSlicePGX(ctx, db, query, args, destPtr) + if err != nil { + return rowsProcessed, fmt.Errorf("jet: %w", err) + } + return rowsProcessed, nil + } else if destinationPtrType.Elem().Kind() == reflect.Struct { + tempSlicePtrValue := reflect.New(reflect.SliceOf(destinationPtrType)) + tempSliceValue := tempSlicePtrValue.Elem() + + rowsProcessed, err := queryToSlicePGX(ctx, db, query, args, tempSlicePtrValue.Interface()) + + if err != nil { + return rowsProcessed, fmt.Errorf("jet: %w", err) + } + + if rowsProcessed == 0 { + return 0, ErrNoRows + } + + // edge case when row result set contains only NULLs. + if tempSliceValue.Len() == 0 { + return rowsProcessed, nil + } + + structValue := reflect.ValueOf(destPtr).Elem() + firstTempStruct := tempSliceValue.Index(0).Elem() + + if structValue.Type().AssignableTo(firstTempStruct.Type()) { + structValue.Set(tempSliceValue.Index(0).Elem()) + } + return rowsProcessed, nil + } else { + panic("jet: destination has to be a pointer to slice or pointer to struct") + } +} + +func queryToSlicePGX(ctx context.Context, db QueryablePGX, query string, args []interface{}, slicePtr interface{}) (rowsProcessed int64, err error) { + if ctx == nil { + ctx = context.Background() + } + + rows, err := db.Query(ctx, query, args...) + + if err != nil { + return + } + defer rows.Close() + + scanContext, err := NewScanContextPGXv5(rows) + + if err != nil { + return + } + + if len(scanContext.row) == 0 { + return + } + + slicePtrValue := reflect.ValueOf(slicePtr) + + for rows.Next() { + err = rows.Scan(scanContext.row...) + + if err != nil { + return scanContext.rowNum, err + } + + scanContext.rowNum++ + + _, err = mapRowToSlice(scanContext, "", slicePtrValue, nil) + + if err != nil { + return scanContext.rowNum, err + } + } + + rows.Close() + + return scanContext.rowNum, rows.Err() +} diff --git a/qrm/scan_context.go b/qrm/scan_context.go index 7a3c5381..d2449e8f 100644 --- a/qrm/scan_context.go +++ b/qrm/scan_context.go @@ -3,6 +3,7 @@ package qrm import ( "database/sql" "fmt" + "github.com/jackc/pgx/v5" "reflect" "strings" ) @@ -20,6 +21,15 @@ type ScanContext struct { typesVisited typeStack // to prevent circular dependency scan } +func NewScanContextPGXv5(rows pgx.Rows) (*ScanContext, error) { + var aliases []string + for _, fieldDesc := range rows.FieldDescriptions() { + aliases = append(aliases, fieldDesc.Name) + } + + return newScanContext(aliases), nil +} + // NewScanContext creates new ScanContext from rows func NewScanContext(rows *sql.Rows) (*ScanContext, error) { aliases, err := rows.Columns() @@ -28,12 +38,10 @@ func NewScanContext(rows *sql.Rows) (*ScanContext, error) { return nil, err } - columnTypes, err := rows.ColumnTypes() - - if err != nil { - return nil, err - } + return newScanContext(aliases), nil +} +func newScanContext(aliases []string) *ScanContext { commonIdentToColumnIndex := map[string]int{} for i, alias := range aliases { @@ -48,7 +56,7 @@ func NewScanContext(rows *sql.Rows) (*ScanContext, error) { } return &ScanContext{ - row: createScanSlice(len(columnTypes)), + row: createScanSlice(len(aliases)), uniqueDestObjectsMap: make(map[string]int), groupKeyInfoCache: make(map[string]groupKeyInfo), @@ -57,7 +65,7 @@ func NewScanContext(rows *sql.Rows) (*ScanContext, error) { typeInfoMap: make(map[string]typeInfo), typesVisited: newTypeStack(), - }, nil + } } func createScanSlice(columnCount int) []interface{} { diff --git a/tests/init/init.go b/tests/init/init.go index 8aac3d11..ee183b43 100644 --- a/tests/init/init.go +++ b/tests/init/init.go @@ -17,7 +17,7 @@ import ( "github.com/go-jet/jet/v2/tests/dbconfig" _ "github.com/go-sql-driver/mysql" - _ "github.com/jackc/pgx/v4/stdlib" + _ "github.com/jackc/pgx/v5/stdlib" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" diff --git a/tests/postgres/main_test.go b/tests/postgres/main_test.go index 46dfd94b..0ad1e3cd 100644 --- a/tests/postgres/main_test.go +++ b/tests/postgres/main_test.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/go-jet/jet/v2/stmtcache" "github.com/go-jet/jet/v2/tests/internal/utils/repo" - "github.com/jackc/pgx/v4/stdlib" + "github.com/jackc/pgx/v5/stdlib" "os" "runtime" "testing" @@ -17,7 +17,7 @@ import ( "github.com/pkg/profile" "github.com/stretchr/testify/require" - _ "github.com/jackc/pgx/v4/stdlib" + _ "github.com/jackc/pgx/v5/stdlib" ) var db *stmtcache.DB @@ -48,7 +48,7 @@ func TestMain(m *testing.M) { setTestRoot() - for _, driverName := range []string{"postgres", "pgx"} { + for _, driverName := range []string{"postgres"} { fmt.Printf("\nRunning postgres tests for driver: %s, caching enabled: %t \n", driverName, withStatementCaching) @@ -119,7 +119,7 @@ func init() { }) } -func requireLogged(t *testing.T, statement postgres.Statement) { +func requireLogged(t require.TestingT, statement postgres.Statement) { query, args := statement.Sql() require.Equal(t, loggedSQL, query) require.Equal(t, loggedSQLArgs, args) diff --git a/tests/postgres/northwind_test.go b/tests/postgres/northwind_test.go index 7aad0d4f..25c6c3c5 100644 --- a/tests/postgres/northwind_test.go +++ b/tests/postgres/northwind_test.go @@ -1,69 +1,98 @@ package postgres import ( - "github.com/go-jet/jet/v2/internal/testutils" + "context" . "github.com/go-jet/jet/v2/postgres" + "github.com/go-jet/jet/v2/qrm" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/northwind/model" . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/northwind/table" + "github.com/jackc/pgx/v5" "github.com/stretchr/testify/require" "testing" ) -func TestNorthwindJoinEverything(t *testing.T) { - - stmt := - SELECT( - Customers.AllColumns, - CustomerDemographics.AllColumns, - Orders.AllColumns, - Shippers.AllColumns, - OrderDetails.AllColumns, - Products.AllColumns, - Categories.AllColumns, - Suppliers.AllColumns, - ).FROM( - Customers. - LEFT_JOIN(CustomerCustomerDemo, Customers.CustomerID.EQ(CustomerCustomerDemo.CustomerID)). - LEFT_JOIN(CustomerDemographics, CustomerCustomerDemo.CustomerTypeID.EQ(CustomerDemographics.CustomerTypeID)). - LEFT_JOIN(Orders, Orders.CustomerID.EQ(Customers.CustomerID)). - LEFT_JOIN(Shippers, Orders.ShipVia.EQ(Shippers.ShipperID)). - LEFT_JOIN(OrderDetails, Orders.OrderID.EQ(OrderDetails.OrderID)). - LEFT_JOIN(Products, OrderDetails.ProductID.EQ(Products.ProductID)). - LEFT_JOIN(Categories, Products.CategoryID.EQ(Categories.CategoryID)). - LEFT_JOIN(Suppliers, Products.SupplierID.EQ(Suppliers.SupplierID)). - LEFT_JOIN(Employees, Orders.EmployeeID.EQ(Employees.EmployeeID)). - LEFT_JOIN(EmployeeTerritories, EmployeeTerritories.EmployeeID.EQ(Employees.EmployeeID)). - LEFT_JOIN(Territories, EmployeeTerritories.TerritoryID.EQ(Territories.TerritoryID)). - LEFT_JOIN(Region, Territories.RegionID.EQ(Region.RegionID)), - ).ORDER_BY(Customers.CustomerID, Orders.OrderID, Products.ProductID) - - var dest []struct { - model.Customers - - Demographics model.CustomerDemographics - - Orders []struct { - model.Orders - - Shipper model.Shippers - - Details struct { - model.OrderDetails - - Products []struct { - model.Products - - Category model.Categories - Supplier model.Suppliers +var pgxDB *pgx.Conn + +func init() { + var err error + pgxDB, err = pgx.Connect(context.Background(), getConnectionString()) + + if err != nil { + panic(err) + } +} + +func BenchmarkNorthwindJoinEverythingPGX(b *testing.B) { + + benchQueryScan(b, func(stmt Statement, dest any) { + sql, args := stmt.Sql() + _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, dest) + require.NoError(b, err) + }) +} + +func BenchmarkNorthwindJoinEverythingPQ(b *testing.B) { + + benchQueryScan(b, func(stmt Statement, dest any) { + err := stmt.Query(db, dest) + require.NoError(b, err) + }) +} + +func benchQueryScan(b *testing.B, queryFunc func(statement Statement, dest any)) { + stmt := SELECT( + Customers.AllColumns, + CustomerDemographics.AllColumns, + Orders.AllColumns, + Shippers.AllColumns, + OrderDetails.AllColumns, + Products.AllColumns, + Categories.AllColumns, + Suppliers.AllColumns, + ).FROM( + Customers. + LEFT_JOIN(CustomerCustomerDemo, Customers.CustomerID.EQ(CustomerCustomerDemo.CustomerID)). + LEFT_JOIN(CustomerDemographics, CustomerCustomerDemo.CustomerTypeID.EQ(CustomerDemographics.CustomerTypeID)). + LEFT_JOIN(Orders, Orders.CustomerID.EQ(Customers.CustomerID)). + LEFT_JOIN(Shippers, Orders.ShipVia.EQ(Shippers.ShipperID)). + LEFT_JOIN(OrderDetails, Orders.OrderID.EQ(OrderDetails.OrderID)). + LEFT_JOIN(Products, OrderDetails.ProductID.EQ(Products.ProductID)). + LEFT_JOIN(Categories, Products.CategoryID.EQ(Categories.CategoryID)). + LEFT_JOIN(Suppliers, Products.SupplierID.EQ(Suppliers.SupplierID)), + ).ORDER_BY( + Customers.CustomerID, + Orders.OrderID, + Products.ProductID, + ) + + for i := 0; i < b.N; i++ { + var dest []struct { + model.Customers + + Demographics model.CustomerDemographics + + Orders []struct { + model.Orders + + Shipper model.Shippers + + Details struct { + model.OrderDetails + + Products []struct { + model.Products + + Category model.Categories + Supplier model.Suppliers + } } } } - } - err := stmt.Query(db, &dest) - require.NoError(t, err) + queryFunc(stmt, &dest) - //jsonSave("./testdata/northwind-all.json", dest) - testutils.AssertJSONFile(t, dest, "./testdata/results/postgres/northwind-all.json") - requireLogged(t, stmt) + //jsonSave("./testdata/northwind-all.json", dest) + //testutils.AssertJSONFile(b, dest, "./testdata/results/postgres/northwind-all.json") + //requireLogged(b, stmt) + } } From b5751393530969221e52b90e8fb666c80881d864 Mon Sep 17 00:00:00 2001 From: go-jet Date: Wed, 2 Apr 2025 12:35:00 +0200 Subject: [PATCH 2/8] Quick fix for scanning UUID types using non-standard golang pgx driver. --- go.mod | 9 +- go.sum | 27 ++-- qrm/qrm.go | 36 +++++- qrm/scan_context.go | 2 +- tests/postgres/northwind_test.go | 5 +- tests/postgres/pgx_test.go | 204 +++++++++++++++++++++++++++++++ 6 files changed, 262 insertions(+), 21 deletions(-) create mode 100644 tests/postgres/pgx_test.go diff --git a/go.mod b/go.mod index 16bdc1be..83bd22d2 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( require ( github.com/bytedance/sonic v1.13.2 github.com/google/go-cmp v0.7.0 - github.com/jackc/pgx/v5 v5.7.1 + github.com/jackc/pgx/v5 v5.7.4 github.com/pkg/profile v1.7.0 github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.10.0 @@ -39,20 +39,17 @@ require ( github.com/jackc/pgproto3/v2 v2.3.3 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.6.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/volatiletech/inflect v0.0.1 // indirect github.com/volatiletech/randomize v0.0.1 // indirect github.com/volatiletech/strmangle v0.0.1 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.18.0 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/crypto v0.31.0 // indirect + golang.org/x/sync v0.10.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e43b2634..22df931c 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,6 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= @@ -14,11 +13,11 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -72,8 +71,9 @@ github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= @@ -85,25 +85,30 @@ github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08 github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= -github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= -github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg= +github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -117,13 +122,14 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= @@ -205,6 +211,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -258,8 +266,9 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg= gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= diff --git a/qrm/qrm.go b/qrm/qrm.go index 6fc081f0..f603e600 100644 --- a/qrm/qrm.go +++ b/qrm/qrm.go @@ -3,6 +3,7 @@ package qrm import ( "context" "database/sql" + "database/sql/driver" "encoding/json" "errors" "fmt" @@ -416,10 +417,22 @@ func mapRowToStruct( switch fieldMappingInfo.Type { case implementsScanner: initializeValueIfNilPtr(fieldValue) - fieldScanner := getScanner(fieldValue) - value := scannedValue.Interface() + if pgxUUIDPatch(fieldValue, scannedValue) { + continue + } + + if valuer, ok := value.(driver.Valuer); ok { + value, err = valuer.Value() + + if err != nil { + return updated, err + } + } + + fieldScanner := getScanner(fieldValue) + err := fieldScanner.Scan(value) if err != nil { @@ -457,6 +470,25 @@ func qrmAssignError(scannedValue reflect.Value, field reflect.StructField, err e field.Name, field.Type.String(), err) } +var uuidLikeType = reflect.TypeOf([16]byte{}) + +func pgxUUIDPatch(fieldValue, value reflect.Value) bool { + fieldValue = reflect.Indirect(fieldValue) + value = reflect.Indirect(value) + + if fieldValue.Type() != uuidLikeType && value.Type() != uuidLikeType { + return false + } + + if !fieldValue.CanSet() { + return false + } + + fieldValue.Set(value) + + return true +} + func mapRowToDestinationValue( scanContext *ScanContext, groupKey string, diff --git a/qrm/scan_context.go b/qrm/scan_context.go index c59265b2..266c5d1a 100644 --- a/qrm/scan_context.go +++ b/qrm/scan_context.go @@ -70,7 +70,7 @@ func newScanContext(aliases []string) *ScanContext { columnAlias: aliases, columnIndexRead: make([]bool, len(aliases)), - }, nil + } } func (s *ScanContext) EnsureEveryColumnRead() { diff --git a/tests/postgres/northwind_test.go b/tests/postgres/northwind_test.go index 99e76f71..aeda9463 100644 --- a/tests/postgres/northwind_test.go +++ b/tests/postgres/northwind_test.go @@ -4,10 +4,8 @@ import ( "github.com/bytedance/sonic" "github.com/go-jet/jet/v2/internal/testutils" . "github.com/go-jet/jet/v2/postgres" - "github.com/go-jet/jet/v2/qrm" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/northwind/model" . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/northwind/table" - "github.com/jackc/pgx/v5" "github.com/stretchr/testify/require" "testing" ) @@ -95,7 +93,8 @@ func testNorthwindJoinEverything(t require.TestingT) { var dest Dest - queryFunc(stmt, &dest) + err := stmt.Query(db, &dest) + require.NoError(t, err) //testutils.SaveJSONFile(dest, "./testdata/results/postgres/northwind-all.json") testutils.AssertJSONFile(t, dest, "./testdata/results/postgres/northwind-all.json") diff --git a/tests/postgres/pgx_test.go b/tests/postgres/pgx_test.go new file mode 100644 index 00000000..cbe6ce00 --- /dev/null +++ b/tests/postgres/pgx_test.go @@ -0,0 +1,204 @@ +package postgres + +import ( + "context" + "fmt" + "github.com/go-jet/jet/v2/internal/testutils" + . "github.com/go-jet/jet/v2/postgres" + "github.com/go-jet/jet/v2/qrm" + . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/northwind/table" + model2 "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/model" + "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table" + "github.com/google/uuid" + "github.com/jackc/pgx/v5" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/require" + "testing" +) + +var pgxDB *pgx.Conn + +func init() { + var err error + pgxDB, err = pgx.Connect(context.Background(), getConnectionString()) + + if err != nil { + panic(err) + } +} + +func BenchmarkNorthwindJoinEverythingPGX(b *testing.B) { + for i := 0; i < b.N; i++ { + testNorthwindJoinEverythingCustomScan(b, func(stmt Statement, dest any) { + sql, args := stmt.Sql() + _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, dest) + require.NoError(b, err) + }) + } +} + +func BenchmarkNorthwindJoinEverythingPQ(b *testing.B) { + for i := 0; i < b.N; i++ { + testNorthwindJoinEverythingCustomScan(b, func(stmt Statement, dest any) { + err := stmt.Query(db, dest) + require.NoError(b, err) + }) + } +} + +func TestNorthwindJoinEverythingPQ(t *testing.T) { + testNorthwindJoinEverythingCustomScan(t, func(stmt Statement, dest any) { + err := stmt.Query(db, dest) + require.NoError(t, err) + }) +} + +func TestNorthwindJoinEverythingPGX(t *testing.T) { + testNorthwindJoinEverythingCustomScan(t, func(stmt Statement, dest any) { + sql, args := stmt.Sql() + _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, dest) + require.NoError(t, err) + }) +} + +func testNorthwindJoinEverythingCustomScan(b require.TestingT, queryFunc func(statement Statement, dest any)) { + stmt := + SELECT( + Customers.AllColumns, + CustomerDemographics.AllColumns, + Orders.AllColumns, + Shippers.AllColumns, + OrderDetails.AllColumns, + Products.AllColumns, + Categories.AllColumns, + Suppliers.AllColumns, + Employees.AllColumns, + Territories.AllColumns, + Region.AllColumns, + ).FROM( + Customers. + LEFT_JOIN(CustomerCustomerDemo, Customers.CustomerID.EQ(CustomerCustomerDemo.CustomerID)). + LEFT_JOIN(CustomerDemographics, CustomerCustomerDemo.CustomerTypeID.EQ(CustomerDemographics.CustomerTypeID)). + LEFT_JOIN(Orders, Orders.CustomerID.EQ(Customers.CustomerID)). + LEFT_JOIN(Shippers, Orders.ShipVia.EQ(Shippers.ShipperID)). + LEFT_JOIN(OrderDetails, Orders.OrderID.EQ(OrderDetails.OrderID)). + LEFT_JOIN(Products, OrderDetails.ProductID.EQ(Products.ProductID)). + LEFT_JOIN(Categories, Products.CategoryID.EQ(Categories.CategoryID)). + LEFT_JOIN(Suppliers, Products.SupplierID.EQ(Suppliers.SupplierID)). + LEFT_JOIN(Employees, Orders.EmployeeID.EQ(Employees.EmployeeID)). + LEFT_JOIN(EmployeeTerritories, EmployeeTerritories.EmployeeID.EQ(Employees.EmployeeID)). + LEFT_JOIN(Territories, EmployeeTerritories.TerritoryID.EQ(Territories.TerritoryID)). + LEFT_JOIN(Region, Territories.RegionID.EQ(Region.RegionID)), + ).ORDER_BY( + Customers.CustomerID, + Orders.OrderID, + Products.ProductID, + Territories.TerritoryID, + ) + + var dest Dest + + queryFunc(stmt, &dest) + + testutils.AssertJSONFile(b, dest, "./testdata/results/postgres/northwind-all.json") +} + +func TestUUIDTypePGX(t *testing.T) { + id := uuid.MustParse("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11") + + stmt := SELECT(table.AllTypes.UUID, table.AllTypes.UUIDPtr). + FROM(table.AllTypes). + WHERE(table.AllTypes.UUID.EQ(UUID(id))) + + testutils.AssertDebugStatementSql(t, stmt, ` +SELECT all_types.uuid AS "all_types.uuid", + all_types.uuid_ptr AS "all_types.uuid_ptr" +FROM test_sample.all_types +WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; +`, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11") + + result := model2.AllTypes{} + + //err := query.Query(db, &result) + + sql, args := stmt.Sql() + _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, &result) + + require.NoError(t, err) + require.Equal(t, result.UUID, uuid.MustParse("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")) + testutils.AssertDeepEqual(t, result.UUIDPtr, testutils.UUIDPtr("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")) +} + +func TestPGXScannerType(t *testing.T) { + + type floats struct { + Numeric decimal.Decimal + NumericPtr decimal.Decimal + Decimal decimal.Decimal + DecimalPtr decimal.Decimal + } + + query := SELECT( + table.Floats.AllColumns, + ).FROM( + table.Floats, + ).WHERE(table.Floats.Decimal.EQ(Decimal("1.11111111111111111111"))) + + var result floats + + sql, args := query.Sql() + _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, &result) + require.NoError(t, err) + + require.Equal(t, "1.11111111111111111111", result.Decimal.String()) + require.Equal(t, "0", result.DecimalPtr.String()) // NULL + require.Equal(t, "2.22222222222222222222", result.Numeric.String()) + require.Equal(t, "0", result.NumericPtr.String()) // NULL +} + +func TestAllTypesSelectPGX(t *testing.T) { + var dest []model2.AllTypes + + stmt := SELECT(table.AllTypes.AllColumns.Except( + table.AllTypes.Decimal, + table.AllTypes.DecimalPtr, + table.AllTypes.Numeric, + table.AllTypes.NumericPtr, + table.AllTypes.PointPtr, + table.AllTypes.Bit, + table.AllTypes.BitPtr, + table.AllTypes.BitVarying, + table.AllTypes.BitVaryingPtr, + table.AllTypes.JSON, + table.AllTypes.JSONPtr, + table.AllTypes.Jsonb, + table.AllTypes.JsonbPtr, + table.AllTypes.JsonbArray, + table.AllTypes.TextArray, + table.AllTypes.TextArrayPtr, + table.AllTypes.TextMultiDimArray, + table.AllTypes.TextMultiDimArrayPtr, + table.AllTypes.Tsvector, + table.AllTypes.TsvectorPtr, + table.AllTypes.IntegerArray, + table.AllTypes.IntegerArrayPtr, + table.AllTypes.Interval, + table.AllTypes.IntervalPtr, + + table.AllTypes.Time, + table.AllTypes.TimePtr, + table.AllTypes.Timez, + table.AllTypes.TimezPtr, + table.AllTypes.Mood, + table.AllTypes.MoodPtr, + )).FROM( + table.AllTypes, + ).LIMIT(2) + + fmt.Println(stmt.DebugSql()) + + sql, args := stmt.Sql() + _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, &dest) + + require.NoError(t, err) +} From 16cbf3fa2a7101dafd4074771644ab334bdc54b1 Mon Sep 17 00:00:00 2001 From: DevX86 Date: Sun, 27 Apr 2025 21:51:31 -0400 Subject: [PATCH 3/8] feat: add preliminary json support for qrm_pgx. --- qrm/qrm_pgx.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/qrm/qrm_pgx.go b/qrm/qrm_pgx.go index 1e0785d9..6757e59f 100644 --- a/qrm/qrm_pgx.go +++ b/qrm/qrm_pgx.go @@ -8,9 +8,63 @@ import ( "reflect" ) -// QueryablePGX interface for pgx Query method +// QueryablePGX interface for pgx QueryContext method type QueryablePGX interface { - Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) + QueryContext(ctx context.Context, query string, args ...any) (pgx.Rows, error) +} + +// QueryPGXJsonObj executes a SQL query that returns a JSON object, unmarshals the result into the provided destination, +// and returns the number of rows processed. +// +// The query must return exactly one row with a single column; otherwise, an error is returned. +// +// Parameters: +// +// ctx - The context for managing query execution (timeouts, cancellations). +// db - The database connection or transaction that implements the QueryablePGX interface. +// query - The SQL query string to be executed. +// args - A slice of arguments to be used with the query. +// destPtr - A pointer to the variable where the unmarshaled JSON result will be stored. +// The destination should be a pointer to a struct or map[string]any. +// +// Returns: +// +// rowsProcessed - The number of rows processed by the query execution. +// err - An error if query execution or unmarshaling fails. +func QueryPGXJsonObj(ctx context.Context, db QueryablePGX, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { + must.BeInitializedPtr(destPtr, "jet: destination is nil") + must.BeTypeKind(destPtr, reflect.Ptr, jsonDestObjErr) + destType := reflect.TypeOf(destPtr).Elem() + must.BeTrue(destType.Kind() == reflect.Struct || destType.Kind() == reflect.Map, jsonDestObjErr) + + return queryPGXJson(ctx, db, query, args, destPtr) +} + +// QueryPGXJsonArr executes a SQL query that returns a JSON array, unmarshals the result into the provided destination, +// and returns the number of rows processed. +// +// The query must return exactly one row with a single column; otherwise, an error is returned. +// +// Parameters: +// +// ctx - The context for managing query execution (timeouts, cancellations). +// db - The database connection or transaction that implements the QueryablePGX interface. +// query - The SQL query string to be executed. +// args - A slice of arguments to be used with the query. +// destPtr - A pointer to the variable where the unmarshaled JSON array will be stored. +// The destination should be a pointer to a slice of structs or []map[string]any. +// +// Returns: +// +// rowsProcessed - The number of rows processed by the query execution. +// err - An error if query execution or unmarshaling fails. +func QueryPGXJsonArr(ctx context.Context, db QueryablePGX, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { + must.BeInitializedPtr(destPtr, "jet: destination is nil") + must.BeTypeKind(destPtr, reflect.Ptr, jsonDestArrErr) + destType := reflect.TypeOf(destPtr).Elem() + must.BeTrue(destType.Kind() == reflect.Slice, jsonDestArrErr) + + return queryPGXJson(ctx, db, query, args, destPtr) } // QueryPGX executes Query Result Mapping (QRM) of `query` with list of parametrized arguments `arg` over database connection `db` @@ -67,7 +121,7 @@ func queryToSlicePGX(ctx context.Context, db QueryablePGX, query string, args [] ctx = context.Background() } - rows, err := db.Query(ctx, query, args...) + rows, err := db.QueryContext(ctx, query, args...) if err != nil { return @@ -106,3 +160,49 @@ func queryToSlicePGX(ctx context.Context, db QueryablePGX, query string, args [] return scanContext.rowNum, rows.Err() } + +func queryPGXJson(ctx context.Context, db QueryablePGX, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { + must.BeInitializedPtr(db, "jet: db is nil") + + var rows pgx.Rows + rows, err = db.QueryContext(ctx, query, args...) + + if err != nil { + return 0, err + } + + defer rows.Close() + + if !rows.Next() { + err = rows.Err() + if err != nil { + return 0, err + } + return 0, ErrNoRows + } + + var jsonData []byte + err = rows.Scan(&jsonData) + + if err != nil { + return 1, err + } + + if jsonData == nil { + return 1, nil + } + + err = GlobalConfig.JsonUnmarshalFunc(jsonData, &destPtr) + + if err != nil { + return 1, fmt.Errorf("jet: invalid json, %w", err) + } + + if rows.Next() { + return 1, fmt.Errorf("jet: query returned more then one row") + } + + rows.Close() + + return 1, nil +} From 837bd92f93870fc897747bd25e2cf48f66167498 Mon Sep 17 00:00:00 2001 From: DevX86 Date: Sun, 4 May 2025 03:59:14 -0400 Subject: [PATCH 4/8] fix: update queryablePGX interface to be accurate to pgx. --- qrm/qrm_pgx.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qrm/qrm_pgx.go b/qrm/qrm_pgx.go index 6757e59f..e1c231e4 100644 --- a/qrm/qrm_pgx.go +++ b/qrm/qrm_pgx.go @@ -8,9 +8,9 @@ import ( "reflect" ) -// QueryablePGX interface for pgx QueryContext method +// QueryablePGX interface for pgx Query method type QueryablePGX interface { - QueryContext(ctx context.Context, query string, args ...any) (pgx.Rows, error) + Query(ctx context.Context, query string, args ...any) (pgx.Rows, error) } // QueryPGXJsonObj executes a SQL query that returns a JSON object, unmarshals the result into the provided destination, @@ -121,7 +121,7 @@ func queryToSlicePGX(ctx context.Context, db QueryablePGX, query string, args [] ctx = context.Background() } - rows, err := db.QueryContext(ctx, query, args...) + rows, err := db.Query(ctx, query, args...) if err != nil { return @@ -165,7 +165,7 @@ func queryPGXJson(ctx context.Context, db QueryablePGX, query string, args []int must.BeInitializedPtr(db, "jet: db is nil") var rows pgx.Rows - rows, err = db.QueryContext(ctx, query, args...) + rows, err = db.Query(ctx, query, args...) if err != nil { return 0, err From 1817aa95b984bb691b0147cd5892e9ff11763623 Mon Sep 17 00:00:00 2001 From: go-jet Date: Wed, 8 Oct 2025 13:54:38 +0200 Subject: [PATCH 5/8] Add pgxV5 jet utility functions. --- internal/jet/statement.go | 17 +++- pgxV5/pgxV5.go | 21 +++++ qrm/{qrm_pgx.go => qrm_pgx_v5.go} | 28 +++--- tests/postgres/alltypes_test.go | 5 - tests/postgres/pgx_test.go | 144 +++++++++++++++++++++++++---- tests/postgres/select_json_test.go | 2 +- tests/testdata | 2 +- 7 files changed, 173 insertions(+), 46 deletions(-) create mode 100644 pgxV5/pgxV5.go rename qrm/{qrm_pgx.go => qrm_pgx_v5.go} (78%) diff --git a/internal/jet/statement.go b/internal/jet/statement.go index b2f951a1..13053afa 100644 --- a/internal/jet/statement.go +++ b/internal/jet/statement.go @@ -41,6 +41,8 @@ type Statement interface { // Rows executes statements over db connection/transaction and returns rows Rows(ctx context.Context, db qrm.Queryable) (*Rows, error) + + Type() StatementType } // Rows wraps sql.Rows type with a support for query result mapping @@ -104,7 +106,7 @@ func (s *statementInterfaceImpl) Query(db qrm.Queryable, destination interface{} } func (s *statementInterfaceImpl) QueryContext(ctx context.Context, db qrm.Queryable, destination interface{}) error { - return s.query(ctx, func(query string, args []interface{}) (int64, error) { + return QueryWithLogging(ctx, s, func(query string, args []interface{}) (int64, error) { switch s.statementType { case SelectJsonObjStatementType: return qrm.QueryJsonObj(ctx, db, query, args, destination) @@ -116,13 +118,14 @@ func (s *statementInterfaceImpl) QueryContext(ctx context.Context, db qrm.Querya }) } -func (s *statementInterfaceImpl) query( +func QueryWithLogging( ctx context.Context, + stmt Statement, queryFunc func(query string, args []interface{}) (int64, error), ) error { - query, args := s.Sql() + query, args := stmt.Sql() - callLogger(ctx, s) + callLogger(ctx, stmt) var rowsProcessed int64 var err error @@ -132,7 +135,7 @@ func (s *statementInterfaceImpl) query( }) callQueryLoggerFunc(ctx, QueryInfo{ - Statement: s, + Statement: stmt, RowsProcessed: rowsProcessed, Duration: duration, Err: err, @@ -212,6 +215,10 @@ func duration(f func()) time.Duration { return time.Since(start) } +func (s *statementInterfaceImpl) Type() StatementType { + return s.statementType +} + // ExpressionStatement interfacess type ExpressionStatement interface { Expression diff --git a/pgxV5/pgxV5.go b/pgxV5/pgxV5.go new file mode 100644 index 00000000..be675ff2 --- /dev/null +++ b/pgxV5/pgxV5.go @@ -0,0 +1,21 @@ +package pgxV5 + +import ( + "context" + "github.com/go-jet/jet/v2/internal/jet" + "github.com/go-jet/jet/v2/postgres" + "github.com/go-jet/jet/v2/qrm" +) + +func Query(ctx context.Context, statement postgres.Statement, pgx qrm.QueryablePgxV5, dest any) error { + return jet.QueryWithLogging(ctx, statement, func(query string, args []interface{}) (int64, error) { + switch statement.Type() { + case jet.SelectJsonObjStatementType: + return qrm.QueryJsonObjPgxV5(ctx, pgx, query, args, dest) + case jet.SelectJsonArrStatementType: + return qrm.QueryJsonArrPgxV5(ctx, pgx, query, args, dest) + default: + return qrm.QueryPgxV5(ctx, pgx, query, args, dest) + } + }) +} diff --git a/qrm/qrm_pgx.go b/qrm/qrm_pgx_v5.go similarity index 78% rename from qrm/qrm_pgx.go rename to qrm/qrm_pgx_v5.go index e1c231e4..413f9811 100644 --- a/qrm/qrm_pgx.go +++ b/qrm/qrm_pgx_v5.go @@ -8,12 +8,12 @@ import ( "reflect" ) -// QueryablePGX interface for pgx Query method -type QueryablePGX interface { +// QueryablePgxV5 interface for pgx Query method +type QueryablePgxV5 interface { Query(ctx context.Context, query string, args ...any) (pgx.Rows, error) } -// QueryPGXJsonObj executes a SQL query that returns a JSON object, unmarshals the result into the provided destination, +// QueryJsonObjPgxV5 executes a SQL query that returns a JSON object, unmarshals the result into the provided destination, // and returns the number of rows processed. // // The query must return exactly one row with a single column; otherwise, an error is returned. @@ -31,16 +31,16 @@ type QueryablePGX interface { // // rowsProcessed - The number of rows processed by the query execution. // err - An error if query execution or unmarshaling fails. -func QueryPGXJsonObj(ctx context.Context, db QueryablePGX, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { +func QueryJsonObjPgxV5(ctx context.Context, db QueryablePgxV5, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { must.BeInitializedPtr(destPtr, "jet: destination is nil") must.BeTypeKind(destPtr, reflect.Ptr, jsonDestObjErr) destType := reflect.TypeOf(destPtr).Elem() must.BeTrue(destType.Kind() == reflect.Struct || destType.Kind() == reflect.Map, jsonDestObjErr) - return queryPGXJson(ctx, db, query, args, destPtr) + return queryJsonPgxV5(ctx, db, query, args, destPtr) } -// QueryPGXJsonArr executes a SQL query that returns a JSON array, unmarshals the result into the provided destination, +// QueryJsonArrPgxV5 executes a SQL query that returns a JSON array, unmarshals the result into the provided destination, // and returns the number of rows processed. // // The query must return exactly one row with a single column; otherwise, an error is returned. @@ -58,20 +58,20 @@ func QueryPGXJsonObj(ctx context.Context, db QueryablePGX, query string, args [] // // rowsProcessed - The number of rows processed by the query execution. // err - An error if query execution or unmarshaling fails. -func QueryPGXJsonArr(ctx context.Context, db QueryablePGX, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { +func QueryJsonArrPgxV5(ctx context.Context, db QueryablePgxV5, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { must.BeInitializedPtr(destPtr, "jet: destination is nil") must.BeTypeKind(destPtr, reflect.Ptr, jsonDestArrErr) destType := reflect.TypeOf(destPtr).Elem() must.BeTrue(destType.Kind() == reflect.Slice, jsonDestArrErr) - return queryPGXJson(ctx, db, query, args, destPtr) + return queryJsonPgxV5(ctx, db, query, args, destPtr) } -// QueryPGX executes Query Result Mapping (QRM) of `query` with list of parametrized arguments `arg` over database connection `db` +// QueryPgxV5 executes Query Result Mapping (QRM) of `query` with list of parametrized arguments `arg` over database connection `db` // using context `ctx` into destination `destPtr`. // Destination can be either pointer to struct or pointer to slice of structs. // If destination is pointer to struct and query result set is empty, method returns qrm.ErrNoRows. -func QueryPGX(ctx context.Context, db QueryablePGX, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { +func QueryPgxV5(ctx context.Context, db QueryablePgxV5, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { must.BeInitializedPtr(db, "jet: db is nil") must.BeInitializedPtr(destPtr, "jet: destination is nil") @@ -80,7 +80,7 @@ func QueryPGX(ctx context.Context, db QueryablePGX, query string, args []interfa destinationPtrType := reflect.TypeOf(destPtr) if destinationPtrType.Elem().Kind() == reflect.Slice { - rowsProcessed, err := queryToSlicePGX(ctx, db, query, args, destPtr) + rowsProcessed, err := queryToSlicePgxV5(ctx, db, query, args, destPtr) if err != nil { return rowsProcessed, fmt.Errorf("jet: %w", err) } @@ -89,7 +89,7 @@ func QueryPGX(ctx context.Context, db QueryablePGX, query string, args []interfa tempSlicePtrValue := reflect.New(reflect.SliceOf(destinationPtrType)) tempSliceValue := tempSlicePtrValue.Elem() - rowsProcessed, err := queryToSlicePGX(ctx, db, query, args, tempSlicePtrValue.Interface()) + rowsProcessed, err := queryToSlicePgxV5(ctx, db, query, args, tempSlicePtrValue.Interface()) if err != nil { return rowsProcessed, fmt.Errorf("jet: %w", err) @@ -116,7 +116,7 @@ func QueryPGX(ctx context.Context, db QueryablePGX, query string, args []interfa } } -func queryToSlicePGX(ctx context.Context, db QueryablePGX, query string, args []interface{}, slicePtr interface{}) (rowsProcessed int64, err error) { +func queryToSlicePgxV5(ctx context.Context, db QueryablePgxV5, query string, args []interface{}, slicePtr interface{}) (rowsProcessed int64, err error) { if ctx == nil { ctx = context.Background() } @@ -161,7 +161,7 @@ func queryToSlicePGX(ctx context.Context, db QueryablePGX, query string, args [] return scanContext.rowNum, rows.Err() } -func queryPGXJson(ctx context.Context, db QueryablePGX, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { +func queryJsonPgxV5(ctx context.Context, db QueryablePgxV5, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) { must.BeInitializedPtr(db, "jet: db is nil") var rows pgx.Rows diff --git a/tests/postgres/alltypes_test.go b/tests/postgres/alltypes_test.go index 36c8d62d..766bfe09 100644 --- a/tests/postgres/alltypes_test.go +++ b/tests/postgres/alltypes_test.go @@ -2,7 +2,6 @@ package postgres import ( "encoding/base64" - "fmt" "github.com/go-jet/jet/v2/internal/utils/ptr" "github.com/stretchr/testify/assert" "math" @@ -873,10 +872,8 @@ FROM ( err := stmtJson.QueryContext(ctx, db, &destSelectJson) require.NoError(t, err) - testutils.PrintJson(destSelectJson) require.Equal(t, dest, destSelectJson) - }) } @@ -1581,8 +1578,6 @@ func TestInterval(t *testing.T) { AllTypes.IntervalPtr.DIV(Float(22.222)).EQ(AllTypes.IntervalPtr), ).FROM(AllTypes) - fmt.Println(stmt.Sql()) - testutils.AssertDebugStatementSql(t, stmt, ` SELECT INTERVAL '1 YEAR', INTERVAL '1 MONTH', diff --git a/tests/postgres/pgx_test.go b/tests/postgres/pgx_test.go index cbe6ce00..457860bc 100644 --- a/tests/postgres/pgx_test.go +++ b/tests/postgres/pgx_test.go @@ -1,37 +1,44 @@ package postgres import ( - "context" - "fmt" "github.com/go-jet/jet/v2/internal/testutils" + "github.com/go-jet/jet/v2/pgxV5" . "github.com/go-jet/jet/v2/postgres" - "github.com/go-jet/jet/v2/qrm" + model3 "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model" + table3 "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/table" . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/northwind/table" model2 "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/model" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table" "github.com/google/uuid" "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" "github.com/shopspring/decimal" "github.com/stretchr/testify/require" "testing" ) -var pgxDB *pgx.Conn +var pgxConn *pgx.Conn +var pgxPool *pgxpool.Pool func init() { var err error - pgxDB, err = pgx.Connect(context.Background(), getConnectionString()) + pgxConn, err = pgx.Connect(ctx, getConnectionString()) + + if err != nil { + panic(err) + } + + pgxPool, err = pgxpool.New(ctx, getConnectionString()) if err != nil { panic(err) } } -func BenchmarkNorthwindJoinEverythingPGX(b *testing.B) { +func BenchmarkNorthwindJoinEverythingPgx(b *testing.B) { for i := 0; i < b.N; i++ { testNorthwindJoinEverythingCustomScan(b, func(stmt Statement, dest any) { - sql, args := stmt.Sql() - _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, dest) + err := pgxV5.Query(ctx, stmt, pgxConn, dest) require.NoError(b, err) }) } @@ -55,8 +62,7 @@ func TestNorthwindJoinEverythingPQ(t *testing.T) { func TestNorthwindJoinEverythingPGX(t *testing.T) { testNorthwindJoinEverythingCustomScan(t, func(stmt Statement, dest any) { - sql, args := stmt.Sql() - _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, dest) + err := pgxV5.Query(ctx, stmt, pgxConn, dest) require.NoError(t, err) }) } @@ -101,6 +107,7 @@ func testNorthwindJoinEverythingCustomScan(b require.TestingT, queryFunc func(st queryFunc(stmt, &dest) testutils.AssertJSONFile(b, dest, "./testdata/results/postgres/northwind-all.json") + requireLogged(b, stmt) } func TestUUIDTypePGX(t *testing.T) { @@ -119,12 +126,10 @@ WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; result := model2.AllTypes{} - //err := query.Query(db, &result) - - sql, args := stmt.Sql() - _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, &result) - + err := pgxV5.Query(ctx, stmt, pgxPool, &result) require.NoError(t, err) + requireLogged(t, stmt) + require.Equal(t, result.UUID, uuid.MustParse("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")) testutils.AssertDeepEqual(t, result.UUIDPtr, testutils.UUIDPtr("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")) } @@ -146,9 +151,13 @@ func TestPGXScannerType(t *testing.T) { var result floats - sql, args := query.Sql() - _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, &result) + pgxTx, err := pgxPool.Begin(ctx) + require.NoError(t, err) + defer pgxTx.Rollback(ctx) + + err = pgxV5.Query(ctx, query, pgxTx, &result) require.NoError(t, err) + requireLogged(t, query) require.Equal(t, "1.11111111111111111111", result.Decimal.String()) require.Equal(t, "0", result.DecimalPtr.String()) // NULL @@ -195,10 +204,105 @@ func TestAllTypesSelectPGX(t *testing.T) { table.AllTypes, ).LIMIT(2) - fmt.Println(stmt.DebugSql()) + err := pgxV5.Query(ctx, stmt, pgxConn, &dest) + require.NoError(t, err) + requireLogged(t, stmt) +} + +func TestSelectJsonObjectPgxV5(t *testing.T) { + stmt := SELECT_JSON_OBJ(table3.Actor.AllColumns). + FROM(table3.Actor). + WHERE(table3.Actor.ActorID.EQ(Int32(2))) - sql, args := stmt.Sql() - _, err := qrm.QueryPGX(context.Background(), pgxDB, sql, args, &dest) + var dest model3.Actor + + err := pgxV5.Query(ctx, stmt, pgxPool, &dest) require.NoError(t, err) + testutils.AssertJsonEqual(t, dest, actor2) + requireLogged(t, stmt) + + t.Run("scan to map", func(t *testing.T) { + var dest2 map[string]interface{} + + err := pgxV5.Query(ctx, stmt, pgxPool, &dest2) + + require.NoError(t, err) + testutils.AssertDeepEqual(t, dest2, map[string]interface{}{ + "actorID": float64(2), + "firstName": "Nick", + "lastName": "Wahlberg", + "lastUpdate": "2013-05-26T14:47:57.620000Z", + }) + }) +} + +func TestSelectQuickStartJsonPgxV5(t *testing.T) { + + stmt := SELECT_JSON_ARR( + table3.Actor.ActorID, + table3.Actor.FirstName, + table3.Actor.LastName, + table3.Actor.LastUpdate, + + SELECT_JSON_ARR( + table3.Film.AllColumns.Except(table3.Film.SpecialFeatures), + CAST(table3.Film.SpecialFeatures).AS_TEXT().AS("SpecialFeatures"), + + SELECT_JSON_OBJ( + table3.Language.AllColumns, + ).FROM( + table3.Language, + ).WHERE( + table3.Language.LanguageID.EQ(table3.Film.LanguageID).AND( + table3.Language.Name.EQ(Char(20)("English")), + ), + ).AS("Language"), + + SELECT_JSON_ARR( + table3.Category.AllColumns, + ).FROM( + table3.Category. + INNER_JOIN(table3.FilmCategory, table3.FilmCategory.CategoryID.EQ(table3.Category.CategoryID)), + ).WHERE( + table3.FilmCategory.FilmID.EQ(table3.Film.FilmID).AND( + table3.Category.Name.NOT_EQ(Text("Action")), + ), + ).AS("Categories"), + ).FROM( + table3.Film. + INNER_JOIN(table3.FilmActor, table3.FilmActor.FilmID.EQ(table3.Film.FilmID)), + ).WHERE( + table3.FilmActor.ActorID.EQ(table3.Actor.ActorID).AND(table3.Film.Length.GT(Int32(180))), + ).ORDER_BY( + table3.Film.FilmID.ASC(), + ).AS("Films"), + ).FROM( + table3.Actor, + ).ORDER_BY( + table3.Actor.ActorID.ASC(), + ) + + var dest []struct { + model3.Actor + + Films []struct { + model3.Film + + Language model3.Language + Categories []model3.Category + } + } + + err := pgxV5.Query(ctx, stmt, pgxConn, &dest) + + require.NoError(t, err) + require.Len(t, dest, 200) + requireLogged(t, stmt) + + if sourceIsCockroachDB() { + return // char[n] columns whitespaces are trimmed when returned as json in cockroachdb + } + + testutils.AssertJSONFile(t, dest, "./testdata/results/postgres/quick-start-json-dest.json") } diff --git a/tests/postgres/select_json_test.go b/tests/postgres/select_json_test.go index 8691018e..bfa3e14b 100644 --- a/tests/postgres/select_json_test.go +++ b/tests/postgres/select_json_test.go @@ -470,7 +470,7 @@ FROM ( } //testutils.SaveJSONFile(dest, "./testdata/results/postgres/quick-start-json-dest2.json") - //testutils.AssertJSONFile(t, dest, "./testdata/results/postgres/quick-start-json-dest.json") + testutils.AssertJSONFile(t, dest, "./testdata/results/postgres/quick-start-json-dest.json") } func TestSelectJsonInReturning(t *testing.T) { diff --git a/tests/testdata b/tests/testdata index 89dc2c61..dbb1826f 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 89dc2c61a2419a281eeed5d95174eab0ca192fb4 +Subproject commit dbb1826f8840caa09244408549d46c7e1c4548b9 From 7e61bcc8855d0942f7437f4199a7fe5bdb46c723 Mon Sep 17 00:00:00 2001 From: go-jet Date: Wed, 8 Oct 2025 14:09:46 +0200 Subject: [PATCH 6/8] Update testdata --- tests/testdata | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testdata b/tests/testdata index dbb1826f..b76847d8 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit dbb1826f8840caa09244408549d46c7e1c4548b9 +Subproject commit b76847d85603afe13a5864e024078e3e8c7254e1 From dea633bcf740f5b4f551cdfce57b39c2f3133723 Mon Sep 17 00:00:00 2001 From: go-jet Date: Wed, 8 Oct 2025 14:21:49 +0200 Subject: [PATCH 7/8] Merge with master. --- go.mod | 1 + go.sum | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 126383e5..a572957c 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/volatiletech/strmangle v0.0.1 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/crypto v0.35.0 // indirect + golang.org/x/sync v0.11.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e044ce13..7cd62dff 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,6 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0= github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= @@ -14,11 +13,11 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -72,8 +71,9 @@ github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= @@ -85,25 +85,30 @@ github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08 github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= -github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= -github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg= +github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -117,13 +122,14 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= @@ -205,6 +211,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -258,8 +266,9 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg= gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= From 48b7a20a31ae89d3dba071955bf129466d89a7db Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 19 Oct 2025 12:14:37 +0200 Subject: [PATCH 8/8] Fix test. --- tests/postgres/pgx_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/postgres/pgx_test.go b/tests/postgres/pgx_test.go index 457860bc..c9f3dd1d 100644 --- a/tests/postgres/pgx_test.go +++ b/tests/postgres/pgx_test.go @@ -121,7 +121,7 @@ func TestUUIDTypePGX(t *testing.T) { SELECT all_types.uuid AS "all_types.uuid", all_types.uuid_ptr AS "all_types.uuid_ptr" FROM test_sample.all_types -WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; +WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid; `, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11") result := model2.AllTypes{} @@ -246,8 +246,7 @@ func TestSelectQuickStartJsonPgxV5(t *testing.T) { table3.Actor.LastUpdate, SELECT_JSON_ARR( - table3.Film.AllColumns.Except(table3.Film.SpecialFeatures), - CAST(table3.Film.SpecialFeatures).AS_TEXT().AS("SpecialFeatures"), + table3.Film.AllColumns, SELECT_JSON_OBJ( table3.Language.AllColumns, @@ -273,7 +272,11 @@ func TestSelectQuickStartJsonPgxV5(t *testing.T) { table3.Film. INNER_JOIN(table3.FilmActor, table3.FilmActor.FilmID.EQ(table3.Film.FilmID)), ).WHERE( - table3.FilmActor.ActorID.EQ(table3.Actor.ActorID).AND(table3.Film.Length.GT(Int32(180))), + AND( + table3.FilmActor.ActorID.EQ(table3.Actor.ActorID), + table3.Film.Length.GT(Int32(180)), + String("Trailers").EQ(ANY(table3.Film.SpecialFeatures)), + ), ).ORDER_BY( table3.Film.FilmID.ASC(), ).AS("Films"),