feat: add mysql update undo log buildertags/1.0.2-RC1
@@ -97,7 +97,7 @@ require ( | |||
github.com/mschoch/smat v0.2.0 // indirect | |||
github.com/nacos-group/nacos-sdk-go v1.1.1 // indirect | |||
github.com/opentracing/opentracing-go v1.2.0 // indirect | |||
github.com/pelletier/go-toml v1.7.0 // indirect | |||
github.com/pelletier/go-toml v1.9.3 // indirect | |||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect | |||
github.com/pierrec/lz4 v2.5.2+incompatible // indirect | |||
github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 // indirect | |||
@@ -115,11 +115,11 @@ require ( | |||
github.com/shirou/gopsutil/v3 v3.22.2 // indirect | |||
github.com/sirupsen/logrus v1.8.1 // indirect | |||
github.com/spaolacci/murmur3 v1.1.0 // indirect | |||
github.com/spf13/afero v1.2.2 // indirect | |||
github.com/spf13/cast v1.3.0 // indirect | |||
github.com/spf13/jwalterweatherman v1.0.0 // indirect | |||
github.com/spf13/afero v1.6.0 // indirect | |||
github.com/spf13/cast v1.3.1 // indirect | |||
github.com/spf13/jwalterweatherman v1.1.0 // indirect | |||
github.com/spf13/pflag v1.0.5 // indirect | |||
github.com/spf13/viper v1.7.1 // indirect | |||
github.com/spf13/viper v1.8.1 // indirect | |||
github.com/subosito/gotenv v1.2.0 // indirect | |||
github.com/tklauser/go-sysconf v0.3.10 // indirect | |||
github.com/tklauser/numcpus v0.4.0 // indirect | |||
@@ -141,7 +141,7 @@ require ( | |||
golang.org/x/text v0.3.7 // indirect | |||
google.golang.org/appengine v1.6.7 // indirect | |||
google.golang.org/genproto v0.0.0-20220630174209-ad1d48641aa7 // indirect | |||
gopkg.in/ini.v1 v1.51.0 // indirect | |||
gopkg.in/ini.v1 v1.62.0 // indirect | |||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect | |||
gopkg.in/yaml.v2 v2.4.0 // indirect | |||
gopkg.in/yaml.v3 v3.0.1 // indirect | |||
@@ -90,9 +90,6 @@ github.com/apache/dubbo-go-hessian2 v1.11.0 h1:VTdT6NStuEqNmyT3AdSN2DLDBqhXvAAyA | |||
github.com/apache/dubbo-go-hessian2 v1.11.0/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= | |||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= | |||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= | |||
github.com/apache/thrift v0.13.1-0.20201008052519-daf620915714/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= | |||
github.com/appleboy/gin-jwt/v2 v2.6.3/go.mod h1:MfPYA4ogzvOcVkRwAxT7quHOtQmVKDpTwxyUrC2DNw0= | |||
github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= | |||
github.com/arana-db/parser v0.2.5 h1:X7SZUjs52nNkX+PL3wrJVd7+BL4VALIXahX+Bx+pmOQ= | |||
github.com/arana-db/parser v0.2.5/go.mod h1:/XA29bplweWSEAjgoM557ZCzhBilSawUlHcZFjOeDAc= | |||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= | |||
@@ -124,6 +121,7 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB | |||
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= | |||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= | |||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= | |||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= | |||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= | |||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= | |||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= | |||
@@ -533,6 +531,7 @@ github.com/knadh/koanf v1.4.1/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO4 | |||
github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7/go.mod h1:Y2SaZf2Rzd0pXkLVhLlCiAXFCLSXAIbTKDivVgff/AM= | |||
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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | |||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= | |||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= | |||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | |||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | |||
@@ -554,6 +553,7 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ | |||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= | |||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= | |||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= | |||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= | |||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= | |||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= | |||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= | |||
@@ -646,8 +646,9 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI | |||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= | |||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= | |||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | |||
github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= | |||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= | |||
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= | |||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= | |||
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= | |||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= | |||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= | |||
@@ -668,6 +669,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE | |||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | |||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | |||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= | |||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= | |||
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/polarismesh/polaris-go v1.0.2 h1:vMPTgO+DKNq9mC5HP7wFnlotIqg2waOAIKQq0qoZchY= | |||
@@ -765,21 +767,25 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO | |||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= | |||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= | |||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= | |||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= | |||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= | |||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= | |||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= | |||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= | |||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= | |||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= | |||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= | |||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= | |||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= | |||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= | |||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= | |||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= | |||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= | |||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= | |||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= | |||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | |||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | |||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= | |||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= | |||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= | |||
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= | |||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= | |||
github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= | |||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= | |||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= | |||
@@ -846,12 +852,15 @@ go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= | |||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= | |||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= | |||
go.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw= | |||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= | |||
go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= | |||
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= | |||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= | |||
go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg= | |||
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= | |||
go.etcd.io/etcd/client/v2 v2.305.0-alpha.0 h1:jZepGpOeJATxsbMNBZczDS2jHdK/QVHM1iPe9jURJ8o= | |||
go.etcd.io/etcd/client/v2 v2.305.0-alpha.0/go.mod h1:kdV+xzCJ3luEBSIeQyB/OEKkWKd8Zkux4sbDeANrosU= | |||
go.etcd.io/etcd/client/v2 v2.305.0 h1:ftQ0nOOHMcbMS3KIaDQ0g5Qcd6bhaBrQT6b89DfwLTs= | |||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= | |||
go.etcd.io/etcd/client/v3 v3.5.0-alpha.0/go.mod h1:wKt7jgDgf/OfKiYmCq5WFGxOFAkVMLxiiXgLDFhECr8= | |||
go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4= | |||
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= | |||
@@ -907,6 +916,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk | |||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | |||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | |||
@@ -1020,6 +1030,7 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ | |||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | |||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | |||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | |||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | |||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | |||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | |||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | |||
@@ -1237,6 +1248,7 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q | |||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= | |||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= | |||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= | |||
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= | |||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= | |||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= | |||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= | |||
@@ -1379,8 +1391,9 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | |||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | |||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= | |||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | |||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= | |||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | |||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= | |||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | |||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= | |||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= | |||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= | |||
@@ -1410,8 +1423,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh | |||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= | |||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= | |||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= | |||
honnef.co/go/tools v0.2.0/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= | |||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= | |||
modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= | |||
modernc.org/golex v1.0.1/go.mod h1:QCA53QtsT1NdGkaZZkF5ezFwk4IXh4BGNafAARTC254= | |||
modernc.org/lex v1.0.0/go.mod h1:G6rxMTy3cH2iA0iXL/HRRv4Znu8MK4higxph/lE7ypk= | |||
@@ -1425,7 +1436,6 @@ modernc.org/sortutil v1.0.0/go.mod h1:1QO0q8IlIlmjBIwm6t/7sof874+xCfZouyqZMLIAtx | |||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= | |||
modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= | |||
modernc.org/y v1.0.1/go.mod h1:Ho86I+LVHEI+LYXoUKlmOMAM1JTXOCfj8qi1T8PsClE= | |||
moul.io/zapgorm2 v1.1.0/go.mod h1:emRfKjNqSzVj5lcgasBdovIXY1jSOwFz2GQZn1Rddks= | |||
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= | |||
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= | |||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= | |||
@@ -0,0 +1,33 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package reflectx | |||
import ( | |||
"reflect" | |||
"unsafe" | |||
) | |||
func SetUnexportedField(field reflect.Value, value interface{}) { | |||
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())). | |||
Elem(). | |||
Set(reflect.ValueOf(value)) | |||
} | |||
func GetUnexportedField(field reflect.Value) interface{} { | |||
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface() | |||
} |
@@ -111,6 +111,7 @@ func (c *Conn) Exec(query string, args []driver.Value) (driver.Result, error) { | |||
TxCtx: c.txCtx, | |||
Query: query, | |||
Values: args, | |||
Conn: c.targetConn, | |||
} | |||
return executor.ExecWithValue(context.Background(), execCtx, | |||
@@ -153,6 +154,7 @@ func (c *Conn) ExecContext(ctx context.Context, query string, args []driver.Name | |||
TxCtx: c.txCtx, | |||
Query: query, | |||
NamedValues: args, | |||
Conn: c.targetConn, | |||
} | |||
ret, err := executor.ExecWithNamedValue(ctx, execCtx, | |||
@@ -43,17 +43,19 @@ func (mi *mockSQLInterceptor) Type() types.SQLType { | |||
} | |||
// Before | |||
func (mi *mockSQLInterceptor) Before(ctx context.Context, execCtx *exec.ExecContext) { | |||
func (mi *mockSQLInterceptor) Before(ctx context.Context, execCtx *exec.ExecContext) error { | |||
if mi.before != nil { | |||
mi.before(ctx, execCtx) | |||
} | |||
return nil | |||
} | |||
// After | |||
func (mi *mockSQLInterceptor) After(ctx context.Context, execCtx *exec.ExecContext) { | |||
func (mi *mockSQLInterceptor) After(ctx context.Context, execCtx *exec.ExecContext) error { | |||
if mi.after != nil { | |||
mi.after(ctx, execCtx) | |||
} | |||
return nil | |||
} | |||
type mockTxHook struct { | |||
@@ -25,6 +25,7 @@ import ( | |||
"testing" | |||
"github.com/golang/mock/gomock" | |||
"github.com/seata/seata-go/pkg/common/reflectx" | |||
"github.com/seata/seata-go/pkg/datasource/sql/mock" | |||
"github.com/seata/seata-go/pkg/datasource/sql/types" | |||
"github.com/stretchr/testify/assert" | |||
@@ -47,7 +48,7 @@ func initMockAtConnector(t *testing.T, ctrl *gomock.Controller, db *sql.DB, f in | |||
} | |||
field := v.FieldByName("connector") | |||
fieldVal := GetUnexportedField(field) | |||
fieldVal := reflectx.GetUnexportedField(field) | |||
atConnector, ok := fieldVal.(*seataATConnector) | |||
assert.True(t, ok, "need return seata at connector") | |||
@@ -56,7 +57,7 @@ func initMockAtConnector(t *testing.T, ctrl *gomock.Controller, db *sql.DB, f in | |||
if v.Kind() == reflect.Ptr { | |||
v = v.Elem() | |||
} | |||
SetUnexportedField(v.FieldByName("target"), f(t, ctrl)) | |||
reflectx.SetUnexportedField(v.FieldByName("target"), f(t, ctrl)) | |||
return fieldVal.(driver.Connector) | |||
} | |||
@@ -91,7 +92,7 @@ func initMockXaConnector(t *testing.T, ctrl *gomock.Controller, db *sql.DB, f in | |||
} | |||
field := v.FieldByName("connector") | |||
fieldVal := GetUnexportedField(field) | |||
fieldVal := reflectx.GetUnexportedField(field) | |||
atConnector, ok := fieldVal.(*seataXAConnector) | |||
assert.True(t, ok, "need return seata xa connector") | |||
@@ -100,7 +101,7 @@ func initMockXaConnector(t *testing.T, ctrl *gomock.Controller, db *sql.DB, f in | |||
if v.Kind() == reflect.Ptr { | |||
v = v.Elem() | |||
} | |||
SetUnexportedField(v.FieldByName("target"), f(t, ctrl)) | |||
reflectx.SetUnexportedField(v.FieldByName("target"), f(t, ctrl)) | |||
return fieldVal.(driver.Connector) | |||
} | |||
@@ -22,6 +22,7 @@ import ( | |||
"github.com/seata/seata-go/pkg/datasource/sql/types" | |||
) | |||
// todo | |||
func init() { | |||
datasource.RegisterTableCache(types.DBTypeMySQL, func() datasource.TableMetaCache { | |||
return &tableMetaCache{} | |||
@@ -24,11 +24,10 @@ import ( | |||
"fmt" | |||
"reflect" | |||
"strings" | |||
"unsafe" | |||
"github.com/seata/seata-go/pkg/common/log" | |||
"github.com/go-sql-driver/mysql" | |||
"github.com/seata/seata-go/pkg/common/log" | |||
"github.com/seata/seata-go/pkg/common/reflectx" | |||
"github.com/seata/seata-go/pkg/datasource/sql/datasource" | |||
"github.com/seata/seata-go/pkg/datasource/sql/types" | |||
"github.com/seata/seata-go/pkg/protocol/branch" | |||
@@ -116,7 +115,7 @@ func (d *seataDriver) Open(name string) (driver.Conn, error) { | |||
return nil, err | |||
} | |||
SetUnexportedField(field, proxy) | |||
reflectx.SetUnexportedField(field, proxy) | |||
return conn, nil | |||
} | |||
@@ -193,9 +192,9 @@ func registerResource(connector driver.Connector, txType types.TransactionType, | |||
} | |||
return &seataConnector{ | |||
res: res, | |||
target: connector, | |||
conf: conf, | |||
res: res, | |||
target: connector, | |||
conf: conf, | |||
}, nil | |||
} | |||
@@ -241,13 +240,3 @@ func parseResourceID(dsn string) string { | |||
return strings.ReplaceAll(res, ",", "|") | |||
} | |||
func GetUnexportedField(field reflect.Value) interface{} { | |||
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface() | |||
} | |||
func SetUnexportedField(field reflect.Value, value interface{}) { | |||
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())). | |||
Elem(). | |||
Set(reflect.ValueOf(value)) | |||
} |
@@ -23,7 +23,7 @@ import ( | |||
"testing" | |||
"github.com/golang/mock/gomock" | |||
"github.com/seata/seata-go/pkg/common/reflectx" | |||
"github.com/seata/seata-go/pkg/datasource/sql/datasource" | |||
"github.com/seata/seata-go/pkg/datasource/sql/mock" | |||
"github.com/seata/seata-go/pkg/protocol/branch" | |||
@@ -60,7 +60,7 @@ func Test_seataATDriver_OpenConnector(t *testing.T) { | |||
} | |||
field := v.FieldByName("connector") | |||
fieldVal := GetUnexportedField(field) | |||
fieldVal := reflectx.GetUnexportedField(field) | |||
_, ok := fieldVal.(*seataATConnector) | |||
assert.True(t, ok, "need return seata at connector") | |||
@@ -86,7 +86,7 @@ func Test_seataXADriver_OpenConnector(t *testing.T) { | |||
} | |||
field := v.FieldByName("connector") | |||
fieldVal := GetUnexportedField(field) | |||
fieldVal := reflectx.GetUnexportedField(field) | |||
_, ok := fieldVal.(*seataXAConnector) | |||
assert.True(t, ok, "need return seata xa connector") | |||
@@ -48,7 +48,7 @@ type ( | |||
SQLExecutor interface { | |||
// Interceptors | |||
interceptors(interceptors []SQLInterceptor) | |||
interceptors(interceptors []SQLHook) | |||
// Exec | |||
ExecWithNamedValue(ctx context.Context, execCtx *ExecContext, f CallbackWithNamedValue) (types.ExecResult, error) | |||
// Exec | |||
@@ -59,7 +59,7 @@ type ( | |||
// BuildExecutor | |||
func BuildExecutor(dbType types.DBType, txType types.TransactionType, query string) (SQLExecutor, error) { | |||
if txType == types.XAMode { | |||
hooks := make([]SQLInterceptor, 0, 4) | |||
hooks := make([]SQLHook, 0, 4) | |||
hooks = append(hooks, commonHook...) | |||
e := &BaseExecutor{} | |||
@@ -72,7 +72,7 @@ func BuildExecutor(dbType types.DBType, txType types.TransactionType, query stri | |||
return nil, err | |||
} | |||
hooks := make([]SQLInterceptor, 0, 4) | |||
hooks := make([]SQLHook, 0, 4) | |||
hooks = append(hooks, commonHook...) | |||
hooks = append(hooks, hookSolts[parseCtx.SQLType]...) | |||
@@ -99,12 +99,12 @@ func BuildExecutor(dbType types.DBType, txType types.TransactionType, query stri | |||
} | |||
type BaseExecutor struct { | |||
is []SQLInterceptor | |||
is []SQLHook | |||
ex SQLExecutor | |||
} | |||
// interceptors | |||
func (e *BaseExecutor) interceptors(interceptors []SQLInterceptor) { | |||
// Interceptors | |||
func (e *BaseExecutor) interceptors(interceptors []SQLHook) { | |||
e.is = interceptors | |||
} | |||
@@ -25,26 +25,26 @@ import ( | |||
) | |||
var ( | |||
commonHook = make([]SQLInterceptor, 0, 4) | |||
commonHook = make([]SQLHook, 0, 4) | |||
// todo support distinguish between different db type | |||
hookSolts = map[types.SQLType][]SQLInterceptor{} | |||
hookSolts = map[types.SQLType][]SQLHook{} | |||
) | |||
// RegisCommonHook not goroutine safe | |||
func RegisCommonHook(hook SQLInterceptor) { | |||
func RegisCommonHook(hook SQLHook) { | |||
commonHook = append(commonHook, hook) | |||
} | |||
func CleanCommonHook() { | |||
commonHook = make([]SQLInterceptor, 0, 4) | |||
commonHook = make([]SQLHook, 0, 4) | |||
} | |||
// RegisHook not goroutine safe | |||
func RegisHook(hook SQLInterceptor) { | |||
func RegisHook(hook SQLHook) { | |||
_, ok := hookSolts[hook.Type()] | |||
if !ok { | |||
hookSolts[hook.Type()] = make([]SQLInterceptor, 0, 4) | |||
hookSolts[hook.Type()] = make([]SQLHook, 0, 4) | |||
} | |||
hookSolts[hook.Type()] = append(hookSolts[hook.Type()], hook) | |||
@@ -56,18 +56,21 @@ type ExecContext struct { | |||
Query string | |||
NamedValues []driver.NamedValue | |||
Values []driver.Value | |||
// metaData | |||
MetaData types.TableMeta | |||
Conn driver.Conn | |||
} | |||
// SQLHook SQL execution front and back interceptor | |||
// case 1. Used to intercept SQL to achieve the generation of front and rear mirrors | |||
// case 2. Burning point to report | |||
// case 3. SQL black and white list | |||
type SQLInterceptor interface { | |||
type SQLHook interface { | |||
Type() types.SQLType | |||
// Before | |||
Before(ctx context.Context, execCtx *ExecContext) | |||
Before(ctx context.Context, execCtx *ExecContext) error | |||
// After | |||
After(ctx context.Context, execCtx *ExecContext) | |||
After(ctx context.Context, execCtx *ExecContext) error | |||
} |
@@ -15,11 +15,12 @@ | |||
* limitations under the License. | |||
*/ | |||
package exec | |||
package hook | |||
import ( | |||
"context" | |||
"fmt" | |||
"github.com/arana-db/parser/ast" | |||
"github.com/arana-db/parser/format" | |||
"github.com/seata/seata-go/pkg/common/bytes" |
@@ -15,7 +15,7 @@ | |||
* limitations under the License. | |||
*/ | |||
package exec | |||
package hook | |||
import ( | |||
"testing" |
@@ -15,7 +15,7 @@ | |||
* limitations under the License. | |||
*/ | |||
package exec | |||
package hook | |||
import ( | |||
"context" | |||
@@ -37,7 +37,7 @@ func (h *loggerSQLHook) Type() types.SQLType { | |||
} | |||
// Before | |||
func (h *loggerSQLHook) Before(ctx context.Context, execCtx *exec.ExecContext) { | |||
func (h *loggerSQLHook) Before(ctx context.Context, execCtx *exec.ExecContext) error { | |||
var txID string | |||
if execCtx.TxCtx != nil { | |||
txID = execCtx.TxCtx.LocalTransID | |||
@@ -56,8 +56,10 @@ func (h *loggerSQLHook) Before(ctx context.Context, execCtx *exec.ExecContext) { | |||
} | |||
log.Debug("sql exec log", fields) | |||
return nil | |||
} | |||
// After | |||
func (h *loggerSQLHook) After(ctx context.Context, execCtx *exec.ExecContext) { | |||
func (h *loggerSQLHook) After(ctx context.Context, execCtx *exec.ExecContext) error { | |||
return nil | |||
} |
@@ -0,0 +1,75 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package hook | |||
import ( | |||
"context" | |||
"github.com/seata/seata-go/pkg/datasource/sql/exec" | |||
"github.com/seata/seata-go/pkg/datasource/sql/parser" | |||
"github.com/seata/seata-go/pkg/datasource/sql/undo" | |||
"github.com/seata/seata-go/pkg/tm" | |||
"github.com/seata/seata-go/pkg/datasource/sql/types" | |||
) | |||
func init() { | |||
exec.RegisCommonHook(&undoLogSQLHook{}) | |||
} | |||
type undoLogSQLHook struct { | |||
} | |||
func (h *undoLogSQLHook) Type() types.SQLType { | |||
return types.SQLTypeUnknown | |||
} | |||
// Before | |||
func (h *undoLogSQLHook) Before(ctx context.Context, execCtx *exec.ExecContext) error { | |||
if !tm.IsTransactionOpened(ctx) { | |||
return nil | |||
} | |||
pc, err := parser.DoParser(execCtx.Query) | |||
if err != nil { | |||
return err | |||
} | |||
if !pc.HasValidStmt() { | |||
return nil | |||
} | |||
builder := undo.GetUndologBuilder(pc.SQLType) | |||
if builder == nil { | |||
return nil | |||
} | |||
recordImage, err := builder.BeforeImage(ctx, execCtx) | |||
if err != nil { | |||
return err | |||
} | |||
execCtx.TxCtx.RoundImages.AppendBeofreImage(recordImage) | |||
return nil | |||
} | |||
// After | |||
func (h *undoLogSQLHook) After(ctx context.Context, execCtx *exec.ExecContext) error { | |||
if !tm.IsTransactionOpened(ctx) { | |||
return nil | |||
} | |||
return nil | |||
} |
@@ -15,7 +15,7 @@ | |||
* limitations under the License. | |||
*/ | |||
package exec | |||
package hook | |||
import ( | |||
"context" |
@@ -1,3 +1,20 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
// Code generated by MockGen. DO NOT EDIT. | |||
// Source: datasource_manager.go | |||
@@ -7,13 +24,14 @@ package mock | |||
import ( | |||
context "context" | |||
sql "database/sql" | |||
reflect "reflect" | |||
gomock "github.com/golang/mock/gomock" | |||
datasource "github.com/seata/seata-go/pkg/datasource/sql/datasource" | |||
types "github.com/seata/seata-go/pkg/datasource/sql/types" | |||
branch "github.com/seata/seata-go/pkg/protocol/branch" | |||
message "github.com/seata/seata-go/pkg/protocol/message" | |||
rm "github.com/seata/seata-go/pkg/rm" | |||
reflect "reflect" | |||
) | |||
// MockDataSourceManager is a mock of DataSourceManager interface | |||
@@ -1,3 +1,20 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
// Code generated by MockGen. DO NOT EDIT. | |||
// Source: test_driver.go | |||
@@ -7,8 +24,9 @@ package mock | |||
import ( | |||
context "context" | |||
driver "database/sql/driver" | |||
gomock "github.com/golang/mock/gomock" | |||
reflect "reflect" | |||
gomock "github.com/golang/mock/gomock" | |||
) | |||
// MockTestDriverConnector is a mock of TestDriverConnector interface | |||
@@ -50,6 +50,10 @@ type ParseContext struct { | |||
DeleteStmt *ast.DeleteStmt | |||
} | |||
func (p *ParseContext) HasValidStmt() bool { | |||
return p.InsertStmt != nil || p.UpdateStmt != nil || p.DeleteStmt != nil | |||
} | |||
func DoParser(query string) (*ParseContext, error) { | |||
p := aparser.New() | |||
stmtNode, err := p.ParseOneStmt(query, "", "") | |||
@@ -18,7 +18,7 @@ | |||
package sql | |||
import ( | |||
_ "github.com/seata/seata-go/pkg/datasource/sql/exec/hook" | |||
_ "github.com/seata/seata-go/pkg/datasource/sql/hook" | |||
// mysql 相关插件 | |||
_ "github.com/seata/seata-go/pkg/datasource/sql/datasource/mysql" | |||
@@ -0,0 +1,213 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package types | |||
import ( | |||
"database/sql" | |||
"reflect" | |||
) | |||
// https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType | |||
type FieldType byte | |||
const ( | |||
FieldTypeDecimal FieldType = iota | |||
FieldTypeTiny | |||
FieldTypeShort | |||
FieldTypeLong | |||
FieldTypeFloat | |||
FieldTypeDouble | |||
FieldTypeNULL | |||
FieldTypeTimestamp | |||
FieldTypeLongLong | |||
FieldTypeInt24 | |||
FieldTypeDate | |||
FieldTypeTime | |||
FieldTypeDateTime | |||
FieldTypeYear | |||
FieldTypeNewDate | |||
FieldTypeVarChar | |||
FieldTypeBit | |||
) | |||
const ( | |||
FieldTypeJSON FieldType = iota + 0xf5 | |||
FieldTypeNewDecimal | |||
FieldTypeEnum | |||
FieldTypeSet | |||
FieldTypeTinyBLOB | |||
FieldTypeMediumBLOB | |||
FieldTypeLongBLOB | |||
FieldTypeBLOB | |||
FieldTypeVarString | |||
FieldTypeString | |||
FieldTypeGeometry | |||
) | |||
type nullTime = sql.NullTime | |||
var ( | |||
ScanTypeFloat32 = reflect.TypeOf(float32(0)) | |||
ScanTypeFloat64 = reflect.TypeOf(float64(0)) | |||
ScanTypeInt8 = reflect.TypeOf(int8(0)) | |||
ScanTypeInt16 = reflect.TypeOf(int16(0)) | |||
ScanTypeInt32 = reflect.TypeOf(int32(0)) | |||
ScanTypeInt64 = reflect.TypeOf(int64(0)) | |||
ScanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{}) | |||
ScanTypeNullInt = reflect.TypeOf(sql.NullInt64{}) | |||
ScanTypeNullTime = reflect.TypeOf(nullTime{}) | |||
ScanTypeUint8 = reflect.TypeOf(uint8(0)) | |||
ScanTypeUint16 = reflect.TypeOf(uint16(0)) | |||
ScanTypeUint32 = reflect.TypeOf(uint32(0)) | |||
ScanTypeUint64 = reflect.TypeOf(uint64(0)) | |||
ScanTypeRawBytes = reflect.TypeOf(sql.RawBytes{}) | |||
ScanTypeUnknown = reflect.TypeOf(new(interface{})) | |||
) | |||
// JDBCType's source is seata java: java.sql.Types.java | |||
// it used in undo_log.rollback_info.sqlUndoLogs.afterImage.rows.fields.type field | |||
type JDBCType int16 | |||
const ( | |||
JDBCTypeBit JDBCType = -7 | |||
JDBCTypeTinyInt JDBCType = -6 | |||
JDBCTypeSmallInt JDBCType = 5 | |||
JDBCTypeInteger JDBCType = 4 | |||
JDBCTypeBigInt JDBCType = -5 | |||
JDBCTypeFloat JDBCType = 6 | |||
JDBCTypeReal JDBCType = 7 | |||
JDBCTypeDouble JDBCType = 8 | |||
JDBCTypeNumberic JDBCType = 2 | |||
JDBCTypeDecimal JDBCType = 3 | |||
JDBCTypeChar JDBCType = 1 | |||
JDBCTypeVarchar JDBCType = 12 | |||
JDBCTypeLongVarchar JDBCType = -1 | |||
JDBCTypeDate JDBCType = 91 | |||
JDBCTypeTime JDBCType = 92 | |||
JDBCTypeTimestamp JDBCType = 93 | |||
JDBCTypeBinary JDBCType = -2 | |||
JDBCTypeVarBinary JDBCType = -3 | |||
JDBCTypeLongVarBinary JDBCType = -4 | |||
JDBCTypeNull JDBCType = 0 | |||
JDBCTypeOther JDBCType = 1111 | |||
JDBCTypeJavaObject JDBCType = 2000 | |||
JDBCTypeDistinct JDBCType = 2001 | |||
JDBCTypeStruct JDBCType = 2002 | |||
JDBCTypeArray JDBCType = 2003 | |||
JDBCTypeBlob JDBCType = 2004 | |||
JDBCTypeClob JDBCType = 2005 | |||
JDBCTypeRef JDBCType = 2006 | |||
JDBCTypeDateLink JDBCType = 70 | |||
JDBCTypeBoolean JDBCType = 16 | |||
JDBCTypeRowID JDBCType = -8 | |||
JDBCTypeNchar JDBCType = -15 | |||
JDBCTypeNvarchar JDBCType = -9 | |||
JDBCTypeLongNvVarchar JDBCType = -16 | |||
JDBCTypeNclob JDBCType = 2011 | |||
JDBCTypeSqlXML JDBCType = 2009 | |||
JDBCTypeRefCursor JDBCType = 2012 | |||
JDBCTypeTimeWithTimeZone JDBCType = 2013 | |||
JDBCTypeTimestampWithTimezone JDBCType = 2014 | |||
) | |||
// todo perfect all type name | |||
func GetJDBCTypeByTypeName(typeName string) JDBCType { | |||
switch typeName { | |||
case "BIT": | |||
return JDBCTypeBit | |||
case "TEXT": | |||
return JDBCTypeLongVarchar | |||
case "BLOB": | |||
return JDBCTypeBlob | |||
case "DATE": | |||
return JDBCTypeDate | |||
case "DATETIME": | |||
return JDBCTypeTimestamp | |||
case "DECIMAL": | |||
return JDBCTypeDecimal | |||
case "DOUBLE": | |||
return JDBCTypeDouble | |||
case "ENUM": | |||
return JDBCTypeTinyInt | |||
// todo 待完善 | |||
//case fieldTypeEnum: | |||
// return "ENUM" | |||
//case fieldTypeFloat: | |||
// return "FLOAT" | |||
//case fieldTypeGeometry: | |||
// return "GEOMETRY" | |||
//case fieldTypeInt24: | |||
// return "MEDIUMINT" | |||
//case fieldTypeJSON: | |||
// return "JSON" | |||
//case fieldTypeLong: | |||
// return "INT" | |||
//case fieldTypeLongBLOB: | |||
// if mf.charSet != collations[binaryCollation] { | |||
// return "LONGTEXT" | |||
// } | |||
// return "LONGBLOB" | |||
//case fieldTypeLongLong: | |||
// return "BIGINT" | |||
//case fieldTypeMediumBLOB: | |||
// if mf.charSet != collations[binaryCollation] { | |||
// return "MEDIUMTEXT" | |||
// } | |||
// return "MEDIUMBLOB" | |||
//case fieldTypeNewDate: | |||
// return "DATE" | |||
//case fieldTypeNewDecimal: | |||
// return "DECIMAL" | |||
//case fieldTypeNULL: | |||
// return "NULL" | |||
//case fieldTypeSet: | |||
// return "SET" | |||
//case fieldTypeShort: | |||
// return "SMALLINT" | |||
//case fieldTypeString: | |||
// if mf.charSet == collations[binaryCollation] { | |||
// return "BINARY" | |||
// } | |||
// return "CHAR" | |||
//case fieldTypeTime: | |||
// return "TIME" | |||
//case fieldTypeTimestamp: | |||
// return "TIMESTAMP" | |||
//case fieldTypeTiny: | |||
// return "TINYINT" | |||
//case fieldTypeTinyBLOB: | |||
// if mf.charSet != collations[binaryCollation] { | |||
// return "TINYTEXT" | |||
// } | |||
// return "TINYBLOB" | |||
//case fieldTypeVarChar: | |||
// if mf.charSet == collations[binaryCollation] { | |||
// return "VARBINARY" | |||
// } | |||
// return "VARCHAR" | |||
//case fieldTypeVarString: | |||
// if mf.charSet == collations[binaryCollation] { | |||
// return "VARBINARY" | |||
// } | |||
// return "VARCHAR" | |||
//case fieldTypeYear: | |||
// return "YEAR" | |||
default: | |||
return -1 | |||
} | |||
} |
@@ -17,10 +17,6 @@ | |||
package types | |||
import ( | |||
gosql "database/sql" | |||
) | |||
// RoundRecordImage Front and rear mirror data | |||
type RoundRecordImage struct { | |||
bIndex int32 | |||
@@ -89,29 +85,29 @@ func (rs RecordImages) Reserve() { | |||
// RecordImage | |||
type RecordImage struct { | |||
// index | |||
index int32 | |||
// Table table name | |||
Table string | |||
index int32 `json:"-"` | |||
// TableName table name | |||
TableName string `json:"tableName"` | |||
// SQLType sql type | |||
SQLType SQLType | |||
SQLType SQLType `json:"-"` | |||
// Rows | |||
Rows []RowImage | |||
Rows []RowImage `json:"rows"` | |||
} | |||
// RowImage Mirror data information information | |||
type RowImage struct { | |||
// Columns All columns of image data | |||
Columns []ColumnImage | |||
Columns []ColumnImage `json:"fields"` | |||
} | |||
// ColumnImage The mirror data information of the column | |||
type ColumnImage struct { | |||
// KeyType index type | |||
KeyType string | |||
KeyType IndexType `json:"keyType"` | |||
// Name column name | |||
Name string | |||
Name string `json:"name"` | |||
// Type column type | |||
Type gosql.ColumnType | |||
Type int16 `json:"type"` | |||
// Value column value | |||
Value interface{} | |||
Value interface{} `json:"value"` | |||
} |
@@ -35,6 +35,11 @@ type ( | |||
IndexType int16 | |||
) | |||
const ( | |||
IndexTypeNull IndexType = 0 | |||
IndexTypePrimaryKey IndexType = 1 | |||
) | |||
const ( | |||
_ DBType = iota | |||
DBTypeUnknown | |||
@@ -21,9 +21,10 @@ import ( | |||
"context" | |||
"database/sql" | |||
"database/sql/driver" | |||
"github.com/arana-db/parser/mysql" | |||
"strings" | |||
"github.com/arana-db/parser/mysql" | |||
"github.com/pkg/errors" | |||
"github.com/seata/seata-go/pkg/common/log" | |||
"github.com/seata/seata-go/pkg/common/util" | |||
@@ -0,0 +1,187 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package builder | |||
import ( | |||
"database/sql" | |||
"database/sql/driver" | |||
"io" | |||
"github.com/arana-db/parser/ast" | |||
"github.com/arana-db/parser/test_driver" | |||
gxsort "github.com/dubbogo/gost/sort" | |||
"github.com/seata/seata-go/pkg/datasource/sql/types" | |||
) | |||
type BasicUndoLogBuilder struct { | |||
} | |||
// getScanSlice get the column type for scann | |||
func (*BasicUndoLogBuilder) getScanSlice(columnNames []string, tableMeta types.TableMeta) []driver.Value { | |||
scanSlice := make([]driver.Value, 0, len(columnNames)) | |||
for _, columnNmae := range columnNames { | |||
var ( | |||
scanVal interface{} | |||
// 从metData获取该列的元信息 | |||
columnMeta = tableMeta.Columns[columnNmae] | |||
) | |||
switch columnMeta.Info.ScanType() { | |||
case types.ScanTypeFloat32: | |||
scanVal = float32(0) | |||
break | |||
case types.ScanTypeFloat64: | |||
scanVal = float64(0) | |||
break | |||
case types.ScanTypeInt8: | |||
scanVal = int8(0) | |||
break | |||
case types.ScanTypeInt16: | |||
scanVal = int16(0) | |||
break | |||
case types.ScanTypeInt32: | |||
scanVal = int32(0) | |||
break | |||
case types.ScanTypeInt64: | |||
scanVal = int64(0) | |||
break | |||
case types.ScanTypeNullFloat: | |||
scanVal = sql.NullFloat64{} | |||
break | |||
case types.ScanTypeNullInt: | |||
scanVal = sql.NullInt64{} | |||
break | |||
case types.ScanTypeNullTime: | |||
scanVal = sql.NullTime{} | |||
break | |||
case types.ScanTypeUint8: | |||
scanVal = uint8(0) | |||
break | |||
case types.ScanTypeUint16: | |||
scanVal = uint16(0) | |||
break | |||
case types.ScanTypeUint32: | |||
scanVal = uint32(0) | |||
break | |||
case types.ScanTypeUint64: | |||
scanVal = uint64(0) | |||
break | |||
case types.ScanTypeRawBytes: | |||
scanVal = sql.RawBytes{} | |||
break | |||
case types.ScanTypeUnknown: | |||
scanVal = new(interface{}) | |||
break | |||
} | |||
scanSlice = append(scanSlice, &scanVal) | |||
} | |||
return scanSlice | |||
} | |||
func (b *BasicUndoLogBuilder) buildSelectArgs(stmt *ast.SelectStmt, args []driver.Value) []driver.Value { | |||
var ( | |||
selectArgsIndexs = make([]int32, 0) | |||
selectArgs = make([]driver.Value, 0) | |||
) | |||
b.traversalArgs(stmt.Where, &selectArgsIndexs) | |||
if stmt.OrderBy != nil { | |||
for _, item := range stmt.OrderBy.Items { | |||
b.traversalArgs(item, &selectArgsIndexs) | |||
} | |||
} | |||
if stmt.Limit != nil { | |||
if stmt.Limit.Offset != nil { | |||
b.traversalArgs(stmt.Limit.Offset, &selectArgsIndexs) | |||
} | |||
if stmt.Limit.Count != nil { | |||
b.traversalArgs(stmt.Limit.Count, &selectArgsIndexs) | |||
} | |||
} | |||
// sort selectArgs index array | |||
gxsort.Int32(selectArgsIndexs) | |||
for _, index := range selectArgsIndexs { | |||
selectArgs = append(selectArgs, args[index]) | |||
} | |||
return selectArgs | |||
} | |||
// todo perfect all sql operation | |||
func (b *BasicUndoLogBuilder) traversalArgs(node ast.Node, argsIndex *[]int32) { | |||
if node == nil { | |||
return | |||
} | |||
switch node.(type) { | |||
case *ast.BinaryOperationExpr: | |||
expr := node.(*ast.BinaryOperationExpr) | |||
b.traversalArgs(expr.L, argsIndex) | |||
b.traversalArgs(expr.R, argsIndex) | |||
break | |||
case *ast.BetweenExpr: | |||
expr := node.(*ast.BetweenExpr) | |||
b.traversalArgs(expr.Left, argsIndex) | |||
b.traversalArgs(expr.Right, argsIndex) | |||
break | |||
case *ast.PatternInExpr: | |||
exprs := node.(*ast.PatternInExpr).List | |||
for i := 0; i < len(exprs); i++ { | |||
b.traversalArgs(exprs[i], argsIndex) | |||
} | |||
break | |||
case *test_driver.ParamMarkerExpr: | |||
*argsIndex = append(*argsIndex, int32(node.(*test_driver.ParamMarkerExpr).Order)) | |||
break | |||
} | |||
} | |||
func (u *BasicUndoLogBuilder) buildRecordImages(rowsi driver.Rows, tableMetaData types.TableMeta) (*types.RecordImage, error) { | |||
// select column names | |||
columnNames := rowsi.Columns() | |||
rowImages := make([]types.RowImage, 0) | |||
ss := u.getScanSlice(columnNames, tableMetaData) | |||
for { | |||
err := rowsi.Next(ss) | |||
if err == io.EOF { | |||
break | |||
} | |||
columns := make([]types.ColumnImage, 0) | |||
// build record image | |||
for i, name := range columnNames { | |||
columnMeta := tableMetaData.Columns[name] | |||
keyType := types.IndexTypeNull | |||
if data, ok := tableMetaData.Indexs[name]; ok { | |||
keyType = data.IType | |||
} | |||
jdbcType := types.GetJDBCTypeByTypeName(columnMeta.Info.DatabaseTypeName()) | |||
columns = append(columns, types.ColumnImage{ | |||
KeyType: keyType, | |||
Name: name, | |||
Type: int16(jdbcType), | |||
Value: ss[i], | |||
}) | |||
} | |||
rowImages = append(rowImages, types.RowImage{Columns: columns}) | |||
} | |||
return &types.RecordImage{TableName: tableMetaData.Name, Rows: rowImages}, nil | |||
} |
@@ -0,0 +1,112 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package builder | |||
import ( | |||
"context" | |||
"database/sql/driver" | |||
"fmt" | |||
"github.com/seata/seata-go/pkg/datasource/sql/exec" | |||
"github.com/arana-db/parser/ast" | |||
"github.com/arana-db/parser/format" | |||
"github.com/seata/seata-go/pkg/common/bytes" | |||
"github.com/seata/seata-go/pkg/common/log" | |||
"github.com/seata/seata-go/pkg/datasource/sql/parser" | |||
"github.com/seata/seata-go/pkg/datasource/sql/types" | |||
) | |||
type MySQLUpdateUndoLogBuilder struct { | |||
BasicUndoLogBuilder | |||
} | |||
func (u *MySQLUpdateUndoLogBuilder) BeforeImage(ctx context.Context, execCtx *exec.ExecContext) (*types.RecordImage, error) { | |||
vals := execCtx.Values | |||
if vals == nil { | |||
for n, param := range execCtx.NamedValues { | |||
vals[n] = param.Value | |||
} | |||
} | |||
selectSQL, selectArgs, err := u.buildBeforeImageSQL(execCtx.Query, vals) | |||
if err != nil { | |||
return nil, err | |||
} | |||
stmt, err := execCtx.Conn.Prepare(selectSQL) | |||
if err != nil { | |||
log.Errorf("build prepare stmt: %+v", err) | |||
return nil, err | |||
} | |||
rows, err := stmt.Query(selectArgs) | |||
if err != nil { | |||
log.Errorf("stmt query: %+v", err) | |||
return nil, err | |||
} | |||
return u.buildRecordImages(rows, execCtx.MetaData) | |||
} | |||
func (u *MySQLUpdateUndoLogBuilder) AfterImage(types.RecordImages) (*types.RecordImages, error) { | |||
return nil, nil | |||
} | |||
// buildBeforeImageSQL build select sql from update sql | |||
func (u *MySQLUpdateUndoLogBuilder) buildBeforeImageSQL(query string, args []driver.Value) (string, []driver.Value, error) { | |||
p, err := parser.DoParser(query) | |||
if err != nil { | |||
return "", nil, err | |||
} | |||
if p.UpdateStmt == nil { | |||
log.Errorf("invalid update stmt") | |||
return "", nil, fmt.Errorf("invalid update stmt") | |||
} | |||
fields := []*ast.SelectField{} | |||
for _, column := range p.UpdateStmt.List { | |||
fields = append(fields, &ast.SelectField{ | |||
Expr: &ast.ColumnNameExpr{ | |||
Name: column.Column, | |||
}, | |||
}) | |||
} | |||
selStmt := ast.SelectStmt{ | |||
SelectStmtOpts: &ast.SelectStmtOpts{}, | |||
From: p.UpdateStmt.TableRefs, | |||
Where: p.UpdateStmt.Where, | |||
Fields: &ast.FieldList{Fields: fields}, | |||
OrderBy: p.UpdateStmt.Order, | |||
Limit: p.UpdateStmt.Limit, | |||
TableHints: p.UpdateStmt.TableHints, | |||
} | |||
b := bytes.NewByteBuffer([]byte{}) | |||
selStmt.Restore(format.NewRestoreCtx(format.RestoreKeyWordUppercase, b)) | |||
sql := string(b.Bytes()) | |||
log.Infof("build select sql by update sourceQuery, sql {}", sql) | |||
return sql, u.buildSelectArgs(&selStmt, args), nil | |||
} | |||
func (u *MySQLUpdateUndoLogBuilder) GetSQLType() types.SQLType { | |||
return types.SQLTypeUpdate | |||
} |
@@ -0,0 +1,74 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package builder | |||
import ( | |||
"database/sql/driver" | |||
"testing" | |||
_ "github.com/arana-db/parser/test_driver" | |||
_ "github.com/seata/seata-go/pkg/common/log" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func TestBuildBeforeImageSQL(t *testing.T) { | |||
var ( | |||
builder = MySQLUpdateUndoLogBuilder{} | |||
) | |||
tests := []struct { | |||
name string | |||
sourceQuery string | |||
sourceQueryArgs []driver.Value | |||
expectQuery string | |||
expectQueryArgs []driver.Value | |||
}{ | |||
{ | |||
sourceQuery: "update t_user set name = ?, age = ? where id = ?", | |||
sourceQueryArgs: []driver.Value{"Jack", 1, 100}, | |||
expectQuery: "SELECT SQL_NO_CACHE name,age FROM t_user WHERE id=?", | |||
expectQueryArgs: []driver.Value{100}, | |||
}, | |||
{ | |||
sourceQuery: "update t_user set name = ?, age = ? where id = ? and name = 'Jack' and age between ? and ?", | |||
sourceQueryArgs: []driver.Value{"Jack", 1, 100, 18, 28}, | |||
expectQuery: "SELECT SQL_NO_CACHE name,age FROM t_user WHERE id=? AND name=_UTF8MB4Jack AND age BETWEEN ? AND ?", | |||
expectQueryArgs: []driver.Value{100, 18, 28}, | |||
}, | |||
{ | |||
sourceQuery: "update t_user set name = ?, age = ? where id = ? and name = 'Jack' and age in (?,?)", | |||
sourceQueryArgs: []driver.Value{"Jack", 1, 100, 18, 28}, | |||
expectQuery: "SELECT SQL_NO_CACHE name,age FROM t_user WHERE id=? AND name=_UTF8MB4Jack AND age IN (?,?)", | |||
expectQueryArgs: []driver.Value{100, 18, 28}, | |||
}, | |||
{ | |||
sourceQuery: "update t_user set name = ?, age = ? where kk between ? and ? and id = ? and addr in(?,?) and age > ? order by name desc limit ?", | |||
sourceQueryArgs: []driver.Value{"Jack", 1, 10, 20, 17, "Beijing", "Guangzhou", 18, 2}, | |||
expectQuery: "SELECT SQL_NO_CACHE name,age FROM t_user WHERE kk BETWEEN ? AND ? AND id=? AND addr IN (?,?) AND age>? ORDER BY name DESC LIMIT ?", | |||
expectQueryArgs: []driver.Value{10, 20, 17, "Beijing", "Guangzhou", 18, 2}, | |||
}, | |||
} | |||
for _, tt := range tests { | |||
t.Run(tt.name, func(t *testing.T) { | |||
query, args, err := builder.buildBeforeImageSQL(tt.sourceQuery, tt.sourceQueryArgs) | |||
assert.Nil(t, err) | |||
assert.Equal(t, tt.expectQuery, query) | |||
assert.Equal(t, tt.expectQueryArgs, args) | |||
}) | |||
} | |||
} |
@@ -23,7 +23,7 @@ import ( | |||
) | |||
func init() { | |||
if err := undo.Regis(&undoLogManager{}); err != nil { | |||
if err := undo.RegisterUndoLogManager(&undoLogManager{}); err != nil { | |||
panic(errors.WithStack(err)) | |||
} | |||
} |
@@ -24,17 +24,24 @@ import ( | |||
"errors" | |||
"sync" | |||
"github.com/seata/seata-go/pkg/datasource/sql/exec" | |||
"github.com/seata/seata-go/pkg/datasource/sql/types" | |||
"github.com/seata/seata-go/pkg/datasource/sql/undo/builder" | |||
) | |||
func init() { | |||
RegistrUndoLogBuilder(&builder.MySQLUpdateUndoLogBuilder{}) | |||
} | |||
var solts = map[types.DBType]*undoLogMgrHolder{} | |||
var builders = map[types.SQLType]UndoLogBuilder{} | |||
type undoLogMgrHolder struct { | |||
once sync.Once | |||
mgr UndoLogManager | |||
} | |||
func Regis(m UndoLogManager) error { | |||
func RegisterUndoLogManager(m UndoLogManager) error { | |||
if _, exist := solts[m.DBType()]; exist { | |||
return nil | |||
} | |||
@@ -46,6 +53,16 @@ func Regis(m UndoLogManager) error { | |||
return nil | |||
} | |||
func RegistrUndoLogBuilder(m UndoLogBuilder) { | |||
if _, ok := builders[m.GetSQLType()]; !ok { | |||
builders[m.GetSQLType()] = m | |||
} | |||
} | |||
func GetUndologBuilder(sqlType types.SQLType) UndoLogBuilder { | |||
return builders[sqlType] | |||
} | |||
// UndoLogManager | |||
type UndoLogManager interface { | |||
Init() | |||
@@ -113,3 +130,9 @@ type UndoLogParser interface { | |||
// Decode | |||
Decode(b []byte) BranchUndoLog | |||
} | |||
type UndoLogBuilder interface { | |||
BeforeImage(ctx context.Context, execCtx *exec.ExecContext) (*types.RecordImage, error) | |||
AfterImage(types.RecordImages) (*types.RecordImages, error) | |||
GetSQLType() types.SQLType | |||
} |
@@ -78,7 +78,7 @@ func TestHasUndoLogTable(t *testing.T) { | |||
t.SkipNow() | |||
testHasUndoLogTable := func() { | |||
db, err := sql.Open(SeataMySQLDriver, "root:12345678@tcp(127.0.0.1:3306)/seata_order?multiStatements=true") | |||
db, err := sql.Open(SeataATMySQLDriver, "root:12345678@tcp(127.0.0.1:3306)/seata_order?multiStatements=true") | |||
assert.Nil(t, err) | |||
ctx := context.Background() | |||
@@ -1,3 +1,20 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package gin | |||
import ( | |||
@@ -35,7 +35,7 @@ func InitTmClient() { | |||
// initConfig init config processor | |||
func initConfig() { | |||
// todo implement | |||
// todo implement | |||
} | |||
// initRemoting init rpc client | |||
@@ -1,3 +1,20 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package main | |||
import ( | |||
@@ -1,3 +1,20 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package main | |||
import ( | |||
@@ -1,3 +1,20 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package main | |||
import ( | |||