@ -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)
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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.
|
||||
@ -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-----
|
||||
@ -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
|
||||
@ -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-----
|
||||
@ -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-----
|
||||
@ -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
|
||||
@ -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-----
|
||||
@ -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"
|
||||
@ -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-----
|
||||
@ -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-----
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
@ -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 ""
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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
|
||||
@ -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=
|
||||
@ -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"))
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
PASS
|
||||
@ -1 +0,0 @@
|
||||
PASS
|
||||
@ -1 +0,0 @@
|
||||
PASS
|
||||
@ -1 +0,0 @@
|
||||
PASS
|
||||
@ -1,3 +0,0 @@
|
||||
count: 10
|
||||
count: 10
|
||||
PASS
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1 +0,0 @@
|
||||
PASS
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1,4 +0,0 @@
|
||||
name: abc
|
||||
id: 1
|
||||
name: abc
|
||||
PASS
|
||||
@ -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
|
||||
@ -1 +0,0 @@
|
||||
PASS
|
||||
@ -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
|
||||
@ -1,9 +0,0 @@
|
||||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@ -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
|
||||
@ -1 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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.
|
||||
@ -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 🌟
|
||||
@ -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
|
||||
@ -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 }}
|
||||
@ -1,7 +0,0 @@
|
||||
testdata/conf_out.ini
|
||||
ini.sublime-project
|
||||
ini.sublime-workspace
|
||||
testdata/conf_reflect.ini
|
||||
.idea
|
||||
/.vscode
|
||||
.DS_Store
|
||||
@ -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
|
||||
@ -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.
|
||||
@ -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
|
||||
@ -1,43 +0,0 @@
|
||||
# INI
|
||||
|
||||
[](https://github.com/go-ini/ini/actions?query=workflow%3AGo)
|
||||
[](https://codecov.io/gh/go-ini/ini)
|
||||
[](https://pkg.go.dev/github.com/go-ini/ini?tab=doc)
|
||||
[](https://sourcegraph.com/github.com/go-ini/ini)
|
||||
|
||||

|
||||
|
||||
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.
|
||||
@ -1,9 +0,0 @@
|
||||
coverage:
|
||||
range: "60...95"
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 1%
|
||||
|
||||
comment:
|
||||
layout: 'diff'
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
)
|
||||
@ -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)
|
||||
}
|
||||
@ -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, "")
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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...)
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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)
|
||||
@ -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
|
||||
@ -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/
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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.
|
||||
|
||||
[](https://circleci.com/gh/go-xorm/xorm) [](https://codecov.io/gh/go-xorm/xorm)
|
||||
[](https://goreportcard.com/report/github.com/go-xorm/xorm)
|
||||
[](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/)
|
||||
@ -1,500 +0,0 @@
|
||||
# xorm
|
||||
|
||||
[English](https://github.com/go-xorm/xorm/blob/master/README.md)
|
||||
|
||||
xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
|
||||
|
||||
[](https://circleci.com/gh/go-xorm/xorm) [](https://codecov.io/gh/go-xorm/xorm)
|
||||
[](https://goreportcard.com/report/github.com/go-xorm/xorm)
|
||||
[](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/)
|
||||
@ -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()}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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]
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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¶mN=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
|
||||
}
|
||||
@ -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¶mN=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
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
||||
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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
Reference in New Issue
Block a user