diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 11:36:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 11:36:04 +0000 |
commit | 040eee1aa49b49df4698d83a05af57c220127fd1 (patch) | |
tree | f635435954e6ccde5eee9893889e24f30ca68346 /src/lib/mysql | |
parent | Initial commit. (diff) | |
download | isc-kea-upstream.tar.xz isc-kea-upstream.zip |
Adding upstream version 2.2.0.upstream/2.2.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/mysql')
-rw-r--r-- | src/lib/mysql/Makefile.am | 32 | ||||
-rw-r--r-- | src/lib/mysql/Makefile.in | 936 | ||||
-rw-r--r-- | src/lib/mysql/mysql_binding.cc | 349 | ||||
-rw-r--r-- | src/lib/mysql/mysql_binding.h | 671 | ||||
-rw-r--r-- | src/lib/mysql/mysql_connection.cc | 521 | ||||
-rw-r--r-- | src/lib/mysql/mysql_connection.h | 760 | ||||
-rw-r--r-- | src/lib/mysql/mysql_constants.h | 64 | ||||
-rw-r--r-- | src/lib/mysql/tests/Makefile.am | 40 | ||||
-rw-r--r-- | src/lib/mysql/tests/Makefile.in | 1006 | ||||
-rw-r--r-- | src/lib/mysql/tests/mysql_binding_unittest.cc | 249 | ||||
-rw-r--r-- | src/lib/mysql/tests/mysql_connection_unittest.cc | 789 | ||||
-rw-r--r-- | src/lib/mysql/tests/run_unittests.cc | 20 | ||||
-rw-r--r-- | src/lib/mysql/testutils/Makefile.am | 23 | ||||
-rw-r--r-- | src/lib/mysql/testutils/Makefile.in | 837 | ||||
-rw-r--r-- | src/lib/mysql/testutils/mysql_schema.cc | 163 | ||||
-rw-r--r-- | src/lib/mysql/testutils/mysql_schema.h | 123 |
16 files changed, 6583 insertions, 0 deletions
diff --git a/src/lib/mysql/Makefile.am b/src/lib/mysql/Makefile.am new file mode 100644 index 0000000..5ea9782 --- /dev/null +++ b/src/lib/mysql/Makefile.am @@ -0,0 +1,32 @@ +SUBDIRS = . testutils tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) $(MYSQL_CPPFLAGS) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +CLEANFILES = *.gcno *.gcda + +lib_LTLIBRARIES = libkea-mysql.la +libkea_mysql_la_SOURCES = mysql_connection.cc mysql_connection.h +libkea_mysql_la_SOURCES += mysql_binding.cc mysql_binding.h +libkea_mysql_la_SOURCES += mysql_constants.h + +libkea_mysql_la_LIBADD = $(top_builddir)/src/lib/database/libkea-database.la +libkea_mysql_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libkea_mysql_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libkea_mysql_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libkea_mysql_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libkea_mysql_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libkea_mysql_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) + +libkea_mysql_la_LDFLAGS = -no-undefined -version-info 38:0:0 + +libkea_mysql_la_LDFLAGS += $(MYSQL_LIBS) + +# Specify the headers for copying into the installation directory tree. +libkea_mysql_includedir = $(pkgincludedir)/mysql +libkea_mysql_include_HEADERS = \ + mysql_binding.h \ + mysql_connection.h \ + mysql_constants.h diff --git a/src/lib/mysql/Makefile.in b/src/lib/mysql/Makefile.in new file mode 100644 index 0000000..e308b36 --- /dev/null +++ b/src/lib/mysql/Makefile.in @@ -0,0 +1,936 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/lib/mysql +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_sysrepo.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(libkea_mysql_include_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(libkea_mysql_includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libkea_mysql_la_DEPENDENCIES = \ + $(top_builddir)/src/lib/database/libkea-database.la \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/log/libkea-log.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libkea_mysql_la_OBJECTS = mysql_connection.lo mysql_binding.lo +libkea_mysql_la_OBJECTS = $(am_libkea_mysql_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libkea_mysql_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libkea_mysql_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/mysql_binding.Plo \ + ./$(DEPDIR)/mysql_connection.Plo +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libkea_mysql_la_SOURCES) +DIST_SOURCES = $(libkea_mysql_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(libkea_mysql_include_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_SYSREPO = @HAVE_SYSREPO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . testutils tests +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib \ + $(BOOST_INCLUDES) $(MYSQL_CPPFLAGS) +AM_CXXFLAGS = $(KEA_CXXFLAGS) +CLEANFILES = *.gcno *.gcda +lib_LTLIBRARIES = libkea-mysql.la +libkea_mysql_la_SOURCES = mysql_connection.cc mysql_connection.h \ + mysql_binding.cc mysql_binding.h mysql_constants.h +libkea_mysql_la_LIBADD = \ + $(top_builddir)/src/lib/database/libkea-database.la \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/log/libkea-log.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(LOG4CPLUS_LIBS) $(BOOST_LIBS) +libkea_mysql_la_LDFLAGS = -no-undefined -version-info 38:0:0 \ + $(MYSQL_LIBS) + +# Specify the headers for copying into the installation directory tree. +libkea_mysql_includedir = $(pkgincludedir)/mysql +libkea_mysql_include_HEADERS = \ + mysql_binding.h \ + mysql_connection.h \ + mysql_constants.h + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/mysql/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/mysql/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libkea-mysql.la: $(libkea_mysql_la_OBJECTS) $(libkea_mysql_la_DEPENDENCIES) $(EXTRA_libkea_mysql_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libkea_mysql_la_LINK) -rpath $(libdir) $(libkea_mysql_la_OBJECTS) $(libkea_mysql_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mysql_binding.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mysql_connection.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-libkea_mysql_includeHEADERS: $(libkea_mysql_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(libkea_mysql_include_HEADERS)'; test -n "$(libkea_mysql_includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libkea_mysql_includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libkea_mysql_includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libkea_mysql_includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libkea_mysql_includedir)" || exit $$?; \ + done + +uninstall-libkea_mysql_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libkea_mysql_include_HEADERS)'; test -n "$(libkea_mysql_includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libkea_mysql_includedir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libkea_mysql_includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/mysql_binding.Plo + -rm -f ./$(DEPDIR)/mysql_connection.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-libkea_mysql_includeHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/mysql_binding.Plo + -rm -f ./$(DEPDIR)/mysql_connection.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES \ + uninstall-libkea_mysql_includeHEADERS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES \ + install-libkea_mysql_includeHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-libLTLIBRARIES uninstall-libkea_mysql_includeHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib/mysql/mysql_binding.cc b/src/lib/mysql/mysql_binding.cc new file mode 100644 index 0000000..67ad95d --- /dev/null +++ b/src/lib/mysql/mysql_binding.cc @@ -0,0 +1,349 @@ +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <asiolink/io_address.h> +#include <exceptions/exceptions.h> +#include <boost/date_time/gregorian/gregorian.hpp> +#include <mysql/mysql_binding.h> + +using namespace boost::posix_time; +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::util; + +namespace isc { +namespace db { + +std::string +MySqlBinding::getString() const { + // Make sure the binding type is text. + validateAccess<std::string>(); + if (length_ == 0) { + return (std::string()); + } + return (std::string(buffer_.begin(), buffer_.begin() + length_)); +} + +std::string +MySqlBinding::getStringOrDefault(const std::string& default_value) const { + if (amNull()) { + return (default_value); + } + return (getString()); +} + +ElementPtr +MySqlBinding::getJSON() const { + if (amNull()) { + return (ElementPtr()); + } + std::string s = getString(); + return (Element::fromJSON(s)); +} + +std::vector<uint8_t> +MySqlBinding::getBlob() const { + // Make sure the binding type is blob. + validateAccess<std::vector<uint8_t> >(); + if (length_ == 0) { + return (std::vector<uint8_t>()); + } + return (std::vector<uint8_t>(buffer_.begin(), buffer_.begin() + length_)); +} + +std::vector<uint8_t> +MySqlBinding::getBlobOrDefault(const std::vector<uint8_t>& default_value) const { + if (amNull()) { + return (default_value); + } + return (getBlob()); +} + +float +MySqlBinding::getFloat() const { + // It may seem a bit weird that we use getInteger template method + // for getting a floating point value. However, the getInteger method + // seems to be generic enough to support it. If we were to redo the + // API of this class we would probably introduce a getNumericValue + // method instead of getInteger. However, we already have getInteger + // used in many places so we should stick to it. + return (getInteger<float>()); +} + +ptime +MySqlBinding::getTimestamp() const { + // Make sure the binding type is timestamp. + validateAccess<ptime>(); + // Copy the buffer contents into native timestamp structure and + // then convert it to posix time. + const MYSQL_TIME* database_time = reinterpret_cast<const MYSQL_TIME*>(&buffer_[0]); + return (convertFromDatabaseTime(*database_time)); +} + +ptime +MySqlBinding::getTimestampOrDefault(const ptime& default_value) const { + if (amNull()) { + return (default_value); + } + return (getTimestamp()); +} + +MySqlBindingPtr +MySqlBinding::createString(const unsigned long length) { + MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<std::string>::column_type, + length)); + return (binding); +} + +MySqlBindingPtr +MySqlBinding::createString(const std::string& value) { + MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<std::string>::column_type, + value.size())); + binding->setBufferValue(value.begin(), value.end()); + return (binding); +} + +MySqlBindingPtr +MySqlBinding::condCreateString(const Optional<std::string>& value) { + return (value.unspecified() ? MySqlBinding::createNull() : createString(value)); +} + +MySqlBindingPtr +MySqlBinding::createBlob(const unsigned long length) { + MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<std::vector<uint8_t> >::column_type, + length)); + return (binding); +} + +MySqlBindingPtr +MySqlBinding::createFloat(const float value) { + // It may seem a bit weird that we use createInteger template method + // for setting a floating point value. However, the setInteger method + // seems to be generic enough to support it. If we were to redo the + // API of this class we would probably introduce a createNumericValue + // method instead of createInteger. However, we already have createInteger + // used in many places so we should stick to it. + return (createInteger<float>(value)); +} + +MySqlBindingPtr +MySqlBinding::createBool() { + return (createInteger<uint8_t>(static_cast<uint8_t>(false))); +} + +MySqlBindingPtr +MySqlBinding::createBool(const bool value) { + return (createInteger<uint8_t>(static_cast<uint8_t>(value))); +} + +MySqlBindingPtr +MySqlBinding::condCreateBool(const util::Optional<bool>& value) { + if (value.unspecified()) { + return (MySqlBinding::createNull()); + } + + return (createInteger<uint8_t>(static_cast<uint8_t>(value.get()))); +} + +MySqlBindingPtr +MySqlBinding::condCreateIPv4Address(const Optional<IOAddress>& value) { + // If the value is unspecified it doesn't matter what the value is. + if (value.unspecified()) { + return (MySqlBinding::createNull()); + } + + // Make sure it is an IPv4 address. + if (!value.get().isV4()) { + isc_throw(BadValue, "unable to create a MySQL binding: specified value '" + << value.get().toText() << "' is not an IPv4 address"); + } + + return (createInteger<uint32_t>(value.get().toUint32())); +} + +MySqlBindingPtr +MySqlBinding::createTimestamp(const boost::posix_time::ptime& timestamp) { + MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<ptime>::column_type, + MySqlBindingTraits<ptime>::length)); + binding->setTimestampValue(timestamp); + return (binding); +} + +MySqlBindingPtr +MySqlBinding::createTimestamp() { + MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<ptime>::column_type, + MySqlBindingTraits<ptime>::length)); + return (binding); +} + +MySqlBindingPtr +MySqlBinding::createNull() { + MySqlBindingPtr binding(new MySqlBinding(MYSQL_TYPE_NULL, 0)); + return (binding); +} + +void +MySqlBinding::convertToDatabaseTime(const time_t input_time, + MYSQL_TIME& output_time) { + + // Clear output data. + memset(&output_time, 0, sizeof(MYSQL_TIME)); + + // Convert to broken-out time + struct tm time_tm; + (void) localtime_r(&input_time, &time_tm); + + // Place in output expire structure. + output_time.year = time_tm.tm_year + 1900; + output_time.month = time_tm.tm_mon + 1; // Note different base + output_time.day = time_tm.tm_mday; + output_time.hour = time_tm.tm_hour; + output_time.minute = time_tm.tm_min; + output_time.second = time_tm.tm_sec; + output_time.second_part = 0; // No fractional seconds + output_time.neg = my_bool(0); // Not negative +} + +void +MySqlBinding::convertToDatabaseTime(const boost::posix_time::ptime& input_time, + MYSQL_TIME& output_time) { + if (input_time.is_not_a_date_time()) { + isc_throw(BadValue, "Time value is not a valid posix time"); + } + + // Clear output data. + memset(&output_time, 0, sizeof(MYSQL_TIME)); + + output_time.year = input_time.date().year(); + output_time.month = input_time.date().month(); + output_time.day = input_time.date().day(); + output_time.hour = input_time.time_of_day().hours(); + output_time.minute = input_time.time_of_day().minutes(); + output_time.second = input_time.time_of_day().seconds(); + /// @todo Use fractional seconds instead of 0 when minimum supported + /// MySQL version has it. + output_time.second_part = 0; +/* output_time.second_part = input_time.time_of_day().fractional_seconds() + *1000000/time_duration::ticks_per_second(); */ + output_time.neg = my_bool(0); +} + +void +MySqlBinding::convertToDatabaseTime(const time_t cltt, + const uint32_t valid_lifetime, + MYSQL_TIME& expire) { + + // Calculate expiry time. Store it in the 64-bit value so as we can detect + // overflows. + int64_t expire_time_64 = static_cast<int64_t>(cltt) + + static_cast<int64_t>(valid_lifetime); + + // Even on 64-bit systems MySQL doesn't seem to accept the timestamps + // beyond the max value of int32_t. + if (expire_time_64 > DatabaseConnection::MAX_DB_TIME) { + isc_throw(BadValue, "Time value is too large: " << expire_time_64); + } + + // Clear output data. + memset(&expire, 0, sizeof(MYSQL_TIME)); + + const time_t expire_time = static_cast<time_t>(expire_time_64); + + // Convert to broken-out time + struct tm expire_tm; + (void) localtime_r(&expire_time, &expire_tm); + + // Place in output expire structure. + expire.year = expire_tm.tm_year + 1900; + expire.month = expire_tm.tm_mon + 1; // Note different base + expire.day = expire_tm.tm_mday; + expire.hour = expire_tm.tm_hour; + expire.minute = expire_tm.tm_min; + expire.second = expire_tm.tm_sec; + expire.second_part = 0; // No fractional seconds + expire.neg = my_bool(0); // Not negative +} + +void +MySqlBinding::convertFromDatabaseTime(const MYSQL_TIME& expire, + uint32_t valid_lifetime, + time_t& cltt) { + // Copy across fields from MYSQL_TIME structure. + struct tm expire_tm; + memset(&expire_tm, 0, sizeof(expire_tm)); + + expire_tm.tm_year = expire.year - 1900; + expire_tm.tm_mon = expire.month - 1; + expire_tm.tm_mday = expire.day; + expire_tm.tm_hour = expire.hour; + expire_tm.tm_min = expire.minute; + expire_tm.tm_sec = expire.second; + expire_tm.tm_isdst = -1; // Let the system work out about DST + + // Convert to local time + cltt = mktime(&expire_tm) - valid_lifetime; +} + +ptime +MySqlBinding::convertFromDatabaseTime(const MYSQL_TIME& database_time) { + /// @todo Use fractional seconds instead of 0 when minimum supported + /// MySQL version has it. + long fractional = 0; + // long fractional = database_time.second_part * time_duration::ticks_per_second()/1000000; + ptime pt(boost::gregorian::date(database_time.year, + boost::gregorian::greg_month(database_time.month), + database_time.day), + time_duration(database_time.hour, database_time.minute, + database_time.second, fractional)); + + return (pt); +} + +MySqlBinding::MySqlBinding(enum_field_types buffer_type, + const size_t length) + // Make sure that the buffer has non-zero length in case we need to + // reference its first element to assign it to the MySQL binding. + : buffer_(length > 0 ? length : 1), length_(length), + null_value_(buffer_type == MYSQL_TYPE_NULL) { + memset(&bind_, 0, sizeof(MYSQL_BIND)); + bind_.buffer_type = buffer_type; + + if (buffer_type != MYSQL_TYPE_NULL) { + bind_.buffer = &buffer_[0]; + bind_.buffer_length = length_; + bind_.length = &length_; + bind_.is_null = &null_value_; + } +} + +void +MySqlBinding::setBufferLength(const unsigned long length) { + length_ = length; + // It appears that the MySQL connectors sometimes require that the + // buffer is specified (set to a non-zero value), even if the buffer + // length is 0. We have found that setting the buffer to 0 value would + // cause the value inserted to the database be NULL. In order to avoid + // it, we simply make sure that the buffer length is at least 1 byte and + // provide the pointer to this byte within the binding. + buffer_.resize(length_ > 0 ? length_ : 1); + bind_.buffer = &buffer_[0]; + bind_.buffer_length = length_; +} + +void +MySqlBinding::setTimestampValue(const ptime& timestamp) { + MYSQL_TIME database_time; + convertToDatabaseTime(timestamp, database_time); + // Copy database time into the buffer. + memcpy(static_cast<void*>(&buffer_[0]), reinterpret_cast<char*>(&database_time), + sizeof(MYSQL_TIME)); + bind_.buffer = &buffer_[0]; +} + +} // end of namespace isc::db +} // end of namespace isc diff --git a/src/lib/mysql/mysql_binding.h b/src/lib/mysql/mysql_binding.h new file mode 100644 index 0000000..57333f3 --- /dev/null +++ b/src/lib/mysql/mysql_binding.h @@ -0,0 +1,671 @@ +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MYSQL_BINDING_H +#define MYSQL_BINDING_H + +#include <asiolink/io_address.h> +#include <cc/data.h> +#include <database/database_connection.h> +#include <exceptions/exceptions.h> +#include <util/optional.h> +#include <boost/date_time/posix_time/conversion.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/shared_ptr.hpp> +#include <mysql/mysql_constants.h> +#include <mysql.h> +#include <mysqld_error.h> +#include <cstdint> +#include <iterator> +#include <string> +#include <vector> + +namespace isc { +namespace db { + +/// @brief Trait class for column types supported in MySQL. +/// +/// This class is used to map C++ types to MySQL column types +/// defined in MySQL C API and their sizes. Specializations of +/// this class provide such mapping. The default is a BLOB type +/// which can be used for various input types. +template<typename T> +struct MySqlBindingTraits { + /// @brief Column type represented in MySQL C API. + static const enum_field_types column_type = MYSQL_TYPE_BLOB; + /// @brief Length of data in this column. + /// + /// The value of 0 is used for variable size columns. + static const size_t length = 0; + /// @brief Boolean value indicating if the numeric value is + /// unsigned. + static const bool am_unsigned = false; +}; + +/// @brief Specialization for MySQL TEXT type. +template<> +struct MySqlBindingTraits<std::string> { + static const enum_field_types column_type = MYSQL_TYPE_STRING; + static const size_t length = 0; + static const bool am_unsigned = false; +}; + +/// @brief Specialization for MySQL TIMESTAMP type. +template<> +struct MySqlBindingTraits<boost::posix_time::ptime> { + static const enum_field_types column_type = MYSQL_TYPE_TIMESTAMP; + static const size_t length = sizeof(MYSQL_TIME); + static const bool am_unsignged = false; +}; + +/// @brief Specialization for MySQL TINYINT type. +template<> +struct MySqlBindingTraits<int8_t> { + static const enum_field_types column_type = MYSQL_TYPE_TINY; + static const size_t length = 1; + static const bool am_unsigned = false; +}; + +/// @brief Specialization for MySQL TINYINT UNSIGNED type. +template<> +struct MySqlBindingTraits<uint8_t> { + static const enum_field_types column_type = MYSQL_TYPE_TINY; + static const size_t length = 1; + static const bool am_unsigned = true; +}; + +/// @brief Speclialization for MySQL SMALLINT type. +template<> +struct MySqlBindingTraits<int16_t> { + static const enum_field_types column_type = MYSQL_TYPE_SHORT; + static const size_t length = 2; + static const bool am_unsigned = false; +}; + +/// @brief Specialization for MySQL SMALLINT UNSIGNED type. +template<> +struct MySqlBindingTraits<uint16_t> { + static const enum_field_types column_type = MYSQL_TYPE_SHORT; + static const size_t length = 2; + static const bool am_unsigned = true; +}; + +/// @brief Specialization for MySQL INT type. +template<> +struct MySqlBindingTraits<int32_t> { + static const enum_field_types column_type = MYSQL_TYPE_LONG; + static const size_t length = 4; + static const bool am_unsigned = false; +}; + +/// @brief Specialization for MySQL INT UNSIGNED type. +template<> +struct MySqlBindingTraits<uint32_t> { + static const enum_field_types column_type = MYSQL_TYPE_LONG; + static const size_t length = 4; + static const bool am_unsigned = true; +}; + +/// @brief Specialization for MySQL BIGINT type. +template<> +struct MySqlBindingTraits<int64_t> { + static const enum_field_types column_type = MYSQL_TYPE_LONGLONG; + static const size_t length = 8; + static const bool am_unsigned = false; +}; + +/// @brief Specialization for MySQL BIGINT UNSIGNED type. +template<> +struct MySqlBindingTraits<uint64_t> { + static const enum_field_types column_type = MYSQL_TYPE_LONGLONG; + static const size_t length = 8; + static const bool am_unsigned = true; +}; + +template<> +struct MySqlBindingTraits<float> { + static const enum_field_types column_type = MYSQL_TYPE_FLOAT; + static const size_t length = 4; + static const bool am_unsigned = false; +}; + +/// @brief Forward declaration of @c MySqlBinding class. +class MySqlBinding; + +/// @brief Shared pointer to the @c Binding class. +typedef boost::shared_ptr<MySqlBinding> MySqlBindingPtr; + +/// @brief MySQL binding used in prepared statements. +/// +/// Kea uses prepared statements to execute queries in a database. +/// Prepared statements include placeholders for the input parameters. +/// These parameters are passed to the prepared statements via a +/// collection of @c MYSQL_BIND structures. The same structures are +/// used to receive the values from the database as a result of +/// SELECT statements. +/// +/// The @c MYSQL_BIND structure contains information about the +/// data type and length. It also contains pointer to the buffer +/// actually holding the data to be passed to the database, a +/// flag indicating if the value is null etc. +/// +/// The @c MySqlBinding is a C++ wrapper around this structure which +/// is meant to ease management of the MySQL bindings. The major +/// benefit is that the @c MySqlBinding class owns the buffer, +/// holding the data as well as other variables which are assigned +/// to the @c MYSQL_BIND structure. It also automatically detects +/// the appropriate @c enum_field_types value based on the C++ +/// type used in the binding. +class MySqlBinding { +public: + + /// @brief Returns MySQL column type for the binding. + /// + /// @return column type, e.g. MYSQL_TYPE_STRING. + enum_field_types getType() const { + return (bind_.buffer_type); + } + + /// @brief Returns reference to the native binding. + /// + /// The returned reference is only valid for the lifetime of the + /// object. Make sure that the object is not destroyed as long + /// as the binding is required. In particular, do not destroy this + /// object before database query is complete. + /// + /// @return Reference to native MySQL binding. + MYSQL_BIND& getMySqlBinding() { + return (bind_); + } + + /// @brief Returns value held in the binding as string. + /// + /// Call @c MySqlBinding::amNull to verify that the value is not + /// null prior to calling this method. + /// + /// @throw InvalidOperation if the value is NULL or the binding + /// type is not @c MYSQL_TYPE_STRING. + /// + /// @return String value. + std::string getString() const; + + /// @brief Returns value held in the binding as string. + /// + /// If the value to be returned is null, a default value is returned. + /// + /// @param default_value Default value. + /// + /// @throw InvalidOperation if the binding type is not @c MYSQL_TYPE_STRING. + /// + /// @return String value. + std::string getStringOrDefault(const std::string& default_value) const; + + /// @brief Returns value held in the binding as JSON. + /// + /// Call @c MySqlBinding::amNull to verify that the value is not + /// null prior to calling this method. + /// + /// @throw InvalidOperation if the binding is not @c MYSQL_TYPE_STRING. + /// @throw data::JSONError if the string value is not a valid JSON. + /// + /// @return JSON structure or NULL if the string is null. + data::ElementPtr getJSON() const; + + /// @brief Returns value held in the binding as blob. + /// + /// Call @c MySqlBinding::amNull to verify that the value is not + /// null prior to calling this method. + /// + /// @throw InvalidOperation if the value is NULL or the binding + /// type is not @c MYSQL_TYPE_BLOB. + /// + /// @return Blob in a vector. + std::vector<uint8_t> getBlob() const; + + /// @brief Returns value held in the binding as blob. + /// + /// If the value to be returned is null, a default value is returned. + /// + /// @param default_value Default value. + /// + /// @throw InvalidOperation if the binding type is not @c MYSQL_TYPE_BLOB. + /// + /// @return Blob in a vector. + std::vector<uint8_t> + getBlobOrDefault(const std::vector<uint8_t>& default_value) const; + + /// @brief Returns numeric value held in the binding. + /// + /// Call @c MySqlBinding::amNull to verify that the value is not + /// null prior to calling this method. + /// + /// @tparam Numeric type corresponding to the binding type, e.g. + /// @c uint8_t, @c uint16_t etc. + /// + /// @throw InvalidOperation if the value is NULL or the binding + /// type does not match the template parameter. + /// + /// @return Numeric value of a specified type. + template<typename T> + T getInteger() const { + // Make sure that the binding type is numeric. + validateAccess<T>(); + + // Convert the buffer to a numeric type and then return a copy. + const T* value = reinterpret_cast<const T*>(&buffer_[0]); + return (*value); + } + + /// @brief Returns numeric value held in the binding. + /// + /// If the value to be returned is null, a default value is returned. + /// + /// @tparam Numeric type corresponding to the binding type, e.g. + /// @c uint8_t, @c uint16_t etc. + /// @param default_value Default value. + /// + /// @throw InvalidOperation if the binding type does not match the + /// template parameter. + /// + /// @return Numeric value of a specified type. + template<typename T> + T getIntegerOrDefault(T default_value) const { + if (amNull()) { + return (default_value); + } + return (getInteger<T>()); + } + + /// @brief Returns float value held in the binding. + /// + /// Call @c MySqlBinding::amNull to verify that the value is not + /// null prior to calling this method. + /// + /// @throw InvalidOperation if the value is NULL or the binding + /// type does not match the template parameter. + /// + /// @return Float value. + float getFloat() const; + + /// @brief Returns boolean value held in the binding. + /// + /// Call @c MySqlBinding::amNull to verify that the value is not + /// null prior to calling this method. + /// + /// @throw InvalidOperation if the value is NULL or the binding + /// type is not uint8_t. + /// + /// @return Boolean value. + bool getBool() const { + return (static_cast<bool>(getInteger<uint8_t>())); + } + + /// @brief Returns timestamp value held in the binding. + /// + /// Call @c MySqlBinding::amNull to verify that the value is not + /// null prior to calling this method. + /// + /// @throw InvalidOperation if the value is NULL or the binding + /// type is not @c MYSQL_TYPE_TIMESTAMP. + /// + /// @return Timestamp converted to POSIX time. + boost::posix_time::ptime getTimestamp() const; + + /// @brief Returns timestamp value held in the binding. + /// + /// If the value to be returned is null, a default value is returned. + /// + /// @param default_value Default value. + /// + /// @throw InvalidOperation if the binding type is not @c MYSQL_TYPE_TIMESTAMP. + /// + /// @return Timestamp converted to POSIX time. + boost::posix_time::ptime + getTimestampOrDefault(const boost::posix_time::ptime& default_value) const; + + /// @brief Checks if the bound value is NULL. + /// + /// @return true if the value in the binding is NULL, false otherwise. + bool amNull() const { + return (null_value_ == MLM_TRUE); + } + + /// @brief Creates binding of text type for receiving data. + /// + /// @param length Length of the buffer into which received data will + /// be stored. + /// + /// @return Pointer to the created binding. + static MySqlBindingPtr createString(const unsigned long length); + + /// @brief Creates binding of text type for sending data. + /// + /// @param value String value to be sent to the database. + /// + /// @return Pointer to the created binding. + static MySqlBindingPtr createString(const std::string& value); + + /// @brief Conditionally creates binding of text type for sending + /// data if provided value is unspecified. + /// + /// @param value String value to be sent to the database. + /// + /// @return Pointer to the created binding. + static MySqlBindingPtr condCreateString(const util::Optional<std::string>& value); + + /// @brief Creates binding of blob type for receiving data. + /// + /// @param length Length of the buffer into which received data will + /// be stored. + /// + /// @return Pointer to the created binding. + static MySqlBindingPtr createBlob(const unsigned long length); + + /// @brief Creates binding of blob type for sending data. + /// + /// @tparam Iterator Type of the iterator. + /// + /// @param begin Iterator pointing to the beginning of the input + /// buffer holding the data to be sent to the database. + /// @param end Iterator pointing to the end of the input buffer + /// holding the data to be sent to the database. + /// + /// @return Pointer to the created binding. + template<typename Iterator> + static MySqlBindingPtr createBlob(Iterator begin, Iterator end) { + MySqlBindingPtr binding(new MySqlBinding(MYSQL_TYPE_BLOB, + std::distance(begin, end))); + binding->setBufferValue(begin, end); + return (binding); + } + + /// @brief Creates binding of numeric type for receiving data. + /// + /// @tparam Numeric type corresponding to the binding type, e.g. + /// @c uint8_t, @c uint16_t etc. + /// + /// @return Pointer to the created binding. + template<typename T> + static MySqlBindingPtr createInteger() { + MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<T>::column_type, + MySqlBindingTraits<T>::length)); + binding->setValue<T>(0); + return (binding); + } + + /// @brief Creates binding of numeric type for sending data. + /// + /// @tparam Numeric type corresponding to the binding type, e.g. + /// @c uint8_t, @c uint16_t etc. + /// + /// @param value Numeric value to be sent to the database. + /// + /// @return Pointer to the created binding. + template<typename T> + static MySqlBindingPtr createInteger(T value) { + MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<T>::column_type, + MySqlBindingTraits<T>::length)); + binding->setValue(value); + return (binding); + } + + /// @brief Conditionally creates binding of numeric type for sending + /// data if provided value is specified. + /// + /// @tparam T Numeric type corresponding to the binding type, e.g. + /// @c uint8_t, @c uint16_t etc. + /// + /// @param value Numeric value to be sent to the database. + /// + /// @return Pointer to the created binding. + template<typename T> + static MySqlBindingPtr condCreateInteger(const util::Optional<T>& value) { + return (value.unspecified() ? createNull() : createInteger<T>(value.get())); + } + + /// @brief Creates binding having a float type for sending data. + /// + /// @param value Float value to be sent to the database. + /// + /// @return Pointer to the created binding. + static MySqlBindingPtr createFloat(const float value); + + /// @brief Conditionally creates binding of float type for sending data if + /// provided value is specified. + /// + /// @tparam T Floating point type to be converted to float. + /// + /// @param value Value to be stored in the database as float. + /// + /// @return Pointer to the created binding. + template<typename T> + static MySqlBindingPtr condCreateFloat(const util::Optional<T>& value) { + return (value.unspecified() ? createNull() : + createInteger<float> (static_cast<float>(value.get()))); + } + + /// @brief Creates binding having a bool type for receiving data. + /// + /// @return Pointer to the created binding holding an @c uint8_t + /// value representing the boolean value. + static MySqlBindingPtr createBool(); + + /// @brief Creates binding having a bool type for sending data. + /// + /// @param value Boolean value to be sent to the database. + /// + /// @return Pointer to the created binding holding an @c uint8_t + /// value representing the boolean value. + static MySqlBindingPtr createBool(const bool value); + + /// @brief Conditionally creates binding of @c uint8_t type representing + /// a boolean value if provided value is specified. + /// + /// @param value Boolean value for which the binding should be created. + /// + /// @return Pointer to the created binding. + static MySqlBindingPtr condCreateBool(const util::Optional<bool>& value); + + /// @brief Conditionally creates binding of @c uint32_t type representing + /// an IPv4 address if provided value is specified. + /// + /// @param value @c IOAddress encapsulating an IPv4 address. + /// + /// @return Pointer to the created binding. + static MySqlBindingPtr + condCreateIPv4Address(const util::Optional<asiolink::IOAddress>& value); + + /// @brief Creates binding of timestamp type for receiving data. + /// + /// @return Pointer to the created binding. + static MySqlBindingPtr createTimestamp(); + + /// @brief Creates binding of timestamp type for sending data. + /// + /// @param timestamp Timestamp value to be sent to the database. + /// + /// @return Pointer to the created binding. + static MySqlBindingPtr createTimestamp(const boost::posix_time::ptime& timestamp); + + /// @brief Creates binding encapsulating a NULL value. + /// + /// This method is used to create a binding encapsulating a NULL + /// value, which can be used to assign NULL to any type of column. + /// + /// @return Pointer to the created binding. + static MySqlBindingPtr createNull(); + + /// @brief Converts time_t value to database time. + /// + /// @param input_time A time_t value representing time. + /// @param output_time Reference to MYSQL_TIME object where converted time + /// will be put. + static void convertToDatabaseTime(const time_t input_time, + MYSQL_TIME& output_time); + + /// @brief Converts POSIX time value to database time. + /// + /// @param input_time A POSIX time value representing local time. + /// @param output_time Reference to MYSQL_TIME object where converted time + /// will be put. + static void convertToDatabaseTime(const boost::posix_time::ptime& input_time, + MYSQL_TIME& output_time); + + /// @brief Converts Lease Time to Database Times + /// + /// Within the DHCP servers, times are stored as client last transmit time + /// and valid lifetime. In the database, the information is stored as + /// valid lifetime and "expire" (time of expiry of the lease). They are + /// related by the equation: + /// + /// - expire = client last transmit time + valid lifetime + /// + /// This method converts from the times in the lease object into times + /// able to be added to the database. + /// + /// @param cltt Client last transmit time + /// @param valid_lifetime Valid lifetime + /// @param expire Reference to MYSQL_TIME object where the expiry time of + /// the DHCP lease will be put. + /// + /// @throw isc::BadValue if the sum of the calculated expiration time is + /// greater than the value of @c LeaseMgr::MAX_DB_TIME. + static void convertToDatabaseTime(const time_t cltt, + const uint32_t valid_lifetime, + MYSQL_TIME& expire); + + /// @brief Converts Database Time to Lease Times + /// + /// Within the database, time is stored as "expire" (time of expiry of the + /// lease) and valid lifetime. In the DHCP server, the information is + /// stored client last transmit time and valid lifetime. These are related + /// by the equation: + /// + /// - client last transmit time = expire - valid_lifetime + /// + /// This method converts from the times in the database into times + /// able to be inserted into the lease object. + /// + /// @param expire Reference to MYSQL_TIME object from where the expiry + /// time of the lease is taken. + /// @param valid_lifetime lifetime of the lease. + /// @param cltt Reference to location where client last transmit time + /// is put. + static void convertFromDatabaseTime(const MYSQL_TIME& expire, + uint32_t valid_lifetime, + time_t& cltt); + + /// @brief Converts database time to POSIX time. + /// + /// @param database_time Reference to MYSQL_TIME object where database + /// time is stored. + /// + /// @return Database time converted to local POSIX time. + static boost::posix_time::ptime + convertFromDatabaseTime(const MYSQL_TIME& database_time); + +private: + + /// @brief Private constructor. + /// + /// This constructor is private because MySQL bindings should only be + /// created using static factory functions. + /// + /// @param buffer_type MySQL buffer type as defined in MySQL C API. + /// @param length Buffer length. + MySqlBinding(enum_field_types buffer_type, const size_t length); + + /// @brief Assigns new value to a buffer. + /// + /// @tparam Iterator Type of the iterators marking beginning and end + /// of the range to be assigned to the buffer. + /// + /// @param begin Iterator pointing to the beginning of the assigned + /// range. + /// @param end Iterator pointing to the end of the assigned range. + template<typename Iterator> + void setBufferValue(Iterator begin, Iterator end) { + length_ = std::distance(begin, end); + buffer_.assign(begin, end); + // It appears that the MySQL connectors sometimes require that the + // buffer is specified (set to a non-zero value), even if the buffer + // length is 0. We have found that setting the buffer to 0 value would + // cause the value inserted to the database be NULL. In order to avoid + // it, we simply make sure that the buffer length is at least 1 byte and + // provide the pointer to this byte within the binding. + if (buffer_.empty()) { + buffer_.resize(1); + } + bind_.buffer = &buffer_[0]; + bind_.buffer_length = length_; + } + + /// @brief Resizes the buffer and assigns new length to the binding. + /// + /// @param length New buffer length to be used. + void setBufferLength(const unsigned long length); + + /// @brief Copies numeric value to a buffer and modifies "unsigned" flag + /// accoriding to the numeric type used. + /// + /// @tparam T Type of the numeric value. + /// + /// @param value Value to be copied to the buffer. + template<typename T> + void setValue(T value) { + memcpy(static_cast<void*>(&buffer_[0]), reinterpret_cast<char*>(&value), + sizeof(value)); + bind_.buffer = &buffer_[0]; + bind_.is_unsigned = (MySqlBindingTraits<T>::am_unsigned ? MLM_TRUE : MLM_FALSE); + } + + /// @brief Converts timestamp to database time value and copies it to + /// the buffer. + /// + /// @param timestamp Timestamp value as POSIX time. + void setTimestampValue(const boost::posix_time::ptime& timestamp); + + /// @brief Checks if the data accessor called is matching the type + /// of the data held in the binding. + /// + /// @tparam Data type requested, e.g. @c std::string. + /// + /// @throw InvalidOperation if the requested data type is not matching + /// the type of the binding, e.g. called @c getString but the binding + /// type is @c MYSQL_TYPE_LONG. + template<typename T> + void validateAccess() const { + // Can't retrieve null value. + if (amNull()) { + isc_throw(InvalidOperation, "retrieved value is null"); + } + // The type of the accessor must match the stored data type. + if (MySqlBindingTraits<T>::column_type != getType()) { + isc_throw(InvalidOperation, "mismatched column type"); + } + } + + /// @brief Data buffer (both input and output). + std::vector<uint8_t> buffer_; + + /// @brief Buffer length. + unsigned long length_; + + /// @brief Flag indicating whether the value of the binding is NULL. + my_bool null_value_; + + /// @brief Native MySQL binding wrapped by this class. + MYSQL_BIND bind_; +}; + +/// @brief Collection of bindings. +typedef std::vector<MySqlBindingPtr> MySqlBindingCollection; + + +} // end of namespace isc::db +} // end of namespace isc + +#endif diff --git a/src/lib/mysql/mysql_connection.cc b/src/lib/mysql/mysql_connection.cc new file mode 100644 index 0000000..fc65cce --- /dev/null +++ b/src/lib/mysql/mysql_connection.cc @@ -0,0 +1,521 @@ +// Copyright (C) 2012-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <database/db_log.h> +#include <exceptions/exceptions.h> +#include <mysql/mysql_connection.h> +#include <util/file_utilities.h> + +#include <boost/lexical_cast.hpp> + +#include <algorithm> +#include <stdint.h> +#include <string> +#include <limits> + +using namespace isc; +using namespace std; + +namespace isc { +namespace db { + +int MySqlHolder::atexit_ = [] { + return atexit([] { mysql_library_end(); }); +}(); + +/// @todo: Migrate this default value to src/bin/dhcpX/simple_parserX.cc +const int MYSQL_DEFAULT_CONNECTION_TIMEOUT = 5; // seconds + +MySqlTransaction::MySqlTransaction(MySqlConnection& conn) + : conn_(conn), committed_(false) { + conn_.startTransaction(); +} + +MySqlTransaction::~MySqlTransaction() { + // Rollback if the MySqlTransaction::commit wasn't explicitly + // called. + if (!committed_) { + conn_.rollback(); + } +} + +void +MySqlTransaction::commit() { + conn_.commit(); + committed_ = true; +} + +// Open the database using the parameters passed to the constructor. + +void +MySqlConnection::openDatabase() { + // Set up the values of the parameters + const char* host = "localhost"; + string shost; + try { + shost = getParameter("host"); + host = shost.c_str(); + } catch (...) { + // No host. Fine, we'll use "localhost" + } + + unsigned int port = 0; + string sport; + try { + sport = getParameter("port"); + } catch (...) { + // No port parameter, we are going to use the default port. + sport = ""; + } + + if (sport.size() > 0) { + // Port was given, so try to convert it to an integer. + + try { + port = boost::lexical_cast<unsigned int>(sport); + } catch (...) { + // Port given but could not be converted to an unsigned int. + // Just fall back to the default value. + port = 0; + } + + // The port is only valid when it is in the 0..65535 range. + // Again fall back to the default when the given value is invalid. + if (port > numeric_limits<uint16_t>::max()) { + port = 0; + } + } + + const char* user = NULL; + string suser; + try { + suser = getParameter("user"); + user = suser.c_str(); + } catch (...) { + // No user. Fine, we'll use NULL + } + + const char* password = NULL; + string spassword; + try { + spassword = getParameter("password"); + password = spassword.c_str(); + } catch (...) { + // No password. Fine, we'll use NULL + } + + const char* name = NULL; + string sname; + try { + sname = getParameter("name"); + name = sname.c_str(); + } catch (...) { + // No database name. Throw a "NoName" exception + isc_throw(NoDatabaseName, "must specify a name for the database"); + } + + unsigned int connect_timeout = MYSQL_DEFAULT_CONNECTION_TIMEOUT; + string stimeout; + try { + stimeout = getParameter("connect-timeout"); + } catch (...) { + // No timeout parameter, we are going to use the default timeout. + stimeout = ""; + } + + if (stimeout.size() > 0) { + // Timeout was given, so try to convert it to an integer. + + try { + connect_timeout = boost::lexical_cast<unsigned int>(stimeout); + } catch (...) { + // Timeout given but could not be converted to an unsigned int. Set + // the connection timeout to an invalid value to trigger throwing + // of an exception. + connect_timeout = 0; + } + + // The timeout is only valid if greater than zero, as depending on the + // database, a zero timeout might signify something like "wait + // indefinitely". + // + // The check below also rejects a value greater than the maximum + // integer value. The lexical_cast operation used to obtain a numeric + // value from a string can get confused if trying to convert a negative + // integer to an unsigned int: instead of throwing an exception, it may + // produce a large positive value. + if ((connect_timeout == 0) || + (connect_timeout > numeric_limits<int>::max())) { + isc_throw(DbInvalidTimeout, "database connection timeout (" << + stimeout << ") must be an integer greater than 0"); + } + } + + const char* ca_file(0); + const char* ca_dir(0); + string sca; + try { + sca = getParameter("trust-anchor"); + tls_ = true; + if (util::file::isDir(sca)) { + ca_dir = sca.c_str(); + } else { + ca_file = sca.c_str(); + } + } catch (...) { + // No trust anchor + } + + const char* cert_file(0); + string scert; + try { + scert = getParameter("cert-file"); + tls_ = true; + cert_file = scert.c_str(); + } catch (...) { + // No client certificate file + } + + const char* key_file(0); + string skey; + try { + skey = getParameter("key-file"); + tls_ = true; + key_file = skey.c_str(); + } catch (...) { + // No private key file + } + + const char* cipher_list(0); + string scipher; + try { + scipher = getParameter("cipher-list"); + tls_ = true; + cipher_list = scipher.c_str(); + } catch (...) { + // No cipher list + } + + // Set options for the connection: + // + // Set options for the connection: + // Make sure auto_reconnect is OFF! Enabling it leaves us with an unusable + // connection after a reconnect as among other things, it drops all our + // pre-compiled statements. + my_bool auto_reconnect = MLM_FALSE; + int result = mysql_options(mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect); + if (result != 0) { + isc_throw(DbOpenError, "unable to set auto-reconnect option: " << + mysql_error(mysql_)); + } + + // Make sure we have a large idle time window ... say 30 days... + const char *wait_time = "SET SESSION wait_timeout = 30 * 86400"; + result = mysql_options(mysql_, MYSQL_INIT_COMMAND, wait_time); + if (result != 0) { + isc_throw(DbOpenError, "unable to set wait_timeout " << + mysql_error(mysql_)); + } + + // Set SQL mode options for the connection: SQL mode governs how what + // constitutes insertable data for a given column, and how to handle + // invalid data. We want to ensure we get the strictest behavior and + // to reject invalid data with an error. + const char *sql_mode = "SET SESSION sql_mode ='STRICT_ALL_TABLES'"; + result = mysql_options(mysql_, MYSQL_INIT_COMMAND, sql_mode); + if (result != 0) { + isc_throw(DbOpenError, "unable to set SQL mode options: " << + mysql_error(mysql_)); + } + + // Connection timeout, the amount of time taken for the client to drop + // the connection if the server is not responding. + result = mysql_options(mysql_, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout); + if (result != 0) { + isc_throw(DbOpenError, "unable to set database connection timeout: " << + mysql_error(mysql_)); + } + + // If TLS is enabled set it. If something should go wrong it will happen + // later at the mysql_real_connect call. + if (tls_) { + mysql_ssl_set(mysql_, key_file, cert_file, ca_file, ca_dir, + cipher_list); + } + + // Open the database. + // + // The option CLIENT_FOUND_ROWS is specified so that in an UPDATE, + // the affected rows are the number of rows found that match the + // WHERE clause of the SQL statement, not the rows changed. The reason + // here is that MySQL apparently does not update a row if data has not + // changed and so the "affected rows" (retrievable from MySQL) is zero. + // This makes it hard to distinguish whether the UPDATE changed no rows + // because no row matching the WHERE clause was found, or because a + // row was found but no data was altered. + MYSQL* status = mysql_real_connect(mysql_, host, user, password, name, + port, NULL, CLIENT_FOUND_ROWS); + if (status != mysql_) { + isc_throw(DbOpenError, mysql_error(mysql_)); + } + + // Enable autocommit. In case transaction is explicitly used, this + // setting will be overwritten for the transaction. However, there are + // cases when lack of autocommit could cause transactions to hang + // until commit or rollback is explicitly called. This already + // caused issues for some unit tests which were unable to cleanup + // the database after the test because of pending transactions. + // Use of autocommit will eliminate this problem. + my_bool autocommit_result = mysql_autocommit(mysql_, 1); + if (autocommit_result != 0) { + isc_throw(DbOperationError, mysql_error(mysql_)); + } + + // To avoid a flush to disk on every commit, the global parameter + // innodb_flush_log_at_trx_commit should be set to 2. This will cause the + // changes to be written to the log, but flushed to disk in the background + // every second. Setting the parameter to that value will speed up the + // system, but at the risk of losing data if the system crashes. +} + +// Get schema version. + +std::pair<uint32_t, uint32_t> +MySqlConnection::getVersion(const ParameterMap& parameters) { + // Get a connection. + MySqlConnection conn(parameters); + + // Open the database. + conn.openDatabase(); + + // Allocate a new statement. + MYSQL_STMT *stmt = mysql_stmt_init(conn.mysql_); + if (stmt == NULL) { + isc_throw(DbOperationError, "unable to allocate MySQL prepared " + "statement structure, reason: " << mysql_error(conn.mysql_)); + } + + try { + + // Prepare the statement from SQL text. + const char* version_sql = "SELECT version, minor FROM schema_version"; + int status = mysql_stmt_prepare(stmt, version_sql, strlen(version_sql)); + if (status != 0) { + isc_throw(DbOperationError, "unable to prepare MySQL statement <" + << version_sql << ">, reason: " + << mysql_error(conn.mysql_)); + } + + // Execute the prepared statement. + if (MysqlExecuteStatement(stmt) != 0) { + isc_throw(DbOperationError, "cannot execute schema version query <" + << version_sql << ">, reason: " + << mysql_errno(conn.mysql_)); + } + + // Bind the output of the statement to the appropriate variables. + MYSQL_BIND bind[2]; + memset(bind, 0, sizeof(bind)); + + uint32_t version; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].is_unsigned = 1; + bind[0].buffer = &version; + bind[0].buffer_length = sizeof(version); + + uint32_t minor; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].is_unsigned = 1; + bind[1].buffer = &minor; + bind[1].buffer_length = sizeof(minor); + + if (mysql_stmt_bind_result(stmt, bind)) { + isc_throw(DbOperationError, "unable to bind result set for <" + << version_sql << ">, reason: " + << mysql_errno(conn.mysql_)); + } + + // Fetch the data. + if (mysql_stmt_fetch(stmt)) { + isc_throw(DbOperationError, "unable to bind result set for <" + << version_sql << ">, reason: " + << mysql_errno(conn.mysql_)); + } + + // Discard the statement and its resources + mysql_stmt_close(stmt); + + return (std::make_pair(version, minor)); + + } catch (const std::exception&) { + // Avoid a memory leak on error. + mysql_stmt_close(stmt); + + // Send the exception to the caller. + throw; + } +} + +// Prepared statement setup. The textual form of an SQL statement is stored +// in a vector of strings (text_statements_) and is used in the output of +// error messages. The SQL statement is also compiled into a "prepared +// statement" (stored in statements_), which avoids the overhead of compilation +// during use. As prepared statements have resources allocated to them, the +// class destructor explicitly destroys them. + +void +MySqlConnection::prepareStatement(uint32_t index, const char* text) { + // Validate that there is space for the statement in the statements array + // and that nothing has been placed there before. + if ((index >= statements_.size()) || (statements_[index] != NULL)) { + isc_throw(InvalidParameter, "invalid prepared statement index (" << + static_cast<int>(index) << ") or indexed prepared " << + "statement is not null"); + } + + // All OK, so prepare the statement + text_statements_[index] = std::string(text); + statements_[index] = mysql_stmt_init(mysql_); + if (statements_[index] == NULL) { + isc_throw(DbOperationError, "unable to allocate MySQL prepared " + "statement structure, reason: " << mysql_error(mysql_)); + } + + int status = mysql_stmt_prepare(statements_[index], text, strlen(text)); + if (status != 0) { + isc_throw(DbOperationError, "unable to prepare MySQL statement <" << + text << ">, reason: " << mysql_error(mysql_)); + } +} + +void +MySqlConnection::prepareStatements(const TaggedStatement* start_statement, + const TaggedStatement* end_statement) { + // Created the MySQL prepared statements for each DML statement. + for (const TaggedStatement* tagged_statement = start_statement; + tagged_statement != end_statement; ++tagged_statement) { + if (tagged_statement->index >= statements_.size()) { + statements_.resize(tagged_statement->index + 1, NULL); + text_statements_.resize(tagged_statement->index + 1, + std::string("")); + } + prepareStatement(tagged_statement->index, + tagged_statement->text); + } +} + +void MySqlConnection::clearStatements() { + statements_.clear(); + text_statements_.clear(); +} + +/// @brief Destructor +MySqlConnection::~MySqlConnection() { + // Free up the prepared statements, ignoring errors. (What would we do + // about them? We're destroying this object and are not really concerned + // with errors on a database connection that is about to go away.) + for (int i = 0; i < statements_.size(); ++i) { + if (statements_[i] != NULL) { + (void) mysql_stmt_close(statements_[i]); + statements_[i] = NULL; + } + } + statements_.clear(); + text_statements_.clear(); +} + +// Time conversion methods. +// +// Note that the MySQL TIMESTAMP data type (used for "expire") converts data +// from the current timezone to UTC for storage, and from UTC to the current +// timezone for retrieval. +// +// This causes no problems providing that: +// a) cltt is given in local time +// b) We let the system take care of timezone conversion when converting +// from a time read from the database into a local time. +void +MySqlConnection::convertToDatabaseTime(const time_t input_time, + MYSQL_TIME& output_time) { + MySqlBinding::convertToDatabaseTime(input_time, output_time); +} + +void +MySqlConnection::convertToDatabaseTime(const time_t cltt, + const uint32_t valid_lifetime, + MYSQL_TIME& expire) { + MySqlBinding::convertToDatabaseTime(cltt, valid_lifetime, expire); +} + +void +MySqlConnection::convertFromDatabaseTime(const MYSQL_TIME& expire, + uint32_t valid_lifetime, time_t& cltt) { + MySqlBinding::convertFromDatabaseTime(expire, valid_lifetime, cltt); +} + +void +MySqlConnection::startTransaction() { + // If it is nested transaction, do nothing. + if (++transaction_ref_count_ > 1) { + return; + } + + DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_START_TRANSACTION); + checkUnusable(); + // We create prepared statements for all other queries, but MySQL + // don't support prepared statements for START TRANSACTION. + int status = mysql_query(mysql_, "START TRANSACTION"); + if (status != 0) { + isc_throw(DbOperationError, "unable to start transaction, " + "reason: " << mysql_error(mysql_)); + } +} + +bool +MySqlConnection::isTransactionStarted() const { + return (transaction_ref_count_ > 0); +} + +void +MySqlConnection::commit() { + if (transaction_ref_count_ <= 0) { + isc_throw(Unexpected, "commit called for not started transaction - coding error"); + } + + // When committing nested transaction, do nothing. + if (--transaction_ref_count_ > 0) { + return; + } + DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_COMMIT); + checkUnusable(); + if (mysql_commit(mysql_) != 0) { + isc_throw(DbOperationError, "commit failed: " + << mysql_error(mysql_)); + } +} + +void +MySqlConnection::rollback() { + if (transaction_ref_count_ <= 0) { + isc_throw(Unexpected, "rollback called for not started transaction - coding error"); + } + + // When rolling back nested transaction, do nothing. + if (--transaction_ref_count_ > 0) { + return; + } + DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_ROLLBACK); + checkUnusable(); + if (mysql_rollback(mysql_) != 0) { + isc_throw(DbOperationError, "rollback failed: " + << mysql_error(mysql_)); + } +} + +} // namespace db +} // namespace isc diff --git a/src/lib/mysql/mysql_connection.h b/src/lib/mysql/mysql_connection.h new file mode 100644 index 0000000..50ebc28 --- /dev/null +++ b/src/lib/mysql/mysql_connection.h @@ -0,0 +1,760 @@ +// Copyright (C) 2012-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MYSQL_CONNECTION_H +#define MYSQL_CONNECTION_H + +#include <asiolink/io_service.h> +#include <database/database_connection.h> +#include <database/db_exceptions.h> +#include <database/db_log.h> +#include <exceptions/exceptions.h> +#include <mysql/mysql_binding.h> +#include <mysql/mysql_constants.h> +#include <boost/scoped_ptr.hpp> +#include <mysql.h> +#include <mysqld_error.h> +#include <errmsg.h> +#include <functional> +#include <vector> +#include <stdint.h> + +namespace isc { +namespace db { + + +/// @brief Fetch and Release MySQL Results +/// +/// When a MySQL statement is expected, to fetch the results the function +/// mysql_stmt_fetch() must be called. As well as getting data, this +/// allocates internal state. Subsequent calls to mysql_stmt_fetch can be +/// made, but when all the data is retrieved, mysql_stmt_free_result must be +/// called to free up the resources allocated. +/// +/// Created prior to the first fetch, this class's destructor calls +/// mysql_stmt_free_result, so eliminating the need for an explicit release +/// in the method calling mysql_stmt_free_result. In this way, it guarantees +/// that the resources are released even if the MySqlLeaseMgr method concerned +/// exits via an exception. + +class MySqlFreeResult { +public: + + /// @brief Constructor + /// + /// Store the pointer to the statement for which data is being fetched. + /// + /// Note that according to the MySQL documentation, mysql_stmt_free_result + /// only releases resources if a cursor has been allocated for the + /// statement. This implies that it is a no-op if none have been. Either + /// way, any error from mysql_stmt_free_result is ignored. (Generating + /// an exception is not much help, as it will only confuse things if the + /// method calling mysql_stmt_fetch is exiting via an exception.) + MySqlFreeResult(MYSQL_STMT* statement) : statement_(statement) + {} + + /// @brief Destructor + /// + /// Frees up fetch context if a fetch has been successfully executed. + ~MySqlFreeResult() { + (void) mysql_stmt_free_result(statement_); + } + +private: + MYSQL_STMT* statement_; ///< Statement for which results are freed +}; + +/// @brief MySQL Selection Statements +/// +/// Each statement is associated with an index, which is used to reference the +/// associated prepared statement. +struct TaggedStatement { + uint32_t index; + const char* text; +}; + +/// @brief Retry on InnoDB deadlock. +/// +/// When f(args) returns ER_LOCK_DEADLOCK f(args) is called again up to 5 times. +/// +/// @tparam Fun Type of the function which must return an int. +/// @tparam Args Types of arguments. +/// @param fun The function to call. +/// @param args Arguments. +/// @return status (can be ER_LOCK_DEADLOCK after 5 retries). +template <typename Fun, typename... Args> +int retryOnDeadlock(Fun& fun, Args... args) { + int status; + for (unsigned count = 0; count < 5; ++count) { + status = fun(args...); + if (status != ER_LOCK_DEADLOCK) { + break; + } + } + return (status); +} + +/// @brief Execute a prepared statement. +/// +/// Call mysql_stmt_execute and retry on ER_LOCK_DEADLOCK. +/// +/// @param stmt Statement to execute. +/// @return status (can be ER_LOCK_DEADLOCK after 5 retries). +inline int MysqlExecuteStatement(MYSQL_STMT* stmt) { + return (retryOnDeadlock(mysql_stmt_execute, stmt)); +} + +/// @brief Execute a literal statement. +/// +/// Call mysql_query and retry on ER_LOCK_DEADLOCK. +/// +/// @param mysql MySQL context. +/// @param stmt Statement to execute. +/// @return status (can be ER_LOCK_DEADLOCK after 5 retries). +inline int MysqlQuery(MYSQL* mysql, const char* stmt) { + return (retryOnDeadlock(mysql_query, mysql, stmt)); +} + +/// @brief MySQL Handle Holder +/// +/// Small RAII object for safer initialization, will close the database +/// connection upon destruction. This means that if an exception is thrown +/// during database initialization, resources allocated to the database are +/// guaranteed to be freed. +/// +/// It makes no sense to copy an object of this class. After the copy, both +/// objects would contain pointers to the same MySql context object. The +/// destruction of one would invalid the context in the remaining object. +/// For this reason, the class is declared noncopyable. +class MySqlHolder : public boost::noncopyable { +public: + + /// @brief Constructor + /// + /// Initialize MySql and store the associated context object. + /// + /// @throw DbOpenError Unable to initialize MySql handle. + MySqlHolder() : mysql_(mysql_init(NULL)) { + if (mysql_ == NULL) { + isc_throw(db::DbOpenError, "unable to initialize MySQL"); + } + } + + /// @brief Destructor + /// + /// Frees up resources allocated by the initialization of MySql. + ~MySqlHolder() { + if (mysql_ != NULL) { + mysql_close(mysql_); + } + } + + /// @brief Conversion Operator + /// + /// Allows the MySqlHolder object to be passed as the context argument to + /// mysql_xxx functions. + operator MYSQL*() const { + return (mysql_); + } + +private: + /// @brief Variable used for its static property to call atexit() once. + static int atexit_; + + /// @brief Initialization context + MYSQL* mysql_; +}; + +/// @brief Forward declaration to @ref MySqlConnection. +class MySqlConnection; + +/// @brief RAII object representing MySQL transaction. +/// +/// An instance of this class should be created in a scope where multiple +/// INSERT statements should be executed within a single transaction. The +/// transaction is started when the constructor of this class is invoked. +/// The transaction is ended when the @ref MySqlTransaction::commit is +/// explicitly called or when the instance of this class is destroyed. +/// The @ref MySqlTransaction::commit commits changes to the database +/// and the changes remain in the database when the instance of the +/// class is destroyed. If the class instance is destroyed before the +/// @ref MySqlTransaction::commit is called, the transaction is rolled +/// back. The rollback on destruction guarantees that partial data is +/// not stored in the database when there is an error during any +/// of the operations belonging to a transaction. +/// +/// The default MySQL backend configuration enables 'autocommit'. +/// Starting a transaction overrides 'autocommit' setting for this +/// particular transaction only. It does not affect the global 'autocommit' +/// setting for the database connection, i.e. all modifications to the +/// database which don't use transactions will still be auto committed. +class MySqlTransaction : public boost::noncopyable { +public: + + /// @brief Constructor. + /// + /// Starts transaction by making a "START TRANSACTION" query. + /// + /// @param conn MySQL connection to use for the transaction. This + /// connection will be later used to commit or rollback changes. + /// + /// @throw DbOperationError if "START TRANSACTION" query fails. + MySqlTransaction(MySqlConnection& conn); + + /// @brief Destructor. + /// + /// Rolls back the transaction if changes haven't been committed. + ~MySqlTransaction(); + + /// @brief Commits transaction. + void commit(); + +private: + + /// @brief Holds reference to the MySQL database connection. + MySqlConnection& conn_; + + /// @brief Boolean flag indicating if the transaction has been committed. + /// + /// This flag is used in the class destructor to assess if the + /// transaction should be rolled back. + bool committed_; +}; + + +/// @brief Common MySQL Connector Pool +/// +/// This class provides common operations for MySQL database connection +/// used by both MySqlLeaseMgr and MySqlHostDataSource. It manages connecting +/// to the database and preparing compiled statements. Its fields are +/// public, because they are used (both set and retrieved) in classes +/// that use instances of MySqlConnection. +class MySqlConnection : public db::DatabaseConnection { +public: + + /// @brief Function invoked to process fetched row. + typedef std::function<void(MySqlBindingCollection&)> ConsumeResultFun; + + /// @brief Constructor + /// + /// Initialize MySqlConnection object with parameters needed for connection. + /// + /// @param parameters Specify the connection details. + /// @param io_accessor The IOService accessor function. + /// @param callback The connection recovery callback. + MySqlConnection(const ParameterMap& parameters, + IOServiceAccessorPtr io_accessor = IOServiceAccessorPtr(), + DbCallback callback = DbCallback()) + : DatabaseConnection(parameters, callback), + io_service_accessor_(io_accessor), io_service_(), + transaction_ref_count_(0), tls_(false) { + } + + /// @brief Destructor + virtual ~MySqlConnection(); + + /// @brief Get the schema version. + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + /// + /// @return Version number as a pair of unsigned integers. "first" is the + /// major version number, "second" the minor number. + /// + /// @throw isc::db::DbOperationError An operation on the open database has + /// failed. + static std::pair<uint32_t, uint32_t> + getVersion(const ParameterMap& parameters); + + /// @brief Prepare Single Statement + /// + /// Creates a prepared statement from the text given and adds it to the + /// statements_ vector at the given index. + /// + /// @param index Index into the statements_ vector into which the text + /// should be placed. The vector must be big enough for the index + /// to be valid, else an exception will be thrown. + /// @param text Text of the SQL statement to be prepared. + /// + /// @throw isc::db::DbOperationError An operation on the open database has + /// failed. + /// @throw isc::InvalidParameter 'index' is not valid for the vector. + void prepareStatement(uint32_t index, const char* text); + + /// @brief Prepare statements + /// + /// Creates the prepared statements for all of the SQL statements used + /// by the MySQL backend. + /// + /// @param start_statement Pointer to the first statement in range of the + /// statements to be compiled. + /// @param end_statement Pointer to the statement marking end of the + /// range of statements to be compiled. This last statement is not compiled. + /// + /// @throw isc::db::DbOperationError An operation on the open database has + /// failed. + /// @throw isc::InvalidParameter 'index' is not valid for the vector. This + /// represents an internal error within the code. + void prepareStatements(const TaggedStatement* start_statement, + const TaggedStatement* end_statement); + + /// @brief Clears prepared statements and text statements. + void clearStatements(); + + /// @brief Open Database + /// + /// Opens the database using the information supplied in the parameters + /// passed to the constructor. + /// + /// @throw NoDatabaseName Mandatory database name not given + /// @throw DbOpenError Error opening the database + void openDatabase(); + + ///@{ + /// The following methods are used to convert between times and time + /// intervals stored in the Lease object, and the times stored in the + /// database. The reason for the difference is because in the DHCP server, + /// the cltt (Client Time Since Last Transmission) is the natural data; in + /// the lease file - which may be read by the user - it is the expiry time + /// of the lease. + + /// @brief Convert time_t value to database time. + /// + /// @param input_time A time_t value representing time. + /// @param output_time Reference to MYSQL_TIME object where converted time + /// will be put. + static + void convertToDatabaseTime(const time_t input_time, MYSQL_TIME& output_time); + + /// @brief Convert Lease Time to Database Times + /// + /// Within the DHCP servers, times are stored as client last transmit time + /// and valid lifetime. In the database, the information is stored as + /// valid lifetime and "expire" (time of expiry of the lease). They are + /// related by the equation: + /// + /// - expire = client last transmit time + valid lifetime + /// + /// This method converts from the times in the lease object into times + /// able to be added to the database. + /// + /// @param cltt Client last transmit time + /// @param valid_lifetime Valid lifetime + /// @param expire Reference to MYSQL_TIME object where the expiry time of + /// the lease will be put. + /// + /// @throw isc::BadValue if the sum of the calculated expiration time is + /// greater than the value of @c LeaseMgr::MAX_DB_TIME. + static + void convertToDatabaseTime(const time_t cltt, const uint32_t valid_lifetime, + MYSQL_TIME& expire); + + /// @brief Convert Database Time to Lease Times + /// + /// Within the database, time is stored as "expire" (time of expiry of the + /// lease) and valid lifetime. In the DHCP server, the information is + /// stored client last transmit time and valid lifetime. These are related + /// by the equation: + /// + /// - client last transmit time = expire - valid_lifetime + /// + /// This method converts from the times in the database into times + /// able to be inserted into the lease object. + /// + /// @param expire Reference to MYSQL_TIME object from where the expiry + /// time of the lease is taken. + /// @param valid_lifetime lifetime of the lease. + /// @param cltt Reference to location where client last transmit time + /// is put. + static + void convertFromDatabaseTime(const MYSQL_TIME& expire, + uint32_t valid_lifetime, time_t& cltt); + ///@} + + /// @brief Starts new transaction + /// + /// This function begins a new transaction by sending the START TRANSACTION + /// statement to the database. The transaction should be explicitly committed + /// by calling @c commit() or rolled back by calling @c rollback(). MySQL + /// does not support nested transactions, and it implicitly commits a + /// current transaction when the new transaction begins. Therefore, this + /// function checks if a transaction has already started and does not start + /// a new transaction. However, it increments a transaction reference counter + /// which is later decremented when @c commit() or @c rollback() is called. + /// When this mechanism is used properly, it guarantees that nested + /// transactions are not attempted, thus avoiding implicit (unexpected) + /// commits of the pending transaction. + void startTransaction(); + + /// @brief Checks if there is a transaction in progress. + /// + /// @return true if a transaction has been started, false otherwise. + bool isTransactionStarted() const; + + /// @brief Executes SELECT query using prepared statement. + /// + /// The statement index must point to an existing prepared statement + /// associated with the connection. The @c in_bindings size must match + /// the number of placeholders in the prepared statement. The size of + /// the @c out_bindings must match the number of selected columns. The + /// output bindings must be created and must encapsulate values of + /// the appropriate type, e.g. string, uint32_t etc. + /// + /// This method executes prepared statement using provided bindings and + /// calls @c process_result function for each returned row. The + /// @c process_result function is implemented by the caller and should + /// gather and store each returned row in an external data structure prior + /// to returning because the values in the @c out_bindings will be + /// overwritten by the values of the next returned row when this function + /// is called again. + /// + /// @tparam StatementIndex Type of the statement index enum. + /// + /// @param index Index of the query to be executed. + /// @param in_bindings Input bindings holding values to substitue placeholders + /// in the query. + /// @param [out] out_bindings Output bindings where retrieved data will be + /// stored. + /// @param process_result Pointer to the function to be invoked for each + /// retrieved row. This function consumes the retrieved data from the + /// output bindings. + template<typename StatementIndex> + void selectQuery(const StatementIndex& index, + const MySqlBindingCollection& in_bindings, + MySqlBindingCollection& out_bindings, + ConsumeResultFun process_result) { + checkUnusable(); + // Extract native input bindings. + std::vector<MYSQL_BIND> in_bind_vec; + for (MySqlBindingPtr in_binding : in_bindings) { + in_bind_vec.push_back(in_binding->getMySqlBinding()); + } + + int status = 0; + if (!in_bind_vec.empty()) { + // Bind parameters to the prepared statement. + status = mysql_stmt_bind_param(statements_[index], + in_bind_vec.empty() ? 0 : &in_bind_vec[0]); + checkError(status, index, "unable to bind parameters for select"); + } + + // Bind variables that will receive results as well. + std::vector<MYSQL_BIND> out_bind_vec; + for (MySqlBindingPtr out_binding : out_bindings) { + out_bind_vec.push_back(out_binding->getMySqlBinding()); + } + if (!out_bind_vec.empty()) { + status = mysql_stmt_bind_result(statements_[index], &out_bind_vec[0]); + checkError(status, index, "unable to bind result parameters for select"); + } + + // Execute query. + status = MysqlExecuteStatement(statements_[index]); + checkError(status, index, "unable to execute"); + + status = mysql_stmt_store_result(statements_[index]); + checkError(status, index, "unable to set up for storing all results"); + + // Fetch results. + MySqlFreeResult fetch_release(statements_[index]); + while ((status = mysql_stmt_fetch(statements_[index])) == + MLM_MYSQL_FETCH_SUCCESS) { + try { + // For each returned row call user function which should + // consume the row and copy the data to a safe place. + process_result(out_bindings); + + } catch (const std::exception& ex) { + // Rethrow the exception with a bit more data. + isc_throw(BadValue, ex.what() << ". Statement is <" << + text_statements_[index] << ">"); + } + } + + // How did the fetch end? + // If mysql_stmt_fetch return value is equal to 1 an error occurred. + if (status == MLM_MYSQL_FETCH_FAILURE) { + // Error - unable to fetch results + checkError(status, index, "unable to fetch results"); + + } else if (status == MYSQL_DATA_TRUNCATED) { + // Data truncated - throw an exception indicating what was at fault + isc_throw(DataTruncated, text_statements_[index] + << " returned truncated data"); + } + } + + /// @brief Executes INSERT prepared statement. + /// + /// The statement index must point to an existing prepared statement + /// associated with the connection. The @c in_bindings size must match + /// the number of placeholders in the prepared statement. + /// + /// This method executes prepared statement using provided bindings to + /// insert data into the database. + /// + /// @tparam StatementIndex Type of the statement index enum. + /// + /// @param index Index of the query to be executed. + /// @param in_bindings Input bindings holding values to substitue placeholders + /// in the query. + template<typename StatementIndex> + void insertQuery(const StatementIndex& index, + const MySqlBindingCollection& in_bindings) { + checkUnusable(); + std::vector<MYSQL_BIND> in_bind_vec; + for (MySqlBindingPtr in_binding : in_bindings) { + in_bind_vec.push_back(in_binding->getMySqlBinding()); + } + + // Bind the parameters to the statement + int status = mysql_stmt_bind_param(statements_[index], + in_bind_vec.empty() ? 0 : &in_bind_vec[0]); + checkError(status, index, "unable to bind parameters"); + + // Execute the statement + status = MysqlExecuteStatement(statements_[index]); + + if (status != 0) { + // Failure: check for the special case of duplicate entry. + if (mysql_errno(mysql_) == ER_DUP_ENTRY) { + isc_throw(DuplicateEntry, "Database duplicate entry error"); + } + // Failure: check for the special case of WHERE returning NULL. + if (mysql_errno(mysql_) == ER_BAD_NULL_ERROR) { + isc_throw(NullKeyError, "Database bad NULL error"); + } + checkError(status, index, "unable to execute"); + } + } + + /// @brief Executes UPDATE or DELETE prepared statement and returns + /// the number of affected rows. + /// + /// The statement index must point to an existing prepared statement + /// associated with the connection. The @c in_bindings size must match + /// the number of placeholders in the prepared statement. + /// + /// @tparam StatementIndex Type of the statement index enum. + /// + /// @param index Index of the query to be executed. + /// @param in_bindings Input bindings holding values to substitute placeholders + /// in the query. + /// + /// @return Number of affected rows. + template<typename StatementIndex> + uint64_t updateDeleteQuery(const StatementIndex& index, + const MySqlBindingCollection& in_bindings) { + checkUnusable(); + std::vector<MYSQL_BIND> in_bind_vec; + for (MySqlBindingPtr in_binding : in_bindings) { + in_bind_vec.push_back(in_binding->getMySqlBinding()); + } + + // Bind the parameters to the statement + int status = mysql_stmt_bind_param(statements_[index], + in_bind_vec.empty() ? 0 : &in_bind_vec[0]); + checkError(status, index, "unable to bind parameters"); + + // Execute the statement + status = MysqlExecuteStatement(statements_[index]); + + if (status != 0) { + // Failure: check for the special case of duplicate entry. + if ((mysql_errno(mysql_) == ER_DUP_ENTRY) +#ifdef ER_FOREIGN_DUPLICATE_KEY + || (mysql_errno(mysql_) == ER_FOREIGN_DUPLICATE_KEY) +#endif +#ifdef ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO + || (mysql_errno(mysql_) == ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO) +#endif +#ifdef ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO + || (mysql_errno(mysql_) == ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO) +#endif + ) { + isc_throw(DuplicateEntry, "Database duplicate entry error"); + } + checkError(status, index, "unable to execute"); + } + + // Let's return how many rows were affected. + return (static_cast<uint64_t>(mysql_stmt_affected_rows(statements_[index]))); + } + + /// @brief Commits current transaction + /// + /// Commits all pending database operations. On databases that don't + /// support transactions, this is a no-op. + /// + /// When this method is called for a nested transaction it decrements the + /// transaction reference counter incremented during the call to + /// @c startTransaction. + /// + /// @throw DbOperationError If the commit failed. + void commit(); + + /// @brief Rollbacks current transaction + /// + /// Rolls back all pending database operations. On databases that don't + /// support transactions, this is a no-op. + /// + /// When this method is called for a nested transaction it decrements the + /// transaction reference counter incremented during the call to + /// @c startTransaction. + /// + /// @throw DbOperationError If the rollback failed. + void rollback(); + + /// @brief Check Error and Throw Exception + /// + /// Virtually all MySQL functions return a status which, if non-zero, + /// indicates an error. This function centralizes the error checking + /// code. + /// + /// It is used to determine whether or not the function succeeded, and + /// in the event of failures, decide whether or not those failures are + /// recoverable. + /// + /// If the error is recoverable, the function will throw a DbOperationError. + /// If the error is deemed unrecoverable, such as a loss of connectivity + /// with the server, the function will call startRecoverDbConnection() which + /// will start the connection recovery. + /// + /// If the invocation returns true, this indicates the calling layer will + /// attempt recovery, and the function throws a DbOperationError to allow + /// the caller to error handle the failed db access attempt. + /// + /// @param status Status code: non-zero implies an error + /// @param index Index of statement that caused the error + /// @param what High-level description of the error + /// + /// @tparam Enumeration representing index of a statement to which an + /// error pertains. + /// + /// @throw isc::db::DbOperationError An operation on the open database has + /// failed. + template<typename StatementIndex> + void checkError(const int status, const StatementIndex& index, + const char* what) { + if (status != 0) { + switch(mysql_errno(mysql_)) { + // These are the ones we consider fatal. Remember this method is + // used to check errors of API calls made subsequent to successfully + // connecting. Errors occurring while attempting to connect are + // checked in the connection code. An alternative would be to call + // mysql_ping() - assuming autoreconnect is off. If that fails + // then we know connection is toast. + case CR_SERVER_GONE_ERROR: + case CR_SERVER_LOST: + case CR_OUT_OF_MEMORY: + case CR_CONNECTION_ERROR: { + DB_LOG_ERROR(db::MYSQL_FATAL_ERROR) + .arg(what) + .arg(text_statements_[static_cast<int>(index)]) + .arg(mysql_error(mysql_)) + .arg(mysql_errno(mysql_)); + + // Mark this connection as no longer usable. + markUnusable(); + + // Start the connection recovery. + startRecoverDbConnection(); + + // We still need to throw so caller can error out of the current + // processing. + isc_throw(db::DbConnectionUnusable, + "fatal database error or connectivity lost"); + } + default: + // Connection is ok, so it must be an SQL error + isc_throw(db::DbOperationError, what << " for <" + << text_statements_[static_cast<int>(index)] + << ">, reason: " + << mysql_error(mysql_) << " (error code " + << mysql_errno(mysql_) << ")"); + } + } + } + + /// @brief The recover connection + /// + /// This function starts the recover process of the connection. + /// + /// @note The recover function must be run on the IO Service thread. + void startRecoverDbConnection() { + if (callback_) { + if (!io_service_ && io_service_accessor_) { + io_service_ = (*io_service_accessor_)(); + io_service_accessor_.reset(); + } + + if (io_service_) { + io_service_->post(std::bind(callback_, reconnectCtl())); + } + } + } + + /// @brief Get the TLS flag. + /// + /// @return True if TLS was required, false otherwise. + bool getTls() const { + return (tls_); + } + + /// @brief Get the TLS cipher. + /// + /// This method is used to check if required TLS was setup. + std::string getTlsCipher() { + const char* cipher = mysql_get_ssl_cipher(mysql_); + return (cipher ? std::string(cipher) : ""); + } + + /// @brief Prepared statements + /// + /// This field is public, because it is used heavily from MySqlConnection + /// and will be from MySqlHostDataSource. + std::vector<MYSQL_STMT*> statements_; + + /// @brief Raw text of statements + /// + /// This field is public, because it is used heavily from MySqlConnection + /// and will be from MySqlHostDataSource. + std::vector<std::string> text_statements_; + + /// @brief MySQL connection handle + /// + /// This field is public, because it is used heavily from MySqlConnection + /// and will be from MySqlHostDataSource. + MySqlHolder mysql_; + + /// @brief Accessor function which returns the IOService that can be used to + /// recover the connection. + /// + /// This accessor is used to lazy retrieve the IOService when the connection + /// is lost. It is useful to retrieve it at a later time to support hook + /// libraries which create managers on load and set IOService later on by + /// using the dhcp4_srv_configured and dhcp6_srv_configured hooks. + IOServiceAccessorPtr io_service_accessor_; + + /// @brief IOService object, used for all ASIO operations. + isc::asiolink::IOServicePtr io_service_; + + /// @brief Reference counter for transactions. + /// + /// It precludes starting and committing nested transactions. MySQL + /// implicitly commits current transaction when new transaction is + /// started. We want to not start new transactions when one is already + /// in progress. + int transaction_ref_count_; + + /// @brief TLS flag (true when TLS was required, false otherwise). + bool tls_; +}; + +} // end of isc::db namespace +} // end of isc namespace + +#endif // MYSQL_CONNECTION_H diff --git a/src/lib/mysql/mysql_constants.h b/src/lib/mysql/mysql_constants.h new file mode 100644 index 0000000..f7f38b9 --- /dev/null +++ b/src/lib/mysql/mysql_constants.h @@ -0,0 +1,64 @@ +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MYSQL_CONSTANTS_H +#define MYSQL_CONSTANTS_H + +#include <mysql.h> + +namespace isc { +namespace db { + +/// @name MySQL constants. +/// +//@{ + +/// @brief my_bools type for vectors. +/// @note vector<bool> is specialized into a bitset, so vector<char> +/// must be used instead +typedef char my_bools; + +#ifdef HAVE_MYSQL_MY_BOOL +/// @brief MySQL false value. +const my_bool MLM_FALSE = 0; + +/// @brief MySQL true value. +const my_bool MLM_TRUE = 1; + +#else +/// @brief my_bool type in MySQL 8.x. +typedef bool my_bool; + +/// @brief MySQL false value. +const my_bool MLM_FALSE = false; + +/// @brief MySQL true value. +const my_bool MLM_TRUE = true; +#endif + +///@brief check for bool size +static_assert(sizeof(my_bool) == sizeof(char), "unsupported bool size"); + +/// @brief MySQL fetch success code. +const int MLM_MYSQL_FETCH_SUCCESS = 0; + +/// @brief MySQL fetch failure code. +const int MLM_MYSQL_FETCH_FAILURE = 0; + +//@} + +/// @name Current database schema version values. +//@{ +const uint32_t MYSQL_SCHEMA_VERSION_MAJOR = 14; +const uint32_t MYSQL_SCHEMA_VERSION_MINOR = 0; + +//@} + + +} // end of namespace isc::db +} // end of namespace isc + +#endif diff --git a/src/lib/mysql/tests/Makefile.am b/src/lib/mysql/tests/Makefile.am new file mode 100644 index 0000000..f11254e --- /dev/null +++ b/src/lib/mysql/tests/Makefile.am @@ -0,0 +1,40 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) $(MYSQL_CPPFLAGS) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +TESTS = +if HAVE_GTEST +TESTS += libmysql_unittests + +libmysql_unittests_SOURCES = mysql_binding_unittest.cc +libmysql_unittests_SOURCES += mysql_connection_unittest.cc +libmysql_unittests_SOURCES += run_unittests.cc + +libmysql_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +libmysql_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(MYSQL_LIBS) + +libmysql_unittests_LDADD = $(top_builddir)/src/lib/mysql/testutils/libmysqltest.la +libmysql_unittests_LDADD += $(top_builddir)/src/lib/mysql/libkea-mysql.la +libmysql_unittests_LDADD += $(top_builddir)/src/lib/database/libkea-database.la +libmysql_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libmysql_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libmysql_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +libmysql_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +libmysql_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libmysql_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(GTEST_LDADD) + +endif + +noinst_PROGRAMS = $(TESTS) diff --git a/src/lib/mysql/tests/Makefile.in b/src/lib/mysql/tests/Makefile.in new file mode 100644 index 0000000..d9ae160 --- /dev/null +++ b/src/lib/mysql/tests/Makefile.in @@ -0,0 +1,1006 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +TESTS = $(am__EXEEXT_1) +@HAVE_GTEST_TRUE@am__append_1 = libmysql_unittests +noinst_PROGRAMS = $(am__EXEEXT_2) +subdir = src/lib/mysql/tests +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_sysrepo.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@HAVE_GTEST_TRUE@am__EXEEXT_1 = libmysql_unittests$(EXEEXT) +am__EXEEXT_2 = $(am__EXEEXT_1) +PROGRAMS = $(noinst_PROGRAMS) +am__libmysql_unittests_SOURCES_DIST = mysql_binding_unittest.cc \ + mysql_connection_unittest.cc run_unittests.cc +@HAVE_GTEST_TRUE@am_libmysql_unittests_OBJECTS = libmysql_unittests-mysql_binding_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libmysql_unittests-mysql_connection_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libmysql_unittests-run_unittests.$(OBJEXT) +libmysql_unittests_OBJECTS = $(am_libmysql_unittests_OBJECTS) +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@libmysql_unittests_DEPENDENCIES = $(top_builddir)/src/lib/mysql/testutils/libmysqltest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/mysql/libkea-mysql.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/database/libkea-database.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libmysql_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libmysql_unittests_LDFLAGS) \ + $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = \ + ./$(DEPDIR)/libmysql_unittests-mysql_binding_unittest.Po \ + ./$(DEPDIR)/libmysql_unittests-mysql_connection_unittest.Po \ + ./$(DEPDIR)/libmysql_unittests-run_unittests.Po +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libmysql_unittests_SOURCES) +DIST_SOURCES = $(am__libmysql_unittests_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_SYSREPO = @HAVE_SYSREPO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + $(BOOST_INCLUDES) $(MYSQL_CPPFLAGS) +AM_CXXFLAGS = $(KEA_CXXFLAGS) +@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static +CLEANFILES = *.gcno *.gcda +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +@HAVE_GTEST_TRUE@libmysql_unittests_SOURCES = \ +@HAVE_GTEST_TRUE@ mysql_binding_unittest.cc \ +@HAVE_GTEST_TRUE@ mysql_connection_unittest.cc run_unittests.cc +@HAVE_GTEST_TRUE@libmysql_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +@HAVE_GTEST_TRUE@libmysql_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(MYSQL_LIBS) +@HAVE_GTEST_TRUE@libmysql_unittests_LDADD = $(top_builddir)/src/lib/mysql/testutils/libmysqltest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/mysql/libkea-mysql.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/database/libkea-database.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(BOOST_LIBS) \ +@HAVE_GTEST_TRUE@ $(GTEST_LDADD) +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/mysql/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/mysql/tests/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +libmysql_unittests$(EXEEXT): $(libmysql_unittests_OBJECTS) $(libmysql_unittests_DEPENDENCIES) $(EXTRA_libmysql_unittests_DEPENDENCIES) + @rm -f libmysql_unittests$(EXEEXT) + $(AM_V_CXXLD)$(libmysql_unittests_LINK) $(libmysql_unittests_OBJECTS) $(libmysql_unittests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmysql_unittests-mysql_binding_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmysql_unittests-mysql_connection_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmysql_unittests-run_unittests.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libmysql_unittests-mysql_binding_unittest.o: mysql_binding_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libmysql_unittests-mysql_binding_unittest.o -MD -MP -MF $(DEPDIR)/libmysql_unittests-mysql_binding_unittest.Tpo -c -o libmysql_unittests-mysql_binding_unittest.o `test -f 'mysql_binding_unittest.cc' || echo '$(srcdir)/'`mysql_binding_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmysql_unittests-mysql_binding_unittest.Tpo $(DEPDIR)/libmysql_unittests-mysql_binding_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='mysql_binding_unittest.cc' object='libmysql_unittests-mysql_binding_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libmysql_unittests-mysql_binding_unittest.o `test -f 'mysql_binding_unittest.cc' || echo '$(srcdir)/'`mysql_binding_unittest.cc + +libmysql_unittests-mysql_binding_unittest.obj: mysql_binding_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libmysql_unittests-mysql_binding_unittest.obj -MD -MP -MF $(DEPDIR)/libmysql_unittests-mysql_binding_unittest.Tpo -c -o libmysql_unittests-mysql_binding_unittest.obj `if test -f 'mysql_binding_unittest.cc'; then $(CYGPATH_W) 'mysql_binding_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/mysql_binding_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmysql_unittests-mysql_binding_unittest.Tpo $(DEPDIR)/libmysql_unittests-mysql_binding_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='mysql_binding_unittest.cc' object='libmysql_unittests-mysql_binding_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libmysql_unittests-mysql_binding_unittest.obj `if test -f 'mysql_binding_unittest.cc'; then $(CYGPATH_W) 'mysql_binding_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/mysql_binding_unittest.cc'; fi` + +libmysql_unittests-mysql_connection_unittest.o: mysql_connection_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libmysql_unittests-mysql_connection_unittest.o -MD -MP -MF $(DEPDIR)/libmysql_unittests-mysql_connection_unittest.Tpo -c -o libmysql_unittests-mysql_connection_unittest.o `test -f 'mysql_connection_unittest.cc' || echo '$(srcdir)/'`mysql_connection_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmysql_unittests-mysql_connection_unittest.Tpo $(DEPDIR)/libmysql_unittests-mysql_connection_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='mysql_connection_unittest.cc' object='libmysql_unittests-mysql_connection_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libmysql_unittests-mysql_connection_unittest.o `test -f 'mysql_connection_unittest.cc' || echo '$(srcdir)/'`mysql_connection_unittest.cc + +libmysql_unittests-mysql_connection_unittest.obj: mysql_connection_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libmysql_unittests-mysql_connection_unittest.obj -MD -MP -MF $(DEPDIR)/libmysql_unittests-mysql_connection_unittest.Tpo -c -o libmysql_unittests-mysql_connection_unittest.obj `if test -f 'mysql_connection_unittest.cc'; then $(CYGPATH_W) 'mysql_connection_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/mysql_connection_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmysql_unittests-mysql_connection_unittest.Tpo $(DEPDIR)/libmysql_unittests-mysql_connection_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='mysql_connection_unittest.cc' object='libmysql_unittests-mysql_connection_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libmysql_unittests-mysql_connection_unittest.obj `if test -f 'mysql_connection_unittest.cc'; then $(CYGPATH_W) 'mysql_connection_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/mysql_connection_unittest.cc'; fi` + +libmysql_unittests-run_unittests.o: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libmysql_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/libmysql_unittests-run_unittests.Tpo -c -o libmysql_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmysql_unittests-run_unittests.Tpo $(DEPDIR)/libmysql_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='libmysql_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libmysql_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc + +libmysql_unittests-run_unittests.obj: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libmysql_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/libmysql_unittests-run_unittests.Tpo -c -o libmysql_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmysql_unittests-run_unittests.Tpo $(DEPDIR)/libmysql_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='libmysql_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysql_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libmysql_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libmysql_unittests-mysql_binding_unittest.Po + -rm -f ./$(DEPDIR)/libmysql_unittests-mysql_connection_unittest.Po + -rm -f ./$(DEPDIR)/libmysql_unittests-run_unittests.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/libmysql_unittests-mysql_binding_unittest.Po + -rm -f ./$(DEPDIR)/libmysql_unittests-mysql_connection_unittest.Po + -rm -f ./$(DEPDIR)/libmysql_unittests-run_unittests.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-TESTS check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib/mysql/tests/mysql_binding_unittest.cc b/src/lib/mysql/tests/mysql_binding_unittest.cc new file mode 100644 index 0000000..331dfa8 --- /dev/null +++ b/src/lib/mysql/tests/mysql_binding_unittest.cc @@ -0,0 +1,249 @@ +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <asiolink/io_address.h> +#include <exceptions/exceptions.h> +#include <mysql/mysql_binding.h> +#include <util/optional.h> +#include <boost/date_time/gregorian/gregorian.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <gtest/gtest.h> + +using namespace isc; +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::db; +using namespace isc::util; + +namespace { + +// This test verifies that default string is returned if binding is null. +TEST(MySqlBindingTest, defaultString) { + auto binding = MySqlBinding::createNull(); + EXPECT_EQ("foo", binding->getStringOrDefault("foo")); + + binding = MySqlBinding::createString("bar"); + ASSERT_FALSE(binding->amNull()); + EXPECT_EQ("bar", binding->getStringOrDefault("foo")); +} + +// This test verifies that null binding is created for unspecified string +// and the string binding is created for a specified string. +TEST(MySqlBindingTest, conditionalString) { + auto binding = MySqlBinding::condCreateString(Optional<std::string>()); + EXPECT_TRUE(binding->amNull()); + + binding = MySqlBinding::condCreateString("foo"); + ASSERT_FALSE(binding->amNull()); + EXPECT_EQ("foo", binding->getString()); +} + +// This test verifies that empty string is stored in the database. +TEST(MySqlBindingTest, emptyString) { + auto binding = MySqlBinding::condCreateString(Optional<std::string>("")); + ASSERT_FALSE(binding->amNull()); + EXPECT_TRUE(binding->getString().empty()); +} + +// This test verifies that an error is thrown upon an attempt to use +// invalid accessor for a string binding. +TEST(MySqlBindingTest, stringTypeMismatch) { + auto binding = MySqlBinding::createString("foo"); + EXPECT_NO_THROW(static_cast<void>(binding->getString())); + + EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation); +} + +// This test verifies that null JSON is returned if the string binding +// is null, JSON value is returned when string value is valid JSON and +// that exception is thrown if the string is not a valid JSON. +TEST(MySqlBindingTest, getJSON) { + auto binding = MySqlBinding::createNull(); + EXPECT_FALSE(binding->getJSON()); + + binding = MySqlBinding::createString("{ \"foo\": \"bar\" }"); + auto json = binding->getJSON(); + ASSERT_TRUE(json); + ASSERT_EQ(Element::map, json->getType()); + auto foo = json->get("foo"); + ASSERT_TRUE(foo); + ASSERT_EQ(Element::string, foo->getType()); + EXPECT_EQ("bar", foo->stringValue()); +} + +// This test verifies that default blob is returned if binding is null. +TEST(MySqlBindingTest, defaultBlob) { + std::vector<uint8_t> blob(10, 1); + std::vector<uint8_t> default_blob(10, 5); + auto binding = MySqlBinding::createNull(); + EXPECT_EQ(default_blob, binding->getBlobOrDefault(default_blob)); + + binding = MySqlBinding::createBlob(blob.begin(), blob.end()); + EXPECT_EQ(blob, binding->getBlobOrDefault(default_blob)); +} + +// This test verifies that an error is thrown upon an attempt to use +// invalid accessor for a blob binding. +TEST(MySqlBindingTest, blobTypeMismatch) { + std::vector<uint8_t> blob(10, 1); + auto binding = MySqlBinding::createBlob(blob.begin(), blob.end()); + EXPECT_NO_THROW(static_cast<void>(binding->getBlob())); + + EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation); +} + +// This test verifies that default number is returned if binding is null. +TEST(MySqlBindingTest, defaultInteger) { + auto binding = MySqlBinding::createNull(); + ASSERT_TRUE(binding->amNull()); + EXPECT_EQ(123, binding->getIntegerOrDefault<uint32_t>(123)); + + binding = MySqlBinding::createInteger<uint32_t>(1024); + ASSERT_FALSE(binding->amNull()); + EXPECT_EQ(1024, binding->getIntegerOrDefault<uint32_t>(123)); +} + +// This test verifies that null binding is created for unspecified number +// and the integer binding is created for a specified number. +TEST(MySqlBindingTest, conditionalInteger) { + auto binding = MySqlBinding::condCreateInteger<uint16_t>(Optional<uint16_t>()); + EXPECT_TRUE(binding->amNull()); + + binding = MySqlBinding::condCreateInteger<uint16_t>(1); + ASSERT_FALSE(binding->amNull()); + EXPECT_EQ(1, binding->getInteger<uint16_t>()); +} + +// This test verifies that an error is thrown upon an attempt to use +// invalid accessor for an integer binding. +TEST(MySqlBindingTest, integerTypeMismatch) { + auto binding = MySqlBinding::createInteger<uint32_t>(123); + EXPECT_NO_THROW(static_cast<void>(binding->getInteger<uint32_t>())); + + EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getInteger<uint8_t>()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation); +} + +// This test verifies that null binding is created for unspecified floating +// point value and the float binding is created for the specified value. +TEST(MySqlBindingTest, conditionalFloat) { + auto binding = MySqlBinding::condCreateFloat(Optional<float>()); + EXPECT_TRUE(binding->amNull()); + + binding = MySqlBinding::condCreateFloat<float>(1.567f); + ASSERT_FALSE(binding->amNull()); + EXPECT_EQ(1.567f, binding->getFloat()); +} + +// This test verifies that an error is thrown upon an attempt to use +// invalid accessor for a float binding. +TEST(MySqlBindingTest, floatTypeMismatch) { + auto binding = MySqlBinding::createFloat(123.123f); + EXPECT_NO_THROW(static_cast<void>(binding->getFloat())); + + EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getInteger<uint8_t>()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getInteger<uint32_t>()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation); +} + +// This test verifies that null binding is created for unspecified boolean +// value and the uint8_t binding is created for a specified boolean +// value. +TEST(MySqlBindingTest, conditionalBoolean) { + auto binding = MySqlBinding::condCreateBool(Optional<bool>()); + EXPECT_TRUE(binding->amNull()); + + binding = MySqlBinding::condCreateBool(false); + ASSERT_FALSE(binding->amNull()); + EXPECT_FALSE(binding->getBool()); + + binding = MySqlBinding::condCreateBool(true); + ASSERT_FALSE(binding->amNull()); + EXPECT_TRUE(binding->getBool()); +} + +// This test verifies that an error is thrown upon an attempt to use +// invalid accessor for a float binding. +TEST(MySqlBindingTest, booleanTypeMismatch) { + auto binding = MySqlBinding::createBool(false); + EXPECT_NO_THROW(static_cast<void>(binding->getBool())); + EXPECT_NO_THROW(static_cast<void>(binding->getInteger<uint8_t>())); + + EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getInteger<uint32_t>()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation); + EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation); +} + +// This test verifies that null binding is created for unspecified address +// and the uint32_t binding is created for the specified address. +TEST(MySqlBindingTest, conditionalIPv4Address) { + auto binding = MySqlBinding::condCreateIPv4Address(Optional<IOAddress>()); + EXPECT_TRUE(binding->amNull()); + + binding = MySqlBinding::condCreateIPv4Address(IOAddress("192.0.2.1")); + ASSERT_FALSE(binding->amNull()); + EXPECT_EQ(0xC0000201, binding->getInteger<uint32_t>()); + + EXPECT_THROW(MySqlBinding::condCreateIPv4Address(IOAddress("2001:db8:1::1")), + isc::BadValue); +} + +// This test verifies that default timestamp is returned if binding is null. +TEST(MySqlBindingTest, defaultTimestamp) { + boost::posix_time::ptime current_time = boost::posix_time::second_clock::local_time(); + boost::posix_time::ptime past_time = current_time - boost::posix_time::hours(1); + + auto binding = MySqlBinding::createNull(); + EXPECT_TRUE(past_time == binding->getTimestampOrDefault(past_time)); + + binding = MySqlBinding::createTimestamp(current_time); + EXPECT_TRUE(current_time == binding->getTimestampOrDefault(past_time)); +} + +// This test verifies that the binding preserves fractional seconds in +// millisecond precision. +/// @todo This test is disabled until we decide that the minimum +/// supported MySQL version has a fractional seconds precision. +TEST(MySqlBindingTest, DISABLED_millisecondTimestampPrecision) { + // Set timestamp of 2019-01-28 01:12:10.123 + + // Fractional part depends on the clock resolution. + long fractional = 123*(boost::posix_time::time_duration::ticks_per_second()/1000); + boost::posix_time::ptime + test_time(boost::gregorian::date(2019, boost::gregorian::Jan, 28), + boost::posix_time::time_duration(1, 12, 10, fractional)); + + auto binding = MySqlBinding::createTimestamp(test_time); + + boost::posix_time::ptime returned_test_time = binding->getTimestamp(); + + EXPECT_EQ(returned_test_time, test_time); +} + +} + diff --git a/src/lib/mysql/tests/mysql_connection_unittest.cc b/src/lib/mysql/tests/mysql_connection_unittest.cc new file mode 100644 index 0000000..19d1a6a --- /dev/null +++ b/src/lib/mysql/tests/mysql_connection_unittest.cc @@ -0,0 +1,789 @@ +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <exceptions/exceptions.h> +#include <mysql/mysql_connection.h> +#include <mysql/testutils/mysql_schema.h> +#include <testutils/gtest_utils.h> + +#include <array> + +#include <gtest/gtest.h> + +#include <boost/date_time/posix_time/posix_time.hpp> + +using namespace isc::db; +using namespace isc::db::test; + +namespace { + +/// @brief RAII wrapper over MYSQL_RES obtained from MySQL library functions like +/// mysql_use_result(). +struct MySqlResult { + MySqlResult(MYSQL_RES* result) : result_(result) { + } + + ~MySqlResult() { + mysql_free_result(result_); + } + + MYSQL_RES* const result_; +}; + +/// @brief Test fixture class for @c MySqlConnection class. +class MySqlConnectionTest : public ::testing::Test { +public: + + /// @brief Indexes of prepared statements used within the tests. + enum StatementIndex { + GET_BY_INT_VALUE, + DELETE_BY_INT_VALUE, + INSERT_VALUE, + NUM_STATEMENTS + }; + + /// @brief Array of tagged MySQL statements. + typedef std::array<TaggedStatement, NUM_STATEMENTS> TaggedStatementArray; + + /// @brief Prepared MySQL statements used in the tests. + TaggedStatementArray tagged_statements = {{ + { GET_BY_INT_VALUE, + "SELECT tinyint_value, int_value, bigint_value, string_value," + " blob_value, timestamp_value" + " FROM mysql_connection_test WHERE int_value = ?" }, + + { DELETE_BY_INT_VALUE, + "DELETE FROM mysql_connection_test WHERE int_value = ?" }, + + { INSERT_VALUE, + "INSERT INTO mysql_connection_test (tinyint_value, int_value," + "bigint_value, string_value, blob_value, timestamp_value)" + " VALUES (?, ?, ?, ?, ?, ?)" } + }}; + + /// @brief Constructor. + /// + /// Re-creates database schema, opens new database connection and creates + /// prepared statements used in tests. Created schema contains a test + /// table @c mysql_connection_test which includes 6 columns of various + /// types. + MySqlConnectionTest(bool const primary_key = false) + : conn_(DatabaseConnection::parse(validMySQLConnectionString())) { + + try { + // Open new connection. + conn_.openDatabase(); + + // Create mysql_connection_test table. + createTestTable(primary_key); + + // In Percona XtraDB cluster, you can't do much on tables with + // primary keys. So far the connection and the table creation have + // been tested. Continue only if: + // * we are in primary key mode + // * a MySQL database other than Percona is running + // * Percona's pxc_strict_mode is set to "DISABLED" or "PERMISSIVE" + // The last two checks are done with inverse logic against the two + // modes that restrict this: "ENFORCING" and "MASTER". This check is + // to be paired inside the tests without a primary key to disable + // those tests. + if (!primary_key && + (showPxcStrictMode() == "ENFORCING" || showPxcStrictMode() == "MASTER")) { + return; + } + + // Created prepared statements for basic queries to test table. + conn_.prepareStatements(tagged_statements.begin(), + tagged_statements.end()); + } catch (...) { + std::cerr << "*** ERROR: unable to open database. The test\n" + "*** environment is broken and must be fixed before\n" + "*** the MySQL tests will run correctly.\n" + "*** The reason for the problem is described in the\n" + "*** accompanying exception output.\n"; + throw; + } + } + + /// @brief Destructor + /// + /// Removes test table from the database. + virtual ~MySqlConnectionTest() { + if (conn_.isTransactionStarted()) { + conn_.rollback(); + } + dropTestTable(); + } + + /// @brief Creates test table @c mysql_connection_test. + /// + /// The new table contains 6 columns of various data types. All of + /// the columns accept null values. + void createTestTable(bool const primary_key = false) { + /// @todo TIMESTAMP value lacks sub second precision because + /// it is supported since MySQL 5.6.4, which is still not a + /// default version on some OSes. When the subsecond precision + /// is available on all OSes that Kea supports, the timestamp + /// column should be turned to TIMESTAMP(6). Until then, it + /// must remain TIMESTAMP. + runQuery("CREATE TABLE IF NOT EXISTS mysql_connection_test (" + "tinyint_value TINYINT " + + std::string(primary_key ? "PRIMARY KEY NOT NULL," : "NULL,") + + "int_value INT NULL," + "bigint_value BIGINT NULL," + "string_value TEXT NULL," + "blob_value BLOB NULL," + "timestamp_value TIMESTAMP NULL" + ")"); + } + + /// @brief Drops test table. + void dropTestTable() { + runQuery("DROP TABLE IF EXISTS mysql_connection_test"); + } + + /// @brief Runs MySQL query on the opened connection. + /// + /// @param sql Query in the textual form. + void runQuery(const std::string& sql) { + MYSQL_STMT *stmt = mysql_stmt_init(conn_.mysql_); + if (stmt == NULL) { + isc_throw(DbOperationError, "unable to allocate MySQL prepared " + "statement structure, reason: " << mysql_error(conn_.mysql_)); + } + + int status = mysql_stmt_prepare(stmt, sql.c_str(), sql.length()); + if (status != 0) { + isc_throw(DbOperationError, "unable to prepare MySQL statement <" + << sql << ">, reason: " << mysql_errno(conn_.mysql_)); + } + + // Execute the prepared statement. + if (MysqlExecuteStatement(stmt) != 0) { + isc_throw(DbOperationError, "cannot execute MySQL query <" + << sql << ">, reason: " << mysql_errno(conn_.mysql_)); + } + + // Discard the statement and its resources + mysql_stmt_close(stmt); + } + + + /// @brief Tests inserting and retrieving data from the database. + /// + /// In this test data carried in the bindings is inserted into the database. + /// Then this data is retrieved from the database and compared with the + /// orginal. + /// + /// @param in_bindings Collection of bindings encapsulating the data to + /// be inserted into the database and then retrieved. + void testInsertSelect(const MySqlBindingCollection& in_bindings) { + // Expecting 6 bindings because we have 6 columns in our table. + ASSERT_EQ(6, in_bindings.size()); + // We are going to select by int_value so this value must not be null. + ASSERT_FALSE(in_bindings[1]->amNull()); + + // Store data in the database. + ASSERT_NO_THROW_LOG(conn_.insertQuery(MySqlConnectionTest::INSERT_VALUE, + in_bindings)); + + // Create input binding for select query. + MySqlBindingCollection bindings = + { MySqlBinding::createInteger<uint32_t>(in_bindings[1]->getInteger<uint32_t>()) }; + + // Also, create output (placeholder) bindings for receiving data. + MySqlBindingCollection out_bindings = { + MySqlBinding::createInteger<uint8_t>(), + MySqlBinding::createInteger<uint32_t>(), + MySqlBinding::createInteger<int64_t>(), + MySqlBinding::createString(512), + MySqlBinding::createBlob(512), + MySqlBinding::createTimestamp() + }; + + // Execute select statement. We expect one row to be returned. For this + // returned row the lambda provided as 4th argument should be executed. + ASSERT_NO_THROW_LOG(conn_.selectQuery(MySqlConnectionTest::GET_BY_INT_VALUE, + bindings, out_bindings, + [&](MySqlBindingCollection& out_bindings) { + + // Compare received data with input data assuming they are both non-null. + + if (!out_bindings[0]->amNull() && !in_bindings[0]->amNull()) { + EXPECT_EQ(static_cast<int>(in_bindings[0]->getInteger<uint8_t>()), + static_cast<int>(out_bindings[0]->getInteger<uint8_t>())); + } + + if (!out_bindings[1]->amNull() && !in_bindings[1]->amNull()) { + EXPECT_EQ(in_bindings[1]->getInteger<uint32_t>(), + out_bindings[1]->getInteger<uint32_t>()); + } + + if (!out_bindings[2]->amNull() && !in_bindings[2]->amNull()) { + EXPECT_EQ(in_bindings[2]->getInteger<int64_t>(), + out_bindings[2]->getInteger<int64_t>()); + } + + if (!out_bindings[3]->amNull() && !in_bindings[3]->amNull()) { + EXPECT_EQ(in_bindings[3]->getString(), + out_bindings[3]->getString()); + } + + if (!out_bindings[4]->amNull() && !in_bindings[4]->amNull()) { + EXPECT_EQ(in_bindings[4]->getBlob(), + out_bindings[4]->getBlob()); + } + + if (!out_bindings[5]->amNull() && !in_bindings[5]->amNull()) { + EXPECT_TRUE(in_bindings[5]->getTimestamp() == + out_bindings[5]->getTimestamp()); + } + })); + + // Make sure that null values were returned for columns for which null + // was set. + ASSERT_EQ(in_bindings.size(), out_bindings.size()); + for (auto i = 0; i < in_bindings.size(); ++i) { + EXPECT_EQ(in_bindings[i]->amNull(), out_bindings[i]->amNull()) + << "null value test failed for binding #" << i; + } + } + + /// @brief Run a raw, unprepared statement and return the result. + /// + /// This is useful when running statements that can't be parameterized with a + /// question mark in place of a bound variable e.g. "SHOW GLOBAL VARIABLES" + /// and thus cannot be prepared beforehand. All the results are string, the + /// output should be the same as that which one would see in a mysql command + /// line client. + /// + /// @param statement the statement in string form + /// @throw DbOperationError if the statement could not be run + /// @return the list of rows, each row consisting of a list of values for + /// each column + std::vector<std::vector<std::string>> + rawStatement(std::string const& statement) const { + // Execute a SQL statement. + if (mysql_query(conn_.mysql_, statement.c_str())) { + isc_throw(DbOperationError, + statement << ": " << mysql_error(conn_.mysql_)); + } + + // Get a result set. + MySqlResult result(mysql_use_result(conn_.mysql_)); + + // Fetch a result set. + std::vector<std::vector<std::string>> output; + size_t r(0); + MYSQL_ROW row; + size_t const column_count(mysql_num_fields(result.result_)); + while ((row = mysql_fetch_row(result.result_)) != NULL) { + output.push_back(std::vector<std::string>()); + for (size_t i = 0; i < column_count; ++i) { + output[r].push_back(row[i]); + } + ++r; + } + + return output; + } + + /// @brief Get pxc_strict_mode global variable from the database. + /// For Percona, they can be: DISABLED, PERMISSIVE, ENFORCING, MASTER. + std::string showPxcStrictMode() { + // Store in a static variable so this only runs once per gtest binary + // invocation. + static std::string const pxc_strict_mode([&]() -> std::string { + // Execute select statement. We expect one row to be returned. For + // this returned row the lambda provided as 4th argument should be + // executed. + std::vector<std::vector<std::string>> const result( + rawStatement("SHOW GLOBAL VARIABLES LIKE 'pxc_strict_mode'")); + if (result.size() < 1 || result[0].size() < 2) { + // Not Percona + return ""; + } + + return result[0][1]; + }()); + return pxc_strict_mode; + } + + /// *** Test definitions start here. Tests are invoked *** + /// *** multiple times further below in different test fixtures. *** + + /// @brief Test that non-null values of various types can be inserted and + /// retrieved from the dataabse. + void select() { + std::string blob = "myblob"; + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger<uint8_t>(123), + MySqlBinding::createInteger<uint32_t>(1024), + MySqlBinding::createInteger<int64_t>(-4096), + MySqlBinding::createString("shellfish"), + MySqlBinding::createBlob(blob.begin(), blob.end()), + /// @todo Change it to microsec_clock once we transition to + /// subsecond precision. + MySqlBinding::createTimestamp( + boost::posix_time::second_clock::local_time()), + }; + + testInsertSelect(in_bindings); + } + + /// @brief Test that null value can be inserted to a column having numeric + /// type and retrieved. + void selectNullInteger() { + std::string blob = "myblob"; + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger<uint8_t>(123), + MySqlBinding::createInteger<uint32_t>(1024), + MySqlBinding::createNull(), + MySqlBinding::createString("shellfish"), + MySqlBinding::createBlob(blob.begin(), blob.end()), + /// @todo Change it to microsec_clock once we transition to + /// subsecond precision. + MySqlBinding::createTimestamp( + boost::posix_time::second_clock::local_time()), + }; + + testInsertSelect(in_bindings); + } + + /// @brief Test that null value can be inserted to a column having string + /// type and retrieved. + void selectNullString() { + std::string blob = "myblob"; + + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger<uint8_t>(123), + MySqlBinding::createInteger<uint32_t>(1024), + MySqlBinding::createInteger<int64_t>(-4096), + MySqlBinding::createNull(), + MySqlBinding::createBlob(blob.begin(), blob.end()), + /// @todo Change it to microsec_clock once we transition to + /// subsecond precision. + MySqlBinding::createTimestamp( + boost::posix_time::second_clock::local_time()), + }; + + testInsertSelect(in_bindings); + } + + /// @brief Test that null value can be inserted to a column having blob type + /// and retrieved. + void selectNullBlob() { + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger<uint8_t>(123), + MySqlBinding::createInteger<uint32_t>(1024), + MySqlBinding::createInteger<int64_t>(-4096), + MySqlBinding::createString("shellfish"), + MySqlBinding::createNull(), + /// @todo Change it to microsec_clock once we transition to + /// subsecond precision. + MySqlBinding::createTimestamp( + boost::posix_time::second_clock::local_time()), + }; + + testInsertSelect(in_bindings); + } + + /// @brief Test that null value can be inserted to a column having timestamp + /// type and retrieved. + void selectNullTimestamp() { + std::string blob = "myblob"; + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger<uint8_t>(123), + MySqlBinding::createInteger<uint32_t>(1024), + MySqlBinding::createInteger<int64_t>(-4096), + MySqlBinding::createString("shellfish"), + MySqlBinding::createBlob(blob.begin(), blob.end()), + MySqlBinding::createNull(), + }; + + testInsertSelect(in_bindings); + } + + /// @brief Test that empty string and empty blob can be inserted to a + /// database. + void selectEmptyStringBlob() { + std::string blob = ""; + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger<uint8_t>(123), + MySqlBinding::createInteger<uint32_t>(1024), + MySqlBinding::createInteger<int64_t>(-4096), + MySqlBinding::createString(""), + MySqlBinding::createBlob(blob.begin(), blob.end()), + /// @todo Change it to microsec_clock once we transition to + /// subsecond precision. + MySqlBinding::createTimestamp( + boost::posix_time::second_clock::local_time()), + }; + + testInsertSelect(in_bindings); + } + + /// @brief Test that a row can be deleted from the database. + void deleteByValue() { + // Insert a row with numeric values. + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger<uint8_t>(123), + MySqlBinding::createInteger<uint32_t>(1024), + MySqlBinding::createInteger<int64_t>(-4096), + MySqlBinding::createNull(), + MySqlBinding::createNull(), + MySqlBinding::createNull(), + }; + + ASSERT_NO_THROW_LOG( + conn_.insertQuery(MySqlConnectionTest::INSERT_VALUE, in_bindings)); + + // This variable will be checked to see if the row has been deleted + // from the database. + bool deleted = false; + + // Execute delete query but use int_value of non existing row. + // The row should not be deleted. + in_bindings = {MySqlBinding::createInteger<uint32_t>(1)}; + ASSERT_NO_THROW_LOG( + deleted = conn_.updateDeleteQuery( + MySqlConnectionTest::DELETE_BY_INT_VALUE, in_bindings)); + ASSERT_FALSE(deleted); + + // This time use the correct value. + in_bindings = {MySqlBinding::createInteger<uint32_t>(1024)}; + ASSERT_NO_THROW_LOG( + deleted = conn_.updateDeleteQuery( + MySqlConnectionTest::DELETE_BY_INT_VALUE, in_bindings)); + // The row should have been deleted. + ASSERT_TRUE(deleted); + + // Let's confirm that it has been deleted by issuing a select query. + MySqlBindingCollection out_bindings = { + MySqlBinding::createInteger<uint8_t>(), + MySqlBinding::createInteger<uint32_t>(), + MySqlBinding::createInteger<int64_t>(), + MySqlBinding::createString(512), + MySqlBinding::createBlob(512), + MySqlBinding::createTimestamp(), + }; + + ASSERT_NO_THROW_LOG(conn_.selectQuery(MySqlConnectionTest::GET_BY_INT_VALUE, + in_bindings, out_bindings, + [&deleted](MySqlBindingCollection&) { + // This will be executed if the + // row is returned as a result of + // select query. We expect that + // this is not executed. + deleted = false; + })); + // Make sure that select query returned nothing. + EXPECT_TRUE(deleted); + } + + /// @brief Test MySQL connection. + MySqlConnection conn_; +}; + +struct MySqlConnectionWithPrimaryKeyTest : MySqlConnectionTest { + MySqlConnectionWithPrimaryKeyTest() + : MySqlConnectionTest(/* primary_key = */ true) { + } +}; + +TEST_F(MySqlConnectionTest, select) { + if (showPxcStrictMode() == "ENFORCING" || showPxcStrictMode() == "MASTER") { + return; + } + select(); +} + +TEST_F(MySqlConnectionTest, selectNullInteger) { + if (showPxcStrictMode() == "ENFORCING" || showPxcStrictMode() == "MASTER") { + return; + } + selectNullInteger(); +} + +TEST_F(MySqlConnectionTest, selectNullString) { + if (showPxcStrictMode() == "ENFORCING" || showPxcStrictMode() == "MASTER") { + return; + } + selectNullString(); +} + +TEST_F(MySqlConnectionTest, selectNullBlob) { + if (showPxcStrictMode() == "ENFORCING" || showPxcStrictMode() == "MASTER") { + return; + } + selectNullBlob(); +} + +TEST_F(MySqlConnectionTest, selectNullTimestamp) { + if (showPxcStrictMode() == "ENFORCING" || showPxcStrictMode() == "MASTER") { + return; + } + selectNullTimestamp(); +} + +TEST_F(MySqlConnectionTest, selectEmptyStringBlob) { + if (showPxcStrictMode() == "ENFORCING" || showPxcStrictMode() == "MASTER") { + return; + } + selectEmptyStringBlob(); +} + +TEST_F(MySqlConnectionTest, deleteByValue) { + if (showPxcStrictMode() == "ENFORCING" || showPxcStrictMode() == "MASTER") { + return; + } + deleteByValue(); +} + +TEST_F(MySqlConnectionTest, transactions) { + // Two inserts within a transaction and successful commit. + conn_.startTransaction(); + EXPECT_TRUE(conn_.isTransactionStarted()); + runQuery("INSERT INTO mysql_connection_test (tinyint_value) VALUES (1)"); + runQuery("INSERT INTO mysql_connection_test (tinyint_value) VALUES (2)"); + conn_.commit(); + EXPECT_FALSE(conn_.isTransactionStarted()); + auto result = rawStatement("SELECT COUNT(*) FROM mysql_connection_test"); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(1, result[0].size()); + EXPECT_EQ("2", result[0][0]); + + // Add third row but roll back the transaction. We should still have + // two rows in the table. + conn_.startTransaction(); + EXPECT_TRUE(conn_.isTransactionStarted()); + runQuery("INSERT INTO mysql_connection_test (tinyint_value) VALUES (5)"); + conn_.rollback(); + EXPECT_FALSE(conn_.isTransactionStarted()); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(1, result[0].size()); + EXPECT_EQ("2", result[0][0]); + + // Nested transaction. The inner transaction should be ignored and the outer + // transaction rolled back. We should still have two rows in the database. + conn_.startTransaction(); + EXPECT_TRUE(conn_.isTransactionStarted()); + runQuery("INSERT INTO mysql_connection_test (tinyint_value) VALUES (3)"); + conn_.startTransaction(); + EXPECT_TRUE(conn_.isTransactionStarted()); + runQuery("INSERT INTO mysql_connection_test (tinyint_value) VALUES (4)"); + conn_.commit(); + EXPECT_TRUE(conn_.isTransactionStarted()); + conn_.rollback(); + EXPECT_FALSE(conn_.isTransactionStarted()); + result = rawStatement("SELECT COUNT(*) FROM mysql_connection_test"); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(1, result[0].size()); + EXPECT_EQ("2", result[0][0]); + + // Nested transaction. The inner transaction is rolled back but this should + // be ignored because nested transactions are not supported. We should + // have two new rows. + conn_.startTransaction(); + EXPECT_TRUE(conn_.isTransactionStarted()); + runQuery("INSERT INTO mysql_connection_test (tinyint_value) VALUES (5)"); + conn_.startTransaction(); + EXPECT_TRUE(conn_.isTransactionStarted()); + runQuery("INSERT INTO mysql_connection_test (tinyint_value) VALUES (6)"); + conn_.rollback(); + EXPECT_TRUE(conn_.isTransactionStarted()); + conn_.commit(); + EXPECT_FALSE(conn_.isTransactionStarted()); + result = rawStatement("SELECT COUNT(*) FROM mysql_connection_test"); + ASSERT_EQ(1, result.size()); + ASSERT_EQ(1, result[0].size()); + EXPECT_EQ("4", result[0][0]); + + // Committing or rolling back a not started transaction is a coding error. + EXPECT_THROW(conn_.commit(), isc::Unexpected); + EXPECT_THROW(conn_.rollback(), isc::Unexpected); +} + +TEST_F(MySqlConnectionWithPrimaryKeyTest, select) { + select(); +} + +TEST_F(MySqlConnectionWithPrimaryKeyTest, selectNullInteger) { + selectNullInteger(); +} + +TEST_F(MySqlConnectionWithPrimaryKeyTest, selectNullString) { + selectNullString(); +} + +TEST_F(MySqlConnectionWithPrimaryKeyTest, selectNullBlob) { + selectNullBlob(); +} + +TEST_F(MySqlConnectionWithPrimaryKeyTest, selectNullTimestamp) { + selectNullTimestamp(); +} + +TEST_F(MySqlConnectionWithPrimaryKeyTest, selectEmptyStringBlob) { + selectEmptyStringBlob(); +} + +TEST_F(MySqlConnectionWithPrimaryKeyTest, deleteByValue) { + deleteByValue(); +} + +/// @brief Test fixture class for @c MySqlConnection class methods. +class MySqlSchemaTest : public ::testing::Test { +public: + /// @brief Constructor. + MySqlSchemaTest() { + // Ensure we have the proper schema. + createMySQLSchema(); + } + + /// @brief Destructor. + virtual ~MySqlSchemaTest() { + destroyMySQLSchema(); + } +}; + +/// @brief Check that getVersion() returns the expected version. +TEST_F(MySqlSchemaTest, checkVersion) { + // Check version + auto parameters = DatabaseConnection::parse(validMySQLConnectionString()); + std::pair<uint32_t, uint32_t> version; + ASSERT_NO_THROW_LOG(version = MySqlConnection::getVersion(parameters)); + EXPECT_EQ(MYSQL_SCHEMA_VERSION_MAJOR, version.first); + EXPECT_EQ(MYSQL_SCHEMA_VERSION_MINOR, version.second); +} + +/// @brief Test fixture class for secure connection. +class MySqlSecureConnectionTest : public ::testing::Test { +public: + + /// @brief Check if SSL/TLS support is available and configured. + bool hasMySQLTls() { + std::string tls = getMySQLTlsEnv(); + if (tls.empty()) { + tls = getMySQLTlsServer(); + } + return (tls == "YES"); + } +}; + +/// @brief Check that we can get the MySQL support status. +TEST_F(MySqlSecureConnectionTest, getMySQLTls) { + std::string env; + try { + env = getMySQLTlsEnv(); + std::cout << "getMySQLTlsEnv returns '" << env << "'\n"; + } catch (const isc::Exception& ex) { + std::cerr << "getMySQLTlsEnv fails with " << ex.what() << "\n"; + } + if (!env.empty()) { + return; + } + try { + std::cout << "getMySQLTlsServer returns '" << getMySQLTlsServer() << "'\n"; + } catch (const isc::Exception& ex) { + std::cerr << "getMySQLTlsServer fails with " << ex.what() << "\n"; + } +} + +/// @brief Check the enforced TCP connection. +TEST_F(MySqlSecureConnectionTest, Tcp) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + ASSERT_NO_THROW_LOG(conn.openDatabase()); +} + +/// @brief Check the SSL/TLS protected connection. +TEST_F(MySqlSecureConnectionTest, Tls) { + if (!hasMySQLTls()) { + std::cout << "SSL/TLS support is not available or configured: " + << "skipping this test\n"; + return; + } + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_SECURE_USER, + VALID_PASSWORD, 0, 0, + VALID_CERT, VALID_KEY, VALID_CA, + VALID_CIPHER); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + ASSERT_NO_THROW_LOG(conn.openDatabase()); + EXPECT_TRUE(conn.getTls()); + std::string cipher = conn.getTlsCipher(); + EXPECT_FALSE(cipher.empty()); + std::cout << "TLS cipher is '" << cipher << "'\n"; +} + +/// @brief Check the SSL/TLS protected connection still requires the password. +TEST_F(MySqlSecureConnectionTest, TlsInvalidPassword) { + if (!hasMySQLTls()) { + std::cout << "SSL/TLS support is not available or configured: " + << "skipping this test\n"; + return; + } + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_SECURE_USER, + INVALID_PASSWORD, 0, 0, + VALID_CERT, VALID_KEY, VALID_CA, + VALID_CIPHER); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + EXPECT_THROW(conn.openDatabase(), DbOpenError); +} + +/// @brief Check the SSL/TLS protected connection requires crypto parameters. +TEST_F(MySqlSecureConnectionTest, TlsNoCrypto) { + if (!hasMySQLTls()) { + std::cout << "SSL/TLS support is not available or configured: " + << "skipping this test\n"; + return; + } + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_SECURE_USER, + VALID_PASSWORD); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + EXPECT_THROW(conn.openDatabase(), DbOpenError); +} + +/// @brief Check the SSL/TLS protected connection requires valid key. +TEST_F(MySqlSecureConnectionTest, TlsInvalidKey) { + if (!hasMySQLTls()) { + std::cout << "SSL/TLS support is not available or configured: " + << "skipping this test\n"; + return; + } + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_SECURE_USER, + VALID_PASSWORD, 0, 0, + VALID_CERT, INVALID_KEY, VALID_CA, + VALID_CIPHER); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + EXPECT_THROW(conn.openDatabase(), DbOpenError); +} + +/// @brief Check the SSL/TLS protected connection requires a key. +TEST_F(MySqlSecureConnectionTest, TlsNoKey) { + if (!hasMySQLTls()) { + std::cout << "SSL/TLS support is not available or configured: " + << "skipping this test\n"; + return; + } + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_SECURE_USER, + VALID_PASSWORD, 0, 0, + VALID_CERT, 0, VALID_CA, + VALID_CIPHER); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + EXPECT_THROW(conn.openDatabase(), DbOpenError); +} + +} // namespace diff --git a/src/lib/mysql/tests/run_unittests.cc b/src/lib/mysql/tests/run_unittests.cc new file mode 100644 index 0000000..4e83d4b --- /dev/null +++ b/src/lib/mysql/tests/run_unittests.cc @@ -0,0 +1,20 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <log/logger_support.h> +#include <gtest/gtest.h> + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + + int result = RUN_ALL_TESTS(); + + return (result); +} diff --git a/src/lib/mysql/testutils/Makefile.am b/src/lib/mysql/testutils/Makefile.am new file mode 100644 index 0000000..64680e6 --- /dev/null +++ b/src/lib/mysql/testutils/Makefile.am @@ -0,0 +1,23 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += -DDATABASE_SCRIPTS_DIR=\"$(abs_top_srcdir)/src/share/database/scripts\" +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +CLEANFILES = *.gcno *.gcda + +if HAVE_GTEST + +noinst_LTLIBRARIES = libmysqltest.la + +libmysqltest_la_SOURCES = mysql_schema.cc mysql_schema.h + +libmysqltest_la_CXXFLAGS = $(AM_CXXFLAGS) +libmysqltest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(MYSQL_CPPFLAGS) +libmysqltest_la_LDFLAGS = $(AM_LDFLAGS) $(MYSQL_LIBS) + +libmysqltest_la_LIBADD = $(top_builddir)/src/lib/database/testutils/libdatabasetest.la + +endif diff --git a/src/lib/mysql/testutils/Makefile.in b/src/lib/mysql/testutils/Makefile.in new file mode 100644 index 0000000..a2ded3d --- /dev/null +++ b/src/lib/mysql/testutils/Makefile.in @@ -0,0 +1,837 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/lib/mysql/testutils +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_sysrepo.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +@HAVE_GTEST_TRUE@libmysqltest_la_DEPENDENCIES = $(top_builddir)/src/lib/database/testutils/libdatabasetest.la +am__libmysqltest_la_SOURCES_DIST = mysql_schema.cc mysql_schema.h +@HAVE_GTEST_TRUE@am_libmysqltest_la_OBJECTS = \ +@HAVE_GTEST_TRUE@ libmysqltest_la-mysql_schema.lo +libmysqltest_la_OBJECTS = $(am_libmysqltest_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libmysqltest_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libmysqltest_la_CXXFLAGS) $(CXXFLAGS) \ + $(libmysqltest_la_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_GTEST_TRUE@am_libmysqltest_la_rpath = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libmysqltest_la-mysql_schema.Plo +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libmysqltest_la_SOURCES) +DIST_SOURCES = $(am__libmysqltest_la_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_SYSREPO = @HAVE_SYSREPO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + -DDATABASE_SCRIPTS_DIR=\"$(abs_top_srcdir)/src/share/database/scripts\" \ + $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) +CLEANFILES = *.gcno *.gcda +@HAVE_GTEST_TRUE@noinst_LTLIBRARIES = libmysqltest.la +@HAVE_GTEST_TRUE@libmysqltest_la_SOURCES = mysql_schema.cc mysql_schema.h +@HAVE_GTEST_TRUE@libmysqltest_la_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@libmysqltest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(MYSQL_CPPFLAGS) +@HAVE_GTEST_TRUE@libmysqltest_la_LDFLAGS = $(AM_LDFLAGS) $(MYSQL_LIBS) +@HAVE_GTEST_TRUE@libmysqltest_la_LIBADD = $(top_builddir)/src/lib/database/testutils/libdatabasetest.la +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/mysql/testutils/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/mysql/testutils/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libmysqltest.la: $(libmysqltest_la_OBJECTS) $(libmysqltest_la_DEPENDENCIES) $(EXTRA_libmysqltest_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libmysqltest_la_LINK) $(am_libmysqltest_la_rpath) $(libmysqltest_la_OBJECTS) $(libmysqltest_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmysqltest_la-mysql_schema.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libmysqltest_la-mysql_schema.lo: mysql_schema.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysqltest_la_CPPFLAGS) $(CPPFLAGS) $(libmysqltest_la_CXXFLAGS) $(CXXFLAGS) -MT libmysqltest_la-mysql_schema.lo -MD -MP -MF $(DEPDIR)/libmysqltest_la-mysql_schema.Tpo -c -o libmysqltest_la-mysql_schema.lo `test -f 'mysql_schema.cc' || echo '$(srcdir)/'`mysql_schema.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmysqltest_la-mysql_schema.Tpo $(DEPDIR)/libmysqltest_la-mysql_schema.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='mysql_schema.cc' object='libmysqltest_la-mysql_schema.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmysqltest_la_CPPFLAGS) $(CPPFLAGS) $(libmysqltest_la_CXXFLAGS) $(CXXFLAGS) -c -o libmysqltest_la-mysql_schema.lo `test -f 'mysql_schema.cc' || echo '$(srcdir)/'`mysql_schema.cc + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libmysqltest_la-mysql_schema.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/libmysqltest_la-mysql_schema.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib/mysql/testutils/mysql_schema.cc b/src/lib/mysql/testutils/mysql_schema.cc new file mode 100644 index 0000000..936beb7 --- /dev/null +++ b/src/lib/mysql/testutils/mysql_schema.cc @@ -0,0 +1,163 @@ +// Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <string> +#include <mysql.h> +#include <mysql/testutils/mysql_schema.h> +#include <mysql/mysql_connection.h> +#include <exceptions/exceptions.h> + +#include <fstream> +#include <iostream> +#include <sstream> +#include <stdlib.h> + +using namespace std; + +namespace isc { +namespace db { +namespace test { + +const char* MYSQL_VALID_TYPE = "type=mysql"; + +string +validMySQLConnectionString() { + return (connectionString(MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, + VALID_USER, VALID_PASSWORD)); +} + +void destroyMySQLSchema(bool show_err, bool force) { + // If force is true or wipeMySQLData() fails, destroy the schema. + if (force || (!softWipeEnabled()) || wipeMySQLData(show_err)) { + runMySQLScript(DATABASE_SCRIPTS_DIR, "mysql/dhcpdb_drop.mysql", show_err); + } +} + +void createMySQLSchema(bool show_err, bool force) { + // If force is true or wipeMySQLData() fails, recreate the schema. + if (force || (!softWipeEnabled()) || wipeMySQLData(show_err)) { + destroyMySQLSchema(show_err, true); + runMySQLScript(DATABASE_SCRIPTS_DIR, "mysql/dhcpdb_create.mysql", show_err); + } +} + +bool wipeMySQLData(bool show_err) { + std::ostringstream cmd; + cmd << "sh " << DATABASE_SCRIPTS_DIR << "/"; + + std::ostringstream version; + version << MYSQL_SCHEMA_VERSION_MAJOR << "." << MYSQL_SCHEMA_VERSION_MINOR; + + cmd << "mysql/wipe_data.sh " << version.str() + << " -N -B --user=keatest --password=keatest keatest"; + if (!show_err) { + cmd << " 2>/dev/null "; + } + + int retval = ::system(cmd.str().c_str()); + if (retval) { + std::cerr << "wipeMySQLData failed:[" << cmd.str() << "]" << std::endl; + } + + return(retval); +} + +void runMySQLScript(const std::string& path, const std::string& script_name, + bool show_err) { + std::ostringstream cmd; + cmd << "mysql -N -B --user=keatest --password=keatest keatest"; + if (!show_err) { + cmd << " 2>/dev/null "; + } + + if (!path.empty()) { + cmd << " < " << path << "/"; + } + + cmd << script_name; + + int retval = ::system(cmd.str().c_str()); + if (retval) { + std::cerr << "runMySQLSchema failed: " << cmd.str() << std::endl; + isc_throw(Unexpected, "runMySQLSchema failed: " << cmd.str()); + } +} + +string getMySQLTlsEnv() { + const string name("KEA_MYSQL_HAVE_SSL"); + const char* val = getenv(name.c_str()); + return (val ? string(val) : ""); +} + +string getMySQLTlsServerVariable(string variable) { + MYSQL_RES* result(0); + try { + DatabaseConnection::ParameterMap parameters = + DatabaseConnection::parse(validMySQLConnectionString()); + MySqlConnection conn(parameters); + conn.openDatabase(); + string sql("SHOW GLOBAL VARIABLES LIKE '"); + sql += variable; + sql += "'"; + if (mysql_query(conn.mysql_, sql.c_str())) { + isc_throw(DbOperationError, + sql << ": " << mysql_error(conn.mysql_)); + } + result = mysql_use_result(conn.mysql_); + size_t count = mysql_num_fields(result); + if (count != 2) { + isc_throw(DbOperationError, + sql << " returned " << count << " rows, expecting 2"); + } + MYSQL_ROW row = mysql_fetch_row(result); + if (!row) { + isc_throw(DbOperationError, sql << " returned row is null"); + } + // first column is 'have_ssl', second is the status. + string name(row[0]); + if (name != variable) { + isc_throw(DbOperationError, + sql << " returned a wrong name '" << name + << "', expected '" << variable << "'"); + } + string value(row[1]); + mysql_free_result(result); + return (value); + } catch (...) { + if (result) { + mysql_free_result(result); + } + throw; + } +} + +bool isMySQLTlsConfigured() { + if (getMySQLTlsServerVariable("ssl_ca").find("kea-ca.crt") == string::npos) { + return (false); + } + if (getMySQLTlsServerVariable("ssl_cert").find("kea-server.crt") == string::npos) { + return (false); + } + if (getMySQLTlsServerVariable("ssl_key").find("kea-server.key") == string::npos) { + return (false); + } + return (true); +} + +string getMySQLTlsServer() { + string value = getMySQLTlsServerVariable("have_ssl"); + if (value == "YES" && !isMySQLTlsConfigured()) { + value = "UNCONFIGURED"; + } + const string env("KEA_MYSQL_HAVE_SSL"); + static_cast<void>(setenv(env.c_str(), value.c_str(), 1)); + return (value); +} + +} // namespace test +} // namespace db +} // namespace isc diff --git a/src/lib/mysql/testutils/mysql_schema.h b/src/lib/mysql/testutils/mysql_schema.h new file mode 100644 index 0000000..a3df21f --- /dev/null +++ b/src/lib/mysql/testutils/mysql_schema.h @@ -0,0 +1,123 @@ +// Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef TEST_MYSQL_SCHEMA_H +#define TEST_MYSQL_SCHEMA_H + +#include <config.h> +#include <database/testutils/schema.h> +#include <string> + +namespace isc { +namespace db { +namespace test { + +extern const char* MYSQL_VALID_TYPE; + +/// Return valid connection string +/// +/// @return valid MySQL connection string. +std::string validMySQLConnectionString(); + +/// @brief Clear the unit test database +/// +/// In order to reduce test execution time, this function +/// defaults to first attempting to delete transient data +/// from the database by calling @c wipeMySQLData. If that +/// function fails it will then attempt to destroy the database +/// schema by running the SQL script: +/// +/// <TEST_ADMIN_SCRIPTS_DIR>/mysql/dhcpdb_drop.mysql +/// +/// The default behavior of wiping the data only may be overridden +/// in one of two ways: +/// +/// -# Setting the force parameter to true +/// -# Defining the environment variable: +/// KEA_TEST_DB_WIPE_DATA_ONLY="false" +/// +/// @param show_err flag which governs whether or not stderr is suppressed. +/// @param force if true, the function will skip deleting the data and +/// destroy the schema. +void destroyMySQLSchema(bool show_err = false, bool force = false); + +/// @brief Create the unit test MySQL Schema +/// +/// Ensures the unit test database is a empty and version-correct. +/// Unless, the force parameter is true, it will first attempt +/// to wipe the data from the database by calling @c wipeMySQLData. +/// If this call succeeds the function returns, otherwise it will +/// will call @c destroyMySQLSchema to forcibly remove the +/// existing schema and then submits the SQL script: +/// +/// <TEST_ADMIN_SCRIPTS_DIR>/mysql/dhcpdb_create.mysql +/// +/// to the unit test MySQL database. +/// +/// The default behavior of wiping the data only may be overridden +/// in one of two ways: +/// +/// -# Setting the force parameter to true +/// -# Defining the environment variable: +/// KEA_TEST_DB_WIPE_DATA_ONLY="false" +/// +/// @param show_err flag which governs whether or not stderr is suppressed. +/// @param force flag when true, the function will recreate the database +/// schema. +void createMySQLSchema(bool show_err = false, bool force = false); + +/// @brief Attempts to wipe data from the MySQL unit test database +/// +/// Runs the shell script +/// +/// <TEST_ADMIN_SCRIPTS_DIR>/mysql/wipe_data.sh +/// +/// This will fail if there is no schema, if the existing schema +/// version is incorrect (i.e. does not match MYSQL_SCHEMA_VERSION_MAJOR +/// and MYSQL_SCHEMA_VERSION_MINOR), or a SQL error occurs. Otherwise, +/// the script is should delete all transient data, leaving intact +/// reference tables. +/// +/// @param show_err flag which governs whether or not stderr is suppressed. +bool wipeMySQLData(bool show_err = false); + +/// @brief Run a MySQL SQL script against the MySQL unit test database +/// +/// Submits the given SQL script to MySQL via mysql CLI. The output of +/// stderr is suppressed unless the parameter, show_err is true. The is done +/// to suppress warnings that might otherwise make test output needlessly +/// noisy. An exception is thrown if the script fails to execute. +/// +/// @param path - path (if not blank) of the script to execute +/// @param script_name - file name of the path to execute +/// @param show_err flag which governs whether or not stderr is suppressed. +/// @throw Unexpected when the script returns an error. +void runMySQLScript(const std::string& path, const std::string& script_name, + bool show_err); + +/// @brief Get the SSL/TLS support status from the environment +/// +/// The environment variable is KEA_MYSQL_HAVE_SSL +std::string getMySQLTlsEnv(); + +/// @brief Get the SSL/TLS support status from the server +/// @note the returned value is set in the environment +std::string getMySQLTlsServer(); + +/// @brief Return true if the server has been configured with proper SSL/TLS +/// credentials, false otherwise +bool isMySQLTlsConfigured(); + +/// @brief Get the server global variable value +/// +/// @param variable The server global variable name +std::string getMySQLTlsServerVariable(std::string variable); + +} +} +} + +#endif |