!2270 移除go driver test目录

Merge pull request !2270 from 杨皓/master
This commit is contained in:
opengauss-bot
2022-09-29 12:39:47 +00:00
committed by Gitee
249 changed files with 0 additions and 50547 deletions

View File

@ -1,17 +0,0 @@
This directory is using for go driver test
such way:
make gocheck p=8000 -sj
Install db information:
datapath : current_dir/data
inituser: current os login user
inituser password: Gauss_234
default databse: postgres
install port: default 5432
support parameter:
p: install db port (default 5432)
u: go driver login user (default gauss)
w: go driver login user password (default Gauss_234)
h: go driver login IP address (default 127.0.0.1)
d: go driver login user database (default gauss)

View File

@ -1,20 +0,0 @@
#!/bin/bash
GO_DRIVRR_DIR=$work_dir/src/gitee.com/opengauss
GO_DRIVRR_PATH=$GO_DRIVRR_DIR/openGauss-connector-go-pq
DEVELOP_BRANCH=huawei/csi/gaussdb_kernel/v1.0.2
REPOSITORY=https://codehub-dg-y.huawei.com/OpenSourceCenter/openGauss-connector-go-pq.git
#prepare code
if [ ! -d ${GO_DRIVRR_PATH} ]; then
git config --global http.sslVerify false
mkdir -p $GO_DRIVRR_DIR
cd $GO_DRIVRR_DIR
git clone $REPOSITORY
cd $GO_DRIVRR_PATH
git reset --hard remotes/origin/${DEVELOP_BRANCH}
else
cd $GO_DRIVRR_PATH
git remote set-url origin $REPOSITORY
git remote update origin
git reset --hard remotes/origin/${DEVELOP_BRANCH}
fi

View File

@ -1,251 +0,0 @@
#!/bin/bash
export initu_p="Gauss@234"
work_dir=`dirname $0`
export work_dir=`realpath $work_dir`
echo "work_dir : " $work_dir
echo "######## download go test code ######"
sh ${work_dir}/download_godriver.sh
##set go test connection info
port=5432
user=gauss
passwd=Gauss_234
database=gauss
hostip=127.0.0.1
echo "######## parsing go test args begin ######" $@
for arg in $@
do
val=${arg#*=}
key=${arg%=*}
echo "key="$key" val="$val" vallen="${#val}
if [ x"${#val}" != x"0" ]; then
case ${key} in
i) hostip=$val
;;
p) port=$val
;;
w) passwd=$val
;;
d) database=$val
;;
u) user=$val
;;
*) echo "not support this arg: " $arg
;;
esac
fi
done
echo "host ip = "$hostip
echo "port = "$port
echo "go running user = "$user
echo "go running user password = "$passwd
echo "go running user database = "$database
echo "######## parsing go test args end ######"
path_netstat=`which netstat`
if [ -x $path_netstat ]; then
port_check=`netstat -t | awk '{print $4}' | grep $port | wc -l`
if [ ! x"$port_check" == x"0" ]; then
echo "your input port=$port is being used, please try another port."
exit 1
else
echo "port $port is OK."
fi
fi
echo "######## modify go config begin ######"
go_config_filtpath=$work_dir/src/github.com/GaussKernel/property.ini
rm -rf ${go_config_filtpath}.bak
cp $go_config_filtpath ${go_config_filtpath}.bak
sed -i "s/^host=.*/host=${hostip}/g" $go_config_filtpath
sed -i "s/^port=.*/port=${port}/g" $go_config_filtpath
sed -i "s/^password=.*/password=${passwd}/g" $go_config_filtpath
sed -i "s/^dbname=.*/dbname=${database}/g" $go_config_filtpath
sed -i "s/^user=.*/user=${user}/g" $go_config_filtpath
# echo "port=$port" >> $go_config_filtpath
# echo "password=$passwd" >> $go_config_filtpath
# echo "dbname=$database" >> $go_config_filtpath
# echo "user=$user" >> $go_config_filtpath
# echo "host=$hostip" >> $go_config_filtpath
echo "######## modify go config end ######"
echo "######## installing gaussdb begin ######"
command_found=`echo $PATH | grep gaussdb | wc -l`
if [ x$command_found == x"0" ]; then
exp PATH=${PREFIX_HOME}/bin:$portATH
fi
#set install db info
export datapath=$work_dir/data
export initpasswd=Gauss_234
echo "install db path = "$datapath
export ssl_file_dir=$work_dir/src/github.com/GaussKernel/certs
function check_res()
{
if [ $1 -ne 0 ]; then
echo "process error. please check"
exit 1
fi
}
function stop_db_running()
{
is_running=`gs_ctl status -D $database | grep "no server running" | wc -l`
if [ x"$is_running" != x"1" ]; then
gs_ctl stop -D $datapath > /dev/null 2>&1
fi
for pid in `ps -ux | grep $datapath | grep -v grep | awk '{print $2}'`
do
kill -9 $pid
done
}
function prepare_ssl()
{
cp ${ssl_file_dir}/server.crt $datapath
cp ${ssl_file_dir}/server.key $datapath
cp ${ssl_file_dir}/root.crt $datapath
chmod 0600 $datapath/server.crt
chmod 0600 $datapath/server.key
chmod 0600 $datapath/root.crt
rm -rf ${HOME}/.postgresql
cp -r $ssl_file_dir ${HOME}
mv ${HOME}/certs ${HOME}/.postgresql
export PGSSLKEY=${HOME}/.postgresql/postgresql.key
export PGSSLCERT=${HOME}/.postgresql/postgresql.crt
export PGSSLROOTCERT=${HOME}/.postgresql/root.crt
chmod 0600 $PGSSLKEY
chmod 0600 $PGSSLCERT
chmod 0600 $PGSSLROOTCERT
}
function modify_param(){
gs_guc set -Z datanode -D $datapath -c "ssl = on"
gs_guc set -Z datanode -D $datapath -c "ssl_cert_file = 'server.crt'"
gs_guc set -Z datanode -D $datapath -c "ssl_key_file = 'server.key'"
gs_guc set -Z datanode -D $datapath -c "ssl_ca_file = 'root.crt'"
gs_guc set -Z datanode -D $datapath -c "logging_collector = 'on'"
gs_guc set -Z datanode -D $datapath -c "log_connections = 'on'"
gs_guc set -Z datanode -D $datapath -c "log_disconnections = 'on'"
gs_guc set -Z datanode -D $datapath -c "log_statement = 'ddl'"
gs_guc set -Z datanode -D $datapath -c "log_rotation_age = 30d"
gs_guc set -Z datanode -D $datapath -c "log_rotation_size = 100MB"
gs_guc set -Z datanode -D $datapath -c "log_duration = 'on'"
gs_guc set -Z datanode -D $datapath -c "log_lock_waits = 'on'"
gs_guc set -Z datanode -D $datapath -c "listen_addresses = '*'"
}
function modify_hba()
{
echo "local all all trust" >> $datapath/pg_hba.conf
echo "host all all 0.0.0.0/0 sha256" >> $datapath/pg_hba.conf
echo "hostssl all all 0.0.0.0/0 sha256" >> $datapath/pg_hba.conf
}
stop_db_running
rm -rf $datapath
gs_initdb -D $datapath --nodename=datanode -w $initpasswd -A trust
check_res $?
echo "port=$port" >>$datapath/postgresql.conf
gs_ctl start -D $datapath -Z single_node -l logfile
check_res $?
prepare_ssl
modify_param
modify_hba
gs_ctl restart -D $datapath -Z single_node -l logfile
check_res $?
echo "######## installing gaussdb end ######"
echo "######## create db user begin ######"
echo "
CREATE user ${user} WITH PASSWORD '${passwd}';
CREATE database ${database} OWNER ${user};
GRANT ALL PRIVILEGES ON database ${database} to ${user};
" | gsql -d postgres -p ${port}
echo "
create schema ${user};
create table people(id int,name varchar2(200));
insert into people values(10,20);
" | gsql -d ${database} -p ${port} -U ${user} -W ${passwd} -h ${hostip}
echo "######## create db user end (go run scipt with this user)######"
echo "######## running go test begin ######"
function cal_interval_time
{
local t=$(awk 'BEGIN{print '$2' - '$1'}')
local z=${t%.*}
printf "%.2d:%.2d:%.2d" $((z/60/60)) $((z/60%60)) $((z%60))
}
export GO111MODULE=on
export GONOSUMDB="*"
export GOPATH=$work_dir
export GOPROXY=http://cmc.centralrepo.rnd.huawei.com/go,http://mirrors.tools.huawei.com/goproxy
gotest_base=$work_dir/src/github.com/GaussKernel
gotest_base_out=${gotest_base}/go_test_out
gotest_base_expect=${gotest_base}/go_test_expect
gotest_base_diff=${gotest_base}/go_test_diff
if [ ! -d ${gotest_base_out} ];then
mkdir -p ${gotest_base_out}
fi
if [ ! -d ${gotest_base_expect} ];then
mkdir -p ${gotest_base_expect}
fi
if [ ! -d ${gotest_base_diff} ];then
mkdir -p ${gotest_base_diff}
fi
rm -rf ${gotest_base_out}/*
cd ${gotest_base}/go_test
declare -i go_test_files=0
declare -i failed_counts=0
for file in `ls`
do
if [ -f ${file} ] && [ x"${file:0-7}" == x"test.go" ]; then
((go_test_files++))
start_time=$(date +%s.%N)
go test ${file} -c -o ${file}.exec
if [ $? != 0 ]; then
echo "compile file ${file} error."
continue
fi
chmod +x ${file}.exec
./${file}.exec > ${gotest_base_out}/${file}.out 2>&1
diff -w -B -u ${gotest_base_expect}/${file}.out ${gotest_base_out}/${file}.out > ${gotest_base_diff}/${file}.diff
end_time=$(date +%s.%N)
time_cost="$(cal_interval_time ${start_time} ${end_time})"
is_fail=`cat ${gotest_base_diff}/${file}.diff | wc -l`
if [ x"$is_fail" != x"0" ]; then
((failed_counts++))
printf "%-40s : FAIL %s\r\n" ${file} ${time_cost}
else
printf "%-40s : SUCCESS %s\r\n" ${file} ${time_cost}
fi
fi
done
success_counts=`expr $go_test_files - $failed_counts`
echo "Execute go test $go_test_files examples, there are $success_counts success, and $failed_counts failed."
echo "######## running go test end ######"
echo "######## stop db and clean ######"
stop_db_running
echo "######## stop db end ######"
if [ x"$failed_counts" != x"0" ]; then
exit 1
fi

View File

@ -1,37 +0,0 @@
.PHONY: all root-ssl server-ssl client-ssl
# Rebuilds self-signed root/server/client certs/keys in a consistent way
all: root-ssl server-ssl client-ssl
rm -f .srl
root-ssl:
openssl req -new -sha256 -nodes -newkey rsa:2048 \
-config ./certs/root.cnf \
-keyout /tmp/root.key \
-out /tmp/root.csr
openssl x509 -req -days 3653 -sha256 \
-in /tmp/root.csr \
-extfile /etc/ssl/openssl.cnf -extensions v3_ca \
-signkey /tmp/root.key \
-out ./certs/root.crt
server-ssl:
openssl req -new -sha256 -nodes -newkey rsa:2048 \
-config ./certs/server.cnf \
-keyout ./certs/server.key \
-out /tmp/server.csr
openssl x509 -req -days 3653 -sha256 \
-extfile ./certs/server.cnf -extensions req_ext \
-CA ./certs/root.crt -CAkey /tmp/root.key -CAcreateserial \
-in /tmp/server.csr \
-out ./certs/server.crt
client-ssl:
openssl req -new -sha256 -nodes -newkey rsa:2048 \
-config ./certs/postgresql.cnf \
-keyout ./certs/postgresql.key \
-out /tmp/postgresql.csr
openssl x509 -req -days 3653 -sha256 \
-CA ./certs/root.crt -CAkey /tmp/root.key -CAcreateserial \
-in /tmp/postgresql.csr \
-out ./certs/postgresql.crt

View File

@ -1,3 +0,0 @@
This directory contains certificates and private keys for testing some
SSL-related functionality in Travis. Do NOT use these certificates for
anything other than testing.

View File

@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDBjCCAe6gAwIBAgIQSnDYp/Naet9HOZljF5PuwDANBgkqhkiG9w0BAQsFADAr
MRIwEAYDVQQKEwlDb2Nrcm9hY2gxFTATBgNVBAMTDENvY2tyb2FjaCBDQTAeFw0x
NjAyMDcxNjQ0MzdaFw0xNzAyMDYxNjQ0MzdaMCsxEjAQBgNVBAoTCUNvY2tyb2Fj
aDEVMBMGA1UEAxMMQ29ja3JvYWNoIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAxdln3/UdgP7ayA/G1kT7upjLe4ERwQjYQ25q0e1+vgsB5jhiirxJ
e0+WkhhYu/mwoSAXzvlsbZ2PWFyfdanZeD/Lh6SvIeWXVVaPcWVWL1TEcoN2jr5+
E85MMHmbbmaT2he8s6br2tM/UZxyTQ2XRprIzApbDssyw1c0Yufcpu3C6267FLEl
IfcWrzDhnluFhthhtGXv3ToD8IuMScMC5qlKBXtKmD1B5x14ngO/ecNJ+OlEi0HU
mavK4KWgI2rDXRZ2EnCpyTZdkc3kkRnzKcg653oOjMDRZdrhfIrha+Jq38ACsUmZ
Su7Sp5jkIHOCO8Zg+l6GKVSq37dKMapD8wIDAQABoyYwJDAOBgNVHQ8BAf8EBAMC
AuQwEgYDVR0TAQH/BAgwBgEB/wIBATANBgkqhkiG9w0BAQsFAAOCAQEAwZ2Tu0Yu
rrSVdMdoPEjT1IZd+5OhM/SLzL0ddtvTithRweLHsw2lDQYlXFqr24i3UGZJQ1sp
cqSrNwswgLUQT3vWyTjmM51HEb2vMYWKmjZ+sBQYAUP1CadrN/+OTfNGnlF1+B4w
IXOzh7EvQmJJnNybLe4a/aRvj1NE2n8Z898B76SVU9WbfKKz8VwLzuIPDqkKcZda
lMy5yzthyztV9YjcWs2zVOUGZvGdAhDrvZuUq6mSmxrBEvR2LBOggmVf3tGRT+Ls
lW7c9Lrva5zLHuqmoPP07A+vuI9a0D1X44jwGDuPWJ5RnTOQ63Uez12mKNjqleHw
DnkwNanuO8dhAA==
-----END CERTIFICATE-----

View File

@ -1,10 +0,0 @@
[req]
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
C = US
ST = Nevada
L = Las Vegas
O = github.com/lib/pq
CN = pqgosslcert

View File

@ -1,20 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDPjCCAiYCCQD4nsC6zsmIqjANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJV
UzEPMA0GA1UECAwGTmV2YWRhMRIwEAYDVQQHDAlMYXMgVmVnYXMxGjAYBgNVBAoM
EWdpdGh1Yi5jb20vbGliL3BxMQ4wDAYDVQQDDAVwcSBDQTAeFw0yMTA5MDIwMTU1
MDJaFw0zMTA5MDMwMTU1MDJaMGQxCzAJBgNVBAYTAlVTMQ8wDQYDVQQIDAZOZXZh
ZGExEjAQBgNVBAcMCUxhcyBWZWdhczEaMBgGA1UECgwRZ2l0aHViLmNvbS9saWIv
cHExFDASBgNVBAMMC3BxZ29zc2xjZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAx0ucPVUNCrVmbyithwWrmmZ1dGudBwhSyDB6af4z5Cr+S6dx2SRU
UGUw3Lv+z+tUqQ7hJj0oNddIQeYKl/Tt6JPpZsQfERP/cUGedtyt7HnCKobBL+0B
NvHnDIUiIL4LgfiZK4DWJkGmm7nTHo/7qKAw60vCMLUW98DC0Xhlk9MHYG+e9Zai
3G0vY2X6DUYcSmzBI3JakFEgMZTQg3ofUQMz8TYeK3/DYadLXkl08d18LL3Dnefx
0xRuBPNTa2tLfVnFkfFi6Z9xVB/WhG6+X4OLnO85v5xUOGTV+g154iR7FOkrrl5F
lEUBj+yaIoTRi+MyZ/oYqWwQUDYS3+Te9wIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
AQCCJpwUWCx7xfXv3vH3LQcffZycyRHYPgTCbiQw3x9aBb77jUAh5O6lEj/W0nx2
SCTEsCsRSAiFwfUb+g/AFCW84dELRWmf38eoqACebLymqnvxyZA+O87yu07XyFZR
TnmbDMzZgsyWWGwS3JoGFk+ibWY4AImYQnSJO8Pi0kZ37ngbAyJ3RtDhhEQJWw/Q
D04p3uky/ea7Gyz0QTx5o40n4gq7nEzF1OS6IHozM840J5aZrxRiXEa56fsmJHmI
IGyI07SGlWJ15r1wc8lB+8ilnAqH1QQlYzTIW0Q4NZE7n3uQg1EVuueGiGO2ex2/
he9lDiJfOQuPuLbOxzctP9v9
-----END CERTIFICATE-----

View File

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDHS5w9VQ0KtWZv
KK2HBauaZnV0a50HCFLIMHpp/jPkKv5Lp3HZJFRQZTDcu/7P61SpDuEmPSg110hB
5gqX9O3ok+lmxB8RE/9xQZ523K3secIqhsEv7QE28ecMhSIgvguB+JkrgNYmQaab
udMej/uooDDrS8IwtRb3wMLReGWT0wdgb571lqLcbS9jZfoNRhxKbMEjclqQUSAx
lNCDeh9RAzPxNh4rf8Nhp0teSXTx3XwsvcOd5/HTFG4E81Nra0t9WcWR8WLpn3FU
H9aEbr5fg4uc7zm/nFQ4ZNX6DXniJHsU6SuuXkWURQGP7JoihNGL4zJn+hipbBBQ
NhLf5N73AgMBAAECggEAHLNY1sRO0oH5NHzpMI6yfdPPimqM/JxIP6grmOQQ2QUQ
BhkhHiJLOiC4frFcKtk7IfWQmw8noUlVkJfuYp/VOy9B55jK2IzGtqq6hWeWbH3E
Zpdtbtd021LO8VCi75Au3BLPDCLLtEq0Ea0bKEWX+lrHcLtCRf1uR1OtOrlZ94Wl
DUhm7YJC4cS1bi6Kdf03R+fw2oFi7/QdywcT4ow032jGWOly/Jl7bSHZK7xLtM/i
9HfMwmusD/iuz7mtLU7VCpnlKZm6MfS5D427ybW8MruuiZEtQJ6QtRIrHBHk93aK
Op0tjJ6tMav1UsJzgVz9+uWILE9l0AjAa4AvbfNzEQKBgQD8mma9SLQPtBb6cXuT
CQgjE4vyph8mRnm/pTz3QLIpMiLy2+aKJD/u4cduzLw1vjuH1tlb7NQ9c891jAJh
JhwDwqKAXfFicfRs/PYWngx/XtGhbbpgm1yA6XuYL1D06gzmjzXgHvZMOFcts+GF
y0JEuV7v6eYrpQJRQYCwY6xTgwKBgQDJ+bHAlgOaC94DZEXZMiUznCCjBjAstiXG
BEN7Cnfn6vgvPm/b6BkKn4VrsCmbZQKT7QJDSOhYwXCC2ZlrKiF8GEUHX4mi8347
8B+DsuokTLNmN61QAZbb1c3XQVnr15xH8ijm7yYs4tCBmVLKBmpw1T4IZXXlVE5k
gmee+AwIfQKBgGr+P0wnclVAc4cq8CusZKzux5VEtebxbPo21CbqWUxHtzPk3rZe
elIFggK1Z3bgF7kG0NQ18QQCfLoOTqe1i6IwG8KBiA+pst1DHD0iPqroj6RvpMTs
qXbU7ovcZs8GH+a8fBZtJufL6WkrSvfvyybu2X6HNP4Bi4S9WPPdlA1fAoGAE5m/
vkjQoKp2KS4Z+TH8mj2UjT2Uf0JN+CGByvcBG+iZnTwZ7uVfSMCiWgkGgKYU0fY2
OgFhSvu6x3gGg3fbOAfC6yxCVyX6IibzZ/x87HjlEA5nK1R8J2lgSHt3FoQeDn1Z
qs+ajNCWG32doy1sNvb6xiXSgybjVK2zEKJRyKECgYBJTk2IABebjvInNb6tagcI
nD4d2LgBmZJZsTruHXrpO0s3XCQcFKks4JKH1CVjd34f7LkxzEOGbE7wKBBd652s
ob6gFKnbqTniTo3NRUycB6ymo4LSaBvKgeY5hYbVxrYheRLPGY+gPVYb3VMKu9N9
76rcaFqJOz7OeywRG5bHUg==
-----END PRIVATE KEY-----

View File

@ -1,10 +0,0 @@
[req]
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
C = US
ST = Nevada
L = Las Vegas
O = github.com/lib/pq
CN = pq CA

View File

@ -1,24 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEBjCCAu6gAwIBAgIJAPizR+OD14YnMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV
BAYTAlVTMQ8wDQYDVQQIDAZOZXZhZGExEjAQBgNVBAcMCUxhcyBWZWdhczEaMBgG
A1UECgwRZ2l0aHViLmNvbS9saWIvcHExDjAMBgNVBAMMBXBxIENBMB4XDTIxMDkw
MjAxNTUwMloXDTMxMDkwMzAxNTUwMlowXjELMAkGA1UEBhMCVVMxDzANBgNVBAgM
Bk5ldmFkYTESMBAGA1UEBwwJTGFzIFZlZ2FzMRowGAYDVQQKDBFnaXRodWIuY29t
L2xpYi9wcTEOMAwGA1UEAwwFcHEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDb9d6sjdU6GdibGrXRMOHREH3MRUS8T4TFqGgPEGVDP/V5bAZlBSGP
AN0o9DTyVLcbQpBt8zMTw9KeIzIIe5NIVkSmA16lw/YckGhOM+kZIkiDuE6qt5Ia
OQCRMdXkZ8ejG/JUu+rHU8FJZL8DE+jyYherzdjkeVAQ7JfzxAwW2Dl7T/47g337
Pwmf17AEb8ibSqmXyUN7R5NhJQs+hvaYdNagzdx91E1H+qlyBvmiNeasUQljLvZ+
Y8wAuU79neA+d09O4PBiYwV17rSP6SZCeGE3oLZviL/0KM9Xig88oB+2FmvQ6Zxa
L7SoBlqS+5pBZwpH7eee/wCIKAnJtMAJAgMBAAGjgcYwgcMwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUfIXEczahbcM2cFrwclJF7GbdajkwgZAGA1UdIwSBiDCB
hYAUfIXEczahbcM2cFrwclJF7GbdajmhYqRgMF4xCzAJBgNVBAYTAlVTMQ8wDQYD
VQQIDAZOZXZhZGExEjAQBgNVBAcMCUxhcyBWZWdhczEaMBgGA1UECgwRZ2l0aHVi
LmNvbS9saWIvcHExDjAMBgNVBAMMBXBxIENBggkA+LNH44PXhicwDQYJKoZIhvcN
AQELBQADggEBABFyGgSz2mHVJqYgX1Y+7P+MfKt83cV2uYDGYvXrLG2OGiCilVul
oTBG+8omIMSHOsQZvWMpA5H0tnnlQHrKpKpUyKkSL+Wv5GL0UtBmHX7mVRiaK2l4
q2BjRaQUitp/FH4NSdXtVrMME5T1JBBZHsQkNL3cNRzRKwY/Vj5UGEDxDS7lILUC
e01L4oaK0iKQn4beALU+TvKoAHdPvoxpPpnhkF5ss9HmdcvRktJrKZemDJZswZ7/
+omx8ZPIYYUH5VJJYYE88S7guAt+ZaKIUlel/t6xPbo2ZySFSg9u1uB99n+jTo3L
1rAxFnN3FCX2jBqgP29xMVmisaN5k04UmyI=
-----END CERTIFICATE-----

View File

@ -1,29 +0,0 @@
[ req ]
default_bits = 2048
distinguished_name = subject
req_extensions = req_ext
x509_extensions = x509_ext
string_mask = utf8only
prompt = no
[ subject ]
C = US
ST = Nevada
L = Las Vegas
O = github.com/lib/pq
[ x509_ext ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
subjectAltName = DNS:postgres
nsComment = "OpenSSL Generated Certificate"
[ req_ext ]
subjectKeyIdentifier = hash
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
subjectAltName = DNS:postgres
nsComment = "OpenSSL Generated Certificate"

View File

@ -1,22 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDqzCCApOgAwIBAgIJAPiewLrOyYipMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV
BAYTAlVTMQ8wDQYDVQQIDAZOZXZhZGExEjAQBgNVBAcMCUxhcyBWZWdhczEaMBgG
A1UECgwRZ2l0aHViLmNvbS9saWIvcHExDjAMBgNVBAMMBXBxIENBMB4XDTIxMDkw
MjAxNTUwMloXDTMxMDkwMzAxNTUwMlowTjELMAkGA1UEBhMCVVMxDzANBgNVBAgM
Bk5ldmFkYTESMBAGA1UEBwwJTGFzIFZlZ2FzMRowGAYDVQQKDBFnaXRodWIuY29t
L2xpYi9wcTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKf6H4UzmANN
QiQJe92Mf3ETMYmpZKNNO9DPEHyNLIkag+XwMrBTdcCK0mLvsNCYpXuBN6703KCd
WAFOeMmj7gOsWtvjt5Xm6bRHLgegekXzcG/jDwq/wyzeDzr/YkITuIlG44Lf9lhY
FLwiHlHOWHnwrZaEh6aU//02aQkzyX5INeXl/3TZm2G2eIH6AOxOKOU27MUsyVSQ
5DE+SDKGcRP4bElueeQWvxAXNMZYb7sVSDdfHI3zr32K4k/tC8x0fZJ5XN/dvl4t
4N4MrYlmDO5XOrb/gQH1H4iu6+5EMDfZYab4fkThnNFdfFqu4/8Scv7KZ8mWqpKM
fGAjEPctQi0CAwEAAaN8MHowHQYDVR0OBBYEFENExPbmDyFB2AJUdbMvVyhlNPD5
MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMBMGA1UdEQQMMAqCCHBvc3RncmVzMCwG
CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTANBgkq
hkiG9w0BAQsFAAOCAQEAMRVbV8RiEsmp9HAtnVCZmRXMIbgPGrqjeSwk586s4K8v
BSqNCqxv6s5GfCRmDYiqSqeuCVDtUJS1HsTmbxVV7Ke71WMo+xHR1ICGKOa8WGCb
TGsuicG5QZXWaxeMOg4s0qpKmKko0d1aErdVsanU5dkrVS7D6729Ffnzu4lwApk6
invAB67p8u7sojwqRq5ce0vRaG+YFylTrWomF9kauEb8gKbQ9Xc7QfX+h+UH/mq9
Nvdj8LOHp6/82bZdnsYUOtV4lS1IA/qzeXpqBphxqfWabD1yLtkyJyImZKq8uIPp
0CG4jhObPdWcCkXD6bg3QK3mhwlC79OtFgxWmldCRQ==
-----END CERTIFICATE-----

View File

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCn+h+FM5gDTUIk
CXvdjH9xEzGJqWSjTTvQzxB8jSyJGoPl8DKwU3XAitJi77DQmKV7gTeu9NygnVgB
TnjJo+4DrFrb47eV5um0Ry4HoHpF83Bv4w8Kv8Ms3g86/2JCE7iJRuOC3/ZYWBS8
Ih5Rzlh58K2WhIemlP/9NmkJM8l+SDXl5f902ZthtniB+gDsTijlNuzFLMlUkOQx
PkgyhnET+GxJbnnkFr8QFzTGWG+7FUg3XxyN8699iuJP7QvMdH2SeVzf3b5eLeDe
DK2JZgzuVzq2/4EB9R+IruvuRDA32WGm+H5E4ZzRXXxaruP/EnL+ymfJlqqSjHxg
IxD3LUItAgMBAAECggEAOE2naQ9tIZYw2EFxikZApVcooJrtx6ropMnzHbx4NBB2
K4mChAXFj184u77ZxmGT/jzGvFcI6LE0wWNbK0NOUV7hKZk/fPhkV3AQZrAMrAu4
IVi7PwAd3JkmA8F8XuebUDA5rDGDsgL8GD9baFJA58abeLs9eMGyuF4XgOUh4bip
hgHa76O2rcDWNY5HZqqRslw75FzlYkB0PCts/UJxSswj70kTTihyOhDlrm2TnyxI
ne54UbGRrpfs9wiheSGLjDG81qZToBHQDwoAnjjZhu1VCaBISuGbgZrxyyRyqdnn
xPW+KczMv04XyvF7v6Pz+bUEppalLXGiXnH5UtWvZQKBgQDTPCdMpNE/hwlq4nAw
Kf42zIBWfbnMLVWYoeDiAOhtl9XAUAXn76xe6Rvo0qeAo67yejdbJfRq3HvGyw+q
4PS8r9gXYmLYIPQxSoLL5+rFoBCN3qFippfjLB1j32mp7+15KjRj8FF2r6xIN8fu
XatSRsaqmvCWYLDRv/rbHnxwkwKBgQDLkyfFLF7BtwtPWKdqrwOM7ip1UKh+oDBS
vkCQ08aEFRBU7T3jChsx5GbaW6zmsSBwBwcrHclpSkz7n3aq19DDWObJR2p80Fma
rsXeIcvtEpkvT3pVX268P5d+XGs1kxgFunqTysG9yChW+xzcs5MdKBzuMPPn7rL8
MKAzdar6PwKBgEypkzW8x3h/4Moa3k6MnwdyVs2NGaZheaRIc95yJ+jGZzxBjrMr
h+p2PbvU4BfO0AqOkpKRBtDVrlJqlggVVp04UHvEKE16QEW3Xhr0037f5cInX3j3
Lz6yXwRFLAsR2aTUzWjL6jTh8uvO2s/GzQuyRh3a16Ar/WBShY+K0+zjAoGATnLT
xZjWnyHRmu8X/PWakamJ9RFzDPDgDlLAgM8LVgTj+UY/LgnL9wsEU6s2UuP5ExKy
QXxGDGwUhHar/SQTj+Pnc7Mwpw6HKSOmnnY5po8fNusSwml3O9XppEkrC0c236Y/
7EobJO5IFVTJh4cv7vFxTJzSsRL8KFD4uzvh+nMCgYEAqY8NBYtIgNJA2B6C6hHF
+bG7v46434ZHFfGTmMQwzE4taVg7YRnzYESAlvK4bAP5ZXR90n7GRGFhrXzoMZ38
r0bw/q9rV+ReGda7/Bjf7ciCKiq0RODcHtf4IaskjPXCoQRGJtgCPLhWPfld6g9v
/HTvO96xv9e3eG/PKSPog94=
-----END PRIVATE KEY-----

View File

@ -1,134 +0,0 @@
package common
import (
"fmt"
"log"
"os"
"runtime"
"strconv"
"strings"
"time"
)
func init(){
log.SetPrefix("TRACE: ")
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Llongfile)
}
func itoa(buf *[]byte, i int, wid int) {
// Assemble decimal in reverse order.
var b [20]byte
bp := len(b) - 1
for i >= 10 || wid > 1 {
wid--
q := i / 10
b[bp] = byte('0' + i - q*10)
bp--
i = q
}
// i < 10
b[bp] = byte('0' + i)
*buf = append(*buf, b[bp:]...)
}
func formatHeader(buf []byte, t time.Time, file string, line int) []byte{
t = t.UTC()
year, month, day := t.Date()
itoa(&buf, year, 4)
buf = append(buf, '/')
itoa(&buf, int(month), 2)
buf = append(buf, '/')
itoa(&buf, day, 2)
buf = append(buf, ' ')
hour, min, sec := t.Clock()
itoa(&buf, hour, 2)
buf = append(buf, ':')
itoa(&buf, min, 2)
buf = append(buf, ':')
itoa(&buf, sec, 2)
buf = append(buf, '.')
itoa(&buf, t.Nanosecond()/1e3, 6)
buf = append(buf, ' ')
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
file = short
buf = append(buf, file...)
buf = append(buf, ':')
itoa(&buf, line, -1)
return append(buf, ": "...)
}
func CheckErr(res ...interface{}){
for _,oriErr :=range res{
if oriErr == nil{
continue
}
switch oriErr.(type) {
case error:
err :=oriErr.(error)
s :=fmt.Sprintln(err)
_,file,line,ok:=runtime.Caller(1)
if ok {
info :="[CheckErr] "
buf :=make([]byte,0,1024)
buf=append(buf,info...)
buf = formatHeader(buf,time.Now(),file,line)
buf = append(buf, s...)
os.Stdout.Write(buf)
}else{
fmt.Printf("%s %s",time.Now(),s)
}
os.Exit(2)
default:
}
}
}
func createNewFile(fileName string){
err := os.Remove(fileName)
if err!=nil{
log.Println(err)
}
fd,err:=os.Create(fileName)
if err!=nil{
log.Fatal(err)
}
fd.Close()
}
func GetGoid() int64 {
var (
buf [64]byte
n = runtime.Stack(buf[:], false)
stk = strings.TrimPrefix(string(buf[:n]), "goroutine ")
)
idField := strings.Fields(stk)[0]
id, err := strconv.Atoi(idField)
if err != nil {
panic(fmt.Errorf("can not get goroutine id: %v", err))
}
return int64(id)
}
func PrintType(a interface{}){
switch a.(type) {
case int:
fmt.Println("type is int")
case string:
fmt.Println("type is string")
case float32:
fmt.Println("type is float32")
case float64:
fmt.Println("type is float64")
default:
fmt.Println("type is unknown type")
}
}

View File

@ -1,46 +0,0 @@
package common
import (
"math/rand"
"reflect"
"runtime"
"strings"
"time"
)
func init(){
rand.Seed(time.Now().UnixNano())
}
func GetRandomString(n int)string{
str := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
bytes :=[]byte(str)
result :=make([]byte,n)
for i:=0;i<n;i++{
result[i]=bytes[rand.Intn(len(str))]
}
return string(result)
}
func GetFunctionName(i interface{}, seps ...rune) string {
// 获取函数名称
fn := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
// 用 seps 进行分割
fields := strings.FieldsFunc(fn, func(sep rune) bool {
for _, s := range seps {
if sep == s {
return true
}
}
return false
})
// fmt.Println(fields)
if size := len(fields); size > 0 {
return fields[size-1]
}
return ""
}

View File

@ -1,357 +0,0 @@
package common
import (
"database/sql"
"fmt"
"gitee.com/opengauss/openGauss-connector-go-pq"
ini "github.com/GaussKernel/ini-main"
"log"
"os"
"strconv"
"strings"
"sync"
"testing"
)
var (
parse_config =make(map[string]string)
onceDsnBase sync.Once
t =&testing.T{}
SQL_QUERYTIME="select current_date"
//pg
DSN_PG_WIN = "host=127.0.0.1 port=5433 user=postgres password=Gauss_234 dbname=postgres sslmode=require"
DSN_PG_L_G="host=7.189.51.79 port=5431 user=gauss password=Gauss_234 dbname=gauss sslmode=disable"
//openGauss
DSN_OG_L_G = GetOGDsnBase()
)
func init() {
goPath := os.Getenv("GOPATH")
//filePathPrefix :=d[:strings.Index(d, keyPath)]
filePathPrefix:=strings.Join([]string{goPath,"src","github.com","GaussKernel"},string(os.PathSeparator))
iniPath := strings.Join([]string{filePathPrefix,"property.ini"},string(os.PathSeparator))
cfg, err := ini.Load(iniPath)
if err!=nil{
log.Fatal("load property error, ",err)
}
for _,key :=range cfg.Section("opengauss").Keys(){
if len(key.Value())>0{
parse_config[key.Name()]=key.Value()
}
}
sslPath :=strings.Join([]string{filePathPrefix,"certs"},string(os.PathSeparator))
parse_config["sslkey"]=strings.Join([]string{sslPath,"postgresql.key"},string(os.PathSeparator))
parse_config["sslcert"]=strings.Join([]string{sslPath,"postgresql.crt"},string(os.PathSeparator))
parse_config["sslrootcert"]=strings.Join([]string{sslPath,"root.crt"},string(os.PathSeparator))
}
func GetOGDsnFull()(res string){
for k,v :=range parse_config{
res+=" "+k+"="+v
}
return
}
func GetOGDsnBase()(res string){
baseOpt :=[]string{
"host",
"port",
"user",
"password",
"dbname",
}
for _,opt :=range baseOpt{
if v,ok:=parse_config[opt];ok{
res+=" "+opt+"="+v
}
}
return
}
func GetOGUrlBase()(res string){
return ""
}
func GetPropertyUser()string{
res,ok := parse_config["user"]
if ok{
return res
}
log.Fatal("Your should set user in proterty.ini")
return ""
}
func GetPropertyPort()string{
res,ok := parse_config["port"]
if ok{
return res
}
log.Fatal("Your should set port in proterty.ini")
return ""
}
func GetPropertyDbName()string{
res,ok := parse_config["dbname"]
if ok{
return res
}
log.Fatal("Your should set hot in proterty.ini")
return ""
}
func GetPropertyHost()string{
res,ok := parse_config["host"]
if ok{
return res
}
log.Fatal("Your should set host in proterty.ini")
return ""
}
func GetPropertyPassword()string{
res,ok := parse_config["password"]
if ok{
return res
}
log.Fatal("Your should set password in proterty.ini")
return ""
}
//func GetPropertySSLKey()string{
// return parse_config["sslkey"]
//}
//func GetPropertySSLCert()string{
// return parse_config["sslcert"]
//}
//func GetPropertySSLRootCert()string{
// return parse_config["sslrootcert"]
//}
//func GetPropertySSLDSN()string {
// return " sslkey="+GetPropertySSLKey()+" sslcert="+GetPropertySSLCert()+" sslrootcert="+GetPropertySSLRootCert()
//}
func GetDefaultOGDB()(*sql.DB){
db,err :=sql.Open("opengauss",GetOGDsnBase())
CheckErr(err)
return db
}
func TestDB(dv string, dsn string){
db, err := sql.Open(dv, dsn)
if err != nil {
CheckErr(err)
}
defer db.Close()
var rc = 0
err =db.QueryRow("select 1").Scan(&rc)
CheckErr(err)
if rc != 1 {
log.Fatal(dv,dsn,"["+dsn+"]query 1 wrong, rc=",rc)
return
}
}
func TestConnExpect(dv string, dsn string, isExpectTrue bool){
db, err := sql.Open(dv, dsn)
if err != nil {
if isExpectTrue {
log.Fatalf("this dsn \"%v\" should shout be ok, but error: %v",dsn,err)
}
return
}
defer db.Close()
var rc = 0
err =db.QueryRow("select 1").Scan(&rc)
if err != nil {
if isExpectTrue {
log.Fatalf("this dsn \"%v\" should shout be ok, but error: %v",dsn,err)
}
return
}
if rc != 1 {
log.Fatal(dv,dsn,"["+dsn+"]query 1 wrong, rc=",rc)
return
}
}
func PrintRowData(rows *sql.Rows){
columns,err :=rows.Columns()
if err!=nil{
log.Fatal("rows coulumns error",err)
}
cols:=len(columns)
var scanResults = make([]interface{}, cols)
for i := 0; i < cols; i++ {
var s sql.NullString
scanResults[i] = &s
}
if err :=rows.Scan(scanResults...);err!=nil{
log.Fatal("rows Scan error, ",err)
}
for i,key:=range columns{
s := scanResults[i].(*sql.NullString)
if !s.Valid{
fmt.Println(key+": NULL")
}else {
fmt.Println(key+": "+s.String)
}
}
}
func IterScanRows(rows *sql.Rows)(idx int){
for rows.Next(){
idx++
PrintRowData(rows)
}
return
}
func IterScanDbTable(db *sql.DB, tname string)(idx int){
sql :="select * from "+tname+" order by 1"
row,err:=db.Query(sql)
CheckErr(err)
return IterScanRows(row)
}
func IterScanStmtTable(stmt *sql.Stmt, tname string)(idx int){
sql :="select * from "+tname+" order by 1"
row,err:=stmt.Query(sql)
CheckErr(err)
return IterScanRows(row)
}
func IterScanTxTable(txn *sql.Tx, tname string)(idx int){
sql :="select * from "+tname+" order by 1"
row,err := txn.Query(sql)
CheckErr(err)
return IterScanRows(row)
}
type QueryInterface interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
}
func IterScanTable(sqlObject interface{}, tname string)(idx int){
sql :="select * from "+tname+" order by 1"
if fQuery,ok:=sqlObject.(QueryInterface);ok{
row,err := fQuery.Query(sql)
CheckErr(err)
return IterScanRows(row)
} else {
log.Fatal("cannot parse your input args")
}
return -1
}
func PrintQuery(db *sql.DB, sql string){
rows,err:=db.Query(sql)
if err !=nil{
//t.Fatal(err)
fmt.Println(err)
panic(err)
}
sqls :=strings.Split(sql,";")
tab_idx :=0
LOOP:
tab_idx++
fmt.Println("Query["+strconv.Itoa(tab_idx)+"]==> "+sqls[tab_idx-1])
rowsCount :=IterScanRows(rows)
fmt.Println("<==Query["+strconv.Itoa(tab_idx)+"]"+" has "+strconv.Itoa(rowsCount)+" rows.")
if rows.NextResultSet(){
goto LOOP
}
}
func PrintTable(db *sql.DB, tables... string){
for _,table := range tables{
sql := "select * from "+table+" order by 1"
PrintQuery(db,sql)
}
}
func PrintTableMeta(db *sql.DB, tableName, schameName string){
getColumn:="SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, description," +
" CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey," +
" CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey" +
" FROM pg_attribute f" +
" JOIN pg_class c ON c.oid = f.attrelid JOIN pg_type t ON t.oid = f.atttypid" +
" LEFT JOIN pg_attrdef d ON d.adrelid = c.oid AND d.adnum = f.attnum" +
" LEFT JOIN pg_description de ON f.attrelid=de.objoid AND f.attnum=de.objsubid" +
" LEFT JOIN pg_namespace n ON n.oid = c.relnamespace" +
" LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey)" +
" LEFT JOIN pg_class AS g ON p.confrelid = g.oid" +
" LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name" +
" WHERE n.nspname= s.table_schema AND c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.attnum > 0 ORDER BY f.attnum;"
getIndex :="SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1 AND schemaname=$2"
row,err:=db.Query(getColumn,tableName,schameName)
CheckErr(err)
IterScanRows(row)
row,err=db.Query(getIndex,tableName,schameName)
CheckErr(err)
IterScanRows(row)
}
func GetTableRowCounts(db *sql.DB,table string)(rows int){
sql :="select count(*) from "+table;
err :=db.QueryRow(sql).Scan(&rows)
if err!=nil{
log.Fatal("query "+table+" row counts error",err)
}
return
}
func PrintTableRowCounts(db *sql.DB,table string){
fmt.Printf("%s : %d\n",table,GetTableRowCounts(db,table))
}
func GetDriverErrorMessage(err error)string{
if nerr,ok :=err.(*pq.Error);ok{
return nerr.Message
}
return ""
}
func TruncateTable(db *sql.DB,table string){
sql :="select count(*) from "+table;
var rows int
err :=db.QueryRow(sql).Scan(&rows)
res,err:=db.Exec("truncate table "+table)
if err!=nil{
log.Fatal("truncate table error,",err)
}
_,err = res.RowsAffected()
if err!=nil{
log.Fatal("get rows affected error,",err)
}
}
func CheckTabExists(db *sql.DB, schema,table string)bool{
sql :="select count(*) from information_schema.columns where table_schema='"+schema+"' and table_name='"+table+"'"
var rowcount int
if err :=db.QueryRow(sql).Scan(&rowcount);err!=nil{
fmt.Println(err)
return false
}
return rowcount==1
}
func PrintListenPort(db *sql.DB){
var ips,port string
err :=db.QueryRow("select (select setting from pg_settings where name ='listen_addresses'), (select setting from pg_settings where name ='port')").Scan(&ips,&port)
if err!=nil{
log.Fatal("PrintListenPort error,",err)
}
fmt.Println(ips,port)
}
func SqlExec(db *sql.DB, sql string, args ...interface{}) {
r,err :=db.Exec(sql,args...)
if err !=nil{
log.Fatal("sql["+sql+"] exec error,",err)
}
n,err:=r.RowsAffected()
if err!=nil{
log.Fatal("sql["+sql+"] exec rows affect error,",err)
}
log.Println("sql["+sql+"] exec rows affect=",n)
}

View File

@ -1,25 +0,0 @@
module github.com/GaussKernel
go 1.17
require (
gitee.com/opengauss/openGauss-connector-go-pq v0.0.0-00010101000000-000000000000
github.com/go-xorm/xorm v0.0.0-00010101000000-000000000000
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
xorm.io/builder v0.3.6 // indirect
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb // indirect
)
//replace gitee.com/opengauss/openGauss-connector-go-pq => ../../gitee.com/opengauss/openGauss-connector-go-pq
replace github.com/go-xorm/xorm => ../../github.com/go-xorm/xorm
replace gitee.com/opengauss/openGauss-connector-go-pq => ../../gitee.com/opengauss/openGauss-connector-go-pq

View File

@ -1,172 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb h1:msX3zG3BPl8Ti+LDzP33/9K7BzO/WqFXk610K1kYKfo=
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=

View File

@ -1,30 +0,0 @@
package bench
import (
"database/sql"
"github.com/GaussKernel/common"
"log"
"testing"
)
func TestDML(t *testing.T){
insertRows :=100
pool:=common.GetDefaultOGDB()
common.CheckErr(pool.Exec("drop table if exists testdml"))
common.CheckErr(pool.Exec("create table testdml(id int,name varchar(8000))"))
defer pool.Close()
st,err:=pool.Prepare("insert into testdml(id,name) values(:id,:name)")
if err!=nil{
log.Fatal("insert error",err)
}
defer st.Close()
for i:=0;i<insertRows;i++{
tname:=common.GetRandomString(20)
st.Exec(sql.Named("id",i),sql.Named("name",tname))
}
rows := common.GetTableRowCounts(pool,"testdml")
if rows!=insertRows{
log.Fatal("insert error")
}
common.CheckErr(pool.Exec("drop table if exists testdml"))
}

View File

@ -1,70 +0,0 @@
package bench
import (
. "github.com/GaussKernel/common"
"testing"
)
func TestMuiltSql(t *testing.T){
db := GetDefaultOGDB()
defer db.Close()
rows, err := db.Query("select 1; set time zone default; select 2; select 3")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
var i int
for rows.Next() {
if err := rows.Scan(&i); err != nil {
t.Fatal(err)
}
if i != 1 {
t.Fatalf("expected 1, got %d", i)
}
}
if !rows.NextResultSet() {
t.Fatal("expected more result sets", rows.Err())
}
for rows.Next() {
if err := rows.Scan(&i); err != nil {
t.Fatal(err)
}
if i != 2 {
t.Fatalf("expected 2, got %d", i)
}
}
// Make sure that if we ignore a result we can still query.
rows, err = db.Query("select 4; select 5")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
for rows.Next() {
if err := rows.Scan(&i); err != nil {
t.Fatal(err)
}
if i != 4 {
t.Fatalf("expected 4, got %d", i)
}
}
if !rows.NextResultSet() {
t.Fatal("expected more result sets", rows.Err())
}
for rows.Next() {
if err := rows.Scan(&i); err != nil {
t.Fatal(err)
}
if i != 5 {
t.Fatalf("expected 5, got %d", i)
}
}
if rows.NextResultSet() {
t.Fatal("unexpected result set")
}
}

View File

@ -1,55 +0,0 @@
package bench
import (
"database/sql"
"database/sql/driver"
"fmt"
pq "gitee.com/opengauss/openGauss-connector-go-pq"
. "github.com/GaussKernel/common"
"log"
"testing"
)
func TestConnectorWithNoticeHandler_Simple(t *testing.T) {
db,err :=pq.NewConnector(GetOGDsnBase())
if err!=nil{
log.Fatal(err)
}
TestConnExpect("opengauss",GetOGDsnBase(),true)
var notice *pq.Error
// Make connector w/ handler to set the local var
c := pq.ConnectorWithNoticeHandler(db, func(n *pq.Error) {
notice = n
})
raiseNotice(c, t, "Test notice #1")
if notice == nil || notice.Message != "Test notice #1" {
t.Fatalf("Expected notice w/ message, got %v", notice)
}
// Unset the handler on the same connector
prevC := c
if c = pq.ConnectorWithNoticeHandler(c, nil); c != prevC {
t.Fatalf("Expected to not create new connector but did")
}
raiseNotice(c, t, "Test notice #2")
if notice == nil || notice.Message != "Test notice #1" {
t.Fatalf("Expected notice to not change, got %v", notice)
}
// Set it back on the same connector
if c = pq.ConnectorWithNoticeHandler(c, func(n *pq.Error) { notice = n }); c != prevC {
t.Fatal("Expected to not create new connector but did")
}
raiseNotice(c, t, "Test notice #3")
if notice == nil || notice.Message != "Test notice #3" {
t.Fatalf("Expected notice w/ message, got %v", notice)
}
}
func raiseNotice(c driver.Connector, t *testing.T, escapedNotice string) {
db := sql.OpenDB(c)
defer db.Close()
sql := "DO language plpgsql $$ BEGIN RAISE NOTICE '" + escapedNotice + "'; END $$"
if _, err := db.Exec(sql); err != nil {
fmt.Println(err)
t.Fatal(err)
}
}

View File

@ -1,53 +0,0 @@
package bench
import (
"context"
. "github.com/GaussKernel/common"
"log"
"regexp"
"testing"
"time"
)
func TestContextCancelExec(t *testing.T) {
db := GetDefaultOGDB()
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
// Delay execution for just a bit until db.ExecContext has begun.
defer time.AfterFunc(time.Millisecond*10, cancel).Stop()
// Not canceled until after the exec has started.
if _, err := db.ExecContext(ctx, "select pg_sleep(1)"); err == nil {
log.Fatal("expected error")
} else {
errStr := err.Error()
//reg := regexp.Compile("cancel")
f,err := regexp.MatchString("cancel", errStr)
CheckErr(err)
if !f{
log.Fatalf("unexpected error: %s", err)
}
}
// Context is already canceled, so error should come before execution.
if _, err := db.ExecContext(ctx, "select pg_sleep(1)"); err == nil {
log.Fatal("expected error")
} else if err.Error() != "context canceled" {
log.Fatalf("unexpected error: %s", err)
}
for i := 0; i < 100; i++ {
func() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if _, err := db.ExecContext(ctx, "select 1"); err != nil {
log.Fatal(err)
}
}()
if _, err := db.Exec("select 1"); err != nil {
log.Fatal(err)
}
}
}

View File

@ -1,59 +0,0 @@
package bench
import (
"github.com/GaussKernel/common"
"log"
"regexp"
"strings"
"testing"
)
func TestTempTablet(t *testing.T){
db :=common.GetDefaultOGDB()
defer db.Close()
common.CheckErr(db.Exec("drop table if exists temp"))
txn, err := db.Begin()
if err != nil {
t.Fatal(err)
}
_, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
if err != nil {
t.Fatal(err)
}
stmt, err := txn.Prepare("insert into temp values(:1,:2)")
common.CheckErr(err)
var repeatCount =100
var insertCount=10
longString := strings.Repeat("#", repeatCount)
for i := 0; i < insertCount; i++ {
_, err := stmt.Exec(int64(i), longString)
if err != nil {
t.Fatal(err)
}
}
row,err := txn.Query("SELECT COUNT(*) FROM temp")
common.CheckErr(err)
common.IterScanRows(row)
err = stmt.Close()
common.CheckErr(err)
row,err = txn.Query("SELECT COUNT(*) FROM temp")
if err != nil {
t.Fatal(err)
}
common.IterScanRows(row)
common.CheckErr(txn.Rollback())
_,err = db.Query("SELECT COUNT(*) FROM temp")
f,err :=regexp.MatchString("relation \"temp\" does not exist on datanode", err.Error())
common.CheckErr(err)
if !f {
log.Fatal("message has changed")
}
db.Close()
}

View File

@ -1,50 +0,0 @@
package bench
import (
//"fmt"
"github.com/GaussKernel/common"
"github.com/go-xorm/xorm"
"log"
"testing"
"time"
)
func TestXorm(t *testing.T){
engine, err := xorm.NewEngine("postgres", common.GetOGDsnBase())
if err != nil{
log.Fatal(err)
}
//engine.ShowSQL(true) //则会在控制台打印出生成的SQL语句;
engine.SetSchema("gauss")
if err = engine.DropTables("user"); err!=nil{
log.Fatal(err)
}
if f,err:=engine.IsTableExist("user");err !=nil || f{
log.Fatal("this table should not exists")
}
type User struct {
Id int64
Name string
Salt string
Age int
Passwd string `xorm:"varchar(200)"`
Created time.Time `xorm:"created"`
Updated time.Time `xorm:"updated"`
}
err = engine.Sync2(new(User))
common.CheckErr(err)
time.Sleep(time.Millisecond*1000)
if f,err:=engine.IsTableExist("user");err !=nil || !f{
log.Fatal("this table should exists",err,f)
}
db:=common.GetDefaultOGDB()
common.PrintTableMeta(db,"user","gauss")
if err = engine.DropTables("user"); err!=nil{
log.Fatal(err)
}
}

View File

@ -1,247 +0,0 @@
package bench
import (
"context"
"database/sql"
"fmt"
"github.com/GaussKernel/common"
"log"
"strings"
"testing"
"time"
)
func QueryStmt_noctx(pool *sql.DB) {
st,err :=pool.Prepare("select :name,:id;")
if err!=nil{
log.Fatal("prepare error",err)
}
row,err :=st.Query(sql.Named("name","liu"),sql.Named("id","3"))
if err!=nil {
log.Fatal("query error",err)
}
cs,err:=row.Columns()
if err!=nil{
log.Fatal("get columns error",err)
}
fmt.Println(cs)
columns, err := row.ColumnTypes()
if err!=nil{
log.Fatal("get col error",err)
}
for _,col :=range columns{
fmt.Println(col.Length())
fmt.Println(col.Name(),col.ScanType())
fmt.Println(col.DecimalSize())
fmt.Println(col.Nullable())
}
common.IterScanRows(row)
row.Close()
st.Close()
}
func QueryStmtCtx(pool *sql.DB) {
st,err :=pool.Prepare("select :name as name,:id as ID;")
if err!=nil{
log.Fatal("prepare error",err)
}
res,err:=st.Exec(sql.Named("name","liu"),sql.Named("id",1.34))
if err!=nil{
log.Fatal(err)
}
fmt.Println(res.RowsAffected())
row,err :=st.QueryContext(context.Background(),sql.Named("name","liu"),sql.Named("id",3.34))
if err !=nil {
log.Fatal("query error",err)
}
cs,err:=row.Columns()
if err!=nil{
log.Fatal("get columns error",err)
}
fmt.Println(cs)
columns, err := row.ColumnTypes()
if err!=nil{
log.Fatal("get col error",err)
}
for _,col :=range columns{
fmt.Println(col.Length())
fmt.Println(col.Name(),col.ScanType())
fmt.Println(col.DecimalSize())
fmt.Println(col.Nullable())
}
common.IterScanRows(row)
row.Close()
st.Close()
}
func QueryBindWithoutName(pool *sql.DB) {
st,err :=pool.Prepare("select :name as name,:id as ID;")
if err!=nil{
log.Fatal("prepare error",err)
}
res,err:=st.Exec("liuhang",3)
if err!=nil{
log.Fatal(err)
}
fmt.Println(res.RowsAffected())
}
func TestBindParam(t *testing.T){
pool:=common.GetDefaultOGDB()
QueryStmtCtx(pool)
QueryStmt_noctx(pool)
QueryBindWithoutName(pool)
}
func TestBinaryParam(t *testing.T){
dsn :=common.GetOGDsnBase()
dsn +=" binary_parameters=yes"
db,err:=sql.Open("opengauss",dsn)
common.CheckErr(err)
defer db.Close()
tx,err:=db.Begin()
common.CheckErr(err)
var name,id string
err =tx.QueryRow("select :name as name,:id as ID;",sql.Named("name","abc"),sql.Named("id",[]byte("123"))).Scan(&name,&id)
common.CheckErr(err)
fmt.Println(name,id)
stmt,err:=tx.Prepare("select :name as name,:id as ID;")
common.CheckErr(err)
err = stmt.QueryRow(sql.Named("name","abc"),sql.Named("id",123)).Scan(&name,&id)
}
func TestBindWithLocation(t *testing.T) {
fmt.Println("********************Bind With Location********************")
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testBindWithLocation",
"create table testBindWithLocation(f1 int, f2 varchar(20), f3 number, f4 timestamptz, f5 boolean)",
"insert into testBindWithLocation values(:f1, :f2, :f3, :f4, :f5)",
}
inF1 := []int{2, 3, 4, 5, 6}
intF2 := []string{"hello world", "华为", "北京2022冬奥会", "nanjing", "研究所"}
intF3 := []float64{641.43, 431.54, 5423.52, 665537.63, 6503.1}
intF4 := []time.Time{
time.Date(2022, 2, 8, 10, 35, 43, 623431, time.Local),
time.Date(2022, 2, 10, 19, 11, 54, 353431, time.Local),
time.Date(2022, 2, 12, 6, 11, 15, 636431, time.Local),
time.Date(2022, 2, 14, 4, 51, 22, 747653, time.Local),
time.Date(2022, 2, 16, 13, 45, 55, 674636, time.Local),
}
intF5 := []bool{false, true, false, true, true}
for _, str := range sqls {
if strings.Contains(str, ":f") {
for i, _ := range inF1 {
_, err := db.Exec(str, inF1[i], intF2[i], intF3[i], intF4[i], intF5[i])
if err != nil {
t.Fatal(err)
}
}
} else {
_, err = db.Exec(str)
if err != nil {
t.Fatal(err)
}
}
}
var f1 int
var f2 string
var f3 float64
var f4 time.Time
var f5 bool
row, err :=db.Query("select * from testBindWithLocation")
if err != nil {
t.Fatal(err)
}
defer row.Close()
for row.Next() {
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
}
if row.Err() != nil {
t.Fatal(err)
}
}
func TestBindWithName(t *testing.T) {
fmt.Println("********************Bind With Name********************")
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testBindWithName",
"create table testBindWithName(f1 int, f2 varchar(20), f3 number, f4 timestamptz, f5 boolean)",
"insert into testBindWithName values(:f1, :f2, :f3, :f4, :f5)",
}
inF1 := []int{2, 3, 4, 5, 6}
inF2 := []string{"hello world", "华为", "北京2022冬奥会", "nanjing", "研究所"}
inF3 := []float64{641.43, 431.54, 5423.52, 665537.63, 6503.1}
inF4 := []time.Time{
time.Date(2022, 2, 8, 10, 35, 43, 623431, time.Local),
time.Date(2022, 2, 10, 19, 11, 54, 353431, time.Local),
time.Date(2022, 2, 12, 6, 11, 15, 636431, time.Local),
time.Date(2022, 2, 14, 4, 51, 22, 747653, time.Local),
time.Date(2022, 2, 16, 13, 45, 55, 674636, time.Local),
}
inF5 := []bool{false, true, false, true, true}
for _, str := range sqls {
if strings.Contains(str, ":f") {
for i, _ := range inF1 {
_, err := db.Exec(str, sql.Named("f1", inF1[i]), sql.Named("f2", inF2[i]), sql.Named("f3", inF3[i]),
sql.Named("f4", inF4[i]), sql.Named("f5", inF5[i]))
if err != nil {
t.Fatal(err)
}
}
} else {
_, err = db.Exec(str)
if err != nil {
t.Fatal(err)
}
}
}
var f1 int
var f2 string
var f3 float64
var f4 time.Time
var f5 bool
row, err :=db.Query("select * from testBindWithName")
if err != nil {
t.Fatal(err)
}
defer row.Close()
for row.Next() {
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
}
if row.Err() != nil {
t.Fatal(err)
}
}

View File

@ -1,102 +0,0 @@
package bench
import (
"database/sql"
"fmt"
"github.com/GaussKernel/common"
"math/rand"
"testing"
"time"
)
// 连接池大小
var MAX_POOL_SIZE = 20
var dbPoll chan *sql.DB
func putDB(db *sql.DB) {
// 基于函数和接口间互不信任原则,这里再判断一次,养成这个好习惯哦
if dbPoll == nil {
dbPoll = make(chan *sql.DB, MAX_POOL_SIZE)
}
if len(dbPoll) >= MAX_POOL_SIZE {
db.Close()
return
}
dbPoll <- db
}
func initDB(t *testing.T) {
// 缓冲机制,相当于消息队列
if len(dbPoll) == 0 {
// 如果长度为0,就定义一个redis.Conn类型长度为MAX_POOL_SIZE的channel
dbPoll = make(chan *sql.DB, MAX_POOL_SIZE)
go func() {
for i := 0; i < MAX_POOL_SIZE / 2; i++ {
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
putDB(db)
}
} ()
}
}
func GetDB(t *testing.T) *sql.DB {
// 如果为空就初始化或者长度为零
if dbPoll == nil || len(dbPoll) == 0 {
initDB(t)
}
return <- dbPoll
}
func changeCount(num int, t *testing.T) {
db := GetDB(t)
tx, err := db.Begin() // begin transaction
defer tx.Commit() // commit transaction
res,_ := tx.Exec("update testTxLock set f3 = f3 + 100")
RowsAffected, err := res.RowsAffected()
if err != nil {
t.Fatal("res.RowsAffected==================Err")
}
if RowsAffected <= 0 {
fmt.Println("抢购失败")
}
}
func TestConcurrency(t *testing.T) {
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
sqls := []string {
"drop table if exists testTxLock",
"create table testTxLock(f1 int, f2 varchar(20), f3 number, f4 timestamptz, f5 boolean)",
"insert into testTxLock values(1, '华为', 123.3, '2022-02-08 10:30:43.31 +08', true)",
}
for i := 0; i <= 2; i++ {
_, err = db.Exec(sqls[i])
if err != nil {
t.Fatal(err)
}
}
err = db.Close()
if err != nil {
t.Fatal(err)
}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 10; i++ {
go changeCount(r.Intn(50), t)
go changeCount(r.Intn(50), t)
go changeCount(r.Intn(50), t)
go changeCount(r.Intn(50), t)
}
time.Sleep(3 * time.Second)
}

View File

@ -1,311 +0,0 @@
package bench
import (
"database/sql"
"fmt"
"github.com/GaussKernel/common"
"log"
"math"
"regexp"
"strconv"
"strings"
"testing"
)
func TestWrongPort(t *testing.T){
var dsn="user=gauss dbname=gauss"
ports:=[]int{0,math.MaxUint16+1}
expectWrongMsg :="invalid port"
reg :=regexp.MustCompile(expectWrongMsg)
for _,port:=range ports{
ndsn :=dsn+" port="+strconv.Itoa(port)
_,err :=sql.Open("opengauss",ndsn)
if err!=nil{
errMsg :=err.Error()
if !reg.MatchString(errMsg){
log.Fatal("wrong information change")
}
} else{
log.Fatal("expect wrong")
}
}
}
func TestConnApplicationName(t *testing.T){
dsn :=common.GetOGDsnBase()
db,err :=sql.Open("opengauss",dsn)
if err!=nil{
log.Fatal("conn err,",err)
}
var appName string
err =db.QueryRow("select application_name from pg_stat_activity where pid=pg_backend_pid()").Scan(&appName)
if err!=nil{
log.Fatal(err)
}
if(appName != "go-driver"){ //default name
log.Fatal("test application name fail")
}
db.Close()
testAppName:="test_application_Name"
dsn +=" application_name="+testAppName
db,err =sql.Open("opengauss",dsn)
if err!=nil{
log.Fatal("conn err,",err)
}
err =db.QueryRow("select application_name from pg_stat_activity where pid=pg_backend_pid()").Scan(&appName)
if err!=nil{
log.Fatal(err)
}
if appName != testAppName {
log.Fatal("test application name fail")
}
}
func TestTargetSessionAttr(t *testing.T){
attrs :=[]struct{
attr string
expect bool
}{
{"any", true},
{"read-write", true},
{"read-only", false},
{"master",false},
{"slave",false},
{"preferSlave",false},
}
dsn :=common.GetOGDsnBase()
for _,attr :=range attrs{
ndsn :=dsn+" target_session_attrs=" + attr.attr
common.TestConnExpect("opengauss",ndsn, attr.expect)
}
}
func TestBinaryParameters(t *testing.T){
dsn :=common.GetOGDsnBase()
baseDB,err:=sql.Open("opengauss",dsn)
if err!=nil{
log.Fatal(err)
}
defer baseDB.Close()
common.TruncateTable(baseDB,"people")
binParams:=[]string{
"yes",
"no",
}
disablePreparedBinaryResult:=[]string{
"yes",
"no",
}
for _,v1:=range binParams{
for _,v2:=range disablePreparedBinaryResult{
ndsn:=dsn+" binary_parameters="+v1+" disable_prepared_binary_result="+v2
db,err :=sql.Open("opengauss",ndsn)
if err!=nil{
log.Fatal("["+ndsn+"] error,",err)
}
stmt,err :=db.Prepare("insert into people values(:id,:name)")
if err!=nil{
log.Fatal(err)
}
_,err = stmt.Exec(sql.Named("id",10000),sql.Named("name","test"))
if err!=nil{
log.Fatal(err)
}
stmt.Close()
db.Close()
}
}
var rows int
baseDB.QueryRow("select count(*) from people where id=10000 and name='test'").Scan(&rows)
if rows != 4{
log.Fatal("query res error")
}
}
func TestErasePassword(t *testing.T){
pws :=[]string{
//"secretpw",
//"secretpw@",
//"@secretpw",
//"@secretpw@",
"secretpw\t",
"secretpw\n",
"secretpw\v",
"secretpw\f",
"secretpw,",
}
errorMode :=[]string{
"xxx",
"abc=",
"abc",
"xxx\\",
"binary_parameters",
"binary_parameters=",
"binary_parameters=notval",
}
port:=common.GetPropertyPort()
dbname:=common.GetPropertyDbName()
host :=common.GetPropertyHost()
user:="notexist"
prefixDsn:="user="+user+" dbname="+dbname+" port="+port+" host="+host
prefixUrl:="opengauss://"+user+":"
suffixUrl:="@"+common.GetPropertyHost()+":"+common.GetPropertyPort()+"/"+common.GetPropertyDbName()+"?sslmode=disable&"
regPw := regexp.MustCompile("secretpw")
for _,emode :=range errorMode{
for _,pw:=range pws{
ndsn:=prefixDsn+emode+" password="+pw
nurl :=prefixUrl+pw+suffixUrl+emode
openinfo :=[]string{
ndsn,
nurl,
}
for _,info :=range openinfo{
db,err:=sql.Open("opengauss",info)
if err!=nil{
s:=err.Error()
if regPw.MatchString(s){
log.Fatal("Error, password erase fail, dataSourceName="+info)
}
continue
}
stmt,err :=db.Prepare("select 1")
if err!=nil{
s:=err.Error()
if regPw.MatchString(s){
log.Fatal("Error, password erase fail, dataSourceName="+info)
}
continue
}
stmt.Close()
db.Close()
}
}
}
}
func TestErasePassword2(t *testing.T) {
sqlconn0 := "opengauss://test:Gauss_234@10.244.49.15:17777/godb?loggerLevel=debug1&password=Gauss_234&password= Gauss_234&password =Gauss_234&password = Gauss_234"
sqlconn00 := "opengauss://test:Gauss_234@10.244.49.15:17777/godb?loggerLevel=debug1&password='Gauss_234'&password= 'Gauss_234'&password ='Gauss_234'&password = 'Gauss_234'"
sqlconn1 := "opengauss://test:Gauss_234@10.244.49.15:17777/godb?loggerLevel=debug1&password=Gauss_234&sslpassword=Gauss_234&sslpassword= Gauss_234&sslpassword =Gauss_234&&sslpassword = Gauss_234"
sqlconn2 := "opengauss://test:Gauss_234@10.244.49.15:17777/godb?loggerLevel=debug1&password=Gauss_234&sslpassword='Gauss_234'&sslpassword= 'Gauss_234'&sslpassword ='Gauss_234'&&sslpassword = 'Gauss_234'"
sqlconns :=[]string{sqlconn00,sqlconn0, sqlconn1,sqlconn2}
for _,conn :=range sqlconns{
_, err := sql.Open("opengauss", conn)
if err == nil {
log.Fatal("expect error")
}
errStr := err.Error()
f,err := regexp.MatchString("Gauss_234", errStr)
if err!=nil{
log.Fatal(err)
}
if f {
log.Fatal("erase password failed. ", errStr)
}
}
}
func TestConnParameter(t *testing.T) {
fmt.Println("********************No Runtime Parameter********************")
dsnStr := common.GetOGDsnBase()
parameters := []string {
" connect_timeout=5",
" connect_timeout=-1",
" connect_timeout=0",
" connect_timeout=22.33",
" disable_prepared_binary_result=no",
" disable_prepared_binary_result=yes",
" disable_prepared_binary_result=NO",
" disable_prepared_binary_result=YES",
" disable_prepared_binary_result=n",
" disable_prepared_binary_result=y",
" disable_prepared_binary_result=N",
" disable_prepared_binary_result=Y",
" binary_parameters=yes",
" binary_parameters=YES",
" binary_parameters=NO",
" binary_parameters=no",
" binary_parameters=y",
" binary_parameters=Y",
" binary_parameters=n",
" binary_parameters=N",
}
for _, param := range parameters {
db, err:= sql.Open("opengauss", dsnStr+param)
if err != nil {
arr := strings.Split(fmt.Sprintf("%v", err),":")
fmt.Printf("FAILED:%v, parse error:%v\n", param, arr[len(arr)-1])
} else {
fmt.Printf("PARSE SUCCESS:%v\n", param)
db.Close()
}
}
}
func TestConnRuntimeParameter(t *testing.T) {
fmt.Println("********************Runtime Parameter********************")
dsnStr := common.GetOGDsnBase()
infos := []struct {
parameter, value string
} {
{" application_name", "go-test-api"},
{" application_name", "default"},
{ " application_name", "123456"},
{" datestyle", "SQL,YMD"},
{" datestyle", "default"},
{" datestyle", "SQL, YMD"},
{" timezone", "GMT"},
{" timezone", "ABC"},
{" timezone", "+8"},
{" timezone", "-05:00"},
{" timezone", "+22"},
{" timezone", "24"},
{" timezone", "-23"},
{" timezone", "25"},
{" timezone", "-24"},
{" timezone", "1234"},
{" client_encoding", "SQL_ASCII"},
{" client_encoding", "GBK"},
{" client_encoding", "UTF8"},
{" client_encoding", "ABCD"},
{" client_encoding", "default"},
{" client_encoding", "0"},
{" search_path", "pqgotest"},
{" search_path", "12345"},
{" search_path", "default"},
{" extra_float_digits", "0"},
{" extra_float_digits", "-15"},
{" extra_float_digits", "3"},
{" extra_float_digits", "4"},
{" extra_float_digits", "-16"},
{" extra_float_digits", "3.4"},
}
for _, info := range infos {
dsn := dsnStr + info.parameter + "=" + info.value
db, err:= sql.Open("opengauss", dsn)
if err != nil {
arr := strings.Split(fmt.Sprintf("%v", err),":")
fmt.Printf("FAILED:%v, parse error:%v\n", info.parameter + "=" + info.value, arr[len(arr)-1])
continue
}
var f1 string
err = db.QueryRow("show " + info.parameter).Scan(&f1)
if err != nil {
arr := strings.Split(fmt.Sprintf("%v", err),":")
fmt.Printf("FAILED:%v, parse error:%v\n", info.parameter + "=" + info.value, arr[len(arr)-1])
} else {
fmt.Printf("RESULT: show%s: %s\n", info.parameter, f1)
}
db.Close()
}
}

View File

@ -1,968 +0,0 @@
package bench
import (
"database/sql"
"fmt"
"github.com/GaussKernel/common"
"testing"
"time"
)
func TestDatatype(t *testing.T){
db :=common.GetDefaultOGDB()
common.CheckErr(db.Exec("drop table if exists mydata"))
common.CheckErr(db.Exec("create table mydata(a int, b varchar(100), d decimal(20,10),e tinyint, f boolean, g raw,k tsrange)"))
common.CheckErr(db.Exec("INSERT INTO mydata VALUES (1,'abc',1.234,127,false,'123456','[2010-01-01 14:30, 2010-01-01 15:30]')"))
common.CheckErr(db.Exec("INSERT INTO mydata VALUES (2,'abc',1.234,127,true,'223456','[2010-01-01 14:30, 2010-01-01 15:30]')"))
common.IterScanDbTable(db,"mydata")
common.CheckErr(db.Exec("drop table if exists mydata"))
db.Close()
}
func TestNumber(t *testing.T) {
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testInteger",
"create table testInteger(f1 TINYINT, f2 SMALLINT, f3 INTEGER, f4 INTEGER, f5 BIGINT)",
"insert into testInteger values(255, 32767, 2147483647, 2147483647, 9223372036854775807)",
"insert into testInteger values(0, 0, 0, 0, 0)",
"insert into testInteger values(0, -32768, -2147483648, -2147483648, -9223372036854775808)",
"drop table if exists testNumeric",
"create table testNumeric(f1 numeric, f2 decimal(10,4), f3 number(20))",
"insert into testNumeric values(437164.41907403710, 71.6149329, 34819647147.0438148321)",
"drop table if exists testFloat",
"create table testFloat(f1 float4, f2 float8, f3 float, f4 binary_double, f5 dec(10,4), f6 integer(20,2))",
"insert into testFloat values(4164.5190, 34847.0438321, 437164.419403710, 71.6149329, 71.693291, 34819647147.04381321)",
"drop table if exists testSerial",
"create table testSerial(f1 smallserial, f2 serial, f3 bigserial)",
"insert into testSerial values(default, default, default)",
}
for _, s := range sqls {
_, err = db.Exec(s)
if err != nil {
t.Fatal(err)
}
}
infos := []struct {
descrip, datatype string
} {
{"----------result is golang string----------", "string"},
{"\n----------result is golang interface{}----------", "interface{}"},
{"\n----------result is golang []byte----------", "[]byte"},
{"\n----------result is golang sql.RawBytes----------", "sql.RawBytes"},
{"\n----------result is golang bool----------", "bool"},
{"\n----------result is golang integer----------", "integer"},
{"\n----------result is golang float----------", "float"},
{"\n----------result is golang others----------", "others"},
}
queryInfos := []struct {
typeDescrip, querySql string
} {
{"********************Integer Datatype********************", "select *,i1toi2(123) from testInteger"},
{"\n********************Numeric Datatype********************", "select *,* from testNumeric"},
{"\n********************Float Datatype********************", "select * from testFloat"},
{"\n********************Serial Datatype********************", "select *,* from testSerial"},
}
row, err := db.Query("select 1")
if err != nil {
t.Fatal(err)
}
defer row.Close()
for _, q := range queryInfos {
fmt.Println(q.typeDescrip)
for _, info := range infos {
fmt.Println(info.descrip)
row, err = db.Query(q.querySql)
if err != nil {
t.Fatal(err)
}
for row.Next() {
switch info.datatype {
case "string":
var f1, f2, f3, f4, f5, f6 string
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "interface{}":
var f1, f2, f3, f4, f5, f6 interface{}
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "[]byte":
var f1, f2, f3, f4, f5, f6 []byte
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "sql.RawBytes":
var f1, f2, f3, f4, f5, f6 sql.RawBytes
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "bool":
var f1, f2, f3, f4, f5, f6 bool
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "integer":
var f1, f2, f3, f4, f5, f6 int64
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "float":
var f1, f2, f3, f4, f5, f6 float64
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "others":
var f1, f2, f3, f4, f5, f6 time.Time
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
}
}
if err = row.Err(); err != nil {
t.Fatal(err)
}
}
}
}
func TestDateAndTime(t *testing.T) {
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testTime",
"create table testTime(f1 date, f2 time, f3 timetz, f4 timestamp, f5 timestamptz)",
"insert into testTime values('2022-01-28', '14:56:38.687146', '14:56:38.687146 GMT', '2022-01-28 14:56:38.687146', '2022-01-28 15:01:28.382264 GMT')",
"drop table if exists testInterval",
"create table testInterval(f1 smalldatetime, f2 interval day(1) to second(2), f3 interval hour(2), f4 reltime, f5 abstime)",
"insert into testInterval values('2022-01-28 15:01:00', interval '3' day, interval '2' year, '2022 year 1 mons 28 days 15:00:00', '2022-1-28 15:00:00 GMT')",
}
for _, s := range sqls {
_, err = db.Exec(s)
if err != nil {
t.Fatal(err)
}
}
infos := []struct {
descrip, datatype string
} {
{"----------result is golang string----------", "string"},
{"\n----------result is golang interface{}----------", "interface{}"},
{"\n----------result is golang []byte----------", "[]byte"},
{"\n----------result is golang sql.RawBytes----------", "sql.RawBytes"},
{"\n----------result is golang time.Time----------", "time.Time"},
{"\n----------result is golang others----------", "others"},
}
queryInfos := []struct {
typeDescrip, querySql string
} {
{"********************Datetime Datatype********************", "select * from testTime"},
{"\n********************Interval Datatype********************", "select * from testInterval"},
}
row, err := db.Query("select 1")
if err != nil {
t.Fatal(err)
}
defer row.Close()
for _, q := range queryInfos {
fmt.Println(q.typeDescrip)
for _, info := range infos {
fmt.Println(info.descrip)
row, err = db.Query(q.querySql)
if err != nil {
t.Fatal(err)
}
for row.Next() {
switch info.datatype {
case "string":
var f1, f2, f3, f4, f5 string
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v\n", f1, f2, f3, f4, f5)
}
case "interface{}":
var f1, f2, f3, f4, f5 interface{}
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v\n", f1, f2, f3, f4, f5)
}
case "[]byte":
var f1, f2, f3, f4, f5 []byte
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v\n", f1, f2, f3, f4, f5)
}
case "sql.RawBytes":
var f1, f2, f3, f4, f5 sql.RawBytes
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v\n", f1, f2, f3, f4, f5)
}
case "time.Time":
var f1, f2, f3, f4, f5 time.Time
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v\n", f1, f2, f3, f4, f5)
}
case "others":
var f1, f2, f3, f4, f5 int64
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v\n", f1, f2, f3, f4, f5)
}
}
}
if err = row.Err(); err != nil {
t.Fatal(err)
}
}
}
}
func TestMoneyAndBool(t *testing.T) {
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testMoney",
"create table testMoney(f1 money)",
"insert into testMoney values(100), (2090.11), ('$12.432')",
"drop table if exists testBool",
"create table testBool(f1 bool)",
"insert into testBool values('t'),('no'),(6473)",
}
for _, s := range sqls {
_, err = db.Exec(s)
if err != nil {
t.Fatal(err)
}
}
infos := []struct {
descrip, datatype string
} {
{"----------result is golang string----------", "string"},
{"\n----------result is golang interface{}----------", "interface{}"},
{"\n----------result is golang []byte----------", "[]byte"},
{"\n----------result is golang sql.RawBytes----------", "sql.RawBytes"},
{"\n----------result is golang bool----------", "bool"},
{"\n----------result is golang others----------", "others"},
}
queryInfos := []struct {
typeDescrip, querySql string
} {
{"********************Money Datatype********************", "select * from testMoney"},
{"\n********************Bool Datatype********************", "select * from testBool"},
}
row, err := db.Query("select 1")
if err != nil {
t.Fatal(err)
}
defer row.Close()
for _, q := range queryInfos {
fmt.Println(q.typeDescrip)
for _, info := range infos {
fmt.Println(info.descrip)
row, err = db.Query(q.querySql)
if err != nil {
t.Fatal(err)
}
for row.Next() {
switch info.datatype {
case "string":
var f1 string
err = row.Scan(&f1)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v\n", f1)
}
case "interface{}":
var f1 interface{}
err = row.Scan(&f1)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v\n", f1)
}
case "[]byte":
var f1 []byte
err = row.Scan(&f1)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v\n", f1)
}
case "sql.RawBytes":
var f1 sql.RawBytes
err = row.Scan(&f1)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v\n", f1)
}
case "bool":
var f1 bool
err = row.Scan(&f1)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v\n", f1)
}
case "others":
var f1 int64
err = row.Scan(&f1)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v\n", f1)
}
}
}
if err = row.Err(); err != nil {
t.Fatal(err)
}
}
}
}
func TestCharacterAndBinary(t *testing.T) {
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testCharacter",
"create table testCharacter(f1 char(1), f2 nchar(4), f3 varchar(10), f4 varchar2(10), f5 nvarchar2(2), f6 text, f7 clob)",
"insert into testCharacter values('a', '你', 'bbb', '2090.11', '中国', 'dgjqgdgfakgfekikgqif', 'dgjqgdgfakgfekikgqif')",
"insert into testCharacter values('1', 'true', '0', 'false', 'f', 'TRUE', 'FALSE')",
"insert into testCharacter values('1', '2000', '0', '4', '5', '6', '7')",
"drop table if exists testBinary",
"create table testBinary(f1 blob, f2 raw, f3 bytea)",
"insert into testBinary values('123','abc','deqde')",
}
for _, s := range sqls {
_, err = db.Exec(s)
if err != nil {
t.Fatal(err)
}
}
infos := []struct {
descrip, datatype string
} {
{"----------result is golang string----------", "string"},
{"\n----------result is golang interface{}----------", "interface{}"},
{"\n----------result is golang []byte----------", "[]byte"},
{"\n----------result is golang sql.RawBytes----------", "sql.RawBytes"},
{"\n----------result is golang bool----------", "bool"},
{"\n----------result is golang integer----------", "integer"},
{"\n----------result is golang float----------", "float"},
{"\n----------result is golang others----------", "others"},
}
queryInfos := []struct {
typeDescrip, querySql string
} {
{"********************Character Datatype********************", "select * from testCharacter"},
{"\n********************Binary Datatype********************", "select *,*,f1 from testBinary"},
}
row, err := db.Query("select 1")
if err != nil {
t.Fatal(err)
}
defer row.Close()
for _, q := range queryInfos {
fmt.Println(q.typeDescrip)
for _, info := range infos {
fmt.Println(info.descrip)
row, err = db.Query(q.querySql)
if err != nil {
t.Fatal(err)
}
for row.Next() {
switch info.datatype {
case "string":
var f1, f2, f3, f4, f5, f6, f7 string
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6, &f7)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v, F7:%v\n", f1, f2, f3, f4, f5, f6, f7)
}
case "interface{}":
var f1, f2, f3, f4, f5, f6, f7 interface{}
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6, &f7)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v, F7:%v\n", f1, f2, f3, f4, f5, f6, f7)
}
case "[]byte":
var f1, f2, f3, f4, f5, f6, f7 []byte
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6, &f7)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v, F7:%v\n", f1, f2, f3, f4, f5, f6, f7)
}
case "sql.RawBytes":
var f1, f2, f3, f4, f5, f6, f7 sql.RawBytes
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6, &f7)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v, F7:%v\n", f1, f2, f3, f4, f5, f6, f7)
}
case "bool":
var f1, f2, f3, f4, f5, f6, f7 bool
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6, &f7)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v, F7:%v\n", f1, f2, f3, f4, f5, f6, f7)
}
case "integer":
var f1, f2, f3, f4, f5, f6, f7 int64
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6, &f7)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v, F7:%v\n", f1, f2, f3, f4, f5, f6, f7)
}
case "float":
var f1, f2, f3, f4, f5, f6, f7 float64
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6, &f7)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v, F7:%v\n", f1, f2, f3, f4, f5, f6, f7)
}
case "others":
var f1, f2, f3, f4, f5, f6, f7 time.Time
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6, &f7)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v, F7:%v\n", f1, f2, f3, f4, f5, f6, f7)
}
}
}
if err = row.Err(); err != nil {
t.Fatal(err)
}
}
}
}
func TestGeometry(t *testing.T) {
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testGeometry",
"create table testGeometry(f1 point, f2 lseg, f3 box, f4 path, f5 polygon, f6 circle)",
"insert into testGeometry values('1,2', '1,2,3,4', '1,1,2,2', '1,0,2,0,3,4', '1,0,2,0,3,4', '0,0,2')",
}
for _, s := range sqls {
_, err = db.Exec(s)
if err != nil {
t.Fatal(err)
}
}
infos := []struct {
descrip, datatype string
} {
{"----------result is golang string----------", "string"},
{"\n----------result is golang interface{}----------", "interface{}"},
{"\n----------result is golang []byte----------", "[]byte"},
{"\n----------result is golang sql.RawBytes----------", "sql.RawBytes"},
{"\n----------result is golang others----------", "others"},
}
fmt.Println("********************Geometry Datatype********************")
row, err := db.Query("select 1")
if err != nil {
t.Fatal(err)
}
defer row.Close()
for _, info := range infos {
fmt.Println(info.descrip)
row, err = db.Query("select * from testGeometry")
if err != nil {
t.Fatal(err)
}
for row.Next() {
switch info.datatype {
case "string":
var f1, f2, f3, f4, f5, f6 string
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "interface{}":
var f1, f2, f3, f4, f5, f6 interface{}
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "[]byte":
var f1, f2, f3, f4, f5, f6 []byte
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "sql.RawBytes":
var f1, f2, f3, f4, f5, f6 sql.RawBytes
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "others":
var f1, f2, f3, f4, f5, f6 time.Time
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
}
}
if err = row.Err(); err != nil {
t.Fatal(err)
}
}
}
func TestMacBitTS(t *testing.T) {
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testMac",
"create table testMac(f1 cidr, f2 inet, f3 macaddr)",
"insert into testMac values('192.168/24', '192.168.1.5/8', '08:00:2b:01:02:03')",
"insert into testMac values('192.1', '192.1.2.1', '08002b:010203')",
"drop table if exists testBit",
"create table testBit(f1 serial, f2 bit(3), f3 bit varying(5))",
"insert into testBit values(default, B'101', B'1011')",
"insert into testBit values(default, B'10'::bit(3), B'01')",
"drop table if exists testTs",
"create table testTs(f1 serial, f2 tsvector, f3 tsquery)",
"insert into testTs values(default, 'a fat cat sat on a mat and ate a fat rat', 'fat & rat')",
}
for _, s := range sqls {
_, err = db.Exec(s)
if err != nil {
t.Fatal(err)
}
}
infos := []struct {
descrip, datatype string
} {
{"----------result is golang string----------", "string"},
{"\n----------result is golang interface{}----------", "interface{}"},
{"\n----------result is golang []byte----------", "[]byte"},
{"\n----------result is golang sql.RawBytes----------", "sql.RawBytes"},
{"\n----------result is golang others----------", "others"},
}
queryInfos := []struct {
typeDescrip, querySql string
} {
{"********************NetAddr Datatype********************", "select * from testMac"},
{"\n********************BitStr Datatype********************", "select * from testBit"},
{"\n********************TextSearch Datatype********************", "select * from testTs"},
}
row, err := db.Query("select 1")
if err != nil {
t.Fatal(err)
}
defer row.Close()
for _, q := range queryInfos {
fmt.Println(q.typeDescrip)
for _, info := range infos {
fmt.Println(info.descrip)
row, err = db.Query(q.querySql)
if err != nil {
t.Fatal(err)
}
for row.Next() {
switch info.datatype {
case "string":
var f1, f2, f3 string
err = row.Scan(&f1, &f2, &f3)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v\n", f1, f2, f3)
}
case "interface{}":
var f1, f2, f3 interface{}
err = row.Scan(&f1, &f2, &f3)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v\n", f1, f2, f3)
}
case "[]byte":
var f1, f2, f3 []byte
err = row.Scan(&f1, &f2, &f3)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v\n", f1, f2, f3)
}
case "sql.RawBytes":
var f1, f2, f3 sql.RawBytes
err = row.Scan(&f1, &f2, &f3)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v\n", f1, f2, f3)
}
case "others":
var f1, f2, f3 time.Time
err = row.Scan(&f1, &f2, &f3)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v\n", f1, f2, f3)
}
}
}
if err = row.Err(); err != nil {
t.Fatal(err)
}
}
}
}
func TestRangeAndJson(t *testing.T) {
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testRange",
"create table testRange(f1 int4range, f2 int8range, f3 numrange, f4 tsrange, f5 tstzrange, f6 daterange)",
"insert into testRange values('[-1,10]', '[-2910,7738)', '(367.431, 731.431]', '(2010-01-01 14:30, 2010-01-04 15:30)', " +
"'[2022-01-28 15:01:28.382264 GMT , 2022-01-30 15:01:28.382264 GMT]', '[2022-01-28, 2022-01-31]')",
"drop table if exists testJson",
"create table testJson(f1 json, f2 jsonb, f3 json, f4 jsonb, f5 json, f6 jsonb)",
"insert into testJson values(' [1, \" a \", {\"a\" :1 }] ', ' [1, \" a \", {\"a\" :1 }] ', " +
"'{\"a\" : 1, \"a\" : 2}', '{\"a\" : 1, \"a\" : 2}', " +
"'{\"aa\" : 1, \"b\" : 2, \"a\" : 3}', '{\"aa\" : 1, \"b\" : 2, \"a\" : 3}')",
}
for _, s := range sqls {
_, err = db.Exec(s)
if err != nil {
t.Fatal(err)
}
}
infos := []struct {
descrip, datatype string
} {
{"----------result is golang string----------", "string"},
{"\n----------result is golang interface{}----------", "interface{}"},
{"\n----------result is golang []byte----------", "[]byte"},
{"\n----------result is golang sql.RawBytes----------", "sql.RawBytes"},
{"\n----------result is golang others----------", "others"},
}
queryInfos := []struct {
typeDescrip, querySql string
} {
{"********************Range Datatype********************", "select * from testRange"},
{"\n********************Json Datatype********************", "select * from testJson"},
}
row, err := db.Query("select 1")
if err != nil {
t.Fatal(err)
}
defer row.Close()
for _, q := range queryInfos {
fmt.Println(q.typeDescrip)
for _, info := range infos {
fmt.Println(info.descrip)
row, err = db.Query(q.querySql)
if err != nil {
t.Fatal(err)
}
for row.Next() {
switch info.datatype {
case "string":
var f1, f2, f3, f4, f5, f6 string
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "interface{}":
var f1, f2, f3, f4, f5, f6 interface{}
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "[]byte":
var f1, f2, f3, f4, f5, f6 []byte
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "sql.RawBytes":
var f1, f2, f3, f4, f5, f6 sql.RawBytes
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
case "others":
var f1, f2, f3, f4, f5, f6 bool
err = row.Scan(&f1, &f2, &f3, &f4, &f5, &f6)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v, F5:%v, F6:%v\n", f1, f2, f3, f4, f5, f6)
}
}
}
if err = row.Err(); err != nil {
t.Fatal(err)
}
}
}
}
func TestOthers(t *testing.T) {
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testOthers",
"create table testOthers(f1 uuid, f2 hash16, f3 hash32, set hll)",
"insert into testOthers values('A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11', 'e697da2eaa3a775b', '685847ed1fe38e18f6b0e2b18c00edee', hll_empty())",
}
for _, s := range sqls {
_, err = db.Exec(s)
if err != nil {
t.Fatal(err)
}
}
infos := []struct {
descrip, datatype string
} {
{"----------result is golang string----------", "string"},
{"\n----------result is golang interface{}----------", "interface{}"},
{"\n----------result is golang []byte----------", "[]byte"},
{"\n----------result is golang sql.RawBytes----------", "sql.RawBytes"},
{"\n----------result is golang others----------", "others"},
}
fmt.Println("********************Other Datatype********************")
row, err := db.Query("select 1")
if err != nil {
t.Fatal(err)
}
defer row.Close()
for _, info := range infos {
fmt.Println(info.descrip)
row, err = db.Query("select * from testOthers")
if err != nil {
t.Fatal(err)
}
for row.Next() {
switch info.datatype {
case "string":
var f1, f2, f3, f4 string
err = row.Scan(&f1, &f2, &f3, &f4)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v\n", f1, f2, f3, f4)
}
case "interface{}":
var f1, f2, f3, f4 interface{}
err = row.Scan(&f1, &f2, &f3, &f4)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v\n", f1, f2, f3, f4)
}
case "[]byte":
var f1, f2, f3, f4 []byte
err = row.Scan(&f1, &f2, &f3, &f4)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v\n", f1, f2, f3, f4)
}
case "sql.RawBytes":
var f1, f2, f3, f4 sql.RawBytes
err = row.Scan(&f1, &f2, &f3, &f4)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v\n", f1, f2, f3, f4)
}
case "others":
var f1, f2, f3, f4 bool
err = row.Scan(&f1, &f2, &f3, &f4)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("F1:%v, F2:%v, F3:%v, F4:%v\n", f1, f2, f3, f4)
}
}
}
if err = row.Err(); err != nil {
t.Fatal(err)
}
}
}

View File

@ -1,171 +0,0 @@
package bench
import (
"context"
"database/sql"
"fmt"
"github.com/GaussKernel/common"
"strings"
"testing"
"time"
)
func TestDBExec(t *testing.T) {
fmt.Println("********************DB Exec********************")
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err != nil {
t.Fatal(err)
}
sqls := []string {
"drop table if exists testExec",
"create table testExec(f1 int, f2 varchar(20), f3 number, f4 timestamptz, f5 boolean)",
"insert into testExec values(1, 'abcdefg', 123.3, '2022-02-08 10:30:43.31 +08', true)",
"insert into testExec values(:f1, :f2, :f3, :f4, :f5)",
}
inF1 := []int{2, 3, 4, 5, 6}
intF2 := []string{"hello world", "华为", "北京2022冬奥会", "nanjing", "研究所"}
intF3 := []float64{641.43, 431.54, 5423.52, 665537.63, 6503.1}
intF4 := []time.Time{
time.Date(2022, 2, 8, 10, 35, 43, 623431, time.Local),
time.Date(2022, 2, 10, 19, 11, 54, 353431, time.Local),
time.Date(2022, 2, 12, 6, 11, 15, 636431, time.Local),
time.Date(2022, 2, 14, 4, 51, 22, 747653, time.Local),
time.Date(2022, 2, 16, 13, 45, 55, 674636, time.Local),
}
intF5 := []bool{false, true, false, true, true}
for _, str := range sqls {
if strings.Contains(str, ":f") {
for i, _ := range inF1 {
_, err := db.Exec(str, inF1[i], intF2[i], intF3[i], intF4[i], intF5[i])
if err != nil {
t.Fatal(err)
}
}
} else {
_, err = db.Exec(str)
if err != nil {
t.Fatal(err)
}
}
}
var f1 int
var f2 string
var f3 float64
var f4 time.Time
var f5 bool
err = db.QueryRow("select * from testExec").Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
row, err :=db.Query("select * from testExec where f1 > :1", 1)
if err != nil {
t.Fatal(err)
}
defer row.Close()
for row.Next() {
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
}
}
func TestDBExecContext(t *testing.T) {
fmt.Println("********************DB ExecContext********************")
ctx := context.Background()
ctx2SecondTimeout, cancelFunc2SecondTimeout := context.WithTimeout(ctx, 2 * time.Second)
defer cancelFunc2SecondTimeout()
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
// Ping database connection with 2 second timeout
err = db.PingContext(ctx2SecondTimeout)
if err != nil {
t.Fatal(err)
}
sqls := []string {
"drop table if exists testExecContext",
"create table testExecContext(f1 int, f2 varchar(20), f3 number, f4 timestamptz, f5 boolean)",
"insert into testExecContext values(1, 'abcdefg', 123.3, '2022-02-08 10:30:43.31 +08', true)",
"insert into testExecContext values(:f1, :f2, :f3, :f4, :f5)",
}
inF1 := []int{2, 3, 4, 5, 6}
intF2 := []string{"hello world", "华为", "北京2022冬奥会", "nanjing", "研究所"}
intF3 := []float64{641.43, 431.54, 5423.52, 665537.63, 6503.1}
intF4 := []time.Time{
time.Date(2022, 2, 8, 10, 35, 43, 623431, time.Local),
time.Date(2022, 2, 10, 19, 11, 54, 353431, time.Local),
time.Date(2022, 2, 12, 6, 11, 15, 636431, time.Local),
time.Date(2022, 2, 14, 4, 51, 22, 747653, time.Local),
time.Date(2022, 2, 16, 13, 45, 55, 674636, time.Local),
}
intF5 := []bool{false, true, false, true, true}
for _, str := range sqls {
if strings.Contains(str, ":f") {
for i, _ := range inF1 {
_, err := db.ExecContext(ctx2SecondTimeout, str, inF1[i], intF2[i], intF3[i], intF4[i], intF5[i])
if err != nil {
t.Fatal(err)
}
}
} else {
_, err = db.ExecContext(ctx2SecondTimeout, str)
if err != nil {
t.Fatal(err)
}
}
}
var f1 int
var f2 string
var f3 float64
var f4 time.Time
var f5 bool
err = db.QueryRowContext(ctx2SecondTimeout, "select * from testExecContext").Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
row, err :=db.QueryContext(ctx2SecondTimeout, "select * from testExecContext where f1 > :1", 1)
if err != nil {
t.Fatal(err)
}
defer row.Close()
for row.Next() {
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
}
}

View File

@ -1,60 +0,0 @@
package bench
import (
"context"
"database/sql"
"flag"
. "github.com/GaussKernel/common"
"log"
"os"
"os/signal"
"testing"
"time"
)
func TestPing(t *testing.T) {
id := flag.Int64("id", 1, "person ID to find")
flag.Parse()
pool :=GetDefaultOGDB()
defer pool.Close()
CheckErr(pool.Exec("drop table if exists testping"))
CheckErr(pool.Exec("create table testping(id int,name varchar(8000))"))
CheckErr(pool.Exec("insert into testping values(:id,'abc')",*id))
ctx, stop := context.WithCancel(context.Background())
defer stop()
appSignal := make(chan os.Signal, 3)
signal.Notify(appSignal, os.Interrupt)
go func() {
select {
case <-appSignal:
stop()
}
}()
Ping(pool,ctx)
Query(pool, ctx, *id)
row,err := pool.Query("select * from testping")
CheckErr(err)
IterScanRows(row)
CheckErr(pool.Exec("drop table testping"))
}
func Ping(pool *sql.DB,ctx context.Context) {
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
if err := pool.PingContext(ctx); err != nil {
log.Fatalf("unable to connect to database: %v", err)
}
}
func Query(pool *sql.DB, ctx context.Context, id int64) {
ctx, cancel := context.WithTimeout(ctx, 5000*time.Second)
defer cancel()
row,err := pool.QueryContext(ctx, "select p.name from testping as p where p.id = :id", sql.Named("id", id))
CheckErr(err)
IterScanRows(row)
}

View File

@ -1,174 +0,0 @@
package bench
import (
"database/sql"
"fmt"
"github.com/GaussKernel/common"
"strings"
"testing"
"time"
)
func TestRows(t *testing.T) {
fmt.Println("********************Query with Row********************")
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testRows",
"create table testRows(f1 int, f2 varchar(20), f3 number, f4 timestamptz, f5 boolean)",
"insert into testRows values(1, 'abcdefg', 123.3, '2022-02-08 10:30:43.31 +08', true)",
"insert into testRows values(:f1, :f2, :f3, :f4, :f5)",
}
inF1 := []int{2, 3, 4, 5, 6}
intF2 := []string{"hello world", "华为", "北京2022冬奥会", "nanjing", "研究所"}
intF3 := []float64{641.43, 431.54, 5423.52, 665537.63, 6503.1}
intF4 := []time.Time{
time.Date(2022, 2, 8, 10, 35, 43, 623431, time.Local),
time.Date(2022, 2, 10, 19, 11, 54, 353431, time.Local),
time.Date(2022, 2, 12, 6, 11, 15, 636431, time.Local),
time.Date(2022, 2, 14, 4, 51, 22, 747653, time.Local),
time.Date(2022, 2, 16, 13, 45, 55, 674636, time.Local),
}
intF5 := []bool{false, true, false, true, true}
for _, str := range sqls {
if strings.Contains(str, ":f") {
for i, _ := range inF1 {
_, err := db.Exec(str, inF1[i], intF2[i], intF3[i], intF4[i], intF5[i])
if err != nil {
t.Fatal(err)
}
}
} else {
_, err = db.Exec(str)
if err != nil {
t.Fatal(err)
}
}
}
var f1 int
var f2 string
var f3 float64
var f4 time.Time
var f5 bool
err = db.QueryRow("select * from testRows").Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
row, err :=db.Query("select * from testRows where f1 > :1", 1)
if err != nil {
t.Fatal(err)
}
defer row.Close()
column, err := row.Columns()
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("The name of columns is ")
for _, c := range column {
fmt.Printf("%v ",c)
}
}
fmt.Println("\n********************Query with Rows********************")
for row.Next() {
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
}
if row.Err() != nil {
t.Fatal(err)
}
}
func TestColumnType(t *testing.T) {
fmt.Println("********************Get ColumnType********************")
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testRows",
"create table testRows(f1 int, f2 VARCHAR(20), f3 number(20,4), f4 timestamptz, f5 boolean, f6 float, f7 money, f8 polygon, f9 bytea)",
"insert into testRows values(1, 'abcdefg', 123.3, '2022-02-08 10:30:43.31 +08', true, 123.3, '$12.432', '1,0,2,0,3,4', 'deqde')",
"insert into testRows values(:f1, :f2, :f3, :f4, :f5, :f6, :f7, :f8, :f9)",
}
inF1 := []int{2, 3, 4, 5, 6}
intF2 := []string{"hello world", "华为", "北京2022冬奥会", "nanjing", "研究所"}
intF3 := []float64{641.43, 431.54, 5423.52, 665537.63, 6503.1}
intF4 := []time.Time{
time.Date(2022, 2, 8, 10, 35, 43, 623431, time.Local),
time.Date(2022, 2, 10, 19, 11, 54, 353431, time.Local),
time.Date(2022, 2, 12, 6, 11, 15, 636431, time.Local),
time.Date(2022, 2, 14, 4, 51, 22, 747653, time.Local),
time.Date(2022, 2, 16, 13, 45, 55, 674636, time.Local),
}
intF5 := []bool{false, true, false, true, true}
intF6 := []float32{641.43, 431.54, 5423.52, 665537.63, 6503.1}
intF7 := []string{"123.11", "$1234.41", "11.41", "41.41", "$12.525"}
intF8 := []string{"1,2,3,4,5,6", "2,3,4,5,6,7", "3,4,5,6,7,8", "4,5,6,7,8,9", "1,3,5,7,9,0", "2,4,6,8,0,2"}
intF9 := []string{"rwr", "fafa", "dfaj", "fhfijheio", "fnak"}
for _, str := range sqls {
if strings.Contains(str, ":f") {
for i, _ := range inF1 {
_, err := db.Exec(str, inF1[i], intF2[i], intF3[i], intF4[i], intF5[i], intF6[i], intF7[i], intF8[i], intF9[i])
if err != nil {
t.Fatal(err)
}
}
} else {
_, err = db.Exec(str)
if err != nil {
t.Fatal(err)
}
}
}
row, err :=db.Query("select * from testRows where f1 > :1", 1)
if err != nil {
t.Fatal(err)
}
defer row.Close()
ct, err := row.ColumnTypes()
if err != nil {
t.Fatal(err)
} else {
for _, c := range ct {
name := c.Name()
typeName := c.DatabaseTypeName()
fmt.Printf("Name is %v, DatabaseTypeName is %v. ", name, typeName)
precision, scale, isDecimal := c.DecimalSize()
if isDecimal {
fmt.Printf("Decimal precision is %v, scale is %v. ", precision, scale)
}
length, isLen := c.Length()
if isLen {
fmt.Printf("Sting length is %v. ",length)
}
scanType := c.ScanType()
fmt.Printf("ScanType is %v.\n", scanType)
}
}
}

View File

@ -1,22 +0,0 @@
package bench
import (
"github.com/GaussKernel/common"
"testing"
)
func TestSSL(t *testing.T){
var dsn=common.GetOGDsnBase()
sslmodes :=[]string{
"disable",
"prefer",
"allow",
"require",
"verify-ca",
//"verify-full",
}
for _,sslmode:= range sslmodes{
fdsn :=dsn+" sslmode="+sslmode
common.TestConnExpect("opengauss",fdsn,true)
}
}

View File

@ -1,185 +0,0 @@
package bench
import (
"context"
"database/sql"
"fmt"
"github.com/GaussKernel/common"
"strings"
"testing"
"time"
)
func TestStmtExec(t *testing.T) {
fmt.Println("********************Stmt Exec********************")
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testStmtExec",
"create table testStmtExec(f1 int, f2 varchar(20), f3 number, f4 timestamptz, f5 boolean)",
"insert into testStmtExec values(1, 'abcdefg', 123.3, '2022-02-08 10:30:43.31 +08', true)",
"insert into testStmtExec values(:f1, :f2, :f3, :f4, :f5)",
}
inF1 := []int{2, 3, 4, 5, 6}
intF2 := []string{"hello world", "华为", "北京2022冬奥会", "nanjing", "研究所"}
intF3 := []float64{641.43, 431.54, 5423.52, 665537.63, 6503.1}
intF4 := []time.Time{
time.Date(2022, 2, 8, 10, 35, 43, 623431, time.Local),
time.Date(2022, 2, 10, 19, 11, 54, 353431, time.Local),
time.Date(2022, 2, 12, 6, 11, 15, 636431, time.Local),
time.Date(2022, 2, 14, 4, 51, 22, 747653, time.Local),
time.Date(2022, 2, 16, 13, 45, 55, 674636, time.Local),
}
intF5 := []bool{false, true, false, true, true}
for _, str := range sqls {
s, err := db.Prepare(str)
if err != nil {
t.Fatal(err)
}
if strings.Contains(str, ":f") {
for i, _ := range inF1 {
_, err := s.Exec(inF1[i], intF2[i], intF3[i], intF4[i], intF5[i])
if err != nil {
t.Fatal(err)
}
}
} else {
_, err = s.Exec()
if err != nil {
t.Fatal(err)
}
}
}
var f1 int
var f2 string
var f3 float64
var f4 time.Time
var f5 bool
s, err := db.Prepare("select * from testStmtExec")
if err != nil {
t.Fatal(err)
}
defer s.Close()
err =s.QueryRow().Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
s, err = db.Prepare("select * from testStmtExec where f1 > :1")
if err != nil {
t.Fatal(err)
}
row, err :=s.Query(1)
if err != nil {
t.Fatal(err)
}
defer row.Close()
for row.Next() {
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
}
}
func TestStmtExecCtx(t *testing.T) {
fmt.Println("********************Stmt ExecContext********************")
ctx := context.Background()
ctx2SecondTimeout, cancelFunc2SecondTimeout := context.WithTimeout(ctx, 2 * time.Second)
defer cancelFunc2SecondTimeout()
dsn := common.GetOGDsnBase()
db, err:= sql.Open("opengauss", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqls := []string {
"drop table if exists testStmtExecCtx",
"create table testStmtExecCtx(f1 int, f2 varchar(20), f3 number, f4 timestamptz, f5 boolean)",
"insert into testStmtExecCtx values(1, 'abcdefg', 123.3, '2022-02-08 10:30:43.31 +08', true)",
"insert into testStmtExecCtx values(:f1, :f2, :f3, :f4, :f5)",
}
inF1 := []int{2, 3, 4, 5, 6}
intF2 := []string{"hello world", "华为", "北京2022冬奥会", "nanjing", "研究所"}
intF3 := []float64{641.43, 431.54, 5423.52, 665537.63, 6503.1}
intF4 := []time.Time{
time.Date(2022, 2, 8, 10, 35, 43, 623431, time.Local),
time.Date(2022, 2, 10, 19, 11, 54, 353431, time.Local),
time.Date(2022, 2, 12, 6, 11, 15, 636431, time.Local),
time.Date(2022, 2, 14, 4, 51, 22, 747653, time.Local),
time.Date(2022, 2, 16, 13, 45, 55, 674636, time.Local),
}
intF5 := []bool{false, true, false, true, true}
for _, str := range sqls {
s, err := db.PrepareContext(ctx2SecondTimeout, str)
if err != nil {
t.Fatal(err)
}
if strings.Contains(str, ":f") {
for i, _ := range inF1 {
_, err := s.ExecContext(ctx2SecondTimeout, inF1[i], intF2[i], intF3[i], intF4[i], intF5[i])
if err != nil {
t.Fatal(err)
}
}
} else {
_, err = s.ExecContext(ctx2SecondTimeout)
if err != nil {
t.Fatal(err)
}
}
}
var f1 int
var f2 string
var f3 float64
var f4 time.Time
var f5 bool
s, err := db.PrepareContext(ctx2SecondTimeout, "select * from testStmtExecCtx")
if err != nil {
t.Fatal(err)
}
defer s.Close()
err = s.QueryRowContext(ctx2SecondTimeout).Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
s, err = db.PrepareContext(ctx2SecondTimeout, "select * from testStmtExecCtx where f1 > :1")
if err != nil {
t.Fatal(err)
}
row, err :=s.QueryContext(ctx2SecondTimeout, 1)
if err != nil {
t.Fatal(err)
}
defer row.Close()
for row.Next() {
err = row.Scan(&f1, &f2, &f3, &f4, &f5)
if err != nil {
t.Fatal(err)
} else {
fmt.Printf("f1:%v, f2:%v, f3:%v, f4:%v, f5:%v\n", f1, f2, f3, f4, f5)
}
}
}

View File

@ -1,166 +0,0 @@
testtx1 : 10
1
testtx1 : 10
abc
def
id: 1
name: abc
id: 1
name: abc
id: 1
name: abc
********************Tx without opts: Scenario 1********************
----------IN TX1: update and select----------
f1:1, f2:华为, f3:223.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:23.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: select, and TX1 not commit ----------
f1:1, f2:华为, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 commit----------
f1:1, f2:华为, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
********************Tx without opts: Scenario 2********************
----------IN TX1: update and select----------
f1:1, f2:华为, f3:223.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:23.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: update and select, and after TX1 roolback ----------
f1:1, f2:华为, f3:423.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-176.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 not commit----------
f1:1, f2:华为, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 commit----------
f1:1, f2:华为, f3:423.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-176.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
********************Tx without opts: Scenario 3********************
----------IN TX1: update and select----------
f1:1, f2:华为, f3:223.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:23.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: update and select, and after TX1 commit ----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 commit and TX2 not commit----------
f1:1, f2:华为, f3:223.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:23.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 commit and TX2 commit----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
********************Tx with opts: Scenario 1********************
****ISOLATION LEVEL:Read Uncommitted****
----------IN TX1: update and select----------
f1:1, f2:华为, f3:223.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:23.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: select, and TX1 not commit ----------
f1:1, f2:华为, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 commit----------
f1:1, f2:华为, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
****ISOLATION LEVEL:Read Committed****
----------IN TX1: update and select----------
f1:1, f2:华为, f3:223.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:23.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: select, and TX1 not commit ----------
f1:1, f2:华为, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 commit----------
f1:1, f2:华为, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
****ISOLATION LEVEL:Repeatable Read****
----------IN TX1: update and select----------
f1:1, f2:华为, f3:223.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:23.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: select, and TX1 not commit ----------
f1:1, f2:华为, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 commit----------
f1:1, f2:华为, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
********************Tx with opts: Scenario 2********************
****ISOLATION LEVEL:Read Uncommitted****
----------IN TX1: update and select----------
f1:1, f2:华为, f3:223.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:23.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: update and select, and after TX1 roolback ----------
f1:1, f2:华为, f3:423.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-176.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 not commit----------
f1:1, f2:华为, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 commit----------
f1:1, f2:华为, f3:423.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-176.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
****ISOLATION LEVEL:Read Committed****
----------IN TX1: update and select----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: update and select, and after TX1 roolback ----------
f1:1, f2:华为, f3:723.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-476.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 not commit----------
f1:1, f2:华为, f3:423.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-176.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 commit----------
f1:1, f2:华为, f3:723.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-476.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
****ISOLATION LEVEL:Repeatable Read****
----------IN TX1: update and select----------
f1:1, f2:华为, f3:823.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-576.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: update and select, and after TX1 roolback ----------
f1:1, f2:华为, f3:1023.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-776.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 not commit----------
f1:1, f2:华为, f3:723.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-476.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 rollback and TX2 commit----------
f1:1, f2:华为, f3:1023.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-776.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
********************Tx with opts: Scenario 3********************
****ISOLATION LEVEL:Read Uncommitted****
----------IN TX1: update and select----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: update and select, and after TX1 commit ----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 commit and TX2 not commit----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 commit and TX2 commit----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
****ISOLATION LEVEL:Read Committed****
----------IN TX1: update and select----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: update and select, and after TX1 commit ----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 commit and TX2 not commit----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 commit and TX2 commit----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
****ISOLATION LEVEL:Repeatable Read****
----------IN TX1: update and select----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN TX2: update and select, and after TX1 commit ----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 commit and TX2 not commit----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
----------IN DB: select, after TX1 commit and TX2 commit----------
f1:1, f2:华为, f3:523.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:研究所, f3:-276.7, f4:2022-02-08 10:30:43.31 +0800 CST, f5:false
********************Tx with opts and read only********************
In a read-only transaction, cannot execute write operation.
current transaction is aborted.
********************Tx without opts and stmt********************
f1:1, f2:华为, f3:423.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
********************Tx with opts and stmt********************
f1:1, f2:华为, f3:423.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
PASS

View File

@ -1,59 +0,0 @@
column_name: id
column_default: nextval('user_id_seq'::regclass)
is_nullable: NO
data_type: bigint
character_maximum_length: NULL
description: NULL
primarykey: true
uniquekey: false
column_name: name
column_default: NULL
is_nullable: YES
data_type: character varying
character_maximum_length: 255
description: NULL
primarykey: false
uniquekey: false
column_name: salt
column_default: NULL
is_nullable: YES
data_type: character varying
character_maximum_length: 255
description: NULL
primarykey: false
uniquekey: false
column_name: age
column_default: NULL
is_nullable: YES
data_type: integer
character_maximum_length: NULL
description: NULL
primarykey: false
uniquekey: false
column_name: passwd
column_default: NULL
is_nullable: YES
data_type: character varying
character_maximum_length: 200
description: NULL
primarykey: false
uniquekey: false
column_name: created
column_default: NULL
is_nullable: YES
data_type: timestamp without time zone
character_maximum_length: NULL
description: NULL
primarykey: false
uniquekey: false
column_name: updated
column_default: NULL
is_nullable: YES
data_type: timestamp without time zone
character_maximum_length: NULL
description: NULL
primarykey: false
uniquekey: false
indexname: user_pkey
indexdef: CREATE UNIQUE INDEX user_pkey ON "user" USING btree (id) TABLESPACE pg_default
PASS

View File

@ -1,38 +0,0 @@
1 <nil>
[name id]
1073741821 true
name string
0 0 false
false false
1073741821 true
id string
0 0 false
false false
name: liu
id: 3.34
[?column? ?column?]
1073741821 true
?column? string
0 0 false
false false
1073741821 true
?column? string
0 0 false
false false
?column?: liu
?column?: 3
1 <nil>
abc 123
********************Bind With Location********************
f1:2, f2:hello world, f3:641.43, f4:2022-02-08 10:35:43.000623 +0800 CST, f5:false
f1:3, f2:华为, f3:431.54, f4:2022-02-10 19:11:54.000353 +0800 CST, f5:true
f1:4, f2:北京2022冬奥会, f3:5423.52, f4:2022-02-12 06:11:15.000636 +0800 CST, f5:false
f1:5, f2:nanjing, f3:665537.63, f4:2022-02-14 04:51:22.000748 +0800 CST, f5:true
f1:6, f2:研究所, f3:6503.1, f4:2022-02-16 13:45:55.000675 +0800 CST, f5:true
********************Bind With Name********************
f1:2, f2:hello world, f3:641.43, f4:2022-02-08 10:35:43.000623 +0800 CST, f5:false
f1:3, f2:华为, f3:431.54, f4:2022-02-10 19:11:54.000353 +0800 CST, f5:true
f1:4, f2:北京2022冬奥会, f3:5423.52, f4:2022-02-12 06:11:15.000636 +0800 CST, f5:false
f1:5, f2:nanjing, f3:665537.63, f4:2022-02-14 04:51:22.000748 +0800 CST, f5:true
f1:6, f2:研究所, f3:6503.1, f4:2022-02-16 13:45:55.000675 +0800 CST, f5:true
PASS

View File

@ -1,54 +0,0 @@
********************No Runtime Parameter********************
PARSE SUCCESS: connect_timeout=5
FAILED: connect_timeout=-1, parse error: invalid connect_timeout (negative timeout)
PARSE SUCCESS: connect_timeout=0
FAILED: connect_timeout=22.33, parse error: invalid syntax)
PARSE SUCCESS: disable_prepared_binary_result=no
PARSE SUCCESS: disable_prepared_binary_result=yes
FAILED: disable_prepared_binary_result=NO, parse error: invalid disable_prepared_binary_result (unrecognized value "NO" for disable_prepared_binary_result)
FAILED: disable_prepared_binary_result=YES, parse error: invalid disable_prepared_binary_result (unrecognized value "YES" for disable_prepared_binary_result)
FAILED: disable_prepared_binary_result=n, parse error: invalid disable_prepared_binary_result (unrecognized value "n" for disable_prepared_binary_result)
FAILED: disable_prepared_binary_result=y, parse error: invalid disable_prepared_binary_result (unrecognized value "y" for disable_prepared_binary_result)
FAILED: disable_prepared_binary_result=N, parse error: invalid disable_prepared_binary_result (unrecognized value "N" for disable_prepared_binary_result)
FAILED: disable_prepared_binary_result=Y, parse error: invalid disable_prepared_binary_result (unrecognized value "Y" for disable_prepared_binary_result)
PARSE SUCCESS: binary_parameters=yes
FAILED: binary_parameters=YES, parse error: invalid binary_parameters (unrecognized value "YES" for binary_parameters)
FAILED: binary_parameters=NO, parse error: invalid binary_parameters (unrecognized value "NO" for binary_parameters)
PARSE SUCCESS: binary_parameters=no
FAILED: binary_parameters=y, parse error: invalid binary_parameters (unrecognized value "y" for binary_parameters)
FAILED: binary_parameters=Y, parse error: invalid binary_parameters (unrecognized value "Y" for binary_parameters)
FAILED: binary_parameters=n, parse error: invalid binary_parameters (unrecognized value "n" for binary_parameters)
FAILED: binary_parameters=N, parse error: invalid binary_parameters (unrecognized value "N" for binary_parameters)
********************Runtime Parameter********************
RESULT: show application_name: go-test-api
RESULT: show application_name: default
RESULT: show application_name: 123456
RESULT: show datestyle: SQL, YMD
RESULT: show datestyle: ISO, MDY
FAILED: datestyle=SQL, YMD, parse error: failed to parse as DSN (invalid dsn)
RESULT: show timezone: GMT
FAILED: timezone=ABC, parse error: bad connection
RESULT: show timezone: 08:00:00
RESULT: show timezone: -05:00
RESULT: show timezone: 22:00:00
RESULT: show timezone: 24:00:00
RESULT: show timezone: -23:00:00
RESULT: show timezone: 25:00:00
RESULT: show timezone: -24:00:00
RESULT: show timezone: 1234:00:00
RESULT: show client_encoding: SQL_ASCII
RESULT: show client_encoding: GBK
RESULT: show client_encoding: UTF8
FAILED: client_encoding=ABCD, parse error: bad connection
FAILED: client_encoding=default, parse error: bad connection
FAILED: client_encoding=0, parse error: bad connection
RESULT: show search_path: pqgotest
RESULT: show search_path: 12345
RESULT: show search_path: default
RESULT: show extra_float_digits: 0
RESULT: show extra_float_digits: -15
RESULT: show extra_float_digits: 3
FAILED: extra_float_digits=4, parse error: bad connection
FAILED: extra_float_digits=-16, parse error: bad connection
FAILED: extra_float_digits=3.4, parse error: bad connection
PASS

View File

@ -1,411 +0,0 @@
a: 1
b: abc
d: 1.2340000000
e: 127
f: false
g: 123456
k: ["2010-01-01 14:30:00","2010-01-01 15:30:00"]
a: 2
b: abc
d: 1.2340000000
e: 127
f: true
g: 223456
k: ["2010-01-01 14:30:00","2010-01-01 15:30:00"]
********************Integer Datatype********************
----------result is golang string----------
F1:255, F2:32767, F3:2147483647, F4:2147483647, F5:9223372036854775807, F6:123
F1:0, F2:0, F3:0, F4:0, F5:0, F6:123
F1:0, F2:-32768, F3:-2147483648, F4:-2147483648, F5:-9223372036854775808, F6:123
----------result is golang interface{}----------
F1:[50 53 53], F2:32767, F3:2147483647, F4:2147483647, F5:9223372036854775807, F6:123
F1:[48], F2:0, F3:0, F4:0, F5:0, F6:123
F1:[48], F2:-32768, F3:-2147483648, F4:-2147483648, F5:-9223372036854775808, F6:123
----------result is golang []byte----------
F1:[50 53 53], F2:[51 50 55 54 55], F3:[50 49 52 55 52 56 51 54 52 55], F4:[50 49 52 55 52 56 51 54 52 55], F5:[57 50 50 51 51 55 50 48 51 54 56 53 52 55 55 53 56 48 55], F6:[49 50 51]
F1:[48], F2:[48], F3:[48], F4:[48], F5:[48], F6:[49 50 51]
F1:[48], F2:[45 51 50 55 54 56], F3:[45 50 49 52 55 52 56 51 54 52 56], F4:[45 50 49 52 55 52 56 51 54 52 56], F5:[45 57 50 50 51 51 55 50 48 51 54 56 53 52 55 55 53 56 48 56], F6:[49 50 51]
----------result is golang sql.RawBytes----------
F1:[50 53 53], F2:[51 50 55 54 55], F3:[50 49 52 55 52 56 51 54 52 55], F4:[50 49 52 55 52 56 51 54 52 55], F5:[57 50 50 51 51 55 50 48 51 54 56 53 52 55 55 53 56 48 55], F6:[49 50 51]
F1:[48], F2:[48], F3:[48], F4:[48], F5:[48], F6:[49 50 51]
F1:[48], F2:[45 51 50 55 54 56], F3:[45 50 49 52 55 52 56 51 54 52 56], F4:[45 50 49 52 55 52 56 51 54 52 56], F5:[45 57 50 50 51 51 55 50 48 51 54 56 53 52 55 55 53 56 48 56], F6:[49 50 51]
----------result is golang bool----------
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert "255" into type bool
sql: Scan error on column index 5, name "i1toi2": sql/driver: couldn't convert 123 into type bool
sql: Scan error on column index 1, name "f2": sql/driver: couldn't convert -32768 into type bool
----------result is golang integer----------
F1:255, F2:32767, F3:2147483647, F4:2147483647, F5:9223372036854775807, F6:123
F1:0, F2:0, F3:0, F4:0, F5:0, F6:123
F1:0, F2:-32768, F3:-2147483648, F4:-2147483648, F5:-9223372036854775808, F6:123
----------result is golang float----------
F1:255, F2:32767, F3:2.147483647e+09, F4:2.147483647e+09, F5:9.223372036854776e+18, F6:123
F1:0, F2:0, F3:0, F4:0, F5:0, F6:123
F1:0, F2:-32768, F3:-2.147483648e+09, F4:-2.147483648e+09, F5:-9.223372036854776e+18, F6:123
----------result is golang others----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
********************Numeric Datatype********************
----------result is golang string----------
F1:437164.41907403710, F2:71.6149, F3:34819647147, F4:437164.41907403710, F5:71.6149, F6:34819647147
----------result is golang interface{}----------
F1:[52 51 55 49 54 52 46 52 49 57 48 55 52 48 51 55 49 48], F2:[55 49 46 54 49 52 57], F3:[51 52 56 49 57 54 52 55 49 52 55], F4:[52 51 55 49 54 52 46 52 49 57 48 55 52 48 51 55 49 48], F5:[55 49 46 54 49 52 57], F6:[51 52 56 49 57 54 52 55 49 52 55]
----------result is golang []byte----------
F1:[52 51 55 49 54 52 46 52 49 57 48 55 52 48 51 55 49 48], F2:[55 49 46 54 49 52 57], F3:[51 52 56 49 57 54 52 55 49 52 55], F4:[52 51 55 49 54 52 46 52 49 57 48 55 52 48 51 55 49 48], F5:[55 49 46 54 49 52 57], F6:[51 52 56 49 57 54 52 55 49 52 55]
----------result is golang sql.RawBytes----------
F1:[52 51 55 49 54 52 46 52 49 57 48 55 52 48 51 55 49 48], F2:[55 49 46 54 49 52 57], F3:[51 52 56 49 57 54 52 55 49 52 55], F4:[52 51 55 49 54 52 46 52 49 57 48 55 52 48 51 55 49 48], F5:[55 49 46 54 49 52 57], F6:[51 52 56 49 57 54 52 55 49 52 55]
----------result is golang bool----------
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert "437164.41907403710" into type bool
----------result is golang integer----------
sql: Scan error on column index 0, name "f1": converting driver.Value type []uint8 ("437164.41907403710") to a int64: invalid syntax
----------result is golang float----------
F1:437164.4190740371, F2:71.6149, F3:3.4819647147e+10, F4:437164.4190740371, F5:71.6149, F6:3.4819647147e+10
----------result is golang others----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
********************Float Datatype********************
----------result is golang string----------
F1:4164.52, F2:34847.0438321, F3:437164.41940371, F4:71.6149329, F5:71.6933, F6:34819647147.04
----------result is golang interface{}----------
F1:4164.52, F2:34847.0438321, F3:437164.41940371, F4:71.6149329, F5:[55 49 46 54 57 51 51], F6:[51 52 56 49 57 54 52 55 49 52 55 46 48 52]
----------result is golang []byte----------
F1:[52 49 54 52 46 53 50], F2:[51 52 56 52 55 46 48 52 51 56 51 50 49], F3:[52 51 55 49 54 52 46 52 49 57 52 48 51 55 49], F4:[55 49 46 54 49 52 57 51 50 57], F5:[55 49 46 54 57 51 51], F6:[51 52 56 49 57 54 52 55 49 52 55 46 48 52]
----------result is golang sql.RawBytes----------
F1:[52 49 54 52 46 53 50], F2:[51 52 56 52 55 46 48 52 51 56 51 50 49], F3:[52 51 55 49 54 52 46 52 49 57 52 48 51 55 49], F4:[55 49 46 54 49 52 57 51 50 57], F5:[55 49 46 54 57 51 51], F6:[51 52 56 49 57 54 52 55 49 52 55 46 48 52]
----------result is golang bool----------
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert 4164.52 (float64) into type bool
----------result is golang integer----------
sql: Scan error on column index 0, name "f1": converting driver.Value type float64 ("4164.52") to a int64: invalid syntax
----------result is golang float----------
F1:4164.52, F2:34847.0438321, F3:437164.41940371, F4:71.6149329, F5:71.6933, F6:3.481964714704e+10
----------result is golang others----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type float64 into type *time.Time
********************Serial Datatype********************
----------result is golang string----------
F1:1, F2:1, F3:1, F4:1, F5:1, F6:1
----------result is golang interface{}----------
F1:1, F2:1, F3:1, F4:1, F5:1, F6:1
----------result is golang []byte----------
F1:[49], F2:[49], F3:[49], F4:[49], F5:[49], F6:[49]
----------result is golang sql.RawBytes----------
F1:[49], F2:[49], F3:[49], F4:[49], F5:[49], F6:[49]
----------result is golang bool----------
F1:true, F2:true, F3:true, F4:true, F5:true, F6:true
----------result is golang integer----------
F1:1, F2:1, F3:1, F4:1, F5:1, F6:1
----------result is golang float----------
F1:1, F2:1, F3:1, F4:1, F5:1, F6:1
----------result is golang others----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type int64 into type *time.Time
********************Datetime Datatype********************
----------result is golang string----------
F1:2022-01-28T00:00:00Z, F2:0000-01-01T14:56:38.687146Z, F3:0000-01-01T14:56:38.687146Z, F4:2022-01-28T14:56:38.687146Z, F5:2022-01-28T23:01:28.382264+08:00
----------result is golang interface{}----------
F1:2022-01-28 00:00:00 +0000 +0000, F2:0000-01-01 14:56:38.687146 +0000 UTC, F3:0000-01-01 14:56:38.687146 +0000 +0000, F4:2022-01-28 14:56:38.687146 +0000 +0000, F5:2022-01-28 23:01:28.382264 +0800 CST
----------result is golang []byte----------
F1:[50 48 50 50 45 48 49 45 50 56 84 48 48 58 48 48 58 48 48 90], F2:[48 48 48 48 45 48 49 45 48 49 84 49 52 58 53 54 58 51 56 46 54 56 55 49 52 54 90], F3:[48 48 48 48 45 48 49 45 48 49 84 49 52 58 53 54 58 51 56 46 54 56 55 49 52 54 90], F4:[50 48 50 50 45 48 49 45 50 56 84 49 52 58 53 54 58 51 56 46 54 56 55 49 52 54 90], F5:[50 48 50 50 45 48 49 45 50 56 84 50 51 58 48 49 58 50 56 46 51 56 50 50 54 52 43 48 56 58 48 48]
----------result is golang sql.RawBytes----------
F1:[50 48 50 50 45 48 49 45 50 56 84 48 48 58 48 48 58 48 48 90], F2:[48 48 48 48 45 48 49 45 48 49 84 49 52 58 53 54 58 51 56 46 54 56 55 49 52 54 90], F3:[48 48 48 48 45 48 49 45 48 49 84 49 52 58 53 54 58 51 56 46 54 56 55 49 52 54 90], F4:[50 48 50 50 45 48 49 45 50 56 84 49 52 58 53 54 58 51 56 46 54 56 55 49 52 54 90], F5:[50 48 50 50 45 48 49 45 50 56 84 50 51 58 48 49 58 50 56 46 51 56 50 50 54 52 43 48 56 58 48 48]
----------result is golang time.Time----------
F1:2022-01-28 00:00:00 +0000 +0000, F2:0000-01-01 14:56:38.687146 +0000 UTC, F3:0000-01-01 14:56:38.687146 +0000 +0000, F4:2022-01-28 14:56:38.687146 +0000 +0000, F5:2022-01-28 23:01:28.382264 +0800 CST
----------result is golang others----------
sql: Scan error on column index 0, name "f1": converting driver.Value type time.Time ("2022-01-28 00:00:00 +0000 +0000") to a int64: invalid syntax
********************Interval Datatype********************
----------result is golang string----------
F1:2022-01-28 15:01:00, F2:3 days, F3:2 years, F4:-19 years -4 mons -04:04:00, F5:2022-01-28 23:00:00+08
----------result is golang interface{}----------
F1:[50 48 50 50 45 48 49 45 50 56 32 49 53 58 48 49 58 48 48], F2:[51 32 100 97 121 115], F3:[50 32 121 101 97 114 115], F4:[45 49 57 32 121 101 97 114 115 32 45 52 32 109 111 110 115 32 45 48 52 58 48 52 58 48 48], F5:[50 48 50 50 45 48 49 45 50 56 32 50 51 58 48 48 58 48 48 43 48 56]
----------result is golang []byte----------
F1:[50 48 50 50 45 48 49 45 50 56 32 49 53 58 48 49 58 48 48], F2:[51 32 100 97 121 115], F3:[50 32 121 101 97 114 115], F4:[45 49 57 32 121 101 97 114 115 32 45 52 32 109 111 110 115 32 45 48 52 58 48 52 58 48 48], F5:[50 48 50 50 45 48 49 45 50 56 32 50 51 58 48 48 58 48 48 43 48 56]
----------result is golang sql.RawBytes----------
F1:[50 48 50 50 45 48 49 45 50 56 32 49 53 58 48 49 58 48 48], F2:[51 32 100 97 121 115], F3:[50 32 121 101 97 114 115], F4:[45 49 57 32 121 101 97 114 115 32 45 52 32 109 111 110 115 32 45 48 52 58 48 52 58 48 48], F5:[50 48 50 50 45 48 49 45 50 56 32 50 51 58 48 48 58 48 48 43 48 56]
----------result is golang time.Time----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
----------result is golang others----------
sql: Scan error on column index 0, name "f1": converting driver.Value type []uint8 ("2022-01-28 15:01:00") to a int64: invalid syntax
********************Money Datatype********************
----------result is golang string----------
F1:$100.00
F1:$2,090.11
F1:$12.43
----------result is golang interface{}----------
F1:[36 49 48 48 46 48 48]
F1:[36 50 44 48 57 48 46 49 49]
F1:[36 49 50 46 52 51]
----------result is golang []byte----------
F1:[36 49 48 48 46 48 48]
F1:[36 50 44 48 57 48 46 49 49]
F1:[36 49 50 46 52 51]
----------result is golang sql.RawBytes----------
F1:[36 49 48 48 46 48 48]
F1:[36 50 44 48 57 48 46 49 49]
F1:[36 49 50 46 52 51]
----------result is golang bool----------
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert "$100.00" into type bool
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert "$2,090.11" into type bool
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert "$12.43" into type bool
----------result is golang others----------
sql: Scan error on column index 0, name "f1": converting driver.Value type []uint8 ("$100.00") to a int64: invalid syntax
sql: Scan error on column index 0, name "f1": converting driver.Value type []uint8 ("$2,090.11") to a int64: invalid syntax
sql: Scan error on column index 0, name "f1": converting driver.Value type []uint8 ("$12.43") to a int64: invalid syntax
********************Bool Datatype********************
----------result is golang string----------
F1:true
F1:false
F1:true
----------result is golang interface{}----------
F1:true
F1:false
F1:true
----------result is golang []byte----------
F1:[116 114 117 101]
F1:[102 97 108 115 101]
F1:[116 114 117 101]
----------result is golang sql.RawBytes----------
F1:[116 114 117 101]
F1:[102 97 108 115 101]
F1:[116 114 117 101]
----------result is golang bool----------
F1:true
F1:false
F1:true
----------result is golang others----------
sql: Scan error on column index 0, name "f1": converting driver.Value type bool ("true") to a int64: invalid syntax
sql: Scan error on column index 0, name "f1": converting driver.Value type bool ("false") to a int64: invalid syntax
sql: Scan error on column index 0, name "f1": converting driver.Value type bool ("true") to a int64: invalid syntax
********************Character Datatype********************
----------result is golang string----------
F1:a, F2:你 , F3:bbb, F4:2090.11, F5:中国, F6:dgjqgdgfakgfekikgqif, F7:dgjqgdgfakgfekikgqif
F1:1, F2:true, F3:0, F4:false, F5:f, F6:TRUE, F7:FALSE
F1:1, F2:2000, F3:0, F4:4, F5:5, F6:6, F7:7
----------result is golang interface{}----------
F1:[97], F2:[228 189 160 32], F3:bbb, F4:2090.11, F5:[228 184 173 229 155 189], F6:dgjqgdgfakgfekikgqif, F7:[100 103 106 113 103 100 103 102 97 107 103 102 101 107 105 107 103 113 105 102]
F1:[49], F2:[116 114 117 101], F3:0, F4:false, F5:[102], F6:TRUE, F7:[70 65 76 83 69]
F1:[49], F2:[50 48 48 48], F3:0, F4:4, F5:[53], F6:6, F7:[55]
----------result is golang []byte----------
F1:[97], F2:[228 189 160 32], F3:[98 98 98], F4:[50 48 57 48 46 49 49], F5:[228 184 173 229 155 189], F6:[100 103 106 113 103 100 103 102 97 107 103 102 101 107 105 107 103 113 105 102], F7:[100 103 106 113 103 100 103 102 97 107 103 102 101 107 105 107 103 113 105 102]
F1:[49], F2:[116 114 117 101], F3:[48], F4:[102 97 108 115 101], F5:[102], F6:[84 82 85 69], F7:[70 65 76 83 69]
F1:[49], F2:[50 48 48 48], F3:[48], F4:[52], F5:[53], F6:[54], F7:[55]
----------result is golang sql.RawBytes----------
F1:[97], F2:[228 189 160 32], F3:[98 98 98], F4:[50 48 57 48 46 49 49], F5:[228 184 173 229 155 189], F6:[100 103 106 113 103 100 103 102 97 107 103 102 101 107 105 107 103 113 105 102], F7:[100 103 106 113 103 100 103 102 97 107 103 102 101 107 105 107 103 113 105 102]
F1:[49], F2:[116 114 117 101], F3:[48], F4:[102 97 108 115 101], F5:[102], F6:[84 82 85 69], F7:[70 65 76 83 69]
F1:[49], F2:[50 48 48 48], F3:[48], F4:[52], F5:[53], F6:[54], F7:[55]
----------result is golang bool----------
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert "a" into type bool
F1:true, F2:true, F3:false, F4:false, F5:false, F6:true, F7:false
sql: Scan error on column index 1, name "f2": sql/driver: couldn't convert "2000" into type bool
----------result is golang integer----------
sql: Scan error on column index 0, name "f1": converting driver.Value type []uint8 ("a") to a int64: invalid syntax
sql: Scan error on column index 1, name "f2": converting driver.Value type []uint8 ("true") to a int64: invalid syntax
F1:1, F2:2000, F3:0, F4:4, F5:5, F6:6, F7:7
----------result is golang float----------
sql: Scan error on column index 0, name "f1": converting driver.Value type []uint8 ("a") to a float64: invalid syntax
sql: Scan error on column index 1, name "f2": converting driver.Value type []uint8 ("true") to a float64: invalid syntax
F1:1, F2:2000, F3:0, F4:4, F5:5, F6:6, F7:7
----------result is golang others----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
********************Binary Datatype********************
----------result is golang string----------
F1:0123, F2:0ABC, F3:deqde, F4:0123, F5:0ABC, F6:deqde, F7:0123
----------result is golang interface{}----------
F1:[48 49 50 51], F2:[48 65 66 67], F3:[100 101 113 100 101], F4:[48 49 50 51], F5:[48 65 66 67], F6:[100 101 113 100 101], F7:[48 49 50 51]
----------result is golang []byte----------
F1:[48 49 50 51], F2:[48 65 66 67], F3:[100 101 113 100 101], F4:[48 49 50 51], F5:[48 65 66 67], F6:[100 101 113 100 101], F7:[48 49 50 51]
----------result is golang sql.RawBytes----------
F1:[48 49 50 51], F2:[48 65 66 67], F3:[100 101 113 100 101], F4:[48 49 50 51], F5:[48 65 66 67], F6:[100 101 113 100 101], F7:[48 49 50 51]
----------result is golang bool----------
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert "0123" into type bool
----------result is golang integer----------
sql: Scan error on column index 1, name "f2": converting driver.Value type []uint8 ("0ABC") to a int64: invalid syntax
----------result is golang float----------
sql: Scan error on column index 1, name "f2": converting driver.Value type []uint8 ("0ABC") to a float64: invalid syntax
----------result is golang others----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
********************Geometry Datatype********************
----------result is golang string----------
F1:(1,2), F2:[(1,2),(3,4)], F3:(2,2),(1,1), F4:((1,0),(2,0),(3,4)), F5:((1,0),(2,0),(3,4)), F6:<(0,0),2>
----------result is golang interface{}----------
F1:[40 49 44 50 41], F2:[91 40 49 44 50 41 44 40 51 44 52 41 93], F3:[40 50 44 50 41 44 40 49 44 49 41], F4:[40 40 49 44 48 41 44 40 50 44 48 41 44 40 51 44 52 41 41], F5:[40 40 49 44 48 41 44 40 50 44 48 41 44 40 51 44 52 41 41], F6:[60 40 48 44 48 41 44 50 62]
----------result is golang []byte----------
F1:[40 49 44 50 41], F2:[91 40 49 44 50 41 44 40 51 44 52 41 93], F3:[40 50 44 50 41 44 40 49 44 49 41], F4:[40 40 49 44 48 41 44 40 50 44 48 41 44 40 51 44 52 41 41], F5:[40 40 49 44 48 41 44 40 50 44 48 41 44 40 51 44 52 41 41], F6:[60 40 48 44 48 41 44 50 62]
----------result is golang sql.RawBytes----------
F1:[40 49 44 50 41], F2:[91 40 49 44 50 41 44 40 51 44 52 41 93], F3:[40 50 44 50 41 44 40 49 44 49 41], F4:[40 40 49 44 48 41 44 40 50 44 48 41 44 40 51 44 52 41 41], F5:[40 40 49 44 48 41 44 40 50 44 48 41 44 40 51 44 52 41 41], F6:[60 40 48 44 48 41 44 50 62]
----------result is golang others----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
********************NetAddr Datatype********************
----------result is golang string----------
F1:192.168.0.0/24, F2:192.168.1.5/8, F3:08:00:2b:01:02:03
F1:192.1.0.0/24, F2:192.1.2.1, F3:08:00:2b:01:02:03
----------result is golang interface{}----------
F1:[49 57 50 46 49 54 56 46 48 46 48 47 50 52], F2:[49 57 50 46 49 54 56 46 49 46 53 47 56], F3:[48 56 58 48 48 58 50 98 58 48 49 58 48 50 58 48 51]
F1:[49 57 50 46 49 46 48 46 48 47 50 52], F2:[49 57 50 46 49 46 50 46 49], F3:[48 56 58 48 48 58 50 98 58 48 49 58 48 50 58 48 51]
----------result is golang []byte----------
F1:[49 57 50 46 49 54 56 46 48 46 48 47 50 52], F2:[49 57 50 46 49 54 56 46 49 46 53 47 56], F3:[48 56 58 48 48 58 50 98 58 48 49 58 48 50 58 48 51]
F1:[49 57 50 46 49 46 48 46 48 47 50 52], F2:[49 57 50 46 49 46 50 46 49], F3:[48 56 58 48 48 58 50 98 58 48 49 58 48 50 58 48 51]
----------result is golang sql.RawBytes----------
F1:[49 57 50 46 49 54 56 46 48 46 48 47 50 52], F2:[49 57 50 46 49 54 56 46 49 46 53 47 56], F3:[48 56 58 48 48 58 50 98 58 48 49 58 48 50 58 48 51]
F1:[49 57 50 46 49 46 48 46 48 47 50 52], F2:[49 57 50 46 49 46 50 46 49], F3:[48 56 58 48 48 58 50 98 58 48 49 58 48 50 58 48 51]
----------result is golang others----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
********************BitStr Datatype********************
----------result is golang string----------
F1:1, F2:101, F3:1011
F1:2, F2:100, F3:01
----------result is golang interface{}----------
F1:1, F2:[49 48 49], F3:[49 48 49 49]
F1:2, F2:[49 48 48], F3:[48 49]
----------result is golang []byte----------
F1:[49], F2:[49 48 49], F3:[49 48 49 49]
F1:[50], F2:[49 48 48], F3:[48 49]
----------result is golang sql.RawBytes----------
F1:[49], F2:[49 48 49], F3:[49 48 49 49]
F1:[50], F2:[49 48 48], F3:[48 49]
----------result is golang others----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type int64 into type *time.Time
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type int64 into type *time.Time
********************TextSearch Datatype********************
----------result is golang string----------
F1:1, F2:'a' 'and' 'ate' 'cat' 'fat' 'mat' 'on' 'rat' 'sat', F3:'fat' & 'rat'
----------result is golang interface{}----------
F1:1, F2:[39 97 39 32 39 97 110 100 39 32 39 97 116 101 39 32 39 99 97 116 39 32 39 102 97 116 39 32 39 109 97 116 39 32 39 111 110 39 32 39 114 97 116 39 32 39 115 97 116 39], F3:[39 102 97 116 39 32 38 32 39 114 97 116 39]
----------result is golang []byte----------
F1:[49], F2:[39 97 39 32 39 97 110 100 39 32 39 97 116 101 39 32 39 99 97 116 39 32 39 102 97 116 39 32 39 109 97 116 39 32 39 111 110 39 32 39 114 97 116 39 32 39 115 97 116 39], F3:[39 102 97 116 39 32 38 32 39 114 97 116 39]
----------result is golang sql.RawBytes----------
F1:[49], F2:[39 97 39 32 39 97 110 100 39 32 39 97 116 101 39 32 39 99 97 116 39 32 39 102 97 116 39 32 39 109 97 116 39 32 39 111 110 39 32 39 114 97 116 39 32 39 115 97 116 39], F3:[39 102 97 116 39 32 38 32 39 114 97 116 39]
----------result is golang others----------
sql: Scan error on column index 0, name "f1": unsupported Scan, storing driver.Value type int64 into type *time.Time
********************Range Datatype********************
----------result is golang string----------
F1:[-1,11), F2:[-2910,7738), F3:(367.431,731.431], F4:("2010-01-01 14:30:00","2010-01-04 15:30:00"), F5:["2022-01-28 23:01:28.382264+08","2022-01-30 23:01:28.382264+08"], F6:[2022-01-28,2022-02-01)
----------result is golang interface{}----------
F1:[91 45 49 44 49 49 41], F2:[91 45 50 57 49 48 44 55 55 51 56 41], F3:[40 51 54 55 46 52 51 49 44 55 51 49 46 52 51 49 93], F4:[40 34 50 48 49 48 45 48 49 45 48 49 32 49 52 58 51 48 58 48 48 34 44 34 50 48 49 48 45 48 49 45 48 52 32 49 53 58 51 48 58 48 48 34 41], F5:[91 34 50 48 50 50 45 48 49 45 50 56 32 50 51 58 48 49 58 50 56 46 51 56 50 50 54 52 43 48 56 34 44 34 50 48 50 50 45 48 49 45 51 48 32 50 51 58 48 49 58 50 56 46 51 56 50 50 54 52 43 48 56 34 93], F6:[91 50 48 50 50 45 48 49 45 50 56 44 50 48 50 50 45 48 50 45 48 49 41]
----------result is golang []byte----------
F1:[91 45 49 44 49 49 41], F2:[91 45 50 57 49 48 44 55 55 51 56 41], F3:[40 51 54 55 46 52 51 49 44 55 51 49 46 52 51 49 93], F4:[40 34 50 48 49 48 45 48 49 45 48 49 32 49 52 58 51 48 58 48 48 34 44 34 50 48 49 48 45 48 49 45 48 52 32 49 53 58 51 48 58 48 48 34 41], F5:[91 34 50 48 50 50 45 48 49 45 50 56 32 50 51 58 48 49 58 50 56 46 51 56 50 50 54 52 43 48 56 34 44 34 50 48 50 50 45 48 49 45 51 48 32 50 51 58 48 49 58 50 56 46 51 56 50 50 54 52 43 48 56 34 93], F6:[91 50 48 50 50 45 48 49 45 50 56 44 50 48 50 50 45 48 50 45 48 49 41]
----------result is golang sql.RawBytes----------
F1:[91 45 49 44 49 49 41], F2:[91 45 50 57 49 48 44 55 55 51 56 41], F3:[40 51 54 55 46 52 51 49 44 55 51 49 46 52 51 49 93], F4:[40 34 50 48 49 48 45 48 49 45 48 49 32 49 52 58 51 48 58 48 48 34 44 34 50 48 49 48 45 48 49 45 48 52 32 49 53 58 51 48 58 48 48 34 41], F5:[91 34 50 48 50 50 45 48 49 45 50 56 32 50 51 58 48 49 58 50 56 46 51 56 50 50 54 52 43 48 56 34 44 34 50 48 50 50 45 48 49 45 51 48 32 50 51 58 48 49 58 50 56 46 51 56 50 50 54 52 43 48 56 34 93], F6:[91 50 48 50 50 45 48 49 45 50 56 44 50 48 50 50 45 48 50 45 48 49 41]
----------result is golang others----------
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert "[-1,11)" into type bool
********************Json Datatype********************
----------result is golang string----------
F1: [1, " a ", {"a" :1 }] , F2:[1, " a ", {"a": 1}], F3:{"a" : 1, "a" : 2}, F4:{"a": 2}, F5:{"aa" : 1, "b" : 2, "a" : 3}, F6:{"a": 3, "b": 2, "aa": 1}
----------result is golang interface{}----------
F1:[32 32 32 91 49 44 32 34 32 97 32 34 44 32 123 34 97 34 32 32 32 58 49 32 32 32 32 125 93 32 32], F2:[91 49 44 32 34 32 97 32 34 44 32 123 34 97 34 58 32 49 125 93], F3:[123 34 97 34 32 58 32 49 44 32 34 97 34 32 58 32 50 125], F4:[123 34 97 34 58 32 50 125], F5:[123 34 97 97 34 32 58 32 49 44 32 34 98 34 32 58 32 50 44 32 34 97 34 32 58 32 51 125], F6:[123 34 97 34 58 32 51 44 32 34 98 34 58 32 50 44 32 34 97 97 34 58 32 49 125]
----------result is golang []byte----------
F1:[32 32 32 91 49 44 32 34 32 97 32 34 44 32 123 34 97 34 32 32 32 58 49 32 32 32 32 125 93 32 32], F2:[91 49 44 32 34 32 97 32 34 44 32 123 34 97 34 58 32 49 125 93], F3:[123 34 97 34 32 58 32 49 44 32 34 97 34 32 58 32 50 125], F4:[123 34 97 34 58 32 50 125], F5:[123 34 97 97 34 32 58 32 49 44 32 34 98 34 32 58 32 50 44 32 34 97 34 32 58 32 51 125], F6:[123 34 97 34 58 32 51 44 32 34 98 34 58 32 50 44 32 34 97 97 34 58 32 49 125]
----------result is golang sql.RawBytes----------
F1:[32 32 32 91 49 44 32 34 32 97 32 34 44 32 123 34 97 34 32 32 32 58 49 32 32 32 32 125 93 32 32], F2:[91 49 44 32 34 32 97 32 34 44 32 123 34 97 34 58 32 49 125 93], F3:[123 34 97 34 32 58 32 49 44 32 34 97 34 32 58 32 50 125], F4:[123 34 97 34 58 32 50 125], F5:[123 34 97 97 34 32 58 32 49 44 32 34 98 34 32 58 32 50 44 32 34 97 34 32 58 32 51 125], F6:[123 34 97 34 58 32 51 44 32 34 98 34 58 32 50 44 32 34 97 97 34 58 32 49 125]
----------result is golang others----------
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert " [1, \" a \", {\"a\" :1 }] " into type bool
********************Other Datatype********************
----------result is golang string----------
F1:a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11, F2:e697da2eaa3a775b, F3:685847ed1fe38e18f6b0e2b18c00edee, F4:\x484c4c00000000002b05000000000000000000000000000000000000
----------result is golang interface{}----------
F1:[97 48 101 101 98 99 57 57 45 57 99 48 98 45 52 101 102 56 45 98 98 54 100 45 54 98 98 57 98 100 51 56 48 97 49 49], F2:[101 54 57 55 100 97 50 101 97 97 51 97 55 55 53 98], F3:[54 56 53 56 52 55 101 100 49 102 101 51 56 101 49 56 102 54 98 48 101 50 98 49 56 99 48 48 101 100 101 101], F4:[92 120 52 56 52 99 52 99 48 48 48 48 48 48 48 48 48 48 50 98 48 53 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48]
----------result is golang []byte----------
F1:[97 48 101 101 98 99 57 57 45 57 99 48 98 45 52 101 102 56 45 98 98 54 100 45 54 98 98 57 98 100 51 56 48 97 49 49], F2:[101 54 57 55 100 97 50 101 97 97 51 97 55 55 53 98], F3:[54 56 53 56 52 55 101 100 49 102 101 51 56 101 49 56 102 54 98 48 101 50 98 49 56 99 48 48 101 100 101 101], F4:[92 120 52 56 52 99 52 99 48 48 48 48 48 48 48 48 48 48 50 98 48 53 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48]
----------result is golang sql.RawBytes----------
F1:[97 48 101 101 98 99 57 57 45 57 99 48 98 45 52 101 102 56 45 98 98 54 100 45 54 98 98 57 98 100 51 56 48 97 49 49], F2:[101 54 57 55 100 97 50 101 97 97 51 97 55 55 53 98], F3:[54 56 53 56 52 55 101 100 49 102 101 51 56 101 49 56 102 54 98 48 101 50 98 49 56 99 48 48 101 100 101 101], F4:[92 120 52 56 52 99 52 99 48 48 48 48 48 48 48 48 48 48 50 98 48 53 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48]
----------result is golang others----------
sql: Scan error on column index 0, name "f1": sql/driver: couldn't convert "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11" into type bool
PASS

View File

@ -1,15 +0,0 @@
********************DB Exec********************
f1:1, f2:abcdefg, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:hello world, f3:641.43, f4:2022-02-08 10:35:43.000623 +0800 CST, f5:false
f1:3, f2:华为, f3:431.54, f4:2022-02-10 19:11:54.000353 +0800 CST, f5:true
f1:4, f2:北京2022冬奥会, f3:5423.52, f4:2022-02-12 06:11:15.000636 +0800 CST, f5:false
f1:5, f2:nanjing, f3:665537.63, f4:2022-02-14 04:51:22.000748 +0800 CST, f5:true
f1:6, f2:研究所, f3:6503.1, f4:2022-02-16 13:45:55.000675 +0800 CST, f5:true
********************DB ExecContext********************
f1:1, f2:abcdefg, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:hello world, f3:641.43, f4:2022-02-08 10:35:43.000623 +0800 CST, f5:false
f1:3, f2:华为, f3:431.54, f4:2022-02-10 19:11:54.000353 +0800 CST, f5:true
f1:4, f2:北京2022冬奥会, f3:5423.52, f4:2022-02-12 06:11:15.000636 +0800 CST, f5:false
f1:5, f2:nanjing, f3:665537.63, f4:2022-02-14 04:51:22.000748 +0800 CST, f5:true
f1:6, f2:研究所, f3:6503.1, f4:2022-02-16 13:45:55.000675 +0800 CST, f5:true
PASS

View File

@ -1,4 +0,0 @@
name: abc
id: 1
name: abc
PASS

View File

@ -1,20 +0,0 @@
********************Query with Row********************
f1:1, f2:abcdefg, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
The name of columns is f1 f2 f3 f4 f5
********************Query with Rows********************
f1:2, f2:hello world, f3:641.43, f4:2022-02-08 10:35:43.000623 +0800 CST, f5:false
f1:3, f2:华为, f3:431.54, f4:2022-02-10 19:11:54.000353 +0800 CST, f5:true
f1:4, f2:北京2022冬奥会, f3:5423.52, f4:2022-02-12 06:11:15.000636 +0800 CST, f5:false
f1:5, f2:nanjing, f3:665537.63, f4:2022-02-14 04:51:22.000748 +0800 CST, f5:true
f1:6, f2:研究所, f3:6503.1, f4:2022-02-16 13:45:55.000675 +0800 CST, f5:true
********************Get ColumnType********************
Name is f1, DatabaseTypeName is INT4. ScanType is int32.
Name is f2, DatabaseTypeName is VARCHAR. Sting length is 20. ScanType is string.
Name is f3, DatabaseTypeName is NUMERIC. Decimal precision is 20, scale is 4. ScanType is interface {}.
Name is f4, DatabaseTypeName is TIMESTAMPTZ. ScanType is time.Time.
Name is f5, DatabaseTypeName is BOOL. ScanType is bool.
Name is f6, DatabaseTypeName is FLOAT8. ScanType is float64.
Name is f7, DatabaseTypeName is MONEY. ScanType is interface {}.
Name is f8, DatabaseTypeName is POLYGON. ScanType is interface {}.
Name is f9, DatabaseTypeName is BYTEA. Sting length is 1073733621. ScanType is []uint8.
PASS

View File

@ -1,15 +0,0 @@
********************Stmt Exec********************
f1:1, f2:abcdefg, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:hello world, f3:641.43, f4:2022-02-08 10:35:43.000623 +0800 CST, f5:false
f1:3, f2:华为, f3:431.54, f4:2022-02-10 19:11:54.000353 +0800 CST, f5:true
f1:4, f2:北京2022冬奥会, f3:5423.52, f4:2022-02-12 06:11:15.000636 +0800 CST, f5:false
f1:5, f2:nanjing, f3:665537.63, f4:2022-02-14 04:51:22.000748 +0800 CST, f5:true
f1:6, f2:研究所, f3:6503.1, f4:2022-02-16 13:45:55.000675 +0800 CST, f5:true
********************Stmt ExecContext********************
f1:1, f2:abcdefg, f3:123.3, f4:2022-02-08 10:30:43.31 +0800 CST, f5:true
f1:2, f2:hello world, f3:641.43, f4:2022-02-08 10:35:43.000623 +0800 CST, f5:false
f1:3, f2:华为, f3:431.54, f4:2022-02-10 19:11:54.000353 +0800 CST, f5:true
f1:4, f2:北京2022冬奥会, f3:5423.52, f4:2022-02-12 06:11:15.000636 +0800 CST, f5:false
f1:5, f2:nanjing, f3:665537.63, f4:2022-02-14 04:51:22.000748 +0800 CST, f5:true
f1:6, f2:研究所, f3:6503.1, f4:2022-02-16 13:45:55.000675 +0800 CST, f5:true
PASS

View File

@ -1,9 +0,0 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -1,51 +0,0 @@
name: Bug report
description: File a bug report to help us improve
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- Before you file an issue read the [Contributing guide](https://github.com/go-ini/ini/blob/main/.github/contributing.md).
- Check to make sure someone hasn't already opened a similar [issue](https://github.com/go-ini/ini/issues).
- type: input
attributes:
label: Version
description: Please specify the exact Go module version you're reporting for.
validations:
required: true
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: To reproduce
description: A code snippet to reproduce the problem described above.
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: |
Links? References? Suggestions? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false
- type: checkboxes
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://go.dev/conduct)
options:
- label: I agree to follow this project's Code of Conduct
required: true

View File

@ -1,30 +0,0 @@
name: Improve documentation
description: Suggest an idea or a patch for documentation
labels: ["documentation"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this form!
- Before you file an issue read the [Contributing guide](https://github.com/go-ini/ini/blob/main/.github/contributing.md).
- Check to make sure someone hasn't already opened a similar [issue](https://github.com/go-ini/ini/issues).
- type: textarea
attributes:
label: What needs to be improved? Please describe
description: A clear and concise description of what is wrong or missing.
validations:
required: true
- type: textarea
attributes:
label: Why do you think it is important?
description: A clear and concise explanation of the rationale.
validations:
required: true
- type: checkboxes
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://go.dev/conduct)
options:
- label: I agree to follow this project's Code of Conduct
required: true

View File

@ -1,45 +0,0 @@
name: Feature request
description: Suggest an idea for this project
labels: ["feature"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this form!
- Before you file an issue read the [Contributing guide](https://github.com/go-ini/ini/blob/main/.github/contributing.md).
- Check to make sure someone hasn't already opened a similar [issue](https://github.com/go-ini/ini/issues).
- type: textarea
attributes:
label: Describe the feature
description: A clear and concise description of what the problem is, e.g. I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: |
Links? References? Suggestions? Anything that will give us more context about the feature you are requesting!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false
- type: checkboxes
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://go.dev/conduct)
options:
- label: I agree to follow this project's Code of Conduct
required: true

View File

@ -1,11 +0,0 @@
### Describe the pull request
A clear and concise description of what the pull request is about, i.e. what problem should be fixed?
Link to the issue: <!-- paste the issue link here, or put "n/a" if not applicable -->
### Checklist
- [ ] I agree to follow the [Code of Conduct](https://go.dev/conduct) by submitting this pull request.
- [ ] I have read and acknowledge the [Contributing guide](https://github.com/go-ini/ini/blob/main/.github/contributing.md).
- [ ] I have added test cases to cover the new code.

View File

@ -1,55 +0,0 @@
# Welcome to go-ini contributing guide
Thank you for investing your time in contributing to our projects!
Read our [Code of Conduct](https://go.dev/conduct) to keep our community approachable and respectable.
In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR.
Use the table of contents icon <img src="https://github.com/github/docs/raw/50561895328b8f369694973252127b7d93899d83/assets/images/table-of-contents.png" width="25" height="25" /> on the top left corner of this document to get to a specific section of this guide quickly.
## New contributor guide
To get an overview of the project, read the [README](/README.md). Here are some resources to help you get started with open source contributions:
- [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github)
- [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git)
- [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow)
- [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests)
In addition to the general guides with open source contributions, you would also need to:
- Have basic knowledge about INI configuration format and programming in [Go](https://go.dev/).
- Have a working local development setup with a reasonable good IDE or editor like [Visual Studio Code](https://code.visualstudio.com/docs/languages/go), [GoLand](https://www.jetbrains.com/go/) or [Vim](https://github.com/fatih/vim-go).
## Issues
### Create a new issue
- [Check to make sure](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments) someone hasn't already opened a similar [issue](https://github.com/go-ini/ini/issues).
- If a similar issue doesn't exist, open a new issue using a relevant [issue form](https://github.com/go-ini/ini/issues/new/choose).
### Pick up an issue to solve
- Scan through our [existing issues](https://github.com/go-ini/ini/issues) to find one that interests you.
- The [good first issue](https://github.com/go-ini/ini/labels/good%20first%20issue) is a good place to start exploring issues that are well-groomed for newcomers.
- Do not hesitate to ask for more details or clarifying questions on the issue!
- Communicate on the issue you are intended to pick up _before_ starting working on it.
- Every issue that gets picked up will have an expected timeline for the implementation, the issue may be reassigned after the expected timeline. Please be responsible and proactive on the communication 🙇‍♂️
## Pull requests
When you're finished with the changes, create a pull request, or a series of pull requests if necessary.
Contributing to another codebase is not as simple as code changes, it is also about contributing influence to the design. Therefore, we kindly ask you that:
- Please acknowledge that no pull request is guaranteed to be merged.
- Please always do a self-review before requesting reviews from others.
- Please expect code review to be strict and may have multiple rounds.
- Please make self-contained incremental changes, pull requests with huge diff may be rejected for review.
- Please use English in code comments and docstring.
- Please do not force push unless absolutely necessary. Force pushes make review much harder in multiple rounds, and we use [Squash and merge](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-pull-request-commits) so you don't need to worry about messy commits and just focus on the changes.
## Your PR is merged!
Congratulations 🎉🎉 Thanks again for taking the effort to have this journey with us 🌟

View File

@ -1,60 +0,0 @@
name: Go
on:
push:
branches: [ main ]
paths:
- '**.go'
- 'go.mod'
- '.golangci.yml'
- '.github/workflows/go.yml'
pull_request:
paths:
- '**.go'
- 'go.mod'
- '.golangci.yml'
- '.github/workflows/go.yml'
env:
GOPROXY: "https://proxy.golang.org"
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Init Go Modules
run: |
go mod init github.com/go-ini/ini
go mod tidy
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: latest
args: --timeout=30m
skip-pkg-cache: true # Wrokaround of the "tar" problem: https://github.com/golangci/golangci-lint-action/issues/244
test:
name: Test
strategy:
matrix:
go-version: [ 1.15.x, 1.16.x, 1.17.x ]
platform: [ ubuntu-latest, macos-latest, windows-latest ]
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Run tests with coverage
run: |
go mod init github.com/go-ini/ini
go mod tidy
go test -v -race -coverprofile=coverage -covermode=atomic ./...
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v1.5.0
with:
file: ./coverage
flags: unittests

View File

@ -1,28 +0,0 @@
name: LSIF
on:
push:
paths:
- '**.go'
- 'go.mod'
- '.github/workflows/lsif.yml'
env:
GOPROXY: "https://proxy.golang.org"
jobs:
lsif-go:
if: github.repository == 'go-ini/ini'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Generate LSIF data
uses: sourcegraph/lsif-go-action@master
- name: Upload LSIF data to sourcegraph.com
continue-on-error: true
uses: docker://sourcegraph/src-cli:latest
with:
args: lsif upload -github-token=${{ secrets.GITHUB_TOKEN }}
- name: Upload LSIF data to sourcegraph.unknwon.cn
continue-on-error: true
uses: docker://sourcegraph/src-cli:latest
with:
args: -endpoint=https://sourcegraph.unknwon.cn lsif upload -github-token=${{ secrets.GITHUB_TOKEN }}

View File

@ -1,7 +0,0 @@
testdata/conf_out.ini
ini.sublime-project
ini.sublime-workspace
testdata/conf_reflect.ini
.idea
/.vscode
.DS_Store

View File

@ -1,21 +0,0 @@
linters-settings:
nakedret:
max-func-lines: 0 # Disallow any unnamed return statement
linters:
enable:
- deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
- nakedret
- gofmt
- rowserrcheck
- unconvert
- goimports

View File

@ -1,191 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright 2014 Unknwon
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,15 +0,0 @@
.PHONY: build test bench vet coverage
build: vet bench
test:
go test -v -cover -race
bench:
go test -v -cover -test.bench=. -test.benchmem
vet:
go vet
coverage:
go test -coverprofile=c.out && go tool cover -html=c.out && rm c.out

View File

@ -1,43 +0,0 @@
# INI
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/go-ini/ini/Go?logo=github&style=for-the-badge)](https://github.com/go-ini/ini/actions?query=workflow%3AGo)
[![codecov](https://img.shields.io/codecov/c/github/go-ini/ini/master?logo=codecov&style=for-the-badge)](https://codecov.io/gh/go-ini/ini)
[![GoDoc](https://img.shields.io/badge/GoDoc-Reference-blue?style=for-the-badge&logo=go)](https://pkg.go.dev/github.com/go-ini/ini?tab=doc)
[![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg?style=for-the-badge&logo=sourcegraph)](https://sourcegraph.com/github.com/go-ini/ini)
![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200)
Package ini provides INI file read and write functionality in Go.
## Features
- Load from multiple data sources(file, `[]byte`, `io.Reader` and `io.ReadCloser`) with overwrites.
- Read with recursion values.
- Read with parent-child sections.
- Read with auto-increment key names.
- Read with multiple-line values.
- Read with tons of helper methods.
- Read and convert values to Go types.
- Read and **WRITE** comments of sections and keys.
- Manipulate sections, keys and comments with ease.
- Keep sections and keys in order as you parse and save.
## Installation
The minimum requirement of Go is **1.12**.
```sh
$ go get gopkg.in/ini.v1
```
Please add `-u` flag to update in the future.
## Getting Help
- [Getting Started](https://ini.unknwon.io/docs/intro/getting_started)
- [API Documentation](https://gowalker.org/gopkg.in/ini.v1)
- 中国大陆镜像:https://ini.unknwon.cn
## License
This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.

View File

@ -1,9 +0,0 @@
coverage:
range: "60...95"
status:
project:
default:
threshold: 1%
comment:
layout: 'diff'

View File

@ -1,76 +0,0 @@
// Copyright 2019 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
)
var (
_ dataSource = (*sourceFile)(nil)
_ dataSource = (*sourceData)(nil)
_ dataSource = (*sourceReadCloser)(nil)
)
// dataSource is an interface that returns object which can be read and closed.
type dataSource interface {
ReadCloser() (io.ReadCloser, error)
}
// sourceFile represents an object that contains content on the local file system.
type sourceFile struct {
name string
}
func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
return os.Open(s.name)
}
// sourceData represents an object that contains content in memory.
type sourceData struct {
data []byte
}
func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
return ioutil.NopCloser(bytes.NewReader(s.data)), nil
}
// sourceReadCloser represents an input stream with Close method.
type sourceReadCloser struct {
reader io.ReadCloser
}
func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
return s.reader, nil
}
func parseDataSource(source interface{}) (dataSource, error) {
switch s := source.(type) {
case string:
return sourceFile{s}, nil
case []byte:
return &sourceData{s}, nil
case io.ReadCloser:
return &sourceReadCloser{s}, nil
case io.Reader:
return &sourceReadCloser{ioutil.NopCloser(s)}, nil
default:
return nil, fmt.Errorf("error parsing data source: unknown type %q", s)
}
}

View File

@ -1,25 +0,0 @@
// Copyright 2019 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
const (
// Deprecated: Use "DefaultSection" instead.
DEFAULT_SECTION = DefaultSection
)
var (
// Deprecated: AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.
AllCapsUnderscore = SnackCase
)

View File

@ -1,34 +0,0 @@
// Copyright 2016 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"fmt"
)
// ErrDelimiterNotFound indicates the error type of no delimiter is found which there should be one.
type ErrDelimiterNotFound struct {
Line string
}
// IsErrDelimiterNotFound returns true if the given error is an instance of ErrDelimiterNotFound.
func IsErrDelimiterNotFound(err error) bool {
_, ok := err.(ErrDelimiterNotFound)
return ok
}
func (err ErrDelimiterNotFound) Error() string {
return fmt.Sprintf("key-value delimiter not found: %s", err.Line)
}

View File

@ -1,524 +0,0 @@
// Copyright 2017 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"sync"
)
// File represents a combination of one or more INI files in memory.
type File struct {
options LoadOptions
dataSources []dataSource
// Should make things safe, but sometimes doesn't matter.
BlockMode bool
lock sync.RWMutex
// To keep data in order.
sectionList []string
// To keep track of the index of a section with same name.
// This meta list is only used with non-unique section names are allowed.
sectionIndexes []int
// Actual data is stored here.
sections map[string][]*Section
NameMapper
ValueMapper
}
// newFile initializes File object with given data sources.
func newFile(dataSources []dataSource, opts LoadOptions) *File {
if len(opts.KeyValueDelimiters) == 0 {
opts.KeyValueDelimiters = "=:"
}
if len(opts.KeyValueDelimiterOnWrite) == 0 {
opts.KeyValueDelimiterOnWrite = "="
}
if len(opts.ChildSectionDelimiter) == 0 {
opts.ChildSectionDelimiter = "."
}
return &File{
BlockMode: true,
dataSources: dataSources,
sections: make(map[string][]*Section),
options: opts,
}
}
// Empty returns an empty file object.
func Empty(opts ...LoadOptions) *File {
var opt LoadOptions
if len(opts) > 0 {
opt = opts[0]
}
// Ignore error here, we are sure our data is good.
f, _ := LoadSources(opt, []byte(""))
return f
}
// NewSection creates a new section.
func (f *File) NewSection(name string) (*Section, error) {
if len(name) == 0 {
return nil, errors.New("empty section name")
}
if (f.options.Insensitive || f.options.InsensitiveSections) && name != DefaultSection {
name = strings.ToLower(name)
}
if f.BlockMode {
f.lock.Lock()
defer f.lock.Unlock()
}
if !f.options.AllowNonUniqueSections && inSlice(name, f.sectionList) {
return f.sections[name][0], nil
}
f.sectionList = append(f.sectionList, name)
// NOTE: Append to indexes must happen before appending to sections,
// otherwise index will have off-by-one problem.
f.sectionIndexes = append(f.sectionIndexes, len(f.sections[name]))
sec := newSection(f, name)
f.sections[name] = append(f.sections[name], sec)
return sec, nil
}
// NewRawSection creates a new section with an unparseable body.
func (f *File) NewRawSection(name, body string) (*Section, error) {
section, err := f.NewSection(name)
if err != nil {
return nil, err
}
section.isRawSection = true
section.rawBody = body
return section, nil
}
// NewSections creates a list of sections.
func (f *File) NewSections(names ...string) (err error) {
for _, name := range names {
if _, err = f.NewSection(name); err != nil {
return err
}
}
return nil
}
// GetSection returns section by given name.
func (f *File) GetSection(name string) (*Section, error) {
secs, err := f.SectionsByName(name)
if err != nil {
return nil, err
}
return secs[0], err
}
// HasSection returns true if the file contains a section with given name.
func (f *File) HasSection(name string) bool {
section, _ := f.GetSection(name)
return section != nil
}
// SectionsByName returns all sections with given name.
func (f *File) SectionsByName(name string) ([]*Section, error) {
if len(name) == 0 {
name = DefaultSection
}
if f.options.Insensitive || f.options.InsensitiveSections {
name = strings.ToLower(name)
}
if f.BlockMode {
f.lock.RLock()
defer f.lock.RUnlock()
}
secs := f.sections[name]
if len(secs) == 0 {
return nil, fmt.Errorf("section %q does not exist", name)
}
return secs, nil
}
// Section assumes named section exists and returns a zero-value when not.
func (f *File) Section(name string) *Section {
sec, err := f.GetSection(name)
if err != nil {
if name == "" {
name = DefaultSection
}
sec, _ = f.NewSection(name)
return sec
}
return sec
}
// SectionWithIndex assumes named section exists and returns a new section when not.
func (f *File) SectionWithIndex(name string, index int) *Section {
secs, err := f.SectionsByName(name)
if err != nil || len(secs) <= index {
// NOTE: It's OK here because the only possible error is empty section name,
// but if it's empty, this piece of code won't be executed.
newSec, _ := f.NewSection(name)
return newSec
}
return secs[index]
}
// Sections returns a list of Section stored in the current instance.
func (f *File) Sections() []*Section {
if f.BlockMode {
f.lock.RLock()
defer f.lock.RUnlock()
}
sections := make([]*Section, len(f.sectionList))
for i, name := range f.sectionList {
sections[i] = f.sections[name][f.sectionIndexes[i]]
}
return sections
}
// ChildSections returns a list of child sections of given section name.
func (f *File) ChildSections(name string) []*Section {
return f.Section(name).ChildSections()
}
// SectionStrings returns list of section names.
func (f *File) SectionStrings() []string {
list := make([]string, len(f.sectionList))
copy(list, f.sectionList)
return list
}
// DeleteSection deletes a section or all sections with given name.
func (f *File) DeleteSection(name string) {
secs, err := f.SectionsByName(name)
if err != nil {
return
}
for i := 0; i < len(secs); i++ {
// For non-unique sections, it is always needed to remove the first one so
// in the next iteration, the subsequent section continue having index 0.
// Ignoring the error as index 0 never returns an error.
_ = f.DeleteSectionWithIndex(name, 0)
}
}
// DeleteSectionWithIndex deletes a section with given name and index.
func (f *File) DeleteSectionWithIndex(name string, index int) error {
if !f.options.AllowNonUniqueSections && index != 0 {
return fmt.Errorf("delete section with non-zero index is only allowed when non-unique sections is enabled")
}
if len(name) == 0 {
name = DefaultSection
}
if f.options.Insensitive || f.options.InsensitiveSections {
name = strings.ToLower(name)
}
if f.BlockMode {
f.lock.Lock()
defer f.lock.Unlock()
}
// Count occurrences of the sections
occurrences := 0
sectionListCopy := make([]string, len(f.sectionList))
copy(sectionListCopy, f.sectionList)
for i, s := range sectionListCopy {
if s != name {
continue
}
if occurrences == index {
if len(f.sections[name]) <= 1 {
delete(f.sections, name) // The last one in the map
} else {
f.sections[name] = append(f.sections[name][:index], f.sections[name][index+1:]...)
}
// Fix section lists
f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
f.sectionIndexes = append(f.sectionIndexes[:i], f.sectionIndexes[i+1:]...)
} else if occurrences > index {
// Fix the indices of all following sections with this name.
f.sectionIndexes[i-1]--
}
occurrences++
}
return nil
}
func (f *File) reload(s dataSource) error {
r, err := s.ReadCloser()
if err != nil {
return err
}
defer r.Close()
return f.parse(r)
}
// Reload reloads and parses all data sources.
func (f *File) Reload() (err error) {
for _, s := range f.dataSources {
if err = f.reload(s); err != nil {
// In loose mode, we create an empty default section for nonexistent files.
if os.IsNotExist(err) && f.options.Loose {
_ = f.parse(bytes.NewBuffer(nil))
continue
}
return err
}
if f.options.ShortCircuit {
return nil
}
}
return nil
}
// Append appends one or more data sources and reloads automatically.
func (f *File) Append(source interface{}, others ...interface{}) error {
ds, err := parseDataSource(source)
if err != nil {
return err
}
f.dataSources = append(f.dataSources, ds)
for _, s := range others {
ds, err = parseDataSource(s)
if err != nil {
return err
}
f.dataSources = append(f.dataSources, ds)
}
return f.Reload()
}
func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
equalSign := DefaultFormatLeft + f.options.KeyValueDelimiterOnWrite + DefaultFormatRight
if PrettyFormat || PrettyEqual {
equalSign = fmt.Sprintf(" %s ", f.options.KeyValueDelimiterOnWrite)
}
// Use buffer to make sure target is safe until finish encoding.
buf := bytes.NewBuffer(nil)
for i, sname := range f.sectionList {
sec := f.SectionWithIndex(sname, f.sectionIndexes[i])
if len(sec.Comment) > 0 {
// Support multiline comments
lines := strings.Split(sec.Comment, LineBreak)
for i := range lines {
if lines[i][0] != '#' && lines[i][0] != ';' {
lines[i] = "; " + lines[i]
} else {
lines[i] = lines[i][:1] + " " + strings.TrimSpace(lines[i][1:])
}
if _, err := buf.WriteString(lines[i] + LineBreak); err != nil {
return nil, err
}
}
}
if i > 0 || DefaultHeader || (i == 0 && strings.ToUpper(sec.name) != DefaultSection) {
if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
return nil, err
}
} else {
// Write nothing if default section is empty
if len(sec.keyList) == 0 {
continue
}
}
if sec.isRawSection {
if _, err := buf.WriteString(sec.rawBody); err != nil {
return nil, err
}
if PrettySection {
// Put a line between sections
if _, err := buf.WriteString(LineBreak); err != nil {
return nil, err
}
}
continue
}
// Count and generate alignment length and buffer spaces using the
// longest key. Keys may be modified if they contain certain characters so
// we need to take that into account in our calculation.
alignLength := 0
if PrettyFormat {
for _, kname := range sec.keyList {
keyLength := len(kname)
// First case will surround key by ` and second by """
if strings.Contains(kname, "\"") || strings.ContainsAny(kname, f.options.KeyValueDelimiters) {
keyLength += 2
} else if strings.Contains(kname, "`") {
keyLength += 6
}
if keyLength > alignLength {
alignLength = keyLength
}
}
}
alignSpaces := bytes.Repeat([]byte(" "), alignLength)
KeyList:
for _, kname := range sec.keyList {
key := sec.Key(kname)
if len(key.Comment) > 0 {
if len(indent) > 0 && sname != DefaultSection {
buf.WriteString(indent)
}
// Support multiline comments
lines := strings.Split(key.Comment, LineBreak)
for i := range lines {
if lines[i][0] != '#' && lines[i][0] != ';' {
lines[i] = "; " + strings.TrimSpace(lines[i])
} else {
lines[i] = lines[i][:1] + " " + strings.TrimSpace(lines[i][1:])
}
if _, err := buf.WriteString(lines[i] + LineBreak); err != nil {
return nil, err
}
}
}
if len(indent) > 0 && sname != DefaultSection {
buf.WriteString(indent)
}
switch {
case key.isAutoIncrement:
kname = "-"
case strings.Contains(kname, "\"") || strings.ContainsAny(kname, f.options.KeyValueDelimiters):
kname = "`" + kname + "`"
case strings.Contains(kname, "`"):
kname = `"""` + kname + `"""`
}
for _, val := range key.ValueWithShadows() {
if _, err := buf.WriteString(kname); err != nil {
return nil, err
}
if key.isBooleanType {
if kname != sec.keyList[len(sec.keyList)-1] {
buf.WriteString(LineBreak)
}
continue KeyList
}
// Write out alignment spaces before "=" sign
if PrettyFormat {
buf.Write(alignSpaces[:alignLength-len(kname)])
}
// In case key value contains "\n", "`", "\"", "#" or ";"
if strings.ContainsAny(val, "\n`") {
val = `"""` + val + `"""`
} else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
val = "`" + val + "`"
} else if len(strings.TrimSpace(val)) != len(val) {
val = `"` + val + `"`
}
if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
return nil, err
}
}
for _, val := range key.nestedValues {
if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil {
return nil, err
}
}
}
if PrettySection {
// Put a line between sections
if _, err := buf.WriteString(LineBreak); err != nil {
return nil, err
}
}
}
return buf, nil
}
// WriteToIndent writes content into io.Writer with given indention.
// If PrettyFormat has been set to be true,
// it will align "=" sign with spaces under each section.
func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) {
buf, err := f.writeToBuffer(indent)
if err != nil {
return 0, err
}
return buf.WriteTo(w)
}
// WriteTo writes file content into io.Writer.
func (f *File) WriteTo(w io.Writer) (int64, error) {
return f.WriteToIndent(w, "")
}
// SaveToIndent writes content to file system with given value indention.
func (f *File) SaveToIndent(filename, indent string) error {
// Note: Because we are truncating with os.Create,
// so it's safer to save to a temporary file location and rename after done.
buf, err := f.writeToBuffer(indent)
if err != nil {
return err
}
return ioutil.WriteFile(filename, buf.Bytes(), 0666)
}
// SaveTo writes content to file system.
func (f *File) SaveTo(filename string) error {
return f.SaveToIndent(filename, "")
}

View File

@ -1,24 +0,0 @@
// Copyright 2019 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
func inSlice(str string, s []string) bool {
for _, v := range s {
if str == v {
return true
}
}
return false
}

View File

@ -1,176 +0,0 @@
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// Package ini provides INI file read and write functionality in Go.
package ini
import (
"os"
"regexp"
"runtime"
"strings"
)
const (
// DefaultSection is the name of default section. You can use this constant or the string literal.
// In most of cases, an empty string is all you need to access the section.
DefaultSection = "DEFAULT"
// Maximum allowed depth when recursively substituing variable names.
depthValues = 99
)
var (
// LineBreak is the delimiter to determine or compose a new line.
// This variable will be changed to "\r\n" automatically on Windows at package init time.
LineBreak = "\n"
// Variable regexp pattern: %(variable)s
varPattern = regexp.MustCompile(`%\(([^)]+)\)s`)
// DefaultHeader explicitly writes default section header.
DefaultHeader = false
// PrettySection indicates whether to put a line between sections.
PrettySection = true
// PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output
// or reduce all possible spaces for compact format.
PrettyFormat = true
// PrettyEqual places spaces around "=" sign even when PrettyFormat is false.
PrettyEqual = false
// DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled.
DefaultFormatLeft = ""
// DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled.
DefaultFormatRight = ""
)
var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test")
func init() {
if runtime.GOOS == "windows" && !inTest {
LineBreak = "\r\n"
}
}
// LoadOptions contains all customized options used for load data source(s).
type LoadOptions struct {
// Loose indicates whether the parser should ignore nonexistent files or return error.
Loose bool
// Insensitive indicates whether the parser forces all section and key names to lowercase.
Insensitive bool
// InsensitiveSections indicates whether the parser forces all section to lowercase.
InsensitiveSections bool
// InsensitiveKeys indicates whether the parser forces all key names to lowercase.
InsensitiveKeys bool
// IgnoreContinuation indicates whether to ignore continuation lines while parsing.
IgnoreContinuation bool
// IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value.
IgnoreInlineComment bool
// SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs.
SkipUnrecognizableLines bool
// ShortCircuit indicates whether to ignore other configuration sources after loaded the first available configuration source.
ShortCircuit bool
// AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
// This type of keys are mostly used in my.cnf.
AllowBooleanKeys bool
// AllowShadows indicates whether to keep track of keys with same name under same section.
AllowShadows bool
// AllowNestedValues indicates whether to allow AWS-like nested values.
// Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values
AllowNestedValues bool
// AllowPythonMultilineValues indicates whether to allow Python-like multi-line values.
// Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure
// Relevant quote: Values can also span multiple lines, as long as they are indented deeper
// than the first line of the value.
AllowPythonMultilineValues bool
// SpaceBeforeInlineComment indicates whether to allow comment symbols (\# and \;) inside value.
// Docs: https://docs.python.org/2/library/configparser.html
// Quote: Comments may appear on their own in an otherwise empty line, or may be entered in lines holding values or section names.
// In the latter case, they need to be preceded by a whitespace character to be recognized as a comment.
SpaceBeforeInlineComment bool
// UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format
// when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value"
UnescapeValueDoubleQuotes bool
// UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format
// when value is NOT surrounded by any quotes.
// Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all.
UnescapeValueCommentSymbols bool
// UnparseableSections stores a list of blocks that are allowed with raw content which do not otherwise
// conform to key/value pairs. Specify the names of those blocks here.
UnparseableSections []string
// KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:".
KeyValueDelimiters string
// KeyValueDelimiterOnWrite is the delimiter that are used to separate key and value output. By default, it is "=".
KeyValueDelimiterOnWrite string
// ChildSectionDelimiter is the delimiter that is used to separate child sections. By default, it is ".".
ChildSectionDelimiter string
// PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes).
PreserveSurroundedQuote bool
// DebugFunc is called to collect debug information (currently only useful to debug parsing Python-style multiline values).
DebugFunc DebugFunc
// ReaderBufferSize is the buffer size of the reader in bytes.
ReaderBufferSize int
// AllowNonUniqueSections indicates whether to allow sections with the same name multiple times.
AllowNonUniqueSections bool
// AllowDuplicateShadowValues indicates whether values for shadowed keys should be deduplicated.
AllowDuplicateShadowValues bool
}
// DebugFunc is the type of function called to log parse events.
type DebugFunc func(message string)
// LoadSources allows caller to apply customized options for loading from data source(s).
func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) {
sources := make([]dataSource, len(others)+1)
sources[0], err = parseDataSource(source)
if err != nil {
return nil, err
}
for i := range others {
sources[i+1], err = parseDataSource(others[i])
if err != nil {
return nil, err
}
}
f := newFile(sources, opts)
if err = f.Reload(); err != nil {
return nil, err
}
return f, nil
}
// Load loads and parses from INI data sources.
// Arguments can be mixed of file name with string type, or raw data in []byte.
// It will return error if list contains nonexistent files.
func Load(source interface{}, others ...interface{}) (*File, error) {
return LoadSources(LoadOptions{}, source, others...)
}
// LooseLoad has exactly same functionality as Load function
// except it ignores nonexistent files instead of returning error.
func LooseLoad(source interface{}, others ...interface{}) (*File, error) {
return LoadSources(LoadOptions{Loose: true}, source, others...)
}
// InsensitiveLoad has exactly same functionality as Load function
// except it forces all section and key names to be lowercased.
func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) {
return LoadSources(LoadOptions{Insensitive: true}, source, others...)
}
// ShadowLoad has exactly same functionality as Load function
// except it allows have shadow keys.
func ShadowLoad(source interface{}, others ...interface{}) (*File, error) {
return LoadSources(LoadOptions{AllowShadows: true}, source, others...)
}

View File

@ -1,828 +0,0 @@
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"
"time"
)
// Key represents a key under a section.
type Key struct {
s *Section
Comment string
name string
value string
isAutoIncrement bool
isBooleanType bool
isShadow bool
shadows []*Key
nestedValues []string
}
// newKey simply return a key object with given values.
func newKey(s *Section, name, val string) *Key {
return &Key{
s: s,
name: name,
value: val,
}
}
func (k *Key) addShadow(val string) error {
if k.isShadow {
return errors.New("cannot add shadow to another shadow key")
} else if k.isAutoIncrement || k.isBooleanType {
return errors.New("cannot add shadow to auto-increment or boolean key")
}
if !k.s.f.options.AllowDuplicateShadowValues {
// Deduplicate shadows based on their values.
if k.value == val {
return nil
}
for i := range k.shadows {
if k.shadows[i].value == val {
return nil
}
}
}
shadow := newKey(k.s, k.name, val)
shadow.isShadow = true
k.shadows = append(k.shadows, shadow)
return nil
}
// AddShadow adds a new shadow key to itself.
func (k *Key) AddShadow(val string) error {
if !k.s.f.options.AllowShadows {
return errors.New("shadow key is not allowed")
}
return k.addShadow(val)
}
func (k *Key) addNestedValue(val string) error {
if k.isAutoIncrement || k.isBooleanType {
return errors.New("cannot add nested value to auto-increment or boolean key")
}
k.nestedValues = append(k.nestedValues, val)
return nil
}
// AddNestedValue adds a nested value to the key.
func (k *Key) AddNestedValue(val string) error {
if !k.s.f.options.AllowNestedValues {
return errors.New("nested value is not allowed")
}
return k.addNestedValue(val)
}
// ValueMapper represents a mapping function for values, e.g. os.ExpandEnv
type ValueMapper func(string) string
// Name returns name of key.
func (k *Key) Name() string {
return k.name
}
// Value returns raw value of key for performance purpose.
func (k *Key) Value() string {
return k.value
}
// ValueWithShadows returns raw values of key and its shadows if any.
func (k *Key) ValueWithShadows() []string {
if len(k.shadows) == 0 {
return []string{k.value}
}
vals := make([]string, len(k.shadows)+1)
vals[0] = k.value
for i := range k.shadows {
vals[i+1] = k.shadows[i].value
}
return vals
}
// NestedValues returns nested values stored in the key.
// It is possible returned value is nil if no nested values stored in the key.
func (k *Key) NestedValues() []string {
return k.nestedValues
}
// transformValue takes a raw value and transforms to its final string.
func (k *Key) transformValue(val string) string {
if k.s.f.ValueMapper != nil {
val = k.s.f.ValueMapper(val)
}
// Fail-fast if no indicate char found for recursive value
if !strings.Contains(val, "%") {
return val
}
for i := 0; i < depthValues; i++ {
vr := varPattern.FindString(val)
if len(vr) == 0 {
break
}
// Take off leading '%(' and trailing ')s'.
noption := vr[2 : len(vr)-2]
// Search in the same section.
// If not found or found the key itself, then search again in default section.
nk, err := k.s.GetKey(noption)
if err != nil || k == nk {
nk, _ = k.s.f.Section("").GetKey(noption)
if nk == nil {
// Stop when no results found in the default section,
// and returns the value as-is.
break
}
}
// Substitute by new value and take off leading '%(' and trailing ')s'.
val = strings.Replace(val, vr, nk.value, -1)
}
return val
}
// String returns string representation of value.
func (k *Key) String() string {
return k.transformValue(k.value)
}
// Validate accepts a validate function which can
// return modifed result as key value.
func (k *Key) Validate(fn func(string) string) string {
return fn(k.String())
}
// parseBool returns the boolean value represented by the string.
//
// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On,
// 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off.
// Any other value returns an error.
func parseBool(str string) (value bool, err error) {
switch str {
case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "y", "ON", "on", "On":
return true, nil
case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "n", "OFF", "off", "Off":
return false, nil
}
return false, fmt.Errorf("parsing \"%s\": invalid syntax", str)
}
// Bool returns bool type value.
func (k *Key) Bool() (bool, error) {
return parseBool(k.String())
}
// Float64 returns float64 type value.
func (k *Key) Float64() (float64, error) {
return strconv.ParseFloat(k.String(), 64)
}
// Int returns int type value.
func (k *Key) Int() (int, error) {
v, err := strconv.ParseInt(k.String(), 0, 64)
return int(v), err
}
// Int64 returns int64 type value.
func (k *Key) Int64() (int64, error) {
return strconv.ParseInt(k.String(), 0, 64)
}
// Uint returns uint type valued.
func (k *Key) Uint() (uint, error) {
u, e := strconv.ParseUint(k.String(), 0, 64)
return uint(u), e
}
// Uint64 returns uint64 type value.
func (k *Key) Uint64() (uint64, error) {
return strconv.ParseUint(k.String(), 0, 64)
}
// Duration returns time.Duration type value.
func (k *Key) Duration() (time.Duration, error) {
return time.ParseDuration(k.String())
}
// TimeFormat parses with given format and returns time.Time type value.
func (k *Key) TimeFormat(format string) (time.Time, error) {
return time.Parse(format, k.String())
}
// Time parses with RFC3339 format and returns time.Time type value.
func (k *Key) Time() (time.Time, error) {
return k.TimeFormat(time.RFC3339)
}
// MustString returns default value if key value is empty.
func (k *Key) MustString(defaultVal string) string {
val := k.String()
if len(val) == 0 {
k.value = defaultVal
return defaultVal
}
return val
}
// MustBool always returns value without error,
// it returns false if error occurs.
func (k *Key) MustBool(defaultVal ...bool) bool {
val, err := k.Bool()
if len(defaultVal) > 0 && err != nil {
k.value = strconv.FormatBool(defaultVal[0])
return defaultVal[0]
}
return val
}
// MustFloat64 always returns value without error,
// it returns 0.0 if error occurs.
func (k *Key) MustFloat64(defaultVal ...float64) float64 {
val, err := k.Float64()
if len(defaultVal) > 0 && err != nil {
k.value = strconv.FormatFloat(defaultVal[0], 'f', -1, 64)
return defaultVal[0]
}
return val
}
// MustInt always returns value without error,
// it returns 0 if error occurs.
func (k *Key) MustInt(defaultVal ...int) int {
val, err := k.Int()
if len(defaultVal) > 0 && err != nil {
k.value = strconv.FormatInt(int64(defaultVal[0]), 10)
return defaultVal[0]
}
return val
}
// MustInt64 always returns value without error,
// it returns 0 if error occurs.
func (k *Key) MustInt64(defaultVal ...int64) int64 {
val, err := k.Int64()
if len(defaultVal) > 0 && err != nil {
k.value = strconv.FormatInt(defaultVal[0], 10)
return defaultVal[0]
}
return val
}
// MustUint always returns value without error,
// it returns 0 if error occurs.
func (k *Key) MustUint(defaultVal ...uint) uint {
val, err := k.Uint()
if len(defaultVal) > 0 && err != nil {
k.value = strconv.FormatUint(uint64(defaultVal[0]), 10)
return defaultVal[0]
}
return val
}
// MustUint64 always returns value without error,
// it returns 0 if error occurs.
func (k *Key) MustUint64(defaultVal ...uint64) uint64 {
val, err := k.Uint64()
if len(defaultVal) > 0 && err != nil {
k.value = strconv.FormatUint(defaultVal[0], 10)
return defaultVal[0]
}
return val
}
// MustDuration always returns value without error,
// it returns zero value if error occurs.
func (k *Key) MustDuration(defaultVal ...time.Duration) time.Duration {
val, err := k.Duration()
if len(defaultVal) > 0 && err != nil {
k.value = defaultVal[0].String()
return defaultVal[0]
}
return val
}
// MustTimeFormat always parses with given format and returns value without error,
// it returns zero value if error occurs.
func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time {
val, err := k.TimeFormat(format)
if len(defaultVal) > 0 && err != nil {
k.value = defaultVal[0].Format(format)
return defaultVal[0]
}
return val
}
// MustTime always parses with RFC3339 format and returns value without error,
// it returns zero value if error occurs.
func (k *Key) MustTime(defaultVal ...time.Time) time.Time {
return k.MustTimeFormat(time.RFC3339, defaultVal...)
}
// In always returns value without error,
// it returns default value if error occurs or doesn't fit into candidates.
func (k *Key) In(defaultVal string, candidates []string) string {
val := k.String()
for _, cand := range candidates {
if val == cand {
return val
}
}
return defaultVal
}
// InFloat64 always returns value without error,
// it returns default value if error occurs or doesn't fit into candidates.
func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 {
val := k.MustFloat64()
for _, cand := range candidates {
if val == cand {
return val
}
}
return defaultVal
}
// InInt always returns value without error,
// it returns default value if error occurs or doesn't fit into candidates.
func (k *Key) InInt(defaultVal int, candidates []int) int {
val := k.MustInt()
for _, cand := range candidates {
if val == cand {
return val
}
}
return defaultVal
}
// InInt64 always returns value without error,
// it returns default value if error occurs or doesn't fit into candidates.
func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 {
val := k.MustInt64()
for _, cand := range candidates {
if val == cand {
return val
}
}
return defaultVal
}
// InUint always returns value without error,
// it returns default value if error occurs or doesn't fit into candidates.
func (k *Key) InUint(defaultVal uint, candidates []uint) uint {
val := k.MustUint()
for _, cand := range candidates {
if val == cand {
return val
}
}
return defaultVal
}
// InUint64 always returns value without error,
// it returns default value if error occurs or doesn't fit into candidates.
func (k *Key) InUint64(defaultVal uint64, candidates []uint64) uint64 {
val := k.MustUint64()
for _, cand := range candidates {
if val == cand {
return val
}
}
return defaultVal
}
// InTimeFormat always parses with given format and returns value without error,
// it returns default value if error occurs or doesn't fit into candidates.
func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time {
val := k.MustTimeFormat(format)
for _, cand := range candidates {
if val == cand {
return val
}
}
return defaultVal
}
// InTime always parses with RFC3339 format and returns value without error,
// it returns default value if error occurs or doesn't fit into candidates.
func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time {
return k.InTimeFormat(time.RFC3339, defaultVal, candidates)
}
// RangeFloat64 checks if value is in given range inclusively,
// and returns default value if it's not.
func (k *Key) RangeFloat64(defaultVal, min, max float64) float64 {
val := k.MustFloat64()
if val < min || val > max {
return defaultVal
}
return val
}
// RangeInt checks if value is in given range inclusively,
// and returns default value if it's not.
func (k *Key) RangeInt(defaultVal, min, max int) int {
val := k.MustInt()
if val < min || val > max {
return defaultVal
}
return val
}
// RangeInt64 checks if value is in given range inclusively,
// and returns default value if it's not.
func (k *Key) RangeInt64(defaultVal, min, max int64) int64 {
val := k.MustInt64()
if val < min || val > max {
return defaultVal
}
return val
}
// RangeTimeFormat checks if value with given format is in given range inclusively,
// and returns default value if it's not.
func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time {
val := k.MustTimeFormat(format)
if val.Unix() < min.Unix() || val.Unix() > max.Unix() {
return defaultVal
}
return val
}
// RangeTime checks if value with RFC3339 format is in given range inclusively,
// and returns default value if it's not.
func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time {
return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max)
}
// Strings returns list of string divided by given delimiter.
func (k *Key) Strings(delim string) []string {
str := k.String()
if len(str) == 0 {
return []string{}
}
runes := []rune(str)
vals := make([]string, 0, 2)
var buf bytes.Buffer
escape := false
idx := 0
for {
if escape {
escape = false
if runes[idx] != '\\' && !strings.HasPrefix(string(runes[idx:]), delim) {
buf.WriteRune('\\')
}
buf.WriteRune(runes[idx])
} else {
if runes[idx] == '\\' {
escape = true
} else if strings.HasPrefix(string(runes[idx:]), delim) {
idx += len(delim) - 1
vals = append(vals, strings.TrimSpace(buf.String()))
buf.Reset()
} else {
buf.WriteRune(runes[idx])
}
}
idx++
if idx == len(runes) {
break
}
}
if buf.Len() > 0 {
vals = append(vals, strings.TrimSpace(buf.String()))
}
return vals
}
// StringsWithShadows returns list of string divided by given delimiter.
// Shadows will also be appended if any.
func (k *Key) StringsWithShadows(delim string) []string {
vals := k.ValueWithShadows()
results := make([]string, 0, len(vals)*2)
for i := range vals {
if len(vals) == 0 {
continue
}
results = append(results, strings.Split(vals[i], delim)...)
}
for i := range results {
results[i] = k.transformValue(strings.TrimSpace(results[i]))
}
return results
}
// Float64s returns list of float64 divided by given delimiter. Any invalid input will be treated as zero value.
func (k *Key) Float64s(delim string) []float64 {
vals, _ := k.parseFloat64s(k.Strings(delim), true, false)
return vals
}
// Ints returns list of int divided by given delimiter. Any invalid input will be treated as zero value.
func (k *Key) Ints(delim string) []int {
vals, _ := k.parseInts(k.Strings(delim), true, false)
return vals
}
// Int64s returns list of int64 divided by given delimiter. Any invalid input will be treated as zero value.
func (k *Key) Int64s(delim string) []int64 {
vals, _ := k.parseInt64s(k.Strings(delim), true, false)
return vals
}
// Uints returns list of uint divided by given delimiter. Any invalid input will be treated as zero value.
func (k *Key) Uints(delim string) []uint {
vals, _ := k.parseUints(k.Strings(delim), true, false)
return vals
}
// Uint64s returns list of uint64 divided by given delimiter. Any invalid input will be treated as zero value.
func (k *Key) Uint64s(delim string) []uint64 {
vals, _ := k.parseUint64s(k.Strings(delim), true, false)
return vals
}
// Bools returns list of bool divided by given delimiter. Any invalid input will be treated as zero value.
func (k *Key) Bools(delim string) []bool {
vals, _ := k.parseBools(k.Strings(delim), true, false)
return vals
}
// TimesFormat parses with given format and returns list of time.Time divided by given delimiter.
// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
func (k *Key) TimesFormat(format, delim string) []time.Time {
vals, _ := k.parseTimesFormat(format, k.Strings(delim), true, false)
return vals
}
// Times parses with RFC3339 format and returns list of time.Time divided by given delimiter.
// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
func (k *Key) Times(delim string) []time.Time {
return k.TimesFormat(time.RFC3339, delim)
}
// ValidFloat64s returns list of float64 divided by given delimiter. If some value is not float, then
// it will not be included to result list.
func (k *Key) ValidFloat64s(delim string) []float64 {
vals, _ := k.parseFloat64s(k.Strings(delim), false, false)
return vals
}
// ValidInts returns list of int divided by given delimiter. If some value is not integer, then it will
// not be included to result list.
func (k *Key) ValidInts(delim string) []int {
vals, _ := k.parseInts(k.Strings(delim), false, false)
return vals
}
// ValidInt64s returns list of int64 divided by given delimiter. If some value is not 64-bit integer,
// then it will not be included to result list.
func (k *Key) ValidInt64s(delim string) []int64 {
vals, _ := k.parseInt64s(k.Strings(delim), false, false)
return vals
}
// ValidUints returns list of uint divided by given delimiter. If some value is not unsigned integer,
// then it will not be included to result list.
func (k *Key) ValidUints(delim string) []uint {
vals, _ := k.parseUints(k.Strings(delim), false, false)
return vals
}
// ValidUint64s returns list of uint64 divided by given delimiter. If some value is not 64-bit unsigned
// integer, then it will not be included to result list.
func (k *Key) ValidUint64s(delim string) []uint64 {
vals, _ := k.parseUint64s(k.Strings(delim), false, false)
return vals
}
// ValidBools returns list of bool divided by given delimiter. If some value is not 64-bit unsigned
// integer, then it will not be included to result list.
func (k *Key) ValidBools(delim string) []bool {
vals, _ := k.parseBools(k.Strings(delim), false, false)
return vals
}
// ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter.
func (k *Key) ValidTimesFormat(format, delim string) []time.Time {
vals, _ := k.parseTimesFormat(format, k.Strings(delim), false, false)
return vals
}
// ValidTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter.
func (k *Key) ValidTimes(delim string) []time.Time {
return k.ValidTimesFormat(time.RFC3339, delim)
}
// StrictFloat64s returns list of float64 divided by given delimiter or error on first invalid input.
func (k *Key) StrictFloat64s(delim string) ([]float64, error) {
return k.parseFloat64s(k.Strings(delim), false, true)
}
// StrictInts returns list of int divided by given delimiter or error on first invalid input.
func (k *Key) StrictInts(delim string) ([]int, error) {
return k.parseInts(k.Strings(delim), false, true)
}
// StrictInt64s returns list of int64 divided by given delimiter or error on first invalid input.
func (k *Key) StrictInt64s(delim string) ([]int64, error) {
return k.parseInt64s(k.Strings(delim), false, true)
}
// StrictUints returns list of uint divided by given delimiter or error on first invalid input.
func (k *Key) StrictUints(delim string) ([]uint, error) {
return k.parseUints(k.Strings(delim), false, true)
}
// StrictUint64s returns list of uint64 divided by given delimiter or error on first invalid input.
func (k *Key) StrictUint64s(delim string) ([]uint64, error) {
return k.parseUint64s(k.Strings(delim), false, true)
}
// StrictBools returns list of bool divided by given delimiter or error on first invalid input.
func (k *Key) StrictBools(delim string) ([]bool, error) {
return k.parseBools(k.Strings(delim), false, true)
}
// StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter
// or error on first invalid input.
func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) {
return k.parseTimesFormat(format, k.Strings(delim), false, true)
}
// StrictTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter
// or error on first invalid input.
func (k *Key) StrictTimes(delim string) ([]time.Time, error) {
return k.StrictTimesFormat(time.RFC3339, delim)
}
// parseBools transforms strings to bools.
func (k *Key) parseBools(strs []string, addInvalid, returnOnInvalid bool) ([]bool, error) {
vals := make([]bool, 0, len(strs))
parser := func(str string) (interface{}, error) {
val, err := parseBool(str)
return val, err
}
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
if err == nil {
for _, val := range rawVals {
vals = append(vals, val.(bool))
}
}
return vals, err
}
// parseFloat64s transforms strings to float64s.
func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) {
vals := make([]float64, 0, len(strs))
parser := func(str string) (interface{}, error) {
val, err := strconv.ParseFloat(str, 64)
return val, err
}
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
if err == nil {
for _, val := range rawVals {
vals = append(vals, val.(float64))
}
}
return vals, err
}
// parseInts transforms strings to ints.
func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) {
vals := make([]int, 0, len(strs))
parser := func(str string) (interface{}, error) {
val, err := strconv.ParseInt(str, 0, 64)
return val, err
}
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
if err == nil {
for _, val := range rawVals {
vals = append(vals, int(val.(int64)))
}
}
return vals, err
}
// parseInt64s transforms strings to int64s.
func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) {
vals := make([]int64, 0, len(strs))
parser := func(str string) (interface{}, error) {
val, err := strconv.ParseInt(str, 0, 64)
return val, err
}
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
if err == nil {
for _, val := range rawVals {
vals = append(vals, val.(int64))
}
}
return vals, err
}
// parseUints transforms strings to uints.
func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) {
vals := make([]uint, 0, len(strs))
parser := func(str string) (interface{}, error) {
val, err := strconv.ParseUint(str, 0, 64)
return val, err
}
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
if err == nil {
for _, val := range rawVals {
vals = append(vals, uint(val.(uint64)))
}
}
return vals, err
}
// parseUint64s transforms strings to uint64s.
func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) {
vals := make([]uint64, 0, len(strs))
parser := func(str string) (interface{}, error) {
val, err := strconv.ParseUint(str, 0, 64)
return val, err
}
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
if err == nil {
for _, val := range rawVals {
vals = append(vals, val.(uint64))
}
}
return vals, err
}
type Parser func(str string) (interface{}, error)
// parseTimesFormat transforms strings to times in given format.
func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) {
vals := make([]time.Time, 0, len(strs))
parser := func(str string) (interface{}, error) {
val, err := time.Parse(format, str)
return val, err
}
rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser)
if err == nil {
for _, val := range rawVals {
vals = append(vals, val.(time.Time))
}
}
return vals, err
}
// doParse transforms strings to different types
func (k *Key) doParse(strs []string, addInvalid, returnOnInvalid bool, parser Parser) ([]interface{}, error) {
vals := make([]interface{}, 0, len(strs))
for _, str := range strs {
val, err := parser(str)
if err != nil && returnOnInvalid {
return nil, err
}
if err == nil || addInvalid {
vals = append(vals, val)
}
}
return vals, nil
}
// SetValue changes key value.
func (k *Key) SetValue(v string) {
if k.s.f.BlockMode {
k.s.f.lock.Lock()
defer k.s.f.lock.Unlock()
}
k.value = v
k.s.keysHash[k.name] = v
}

View File

@ -1,513 +0,0 @@
// Copyright 2015 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"bufio"
"bytes"
"fmt"
"io"
"regexp"
"strconv"
"strings"
"unicode"
)
const minReaderBufferSize = 4096
var pythonMultiline = regexp.MustCompile(`^([\t\f ]+)(.*)`)
type parserOptions struct {
IgnoreContinuation bool
IgnoreInlineComment bool
AllowPythonMultilineValues bool
SpaceBeforeInlineComment bool
UnescapeValueDoubleQuotes bool
UnescapeValueCommentSymbols bool
PreserveSurroundedQuote bool
DebugFunc DebugFunc
ReaderBufferSize int
}
type parser struct {
buf *bufio.Reader
options parserOptions
isEOF bool
count int
comment *bytes.Buffer
}
func (p *parser) debug(format string, args ...interface{}) {
if p.options.DebugFunc != nil {
p.options.DebugFunc(fmt.Sprintf(format, args...))
}
}
func newParser(r io.Reader, opts parserOptions) *parser {
size := opts.ReaderBufferSize
if size < minReaderBufferSize {
size = minReaderBufferSize
}
return &parser{
buf: bufio.NewReaderSize(r, size),
options: opts,
count: 1,
comment: &bytes.Buffer{},
}
}
// BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format.
// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
func (p *parser) BOM() error {
mask, err := p.buf.Peek(2)
if err != nil && err != io.EOF {
return err
} else if len(mask) < 2 {
return nil
}
switch {
case mask[0] == 254 && mask[1] == 255:
fallthrough
case mask[0] == 255 && mask[1] == 254:
_, err = p.buf.Read(mask)
if err != nil {
return err
}
case mask[0] == 239 && mask[1] == 187:
mask, err := p.buf.Peek(3)
if err != nil && err != io.EOF {
return err
} else if len(mask) < 3 {
return nil
}
if mask[2] == 191 {
_, err = p.buf.Read(mask)
if err != nil {
return err
}
}
}
return nil
}
func (p *parser) readUntil(delim byte) ([]byte, error) {
data, err := p.buf.ReadBytes(delim)
if err != nil {
if err == io.EOF {
p.isEOF = true
} else {
return nil, err
}
}
return data, nil
}
func cleanComment(in []byte) ([]byte, bool) {
i := bytes.IndexAny(in, "#;")
if i == -1 {
return nil, false
}
return in[i:], true
}
func readKeyName(delimiters string, in []byte) (string, int, error) {
line := string(in)
// Check if key name surrounded by quotes.
var keyQuote string
if line[0] == '"' {
if len(line) > 6 && line[0:3] == `"""` {
keyQuote = `"""`
} else {
keyQuote = `"`
}
} else if line[0] == '`' {
keyQuote = "`"
}
// Get out key name
var endIdx int
if len(keyQuote) > 0 {
startIdx := len(keyQuote)
// FIXME: fail case -> """"""name"""=value
pos := strings.Index(line[startIdx:], keyQuote)
if pos == -1 {
return "", -1, fmt.Errorf("missing closing key quote: %s", line)
}
pos += startIdx
// Find key-value delimiter
i := strings.IndexAny(line[pos+startIdx:], delimiters)
if i < 0 {
return "", -1, ErrDelimiterNotFound{line}
}
endIdx = pos + i
return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil
}
endIdx = strings.IndexAny(line, delimiters)
if endIdx < 0 {
return "", -1, ErrDelimiterNotFound{line}
}
return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil
}
func (p *parser) readMultilines(line, val, valQuote string) (string, error) {
for {
data, err := p.readUntil('\n')
if err != nil {
return "", err
}
next := string(data)
pos := strings.LastIndex(next, valQuote)
if pos > -1 {
val += next[:pos]
comment, has := cleanComment([]byte(next[pos:]))
if has {
p.comment.Write(bytes.TrimSpace(comment))
}
break
}
val += next
if p.isEOF {
return "", fmt.Errorf("missing closing key quote from %q to %q", line, next)
}
}
return val, nil
}
func (p *parser) readContinuationLines(val string) (string, error) {
for {
data, err := p.readUntil('\n')
if err != nil {
return "", err
}
next := strings.TrimSpace(string(data))
if len(next) == 0 {
break
}
val += next
if val[len(val)-1] != '\\' {
break
}
val = val[:len(val)-1]
}
return val, nil
}
// hasSurroundedQuote check if and only if the first and last characters
// are quotes \" or \'.
// It returns false if any other parts also contain same kind of quotes.
func hasSurroundedQuote(in string, quote byte) bool {
return len(in) >= 2 && in[0] == quote && in[len(in)-1] == quote &&
strings.IndexByte(in[1:], quote) == len(in)-2
}
func (p *parser) readValue(in []byte, bufferSize int) (string, error) {
line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
if len(line) == 0 {
if p.options.AllowPythonMultilineValues && len(in) > 0 && in[len(in)-1] == '\n' {
return p.readPythonMultilines(line, bufferSize)
}
return "", nil
}
var valQuote string
if len(line) > 3 && line[0:3] == `"""` {
valQuote = `"""`
} else if line[0] == '`' {
valQuote = "`"
} else if p.options.UnescapeValueDoubleQuotes && line[0] == '"' {
valQuote = `"`
}
if len(valQuote) > 0 {
startIdx := len(valQuote)
pos := strings.LastIndex(line[startIdx:], valQuote)
// Check for multi-line value
if pos == -1 {
return p.readMultilines(line, line[startIdx:], valQuote)
}
if p.options.UnescapeValueDoubleQuotes && valQuote == `"` {
return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil
}
return line[startIdx : pos+startIdx], nil
}
lastChar := line[len(line)-1]
// Won't be able to reach here if value only contains whitespace
line = strings.TrimSpace(line)
trimmedLastChar := line[len(line)-1]
// Check continuation lines when desired
if !p.options.IgnoreContinuation && trimmedLastChar == '\\' {
return p.readContinuationLines(line[:len(line)-1])
}
// Check if ignore inline comment
if !p.options.IgnoreInlineComment {
var i int
if p.options.SpaceBeforeInlineComment {
i = strings.Index(line, " #")
if i == -1 {
i = strings.Index(line, " ;")
}
} else {
i = strings.IndexAny(line, "#;")
}
if i > -1 {
p.comment.WriteString(line[i:])
line = strings.TrimSpace(line[:i])
}
}
// Trim single and double quotes
if (hasSurroundedQuote(line, '\'') ||
hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote {
line = line[1 : len(line)-1]
} else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols {
line = strings.ReplaceAll(line, `\;`, ";")
line = strings.ReplaceAll(line, `\#`, "#")
} else if p.options.AllowPythonMultilineValues && lastChar == '\n' {
return p.readPythonMultilines(line, bufferSize)
}
return line, nil
}
func (p *parser) readPythonMultilines(line string, bufferSize int) (string, error) {
parserBufferPeekResult, _ := p.buf.Peek(bufferSize)
peekBuffer := bytes.NewBuffer(parserBufferPeekResult)
for {
peekData, peekErr := peekBuffer.ReadBytes('\n')
if peekErr != nil && peekErr != io.EOF {
p.debug("readPythonMultilines: failed to peek with error: %v", peekErr)
return "", peekErr
}
p.debug("readPythonMultilines: parsing %q", string(peekData))
peekMatches := pythonMultiline.FindStringSubmatch(string(peekData))
p.debug("readPythonMultilines: matched %d parts", len(peekMatches))
for n, v := range peekMatches {
p.debug(" %d: %q", n, v)
}
// Return if not a Python multiline value.
if len(peekMatches) != 3 {
p.debug("readPythonMultilines: end of value, got: %q", line)
return line, nil
}
// Advance the parser reader (buffer) in-sync with the peek buffer.
_, err := p.buf.Discard(len(peekData))
if err != nil {
p.debug("readPythonMultilines: failed to skip to the end, returning error")
return "", err
}
line += "\n" + peekMatches[0]
}
}
// parse parses data through an io.Reader.
func (f *File) parse(reader io.Reader) (err error) {
p := newParser(reader, parserOptions{
IgnoreContinuation: f.options.IgnoreContinuation,
IgnoreInlineComment: f.options.IgnoreInlineComment,
AllowPythonMultilineValues: f.options.AllowPythonMultilineValues,
SpaceBeforeInlineComment: f.options.SpaceBeforeInlineComment,
UnescapeValueDoubleQuotes: f.options.UnescapeValueDoubleQuotes,
UnescapeValueCommentSymbols: f.options.UnescapeValueCommentSymbols,
PreserveSurroundedQuote: f.options.PreserveSurroundedQuote,
DebugFunc: f.options.DebugFunc,
ReaderBufferSize: f.options.ReaderBufferSize,
})
if err = p.BOM(); err != nil {
return fmt.Errorf("BOM: %v", err)
}
// Ignore error because default section name is never empty string.
name := DefaultSection
if f.options.Insensitive || f.options.InsensitiveSections {
name = strings.ToLower(DefaultSection)
}
section, _ := f.NewSection(name)
// This "last" is not strictly equivalent to "previous one" if current key is not the first nested key
var isLastValueEmpty bool
var lastRegularKey *Key
var line []byte
var inUnparseableSection bool
// NOTE: Iterate and increase `currentPeekSize` until
// the size of the parser buffer is found.
// TODO(unknwon): When Golang 1.10 is the lowest version supported, replace with `parserBufferSize := p.buf.Size()`.
parserBufferSize := 0
// NOTE: Peek 4kb at a time.
currentPeekSize := minReaderBufferSize
if f.options.AllowPythonMultilineValues {
for {
peekBytes, _ := p.buf.Peek(currentPeekSize)
peekBytesLength := len(peekBytes)
if parserBufferSize >= peekBytesLength {
break
}
currentPeekSize *= 2
parserBufferSize = peekBytesLength
}
}
for !p.isEOF {
line, err = p.readUntil('\n')
if err != nil {
return err
}
if f.options.AllowNestedValues &&
isLastValueEmpty && len(line) > 0 {
if line[0] == ' ' || line[0] == '\t' {
err = lastRegularKey.addNestedValue(string(bytes.TrimSpace(line)))
if err != nil {
return err
}
continue
}
}
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
if len(line) == 0 {
continue
}
// Comments
if line[0] == '#' || line[0] == ';' {
// Note: we do not care ending line break,
// it is needed for adding second line,
// so just clean it once at the end when set to value.
p.comment.Write(line)
continue
}
// Section
if line[0] == '[' {
// Read to the next ']' (TODO: support quoted strings)
closeIdx := bytes.LastIndexByte(line, ']')
if closeIdx == -1 {
return fmt.Errorf("unclosed section: %s", line)
}
name := string(line[1:closeIdx])
section, err = f.NewSection(name)
if err != nil {
return err
}
comment, has := cleanComment(line[closeIdx+1:])
if has {
p.comment.Write(comment)
}
section.Comment = strings.TrimSpace(p.comment.String())
// Reset auto-counter and comments
p.comment.Reset()
p.count = 1
// Nested values can't span sections
isLastValueEmpty = false
inUnparseableSection = false
for i := range f.options.UnparseableSections {
if f.options.UnparseableSections[i] == name ||
((f.options.Insensitive || f.options.InsensitiveSections) && strings.EqualFold(f.options.UnparseableSections[i], name)) {
inUnparseableSection = true
continue
}
}
continue
}
if inUnparseableSection {
section.isRawSection = true
section.rawBody += string(line)
continue
}
kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line)
if err != nil {
// Treat as boolean key when desired, and whole line is key name.
if IsErrDelimiterNotFound(err) {
switch {
case f.options.AllowBooleanKeys:
kname, err := p.readValue(line, parserBufferSize)
if err != nil {
return err
}
key, err := section.NewBooleanKey(kname)
if err != nil {
return err
}
key.Comment = strings.TrimSpace(p.comment.String())
p.comment.Reset()
continue
case f.options.SkipUnrecognizableLines:
continue
}
}
return err
}
// Auto increment.
isAutoIncr := false
if kname == "-" {
isAutoIncr = true
kname = "#" + strconv.Itoa(p.count)
p.count++
}
value, err := p.readValue(line[offset:], parserBufferSize)
if err != nil {
return err
}
isLastValueEmpty = len(value) == 0
key, err := section.NewKey(kname, value)
if err != nil {
return err
}
key.isAutoIncrement = isAutoIncr
key.Comment = strings.TrimSpace(p.comment.String())
p.comment.Reset()
lastRegularKey = key
}
return nil
}

View File

@ -1,256 +0,0 @@
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"errors"
"fmt"
"strings"
)
// Section represents a config section.
type Section struct {
f *File
Comment string
name string
keys map[string]*Key
keyList []string
keysHash map[string]string
isRawSection bool
rawBody string
}
func newSection(f *File, name string) *Section {
return &Section{
f: f,
name: name,
keys: make(map[string]*Key),
keyList: make([]string, 0, 10),
keysHash: make(map[string]string),
}
}
// Name returns name of Section.
func (s *Section) Name() string {
return s.name
}
// Body returns rawBody of Section if the section was marked as unparseable.
// It still follows the other rules of the INI format surrounding leading/trailing whitespace.
func (s *Section) Body() string {
return strings.TrimSpace(s.rawBody)
}
// SetBody updates body content only if section is raw.
func (s *Section) SetBody(body string) {
if !s.isRawSection {
return
}
s.rawBody = body
}
// NewKey creates a new key to given section.
func (s *Section) NewKey(name, val string) (*Key, error) {
if len(name) == 0 {
return nil, errors.New("error creating new key: empty key name")
} else if s.f.options.Insensitive || s.f.options.InsensitiveKeys {
name = strings.ToLower(name)
}
if s.f.BlockMode {
s.f.lock.Lock()
defer s.f.lock.Unlock()
}
if inSlice(name, s.keyList) {
if s.f.options.AllowShadows {
if err := s.keys[name].addShadow(val); err != nil {
return nil, err
}
} else {
s.keys[name].value = val
s.keysHash[name] = val
}
return s.keys[name], nil
}
s.keyList = append(s.keyList, name)
s.keys[name] = newKey(s, name, val)
s.keysHash[name] = val
return s.keys[name], nil
}
// NewBooleanKey creates a new boolean type key to given section.
func (s *Section) NewBooleanKey(name string) (*Key, error) {
key, err := s.NewKey(name, "true")
if err != nil {
return nil, err
}
key.isBooleanType = true
return key, nil
}
// GetKey returns key in section by given name.
func (s *Section) GetKey(name string) (*Key, error) {
if s.f.BlockMode {
s.f.lock.RLock()
}
if s.f.options.Insensitive || s.f.options.InsensitiveKeys {
name = strings.ToLower(name)
}
key := s.keys[name]
if s.f.BlockMode {
s.f.lock.RUnlock()
}
if key == nil {
// Check if it is a child-section.
sname := s.name
for {
if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 {
sname = sname[:i]
sec, err := s.f.GetSection(sname)
if err != nil {
continue
}
return sec.GetKey(name)
}
break
}
return nil, fmt.Errorf("error when getting key of section %q: key %q not exists", s.name, name)
}
return key, nil
}
// HasKey returns true if section contains a key with given name.
func (s *Section) HasKey(name string) bool {
key, _ := s.GetKey(name)
return key != nil
}
// Deprecated: Use "HasKey" instead.
func (s *Section) Haskey(name string) bool {
return s.HasKey(name)
}
// HasValue returns true if section contains given raw value.
func (s *Section) HasValue(value string) bool {
if s.f.BlockMode {
s.f.lock.RLock()
defer s.f.lock.RUnlock()
}
for _, k := range s.keys {
if value == k.value {
return true
}
}
return false
}
// Key assumes named Key exists in section and returns a zero-value when not.
func (s *Section) Key(name string) *Key {
key, err := s.GetKey(name)
if err != nil {
// It's OK here because the only possible error is empty key name,
// but if it's empty, this piece of code won't be executed.
key, _ = s.NewKey(name, "")
return key
}
return key
}
// Keys returns list of keys of section.
func (s *Section) Keys() []*Key {
keys := make([]*Key, len(s.keyList))
for i := range s.keyList {
keys[i] = s.Key(s.keyList[i])
}
return keys
}
// ParentKeys returns list of keys of parent section.
func (s *Section) ParentKeys() []*Key {
var parentKeys []*Key
sname := s.name
for {
if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 {
sname = sname[:i]
sec, err := s.f.GetSection(sname)
if err != nil {
continue
}
parentKeys = append(parentKeys, sec.Keys()...)
} else {
break
}
}
return parentKeys
}
// KeyStrings returns list of key names of section.
func (s *Section) KeyStrings() []string {
list := make([]string, len(s.keyList))
copy(list, s.keyList)
return list
}
// KeysHash returns keys hash consisting of names and values.
func (s *Section) KeysHash() map[string]string {
if s.f.BlockMode {
s.f.lock.RLock()
defer s.f.lock.RUnlock()
}
hash := make(map[string]string, len(s.keysHash))
for key, value := range s.keysHash {
hash[key] = value
}
return hash
}
// DeleteKey deletes a key from section.
func (s *Section) DeleteKey(name string) {
if s.f.BlockMode {
s.f.lock.Lock()
defer s.f.lock.Unlock()
}
for i, k := range s.keyList {
if k == name {
s.keyList = append(s.keyList[:i], s.keyList[i+1:]...)
delete(s.keys, name)
delete(s.keysHash, name)
return
}
}
}
// ChildSections returns a list of child sections of current section.
// For example, "[parent.child1]" and "[parent.child12]" are child sections
// of section "[parent]".
func (s *Section) ChildSections() []*Section {
prefix := s.name + s.f.options.ChildSectionDelimiter
children := make([]*Section, 0, 3)
for _, name := range s.f.sectionList {
if strings.HasPrefix(name, prefix) {
children = append(children, s.f.sections[name]...)
}
}
return children
}

View File

@ -1,747 +0,0 @@
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"bytes"
"errors"
"fmt"
"reflect"
"strings"
"time"
"unicode"
)
// NameMapper represents a ini tag name mapper.
type NameMapper func(string) string
// Built-in name getters.
var (
// SnackCase converts to format SNACK_CASE.
SnackCase NameMapper = func(raw string) string {
newstr := make([]rune, 0, len(raw))
for i, chr := range raw {
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
if i > 0 {
newstr = append(newstr, '_')
}
}
newstr = append(newstr, unicode.ToUpper(chr))
}
return string(newstr)
}
// TitleUnderscore converts to format title_underscore.
TitleUnderscore NameMapper = func(raw string) string {
newstr := make([]rune, 0, len(raw))
for i, chr := range raw {
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
if i > 0 {
newstr = append(newstr, '_')
}
chr -= 'A' - 'a'
}
newstr = append(newstr, chr)
}
return string(newstr)
}
)
func (s *Section) parseFieldName(raw, actual string) string {
if len(actual) > 0 {
return actual
}
if s.f.NameMapper != nil {
return s.f.NameMapper(raw)
}
return raw
}
func parseDelim(actual string) string {
if len(actual) > 0 {
return actual
}
return ","
}
var reflectTime = reflect.TypeOf(time.Now()).Kind()
// setSliceWithProperType sets proper values to slice based on its type.
func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
var strs []string
if allowShadow {
strs = key.StringsWithShadows(delim)
} else {
strs = key.Strings(delim)
}
numVals := len(strs)
if numVals == 0 {
return nil
}
var vals interface{}
var err error
sliceOf := field.Type().Elem().Kind()
switch sliceOf {
case reflect.String:
vals = strs
case reflect.Int:
vals, err = key.parseInts(strs, true, false)
case reflect.Int64:
vals, err = key.parseInt64s(strs, true, false)
case reflect.Uint:
vals, err = key.parseUints(strs, true, false)
case reflect.Uint64:
vals, err = key.parseUint64s(strs, true, false)
case reflect.Float64:
vals, err = key.parseFloat64s(strs, true, false)
case reflect.Bool:
vals, err = key.parseBools(strs, true, false)
case reflectTime:
vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false)
default:
return fmt.Errorf("unsupported type '[]%s'", sliceOf)
}
if err != nil && isStrict {
return err
}
slice := reflect.MakeSlice(field.Type(), numVals, numVals)
for i := 0; i < numVals; i++ {
switch sliceOf {
case reflect.String:
slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i]))
case reflect.Int:
slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i]))
case reflect.Int64:
slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i]))
case reflect.Uint:
slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i]))
case reflect.Uint64:
slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i]))
case reflect.Float64:
slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i]))
case reflect.Bool:
slice.Index(i).Set(reflect.ValueOf(vals.([]bool)[i]))
case reflectTime:
slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i]))
}
}
field.Set(slice)
return nil
}
func wrapStrictError(err error, isStrict bool) error {
if isStrict {
return err
}
return nil
}
// setWithProperType sets proper value to field based on its type,
// but it does not return error for failing parsing,
// because we want to use default value that is already assigned to struct.
func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
vt := t
isPtr := t.Kind() == reflect.Ptr
if isPtr {
vt = t.Elem()
}
switch vt.Kind() {
case reflect.String:
stringVal := key.String()
if isPtr {
field.Set(reflect.ValueOf(&stringVal))
} else if len(stringVal) > 0 {
field.SetString(key.String())
}
case reflect.Bool:
boolVal, err := key.Bool()
if err != nil {
return wrapStrictError(err, isStrict)
}
if isPtr {
field.Set(reflect.ValueOf(&boolVal))
} else {
field.SetBool(boolVal)
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
// ParseDuration will not return err for `0`, so check the type name
if vt.Name() == "Duration" {
durationVal, err := key.Duration()
if err != nil {
if intVal, err := key.Int64(); err == nil {
field.SetInt(intVal)
return nil
}
return wrapStrictError(err, isStrict)
}
if isPtr {
field.Set(reflect.ValueOf(&durationVal))
} else if int64(durationVal) > 0 {
field.Set(reflect.ValueOf(durationVal))
}
return nil
}
intVal, err := key.Int64()
if err != nil {
return wrapStrictError(err, isStrict)
}
if isPtr {
pv := reflect.New(t.Elem())
pv.Elem().SetInt(intVal)
field.Set(pv)
} else {
field.SetInt(intVal)
}
// byte is an alias for uint8, so supporting uint8 breaks support for byte
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
durationVal, err := key.Duration()
// Skip zero value
if err == nil && uint64(durationVal) > 0 {
if isPtr {
field.Set(reflect.ValueOf(&durationVal))
} else {
field.Set(reflect.ValueOf(durationVal))
}
return nil
}
uintVal, err := key.Uint64()
if err != nil {
return wrapStrictError(err, isStrict)
}
if isPtr {
pv := reflect.New(t.Elem())
pv.Elem().SetUint(uintVal)
field.Set(pv)
} else {
field.SetUint(uintVal)
}
case reflect.Float32, reflect.Float64:
floatVal, err := key.Float64()
if err != nil {
return wrapStrictError(err, isStrict)
}
if isPtr {
pv := reflect.New(t.Elem())
pv.Elem().SetFloat(floatVal)
field.Set(pv)
} else {
field.SetFloat(floatVal)
}
case reflectTime:
timeVal, err := key.Time()
if err != nil {
return wrapStrictError(err, isStrict)
}
if isPtr {
field.Set(reflect.ValueOf(&timeVal))
} else {
field.Set(reflect.ValueOf(timeVal))
}
case reflect.Slice:
return setSliceWithProperType(key, field, delim, allowShadow, isStrict)
default:
return fmt.Errorf("unsupported type %q", t)
}
return nil
}
func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool, allowNonUnique bool, extends bool) {
opts := strings.SplitN(tag, ",", 5)
rawName = opts[0]
for _, opt := range opts[1:] {
omitEmpty = omitEmpty || (opt == "omitempty")
allowShadow = allowShadow || (opt == "allowshadow")
allowNonUnique = allowNonUnique || (opt == "nonunique")
extends = extends || (opt == "extends")
}
return rawName, omitEmpty, allowShadow, allowNonUnique, extends
}
// mapToField maps the given value to the matching field of the given section.
// The sectionIndex is the index (if non unique sections are enabled) to which the value should be added.
func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, sectionName string) error {
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
typ := val.Type()
for i := 0; i < typ.NumField(); i++ {
field := val.Field(i)
tpField := typ.Field(i)
tag := tpField.Tag.Get("ini")
if tag == "-" {
continue
}
rawName, _, allowShadow, allowNonUnique, extends := parseTagOptions(tag)
fieldName := s.parseFieldName(tpField.Name, rawName)
if len(fieldName) == 0 || !field.CanSet() {
continue
}
isStruct := tpField.Type.Kind() == reflect.Struct
isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct
isAnonymousPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
if isAnonymousPtr {
field.Set(reflect.New(tpField.Type.Elem()))
}
if extends && (isAnonymousPtr || (isStruct && tpField.Anonymous)) {
if isStructPtr && field.IsNil() {
field.Set(reflect.New(tpField.Type.Elem()))
}
fieldSection := s
if rawName != "" {
sectionName = s.name + s.f.options.ChildSectionDelimiter + rawName
if secs, err := s.f.SectionsByName(sectionName); err == nil && sectionIndex < len(secs) {
fieldSection = secs[sectionIndex]
}
}
if err := fieldSection.mapToField(field, isStrict, sectionIndex, sectionName); err != nil {
return fmt.Errorf("map to field %q: %v", fieldName, err)
}
} else if isAnonymousPtr || isStruct || isStructPtr {
if secs, err := s.f.SectionsByName(fieldName); err == nil {
if len(secs) <= sectionIndex {
return fmt.Errorf("there are not enough sections (%d <= %d) for the field %q", len(secs), sectionIndex, fieldName)
}
// Only set the field to non-nil struct value if we have a section for it.
// Otherwise, we end up with a non-nil struct ptr even though there is no data.
if isStructPtr && field.IsNil() {
field.Set(reflect.New(tpField.Type.Elem()))
}
if err = secs[sectionIndex].mapToField(field, isStrict, sectionIndex, fieldName); err != nil {
return fmt.Errorf("map to field %q: %v", fieldName, err)
}
continue
}
}
// Map non-unique sections
if allowNonUnique && tpField.Type.Kind() == reflect.Slice {
newField, err := s.mapToSlice(fieldName, field, isStrict)
if err != nil {
return fmt.Errorf("map to slice %q: %v", fieldName, err)
}
field.Set(newField)
continue
}
if key, err := s.GetKey(fieldName); err == nil {
delim := parseDelim(tpField.Tag.Get("delim"))
if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil {
return fmt.Errorf("set field %q: %v", fieldName, err)
}
}
}
return nil
}
// mapToSlice maps all sections with the same name and returns the new value.
// The type of the Value must be a slice.
func (s *Section) mapToSlice(secName string, val reflect.Value, isStrict bool) (reflect.Value, error) {
secs, err := s.f.SectionsByName(secName)
if err != nil {
return reflect.Value{}, err
}
typ := val.Type().Elem()
for i, sec := range secs {
elem := reflect.New(typ)
if err = sec.mapToField(elem, isStrict, i, sec.name); err != nil {
return reflect.Value{}, fmt.Errorf("map to field from section %q: %v", secName, err)
}
val = reflect.Append(val, elem.Elem())
}
return val, nil
}
// mapTo maps a section to object v.
func (s *Section) mapTo(v interface{}, isStrict bool) error {
typ := reflect.TypeOf(v)
val := reflect.ValueOf(v)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
} else {
return errors.New("not a pointer to a struct")
}
if typ.Kind() == reflect.Slice {
newField, err := s.mapToSlice(s.name, val, isStrict)
if err != nil {
return err
}
val.Set(newField)
return nil
}
return s.mapToField(val, isStrict, 0, s.name)
}
// MapTo maps section to given struct.
func (s *Section) MapTo(v interface{}) error {
return s.mapTo(v, false)
}
// StrictMapTo maps section to given struct in strict mode,
// which returns all possible error including value parsing error.
func (s *Section) StrictMapTo(v interface{}) error {
return s.mapTo(v, true)
}
// MapTo maps file to given struct.
func (f *File) MapTo(v interface{}) error {
return f.Section("").MapTo(v)
}
// StrictMapTo maps file to given struct in strict mode,
// which returns all possible error including value parsing error.
func (f *File) StrictMapTo(v interface{}) error {
return f.Section("").StrictMapTo(v)
}
// MapToWithMapper maps data sources to given struct with name mapper.
func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
cfg, err := Load(source, others...)
if err != nil {
return err
}
cfg.NameMapper = mapper
return cfg.MapTo(v)
}
// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode,
// which returns all possible error including value parsing error.
func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
cfg, err := Load(source, others...)
if err != nil {
return err
}
cfg.NameMapper = mapper
return cfg.StrictMapTo(v)
}
// MapTo maps data sources to given struct.
func MapTo(v, source interface{}, others ...interface{}) error {
return MapToWithMapper(v, nil, source, others...)
}
// StrictMapTo maps data sources to given struct in strict mode,
// which returns all possible error including value parsing error.
func StrictMapTo(v, source interface{}, others ...interface{}) error {
return StrictMapToWithMapper(v, nil, source, others...)
}
// reflectSliceWithProperType does the opposite thing as setSliceWithProperType.
func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow bool) error {
slice := field.Slice(0, field.Len())
if field.Len() == 0 {
return nil
}
sliceOf := field.Type().Elem().Kind()
if allowShadow {
var keyWithShadows *Key
for i := 0; i < field.Len(); i++ {
var val string
switch sliceOf {
case reflect.String:
val = slice.Index(i).String()
case reflect.Int, reflect.Int64:
val = fmt.Sprint(slice.Index(i).Int())
case reflect.Uint, reflect.Uint64:
val = fmt.Sprint(slice.Index(i).Uint())
case reflect.Float64:
val = fmt.Sprint(slice.Index(i).Float())
case reflect.Bool:
val = fmt.Sprint(slice.Index(i).Bool())
case reflectTime:
val = slice.Index(i).Interface().(time.Time).Format(time.RFC3339)
default:
return fmt.Errorf("unsupported type '[]%s'", sliceOf)
}
if i == 0 {
keyWithShadows = newKey(key.s, key.name, val)
} else {
_ = keyWithShadows.AddShadow(val)
}
}
*key = *keyWithShadows
return nil
}
var buf bytes.Buffer
for i := 0; i < field.Len(); i++ {
switch sliceOf {
case reflect.String:
buf.WriteString(slice.Index(i).String())
case reflect.Int, reflect.Int64:
buf.WriteString(fmt.Sprint(slice.Index(i).Int()))
case reflect.Uint, reflect.Uint64:
buf.WriteString(fmt.Sprint(slice.Index(i).Uint()))
case reflect.Float64:
buf.WriteString(fmt.Sprint(slice.Index(i).Float()))
case reflect.Bool:
buf.WriteString(fmt.Sprint(slice.Index(i).Bool()))
case reflectTime:
buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339))
default:
return fmt.Errorf("unsupported type '[]%s'", sliceOf)
}
buf.WriteString(delim)
}
key.SetValue(buf.String()[:buf.Len()-len(delim)])
return nil
}
// reflectWithProperType does the opposite thing as setWithProperType.
func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow bool) error {
switch t.Kind() {
case reflect.String:
key.SetValue(field.String())
case reflect.Bool:
key.SetValue(fmt.Sprint(field.Bool()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
key.SetValue(fmt.Sprint(field.Int()))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
key.SetValue(fmt.Sprint(field.Uint()))
case reflect.Float32, reflect.Float64:
key.SetValue(fmt.Sprint(field.Float()))
case reflectTime:
key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339)))
case reflect.Slice:
return reflectSliceWithProperType(key, field, delim, allowShadow)
case reflect.Ptr:
if !field.IsNil() {
return reflectWithProperType(t.Elem(), key, field.Elem(), delim, allowShadow)
}
default:
return fmt.Errorf("unsupported type %q", t)
}
return nil
}
// CR: copied from encoding/json/encode.go with modifications of time.Time support.
// TODO: add more test coverage.
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
case reflectTime:
t, ok := v.Interface().(time.Time)
return ok && t.IsZero()
}
return false
}
// StructReflector is the interface implemented by struct types that can extract themselves into INI objects.
type StructReflector interface {
ReflectINIStruct(*File) error
}
func (s *Section) reflectFrom(val reflect.Value) error {
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
typ := val.Type()
for i := 0; i < typ.NumField(); i++ {
if !val.Field(i).CanInterface() {
continue
}
field := val.Field(i)
tpField := typ.Field(i)
tag := tpField.Tag.Get("ini")
if tag == "-" {
continue
}
rawName, omitEmpty, allowShadow, allowNonUnique, extends := parseTagOptions(tag)
if omitEmpty && isEmptyValue(field) {
continue
}
if r, ok := field.Interface().(StructReflector); ok {
return r.ReflectINIStruct(s.f)
}
fieldName := s.parseFieldName(tpField.Name, rawName)
if len(fieldName) == 0 || !field.CanSet() {
continue
}
if extends && tpField.Anonymous && (tpField.Type.Kind() == reflect.Ptr || tpField.Type.Kind() == reflect.Struct) {
if err := s.reflectFrom(field); err != nil {
return fmt.Errorf("reflect from field %q: %v", fieldName, err)
}
continue
}
if (tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct) ||
(tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") {
// Note: The only error here is section doesn't exist.
sec, err := s.f.GetSection(fieldName)
if err != nil {
// Note: fieldName can never be empty here, ignore error.
sec, _ = s.f.NewSection(fieldName)
}
// Add comment from comment tag
if len(sec.Comment) == 0 {
sec.Comment = tpField.Tag.Get("comment")
}
if err = sec.reflectFrom(field); err != nil {
return fmt.Errorf("reflect from field %q: %v", fieldName, err)
}
continue
}
if allowNonUnique && tpField.Type.Kind() == reflect.Slice {
slice := field.Slice(0, field.Len())
if field.Len() == 0 {
return nil
}
sliceOf := field.Type().Elem().Kind()
for i := 0; i < field.Len(); i++ {
if sliceOf != reflect.Struct && sliceOf != reflect.Ptr {
return fmt.Errorf("field %q is not a slice of pointer or struct", fieldName)
}
sec, err := s.f.NewSection(fieldName)
if err != nil {
return err
}
// Add comment from comment tag
if len(sec.Comment) == 0 {
sec.Comment = tpField.Tag.Get("comment")
}
if err := sec.reflectFrom(slice.Index(i)); err != nil {
return fmt.Errorf("reflect from field %q: %v", fieldName, err)
}
}
continue
}
// Note: Same reason as section.
key, err := s.GetKey(fieldName)
if err != nil {
key, _ = s.NewKey(fieldName, "")
}
// Add comment from comment tag
if len(key.Comment) == 0 {
key.Comment = tpField.Tag.Get("comment")
}
delim := parseDelim(tpField.Tag.Get("delim"))
if err = reflectWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil {
return fmt.Errorf("reflect field %q: %v", fieldName, err)
}
}
return nil
}
// ReflectFrom reflects section from given struct. It overwrites existing ones.
func (s *Section) ReflectFrom(v interface{}) error {
typ := reflect.TypeOf(v)
val := reflect.ValueOf(v)
if s.name != DefaultSection && s.f.options.AllowNonUniqueSections &&
(typ.Kind() == reflect.Slice || typ.Kind() == reflect.Ptr) {
// Clear sections to make sure none exists before adding the new ones
s.f.DeleteSection(s.name)
if typ.Kind() == reflect.Ptr {
sec, err := s.f.NewSection(s.name)
if err != nil {
return err
}
return sec.reflectFrom(val.Elem())
}
slice := val.Slice(0, val.Len())
sliceOf := val.Type().Elem().Kind()
if sliceOf != reflect.Ptr {
return fmt.Errorf("not a slice of pointers")
}
for i := 0; i < slice.Len(); i++ {
sec, err := s.f.NewSection(s.name)
if err != nil {
return err
}
err = sec.reflectFrom(slice.Index(i))
if err != nil {
return fmt.Errorf("reflect from %dth field: %v", i, err)
}
}
return nil
}
if typ.Kind() == reflect.Ptr {
val = val.Elem()
} else {
return errors.New("not a pointer to a struct")
}
return s.reflectFrom(val)
}
// ReflectFrom reflects file from given struct.
func (f *File) ReflectFrom(v interface{}) error {
return f.Section("").ReflectFrom(v)
}
// ReflectFromWithMapper reflects data sources from given struct with name mapper.
func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error {
cfg.NameMapper = mapper
return cfg.ReflectFrom(v)
}
// ReflectFrom reflects data sources from given struct.
func ReflectFrom(cfg *File, v interface{}) error {
return ReflectFromWithMapper(cfg, v, nil)
}

View File

@ -1,27 +0,0 @@
# This file just for local test
[opengauss]
host=10.244.19.211
port=5432
user=gauss
password=Gauss_234
dbname=gauss
database=
sslmode=require
sslkey=
sslcert=
sslrootcert=
target_session_attrs=
target_server_type=
min_read_buffer_size=
passfile=
connect_timeout=
krbsrvname=
krbspn=
servicefile=
service=
disable_prepared_binary_result=
loggerLevel=trace

View File

@ -1,61 +0,0 @@
# Golang CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-go/ for more details
version: 2
jobs:
build:
docker:
# specify the version
- image: circleci/golang:1.10
- image: circleci/mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: xorm_test
MYSQL_HOST: 127.0.0.1
MYSQL_ROOT_HOST: '%'
MYSQL_USER: root
# CircleCI PostgreSQL images available at: https://hub.docker.com/r/circleci/postgres/
- image: circleci/postgres:9.6.2-alpine
environment:
POSTGRES_USER: circleci
POSTGRES_DB: xorm_test
- image: microsoft/mssql-server-linux:latest
environment:
ACCEPT_EULA: Y
SA_PASSWORD: yourStrong(!)Password
MSSQL_PID: Developer
- image: pingcap/tidb:v2.1.2
working_directory: /go/src/github.com/go-xorm/xorm
steps:
- checkout
- run: go get -t -d -v ./...
- run: go get -u xorm.io/core
- run: go get -u xorm.io/builder
- run: GO111MODULE=off go build -v
- run: GO111MODULE=on go build -v
- run: go get -u github.com/wadey/gocovmerge
- run: go test -v -race -db="sqlite3" -conn_str="./test.db" -coverprofile=coverage1-1.txt -covermode=atomic
- run: go test -v -race -db="sqlite3" -conn_str="./test.db" -cache=true -coverprofile=coverage1-2.txt -covermode=atomic
- run: go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -coverprofile=coverage2-1.txt -covermode=atomic
- run: go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic
- run: go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -coverprofile=coverage3-1.txt -covermode=atomic
- run: go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic
- run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -coverprofile=coverage4-1.txt -covermode=atomic
- run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic
- run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic
- run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic
- run: go test -v -race -db="mssql" -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" -coverprofile=coverage6-1.txt -covermode=atomic
- run: go test -v -race -db="mssql" -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic
- run: go test -v -race -db="mysql" -conn_str="root:@tcp(localhost:4000)/xorm_test" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic
- run: go test -v -race -db="mysql" -conn_str="root:@tcp(localhost:4000)/xorm_test" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic
- run: 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 coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt
- run: bash <(curl -s https://codecov.io/bash)

View File

@ -1,823 +0,0 @@
---
kind: pipeline
name: matrix-1
platform:
os: linux
arch: amd64
clone:
disable: true
workspace:
base: /go
path: src/github.com/go-xorm/xorm
steps:
- name: git
pull: default
image: plugins/git:next
settings:
depth: 50
tags: true
- name: init_postgres
pull: default
image: postgres:9.5
commands:
- "until psql -U postgres -d xorm_test -h pgsql \\\n -c \"SELECT 1;\" >/dev/null 2>&1; do sleep 1; done\n"
- "psql -U postgres -d xorm_test -h pgsql \\\n -c \"create schema xorm;\"\n"
- name: build
pull: default
image: golang:1.10
commands:
- go get -t -d -v ./...
- go get -u xorm.io/core
- go get -u xorm.io/builder
- go build -v
when:
event:
- push
- pull_request
- name: test-sqlite
pull: default
image: golang:1.10
commands:
- 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"
when:
event:
- push
- pull_request
- name: test-mysql
pull: default
image: golang:1.10
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mysql-utf8mb4
pull: default
image: golang:1.10
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mymysql
pull: default
image: golang:1.10
commands:
- "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic"
- "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-postgres
pull: default
image: golang:1.10
commands:
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic"
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-postgres-schema
pull: default
image: golang:1.10
commands:
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic"
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mssql
pull: default
image: golang:1.10
commands:
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic"
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-tidb
pull: default
image: golang:1.10
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic"
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt
when:
event:
- push
- pull_request
services:
- name: mysql
pull: default
image: mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: xorm_test
when:
event:
- push
- tag
- pull_request
- name: tidb
pull: default
image: pingcap/tidb:v3.0.3
when:
event:
- push
- tag
- pull_request
- name: pgsql
pull: default
image: postgres:9.5
environment:
POSTGRES_DB: xorm_test
POSTGRES_USER: postgres
when:
event:
- push
- tag
- pull_request
- name: mssql
pull: default
image: microsoft/mssql-server-linux:latest
environment:
ACCEPT_EULA: Y
SA_PASSWORD: yourStrong(!)Password
MSSQL_PID: Developer
when:
event:
- push
- tag
- pull_request
---
kind: pipeline
name: matrix-2
platform:
os: linux
arch: amd64
clone:
disable: true
workspace:
base: /go
path: src/github.com/go-xorm/xorm
steps:
- name: git
pull: default
image: plugins/git:next
settings:
depth: 50
tags: true
- name: init_postgres
pull: default
image: postgres:9.5
commands:
- "until psql -U postgres -d xorm_test -h pgsql \\\n -c \"SELECT 1;\" >/dev/null 2>&1; do sleep 1; done\n"
- "psql -U postgres -d xorm_test -h pgsql \\\n -c \"create schema xorm;\"\n"
- name: build
pull: default
image: golang:1.11
environment:
GO111MODULE: "off"
commands:
- go get -t -d -v ./...
- go get -u xorm.io/core
- go get -u xorm.io/builder
- go build -v
when:
event:
- push
- pull_request
- name: build-gomod
pull: default
image: golang:1.11
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- go build -v
when:
event:
- push
- pull_request
- name: test-sqlite
pull: default
image: golang:1.11
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "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"
when:
event:
- push
- pull_request
- name: test-mysql
pull: default
image: golang:1.11
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mysql-utf8mb4
pull: default
image: golang:1.11
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mymysql
pull: default
image: golang:1.11
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic"
- "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-postgres
pull: default
image: golang:1.11
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic"
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-postgres-schema
pull: default
image: golang:1.11
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic"
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mssql
pull: default
image: golang:1.11
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic"
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-tidb
pull: default
image: golang:1.11
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic"
- go get github.com/wadey/gocovmerge
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt
when:
event:
- push
- pull_request
services:
- name: mysql
pull: default
image: mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: xorm_test
when:
event:
- push
- tag
- pull_request
- name: tidb
pull: default
image: pingcap/tidb:v3.0.3
when:
event:
- push
- tag
- pull_request
- name: pgsql
pull: default
image: postgres:9.5
environment:
POSTGRES_DB: xorm_test
POSTGRES_USER: postgres
when:
event:
- push
- tag
- pull_request
- name: mssql
pull: default
image: microsoft/mssql-server-linux:latest
environment:
ACCEPT_EULA: Y
SA_PASSWORD: yourStrong(!)Password
MSSQL_PID: Developer
when:
event:
- push
- tag
- pull_request
---
kind: pipeline
name: matrix-3
platform:
os: linux
arch: amd64
clone:
disable: true
workspace:
base: /go
path: src/github.com/go-xorm/xorm
steps:
- name: git
pull: default
image: plugins/git:next
settings:
depth: 50
tags: true
- name: build
pull: default
image: golang:1.12
environment:
GO111MODULE: "off"
commands:
- go get -t -d -v ./...
- go get -u xorm.io/core
- go get -u xorm.io/builder
- go build -v
when:
event:
- push
- pull_request
- name: build-gomod
pull: default
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- go build -v
when:
event:
- push
- pull_request
- name: test-sqlite
pull: default
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "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"
when:
event:
- push
- pull_request
- name: test-mysql
pull: default
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mysql-utf8mb4
pull: default
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mymysql
pull: default
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic"
- "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-postgres
pull: default
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic"
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-postgres-schema
pull: default
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic"
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mssql
pull: default
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic"
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-tidb
pull: default
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic"
- go get github.com/wadey/gocovmerge
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt
when:
event:
- push
- pull_request
services:
- name: mysql
pull: default
image: mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: xorm_test
when:
event:
- push
- tag
- pull_request
- name: tidb
pull: default
image: pingcap/tidb:v3.0.3
when:
event:
- push
- tag
- pull_request
- name: pgsql
pull: default
image: postgres:9.5
environment:
POSTGRES_DB: xorm_test
POSTGRES_USER: postgres
when:
event:
- push
- tag
- pull_request
- name: mssql
pull: default
image: microsoft/mssql-server-linux:latest
environment:
ACCEPT_EULA: Y
SA_PASSWORD: yourStrong(!)Password
MSSQL_PID: Developer
when:
event:
- push
- tag
- pull_request
---
kind: pipeline
name: go1.13
platform:
os: linux
arch: amd64
clone:
disable: true
workspace:
base: /go
path: src/github.com/go-xorm/xorm
steps:
- name: git
pull: default
image: plugins/git:next
settings:
depth: 50
tags: true
- name: build
pull: default
image: golang:1.13
environment:
GO111MODULE: "off"
commands:
- go get -t -d -v ./...
- go get -u xorm.io/core
- go get -u xorm.io/builder
- go build -v
when:
event:
- push
- pull_request
- name: build-gomod
pull: default
image: golang:1.13
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- go build -v
when:
event:
- push
- pull_request
- name: test-sqlite
pull: default
image: golang:1.13
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "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"
when:
event:
- push
- pull_request
- name: test-mysql
pull: default
image: golang:1.13
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mysql-utf8mb4
pull: default
image: golang:1.13
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mymysql
pull: default
image: golang:1.13
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic"
- "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-postgres
pull: default
image: golang:1.13
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic"
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-postgres-schema
pull: default
image: golang:1.13
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic"
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mssql
pull: default
image: golang:1.13
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic"
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-tidb
pull: default
image: golang:1.13
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic"
- go get github.com/wadey/gocovmerge
- gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt
when:
event:
- push
- pull_request
services:
- name: mysql
pull: default
image: mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: xorm_test
when:
event:
- push
- tag
- pull_request
- name: tidb
pull: default
image: pingcap/tidb:v3.0.3
when:
event:
- push
- tag
- pull_request
- name: pgsql
pull: default
image: postgres:9.5
environment:
POSTGRES_DB: xorm_test
POSTGRES_USER: postgres
when:
event:
- push
- tag
- pull_request
- name: mssql
pull: default
image: microsoft/mssql-server-linux:latest
environment:
ACCEPT_EULA: Y
SA_PASSWORD: yourStrong(!)Password
MSSQL_PID: Developer
when:
event:
- push
- tag
- pull_request

View File

@ -1,33 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
*.db
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.log
.vendor
temp_test.go
.vscode
xorm.test
*.sqlite3
test.db.sql
.idea/

View File

@ -1,46 +0,0 @@
## Contributing to xorm
`xorm` has a backlog of [pull requests](https://help.github.com/articles/using-pull-requests), but contributions are still very
much welcome. You can help with patch review, submitting bug reports,
or adding new functionality. There is no formal style guide, but
please conform to the style of existing code and general Go formatting
conventions when submitting patches.
* [fork a repo](https://help.github.com/articles/fork-a-repo)
* [creating a pull request ](https://help.github.com/articles/creating-a-pull-request)
### Language
Since `xorm` is a world-wide open source project, please describe your issues or code changes in English as soon as possible.
### Sign your codes with comments
```
// !<you github id>! your comments
e.g.,
// !lunny! this is comments made by lunny
```
### Patch review
Help review existing open [pull requests](https://help.github.com/articles/using-pull-requests) by commenting on the code or
proposed functionality.
### Bug reports
We appreciate any bug reports, but especially ones with self-contained
(doesn't depend on code outside of xorm), minimal (can't be simplified
further) test cases. It's especially helpful if you can submit a pull
request with just the failing test case(you can find some example test file like [session_get_test.go](https://github.com/go-xorm/xorm/blob/master/session_get_test.go)).
If you implements a new database interface, you maybe need to add a test_<databasename>.sh file.
For example, [mysql_test.go](https://github.com/go-xorm/xorm/blob/master/test_mysql.sh)
### New functionality
There are a number of pending patches for new functionality, so
additional feature patches will take a while to merge. Still, patches
are generally reviewed based on usefulness and complexity in addition
to time-in-queue, so if you have a knockout idea, take a shot. Feel
free to open an issue discussion your proposed patch beforehand.

View File

@ -1,27 +0,0 @@
Copyright (c) 2013 - 2015 The Xorm Authors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,503 +0,0 @@
# xorm HAS BEEN MOVED TO https://gitea.com/xorm/xorm . THIS REPOSITORY WILL NOT BE UPDATED ANY MORE.
[中文](https://github.com/go-xorm/xorm/blob/master/README_CN.md)
Xorm is a simple and powerful ORM for Go.
[![CircleCI](https://circleci.com/gh/go-xorm/xorm.svg?style=shield)](https://circleci.com/gh/go-xorm/xorm) [![codecov](https://codecov.io/gh/go-xorm/xorm/branch/master/graph/badge.svg)](https://codecov.io/gh/go-xorm/xorm)
[![](https://goreportcard.com/badge/github.com/go-xorm/xorm)](https://goreportcard.com/report/github.com/go-xorm/xorm)
[![Join the chat at https://img.shields.io/discord/323460943201959939.svg](https://img.shields.io/discord/323460943201959939.svg)](https://discord.gg/HuR2CF3)
## Features
* Struct <-> Table Mapping Support
* Chainable APIs
* Transaction Support
* Both ORM and raw SQL operation Support
* Sync database schema Support
* Query Cache speed up
* Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/cmd/blob/master/README.md)
* Simple cascade loading support
* Optimistic Locking support
* SQL Builder support via [xorm.io/builder](https://xorm.io/builder)
* Automatical Read/Write seperatelly
* Postgres schema support
* Context Cache support
## Drivers Support
Drivers for Go's sql package which currently support database/sql includes:
* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/tree/master/godrv)
* Postgres: [github.com/lib/pq](https://github.com/lib/pq)
* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb)
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
* MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment)
## Installation
go get github.com/go-xorm/xorm
## Documents
* [Manual](http://xorm.io/docs)
* [GoDoc](http://godoc.org/github.com/go-xorm/xorm)
## Quick Start
* Create Engine
```Go
engine, err := xorm.NewEngine(driverName, dataSourceName)
```
* Define a struct and Sync2 table struct to database
```Go
type User struct {
Id int64
Name string
Salt string
Age int
Passwd string `xorm:"varchar(200)"`
Created time.Time `xorm:"created"`
Updated time.Time `xorm:"updated"`
}
err := engine.Sync2(new(User))
```
* Create Engine Group
```Go
dataSourceNameSlice := []string{masterDataSourceName, slave1DataSourceName, slave2DataSourceName}
engineGroup, err := xorm.NewEngineGroup(driverName, dataSourceNameSlice)
```
```Go
masterEngine, err := xorm.NewEngine(driverName, masterDataSourceName)
slave1Engine, err := xorm.NewEngine(driverName, slave1DataSourceName)
slave2Engine, err := xorm.NewEngine(driverName, slave2DataSourceName)
engineGroup, err := xorm.NewEngineGroup(masterEngine, []*Engine{slave1Engine, slave2Engine})
```
Then all place where `engine` you can just use `engineGroup`.
* `Query` runs a SQL string, the returned results is `[]map[string][]byte`, `QueryString` returns `[]map[string]string`, `QueryInterface` returns `[]map[string]interface{}`.
```Go
results, err := engine.Query("select * from user")
results, err := engine.Where("a = 1").Query()
results, err := engine.QueryString("select * from user")
results, err := engine.Where("a = 1").QueryString()
results, err := engine.QueryInterface("select * from user")
results, err := engine.Where("a = 1").QueryInterface()
```
* `Exec` runs a SQL string, it returns `affected` and `error`
```Go
affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
```
* `Insert` one or multiple records to database
```Go
affected, err := engine.Insert(&user)
// INSERT INTO struct () values ()
affected, err := engine.Insert(&user1, &user2)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values ()
affected, err := engine.Insert(&users)
// INSERT INTO struct () values (),(),()
affected, err := engine.Insert(&user1, &users)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values (),(),()
```
* `Get` query one record from database
```Go
has, err := engine.Get(&user)
// SELECT * FROM user LIMIT 1
has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
var name string
has, err := engine.Table(&user).Where("id = ?", id).Cols("name").Get(&name)
// SELECT name FROM user WHERE id = ?
var id int64
has, err := engine.Table(&user).Where("name = ?", name).Cols("id").Get(&id)
has, err := engine.SQL("select id from user").Get(&id)
// SELECT id FROM user WHERE name = ?
var valuesMap = make(map[string]string)
has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap)
// SELECT * FROM user WHERE id = ?
var valuesSlice = make([]interface{}, len(cols))
has, err := engine.Table(&user).Where("id = ?", id).Cols(cols...).Get(&valuesSlice)
// SELECT col1, col2, col3 FROM user WHERE id = ?
```
* `Exist` check if one record exist on table
```Go
has, err := testEngine.Exist(new(RecordExist))
// SELECT * FROM record_exist LIMIT 1
has, err = testEngine.Exist(&RecordExist{
Name: "test1",
})
// SELECT * FROM record_exist WHERE name = ? LIMIT 1
has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{})
// SELECT * FROM record_exist WHERE name = ? LIMIT 1
has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist()
// select * from record_exist where name = ?
has, err = testEngine.Table("record_exist").Exist()
// SELECT * FROM record_exist LIMIT 1
has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist()
// SELECT * FROM record_exist WHERE name = ? LIMIT 1
```
* `Find` query multiple records from database, also you can use join and extends
```Go
var users []User
err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
// SELECT * FROM user WHERE name = ? AND age > 10 limit 10 offset 0
type Detail struct {
Id int64
UserId int64 `xorm:"index"`
}
type UserDetail struct {
User `xorm:"extends"`
Detail `xorm:"extends"`
}
var users []UserDetail
err := engine.Table("user").Select("user.*, detail.*").
Join("INNER", "detail", "detail.user_id = user.id").
Where("user.name = ?", name).Limit(10, 0).
Find(&users)
// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 10 offset 0
```
* `Iterate` and `Rows` query multiple records and record by record handle, there are two methods Iterate and Rows
```Go
err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
user := bean.(*User)
return nil
})
// SELECT * FROM user
err := engine.BufferSize(100).Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
user := bean.(*User)
return nil
})
// SELECT * FROM user Limit 0, 100
// SELECT * FROM user Limit 101, 100
rows, err := engine.Rows(&User{Name:name})
// SELECT * FROM user
defer rows.Close()
bean := new(Struct)
for rows.Next() {
err = rows.Scan(bean)
}
```
* `Update` update one or more records, default will update non-empty and non-zero fields except when you use Cols, AllCols and so on.
```Go
affected, err := engine.ID(1).Update(&user)
// UPDATE user SET ... Where id = ?
affected, err := engine.Update(&user, &User{Name:name})
// UPDATE user SET ... Where name = ?
var ids = []int64{1, 2, 3}
affected, err := engine.In("id", ids).Update(&user)
// UPDATE user SET ... Where id IN (?, ?, ?)
// force update indicated columns by Cols
affected, err := engine.ID(1).Cols("age").Update(&User{Name:name, Age: 12})
// UPDATE user SET age = ?, updated=? Where id = ?
// force NOT update indicated columns by Omit
affected, err := engine.ID(1).Omit("name").Update(&User{Name:name, Age: 12})
// UPDATE user SET age = ?, updated=? Where id = ?
affected, err := engine.ID(1).AllCols().Update(&user)
// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
```
* `Delete` delete one or more records, Delete MUST have condition
```Go
affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ...
affected, err := engine.ID(2).Delete(&user)
// DELETE FROM user Where id = ?
```
* `Count` count records
```Go
counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user
```
* `FindAndCount` combines function `Find` with `Count` which is usually used in query by page
```Go
var users []User
counts, err := engine.FindAndCount(&users)
```
* `Sum` sum functions
```Go
agesFloat64, err := engine.Sum(&user, "age")
// SELECT sum(age) AS total FROM user
agesInt64, err := engine.SumInt(&user, "age")
// SELECT sum(age) AS total FROM user
sumFloat64Slice, err := engine.Sums(&user, "age", "score")
// SELECT sum(age), sum(score) FROM user
sumInt64Slice, err := engine.SumsInt(&user, "age", "score")
// SELECT sum(age), sum(score) FROM user
```
* Query conditions builder
```Go
err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e"))).Find(&users)
// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?)
```
* Multiple operations in one go routine, no transation here but resue session memory
```Go
session := engine.NewSession()
defer session.Close()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
if _, err := session.Insert(&user1); err != nil {
return err
}
user2 := Userinfo{Username: "yyy"}
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
return err
}
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return err
}
return nil
```
* Transation should on one go routine. There is transaction and resue session memory
```Go
session := engine.NewSession()
defer session.Close()
// add Begin() before any action
if err := session.Begin(); err != nil {
// if returned then will rollback automatically
return err
}
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
if _, err := session.Insert(&user1); err != nil {
return err
}
user2 := Userinfo{Username: "yyy"}
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
return err
}
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return err
}
// add Commit() after all actions
return session.Commit()
```
* Or you can use `Transaction` to replace above codes.
```Go
res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) {
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
if _, err := session.Insert(&user1); err != nil {
return nil, err
}
user2 := Userinfo{Username: "yyy"}
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
return nil, err
}
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return nil, err
}
return nil, nil
})
```
* Context Cache, if enabled, current query result will be cached on session and be used by next same statement on the same session.
```Go
sess := engine.NewSession()
defer sess.Close()
var context = xorm.NewMemoryContextCache()
var c2 ContextGetStruct
has, err := sess.ID(1).ContextCache(context).Get(&c2)
assert.NoError(t, err)
assert.True(t, has)
assert.EqualValues(t, 1, c2.Id)
assert.EqualValues(t, "1", c2.Name)
sql, args := sess.LastSQL()
assert.True(t, len(sql) > 0)
assert.True(t, len(args) > 0)
var c3 ContextGetStruct
has, err = sess.ID(1).ContextCache(context).Get(&c3)
assert.NoError(t, err)
assert.True(t, has)
assert.EqualValues(t, 1, c3.Id)
assert.EqualValues(t, "1", c3.Name)
sql, args = sess.LastSQL()
assert.True(t, len(sql) == 0)
assert.True(t, len(args) == 0)
```
## Contributing
If you want to pull request, please see [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md). And we also provide [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm) to discuss.
## Credits
### Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="graphs/contributors"><img src="https://opencollective.com/xorm/contributors.svg?width=890&button=false" /></a>
### Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/xorm#backer)]
<a href="https://opencollective.com/xorm#backers" target="_blank"><img src="https://opencollective.com/xorm/backers.svg?width=890"></a>
### Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/xorm#sponsor)]
## Changelog
* **v0.7.0**
* Some bugs fixed
* **v0.6.6**
* Some bugs fixed
* **v0.6.5**
* Postgres schema support
* vgo support
* Add FindAndCount
* Database special params support via NewEngineWithParams
* Some bugs fixed
* **v0.6.4**
* Automatical Read/Write seperatelly
* Query/QueryString/QueryInterface and action with Where/And
* Get support non-struct variables
* BufferSize on Iterate
* fix some other bugs.
[More changes ...](https://github.com/go-xorm/manual-en-US/tree/master/chapter-16)
## Cases
* [studygolang](http://studygolang.com/) - [github.com/studygolang/studygolang](https://github.com/studygolang/studygolang)
* [Gitea](http://gitea.io) - [github.com/go-gitea/gitea](http://github.com/go-gitea/gitea)
* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs)
* [grafana](https://grafana.com/) - [github.com/grafana/grafana](http://github.com/grafana/grafana)
* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)
* [Wego](http://github.com/go-tango/wego)
* [Docker.cn](https://docker.cn/)
* [Xorm Adapter](https://github.com/casbin/xorm-adapter) for [Casbin](https://github.com/casbin/casbin) - [github.com/casbin/xorm-adapter](https://github.com/casbin/xorm-adapter)
* [Gorevel](http://gorevel.cn/) - [github.com/goofcc/gorevel](http://github.com/goofcc/gorevel)
* [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
* [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild)
* [Sudo China](http://sudochina.com) - [github.com/insionng/toropress](http://github.com/insionng/toropress)
* [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
* [YouGam](http://www.yougam.com/)
* [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
* [GoBBS - gobbs.domolo.com](http://gobbs.domolo.com/)
* [go-blog](http://wangcheng.me) - [github.com/easykoo/go-blog](https://github.com/easykoo/go-blog)
## LICENSE
BSD License [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)

View File

@ -1,500 +0,0 @@
# xorm
[English](https://github.com/go-xorm/xorm/blob/master/README.md)
xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
[![CircleCI](https://circleci.com/gh/go-xorm/xorm.svg?style=shield)](https://circleci.com/gh/go-xorm/xorm) [![codecov](https://codecov.io/gh/go-xorm/xorm/branch/master/graph/badge.svg)](https://codecov.io/gh/go-xorm/xorm)
[![](https://goreportcard.com/badge/github.com/go-xorm/xorm)](https://goreportcard.com/report/github.com/go-xorm/xorm)
[![Join the chat at https://img.shields.io/discord/323460943201959939.svg](https://img.shields.io/discord/323460943201959939.svg)](https://discord.gg/HuR2CF3)
## 特性
* 支持Struct和数据库表之间的灵活映射,并支持自动同步
* 事务支持
* 同时支持原始SQL语句和ORM操作的混合执行
* 使用连写来简化调用
* 支持使用Id, In, Where, Limit, Join, Having, Table, Sql, Cols等函数和结构体等方式作为条件
* 支持级联加载Struct
* Schema支持(仅Postgres)
* 支持缓存
* 支持根据数据库自动生成xorm的结构体
* 支持记录版本(即乐观锁)
* 内置SQL Builder支持
* 上下文缓存支持
## 驱动支持
目前支持的Go数据库驱动和对应的数据库如下:
* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
* Postgres: [github.com/lib/pq](https://github.com/lib/pq)
* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb)
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
* MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持)
## 安装
go get github.com/go-xorm/xorm
## 文档
* [操作指南](http://xorm.io/docs)
* [GoWalker代码文档](http://gowalker.org/github.com/go-xorm/xorm)
* [Godoc代码文档](http://godoc.org/github.com/go-xorm/xorm)
# 快速开始
* 第一步创建引擎,driverName, dataSourceName和database/sql接口相同
```Go
engine, err := xorm.NewEngine(driverName, dataSourceName)
```
* 定义一个和表同步的结构体,并且自动同步结构体到数据库
```Go
type User struct {
Id int64
Name string
Salt string
Age int
Passwd string `xorm:"varchar(200)"`
Created time.Time `xorm:"created"`
Updated time.Time `xorm:"updated"`
}
err := engine.Sync2(new(User))
```
* 创建Engine组
```Go
dataSourceNameSlice := []string{masterDataSourceName, slave1DataSourceName, slave2DataSourceName}
engineGroup, err := xorm.NewEngineGroup(driverName, dataSourceNameSlice)
```
```Go
masterEngine, err := xorm.NewEngine(driverName, masterDataSourceName)
slave1Engine, err := xorm.NewEngine(driverName, slave1DataSourceName)
slave2Engine, err := xorm.NewEngine(driverName, slave2DataSourceName)
engineGroup, err := xorm.NewEngineGroup(masterEngine, []*Engine{slave1Engine, slave2Engine})
```
所有使用 `engine` 都可以简单的用 `engineGroup` 来替换。
* `Query` 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte。`QueryString` 返回 []map[string]string, `QueryInterface` 返回 `[]map[string]interface{}`.
```Go
results, err := engine.Query("select * from user")
results, err := engine.Where("a = 1").Query()
results, err := engine.QueryString("select * from user")
results, err := engine.Where("a = 1").QueryString()
results, err := engine.QueryInterface("select * from user")
results, err := engine.Where("a = 1").QueryInterface()
```
* `Exec` 执行一个SQL语句
```Go
affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
```
* `Insert` 插入一条或者多条记录
```Go
affected, err := engine.Insert(&user)
// INSERT INTO struct () values ()
affected, err := engine.Insert(&user1, &user2)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values ()
affected, err := engine.Insert(&users)
// INSERT INTO struct () values (),(),()
affected, err := engine.Insert(&user1, &users)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values (),(),()
```
* `Get` 查询单条记录
```Go
has, err := engine.Get(&user)
// SELECT * FROM user LIMIT 1
has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
var name string
has, err := engine.Table(&user).Where("id = ?", id).Cols("name").Get(&name)
// SELECT name FROM user WHERE id = ?
var id int64
has, err := engine.Table(&user).Where("name = ?", name).Cols("id").Get(&id)
has, err := engine.SQL("select id from user").Get(&id)
// SELECT id FROM user WHERE name = ?
var valuesMap = make(map[string]string)
has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap)
// SELECT * FROM user WHERE id = ?
var valuesSlice = make([]interface{}, len(cols))
has, err := engine.Table(&user).Where("id = ?", id).Cols(cols...).Get(&valuesSlice)
// SELECT col1, col2, col3 FROM user WHERE id = ?
```
* `Exist` 检测记录是否存在
```Go
has, err := testEngine.Exist(new(RecordExist))
// SELECT * FROM record_exist LIMIT 1
has, err = testEngine.Exist(&RecordExist{
Name: "test1",
})
// SELECT * FROM record_exist WHERE name = ? LIMIT 1
has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{})
// SELECT * FROM record_exist WHERE name = ? LIMIT 1
has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist()
// select * from record_exist where name = ?
has, err = testEngine.Table("record_exist").Exist()
// SELECT * FROM record_exist LIMIT 1
has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist()
// SELECT * FROM record_exist WHERE name = ? LIMIT 1
```
* `Find` 查询多条记录,当然可以使用Join和extends来组合使用
```Go
var users []User
err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
// SELECT * FROM user WHERE name = ? AND age > 10 limit 10 offset 0
type Detail struct {
Id int64
UserId int64 `xorm:"index"`
}
type UserDetail struct {
User `xorm:"extends"`
Detail `xorm:"extends"`
}
var users []UserDetail
err := engine.Table("user").Select("user.*, detail.*")
Join("INNER", "detail", "detail.user_id = user.id").
Where("user.name = ?", name).Limit(10, 0).
Find(&users)
// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 10 offset 0
```
* `Iterate``Rows` 根据条件遍历数据库,可以有两种方式: Iterate and Rows
```Go
err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
user := bean.(*User)
return nil
})
// SELECT * FROM user
err := engine.BufferSize(100).Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
user := bean.(*User)
return nil
})
// SELECT * FROM user Limit 0, 100
// SELECT * FROM user Limit 101, 100
rows, err := engine.Rows(&User{Name:name})
// SELECT * FROM user
defer rows.Close()
bean := new(Struct)
for rows.Next() {
err = rows.Scan(bean)
}
```
* `Update` 更新数据,除非使用Cols,AllCols函数指明,默认只更新非空和非0的字段
```Go
affected, err := engine.ID(1).Update(&user)
// UPDATE user SET ... Where id = ?
affected, err := engine.Update(&user, &User{Name:name})
// UPDATE user SET ... Where name = ?
var ids = []int64{1, 2, 3}
affected, err := engine.In(ids).Update(&user)
// UPDATE user SET ... Where id IN (?, ?, ?)
// force update indicated columns by Cols
affected, err := engine.ID(1).Cols("age").Update(&User{Name:name, Age: 12})
// UPDATE user SET age = ?, updated=? Where id = ?
// force NOT update indicated columns by Omit
affected, err := engine.ID(1).Omit("name").Update(&User{Name:name, Age: 12})
// UPDATE user SET age = ?, updated=? Where id = ?
affected, err := engine.ID(1).AllCols().Update(&user)
// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
```
* `Delete` 删除记录,需要注意,删除必须至少有一个条件,否则会报错。要清空数据库可以用EmptyTable
```Go
affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ...
affected, err := engine.ID(2).Delete(&user)
// DELETE FROM user Where id = ?
```
* `Count` 获取记录条数
```Go
counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user
```
* `Sum` 求和函数
```Go
agesFloat64, err := engine.Sum(&user, "age")
// SELECT sum(age) AS total FROM user
agesInt64, err := engine.SumInt(&user, "age")
// SELECT sum(age) AS total FROM user
sumFloat64Slice, err := engine.Sums(&user, "age", "score")
// SELECT sum(age), sum(score) FROM user
sumInt64Slice, err := engine.SumsInt(&user, "age", "score")
// SELECT sum(age), sum(score) FROM user
```
* 条件编辑器
```Go
err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e"))).Find(&users)
// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?)
```
* 在一个Go程中多次操作数据库,但没有事务
```Go
session := engine.NewSession()
defer session.Close()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
if _, err := session.Insert(&user1); err != nil {
return err
}
user2 := Userinfo{Username: "yyy"}
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
return err
}
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return err
}
return nil
```
* 在一个Go程中有事务
```Go
session := engine.NewSession()
defer session.Close()
// add Begin() before any action
if err := session.Begin(); err != nil {
// if returned then will rollback automatically
return err
}
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
if _, err := session.Insert(&user1); err != nil {
return err
}
user2 := Userinfo{Username: "yyy"}
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
return err
}
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return err
}
// add Commit() after all actions
return session.Commit()
```
* 事务的简写方法
```Go
res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) {
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
if _, err := session.Insert(&user1); err != nil {
return nil, err
}
user2 := Userinfo{Username: "yyy"}
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
return nil, err
}
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return nil, err
}
return nil, nil
})
```
* 上下文缓存,如果启用,那么针对单个对象的查询将会被缓存到系统中,可以被下一个查询使用。
```Go
sess := engine.NewSession()
defer sess.Close()
var context = xorm.NewMemoryContextCache()
var c2 ContextGetStruct
has, err := sess.ID(1).ContextCache(context).Get(&c2)
assert.NoError(t, err)
assert.True(t, has)
assert.EqualValues(t, 1, c2.Id)
assert.EqualValues(t, "1", c2.Name)
sql, args := sess.LastSQL()
assert.True(t, len(sql) > 0)
assert.True(t, len(args) > 0)
var c3 ContextGetStruct
has, err = sess.ID(1).ContextCache(context).Get(&c3)
assert.NoError(t, err)
assert.True(t, has)
assert.EqualValues(t, 1, c3.Id)
assert.EqualValues(t, "1", c3.Name)
sql, args = sess.LastSQL()
assert.True(t, len(sql) == 0)
assert.True(t, len(args) == 0)
```
## 贡献
如果您也想为Xorm贡献您的力量,请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)。您也可以加入QQ群 技术帮助和讨论。
群一:280360085 (已满)
群二:795010183
## Credits
### Contributors
感谢所有的贡献者. [[Contribute](CONTRIBUTING.md)].
<a href="graphs/contributors"><img src="https://opencollective.com/xorm/contributors.svg?width=890&button=false" /></a>
### Backers
感谢我们所有的 backers! 🙏 [[成为 backer](https://opencollective.com/xorm#backer)]
<a href="https://opencollective.com/xorm#backers" target="_blank"><img src="https://opencollective.com/xorm/backers.svg?width=890"></a>
### Sponsors
成为 sponsor 来支持 xorm。您的 logo 将会被显示并被链接到您的网站。 [[成为 sponsor](https://opencollective.com/xorm#sponsor)]
# 案例
* [Go语言中文网](http://studygolang.com/) - [github.com/studygolang/studygolang](https://github.com/studygolang/studygolang)
* [Gitea](http://gitea.io) - [github.com/go-gitea/gitea](http://github.com/go-gitea/gitea)
* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs)
* [grafana](https://grafana.com/) - [github.com/grafana/grafana](http://github.com/grafana/grafana)
* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)
* [Wego](http://github.com/go-tango/wego)
* [Docker.cn](https://docker.cn/)
* [Xorm Adapter](https://github.com/casbin/xorm-adapter) for [Casbin](https://github.com/casbin/casbin) - [github.com/casbin/xorm-adapter](https://github.com/casbin/xorm-adapter)
* [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
* [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild)
* [Sudo China](http://sudochina.com) - [github.com/insionng/toropress](http://github.com/insionng/toropress)
* [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
* [YouGam](http://www.yougam.com/)
* [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
* [GoBBS - gobbs.domolo.com](http://gobbs.domolo.com/)
* [go-blog](http://wangcheng.me) - [github.com/easykoo/go-blog](https://github.com/easykoo/go-blog)
## 更新日志
* **v0.7.0**
* 修正部分Bug
* **v0.6.6**
* 修正部分Bug
* **v0.6.5**
* 通过 engine.SetSchema 来支持 schema,当前仅支持Postgres
* vgo 支持
* 新增 `FindAndCount` 函数
* 通过 `NewEngineWithParams` 支持数据库特别参数
* 修正部分Bug
* **v0.6.4**
* 自动读写分离支持
* Query/QueryString/QueryInterface 支持与 Where/And 合用
* `Get` 支持获取非结构体变量
* `Iterate` 支持 `BufferSize`
* 修正部分Bug
[更多更新日志...](https://github.com/go-xorm/manual-zh-CN/tree/master/chapter-16)
## LICENSE
BSD License
[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)

View File

@ -1,284 +0,0 @@
// Copyright 2015 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 (
"container/list"
"fmt"
"sync"
"time"
"xorm.io/core"
)
// LRUCacher implments cache object facilities
type LRUCacher struct {
idList *list.List
sqlList *list.List
idIndex map[string]map[string]*list.Element
sqlIndex map[string]map[string]*list.Element
store core.CacheStore
mutex sync.Mutex
MaxElementSize int
Expired time.Duration
GcInterval time.Duration
}
// NewLRUCacher creates a cacher
func NewLRUCacher(store core.CacheStore, maxElementSize int) *LRUCacher {
return NewLRUCacher2(store, 3600*time.Second, maxElementSize)
}
// NewLRUCacher2 creates a cache include different params
func NewLRUCacher2(store core.CacheStore, expired time.Duration, maxElementSize int) *LRUCacher {
cacher := &LRUCacher{store: store, idList: list.New(),
sqlList: list.New(), Expired: expired,
GcInterval: core.CacheGcInterval, MaxElementSize: maxElementSize,
sqlIndex: make(map[string]map[string]*list.Element),
idIndex: make(map[string]map[string]*list.Element),
}
cacher.RunGC()
return cacher
}
// RunGC run once every m.GcInterval
func (m *LRUCacher) RunGC() {
time.AfterFunc(m.GcInterval, func() {
m.RunGC()
m.GC()
})
}
// GC check ids lit and sql list to remove all element expired
func (m *LRUCacher) GC() {
m.mutex.Lock()
defer m.mutex.Unlock()
var removedNum int
for e := m.idList.Front(); e != nil; {
if removedNum <= core.CacheGcMaxRemoved &&
time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired {
removedNum++
next := e.Next()
node := e.Value.(*idNode)
m.delBean(node.tbName, node.id)
e = next
} else {
break
}
}
removedNum = 0
for e := m.sqlList.Front(); e != nil; {
if removedNum <= core.CacheGcMaxRemoved &&
time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired {
removedNum++
next := e.Next()
node := e.Value.(*sqlNode)
m.delIds(node.tbName, node.sql)
e = next
} else {
break
}
}
}
// GetIds returns all bean's ids according to sql and parameter from cache
func (m *LRUCacher) GetIds(tableName, sql string) interface{} {
m.mutex.Lock()
defer m.mutex.Unlock()
if _, ok := m.sqlIndex[tableName]; !ok {
m.sqlIndex[tableName] = make(map[string]*list.Element)
}
if v, err := m.store.Get(sql); err == nil {
if el, ok := m.sqlIndex[tableName][sql]; !ok {
el = m.sqlList.PushBack(newSQLNode(tableName, sql))
m.sqlIndex[tableName][sql] = el
} else {
lastTime := el.Value.(*sqlNode).lastVisit
// if expired, remove the node and return nil
if time.Now().Sub(lastTime) > m.Expired {
m.delIds(tableName, sql)
return nil
}
m.sqlList.MoveToBack(el)
el.Value.(*sqlNode).lastVisit = time.Now()
}
return v
}
m.delIds(tableName, sql)
return nil
}
// GetBean returns bean according tableName and id from cache
func (m *LRUCacher) GetBean(tableName string, id string) interface{} {
m.mutex.Lock()
defer m.mutex.Unlock()
if _, ok := m.idIndex[tableName]; !ok {
m.idIndex[tableName] = make(map[string]*list.Element)
}
tid := genID(tableName, id)
if v, err := m.store.Get(tid); err == nil {
if el, ok := m.idIndex[tableName][id]; ok {
lastTime := el.Value.(*idNode).lastVisit
// if expired, remove the node and return nil
if time.Now().Sub(lastTime) > m.Expired {
m.delBean(tableName, id)
return nil
}
m.idList.MoveToBack(el)
el.Value.(*idNode).lastVisit = time.Now()
} else {
el = m.idList.PushBack(newIDNode(tableName, id))
m.idIndex[tableName][id] = el
}
return v
}
// store bean is not exist, then remove memory's index
m.delBean(tableName, id)
return nil
}
// clearIds clears all sql-ids mapping on table tableName from cache
func (m *LRUCacher) clearIds(tableName string) {
if tis, ok := m.sqlIndex[tableName]; ok {
for sql, v := range tis {
m.sqlList.Remove(v)
m.store.Del(sql)
}
}
m.sqlIndex[tableName] = make(map[string]*list.Element)
}
// ClearIds clears all sql-ids mapping on table tableName from cache
func (m *LRUCacher) ClearIds(tableName string) {
m.mutex.Lock()
m.clearIds(tableName)
m.mutex.Unlock()
}
func (m *LRUCacher) clearBeans(tableName string) {
if tis, ok := m.idIndex[tableName]; ok {
for id, v := range tis {
m.idList.Remove(v)
tid := genID(tableName, id)
m.store.Del(tid)
}
}
m.idIndex[tableName] = make(map[string]*list.Element)
}
// ClearBeans clears all beans in some table
func (m *LRUCacher) ClearBeans(tableName string) {
m.mutex.Lock()
m.clearBeans(tableName)
m.mutex.Unlock()
}
// PutIds pus ids into table
func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
m.mutex.Lock()
if _, ok := m.sqlIndex[tableName]; !ok {
m.sqlIndex[tableName] = make(map[string]*list.Element)
}
if el, ok := m.sqlIndex[tableName][sql]; !ok {
el = m.sqlList.PushBack(newSQLNode(tableName, sql))
m.sqlIndex[tableName][sql] = el
} else {
el.Value.(*sqlNode).lastVisit = time.Now()
}
m.store.Put(sql, ids)
if m.sqlList.Len() > m.MaxElementSize {
e := m.sqlList.Front()
node := e.Value.(*sqlNode)
m.delIds(node.tbName, node.sql)
}
m.mutex.Unlock()
}
// PutBean puts beans into table
func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) {
m.mutex.Lock()
var el *list.Element
var ok bool
if el, ok = m.idIndex[tableName][id]; !ok {
el = m.idList.PushBack(newIDNode(tableName, id))
m.idIndex[tableName][id] = el
} else {
el.Value.(*idNode).lastVisit = time.Now()
}
m.store.Put(genID(tableName, id), obj)
if m.idList.Len() > m.MaxElementSize {
e := m.idList.Front()
node := e.Value.(*idNode)
m.delBean(node.tbName, node.id)
}
m.mutex.Unlock()
}
func (m *LRUCacher) delIds(tableName, sql string) {
if _, ok := m.sqlIndex[tableName]; ok {
if el, ok := m.sqlIndex[tableName][sql]; ok {
delete(m.sqlIndex[tableName], sql)
m.sqlList.Remove(el)
}
}
m.store.Del(sql)
}
// DelIds deletes ids
func (m *LRUCacher) DelIds(tableName, sql string) {
m.mutex.Lock()
m.delIds(tableName, sql)
m.mutex.Unlock()
}
func (m *LRUCacher) delBean(tableName string, id string) {
tid := genID(tableName, id)
if el, ok := m.idIndex[tableName][id]; ok {
delete(m.idIndex[tableName], id)
m.idList.Remove(el)
m.clearIds(tableName)
}
m.store.Del(tid)
}
// DelBean deletes beans in some table
func (m *LRUCacher) DelBean(tableName string, id string) {
m.mutex.Lock()
m.delBean(tableName, id)
m.mutex.Unlock()
}
type idNode struct {
tbName string
id string
lastVisit time.Time
}
type sqlNode struct {
tbName string
sql string
lastVisit time.Time
}
func genSQLKey(sql string, args interface{}) string {
return fmt.Sprintf("%v-%v", sql, args)
}
func genID(prefix string, id string) string {
return fmt.Sprintf("%v-%v", prefix, id)
}
func newIDNode(tbName string, id string) *idNode {
return &idNode{tbName, id, time.Now()}
}
func newSQLNode(tbName, sql string) *sqlNode {
return &sqlNode{tbName, sql, time.Now()}
}

View File

@ -1,52 +0,0 @@
// Copyright 2015 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 (
"testing"
"xorm.io/core"
"github.com/stretchr/testify/assert"
)
func TestLRUCache(t *testing.T) {
type CacheObject1 struct {
Id int64
}
store := NewMemoryStore()
cacher := NewLRUCacher(store, 10000)
tableName := "cache_object1"
pks := []core.PK{
{1},
{2},
}
for _, pk := range pks {
sid, err := pk.ToString()
assert.NoError(t, err)
cacher.PutIds(tableName, "select * from cache_object1", sid)
ids := cacher.GetIds(tableName, "select * from cache_object1")
assert.EqualValues(t, sid, ids)
cacher.ClearIds(tableName)
ids2 := cacher.GetIds(tableName, "select * from cache_object1")
assert.Nil(t, ids2)
obj2 := cacher.GetBean(tableName, sid)
assert.Nil(t, obj2)
var obj = new(CacheObject1)
cacher.PutBean(tableName, sid, obj)
obj3 := cacher.GetBean(tableName, sid)
assert.EqualValues(t, obj, obj3)
cacher.DelBean(tableName, sid)
obj4 := cacher.GetBean(tableName, sid)
assert.Nil(t, obj4)
}
}

View File

@ -1,51 +0,0 @@
// Copyright 2015 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 (
"sync"
"xorm.io/core"
)
var _ core.CacheStore = NewMemoryStore()
// MemoryStore represents in-memory store
type MemoryStore struct {
store map[interface{}]interface{}
mutex sync.RWMutex
}
// NewMemoryStore creates a new store in memory
func NewMemoryStore() *MemoryStore {
return &MemoryStore{store: make(map[interface{}]interface{})}
}
// Put puts object into store
func (s *MemoryStore) Put(key string, value interface{}) error {
s.mutex.Lock()
defer s.mutex.Unlock()
s.store[key] = value
return nil
}
// Get gets object from store
func (s *MemoryStore) Get(key string) (interface{}, error) {
s.mutex.RLock()
defer s.mutex.RUnlock()
if v, ok := s.store[key]; ok {
return v, nil
}
return nil, ErrNotExist
}
// Del deletes object
func (s *MemoryStore) Del(key string) error {
s.mutex.Lock()
defer s.mutex.Unlock()
delete(s.store, key)
return nil
}

View File

@ -1,30 +0,0 @@
// Copyright 2018 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
// ContextCache is the interface that operates the cache data.
type ContextCache interface {
// Put puts value into cache with key.
Put(key string, val interface{})
// Get gets cached value by given key.
Get(key string) interface{}
}
type memoryContextCache map[string]interface{}
// NewMemoryContextCache return memoryContextCache
func NewMemoryContextCache() memoryContextCache {
return make(map[string]interface{})
}
// Put puts value into cache with key.
func (m memoryContextCache) Put(key string, val interface{}) {
m[key] = val
}
// Get gets cached value by given key.
func (m memoryContextCache) Get(key string) interface{} {
return m[key]
}

View File

@ -1,348 +0,0 @@
// 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
import (
"database/sql/driver"
"errors"
"fmt"
"reflect"
"strconv"
"time"
)
var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
func strconvErr(err error) error {
if ne, ok := err.(*strconv.NumError); ok {
return ne.Err
}
return err
}
func cloneBytes(b []byte) []byte {
if b == nil {
return nil
} else {
c := make([]byte, len(b))
copy(c, b)
return c
}
}
func asString(src interface{}) string {
switch v := src.(type) {
case string:
return v
case []byte:
return string(v)
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10)
case reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'g', -1, 64)
case reflect.Float32:
return strconv.FormatFloat(rv.Float(), 'g', -1, 32)
case reflect.Bool:
return strconv.FormatBool(rv.Bool())
}
return fmt.Sprintf("%v", src)
}
func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) {
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.AppendInt(buf, rv.Int(), 10), true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.AppendUint(buf, rv.Uint(), 10), true
case reflect.Float32:
return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true
case reflect.Float64:
return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true
case reflect.Bool:
return strconv.AppendBool(buf, rv.Bool()), true
case reflect.String:
s := rv.String()
return append(buf, s...), true
}
return
}
// convertAssign copies to dest the value in src, converting it if possible.
// An error is returned if the copy would result in loss of information.
// dest should be a pointer type.
func convertAssign(dest, src interface{}) error {
// Common cases, without reflect.
switch s := src.(type) {
case string:
switch d := dest.(type) {
case *string:
if d == nil {
return errNilPtr
}
*d = s
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = []byte(s)
return nil
}
case []byte:
switch d := dest.(type) {
case *string:
if d == nil {
return errNilPtr
}
*d = string(s)
return nil
case *interface{}:
if d == nil {
return errNilPtr
}
*d = cloneBytes(s)
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = cloneBytes(s)
return nil
}
case time.Time:
switch d := dest.(type) {
case *string:
*d = s.Format(time.RFC3339Nano)
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = []byte(s.Format(time.RFC3339Nano))
return nil
}
case nil:
switch d := dest.(type) {
case *interface{}:
if d == nil {
return errNilPtr
}
*d = nil
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = nil
return nil
}
}
var sv reflect.Value
switch d := dest.(type) {
case *string:
sv = reflect.ValueOf(src)
switch sv.Kind() {
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
*d = asString(src)
return nil
}
case *[]byte:
sv = reflect.ValueOf(src)
if b, ok := asBytes(nil, sv); ok {
*d = b
return nil
}
case *bool:
bv, err := driver.Bool.ConvertValue(src)
if err == nil {
*d = bv.(bool)
}
return err
case *interface{}:
*d = src
return nil
}
dpv := reflect.ValueOf(dest)
if dpv.Kind() != reflect.Ptr {
return errors.New("destination not a pointer")
}
if dpv.IsNil() {
return errNilPtr
}
if !sv.IsValid() {
sv = reflect.ValueOf(src)
}
dv := reflect.Indirect(dpv)
if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) {
switch b := src.(type) {
case []byte:
dv.Set(reflect.ValueOf(cloneBytes(b)))
default:
dv.Set(sv)
}
return nil
}
if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) {
dv.Set(sv.Convert(dv.Type()))
return nil
}
switch dv.Kind() {
case reflect.Ptr:
if src == nil {
dv.Set(reflect.Zero(dv.Type()))
return nil
}
dv.Set(reflect.New(dv.Type().Elem()))
return convertAssign(dv.Interface(), src)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
s := asString(src)
i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())
if err != nil {
err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
}
dv.SetInt(i64)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
s := asString(src)
u64, err := strconv.ParseUint(s, 10, dv.Type().Bits())
if err != nil {
err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
}
dv.SetUint(u64)
return nil
case reflect.Float32, reflect.Float64:
s := asString(src)
f64, err := strconv.ParseFloat(s, dv.Type().Bits())
if err != nil {
err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
}
dv.SetFloat(f64)
return nil
case reflect.String:
dv.SetString(asString(src))
return nil
}
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
}
func asKind(vv reflect.Value, tp reflect.Type) (interface{}, error) {
switch tp.Kind() {
case reflect.Int64:
return vv.Int(), nil
case reflect.Int:
return int(vv.Int()), nil
case reflect.Int32:
return int32(vv.Int()), nil
case reflect.Int16:
return int16(vv.Int()), nil
case reflect.Int8:
return int8(vv.Int()), nil
case reflect.Uint64:
return vv.Uint(), nil
case reflect.Uint:
return uint(vv.Uint()), nil
case reflect.Uint32:
return uint32(vv.Uint()), nil
case reflect.Uint16:
return uint16(vv.Uint()), nil
case reflect.Uint8:
return uint8(vv.Uint()), nil
case reflect.String:
return vv.String(), nil
case reflect.Slice:
if tp.Elem().Kind() == reflect.Uint8 {
v, err := strconv.ParseInt(string(vv.Interface().([]byte)), 10, 64)
if err != nil {
return nil, err
}
return v, nil
}
}
return nil, fmt.Errorf("unsupported primary key type: %v, %v", tp, vv)
}
func convertFloat(v interface{}) (float64, error) {
switch v.(type) {
case float32:
return float64(v.(float32)), nil
case float64:
return v.(float64), nil
case string:
i, err := strconv.ParseFloat(v.(string), 64)
if err != nil {
return 0, err
}
return i, nil
case []byte:
i, err := strconv.ParseFloat(string(v.([]byte)), 64)
if err != nil {
return 0, err
}
return i, nil
}
return 0, fmt.Errorf("unsupported type: %v", v)
}
func convertInt(v interface{}) (int64, error) {
switch v.(type) {
case int:
return int64(v.(int)), nil
case int8:
return int64(v.(int8)), nil
case int16:
return int64(v.(int16)), nil
case int32:
return int64(v.(int32)), nil
case int64:
return v.(int64), nil
case []byte:
i, err := strconv.ParseInt(string(v.([]byte)), 10, 64)
if err != nil {
return 0, err
}
return i, nil
case string:
i, err := strconv.ParseInt(v.(string), 10, 64)
if err != nil {
return 0, err
}
return i, nil
}
return 0, fmt.Errorf("unsupported type: %v", v)
}
func asBool(bs []byte) (bool, error) {
if len(bs) == 0 {
return false, nil
}
if bs[0] == 0x00 {
return false, nil
} else if bs[0] == 0x01 {
return true, nil
}
return strconv.ParseBool(string(bs))
}

View File

@ -1,567 +0,0 @@
// Copyright 2015 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 (
"errors"
"fmt"
"net/url"
"strconv"
"strings"
"xorm.io/core"
)
var (
mssqlReservedWords = map[string]bool{
"ADD": true,
"EXTERNAL": true,
"PROCEDURE": true,
"ALL": true,
"FETCH": true,
"PUBLIC": true,
"ALTER": true,
"FILE": true,
"RAISERROR": true,
"AND": true,
"FILLFACTOR": true,
"READ": true,
"ANY": true,
"FOR": true,
"READTEXT": true,
"AS": true,
"FOREIGN": true,
"RECONFIGURE": true,
"ASC": true,
"FREETEXT": true,
"REFERENCES": true,
"AUTHORIZATION": true,
"FREETEXTTABLE": true,
"REPLICATION": true,
"BACKUP": true,
"FROM": true,
"RESTORE": true,
"BEGIN": true,
"FULL": true,
"RESTRICT": true,
"BETWEEN": true,
"FUNCTION": true,
"RETURN": true,
"BREAK": true,
"GOTO": true,
"REVERT": true,
"BROWSE": true,
"GRANT": true,
"REVOKE": true,
"BULK": true,
"GROUP": true,
"RIGHT": true,
"BY": true,
"HAVING": true,
"ROLLBACK": true,
"CASCADE": true,
"HOLDLOCK": true,
"ROWCOUNT": true,
"CASE": true,
"IDENTITY": true,
"ROWGUIDCOL": true,
"CHECK": true,
"IDENTITY_INSERT": true,
"RULE": true,
"CHECKPOINT": true,
"IDENTITYCOL": true,
"SAVE": true,
"CLOSE": true,
"IF": true,
"SCHEMA": true,
"CLUSTERED": true,
"IN": true,
"SECURITYAUDIT": true,
"COALESCE": true,
"INDEX": true,
"SELECT": true,
"COLLATE": true,
"INNER": true,
"SEMANTICKEYPHRASETABLE": true,
"COLUMN": true,
"INSERT": true,
"SEMANTICSIMILARITYDETAILSTABLE": true,
"COMMIT": true,
"INTERSECT": true,
"SEMANTICSIMILARITYTABLE": true,
"COMPUTE": true,
"INTO": true,
"SESSION_USER": true,
"CONSTRAINT": true,
"IS": true,
"SET": true,
"CONTAINS": true,
"JOIN": true,
"SETUSER": true,
"CONTAINSTABLE": true,
"KEY": true,
"SHUTDOWN": true,
"CONTINUE": true,
"KILL": true,
"SOME": true,
"CONVERT": true,
"LEFT": true,
"STATISTICS": true,
"CREATE": true,
"LIKE": true,
"SYSTEM_USER": true,
"CROSS": true,
"LINENO": true,
"TABLE": true,
"CURRENT": true,
"LOAD": true,
"TABLESAMPLE": true,
"CURRENT_DATE": true,
"MERGE": true,
"TEXTSIZE": true,
"CURRENT_TIME": true,
"NATIONAL": true,
"THEN": true,
"CURRENT_TIMESTAMP": true,
"NOCHECK": true,
"TO": true,
"CURRENT_USER": true,
"NONCLUSTERED": true,
"TOP": true,
"CURSOR": true,
"NOT": true,
"TRAN": true,
"DATABASE": true,
"NULL": true,
"TRANSACTION": true,
"DBCC": true,
"NULLIF": true,
"TRIGGER": true,
"DEALLOCATE": true,
"OF": true,
"TRUNCATE": true,
"DECLARE": true,
"OFF": true,
"TRY_CONVERT": true,
"DEFAULT": true,
"OFFSETS": true,
"TSEQUAL": true,
"DELETE": true,
"ON": true,
"UNION": true,
"DENY": true,
"OPEN": true,
"UNIQUE": true,
"DESC": true,
"OPENDATASOURCE": true,
"UNPIVOT": true,
"DISK": true,
"OPENQUERY": true,
"UPDATE": true,
"DISTINCT": true,
"OPENROWSET": true,
"UPDATETEXT": true,
"DISTRIBUTED": true,
"OPENXML": true,
"USE": true,
"DOUBLE": true,
"OPTION": true,
"USER": true,
"DROP": true,
"OR": true,
"VALUES": true,
"DUMP": true,
"ORDER": true,
"VARYING": true,
"ELSE": true,
"OUTER": true,
"VIEW": true,
"END": true,
"OVER": true,
"WAITFOR": true,
"ERRLVL": true,
"PERCENT": true,
"WHEN": true,
"ESCAPE": true,
"PIVOT": true,
"WHERE": true,
"EXCEPT": true,
"PLAN": true,
"WHILE": true,
"EXEC": true,
"PRECISION": true,
"WITH": true,
"EXECUTE": true,
"PRIMARY": true,
"WITHIN": true,
"EXISTS": true,
"PRINT": true,
"WRITETEXT": true,
"EXIT": true,
"PROC": true,
}
)
type mssql struct {
core.Base
}
func (db *mssql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
return db.Base.Init(d, db, uri, drivername, dataSourceName)
}
func (db *mssql) SqlType(c *core.Column) string {
var res string
switch t := c.SQLType.Name; t {
case core.Bool:
res = core.Bit
if strings.EqualFold(c.Default, "true") {
c.Default = "1"
} else if strings.EqualFold(c.Default, "false") {
c.Default = "0"
}
case core.Serial:
c.IsAutoIncrement = true
c.IsPrimaryKey = true
c.Nullable = false
res = core.Int
case core.BigSerial:
c.IsAutoIncrement = true
c.IsPrimaryKey = true
c.Nullable = false
res = core.BigInt
case core.Bytea, core.Blob, core.Binary, core.TinyBlob, core.MediumBlob, core.LongBlob:
res = core.VarBinary
if c.Length == 0 {
c.Length = 50
}
case core.TimeStamp:
res = core.DateTime
case core.TimeStampz:
res = "DATETIMEOFFSET"
c.Length = 7
case core.MediumInt:
res = core.Int
case core.Text, core.MediumText, core.TinyText, core.LongText, core.Json:
res = core.Varchar + "(MAX)"
case core.Double:
res = core.Real
case core.Uuid:
res = core.Varchar
c.Length = 40
case core.TinyInt:
res = core.TinyInt
c.Length = 0
case core.BigInt:
res = core.BigInt
c.Length = 0
default:
res = t
}
if res == core.Int {
return core.Int
}
hasLen1 := (c.Length > 0)
hasLen2 := (c.Length2 > 0)
if hasLen2 {
res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
} else if hasLen1 {
res += "(" + strconv.Itoa(c.Length) + ")"
}
return res
}
func (db *mssql) SupportInsertMany() bool {
return true
}
func (db *mssql) IsReserved(name string) bool {
_, ok := mssqlReservedWords[name]
return ok
}
func (db *mssql) Quote(name string) string {
return "\"" + name + "\""
}
func (db *mssql) SupportEngine() bool {
return false
}
func (db *mssql) AutoIncrStr() string {
return "IDENTITY"
}
func (db *mssql) DropTableSql(tableName string) string {
return fmt.Sprintf("IF EXISTS (SELECT * FROM sysobjects WHERE id = "+
"object_id(N'%s') and OBJECTPROPERTY(id, N'IsUserTable') = 1) "+
"DROP TABLE \"%s\"", tableName, tableName)
}
func (db *mssql) SupportCharset() bool {
return false
}
func (db *mssql) IndexOnTable() bool {
return true
}
func (db *mssql) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
args := []interface{}{idxName}
sql := "select name from sysindexes where id=object_id('" + tableName + "') and name=?"
return sql, args
}
/*func (db *mssql) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
args := []interface{}{tableName, colName}
sql := `SELECT "COLUMN_NAME" FROM "INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = ? AND "COLUMN_NAME" = ?`
return sql, args
}*/
func (db *mssql) IsColumnExist(tableName, colName string) (bool, error) {
query := `SELECT "COLUMN_NAME" FROM "INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = ? AND "COLUMN_NAME" = ?`
return db.HasRecords(query, tableName, colName)
}
func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
args := []interface{}{}
sql := "select * from sysobjects where id = object_id(N'" + tableName + "') and OBJECTPROPERTY(id, N'IsUserTable') = 1"
return sql, args
}
func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
args := []interface{}{}
s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable,
"default_is_null" = (CASE WHEN c.text is null THEN 1 ELSE 0 END),
replace(replace(isnull(c.text,''),'(',''),')','') as vdefault,
ISNULL(i.is_primary_key, 0), a.is_identity as is_identity
from sys.columns a
left join sys.types b on a.user_type_id=b.user_type_id
left join sys.syscomments c on a.default_object_id=c.id
LEFT OUTER JOIN
sys.index_columns ic ON ic.object_id = a.object_id AND ic.column_id = a.column_id
LEFT OUTER JOIN
sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
where a.object_id=object_id('` + tableName + `')`
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, nil, err
}
defer rows.Close()
cols := make(map[string]*core.Column)
colSeq := make([]string, 0)
for rows.Next() {
var name, ctype, vdefault string
var maxLen, precision, scale int
var nullable, isPK, defaultIsNull, isIncrement bool
err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &defaultIsNull, &vdefault, &isPK, &isIncrement)
if err != nil {
return nil, nil, err
}
col := new(core.Column)
col.Indexes = make(map[string]int)
col.Name = strings.Trim(name, "` ")
col.Nullable = nullable
col.DefaultIsEmpty = defaultIsNull
if !defaultIsNull {
col.Default = vdefault
}
col.IsPrimaryKey = isPK
col.IsAutoIncrement = isIncrement
ct := strings.ToUpper(ctype)
if ct == "DECIMAL" {
col.Length = precision
col.Length2 = scale
} else {
col.Length = maxLen
}
switch ct {
case "DATETIMEOFFSET":
col.SQLType = core.SQLType{Name: core.TimeStampz, DefaultLength: 0, DefaultLength2: 0}
case "NVARCHAR":
col.SQLType = core.SQLType{Name: core.NVarchar, DefaultLength: 0, DefaultLength2: 0}
case "IMAGE":
col.SQLType = core.SQLType{Name: core.VarBinary, DefaultLength: 0, DefaultLength2: 0}
default:
if _, ok := core.SqlTypes[ct]; ok {
col.SQLType = core.SQLType{Name: ct, DefaultLength: 0, DefaultLength2: 0}
} else {
return nil, nil, fmt.Errorf("Unknown colType %v for %v - %v", ct, tableName, col.Name)
}
}
cols[col.Name] = col
colSeq = append(colSeq, col.Name)
}
return colSeq, cols, nil
}
func (db *mssql) GetTables() ([]*core.Table, error) {
args := []interface{}{}
s := `select name from sysobjects where xtype ='U'`
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, err
}
defer rows.Close()
tables := make([]*core.Table, 0)
for rows.Next() {
table := core.NewEmptyTable()
var name string
err = rows.Scan(&name)
if err != nil {
return nil, err
}
table.Name = strings.Trim(name, "` ")
tables = append(tables, table)
}
return tables, nil
}
func (db *mssql) GetIndexes(tableName string) (map[string]*core.Index, error) {
args := []interface{}{tableName}
s := `SELECT
IXS.NAME AS [INDEX_NAME],
C.NAME AS [COLUMN_NAME],
IXS.is_unique AS [IS_UNIQUE]
FROM SYS.INDEXES IXS
INNER JOIN SYS.INDEX_COLUMNS IXCS
ON IXS.OBJECT_ID=IXCS.OBJECT_ID AND IXS.INDEX_ID = IXCS.INDEX_ID
INNER JOIN SYS.COLUMNS C ON IXS.OBJECT_ID=C.OBJECT_ID
AND IXCS.COLUMN_ID=C.COLUMN_ID
WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
`
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, err
}
defer rows.Close()
indexes := make(map[string]*core.Index, 0)
for rows.Next() {
var indexType int
var indexName, colName, isUnique string
err = rows.Scan(&indexName, &colName, &isUnique)
if err != nil {
return nil, err
}
i, err := strconv.ParseBool(isUnique)
if err != nil {
return nil, err
}
if i {
indexType = core.UniqueType
} else {
indexType = core.IndexType
}
colName = strings.Trim(colName, "` ")
var isRegular bool
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
indexName = indexName[5+len(tableName):]
isRegular = true
}
var index *core.Index
var ok bool
if index, ok = indexes[indexName]; !ok {
index = new(core.Index)
index.Type = indexType
index.Name = indexName
index.IsRegular = isRegular
indexes[indexName] = index
}
index.AddColumn(colName)
}
return indexes, nil
}
func (db *mssql) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string {
var sql string
if tableName == "" {
tableName = table.Name
}
sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + tableName + "' ) CREATE TABLE "
sql += db.Quote(tableName) + " ("
pkList := table.PrimaryKeys
for _, colName := range table.ColumnsSeq() {
col := table.GetColumn(colName)
if col.IsPrimaryKey && len(pkList) == 1 {
sql += col.String(db)
} else {
sql += col.StringNoPk(db)
}
sql = strings.TrimSpace(sql)
sql += ", "
}
if len(pkList) > 1 {
sql += "PRIMARY KEY ( "
sql += strings.Join(pkList, ",")
sql += " ), "
}
sql = sql[:len(sql)-2] + ")"
sql += ";"
return sql
}
func (db *mssql) ForUpdateSql(query string) string {
return query
}
func (db *mssql) Filters() []core.Filter {
return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}}
}
type odbcDriver struct {
}
func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
var dbName string
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]
}
}
}
}
if dbName == "" {
return nil, errors.New("no db name provided")
}
return &core.Uri{DbName: dbName, DbType: core.MSSQL}, nil
}

View File

@ -1,654 +0,0 @@
// Copyright 2015 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 (
"crypto/tls"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"time"
"xorm.io/core"
)
var (
mysqlReservedWords = map[string]bool{
"ADD": true,
"ALL": true,
"ALTER": true,
"ANALYZE": true,
"AND": true,
"AS": true,
"ASC": true,
"ASENSITIVE": true,
"BEFORE": true,
"BETWEEN": true,
"BIGINT": true,
"BINARY": true,
"BLOB": true,
"BOTH": true,
"BY": true,
"CALL": true,
"CASCADE": true,
"CASE": true,
"CHANGE": true,
"CHAR": true,
"CHARACTER": true,
"CHECK": true,
"COLLATE": true,
"COLUMN": true,
"CONDITION": true,
"CONNECTION": true,
"CONSTRAINT": true,
"CONTINUE": true,
"CONVERT": true,
"CREATE": true,
"CROSS": true,
"CURRENT_DATE": true,
"CURRENT_TIME": true,
"CURRENT_TIMESTAMP": true,
"CURRENT_USER": true,
"CURSOR": true,
"DATABASE": true,
"DATABASES": true,
"DAY_HOUR": true,
"DAY_MICROSECOND": true,
"DAY_MINUTE": true,
"DAY_SECOND": true,
"DEC": true,
"DECIMAL": true,
"DECLARE": true,
"DEFAULT": true,
"DELAYED": true,
"DELETE": true,
"DESC": true,
"DESCRIBE": true,
"DETERMINISTIC": true,
"DISTINCT": true,
"DISTINCTROW": true,
"DIV": true,
"DOUBLE": true,
"DROP": true,
"DUAL": true,
"EACH": true,
"ELSE": true,
"ELSEIF": true,
"ENCLOSED": true,
"ESCAPED": true,
"EXISTS": true,
"EXIT": true,
"EXPLAIN": true,
"FALSE": true,
"FETCH": true,
"FLOAT": true,
"FLOAT4": true,
"FLOAT8": true,
"FOR": true,
"FORCE": true,
"FOREIGN": true,
"FROM": true,
"FULLTEXT": true,
"GOTO": true,
"GRANT": true,
"GROUP": true,
"HAVING": true,
"HIGH_PRIORITY": true,
"HOUR_MICROSECOND": true,
"HOUR_MINUTE": true,
"HOUR_SECOND": true,
"IF": true,
"IGNORE": true,
"IN": true, "INDEX": true,
"INFILE": true, "INNER": true, "INOUT": true,
"INSENSITIVE": true, "INSERT": true, "INT": true,
"INT1": true, "INT2": true, "INT3": true,
"INT4": true, "INT8": true, "INTEGER": true,
"INTERVAL": true, "INTO": true, "IS": true,
"ITERATE": true, "JOIN": true, "KEY": true,
"KEYS": true, "KILL": true, "LABEL": true,
"LEADING": true, "LEAVE": true, "LEFT": true,
"LIKE": true, "LIMIT": true, "LINEAR": true,
"LINES": true, "LOAD": true, "LOCALTIME": true,
"LOCALTIMESTAMP": true, "LOCK": true, "LONG": true,
"LONGBLOB": true, "LONGTEXT": true, "LOOP": true,
"LOW_PRIORITY": true, "MATCH": true, "MEDIUMBLOB": true,
"MEDIUMINT": true, "MEDIUMTEXT": true, "MIDDLEINT": true,
"MINUTE_MICROSECOND": true, "MINUTE_SECOND": true, "MOD": true,
"MODIFIES": true, "NATURAL": true, "NOT": true,
"NO_WRITE_TO_BINLOG": true, "NULL": true, "NUMERIC": true,
"ON OPTIMIZE": true, "OPTION": true,
"OPTIONALLY": true, "OR": true, "ORDER": true,
"OUT": true, "OUTER": true, "OUTFILE": true,
"PRECISION": true, "PRIMARY": true, "PROCEDURE": true,
"PURGE": true, "RAID0": true, "RANGE": true,
"READ": true, "READS": true, "REAL": true,
"REFERENCES": true, "REGEXP": true, "RELEASE": true,
"RENAME": true, "REPEAT": true, "REPLACE": true,
"REQUIRE": true, "RESTRICT": true, "RETURN": true,
"REVOKE": true, "RIGHT": true, "RLIKE": true,
"SCHEMA": true, "SCHEMAS": true, "SECOND_MICROSECOND": true,
"SELECT": true, "SENSITIVE": true, "SEPARATOR": true,
"SET": true, "SHOW": true, "SMALLINT": true,
"SPATIAL": true, "SPECIFIC": true, "SQL": true,
"SQLEXCEPTION": true, "SQLSTATE": true, "SQLWARNING": true,
"SQL_BIG_RESULT": true, "SQL_CALC_FOUND_ROWS": true, "SQL_SMALL_RESULT": true,
"SSL": true, "STARTING": true, "STRAIGHT_JOIN": true,
"TABLE": true, "TERMINATED": true, "THEN": true,
"TINYBLOB": true, "TINYINT": true, "TINYTEXT": true,
"TO": true, "TRAILING": true, "TRIGGER": true,
"TRUE": true, "UNDO": true, "UNION": true,
"UNIQUE": true, "UNLOCK": true, "UNSIGNED": true,
"UPDATE": true, "USAGE": true, "USE": true,
"USING": true, "UTC_DATE": true, "UTC_TIME": true,
"UTC_TIMESTAMP": true, "VALUES": true, "VARBINARY": true,
"VARCHAR": true,
"VARCHARACTER": true,
"VARYING": true,
"WHEN": true,
"WHERE": true,
"WHILE": true,
"WITH": true,
"WRITE": true,
"X509": true,
"XOR": true,
"YEAR_MONTH": true,
"ZEROFILL": true,
}
)
type mysql struct {
core.Base
net string
addr string
params map[string]string
loc *time.Location
timeout time.Duration
tls *tls.Config
allowAllFiles bool
allowOldPasswords bool
clientFoundRows bool
rowFormat string
}
func (db *mysql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
return db.Base.Init(d, db, uri, drivername, dataSourceName)
}
func (db *mysql) SetParams(params map[string]string) {
rowFormat, ok := params["rowFormat"]
if ok {
var t = strings.ToUpper(rowFormat)
switch t {
case "COMPACT":
fallthrough
case "REDUNDANT":
fallthrough
case "DYNAMIC":
fallthrough
case "COMPRESSED":
db.rowFormat = t
break
default:
break
}
}
}
func (db *mysql) SqlType(c *core.Column) string {
var res string
switch t := c.SQLType.Name; t {
case core.Bool:
res = core.TinyInt
c.Length = 1
case core.Serial:
c.IsAutoIncrement = true
c.IsPrimaryKey = true
c.Nullable = false
res = core.Int
case core.BigSerial:
c.IsAutoIncrement = true
c.IsPrimaryKey = true
c.Nullable = false
res = core.BigInt
case core.Bytea:
res = core.Blob
case core.TimeStampz:
res = core.Char
c.Length = 64
case core.Enum: // mysql enum
res = core.Enum
res += "("
opts := ""
for v := range c.EnumOptions {
opts += fmt.Sprintf(",'%v'", v)
}
res += strings.TrimLeft(opts, ",")
res += ")"
case core.Set: // mysql set
res = core.Set
res += "("
opts := ""
for v := range c.SetOptions {
opts += fmt.Sprintf(",'%v'", v)
}
res += strings.TrimLeft(opts, ",")
res += ")"
case core.NVarchar:
res = core.Varchar
case core.Uuid:
res = core.Varchar
c.Length = 40
case core.Json:
res = core.Text
default:
res = t
}
hasLen1 := (c.Length > 0)
hasLen2 := (c.Length2 > 0)
if res == core.BigInt && !hasLen1 && !hasLen2 {
c.Length = 20
hasLen1 = true
}
if hasLen2 {
res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
} else if hasLen1 {
res += "(" + strconv.Itoa(c.Length) + ")"
}
return res
}
func (db *mysql) SupportInsertMany() bool {
return true
}
func (db *mysql) IsReserved(name string) bool {
_, ok := mysqlReservedWords[name]
return ok
}
func (db *mysql) Quote(name string) string {
return "`" + name + "`"
}
func (db *mysql) SupportEngine() bool {
return true
}
func (db *mysql) AutoIncrStr() string {
return "AUTO_INCREMENT"
}
func (db *mysql) SupportCharset() bool {
return true
}
func (db *mysql) IndexOnTable() bool {
return true
}
func (db *mysql) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
args := []interface{}{db.DbName, tableName, idxName}
sql := "SELECT `INDEX_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS`"
sql += " WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `INDEX_NAME`=?"
return sql, args
}
/*func (db *mysql) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
args := []interface{}{db.DbName, tableName, colName}
sql := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?"
return sql, args
}*/
func (db *mysql) TableCheckSql(tableName string) (string, []interface{}) {
args := []interface{}{db.DbName, tableName}
sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?"
return sql, args
}
func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
args := []interface{}{db.DbName, tableName}
s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," +
" `COLUMN_KEY`, `EXTRA`,`COLUMN_COMMENT` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, nil, err
}
defer rows.Close()
cols := make(map[string]*core.Column)
colSeq := make([]string, 0)
for rows.Next() {
col := new(core.Column)
col.Indexes = make(map[string]int)
var columnName, isNullable, colType, colKey, extra, comment string
var colDefault *string
err = rows.Scan(&columnName, &isNullable, &colDefault, &colType, &colKey, &extra, &comment)
if err != nil {
return nil, nil, err
}
col.Name = strings.Trim(columnName, "` ")
col.Comment = comment
if "YES" == isNullable {
col.Nullable = true
}
if colDefault != nil {
col.Default = *colDefault
col.DefaultIsEmpty = false
} else {
col.DefaultIsEmpty = true
}
cts := strings.Split(colType, "(")
colName := cts[0]
colType = strings.ToUpper(colName)
var len1, len2 int
if len(cts) == 2 {
idx := strings.Index(cts[1], ")")
if colType == core.Enum && cts[1][0] == '\'' { // enum
options := strings.Split(cts[1][0:idx], ",")
col.EnumOptions = make(map[string]int)
for k, v := range options {
v = strings.TrimSpace(v)
v = strings.Trim(v, "'")
col.EnumOptions[v] = k
}
} else if colType == core.Set && cts[1][0] == '\'' {
options := strings.Split(cts[1][0:idx], ",")
col.SetOptions = make(map[string]int)
for k, v := range options {
v = strings.TrimSpace(v)
v = strings.Trim(v, "'")
col.SetOptions[v] = k
}
} else {
lens := strings.Split(cts[1][0:idx], ",")
len1, err = strconv.Atoi(strings.TrimSpace(lens[0]))
if err != nil {
return nil, nil, err
}
if len(lens) == 2 {
len2, err = strconv.Atoi(lens[1])
if err != nil {
return nil, nil, err
}
}
}
}
if colType == "FLOAT UNSIGNED" {
colType = "FLOAT"
}
if colType == "DOUBLE UNSIGNED" {
colType = "DOUBLE"
}
col.Length = len1
col.Length2 = len2
if _, ok := core.SqlTypes[colType]; ok {
col.SQLType = core.SQLType{Name: colType, DefaultLength: len1, DefaultLength2: len2}
} else {
return nil, nil, fmt.Errorf("Unknown colType %v", colType)
}
if colKey == "PRI" {
col.IsPrimaryKey = true
}
if colKey == "UNI" {
// col.is
}
if extra == "auto_increment" {
col.IsAutoIncrement = true
}
if !col.DefaultIsEmpty {
if col.SQLType.IsText() {
col.Default = "'" + col.Default + "'"
} else if col.SQLType.IsTime() && col.Default != "CURRENT_TIMESTAMP" {
col.Default = "'" + col.Default + "'"
}
}
cols[col.Name] = col
colSeq = append(colSeq, col.Name)
}
return colSeq, cols, nil
}
func (db *mysql) GetTables() ([]*core.Table, error) {
args := []interface{}{db.DbName}
s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT`, `TABLE_COMMENT` from " +
"`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB' OR `ENGINE` = 'TokuDB')"
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, err
}
defer rows.Close()
tables := make([]*core.Table, 0)
for rows.Next() {
table := core.NewEmptyTable()
var name, engine, tableRows, comment string
var autoIncr *string
err = rows.Scan(&name, &engine, &tableRows, &autoIncr, &comment)
if err != nil {
return nil, err
}
table.Name = name
table.Comment = comment
table.StoreEngine = engine
tables = append(tables, table)
}
return tables, nil
}
func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) {
args := []interface{}{db.DbName, tableName}
s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, err
}
defer rows.Close()
indexes := make(map[string]*core.Index, 0)
for rows.Next() {
var indexType int
var indexName, colName, nonUnique string
err = rows.Scan(&indexName, &nonUnique, &colName)
if err != nil {
return nil, err
}
if indexName == "PRIMARY" {
continue
}
if "YES" == nonUnique || nonUnique == "1" {
indexType = core.IndexType
} else {
indexType = core.UniqueType
}
colName = strings.Trim(colName, "` ")
var isRegular bool
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
indexName = indexName[5+len(tableName):]
isRegular = true
}
var index *core.Index
var ok bool
if index, ok = indexes[indexName]; !ok {
index = new(core.Index)
index.IsRegular = isRegular
index.Type = indexType
index.Name = indexName
indexes[indexName] = index
}
index.AddColumn(colName)
}
return indexes, nil
}
func (db *mysql) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string {
var sql string
sql = "CREATE TABLE IF NOT EXISTS "
if tableName == "" {
tableName = table.Name
}
sql += db.Quote(tableName)
sql += " ("
if len(table.ColumnsSeq()) > 0 {
pkList := table.PrimaryKeys
for _, colName := range table.ColumnsSeq() {
col := table.GetColumn(colName)
if col.IsPrimaryKey && len(pkList) == 1 {
sql += col.String(db)
} else {
sql += col.StringNoPk(db)
}
sql = strings.TrimSpace(sql)
if len(col.Comment) > 0 {
sql += " COMMENT '" + col.Comment + "'"
}
sql += ", "
}
if len(pkList) > 1 {
sql += "PRIMARY KEY ( "
sql += db.Quote(strings.Join(pkList, db.Quote(",")))
sql += " ), "
}
sql = sql[:len(sql)-2]
}
sql += ")"
if storeEngine != "" {
sql += " ENGINE=" + storeEngine
}
if len(charset) == 0 {
charset = db.URI().Charset
}
if len(charset) != 0 {
sql += " DEFAULT CHARSET " + charset
}
if db.rowFormat != "" {
sql += " ROW_FORMAT=" + db.rowFormat
}
return sql
}
func (db *mysql) Filters() []core.Filter {
return []core.Filter{&core.IdFilter{}}
}
type mymysqlDriver struct {
}
func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
db := &core.Uri{DbType: core.MYSQL}
pd := strings.SplitN(dataSourceName, "*", 2)
if len(pd) == 2 {
// Parse protocol part of URI
p := strings.SplitN(pd[0], ":", 2)
if len(p) != 2 {
return nil, errors.New("Wrong protocol part of URI")
}
db.Proto = p[0]
options := strings.Split(p[1], ",")
db.Raddr = options[0]
for _, o := range options[1:] {
kv := strings.SplitN(o, "=", 2)
var k, v string
if len(kv) == 2 {
k, v = kv[0], kv[1]
} else {
k, v = o, "true"
}
switch k {
case "laddr":
db.Laddr = v
case "timeout":
to, err := time.ParseDuration(v)
if err != nil {
return nil, err
}
db.Timeout = to
default:
return nil, errors.New("Unknown option: " + k)
}
}
// Remove protocol part
pd = pd[1:]
}
// Parse database part of URI
dup := strings.SplitN(pd[0], "/", 3)
if len(dup) != 3 {
return nil, errors.New("Wrong database part of URI")
}
db.DbName = dup[0]
db.User = dup[1]
db.Passwd = dup[2]
return db, nil
}
type mysqlDriver struct {
}
func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
dsnPattern := regexp.MustCompile(
`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
`\/(?P<dbname>.*?)` + // /dbname
`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
matches := dsnPattern.FindStringSubmatch(dataSourceName)
// tlsConfigRegister := make(map[string]*tls.Config)
names := dsnPattern.SubexpNames()
uri := &core.Uri{DbType: core.MYSQL}
for i, match := range matches {
switch names[i] {
case "dbname":
uri.DbName = match
case "params":
if len(match) > 0 {
kvs := strings.Split(match, "&")
for _, kv := range kvs {
splits := strings.Split(kv, "=")
if len(splits) == 2 {
switch splits[0] {
case "charset":
uri.Charset = splits[1]
}
}
}
}
}
}
return uri, nil
}

View File

@ -1,902 +0,0 @@
// Copyright 2015 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 (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"xorm.io/core"
)
var (
oracleReservedWords = map[string]bool{
"ACCESS": true,
"ACCOUNT": true,
"ACTIVATE": true,
"ADD": true,
"ADMIN": true,
"ADVISE": true,
"AFTER": true,
"ALL": true,
"ALL_ROWS": true,
"ALLOCATE": true,
"ALTER": true,
"ANALYZE": true,
"AND": true,
"ANY": true,
"ARCHIVE": true,
"ARCHIVELOG": true,
"ARRAY": true,
"AS": true,
"ASC": true,
"AT": true,
"AUDIT": true,
"AUTHENTICATED": true,
"AUTHORIZATION": true,
"AUTOEXTEND": true,
"AUTOMATIC": true,
"BACKUP": true,
"BECOME": true,
"BEFORE": true,
"BEGIN": true,
"BETWEEN": true,
"BFILE": true,
"BITMAP": true,
"BLOB": true,
"BLOCK": true,
"BODY": true,
"BY": true,
"CACHE": true,
"CACHE_INSTANCES": true,
"CANCEL": true,
"CASCADE": true,
"CAST": true,
"CFILE": true,
"CHAINED": true,
"CHANGE": true,
"CHAR": true,
"CHAR_CS": true,
"CHARACTER": true,
"CHECK": true,
"CHECKPOINT": true,
"CHOOSE": true,
"CHUNK": true,
"CLEAR": true,
"CLOB": true,
"CLONE": true,
"CLOSE": true,
"CLOSE_CACHED_OPEN_CURSORS": true,
"CLUSTER": true,
"COALESCE": true,
"COLUMN": true,
"COLUMNS": true,
"COMMENT": true,
"COMMIT": true,
"COMMITTED": true,
"COMPATIBILITY": true,
"COMPILE": true,
"COMPLETE": true,
"COMPOSITE_LIMIT": true,
"COMPRESS": true,
"COMPUTE": true,
"CONNECT": true,
"CONNECT_TIME": true,
"CONSTRAINT": true,
"CONSTRAINTS": true,
"CONTENTS": true,
"CONTINUE": true,
"CONTROLFILE": true,
"CONVERT": true,
"COST": true,
"CPU_PER_CALL": true,
"CPU_PER_SESSION": true,
"CREATE": true,
"CURRENT": true,
"CURRENT_SCHEMA": true,
"CURREN_USER": true,
"CURSOR": true,
"CYCLE": true,
"DANGLING": true,
"DATABASE": true,
"DATAFILE": true,
"DATAFILES": true,
"DATAOBJNO": true,
"DATE": true,
"DBA": true,
"DBHIGH": true,
"DBLOW": true,
"DBMAC": true,
"DEALLOCATE": true,
"DEBUG": true,
"DEC": true,
"DECIMAL": true,
"DECLARE": true,
"DEFAULT": true,
"DEFERRABLE": true,
"DEFERRED": true,
"DEGREE": true,
"DELETE": true,
"DEREF": true,
"DESC": true,
"DIRECTORY": true,
"DISABLE": true,
"DISCONNECT": true,
"DISMOUNT": true,
"DISTINCT": true,
"DISTRIBUTED": true,
"DML": true,
"DOUBLE": true,
"DROP": true,
"DUMP": true,
"EACH": true,
"ELSE": true,
"ENABLE": true,
"END": true,
"ENFORCE": true,
"ENTRY": true,
"ESCAPE": true,
"EXCEPT": true,
"EXCEPTIONS": true,
"EXCHANGE": true,
"EXCLUDING": true,
"EXCLUSIVE": true,
"EXECUTE": true,
"EXISTS": true,
"EXPIRE": true,
"EXPLAIN": true,
"EXTENT": true,
"EXTENTS": true,
"EXTERNALLY": true,
"FAILED_LOGIN_ATTEMPTS": true,
"FALSE": true,
"FAST": true,
"FILE": true,
"FIRST_ROWS": true,
"FLAGGER": true,
"FLOAT": true,
"FLOB": true,
"FLUSH": true,
"FOR": true,
"FORCE": true,
"FOREIGN": true,
"FREELIST": true,
"FREELISTS": true,
"FROM": true,
"FULL": true,
"FUNCTION": true,
"GLOBAL": true,
"GLOBALLY": true,
"GLOBAL_NAME": true,
"GRANT": true,
"GROUP": true,
"GROUPS": true,
"HASH": true,
"HASHKEYS": true,
"HAVING": true,
"HEADER": true,
"HEAP": true,
"IDENTIFIED": true,
"IDGENERATORS": true,
"IDLE_TIME": true,
"IF": true,
"IMMEDIATE": true,
"IN": true,
"INCLUDING": true,
"INCREMENT": true,
"INDEX": true,
"INDEXED": true,
"INDEXES": true,
"INDICATOR": true,
"IND_PARTITION": true,
"INITIAL": true,
"INITIALLY": true,
"INITRANS": true,
"INSERT": true,
"INSTANCE": true,
"INSTANCES": true,
"INSTEAD": true,
"INT": true,
"INTEGER": true,
"INTERMEDIATE": true,
"INTERSECT": true,
"INTO": true,
"IS": true,
"ISOLATION": true,
"ISOLATION_LEVEL": true,
"KEEP": true,
"KEY": true,
"KILL": true,
"LABEL": true,
"LAYER": true,
"LESS": true,
"LEVEL": true,
"LIBRARY": true,
"LIKE": true,
"LIMIT": true,
"LINK": true,
"LIST": true,
"LOB": true,
"LOCAL": true,
"LOCK": true,
"LOCKED": true,
"LOG": true,
"LOGFILE": true,
"LOGGING": true,
"LOGICAL_READS_PER_CALL": true,
"LOGICAL_READS_PER_SESSION": true,
"LONG": true,
"MANAGE": true,
"MASTER": true,
"MAX": true,
"MAXARCHLOGS": true,
"MAXDATAFILES": true,
"MAXEXTENTS": true,
"MAXINSTANCES": true,
"MAXLOGFILES": true,
"MAXLOGHISTORY": true,
"MAXLOGMEMBERS": true,
"MAXSIZE": true,
"MAXTRANS": true,
"MAXVALUE": true,
"MIN": true,
"MEMBER": true,
"MINIMUM": true,
"MINEXTENTS": true,
"MINUS": true,
"MINVALUE": true,
"MLSLABEL": true,
"MLS_LABEL_FORMAT": true,
"MODE": true,
"MODIFY": true,
"MOUNT": true,
"MOVE": true,
"MTS_DISPATCHERS": true,
"MULTISET": true,
"NATIONAL": true,
"NCHAR": true,
"NCHAR_CS": true,
"NCLOB": true,
"NEEDED": true,
"NESTED": true,
"NETWORK": true,
"NEW": true,
"NEXT": true,
"NOARCHIVELOG": true,
"NOAUDIT": true,
"NOCACHE": true,
"NOCOMPRESS": true,
"NOCYCLE": true,
"NOFORCE": true,
"NOLOGGING": true,
"NOMAXVALUE": true,
"NOMINVALUE": true,
"NONE": true,
"NOORDER": true,
"NOOVERRIDE": true,
"NOPARALLEL": true,
"NOREVERSE": true,
"NORMAL": true,
"NOSORT": true,
"NOT": true,
"NOTHING": true,
"NOWAIT": true,
"NULL": true,
"NUMBER": true,
"NUMERIC": true,
"NVARCHAR2": true,
"OBJECT": true,
"OBJNO": true,
"OBJNO_REUSE": true,
"OF": true,
"OFF": true,
"OFFLINE": true,
"OID": true,
"OIDINDEX": true,
"OLD": true,
"ON": true,
"ONLINE": true,
"ONLY": true,
"OPCODE": true,
"OPEN": true,
"OPTIMAL": true,
"OPTIMIZER_GOAL": true,
"OPTION": true,
"OR": true,
"ORDER": true,
"ORGANIZATION": true,
"OSLABEL": true,
"OVERFLOW": true,
"OWN": true,
"PACKAGE": true,
"PARALLEL": true,
"PARTITION": true,
"PASSWORD": true,
"PASSWORD_GRACE_TIME": true,
"PASSWORD_LIFE_TIME": true,
"PASSWORD_LOCK_TIME": true,
"PASSWORD_REUSE_MAX": true,
"PASSWORD_REUSE_TIME": true,
"PASSWORD_VERIFY_FUNCTION": true,
"PCTFREE": true,
"PCTINCREASE": true,
"PCTTHRESHOLD": true,
"PCTUSED": true,
"PCTVERSION": true,
"PERCENT": true,
"PERMANENT": true,
"PLAN": true,
"PLSQL_DEBUG": true,
"POST_TRANSACTION": true,
"PRECISION": true,
"PRESERVE": true,
"PRIMARY": true,
"PRIOR": true,
"PRIVATE": true,
"PRIVATE_SGA": true,
"PRIVILEGE": true,
"PRIVILEGES": true,
"PROCEDURE": true,
"PROFILE": true,
"PUBLIC": true,
"PURGE": true,
"QUEUE": true,
"QUOTA": true,
"RANGE": true,
"RAW": true,
"RBA": true,
"READ": true,
"READUP": true,
"REAL": true,
"REBUILD": true,
"RECOVER": true,
"RECOVERABLE": true,
"RECOVERY": true,
"REF": true,
"REFERENCES": true,
"REFERENCING": true,
"REFRESH": true,
"RENAME": true,
"REPLACE": true,
"RESET": true,
"RESETLOGS": true,
"RESIZE": true,
"RESOURCE": true,
"RESTRICTED": true,
"RETURN": true,
"RETURNING": true,
"REUSE": true,
"REVERSE": true,
"REVOKE": true,
"ROLE": true,
"ROLES": true,
"ROLLBACK": true,
"ROW": true,
"ROWID": true,
"ROWNUM": true,
"ROWS": true,
"RULE": true,
"SAMPLE": true,
"SAVEPOINT": true,
"SB4": true,
"SCAN_INSTANCES": true,
"SCHEMA": true,
"SCN": true,
"SCOPE": true,
"SD_ALL": true,
"SD_INHIBIT": true,
"SD_SHOW": true,
"SEGMENT": true,
"SEG_BLOCK": true,
"SEG_FILE": true,
"SELECT": true,
"SEQUENCE": true,
"SERIALIZABLE": true,
"SESSION": true,
"SESSION_CACHED_CURSORS": true,
"SESSIONS_PER_USER": true,
"SET": true,
"SHARE": true,
"SHARED": true,
"SHARED_POOL": true,
"SHRINK": true,
"SIZE": true,
"SKIP": true,
"SKIP_UNUSABLE_INDEXES": true,
"SMALLINT": true,
"SNAPSHOT": true,
"SOME": true,
"SORT": true,
"SPECIFICATION": true,
"SPLIT": true,
"SQL_TRACE": true,
"STANDBY": true,
"START": true,
"STATEMENT_ID": true,
"STATISTICS": true,
"STOP": true,
"STORAGE": true,
"STORE": true,
"STRUCTURE": true,
"SUCCESSFUL": true,
"SWITCH": true,
"SYS_OP_ENFORCE_NOT_NULL$": true,
"SYS_OP_NTCIMG$": true,
"SYNONYM": true,
"SYSDATE": true,
"SYSDBA": true,
"SYSOPER": true,
"SYSTEM": true,
"TABLE": true,
"TABLES": true,
"TABLESPACE": true,
"TABLESPACE_NO": true,
"TABNO": true,
"TEMPORARY": true,
"THAN": true,
"THE": true,
"THEN": true,
"THREAD": true,
"TIMESTAMP": true,
"TIME": true,
"TO": true,
"TOPLEVEL": true,
"TRACE": true,
"TRACING": true,
"TRANSACTION": true,
"TRANSITIONAL": true,
"TRIGGER": true,
"TRIGGERS": true,
"TRUE": true,
"TRUNCATE": true,
"TX": true,
"TYPE": true,
"UB2": true,
"UBA": true,
"UID": true,
"UNARCHIVED": true,
"UNDO": true,
"UNION": true,
"UNIQUE": true,
"UNLIMITED": true,
"UNLOCK": true,
"UNRECOVERABLE": true,
"UNTIL": true,
"UNUSABLE": true,
"UNUSED": true,
"UPDATABLE": true,
"UPDATE": true,
"USAGE": true,
"USE": true,
"USER": true,
"USING": true,
"VALIDATE": true,
"VALIDATION": true,
"VALUE": true,
"VALUES": true,
"VARCHAR": true,
"VARCHAR2": true,
"VARYING": true,
"VIEW": true,
"WHEN": true,
"WHENEVER": true,
"WHERE": true,
"WITH": true,
"WITHOUT": true,
"WORK": true,
"WRITE": true,
"WRITEDOWN": true,
"WRITEUP": true,
"XID": true,
"YEAR": true,
"ZONE": true,
}
)
type oracle struct {
core.Base
}
func (db *oracle) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
return db.Base.Init(d, db, uri, drivername, dataSourceName)
}
func (db *oracle) SqlType(c *core.Column) string {
var res string
switch t := c.SQLType.Name; t {
case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool, core.Serial, core.BigSerial:
res = "NUMBER"
case core.Binary, core.VarBinary, core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob, core.Bytea:
return core.Blob
case core.Time, core.DateTime, core.TimeStamp:
res = core.TimeStamp
case core.TimeStampz:
res = "TIMESTAMP WITH TIME ZONE"
case core.Float, core.Double, core.Numeric, core.Decimal:
res = "NUMBER"
case core.Text, core.MediumText, core.LongText, core.Json:
res = "CLOB"
case core.Char, core.Varchar, core.TinyText:
res = "VARCHAR2"
default:
res = t
}
hasLen1 := (c.Length > 0)
hasLen2 := (c.Length2 > 0)
if hasLen2 {
res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
} else if hasLen1 {
res += "(" + strconv.Itoa(c.Length) + ")"
}
return res
}
func (db *oracle) AutoIncrStr() string {
return "AUTO_INCREMENT"
}
func (db *oracle) SupportInsertMany() bool {
return true
}
func (db *oracle) IsReserved(name string) bool {
_, ok := oracleReservedWords[name]
return ok
}
func (db *oracle) Quote(name string) string {
return "[" + name + "]"
}
func (db *oracle) SupportEngine() bool {
return false
}
func (db *oracle) SupportCharset() bool {
return false
}
func (db *oracle) SupportDropIfExists() bool {
return false
}
func (db *oracle) IndexOnTable() bool {
return false
}
func (db *oracle) DropTableSql(tableName string) string {
return fmt.Sprintf("DROP TABLE `%s`", tableName)
}
func (db *oracle) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string {
var sql string
sql = "CREATE TABLE "
if tableName == "" {
tableName = table.Name
}
sql += db.Quote(tableName) + " ("
pkList := table.PrimaryKeys
for _, colName := range table.ColumnsSeq() {
col := table.GetColumn(colName)
/*if col.IsPrimaryKey && len(pkList) == 1 {
sql += col.String(b.dialect)
} else {*/
sql += col.StringNoPk(db)
// }
sql = strings.TrimSpace(sql)
sql += ", "
}
if len(pkList) > 0 {
sql += "PRIMARY KEY ( "
sql += db.Quote(strings.Join(pkList, db.Quote(",")))
sql += " ), "
}
sql = sql[:len(sql)-2] + ")"
if db.SupportEngine() && storeEngine != "" {
sql += " ENGINE=" + storeEngine
}
if db.SupportCharset() {
if len(charset) == 0 {
charset = db.URI().Charset
}
if len(charset) > 0 {
sql += " DEFAULT CHARSET " + charset
}
}
return sql
}
func (db *oracle) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
args := []interface{}{tableName, idxName}
return `SELECT INDEX_NAME FROM USER_INDEXES ` +
`WHERE TABLE_NAME = :1 AND INDEX_NAME = :2`, args
}
func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) {
args := []interface{}{tableName}
return `SELECT table_name FROM user_tables WHERE table_name = :1`, args
}
func (db *oracle) MustDropTable(tableName string) error {
sql, args := db.TableCheckSql(tableName)
db.LogSQL(sql, args)
rows, err := db.DB().Query(sql, args...)
if err != nil {
return err
}
defer rows.Close()
if !rows.Next() {
return nil
}
sql = "Drop Table \"" + tableName + "\""
db.LogSQL(sql, args)
_, err = db.DB().Exec(sql)
return err
}
/*func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(colName)}
return "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" +
" AND column_name = ?", args
}*/
func (db *oracle) IsColumnExist(tableName, colName string) (bool, error) {
args := []interface{}{tableName, colName}
query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = :1" +
" AND column_name = :2"
db.LogSQL(query, args)
rows, err := db.DB().Query(query, args...)
if err != nil {
return false, err
}
defer rows.Close()
if rows.Next() {
return true, nil
}
return false, nil
}
func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
args := []interface{}{tableName}
s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," +
"nullable FROM USER_TAB_COLUMNS WHERE table_name = :1"
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, nil, err
}
defer rows.Close()
cols := make(map[string]*core.Column)
colSeq := make([]string, 0)
for rows.Next() {
col := new(core.Column)
col.Indexes = make(map[string]int)
var colName, colDefault, nullable, dataType, dataPrecision, dataScale *string
var dataLen int
err = rows.Scan(&colName, &colDefault, &dataType, &dataLen, &dataPrecision,
&dataScale, &nullable)
if err != nil {
return nil, nil, err
}
col.Name = strings.Trim(*colName, `" `)
if colDefault != nil {
col.Default = *colDefault
col.DefaultIsEmpty = false
}
if *nullable == "Y" {
col.Nullable = true
} else {
col.Nullable = false
}
var ignore bool
var dt string
var len1, len2 int
dts := strings.Split(*dataType, "(")
dt = dts[0]
if len(dts) > 1 {
lens := strings.Split(dts[1][:len(dts[1])-1], ",")
if len(lens) > 1 {
len1, _ = strconv.Atoi(lens[0])
len2, _ = strconv.Atoi(lens[1])
} else {
len1, _ = strconv.Atoi(lens[0])
}
}
switch dt {
case "VARCHAR2":
col.SQLType = core.SQLType{Name: core.Varchar, DefaultLength: len1, DefaultLength2: len2}
case "NVARCHAR2":
col.SQLType = core.SQLType{Name: core.NVarchar, DefaultLength: len1, DefaultLength2: len2}
case "TIMESTAMP WITH TIME ZONE":
col.SQLType = core.SQLType{Name: core.TimeStampz, DefaultLength: 0, DefaultLength2: 0}
case "NUMBER":
col.SQLType = core.SQLType{Name: core.Double, DefaultLength: len1, DefaultLength2: len2}
case "LONG", "LONG RAW":
col.SQLType = core.SQLType{Name: core.Text, DefaultLength: 0, DefaultLength2: 0}
case "RAW":
col.SQLType = core.SQLType{Name: core.Binary, DefaultLength: 0, DefaultLength2: 0}
case "ROWID":
col.SQLType = core.SQLType{Name: core.Varchar, DefaultLength: 18, DefaultLength2: 0}
case "AQ$_SUBSCRIBERS":
ignore = true
default:
col.SQLType = core.SQLType{Name: strings.ToUpper(dt), DefaultLength: len1, DefaultLength2: len2}
}
if ignore {
continue
}
if _, ok := core.SqlTypes[col.SQLType.Name]; !ok {
return nil, nil, fmt.Errorf("Unknown colType %v %v", *dataType, col.SQLType)
}
col.Length = dataLen
if col.SQLType.IsText() || col.SQLType.IsTime() {
if !col.DefaultIsEmpty {
col.Default = "'" + col.Default + "'"
}
}
cols[col.Name] = col
colSeq = append(colSeq, col.Name)
}
return colSeq, cols, nil
}
func (db *oracle) GetTables() ([]*core.Table, error) {
args := []interface{}{}
s := "SELECT table_name FROM user_tables"
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, err
}
defer rows.Close()
tables := make([]*core.Table, 0)
for rows.Next() {
table := core.NewEmptyTable()
err = rows.Scan(&table.Name)
if err != nil {
return nil, err
}
tables = append(tables, table)
}
return tables, nil
}
func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
args := []interface{}{tableName}
s := "SELECT t.column_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " +
"WHERE t.index_name = i.index_name and t.table_name = i.table_name and t.table_name =:1"
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, err
}
defer rows.Close()
indexes := make(map[string]*core.Index, 0)
for rows.Next() {
var indexType int
var indexName, colName, uniqueness string
err = rows.Scan(&colName, &uniqueness, &indexName)
if err != nil {
return nil, err
}
indexName = strings.Trim(indexName, `" `)
var isRegular bool
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
indexName = indexName[5+len(tableName):]
isRegular = true
}
if uniqueness == "UNIQUE" {
indexType = core.UniqueType
} else {
indexType = core.IndexType
}
var index *core.Index
var ok bool
if index, ok = indexes[indexName]; !ok {
index = new(core.Index)
index.Type = indexType
index.Name = indexName
index.IsRegular = isRegular
indexes[indexName] = index
}
index.AddColumn(colName)
}
return indexes, nil
}
func (db *oracle) Filters() []core.Filter {
return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{Prefix: ":", Start: 1}, &core.IdFilter{}}
}
type goracleDriver struct {
}
func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
db := &core.Uri{DbType: core.ORACLE}
dsnPattern := regexp.MustCompile(
`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
`\/(?P<dbname>.*?)` + // /dbname
`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
matches := dsnPattern.FindStringSubmatch(dataSourceName)
// tlsConfigRegister := make(map[string]*tls.Config)
names := dsnPattern.SubexpNames()
for i, match := range matches {
switch names[i] {
case "dbname":
db.DbName = match
}
}
if db.DbName == "" {
return nil, errors.New("dbname is empty")
}
return db, nil
}
type oci8Driver struct {
}
// dataSourceName=user/password@ipv4:port/dbname
// dataSourceName=user/password@[ipv6]:port/dbname
func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
db := &core.Uri{DbType: core.ORACLE}
dsnPattern := regexp.MustCompile(
`^(?P<user>.*)\/(?P<password>.*)@` + // user:password@
`(?P<net>.*)` + // ip:port
`\/(?P<dbname>.*)`) // dbname
matches := dsnPattern.FindStringSubmatch(dataSourceName)
names := dsnPattern.SubexpNames()
for i, match := range matches {
switch names[i] {
case "dbname":
db.DbName = match
}
}
if db.DbName == "" {
return nil, errors.New("dbname is empty")
}
return db, nil
}

View File

@ -1,492 +0,0 @@
// Copyright 2015 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 (
"database/sql"
"errors"
"fmt"
"regexp"
"strings"
"xorm.io/core"
)
var (
sqlite3ReservedWords = map[string]bool{
"ABORT": true,
"ACTION": true,
"ADD": true,
"AFTER": true,
"ALL": true,
"ALTER": true,
"ANALYZE": true,
"AND": true,
"AS": true,
"ASC": true,
"ATTACH": true,
"AUTOINCREMENT": true,
"BEFORE": true,
"BEGIN": true,
"BETWEEN": true,
"BY": true,
"CASCADE": true,
"CASE": true,
"CAST": true,
"CHECK": true,
"COLLATE": true,
"COLUMN": true,
"COMMIT": true,
"CONFLICT": true,
"CONSTRAINT": true,
"CREATE": true,
"CROSS": true,
"CURRENT_DATE": true,
"CURRENT_TIME": true,
"CURRENT_TIMESTAMP": true,
"DATABASE": true,
"DEFAULT": true,
"DEFERRABLE": true,
"DEFERRED": true,
"DELETE": true,
"DESC": true,
"DETACH": true,
"DISTINCT": true,
"DROP": true,
"EACH": true,
"ELSE": true,
"END": true,
"ESCAPE": true,
"EXCEPT": true,
"EXCLUSIVE": true,
"EXISTS": true,
"EXPLAIN": true,
"FAIL": true,
"FOR": true,
"FOREIGN": true,
"FROM": true,
"FULL": true,
"GLOB": true,
"GROUP": true,
"HAVING": true,
"IF": true,
"IGNORE": true,
"IMMEDIATE": true,
"IN": true,
"INDEX": true,
"INDEXED": true,
"INITIALLY": true,
"INNER": true,
"INSERT": true,
"INSTEAD": true,
"INTERSECT": true,
"INTO": true,
"IS": true,
"ISNULL": true,
"JOIN": true,
"KEY": true,
"LEFT": true,
"LIKE": true,
"LIMIT": true,
"MATCH": true,
"NATURAL": true,
"NO": true,
"NOT": true,
"NOTNULL": true,
"NULL": true,
"OF": true,
"OFFSET": true,
"ON": true,
"OR": true,
"ORDER": true,
"OUTER": true,
"PLAN": true,
"PRAGMA": true,
"PRIMARY": true,
"QUERY": true,
"RAISE": true,
"RECURSIVE": true,
"REFERENCES": true,
"REGEXP": true,
"REINDEX": true,
"RELEASE": true,
"RENAME": true,
"REPLACE": true,
"RESTRICT": true,
"RIGHT": true,
"ROLLBACK": true,
"ROW": true,
"SAVEPOINT": true,
"SELECT": true,
"SET": true,
"TABLE": true,
"TEMP": true,
"TEMPORARY": true,
"THEN": true,
"TO": true,
"TRANSACTI": true,
"TRIGGER": true,
"UNION": true,
"UNIQUE": true,
"UPDATE": true,
"USING": true,
"VACUUM": true,
"VALUES": true,
"VIEW": true,
"VIRTUAL": true,
"WHEN": true,
"WHERE": true,
"WITH": true,
"WITHOUT": true,
}
)
type sqlite3 struct {
core.Base
}
func (db *sqlite3) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
return db.Base.Init(d, db, uri, drivername, dataSourceName)
}
func (db *sqlite3) SqlType(c *core.Column) string {
switch t := c.SQLType.Name; t {
case core.Bool:
if c.Default == "true" {
c.Default = "1"
} else if c.Default == "false" {
c.Default = "0"
}
return core.Integer
case core.Date, core.DateTime, core.TimeStamp, core.Time:
return core.DateTime
case core.TimeStampz:
return core.Text
case core.Char, core.Varchar, core.NVarchar, core.TinyText,
core.Text, core.MediumText, core.LongText, core.Json:
return core.Text
case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt:
return core.Integer
case core.Float, core.Double, core.Real:
return core.Real
case core.Decimal, core.Numeric:
return core.Numeric
case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea, core.Binary, core.VarBinary:
return core.Blob
case core.Serial, core.BigSerial:
c.IsPrimaryKey = true
c.IsAutoIncrement = true
c.Nullable = false
return core.Integer
default:
return t
}
}
func (db *sqlite3) FormatBytes(bs []byte) string {
return fmt.Sprintf("X'%x'", bs)
}
func (db *sqlite3) SupportInsertMany() bool {
return true
}
func (db *sqlite3) IsReserved(name string) bool {
_, ok := sqlite3ReservedWords[name]
return ok
}
func (db *sqlite3) Quote(name string) string {
return "`" + name + "`"
}
func (db *sqlite3) AutoIncrStr() string {
return "AUTOINCREMENT"
}
func (db *sqlite3) SupportEngine() bool {
return false
}
func (db *sqlite3) SupportCharset() bool {
return false
}
func (db *sqlite3) IndexOnTable() bool {
return false
}
func (db *sqlite3) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
args := []interface{}{idxName}
return "SELECT name FROM sqlite_master WHERE type='index' and name = ?", args
}
func (db *sqlite3) TableCheckSql(tableName string) (string, []interface{}) {
args := []interface{}{tableName}
return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
}
func (db *sqlite3) DropIndexSql(tableName string, index *core.Index) string {
// var unique string
quote := db.Quote
idxName := index.Name
if !strings.HasPrefix(idxName, "UQE_") &&
!strings.HasPrefix(idxName, "IDX_") {
if index.Type == core.UniqueType {
idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
} else {
idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
}
}
return fmt.Sprintf("DROP INDEX %v", quote(idxName))
}
func (db *sqlite3) ForUpdateSql(query string) string {
return query
}
/*func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
args := []interface{}{tableName}
sql := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
return sql, args
}*/
func (db *sqlite3) IsColumnExist(tableName, colName string) (bool, error) {
args := []interface{}{tableName}
query := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
db.LogSQL(query, args)
rows, err := db.DB().Query(query, args...)
if err != nil {
return false, err
}
defer rows.Close()
if rows.Next() {
return true, nil
}
return false, nil
}
// splitColStr splits a sqlite col strings as fields
func splitColStr(colStr string) []string {
colStr = strings.TrimSpace(colStr)
var results = make([]string, 0, 10)
var lastIdx int
var hasC, hasQuote bool
for i, c := range colStr {
if c == ' ' && !hasQuote {
if hasC {
results = append(results, colStr[lastIdx:i])
hasC = false
}
} else {
if c == '\'' {
hasQuote = !hasQuote
}
if !hasC {
lastIdx = i
}
hasC = true
if i == len(colStr)-1 {
results = append(results, colStr[lastIdx:i+1])
}
}
}
return results
}
func parseString(colStr string) (*core.Column, error) {
fields := splitColStr(colStr)
col := new(core.Column)
col.Indexes = make(map[string]int)
col.Nullable = true
col.DefaultIsEmpty = true
for idx, field := range fields {
if idx == 0 {
col.Name = strings.Trim(strings.Trim(field, "`[] "), `"`)
continue
} else if idx == 1 {
col.SQLType = core.SQLType{Name: field, DefaultLength: 0, DefaultLength2: 0}
continue
}
switch field {
case "PRIMARY":
col.IsPrimaryKey = true
case "AUTOINCREMENT":
col.IsAutoIncrement = true
case "NULL":
if fields[idx-1] == "NOT" {
col.Nullable = false
} else {
col.Nullable = true
}
case "DEFAULT":
col.Default = fields[idx+1]
col.DefaultIsEmpty = false
}
}
return col, nil
}
func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
args := []interface{}{tableName}
s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?"
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, nil, err
}
defer rows.Close()
var name string
for rows.Next() {
err = rows.Scan(&name)
if err != nil {
return nil, nil, err
}
break
}
if name == "" {
return nil, nil, errors.New("no table named " + tableName)
}
nStart := strings.Index(name, "(")
nEnd := strings.LastIndex(name, ")")
reg := regexp.MustCompile(`[^\(,\)]*(\([^\(]*\))?`)
colCreates := reg.FindAllString(name[nStart+1:nEnd], -1)
cols := make(map[string]*core.Column)
colSeq := make([]string, 0)
for _, colStr := range colCreates {
reg = regexp.MustCompile(`,\s`)
colStr = reg.ReplaceAllString(colStr, ",")
if strings.HasPrefix(strings.TrimSpace(colStr), "PRIMARY KEY") {
parts := strings.Split(strings.TrimSpace(colStr), "(")
if len(parts) == 2 {
pkCols := strings.Split(strings.TrimRight(strings.TrimSpace(parts[1]), ")"), ",")
for _, pk := range pkCols {
if col, ok := cols[strings.Trim(strings.TrimSpace(pk), "`")]; ok {
col.IsPrimaryKey = true
}
}
}
continue
}
col, err := parseString(colStr)
if err != nil {
return colSeq, cols, err
}
cols[col.Name] = col
colSeq = append(colSeq, col.Name)
}
return colSeq, cols, nil
}
func (db *sqlite3) GetTables() ([]*core.Table, error) {
args := []interface{}{}
s := "SELECT name FROM sqlite_master WHERE type='table'"
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, err
}
defer rows.Close()
tables := make([]*core.Table, 0)
for rows.Next() {
table := core.NewEmptyTable()
err = rows.Scan(&table.Name)
if err != nil {
return nil, err
}
if table.Name == "sqlite_sequence" {
continue
}
tables = append(tables, table)
}
return tables, nil
}
func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) {
args := []interface{}{tableName}
s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?"
db.LogSQL(s, args)
rows, err := db.DB().Query(s, args...)
if err != nil {
return nil, err
}
defer rows.Close()
indexes := make(map[string]*core.Index, 0)
for rows.Next() {
var tmpSQL sql.NullString
err = rows.Scan(&tmpSQL)
if err != nil {
return nil, err
}
if !tmpSQL.Valid {
continue
}
sql := tmpSQL.String
index := new(core.Index)
nNStart := strings.Index(sql, "INDEX")
nNEnd := strings.Index(sql, "ON")
if nNStart == -1 || nNEnd == -1 {
continue
}
indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []")
var isRegular bool
if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
index.Name = indexName[5+len(tableName):]
isRegular = true
} else {
index.Name = indexName
}
if strings.HasPrefix(sql, "CREATE UNIQUE INDEX") {
index.Type = core.UniqueType
} else {
index.Type = core.IndexType
}
nStart := strings.Index(sql, "(")
nEnd := strings.Index(sql, ")")
colIndexes := strings.Split(sql[nStart+1:nEnd], ",")
index.Cols = make([]string, 0)
for _, col := range colIndexes {
index.Cols = append(index.Cols, strings.Trim(col, "` []"))
}
index.IsRegular = isRegular
indexes[index.Name] = index
}
return indexes, nil
}
func (db *sqlite3) Filters() []core.Filter {
return []core.Filter{&core.IdFilter{}}
}
type sqlite3Driver struct {
}
func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
if strings.Contains(dataSourceName, "?") {
dataSourceName = dataSourceName[:strings.Index(dataSourceName, "?")]
}
return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil
}

View File

@ -1,184 +0,0 @@
// Copyright 2013 - 2016 The XORM Authors. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.
/*
Package xorm is a simple and powerful ORM for Go.
Installation
Make sure you have installed Go 1.6+ and then:
go get github.com/go-xorm/xorm
Create Engine
Firstly, we should new an engine for a database
engine, err := xorm.NewEngine(driverName, dataSourceName)
Method NewEngine's parameters is the same as sql.Open. It depends
drivers' implementation.
Generally, one engine for an application is enough. You can set it as package variable.
Raw Methods
XORM also support raw SQL execution:
1. query a SQL string, the returned results is []map[string][]byte
results, err := engine.Query("select * from user")
2. execute a SQL string, the returned results
affected, err := engine.Exec("update user set .... where ...")
ORM Methods
There are 8 major ORM methods and many helpful methods to use to operate database.
1. Insert one or multiple records to database
affected, err := engine.Insert(&struct)
// INSERT INTO struct () values ()
affected, err := engine.Insert(&struct1, &struct2)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values ()
affected, err := engine.Insert(&sliceOfStruct)
// INSERT INTO struct () values (),(),()
affected, err := engine.Insert(&struct1, &sliceOfStruct2)
// INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values (),(),()
2. Query one record or one variable from database
has, err := engine.Get(&user)
// SELECT * FROM user LIMIT 1
var id int64
has, err := engine.Table("user").Where("name = ?", name).Get(&id)
// SELECT id FROM user WHERE name = ? LIMIT 1
3. Query multiple records from database
var sliceOfStructs []Struct
err := engine.Find(&sliceOfStructs)
// SELECT * FROM user
var mapOfStructs = make(map[int64]Struct)
err := engine.Find(&mapOfStructs)
// SELECT * FROM user
var int64s []int64
err := engine.Table("user").Cols("id").Find(&int64s)
// SELECT id FROM user
4. Query multiple records and record by record handle, there two methods, one is Iterate,
another is Rows
err := engine.Iterate(...)
// SELECT * FROM user
rows, err := engine.Rows(...)
// SELECT * FROM user
defer rows.Close()
bean := new(Struct)
for rows.Next() {
err = rows.Scan(bean)
}
5. Update one or more records
affected, err := engine.ID(...).Update(&user)
// UPDATE user SET ...
6. Delete one or more records, Delete MUST has condition
affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ...
7. Count records
counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user
counts, err := engine.SQL("select count(*) FROM user").Count()
// select count(*) FROM user
8. Sum records
sumFloat64, err := engine.Sum(&user, "id")
// SELECT sum(id) from user
sumFloat64s, err := engine.Sums(&user, "id1", "id2")
// SELECT sum(id1), sum(id2) from user
sumInt64s, err := engine.SumsInt(&user, "id1", "id2")
// SELECT sum(id1), sum(id2) from user
Conditions
The above 8 methods could use with condition methods chainable.
Attention: the above 8 methods should be the last chainable method.
1. ID, In
engine.ID(1).Get(&user) // for single primary key
// SELECT * FROM user WHERE id = 1
engine.ID(core.PK{1, 2}).Get(&user) // for composite primary keys
// SELECT * FROM user WHERE id1 = 1 AND id2 = 2
engine.In("id", 1, 2, 3).Find(&users)
// SELECT * FROM user WHERE id IN (1, 2, 3)
engine.In("id", []int{1, 2, 3}).Find(&users)
// SELECT * FROM user WHERE id IN (1, 2, 3)
2. Where, And, Or
engine.Where().And().Or().Find()
// SELECT * FROM user WHERE (.. AND ..) OR ...
3. OrderBy, Asc, Desc
engine.Asc().Desc().Find()
// SELECT * FROM user ORDER BY .. ASC, .. DESC
engine.OrderBy().Find()
// SELECT * FROM user ORDER BY ..
4. Limit, Top
engine.Limit().Find()
// SELECT * FROM user LIMIT .. OFFSET ..
engine.Top(5).Find()
// SELECT TOP 5 * FROM user // for mssql
// SELECT * FROM user LIMIT .. OFFSET 0 //for other databases
5. SQL, let you custom SQL
var users []User
engine.SQL("select * from user").Find(&users)
6. Cols, Omit, Distinct
var users []*User
engine.Cols("col1, col2").Find(&users)
// SELECT col1, col2 FROM user
engine.Cols("col1", "col2").Where().Update(user)
// UPDATE user set col1 = ?, col2 = ? Where ...
engine.Omit("col1").Find(&users)
// SELECT col2, col3 FROM user
engine.Omit("col1").Insert(&user)
// INSERT INTO table (non-col1) VALUES ()
engine.Distinct("col1").Find(&users)
// SELECT DISTINCT col1 FROM user
7. Join, GroupBy, Having
engine.GroupBy("name").Having("name='xlw'").Find(&users)
//SELECT * FROM user GROUP BY name HAVING name='xlw'
engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users)
//SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id
More usage, please visit http://xorm.io/docs
*/
package xorm

View File

@ -1,232 +0,0 @@
// 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
import (
"database/sql/driver"
"fmt"
"reflect"
"strings"
"time"
"xorm.io/builder"
"xorm.io/core"
)
func (engine *Engine) buildConds(table *core.Table, bean interface{},
includeVersion bool, includeUpdated bool, includeNil bool,
includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool,
mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) (builder.Cond, error) {
var conds []builder.Cond
for _, col := range table.Columns() {
if !includeVersion && col.IsVersion {
continue
}
if !includeUpdated && col.IsUpdated {
continue
}
if !includeAutoIncr && col.IsAutoIncrement {
continue
}
if engine.dialect.DBType() == core.MSSQL && (col.SQLType.Name == core.Text || col.SQLType.IsBlob() || col.SQLType.Name == core.TimeStampz) {
continue
}
if col.SQLType.IsJson() {
continue
}
var colName string
if addedTableName {
var nm = tableName
if len(aliasName) > 0 {
nm = aliasName
}
colName = engine.Quote(nm) + "." + engine.Quote(col.Name)
} else {
colName = engine.Quote(col.Name)
}
fieldValuePtr, err := col.ValueOf(bean)
if err != nil {
if !strings.Contains(err.Error(), "is not valid") {
engine.logger.Warn(err)
}
continue
}
if col.IsDeleted && !unscoped { // tag "deleted" is enabled
conds = append(conds, engine.CondDeleted(colName))
}
fieldValue := *fieldValuePtr
if fieldValue.Interface() == nil {
continue
}
fieldType := reflect.TypeOf(fieldValue.Interface())
requiredField := useAllCols
if b, ok := getFlagForColumn(mustColumnMap, col); ok {
if b {
requiredField = true
} else {
continue
}
}
if fieldType.Kind() == reflect.Ptr {
if fieldValue.IsNil() {
if includeNil {
conds = append(conds, builder.Eq{colName: nil})
}
continue
} else if !fieldValue.IsValid() {
continue
} else {
// dereference ptr type to instance type
fieldValue = fieldValue.Elem()
fieldType = reflect.TypeOf(fieldValue.Interface())
requiredField = true
}
}
var val interface{}
switch fieldType.Kind() {
case reflect.Bool:
if allUseBool || requiredField {
val = fieldValue.Interface()
} else {
// if a bool in a struct, it will not be as a condition because it default is false,
// please use Where() instead
continue
}
case reflect.String:
if !requiredField && fieldValue.String() == "" {
continue
}
// for MyString, should convert to string or panic
if fieldType.String() != reflect.String.String() {
val = fieldValue.String()
} else {
val = fieldValue.Interface()
}
case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
if !requiredField && fieldValue.Int() == 0 {
continue
}
val = fieldValue.Interface()
case reflect.Float32, reflect.Float64:
if !requiredField && fieldValue.Float() == 0.0 {
continue
}
val = fieldValue.Interface()
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
if !requiredField && fieldValue.Uint() == 0 {
continue
}
t := int64(fieldValue.Uint())
val = reflect.ValueOf(&t).Interface()
case reflect.Struct:
if fieldType.ConvertibleTo(core.TimeType) {
t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
continue
}
val = engine.formatColTime(col, t)
} else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok {
continue
} else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
val, _ = valNul.Value()
if val == nil {
continue
}
} else {
if col.SQLType.IsJson() {
if col.SQLType.IsText() {
bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface())
if err != nil {
engine.logger.Error(err)
continue
}
val = string(bytes)
} else if col.SQLType.IsBlob() {
var bytes []byte
var err error
bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface())
if err != nil {
engine.logger.Error(err)
continue
}
val = bytes
}
} else {
engine.autoMapType(fieldValue)
if table, ok := engine.Tables[fieldValue.Type()]; ok {
if len(table.PrimaryKeys) == 1 {
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
// fix non-int pk issues
//if pkField.Int() != 0 {
if pkField.IsValid() && !isZero(pkField.Interface()) {
val = pkField.Interface()
} else {
continue
}
} else {
//TODO: how to handler?
return nil, fmt.Errorf("not supported %v as %v", fieldValue.Interface(), table.PrimaryKeys)
}
} else {
val = fieldValue.Interface()
}
}
}
case reflect.Array:
continue
case reflect.Slice, reflect.Map:
if fieldValue == reflect.Zero(fieldType) {
continue
}
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
continue
}
if col.SQLType.IsText() {
bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface())
if err != nil {
engine.logger.Error(err)
continue
}
val = string(bytes)
} else if col.SQLType.IsBlob() {
var bytes []byte
var err error
if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
fieldType.Elem().Kind() == reflect.Uint8 {
if fieldValue.Len() > 0 {
val = fieldValue.Bytes()
} else {
continue
}
} else {
bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface())
if err != nil {
engine.logger.Error(err)
continue
}
val = bytes
}
} else {
continue
}
default:
val = fieldValue.Interface()
}
conds = append(conds, builder.Eq{colName: val})
}
return builder.And(conds...), nil
}

View File

@ -1,28 +0,0 @@
// 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)
}

View File

@ -1,219 +0,0 @@
// 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
import (
"context"
"time"
"xorm.io/core"
)
// EngineGroup defines an engine group
type EngineGroup struct {
*Engine
slaves []*Engine
policy GroupPolicy
}
// NewEngineGroup creates a new engine group
func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error) {
var eg EngineGroup
if len(policies) > 0 {
eg.policy = policies[0]
} else {
eg.policy = RoundRobinPolicy()
}
driverName, ok1 := args1.(string)
conns, ok2 := args2.([]string)
if ok1 && ok2 {
engines := make([]*Engine, len(conns))
for i, conn := range conns {
engine, err := NewEngine(driverName, conn)
if err != nil {
return nil, err
}
engine.engineGroup = &eg
engines[i] = engine
}
eg.Engine = engines[0]
eg.slaves = engines[1:]
return &eg, nil
}
master, ok3 := args1.(*Engine)
slaves, ok4 := args2.([]*Engine)
if ok3 && ok4 {
master.engineGroup = &eg
for i := 0; i < len(slaves); i++ {
slaves[i].engineGroup = &eg
}
eg.Engine = master
eg.slaves = slaves
return &eg, nil
}
return nil, ErrParamsType
}
// Close the engine
func (eg *EngineGroup) Close() error {
err := eg.Engine.Close()
if err != nil {
return err
}
for i := 0; i < len(eg.slaves); i++ {
err := eg.slaves[i].Close()
if err != nil {
return err
}
}
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
func (eg *EngineGroup) Master() *Engine {
return eg.Engine
}
// Ping tests if database is alive
func (eg *EngineGroup) Ping() error {
if err := eg.Engine.Ping(); err != nil {
return err
}
for _, slave := range eg.slaves {
if err := slave.Ping(); err != nil {
return err
}
}
return nil
}
// SetColumnMapper set the column name mapping rule
func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) {
eg.Engine.ColumnMapper = mapper
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].ColumnMapper = mapper
}
}
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) {
eg.Engine.SetConnMaxLifetime(d)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].SetConnMaxLifetime(d)
}
}
// SetDefaultCacher set the default cacher
func (eg *EngineGroup) SetDefaultCacher(cacher core.Cacher) {
eg.Engine.SetDefaultCacher(cacher)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].SetDefaultCacher(cacher)
}
}
// SetLogger set the new logger
func (eg *EngineGroup) SetLogger(logger core.ILogger) {
eg.Engine.SetLogger(logger)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].SetLogger(logger)
}
}
// SetLogLevel sets the logger level
func (eg *EngineGroup) SetLogLevel(level core.LogLevel) {
eg.Engine.SetLogLevel(level)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].SetLogLevel(level)
}
}
// SetMapper set the name mapping rules
func (eg *EngineGroup) SetMapper(mapper core.IMapper) {
eg.Engine.SetMapper(mapper)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].SetMapper(mapper)
}
}
// SetMaxIdleConns set the max idle connections on pool, default is 2
func (eg *EngineGroup) SetMaxIdleConns(conns int) {
eg.Engine.db.SetMaxIdleConns(conns)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].db.SetMaxIdleConns(conns)
}
}
// SetMaxOpenConns is only available for go 1.2+
func (eg *EngineGroup) SetMaxOpenConns(conns int) {
eg.Engine.db.SetMaxOpenConns(conns)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].db.SetMaxOpenConns(conns)
}
}
// SetPolicy set the group policy
func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup {
eg.policy = policy
return eg
}
// SetTableMapper set the table name mapping rule
func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) {
eg.Engine.TableMapper = mapper
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].TableMapper = mapper
}
}
// ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO
func (eg *EngineGroup) ShowExecTime(show ...bool) {
eg.Engine.ShowExecTime(show...)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].ShowExecTime(show...)
}
}
// ShowSQL show SQL statement or not on logger if log level is great than INFO
func (eg *EngineGroup) ShowSQL(show ...bool) {
eg.Engine.ShowSQL(show...)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].ShowSQL(show...)
}
}
// Slave returns one of the physical databases which is a slave according the policy
func (eg *EngineGroup) Slave() *Engine {
switch len(eg.slaves) {
case 0:
return eg.Engine
case 1:
return eg.slaves[0]
}
return eg.policy.Slave(eg)
}
// Slaves returns all the slaves
func (eg *EngineGroup) Slaves() []*Engine {
return eg.slaves
}

Some files were not shown because too many files have changed in this diff Show More