* fix error log when loading issues caused by a xorm bug * upgrade packages * fix fmt * fix Consistency * fix teststags/v1.9.0-rc1
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect | github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect | ||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect | ||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect | github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect | ||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f | |||||
github.com/denisenkom/go-mssqldb v0.0.0-20190121005146-b04fd42d9952 | |||||
github.com/dgrijalva/jwt-go v0.0.0-20161101193935-9ed569b5d1ac | github.com/dgrijalva/jwt-go v0.0.0-20161101193935-9ed569b5d1ac | ||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect | github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect | ||||
github.com/emirpasic/gods v1.12.0 | github.com/emirpasic/gods v1.12.0 | ||||
github.com/go-macaron/session v0.0.0-20190131233854-0a0a789bf193 | github.com/go-macaron/session v0.0.0-20190131233854-0a0a789bf193 | ||||
github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90 | github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90 | ||||
github.com/go-redis/redis v6.15.2+incompatible | github.com/go-redis/redis v6.15.2+incompatible | ||||
github.com/go-sql-driver/mysql v1.4.0 | |||||
github.com/go-xorm/builder v0.3.3 | |||||
github.com/go-xorm/core v0.6.0 | |||||
github.com/go-xorm/xorm v0.0.0-20190116032649-a6300f2a45e0 | |||||
github.com/go-sql-driver/mysql v1.4.1 | |||||
github.com/go-xorm/core v0.6.0 // indirect | |||||
github.com/go-xorm/xorm v0.7.3-0.20190620151208-f1b4f8368459 | |||||
github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561 | github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561 | ||||
github.com/gogits/cron v0.0.0-20160810035002-7f3990acf183 | github.com/gogits/cron v0.0.0-20160810035002-7f3990acf183 | ||||
github.com/gogo/protobuf v1.2.1 // indirect | github.com/gogo/protobuf v1.2.1 // indirect | ||||
gopkg.in/redis.v2 v2.3.2 // indirect | gopkg.in/redis.v2 v2.3.2 // indirect | ||||
gopkg.in/src-d/go-billy.v4 v4.3.0 | gopkg.in/src-d/go-billy.v4 v4.3.0 | ||||
gopkg.in/src-d/go-git.v4 v4.12.0 | gopkg.in/src-d/go-git.v4 v4.12.0 | ||||
gopkg.in/stretchr/testify.v1 v1.2.2 // indirect | |||||
gopkg.in/testfixtures.v2 v2.5.0 | gopkg.in/testfixtures.v2 v2.5.0 | ||||
mvdan.cc/xurls/v2 v2.0.0 | mvdan.cc/xurls/v2 v2.0.0 | ||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a | strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a | ||||
xorm.io/builder v0.3.5 | |||||
xorm.io/core v0.6.3 | |||||
) | ) | ||||
replace ( | |||||
github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20161128230840-e32ca5036449 | |||||
github.com/go-sql-driver/mysql => github.com/go-sql-driver/mysql v0.0.0-20181218123637-c45f530f8e7f | |||||
) | |||||
replace github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20161128230840-e32ca5036449 |
cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= | |||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | |||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | ||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 h1:UsHpWO0Elp6NaWVARdZHjiYwkhrspHVEGsyIKPb9OI8= | github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 h1:UsHpWO0Elp6NaWVARdZHjiYwkhrspHVEGsyIKPb9OI8= | ||||
github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90/go.mod h1:Ut/NmkIMGVYlEdJBzEZgWVWG5ZpYS9BLmUgXfAgi+qM= | github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90/go.mod h1:Ut/NmkIMGVYlEdJBzEZgWVWG5ZpYS9BLmUgXfAgi+qM= | ||||
github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4= | github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4= | ||||
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= | github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= | ||||
github.com/go-sql-driver/mysql v0.0.0-20181218123637-c45f530f8e7f h1:fbIzwEaXt5b2bl9mm+PIufKTSGKk6ZuwSSTQ7iZj7Lo= | |||||
github.com/go-sql-driver/mysql v0.0.0-20181218123637-c45f530f8e7f/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= | |||||
github.com/go-xorm/builder v0.3.2/go.mod h1:v8mE3MFBgtL+RGFNfUnAMUqqfk/Y4W5KuwCFQIEpQLk= | |||||
github.com/go-xorm/builder v0.3.3 h1:v8grgrwOGv/iHXIEhIvOwHZIPLrpxRKSX8yWSMLFn/4= | |||||
github.com/go-xorm/builder v0.3.3/go.mod h1:v8mE3MFBgtL+RGFNfUnAMUqqfk/Y4W5KuwCFQIEpQLk= | |||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= | |||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= | |||||
github.com/go-xorm/core v0.6.0 h1:tp6hX+ku4OD9khFZS8VGBDRY3kfVCtelPfmkgCyHxL0= | github.com/go-xorm/core v0.6.0 h1:tp6hX+ku4OD9khFZS8VGBDRY3kfVCtelPfmkgCyHxL0= | ||||
github.com/go-xorm/core v0.6.0/go.mod h1:d8FJ9Br8OGyQl12MCclmYBuBqqxsyeedpXciV5Myih8= | github.com/go-xorm/core v0.6.0/go.mod h1:d8FJ9Br8OGyQl12MCclmYBuBqqxsyeedpXciV5Myih8= | ||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= | github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= | ||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= | github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= | ||||
github.com/go-xorm/xorm v0.0.0-20190116032649-a6300f2a45e0 h1:GBnJjWjp2SGXBZsyZfYksyp7QocvQwf9vZQ0NRN2FXM= | |||||
github.com/go-xorm/xorm v0.0.0-20190116032649-a6300f2a45e0/go.mod h1:EHS1htMQFptzMaIHKyzqpHGw6C9Rtug75nsq6DA9unI= | |||||
github.com/go-xorm/xorm v0.7.3-0.20190620151208-f1b4f8368459 h1:JGEuhH169J7Wtm1hN/HFOGENsAq+6FDHfuhGEZj/1e4= | |||||
github.com/go-xorm/xorm v0.7.3-0.20190620151208-f1b4f8368459/go.mod h1:UK1YDlWscDspd23xW9HC24749jhvwO6riZ/HUt3gbHQ= | |||||
github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561 h1:deE7ritpK04PgtpyVOS2TYcQEld9qLCD5b5EbVNOuLA= | github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561 h1:deE7ritpK04PgtpyVOS2TYcQEld9qLCD5b5EbVNOuLA= | ||||
github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:YgYOrVn3Nj9Tq0EvjmFbphRytDj7JNRoWSStJZWDJTQ= | github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:YgYOrVn3Nj9Tq0EvjmFbphRytDj7JNRoWSStJZWDJTQ= | ||||
github.com/gogits/cron v0.0.0-20160810035002-7f3990acf183 h1:EBTlva3AOSb80G3JSwY6ZMdILEZJ1JKuewrbqrNjWuE= | github.com/gogits/cron v0.0.0-20160810035002-7f3990acf183 h1:EBTlva3AOSb80G3JSwY6ZMdILEZJ1JKuewrbqrNjWuE= | ||||
github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c/go.mod h1:5mTb/PQNkqmq2x3IxlQZE0aSnTksJg7fg/oWmJ5SKXQ= | github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c/go.mod h1:5mTb/PQNkqmq2x3IxlQZE0aSnTksJg7fg/oWmJ5SKXQ= | ||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= | github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= | ||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= | github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= | ||||
github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY= | |||||
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= | |||||
github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90= | |||||
github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= | |||||
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= | github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= | ||||
github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d h1:ig/iUfDDg06RVW8OMby+GrmW6K2nPO3AFHlEIdvJSd4= | github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d h1:ig/iUfDDg06RVW8OMby+GrmW6K2nPO3AFHlEIdvJSd4= | ||||
github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= | github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= | ||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||||
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d h1:m+dSK37rFf2fqppZhg15yI2IwC9BtucBiRwSDm9VL8g= | github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d h1:m+dSK37rFf2fqppZhg15yI2IwC9BtucBiRwSDm9VL8g= | ||||
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8= | github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8= | ||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | |||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= | github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= | ||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | ||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= | ||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= | github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= | ||||
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= | github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= | ||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= | github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= | ||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | |||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= | 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/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= | go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= | ||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= | ||||
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||
golang.org/x/crypto v0.0.0-20190122013713-64072686203f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | |||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk= | golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk= | ||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||
google.golang.org/appengine v1.2.0 h1:S0iUepdCWODXRvtE+gcRDd15L+k+k1AiHlMiMjefH24= | google.golang.org/appengine v1.2.0 h1:S0iUepdCWODXRvtE+gcRDd15L+k+k1AiHlMiMjefH24= | ||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= | |||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | |||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= | gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= | ||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= | gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= | ||||
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 h1:nn6Zav2sOQHCFJHEspya8KqxhFwKci30UxHy3HXPTyQ= | gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 h1:nn6Zav2sOQHCFJHEspya8KqxhFwKci30UxHy3HXPTyQ= | ||||
mvdan.cc/xurls/v2 v2.0.0/go.mod h1:2/webFPYOXN9jp/lzuj0zuAVlF+9g4KPFJANH1oJhRU= | mvdan.cc/xurls/v2 v2.0.0/go.mod h1:2/webFPYOXN9jp/lzuj0zuAVlF+9g4KPFJANH1oJhRU= | ||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a h1:8q33ShxKXRwQ7JVd1ZnhIU3hZhwwn0Le+4fTeAackuM= | strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a h1:8q33ShxKXRwQ7JVd1ZnhIU3hZhwwn0Le+4fTeAackuM= | ||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= | strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= | ||||
xorm.io/builder v0.3.5 h1:EilU39fvWDxjb1cDaELpYhsF+zziRBhew8xk4pngO+A= | |||||
xorm.io/builder v0.3.5/go.mod h1:ZFbByS/KxZI1FKRjL05PyJ4YrK2bcxlUaAxdum5aTR8= | |||||
xorm.io/core v0.6.3 h1:n1NhVZt1s2oLw1BZfX2ocIJsHyso259uPgg63BGr37M= | |||||
xorm.io/core v0.6.3/go.mod h1:8kz/C6arVW/O9vk3PgCiMJO2hIAm1UcuOL3dSPyZ2qo= |
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/builder" | |||||
"xorm.io/builder" | |||||
) | ) | ||||
// ActionType represents the type of an action. | // ActionType represents the type of an action. |
ptrToSliceValue := reflect.New(sliceType) | ptrToSliceValue := reflect.New(sliceType) | ||||
ptrToSliceValue.Elem().Set(sliceValue) | ptrToSliceValue.Elem().Set(sliceValue) | ||||
assert.NoError(t, x.Where(bean).Find(ptrToSliceValue.Interface())) | |||||
assert.NoError(t, x.Table(bean).Find(ptrToSliceValue.Interface())) | |||||
sliceValue = ptrToSliceValue.Elem() | sliceValue = ptrToSliceValue.Elem() | ||||
for i := 0; i < sliceValue.Len(); i++ { | for i := 0; i < sliceValue.Len(); i++ { |
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/builder" | |||||
) | ) | ||||
// Issue represents an issue or pull request of repository. | // Issue represents an issue or pull request of repository. | ||||
if err := sess.Find(&issues); err != nil { | if err := sess.Find(&issues); err != nil { | ||||
return nil, fmt.Errorf("Find: %v", err) | return nil, fmt.Errorf("Find: %v", err) | ||||
} | } | ||||
sess.Close() | |||||
if err := IssueList(issues).LoadAttributes(); err != nil { | if err := IssueList(issues).LoadAttributes(); err != nil { | ||||
return nil, fmt.Errorf("LoadAttributes: %v", err) | return nil, fmt.Errorf("LoadAttributes: %v", err) |
"code.gitea.io/gitea/modules/markup/markdown" | "code.gitea.io/gitea/modules/markup/markdown" | ||||
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/builder" | |||||
api "code.gitea.io/gitea/modules/structs" | api "code.gitea.io/gitea/modules/structs" | ||||
import ( | import ( | ||||
"fmt" | "fmt" | ||||
"code.gitea.io/gitea/modules/log" | |||||
"github.com/go-xorm/builder" | |||||
"xorm.io/builder" | |||||
) | ) | ||||
// IssueList defines a list of issues | // IssueList defines a list of issues | ||||
var labelIssue LabelIssue | var labelIssue LabelIssue | ||||
err = rows.Scan(&labelIssue) | err = rows.Scan(&labelIssue) | ||||
if err != nil { | if err != nil { | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadLabels: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadLabels: Close: %v", err1) | |||||
} | } | ||||
return err | return err | ||||
} | } | ||||
issueLabels[labelIssue.IssueLabel.IssueID] = append(issueLabels[labelIssue.IssueLabel.IssueID], labelIssue.Label) | issueLabels[labelIssue.IssueLabel.IssueID] = append(issueLabels[labelIssue.IssueLabel.IssueID], labelIssue.Label) | ||||
} | } | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// When there are no rows left and we try to close it. | |||||
// Since that is not relevant for us, we can safely ignore it. | // Since that is not relevant for us, we can safely ignore it. | ||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadLabels: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadLabels: Close: %v", err1) | |||||
} | } | ||||
left -= limit | left -= limit | ||||
issueIDs = issueIDs[limit:] | issueIDs = issueIDs[limit:] | ||||
var assigneeIssue AssigneeIssue | var assigneeIssue AssigneeIssue | ||||
err = rows.Scan(&assigneeIssue) | err = rows.Scan(&assigneeIssue) | ||||
if err != nil { | if err != nil { | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadAssignees: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadAssignees: Close: %v", err1) | |||||
} | } | ||||
return err | return err | ||||
} | } | ||||
assignees[assigneeIssue.IssueAssignee.IssueID] = append(assignees[assigneeIssue.IssueAssignee.IssueID], assigneeIssue.Assignee) | assignees[assigneeIssue.IssueAssignee.IssueID] = append(assignees[assigneeIssue.IssueAssignee.IssueID], assigneeIssue.Assignee) | ||||
} | } | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadAssignees: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadAssignees: Close: %v", err1) | |||||
} | } | ||||
left -= limit | left -= limit | ||||
issueIDs = issueIDs[limit:] | issueIDs = issueIDs[limit:] | ||||
var pr PullRequest | var pr PullRequest | ||||
err = rows.Scan(&pr) | err = rows.Scan(&pr) | ||||
if err != nil { | if err != nil { | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadPullRequests: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadPullRequests: Close: %v", err1) | |||||
} | } | ||||
return err | return err | ||||
} | } | ||||
pullRequestMaps[pr.IssueID] = &pr | pullRequestMaps[pr.IssueID] = &pr | ||||
} | } | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadPullRequests: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadPullRequests: Close: %v", err1) | |||||
} | } | ||||
left -= limit | left -= limit | ||||
issuesIDs = issuesIDs[limit:] | issuesIDs = issuesIDs[limit:] | ||||
var attachment Attachment | var attachment Attachment | ||||
err = rows.Scan(&attachment) | err = rows.Scan(&attachment) | ||||
if err != nil { | if err != nil { | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadAttachments: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadAttachments: Close: %v", err1) | |||||
} | } | ||||
return err | return err | ||||
} | } | ||||
attachments[attachment.IssueID] = append(attachments[attachment.IssueID], &attachment) | attachments[attachment.IssueID] = append(attachments[attachment.IssueID], &attachment) | ||||
} | } | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadAttachments: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadAttachments: Close: %v", err1) | |||||
} | } | ||||
left -= limit | left -= limit | ||||
issuesIDs = issuesIDs[limit:] | issuesIDs = issuesIDs[limit:] | ||||
var comment Comment | var comment Comment | ||||
err = rows.Scan(&comment) | err = rows.Scan(&comment) | ||||
if err != nil { | if err != nil { | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadComments: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadComments: Close: %v", err1) | |||||
} | } | ||||
return err | return err | ||||
} | } | ||||
comments[comment.IssueID] = append(comments[comment.IssueID], &comment) | comments[comment.IssueID] = append(comments[comment.IssueID], &comment) | ||||
} | } | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadComments: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadComments: Close: %v", err1) | |||||
} | } | ||||
left -= limit | left -= limit | ||||
issuesIDs = issuesIDs[limit:] | issuesIDs = issuesIDs[limit:] | ||||
var totalTime totalTimesByIssue | var totalTime totalTimesByIssue | ||||
err = rows.Scan(&totalTime) | err = rows.Scan(&totalTime) | ||||
if err != nil { | if err != nil { | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadTotalTrackedTimes: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadTotalTrackedTimes: Close: %v", err1) | |||||
} | } | ||||
return err | return err | ||||
} | } | ||||
trackedTimes[totalTime.IssueID] = totalTime.Time | trackedTimes[totalTime.IssueID] = totalTime.Time | ||||
} | } | ||||
// When there are no rows left and we try to close it, xorm will complain with an error. | |||||
// Since that is not relevant for us, we can safely ignore it. | |||||
if err := rows.Close(); err != nil { | |||||
log.Error("IssueList.loadTotalTrackedTimes: Close: %v", err) | |||||
if err1 := rows.Close(); err1 != nil { | |||||
return fmt.Errorf("IssueList.loadTotalTrackedTimes: Close: %v", err1) | |||||
} | } | ||||
left -= limit | left -= limit | ||||
ids = ids[limit:] | ids = ids[limit:] |
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/builder" | |||||
) | ) | ||||
// Reaction represents a reactions on issues and comments. | // Reaction represents a reactions on issues and comments. |
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
api "code.gitea.io/gitea/modules/structs" | api "code.gitea.io/gitea/modules/structs" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/builder" | |||||
) | ) | ||||
// TrackedTime represents a time that was spent for a specific issue. | // TrackedTime represents a time that was spent for a specific issue. |
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// XORMLogBridge a logger bridge from Logger to xorm | // XORMLogBridge a logger bridge from Logger to xorm |
"strings" | "strings" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/core" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/core" | |||||
"code.gitea.io/gitea/modules/auth/ldap" | "code.gitea.io/gitea/modules/auth/ldap" | ||||
"code.gitea.io/gitea/modules/auth/oauth2" | "code.gitea.io/gitea/modules/auth/oauth2" |
"fmt" | "fmt" | ||||
"time" | "time" | ||||
"github.com/go-xorm/core" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/core" | |||||
) | ) | ||||
func addLoginSourceSyncEnabledColumn(x *xorm.Engine) error { | func addLoginSourceSyncEnabledColumn(x *xorm.Engine) error { |
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"github.com/go-xorm/core" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/core" | |||||
) | ) | ||||
func removeCommitsUnitType(x *xorm.Engine) (err error) { | func removeCommitsUnitType(x *xorm.Engine) (err error) { |
package migrations | package migrations | ||||
import ( | import ( | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/builder" | |||||
) | ) | ||||
func clearNonusedData(x *xorm.Engine) error { | func clearNonusedData(x *xorm.Engine) error { |
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
"github.com/go-xorm/core" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/core" | |||||
) | ) | ||||
func renameRepoIsBareToIsEmpty(x *xorm.Engine) error { | func renameRepoIsBareToIsEmpty(x *xorm.Engine) error { |
import ( | import ( | ||||
"fmt" | "fmt" | ||||
"github.com/go-xorm/core" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/core" | |||||
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"code.gitea.io/gitea/modules/generate" | "code.gitea.io/gitea/modules/generate" |
// Needed for the MySQL driver | // Needed for the MySQL driver | ||||
_ "github.com/go-sql-driver/mysql" | _ "github.com/go-sql-driver/mysql" | ||||
"github.com/go-xorm/core" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/core" | |||||
// Needed for the Postgresql driver | // Needed for the Postgresql driver | ||||
_ "github.com/lib/pq" | _ "github.com/lib/pq" |
"code.gitea.io/gitea/modules/structs" | "code.gitea.io/gitea/modules/structs" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/builder" | |||||
) | ) | ||||
var ( | var ( |
api "code.gitea.io/gitea/modules/structs" | api "code.gitea.io/gitea/modules/structs" | ||||
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/go-xorm/builder" | |||||
"xorm.io/builder" | |||||
) | ) | ||||
// Release represents a release of repository. | // Release represents a release of repository. |
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
ini "gopkg.in/ini.v1" | ini "gopkg.in/ini.v1" | ||||
"xorm.io/builder" | |||||
) | ) | ||||
var repoWorkingPool = sync.NewExclusivePool() | var repoWorkingPool = sync.NewExclusivePool() |
"code.gitea.io/gitea/modules/structs" | "code.gitea.io/gitea/modules/structs" | ||||
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/go-xorm/builder" | |||||
"xorm.io/builder" | |||||
) | ) | ||||
// RepositoryListDefaultPageSize is the default number of repositories | // RepositoryListDefaultPageSize is the default number of repositories |
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/core" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/core" | |||||
) | ) | ||||
// RepoUnit describes all units of a repository | // RepoUnit describes all units of a repository |
api "code.gitea.io/gitea/modules/structs" | api "code.gitea.io/gitea/modules/structs" | ||||
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/core" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"xorm.io/builder" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// ReviewType defines the sort of feedback a review gives | // ReviewType defines the sort of feedback a review gives |
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"golang.org/x/crypto/ssh" | "golang.org/x/crypto/ssh" | ||||
"xorm.io/builder" | |||||
) | ) | ||||
const ( | const ( |
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/go-xorm/builder" | |||||
"xorm.io/builder" | |||||
) | ) | ||||
func init() { | func init() { |
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/core" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"github.com/stretchr/testify/assert" | "github.com/stretchr/testify/assert" | ||||
"gopkg.in/testfixtures.v2" | "gopkg.in/testfixtures.v2" | ||||
"xorm.io/core" | |||||
) | ) | ||||
// NonexistentID an ID that will never exist | // NonexistentID an ID that will never exist |
"code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/core" | |||||
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"golang.org/x/crypto/pbkdf2" | "golang.org/x/crypto/pbkdf2" | ||||
"golang.org/x/crypto/ssh" | "golang.org/x/crypto/ssh" | ||||
"xorm.io/builder" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// UserType defines the user type | // UserType defines the user type |
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
"github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
const ( | const ( |
sudo: false | sudo: false | ||||
language: go | language: go | ||||
go: | go: | ||||
- 1.7.x | |||||
- 1.8.x | - 1.8.x | ||||
- 1.9.x | - 1.9.x | ||||
- 1.10.x | - 1.10.x | ||||
- 1.11.x | |||||
- master | - master | ||||
before_install: | before_install: |
Henri Yandell <flamefew at gmail.com> | Henri Yandell <flamefew at gmail.com> | ||||
Hirotaka Yamamoto <ymmt2005 at gmail.com> | Hirotaka Yamamoto <ymmt2005 at gmail.com> | ||||
ICHINOSE Shogo <shogo82148 at gmail.com> | ICHINOSE Shogo <shogo82148 at gmail.com> | ||||
Ilia Cimpoes <ichimpoesh at gmail.com> | |||||
INADA Naoki <songofacandy at gmail.com> | INADA Naoki <songofacandy at gmail.com> | ||||
Jacek Szwec <szwec.jacek at gmail.com> | Jacek Szwec <szwec.jacek at gmail.com> | ||||
James Harr <james.harr at gmail.com> | James Harr <james.harr at gmail.com> | ||||
Soroush Pour <me at soroushjp.com> | Soroush Pour <me at soroushjp.com> | ||||
Stan Putrya <root.vagner at gmail.com> | Stan Putrya <root.vagner at gmail.com> | ||||
Stanley Gunawan <gunawan.stanley at gmail.com> | Stanley Gunawan <gunawan.stanley at gmail.com> | ||||
Steven Hartland <steven.hartland at multiplay.co.uk> | |||||
Thomas Wodarek <wodarekwebpage at gmail.com> | |||||
Tom Jenkinson <tom at tjenkinson.me> | |||||
Xiangyu Hu <xiangyu.hu at outlook.com> | Xiangyu Hu <xiangyu.hu at outlook.com> | ||||
Xiaobing Jiang <s7v7nislands at gmail.com> | Xiaobing Jiang <s7v7nislands at gmail.com> | ||||
Xiuming Chen <cc at cxm.cc> | Xiuming Chen <cc at cxm.cc> | ||||
Percona LLC | Percona LLC | ||||
Pivotal Inc. | Pivotal Inc. | ||||
Stripe Inc. | Stripe Inc. | ||||
Multiplay Ltd. |
## Version 1.4.1 (2018-11-14) | |||||
Bugfixes: | |||||
- Fix TIME format for binary columns (#818) | |||||
- Fix handling of empty auth plugin names (#835) | |||||
- Fix caching_sha2_password with empty password (#826) | |||||
- Fix canceled context broke mysqlConn (#862) | |||||
- Fix OldAuthSwitchRequest support (#870) | |||||
- Fix Auth Response packet for cleartext password (#887) | |||||
## Version 1.4 (2018-06-03) | ## Version 1.4 (2018-06-03) | ||||
Changes: | Changes: |
* Optional placeholder interpolation | * Optional placeholder interpolation | ||||
## Requirements | ## Requirements | ||||
* Go 1.8 or higher. We aim to support the 3 latest versions of Go. | |||||
* Go 1.7 or higher. We aim to support the 3 latest versions of Go. | |||||
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+) | * MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+) | ||||
--------------------------------------- | --------------------------------------- | ||||
``` | ``` | ||||
Type: bool / string | Type: bool / string | ||||
Valid Values: true, false, skip-verify, preferred, <name> | |||||
Valid Values: true, false, skip-verify, <name> | |||||
Default: false | Default: false | ||||
``` | ``` | ||||
`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side) or use `preferred` to use TLS only when advertised by the server. This is similar to `skip-verify`, but additionally allows a fallback to a connection which is not encrypted. Neither `skip-verify` nor `preferred` add any reliable security. You can use a custom TLS config after registering it with [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig). | |||||
`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig). | |||||
##### `writeTimeout` | ##### `writeTimeout` |
pubKey := mc.cfg.pubKey | pubKey := mc.cfg.pubKey | ||||
if pubKey == nil { | if pubKey == nil { | ||||
// request public key from server | // request public key from server | ||||
data, err := mc.buf.takeSmallBuffer(4 + 1) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
data := mc.buf.takeSmallBuffer(4 + 1) | |||||
data[4] = cachingSha2PasswordRequestPublicKey | data[4] = cachingSha2PasswordRequestPublicKey | ||||
mc.writePacket(data) | mc.writePacket(data) | ||||
// parse public key | // parse public key | ||||
if data, err = mc.readPacket(); err != nil { | |||||
data, err := mc.readPacket() | |||||
if err != nil { | |||||
return err | return err | ||||
} | } | ||||
// The buffer is similar to bufio.Reader / Writer but zero-copy-ish | // The buffer is similar to bufio.Reader / Writer but zero-copy-ish | ||||
// Also highly optimized for this particular use case. | // Also highly optimized for this particular use case. | ||||
type buffer struct { | type buffer struct { | ||||
buf []byte // buf is a byte buffer who's length and capacity are equal. | |||||
buf []byte | |||||
nc net.Conn | nc net.Conn | ||||
idx int | idx int | ||||
length int | length int | ||||
timeout time.Duration | timeout time.Duration | ||||
} | } | ||||
// newBuffer allocates and returns a new buffer. | |||||
func newBuffer(nc net.Conn) buffer { | func newBuffer(nc net.Conn) buffer { | ||||
var b [defaultBufSize]byte | |||||
return buffer{ | return buffer{ | ||||
buf: make([]byte, defaultBufSize), | |||||
buf: b[:], | |||||
nc: nc, | nc: nc, | ||||
} | } | ||||
} | } | ||||
return b.buf[offset:b.idx], nil | return b.buf[offset:b.idx], nil | ||||
} | } | ||||
// takeBuffer returns a buffer with the requested size. | |||||
// returns a buffer with the requested size. | |||||
// If possible, a slice from the existing buffer is returned. | // If possible, a slice from the existing buffer is returned. | ||||
// Otherwise a bigger buffer is made. | // Otherwise a bigger buffer is made. | ||||
// Only one buffer (total) can be used at a time. | // Only one buffer (total) can be used at a time. | ||||
func (b *buffer) takeBuffer(length int) ([]byte, error) { | |||||
func (b *buffer) takeBuffer(length int) []byte { | |||||
if b.length > 0 { | if b.length > 0 { | ||||
return nil, ErrBusyBuffer | |||||
return nil | |||||
} | } | ||||
// test (cheap) general case first | // test (cheap) general case first | ||||
if length <= cap(b.buf) { | |||||
return b.buf[:length], nil | |||||
if length <= defaultBufSize || length <= cap(b.buf) { | |||||
return b.buf[:length] | |||||
} | } | ||||
if length < maxPacketSize { | if length < maxPacketSize { | ||||
b.buf = make([]byte, length) | b.buf = make([]byte, length) | ||||
return b.buf, nil | |||||
return b.buf | |||||
} | } | ||||
// buffer is larger than we want to store. | |||||
return make([]byte, length), nil | |||||
return make([]byte, length) | |||||
} | } | ||||
// takeSmallBuffer is shortcut which can be used if length is | |||||
// known to be smaller than defaultBufSize. | |||||
// shortcut which can be used if the requested buffer is guaranteed to be | |||||
// smaller than defaultBufSize | |||||
// Only one buffer (total) can be used at a time. | // Only one buffer (total) can be used at a time. | ||||
func (b *buffer) takeSmallBuffer(length int) ([]byte, error) { | |||||
func (b *buffer) takeSmallBuffer(length int) []byte { | |||||
if b.length > 0 { | if b.length > 0 { | ||||
return nil, ErrBusyBuffer | |||||
return nil | |||||
} | } | ||||
return b.buf[:length], nil | |||||
return b.buf[:length] | |||||
} | } | ||||
// takeCompleteBuffer returns the complete existing buffer. | // takeCompleteBuffer returns the complete existing buffer. | ||||
// This can be used if the necessary buffer size is unknown. | // This can be used if the necessary buffer size is unknown. | ||||
// cap and len of the returned buffer will be equal. | |||||
// Only one buffer (total) can be used at a time. | // Only one buffer (total) can be used at a time. | ||||
func (b *buffer) takeCompleteBuffer() ([]byte, error) { | |||||
if b.length > 0 { | |||||
return nil, ErrBusyBuffer | |||||
} | |||||
return b.buf, nil | |||||
} | |||||
// store stores buf, an updated buffer, if its suitable to do so. | |||||
func (b *buffer) store(buf []byte) error { | |||||
func (b *buffer) takeCompleteBuffer() []byte { | |||||
if b.length > 0 { | if b.length > 0 { | ||||
return ErrBusyBuffer | |||||
} else if cap(buf) <= maxPacketSize && cap(buf) > cap(b.buf) { | |||||
b.buf = buf[:cap(buf)] | |||||
return nil | |||||
} | } | ||||
return nil | |||||
return b.buf | |||||
} | } |
package mysql | package mysql | ||||
import ( | import ( | ||||
"context" | |||||
"database/sql" | |||||
"database/sql/driver" | "database/sql/driver" | ||||
"io" | "io" | ||||
"net" | "net" | ||||
"time" | "time" | ||||
) | ) | ||||
// a copy of context.Context for Go 1.7 and earlier | |||||
type mysqlContext interface { | |||||
Done() <-chan struct{} | |||||
Err() error | |||||
// defined in context.Context, but not used in this driver: | |||||
// Deadline() (deadline time.Time, ok bool) | |||||
// Value(key interface{}) interface{} | |||||
} | |||||
type mysqlConn struct { | type mysqlConn struct { | ||||
buf buffer | buf buffer | ||||
netConn net.Conn | netConn net.Conn | ||||
// for context support (Go 1.8+) | // for context support (Go 1.8+) | ||||
watching bool | watching bool | ||||
watcher chan<- context.Context | |||||
watcher chan<- mysqlContext | |||||
closech chan struct{} | closech chan struct{} | ||||
finished chan<- struct{} | finished chan<- struct{} | ||||
canceled atomicError // set non-nil if conn is canceled | canceled atomicError // set non-nil if conn is canceled | ||||
return "", driver.ErrSkip | return "", driver.ErrSkip | ||||
} | } | ||||
buf, err := mc.buf.takeCompleteBuffer() | |||||
if err != nil { | |||||
buf := mc.buf.takeCompleteBuffer() | |||||
if buf == nil { | |||||
// can not take the buffer. Something must be wrong with the connection | // can not take the buffer. Something must be wrong with the connection | ||||
errLog.Print(err) | |||||
errLog.Print(ErrBusyBuffer) | |||||
return "", ErrInvalidConn | return "", ErrInvalidConn | ||||
} | } | ||||
buf = buf[:0] | buf = buf[:0] | ||||
case <-mc.closech: | case <-mc.closech: | ||||
} | } | ||||
} | } | ||||
// Ping implements driver.Pinger interface | |||||
func (mc *mysqlConn) Ping(ctx context.Context) (err error) { | |||||
if mc.closed.IsSet() { | |||||
errLog.Print(ErrInvalidConn) | |||||
return driver.ErrBadConn | |||||
} | |||||
if err = mc.watchCancel(ctx); err != nil { | |||||
return | |||||
} | |||||
defer mc.finish() | |||||
if err = mc.writeCommandPacket(comPing); err != nil { | |||||
return mc.markBadConn(err) | |||||
} | |||||
return mc.readResultOK() | |||||
} | |||||
// BeginTx implements driver.ConnBeginTx interface | |||||
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { | |||||
if err := mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
defer mc.finish() | |||||
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault { | |||||
level, err := mapIsolationLevel(opts.Isolation) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
} | |||||
return mc.begin(opts.ReadOnly) | |||||
} | |||||
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { | |||||
dargs, err := namedValueToValue(args) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if err := mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
rows, err := mc.query(query, dargs) | |||||
if err != nil { | |||||
mc.finish() | |||||
return nil, err | |||||
} | |||||
rows.finish = mc.finish | |||||
return rows, err | |||||
} | |||||
func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { | |||||
dargs, err := namedValueToValue(args) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if err := mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
defer mc.finish() | |||||
return mc.Exec(query, dargs) | |||||
} | |||||
func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { | |||||
if err := mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
stmt, err := mc.Prepare(query) | |||||
mc.finish() | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
select { | |||||
default: | |||||
case <-ctx.Done(): | |||||
stmt.Close() | |||||
return nil, ctx.Err() | |||||
} | |||||
return stmt, nil | |||||
} | |||||
func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { | |||||
dargs, err := namedValueToValue(args) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if err := stmt.mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
rows, err := stmt.query(dargs) | |||||
if err != nil { | |||||
stmt.mc.finish() | |||||
return nil, err | |||||
} | |||||
rows.finish = stmt.mc.finish | |||||
return rows, err | |||||
} | |||||
func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { | |||||
dargs, err := namedValueToValue(args) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if err := stmt.mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
defer stmt.mc.finish() | |||||
return stmt.Exec(dargs) | |||||
} | |||||
func (mc *mysqlConn) watchCancel(ctx context.Context) error { | |||||
if mc.watching { | |||||
// Reach here if canceled, | |||||
// so the connection is already invalid | |||||
mc.cleanup() | |||||
return nil | |||||
} | |||||
// When ctx is already cancelled, don't watch it. | |||||
if err := ctx.Err(); err != nil { | |||||
return err | |||||
} | |||||
// When ctx is not cancellable, don't watch it. | |||||
if ctx.Done() == nil { | |||||
return nil | |||||
} | |||||
// When watcher is not alive, can't watch it. | |||||
if mc.watcher == nil { | |||||
return nil | |||||
} | |||||
mc.watching = true | |||||
mc.watcher <- ctx | |||||
return nil | |||||
} | |||||
func (mc *mysqlConn) startWatcher() { | |||||
watcher := make(chan context.Context, 1) | |||||
mc.watcher = watcher | |||||
finished := make(chan struct{}) | |||||
mc.finished = finished | |||||
go func() { | |||||
for { | |||||
var ctx context.Context | |||||
select { | |||||
case ctx = <-watcher: | |||||
case <-mc.closech: | |||||
return | |||||
} | |||||
select { | |||||
case <-ctx.Done(): | |||||
mc.cancel(ctx.Err()) | |||||
case <-finished: | |||||
case <-mc.closech: | |||||
return | |||||
} | |||||
} | |||||
}() | |||||
} | |||||
func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) { | |||||
nv.Value, err = converter{}.ConvertValue(nv.Value) | |||||
return | |||||
} | |||||
// ResetSession implements driver.SessionResetter. | |||||
// (From Go 1.10) | |||||
func (mc *mysqlConn) ResetSession(ctx context.Context) error { | |||||
if mc.closed.IsSet() { | |||||
return driver.ErrBadConn | |||||
} | |||||
return nil | |||||
} |
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package | |||||
// | |||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. | |||||
// | |||||
// This Source Code Form is subject to the terms of the Mozilla Public | |||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file, | |||||
// You can obtain one at http://mozilla.org/MPL/2.0/. | |||||
// +build go1.8 | |||||
package mysql | |||||
import ( | |||||
"context" | |||||
"database/sql" | |||||
"database/sql/driver" | |||||
) | |||||
// Ping implements driver.Pinger interface | |||||
func (mc *mysqlConn) Ping(ctx context.Context) (err error) { | |||||
if mc.closed.IsSet() { | |||||
errLog.Print(ErrInvalidConn) | |||||
return driver.ErrBadConn | |||||
} | |||||
if err = mc.watchCancel(ctx); err != nil { | |||||
return | |||||
} | |||||
defer mc.finish() | |||||
if err = mc.writeCommandPacket(comPing); err != nil { | |||||
return | |||||
} | |||||
return mc.readResultOK() | |||||
} | |||||
// BeginTx implements driver.ConnBeginTx interface | |||||
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { | |||||
if err := mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
defer mc.finish() | |||||
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault { | |||||
level, err := mapIsolationLevel(opts.Isolation) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
} | |||||
return mc.begin(opts.ReadOnly) | |||||
} | |||||
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { | |||||
dargs, err := namedValueToValue(args) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if err := mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
rows, err := mc.query(query, dargs) | |||||
if err != nil { | |||||
mc.finish() | |||||
return nil, err | |||||
} | |||||
rows.finish = mc.finish | |||||
return rows, err | |||||
} | |||||
func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { | |||||
dargs, err := namedValueToValue(args) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if err := mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
defer mc.finish() | |||||
return mc.Exec(query, dargs) | |||||
} | |||||
func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { | |||||
if err := mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
stmt, err := mc.Prepare(query) | |||||
mc.finish() | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
select { | |||||
default: | |||||
case <-ctx.Done(): | |||||
stmt.Close() | |||||
return nil, ctx.Err() | |||||
} | |||||
return stmt, nil | |||||
} | |||||
func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { | |||||
dargs, err := namedValueToValue(args) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if err := stmt.mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
rows, err := stmt.query(dargs) | |||||
if err != nil { | |||||
stmt.mc.finish() | |||||
return nil, err | |||||
} | |||||
rows.finish = stmt.mc.finish | |||||
return rows, err | |||||
} | |||||
func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { | |||||
dargs, err := namedValueToValue(args) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if err := stmt.mc.watchCancel(ctx); err != nil { | |||||
return nil, err | |||||
} | |||||
defer stmt.mc.finish() | |||||
return stmt.Exec(dargs) | |||||
} | |||||
func (mc *mysqlConn) watchCancel(ctx context.Context) error { | |||||
if mc.watching { | |||||
// Reach here if canceled, | |||||
// so the connection is already invalid | |||||
mc.cleanup() | |||||
return nil | |||||
} | |||||
// When ctx is already cancelled, don't watch it. | |||||
if err := ctx.Err(); err != nil { | |||||
return err | |||||
} | |||||
// When ctx is not cancellable, don't watch it. | |||||
if ctx.Done() == nil { | |||||
return nil | |||||
} | |||||
// When watcher is not alive, can't watch it. | |||||
if mc.watcher == nil { | |||||
return nil | |||||
} | |||||
mc.watching = true | |||||
mc.watcher <- ctx | |||||
return nil | |||||
} | |||||
func (mc *mysqlConn) startWatcher() { | |||||
watcher := make(chan mysqlContext, 1) | |||||
mc.watcher = watcher | |||||
finished := make(chan struct{}) | |||||
mc.finished = finished | |||||
go func() { | |||||
for { | |||||
var ctx mysqlContext | |||||
select { | |||||
case ctx = <-watcher: | |||||
case <-mc.closech: | |||||
return | |||||
} | |||||
select { | |||||
case <-ctx.Done(): | |||||
mc.cancel(ctx.Err()) | |||||
case <-finished: | |||||
case <-mc.closech: | |||||
return | |||||
} | |||||
} | |||||
}() | |||||
} | |||||
func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) { | |||||
nv.Value, err = converter{}.ConvertValue(nv.Value) | |||||
return | |||||
} | |||||
// ResetSession implements driver.SessionResetter. | |||||
// (From Go 1.10) | |||||
func (mc *mysqlConn) ResetSession(ctx context.Context) error { | |||||
if mc.closed.IsSet() { | |||||
return driver.ErrBadConn | |||||
} | |||||
return nil | |||||
} |
"sync" | "sync" | ||||
) | ) | ||||
// watcher interface is used for context support (From Go 1.8) | |||||
type watcher interface { | |||||
startWatcher() | |||||
} | |||||
// MySQLDriver is exported to make the driver directly accessible. | // MySQLDriver is exported to make the driver directly accessible. | ||||
// In general the driver is used via the database/sql package. | // In general the driver is used via the database/sql package. | ||||
type MySQLDriver struct{} | type MySQLDriver struct{} | ||||
// Open new Connection. | // Open new Connection. | ||||
// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how | // See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how | ||||
// the DSN string is formatted | |||||
// the DSN string is formated | |||||
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) { | func (d MySQLDriver) Open(dsn string) (driver.Conn, error) { | ||||
var err error | var err error | ||||
mc.netConn, err = nd.Dial(mc.cfg.Net, mc.cfg.Addr) | mc.netConn, err = nd.Dial(mc.cfg.Net, mc.cfg.Addr) | ||||
} | } | ||||
if err != nil { | if err != nil { | ||||
if nerr, ok := err.(net.Error); ok && nerr.Temporary() { | |||||
errLog.Print("net.Error from Dial()': ", nerr.Error()) | |||||
return nil, driver.ErrBadConn | |||||
} | |||||
return nil, err | return nil, err | ||||
} | } | ||||
} | } | ||||
// Call startWatcher for context support (From Go 1.8) | // Call startWatcher for context support (From Go 1.8) | ||||
mc.startWatcher() | |||||
if s, ok := interface{}(mc).(watcher); ok { | |||||
s.startWatcher() | |||||
} | |||||
mc.buf = newBuffer(mc.netConn) | mc.buf = newBuffer(mc.netConn) | ||||
} else { | } else { | ||||
cfg.TLSConfig = "false" | cfg.TLSConfig = "false" | ||||
} | } | ||||
} else if vl := strings.ToLower(value); vl == "skip-verify" || vl == "preferred" { | |||||
} else if vl := strings.ToLower(value); vl == "skip-verify" { | |||||
cfg.TLSConfig = vl | cfg.TLSConfig = vl | ||||
cfg.tls = &tls.Config{InsecureSkipVerify: true} | cfg.tls = &tls.Config{InsecureSkipVerify: true} | ||||
} else { | } else { |
mc.sequence++ | mc.sequence++ | ||||
// packets with length 0 terminate a previous packet which is a | // packets with length 0 terminate a previous packet which is a | ||||
// multiple of (2^24)-1 bytes long | |||||
// multiple of (2^24)−1 bytes long | |||||
if pktLen == 0 { | if pktLen == 0 { | ||||
// there was no previous packet | // there was no previous packet | ||||
if prevData == nil { | if prevData == nil { | ||||
return nil, "", ErrOldProtocol | return nil, "", ErrOldProtocol | ||||
} | } | ||||
if mc.flags&clientSSL == 0 && mc.cfg.tls != nil { | if mc.flags&clientSSL == 0 && mc.cfg.tls != nil { | ||||
if mc.cfg.TLSConfig == "preferred" { | |||||
mc.cfg.tls = nil | |||||
} else { | |||||
return nil, "", ErrNoTLS | |||||
} | |||||
return nil, "", ErrNoTLS | |||||
} | } | ||||
pos += 2 | pos += 2 | ||||
} | } | ||||
// Calculate packet length and get buffer with that size | // Calculate packet length and get buffer with that size | ||||
data, err := mc.buf.takeSmallBuffer(pktLen + 4) | |||||
if err != nil { | |||||
data := mc.buf.takeSmallBuffer(pktLen + 4) | |||||
if data == nil { | |||||
// cannot take the buffer. Something must be wrong with the connection | // cannot take the buffer. Something must be wrong with the connection | ||||
errLog.Print(err) | |||||
errLog.Print(ErrBusyBuffer) | |||||
return errBadConnNoWrite | return errBadConnNoWrite | ||||
} | } | ||||
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse | // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse | ||||
func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error { | func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error { | ||||
pktLen := 4 + len(authData) | pktLen := 4 + len(authData) | ||||
data, err := mc.buf.takeSmallBuffer(pktLen) | |||||
if err != nil { | |||||
data := mc.buf.takeSmallBuffer(pktLen) | |||||
if data == nil { | |||||
// cannot take the buffer. Something must be wrong with the connection | // cannot take the buffer. Something must be wrong with the connection | ||||
errLog.Print(err) | |||||
errLog.Print(ErrBusyBuffer) | |||||
return errBadConnNoWrite | return errBadConnNoWrite | ||||
} | } | ||||
// Reset Packet Sequence | // Reset Packet Sequence | ||||
mc.sequence = 0 | mc.sequence = 0 | ||||
data, err := mc.buf.takeSmallBuffer(4 + 1) | |||||
if err != nil { | |||||
data := mc.buf.takeSmallBuffer(4 + 1) | |||||
if data == nil { | |||||
// cannot take the buffer. Something must be wrong with the connection | // cannot take the buffer. Something must be wrong with the connection | ||||
errLog.Print(err) | |||||
errLog.Print(ErrBusyBuffer) | |||||
return errBadConnNoWrite | return errBadConnNoWrite | ||||
} | } | ||||
mc.sequence = 0 | mc.sequence = 0 | ||||
pktLen := 1 + len(arg) | pktLen := 1 + len(arg) | ||||
data, err := mc.buf.takeBuffer(pktLen + 4) | |||||
if err != nil { | |||||
data := mc.buf.takeBuffer(pktLen + 4) | |||||
if data == nil { | |||||
// cannot take the buffer. Something must be wrong with the connection | // cannot take the buffer. Something must be wrong with the connection | ||||
errLog.Print(err) | |||||
errLog.Print(ErrBusyBuffer) | |||||
return errBadConnNoWrite | return errBadConnNoWrite | ||||
} | } | ||||
// Reset Packet Sequence | // Reset Packet Sequence | ||||
mc.sequence = 0 | mc.sequence = 0 | ||||
data, err := mc.buf.takeSmallBuffer(4 + 1 + 4) | |||||
if err != nil { | |||||
data := mc.buf.takeSmallBuffer(4 + 1 + 4) | |||||
if data == nil { | |||||
// cannot take the buffer. Something must be wrong with the connection | // cannot take the buffer. Something must be wrong with the connection | ||||
errLog.Print(err) | |||||
errLog.Print(ErrBusyBuffer) | |||||
return errBadConnNoWrite | return errBadConnNoWrite | ||||
} | } | ||||
const minPktLen = 4 + 1 + 4 + 1 + 4 | const minPktLen = 4 + 1 + 4 + 1 + 4 | ||||
mc := stmt.mc | mc := stmt.mc | ||||
// Determine threshold dynamically to avoid packet size shortage. | |||||
// Determine threshould dynamically to avoid packet size shortage. | |||||
longDataSize := mc.maxAllowedPacket / (stmt.paramCount + 1) | longDataSize := mc.maxAllowedPacket / (stmt.paramCount + 1) | ||||
if longDataSize < 64 { | if longDataSize < 64 { | ||||
longDataSize = 64 | longDataSize = 64 | ||||
mc.sequence = 0 | mc.sequence = 0 | ||||
var data []byte | var data []byte | ||||
var err error | |||||
if len(args) == 0 { | if len(args) == 0 { | ||||
data, err = mc.buf.takeBuffer(minPktLen) | |||||
data = mc.buf.takeBuffer(minPktLen) | |||||
} else { | } else { | ||||
data, err = mc.buf.takeCompleteBuffer() | |||||
// In this case the len(data) == cap(data) which is used to optimise the flow below. | |||||
data = mc.buf.takeCompleteBuffer() | |||||
} | } | ||||
if err != nil { | |||||
if data == nil { | |||||
// cannot take the buffer. Something must be wrong with the connection | // cannot take the buffer. Something must be wrong with the connection | ||||
errLog.Print(err) | |||||
errLog.Print(ErrBusyBuffer) | |||||
return errBadConnNoWrite | return errBadConnNoWrite | ||||
} | } | ||||
pos := minPktLen | pos := minPktLen | ||||
var nullMask []byte | var nullMask []byte | ||||
if maskLen, typesLen := (len(args)+7)/8, 1+2*len(args); pos+maskLen+typesLen >= cap(data) { | |||||
if maskLen, typesLen := (len(args)+7)/8, 1+2*len(args); pos+maskLen+typesLen >= len(data) { | |||||
// buffer has to be extended but we don't know by how much so | // buffer has to be extended but we don't know by how much so | ||||
// we depend on append after all data with known sizes fit. | // we depend on append after all data with known sizes fit. | ||||
// We stop at that because we deal with a lot of columns here | // We stop at that because we deal with a lot of columns here | ||||
copy(tmp[:pos], data[:pos]) | copy(tmp[:pos], data[:pos]) | ||||
data = tmp | data = tmp | ||||
nullMask = data[pos : pos+maskLen] | nullMask = data[pos : pos+maskLen] | ||||
// No need to clean nullMask as make ensures that. | |||||
pos += maskLen | pos += maskLen | ||||
} else { | } else { | ||||
nullMask = data[pos : pos+maskLen] | nullMask = data[pos : pos+maskLen] | ||||
for i := range nullMask { | |||||
for i := 0; i < maskLen; i++ { | |||||
nullMask[i] = 0 | nullMask[i] = 0 | ||||
} | } | ||||
pos += maskLen | pos += maskLen | ||||
// In that case we must build the data packet with the new values buffer | // In that case we must build the data packet with the new values buffer | ||||
if valuesCap != cap(paramValues) { | if valuesCap != cap(paramValues) { | ||||
data = append(data[:pos], paramValues...) | data = append(data[:pos], paramValues...) | ||||
if err = mc.buf.store(data); err != nil { | |||||
errLog.Print(err) | |||||
return errBadConnNoWrite | |||||
} | |||||
mc.buf.buf = data | |||||
} | } | ||||
pos += len(paramValues) | pos += len(paramValues) |
import ( | import ( | ||||
"crypto/tls" | "crypto/tls" | ||||
"database/sql" | |||||
"database/sql/driver" | "database/sql/driver" | ||||
"encoding/binary" | "encoding/binary" | ||||
"errors" | |||||
"fmt" | "fmt" | ||||
"io" | "io" | ||||
"strconv" | "strconv" | ||||
func getTLSConfigClone(key string) (config *tls.Config) { | func getTLSConfigClone(key string) (config *tls.Config) { | ||||
tlsConfigLock.RLock() | tlsConfigLock.RLock() | ||||
if v, ok := tlsConfigRegistry[key]; ok { | if v, ok := tlsConfigRegistry[key]; ok { | ||||
config = v.Clone() | |||||
config = cloneTLSConfig(v) | |||||
} | } | ||||
tlsConfigLock.RUnlock() | tlsConfigLock.RUnlock() | ||||
return | return | ||||
} | } | ||||
return nil | return nil | ||||
} | } | ||||
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) { | |||||
dargs := make([]driver.Value, len(named)) | |||||
for n, param := range named { | |||||
if len(param.Name) > 0 { | |||||
// TODO: support the use of Named Parameters #561 | |||||
return nil, errors.New("mysql: driver does not support the use of Named Parameters") | |||||
} | |||||
dargs[n] = param.Value | |||||
} | |||||
return dargs, nil | |||||
} | |||||
func mapIsolationLevel(level driver.IsolationLevel) (string, error) { | |||||
switch sql.IsolationLevel(level) { | |||||
case sql.LevelRepeatableRead: | |||||
return "REPEATABLE READ", nil | |||||
case sql.LevelReadCommitted: | |||||
return "READ COMMITTED", nil | |||||
case sql.LevelReadUncommitted: | |||||
return "READ UNCOMMITTED", nil | |||||
case sql.LevelSerializable: | |||||
return "SERIALIZABLE", nil | |||||
default: | |||||
return "", fmt.Errorf("mysql: unsupported isolation level: %v", level) | |||||
} | |||||
} |
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package | |||||
// | |||||
// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved. | |||||
// | |||||
// This Source Code Form is subject to the terms of the Mozilla Public | |||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file, | |||||
// You can obtain one at http://mozilla.org/MPL/2.0/. | |||||
// +build go1.7 | |||||
// +build !go1.8 | |||||
package mysql | |||||
import "crypto/tls" | |||||
func cloneTLSConfig(c *tls.Config) *tls.Config { | |||||
return &tls.Config{ | |||||
Rand: c.Rand, | |||||
Time: c.Time, | |||||
Certificates: c.Certificates, | |||||
NameToCertificate: c.NameToCertificate, | |||||
GetCertificate: c.GetCertificate, | |||||
RootCAs: c.RootCAs, | |||||
NextProtos: c.NextProtos, | |||||
ServerName: c.ServerName, | |||||
ClientAuth: c.ClientAuth, | |||||
ClientCAs: c.ClientCAs, | |||||
InsecureSkipVerify: c.InsecureSkipVerify, | |||||
CipherSuites: c.CipherSuites, | |||||
PreferServerCipherSuites: c.PreferServerCipherSuites, | |||||
SessionTicketsDisabled: c.SessionTicketsDisabled, | |||||
SessionTicketKey: c.SessionTicketKey, | |||||
ClientSessionCache: c.ClientSessionCache, | |||||
MinVersion: c.MinVersion, | |||||
MaxVersion: c.MaxVersion, | |||||
CurvePreferences: c.CurvePreferences, | |||||
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled, | |||||
Renegotiation: c.Renegotiation, | |||||
} | |||||
} |
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package | |||||
// | |||||
// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved. | |||||
// | |||||
// This Source Code Form is subject to the terms of the Mozilla Public | |||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file, | |||||
// You can obtain one at http://mozilla.org/MPL/2.0/. | |||||
// +build go1.8 | |||||
package mysql | |||||
import ( | |||||
"crypto/tls" | |||||
"database/sql" | |||||
"database/sql/driver" | |||||
"errors" | |||||
"fmt" | |||||
) | |||||
func cloneTLSConfig(c *tls.Config) *tls.Config { | |||||
return c.Clone() | |||||
} | |||||
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) { | |||||
dargs := make([]driver.Value, len(named)) | |||||
for n, param := range named { | |||||
if len(param.Name) > 0 { | |||||
// TODO: support the use of Named Parameters #561 | |||||
return nil, errors.New("mysql: driver does not support the use of Named Parameters") | |||||
} | |||||
dargs[n] = param.Value | |||||
} | |||||
return dargs, nil | |||||
} | |||||
func mapIsolationLevel(level driver.IsolationLevel) (string, error) { | |||||
switch sql.IsolationLevel(level) { | |||||
case sql.LevelRepeatableRead: | |||||
return "REPEATABLE READ", nil | |||||
case sql.LevelReadCommitted: | |||||
return "READ COMMITTED", nil | |||||
case sql.LevelReadUncommitted: | |||||
return "READ UNCOMMITTED", nil | |||||
case sql.LevelSerializable: | |||||
return "SERIALIZABLE", nil | |||||
default: | |||||
return "", fmt.Errorf("mysql: unsupported isolation level: %v", level) | |||||
} | |||||
} |
module "github.com/go-xorm/builder" |
dependencies: | |||||
override: | |||||
# './...' is a relative pattern which means all subdirectories | |||||
- go get -t -d -v ./... | |||||
- go build -v | |||||
database: | |||||
override: | |||||
- mysql -u root -e "CREATE DATABASE core_test DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" | |||||
test: | |||||
override: | |||||
# './...' is a relative pattern which means all subdirectories | |||||
- go test -v -race | |||||
- go test -v -race --dbtype=sqlite3 |
package core | |||||
import ( | |||||
"database/sql" | |||||
"database/sql/driver" | |||||
"errors" | |||||
"fmt" | |||||
"reflect" | |||||
"regexp" | |||||
"sync" | |||||
) | |||||
var ( | |||||
DefaultCacheSize = 200 | |||||
) | |||||
func MapToSlice(query string, mp interface{}) (string, []interface{}, error) { | |||||
vv := reflect.ValueOf(mp) | |||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { | |||||
return "", []interface{}{}, ErrNoMapPointer | |||||
} | |||||
args := make([]interface{}, 0, len(vv.Elem().MapKeys())) | |||||
var err error | |||||
query = re.ReplaceAllStringFunc(query, func(src string) string { | |||||
v := vv.Elem().MapIndex(reflect.ValueOf(src[1:])) | |||||
if !v.IsValid() { | |||||
err = fmt.Errorf("map key %s is missing", src[1:]) | |||||
} else { | |||||
args = append(args, v.Interface()) | |||||
} | |||||
return "?" | |||||
}) | |||||
return query, args, err | |||||
} | |||||
func StructToSlice(query string, st interface{}) (string, []interface{}, error) { | |||||
vv := reflect.ValueOf(st) | |||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { | |||||
return "", []interface{}{}, ErrNoStructPointer | |||||
} | |||||
args := make([]interface{}, 0) | |||||
var err error | |||||
query = re.ReplaceAllStringFunc(query, func(src string) string { | |||||
fv := vv.Elem().FieldByName(src[1:]).Interface() | |||||
if v, ok := fv.(driver.Valuer); ok { | |||||
var value driver.Value | |||||
value, err = v.Value() | |||||
if err != nil { | |||||
return "?" | |||||
} | |||||
args = append(args, value) | |||||
} else { | |||||
args = append(args, fv) | |||||
} | |||||
return "?" | |||||
}) | |||||
if err != nil { | |||||
return "", []interface{}{}, err | |||||
} | |||||
return query, args, nil | |||||
} | |||||
type cacheStruct struct { | |||||
value reflect.Value | |||||
idx int | |||||
} | |||||
type DB struct { | |||||
*sql.DB | |||||
Mapper IMapper | |||||
reflectCache map[reflect.Type]*cacheStruct | |||||
reflectCacheMutex sync.RWMutex | |||||
} | |||||
func Open(driverName, dataSourceName string) (*DB, error) { | |||||
db, err := sql.Open(driverName, dataSourceName) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return &DB{ | |||||
DB: db, | |||||
Mapper: NewCacheMapper(&SnakeMapper{}), | |||||
reflectCache: make(map[reflect.Type]*cacheStruct), | |||||
}, nil | |||||
} | |||||
func FromDB(db *sql.DB) *DB { | |||||
return &DB{ | |||||
DB: db, | |||||
Mapper: NewCacheMapper(&SnakeMapper{}), | |||||
reflectCache: make(map[reflect.Type]*cacheStruct), | |||||
} | |||||
} | |||||
func (db *DB) reflectNew(typ reflect.Type) reflect.Value { | |||||
db.reflectCacheMutex.Lock() | |||||
defer db.reflectCacheMutex.Unlock() | |||||
cs, ok := db.reflectCache[typ] | |||||
if !ok || cs.idx+1 > DefaultCacheSize-1 { | |||||
cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), DefaultCacheSize, DefaultCacheSize), 0} | |||||
db.reflectCache[typ] = cs | |||||
} else { | |||||
cs.idx = cs.idx + 1 | |||||
} | |||||
return cs.value.Index(cs.idx).Addr() | |||||
} | |||||
func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { | |||||
rows, err := db.DB.Query(query, args...) | |||||
if err != nil { | |||||
if rows != nil { | |||||
rows.Close() | |||||
} | |||||
return nil, err | |||||
} | |||||
return &Rows{rows, db}, nil | |||||
} | |||||
func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) { | |||||
query, args, err := MapToSlice(query, mp) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return db.Query(query, args...) | |||||
} | |||||
func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) { | |||||
query, args, err := StructToSlice(query, st) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return db.Query(query, args...) | |||||
} | |||||
func (db *DB) QueryRow(query string, args ...interface{}) *Row { | |||||
rows, err := db.Query(query, args...) | |||||
if err != nil { | |||||
return &Row{nil, err} | |||||
} | |||||
return &Row{rows, nil} | |||||
} | |||||
func (db *DB) QueryRowMap(query string, mp interface{}) *Row { | |||||
query, args, err := MapToSlice(query, mp) | |||||
if err != nil { | |||||
return &Row{nil, err} | |||||
} | |||||
return db.QueryRow(query, args...) | |||||
} | |||||
func (db *DB) QueryRowStruct(query string, st interface{}) *Row { | |||||
query, args, err := StructToSlice(query, st) | |||||
if err != nil { | |||||
return &Row{nil, err} | |||||
} | |||||
return db.QueryRow(query, args...) | |||||
} | |||||
type Stmt struct { | |||||
*sql.Stmt | |||||
db *DB | |||||
names map[string]int | |||||
} | |||||
func (db *DB) Prepare(query string) (*Stmt, error) { | |||||
names := make(map[string]int) | |||||
var i int | |||||
query = re.ReplaceAllStringFunc(query, func(src string) string { | |||||
names[src[1:]] = i | |||||
i += 1 | |||||
return "?" | |||||
}) | |||||
stmt, err := db.DB.Prepare(query) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return &Stmt{stmt, db, names}, nil | |||||
} | |||||
func (s *Stmt) ExecMap(mp interface{}) (sql.Result, error) { | |||||
vv := reflect.ValueOf(mp) | |||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { | |||||
return nil, errors.New("mp should be a map's pointer") | |||||
} | |||||
args := make([]interface{}, len(s.names)) | |||||
for k, i := range s.names { | |||||
args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() | |||||
} | |||||
return s.Stmt.Exec(args...) | |||||
} | |||||
func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) { | |||||
vv := reflect.ValueOf(st) | |||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { | |||||
return nil, errors.New("mp should be a map's pointer") | |||||
} | |||||
args := make([]interface{}, len(s.names)) | |||||
for k, i := range s.names { | |||||
args[i] = vv.Elem().FieldByName(k).Interface() | |||||
} | |||||
return s.Stmt.Exec(args...) | |||||
} | |||||
func (s *Stmt) Query(args ...interface{}) (*Rows, error) { | |||||
rows, err := s.Stmt.Query(args...) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return &Rows{rows, s.db}, nil | |||||
} | |||||
func (s *Stmt) QueryMap(mp interface{}) (*Rows, error) { | |||||
vv := reflect.ValueOf(mp) | |||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { | |||||
return nil, errors.New("mp should be a map's pointer") | |||||
} | |||||
args := make([]interface{}, len(s.names)) | |||||
for k, i := range s.names { | |||||
args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() | |||||
} | |||||
return s.Query(args...) | |||||
} | |||||
func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) { | |||||
vv := reflect.ValueOf(st) | |||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { | |||||
return nil, errors.New("mp should be a map's pointer") | |||||
} | |||||
args := make([]interface{}, len(s.names)) | |||||
for k, i := range s.names { | |||||
args[i] = vv.Elem().FieldByName(k).Interface() | |||||
} | |||||
return s.Query(args...) | |||||
} | |||||
func (s *Stmt) QueryRow(args ...interface{}) *Row { | |||||
rows, err := s.Query(args...) | |||||
return &Row{rows, err} | |||||
} | |||||
func (s *Stmt) QueryRowMap(mp interface{}) *Row { | |||||
vv := reflect.ValueOf(mp) | |||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { | |||||
return &Row{nil, errors.New("mp should be a map's pointer")} | |||||
} | |||||
args := make([]interface{}, len(s.names)) | |||||
for k, i := range s.names { | |||||
args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() | |||||
} | |||||
return s.QueryRow(args...) | |||||
} | |||||
func (s *Stmt) QueryRowStruct(st interface{}) *Row { | |||||
vv := reflect.ValueOf(st) | |||||
if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { | |||||
return &Row{nil, errors.New("st should be a struct's pointer")} | |||||
} | |||||
args := make([]interface{}, len(s.names)) | |||||
for k, i := range s.names { | |||||
args[i] = vv.Elem().FieldByName(k).Interface() | |||||
} | |||||
return s.QueryRow(args...) | |||||
} | |||||
var ( | |||||
re = regexp.MustCompile(`[?](\w+)`) | |||||
) | |||||
// insert into (name) values (?) | |||||
// insert into (name) values (?name) | |||||
func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) { | |||||
query, args, err := MapToSlice(query, mp) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return db.DB.Exec(query, args...) | |||||
} | |||||
func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) { | |||||
query, args, err := StructToSlice(query, st) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return db.DB.Exec(query, args...) | |||||
} | |||||
type EmptyScanner struct { | |||||
} | |||||
func (EmptyScanner) Scan(src interface{}) error { | |||||
return nil | |||||
} | |||||
type Tx struct { | |||||
*sql.Tx | |||||
db *DB | |||||
} | |||||
func (db *DB) Begin() (*Tx, error) { | |||||
tx, err := db.DB.Begin() | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return &Tx{tx, db}, nil | |||||
} | |||||
func (tx *Tx) Prepare(query string) (*Stmt, error) { | |||||
names := make(map[string]int) | |||||
var i int | |||||
query = re.ReplaceAllStringFunc(query, func(src string) string { | |||||
names[src[1:]] = i | |||||
i += 1 | |||||
return "?" | |||||
}) | |||||
stmt, err := tx.Tx.Prepare(query) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return &Stmt{stmt, tx.db, names}, nil | |||||
} | |||||
func (tx *Tx) Stmt(stmt *Stmt) *Stmt { | |||||
// TODO: | |||||
return stmt | |||||
} | |||||
func (tx *Tx) ExecMap(query string, mp interface{}) (sql.Result, error) { | |||||
query, args, err := MapToSlice(query, mp) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return tx.Tx.Exec(query, args...) | |||||
} | |||||
func (tx *Tx) ExecStruct(query string, st interface{}) (sql.Result, error) { | |||||
query, args, err := StructToSlice(query, st) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return tx.Tx.Exec(query, args...) | |||||
} | |||||
func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) { | |||||
rows, err := tx.Tx.Query(query, args...) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return &Rows{rows, tx.db}, nil | |||||
} | |||||
func (tx *Tx) QueryMap(query string, mp interface{}) (*Rows, error) { | |||||
query, args, err := MapToSlice(query, mp) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return tx.Query(query, args...) | |||||
} | |||||
func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) { | |||||
query, args, err := StructToSlice(query, st) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return tx.Query(query, args...) | |||||
} | |||||
func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { | |||||
rows, err := tx.Query(query, args...) | |||||
return &Row{rows, err} | |||||
} | |||||
func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row { | |||||
query, args, err := MapToSlice(query, mp) | |||||
if err != nil { | |||||
return &Row{nil, err} | |||||
} | |||||
return tx.QueryRow(query, args...) | |||||
} | |||||
func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row { | |||||
query, args, err := StructToSlice(query, st) | |||||
if err != nil { | |||||
return &Row{nil, err} | |||||
} | |||||
return tx.QueryRow(query, args...) | |||||
} |
module "github.com/go-xorm/core" |
image: golang:${GO_VERSION} | image: golang:${GO_VERSION} | ||||
commands: | commands: | ||||
- go get -t -d -v ./... | - go get -t -d -v ./... | ||||
- go get -u github.com/go-xorm/core | |||||
- go get -u github.com/go-xorm/builder | |||||
- go get -u xorm.io/core | |||||
- go get -u xorm.io/builder | |||||
- go build -v | - go build -v | ||||
when: | when: | ||||
event: [ push, pull_request ] | event: [ push, pull_request ] |
* Optimistic Locking support | * Optimistic Locking support | ||||
* SQL Builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder) | |||||
* SQL Builder support via [xorm.io/builder](https://xorm.io/builder) | |||||
* Automatical Read/Write seperatelly | * Automatical Read/Write seperatelly | ||||
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 | // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 | ||||
var name string | var name string | ||||
has, err := engine.Where("id = ?", id).Cols("name").Get(&name) | |||||
has, err := engine.Table(&user).Where("id = ?", id).Cols("name").Get(&name) | |||||
// SELECT name FROM user WHERE id = ? | // SELECT name FROM user WHERE id = ? | ||||
var id int64 | var id int64 | ||||
has, err := engine.Where("name = ?", name).Cols("id").Get(&id) | |||||
has, err := engine.Table(&user).Where("name = ?", name).Cols("id").Get(&id) | |||||
has, err := engine.SQL("select id from user").Get(&id) | has, err := engine.SQL("select id from user").Get(&id) | ||||
// SELECT id FROM user WHERE name = ? | // SELECT id FROM user WHERE name = ? | ||||
var valuesMap = make(map[string]string) | var valuesMap = make(map[string]string) | ||||
has, err := engine.Where("id = ?", id).Get(&valuesMap) | |||||
has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap) | |||||
// SELECT * FROM user WHERE id = ? | // SELECT * FROM user WHERE id = ? | ||||
var valuesSlice = make([]interface{}, len(cols)) | var valuesSlice = make([]interface{}, len(cols)) | ||||
has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice) | |||||
has, err := engine.Table(&user).Where("id = ?", id).Cols(cols...).Get(&valuesSlice) | |||||
// SELECT col1, col2, col3 FROM user WHERE id = ? | // SELECT col1, col2, col3 FROM user WHERE id = ? | ||||
``` | ``` | ||||
* Or you can use `Transaction` to replace above codes. | * Or you can use `Transaction` to replace above codes. | ||||
```Go | ```Go | ||||
res, err := engine.Transaction(func(sess *xorm.Session) (interface{}, error) { | |||||
res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) { | |||||
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} | user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} | ||||
if _, err := session.Insert(&user1); err != nil { | if _, err := session.Insert(&user1); err != nil { | ||||
return nil, err | return nil, err | ||||
## LICENSE | ## LICENSE | ||||
BSD License [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) | |||||
BSD License [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) |
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 | // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 | ||||
var name string | var name string | ||||
has, err := engine.Where("id = ?", id).Cols("name").Get(&name) | |||||
has, err := engine.Table(&user).Where("id = ?", id).Cols("name").Get(&name) | |||||
// SELECT name FROM user WHERE id = ? | // SELECT name FROM user WHERE id = ? | ||||
var id int64 | var id int64 | ||||
has, err := engine.Where("name = ?", name).Cols("id").Get(&id) | |||||
has, err := engine.Table(&user).Where("name = ?", name).Cols("id").Get(&id) | |||||
has, err := engine.SQL("select id from user").Get(&id) | has, err := engine.SQL("select id from user").Get(&id) | ||||
// SELECT id FROM user WHERE name = ? | // SELECT id FROM user WHERE name = ? | ||||
var valuesMap = make(map[string]string) | var valuesMap = make(map[string]string) | ||||
has, err := engine.Where("id = ?", id).Get(&valuesMap) | |||||
has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap) | |||||
// SELECT * FROM user WHERE id = ? | // SELECT * FROM user WHERE id = ? | ||||
var valuesSlice = make([]interface{}, len(cols)) | var valuesSlice = make([]interface{}, len(cols)) | ||||
has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice) | |||||
has, err := engine.Table(&user).Where("id = ?", id).Cols(cols...).Get(&valuesSlice) | |||||
// SELECT col1, col2, col3 FROM user WHERE id = ? | // SELECT col1, col2, col3 FROM user WHERE id = ? | ||||
``` | ``` | ||||
return session.Commit() | return session.Commit() | ||||
``` | ``` | ||||
* 事物的简写方法 | |||||
* 事务的简写方法 | |||||
```Go | ```Go | ||||
res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) { | res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) { |
"sync" | "sync" | ||||
"time" | "time" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// LRUCacher implments cache object facilities | // LRUCacher implments cache object facilities |
import ( | import ( | ||||
"sync" | "sync" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
var _ core.CacheStore = NewMemoryStore() | var _ core.CacheStore = NewMemoryStore() |
dependencies: | |||||
override: | |||||
# './...' is a relative pattern which means all subdirectories | |||||
- go get -t -d -v ./... | |||||
- go get -t -d -v github.com/go-xorm/tests | |||||
- go get -u github.com/go-xorm/core | |||||
- go get -u github.com/go-xorm/builder | |||||
- go build -v | |||||
database: | |||||
override: | |||||
- mysql -u root -e "CREATE DATABASE xorm_test DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" | |||||
- mysql -u root -e "CREATE DATABASE xorm_test1 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" | |||||
- mysql -u root -e "CREATE DATABASE xorm_test2 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" | |||||
- mysql -u root -e "CREATE DATABASE xorm_test3 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" | |||||
- createdb -p 5432 -e -U postgres xorm_test | |||||
- createdb -p 5432 -e -U postgres xorm_test1 | |||||
- createdb -p 5432 -e -U postgres xorm_test2 | |||||
- createdb -p 5432 -e -U postgres xorm_test3 | |||||
- psql xorm_test postgres -c "create schema xorm" | |||||
test: | |||||
override: | |||||
# './...' is a relative pattern which means all subdirectories | |||||
- go get -u github.com/wadey/gocovmerge | |||||
- go test -v -race -db="sqlite3" -conn_str="./test.db" -coverprofile=coverage1-1.txt -covermode=atomic | |||||
- go test -v -race -db="sqlite3" -conn_str="./test.db" -cache=true -coverprofile=coverage1-2.txt -covermode=atomic | |||||
- go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -coverprofile=coverage2-1.txt -covermode=atomic | |||||
- go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic | |||||
- go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -coverprofile=coverage3-1.txt -covermode=atomic | |||||
- go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic | |||||
- go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -coverprofile=coverage4-1.txt -covermode=atomic | |||||
- go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic | |||||
- go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic | |||||
- go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic | |||||
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt > coverage.txt | |||||
- cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./sqlite3.sh | |||||
- cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./mysql.sh | |||||
- cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./postgres.sh | |||||
post: | |||||
- bash <(curl -s https://codecov.io/bash) |
import ( | import ( | ||||
"errors" | "errors" | ||||
"fmt" | "fmt" | ||||
"net/url" | |||||
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
var ( | var ( | ||||
} | } | ||||
func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { | func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { | ||||
kv := strings.Split(dataSourceName, ";") | |||||
var dbName string | var dbName string | ||||
for _, c := range kv { | |||||
vv := strings.Split(strings.TrimSpace(c), "=") | |||||
if len(vv) == 2 { | |||||
switch strings.ToLower(vv[0]) { | |||||
case "database": | |||||
dbName = vv[1] | |||||
if strings.HasPrefix(dataSourceName, "sqlserver://") { | |||||
u, err := url.Parse(dataSourceName) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
dbName = u.Query().Get("database") | |||||
} else { | |||||
kv := strings.Split(dataSourceName, ";") | |||||
for _, c := range kv { | |||||
vv := strings.Split(strings.TrimSpace(c), "=") | |||||
if len(vv) == 2 { | |||||
switch strings.ToLower(vv[0]) { | |||||
case "database": | |||||
dbName = vv[1] | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
"strings" | "strings" | ||||
"time" | "time" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
var ( | var ( | ||||
if colType == "FLOAT UNSIGNED" { | if colType == "FLOAT UNSIGNED" { | ||||
colType = "FLOAT" | colType = "FLOAT" | ||||
} | } | ||||
if colType == "DOUBLE UNSIGNED" { | |||||
colType = "DOUBLE" | |||||
} | |||||
col.Length = len1 | col.Length = len1 | ||||
col.Length2 = len2 | col.Length2 = len2 | ||||
if _, ok := core.SqlTypes[colType]; ok { | if _, ok := core.SqlTypes[colType]; ok { |
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
var ( | var ( |
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// from http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html | // from http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html | ||||
return tables, nil | return tables, nil | ||||
} | } | ||||
func getIndexColName(indexdef string) []string { | |||||
var colNames []string | |||||
cs := strings.Split(indexdef, "(") | |||||
for _, v := range strings.Split(strings.Split(cs[1], ")")[0], ",") { | |||||
colNames = append(colNames, strings.Split(strings.TrimLeft(v, " "), " ")[0]) | |||||
} | |||||
return colNames | |||||
} | |||||
func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) { | func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) { | ||||
args := []interface{}{tableName} | args := []interface{}{tableName} | ||||
s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1") | s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1") | ||||
} else { | } else { | ||||
indexType = core.IndexType | indexType = core.IndexType | ||||
} | } | ||||
cs := strings.Split(indexdef, "(") | |||||
colNames = strings.Split(cs[1][0:len(cs[1])-1], ",") | |||||
colNames = getIndexColName(indexdef) | |||||
var isRegular bool | var isRegular bool | ||||
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { | if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { | ||||
newIdxName := indexName[5+len(tableName):] | newIdxName := indexName[5+len(tableName):] |
"regexp" | "regexp" | ||||
"strings" | "strings" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
var ( | var ( |
import ( | import ( | ||||
"bufio" | "bufio" | ||||
"bytes" | "bytes" | ||||
"context" | |||||
"database/sql" | "database/sql" | ||||
"encoding/gob" | "encoding/gob" | ||||
"errors" | "errors" | ||||
"sync" | "sync" | ||||
"time" | "time" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/core" | |||||
"xorm.io/builder" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// Engine is the major struct of xorm, it means a database manager. | // Engine is the major struct of xorm, it means a database manager. | ||||
cachers map[string]core.Cacher | cachers map[string]core.Cacher | ||||
cacherLock sync.RWMutex | cacherLock sync.RWMutex | ||||
defaultContext context.Context | |||||
} | } | ||||
func (engine *Engine) setCacher(tableName string, cacher core.Cacher) { | func (engine *Engine) setCacher(tableName string, cacher core.Cacher) { | ||||
// SetLogger set the new logger | // SetLogger set the new logger | ||||
func (engine *Engine) SetLogger(logger core.ILogger) { | func (engine *Engine) SetLogger(logger core.ILogger) { | ||||
engine.logger = logger | engine.logger = logger | ||||
engine.showSQL = logger.IsShowSQL() | |||||
engine.dialect.SetLogger(logger) | engine.dialect.SetLogger(logger) | ||||
} | } | ||||
} | } | ||||
// Exec raw sql | // Exec raw sql | ||||
func (engine *Engine) Exec(sqlorArgs ...interface{}) (sql.Result, error) { | |||||
func (engine *Engine) Exec(sqlOrArgs ...interface{}) (sql.Result, error) { | |||||
session := engine.NewSession() | session := engine.NewSession() | ||||
defer session.Close() | defer session.Close() | ||||
return session.Exec(sqlorArgs...) | |||||
return session.Exec(sqlOrArgs...) | |||||
} | } | ||||
// Query a raw sql and return records as []map[string][]byte | // Query a raw sql and return records as []map[string][]byte | ||||
func (engine *Engine) Query(sqlorArgs ...interface{}) (resultsSlice []map[string][]byte, err error) { | |||||
func (engine *Engine) Query(sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error) { | |||||
session := engine.NewSession() | session := engine.NewSession() | ||||
defer session.Close() | defer session.Close() | ||||
return session.Query(sqlorArgs...) | |||||
return session.Query(sqlOrArgs...) | |||||
} | } | ||||
// QueryString runs a raw sql and return records as []map[string]string | // QueryString runs a raw sql and return records as []map[string]string | ||||
func (engine *Engine) QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) { | |||||
func (engine *Engine) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) { | |||||
session := engine.NewSession() | session := engine.NewSession() | ||||
defer session.Close() | defer session.Close() | ||||
return session.QueryString(sqlorArgs...) | |||||
return session.QueryString(sqlOrArgs...) | |||||
} | } | ||||
// QueryInterface runs a raw sql and return records as []map[string]interface{} | // QueryInterface runs a raw sql and return records as []map[string]interface{} | ||||
func (engine *Engine) QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) { | |||||
func (engine *Engine) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) { | |||||
session := engine.NewSession() | session := engine.NewSession() | ||||
defer session.Close() | defer session.Close() | ||||
return session.QueryInterface(sqlorArgs...) | |||||
return session.QueryInterface(sqlOrArgs...) | |||||
} | } | ||||
// Insert one or more records | // Insert one or more records |
import ( | import ( | ||||
"database/sql/driver" | "database/sql/driver" | ||||
"encoding/json" | |||||
"fmt" | "fmt" | ||||
"reflect" | "reflect" | ||||
"strings" | "strings" | ||||
"time" | "time" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/core" | |||||
"xorm.io/builder" | |||||
"xorm.io/core" | |||||
) | ) | ||||
func (engine *Engine) buildConds(table *core.Table, bean interface{}, | func (engine *Engine) buildConds(table *core.Table, bean interface{}, | ||||
} else { | } else { | ||||
if col.SQLType.IsJson() { | if col.SQLType.IsJson() { | ||||
if col.SQLType.IsText() { | if col.SQLType.IsText() { | ||||
bytes, err := json.Marshal(fieldValue.Interface()) | |||||
bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
engine.logger.Error(err) | engine.logger.Error(err) | ||||
continue | continue | ||||
} else if col.SQLType.IsBlob() { | } else if col.SQLType.IsBlob() { | ||||
var bytes []byte | var bytes []byte | ||||
var err error | var err error | ||||
bytes, err = json.Marshal(fieldValue.Interface()) | |||||
bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
engine.logger.Error(err) | engine.logger.Error(err) | ||||
continue | continue | ||||
} | } | ||||
if col.SQLType.IsText() { | if col.SQLType.IsText() { | ||||
bytes, err := json.Marshal(fieldValue.Interface()) | |||||
bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
engine.logger.Error(err) | engine.logger.Error(err) | ||||
continue | continue | ||||
continue | continue | ||||
} | } | ||||
} else { | } else { | ||||
bytes, err = json.Marshal(fieldValue.Interface()) | |||||
bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
engine.logger.Error(err) | engine.logger.Error(err) | ||||
continue | continue |
// Copyright 2019 The Xorm Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
// +build go1.8 | |||||
package xorm | |||||
import "context" | |||||
// Context creates a session with the context | |||||
func (engine *Engine) Context(ctx context.Context) *Session { | |||||
session := engine.NewSession() | |||||
session.isAutoClose = true | |||||
return session.Context(ctx) | |||||
} | |||||
// SetDefaultContext set the default context | |||||
func (engine *Engine) SetDefaultContext(ctx context.Context) { | |||||
engine.defaultContext = ctx | |||||
} | |||||
// PingContext tests if database is alive | |||||
func (engine *Engine) PingContext(ctx context.Context) error { | |||||
session := engine.NewSession() | |||||
defer session.Close() | |||||
return session.PingContext(ctx) | |||||
} |
package xorm | package xorm | ||||
import ( | import ( | ||||
"context" | |||||
"time" | "time" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// EngineGroup defines an engine group | // EngineGroup defines an engine group | ||||
return nil | return nil | ||||
} | } | ||||
// Context returned a group session | |||||
func (eg *EngineGroup) Context(ctx context.Context) *Session { | |||||
sess := eg.NewSession() | |||||
sess.isAutoClose = true | |||||
return sess.Context(ctx) | |||||
} | |||||
// NewSession returned a group session | |||||
func (eg *EngineGroup) NewSession() *Session { | |||||
sess := eg.Engine.NewSession() | |||||
sess.sessionType = groupSession | |||||
return sess | |||||
} | |||||
// Master returns the master engine | // Master returns the master engine | ||||
func (eg *EngineGroup) Master() *Engine { | func (eg *EngineGroup) Master() *Engine { | ||||
return eg.Engine | return eg.Engine |
"reflect" | "reflect" | ||||
"strings" | "strings" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// TableNameWithSchema will automatically add schema prefix on table name | |||||
// tbNameWithSchema will automatically add schema prefix on table name | |||||
func (engine *Engine) tbNameWithSchema(v string) string { | func (engine *Engine) tbNameWithSchema(v string) string { | ||||
// Add schema name as prefix of table name. | // Add schema name as prefix of table name. | ||||
// Only for postgres database. | // Only for postgres database. |
ErrNotImplemented = errors.New("Not implemented") | ErrNotImplemented = errors.New("Not implemented") | ||||
// ErrConditionType condition type unsupported | // ErrConditionType condition type unsupported | ||||
ErrConditionType = errors.New("Unsupported condition type") | ErrConditionType = errors.New("Unsupported condition type") | ||||
// ErrUnSupportedSQLType parameter of SQL is not supported | |||||
ErrUnSupportedSQLType = errors.New("unsupported sql type") | |||||
) | ) | ||||
// ErrFieldIsNotExist columns does not exist | // ErrFieldIsNotExist columns does not exist |
module github.com/go-xorm/xorm | module github.com/go-xorm/xorm | ||||
require ( | require ( | ||||
cloud.google.com/go v0.34.0 // indirect | |||||
github.com/cockroachdb/apd v1.1.0 // indirect | github.com/cockroachdb/apd v1.1.0 // indirect | ||||
github.com/davecgh/go-spew v1.1.1 // indirect | github.com/davecgh/go-spew v1.1.1 // indirect | ||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f | |||||
github.com/go-sql-driver/mysql v1.4.0 | |||||
github.com/go-xorm/builder v0.3.2 | |||||
github.com/go-xorm/core v0.6.0 | |||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a // indirect | |||||
github.com/denisenkom/go-mssqldb v0.0.0-20190121005146-b04fd42d9952 | |||||
github.com/go-sql-driver/mysql v1.4.1 | |||||
github.com/google/go-cmp v0.2.0 // indirect | |||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect | github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect | ||||
github.com/jackc/pgx v3.2.0+incompatible | |||||
github.com/jackc/pgx v3.3.0+incompatible | |||||
github.com/kr/pretty v0.1.0 // indirect | github.com/kr/pretty v0.1.0 // indirect | ||||
github.com/lib/pq v1.0.0 | github.com/lib/pq v1.0.0 | ||||
github.com/mattn/go-sqlite3 v1.9.0 | |||||
github.com/pkg/errors v0.8.0 // indirect | |||||
github.com/pmezard/go-difflib v1.0.0 // indirect | |||||
github.com/mattn/go-sqlite3 v1.10.0 | |||||
github.com/pkg/errors v0.8.1 // indirect | |||||
github.com/satori/go.uuid v1.2.0 // indirect | github.com/satori/go.uuid v1.2.0 // indirect | ||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect | ||||
github.com/stretchr/testify v1.2.2 | |||||
github.com/stretchr/testify v1.3.0 | |||||
github.com/ziutek/mymysql v1.5.4 | github.com/ziutek/mymysql v1.5.4 | ||||
golang.org/x/crypto v0.0.0-20190122013713-64072686203f // indirect | |||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect | ||||
gopkg.in/stretchr/testify.v1 v1.2.2 | |||||
xorm.io/builder v0.3.5 | |||||
xorm.io/core v0.6.3 | |||||
) | ) |
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= | |||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | |||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= | ||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= | ||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | |||||
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f h1:WH0w/R4Yoey+04HhFxqZ6VX6I0d7RMyw5aXQ9UTvQPs= | |||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= | |||||
github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= | |||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= | |||||
github.com/go-xorm/builder v0.3.2 h1:pSsZQRRzJNapKEAEhigw3xLmiLPeAYv5GFlpYZ8+a5I= | |||||
github.com/go-xorm/builder v0.3.2/go.mod h1:v8mE3MFBgtL+RGFNfUnAMUqqfk/Y4W5KuwCFQIEpQLk= | |||||
github.com/go-xorm/core v0.6.0 h1:tp6hX+ku4OD9khFZS8VGBDRY3kfVCtelPfmkgCyHxL0= | |||||
github.com/go-xorm/core v0.6.0/go.mod h1:d8FJ9Br8OGyQl12MCclmYBuBqqxsyeedpXciV5Myih8= | |||||
github.com/denisenkom/go-mssqldb v0.0.0-20190121005146-b04fd42d9952 h1:b5OnbZD49x9g+/FcYbs/vukEt8C/jUbGhCJ3uduQmu8= | |||||
github.com/denisenkom/go-mssqldb v0.0.0-20190121005146-b04fd42d9952/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= | |||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= | |||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= | |||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= | github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= | ||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= | github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= | ||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= | |||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | |||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= | github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= | ||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= | github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= | ||||
github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY= | |||||
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= | |||||
github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90= | |||||
github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= | |||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= | 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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= | github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= | ||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= | ||||
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= | |||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | |||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= | |||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | |||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= | |||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | |||||
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= | ||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= | ||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= | ||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= | ||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= | |||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | |||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= | |||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | |||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= | github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= | ||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= | github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= | ||||
golang.org/x/crypto v0.0.0-20190122013713-64072686203f h1:u1CmMhe3a44hy8VIgpInORnI01UVaUYheqR7x9BxT3c= | |||||
golang.org/x/crypto v0.0.0-20190122013713-64072686203f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | |||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= | |||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | |||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= | 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M= | |||||
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU= | |||||
xorm.io/builder v0.3.5 h1:EilU39fvWDxjb1cDaELpYhsF+zziRBhew8xk4pngO+A= | |||||
xorm.io/builder v0.3.5/go.mod h1:ZFbByS/KxZI1FKRjL05PyJ4YrK2bcxlUaAxdum5aTR8= | |||||
xorm.io/core v0.6.2 h1:EJLcSxf336POJr670wKB55Mah9f93xzvGYzNRgnT8/Y= | |||||
xorm.io/core v0.6.2/go.mod h1:bwPIfLdm/FzWgVUH8WPVlr+uJhscvNGFcaZKXsI3n2c= | |||||
xorm.io/core v0.6.3 h1:n1NhVZt1s2oLw1BZfX2ocIJsHyso259uPgg63BGr37M= | |||||
xorm.io/core v0.6.3/go.mod h1:8kz/C6arVW/O9vk3PgCiMJO2hIAm1UcuOL3dSPyZ2qo= |
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// str2PK convert string value to primary key value according to tp | // str2PK convert string value to primary key value according to tp |
package xorm | package xorm | ||||
import ( | import ( | ||||
"context" | |||||
"database/sql" | "database/sql" | ||||
"reflect" | "reflect" | ||||
"time" | "time" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// Interface defines the interface which Engine, EngineGroup and Session will implementate. | // Interface defines the interface which Engine, EngineGroup and Session will implementate. | ||||
Delete(interface{}) (int64, error) | Delete(interface{}) (int64, error) | ||||
Distinct(columns ...string) *Session | Distinct(columns ...string) *Session | ||||
DropIndexes(bean interface{}) error | DropIndexes(bean interface{}) error | ||||
Exec(sqlOrAgrs ...interface{}) (sql.Result, error) | |||||
Exec(sqlOrArgs ...interface{}) (sql.Result, error) | |||||
Exist(bean ...interface{}) (bool, error) | Exist(bean ...interface{}) (bool, error) | ||||
Find(interface{}, ...interface{}) error | Find(interface{}, ...interface{}) error | ||||
FindAndCount(interface{}, ...interface{}) (int64, error) | FindAndCount(interface{}, ...interface{}) (int64, error) | ||||
Omit(columns ...string) *Session | Omit(columns ...string) *Session | ||||
OrderBy(order string) *Session | OrderBy(order string) *Session | ||||
Ping() error | Ping() error | ||||
Query(sqlOrAgrs ...interface{}) (resultsSlice []map[string][]byte, err error) | |||||
QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) | |||||
QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) | |||||
Query(sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error) | |||||
QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) | |||||
QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) | |||||
Rows(bean interface{}) (*Rows, error) | Rows(bean interface{}) (*Rows, error) | ||||
SetExpr(string, string) *Session | SetExpr(string, string) *Session | ||||
SQL(interface{}, ...interface{}) *Session | SQL(interface{}, ...interface{}) *Session | ||||
Before(func(interface{})) *Session | Before(func(interface{})) *Session | ||||
Charset(charset string) *Session | Charset(charset string) *Session | ||||
ClearCache(...interface{}) error | ClearCache(...interface{}) error | ||||
Context(context.Context) *Session | |||||
CreateTables(...interface{}) error | CreateTables(...interface{}) error | ||||
DBMetas() ([]*core.Table, error) | DBMetas() ([]*core.Table, error) | ||||
Dialect() core.Dialect | Dialect() core.Dialect |
// Copyright 2019 The Xorm Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
package xorm | |||||
import "encoding/json" | |||||
// JSONInterface represents an interface to handle json data | |||||
type JSONInterface interface { | |||||
Marshal(v interface{}) ([]byte, error) | |||||
Unmarshal(data []byte, v interface{}) error | |||||
} | |||||
var ( | |||||
// DefaultJSONHandler default json handler | |||||
DefaultJSONHandler JSONInterface = StdJSON{} | |||||
) | |||||
// StdJSON implements JSONInterface via encoding/json | |||||
type StdJSON struct{} | |||||
// Marshal implements JSONInterface | |||||
func (StdJSON) Marshal(v interface{}) ([]byte, error) { | |||||
return json.Marshal(v) | |||||
} | |||||
// Unmarshal implements JSONInterface | |||||
func (StdJSON) Unmarshal(data []byte, v interface{}) error { | |||||
return json.Unmarshal(data, v) | |||||
} |
"io" | "io" | ||||
"log" | "log" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// default log options | // default log options |
"fmt" | "fmt" | ||||
"reflect" | "reflect" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// Rows rows wrapper a rows to | // Rows rows wrapper a rows to | ||||
type Rows struct { | type Rows struct { | ||||
NoTypeCheck bool | |||||
session *Session | session *Session | ||||
rows *core.Rows | rows *core.Rows | ||||
fields []string | |||||
beanType reflect.Type | beanType reflect.Type | ||||
lastError error | lastError error | ||||
} | } | ||||
return nil, err | return nil, err | ||||
} | } | ||||
rows.fields, err = rows.rows.Columns() | |||||
if err != nil { | |||||
rows.lastError = err | |||||
rows.Close() | |||||
return nil, err | |||||
} | |||||
return rows, nil | return rows, nil | ||||
} | } | ||||
return rows.lastError | return rows.lastError | ||||
} | } | ||||
if !rows.NoTypeCheck && reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType { | |||||
if reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType { | |||||
return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) | return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) | ||||
} | } | ||||
return err | return err | ||||
} | } | ||||
scanResults, err := rows.session.row2Slice(rows.rows, rows.fields, bean) | |||||
fields, err := rows.rows.Columns() | |||||
if err != nil { | |||||
return err | |||||
} | |||||
scanResults, err := rows.session.row2Slice(rows.rows, fields, bean) | |||||
if err != nil { | if err != nil { | ||||
return err | return err | ||||
} | } | ||||
dataStruct := rValue(bean) | dataStruct := rValue(bean) | ||||
_, err = rows.session.slice2Bean(scanResults, rows.fields, bean, &dataStruct, rows.session.statement.RefTable) | |||||
_, err = rows.session.slice2Bean(scanResults, fields, bean, &dataStruct, rows.session.statement.RefTable) | |||||
if err != nil { | if err != nil { | ||||
return err | return err | ||||
} | } | ||||
defer rows.session.Close() | defer rows.session.Close() | ||||
} | } | ||||
if rows.lastError == nil { | |||||
if rows.rows != nil { | |||||
rows.lastError = rows.rows.Close() | |||||
if rows.lastError != nil { | |||||
return rows.lastError | |||||
} | |||||
} | |||||
} else { | |||||
if rows.rows != nil { | |||||
defer rows.rows.Close() | |||||
} | |||||
if rows.rows != nil { | |||||
return rows.rows.Close() | |||||
} | } | ||||
return rows.lastError | return rows.lastError | ||||
} | } |
package xorm | package xorm | ||||
import ( | import ( | ||||
"context" | |||||
"database/sql" | "database/sql" | ||||
"encoding/json" | |||||
"errors" | "errors" | ||||
"fmt" | "fmt" | ||||
"hash/crc32" | "hash/crc32" | ||||
"strings" | "strings" | ||||
"time" | "time" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | |||||
type sessionType int | |||||
const ( | |||||
engineSession sessionType = iota | |||||
groupSession | |||||
) | ) | ||||
// Session keep a pointer to sql.DB and provides all execution of all | // Session keep a pointer to sql.DB and provides all execution of all | ||||
lastSQL string | lastSQL string | ||||
lastSQLArgs []interface{} | lastSQLArgs []interface{} | ||||
err error | |||||
ctx context.Context | |||||
sessionType sessionType | |||||
} | } | ||||
// Clone copy all the session's content and return a new session | // Clone copy all the session's content and return a new session | ||||
session.lastSQL = "" | session.lastSQL = "" | ||||
session.lastSQLArgs = []interface{}{} | session.lastSQLArgs = []interface{}{} | ||||
session.ctx = session.engine.defaultContext | |||||
} | } | ||||
// Close release the connection from pool | // Close release the connection from pool | ||||
var has bool | var has bool | ||||
stmt, has = session.stmtCache[crc] | stmt, has = session.stmtCache[crc] | ||||
if !has { | if !has { | ||||
stmt, err = db.Prepare(sqlStr) | |||||
stmt, err = db.PrepareContext(session.ctx, sqlStr) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
continue | continue | ||||
} | } | ||||
if fieldValue.CanAddr() { | if fieldValue.CanAddr() { | ||||
err := json.Unmarshal(bs, fieldValue.Addr().Interface()) | |||||
err := DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface()) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
} else { | } else { | ||||
x := reflect.New(fieldType) | x := reflect.New(fieldType) | ||||
err := json.Unmarshal(bs, x.Interface()) | |||||
err := DefaultJSONHandler.Unmarshal(bs, x.Interface()) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
hasAssigned = true | hasAssigned = true | ||||
if len(bs) > 0 { | if len(bs) > 0 { | ||||
if fieldValue.CanAddr() { | if fieldValue.CanAddr() { | ||||
err := json.Unmarshal(bs, fieldValue.Addr().Interface()) | |||||
err := DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface()) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
} else { | } else { | ||||
x := reflect.New(fieldType) | x := reflect.New(fieldType) | ||||
err := json.Unmarshal(bs, x.Interface()) | |||||
err := DefaultJSONHandler.Unmarshal(bs, x.Interface()) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
hasAssigned = true | hasAssigned = true | ||||
if col.SQLType.IsText() { | if col.SQLType.IsText() { | ||||
x := reflect.New(fieldType) | x := reflect.New(fieldType) | ||||
err := json.Unmarshal(vv.Bytes(), x.Interface()) | |||||
err := DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface()) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
hasAssigned = true | hasAssigned = true | ||||
x := reflect.New(fieldType) | x := reflect.New(fieldType) | ||||
if len([]byte(vv.String())) > 0 { | if len([]byte(vv.String())) > 0 { | ||||
err := json.Unmarshal([]byte(vv.String()), x.Interface()) | |||||
err := DefaultJSONHandler.Unmarshal([]byte(vv.String()), x.Interface()) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
hasAssigned = true | hasAssigned = true | ||||
x := reflect.New(fieldType) | x := reflect.New(fieldType) | ||||
if len(vv.Bytes()) > 0 { | if len(vv.Bytes()) > 0 { | ||||
err := json.Unmarshal(vv.Bytes(), x.Interface()) | |||||
err := DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface()) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
case core.Complex64Type: | case core.Complex64Type: | ||||
var x complex64 | var x complex64 | ||||
if len([]byte(vv.String())) > 0 { | if len([]byte(vv.String())) > 0 { | ||||
err := json.Unmarshal([]byte(vv.String()), &x) | |||||
err := DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
case core.Complex128Type: | case core.Complex128Type: | ||||
var x complex128 | var x complex128 | ||||
if len([]byte(vv.String())) > 0 { | if len([]byte(vv.String())) > 0 { | ||||
err := json.Unmarshal([]byte(vv.String()), &x) | |||||
err := DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } |
"strings" | "strings" | ||||
"time" | "time" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
type incrParam struct { | type incrParam struct { |
package xorm | package xorm | ||||
import "github.com/go-xorm/builder" | |||||
import "xorm.io/builder" | |||||
// Sql provides raw sql input parameter. When you have a complex SQL statement | // Sql provides raw sql input parameter. When you have a complex SQL statement | ||||
// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. | // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. |
// Copyright 2017 The Xorm Authors. All rights reserved. | |||||
// Copyright 2019 The Xorm Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | // Use of this source code is governed by a BSD-style | ||||
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build go1.8 | |||||
package xorm | package xorm | ||||
import "context" | import "context" | ||||
// PingContext tests if database is alive | |||||
func (engine *Engine) PingContext(ctx context.Context) error { | |||||
session := engine.NewSession() | |||||
defer session.Close() | |||||
return session.PingContext(ctx) | |||||
// Context sets the context on this session | |||||
func (session *Session) Context(ctx context.Context) *Session { | |||||
session.ctx = ctx | |||||
return session | |||||
} | } | ||||
// PingContext test if database is ok | // PingContext test if database is ok |
import ( | import ( | ||||
"database/sql" | "database/sql" | ||||
"database/sql/driver" | "database/sql/driver" | ||||
"encoding/json" | |||||
"errors" | "errors" | ||||
"fmt" | "fmt" | ||||
"reflect" | "reflect" | ||||
"strings" | "strings" | ||||
"time" | "time" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) { | func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) { | ||||
case reflect.Complex64, reflect.Complex128: | case reflect.Complex64, reflect.Complex128: | ||||
x := reflect.New(fieldType) | x := reflect.New(fieldType) | ||||
if len(data) > 0 { | if len(data) > 0 { | ||||
err := json.Unmarshal(data, x.Interface()) | |||||
err := DefaultJSONHandler.Unmarshal(data, x.Interface()) | |||||
if err != nil { | if err != nil { | ||||
session.engine.logger.Error(err) | session.engine.logger.Error(err) | ||||
return err | return err | ||||
if col.SQLType.IsText() { | if col.SQLType.IsText() { | ||||
x := reflect.New(fieldType) | x := reflect.New(fieldType) | ||||
if len(data) > 0 { | if len(data) > 0 { | ||||
err := json.Unmarshal(data, x.Interface()) | |||||
err := DefaultJSONHandler.Unmarshal(data, x.Interface()) | |||||
if err != nil { | if err != nil { | ||||
session.engine.logger.Error(err) | session.engine.logger.Error(err) | ||||
return err | return err | ||||
} else { | } else { | ||||
x := reflect.New(fieldType) | x := reflect.New(fieldType) | ||||
if len(data) > 0 { | if len(data) > 0 { | ||||
err := json.Unmarshal(data, x.Interface()) | |||||
err := DefaultJSONHandler.Unmarshal(data, x.Interface()) | |||||
if err != nil { | if err != nil { | ||||
session.engine.logger.Error(err) | session.engine.logger.Error(err) | ||||
return err | return err | ||||
case core.Complex64Type.Kind(): | case core.Complex64Type.Kind(): | ||||
var x complex64 | var x complex64 | ||||
if len(data) > 0 { | if len(data) > 0 { | ||||
err := json.Unmarshal(data, &x) | |||||
err := DefaultJSONHandler.Unmarshal(data, &x) | |||||
if err != nil { | if err != nil { | ||||
session.engine.logger.Error(err) | session.engine.logger.Error(err) | ||||
return err | return err | ||||
case core.Complex128Type.Kind(): | case core.Complex128Type.Kind(): | ||||
var x complex128 | var x complex128 | ||||
if len(data) > 0 { | if len(data) > 0 { | ||||
err := json.Unmarshal(data, &x) | |||||
err := DefaultJSONHandler.Unmarshal(data, &x) | |||||
if err != nil { | if err != nil { | ||||
session.engine.logger.Error(err) | session.engine.logger.Error(err) | ||||
return err | return err | ||||
} | } | ||||
if col.SQLType.IsText() { | if col.SQLType.IsText() { | ||||
bytes, err := json.Marshal(fieldValue.Interface()) | |||||
bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
session.engine.logger.Error(err) | session.engine.logger.Error(err) | ||||
return 0, err | return 0, err | ||||
} | } | ||||
return string(bytes), nil | return string(bytes), nil | ||||
} else if col.SQLType.IsBlob() { | } else if col.SQLType.IsBlob() { | ||||
bytes, err := json.Marshal(fieldValue.Interface()) | |||||
bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
session.engine.logger.Error(err) | session.engine.logger.Error(err) | ||||
return 0, err | return 0, err | ||||
} | } | ||||
return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type()) | return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type()) | ||||
case reflect.Complex64, reflect.Complex128: | case reflect.Complex64, reflect.Complex128: | ||||
bytes, err := json.Marshal(fieldValue.Interface()) | |||||
bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
session.engine.logger.Error(err) | session.engine.logger.Error(err) | ||||
return 0, err | return 0, err | ||||
} | } | ||||
if col.SQLType.IsText() { | if col.SQLType.IsText() { | ||||
bytes, err := json.Marshal(fieldValue.Interface()) | |||||
bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
session.engine.logger.Error(err) | session.engine.logger.Error(err) | ||||
return 0, err | return 0, err | ||||
(fieldValue.Type().Elem().Kind() == reflect.Uint8) { | (fieldValue.Type().Elem().Kind() == reflect.Uint8) { | ||||
bytes = fieldValue.Bytes() | bytes = fieldValue.Bytes() | ||||
} else { | } else { | ||||
bytes, err = json.Marshal(fieldValue.Interface()) | |||||
bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
session.engine.logger.Error(err) | session.engine.logger.Error(err) | ||||
return 0, err | return 0, err |
"fmt" | "fmt" | ||||
"strconv" | "strconv" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
func (session *Session) cacheDelete(table *core.Table, tableName, sqlStr string, args ...interface{}) error { | func (session *Session) cacheDelete(table *core.Table, tableName, sqlStr string, args ...interface{}) error { | ||||
defer session.Close() | defer session.Close() | ||||
} | } | ||||
if session.statement.lastError != nil { | |||||
return 0, session.statement.lastError | |||||
} | |||||
if err := session.statement.setRefBean(bean); err != nil { | if err := session.statement.setRefBean(bean); err != nil { | ||||
return 0, err | return 0, err | ||||
} | } |
"fmt" | "fmt" | ||||
"reflect" | "reflect" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/core" | |||||
"xorm.io/builder" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// Exist returns true if the record exist otherwise return false | // Exist returns true if the record exist otherwise return false | ||||
defer session.Close() | defer session.Close() | ||||
} | } | ||||
if session.statement.lastError != nil { | |||||
return false, session.statement.lastError | |||||
} | |||||
var sqlStr string | var sqlStr string | ||||
var args []interface{} | var args []interface{} | ||||
var err error | var err error | ||||
return false, ErrTableNotFound | return false, ErrTableNotFound | ||||
} | } | ||||
tableName = session.statement.Engine.Quote(tableName) | |||||
if session.statement.cond.IsValid() { | if session.statement.cond.IsValid() { | ||||
condSQL, condArgs, err := builder.ToSQL(session.statement.cond) | condSQL, condArgs, err := builder.ToSQL(session.statement.cond) | ||||
if err != nil { | if err != nil { | ||||
} | } | ||||
if session.engine.dialect.DBType() == core.MSSQL { | if session.engine.dialect.DBType() == core.MSSQL { | ||||
sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s WHERE %s", tableName, condSQL) | |||||
sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s WHERE %s", tableName, condSQL) | |||||
} else if session.engine.dialect.DBType() == core.ORACLE { | |||||
sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE (%s) AND ROWNUM=1", tableName, condSQL) | |||||
} else { | } else { | ||||
sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) | sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) | ||||
} | } | ||||
args = condArgs | args = condArgs | ||||
} else { | } else { | ||||
if session.engine.dialect.DBType() == core.MSSQL { | if session.engine.dialect.DBType() == core.MSSQL { | ||||
sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s", tableName) | |||||
sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s", tableName) | |||||
} else if session.engine.dialect.DBType() == core.ORACLE { | |||||
sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE ROWNUM=1", tableName) | |||||
} else { | } else { | ||||
sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) | sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) | ||||
} | } |
"reflect" | "reflect" | ||||
"strings" | "strings" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/core" | |||||
"xorm.io/builder" | |||||
"xorm.io/core" | |||||
) | ) | ||||
const ( | const ( | ||||
} | } | ||||
func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) error { | func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) error { | ||||
if session.statement.lastError != nil { | |||||
return session.statement.lastError | |||||
} | |||||
sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) | sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) | ||||
if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map { | if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map { | ||||
return errors.New("needs a pointer to a slice or a map") | return errors.New("needs a pointer to a slice or a map") |
"reflect" | "reflect" | ||||
"strconv" | "strconv" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// Get retrieve one record from database, bean's non-empty fields | // Get retrieve one record from database, bean's non-empty fields | ||||
} | } | ||||
func (session *Session) get(bean interface{}) (bool, error) { | func (session *Session) get(bean interface{}) (bool, error) { | ||||
if session.statement.lastError != nil { | |||||
return false, session.statement.lastError | |||||
} | |||||
beanValue := reflect.ValueOf(bean) | beanValue := reflect.ValueOf(bean) | ||||
if beanValue.Kind() != reflect.Ptr { | if beanValue.Kind() != reflect.Ptr { | ||||
return false, errors.New("needs a pointer to a value") | return false, errors.New("needs a pointer to a value") |
"errors" | "errors" | ||||
"fmt" | "fmt" | ||||
"reflect" | "reflect" | ||||
"sort" | |||||
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// Insert insert one or more beans | // Insert insert one or more beans | ||||
} | } | ||||
for _, bean := range beans { | for _, bean := range beans { | ||||
sliceValue := reflect.Indirect(reflect.ValueOf(bean)) | |||||
if sliceValue.Kind() == reflect.Slice { | |||||
size := sliceValue.Len() | |||||
if size > 0 { | |||||
if session.engine.SupportInsertMany() { | |||||
cnt, err := session.innerInsertMulti(bean) | |||||
if err != nil { | |||||
return affected, err | |||||
} | |||||
affected += cnt | |||||
} else { | |||||
for i := 0; i < size; i++ { | |||||
cnt, err := session.innerInsert(sliceValue.Index(i).Interface()) | |||||
switch bean.(type) { | |||||
case map[string]interface{}: | |||||
cnt, err := session.insertMapInterface(bean.(map[string]interface{})) | |||||
if err != nil { | |||||
return affected, err | |||||
} | |||||
affected += cnt | |||||
case []map[string]interface{}: | |||||
s := bean.([]map[string]interface{}) | |||||
session.autoResetStatement = false | |||||
for i := 0; i < len(s); i++ { | |||||
cnt, err := session.insertMapInterface(s[i]) | |||||
if err != nil { | |||||
return affected, err | |||||
} | |||||
affected += cnt | |||||
} | |||||
case map[string]string: | |||||
cnt, err := session.insertMapString(bean.(map[string]string)) | |||||
if err != nil { | |||||
return affected, err | |||||
} | |||||
affected += cnt | |||||
case []map[string]string: | |||||
s := bean.([]map[string]string) | |||||
session.autoResetStatement = false | |||||
for i := 0; i < len(s); i++ { | |||||
cnt, err := session.insertMapString(s[i]) | |||||
if err != nil { | |||||
return affected, err | |||||
} | |||||
affected += cnt | |||||
} | |||||
default: | |||||
sliceValue := reflect.Indirect(reflect.ValueOf(bean)) | |||||
if sliceValue.Kind() == reflect.Slice { | |||||
size := sliceValue.Len() | |||||
if size > 0 { | |||||
if session.engine.SupportInsertMany() { | |||||
cnt, err := session.innerInsertMulti(bean) | |||||
if err != nil { | if err != nil { | ||||
return affected, err | return affected, err | ||||
} | } | ||||
affected += cnt | affected += cnt | ||||
} else { | |||||
for i := 0; i < size; i++ { | |||||
cnt, err := session.innerInsert(sliceValue.Index(i).Interface()) | |||||
if err != nil { | |||||
return affected, err | |||||
} | |||||
affected += cnt | |||||
} | |||||
} | } | ||||
} | } | ||||
} else { | |||||
cnt, err := session.innerInsert(bean) | |||||
if err != nil { | |||||
return affected, err | |||||
} | |||||
affected += cnt | |||||
} | } | ||||
} else { | |||||
cnt, err := session.innerInsert(bean) | |||||
if err != nil { | |||||
return affected, err | |||||
} | |||||
affected += cnt | |||||
} | } | ||||
} | } | ||||
var sqlStr string | var sqlStr string | ||||
var tableName = session.statement.TableName() | var tableName = session.statement.TableName() | ||||
var output string | |||||
if session.engine.dialect.DBType() == core.MSSQL && len(table.AutoIncrement) > 0 { | |||||
output = fmt.Sprintf(" OUTPUT Inserted.%s", table.AutoIncrement) | |||||
} | |||||
if len(colPlaces) > 0 { | if len(colPlaces) > 0 { | ||||
sqlStr = fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)", | |||||
sqlStr = fmt.Sprintf("INSERT INTO %s (%v%v%v)%s VALUES (%v)", | |||||
session.engine.Quote(tableName), | session.engine.Quote(tableName), | ||||
session.engine.QuoteStr(), | session.engine.QuoteStr(), | ||||
strings.Join(colNames, session.engine.Quote(", ")), | strings.Join(colNames, session.engine.Quote(", ")), | ||||
session.engine.QuoteStr(), | session.engine.QuoteStr(), | ||||
output, | |||||
colPlaces) | colPlaces) | ||||
} else { | } else { | ||||
if session.engine.dialect.DBType() == core.MYSQL { | if session.engine.dialect.DBType() == core.MYSQL { | ||||
sqlStr = fmt.Sprintf("INSERT INTO %s VALUES ()", session.engine.Quote(tableName)) | sqlStr = fmt.Sprintf("INSERT INTO %s VALUES ()", session.engine.Quote(tableName)) | ||||
} else { | } else { | ||||
sqlStr = fmt.Sprintf("INSERT INTO %s DEFAULT VALUES", session.engine.Quote(tableName)) | |||||
sqlStr = fmt.Sprintf("INSERT INTO %s%s DEFAULT VALUES", session.engine.Quote(tableName), output) | |||||
} | } | ||||
} | } | ||||
if len(table.AutoIncrement) > 0 && session.engine.dialect.DBType() == core.POSTGRES { | |||||
sqlStr = sqlStr + " RETURNING " + session.engine.Quote(table.AutoIncrement) | |||||
} | |||||
handleAfterInsertProcessorFunc := func(bean interface{}) { | handleAfterInsertProcessorFunc := func(bean interface{}) { | ||||
if session.isAutoCommit { | if session.isAutoCommit { | ||||
for _, closure := range session.afterClosures { | for _, closure := range session.afterClosures { | ||||
aiValue.Set(int64ToIntValue(id, aiValue.Type())) | aiValue.Set(int64ToIntValue(id, aiValue.Type())) | ||||
return 1, nil | return 1, nil | ||||
} else if session.engine.dialect.DBType() == core.POSTGRES && len(table.AutoIncrement) > 0 { | |||||
//assert table.AutoIncrement != "" | |||||
sqlStr = sqlStr + " RETURNING " + session.engine.Quote(table.AutoIncrement) | |||||
} else if len(table.AutoIncrement) > 0 && (session.engine.dialect.DBType() == core.POSTGRES || session.engine.dialect.DBType() == core.MSSQL) { | |||||
res, err := session.queryBytes(sqlStr, args...) | res, err := session.queryBytes(sqlStr, args...) | ||||
if err != nil { | if err != nil { | ||||
} | } | ||||
if len(res) < 1 { | if len(res) < 1 { | ||||
return 0, errors.New("insert no error but not returned id") | |||||
return 0, errors.New("insert successfully but not returned id") | |||||
} | } | ||||
idByte := res[0][table.AutoIncrement] | idByte := res[0][table.AutoIncrement] | ||||
} | } | ||||
return colNames, args, nil | return colNames, args, nil | ||||
} | } | ||||
func (session *Session) insertMapInterface(m map[string]interface{}) (int64, error) { | |||||
if len(m) == 0 { | |||||
return 0, ErrParamsType | |||||
} | |||||
var columns = make([]string, 0, len(m)) | |||||
for k := range m { | |||||
columns = append(columns, k) | |||||
} | |||||
sort.Strings(columns) | |||||
qm := strings.Repeat("?,", len(columns)) | |||||
qm = "(" + qm[:len(qm)-1] + ")" | |||||
tableName := session.statement.TableName() | |||||
if len(tableName) <= 0 { | |||||
return 0, ErrTableNotFound | |||||
} | |||||
var sql = fmt.Sprintf("INSERT INTO %s (`%s`) VALUES %s", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm) | |||||
var args = make([]interface{}, 0, len(m)) | |||||
for _, colName := range columns { | |||||
args = append(args, m[colName]) | |||||
} | |||||
if err := session.cacheInsert(tableName); err != nil { | |||||
return 0, err | |||||
} | |||||
res, err := session.exec(sql, args...) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
affected, err := res.RowsAffected() | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
return affected, nil | |||||
} | |||||
func (session *Session) insertMapString(m map[string]string) (int64, error) { | |||||
if len(m) == 0 { | |||||
return 0, ErrParamsType | |||||
} | |||||
var columns = make([]string, 0, len(m)) | |||||
for k := range m { | |||||
columns = append(columns, k) | |||||
} | |||||
sort.Strings(columns) | |||||
qm := strings.Repeat("?,", len(columns)) | |||||
qm = "(" + qm[:len(qm)-1] + ")" | |||||
tableName := session.statement.TableName() | |||||
if len(tableName) <= 0 { | |||||
return 0, ErrTableNotFound | |||||
} | |||||
var sql = fmt.Sprintf("INSERT INTO %s (`%s`) VALUES %s", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm) | |||||
var args = make([]interface{}, 0, len(m)) | |||||
for _, colName := range columns { | |||||
args = append(args, m[colName]) | |||||
} | |||||
if err := session.cacheInsert(tableName); err != nil { | |||||
return 0, err | |||||
} | |||||
res, err := session.exec(sql, args...) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
affected, err := res.RowsAffected() | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
return affected, nil | |||||
} |
defer session.Close() | defer session.Close() | ||||
} | } | ||||
if session.statement.lastError != nil { | |||||
return session.statement.lastError | |||||
} | |||||
if session.statement.bufferSize > 0 { | if session.statement.bufferSize > 0 { | ||||
return session.bufferIterate(bean, fun) | return session.bufferIterate(bean, fun) | ||||
} | } |
"strings" | "strings" | ||||
"time" | "time" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/core" | |||||
"xorm.io/builder" | |||||
"xorm.io/core" | |||||
) | ) | ||||
func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interface{}, error) { | |||||
if len(sqlorArgs) > 0 { | |||||
return convertSQLOrArgs(sqlorArgs...) | |||||
func (session *Session) genQuerySQL(sqlOrArgs ...interface{}) (string, []interface{}, error) { | |||||
if len(sqlOrArgs) > 0 { | |||||
return convertSQLOrArgs(sqlOrArgs...) | |||||
} | } | ||||
if session.statement.RawSQL != "" { | if session.statement.RawSQL != "" { | ||||
} | } | ||||
// Query runs a raw sql and return records as []map[string][]byte | // Query runs a raw sql and return records as []map[string][]byte | ||||
func (session *Session) Query(sqlorArgs ...interface{}) ([]map[string][]byte, error) { | |||||
func (session *Session) Query(sqlOrArgs ...interface{}) ([]map[string][]byte, error) { | |||||
if session.isAutoClose { | if session.isAutoClose { | ||||
defer session.Close() | defer session.Close() | ||||
} | } | ||||
sqlStr, args, err := session.genQuerySQL(sqlorArgs...) | |||||
sqlStr, args, err := session.genQuerySQL(sqlOrArgs...) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
} | } | ||||
// QueryString runs a raw sql and return records as []map[string]string | // QueryString runs a raw sql and return records as []map[string]string | ||||
func (session *Session) QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) { | |||||
func (session *Session) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) { | |||||
if session.isAutoClose { | if session.isAutoClose { | ||||
defer session.Close() | defer session.Close() | ||||
} | } | ||||
sqlStr, args, err := session.genQuerySQL(sqlorArgs...) | |||||
sqlStr, args, err := session.genQuerySQL(sqlOrArgs...) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
} | } | ||||
// QuerySliceString runs a raw sql and return records as [][]string | // QuerySliceString runs a raw sql and return records as [][]string | ||||
func (session *Session) QuerySliceString(sqlorArgs ...interface{}) ([][]string, error) { | |||||
func (session *Session) QuerySliceString(sqlOrArgs ...interface{}) ([][]string, error) { | |||||
if session.isAutoClose { | if session.isAutoClose { | ||||
defer session.Close() | defer session.Close() | ||||
} | } | ||||
sqlStr, args, err := session.genQuerySQL(sqlorArgs...) | |||||
sqlStr, args, err := session.genQuerySQL(sqlOrArgs...) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
} | } | ||||
// QueryInterface runs a raw sql and return records as []map[string]interface{} | // QueryInterface runs a raw sql and return records as []map[string]interface{} | ||||
func (session *Session) QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) { | |||||
func (session *Session) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) { | |||||
if session.isAutoClose { | if session.isAutoClose { | ||||
defer session.Close() | defer session.Close() | ||||
} | } | ||||
sqlStr, args, err := session.genQuerySQL(sqlorArgs...) | |||||
sqlStr, args, err := session.genQuerySQL(sqlOrArgs...) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } |
"reflect" | "reflect" | ||||
"time" | "time" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/core" | |||||
"xorm.io/builder" | |||||
"xorm.io/core" | |||||
) | ) | ||||
func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) { | func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) { | ||||
if session.isAutoCommit { | if session.isAutoCommit { | ||||
var db *core.DB | var db *core.DB | ||||
if session.engine.engineGroup != nil { | |||||
if session.sessionType == groupSession { | |||||
db = session.engine.engineGroup.Slave().DB() | db = session.engine.engineGroup.Slave().DB() | ||||
} else { | } else { | ||||
db = session.DB() | db = session.DB() | ||||
return nil, err | return nil, err | ||||
} | } | ||||
rows, err := stmt.Query(args...) | |||||
rows, err := stmt.QueryContext(session.ctx, args...) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
return rows, nil | return rows, nil | ||||
} | } | ||||
rows, err := db.Query(sqlStr, args...) | |||||
rows, err := db.QueryContext(session.ctx, sqlStr, args...) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
return rows, nil | return rows, nil | ||||
} | } | ||||
rows, err := session.tx.Query(sqlStr, args...) | |||||
rows, err := session.tx.QueryContext(session.ctx, sqlStr, args...) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
} | } | ||||
if !session.isAutoCommit { | if !session.isAutoCommit { | ||||
return session.tx.Exec(sqlStr, args...) | |||||
return session.tx.ExecContext(session.ctx, sqlStr, args...) | |||||
} | } | ||||
if session.prepareStmt { | if session.prepareStmt { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
res, err := stmt.Exec(args...) | |||||
res, err := stmt.ExecContext(session.ctx, args...) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
return res, nil | return res, nil | ||||
} | } | ||||
return session.DB().Exec(sqlStr, args...) | |||||
return session.DB().ExecContext(session.ctx, sqlStr, args...) | |||||
} | } | ||||
func convertSQLOrArgs(sqlorArgs ...interface{}) (string, []interface{}, error) { | |||||
switch sqlorArgs[0].(type) { | |||||
func convertSQLOrArgs(sqlOrArgs ...interface{}) (string, []interface{}, error) { | |||||
switch sqlOrArgs[0].(type) { | |||||
case string: | case string: | ||||
return sqlorArgs[0].(string), sqlorArgs[1:], nil | |||||
return sqlOrArgs[0].(string), sqlOrArgs[1:], nil | |||||
case *builder.Builder: | case *builder.Builder: | ||||
return sqlorArgs[0].(*builder.Builder).ToSQL() | |||||
return sqlOrArgs[0].(*builder.Builder).ToSQL() | |||||
case builder.Builder: | case builder.Builder: | ||||
bd := sqlorArgs[0].(builder.Builder) | |||||
bd := sqlOrArgs[0].(builder.Builder) | |||||
return bd.ToSQL() | return bd.ToSQL() | ||||
} | } | ||||
} | } | ||||
// Exec raw sql | // Exec raw sql | ||||
func (session *Session) Exec(sqlorArgs ...interface{}) (sql.Result, error) { | |||||
func (session *Session) Exec(sqlOrArgs ...interface{}) (sql.Result, error) { | |||||
if session.isAutoClose { | if session.isAutoClose { | ||||
defer session.Close() | defer session.Close() | ||||
} | } | ||||
if len(sqlorArgs) == 0 { | |||||
if len(sqlOrArgs) == 0 { | |||||
return nil, ErrUnSupportedType | return nil, ErrUnSupportedType | ||||
} | } | ||||
sqlStr, args, err := convertSQLOrArgs(sqlorArgs...) | |||||
sqlStr, args, err := convertSQLOrArgs(sqlOrArgs...) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } |
"fmt" | "fmt" | ||||
"strings" | "strings" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// Ping test if database is ok | // Ping test if database is ok | ||||
} | } | ||||
session.engine.logger.Infof("PING DATABASE %v", session.engine.DriverName()) | session.engine.logger.Infof("PING DATABASE %v", session.engine.DriverName()) | ||||
return session.DB().Ping() | |||||
return session.DB().PingContext(session.ctx) | |||||
} | } | ||||
// CreateTable create a table according a bean | // CreateTable create a table according a bean |
// Begin a transaction | // Begin a transaction | ||||
func (session *Session) Begin() error { | func (session *Session) Begin() error { | ||||
if session.isAutoCommit { | if session.isAutoCommit { | ||||
tx, err := session.DB().Begin() | |||||
tx, err := session.DB().BeginTx(session.ctx, nil) | |||||
if err != nil { | if err != nil { | ||||
return err | return err | ||||
} | } |
"strconv" | "strconv" | ||||
"strings" | "strings" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/core" | |||||
"xorm.io/builder" | |||||
"xorm.io/core" | |||||
) | ) | ||||
func (session *Session) cacheUpdate(table *core.Table, tableName, sqlStr string, args ...interface{}) error { | func (session *Session) cacheUpdate(table *core.Table, tableName, sqlStr string, args ...interface{}) error { | ||||
defer session.Close() | defer session.Close() | ||||
} | } | ||||
if session.statement.lastError != nil { | |||||
return 0, session.statement.lastError | |||||
} | |||||
v := rValue(bean) | v := rValue(bean) | ||||
t := v.Type() | t := v.Type() | ||||
} | } | ||||
var autoCond builder.Cond | var autoCond builder.Cond | ||||
if !session.statement.noAutoCondition && len(condiBean) > 0 { | |||||
if c, ok := condiBean[0].(map[string]interface{}); ok { | |||||
autoCond = builder.Eq(c) | |||||
} else { | |||||
ct := reflect.TypeOf(condiBean[0]) | |||||
k := ct.Kind() | |||||
if k == reflect.Ptr { | |||||
k = ct.Elem().Kind() | |||||
if !session.statement.noAutoCondition { | |||||
condBeanIsStruct := false | |||||
if len(condiBean) > 0 { | |||||
if c, ok := condiBean[0].(map[string]interface{}); ok { | |||||
autoCond = builder.Eq(c) | |||||
} else { | |||||
ct := reflect.TypeOf(condiBean[0]) | |||||
k := ct.Kind() | |||||
if k == reflect.Ptr { | |||||
k = ct.Elem().Kind() | |||||
} | |||||
if k == reflect.Struct { | |||||
var err error | |||||
autoCond, err = session.statement.buildConds(session.statement.RefTable, condiBean[0], true, true, false, true, false) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
condBeanIsStruct = true | |||||
} else { | |||||
return 0, ErrConditionType | |||||
} | |||||
} | } | ||||
if k == reflect.Struct { | |||||
var err error | |||||
autoCond, err = session.statement.buildConds(session.statement.RefTable, condiBean[0], true, true, false, true, false) | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
if !condBeanIsStruct && table != nil { | |||||
if col := table.DeletedColumn(); col != nil && !session.statement.unscoped { // tag "deleted" is enabled | |||||
autoCond1 := session.engine.CondDeleted(session.engine.Quote(col.Name)) | |||||
if autoCond == nil { | |||||
autoCond = autoCond1 | |||||
} else { | |||||
autoCond = autoCond.And(autoCond1) | |||||
} | } | ||||
} else { | |||||
return 0, ErrConditionType | |||||
} | } | ||||
} | } | ||||
} | } |
import ( | import ( | ||||
"database/sql/driver" | "database/sql/driver" | ||||
"encoding/json" | |||||
"errors" | "errors" | ||||
"fmt" | "fmt" | ||||
"reflect" | "reflect" | ||||
"strings" | "strings" | ||||
"time" | "time" | ||||
"github.com/go-xorm/builder" | |||||
"github.com/go-xorm/core" | |||||
"xorm.io/builder" | |||||
"xorm.io/core" | |||||
) | ) | ||||
// Statement save all the sql info for executing SQL | // Statement save all the sql info for executing SQL | ||||
cond builder.Cond | cond builder.Cond | ||||
bufferSize int | bufferSize int | ||||
context ContextCache | context ContextCache | ||||
lastError error | |||||
} | } | ||||
// Init reset all the statement's fields | // Init reset all the statement's fields | ||||
statement.cond = builder.NewCond() | statement.cond = builder.NewCond() | ||||
statement.bufferSize = 0 | statement.bufferSize = 0 | ||||
statement.context = nil | statement.context = nil | ||||
statement.lastError = nil | |||||
} | } | ||||
// NoAutoCondition if you do not want convert bean's field as query condition, then use this function | // NoAutoCondition if you do not want convert bean's field as query condition, then use this function | ||||
var err error | var err error | ||||
statement.RawSQL, statement.RawParams, err = query.(*builder.Builder).ToSQL() | statement.RawSQL, statement.RawParams, err = query.(*builder.Builder).ToSQL() | ||||
if err != nil { | if err != nil { | ||||
statement.Engine.logger.Error(err) | |||||
statement.lastError = err | |||||
} | } | ||||
case string: | case string: | ||||
statement.RawSQL = query.(string) | statement.RawSQL = query.(string) | ||||
statement.RawParams = args | statement.RawParams = args | ||||
default: | default: | ||||
statement.Engine.logger.Error("unsupported sql type") | |||||
statement.lastError = ErrUnSupportedSQLType | |||||
} | } | ||||
return statement | return statement | ||||
} | } | ||||
} | } | ||||
default: | default: | ||||
// TODO: not support condition type | |||||
statement.lastError = ErrConditionType | |||||
} | } | ||||
return statement | return statement | ||||
} else { | } else { | ||||
// Blank struct could not be as update data | // Blank struct could not be as update data | ||||
if requiredField || !isStructZero(fieldValue) { | if requiredField || !isStructZero(fieldValue) { | ||||
bytes, err := json.Marshal(fieldValue.Interface()) | |||||
bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
panic(fmt.Sprintf("mashal %v failed", fieldValue.Interface())) | panic(fmt.Sprintf("mashal %v failed", fieldValue.Interface())) | ||||
} | } | ||||
} | } | ||||
if col.SQLType.IsText() { | if col.SQLType.IsText() { | ||||
bytes, err := json.Marshal(fieldValue.Interface()) | |||||
bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
engine.logger.Error(err) | engine.logger.Error(err) | ||||
continue | continue | ||||
fieldType.Elem().Kind() == reflect.Uint8 { | fieldType.Elem().Kind() == reflect.Uint8 { | ||||
val = fieldValue.Slice(0, 0).Interface() | val = fieldValue.Slice(0, 0).Interface() | ||||
} else { | } else { | ||||
bytes, err = json.Marshal(fieldValue.Interface()) | |||||
bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface()) | |||||
if err != nil { | if err != nil { | ||||
engine.logger.Error(err) | engine.logger.Error(err) | ||||
continue | continue | ||||
fmt.Fprintf(&buf, "%v JOIN ", joinOP) | fmt.Fprintf(&buf, "%v JOIN ", joinOP) | ||||
} | } | ||||
tbName := statement.Engine.TableName(tablename, true) | |||||
switch tp := tablename.(type) { | |||||
case builder.Builder: | |||||
subSQL, subQueryArgs, err := tp.ToSQL() | |||||
if err != nil { | |||||
statement.lastError = err | |||||
return statement | |||||
} | |||||
tbs := strings.Split(tp.TableName(), ".") | |||||
var aliasName = strings.Trim(tbs[len(tbs)-1], statement.Engine.QuoteStr()) | |||||
fmt.Fprintf(&buf, "(%s) %s ON %v", subSQL, aliasName, condition) | |||||
statement.joinArgs = append(statement.joinArgs, subQueryArgs...) | |||||
case *builder.Builder: | |||||
subSQL, subQueryArgs, err := tp.ToSQL() | |||||
if err != nil { | |||||
statement.lastError = err | |||||
return statement | |||||
} | |||||
tbs := strings.Split(tp.TableName(), ".") | |||||
var aliasName = strings.Trim(tbs[len(tbs)-1], statement.Engine.QuoteStr()) | |||||
fmt.Fprintf(&buf, "(%s) %s ON %v", subSQL, aliasName, condition) | |||||
statement.joinArgs = append(statement.joinArgs, subQueryArgs...) | |||||
default: | |||||
tbName := statement.Engine.TableName(tablename, true) | |||||
fmt.Fprintf(&buf, "%s ON %v", tbName, condition) | |||||
} | |||||
fmt.Fprintf(&buf, "%s ON %v", tbName, condition) | |||||
statement.JoinStr = buf.String() | statement.JoinStr = buf.String() | ||||
statement.joinArgs = append(statement.joinArgs, args...) | statement.joinArgs = append(statement.joinArgs, args...) | ||||
return statement | return statement | ||||
if dialect.DBType() == core.MSSQL { | if dialect.DBType() == core.MSSQL { | ||||
if statement.LimitN > 0 { | if statement.LimitN > 0 { | ||||
top = fmt.Sprintf(" TOP %d ", statement.LimitN) | |||||
top = fmt.Sprintf("TOP %d ", statement.LimitN) | |||||
} | } | ||||
if statement.Start > 0 { | if statement.Start > 0 { | ||||
var column string | var column string |
"fmt" | "fmt" | ||||
"log/syslog" | "log/syslog" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
var _ core.ILogger = &SyslogLogger{} | var _ core.ILogger = &SyslogLogger{} |
"strings" | "strings" | ||||
"time" | "time" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
type tagContext struct { | type tagContext struct { |
go test -db=mssql -conn_str="server=192.168.1.58;user id=sa;password=123456;database=xorm_test" | |||||
go test -db=mssql -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" |
go test -db=mysql -conn_str="root:@tcp(localhost:4000)/xorm_test" -ignore_select_update=true |
// Copyright 2017 The Xorm Authors. All rights reserved. | |||||
// Use of this source code is governed by a BSD-style | |||||
// license that can be found in the LICENSE file. | |||||
package xorm | package xorm | ||||
import ( | import ( | ||||
"reflect" | "reflect" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
var ( | var ( |
package xorm | package xorm | ||||
import ( | import ( | ||||
"context" | |||||
"fmt" | "fmt" | ||||
"os" | "os" | ||||
"reflect" | "reflect" | ||||
"sync" | "sync" | ||||
"time" | "time" | ||||
"github.com/go-xorm/core" | |||||
"xorm.io/core" | |||||
) | ) | ||||
const ( | const ( | ||||
} | } | ||||
engine := &Engine{ | engine := &Engine{ | ||||
db: db, | |||||
dialect: dialect, | |||||
Tables: make(map[reflect.Type]*core.Table), | |||||
mutex: &sync.RWMutex{}, | |||||
TagIdentifier: "xorm", | |||||
TZLocation: time.Local, | |||||
tagHandlers: defaultTagHandlers, | |||||
cachers: make(map[string]core.Cacher), | |||||
db: db, | |||||
dialect: dialect, | |||||
Tables: make(map[reflect.Type]*core.Table), | |||||
mutex: &sync.RWMutex{}, | |||||
TagIdentifier: "xorm", | |||||
TZLocation: time.Local, | |||||
tagHandlers: defaultTagHandlers, | |||||
cachers: make(map[string]core.Cacher), | |||||
defaultContext: context.Background(), | |||||
} | } | ||||
if uri.DbType == core.SQLITE { | if uri.DbType == core.SQLITE { |
// license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||
// +build !appengine | // +build !appengine | ||||
// +build go1.7 | |||||
package internal | package internal | ||||
flushes++ | flushes++ | ||||
} | } | ||||
c.pendingLogs.Unlock() | c.pendingLogs.Unlock() | ||||
go c.flushLog(false) | |||||
flushed := make(chan struct{}) | |||||
go func() { | |||||
defer close(flushed) | |||||
// Force a log flush, because with very short requests we | |||||
// may not ever flush logs. | |||||
c.flushLog(true) | |||||
}() | |||||
w.Header().Set(logFlushHeader, strconv.Itoa(flushes)) | w.Header().Set(logFlushHeader, strconv.Itoa(flushes)) | ||||
// Avoid nil Write call if c.Write is never called. | // Avoid nil Write call if c.Write is never called. | ||||
if c.outBody != nil { | if c.outBody != nil { | ||||
w.Write(c.outBody) | w.Write(c.outBody) | ||||
} | } | ||||
// Wait for the last flush to complete before returning, | |||||
// otherwise the security ticket will not be valid. | |||||
<-flushed | |||||
} | } | ||||
func executeRequestSafely(c *context, r *http.Request) { | func executeRequestSafely(c *context, r *http.Request) { | ||||
Level: &level, | Level: &level, | ||||
Message: &s, | Message: &s, | ||||
}) | }) | ||||
log.Print(logLevelName[level] + ": " + s) | |||||
// Only duplicate log to stderr if not running on App Engine second generation | |||||
if !IsSecondGen() { | |||||
log.Print(logLevelName[level] + ": " + s) | |||||
} | |||||
} | } | ||||
// flushLog attempts to flush any pending logs to the appserver. | // flushLog attempts to flush any pending logs to the appserver. |
// Copyright 2011 Google Inc. All rights reserved. | |||||
// Use of this source code is governed by the Apache 2.0 | |||||
// license that can be found in the LICENSE file. | |||||
// +build !appengine | |||||
// +build !go1.7 | |||||
package internal | |||||
import ( | |||||
"bytes" | |||||
"errors" | |||||
"fmt" | |||||
"io/ioutil" | |||||
"log" | |||||
"net" | |||||
"net/http" | |||||
"net/url" | |||||
"os" | |||||
"runtime" | |||||
"strconv" | |||||
"strings" | |||||
"sync" | |||||
"sync/atomic" | |||||
"time" | |||||
"github.com/golang/protobuf/proto" | |||||
netcontext "golang.org/x/net/context" | |||||
basepb "google.golang.org/appengine/internal/base" | |||||
logpb "google.golang.org/appengine/internal/log" | |||||
remotepb "google.golang.org/appengine/internal/remote_api" | |||||
) | |||||
const ( | |||||
apiPath = "/rpc_http" | |||||
defaultTicketSuffix = "/default.20150612t184001.0" | |||||
) | |||||
var ( | |||||
// Incoming headers. | |||||
ticketHeader = http.CanonicalHeaderKey("X-AppEngine-API-Ticket") | |||||
dapperHeader = http.CanonicalHeaderKey("X-Google-DapperTraceInfo") | |||||
traceHeader = http.CanonicalHeaderKey("X-Cloud-Trace-Context") | |||||
curNamespaceHeader = http.CanonicalHeaderKey("X-AppEngine-Current-Namespace") | |||||
userIPHeader = http.CanonicalHeaderKey("X-AppEngine-User-IP") | |||||
remoteAddrHeader = http.CanonicalHeaderKey("X-AppEngine-Remote-Addr") | |||||
// Outgoing headers. | |||||
apiEndpointHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Endpoint") | |||||
apiEndpointHeaderValue = []string{"app-engine-apis"} | |||||
apiMethodHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Method") | |||||
apiMethodHeaderValue = []string{"/VMRemoteAPI.CallRemoteAPI"} | |||||
apiDeadlineHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Deadline") | |||||
apiContentType = http.CanonicalHeaderKey("Content-Type") | |||||
apiContentTypeValue = []string{"application/octet-stream"} | |||||
logFlushHeader = http.CanonicalHeaderKey("X-AppEngine-Log-Flush-Count") | |||||
apiHTTPClient = &http.Client{ | |||||
Transport: &http.Transport{ | |||||
Proxy: http.ProxyFromEnvironment, | |||||
Dial: limitDial, | |||||
}, | |||||
} | |||||
defaultTicketOnce sync.Once | |||||
defaultTicket string | |||||
) | |||||
func apiURL() *url.URL { | |||||
host, port := "appengine.googleapis.internal", "10001" | |||||
if h := os.Getenv("API_HOST"); h != "" { | |||||
host = h | |||||
} | |||||
if p := os.Getenv("API_PORT"); p != "" { | |||||
port = p | |||||
} | |||||
return &url.URL{ | |||||
Scheme: "http", | |||||
Host: host + ":" + port, | |||||
Path: apiPath, | |||||
} | |||||
} | |||||
func handleHTTP(w http.ResponseWriter, r *http.Request) { | |||||
c := &context{ | |||||
req: r, | |||||
outHeader: w.Header(), | |||||
apiURL: apiURL(), | |||||
} | |||||
stopFlushing := make(chan int) | |||||
ctxs.Lock() | |||||
ctxs.m[r] = c | |||||
ctxs.Unlock() | |||||
defer func() { | |||||
ctxs.Lock() | |||||
delete(ctxs.m, r) | |||||
ctxs.Unlock() | |||||
}() | |||||
// Patch up RemoteAddr so it looks reasonable. | |||||
if addr := r.Header.Get(userIPHeader); addr != "" { | |||||
r.RemoteAddr = addr | |||||
} else if addr = r.Header.Get(remoteAddrHeader); addr != "" { | |||||
r.RemoteAddr = addr | |||||
} else { | |||||
// Should not normally reach here, but pick a sensible default anyway. | |||||
r.RemoteAddr = "127.0.0.1" | |||||
} | |||||
// The address in the headers will most likely be of these forms: | |||||
// 123.123.123.123 | |||||
// 2001:db8::1 | |||||
// net/http.Request.RemoteAddr is specified to be in "IP:port" form. | |||||
if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil { | |||||
// Assume the remote address is only a host; add a default port. | |||||
r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80") | |||||
} | |||||
// Start goroutine responsible for flushing app logs. | |||||
// This is done after adding c to ctx.m (and stopped before removing it) | |||||
// because flushing logs requires making an API call. | |||||
go c.logFlusher(stopFlushing) | |||||
executeRequestSafely(c, r) | |||||
c.outHeader = nil // make sure header changes aren't respected any more | |||||
stopFlushing <- 1 // any logging beyond this point will be dropped | |||||
// Flush any pending logs asynchronously. | |||||
c.pendingLogs.Lock() | |||||
flushes := c.pendingLogs.flushes | |||||
if len(c.pendingLogs.lines) > 0 { | |||||
flushes++ | |||||
} | |||||
c.pendingLogs.Unlock() | |||||
go c.flushLog(false) | |||||
w.Header().Set(logFlushHeader, strconv.Itoa(flushes)) | |||||
// Avoid nil Write call if c.Write is never called. | |||||
if c.outCode != 0 { | |||||
w.WriteHeader(c.outCode) | |||||
} | |||||
if c.outBody != nil { | |||||
w.Write(c.outBody) | |||||
} | |||||
} | |||||
func executeRequestSafely(c *context, r *http.Request) { | |||||
defer func() { | |||||
if x := recover(); x != nil { | |||||
logf(c, 4, "%s", renderPanic(x)) // 4 == critical | |||||
c.outCode = 500 | |||||
} | |||||
}() | |||||
http.DefaultServeMux.ServeHTTP(c, r) | |||||
} | |||||
func renderPanic(x interface{}) string { | |||||
buf := make([]byte, 16<<10) // 16 KB should be plenty | |||||
buf = buf[:runtime.Stack(buf, false)] | |||||
// Remove the first few stack frames: | |||||
// this func | |||||
// the recover closure in the caller | |||||
// That will root the stack trace at the site of the panic. | |||||
const ( | |||||
skipStart = "internal.renderPanic" | |||||
skipFrames = 2 | |||||
) | |||||
start := bytes.Index(buf, []byte(skipStart)) | |||||
p := start | |||||
for i := 0; i < skipFrames*2 && p+1 < len(buf); i++ { | |||||
p = bytes.IndexByte(buf[p+1:], '\n') + p + 1 | |||||
if p < 0 { | |||||
break | |||||
} | |||||
} | |||||
if p >= 0 { | |||||
// buf[start:p+1] is the block to remove. | |||||
// Copy buf[p+1:] over buf[start:] and shrink buf. | |||||
copy(buf[start:], buf[p+1:]) | |||||
buf = buf[:len(buf)-(p+1-start)] | |||||
} | |||||
// Add panic heading. | |||||
head := fmt.Sprintf("panic: %v\n\n", x) | |||||
if len(head) > len(buf) { | |||||
// Extremely unlikely to happen. | |||||
return head | |||||
} | |||||
copy(buf[len(head):], buf) | |||||
copy(buf, head) | |||||
return string(buf) | |||||
} | |||||
var ctxs = struct { | |||||
sync.Mutex | |||||
m map[*http.Request]*context | |||||
bg *context // background context, lazily initialized | |||||
// dec is used by tests to decorate the netcontext.Context returned | |||||
// for a given request. This allows tests to add overrides (such as | |||||
// WithAppIDOverride) to the context. The map is nil outside tests. | |||||
dec map[*http.Request]func(netcontext.Context) netcontext.Context | |||||
}{ | |||||
m: make(map[*http.Request]*context), | |||||
} | |||||
// context represents the context of an in-flight HTTP request. | |||||
// It implements the appengine.Context and http.ResponseWriter interfaces. | |||||
type context struct { | |||||
req *http.Request | |||||
outCode int | |||||
outHeader http.Header | |||||
outBody []byte | |||||
pendingLogs struct { | |||||
sync.Mutex | |||||
lines []*logpb.UserAppLogLine | |||||
flushes int | |||||
} | |||||
apiURL *url.URL | |||||
} | |||||
var contextKey = "holds a *context" | |||||
// fromContext returns the App Engine context or nil if ctx is not | |||||
// derived from an App Engine context. | |||||
func fromContext(ctx netcontext.Context) *context { | |||||
c, _ := ctx.Value(&contextKey).(*context) | |||||
return c | |||||
} | |||||
func withContext(parent netcontext.Context, c *context) netcontext.Context { | |||||
ctx := netcontext.WithValue(parent, &contextKey, c) | |||||
if ns := c.req.Header.Get(curNamespaceHeader); ns != "" { | |||||
ctx = withNamespace(ctx, ns) | |||||
} | |||||
return ctx | |||||
} | |||||
func toContext(c *context) netcontext.Context { | |||||
return withContext(netcontext.Background(), c) | |||||
} | |||||
func IncomingHeaders(ctx netcontext.Context) http.Header { | |||||
if c := fromContext(ctx); c != nil { | |||||
return c.req.Header | |||||
} | |||||
return nil | |||||
} | |||||
func ReqContext(req *http.Request) netcontext.Context { | |||||
return WithContext(netcontext.Background(), req) | |||||
} | |||||
func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context { | |||||
ctxs.Lock() | |||||
c := ctxs.m[req] | |||||
d := ctxs.dec[req] | |||||
ctxs.Unlock() | |||||
if d != nil { | |||||
parent = d(parent) | |||||
} | |||||
if c == nil { | |||||
// Someone passed in an http.Request that is not in-flight. | |||||
// We panic here rather than panicking at a later point | |||||
// so that stack traces will be more sensible. | |||||
log.Panic("appengine: NewContext passed an unknown http.Request") | |||||
} | |||||
return withContext(parent, c) | |||||
} | |||||
// DefaultTicket returns a ticket used for background context or dev_appserver. | |||||
func DefaultTicket() string { | |||||
defaultTicketOnce.Do(func() { | |||||
if IsDevAppServer() { | |||||
defaultTicket = "testapp" + defaultTicketSuffix | |||||
return | |||||
} | |||||
appID := partitionlessAppID() | |||||
escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1) | |||||
majVersion := VersionID(nil) | |||||
if i := strings.Index(majVersion, "."); i > 0 { | |||||
majVersion = majVersion[:i] | |||||
} | |||||
defaultTicket = fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(nil), majVersion, InstanceID()) | |||||
}) | |||||
return defaultTicket | |||||
} | |||||
func BackgroundContext() netcontext.Context { | |||||
ctxs.Lock() | |||||
defer ctxs.Unlock() | |||||
if ctxs.bg != nil { | |||||
return toContext(ctxs.bg) | |||||
} | |||||
// Compute background security ticket. | |||||
ticket := DefaultTicket() | |||||
ctxs.bg = &context{ | |||||
req: &http.Request{ | |||||
Header: http.Header{ | |||||
ticketHeader: []string{ticket}, | |||||
}, | |||||
}, | |||||
apiURL: apiURL(), | |||||
} | |||||
// TODO(dsymonds): Wire up the shutdown handler to do a final flush. | |||||
go ctxs.bg.logFlusher(make(chan int)) | |||||
return toContext(ctxs.bg) | |||||
} | |||||
// RegisterTestRequest registers the HTTP request req for testing, such that | |||||
// any API calls are sent to the provided URL. It returns a closure to delete | |||||
// the registration. | |||||
// It should only be used by aetest package. | |||||
func RegisterTestRequest(req *http.Request, apiURL *url.URL, decorate func(netcontext.Context) netcontext.Context) (*http.Request, func()) { | |||||
c := &context{ | |||||
req: req, | |||||
apiURL: apiURL, | |||||
} | |||||
ctxs.Lock() | |||||
defer ctxs.Unlock() | |||||
if _, ok := ctxs.m[req]; ok { | |||||
log.Panic("req already associated with context") | |||||
} | |||||
if _, ok := ctxs.dec[req]; ok { | |||||
log.Panic("req already associated with context") | |||||
} | |||||
if ctxs.dec == nil { | |||||
ctxs.dec = make(map[*http.Request]func(netcontext.Context) netcontext.Context) | |||||
} | |||||
ctxs.m[req] = c | |||||
ctxs.dec[req] = decorate | |||||
return req, func() { | |||||
ctxs.Lock() | |||||
delete(ctxs.m, req) | |||||
delete(ctxs.dec, req) | |||||
ctxs.Unlock() | |||||
} | |||||
} | |||||
var errTimeout = &CallError{ | |||||
Detail: "Deadline exceeded", | |||||
Code: int32(remotepb.RpcError_CANCELLED), | |||||
Timeout: true, | |||||
} | |||||
func (c *context) Header() http.Header { return c.outHeader } | |||||
// Copied from $GOROOT/src/pkg/net/http/transfer.go. Some response status | |||||
// codes do not permit a response body (nor response entity headers such as | |||||
// Content-Length, Content-Type, etc). | |||||
func bodyAllowedForStatus(status int) bool { | |||||
switch { | |||||
case status >= 100 && status <= 199: | |||||
return false | |||||
case status == 204: | |||||
return false | |||||
case status == 304: | |||||
return false | |||||
} | |||||
return true | |||||
} | |||||
func (c *context) Write(b []byte) (int, error) { | |||||
if c.outCode == 0 { | |||||
c.WriteHeader(http.StatusOK) | |||||
} | |||||
if len(b) > 0 && !bodyAllowedForStatus(c.outCode) { | |||||
return 0, http.ErrBodyNotAllowed | |||||
} | |||||
c.outBody = append(c.outBody, b...) | |||||
return len(b), nil | |||||
} | |||||
func (c *context) WriteHeader(code int) { | |||||
if c.outCode != 0 { | |||||
logf(c, 3, "WriteHeader called multiple times on request.") // error level | |||||
return | |||||
} | |||||
c.outCode = code | |||||
} | |||||
func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error) { | |||||
hreq := &http.Request{ | |||||
Method: "POST", | |||||
URL: c.apiURL, | |||||
Header: http.Header{ | |||||
apiEndpointHeader: apiEndpointHeaderValue, | |||||
apiMethodHeader: apiMethodHeaderValue, | |||||
apiContentType: apiContentTypeValue, | |||||
apiDeadlineHeader: []string{strconv.FormatFloat(timeout.Seconds(), 'f', -1, 64)}, | |||||
}, | |||||
Body: ioutil.NopCloser(bytes.NewReader(body)), | |||||
ContentLength: int64(len(body)), | |||||
Host: c.apiURL.Host, | |||||
} | |||||
if info := c.req.Header.Get(dapperHeader); info != "" { | |||||
hreq.Header.Set(dapperHeader, info) | |||||
} | |||||
if info := c.req.Header.Get(traceHeader); info != "" { | |||||
hreq.Header.Set(traceHeader, info) | |||||
} | |||||
tr := apiHTTPClient.Transport.(*http.Transport) | |||||
var timedOut int32 // atomic; set to 1 if timed out | |||||
t := time.AfterFunc(timeout, func() { | |||||
atomic.StoreInt32(&timedOut, 1) | |||||
tr.CancelRequest(hreq) | |||||
}) | |||||
defer t.Stop() | |||||
defer func() { | |||||
// Check if timeout was exceeded. | |||||
if atomic.LoadInt32(&timedOut) != 0 { | |||||
err = errTimeout | |||||
} | |||||
}() | |||||
hresp, err := apiHTTPClient.Do(hreq) | |||||
if err != nil { | |||||
return nil, &CallError{ | |||||
Detail: fmt.Sprintf("service bridge HTTP failed: %v", err), | |||||
Code: int32(remotepb.RpcError_UNKNOWN), | |||||
} | |||||
} | |||||
defer hresp.Body.Close() | |||||
hrespBody, err := ioutil.ReadAll(hresp.Body) | |||||
if hresp.StatusCode != 200 { | |||||
return nil, &CallError{ | |||||
Detail: fmt.Sprintf("service bridge returned HTTP %d (%q)", hresp.StatusCode, hrespBody), | |||||
Code: int32(remotepb.RpcError_UNKNOWN), | |||||
} | |||||
} | |||||
if err != nil { | |||||
return nil, &CallError{ | |||||
Detail: fmt.Sprintf("service bridge response bad: %v", err), | |||||
Code: int32(remotepb.RpcError_UNKNOWN), | |||||
} | |||||
} | |||||
return hrespBody, nil | |||||
} | |||||
func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error { | |||||
if ns := NamespaceFromContext(ctx); ns != "" { | |||||
if fn, ok := NamespaceMods[service]; ok { | |||||
fn(in, ns) | |||||
} | |||||
} | |||||
if f, ctx, ok := callOverrideFromContext(ctx); ok { | |||||
return f(ctx, service, method, in, out) | |||||
} | |||||
// Handle already-done contexts quickly. | |||||
select { | |||||
case <-ctx.Done(): | |||||
return ctx.Err() | |||||
default: | |||||
} | |||||
c := fromContext(ctx) | |||||
if c == nil { | |||||
// Give a good error message rather than a panic lower down. | |||||
return errNotAppEngineContext | |||||
} | |||||
// Apply transaction modifications if we're in a transaction. | |||||
if t := transactionFromContext(ctx); t != nil { | |||||
if t.finished { | |||||
return errors.New("transaction context has expired") | |||||
} | |||||
applyTransaction(in, &t.transaction) | |||||
} | |||||
// Default RPC timeout is 60s. | |||||
timeout := 60 * time.Second | |||||
if deadline, ok := ctx.Deadline(); ok { | |||||
timeout = deadline.Sub(time.Now()) | |||||
} | |||||
data, err := proto.Marshal(in) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
ticket := c.req.Header.Get(ticketHeader) | |||||
// Use a test ticket under test environment. | |||||
if ticket == "" { | |||||
if appid := ctx.Value(&appIDOverrideKey); appid != nil { | |||||
ticket = appid.(string) + defaultTicketSuffix | |||||
} | |||||
} | |||||
// Fall back to use background ticket when the request ticket is not available in Flex or dev_appserver. | |||||
if ticket == "" { | |||||
ticket = DefaultTicket() | |||||
} | |||||
req := &remotepb.Request{ | |||||
ServiceName: &service, | |||||
Method: &method, | |||||
Request: data, | |||||
RequestId: &ticket, | |||||
} | |||||
hreqBody, err := proto.Marshal(req) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
hrespBody, err := c.post(hreqBody, timeout) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
res := &remotepb.Response{} | |||||
if err := proto.Unmarshal(hrespBody, res); err != nil { | |||||
return err | |||||
} | |||||
if res.RpcError != nil { | |||||
ce := &CallError{ | |||||
Detail: res.RpcError.GetDetail(), | |||||
Code: *res.RpcError.Code, | |||||
} | |||||
switch remotepb.RpcError_ErrorCode(ce.Code) { | |||||
case remotepb.RpcError_CANCELLED, remotepb.RpcError_DEADLINE_EXCEEDED: | |||||
ce.Timeout = true | |||||
} | |||||
return ce | |||||
} | |||||
if res.ApplicationError != nil { | |||||
return &APIError{ | |||||
Service: *req.ServiceName, | |||||
Detail: res.ApplicationError.GetDetail(), | |||||
Code: *res.ApplicationError.Code, | |||||
} | |||||
} | |||||
if res.Exception != nil || res.JavaException != nil { | |||||
// This shouldn't happen, but let's be defensive. | |||||
return &CallError{ | |||||
Detail: "service bridge returned exception", | |||||
Code: int32(remotepb.RpcError_UNKNOWN), | |||||
} | |||||
} | |||||
return proto.Unmarshal(res.Response, out) | |||||
} | |||||
func (c *context) Request() *http.Request { | |||||
return c.req | |||||
} | |||||
func (c *context) addLogLine(ll *logpb.UserAppLogLine) { | |||||
// Truncate long log lines. | |||||
// TODO(dsymonds): Check if this is still necessary. | |||||
const lim = 8 << 10 | |||||
if len(*ll.Message) > lim { | |||||
suffix := fmt.Sprintf("...(length %d)", len(*ll.Message)) | |||||
ll.Message = proto.String((*ll.Message)[:lim-len(suffix)] + suffix) | |||||
} | |||||
c.pendingLogs.Lock() | |||||
c.pendingLogs.lines = append(c.pendingLogs.lines, ll) | |||||
c.pendingLogs.Unlock() | |||||
} | |||||
var logLevelName = map[int64]string{ | |||||
0: "DEBUG", | |||||
1: "INFO", | |||||
2: "WARNING", | |||||
3: "ERROR", | |||||
4: "CRITICAL", | |||||
} | |||||
func logf(c *context, level int64, format string, args ...interface{}) { | |||||
if c == nil { | |||||
panic("not an App Engine context") | |||||
} | |||||
s := fmt.Sprintf(format, args...) | |||||
s = strings.TrimRight(s, "\n") // Remove any trailing newline characters. | |||||
c.addLogLine(&logpb.UserAppLogLine{ | |||||
TimestampUsec: proto.Int64(time.Now().UnixNano() / 1e3), | |||||
Level: &level, | |||||
Message: &s, | |||||
}) | |||||
log.Print(logLevelName[level] + ": " + s) | |||||
} | |||||
// flushLog attempts to flush any pending logs to the appserver. | |||||
// It should not be called concurrently. | |||||
func (c *context) flushLog(force bool) (flushed bool) { | |||||
c.pendingLogs.Lock() | |||||
// Grab up to 30 MB. We can get away with up to 32 MB, but let's be cautious. | |||||
n, rem := 0, 30<<20 | |||||
for ; n < len(c.pendingLogs.lines); n++ { | |||||
ll := c.pendingLogs.lines[n] | |||||
// Each log line will require about 3 bytes of overhead. | |||||
nb := proto.Size(ll) + 3 | |||||
if nb > rem { | |||||
break | |||||
} | |||||
rem -= nb | |||||
} | |||||
lines := c.pendingLogs.lines[:n] | |||||
c.pendingLogs.lines = c.pendingLogs.lines[n:] | |||||
c.pendingLogs.Unlock() | |||||
if len(lines) == 0 && !force { | |||||
// Nothing to flush. | |||||
return false | |||||
} | |||||
rescueLogs := false | |||||
defer func() { | |||||
if rescueLogs { | |||||
c.pendingLogs.Lock() | |||||
c.pendingLogs.lines = append(lines, c.pendingLogs.lines...) | |||||
c.pendingLogs.Unlock() | |||||
} | |||||
}() | |||||
buf, err := proto.Marshal(&logpb.UserAppLogGroup{ | |||||
LogLine: lines, | |||||
}) | |||||
if err != nil { | |||||
log.Printf("internal.flushLog: marshaling UserAppLogGroup: %v", err) | |||||
rescueLogs = true | |||||
return false | |||||
} | |||||
req := &logpb.FlushRequest{ | |||||
Logs: buf, | |||||
} | |||||
res := &basepb.VoidProto{} | |||||
c.pendingLogs.Lock() | |||||
c.pendingLogs.flushes++ | |||||
c.pendingLogs.Unlock() | |||||
if err := Call(toContext(c), "logservice", "Flush", req, res); err != nil { | |||||
log.Printf("internal.flushLog: Flush RPC: %v", err) | |||||
rescueLogs = true | |||||
return false | |||||
} | |||||
return true | |||||
} | |||||
const ( | |||||
// Log flushing parameters. | |||||
flushInterval = 1 * time.Second | |||||
forceFlushInterval = 60 * time.Second | |||||
) | |||||
func (c *context) logFlusher(stop <-chan int) { | |||||
lastFlush := time.Now() | |||||
tick := time.NewTicker(flushInterval) | |||||
for { | |||||
select { | |||||
case <-stop: | |||||
// Request finished. | |||||
tick.Stop() | |||||
return | |||||
case <-tick.C: | |||||
force := time.Now().Sub(lastFlush) > forceFlushInterval | |||||
if c.flushLog(force) { | |||||
lastFlush = time.Now() | |||||
} | |||||
} | |||||
} | |||||
} | |||||
func ContextForTesting(req *http.Request) netcontext.Context { | |||||
return toContext(&context{req: req}) | |||||
} |
package internal | package internal | ||||
import netcontext "golang.org/x/net/context" | |||||
import ( | |||||
"os" | |||||
// These functions are implementations of the wrapper functions | |||||
// in ../appengine/identity.go. See that file for commentary. | |||||
netcontext "golang.org/x/net/context" | |||||
) | |||||
var ( | |||||
// This is set to true in identity_classic.go, which is behind the appengine build tag. | |||||
// The appengine build tag is set for the first generation runtimes (<= Go 1.9) but not | |||||
// the second generation runtimes (>= Go 1.11), so this indicates whether we're on a | |||||
// first-gen runtime. See IsStandard below for the second-gen check. | |||||
appengineStandard bool | |||||
// This is set to true in identity_flex.go, which is behind the appenginevm build tag. | |||||
appengineFlex bool | |||||
) | |||||
// AppID is the implementation of the wrapper function of the same name in | |||||
// ../identity.go. See that file for commentary. | |||||
func AppID(c netcontext.Context) string { | func AppID(c netcontext.Context) string { | ||||
return appID(FullyQualifiedAppID(c)) | return appID(FullyQualifiedAppID(c)) | ||||
} | } | ||||
// IsStandard is the implementation of the wrapper function of the same name in | |||||
// ../appengine.go. See that file for commentary. | |||||
func IsStandard() bool { | |||||
// appengineStandard will be true for first-gen runtimes (<= Go 1.9) but not | |||||
// second-gen (>= Go 1.11). | |||||
return appengineStandard || IsSecondGen() | |||||
} | |||||
// IsStandard is the implementation of the wrapper function of the same name in | |||||
// ../appengine.go. See that file for commentary. | |||||
func IsSecondGen() bool { | |||||
// Second-gen runtimes set $GAE_ENV so we use that to check if we're on a second-gen runtime. | |||||
return os.Getenv("GAE_ENV") == "standard" | |||||
} | |||||
// IsFlex is the implementation of the wrapper function of the same name in | |||||
// ../appengine.go. See that file for commentary. | |||||
func IsFlex() bool { | |||||
return appengineFlex | |||||
} | |||||
// IsAppEngine is the implementation of the wrapper function of the same name in | |||||
// ../appengine.go. See that file for commentary. | |||||
func IsAppEngine() bool { | |||||
return IsStandard() || IsFlex() | |||||
} |
netcontext "golang.org/x/net/context" | netcontext "golang.org/x/net/context" | ||||
) | ) | ||||
func init() { | |||||
appengineStandard = true | |||||
} | |||||
func DefaultVersionHostname(ctx netcontext.Context) string { | func DefaultVersionHostname(ctx netcontext.Context) string { | ||||
c := fromContext(ctx) | c := fromContext(ctx) | ||||
if c == nil { | if c == nil { |
// Copyright 2018 Google LLC. All rights reserved. | |||||
// Use of this source code is governed by the Apache 2.0 | |||||
// license that can be found in the LICENSE file. | |||||
// +build appenginevm | |||||
package internal | |||||
func init() { | |||||
appengineFlex = true | |||||
} |
) | ) | ||||
func Main() { | func Main() { | ||||
MainPath = "" | |||||
appengine_internal.Main() | appengine_internal.Main() | ||||
} | } |