diff --git a/MySQL2openGauss/bin/run.sh b/MySQL2openGauss/bin/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..38883d94a7a66c9e89ce1c337ac31ff9bfee1337 --- /dev/null +++ b/MySQL2openGauss/bin/run.sh @@ -0,0 +1,43 @@ +#!/bin/bash +cd `dirname $0` +cd .. +BASEDIR=`pwd` + +# set env +export MYSQL_TO_OPENGAUSS_HOME=$BASEDIR +export LD_LIBRARY_PATH=$BASEDIR/lib/unixODBC/unixODBC-2.3.9/lib:$BASEDIR/unixODBC-2.3.9/lib:$BASEDIR/lib:$LD_LIBRARY_PATH +export ODBCSYSINI=$BASEDIR/conf +export ODBCINI=$BASEDIR/conf/odbc.ini +export CREATE_TABLE_REPLACE_KV=$BASEDIR/conf/createtablerkv.prop +source $BASEDIR/conf/mysql2opengauss.prop +source $BASEDIR/conf/metrics.prop + +# odbc.ini +sed "s:OPENGAUSS_SERVERNAME:$opengauss_host:g" $BASEDIR/conf/odbc.ini.template > $BASEDIR/conf/odbc.ini +sed -i "s:OPENGAUSS_PORT:$opengauss_port:g" $BASEDIR/conf/odbc.ini +sed -i "s:OPENGAUSS_USERNAME:$opengauss_username:g" $BASEDIR/conf/odbc.ini +sed -i "s:OPENGAUSS_PASSWORD:$opengauss_password:g" $BASEDIR/conf/odbc.ini +sed -i "s:OPENGAUSS_DATABASE:$opengauss_database:g" $BASEDIR/conf/odbc.ini + +sed -i "s:METRICS_SERVERNAME:$metrics_host:g" $BASEDIR/conf/odbc.ini +sed -i "s:METRICS_PORT:$metrics_port:g" $BASEDIR/conf/odbc.ini +sed -i "s:METRICS_USERNAME:$metrics_username:g" $BASEDIR/conf/odbc.ini +sed -i "s:METRICS_PASSWORD:$metrics_password:g" $BASEDIR/conf/odbc.ini +sed -i "s:METRICS_DATABASE:$metrics_database:g" $BASEDIR/conf/odbc.ini + +export metrics_dbtype + +# odbcinst.ini +sed "s:BASEDIR:$BASEDIR:g" $BASEDIR/conf/odbcinst.ini.template > $BASEDIR/conf/odbcinst.ini + +# run +cd bin +./mysqltoopengauss + +if [ "$?" == "0" ]; +then + echo success. +else + echo failed to migrate mysql data!!! +fi + diff --git a/MySQL2openGauss/build_aarch64.sh b/MySQL2openGauss/build_aarch64.sh new file mode 100644 index 0000000000000000000000000000000000000000..da3202c5b9dafcc41f355fa1c423c09e7d7aa0c4 --- /dev/null +++ b/MySQL2openGauss/build_aarch64.sh @@ -0,0 +1,54 @@ +#!/bin/bash +cd `dirname $0` +BASEDIR=`pwd` + +build_error () { + echo build error !!! + exit 1 +} + +# unixODBC +cd lib/unixODBC +tar -xf unixODBC-2.3.9-aarch64.tar.gz +cd $BASEDIR + +# mysql +cd mysqlsrc +tar -xf mysql-boost-5.7.27.tar.gz +cd mysql-5.7.27 +ln -s $BASEDIR/lib/unixODBC unixODBC +rm -f ./client/CMakeLists.txt +cp -f $BASEDIR/cmake/mysql-5.7.27/client/CMakeLists.txt ./client/ +cp -f $BASEDIR/src/* ./client/ +cmake . -DCMAKE_INSTALL_PREFIX=$BASEDIR/mysql5727 -DWITH_BOOST=$BASEDIR/mysqlsrc/mysql-5.7.27/boost/boost_1_59_0 +make -j2 +# ignore error and make again +make +if [ "$?" != "0" ]; then + build_error +fi +rm -f $BASEDIR/bin/mysqltoopengauss +cp -f $BASEDIR/mysqlsrc/mysql-5.7.27/client/mysqltoopengauss $BASEDIR/bin/ +cd $BASEDIR + +# libs +cd lib +tar -xf depends-aarch64.tar.gz +cd $BASEDIR + +# pack +cd bin +chmod +x * +cd .. +rm -rf output/mysqltoopengauss +mkdir -p output/mysqltoopengauss +cp -rf bin output/mysqltoopengauss +cp -rf conf output/mysqltoopengauss +cp -rf lib output/mysqltoopengauss +rm -rf output/mysqltoopengauss/lib/unixODBC/*.tar.gz +rm -rf output/mysqltoopengauss/lib/*.tar.gz +cd output +chmod +x mysqltoopengauss/bin/* +tar -czf mysqltoopengauss.tar.gz mysqltoopengauss +rm -rf mysqltoopengauss +echo mysqltoopengauss is pack to `pwd`/mysqltoopengauss.tar.gz diff --git a/MySQL2openGauss/build_x86.sh b/MySQL2openGauss/build_x86.sh new file mode 100644 index 0000000000000000000000000000000000000000..ec540b9c043624ff2a2e5ac49ed07dac2799bf67 --- /dev/null +++ b/MySQL2openGauss/build_x86.sh @@ -0,0 +1,54 @@ +#!/bin/bash +cd `dirname $0` +BASEDIR=`pwd` + +build_error () { + echo build error !!! + exit 1 +} + +# unixODBC +cd lib/unixODBC +tar -xf unixODBC-2.3.9.tar.gz +cd $BASEDIR + +# mysql +cd mysqlsrc +tar -xf mysql-boost-5.7.27.tar.gz +cd mysql-5.7.27 +ln -s $BASEDIR/lib/unixODBC unixODBC +rm -f ./client/CMakeLists.txt +cp -f $BASEDIR/cmake/mysql-5.7.27/client/CMakeLists.txt ./client/ +cp -f $BASEDIR/src/* ./client/ +cmake . -DCMAKE_INSTALL_PREFIX=$BASEDIR/mysql5727 -DWITH_BOOST=$BASEDIR/mysqlsrc/mysql-5.7.27/boost/boost_1_59_0 +make -j2 +# ignore error and make again +make +if [ "$?" != "0" ]; then + build_error +fi +rm -f $BASEDIR/bin/mysqltoopengauss +cp -f $BASEDIR/mysqlsrc/mysql-5.7.27/client/mysqltoopengauss $BASEDIR/bin/ +cd $BASEDIR + +# libs +cd lib +tar -xf depends.tar.gz +cd $BASEDIR + +# pack +cd bin +chmod +x * +cd .. +rm -rf output/mysqltoopengauss +mkdir -p output/mysqltoopengauss +cp -rf bin output/mysqltoopengauss +cp -rf conf output/mysqltoopengauss +cp -rf lib output/mysqltoopengauss +rm -rf output/mysqltoopengauss/lib/unixODBC/*.tar.gz +rm -rf output/mysqltoopengauss/lib/*.tar.gz +cd output +chmod +x mysqltoopengauss/bin/* +tar -czf mysqltoopengauss.tar.gz mysqltoopengauss +rm -rf mysqltoopengauss +echo mysqltoopengauss is pack to `pwd`/mysqltoopengauss.tar.gz diff --git a/MySQL2openGauss/cmake/mysql-5.7.27/client/CMakeLists.txt b/MySQL2openGauss/cmake/mysql-5.7.27/client/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..638c8a9e7f92f34d5f58295577eafded12010c45 --- /dev/null +++ b/MySQL2openGauss/cmake/mysql-5.7.27/client/CMakeLists.txt @@ -0,0 +1,143 @@ +# Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/client + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/mysys_ssl + ${LZ4_INCLUDE_DIR} + ${SSL_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/libmysql + ${CMAKE_SOURCE_DIR}/libbinlogevents/include + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/strings + ${EDITLINE_INCLUDE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/compile_flags.cmake) + +## Subdirectory with common client code. +ADD_SUBDIRECTORY(base) +## Subdirectory for mysqlpump code. +ADD_SUBDIRECTORY(dump) + +## We will need libeay32.dll and ssleay32.dll when running client executables. +COPY_OPENSSL_DLLS(copy_openssl_client) + +INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/compile_flags.cmake) + +ADD_DEFINITIONS(${SSL_DEFINES}) +MYSQL_ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc ../sql-common/sql_string.cc) +TARGET_LINK_LIBRARIES(mysql mysqlclient) +IF(UNIX) + TARGET_LINK_LIBRARIES(mysql ${EDITLINE_LIBRARY}) +ENDIF(UNIX) + +IF(NOT WITHOUT_SERVER) + MYSQL_ADD_EXECUTABLE(mysql_upgrade + upgrade/program.cc + ) +ADD_COMPILE_FLAGS( + upgrade/program.cc COMPILE_FLAGS -I${BOOST_PATCHES_DIR} -I${BOOST_INCLUDE_DIR} +) +TARGET_LINK_LIBRARIES(mysql_upgrade mysqlclient client_base mysqlcheck_core) +ADD_DEPENDENCIES(mysql_upgrade GenFixPrivs GenSysSchema) +ENDIF() + +MYSQL_ADD_EXECUTABLE(mysqltest mysqltest.cc COMPONENT Test) +SET_SOURCE_FILES_PROPERTIES(mysqltest.cc PROPERTIES COMPILE_FLAGS "-DTHREADS") +TARGET_LINK_LIBRARIES(mysqltest mysqlclient regex) +ADD_DEPENDENCIES(mysqltest GenError) + +ADD_CONVENIENCE_LIBRARY(mysqlcheck_core check/mysqlcheck_core.cc) +TARGET_LINK_LIBRARIES(mysqlcheck_core mysqlclient) + +MYSQL_ADD_EXECUTABLE(mysqlcheck check/mysqlcheck.cc) +TARGET_LINK_LIBRARIES(mysqlcheck mysqlcheck_core) + +MYSQL_ADD_EXECUTABLE(mysqldump mysqldump.c ../sql-common/my_user.c) +TARGET_LINK_LIBRARIES(mysqldump mysqlclient) + +LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/unixODBC/unixODBC-2.3.9/lib) +MYSQL_ADD_EXECUTABLE(mysqltoopengauss mysqltoopengauss.cpp ProcOdbc.cpp ProcMetrics.cpp ../sql-common/my_user.c) +ADD_COMPILE_FLAGS( + ProcOdbc.cpp + COMPILE_FLAGS "-I${CMAKE_SOURCE_DIR}/unixODBC/unixODBC-2.3.9/include" +) +TARGET_LINK_LIBRARIES(mysqltoopengauss mysqlclient odbc) + +MYSQL_ADD_EXECUTABLE(mysqlimport mysqlimport.c) +SET_SOURCE_FILES_PROPERTIES(mysqlimport.c PROPERTIES COMPILE_FLAGS "-DTHREADS") +TARGET_LINK_LIBRARIES(mysqlimport mysqlclient) + +MYSQL_ADD_EXECUTABLE(mysqlshow mysqlshow.c) +TARGET_LINK_LIBRARIES(mysqlshow mysqlclient) + +MYSQL_ADD_EXECUTABLE(mysql_plugin mysql_plugin.c) +TARGET_LINK_LIBRARIES(mysql_plugin mysqlclient) + +MYSQL_ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc) +ADD_COMPILE_FLAGS( + mysqlbinlog.cc + COMPILE_FLAGS "-I${CMAKE_SOURCE_DIR}/sql" "-DHAVE_REPLICATION" "-DDISABLE_PSI_MUTEX" +) +TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient binlogevents_static) + +MYSQL_ADD_EXECUTABLE(mysqladmin mysqladmin.cc) +TARGET_LINK_LIBRARIES(mysqladmin mysqlclient) + +MYSQL_ADD_EXECUTABLE(mysqlslap mysqlslap.cc) +SET_SOURCE_FILES_PROPERTIES(mysqlslap.cc PROPERTIES COMPILE_FLAGS "-DTHREADS") +TARGET_LINK_LIBRARIES(mysqlslap mysqlclient) + +MYSQL_ADD_EXECUTABLE(mysql_config_editor mysql_config_editor.cc) +TARGET_LINK_LIBRARIES(mysql_config_editor mysqlclient) + +MYSQL_ADD_EXECUTABLE(mysql_secure_installation mysql_secure_installation.cc) +TARGET_LINK_LIBRARIES(mysql_secure_installation mysqlclient) + +IF(UNIX AND NOT WITHOUT_SERVER) + MYSQL_ADD_EXECUTABLE(mysql_install_db + mysql_install_db.cc auth_utils.cc path.cc logger.cc) + TARGET_LINK_LIBRARIES(mysql_install_db mysqlclient) + ADD_COMPILE_FLAGS( + auth_utils.cc mysql_install_db.cc + COMPILE_FLAGS "-I${CMAKE_SOURCE_DIR}/sql/auth" + ) + ADD_DEPENDENCIES(mysql_install_db GenBootstrapPriv GenSysSchema) +ENDIF() + +MYSQL_ADD_EXECUTABLE(mysql_ssl_rsa_setup mysql_ssl_rsa_setup.cc path.cc logger.cc) +TARGET_LINK_LIBRARIES(mysql_ssl_rsa_setup mysys mysys_ssl) +SET_TARGET_PROPERTIES(mysql_ssl_rsa_setup PROPERTIES LINKER_LANGUAGE CXX) + +# "WIN32" also covers 64 bit. "echo" is used in some files below "mysql-test/". +IF(WIN32) + MYSQL_ADD_EXECUTABLE(echo echo.c) +ENDIF(WIN32) + +SET_TARGET_PROPERTIES ( + mysql_plugin + mysqlcheck + mysqldump + mysqltoopengauss + mysqlimport + mysqlshow + mysqlslap +PROPERTIES HAS_CXX TRUE) + +ADD_DEFINITIONS(-DHAVE_DLOPEN) + diff --git a/MySQL2openGauss/conf/createfunctionrkv.prop b/MySQL2openGauss/conf/createfunctionrkv.prop new file mode 100644 index 0000000000000000000000000000000000000000..aa70d61c992c4a79e37306f5cc03ab534dcd0558 --- /dev/null +++ b/MySQL2openGauss/conf/createfunctionrkv.prop @@ -0,0 +1,4 @@ +) CHARSET latin1_bin=) +) CHARSET latin1=) +) CHARSET utf8_bin=) +) CHARSET utf8=) diff --git a/MySQL2openGauss/conf/createfunctiontkv.prop b/MySQL2openGauss/conf/createfunctiontkv.prop new file mode 100644 index 0000000000000000000000000000000000000000..954178dff8c89c0f71ab8b92ff2554c817774d68 --- /dev/null +++ b/MySQL2openGauss/conf/createfunctiontkv.prop @@ -0,0 +1,26 @@ +RETURNS longtext=RETURNS text +RETURNS tinyint(=RETURNS integer( +RETURNS bigint(=RETURNS integer( +RETURNS datetime(3)=RETURNS timestamp +RETURNS datetime=RETURNS timestamp +RETURNS tinytext=RETURNS text +RETURNS longblob=RETURNS bytea +RETURNS mediumtext=RETURNS text +RETURNS mediumint(=RETURNS integer( +RETURNS smallint(=RETURNS integer( +RETURNS tinyblob=RETURNS bytea +RETURNS mediumblob=RETURNS bytea +RETURNS varbinary(=RETURNS bytea( +RETURNS binary(=RETURNS bytea( +RETURNS decimal(=RETURNS numeric( +RETURNS double=RETURNS double precision +RETURNS float=RETURNS double precision +RETURNS bit(=RETURNS integer( +RETURNS year(=RETURNS integer( +RETURNS geometrycollection=RETURNS bytea +RETURNS geometry=RETURNS point +RETURNS linestring=RETURNS path +RETURNS multipoint=RETURNS bytea +RETURNS multilinestring=RETURNS bytea +RETURNS multipolygon=RETURNS bytea +RETURNS int(=RETURNS integer( diff --git a/MySQL2openGauss/conf/createtablerkv.prop b/MySQL2openGauss/conf/createtablerkv.prop new file mode 100644 index 0000000000000000000000000000000000000000..2fa6c06a3ff14c926978d2c10098024265d8e85b --- /dev/null +++ b/MySQL2openGauss/conf/createtablerkv.prop @@ -0,0 +1,45 @@ +CHARACTER SET utf8 COLLATE utf8_bin= +CHARACTER SET utf8= +COLLATE utf8_bin= +COLLATE utf8_general_ci= +CHARACTER SET latin1 COLLATE latin1_bin= +CHARACTER SET latin1= +COLLATE latin1_bin= +AUTO_INCREMENT= +" longtext=" text +tinyint(=integer( +bigint(=integer( +datetime(3)=timestamp without time zone +" datetime=" timestamp without time zone +" timestamp,=" timestamp without time zone, +" timestamp NULL DEFAULT NULL,=" timestamp without time zone NULL, +" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,=" timestamp without time zone NOT NULL, +" timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',=" timestamp without time zone NOT NULL, +" tinytext=" text +" longblob=" bytea +" mediumtext=" text +mediumint(=integer( +smallint(=integer( +" time,=" time without time zone, +" time NOT NULL,=" time without time zone NOT NULL, +" time DEFAULT NULL,=" time without time zone DEFAULT NULL, +" tinyblob=" bytea +" mediumblob=" bytea +varbinary(=bytea( +binary(=bytea( +decimal(=numeric( +ON UPDATE CURRENT_TIMESTAMP= +" double,=" double precision, +" double DEFAULT=" double precision DEFAULT +" double NOT NULL=" double precision NOT NULL +" float=" double precision +bit(=integer( +year(=integer( +" geometrycollection=" bytea +" geometry=" point +" linestring=" path +" multipoint=" bytea +" multilinestring=" bytea +" multipolygon=" bytea +int(=integer( + diff --git a/MySQL2openGauss/conf/metrics.prop b/MySQL2openGauss/conf/metrics.prop new file mode 100644 index 0000000000000000000000000000000000000000..1a4fcc06f1777dd941a92735661eeab181adf164 --- /dev/null +++ b/MySQL2openGauss/conf/metrics.prop @@ -0,0 +1,6 @@ +metrics_host=127.0.0.1 +metrics_port=3306 +metrics_username=root +metrics_password=password +metrics_database=metricsdb +metrics_dbtype=mysql diff --git a/MySQL2openGauss/conf/mysql2opengauss.prop b/MySQL2openGauss/conf/mysql2opengauss.prop new file mode 100644 index 0000000000000000000000000000000000000000..3d4b87e44861c6fe2ecdb45f4061dbe60bee43c7 --- /dev/null +++ b/MySQL2openGauss/conf/mysql2opengauss.prop @@ -0,0 +1,11 @@ +mysql_host=127.0.0.1 +mysql_port=3306 +mysql_username=username +mysql_password=password +mysql_database=database +opengauss_host=127.0.0.1 +opengauss_port=5432 +opengauss_username=opengauss +opengauss_password=password +opengauss_database=database +loglevel=4 diff --git a/MySQL2openGauss/conf/odbc.ini.template b/MySQL2openGauss/conf/odbc.ini.template new file mode 100644 index 0000000000000000000000000000000000000000..e268d2b8e6071103d85b5f62ac159f20a27ae16d --- /dev/null +++ b/MySQL2openGauss/conf/odbc.ini.template @@ -0,0 +1,28 @@ +[opengauss] +Driver=opengauss +Servername=OPENGAUSS_SERVERNAME +Database=OPENGAUSS_DATABASE +Username=OPENGAUSS_USERNAME +Password=OPENGAUSS_PASSWORD +Port=OPENGAUSS_PORT +Sslmode=disable + +[metrics_mysql] +Driver=mysql +SERVER=METRICS_SERVERNAME +PORT=METRICS_PORT +USER=METRICS_USERNAME +Password=METRICS_PASSWORD +Database=METRICS_DATABASE +OPTION=3 +Threading=1 +ClientCharSet=UTF8 + +[metrics_opengauss] +Driver=opengauss +Servername=METRICS_SERVERNAME +Database=METRICS_DATABASE +Username=METRICS_USERNAME +Password=METRICS_PASSWORD +Port=METRICS_PORT +Sslmode=disable diff --git a/MySQL2openGauss/conf/odbcinst.ini.template b/MySQL2openGauss/conf/odbcinst.ini.template new file mode 100644 index 0000000000000000000000000000000000000000..bd6d0cd264bd0c29b68736016758ca22cddccec4 --- /dev/null +++ b/MySQL2openGauss/conf/odbcinst.ini.template @@ -0,0 +1,7 @@ +[opengauss] +Driver64=BASEDIR/lib/psqlodbcw.so +setup=BASEDIR/lib/psqlodbcw.so + +[mysql] +Driver64=BASEDIR/lib/libmyodbc5w.so +setup=BASEDIR/lib/libmyodbc5S.so diff --git a/MySQL2openGauss/grafana/samplemodel.json b/MySQL2openGauss/grafana/samplemodel.json new file mode 100644 index 0000000000000000000000000000000000000000..e883646b38f76446108bf3be5b4bb9453b1980f3 --- /dev/null +++ b/MySQL2openGauss/grafana/samplemodel.json @@ -0,0 +1,1293 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": "MySQL", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1000000 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 19, + "options": { + "displayMode": "gradient", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "text": {} + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "format": "table", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\nUNIX_TIMESTAMP(proctime) AS time_sec,\r\n '数据库数量' AS metric,\r\n count(*) AS '数据库数量' \r\nFROM db_info_metrics\r\n WHERE\r\n $__timeFilter(proctime) group by metric;\r\n ", + "refId": "A1", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + }, + { + "format": "table", + "group": [], + "metricColumn": "none", + "queryType": "randomWalk", + "rawQuery": true, + "rawSql": "SELECT\r\nUNIX_TIMESTAMP(proctime) AS time_sec,\r\n '表数量' AS metric,\r\n sum(tablecount) AS '表数量' \r\nFROM db_info_metrics\r\n WHERE\r\n $__timeFilter(proctime) group by metric;\r\n ", + "refId": "A2", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + }, + { + "format": "table", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\nUNIX_TIMESTAMP(proctime) AS time_sec,\r\n '视图数量' AS metric,\r\n sum(viewcount) AS '视图数量' \r\nFROM db_info_metrics\r\n WHERE\r\n $__timeFilter(proctime) group by metric;\r\n ", + "refId": "B", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + }, + { + "format": "table", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\nUNIX_TIMESTAMP(proctime) AS time_sec,\r\n '存储过程数量' AS metric,\r\n sum(procedurecount) AS '存储过程数量' \r\nFROM db_info_metrics WHERE\r\n $__timeFilter(proctime) group by metric;\r\n ", + "refId": "C", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + }, + { + "format": "table", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\nUNIX_TIMESTAMP(proctime) AS time_sec,\r\n '函数数量' AS metric,\r\n sum(functioncount) AS '函数数量' \r\nFROM db_info_metrics WHERE\r\n $__timeFilter(proctime) group by metric;\r\n ", + "refId": "D", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "迁移数量", + "type": "bargauge" + }, + { + "datasource": "MySQL", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 12, + "y": 0 + }, + "id": 20, + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom" + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.0.6", + "targets": [ + { + "format": "table", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n '执行正确语句数量' AS metric,\r\n sum(rightcount) AS '执行正确语句数量' \r\nFROM db_info_metrics\r\n WHERE\r\n $__timeFilter(proctime);\r\n ", + "refId": "A", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + }, + { + "format": "table", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n '执行错误语句数量' AS metric,\r\n sum(errorcount) AS '执行错误语句数量' \r\nFROM db_info_metrics\r\n WHERE\r\n $__timeFilter(proctime);\r\n ", + "refId": "B", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "执行语句情况", + "type": "piechart" + }, + { + "datasource": "MySQL", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 4, + "x": 17, + "y": 0 + }, + "id": 21, + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom" + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.0.6", + "targets": [ + { + "format": "table", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n proctime AS time,\r\n concat(concat(srcaddr, srcdb), tablename) as series,\r\n insertrightcount AS value \r\nFROM table_info_metrics\r\n WHERE\r\n $__timeFilter(proctime)\r\norder by value desc;\r\n ", + "refId": "A", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "插入数据量", + "type": "piechart" + }, + { + "datasource": "MySQL", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 3, + "x": 21, + "y": 0 + }, + "id": 22, + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom" + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.0.6", + "targets": [ + { + "format": "table", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n proctime AS time,\r\n concat(concat(srcaddr, srcdb), tablename) as series,\r\n consumetime AS value \r\nFROM table_info_metrics\r\n WHERE\r\n $__timeFilter(proctime)\r\norder by value desc;\r\n ", + "refId": "A", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "耗时情况(毫秒)", + "type": "piechart" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 14, + "panels": [], + "title": "基本信息", + "type": "row" + }, + { + "datasource": "MySQL", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "fillOpacity": 70, + "lineWidth": 0 + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-green", + "value": null + }, + { + "color": "dark-blue", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 16, + "options": { + "alignValue": "left", + "legend": { + "displayMode": "list", + "placement": "bottom" + }, + "mergeValues": true, + "rowHeight": 0.9, + "showValue": "auto", + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.0.6", + "targets": [ + { + "format": "time_series", + "group": [], + "metricColumn": "none", + "queryType": "randomWalk", + "rawQuery": true, + "rawSql": "SELECT\r\n $__timeGroupAlias(A.updatetime, $__interval),\r\n concat(A.srcdb, '表') AS metric,\r\n A.tablecount AS value \r\nFROM time_db_info_metrics A, (select srcdb from time_db_info_metrics group by proctime, srcdb order by updatetime desc limit 2) B\r\n WHERE\r\n $__timeFilter(A.updatetime) and \r\n A.srcdb = B.srcdb\r\n ORDER BY A.updatetime;\r\n ", + "refId": "表数量", + "select": [ + [ + { + "params": [ + "tablecount" + ], + "type": "column" + }, + { + "params": [ + "value" + ], + "type": "alias" + } + ] + ], + "table": "time_db_info_metrics", + "timeColumn": "updatetime", + "timeColumnType": "datetime", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + }, + { + "format": "time_series", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n $__timeGroupAlias(A.updatetime, $__interval),\r\n concat(A.srcdb, '视图') AS metric,\r\n A.viewcount AS value \r\nFROM time_db_info_metrics A, (select srcdb from time_db_info_metrics group by proctime, srcdb order by updatetime desc limit 2) B\r\n WHERE\r\n $__timeFilter(A.updatetime) and \r\n A.srcdb = B.srcdb\r\n ORDER BY A.updatetime;\r\n ", + "refId": "视图数量", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + }, + { + "format": "time_series", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n $__timeGroupAlias(A.updatetime, $__interval),\r\n concat(A.srcdb, '过程') AS metric,\r\n A.procedurecount AS value \r\nFROM time_db_info_metrics A, (select srcdb from time_db_info_metrics group by proctime, srcdb order by updatetime desc limit 2) B\r\n WHERE\r\n $__timeFilter(A.updatetime) and \r\n A.srcdb = B.srcdb\r\n ORDER BY A.updatetime;\r\n ", + "refId": "过程数量", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + }, + { + "format": "time_series", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n $__timeGroupAlias(A.updatetime, $__interval),\r\n concat(A.srcdb, '函数') AS metric,\r\n A.functioncount AS value \r\nFROM time_db_info_metrics A, (select srcdb from time_db_info_metrics group by proctime, srcdb order by updatetime desc limit 2) B\r\n WHERE\r\n $__timeFilter(A.updatetime) and \r\n A.srcdb = B.srcdb\r\n ORDER BY A.updatetime;\r\n ", + "refId": "函数数量", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "最近两个数据库的迁移数量", + "type": "state-timeline" + }, + { + "datasource": "MySQL", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "fillOpacity": 70, + "lineWidth": 0 + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-green", + "value": null + }, + { + "color": "dark-blue", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 17, + "options": { + "alignValue": "left", + "legend": { + "displayMode": "list", + "placement": "bottom" + }, + "mergeValues": true, + "rowHeight": 0.9, + "showValue": "auto", + "tooltip": { + "mode": "single" + } + }, + "pluginVersion": "8.0.6", + "targets": [ + { + "format": "time_series", + "group": [], + "metricColumn": "none", + "queryType": "randomWalk", + "rawQuery": true, + "rawSql": "SELECT\r\n UNIX_TIMESTAMP(A.updatetime) AS time_sec,\r\n concat(A.srcdb, '总数') AS metric,\r\n A.totalcount AS value \r\nFROM time_db_info_metrics A, (select srcdb from time_db_info_metrics group by proctime, srcdb order by updatetime desc limit 2) B\r\n WHERE\r\n $__timeFilter(updatetime) and \r\n A.srcdb = B.srcdb\r\n ORDER BY A.updatetime;\r\n ", + "refId": "总数", + "select": [ + [ + { + "params": [ + "tablecount" + ], + "type": "column" + }, + { + "params": [ + "value" + ], + "type": "alias" + } + ] + ], + "table": "time_db_info_metrics", + "timeColumn": "updatetime", + "timeColumnType": "datetime", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + }, + { + "format": "time_series", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n UNIX_TIMESTAMP(A.updatetime) AS time_sec,\r\n concat(A.srcdb, '错误数') AS metric,\r\n A.errorcount AS value \r\nFROM time_db_info_metrics A, (select srcdb from time_db_info_metrics group by proctime, srcdb order by updatetime desc limit 2) B\r\n WHERE\r\n $__timeFilter(updatetime) and \r\n A.srcdb = B.srcdb\r\n ORDER BY A.updatetime;\r\n ", + "refId": "错误数量", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "最近两个数据库的迁移语句执行数量", + "type": "state-timeline" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 12, + "panels": [], + "title": "详细信息", + "type": "row" + }, + { + "datasource": "MySQL", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "源数据库" + }, + "properties": [ + { + "id": "custom.width", + "value": null + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "迁移时间" + }, + "properties": [ + { + "id": "custom.width", + "value": null + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "错误语句总数" + }, + "properties": [ + { + "id": "custom.width", + "value": null + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 6, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "format": "table", + "group": [], + "metricColumn": "none", + "queryType": "randomWalk", + "rawQuery": true, + "rawSql": "SELECT\n date_sub(proctime, interval 8 hour) AS \"迁移时间\",\n srcdb AS \"源数据库\",\n dstdb AS \"目的数据库\",\n errorcount AS \"错误语句总数\",\n totalcount AS \"执行语句总数\",\n rightcount AS \"正确语句总数\",\n insertrightcount AS \"正确插入数据总量\",\n inserterrorcount AS \"错误插入数据总量\",\n consumetime AS \"迁移耗时(毫秒)\",\n srcaddr AS \"源数据库地址\",\n dstaddr AS \"目的数据库地址\",\n date_sub(starttime, interval 8 hour) AS \"开始时间\",\n date_sub(endtime, interval 8 hour) AS \"结束时间\"\nFROM db_info_metrics\nWHERE\n $__timeFilter(starttime)\nORDER BY errorcount desc,srcdb asc limit 10", + "refId": "A", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "错误最多的数据库", + "type": "table" + }, + { + "datasource": "MySQL", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 4, + "options": { + "showHeader": true + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "format": "table", + "group": [], + "metricColumn": "none", + "queryType": "randomWalk", + "rawQuery": true, + "rawSql": "SELECT\n date_sub(proctime, interval 8 hour) AS \"迁移时间\",\n srcdb AS \"源数据库\",\n dstdb AS \"目的数据库\",\n consumetime AS \"迁移耗时(毫秒)\",\n totalcount AS \"执行语句总数\",\n rightcount AS \"正确语句总数\",\n errorcount AS \"错误语句总数\",\n insertrightcount AS \"正确插入数据总量\",\n inserterrorcount AS \"错误插入数据总量\",\n srcaddr AS \"源数据库地址\",\n dstaddr AS \"目的数据库地址\",\n date_sub(starttime, interval 8 hour) AS \"开始时间\",\n date_sub(endtime, interval 8 hour) AS \"结束时间\"\nFROM db_info_metrics\nWHERE\n $__timeFilter(starttime)\nORDER BY consumetime desc,srcdb asc limit 10", + "refId": "A", + "select": [ + [ + { + "params": [ + "srcdb" + ], + "type": "column" + }, + { + "params": [ + "源数据库" + ], + "type": "alias" + } + ], + [ + { + "params": [ + "dstdb" + ], + "type": "column" + }, + { + "params": [ + "目标数据库" + ], + "type": "alias" + } + ], + [ + { + "params": [ + "consumetime" + ], + "type": "column" + }, + { + "params": [ + "迁移耗时" + ], + "type": "alias" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "starttime", + "timeColumnType": "datetime", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "迁移耗时最长的数据库", + "type": "table" + }, + { + "datasource": "MySQL", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "表名" + }, + "properties": [ + { + "id": "custom.width", + "value": 150 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 22 + }, + "id": 8, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "format": "table", + "group": [], + "metricColumn": "none", + "queryType": "randomWalk", + "rawQuery": true, + "rawSql": "SELECT\n date_sub(proctime, interval 8 hour) AS \"迁移时间\",\n tablename AS \"表名\",\n errorcount AS \"错误语句总数\",\n totalcount AS \"执行语句总数\",\n rightcount AS \"正确语句总数\",\n insertrightcount AS \"正确插入数据总量\",\n inserterrorcount AS \"错误插入数据总量\",\n srcdb AS \"源数据库\",\n dstdb AS \"目的数据库\",\n consumetime AS \"迁移耗时(毫秒)\",\n srcaddr AS \"源数据库地址\",\n dstaddr AS \"目的数据库地址\",\n date_sub(starttime, interval 8 hour) AS \"开始时间\",\n date_sub(endtime, interval 8 hour) AS \"结束时间\"\nFROM table_info_metrics\nWHERE\n $__timeFilter(starttime)\nORDER BY errorcount desc,tablename asc limit 10", + "refId": "A", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "错误最多的表", + "type": "table" + }, + { + "datasource": "MySQL", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "time" + }, + "properties": [ + { + "id": "custom.width", + "value": 172 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "tablename" + }, + "properties": [ + { + "id": "custom.width", + "value": null + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 22 + }, + "id": 2, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "format": "table", + "group": [], + "metricColumn": "none", + "queryType": "randomWalk", + "rawQuery": true, + "rawSql": "SELECT\n date_sub(proctime, interval 8 hour) AS \"迁移时间\",\n tablename AS \"表名\",\n consumetime AS \"迁移耗时(毫秒)\",\n srcdb AS \"源数据库\",\n dstdb AS \"目的数据库\",\n totalcount AS \"执行语句总数\",\n rightcount AS \"正确语句总数\",\n errorcount AS \"错误语句总数\",\n insertrightcount AS \"正确插入数据总量\",\n inserterrorcount AS \"错误插入数据总量\",\n srcaddr AS \"源数据库地址\",\n dstaddr AS \"目的数据库地址\",\n date_sub(starttime, interval 8 hour) AS \"开始时间\",\n date_sub(endtime, interval 8 hour) AS \"结束时间\"\nFROM table_info_metrics\nWHERE\n $__timeFilter(starttime)\nORDER BY consumetime desc, tablename asc limit 10", + "refId": "A", + "select": [ + [ + { + "params": [ + "consumetime" + ], + "type": "column" + } + ] + ], + "table": "table_info_metrics", + "timeColumn": "starttime", + "timeColumnType": "datetime", + "where": [] + } + ], + "title": "迁移耗时最长的表", + "type": "table" + }, + { + "datasource": "MySQL", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 16, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 10, + "options": { + "showHeader": true + }, + "pluginVersion": "8.1.1", + "targets": [ + { + "format": "table", + "group": [], + "metricColumn": "none", + "queryType": "randomWalk", + "rawQuery": true, + "rawSql": "SELECT\n date_sub(proctime, interval 8 hour) AS \"迁移时间\",\n name AS \"名称\",\n type AS \"类型\",\n errorinfo AS \"错误信息\",\n sqldata AS \"sql语句\",\n srcaddr AS \"源数据库地址\",\n dstaddr AS \"目的数据库地址\",\n srcdb AS \"源数据库\",\n dstdb AS \"目的数据库\",\n date_sub(starttime, interval 8 hour) AS \"开始时间\",\n date_sub(endtime, interval 8 hour) AS \"结束时间\",\n consumetime AS \"执行耗时(毫秒)\"\nFROM sql_info_metrics\nWHERE\n $__timeFilter(starttime)\nORDER BY starttime desc, name asc limit 20", + "refId": "A", + "select": [ + [ + { + "params": [ + "procpid" + ], + "type": "column" + } + ] + ], + "table": "db_info_metrics", + "timeColumn": "proctime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "最近的sql错误", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 30, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "mysql2opengauss", + "uid": "JK9lbinnz", + "version": 6 +} \ No newline at end of file diff --git a/MySQL2openGauss/lib/depends-aarch64.tar.gz b/MySQL2openGauss/lib/depends-aarch64.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..387702659eea476825866f5b9902d3940f65652f Binary files /dev/null and b/MySQL2openGauss/lib/depends-aarch64.tar.gz differ diff --git a/MySQL2openGauss/lib/depends-aarch64.tar.gz.txt b/MySQL2openGauss/lib/depends-aarch64.tar.gz.txt new file mode 100644 index 0000000000000000000000000000000000000000..f04371d5445a6a667758751f4cb343eb1dd44a63 --- /dev/null +++ b/MySQL2openGauss/lib/depends-aarch64.tar.gz.txt @@ -0,0 +1 @@ +把depends-aarch64.tar.gz放到此目录 \ No newline at end of file diff --git a/MySQL2openGauss/lib/depends.tar.gz b/MySQL2openGauss/lib/depends.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..077b17de56705101f1228787eca0402c626252e2 Binary files /dev/null and b/MySQL2openGauss/lib/depends.tar.gz differ diff --git a/MySQL2openGauss/lib/depends.tar.gz.txt b/MySQL2openGauss/lib/depends.tar.gz.txt new file mode 100644 index 0000000000000000000000000000000000000000..d6253ec0cb896f7d689675279d568ab47e713dcd --- /dev/null +++ b/MySQL2openGauss/lib/depends.tar.gz.txt @@ -0,0 +1 @@ +把depends.tar.gz放到此目录 \ No newline at end of file diff --git a/MySQL2openGauss/lib/libmyodbc5S.so b/MySQL2openGauss/lib/libmyodbc5S.so new file mode 100644 index 0000000000000000000000000000000000000000..a4047a3cf373a8644ae402c0749aec1e6148f65e Binary files /dev/null and b/MySQL2openGauss/lib/libmyodbc5S.so differ diff --git a/MySQL2openGauss/lib/libmyodbc5w.so b/MySQL2openGauss/lib/libmyodbc5w.so new file mode 100644 index 0000000000000000000000000000000000000000..95a3b4cc78459a478b00012392359fefda87de97 Binary files /dev/null and b/MySQL2openGauss/lib/libmyodbc5w.so differ diff --git a/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9-aarch64.tar.gz b/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9-aarch64.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..5d5dfa655d0cbf17b1e13464eae6223e59b4f58e Binary files /dev/null and b/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9-aarch64.tar.gz differ diff --git a/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9-aarch64.tar.gz.txt b/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9-aarch64.tar.gz.txt new file mode 100644 index 0000000000000000000000000000000000000000..42ff404169184f2cac8284469c9417d1c165399c --- /dev/null +++ b/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9-aarch64.tar.gz.txt @@ -0,0 +1 @@ +把unixODBC-2.3.9-aarch64.tar.gz放到此目录 \ No newline at end of file diff --git a/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9.tar.gz b/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..68760dcd0f18f2c881771c8eb89d51e480052c9a Binary files /dev/null and b/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9.tar.gz differ diff --git a/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9.tar.gz.txt b/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9.tar.gz.txt new file mode 100644 index 0000000000000000000000000000000000000000..a5ac4fa4a63ec56b98d52af4ce428c7129ad90fb --- /dev/null +++ b/MySQL2openGauss/lib/unixODBC/unixODBC-2.3.9.tar.gz.txt @@ -0,0 +1 @@ +把unixODBC-2.3.9.tar.gz放到此目录 \ No newline at end of file diff --git a/MySQL2openGauss/mysql2opengauss.pdf b/MySQL2openGauss/mysql2opengauss.pdf new file mode 100644 index 0000000000000000000000000000000000000000..304fa34a325401893cf6c3e75405513105d872fe Binary files /dev/null and b/MySQL2openGauss/mysql2opengauss.pdf differ diff --git a/MySQL2openGauss/mysqlsrc/mysql-boost-5.7.27.tar.gz b/MySQL2openGauss/mysqlsrc/mysql-boost-5.7.27.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..bc300a84e8dbdefbb3461eb20700a7adea376001 Binary files /dev/null and b/MySQL2openGauss/mysqlsrc/mysql-boost-5.7.27.tar.gz differ diff --git a/MySQL2openGauss/mysqlsrc/mysql-boost-5.7.27.tar.gz.txt b/MySQL2openGauss/mysqlsrc/mysql-boost-5.7.27.tar.gz.txt new file mode 100644 index 0000000000000000000000000000000000000000..d499f50a5ceb84cfbfe5219b7a530e70c5c5c7c8 --- /dev/null +++ b/MySQL2openGauss/mysqlsrc/mysql-boost-5.7.27.tar.gz.txt @@ -0,0 +1 @@ +把mysql-boost-5.7.27.tar.gz放到此目录 \ No newline at end of file diff --git a/MySQL2openGauss/mysqltoopengauss.centos7.x86_64.tar.gz b/MySQL2openGauss/mysqltoopengauss.centos7.x86_64.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..4bd7263a37e8299f1bf917dc7b6ec96912ae5a95 Binary files /dev/null and b/MySQL2openGauss/mysqltoopengauss.centos7.x86_64.tar.gz differ diff --git a/MySQL2openGauss/mysqltoopengauss.openeuler.aarch64.tar.gz b/MySQL2openGauss/mysqltoopengauss.openeuler.aarch64.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..b31022bbe466c7df448028ae10db8ed6afa8acdd Binary files /dev/null and b/MySQL2openGauss/mysqltoopengauss.openeuler.aarch64.tar.gz differ diff --git a/MySQL2openGauss/sql/metrics_mysql.sql b/MySQL2openGauss/sql/metrics_mysql.sql new file mode 100644 index 0000000000000000000000000000000000000000..19b4dce8ae001b1d1625c692f76b235e5991b9c4 --- /dev/null +++ b/MySQL2openGauss/sql/metrics_mysql.sql @@ -0,0 +1,100 @@ +CREATE TABLE `db_info_metrics` ( + `procpid` int(11) NOT NULL, + `proctime` datetime(3) NOT NULL, + `srcaddr` varchar(255), + `dstaddr` varchar(255), + `srcdb` varchar(255), + `dstdb` varchar(255), + `starttime` datetime(3), + `endtime` datetime(3), + `consumetime` int(15), + `tablecount` int(11), + `viewcount` int(11), + `procedurecount` int(11), + `functioncount` int(11), + `totalcount` int(15), + `rightcount` int(15), + `errorcount` int(15), + `insertrightcount` int(15), + `inserterrorcount` int(15), + PRIMARY KEY (`procpid`, `proctime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `time_db_info_metrics` ( + `updatetime` datetime(3) NOT NULL, + `procpid` int(11) NOT NULL, + `proctime` datetime(3) NOT NULL, + `srcaddr` varchar(255), + `dstaddr` varchar(255), + `srcdb` varchar(255), + `dstdb` varchar(255), + `starttime` datetime(3), + `endtime` datetime(3), + `consumetime` int(15), + `tablecount` int(11), + `viewcount` int(11), + `procedurecount` int(11), + `functioncount` int(11), + `totalcount` int(15), + `rightcount` int(15), + `errorcount` int(15), + `insertrightcount` int(15), + `inserterrorcount` int(15), + PRIMARY KEY (`updatetime`, `procpid`, `proctime`, `endtime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `table_info_metrics` ( + `procpid` int(11) NOT NULL, + `proctime` datetime(3) NOT NULL, + `tablename` varchar(255) NOT NULL, + `srcaddr` varchar(255), + `dstaddr` varchar(255), + `srcdb` varchar(255), + `dstdb` varchar(255), + `starttime` datetime(3), + `endtime` datetime(3), + `consumetime` int(15), + `totalcount` int(15), + `rightcount` int(15), + `errorcount` int(15), + `insertrightcount` int(15), + `inserterrorcount` int(15), + PRIMARY KEY (`procpid`, `proctime`, `tablename`, `starttime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `time_table_info_metrics` ( + `updatetime` datetime(3) NOT NULL, + `procpid` int(11) NOT NULL, + `proctime` datetime(3) NOT NULL, + `tablename` varchar(255) NOT NULL, + `srcaddr` varchar(255), + `dstaddr` varchar(255), + `srcdb` varchar(255), + `dstdb` varchar(255), + `starttime` datetime(3) NOT NULL, + `endtime` datetime(3), + `consumetime` int(15), + `totalcount` int(15), + `rightcount` int(15), + `errorcount` int(15), + `insertrightcount` int(15), + `inserterrorcount` int(15), + PRIMARY KEY (`updatetime`, `procpid`, `proctime`, `tablename`, `starttime`, `endtime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `sql_info_metrics` ( + `procpid` int(11) NOT NULL, + `proctime` datetime(3) NOT NULL, + `name` varchar(255) NOT NULL, + `type` varchar(255) NOT NULL, + `srcaddr` varchar(255), + `dstaddr` varchar(255), + `srcdb` varchar(255), + `dstdb` varchar(255), + `starttime` datetime(3), + `endtime` datetime(3), + `consumetime` int(15), + `iserror` int(1), + `errorinfo` varchar(255), + `sqldata` mediumtext +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/MySQL2openGauss/sql/metrics_opengauss.sql b/MySQL2openGauss/sql/metrics_opengauss.sql new file mode 100644 index 0000000000000000000000000000000000000000..f44147e492439d772d1cc44f8d962c3f0bbf1a69 --- /dev/null +++ b/MySQL2openGauss/sql/metrics_opengauss.sql @@ -0,0 +1,100 @@ +CREATE TABLE "db_info_metrics" ( + "procpid" integer(11) NOT NULL, + "proctime" timestamp without time zone NOT NULL, + "srcaddr" varchar(255), + "dstaddr" varchar(255), + "srcdb" varchar(255), + "dstdb" varchar(255), + "starttime" timestamp without time zone, + "endtime" timestamp without time zone, + "consumetime" integer(15), + "tablecount" integer(11), + "viewcount" integer(11), + "procedurecount" integer(11), + "functioncount" integer(11), + "totalcount" integer(15), + "rightcount" integer(15), + "errorcount" integer(15), + "insertrightcount" integer(15), + "inserterrorcount" integer(15), + PRIMARY KEY ("procpid", "proctime") +); + +CREATE TABLE "time_db_info_metrics" ( + "updatetime" timestamp without time zone NOT NULL, + "procpid" integer(11) NOT NULL, + "proctime" timestamp without time zone NOT NULL, + "srcaddr" varchar(255), + "dstaddr" varchar(255), + "srcdb" varchar(255), + "dstdb" varchar(255), + "starttime" timestamp without time zone, + "endtime" timestamp without time zone, + "consumetime" integer(15), + "tablecount" integer(11), + "viewcount" integer(11), + "procedurecount" integer(11), + "functioncount" integer(11), + "totalcount" integer(15), + "rightcount" integer(15), + "errorcount" integer(15), + "insertrightcount" integer(15), + "inserterrorcount" integer(15), + PRIMARY KEY ("updatetime", "procpid", "proctime", "endtime") +); + +CREATE TABLE "table_info_metrics" ( + "procpid" integer(11) NOT NULL, + "proctime" timestamp without time zone NOT NULL, + "tablename" varchar(255) NOT NULL, + "srcaddr" varchar(255), + "dstaddr" varchar(255), + "srcdb" varchar(255), + "dstdb" varchar(255), + "starttime" timestamp without time zone, + "endtime" timestamp without time zone, + "consumetime" integer(15), + "totalcount" integer(15), + "rightcount" integer(15), + "errorcount" integer(15), + "insertrightcount" integer(15), + "inserterrorcount" integer(15), + PRIMARY KEY ("procpid", "proctime", "tablename", "starttime") +); + +CREATE TABLE "time_table_info_metrics" ( + "updatetime" timestamp without time zone NOT NULL, + "procpid" integer(11) NOT NULL, + "proctime" timestamp without time zone NOT NULL, + "tablename" varchar(255) NOT NULL, + "srcaddr" varchar(255), + "dstaddr" varchar(255), + "srcdb" varchar(255), + "dstdb" varchar(255), + "starttime" timestamp without time zone NOT NULL, + "endtime" timestamp without time zone, + "consumetime" integer(15), + "totalcount" integer(15), + "rightcount" integer(15), + "errorcount" integer(15), + "insertrightcount" integer(15), + "inserterrorcount" integer(15), + PRIMARY KEY ("updatetime", "procpid", "proctime", "tablename", "starttime", "endtime") +); + +CREATE TABLE "sql_info_metrics" ( + "procpid" integer(11) NOT NULL, + "proctime" timestamp without time zone NOT NULL, + "name" varchar(255) NOT NULL, + "type" varchar(255) NOT NULL, + "srcaddr" varchar(255), + "dstaddr" varchar(255), + "srcdb" varchar(255), + "dstdb" varchar(255), + "starttime" timestamp without time zone, + "endtime" timestamp without time zone, + "consumetime" integer(15), + "iserror" integer(1), + "errorinfo" varchar(255), + "sqldata" text +); diff --git a/MySQL2openGauss/src/ProcMetrics.cpp b/MySQL2openGauss/src/ProcMetrics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c2941340cb043ed8033b3a33acbdfb2507994055 --- /dev/null +++ b/MySQL2openGauss/src/ProcMetrics.cpp @@ -0,0 +1,734 @@ +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif +#include +#include + +#include +#include +#include +#include + +#include "ProcMetrics.h" + +#define OPENGAUSS_DBTYPE "opengauss" +#define MYSQL_DBTYPE "mysql" +#define OPENGAUSS_DB 1 +#define MYSQL_DB 2 + +#define INSERT_KEY "),(" +#define INSERT_KEY_LEN 3 + +namespace DMODBC { + +static int sg_dbtype; + +static int countInsertSql(const char* sql) { + int ret = 1; + sql = strstr(sql, INSERT_KEY); + while (sql) { + ret++; + sql = strstr(sql + INSERT_KEY_LEN, INSERT_KEY); + } + return ret; +} + +Synchronized::Synchronized(Mutex& m) : mutex_(&m) { mutex_->lock(); } + +Synchronized::~Synchronized() { mutex_->unlock(); } + +Mutex::Mutex() { +#ifdef WIN32 + InitializeCriticalSection(&lock_); +#else + pthread_mutex_init(&lock_, NULL); +#endif +} + +Mutex::~Mutex() { +#ifdef WIN32 + DeleteCriticalSection(&lock_); +#else + pthread_mutex_destroy(&lock_); +#endif +} + +void Mutex::lock() { +#ifdef WIN32 + EnterCriticalSection(&lock_); +#else + pthread_mutex_lock(&lock_); +#endif +} + +void Mutex::unlock() { +#ifdef WIN32 + LeaveCriticalSection(&lock_); +#else + pthread_mutex_unlock(&lock_); +#endif +} + +MetricInfo::~MetricInfo() {} + +SqlInfo::SqlInfo() + : baseinfo_(NULL), starttime_(0), endtime_(0), error_(false) {} + +SqlInfo::~SqlInfo() {} + +std::string SqlInfo::toSql() { + std::string ret; + if (error_) { + char* tmp; + int len = 1024; + if (sql_.size() > errordetail_.size()) { + if (len < (int)sql_.size() * 2) { + len = (int)sql_.size() * 2; + } + } else { + if (len < (int)errordetail_.size() * 2) { + len = (int)errordetail_.size() * 2; + } + } + tmp = (char*)malloc(len); + switch (sg_dbtype) { + case OPENGAUSS_DB: + ret = + "insert into sql_info_metrics(procpid, proctime, name, type, " + "srcaddr, " + "dstaddr, srcdb, " + "dstdb, starttime, endtime, consumetime, iserror, errorinfo, " + "sqldata) " + "values("; + sprintf(tmp, + "%d, TO_TIMESTAMP(%d), '%s', '%s', '%s', '%s', '%s', '%s', " + "TO_TIMESTAMP(%d), " + "TO_TIMESTAMP(%d), %d, %d, '", + baseinfo_->procpid, (int)(baseinfo_->proctime / 1000), + name_.c_str(), type_.c_str(), baseinfo_->srcaddr.c_str(), + baseinfo_->dstaddr.c_str(), baseinfo_->srcdb.c_str(), + baseinfo_->dstdb.c_str(), (int)(starttime_ / 1000), + (int)(endtime_ / 1000), (int)(endtime_ - starttime_), + error_ ? 1 : 0); + break; + case MYSQL_DB: + ret = + "insert into sql_info_metrics(procpid, proctime, name, type, " + "srcaddr, " + "dstaddr, srcdb, " + "dstdb, starttime, endtime, consumetime, iserror, errorinfo, " + "sqldata) " + "values("; + sprintf(tmp, + "%d, FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), '%s', '%s', " + "'%s', " + "'%s', '%s', '%s', " + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), " + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), %d, %d, '", + baseinfo_->procpid, (int)(baseinfo_->proctime / 1000), + name_.c_str(), type_.c_str(), baseinfo_->srcaddr.c_str(), + baseinfo_->dstaddr.c_str(), baseinfo_->srcdb.c_str(), + baseinfo_->dstdb.c_str(), (int)(starttime_ / 1000), + (int)(endtime_ / 1000), (int)(endtime_ - starttime_), + error_ ? 1 : 0); + break; + default: + // 涓嶆敮鎸佸叾浠栫被鍨 + return ""; + } + ret += tmp; + formatSql(errordetail_.c_str(), errordetail_.size(), tmp); + ret += tmp; + ret += "', '"; + formatSql(sql_.c_str(), sql_.size(), tmp); + ret += tmp; + ret += "')"; + free(tmp); + } + return ret; +} + +void SqlInfo::formatSql(const char* in, int inlen, char* out) { + int outindex = 0; + for (int index = 0; index < inlen; index++) { + if (in[index] == '\'') { + out[outindex] = '\''; + out[outindex + 1] = '\''; + outindex += 2; + } else { + out[outindex] = in[index]; + outindex++; + } + } + out[outindex] = '\0'; +} + +TableInfo::TableInfo() + : baseinfo_(NULL), + starttime_(0), + endtime_(0), + rightcount_(0), + errorcount_(0), + insertrightcount_(0), + inserterrorcount_(0) {} + +TableInfo::~TableInfo() {} + +std::string TableInfo::toSql() { + std::string ret; + char tmp[1024]; + switch (sg_dbtype) { + case OPENGAUSS_DB: + sprintf(tmp, + "%d, TO_TIMESTAMP(%d), '%s', '%s', '%s', '%s', '%s', " + "TO_TIMESTAMP(%d), " + "TO_TIMESTAMP(%d), %d, %d, %d, %d, %d, %d", + baseinfo_->procpid, (int)(baseinfo_->proctime / 1000), + table_.c_str(), baseinfo_->srcaddr.c_str(), + baseinfo_->dstaddr.c_str(), baseinfo_->srcdb.c_str(), + baseinfo_->dstdb.c_str(), (int)(starttime_ / 1000), + (int)(endtime_ / 1000), (int)(endtime_ - starttime_), + (rightcount_ + errorcount_), rightcount_, errorcount_, + insertrightcount_, inserterrorcount_); + break; + case MYSQL_DB: + sprintf( + tmp, + "%d, FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), '%s', '%s', '%s', " + "'%s', '%s', " + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), " + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), %d, %d, %d, %d, %d, " + "%d", + baseinfo_->procpid, (int)(baseinfo_->proctime / 1000), table_.c_str(), + baseinfo_->srcaddr.c_str(), baseinfo_->dstaddr.c_str(), + baseinfo_->srcdb.c_str(), baseinfo_->dstdb.c_str(), + (int)(starttime_ / 1000), (int)(endtime_ / 1000), + (int)(endtime_ - starttime_), (rightcount_ + errorcount_), + rightcount_, errorcount_, insertrightcount_, inserterrorcount_); + break; + default: + // 涓嶆敮鎸佸叾浠栫被鍨 + return ""; + } + ret = + "insert into table_info_metrics(procpid, proctime, tablename, srcaddr, " + "dstaddr, srcdb, " + "dstdb, starttime, endtime, consumetime, totalcount, rightcount, " + "errorcount, insertrightcount, inserterrorcount) values("; + + ret += tmp; + ret += ")"; + return ret; +} + +TimeTableInfo::TimeTableInfo(long long updatetime, TableInfo* tableInfo) { + baseinfo_ = tableInfo->baseinfo_; + updatetime_ = updatetime; + table_ = tableInfo->table_; + starttime_ = tableInfo->starttime_; + endtime_ = tableInfo->endtime_; + rightcount_ = tableInfo->rightcount_; + errorcount_ = tableInfo->errorcount_; + insertrightcount_ = tableInfo->insertrightcount_; + inserterrorcount_ = tableInfo->inserterrorcount_; +} + +TimeTableInfo::TimeTableInfo(TableInfo* tableInfo) { + baseinfo_ = tableInfo->baseinfo_; + updatetime_ = ProcMetrics::getCurrentTime(); + table_ = tableInfo->table_; + starttime_ = tableInfo->starttime_; + endtime_ = tableInfo->endtime_; + rightcount_ = tableInfo->rightcount_; + errorcount_ = tableInfo->errorcount_; + insertrightcount_ = tableInfo->insertrightcount_; + inserterrorcount_ = tableInfo->inserterrorcount_; +} + +TimeTableInfo::~TimeTableInfo() {} + +std::string TimeTableInfo::toSql() { + std::string ret; + char tmp[1024]; + switch (sg_dbtype) { + case OPENGAUSS_DB: + sprintf(tmp, + "TO_TIMESTAMP(%d), %d, TO_TIMESTAMP(%d), '%s', '%s', '%s', '%s', " + "'%s', " + "TO_TIMESTAMP(%d), " + "TO_TIMESTAMP(%d), %d, %d, %d, %d, %d, %d", + (int)(updatetime_ / 1000), baseinfo_->procpid, + (int)(baseinfo_->proctime / 1000), table_.c_str(), + baseinfo_->srcaddr.c_str(), baseinfo_->dstaddr.c_str(), + baseinfo_->srcdb.c_str(), baseinfo_->dstdb.c_str(), + (int)(starttime_ / 1000), (int)(endtime_ / 1000), + (int)(updatetime_ - starttime_), (rightcount_ + errorcount_), + rightcount_, errorcount_, insertrightcount_, inserterrorcount_); + break; + case MYSQL_DB: + sprintf(tmp, + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), %d, " + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), '%s', '%s', '%s', " + "'%s', '%s', " + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), " + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), %d, %d, %d, %d, " + "%d, %d", + (int)(updatetime_ / 1000), baseinfo_->procpid, + (int)(baseinfo_->proctime / 1000), table_.c_str(), + baseinfo_->srcaddr.c_str(), baseinfo_->dstaddr.c_str(), + baseinfo_->srcdb.c_str(), baseinfo_->dstdb.c_str(), + (int)(starttime_ / 1000), (int)(endtime_ / 1000), + (int)(updatetime_ - starttime_), (rightcount_ + errorcount_), + rightcount_, errorcount_, insertrightcount_, inserterrorcount_); + break; + default: + // 涓嶆敮鎸佸叾浠栫被鍨 + return ""; + } + ret = + "insert into time_table_info_metrics(updatetime, procpid, proctime, " + "tablename, " + "srcaddr, " + "dstaddr, srcdb, " + "dstdb, starttime, endtime, consumetime, totalcount, rightcount, " + "errorcount, insertrightcount, inserterrorcount) values("; + ret += tmp; + ret += ")"; + return ret; +} + +DBInfo::DBInfo() + : baseinfo_(NULL), + starttime_(0), + endtime_(0), + tablecount_(0), + viewcount_(0), + procedurecount_(0), + functioncount_(0), + rightcount_(0), + errorcount_(0), + insertrightcount_(0), + inserterrorcount_(0) {} + +DBInfo::~DBInfo() {} + +std::string DBInfo::toSql() { + std::string ret; + char tmp[1024]; + switch (sg_dbtype) { + case OPENGAUSS_DB: + sprintf(tmp, + "%d, TO_TIMESTAMP(%d), '%s', '%s', '%s', '%s', TO_TIMESTAMP(%d), " + "TO_TIMESTAMP(%d), %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + baseinfo_->procpid, (int)(baseinfo_->proctime / 1000), + baseinfo_->srcaddr.c_str(), baseinfo_->dstaddr.c_str(), + baseinfo_->srcdb.c_str(), baseinfo_->dstdb.c_str(), + (int)(starttime_ / 1000), (int)(endtime_ / 1000), + (int)(endtime_ - starttime_), tablecount_, viewcount_, + procedurecount_, functioncount_, (rightcount_ + errorcount_), + rightcount_, errorcount_, insertrightcount_, inserterrorcount_); + break; + case MYSQL_DB: + sprintf( + tmp, + "%d, FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), '%s', '%s', '%s', " + "'%s', FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), " + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), %d, %d, %d, %d, %d, " + "%d, %d, %d, %d, %d", + baseinfo_->procpid, (int)(baseinfo_->proctime / 1000), + baseinfo_->srcaddr.c_str(), baseinfo_->dstaddr.c_str(), + baseinfo_->srcdb.c_str(), baseinfo_->dstdb.c_str(), + (int)(starttime_ / 1000), (int)(endtime_ / 1000), + (int)(endtime_ - starttime_), tablecount_, viewcount_, + procedurecount_, functioncount_, (rightcount_ + errorcount_), + rightcount_, errorcount_, insertrightcount_, inserterrorcount_); + break; + default: + // 涓嶆敮鎸佸叾浠栫被鍨 + return ""; + } + ret = + "insert into db_info_metrics(procpid, proctime, srcaddr, dstaddr, srcdb, " + "dstdb, starttime, endtime, consumetime, tablecount, viewcount, " + "procedurecount, functioncount, totalcount, rightcount, " + "errorcount, insertrightcount, inserterrorcount) values("; + ret += tmp; + ret += ")"; + return ret; +} + +TimeDBInfo::TimeDBInfo(DBInfo* dbInfo) { + baseinfo_ = dbInfo->baseinfo_; + updatetime_ = ProcMetrics::getCurrentTime(); + starttime_ = dbInfo->starttime_; + endtime_ = dbInfo->endtime_; + tablecount_ = dbInfo->tablecount_; + viewcount_ = dbInfo->viewcount_; + procedurecount_ = dbInfo->procedurecount_; + functioncount_ = dbInfo->functioncount_; + rightcount_ = dbInfo->rightcount_; + errorcount_ = dbInfo->errorcount_; + insertrightcount_ = dbInfo->insertrightcount_; + inserterrorcount_ = dbInfo->inserterrorcount_; +} + +TimeDBInfo::TimeDBInfo(long long updatetime, DBInfo* dbInfo) { + baseinfo_ = dbInfo->baseinfo_; + updatetime_ = updatetime; + starttime_ = dbInfo->starttime_; + endtime_ = dbInfo->endtime_; + tablecount_ = dbInfo->tablecount_; + viewcount_ = dbInfo->viewcount_; + procedurecount_ = dbInfo->procedurecount_; + functioncount_ = dbInfo->functioncount_; + rightcount_ = dbInfo->rightcount_; + errorcount_ = dbInfo->errorcount_; + insertrightcount_ = dbInfo->insertrightcount_; + inserterrorcount_ = dbInfo->inserterrorcount_; +} + +TimeDBInfo::~TimeDBInfo() {} + +std::string TimeDBInfo::toSql() { + std::string ret; + char tmp[1024]; + switch (sg_dbtype) { + case OPENGAUSS_DB: + sprintf(tmp, + "TO_TIMESTAMP(%d), %d, TO_TIMESTAMP(%d), '%s', '%s', '%s', '%s', " + "TO_TIMESTAMP(%d), " + "TO_TIMESTAMP(%d), %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + (int)(updatetime_ / 1000), baseinfo_->procpid, + (int)(baseinfo_->proctime / 1000), baseinfo_->srcaddr.c_str(), + baseinfo_->dstaddr.c_str(), baseinfo_->srcdb.c_str(), + baseinfo_->dstdb.c_str(), (int)(starttime_ / 1000), + (int)(endtime_ / 1000), (int)(updatetime_ - starttime_), + tablecount_, viewcount_, procedurecount_, functioncount_, + (rightcount_ + errorcount_), rightcount_, errorcount_, + insertrightcount_, inserterrorcount_); + break; + case MYSQL_DB: + sprintf(tmp, + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), %d, " + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), '%s', '%s', '%s', " + "'%s', FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), " + "FROM_UNIXTIME(%d, '%%Y-%%m-%%d %%H-%%i-%%s'), %d, %d, %d, %d, " + "%d, %d, %d, %d, %d, %d", + (int)(updatetime_ / 1000), baseinfo_->procpid, + (int)(baseinfo_->proctime / 1000), baseinfo_->srcaddr.c_str(), + baseinfo_->dstaddr.c_str(), baseinfo_->srcdb.c_str(), + baseinfo_->dstdb.c_str(), (int)(starttime_ / 1000), + (int)(endtime_ / 1000), (int)(updatetime_ - starttime_), + tablecount_, viewcount_, procedurecount_, functioncount_, + (rightcount_ + errorcount_), rightcount_, errorcount_, + insertrightcount_, inserterrorcount_); + break; + default: + // 涓嶆敮鎸佸叾浠栫被鍨 + return ""; + } + ret = + "insert into time_db_info_metrics(updatetime, procpid, proctime, " + "srcaddr, dstaddr, srcdb, " + "dstdb, starttime, endtime, consumetime, tablecount, viewcount, " + "procedurecount, functioncount, totalcount, rightcount, " + "errorcount, insertrightcount, inserterrorcount) values("; + ret += tmp; + ret += ")"; + return ret; +} + +MetricInfoSeq::MetricInfoSeq(int maxsize) : maxsize_(maxsize) {} + +MetricInfoSeq::~MetricInfoSeq() { destroy(); } + +int MetricInfoSeq::getSize() { + Synchronized sync(lock_); + return metrics_.size(); +} + +std::list MetricInfoSeq::pollAllMetrics() { + std::list ret; + Synchronized sync(lock_); + ret = metrics_; + metrics_ = std::list(); + return ret; +} + +bool MetricInfoSeq::add(MetricInfo* info) { + bool ret = true; + Synchronized sync(lock_); + if ((maxsize_ <= 0) || ((int)(metrics_.size()) < maxsize_)) { + metrics_.push_back(info); + } else { + // 杈惧埌鏈澶ф暟缁勫閲 + ret = false; + } + return ret; +} + +void MetricInfoSeq::destroy() { + Synchronized sync(lock_); + for (std::list::iterator iter = metrics_.begin(); + iter != metrics_.end(); iter++) { + delete (*iter); + } + metrics_.clear(); +} + +static void* ProcThread(void* arg) { + ProcMetrics* procMetrics = (ProcMetrics*)arg; + procMetrics->saveMetricsToDb(); + return NULL; +} + +ProcMetrics::ProcMetrics(const char* metricDatasource, const char* srcaddr, + const char* dstaddr, const char* srcdb, + const char* dstdb, int seqmaxsize, const char* dbtype) + : startingDB_(NULL), running_(true), timeDB_(false), timeTable_(false) { + baseDBInfo_.procpid = getPid(); + baseDBInfo_.proctime = getCurrentTime(); + baseDBInfo_.srcaddr = srcaddr; + baseDBInfo_.dstaddr = dstaddr; + baseDBInfo_.srcdb = srcdb; + baseDBInfo_.dstdb = dstdb; + metricInfoSeq_ = new MetricInfoSeq(seqmaxsize); + + procOdbc_ = new DMODBC::ProcOdbc(metricDatasource); + procOdbc_->setLoglevel(5); + if (strcmp(OPENGAUSS_DBTYPE, dbtype) == 0) { + sg_dbtype = OPENGAUSS_DB; + } else if (strcmp(MYSQL_DBTYPE, dbtype) == 0) { + sg_dbtype = MYSQL_DB; + } else { + printf("unsupport db type: %s\n", dbtype); + } + if (!procOdbc_->connect()) { + printf("cannot connect to the datasource %s\n", metricDatasource); + } else { + pthread_create(&savingDatasThread_, NULL, ProcThread, this); + } +} + +ProcMetrics::~ProcMetrics() { + while (hasFinishedInfos()) { + sleep(1); + } + running_ = false; + pthread_join(savingDatasThread_, NULL); + if (startingDB_) { + delete startingDB_; + startingDB_ = NULL; + } + + if (metricInfoSeq_) { + delete metricInfoSeq_; + metricInfoSeq_ = NULL; + } + + if (procOdbc_) { + procOdbc_->disConnect(); + delete procOdbc_; + procOdbc_ = NULL; + } +} + +void ProcMetrics::startDB() { + if (startingDB_) { + delete startingDB_; + } + startingDB_ = new DBInfo(); + startingDB_->baseinfo_ = &baseDBInfo_; + startingDB_->starttime_ = getCurrentTime(); +} + +void ProcMetrics::startTable(const char* table) { + startingTable_ = new TableInfo(); + startingTable_->baseinfo_ = &baseDBInfo_; + startingTable_->table_ = table; + startingTable_->starttime_ = getCurrentTime(); +} + +void ProcMetrics::startSql(const char* name, const char* type, + const char* sql) { + startingSql_ = new SqlInfo(); + startingSql_->baseinfo_ = &baseDBInfo_; + startingSql_->name_ = name; + startingSql_->type_ = type; + startingSql_->sql_ = sql; + startingSql_->starttime_ = getCurrentTime(); +} + +void ProcMetrics::endSql(const char* name, const char* type, const char* sql, + bool error, const char* errordetail, bool isinsert) { + if (startingSql_) { + startingSql_->error_ = error; + startingSql_->errordetail_ = errordetail; + startingSql_->endtime_ = getCurrentTime(); + { + Synchronized sync(lock_); + finishedInfos_.push_back(startingSql_); + } + startingSql_ = NULL; + } + + int insertcount = 0; + if (isinsert) { + insertcount = countInsertSql(sql); + } + + if (startingTable_) { + if (error) { + startingTable_->errorcount_++; + if (insertcount) { + startingTable_->inserterrorcount_ += insertcount; + } + } else { + startingTable_->rightcount_++; + if (insertcount) { + startingTable_->insertrightcount_ += insertcount; + } + } + + if (timeTable_) { + { + Synchronized sync(lock_); + finishedInfos_.push_back(new TimeTableInfo(startingTable_)); + } + timeTable_ = false; + } + } + + if (startingDB_) { + if (error) { + startingDB_->errorcount_++; + if (insertcount) { + startingDB_->inserterrorcount_ += insertcount; + } + } else { + startingDB_->rightcount_++; + if (insertcount) { + startingDB_->insertrightcount_ += insertcount; + } + } + + if (timeDB_) { + { + Synchronized sync(lock_); + finishedInfos_.push_back(new TimeDBInfo(startingDB_)); + } + timeDB_ = false; + } + } +} + +void ProcMetrics::endTable(const char* table) { + if (startingTable_) { + startingTable_->endtime_ = getCurrentTime(); + { + Synchronized sync(lock_); + finishedInfos_.push_back(startingTable_); + finishedInfos_.push_back( + new TimeTableInfo(startingTable_->endtime_, startingTable_)); + } + startingTable_ = NULL; + } +} + +void ProcMetrics::endDB() { + if (startingDB_) { + startingDB_->endtime_ = getCurrentTime(); + { + Synchronized sync(lock_); + finishedInfos_.push_back(startingDB_); + finishedInfos_.push_back( + new TimeDBInfo(startingDB_->endtime_, startingDB_)); + } + startingDB_ = NULL; + } +} + +void ProcMetrics::addTablecount(int count) { + if (startingDB_) { + startingDB_->tablecount_ += count; + } +} + +void ProcMetrics::addViewcount(int count) { + if (startingDB_) { + startingDB_->viewcount_ += count; + } +} + +void ProcMetrics::addProcedurecount(int count) { + if (startingDB_) { + startingDB_->procedurecount_ += count; + } +} + +void ProcMetrics::addFunctioncount(int count) { + if (startingDB_) { + startingDB_->functioncount_ += count; + } +} + +int ProcMetrics::getPid() { +#ifdef WIN32 + return (int)GetCurrentProcessId(); +#else + return (int)getpid(); +#endif +} + +long long ProcMetrics::getCurrentTime() { +#ifdef WIN32 + timeb timebuffer; + ftime(&timebuffer); + return (long long)(timebuffer.time * 1000ULL + timebuffer.millitm); +#else + timeval t; + gettimeofday(&t, 0); + return (long long)(t.tv_sec * 1000ULL + t.tv_usec / 1000); +#endif +} + +std::list ProcMetrics::clearAndGetFinishedInfos() { + std::list ret; + Synchronized sync(lock_); + ret = finishedInfos_; + finishedInfos_ = std::list(); + return ret; +} + +bool ProcMetrics::hasFinishedInfos() { + bool ret = true; + Synchronized sync(lock_); + if (finishedInfos_.empty()) { + ret = false; + } + return ret; +} + +void ProcMetrics::saveMetricsToDb() { + std::string str; + while (running_) { + timeDB_ = true; + timeTable_ = true; + std::list metricInfos = clearAndGetFinishedInfos(); + for (std::list::iterator iter = metricInfos.begin(); + iter != metricInfos.end(); iter++) { + str = (*iter)->toSql(); + if (!str.empty()) { + procOdbc_->executeGeneralSql(str.c_str(), NULL); + } + delete *iter; + } + sleep(2); + } +} + +}; // namespace DMODBC diff --git a/MySQL2openGauss/src/ProcMetrics.h b/MySQL2openGauss/src/ProcMetrics.h new file mode 100644 index 0000000000000000000000000000000000000000..468837802ad68d623b15f6a59839522232da965e --- /dev/null +++ b/MySQL2openGauss/src/ProcMetrics.h @@ -0,0 +1,220 @@ +#ifndef _PROCMETRICS_H_ +#define _PROCMETRICS_H_ + +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif +#include "ProcOdbc.h" + +namespace DMODBC { + +struct BaseDBInfo { + int procpid; + long long proctime; + std::string srcaddr; + std::string dstaddr; + std::string srcdb; + std::string dstdb; +}; + +class Mutex { + public: + Mutex(); + + virtual ~Mutex(); + + void lock(); + + void unlock(); + + private: +#ifdef WIN32 + CRITICAL_SECTION lock_; +#else + pthread_mutex_t lock_; +#endif +}; + +class Synchronized { + public: + Synchronized(Mutex& m); + + virtual ~Synchronized(); + + private: + Mutex* mutex_; +}; + +class MetricInfo { + public: + virtual ~MetricInfo(); + virtual std::string toSql() = 0; +}; + +class SqlInfo : public MetricInfo { + public: + SqlInfo(); + virtual ~SqlInfo(); + + virtual std::string toSql(); + + BaseDBInfo* baseinfo_; + std::string name_; + std::string type_; + std::string sql_; + long long starttime_; + long long endtime_; + bool error_; + std::string errordetail_; + + private: + void formatSql(const char* in, int inlen, char* out); +}; + +class TableInfo : public MetricInfo { + public: + TableInfo(); + virtual ~TableInfo(); + + virtual std::string toSql(); + + BaseDBInfo* baseinfo_; + std::string table_; + long long starttime_; + long long endtime_; + int rightcount_; + int errorcount_; + int insertrightcount_; + int inserterrorcount_; +}; + +class TimeTableInfo : public MetricInfo { + public: + TimeTableInfo(TableInfo* tableInfo); + TimeTableInfo(long long updatetime_, TableInfo* tableInfo); + virtual ~TimeTableInfo(); + + virtual std::string toSql(); + + BaseDBInfo* baseinfo_; + long long updatetime_; + std::string table_; + long long starttime_; + long long endtime_; + int rightcount_; + int errorcount_; + int insertrightcount_; + int inserterrorcount_; +}; + +class DBInfo : public MetricInfo { + public: + DBInfo(); + virtual ~DBInfo(); + + virtual std::string toSql(); + + BaseDBInfo* baseinfo_; + long long starttime_; + long long endtime_; + int tablecount_; + int viewcount_; + int procedurecount_; + int functioncount_; + int rightcount_; + int errorcount_; + int insertrightcount_; + int inserterrorcount_; +}; + +class TimeDBInfo : public MetricInfo { + public: + TimeDBInfo(DBInfo* dbInfo); + TimeDBInfo(long long updatetime, DBInfo* dbInfo); + virtual ~TimeDBInfo(); + + virtual std::string toSql(); + + BaseDBInfo* baseinfo_; + long long updatetime_; + long long starttime_; + long long endtime_; + int tablecount_; + int viewcount_; + int procedurecount_; + int functioncount_; + int rightcount_; + int errorcount_; + int insertrightcount_; + int inserterrorcount_; +}; + +class MetricInfoSeq { + public: + MetricInfoSeq(int maxsize); + virtual ~MetricInfoSeq(); + int getSize(); + std::list pollAllMetrics(); + bool add(MetricInfo* info); + + private: + void destroy(); + + private: + int maxsize_; + Mutex lock_; + std::list metrics_; +}; + +class ProcMetrics { + public: + ProcMetrics(const char* metricDatasource, const char* srcaddr, + const char* dstaddr, const char* srcdb, const char* dstdb, + int seqmaxsize, const char* dbtype); + virtual ~ProcMetrics(); + + void startDB(); + void startTable(const char* table); + void startSql(const char* name, const char* type, const char* sql); + void endSql(const char* name, const char* type, const char* sql, bool error, + const char* errordetail, bool isinsert); + void endTable(const char* table); + void endDB(); + void addTablecount(int count); + void addViewcount(int count); + void addProcedurecount(int count); + void addFunctioncount(int count); + + void saveMetricsToDb(); + + static long long getCurrentTime(); + + private: + int getPid(); + std::list clearAndGetFinishedInfos(); + bool hasFinishedInfos(); + + private: + ProcOdbc* procOdbc_; + BaseDBInfo baseDBInfo_; + DBInfo* startingDB_; + TableInfo* startingTable_; + SqlInfo* startingSql_; + int size_; + MetricInfoSeq* metricInfoSeq_; + Mutex lock_; + std::list finishedInfos_; + bool running_; + bool timeDB_; + bool timeTable_; + pthread_t savingDatasThread_; +}; + +}; // namespace DMODBC + +#endif diff --git a/MySQL2openGauss/src/ProcOdbc.cpp b/MySQL2openGauss/src/ProcOdbc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd3d807fecfcc362fcfdd79f6c9df202e285028e --- /dev/null +++ b/MySQL2openGauss/src/ProcOdbc.cpp @@ -0,0 +1,993 @@ +#include +#include +#include +#ifdef WIN32 +#include +#endif +#include +#include + +#include +#include +#include +#include + +#include "ProcOdbc.h" + +#define SQLK_CREATE_TABLE "CREATE TABLE " +#define SQLK_UNIQUE_KEY "UNIQUE KEY " +#define SQLK_PRIMARY_KEY "PRIMARY KEY " +#define SQLK_FOREIGN_KEY "FOREIGN KEY " +#define SQLK_KEY "KEY " +#define SQLK_COMMENT " COMMENT '" +#define SQLK_BYTEA_START " bytea(" +#define SQLK_BYTEA_END ")" +#define SQLK_BYTEA " bytea" +#define SQLK_SET_START " set(" +#define SQLK_SET_END ")" +#define SQLK_TEXT " text" +#define SQLK_PROCEDURE "PROCEDURE" +#define SQLK_PROCEDURE_DEF_END ")" +#define SQLK_FUNCTION "FUNCTION" +#define SQLK_FUNCTION_DEF_END "\n" +#define SQLK_INSERT_INTO "\nINSERT INTO \"" +#define SQLK_DOUBLE_BRACKET " double(" +#define SQLK_DOUBLE_BRACKET_END ")" + +#define DATA_NOT_SEND_ERROR "could not send data" +#define NO_CONNECTION_ERROR "no connection" +#define NULL_STRING_ERROR "violates not-null constraint" + +#define TABLE_ALREADY_EXIST_ERROR "\" already exists;" + +#define INFO_LOG_LEVEL 3 +#define WARN_LOG_LEVEL 4 +#define ERROR_LOG_LEVEL 5 + +namespace DMODBC { + +struct ReplaceKV { + std::string key; + std::string value; +}; + +struct DBInfo { + SQLHENV V_OD_Env; // Handle ODBC environment + SQLHSTMT V_OD_hstmt; // Handle statement + SQLHDBC V_OD_hdbc; // Handle connection + char sqlattr[100]; + SQLINTEGER V_OD_erg; + SQLINTEGER V_OD_buffer; + SQLINTEGER V_OD_err; + SQLINTEGER V_OD_id; +}; + +static void printDBError(SQLSMALLINT type, SQLHDBC hdbc, + struct DBError *dbError) { + char status[16]; + int buflen = 1024; + char *msg = (char *)malloc(buflen); + msg[0] = '\0'; + SQLSMALLINT sqlmsglen; + SQLINTEGER sqlerr; + SQLGetDiagRec(type, hdbc, 1, (SQLCHAR *)status, &sqlerr, (SQLCHAR *)msg, + buflen - 1, &sqlmsglen); + if (buflen < sqlmsglen) { + free(msg); + buflen = sqlmsglen; + msg = (char *)malloc(buflen); + msg[0] = '\0'; + SQLGetDiagRec(type, hdbc, 1, (SQLCHAR *)status, &sqlerr, (SQLCHAR *)msg, + buflen - 1, &sqlmsglen); + } + printf("%s: %s\n", status, msg); + if (dbError != NULL) { + dbError->error = status; + dbError->error += ": "; + dbError->error += msg; + if (strstr(msg, TABLE_ALREADY_EXIST_ERROR)) { + dbError->alreadyExistError = true; + } else { + dbError->alreadyExistError = false; + } + } + free(msg); +} + +static bool isConnectionError(SQLSMALLINT type, SQLHDBC hdbc) { + bool ret = false; + char status[16]; + int buflen = 1024; + char *msg = (char *)malloc(buflen); + msg[0] = '\0'; + SQLSMALLINT sqlmsglen; + SQLINTEGER sqlerr; + SQLGetDiagRec(type, hdbc, 1, (SQLCHAR *)status, &sqlerr, (SQLCHAR *)msg, + buflen - 1, &sqlmsglen); + if (buflen < sqlmsglen) { + free(msg); + buflen = sqlmsglen; + msg = (char *)malloc(buflen); + msg[0] = '\0'; + SQLGetDiagRec(type, hdbc, 1, (SQLCHAR *)status, &sqlerr, (SQLCHAR *)msg, + buflen - 1, &sqlmsglen); + } + if (strstr(msg, DATA_NOT_SEND_ERROR) || strstr(msg, NO_CONNECTION_ERROR)) { + ret = true; + } + free(msg); + return ret; +} + +static bool isNullStringError(SQLSMALLINT type, SQLHDBC hdbc) { + bool ret = false; + char status[16]; + int buflen = 1024; + char *msg = (char *)malloc(buflen); + msg[0] = '\0'; + SQLSMALLINT sqlmsglen; + SQLINTEGER sqlerr; + SQLGetDiagRec(type, hdbc, 1, (SQLCHAR *)status, &sqlerr, (SQLCHAR *)msg, + buflen - 1, &sqlmsglen); + if (buflen < sqlmsglen) { + free(msg); + buflen = sqlmsglen; + msg = (char *)malloc(buflen); + msg[0] = '\0'; + SQLGetDiagRec(type, hdbc, 1, (SQLCHAR *)status, &sqlerr, (SQLCHAR *)msg, + buflen - 1, &sqlmsglen); + } + if (strstr(msg, NULL_STRING_ERROR)) { + ret = true; + } + free(msg); + return ret; +} + +static void replace(std::string &src, const char *orgstr, const char *newstr) { + std::string::size_type pos = src.find(orgstr); + while (pos != std::string::npos) { + src.replace(pos, strlen(orgstr), newstr); + + pos = src.find(orgstr); + } +} + +static bool replaceonce(std::string &src, const char *orgstr, + const char *newstr) { + bool ret = false; + std::string::size_type pos = src.find(orgstr); + if (pos != std::string::npos) { + src.replace(pos, strlen(orgstr), newstr); + ret = true; + } + return ret; +} + +static std::string trim(const std::string &s) { + static const char *whiteSpace = " \t\r\n"; + + if (s.empty()) { + return s; + } + + std::string::size_type b = s.find_first_not_of(whiteSpace); + if (std::string::npos == b) { + return ""; + } + + std::string::size_type e = s.find_last_not_of(whiteSpace); + + return std::string(s, b, e - b + 1); +} + +static int getLine(std::istream &in, std::string &strLine) { + strLine = ""; + int count = 0; + bool lineFlag = false; + char ch; + + do { + ch = in.get(); + if (!in.good()) { + if (count > 0) { + return count; + } else { + return -1; + } + } + + switch (ch) { + case '\r': + break; + case '\n': + lineFlag = true; + break; + default: + strLine += ch; + count++; + break; + } + } while (!lineFlag); + + return count; +} + +static void loadKvList(std::istream &in, std::list &replaceKvList) { + std::string fullLine, command; + std::string leftSide, rightSide; + std::string::size_type length; + + while (getLine(in, fullLine) != -1) { + length = fullLine.find('#'); + if (std::string::npos == length) { + command = fullLine; + } else if (length > 0) { + command = fullLine.substr(0, length); + } else { + continue; + } + + command = trim(command); + + length = command.find('='); + if (std::string::npos != length) { + if ((length + 1) != command.size()) { + leftSide = trim(command.substr(0, length)); + rightSide = trim(command.substr(length + 1)); + } else { + leftSide = trim(command.substr(0, length)); + rightSide = ""; + } + } else { + continue; + } + + ReplaceKV replaceKv; + replaceKv.key = leftSide; + replaceKv.value = rightSide; + replaceKvList.push_back(replaceKv); + } +} + +static void loadKvList(const char *kvListFile, + std::list &replaceKvList) { + std::ifstream inputStream; + inputStream.open(kvListFile); + if (!inputStream.fail()) { + loadKvList(inputStream, replaceKvList); + inputStream.close(); + } else { + printf("cannot open kv file %s\n", kvListFile); + } +} + +static void loadKvMap(std::istream &in, + std::map &replaceKvMap) { + std::string fullLine, command; + std::string leftSide, rightSide; + std::string::size_type length; + + while (getLine(in, fullLine) != -1) { + length = fullLine.find('#'); + if (std::string::npos == length) { + command = fullLine; + } else if (length > 0) { + command = fullLine.substr(0, length); + } else { + continue; + } + + command = trim(command); + + length = command.find('='); + if (std::string::npos != length) { + if ((length + 1) != command.size()) { + leftSide = trim(command.substr(0, length)); + rightSide = trim(command.substr(length + 1, command.size() - length)); + } else { + leftSide = trim(command.substr(0, length)); + } + } else { + continue; + } + + replaceKvMap[leftSide] = rightSide; + } +} + +static void loadKvMap(const char *kvListFile, + std::map &replaceKvMap) { + std::ifstream inputStream; + inputStream.open(kvListFile); + if (!inputStream.fail()) { + loadKvMap(inputStream, replaceKvMap); + inputStream.close(); + } else { + printf("cannot open kv file %s\n", kvListFile); + } +} + +static std::list getCreateTableReplaceKV() { + std::list ret; + std::string replaceKvFile; + const char *homedir = getenv(MYSQL_TO_OPENGAUSS_HOME_DIR); + if (!homedir) { + return ret; + } + + replaceKvFile = homedir; + replaceKvFile += "/conf/createtablerkv.prop"; + loadKvList(replaceKvFile.c_str(), ret); + return ret; +} + +static std::list getCreateFunctionReplaceKV() { + std::list ret; + std::string replaceKvFile; + const char *homedir = getenv(MYSQL_TO_OPENGAUSS_HOME_DIR); + if (!homedir) { + return ret; + } + + replaceKvFile = homedir; + replaceKvFile += "/conf/createfunctionrkv.prop"; + loadKvList(replaceKvFile.c_str(), ret); + return ret; +} + +static std::list getCreateFunctionTypeKV() { + std::list ret; + std::string replaceKvFile; + const char *homedir = getenv(MYSQL_TO_OPENGAUSS_HOME_DIR); + if (!homedir) { + return ret; + } + + replaceKvFile = homedir; + replaceKvFile += "/conf/createfunctiontkv.prop"; + loadKvList(replaceKvFile.c_str(), ret); + return ret; +} + +ProcOdbc::ProcOdbc(const char *datasource) + : isconn_(false), loglevel_(INFO_LOG_LEVEL) { + strcpy(datasource_, datasource); + dbinfo_ = new DBInfo; + + currentArgc_ = 4; + currentArgv_ = (char **)malloc(sizeof(char *) * currentArgc_); + currentArgv_[0] = (char *)malloc(256); + strcpy(currentArgv_[0], "mysqltoopengauss"); + currentArgv_[1] = (char *)malloc(256); + strcpy(currentArgv_[1], "--compatible=postgresql"); + currentArgv_[2] = (char *)malloc(256); + strcpy(currentArgv_[2], "--default-character-set=utf8"); + currentArgv_[3] = (char *)malloc(256); + currentArgv_[3][0] = '\0'; +} + +ProcOdbc::~ProcOdbc() { + disConnect(); + delete (DBInfo *)dbinfo_; + freeCurrentArgv(); +} + +struct MysqlInfo ProcOdbc::getMysqlInfo() { + std::string confFile; + std::map confMap; + struct MysqlInfo ret; + const char *homedir = getenv(MYSQL_TO_OPENGAUSS_HOME_DIR); + if (!homedir) { + return ret; + } + + confFile = homedir; + confFile += "/conf/mysql2opengauss.prop"; + loadKvMap(confFile.c_str(), confMap); + ret.host = confMap["mysql_host"]; + ret.port = atoi(confMap["mysql_port"].c_str()); + ret.username = confMap["mysql_username"]; + ret.password = confMap["mysql_password"]; + ret.database = confMap["mysql_database"]; + ret.loglevel = atoi(confMap["loglevel"].c_str()); + ret.dsthost = confMap["opengauss_host"]; + ret.dstport = atoi(confMap["opengauss_port"].c_str()); + ret.dstdatabase = confMap["opengauss_database"]; + printf("from MySQL %s:%d database %s to openGauss %s:%d database %s\n", + confMap["mysql_host"].c_str(), atoi(confMap["mysql_port"].c_str()), + confMap["mysql_database"].c_str(), confMap["opengauss_host"].c_str(), + atoi(confMap["opengauss_port"].c_str()), + confMap["opengauss_database"].c_str()); + + return ret; +} + +bool ProcOdbc::isConnected() { return isconn_; } + +bool ProcOdbc::connect() { + if (isconn_) { + // 宸茬粡杩炴帴 + return isconn_; + } + + DBInfo *dbinfo = (DBInfo *)dbinfo_; + // 1. 鐢宠鐜鍙ユ焺 + dbinfo->V_OD_erg = + SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &dbinfo->V_OD_Env); + if ((dbinfo->V_OD_erg != SQL_SUCCESS) && + (dbinfo->V_OD_erg != SQL_SUCCESS_WITH_INFO)) { + printf("Error AllocHandle SQL_HANDLE_ENV\n"); + return isconn_; + } + // 2. 璁剧疆鐜灞炴э紙鐗堟湰淇℃伅锛 + SQLSetEnvAttr(dbinfo->V_OD_Env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, + 0); + // 3. 鐢宠杩炴帴鍙ユ焺 + dbinfo->V_OD_erg = + SQLAllocHandle(SQL_HANDLE_DBC, dbinfo->V_OD_Env, &dbinfo->V_OD_hdbc); + if ((dbinfo->V_OD_erg != SQL_SUCCESS) && + (dbinfo->V_OD_erg != SQL_SUCCESS_WITH_INFO)) { + SQLFreeHandle(SQL_HANDLE_ENV, dbinfo->V_OD_Env); + printf("Error AllocHandle SQL_HANDLE_DBC\n"); + printDBError(SQL_HANDLE_ENV, dbinfo->V_OD_Env, NULL); + return isconn_; + } + // 4. 璁剧疆杩炴帴灞炴 + SQLSetConnectAttr(dbinfo->V_OD_hdbc, SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0); + // 5. + // odbc.ini鏂囦欢涓凡缁忛厤缃簡鐢ㄦ埛鍚嶅瘑鐮侊紝閭d箞杩欓噷鍙互鐣欑┖锛""锛夛紱浣嗘槸涓嶅缓璁繖涔堝仛锛屽洜涓轰竴鏃dbc.ini鏉冮檺绠$悊涓嶅杽锛屽皢瀵艰嚧鏁版嵁搴撶敤鎴峰瘑鐮佹硠闇层 + printf("datasource: %s\n", datasource_); + dbinfo->V_OD_erg = + SQLConnect(dbinfo->V_OD_hdbc, (SQLCHAR *)datasource_, SQL_NTS, + (SQLCHAR *)"", SQL_NTS, (SQLCHAR *)"", SQL_NTS); + if ((dbinfo->V_OD_erg != SQL_SUCCESS) && + (dbinfo->V_OD_erg != SQL_SUCCESS_WITH_INFO)) { + printf("Error SQLConnect %d\n", (int)(dbinfo->V_OD_erg)); + printDBError(SQL_HANDLE_DBC, dbinfo->V_OD_hdbc, NULL); + SQLFreeHandle(SQL_HANDLE_ENV, dbinfo->V_OD_Env); + return isconn_; + } + printf("Connected !\n"); + + // 6. 璁剧疆璇彞灞炴 + SQLSetStmtAttr(dbinfo->V_OD_hstmt, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER *)3, + 0); + // 7. 鐢宠璇彞鍙ユ焺 + SQLAllocHandle(SQL_HANDLE_STMT, dbinfo->V_OD_hdbc, &dbinfo->V_OD_hstmt); + + isconn_ = true; + + return isconn_; +} + +void ProcOdbc::disConnect() { + if (!isconn_) { + // 娌℃湁杩炴帴锛岀洿鎺ヨ繑鍥 + return; + } + isconn_ = false; + + DBInfo *dbinfo = (DBInfo *)dbinfo_; + + // 16. 鏂紑鏁版嵁婧愯繛鎺ュ苟閲婃斁鍙ユ焺璧勬簮 + SQLFreeHandle(SQL_HANDLE_STMT, dbinfo->V_OD_hstmt); + SQLDisconnect(dbinfo->V_OD_hdbc); + SQLFreeHandle(SQL_HANDLE_DBC, dbinfo->V_OD_hdbc); + SQLFreeHandle(SQL_HANDLE_ENV, dbinfo->V_OD_Env); +} + +bool ProcOdbc::reConnect() { + disConnect(); + return connect(); +} + +std::list ProcOdbc::procCreateTableSql(const char *sql, + const char *incrementkey) { + std::string retsql = sql; + std::string ik; + if (incrementkey != NULL) { + ik = incrementkey; + } + replaceCreateTableSql(retsql); + return procCreateTableSqlLine(retsql, ik); +} + +std::string ProcOdbc::procProcedureFunctionSql(const char *sql, + bool &isfunction) { + static std::list funrkv; + static std::list funtkv; + std::string ret; + std::string sqlstr = sql; + replace(sqlstr, "`", "\""); + replace(sqlstr, "\r", ""); + std::string::size_type pos = sqlstr.find(SQLK_PROCEDURE); + std::string::size_type pos2 = sqlstr.find(SQLK_PROCEDURE_DEF_END, pos + 1); + if ((pos != std::string::npos) && (pos2 != std::string::npos)) { + isfunction = false; + ret = "CREATE "; + ret += sqlstr.substr(pos, pos2 - pos + 1); + ret += " IS "; + ret += sqlstr.substr(pos2 + 1); + } else { + isfunction = true; + if (funrkv.empty()) { + funrkv = getCreateFunctionReplaceKV(); + funtkv = getCreateFunctionTypeKV(); + } + + // 鏇挎崲涓鑸俊鎭紝鍙崲涓娆 + for (std::list::iterator iter = funrkv.begin(); + iter != funrkv.end(); iter++) { + if (replaceonce(sqlstr, iter->key.c_str(), iter->value.c_str())) { + break; + } + } + + // 鏇挎崲绫诲瀷锛屽彧鎹竴娆 + for (std::list::iterator iter = funtkv.begin(); + iter != funtkv.end(); iter++) { + if (replaceonce(sqlstr, iter->key.c_str(), iter->value.c_str())) { + break; + } + } + + pos = sqlstr.find(SQLK_FUNCTION); + pos2 = sqlstr.find(SQLK_FUNCTION_DEF_END, pos + 1); + if ((pos != std::string::npos) && (pos2 != std::string::npos)) { + ret = "CREATE "; + ret += sqlstr.substr(pos, pos2 - pos + 1); + ret += " AS $$ "; + ret += sqlstr.substr(pos2 + 1); + ret += "; $$ LANGUAGE plpgsql"; + } + } + return ret; +} + +std::string &ProcOdbc::replaceCreateTableSql(std::string &sql) { + static std::list replaceKVs; + if (replaceKVs.empty()) { + replaceKVs = getCreateTableReplaceKV(); + } + for (std::list::iterator iter = replaceKVs.begin(); + iter != replaceKVs.end(); iter++) { + replace(sql, iter->key.c_str(), iter->value.c_str()); + } + formatByteaAtCreateTableSql(sql); + formatSetAtCreateTableSql(sql); + return sql; +} + +std::list ProcOdbc::procCreateTableSqlLine( + const std::string &sql, const std::string &incrementkey) { + std::list ret; + std::string table; + std::string outputsql; + std::string tmp; + std::string tmp2; + std::string::size_type tmppos; + std::string::size_type tmppos2; + std::string::size_type tmppos3; + std::string::size_type tmppos4; + std::string::size_type pos = 0; + std::string::size_type nextpos; + bool iskeyline; + int createTableLen = strlen(SQLK_CREATE_TABLE); + int uniqueKeyLen = strlen(SQLK_UNIQUE_KEY); + int keyLen = strlen(SQLK_KEY); + int commentLen = strlen(SQLK_COMMENT); + int doubleBracketLen = strlen(SQLK_DOUBLE_BRACKET); + nextpos = sql.find('\n', pos); + while (nextpos != std::string::npos) { + iskeyline = false; + tmp = sql.substr(pos, nextpos - pos); + + // CREATE TABLE + tmppos = tmp.find(SQLK_CREATE_TABLE); + if (tmppos != std::string::npos) { + tmppos2 = tmp.find('('); + if (tmppos2 != std::string::npos) { + table = tmp.substr(tmppos + createTableLen, + tmppos2 - tmppos - createTableLen - 1); + } + iskeyline = true; + } else { + // double bracket + tmppos = tmp.find(SQLK_DOUBLE_BRACKET); + if (tmppos != std::string::npos) { + tmppos2 = tmp.find(SQLK_DOUBLE_BRACKET_END, tmppos + doubleBracketLen); + if (tmppos2 != std::string::npos) { + tmp2 = tmp.substr(0, tmppos); + tmp2 += " double precision "; + tmp2 += tmp.substr(tmppos2 + strlen(SQLK_DOUBLE_BRACKET_END)); + tmp = tmp2; + } + } + + // COMMENT + tmppos = tmp.find(SQLK_COMMENT); + if (tmppos != std::string::npos) { + tmppos2 = tmp.find('\'', tmppos + commentLen); + tmppos3 = tmp.find('"'); + tmppos4 = tmp.find('"', tmppos3 + 2); + if ((tmppos2 != std::string::npos) && (tmppos3 != std::string::npos) && + (tmppos4 != std::string::npos)) { + tmp2 = "COMMENT ON COLUMN "; + tmp2 += table + "."; + tmp2 += tmp.substr(tmppos3, tmppos4 - tmppos3 + 1); + tmp2 += " IS "; + tmp2 += tmp.substr(tmppos + commentLen - 1, + tmppos2 + 2 - tmppos - commentLen); + ret.push_back(tmp2); + } + tmp = tmp.substr(0, tmppos) + ","; + } else { + // UNIQUE KEY + tmppos = tmp.find(SQLK_UNIQUE_KEY); + if (tmppos != std::string::npos) { + tmppos2 = tmp.find('('); + if (tmppos2 != std::string::npos) { + tmp2 = " CONSTRAINT"; + tmp2 += " \""; + tmp2 += table.substr(1, table.size() - 2); + tmp2 += "_"; + tmp2 += tmp.substr(tmppos + uniqueKeyLen + 1, + tmppos2 - tmppos - uniqueKeyLen - 1); + tmp2 += "UNIQUE "; + tmp2 += tmp.substr(tmppos2); + tmp = tmp2; + } + iskeyline = true; + } else { + // PRIMARY KEY + tmppos = tmp.find(SQLK_PRIMARY_KEY); + if (tmppos != std::string::npos) { + // PRIMARY KEY涓嶉渶瑕佸鐞 + iskeyline = true; + } else { + // FOREIGN KEY + tmppos = tmp.find(SQLK_FOREIGN_KEY); + if (tmppos != std::string::npos) { + // FOREIGN KEY涓嶉渶瑕佸鐞 + iskeyline = true; + } else { + // KEY + tmppos = tmp.find(SQLK_KEY); + if (tmppos != std::string::npos) { + tmppos2 = tmp.find('('); + if (tmppos2 != std::string::npos) { + tmppos3 = tmp.find(')', tmppos2); + if (tmppos3 != std::string::npos) { + tmp2 = "CREATE INDEX"; + tmp2 += " \""; + tmp2 += table.substr(1, table.size() - 2); + tmp2 += "_"; + tmp2 += tmp.substr(tmppos + keyLen + 1, + tmppos2 - tmppos - keyLen - 2); + tmp2 += " ON "; + tmp2 += table; + tmp2 += tmp.substr(tmppos2, tmppos3 - tmppos2 + 1); + ret.push_back(tmp2); + tmp = ""; + } + } + iskeyline = true; + } + } + } + } + } + } + + if (!tmp.empty()) { + if (!iskeyline && !incrementkey.empty()) { + if (tmp.find(incrementkey) != std::string::npos) { + // 鑷涓婚敭 + tmppos = tmp.rfind(","); + if (tmppos != std::string::npos) { + tmp = tmp.substr(0, tmppos) + " default nextval('seq_" + + table.substr(1, table.size() - 2) + "_" + + incrementkey.substr(1, incrementkey.size() - 2) + "'),"; + } else { + tmp = tmp.substr(0, tmppos) + " default nextval('seq_" + + table.substr(1, table.size() - 2) + "_" + + incrementkey.substr(1, incrementkey.size() - 2) + "')"; + } + } + } + outputsql += tmp + "\n"; + } + + pos = nextpos + 1; + nextpos = sql.find('\n', pos); + } + if (outputsql.at(outputsql.size() - 2) == ',') { + outputsql = outputsql.substr(0, outputsql.size() - 2) + "\n"; + } + outputsql += sql.substr(pos); + ret.push_front(outputsql); + + if (!incrementkey.empty()) { + // 濡傛灉鏈夎嚜澧炰富閿紝澧炲姞鑷涓婚敭鍒涘缓璇彞 + outputsql = "create SEQUENCE seq_"; + outputsql += table.substr(1, table.size() - 2) + "_" + + incrementkey.substr(1, incrementkey.size() - 2); + outputsql += " cache 100"; + ret.push_front(outputsql); + + outputsql = "alter SEQUENCE seq_"; + outputsql += table.substr(1, table.size() - 2) + "_" + + incrementkey.substr(1, incrementkey.size() - 2); + outputsql += " OWNED by "; + outputsql += table; + outputsql += "."; + outputsql += incrementkey; + ret.push_back(outputsql); + } + return ret; +} + +std::string &ProcOdbc::formatByteaAtCreateTableSql(std::string &sql) { + std::string::size_type pos1 = sql.find(SQLK_BYTEA_START); + std::string::size_type pos2; + while (pos1 != std::string::npos) { + pos2 = sql.find(SQLK_BYTEA_END, pos1 + 1); + if (pos2 == std::string::npos) { + break; + } + sql.replace(pos1, pos2 - pos1 + strlen(SQLK_BYTEA_END), SQLK_BYTEA); + pos1 = sql.find(SQLK_BYTEA_START); + } + return sql; +} + +std::string &ProcOdbc::formatSetAtCreateTableSql(std::string &sql) { + std::string::size_type pos1 = sql.find(SQLK_SET_START); + std::string::size_type pos2; + while (pos1 != std::string::npos) { + pos2 = sql.find(SQLK_SET_END, pos1 + 1); + if (pos2 == std::string::npos) { + break; + } + sql.replace(pos1, pos2 - pos1 + strlen(SQLK_SET_END), SQLK_TEXT); + pos1 = sql.find(SQLK_SET_START); + } + return sql; +} + +char **ProcOdbc::getCurrentArgv() { return currentArgv_; } + +int ProcOdbc::getCurrentArgc() { return currentArgc_ - 1; } + +void ProcOdbc::setLoglevel(int loglevel) { loglevel_ = loglevel; }; + +bool ProcOdbc::executeCreateTableSql(const char *sql, DBError *dbError) { + bool ret = true; + DBInfo *dbinfo = (DBInfo *)dbinfo_; + + // 鐩存帴鎵цSQL璇彞銆 + if (loglevel_ <= INFO_LOG_LEVEL) { + printf("start: execute sql %s\n", sql); + } + int execret = execSqlStably(sql); + if ((execret != SQL_SUCCESS) && (execret != SQL_SUCCESS_WITH_INFO)) { + printf("finished error(%d): execute sql %s\n", (int)execret, sql); + printDBError(SQL_HANDLE_STMT, dbinfo->V_OD_hstmt, dbError); + ret = false; + + // if (dbError) { + // if (!dbError->alreadyExistError) { + // errcreatetablesqls_.push_back(sql); + // } + //} + } else { + if (loglevel_ <= INFO_LOG_LEVEL) { + printf("finished successfully: execute sql %s\n", sql); + } + } + return ret; +} + +bool ProcOdbc::executeInsertSql(const char *sql, DBError *dbError) { + bool ret = true; + int insertIntoLen = strlen(SQLK_INSERT_INTO); + std::string sqlstr = sql; + std::string insertSql; + + // 鎷嗗垎鎴愬鏉ql璇彞 + std::string::size_type pos = 0; + std::string::size_type pos2 = sqlstr.find(SQLK_INSERT_INTO, insertIntoLen); + while (true) { + if (pos2 == std::string::npos) { + insertSql = sqlstr.substr(pos); + ret = executeInsertSql(insertSql.c_str(), "' '", dbError); + break; + } else { + insertSql = sqlstr.substr(pos, pos2 - pos); + ret = executeInsertSql(insertSql.c_str(), "' '", dbError); + } + pos = pos2 + 1; + pos2 = sqlstr.find(SQLK_INSERT_INTO, pos + insertIntoLen); + } + return ret; +} + +bool ProcOdbc::executeInsertSql(const char *sql, const char *replaceNullStr, + DBError *dbError) { + bool ret = true; + std::string sqlstr; + DBInfo *dbinfo = (DBInfo *)dbinfo_; + + // 鐩存帴鎵цSQL璇彞銆 + if (loglevel_ <= INFO_LOG_LEVEL) { + printf("start: execute sql %s\n", sql); + } + int execret = execSqlStably(sql); + if ((execret != SQL_SUCCESS) && (execret != SQL_SUCCESS_WITH_INFO)) { + if (isNullStringError(SQL_HANDLE_STMT, dbinfo->V_OD_hstmt)) { + // 闈炵┖瀛楃涓茬害鏉燂紝鎶''鏀规垚replaceNullStr锛屽苟閲嶈瘯涓娆 + printDBError(SQL_HANDLE_STMT, dbinfo->V_OD_hstmt, dbError); + printf("replace '' by %s and retry\n", replaceNullStr); + + sqlstr = sql; + replace(sqlstr, "''", replaceNullStr); + execret = execSqlStably(sqlstr.c_str()); + } + } + + if ((execret != SQL_SUCCESS) && (execret != SQL_SUCCESS_WITH_INFO)) { + printf("finished error(%d): execute sql %s\n", (int)execret, sql); + printDBError(SQL_HANDLE_STMT, dbinfo->V_OD_hstmt, dbError); + ret = false; + } else { + if (loglevel_ <= INFO_LOG_LEVEL) { + printf("finished successfully: execute sql %s\n", sql); + } + } + return ret; +} + +bool ProcOdbc::executeCreateViewSql(const char *sql, DBError *dbError) { + bool ret = true; + DBInfo *dbinfo = (DBInfo *)dbinfo_; + + // 鐩存帴鎵цSQL璇彞銆 + if (loglevel_ <= INFO_LOG_LEVEL) { + printf("start: execute sql %s\n", sql); + } + int execret = execSqlStably(sql); + if ((execret != SQL_SUCCESS) && (execret != SQL_SUCCESS_WITH_INFO)) { + printf("finished error(%d): execute sql %s\n", (int)execret, sql); + printDBError(SQL_HANDLE_STMT, dbinfo->V_OD_hstmt, dbError); + ret = false; + } else { + if (loglevel_ <= INFO_LOG_LEVEL) { + printf("finished successfully: execute sql %s\n", sql); + } + } + return ret; +} + +bool ProcOdbc::executeGeneralSql(const char *sql, DBError *dbError) { + bool ret = true; + DBInfo *dbinfo = (DBInfo *)dbinfo_; + + // 鐩存帴鎵цSQL璇彞銆 + if (loglevel_ <= INFO_LOG_LEVEL) { + printf("start: execute sql %s\n", sql); + } + int execret = execSqlStably(sql); + if ((execret != SQL_SUCCESS) && (execret != SQL_SUCCESS_WITH_INFO)) { + printf("finished error(%d): execute sql %s\n", (int)execret, sql); + printDBError(SQL_HANDLE_STMT, dbinfo->V_OD_hstmt, dbError); + ret = false; + } else { + if (loglevel_ <= INFO_LOG_LEVEL) { + printf("finished successfully: execute sql %s\n", sql); + } + } + return ret; +} + +int ProcOdbc::execSqlStably(const char *sql) { + int retry = 30; + DBInfo *dbinfo = (DBInfo *)dbinfo_; + SQLINTEGER execret; + while (retry-- > 0) { + execret = SQLExecDirect(dbinfo->V_OD_hstmt, (SQLCHAR *)sql, SQL_NTS); + if ((execret != SQL_SUCCESS) && (execret != SQL_SUCCESS_WITH_INFO)) { + if (isConnectionError(SQL_HANDLE_STMT, dbinfo->V_OD_hstmt)) { + // 杩炴帴閿欒锛岄噸鏂拌繛鎺ワ紝骞堕噸璇 + printDBError(SQL_HANDLE_STMT, dbinfo->V_OD_hstmt, NULL); + if (retry > 0) { + printf("reconnect and retry(%d)...", retry); + sleep(2); + } + reConnect(); + } else { + // 鍏朵粬閿欒锛岄鍑哄惊鐜 + break; + } + } else { + // 鎵ц鎴愬姛锛岄鍑哄惊鐜 + break; + } + } + return (int)execret; +} + +bool ProcOdbc::isBinaryBlobChar(unsigned char ch) { + return (ch <= 31) || (ch >= 127) /*&& (ch <= 255)*/ || (ch == 39) || + (ch == 92); +} + +std::list ProcOdbc::clearAndGetErrCreateTableSqls() { + std::list tmp; + std::list ret = errcreatetablesqls_; + errcreatetablesqls_ = tmp; + return ret; +} + +bool ProcOdbc::executeLeftErrorSqls() { + bool ret = true; + if (!executeLeftErrorCreateTableSqls()) { + ret = false; + } + + return ret; +} + +bool ProcOdbc::executeLeftErrorCreateTableSqls() { + std::list sqls = clearAndGetErrCreateTableSqls(); + int retry = 1000; + struct DBError dbError; + while (sqls.size() > 0) { + // 鏈夐敊璇痵ql鏃朵笉鏂墽琛 + printf("retry...\n"); + for (std::list::iterator iter = sqls.begin(); + iter != sqls.end(); iter++) { + printf("retry sql %s\n", iter->c_str()); + executeCreateTableSql(iter->c_str(), &dbError); + } + + if (--retry == 0) { + if (hasErrorCreateTableSqls()) { + // 杩樹粛鐒舵湁閿欒 + printErrorCreateTableSqls(); + } + break; + } + + sqls = clearAndGetErrCreateTableSqls(); + } + return !hasErrorCreateTableSqls(); +} + +bool ProcOdbc::hasErrorSqls() { return hasErrorCreateTableSqls(); } + +bool ProcOdbc::hasErrorCreateTableSqls() { + return errcreatetablesqls_.size() > 0; +} + +void ProcOdbc::printErrorSqls() { printErrorCreateTableSqls(); } + +void ProcOdbc::printErrorCreateTableSqls() { + if (errcreatetablesqls_.size() > 0) { + printf("error sql count: %d\n", (int)errcreatetablesqls_.size()); + for (std::list::iterator iter = errcreatetablesqls_.begin(); + iter != errcreatetablesqls_.end(); iter++) { + printf(" %s\n", iter->c_str()); + } + } +} + +void ProcOdbc::freeCurrentArgv() { + for (int index = 0; index < currentArgc_; index++) { + free(currentArgv_[index]); + } + free(currentArgv_); +} + +}; // namespace DMODBC diff --git a/MySQL2openGauss/src/ProcOdbc.h b/MySQL2openGauss/src/ProcOdbc.h new file mode 100644 index 0000000000000000000000000000000000000000..a204149c3a9288072ce1e9109ee2da9dd0604618 --- /dev/null +++ b/MySQL2openGauss/src/ProcOdbc.h @@ -0,0 +1,84 @@ +#ifndef _PROCODBC_H_ +#define _PROCODBC_H_ + +#include +#include + +#define MYSQL_TO_OPENGAUSS_HOME_DIR "MYSQL_TO_OPENGAUSS_HOME" + +namespace DMODBC { + +struct DBError { + bool alreadyExistError; + std::string error; +}; + +struct MysqlInfo { + std::string host; + int port; + std::string username; + std::string password; + std::string database; + std::string dsthost; + int dstport; + std::string dstdatabase; + int loglevel; +}; + +class ProcOdbc { + public: + ProcOdbc(const char* datasource); + virtual ~ProcOdbc(); + + static struct MysqlInfo getMysqlInfo(); + + bool isConnected(); + + bool connect(); + void disConnect(); + bool reConnect(); + + std::list procCreateTableSql(const char* sql, + const char* incrementkey); + std::string procProcedureFunctionSql(const char* sql, bool& isfunction); + + bool executeCreateTableSql(const char* sql, DBError* dbError); + bool executeInsertSql(const char* sql, DBError* dbError); + bool executeCreateViewSql(const char* sql, DBError* dbError); + bool executeGeneralSql(const char* sql, DBError* dbError); + + std::list clearAndGetErrCreateTableSqls(); + bool executeLeftErrorSqls(); + bool hasErrorSqls(); + void printErrorSqls(); + char** getCurrentArgv(); + int getCurrentArgc(); + void setLoglevel(int loglevel); + + private: + std::string& replaceCreateTableSql(std::string& sql); + std::list procCreateTableSqlLine( + const std::string& sql, const std::string& incrementkey); + std::string& formatByteaAtCreateTableSql(std::string& sql); + std::string& formatSetAtCreateTableSql(std::string& sql); + bool executeLeftErrorCreateTableSqls(); + bool hasErrorCreateTableSqls(); + void printErrorCreateTableSqls(); + void freeCurrentArgv(); + bool executeInsertSql(const char* sql, const char* replaceNullStr, DBError* dbError); + int execSqlStably(const char* sql); + static bool isBinaryBlobChar(unsigned char ch); + + private: + char datasource_[256]; + bool isconn_; + void* dbinfo_; + char** currentArgv_; + int currentArgc_; + std::list errcreatetablesqls_; + int loglevel_; +}; + +}; // namespace DMODBC + +#endif diff --git a/MySQL2openGauss/src/mysqltoopengauss.cpp b/MySQL2openGauss/src/mysqltoopengauss.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8e44a2ec407aeed30c2d36596270bd434e3d51c9 --- /dev/null +++ b/MySQL2openGauss/src/mysqltoopengauss.cpp @@ -0,0 +1,5646 @@ +#define DUMP_VERSION "10.13" + +#include +#include +#include +#include +#include +#include +#include +#include /* ORACLE_WELCOME_COPYRIGHT_NOTICE */ + +#include +#include +#include + +#include "ProcMetrics.h" +#include "ProcOdbc.h" +#include "client_priv.h" +#include "my_default.h" +#include "mysql.h" +#include "mysql_version.h" +#include "mysqld_error.h" + +/* Exit codes */ + +#define EX_USAGE 1 +#define EX_MYSQLERR 2 +#define EX_CONSCHECK 3 +#define EX_EOM 4 +#define EX_EOF 5 /* ferror for output file was got */ +#define EX_ILLEGAL_TABLE 6 + +/* index into 'show fields from table' */ + +#define SHOW_FIELDNAME 0 +#define SHOW_TYPE 1 +#define SHOW_NULL 2 +#define SHOW_DEFAULT 4 +#define SHOW_EXTRA 5 + +/* Size of buffer for dump's select query */ +#define QUERY_LENGTH 1536 + +/* Size of comment buffer. */ +#define COMMENT_LENGTH 2048 + +/* ignore table flags */ +#define IGNORE_NONE 0x00 /* no ignore */ +#define IGNORE_DATA 0x01 /* don't dump data for this table */ + +/* Maximum number of fields per table */ +#define MAX_FIELDS 4000 + +static DMODBC::ProcOdbc *sg_procOdbc; +static DMODBC::ProcMetrics *sg_procMetrics; +static struct DMODBC::MysqlInfo sg_mysqlInfo; +static std::map sg_createSuccessTables; +static std::list sg_createFailedTables; +static bool sg_firstdumptables; +static pthread_key_t sg_connectionkey; +// static std::map > +// sg_createFailedTableSqls; + +static void add_load_option(DYNAMIC_STRING *str, const char *option, + const char *option_value); +// static ulong find_set(TYPELIB *lib, const char *x, size_t length, +// char **err_pos, uint *err_len); +static char *alloc_query_str(size_t size); + +static void field_escape(DYNAMIC_STRING *in, const char *from); + +#define MYSQL_OPT_MASTER_DATA_EFFECTIVE_SQL 1 +#define MYSQL_OPT_MASTER_DATA_COMMENTED_SQL 2 +#define MYSQL_OPT_SLAVE_DATA_EFFECTIVE_SQL 1 +#define MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL 2 + +my_bool verbose = 0; +char *opt_ignore_error = 0; +my_bool opt_force = 0; +my_bool opt_comments = 1; +my_bool opt_comments_used = 1; +char *path = 0; +my_bool opt_xml = 0; + +class MySQLContext { + public: + MySQLContext(); + void init_mysql_context(); + void write_header(FILE *sql_file, char *db_name); + void write_footer(FILE *sql_file); + my_bool get_one_option(int optid, const char *argument); + int get_options(int *argc, char ***argv); + int fetch_db_collation(const char *db_name, char *db_cl_name, int db_cl_size); + void free_resources(); + int mainproc(int argc, char **argv); + int switch_character_set_results(MYSQL *mysql, const char *cs_name); + int connect_to_db(char *host, char *user, char *passwd); + my_bool do_ignore_error(); + void maybe_exit(int error); + void dbDisconnect(char *host); + void unescape(FILE *file, char *pos, size_t length, DYNAMIC_STRING *output); + void unescapestr(char *pos, size_t length, DYNAMIC_STRING *output); + char *quote_name(const char *name, char *buff, my_bool force); + uint dump_events_for_db(char *db); + uint dump_routines_for_db(char *db); + uint get_table_structure(char *table, char *db, char *table_type, + char *ignore_flag, my_bool real_columns[]); + void dump_trigger_old(FILE *sql_file, MYSQL_RES *show_triggers_rs, + MYSQL_ROW *show_trigger_row, const char *table_name); + int dump_trigger(FILE *sql_file, MYSQL_RES *show_create_trigger_rs, + const char *db_name, const char *db_cl_name); + int dump_triggers_for_table(char *table_name, char *db_name); + void dump_table(char *table, char *db); + char *getTableName(int reset); + int dump_tablespaces_for_tables(char *db, char **table_names, int tables); + int dump_tablespaces(char *ts_where); + int is_ndbinfo(MYSQL *mysql, const char *dbname); + int dump_all_databases(); + int dump_databases(char **db_names); + int init_dumping(char *database, int init_func); + int init_dumping_tables(char *qdatabase); + int init_dumping_views(char *qdatabase); + int dump_all_tables_in_db(char *database); + my_bool dump_all_views_in_db(char *database); + char *get_actual_table_name(const char *old_table_name, MEM_ROOT *root); + int dump_selected_tables(char *db, char **table_names, int tables); + int dump_tablespaces_for_databases(char **databases); + int switch_db_collation(FILE *sql_file, const char *db_name, + const char *delimiter, const char *current_db_cl_name, + const char *required_db_cl_name, int *db_cl_altered); + int restore_db_collation(FILE *sql_file, const char *db_name, + const char *delimiter, const char *db_cl_name); + char *primary_key_fields(const char *table_name); + char check_if_ignore_table(const char *table_name, char *table_type); + int dump_all_tablespaces(); + my_bool get_view_structure(char *table, char *db); + int do_show_master_status(MYSQL *mysql_con); + int do_stop_slave_sql(MYSQL *mysql_con); + int add_stop_slave(void); + int add_slave_statements(void); + int do_show_slave_status(MYSQL *mysql_con); + int do_start_slave_sql(MYSQL *mysql_con); + int do_flush_tables_read_lock(MYSQL *mysql_con); + int do_unlock_tables(MYSQL *mysql_con); + int get_bin_log_name(MYSQL *mysql_con, char *buff_log_name, uint buff_len); + int purge_bin_logs_to(MYSQL *mysql_con, char *log_name); + int start_transaction(MYSQL *mysql_con); + ulong find_set(TYPELIB *lib, const char *x, size_t length, char **err_pos, + uint *err_len); + void print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row, + const char *prefix, const char *name, int string_value); + my_bool process_set_gtid_purged(MYSQL *mysql_con); + + private: + my_bool opt_no_create_info; + my_bool opt_no_data; + my_bool quick; + my_bool extended_insert; + my_bool lock_tables; + my_bool flush_logs; + my_bool flush_privileges; + my_bool opt_drop; + my_bool opt_keywords; + my_bool opt_lock; + my_bool opt_compress; + my_bool create_options; + my_bool opt_quoted; + my_bool opt_databases; + my_bool opt_alldbs; + my_bool opt_create_db; + my_bool opt_lock_all_tables; + my_bool opt_set_charset; + my_bool opt_dump_date; + my_bool opt_autocommit; + my_bool opt_disable_keys; + my_bool opt_delete_master_logs; + my_bool tty_password; + my_bool opt_single_transaction; + my_bool opt_compact; + my_bool opt_hex_blob; + my_bool opt_order_by_primary; + my_bool opt_ignore; + my_bool opt_complete_insert; + my_bool opt_drop_database; + my_bool opt_replace_into; + my_bool opt_dump_triggers; + my_bool opt_routines; + my_bool opt_tz_utc; + my_bool opt_slave_apply; + my_bool opt_include_master_host_port; + my_bool opt_events; + my_bool opt_alltspcs; + my_bool opt_notspcs; + my_bool opt_drop_trigger; + my_bool opt_secure_auth; + my_bool insert_pat_inited; + my_bool debug_info_flag; + my_bool debug_check_flag; + ulong opt_max_allowed_packet; + ulong opt_net_buffer_length; + MYSQL mysql_connection; + MYSQL *mysql; + DYNAMIC_STRING insert_pat; + char *opt_password; + char *current_user; + char *current_host; + char *fields_terminated; + char *lines_terminated; + char *enclosed; + char *opt_enclosed; + char *escaped; + char *where; + char *order_by; + char *opt_compatible_mode_str; + char *err_ptr; + char *log_error_file; + char **defaults_argv; + char compatible_mode_normal_str[255]; + /* Server supports character_set_results session variable? */ + my_bool server_supports_switching_charsets; + ulong opt_compatible_mode; + uint opt_enable_cleartext_plugin; + my_bool using_opt_enable_cleartext_plugin; + uint opt_mysql_port; + uint opt_master_data; + uint opt_slave_data; + uint my_end_arg; + char *opt_mysql_unix_port; + char *opt_bind_addr; + int first_error; +}; + +MySQLContext::MySQLContext() { init_mysql_context(); } + +void MySQLContext::init_mysql_context() { + verbose = 0; + opt_no_create_info = 0; + opt_no_data = 0; + quick = 1; + extended_insert = 1; + lock_tables = 1; + opt_force = 0; + flush_logs = 0; + flush_privileges = 0; + opt_drop = 1; + opt_keywords = 0; + opt_lock = 1; + opt_compress = 0; + create_options = 1; + opt_quoted = 0; + opt_databases = 0; + opt_alldbs = 0; + opt_create_db = 0; + opt_lock_all_tables = 0; + opt_set_charset = 1; + opt_dump_date = 1; + opt_autocommit = 0; + opt_disable_keys = 1; + opt_delete_master_logs = 0; + tty_password = 0; + opt_single_transaction = 0; + opt_compact = 0; + opt_hex_blob = 0; + opt_order_by_primary = 0; + opt_ignore = 0; + opt_complete_insert = 0; + opt_drop_database = 0; + opt_replace_into = 0; + opt_dump_triggers = 0; + opt_routines = 0; + opt_tz_utc = 1; + opt_slave_apply = 0; + opt_include_master_host_port = 0; + opt_events = 0; + opt_alltspcs = 0; + opt_notspcs = 0; + opt_drop_trigger = 0; + opt_secure_auth = TRUE; + insert_pat_inited = 0; + debug_info_flag = 0; + debug_check_flag = 0; + opt_max_allowed_packet = 24*1024*1024; + opt_net_buffer_length = 1024*1024L-1025; + mysql = 0; + opt_password = 0; + current_user = 0; + current_host = 0; + fields_terminated = 0; + lines_terminated = 0; + enclosed = 0; + opt_enclosed = 0; + escaped = 0; + where = 0; + order_by = 0; + opt_compatible_mode_str = 0; + err_ptr = 0; + log_error_file = NULL; + defaults_argv = 0; + server_supports_switching_charsets = TRUE; + opt_compatible_mode = 0; + opt_enable_cleartext_plugin = 0; + using_opt_enable_cleartext_plugin = 0; + opt_mysql_port = 0; + opt_master_data = 0; + opt_slave_data = 0; + my_end_arg = 0; + opt_mysql_unix_port = 0; + opt_bind_addr = NULL; + first_error = 0; +} + +static MySQLContext *create_connection_thread_context() { + MySQLContext *ret = new MySQLContext(); + pthread_setspecific(sg_connectionkey, ret); + return ret; +} + +static MySQLContext *get_connection_thread_context() { + return (MySQLContext *)pthread_getspecific(sg_connectionkey); +} + +static void release_connection_thread_context() { + MySQLContext *context = (MySQLContext *)pthread_getspecific(sg_connectionkey); + if (context) { + delete context; + } +} + +#include +#include +FILE *md_result_file = 0; +FILE *stderror_file = 0; + +const char *set_gtid_purged_mode_names[] = {"OFF", "AUTO", "ON", NullS}; +static TYPELIB set_gtid_purged_mode_typelib = { + array_elements(set_gtid_purged_mode_names) - 1, "", + set_gtid_purged_mode_names, NULL}; +static enum enum_set_gtid_purged_mode { + SET_GTID_PURGED_OFF = 0, + SET_GTID_PURGED_AUTO = 1, + SET_GTID_PURGED_ON = 2 +} opt_set_gtid_purged_mode = SET_GTID_PURGED_AUTO; + +#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY) +static char *shared_memory_base_name = 0; +#endif +static uint opt_protocol = 0; +static char *opt_plugin_dir = 0, *opt_default_auth = 0; + +DYNAMIC_ARRAY ignore_error; +static int parse_ignore_error(); + +/* +Dynamic_string wrapper functions. In this file use these +wrappers, they will terminate the process if there is +an allocation failure. +*/ +static void init_dynamic_string_checked(DYNAMIC_STRING *str, + const char *init_str, size_t init_alloc, + size_t alloc_increment); +static void dynstr_append_checked(DYNAMIC_STRING *dest, const char *src); +static void dynstr_set_checked(DYNAMIC_STRING *str, const char *init_str); +static void dynstr_append_mem_checked(DYNAMIC_STRING *str, const char *append, + size_t length); +static void dynstr_realloc_checked(DYNAMIC_STRING *str, size_t additional_size); + +/* + Constant for detection of default value of default_charset. + If default_charset is equal to mysql_universal_client_charset, then + it is the default value which assigned at the very beginning of main(). +*/ +static const char *mysql_universal_client_charset = "utf8"; +static char *default_charset; +static CHARSET_INFO *charset_info = &my_charset_latin1; +const char *default_dbug_option = "d:t:o,/tmp/mysqldump.trace"; +/* have we seen any VIEWs during table scanning? */ +my_bool seen_views = 0; +const char *compatible_mode_names[] = {"MYSQL323", + "MYSQL40", + "POSTGRESQL", + "ORACLE", + "MSSQL", + "DB2", + "MAXDB", + "NO_KEY_OPTIONS", + "NO_TABLE_OPTIONS", + "NO_FIELD_OPTIONS", + "ANSI", + NullS}; +#define MASK_ANSI_QUOTES \ + ((1 << 2) | /* POSTGRESQL */ \ + (1 << 3) | /* ORACLE */ \ + (1 << 4) | /* MSSQL */ \ + (1 << 5) | /* DB2 */ \ + (1 << 6) | /* MAXDB */ \ + (1 << 10) /* ANSI */ \ + ) +TYPELIB compatible_mode_typelib = {array_elements(compatible_mode_names) - 1, + "", compatible_mode_names, NULL}; + +HASH ignore_table; + +static const char *load_default_groups[] = {"mysqldump", "client", 0}; + +static void die(int error, const char *reason, ...); +static void maybe_die(int error, const char *reason, ...); +static void print_comment(FILE *sql_file, my_bool is_error, const char *format, + ...); +static char const *fix_identifier_with_newline(char const *object_name, + my_bool *freemem); + +/* + Print the supplied message if in verbose mode + + SYNOPSIS + verbose_msg() + fmt format specifier + ... variable number of parameters +*/ + +static void verbose_msg(const char *fmt, ...) { + va_list args; + DBUG_ENTER("verbose_msg"); + + if (!verbose) DBUG_VOID_RETURN; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + fflush(stderr); + + DBUG_VOID_RETURN; +} + +/* + exit with message if ferror(file) + + SYNOPSIS + check_io() + file - checked file +*/ + +void check_io(FILE *file) { + if (ferror(file) || errno == 5) die(EX_EOF, "Got errno %d on write", errno); +} + +static void print_version(void) { + printf("%s Ver %s Distrib %s, for %s (%s)\n", my_progname, DUMP_VERSION, + MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); +} /* print_version */ + +static void short_usage_sub(void) { + printf("Usage: %s [OPTIONS] database [tables]\n", my_progname); + printf("OR %s [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...]\n", + my_progname); + printf("OR %s [OPTIONS] --all-databases [OPTIONS]\n", my_progname); +} + +void MySQLContext::write_header(FILE *sql_file, char *db_name) { + if (opt_xml) { + fputs("\n", sql_file); + /* + Schema reference. Allows use of xsi:nil for NULL values and + xsi:type to define an element's data type. + */ + fputs("\n", sql_file); + check_io(sql_file); + } else if (!opt_compact) { + my_bool freemem = FALSE; + char const *text = fix_identifier_with_newline(db_name, &freemem); + + print_comment( + sql_file, 0, "-- MySQL dump %s Distrib %s, for %s (%s)\n--\n", + DUMP_VERSION, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); + print_comment(sql_file, 0, "-- Host: %s Database: %s\n", + current_host ? current_host : "localhost", text); + + if (freemem) my_free((void *)text); + + print_comment( + sql_file, 0, + "-- ------------------------------------------------------\n"); + print_comment(sql_file, 0, "-- Server version\t%s\n", + mysql_get_server_info(&mysql_connection)); + + if (opt_set_charset) + fprintf( + sql_file, + "\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" + "\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS " + "*/;" + "\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" + "\n/*!40101 SET NAMES %s */;\n", + default_charset); + + if (opt_tz_utc) { + fprintf(sql_file, "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;\n"); + fprintf(sql_file, "/*!40103 SET TIME_ZONE='+00:00' */;\n"); + } + + if (!path) { + fprintf(md_result_file, + "\ +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n\ +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;\n\ +"); + } + fprintf(sql_file, + "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='%s%s%s' */;\n" + "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;\n", + path ? "" : "NO_AUTO_VALUE_ON_ZERO", + compatible_mode_normal_str[0] == 0 ? "" : ",", + compatible_mode_normal_str); + check_io(sql_file); + } +} /* write_header */ + +void MySQLContext::write_footer(FILE *sql_file) { + if (opt_xml) { + fputs("\n", sql_file); + check_io(sql_file); + } else if (!opt_compact) { + if (opt_tz_utc) + fprintf(sql_file, "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;\n"); + + fprintf(sql_file, "\n/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;\n"); + if (!path) { + fprintf(md_result_file, + "\ +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n\ +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\n"); + } + if (opt_set_charset) + fprintf( + sql_file, + "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n" + "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n" + "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n"); + fprintf(sql_file, "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n"); + fputs("\n", sql_file); + + if (opt_dump_date) { + char time_str[20]; + get_date(time_str, GETDATE_DATE_TIME, 0); + print_comment(sql_file, 0, "-- Dump completed on %s\n", time_str); + } else + print_comment(sql_file, 0, "-- Dump completed\n"); + + check_io(sql_file); + } +} /* write_footer */ + +uchar *get_table_key(const char *entry, size_t *length, + my_bool not_used MY_ATTRIBUTE((unused))) { + *length = strlen(entry); + return (uchar *)entry; +} + +my_bool MySQLContext::get_one_option(int optid, const char *argument) { + switch (optid) { + case (int)OPT_COMPATIBLE: { + char buff[255]; + char *end = compatible_mode_normal_str; + int i; + ulong mode; + uint err_len; + + opt_quoted = 1; + opt_set_charset = 1; + opt_compatible_mode_str = (char *)argument; + opt_compatible_mode = find_set(&compatible_mode_typelib, argument, + strlen(argument), &err_ptr, &err_len); + if (err_len) { + strmake(buff, err_ptr, MY_MIN(sizeof(buff) - 1, err_len)); + fprintf(stderr, "Invalid mode to --compatible: %s\n", buff); + exit(1); + } +#if !defined(DBUG_OFF) + { + size_t size_for_sql_mode = 0; + const char **ptr; + for (ptr = compatible_mode_names; *ptr; ptr++) + size_for_sql_mode += strlen(*ptr); + size_for_sql_mode += sizeof(compatible_mode_names) - 1; + DBUG_ASSERT(sizeof(compatible_mode_normal_str) >= size_for_sql_mode); + } +#endif + mode = opt_compatible_mode; + for (i = 0, mode = opt_compatible_mode; mode; mode >>= 1, i++) { + if (mode & 1) { + end = my_stpcpy(end, compatible_mode_names[i]); + end = my_stpcpy(end, ","); + } + } + if (end != compatible_mode_normal_str) end[-1] = 0; + /* + Set charset to the default compiled value if it hasn't + been reset yet by --default-character-set=xxx. + */ + // if (default_charset == mysql_universal_client_charset) + // default_charset = (char *)MYSQL_DEFAULT_CHARSET_NAME; + break; + } + } + return 0; +} + +int MySQLContext::get_options(int *argc, char ***argv) { + if (mysql_get_option(NULL, MYSQL_OPT_MAX_ALLOWED_PACKET, + &opt_max_allowed_packet) || + mysql_get_option(NULL, MYSQL_OPT_NET_BUFFER_LENGTH, + &opt_max_allowed_packet)) { + exit(1); + } + + md_result_file = stdout; + my_getopt_use_args_separator = TRUE; + if (load_defaults("my", load_default_groups, argc, argv)) return 1; + my_getopt_use_args_separator = FALSE; + + defaults_argv = *argv; + + if (my_hash_init(&ignore_table, charset_info, 16, 0, 0, + (my_hash_get_key)get_table_key, my_free, 0, + PSI_NOT_INSTRUMENTED)) + return (EX_EOM); + /* Don't copy internal log tables */ + if (my_hash_insert(&ignore_table, + (uchar *)my_strdup(PSI_NOT_INSTRUMENTED, + "mysql.apply_status", MYF(MY_WME))) || + my_hash_insert(&ignore_table, + (uchar *)my_strdup(PSI_NOT_INSTRUMENTED, "mysql.schema", + MYF(MY_WME))) || + my_hash_insert(&ignore_table, + (uchar *)my_strdup(PSI_NOT_INSTRUMENTED, + "mysql.general_log", MYF(MY_WME))) || + my_hash_insert(&ignore_table, + (uchar *)my_strdup(PSI_NOT_INSTRUMENTED, "mysql.slow_log", + MYF(MY_WME)))) + return (EX_EOM); + + get_one_option(OPT_COMPATIBLE, "postgresql"); + + if (mysql_options(NULL, MYSQL_OPT_MAX_ALLOWED_PACKET, + &opt_max_allowed_packet) || + mysql_options(NULL, MYSQL_OPT_NET_BUFFER_LENGTH, + &opt_net_buffer_length)) { + exit(1); + } + + if (debug_info_flag) my_end_arg = MY_CHECK_ERROR | MY_GIVE_INFO; + if (debug_check_flag) my_end_arg = MY_CHECK_ERROR; + + if (!path && (enclosed || opt_enclosed || escaped || lines_terminated || + fields_terminated)) { + fprintf(stderr, "%s: You must use option --tab with --fields-...\n", + my_progname); + return (EX_USAGE); + } + + /* We don't delete master logs if slave data option */ + if (opt_slave_data) { + opt_lock_all_tables = !opt_single_transaction; + opt_master_data = 0; + opt_delete_master_logs = 0; + } + + /* Ensure consistency of the set of binlog & locking options */ + if (opt_delete_master_logs && !opt_master_data) + opt_master_data = MYSQL_OPT_MASTER_DATA_COMMENTED_SQL; + if (opt_single_transaction && opt_lock_all_tables) { + fprintf(stderr, + "%s: You can't use --single-transaction and " + "--lock-all-tables at the same time.\n", + my_progname); + return (EX_USAGE); + } + if (opt_master_data) { + opt_lock_all_tables = !opt_single_transaction; + opt_slave_data = 0; + } + if (opt_single_transaction || opt_lock_all_tables) lock_tables = 0; + if (enclosed && opt_enclosed) { + fprintf(stderr, + "%s: You can't use ..enclosed.. and ..optionally-enclosed.. at the " + "same time.\n", + my_progname); + return (EX_USAGE); + } + if ((opt_databases || opt_alldbs) && path) { + fprintf(stderr, + "%s: --databases or --all-databases can't be used with --tab.\n", + my_progname); + return (EX_USAGE); + } + if (strcmp(default_charset, charset_info->csname) && + !(charset_info = + get_charset_by_csname(default_charset, MY_CS_PRIMARY, MYF(MY_WME)))) + exit(1); + // if ((*argc < 1 && !opt_alldbs) || (*argc > 0 && opt_alldbs)) + // { + // short_usage(); + // return EX_USAGE; + // } + // if (tty_password) + // opt_password=get_tty_password(NullS); + return (0); +} /* get_options */ + +/* +** DB_error -- prints mysql error message and exits the program. +*/ +static void DB_error(MYSQL *mysql_arg, const char *when) { + DBUG_ENTER("DB_error"); + maybe_die(EX_MYSQLERR, "Got error: %d: %s %s", mysql_errno(mysql_arg), + mysql_error(mysql_arg), when); + DBUG_VOID_RETURN; +} + +/* + Prints out an error message and kills the process. + + SYNOPSIS + die() + error_num - process return value + fmt_reason - a format string for use by my_vsnprintf. + ... - variable arguments for above fmt_reason string + + DESCRIPTION + This call prints out the formatted error message to stderr and then + terminates the process. +*/ +static void die(int error_num, const char *fmt_reason, ...) { + char buffer[1000]; + va_list args; + va_start(args, fmt_reason); + my_vsnprintf(buffer, sizeof(buffer), fmt_reason, args); + va_end(args); + + fprintf(stderr, "%s: %s\n", my_progname, buffer); + fflush(stderr); + + /* force the exit */ + opt_force = 0; + if (opt_ignore_error) my_free(opt_ignore_error); + opt_ignore_error = 0; + + MySQLContext *context = get_connection_thread_context(); + context->maybe_exit(error_num); +} + +/* + Prints out an error message and maybe kills the process. + + SYNOPSIS + maybe_die() + error_num - process return value + fmt_reason - a format string for use by my_vsnprintf. + ... - variable arguments for above fmt_reason string + + DESCRIPTION + This call prints out the formatted error message to stderr and then + terminates the process, unless the --force command line option is used. + + This call should be used for non-fatal errors (such as database + errors) that the code may still be able to continue to the next unit + of work. + +*/ +static void maybe_die(int error_num, const char *fmt_reason, ...) { + char buffer[1000]; + va_list args; + va_start(args, fmt_reason); + my_vsnprintf(buffer, sizeof(buffer), fmt_reason, args); + va_end(args); + + fprintf(stderr, "%s: %s\n", my_progname, buffer); + fflush(stderr); + + MySQLContext *context = get_connection_thread_context(); + if (context) { + context->maybe_exit(error_num); + } +} + +/* + Sends a query to server, optionally reads result, prints error message if + some. + + SYNOPSIS + mysql_query_with_error_report() + mysql_con connection to use + res if non zero, result will be put there with + mysql_store_result() + query query to send to server + + RETURN VALUES + 0 query sending and (if res!=0) result reading went ok + 1 error +*/ + +static int mysql_query_with_error_report(MYSQL *mysql_con, MYSQL_RES **res, + const char *query) { + if (mysql_query(mysql_con, query) || + (res && !((*res) = mysql_store_result(mysql_con)))) { + maybe_die(EX_MYSQLERR, "Couldn't execute '%s': %s (%d)", query, + mysql_error(mysql_con), mysql_errno(mysql_con)); + return 1; + } + return 0; +} + +static int mysql_query_without_exit(MYSQL *mysql_con, MYSQL_RES **res, + const char *query) { + if (mysql_query(mysql_con, query) || + (res && !((*res) = mysql_store_result(mysql_con)))) { + printf("Couldn't execute '%s': %s (%d)\n", query, mysql_error(mysql_con), + mysql_errno(mysql_con)); + return 1; + } + return 0; +} + +int MySQLContext::fetch_db_collation(const char *db_name, char *db_cl_name, + int db_cl_size) { + my_bool err_status = FALSE; + char query[QUERY_LENGTH]; + MYSQL_RES *db_cl_res; + MYSQL_ROW db_cl_row; + char quoted_database_buf[NAME_LEN * 2 + 3]; + char *qdatabase = quote_name(db_name, quoted_database_buf, 1); + + my_snprintf(query, sizeof(query), "use %s", qdatabase); + + if (mysql_query_with_error_report(mysql, NULL, query)) return 1; + + if (mysql_query_with_error_report(mysql, &db_cl_res, + "select @@collation_database")) + return 1; + + do { + if (mysql_num_rows(db_cl_res) != 1) { + err_status = TRUE; + break; + } + + if (!(db_cl_row = mysql_fetch_row(db_cl_res))) { + err_status = TRUE; + break; + } + + strncpy(db_cl_name, db_cl_row[0], db_cl_size - 1); + db_cl_name[db_cl_size - 1] = 0; + + } while (FALSE); + + mysql_free_result(db_cl_res); + + return err_status ? 1 : 0; +} + +static char *my_case_str(const char *str, size_t str_len, const char *token, + size_t token_len) { + my_match_t match; + + uint status = my_charset_latin1.coll->instr(&my_charset_latin1, str, str_len, + token, token_len, &match, 1); + + return status ? (char *)str + match.end : NULL; +} + +int MySQLContext::switch_db_collation(FILE *sql_file, const char *db_name, + const char *delimiter, + const char *current_db_cl_name, + const char *required_db_cl_name, + int *db_cl_altered) { + if (strcmp(current_db_cl_name, required_db_cl_name) != 0) { + char quoted_db_buf[NAME_LEN * 2 + 3]; + char *quoted_db_name = quote_name(db_name, quoted_db_buf, FALSE); + + CHARSET_INFO *db_cl = get_charset_by_name(required_db_cl_name, MYF(0)); + + if (!db_cl) return 1; + + fprintf(sql_file, "ALTER DATABASE %s CHARACTER SET %s COLLATE %s %s\n", + (const char *)quoted_db_name, (const char *)db_cl->csname, + (const char *)db_cl->name, (const char *)delimiter); + + *db_cl_altered = 1; + + return 0; + } + + *db_cl_altered = 0; + + return 0; +} + +int MySQLContext::restore_db_collation(FILE *sql_file, const char *db_name, + const char *delimiter, + const char *db_cl_name) { + char quoted_db_buf[NAME_LEN * 2 + 3]; + char *quoted_db_name = quote_name(db_name, quoted_db_buf, FALSE); + + CHARSET_INFO *db_cl = get_charset_by_name(db_cl_name, MYF(0)); + + if (!db_cl) return 1; + + fprintf(sql_file, "ALTER DATABASE %s CHARACTER SET %s COLLATE %s %s\n", + (const char *)quoted_db_name, (const char *)db_cl->csname, + (const char *)db_cl->name, (const char *)delimiter); + + return 0; +} + +static void switch_cs_variables(FILE *sql_file, const char *delimiter, + const char *character_set_client, + const char *character_set_results, + const char *collation_connection) { + fprintf(sql_file, + "/*!50003 SET @saved_cs_client = @@character_set_client */ %s\n" + "/*!50003 SET @saved_cs_results = @@character_set_results */ %s\n" + "/*!50003 SET @saved_col_connection = @@collation_connection */ %s\n" + "/*!50003 SET character_set_client = %s */ %s\n" + "/*!50003 SET character_set_results = %s */ %s\n" + "/*!50003 SET collation_connection = %s */ %s\n", + (const char *)delimiter, (const char *)delimiter, + (const char *)delimiter, + + (const char *)character_set_client, (const char *)delimiter, + + (const char *)character_set_results, (const char *)delimiter, + + (const char *)collation_connection, (const char *)delimiter); +} + +static void restore_cs_variables(FILE *sql_file, const char *delimiter) { + fprintf(sql_file, + "/*!50003 SET character_set_client = @saved_cs_client */ %s\n" + "/*!50003 SET character_set_results = @saved_cs_results */ %s\n" + "/*!50003 SET collation_connection = @saved_col_connection */ %s\n", + (const char *)delimiter, (const char *)delimiter, + (const char *)delimiter); +} + +/* + This function will remove specific sql mode. + + @param[in] sql_mode Original sql mode from where input mode needs to + be removed. + @param[in] replace_mode sql mode which needs to be removed from original + sql mode. + @param[in] replace_len length of sql mode which needs to be removed. + + @retval 1 replace_mode is not present + 0 replace_mode is removed successfully +*/ +static int remove_sql_mode(char *sql_mode, const char *replace_mode, + size_t replace_len) { + char *start = strstr(sql_mode, replace_mode); + /* nothing to replace */ + if (!start) return 1; + /* sql mode to replace is the only sql mode present or the last one */ + if (strlen(start) == replace_len) { + if (start == sql_mode) + *start = 0; + else + start[-1] = 0; + } else { + const char *next = start + replace_len + 1; + memmove(start, next, strlen(next) + 1); + } + return 0; +} + +static void switch_sql_mode(FILE *sql_file, const char *delimiter, + const char *sql_mode) { + fprintf(sql_file, + "/*!50003 SET @saved_sql_mode = @@sql_mode */ %s\n" + "/*!50003 SET sql_mode = '%s' */ %s\n", + (const char *)delimiter, + + (const char *)sql_mode, (const char *)delimiter); +} + +static void restore_sql_mode(FILE *sql_file, const char *delimiter) { + fprintf(sql_file, + "/*!50003 SET sql_mode = @saved_sql_mode */ %s\n", + (const char *)delimiter); +} + +static void switch_time_zone(FILE *sql_file, const char *delimiter, + const char *time_zone) { + fprintf(sql_file, + "/*!50003 SET @saved_time_zone = @@time_zone */ %s\n" + "/*!50003 SET time_zone = '%s' */ %s\n", + (const char *)delimiter, + + (const char *)time_zone, (const char *)delimiter); +} + +static void restore_time_zone(FILE *sql_file, const char *delimiter) { + fprintf(sql_file, + "/*!50003 SET time_zone = @saved_time_zone */ %s\n", + (const char *)delimiter); +} + +/** + Switch charset for results to some specified charset. If the server does not + support character_set_results variable, nothing can be done here. As for + whether something should be done here, future new callers of this function + should be aware that the server lacking the facility of switching charsets is + treated as success. + + @note If the server lacks support, then nothing is changed and no error + condition is returned. + + @returns whether there was an error or not +*/ +int MySQLContext::switch_character_set_results(MYSQL *mysql, + const char *cs_name) { + char query_buffer[QUERY_LENGTH]; + size_t query_length; + + /* Server lacks facility. This is not an error, by arbitrary decision . */ + if (!server_supports_switching_charsets) return FALSE; + + query_length = my_snprintf(query_buffer, sizeof(query_buffer), + "SET SESSION character_set_results = '%s'", + (const char *)cs_name); + + return mysql_real_query(mysql, query_buffer, (ulong)query_length); +} + +/** + Rewrite statement, enclosing DEFINER clause in version-specific comment. + + This function parses any CREATE statement and encloses DEFINER-clause in + version-specific comment: + input query: CREATE DEFINER=a@b FUNCTION ... + rewritten query: CREATE * / / *!50020 DEFINER=a@b * / / *!50003 FUNCTION ... + + @note This function will go away when WL#3995 is implemented. + + @param[in] stmt_str CREATE statement string. + @param[in] stmt_length Length of the stmt_str. + @param[in] definer_version_str Minimal MySQL version number when + DEFINER clause is supported in the + given statement. + @param[in] definer_version_length Length of definer_version_str. + @param[in] stmt_version_str Minimal MySQL version number when the + given statement is supported. + @param[in] stmt_version_length Length of stmt_version_str. + @param[in] keyword_str Keyword to look for after CREATE. + @param[in] keyword_length Length of keyword_str. + + @return pointer to the new allocated query string. +*/ + +static char *cover_definer_clause(const char *stmt_str, size_t stmt_length, + const char *definer_version_str, + size_t definer_version_length, + const char *stmt_version_str, + size_t stmt_version_length, + const char *keyword_str, + size_t keyword_length) { + char *definer_begin = + my_case_str(stmt_str, stmt_length, C_STRING_WITH_LEN(" DEFINER")); + char *definer_end = NULL; + + char *query_str = NULL; + char *query_ptr; + LEX_CSTRING comment = {C_STRING_WITH_LEN("*/ /*!")}; + + if (!definer_begin) return NULL; + + definer_end = my_case_str(definer_begin, strlen(definer_begin), keyword_str, + keyword_length); + + if (!definer_end) return NULL; + + /* + Allocate memory for new query string: original string + from SHOW statement and version-specific comments. + */ + query_str = alloc_query_str(stmt_length + 23); + + query_ptr = my_stpncpy(query_str, stmt_str, definer_begin - stmt_str); + query_ptr = my_stpncpy(query_ptr, comment.str, comment.length + 1); + query_ptr = + my_stpncpy(query_ptr, definer_version_str, definer_version_length); + query_ptr = my_stpncpy(query_ptr, definer_begin, definer_end - definer_begin); + query_ptr = my_stpncpy(query_ptr, comment.str, comment.length + 1); + query_ptr = my_stpncpy(query_ptr, stmt_version_str, stmt_version_length); + query_ptr = strxmov(query_ptr, definer_end, NullS); + + return query_str; +} + +/* + Open a new .sql file to dump the table or view into + + SYNOPSIS + open_sql_file_for_table + name name of the table or view + flags flags (as per "man 2 open") + + RETURN VALUES + 0 Failed to open file + > 0 Handle of the open file +*/ +static FILE *open_sql_file_for_table(const char *table, int flags) { + FILE *res; + char filename[FN_REFLEN], tmp_path[FN_REFLEN]; + convert_dirname(tmp_path, path, NullS); + res = my_fopen(fn_format(filename, table, tmp_path, ".sql", 4), flags, + MYF(MY_WME)); + return res; +} + +void MySQLContext::free_resources() { + if (md_result_file && md_result_file != stdout) + my_fclose(md_result_file, MYF(0)); + my_free(opt_password); + if (my_hash_inited(&ignore_table)) my_hash_free(&ignore_table); + if (insert_pat_inited) dynstr_free(&insert_pat); + if (defaults_argv) free_defaults(defaults_argv); + if (opt_ignore_error) my_free(opt_ignore_error); + delete_dynamic(&ignore_error); + my_end(my_end_arg); + pthread_key_delete(sg_connectionkey); +} + +/** + Parse the list of error numbers to be ignored and store into a dynamic + array. + + @return Operation status + @retval 0 Success + @retval >0 Failure +*/ +static int parse_ignore_error() { + const char *search = ","; + char *token; + uint my_err; + + DBUG_ENTER("parse_ignore_error"); + + if (my_init_dynamic_array(&ignore_error, PSI_NOT_INSTRUMENTED, sizeof(uint), + NULL, 12, 12)) + goto error; + + token = strtok(opt_ignore_error, search); + + while (token != NULL) { + my_err = atoi(token); + // filter out 0s, if any + if (my_err != 0) { + if (insert_dynamic(&ignore_error, &my_err)) goto error; + } + token = strtok(NULL, search); + } + DBUG_RETURN(0); + +error: + DBUG_RETURN(EX_EOM); +} + +/** + Check if the last error should be ignored. + @retval 1 yes + 0 no +*/ +my_bool MySQLContext::do_ignore_error() { + uint i, last_errno, *my_err; + my_bool found = 0; + + DBUG_ENTER("do_ignore_error"); + + last_errno = mysql_errno(mysql); + + if (last_errno == 0) goto done; + + for (i = 0; i < ignore_error.elements; i++) { + my_err = dynamic_element(&ignore_error, i, uint *); + if (last_errno == *my_err) { + found = 1; + break; + } + } +done: + DBUG_RETURN(found); +} + +void MySQLContext::maybe_exit(int error) { + if (!first_error) first_error = error; + + /* + Return if --force is used; else return only if the + last error number is in the list of error numbers + specified using --ignore-error option. + */ + if (opt_force || (opt_ignore_error && do_ignore_error())) return; + if (mysql) mysql_close(mysql); + free_resources(); + exit(error); +} + +/* + db_connect -- connects to the host and selects DB. +*/ + +int MySQLContext::connect_to_db(char *host, char *user, char *passwd) { + char buff[20 + FN_REFLEN]; + DBUG_ENTER("connect_to_db"); + + verbose_msg("-- Connecting to %s...\n", host ? host : "localhost"); + mysql_init(&mysql_connection); + if (opt_compress) mysql_options(&mysql_connection, MYSQL_OPT_COMPRESS, NullS); + SSL_SET_OPTIONS(&mysql_connection); + if (opt_protocol) + mysql_options(&mysql_connection, MYSQL_OPT_PROTOCOL, (char *)&opt_protocol); + if (opt_bind_addr) + mysql_options(&mysql_connection, MYSQL_OPT_BIND, opt_bind_addr); +#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY) + if (shared_memory_base_name) + mysql_options(&mysql_connection, MYSQL_SHARED_MEMORY_BASE_NAME, + shared_memory_base_name); +#endif + mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset); + + if (opt_plugin_dir && *opt_plugin_dir) + mysql_options(&mysql_connection, MYSQL_PLUGIN_DIR, opt_plugin_dir); + + if (opt_default_auth && *opt_default_auth) + mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth); + + if (using_opt_enable_cleartext_plugin) + mysql_options(&mysql_connection, MYSQL_ENABLE_CLEARTEXT_PLUGIN, + (char *)&opt_enable_cleartext_plugin); + + mysql_options(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_ADD, "program_name", + "mysqldump"); + + set_server_public_key(&mysql_connection); + set_get_server_public_key_option(&mysql_connection); + + if (!(mysql = mysql_real_connect(&mysql_connection, host, user, passwd, NULL, + opt_mysql_port, opt_mysql_unix_port, 0))) { + DB_error(&mysql_connection, "when trying to connect"); + DBUG_RETURN(1); + } + if ((mysql_get_server_version(&mysql_connection) < 40100) || + (opt_compatible_mode & 3)) { + /* Don't dump SET NAMES with a pre-4.1 server (bug#7997). */ + opt_set_charset = 0; + + /* Don't switch charsets for 4.1 and earlier. (bug#34192). */ + server_supports_switching_charsets = FALSE; + } + /* + As we're going to set SQL_MODE, it would be lost on reconnect, so we + cannot reconnect. + */ + mysql->reconnect = 0; + my_snprintf(buff, sizeof(buff), "/*!40100 SET @@SQL_MODE='%s' */", + compatible_mode_normal_str); + if (mysql_query_without_exit(mysql, 0, buff)) { + printf("cannot set sql_mode to %s, set sql_mode to ansi and retry", + compatible_mode_normal_str); + my_snprintf(buff, sizeof(buff), "/*!40100 SET @@SQL_MODE='ANSI' */"); + if (mysql_query_with_error_report(mysql, 0, buff)) { + DBUG_RETURN(1); + } + } + /* + set time_zone to UTC to allow dumping date types between servers with + different time zone settings + */ + if (opt_tz_utc) { + my_snprintf(buff, sizeof(buff), "/*!40103 SET TIME_ZONE='+00:00' */"); + if (mysql_query_with_error_report(mysql, 0, buff)) DBUG_RETURN(1); + } + DBUG_RETURN(0); +} /* connect_to_db */ + +/* +** dbDisconnect -- disconnects from the host. +*/ +void MySQLContext::dbDisconnect(char *host) { + verbose_msg("-- Disconnecting from %s...\n", host ? host : "localhost"); + mysql_close(mysql); +} /* dbDisconnect */ + +void MySQLContext::unescape(FILE *file, char *pos, size_t length, + DYNAMIC_STRING *output) { + char *tmp; + DBUG_ENTER("unescape"); + if (!(tmp = (char *)my_malloc(PSI_NOT_INSTRUMENTED, length * 2 + 1, + MYF(MY_WME)))) + die(EX_MYSQLERR, "Couldn't allocate memory"); + + mysql_real_escape_string_quote(&mysql_connection, tmp, pos, (ulong)length, + '\''); + fputc('\'', file); + fputs(tmp, file); + fputc('\'', file); + if (output) { + dynstr_append_checked(output, "\'"); + dynstr_append_checked(output, tmp); + dynstr_append_checked(output, "\'"); + } + check_io(file); + my_free(tmp); + DBUG_VOID_RETURN; +} /* unescape */ + +void MySQLContext::unescapestr(char *pos, size_t length, + DYNAMIC_STRING *output) { + char *tmp; + DBUG_ENTER("unescape"); + if (!(tmp = (char *)my_malloc(PSI_NOT_INSTRUMENTED, length * 2 + 1, + MYF(MY_WME)))) + die(EX_MYSQLERR, "Couldn't allocate memory"); + + mysql_real_escape_string_quote(&mysql_connection, tmp, pos, (ulong)length, + '\''); + if (output) { + dynstr_append_checked(output, "\'"); + dynstr_append_checked(output, tmp); + dynstr_append_checked(output, "\'"); + } + my_free(tmp); + DBUG_VOID_RETURN; +} /* unescape */ + +static my_bool test_if_special_chars(const char *str) { + for (; *str; str++) + if (!my_isvar(charset_info, *str) && *str != '$') return 1; + return 0; +} /* test_if_special_chars */ + +/* + quote_name(name, buff, force) + + Quotes char string, taking into account compatible mode + + Args + + name Unquoted string containing that which will be quoted + buff The buffer that contains the quoted value, also returned + force Flag to make it ignore 'test_if_special_chars' + + Returns + + buff quoted string + +*/ +char *MySQLContext::quote_name(const char *name, char *buff, my_bool force) { + char *to = buff; + char qtype = (opt_compatible_mode & MASK_ANSI_QUOTES) ? '\"' : '`'; + + if (!force && !opt_quoted && !test_if_special_chars(name)) + return (char *)name; + *to++ = qtype; + while (*name) { + if (*name == qtype) *to++ = qtype; + *to++ = *name++; + } + to[0] = qtype; + to[1] = 0; + return buff; +} /* quote_name */ + +/* + Quote a table name so it can be used in "SHOW TABLES LIKE " + + SYNOPSIS + quote_for_like() + name name of the table + buff quoted name of the table + + DESCRIPTION + Quote \, _, ' and % characters + + Note: Because MySQL uses the C escape syntax in strings + (for example, '\n' to represent newline), you must double + any '\' that you use in your LIKE strings. For example, to + search for '\n', specify it as '\\n'. To search for '\', specify + it as '\\\\' (the backslashes are stripped once by the parser + and another time when the pattern match is done, leaving a + single backslash to be matched). + + Example: "t\1" => "t\\\\1" + +*/ +static char *quote_for_like(const char *name, char *buff) { + char *to = buff; + *to++ = '\''; + while (*name) { + if (*name == '\\') { + *to++ = '\\'; + *to++ = '\\'; + *to++ = '\\'; + } else if (*name == '\'' || *name == '_' || *name == '%') + *to++ = '\\'; + *to++ = *name++; + } + to[0] = '\''; + to[1] = 0; + return buff; +} + +/** + Quote and print a string. + + @param xml_file - Output file. + @param str - String to print. + @param len - Its length. + @param is_attribute_name - A check for attribute name or value. + + @description + Quote '<' '>' '&' '\"' chars and print a string to the xml_file. +*/ + +static void print_quoted_xml(FILE *xml_file, const char *str, size_t len, + my_bool is_attribute_name) { + const char *end; + + for (end = str + len; str != end; str++) { + switch (*str) { + case '<': + fputs("<", xml_file); + break; + case '>': + fputs(">", xml_file); + break; + case '&': + fputs("&", xml_file); + break; + case '\"': + fputs(""", xml_file); + break; + case ' ': + /* Attribute names cannot contain spaces. */ + if (is_attribute_name) { + fputs("_", xml_file); + break; + } + /* fall through */ + default: + fputc(*str, xml_file); + break; + } + } + check_io(xml_file); +} + +/* + Print xml tag. Optionally add attribute(s). + + SYNOPSIS + print_xml_tag(xml_file, sbeg, send, tag_name, first_attribute_name, + ..., attribute_name_n, attribute_value_n, NullS) + xml_file - output file + sbeg - line beginning + line_end - line ending + tag_name - XML tag name. + first_attribute_name - tag and first attribute + first_attribute_value - (Implied) value of first attribute + attribute_name_n - attribute n + attribute_value_n - value of attribute n + + DESCRIPTION + Print XML tag with any number of attribute="value" pairs to the xml_file. + + Format is: + sbegsend + NOTE + Additional arguments must be present in attribute/value pairs. + The last argument should be the null character pointer. + All attribute_value arguments MUST be NULL terminated strings. + All attribute_value arguments will be quoted before output. +*/ + +static void print_xml_tag(FILE *xml_file, const char *sbeg, + const char *line_end, const char *tag_name, + const char *first_attribute_name, ...) { + va_list arg_list; + const char *attribute_name, *attribute_value; + + fputs(sbeg, xml_file); + fputc('<', xml_file); + fputs(tag_name, xml_file); + + va_start(arg_list, first_attribute_name); + attribute_name = first_attribute_name; + while (attribute_name != NullS) { + attribute_value = va_arg(arg_list, char *); + DBUG_ASSERT(attribute_value != NullS); + + fputc(' ', xml_file); + fputs(attribute_name, xml_file); + fputc('\"', xml_file); + + print_quoted_xml(xml_file, attribute_value, strlen(attribute_value), 0); + fputc('\"', xml_file); + + attribute_name = va_arg(arg_list, char *); + } + va_end(arg_list); + + fputc('>', xml_file); + fputs(line_end, xml_file); + check_io(xml_file); +} + +/* + Print xml tag with for a field that is null + + SYNOPSIS + print_xml_null_tag() + xml_file - output file + sbeg - line beginning + stag_atr - tag and attribute + sval - value of attribute + line_end - line ending + + DESCRIPTION + Print tag with one attribute to the xml_file. Format is: + + NOTE + sval MUST be a NULL terminated string. + sval string will be qouted before output. +*/ + +static void print_xml_null_tag(FILE *xml_file, const char *sbeg, + const char *stag_atr, const char *sval, + const char *line_end) { + fputs(sbeg, xml_file); + fputs("<", xml_file); + fputs(stag_atr, xml_file); + fputs("\"", xml_file); + print_quoted_xml(xml_file, sval, strlen(sval), 0); + fputs("\" xsi:nil=\"true\" />", xml_file); + fputs(line_end, xml_file); + check_io(xml_file); +} + +/** + Print xml CDATA section. + + @param xml_file - output file + @param str - string to print + @param len - length of the string + + @note + This function also takes care of the presence of '[[>' + string in the str. If found, the CDATA section is broken + into two CDATA sections, and ]]. +*/ + +static void print_xml_cdata(FILE *xml_file, const char *str, ulong len) { + const char *end; + + fputs("')) { + fputs("]]]]>", xml_file); + str += 2; + continue; + } + /* fall through */ + default: + fputc(*str, xml_file); + break; + } + } + fputs("\n]]>\n", xml_file); + check_io(xml_file); +} + +/* + Print xml tag with many attributes. + + SYNOPSIS + print_xml_row() + xml_file - output file + row_name - xml tag name + tableRes - query result + row - result row + str_create - create statement header string + + DESCRIPTION + Print tag with many attribute to the xml_file. Format is: + \t\t + NOTE + All atributes and values will be quoted before output. +*/ + +static void print_xml_row(FILE *xml_file, const char *row_name, + MYSQL_RES *tableRes, MYSQL_ROW *row, + const char *str_create) { + uint i; + char *create_stmt_ptr = NULL; + ulong create_stmt_len = 0; + MYSQL_FIELD *field; + ulong *lengths = mysql_fetch_lengths(tableRes); + + fprintf(xml_file, "\t\t<%s", row_name); + check_io(xml_file); + mysql_field_seek(tableRes, 0); + for (i = 0; (field = mysql_fetch_field(tableRes)); i++) { + if ((*row)[i]) { + /* For 'create' statements, dump using CDATA. */ + if ((str_create) && (strcmp(str_create, field->name) == 0)) { + create_stmt_ptr = (*row)[i]; + create_stmt_len = lengths[i]; + } else { + fputc(' ', xml_file); + print_quoted_xml(xml_file, field->name, field->name_length, 1); + fputs("=\"", xml_file); + print_quoted_xml(xml_file, (*row)[i], lengths[i], 0); + fputc('"', xml_file); + check_io(xml_file); + } + } + } + + if (create_stmt_len) { + fputs(">\n", xml_file); + print_xml_cdata(xml_file, create_stmt_ptr, create_stmt_len); + fprintf(xml_file, "\t\t\n", row_name); + } else + fputs(" />\n", xml_file); + + check_io(xml_file); +} + +/** + Print xml comments. + + @param xml_file - output file + @param len - length of comment message + @param comment_string - comment message + + @description + Print the comment message in the format: + "\n" + + @note + Any occurrence of continuous hyphens will be + squeezed to a single hyphen. +*/ + +static void print_xml_comment(FILE *xml_file, size_t len, + const char *comment_string) { + const char *end; + + fputs("\n", xml_file); + check_io(xml_file); +} + +/* A common printing function for xml and non-xml modes. */ + +static void print_comment(FILE *sql_file, my_bool is_error, const char *format, + ...) { + static char comment_buff[COMMENT_LENGTH]; + va_list args; + + /* If its an error message, print it ignoring opt_comments. */ + if (!is_error && !opt_comments) return; + + va_start(args, format); + my_vsnprintf(comment_buff, COMMENT_LENGTH, format, args); + va_end(args); + + if (!opt_xml) { + fputs(comment_buff, sql_file); + check_io(sql_file); + return; + } + + print_xml_comment(sql_file, strlen(comment_buff), comment_buff); +} + +/** + @brief Accepts object names and prefixes them with "-- " wherever + end-of-line character ('\n') is found. + + @param[in] object_name object name list (concatenated string) + @param[out] freemem should buffer be released after usage + + @return + @retval pointer to a string with prefixed objects +*/ +static char const *fix_identifier_with_newline(char const *object_name, + my_bool *freemem) { + const size_t PREFIX_LENGTH = 3; // strlen ("-- ") + + // static buffer for replacement procedure + static char storage[NAME_LEN + 1]; + static char *buffer = storage; + static size_t buffer_size = sizeof(storage) - 1; + size_t index = 0; + size_t required_size = 0; + + // we presume memory allocation won't be needed + *freemem = FALSE; + + // traverse and reformat objects + while (object_name && *object_name) { + ++required_size; + if (*object_name == '\n') required_size += PREFIX_LENGTH; + + // do we need dynamic (re)allocation + if (required_size > buffer_size) { + // new alloc size increased in COMMENT_LENGTH multiple + buffer_size = COMMENT_LENGTH * (1 + required_size / COMMENT_LENGTH); + + // is our buffer already dynamically allocated + if (*freemem) { + // just realloc + buffer = (char *)my_realloc(PSI_NOT_INSTRUMENTED, buffer, + buffer_size + 1, MYF(MY_WME)); + if (!buffer) exit(1); + } else { + // dynamic allocation + copy from static buffer + buffer = (char *)my_malloc(PSI_NOT_INSTRUMENTED, buffer_size + 1, + MYF(MY_WME)); + if (!buffer) exit(1); + + strncpy(buffer, storage, index); + *freemem = TRUE; + } + } + + // copy a character + buffer[index] = *object_name; + ++index; + + // prefix new lines with double dash + if (*object_name == '\n') { + strcpy(buffer + index, "-- "); + index += PREFIX_LENGTH; + } + + ++object_name; + } + + // don't forget null termination + buffer[index] = '\0'; + return buffer; +} + +/* + create_delimiter + Generate a new (null-terminated) string that does not exist in query + and is therefore suitable for use as a query delimiter. Store this + delimiter in delimiter_buff . + + This is quite simple in that it doesn't even try to parse statements as an + interpreter would. It merely returns a string that is not in the query, which + is much more than adequate for constructing a delimiter. + + RETURN + ptr to the delimiter on Success + NULL on Failure +*/ +static char *create_delimiter(char *query, char *delimiter_buff, + int delimiter_max_size) { + int proposed_length; + char *presence; + + delimiter_buff[0] = ';'; /* start with one semicolon, and */ + + for (proposed_length = 2; proposed_length < delimiter_max_size; + delimiter_max_size++) { + delimiter_buff[proposed_length - 1] = ';'; /* add semicolons, until */ + delimiter_buff[proposed_length] = '\0'; + + presence = strstr(query, delimiter_buff); + if (presence == NULL) { /* the proposed delimiter is not in the query. */ + return delimiter_buff; + } + } + return NULL; /* but if we run out of space, return nothing at all. */ +} + +/* + dump_events_for_db + -- retrieves list of events for a given db, and prints out + the CREATE EVENT statement into the output (the dump). + + RETURN + 0 Success + 1 Error +*/ +uint MySQLContext::dump_events_for_db(char *db) { + char query_buff[QUERY_LENGTH]; + char db_name_buff[NAME_LEN * 2 + 3], name_buff[NAME_LEN * 2 + 3]; + char *event_name; + char delimiter[QUERY_LENGTH]; + FILE *sql_file = md_result_file; + MYSQL_RES *event_res, *event_list_res; + MYSQL_ROW row, event_list_row; + + char db_cl_name[MY_CS_NAME_SIZE]; + int db_cl_altered = FALSE; + my_bool freemem = FALSE; + char const *text = fix_identifier_with_newline(db, &freemem); + + DBUG_ENTER("dump_events_for_db"); + DBUG_PRINT("enter", ("db: '%s'", db)); + + mysql_real_escape_string_quote(mysql, db_name_buff, db, (ulong)strlen(db), + '\''); + /* nice comments */ + print_comment(sql_file, 0, "\n--\n-- Dumping events for database '%s'\n--\n", + text); + if (freemem) my_free((void *)text); + + /* + not using "mysql_query_with_error_report" because we may have not + enough privileges to lock mysql.events. + */ + if (lock_tables) mysql_query(mysql, "LOCK TABLES mysql.event READ"); + + if (mysql_query_with_error_report(mysql, &event_list_res, "show events")) + DBUG_RETURN(0); + + strcpy(delimiter, ";"); + if (mysql_num_rows(event_list_res) > 0) { + if (opt_xml) + fputs("\t\n", sql_file); + else { + fprintf(sql_file, "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;\n"); + + /* Get database collation. */ + + if (fetch_db_collation(db_name_buff, db_cl_name, sizeof(db_cl_name))) + DBUG_RETURN(1); + } + + if (switch_character_set_results(mysql, "binary")) DBUG_RETURN(1); + + while ((event_list_row = mysql_fetch_row(event_list_res)) != NULL) { + event_name = quote_name(event_list_row[1], name_buff, 0); + DBUG_PRINT("info", ("retrieving CREATE EVENT for %s", name_buff)); + my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE EVENT %s", + event_name); + + if (mysql_query_with_error_report(mysql, &event_res, query_buff)) + DBUG_RETURN(1); + + while ((row = mysql_fetch_row(event_res)) != NULL) { + if (opt_xml) { + print_xml_row(sql_file, "event", event_res, &row, "Create Event"); + continue; + } + + /* + if the user has EXECUTE privilege he can see event names, but not the + event body! + */ + if (strlen(row[3]) != 0) { + char *query_str; + + if (opt_drop) + fprintf(sql_file, "/*!50106 DROP EVENT IF EXISTS %s */%s\n", + event_name, delimiter); + + if (create_delimiter(row[3], delimiter, sizeof(delimiter)) == NULL) { + fprintf(stderr, + "%s: Warning: Can't create delimiter for event '%s'\n", + my_progname, event_name); + DBUG_RETURN(1); + } + + fprintf(sql_file, "DELIMITER %s\n", delimiter); + + if (mysql_num_fields(event_res) >= 7) { + if (switch_db_collation(sql_file, db_name_buff, delimiter, + db_cl_name, row[6], &db_cl_altered)) { + DBUG_RETURN(1); + } + + switch_cs_variables(sql_file, delimiter, + row[4], /* character_set_client */ + row[4], /* character_set_results */ + row[5]); /* collation_connection */ + } else { + /* + mysqldump is being run against the server, that does not + provide character set information in SHOW CREATE + statements. + + NOTE: the dump may be incorrect, since character set + information is required in order to restore event properly. + */ + + fprintf(sql_file, + "--\n" + "-- WARNING: old server version. " + "The following dump may be incomplete.\n" + "--\n"); + } + remove_sql_mode(row[1], C_STRING_WITH_LEN("NO_AUTO_CREATE_USER")); + switch_sql_mode(sql_file, delimiter, row[1]); + + switch_time_zone(sql_file, delimiter, row[2]); + + query_str = cover_definer_clause( + row[3], strlen(row[3]), C_STRING_WITH_LEN("50117"), + C_STRING_WITH_LEN("50106"), C_STRING_WITH_LEN(" EVENT")); + + fprintf(sql_file, "/*!50106 %s */ %s\n", + (const char *)(query_str != NULL ? query_str : row[3]), + (const char *)delimiter); + + my_free(query_str); + restore_time_zone(sql_file, delimiter); + restore_sql_mode(sql_file, delimiter); + + if (mysql_num_fields(event_res) >= 7) { + restore_cs_variables(sql_file, delimiter); + + if (db_cl_altered) { + if (restore_db_collation(sql_file, db_name_buff, delimiter, + db_cl_name)) + DBUG_RETURN(1); + } + } + } + } /* end of event printing */ + mysql_free_result(event_res); + + } /* end of list of events */ + if (opt_xml) { + fputs("\t\n", sql_file); + check_io(sql_file); + } else { + fprintf(sql_file, "DELIMITER ;\n"); + fprintf(sql_file, "/*!50106 SET TIME_ZONE= @save_time_zone */ ;\n"); + } + + if (switch_character_set_results(mysql, default_charset)) DBUG_RETURN(1); + } + mysql_free_result(event_list_res); + + if (lock_tables) + (void)mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES"); + DBUG_RETURN(0); +} + +/* + Print hex value for blob data. + + SYNOPSIS + print_blob_as_hex() + output_file - output file + str - string to print + len - its length + + DESCRIPTION + Print hex value for blob data. +*/ + +static void print_blob_as_hex(FILE *output_file, const char *str, ulong len) { + /* sakaik got the idea to to provide blob's in hex notation. */ + const char *ptr = str, *end = ptr + len; + for (; ptr < end; ptr++) fprintf(output_file, "%02X", *((uchar *)ptr)); + check_io(output_file); +} + +static void append_blob_as_hex(DYNAMIC_STRING *output, const char *str, + ulong len) { + /* sakaik got the idea to to provide blob's in hex notation. */ + static char tmp[256]; + const char *ptr = str, *end = ptr + len; + for (; ptr < end; ptr++) { + sprintf(tmp, "%02X", *((uchar *)ptr)); + dynstr_append_checked(output, tmp); + } +} + +/* + dump_routines_for_db + -- retrieves list of routines for a given db, and prints out + the CREATE PROCEDURE definition into the output (the dump). + + This function has logic to print the appropriate syntax depending on whether + this is a procedure or functions + + RETURN + 0 Success + 1 Error +*/ + +uint MySQLContext::dump_routines_for_db(char *db) { + char query_buff[QUERY_LENGTH]; + const char *routine_type[] = {"FUNCTION", "PROCEDURE"}; + char db_name_buff[NAME_LEN * 2 + 3], name_buff[NAME_LEN * 2 + 3]; + char *routine_name; + int i; + FILE *sql_file = md_result_file; + MYSQL_RES *routine_res, *routine_list_res; + MYSQL_ROW row, routine_list_row; + std::string sql; + + char db_cl_name[MY_CS_NAME_SIZE]; + // int db_cl_altered = FALSE; + my_bool freemem = FALSE; + char const *text = fix_identifier_with_newline(db, &freemem); + struct DMODBC::DBError dbError; + bool isfunction; + + DBUG_ENTER("dump_routines_for_db"); + DBUG_PRINT("enter", ("db: '%s'", db)); + + mysql_real_escape_string_quote(mysql, db_name_buff, db, (ulong)strlen(db), + '\''); + /* nice comments */ + print_comment(sql_file, 0, + "\n--\n-- Dumping routines for database '%s'\n--\n", text); + + if (freemem) my_free((void *)text); + + /* + not using "mysql_query_with_error_report" because we may have not + enough privileges to lock mysql.proc. + */ + if (lock_tables) mysql_query(mysql, "LOCK TABLES mysql.proc READ"); + + /* Get database collation. */ + + if (fetch_db_collation(db_name_buff, db_cl_name, sizeof(db_cl_name))) + DBUG_RETURN(1); + + if (switch_character_set_results(mysql, "binary")) DBUG_RETURN(1); + + if (opt_xml) fputs("\t\n", sql_file); + + /* 0, retrieve and dump functions, 1, procedures */ + for (i = 0; i <= 1; i++) { + my_snprintf(query_buff, sizeof(query_buff), + "SHOW %s STATUS WHERE Db = '%s'", routine_type[i], + db_name_buff); + + if (mysql_query_with_error_report(mysql, &routine_list_res, query_buff)) + DBUG_RETURN(1); + + if (mysql_num_rows(routine_list_res)) { + while ((routine_list_row = mysql_fetch_row(routine_list_res))) { + routine_name = quote_name(routine_list_row[1], name_buff, 0); + DBUG_PRINT("info", + ("retrieving CREATE %s for %s", routine_type[i], name_buff)); + my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE %s %s", + routine_type[i], routine_name); + + if (mysql_query_with_error_report(mysql, &routine_res, query_buff)) + DBUG_RETURN(1); + + while ((row = mysql_fetch_row(routine_res))) { + /* + if the user has EXECUTE privilege he see routine names, but NOT the + routine body of other routines that are not the creator of! + */ + DBUG_PRINT("info", + ("length of body for %s row[2] '%s' is %zu", routine_name, + row[2] ? row[2] : "(null)", row[2] ? strlen(row[2]) : 0)); + if (row[2] == NULL) { + my_bool freemem = FALSE; + char const *text = + fix_identifier_with_newline(current_user, &freemem); + + print_comment(sql_file, 1, "\n-- insufficient privileges to %s\n", + query_buff); + print_comment(sql_file, 1, + "-- does %s have permissions on mysql.proc?\n\n", + text); + if (freemem) my_free((void *)text); + + maybe_die(EX_MYSQLERR, "%s has insufficent privileges to %s!", + current_user, query_buff); + } else if (strlen(row[2])) { + // if (opt_xml) + // { + // if (i) // Procedures. + // print_xml_row(sql_file, "routine", routine_res, &row, + // "Create Procedure"); + // else // Functions. + // print_xml_row(sql_file, "routine", routine_res, &row, + // "Create Function"); + // continue; + // } + // if (opt_drop) + // fprintf(sql_file, "/*!50003 DROP %s IF EXISTS %s */;\n", + // routine_type[i], routine_name); + + // if (mysql_num_fields(routine_res) >= 6) + // { + // if (switch_db_collation(sql_file, db_name_buff, ";", + // db_cl_name, row[5], &db_cl_altered)) + // { + // DBUG_RETURN(1); + // } + + // switch_cs_variables(sql_file, ";", + // row[3], /* character_set_client */ + // row[3], /* character_set_results */ + // row[4]); /* collation_connection */ + // } + // else + // { + // /* + // mysqldump is being run against the server, that does not + // provide character set information in SHOW CREATE + // statements. + + // NOTE: the dump may be incorrect, since character set + // information is required in order to restore stored + // procedure/function properly. + // */ + + // fprintf(sql_file, + // "--\n" + // "-- WARNING: old server version. " + // "The following dump may be incomplete.\n" + // "--\n"); + // } + + // remove_sql_mode(row[1], + // C_STRING_WITH_LEN("NO_AUTO_CREATE_USER")); + // switch_sql_mode(sql_file, ";", row[1]); + + // fprintf(sql_file, + // "DELIMITER ;;\n" + // "%s ;;\n" + // "DELIMITER ;\n", + // (const char *) row[2]); + + sql = sg_procOdbc->procProcedureFunctionSql((const char *)row[2], + isfunction); + if (!sql.empty()) { + // execute sql + if (isfunction) { + sg_procMetrics->addFunctioncount(1); + sg_procMetrics->startSql("function", "function", sql.c_str()); + } else { + sg_procMetrics->addProcedurecount(1); + sg_procMetrics->startSql("procedure", "procedure", sql.c_str()); + } + if (sg_procOdbc->executeGeneralSql(sql.c_str(), &dbError)) { + // successful + if (isfunction) { + sg_procMetrics->endSql("function", "function", sql.c_str(), + false, "", false); + } else { + sg_procMetrics->endSql("procedure", "procedure", sql.c_str(), + false, "", false); + } + } else { + // error + if (isfunction) { + sg_procMetrics->endSql("function", "function", sql.c_str(), + true, dbError.error.c_str(), false); + } else { + sg_procMetrics->endSql("procedure", "procedure", sql.c_str(), + true, dbError.error.c_str(), false); + } + } + } + + // restore_sql_mode(sql_file, ";"); + + // if (mysql_num_fields(routine_res) >= 6) + // { + // restore_cs_variables(sql_file, ";"); + + // if (db_cl_altered) + // { + // if (restore_db_collation(sql_file, db_name_buff, ";", + // db_cl_name)) + // DBUG_RETURN(1); + // } + // } + } + } /* end of routine printing */ + mysql_free_result(routine_res); + + } /* end of list of routines */ + } + mysql_free_result(routine_list_res); + } /* end of for i (0 .. 1) */ + + if (opt_xml) { + fputs("\t\n", sql_file); + check_io(sql_file); + } + + if (switch_character_set_results(mysql, default_charset)) DBUG_RETURN(1); + + if (lock_tables) + (void)mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES"); + DBUG_RETURN(0); +} + +/* general_log or slow_log tables under mysql database */ +static inline my_bool general_log_or_slow_log_tables(const char *db, + const char *table) { + return (!my_strcasecmp(charset_info, db, "mysql")) && + (!my_strcasecmp(charset_info, table, "general_log") || + !my_strcasecmp(charset_info, table, "slow_log")); +} + +/* + slave_master_info,slave_relay_log_info and gtid_executed tables under + mysql database +*/ +static inline my_bool replication_metadata_tables(const char *db, + const char *table) { + return (!my_strcasecmp(charset_info, db, "mysql")) && + (!my_strcasecmp(charset_info, table, "slave_master_info") || + !my_strcasecmp(charset_info, table, "slave_relay_log_info") || + !my_strcasecmp(charset_info, table, "gtid_executed")); +} + +/* + get_table_structure -- retrievs database structure, prints out corresponding + CREATE statement and fills out insert_pat if the table is the type we will + be dumping. + + ARGS + table - table name + db - db name + table_type - table type, e.g. "MyISAM" or "InnoDB", but also "VIEW" + ignore_flag - what we must particularly ignore - see IGNORE_ defines above + real_columns- Contains one byte per column, 0 means unused, 1 is used + Generated columns are marked as unused + RETURN + number of fields in table, 0 if error +*/ + +uint MySQLContext::get_table_structure(char *table, char *db, char *table_type, + char *ignore_flag, + my_bool real_columns[]) { + my_bool init = 0, write_data, complete_insert; + my_ulonglong num_fields; + char *result_table, *opt_quoted_table; + const char *insert_option; + char name_buff[NAME_LEN + 3], table_buff[NAME_LEN * 2 + 3]; + char table_buff2[NAME_LEN * 2 + 3], query_buff[QUERY_LENGTH]; + const char *show_fields_stmt = + "SELECT `COLUMN_NAME` AS `Field`, " + "`COLUMN_TYPE` AS `Type`, " + "`IS_NULLABLE` AS `Null`, " + "`COLUMN_KEY` AS `Key`, " + "`COLUMN_DEFAULT` AS `Default`, " + "`EXTRA` AS `Extra`, " + "`COLUMN_COMMENT` AS `Comment` " + "FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE " + "TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'"; + + const char *show_increment = + "SELECT `COLUMN_NAME` AS `Field`" + "FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE " + "TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s' AND EXTRA='auto_increment'"; + + FILE *sql_file = md_result_file; + size_t len; + my_bool is_log_table; + my_bool is_replication_metadata_table; + unsigned int colno; + MYSQL_RES *result; + MYSQL_ROW row; + std::string incrementkey; + DBUG_ENTER("get_table_structure"); + DBUG_PRINT("enter", ("db: %s table: %s", db, table)); + + *ignore_flag = check_if_ignore_table(table, table_type); + + complete_insert = 0; + if ((write_data = !(*ignore_flag & IGNORE_DATA))) { + complete_insert = opt_complete_insert; + if (!insert_pat_inited) { + insert_pat_inited = 1; + init_dynamic_string_checked(&insert_pat, "", 1024, 1024); + } else + dynstr_set_checked(&insert_pat, ""); + } + + insert_option = (opt_ignore ? " IGNORE " : ""); + + verbose_msg("-- Retrieving table structure for table %s...\n", table); + + len = + my_snprintf(query_buff, sizeof(query_buff), + "SET SQL_QUOTE_SHOW_CREATE=%d", (opt_quoted || opt_keywords)); + if (!create_options) + my_stpcpy(query_buff + len, + "/*!40102 ,SQL_MODE=concat(@@sql_mode, _utf8 " + "',NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS') */"); + + result_table = quote_name(table, table_buff, 1); + opt_quoted_table = quote_name(table, table_buff2, 0); + + if (opt_order_by_primary) order_by = primary_key_fields(result_table); + + // 鏌ヨ鑷涓婚敭 + char buff4ik[20 + FN_REFLEN]; + my_snprintf(buff4ik, sizeof(buff4ik), show_increment, db, table); + if (!mysql_query_with_error_report(mysql, &result, buff4ik)) { + if (result->row_count > 0) { + row = mysql_fetch_row(result); + incrementkey = "\""; + incrementkey += row[0]; + incrementkey += "\""; + } + mysql_free_result(result); + } + + if (!opt_xml && !mysql_query_with_error_report(mysql, 0, query_buff)) { + /* using SHOW CREATE statement */ + if (!opt_no_create_info) { + /* Make an sql-file, if path was given iow. option -T was given */ + char buff[20 + FN_REFLEN]; + MYSQL_FIELD *field; + my_bool freemem = FALSE; + char const *text; + + my_snprintf(buff, sizeof(buff), "show create table %s", result_table); + + if (switch_character_set_results(mysql, "binary") || + mysql_query_with_error_report(mysql, &result, buff) || + switch_character_set_results(mysql, default_charset)) + DBUG_RETURN(0); + + if (path) { + if (!(sql_file = open_sql_file_for_table(table, O_WRONLY))) + DBUG_RETURN(0); + + write_header(sql_file, db); + } + + text = fix_identifier_with_newline(result_table, &freemem); + // if (strcmp (table_type, "VIEW") == 0) /* view */ + // print_comment(sql_file, 0, + // "\n--\n-- Temporary table structure for view + // %s\n--\n\n", text); + // else + // print_comment(sql_file, 0, + // "\n--\n-- Table structure for table %s\n--\n\n", text); + + if (freemem) my_free((void *)text); + + // if (opt_drop) + // { + // /* + // Even if the "table" is a view, we do a DROP TABLE here. The + // view-specific code below fills in the DROP VIEW. + // We will skip the DROP TABLE for general_log and slow_log, since + // those stmts will fail, in case we apply dump by enabling logging. + // We will skip this for replication metadata tables as well. + // */ + // if (!(general_log_or_slow_log_tables(db, table) || + // replication_metadata_tables(db, table))) + // fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n", + // opt_quoted_table); + // check_io(sql_file); + // } + + field = mysql_fetch_field_direct(result, 0); + if (strcmp(field->name, "View") == 0) { + char *scv_buff = NULL; + my_ulonglong n_cols; + + verbose_msg("-- It's a view, create dummy view\n"); + + /* save "show create" statement for later */ + if ((row = mysql_fetch_row(result)) && (scv_buff = row[1])) + scv_buff = my_strdup(PSI_NOT_INSTRUMENTED, scv_buff, MYF(0)); + + mysql_free_result(result); + + /* + Create a table with the same name as the view and with columns of + the same name in order to satisfy views that depend on this view. + The table will be removed when the actual view is created. + + The properties of each column, are not preserved in this temporary + table, because they are not necessary. + + This will not be necessary once we can determine dependencies + between views and can simply dump them in the appropriate order. + */ + my_snprintf(query_buff, sizeof(query_buff), "SHOW FIELDS FROM %s", + result_table); + if (switch_character_set_results(mysql, "binary") || + mysql_query_with_error_report(mysql, &result, query_buff) || + switch_character_set_results(mysql, default_charset)) { + /* + View references invalid or privileged table/col/fun (err 1356), + so we cannot create a stand-in table. Be defensive and dump + a comment with the view's 'show create' statement. (Bug #17371) + */ + + if (mysql_errno(mysql) == ER_VIEW_INVALID) + fprintf(sql_file, "\n-- failed on view %s: %s\n\n", result_table, + scv_buff ? scv_buff : ""); + + my_free(scv_buff); + + DBUG_RETURN(0); + } else + my_free(scv_buff); + + n_cols = mysql_num_rows(result); + if (0 != n_cols) { + /* + The actual formula is based on the column names and how the .FRM + files are stored and is too volatile to be repeated here. + Thus we simply warn the user if the columns exceed a limit we + know works most of the time. + */ + if (n_cols >= 1000) + fprintf(stderr, + "-- Warning: Creating a stand-in table for view %s may" + " fail when replaying the dump file produced because " + "of the number of columns exceeding 1000. Exercise " + "caution when replaying the produced dump file.\n", + table); + // if (opt_drop) + // { + // /* + // We have already dropped any table of the same name above, so + // here we just drop the view. + // */ + + // fprintf(sql_file, "/*!50001 DROP VIEW IF EXISTS %s*/;\n", + // opt_quoted_table); + // check_io(sql_file); + // } + + // fprintf(sql_file, + // "SET @saved_cs_client = @@character_set_client;\n" + // "SET character_set_client = utf8;\n" + // "/*!50001 CREATE VIEW %s AS SELECT \n", + // result_table); + + /* + Get first row, following loop will prepend comma - keeps from + having to know if the row being printed is last to determine if + there should be a _trailing_ comma. + */ + + row = mysql_fetch_row(result); + + /* + A temporary view is created to resolve the view interdependencies. + This temporary view is dropped when the actual view is created. + */ + + // fprintf(sql_file, " 1 AS %s", + // quote_name(row[0], name_buff, 0)); + + // while((row= mysql_fetch_row(result))) + // { + // fprintf(sql_file, ",\n 1 AS %s", + // quote_name(row[0], name_buff, 0)); + // } + + // fprintf(sql_file,"*/;\n" + // "SET character_set_client = @saved_cs_client;\n"); + + // check_io(sql_file); + } + + mysql_free_result(result); + + if (path) my_fclose(sql_file, MYF(MY_WME)); + + seen_views = 1; + DBUG_RETURN(0); + } + + row = mysql_fetch_row(result); + + is_log_table = general_log_or_slow_log_tables(db, table); + is_replication_metadata_table = replication_metadata_tables(db, table); + if (is_log_table || is_replication_metadata_table) + row[1] += 13; /* strlen("CREATE TABLE ")= 13 */ + // if (opt_compatible_mode & 3) + // { + // fprintf(sql_file, + // (is_log_table || is_replication_metadata_table) ? + // "CREATE TABLE IF NOT EXISTS %s;\n" : "%s;\n", row[1]); + // } + // else + // { + // fprintf(sql_file, + // "/*!40101 SET @saved_cs_client = @@character_set_client + // */;\n" + // "/*!40101 SET character_set_client = utf8 */;\n" + // "%s%s;\n" + // "/*!40101 SET character_set_client = @saved_cs_client */;\n", + // (is_log_table || is_replication_metadata_table) ? + // "CREATE TABLE IF NOT EXISTS " : "", row[1]); + // } + + // execute sql + if (sg_firstdumptables) { + sg_procMetrics->addTablecount(1); + } + std::list createsqls = + sg_procOdbc->procCreateTableSql(row[1], incrementkey.c_str()); + bool hastable = false; + struct DMODBC::DBError dbError; + for (std::list::iterator iter = createsqls.begin(); + iter != createsqls.end(); iter++) { + sg_procMetrics->startSql(table, "table", iter->c_str()); + if (sg_procOdbc->executeCreateTableSql(iter->c_str(), &dbError)) { + // successful sqls + sg_procMetrics->endSql(table, "table", iter->c_str(), false, "", false); + if (!hastable) { + if (iter->find("TABLE ") != std::string::npos) { + sg_createSuccessTables[table] = table; + hastable = true; + } + } + } else { + // failed sqls + sg_procMetrics->endSql(table, "table", iter->c_str(), true, + dbError.error.c_str(), false); + if (!hastable) { + if (iter->find("TABLE ") != std::string::npos) { + // 鍒涘缓琛ㄨ鍙ュけ璐 + if (!dbError.alreadyExistError) { + // 濡傛灉涓嶆槸宸茬粡瀛樺湪鐨勯敊璇紝閭d箞淇濆瓨閿欒淇℃伅 + sg_createFailedTables.push_back(table); + // sg_createFailedTableSqls[table] = createsqls; + hastable = true; + } + break; + } + } + } + } + + check_io(sql_file); + mysql_free_result(result); + } + my_snprintf(query_buff, sizeof(query_buff), "show fields from %s", + result_table); + if (mysql_query_with_error_report(mysql, &result, query_buff)) { + if (path) my_fclose(sql_file, MYF(MY_WME)); + DBUG_RETURN(0); + } + + if (write_data && !complete_insert) { + /* + If data contents of table are to be written and complete_insert + is false (column list not required in INSERT statement), scan the + column list for generated columns, as presence of any generated column + will require that an explicit list of columns is printed. + */ + while ((row = mysql_fetch_row(result))) { + complete_insert |= strcmp(row[SHOW_EXTRA], "STORED GENERATED") == 0 || + strcmp(row[SHOW_EXTRA], "VIRTUAL GENERATED") == 0; + } + mysql_free_result(result); + + if (mysql_query_with_error_report(mysql, &result, query_buff)) { + if (path) my_fclose(sql_file, MYF(MY_WME)); + DBUG_RETURN(0); + } + } + /* + If write_data is true, then we build up insert statements for + the table's data. Note: in subsequent lines of code, this test + will have to be performed each time we are appending to + insert_pat. + */ + if (write_data) { + if (opt_replace_into) + dynstr_append_checked(&insert_pat, "REPLACE "); + else + dynstr_append_checked(&insert_pat, "INSERT "); + dynstr_append_checked(&insert_pat, insert_option); + dynstr_append_checked(&insert_pat, "INTO "); + dynstr_append_checked(&insert_pat, opt_quoted_table); + if (complete_insert) { + dynstr_append_checked(&insert_pat, " ("); + } else { + dynstr_append_checked(&insert_pat, " VALUES "); + if (!extended_insert) dynstr_append_checked(&insert_pat, "("); + } + } + + colno = 0; + while ((row = mysql_fetch_row(result))) { + real_columns[colno] = strcmp(row[SHOW_EXTRA], "STORED GENERATED") != 0 && + strcmp(row[SHOW_EXTRA], "VIRTUAL GENERATED") != 0; + + if (real_columns[colno++] && complete_insert) { + if (init) { + dynstr_append_checked(&insert_pat, ", "); + } + init = 1; + dynstr_append_checked(&insert_pat, + quote_name(row[SHOW_FIELDNAME], name_buff, 0)); + } + } + num_fields = mysql_num_rows(result); + mysql_free_result(result); + } else { + verbose_msg("%s: Warning: Can't set SQL_QUOTE_SHOW_CREATE option (%s)\n", + my_progname, mysql_error(mysql)); + + my_snprintf(query_buff, sizeof(query_buff), show_fields_stmt, db, table); + + if (mysql_query_with_error_report(mysql, &result, query_buff)) + DBUG_RETURN(0); + + if (write_data && !complete_insert) { + /* + If data contents of table are to be written and complete_insert + is false (column list not required in INSERT statement), scan the + column list for generated columns, as presence of any generated column + will require that an explicit list of columns is printed. + */ + while ((row = mysql_fetch_row(result))) { + complete_insert |= strcmp(row[SHOW_EXTRA], "STORED GENERATED") == 0 || + strcmp(row[SHOW_EXTRA], "VIRTUAL GENERATED") == 0; + } + mysql_free_result(result); + + if (mysql_query_with_error_report(mysql, &result, query_buff)) { + if (path) my_fclose(sql_file, MYF(MY_WME)); + DBUG_RETURN(0); + } + } + /* Make an sql-file, if path was given iow. option -T was given */ + if (!opt_no_create_info) { + my_bool freemem = FALSE; + char const *text; + + if (path) { + if (!(sql_file = open_sql_file_for_table(table, O_WRONLY))) + DBUG_RETURN(0); + write_header(sql_file, db); + } + + text = fix_identifier_with_newline(result_table, &freemem); + print_comment(sql_file, 0, + "\n--\n-- Table structure for table %s\n--\n\n", text); + if (freemem) my_free((void *)text); + + if (opt_drop) + fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n", result_table); + if (!opt_xml) + fprintf(sql_file, "CREATE TABLE %s (\n", result_table); + else + print_xml_tag(sql_file, "\t", "\n", "table_structure", "name=", table, + NullS); + check_io(sql_file); + } + + if (write_data) { + if (opt_replace_into) + dynstr_append_checked(&insert_pat, "REPLACE "); + else + dynstr_append_checked(&insert_pat, "INSERT "); + dynstr_append_checked(&insert_pat, insert_option); + dynstr_append_checked(&insert_pat, "INTO "); + dynstr_append_checked(&insert_pat, result_table); + if (complete_insert) + dynstr_append_checked(&insert_pat, " ("); + else { + dynstr_append_checked(&insert_pat, " VALUES "); + if (!extended_insert) dynstr_append_checked(&insert_pat, "("); + } + } + + colno = 0; + while ((row = mysql_fetch_row(result))) { + ulong *lengths = mysql_fetch_lengths(result); + + real_columns[colno] = strcmp(row[SHOW_EXTRA], "STORED GENERATED") != 0 && + strcmp(row[SHOW_EXTRA], "VIRTUAL GENERATED") != 0; + + if (!real_columns[colno++]) continue; + + if (init) { + if (!opt_xml && !opt_no_create_info) { + fputs(",\n", sql_file); + check_io(sql_file); + } + if (complete_insert) dynstr_append_checked(&insert_pat, ", "); + } + init = 1; + if (complete_insert) + dynstr_append_checked(&insert_pat, + quote_name(row[SHOW_FIELDNAME], name_buff, 0)); + if (!opt_no_create_info) { + if (opt_xml) { + print_xml_row(sql_file, "field", result, &row, NullS); + continue; + } + + if (opt_keywords) + fprintf(sql_file, " %s.%s %s", result_table, + quote_name(row[SHOW_FIELDNAME], name_buff, 0), + row[SHOW_TYPE]); + else + fprintf(sql_file, " %s %s", + quote_name(row[SHOW_FIELDNAME], name_buff, 0), + row[SHOW_TYPE]); + if (row[SHOW_DEFAULT]) { + fputs(" DEFAULT ", sql_file); + unescape(sql_file, row[SHOW_DEFAULT], lengths[SHOW_DEFAULT], NULL); + } + if (!row[SHOW_NULL][0]) fputs(" NOT NULL", sql_file); + if (row[SHOW_EXTRA][0]) fprintf(sql_file, " %s", row[SHOW_EXTRA]); + check_io(sql_file); + } + } + num_fields = mysql_num_rows(result); + mysql_free_result(result); + if (!opt_no_create_info) { + /* Make an sql-file, if path was given iow. option -T was given */ + char buff[20 + FN_REFLEN]; + uint keynr, primary_key; + my_snprintf(buff, sizeof(buff), "show keys from %s", result_table); + if (mysql_query_with_error_report(mysql, &result, buff)) { + if (mysql_errno(mysql) == ER_WRONG_OBJECT) { + /* it is VIEW */ + fputs("\t\t\n", sql_file); + goto continue_xml; + } + fprintf(stderr, "%s: Can't get keys for table %s (%s)\n", my_progname, + result_table, mysql_error(mysql)); + if (path) my_fclose(sql_file, MYF(MY_WME)); + DBUG_RETURN(0); + } + + /* Find first which key is primary key */ + keynr = 0; + primary_key = INT_MAX; + while ((row = mysql_fetch_row(result))) { + if (atoi(row[3]) == 1) { + keynr++; + if (!strcmp(row[2], "PRIMARY")) { + primary_key = keynr; + break; + } + } + } + mysql_data_seek(result, 0); + keynr = 0; + while ((row = mysql_fetch_row(result))) { + if (opt_xml) { + print_xml_row(sql_file, "key", result, &row, NullS); + continue; + } + + if (atoi(row[3]) == 1) { + if (keynr++) putc(')', sql_file); + if (atoi(row[1])) /* Test if duplicate key */ + /* Duplicate allowed */ + fprintf(sql_file, ",\n KEY %s (", + quote_name(row[2], name_buff, 0)); + else if (keynr == primary_key) + fputs(",\n PRIMARY KEY (", sql_file); /* First UNIQUE is primary */ + else + fprintf(sql_file, ",\n UNIQUE %s (", + quote_name(row[2], name_buff, 0)); + } else + putc(',', sql_file); + fputs(quote_name(row[4], name_buff, 0), sql_file); + if (row[7]) fprintf(sql_file, " (%s)", row[7]); /* Sub key */ + check_io(sql_file); + } + mysql_free_result(result); + if (!opt_xml) { + if (keynr) putc(')', sql_file); + fputs("\n)", sql_file); + check_io(sql_file); + } + + /* Get MySQL specific create options */ + if (create_options) { + char show_name_buff[NAME_LEN * 2 + 2 + 24]; + + /* Check memory for quote_for_like() */ + my_snprintf(buff, sizeof(buff), "show table status like %s", + quote_for_like(table, show_name_buff)); + + if (mysql_query_with_error_report(mysql, &result, buff)) { + if (mysql_errno(mysql) != ER_PARSE_ERROR) { /* If old MySQL version */ + verbose_msg( + "-- Warning: Couldn't get status information for " + "table %s (%s)\n", + result_table, mysql_error(mysql)); + } + } else if (!(row = mysql_fetch_row(result))) { + fprintf(stderr, + "Error: Couldn't read status information for table %s (%s)\n", + result_table, mysql_error(mysql)); + } else { + if (opt_xml) + print_xml_row(sql_file, "options", result, &row, NullS); + else { + fputs("/*!", sql_file); + print_value(sql_file, result, row, "engine=", "Engine", 0); + print_value(sql_file, result, row, "", "Create_options", 0); + print_value(sql_file, result, row, "comment=", "Comment", 1); + fputs(" */", sql_file); + check_io(sql_file); + } + } + mysql_free_result(result); /* Is always safe to free */ + } + continue_xml: + if (!opt_xml) + fputs(";\n", sql_file); + else + fputs("\t\n", sql_file); + check_io(sql_file); + } + } + if (complete_insert) { + dynstr_append_checked(&insert_pat, ") VALUES "); + if (!extended_insert) dynstr_append_checked(&insert_pat, "("); + } + if (sql_file != md_result_file) { + fputs("\n", sql_file); + write_footer(sql_file); + my_fclose(sql_file, MYF(MY_WME)); + } + DBUG_RETURN((uint)num_fields); +} /* get_table_structure */ + +void MySQLContext::dump_trigger_old(FILE *sql_file, MYSQL_RES *show_triggers_rs, + MYSQL_ROW *show_trigger_row, + const char *table_name) { + char quoted_table_name_buf[NAME_LEN * 2 + 3]; + char *quoted_table_name = quote_name(table_name, quoted_table_name_buf, 1); + + char name_buff[NAME_LEN * 4 + 3]; + const char *xml_msg = + "\nWarning! mysqldump being run against old server " + "that does not\nsupport 'SHOW CREATE TRIGGERS' " + "statement. Skipping..\n"; + + DBUG_ENTER("dump_trigger_old"); + + if (opt_xml) { + print_xml_comment(sql_file, strlen(xml_msg), xml_msg); + check_io(sql_file); + DBUG_VOID_RETURN; + } + + fprintf(sql_file, + "--\n" + "-- WARNING: old server version. " + "The following dump may be incomplete.\n" + "--\n"); + + if (opt_compact) + fprintf(sql_file, "/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;\n"); + + if (opt_drop_trigger) + fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS %s */;\n", + (*show_trigger_row)[0]); + + fprintf(sql_file, + "DELIMITER ;;\n" + "/*!50003 SET SESSION SQL_MODE=\"%s\" */;;\n" + "/*!50003 CREATE */ ", + (*show_trigger_row)[6]); + + if (mysql_num_fields(show_triggers_rs) > 7) { + /* + mysqldump can be run against the server, that does not support + definer in triggers (there is no DEFINER column in SHOW TRIGGERS + output). So, we should check if we have this column before + accessing it. + */ + + size_t user_name_len; + char user_name_str[USERNAME_LENGTH + 1]; + char quoted_user_name_str[USERNAME_LENGTH * 2 + 3]; + size_t host_name_len; + char host_name_str[HOSTNAME_LENGTH + 1]; + char quoted_host_name_str[HOSTNAME_LENGTH * 2 + 3]; + + parse_user((*show_trigger_row)[7], strlen((*show_trigger_row)[7]), + user_name_str, &user_name_len, host_name_str, &host_name_len); + + fprintf(sql_file, "/*!50017 DEFINER=%s@%s */ ", + quote_name(user_name_str, quoted_user_name_str, FALSE), + quote_name(host_name_str, quoted_host_name_str, FALSE)); + } + + fprintf(sql_file, + "/*!50003 TRIGGER %s %s %s ON %s FOR EACH ROW%s%s */;;\n" + "DELIMITER ;\n", + quote_name((*show_trigger_row)[0], name_buff, 0), /* Trigger */ + (*show_trigger_row)[4], /* Timing */ + (*show_trigger_row)[1], /* Event */ + quoted_table_name, + (strchr(" \t\n\r", *((*show_trigger_row)[3]))) ? "" : " ", + (*show_trigger_row)[3] /* Statement */); + + if (opt_compact) + fprintf(sql_file, "/*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE */;\n"); + + DBUG_VOID_RETURN; +} + +int MySQLContext::dump_trigger(FILE *sql_file, + MYSQL_RES *show_create_trigger_rs, + const char *db_name, const char *db_cl_name) { + MYSQL_ROW row; + char *query_str; + int db_cl_altered = FALSE; + + DBUG_ENTER("dump_trigger"); + + while ((row = mysql_fetch_row(show_create_trigger_rs))) { + if (opt_xml) { + print_xml_row(sql_file, "trigger", show_create_trigger_rs, &row, + "SQL Original Statement"); + check_io(sql_file); + continue; + } + + query_str = cover_definer_clause( + row[2], strlen(row[2]), C_STRING_WITH_LEN("50017"), + C_STRING_WITH_LEN("50003"), C_STRING_WITH_LEN(" TRIGGER")); + if (switch_db_collation(sql_file, db_name, ";", db_cl_name, row[5], + &db_cl_altered)) + DBUG_RETURN(TRUE); + + switch_cs_variables(sql_file, ";", row[3], /* character_set_client */ + row[3], /* character_set_results */ + row[4]); /* collation_connection */ + + remove_sql_mode(row[1], C_STRING_WITH_LEN("NO_AUTO_CREATE_USER")); + switch_sql_mode(sql_file, ";", row[1]); + + if (opt_drop_trigger) + fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS %s */;\n", row[0]); + + fprintf(sql_file, + "DELIMITER ;;\n" + "/*!50003 %s */;;\n" + "DELIMITER ;\n", + (const char *)(query_str != NULL ? query_str : row[2])); + + restore_sql_mode(sql_file, ";"); + restore_cs_variables(sql_file, ";"); + + if (db_cl_altered) { + if (restore_db_collation(sql_file, db_name, ";", db_cl_name)) + DBUG_RETURN(TRUE); + } + + my_free(query_str); + } + + DBUG_RETURN(FALSE); +} + +/** + Dump the triggers for a given table. + + This should be called after the tables have been dumped in case a trigger + depends on the existence of a table. + + @param[in] table_name + @param[in] db_name + + @return Error status. + @retval TRUE error has occurred. + @retval FALSE operation succeed. +*/ + +int MySQLContext::dump_triggers_for_table(char *table_name, char *db_name) { + char name_buff[NAME_LEN * 4 + 3]; + char query_buff[QUERY_LENGTH]; + uint old_opt_compatible_mode = opt_compatible_mode; + MYSQL_RES *show_triggers_rs; + MYSQL_ROW row; + FILE *sql_file = md_result_file; + + char db_cl_name[MY_CS_NAME_SIZE]; + int ret = TRUE; + + DBUG_ENTER("dump_triggers_for_table"); + DBUG_PRINT("enter", ("db: %s, table_name: %s", db_name, table_name)); + + if (path && + !(sql_file = open_sql_file_for_table(table_name, O_WRONLY | O_APPEND))) + DBUG_RETURN(1); + + /* Do not use ANSI_QUOTES on triggers in dump */ + opt_compatible_mode &= ~MASK_ANSI_QUOTES; + + /* Get database collation. */ + + if (switch_character_set_results(mysql, "binary")) goto done; + + if (fetch_db_collation(db_name, db_cl_name, sizeof(db_cl_name))) goto done; + + /* Get list of triggers. */ + + my_snprintf(query_buff, sizeof(query_buff), "SHOW TRIGGERS LIKE %s", + quote_for_like(table_name, name_buff)); + + if (mysql_query_with_error_report(mysql, &show_triggers_rs, query_buff)) + goto done; + + /* Dump triggers. */ + + if (!mysql_num_rows(show_triggers_rs)) goto skip; + + if (opt_xml) + print_xml_tag(sql_file, "\t", "\n", "triggers", "name=", table_name, NullS); + + while ((row = mysql_fetch_row(show_triggers_rs))) { + my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE TRIGGER %s", + quote_name(row[0], name_buff, TRUE)); + + if (mysql_query(mysql, query_buff)) { + /* + mysqldump is being run against old server, that does not support + SHOW CREATE TRIGGER statement. We should use SHOW TRIGGERS output. + + NOTE: the dump may be incorrect, as old SHOW TRIGGERS does not + provide all the necessary information to restore trigger properly. + */ + + dump_trigger_old(sql_file, show_triggers_rs, &row, table_name); + } else { + MYSQL_RES *show_create_trigger_rs = mysql_store_result(mysql); + + if (!show_create_trigger_rs || + dump_trigger(sql_file, show_create_trigger_rs, db_name, db_cl_name)) + goto done; + + mysql_free_result(show_create_trigger_rs); + } + } + + if (opt_xml) { + fputs("\t\n", sql_file); + check_io(sql_file); + } + +skip: + mysql_free_result(show_triggers_rs); + + if (switch_character_set_results(mysql, default_charset)) goto done; + + /* + make sure to set back opt_compatible mode to + original value + */ + opt_compatible_mode = old_opt_compatible_mode; + + ret = FALSE; + +done: + if (path) my_fclose(sql_file, MYF(0)); + + DBUG_RETURN(ret); +} + +static void add_load_option(DYNAMIC_STRING *str, const char *option, + const char *option_value) { + if (!option_value) { + /* Null value means we don't add this option. */ + return; + } + + dynstr_append_checked(str, option); + + if (strncmp(option_value, "0x", sizeof("0x") - 1) == 0) { + /* It's a hex constant, don't escape */ + dynstr_append_checked(str, option_value); + } else { + /* char constant; escape */ + field_escape(str, option_value); + } +} + +/* + Allow the user to specify field terminator strings like: + "'", "\", "\\" (escaped backslash), "\t" (tab), "\n" (newline) + This is done by doubling ' and add a end -\ if needed to avoid + syntax errors from the SQL parser. +*/ + +static void field_escape(DYNAMIC_STRING *in, const char *from) { + uint end_backslashes = 0; + + dynstr_append_checked(in, "'"); + + while (*from) { + dynstr_append_mem_checked(in, from, 1); + + if (*from == '\\') + end_backslashes ^= 1; /* find odd number of backslashes */ + else { + if (*from == '\'' && !end_backslashes) { + /* We want a duplicate of "'" for MySQL */ + dynstr_append_checked(in, "\'"); + } + end_backslashes = 0; + } + from++; + } + /* Add missing backslashes if user has specified odd number of backs.*/ + if (end_backslashes) dynstr_append_checked(in, "\\"); + + dynstr_append_checked(in, "'"); +} + +static char *alloc_query_str(size_t size) { + char *query; + + if (!(query = (char *)my_malloc(PSI_NOT_INSTRUMENTED, size, MYF(MY_WME)))) + die(EX_MYSQLERR, "Couldn't allocate a query string."); + + return query; +} + +/* + + SYNOPSIS + dump_table() + + dump_table saves database contents as a series of INSERT statements. + + ARGS + table - table name + db - db name + + RETURNS + void +*/ + +void MySQLContext::dump_table(char *table, char *db) { + char ignore_flag; + char buf[200], table_buff[NAME_LEN + 3]; + DYNAMIC_STRING query_string; + DYNAMIC_STRING extended_row; + DYNAMIC_STRING insert_row; + char table_type[NAME_LEN]; + char *result_table, table_buff2[NAME_LEN * 2 + 3]; //, *opt_quoted_table; + int error = 0; + ulong rownr, row_break; + size_t total_length, init_length; + uint num_fields; + MYSQL_RES *res; + MYSQL_FIELD *field; + MYSQL_ROW row; + struct DMODBC::DBError dbError; + my_bool real_columns[MAX_FIELDS]; + DBUG_ENTER("dump_table"); + + /* + Make sure you get the create table info before the following check for + --no-data flag below. Otherwise, the create table info won't be printed. + */ + num_fields = + get_table_structure(table, db, table_type, &ignore_flag, real_columns); + + /* + The "table" could be a view. If so, we don't do anything here. + */ + if (strcmp(table_type, "VIEW") == 0) DBUG_VOID_RETURN; + + /* + We don't dump data fo`r replication metadata tables. + */ + if (replication_metadata_tables(db, table)) DBUG_VOID_RETURN; + + /* Check --no-data flag */ + if (opt_no_data) { + verbose_msg("-- Skipping dump data for table '%s', --no-data was used\n", + table); + DBUG_VOID_RETURN; + } + + DBUG_PRINT("info", + ("ignore_flag: %x num_fields: %d", (int)ignore_flag, num_fields)); + /* + If the table type is a merge table or any type that has to be + _completely_ ignored and no data dumped + */ + if (ignore_flag & IGNORE_DATA) { + verbose_msg( + "-- Warning: Skipping data for table '%s' because " + "it's of type %s\n", + table, table_type); + DBUG_VOID_RETURN; + } + /* Check that there are any fields in the table */ + if (num_fields == 0) { + verbose_msg("-- Skipping dump data for table '%s', it has no fields\n", + table); + DBUG_VOID_RETURN; + } + + result_table = quote_name(table, table_buff, 1); + // opt_quoted_table = + quote_name(table, table_buff2, 0); + + verbose_msg("-- Sending SELECT query...\n"); + + init_dynamic_string_checked(&query_string, "", 1024, 1024); + if (extended_insert) + init_dynamic_string_checked(&extended_row, "", 1024, 1024); + + init_dynamic_string_checked(&insert_row, "", 1024, 1024); + + if (path) { + char filename[FN_REFLEN], tmp_path[FN_REFLEN]; + + /* + Convert the path to native os format + and resolve to the full filepath. + */ + convert_dirname(tmp_path, path, NullS); + my_load_path(tmp_path, tmp_path, NULL); + fn_format(filename, table, tmp_path, ".txt", MYF(MY_UNPACK_FILENAME)); + + /* Must delete the file that 'INTO OUTFILE' will write to */ + my_delete(filename, MYF(0)); + + /* convert to a unix path name to stick into the query */ + to_unix_path(filename); + + /* now build the query string */ + + dynstr_append_checked(&query_string, + "SELECT /*!40001 SQL_NO_CACHE */ * INTO OUTFILE '"); + dynstr_append_checked(&query_string, filename); + dynstr_append_checked(&query_string, "'"); + + dynstr_append_checked(&query_string, " /*!50138 CHARACTER SET "); + dynstr_append_checked(&query_string, + default_charset == mysql_universal_client_charset + ? my_charset_bin.name + : /* backward compatibility */ + default_charset); + dynstr_append_checked(&query_string, " */"); + + if (fields_terminated || enclosed || opt_enclosed || escaped) + dynstr_append_checked(&query_string, " FIELDS"); + + add_load_option(&query_string, " TERMINATED BY ", fields_terminated); + add_load_option(&query_string, " ENCLOSED BY ", enclosed); + add_load_option(&query_string, " OPTIONALLY ENCLOSED BY ", opt_enclosed); + add_load_option(&query_string, " ESCAPED BY ", escaped); + add_load_option(&query_string, " LINES TERMINATED BY ", lines_terminated); + + dynstr_append_checked(&query_string, " FROM "); + dynstr_append_checked(&query_string, result_table); + + if (where) { + dynstr_append_checked(&query_string, " WHERE "); + dynstr_append_checked(&query_string, where); + } else if ((!my_strcasecmp(charset_info, db, "mysql")) && + (!my_strcasecmp(charset_info, table, "proc")) && opt_alldbs) { + dynstr_append_checked(&query_string, " WHERE db != 'sys'"); + } + + if (order_by) { + dynstr_append_checked(&query_string, " ORDER BY "); + dynstr_append_checked(&query_string, order_by); + } + + if (mysql_real_query(mysql, query_string.str, (ulong)query_string.length)) { + DB_error(mysql, "when executing 'SELECT INTO OUTFILE'"); + dynstr_free(&query_string); + DBUG_VOID_RETURN; + } + } else { + my_bool freemem = FALSE; + char const *text = fix_identifier_with_newline(result_table, &freemem); + print_comment(md_result_file, 0, "\n--\n-- Dumping data for table %s\n--\n", + text); + if (freemem) my_free((void *)text); + + dynstr_append_checked(&query_string, + "SELECT /*!40001 SQL_NO_CACHE */ * FROM "); + dynstr_append_checked(&query_string, result_table); + + if (where) { + freemem = FALSE; + text = fix_identifier_with_newline(where, &freemem); + print_comment(md_result_file, 0, "-- WHERE: %s\n", text); + if (freemem) my_free((void *)text); + + dynstr_append_checked(&query_string, " WHERE "); + dynstr_append_checked(&query_string, where); + } + /* + If table is mysql.proc then do not dump routines which belong + to sys schema + */ + else if ((!my_strcasecmp(charset_info, db, "mysql")) && + (!my_strcasecmp(charset_info, table, "proc")) && opt_alldbs) { + dynstr_append_checked(&query_string, " WHERE db != 'sys'"); + } + if (order_by) { + freemem = FALSE; + text = fix_identifier_with_newline(order_by, &freemem); + print_comment(md_result_file, 0, "-- ORDER BY: %s\n", text); + if (freemem) my_free((void *)text); + + dynstr_append_checked(&query_string, " ORDER BY "); + dynstr_append_checked(&query_string, order_by); + } + + if (!opt_xml && !opt_compact) { + fputs("\n", md_result_file); + check_io(md_result_file); + } + if (mysql_query_with_error_report(mysql, 0, query_string.str)) { + DB_error(mysql, "when retrieving data from server"); + goto err; + } + if (quick) + res = mysql_use_result(mysql); + else + res = mysql_store_result(mysql); + if (!res) { + DB_error(mysql, "when retrieving data from server"); + goto err; + } + + verbose_msg("-- Retrieving rows...\n"); + if (mysql_num_fields(res) != num_fields) { + fprintf(stderr, "%s: Error in field count for table: %s ! Aborting.\n", + my_progname, result_table); + error = EX_CONSCHECK; + goto err; + } + + // if (opt_lock) + // { + // fprintf(md_result_file,"LOCK TABLES %s WRITE;\n", opt_quoted_table); + // check_io(md_result_file); + // } + /* Moved disable keys to after lock per bug 15977 */ + // if (opt_disable_keys) + // { + // fprintf(md_result_file, "/*!40000 ALTER TABLE %s DISABLE KEYS */;\n", + // opt_quoted_table); + // check_io(md_result_file); + // } + + total_length = opt_net_buffer_length; /* Force row break */ + row_break = 0; + rownr = 0; + init_length = (uint)insert_pat.length + 4; + if (opt_xml) + print_xml_tag(md_result_file, "\t", "\n", "table_data", "name=", table, + NullS); + // if (opt_autocommit) + // { + // fprintf(md_result_file, "set autocommit=0;\n"); + // check_io(md_result_file); + // } + + while ((row = mysql_fetch_row(res))) { + uint i; + ulong *lengths = mysql_fetch_lengths(res); + rownr++; + // if (!extended_insert && !opt_xml) + // { + // fputs(insert_pat.str,md_result_file); + // check_io(md_result_file); + // } + mysql_field_seek(res, 0); + + if (opt_xml) { + fputs("\t\n", md_result_file); + check_io(md_result_file); + } + + for (i = 0; i < mysql_num_fields(res); i++) { + int is_blob; + ulong length = lengths[i]; + + if (!(field = mysql_fetch_field(res))) + die(EX_CONSCHECK, "Not enough fields from table %s! Aborting.\n", + result_table); + + if (!real_columns[i]) continue; + /* + 63 is my_charset_bin. If charsetnr is not 63, + we have not a BLOB but a TEXT column. + */ + is_blob = + (field->charsetnr == 63 && (field->type == MYSQL_TYPE_BIT || + field->type == MYSQL_TYPE_STRING || + field->type == MYSQL_TYPE_VAR_STRING || + field->type == MYSQL_TYPE_VARCHAR || + field->type == MYSQL_TYPE_BLOB || + field->type == MYSQL_TYPE_LONG_BLOB || + field->type == MYSQL_TYPE_MEDIUM_BLOB || + field->type == MYSQL_TYPE_TINY_BLOB || + field->type == MYSQL_TYPE_GEOMETRY)) + ? 1 + : 0; + if (extended_insert && !opt_xml) { + if (i == 0) + dynstr_set_checked(&extended_row, "("); + else + dynstr_append_checked(&extended_row, ","); + + if (row[i]) { + if (length) { + if (!(field->flags & NUM_FLAG)) { + /* + "length * 2 + 4 + 1" is OK for both HEX non-HEX modes + */ + dynstr_realloc_checked(&extended_row, length * 2 + 4 + 1); + if (opt_hex_blob && is_blob) { + dynstr_append_checked(&extended_row, "0x"); + extended_row.length += mysql_hex_string( + extended_row.str + extended_row.length, row[i], length); + DBUG_ASSERT(extended_row.length + 1 <= + extended_row.max_length); + /* mysql_hex_string() already terminated string by '\0' */ + DBUG_ASSERT(extended_row.str[extended_row.length] == '\0'); + } else { + if (is_blob) { + dynstr_append_checked(&extended_row, "'\\x"); + extended_row.length += mysql_hex_string( + extended_row.str + extended_row.length, row[i], length); + dynstr_append_checked(&extended_row, "'"); + } else { + dynstr_append_checked(&extended_row, "'"); + extended_row.length += mysql_real_escape_string_quote( + &mysql_connection, + &extended_row.str[extended_row.length], row[i], length, + '\''); + extended_row.str[extended_row.length] = '\0'; + dynstr_append_checked(&extended_row, "'"); + } + } + } else { + /* change any strings ("inf", "-inf", "nan") into NULL */ + char *ptr = row[i]; + if (my_isalpha(charset_info, *ptr) || + (*ptr == '-' && my_isalpha(charset_info, ptr[1]))) + dynstr_append_checked(&extended_row, "NULL"); + else { + if (field->type == MYSQL_TYPE_DECIMAL) { + /* add " signs around */ + dynstr_append_checked(&extended_row, "'"); + dynstr_append_checked(&extended_row, ptr); + dynstr_append_checked(&extended_row, "'"); + } else + dynstr_append_checked(&extended_row, ptr); + } + } + } else + dynstr_append_checked(&extended_row, "''"); + } else + dynstr_append_checked(&extended_row, "NULL"); + } else { + if (i && !opt_xml) { + dynstr_append_checked(&insert_row, ","); + // fputc(',', md_result_file); + // check_io(md_result_file); + } + if (row[i]) { + if (!(field->flags & NUM_FLAG)) { + if (opt_xml) { + if (opt_hex_blob && is_blob && length) { + /* Define xsi:type="xs:hexBinary" for hex encoded data */ + print_xml_tag(md_result_file, "\t\t", "", "field", + "name=", field->name, + "xsi:type=", "xs:hexBinary", NullS); + print_blob_as_hex(md_result_file, row[i], length); + } else { + print_xml_tag(md_result_file, "\t\t", "", "field", + "name=", field->name, NullS); + print_quoted_xml(md_result_file, row[i], length, 0); + } + fputs("\n", md_result_file); + } else if (opt_hex_blob && is_blob && length) { + dynstr_append_checked(&insert_row, "0x"); + // fputs("0x", md_result_file); + append_blob_as_hex(&insert_row, row[i], length); + // print_blob_as_hex(md_result_file, row[i], length); + } else { + if (is_blob) { + // dynstr_append_checked(&insert_row,"_binary "); + // fputs("_binary ", md_result_file); + // check_io(md_result_file); + } + // unescape(md_result_file, row[i], length, &insert_row); + unescapestr(row[i], length, &insert_row); + } + } else { + /* change any strings ("inf", "-inf", "nan") into NULL */ + char *ptr = row[i]; + if (opt_xml) { + print_xml_tag(md_result_file, "\t\t", "", "field", + "name=", field->name, NullS); + fputs(!my_isalpha(charset_info, *ptr) ? ptr : "NULL", + md_result_file); + fputs("\n", md_result_file); + } else if (my_isalpha(charset_info, *ptr) || + (*ptr == '-' && my_isalpha(charset_info, ptr[1]))) { + dynstr_append_checked(&insert_row, "NULL "); + // fputs("NULL", md_result_file); + } else if (field->type == MYSQL_TYPE_DECIMAL) { + /* add " signs around */ + dynstr_append_checked(&insert_row, "\'"); + dynstr_append_checked(&insert_row, ptr); + dynstr_append_checked(&insert_row, "\'"); + // fputc('\'', md_result_file); + // fputs(ptr, md_result_file); + // fputc('\'', md_result_file); + } else { + dynstr_append_checked(&insert_row, ptr); + // fputs(ptr, md_result_file); + } + } + } else { + /* The field value is NULL */ + if (!opt_xml) { + // fputs("NULL", md_result_file); + dynstr_append_checked(&insert_row, "NULL"); + } + // else + // print_xml_null_tag(md_result_file, "\t\t", "field name=", + // field->name, "\n"); + } + // check_io(md_result_file); + } + } + + if (opt_xml) { + fputs("\t\n", md_result_file); + check_io(md_result_file); + } + + if (extended_insert) { + size_t row_length; + dynstr_append_checked(&extended_row, ")"); + row_length = 2 + extended_row.length; + if (total_length + row_length < opt_net_buffer_length) { + total_length += row_length; + dynstr_append_checked(&insert_row, ","); + dynstr_append_checked(&insert_row, extended_row.str); + // fputc(',',md_result_file); /* Always row break */ + // fputs(extended_row.str,md_result_file); + } else { + if (row_break) { + // fputs(";\n", md_result_file); + dynstr_append_checked(&insert_row, ";\n"); + + if (sg_createSuccessTables.find(table) != + sg_createSuccessTables.end()) { + // 濡傛灉琛ㄥ凡缁忓垱寤烘垚鍔燂紝閭d箞鎻掑叆sql + sg_procMetrics->startSql(table, "table", insert_row.str); + if (sg_procOdbc->executeInsertSql(insert_row.str, &dbError)) { + // success + sg_procMetrics->endSql(table, "table", insert_row.str, false, + "", true); + } else { + // failed + sg_procMetrics->endSql(table, "table", insert_row.str, true, + dbError.error.c_str(), true); + } + } + dynstr_set_checked(&insert_row, ""); + } + row_break = 1; /* This is first row */ + + dynstr_append_checked(&insert_row, insert_pat.str); + dynstr_append_checked(&insert_row, extended_row.str); + // fputs(insert_pat.str,md_result_file); + // fputs(extended_row.str,md_result_file); + total_length = row_length + init_length; + } + // check_io(md_result_file); + } else if (!opt_xml) { + dynstr_append_checked(&insert_row, ");\n"); + // fputs(");\n", md_result_file); + // check_io(md_result_file); + } + } + + /* XML - close table tag and supress regular output */ + if (opt_xml) + fputs("\t\n", md_result_file); + else if (extended_insert && row_break) { + dynstr_append_checked(&insert_row, ";\n"); + // fputs(";\n", md_result_file); /* If not empty table */ + } + // fflush(md_result_file); + // check_io(md_result_file); + + if (strlen(insert_row.str) > 0) { + if (sg_createSuccessTables.find(table) != sg_createSuccessTables.end()) { + // 濡傛灉琛ㄥ凡缁忓垱寤烘垚鍔燂紝閭d箞鎻掑叆sql + sg_procMetrics->startSql(table, "table", insert_row.str); + if (sg_procOdbc->executeInsertSql(insert_row.str, &dbError)) { + // success + sg_procMetrics->endSql(table, "table", insert_row.str, false, "", true); + } else { + // failed + sg_procMetrics->endSql(table, "table", insert_row.str, true, + dbError.error.c_str(), true); + } + } + } + + if (mysql_errno(mysql)) { + my_snprintf(buf, sizeof(buf), + "%s: Error %d: %s when dumping table %s at row: %ld\n", + my_progname, mysql_errno(mysql), mysql_error(mysql), + result_table, rownr); + fputs(buf, stderr); + error = EX_CONSCHECK; + goto err; + } + + /* Moved enable keys to before unlock per bug 15977 */ + if (opt_disable_keys) { + // fprintf(md_result_file,"/*!40000 ALTER TABLE %s ENABLE KEYS */;\n", + // opt_quoted_table); + // check_io(md_result_file); + } + if (opt_lock) { + // fputs("UNLOCK TABLES;\n", md_result_file); + // check_io(md_result_file); + } + if (opt_autocommit) { + // fprintf(md_result_file, "commit;\n"); + // check_io(md_result_file); + } + mysql_free_result(res); + } + dynstr_free(&query_string); + if (extended_insert) dynstr_free(&extended_row); + dynstr_free(&insert_row); + DBUG_VOID_RETURN; + +err: + dynstr_free(&query_string); + if (extended_insert) dynstr_free(&extended_row); + dynstr_free(&insert_row); + maybe_exit(error); + DBUG_VOID_RETURN; +} /* dump_table */ + +char *MySQLContext::getTableName(int reset) { + static MYSQL_RES *res = NULL; + MYSQL_ROW row; + + if (!res) { + if (!(res = mysql_list_tables(mysql, NullS))) return (NULL); + } + if ((row = mysql_fetch_row(res))) return ((char *)row[0]); + + if (reset) + mysql_data_seek(res, 0); /* We want to read again */ + else { + mysql_free_result(res); + res = NULL; + } + return (NULL); +} /* getTableName */ + +/* + dump all logfile groups and tablespaces +*/ + +int MySQLContext::dump_all_tablespaces() { return dump_tablespaces(NULL); } + +int MySQLContext::dump_tablespaces_for_tables(char *db, char **table_names, + int tables) { + DYNAMIC_STRING where; + int r; + int i; + char name_buff[NAME_LEN * 2 + 3]; + + mysql_real_escape_string_quote(mysql, name_buff, db, (ulong)strlen(db), '\''); + + init_dynamic_string_checked(&where, + " AND TABLESPACE_NAME IN (" + "SELECT DISTINCT TABLESPACE_NAME FROM" + " INFORMATION_SCHEMA.PARTITIONS" + " WHERE" + " TABLE_SCHEMA='", + 256, 1024); + dynstr_append_checked(&where, name_buff); + dynstr_append_checked(&where, "' AND TABLE_NAME IN ("); + + for (i = 0; i < tables; i++) { + mysql_real_escape_string_quote(mysql, name_buff, table_names[i], + (ulong)strlen(table_names[i]), '\''); + + dynstr_append_checked(&where, "'"); + dynstr_append_checked(&where, name_buff); + dynstr_append_checked(&where, "',"); + } + dynstr_trunc(&where, 1); + dynstr_append_checked(&where, "))"); + + DBUG_PRINT("info", ("Dump TS for Tables where: %s", where.str)); + r = dump_tablespaces(where.str); + dynstr_free(&where); + return r; +} + +int MySQLContext::dump_tablespaces_for_databases(char **databases) { + DYNAMIC_STRING where; + int r; + int i; + + init_dynamic_string_checked(&where, + " AND TABLESPACE_NAME IN (" + "SELECT DISTINCT TABLESPACE_NAME FROM" + " INFORMATION_SCHEMA.PARTITIONS" + " WHERE" + " TABLE_SCHEMA IN (", + 256, 1024); + + for (i = 0; databases[i] != NULL; i++) { + char db_name_buff[NAME_LEN * 2 + 3]; + mysql_real_escape_string_quote(mysql, db_name_buff, databases[i], + (ulong)strlen(databases[i]), '\''); + dynstr_append_checked(&where, "'"); + dynstr_append_checked(&where, db_name_buff); + dynstr_append_checked(&where, "',"); + } + dynstr_trunc(&where, 1); + dynstr_append_checked(&where, "))"); + + DBUG_PRINT("info", ("Dump TS for DBs where: %s", where.str)); + r = dump_tablespaces(where.str); + dynstr_free(&where); + return r; +} + +int MySQLContext::dump_tablespaces(char *ts_where) { + MYSQL_ROW row; + MYSQL_RES *tableres; + char buf[FN_REFLEN]; + DYNAMIC_STRING sqlbuf; + int first = 0; + /* + The following are used for parsing the EXTRA field + */ + char extra_format[] = "UNDO_BUFFER_SIZE="; + char *ubs; + char *endsemi; + DBUG_ENTER("dump_tablespaces"); + + init_dynamic_string_checked(&sqlbuf, + "SELECT LOGFILE_GROUP_NAME," + " FILE_NAME," + " TOTAL_EXTENTS," + " INITIAL_SIZE," + " ENGINE," + " EXTRA" + " FROM INFORMATION_SCHEMA.FILES" + " WHERE FILE_TYPE = 'UNDO LOG'" + " AND FILE_NAME IS NOT NULL" + " AND LOGFILE_GROUP_NAME IS NOT NULL", + 256, 1024); + if (ts_where) { + dynstr_append_checked(&sqlbuf, + " AND LOGFILE_GROUP_NAME IN (" + "SELECT DISTINCT LOGFILE_GROUP_NAME" + " FROM INFORMATION_SCHEMA.FILES" + " WHERE FILE_TYPE = 'DATAFILE'"); + dynstr_append_checked(&sqlbuf, ts_where); + dynstr_append_checked(&sqlbuf, ")"); + } + dynstr_append_checked(&sqlbuf, + " GROUP BY LOGFILE_GROUP_NAME, FILE_NAME" + ", ENGINE, TOTAL_EXTENTS, INITIAL_SIZE" + " ORDER BY LOGFILE_GROUP_NAME"); + + if (mysql_query(mysql, sqlbuf.str) || + !(tableres = mysql_store_result(mysql))) { + dynstr_free(&sqlbuf); + if (mysql_errno(mysql) == ER_BAD_TABLE_ERROR || + mysql_errno(mysql) == ER_BAD_DB_ERROR || + mysql_errno(mysql) == ER_UNKNOWN_TABLE) { + fprintf(md_result_file, + "\n--\n-- Not dumping tablespaces as no INFORMATION_SCHEMA.FILES" + " table on this server\n--\n"); + check_io(md_result_file); + DBUG_RETURN(0); + } + + my_printf_error(0, "Error: '%s' when trying to dump tablespaces", MYF(0), + mysql_error(mysql)); + DBUG_RETURN(1); + } + + buf[0] = 0; + while ((row = mysql_fetch_row(tableres))) { + if (strcmp(buf, row[0]) != 0) first = 1; + if (first) { + print_comment(md_result_file, 0, "\n--\n-- Logfile group: %s\n--\n", + row[0]); + + fprintf(md_result_file, "\nCREATE"); + } else { + fprintf(md_result_file, "\nALTER"); + } + fprintf(md_result_file, + " LOGFILE GROUP %s\n" + " ADD UNDOFILE '%s'\n", + row[0], row[1]); + if (first) { + ubs = strstr(row[5], extra_format); + if (!ubs) break; + ubs += strlen(extra_format); + endsemi = strstr(ubs, ";"); + if (endsemi) endsemi[0] = '\0'; + fprintf(md_result_file, " UNDO_BUFFER_SIZE %s\n", ubs); + } + fprintf(md_result_file, + " INITIAL_SIZE %s\n" + " ENGINE=%s;\n", + row[3], row[4]); + check_io(md_result_file); + if (first) { + first = 0; + strxmov(buf, row[0], NullS); + } + } + dynstr_free(&sqlbuf); + mysql_free_result(tableres); + init_dynamic_string_checked(&sqlbuf, + "SELECT DISTINCT TABLESPACE_NAME," + " FILE_NAME," + " LOGFILE_GROUP_NAME," + " EXTENT_SIZE," + " INITIAL_SIZE," + " ENGINE" + " FROM INFORMATION_SCHEMA.FILES" + " WHERE FILE_TYPE = 'DATAFILE'", + 256, 1024); + + if (ts_where) dynstr_append_checked(&sqlbuf, ts_where); + + dynstr_append_checked(&sqlbuf, + " ORDER BY TABLESPACE_NAME, LOGFILE_GROUP_NAME"); + + if (mysql_query_with_error_report(mysql, &tableres, sqlbuf.str)) { + dynstr_free(&sqlbuf); + DBUG_RETURN(1); + } + + buf[0] = 0; + while ((row = mysql_fetch_row(tableres))) { + if (strcmp(buf, row[0]) != 0) first = 1; + if (first) { + print_comment(md_result_file, 0, "\n--\n-- Tablespace: %s\n--\n", row[0]); + fprintf(md_result_file, "\nCREATE"); + } else { + fprintf(md_result_file, "\nALTER"); + } + fprintf(md_result_file, + " TABLESPACE %s\n" + " ADD DATAFILE '%s'\n", + row[0], row[1]); + if (first) { + fprintf(md_result_file, + " USE LOGFILE GROUP %s\n" + " EXTENT_SIZE %s\n", + row[2], row[3]); + } + fprintf(md_result_file, + " INITIAL_SIZE %s\n" + " ENGINE=%s;\n", + row[4], row[5]); + check_io(md_result_file); + if (first) { + first = 0; + strxmov(buf, row[0], NullS); + } + } + + mysql_free_result(tableres); + dynstr_free(&sqlbuf); + DBUG_RETURN(0); +} + +int MySQLContext::is_ndbinfo(MYSQL *mysql, const char *dbname) { + static int checked_ndbinfo = 0; + static int have_ndbinfo = 0; + + if (!checked_ndbinfo) { + MYSQL_RES *res; + MYSQL_ROW row; + char buf[32], query[64]; + + my_snprintf(query, sizeof(query), "SHOW VARIABLES LIKE %s", + quote_for_like("ndbinfo_version", buf)); + + checked_ndbinfo = 1; + + if (mysql_query_with_error_report(mysql, &res, query)) return 0; + + if (!(row = mysql_fetch_row(res))) { + mysql_free_result(res); + return 0; + } + + have_ndbinfo = 1; + mysql_free_result(res); + } + + if (!have_ndbinfo) return 0; + + if (my_strcasecmp(&my_charset_latin1, dbname, "ndbinfo") == 0) return 1; + + return 0; +} + +int MySQLContext::dump_all_databases() { + MYSQL_ROW row; + MYSQL_RES *tableres; + int result = 0; + + if (mysql_query_with_error_report(mysql, &tableres, "SHOW DATABASES")) + return 1; + while ((row = mysql_fetch_row(tableres))) { + if (mysql_get_server_version(mysql) >= FIRST_INFORMATION_SCHEMA_VERSION && + !my_strcasecmp(&my_charset_latin1, row[0], INFORMATION_SCHEMA_DB_NAME)) + continue; + + if (mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION && + !my_strcasecmp(&my_charset_latin1, row[0], PERFORMANCE_SCHEMA_DB_NAME)) + continue; + + if (mysql_get_server_version(mysql) >= FIRST_SYS_SCHEMA_VERSION && + !my_strcasecmp(&my_charset_latin1, row[0], SYS_SCHEMA_DB_NAME)) + continue; + + if (is_ndbinfo(mysql, row[0])) continue; + + if (dump_all_tables_in_db(row[0])) result = 1; + } + mysql_free_result(tableres); + if (seen_views) { + if (mysql_query(mysql, "SHOW DATABASES") || + !(tableres = mysql_store_result(mysql))) { + my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s", MYF(0), + mysql_error(mysql)); + return 1; + } + while ((row = mysql_fetch_row(tableres))) { + if (mysql_get_server_version(mysql) >= FIRST_INFORMATION_SCHEMA_VERSION && + !my_strcasecmp(&my_charset_latin1, row[0], + INFORMATION_SCHEMA_DB_NAME)) + continue; + + if (mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION && + !my_strcasecmp(&my_charset_latin1, row[0], + PERFORMANCE_SCHEMA_DB_NAME)) + continue; + + if (mysql_get_server_version(mysql) >= FIRST_SYS_SCHEMA_VERSION && + !my_strcasecmp(&my_charset_latin1, row[0], SYS_SCHEMA_DB_NAME)) + continue; + + if (is_ndbinfo(mysql, row[0])) continue; + + if (dump_all_views_in_db(row[0])) result = 1; + } + mysql_free_result(tableres); + } + return result; +} +/* dump_all_databases */ + +int MySQLContext::dump_databases(char **db_names) { + int result = 0; + char **db; + DBUG_ENTER("dump_databases"); + + for (db = db_names; *db; db++) { + if (dump_all_tables_in_db(*db)) result = 1; + } + if (!result && seen_views) { + for (db = db_names; *db; db++) { + if (dump_all_views_in_db(*db)) result = 1; + } + } + DBUG_RETURN(result); +} /* dump_databases */ + +/* +View Specific database initalization. + +SYNOPSIS + init_dumping_views + qdatabase quoted name of the database + +RETURN VALUES + 0 Success. + 1 Failure. +*/ +int MySQLContext::init_dumping_views(char *qdatabase MY_ATTRIBUTE((unused))) { + return 0; +} /* init_dumping_views */ + +/* +Table Specific database initalization. + +SYNOPSIS + init_dumping_tables + qdatabase quoted name of the database + +RETURN VALUES + 0 Success. + 1 Failure. +*/ + +int MySQLContext::init_dumping_tables(char *qdatabase) { + DBUG_ENTER("init_dumping_tables"); + + if (!opt_create_db) { + char qbuf[256]; + MYSQL_ROW row; + MYSQL_RES *dbinfo; + + my_snprintf(qbuf, sizeof(qbuf), "SHOW CREATE DATABASE IF NOT EXISTS %s", + qdatabase); + + if (mysql_query(mysql, qbuf) || !(dbinfo = mysql_store_result(mysql))) { + /* Old server version, dump generic CREATE DATABASE */ + if (opt_drop_database) + fprintf(md_result_file, "\n/*!40000 DROP DATABASE IF EXISTS %s*/;\n", + qdatabase); + fprintf(md_result_file, + "\nCREATE DATABASE /*!32312 IF NOT EXISTS*/ %s;\n", qdatabase); + } else { + if (opt_drop_database) + fprintf(md_result_file, "\n/*!40000 DROP DATABASE IF EXISTS %s*/;\n", + qdatabase); + row = mysql_fetch_row(dbinfo); + if (row[1]) { + fprintf(md_result_file, "\n%s;\n", row[1]); + } + mysql_free_result(dbinfo); + } + } + DBUG_RETURN(0); +} /* init_dumping_tables */ + +int MySQLContext::init_dumping(char *database, int init_func) { + if (is_ndbinfo(mysql, database)) { + verbose_msg("-- Skipping dump of ndbinfo database\n"); + return 0; + } + + if (mysql_select_db(mysql, database)) { + DB_error(mysql, "when selecting the database"); + return 1; /* If --force */ + } + if (!path && !opt_xml) { + if (opt_databases || opt_alldbs) { + /* + length of table name * 2 (if name contains quotes), 2 quotes and 0 + */ + char quoted_database_buf[NAME_LEN * 2 + 3]; + char *qdatabase = quote_name(database, quoted_database_buf, opt_quoted); + my_bool freemem = FALSE; + char const *text = fix_identifier_with_newline(qdatabase, &freemem); + + print_comment(md_result_file, 0, "\n--\n-- Current Database: %s\n--\n", + text); + if (freemem) my_free((void *)text); + + /* Call the view or table specific function */ + switch (init_func) { + case 1: + init_dumping_tables(qdatabase); + break; + case 2: + init_dumping_views(qdatabase); + break; + } + + fprintf(md_result_file, "\nUSE %s;\n", qdatabase); + check_io(md_result_file); + } + } + return 0; +} /* init_dumping */ + +/* Return 1 if we should copy the table */ + +my_bool include_table(const uchar *hash_key, size_t len) { + return !my_hash_search(&ignore_table, hash_key, len); +} + +int MySQLContext::dump_all_tables_in_db(char *database) { + char *table; + uint numrows; + char table_buff[NAME_LEN * 2 + 3]; + char hash_key[2 * NAME_LEN + 2]; /* "db.tablename" */ + char *afterdot; + my_bool general_log_table_exists = 0, slow_log_table_exists = 0; + int using_mysql_db = !my_strcasecmp(charset_info, database, "mysql"); + my_bool real_columns[MAX_FIELDS]; + + DBUG_ENTER("dump_all_tables_in_db"); + + afterdot = my_stpcpy(hash_key, database); + *afterdot++ = '.'; + + if (init_dumping(database, 1)) DBUG_RETURN(1); + if (opt_xml) + print_xml_tag(md_result_file, "", "\n", "database", "name=", database, + NullS); + + if (lock_tables) { + DYNAMIC_STRING query; + init_dynamic_string_checked(&query, "LOCK TABLES ", 256, 1024); + for (numrows = 0; (table = getTableName(1));) { + char *end = my_stpcpy(afterdot, table); + if (include_table((uchar *)hash_key, end - hash_key)) { + numrows++; + dynstr_append_checked(&query, quote_name(table, table_buff, 1)); + dynstr_append_checked(&query, " READ /*!32311 LOCAL */,"); + } + } + if (numrows && + mysql_real_query(mysql, query.str, (ulong)(query.length - 1))) + DB_error(mysql, "when using LOCK TABLES"); + /* We shall continue here, if --force was given */ + dynstr_free(&query); + } + if (flush_logs) { + if (mysql_refresh(mysql, REFRESH_LOG)) + DB_error(mysql, "when doing refresh"); + /* We shall continue here, if --force was given */ + else + verbose_msg("-- dump_all_tables_in_db : logs flushed successfully!\n"); + } + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) { + verbose_msg("-- Setting savepoint...\n"); + if (mysql_query_with_error_report(mysql, 0, "SAVEPOINT sp")) DBUG_RETURN(1); + } + + std::list alltables; + while ((table = getTableName(0))) { + alltables.push_back(table); + } + + sg_firstdumptables = true; + while (alltables.size() > 0) { + for (std::list::iterator iter = alltables.begin(); + iter != alltables.end(); iter++) { + table = (char *)iter->c_str(); + sg_procMetrics->startTable(table); + char *end = my_stpcpy(afterdot, table); + if (include_table((uchar *)hash_key, end - hash_key)) { + dump_table(table, database); + my_free(order_by); + order_by = 0; + if (opt_dump_triggers && mysql_get_server_version(mysql) >= 50009) { + if (dump_triggers_for_table(table, database)) { + if (path) my_fclose(md_result_file, MYF(MY_WME)); + maybe_exit(EX_MYSQLERR); + } + } + + /** + ROLLBACK TO SAVEPOINT in --single-transaction mode to release metadata + lock on table which was already dumped. This allows to avoid blocking + concurrent DDL on this table without sacrificing correctness, as we + won't access table second time and dumps created by + --single-transaction mode have validity point at the start of + transaction anyway. Note that this doesn't make --single-transaction + mode with concurrent DDL safe in general case. It just improves + situation for people for whom it might be working. + */ + if (opt_single_transaction && + mysql_get_server_version(mysql) >= 50500) { + verbose_msg("-- Rolling back to savepoint sp...\n"); + if (mysql_query_with_error_report(mysql, 0, + "ROLLBACK TO SAVEPOINT sp")) + maybe_exit(EX_MYSQLERR); + } + } else { + /* + If general_log and slow_log exists in the 'mysql' database, + we should dump the table structure. But we cannot + call get_table_structure() here as 'LOCK TABLES' query got executed + above on the session and that 'LOCK TABLES' query does not contain + 'general_log' and 'slow_log' tables. (you cannot acquire lock + on log tables). Hence mark the existence of these log tables here and + after 'UNLOCK TABLES' query is executed on the session, get the table + structure from server and dump it in the file. + */ + if (using_mysql_db) { + if (!my_strcasecmp(charset_info, table, "general_log")) + general_log_table_exists = 1; + else if (!my_strcasecmp(charset_info, table, "slow_log")) + slow_log_table_exists = 1; + } + } + sg_procMetrics->endTable(table); + } + + alltables = sg_createFailedTables; + sg_createFailedTables = std::list(); + sg_firstdumptables = false; + } + + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) { + verbose_msg("-- Releasing savepoint...\n"); + if (mysql_query_with_error_report(mysql, 0, "RELEASE SAVEPOINT sp")) + DBUG_RETURN(1); + } + + if (opt_events && mysql_get_server_version(mysql) >= 50106) { + DBUG_PRINT("info", ("Dumping events for database %s", database)); + dump_events_for_db(database); + } + if (opt_routines && mysql_get_server_version(mysql) >= 50009) { + DBUG_PRINT("info", ("Dumping routines for database %s", database)); + dump_routines_for_db(database); + } + if (opt_xml) { + fputs("\n", md_result_file); + check_io(md_result_file); + } + if (lock_tables) + (void)mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES"); + if (using_mysql_db) { + char table_type[NAME_LEN]; + char ignore_flag; + if (general_log_table_exists) { + if (!get_table_structure((char *)"general_log", database, table_type, + &ignore_flag, real_columns)) + verbose_msg( + "-- Warning: get_table_structure() failed with some internal " + "error for 'general_log' table\n"); + } + if (slow_log_table_exists) { + if (!get_table_structure((char *)"slow_log", database, table_type, + &ignore_flag, real_columns)) + verbose_msg( + "-- Warning: get_table_structure() failed with some internal " + "error for 'slow_log' table\n"); + } + } + if (flush_privileges && using_mysql_db) { + fprintf(md_result_file, "\n--\n-- Flush Grant Tables \n--\n"); + fprintf(md_result_file, "\n/*! FLUSH PRIVILEGES */;\n"); + } + DBUG_RETURN(0); +} /* dump_all_tables_in_db */ + +/* + dump structure of views of database + + SYNOPSIS + dump_all_views_in_db() + database database name + + RETURN + 0 OK + 1 ERROR +*/ + +my_bool MySQLContext::dump_all_views_in_db(char *database) { + char *table; + uint numrows; + char table_buff[NAME_LEN * 2 + 3]; + char hash_key[2 * NAME_LEN + 2]; /* "db.tablename" */ + char *afterdot; + + afterdot = my_stpcpy(hash_key, database); + *afterdot++ = '.'; + + if (init_dumping(database, 2)) return 1; + if (opt_xml) + print_xml_tag(md_result_file, "", "\n", "database", "name=", database, + NullS); + if (lock_tables) { + DYNAMIC_STRING query; + init_dynamic_string_checked(&query, "LOCK TABLES ", 256, 1024); + for (numrows = 0; (table = getTableName(1));) { + char *end = my_stpcpy(afterdot, table); + if (include_table((uchar *)hash_key, end - hash_key)) { + numrows++; + dynstr_append_checked(&query, quote_name(table, table_buff, 1)); + dynstr_append_checked(&query, " READ /*!32311 LOCAL */,"); + } + } + if (numrows && + mysql_real_query(mysql, query.str, (ulong)(query.length - 1))) + DB_error(mysql, "when using LOCK TABLES"); + /* We shall continue here, if --force was given */ + dynstr_free(&query); + } + if (flush_logs) { + if (mysql_refresh(mysql, REFRESH_LOG)) + DB_error(mysql, "when doing refresh"); + /* We shall continue here, if --force was given */ + else + verbose_msg("-- dump_all_views_in_db : logs flushed successfully!\n"); + } + while ((table = getTableName(0))) { + char *end = my_stpcpy(afterdot, table); + if (include_table((uchar *)hash_key, end - hash_key)) + get_view_structure(table, database); + } + if (opt_xml) { + fputs("\n", md_result_file); + check_io(md_result_file); + } + if (lock_tables) + (void)mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES"); + return 0; +} /* dump_all_tables_in_db */ + +/* + get_actual_table_name -- executes a SHOW TABLES LIKE '%s' to get the actual + table name from the server for the table name given on the command line. + we do this because the table name given on the command line may be a + different case (e.g. T1 vs t1) + + RETURN + pointer to the table name + 0 if error +*/ + +char *MySQLContext::get_actual_table_name(const char *old_table_name, + MEM_ROOT *root) { + char *name = 0; + MYSQL_RES *table_res; + MYSQL_ROW row; + char query[50 + 2 * NAME_LEN]; + char show_name_buff[FN_REFLEN]; + DBUG_ENTER("get_actual_table_name"); + + /* Check memory for quote_for_like() */ + DBUG_ASSERT(2 * sizeof(old_table_name) < sizeof(show_name_buff)); + my_snprintf(query, sizeof(query), "SHOW TABLES LIKE %s", + quote_for_like(old_table_name, show_name_buff)); + + if (mysql_query_with_error_report(mysql, 0, query)) DBUG_RETURN(NullS); + + if ((table_res = mysql_store_result(mysql))) { + my_ulonglong num_rows = mysql_num_rows(table_res); + if (num_rows > 0) { + ulong *lengths; + /* + Return first row + TODO: Return all matching rows + */ + row = mysql_fetch_row(table_res); + lengths = mysql_fetch_lengths(table_res); + name = strmake_root(root, row[0], lengths[0]); + } + mysql_free_result(table_res); + } + DBUG_PRINT("exit", ("new_table_name: %s", name)); + DBUG_RETURN(name); +} + +int MySQLContext::dump_selected_tables(char *db, char **table_names, + int tables) { + char table_buff[NAME_LEN * 2 + 3]; + DYNAMIC_STRING lock_tables_query; + MEM_ROOT root; + char **dump_tables, **pos, **end; + DBUG_ENTER("dump_selected_tables"); + + if (init_dumping(db, 1)) DBUG_RETURN(1); + + init_alloc_root(PSI_NOT_INSTRUMENTED, &root, 8192, 0); + if (!(dump_tables = pos = + (char **)alloc_root(&root, tables * sizeof(char *)))) + die(EX_EOM, "alloc_root failure."); + + init_dynamic_string_checked(&lock_tables_query, "LOCK TABLES ", 256, 1024); + for (; tables > 0; tables--, table_names++) { + /* the table name passed on commandline may be wrong case */ + if ((*pos = get_actual_table_name(*table_names, &root))) { + /* Add found table name to lock_tables_query */ + if (lock_tables) { + dynstr_append_checked(&lock_tables_query, + quote_name(*pos, table_buff, 1)); + dynstr_append_checked(&lock_tables_query, " READ /*!32311 LOCAL */,"); + } + pos++; + } else { + if (!opt_force) { + dynstr_free(&lock_tables_query); + free_root(&root, MYF(0)); + } + maybe_die(EX_ILLEGAL_TABLE, "Couldn't find table: \"%s\"", *table_names); + /* We shall countinue here, if --force was given */ + } + } + end = pos; + + /* Can't LOCK TABLES in I_S / P_S, so don't try. */ + if (lock_tables && + !(mysql_get_server_version(mysql) >= FIRST_INFORMATION_SCHEMA_VERSION && + !my_strcasecmp(&my_charset_latin1, db, INFORMATION_SCHEMA_DB_NAME)) && + !(mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION && + !my_strcasecmp(&my_charset_latin1, db, PERFORMANCE_SCHEMA_DB_NAME))) { + if (mysql_real_query(mysql, lock_tables_query.str, + (ulong)(lock_tables_query.length - 1))) { + if (!opt_force) { + dynstr_free(&lock_tables_query); + free_root(&root, MYF(0)); + } + DB_error(mysql, "when doing LOCK TABLES"); + /* We shall countinue here, if --force was given */ + } + } + dynstr_free(&lock_tables_query); + if (flush_logs) { + if (mysql_refresh(mysql, REFRESH_LOG)) { + if (!opt_force) free_root(&root, MYF(0)); + DB_error(mysql, "when doing refresh"); + } + /* We shall countinue here, if --force was given */ + else + verbose_msg("-- dump_selected_tables : logs flushed successfully!\n"); + } + if (opt_xml) + print_xml_tag(md_result_file, "", "\n", "database", "name=", db, NullS); + + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) { + verbose_msg("-- Setting savepoint...\n"); + if (mysql_query_with_error_report(mysql, 0, "SAVEPOINT sp")) DBUG_RETURN(1); + } + + /* Dump each selected table */ + for (pos = dump_tables; pos < end; pos++) { + DBUG_PRINT("info", ("Dumping table %s", *pos)); + dump_table(*pos, db); + if (opt_dump_triggers && mysql_get_server_version(mysql) >= 50009) { + if (dump_triggers_for_table(*pos, db)) { + if (path) my_fclose(md_result_file, MYF(MY_WME)); + maybe_exit(EX_MYSQLERR); + } + } + + /** + ROLLBACK TO SAVEPOINT in --single-transaction mode to release metadata + lock on table which was already dumped. This allows to avoid blocking + concurrent DDL on this table without sacrificing correctness, as we + won't access table second time and dumps created by --single-transaction + mode have validity point at the start of transaction anyway. + Note that this doesn't make --single-transaction mode with concurrent + DDL safe in general case. It just improves situation for people for whom + it might be working. + */ + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) { + verbose_msg("-- Rolling back to savepoint sp...\n"); + if (mysql_query_with_error_report(mysql, 0, "ROLLBACK TO SAVEPOINT sp")) + maybe_exit(EX_MYSQLERR); + } + } + + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) { + verbose_msg("-- Releasing savepoint...\n"); + if (mysql_query_with_error_report(mysql, 0, "RELEASE SAVEPOINT sp")) + DBUG_RETURN(1); + } + + /* Dump each selected view */ + if (seen_views) { + for (pos = dump_tables; pos < end; pos++) get_view_structure(*pos, db); + } + if (opt_events && mysql_get_server_version(mysql) >= 50106) { + DBUG_PRINT("info", ("Dumping events for database %s", db)); + dump_events_for_db(db); + } + /* obtain dump of routines (procs/functions) */ + if (opt_routines && mysql_get_server_version(mysql) >= 50009) { + DBUG_PRINT("info", ("Dumping routines for database %s", db)); + dump_routines_for_db(db); + } + free_root(&root, MYF(0)); + my_free(order_by); + order_by = 0; + if (opt_xml) { + fputs("\n", md_result_file); + check_io(md_result_file); + } + if (lock_tables) + (void)mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES"); + DBUG_RETURN(0); +} /* dump_selected_tables */ + +int MySQLContext::do_show_master_status(MYSQL *mysql_con) { + MYSQL_ROW row; + MYSQL_RES *master; + const char *comment_prefix = + (opt_master_data == MYSQL_OPT_MASTER_DATA_COMMENTED_SQL) ? "-- " : ""; + if (mysql_query_with_error_report(mysql_con, &master, "SHOW MASTER STATUS")) { + return 1; + } else { + row = mysql_fetch_row(master); + if (row && row[0] && row[1]) { + /* SHOW MASTER STATUS reports file and position */ + print_comment(md_result_file, 0, + "\n--\n-- Position to start replication or point-in-time " + "recovery from\n--\n\n"); + fprintf(md_result_file, + "%sCHANGE MASTER TO MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", + comment_prefix, row[0], row[1]); + check_io(md_result_file); + } else if (!opt_force) { + /* SHOW MASTER STATUS reports nothing and --force is not enabled */ + my_printf_error(0, "Error: Binlogging on server not active", MYF(0)); + mysql_free_result(master); + maybe_exit(EX_MYSQLERR); + return 1; + } + mysql_free_result(master); + } + return 0; +} + +int MySQLContext::do_stop_slave_sql(MYSQL *mysql_con) { + MYSQL_RES *slave; + /* We need to check if the slave sql is running in the first place */ + if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS")) + return (1); + else { + MYSQL_ROW row = mysql_fetch_row(slave); + if (row && row[11]) { + /* if SLAVE SQL is not running, we don't stop it */ + if (!strcmp(row[11], "No")) { + mysql_free_result(slave); + /* Silently assume that they don't have the slave running */ + return (0); + } + } + } + mysql_free_result(slave); + + /* now, stop slave if running */ + if (mysql_query_with_error_report(mysql_con, 0, "STOP SLAVE SQL_THREAD")) + return (1); + + return (0); +} + +int MySQLContext::add_stop_slave(void) { + if (opt_comments) + fprintf(md_result_file, + "\n--\n-- stop slave statement to make a recovery dump)\n--\n\n"); + fprintf(md_result_file, "STOP SLAVE;\n"); + return (0); +} + +int MySQLContext::add_slave_statements(void) { + if (opt_comments) + fprintf(md_result_file, + "\n--\n-- start slave statement to make a recovery dump)\n--\n\n"); + fprintf(md_result_file, "START SLAVE;\n"); + return (0); +} + +int MySQLContext::do_show_slave_status(MYSQL *mysql_con) { + MYSQL_RES *slave = NULL; + const char *comment_prefix = + (opt_slave_data == MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL) ? "-- " : ""; + if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS")) { + if (!opt_force) { + /* SHOW SLAVE STATUS reports nothing and --force is not enabled */ + my_printf_error(0, "Error: Slave not set up", MYF(0)); + } + mysql_free_result(slave); + return 1; + } else { + const int n_master_host = 1; + const int n_master_port = 3; + const int n_master_log_file = 9; + const int n_master_log_pos = 21; + const int n_channel_name = 55; + MYSQL_ROW row = mysql_fetch_row(slave); + /* Since 5.7 is is possible that SSS returns multiple channels */ + while (row) { + if (row[n_master_log_file] && row[n_master_log_pos]) { + /* SHOW MASTER STATUS reports file and position */ + if (opt_comments) + fprintf(md_result_file, + "\n--\n-- Position to start replication or point-in-time " + "recovery from (the master of this slave)\n--\n\n"); + + fprintf(md_result_file, "%sCHANGE MASTER TO ", comment_prefix); + + if (opt_include_master_host_port) { + if (row[n_master_host]) + fprintf(md_result_file, "MASTER_HOST='%s', ", row[n_master_host]); + if (row[n_master_port]) + fprintf(md_result_file, "MASTER_PORT=%s, ", row[n_master_port]); + } + fprintf(md_result_file, "MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s", + row[n_master_log_file], row[n_master_log_pos]); + + /* Only print the FOR CHANNEL if there is more than one channel */ + if (slave->row_count > 1) + fprintf(md_result_file, " FOR CHANNEL '%s'", row[n_channel_name]); + + fprintf(md_result_file, ";\n"); + } + row = mysql_fetch_row(slave); + } + check_io(md_result_file); + mysql_free_result(slave); + } + return 0; +} + +int MySQLContext::do_start_slave_sql(MYSQL *mysql_con) { + MYSQL_RES *slave; + /* We need to check if the slave sql is stopped in the first place */ + if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS")) + return (1); + else { + MYSQL_ROW row = mysql_fetch_row(slave); + if (row && row[11]) { + /* if SLAVE SQL is not running, we don't start it */ + if (!strcmp(row[11], "Yes")) { + mysql_free_result(slave); + /* Silently assume that they don't have the slave running */ + return (0); + } + } + } + mysql_free_result(slave); + + /* now, start slave if stopped */ + if (mysql_query_with_error_report(mysql_con, 0, "START SLAVE")) { + my_printf_error(0, "Error: Unable to start slave", MYF(0)); + return 1; + } + return (0); +} + +int MySQLContext::do_flush_tables_read_lock(MYSQL *mysql_con) { + /* + We do first a FLUSH TABLES. If a long update is running, the FLUSH TABLES + will wait but will not stall the whole mysqld, and when the long update is + done the FLUSH TABLES WITH READ LOCK will start and succeed quickly. So, + FLUSH TABLES is to lower the probability of a stage where both mysqldump + and most client connections are stalled. Of course, if a second long + update starts between the two FLUSHes, we have that bad stall. + */ + return (mysql_query_with_error_report( + mysql_con, 0, + ((opt_master_data != 0) ? "FLUSH /*!40101 LOCAL */ TABLES" + : "FLUSH TABLES")) || + mysql_query_with_error_report(mysql_con, 0, + "FLUSH TABLES WITH READ LOCK")); +} + +int MySQLContext::do_unlock_tables(MYSQL *mysql_con) { + return mysql_query_with_error_report(mysql_con, 0, "UNLOCK TABLES"); +} + +int MySQLContext::get_bin_log_name(MYSQL *mysql_con, char *buff_log_name, + uint buff_len) { + MYSQL_RES *res; + MYSQL_ROW row; + + if (mysql_query(mysql_con, "SHOW MASTER STATUS") || + !(res = mysql_store_result(mysql))) + return 1; + + if (!(row = mysql_fetch_row(res))) { + mysql_free_result(res); + return 1; + } + /* + Only one row is returned, and the first column is the name of the + active log. + */ + strmake(buff_log_name, row[0], buff_len - 1); + + mysql_free_result(res); + return 0; +} + +int MySQLContext::purge_bin_logs_to(MYSQL *mysql_con, char *log_name) { + DYNAMIC_STRING str; + int err; + init_dynamic_string_checked(&str, "PURGE BINARY LOGS TO '", 1024, 1024); + dynstr_append_checked(&str, log_name); + dynstr_append_checked(&str, "'"); + err = mysql_query_with_error_report(mysql_con, 0, str.str); + dynstr_free(&str); + return err; +} + +int MySQLContext::start_transaction(MYSQL *mysql_con) { + verbose_msg("-- Starting transaction...\n"); + /* + We use BEGIN for old servers. --single-transaction --master-data will fail + on old servers, but that's ok as it was already silently broken (it didn't + do a consistent read, so better tell people frankly, with the error). + + We want the first consistent read to be used for all tables to dump so we + need the REPEATABLE READ level (not anything lower, for example READ + COMMITTED would give one new consistent read per dumped table). + */ + if ((mysql_get_server_version(mysql_con) < 40100) && opt_master_data) { + fprintf(stderr, + "-- %s: the combination of --single-transaction and " + "--master-data requires a MySQL server version of at least 4.1 " + "(current server's version is %s). %s\n", + opt_force ? "Warning" : "Error", + mysql_con->server_version ? mysql_con->server_version : "unknown", + opt_force ? "Continuing due to --force, backup may not be " + "consistent across all tables!" + : "Aborting."); + if (!opt_force) exit(EX_MYSQLERR); + } + + return ( + mysql_query_with_error_report(mysql_con, 0, + "SET SESSION TRANSACTION ISOLATION " + "LEVEL REPEATABLE READ") || + mysql_query_with_error_report(mysql_con, 0, + "START TRANSACTION " + "/*!40100 WITH CONSISTENT SNAPSHOT */")); +} + +ulong MySQLContext::find_set(TYPELIB *lib, const char *x, size_t length, + char **err_pos, uint *err_len) { + const char *end = x + length; + ulong found = 0; + uint find; + char buff[255]; + + *err_pos = 0; /* No error yet */ + while (end > x && my_isspace(charset_info, end[-1])) end--; + + *err_len = 0; + if (x != end) { + const char *start = x; + for (;;) { + const char *pos = start; + uint var_len; + + for (; pos != end && *pos != ','; pos++) + ; + var_len = (uint)(pos - start); + strmake(buff, start, MY_MIN(sizeof(buff) - 1, var_len)); + find = find_type(buff, lib, FIND_TYPE_BASIC); + if (!find) { + *err_pos = (char *)start; + *err_len = var_len; + } else + found |= ((longlong)1 << (find - 1)); + if (pos == end) break; + start = pos + 1; + } + } + return found; +} + +/* Print a value with a prefix on file */ +void MySQLContext::print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row, + const char *prefix, const char *name, + int string_value) { + MYSQL_FIELD *field; + mysql_field_seek(result, 0); + + for (; (field = mysql_fetch_field(result)); row++) { + if (!strcmp(field->name, name)) { + if (row[0] && row[0][0] && strcmp(row[0], "0")) /* Skip default */ + { + fputc(' ', file); + fputs(prefix, file); + if (string_value) + unescape(file, row[0], strlen(row[0]), NULL); + else + fputs(row[0], file); + check_io(file); + return; + } + } + } + return; /* This shouldn't happen */ +} /* print_value */ + +/* + SYNOPSIS + + Check if the table is one of the table types that should be ignored: + MRG_ISAM, MRG_MYISAM. + + If the table should be altogether ignored, it returns a TRUE, FALSE if it + should not be ignored. + + ARGS + + check_if_ignore_table() + table_name Table name to check + table_type Type of table + + GLOBAL VARIABLES + mysql MySQL connection + verbose Write warning messages + + RETURN + char (bit value) See IGNORE_ values at top +*/ + +char MySQLContext::check_if_ignore_table(const char *table_name, + char *table_type) { + char result = IGNORE_NONE; + char buff[FN_REFLEN + 80], show_name_buff[FN_REFLEN]; + MYSQL_RES *res = NULL; + MYSQL_ROW row; + DBUG_ENTER("check_if_ignore_table"); + + /* Check memory for quote_for_like() */ + DBUG_ASSERT(2 * sizeof(table_name) < sizeof(show_name_buff)); + my_snprintf(buff, sizeof(buff), "show table status like %s", + quote_for_like(table_name, show_name_buff)); + if (mysql_query_with_error_report(mysql, &res, buff)) { + if (mysql_errno(mysql) != ER_PARSE_ERROR) { /* If old MySQL version */ + verbose_msg( + "-- Warning: Couldn't get status information for " + "table %s (%s)\n", + table_name, mysql_error(mysql)); + DBUG_RETURN(result); /* assume table is ok */ + } + } + if (!(row = mysql_fetch_row(res))) { + fprintf(stderr, + "Error: Couldn't read status information for table %s (%s)\n", + table_name, mysql_error(mysql)); + mysql_free_result(res); + DBUG_RETURN(result); /* assume table is ok */ + } + if (!(row[1])) + strmake(table_type, "VIEW", NAME_LEN - 1); + else { + strmake(table_type, row[1], NAME_LEN - 1); + + /* If these two types, we want to skip dumping the table. */ + if (!opt_no_data && + (!my_strcasecmp(&my_charset_latin1, table_type, "MRG_MyISAM") || + !strcmp(table_type, "MRG_ISAM") || !strcmp(table_type, "FEDERATED"))) + result = IGNORE_DATA; + } + mysql_free_result(res); + DBUG_RETURN(result); +} + +/* + Get string of comma-separated primary key field names + + SYNOPSIS + char *primary_key_fields(const char *table_name) + RETURNS pointer to allocated buffer (must be freed by caller) + table_name quoted table name + + DESCRIPTION + Use SHOW KEYS FROM table_name, allocate a buffer to hold the + field names, and then build that string and return the pointer + to that buffer. + + Returns NULL if there is no PRIMARY or UNIQUE key on the table, + or if there is some failure. It is better to continue to dump + the table unsorted, rather than exit without dumping the data. +*/ + +char *MySQLContext::primary_key_fields(const char *table_name) { + MYSQL_RES *res = NULL; + MYSQL_ROW row; + /* SHOW KEYS FROM + table name * 2 (escaped) + 2 quotes + \0 */ + char show_keys_buff[15 + NAME_LEN * 2 + 3]; + size_t result_length = 0; + char *result = 0; + char buff[NAME_LEN * 2 + 3]; + char *quoted_field; + + my_snprintf(show_keys_buff, sizeof(show_keys_buff), "SHOW KEYS FROM %s", + table_name); + if (mysql_query(mysql, show_keys_buff) || + !(res = mysql_store_result(mysql))) { + fprintf(stderr, + "Warning: Couldn't read keys from table %s;" + " records are NOT sorted (%s)\n", + table_name, mysql_error(mysql)); + /* Don't exit, because it's better to print out unsorted records */ + goto cleanup; + } + + /* + * Figure out the length of the ORDER BY clause result. + * Note that SHOW KEYS is ordered: a PRIMARY key is always the first + * row, and UNIQUE keys come before others. So we only need to check + * the first key, not all keys. + */ + if ((row = mysql_fetch_row(res)) && atoi(row[1]) == 0) { + /* Key is unique */ + do { + quoted_field = quote_name(row[4], buff, 0); + result_length += strlen(quoted_field) + 1; /* + 1 for ',' or \0 */ + } while ((row = mysql_fetch_row(res)) && atoi(row[3]) > 1); + } + + /* Build the ORDER BY clause result */ + if (result_length) { + char *end; + /* result (terminating \0 is already in result_length) */ + result = (char *)my_malloc(PSI_NOT_INSTRUMENTED, result_length + 10, + MYF(MY_WME)); + if (!result) { + fprintf(stderr, "Error: Not enough memory to store ORDER BY clause\n"); + goto cleanup; + } + mysql_data_seek(res, 0); + row = mysql_fetch_row(res); + quoted_field = quote_name(row[4], buff, 0); + end = my_stpcpy(result, quoted_field); + while ((row = mysql_fetch_row(res)) && atoi(row[3]) > 1) { + quoted_field = quote_name(row[4], buff, 0); + end = strxmov(end, ",", quoted_field, NullS); + } + } + +cleanup: + if (res) mysql_free_result(res); + + return result; +} + +/* + Replace a substring + + SYNOPSIS + replace + ds_str The string to search and perform the replace in + search_str The string to search for + search_len Length of the string to search for + replace_str The string to replace with + replace_len Length of the string to replace with + + RETURN + 0 String replaced + 1 Could not find search_str in str +*/ + +static int replace(DYNAMIC_STRING *ds_str, const char *search_str, + size_t search_len, const char *replace_str, + size_t replace_len) { + DYNAMIC_STRING ds_tmp; + const char *start = strstr(ds_str->str, search_str); + if (!start) return 1; + init_dynamic_string_checked(&ds_tmp, "", ds_str->length + replace_len, 256); + dynstr_append_mem_checked(&ds_tmp, ds_str->str, start - ds_str->str); + dynstr_append_mem_checked(&ds_tmp, replace_str, replace_len); + dynstr_append_checked(&ds_tmp, start + search_len); + dynstr_set_checked(ds_str, ds_tmp.str); + dynstr_free(&ds_tmp); + return 0; +} + +/** + This function sets the session binlog in the dump file. + When --set-gtid-purged is used, this function is called to + disable the session binlog and at the end of the dump, to restore + the session binlog. + + @note: md_result_file should have been opened, before + this function is called. + + @param[in] flag If FALSE, disable binlog. + If TRUE and binlog disabled previously, + restore the session binlog. +*/ + +static void set_session_binlog(my_bool flag) { + static my_bool is_binlog_disabled = FALSE; + + if (!flag && !is_binlog_disabled) { + fprintf(md_result_file, + "SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;\n"); + fprintf(md_result_file, "SET @@SESSION.SQL_LOG_BIN= 0;\n"); + is_binlog_disabled = 1; + } else if (flag && is_binlog_disabled) { + fprintf(md_result_file, + "SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;\n"); + is_binlog_disabled = 0; + } +} + +/** + This function gets the GTID_EXECUTED sets from the + server and assigns those sets to GTID_PURGED in the + dump file. + + @param[in] mysql_con connection to the server + + @retval FALSE succesfully printed GTID_PURGED sets + in the dump file. + @retval TRUE failed. + +*/ + +static my_bool add_set_gtid_purged(MYSQL *mysql_con) { + MYSQL_RES *gtid_purged_res; + MYSQL_ROW gtid_set; + ulonglong num_sets, idx; + + /* query to get the GTID_EXECUTED */ + if (mysql_query_with_error_report(mysql_con, >id_purged_res, + "SELECT @@GLOBAL.GTID_EXECUTED")) + return TRUE; + + /* Proceed only if gtid_purged_res is non empty */ + if ((num_sets = mysql_num_rows(gtid_purged_res)) > 0) { + if (opt_comments) + fprintf(md_result_file, + "\n--\n-- GTID state at the beginning of the backup \n--\n\n"); + + fprintf(md_result_file, "SET @@GLOBAL.GTID_PURGED='"); + + /* formatting is not required, even for multiple gtid sets */ + for (idx = 0; idx < num_sets - 1; idx++) { + gtid_set = mysql_fetch_row(gtid_purged_res); + fprintf(md_result_file, "%s,", (char *)gtid_set[0]); + } + /* for the last set */ + gtid_set = mysql_fetch_row(gtid_purged_res); + /* close the SET expression */ + fprintf(md_result_file, "%s';\n", (char *)gtid_set[0]); + } + mysql_free_result(gtid_purged_res); + + return FALSE; /*success */ +} + +/** + This function processes the opt_set_gtid_purged option. + This function also calls set_session_binlog() function before + setting the SET @@GLOBAL.GTID_PURGED in the output. + + @param[in] mysql_con the connection to the server + + @retval FALSE successful according to the value + of opt_set_gtid_purged. + @retval TRUE fail. +*/ + +my_bool MySQLContext::process_set_gtid_purged(MYSQL *mysql_con) { + MYSQL_RES *gtid_mode_res; + MYSQL_ROW gtid_mode_row; + char *gtid_mode_val = 0; + char buf[32], query[64]; + + if (opt_set_gtid_purged_mode == SET_GTID_PURGED_OFF) + return FALSE; /* nothing to be done */ + + /* + Check if the server has the knowledge of GTIDs(pre mysql-5.6) + or if the gtid_mode is ON or OFF. + */ + my_snprintf(query, sizeof(query), "SHOW VARIABLES LIKE %s", + quote_for_like("gtid_mode", buf)); + + if (mysql_query_with_error_report(mysql_con, >id_mode_res, query)) + return TRUE; + + gtid_mode_row = mysql_fetch_row(gtid_mode_res); + + /* + gtid_mode_row is NULL for pre 5.6 versions. For versions >= 5.6, + get the gtid_mode value from the second column. + */ + gtid_mode_val = gtid_mode_row ? (char *)gtid_mode_row[1] : NULL; + + if (gtid_mode_val && strcmp(gtid_mode_val, "OFF")) { + /* + For any gtid_mode !=OFF and irrespective of --set-gtid-purged + being AUTO or ON, add GTID_PURGED in the output. + */ + if (opt_databases || !opt_alldbs || !opt_dump_triggers || !opt_routines || + !opt_events) { + fprintf(stderr, + "Warning: A partial dump from a server that has GTIDs will " + "by default include the GTIDs of all transactions, even " + "those that changed suppressed parts of the database. If " + "you don't want to restore GTIDs, pass " + "--set-gtid-purged=OFF. To make a complete dump, pass " + "--all-databases --triggers --routines --events. \n"); + } + + set_session_binlog(FALSE); + if (add_set_gtid_purged(mysql_con)) { + mysql_free_result(gtid_mode_res); + return TRUE; + } + } else /* gtid_mode is off */ + { + if (opt_set_gtid_purged_mode == SET_GTID_PURGED_ON) { + fprintf(stderr, "Error: Server has GTIDs disabled.\n"); + mysql_free_result(gtid_mode_res); + return TRUE; + } + } + + mysql_free_result(gtid_mode_res); + return FALSE; +} + +/* + Getting VIEW structure + + SYNOPSIS + get_view_structure() + table view name + db db name + + RETURN + 0 OK + 1 ERROR +*/ + +my_bool MySQLContext::get_view_structure(char *table, char *db) { + MYSQL_RES *table_res; + MYSQL_ROW row; + MYSQL_FIELD *field; + char *result_table; //, *opt_quoted_table; + char table_buff[NAME_LEN * 2 + 3]; + char table_buff2[NAME_LEN * 2 + 3]; + char query[QUERY_LENGTH]; + FILE *sql_file = md_result_file; + my_bool freemem = FALSE; + char const *text; + std::string createviewsql; + DBUG_ENTER("get_view_structure"); + + if (opt_no_create_info) /* Don't write table creation info */ + DBUG_RETURN(0); + + verbose_msg("-- Retrieving view structure for table %s...\n", table); + + result_table = quote_name(table, table_buff, 1); + // opt_quoted_table = quote_name(table, table_buff2, 0); + quote_name(table, table_buff2, 0); + + if (switch_character_set_results(mysql, "binary")) DBUG_RETURN(1); + + my_snprintf(query, sizeof(query), "SHOW CREATE TABLE %s", result_table); + + if (mysql_query_with_error_report(mysql, &table_res, query)) { + switch_character_set_results(mysql, default_charset); + DBUG_RETURN(0); + } + + /* Check if this is a view */ + field = mysql_fetch_field_direct(table_res, 0); + if (strcmp(field->name, "View") != 0) { + switch_character_set_results(mysql, default_charset); + verbose_msg("-- It's base table, skipped\n"); + mysql_free_result(table_res); + DBUG_RETURN(0); + } + + /* If requested, open separate .sql file for this view */ + if (path) { + if (!(sql_file = open_sql_file_for_table(table, O_WRONLY))) { + mysql_free_result(table_res); + DBUG_RETURN(1); + } + + write_header(sql_file, db); + } + + text = fix_identifier_with_newline(result_table, &freemem); + print_comment(sql_file, 0, + "\n--\n-- Final view structure for view %s\n--\n\n", text); + if (freemem) my_free((void *)text); + + verbose_msg("-- Dropping the temporary view structure created\n"); + // fprintf(sql_file, "/*!50001 DROP VIEW IF EXISTS %s*/;\n", + // opt_quoted_table); + + my_snprintf(query, sizeof(query), + "SELECT CHECK_OPTION, DEFINER, SECURITY_TYPE, " + " CHARACTER_SET_CLIENT, COLLATION_CONNECTION " + "FROM information_schema.views " + "WHERE table_name=\"%s\" AND table_schema=\"%s\"", + table, db); + + if (mysql_query(mysql, query)) { + /* + Use the raw output from SHOW CREATE TABLE if + information_schema query fails. + */ + row = mysql_fetch_row(table_res); + // fprintf(sql_file, "/*!50001 %s */;\n", row[1]); + createviewsql = row[1]; + // check_io(sql_file); + mysql_free_result(table_res); + } else { + char *ptr; + ulong *lengths; + char search_buf[256], replace_buf[256]; + ulong search_len, replace_len; + DYNAMIC_STRING ds_view; + + /* Save the result of SHOW CREATE TABLE in ds_view */ + row = mysql_fetch_row(table_res); + lengths = mysql_fetch_lengths(table_res); + init_dynamic_string_checked(&ds_view, row[1], lengths[1] + 1, 1024); + mysql_free_result(table_res); + + /* Get the result from "select ... information_schema" */ + if (!(table_res = mysql_store_result(mysql)) || + !(row = mysql_fetch_row(table_res))) { + if (table_res) mysql_free_result(table_res); + dynstr_free(&ds_view); + DB_error( + mysql, + "when trying to save the result of SHOW CREATE TABLE in ds_view."); + DBUG_RETURN(1); + } + + lengths = mysql_fetch_lengths(table_res); + + /* + "WITH %s CHECK OPTION" is available from 5.0.2 + Surround it with !50002 comments + */ + if (strcmp(row[0], "NONE")) { + ptr = search_buf; + search_len = + (ulong)(strxmov(ptr, "WITH ", row[0], " CHECK OPTION", NullS) - ptr); + ptr = replace_buf; + replace_len = (ulong)( + strxmov(ptr, "*/\n/*!50002 WITH ", row[0], " CHECK OPTION", NullS) - + ptr); + replace(&ds_view, search_buf, search_len, replace_buf, replace_len); + } + + /* + "DEFINER=%s SQL SECURITY %s" is available from 5.0.13 + Surround it with !50013 comments + */ + { + size_t user_name_len; + char user_name_str[USERNAME_LENGTH + 1]; + char quoted_user_name_str[USERNAME_LENGTH * 2 + 3]; + size_t host_name_len; + char host_name_str[HOSTNAME_LENGTH + 1]; + char quoted_host_name_str[HOSTNAME_LENGTH * 2 + 3]; + + parse_user(row[1], lengths[1], user_name_str, &user_name_len, + host_name_str, &host_name_len); + + ptr = search_buf; + search_len = (ulong)( + strxmov(ptr, "DEFINER=", + quote_name(user_name_str, quoted_user_name_str, FALSE), "@", + quote_name(host_name_str, quoted_host_name_str, FALSE), + " SQL SECURITY ", row[2], NullS) - + ptr); + ptr = replace_buf; + replace_len = (ulong)( + strxmov(ptr, "*/\n/*!50013 DEFINER=", + quote_name(user_name_str, quoted_user_name_str, FALSE), "@", + quote_name(host_name_str, quoted_host_name_str, FALSE), + " SQL SECURITY ", row[2], " */\n/*!50001", NullS) - + ptr); + replace(&ds_view, search_buf, search_len, replace_buf, replace_len); + } + + /* Dump view structure to file */ + + // fprintf(sql_file, + // "/*!50001 SET @saved_cs_client = @@character_set_client + // */;\n" + // "/*!50001 SET @saved_cs_results = @@character_set_results + // */;\n" + // "/*!50001 SET @saved_col_connection = @@collation_connection + // */;\n" + // "/*!50001 SET character_set_client = %s */;\n" + // "/*!50001 SET character_set_results = %s */;\n" + // "/*!50001 SET collation_connection = %s */;\n" + // "/*!50001 %s */;\n" + // "/*!50001 SET character_set_client = @saved_cs_client */;\n" + // "/*!50001 SET character_set_results = @saved_cs_results + // */;\n" + // "/*!50001 SET collation_connection = @saved_col_connection + // */;\n", (const char *) row[3], (const char *) row[3], (const char + // *) row[4], (const char *) ds_view.str); + + createviewsql = "CREATE "; + createviewsql += ds_view.str; + check_io(sql_file); + mysql_free_result(table_res); + dynstr_free(&ds_view); + } + + // execute sql + struct DMODBC::DBError dbError; + sg_procMetrics->startSql(table, "view", createviewsql.c_str()); + sg_procMetrics->addViewcount(1); + if (sg_procOdbc->executeCreateViewSql(createviewsql.c_str(), &dbError)) { + // successful + sg_procMetrics->endSql(table, "view", createviewsql.c_str(), false, "", false); + } else { + // error + sg_procMetrics->endSql(table, "view", createviewsql.c_str(), true, + dbError.error.c_str(), false); + } + + if (switch_character_set_results(mysql, default_charset)) DBUG_RETURN(1); + + /* If a separate .sql file was opened, close it now */ + if (sql_file != md_result_file) { + fputs("\n", sql_file); + write_footer(sql_file); + my_fclose(sql_file, MYF(MY_WME)); + } + DBUG_RETURN(0); +} + +/* + The following functions are wrappers for the dynamic string functions + and if they fail, the wrappers will terminate the current process. +*/ + +#define DYNAMIC_STR_ERROR_MSG "Couldn't perform DYNAMIC_STRING operation" + +static void init_dynamic_string_checked(DYNAMIC_STRING *str, + const char *init_str, size_t init_alloc, + size_t alloc_increment) { + if (init_dynamic_string(str, init_str, init_alloc, alloc_increment)) + die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG); +} + +static void dynstr_append_checked(DYNAMIC_STRING *dest, const char *src) { + if (dynstr_append(dest, src)) die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG); +} + +static void dynstr_set_checked(DYNAMIC_STRING *str, const char *init_str) { + if (dynstr_set(str, init_str)) die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG); +} + +static void dynstr_append_mem_checked(DYNAMIC_STRING *str, const char *append, + size_t length) { + if (dynstr_append_mem(str, append, length)) + die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG); +} + +static void dynstr_realloc_checked(DYNAMIC_STRING *str, + size_t additional_size) { + if (dynstr_realloc(str, additional_size)) + die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG); +} + +int main(int argc, char **argv) { + MySQLContext mySQLContext; + return mySQLContext.mainproc(argc, argv); +} + +int MySQLContext::mainproc(int argc, char **argv) { + sg_procOdbc = new DMODBC::ProcOdbc("opengauss"); + if (!sg_procOdbc->connect()) { + return 1; + } + + if (pthread_key_create(&sg_connectionkey, 0) != 0) { + printf("cannot create connection thread local key"); + return 1; + } + + sg_mysqlInfo = DMODBC::ProcOdbc::getMysqlInfo(); + printf("mysql addr: %s:%d\n", sg_mysqlInfo.host.c_str(), sg_mysqlInfo.port); + printf("mysql username: %s\n", sg_mysqlInfo.username.c_str()); + printf("mysql database: %s\n", sg_mysqlInfo.database.c_str()); + sg_procOdbc->setLoglevel(sg_mysqlInfo.loglevel); + sg_firstdumptables = true; + + char srcaddr[256]; + char dstaddr[256]; + const char *metricsDbtype = getenv("metrics_dbtype"); + if (metricsDbtype == NULL) { + metricsDbtype = "mysql"; + } + char metricds[256]; + sprintf(metricds, "metrics_%s", metricsDbtype); + sprintf(srcaddr, "%s:%d", sg_mysqlInfo.host.c_str(), sg_mysqlInfo.port); + sprintf(dstaddr, "%s:%d", sg_mysqlInfo.dsthost.c_str(), sg_mysqlInfo.dstport); + sg_procMetrics = new DMODBC::ProcMetrics( + metricds, srcaddr, dstaddr, sg_mysqlInfo.database.c_str(), + sg_mysqlInfo.dstdatabase.c_str(), 1000000, metricsDbtype); + + char bin_log_name[FN_REFLEN]; + // int exit_code; + int md_result_fd; + MY_INIT("mysqldump"); + + compatible_mode_normal_str[0] = 0; + // default_charset = (char *)mysql_universal_client_charset; + default_charset = "utf8"; + memset(&ignore_table, 0, sizeof(ignore_table)); + + // exit_code= get_options(&argc, &argv); + // if (exit_code) + // { + // free_resources(); + // exit(exit_code); + // } + argc = sg_procOdbc->getCurrentArgc(); + argv = sg_procOdbc->getCurrentArgv(); + get_options(&argc, &argv); + md_result_file = stdout; + current_host = (char *)sg_mysqlInfo.host.c_str(); + current_user = (char *)sg_mysqlInfo.username.c_str(); + opt_password = my_strdup(PSI_NOT_INSTRUMENTED, sg_mysqlInfo.password.c_str(), + MYF(MY_WME)); + opt_mysql_port = sg_mysqlInfo.port; + opt_alldbs = 0; + // default_charset = "utf8"; + // charset_info = get_charset_by_csname(default_charset, + // MY_CS_PRIMARY, MYF(MY_WME)); + // opt_compatible_mode = 2; + // sprintf(compatible_mode_normal_str, "POSTGRESQL"); + + /* + Disable comments in xml mode if 'comments' option is not explicitly used. + */ + if (opt_xml && !opt_comments_used) opt_comments = 0; + + if (log_error_file) { + if (!(stderror_file = freopen(log_error_file, "a+", stderr))) { + free_resources(); + exit(EX_MYSQLERR); + } + } + + if (connect_to_db(current_host, current_user, opt_password)) { + free_resources(); + exit(EX_MYSQLERR); + } + if (!path) write_header(md_result_file, *argv); + + if (opt_slave_data && do_stop_slave_sql(mysql)) goto err; + + if ((opt_lock_all_tables || opt_master_data || + (opt_single_transaction && flush_logs)) && + do_flush_tables_read_lock(mysql)) + goto err; + + /* + Flush logs before starting transaction since + this causes implicit commit starting mysql-5.5. + */ + if (opt_lock_all_tables || opt_master_data || + (opt_single_transaction && flush_logs) || opt_delete_master_logs) { + if (flush_logs || opt_delete_master_logs) { + if (mysql_refresh(mysql, REFRESH_LOG)) { + DB_error(mysql, "when doing refresh"); + goto err; + } + verbose_msg("-- main : logs flushed successfully!\n"); + } + + /* Not anymore! That would not be sensible. */ + flush_logs = 0; + } + + if (opt_delete_master_logs) { + if (get_bin_log_name(mysql, bin_log_name, sizeof(bin_log_name))) goto err; + } + + if (opt_single_transaction && start_transaction(mysql)) goto err; + + /* Add 'STOP SLAVE to beginning of dump */ + if (opt_slave_apply && add_stop_slave()) goto err; + + /* Process opt_set_gtid_purged and add SET @@GLOBAL.GTID_PURGED if required. + */ + if (process_set_gtid_purged(mysql)) goto err; + + if (opt_master_data && do_show_master_status(mysql)) goto err; + if (opt_slave_data && do_show_slave_status(mysql)) goto err; + if (opt_single_transaction && + do_unlock_tables(mysql)) /* unlock but no commit! */ + goto err; + + // if (opt_alltspcs) + // dump_all_tablespaces(); + + // 璁剧疆60鍒嗛挓瓒呮椂 + mysql_query_with_error_report(mysql, 0, "set net_read_timeout = 3600"); + mysql_query_with_error_report(mysql, 0, "set net_write_timeout = 3600"); + + sg_procMetrics->startDB(); + dump_all_tables_in_db((char *)sg_mysqlInfo.database.c_str()); + dump_all_views_in_db((char *)sg_mysqlInfo.database.c_str()); + dump_routines_for_db((char *)sg_mysqlInfo.database.c_str()); + sg_procMetrics->endDB(); + + // if (opt_alldbs) + // { + // if (!opt_alltspcs && !opt_notspcs) + // dump_all_tablespaces(); + // dump_all_databases(); + // } + // else + // { + // // Check all arguments meet length condition. Currently database and + // table + // // names are limited to NAME_LEN bytes and stack-based buffers assumes + // // that escaped name will be not longer than NAME_LEN*2 + 2 bytes long. + // int argument; + // for (argument= 0; argument < argc; argument++) + // { + // size_t argument_length= strlen(argv[argument]); + // if (argument_length > NAME_LEN) + // { + // die(EX_CONSCHECK, "[ERROR] Argument '%s' is too long, it cannot be " + // "name for any table or database.\n", argv[argument]); + // } + // } + + // if (argc > 1 && !opt_databases) + // { + // /* Only one database and selected table(s) */ + // if (!opt_alltspcs && !opt_notspcs) + // dump_tablespaces_for_tables(*argv, (argv + 1), (argc - 1)); + // dump_selected_tables(*argv, (argv + 1), (argc - 1)); + // } + // else + // { + // /* One or more databases, all tables */ + // if (!opt_alltspcs && !opt_notspcs) + // dump_tablespaces_for_databases(argv); + // dump_databases(argv); + // } + // } + + /* if --dump-slave , start the slave sql thread */ + if (opt_slave_data && do_start_slave_sql(mysql)) goto err; + + /* + if --set-gtid-purged, restore binlog at the end of the session + if required. + */ + set_session_binlog(TRUE); + + /* add 'START SLAVE' to end of dump */ + if (opt_slave_apply && add_slave_statements()) goto err; + + if (md_result_file) md_result_fd = my_fileno(md_result_file); + + /* + Ensure dumped data flushed. + First we will flush the file stream data to kernel buffers with fflush(). + Second we will flush the kernel buffers data to physical disk file with + my_sync(), this will make sure the data succeessfully dumped to disk file. + fsync() fails with EINVAL if stdout is not redirected to any file, hence + MY_IGNORE_BADFD is passed to ingnore that error. + */ + if (md_result_file && + (fflush(md_result_file) || my_sync(md_result_fd, MYF(MY_IGNORE_BADFD)))) { + if (!first_error) first_error = EX_MYSQLERR; + goto err; + } + /* everything successful, purge the old logs files */ + if (opt_delete_master_logs && purge_bin_logs_to(mysql, bin_log_name)) + goto err; + +#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY) + my_free(shared_memory_base_name); +#endif + /* + No reason to explicitely COMMIT the transaction, neither to explicitely + UNLOCK TABLES: these will be automatically be done by the server when we + disconnect now. Saves some code here, some network trips, adds nothing to + server. + */ +err: + dbDisconnect(current_host); + if (!path) write_footer(md_result_file); + free_resources(); + + if (stderror_file) fclose(stderror_file); + + sg_procOdbc->disConnect(); + delete sg_procOdbc; + delete sg_procMetrics; + + return (first_error); +} /* main */