mysql-connector-odbc-5.1.10-src/cmake/FindLtdl.cmake100644 15766 12 4663 11707541005 20761 0ustar00cteamstaff# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## SET(LTDL_LIBS "ltdl") IF(LTDL_PATH) SET(LTDL_INCLUDES "${LTDL_PATH}/include") SET(LTDL_LIB_DIR "${LTDL_PATH}/lib") SET(LTDL_LFLAGS "-L${LTDL_LIB_DIR}") IF(NOT LTDL_LINK_DYNAMIC AND EXISTS "${LTDL_LIB_DIR}/libltdl.a") SET(LTDL_LIBS "${LTDL_LIB_DIR}/libltdl.a") ENDIF(NOT LTDL_LINK_DYNAMIC AND EXISTS "${LTDL_LIB_DIR}/libltdl.a") ELSE(LTDL_PATH) INCLUDE (CheckIncludeFiles) CHECK_INCLUDE_FILES(ltdl.h HAVE_LTDL_H) IF(NOT HAVE_LTDL_H) MESSAGE(FATAL_ERROR "ltdl.h could not be found") ENDIF(NOT HAVE_LTDL_H) INCLUDE (CheckLibraryExists) CHECK_LIBRARY_EXISTS(ltdl lt_dlopen "" HAVE_LTDL_LIB) IF(NOT HAVE_LTDL_LIB) MESSAGE(FATAL_ERROR "ltdl lib could not be found") ENDIF(NOT HAVE_LTDL_LIB) ENDIF(LTDL_PATH) TRY_COMPILE(COMPILE_RESULT ${CMAKE_SOURCE_DIR}/cmake/CMakeTmp ${CMAKE_SOURCE_DIR}/cmake/needdl.c CMAKE_FLAGS -DINCLUDE_DIRECTORIES=${LTDL_INCLUDES} -DLINK_DIRECTORIES=${LTDL_LIB_DIR} -DLINK_LIBRARIES=${LTDL_LIBS} OUTPUT_VARIABLE COMPILE_OUTPUT) # Perhaps to add try_compile with dl would be safer IF(COMPILE_RESULT) MESSAGE(STATUS "Checking if need to link dl - FALSE") ElSE(COMPILE_RESULT) MESSAGE(STATUS "Checking if need to link dl - TRUE") SET(LTDL_LIBS ${LTDL_LIBS} "dl") ENDIF(COMPILE_RESULT) mysql-connector-odbc-5.1.10-src/cmake/needdl.c100644 15766 12 2414 11707541005 17646 0ustar00cteamstaff/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include int main( int argc, char *argv[] ) { void * dummy; lt_dlinit(); dummy= lt_dlopen("dummy.so"); if (dummy) lt_dlclose(dummy); return(0); } mysql-connector-odbc-5.1.10-src/cmake/sqlparamopt1.c.cmake100644 15766 12 2647 11707541005 22126 0ustar00cteamstaff/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #cmakedefine ODBC_INCLUDES #ifdef ODBC_INCLUDES #include "@ODBC_INCLUDES@/sql.h" #include "@ODBC_INCLUDES@/sqlext.h" #else #include #include #endif SQLRETURN SQL_API SQLParamOptions( SQLHSTMT hstmt, SQLULEN crow, SQLULEN *pirow ) { return 1; } int main() {} mysql-connector-odbc-5.1.10-src/cmake/FindODBC.cmake100644 15766 12 10035 11707541005 20577 0ustar00cteamstaff# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## MACRO(_FIX_NOPREFIX VAR _odbc_config _param) EXECUTE_PROCESS(COMMAND ${_odbc_config} ${_param} OUTPUT_VARIABLE _output ) STRING(REGEX REPLACE "\n" "" _output "${_output}") IF(${VAR} MATCHES "/noprefix") IF(NOT GUESSED_PREFIX) GET_FILENAME_COMPONENT(GUESSED_PREFIX "${_odbc_config}" PATH) GET_FILENAME_COMPONENT(GUESSED_PREFIX "${GUESSED_PREFIX}" PATH) ENDIF(NOT GUESSED_PREFIX) STRING(REGEX REPLACE "/noprefix" "${GUESSED_PREFIX}" _output "${_output}") ENDIF(${VAR} MATCHES "/noprefix") SET(${VAR} ${_output}) ENDMACRO(_FIX_NOPREFIX VAR _odbc_config _param) IF(ODBC_INCLUDES) INCLUDE_DIRECTORIES(${ODBC_INCLUDES}) ENDIF(ODBC_INCLUDES) IF(ODBC_LIB_DIR) SET(ODBC_LINK_FLAGS "-L${ODBC_LIB_DIR} -l${ODBCLIB} -l${ODBCINSTLIB}") ENDIF(ODBC_LIB_DIR) # No need to look for (i)odbc[_-]config and run it IF(NOT ODBC_INCLUDES OR NOT ODBC_LIB_DIR) IF(WITH_UNIXODBC) # check for location of odbc_config FIND_PROGRAM(ODBC_CONFIG odbc_config PATHS $ENV{ODBC_PATH}/bin) IF(NOT ODBC_CONFIG) MESSAGE(STATUS "Couldn't find unixODBC' odbc_config") #odbc config may be not present, and that can be ok IF(NOT ODBC_INCLUDES) INCLUDE (CheckIncludeFiles) CHECK_INCLUDE_FILES(sql.h HAVE_SQL_H) IF(NOT HAVE_SQL_H) MESSAGE(FATAL_ERROR "sql.h is not found either!") ENDIF(NOT HAVE_SQL_H) ENDIF(NOT ODBC_INCLUDES) IF(NOT ODBC_LIB_DIR) INCLUDE (CheckLibraryExists) CHECK_LIBRARY_EXISTS(odbc SQLConnect "" HAVE_ODBC_LIB) IF(NOT HAVE_ODBC_LIB) MESSAGE(FATAL_ERROR "odbc lib is not found either!") ENDIF(NOT HAVE_ODBC_LIB) SET(ODBC_LINK_FLAGS "-l${ODBCLIB} -l${ODBCINSTLIB}") ENDIF(NOT ODBC_LIB_DIR) ELSE(NOT ODBC_CONFIG) MESSAGE(STATUS "unixODBC: Found odbc_config in ${ODBC_CONFIG}") IF(NOT ODBC_INCLUDES) _FIX_NOPREFIX(ODBC_INCLUDE_DIR ${ODBC_CONFIG} "--include-prefix") INCLUDE_DIRECTORIES(${ODBC_INCLUDE_DIR}) ENDIF(NOT ODBC_INCLUDES) IF(NOT ODBC_LIB_DIR) _FIX_NOPREFIX(ODBC_LINK_FLAGS ${ODBC_CONFIG} "--libs") ENDIF(NOT ODBC_LIB_DIR) ENDIF(NOT ODBC_CONFIG) ELSE(WITH_UNIXODBC) FIND_PROGRAM(ODBC_CONFIG iodbc-config PATHS $ENV{ODBC_PATH}/bin) IF(NOT ODBC_CONFIG) MESSAGE(FATAL_ERROR "Couldn't find iODBC") ENDIF(NOT ODBC_CONFIG) MESSAGE(STATUS "iODBC: Found iodbc-config in ${ODBC_CONFIG}") IF(NOT ODBC_INCLUDES) _FIX_NOPREFIX(ODBC_CFLAGS ${ODBC_CONFIG} "--cflags") SET(CMAKE_FLAGS "${CMAKE_FLAGS} ${ODBC_CFLAGS}") ENDIF(NOT ODBC_INCLUDES) IF(NOT ODBC_LIB_DIR) _FIX_NOPREFIX(ODBC_LINK_FLAGS ${ODBC_CONFIG} "--libs") ENDIF(NOT ODBC_LIB_DIR) ENDIF(WITH_UNIXODBC) ENDIF(NOT ODBC_INCLUDES OR NOT ODBC_LIB_DIR) mysql-connector-odbc-5.1.10-src/cmake/FindMySQL.cmake100644 15766 12 10231 11707541005 21033 0ustar00cteamstaff#-------------------------------------------------------- # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## #-------------- FIND MYSQL_INCLUDE_DIR ------------------ FIND_PATH(MYSQL_INCLUDE_DIR mysql.h $ENV{MYSQL_INCLUDE_DIR} $ENV{MYSQL_DIR}/include /usr/include/mysql /usr/local/include/mysql /opt/mysql/mysql/include /opt/mysql/mysql/include/mysql /usr/local/mysql/include /usr/local/mysql/include/mysql $ENV{ProgramFiles}/MySQL/*/include $ENV{SystemDrive}/MySQL/*/include) #----------------- FIND MYSQL_LIB_DIR ------------------- IF (WIN32) # Set lib path suffixes # dist = for mysql binary distributions # build = for custom built tree IF (CMAKE_BUILD_TYPE STREQUAL Debug) SET(libsuffixDist debug) SET(libsuffixBuild Debug) ELSE (CMAKE_BUILD_TYPE STREQUAL Debug) SET(libsuffixDist opt) SET(libsuffixBuild Release) ADD_DEFINITIONS(-DDBUG_OFF) ENDIF (CMAKE_BUILD_TYPE STREQUAL Debug) FIND_LIBRARY(MYSQL_LIB NAMES mysqlclient PATHS $ENV{MYSQL_DIR}/lib/${libsuffixDist} $ENV{MYSQL_DIR}/lib $ENV{MYSQL_DIR}/libmysql $ENV{MYSQL_DIR}/lib $ENV{MYSQL_DIR}/libmysql/${libsuffixBuild} $ENV{MYSQL_DIR}/client/${libsuffixBuild} $ENV{MYSQL_DIR}/libmysql/${libsuffixBuild} $ENV{ProgramFiles}/MySQL/*/lib/${libsuffixDist} $ENV{ProgramFiles}/MySQL/*/lib $ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist}) ELSE (WIN32) FIND_LIBRARY(MYSQL_LIB NAMES mysqlclient_r PATHS $ENV{MYSQL_DIR}/libmysql_r/.libs $ENV{MYSQL_DIR}/lib $ENV{MYSQL_DIR}/lib/mysql /usr/lib/mysql /usr/local/lib/mysql /usr/local/mysql/lib /usr/local/mysql/lib/mysql /opt/mysql/mysql/lib /opt/mysql/mysql/lib/mysql) ENDIF (WIN32) IF(MYSQL_LIB) GET_FILENAME_COMPONENT(MYSQL_LIB_DIR ${MYSQL_LIB} PATH) ENDIF(MYSQL_LIB) IF (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR) SET(MYSQL_FOUND TRUE) INCLUDE_DIRECTORIES(${MYSQL_INCLUDE_DIR}) LINK_DIRECTORIES(${MYSQL_LIB_DIR}) FIND_LIBRARY(MYSQL_ZLIB zlib PATHS ${MYSQL_LIB_DIR}) FIND_LIBRARY(MYSQL_YASSL yassl PATHS ${MYSQL_LIB_DIR}) FIND_LIBRARY(MYSQL_TAOCRYPT taocrypt PATHS ${MYSQL_LIB_DIR}) IF (WIN32) SET(MYSQL_CLIENT_LIBS mysqlclient) ELSE (WIN32) SET(MYSQL_CLIENT_LIBS mysqlclient_r) ENDIF (WIN32) IF (MYSQL_ZLIB) SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} zlib) ENDIF (MYSQL_ZLIB) IF (MYSQL_YASSL) SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} yassl) ENDIF (MYSQL_YASSL) IF (MYSQL_TAOCRYPT) SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} taocrypt) ENDIF (MYSQL_TAOCRYPT) # Added needed mysqlclient dependencies on Windows IF (WIN32) SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} ws2_32) ELSE (WIN32) FIND_PACKAGE(Threads) SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} ${CMAKE_THREAD_LIBS_INIT}) ENDIF (WIN32) MESSAGE(STATUS "MySQL Include dir: ${MYSQL_INCLUDE_DIR} library dir: ${MYSQL_LIB_DIR}") MESSAGE(STATUS "MySQL client libraries: ${MYSQL_CLIENT_LIBS}") ELSE (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR) MESSAGE(FATAL_ERROR "Cannot find MySQL. Include dir: ${MYSQL_INCLUDE_DIR} library dir: ${MYSQL_LIB_DIR}") ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR) mysql-connector-odbc-5.1.10-src/cmake/getmysqlversion.c100644 15766 12 2301 11707541005 21661 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include int main() { printf("%s", MYSQL_SERVER_VERSION); } mysql-connector-odbc-5.1.10-src/cmake/sqlcolattrib1.c.cmake100644 15766 12 3365 11707541005 22264 0ustar00cteamstaff/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #cmakedefine ODBC_INCLUDES #ifdef ODBC_INCLUDES #include "@ODBC_INCLUDES@/sql.h" #include "@ODBC_INCLUDES@/sqlext.h" #else #include #include #endif SQLRETURN SQL_API SQLColAttribute( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLLEN * NumericAttributePtr ) { return 1; } int main() { } mysql-connector-odbc-5.1.10-src/cmake/sqlparamopt2.c.cmake100644 15766 12 2657 11707541005 22130 0ustar00cteamstaff/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #cmakedefine ODBC_INCLUDES #ifdef ODBC_INCLUDES #include "@ODBC_INCLUDES@/sql.h" #include "@ODBC_INCLUDES@/sqlext.h" #else #include #include #endif SQLRETURN SQL_API SQLParamOptions( SQLHSTMT hstmt, SQLUINTEGER crow, SQLUINTEGER *pirow ) { return 1; } int main() {} mysql-connector-odbc-5.1.10-src/cmake/havelpcwstr.c100644 15766 12 2261 11707541005 20755 0ustar00cteamstaff/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include int main( int argc, char *argv[] ) { LPCWSTR dummy; } mysql-connector-odbc-5.1.10-src/cmake/sqlcolattrib2.c.cmake100644 15766 12 3367 11707541005 22267 0ustar00cteamstaff/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #cmakedefine ODBC_INCLUDES #ifdef ODBC_INCLUDES #include "@ODBC_INCLUDES@/sql.h" #include "@ODBC_INCLUDES@/sqlext.h" #else #include #include #endif SQLRETURN SQL_API SQLColAttribute( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLPOINTER NumericAttributePtr ) { return 1; } int main() { } mysql-connector-odbc-5.1.10-src/dltest/Makefile.am100644 15766 12 1536 11707541005 20526 0ustar00cteamstaff################################################################### # # BRIEF: # Test program to load and check a symbol in a shared lib. # # DESCRIPTION: # This is a utility to load a shared library and check # for a given symbol on it. Only a test tool, not specific # to ODBC or databases. # ################################################################### noinst_PROGRAMS = dltest dltest_SOURCES = dltest.c dltest_LDADD = @DL_LIB@ ################################################################### # # More files to include in source distro. # ################################################################### EXTRA_DIST = \ CMakeLists.txt mysql-connector-odbc-5.1.10-src/dltest/CMakeLists.txt100644 15766 12 2572 11707541005 21233 0ustar00cteamstaff# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## ADD_EXECUTABLE(dltest dltest.c) IF(NOT WIN32) INCLUDE_DIRECTORIES(${DL_INCLUDES}) TARGET_LINK_LIBRARIES(dltest ${DL_LIBS}) SET_TARGET_PROPERTIES(dltest PROPERTIES LINK_FLAGS "${DL_LFLAGS}") ENDIF(NOT WIN32) mysql-connector-odbc-5.1.10-src/dltest/dltest.c100644 15766 12 15021 11707541005 20147 0ustar00cteamstaff/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #ifdef WIN32 #include #else #include #endif char *szSyntax = "\n" \ "**********************************************\n" \ "* dltest *\n" \ "**********************************************\n" \ "* Syntax *\n" \ "* *\n" \ "* dltest libName Symbol *\n" \ "* dltest lib:libName... sym:Symbol... *\n" \ "* *\n" \ "* libName *\n" \ "* *\n" \ "* Full path + file name of share to test*\n" \ "* *\n" \ "* Symbol *\n" \ "* *\n" \ "* ie a function name in the share *\n" \ "* *\n" \ "* Notes *\n" \ "* *\n" \ "* This can be placed into a makefile *\n" \ "* to throw an error if test fails. *\n" \ "* *\n" \ "* If this segfaults you probably have an*\n" \ "* unresolved symbol in the lib. This is *\n" \ "* not caught since dltest started using *\n" \ "* libtool. Linux users can refer to the *\n" \ "* man page for dlopen to create a *\n" \ "* better test. *\n" \ "* *\n" \ "* *\n" \ "* Examples *\n" \ "* *\n" \ "* dltest /usr/lib/libMy.so MyFunc *\n" \ "* *\n" \ "**********************************************\n\n"; #ifdef WIN32 typedef HMODULE DLTestModule; #else typedef void * DLTestModule; #endif static void dltest_dlinit(void); static DLTestModule dltest_dlopen(const char *); static void dltest_dlsym(DLTestModule, const char *); static void dltest_dlclose(DLTestModule); int main( int argc, char *argv[] ) { DLTestModule hModule = NULL; if ( argc < 2 ) { printf( szSyntax ); exit( 1 ); } /* At least one argument, a library path */ dltest_dlinit(); if ( strncmp(argv[1],"lib:",4) == 0 || strncmp(argv[1],"sym:",4) == 0 ) { /* Alternative API, can handle multiple libs and symbols, in any mix */ int i; for (i = 1; i < argc; i++) { if (strncmp(argv[1],"lib:",4) == 0) { hModule = dltest_dlopen(argv[i]+4); /* Open a new module */ } else if (strncmp(argv[1],"sym:",4) == 0) { dltest_dlsym(hModule,argv[i]+4); } } /* Why close at all, and if we load libraries dependent on each other, closing will prevent a later opened lib from accessing symbols from the previous one, at least on AIX 5.2 */ } else { /* Old API */ hModule = dltest_dlopen(argv[1]); if ( argc > 2 ) dltest_dlsym(hModule,argv[2]); dltest_dlclose(hModule); } return(0); } static void dltest_dlinit(void) { #ifndef WIN32 /* if ( lt_dlinit() ) { printf( "[%s][%d] ERROR: Failed to lt_dlinit()\n", __FILE__, __LINE__ ); exit( 1 ); } */ #endif } static DLTestModule dltest_dlopen(const char *path) { #ifdef WIN32 DLTestModule hModule = LoadLibrary((LPCSTR)path); if ( !hModule ) { LPVOID pszMsg; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pszMsg, 0, NULL); printf("[%s][%d] ERROR LoadLibrary(): %s\n", __FILE__, __LINE__, pszMsg); LocalFree(pszMsg); exit(1); } #else DLTestModule hModule = dlopen(path, RTLD_GLOBAL | RTLD_NOW); if ( !hModule ) { printf("[%s][%d] ERROR dlopen(): %s\n", __FILE__, __LINE__, dlerror()); exit(1); } #endif /* WIN32 */ printf("[%s][%d] SUCCESS: Loaded %s\n", __FILE__, __LINE__, path); return hModule; } static void dltest_dlclose(DLTestModule hModule) { #ifdef WIN32 FreeLibrary(hModule); #else dlclose(hModule); #endif /* WIN32 */ } static void dltest_dlsym(DLTestModule hModule, const char *sym) { void (*pFunc)(); #ifdef WIN32 pFunc = (void (*)())GetProcAddress(hModule,sym); if ( !pFunc ) { LPVOID pszMsg; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (LPTSTR) &pszMsg, 0, NULL); printf("[%s][%d] ERROR: Could not find %s. %s\n",__FILE__,__LINE__,sym,pszMsg); LocalFree(pszMsg); FreeLibrary(hModule); exit(1); } #else pFunc = (void (*)())dlsym(hModule,sym); /* PAH - dlerror() is not a good indicator of success */ /* if ( (pError = dlerror()) != NULL ) */ if ( !pFunc ) { const char *pError; if ( (pError = dlerror()) != NULL ) printf("[%s][%d] ERROR: %s\n Could not find %s\n",__FILE__,__LINE__,pError,sym); else printf("[%s][%d] ERROR: Could not find %s\n",__FILE__,__LINE__,sym); exit(1); } #endif printf("[%s][%d] SUCCESS: Found %s\n",__FILE__,__LINE__,sym); } mysql-connector-odbc-5.1.10-src/driver/Makefile.am100644 15766 12 5131 11707541005 20515 0ustar00cteamstaff# Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA AUTOMAKE_OPTIONS=foreign lib_LTLIBRARIES=libmyodbc5.la INCLUDES=-I$(top_srcdir)/util -I$(top_srcdir) ################################################################### # # # Driver Source files # # # ################################################################### libmyodbc5_la_SOURCES= \ ansi.c \ catalog.c \ catalog_no_i_s.c \ catalog.h \ connect.c \ cursor.c \ desc.c \ dll.c \ driver.c \ error.c \ execute.c \ handle.c\ info.c \ options.c \ prepare.c \ results.c \ transact.c \ unicode.c \ utility.c \ driver.h ################################################################### # # # Linker and preprocessor options # # # ################################################################### libmyodbc5_la_CPPFLAGS = -DNONTHREADSAFE libmyodbc5_la_LDFLAGS = -release @NUMERIC_VERSION@ -module if WITH_DEBUG LTLIBS_DEPS = $(LTLIBS_DEBUG_DEPS) else LTLIBS_DEPS = ../util/libmyodbc3u.la endif LTLIBS_DEBUG_DEPS = \ ../util/libmyodbc3u.la libmyodbc5_la_LIBADD = $(LTLIBS_DEPS) @DL_LIB@ @MYSQL_LIB@ libmyodbc5_la_DEPENDENCIES = $(LTLIBS_DEPS) LDFLAGS=@EXTRA_LDFLAGS@ EXTRA_DIST = error.h \ myutil.h \ driver.def \ driver.rc \ CMakeLists.txt mysql-connector-odbc-5.1.10-src/driver/stamp-h.in100644 15766 12 12 11707541005 20313 0ustar00cteamstafftimestamp mysql-connector-odbc-5.1.10-src/driver/driver.h100644 15766 12 50554 11707541005 20156 0ustar00cteamstaff/* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file driver.h @brief Definitions needed by the driver */ #ifndef __DRIVER_H__ #define __DRIVER_H__ #include "../MYODBC_MYSQL.h" #include "../MYODBC_CONF.h" #include "../MYODBC_ODBC.h" #include "installer.h" #ifdef APSTUDIO_READONLY_SYMBOLS #define WIN32 /* Hack for rc files */ #endif /* Needed for offsetof() CPP macro */ #include #ifdef __cplusplus extern "C" { #endif #ifdef RC_INVOKED #define stdin #endif /* Misc definitions for AIX .. */ #ifndef crid_t typedef int crid_t; #endif #ifndef class_id_t typedef unsigned int class_id_t; #endif #ifdef __cplusplus } #endif #include "error.h" #if defined(_WIN32) || defined(WIN32) # define INTFUNC __stdcall # define EXPFUNC __stdcall # if !defined(HAVE_LOCALTIME_R) # define HAVE_LOCALTIME_R 1 # endif #else # define INTFUNC PASCAL # define EXPFUNC __export CALLBACK /* Simple macros to make dl look like the Windows library funcs. */ # define HMODULE void* # define LoadLibrary(library) dlopen((library), RTLD_GLOBAL | RTLD_LAZY) # define GetProcAddress(module, proc) dlsym((module), (proc)) # define FreeLibrary(module) dlclose((module)) #endif #define ODBC_DRIVER "ODBC 5.1 Driver" #define DRIVER_NAME "MySQL ODBC 5.1 Driver" #define DRIVER_NONDSN_TAG "DRIVER={MySQL ODBC 5.1 Driver}" #if defined(__APPLE__) #define DRIVER_QUERY_LOGFILE "/tmp/myodbc.sql" #elif defined(_UNIX_) #define DRIVER_QUERY_LOGFILE "/tmp/myodbc.sql" #else #define DRIVER_QUERY_LOGFILE "c:\\myodbc.sql" #endif /* Internal driver definitions */ #define MYSQL_RESET_BUFFERS 1000 /* param to SQLFreeStmt */ #define MYSQL_RESET 1001 /* param to SQLFreeStmt */ #define MYSQL_3_21_PROTOCOL 10 /* OLD protocol */ #define CHECK_IF_ALIVE 1800 /* Seconds between queries for ping */ #define MYSQL_MAX_CURSOR_LEN 18 /* Max cursor name length */ #define MYSQL_STMT_LEN 1024 /* Max statement length */ #define MYSQL_STRING_LEN 1024 /* Max string length */ #define MYSQL_MAX_SEARCH_STRING_LEN NAME_LEN+10 /* Max search string length */ /* Max Primary keys in a cursor * WHERE clause */ #define MY_MAX_PK_PARTS 32 #if MYSQL_VERSION_ID >= 50500 # define x_free(A) { void *tmp= (A); if (tmp) my_free((char *) tmp); } # ifndef NEAR # define NEAR # endif #else # define x_free(A) { void *tmp= (A); if (tmp) my_free((char *) tmp,MYF(MY_WME+MY_FAE)); } #endif /* We don't make any assumption about what the default may be. */ #ifndef DEFAULT_TXN_ISOLATION # define DEFAULT_TXN_ISOLATION 0 #endif /* Connection flags to validate after the connection*/ #define CHECK_AUTOCOMMIT_ON 1 /* AUTOCOMMIT_ON */ #define CHECK_AUTOCOMMIT_OFF 2 /* AUTOCOMMIT_OFF */ /* implementation or application descriptor? */ typedef enum { DESC_IMP, DESC_APP } desc_ref_type; /* parameter or row descriptor? */ typedef enum { DESC_PARAM, DESC_ROW, DESC_UNKNOWN } desc_desc_type; /* header or record field? (location in descriptor) */ typedef enum { DESC_HDR, DESC_REC } fld_loc; /* permissions - header, and base for record */ #define P_RI 1 /* imp */ #define P_WI 2 #define P_RA 4 /* app */ #define P_WA 8 /* macros to encode the constants above */ #define P_ROW(P) (P) #define P_PAR(P) ((P) << 4) #define PR_RIR P_ROW(P_RI) #define PR_WIR (P_ROW(P_WI) | PR_RIR) #define PR_RAR P_ROW(P_RA) #define PR_WAR (P_ROW(P_WA) | PR_RAR) #define PR_RIP P_PAR(P_RI) #define PR_WIP (P_PAR(P_WI) | PR_RIP) #define PR_RAP P_PAR(P_RI) #define PR_WAP (P_PAR(P_WA) | PR_RAP) /* macros to test type */ #define IS_APD(d) ((d)->desc_type == DESC_PARAM && (d)->ref_type == DESC_APP) #define IS_IPD(d) ((d)->desc_type == DESC_PARAM && (d)->ref_type == DESC_IMP) #define IS_ARD(d) ((d)->desc_type == DESC_ROW && (d)->ref_type == DESC_APP) #define IS_IRD(d) ((d)->desc_type == DESC_ROW && (d)->ref_type == DESC_IMP) /* additional field types needed, but not defined in ODBC */ #define SQL_IS_ULEN (-9) #define SQL_IS_LEN (-10) /* check if ARD record is a bound column */ #define ARD_IS_BOUND(d) ((d)->data_ptr || (d)->octet_length_ptr) /* get the dbc from a descriptor */ #define DESC_GET_DBC(X) (((X)->alloc_type == SQL_DESC_ALLOC_USER) ? \ (X)->exp.dbc : (X)->stmt->dbc) /* data-at-exec type */ #define DAE_NORMAL 1 /* normal SQLExecute() */ #define DAE_SETPOS_INSERT 2 /* SQLSetPos() insert */ #define DAE_SETPOS_UPDATE 3 /* SQLSetPos() update */ /* data-at-exec handling done for current SQLSetPos() call */ #define DAE_SETPOS_DONE 10 typedef struct { int perms; SQLSMALLINT data_type; /* SQL_IS_SMALLINT, etc */ fld_loc loc; size_t offset; /* offset of field in struct */ } desc_field; /* descriptor */ struct tagSTMT; struct tagDBC; typedef struct { /* header fields */ SQLSMALLINT alloc_type; SQLULEN array_size; SQLUSMALLINT *array_status_ptr; /* NOTE: This field is defined as SQLINTEGER* in the descriptor * documentation, but corresponds to SQL_ATTR_ROW_BIND_OFFSET_PTR or * SQL_ATTR_PARAM_BIND_OFFSET_PTR when set via SQLSetStmtAttr(). The * 64-bit ODBC addendum says that when set via SQLSetStmtAttr(), this * is now a 64-bit value. These two are conflicting, so we opt for * the 64-bit value. */ SQLULEN *bind_offset_ptr; SQLINTEGER bind_type; SQLLEN count; /* Everywhere(http://msdn.microsoft.com/en-us/library/ms713560(VS.85).aspx http://msdn.microsoft.com/en-us/library/ms712631(VS.85).aspx) I found it's referred as SQLULEN* */ SQLULEN *rows_processed_ptr; /* internal fields */ desc_desc_type desc_type; desc_ref_type ref_type; DYNAMIC_ARRAY records; MYERROR error; struct tagSTMT *stmt; /* SQL_DESC_ALLOC_USER-specific */ struct { /* We keep a list of all statements we've been set on because we need to put the implicit descriptor back if this one is freed. */ LIST *stmts; /* connection we were allocated on */ struct tagDBC *dbc; } exp; } DESC; /* descriptor record */ typedef struct { /* ODBC spec fields */ SQLINTEGER auto_unique_value; /* row only */ SQLCHAR * base_column_name; /* row only */ SQLCHAR * base_table_name; /* row only */ SQLINTEGER case_sensitive; /* row only */ SQLCHAR * catalog_name; /* row only */ SQLSMALLINT concise_type; SQLPOINTER data_ptr; SQLSMALLINT datetime_interval_code; SQLINTEGER datetime_interval_precision; SQLLEN display_size; /* row only */ SQLSMALLINT fixed_prec_scale; SQLLEN * indicator_ptr; SQLCHAR * label; /* row only */ SQLULEN length; SQLCHAR * literal_prefix; /* row only */ SQLCHAR * literal_suffix; /* row only */ SQLCHAR * local_type_name; SQLCHAR * name; SQLSMALLINT nullable; SQLINTEGER num_prec_radix; SQLLEN octet_length; SQLLEN *octet_length_ptr; SQLSMALLINT parameter_type; /* param only */ SQLSMALLINT precision; SQLSMALLINT rowver; SQLSMALLINT scale; SQLCHAR * schema_name; /* row only */ SQLSMALLINT searchable; /* row only */ SQLCHAR * table_name; /* row only */ SQLSMALLINT type; SQLCHAR * type_name; SQLSMALLINT unnamed; SQLSMALLINT is_unsigned; SQLSMALLINT updatable; /* row only */ /* internal descriptor fields */ /* parameter-specific */ struct { /* value, value_length, and alloced are used for data * at exec parameters */ char *value; SQLINTEGER value_length; /* this parameter is data-at-exec. this is needed as cursor updates in ADO change the bind_offset_ptr between SQLSetPos() and the final call to SQLParamData() which makes it impossible for us to know any longer it was a data-at-exec param. */ char is_dae; my_bool alloced; /* Whether this parameter has been bound by the application * (if not, was created by dummy execution) */ my_bool real_param_done; } par; /* row-specific */ struct { MYSQL_FIELD * field; /* Used *only* by IRD */ ulong datalen; /* actual length, maintained for *each* row */ /* TODO ugly, but easiest way to handle memory */ SQLCHAR type_name[40]; } row; } DESCREC; /* Statement attributes */ typedef struct stmt_options { SQLUINTEGER cursor_type; SQLUINTEGER simulateCursor; SQLULEN max_length, max_rows; SQLUSMALLINT *rowStatusPtr_ex; /* set by SQLExtendedFetch */ my_bool retrieve_data; } STMT_OPTIONS; /* Environment handler */ typedef struct tagENV { SQLINTEGER odbc_ver; LIST *connections; MYERROR error; } ENV; /* Connection handler */ typedef struct tagDBC { ENV *env; MYSQL mysql; LIST *statements; LIST *exp_desc; /* explicit descriptors */ LIST list; STMT_OPTIONS stmt_options; MYERROR error; FILE *query_log; char st_error_prefix[255]; char *database; SQLUINTEGER login_timeout; time_t last_query_time; int txn_isolation; uint port; uint cursor_count; uint commit_flag; #ifdef THREAD pthread_mutex_t lock; #endif my_bool unicode; /* Whether SQL*ConnectW was used */ CHARSET_INFO *ansi_charset_info, /* 'ANSI' charset (SQL_C_CHAR) */ *cxn_charset_info; /* Connection charset ('ANSI' or utf-8) */ DataSource *ds; /* data source used to connect (parsed or stored) */ SQLULEN sql_select_limit; /* value of the sql_select_limit currently set for a session (SQLULEN)(-1) if wasn't set */ } DBC; /* Statement states */ enum MY_STATE { ST_UNKNOWN, ST_PREPARED, ST_PRE_EXECUTED, ST_EXECUTED }; enum MY_DUMMY_STATE { ST_DUMMY_UNKNOWN, ST_DUMMY_PREPARED, ST_DUMMY_EXECUTED }; /* Statement primary key handler for cursors */ typedef struct pk_column { char name[NAME_LEN+1]; my_bool bind_done; } MY_PK_COLUMN; /* Statement cursor handler */ typedef struct cursor { char *name; uint pk_count; my_bool pk_validated; MY_PK_COLUMN pkcol[MY_MAX_PK_PARTS]; } MYCURSOR; /* Main statement handler */ typedef struct tagSTMT { DBC FAR *dbc; MYSQL_RES *result; my_bool fake_result; MYSQL_ROW array,result_array,current_values; MYSQL_ROW (*fix_fields)(struct tagSTMT FAR* stmt,MYSQL_ROW row); MYSQL_FIELD *fields; MYSQL_ROW_OFFSET end_of_set; DYNAMIC_ARRAY param_pos; /* param placeholder positions */ LIST list; MYCURSOR cursor; MYERROR error; STMT_OPTIONS stmt_options; char *table_name; char *query,*query_end; unsigned long *lengths; /* used to set lengths if we shuffle field values of the resultset of auxiliary query or if we fix_fields. */ /* We save a copy of the original query before we modify it for 'WHERE CURRENT OF' cursor handling. */ char *orig_query,*orig_query_end; my_ulonglong affected_rows; long current_row; long cursor_row; char dae_type; /* data-at-exec type */ struct { uint column; /* Which column is being used with SQLGetData() */ char *source; /* Our current position in the source. */ uchar latest[7]; /* Latest character to be converted. */ int latest_bytes; /* Bytes of data in latest. */ int latest_used; /* Bytes of latest that have been used. */ ulong src_offset; /* @todo remove */ ulong dst_bytes; /* Length of data once it is all converted (in chars). */ ulong dst_offset; /* Current offset into dest. (ulong)~0L when not set. */ } getdata; uint *order,order_count,param_count,current_param,rows_found_in_set; enum MY_STATE state; enum MY_DUMMY_STATE dummy_state; DESC *ard; DESC *ird; DESC *apd; DESC *ipd; /* implicit descriptors */ DESC *imp_ard; DESC *imp_apd; /* APD for data-at-exec on SQLSetPos() */ DESC *setpos_apd; SQLSETPOSIROW setpos_row; SQLUSMALLINT setpos_lock; } STMT; extern char *default_locale, *decimal_point, *thousands_sep; extern uint decimal_point_length,thousands_sep_length; #ifndef _UNIX_ extern HINSTANCE NEAR s_hModule; /* DLL handle. */ #endif #ifdef THREAD extern pthread_mutex_t myodbc_lock; #endif /* Resource defines for "SQLDriverConnect" dialog box */ #define ID_LISTBOX 100 #define CONFIGDSN 1001 #define CONFIGDEFAULT 1002 #define EDRIVERCONNECT 1003 /* New data type definitions for compatibility with MySQL 5 */ #ifndef MYSQL_TYPE_NEWDECIMAL # define MYSQL_TYPE_NEWDECIMAL 246 #endif #ifndef MYSQL_TYPE_BIT # define MYSQL_TYPE_BIT 16 #endif #include "myutil.h" #include "stringutil.h" SQLRETURN SQL_API MySQLColAttribute(SQLHSTMT hstmt, SQLUSMALLINT column, SQLUSMALLINT attrib, SQLCHAR **char_attr, SQLLEN *num_attr); SQLRETURN SQL_API MySQLColumnPrivileges(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *column, SQLSMALLINT column_len); SQLRETURN SQL_API MySQLColumns(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *sztable, SQLSMALLINT table_len, SQLCHAR *column, SQLSMALLINT column_len); SQLRETURN SQL_API MySQLConnect(SQLHDBC hdbc, SQLWCHAR *szDSN, SQLSMALLINT cbDSN, SQLWCHAR *szUID, SQLSMALLINT cbUID, SQLWCHAR *szAuth, SQLSMALLINT cbAuth); SQLRETURN SQL_API MySQLDescribeCol(SQLHSTMT hstmt, SQLUSMALLINT column, SQLCHAR **name, SQLSMALLINT *need_free, SQLSMALLINT *type, SQLULEN *def, SQLSMALLINT *scale, SQLSMALLINT *nullable); SQLRETURN SQL_API MySQLDriverConnect(SQLHDBC hdbc, SQLHWND hwnd, SQLWCHAR *in, SQLSMALLINT in_len, SQLWCHAR *out, SQLSMALLINT out_max, SQLSMALLINT *out_len, SQLUSMALLINT completion); SQLRETURN SQL_API MySQLForeignKeys(SQLHSTMT hstmt, SQLCHAR *pkcatalog, SQLSMALLINT pkcatalog_len, SQLCHAR *pkschema, SQLSMALLINT pkschema_len, SQLCHAR *pktable, SQLSMALLINT pktable_len, SQLCHAR *fkcatalog, SQLSMALLINT fkcatalog_len, SQLCHAR *fkschema, SQLSMALLINT fkschema_len, SQLCHAR *fktable, SQLSMALLINT fktable_len); SQLCHAR *MySQLGetCursorName(HSTMT hstmt); SQLRETURN SQL_API MySQLGetInfo(SQLHDBC hdbc, SQLUSMALLINT fInfoType, SQLCHAR **char_info, SQLPOINTER num_info, SQLSMALLINT *value_len); SQLRETURN SQL_API MySQLGetConnectAttr(SQLHDBC hdbc, SQLINTEGER attrib, SQLCHAR **char_attr, SQLPOINTER num_attr); SQLRETURN MySQLGetDescField(SQLHDESC hdesc, SQLSMALLINT recnum, SQLSMALLINT fldid, SQLPOINTER valptr, SQLINTEGER buflen, SQLINTEGER *strlen); SQLRETURN SQL_API MySQLGetDiagField(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLSMALLINT identifier, SQLCHAR **char_value, SQLPOINTER num_value); SQLRETURN SQL_API MySQLGetDiagRec(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLCHAR **sqlstate, SQLINTEGER *native, SQLCHAR **message); SQLRETURN SQL_API MySQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength __attribute__((unused)), SQLINTEGER *StringLengthPtr); SQLRETURN SQL_API MySQLGetTypeInfo(SQLHSTMT hstmt, SQLSMALLINT fSqlType); SQLRETURN SQL_API MySQLPrepare(SQLHSTMT hstmt, SQLCHAR *query, SQLINTEGER len, my_bool dupe); SQLRETURN SQL_API MySQLPrimaryKeys(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len); SQLRETURN SQL_API MySQLProcedureColumns(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *proc, SQLSMALLINT proc_len, SQLCHAR *column, SQLSMALLINT column_len); SQLRETURN SQL_API MySQLProcedures(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *proc, SQLSMALLINT proc_len); SQLRETURN SQL_API MySQLSetConnectAttr(SQLHDBC hdbc, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLengthPtr); SQLRETURN SQL_API MySQLSetCursorName(SQLHSTMT hstmt, SQLCHAR *name, SQLSMALLINT len); SQLRETURN MySQLSetDescField(SQLHDESC hdesc, SQLSMALLINT recnum, SQLSMALLINT fldid, SQLPOINTER val, SQLINTEGER buflen); SQLRETURN SQL_API MySQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER len); SQLRETURN SQL_API MySQLSpecialColumns(SQLHSTMT hstmt, SQLUSMALLINT type, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLUSMALLINT scope, SQLUSMALLINT nullable); SQLRETURN SQL_API MySQLStatistics(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLUSMALLINT unique, SQLUSMALLINT accuracy); SQLRETURN SQL_API MySQLTablePrivileges(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len); SQLRETURN SQL_API MySQLTables(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *type, SQLSMALLINT type_len); #endif /* __DRIVER_H__ */ mysql-connector-odbc-5.1.10-src/driver/connect.c100644 15766 12 55135 11707541005 20307 0ustar00cteamstaff/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file connect.c @brief Connection functions. */ #include "driver.h" #include "installer.h" #include "stringutil.h" #include "MYODBCUtil.h" #ifndef CLIENT_NO_SCHEMA # define CLIENT_NO_SCHEMA 16 #endif typedef BOOL (*PromptFunc)(SQLHWND, SQLWCHAR *, SQLUSMALLINT, SQLWCHAR *, SQLSMALLINT, SQLSMALLINT *); /** Get the connection flags based on the driver options. @param[in] options Options flags @return Client flags suitable for @c mysql_real_connect(). */ unsigned long get_client_flags(DataSource *ds) { unsigned long flags= CLIENT_MULTI_RESULTS; if (ds->safe || ds->return_matching_rows) flags|= CLIENT_FOUND_ROWS; if (ds->no_catalog) flags|= CLIENT_NO_SCHEMA; if (ds->use_compressed_protocol) flags|= CLIENT_COMPRESS; if (ds->ignore_space_after_function_names) flags|= CLIENT_IGNORE_SPACE; if (ds->allow_multiple_statements) flags|= CLIENT_MULTI_STATEMENTS; if (ds->clientinteractive) flags|= CLIENT_INTERACTIVE; return flags; } /** If it was specified, set the character set for the connection. @param[in] dbc Database connection @param[in] charset Character set name */ SQLRETURN myodbc_set_initial_character_set(DBC *dbc, const char *charset) { if (dbc->unicode) { if (charset && charset[0]) { dbc->ansi_charset_info= get_charset_by_csname(charset, MYF(MY_CS_PRIMARY), MYF(0)); } charset= "utf8"; } if (charset && charset[0]) { if (mysql_set_character_set(&dbc->mysql, charset)) { set_dbc_error(dbc, "HY000", mysql_error(&dbc->mysql), mysql_errno(&dbc->mysql)); return SQL_ERROR; } } else { if (mysql_set_character_set(&dbc->mysql, dbc->ansi_charset_info->csname)) { set_dbc_error(dbc, "HY000", mysql_error(&dbc->mysql), mysql_errno(&dbc->mysql)); return SQL_ERROR; } } { MY_CHARSET_INFO my_charset; mysql_get_character_set_info(&dbc->mysql, &my_charset); dbc->cxn_charset_info= get_charset(my_charset.number, MYF(0)); } if (!dbc->unicode) dbc->ansi_charset_info= dbc->cxn_charset_info; /* We always set character_set_results to NULL so we can do our own conversion to the ANSI character set or Unicode. */ if (is_minimum_version(dbc->mysql.server_version, "4.1.1", 5) && odbc_stmt(dbc, "SET character_set_results = NULL") != SQL_SUCCESS) { return SQL_ERROR; } return SQL_SUCCESS; } /** Try to establish a connection to a MySQL server based on the data source configuration. @param[in] dbc Database connection @param[in] ds Data source information @return Standard SQLRETURN code. If it is @c SQL_SUCCESS or @c SQL_SUCCESS_WITH_INFO, a connection has been established. */ SQLRETURN myodbc_do_connect(DBC *dbc, DataSource *ds) { SQLRETURN rc= SQL_SUCCESS; MYSQL *mysql= &dbc->mysql; unsigned long flags; /* Use 'int' and fill all bits to avoid alignment Bug#25920 */ unsigned int opt_ssl_verify_server_cert = ~0; #ifdef WIN32 /* Detect if we are running with ADO present, and force on the FLAG_COLUMN_SIZE_S32 option if we are. */ if (GetModuleHandle("msado15.dll") != NULL) ds->limit_column_size= 1; /* Detect another problem specific to MS Access */ if (GetModuleHandle("msaccess.exe") != NULL) ds->default_bigint_bind_str= 1; #endif mysql_init(mysql); flags= get_client_flags(ds); /* Set other connection options */ if (ds->allow_big_results || ds->safe) /* max_allowed_packet is a magical mysql macro. */ max_allowed_packet= ~0L; if (ds->force_use_of_named_pipes) mysql_options(mysql, MYSQL_OPT_NAMED_PIPE, NullS); if (ds->read_options_from_mycnf) mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "odbc"); if (ds->initstmt && ds->initstmt[0]) { /* Check for SET NAMES */ if (is_set_names_statement((SQLCHAR *)ds_get_utf8attr(ds->initstmt, &ds->initstmt8))) { return set_dbc_error(dbc, "HY000", "SET NAMES not allowed by driver", 0); } mysql_options(mysql, MYSQL_INIT_COMMAND, ds->initstmt8); } if (dbc->login_timeout) mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&dbc->login_timeout); if (ds->readtimeout) mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (const char *) &ds->readtimeout); if (ds->writetimeout) mysql_options(mysql, MYSQL_OPT_WRITE_TIMEOUT, (const char *) &ds->writetimeout); /* set SSL parameters */ mysql_ssl_set(mysql, ds_get_utf8attr(ds->sslkey, &ds->sslkey8), ds_get_utf8attr(ds->sslcert, &ds->sslcert8), ds_get_utf8attr(ds->sslca, &ds->sslca8), ds_get_utf8attr(ds->sslcapath, &ds->sslcapath8), ds_get_utf8attr(ds->sslcipher, &ds->sslcipher8)); if (ds->sslverify) mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *)&opt_ssl_verify_server_cert); { /* Get the ANSI charset info before we change connection to UTF-8. */ MY_CHARSET_INFO my_charset; mysql_get_character_set_info(&dbc->mysql, &my_charset); dbc->ansi_charset_info= get_charset(my_charset.number, MYF(0)); } /* We always use utf8 for the connection, and change it afterwards if needed. */ mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "utf8"); dbc->cxn_charset_info= utf8_charset_info; if (!mysql_real_connect(mysql, ds_get_utf8attr(ds->server, &ds->server8), ds_get_utf8attr(ds->uid, &ds->uid8), ds_get_utf8attr(ds->pwd, &ds->pwd8), ds_get_utf8attr(ds->database, &ds->database8), ds->port, ds_get_utf8attr(ds->socket, &ds->socket8), flags)) { set_dbc_error(dbc, "HY000", mysql_error(mysql), mysql_errno(mysql)); translate_error(dbc->error.sqlstate, MYERR_S1000, mysql_errno(mysql)); return SQL_ERROR; } if (!is_minimum_version(dbc->mysql.server_version, "4.1.1", 5)) { mysql_close(mysql); set_dbc_error(dbc, "08001", "Driver does not support server versions under 4.1.1", 0); return SQL_ERROR; } rc= myodbc_set_initial_character_set(dbc, ds_get_utf8attr(ds->charset, &ds->charset8)); if (!SQL_SUCCEEDED(rc)) { /** @todo set failure reason */ goto error; } /* The MySQL server has a workaround for old versions of Microsoft Access (and possibly other products) that is no longer necessary, but is unfortunately enabled by default. We have to turn it off, or it causes other problems. */ if (!ds->auto_increment_null_search && odbc_stmt(dbc, "SET SQL_AUTO_IS_NULL = 0") != SQL_SUCCESS) { /** @todo set error reason */ goto error; } dbc->ds= ds; /* init all needed UTF-8 strings */ ds_get_utf8attr(ds->name, &ds->name8); ds_get_utf8attr(ds->server, &ds->server8); ds_get_utf8attr(ds->uid, &ds->uid8); ds_get_utf8attr(ds->pwd, &ds->pwd8); ds_get_utf8attr(ds->socket, &ds->socket8); if (ds->database) dbc->database= my_strdup(ds_get_utf8attr(ds->database, &ds->database8), MYF(MY_WME)); if (ds->save_queries && !dbc->query_log) dbc->query_log= init_query_log(); /* Set the statement error prefix based on the server version. */ strxmov(dbc->st_error_prefix, MYODBC3_ERROR_PREFIX, "[mysqld-", mysql->server_version, "]", NullS); /* This needs to be set after connection, or it doesn't stick. */ if (ds->auto_reconnect) { my_bool reconnect= 1; mysql_options(mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); } /* Make sure autocommit is set as configured. */ if (dbc->commit_flag == CHECK_AUTOCOMMIT_OFF) { if (!trans_supported(dbc) || ds->disable_transactions) { rc= SQL_SUCCESS_WITH_INFO; dbc->commit_flag= CHECK_AUTOCOMMIT_ON; set_conn_error(dbc, MYERR_01S02, "Transactions are not enabled, option value " "SQL_AUTOCOMMIT_OFF changed to SQL_AUTOCOMMIT_ON", 0); } else if (autocommit_on(dbc) && mysql_autocommit(mysql, FALSE)) { /** @todo set error */ goto error; } } else if ((dbc->commit_flag == CHECK_AUTOCOMMIT_ON) && trans_supported(dbc) && !autocommit_on(dbc)) { if (mysql_autocommit(mysql, TRUE)) { /** @todo set error */ goto error; } } /* Set transaction isolation as configured. */ if (dbc->txn_isolation != DEFAULT_TXN_ISOLATION) { char buff[80]; const char *level; if (dbc->txn_isolation & SQL_TXN_SERIALIZABLE) level= "SERIALIZABLE"; else if (dbc->txn_isolation & SQL_TXN_REPEATABLE_READ) level= "REPEATABLE READ"; else if (dbc->txn_isolation & SQL_TXN_READ_COMMITTED) level= "READ COMMITTED"; else level= "READ UNCOMMITTED"; if (trans_supported(dbc)) { sprintf(buff, "SET SESSION TRANSACTION ISOLATION LEVEL %s", level); if (odbc_stmt(dbc, buff) != SQL_SUCCESS) { /** @todo set error reason */ goto error; } } else { dbc->txn_isolation= SQL_TXN_READ_UNCOMMITTED; rc= SQL_SUCCESS_WITH_INFO; set_conn_error(dbc, MYERR_01S02, "Transactions are not enabled, so transaction isolation " "was ignored.", 0); } } return rc; error: mysql_close(mysql); return SQL_ERROR; } /** Establish a connection to a data source. @param[in] hdbc Connection handle @param[in] szDSN Data source name (in connection charset) @param[in] cbDSN Length of data source name in bytes or @c SQL_NTS @param[in] szUID User identifier (in connection charset) @param[in] cbUID Length of user identifier in bytes or @c SQL_NTS @param[in] szAuth Authentication string (password) (in connection charset) @param[in] cbAuth Length of authentication string in bytes or @c SQL_NTS @return Standard ODBC success codes @since ODBC 1.0 @since ISO SQL 92 */ SQLRETURN SQL_API MySQLConnect(SQLHDBC hdbc, SQLWCHAR *szDSN, SQLSMALLINT cbDSN, SQLWCHAR *szUID, SQLSMALLINT cbUID, SQLWCHAR *szAuth, SQLSMALLINT cbAuth) { SQLRETURN rc; DBC *dbc= (DBC *)hdbc; DataSource *ds; #ifdef NO_DRIVERMANAGER return set_dbc_error(dbc, "HY000", "SQLConnect requires DSN and driver manager", 0); #else /* Can't connect if we're already connected. */ if (is_connected(dbc)) return set_conn_error(hdbc, MYERR_08002, NULL, 0); /* Reset error state */ CLEAR_DBC_ERROR(dbc); if (szDSN && !szDSN[0]) { return set_conn_error(hdbc, MYERR_S1000, "Invalid connection parameters", 0); } ds= ds_new(); ds_set_strnattr(&ds->name, szDSN, cbDSN); ds_set_strnattr(&ds->uid, szUID, cbUID); ds_set_strnattr(&ds->pwd, szAuth, cbAuth); ds_lookup(ds); rc= myodbc_do_connect(dbc, ds); if (!dbc->ds) ds_delete(ds); return rc; #endif } /** An alternative to SQLConnect that allows specifying more of the connection parameters, and whether or not to prompt the user for more information using the setup library. NOTE: The prompting done in this function using MYODBCUTIL_DATASOURCE has been observed to cause an access violation outside of our code (specifically in Access after prompting, before table link list). This has only been seen when setting a breakpoint on this function. @param[in] hdbc Handle of database connection @param[in] hwnd Window handle. May be @c NULL if no prompting will be done. @param[in] szConnStrIn The connection string @param[in] cbConnStrIn The length of the connection string in characters @param[out] szConnStrOut Pointer to a buffer for the completed connection string. Must be at least 1024 characters. @param[in] cbConnStrOutMax Length of @c szConnStrOut buffer in characters @param[out] pcbConnStrOut Pointer to buffer for returning length of completed connection string, in characters @param[in] fDriverCompletion Complete mode, one of @cSQL_DRIVER_PROMPT, @c SQL_DRIVER_COMPLETE, @c SQL_DRIVER_COMPLETE_REQUIRED, or @cSQL_DRIVER_NOPROMPT @return Standard ODBC return codes @since ODBC 1.0 @since ISO SQL 92 */ SQLRETURN SQL_API MySQLDriverConnect(SQLHDBC hdbc, SQLHWND hwnd, SQLWCHAR *szConnStrIn, SQLSMALLINT cbConnStrIn, SQLWCHAR *szConnStrOut, SQLSMALLINT cbConnStrOutMax, SQLSMALLINT *pcbConnStrOut, SQLUSMALLINT fDriverCompletion) { SQLRETURN rc= SQL_SUCCESS; DBC *dbc= (DBC *)hdbc; DataSource *ds= ds_new(); /* We may have to read driver info to find the setup library. */ Driver *pDriver= driver_new(); SQLWCHAR *prompt_instr= NULL; size_t prompt_inlen; BOOL bPrompt= FALSE; HMODULE hModule= NULL; if (cbConnStrIn != SQL_NTS) szConnStrIn= sqlwchardup(szConnStrIn, cbConnStrIn); /* Parse the incoming string */ if (ds_from_kvpair(ds, szConnStrIn, (SQLWCHAR)';')) { rc= set_dbc_error(dbc, "HY000", "Failed to parse the incoming connect string.", 0); goto error; } #ifndef NO_DRIVERMANAGER /* If the connection string contains the DSN keyword, the driver retrieves the information for the specified data source (and merges it into the connection info with the provided connection info having precedence). This also allows us to get pszDRIVER (if not already given). */ if (ds->name) ds_lookup(ds); #endif /* If FLAG_NO_PROMPT is not set, force prompting off. */ if (ds->dont_prompt_upon_connect) fDriverCompletion= SQL_DRIVER_NOPROMPT; /* We only prompt if we need to. A not-so-obvious gray area is when SQL_DRIVER_COMPLETE or SQL_DRIVER_COMPLETE_REQUIRED was specified along without a server or password - particularly password. These can work with defaults/blank but many callers expect prompting when these are blank. So we compromise; we try to connect and if it works we say its ok otherwise we go to a prompt. */ switch (fDriverCompletion) { case SQL_DRIVER_PROMPT: bPrompt= TRUE; break; case SQL_DRIVER_COMPLETE: case SQL_DRIVER_COMPLETE_REQUIRED: if (myodbc_do_connect(dbc, ds) == SQL_SUCCESS) goto connected; bPrompt= TRUE; break; case SQL_DRIVER_NOPROMPT: bPrompt= FALSE; break; default: rc= set_dbc_error(hdbc, "HY110", "Invalid driver completion.", 0); goto error; } #ifdef __APPLE__ /* We don't support prompting on Mac OS X. */ if (bPrompt) { rc= set_dbc_error(hdbc, "HY000", "Prompting is not supported on this platform. " "Please provide all required connect information.", 0); goto error; } #endif if (bPrompt) { PromptFunc pFunc; /* We can not present a prompt unless we can lookup the name of the setup library file name. This means we need a DRIVER. We try to look it up using a DSN (above) or, hopefully, get one in the connection string. This will, when we need to prompt, trump the ODBC rule where we can connect with a DSN which does not exist. A possible solution would be to hard-code some fall-back value for ds->pszDRIVER. */ if (!ds->driver) { char szError[1024]; sprintf(szError, "Could not determine the driver name; " "could not lookup setup library. DSN=(%s)\n", ds_get_utf8attr(ds->name, &ds->name8)); rc= set_dbc_error(hdbc, "HY000", szError, 0); goto error; } #ifndef NO_DRIVERMANAGER /* We can not present a prompt if we have a null window handle. */ if (!hwnd) { rc= set_dbc_error(hdbc, "IM008", "Invalid window handle", 0); goto error; } /* if given a named DSN, we will have the path in the DRIVER field */ if (ds->name) sqlwcharncpy(pDriver->lib, ds->driver, ODBCDRIVER_STRLEN); /* otherwise, it's the driver name */ else sqlwcharncpy(pDriver->name, ds->driver, ODBCDRIVER_STRLEN); if (driver_lookup(pDriver)) { char sz[1024]; sprintf(sz, "Could not find driver '%s' in system information.", ds_get_utf8attr(ds->driver, &ds->driver8)); rc= set_dbc_error(hdbc, "IM003", sz, 0); goto error; } if (!*pDriver->setup_lib) #endif { rc= set_dbc_error(hdbc, "HY000", "Could not determine the file name of setup library.", 0); goto error; } /* We dynamically load the setup library so the driver itself does not depend on GUI libraries. */ #ifndef WIN32 /* lt_dlinit(); */ #endif if (!(hModule= LoadLibrary(ds_get_utf8attr(pDriver->setup_lib, &pDriver->setup_lib8)))) { char sz[1024]; sprintf(sz, "Could not load the setup library '%s'.", ds_get_utf8attr(pDriver->setup_lib, &pDriver->setup_lib8)); rc= set_dbc_error(hdbc, "HY000", sz, 0); goto error; } pFunc= (PromptFunc)GetProcAddress(hModule, "Driver_Prompt"); if (pFunc == NULL) { #ifdef WIN32 LPVOID pszMsg; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pszMsg, 0, NULL); rc= set_dbc_error(hdbc, "HY000", (char *)pszMsg, 0); LocalFree(pszMsg); #else rc= set_dbc_error(hdbc, "HY000", dlerror(), 0); #endif goto error; } /* create a string for prompting, and add driver manually */ prompt_inlen= ds_to_kvpair_len(ds) + sqlwcharlen(W_DRIVER_PARAM) + sqlwcharlen(ds->driver) + 1; prompt_instr= (SQLWCHAR *) my_malloc(prompt_inlen * sizeof(SQLWCHAR), MYF(0)); if (ds_to_kvpair(ds, prompt_instr, prompt_inlen, ';') == -1) { rc= set_dbc_error(dbc, "HY000", "Failed to prepare prompt input.", 0); goto error; } prompt_inlen-= sqlwcharlen(prompt_instr); sqlwcharncat2(prompt_instr, W_DRIVER_PARAM, &prompt_inlen); sqlwcharncat2(prompt_instr, ds->driver, &prompt_inlen); if (!pFunc(hwnd, prompt_instr, fDriverCompletion, szConnStrOut, cbConnStrOutMax, pcbConnStrOut)) { set_dbc_error(hdbc, "HY000", "User cancelled.", 0); rc= SQL_NO_DATA; goto error; } /* refresh our DataSource */ ds_delete(ds); ds= ds_new(); if (ds_from_kvpair(ds, szConnStrOut, ';')) { rc= set_dbc_error(dbc, "HY000", "Failed to parse the prompt output string.", 0); goto error; } } if ((rc= myodbc_do_connect(dbc, ds)) != SQL_SUCCESS) goto error; connected: /* copy input to output if connected without prompting */ if (!bPrompt && szConnStrOut && cbConnStrOutMax) { size_t inlen= (sqlwcharlen(szConnStrIn) + 1) * sizeof(SQLWCHAR); size_t copylen= myodbc_min((size_t)cbConnStrOutMax, inlen); memcpy(szConnStrOut, szConnStrIn, copylen); /* term needed if possibly truncated */ szConnStrOut[(copylen / sizeof(SQLWCHAR)) - 1] = 0; if (pcbConnStrOut) *pcbConnStrOut= (copylen / sizeof(SQLWCHAR)) - 1; } /* return SQL_SUCCESS_WITH_INFO if truncated output string */ if (pcbConnStrOut && cbConnStrOutMax - sizeof(SQLWCHAR) == *pcbConnStrOut * sizeof(SQLWCHAR)) { set_dbc_error(hdbc, "01004", "String data, right truncated.", 0); rc= SQL_SUCCESS_WITH_INFO; } error: if (hModule) FreeLibrary(hModule); if (cbConnStrIn != SQL_NTS) x_free(szConnStrIn); driver_delete(pDriver); /* delete data source unless connected */ if (!dbc->ds) ds_delete(ds); x_free(prompt_instr); return rc; } /** Discover and enumerate the attributes and attribute values required to connect. @return Always returns @c SQL_ERROR, because the driver does not support this. @since ODBC 1.0 */ SQLRETURN SQL_API SQLBrowseConnect(SQLHDBC hdbc, SQLCHAR FAR *szConnStrIn __attribute__((unused)), SQLSMALLINT cbConnStrIn __attribute__((unused)), SQLCHAR FAR *szConnStrOut __attribute__((unused)), SQLSMALLINT cbConnStrOutMax __attribute__((unused)), SQLSMALLINT FAR *pcbConnStrOut __attribute__((unused))) { return set_conn_error(hdbc,MYERR_S1000, "Driver does not support this API", 0); } /** Disconnect a connection. @param[in] hdbc Connection handle @return Standard ODBC return codes @since ODBC 1.0 @since ISO SQL 92 */ SQLRETURN SQL_API SQLDisconnect(SQLHDBC hdbc) { LIST *list_element, *next_element; DBC FAR *dbc= (DBC FAR*) hdbc; for (list_element= dbc->statements; list_element; list_element= next_element) { next_element= list_element->next; my_SQLFreeStmt((SQLHSTMT)list_element->data, SQL_DROP); } mysql_close(&dbc->mysql); if (dbc->ds->save_queries) end_query_log(dbc->query_log); x_free(dbc->database); assert(dbc->ds); ds_delete(dbc->ds); dbc->ds= NULL; dbc->database= NULL; return SQL_SUCCESS; } mysql-connector-odbc-5.1.10-src/driver/catalog.h100644 15766 12 16562 11707541005 20276 0ustar00cteamstaff/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file catalog.h @brief some definitions required for catalog functions */ /** enums for resultsets returned by catalog functions - we don't want magic numbers */ /* SQLColumns */ enum myodbcColumns {mycTABLE_CAT= 0, mycTABLE_SCHEM, mycTABLE_NAME, /*3*/ mycCOLUMN_NAME, mycDATA_TYPE, mycTYPE_NAME, /*6*/ mycCOLUMN_SIZE, mycBUFFER_LENGTH, mycDECIMAL_DIGITS, /*9*/ mycNUM_PREC_RADIX, mycNULLABLE, mycREMARKS, /*12*/mycCOLUMN_DEF, mycSQL_DATA_TYPE, mycSQL_DATETIME_SUB, /*15*/mycCHAR_OCTET_LENGTH, mycORDINAL_POSITION, mycIS_NULLABLE }; /* SQLProcedureColumns */ enum myodbcProcColumns {mypcPROCEDURE_CAT= 0, mypcPROCEDURE_SCHEM, mypcPROCEDURE_NAME, /*3*/ mypcCOLUMN_NAME, mypcCOLUMN_TYPE, mypcDATA_TYPE, /*6*/ mypcTYPE_NAME, mypcCOLUMN_SIZE, mypcBUFFER_LENGTH, /*9*/ mypcDECIMAL_DIGITS, mypcNUM_PREC_RADIX, mypcNULLABLE, /*12*/mypcREMARKS, mypcCOLUMN_DEF, mypcSQL_DATA_TYPE, /*15*/mypcSQL_DATETIME_SUB, mypcCHAR_OCTET_LENGTH,mypcORDINAL_POSITION, /*18*/mypcIS_NULLABLE }; /* Some common(for i_s/no_i_s) helper functions */ const char *my_next_token(const char *prev_token, char **token, char *data, const char chr); SQLRETURN create_empty_fake_resultset(STMT *stmt, MYSQL_ROW rowval, size_t rowsize, MYSQL_FIELD *fields, uint fldcnt); SQLRETURN create_fake_resultset(STMT *stmt, MYSQL_ROW rowval, size_t rowsize, my_ulonglong rowcnt, MYSQL_FIELD *fields, uint fldcnt); my_bool server_has_i_s(DBC FAR *dbc); /* no_i_s functions */ SQLRETURN mysql_columns(STMT *hstmt, SQLCHAR *szCatalog, SQLSMALLINT cbCatalog, SQLCHAR *szSchema __attribute__((unused)), SQLSMALLINT cbSchema __attribute__((unused)), SQLCHAR *szTable, SQLSMALLINT cbTable, SQLCHAR *szColumn, SQLSMALLINT cbColumn); SQLRETURN mysql_list_column_priv(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *column, SQLSMALLINT column_len); SQLRETURN mysql_list_table_priv(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len); MYSQL_RES *mysql_table_status(STMT *stmt, SQLCHAR *catalog, SQLSMALLINT catalog_length, SQLCHAR *table, SQLSMALLINT table_length, my_bool wildcard, my_bool show_tables, my_bool show_views); MYSQL_RES *mysql_table_status_show(STMT *stmt, SQLCHAR *catalog, SQLSMALLINT catalog_length, SQLCHAR *table, SQLSMALLINT table_length, my_bool wildcard); SQLRETURN mysql_foreign_keys(SQLHSTMT hstmt, SQLCHAR *szPkCatalogName __attribute__((unused)), SQLSMALLINT cbPkCatalogName __attribute__((unused)), SQLCHAR *szPkSchemaName __attribute__((unused)), SQLSMALLINT cbPkSchemaName __attribute__((unused)), SQLCHAR *szPkTableName, SQLSMALLINT cbPkTableName, SQLCHAR *szFkCatalogName, SQLSMALLINT cbFkCatalogName, SQLCHAR *szFkSchemaName __attribute__((unused)), SQLSMALLINT cbFkSchemaName __attribute__((unused)), SQLCHAR *szFkTableName, SQLSMALLINT cbFkTableName); SQLRETURN mysql_primary_keys(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len); SQLRETURN mysql_procedure_columns(SQLHSTMT hstmt, SQLCHAR *szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR *szSchemaName __attribute__((unused)), SQLSMALLINT cbSchemaName __attribute__((unused)), SQLCHAR *szProcName, SQLSMALLINT cbProcName, SQLCHAR *szColumnName, SQLSMALLINT cbColumnName); SQLRETURN mysql_special_columns(SQLHSTMT hstmt, SQLUSMALLINT fColType, SQLCHAR *szTableQualifier, SQLSMALLINT cbTableQualifier, SQLCHAR *szTableOwner __attribute__((unused)), SQLSMALLINT cbTableOwner __attribute__((unused)), SQLCHAR *szTableName, SQLSMALLINT cbTableName, SQLUSMALLINT fScope __attribute__((unused)), SQLUSMALLINT fNullable __attribute__((unused))); /* @purpose : retrieves a list of statistics about a single table and the indexes associated with the table. The driver returns the information as a result set. */ SQLRETURN mysql_statistics(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len, SQLUSMALLINT fUnique, SQLUSMALLINT fAccuracy __attribute__((unused))); SQLRETURN mysql_tables(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *type, SQLSMALLINT type_len); mysql-connector-odbc-5.1.10-src/driver/ansi.c100644 15766 12 114447 11707541005 17632 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file ansi.c @brief Entry points for ANSI versions of ODBC functions */ #include "driver.h" #define NOT_IMPLEMENTED \ return SQL_ERROR SQLRETURN SQL_API SQLColAttributeImpl(SQLHSTMT hstmt, SQLUSMALLINT column, SQLUSMALLINT field, SQLPOINTER char_attr, SQLSMALLINT char_attr_max, SQLSMALLINT *char_attr_len, SQLLEN *num_attr); SQLRETURN SQL_API SQLGetConnectAttrImpl(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_max, SQLINTEGER *value_len); SQLRETURN SQL_API SQLGetDiagRecImpl(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLCHAR *sqlstate, SQLINTEGER *native_error, SQLCHAR *message, SQLSMALLINT message_max, SQLSMALLINT *message_len); SQLRETURN SQL_API SQLPrepareImpl(SQLHSTMT hstmt, SQLCHAR *str, SQLINTEGER str_len); SQLRETURN SQL_API SQLSetConnectAttrImpl(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_len); SQLRETURN SQL_API SQLColAttribute(SQLHSTMT hstmt, SQLUSMALLINT column, SQLUSMALLINT field, SQLPOINTER char_attr, SQLSMALLINT char_attr_max, SQLSMALLINT *char_attr_len, #ifdef USE_SQLCOLATTRIBUTE_SQLLEN_PTR SQLLEN *num_attr #else SQLPOINTER num_attr #endif ) { return SQLColAttributeImpl(hstmt, column, field, char_attr, char_attr_max, char_attr_len, num_attr); } SQLRETURN SQL_API SQLColAttributeImpl(SQLHSTMT hstmt, SQLUSMALLINT column, SQLUSMALLINT field, SQLPOINTER char_attr, SQLSMALLINT char_attr_max, SQLSMALLINT *char_attr_len, SQLLEN *num_attr) { STMT *stmt= (STMT *)hstmt; SQLCHAR *value= NULL; SQLINTEGER len= SQL_NTS; uint errors; SQLRETURN rc= MySQLColAttribute(hstmt, column, field, &value, num_attr); if (value) { my_bool free_value= FALSE; SQLCHAR *old_value= value; if (stmt->dbc->ansi_charset_info->number != stmt->dbc->cxn_charset_info->number) { value= sqlchar_as_sqlchar(stmt->dbc->ansi_charset_info, stmt->dbc->cxn_charset_info, value, &len, &errors); if (free_value) x_free(old_value); free_value= TRUE; } else len= strlen((char *)value); if (len > char_attr_max - 1) rc= set_error(stmt, MYERR_01004, NULL, 0); if (char_attr && char_attr_max > 1) strmake((char *)char_attr, (char *)value, char_attr_max - 1); if (char_attr_len) *char_attr_len= (SQLSMALLINT)len; if (free_value) x_free(value); } return rc; } SQLRETURN SQL_API SQLColAttributes(SQLHSTMT hstmt, SQLUSMALLINT column, SQLUSMALLINT field, SQLPOINTER char_attr, SQLSMALLINT char_attr_max, SQLSMALLINT *char_attr_len, SQLLEN *num_attr) { return SQLColAttributeImpl(hstmt, column, field, char_attr, char_attr_max, char_attr_len, num_attr); } SQLRETURN SQL_API SQLColumnPrivileges(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *column, SQLSMALLINT column_len) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { SQLINTEGER len= SQL_NTS; uint errors= 0; if (catalog) { catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (schema) { schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (table) { table= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; len= SQL_NTS; } if (column) { column= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, column, &len, &errors); column_len= (SQLSMALLINT)len; } } rc= MySQLColumnPrivileges(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, column, column_len); if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { x_free(catalog); x_free(schema); x_free(table); x_free(column); } return rc; } SQLRETURN SQL_API SQLColumns(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *column, SQLSMALLINT column_len) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { SQLINTEGER len= SQL_NTS; uint errors= 0; if (catalog) { catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (schema) { schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (table) { table= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; len= SQL_NTS; } if (column) { column= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, column, &len, &errors); column_len= (SQLSMALLINT)len; len= SQL_NTS; } } rc= MySQLColumns(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, column, column_len); if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { x_free(catalog); x_free(schema); x_free(table); x_free(column); } return rc; } SQLRETURN SQL_API SQLConnect(SQLHDBC hdbc, SQLCHAR *dsn, SQLSMALLINT dsn_len_in, SQLCHAR *user, SQLSMALLINT user_len_in, SQLCHAR *auth, SQLSMALLINT auth_len_in) { uint errors; SQLRETURN rc; SQLINTEGER dsn_len= dsn_len_in, user_len= user_len_in, auth_len= auth_len_in; SQLWCHAR *dsnw= sqlchar_as_sqlwchar(default_charset_info, dsn, &dsn_len, &errors); SQLWCHAR *userw= sqlchar_as_sqlwchar(default_charset_info, user, &user_len, &errors); SQLWCHAR *authw= sqlchar_as_sqlwchar(default_charset_info, auth, &auth_len, &errors); rc= MySQLConnect(hdbc, dsnw, dsn_len_in, userw, user_len_in, authw, auth_len_in); x_free(dsnw); x_free(userw); x_free(authw); return rc; } SQLRETURN SQL_API SQLDescribeCol(SQLHSTMT hstmt, SQLUSMALLINT column, SQLCHAR *name, SQLSMALLINT name_max, SQLSMALLINT *name_len, SQLSMALLINT *type, SQLULEN *size, SQLSMALLINT *scale, SQLSMALLINT *nullable) { STMT *stmt= (STMT *)hstmt; SQLCHAR *value= NULL; SQLINTEGER len= SQL_NTS; SQLSMALLINT free_value; uint errors; SQLRETURN rc= MySQLDescribeCol(hstmt, column, &value, &free_value, type, size, scale, nullable); if (free_value == -1) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } if (value) { SQLCHAR *old_value= value; if (stmt->dbc->ansi_charset_info->number != stmt->dbc->cxn_charset_info->number) { value= sqlchar_as_sqlchar(stmt->dbc->cxn_charset_info, stmt->dbc->ansi_charset_info, value, &len, &errors); if (free_value) x_free(old_value); free_value= TRUE; } else len= strlen((char *)value); if (len > name_max - 1) rc= set_error(stmt, MYERR_01004, NULL, 0); if (name && name_max > 1) strmake((char *)name, (char *)value, name_max - 1); if (name_len) *name_len= (SQLSMALLINT)len; if (free_value) x_free(value); } return rc; } SQLRETURN SQL_API SQLDriverConnect(SQLHDBC hdbc, SQLHWND hwnd, SQLCHAR *in, SQLSMALLINT in_len, SQLCHAR *out, SQLSMALLINT out_max, SQLSMALLINT *out_len, SQLUSMALLINT completion) { SQLRETURN rc; uint errors; SQLWCHAR *outw= NULL; SQLINTEGER inw_len; SQLWCHAR *inw; SQLSMALLINT outw_max, dummy_out; if (in_len == SQL_NTS) in_len= strlen((char *)in); if (!out_len) out_len= &dummy_out; inw_len= in_len; inw= sqlchar_as_sqlwchar(utf8_charset_info, in, &inw_len, &errors); outw_max= (sizeof(SQLWCHAR) * out_max) / MAX_BYTES_PER_UTF8_CP; if (outw_max) { outw= (SQLWCHAR *)my_malloc(sizeof(SQLWCHAR) * out_max, MYF(0)); if (!outw) { rc= set_dbc_error((DBC *)hdbc, "HY001", NULL, 0); goto error; } } rc= MySQLDriverConnect(hdbc, hwnd, inw, in_len, outw, out_max, out_len, completion); #ifdef WIN32 /* Microsoft ADO/VB? specifies in and out to be the same, but then gives a nonsense value for out_max. Just don't do anything in this case. @todo verify that this actually happens. */ if (rc == SQL_SUCCESS && out && in != out) #else if (rc == SQL_SUCCESS && out) #endif { uint errors; /* Now we have to convert SQLWCHAR back into a SQLCHAR. */ *out_len= sqlwchar_as_sqlchar_buf(default_charset_info, out, out_max, outw, *out_len, &errors); /* TODO what to do with errors? */ if (*out_len > out_max - 1) rc= set_dbc_error((DBC *)hdbc, "01004", NULL, 0); } error: x_free(outw); x_free(inw); return rc; } SQLRETURN SQL_API SQLError(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLCHAR *sqlstate, SQLINTEGER *native_error, SQLCHAR *message, SQLSMALLINT message_max, SQLSMALLINT *message_len) { SQLRETURN rc= SQL_INVALID_HANDLE; if (hstmt) { rc= SQLGetDiagRecImpl(SQL_HANDLE_STMT, hstmt, NEXT_STMT_ERROR(hstmt), sqlstate, native_error, message, message_max, message_len); } else if (hdbc) { rc= SQLGetDiagRecImpl(SQL_HANDLE_DBC, hdbc, NEXT_DBC_ERROR(hdbc), sqlstate, native_error, message, message_max, message_len); } else if (henv) { rc= SQLGetDiagRecImpl(SQL_HANDLE_ENV, henv, NEXT_ENV_ERROR(henv), sqlstate, native_error, message, message_max, message_len); } return rc; } SQLRETURN SQL_API SQLExecDirect(SQLHSTMT hstmt, SQLCHAR *str, SQLINTEGER str_len) { int error; if ((error= SQLPrepareImpl(hstmt, str, str_len))) return error; error= my_SQLExecute((STMT *)hstmt); return error; } SQLRETURN SQL_API SQLForeignKeys(SQLHSTMT hstmt, SQLCHAR *pk_catalog, SQLSMALLINT pk_catalog_len, SQLCHAR *pk_schema, SQLSMALLINT pk_schema_len, SQLCHAR *pk_table, SQLSMALLINT pk_table_len, SQLCHAR *fk_catalog, SQLSMALLINT fk_catalog_len, SQLCHAR *fk_schema, SQLSMALLINT fk_schema_len, SQLCHAR *fk_table, SQLSMALLINT fk_table_len) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { SQLINTEGER len= SQL_NTS; uint errors= 0; if (pk_catalog) { pk_catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, pk_catalog, &len, &errors); pk_catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (pk_schema) { pk_schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, pk_schema, &len, &errors); pk_schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (pk_table) { pk_table= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, pk_table, &len, &errors); pk_table_len= (SQLSMALLINT)len; len= SQL_NTS; } if (fk_catalog) { fk_catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, fk_catalog, &len, &errors); fk_catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (fk_schema) { fk_schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, fk_schema, &len, &errors); fk_schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (fk_table) { fk_table= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, fk_table, &len, &errors); fk_table_len= (SQLSMALLINT)len; len= SQL_NTS; } } rc= MySQLForeignKeys(hstmt, pk_catalog, pk_catalog_len, pk_schema, pk_schema_len, pk_table, pk_table_len, fk_catalog, fk_catalog_len, fk_schema, fk_schema_len, fk_table, fk_table_len); if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { x_free(pk_catalog); x_free(pk_schema); x_free(pk_table); x_free(fk_catalog); x_free(fk_schema); x_free(fk_table); } return rc; } SQLRETURN SQL_API SQLGetConnectAttr(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_max, SQLINTEGER *value_len) { return SQLGetConnectAttrImpl(hdbc, attribute, value, value_max, value_len); } SQLRETURN SQL_API SQLGetConnectAttrImpl(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_max, SQLINTEGER *value_len) { DBC *dbc= (DBC *)hdbc; SQLCHAR *char_value= NULL; SQLRETURN rc= MySQLGetConnectAttr(hdbc, attribute, &char_value, value); if (char_value) { SQLSMALLINT free_value= FALSE; SQLINTEGER len= SQL_NTS; uint errors; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { char_value= sqlchar_as_sqlchar(dbc->cxn_charset_info, dbc->ansi_charset_info, char_value, &len, &errors); free_value= TRUE; } else len= strlen((char *)char_value); if (len > value_max - 1) rc= set_conn_error(dbc, MYERR_01004, NULL, 0); if (value && value_max > 1) strmake((char *)value, (char *)char_value, value_max - 1); if (value_len) *value_len= len; if (free_value) x_free(char_value); } return rc; } SQLRETURN SQL_API SQLGetConnectOption(SQLHDBC hdbc, SQLUSMALLINT option, SQLPOINTER value) { return SQLGetConnectAttrImpl(hdbc, option, value, ((option == SQL_ATTR_CURRENT_CATALOG) ? SQL_MAX_OPTION_STRING_LENGTH : 0), NULL); } SQLRETURN SQL_API SQLGetCursorName(SQLHSTMT hstmt, SQLCHAR *cursor, SQLSMALLINT cursor_max, SQLSMALLINT *cursor_len) { STMT *stmt= (STMT *)hstmt; SQLCHAR *name; my_bool free_name= FALSE; SQLINTEGER len; uint errors; CLEAR_STMT_ERROR(stmt); if (cursor_max < 0) return set_error(stmt, MYERR_S1090, NULL, 0); if (stmt->dbc->ansi_charset_info->number == stmt->dbc->cxn_charset_info->number) { name= MySQLGetCursorName(hstmt); len= strlen((char *)name); } else { name= sqlchar_as_sqlchar(stmt->dbc->cxn_charset_info, stmt->dbc->ansi_charset_info, MySQLGetCursorName(hstmt), &len, &errors); free_name= TRUE; } if (cursor && cursor_max > 1) strmake((char *)cursor, (char *)name, cursor_max - 1); if (cursor_len) *cursor_len= (SQLSMALLINT)len; if (free_name) x_free(name); /* Warn if name truncated */ if (len > cursor_max - 1) return set_error(stmt, MYERR_01004, NULL, 0); return SQL_SUCCESS; } SQLRETURN SQL_API SQLGetDiagField(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLSMALLINT field, SQLPOINTER info, SQLSMALLINT info_max, SQLSMALLINT *info_len) { DBC *dbc; SQLCHAR *value= NULL; SQLINTEGER len= SQL_NTS; SQLRETURN rc= MySQLGetDiagField(handle_type, handle, record, field, &value, info); switch (handle_type) { case SQL_HANDLE_DBC: dbc= (DBC *)handle; break; case SQL_HANDLE_STMT: dbc= ((STMT *)handle)->dbc; break; case SQL_HANDLE_DESC: dbc= DESC_GET_DBC((DESC *)handle); break; case SQL_HANDLE_ENV: default: dbc= NULL; } if (value) { SQLINTEGER free_value= FALSE; uint errors; if (dbc && dbc->ansi_charset_info && dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { value= sqlchar_as_sqlchar(dbc->cxn_charset_info, dbc->ansi_charset_info, value, &len, &errors); free_value= TRUE; } else len= strlen((char *)value); if (len > info_max - 1) rc= set_conn_error(dbc, MYERR_01004, NULL, 0); if (info_len) *info_len= (SQLSMALLINT)len; if (info && info_max > 1) strmake((char *)info, (char *)value, info_max - 1); if (free_value) x_free(value); } return rc; } SQLRETURN SQL_API SQLGetDiagRec(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLCHAR *sqlstate, SQLINTEGER *native_error, SQLCHAR *message, SQLSMALLINT message_max, SQLSMALLINT *message_len) { return SQLGetDiagRecImpl(handle_type, handle, record, sqlstate, native_error, message, message_max, message_len); } SQLRETURN SQL_API SQLGetDiagRecImpl(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLCHAR *sqlstate, SQLINTEGER *native_error, SQLCHAR *message, SQLSMALLINT message_max, SQLSMALLINT *message_len) { SQLRETURN rc; DBC *dbc; SQLCHAR *msg_value= NULL, *sqlstate_value= NULL; SQLINTEGER len= SQL_NTS; SQLSMALLINT free_value= FALSE; uint errors; switch (handle_type) { case SQL_HANDLE_DBC: dbc= (DBC *)handle; break; case SQL_HANDLE_STMT: dbc= ((STMT *)handle)->dbc; break; case SQL_HANDLE_DESC: dbc= DESC_GET_DBC((DESC *)handle); break; case SQL_HANDLE_ENV: default: dbc= NULL; } if (message_max < 0) return SQL_ERROR; rc= MySQLGetDiagRec(handle_type, handle, record, &sqlstate_value, native_error, &msg_value); if (msg_value) { if (dbc && dbc->ansi_charset_info && dbc->cxn_charset_info && dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { msg_value= sqlchar_as_sqlchar(dbc->cxn_charset_info, dbc->ansi_charset_info, msg_value, &len, &errors); free_value= TRUE; } else len= strlen((char *)msg_value); if (len > message_max - 1) rc= set_conn_error(dbc, MYERR_01004, NULL, 0); if (message_len) *message_len= (SQLSMALLINT)len; if (message && message_max > 1) strmake((char *)message, (char *)msg_value, message_max - 1); if (free_value) x_free(msg_value); } if (sqlstate && sqlstate_value) { if (dbc && dbc->ansi_charset_info && dbc->cxn_charset_info && dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { len= SQL_NTS; sqlstate_value= sqlchar_as_sqlchar(dbc->cxn_charset_info, dbc->ansi_charset_info, sqlstate_value, &len, &errors); free_value= TRUE; } else free_value= FALSE; strmake((char *)sqlstate, sqlstate_value ? (char *)sqlstate_value : "00000", 5); if (free_value) x_free(sqlstate_value); } return rc; } SQLRETURN SQL_API SQLGetInfo(SQLHDBC hdbc, SQLUSMALLINT type, SQLPOINTER value, SQLSMALLINT value_max, SQLSMALLINT *value_len) { DBC *dbc= (DBC *)hdbc; SQLCHAR *char_value= NULL; SQLINTEGER len= SQL_NTS; SQLSMALLINT free_value= FALSE; uint errors; SQLRETURN rc= MySQLGetInfo(hdbc, type, &char_value, value, value_len); if (char_value) { if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { char_value= sqlchar_as_sqlchar(dbc->cxn_charset_info, dbc->ansi_charset_info, char_value, &len, &errors); free_value= TRUE; } else len= strlen((char *)char_value); if (len > value_max - 1) rc= set_conn_error(dbc, MYERR_01004, NULL, 0); if (value && value_max > 1) strmake((char *)value, (char *)char_value, value_max - 1); if (value_len) *value_len= (SQLSMALLINT)len; if (free_value) x_free(char_value); } return rc; } SQLRETURN SQL_API SQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_max, SQLINTEGER *value_len) { /* Nothing special to do, since we don't have any string stmt attribs */ return MySQLGetStmtAttr(hstmt, attribute, value, value_max, value_len); } SQLRETURN SQL_API SQLGetTypeInfo(SQLHSTMT hstmt, SQLSMALLINT type) { return MySQLGetTypeInfo(hstmt, type); } SQLRETURN SQL_API SQLNativeSql(SQLHDBC hdbc, SQLCHAR *in, SQLINTEGER in_len, SQLCHAR *out, SQLINTEGER out_max, SQLINTEGER *out_len) { if (in_len == SQL_NTS) in_len= strlen((char *)in); if (out_len) *out_len= in_len; (void)strncpy((char *)out, (const char *)in, out_max); if (in_len > out_max) return set_conn_error((DBC *)hdbc, MYERR_01004, NULL, 0); return SQL_SUCCESS; } SQLRETURN SQL_API SQLPrepare(SQLHSTMT hstmt, SQLCHAR *str, SQLINTEGER str_len) { return SQLPrepareImpl(hstmt, str, str_len); } SQLRETURN SQL_API SQLPrepareImpl(SQLHSTMT hstmt, SQLCHAR *str, SQLINTEGER str_len) { STMT *stmt= (STMT *)hstmt; /* If the ANSI character set is the same as the connection character set, we can pass it straight through. Otherwise it needs to be converted to the connection character set (probably utf-8). */ if (stmt->dbc->ansi_charset_info->number == stmt->dbc->cxn_charset_info->number) return MySQLPrepare(hstmt, str, str_len, FALSE); else { uint errors= 0; str= sqlchar_as_sqlchar(stmt->dbc->ansi_charset_info, stmt->dbc->cxn_charset_info, str, &str_len, &errors); if (!str && str_len == -1) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } /* Character conversion problems are not tolerated. */ if (errors) { x_free(str); return set_stmt_error(stmt, "22018", NULL, 0); } return MySQLPrepare(hstmt, str, str_len, TRUE); } } SQLRETURN SQL_API SQLPrimaryKeys(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { SQLINTEGER len= SQL_NTS; uint errors= 0; if (catalog) { catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (schema) { schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (table) { table= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; len= SQL_NTS; } } rc= MySQLPrimaryKeys(hstmt, catalog, catalog_len, schema, schema_len, table, table_len); if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { x_free(catalog); x_free(schema); x_free(table); } return rc; } SQLRETURN SQL_API SQLProcedureColumns(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *proc, SQLSMALLINT proc_len, SQLCHAR *column, SQLSMALLINT column_len) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { SQLINTEGER len= SQL_NTS; uint errors= 0; if (catalog) { catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (schema) { schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (proc) { proc= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, proc, &len, &errors); proc_len= (SQLSMALLINT)len; len= SQL_NTS; } if (column) { column= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, column, &len, &errors); column_len= (SQLSMALLINT)len; } } rc= MySQLProcedureColumns(hstmt, catalog, catalog_len, schema, schema_len, proc, proc_len, column, column_len); if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { x_free(catalog); x_free(schema); x_free(proc); } return rc; } SQLRETURN SQL_API SQLProcedures(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *proc, SQLSMALLINT proc_len) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { SQLINTEGER len= SQL_NTS; uint errors= 0; if (catalog) { catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (schema) { schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (proc) { proc= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, proc, &len, &errors); proc_len= (SQLSMALLINT)len; len= SQL_NTS; } } rc= MySQLProcedures(hstmt, catalog, catalog_len, schema, schema_len, proc, proc_len); if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { x_free(catalog); x_free(schema); x_free(proc); } return rc; } SQLRETURN SQL_API SQLSetConnectAttr(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_len) { return SQLSetConnectAttrImpl(hdbc, attribute, value, value_len); } SQLRETURN SQL_API SQLSetConnectAttrImpl(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_len) { SQLRETURN rc; DBC *dbc= (DBC *)hdbc; my_bool free_value= FALSE; if (dbc->ansi_charset_info && dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { /* SQL_ATTR_CURRENT_CATALOG is the only string attribute we support. */ if (attribute == SQL_ATTR_CURRENT_CATALOG) { uint errors= 0; value= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, value, &value_len, &errors); if (!value && value_len == -1) { set_mem_error(&dbc->mysql); return set_conn_error(dbc, MYERR_S1001, mysql_error(&dbc->mysql), mysql_errno(&dbc->mysql)); } free_value= TRUE; } } rc= MySQLSetConnectAttr(hdbc, attribute, value, value_len); if (free_value) x_free(value); return rc; } SQLRETURN SQL_API SQLSetConnectOption(SQLHDBC hdbc, SQLUSMALLINT option, SQLULEN param) { SQLINTEGER value_len= 0; if (option == SQL_ATTR_CURRENT_CATALOG) value_len= SQL_NTS; return SQLSetConnectAttrImpl(hdbc, option, (SQLPOINTER)param, value_len); } SQLRETURN SQL_API SQLSetCursorName(SQLHSTMT hstmt, SQLCHAR *name, SQLSMALLINT name_len) { STMT *stmt= (STMT *)hstmt; SQLINTEGER len= name_len; uint errors= 0; if (stmt->dbc->ansi_charset_info->number == stmt->dbc->cxn_charset_info->number) return MySQLSetCursorName(hstmt, name, name_len); name= sqlchar_as_sqlchar(stmt->dbc->ansi_charset_info, stmt->dbc->cxn_charset_info, name, &len, &errors); if (!name && len == -1) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } /* Character conversion problems are not tolerated. */ if (errors) { x_free(name); return set_stmt_error(stmt, "HY000", "Cursor name included characters that could not " "be converted to connection character set", 0); } return MySQLSetCursorName(hstmt, name, (SQLSMALLINT)len); } SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_len) { /* Nothing special to do, since we don't have any string stmt attribs */ return MySQLSetStmtAttr(hstmt, attribute, value, value_len); } SQLRETURN SQL_API SQLSpecialColumns(SQLHSTMT hstmt, SQLUSMALLINT type, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLUSMALLINT scope, SQLUSMALLINT nullable) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { SQLINTEGER len= SQL_NTS; uint errors= 0; if (catalog) { catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (schema) { schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (table) { table= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; len= SQL_NTS; } } rc= MySQLSpecialColumns(hstmt, type, catalog, catalog_len, schema, schema_len, table, table_len, scope, nullable); if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { x_free(catalog); x_free(schema); x_free(table); } return rc; } SQLRETURN SQL_API SQLStatistics(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLUSMALLINT unique, SQLUSMALLINT accuracy) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { SQLINTEGER len= SQL_NTS; uint errors= 0; if (catalog) { catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (schema) { schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (table) { table= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; len= SQL_NTS; } } rc= MySQLStatistics(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, unique, accuracy); if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { x_free(catalog); x_free(schema); x_free(table); } return rc; } SQLRETURN SQL_API SQLTablePrivileges(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { SQLINTEGER len= SQL_NTS; uint errors= 0; if (catalog) { catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (schema) { schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (table) { table= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; len= SQL_NTS; } } rc= MySQLTablePrivileges(hstmt, catalog, catalog_len, schema, schema_len, table, table_len); if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { x_free(catalog); x_free(schema); x_free(table); } return rc; } SQLRETURN SQL_API SQLTables(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *type, SQLSMALLINT type_len) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { SQLINTEGER len= SQL_NTS; uint errors= 0; if (catalog) { catalog= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, catalog, &len, &errors); if (!len) catalog= ""; catalog_len= (SQLSMALLINT)len; len= SQL_NTS; } if (schema) { schema= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, schema, &len, &errors); if (!len) schema= ""; schema_len= (SQLSMALLINT)len; len= SQL_NTS; } if (table) { table= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, table, &len, &errors); if (!len) table= ""; table_len= (SQLSMALLINT)len; len= SQL_NTS; } if (type) { type= sqlchar_as_sqlchar(dbc->ansi_charset_info, dbc->cxn_charset_info, type, &len, &errors); type_len= (SQLSMALLINT)len; len= SQL_NTS; } } rc= MySQLTables(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, type, type_len); if (dbc->ansi_charset_info->number != dbc->cxn_charset_info->number) { if (catalog_len) x_free(catalog); if (schema_len) x_free(schema); if (table_len) x_free(table); x_free(type); } return rc; } SQLRETURN SQL_API SQLGetDescField(SQLHDESC hdesc, SQLSMALLINT record, SQLSMALLINT field, SQLPOINTER value, SQLINTEGER value_max, SQLINTEGER *value_len) { return MySQLGetDescField(hdesc, record, field, value, value_max, value_len); } SQLRETURN SQL_API SQLGetDescRec(SQLHDESC hdesc, SQLSMALLINT record, SQLCHAR *name, SQLSMALLINT name_max, SQLSMALLINT *name_len, SQLSMALLINT *type, SQLSMALLINT *subtype, SQLLEN *length, SQLSMALLINT *precision, SQLSMALLINT *scale, SQLSMALLINT *nullable) { NOT_IMPLEMENTED; } SQLRETURN SQL_API SQLSetDescField(SQLHDESC hdesc, SQLSMALLINT record, SQLSMALLINT field, SQLPOINTER value, SQLINTEGER value_len) { return MySQLSetDescField(hdesc, record, field, value, value_len); } SQLRETURN SQL_API SQLSetDescRec(SQLHDESC hdesc, SQLSMALLINT record, SQLSMALLINT type, SQLSMALLINT subtype, SQLLEN length, SQLSMALLINT precision, SQLSMALLINT scale, SQLPOINTER data_ptr, SQLLEN *octet_length_ptr, SQLLEN *indicator_ptr) { NOT_IMPLEMENTED; } #ifdef NOT_IMPLEMENTED_YET SQLRETURN SQL_API SQLBrowseConnect(SQLHDBC hdbc, SQLCHAR *in, SQLSMALLINT in_len, SQLCHAR *out, SQLSMALLINT out_max, SQLSMALLINT *out_len) { NOT_IMPLEMENTED; } //SQLDataSources //SQLDrivers #endif mysql-connector-odbc-5.1.10-src/driver/driver.c100644 15766 12 2276 11707541005 20127 0ustar00cteamstaff/* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file driver.c @brief Stub for forcing creation of precompiled headers. */ #include "driver.h" mysql-connector-odbc-5.1.10-src/driver/transact.c100644 15766 12 11516 11707541005 20470 0ustar00cteamstaff/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file transact.c @brief Transaction processing functions. */ #include "driver.h" /** Commit or roll back the transactions associated with a particular database connection. @param[in] hdbc Handle of database connection @param[in] CompletionType How to complete the transactions, @c SQL_COMMIT or @c SQL_ROLLBACK */ static SQLRETURN my_transact(SQLHDBC hdbc, SQLSMALLINT CompletionType) { SQLRETURN result= SQL_SUCCESS; DBC FAR *dbc= (DBC FAR *)hdbc; const char *query; uint length; if (dbc && !dbc->ds->disable_transactions) { switch(CompletionType) { case SQL_COMMIT: query= "COMMIT"; length= 6; break; case SQL_ROLLBACK: if (!trans_supported(dbc)) { return set_conn_error(hdbc,MYERR_S1C00, "Underlying server does not support transactions, upgrade to version >= 3.23.38",0); } query= "ROLLBACK"; length= 8; break; default: return set_conn_error(hdbc,MYERR_S1012,NULL,0); } MYLOG_DBC_QUERY(dbc, query); pthread_mutex_lock(&dbc->lock); if (check_if_server_is_alive(dbc) || mysql_real_query(&dbc->mysql,query,length)) { result= set_conn_error(hdbc,MYERR_S1000, mysql_error(&dbc->mysql), mysql_errno(&dbc->mysql)); } pthread_mutex_unlock(&dbc->lock); } return(result); } /** Commit or roll back the transactions associated with a particular database connection, or all connections in an environment. @param[in] HandleType Type of @a Handle, @c SQL_HANDLE_ENV or @c SQL_HANDLE_DBC @param[in] Handle Handle to database connection or environment @param[in] CompletionType How to complete the transactions, @c SQL_COMMIT or @c SQL_ROLLBACK */ static SQLRETURN SQL_API end_transaction(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) { SQLRETURN result= SQL_SUCCESS; ENV FAR *henv; LIST *current; switch (HandleType) { case SQL_HANDLE_ENV: henv= (ENV *)Handle; for (current= henv->connections; current; current= current->next) { my_transact((DBC *)current->data, CompletionType); } break; case SQL_HANDLE_DBC: result= my_transact(Handle,CompletionType); break; default: result= SQL_ERROR; set_error(Handle,MYERR_S1092,NULL,0); break; } return result; } /** Commit or roll back the transactions associated with a particular database connection, or all connections in an environment. @param[in] HandleType Type of @a Handle, @c SQL_HANDLE_ENV or @c SQL_HANDLE_DBC @param[in] Handle Handle to database connection or environment @param[in] CompletionType How to complete the transactions, @c SQL_COMMIT or @c SQL_ROLLBACK @since ODBC 3.0 @since ISO SQL 92 */ SQLRETURN SQL_API SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) { return end_transaction(HandleType, Handle, CompletionType); } /** Commit or roll back the transactions associated with a particular database connection, or all connections in an environment. @deprecated This function is deprecated, SQLEndTran() should be used instead. @param[in] henv Handle to database environment @param[in] hdbc Handle to database connection @param[in] fType How to complete the transactions, @c SQL_COMMIT or @c SQL_ROLLBACK @since ODBC 1.0 */ SQLRETURN SQL_API SQLTransact(SQLHENV henv, SQLHDBC hdbc, SQLUSMALLINT fType) { return end_transaction(hdbc ? SQL_HANDLE_DBC : SQL_HANDLE_ENV, hdbc ? hdbc : henv, fType); } mysql-connector-odbc-5.1.10-src/driver/error.c100644 15766 12 46416 11707541005 20011 0ustar00cteamstaff/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file error.c @brief Error handling functions. */ #include "driver.h" #include "mysqld_error.h" #include "errmsg.h" /* @type : myodbc3 internal error structure @purpose : set of internal errors, in the following order - SQLSTATE2 (when version is SQL_OV_ODBC2) - SQLSTATE3 (when version is SQL_OV_ODBC3) - error message text - return code */ static MYODBC3_ERR_STR myodbc3_errors[]= { {"01000","General warning", SQL_SUCCESS_WITH_INFO}, {"01004","String data, right truncated", SQL_SUCCESS_WITH_INFO}, {"01S02","Option value changed", SQL_SUCCESS_WITH_INFO}, {"01S03","No rows updated/deleted", SQL_SUCCESS_WITH_INFO}, {"01S04","More than one row updated/deleted", SQL_SUCCESS_WITH_INFO}, {"01S06","Attempt to fetch before the result set returned the first rowset", SQL_SUCCESS_WITH_INFO}, {"07001","SQLBindParameter not used for all parameters", SQL_ERROR}, {"07005","Prepared statement not a cursor-specification", SQL_ERROR}, {"07006","Restricted data type attribute violation", SQL_ERROR}, {"07009","Invalid descriptor index", SQL_ERROR}, {"08002","Connection name in use", SQL_ERROR}, {"08003","Connection does not exist", SQL_ERROR}, {"24000","Invalid cursor state", SQL_ERROR}, {"25000","Invalid transaction state", SQL_ERROR}, {"25S01","Transaction state unknown", SQL_ERROR}, {"34000","Invalid cursor name", SQL_ERROR}, {"HYT00","Timeout expired", SQL_ERROR}, {"HY000","General driver defined error", SQL_ERROR}, {"HY001","Memory allocation error", SQL_ERROR}, {"HY002","Invalid column number", SQL_ERROR}, {"HY003","Invalid application buffer type", SQL_ERROR}, {"HY004","Invalid SQL data type", SQL_ERROR}, {"HY007","Associated statement is not prepared", SQL_ERROR}, {"HY009","Invalid use of null pointer", SQL_ERROR}, {"HY010","Function sequence error", SQL_ERROR}, {"HY011","Attribute can not be set now", SQL_ERROR}, {"HY012","Invalid transaction operation code", SQL_ERROR}, {"HY013","Memory management error", SQL_ERROR}, {"HY015","No cursor name available", SQL_ERROR}, {"HY016","Cannot modify an implementation row descriptor", SQL_ERROR}, {"HY017","Invalid use of an automatically allocated descriptor handle", SQL_ERROR}, {"HY024","Invalid attribute value", SQL_ERROR}, {"HY090","Invalid string or buffer length", SQL_ERROR}, {"HY091","Invalid descriptor field identifier", SQL_ERROR}, {"HY092","Invalid attribute/option identifier", SQL_ERROR}, {"HY093","Invalid parameter number", SQL_ERROR}, {"HY095","Function type out of range", SQL_ERROR}, {"HY106","Fetch type out of range", SQL_ERROR}, {"HY107","Row value out of range", SQL_ERROR}, {"HY109","Invalid cursor position", SQL_ERROR}, {"HYC00","Optional feature not implemented", SQL_ERROR}, /* server related..*/ {"21S01","Column count does not match value count", SQL_ERROR}, {"23000","Integrity constraint violation", SQL_ERROR}, {"42000","Syntax error or access violation", SQL_ERROR}, {"42S01","Base table or view already exists ", SQL_ERROR}, {"42S02","Base table or view not found", SQL_ERROR}, {"42S12","Index not found", SQL_ERROR}, {"42S21","Column already exists", SQL_ERROR}, {"42S22","Column not found", SQL_ERROR}, {"08S01","Communication link failure", SQL_ERROR}, }; /* @type : myodbc3 internal @purpose : If ODBC version is SQL_OV_ODBC2, initialize old SQLSTATEs */ void myodbc_sqlstate2_init(void) { /* As accessing the SQLSTATE2 is very rare, set this to global when required .. one time ..for quick processing in set_error/set_conn_error */ uint i; for ( i= MYERR_S1000; i <= MYERR_S1C00; ++i ) { myodbc3_errors[i].sqlstate[0]= 'S'; myodbc3_errors[i].sqlstate[1]= '1'; } strmov(myodbc3_errors[MYERR_07005].sqlstate,"24000"); strmov(myodbc3_errors[MYERR_42000].sqlstate,"37000"); strmov(myodbc3_errors[MYERR_42S01].sqlstate,"S0001"); strmov(myodbc3_errors[MYERR_42S02].sqlstate,"S0002"); strmov(myodbc3_errors[MYERR_42S12].sqlstate,"S0012"); strmov(myodbc3_errors[MYERR_42S21].sqlstate,"S0021"); strmov(myodbc3_errors[MYERR_42S22].sqlstate,"S0022"); } /* @type : myodbc3 internal @purpose : If ODBC version is SQL_OV_ODBC3, initialize to original SQLSTATEs */ void myodbc_sqlstate3_init(void) { uint i; for ( i= MYERR_S1000; i <= MYERR_S1C00; ++i ) { myodbc3_errors[i].sqlstate[0]= 'H'; myodbc3_errors[i].sqlstate[1]= 'Y'; } strmov(myodbc3_errors[MYERR_07005].sqlstate,"07005"); strmov(myodbc3_errors[MYERR_42000].sqlstate,"42000"); strmov(myodbc3_errors[MYERR_42S01].sqlstate,"42S01"); strmov(myodbc3_errors[MYERR_42S02].sqlstate,"42S02"); strmov(myodbc3_errors[MYERR_42S12].sqlstate,"42S12"); strmov(myodbc3_errors[MYERR_42S21].sqlstate,"42S21"); strmov(myodbc3_errors[MYERR_42S22].sqlstate,"42S22"); } /* @type : myodbc3 internal @purpose : copies error from one handle to other */ SQLRETURN copy_stmt_error(STMT FAR *dst,STMT FAR *src) { strmov(dst->error.sqlstate,src->error.sqlstate); strmov(dst->error.message, src->error.message); dst->error.native_error= src->error.native_error; dst->error.retcode= src->error.retcode; return(SQL_SUCCESS); } /* @type : myodbc3 internal @purpose : sets the connection level errors, which doesn't need any conversion */ SQLRETURN set_dbc_error(DBC FAR *dbc,char *state, const char *message,uint errcode) { strmov(dbc->error.sqlstate,state); strxmov(dbc->error.message,MYODBC3_ERROR_PREFIX,message,NullS); dbc->error.native_error= errcode; return SQL_ERROR; } /* @type : myodbc3 internal @purpose : sets the statement level errors, which doesn't need any conversion */ SQLRETURN set_stmt_error( STMT FAR * stmt, char * state, const char * message, uint errcode ) { strmov( stmt->error.sqlstate, state ); strxmov( stmt->error.message, stmt->dbc->st_error_prefix, message, NullS ); stmt->error.native_error = errcode; return SQL_ERROR; } /* @type : myodbc3 internal @purpose : sets the descriptor level errors */ SQLRETURN set_desc_error(DESC * desc, char * state, const char * message, uint errcode) { strmov(desc->error.sqlstate, state); strxmov(desc->error.message, desc->stmt->dbc->st_error_prefix, message, NullS); desc->error.native_error = errcode; return SQL_ERROR; } /* @type : myodbc3 internal @purpose : translates SQL error to ODBC error */ void translate_error(char *save_state,myodbc_errid errid,uint mysql_err) { char *state= myodbc3_errors[errid].sqlstate; switch ( mysql_err ) { case ER_WRONG_VALUE_COUNT: state= "21S01"; break; case ER_DUP_ENTRY: case ER_DUP_KEY: state= "23000"; break; case ER_NO_DB_ERROR: state= "3D000"; break; case ER_PARSE_ERROR: #ifdef ER_SP_DOES_NOT_EXIST case ER_SP_DOES_NOT_EXIST: #endif state= myodbc3_errors[MYERR_42000].sqlstate; break; case ER_TABLE_EXISTS_ERROR: state= myodbc3_errors[MYERR_42S01].sqlstate; break; case ER_FILE_NOT_FOUND: case ER_NO_SUCH_TABLE: case ER_CANT_OPEN_FILE: case ER_BAD_TABLE_ERROR: state= myodbc3_errors[MYERR_42S02].sqlstate; break; case ER_NO_SUCH_INDEX: case ER_CANT_DROP_FIELD_OR_KEY: state= myodbc3_errors[MYERR_42S12].sqlstate; break; case ER_DUP_FIELDNAME: state= myodbc3_errors[MYERR_42S21].sqlstate; break; case ER_BAD_FIELD_ERROR: state= myodbc3_errors[MYERR_42S22].sqlstate; break; case CR_SERVER_HANDSHAKE_ERR: case CR_CONNECTION_ERROR: case CR_SERVER_GONE_ERROR: case CR_SERVER_LOST: state= "08S01"; break; default: break; } strmov(save_state,state); } /* @type : myodbc3 internal @purpose : copy error to error structure */ static SQLRETURN copy_error(MYERROR *error, myodbc_errid errid, const char *errtext, SQLINTEGER errcode, char *prefix) { SQLRETURN sqlreturn; SQLCHAR *errmsg; SQLINTEGER code; errmsg= (errtext ? (SQLCHAR *)errtext : (SQLCHAR *)myodbc3_errors[errid].message); code= errcode ? (myodbc_errid) errcode : errid + MYODBC_ERROR_CODE_START; sqlreturn= error->retcode= myodbc3_errors[errid].retcode; /* RETCODE */ error->native_error= code; /* NATIVE */ strmov(error->sqlstate, myodbc3_errors[errid].sqlstate); /* SQLSTATE */ strxmov(error->message,prefix,errmsg,NullS); /* MESSAGE */ return sqlreturn; } /* @type : myodbc3 internal @purpose : sets the error information to environment handle. */ SQLRETURN set_env_error(ENV *env, myodbc_errid errid, const char *errtext, SQLINTEGER errcode) { return copy_error(&env->error,errid,errtext,errcode,MYODBC3_ERROR_PREFIX); } /* @type : myodbc3 internal @purpose : sets the error information to connection handle. */ SQLRETURN set_conn_error(DBC *dbc, myodbc_errid errid, const char *errtext, SQLINTEGER errcode) { return copy_error(&dbc->error,errid,errtext,errcode,MYODBC3_ERROR_PREFIX); } /* @type : myodbc3 internal @purpose : sets the error information to statement handle. */ SQLRETURN set_error(STMT *stmt, myodbc_errid errid, const char *errtext, SQLINTEGER errcode) { return copy_error(&stmt->error,errid,errtext,errcode, stmt->dbc->st_error_prefix); } /* @type : myodbc3 internal @purpose : sets a my_malloc() failure on a MYSQL* connection */ void set_mem_error(MYSQL *mysql) { mysql->net.last_errno= CR_OUT_OF_MEMORY; strmov(mysql->net.last_error, "Memory allocation failed"); strmov(mysql->net.sqlstate, "HY001"); } /** Handle a connection-related error. @param[in] stmt Statement */ SQLRETURN handle_connection_error(STMT *stmt) { unsigned int err= mysql_errno(&stmt->dbc->mysql); switch (err) { case 0: /* no error */ return SQL_SUCCESS; case CR_SERVER_GONE_ERROR: case CR_SERVER_LOST: return set_stmt_error(stmt, "08S01", mysql_error(&stmt->dbc->mysql), err); case CR_OUT_OF_MEMORY: return set_stmt_error(stmt, "HY001", mysql_error(&stmt->dbc->mysql), err); case CR_COMMANDS_OUT_OF_SYNC: case CR_UNKNOWN_ERROR: default: return set_stmt_error(stmt, "HY000", mysql_error(&stmt->dbc->mysql), err); } } /* Little helper to check if error code means that we have lost connection @param[in] errcode code of the last error */ my_bool is_connection_lost(uint errcode) { if (errcode==CR_SERVER_GONE_ERROR || errcode==CR_SERVER_LOST) { return 1; } return 0; } /* @type : myodbc3 internal @purpose : sets the error information to appropriate handle. it also sets the SQLSTATE based on the ODBC VERSION */ SQLRETURN set_handle_error(SQLSMALLINT HandleType, SQLHANDLE handle, myodbc_errid errid, const char *errtext, SQLINTEGER errcode) { switch ( HandleType ) { case SQL_HANDLE_ENV: return copy_error(&((ENV *)handle)->error,errid,errtext, errcode,MYODBC3_ERROR_PREFIX); case SQL_HANDLE_DBC: return copy_error(&((DBC *)handle)->error,errid,errtext, errcode,MYODBC3_ERROR_PREFIX); case SQL_HANDLE_STMT: return copy_error(&((STMT *)handle)->error,errid,errtext, errcode,((STMT *)handle)->dbc->st_error_prefix); case SQL_HANDLE_DESC: return copy_error(&((DESC *)handle)->error,errid,errtext,errcode, ((DESC *)handle)->stmt->dbc->st_error_prefix); default: return SQL_INVALID_HANDLE; } } /** */ SQLRETURN SQL_API MySQLGetDiagRec(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLCHAR **sqlstate, SQLINTEGER *native, SQLCHAR **message) { MYERROR *error; SQLINTEGER tmp_native; if (!native) native= &tmp_native; if (!handle || record <= 0) return SQL_ERROR; /* Currently we are not supporting error list, so if RecNumber > 1, return no data found */ if (record > 1) return SQL_NO_DATA_FOUND; if (handle_type == SQL_HANDLE_STMT) error= &((STMT *)handle)->error; else if (handle_type == SQL_HANDLE_DBC) error= &((DBC *)handle)->error; else if (handle_type == SQL_HANDLE_ENV) error= &((ENV *)handle)->error; else if (handle_type == SQL_HANDLE_DESC) error= &((DESC *)handle)->error; else return SQL_INVALID_HANDLE; if (!error->message || !error->message[0]) { *message= (SQLCHAR *)""; *sqlstate= (SQLCHAR *)"00000"; *native= 0; return SQL_NO_DATA_FOUND; } *message= (SQLCHAR *)error->message; *sqlstate= (SQLCHAR *)error->sqlstate; *native= error->native_error; return SQL_SUCCESS; } my_bool is_odbc3_subclass(char *sqlstate) { char *states[]= { "01S00", "01S01", "01S02", "01S06", "01S07", "07S01", "08S01", "21S01", "21S02", "25S01", "25S02", "25S03", "42S01", "42S02", "42S11", "42S12", "42S21", "42S22", "HY095", "HY097", "HY098", "HY099", "HY100", "HY101", "HY105", "HY107", "HY109", "HY110", "HY111", "HYT00", "HYT01", "IM001", "IM002", "IM003", "IM004", "IM005", "IM006", "IM007", "IM008", "IM010", "IM011", "IM012"}; size_t i; if (!sqlstate) return FALSE; for (i= 0; i < sizeof(states) / sizeof(states[0]); ++i) if (memcmp(states[i], sqlstate, 5) == 0) return TRUE; return FALSE; } /* @type : ODBC 3.0 API @purpose : returns the current value of a field of a record of the diagnostic data structure (associated with a specified handle) that contains error, warning, and status information */ SQLRETURN SQL_API MySQLGetDiagField(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLSMALLINT identifier, SQLCHAR **char_value, SQLPOINTER num_value) { SQLLEN num; /* Handle may not be these types, but this saves lots of casts below. */ STMT *stmt= (STMT *)handle; DBC *dbc= (DBC *)handle; DESC *desc= (DESC *)handle; MYERROR *error; if (!num_value) num_value= # if (!handle) return SQL_ERROR; if (handle_type == SQL_HANDLE_DESC) error= &desc->error; else if (handle_type == SQL_HANDLE_STMT) error= &stmt->error; else if (handle_type == SQL_HANDLE_DBC) error= &dbc->error; else if (handle_type == SQL_HANDLE_ENV) error= &((ENV *)handle)->error; else return SQL_ERROR; if (record > 1) return SQL_NO_DATA_FOUND; switch (identifier) { /* Header fields */ case SQL_DIAG_CURSOR_ROW_COUNT: if (handle_type != SQL_HANDLE_STMT) return SQL_ERROR; if (!stmt->result) *(SQLLEN *)num_value= 0; else *(SQLLEN *)num_value= (SQLLEN) mysql_num_rows(stmt->result); return SQL_SUCCESS; case SQL_DIAG_DYNAMIC_FUNCTION: if (handle_type != SQL_HANDLE_STMT) return SQL_ERROR; *char_value= (SQLCHAR *)""; return SQL_SUCCESS; case SQL_DIAG_DYNAMIC_FUNCTION_CODE: if (handle_type != SQL_HANDLE_STMT) return SQL_ERROR; *(SQLINTEGER *)num_value= 0; return SQL_SUCCESS; case SQL_DIAG_NUMBER: *(SQLINTEGER *)num_value= 1; return SQL_SUCCESS; case SQL_DIAG_RETURNCODE: *(SQLRETURN *)num_value= error->retcode; return SQL_SUCCESS; case SQL_DIAG_ROW_COUNT: if (handle_type != SQL_HANDLE_STMT) return SQL_ERROR; if (!stmt->result) *(SQLLEN *)num_value= 0; else *(SQLLEN *)num_value= (SQLLEN)stmt->affected_rows; return SQL_SUCCESS; /* Record fields */ case SQL_DIAG_CLASS_ORIGIN: { char *sqlstate; if (record <= 0) return SQL_ERROR; sqlstate= error->sqlstate; if (sqlstate && sqlstate[0] == 'I' && sqlstate[1] == 'M') *char_value= (SQLCHAR *)"ODBC 3.0"; else *char_value= (SQLCHAR *)"ISO 9075"; } return SQL_SUCCESS; case SQL_DIAG_COLUMN_NUMBER: if (record <= 0) return SQL_ERROR; *(SQLINTEGER *)num_value= SQL_COLUMN_NUMBER_UNKNOWN; return SQL_SUCCESS; case SQL_DIAG_CONNECTION_NAME: { DataSource *ds; if (record <= 0) return SQL_ERROR; if (handle_type == SQL_HANDLE_DESC) ds= desc->stmt->dbc->ds; else if (handle_type == SQL_HANDLE_STMT) ds= stmt->dbc->ds; else if (handle_type == SQL_HANDLE_DBC) ds= dbc->ds; else *char_value= (SQLCHAR *)""; if (ds) *char_value= (SQLCHAR *)ds->name8; return SQL_SUCCESS; } case SQL_DIAG_MESSAGE_TEXT: if (record <= 0) return SQL_ERROR; *char_value= (SQLCHAR *)error->message; if (!*char_value) *char_value= (SQLCHAR *)""; return SQL_SUCCESS; case SQL_DIAG_NATIVE: *(SQLINTEGER *)num_value= error->native_error; return SQL_SUCCESS; case SQL_DIAG_ROW_NUMBER: if (record <= 0) return SQL_ERROR; *(SQLLEN *)num_value= SQL_ROW_NUMBER_UNKNOWN; return SQL_SUCCESS; case SQL_DIAG_SERVER_NAME: { DataSource *ds; if (record <= 0) return SQL_ERROR; if (handle_type == SQL_HANDLE_DESC) ds= desc->stmt->dbc->ds; else if (handle_type == SQL_HANDLE_STMT) ds= stmt->dbc->ds; else if (handle_type == SQL_HANDLE_DBC) ds= dbc->ds; else *char_value= (SQLCHAR *)""; if (ds) *char_value= (SQLCHAR *)ds->server8; return SQL_SUCCESS; } case SQL_DIAG_SQLSTATE: if (record <= 0) return SQL_ERROR; *char_value= (SQLCHAR *)error->sqlstate; if (!*char_value) *char_value= (SQLCHAR *)""; return SQL_SUCCESS; case SQL_DIAG_SUBCLASS_ORIGIN: if (record <= 0) return SQL_ERROR; { char *sqlstate; if (record <= 0) return SQL_ERROR; sqlstate= error->sqlstate; if (is_odbc3_subclass(sqlstate)) *char_value= (SQLCHAR *)"ODBC 3.0"; else *char_value= (SQLCHAR *)"ISO 9075"; } return SQL_SUCCESS; default: return SQL_ERROR; } } mysql-connector-odbc-5.1.10-src/driver/myutil.h100644 15766 12 36402 11707541005 20202 0ustar00cteamstaff/* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /*************************************************************************** * MYUTIL.H * * * * @description: Prototype definations needed by the driver * * * * @author : MySQL AB(monty@mysql.com, venu@mysql.com) * * @date : 2001-Sep-22 * * @product : myodbc3 * * * ****************************************************************************/ #ifndef __MYUTIL_H__ #define __MYUTIL_H__ /* Utility macros */ #define if_dynamic_cursor(st) ((st)->stmt_options.cursor_type == SQL_CURSOR_DYNAMIC) #define if_forward_cache(st) ((st)->stmt_options.cursor_type == SQL_CURSOR_FORWARD_ONLY && \ (st)->dbc->ds->dont_cache_result) #define is_connected(dbc) ((dbc)->mysql.net.vio) #define trans_supported(db) ((db)->mysql.server_capabilities & CLIENT_TRANSACTIONS) #define autocommit_on(db) ((db)->mysql.server_status & SERVER_STATUS_AUTOCOMMIT) #define is_no_backslashes_escape_mode(db) ((db)->mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) #define reset_ptr(x) {if (x) x= 0;} #define digit(A) ((int) (A - '0')) #define MYLOG_QUERY(A,B) {if ((A)->dbc->ds->save_queries) \ query_print((A)->dbc->query_log,(char*) B);} #define MYLOG_DBC_QUERY(A,B) {if((A)->ds->save_queries) \ query_print((A)->query_log,(char*) B);} /* A few character sets we care about. */ #define ASCII_CHARSET_NUMBER 11 #define BINARY_CHARSET_NUMBER 63 #define UTF8_CHARSET_NUMBER 33 /* truncation types in SQL_NUMERIC_STRUCT conversions */ #define SQLNUM_TRUNC_FRAC 1 #define SQLNUM_TRUNC_WHOLE 2 /* Wrappers to hide differences in client library versions. */ #if MYSQL_VERSION_ID >= 40100 # define my_int2str(val, dst, radix, upcase) \ int2str((val), (dst), (radix), (upcase)) #else # define my_int2str(val, dst, radix, upcase) \ int2str((val), (dst), (radix)) #endif #if MYSQL_VERSION_ID >= 50100 typedef unsigned char * DYNAMIC_ELEMENT; #else typedef char * DYNAMIC_ELEMENT; #endif #if MYSQL_VERSION_ID >= 50100 /* Same us MYODBC_FIELD_STRING(name, NAME_LEN, flags) */ # define MYODBC_FIELD_NAME(name, flags) \ {(name), (name), NullS, NullS, NullS, NullS, NullS, NAME_LEN, 0, 0, 0, 0, 0, 0, \ 0, 0, (flags), 0, UTF8_CHARSET_NUMBER, MYSQL_TYPE_VAR_STRING, NULL} # define MYODBC_FIELD_STRING(name, len, flags) \ {(name), (name), NullS, NullS, NullS, NullS, NullS, (len*SYSTEM_CHARSET_MBMAXLEN), 0, 0, 0, 0, 0, 0, \ 0, 0, (flags), 0, UTF8_CHARSET_NUMBER, MYSQL_TYPE_VAR_STRING, NULL} # define MYODBC_FIELD_SHORT(name, flags) \ {(name), (name), NullS, NullS, NullS, NullS, NullS, 5, 5, 0, 0, 0, 0, 0, 0, \ 0, (flags), 0, 0, MYSQL_TYPE_SHORT, NULL} # define MYODBC_FIELD_LONG(name, flags) \ {(name), (name), NullS, NullS, NullS, NullS, NullS, 11, 11, 0, 0, 0, 0, 0, \ 0, 0, (flags), 0, 0, MYSQL_TYPE_LONG, NULL} # define MYODBC_FIELD_LONGLONG(name, flags) \ {(name), (name), NullS, NullS, NullS, NullS, NullS, 20, 20, 0, 0, 0, 0, 0, \ 0, 0, (flags), 0, 0, MYSQL_TYPE_LONGLONG, NULL} #elif MYSQL_VERSION_ID >= 40100 # define MYODBC_FIELD_NAME(name, flags) \ {(name), (name), NullS, NullS, NullS, NullS, NullS, NAME_LEN*3, 0, 0, 0, 0, 0, 0, \ 0, 0, (flags), 0, UTF8_CHARSET_NUMBER, MYSQL_TYPE_VAR_STRING, NULL} /* we use 3 here as SYSTEM_CHARSET_MBMAXLEN is not defined in v5.0 mysql_com.h */ # define MYODBC_FIELD_STRING(name, len, flags) \ {(name), (name), NullS, NullS, NullS, NullS, NullS, (len) * 3, 0, 0, 0, 0, \ 0, 0, 0, 0, (flags), 0, UTF8_CHARSET_NUMBER, MYSQL_TYPE_VAR_STRING} # define MYODBC_FIELD_SHORT(name, flags) \ {(name), (name), NullS, NullS, NullS, NullS, NullS, 5, 5, 0, 0, 0, 0, 0, 0, \ 0, (flags), 0, 0, MYSQL_TYPE_SHORT} # define MYODBC_FIELD_LONG(name, flags) \ {(name), (name), NullS, NullS, NullS, NullS, NullS, 11, 11, 0, 0, 0, 0, 0, \ 0, 0, (flags), 0, 0, MYSQL_TYPE_LONG} # define MYODBC_FIELD_LONGLONG(name, flags) \ {(name), (name), NullS, NullS, NullS, NullS, NullS, 20, 20, 0, 0, 0, 0, 0, \ 0, 0, (flags), 0, 0, MYSQL_TYPE_LONGLONG } #endif /* Utility function prototypes that share among files */ SQLRETURN my_SQLPrepare(SQLHSTMT hstmt, SQLCHAR *szSqlStr, SQLINTEGER cbSqlStr, my_bool dupe); SQLRETURN my_SQLExecute(STMT FAR* stmt); SQLRETURN SQL_API my_SQLFreeStmt(SQLHSTMT hstmt,SQLUSMALLINT fOption); SQLRETURN SQL_API my_SQLFreeStmtExtended(SQLHSTMT hstmt, SQLUSMALLINT fOption, uint clearAllResults); SQLRETURN SQL_API my_SQLAllocStmt(SQLHDBC hdbc,SQLHSTMT FAR *phstmt); SQLRETURN do_query(STMT FAR *stmt,char *query, SQLULEN query_length); SQLRETURN insert_params(STMT FAR *stmt, SQLULEN row, char **finalquery, SQLULEN *length); SQLRETURN odbc_stmt(DBC FAR *dbc, const char *query); void mysql_link_fields(STMT *stmt,MYSQL_FIELD *fields,uint field_count); void fix_row_lengths(STMT *stmt, const long* fix_rules, uint row, uint field_count); void fix_result_types(STMT *stmt); char *fix_str(char *to,const char *from,int length); char *dupp_str(char *from,int length); SQLRETURN my_pos_delete(STMT FAR *stmt,STMT FAR *stmtParam, SQLUSMALLINT irow,DYNAMIC_STRING *dynStr); SQLRETURN my_pos_update(STMT FAR *stmt,STMT FAR *stmtParam, SQLUSMALLINT irow,DYNAMIC_STRING *dynStr); char *check_if_positioned_cursor_exists(STMT FAR *stmt, STMT FAR **stmtNew); SQLRETURN insert_param(STMT *stmt, char **to, DESC *apd, DESCREC *aprec, DESCREC *iprec, SQLULEN row); char *add_to_buffer(NET *net,char *to,const char *from,ulong length); int is_set_names_statement(SQLCHAR *query); int is_select_statement(SQLCHAR *query); void reset_getdata_position(STMT *stmt); SQLRETURN set_sql_select_limit(DBC *dbc, SQLULEN new_value); uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, const char *from, uint32 from_length, CHARSET_INFO *from_cs, uint32 *used_bytes, uint32 *used_chars, uint *errors); SQLRETURN copy_ansi_result(STMT *stmt, SQLCHAR *result, SQLLEN result_bytes, SQLLEN *used_bytes, MYSQL_FIELD *field, char *src, unsigned long src_bytes); SQLRETURN copy_binary_result(STMT *stmt, SQLCHAR *result, SQLLEN result_bytes, SQLLEN *used_bytes, MYSQL_FIELD *field, char *src, unsigned long src_bytes); SQLRETURN copy_binhex_result(STMT *stmt, SQLCHAR FAR *rgbValue, SQLINTEGER cbValueMax, SQLLEN *pcbValue, MYSQL_FIELD *field, char *src, ulong src_length); SQLRETURN copy_wchar_result(STMT *stmt, SQLWCHAR *rgbValue, SQLINTEGER cbValueMax, SQLLEN *pcbValue, MYSQL_FIELD *field, char *src, long src_length); SQLRETURN set_dbc_error(DBC FAR *dbc, char *state,const char *message,uint errcode); #define set_stmt_error myodbc_set_stmt_error SQLRETURN set_stmt_error(STMT *stmt, char *state,const char *message,uint errcode); SQLRETURN set_desc_error(DESC *desc, char *state, const char *message, uint errcode); SQLRETURN handle_connection_error(STMT *stmt); my_bool is_connection_lost(uint errcode); void set_mem_error(MYSQL *mysql); void translate_error(char *save_state,myodbc_errid errid,uint mysql_err); SQLSMALLINT get_sql_data_type(STMT *stmt, MYSQL_FIELD *field, char *buff); SQLULEN get_column_size(STMT *stmt, MYSQL_FIELD *field); SQLULEN fill_column_size_buff(char *buff, STMT *stmt, MYSQL_FIELD *field); SQLSMALLINT get_decimal_digits(STMT *stmt, MYSQL_FIELD *field); SQLLEN get_transfer_octet_length(STMT *stmt, MYSQL_FIELD *field); SQLLEN fill_transfer_oct_len_buff(char *buff, STMT *stmt, MYSQL_FIELD *field); SQLLEN get_display_size(STMT *stmt, MYSQL_FIELD *field); SQLLEN fill_display_size_buff(char *buff, STMT *stmt, MYSQL_FIELD *field); SQLSMALLINT get_dticode_from_concise_type(SQLSMALLINT concise_type); SQLSMALLINT get_concise_type_from_datetime_code(SQLSMALLINT dticode); SQLSMALLINT get_concise_type_from_interval_code(SQLSMALLINT dticode); SQLSMALLINT get_type_from_concise_type(SQLSMALLINT concise_type); #define is_char_sql_type(type) \ ((type) == SQL_CHAR || (type) == SQL_VARCHAR || (type) == SQL_LONGVARCHAR) #define is_wchar_sql_type(type) \ ((type) == SQL_WCHAR || (type) == SQL_WVARCHAR || (type) == SQL_WLONGVARCHAR) #define is_binary_sql_type(type) \ ((type) == SQL_BINARY || (type) == SQL_VARBINARY || \ (type) == SQL_LONGVARBINARY) #define is_numeric_mysql_type(field) \ ((field)->type <= MYSQL_TYPE_NULL || (field)->type == MYSQL_TYPE_LONGLONG || \ (field)->type == MYSQL_TYPE_INT24 || \ ((field)->type == MYSQL_TYPE_BIT && (field)->length == 1) || \ (field)->type == MYSQL_TYPE_NEWDECIMAL) SQLRETURN SQL_API my_SQLBindParameter(SQLHSTMT hstmt,SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType, SQLSMALLINT fSqlType, SQLULEN cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLLEN cbValueMax, SQLLEN *pcbValue); SQLRETURN SQL_API my_SQLExtendedFetch(SQLHSTMT hstmt, SQLUSMALLINT fFetchType, SQLLEN irow, SQLULEN *pcrow, SQLUSMALLINT FAR *rgfRowStatus, my_bool upd_status); SQLRETURN SQL_API my_SQLSetPos(SQLHSTMT hstmt, SQLSETPOSIROW irow, SQLUSMALLINT fOption, SQLUSMALLINT fLock); SQLRETURN copy_stmt_error(STMT FAR *src, STMT FAR *dst); int unireg_to_c_datatype(MYSQL_FIELD *field); int default_c_type(int sql_data_type); ulong bind_length(int sql_data_type,ulong length); my_bool str_to_date(SQL_DATE_STRUCT *rgbValue, const char *str, uint length, int zeroToMin); my_bool str_to_ts(SQL_TIMESTAMP_STRUCT *ts, const char *str, int zeroToMin); my_bool str_to_time_st(SQL_TIME_STRUCT *ts, const char *str); ulong str_to_time_as_long(const char *str,uint length); void init_getfunctions(void); void myodbc_init(void); void myodbc_ov_init(SQLINTEGER odbc_version); void myodbc_sqlstate2_init(void); void myodbc_sqlstate3_init(void); int check_if_server_is_alive(DBC FAR *dbc); my_bool dynstr_append_quoted_name(DYNAMIC_STRING *str, const char *name); SQLRETURN set_handle_error(SQLSMALLINT HandleType, SQLHANDLE handle, myodbc_errid errid, const char *errtext, SQLINTEGER errcode); SQLRETURN set_error(STMT *stmt,myodbc_errid errid, const char *errtext, SQLINTEGER errcode); SQLRETURN set_conn_error(DBC *dbc,myodbc_errid errid, const char *errtext, SQLINTEGER errcode); SQLRETURN set_env_error(ENV * env,myodbc_errid errid, const char *errtext, SQLINTEGER errcode); SQLRETURN copy_str_data(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLCHAR FAR *rgbValue, SQLSMALLINT cbValueMax, SQLSMALLINT FAR *pcbValue,char FAR *src); SQLRETURN SQL_API my_SQLAllocEnv(SQLHENV FAR * phenv); SQLRETURN SQL_API my_SQLAllocConnect(SQLHENV henv, SQLHDBC FAR *phdbc); SQLRETURN SQL_API my_SQLFreeConnect(SQLHDBC hdbc); SQLRETURN SQL_API my_SQLFreeEnv(SQLHENV henv); char *extend_buffer(NET *net,char *to,ulong length); void myodbc_end(); my_bool set_dynamic_result(STMT FAR *stmt); void set_current_cursor_data(STMT FAR *stmt,SQLUINTEGER irow); my_bool is_minimum_version(const char *server_version,const char *version, uint length); int myodbc_strcasecmp(const char *s, const char *t); int myodbc_casecmp(const char *s, const char *t, uint len); my_bool reget_current_catalog(DBC FAR *dbc); ulong myodbc_escape_string(MYSQL *mysql, char *to, ulong to_length, const char *from, ulong length, int escape_id); DESCREC *desc_get_rec(DESC *desc, int recnum, my_bool expand); DESC *desc_alloc(STMT *stmt, SQLSMALLINT alloc_type, desc_ref_type ref_type, desc_desc_type desc_type); void desc_free_paramdata(DESC *desc); void desc_free(DESC *desc); void desc_rec_init_apd(DESCREC *rec); void desc_rec_init_ipd(DESCREC *rec); void desc_remove_stmt(DESC *desc, STMT *stmt); int desc_find_dae_rec(DESC *desc); SQLRETURN stmt_SQLSetDescField(STMT *stmt, DESC *desc, SQLSMALLINT recnum, SQLSMALLINT fldid, SQLPOINTER val, SQLINTEGER buflen); SQLRETURN stmt_SQLGetDescField(STMT *stmt, DESC *desc, SQLSMALLINT recnum, SQLSMALLINT fldid, SQLPOINTER valptr, SQLINTEGER buflen, SQLINTEGER *strlen); SQLRETURN stmt_SQLCopyDesc(STMT *stmt, DESC *src, DESC *dest); void sqlnum_from_str(const char *numstr, SQL_NUMERIC_STRUCT *sqlnum, int *overflow_ptr); void sqlnum_to_str(SQL_NUMERIC_STRUCT *sqlnum, SQLCHAR *numstr, SQLCHAR **numbegin, SQLCHAR reqprec, SQLSCHAR reqscale, int *truncptr); void *ptr_offset_adjust(void *ptr, SQLULEN *bind_offset, SQLINTEGER bind_type, SQLINTEGER default_size, SQLULEN row); /* Functions used when debugging */ void query_print(FILE *log_file,char *query); FILE *init_query_log(void); void end_query_log(FILE *query_log); LIST *list_delete_forward(LIST *elem); /* proc_* functions - used to parse prcedures headers in SQLProcedureColumns */ char *proc_param_tokenize(char *str, int *params_num); SQLCHAR *proc_get_param_type(SQLCHAR *proc, int len, SQLSMALLINT *ptype); SQLCHAR* proc_get_param_name(SQLCHAR *proc, int len, SQLCHAR *cname); SQLCHAR* proc_get_param_dbtype(SQLCHAR *proc, int len, SQLCHAR *ptype); SQLUINTEGER proc_get_param_size(SQLCHAR *ptype, int len, int sql_type_index, SQLSMALLINT *dec); SQLLEN proc_get_param_octet_len(STMT *stmt, int sql_type_index, SQLULEN col_size, SQLSMALLINT decimal_digits, unsigned int flags, char * str_buff); SQLLEN proc_get_param_col_len(STMT *stmt, int sql_type_index, SQLULEN col_size, SQLSMALLINT decimal_digits, unsigned int flags, char * str_buff); int proc_get_param_sql_type_index(SQLCHAR *ptype, int len); SQLTypeMap *proc_get_param_map_by_index(int index); char *proc_param_next_token(char *str, char *str_end); void set_row_count(STMT * stmt, my_ulonglong rows); #ifdef __WIN__ #define cmp_database(A,B) myodbc_strcasecmp((const char *)(A),(const char *)(B)) #else #define cmp_database(A,B) strcmp((A),(B)) #endif /* Check if an octet_length_ptr is a data-at-exec field. WARNING: This macro evaluates the argument multiple times. */ #define IS_DATA_AT_EXEC(X) ((X) && \ (*(X) == SQL_DATA_AT_EXEC || \ *(X) <= SQL_LEN_DATA_AT_EXEC_OFFSET)) #endif /* __MYUTIL_H__ */ mysql-connector-odbc-5.1.10-src/driver/dll.c100644 15766 12 12245 11707541005 17424 0ustar00cteamstaff/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file dll.c @brief Library initialization functions. */ #include "driver.h" #include char *default_locale, *decimal_point, *thousands_sep; uint decimal_point_length,thousands_sep_length; static my_bool myodbc_inited=0; /* Sigpipe handler */ #if !defined(__WIN__) && defined(SIGPIPE) #include static sig_handler myodbc_pipe_sig_handler(int sig __attribute__((unused))) { #ifdef DONT_REMEMBER_SIGNAL (void) signal(SIGPIPE,myodbc_pipe_sig_handler); #endif } #endif /* @type : myodbc3 internal @purpose : initializations */ void myodbc_init(void) { if (myodbc_inited++) return; my_init(); { struct lconv *tmp; init_getfunctions(); default_locale=my_strdup(setlocale(LC_NUMERIC,NullS),MYF(0)); setlocale(LC_NUMERIC,""); tmp=localeconv(); decimal_point=my_strdup(tmp->decimal_point,MYF(0)); decimal_point_length=strlen(decimal_point); thousands_sep=my_strdup(tmp->thousands_sep,MYF(0)); thousands_sep_length=strlen(thousands_sep); setlocale(LC_NUMERIC,default_locale); utf8_charset_info= get_charset_by_csname("utf8", MYF(MY_CS_PRIMARY), MYF(0)); } /* If we are not using threads, we may get an SIGPIPE signal when a client aborts. We disable this signal to avoid problems. */ #if !defined(__WIN__) && defined(SIGPIPE) signal(SIGPIPE,myodbc_pipe_sig_handler); #endif } /* @type : myodbc3 internal @purpose : clean all resources while unloading.. */ void myodbc_end() { if (!--myodbc_inited) { x_free(decimal_point); x_free(default_locale); x_free(thousands_sep); /* my_thread_end_wait_time was added in 5.1.14 and 5.0.32 */ #if !defined(NONTHREADSAFE) && \ (MYSQL_VERSION_ID >= 50114 || \ (MYSQL_VERSION_ID >= 50032 && MYSQL_VERSION_ID < 50100)) /* This eliminates the delay when my_end() is called and other threads have been initialized but not ended. */ my_thread_end_wait_time= 0; #endif #ifdef MY_DONT_FREE_DBUG /* Function my_end() was changed to deallocate DBUG memory by default, a flag MY_DONT_FREE_DBUG was added to disable this new behaviour */ my_end(MY_DONT_FREE_DBUG); #else my_end(0); #endif } } /* @type : myodbc3 internal @purpose : main entry point */ #ifdef _WIN32 static int inited= 0; int APIENTRY LibMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved) { switch (ul_reason_being_called) { case DLL_PROCESS_ATTACH: /* case of libentry call in win 3.x */ if (!inited++) myodbc_init(); break; case DLL_PROCESS_DETACH: /* case of wep call in win 3.x */ if (!--inited) myodbc_end(); break; /* We don't explicitly call my_thread_init() to avoid initialization in threads that may not even make ODBC calls. my_thread_init() will be called implicitly when mysys calls are made from the thread. */ case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: #ifdef THREAD my_thread_end(); #endif break; default: break; } return TRUE; UNREFERENCED_PARAMETER(lpReserved); } /* @type : myodbc3 internal @purpose : entry point for the DLL */ int __stdcall DllMain(HANDLE hInst,DWORD ul_reason_being_called, LPVOID lpReserved) { return LibMain(hInst,ul_reason_being_called,lpReserved); } #elif defined(__WIN__) /*************************************************************************** This routine is called by LIBSTART.ASM at module load time. All it does in this sample is remember the DLL module handle. The module handle is needed if you want to do things like load stuff from the resource file (for instance string resources). ***************************************************************************/ int _export FAR PASCAL libmain(HANDLE hModule,short cbHeapSize, SQLCHAR FAR *lszCmdLine) { myodbc_init(); return TRUE; } #endif /* __WIN__ */ #ifdef _WIN32 void __declspec(dllexport) FAR PASCAL LoadByOrdinal(void); /* Entry point to cause DM to load using ordinals */ void __declspec(dllexport) FAR PASCAL LoadByOrdinal(void) {} #endif mysql-connector-odbc-5.1.10-src/driver/unicode.c100644 15766 12 77136 11707541005 20311 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file unicode.c @brief Entry points for Unicode versions of ODBC functions */ #include "driver.h" #include #include #define NOT_IMPLEMENTED \ return SQL_ERROR /* Forward declarations. */ SQLRETURN SQL_API SQLColAttributeWImpl(SQLHSTMT hstmt, SQLUSMALLINT column, SQLUSMALLINT field, SQLPOINTER char_attr, SQLSMALLINT char_attr_max, SQLSMALLINT *char_attr_len, SQLLEN *num_attr); SQLRETURN SQL_API SQLGetConnectAttrWImpl(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_max, SQLINTEGER *value_len); SQLRETURN SQL_API SQLGetDiagRecWImpl(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLWCHAR *sqlstate, SQLINTEGER *native_error, SQLWCHAR *message, SQLSMALLINT message_max, SQLSMALLINT *message_len); SQLRETURN SQL_API SQLPrepareWImpl(SQLHSTMT hstmt, SQLWCHAR *str, SQLINTEGER str_len); SQLRETURN SQL_API SQLSetConnectAttrWImpl(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_len); SQLRETURN SQL_API SQLColAttributeW(SQLHSTMT hstmt, SQLUSMALLINT column, SQLUSMALLINT field, SQLPOINTER char_attr, SQLSMALLINT char_attr_max, SQLSMALLINT *char_attr_len, #ifdef USE_SQLCOLATTRIBUTE_SQLLEN_PTR SQLLEN *num_attr #else SQLPOINTER num_attr #endif ) { return SQLColAttributeWImpl(hstmt, column, field, char_attr, char_attr_max, char_attr_len, num_attr); } SQLRETURN SQL_API SQLColAttributeWImpl(SQLHSTMT hstmt, SQLUSMALLINT column, SQLUSMALLINT field, SQLPOINTER char_attr, SQLSMALLINT char_attr_max, SQLSMALLINT *char_attr_len, SQLLEN *num_attr) { STMT *stmt= (STMT *)hstmt; SQLCHAR *value= NULL; SQLWCHAR *wvalue; SQLINTEGER len= SQL_NTS; uint errors; SQLRETURN rc= MySQLColAttribute(hstmt, column, field, &value, num_attr); if (value) { my_bool free_value= FALSE; wvalue= sqlchar_as_sqlwchar(stmt->dbc->cxn_charset_info, value, &len, &errors); /* char_attr_max is in bytes, we want it in chars. */ char_attr_max/= sizeof(SQLWCHAR); if (len > char_attr_max - 1) rc= set_error(stmt, MYERR_01004, NULL, 0); if (char_attr_len) *char_attr_len= (SQLSMALLINT)len * sizeof(SQLWCHAR); if (char_attr_max > 0) { len= myodbc_min(len, char_attr_max - 1); (void)memcpy((char *)char_attr, (const char *)wvalue, len * sizeof(SQLWCHAR)); ((SQLWCHAR *)char_attr)[len]= 0; } if (free_value) x_free(value); x_free(wvalue); } return rc; } SQLRETURN SQL_API SQLColAttributesW(SQLHSTMT hstmt, SQLUSMALLINT column, SQLUSMALLINT field, SQLPOINTER char_attr, SQLSMALLINT char_attr_max, SQLSMALLINT *char_attr_len, SQLLEN *num_attr) { return SQLColAttributeWImpl(hstmt, column, field, char_attr, char_attr_max, char_attr_len, num_attr); } SQLRETURN SQL_API SQLColumnPrivilegesW(SQLHSTMT hstmt, SQLWCHAR *catalog, SQLSMALLINT catalog_len, SQLWCHAR *schema, SQLSMALLINT schema_len, SQLWCHAR *table, SQLSMALLINT table_len, SQLWCHAR *column, SQLSMALLINT column_len) { SQLRETURN rc; SQLCHAR *catalog8, *schema8, *table8, *column8; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len; uint errors= 0; len= catalog_len; catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= schema_len; schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= table_len; table8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; len= column_len; column8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, column, &len, &errors); column_len= (SQLSMALLINT)len; rc= MySQLColumnPrivileges(hstmt, catalog8, catalog_len, schema8, schema_len, table8, table_len, column8, column_len); x_free(catalog8); x_free(schema8); x_free(table8); x_free(column8); return rc; } SQLRETURN SQL_API SQLColumnsW(SQLHSTMT hstmt, SQLWCHAR *catalog, SQLSMALLINT catalog_len, SQLWCHAR *schema, SQLSMALLINT schema_len, SQLWCHAR *table, SQLSMALLINT table_len, SQLWCHAR *column, SQLSMALLINT column_len) { SQLRETURN rc; SQLCHAR *catalog8, *schema8, *table8, *column8; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len; uint errors= 0; len= catalog_len; catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= schema_len; schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= table_len; table8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; len= column_len; column8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, column, &len, &errors); column_len= (SQLSMALLINT)len; rc= MySQLColumns(hstmt, catalog8, catalog_len, schema8, schema_len, table8, table_len, column8, column_len); x_free(catalog8); x_free(schema8); x_free(table8); x_free(column8); return rc; } SQLRETURN SQL_API SQLConnectW(SQLHDBC hdbc, SQLWCHAR *dsn, SQLSMALLINT dsn_len, SQLWCHAR *user, SQLSMALLINT user_len, SQLWCHAR *auth, SQLSMALLINT auth_len) { ((DBC *)hdbc)->unicode= TRUE; /* Hooray, a Unicode connection! */ return MySQLConnect(hdbc, dsn, dsn_len, user, user_len, auth, auth_len); } SQLRETURN SQL_API SQLDriverConnectW(SQLHDBC hdbc, SQLHWND hwnd, SQLWCHAR *in, SQLSMALLINT in_len, SQLWCHAR *out, SQLSMALLINT out_max, SQLSMALLINT *out_len, SQLUSMALLINT completion) { ((DBC *)hdbc)->unicode= TRUE; /* Hooray, a Unicode connection! */ return MySQLDriverConnect(hdbc, hwnd, in, in_len, out, out_max, out_len, completion); } SQLRETURN SQL_API SQLDescribeColW(SQLHSTMT hstmt, SQLUSMALLINT column, SQLWCHAR *name, SQLSMALLINT name_max, SQLSMALLINT *name_len, SQLSMALLINT *type, SQLULEN *size, SQLSMALLINT *scale, SQLSMALLINT *nullable) { STMT *stmt= (STMT *)hstmt; SQLCHAR *value= NULL; SQLWCHAR *wvalue= NULL; SQLINTEGER len= SQL_NTS; SQLSMALLINT free_value; uint errors; SQLRETURN rc= MySQLDescribeCol(hstmt, column, &value, &free_value, type, size, scale, nullable); if (free_value == -1) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } if (value) { wvalue= sqlchar_as_sqlwchar(stmt->dbc->cxn_charset_info, value, &len, &errors); if (len == -1) { if (free_value) x_free(value); set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } if (len > name_max - 1) rc= set_error(stmt, MYERR_01004, NULL, 0); if (name_len) *name_len= (SQLSMALLINT)len; if (name && name_max > 0) { len= myodbc_min(len, name_max - 1); (void)memcpy((char *)name, (const char *)wvalue, len * sizeof(SQLWCHAR)); ((SQLWCHAR *)name)[len]= 0; } if (free_value) x_free(value); x_free(wvalue); } return rc; } SQLRETURN SQL_API SQLErrorW(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt, SQLWCHAR *sqlstate, SQLINTEGER *native_error, SQLWCHAR *message, SQLSMALLINT message_max, SQLSMALLINT *message_len) { SQLRETURN rc= SQL_INVALID_HANDLE; if (hstmt) { rc= SQLGetDiagRecWImpl(SQL_HANDLE_STMT, hstmt, NEXT_STMT_ERROR(hstmt), sqlstate, native_error, message, message_max, message_len); } else if (hdbc) { rc= SQLGetDiagRecWImpl(SQL_HANDLE_DBC, hdbc, NEXT_DBC_ERROR(hdbc), sqlstate, native_error, message, message_max, message_len); } else if (henv) { rc= SQLGetDiagRecWImpl(SQL_HANDLE_ENV, henv, NEXT_ENV_ERROR(henv), sqlstate, native_error, message, message_max, message_len); } return rc; } SQLRETURN SQL_API SQLExecDirectW(SQLHSTMT hstmt, SQLWCHAR *str, SQLINTEGER str_len) { int error; if ((error= SQLPrepareWImpl(hstmt, str, str_len))) return error; error= my_SQLExecute((STMT *)hstmt); return error; } SQLRETURN SQL_API SQLForeignKeysW(SQLHSTMT hstmt, SQLWCHAR *pk_catalog, SQLSMALLINT pk_catalog_len, SQLWCHAR *pk_schema, SQLSMALLINT pk_schema_len, SQLWCHAR *pk_table, SQLSMALLINT pk_table_len, SQLWCHAR *fk_catalog, SQLSMALLINT fk_catalog_len, SQLWCHAR *fk_schema, SQLSMALLINT fk_schema_len, SQLWCHAR *fk_table, SQLSMALLINT fk_table_len) { SQLRETURN rc; SQLCHAR *pk_catalog8, *pk_schema8, *pk_table8, *fk_catalog8, *fk_schema8, *fk_table8; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len; uint errors= 0; len= pk_catalog_len; pk_catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, pk_catalog, &len, &errors); pk_catalog_len= (SQLSMALLINT)len; len= pk_schema_len; pk_schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, pk_schema, &len, &errors); pk_schema_len= (SQLSMALLINT)len; len= pk_table_len; pk_table8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, pk_table, &len, &errors); pk_table_len= (SQLSMALLINT)len; len= fk_catalog_len; fk_catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, fk_catalog, &len, &errors); fk_catalog_len= (SQLSMALLINT)len; len= fk_schema_len; fk_schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, fk_schema, &len, &errors); fk_schema_len= (SQLSMALLINT)len; len= fk_table_len; fk_table8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, fk_table, &len, &errors); fk_table_len= (SQLSMALLINT)len; rc= MySQLForeignKeys(hstmt, pk_catalog8, pk_catalog_len, pk_schema8, pk_schema_len, pk_table8, pk_table_len, fk_catalog8, fk_catalog_len, fk_schema8, fk_schema_len, fk_table8, fk_table_len); x_free(pk_catalog8); x_free(pk_schema8); x_free(pk_table8); x_free(fk_catalog8); x_free(fk_schema8); x_free(fk_table8); return rc; } SQLRETURN SQL_API SQLGetConnectAttrW(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_max, SQLINTEGER *value_len) { return SQLGetConnectAttrWImpl(hdbc, attribute, value, value_max, value_len); } SQLRETURN SQL_API SQLGetConnectAttrWImpl(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_max, SQLINTEGER *value_len) { DBC *dbc= (DBC *)hdbc; SQLCHAR *char_value= NULL; SQLRETURN rc= MySQLGetConnectAttr(hdbc, attribute, &char_value, value); if (char_value) { SQLWCHAR *wvalue; SQLINTEGER len= SQL_NTS; uint errors; wvalue= sqlchar_as_sqlwchar(dbc->cxn_charset_info, char_value, &len, &errors); /* value_max is in bytes, we want it in chars. */ value_max/= sizeof(SQLWCHAR); if (len > value_max - 1) rc= set_conn_error(dbc, MYERR_01004, NULL, 0); if (value_len) *value_len= len * sizeof(SQLWCHAR); if (value_max > 0) { len= myodbc_min(len, value_max - 1); (void)memcpy((char *)value, (const char *)wvalue, len * sizeof(SQLWCHAR)); ((SQLWCHAR *)value)[len]= 0; } x_free(wvalue); } return rc; } SQLRETURN SQL_API SQLGetConnectOptionW(SQLHDBC hdbc, SQLUSMALLINT option, SQLPOINTER param) { return SQLGetConnectAttrWImpl(hdbc, option, param, ((option == SQL_ATTR_CURRENT_CATALOG) ? SQL_MAX_OPTION_STRING_LENGTH : 0), NULL); } SQLRETURN SQL_API SQLGetCursorNameW(SQLHSTMT hstmt, SQLWCHAR *cursor, SQLSMALLINT cursor_max, SQLSMALLINT *cursor_len) { SQLRETURN rc= SQL_SUCCESS; STMT *stmt= (STMT *)hstmt; SQLWCHAR *name; SQLINTEGER len= SQL_NTS; uint errors; CLEAR_STMT_ERROR(stmt); if (cursor_max < 0) return set_error(stmt, MYERR_S1090, NULL, 0); name= sqlchar_as_sqlwchar(stmt->dbc->cxn_charset_info, MySQLGetCursorName(hstmt), &len, &errors); if (cursor_len) *cursor_len= (SQLSMALLINT)len; /* Warn if name truncated */ if (len > cursor_max - 1) rc= set_error(stmt, MYERR_01004, NULL, 0); if (cursor_max > 0) { len= myodbc_min(len, cursor_max - 1); (void)memcpy((char *)cursor, (const char *)name, len * sizeof(SQLWCHAR)); cursor[len]= 0; } x_free(name); return rc; } SQLRETURN SQL_API SQLGetDiagFieldW(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLSMALLINT field, SQLPOINTER info, SQLSMALLINT info_max, SQLSMALLINT *info_len) { DBC *dbc; SQLCHAR *value= NULL; SQLINTEGER len= SQL_NTS; SQLRETURN rc= MySQLGetDiagField(handle_type, handle, record, field, &value, info); switch (handle_type) { case SQL_HANDLE_DBC: dbc= (DBC *)handle; break; case SQL_HANDLE_STMT: dbc= ((STMT *)handle)->dbc; break; case SQL_HANDLE_DESC: dbc= DESC_GET_DBC((DESC *)handle); break; case SQL_HANDLE_ENV: default: dbc= NULL; } if (value) { uint errors; SQLWCHAR *wvalue= sqlchar_as_sqlwchar((dbc && dbc->cxn_charset_info) ? dbc->cxn_charset_info : default_charset_info, value, &len, &errors); /* info_max is in bytes, we want it in chars. */ info_max/= sizeof(SQLWCHAR); if (len > info_max - 1) rc= set_conn_error(dbc, MYERR_01004, NULL, 0); if (info_len) *info_len= (SQLSMALLINT)len * sizeof(SQLWCHAR); if (info_max > 0) { len= myodbc_min(len, info_max - 1); (void)memcpy((char *)info, (const char *)wvalue, len * sizeof(SQLWCHAR)); ((SQLWCHAR *)info)[len]= 0; } x_free(wvalue); } return rc; } SQLRETURN SQL_API SQLGetDiagRecW(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLWCHAR *sqlstate, SQLINTEGER *native_error, SQLWCHAR *message, SQLSMALLINT message_max, SQLSMALLINT *message_len) { return SQLGetDiagRecWImpl(handle_type, handle, record, sqlstate, native_error, message, message_max, message_len); } SQLRETURN SQL_API SQLGetDiagRecWImpl(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT record, SQLWCHAR *sqlstate, SQLINTEGER *native_error, SQLWCHAR *message, SQLSMALLINT message_max, SQLSMALLINT *message_len) { SQLRETURN rc; DBC *dbc; SQLCHAR *msg_value= NULL, *sqlstate_value= NULL; SQLINTEGER len= SQL_NTS; uint errors; switch (handle_type) { case SQL_HANDLE_DBC: dbc= (DBC *)handle; break; case SQL_HANDLE_STMT: dbc= ((STMT *)handle)->dbc; break; case SQL_HANDLE_DESC: dbc= DESC_GET_DBC((DESC *)handle); break; case SQL_HANDLE_ENV: default: dbc= NULL; } if (message_max < 0) return SQL_ERROR; rc= MySQLGetDiagRec(handle_type, handle, record, &sqlstate_value, native_error, &msg_value); if (msg_value) { SQLWCHAR *wvalue= sqlchar_as_sqlwchar((dbc && dbc->cxn_charset_info) ? dbc->cxn_charset_info : default_charset_info, msg_value, &len, &errors); if (len > message_max - 1) rc= SQL_SUCCESS_WITH_INFO; if (message_len) *message_len= (SQLSMALLINT)len; if (message_max > 0) { len= myodbc_min(len, message_max - 1); (void)memcpy((char *)message, (const char *)wvalue, len * sizeof(SQLWCHAR)); ((SQLWCHAR *)message)[len]= 0; } x_free(wvalue); } len= SQL_NTS; if (sqlstate && sqlstate_value) { SQLWCHAR *wvalue= sqlchar_as_sqlwchar((dbc && dbc->cxn_charset_info) ? dbc->cxn_charset_info : default_charset_info, sqlstate_value, &len, &errors); if (wvalue) { (void)memcpy((char *)sqlstate, (const char *)wvalue, 5 * sizeof(SQLWCHAR)); } else { sqlstate[0]= '0'; sqlstate[1]= '0'; sqlstate[2]= '0'; sqlstate[3]= '0'; sqlstate[4]= '0'; } ((SQLWCHAR *)sqlstate)[5]= 0; x_free(wvalue); } return rc; } SQLRETURN SQL_API SQLGetInfoW(SQLHDBC hdbc, SQLUSMALLINT type, SQLPOINTER value, SQLSMALLINT value_max, SQLSMALLINT *value_len) { DBC *dbc= (DBC *)hdbc; SQLCHAR *char_value= NULL; SQLINTEGER len= SQL_NTS; uint errors; SQLRETURN rc= MySQLGetInfo(hdbc, type, &char_value, value, value_len); if (char_value) { SQLWCHAR *wvalue= sqlchar_as_sqlwchar((dbc->cxn_charset_info ? dbc->cxn_charset_info : default_charset_info), char_value, &len, &errors); /* value_max is in bytes, we want it in chars. */ value_max/= sizeof(SQLWCHAR); if (len > value_max - 1) rc= set_conn_error(dbc, MYERR_01004, NULL, 0); if (value_len) *value_len= (SQLSMALLINT)len * sizeof(SQLWCHAR); if (value && value_max > 0) { len= myodbc_min(len, value_max - 1); (void)memcpy((char *)value, (const char *)wvalue, len * sizeof(SQLWCHAR)); ((SQLWCHAR *)value)[len]= 0; } x_free(wvalue); } return rc; } SQLRETURN SQL_API SQLGetStmtAttrW(SQLHSTMT hstmt, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_max, SQLINTEGER *value_len) { return MySQLGetStmtAttr(hstmt, attribute, value, value_max, value_len); } /* This shouldn't be necessary, but iODBC expects it. */ SQLRETURN SQL_API SQLGetTypeInfoW(SQLHSTMT hstmt, SQLSMALLINT type) { return MySQLGetTypeInfo(hstmt, type); } SQLRETURN SQL_API SQLNativeSqlW(SQLHDBC hdbc, SQLWCHAR *in, SQLINTEGER in_len, SQLWCHAR *out, SQLINTEGER out_max, SQLINTEGER *out_len) { SQLRETURN rc= SQL_SUCCESS; if (in_len == SQL_NTS) in_len= (SQLINTEGER)sqlwcharlen(in); if (out_len) *out_len= in_len; if (in_len > out_max) rc= set_conn_error((DBC *)hdbc, MYERR_01004, NULL, 0); if (out_max > 0) { if (in_len > out_max - 1) in_len= out_max - 1; (void)memcpy((char *)out, (const char *)in, in_len * sizeof(SQLWCHAR)); out[in_len]= 0; } return rc; } SQLRETURN SQL_API SQLPrepareW(SQLHSTMT hstmt, SQLWCHAR *str, SQLINTEGER str_len) { return SQLPrepareWImpl(hstmt, str, str_len); } SQLRETURN SQL_API SQLPrepareWImpl(SQLHSTMT hstmt, SQLWCHAR *str, SQLINTEGER str_len) { STMT *stmt= (STMT *)hstmt; uint errors; SQLCHAR *conv= sqlwchar_as_sqlchar(stmt->dbc->cxn_charset_info, str, &str_len, &errors); /* Character conversion problems are not tolerated. */ if (errors) { x_free(conv); return set_stmt_error(stmt, "22018", NULL, 0); } return MySQLPrepare(hstmt, conv, str_len, TRUE); } SQLRETURN SQL_API SQLPrimaryKeysW(SQLHSTMT hstmt, SQLWCHAR *catalog, SQLSMALLINT catalog_len, SQLWCHAR *schema, SQLSMALLINT schema_len, SQLWCHAR *table, SQLSMALLINT table_len) { SQLRETURN rc; SQLCHAR *catalog8, *schema8, *table8; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len; uint errors= 0; len= catalog_len; catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= schema_len; schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= table_len; table8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; rc= MySQLPrimaryKeys(hstmt, catalog8, catalog_len, schema8, schema_len, table8, table_len); x_free(catalog8); x_free(schema8); x_free(table8); return rc; } SQLRETURN SQL_API SQLProcedureColumnsW(SQLHSTMT hstmt, SQLWCHAR *catalog, SQLSMALLINT catalog_len, SQLWCHAR *schema, SQLSMALLINT schema_len, SQLWCHAR *proc, SQLSMALLINT proc_len, SQLWCHAR *column, SQLSMALLINT column_len) { SQLRETURN rc; SQLCHAR *catalog8, *schema8, *proc8, *column8; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len; uint errors= 0; len= catalog_len; catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= schema_len; schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= proc_len; proc8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, proc, &len, &errors); proc_len= (SQLSMALLINT)len; len= column_len; column8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, column, &len, &errors); column_len= (SQLSMALLINT)len; rc= MySQLProcedureColumns(hstmt, catalog8, catalog_len, schema8, schema_len, proc8, proc_len, column8, column_len); x_free(catalog8); x_free(schema8); x_free(proc8); x_free(column8); return rc; } SQLRETURN SQL_API SQLProceduresW(SQLHSTMT hstmt, SQLWCHAR *catalog, SQLSMALLINT catalog_len, SQLWCHAR *schema, SQLSMALLINT schema_len, SQLWCHAR *proc, SQLSMALLINT proc_len) { SQLRETURN rc; SQLCHAR *catalog8, *schema8, *proc8; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len; uint errors= 0; len= catalog_len; catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= schema_len; schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= proc_len; proc8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, proc, &len, &errors); proc_len= (SQLSMALLINT)len; rc= MySQLProcedures(hstmt, catalog8, catalog_len, schema8, schema_len, proc8, proc_len); x_free(catalog8); x_free(schema8); x_free(proc8); return rc; } SQLRETURN SQL_API SQLSetConnectAttrW(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_len) { return SQLSetConnectAttrWImpl(hdbc, attribute, value, value_len); } SQLRETURN SQL_API SQLSetConnectAttrWImpl(SQLHDBC hdbc, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_len) { SQLRETURN rc; DBC *dbc= (DBC *)hdbc; my_bool free_value= FALSE; /* Let's make it for windows only so far */ #ifdef _WIN32 SQLINTEGER len= value_len == SQL_NTS ? SQL_NTS : value_len / sizeof(SQLWCHAR); #else SQLINTEGER len= value_len == SQL_NTS ? SQL_NTS : value_len; #endif if (attribute == SQL_ATTR_CURRENT_CATALOG) { uint errors= 0; if (is_connected(dbc)) value= sqlwchar_as_sqlchar(dbc->cxn_charset_info, value, &len, &errors); else value= sqlwchar_as_sqlchar(default_charset_info, value, &len, &errors); free_value= TRUE; } rc= MySQLSetConnectAttr(hdbc, attribute, value, len); if (free_value) x_free(value); return rc; } SQLRETURN SQL_API SQLSetCursorNameW(SQLHSTMT hstmt, SQLWCHAR *name, SQLSMALLINT name_len) { SQLRETURN rc; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len= name_len; uint errors= 0; SQLCHAR *name_char= sqlwchar_as_sqlchar(dbc->cxn_charset_info, name, &len, &errors); rc= MySQLSetCursorName(hstmt, name_char, (SQLSMALLINT)len); x_free(name_char); /* Character conversion problems are not tolerated. */ if (errors) { return set_stmt_error((STMT *)hstmt, "HY000", "Cursor name included characters that could not " "be converted to connection character set", 0); } return rc; } SQLRETURN SQL_API SQLSetConnectOptionW(SQLHDBC hdbc, SQLUSMALLINT option, SQLULEN param) { return SQLSetConnectAttrWImpl(hdbc, option, (SQLPOINTER)param, ((option == SQL_ATTR_CURRENT_CATALOG) ? SQL_NTS : 0)); } SQLRETURN SQL_API SQLSetStmtAttrW(SQLHSTMT hstmt, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER value_len) { /* Nothing special to do, since we don't have any string stmt attribs */ return MySQLSetStmtAttr(hstmt, attribute, value, value_len); } SQLRETURN SQL_API SQLSpecialColumnsW(SQLHSTMT hstmt, SQLUSMALLINT type, SQLWCHAR *catalog, SQLSMALLINT catalog_len, SQLWCHAR *schema, SQLSMALLINT schema_len, SQLWCHAR *table, SQLSMALLINT table_len, SQLUSMALLINT scope, SQLUSMALLINT nullable) { SQLRETURN rc; SQLCHAR *catalog8, *schema8, *table8; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len; uint errors= 0; len= catalog_len; catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= schema_len; schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= table_len; table8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; rc= MySQLSpecialColumns(hstmt, type, catalog8, catalog_len, schema8, schema_len, table8, table_len, scope, nullable); x_free(catalog8); x_free(schema8); x_free(table8); return rc; } SQLRETURN SQL_API SQLStatisticsW(SQLHSTMT hstmt, SQLWCHAR *catalog, SQLSMALLINT catalog_len, SQLWCHAR *schema, SQLSMALLINT schema_len, SQLWCHAR *table, SQLSMALLINT table_len, SQLUSMALLINT unique, SQLUSMALLINT accuracy) { SQLRETURN rc; SQLCHAR *catalog8, *schema8, *table8; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len; uint errors= 0; len= catalog_len; catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= schema_len; schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= table_len; table8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; rc= MySQLStatistics(hstmt, catalog8, catalog_len, schema8, schema_len, table8, table_len, unique, accuracy); x_free(catalog8); x_free(schema8); x_free(table8); return rc; } SQLRETURN SQL_API SQLTablePrivilegesW(SQLHSTMT hstmt, SQLWCHAR *catalog, SQLSMALLINT catalog_len, SQLWCHAR *schema, SQLSMALLINT schema_len, SQLWCHAR *table, SQLSMALLINT table_len) { SQLRETURN rc; SQLCHAR *catalog8, *schema8, *table8; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len; uint errors= 0; len= catalog_len; catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, catalog, &len, &errors); catalog_len= (SQLSMALLINT)len; len= schema_len; schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, schema, &len, &errors); schema_len= (SQLSMALLINT)len; len= table_len; table8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, table, &len, &errors); table_len= (SQLSMALLINT)len; rc= MySQLTablePrivileges(hstmt, catalog8, catalog_len, schema8, schema_len, table8, table_len); x_free(catalog8); x_free(schema8); x_free(table8); return rc; } SQLRETURN SQL_API SQLTablesW(SQLHSTMT hstmt, SQLWCHAR *catalog, SQLSMALLINT catalog_len, SQLWCHAR *schema, SQLSMALLINT schema_len, SQLWCHAR *table, SQLSMALLINT table_len, SQLWCHAR *type, SQLSMALLINT type_len) { SQLRETURN rc; SQLCHAR *catalog8, *schema8, *table8, *type8; DBC *dbc= ((STMT *)hstmt)->dbc; SQLINTEGER len; uint errors= 0; /* we must preserve NULL/blank strings for SQLTables() semantics */ len= catalog_len; catalog8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, catalog, &len, &errors); if (catalog && !len) catalog8= ""; catalog_len= (SQLSMALLINT)len; len= schema_len; schema8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, schema, &len, &errors); if (schema && !len) schema8= ""; schema_len= (SQLSMALLINT)len; len= table_len; table8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, table, &len, &errors); if (table && !len) table8= ""; table_len= (SQLSMALLINT)len; len= type_len; type8= sqlwchar_as_sqlchar(dbc->cxn_charset_info, type, &len, &errors); type_len= (SQLSMALLINT)len; rc= MySQLTables(hstmt, catalog8, catalog_len, schema8, schema_len, table8, table_len, type8, type_len); if (catalog_len) x_free(catalog8); if (schema_len) x_free(schema8); if (table_len) x_free(table8); x_free(type8); return rc; } SQLRETURN SQL_API SQLGetDescFieldW(SQLHDESC hdesc, SQLSMALLINT record, SQLSMALLINT field, SQLPOINTER value, SQLINTEGER value_max, SQLINTEGER *value_len) { return MySQLGetDescField(hdesc, record, field, value, value_max, value_len); } SQLRETURN SQL_API SQLGetDescRecW(SQLHDESC hdesc, SQLSMALLINT record, SQLWCHAR *name, SQLSMALLINT name_max, SQLSMALLINT *name_len, SQLSMALLINT *type, SQLSMALLINT *subtype, SQLLEN *length, SQLSMALLINT *precision, SQLSMALLINT *scale, SQLSMALLINT *nullable) { NOT_IMPLEMENTED; } SQLRETURN SQL_API SQLSetDescFieldW(SQLHDESC hdesc, SQLSMALLINT record, SQLSMALLINT field, SQLPOINTER value, SQLINTEGER value_len) { return MySQLSetDescField(hdesc, record, field, value, value_len); } SQLRETURN SQL_API SQLSetDescRecW(SQLHDESC hdesc, SQLSMALLINT record, SQLSMALLINT type, SQLSMALLINT subtype, SQLLEN length, SQLSMALLINT precision, SQLSMALLINT scale, SQLPOINTER data_ptr, SQLLEN *octet_length_ptr, SQLLEN *indicator_ptr) { NOT_IMPLEMENTED; } #ifdef NOT_IMPLEMENTED_YET SQLRETURN SQL_API SQLBrowseConnectW(SQLHDBC hdbc, SQLWCHAR *in, SQLSMALLINT in_len, SQLWCHAR *out, SQLSMALLINT out_max, SQLSMALLINT *out_len) { NOT_IMPLEMENTED; } //SQLDataSourcesW //SQLDriversW #endif mysql-connector-odbc-5.1.10-src/driver/cursor.c100644 15766 12 137113 11707541005 20210 0ustar00cteamstaff/* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file cursor.c @brief Client-side cursor functions */ /*************************************************************************** * The following ODBC APIs are implemented in this file: * * * * SQLSetCursorName (ISO 92) * * SQLGetCursorName (ISO 92) * * SQLCloseCursor (ISO 92) * * SQLSetPos (ODBC) * * SQLBulkOperations (ODBC) * * * ****************************************************************************/ #include "driver.h" #include /* @type : myodbc3 internal @purpose : returns the table used by this query and ensures that all columns are from the same table */ static const char *find_used_table(STMT *stmt) { MYSQL_FIELD *field, *end; char *table_name; MYSQL_RES *result= stmt->result; if ( stmt->table_name && stmt->table_name[0] ) return stmt->table_name; table_name= 0; for ( field= result->fields, end= field+ result->field_count; field < end ; ++field ) { #if MYSQL_VERSION_ID >= 40100 if ( field->org_table ) { if ( !table_name ) table_name= field->org_table; if ( strcmp(field->org_table, table_name) ) { set_error(stmt,MYERR_S1000, "Can't modify a row from a statement that uses more than one table",0); return NULL; } } #else if ( field->table ) { if ( !table_name ) table_name= field->table; if ( strcmp(field->table, table_name) ) { set_error(stmt,MYERR_S1000, "Can't modify a row from a statement that uses more than one table",0); return NULL; } } #endif } /* We have to copy the strings as we may have to re-issue the query while using cursors. */ stmt->table_name= dupp_str(table_name,SQL_NTS); return stmt->table_name; } /* @type : myodbc internal @purpose : returns the previous token in the query by eating white spaces if we found the start of the string, return it */ static const char *mystr_get_prev_token(CHARSET_INFO *charset, const char **query, const char *start) { const char *pos= *query; do { if (pos == start) return (*query = start); /* Return start of string */ --pos; } while (*pos < 0 || !my_isspace(charset, *pos)) ; *query= pos; /* Remember pos to space */ return pos + 1; /* Return found token */ } /** Check if a statement involves a positioned cursor using the WHERE CURRENT OF syntax. @param[in] pStmt Handle of the statement @param[out] pStmtCursor Pointer to the statement referred to by the cursor @return Pointer to the beginning of 'WHERE CURRENT OF' */ char *check_if_positioned_cursor_exists(STMT *pStmt, STMT **pStmtCursor) { if (pStmt->query && pStmt->query_end) { const char *pszQueryTokenPos= pStmt->query_end; const char *pszCursor= mystr_get_prev_token(pStmt->dbc->ansi_charset_info, (const char**)&pszQueryTokenPos, pStmt->query); if (!myodbc_casecmp(mystr_get_prev_token(pStmt->dbc->ansi_charset_info, &pszQueryTokenPos, pStmt->query),"OF",2) && !myodbc_casecmp(mystr_get_prev_token(pStmt->dbc->ansi_charset_info, &pszQueryTokenPos, pStmt->query),"CURRENT",7) && !myodbc_casecmp(mystr_get_prev_token(pStmt->dbc->ansi_charset_info, &pszQueryTokenPos, pStmt->query),"WHERE",5) ) { LIST *list_element; DBC *dbc= (DBC *)pStmt->dbc; /* Scan the list of statements for this connection and see if we can find the cursor name this statement is referring to - it must have a result set to count. */ for (list_element= dbc->statements; list_element; list_element= list_element->next) { *pStmtCursor= (HSTMT)list_element->data; /* Even if the cursor name matches, the statement must have a result set to count. */ if ((*pStmtCursor)->result && (*pStmtCursor)->cursor.name && !myodbc_strcasecmp((*pStmtCursor)->cursor.name, pszCursor)) { return (char *)pszQueryTokenPos; } } /* Did we run out of statements without finding a viable cursor? */ if (!list_element) { char buff[200]; strxmov(buff,"Cursor '", pszCursor, "' does not exist or does not have a result set.", NullS); set_stmt_error(pStmt, "34000", buff, ER_INVALID_CURSOR_NAME); } return (char *)pszQueryTokenPos; } } return NULL; } /** Check if a field exists in a result set. @param[in] name Name of the field @param[in] result Result set to check */ static my_bool have_field_in_result(const char *name, MYSQL_RES *result) { MYSQL_FIELD *field; unsigned int ncol; for (ncol= 0; ncol < result->field_count; ++ncol) { field= result->fields + ncol; if (myodbc_strcasecmp(name, #if MYSQL_VERSION_ID >= 40100 field->org_name #else field->name #endif ) == 0) return TRUE; } return FALSE; } /** Check if a primary or unique key exists in the table referred to by the statement for which all of the component fields are in the result set. If such a key exists, the field names are stored in the cursor. @param[in] stmt Statement @return Whether a usable unique keys exists */ static my_bool check_if_usable_unique_key_exists(STMT *stmt) { char buff[NAME_LEN * 2 + 18], /* Possibly escaped name, plus text for query */ *pos, *table; MYSQL_RES *res; MYSQL_ROW row; int seq_in_index= 0; if (stmt->cursor.pk_validated) return stmt->cursor.pk_count; #if MYSQL_VERSION_ID >= 40100 if (stmt->result->fields->org_table) table= stmt->result->fields->org_table; else #endif table= stmt->result->fields->table; /* Use SHOW KEYS FROM table to check for keys. */ pos= strmov(buff, "SHOW KEYS FROM `"); pos+= mysql_real_escape_string(&stmt->dbc->mysql, pos, table, strlen(table)); pos= strmov(pos, "`"); MYLOG_QUERY(stmt, buff); pthread_mutex_lock(&stmt->dbc->lock); if (mysql_query(&stmt->dbc->mysql, buff) || !(res= mysql_store_result(&stmt->dbc->mysql))) { set_error(stmt, MYERR_S1000, mysql_error(&stmt->dbc->mysql), mysql_errno(&stmt->dbc->mysql)); pthread_mutex_unlock(&stmt->dbc->lock); return FALSE; } while ((row= mysql_fetch_row(res)) && stmt->cursor.pk_count < MY_MAX_PK_PARTS) { int seq= atoi(row[3]); /* If this is a new key, we're done! */ if (seq <= seq_in_index) break; /* Unless it is non_unique, it does us no good. */ if (row[1][0] == '1') continue; /* If this isn't the next part, this key is no good. */ if (seq != seq_in_index + 1) continue; /* Check that we have the key field in our result set. */ if (have_field_in_result(row[4], stmt->result)) { /* We have a unique key field -- copy it, and increment our count. */ strmov(stmt->cursor.pkcol[stmt->cursor.pk_count++].name, row[4]); seq_in_index= seq; } else /* Forget about any key we had in progress, we didn't have it all. */ stmt->cursor.pk_count= seq_in_index= 0; } mysql_free_result(res); pthread_mutex_unlock(&stmt->dbc->lock); /* Remember that we've figured this out already. */ stmt->cursor.pk_validated= 1; return stmt->cursor.pk_count > 0; } /* @type : myodbc3 internal @purpose : positions the data cursor to appropriate row */ void set_current_cursor_data(STMT FAR *stmt,SQLUINTEGER irow) { long nrow, row_pos; MYSQL_RES *result= stmt->result; MYSQL_ROWS *dcursor= result->data->data; /* If irow exists, then position the current row to point to the rowsetsize+irow, this is needed for positioned calls */ row_pos= irow ? (long) (stmt->current_row+irow-1) : stmt->current_row; if ( stmt->cursor_row != row_pos ) { for ( nrow= 0; nrow < row_pos; ++nrow ) dcursor= dcursor->next; stmt->cursor_row= row_pos; result->data_cursor= dcursor; } } /* @type : myodbc3 internal @purpose : sets the dynamic cursor, when the cursor is not set explicitly by the application */ static void set_dynamic_cursor_name(STMT FAR *stmt) { stmt->cursor.name= (char*) my_malloc(MYSQL_MAX_CURSOR_LEN,MYF(MY_ZEROFILL)); sprintf((char*) stmt->cursor.name,"SQL_CUR%d",stmt->dbc->cursor_count++); } /* @type : myodbc3 internal @purpose : updates the stmt status information */ static SQLRETURN update_status(STMT FAR *stmt, SQLUSMALLINT status) { if ( stmt->affected_rows == 0 ) return set_error(stmt,MYERR_01S03,NULL,0); else if ( stmt->affected_rows > 1 ) return set_error(stmt,MYERR_01S04,NULL,0); /* This only comes from SQLExecute(), not SQLSetPos() or SQLBulkOperations(), so we don't have to worry about the row status set by SQLExtendedFetch(). */ else if ( stmt->ird->array_status_ptr ) { SQLUSMALLINT *ptr= stmt->ird->array_status_ptr+stmt->current_row; SQLUSMALLINT *end= ptr+stmt->affected_rows; for ( ; ptr != end ; ++ptr ) *ptr= status; } return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : updates the SQLSetPos status information */ static SQLRETURN update_setpos_status(STMT FAR *stmt, SQLINTEGER irow, my_ulonglong rows, SQLUSMALLINT status) { stmt->affected_rows= stmt->dbc->mysql.affected_rows= rows; if (irow && rows > 1) return set_error(stmt,MYERR_01S04,NULL,0); /* If all rows successful, then only update status..else don't update...just for the sake of performance.. */ if (stmt->ird->array_status_ptr) { SQLUSMALLINT *ptr= stmt->ird->array_status_ptr; SQLUSMALLINT *end= ptr+rows; for ( ; ptr != end; ++ptr) *ptr= status; } if (stmt->stmt_options.rowStatusPtr_ex) { SQLUSMALLINT *ptr= stmt->stmt_options.rowStatusPtr_ex; SQLUSMALLINT *end= ptr+rows; for ( ; ptr != end; ++ptr) *ptr= status; } return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : copy row buffers to statement */ static SQLRETURN copy_rowdata(STMT FAR *stmt, DESCREC *aprec, DESCREC *iprec, NET **net, SQLCHAR **to) { SQLRETURN rc; SQLCHAR *orig_to= *to; /* Negative length means either NULL or DEFAULT, so we need 7 chars. */ SQLUINTEGER length= (*aprec->octet_length_ptr > 0 ? *aprec->octet_length_ptr + 1 : 7); if ( !(*to= (SQLCHAR *) extend_buffer(*net,(char*) *to,length)) ) return set_error(stmt,MYERR_S1001,NULL,4001); rc= insert_param(stmt, (char**) to, stmt->apd, aprec, iprec, 0); if (!SQL_SUCCEEDED(rc)) return rc; /* We have to remove zero bytes or we have problems! */ while ( (*to > orig_to) && (*((*to) - 1) == (SQLCHAR) 0) ) --(*to); /* insert "," */ if (!(*to= (SQLCHAR *)add_to_buffer(*net, (char *)*to, ",", 1))) return set_error(stmt,MYERR_S1001,NULL,4001); return(SQL_SUCCESS); } /* @type : myodbc3 internal @purpose : executes a statement query */ static SQLRETURN exec_stmt_query(STMT FAR *stmt,char *query, SQLUINTEGER len) { DBC FAR *dbc= stmt->dbc; SQLRETURN error= SQL_SUCCESS; MYLOG_QUERY(stmt, query); pthread_mutex_lock(&dbc->lock); if ( check_if_server_is_alive(dbc) || mysql_real_query(&dbc->mysql, query, len) ) { error= set_error(stmt,MYERR_S1000,mysql_error(&dbc->mysql), mysql_errno(&dbc->mysql)); } pthread_mutex_unlock(&dbc->lock); return(error); } /* @type : myodbc3 internal @purpose : copies field data to statement */ static my_bool insert_field(STMT FAR *stmt, MYSQL_RES *result, DYNAMIC_STRING *dynQuery, SQLUSMALLINT nSrcCol) { DESCREC aprec_, iprec_; DESCREC *aprec= &aprec_, *iprec= &iprec_; MYSQL_FIELD *field= mysql_fetch_field_direct(result,nSrcCol); MYSQL_ROW row_data= result->data_cursor->data + nSrcCol; NET *net=&stmt->dbc->mysql.net; unsigned char *to= net->buff; SQLLEN length; desc_rec_init_apd(aprec); desc_rec_init_ipd(iprec); /* Copy row buffer data to statement */ iprec->concise_type= get_sql_data_type(stmt, field, 0); aprec->concise_type= SQL_C_CHAR; if ( row_data && *row_data ) { aprec->data_ptr= (SQLPOINTER) *row_data; length= strlen(*row_data); aprec->octet_length_ptr= &length; aprec->indicator_ptr= &length; if (!SQL_SUCCEEDED(insert_param(stmt, (char **) &to, stmt->apd, aprec, iprec, 0))) return 1; if (!(to= (unsigned char *) add_to_buffer(net, (char *) to, " AND ", 5))) return set_error(stmt, MYERR_S1001, NULL, 4001); length= (uint) ((char *)to - (char*) net->buff); dynstr_append_mem(dynQuery, (char*) net->buff, length); } else { --dynQuery->length; dynstr_append_mem(dynQuery, " IS NULL AND ",13); } return 0; } /* @type : myodbc3 internal @purpose : checks for the existance of pk columns in the resultset, if it is, copy that data to query, else we can't find the right row */ static SQLRETURN insert_pk_fields(STMT FAR *stmt, DYNAMIC_STRING *dynQuery) { MYSQL_RES *result= stmt->result; MYSQL_FIELD *field; SQLUSMALLINT ncol; uint index; MYCURSOR *cursor= &stmt->cursor; SQLUINTEGER pk_count= 0; /* Look for primary key columns in the current result set, */ for (ncol= 0; ncol < result->field_count; ++ncol) { field= result->fields+ncol; for (index= 0; index < cursor->pk_count; ++index) { if (!myodbc_strcasecmp(cursor->pkcol[index].name, field->org_name)) { /* PK data exists...*/ dynstr_append_quoted_name(dynQuery, field->org_name); dynstr_append_mem(dynQuery, "=", 1); if (insert_field(stmt, result, dynQuery, ncol)) return SQL_ERROR; cursor->pkcol[index].bind_done= TRUE; ++pk_count; break; } } } /* If we didn't have data for all the components of the primary key, we can't build a correct WHERE clause. */ if (pk_count != cursor->pk_count) return set_stmt_error(stmt, "HY000", "Not all components of primary key are available, " "so row to modify cannot be identified", 0); return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : generate a WHERE clause based on the fields in the result set */ static SQLRETURN append_all_fields(STMT FAR *stmt, DYNAMIC_STRING *dynQuery) { MYSQL_RES *result= stmt->result; MYSQL_RES *presultAllColumns; char select[NAME_LEN+30]; unsigned int i,j; BOOL found_field; /* Get the base table name. If there was more than one table underlying the result set, this will fail, and we couldn't build a suitable list of fields. */ if (!(find_used_table(stmt))) return SQL_ERROR; /* Get the list of all of the columns of the underlying table by using SELECT * FROM LIMIT 0. */ strxmov(select, "SELECT * FROM `", stmt->table_name, "` LIMIT 0", NullS); MYLOG_QUERY(stmt, select); pthread_mutex_lock(&stmt->dbc->lock); if (mysql_query(&stmt->dbc->mysql, select) || !(presultAllColumns= mysql_store_result(&stmt->dbc->mysql))) { set_error(stmt, MYERR_S1000, mysql_error(&stmt->dbc->mysql), mysql_errno(&stmt->dbc->mysql)); pthread_mutex_unlock(&stmt->dbc->lock); return SQL_ERROR; } pthread_mutex_unlock(&stmt->dbc->lock); /* If the number of fields in the underlying table is not the same as our result set, we bail out -- we need them all! */ if (mysql_num_fields(presultAllColumns) != mysql_num_fields(result)) { mysql_free_result(presultAllColumns); return SQL_ERROR; } /* Now we walk through the list of columns in the underlying table, appending them to the query along with the value from the row at the current cursor position. */ for (i= 0; i < presultAllColumns->field_count; ++i) { MYSQL_FIELD *table_field= presultAllColumns->fields + i; /* We also can't handle floating-point fields because their comparison is inexact. */ if (table_field->type == MYSQL_TYPE_FLOAT || table_field->type == MYSQL_TYPE_DOUBLE || table_field->type == MYSQL_TYPE_DECIMAL) { set_error(stmt,MYERR_S1000, "Invalid use of floating point comparision in positioned operations",0); mysql_free_result(presultAllColumns); return SQL_ERROR; } found_field= FALSE; for (j= 0; j < result->field_count; ++j) { MYSQL_FIELD *cursor_field= result->fields + j; if (cursor_field->org_name && !strcmp(cursor_field->org_name, table_field->name)) { dynstr_append_quoted_name(dynQuery, table_field->name); dynstr_append_mem(dynQuery, "=", 1); if (insert_field(stmt, result, dynQuery, j)) { mysql_free_result(presultAllColumns); return SQL_ERROR; } found_field= TRUE; break; } } /* If we didn't find the field, we have failed. */ if (!found_field) { mysql_free_result(presultAllColumns); return SQL_ERROR; } } mysql_free_result(presultAllColumns); return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : build the where clause */ static SQLRETURN build_where_clause( STMT FAR * pStmt, DYNAMIC_STRING * dynQuery, SQLUSMALLINT irow ) { /* set our cursor to irow - we call assuming irow is valid */ set_current_cursor_data( pStmt, irow ); /* simply append WHERE to our statement */ dynstr_append_mem( dynQuery, " WHERE ", 7 ); /* If a suitable key exists, then we'll use those columns, otherwise we'll try to use all of the columns. */ if (check_if_usable_unique_key_exists(pStmt)) { if (insert_pk_fields(pStmt, dynQuery) != SQL_SUCCESS) return SQL_ERROR; } else { if (append_all_fields(pStmt, dynQuery) != SQL_SUCCESS) return set_stmt_error(pStmt, "HY000", "Build WHERE -> insert_fields() failed.", 0); } /* Remove the trailing ' AND ' */ dynQuery->length -= 5; /* IF irow = 0 THEN delete all rows in the rowset ELSE specific (as in one) row */ if ( irow == 0 ) { char buff[32]; sprintf( buff, " LIMIT %lu", (unsigned long)pStmt->ard->array_size ); dynstr_append( dynQuery, buff ); } else { dynstr_append_mem( dynQuery, " LIMIT 1", 8 ); } return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : set clause building.. */ static SQLRETURN build_set_clause(STMT FAR *stmt, SQLULEN irow, DYNAMIC_STRING *dynQuery) { DESCREC aprec_, iprec_; DESCREC *aprec= &aprec_, *iprec= &iprec_; SQLLEN length= 0; uint ncol, ignore_count= 0; MYSQL_FIELD *field; MYSQL_RES *result= stmt->result; NET *net=&stmt->dbc->mysql.net; DESCREC *arrec, *irrec; dynstr_append_mem(dynQuery," SET ",5); desc_rec_init_apd(aprec); desc_rec_init_ipd(iprec); /* To make sure, it points to correct row in the current rowset.. */ irow= irow ? irow-1: 0; for ( ncol= 0; ncol < stmt->result->field_count; ++ncol ) { SQLLEN *pcbValue; SQLCHAR *to= net->buff; field= mysql_fetch_field_direct(result,ncol); arrec= desc_get_rec(stmt->ard, ncol, FALSE); irrec= desc_get_rec(stmt->ird, ncol, FALSE); assert(irrec); assert(irrec->row.field); if (stmt->setpos_apd) aprec= desc_get_rec(stmt->setpos_apd, ncol, FALSE); if (!arrec || !ARD_IS_BOUND(arrec) || !irrec->row.field) { ++ignore_count; continue; } if ( arrec->octet_length_ptr ) { pcbValue= ptr_offset_adjust(arrec->octet_length_ptr, stmt->ard->bind_offset_ptr, stmt->ard->bind_type, sizeof(SQLLEN), irow); /* If the pcbValue is SQL_COLUMN_IGNORE, then ignore the column in the SET clause */ if ( *pcbValue == SQL_COLUMN_IGNORE ) { ++ignore_count; continue; } length= *pcbValue; } else { /* set SQL_NTS only if its a string */ switch (arrec->concise_type) { case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: length= SQL_NTS; break; } } dynstr_append_quoted_name(dynQuery,field->org_name); dynstr_append_mem(dynQuery,"=",1); iprec->concise_type= get_sql_data_type(stmt, field, NULL); aprec->concise_type= arrec->concise_type; /* copy prec and scale - needed for SQL_NUMERIC values */ iprec->precision= arrec->precision; iprec->scale= arrec->scale; if (stmt->dae_type && aprec->par.is_dae) aprec->data_ptr= aprec->par.value; else aprec->data_ptr= ptr_offset_adjust(arrec->data_ptr, stmt->ard->bind_offset_ptr, stmt->ard->bind_type, bind_length(arrec->concise_type, arrec->octet_length), irow); aprec->octet_length= arrec->octet_length; if (length == SQL_NTS) length= strlen(aprec->data_ptr); aprec->octet_length_ptr= &length; aprec->indicator_ptr= &length; if ( copy_rowdata(stmt,aprec,iprec,&net,&to) != SQL_SUCCESS ) return(SQL_ERROR); length= (uint) ((char *)to - (char*) net->buff); dynstr_append_mem(dynQuery, (char*) net->buff, length); } if (ignore_count == result->field_count) return ER_ALL_COLUMNS_IGNORED; dynQuery->str[--dynQuery->length]='\0'; return(SQL_SUCCESS); } /* @type : myodbc3 internal @purpose : deletes the positioned cursor row */ SQLRETURN my_pos_delete(STMT FAR *stmt, STMT FAR *stmtParam, SQLUSMALLINT irow, DYNAMIC_STRING *dynQuery) { SQLRETURN nReturn; /* Delete only the positioned row, by building where clause */ nReturn = build_where_clause( stmt, dynQuery, irow ); if ( !SQL_SUCCEEDED( nReturn ) ) return nReturn; /* DELETE the row(s) */ nReturn= exec_stmt_query(stmt, dynQuery->str, dynQuery->length); if ( nReturn == SQL_SUCCESS || nReturn == SQL_SUCCESS_WITH_INFO ) { stmtParam->affected_rows= mysql_affected_rows(&stmt->dbc->mysql); nReturn= update_status(stmtParam,SQL_ROW_DELETED); } return nReturn; } /* @type : myodbc3 internal @purpose : updates the positioned cursor row */ SQLRETURN my_pos_update( STMT FAR * pStmtCursor, STMT FAR * pStmt, SQLUSMALLINT nRow, DYNAMIC_STRING * dynQuery ) { SQLRETURN rc; SQLHSTMT hStmtTemp; STMT FAR * pStmtTemp; rc = build_where_clause( pStmtCursor, dynQuery, nRow ); if ( !SQL_SUCCEEDED( rc ) ) return rc; /* Prepare and check if parameters exists in set clause.. this happens with WHERE CURRENT OF statements .. */ if ( my_SQLAllocStmt( pStmt->dbc, &hStmtTemp ) != SQL_SUCCESS ) { return set_stmt_error( pStmt, "HY000", "my_SQLAllocStmt() failed.", 0 ); } pStmtTemp = (STMT FAR*)hStmtTemp; if (my_SQLPrepare(pStmtTemp, (SQLCHAR *)dynQuery->str, dynQuery->length, FALSE) != SQL_SUCCESS) { my_SQLFreeStmt( pStmtTemp, SQL_DROP ); return set_stmt_error( pStmt, "HY000", "my_SQLPrepare() failed.", 0 ); } if ( pStmtTemp->param_count ) /* SET clause has parameters */ { if (!SQL_SUCCEEDED(rc= stmt_SQLCopyDesc(pStmt, pStmt->apd, pStmtTemp->apd))) return rc; if (!SQL_SUCCEEDED(rc= stmt_SQLCopyDesc(pStmt, pStmt->ipd, pStmtTemp->ipd))) return rc; } rc = my_SQLExecute( pStmtTemp ); if ( SQL_SUCCEEDED( rc ) ) { pStmt->affected_rows = mysql_affected_rows( &pStmtTemp->dbc->mysql ); rc = update_status( pStmt, SQL_ROW_UPDATED ); } else if (rc == SQL_NEED_DATA) { /* Re-prepare the statement, which will leave us with a prepared statement that is a non-positioned update. */ if (my_SQLPrepare(pStmt, (SQLCHAR *)dynQuery->str, dynQuery->length, FALSE) != SQL_SUCCESS) return SQL_ERROR; pStmt->dae_type= DAE_NORMAL; } my_SQLFreeStmt( pStmtTemp, SQL_DROP ); return rc; } /* @type : myodbc3 internal @purpose : deletes the positioned cursor row - will del all rows in rowset if irow = 0 */ static SQLRETURN setpos_delete(STMT FAR *stmt, SQLUSMALLINT irow, DYNAMIC_STRING *dynQuery) { SQLUINTEGER rowset_pos,rowset_end; my_ulonglong affected_rows= 0; SQLRETURN nReturn= SQL_SUCCESS; ulong query_length; const char *table_name; /* we want to work with base table name - we expect call to fail if more than one base table involved */ if ( !(table_name= find_used_table(stmt)) ) return SQL_ERROR; /* appened our table name to our DELETE statement */ dynstr_append_quoted_name(dynQuery,table_name); query_length= dynQuery->length; /* IF irow == 0 THEN delete all rows in the current rowset ELSE specific (as in one) row */ if ( irow == 0 ) { rowset_pos= 1; rowset_end= stmt->rows_found_in_set; } else rowset_pos= rowset_end= irow; /* process all desired rows in the rowset - we assume rowset_pos is valid */ do { dynQuery->length= query_length; /* append our WHERE clause to our DELETE statement */ nReturn = build_where_clause( stmt, dynQuery, (SQLUSMALLINT)rowset_pos ); if ( !SQL_SUCCEEDED( nReturn ) ) return nReturn; /* execute our DELETE statement */ if ( !(nReturn= exec_stmt_query(stmt, dynQuery->str, dynQuery->length)) ) affected_rows+= stmt->dbc->mysql.affected_rows; } while ( ++rowset_pos <= rowset_end ); if ( nReturn == SQL_SUCCESS ) nReturn= update_setpos_status(stmt,irow,affected_rows,SQL_ROW_DELETED); /* fix-up so fetching next rowset is correct */ if (if_dynamic_cursor(stmt)) stmt->rows_found_in_set-= (uint) affected_rows; return nReturn; } /* @type : myodbc3 internal @purpose : updates the positioned cursor row. */ static SQLRETURN setpos_update(STMT FAR *stmt, SQLUSMALLINT irow, DYNAMIC_STRING *dynQuery) { SQLUINTEGER rowset_pos,rowset_end; my_ulonglong affected_rows= 0; SQLRETURN nReturn= SQL_SUCCESS; ulong query_length; const char *table_name; if ( !(table_name= find_used_table(stmt)) ) return SQL_ERROR; dynstr_append_quoted_name(dynQuery,table_name); query_length= dynQuery->length; if ( !irow ) { /* If irow == 0, then update all rows in the current rowset */ rowset_pos= 1; rowset_end= stmt->rows_found_in_set; } else rowset_pos= rowset_end= irow; do /* UPDATE, irow from current row set */ { dynQuery->length= query_length; nReturn= build_set_clause(stmt,rowset_pos,dynQuery); if (nReturn == ER_ALL_COLUMNS_IGNORED) { /* If we're updating more than one row, having all columns ignored is fine. If it's just one row, that's an error. */ if (!irow) { nReturn= SQL_SUCCESS; continue; } else { set_stmt_error(stmt, "21S02", "Degree of derived table does not match column list", 0); return SQL_ERROR; } } else if (nReturn == SQL_ERROR) return SQL_ERROR; nReturn= build_where_clause(stmt, dynQuery, (SQLUSMALLINT)rowset_pos); if (!SQL_SUCCEEDED(nReturn)) return nReturn; if ( !(nReturn= exec_stmt_query(stmt, dynQuery->str, dynQuery->length)) ) affected_rows+= stmt->dbc->mysql.affected_rows; } while ( ++rowset_pos <= rowset_end ); if ( nReturn == SQL_SUCCESS ) nReturn= update_setpos_status(stmt,irow,affected_rows,SQL_ROW_UPDATED); return nReturn; } /*! \brief Insert 1 or more rows. This function has been created to support SQLSetPos where SQL_ADD. For each row it will complete the given INSERT statement (ext_query) and call exec_stmt_query() to execute. \note We have a limited capacity to shove data/sql across the wire. We try to handle this. see break_insert. \param stmt Viable statement. \param irow Position of the row in the rowset on which to perform the operation. If RowNumber is 0, the operation applies to every row in the rowset. \param ext_query The INSERT statement up to and including the VALUES. So something like; "INSERT .... VALUES" \return SQLRETURN \retval SQLERROR Something went wrong. \retval SQL_SUCCESS Success! */ static SQLRETURN batch_insert( STMT FAR *stmt, SQLULEN irow, DYNAMIC_STRING *ext_query ) { MYSQL_RES *result= stmt->result; /* result set we are working with */ SQLULEN insert_count= 1; /* num rows to insert - will be real value when row is 0 (all) */ SQLULEN count= 0; /* current row */ SQLLEN length; NET *net= &stmt->dbc->mysql.net; SQLUSMALLINT ncol; SQLCHAR *to; ulong query_length= 0; /* our original query len so we can reset pos if break_insert */ my_bool break_insert= FALSE; /* true if we are to exceed max data size for transmission but this seems to be misused */ DESCREC aprec_, iprec_; DESCREC *aprec= &aprec_, *iprec= &iprec_; desc_rec_init_ipd(iprec); /* determine the number of rows to insert when irow = 0 */ if ( !irow && stmt->ard->array_size > 1 ) /* batch wise */ { insert_count= stmt->ard->array_size; query_length= ext_query->length; } do { /* Have we called exec_stmt_query() as a result of exceeding data size for transmission? If so then we need to reset the pos. and start building a new statement. */ if ( break_insert ) { ext_query->length= query_length; /* "break_insert=FALSE" here? */ } /* For each row, build the value list from its columns */ while (count < insert_count) { to= net->buff; /* Append values for each column. */ dynstr_append_mem(ext_query,"(", 1); for ( ncol= 0; ncol < result->field_count; ++ncol ) { MYSQL_FIELD *field= mysql_fetch_field_direct(result,ncol); DESCREC *arrec; SQLLEN ind_or_len= 0; arrec= desc_get_rec(stmt->ard, ncol, FALSE); /* if there's a separate APD for this (dae), use it */ if (stmt->setpos_apd) aprec= desc_get_rec(stmt->setpos_apd, ncol, FALSE); else desc_rec_init_apd(aprec); if (arrec) { if (aprec->par.is_dae) ind_or_len= aprec->par.value_length; else if (arrec->octet_length_ptr) ind_or_len= *(SQLLEN *) ptr_offset_adjust(arrec->octet_length_ptr, stmt->ard->bind_offset_ptr, stmt->ard->bind_type, sizeof(SQLLEN), count); else ind_or_len= arrec->octet_length; iprec->concise_type= get_sql_data_type(stmt, field, NULL); aprec->concise_type= arrec->concise_type; /* copy prec and scale - needed for SQL_NUMERIC values */ iprec->precision= arrec->precision; iprec->scale= arrec->scale; if (stmt->dae_type && aprec->par.is_dae) /* arrays or offsets are not supported for data-at-exec */ aprec->data_ptr= aprec->par.value; else aprec->data_ptr= ptr_offset_adjust(arrec->data_ptr, stmt->ard->bind_offset_ptr, stmt->ard->bind_type, bind_length(arrec->concise_type, arrec->octet_length), count); } switch (ind_or_len) { case SQL_NTS: if (aprec->data_ptr) length= strlen(aprec->data_ptr); break; /* We pass through SQL_COLUMN_IGNORE and SQL_NULL_DATA, because the insert_data() that is eventually called knows how to deal with them. */ case SQL_COLUMN_IGNORE: case SQL_NULL_DATA: default: length= ind_or_len; } aprec->octet_length_ptr= &length; aprec->indicator_ptr= &length; if (copy_rowdata(stmt, aprec, iprec, &net, &to) != SQL_SUCCESS) return SQL_ERROR; } /* END OF for (ncol= 0; ncol < result->field_count; ++ncol) */ length= (uint) ((char *)to - (char*) net->buff); dynstr_append_mem(ext_query, (char*) net->buff, length-1); dynstr_append_mem(ext_query, "),", 2); ++count; /* We have a limited capacity to shove data across the wire, but we handle this by sending in multiple calls to exec_stmt_query() */ if (ext_query->length + length >= (SQLLEN) net_buffer_length) { break_insert= TRUE; break; } } /* END OF while(count < insert_count) */ ext_query->str[--ext_query->length]= '\0'; if ( exec_stmt_query(stmt, ext_query->str, ext_query->length) != SQL_SUCCESS ) return(SQL_ERROR); } while ( break_insert && count < insert_count ); /* get rows affected count */ stmt->affected_rows= stmt->dbc->mysql.affected_rows= insert_count; /* update row status pointer(s) */ if (stmt->ird->array_status_ptr) { for (count= insert_count; count--; ) stmt->ird->array_status_ptr[count]= SQL_ROW_ADDED; } if (stmt->stmt_options.rowStatusPtr_ex) { for (count= insert_count; count--; ) stmt->stmt_options.rowStatusPtr_ex[count]= SQL_ROW_ADDED; } return SQL_SUCCESS; } /* Setup a data-at-execution for SQLSetPos() on the current statement. */ static SQLRETURN setpos_dae_check_and_init(STMT *stmt, SQLSETPOSIROW irow, SQLSMALLINT fLock, char dae_type) { int dae_rec; SQLRETURN rc; /* If this statement hasn't already had the dae params set, check if there are any and begin the SQLParamData() sequence. */ if (stmt->dae_type != DAE_SETPOS_DONE && (dae_rec= desc_find_dae_rec(stmt->ard)) > -1) { if (!irow && stmt->ard->array_size > 1) return set_stmt_error(stmt, "HYC00", "Multiple row insert " "with data at execution not supported", 0); /* create APD, and copy ARD to it */ stmt->setpos_apd= desc_alloc(stmt, SQL_DESC_ALLOC_AUTO, DESC_APP, DESC_PARAM); if (!stmt->setpos_apd) return set_stmt_error(stmt, "S1001", "Not enough memory", 4001); if(rc= stmt_SQLCopyDesc(stmt, stmt->ard, stmt->setpos_apd)) return rc; stmt->current_param= dae_rec; stmt->dae_type= dae_type; stmt->setpos_row= irow; stmt->setpos_lock= fLock; return SQL_NEED_DATA; } return SQL_SUCCESS; } /*! \brief Shadow function for SQLSetPos. Sets the cursor position in a rowset and allows an application to refresh data in the rowset or to update or delete data in the result set. \param hstmt see SQLSetPos \param irow see SQLSetPos \param fOption see SQLSetPos \param fLock see SQLSetPos \return SQLRETURN see SQLSetPos \sa SQLSetPos */ static const char *alloc_error= "Driver Failed to set the internal dynamic result"; SQLRETURN SQL_API my_SQLSetPos(SQLHSTMT hstmt, SQLSETPOSIROW irow, SQLUSMALLINT fOption, SQLUSMALLINT fLock) { STMT FAR *stmt= (STMT FAR*) hstmt; SQLRETURN sqlRet= SQL_SUCCESS; MYSQL_RES *result= stmt->result; SQLRETURN rc; CLEAR_STMT_ERROR(stmt); if ( !result ) return set_error(stmt,MYERR_S1010,NULL,0); /* If irow > maximum rows in the resultset */ if ( fOption != SQL_ADD && irow > result->row_count ) return set_error(stmt,MYERR_S1107,NULL,0); /* Not a valid lock type ..*/ if ( fLock != SQL_LOCK_NO_CHANGE ) return set_error(stmt,MYERR_S1C00,NULL,0); switch ( fOption ) { case SQL_POSITION: { if ( irow == 0 ) return set_error(stmt,MYERR_S1109,NULL,0); if ( irow > stmt->rows_found_in_set ) return set_error(stmt,MYERR_S1107,NULL,0); /* If Dynamic cursor, fetch the latest resultset */ if ( if_dynamic_cursor(stmt) && set_dynamic_result(stmt) ) { return set_error(stmt,MYERR_S1000, alloc_error, 0); } pthread_mutex_lock(&stmt->dbc->lock); --irow; sqlRet= SQL_SUCCESS; stmt->cursor_row= (long)(stmt->current_row+irow); mysql_data_seek(stmt->result,(my_ulonglong)stmt->cursor_row); stmt->current_values= mysql_fetch_row(stmt->result); reset_getdata_position(stmt); if ( stmt->fix_fields ) stmt->current_values= (*stmt->fix_fields)(stmt,stmt->current_values); /* The call to mysql_fetch_row() moved stmt->result's internal cursor, but we don't want that. We seek back to this row so the MYSQL_RES is in the state we expect. */ mysql_data_seek(stmt->result,(my_ulonglong)stmt->cursor_row); pthread_mutex_unlock(&stmt->dbc->lock); break; } case SQL_DELETE: { DYNAMIC_STRING dynQuery; if ( irow > stmt->rows_found_in_set ) return set_error(stmt,MYERR_S1107,NULL,0); /* IF dynamic cursor THEN rerun query to refresh resultset */ if ( if_dynamic_cursor(stmt) && set_dynamic_result(stmt) ) return set_error(stmt,MYERR_S1000, alloc_error, 0); /* start building our DELETE statement */ if ( init_dynamic_string(&dynQuery, "DELETE FROM ", 1024, 1024) ) return set_error(stmt,MYERR_S1001,NULL,4001); sqlRet = setpos_delete( stmt, irow, &dynQuery ); dynstr_free(&dynQuery); break; } case SQL_UPDATE: { DYNAMIC_STRING dynQuery; if ( irow > stmt->rows_found_in_set ) return set_error(stmt,MYERR_S1107,NULL,0); /* IF dynamic cursor THEN rerun query to refresh resultset */ if (!stmt->dae_type && if_dynamic_cursor(stmt) && set_dynamic_result(stmt)) return set_error(stmt,MYERR_S1000, alloc_error, 0); if (rc= setpos_dae_check_and_init(stmt, irow, fLock, DAE_SETPOS_UPDATE)) return rc; if ( init_dynamic_string(&dynQuery, "UPDATE ", 1024, 1024) ) return set_error(stmt,MYERR_S1001,NULL,4001); sqlRet= setpos_update(stmt,irow,&dynQuery); dynstr_free(&dynQuery); break; } case SQL_ADD: { const char * table_name; DYNAMIC_STRING dynQuery; SQLUSMALLINT nCol = 0; if (!stmt->dae_type && if_dynamic_cursor(stmt) && set_dynamic_result(stmt)) return set_error(stmt,MYERR_S1000, alloc_error, 0); result= stmt->result; if ( !(table_name= find_used_table(stmt)) ) return SQL_ERROR; if (rc= setpos_dae_check_and_init(stmt, irow, fLock, DAE_SETPOS_INSERT)) return rc; if ( init_dynamic_string(&dynQuery, "INSERT INTO ", 1024,1024) ) return set_stmt_error(stmt, "S1001", "Not enough memory", 4001); /* Append the table's DB name if exists */ if(result->fields && result->fields[0].db_length) { dynstr_append_quoted_name(&dynQuery, result->fields[0].db); dynstr_append_mem(&dynQuery,".",1); } dynstr_append_quoted_name(&dynQuery,table_name); dynstr_append_mem(&dynQuery,"(",1); /* build list of column names */ for (nCol= 0; nCol < result->field_count; ++nCol) { MYSQL_FIELD *field= mysql_fetch_field_direct(result, nCol); dynstr_append_quoted_name(&dynQuery, field->org_name); dynstr_append_mem(&dynQuery, ",", 1); } --dynQuery.length; /* Remove last ',' */ dynstr_append_mem(&dynQuery,") VALUES ",9); /* process row(s) using our INSERT as base */ sqlRet= batch_insert(stmt, irow, &dynQuery); dynstr_free(&dynQuery); break; } case SQL_REFRESH: { /* Bug ...SQL_REFRESH is not suppose to fetch any new rows, instead it needs to refresh the positioned buffers */ sqlRet= my_SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, irow, stmt->ird->rows_processed_ptr, stmt->stmt_options.rowStatusPtr_ex ? stmt->stmt_options.rowStatusPtr_ex : stmt->ird->array_status_ptr, 0); break; } default: return set_error(stmt,MYERR_S1009,NULL,0); } return sqlRet; } SQLRETURN SQL_API MySQLSetCursorName(SQLHSTMT hstmt, SQLCHAR *name, SQLSMALLINT len) { STMT *stmt= (STMT *) hstmt; CLEAR_STMT_ERROR(stmt); if (!name) return set_error(stmt, MYERR_S1009, NULL, 0); if (len == SQL_NTS) len= strlen((char *)name); if (len < 0) return set_error(stmt, MYERR_S1009, NULL, 0); /** @todo use charset-aware casecmp */ if (len == 0 || len > MYSQL_MAX_CURSOR_LEN || myodbc_casecmp((char *)name, "SQLCUR", 6) == 0 || myodbc_casecmp((char *)name, "SQL_CUR", 7) == 0) return set_error(stmt, MYERR_34000, NULL, 0); x_free(stmt->cursor.name); stmt->cursor.name= dupp_str((char*)name, len); return SQL_SUCCESS; } /** Get the current cursor name, generating one if necessary. @param[in] hstmt Statement handle @return Pointer to cursor name (terminated with '\0') */ SQLCHAR *MySQLGetCursorName(HSTMT hstmt) { STMT *stmt= (STMT *)hstmt; if (!stmt->cursor.name) set_dynamic_cursor_name(stmt); return (SQLCHAR *)stmt->cursor.name; } /* @type : ODBC 1.0 API @purpose : sets the cursor position in a rowset and allows an application to refresh data in the rowset or to update or delete data in the result set */ SQLRETURN SQL_API SQLSetPos(SQLHSTMT hstmt, SQLSETPOSIROW irow, SQLUSMALLINT fOption, SQLUSMALLINT fLock) { return my_SQLSetPos(hstmt,irow,fOption,fLock); } /* @type : ODBC 1.0 API @purpose : performs bulk insertions and bulk bookmark operations, including update, delete, and fetch by bookmark */ SQLRETURN SQL_API SQLBulkOperations(SQLHSTMT Handle, SQLSMALLINT Operation) { if ( Operation == SQL_ADD ) return my_SQLSetPos(Handle, 0, SQL_ADD, SQL_LOCK_NO_CHANGE); return set_error(Handle,MYERR_S1C00,NULL,0); } /* @type : ODBC 3.0 API @purpose : closes a cursor that has been opened on a statement and discards any pending results */ SQLRETURN SQL_API SQLCloseCursor(SQLHSTMT Handle) { return my_SQLFreeStmt(Handle, SQL_CLOSE); } mysql-connector-odbc-5.1.10-src/driver/prepare.c100644 15766 12 40147 11707541005 20311 0ustar00cteamstaff/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file prepare.c @brief Prepared statement functions. */ /*************************************************************************** * The following ODBC APIs are implemented in this file: * * * * SQLPrepare (ISO 92) * * SQLBindParameter (ODBC) * * SQLDescribeParam (ODBC) * * SQLParamOptions (ODBC, Deprecated) * * SQLNumParams (ISO 92) * * SQLSetScrollOptions (ODBC, Deprecated) * * * ****************************************************************************/ #include "driver.h" #ifndef _UNIX_ # include #endif /* !_UNIX_ */ #include #include /** Prepare a statement for later execution. @param[in] hStmt Handle of the statement @param[in] query The statement to prepare (in connection character set) @param[in] len The length of the statement (or @c SQL_NTS if it is NUL-terminated) @param[in] dupe Set to @c TRUE if query is already a duplicate, and freeing the value is now up to the driver */ SQLRETURN SQL_API MySQLPrepare(SQLHSTMT hstmt, SQLCHAR *query, SQLINTEGER len, my_bool dupe) { STMT *stmt= (STMT *)hstmt; /* We free orig_query here, instead of my_SQLPrepare, because my_SQLPrepare is used by my_pos_update() when a statement requires additional parameters. */ if (stmt->orig_query) { x_free(stmt->orig_query); stmt->orig_query= NULL; } return my_SQLPrepare(hstmt, query, len, dupe); } /* @type : myodbc3 internal @purpose : prepares an SQL string for execution */ SQLRETURN my_SQLPrepare(SQLHSTMT hstmt, SQLCHAR *szSqlStr, SQLINTEGER cbSqlStr, my_bool dupe) { STMT FAR *stmt= (STMT FAR*) hstmt; char in_string, *pos, *end, *pcLastCloseBrace= NULL; uint param_count; CHARSET_INFO *charset_info= stmt->dbc->mysql.charset; int bPerhapsEmbraced= 1, bEmbraced= 0; LINT_INIT(end); CLEAR_STMT_ERROR(stmt); x_free(stmt->query); if (dupe && szSqlStr) stmt->query= (char *)szSqlStr; else if (!(stmt->query= dupp_str((char *)szSqlStr, cbSqlStr))) return set_error(stmt, MYERR_S1001, NULL, 4001); /* Count number of parameters and save position for each parameter */ in_string= 0; param_count= 0; if (use_mb(charset_info)) end= strend(stmt->query); for (pos= stmt->query; *pos ; ++pos) { if (use_mb(charset_info)) { int l; if ((l= my_ismbchar(charset_info, pos, end))) { pos+= l-1; continue; } } /* handle case where we have statement within {} - in this case we want to replace'em with ' ' */ if (bPerhapsEmbraced) { if (*pos == '{') { bPerhapsEmbraced = 0; bEmbraced= 1; *pos= ' '; ++pos; continue; } else if (!isspace(*pos)) bPerhapsEmbraced= 0; } else if (bEmbraced && *pos == '}') pcLastCloseBrace= pos; /* escape char? */ if (*pos == '\\' && pos[1]) /* Next char is escaped */ { /** @todo not multibyte aware */ ++pos; continue; } /* in a string? */ if (*pos == in_string) { if (pos[1] == in_string) /* Two quotes is ok */ ++pos; else in_string= 0; continue; } /* parameter marker? */ if (!in_string) { if (*pos == '\'' || *pos == '"' || *pos == '`') /* start of string? */ { in_string= *pos; continue; } if (*pos == '?') { DESCREC *aprec= desc_get_rec(stmt->apd, param_count, TRUE); DESCREC *iprec= desc_get_rec(stmt->ipd, param_count, TRUE); if (aprec == NULL || iprec == NULL || set_dynamic(&stmt->param_pos, (SQLCHAR *)&pos, param_count)) return set_error(stmt, MYERR_S1001, NULL, 4001); ++param_count; } } } /* remove closing brace if we have one */ if (pcLastCloseBrace) *pcLastCloseBrace= ' '; /* Reset current_param so that SQLParamData starts fresh. */ stmt->current_param= 0; stmt->query_end= pos; stmt->state= ST_PREPARED; stmt->param_count= param_count; return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : binds a buffer to a parameter marker in an SQL statement. */ SQLRETURN SQL_API my_SQLBindParameter( SQLHSTMT StatementHandle, SQLUSMALLINT ParameterNumber, SQLSMALLINT InputOutputType, SQLSMALLINT ValueType, SQLSMALLINT ParameterType, SQLULEN ColumnSize, SQLSMALLINT DecimalDigits, SQLPOINTER ParameterValuePtr, SQLLEN BufferLength, SQLLEN * StrLen_or_IndPtr ) { STMT *stmt= (STMT *)StatementHandle; DESCREC *aprec= desc_get_rec(stmt->apd, ParameterNumber - 1, TRUE); DESCREC *iprec= desc_get_rec(stmt->ipd, ParameterNumber - 1, TRUE); SQLRETURN rc; /* TODO if this function fails, the SQL_DESC_COUNT should be unchanged in apd, ipd */ CLEAR_STMT_ERROR(stmt); if (ParameterNumber < 1) { set_error(stmt,MYERR_S1093,NULL,0); return SQL_ERROR; } if (aprec->par.alloced) { aprec->par.alloced= FALSE; assert(aprec->par.value); x_free(aprec->par.value); aprec->par.value = NULL; } /* reset all param fields */ desc_rec_init_apd(aprec); desc_rec_init_ipd(iprec); /* first, set apd fields */ if (ValueType == SQL_C_DEFAULT) { ValueType= default_c_type(ParameterType); /* Access treats BIGINT as a string on linked tables. The value is read correctly, but bound as a string. */ if (ParameterType == SQL_BIGINT && stmt->dbc->ds->default_bigint_bind_str) ValueType= SQL_C_CHAR; } if (!SQL_SUCCEEDED(rc = stmt_SQLSetDescField(stmt, stmt->apd, ParameterNumber, SQL_DESC_CONCISE_TYPE, (SQLPOINTER)(SQLLEN)ValueType, SQL_IS_SMALLINT))) return rc; if (!SQL_SUCCEEDED(rc= stmt_SQLSetDescField(stmt, stmt->apd, ParameterNumber, SQL_DESC_OCTET_LENGTH, (SQLPOINTER)BufferLength, SQL_IS_INTEGER))) return rc; /* these three *must* be the last APD params bound */ if (!SQL_SUCCEEDED(rc= stmt_SQLSetDescField(stmt, stmt->apd, ParameterNumber, SQL_DESC_DATA_PTR, ParameterValuePtr, SQL_IS_POINTER))) return rc; if (!SQL_SUCCEEDED(rc= stmt_SQLSetDescField(stmt, stmt->apd, ParameterNumber, SQL_DESC_OCTET_LENGTH_PTR, StrLen_or_IndPtr, SQL_IS_POINTER))) return rc; if (!SQL_SUCCEEDED(rc= stmt_SQLSetDescField(stmt, stmt->apd, ParameterNumber, SQL_DESC_INDICATOR_PTR, StrLen_or_IndPtr, SQL_IS_POINTER))) return rc; /* now the ipd fields */ if (!SQL_SUCCEEDED(rc= stmt_SQLSetDescField(stmt, stmt->ipd, ParameterNumber, SQL_DESC_CONCISE_TYPE, (SQLPOINTER)(SQLINTEGER)ParameterType, SQL_IS_SMALLINT))) return rc; if (!SQL_SUCCEEDED(rc= stmt_SQLSetDescField(stmt, stmt->ipd, ParameterNumber, SQL_DESC_PARAMETER_TYPE, (SQLPOINTER)(SQLINTEGER)InputOutputType, SQL_IS_SMALLINT))) return rc; /* set fields from ColumnSize and DecimalDigits */ switch (ParameterType) { case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: case SQL_INTERVAL_SECOND: case SQL_INTERVAL_DAY_TO_SECOND: case SQL_INTERVAL_HOUR_TO_SECOND: case SQL_INTERVAL_MINUTE_TO_SECOND: rc= stmt_SQLSetDescField(stmt, stmt->ipd, ParameterNumber, SQL_DESC_PRECISION, (SQLPOINTER)(SQLINTEGER)DecimalDigits, SQL_IS_SMALLINT); break; case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: rc= stmt_SQLSetDescField(stmt, stmt->ipd, ParameterNumber, SQL_DESC_LENGTH, (SQLPOINTER)ColumnSize, SQL_IS_ULEN); break; case SQL_NUMERIC: case SQL_DECIMAL: rc= stmt_SQLSetDescField(stmt, stmt->ipd, ParameterNumber, SQL_DESC_SCALE, (SQLPOINTER)(SQLINTEGER)DecimalDigits, SQL_IS_SMALLINT); if (!SQL_SUCCEEDED(rc)) return rc; /* fall through */ case SQL_FLOAT: case SQL_REAL: case SQL_DOUBLE: rc= stmt_SQLSetDescField(stmt, stmt->ipd, ParameterNumber, SQL_DESC_PRECISION, (SQLPOINTER)ColumnSize, SQL_IS_ULEN); break; default: rc= SQL_SUCCESS; } if (!SQL_SUCCEEDED(rc)) return rc; aprec->par.real_param_done= TRUE; return SQL_SUCCESS; } /** Deprecated function, for more details see SQLBindParamater. @param[in] stmt Handle to statement @param[in] ipar Parameter number @param[in] fCType Value type @param[in] fSqlType Parameter type @param[in] cbColDef Column size @param[in] ibScale Decimal digits @param[in] rgbValue Parameter value pointer @param[in] pcbValue String length or index pointer @return SQL_SUCCESS or SQL_ERROR (and diag is set) */ SQLRETURN SQL_API SQLSetParam(SQLHSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT fCType, SQLSMALLINT fSqlType, SQLULEN cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLLEN * pcbValue) { return my_SQLBindParameter(hstmt, ipar, SQL_PARAM_INPUT_OUTPUT, fCType, fSqlType, cbColDef, ibScale, rgbValue, SQL_SETPARAM_VALUE_MAX, pcbValue); } /* @type : ODBC 2.0 API @purpose : binds a buffer to a parameter marker in an SQL statement. */ SQLRETURN SQL_API SQLBindParameter( SQLHSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType, SQLSMALLINT fSqlType, SQLULEN cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLLEN cbValueMax, SQLLEN * pcbValue ) { return my_SQLBindParameter(hstmt, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue); } /* @type : ODBC 1.0 API @purpose : returns the description of a parameter marker associated with a prepared SQL statement */ SQLRETURN SQL_API SQLDescribeParam( SQLHSTMT hstmt, SQLUSMALLINT ipar __attribute__((unused)), SQLSMALLINT FAR *pfSqlType, SQLULEN * pcbColDef, SQLSMALLINT FAR *pibScale __attribute__((unused)), SQLSMALLINT FAR *pfNullable ) { STMT FAR *stmt= (STMT FAR*) hstmt; if (pfSqlType) *pfSqlType= SQL_VARCHAR; if (pcbColDef) *pcbColDef= (stmt->dbc->ds->allow_big_results ? 24*1024*1024L : 255); if (pfNullable) *pfNullable= SQL_NULLABLE_UNKNOWN; return SQL_SUCCESS; } /* @type : ODBC 1.0 API @purpose : sets multiple values (arrays) for the set of parameter markers */ #ifdef USE_SQLPARAMOPTIONS_SQLULEN_PTR SQLRETURN SQL_API SQLParamOptions( SQLHSTMT hstmt, SQLULEN crow, SQLULEN *pirow ) { SQLINTEGER buflen= SQL_IS_ULEN; #else SQLRETURN SQL_API SQLParamOptions( SQLHSTMT hstmt, SQLUINTEGER crow, SQLUINTEGER *pirow ) { SQLINTEGER buflen= SQL_IS_UINTEGER; #endif SQLRETURN rc; STMT *stmt= (STMT *)hstmt; rc= stmt_SQLSetDescField(stmt, stmt->apd, 0, SQL_DESC_ARRAY_SIZE, (SQLPOINTER)crow, buflen); if (!SQL_SUCCEEDED(rc)) return rc; /* We make the assumption that if this is using the pointer to a 64-bit value, then it is correct. However the SQL_DESC_ROWS_PROCESSED_PTR is a pointer to a 32-bit value so we zero-out the unused half and save a pointer to the 32-bits we'll actually use. Some more discussion at http://mail.easysoft.com/pipermail/unixodbc-dev/2005-July/000627.html */ #ifdef USE_SQLPARAMOPTIONS_SQLULEN_PTR { int x = 1; if(*(char *)&x != 1) { *pirow= 0; pirow= (SQLULEN *)((char *)pirow + 4); } } #endif rc= stmt_SQLSetDescField(stmt, stmt->ipd, 0, SQL_DESC_ROWS_PROCESSED_PTR, pirow, SQL_IS_POINTER); return rc; } /* @type : ODBC 1.0 API @purpose : returns the number of parameter markers. */ SQLRETURN SQL_API SQLNumParams(SQLHSTMT hstmt, SQLSMALLINT *pcpar) { STMT *stmt= (STMT *)hstmt; if (pcpar) *pcpar= stmt->param_count; return SQL_SUCCESS; } /* @type : ODBC 1.0 API @purpose : sets options that control the behavior of cursors. */ SQLRETURN SQL_API SQLSetScrollOptions( SQLHSTMT hstmt, SQLUSMALLINT fConcurrency __attribute__((unused)), SQLLEN crowKeyset __attribute__((unused)), SQLUSMALLINT crowRowset ) { STMT *stmt= (STMT *)hstmt; return stmt_SQLSetDescField(stmt, stmt->ard, 0, SQL_DESC_ARRAY_SIZE, (SQLPOINTER)(SQLUINTEGER)crowRowset, SQL_IS_USMALLINT); } mysql-connector-odbc-5.1.10-src/driver/handle.c100644 15766 12 45201 11707541005 20102 0ustar00cteamstaff/* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file handle.c @brief Allocation and freeing of handles. */ /*************************************************************************** * The following ODBC APIs are implemented in this file: * * * * SQLAllocHandle (ISO 92) * * SQLFreeHandle (ISO 92) * * SQLAllocEnv (ODBC, Deprecated) * * SQLAllocConnect (ODBC, Deprecated) * * SQLAllocStmt (ODBC, Deprecated) * * SQLFreeEnv (ODBC, Deprecated) * * SQLFreeConnect (ODBC, Deprecated) * * SQLFreeStmt (ISO 92) * * * ****************************************************************************/ #include "driver.h" #ifdef _UNIX_ /* variables for thread counter */ static pthread_key_t myodbc_thread_counter_key; static pthread_once_t myodbc_thread_key_inited= PTHREAD_ONCE_INIT; /* Function to call pthread_key_create from pthread_once*/ void myodbc_thread_key_create() { pthread_key_create (&myodbc_thread_counter_key, 0); } #endif /* @type : myodbc3 internal @purpose : to allocate the environment handle and to maintain its list */ SQLRETURN SQL_API my_SQLAllocEnv(SQLHENV FAR *phenv) { #ifdef _UNIX_ /* Init thread key just once for all threads */ pthread_once(&myodbc_thread_key_inited, myodbc_thread_key_create); myodbc_init(); #endif #ifndef _UNIX_ { HGLOBAL henv= GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof (ENV)); if (henv == NULL || (*phenv= (SQLHENV)GlobalLock(henv)) == NULL) { GlobalFree (henv); /* Free it if lock fails */ *phenv= SQL_NULL_HENV; return SQL_ERROR; } } #else if (!(*phenv= (SQLHENV) my_malloc(sizeof(ENV),MYF(MY_ZEROFILL)))) { *phenv= SQL_NULL_HENV; return SQL_ERROR; } #endif /* _UNIX_ */ return SQL_SUCCESS; } /* @type : ODBC 1.0 API @purpose : to allocate the environment handle */ SQLRETURN SQL_API SQLAllocEnv(SQLHENV FAR *phenv) { SQLRETURN rc; rc= my_SQLAllocEnv(phenv); if (rc == SQL_SUCCESS) { /* --- if OS=WIN32, set default env option for SQL_ATTR_ODBC_VERSION */ #ifdef WIN32 ((ENV FAR*) *phenv)->odbc_ver= SQL_OV_ODBC3; #else ((ENV FAR*) *phenv)->odbc_ver= SQL_OV_ODBC2; #endif /* WIN32 */ } return rc; } /* @type : myodbc3 internal @purpose : to free the environment handle */ SQLRETURN SQL_API my_SQLFreeEnv(SQLHENV henv) { #ifndef _UNIX_ GlobalUnlock(GlobalHandle((HGLOBAL) henv)); GlobalFree(GlobalHandle((HGLOBAL) henv)); #else x_free(henv); myodbc_end(); #endif /* _UNIX_ */ return(SQL_SUCCESS); } /* @type : ODBC 1.0 API @purpose : to free the environment handle */ SQLRETURN SQL_API SQLFreeEnv(SQLHENV henv) { return my_SQLFreeEnv(henv); } #ifndef _UNIX_ SQLRETURN my_GetLastError(ENV *henv) { SQLRETURN ret; LPVOID msg; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &msg, 0, NULL ); ret = set_env_error(henv,MYERR_S1001,msg,0); LocalFree(msg); return ret; } #endif /* @type : myodbc3 internal @purpose : to allocate the connection handle and to maintain the connection list */ SQLRETURN SQL_API my_SQLAllocConnect(SQLHENV henv, SQLHDBC FAR *phdbc) { DBC FAR *dbc; ENV FAR *penv= (ENV FAR*) henv; #ifdef _UNIX_ long *thread_count; thread_count= (long*)pthread_getspecific(myodbc_thread_counter_key); /* Increment or allocate the thread counter */ if (thread_count) { ++(*thread_count); } else { thread_count= my_malloc(sizeof(long), MYF(0)); (*thread_count)= 1; pthread_setspecific(myodbc_thread_counter_key, thread_count); /* Call it just for safety */ mysql_thread_init(); } #endif if (mysql_get_client_version() < MIN_MYSQL_VERSION) { char buff[255]; sprintf(buff, "Wrong libmysqlclient library version: %ld. MyODBC needs at least version: %ld", mysql_get_client_version(), MIN_MYSQL_VERSION); return(set_env_error(henv, MYERR_S1000, buff, 0)); } if (!penv->odbc_ver) { return set_env_error(henv, MYERR_S1010, "Can't allocate connection " "until ODBC version specified.", 0); } #ifndef _UNIX_ { HGLOBAL hdbc= GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof (DBC)); if (!hdbc) { *phdbc= SQL_NULL_HENV; return(my_GetLastError(henv)); } if ((*phdbc= (SQLHDBC)GlobalLock(hdbc)) == SQL_NULL_HDBC) { *phdbc= SQL_NULL_HENV; return(my_GetLastError(henv)); } } #else if (!(*phdbc= (SQLHDBC) my_malloc(sizeof(DBC),MYF(MY_ZEROFILL)))) { *phdbc= SQL_NULL_HDBC; return(set_env_error(henv,MYERR_S1001,NULL,0)); } #endif /* _UNIX_ */ /* --- if OS=WIN32, set default env option for SQL_ATTR_ODBC_VERSION */ #ifdef WIN32 /* This was a fix for BDE (and or Crystal Reports) but messes other apps up. see BUG 8363. */ /* penv->odbc_ver= SQL_OV_ODBC3; */ #endif /* WIN32 */ dbc= (DBC FAR*) *phdbc; dbc->mysql.net.vio= 0; /* Marker if open */ dbc->commit_flag= 0; dbc->stmt_options.max_rows= dbc->stmt_options.max_length= 0L; dbc->stmt_options.cursor_type= SQL_CURSOR_FORWARD_ONLY; /* ODBC default */ dbc->login_timeout= 0; dbc->last_query_time= (time_t) time((time_t*) 0); dbc->txn_isolation= DEFAULT_TXN_ISOLATION; dbc->env= penv; penv->connections= list_add(penv->connections,&dbc->list); dbc->list.data= dbc; dbc->unicode= 0; dbc->ansi_charset_info= dbc->cxn_charset_info= NULL; dbc->exp_desc= NULL; dbc->sql_select_limit= (SQLULEN) -1; pthread_mutex_init(&dbc->lock,NULL); pthread_mutex_lock(&dbc->lock); myodbc_ov_init(penv->odbc_ver); /* Initialize based on ODBC version */ pthread_mutex_unlock(&dbc->lock); return(SQL_SUCCESS); } /* @type : ODBC 1.0 API @purpose : to allocate the connection handle and to maintain the connection list */ SQLRETURN SQL_API SQLAllocConnect(SQLHENV henv, SQLHDBC FAR *phdbc) { return my_SQLAllocConnect(henv, phdbc); } /* @type : myodbc3 internal @purpose : to allocate the connection handle and to maintain the connection list */ SQLRETURN SQL_API my_SQLFreeConnect(SQLHDBC hdbc) { DBC FAR *dbc= (DBC FAR*) hdbc; LIST *ldesc; LIST *next; dbc->env->connections= list_delete(dbc->env->connections,&dbc->list); x_free(dbc->database); if (dbc->ds) ds_delete(dbc->ds); pthread_mutex_destroy(&dbc->lock); /* free any remaining explicitly allocated descriptors */ for (ldesc= dbc->exp_desc; ldesc; ldesc= next) { next= ldesc->next; desc_free((DESC *) ldesc->data); x_free(ldesc); } #ifndef _UNIX_ GlobalUnlock(GlobalHandle((HGLOBAL) hdbc)); GlobalFree(GlobalHandle((HGLOBAL) hdbc)); #else x_free(hdbc); { long *thread_count; thread_count= (long*)pthread_getspecific(myodbc_thread_counter_key); if (thread_count) { if (*thread_count) { --(*thread_count); } if (*thread_count == 0) { /* The value to the key must be reset before freeing the buffer */ pthread_setspecific(myodbc_thread_counter_key, 0); x_free(thread_count); /* Last connection deallocated, supposedly the thread is finishing */ mysql_thread_end(); } } } #endif return SQL_SUCCESS; } /* @type : ODBC 1.0 API @purpose : to allocate the connection handle and to maintain the connection list */ SQLRETURN SQL_API SQLFreeConnect(SQLHDBC hdbc) { return my_SQLFreeConnect(hdbc); } /* @type : myodbc3 internal @purpose : allocates the statement handle */ SQLRETURN SQL_API my_SQLAllocStmt(SQLHDBC hdbc,SQLHSTMT FAR *phstmt) { #ifndef _UNIX_ HGLOBAL hstmt; #endif STMT FAR *stmt; DBC FAR *dbc= (DBC FAR*) hdbc; #ifndef _UNIX_ hstmt= GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(STMT)); if (!hstmt || (*phstmt= (SQLHSTMT)GlobalLock(hstmt)) == SQL_NULL_HSTMT) { *phstmt= SQL_NULL_HSTMT; GlobalFree(hstmt); } #else *phstmt= (SQLHSTMT) my_malloc(sizeof (STMT), MYF(MY_ZEROFILL | MY_WME)); #endif /* IS UNIX */ if (*phstmt == SQL_NULL_HSTMT) goto error; stmt= (STMT FAR*) *phstmt; stmt->dbc= dbc; pthread_mutex_lock(&stmt->dbc->lock); dbc->statements= list_add(dbc->statements,&stmt->list); pthread_mutex_unlock(&stmt->dbc->lock); stmt->list.data= stmt; stmt->stmt_options= dbc->stmt_options; stmt->state= ST_UNKNOWN; stmt->dummy_state= ST_DUMMY_UNKNOWN; strmov(stmt->error.sqlstate, "00000"); my_init_dynamic_array(&stmt->param_pos, sizeof(char *), 0, 0); if (!(stmt->ard= desc_alloc(stmt, SQL_DESC_ALLOC_AUTO, DESC_APP, DESC_ROW))) goto error; if (!(stmt->ird= desc_alloc(stmt, SQL_DESC_ALLOC_AUTO, DESC_IMP, DESC_ROW))) goto error; if (!(stmt->apd= desc_alloc(stmt, SQL_DESC_ALLOC_AUTO, DESC_APP, DESC_PARAM))) goto error; if (!(stmt->ipd= desc_alloc(stmt, SQL_DESC_ALLOC_AUTO, DESC_IMP, DESC_PARAM))) goto error; stmt->imp_ard= stmt->ard; stmt->imp_apd= stmt->apd; return SQL_SUCCESS; error: x_free(stmt->ard); x_free(stmt->ird); x_free(stmt->apd); x_free(stmt->ipd); return set_dbc_error(dbc, "HY001", "Memory allocation error", MYERR_S1001); } /* @type : ODBC 1.0 API @purpose : allocates the statement handle */ SQLRETURN SQL_API SQLAllocStmt(SQLHDBC hdbc,SQLHSTMT FAR *phstmt) { return my_SQLAllocStmt(hdbc,phstmt); } /* @type : ODBC 1.0 API @purpose : stops processing associated with a specific statement, closes any open cursors associated with the statement, discards pending results, or, optionally, frees all resources associated with the statement handle */ SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT hstmt,SQLUSMALLINT fOption) { return my_SQLFreeStmt(hstmt,fOption); } /* @type : myodbc3 internal @purpose : stops processing associated with a specific statement, closes any open cursors associated with the statement, discards pending results, or, optionally, frees all resources associated with the statement handle */ SQLRETURN SQL_API my_SQLFreeStmt(SQLHSTMT hstmt,SQLUSMALLINT fOption) { return my_SQLFreeStmtExtended(hstmt,fOption,1); } /* @type : myodbc3 internal @purpose : stops processing associated with a specific statement, closes any open cursors associated with the statement, discards pending results, or, optionally, frees all resources associated with the statement handle */ SQLRETURN SQL_API my_SQLFreeStmtExtended(SQLHSTMT hstmt,SQLUSMALLINT fOption, uint clearAllResults) { STMT FAR *stmt= (STMT FAR*) hstmt; uint i; if (fOption == SQL_UNBIND) { stmt->ard->records.elements= 0; stmt->ard->count= 0; return SQL_SUCCESS; } desc_free_paramdata(stmt->apd); /* reset data-at-exec state */ stmt->dae_type= 0; if (fOption == SQL_RESET_PARAMS) { /* remove all params and reset count to 0 (per spec) */ /* http://msdn2.microsoft.com/en-us/library/ms709284.aspx */ stmt->apd->count= 0; return SQL_SUCCESS; } if (!stmt->fake_result) { mysql_free_result(stmt->result); /* check if there are more resultsets */ if (clearAllResults) { while (mysql_more_results(&stmt->dbc->mysql)) { if (!mysql_next_result(&stmt->dbc->mysql)) { stmt->result= mysql_store_result(&stmt->dbc->mysql); mysql_free_result(stmt->result); } } } } else { if(stmt->result->field_alloc.pre_alloc) free_root(&stmt->result->field_alloc, MYF(0)); x_free(stmt->result); } x_free(stmt->fields); x_free(stmt->array); x_free(stmt->result_array); x_free(stmt->lengths); stmt->result= 0; stmt->fake_result= 0; stmt->fields= 0; stmt->array= 0; stmt->result_array= 0; stmt->lengths= 0; stmt->current_values= 0; /* For SQLGetData */ stmt->fix_fields= 0; stmt->affected_rows= 0; stmt->current_row= stmt->cursor_row= stmt->rows_found_in_set= 0; stmt->dae_type= 0; if (fOption == MYSQL_RESET_BUFFERS) return SQL_SUCCESS; stmt->state= ST_UNKNOWN; x_free(stmt->table_name); stmt->table_name= 0; stmt->dummy_state= ST_DUMMY_UNKNOWN; stmt->cursor.pk_validated= FALSE; if (stmt->setpos_apd) desc_free(stmt->setpos_apd); stmt->setpos_apd= NULL; for (i= stmt->cursor.pk_count; i--;) stmt->cursor.pkcol[i].bind_done= 0; stmt->cursor.pk_count= 0; if (fOption == SQL_CLOSE) return SQL_SUCCESS; /* At this point, only MYSQL_RESET and SQL_DROP left out */ x_free(stmt->query); x_free(stmt->orig_query); stmt->query= stmt->orig_query= 0; stmt->param_count= 0; reset_ptr(stmt->apd->rows_processed_ptr); reset_ptr(stmt->ard->rows_processed_ptr); reset_ptr(stmt->ipd->array_status_ptr); reset_ptr(stmt->ird->array_status_ptr); reset_ptr(stmt->apd->array_status_ptr); reset_ptr(stmt->ard->array_status_ptr); reset_ptr(stmt->stmt_options.rowStatusPtr_ex); if (fOption == MYSQL_RESET) return SQL_SUCCESS; /* explicitly allocated descriptors are affected up until this point */ desc_remove_stmt(stmt->apd, stmt); desc_remove_stmt(stmt->ard, stmt); desc_free(stmt->imp_apd); desc_free(stmt->imp_ard); desc_free(stmt->ipd); desc_free(stmt->ird); x_free(stmt->cursor.name); delete_dynamic(&stmt->param_pos); pthread_mutex_lock(&stmt->dbc->lock); stmt->dbc->statements= list_delete(stmt->dbc->statements,&stmt->list); pthread_mutex_unlock(&stmt->dbc->lock); #ifndef _UNIX_ GlobalUnlock(GlobalHandle ((HGLOBAL) hstmt)); GlobalFree(GlobalHandle((HGLOBAL) hstmt)); #else x_free(hstmt); #endif /* _UNIX_*/ return SQL_SUCCESS; } /* Explicitly allocate a descriptor. */ SQLRETURN my_SQLAllocDesc(SQLHDBC hdbc, SQLHANDLE *pdesc) { DBC *dbc= (DBC *) hdbc; DESC *desc= desc_alloc(NULL, SQL_DESC_ALLOC_USER, DESC_APP, DESC_UNKNOWN); LIST *e; if (!desc) return set_dbc_error(dbc, "HY001", "Memory allocation error", MYERR_S1001); desc->exp.dbc= dbc; /* add to this connection's list of explicit descriptors */ e= (LIST *) my_malloc(sizeof(LIST), MYF(0)); e->data= desc; dbc->exp_desc= list_add(dbc->exp_desc, e); *pdesc= desc; return SQL_SUCCESS; } /* Free an explicitly allocated descriptor. This resets all statements that it was associated with to their implicitly allocated descriptors. */ SQLRETURN my_SQLFreeDesc(SQLHANDLE hdesc) { DESC *desc= (DESC *) hdesc; DBC *dbc= desc->exp.dbc; LIST *lstmt; LIST *ldesc; LIST *next; if (!desc) return SQL_ERROR; if (desc->alloc_type != SQL_DESC_ALLOC_USER) return set_desc_error(desc, "HY017", "Invalid use of an automatically " "allocated descriptor handle.", MYERR_S1017); /* remove from DBC */ for (ldesc= dbc->exp_desc; ldesc; ldesc= ldesc->next) { if (ldesc->data == desc) { dbc->exp_desc= list_delete(dbc->exp_desc, ldesc); x_free(ldesc); break; } } /* reset all stmts it was on - to their implicit desc */ for (lstmt= desc->exp.stmts; lstmt; lstmt= next) { STMT *stmt= lstmt->data; next= lstmt->next; if (IS_APD(desc)) stmt->apd= stmt->imp_apd; else if (IS_ARD(desc)) stmt->ard= stmt->imp_ard; x_free(lstmt); } desc_free(desc); return SQL_SUCCESS; } /* @type : ODBC 3.0 API @purpose : allocates an environment, connection, statement, or descriptor handle */ SQLRETURN SQL_API SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandlePtr) { SQLRETURN error= SQL_ERROR; switch (HandleType) { case SQL_HANDLE_ENV: error= my_SQLAllocEnv(OutputHandlePtr); break; case SQL_HANDLE_DBC: error= my_SQLAllocConnect(InputHandle,OutputHandlePtr); break; case SQL_HANDLE_STMT: error= my_SQLAllocStmt(InputHandle,OutputHandlePtr); break; case SQL_HANDLE_DESC: error= my_SQLAllocDesc(InputHandle, OutputHandlePtr); break; default: return set_conn_error(InputHandle,MYERR_S1C00,NULL,0); } return error; } /* @type : ODBC 3.0 API @purpose : frees resources associated with a specific environment, connection, statement, or descriptor handle */ SQLRETURN SQL_API SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) { SQLRETURN error= SQL_ERROR; switch (HandleType) { case SQL_HANDLE_ENV: error= my_SQLFreeEnv((ENV *)Handle); break; case SQL_HANDLE_DBC: error= my_SQLFreeConnect((DBC *)Handle); break; case SQL_HANDLE_STMT: error= my_SQLFreeStmt((STMT *)Handle, SQL_DROP); break; case SQL_HANDLE_DESC: error= my_SQLFreeDesc((DESC *) Handle); break; default: break; } return error; } mysql-connector-odbc-5.1.10-src/driver/driver.rc100644 15766 12 6265 11707541005 20313 0ustar00cteamstaff///////////////////////////////////////////////////////////////////////////// // Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved // // The MySQL Connector/ODBC is licensed under the terms of the GPLv2 // , like most // MySQL Connectors. There are special exceptions to the terms and // conditions of the GPLv2 as it is applied to this software, see the // FLOSS License Exception // . // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ///////////////////////////////////////////////////////////////////////////// // \brief Resource file for MS Windows builds. // // \note Hand crafted - do not overwrite and save to source repo! // #include #include "..\resource.h" ///////////////////////////////////////////////////////////////////////////// // // \brief English (U.S.) resources // #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) MYSQL_LOGO BITMAP DISCARDABLE "..\mysql.bmp" #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// // // \brief Version information // // \note We actually share our version with others (ie setup lib) so // use the common VersionInfo.h file. // #include "..\VersionInfo.h" VS_VERSION_INFO VERSIONINFO FILEVERSION MYODBC_FILEVER PRODUCTVERSION MYODBC_PRODUCTVER FILEFLAGSMASK 0x3L #ifdef _DEBUG FILEFLAGS 0x29L #else FILEFLAGS 0x28L #endif FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904e4" BEGIN VALUE "Comments", "provides core driver functionality\0" VALUE "CompanyName", "Oracle Corporation\0" VALUE "FileDescription", "MySQL ODBC 5.1 Driver\0" VALUE "FileVersion", MYODBC_STRFILEVER VALUE "InternalName", "myodbc5\0" VALUE "LegalCopyright", "Copyright (c) 1995, 2011, Oracle and/or its affiliates.\0" VALUE "LegalTrademarks", "MySQL, MyODBC, Connector/ODBC are trademarks of Oracle Corporation\0" VALUE "OriginalFilename", "myodbc5.dll\0" VALUE "PrivateBuild", "Production\0" VALUE "ProductName", "Connector/ODBC 5.1\0" VALUE "ProductVersion", MYODBC_STRPRODUCTVER VALUE "SpecialBuild", "GA release\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END mysql-connector-odbc-5.1.10-src/driver/catalog.c100644 15766 12 130647 11707541005 20312 0ustar00cteamstaff/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file catalog.c @brief Catalog functions. */ #include "driver.h" #include "catalog.h" /* @type : internal @purpose : checks if server supports I_S @remark : All i_s_* functions suppose that parameters specifying other parameters lenthes can't SQL_NTS. caller should take care of that. */ my_bool server_has_i_s(DBC FAR *dbc) { /* According to the server ChangeLog INFORMATION_SCHEMA was introduced in the 5.0.2 */ return is_minimum_version(dbc->mysql.server_version, "5.0.2", 5); } /* @type : internal @purpose : returns the next token */ const char *my_next_token(const char *prev_token, char **token, char *data, const char chr) { const char *cur_token; if ( (cur_token= strchr(*token, chr)) ) { if ( prev_token ) { uint len= (uint)(cur_token-prev_token); strncpy(data,prev_token, len); data[len]= 0; } *token= (char *)cur_token+1; prev_token= cur_token; return cur_token+1; } return 0; } /** Create a fake result set in the current statement @param[in] stmt Handle to statement @param[in] rowval Row array @param[in] rowsize sizeof(row array) @param[in] rowcnt Number of rows @param[in] fields Field array @param[in] fldcnt Number of fields @return SQL_SUCCESS or SQL_ERROR (and diag is set) */ SQLRETURN create_fake_resultset(STMT *stmt, MYSQL_ROW rowval, size_t rowsize, my_ulonglong rowcnt, MYSQL_FIELD *fields, uint fldcnt) { stmt->result= (MYSQL_RES*) my_malloc(sizeof(MYSQL_RES), MYF(MY_ZEROFILL)); stmt->result_array= (MYSQL_ROW)my_memdup((char *)rowval, rowsize, MYF(0)); if (!(stmt->result && stmt->result_array)) { x_free(stmt->result); x_free(stmt->result_array); set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } stmt->fake_result= 1; set_row_count(stmt, rowcnt); mysql_link_fields(stmt, fields, fldcnt); return SQL_SUCCESS; } /** Create an empty fake result set in the current statement @param[in] stmt Handle to statement @param[in] rowval Row array @param[in] rowsize sizeof(row array) @param[in] fields Field array @param[in] fldcnt Number of fields @return SQL_SUCCESS or SQL_ERROR (and diag is set) */ SQLRETURN create_empty_fake_resultset(STMT *stmt, MYSQL_ROW rowval, size_t rowsize, MYSQL_FIELD *fields, uint fldcnt) { return create_fake_resultset(stmt, rowval, rowsize, 0 /* rowcnt */, fields, fldcnt); } /** Get the table status for a table or tables using Information_Schema DB. Lengths may not be SQL_NTS. @param[in] stmt Handle to statement @param[in] catalog Catalog (database) of table, @c NULL for current @param[in] catalog_length Length of catalog name @param[in] table Name of table @param[in] table_length Length of table name @param[in] wildcard Whether the table name is a wildcard @return Result of SHOW TABLE STATUS, or NULL if there is an error or empty result (check mysql_errno(&stmt->dbc->mysql) != 0) */ static MYSQL_RES *mysql_table_status_i_s(STMT *stmt, SQLCHAR *catalog, SQLSMALLINT catalog_length, SQLCHAR *table, SQLSMALLINT table_length, my_bool wildcard, my_bool show_tables, my_bool show_views) { MYSQL *mysql= &stmt->dbc->mysql; /** @todo determine real size for buffer */ char buff[255], *to; my_bool clause_added= FALSE; to= strmov(buff, "SELECT TABLE_NAME, TABLE_COMMENT, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES WHERE "); if (catalog && *catalog) { to= strmov(to, "TABLE_SCHEMA LIKE '"); to+= myodbc_escape_string(mysql, to, (ulong)(sizeof(buff) - (to - buff)), (char *)catalog, catalog_length, 1); to= strmov(to, "' "); clause_added= TRUE; } else { to= strmov(to, "TABLE_SCHEMA = DATABASE() "); } if (show_tables) { to= strmov(to, "AND "); if (show_views) to= strmov(to, "( "); to= strmov(to, "TABLE_TYPE='BASE TABLE' "); } if (show_views) { if (show_tables) to= strmov(to, "OR "); else to= strmov(to, "AND "); to= strmov(to, "TABLE_TYPE='VIEW' "); if (show_tables) to= strmov(to, ") "); } /* As a pattern-value argument, an empty string needs to be treated literally. (It's not the same as NULL, which is the same as '%'.) But it will never match anything, so bail out now. */ if (table && wildcard && !*table) return NULL; if (table && *table) { to= strmov(to, "AND TABLE_NAME LIKE '"); if (wildcard) { to+= mysql_real_escape_string(mysql, to, (char *)table, table_length); } else { to+= myodbc_escape_string(mysql, to, (ulong)(sizeof(buff) - (to - buff)), (char *)table, table_length, 0); } to= strmov(to, "'"); } assert(to - buff < sizeof(buff)); MYLOG_QUERY(stmt, buff); if (mysql_real_query(mysql, buff, (unsigned long)(to - buff))) { return NULL; } return mysql_store_result(mysql); } /** Get the table status for a table or tables. Lengths may not be SQL_NTS. @param[in] stmt Handle to statement @param[in] catalog Catalog (database) of table, @c NULL for current @param[in] catalog_length Length of catalog name @param[in] table Name of table @param[in] table_length Length of table name @param[in] wildcard Whether the table name is a wildcard @return Result of SHOW TABLE STATUS, or NULL if there is an error or empty result (check mysql_errno(&stmt->dbc->mysql) != 0) */ MYSQL_RES *mysql_table_status(STMT *stmt, SQLCHAR *catalog, SQLSMALLINT catalog_length, SQLCHAR *table, SQLSMALLINT table_length, my_bool wildcard, my_bool show_tables, my_bool show_views) { if (server_has_i_s(stmt->dbc) && !stmt->dbc->ds->no_information_schema) return mysql_table_status_i_s(stmt, catalog, catalog_length, table, table_length, wildcard, show_tables, show_views); else return mysql_table_status_show(stmt, catalog, catalog_length, table, table_length, wildcard); } /* @type : internal @purpose : Adding name condition for arguments what depending on SQL_ATTR_METADATA_ID are either ordinary argument(oa) or identifier string(id) NULL _default parameter means that parameter is mandatory and error is generated @returns : 1 if required parameter is NULL, 0 otherwise */ int add_name_condition_oa_id(HSTMT hstmt, char ** pos, SQLCHAR * name, SQLSMALLINT name_len, char * _default) { SQLUINTEGER metadata_id; /* this shouldn't be very expensive, so no need to complicate things with additional parameter etc */ MySQLGetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, 0, NULL); /* we can't rely here that column was checked and is not null */ if (name) { STMT FAR *stmt= (STMT FAR*) hstmt; if (metadata_id) { *pos= strmov(*pos, "="); /* Need also code to remove trailing blanks */ } else *pos= strmov(*pos, "= BINARY "); *pos= strmov(*pos, "'"); *pos+= mysql_real_escape_string(&stmt->dbc->mysql, *pos, (char *)name, name_len); *pos= strmov(*pos, "' "); } else { /* According to http://msdn.microsoft.com/en-us/library/ms714579%28VS.85%29.aspx identifier argument cannot be NULL with one exception not actual for mysql) */ if (!metadata_id && _default) *pos= strmov(*pos, _default); else return 1; } return 0; } /* @type : internal @purpose : Adding name condition for arguments what depending on SQL_ATTR_METADATA_ID are either pattern value(oa) or identifier string(id) @returns : 1 if required parameter is NULL, 0 otherwise */ int add_name_condition_pv_id(HSTMT hstmt, char ** pos, SQLCHAR * name, SQLSMALLINT name_len, char * _default) { SQLUINTEGER metadata_id; /* this shouldn't be very expensive, so no need to complicate things with additional parameter etc */ MySQLGetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, 0, NULL); /* we can't rely here that column was checked and is not null */ if (name) { STMT FAR *stmt= (STMT FAR*) hstmt; if (metadata_id) { *pos= strmov(*pos, "="); /* Need also code to remove trailing blanks */ } else *pos= strmov(*pos, " LIKE BINARY "); *pos= strmov(*pos, "'"); *pos+= mysql_real_escape_string(&stmt->dbc->mysql, *pos, (char *)name, name_len); *pos= strmov(*pos, "' "); } else { /* According to http://msdn.microsoft.com/en-us/library/ms714579%28VS.85%29.aspx identifier argument cannot be NULL with one exception not actual for mysql) */ if (!metadata_id && _default) *pos= strmov(*pos, _default); else return 1; } return 0; } /* **************************************************************************** SQLTables **************************************************************************** */ SQLRETURN i_s_tables(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *type, SQLSMALLINT type_len) { /* The function is just a stub. We call non-I_S version of the function before implementing the I_S one */ return mysql_tables(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, type, type_len); } SQLRETURN SQL_API MySQLTables(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *type, SQLSMALLINT type_len) { STMT *stmt= (STMT *)hstmt; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt, MYSQL_RESET); if (catalog_len == SQL_NTS) catalog_len= catalog ? (SQLSMALLINT)strlen((char *)catalog) : 0; if (schema_len == SQL_NTS) schema_len= schema ? (SQLSMALLINT)strlen((char *)schema) : 0; if (table_len == SQL_NTS) table_len= table ? (SQLSMALLINT)strlen((char *)table) : 0; if (type_len == SQL_NTS) type_len= type ? (SQLSMALLINT)strlen((char *)type) : 0; if (server_has_i_s(stmt->dbc) && !stmt->dbc->ds->no_information_schema) { return i_s_tables(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, type, type_len); } else { return mysql_tables(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, type, type_len); } } /* **************************************************************************** SQLColumns **************************************************************************** */ /** Get information about the columns in one or more tables. @param[in] hstmt Handle of statement @param[in] szCatalog Name of catalog (database) @param[in] cbCatalog Length of catalog @param[in] szSchema Name of schema (unused) @param[in] cbSchema Length of schema name @param[in] szTable Pattern of table names to match @param[in] cbTable Length of table pattern @param[in] szColumn Pattern of column names to match @param[in] cbColumn Length of column pattern */ SQLRETURN i_s_columns(SQLHSTMT hstmt, SQLCHAR *szCatalog, SQLSMALLINT cbCatalog, SQLCHAR *szSchema __attribute__((unused)), SQLSMALLINT cbSchema __attribute__((unused)), SQLCHAR *szTable, SQLSMALLINT cbTable, SQLCHAR *szColumn, SQLSMALLINT cbColumn) { /* The function is just a stub. We call non-I_S version of the function before implementing the I_S one */ return mysql_columns(hstmt, szCatalog, cbCatalog,szSchema, cbSchema, szTable, cbTable, szColumn, cbColumn); } /** Get information about the columns in one or more tables. @param[in] hstmt Handle of statement @param[in] szCatalog Name of catalog (database) @param[in] cbCatalog Length of catalog @param[in] szSchema Name of schema (unused) @param[in] cbSchema Length of schema name @param[in] szTable Pattern of table names to match @param[in] cbTable Length of table pattern @param[in] szColumn Pattern of column names to match @param[in] cbColumn Length of column pattern */ SQLRETURN SQL_API MySQLColumns(SQLHSTMT hstmt, SQLCHAR *szCatalog, SQLSMALLINT cbCatalog, SQLCHAR *szSchema __attribute__((unused)), SQLSMALLINT cbSchema __attribute__((unused)), SQLCHAR *szTable, SQLSMALLINT cbTable, SQLCHAR *szColumn, SQLSMALLINT cbColumn) { STMT *stmt= (STMT *)hstmt; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt, MYSQL_RESET); if (cbCatalog == SQL_NTS) cbCatalog= szCatalog != NULL ? (SQLSMALLINT)strlen((char *)szCatalog) : 0; if (cbColumn == SQL_NTS) cbColumn= szColumn != NULL ? (SQLSMALLINT)strlen((char *)szColumn) : 0; if (cbTable == SQL_NTS) cbTable= szTable != NULL ? (SQLSMALLINT)strlen((char *)szTable) : 0; if (server_has_i_s(stmt->dbc) && !stmt->dbc->ds->no_information_schema) { return i_s_columns(hstmt, szCatalog, cbCatalog,szSchema, cbSchema, szTable, cbTable, szColumn, cbColumn); } else { return mysql_columns(hstmt, szCatalog, cbCatalog,szSchema, cbSchema, szTable, cbTable, szColumn, cbColumn); } } /* **************************************************************************** SQLStatistics **************************************************************************** */ /* @purpose : retrieves a list of statistics about a single table and the indexes associated with the table. The driver returns the information as a result set. */ SQLRETURN i_s_statistics(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len, SQLUSMALLINT fUnique, SQLUSMALLINT fAccuracy __attribute__((unused))) { /* The function is just a stub. We call non-I_S version of the function before implementing the I_S one */ return mysql_statistics(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, fUnique, fAccuracy); } /* @type : ODBC 1.0 API @purpose : retrieves a list of statistics about a single table and the indexes associated with the table. The driver returns the information as a result set. */ SQLRETURN SQL_API MySQLStatistics(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len, SQLUSMALLINT fUnique, SQLUSMALLINT fAccuracy __attribute__((unused))) { STMT *stmt= (STMT *)hstmt; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); if (catalog_len == SQL_NTS) catalog_len= catalog ? (SQLSMALLINT)strlen((char *)catalog) : 0; if (table_len == SQL_NTS) table_len= table ? (SQLSMALLINT)strlen((char *)table) : 0; if (server_has_i_s(stmt->dbc) && !stmt->dbc->ds->no_information_schema) { return i_s_statistics(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, fUnique, fAccuracy); } else { return mysql_statistics(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, fUnique, fAccuracy); } } /* **************************************************************************** SQLTablePrivileges **************************************************************************** */ /* @type : internal @purpose : fetches data from I_S table_privileges. returns SQLRETURN of the operation */ SQLRETURN i_s_list_table_priv(SQLHSTMT hstmt, SQLCHAR * catalog, SQLSMALLINT catalog_len, SQLCHAR * schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR * table, SQLSMALLINT table_len) { STMT FAR *stmt=(STMT FAR*) hstmt; MYSQL *mysql= &stmt->dbc->mysql; char buff[255+4*NAME_LEN+1], *pos; SQLRETURN rc; /* Db,User,Table_name,"NULL" as Grantor,Table_priv*/ pos= strmov(buff, "SELECT TABLE_SCHEMA as TABLE_CAT, TABLE_CATALOG as TABLE_SCHEM," "TABLE_NAME, NULL as GRANTOR, GRANTEE," "PRIVILEGE_TYPE as PRIVILEGE, IS_GRANTABLE " "FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES " "WHERE TABLE_NAME"); add_name_condition_pv_id(hstmt, &pos, table, table_len, " LIKE '%'" ); pos= strmov(pos, " AND TABLE_SCHEMA"); add_name_condition_oa_id(hstmt, &pos, catalog, catalog_len, "=DATABASE()"); /* TABLE_CAT is always NULL in mysql I_S */ pos= strmov(pos, " ORDER BY /*TABLE_CAT,*/ TABLE_SCHEM, TABLE_NAME, PRIVILEGE, GRANTEE"); assert(pos - buff < sizeof(buff)); if( !SQL_SUCCEEDED(rc= MySQLPrepare(hstmt, (SQLCHAR *)buff, (SQLINTEGER)(pos - buff), FALSE))) return rc; return my_SQLExecute(stmt); } /* @type : ODBC 1.0 API @purpose : returns a list of tables and the privileges associated with each table. The driver returns the information as a result set on the specified statement. */ SQLRETURN SQL_API MySQLTablePrivileges(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len) { STMT *stmt= (STMT *)hstmt; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); if (catalog_len == SQL_NTS) catalog_len= catalog ? (SQLSMALLINT)strlen((char *)catalog) : 0; if (table_len == SQL_NTS) table_len= table ? (SQLSMALLINT)strlen((char *)table) : 0; if (server_has_i_s(stmt->dbc) && !stmt->dbc->ds->no_information_schema) { /* Since mysql is also the name of the system db, using here i_s prefix to distinct functions */ return i_s_list_table_priv(hstmt, catalog, catalog_len, schema, schema_len, table, table_len); } else { return mysql_list_table_priv(hstmt, catalog, catalog_len, schema, schema_len, table, table_len); } } /* @type : internal @purpose : returns a column privileges result, NULL on error */ static SQLRETURN i_s_list_column_priv(HSTMT * hstmt, SQLCHAR * catalog, SQLSMALLINT catalog_len, SQLCHAR * schema, SQLSMALLINT schema_len, SQLCHAR * table, SQLSMALLINT table_len, SQLCHAR * column, SQLSMALLINT column_len) { STMT FAR *stmt=(STMT FAR*) hstmt; MYSQL *mysql= &stmt->dbc->mysql; /* 3 names theorethically can have all their characters escaped - thus 6*NAME_LEN */ char buff[351+6*NAME_LEN+1], *pos; SQLRETURN rc; /* Db,User,Table_name,"NULL" as Grantor,Table_priv*/ pos= strmov(buff, "SELECT TABLE_SCHEMA as TABLE_CAT, TABLE_CATALOG as TABLE_SCHEM," "TABLE_NAME, COLUMN_NAME, NULL as GRANTOR, GRANTEE," "PRIVILEGE_TYPE as PRIVILEGE, IS_GRANTABLE " "FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES " "WHERE TABLE_NAME"); if(add_name_condition_oa_id(hstmt, &pos, table, table_len, NULL)) return set_stmt_error(stmt, "HY009", "Invalid use of NULL pointer(table is required parameter)", 0); pos= strmov(pos, " AND TABLE_SCHEMA"); add_name_condition_oa_id(hstmt, &pos, catalog, catalog_len, "=DATABASE()"); pos= strmov(pos, " AND COLUMN_NAME"); add_name_condition_pv_id(hstmt, &pos, column, column_len, " LIKE '%'"); /* TABLE_CAT is always NULL in mysql I_S */ pos= strmov(pos, " ORDER BY /*TABLE_CAT,*/ TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, PRIVILEGE"); assert(pos - buff < sizeof(buff)); if( !SQL_SUCCEEDED(rc= MySQLPrepare(hstmt, (SQLCHAR *)buff, SQL_NTS, FALSE))) return rc; return my_SQLExecute(stmt); } SQLRETURN SQL_API MySQLColumnPrivileges(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *column, SQLSMALLINT column_len) { STMT *stmt= (STMT *)hstmt; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); if (catalog_len == SQL_NTS) catalog_len= catalog ? (SQLSMALLINT)strlen((char *)catalog) : 0; if (table_len == SQL_NTS) table_len= table ? (SQLSMALLINT)strlen((char *)table) : 0; if (column_len == SQL_NTS) column_len= column ? (SQLSMALLINT)strlen((char *)column) : 0; if (server_has_i_s(stmt->dbc) && !stmt->dbc->ds->no_information_schema) { /* Since mysql is also the name of the system db, using here i_s prefix to distinct functions */ return i_s_list_column_priv(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, column, column_len); } else { return mysql_list_column_priv(hstmt, catalog, catalog_len, schema, schema_len, table, table_len, column, column_len); } } /* **************************************************************************** SQLSpecialColumns **************************************************************************** */ SQLRETURN i_s_special_columns(SQLHSTMT hstmt, SQLUSMALLINT fColType, SQLCHAR *szTableQualifier, SQLSMALLINT cbTableQualifier, SQLCHAR *szTableOwner __attribute__((unused)), SQLSMALLINT cbTableOwner __attribute__((unused)), SQLCHAR *szTableName, SQLSMALLINT cbTableName, SQLUSMALLINT fScope __attribute__((unused)), SQLUSMALLINT fNullable __attribute__((unused))) { /* The function is just a stub. We call non-I_S version of the function before implementing the I_S one */ return mysql_special_columns(hstmt, fColType, szTableQualifier, cbTableQualifier, szTableOwner, cbTableOwner, szTableName, cbTableName, fScope, fNullable); } /* @type : ODBC 1.0 API @purpose : retrieves the following information about columns within a specified table: - The optimal set of columns that uniquely identifies a row in the table. - Columns that are automatically updated when any value in the row is updated by a transaction */ SQLRETURN SQL_API MySQLSpecialColumns(SQLHSTMT hstmt, SQLUSMALLINT fColType, SQLCHAR *szTableQualifier, SQLSMALLINT cbTableQualifier, SQLCHAR *szTableOwner __attribute__((unused)), SQLSMALLINT cbTableOwner __attribute__((unused)), SQLCHAR *szTableName, SQLSMALLINT cbTableName, SQLUSMALLINT fScope __attribute__((unused)), SQLUSMALLINT fNullable __attribute__((unused))) { STMT *stmt=(STMT *) hstmt; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); if (cbTableQualifier == SQL_NTS) cbTableQualifier= szTableQualifier ? (SQLSMALLINT)strlen((char *)szTableQualifier) : 0; if (cbTableName == SQL_NTS) cbTableName= szTableName ? (SQLSMALLINT)strlen((char *)szTableName) : 0; if (server_has_i_s(stmt->dbc) && !stmt->dbc->ds->no_information_schema) { return i_s_special_columns(hstmt, fColType, szTableQualifier, cbTableQualifier, szTableOwner, cbTableOwner, szTableName, cbTableName, fScope, fNullable); } else { return mysql_special_columns(hstmt, fColType, szTableQualifier, cbTableQualifier, szTableOwner, cbTableOwner, szTableName, cbTableName, fScope, fNullable); } } /* **************************************************************************** SQLPrimaryKeys **************************************************************************** */ SQLRETURN i_s_primary_keys(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len) { /* The function is just a stub. We call non-I_S version of the function before implementing the I_S one */ return mysql_primary_keys(hstmt, catalog, catalog_len, schema, schema_len, table, table_len); } /* @type : ODBC 1.0 API @purpose : returns the column names that make up the primary key for a table. The driver returns the information as a result set. This function does not support returning primary keys from multiple tables in a single call */ SQLRETURN SQL_API MySQLPrimaryKeys(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len) { STMT FAR *stmt= (STMT FAR*) hstmt; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); if (catalog_len == SQL_NTS) catalog_len= catalog ? (SQLSMALLINT)strlen((char *)catalog) : 0; if (table_len == SQL_NTS) table_len= table ? (SQLSMALLINT)strlen((char *)table) : 0; if (server_has_i_s(stmt->dbc) && !stmt->dbc->ds->no_information_schema) { return i_s_primary_keys(hstmt, catalog, catalog_len, schema, schema_len, table, table_len); } else { return mysql_primary_keys(hstmt, catalog, catalog_len, schema, schema_len, table, table_len); } } /* **************************************************************************** SQLForeignKeys **************************************************************************** */ SQLRETURN i_s_foreign_keys(SQLHSTMT hstmt, SQLCHAR *szPkCatalogName __attribute__((unused)), SQLSMALLINT cbPkCatalogName __attribute__((unused)), SQLCHAR *szPkSchemaName __attribute__((unused)), SQLSMALLINT cbPkSchemaName __attribute__((unused)), SQLCHAR *szPkTableName, SQLSMALLINT cbPkTableName, SQLCHAR *szFkCatalogName, SQLSMALLINT cbFkCatalogName, SQLCHAR *szFkSchemaName __attribute__((unused)), SQLSMALLINT cbFkSchemaName __attribute__((unused)), SQLCHAR *szFkTableName, SQLSMALLINT cbFkTableName) { STMT FAR *stmt=(STMT FAR*) hstmt; MYSQL *mysql= &stmt->dbc->mysql; char query[2048], *buff; /* This should be big enough. */ char *update_rule, *delete_rule, *ref_constraints_join; SQLRETURN rc; /* With 5.1, we can use REFERENTIAL_CONSTRAINTS to get even more info. */ if (is_minimum_version(stmt->dbc->mysql.server_version, "5.1", 3)) { update_rule= "CASE" " WHEN R.UPDATE_RULE = 'CASCADE' THEN 0" " WHEN R.UPDATE_RULE = 'SET NULL' THEN 2" " WHEN R.UPDATE_RULE = 'SET DEFAULT' THEN 4" " WHEN R.UPDATE_RULE = 'SET RESTRICT' THEN 1" " WHEN R.UPDATE_RULE = 'SET NO ACTION' THEN 3" " ELSE 3" " END"; delete_rule= "CASE" " WHEN R.DELETE_RULE = 'CASCADE' THEN 0" " WHEN R.DELETE_RULE = 'SET NULL' THEN 2" " WHEN R.DELETE_RULE = 'SET DEFAULT' THEN 4" " WHEN R.DELETE_RULE = 'SET RESTRICT' THEN 1" " WHEN R.DELETE_RULE = 'SET NO ACTION' THEN 3" " ELSE 3" " END"; ref_constraints_join= " JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R" " ON (R.CONSTRAINT_NAME = A.CONSTRAINT_NAME" " AND R.TABLE_NAME = A.TABLE_NAME" " AND R.CONSTRAINT_SCHEMA = A.TABLE_SCHEMA)"; } else { /* Just return '1' to be compatible with pre-I_S version. */ update_rule= delete_rule= "1"; ref_constraints_join= ""; } /* This is a big, ugly query. But it works! */ buff= strxmov(query, "SELECT A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT," "NULL AS PKTABLE_SCHEM," "A.REFERENCED_TABLE_NAME AS PKTABLE_NAME," "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME," "A.TABLE_SCHEMA AS FKTABLE_CAT, NULL AS FKTABLE_SCHEM," "A.TABLE_NAME AS FKTABLE_NAME," "A.COLUMN_NAME AS FKCOLUMN_NAME," "A.ORDINAL_POSITION AS KEY_SEQ,", update_rule, " AS UPDATE_RULE,", delete_rule, " AS DELETE_RULE," "A.CONSTRAINT_NAME AS FK_NAME," "'PRIMARY' AS PK_NAME," "7 AS DEFERRABILITY" " FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE A" " JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE D" " ON (D.TABLE_SCHEMA=A.REFERENCED_TABLE_SCHEMA AND D.TABLE_NAME=A.REFERENCED_TABLE_NAME" " AND D.COLUMN_NAME=A.REFERENCED_COLUMN_NAME)", ref_constraints_join, " WHERE D.CONSTRAINT_NAME='PRIMARY' ", NullS); if (szPkTableName && szPkTableName[0]) { buff= strmov(buff, "AND A.REFERENCED_TABLE_SCHEMA = "); if (szPkCatalogName && szPkCatalogName[0]) { buff= strmov(buff, "'"); buff+= mysql_real_escape_string(mysql, buff, (char *)szPkCatalogName, cbPkCatalogName); buff= strmov(buff, "' "); } else { buff= strmov(buff, "DATABASE() "); } buff= strmov(buff, "AND A.REFERENCED_TABLE_NAME = '"); buff+= mysql_real_escape_string(mysql, buff, (char *)szPkTableName, cbPkTableName); buff= strmov(buff, "' "); strmov(buff, "ORDER BY PKTABLE_CAT, PKTABLE_NAME, " "KEY_SEQ, FKTABLE_NAME"); } if (szFkTableName && szFkTableName[0]) { buff= strmov(buff, "AND A.TABLE_SCHEMA = "); if (szFkCatalogName && szFkCatalogName[0]) { buff= strmov(buff, "'"); buff+= mysql_real_escape_string(mysql, buff, (char *)szFkCatalogName, cbFkCatalogName); buff= strmov(buff, "' "); } else { buff= strmov(buff, "DATABASE() "); } buff= strmov(buff, "AND A.TABLE_NAME = '"); buff+= mysql_real_escape_string(mysql, buff, (char *)szFkTableName, cbFkTableName); buff= strmov(buff, "' "); buff= strmov(buff, "ORDER BY FKTABLE_CAT, FKTABLE_NAME, " "KEY_SEQ, PKTABLE_NAME"); } assert(buff - query < sizeof(query)); rc= MySQLPrepare(hstmt, (SQLCHAR *)query, (SQLINTEGER)(buff - query), FALSE); if (!SQL_SUCCEEDED(rc)) return rc; return my_SQLExecute(hstmt); } /** Retrieve either a list of foreign keys in a specified table, or the list of foreign keys in other tables that refer to the primary key in the specified table. (We currently only support the former, not the latter.) @param[in] hstmt Handle of statement @param[in] szPkCatalogName Catalog (database) of table with primary key that we want to see foreign keys for @param[in] cbPkCatalogName Length of @a szPkCatalogName @param[in] szPkSchemaName Schema of table with primary key that we want to see foreign keys for (unused) @param[in] cbPkSchemaName Length of @a szPkSchemaName @param[in] szPkTableName Table with primary key that we want to see foreign keys for @param[in] cbPkTableName Length of @a szPkTableName @param[in] szFkCatalogName Catalog (database) of table with foreign keys we are interested in @param[in] cbFkCatalogName Length of @a szFkCatalogName @param[in] szFkSchemaName Schema of table with foreign keys we are interested in @param[in] cbFkSchemaName Length of szFkSchemaName @param[in] szFkTableName Table with foreign keys we are interested in @param[in] cbFkTableName Length of @a szFkTableName @return SQL_SUCCESS @since ODBC 1.0 */ SQLRETURN SQL_API MySQLForeignKeys(SQLHSTMT hstmt, SQLCHAR *szPkCatalogName __attribute__((unused)), SQLSMALLINT cbPkCatalogName __attribute__((unused)), SQLCHAR *szPkSchemaName __attribute__((unused)), SQLSMALLINT cbPkSchemaName __attribute__((unused)), SQLCHAR *szPkTableName, SQLSMALLINT cbPkTableName, SQLCHAR *szFkCatalogName, SQLSMALLINT cbFkCatalogName, SQLCHAR *szFkSchemaName __attribute__((unused)), SQLSMALLINT cbFkSchemaName __attribute__((unused)), SQLCHAR *szFkTableName, SQLSMALLINT cbFkTableName) { STMT FAR *stmt=(STMT FAR*) hstmt; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); if (cbPkTableName == SQL_NTS) cbPkTableName= szPkTableName != NULL ? (SQLSMALLINT)strlen((char *)szPkTableName) : 0; if (cbPkCatalogName == SQL_NTS) cbPkCatalogName= szPkCatalogName != NULL ? (SQLSMALLINT)strlen((char *)szPkCatalogName) : 0; if (cbFkCatalogName == SQL_NTS) cbFkCatalogName= szFkCatalogName != NULL ? (SQLSMALLINT)strlen((char *)szFkCatalogName) : 0; if (cbFkTableName == SQL_NTS) cbFkTableName= szFkTableName != NULL ? (SQLSMALLINT)strlen((char *)szFkTableName) : 0; if (server_has_i_s(stmt->dbc) && !stmt->dbc->ds->no_information_schema) { return i_s_foreign_keys(hstmt, szPkCatalogName, cbPkCatalogName, szPkSchemaName, cbPkSchemaName, szPkTableName, cbPkTableName, szFkCatalogName, cbFkCatalogName, szFkSchemaName, cbFkSchemaName, szFkTableName, cbFkTableName); } /* For 3.23 and later, use comment in SHOW TABLE STATUS (yuck). */ else /* We wouldn't get here if we had server version under 3.23 */ { return mysql_foreign_keys(hstmt, szPkCatalogName, cbPkCatalogName, szPkSchemaName, cbPkSchemaName, szPkTableName, cbPkTableName, szFkCatalogName, cbFkCatalogName, szFkSchemaName, cbFkSchemaName, szFkTableName, cbFkTableName); } } /* **************************************************************************** SQLProcedures and SQLProcedureColumns **************************************************************************** */ /** Get the list of procedures stored in a catalog (database). This is done by generating the appropriate query against INFORMATION_SCHEMA. If no database is specified, the current database is used. @param[in] hstmt Handle of statement @param[in] szCatalogName Name of catalog (database) @param[in] cbCatalogName Length of catalog @param[in] szSchemaName Pattern of schema (unused) @param[in] cbSchemaName Length of schema name @param[in] szProcName Pattern of procedure names to fetch @param[in] cbProcName Length of procedure name */ SQLRETURN SQL_API MySQLProcedures(SQLHSTMT hstmt, SQLCHAR *szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR *szSchemaName __attribute__((unused)), SQLSMALLINT cbSchemaName __attribute__((unused)), SQLCHAR *szProcName, SQLSMALLINT cbProcName) { SQLRETURN rc; STMT *stmt= (STMT *)hstmt; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); /* If earlier than 5.0, the server doesn't even support stored procs. */ if (!server_has_i_s(stmt->dbc)) { /* We use the server to generate a fake result with no rows, but reasonable column information. */ if ((rc= MySQLPrepare(hstmt, (SQLCHAR *)"SELECT " "'' AS PROCEDURE_CAT," "'' AS PROCEDURE_SCHEM," "'' AS PROCEDURE_NAME," "NULL AS NUM_INPUT_PARAMS," "NULL AS NUM_OUTPUT_PARAMS," "NULL AS NUM_RESULT_SETS," "'' AS REMARKS," "0 AS PROCEDURE_TYPE " "FROM DUAL WHERE 1=0", SQL_NTS, FALSE))) return rc; return my_SQLExecute(hstmt); } /* If a catalog (database) was specified, we use that, otherwise we look up procedures from the current database. (This is not standard behavior, but seems useful.) */ if (szCatalogName && szProcName) rc= MySQLPrepare(hstmt, (SQLCHAR *) "SELECT ROUTINE_SCHEMA AS PROCEDURE_CAT," "NULL AS PROCEDURE_SCHEM," "ROUTINE_NAME AS PROCEDURE_NAME," "NULL AS NUM_INPUT_PARAMS," "NULL AS NUM_OUTPUT_PARAMS," "NULL AS NUM_RESULT_SETS," "ROUTINE_COMMENT AS REMARKS," "IF(ROUTINE_TYPE = 'FUNCTION', 2," "IF(ROUTINE_TYPE= 'PROCEDURE', 1, 0)) AS PROCEDURE_TYPE" " FROM INFORMATION_SCHEMA.ROUTINES" " WHERE ROUTINE_NAME LIKE ? AND ROUTINE_SCHEMA = ?", SQL_NTS, FALSE); else if (szProcName) rc= MySQLPrepare(hstmt, (SQLCHAR *) "SELECT ROUTINE_SCHEMA AS PROCEDURE_CAT," "NULL AS PROCEDURE_SCHEM," "ROUTINE_NAME AS PROCEDURE_NAME," "NULL AS NUM_INPUT_PARAMS," "NULL AS NUM_OUTPUT_PARAMS," "NULL AS NUM_RESULT_SETS," "ROUTINE_COMMENT AS REMARKS," "IF(ROUTINE_TYPE = 'FUNCTION', 2," "IF(ROUTINE_TYPE= 'PROCEDURE', 1, 0)) AS PROCEDURE_TYPE" " FROM INFORMATION_SCHEMA.ROUTINES" " WHERE ROUTINE_NAME LIKE ?" " AND ROUTINE_SCHEMA = DATABASE()", SQL_NTS, FALSE); else rc= MySQLPrepare(hstmt, (SQLCHAR *) "SELECT ROUTINE_SCHEMA AS PROCEDURE_CAT," "NULL AS PROCEDURE_SCHEM," "ROUTINE_NAME AS PROCEDURE_NAME," "NULL AS NUM_INPUT_PARAMS," "NULL AS NUM_OUTPUT_PARAMS," "NULL AS NUM_RESULT_SETS," "ROUTINE_COMMENT AS REMARKS," "IF(ROUTINE_TYPE = 'FUNCTION', 2," "IF(ROUTINE_TYPE= 'PROCEDURE', 1, 0)) AS PROCEDURE_TYPE" " FROM INFORMATION_SCHEMA.ROUTINES" " WHERE ROUTINE_SCHEMA = DATABASE()", SQL_NTS, FALSE); if (!SQL_SUCCEEDED(rc)) return rc; if (szProcName) { if (cbProcName == SQL_NTS) cbProcName= (SQLSMALLINT)strlen((const char *)szProcName); rc= my_SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_C_CHAR, 0, 0, szProcName, cbProcName, NULL); if (!SQL_SUCCEEDED(rc)) return rc; } if (szCatalogName) { if (cbCatalogName == SQL_NTS) cbCatalogName= (SQLSMALLINT)strlen((const char *)szCatalogName); rc= my_SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_C_CHAR, 0, 0, szCatalogName, cbCatalogName, NULL); if (!SQL_SUCCEEDED(rc)) return rc; } return my_SQLExecute(hstmt); } /* **************************************************************************** SQLProcedure Columns **************************************************************************** */ /* @purpose : returns the list of input and output parameters, as well as the columns that make up the result set for the specified procedures. The driver returns the information as a result set on the specified statement */ SQLRETURN i_s_procedure_columns(SQLHSTMT hstmt, SQLCHAR *szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR *szSchemaName __attribute__((unused)), SQLSMALLINT cbSchemaName __attribute__((unused)), SQLCHAR *szProcName, SQLSMALLINT cbProcName, SQLCHAR *szColumnName, SQLSMALLINT cbColumnName) { /* The function is just a stub. We call non-I_S version of the function before implementing the I_S one */ return mysql_procedure_columns(hstmt, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szProcName, cbProcName, szColumnName, cbColumnName); } /* @type : ODBC 1.0 API @purpose : returns the list of input and output parameters, as well as the columns that make up the result set for the specified procedures. The driver returns the information as a result set on the specified statement */ SQLRETURN SQL_API MySQLProcedureColumns(SQLHSTMT hstmt, SQLCHAR *szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR *szSchemaName __attribute__((unused)), SQLSMALLINT cbSchemaName __attribute__((unused)), SQLCHAR *szProcName, SQLSMALLINT cbProcName, SQLCHAR *szColumnName, SQLSMALLINT cbColumnName) { STMT *stmt= (STMT *)hstmt; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); if (cbCatalogName == SQL_NTS) cbCatalogName= szCatalogName ? (SQLSMALLINT)strlen((char *)szCatalogName) : 0; if (cbProcName == SQL_NTS) cbProcName= szProcName ? (SQLSMALLINT)strlen((char *)szProcName) : 0; if (cbColumnName == SQL_NTS) cbColumnName= szColumnName ? (SQLSMALLINT)strlen((char *)szColumnName) : 0; if (server_has_i_s(stmt->dbc) && !stmt->dbc->ds->no_information_schema) { return i_s_procedure_columns(hstmt, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szProcName, cbProcName, szColumnName, cbColumnName); } else { return mysql_procedure_columns(hstmt, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szProcName, cbProcName, szColumnName, cbColumnName); } } mysql-connector-odbc-5.1.10-src/driver/error.h100644 15766 12 7634 11707541005 17775 0ustar00cteamstaff/* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /*************************************************************************** * ERROR.H * * * * @description: Definitions for error handling * * * * @author : MySQL AB(monty@mysql.com, venu@mysql.com) * * @date : 2001-Aug-15 * * @product : myodbc3 * * * ****************************************************************************/ #ifndef __ERROR_H__ #define __ERROR_H__ /* myodbc internal error constants */ #define ER_INVALID_CURSOR_NAME 514 #define ER_ALL_COLUMNS_IGNORED 537 /* myodbc3 error prefix */ #define MYODBC3_ERROR_PREFIX "[MySQL][ODBC 5.1 Driver]" #define MYODBC_ERROR_CODE_START 500 /* error handler structure */ typedef struct tagERROR { SQLRETURN retcode; char current; char sqlstate[6]; char message[SQL_MAX_MESSAGE_LENGTH+1]; SQLINTEGER native_error; } MYERROR; #define CLEAR_ERROR(error) do {\ error.message[0]= '\0'; \ error.current= 0; \ } while (0) #define CLEAR_ENV_ERROR(env) CLEAR_ERROR(((ENV *)env)->error) #define CLEAR_DBC_ERROR(dbc) CLEAR_ERROR(((DBC *)dbc)->error) #define CLEAR_STMT_ERROR(stmt) CLEAR_ERROR(((STMT *)stmt)->error) #define CLEAR_DESC_ERROR(desc) CLEAR_ERROR(((DESC *)desc)->error) #define NEXT_ERROR(error) \ (error.current ? 2 : (error.current= 1)) #define NEXT_ENV_ERROR(env) NEXT_ERROR(((ENV *)env)->error) #define NEXT_DBC_ERROR(dbc) NEXT_ERROR(((DBC *)dbc)->error) #define NEXT_STMT_ERROR(stmt) NEXT_ERROR(((STMT *)stmt)->error) #define NEXT_DESC_ERROR(desc) NEXT_ERROR(((DESC *)desc)->error) /* list of MyODBC3 error codes */ typedef enum myodbc_errid { MYERR_01000 = 0, MYERR_01004, MYERR_01S02, MYERR_01S03, MYERR_01S04, MYERR_01S06, MYERR_07001, MYERR_07005, MYERR_07006, MYERR_07009, MYERR_08002, MYERR_08003, MYERR_24000, MYERR_25000, MYERR_25S01, MYERR_34000, MYERR_HYT00, MYERR_S1000, MYERR_S1001, MYERR_S1002, MYERR_S1003, MYERR_S1004, MYERR_S1007, MYERR_S1009, MYERR_S1010, MYERR_S1011, MYERR_S1012, MYERR_S1013, MYERR_S1015, MYERR_S1016, MYERR_S1017, MYERR_S1024, MYERR_S1090, MYERR_S1091, MYERR_S1092, MYERR_S1093, MYERR_S1095, MYERR_S1106, MYERR_S1107, MYERR_S1109, MYERR_S1C00, MYERR_21S01, MYERR_23000, MYERR_42000, MYERR_42S01, MYERR_42S02, MYERR_42S12, MYERR_42S21, MYERR_42S22, MYERR_08S01 } myodbc_errid; /* error handler-predefined structure odbc2 state, odbc3 state, message and return code */ typedef struct myodbc3_err_str { char sqlstate[6]; /* ODBC3 STATE, if SQL_OV_ODBC2, then ODBC2 STATE */ char message[SQL_MAX_MESSAGE_LENGTH+1];/* ERROR MSG */ SQLRETURN retcode; /* RETURN CODE */ } MYODBC3_ERR_STR; #endif /* __ERROR_H__ */ mysql-connector-odbc-5.1.10-src/driver/desc.c100644 15766 12 71576 11707541005 17603 0ustar00cteamstaff/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file desc.c @brief Functions for handling descriptors. */ /*************************************************************************** * The following ODBC APIs are implemented in this file: * * SQLSetDescField (ISO 92) * * SQLGetDescField (ISO 92) * * SQLCopyDesc (ISO 92) * ****************************************************************************/ #include "driver.h" /* Utility macros for defining descriptor fields */ #define HDR_FLD(field, perm, type) \ static desc_field HDR_##field= \ {(perm), (type), DESC_HDR, offsetof(DESC, field)} /* parens around field in offsetof() confuse GCC */ #define REC_FLD(field, perm, type) \ static desc_field REC_##field= \ {(perm), (type), DESC_REC, offsetof(DESCREC, field)} /* * Allocate a new descriptor. * Should be used to allocate 'implicit' descriptors on the statement * or 'explicit' user-requested descriptors. */ DESC *desc_alloc(STMT *stmt, SQLSMALLINT alloc_type, desc_ref_type ref_type, desc_desc_type desc_type) { DESC *desc= (DESC *)my_malloc(sizeof(DESC), MYF(MY_ZEROFILL)); if (!desc) return NULL; /* We let the dynamic array handle the memory for the whole DESCREC, but in desc_get_rec we manually get a pointer to it. This avoids having to call set_dynamic after modifying the DESCREC. */ if (my_init_dynamic_array(&desc->records, sizeof(DESCREC), 0, 0)) { x_free((char *)desc); return NULL; } desc->desc_type= desc_type; desc->alloc_type= alloc_type; desc->ref_type= ref_type; desc->stmt= stmt; /* spec-defined defaults/initialization */ desc->array_size= 1; desc->array_status_ptr= NULL; desc->bind_offset_ptr= NULL; desc->bind_type= SQL_BIND_BY_COLUMN; desc->count= 0; desc->rows_processed_ptr= NULL; desc->exp.stmts= NULL; return desc; } /* Free a descriptor. */ void desc_free(DESC *desc) { assert(desc); if (IS_APD(desc)) desc_free_paramdata(desc); delete_dynamic(&desc->records); x_free(desc); } /* Free any memory allocated for SQLPutData(). This is only useful for APDs. */ void desc_free_paramdata(DESC *desc) { SQLLEN i; for (i= 0; i < desc->count; ++i) { DESCREC *aprec= desc_get_rec(desc, i, FALSE); assert(aprec); if (aprec->par.alloced) { aprec->par.alloced= FALSE; x_free(aprec->par.value); } } } /* * Initialize APD */ void desc_rec_init_apd(DESCREC *rec) { memset(rec, 0, sizeof(DESCREC)); /* ODBC defaults */ rec->concise_type= SQL_C_DEFAULT; rec->data_ptr= NULL; rec->indicator_ptr= NULL; rec->octet_length_ptr= NULL; rec->type= SQL_C_DEFAULT; /* internal */ rec->par.alloced= FALSE; rec->par.value= NULL; } /* * Initialize IPD */ void desc_rec_init_ipd(DESCREC *rec) { memset(rec, 0, sizeof(DESCREC)); /* ODBC defaults */ rec->fixed_prec_scale= SQL_TRUE; rec->local_type_name= (SQLCHAR *)""; rec->nullable= SQL_NULLABLE; rec->parameter_type= SQL_PARAM_INPUT; rec->type_name= (SQLCHAR *)"VARCHAR"; rec->is_unsigned= SQL_FALSE; /* driver defaults */ rec->name= (SQLCHAR *)""; } /* * Initialize ARD */ void desc_rec_init_ard(DESCREC *rec) { memset(rec, 0, sizeof(DESCREC)); /* ODBC defaults */ rec->concise_type= SQL_C_DEFAULT; rec->data_ptr= NULL; rec->indicator_ptr= NULL; rec->octet_length_ptr= NULL; rec->type= SQL_C_DEFAULT; } /* * Initialize IRD */ void desc_rec_init_ird(DESCREC *rec) { memset(rec, 0, sizeof(DESCREC)); /* ODBC defaults */ /* driver defaults */ rec->auto_unique_value= SQL_FALSE; rec->case_sensitive= SQL_TRUE; rec->concise_type= SQL_VARCHAR; rec->display_size= 100;/*?*/ rec->fixed_prec_scale= SQL_TRUE; rec->length= 100;/*?*/ rec->nullable= SQL_NULLABLE_UNKNOWN; rec->type= SQL_VARCHAR; rec->type_name= (SQLCHAR *)"VARCHAR"; rec->unnamed= SQL_UNNAMED; rec->is_unsigned= SQL_FALSE; } /* * Get a record from the descriptor. * * @param desc Descriptor * @param recnum 0-based record number * @param expand Whether to expand the descriptor to include the given * recnum. * @return The requested record of NULL if it doesn't exist * (and isn't created). */ DESCREC *desc_get_rec(DESC *desc, int recnum, my_bool expand) { DESCREC *rec= NULL; int i; assert(recnum >= 0); /* expand if needed */ if (expand) { for (i= desc->count; expand && i <= recnum; ++i) { /* we might have used records lying around from before if * SQLFreeStmt() was called with SQL_UNBIND or SQL_FREE_PARAMS */ if (i < desc->records.elements) { rec= ((DESCREC *)desc->records.buffer) + recnum; } else { rec= (DESCREC *)alloc_dynamic(&desc->records); if (!rec) return NULL; } memset(rec, 0, sizeof(DESCREC)); ++desc->count; /* record initialization */ if (IS_APD(desc)) desc_rec_init_apd(rec); else if (IS_IPD(desc)) desc_rec_init_ipd(rec); else if (IS_ARD(desc)) desc_rec_init_ard(rec); else if (IS_IRD(desc)) desc_rec_init_ird(rec); } } if (recnum < desc->count) rec= ((DESCREC *)desc->records.buffer) + recnum; if (expand) assert(rec); return rec; } /* * Disassociate a statement from an explicitly allocated * descriptor. * * @param desc The descriptor * @param stmt The statement */ void desc_remove_stmt(DESC *desc, STMT *stmt) { LIST *lstmt; if (desc->alloc_type != SQL_DESC_ALLOC_USER) return; for (lstmt= desc->exp.stmts; lstmt; lstmt= lstmt->next) { if (lstmt->data == stmt) { desc->exp.stmts= list_delete(desc->exp.stmts, lstmt); return; } } assert(!"Statement was not associated with descriptor"); } /* * Check with the given descriptor contains any data-at-exec * records. Return the record number or -1 if none are found. */ int desc_find_dae_rec(DESC *desc) { int i; DESCREC *rec; SQLLEN *octet_length_ptr; for (i= 0; i < desc->count; ++i) { rec= desc_get_rec(desc, i, FALSE); assert(rec); octet_length_ptr= ptr_offset_adjust(rec->octet_length_ptr, desc->bind_offset_ptr, desc->bind_type, sizeof(SQLLEN), /*row*/0); if (IS_DATA_AT_EXEC(octet_length_ptr)) return i; } return -1; } /* * Apply the actual value to the descriptor field. * * @param dest Pointer to descriptor field to be set. * @param dest_type Type of descriptor field (same type constants as buflen). * @param src Value to be set. * @param buflen Length of value (as specified by SQLSetDescField). */ static void apply_desc_val(void *dest, SQLSMALLINT dest_type, void *src, SQLINTEGER buflen) { switch (buflen) { case SQL_IS_SMALLINT: case SQL_IS_INTEGER: case SQL_IS_LEN: if (dest_type == SQL_IS_SMALLINT) *(SQLSMALLINT *)dest= (SQLLEN)src; else if (dest_type == SQL_IS_USMALLINT) *(SQLUSMALLINT *)dest= (SQLLEN)src; else if (dest_type == SQL_IS_INTEGER) *(SQLINTEGER *)dest= (SQLLEN)src; else if (dest_type == SQL_IS_UINTEGER) *(SQLUINTEGER *)dest= (SQLLEN)src; else if (dest_type == SQL_IS_LEN) *(SQLLEN *)dest= (SQLLEN)src; else if (dest_type == SQL_IS_ULEN) *(SQLULEN *)dest= (SQLLEN)src; break; case SQL_IS_USMALLINT: case SQL_IS_UINTEGER: case SQL_IS_ULEN: if (dest_type == SQL_IS_SMALLINT) *(SQLSMALLINT *)dest= (SQLULEN)src; else if (dest_type == SQL_IS_USMALLINT) *(SQLUSMALLINT *)dest= (SQLULEN)src; else if (dest_type == SQL_IS_INTEGER) *(SQLINTEGER *)dest= (SQLULEN)src; else if (dest_type == SQL_IS_UINTEGER) *(SQLUINTEGER *)dest= (SQLULEN)src; else if (dest_type == SQL_IS_LEN) *(SQLLEN *)dest= (SQLULEN)src; else if (dest_type == SQL_IS_ULEN) *(SQLULEN *)dest= (SQLULEN)src; break; case SQL_IS_POINTER: *(SQLPOINTER *)dest= src; break; default: /* TODO it's an actual data length */ /* free/malloc to the field and copy it */ /* TODO .. check for 22001 - String data, right truncated * The FieldIdentifier argument was SQL_DESC_NAME, * and the BufferLength argument was a value larger * than SQL_MAX_IDENTIFIER_LEN. */ break; } } /* * Get a descriptor field based on the constant. */ static desc_field * getfield(SQLSMALLINT fldid) { /* all field descriptions are immutable */ /* See: SQLSetDescField() documentation * http://msdn2.microsoft.com/en-us/library/ms713560.aspx */ HDR_FLD(alloc_type , P_RI|P_RA , SQL_IS_SMALLINT); HDR_FLD(array_size , P_RA|P_WA , SQL_IS_ULEN ); HDR_FLD(array_status_ptr , P_RI|P_WI|P_RA|P_WA, SQL_IS_POINTER ); HDR_FLD(bind_offset_ptr , P_RA|P_WA , SQL_IS_POINTER ); HDR_FLD(bind_type , P_RA|P_WA , SQL_IS_INTEGER ); HDR_FLD(count , P_RI|P_WI|P_RA|P_WA, SQL_IS_LEN ); HDR_FLD(rows_processed_ptr, P_RI|P_WI , SQL_IS_POINTER ); REC_FLD(auto_unique_value, PR_RIR , SQL_IS_INTEGER); REC_FLD(base_column_name , PR_RIR , SQL_IS_POINTER); REC_FLD(base_table_name , PR_RIR , SQL_IS_POINTER); REC_FLD(case_sensitive , PR_RIR|PR_RIP , SQL_IS_INTEGER); REC_FLD(catalog_name , PR_RIR , SQL_IS_POINTER); REC_FLD(concise_type , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_SMALLINT); REC_FLD(data_ptr , PR_WAR|PR_WAP , SQL_IS_POINTER); REC_FLD(display_size , PR_RIR , SQL_IS_LEN); REC_FLD(fixed_prec_scale , PR_RIR|PR_RIP , SQL_IS_SMALLINT); REC_FLD(indicator_ptr , PR_WAR|PR_WAP , SQL_IS_POINTER); REC_FLD(label , PR_RIR , SQL_IS_POINTER); REC_FLD(length , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_ULEN); REC_FLD(literal_prefix , PR_RIR , SQL_IS_POINTER); REC_FLD(literal_suffix , PR_RIR , SQL_IS_POINTER); REC_FLD(local_type_name , PR_RIR|PR_RIP , SQL_IS_POINTER); REC_FLD(name , PR_RIR|PR_WIP , SQL_IS_POINTER); REC_FLD(nullable , PR_RIR|PR_RIP , SQL_IS_SMALLINT); REC_FLD(num_prec_radix , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_INTEGER); REC_FLD(octet_length , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_LEN); REC_FLD(octet_length_ptr , PR_WAR|PR_WAP , SQL_IS_POINTER); REC_FLD(parameter_type , PR_WIP , SQL_IS_SMALLINT); REC_FLD(precision , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_SMALLINT); REC_FLD(rowver , PR_RIR|PR_RIP , SQL_IS_SMALLINT); REC_FLD(scale , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_SMALLINT); REC_FLD(schema_name , PR_RIR , SQL_IS_POINTER); REC_FLD(searchable , PR_RIR , SQL_IS_SMALLINT); REC_FLD(table_name , PR_RIR , SQL_IS_POINTER); REC_FLD(type , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_SMALLINT); REC_FLD(type_name , PR_RIR|PR_RIP , SQL_IS_POINTER); REC_FLD(unnamed , PR_RIR|PR_WIP , SQL_IS_SMALLINT); REC_FLD(is_unsigned , PR_RIR|PR_RIP , SQL_IS_SMALLINT); REC_FLD(updatable , PR_RIR , SQL_IS_SMALLINT); REC_FLD(datetime_interval_code , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_SMALLINT); REC_FLD(datetime_interval_precision , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_INTEGER); /* match 'field' names above */ switch(fldid) { case SQL_DESC_ALLOC_TYPE: return &HDR_alloc_type; case SQL_DESC_ARRAY_SIZE: return &HDR_array_size; case SQL_DESC_ARRAY_STATUS_PTR: return &HDR_array_status_ptr; case SQL_DESC_BIND_OFFSET_PTR: return &HDR_bind_offset_ptr; case SQL_DESC_BIND_TYPE: return &HDR_bind_type; case SQL_DESC_COUNT: return &HDR_count; case SQL_DESC_ROWS_PROCESSED_PTR: return &HDR_rows_processed_ptr; case SQL_DESC_AUTO_UNIQUE_VALUE: return &REC_auto_unique_value; case SQL_DESC_BASE_COLUMN_NAME: return &REC_base_column_name; case SQL_DESC_BASE_TABLE_NAME: return &REC_base_table_name; case SQL_DESC_CASE_SENSITIVE: return &REC_case_sensitive; case SQL_DESC_CATALOG_NAME: return &REC_catalog_name; case SQL_DESC_CONCISE_TYPE: return &REC_concise_type; case SQL_DESC_DATA_PTR: return &REC_data_ptr; case SQL_DESC_DISPLAY_SIZE: return &REC_display_size; case SQL_DESC_FIXED_PREC_SCALE: return &REC_fixed_prec_scale; case SQL_DESC_INDICATOR_PTR: return &REC_indicator_ptr; case SQL_DESC_LABEL: return &REC_label; case SQL_DESC_LENGTH: return &REC_length; case SQL_DESC_LITERAL_PREFIX: return &REC_literal_prefix; case SQL_DESC_LITERAL_SUFFIX: return &REC_literal_suffix; case SQL_DESC_LOCAL_TYPE_NAME: return &REC_local_type_name; case SQL_DESC_NAME: return &REC_name; case SQL_DESC_NULLABLE: return &REC_nullable; case SQL_DESC_NUM_PREC_RADIX: return &REC_num_prec_radix; case SQL_DESC_OCTET_LENGTH: return &REC_octet_length; case SQL_DESC_OCTET_LENGTH_PTR: return &REC_octet_length_ptr; case SQL_DESC_PARAMETER_TYPE: return &REC_parameter_type; case SQL_DESC_PRECISION: return &REC_precision; case SQL_DESC_ROWVER: return &REC_rowver; case SQL_DESC_SCALE: return &REC_scale; case SQL_DESC_SCHEMA_NAME: return &REC_schema_name; case SQL_DESC_SEARCHABLE: return &REC_searchable; case SQL_DESC_TABLE_NAME: return &REC_table_name; case SQL_DESC_TYPE: return &REC_type; case SQL_DESC_TYPE_NAME: return &REC_type_name; case SQL_DESC_UNNAMED: return &REC_unnamed; case SQL_DESC_UNSIGNED: return &REC_is_unsigned; case SQL_DESC_UPDATABLE: return &REC_updatable; case SQL_DESC_DATETIME_INTERVAL_CODE: return &REC_datetime_interval_code; case SQL_DESC_DATETIME_INTERVAL_PRECISION: return &REC_datetime_interval_precision; } return NULL; } /* @type : ODBC 3.0 API @purpose : Get a field of a descriptor. */ SQLRETURN MySQLGetDescField(SQLHDESC hdesc, SQLSMALLINT recnum, SQLSMALLINT fldid, SQLPOINTER valptr, SQLINTEGER buflen, SQLINTEGER *outlen) { desc_field *fld= getfield(fldid); DESC *desc= (DESC *)hdesc; void *src_struct; void *src; CLEAR_DESC_ERROR(desc); if (IS_IRD(desc) && desc->stmt->state < ST_PREPARED) /* TODO if it's prepared is the IRD still ok to access? * or must we pre-execute it */ return set_desc_error(desc, "HY007", "Associated statement is not prepared", MYERR_S1007); if ((fld == NULL) || /* header permissions check */ (fld->loc == DESC_HDR && (desc->ref_type == DESC_APP && (~fld->perms & P_RA)) || (desc->ref_type == DESC_IMP && (~fld->perms & P_RI)))) { return set_desc_error(desc, "HY091", "Invalid descriptor field identifier", MYERR_S1091); } else if (fld->loc == DESC_REC) { int perms= 0; /* needed perms to access */ if (desc->ref_type == DESC_APP) perms= P_RA; else if (desc->ref_type == DESC_IMP) perms= P_RI; if (desc->desc_type == DESC_PARAM) perms= P_PAR(perms); else if (desc->desc_type == DESC_ROW) perms= P_ROW(perms); if ((~fld->perms & perms) == perms) return set_desc_error(desc, "HY091", "Invalid descriptor field identifier", MYERR_S1091); } /* get the src struct */ if (fld->loc == DESC_HDR) src_struct= desc; else { if (recnum < 1 || recnum > desc->count) return set_desc_error(desc, "07009", "Invalid descriptor index", MYERR_07009); src_struct= desc_get_rec(desc, recnum - 1, FALSE); assert(src_struct); } src= ((char *)src_struct) + fld->offset; /* TODO checks when strings? */ if ((fld->data_type == SQL_IS_POINTER && buflen != SQL_IS_POINTER) || (fld->data_type != SQL_IS_POINTER && buflen == SQL_IS_POINTER)) return set_desc_error(desc, "HY015", "Invalid parameter type", MYERR_S1015); switch (buflen) { case SQL_IS_SMALLINT: if (fld->data_type == SQL_IS_SMALLINT) *(SQLSMALLINT *)valptr= *(SQLSMALLINT *)src; else if (fld->data_type == SQL_IS_USMALLINT) *(SQLSMALLINT *)valptr= *(SQLUSMALLINT *)src; else if (fld->data_type == SQL_IS_INTEGER) *(SQLSMALLINT *)valptr= *(SQLINTEGER *)src; else if (fld->data_type == SQL_IS_UINTEGER) *(SQLSMALLINT *)valptr= *(SQLUINTEGER *)src; else if (fld->data_type == SQL_IS_LEN) *(SQLSMALLINT *)valptr= *(SQLLEN *)src; else if (fld->data_type == SQL_IS_ULEN) *(SQLSMALLINT *)valptr= *(SQLULEN *)src; break; case SQL_IS_USMALLINT: if (fld->data_type == SQL_IS_SMALLINT) *(SQLUSMALLINT *)valptr= *(SQLSMALLINT *)src; else if (fld->data_type == SQL_IS_USMALLINT) *(SQLUSMALLINT *)valptr= *(SQLUSMALLINT *)src; else if (fld->data_type == SQL_IS_INTEGER) *(SQLUSMALLINT *)valptr= *(SQLINTEGER *)src; else if (fld->data_type == SQL_IS_UINTEGER) *(SQLUSMALLINT *)valptr= *(SQLUINTEGER *)src; else if (fld->data_type == SQL_IS_LEN) *(SQLUSMALLINT *)valptr= *(SQLLEN *)src; else if (fld->data_type == SQL_IS_ULEN) *(SQLUSMALLINT *)valptr= *(SQLULEN *)src; break; case SQL_IS_INTEGER: if (fld->data_type == SQL_IS_SMALLINT) *(SQLINTEGER *)valptr= *(SQLSMALLINT *)src; else if (fld->data_type == SQL_IS_USMALLINT) *(SQLINTEGER *)valptr= *(SQLUSMALLINT *)src; else if (fld->data_type == SQL_IS_INTEGER) *(SQLINTEGER *)valptr= *(SQLINTEGER *)src; else if (fld->data_type == SQL_IS_UINTEGER) *(SQLINTEGER *)valptr= *(SQLUINTEGER *)src; else if (fld->data_type == SQL_IS_LEN) *(SQLINTEGER *)valptr= *(SQLLEN *)src; else if (fld->data_type == SQL_IS_ULEN) *(SQLINTEGER *)valptr= *(SQLULEN *)src; break; case SQL_IS_UINTEGER: if (fld->data_type == SQL_IS_SMALLINT) *(SQLUINTEGER *)valptr= *(SQLSMALLINT *)src; else if (fld->data_type == SQL_IS_USMALLINT) *(SQLUINTEGER *)valptr= *(SQLUSMALLINT *)src; else if (fld->data_type == SQL_IS_INTEGER) *(SQLUINTEGER *)valptr= *(SQLINTEGER *)src; else if (fld->data_type == SQL_IS_UINTEGER) *(SQLUINTEGER *)valptr= *(SQLUINTEGER *)src; else if (fld->data_type == SQL_IS_LEN) *(SQLUINTEGER *)valptr= *(SQLLEN *)src; else if (fld->data_type == SQL_IS_ULEN) *(SQLUINTEGER *)valptr= *(SQLULEN *)src; break; case SQL_IS_LEN: if (fld->data_type == SQL_IS_SMALLINT) *(SQLLEN *)valptr= *(SQLSMALLINT *)src; else if (fld->data_type == SQL_IS_USMALLINT) *(SQLLEN *)valptr= *(SQLUSMALLINT *)src; else if (fld->data_type == SQL_IS_INTEGER) *(SQLLEN *)valptr= *(SQLINTEGER *)src; else if (fld->data_type == SQL_IS_UINTEGER) *(SQLLEN *)valptr= *(SQLUINTEGER *)src; else if (fld->data_type == SQL_IS_LEN) *(SQLLEN *)valptr= *(SQLLEN *)src; else if (fld->data_type == SQL_IS_ULEN) *(SQLLEN *)valptr= *(SQLULEN *)src; break; case SQL_IS_ULEN: if (fld->data_type == SQL_IS_SMALLINT) *(SQLULEN *)valptr= *(SQLSMALLINT *)src; else if (fld->data_type == SQL_IS_USMALLINT) *(SQLULEN *)valptr= *(SQLUSMALLINT *)src; else if (fld->data_type == SQL_IS_INTEGER) *(SQLULEN *)valptr= *(SQLINTEGER *)src; else if (fld->data_type == SQL_IS_UINTEGER) *(SQLULEN *)valptr= *(SQLUINTEGER *)src; else if (fld->data_type == SQL_IS_LEN) *(SQLULEN *)valptr= *(SQLLEN *)src; else if (fld->data_type == SQL_IS_ULEN) *(SQLULEN *)valptr= *(SQLULEN *)src; break; case SQL_IS_POINTER: *(SQLPOINTER *)valptr= *(SQLPOINTER *)src; break; default: /* TODO it's an actual data length */ /* free/malloc to the field and copy it, etc, etc */ break; } return SQL_SUCCESS; } /* @type : ODBC 3.0 API @purpose : Set a field of a descriptor. */ SQLRETURN MySQLSetDescField(SQLHDESC hdesc, SQLSMALLINT recnum, SQLSMALLINT fldid, SQLPOINTER val, SQLINTEGER buflen) { desc_field *fld= getfield(fldid); DESC *desc= (DESC *)hdesc; void *dest_struct; void *dest; CLEAR_DESC_ERROR(desc); /* check for invalid IRD modification */ if (IS_IRD(desc)) { switch (fldid) { case SQL_DESC_ARRAY_STATUS_PTR: case SQL_DESC_ROWS_PROCESSED_PTR: break; default: return set_desc_error(desc, "HY016", "Cannot modify an implementation row descriptor", MYERR_S1016); } } if ((fld == NULL) || /* header permissions check */ (fld->loc == DESC_HDR && ((desc->ref_type == DESC_APP && (~fld->perms & P_WA)) || (desc->ref_type == DESC_IMP && (~fld->perms & P_WI))))) { return set_desc_error(desc, "HY091", "Invalid descriptor field identifier", MYERR_S1091); } else if (fld->loc == DESC_REC) { int perms= 0; /* needed perms to access */ if (desc->ref_type == DESC_APP) perms= P_WA; else if (desc->ref_type == DESC_IMP) perms= P_WI; if (desc->desc_type == DESC_PARAM) perms= P_PAR(perms); else if (desc->desc_type == DESC_ROW) perms= P_ROW(perms); if ((~fld->perms & perms) == perms) return set_desc_error(desc, "HY091", "Invalid descriptor field identifier", MYERR_S1091); } /* get the dest struct */ if (fld->loc == DESC_HDR) dest_struct= desc; else { if (recnum < 1) return set_desc_error(desc, "07009", "Invalid descriptor index", MYERR_07009); dest_struct= desc_get_rec(desc, recnum - 1, TRUE); } dest= ((char *)dest_struct) + fld->offset; /* some applications and even MSDN examples don't give a correct constant */ if (buflen == 0) buflen= fld->data_type; /* TODO checks when strings? */ if ((fld->data_type == SQL_IS_POINTER && buflen != SQL_IS_POINTER) || (fld->data_type != SQL_IS_POINTER && buflen == SQL_IS_POINTER)) return set_desc_error(desc, "HY015", "Invalid parameter type", MYERR_S1015); /* per-field checks/functionality */ switch (fldid) { case SQL_DESC_COUNT: /* we just force the descriptor record count to expand */ (void)desc_get_rec(desc, (SQLINTEGER)val - 1, TRUE); break; case SQL_DESC_NAME: /* We don't support named parameters, values stay as initialized */ return set_desc_error(desc, "01S01", "Option value changed", MYERR_01S02); case SQL_DESC_UNNAMED: if (((SQLINTEGER)val) == SQL_NAMED) return set_desc_error(desc, "HY092", "Invalid attribute/option identifier", MYERR_S1092); } /* We have to unbind the value if not setting a buffer */ switch (fldid) { case SQL_DESC_DATA_PTR: case SQL_DESC_OCTET_LENGTH_PTR: case SQL_DESC_INDICATOR_PTR: break; default: if (fld->loc == DESC_REC) { DESCREC *rec= (DESCREC *) dest_struct; rec->data_ptr= NULL; } } apply_desc_val(dest, fld->data_type, val, buflen); /* post-set responsibilities */ if ((IS_ARD(desc) || IS_APD(desc)) && fld->loc == DESC_REC) { DESCREC *rec= (DESCREC *) dest_struct; switch (fldid) { case SQL_DESC_TYPE: rec->concise_type= rec->type; rec->datetime_interval_code= 0; break; case SQL_DESC_CONCISE_TYPE: rec->type= get_type_from_concise_type(rec->concise_type); rec->datetime_interval_code= get_dticode_from_concise_type(rec->concise_type); break; case SQL_DESC_DATETIME_INTERVAL_CODE: /* TODO validation for this value? */ /* SQL_DESC_TYPE has to have already been set */ if (rec->type == SQL_DATETIME) rec->concise_type= get_concise_type_from_datetime_code(rec->datetime_interval_code); else rec->concise_type= get_concise_type_from_interval_code(rec->datetime_interval_code); break; } switch (fldid) { case SQL_DESC_TYPE: case SQL_DESC_CONCISE_TYPE: /* setup type specific defaults (TODO others besides SQL_C_NUMERIC)? */ if (IS_ARD(desc) && rec->type == SQL_C_NUMERIC) { rec->precision= 38; rec->scale= 0; } } } /* Set "real_param_done" for parameters if all fields needed to bind a parameter are set. */ if (IS_APD(desc) && val != NULL && fld->loc == DESC_REC) { DESCREC *rec= (DESCREC *) dest_struct; switch (fldid) { case SQL_DESC_DATA_PTR: case SQL_DESC_OCTET_LENGTH_PTR: case SQL_DESC_INDICATOR_PTR: rec->par.real_param_done= TRUE; break; } } return SQL_SUCCESS; } /* @type : ODBC 3.0 API @purpose : Copy descriptor information from one descriptor to another. Errors are placed in the TargetDescHandle. */ SQLRETURN MySQLCopyDesc(SQLHDESC SourceDescHandle, SQLHDESC TargetDescHandle) { DESC *src= (DESC *)SourceDescHandle; DESC *dest= (DESC *)TargetDescHandle; CLEAR_DESC_ERROR(dest); if (IS_IRD(dest)) return set_desc_error(dest, "HY016", "Cannot modify an implementation row descriptor", MYERR_S1016); if (IS_IRD(src) && src->stmt->state < ST_PREPARED) return set_desc_error(dest, "HY007", "Associated statement is not prepared", MYERR_S1007); /* copy the records */ delete_dynamic(&dest->records); if (my_init_dynamic_array(&dest->records, sizeof(DESCREC), src->records.max_element, src->records.alloc_increment)) { return set_desc_error(dest, "HY001", "Memory allocation error", MYERR_S1001); } memcpy(dest->records.buffer, src->records.buffer, src->records.max_element * src->records.size_of_element); /* copy all fields */ dest->array_size= src->array_size; dest->array_status_ptr= src->array_status_ptr; dest->bind_offset_ptr= src->bind_offset_ptr; dest->bind_type= src->bind_type; dest->count= src->count; dest->rows_processed_ptr= src->rows_processed_ptr; memcpy(&dest->error, &src->error, sizeof(MYERROR)); /* TODO consistency check on target, if needed (apd) */ return SQL_SUCCESS; } /* * Call SQLGetDescField in the "context" of a statement. This will copy * any error from the descriptor to the statement. */ SQLRETURN stmt_SQLGetDescField(STMT *stmt, DESC *desc, SQLSMALLINT recnum, SQLSMALLINT fldid, SQLPOINTER valptr, SQLINTEGER buflen, SQLINTEGER *outlen) { SQLRETURN rc; if ((rc= MySQLGetDescField((SQLHANDLE)desc, recnum, fldid, valptr, buflen, outlen)) != SQL_SUCCESS) memcpy(&stmt->error, &desc->error, sizeof(MYERROR)); return rc; } /* * Call SQLSetDescField in the "context" of a statement. This will copy * any error from the descriptor to the statement. */ SQLRETURN stmt_SQLSetDescField(STMT *stmt, DESC *desc, SQLSMALLINT recnum, SQLSMALLINT fldid, SQLPOINTER val, SQLINTEGER buflen) { SQLRETURN rc; if ((rc= MySQLSetDescField((SQLHANDLE)desc, recnum, fldid, val, buflen)) != SQL_SUCCESS) memcpy(&stmt->error, &desc->error, sizeof(MYERROR)); return rc; } /* * Call SQLCopyDesc in the "context" of a statement. This will copy * any error from the descriptor to the statement. */ SQLRETURN stmt_SQLCopyDesc(STMT *stmt, DESC *src, DESC *dest) { SQLRETURN rc; if ((rc= MySQLCopyDesc((SQLHANDLE)src, (SQLHANDLE)dest)) != SQL_SUCCESS) memcpy(&stmt->error, &dest->error, sizeof(MYERROR)); return rc; } SQLRETURN SQL_API SQLCopyDesc(SQLHDESC SourceDescHandle, SQLHDESC TargetDescHandle) { return MySQLCopyDesc(SourceDescHandle, TargetDescHandle); } mysql-connector-odbc-5.1.10-src/driver/execute.c100644 15766 12 112146 11707541005 20334 0ustar00cteamstaff/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file execute.c @brief Statement execution functions. */ #include "driver.h" #include /* @type : myodbc3 internal @purpose : internal function to execute query and return result frees query if query != stmt->query */ SQLRETURN do_query(STMT FAR *stmt,char *query, SQLULEN query_length) { int error= SQL_ERROR; if ( !query ) return error; /* Probably error from insert_param */ if(!SQL_SUCCEEDED(set_sql_select_limit(stmt->dbc, stmt->stmt_options.max_rows))) { /* if setting sql_select_limit fails, the query will probably fail anyway too */ return error; } if (query_length == 0) query_length= strlen(query); MYLOG_QUERY(stmt, query); pthread_mutex_lock(&stmt->dbc->lock); if ( check_if_server_is_alive( stmt->dbc ) ) { set_stmt_error( stmt, "08S01" /* "HYT00" */, mysql_error( &stmt->dbc->mysql ), mysql_errno( &stmt->dbc->mysql ) ); translate_error( stmt->error.sqlstate,MYERR_08S01 /* S1000 */, mysql_errno( &stmt->dbc->mysql ) ); goto exit; } if ( mysql_real_query(&stmt->dbc->mysql,query,query_length) ) { set_stmt_error(stmt,"HY000",mysql_error(&stmt->dbc->mysql), mysql_errno(&stmt->dbc->mysql)); translate_error(stmt->error.sqlstate,MYERR_S1000, mysql_errno(&stmt->dbc->mysql)); goto exit; } /* We can't use USE_RESULT because SQLRowCount will fail in this case! */ if ( if_forward_cache(stmt) ) stmt->result= mysql_use_result(&stmt->dbc->mysql); else stmt->result= mysql_store_result(&stmt->dbc->mysql); if ( !stmt->result ) { if ( !mysql_field_count(&stmt->dbc->mysql) ) { error= SQL_SUCCESS; /* no result set */ stmt->state= ST_EXECUTED; stmt->affected_rows+= mysql_affected_rows(&stmt->dbc->mysql); goto exit; } set_error(stmt,MYERR_S1000,mysql_error(&stmt->dbc->mysql), mysql_errno(&stmt->dbc->mysql)); goto exit; } fix_result_types(stmt); error= SQL_SUCCESS; exit: pthread_mutex_unlock(&stmt->dbc->lock); if ( query != stmt->query ) x_free(query); /* If the original query was modified, we reset stmt->query so that the next execution re-starts with the original query. */ if (stmt->orig_query) { x_free(stmt->query); stmt->query= stmt->orig_query; stmt->query_end= stmt->orig_query_end; stmt->orig_query= NULL; } return error; } /* @type : myodbc3 internal @purpose : help function to enlarge buffer if necessary */ char *extend_buffer(NET *net, char *to, ulong length) { ulong need= 0; need= (ulong)(to - (char *)net->buff) + length; if (!to || need > net->max_packet - 10) { if (net_realloc(net, need)) { return 0; } to= (char *)net->buff + need - length; } return to; } /* @type : myodbc3 internal @purpose : help function to extend the buffer and copy the data */ char *add_to_buffer(NET *net,char *to,const char *from,ulong length) { if ( !(to= extend_buffer(net,to,length)) ) return 0; memcpy(to,from,length); return to+length; } /* @type : myodbc3 internal @purpose : insert sql params at parameter positions @param[in] stmt Statement @param[in] row Parameters row @param[in,out] finalquery if NULL, final query is not copied @param[in,out] length Length of the query. Pointed value is used as initial offset */ SQLRETURN insert_params(STMT FAR *stmt, SQLULEN row, char **finalquery, SQLULEN *finalquery_length) { char *query= stmt->query,*to; uint i,length; NET *net; SQLRETURN rc= SQL_SUCCESS; int mutex_was_locked= pthread_mutex_trylock(&stmt->dbc->lock); net= &stmt->dbc->mysql.net; to= (char*) net->buff + (finalquery_length!= NULL ? *finalquery_length : 0); if (!stmt->dbc->ds->dont_use_set_locale) setlocale(LC_NUMERIC, "C"); /* force use of '.' as decimal point */ for ( i= 0; i < stmt->param_count; ++i ) { DESCREC *aprec= desc_get_rec(stmt->apd, i, FALSE); DESCREC *iprec= desc_get_rec(stmt->ipd, i, FALSE); char *pos; assert(aprec && iprec); if (stmt->dummy_state != ST_DUMMY_PREPARED && !aprec->par.real_param_done) { rc= set_error(stmt,MYERR_07001,NULL,0); goto error; } get_dynamic(&stmt->param_pos, (void *)&pos, i); length= (uint) (pos-query); if ( !(to= add_to_buffer(net,to,query,length)) ) goto memerror; query= pos+1; /* Skip '?' */ if (!SQL_SUCCEEDED(rc= insert_param(stmt,&to,stmt->apd,aprec,iprec,row))) goto error; } length= (uint) (stmt->query_end - query); if ( !(to= add_to_buffer(net,to,query,length+1)) ) goto memerror; if (finalquery_length!= NULL) *finalquery_length= to - (char*)net->buff - 1; if (finalquery!=NULL) { if ( !(to= (char*) my_memdup((char*) net->buff, (uint) (to - (char*) net->buff),MYF(0))) ) goto memerror; } if (!mutex_was_locked) pthread_mutex_unlock(&stmt->dbc->lock); if (!stmt->dbc->ds->dont_use_set_locale) setlocale(LC_NUMERIC,default_locale); if (finalquery!=NULL) *finalquery= to; return rc; memerror: /* Too much data */ rc= set_error(stmt,MYERR_S1001,NULL,4001); error: /* ! was _already_ locked, when we tried to lock */ if (!mutex_was_locked) pthread_mutex_unlock(&stmt->dbc->lock); if (!stmt->dbc->ds->dont_use_set_locale) setlocale(LC_NUMERIC,default_locale); return rc; } /* Add the value of parameter to a string buffer. @param[in] mysql @param[in,out] toptr @param[in] apd The APD of the current statement @param[in] aprec The APD record of the parameter @param[in] iprec The IPD record of the parameter */ SQLRETURN insert_param(STMT *stmt, char **toptr, DESC* apd, DESCREC *aprec, DESCREC *iprec, SQLULEN row) { int length; char buff[128], *data= NULL; my_bool convert= FALSE, free_data= FALSE; DBC *dbc= stmt->dbc; NET *net= &dbc->mysql.net; SQLLEN *octet_length_ptr= NULL; SQLLEN *indicator_ptr= NULL; char *to= *toptr; if (aprec->octet_length_ptr) { octet_length_ptr= ptr_offset_adjust(aprec->octet_length_ptr, apd->bind_offset_ptr, apd->bind_type, sizeof(SQLLEN), row); length= *octet_length_ptr; } indicator_ptr= ptr_offset_adjust(aprec->indicator_ptr, apd->bind_offset_ptr, apd->bind_type, sizeof(SQLLEN), row); if (aprec->data_ptr) { SQLINTEGER default_size= bind_length(aprec->concise_type, aprec->octet_length); data= ptr_offset_adjust(aprec->data_ptr, apd->bind_offset_ptr, apd->bind_type, default_size, row); } if (indicator_ptr && *indicator_ptr == SQL_NULL_DATA) { *toptr= add_to_buffer(net,*toptr,"NULL",4); return SQL_SUCCESS; } /* According to http://msdn.microsoft.com/en-us/library/ms710963%28VS.85%29.aspx "... If StrLen_or_IndPtr is a null pointer, the driver assumes that all input parameter values are non-NULL and that character and *binary* data is null-terminated." */ else if (!octet_length_ptr || *octet_length_ptr == SQL_NTS) { if (data) { if (aprec->concise_type == SQL_C_WCHAR) length= sqlwcharlen((SQLWCHAR *)data) * sizeof(SQLWCHAR); else /* TODO this is stupid, check condition above, shouldn't we be checking only octet_length, not ptr? */ length= strlen(data); if (!octet_length_ptr && aprec->octet_length > 0 && aprec->octet_length != SQL_SETPARAM_VALUE_MAX) length= myodbc_min(length, aprec->octet_length); } else { length= 0; /* TODO? This is actually an error */ } } /* We may see SQL_COLUMN_IGNORE from bulk INSERT operations, where we may have been told to ignore a column in one particular row. So we try to insert DEFAULT, or NULL for really old servers. In case there are less parameters than result columns we have to insert NULL or DEFAULT. */ else if (*octet_length_ptr == SQL_COLUMN_IGNORE || /* empty values mean it's an unbound column */ (*octet_length_ptr == 0 && aprec->concise_type == SQL_C_DEFAULT && aprec->par.value == NULL)) { *toptr= add_to_buffer(net,*toptr,"DEFAULT",7); return SQL_SUCCESS; } else if (IS_DATA_AT_EXEC(octet_length_ptr)) { length= aprec->par.value_length; if ( !(data= aprec->par.value) ) { *toptr= add_to_buffer(net,*toptr,"NULL",4); return SQL_SUCCESS; } } switch ( aprec->concise_type ) { case SQL_C_BINARY: case SQL_C_CHAR: convert= 1; break; case SQL_C_WCHAR: { /* Convert SQL_C_WCHAR (utf-16 or utf-32) to utf-8. */ char *to; int i= 0; int utf8len, has_utf8_maxlen4= 0; /* length is in bytes, we want chars */ length= length / sizeof(SQLWCHAR); /* Use buff if it is big enough, otherwise alloc some space. */ if (sizeof(buff) >= (size_t)length * 4) to= buff; else { if (!(to= (char *)my_malloc(length * 4, MYF(0)))) goto memerror; free_data= TRUE; } if (sizeof(SQLWCHAR) == 4) { UTF32 *in= (UTF32 *)data; data= to; while (i < length) { to+= (utf8len= utf32toutf8(in[i++], (UTF8 *)to)); if (utf8len == 4) has_utf8_maxlen4= 1; } } else { UTF16 *in= (UTF16 *)data; data= to; while (i < length) { UTF32 c; i+= utf16toutf32(in + i, &c); to+= (utf8len= utf32toutf8(c, (UTF8 *)to)); if (utf8len == 4) has_utf8_maxlen4= 1; } } /* TODO need to check if it was merged to other versions already */ if (has_utf8_maxlen4 && !is_minimum_version(dbc->mysql.server_version, "6.0.4", 5)) return set_stmt_error(stmt, "HY000", "Server does not support 4-byte encoded " "UTF8 characters.", 0); length= to - data; break; } case SQL_C_BIT: case SQL_C_TINYINT: case SQL_C_STINYINT: length= my_int2str((long)*((signed char *)data),buff,-10,0) -buff; data= buff; break; case SQL_C_UTINYINT: length= my_int2str((long)*((unsigned char *)data),buff,-10,0) -buff; data= buff; break; case SQL_C_SHORT: case SQL_C_SSHORT: length= my_int2str((long)*((short int *)data),buff,-10,0) -buff; data= buff; break; case SQL_C_USHORT: length= my_int2str((long)*((unsigned short int *)data),buff,-10,0) -buff; data= buff; break; case SQL_C_LONG: case SQL_C_SLONG: length= my_int2str(*((SQLINTEGER*) data),buff,-10,0) -buff; data= buff; break; case SQL_C_ULONG: length= my_int2str(*((SQLUINTEGER*) data),buff,10,0) -buff; data= buff; break; case SQL_C_SBIGINT: length= longlong2str(*((longlong*) data),buff, -10) - buff; data= buff; break; case SQL_C_UBIGINT: length= longlong2str(*((ulonglong*) data),buff, 10) - buff; data= buff; break; case SQL_C_FLOAT: if ( iprec->concise_type != SQL_NUMERIC && iprec->concise_type != SQL_DECIMAL ) sprintf(buff,"%.17e",*((float*) data)); else /* We should perpare this data for string comparison */ sprintf(buff,"%.15e",*((float*) data)); length= strlen(data= buff); break; case SQL_C_DOUBLE: if ( iprec->concise_type != SQL_NUMERIC && iprec->concise_type != SQL_DECIMAL ) sprintf(buff,"%.17e",*((double*) data)); else /* We should perpare this data for string comparison */ sprintf(buff,"%.15e",*((double*) data)); length= strlen(data= buff); break; case SQL_C_DATE: case SQL_C_TYPE_DATE: { DATE_STRUCT *date= (DATE_STRUCT*) data; if (dbc->ds->min_date_to_zero && !date->year && (date->month == date->day == 1)) sprintf(buff, "0000-00-00"); else sprintf(buff, "%04d-%02d-%02d", date->year, date->month, date->day); data= buff; length= 10; break; } case SQL_C_TIME: case SQL_C_TYPE_TIME: { TIME_STRUCT *time= (TIME_STRUCT*) data; sprintf(buff, "%02d:%02d:%02d", time->hour, time->minute, time->second); data= buff; length= 8; break; } case SQL_C_TIMESTAMP: case SQL_C_TYPE_TIMESTAMP: { TIMESTAMP_STRUCT *time= (TIMESTAMP_STRUCT*) data; if (dbc->ds->min_date_to_zero && !time->year && (time->month == time->day == 1)) sprintf(buff, "0000-00-00 %02d:%02d:%02d", time->hour, time->minute, time->second); else sprintf(buff, "%04d-%02d-%02d %02d:%02d:%02d", time->year, time->month, time->day, time->hour, time->minute, time->second); data= buff; length= 19; break; } case SQL_C_NUMERIC: { int trunc; SQL_NUMERIC_STRUCT *sqlnum= (SQL_NUMERIC_STRUCT *) data; sqlnum_to_str(sqlnum, (SQLCHAR *)(buff + sizeof(buff) - 1), (SQLCHAR **) &data, (SQLCHAR) iprec->precision, (SQLSCHAR) iprec->scale, &trunc); length= strlen(data); /* TODO no way to return an error here? */ if (trunc == SQLNUM_TRUNC_FRAC) {/* 01S07 SQL_SUCCESS_WITH_INFO */} else if (trunc == SQLNUM_TRUNC_WHOLE) {/* 22003 SQL_ERROR */ return SQL_ERROR; } } } switch ( iprec->concise_type ) { case SQL_DATE: case SQL_TYPE_DATE: case SQL_TYPE_TIMESTAMP: case SQL_TIMESTAMP: if (data[0] == '{') /* Of type {d date } */ { to= add_to_buffer(net, to, data, length); goto out; } /* else _binary introducer for binary data */ case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: { if (dbc->cxn_charset_info->number != dbc->ansi_charset_info->number) { to= add_to_buffer(net, to, "_binary", 7); } /* We have only added the introducer, data is added below. */ break; } /* else treat as a string */ case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: { if (aprec->concise_type == SQL_C_WCHAR && dbc->cxn_charset_info->number != UTF8_CHARSET_NUMBER) to= add_to_buffer(net, to, "_utf8", 5); else if (aprec->concise_type != SQL_C_WCHAR && dbc->cxn_charset_info->number != dbc->ansi_charset_info->number) { to= add_to_buffer(net, to, "_", 1); to= add_to_buffer(net, to, dbc->ansi_charset_info->csname, strlen(dbc->ansi_charset_info->csname)); } /* We have only added the introducer, data is added below. */ break; } case SQL_TIME: case SQL_TYPE_TIME: if ( aprec->concise_type == SQL_C_TIMESTAMP || aprec->concise_type == SQL_C_TYPE_TIMESTAMP ) { TIMESTAMP_STRUCT *time= (TIMESTAMP_STRUCT*) aprec->data_ptr; sprintf(buff,"'%02d:%02d:%02d'",time->hour,time->minute,time->second); to= add_to_buffer(net, to, buff, 10); } else { ulong time= str_to_time_as_long(data,length); sprintf(buff,"'%02d:%02d:%02d'", (int) time/10000, (int) time/100%100, (int) time%100); to= add_to_buffer(net, to, buff, 10); } goto out; case SQL_FLOAT: case SQL_REAL: case SQL_DOUBLE: /* If we have string -> float ; Fix locale characters for number */ if ( convert ) { char *to= buff, *from= data; char *end= from+length; while ( *from && from < end ) { if ( from[0] == thousands_sep[0] && is_prefix(from,thousands_sep) ) from+= thousands_sep_length; else if ( from[0] == decimal_point[0] && is_prefix(from,decimal_point) ) { from+= decimal_point_length; *to++='.'; } else *to++= *from++; } if ( to == buff ) *to++='0'; /* Fix for empty strings */ data= buff; length= (uint) (to-buff); convert= 0; } /* Fall through */ default: if (!convert) { to= add_to_buffer(net, to, data, length); goto out; } } /* Convert binary data to hex sequence */ if(is_no_backslashes_escape_mode(stmt->dbc) && is_binary_sql_type(iprec->concise_type)) { SQLLEN transformed_len = 0; to= add_to_buffer(net,to," 0x",3); /* Make sure we have room for a fully-escaped string. */ if (!(to= extend_buffer(net, to, length * 2))) return 0; copy_binhex_result(stmt, to, length * 2 + 1, &transformed_len, 0, data, length); to += transformed_len; } else { to= add_to_buffer(net,to,"'",1); /* Make sure we have room for a fully-escaped string. */ if (!(to= extend_buffer(net, to, length * 2))) return 0; to+= mysql_real_escape_string(&dbc->mysql, to, data, length); to= add_to_buffer(net, to, "'", 1); } out: if (free_data) { x_free(data); } *toptr= to; return SQL_SUCCESS; memerror: return set_error(stmt, MYERR_S1001, NULL, 4001); } /* @type : myodbc3 internal @purpose : positioned cursor update/delete */ SQLRETURN do_my_pos_cursor( STMT FAR *pStmt, STMT FAR *pStmtCursor ) { char * pszQuery = pStmt->query; DYNAMIC_STRING dynQuery; SQLRETURN nReturn; if ( pStmt->error.native_error == ER_INVALID_CURSOR_NAME ) { return set_stmt_error( pStmt, "HY000", "ER_INVALID_CURSOR_NAME", 0 ); } while ( isspace( *pszQuery ) ) ++pszQuery; if ( init_dynamic_string( &dynQuery, pszQuery, 1024, 1024 ) ) return set_error( pStmt, MYERR_S1001, NULL, 4001 ); if ( !myodbc_casecmp( pszQuery, "delete", 6 ) ) { nReturn = my_pos_delete( pStmtCursor, pStmt, 1, &dynQuery ); } else if ( !myodbc_casecmp( pszQuery, "update", 6 ) ) { nReturn = my_pos_update( pStmtCursor, pStmt, 1, &dynQuery ); } else { nReturn = set_error( pStmt, MYERR_S1000, "Specified SQL syntax is not supported", 0 ); } if ( SQL_SUCCEEDED( nReturn ) ) pStmt->state = ST_EXECUTED; dynstr_free( &dynQuery ); return( nReturn ); } /* @type : ODBC 1.0 API @purpose : executes a prepared statement, using the current values of the parameter marker variables if any parameter markers exist in the statement */ SQLRETURN SQL_API SQLExecute(SQLHSTMT hstmt) { return my_SQLExecute((STMT FAR*)hstmt); } BOOL map_error_to_param_status( SQLUSMALLINT *param_status_ptr, SQLRETURN rc) { if (param_status_ptr) { switch (rc) { case SQL_SUCCESS: *param_status_ptr= SQL_PARAM_SUCCESS; break; case SQL_SUCCESS_WITH_INFO: *param_status_ptr= SQL_PARAM_SUCCESS_WITH_INFO; break; default: /* SQL_PARAM_ERROR is set at the end of processing for last erroneous paramset so we have diagnostics for it */ *param_status_ptr= SQL_PARAM_DIAG_UNAVAILABLE; return TRUE; } } return FALSE; } /* @type : myodbc3 internal @purpose : executes a prepared statement, using the current values of the parameter marker variables if any parameter markers exist in the statement */ SQLRETURN my_SQLExecute( STMT FAR *pStmt ) { char *query, *cursor_pos; int dae_rec, is_select_stmt, one_of_params_not_succeded= 0; int connection_failure= 0; STMT FAR * pStmtCursor = pStmt; SQLRETURN rc; SQLULEN row, length= 0; SQLUSMALLINT *param_operation_ptr= NULL, *param_status_ptr= NULL, *lastError= NULL; /* need to have a flag indicating if all parameters failed */ int all_parameters_failed= pStmt->apd->array_size > 1 ? 1 : 0; if ( !pStmt ) return SQL_ERROR; CLEAR_STMT_ERROR( pStmt ); if ( !pStmt->query ) return set_error(pStmt, MYERR_S1010, "No previous SQLPrepare done", 0); if (is_set_names_statement((SQLCHAR *)pStmt->query)) return set_error(pStmt, MYERR_42000, "SET NAMES not allowed by driver", 0); if ( (cursor_pos= check_if_positioned_cursor_exists(pStmt, &pStmtCursor)) ) { /* Save a copy of the query, because we're about to modify it. */ pStmt->orig_query= my_strdup(pStmt->query, MYF(0)); if (!pStmt->orig_query) { return set_error(pStmt,MYERR_S1001,NULL,4001); } pStmt->orig_query_end= pStmt->orig_query + (pStmt->query_end - pStmt->query); /* Chop off the 'WHERE CURRENT OF ...' */ *(char *)cursor_pos= '\0'; return do_my_pos_cursor(pStmt, pStmtCursor); } my_SQLFreeStmt((SQLHSTMT)pStmt,MYSQL_RESET_BUFFERS); query= pStmt->query; is_select_stmt= is_select_statement((SQLCHAR *)query); if ( pStmt->ipd->rows_processed_ptr ) *pStmt->ipd->rows_processed_ptr= 0; /* Locking if we have params array for "SELECT" statemnt */ /* if param_count is zero, the rest probably are artifacts(not reset attributes) from a previously executed statement. besides this lock is not needed when there are no params*/ if (pStmt->param_count && pStmt->apd->array_size > 1 && is_select_stmt) pthread_mutex_lock(&pStmt->dbc->lock); for (row= 0; row < pStmt->apd->array_size; ++row) { if ( pStmt->param_count ) { /* "The SQL_DESC_ROWS_PROCESSED_PTR field of the APD points to a buffer that contains the number of sets of parameters that have been processed, including error sets." "If SQL_NEED_DATA is returned, the value pointed to by the SQL_DESC_ROWS_PROCESSED_PTR field of the APD is set to the set of parameters that is being processed". And actually driver may continue to process paramsets after error. We need to decide do we want that. (http://msdn.microsoft.com/en-us/library/ms710963%28VS.85%29.aspx see "Using Arrays of Parameters") */ if ( pStmt->ipd->rows_processed_ptr ) *pStmt->ipd->rows_processed_ptr+= 1; param_operation_ptr= ptr_offset_adjust(pStmt->apd->array_status_ptr, NULL, 0/*SQL_BIND_BY_COLUMN*/, sizeof(SQLUSMALLINT), row); param_status_ptr= ptr_offset_adjust(pStmt->ipd->array_status_ptr, NULL, 0/*SQL_BIND_BY_COLUMN*/, sizeof(SQLUSMALLINT), row); if ( param_operation_ptr && *param_operation_ptr == SQL_PARAM_IGNORE) { /* http://msdn.microsoft.com/en-us/library/ms712631%28VS.85%29.aspx - comments for SQL_ATTR_PARAM_STATUS_PTR */ if (param_status_ptr) *param_status_ptr= SQL_PARAM_UNUSED; /* If this is last paramset - we will miss unlock */ if (pStmt->apd->array_size > 1 && is_select_stmt && row == pStmt->apd->array_size - 1) pthread_mutex_unlock(&pStmt->dbc->lock); continue; } /* * If any parameters are required at execution time, cannot perform the * statement. It will be done through SQLPutData() and SQLParamData(). */ if ((dae_rec= desc_find_dae_rec(pStmt->apd)) > -1) { if (pStmt->apd->array_size > 1) { rc= set_stmt_error(pStmt, "HYC00", "Parameter arrays " "with data at execution are not supported", 0); lastError= param_status_ptr; /* unlocking since we do break*/ if (is_select_stmt) pthread_mutex_unlock(&pStmt->dbc->lock); one_of_params_not_succeded= 1; /* For other errors we continue processing of paramsets So this creates some inconsistency. But I guess that's better that user see diagnostics for this type of error */ break; } pStmt->current_param= dae_rec; pStmt->dae_type= DAE_NORMAL; return SQL_NEED_DATA; } /* Making copy of the built query if that is not last paramset for select query. */ if (is_select_stmt && row < pStmt->apd->array_size - 1) rc= insert_params(pStmt, row, NULL, &length); else rc= insert_params(pStmt, row, &query, &length); /* Setting status for this paramset*/ if (map_error_to_param_status( param_status_ptr, rc)) lastError= param_status_ptr; if (rc != SQL_SUCCESS) { one_of_params_not_succeded= 1; } if (!SQL_SUCCEEDED(rc)) { if (pStmt->apd->array_size > 1 && is_select_stmt && row == pStmt->apd->array_size - 1) pthread_mutex_unlock(&pStmt->dbc->lock); continue/*return rc*/; } /* For "SELECT" statement constructing single statement using "UNION ALL" */ if (pStmt->apd->array_size > 1 && is_select_stmt) { if (row < pStmt->apd->array_size - 1) { const char * stmtsBinder= " UNION ALL "; const ulong binderLength= strlen(stmtsBinder); add_to_buffer(&pStmt->dbc->mysql.net, (char*)pStmt->dbc->mysql.net.buff + length, stmtsBinder, binderLength); length+= binderLength; } else /* last select statement has been constructed - so releasing lock*/ pthread_mutex_unlock(&pStmt->dbc->lock); } } if (!is_select_stmt || row == pStmt->apd->array_size-1) { if (!connection_failure) { rc= do_query(pStmt, query, length); } else { /* with broken connection we always return error for all next queries */ rc= SQL_ERROR; } if (is_connection_lost(pStmt->error.native_error) && handle_connection_error(pStmt)) { connection_failure= 1; } if (map_error_to_param_status(param_status_ptr, rc)) { lastError= param_status_ptr; } /* if we have anything but not SQL_SUCCESS for any paramset, we return SQL_SUCCESS_WITH_INFO as the whole operation result */ if (rc != SQL_SUCCESS) { one_of_params_not_succeded= 1; } else { all_parameters_failed= 0; } length= 0; } } /* Changing status for last detected error to SQL_PARAM_ERROR as we have diagnostics for it */ if (lastError != NULL) *lastError= SQL_PARAM_ERROR; /* Setting not processed paramsets status to SQL_PARAM_UNUSED this is needed if we stop paramsets processing on error. */ if (param_status_ptr != NULL) { while (++row < pStmt->apd->array_size) { param_status_ptr= ptr_offset_adjust(pStmt->ipd->array_status_ptr, NULL, 0/*SQL_BIND_BY_COLUMN*/, sizeof(SQLUSMALLINT), row); *param_status_ptr= SQL_PARAM_UNUSED; } } if (pStmt->dummy_state == ST_DUMMY_PREPARED) pStmt->dummy_state= ST_DUMMY_EXECUTED; if (pStmt->apd->array_size > 1) { if (all_parameters_failed) { return SQL_ERROR; } else if (one_of_params_not_succeded != 0) { return SQL_SUCCESS_WITH_INFO; } } return rc; } /* @type : ODBC 1.0 API @purpose : is used in conjunction with SQLPutData to supply parameter data at statement execution time */ SQLRETURN SQL_API SQLParamData(SQLHSTMT hstmt, SQLPOINTER FAR *prbgValue) { STMT FAR *stmt= (STMT FAR*) hstmt; uint i; SQLRETURN rc; char *query; DESC *apd; uint param_count= stmt->param_count; assert(stmt->dae_type); /* get the correct APD for the dae type we're handling */ switch(stmt->dae_type) { case DAE_NORMAL: apd= stmt->apd; break; case DAE_SETPOS_INSERT: case DAE_SETPOS_UPDATE: apd= stmt->setpos_apd; param_count= stmt->ard->count; break; default: return set_stmt_error(stmt, "HY010", "Invalid data at exec state", 0); } for ( i= stmt->current_param; i < param_count; ++i ) { DESCREC *aprec= desc_get_rec(apd, i, FALSE); SQLLEN *octet_length_ptr; assert(aprec); octet_length_ptr= ptr_offset_adjust(aprec->octet_length_ptr, apd->bind_offset_ptr, apd->bind_type, sizeof(SQLLEN), 0); /* get the "placeholder" pointer the application bound */ if (IS_DATA_AT_EXEC(octet_length_ptr)) { SQLINTEGER default_size= bind_length(aprec->concise_type, aprec->octet_length); stmt->current_param= i+1; if ( prbgValue ) *prbgValue= ptr_offset_adjust(aprec->data_ptr, apd->bind_offset_ptr, apd->bind_type, default_size, 0); aprec->par.value= NULL; aprec->par.alloced= FALSE; aprec->par.is_dae= 1; return SQL_NEED_DATA; } } /* all data-at-exec params are complete. continue execution */ switch(stmt->dae_type) { case DAE_NORMAL: if (!SQL_SUCCEEDED(rc= insert_params(stmt, 0, &query, 0))) break; rc= do_query(stmt, query, 0); break; case DAE_SETPOS_INSERT: stmt->dae_type= DAE_SETPOS_DONE; rc= my_SQLSetPos(hstmt, stmt->setpos_row, SQL_ADD, stmt->setpos_lock); desc_free(stmt->setpos_apd); stmt->setpos_apd= NULL; break; case DAE_SETPOS_UPDATE: stmt->dae_type= DAE_SETPOS_DONE; rc= my_SQLSetPos(hstmt, stmt->setpos_row, SQL_UPDATE, stmt->setpos_lock); desc_free(stmt->setpos_apd); stmt->setpos_apd= NULL; break; } stmt->dae_type= 0; return rc; } /* @type : ODBC 1.0 API @purpose : allows an application to send data for a parameter or column to the driver at statement execution time. This function can be used to send character or binary data values in parts to a column with a character, binary, or data source specific data type. */ SQLRETURN SQL_API SQLPutData( SQLHSTMT hstmt, SQLPOINTER rgbValue, SQLLEN cbValue ) { STMT FAR *stmt= (STMT FAR*) hstmt; DESCREC *aprec; if ( !stmt ) return SQL_ERROR; if ( cbValue == SQL_NTS ) cbValue= strlen(rgbValue); if (stmt->dae_type == DAE_NORMAL) aprec= desc_get_rec(stmt->apd, stmt->current_param - 1, FALSE); else aprec= desc_get_rec(stmt->setpos_apd, stmt->current_param - 1, FALSE); assert(aprec); if ( cbValue == SQL_NULL_DATA ) { if ( aprec->par.alloced ) { x_free(aprec->par.value); } aprec->par.alloced= FALSE; aprec->par.value= NULL; return SQL_SUCCESS; } if ( aprec->par.value ) { /* Append to old value */ assert(aprec->par.alloced); if ( !(aprec->par.value= my_realloc(aprec->par.value, aprec->par.value_length + cbValue + 1, MYF(0))) ) return set_error(stmt,MYERR_S1001,NULL,4001); memcpy(aprec->par.value+aprec->par.value_length,rgbValue,cbValue); aprec->par.value_length+= cbValue; aprec->par.value[aprec->par.value_length]= 0; aprec->par.alloced= TRUE; } else { /* New value */ if ( !(aprec->par.value= my_malloc(cbValue+1,MYF(0))) ) return set_error(stmt,MYERR_S1001,NULL,4001); memcpy(aprec->par.value,rgbValue,cbValue); aprec->par.value_length= cbValue; aprec->par.value[aprec->par.value_length]= 0; aprec->par.alloced= TRUE; } return SQL_SUCCESS; } /** Cancel the query by opening another connection and using KILL when called from another thread while the query lock is being held. Otherwise, treat as SQLFreeStmt(hstmt, SQL_CLOSE). @param[in] hstmt Statement handle @return Standard ODBC result code */ SQLRETURN SQL_API SQLCancel(SQLHSTMT hstmt) { DBC *dbc= ((STMT *)hstmt)->dbc; MYSQL *second= NULL; int error; error= pthread_mutex_trylock(&dbc->lock); /* If there's no query going on, just close the statement. */ if (error == 0) { pthread_mutex_unlock(&dbc->lock); return my_SQLFreeStmt(hstmt, SQL_CLOSE); } /* If we got a non-BUSY error, it's just an error. */ if (error != EBUSY) return set_stmt_error((STMT *)hstmt, "HY000", "Unable to get connection mutex status", error); /* If the mutex was locked, we need to make a new connection and KILL the ongoing query. */ second= mysql_init(second); /** @todo need to preserve and use ssl params */ if (!mysql_real_connect(second, dbc->ds->server8, dbc->ds->uid8, dbc->ds->pwd8, NULL, dbc->ds->port, dbc->ds->socket8, 0)) { /* We do not set the SQLSTATE here, per the ODBC spec. */ return SQL_ERROR; } { char buff[40]; /* buff is always big enough because max length of %lu is 15 */ sprintf(buff, "KILL /*!50000 QUERY */ %lu", mysql_thread_id(&dbc->mysql)); if (mysql_real_query(second, buff, strlen(buff))) { mysql_close(second); /* We do not set the SQLSTATE here, per the ODBC spec. */ return SQL_ERROR; } } mysql_close(second); return SQL_SUCCESS; } mysql-connector-odbc-5.1.10-src/driver/driver.def100644 15766 12 6103 11707541005 20434 0ustar00cteamstaff;/* ; Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved ; ; The MySQL Connector/ODBC is licensed under the terms of the GPLv2 ; , like most ; MySQL Connectors. There are special exceptions to the terms and ; conditions of the GPLv2 as it is applied to this software, see the ; FLOSS License Exception ; . ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published ; by the Free Software Foundation; version 2 of the License. ; ; This program is distributed in the hope that it will be useful, but ; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ; for more details. ; ; You should have received a copy of the GNU General Public License along ; with this program; if not, write to the Free Software Foundation, Inc., ; 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ;*/ ; LIBRARY MYODBC5.DLL VERSION 05.01.0010 ;DESCRIPTION "MySQL ODBC 5.1 Driver, Copyright (c) 1995, 2011, Oracle and/or its affiliates" ;CODE MOVEABLE DISCARDABLE ;DATA MOVEABLE MULTIPLE ;PROTMODE ;SEGMENTS ;DLL_TEXT PRELOAD ;INIT_TEXT PRELOAD ;DATA PRELOAD ;HEAPSIZE 10000 ; ; Export definitions EXPORTS ; SQLAllocConnect SQLAllocEnv SQLAllocStmt SQLAllocHandle SQLBindCol SQLCancel SQLColAttributes SQLConnect SQLCopyDesc SQLDescribeCol SQLDisconnect SQLError SQLExecDirect SQLExecute SQLFetch SQLFreeConnect SQLFreeEnv SQLFreeStmt SQLFreeHandle SQLGetCursorName SQLNumResultCols SQLPrepare SQLRowCount SQLSetCursorName SQLTransact SQLColumns SQLDriverConnect SQLGetEnvAttr SQLGetConnectAttr SQLGetConnectOption SQLGetData SQLGetFunctions SQLGetInfo SQLGetStmtAttr SQLGetStmtOption SQLGetTypeInfo SQLParamData SQLPutData SQLSetEnvAttr SQLSetConnectAttr SQLSetConnectOption SQLSetStmtAttr SQLSetStmtOption SQLSpecialColumns SQLStatistics SQLTables SQLBrowseConnect SQLColumnPrivileges SQLDescribeParam SQLExtendedFetch SQLForeignKeys SQLMoreResults SQLNativeSql SQLNumParams SQLParamOptions SQLPrimaryKeys SQLProcedureColumns SQLProcedures SQLSetPos SQLSetScrollOptions SQLTablePrivileges SQLBindParameter SQLGetDiagRec SQLGetDiagField SQLEndTran SQLFetchScroll SQLColAttribute SQLBulkOperations SQLSetDescField SQLSetDescRec SQLGetDescField SQLGetDescRec SQLColAttributeW SQLColAttributesW SQLColumnPrivilegesW SQLColumnsW SQLCloseCursor SQLSetParam SQLConnectW SQLDescribeColW SQLDriverConnectW SQLErrorW SQLExecDirectW SQLForeignKeysW SQLGetConnectAttrW SQLGetConnectOptionW SQLGetCursorNameW SQLGetDescFieldW SQLGetDescRecW SQLGetDiagFieldW SQLGetDiagRecW SQLGetInfoW SQLGetStmtAttrW SQLGetTypeInfoW SQLNativeSqlW SQLPrepareW SQLPrimaryKeysW SQLProcedureColumnsW SQLProceduresW SQLSetConnectAttrW SQLSetConnectOptionW SQLSetCursorNameW SQLSetDescFieldW SQLSetDescRecW SQLSetStmtAttrW SQLSpecialColumnsW SQLStatisticsW SQLTablePrivilegesW SQLTablesW ; DllMain LoadByOrdinal ;_DllMainCRTStartup ; mysql-connector-odbc-5.1.10-src/driver/catalog_no_i_s.c100644 15766 12 211504 11707541005 21630 0ustar00cteamstaff/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file catalog_no_i_s.c @brief Catalog functions not using I_S. @remark All functions suppose that parameters specifying other parameters lenthes can't SQL_NTS. caller should take care of that. */ #include "driver.h" #include "catalog.h" /* @type : internal @purpose : validate for give table type from the list */ static my_bool check_table_type(const SQLCHAR *TableType, const char *req_type, uint len) { char req_type_quoted[NAME_LEN+2], req_type_quoted1[NAME_LEN+2]; char *type, *table_type= (char *)TableType; my_bool found= 0; if ( !TableType || !TableType[0] ) return found; /* Check and return only 'user' tables from current DB and don't return any tables when its called with SYSTEM TABLES. Expected Types: "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM", */ type= strstr(table_type,","); sprintf(req_type_quoted,"'%s'",req_type); sprintf(req_type_quoted1,"`%s`",req_type); while ( type ) { while ( isspace(*(table_type)) ) ++table_type; if ( !myodbc_casecmp(table_type,req_type,len) || !myodbc_casecmp(table_type,req_type_quoted,len+2) || !myodbc_casecmp(table_type,req_type_quoted1,len+2) ) { found= 1; break; } table_type= ++type; type= strstr(table_type,","); } if ( !found ) { while ( isspace(*(table_type)) ) ++table_type; if ( !myodbc_casecmp(table_type,req_type,len) || !myodbc_casecmp(table_type,req_type_quoted,len+2) || !myodbc_casecmp(table_type,req_type_quoted1,len+2) ) found= 1; } return found; } static MYSQL_ROW fix_fields_copy(STMT FAR *stmt,MYSQL_ROW row) { uint i; for ( i=0 ; i < stmt->order_count; ++i ) stmt->array[stmt->order[i]]= row[i]; return stmt->array; } /* @type : internal @purpose : returns columns from a particular table, NULL on error */ static MYSQL_RES *mysql_list_dbkeys(DBC *dbc, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *table, SQLSMALLINT table_len) { MYSQL *mysql= &dbc->mysql; char buff[255], *to; to= strmov(buff, "SHOW KEYS FROM `"); if (catalog_len) { to+= myodbc_escape_string(mysql, to, (ulong)(sizeof(buff) - (to - buff)), (char *)catalog, catalog_len, 1); to= strmov(to, "`.`"); } to+= myodbc_escape_string(mysql, to, (ulong)(sizeof(buff) - (to - buff)), (char *)table, table_len, 1); to= strmov(to, "`"); MYLOG_DBC_QUERY(dbc, buff); if (mysql_query(mysql,buff)) return NULL; return mysql_store_result(mysql); } /* **************************************************************************** SQLColumns **************************************************************************** */ char SC_type[10],SC_typename[20],SC_precision[10],SC_length[10],SC_scale[10], SC_nullable[10], SC_coldef[10], SC_sqltype[10],SC_octlen[10], SC_pos[10],SC_isnull[10]; char *SQLCOLUMNS_values[]= { "","",NullS,NullS,SC_type,SC_typename, SC_precision, SC_length,SC_scale,"10",SC_nullable,"MySQL column", SC_coldef,SC_sqltype,NullS,SC_octlen,NullS,SC_isnull }; MYSQL_FIELD SQLCOLUMNS_fields[]= { MYODBC_FIELD_NAME("TABLE_CAT", 0), MYODBC_FIELD_NAME("TABLE_SCHEM", 0), MYODBC_FIELD_NAME("TABLE_NAME", NOT_NULL_FLAG), MYODBC_FIELD_NAME("COLUMN_NAME", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("DATA_TYPE", NOT_NULL_FLAG), MYODBC_FIELD_STRING("TYPE_NAME", 20, NOT_NULL_FLAG), MYODBC_FIELD_LONG("COLUMN_SIZE", 0), MYODBC_FIELD_LONG("BUFFER_LENGTH", 0), MYODBC_FIELD_SHORT("DECIMAL_DIGITS", 0), MYODBC_FIELD_SHORT("NUM_PREC_RADIX", 0), MYODBC_FIELD_SHORT("NULLABLE", NOT_NULL_FLAG), MYODBC_FIELD_NAME("REMARKS", 0), MYODBC_FIELD_NAME("COLUMN_DEF", 0), MYODBC_FIELD_SHORT("SQL_DATA_TYPE", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("SQL_DATETIME_SUB", 0), MYODBC_FIELD_LONG("CHAR_OCTET_LENGTH", 0), MYODBC_FIELD_LONG("ORDINAL_POSITION", NOT_NULL_FLAG), MYODBC_FIELD_STRING("IS_NULLABLE", 3, 0), }; const uint SQLCOLUMNS_FIELDS= array_elements(SQLCOLUMNS_values); /** Get the list of columns in a table matching a wildcard. @param[in] stmt Statement @param[in] szCatalog Name of catalog (database) @param[in] cbCatalog Length of catalog @param[in] szTable Name of table @param[in] cbTable Length of table @param[in] szColumn Pattern of column names to match @param[in] cbColumn Length of column pattern @return Result of mysql_list_fields, or NULL if there is an error */ static MYSQL_RES * mysql_list_dbcolumns(STMT *stmt, SQLCHAR *szCatalog, SQLSMALLINT cbCatalog, SQLCHAR *szTable, SQLSMALLINT cbTable, SQLCHAR *szColumn, SQLSMALLINT cbColumn) { DBC *dbc= stmt->dbc; MYSQL *mysql= &dbc->mysql; MYSQL_RES *result; char buff[256], column_buff[64*2+1]; /* If a catalog was specified, we have to change working catalog to be able to use mysql_list_fields. */ if (cbCatalog) { if (reget_current_catalog(dbc)) return NULL; /* reget_current_catalog locks and release mutex, so locking here again */ pthread_mutex_lock(&dbc->lock); strncpy(buff, szCatalog, cbCatalog); buff[cbCatalog]= '\0'; if (mysql_select_db(mysql, buff)) { pthread_mutex_unlock(&dbc->lock); return NULL; } } else pthread_mutex_lock(&dbc->lock); strncpy(buff, szTable, cbTable); buff[cbTable]= '\0'; strncpy(column_buff, szColumn, cbColumn); column_buff[cbColumn]= '\0'; result= mysql_list_fields(mysql, buff, column_buff); /* If before this call no database were selected - we cannot revert that */ if (cbCatalog && dbc->database) { if (mysql_select_db( mysql, dbc->database)) { /* Well, probably have to return error here */ mysql_free_result(result); pthread_mutex_unlock(&dbc->lock); return NULL; } } pthread_mutex_unlock(&dbc->lock); return result; } /** Get information about the columns in one or more tables. @param[in] hstmt Handle of statement @param[in] szCatalog Name of catalog (database) @param[in] cbCatalog Length of catalog @param[in] szSchema Name of schema (unused) @param[in] cbSchema Length of schema name @param[in] szTable Pattern of table names to match @param[in] cbTable Length of table pattern @param[in] szColumn Pattern of column names to match @param[in] cbColumn Length of column pattern */ SQLRETURN mysql_columns(STMT * stmt, SQLCHAR *szCatalog, SQLSMALLINT cbCatalog, SQLCHAR *szSchema __attribute__((unused)), SQLSMALLINT cbSchema __attribute__((unused)), SQLCHAR *szTable, SQLSMALLINT cbTable, SQLCHAR *szColumn, SQLSMALLINT cbColumn) { MYSQL_RES *res; MEM_ROOT *alloc; MYSQL_ROW table_row; unsigned long rows= 0, next_row= 0, *lengths; char *db= NULL; BOOL is_access= FALSE; /* Get the list of tables that match szCatalog and szTable */ pthread_mutex_lock(&stmt->dbc->lock); res= mysql_table_status(stmt, szCatalog, cbCatalog, szTable, cbTable, TRUE, TRUE, TRUE); if (!res && mysql_errno(&stmt->dbc->mysql)) { SQLRETURN rc= handle_connection_error(stmt); pthread_mutex_unlock(&stmt->dbc->lock); return rc; } else if (!res) { pthread_mutex_unlock(&stmt->dbc->lock); goto empty_set; } pthread_mutex_unlock(&stmt->dbc->lock); #ifdef _WIN32 if (GetModuleHandle("msaccess.exe") != NULL) is_access= TRUE; #endif stmt->result= res; alloc= &res->field_alloc; if (!stmt->dbc->ds->no_catalog) db= strmake_root(alloc, (char *)szCatalog, cbCatalog); while ((table_row= mysql_fetch_row(res))) { MYSQL_FIELD *field; MYSQL_RES *table_res; int count= 0; /* Get list of columns matching szColumn for each table. */ lengths= mysql_fetch_lengths(res); table_res= mysql_list_dbcolumns(stmt, szCatalog, cbCatalog, (SQLCHAR *)table_row[0], (SQLSMALLINT)lengths[0], szColumn, cbColumn); if (!table_res) { return handle_connection_error(stmt); } rows+= mysql_num_fields(table_res); stmt->result_array= (char **)my_realloc((char *)stmt->result_array, sizeof(char *) * SQLCOLUMNS_FIELDS * rows, MYF(MY_ALLOW_ZERO_PTR)); if (!stmt->result_array) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } while ((field= mysql_fetch_field(table_res))) { SQLSMALLINT type; char buff[255]; /* @todo justify the size of this buffer */ MYSQL_ROW row= stmt->result_array + (SQLCOLUMNS_FIELDS * next_row++); row[0]= db; /* TABLE_CAT */ row[1]= NULL; /* TABLE_SCHEM */ row[2]= strdup_root(alloc, field->table); /* TABLE_NAME */ row[3]= strdup_root(alloc, field->name); /* COLUMN_NAME */ type= get_sql_data_type(stmt, field, buff); row[5]= strdup_root(alloc, buff); /* TYPE_NAME */ sprintf(buff, "%d", type); row[4]= strdup_root(alloc, buff); /* DATA_TYPE */ if (type == SQL_TYPE_DATE || type == SQL_TYPE_TIME || type == SQL_TYPE_TIMESTAMP) { row[14]= row[4]; /* SQL_DATETIME_SUB */ sprintf(buff, "%d", SQL_DATETIME); row[13]= strdup_root(alloc, buff); /* SQL_DATA_TYPE */ } else { row[13]= row[4]; /* SQL_DATA_TYPE */ row[14]= NULL; /* SQL_DATETIME_SUB */ } /* COLUMN_SIZE */ fill_column_size_buff(buff, stmt, field); row[6]= strdup_root(alloc, buff); /* BUFFER_LENGTH */ sprintf(buff, "%ld", get_transfer_octet_length(stmt, field)); row[7]= strdup_root(alloc, buff); if (is_char_sql_type(type) || is_wchar_sql_type(type) || is_binary_sql_type(type)) { row[15]= strdup_root(alloc, buff); /* CHAR_OCTET_LENGTH */ } else { row[15]= NULL; /* CHAR_OCTET_LENGTH */ } { SQLSMALLINT digits= get_decimal_digits(stmt, field); if (digits != SQL_NO_TOTAL) { sprintf(buff, "%d", digits); row[8]= strdup_root(alloc, buff); /* DECIMAL_DIGITS */ row[9]= "10"; /* NUM_PREC_RADIX */ } else { row[8]= row[9]= NullS; /* DECIMAL_DIGITS, NUM_PREC_RADIX */ } } /* If a field is a TIMESTAMP, NULL can be stored to it (although it gets turned into something else). The same logic applies to fields with AUTO_INCREMENT_FLAG set. */ if ((field->flags & NOT_NULL_FLAG) && !(field->type == MYSQL_TYPE_TIMESTAMP) && !(field->flags & AUTO_INCREMENT_FLAG)) { /* Bug#31067. Access seems to try to put NULL value when not null field is cleared. And that contradicts with its knowledge of that the field is not nullable, and it yields an error. Here is a little trick for such case - we don't tell Access the whole truth we know, and return for such field SQL_NULLABLE_UNKNOWN instead*/ if (is_access) { sprintf(buff, "%d", SQL_NULLABLE_UNKNOWN); row[10]= strdup_root(alloc, buff); /* NULLABLE */ row[17]= strdup_root(alloc, "NO");/* IS_NULLABLE */ } else { sprintf(buff, "%d", SQL_NO_NULLS); row[10]= strdup_root(alloc, buff); /* NULLABLE */ row[17]= strdup_root(alloc, "NO"); /* IS_NULLABLE */ } } else { sprintf(buff, "%d", SQL_NULLABLE); row[10]= strdup_root(alloc, buff); /* NULLABLE */ row[17]= strdup_root(alloc, "YES");/* IS_NULLABLE */ } row[11]= ""; /* REMARKS */ /* The default value of the column. The value in this column should be interpreted as a string if it is enclosed in quotation marks. if NULL was specified as the default value, then this column is the word NULL, not enclosed in quotation marks. If the default value cannot be represented without truncation, then this column contains TRUNCATED, with no enclosing single quotation marks. If no default value was specified, then this column is NULL. The value of COLUMN_DEF can be used in generating a new column definition, except when it contains the value TRUNCATED */ if (!field->def) row[12]= NullS; /* COLUMN_DEF */ else { if (field->type == MYSQL_TYPE_TIMESTAMP && !strcmp(field->def,"0000-00-00 00:00:00")) { row[12]= NullS; /* COLUMN_DEF */ } else { char *def= alloc_root(alloc, strlen(field->def) + 3); if (is_numeric_mysql_type(field)) { sprintf(def, "%s", field->def); } else { sprintf(def, "'%s'", field->def); } row[12]= def; /* COLUMN_DEF */ } } sprintf(buff, "%d", ++count); row[16]= strdup_root(alloc, buff); /* ORDINAL_POSITION */ } mysql_free_result(table_res); } set_row_count(stmt, rows); mysql_link_fields(stmt, SQLCOLUMNS_fields, SQLCOLUMNS_FIELDS); return SQL_SUCCESS; empty_set: return create_empty_fake_resultset(stmt, SQLCOLUMNS_values, sizeof(SQLCOLUMNS_values), SQLCOLUMNS_fields, SQLCOLUMNS_FIELDS); } /**************************************************************************** SQLTablePrivileges **************************************************************************** */ /* @type : internal @purpose : checks for the grantability */ static my_bool is_grantable(char *grant_list) { char *grant=dupp_str(grant_list,SQL_NTS);; if ( grant_list && grant_list[0] ) { char seps[] = ","; char *token; token = strtok( grant, seps ); while ( token != NULL ) { if ( !strcmp(token,"Grant") ) { x_free(grant); return(1); } token = strtok( NULL, seps ); } } x_free(grant); return(0); } /* @type : internal @purpose : returns a table privileges result, NULL on error. Uses mysql db tables */ static MYSQL_RES *table_privs_raw_data( DBC * dbc, SQLCHAR * catalog, SQLSMALLINT catalog_len, SQLCHAR * table, SQLSMALLINT table_len) { MYSQL *mysql= &dbc->mysql; char buff[255+2*NAME_LEN+1], *pos; pos= strxmov(buff, "SELECT Db,User,Table_name,Grantor,Table_priv ", "FROM mysql.tables_priv WHERE Table_name LIKE '", NullS); pos+= mysql_real_escape_string(mysql, pos, (char *)table, table_len); pos= strxmov(pos, "' AND Db = ", NullS); if (catalog_len) { pos= strmov(pos, "'"); pos+= mysql_real_escape_string(mysql, pos, (char *)catalog, catalog_len); pos= strmov(pos, "'"); } else pos= strmov(pos, "DATABASE()"); pos= strxmov(pos, " ORDER BY Db, Table_name, Table_priv, User", NullS); MYLOG_DBC_QUERY(dbc, buff); if (mysql_query(mysql,buff)) return NULL; return mysql_store_result(mysql); } #define MY_MAX_TABPRIV_COUNT 21 #define MY_MAX_COLPRIV_COUNT 3 char *SQLTABLES_priv_values[]= { NULL,"",NULL,NULL,NULL,NULL,NULL }; MYSQL_FIELD SQLTABLES_priv_fields[]= { MYODBC_FIELD_NAME("TABLE_CAT", 0), MYODBC_FIELD_NAME("TABLE_SCHEM", 0), MYODBC_FIELD_NAME("TABLE_NAME", NOT_NULL_FLAG), MYODBC_FIELD_NAME("GRANTOR", 0), MYODBC_FIELD_NAME("GRANTEE", NOT_NULL_FLAG), MYODBC_FIELD_NAME("PRIVILEGE", NOT_NULL_FLAG), MYODBC_FIELD_NAME("IS_GRANTABLE", 0), }; const uint SQLTABLES_PRIV_FIELDS= array_elements(SQLTABLES_priv_values); /* @type : ODBC 1.0 API @purpose : returns a list of tables and the privileges associated with each table. The driver returns the information as a result set on the specified statement. */ SQLRETURN mysql_list_table_priv(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len) { STMT *stmt= (STMT *)hstmt; char **data, **row; MEM_ROOT *alloc; uint row_count; pthread_mutex_lock(&stmt->dbc->lock); stmt->result= table_privs_raw_data(stmt->dbc, catalog, catalog_len, table, table_len); if (!stmt->result) { SQLRETURN rc= handle_connection_error(stmt); pthread_mutex_unlock(&stmt->dbc->lock); return rc; } pthread_mutex_unlock(&stmt->dbc->lock); /* Allocate max buffers, to avoid reallocation */ stmt->result_array= (char**) my_malloc(sizeof(char*)* SQLTABLES_PRIV_FIELDS * (ulong)stmt->result->row_count * MY_MAX_TABPRIV_COUNT, MYF(MY_ZEROFILL)); if (!stmt->result_array) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } alloc= &stmt->result->field_alloc; data= stmt->result_array; row_count= 0; while ( (row= mysql_fetch_row(stmt->result)) ) { char *grants= row[4]; char token[NAME_LEN+1]; const char *grant= (const char *)grants; for ( ;; ) { data[0]= row[0]; data[1]= ""; data[2]= row[2]; data[3]= row[3]; data[4]= row[1]; data[6]= is_grantable(row[4]) ? "YES" : "NO"; ++row_count; if ( !(grant= my_next_token(grant,&grants,token,',')) ) { /* End of grants .. */ data[5]= strdup_root(alloc,grants); data+= SQLTABLES_PRIV_FIELDS; break; } data[5]= strdup_root(alloc,token); data+= SQLTABLES_PRIV_FIELDS; } } set_row_count(stmt, row_count); mysql_link_fields(stmt,SQLTABLES_priv_fields,SQLTABLES_PRIV_FIELDS); return SQL_SUCCESS; } /* **************************************************************************** SQLColumnPrivileges **************************************************************************** */ /* @type : internal @purpose : returns a column privileges result, NULL on error */ static MYSQL_RES *column_privs_raw_data( MYSQL * mysql, SQLCHAR * catalog, SQLSMALLINT catalog_len, SQLCHAR * table, SQLSMALLINT table_len, SQLCHAR * column, SQLSMALLINT column_len) { char buff[255+3*NAME_LEN+1], *pos; pos= strmov(buff, "SELECT c.Db, c.User, c.Table_name, c.Column_name," "t.Grantor, c.Column_priv, t.Table_priv " "FROM mysql.columns_priv AS c, mysql.tables_priv AS t " "WHERE c.Table_name = '"); pos+= mysql_real_escape_string(mysql, pos, (char *)table, table_len); pos= strmov(pos, "' AND c.Db = "); if (catalog_len) { pos= strmov(pos, "'"); pos+= mysql_real_escape_string(mysql, pos, (char *)catalog, catalog_len); pos= strmov(pos, "'"); } else pos= strmov(pos, "DATABASE()"); pos= strmov(pos, "AND c.Column_name LIKE '"); pos+= mysql_real_escape_string(mysql, pos, (char *)column, column_len); pos= strmov(pos, "' AND c.Table_name = t.Table_name " "ORDER BY c.Db, c.Table_name, c.Column_name, c.Column_priv"); if (mysql_query(mysql, buff)) return NULL; return mysql_store_result(mysql); } char *SQLCOLUMNS_priv_values[]= { NULL,"",NULL,NULL,NULL,NULL,NULL,NULL }; MYSQL_FIELD SQLCOLUMNS_priv_fields[]= { MYODBC_FIELD_NAME("TABLE_CAT", 0), MYODBC_FIELD_NAME("TABLE_SCHEM", 0), MYODBC_FIELD_NAME("TABLE_NAME", NOT_NULL_FLAG), MYODBC_FIELD_NAME("COLUMN_NAME", NOT_NULL_FLAG), MYODBC_FIELD_NAME("GRANTOR", 0), MYODBC_FIELD_NAME("GRANTEE", NOT_NULL_FLAG), MYODBC_FIELD_NAME("PRIVILEGE", NOT_NULL_FLAG), MYODBC_FIELD_NAME("IS_GRANTABLE", 0), }; const uint SQLCOLUMNS_PRIV_FIELDS= array_elements(SQLCOLUMNS_priv_values); SQLRETURN mysql_list_column_priv(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *column, SQLSMALLINT column_len) { STMT *stmt=(STMT *) hstmt; char **row, **data; MEM_ROOT *alloc; uint row_count; CLEAR_STMT_ERROR(hstmt); my_SQLFreeStmt(hstmt,MYSQL_RESET); pthread_mutex_lock(&stmt->dbc->lock); stmt->result= column_privs_raw_data(&stmt->dbc->mysql, catalog, catalog_len, table, table_len, column, column_len); if (!stmt->result) { SQLRETURN rc= handle_connection_error(stmt); pthread_mutex_unlock(&stmt->dbc->lock); return rc; } pthread_mutex_unlock(&stmt->dbc->lock); stmt->result_array= (char **)my_malloc(sizeof(char *) * SQLCOLUMNS_PRIV_FIELDS * (ulong) stmt->result->row_count * MY_MAX_COLPRIV_COUNT, MYF(MY_ZEROFILL)); if (!stmt->result_array) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } alloc= &stmt->result->field_alloc; data= stmt->result_array; row_count= 0; while ( (row= mysql_fetch_row(stmt->result)) ) { char *grants= row[5]; char token[NAME_LEN+1]; const char *grant= (const char *)grants; for ( ;; ) { data[0]= row[0]; data[1]= ""; data[2]= row[2]; data[3]= row[3]; data[4]= row[4]; data[5]= row[1]; data[7]= is_grantable(row[6]) ? "YES":"NO"; ++row_count; if ( !(grant= my_next_token(grant,&grants,token,',')) ) { /* End of grants .. */ data[6]= strdup_root(alloc,grants); data+= SQLCOLUMNS_PRIV_FIELDS; break; } data[6]= strdup_root(alloc,token); data+= SQLCOLUMNS_PRIV_FIELDS; } } set_row_count(stmt, row_count); mysql_link_fields(stmt,SQLCOLUMNS_priv_fields,SQLCOLUMNS_PRIV_FIELDS); return SQL_SUCCESS; } /** Get the table status for a table or tables using SHOW TABLE STATUS. Lengths may not be SQL_NTS. @param[in] stmt Handle to statement @param[in] catalog Catalog (database) of table, @c NULL for current @param[in] catalog_length Length of catalog name @param[in] table Name of table @param[in] table_length Length of table name @param[in] wildcard Whether the table name is a wildcard @return Result of SHOW TABLE STATUS, or NULL if there is an error or empty result (check mysql_errno(&stmt->dbc->mysql) != 0) */ MYSQL_RES *mysql_table_status_show(STMT *stmt, SQLCHAR *catalog, SQLSMALLINT catalog_length, SQLCHAR *table, SQLSMALLINT table_length, my_bool wildcard) { MYSQL *mysql= &stmt->dbc->mysql; /** @todo determine real size for buffer */ char buff[36 + 4*NAME_LEN + 1], *to; to= strmov(buff, "SHOW TABLE STATUS "); if (catalog && *catalog) { to= strmov(to, "FROM `"); to+= myodbc_escape_string(mysql, to, (ulong)(sizeof(buff) - (to - buff)), (char *)catalog, catalog_length, 1); to= strmov(to, "` "); } /* As a pattern-value argument, an empty string needs to be treated literally. (It's not the same as NULL, which is the same as '%'.) But it will never match anything, so bail out now. */ if (table && wildcard && !*table) return NULL; if (table && *table) { to= strmov(to, "LIKE '"); if (wildcard) to+= mysql_real_escape_string(mysql, to, (char *)table, table_length); else to+= myodbc_escape_string(mysql, to, (ulong)(sizeof(buff) - (to - buff)), (char *)table, table_length, 0); to= strmov(to, "'"); } MYLOG_QUERY(stmt, buff); assert(to - buff < sizeof(buff)); if (mysql_real_query(mysql,buff,(unsigned long)(to - buff))) { return NULL; } return mysql_store_result(mysql); } /* **************************************************************************** SQLForeignKeys **************************************************************************** */ MYSQL_FIELD SQLFORE_KEYS_fields[]= { MYODBC_FIELD_NAME("PKTABLE_CAT", 0), MYODBC_FIELD_NAME("PKTABLE_SCHEM", 0), MYODBC_FIELD_NAME("PKTABLE_NAME", NOT_NULL_FLAG), MYODBC_FIELD_NAME("PKCOLUMN_NAME", NOT_NULL_FLAG), MYODBC_FIELD_NAME("FKTABLE_CAT", 0), MYODBC_FIELD_NAME("FKTABLE_SCHEM", 0), MYODBC_FIELD_NAME("FKTABLE_NAME", NOT_NULL_FLAG), MYODBC_FIELD_NAME("FKCOLUMN_NAME", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("KEY_SEQ", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("UPDATE_RULE", 0), MYODBC_FIELD_SHORT("DELETE_RULE", 0), MYODBC_FIELD_NAME("FK_NAME", 0), MYODBC_FIELD_NAME("PK_NAME", 0), MYODBC_FIELD_SHORT("DEFERRABILITY", 0), }; const uint SQLFORE_KEYS_FIELDS= array_elements(SQLFORE_KEYS_fields); char *SQLFORE_KEYS_values[]= { NULL,"",NULL,NULL, NULL,"",NULL,NULL, 0,0,0,NULL,NULL,0 }; SQLRETURN mysql_foreign_keys(SQLHSTMT hstmt, SQLCHAR *szPkCatalogName __attribute__((unused)), SQLSMALLINT cbPkCatalogName __attribute__((unused)), SQLCHAR *szPkSchemaName __attribute__((unused)), SQLSMALLINT cbPkSchemaName __attribute__((unused)), SQLCHAR *szPkTableName, SQLSMALLINT cbPkTableName, SQLCHAR *szFkCatalogName, SQLSMALLINT cbFkCatalogName, SQLCHAR *szFkSchemaName __attribute__((unused)), SQLSMALLINT cbFkSchemaName __attribute__((unused)), SQLCHAR *szFkTableName, SQLSMALLINT cbFkTableName) { STMT FAR *stmt=(STMT FAR*) hstmt; uint row_count= 0; MEM_ROOT *alloc; MYSQL_ROW row; char **data; char **tempdata; /* We need this array for the cases if key count is greater than 18 */ uint comment_id; pthread_mutex_lock(&stmt->dbc->lock); stmt->result= mysql_table_status(stmt, szFkCatalogName, cbFkCatalogName, szFkTableName, cbFkTableName, FALSE, TRUE, FALSE); if (!stmt->result) { if (mysql_errno(&stmt->dbc->mysql)) { SQLRETURN rc= handle_connection_error(stmt); pthread_mutex_unlock(&stmt->dbc->lock); return rc; } else { pthread_mutex_unlock(&stmt->dbc->lock); goto empty_set; } pthread_mutex_unlock(&stmt->dbc->lock); } pthread_mutex_unlock(&stmt->dbc->lock); tempdata= (char**) my_malloc(sizeof(char*)*SQLFORE_KEYS_FIELDS* 64, /* Maximum index count */ MYF(MY_ZEROFILL)); if (!tempdata) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } /* Convert mysql fields to data that odbc wants */ alloc= &stmt->result->field_alloc; data= tempdata; comment_id= stmt->result->field_count - 1; while ( (row= mysql_fetch_row(stmt->result)) ) { if ( (row[1] && strcmp(row[1],"InnoDB")==0) ) { const char *token,*pktoken,*fk_cols_start,*pk_cols_start; char *comment_token, ref_token[NAME_LEN+1]; char *pkcomment,*fkcomment; uint key_seq,pk_length,fk_length; if ( !(comment_token= strchr(row[comment_id],';')) ) continue; /* MySQL 4.1 and above, the comment field is '15' */ do { /* Found reference information in comment field from InnoDB type, and parse the same to get the FK information .. */ key_seq= 1; if ( !(token= my_next_token(NULL,&comment_token,NULL,'(')) ) break; fk_cols_start = token + 1; if ( !(token= my_next_token(token,&comment_token,ref_token,')')) ) continue; fk_length= (uint)((token-2)-fk_cols_start); if ( !(token= my_next_token(token+8,&comment_token,ref_token,'/')) ) continue; data[0]= strdup_root(alloc,ref_token); /* PKTABLE_CAT */ if (!(token= my_next_token(token, &comment_token, ref_token, '(')) || (szPkTableName && myodbc_casecmp((char *)szPkTableName, ref_token, cbPkTableName))) continue; ref_token[strlen(ref_token)- 1] = 0; /* Remove last quot character */ data[2]= strdup_root(alloc,ref_token); /* PKTABLE_TABLE */ pk_cols_start = token + 1; if ( !(token= my_next_token(token,&comment_token,ref_token,')')) ) continue; pk_length= (uint)((token-2)-pk_cols_start); data[1]= NULL; /* PKTABLE_SCHEM */ /** @todo clean this up when current database tracking is better */ if (!szFkCatalogName && !stmt->dbc->database) reget_current_catalog(stmt->dbc); /* FKTABLE_CAT */ data[4]= (szFkCatalogName ? strdup_root(alloc, (char *)szFkCatalogName) : strdup_root(alloc, stmt->dbc->database ? stmt->dbc->database : "null")); data[5]= NULL; /* FKTABLE_SCHEM */ data[6]= row[0]; /* FKTABLE_TABLE */ /* We could figure out UPDATE_RULE and DELETE_RULE by parsing the comment field. For now, we just return SQL_CASCADE> */ data[9]= "1"; /*SQL_CASCADE*/ /* UPDATE_RULE */ data[10]= "1"; /*SQL_CASCADE*/ /* DELETE_RULE */ data[11]= NULL; /* FK_NAME */ data[12]= NULL; /* PK_NAME */ data[13]= "7"; /*SQL_NOT_DEFERRABLE*/ /* DEFERRABILITY */ token = fkcomment = (char *)fk_cols_start; pktoken = pkcomment = (char *)pk_cols_start; fkcomment[fk_length]= '\0'; pkcomment[pk_length]= '\0'; while ( (token= my_next_token(token,&fkcomment,ref_token,' ')) ) { /* Multiple columns exists .. parse them to individual rows */ char **prev_data= data; data[7]= strdup_root(alloc,ref_token); /* FKTABLE_COLUMN */ pktoken= my_next_token(pktoken,&pkcomment,ref_token,' '); data[3]= strdup_root(alloc,ref_token); /* PKTABLE_COLUMN */ sprintf(ref_token,"%d",key_seq++); data[8]= strdup_root(alloc,ref_token); /* KEY_SEQ */ data+= SQLFORE_KEYS_FIELDS; ++row_count; for ( fk_length= SQLFORE_KEYS_FIELDS; fk_length--; ) data[fk_length]= prev_data[fk_length]; } data[7]= strdup_root(alloc,fkcomment); /* FKTABLE_COLUMN */ data[3]= strdup_root(alloc,pkcomment); /* PKTABLE_COLUMN */ sprintf(ref_token,"%d",key_seq); data[8]= strdup_root(alloc,ref_token); /* KEY_SEQ */ data+= SQLFORE_KEYS_FIELDS; ++row_count; } while ( (comment_token = strchr(comment_token,';')) );/* multi table ref */ } } /* Copy only the elements that contain fk names */ stmt->result_array= (MYSQL_ROW)my_memdup((char *)tempdata, sizeof(char *) * SQLFORE_KEYS_FIELDS * row_count, MYF(0)); x_free((char *)tempdata); if (!stmt->result_array) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } set_row_count(stmt, row_count); mysql_link_fields(stmt,SQLFORE_KEYS_fields,SQLFORE_KEYS_FIELDS); return SQL_SUCCESS; empty_set: return create_empty_fake_resultset(stmt, SQLFORE_KEYS_values, sizeof(SQLFORE_KEYS_values), SQLFORE_KEYS_fields, SQLFORE_KEYS_FIELDS); } /* **************************************************************************** SQLPrimaryKeys **************************************************************************** */ MYSQL_FIELD SQLPRIM_KEYS_fields[]= { MYODBC_FIELD_NAME("TABLE_CAT", 0), MYODBC_FIELD_NAME("TABLE_SCHEM", 0), MYODBC_FIELD_NAME("TABLE_NAME", NOT_NULL_FLAG), MYODBC_FIELD_NAME("COLUMN_NAME", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("KEY_SEQ", NOT_NULL_FLAG), MYODBC_FIELD_STRING("PK_NAME", 128, 0), }; const uint SQLPRIM_KEYS_FIELDS= array_elements(SQLPRIM_KEYS_fields); const long SQLPRIM_LENGTHS[]= {0, 0, 1, 5, 4, -7}; char *SQLPRIM_KEYS_values[]= { NULL,"",NULL,NULL,0,NULL }; /* @purpose : returns the column names that make up the primary key for a table. The driver returns the information as a result set. This function does not support returning primary keys from multiple tables in a single call */ SQLRETURN mysql_primary_keys(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len) { STMT FAR *stmt= (STMT FAR*) hstmt; MYSQL_ROW row; char **data; uint row_count; pthread_mutex_lock(&stmt->dbc->lock); if (!(stmt->result= mysql_list_dbkeys(stmt->dbc, catalog, catalog_len, table, table_len))) { SQLRETURN rc= handle_connection_error(stmt); pthread_mutex_unlock(&stmt->dbc->lock); return rc; } pthread_mutex_unlock(&stmt->dbc->lock); stmt->result_array= (char**) my_malloc(sizeof(char*)*SQLPRIM_KEYS_FIELDS* (ulong) stmt->result->row_count, MYF(MY_ZEROFILL)); if (!stmt->result_array) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } stmt->lengths= (unsigned long*) my_malloc( sizeof(long)*SQLPRIM_KEYS_FIELDS* (ulong) stmt->result->row_count, MYF(MY_ZEROFILL)); if (!stmt->lengths) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } row_count= 0; data= stmt->result_array; while ( (row= mysql_fetch_row(stmt->result)) ) { if ( row[1][0] == '0' ) /* If unique index */ { if ( row_count && !strcmp(row[3],"1") ) break; /* Already found unique key */ fix_row_lengths(stmt, SQLPRIM_LENGTHS, row_count, SQLPRIM_KEYS_FIELDS); ++row_count; data[0]= data[1]=0; data[2]= row[0]; data[3]= row[4]; data[4]= row[3]; data[5]= "PRIMARY"; data+= SQLPRIM_KEYS_FIELDS; } } set_row_count(stmt, row_count); mysql_link_fields(stmt,SQLPRIM_KEYS_fields,SQLPRIM_KEYS_FIELDS); return SQL_SUCCESS; } /* **************************************************************************** SQLProcedure Columns **************************************************************************** */ char *SQLPROCEDURECOLUMNS_values[]= { "", "", NullS, NullS, "", "", "", "", "", "", "10", "", "MySQL column", "", "", NullS, "", NullS, "" }; /* TODO make LONGLONG fields just LONG if SQLLEN is 4 bytes */ MYSQL_FIELD SQLPROCEDURECOLUMNS_fields[]= { MYODBC_FIELD_NAME("PROCEDURE_CAT", 0), MYODBC_FIELD_NAME("PROCEDURE_SCHEM", 0), MYODBC_FIELD_NAME("PROCEDURE_NAME", NOT_NULL_FLAG), MYODBC_FIELD_NAME("COLUMN_NAME", NOT_NULL_FLAG), MYODBC_FIELD_SHORT ("COLUMN_TYPE", NOT_NULL_FLAG), MYODBC_FIELD_SHORT ("DATA_TYPE", NOT_NULL_FLAG), MYODBC_FIELD_STRING("TYPE_NAME", 20, NOT_NULL_FLAG), MYODBC_FIELD_LONGLONG("COLUMN_SIZE", 0), MYODBC_FIELD_LONGLONG("BUFFER_LENGTH", 0), MYODBC_FIELD_SHORT ("DECIMAL_DIGITS", 0), MYODBC_FIELD_SHORT ("NUM_PREC_RADIX", 0), MYODBC_FIELD_SHORT ("NULLABLE", NOT_NULL_FLAG), MYODBC_FIELD_NAME("REMARKS", 0), MYODBC_FIELD_NAME("COLUMN_DEF", 0), MYODBC_FIELD_SHORT ("SQL_DATA_TYPE", NOT_NULL_FLAG), MYODBC_FIELD_SHORT ("SQL_DATETIME_SUB", 0), MYODBC_FIELD_LONGLONG("CHAR_OCTET_LENGTH", 0), MYODBC_FIELD_LONG ("ORDINAL_POSITION", NOT_NULL_FLAG), MYODBC_FIELD_STRING("IS_NULLABLE", 3, 0), }; const uint SQLPROCEDURECOLUMNS_FIELDS= array_elements(SQLPROCEDURECOLUMNS_fields); /* @type : internal @purpose : returns procedure params as resultset */ static MYSQL_RES *mysql_list_proc_params(DBC *dbc, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *proc_name, SQLSMALLINT proc_name_len) { MYSQL *mysql= &dbc->mysql; char buff[255+4*NAME_LEN+1], *pos; pos= strmov(buff, "SELECT name, CONCAT(IF(length(returns)>0, CONCAT('RETURN_VALUE ', returns, if(length(param_list)>0, ',', '')),''), param_list)," "db, type FROM mysql.proc WHERE Db="); if (catalog_len) { pos= strmov(pos, "'"); pos+= mysql_real_escape_string(mysql, pos, (char *)catalog, catalog_len); pos= strmov(pos, "'"); } else pos= strmov(pos, "DATABASE()"); if (proc_name_len) { pos= strmov(pos, " AND name LIKE '"); pos+= mysql_real_escape_string(mysql, pos, (char *)proc_name, proc_name_len); pos= strmov(pos, "'"); } pos= strmov(pos, " ORDER BY Db, name"); assert(pos - buff < sizeof(buff)); MYLOG_DBC_QUERY(dbc, buff); if (mysql_real_query(mysql, buff, (unsigned long)(pos - buff))) return NULL; return mysql_store_result(mysql); } /* @type : internal @purpose : releases memory allocated for internal use in SQLProcedureColumns */ static void free_procedurecolumn_res(int total_records, LIST *params) { int i; uint j; LIST *cur_params; for (i= 1; i <= total_records; ++i) { if(params && params->data) { cur_params= params; for (j= 0; j < SQLPROCEDURECOLUMNS_FIELDS; ++j) { /* check for constant values that do not need to be freed */ if ((j != mypcPROCEDURE_SCHEM) && (j != mypcNUM_PREC_RADIX) && (j != mypcNULLABLE) && (j != mypcREMARKS) && (j != mypcCOLUMN_DEF) && (j != mypcIS_NULLABLE)) { x_free(((char**)cur_params->data)[j]); } } /* cleanup the list */ params= list_delete_forward(params); x_free(cur_params->data); x_free(cur_params); } } } /* @type : ODBC 1.0 API @purpose : returns the list of input and output parameters, as well as the columns that make up the result set for the specified procedures. The driver returns the information as a result set on the specified statement */ SQLRETURN mysql_procedure_columns(SQLHSTMT hstmt, SQLCHAR *szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR *szSchemaName __attribute__((unused)), SQLSMALLINT cbSchemaName __attribute__((unused)), SQLCHAR *szProcName, SQLSMALLINT cbProcName, SQLCHAR *szColumnName, SQLSMALLINT cbColumnName) { STMT *stmt= (STMT *)hstmt; LIST *params= 0, *params_r, *cur_params= 0; SQLRETURN nReturn= SQL_SUCCESS; DYNAMIC_STRING dynQuery; MYSQL_ROW row; MYSQL_RES *proc_list_res, *columns_res= 0; char **tempdata; int params_num= 0, return_params_num= 0; unsigned int i, j, total_params_num= 0; if (init_dynamic_string(&dynQuery, "SELECT 1", 1024,1024)) return set_stmt_error(stmt, "HY001", "Not enough memory", 4001); params_r= params= (LIST *) my_malloc(sizeof(LIST), MYF(MY_ZEROFILL)); if (params_r == NULL) { dynstr_free(&dynQuery); set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } /* get procedures list */ pthread_mutex_lock(&stmt->dbc->lock); if (!(proc_list_res= mysql_list_proc_params(stmt->dbc, szCatalogName, cbCatalogName, szProcName, cbProcName))) { pthread_mutex_unlock(&stmt->dbc->lock); nReturn= set_error(stmt, MYERR_S1000, mysql_error(&stmt->dbc->mysql), mysql_errno(&stmt->dbc->mysql)); goto clean_exit; } pthread_mutex_unlock(&stmt->dbc->lock); while ((row= mysql_fetch_row(proc_list_res))) { char *token; char *param_str; char *param_str_end; SQLINTEGER param_ordinal_position= 1; /* Return value parameter must have 0 as ordinal position */ if(!myodbc_strcasecmp(row[3], "FUNCTION")) param_ordinal_position= 0; param_str= row[1]; if(!param_str[0]) continue; param_str_end= param_str + strlen(param_str); token = proc_param_tokenize(param_str, ¶ms_num); if (params_num == 0) { goto empty_set; } while (token != NULL) { SQLSMALLINT ptype= 0; int sql_type_index; unsigned int flags= 0; SQLCHAR param_name[NAME_LEN]= "\0"; SQLCHAR param_dbtype[1024]= "\0"; SQLCHAR param_type[4]= "\0"; SQLCHAR param_sql_type[6]= "\0"; SQLCHAR param_size_buf[21]= "\0"; SQLCHAR param_buffer_len[21]= "\0"; SQLCHAR param_decimal[6]= "\0"; SQLCHAR param_desc_type[6]= "\0"; SQLCHAR param_pos[6]= "\0"; SQLTypeMap *type_map; SQLSMALLINT dec; SQLULEN param_size= 0; MYSQL_ROW data= my_malloc(sizeof(SQLPROCEDURECOLUMNS_values), MYF(MY_ZEROFILL)); /* temp variables for debugging */ SQLUINTEGER dec_int= 0; SQLINTEGER sql_type_int= 0; if (data == NULL) { mysql_free_result(proc_list_res); set_mem_error(&stmt->dbc->mysql); nReturn= handle_connection_error(stmt); goto clean_exit; } token= proc_get_param_type(token, (int)strlen(token), &ptype); token= proc_get_param_name(token, (int)strlen(token), param_name); token= proc_get_param_dbtype(token, (int)strlen(token), param_dbtype); /* param_dbtype is lowercased in the proc_get_param_dbtype */ if (strstr(param_dbtype, "unsigned")) flags |= UNSIGNED_FLAG; sql_type_index= proc_get_param_sql_type_index(param_dbtype, (int)strlen(param_dbtype)); type_map= proc_get_param_map_by_index(sql_type_index); param_size= proc_get_param_size(param_dbtype, (int)strlen(param_dbtype), sql_type_index, &dec); proc_get_param_octet_len(stmt, sql_type_index, param_size, dec, flags, param_buffer_len); data[mypcPROCEDURE_CAT]= my_strdup(row[2], MYF(0)); /* PROCEDURE_CAT */ data[mypcPROCEDURE_SCHEM]= NULL; /* PROCEDURE_SCHEM */ data[mypcPROCEDURE_NAME]= my_strdup(row[0], MYF(0)); /* PROCEDURE_NAME */ data[mypcCOLUMN_NAME]= my_strdup(param_name, MYF(0)); /* COLUMN_NAME */ if (cbColumnName) { dynstr_append_mem(&dynQuery, ",", 1); dynstr_append_os_quoted(&dynQuery, (char *)param_name, NullS); dynstr_append_mem(&dynQuery, " LIKE ", 6); dynstr_append_os_quoted(&dynQuery, (char *)szColumnName, NullS); } if(param_ordinal_position == 0) { ptype= SQL_RETURN_VALUE; } sprintf(param_type, "%d", ptype); data[mypcCOLUMN_TYPE]= my_strdup(param_type, MYF(0)); /* COLUMN_TYPE */ sprintf(param_sql_type, "%d", (int)type_map->sql_type); data[mypcDATA_TYPE]= my_strdup(param_sql_type, MYF(0)); /* DATA_TYPE */ if(!myodbc_strcasecmp(type_map->type_name, "set") || !myodbc_strcasecmp(type_map->type_name, "enum")) { data[mypcTYPE_NAME]= my_strdup("char", MYF(0)); } else { data[mypcTYPE_NAME]= my_strdup(type_map->type_name, MYF(0)); } /* TYPE_NAME */ proc_get_param_col_len(stmt, sql_type_index, param_size, dec, flags, param_size_buf); data[mypcCOLUMN_SIZE]= my_strdup(param_size_buf, MYF(0)); /* COLUMN_SIZE */ data[mypcBUFFER_LENGTH]= my_strdup(param_buffer_len, MYF(0)); /* BUFFER_LENGTH */ if (dec != SQL_NO_TOTAL) { sprintf(param_decimal, "%d", (int)dec); data[mypcDECIMAL_DIGITS]= my_strdup(param_decimal, MYF(0)); /* DECIMAL_DIGITS */ data[mypcNUM_PREC_RADIX]= "10"; /* NUM_PREC_RADIX */ } else { data[mypcDECIMAL_DIGITS]= NullS; data[mypcNUM_PREC_RADIX]= NullS; /* NUM_PREC_RADIX */ } data[mypcNULLABLE]= "1"; /* NULLABLE */ data[mypcREMARKS]= ""; /* REMARKS */ data[mypcCOLUMN_DEF]= NullS; /* COLUMN_DEF */ if(type_map->sql_type == SQL_TYPE_DATE || type_map->sql_type == SQL_TYPE_TIME || type_map->sql_type == SQL_TYPE_TIMESTAMP) { sprintf(param_desc_type, "%d", SQL_DATETIME); data[mypcSQL_DATA_TYPE]= my_strdup(param_desc_type, MYF(0)); /* SQL_DATA_TYPE */ data[mypcSQL_DATETIME_SUB]= my_strdup(data[mypcDATA_TYPE], MYF(0)); /* SQL_DATETIME_SUB */ } else { data[mypcSQL_DATA_TYPE]= my_strdup(data[mypcDATA_TYPE], MYF(0)); /* SQL_DATA_TYPE */ data[mypcSQL_DATETIME_SUB]= NULL; /* SQL_DATETIME_SUB */ } if (is_char_sql_type(type_map->sql_type) || is_wchar_sql_type(type_map->sql_type) || is_binary_sql_type(type_map->sql_type)) { /* Actualy can use data[mypcBUFFER_LENGTH] here and don't do my_strdup */ data[mypcCHAR_OCTET_LENGTH]= my_strdup(param_buffer_len, MYF(0)); /* CHAR_OCTET_LENGTH */ } else { data[mypcCHAR_OCTET_LENGTH]= NULL; /* CHAR_OCTET_LENGTH */ } sprintf(param_pos, "%d", param_ordinal_position); data[mypcORDINAL_POSITION]= my_strdup(param_pos, MYF(0)); /* ORDINAL_POSITION */ ++param_ordinal_position; data[mypcIS_NULLABLE]= "YES"; /* IS_NULLABLE */ { LIST *new_elem= (LIST *) my_malloc(sizeof(LIST), MYF(MY_ZEROFILL)); if (new_elem == NULL) { mysql_free_result(proc_list_res); set_mem_error(&stmt->dbc->mysql); nReturn= handle_connection_error(stmt); goto clean_exit; } new_elem->data= data; params->next= new_elem; new_elem->prev= params; params= new_elem; ++total_params_num; } token = proc_param_next_token(token, param_str_end); } } return_params_num= total_params_num; if (cbColumnName) { pthread_mutex_lock(&stmt->dbc->lock); if (mysql_real_query(&stmt->dbc->mysql, dynQuery.str, (unsigned long)dynQuery.length) || !(columns_res= mysql_store_result(&stmt->dbc->mysql))) { pthread_mutex_unlock(&stmt->dbc->lock); mysql_free_result(proc_list_res); nReturn= set_error(stmt, MYERR_S1000, mysql_error(&stmt->dbc->mysql), mysql_errno(&stmt->dbc->mysql)); goto clean_exit; } pthread_mutex_unlock(&stmt->dbc->lock); /* should be only one row */ row= mysql_fetch_row(columns_res); if (row == NULL) { mysql_free_result(proc_list_res); nReturn= set_error(stmt, MYERR_S1000, mysql_error(&stmt->dbc->mysql), mysql_errno(&stmt->dbc->mysql)); goto clean_exit; } if(params_r->next) { params= params_r->next; } /* 1st element is always 1 */ for (i= 1; i < columns_res->field_count; ++i) { if(strcmp(row[i], "1")) { --return_params_num; } } if (return_params_num == 0) { goto empty_set; } } stmt->result= proc_list_res; stmt->result_array= (MYSQL_ROW) my_malloc(sizeof(char*) * SQLPROCEDURECOLUMNS_FIELDS * (return_params_num ? return_params_num : total_params_num), MYF(MY_ZEROFILL)); tempdata= stmt->result_array; if(params_r->next) { params= params_r->next; } /* copy data */ for (i= 0; i < total_params_num; ++i) { int skip_result= (columns_res ? strcmp(row[i+1], "1") : 0); if(params && params->data) { cur_params= params; for (j= 0; j < SQLPROCEDURECOLUMNS_FIELDS; ++j) { char *cur_field_val= ((char**)cur_params->data)[j]; /* copy data only if */ if(!skip_result) { if(cur_field_val && cur_field_val[0]) tempdata[j]= strdup_root(&stmt->result->field_alloc, cur_field_val); else tempdata[j]= 0; } } params= params->next ? params->next : params; if(!skip_result) tempdata += SQLPROCEDURECOLUMNS_FIELDS; } } set_row_count(stmt, return_params_num); mysql_link_fields(stmt, SQLPROCEDURECOLUMNS_fields, SQLPROCEDURECOLUMNS_FIELDS); goto clean_exit; empty_set: nReturn= create_empty_fake_resultset(hstmt, SQLPROCEDURECOLUMNS_values, sizeof(SQLPROCEDURECOLUMNS_values), SQLPROCEDURECOLUMNS_fields, SQLPROCEDURECOLUMNS_FIELDS); clean_exit: free_procedurecolumn_res(total_params_num, params_r->next); if(columns_res) { mysql_free_result(columns_res); } dynstr_free(&dynQuery); x_free(params_r); return nReturn; } /* **************************************************************************** SQLSpecialColumns **************************************************************************** */ MYSQL_FIELD SQLSPECIALCOLUMNS_fields[]= { MYODBC_FIELD_SHORT("SCOPE", 0), MYODBC_FIELD_NAME("COLUMN_NAME", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("DATA_TYPE", NOT_NULL_FLAG), MYODBC_FIELD_STRING("TYPE_NAME", 20, NOT_NULL_FLAG), MYODBC_FIELD_LONG("COLUMN_SIZE", 0), MYODBC_FIELD_LONG("BUFFER_LENGTH", 0), MYODBC_FIELD_LONG("DECIMAL_DIGITS", 0), MYODBC_FIELD_SHORT("PSEUDO_COLUMN", 0), }; char *SQLSPECIALCOLUMNS_values[]= { 0,NULL,0,NULL,0,0,0,0 }; const uint SQLSPECIALCOLUMNS_FIELDS= array_elements(SQLSPECIALCOLUMNS_fields); /* @type : ODBC 1.0 API @purpose : retrieves the following information about columns within a specified table: - The optimal set of columns that uniquely identifies a row in the table. - Columns that are automatically updated when any value in the row is updated by a transaction */ SQLRETURN mysql_special_columns(SQLHSTMT hstmt, SQLUSMALLINT fColType, SQLCHAR *szTableQualifier, SQLSMALLINT cbTableQualifier, SQLCHAR *szTableOwner __attribute__((unused)), SQLSMALLINT cbTableOwner __attribute__((unused)), SQLCHAR *szTableName, SQLSMALLINT cbTableName, SQLUSMALLINT fScope __attribute__((unused)), SQLUSMALLINT fNullable __attribute__((unused))) { STMT *stmt=(STMT *) hstmt; char buff[80]; char **row; MYSQL_RES *result; MYSQL_FIELD *field; MEM_ROOT *alloc; uint field_count; my_bool primary_key; /* Reset the statement in order to avoid memory leaks when working with ADODB */ my_SQLFreeStmt(hstmt, MYSQL_RESET); stmt->result= mysql_list_dbcolumns(stmt, szTableQualifier, cbTableQualifier, szTableName, cbTableName, NULL, 0); if (!(result= stmt->result)) { return handle_connection_error(stmt); } if ( fColType == SQL_ROWVER ) { /* Find possible timestamp */ if ( !(stmt->result_array= (char**) my_malloc(sizeof(char*)*SQLSPECIALCOLUMNS_FIELDS* result->field_count, MYF(MY_ZEROFILL))) ) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } /* Convert mysql fields to data that odbc wants */ alloc= &result->field_alloc; field_count= 0; mysql_field_seek(result,0); for ( row= stmt->result_array; (field = mysql_fetch_field(result)); ) { SQLSMALLINT type; if ((field->type != MYSQL_TYPE_TIMESTAMP)) continue; #ifdef ON_UPDATE_NOW_FLAG if (!(field->flags & ON_UPDATE_NOW_FLAG)) continue; #else /* TIMESTAMP_FLAG is only set on fields that are auto-set or auto-updated. We really only want auto-updated, but we can't tell the difference because of Bug #30081. */ if (!(field->flags & TIMESTAMP_FLAG)) continue; #endif ++field_count; row[0]= NULL; row[1]= field->name; type= get_sql_data_type(stmt, field, buff); row[3]= strdup_root(alloc,buff); sprintf(buff,"%d",type); row[2]= strdup_root(alloc,buff); fill_column_size_buff(buff, stmt, field); row[4]= strdup_root(alloc,buff); sprintf(buff, "%ld", get_transfer_octet_length(stmt, field)); row[5]= strdup_root(alloc,buff); { SQLSMALLINT digits= get_decimal_digits(stmt, field); if (digits != SQL_NO_TOTAL) { sprintf(buff,"%d", digits); row[6]= strdup_root(alloc,buff); } else row[6]= NULL; } sprintf(buff,"%d",SQL_PC_NOT_PSEUDO); row[7]= strdup_root(alloc,buff); row+= SQLSPECIALCOLUMNS_FIELDS; } result->row_count= field_count; mysql_link_fields(stmt,SQLSPECIALCOLUMNS_fields,SQLSPECIALCOLUMNS_FIELDS); return SQL_SUCCESS; } if ( fColType != SQL_BEST_ROWID ) { return set_error(stmt, MYERR_S1000, "Unsupported argument to SQLSpecialColumns", 4000); } /* * The optimal set of columns for identifing a row is either * the primary key, or if there is no primary key, then * all the fields. */ /* Check if there is a primary (unique) key */ primary_key= 0; while ( (field= mysql_fetch_field(result)) ) { if ( field->flags & PRI_KEY_FLAG ) { primary_key=1; break; } } if ( !(stmt->result_array= (char**) my_malloc(sizeof(char*)*SQLSPECIALCOLUMNS_FIELDS* result->field_count, MYF(MY_ZEROFILL))) ) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } /* Convert MySQL fields to data that odbc wants */ alloc= &result->field_alloc; field_count= 0; mysql_field_seek(result,0); for ( row= stmt->result_array ; (field= mysql_fetch_field(result)); ) { SQLSMALLINT type; if ( primary_key && !(field->flags & PRI_KEY_FLAG) ) continue; #ifndef SQLSPECIALCOLUMNS_RETURN_ALL_COLUMNS /* The ODBC 'standard' doesn't want us to return all columns if there is no primary or unique key */ if ( !primary_key ) continue; #endif ++field_count; sprintf(buff,"%d",SQL_SCOPE_SESSION); row[0]= strdup_root(alloc,buff); row[1]= field->name; type= get_sql_data_type(stmt, field, buff); row[3]= strdup_root(alloc,buff); sprintf(buff,"%d",type); row[2]= strdup_root(alloc,buff); fill_column_size_buff(buff, stmt, field); row[4]= strdup_root(alloc,buff); sprintf(buff,"%ld", get_transfer_octet_length(stmt, field)); row[5]= strdup_root(alloc,buff); { SQLSMALLINT digits= get_decimal_digits(stmt, field); if (digits != SQL_NO_TOTAL) { sprintf(buff,"%d", digits); row[6]= strdup_root(alloc, buff); } else row[6]= NULL; } sprintf(buff,"%d",SQL_PC_NOT_PSEUDO); row[7]= strdup_root(alloc,buff); row+= SQLSPECIALCOLUMNS_FIELDS; } result->row_count= field_count; mysql_link_fields(stmt,SQLSPECIALCOLUMNS_fields,SQLSPECIALCOLUMNS_FIELDS); return SQL_SUCCESS; } /* **************************************************************************** SQLStatistics **************************************************************************** */ char SS_type[10]; uint SQLSTAT_order[]={2,3,5,7,8,9,10}; char *SQLSTAT_values[]={NullS,NullS,"","",NullS,"",SS_type,"","","","",NullS,NullS}; MYSQL_FIELD SQLSTAT_fields[]= { MYODBC_FIELD_NAME("TABLE_CAT", 0), MYODBC_FIELD_NAME("TABLE_SCHEM", 0), MYODBC_FIELD_NAME("TABLE_NAME", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("NON_UNIQUE", 0), MYODBC_FIELD_NAME("INDEX_QUALIFIER", 0), MYODBC_FIELD_NAME("INDEX_NAME", 0), MYODBC_FIELD_SHORT("TYPE", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("ORDINAL_POSITION", 0), MYODBC_FIELD_NAME("COLUMN_NAME", 0), MYODBC_FIELD_STRING("ASC_OR_DESC", 1, 0), MYODBC_FIELD_SHORT("CARDINALITY", 0), MYODBC_FIELD_SHORT("PAGES", 0), MYODBC_FIELD_STRING("FILTER_CONDITION", 10, 0), }; const uint SQLSTAT_FIELDS= array_elements(SQLSTAT_fields); /* @purpose : retrieves a list of statistics about a single table and the indexes associated with the table. The driver returns the information as a result set. */ SQLRETURN mysql_statistics(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema __attribute__((unused)), SQLSMALLINT schema_len __attribute__((unused)), SQLCHAR *table, SQLSMALLINT table_len, SQLUSMALLINT fUnique, SQLUSMALLINT fAccuracy __attribute__((unused))) { STMT *stmt= (STMT *)hstmt; MYSQL *mysql= &stmt->dbc->mysql; DBC *dbc= stmt->dbc; if (!table_len) goto empty_set; pthread_mutex_lock(&dbc->lock); stmt->result= mysql_list_dbkeys(stmt->dbc, catalog, catalog_len, table, table_len); if (!stmt->result) { SQLRETURN rc= handle_connection_error(stmt); pthread_mutex_unlock(&dbc->lock); return rc; } pthread_mutex_unlock(&dbc->lock); my_int2str(SQL_INDEX_OTHER,SS_type,10,0); stmt->order= SQLSTAT_order; stmt->order_count= array_elements(SQLSTAT_order); stmt->fix_fields= fix_fields_copy; stmt->array= (MYSQL_ROW) my_memdup((char *)SQLSTAT_values, sizeof(SQLSTAT_values),MYF(0)); if (!stmt->array) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } if (stmt->dbc->ds->no_catalog) stmt->array[0]= ""; else stmt->array[0]= strmake_root(&stmt->result->field_alloc, (char *)catalog, catalog_len); if ( fUnique == SQL_INDEX_UNIQUE ) { /* This is too low level... */ MYSQL_ROWS **prev,*pos; prev= &stmt->result->data->data; for ( pos= *prev; pos; pos= pos->next ) { if ( pos->data[1][0] == '0' ) /* Unlink nonunique index */ { (*prev)=pos; prev= &pos->next; } else { --stmt->result->row_count; } } (*prev)= 0; mysql_data_seek(stmt->result,0); /* Restore pointer */ } set_row_count(stmt, stmt->result->row_count); mysql_link_fields(stmt,SQLSTAT_fields,SQLSTAT_FIELDS); return SQL_SUCCESS; empty_set: return create_empty_fake_resultset(stmt, SQLSTAT_values, sizeof(SQLSTAT_values), SQLSTAT_fields, SQLSTAT_FIELDS); } /* **************************************************************************** SQLTables **************************************************************************** */ uint SQLTABLES_qualifier_order[]= {0}; char *SQLTABLES_values[]= {"","",NULL,"TABLE","MySQL table"}; char *SQLTABLES_qualifier_values[]= {"",NULL,NULL,NULL,NULL}; char *SQLTABLES_owner_values[]= {NULL,"",NULL,NULL,NULL}; char *SQLTABLES_type_values[3][5]= { {NULL,NULL,NULL,"TABLE",NULL}, {NULL,NULL,NULL,"SYSTEM TABLE",NULL}, {NULL,NULL,NULL,"VIEW",NULL}, }; MYSQL_FIELD SQLTABLES_fields[]= { MYODBC_FIELD_NAME("TABLE_CAT", 0), MYODBC_FIELD_NAME("TABLE_SCHEM", 0), MYODBC_FIELD_NAME("TABLE_NAME", 0), MYODBC_FIELD_NAME("TABLE_TYPE", 0), /* Table remark length is 80 characters */ MYODBC_FIELD_STRING("REMARKS", 80, 0), }; const uint SQLTABLES_FIELDS= array_elements(SQLTABLES_values); SQLRETURN mysql_tables(SQLHSTMT hstmt, SQLCHAR *catalog, SQLSMALLINT catalog_len, SQLCHAR *schema, SQLSMALLINT schema_len, SQLCHAR *table, SQLSMALLINT table_len, SQLCHAR *type, SQLSMALLINT type_len) { STMT *stmt= (STMT *)hstmt; my_bool all_dbs= 1, user_tables, views; /* empty (but non-NULL) schema and table returns catalog list */ if (catalog_len && !schema_len && schema && !table_len && table) { pthread_mutex_lock(&stmt->dbc->lock); { char buff[32 + NAME_LEN], *to; to= strmov(buff, "SHOW DATABASES LIKE '"); to+= mysql_real_escape_string(&stmt->dbc->mysql, to, (char *)catalog, catalog_len); to= strmov(to, "'"); MYLOG_QUERY(stmt, buff); if (!mysql_query(&stmt->dbc->mysql, buff)) stmt->result= mysql_store_result(&stmt->dbc->mysql); } pthread_mutex_unlock(&stmt->dbc->lock); if (!stmt->result) { return handle_connection_error(stmt); } stmt->order = SQLTABLES_qualifier_order; stmt->order_count = array_elements(SQLTABLES_qualifier_order); stmt->fix_fields = fix_fields_copy; stmt->array= (MYSQL_ROW) my_memdup((char *)SQLTABLES_qualifier_values, sizeof(SQLTABLES_qualifier_values), MYF(0)); if (!stmt->array) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } mysql_link_fields(stmt,SQLTABLES_fields,SQLTABLES_FIELDS); return SQL_SUCCESS; } if (!catalog_len && catalog && schema_len && !table_len && table) { /* Return set of allowed schemas (none) */ return create_fake_resultset(stmt, SQLTABLES_owner_values, sizeof(SQLTABLES_owner_values), 1, SQLTABLES_fields, SQLTABLES_FIELDS); } if (!catalog_len && catalog && !schema_len && schema && !table_len && table && type && !strncmp((char *)type, "%", 2)) { /* Return set of TableType qualifiers */ return create_fake_resultset(stmt, (MYSQL_ROW)SQLTABLES_type_values, sizeof(SQLTABLES_type_values), sizeof(SQLTABLES_type_values) / sizeof(SQLTABLES_type_values[0]), SQLTABLES_fields, SQLTABLES_FIELDS); } /* any other use of catalog="" returns an empty result */ if (catalog && !catalog_len) goto empty_set; user_tables= check_table_type(type, "TABLE", 5); views= check_table_type(type, "VIEW", 4); /* If no types specified, we want tables and views. */ if (!user_tables && !views) { if (!type_len) user_tables= views= 1; } if ((type_len && !views && !user_tables) || (schema_len && strncmp((char *)schema, "%", 2))) { /* Return empty set if unknown TableType or if schema is used and not '%' */ goto empty_set; } /* User Tables with type as 'TABLE' or 'VIEW' */ if (user_tables || views) { pthread_mutex_lock(&stmt->dbc->lock); stmt->result= mysql_table_status(stmt, catalog, catalog_len, table, table_len, TRUE, user_tables, views); if (!stmt->result && mysql_errno(&stmt->dbc->mysql)) { SQLRETURN rc; /* unknown DB will return empty set from SQLTables */ switch (mysql_errno(&stmt->dbc->mysql)) { case ER_BAD_DB_ERROR: pthread_mutex_unlock(&stmt->dbc->lock); goto empty_set; default: rc= handle_connection_error(stmt); pthread_mutex_unlock(&stmt->dbc->lock); return rc; } } pthread_mutex_unlock(&stmt->dbc->lock); } if (!stmt->result) goto empty_set; /* assemble final result set */ { MYSQL_ROW data= 0, row; char *db= ""; my_ulonglong row_count= stmt->result->row_count; if (!row_count) { mysql_free_result(stmt->result); goto empty_set; } if (!(stmt->result_array= (char **)my_malloc((uint)(sizeof(char *) * SQLTABLES_FIELDS * row_count), MYF(MY_ZEROFILL)))) { set_mem_error(&stmt->dbc->mysql); return handle_connection_error(stmt); } data= stmt->result_array; if (!stmt->dbc->ds->no_catalog) { if (!catalog) { if (!reget_current_catalog(stmt->dbc)) { const char *dbname= stmt->dbc->database ? stmt->dbc->database : "null"; db= strmake_root(&stmt->result->field_alloc, dbname, strlen(dbname)); } else { /* error was set in reget_current_catalog */ return SQL_ERROR; } } else db= strmake_root(&stmt->result->field_alloc, (char *)catalog, catalog_len); } while ((row= mysql_fetch_row(stmt->result))) { int type_index= 2; int comment_index= 1; my_bool view; /* TODO Check this condition is really needed here */ if (stmt->dbc->ds->no_information_schema || !server_has_i_s(stmt->dbc)) type_index= comment_index= (stmt->result->field_count == 18) ? 17 : 15; view= (myodbc_casecmp(row[type_index], "VIEW", 4) == 0); if ((view && !views) || (!view && !user_tables)) { --row_count; continue; } data[0]= db; data[1]= ""; data[2]= strdup_root(&stmt->result->field_alloc, row[0]); data[3]= view ? "VIEW" : "TABLE"; data[4]= strdup_root(&stmt->result->field_alloc, row[comment_index]); data+= SQLTABLES_FIELDS; } set_row_count(stmt, row_count); } mysql_link_fields(stmt, SQLTABLES_fields, SQLTABLES_FIELDS); return SQL_SUCCESS; empty_set: return create_empty_fake_resultset(stmt, SQLTABLES_values, sizeof(SQLTABLES_values), SQLTABLES_fields, SQLTABLES_FIELDS); } mysql-connector-odbc-5.1.10-src/driver/results.c100644 15766 12 144451 11707541005 20377 0ustar00cteamstaff/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file results.c @brief Result set and related information functions. */ #include "driver.h" #include #include #include #define SQL_MY_PRIMARY_KEY 1212 void reset_getdata_position(STMT *stmt) { stmt->getdata.column= (uint) ~0L; stmt->getdata.source= NULL; stmt->getdata.dst_bytes= (ulong) ~0L; stmt->getdata.dst_offset= (ulong) ~0L; stmt->getdata.src_offset= (ulong) ~0L; stmt->getdata.latest_bytes= stmt->getdata.latest_used= 0; } /* Verifies if C type is suitable for copying SQL_BINARY data http://msdn.microsoft.com/en-us/library/ms713559%28VS.85%29.aspx */ my_bool is_binary_ctype( SQLSMALLINT cType) { return (cType == SQL_C_CHAR || cType == SQL_C_BINARY || cType == SQL_C_WCHAR); } /* Converts binary(currently used for bit field only) to long long number.*/ void binary2numeric(long long *dst, char *src, uint srcLen) { *dst= 0; while (srcLen) { /* if source binary data is longer than 8 bytes(size of long long) we consider only minor 8 bytes */ if (srcLen > sizeof(long long)) continue; *dst+= (0xff & *src) << (--srcLen)*8; ++src; } } /* Function that verifies if conversion from given sql type to c type supported. Based on http://msdn.microsoft.com/en-us/library/ms709280%28VS.85%29.aspx and underlying pages. Currently checks conversions for MySQL BIT(n) field(SQL_BIT or SQL_BINARY) */ my_bool odbc_supported_conversion(SQLSMALLINT sqlType, SQLSMALLINT cType) { switch (sqlType) { case SQL_BIT: { switch (cType) { case SQL_C_DATE: case SQL_C_TYPE_DATE: case SQL_C_TIME: case SQL_C_TYPE_TIME: case SQL_C_TIMESTAMP: case SQL_C_TYPE_TIMESTAMP: return FALSE; } } case SQL_BINARY: { return is_binary_ctype(cType); } } return TRUE; } /* Conversion supported by driver as exception to odbc specs (i.e. to odbc_supported_conversion() results). e.g. we map bit(n>1) to SQL_BINARY, but provide its conversion to numeric types */ my_bool driver_supported_conversion(MYSQL_FIELD * field, SQLSMALLINT cType) { switch(field->type) { case MYSQL_TYPE_BIT: { switch (cType) { case SQL_C_BIT: case SQL_C_TINYINT: case SQL_C_STINYINT: case SQL_C_UTINYINT: case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: case SQL_C_LONG: case SQL_C_SLONG: case SQL_C_ULONG: case SQL_C_FLOAT: case SQL_C_DOUBLE: case SQL_C_SBIGINT: case SQL_C_UBIGINT: return TRUE; /* SQL_BIT should be converted SQL_C_NUMERIC, while SQL_BINARY should not */ case SQL_C_NUMERIC: return TRUE; } } } return FALSE; } SQLUSMALLINT sqlreturn2row_status(SQLRETURN res) { switch (res) { case SQL_SUCCESS: return SQL_ROW_SUCCESS; case SQL_SUCCESS_WITH_INFO: return SQL_ROW_SUCCESS_WITH_INFO; } return SQL_ROW_ERROR; } /** Retrieve the data from a field as a specified ODBC C type. TODO arrec->indicator_ptr could be different than pcbValue ideally, two separate pointers would be passed here @param[in] stmt Handle of statement @param[in] fCType ODBC C type to return data as @param[in] field Field describing the type of the data @param[out] rgbValue Pointer to buffer for returning data @param[in] cbValueMax Length of buffer @param[out] pcbValue Bytes used in the buffer, or SQL_NULL_DATA @param[out] value The field data to be converted and returned @param[in] length Length of value @param[in] arrec ARD record for this column (can be NULL) */ static SQLRETURN SQL_API sql_get_data(STMT *stmt, SQLSMALLINT fCType, MYSQL_FIELD *field, SQLPOINTER rgbValue, SQLLEN cbValueMax, SQLLEN *pcbValue, char *value, uint length, DESCREC *arrec) { SQLLEN tmp; long long numericValue; my_bool convert= 1; /* get the exact type if we don't already have it */ if (fCType == SQL_C_DEFAULT) { fCType= unireg_to_c_datatype(field); if (!cbValueMax) cbValueMax= bind_length(fCType, 0); } else if (fCType == SQL_ARD_TYPE) { if (!arrec) return set_stmt_error(stmt, "07009", "Invalid descriptor index", 0); fCType= arrec->concise_type; } /* set prec and scale for numeric */ if (fCType == SQL_C_NUMERIC && rgbValue) { SQL_NUMERIC_STRUCT *sqlnum= (SQL_NUMERIC_STRUCT *) rgbValue; if (arrec) /* normally set via ard */ { sqlnum->precision= (SQLSCHAR) arrec->precision; sqlnum->scale= (SQLCHAR) arrec->scale; } else /* just take the defaults */ { sqlnum->precision= 38; sqlnum->scale= 0; } } if (!value) { /* pcbValue must be available if its NULL */ if (!pcbValue) return set_stmt_error(stmt,"22002", "Indicator variable required but not supplied",0); *pcbValue= SQL_NULL_DATA; } else { if (!odbc_supported_conversion(get_sql_data_type(stmt, field, 0), fCType) && !driver_supported_conversion(field,fCType)) { return set_stmt_error(stmt, "07009", "Conversion is not possible", 0); } if (!pcbValue) pcbValue= &tmp; /* Easier code */ if (field->type == MYSQL_TYPE_BIT) { if (is_binary_ctype(fCType)) { return copy_binary_result(stmt, (SQLCHAR *)rgbValue, cbValueMax , pcbValue, field , value , length); } else { binary2numeric(&numericValue, value, length); convert= 0; } } switch (fCType) { case SQL_C_CHAR: /* Handle BLOB -> CHAR conversion */ if ((field->flags & (BLOB_FLAG|BINARY_FLAG)) == (BLOB_FLAG|BINARY_FLAG)) return copy_binhex_result(stmt, (SQLCHAR *)rgbValue, cbValueMax, pcbValue, field, value, length); /* fall through */ case SQL_C_BINARY: { char buff[21]; if (field->type == MYSQL_TYPE_TIMESTAMP && length != 19) { /* Convert MySQL timestamp to full ANSI timestamp format. */ char *pos; uint i; if (length == 6 || length == 10 || length == 12) { /* For two-digit year, < 60 is considered after Y2K */ if (value[0] <= '6') { buff[0]= '2'; buff[1]= '0'; } else { buff[0]= '1'; buff[1]= '9'; } } else { buff[0]= value[0]; buff[1]= value[1]; value+= 2; length-= 2; } buff[2]= *value++; buff[3]= *value++; buff[4]= '-'; if (value[0] == '0' && value[1] == '0') { /* Month was 0, which ODBC can't handle. */ *pcbValue= SQL_NULL_DATA; break; } pos= buff+5; length&= 30; /* Ensure that length is ok */ for (i= 1, length-= 2; (int)length > 0; length-= 2, ++i) { *pos++= *value++; *pos++= *value++; *pos++= i < 2 ? '-' : (i == 2) ? ' ' : ':'; } for ( ; pos != buff + 20; ++i) { *pos++= '0'; *pos++= '0'; *pos++= i < 2 ? '-' : (i == 2) ? ' ' : ':'; } value= buff; length= 19; } if (fCType == SQL_C_BINARY) return copy_binary_result(stmt, (SQLCHAR *)rgbValue, cbValueMax, pcbValue, field, value, length); else return copy_ansi_result(stmt, (SQLCHAR *)rgbValue, cbValueMax, pcbValue, field, value, length); } case SQL_C_WCHAR: return copy_wchar_result(stmt, (SQLWCHAR *)rgbValue, (SQLINTEGER)(cbValueMax / sizeof(SQLWCHAR)), pcbValue, field, value, length); case SQL_C_BIT: if (rgbValue) { /* for MySQL bit(n>1) 1st byte may be '\0'. So testing already converted to a number value or atoi for other types. */ if (!convert) *((char *)rgbValue)= numericValue > 0 ? '\1' : '\0'; else *((char *)rgbValue)= atoi(value) > 0 ? '\1' : '\0'; } *pcbValue= 1; break; case SQL_C_TINYINT: case SQL_C_STINYINT: if (rgbValue) *((SQLSCHAR *)rgbValue)= (SQLSCHAR)(convert ? atoi(value) : (numericValue & (SQLSCHAR)(-1))); *pcbValue= 1; break; case SQL_C_UTINYINT: if (rgbValue) *((SQLCHAR *)rgbValue)= (SQLCHAR)(unsigned int)(convert ? atoi(value) : (numericValue & (SQLCHAR)(-1))); *pcbValue= 1; break; case SQL_C_SHORT: case SQL_C_SSHORT: if (rgbValue) *((SQLSMALLINT *)rgbValue)= (SQLSMALLINT)(convert ? atoi(value) : (numericValue & (SQLUSMALLINT)(-1))); *pcbValue= sizeof(SQLSMALLINT); break; case SQL_C_USHORT: if (rgbValue) *((SQLUSMALLINT *)rgbValue)= (SQLUSMALLINT)(uint)(convert ? atol(value) : (numericValue & (SQLUSMALLINT)(-1))); *pcbValue= sizeof(SQLUSMALLINT); break; case SQL_C_LONG: case SQL_C_SLONG: if (rgbValue) { /* Check if it could be a date...... :) */ if (convert) if (length >= 10 && value[4] == '-' && value[7] == '-' && (!value[10] || value[10] == ' ')) { *((SQLINTEGER *)rgbValue)= ((SQLINTEGER) atol(value) * 10000L + (SQLINTEGER) atol(value + 5) * 100L + (SQLINTEGER) atol(value + 8)); } else *((SQLINTEGER *)rgbValue)= (SQLINTEGER) atol(value); else *((SQLINTEGER *)rgbValue)= (SQLINTEGER)(numericValue & (SQLUINTEGER)(-1)); } *pcbValue= sizeof(SQLINTEGER); break; case SQL_C_ULONG: if (rgbValue) *((SQLUINTEGER *)rgbValue)= (SQLUINTEGER)(convert ? strtoul(value, NULL, 10) : numericValue & (SQLUINTEGER)(-1)); *pcbValue= sizeof(SQLUINTEGER); break; case SQL_C_FLOAT: if (rgbValue) *((float *)rgbValue)= (float)(convert ? atof(value) : numericValue & (int)(-1)); *pcbValue= sizeof(float); break; case SQL_C_DOUBLE: if (rgbValue) *((double *)rgbValue)= (double)(convert ? strtod(value, NULL) : numericValue); *pcbValue= sizeof(double); break; case SQL_C_DATE: case SQL_C_TYPE_DATE: { SQL_DATE_STRUCT tmp_date; if (!rgbValue) rgbValue= (char *)&tmp_date; if (!str_to_date((SQL_DATE_STRUCT *)rgbValue, value, length, stmt->dbc->ds->zero_date_to_min)) *pcbValue= sizeof(SQL_DATE_STRUCT); else *pcbValue= SQL_NULL_DATA; /* ODBC can't handle 0000-00-00 dates */ break; } case SQL_C_TIME: case SQL_C_TYPE_TIME: if (field->type == MYSQL_TYPE_TIMESTAMP || field->type == MYSQL_TYPE_DATETIME) { SQL_TIMESTAMP_STRUCT ts; if (str_to_ts(&ts, value, stmt->dbc->ds->zero_date_to_min)) *pcbValue= SQL_NULL_DATA; else { SQL_TIME_STRUCT *time_info= (SQL_TIME_STRUCT *)rgbValue; if (time_info) { time_info->hour= ts.hour; time_info->minute= ts.minute; time_info->second= ts.second; } *pcbValue= sizeof(TIME_STRUCT); } } else if (field->type == MYSQL_TYPE_DATE) { SQL_TIME_STRUCT *time_info= (SQL_TIME_STRUCT *)rgbValue; if (time_info) { time_info->hour= 0; time_info->minute= 0; time_info->second= 0; } *pcbValue= sizeof(TIME_STRUCT); } else { SQL_TIME_STRUCT ts; if (str_to_time_st(&ts, value)) *pcbValue= SQL_NULL_DATA; else { SQL_TIME_STRUCT *time_info= (SQL_TIME_STRUCT *)rgbValue; if (time_info) { time_info->hour= ts.hour; time_info->minute= ts.minute; time_info->second= ts.second; } *pcbValue= sizeof(TIME_STRUCT); } } break; case SQL_C_TIMESTAMP: case SQL_C_TYPE_TIMESTAMP: if (field->type == MYSQL_TYPE_TIME) { SQL_TIME_STRUCT ts; if (str_to_time_st(&ts, value)) *pcbValue= SQL_NULL_DATA; else { SQL_TIMESTAMP_STRUCT *timestamp_info= (SQL_TIMESTAMP_STRUCT *)rgbValue; time_t sec_time= time(NULL); struct tm cur_tm; localtime_r(&sec_time, &cur_tm); timestamp_info->year= 1900 + cur_tm.tm_year; timestamp_info->month= 1 + cur_tm.tm_mon; /* January is 0 in tm */ timestamp_info->day= cur_tm.tm_mday; timestamp_info->hour= ts.hour; timestamp_info->minute= ts.minute; timestamp_info->second= ts.second; timestamp_info->fraction= 0; *pcbValue= sizeof(SQL_TIMESTAMP_STRUCT); } } else { if (str_to_ts((SQL_TIMESTAMP_STRUCT *)rgbValue, value, stmt->dbc->ds->zero_date_to_min)) *pcbValue= SQL_NULL_DATA; else *pcbValue= sizeof(SQL_TIMESTAMP_STRUCT); } break; case SQL_C_SBIGINT: /** @todo This is not right. SQLBIGINT is not always longlong. */ if (rgbValue) *((longlong *)rgbValue)= (longlong)(convert ? strtoll(value, NULL, 10) : numericValue); *pcbValue= sizeof(longlong); break; case SQL_C_UBIGINT: /** @todo This is not right. SQLUBIGINT is not always ulonglong. */ if (rgbValue) *((ulonglong *)rgbValue)= (ulonglong)(convert ? strtoull(value, NULL, 10) : numericValue); *pcbValue= sizeof(ulonglong); break; case SQL_C_NUMERIC: { int overflow= 0; SQL_NUMERIC_STRUCT *sqlnum= (SQL_NUMERIC_STRUCT *) rgbValue; if (rgbValue) { if (convert) sqlnum_from_str(value, sqlnum, &overflow); else /* bit field */ { /* Lazy way - converting number we have to a string. If it couldn't happen we have to scale/unscale number - we would just reverse binary data */ char _value[21]; /* max string length of 64bit number */ sprintf(_value, "%llu", numericValue); sqlnum_from_str(_value, sqlnum, &overflow); } } *pcbValue= sizeof(ulonglong); if (overflow) return set_stmt_error(stmt, "22003", "Numeric value out of range", 0); } break; default: return set_error(stmt,MYERR_07006, "Restricted data type attribute violation",0); break; } } if (stmt->getdata.source) /* Second call to getdata */ return SQL_NO_DATA_FOUND; stmt->getdata.source= NULL; /* All data is retrieved */ return SQL_SUCCESS; } /*! \brief Returns true if we are dealing with a statement which is likely to result in reading only (SELECT || SHOW). Some ODBC calls require knowledge about a statement which we can not determine until we have executed the statement. This is because we do not parse the SQL - the server does. However if we silently execute a pending statement we may insert rows. So we do a very crude check of the SQL here to reduce the chance of a problem. \sa BUG 5778 */ BOOL isStatementForRead( STMT FAR *stmt ) { char *pCursor; int n = 0; char szToken[55]; if ( !stmt ) return FALSE; if ( !stmt->query ) return FALSE; /* eat up any space */ for ( pCursor = stmt->query; pCursor != stmt->query_end && isspace( *pCursor ); ) { ++pCursor; } /* continue while alpha-numeric */ for ( ; pCursor != stmt->query_end && !isspace( *pCursor ) && n < 50; ) { szToken[n] = toupper( *pCursor ); ++pCursor; ++n; } szToken[n] = '\0'; /* Took from Jess' "ssps rsmd" patch - "CALL" was missing here */ if (!strcmp(szToken, "SELECT") || !strcmp(szToken, "SHOW") || !strcmp(szToken, "CALL")) return TRUE; return FALSE; } /* @type : myodbc3 internal @purpose : execute the query if it is only prepared. This is needed because the ODBC standard allows calling some functions before SQLExecute(). */ static SQLRETURN check_result(STMT FAR *stmt ) { SQLRETURN error= 0; switch ( stmt->state ) { case ST_UNKNOWN: error= set_stmt_error(stmt,"24000","Invalid cursor state",0); break; case ST_PREPARED: if ( isStatementForRead( stmt ) ) { SQLULEN real_max_rows= stmt->stmt_options.max_rows; stmt->stmt_options.max_rows= 1; /* select limit will be restored back to max_rows before real execution */ if ( (error= my_SQLExecute(stmt)) == SQL_SUCCESS ) { stmt->state= ST_PRE_EXECUTED; /* mark for execute */ } else { set_sql_select_limit(stmt->dbc, real_max_rows); } stmt->stmt_options.max_rows= real_max_rows; } else error = SQL_SUCCESS; break; case ST_PRE_EXECUTED: case ST_EXECUTED: error= SQL_SUCCESS; } return(error); } /* @type : myodbc3 internal @purpose : does the any open param binding */ SQLRETURN do_dummy_parambind(SQLHSTMT hstmt) { SQLRETURN rc; STMT FAR *stmt= (STMT FAR *)hstmt; uint nparam; for ( nparam= 0; nparam < stmt->param_count; ++nparam ) { DESCREC *aprec= desc_get_rec(stmt->apd, nparam, TRUE); if (!aprec->par.real_param_done) { /* do the dummy bind temporarily to get the result set and once everything is done, remove it */ if (!SQL_SUCCEEDED(rc= my_SQLBindParameter(hstmt, nparam+1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 0, 0, "NULL", SQL_NTS, NULL))) return rc; /* reset back to false (this is the *dummy* param bind) */ aprec->par.real_param_done= FALSE; } } stmt->dummy_state= ST_DUMMY_PREPARED; return(SQL_SUCCESS); } /* @type : ODBC 1.0 API @purpose : returns the number of columns in a result set */ SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT hstmt, SQLSMALLINT FAR *pccol) { SQLRETURN error; STMT FAR *stmt= (STMT FAR*) hstmt; if ( stmt->param_count > 0 && stmt->dummy_state == ST_DUMMY_UNKNOWN && (stmt->state != ST_PRE_EXECUTED || stmt->state != ST_EXECUTED) ) { if ( do_dummy_parambind(hstmt) != SQL_SUCCESS ) return SQL_ERROR; } if ( (error= check_result(stmt)) != SQL_SUCCESS ) return error; if ( !stmt->result ) *pccol= 0; /* Not a select */ else *pccol= stmt->result->field_count; return SQL_SUCCESS; } /** Get some basic properties of a column. @param[in] hstmt Statement handle @param[in] column Column number (starting with 1) @param[out] name Pointer to column name @param[out] need_free Whether the column name needs to be freed. @param[out] type Column SQL type @param[out] size Column size @param[out] scale Scale @param[out] nullable Whether the column is nullable */ SQLRETURN SQL_API MySQLDescribeCol(SQLHSTMT hstmt, SQLUSMALLINT column, SQLCHAR **name, SQLSMALLINT *need_free, SQLSMALLINT *type, SQLULEN *size, SQLSMALLINT *scale, SQLSMALLINT *nullable) { SQLRETURN error; STMT *stmt= (STMT *)hstmt; DESCREC* irrec; /* SQLDescribeCol can be called before SQLExecute. Thus we need make sure that all parameters have been bound */ if ( stmt->param_count > 0 && stmt->dummy_state == ST_DUMMY_UNKNOWN && (stmt->state != ST_PRE_EXECUTED || stmt->state != ST_EXECUTED) ) { if ( do_dummy_parambind(hstmt) != SQL_SUCCESS ) return SQL_ERROR; } if ((error= check_result(stmt)) != SQL_SUCCESS) return error; if (!stmt->result) return set_stmt_error(stmt, "07005", "No result set", 0); if (column == 0 || column > stmt->ird->count) return set_stmt_error(stmt, "07009", "Invalid descriptor index", 0); irrec= desc_get_rec(stmt->ird, column - 1, FALSE); assert(irrec); if (type) *type= irrec->concise_type; if (size) *size= irrec->length; if (scale) *scale= irrec->scale; if (nullable) *nullable= irrec->nullable; *need_free= 0; if (stmt->dbc->ds->return_table_names_for_SqlDescribeCol && irrec->table_name) { char *tmp= my_malloc(strlen((char *)irrec->name) + strlen((char *)irrec->table_name) + 2, MYF(0)); if (!tmp) { *need_free= -1; *name= NULL; } else { strxmov(tmp, (char *)irrec->table_name, ".", (char *)irrec->name, NullS); *name= (SQLCHAR *)tmp; *need_free= 1; } } else *name= (SQLCHAR *)irrec->name; return SQL_SUCCESS; } /* Retrieve an attribute of a column in a result set. @param[in] hstmt Handle to statement @param[in] column The column to retrieve data for, indexed from 1 @param[in] attrib The attribute to be retrieved @param[out] char_attr Pointer to a string pointer for returning strings (caller must make their own copy) @param[out] num_attr Pointer to an integer to return the value if the @a attrib corresponds to a numeric type @since ODBC 1.0 */ SQLRETURN SQL_API MySQLColAttribute(SQLHSTMT hstmt, SQLUSMALLINT column, SQLUSMALLINT attrib, SQLCHAR **char_attr, SQLLEN *num_attr) { STMT *stmt= (STMT *)hstmt; SQLLEN nparam= 0; SQLRETURN error= SQL_SUCCESS; DESCREC *irrec; /* MySQLColAttribute can be called before SQLExecute. Thus we need make sure that all parameters have been bound */ if ( stmt->param_count > 0 && stmt->dummy_state == ST_DUMMY_UNKNOWN && (stmt->state != ST_PRE_EXECUTED || stmt->state != ST_EXECUTED) ) { if ( do_dummy_parambind(hstmt) != SQL_SUCCESS ) return SQL_ERROR; } if (check_result(stmt) != SQL_SUCCESS) return SQL_ERROR; if (!stmt->result) return set_stmt_error(stmt, "07005", "No result set", 0); /* we report bookmark type if requested, nothing else */ if (attrib == SQL_DESC_TYPE && column == 0) { *(SQLINTEGER *)num_attr= SQL_INTEGER; return SQL_SUCCESS; } if (column == 0 || column > stmt->ird->count) return set_error(hstmt, MYERR_07009, NULL, 0); if (!num_attr) num_attr= &nparam; if ((error= check_result(stmt)) != SQL_SUCCESS) return error; if (attrib == SQL_DESC_COUNT || attrib == SQL_COLUMN_COUNT) { *num_attr= stmt->ird->count; return SQL_SUCCESS; } irrec= desc_get_rec(stmt->ird, column - 1, FALSE); assert(irrec); /* Map to descriptor fields. This approach is only valid for ODBC 3.0 API applications. @todo Add additional logic to properly handle these fields for ODBC 2.0 API applications. */ switch (attrib) { case SQL_COLUMN_SCALE: attrib= SQL_DESC_SCALE; break; case SQL_COLUMN_PRECISION: attrib= SQL_DESC_PRECISION; break; case SQL_COLUMN_NULLABLE: attrib= SQL_DESC_NULLABLE; break; case SQL_COLUMN_LENGTH: attrib= SQL_DESC_OCTET_LENGTH; break; case SQL_COLUMN_NAME: attrib= SQL_DESC_NAME; break; } switch (attrib) { case SQL_DESC_AUTO_UNIQUE_VALUE: case SQL_DESC_CASE_SENSITIVE: case SQL_DESC_FIXED_PREC_SCALE: case SQL_DESC_NULLABLE: case SQL_DESC_NUM_PREC_RADIX: case SQL_DESC_PRECISION: case SQL_DESC_SCALE: case SQL_DESC_SEARCHABLE: case SQL_DESC_TYPE: case SQL_DESC_CONCISE_TYPE: case SQL_DESC_UNNAMED: case SQL_DESC_UNSIGNED: case SQL_DESC_UPDATABLE: error= stmt_SQLGetDescField(stmt, stmt->ird, column, attrib, #ifdef USE_SQLCOLATTRIBUTE_SQLLEN_PTR (SQLPOINTER)num_attr,SQL_IS_LEN, #else num_attr, SQL_IS_INTEGER, #endif NULL); break; case SQL_DESC_DISPLAY_SIZE: case SQL_DESC_LENGTH: case SQL_DESC_OCTET_LENGTH: error= stmt_SQLGetDescField(stmt, stmt->ird, column, attrib, num_attr, SQL_IS_LEN, NULL); break; /* We need support from server, when aliasing is there */ case SQL_DESC_BASE_COLUMN_NAME: *char_attr= irrec->base_column_name ? irrec->base_column_name : (SQLCHAR *) ""; break; case SQL_DESC_LABEL: case SQL_DESC_NAME: *char_attr= irrec->name; break; case SQL_DESC_BASE_TABLE_NAME: *char_attr= irrec->base_table_name ? irrec->base_table_name : (SQLCHAR *) ""; break; case SQL_DESC_TABLE_NAME: *char_attr= irrec->table_name ? irrec->table_name : (SQLCHAR *) ""; break; case SQL_DESC_CATALOG_NAME: *char_attr= irrec->catalog_name; break; case SQL_DESC_LITERAL_PREFIX: *char_attr= irrec->literal_prefix; break; case SQL_DESC_LITERAL_SUFFIX: *char_attr= irrec->literal_suffix; break; case SQL_DESC_SCHEMA_NAME: *char_attr= irrec->schema_name; break; case SQL_DESC_TYPE_NAME: *char_attr= irrec->type_name; break; /* Hack : Fix for the error from ADO 'rs.resync' "Key value for this row was changed or deleted at the data store. The local row is now deleted. This should also fix some Multi-step generated error cases from ADO */ case SQL_MY_PRIMARY_KEY: /* MSSQL extension !! */ *(SQLINTEGER *)num_attr= ((irrec->row.field->flags & PRI_KEY_FLAG) ? SQL_TRUE : SQL_FALSE); break; default: return set_stmt_error(stmt, "HY091", "Invalid descriptor field identifier",0); } return error; } /* @type : ODBC 1.0 API @purpose : binds application data buffers to columns in the result set */ SQLRETURN SQL_API SQLBindCol(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLLEN BufferLength, SQLLEN * StrLen_or_IndPtr) { SQLRETURN rc; STMT FAR *stmt= (STMT FAR*) StatementHandle; DESCREC *arrec; /* TODO if this function fails, the SQL_DESC_COUNT should be unchanged in ard */ CLEAR_STMT_ERROR(stmt); if (!TargetValuePtr && !StrLen_or_IndPtr) /* Handling unbinding */ { /* If unbinding the last bound column, we reduce the ARD records until the highest remaining bound column. */ if (ColumnNumber == stmt->ard->count) { int i; --stmt->ard->count; for (i= (int)stmt->ard->count - 1; i >= 0; --i) { arrec= desc_get_rec(stmt->ard, i, FALSE); if (ARD_IS_BOUND(arrec)) break; else --stmt->ard->count; } } else { arrec= desc_get_rec(stmt->ard, ColumnNumber - 1, FALSE); if (arrec) { arrec->data_ptr= NULL; arrec->octet_length_ptr= NULL; } } return SQL_SUCCESS; } if (ColumnNumber == 0 || (stmt->state == ST_EXECUTED && ColumnNumber > stmt->ird->count)) { return set_stmt_error(stmt, "07009", "Invalid descriptor index", MYERR_07009); } arrec= desc_get_rec(stmt->ard, ColumnNumber - 1, TRUE); if ((rc= stmt_SQLSetDescField(stmt, stmt->ard, ColumnNumber, SQL_DESC_CONCISE_TYPE, (SQLPOINTER)(SQLINTEGER) TargetType, SQL_IS_SMALLINT)) != SQL_SUCCESS) return rc; if ((rc= stmt_SQLSetDescField(stmt, stmt->ard, ColumnNumber, SQL_DESC_OCTET_LENGTH, (SQLPOINTER) bind_length(TargetType, BufferLength), SQL_IS_LEN)) != SQL_SUCCESS) return rc; if ((rc= stmt_SQLSetDescField(stmt, stmt->ard, ColumnNumber, SQL_DESC_DATA_PTR, TargetValuePtr, SQL_IS_POINTER)) != SQL_SUCCESS) return rc; if ((rc= stmt_SQLSetDescField(stmt, stmt->ard, ColumnNumber, SQL_DESC_INDICATOR_PTR, StrLen_or_IndPtr, SQL_IS_POINTER)) != SQL_SUCCESS) return rc; if ((rc= stmt_SQLSetDescField(stmt, stmt->ard, ColumnNumber, SQL_DESC_OCTET_LENGTH_PTR, StrLen_or_IndPtr, SQL_IS_POINTER)) != SQL_SUCCESS) return rc; return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : returns the latest resultset(dynamic) */ my_bool set_dynamic_result(STMT FAR *stmt) { SQLRETURN rc; long row= stmt->current_row; uint rows= stmt->rows_found_in_set; rc= my_SQLExecute(stmt); stmt->current_row= row; stmt->rows_found_in_set= rows; if (SQL_SUCCEEDED(rc)) set_current_cursor_data(stmt,0); return rc != 0; } /* @type : ODBC 1.0 API @purpose : retrieves data for a single column in the result set. It can be called multiple times to retrieve variable-length data in parts */ SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLLEN BufferLength, SQLLEN * StrLen_or_IndPtr) { STMT *stmt= (STMT *) StatementHandle; SQLRETURN result; uint length= 0; DESCREC *irrec; if (!stmt->result || !stmt->current_values) { set_stmt_error(stmt,"24000","SQLGetData without a preceding SELECT",0); return SQL_ERROR; } if (ColumnNumber < 1 || ColumnNumber > stmt->ird->count) { return set_stmt_error(stmt, "07009", "Invalid descriptor index", MYERR_07009); } --ColumnNumber; /* Easier code if start from 0 */ if (ColumnNumber != stmt->getdata.column) { /* New column. Reset old offset */ reset_getdata_position(stmt); stmt->getdata.column= ColumnNumber; } irrec= desc_get_rec(stmt->ird, ColumnNumber, FALSE); assert(irrec); /* catalog functions with "fake" results won't have lengths */ length= irrec->row.datalen; if (!length && stmt->current_values[ColumnNumber]) length= strlen(stmt->current_values[ColumnNumber]); if (!stmt->dbc->ds->dont_use_set_locale) setlocale(LC_NUMERIC, "C"); result= sql_get_data(stmt, TargetType, irrec->row.field, TargetValuePtr, BufferLength, StrLen_or_IndPtr, stmt->current_values[ColumnNumber], length, desc_get_rec(stmt->ard, ColumnNumber, FALSE)); if (!stmt->dbc->ds->dont_use_set_locale) setlocale(LC_NUMERIC,default_locale); return result; } /* @type : ODBC 1.0 API @purpose : determines whether more results are available on a statement containing SELECT, UPDATE, INSERT, or DELETE statements and, if so, initializes processing for those results */ SQLRETURN SQL_API SQLMoreResults( SQLHSTMT hStmt ) { STMT FAR * pStmt = (STMT FAR*)hStmt; int nRetVal; SQLRETURN nReturn = SQL_SUCCESS; pthread_mutex_lock( &pStmt->dbc->lock ); CLEAR_STMT_ERROR( pStmt ); if (!mysql_more_results(&pStmt->dbc->mysql)) { nReturn= SQL_NO_DATA; goto exitSQLMoreResults; } /* SQLExecute or SQLExecDirect need to be called first */ if ( pStmt->state != ST_EXECUTED ) { nReturn = set_stmt_error( pStmt, "HY010", NULL, 0 ); goto exitSQLMoreResults; } /* try to get next resultset */ nRetVal = mysql_next_result( &pStmt->dbc->mysql ); /* call to mysql_next_result() failed */ if ( nRetVal > 0 ) { nRetVal = mysql_errno( &pStmt->dbc->mysql ); switch ( nRetVal ) { case CR_SERVER_GONE_ERROR: case CR_SERVER_LOST: nReturn = set_stmt_error( pStmt, "08S01", mysql_error( &pStmt->dbc->mysql ), nRetVal ); goto exitSQLMoreResults; case CR_COMMANDS_OUT_OF_SYNC: case CR_UNKNOWN_ERROR: nReturn = set_stmt_error( pStmt, "HY000", mysql_error( &pStmt->dbc->mysql ), nRetVal ); goto exitSQLMoreResults; default: nReturn = set_stmt_error( pStmt, "HY000", "unhandled error from mysql_next_result()", nRetVal ); goto exitSQLMoreResults; } } /* no more resultsets */ if ( nRetVal < 0 ) { nReturn = SQL_NO_DATA; goto exitSQLMoreResults; } /* cleanup existing resultset */ nReturn = my_SQLFreeStmtExtended((SQLHSTMT)pStmt,SQL_CLOSE,0); if ( !SQL_SUCCEEDED( nReturn ) ) goto exitSQLMoreResults; /* start using the new resultset */ if ( if_forward_cache( pStmt ) ) pStmt->result = mysql_use_result( &pStmt->dbc->mysql ); else pStmt->result = mysql_store_result( &pStmt->dbc->mysql ); if ( !pStmt->result ) { /* no fields means; INSERT, UPDATE or DELETE so no resultset is fine */ if ( !mysql_field_count( &pStmt->dbc->mysql ) ) { pStmt->state = ST_EXECUTED; pStmt->affected_rows = mysql_affected_rows( &pStmt->dbc->mysql ); goto exitSQLMoreResults; } /* we have fields but no resultset (not even an empty one) - this is bad */ nReturn = set_stmt_error( pStmt, "HY000", mysql_error( &pStmt->dbc->mysql ), mysql_errno( &pStmt->dbc->mysql ) ); goto exitSQLMoreResults; } fix_result_types( pStmt ); exitSQLMoreResults: pthread_mutex_unlock( &pStmt->dbc->lock ); return nReturn; } /* @type : ODBC 1.0 API @purpose : returns the number of rows affected by an UPDATE, INSERT, or DELETE statement;an SQL_ADD, SQL_UPDATE_BY_BOOKMARK, or SQL_DELETE_BY_BOOKMARK operation in SQLBulkOperations; or an SQL_UPDATE or SQL_DELETE operation in SQLSetPos */ SQLRETURN SQL_API SQLRowCount( SQLHSTMT hstmt, SQLLEN * pcrow ) { STMT FAR *stmt= (STMT FAR*) hstmt; if ( stmt->result ) { *pcrow= (SQLLEN) mysql_affected_rows(&stmt->dbc->mysql); } else { *pcrow= (SQLLEN) stmt->affected_rows; } return SQL_SUCCESS; } /** Populate the data lengths in the IRD for the current row @param[in] ird IRD to populate @param[in] lengths Data lengths from mysql_fetch_lengths() @param[in] fields Number of fields */ static void fill_ird_data_lengths(DESC *ird, ulong *lengths, uint fields) { uint i; DESCREC *irrec; assert(fields == ird->count); /* This will be NULL for catalog functions with "fake" results */ if (!lengths) return; for (i= 0; i < fields; ++i) { irrec= desc_get_rec(ird, i, FALSE); assert(irrec); irrec->row.datalen= lengths[i]; } } /** Populate a single row of fetch buffers @param[in] stmt Handle of statement @param[in] values Row buffers from libmysql @param[in] rownum Row number of current fetch block */ static SQLRETURN fill_fetch_buffers(STMT *stmt, MYSQL_ROW values, uint rownum) { SQLRETURN res= SQL_SUCCESS, tmp_res; int i; uint length= 0; DESCREC *irrec, *arrec; for (i= 0; i < myodbc_min(stmt->ird->count, stmt->ard->count); ++i, ++values) { irrec= desc_get_rec(stmt->ird, i, FALSE); arrec= desc_get_rec(stmt->ard, i, FALSE); assert(irrec && arrec); if (ARD_IS_BOUND(arrec)) { SQLLEN offset, pcb_offset; SQLLEN *pcbValue= NULL; SQLPOINTER TargetValuePtr= NULL; if (stmt->ard->bind_type == SQL_BIND_BY_COLUMN) { offset= arrec->octet_length * rownum; pcb_offset= sizeof(SQLLEN) * rownum; } else pcb_offset= offset= stmt->ard->bind_type * rownum; /* apply SQL_ATTR_ROW_BIND_OFFSET_PTR */ if (stmt->ard->bind_offset_ptr) { offset += *stmt->ard->bind_offset_ptr; pcb_offset += *stmt->ard->bind_offset_ptr; } reset_getdata_position(stmt); if (arrec->data_ptr) TargetValuePtr= ((char*) arrec->data_ptr) + offset; /* catalog functions with "fake" results won't have lengths */ length= irrec->row.datalen; if (!length && *values) length= strlen(*values); /* We need to pass that pointer to the sql_get_data so it could detect 22002 error - for NULL values that pointer has to be supplied by user. */ if (arrec->octet_length_ptr) { pcbValue= arrec->octet_length_ptr + (pcb_offset / sizeof(SQLLEN)); } tmp_res= sql_get_data(stmt, arrec->concise_type, irrec->row.field, TargetValuePtr, arrec->octet_length, pcbValue, *values, length, arrec); if (tmp_res != SQL_SUCCESS) { if (tmp_res == SQL_SUCCESS_WITH_INFO) { if (res == SQL_SUCCESS) res= tmp_res; } else res= SQL_ERROR; } } } return res; } /* @type : myodbc3 internal @purpose : fetches the specified rowset of data from the result set and returns data for all bound columns. Rowsets can be specified at an absolute or relative position */ SQLRETURN SQL_API my_SQLExtendedFetch( SQLHSTMT hstmt, SQLUSMALLINT fFetchType, SQLLEN irow, SQLULEN *pcrow, SQLUSMALLINT FAR *rgfRowStatus, my_bool upd_status ) { SQLULEN rows_to_fetch; long cur_row, max_row; SQLULEN i; SQLRETURN row_res, res; STMT FAR *stmt= (STMT FAR*) hstmt; MYSQL_ROW values= 0; MYSQL_ROW_OFFSET save_position; SQLULEN dummy_pcrow; BOOL disconnected= FALSE; LINT_INIT(save_position); if ( !stmt->result ) return set_stmt_error(stmt, "24000", "Fetch without a SELECT", 0); cur_row = stmt->current_row; if ( stmt->stmt_options.cursor_type == SQL_CURSOR_FORWARD_ONLY ) { if ( fFetchType != SQL_FETCH_NEXT && !stmt->dbc->ds->safe ) return set_error(stmt,MYERR_S1106, "Wrong fetchtype with FORWARD ONLY cursor", 0); } if ( if_dynamic_cursor(stmt) && set_dynamic_result(stmt) ) return set_error(stmt,MYERR_S1000, "Driver Failed to set the internal dynamic result", 0); if ( !pcrow ) pcrow= &dummy_pcrow; max_row= (long) mysql_num_rows(stmt->result); reset_getdata_position(stmt); stmt->current_values= 0; /* For SQLGetData */ switch ( fFetchType ) { case SQL_FETCH_NEXT: cur_row= (stmt->current_row < 0 ? 0 : stmt->current_row+stmt->rows_found_in_set); break; case SQL_FETCH_PRIOR: cur_row= (stmt->current_row <= 0 ? -1 : (long)(stmt->current_row - stmt->ard->array_size)); break; case SQL_FETCH_FIRST: cur_row= 0L; break; case SQL_FETCH_LAST: cur_row= max_row - stmt->ard->array_size; break; case SQL_FETCH_ABSOLUTE: if ( irow < 0 ) { /* Fetch from end of result set */ if ( max_row+irow < 0 && -irow <= (long) stmt->ard->array_size ) { /* | FetchOffset | > LastResultRow AND | FetchOffset | <= RowsetSize */ cur_row= 0; /* Return from beginning */ } else cur_row= max_row+irow; /* Ok if max_row <= -irow */ } else cur_row= (long) irow-1; break; case SQL_FETCH_RELATIVE: cur_row= stmt->current_row + irow; if ( stmt->current_row > 0 && cur_row < 0 && (long)-irow <= (long)stmt->ard->array_size ) cur_row= 0; break; default: return set_error(stmt, MYERR_S1106, "Fetch type out of range", 0); } if ( cur_row < 0 ) { stmt->current_row= -1; /* Before first row */ stmt->rows_found_in_set= 0; mysql_data_seek(stmt->result,0L); return SQL_NO_DATA_FOUND; } if ( cur_row > max_row ) cur_row= max_row; if ( !stmt->result_array && !if_forward_cache(stmt) ) { /* If Dynamic, it loses the stmt->end_of_set, so seek to desired row, might have new data or might be deleted */ if ( stmt->stmt_options.cursor_type != SQL_CURSOR_DYNAMIC && cur_row && cur_row == (long)(stmt->current_row + stmt->rows_found_in_set) ) mysql_row_seek(stmt->result,stmt->end_of_set); else mysql_data_seek(stmt->result,cur_row); } stmt->current_row= cur_row; if (if_forward_cache(stmt) && !stmt->result_array) rows_to_fetch= stmt->ard->array_size; else rows_to_fetch= myodbc_min(max_row-cur_row, (long)stmt->ard->array_size); if ( !rows_to_fetch ) { *pcrow= 0; stmt->rows_found_in_set= 0; if ( upd_status && stmt->ird->rows_processed_ptr ) { *stmt->ird->rows_processed_ptr= 0; } return SQL_NO_DATA_FOUND; } if (!stmt->dbc->ds->dont_use_set_locale) { setlocale(LC_NUMERIC, "C"); } res= SQL_SUCCESS; for (i= 0 ; i < rows_to_fetch ; ++i) { if ( stmt->result_array ) { values= stmt->result_array+cur_row*stmt->result->field_count; if ( i == 0 ) { stmt->current_values= values; } } else { /* This code will ensure that values is always set */ if ( i == 0 ) { save_position= mysql_row_tell(stmt->result); } if ( !(values= mysql_fetch_row(stmt->result)) ) { break; } if ( stmt->fix_fields ) { values= (*stmt->fix_fields)(stmt,values); } stmt->current_values= values; } if (!stmt->fix_fields) { /* lengths contains lengths for all rows. Alternate use could be filling ird buffers in the (fix_fields) function. In this case lengths could contain just one array with rules for lengths calculating(it can work out in many cases like in catalog functions there some fields from results of auxiliary query are simply mixed somehow and constant fields added ). Another approach could be using of "array" and "order" arrays and special fix_fields callback, that will fix array and set lengths in ird*/ if (stmt->lengths) fill_ird_data_lengths(stmt->ird, stmt->lengths + cur_row*stmt->result->field_count, stmt->result->field_count); else fill_ird_data_lengths(stmt->ird, mysql_fetch_lengths(stmt->result), stmt->result->field_count); } row_res= fill_fetch_buffers(stmt, values, i); /* For SQL_SUCCESS we need all rows to be SQL_SUCCESS */ if (res != row_res) { /* Any successful row makes overall result SQL_SUCCESS_WITH_INFO */ if (SQL_SUCCEEDED(row_res)) { res= SQL_SUCCESS_WITH_INFO; } /* Else error */ else if (i == 0) { /* SQL_ERROR only if all rows fail */ res= SQL_ERROR; } else { res= SQL_SUCCESS_WITH_INFO; } } /* "Fetching" includes buffers filling. I think errors in that have to affect row status */ if (rgfRowStatus) { rgfRowStatus[i]= sqlreturn2row_status(row_res); } /* No need to update rowStatusPtr_ex, it's the same as rgfRowStatus. */ if (upd_status && stmt->ird->array_status_ptr) { stmt->ird->array_status_ptr[i]= sqlreturn2row_status(row_res); } ++cur_row; } /* fetching cycle end*/ stmt->rows_found_in_set= i; *pcrow= i; disconnected= is_connection_lost(mysql_errno(&stmt->dbc->mysql)) && handle_connection_error(stmt); if ( upd_status && stmt->ird->rows_processed_ptr ) { *stmt->ird->rows_processed_ptr= i; } /* It is possible that both rgfRowStatus and array_status_ptr are set (and upp_status is TRUE) */ for ( ; i < stmt->ard->array_size ; ++i ) { if ( rgfRowStatus ) { rgfRowStatus[i]= disconnected ? SQL_ROW_ERROR : SQL_ROW_NOROW; } /* No need to update rowStatusPtr_ex, it's the same as rgfRowStatus. */ if ( upd_status && stmt->ird->array_status_ptr ) { stmt->ird->array_status_ptr[i]= disconnected? SQL_ROW_ERROR : SQL_ROW_NOROW; } } if (SQL_SUCCEEDED(res) && !stmt->result_array && !if_forward_cache(stmt)) { /* reset result position */ stmt->end_of_set= mysql_row_seek(stmt->result,save_position); } if (!stmt->dbc->ds->dont_use_set_locale) setlocale(LC_NUMERIC,default_locale); if (SQL_SUCCEEDED(res) && stmt->rows_found_in_set < stmt->ard->array_size) { if (disconnected) { return SQL_ERROR; } else if (stmt->rows_found_in_set == 0) { return SQL_NO_DATA_FOUND; } } return res; } /* @type : ODBC 1.0 API @purpose : fetches the specified rowset of data from the result set and returns data for all bound columns. Rowsets can be specified at an absolute or relative position */ SQLRETURN SQL_API SQLExtendedFetch( SQLHSTMT hstmt, SQLUSMALLINT fFetchType, SQLLEN irow, SQLULEN *pcrow, SQLUSMALLINT FAR *rgfRowStatus ) { SQLRETURN rc; SQLULEN rows; STMT_OPTIONS *options= &((STMT FAR *)hstmt)->stmt_options; options->rowStatusPtr_ex= rgfRowStatus; rc= my_SQLExtendedFetch(hstmt, fFetchType, irow, &rows, rgfRowStatus, 1); if (pcrow) *pcrow= (SQLULEN) rows; return rc; } /* @type : ODBC 3.0 API @purpose : fetches the specified rowset of data from the result set and returns data for all bound columns. Rowsets can be specified at an absolute or relative position */ SQLRETURN SQL_API SQLFetchScroll( SQLHSTMT StatementHandle, SQLSMALLINT FetchOrientation, SQLLEN FetchOffset ) { STMT *stmt = (STMT *)StatementHandle; STMT_OPTIONS *options= &stmt->stmt_options; options->rowStatusPtr_ex= NULL; return my_SQLExtendedFetch(StatementHandle, FetchOrientation, FetchOffset, stmt->ird->rows_processed_ptr, stmt->ird->array_status_ptr, 0); } /* @type : ODBC 1.0 API @purpose : fetches the next rowset of data from the result set and returns data for all bound columns */ SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) { STMT *stmt = (STMT *)StatementHandle; STMT_OPTIONS *options= &stmt->stmt_options; options->rowStatusPtr_ex= NULL; return my_SQLExtendedFetch(StatementHandle, SQL_FETCH_NEXT, 0, stmt->ird->rows_processed_ptr, stmt->ird->array_status_ptr, 0); } mysql-connector-odbc-5.1.10-src/driver/utility.c100644 15766 12 255440 11707541005 20402 0ustar00cteamstaff/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file utility.c @brief Utility functions */ #include "driver.h" #include "errmsg.h" #include const SQLULEN sql_select_unlimited= (SQLULEN)-1; /** Execute a SQL statement. @param[in] dbc The database connection @param[in] query The query to execute */ SQLRETURN odbc_stmt(DBC FAR *dbc, const char *query) { SQLRETURN result= SQL_SUCCESS; pthread_mutex_lock(&dbc->lock); if ( check_if_server_is_alive(dbc) || mysql_real_query(&dbc->mysql,query,strlen(query)) ) { result= set_conn_error(dbc,MYERR_S1000,mysql_error(&dbc->mysql), mysql_errno(&dbc->mysql)); } pthread_mutex_unlock(&dbc->lock); return result; } /** Link a list of fields to the current statement result. @todo This is a terrible idea. We need to purge this. @param[in] stmt The statement to modify @param[in] fields The fields to attach to the statement @param[in] field_count The number of fields */ void mysql_link_fields(STMT *stmt, MYSQL_FIELD *fields, uint field_count) { MYSQL_RES *result; pthread_mutex_lock(&stmt->dbc->lock); result= stmt->result; result->fields= fields; result->field_count= field_count; result->current_field= 0; fix_result_types(stmt); pthread_mutex_unlock(&stmt->dbc->lock); } /** Fills STMT's lengths array for given row. Makes use of mysql_link_fields a bit less terrible. @param[in,out] stmt The statement to modify @param[in] fix_rules Describes how to calculate lengths. For each element value N > 0 - length is taken of field #N from original results (counting from 1) N <=0 - constant length (-N) @param[in] row Row for which to fix lengths @param[in] field_count The number of fields */ void fix_row_lengths(STMT *stmt, const long* fix_rules, uint row, uint field_count) { unsigned long* orig_lengths, *row_lengths; uint i; if (stmt->lengths == NULL) return; row_lengths= stmt->lengths + row*field_count; orig_lengths= mysql_fetch_lengths(stmt->result); for (i= 0; i < field_count; ++i) { if (fix_rules[i] > 0) row_lengths[i]= orig_lengths[fix_rules[i] - 1]; else row_lengths[i]= -fix_rules[i]; } } /** Figure out the ODBC result types for each column in the result set. @param[in] stmt The statement with result types to be fixed. */ void fix_result_types(STMT *stmt) { uint i; MYSQL_RES *result= stmt->result; DESCREC *irrec; MYSQL_FIELD *field; int capint32= stmt->dbc->ds->limit_column_size ? 1 : 0; stmt->state= ST_EXECUTED; /* Mark set found */ /* Populate the IRD records */ for (i= 0; i < result->field_count; ++i) { irrec= desc_get_rec(stmt->ird, i, TRUE); field= result->fields + i; irrec->row.field= field; irrec->type= get_sql_data_type(stmt, field, NULL); irrec->concise_type= get_sql_data_type(stmt, field, (char *)irrec->row.type_name); switch (irrec->concise_type) { case SQL_DATE: case SQL_TYPE_DATE: case SQL_TIME: case SQL_TYPE_TIME: case SQL_TIMESTAMP: case SQL_TYPE_TIMESTAMP: irrec->type= SQL_DATETIME; break; default: irrec->type= irrec->concise_type; break; } irrec->datetime_interval_code= get_dticode_from_concise_type(irrec->concise_type); irrec->type_name= (SQLCHAR *) irrec->row.type_name; irrec->length= get_column_size(stmt, field); /* prevent overflowing of result when ADO multiplies the length by sizeof(SQLWCHAR) */ if (capint32 && irrec->length == INT_MAX32 && irrec->concise_type == SQL_WLONGVARCHAR) irrec->length /= sizeof(SQL_WCHAR); irrec->octet_length= get_transfer_octet_length(stmt, field); irrec->display_size= get_display_size(stmt, field); /* According ODBC specs(http://msdn.microsoft.com/en-us/library/ms713558%28v=VS.85%29.aspx) "SQL_DESC_OCTET_LENGTH ... For variable-length character or binary types, this is the maximum length in bytes. This value does not include the null terminator" Thus there is no need to add 1 to octet_length for char types */ irrec->precision= 0; /* Set precision for all non-char/blob types */ switch (irrec->type) { case SQL_BINARY: case SQL_BIT: case SQL_CHAR: case SQL_WCHAR: case SQL_VARBINARY: case SQL_VARCHAR: case SQL_WVARCHAR: case SQL_LONGVARBINARY: case SQL_LONGVARCHAR: case SQL_WLONGVARCHAR: break; default: irrec->precision= (SQLSMALLINT) irrec->length; break; } irrec->scale= myodbc_max(0, get_decimal_digits(stmt, field)); if ((field->flags & NOT_NULL_FLAG) && !(field->type == MYSQL_TYPE_TIMESTAMP) && !(field->flags & AUTO_INCREMENT_FLAG)) irrec->nullable= SQL_NO_NULLS; else irrec->nullable= SQL_NULLABLE; irrec->table_name= (SQLCHAR *)field->table; irrec->name= (SQLCHAR *)field->name; irrec->label= (SQLCHAR *)field->name; if (field->flags & AUTO_INCREMENT_FLAG) irrec->auto_unique_value= SQL_TRUE; else irrec->auto_unique_value= SQL_FALSE; /* We need support from server, when aliasing is there */ irrec->base_column_name= (SQLCHAR *)field->org_name; irrec->base_table_name= (SQLCHAR *)field->org_table; if (field->flags & BINARY_FLAG) /* TODO this doesn't cut it anymore */ irrec->case_sensitive= SQL_TRUE; else irrec->case_sensitive= SQL_FALSE; if (field->db && *field->db) { irrec->catalog_name= (SQLCHAR *)field->db; } else { irrec->catalog_name= (SQLCHAR *)(stmt->dbc->database ? stmt->dbc->database : ""); } irrec->fixed_prec_scale= SQL_FALSE; switch (field->type) { case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: if (field->charsetnr == BINARY_CHARSET_NUMBER) { irrec->literal_prefix= (SQLCHAR *) "0x"; irrec->literal_suffix= (SQLCHAR *) ""; break; } /* FALLTHROUGH */ case MYSQL_TYPE_DATE: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_TIME: case MYSQL_TYPE_YEAR: irrec->literal_prefix= (SQLCHAR *) "'"; irrec->literal_suffix= (SQLCHAR *) "'"; break; default: irrec->literal_prefix= (SQLCHAR *) ""; irrec->literal_suffix= (SQLCHAR *) ""; } switch (field->type) { case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_INT24: case MYSQL_TYPE_TINY: case MYSQL_TYPE_DECIMAL: irrec->num_prec_radix= 10; break; /* overwrite irrec->precision set above */ case MYSQL_TYPE_FLOAT: irrec->num_prec_radix= 2; irrec->precision= 23; break; case MYSQL_TYPE_DOUBLE: irrec->num_prec_radix= 2; irrec->precision= 53; break; default: irrec->num_prec_radix= 0; break; } irrec->schema_name= (SQLCHAR *) ""; /* We limit BLOB/TEXT types to SQL_PRED_CHAR due an oversight in ADO causing problems with updatable cursors. */ switch (irrec->concise_type) { case SQL_LONGVARBINARY: case SQL_LONGVARCHAR: case SQL_WLONGVARCHAR: irrec->searchable= SQL_PRED_CHAR; break; default: irrec->searchable= SQL_SEARCHABLE; break; } irrec->unnamed= SQL_NAMED; if (field->flags & UNSIGNED_FLAG) irrec->is_unsigned= SQL_TRUE; else irrec->is_unsigned= SQL_FALSE; if (field->table && *field->table) irrec->updatable= SQL_ATTR_READWRITE_UNKNOWN; else irrec->updatable= SQL_ATTR_READONLY; } stmt->ird->count= result->field_count; } /** Change a string with a length to a NUL-terminated string. @param[in,out] to A buffer to write the string into, which must be at at least length + 1 bytes long. @param[in] from A pointer to the beginning of the source string. @param[in] length The length of the string, or SQL_NTS if it is already NUL-terminated. @return A pointer to a NUL-terminated string. */ char *fix_str(char *to, const char *from, int length) { if ( !from ) return ""; if ( length == SQL_NTS ) return (char *)from; strmake(to,from,length); return to; } /* @type : myodbc internal @purpose : duplicate the string */ char *dupp_str(char *from,int length) { char *to; if ( !from ) return my_strdup("",MYF(MY_WME)); if ( length == SQL_NTS ) length= strlen(from); if ( (to= my_malloc(length+1,MYF(MY_WME))) ) { memcpy(to,from,length); to[length]= 0; } return to; } /* @type : myodbc internal @purpose : copies the string data to rgbValue buffer. If rgbValue is NULL, then returns warning with full length, else copies the cbValueMax length from 'src' and returns it. */ SQLRETURN copy_str_data(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLCHAR FAR *rgbValue, SQLSMALLINT cbValueMax, SQLSMALLINT FAR *pcbValue,char FAR *src) { SQLSMALLINT dummy; if ( !pcbValue ) pcbValue= &dummy; if ( cbValueMax == SQL_NTS ) cbValueMax= *pcbValue= strlen(src); else if ( cbValueMax < 0 ) return set_handle_error(HandleType,Handle,MYERR_S1090,NULL,0); else { cbValueMax= cbValueMax ? cbValueMax - 1 : 0; *pcbValue= strlen(src); } if ( rgbValue ) strmake((char*) rgbValue, src, cbValueMax); if ( myodbc_min(*pcbValue , cbValueMax) != *pcbValue ) return SQL_SUCCESS_WITH_INFO; return SQL_SUCCESS; } /* Copy a field to a byte string. @param[in] stmt Pointer to statement @param[out] result Buffer for result @param[in] result_bytes Size of result buffer (in bytes) @param[out] avail_bytes Pointer to buffer for storing number of bytes available as result @param[in] field Field being stored @param[in] src Source data for result @param[in] src_bytes Length of source data (in bytes) @return Standard ODBC result code */ SQLRETURN copy_binary_result(STMT *stmt, SQLCHAR *result, SQLLEN result_bytes, SQLLEN *avail_bytes, MYSQL_FIELD *field __attribute__((unused)), char *src, unsigned long src_bytes) { SQLRETURN rc= SQL_SUCCESS; ulong copy_bytes; if (!result_bytes) result= 0; /* Don't copy anything! */ /* Apply max length to source data, if one was specified. */ if (stmt->stmt_options.max_length && src_bytes > stmt->stmt_options.max_length) src_bytes= stmt->stmt_options.max_length; /* Initialize the source offset */ if (!stmt->getdata.source) stmt->getdata.source= src; else { src_bytes-= stmt->getdata.source - src; src= stmt->getdata.source; /* If we've already retrieved everything, return SQL_NO_DATA_FOUND */ if (src_bytes == 0) return SQL_NO_DATA_FOUND; } copy_bytes= myodbc_min((unsigned long)result_bytes, src_bytes); if (result) memcpy(result, src, copy_bytes); if (avail_bytes) *avail_bytes= src_bytes; stmt->getdata.source+= copy_bytes; if (src_bytes > (unsigned long)result_bytes) { set_stmt_error(stmt, "01004", NULL, 0); rc= SQL_SUCCESS_WITH_INFO; } return rc; } /* Copy a field to an ANSI result string. @param[in] stmt Pointer to statement @param[out] result Buffer for result @param[in] result_bytes Size of result buffer (in bytes) @param[out] avail_bytes Pointer to buffer for storing number of bytes available as result @param[in] field Field being stored @param[in] src Source data for result @param[in] src_bytes Length of source data (in bytes) @return Standard ODBC result code */ SQLRETURN copy_ansi_result(STMT *stmt, SQLCHAR *result, SQLLEN result_bytes, SQLLEN *avail_bytes, MYSQL_FIELD *field, char *src, unsigned long src_bytes) { SQLRETURN rc= SQL_SUCCESS; char *src_end; SQLCHAR *result_end; ulong used_bytes= 0, used_chars= 0, error_count= 0; my_bool convert_binary= test(field->charsetnr == BINARY_CHARSET_NUMBER) && test(field->org_table_length == 0) && stmt->dbc->ds->handle_binary_as_char; CHARSET_INFO *to_cs= stmt->dbc->ansi_charset_info, *from_cs= get_charset(field->charsetnr && (!convert_binary) ? field->charsetnr : UTF8_CHARSET_NUMBER, MYF(0)); if (!from_cs) return set_stmt_error(stmt, "07006", "Source character set not " "supported by client", 0); if (!result_bytes) result= 0; /* Don't copy anything! */ /* If we don't have to do any charset conversion, we can just use copy_binary_result() and NUL-terminate the buffer here. */ if (to_cs->number == from_cs->number) { SQLLEN bytes; if (!avail_bytes) avail_bytes= &bytes; if (!result_bytes && !stmt->getdata.source) { *avail_bytes= src_bytes; set_stmt_error(stmt, "01004", NULL, 0); return SQL_SUCCESS_WITH_INFO; } if (result_bytes) --result_bytes; rc= copy_binary_result(stmt, result, result_bytes, avail_bytes, field, src, src_bytes); if (SQL_SUCCEEDED(rc) && result) result[myodbc_min(*avail_bytes, result_bytes)]= '\0'; return rc; } result_end= result + result_bytes - 1; /* Handle when result_bytes is 1 -- we have room for the NUL termination, but nothing else. */ if (result == result_end) { *result= '\0'; result= 0; } /* Apply max length to source data, if one was specified. */ if (stmt->stmt_options.max_length && src_bytes > stmt->stmt_options.max_length) src_bytes= stmt->stmt_options.max_length; src_end= src + src_bytes; /* Initialize the source offset */ if (!stmt->getdata.source) stmt->getdata.source= src; else src= stmt->getdata.source; /* If we've already retrieved everything, return SQL_NO_DATA_FOUND */ if (stmt->getdata.dst_bytes != (ulong)~0L && stmt->getdata.dst_offset >= stmt->getdata.dst_bytes) return SQL_NO_DATA_FOUND; /* If we have leftover bytes from an earlier character conversion, copy as much as we can into place. */ if (stmt->getdata.latest_bytes) { int new_bytes= myodbc_min(stmt->getdata.latest_bytes - stmt->getdata.latest_used, result_end - result); memcpy(result, stmt->getdata.latest + stmt->getdata.latest_used, new_bytes); if (new_bytes + stmt->getdata.latest_used == stmt->getdata.latest_bytes) stmt->getdata.latest_bytes= 0; result+= new_bytes; if (result == result_end) { *result= '\0'; result= NULL; } used_bytes+= new_bytes; stmt->getdata.latest_used+= new_bytes; } while (src < src_end) { /* Find the conversion functions. */ int (*mb_wc)(struct charset_info_st *, my_wc_t *, const uchar *, const uchar *) = from_cs->cset->mb_wc; int (*wc_mb)(struct charset_info_st *, my_wc_t, uchar *s, uchar *e)= to_cs->cset->wc_mb; my_wc_t wc; uchar dummy[7]; /* Longer than any single character in our charsets. */ int to_cnvres; int cnvres= (*mb_wc)(from_cs, &wc, (uchar *)src, (uchar *)src_end); if (cnvres == MY_CS_ILSEQ) { ++error_count; cnvres= 1; wc= '?'; } else if (cnvres < 0 && cnvres > MY_CS_TOOSMALL) { ++error_count; cnvres= abs(cnvres); wc= '?'; } else if (cnvres < 0) return set_stmt_error(stmt, "HY000", "Unknown failure when converting character " "from server character set.", 0); convert_to_out: /* We always convert into a temporary buffer, so we can properly handle characters that are going to get split across requests. */ to_cnvres= (*wc_mb)(to_cs, wc, result ? result : dummy, (result ? result_end : dummy + sizeof(dummy))); if (to_cnvres > 0) { used_chars+= 1; used_bytes+= to_cnvres; if (result) result+= to_cnvres; src+= cnvres; if (result && result == result_end) { if (stmt->getdata.dst_bytes != (ulong)~0L) { stmt->getdata.source+= cnvres; break; } *result= '\0'; result= NULL; } else if (!result) continue; stmt->getdata.source+= cnvres; } else if (result && to_cnvres <= MY_CS_TOOSMALL) { /* If we didn't have enough room for the character, we convert into stmt->getdata.latest and copy what we can. The next call to SQLGetData() will then copy what it can to the next buffer. */ stmt->getdata.latest_bytes= (*wc_mb)(to_cs, wc, stmt->getdata.latest, stmt->getdata.latest + sizeof(stmt->getdata.latest)); stmt->getdata.latest_used= myodbc_min(stmt->getdata.latest_bytes, result_end - result); memcpy(result, stmt->getdata.latest, stmt->getdata.latest_used); result+= stmt->getdata.latest_used; *result= '\0'; result= NULL; used_chars+= 1; used_bytes+= stmt->getdata.latest_bytes; src+= stmt->getdata.latest_bytes; stmt->getdata.source+= stmt->getdata.latest_bytes; } else if (stmt->getdata.latest_bytes == MY_CS_ILUNI && wc != '?') { ++error_count; wc= '?'; goto convert_to_out; } else return set_stmt_error(stmt, "HY000", "Unknown failure when converting character " "to result character set.", 0); } if (result) *result= 0; if (result_bytes && stmt->getdata.dst_bytes == (ulong)~0L) { stmt->getdata.dst_bytes= used_bytes; stmt->getdata.dst_offset= 0; } if (avail_bytes) { if (stmt->getdata.dst_bytes != (ulong)~0L) *avail_bytes= stmt->getdata.dst_bytes - stmt->getdata.dst_offset; else *avail_bytes= used_bytes; } stmt->getdata.dst_offset+= myodbc_min((ulong)(result_bytes ? result_bytes - 1 : 0), used_bytes); /* Did we truncate the data? */ if (!result_bytes || stmt->getdata.dst_bytes > stmt->getdata.dst_offset) { set_stmt_error(stmt, "01004", NULL, 0); rc= SQL_SUCCESS_WITH_INFO; } /* Did we encounter any character conversion problems? */ if (error_count) { set_stmt_error(stmt, "22018", NULL, 0); rc= SQL_SUCCESS_WITH_INFO; } return rc; } /** Copy a result from the server into a buffer as a SQL_C_WCHAR. @param[in] stmt Pointer to statement @param[out] result Buffer for result @param[in] result_len Size of result buffer (in characters) @param[out] avail_bytes Pointer to buffer for storing amount of data available before this call @param[in] field Field being stored @param[in] src Source data for result @param[in] src_bytes Length of source data (in bytes) @return Standard ODBC result code */ SQLRETURN copy_wchar_result(STMT *stmt, SQLWCHAR *result, SQLINTEGER result_len, SQLLEN *avail_bytes, MYSQL_FIELD *field, char *src, long src_bytes) { SQLRETURN rc= SQL_SUCCESS; char *src_end; SQLWCHAR *result_end; ulong used_chars= 0, error_count= 0; CHARSET_INFO *from_cs= get_charset(field->charsetnr ? field->charsetnr : UTF8_CHARSET_NUMBER, MYF(0)); if (!from_cs) return set_stmt_error(stmt, "07006", "Source character set not " "supported by client", 0); if (!result_len) result= NULL; /* Don't copy anything! */ result_end= result + result_len - 1; if (result == result_end) { *result= 0; result= 0; } /* Apply max length to source data, if one was specified. */ if (stmt->stmt_options.max_length && (ulong)src_bytes > stmt->stmt_options.max_length) src_bytes= stmt->stmt_options.max_length; src_end= src + src_bytes; /* Initialize the source data */ if (!stmt->getdata.source) stmt->getdata.source= src; else src= stmt->getdata.source; /* If we've already retrieved everything, return SQL_NO_DATA_FOUND */ if (stmt->getdata.dst_bytes != (ulong)~0L && stmt->getdata.dst_offset >= stmt->getdata.dst_bytes) return SQL_NO_DATA_FOUND; /* We may have a leftover char from the last call. */ if (stmt->getdata.latest_bytes) { memcpy(result, stmt->getdata.latest, sizeof(SQLWCHAR)); ++result; if (result == result_end) { *result= 0; result= NULL; } used_chars+= 1; stmt->getdata.latest_bytes= 0; } while (src < src_end) { /* Find the conversion functions. */ int (*mb_wc)(struct charset_info_st *, my_wc_t *, const uchar *, const uchar *) = from_cs->cset->mb_wc; int (*wc_mb)(struct charset_info_st *, my_wc_t, uchar *s, uchar *e)= utf8_charset_info->cset->wc_mb; my_wc_t wc; uchar u8[5]; /* Max length of utf-8 string we'll see. */ SQLWCHAR dummy[2]; /* If SQLWCHAR is UTF-16, we may need two chars. */ int to_cnvres; int cnvres= (*mb_wc)(from_cs, &wc, (uchar *)src, (uchar *)src_end); if (cnvres == MY_CS_ILSEQ) { ++error_count; cnvres= 1; wc= '?'; } else if (cnvres < 0 && cnvres > MY_CS_TOOSMALL) { ++error_count; cnvres= abs(cnvres); wc= '?'; } else if (cnvres < 0) return set_stmt_error(stmt, "HY000", "Unknown failure when converting character " "from server character set.", 0); convert_to_out: /* We always convert into a temporary buffer, so we can properly handle characters that are going to get split across requests. */ to_cnvres= (*wc_mb)(utf8_charset_info, wc, u8, u8 + sizeof(u8)); if (to_cnvres > 0) { u8[to_cnvres]= '\0'; src+= cnvres; if (sizeof(SQLWCHAR) == 4) { utf8toutf32(u8, (UTF32 *)(result ? result : dummy)); if (result) ++result; used_chars+= 1; } else { UTF32 u32; UTF16 out[2]; int chars; utf8toutf32(u8, &u32); chars= utf32toutf16(u32, (UTF16 *)out); if (result) *result++= out[0]; used_chars+= chars; if (chars > 1 && result && result != result_end) *result++= out[1]; else if (chars > 1 && result) { *((SQLWCHAR *)stmt->getdata.latest)= out[1]; stmt->getdata.latest_bytes= 2; stmt->getdata.latest_used= 0; *result= 0; result= NULL; if (stmt->getdata.dst_bytes != (ulong)~0L) { stmt->getdata.source+= cnvres; break; } } else if (chars > 1) continue; } if (result) stmt->getdata.source+= cnvres; if (result && result == result_end) { *result= 0; result= NULL; } } else if (stmt->getdata.latest_bytes == MY_CS_ILUNI && wc != '?') { ++error_count; wc= '?'; goto convert_to_out; } else return set_stmt_error(stmt, "HY000", "Unknown failure when converting character " "to result character set.", 0); } if (result) *result= 0; if (result_len && stmt->getdata.dst_bytes == (ulong)~0L) { stmt->getdata.dst_bytes= used_chars * sizeof(SQLWCHAR); stmt->getdata.dst_offset= 0; } if (avail_bytes) { if (result_len) *avail_bytes= stmt->getdata.dst_bytes - stmt->getdata.dst_offset; else *avail_bytes= used_chars * sizeof(SQLWCHAR); } stmt->getdata.dst_offset+= myodbc_min((ulong)(result_len ? result_len - 1 : 0), used_chars) * sizeof(SQLWCHAR); /* Did we truncate the data? */ if (!result_len || stmt->getdata.dst_bytes > stmt->getdata.dst_offset) { set_stmt_error(stmt, "01004", NULL, 0); rc= SQL_SUCCESS_WITH_INFO; } /* Did we encounter any character conversion problems? */ if (error_count) { set_stmt_error(stmt, "22018", NULL, 0); rc= SQL_SUCCESS_WITH_INFO; } return rc; } /* @type : myodbc internal @purpose : is used when converting a binary string to a SQL_C_CHAR */ SQLRETURN copy_binhex_result(STMT *stmt, SQLCHAR *rgbValue, SQLINTEGER cbValueMax, SQLLEN *pcbValue, MYSQL_FIELD *field __attribute__((unused)), char *src, ulong src_length) { /** @todo padding of BINARY */ char *dst= (char*) rgbValue; ulong length; ulong max_length= stmt->stmt_options.max_length; ulong *offset= &stmt->getdata.src_offset; #if MYSQL_VERSION_ID >= 40100 char NEAR _dig_vec[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; #endif if ( !cbValueMax ) dst= 0; /* Don't copy anything! */ if ( max_length ) /* If limit on char lengths */ { set_if_smaller(cbValueMax,(long) max_length+1); set_if_smaller(src_length,(max_length+1)/2); } if ( *offset == (ulong) ~0L ) *offset= 0; /* First call */ else if ( *offset >= src_length ) return SQL_NO_DATA_FOUND; src+= *offset; src_length-= *offset; length= cbValueMax ? (ulong)(cbValueMax-1)/2 : 0; length= myodbc_min(src_length,length); (*offset)+= length; /* Fix for next call */ if ( pcbValue ) *pcbValue= src_length*2; if ( dst ) /* Bind allows null pointers */ { ulong i; for ( i= 0 ; i < length ; ++i ) { *dst++= _dig_vec[(uchar) *src >> 4]; *dst++= _dig_vec[(uchar) *src++ & 15]; } *dst= 0; } if ( (ulong) cbValueMax > length*2 ) return SQL_SUCCESS; set_stmt_error(stmt, "01004", NULL, 0); return SQL_SUCCESS_WITH_INFO; } /** Get the SQL data type and (optionally) type name for a MYSQL_FIELD. @param[in] stmt @param[in] field @param[out] buff @return The SQL data type. */ SQLSMALLINT get_sql_data_type(STMT *stmt, MYSQL_FIELD *field, char *buff) { my_bool field_is_binary= test(field->charsetnr == BINARY_CHARSET_NUMBER) && (test(field->org_table_length > 0) || !stmt->dbc->ds->handle_binary_as_char); switch (field->type) { case MYSQL_TYPE_BIT: if (buff) (void)strmov(buff, "bit"); /* MySQL's BIT type can have more than one bit, in which case we treat it as a BINARY field. */ return (field->length > 1) ? SQL_BINARY : SQL_BIT; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: if (buff) (void)strmov(buff, "decimal"); return SQL_DECIMAL; case MYSQL_TYPE_TINY: /* MYSQL_TYPE_TINY could either be a TINYINT or a single CHAR. */ if (buff) { buff= strmov(buff, (field->flags & NUM_FLAG) ? "tinyint" : "char"); if (field->flags & UNSIGNED_FLAG) (void)strmov(buff, " unsigned"); } return (field->flags & NUM_FLAG) ? SQL_TINYINT : SQL_CHAR; case MYSQL_TYPE_SHORT: if (buff) { buff= strmov(buff, "smallint"); if (field->flags & UNSIGNED_FLAG) (void)strmov(buff, " unsigned"); } return SQL_SMALLINT; case MYSQL_TYPE_INT24: if (buff) { buff= strmov(buff, "mediumint"); if (field->flags & UNSIGNED_FLAG) (void)strmov(buff, " unsigned"); } return SQL_INTEGER; case MYSQL_TYPE_LONG: if (buff) { buff= strmov(buff, "integer"); if (field->flags & UNSIGNED_FLAG) (void)strmov(buff, " unsigned"); } return SQL_INTEGER; case MYSQL_TYPE_LONGLONG: if (buff) { if (stmt->dbc->ds->change_bigint_columns_to_int) buff= strmov(buff, "int"); else buff= strmov(buff, "bigint"); if (field->flags & UNSIGNED_FLAG) (void)strmov(buff, " unsigned"); } if (stmt->dbc->ds->change_bigint_columns_to_int) return SQL_INTEGER; return SQL_BIGINT; case MYSQL_TYPE_FLOAT: if (buff) { buff= strmov(buff, "float"); if (field->flags & UNSIGNED_FLAG) (void)strmov(buff, " unsigned"); } return SQL_REAL; case MYSQL_TYPE_DOUBLE: if (buff) { buff= strmov(buff, "double"); if (field->flags & UNSIGNED_FLAG) (void)strmov(buff, " unsigned"); } return SQL_DOUBLE; case MYSQL_TYPE_NULL: if (buff) (void)strmov(buff, "null"); return SQL_VARCHAR; case MYSQL_TYPE_YEAR: if (buff) (void)strmov(buff, "year"); return SQL_SMALLINT; case MYSQL_TYPE_TIMESTAMP: if (buff) (void)strmov(buff, "timestamp"); if (stmt->dbc->env->odbc_ver == SQL_OV_ODBC3) return SQL_TYPE_TIMESTAMP; return SQL_TIMESTAMP; case MYSQL_TYPE_DATETIME: if (buff) (void)strmov(buff, "datetime"); if (stmt->dbc->env->odbc_ver == SQL_OV_ODBC3) return SQL_TYPE_TIMESTAMP; return SQL_TIMESTAMP; case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_DATE: if (buff) (void)strmov(buff, "date"); if (stmt->dbc->env->odbc_ver == SQL_OV_ODBC3) return SQL_TYPE_DATE; return SQL_DATE; case MYSQL_TYPE_TIME: if (buff) (void)strmov(buff, "time"); if (stmt->dbc->env->odbc_ver == SQL_OV_ODBC3) return SQL_TYPE_TIME; return SQL_TIME; case MYSQL_TYPE_STRING: if (buff) (void)strmov(buff, field_is_binary ? "binary" : "char"); return field_is_binary ? SQL_BINARY : (field->charsetnr != stmt->dbc->ansi_charset_info->number ? SQL_WCHAR : SQL_CHAR); /* MYSQL_TYPE_VARCHAR is never actually sent, this just silences a compiler warning. */ case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: if (buff) (void)strmov(buff, field_is_binary ? "varbinary" : "varchar"); return field_is_binary ? SQL_VARBINARY : (field->charsetnr != stmt->dbc->ansi_charset_info->number ? SQL_WVARCHAR : SQL_VARCHAR); case MYSQL_TYPE_TINY_BLOB: if (buff) (void)strmov(buff, field_is_binary ? "tinyblob" : "tinytext"); return field_is_binary ? SQL_LONGVARBINARY : (field->charsetnr != stmt->dbc->ansi_charset_info->number ? SQL_WLONGVARCHAR : SQL_LONGVARCHAR); case MYSQL_TYPE_BLOB: if (buff) (void)strmov(buff, field_is_binary ? "blob" : "text"); return field_is_binary ? SQL_LONGVARBINARY : (field->charsetnr != stmt->dbc->ansi_charset_info->number ? SQL_WLONGVARCHAR : SQL_LONGVARCHAR); case MYSQL_TYPE_MEDIUM_BLOB: if (buff) (void)strmov(buff, field_is_binary ? "mediumblob" : "mediumtext"); return field_is_binary ? SQL_LONGVARBINARY : (field->charsetnr != stmt->dbc->ansi_charset_info->number ? SQL_WLONGVARCHAR : SQL_LONGVARCHAR); case MYSQL_TYPE_LONG_BLOB: if (buff) (void)strmov(buff, field_is_binary ? "longblob" : "longtext"); return field_is_binary ? SQL_LONGVARBINARY : (field->charsetnr != stmt->dbc->ansi_charset_info->number ? SQL_WLONGVARCHAR : SQL_LONGVARCHAR); case MYSQL_TYPE_ENUM: if (buff) (void)strmov(buff, "enum"); return SQL_CHAR; case MYSQL_TYPE_SET: if (buff) (void)strmov(buff, "set"); return SQL_CHAR; case MYSQL_TYPE_GEOMETRY: if (buff) (void)strmov(buff, "geometry"); return SQL_LONGVARBINARY; } if (buff) *buff= '\0'; return SQL_UNKNOWN_TYPE; } void sqlulen_to_str(char *buff, SQLULEN value) { } /** Fill the display size buffer accordingly to size of SQLLEN @param[in,out] buff @param[in] stmt @param[in] field @return void */ SQLLEN fill_display_size_buff(char *buff, STMT *stmt, MYSQL_FIELD *field) { /* See comment for fill_transfer_oct_len_buff()*/ SQLLEN size= get_display_size(stmt, field); sprintf(buff,size == SQL_NO_TOTAL ? "%d" : (sizeof(SQLLEN) == 4 ? "%lu" : "%lld"), size); return size; } /** Fill the transfer octet length buffer accordingly to size of SQLLEN @param[in,out] buff @param[in] stmt @param[in] field @return void */ SQLLEN fill_transfer_oct_len_buff(char *buff, STMT *stmt, MYSQL_FIELD *field) { /* The only possible negative value get_transfer_octet_length can return is SQL_NO_TOTAL But it can return value which is greater that biggest signed integer(%ld). Thus for other values we use %lu. %lld should fit all (currently) possible in mysql values. */ SQLLEN len= get_transfer_octet_length(stmt, field); sprintf(buff, len == SQL_NO_TOTAL ? "%d" : (sizeof(SQLLEN) == 4 ? "%lu" : "%lld"), len ); return len; } /** Fill the column size buffer accordingly to size of SQLULEN @param[in,out] buff @param[in] stmt @param[in] field @return void */ SQLULEN fill_column_size_buff(char *buff, STMT *stmt, MYSQL_FIELD *field) { SQLULEN size= get_column_size(stmt, field); sprintf(buff, (size== SQL_NO_TOTAL ? "%d" : (sizeof(SQLULEN) == 4 ? "%lu" : "%llu")), size); return size; } /** Capping length value if connection option is set */ static SQLLEN cap_length(STMT *stmt, unsigned long real_length) { if (stmt->dbc->ds->limit_column_size != 0 && real_length > INT_MAX32) return INT_MAX32; return real_length; } /** Get the column size (in characters) of a field, as defined at: http://msdn2.microsoft.com/en-us/library/ms711786.aspx @param[in] stmt @param[in] field @return The column size of the field */ SQLULEN get_column_size(STMT *stmt, MYSQL_FIELD *field) { SQLULEN length= field->length; /* Work around a bug in some versions of the server. */ if (field->max_length > field->length) length= field->max_length; length= cap_length(stmt, length); switch (field->type) { case MYSQL_TYPE_TINY: return (field->flags & NUM_FLAG) ? 3 : 1; case MYSQL_TYPE_SHORT: return 5; case MYSQL_TYPE_LONG: return 10; case MYSQL_TYPE_FLOAT: return 7; case MYSQL_TYPE_DOUBLE: return 15; case MYSQL_TYPE_NULL: return 0; case MYSQL_TYPE_LONGLONG: if (stmt->dbc->ds->change_bigint_columns_to_int) return 10; /* same as MYSQL_TYPE_LONG */ else return (field->flags & UNSIGNED_FLAG) ? 20 : 19; case MYSQL_TYPE_INT24: return 8; case MYSQL_TYPE_DATE: return 10; case MYSQL_TYPE_TIME: return 8; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_NEWDATE: return 19; case MYSQL_TYPE_YEAR: return 4; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return (length - test(!(field->flags & UNSIGNED_FLAG)) - /* sign? */ test(field->decimals)); /* decimal point? */ case MYSQL_TYPE_BIT: /* We treat a BIT(n) as a SQL_BIT if n == 1, otherwise we treat it as a SQL_BINARY, so length is (bits + 7) / 8. */ if (length == 1) return 1; return (length + 7) / 8; case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: if (field->charsetnr == BINARY_CHARSET_NUMBER) return length; else { CHARSET_INFO *charset= get_charset(field->charsetnr, MYF(0)); return length / (charset ? charset->mbmaxlen : 1); } case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_GEOMETRY: return length; } return SQL_NO_TOTAL; } /** Get the decimal digits of a field, as defined at: http://msdn2.microsoft.com/en-us/library/ms709314.aspx @param[in] stmt @param[in] field @return The decimal digits, or @c SQL_NO_TOTAL where it makes no sense The function has to return SQLSMALLINT, since it corresponds to SQL_DESC_SCALE or SQL_DESC_PRECISION for some data types. */ SQLSMALLINT get_decimal_digits(STMT *stmt __attribute__((unused)), MYSQL_FIELD *field) { switch (field->type) { case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return field->decimals; /* All exact numeric types. */ case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_INT24: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: return 0; /* We treat MYSQL_TYPE_BIT as an exact numeric type only for BIT(1). */ case MYSQL_TYPE_BIT: if (field->length == 1) return 0; default: /* This value is only used in some catalog functions. It's co-erced to zero for all descriptor use. */ return SQL_NO_TOTAL; } } /** Get the transfer octet length of a field, as defined at: http://msdn2.microsoft.com/en-us/library/ms713979.aspx @param[in] stmt @param[in] field @return The transfer octet length */ SQLLEN get_transfer_octet_length(STMT *stmt, MYSQL_FIELD *field) { int capint32= stmt->dbc->ds->limit_column_size ? 1 : 0; SQLLEN length; /* cap at INT_MAX32 due to signed value */ if (field->length > INT_MAX32) length= INT_MAX32; else length= field->length; switch (field->type) { case MYSQL_TYPE_TINY: return 1; case MYSQL_TYPE_SHORT: return 2; case MYSQL_TYPE_INT24: return 3; case MYSQL_TYPE_LONG: return 4; case MYSQL_TYPE_FLOAT: return 4; case MYSQL_TYPE_DOUBLE: return 8; case MYSQL_TYPE_NULL: return 1; case MYSQL_TYPE_LONGLONG: return 20; case MYSQL_TYPE_DATE: return sizeof(SQL_DATE_STRUCT); case MYSQL_TYPE_TIME: return sizeof(SQL_TIME_STRUCT); case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_NEWDATE: return sizeof(SQL_TIMESTAMP_STRUCT); case MYSQL_TYPE_YEAR: return 1; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return field->length; case MYSQL_TYPE_BIT: /* We treat a BIT(n) as a SQL_BIT if n == 1, otherwise we treat it as a SQL_BINARY, so length is (bits + 7) / 8. field->length has the number of bits. */ return (field->length + 7) / 8; case MYSQL_TYPE_STRING: if (stmt->dbc->ds->pad_char_to_full_length) length= field->max_length; /* FALLTHROUGH */ case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_GEOMETRY: if (field->charsetnr != stmt->dbc->ansi_charset_info->number && field->charsetnr != BINARY_CHARSET_NUMBER) length *= stmt->dbc->ansi_charset_info->mbmaxlen; if (capint32 && length > INT_MAX32) length= INT_MAX32; return length; } return SQL_NO_TOTAL; } /** Get the display size of a field, as defined at: http://msdn2.microsoft.com/en-us/library/ms713974.aspx @param[in] stmt @param[in] field @return The display size */ SQLLEN get_display_size(STMT *stmt __attribute__((unused)),MYSQL_FIELD *field) { int capint32= stmt->dbc->ds->limit_column_size ? 1 : 0; CHARSET_INFO *charset= get_charset(field->charsetnr, MYF(0)); unsigned int mbmaxlen= charset ? charset->mbmaxlen : 1; switch (field->type) { case MYSQL_TYPE_TINY: return 3 + test(field->flags & UNSIGNED_FLAG); case MYSQL_TYPE_SHORT: return 5 + test(field->flags & UNSIGNED_FLAG); case MYSQL_TYPE_INT24: return 8 + test(field->flags & UNSIGNED_FLAG); case MYSQL_TYPE_LONG: return 10 + test(field->flags & UNSIGNED_FLAG); case MYSQL_TYPE_FLOAT: return 14; case MYSQL_TYPE_DOUBLE: return 24; case MYSQL_TYPE_NULL: return 1; case MYSQL_TYPE_LONGLONG: return 20; case MYSQL_TYPE_DATE: return 10; case MYSQL_TYPE_TIME: return 8; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_NEWDATE: return 19; case MYSQL_TYPE_YEAR: return 4; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return field->length; case MYSQL_TYPE_BIT: /* We treat a BIT(n) as a SQL_BIT if n == 1, otherwise we treat it as a SQL_BINARY, so display length is (bits + 7) / 8 * 2. field->length has the number of bits. */ if (field->length == 1) return 1; return (field->length + 7) / 8 * 2; case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_GEOMETRY: { unsigned long length; if (field->charsetnr == BINARY_CHARSET_NUMBER) length= field->length * 2; else length= field->length / mbmaxlen; if (capint32 && length > INT_MAX32) length= INT_MAX32; return length; } } return SQL_NO_TOTAL; } /* Map the concise type (value or param) to the correct datetime or interval code. See SQLSetDescField()/SQL_DESC_DATETIME_INTERVAL_CODE docs for details. */ SQLSMALLINT get_dticode_from_concise_type(SQLSMALLINT concise_type) { /* figure out SQL_DESC_DATETIME_INTERVAL_CODE from SQL_DESC_CONCISE_TYPE */ switch (concise_type) { case SQL_C_TYPE_DATE: return SQL_CODE_DATE; case SQL_C_TYPE_TIME: return SQL_CODE_TIME; case SQL_C_TIMESTAMP: case SQL_C_TYPE_TIMESTAMP: return SQL_CODE_TIMESTAMP; case SQL_C_INTERVAL_DAY: return SQL_CODE_DAY; case SQL_C_INTERVAL_DAY_TO_HOUR: return SQL_CODE_DAY_TO_HOUR; case SQL_C_INTERVAL_DAY_TO_MINUTE: return SQL_CODE_DAY_TO_MINUTE; case SQL_C_INTERVAL_DAY_TO_SECOND: return SQL_CODE_DAY_TO_SECOND; case SQL_C_INTERVAL_HOUR: return SQL_CODE_HOUR; case SQL_C_INTERVAL_HOUR_TO_MINUTE: return SQL_CODE_HOUR_TO_MINUTE; case SQL_C_INTERVAL_HOUR_TO_SECOND: return SQL_CODE_HOUR_TO_SECOND; case SQL_C_INTERVAL_MINUTE: return SQL_CODE_MINUTE; case SQL_C_INTERVAL_MINUTE_TO_SECOND: return SQL_CODE_MINUTE_TO_SECOND; case SQL_C_INTERVAL_MONTH: return SQL_CODE_MONTH; case SQL_C_INTERVAL_SECOND: return SQL_CODE_SECOND; case SQL_C_INTERVAL_YEAR: return SQL_CODE_YEAR; case SQL_C_INTERVAL_YEAR_TO_MONTH: return SQL_CODE_YEAR_TO_MONTH; default: return 0; } } /* Map the SQL_DESC_DATETIME_INTERVAL_CODE to the SQL_DESC_CONCISE_TYPE for datetime types. Constant returned is valid for both param and value types. */ SQLSMALLINT get_concise_type_from_datetime_code(SQLSMALLINT dticode) { switch (dticode) { case SQL_CODE_DATE: return SQL_C_TYPE_DATE; case SQL_CODE_TIME: return SQL_C_TYPE_DATE; case SQL_CODE_TIMESTAMP: return SQL_C_TYPE_TIMESTAMP; default: return 0; } } /* Map the SQL_DESC_DATETIME_INTERVAL_CODE to the SQL_DESC_CONCISE_TYPE for interval types. Constant returned is valid for both param and value types. */ SQLSMALLINT get_concise_type_from_interval_code(SQLSMALLINT dticode) { switch (dticode) { case SQL_CODE_DAY: return SQL_C_INTERVAL_DAY; case SQL_CODE_DAY_TO_HOUR: return SQL_C_INTERVAL_DAY_TO_HOUR; case SQL_CODE_DAY_TO_MINUTE: return SQL_C_INTERVAL_DAY_TO_MINUTE; case SQL_CODE_DAY_TO_SECOND: return SQL_C_INTERVAL_DAY_TO_SECOND; case SQL_CODE_HOUR: return SQL_C_INTERVAL_HOUR; case SQL_CODE_HOUR_TO_MINUTE: return SQL_C_INTERVAL_HOUR_TO_MINUTE; case SQL_CODE_HOUR_TO_SECOND: return SQL_C_INTERVAL_HOUR_TO_SECOND; case SQL_CODE_MINUTE: return SQL_C_INTERVAL_MINUTE; case SQL_CODE_MINUTE_TO_SECOND: return SQL_C_INTERVAL_MINUTE_TO_SECOND; case SQL_CODE_MONTH: return SQL_C_INTERVAL_MONTH; case SQL_CODE_SECOND: return SQL_C_INTERVAL_SECOND; case SQL_CODE_YEAR: return SQL_C_INTERVAL_YEAR; case SQL_CODE_YEAR_TO_MONTH: return SQL_C_INTERVAL_YEAR_TO_MONTH; default: return 0; } } /* Map the concise type to a (possibly) more general type. */ SQLSMALLINT get_type_from_concise_type(SQLSMALLINT concise_type) { /* set SQL_DESC_TYPE from SQL_DESC_CONCISE_TYPE */ switch (concise_type) { /* datetime data types */ case SQL_C_TYPE_DATE: case SQL_C_TYPE_TIME: case SQL_C_TYPE_TIMESTAMP: return SQL_DATETIME; /* interval data types */ case SQL_C_INTERVAL_YEAR: case SQL_C_INTERVAL_MONTH: case SQL_C_INTERVAL_DAY: case SQL_C_INTERVAL_HOUR: case SQL_C_INTERVAL_MINUTE: case SQL_C_INTERVAL_SECOND: case SQL_C_INTERVAL_YEAR_TO_MONTH: case SQL_C_INTERVAL_DAY_TO_HOUR: case SQL_C_INTERVAL_DAY_TO_MINUTE: case SQL_C_INTERVAL_DAY_TO_SECOND: case SQL_C_INTERVAL_HOUR_TO_MINUTE: case SQL_C_INTERVAL_HOUR_TO_SECOND: case SQL_C_INTERVAL_MINUTE_TO_SECOND: return SQL_INTERVAL; /* else, set same */ default: return concise_type; } } /* @type : myodbc internal @purpose : returns internal type to C type */ int unireg_to_c_datatype(MYSQL_FIELD *field) { switch ( field->type ) { case MYSQL_TYPE_BIT: /* MySQL's BIT type can have more than one bit, in which case we treat it as a BINARY field. */ return (field->length > 1) ? SQL_C_BINARY : SQL_C_BIT; case MYSQL_TYPE_TINY: return SQL_C_TINYINT; case MYSQL_TYPE_YEAR: case MYSQL_TYPE_SHORT: return SQL_C_SHORT; case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: return SQL_C_LONG; case MYSQL_TYPE_FLOAT: return SQL_C_FLOAT; case MYSQL_TYPE_DOUBLE: return SQL_C_DOUBLE; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: return SQL_C_TIMESTAMP; case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_DATE: return SQL_C_DATE; case MYSQL_TYPE_TIME: return SQL_C_TIME; case MYSQL_TYPE_BLOB: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: return SQL_C_BINARY; case MYSQL_TYPE_LONGLONG: /* Must be returned as char */ default: return SQL_C_CHAR; } } /* @type : myodbc internal @purpose : returns default C type for a given SQL type */ int default_c_type(int sql_data_type) { switch ( sql_data_type ) { case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_DECIMAL: case SQL_NUMERIC: default: return SQL_C_CHAR; case SQL_BIGINT: return SQL_C_SBIGINT; case SQL_BIT: return SQL_C_BIT; case SQL_TINYINT: return SQL_C_TINYINT; case SQL_SMALLINT: return SQL_C_SHORT; case SQL_INTEGER: return SQL_C_LONG; case SQL_REAL: case SQL_FLOAT: return SQL_C_FLOAT; case SQL_DOUBLE: return SQL_C_DOUBLE; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: return SQL_C_BINARY; case SQL_DATE: case SQL_TYPE_DATE: return SQL_C_DATE; case SQL_TIME: case SQL_TYPE_TIME: return SQL_C_TIME; case SQL_TIMESTAMP: case SQL_TYPE_TIMESTAMP: return SQL_C_TIMESTAMP; } } /* @type : myodbc internal @purpose : returns bind length */ ulong bind_length(int sql_data_type,ulong length) { switch ( sql_data_type ) { case SQL_C_BIT: case SQL_C_TINYINT: case SQL_C_STINYINT: case SQL_C_UTINYINT: return 1; case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: return 2; case SQL_C_LONG: case SQL_C_SLONG: case SQL_C_ULONG: return sizeof(SQLINTEGER); case SQL_C_FLOAT: return sizeof(float); case SQL_C_DOUBLE: return sizeof(double); case SQL_C_DATE: case SQL_C_TYPE_DATE: return sizeof(DATE_STRUCT); case SQL_C_TIME: case SQL_C_TYPE_TIME: return sizeof(TIME_STRUCT); case SQL_C_TIMESTAMP: case SQL_C_TYPE_TIMESTAMP: return sizeof(TIMESTAMP_STRUCT); case SQL_C_SBIGINT: case SQL_C_UBIGINT: return sizeof(longlong); case SQL_C_NUMERIC: return sizeof(SQL_NUMERIC_STRUCT); default: /* For CHAR, VARCHAR, BLOB, DEFAULT...*/ return length; } } /* @type : myodbc internal @purpose : convert a possible string to a timestamp value */ my_bool str_to_ts(SQL_TIMESTAMP_STRUCT *ts, const char *str, int zeroToMin) { uint year, length; char buff[15],*to; SQL_TIMESTAMP_STRUCT tmp_timestamp; if ( !ts ) ts= (SQL_TIMESTAMP_STRUCT *) &tmp_timestamp; for ( to= buff ; *str && to < buff+sizeof(buff)-1 ; ++str ) { if ( isdigit(*str) ) *to++= *str; } length= (uint) (to-buff); if ( length == 6 || length == 12 ) /* YYMMDD or YYMMDDHHMMSS */ { memmove(to+2, to, length); if ( buff[0] <= '6' ) { buff[0]='2'; buff[1]='0'; } else { buff[0]='1'; buff[1]='9'; } length+= 2; to+= 2; } if ( length < 14 ) strfill(to,14 - length,'0'); else *to= 0; year= (digit(buff[0])*1000+digit(buff[1])*100+digit(buff[2])*10+digit(buff[3])); if (!strncmp(&buff[4], "00", 2) || !strncmp(&buff[6], "00", 2)) { if (!zeroToMin) /* Don't convert invalid */ return 1; /* convert invalid to min allowed */ if (!strncmp(&buff[4], "00", 2)) buff[5]= '1'; if (!strncmp(&buff[6], "00", 2)) buff[7]= '1'; } ts->year= year; ts->month= digit(buff[4])*10+digit(buff[5]); ts->day= digit(buff[6])*10+digit(buff[7]); ts->hour= digit(buff[8])*10+digit(buff[9]); ts->minute= digit(buff[10])*10+digit(buff[11]); ts->second= digit(buff[12])*10+digit(buff[13]); ts->fraction= 0; return 0; } /* @type : myodbc internal @purpose : convert a possible string to a time value */ my_bool str_to_time_st(SQL_TIME_STRUCT *ts, const char *str) { char buff[12],*to; SQL_TIME_STRUCT tmp_time; if ( !ts ) ts= (SQL_TIME_STRUCT *) &tmp_time; for ( to= buff ; *str && to < buff+sizeof(buff)-1 ; ++str ) { if ( isdigit(*str) ) *to++= *str; } ts->hour= digit(buff[0])*10+digit(buff[1]); ts->minute= digit(buff[2])*10+digit(buff[3]); ts->second= digit(buff[4])*10+digit(buff[5]); return 0; } /* @type : myodbc internal @purpose : convert a possible string to a data value. if zeroToMin is specified, YEAR-00-00 dates will be converted to the min valid ODBC date */ my_bool str_to_date(SQL_DATE_STRUCT *rgbValue, const char *str, uint length, int zeroToMin) { uint field_length,year_length,digits,i,date[3]; const char *pos; const char *end= str+length; for ( ; !isdigit(*str) && str != end ; ++str ) ; /* Calculate first number of digits. If length= 4, 8 or >= 14 then year is of format YYYY (YYYY-MM-DD, YYYYMMDD) */ for ( pos= str; pos != end && isdigit(*pos) ; ++pos ) ; digits= (uint) (pos-str); year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2; field_length= year_length-1; for ( i= 0 ; i < 3 && str != end; ++i ) { uint tmp_value= (uint) (uchar) (*str++ - '0'); while ( str != end && isdigit(str[0]) && field_length-- ) { tmp_value= tmp_value*10 + (uint) (uchar) (*str - '0'); ++str; } date[i]= tmp_value; while ( str != end && !isdigit(*str) ) ++str; field_length= 1; /* Rest fields can only be 2 */ } if (i <= 1 || (i > 1 && !date[1]) || (i > 2 && !date[2])) { if (!zeroToMin) /* Convert? */ return 1; rgbValue->year= date[0]; rgbValue->month= (i > 1 && date[1]) ? date[1] : 1; rgbValue->day= (i > 2 && date[2]) ? date[2] : 1; } else { while ( i < 3 ) date[i++]= 1; rgbValue->year= date[0]; rgbValue->month= date[1]; rgbValue->day= date[2]; } return 0; } /* @type : myodbc internal @purpose : convert a time string to a (ulong) value. At least following formats are recogniced HHMMSS HHMM HH HH.MM.SS {t HH:MM:SS } @return : HHMMSS */ ulong str_to_time_as_long(const char *str,uint length) { uint i,date[3]; const char *end= str+length; if ( length == 0 ) return 0; for ( ; !isdigit(*str) && str != end ; ++str ) --length; for ( i= 0 ; i < 3 && str != end; ++i ) { uint tmp_value= (uint) (uchar) (*str++ - '0'); --length; while ( str != end && isdigit(str[0]) ) { tmp_value= tmp_value*10 + (uint) (uchar) (*str - '0'); ++str; --length; } date[i]= tmp_value; while ( str != end && !isdigit(*str) ) { ++str; --length; } } if ( length && str != end ) return str_to_time_as_long(str, length);/* timestamp format */ if ( date[0] > 10000L || i < 3 ) /* Properly handle HHMMSS format */ return(ulong) date[0]; return(ulong) date[0] * 10000L + (ulong) (date[1]*100L+date[2]); } /* @type : myodbc internal @purpose : if there was a long time since last question, check that the server is up with mysql_ping (to force a reconnect) */ int check_if_server_is_alive( DBC FAR *dbc ) { time_t seconds= (time_t) time( (time_t*)0 ); int result= 0; if ( (ulong)(seconds - dbc->last_query_time) >= CHECK_IF_ALIVE ) { if ( mysql_ping( &dbc->mysql ) ) { /* BUG: 14639 A. The 4.1 doc says when mysql_ping() fails we can get one of the following errors from mysql_errno(); CR_COMMANDS_OUT_OF_SYNC CR_SERVER_GONE_ERROR CR_UNKNOWN_ERROR But if you do a mysql_ping() after bringing down the server you get CR_SERVER_LOST. PAH - 9.MAR.06 */ if ( mysql_errno( &dbc->mysql ) == CR_SERVER_LOST ) result = 1; } } dbc->last_query_time = seconds; return result; } /* @type : myodbc3 internal @purpose : appends quoted string to dynamic string */ my_bool dynstr_append_quoted_name(DYNAMIC_STRING *str, const char *name) { uint tmp= strlen(name); char *pos; if ( dynstr_realloc(str,tmp+3) ) return 1; pos= str->str+str->length; *pos='`'; memcpy(pos+1,name,tmp); pos[tmp+1]='`'; pos[tmp+2]= 0; /* Safety */ str->length+= tmp+2; return 0; } /* @type : myodbc3 internal @purpose : reset the db name to current_database() */ my_bool reget_current_catalog(DBC FAR *dbc) { x_free(dbc->database); dbc->database= NULL; if ( odbc_stmt(dbc, "select database()") ) { return 1; } else { MYSQL_RES *res; MYSQL_ROW row; if ( (res= mysql_store_result(&dbc->mysql)) && (row= mysql_fetch_row(res)) ) { /* if (cmp_database(row[0], dbc->database)) */ { if ( row[0] ) { dbc->database = my_strdup(row[0], MYF(MY_WME)); } else { dbc->database = NULL; } } } mysql_free_result(res); } return 0; } /* @type : myodbc internal @purpose : compare strings without regarding to case */ int myodbc_strcasecmp(const char *s, const char *t) { while (toupper((uchar) *s) == toupper((uchar) *t++)) if (!*s++) return 0; return((int) toupper((uchar) s[0]) - (int) toupper((uchar) t[-1])); } /* @type : myodbc internal @purpose : compare strings without regarding to case */ int myodbc_casecmp(const char *s, const char *t, uint len) { while (len-- != 0 && toupper(*s++) == toupper(*t++)) ; return (int)len + 1; } /* @type : myodbc3 internal @purpose : logs the queries sent to server */ void query_print(FILE *log_file,char *query) { if ( log_file && query ) fprintf(log_file, "%s;\n",query); } FILE *init_query_log(void) { FILE *query_log; if ( (query_log= fopen(DRIVER_QUERY_LOGFILE, "a+")) ) { fprintf(query_log,"-- Query logging\n"); fprintf(query_log,"--\n"); fprintf(query_log,"-- Driver name: %s Version: %s\n",DRIVER_NAME, DRIVER_VERSION); #ifdef HAVE_LOCALTIME_R { time_t now= time(NULL); struct tm start; localtime_r(&now,&start); fprintf(query_log,"-- Timestamp: %02d%02d%02d %2d:%02d:%02d\n", start.tm_year % 100, start.tm_mon+1, start.tm_mday, start.tm_hour, start.tm_min, start.tm_sec); #endif /* HAVE_LOCALTIME_R */ fprintf(query_log,"\n"); } } return query_log; } void end_query_log(FILE *query_log) { if ( query_log ) { fclose(query_log); query_log= 0; } } my_bool is_minimum_version(const char *server_version,const char *version, uint length) { if ( strncmp(server_version,version,length) >= 0 ) return TRUE; return FALSE; } /** Escapes a string that may contain wildcard characters (%, _) and other problematic characters (", ', \n, etc). Like mysql_real_escape_string() but also including % and _. Can be used with an identified by passing escape_id. @param[in] mysql Pointer to MYSQL structure @param[out] to Buffer for escaped string @param[in] to_length Length of destination buffer, or 0 for "big enough" @param[in] from The string to escape @param[in] length The length of the string to escape @param[in] escape_id Escaping an identified that will be quoted */ ulong myodbc_escape_string(MYSQL *mysql __attribute__((unused)), char *to, ulong to_length, const char *from, ulong length, int escape_id) { const char *to_start= to; const char *end, *to_end=to_start + (to_length ? to_length-1 : 2*length); my_bool overflow= FALSE; CHARSET_INFO *charset_info= mysql->charset; my_bool use_mb_flag= use_mb(charset_info); for (end= from + length; from < end; ++from) { char escape= 0; int tmp_length; if (use_mb_flag && (tmp_length= my_ismbchar(charset_info, from, end))) { if (to + tmp_length > to_end) { overflow= TRUE; break; } while (tmp_length--) *to++= *from++; --from; continue; } /* If the next character appears to begin a multi-byte character, we escape that first byte of that apparent multi-byte character. (The character just looks like a multi-byte character -- if it were actually a multi-byte character, it would have been passed through in the test above.) Without this check, we can create a problem by converting an invalid multi-byte character into a valid one. For example, 0xbf27 is not a valid GBK character, but 0xbf5c is. (0x27 = ', 0x5c = \) */ if (use_mb_flag && (tmp_length= my_mbcharlen(charset_info, *from)) > 1) escape= *from; else switch (*from) { case 0: /* Must be escaped for 'mysql' */ escape= '0'; break; case '\n': /* Must be escaped for logs */ escape= 'n'; break; case '\r': escape= 'r'; break; case '\\': case '\'': case '"': /* Better safe than sorry */ case '_': case '%': escape= *from; break; case '\032': /* This gives problems on Win32 */ escape= 'Z'; break; } /* if escaping an id, only handle back-tick */ if (escape_id) { if (*from == '`') escape= *from; else escape= 0; } if (escape) { if (to + 2 > to_end) { overflow= TRUE; break; } *to++= '\\'; *to++= escape; } else { if (to + 1 > to_end) { overflow= TRUE; break; } *to++= *from; } } *to= 0; return overflow ? (ulong)~0 : (ulong) (to - to_start); } /** Scale an int[] representing SQL_C_NUMERIC @param[in] ary Array in little endian form @param[in] s Scale */ static void sqlnum_scale(int *ary, int s) { /* multiply out all pieces */ while (s--) { ary[0] *= 10; ary[1] *= 10; ary[2] *= 10; ary[3] *= 10; ary[4] *= 10; ary[5] *= 10; ary[6] *= 10; ary[7] *= 10; } } /** Unscale an int[] representing SQL_C_NUMERIC. This leaves the last element (0) with the value of the last digit. @param[in] ary Array in little endian form */ static void sqlnum_unscale_le(int *ary) { int i; for (i= 7; i > 0; --i) { ary[i - 1] += (ary[i] % 10) << 16; ary[i] /= 10; } } /** Unscale an int[] representing SQL_C_NUMERIC. This leaves the last element (7) with the value of the last digit. @param[in] ary Array in big endian form */ static void sqlnum_unscale_be(int *ary, int start) { int i; for (i= start; i < 7; ++i) { ary[i + 1] += (ary[i] % 10) << 16; ary[i] /= 10; } } /** Perform the carry to get all elements below 2^16. Should be called right after sqlnum_scale(). @param[in] ary Array in little endian form */ static void sqlnum_carry(int *ary) { int i; /* carry over rest of structure */ for (i= 0; i < 7; ++i) { ary[i+1] += ary[i] >> 16; ary[i] &= 0xffff; } } /** Retrieve a SQL_NUMERIC_STRUCT from a string. The requested scale and precesion are first read from sqlnum, and then updated values are written back at the end. @param[in] numstr String representation of number to convert @param[in] sqlnum Destination struct @param[in] overflow_ptr Whether or not whole-number overflow occurred. This indicates failure, and the result of sqlnum is undefined. */ void sqlnum_from_str(const char *numstr, SQL_NUMERIC_STRUCT *sqlnum, int *overflow_ptr) { /* We use 16 bits of each integer to convert the current segment of the number leaving extra bits to multiply/carry */ int build_up[8], tmp_prec_calc[8]; /* current segment as integer */ unsigned int curnum; /* current segment digits copied for strtoul() */ char curdigs[5]; /* number of digits in current segment */ int usedig; int i; int len; char *decpt= strchr(numstr, '.'); int overflow= 0; SQLSCHAR reqscale= sqlnum->scale; SQLCHAR reqprec= sqlnum->precision; memset(&sqlnum->val, 0, sizeof(sqlnum->val)); memset(build_up, 0, sizeof(build_up)); /* handle sign */ if (!(sqlnum->sign= !(*numstr == '-'))) ++numstr; len= (int) strlen(numstr); sqlnum->precision= len; sqlnum->scale= 0; /* process digits in groups of <=4 */ for (i= 0; i < len; i += usedig) { if (i + 4 < len) usedig= 4; else usedig= len - i; /* if we have the decimal point, ignore it by setting it to the last char (will be ignored by strtoul) */ if (decpt && decpt >= numstr + i && decpt < numstr + i + usedig) { usedig = (int) (decpt - (numstr + i) + 1); sqlnum->scale= len - (i + usedig); --sqlnum->precision; decpt= NULL; } /* terminate prematurely if we can't do anything else */ /*if (overflow && !decpt) break; else */if (overflow) /*continue;*/goto end; /* grab just this piece, and convert to int */ memcpy(curdigs, numstr + i, usedig); curdigs[usedig]= 0; curnum= strtoul(curdigs, NULL, 10); if (curdigs[usedig - 1] == '.') sqlnum_scale(build_up, usedig - 1); else sqlnum_scale(build_up, usedig); /* add the current number */ build_up[0] += curnum; sqlnum_carry(build_up); if (build_up[7] & ~0xffff) overflow= 1; } /* scale up to SQL_DESC_SCALE */ if (reqscale > 0 && reqscale > sqlnum->scale) { while (reqscale > sqlnum->scale) { sqlnum_scale(build_up, 1); sqlnum_carry(build_up); ++sqlnum->scale; } } /* scale back, truncating decimals */ else if (reqscale < sqlnum->scale) { while (reqscale < sqlnum->scale && sqlnum->scale > 0) { sqlnum_unscale_le(build_up); build_up[0] /= 10; --sqlnum->precision; --sqlnum->scale; } } /* scale back whole numbers while there's no significant digits */ if (reqscale < 0) { memcpy(tmp_prec_calc, build_up, sizeof(build_up)); while (reqscale < sqlnum->scale) { sqlnum_unscale_le(tmp_prec_calc); if (tmp_prec_calc[0] % 10) { overflow= 1; goto end; } sqlnum_unscale_le(build_up); tmp_prec_calc[0] /= 10; build_up[0] /= 10; --sqlnum->precision; --sqlnum->scale; } } /* calculate minimum precision */ memcpy(tmp_prec_calc, build_up, sizeof(build_up)); do { sqlnum_unscale_le(tmp_prec_calc); i= tmp_prec_calc[0] % 10; tmp_prec_calc[0] /= 10; if (i == 0) --sqlnum->precision; } while (i == 0 && sqlnum->precision > 0); /* detect precision overflow */ if (sqlnum->precision > reqprec) overflow= 1; else sqlnum->precision= reqprec; /* compress results into SQL_NUMERIC_STRUCT.val */ for (i= 0; i < 8; ++i) { int elem= 2 * i; sqlnum->val[elem]= build_up[i] & 0xff; sqlnum->val[elem+1]= (build_up[i] >> 8) & 0xff; } end: if (overflow_ptr) *overflow_ptr= overflow; } /** Convert a SQL_NUMERIC_STRUCT to a string. Only val and sign are read from the struct. precision and scale will be updated on the struct with the final values used in the conversion. @param[in] sqlnum Source struct @param[in] numstr Buffer to convert into string. Note that you MUST use numbegin to read the result string. This should point to the LAST byte available. (We fill in digits backwards.) @param[in] numbegin String pointer that will be set to the start of the result string. @param[in] reqprec Requested precision @param[in] reqscale Requested scale @param[in] truncptr Pointer to set the truncation type encountered. If SQLNUM_TRUNC_WHOLE, this indicates a failure and the contents of numstr are undefined and numbegin will not be written to. */ void sqlnum_to_str(SQL_NUMERIC_STRUCT *sqlnum, SQLCHAR *numstr, SQLCHAR **numbegin, SQLCHAR reqprec, SQLSCHAR reqscale, int *truncptr) { int expanded[8]; int i, j; int max_space= 0; int calcprec= 0; int trunc= 0; /* truncation indicator */ *numstr--= 0; /* it's expected to have enough space (~at least min(39, max(prec, scale+2)) + 3) */ /* expand the packed sqlnum->val so we have space to divide through expansion happens into an array in big-endian form */ for (i= 0; i < 8; ++i) expanded[7 - i]= (sqlnum->val[(2 * i) + 1] << 8) | sqlnum->val[2 * i]; /* max digits = 39 = log_10(2^128)+1 */ for (j= 0; j < 39; ++j) { /* skip empty prefix */ while (!expanded[max_space]) ++max_space; /* if only the last piece has a value, it's the end */ if (max_space >= 7) { i= 7; if (!expanded[7]) { /* special case for zero, we'll end immediately */ if (!*(numstr + 1)) { *numstr--= '0'; calcprec= 1; } break; } } else { /* extract the next digit */ sqlnum_unscale_be(expanded, max_space); } *numstr--= '0' + (expanded[7] % 10); expanded[7] /= 10; ++calcprec; if (j == reqscale - 1) *numstr--= '.'; } sqlnum->scale= reqscale; /* add <- dec pt */ if (calcprec < reqscale) { while (calcprec < reqscale) { *numstr--= '0'; --reqscale; } *numstr--= '.'; *numstr--= '0'; } /* handle fractional truncation */ if (calcprec > reqprec && reqscale > 0) { SQLCHAR *end= numstr + strlen((char *)numstr) - 1; while (calcprec > reqprec && reqscale) { *end--= 0; --calcprec; --reqscale; } if (calcprec > reqprec && reqscale == 0) { trunc= SQLNUM_TRUNC_WHOLE; goto end; } if (*end == '.') *end--= 0; else { /* move the dec pt-- ??? */ /* char c2, c= numstr[calcprec - reqscale]; numstr[calcprec - reqscale]= '.'; while (reqscale) { c2= numstr[calcprec + 1 - reqscale]; numstr[calcprec + 1 - reqscale]= c; c= c2; --reqscale; } */ } trunc= SQLNUM_TRUNC_FRAC; } /* add zeros for negative scale */ if (reqscale < 0) { int i; reqscale *= -1; for (i= 1; i <= calcprec; ++i) *(numstr + i - reqscale)= *(numstr + i); numstr -= reqscale; memset(numstr + calcprec + 1, '0', reqscale); } sqlnum->precision= calcprec; /* finish up, handle auxilary fix-ups */ if (!sqlnum->sign) *numstr--= '-'; ++numstr; *numbegin= numstr; end: if (truncptr) *truncptr= trunc; } /** Detect if a statement is a SET NAMES statement. */ int is_set_names_statement(SQLCHAR *query) { /* Skip leading spaces */ while (query && isspace(*query)) ++query; return myodbc_casecmp((char *)query, "SET NAMES", 9) == 0; } /** Detect if a statement is a SELECT statement. */ int is_select_statement(SQLCHAR *query) { /* Skip leading spaces */ while (query && isspace(*query)) ++query; return myodbc_casecmp((char *)query, "SELECT", 6) == 0; } /** Adjust a pointer based on bind offset and bind type. @param[in] ptr The base pointer @param[in] bind_offset_ptr The bind offset ptr (can be NULL). (SQL_ATTR_PARAM_BIND_OFFSET_PTR, SQL_ATTR_ROW_BIND_OFFSET_PTR, SQL_DESC_BIND_OFFSET_PTR) @param[in] bind_type The bind type. Should be SQL_BIND_BY_COLUMN (0) or the length of a row for row-wise binding. (SQL_ATTR_PARAM_BIND_TYPE, SQL_ATTR_ROW_BIND_TYPE, SQL_DESC_BIND_TYPE) @param[in] default_size The column size if bind type = SQL_BIND_BY_COLUMN. @param[in] row The row number. @return The base pointer with the offset added. If the base pointer is NULL, NULL is returned. */ void *ptr_offset_adjust(void *ptr, SQLULEN *bind_offset_ptr, SQLINTEGER bind_type, SQLINTEGER default_size, SQLULEN row) { size_t offset= 0; if (bind_offset_ptr) offset= (size_t) *bind_offset_ptr; if (bind_type == SQL_BIND_BY_COLUMN) offset+= default_size * row; else offset+= bind_type * row; return ptr ? ((SQLCHAR *) ptr) + offset : NULL; } /** Sets the value of @@sql_select_limit @param[in] dbc dbc handler @param[in] new_value Value to set @@sql_select_limit. Returns new_value if operation was successful, -1 otherwise */ SQLRETURN set_sql_select_limit(DBC FAR *dbc, SQLULEN new_value) { char query[44]; SQLRETURN rc; /* Both 0 and max(SQLULEN) value mean no limit and sql_select_limit to DEFAULT */ if (new_value == dbc->sql_select_limit || new_value == sql_select_unlimited && dbc->sql_select_limit == 0) return SQL_SUCCESS; if (new_value > 0 && new_value < sql_select_unlimited) sprintf(query, "set @@sql_select_limit=%lu", (unsigned long)new_value); else { strcpy(query, "set @@sql_select_limit=DEFAULT"); new_value= 0; } if (SQL_SUCCEEDED(rc= odbc_stmt(dbc, query))) { dbc->sql_select_limit= new_value; } return rc; } /** Detects the parameter type. @param[in] proc procedure parameter string @param[in] len param string length @param[out] ptype pointer where to write the param type Returns position in the param string after parameter type */ SQLCHAR *proc_get_param_type(SQLCHAR *proc, int len, SQLSMALLINT *ptype) { while (isspace(*proc) && (len--)) ++proc; if (len >= 6 && !myodbc_casecmp(proc, "INOUT ", 6)) { *ptype= (SQLSMALLINT) SQL_PARAM_INPUT_OUTPUT; return proc + 6; } if (len >= 4 && !myodbc_casecmp(proc, "OUT ", 4)) { *ptype= (SQLSMALLINT) SQL_PARAM_OUTPUT; return proc + 4; } if (len >= 3 && !myodbc_casecmp(proc, "IN ", 3)) { *ptype= (SQLSMALLINT) SQL_PARAM_INPUT; return proc + 3; } *ptype= (SQLSMALLINT)SQL_PARAM_INPUT; return proc; } /** Detects the parameter name @param[in] proc procedure parameter string @param[in] len param string length @param[out] cname pointer where to write the param name Returns position in the param string after parameter name */ SQLCHAR* proc_get_param_name(SQLCHAR *proc, int len, SQLCHAR *cname) { char quote_symbol= '\0'; while (isspace(*proc) && (len--)) ++proc; /* can be '"' if ANSI_QUOTE is enabled */ if (*proc == '`' || *proc == '"') { quote_symbol= *proc; ++proc; } while ((len--) && (quote_symbol != '\0' ? *proc != quote_symbol : !isspace(*proc))) *(cname++)= *(proc++); return quote_symbol ? proc + 1 : proc; } /** Detects the parameter data type @param[in] proc procedure parameter string @param[in] len param string length @param[out] cname pointer where to write the param type name Returns position in the param string after parameter type name */ SQLCHAR* proc_get_param_dbtype(SQLCHAR *proc, int len, SQLCHAR *ptype) { char *trim_str, *start_pos= ptype; while (isspace(*proc) && (len--)) ++proc; while (*proc && (len--) ) *(ptype++)= *(proc++); /* remove the character set definition */ if (trim_str= strstr( myodbc_strlwr(start_pos, 0), " charset ")) { ptype= trim_str; (*ptype)= 0; } /* trim spaces from the end */ ptype-=1; while (isspace(*(ptype))) { *ptype= 0; --ptype; } return proc; } SQLTypeMap SQL_TYPE_MAP_values[TYPE_MAP_SIZE]= { /* SQL_BIT= -7 */ {"bit", 3, SQL_BIT, MYSQL_TYPE_BIT, 1, 1}, {"bool", 4, SQL_BIT, MYSQL_TYPE_BIT, 1, 1}, /* SQL_TINY= -6 */ {"tinyint", 7, SQL_TINYINT, MYSQL_TYPE_TINY, 1, 1}, /* SQL_BIGINT= -5 */ {"bigint", 6, SQL_BIGINT, MYSQL_TYPE_LONGLONG, 20, 1}, /* SQL_LONGVARBINARY= -4 */ {"long varbinary", 14, SQL_LONGVARBINARY, MYSQL_TYPE_MEDIUM_BLOB, 16777215, 1}, {"blob", 4, SQL_LONGVARBINARY, MYSQL_TYPE_BLOB, 65535, 1}, {"longblob", 8, SQL_LONGVARBINARY, MYSQL_TYPE_LONG_BLOB, 4294967295UL, 1}, {"tinyblob", 8, SQL_LONGVARBINARY, MYSQL_TYPE_TINY_BLOB, 255, 1}, {"mediumblob", 10, SQL_LONGVARBINARY, MYSQL_TYPE_MEDIUM_BLOB, 16777215,1 }, /* SQL_VARBINARY= -3 */ {"varbinary", 9, SQL_VARBINARY, MYSQL_TYPE_VAR_STRING, 0, 1}, /* SQL_BINARY= -2 */ {"binary", 6, SQL_BINARY, MYSQL_TYPE_STRING, 0, 1}, /* SQL_LONGVARCHAR= -1 */ {"long varchar", 12, SQL_LONGVARCHAR, MYSQL_TYPE_MEDIUM_BLOB, 16777215, 0}, {"text", 4, SQL_LONGVARCHAR, MYSQL_TYPE_BLOB, 65535, 0}, {"mediumtext", 10, SQL_LONGVARCHAR, MYSQL_TYPE_MEDIUM_BLOB, 16777215, 0}, {"longtext", 8, SQL_LONGVARCHAR, MYSQL_TYPE_LONG_BLOB, 4294967295UL, 0}, {"tinytext", 8, SQL_LONGVARCHAR, MYSQL_TYPE_TINY_BLOB, 255, 0}, /* SQL_CHAR= 1 */ {"char", 4, SQL_CHAR, MYSQL_TYPE_STRING, 0, 0}, {"enum", 4, SQL_CHAR, MYSQL_TYPE_STRING, 0, 0}, {"set", 3, SQL_CHAR, MYSQL_TYPE_STRING, 0, 0}, /* SQL_NUMERIC= 2 */ {"numeric", 7, SQL_NUMERIC, MYSQL_TYPE_DECIMAL, 0, 1}, /* SQL_DECIMAL= 3 */ {"decimal", 7, SQL_DECIMAL, MYSQL_TYPE_DECIMAL, 0, 1}, /* SQL_INTEGER= 4 */ {"int", 3, SQL_INTEGER, MYSQL_TYPE_LONG, 10, 1}, {"mediumint", 9, SQL_INTEGER, MYSQL_TYPE_INT24, 8, 1}, /* SQL_SMALLINT= 5 */ {"smallint", 8, SQL_SMALLINT, MYSQL_TYPE_SHORT, 5, 1}, /* SQL_REAL= 7 */ {"float", 5, SQL_REAL, MYSQL_TYPE_FLOAT, 7, 1}, /* SQL_DOUBLE= 8 */ {"double", 6, SQL_DOUBLE, MYSQL_TYPE_DOUBLE, 15, 1}, /* SQL_DATETIME= 9 */ {"datetime", 8, SQL_TYPE_TIMESTAMP, MYSQL_TYPE_DATETIME, 19, 1}, /* SQL_VARCHAR= 12 */ {"varchar", 7, SQL_VARCHAR, MYSQL_TYPE_VARCHAR, 0, 0}, /* SQL_TYPE_DATE= 91 */ {"date", 4, SQL_TYPE_DATE, MYSQL_TYPE_DATE, 10, 1}, /* YEAR - SQL_SMALLINT */ {"year", 4, SQL_SMALLINT, MYSQL_TYPE_YEAR, 2, 1}, /* SQL_TYPE_TIMESTAMP= 93 */ {"timestamp", 9, SQL_TYPE_TIMESTAMP, MYSQL_TYPE_TIMESTAMP, 19, 1}, /* SQL_TYPE_TIME= 92 */ {"time", 4, SQL_TYPE_TIME, MYSQL_TYPE_TIME, 8, 1} }; /** Gets the parameter index in the type map array @param[in] ptype procedure parameter type name @param[in] len param string length Returns position in the param string after parameter type name */ int proc_get_param_sql_type_index(SQLCHAR *ptype, int len) { int i; for (i= 0; i < TYPE_MAP_SIZE; ++i) { if (len >= SQL_TYPE_MAP_values[i].name_length && (!myodbc_casecmp(ptype, SQL_TYPE_MAP_values[i].type_name, SQL_TYPE_MAP_values[i].name_length))) return i; } return 16; /* "char" */ } /** Gets the parameter info array from the map using index @param[in] index index in the param info array Pointer to the structure that contains parameter info */ SQLTypeMap *proc_get_param_map_by_index(int index) { return &SQL_TYPE_MAP_values[index]; } /** Parses parameter size and decimal digits @param[in] ptype parameter type name @param[in] len type string length @param[out] dec pointer where to write decimal digits Returns parsed size */ SQLUINTEGER proc_parse_sizes(SQLCHAR *ptype, int len, SQLSMALLINT *dec) { int parsed= 0; SQLUINTEGER param_size= 0; if (ptype == NULL) { /* That shouldn't happen though */ return 0; } while ((len > 0) && (*ptype!= ')') && (parsed < 2)) { int n_index= 0; SQLCHAR number_to_parse[16]= "\0"; /* skip all non-digit characters */ while (!isdigit(*ptype) && (len-- >= 0) && (*ptype!= ')')) ++ptype; /* add digit characters to the buffer for parsing */ while (isdigit(*ptype) && (len-- >= 0)) { number_to_parse[n_index++]= *ptype; ++ptype; } /* 1st number is column size, 2nd is decimal digits */ if (!parsed) param_size= atoi(number_to_parse); else *dec= (SQLSMALLINT)atoi(number_to_parse); ++parsed; } return param_size; } /** Determines the length of ENUM/SET @param[in] ptype parameter type name @param[in] len type string length @param[in] is_enum flag to treat string as ENUM instead of SET Returns size of ENUM/SET */ SQLUINTEGER proc_parse_enum_set(SQLCHAR *ptype, int len, BOOL is_enum) { SQLUINTEGER total_len= 0, elem_num= 0, max_len= 0, cur_len= 0; char quote_symbol= '\0'; /* theoretically ')' can be inside quotes as part of enum value */ while ((len > 0) && (quote_symbol != '\0' || *ptype!= ')')) { if (*ptype == quote_symbol) { quote_symbol= '\0'; max_len= max(cur_len, max_len); } else if (*ptype == '\'' || *ptype == '"') { quote_symbol= *ptype; cur_len= 0; ++elem_num; } else if (quote_symbol) { ++cur_len; ++total_len; } ++ptype; --len; } return is_enum ? max_len : total_len + elem_num - 1; } /** Returns parameter size and decimal digits @param[in] ptype parameter type name @param[in] len type string length @param[in] sql_type_index index in the param info array @param[out] dec pointer where to write decimal digits Returns parameter size */ SQLUINTEGER proc_get_param_size(SQLCHAR *ptype, int len, int sql_type_index, SQLSMALLINT *dec) { SQLUINTEGER param_size= SQL_TYPE_MAP_values[sql_type_index].type_length; SQLCHAR *start_pos= strchr(ptype, '('); SQLCHAR *end_pos= strrchr(ptype, ')'); /* no decimal digits by default */ *dec= SQL_NO_TOTAL; switch (SQL_TYPE_MAP_values[sql_type_index].mysql_type) { /* these type sizes need to be parsed */ case MYSQL_TYPE_DECIMAL: param_size= proc_parse_sizes(start_pos, end_pos - start_pos, dec); if(!param_size) param_size= 10; /* by default */ break; case MYSQL_TYPE_YEAR: *dec= 0; param_size= proc_parse_sizes(start_pos, end_pos - start_pos, dec); if(!param_size) param_size= 4; /* by default */ break; case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: if (!myodbc_strcasecmp(SQL_TYPE_MAP_values[sql_type_index].type_name, "set")) { param_size= proc_parse_enum_set(start_pos, end_pos - start_pos, FALSE); } else if (!myodbc_strcasecmp(SQL_TYPE_MAP_values[sql_type_index].type_name, "enum")) { param_size= proc_parse_enum_set(start_pos, end_pos - start_pos, TRUE); } else /* just normal character type */ { param_size= proc_parse_sizes(start_pos, end_pos - start_pos, dec); if (param_size == 0 && SQL_TYPE_MAP_values[sql_type_index].sql_type == SQL_BINARY) param_size= 1; } break; case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_BIT: case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: *dec= 0; break; } return param_size; } /** Gets parameter columns size @param[in] stmt statement @param[in] sql_type_index index in the param info array @param[in] col_size parameter size @param[in] decimal_digits write decimal digits @param[in] flags field flags Returns parameter octet length */ SQLLEN proc_get_param_col_len(STMT *stmt, int sql_type_index, SQLULEN col_size, SQLSMALLINT decimal_digits, unsigned int flags, char * str_buff) { MYSQL_FIELD temp_fld; temp_fld.length= (unsigned long)col_size + (SQL_TYPE_MAP_values[sql_type_index].mysql_type == MYSQL_TYPE_DECIMAL ? 1 + (flags & UNSIGNED_FLAG ? 0 : 1) : 0); /* add 1for sign, if needed, and 1 for decimal point */ temp_fld.max_length= col_size; temp_fld.decimals= decimal_digits; temp_fld.flags= flags; temp_fld.charsetnr= stmt->dbc->ansi_charset_info->number; temp_fld.type= SQL_TYPE_MAP_values[sql_type_index].mysql_type; if (str_buff != NULL) { return fill_column_size_buff(str_buff, stmt, &temp_fld); } else { return get_column_size( stmt, &temp_fld); } } /** Gets parameter octet length @param[in] stmt statement @param[in] sql_type_index index in the param info array @param[in] col_size parameter size @param[in] decimal_digits write decimal digits @param[in] flags field flags Returns parameter octet length */ SQLLEN proc_get_param_octet_len(STMT *stmt, int sql_type_index, SQLULEN col_size, SQLSMALLINT decimal_digits, unsigned int flags, char * str_buff) { MYSQL_FIELD temp_fld; temp_fld.length= (unsigned long)col_size + (SQL_TYPE_MAP_values[sql_type_index].mysql_type == MYSQL_TYPE_DECIMAL ? 1 + (flags & UNSIGNED_FLAG ? 0 : 1) : 0); /* add 1for sign, if needed, and 1 for decimal point */ temp_fld.max_length= col_size; temp_fld.decimals= decimal_digits; temp_fld.flags= flags; temp_fld.charsetnr= stmt->dbc->ansi_charset_info->number; temp_fld.type= SQL_TYPE_MAP_values[sql_type_index].mysql_type; if (str_buff != NULL) { return fill_transfer_oct_len_buff(str_buff, stmt, &temp_fld); } else { return get_transfer_octet_length(stmt, &temp_fld); } } /** tokenize the string by putting \0 bytes to separate params @param[in] str parameters string @param[out] params_num number of detected parameters Returns pointer to the first param */ char *proc_param_tokenize(char *str, int *params_num) { BOOL bracket_open= 0; char *str_begin= str, quote_symbol='\0'; int len= strlen(str); *params_num= 0; /* if no params at all */ while (len > 0 && isspace(*str)) { ++str; --len; } if (len && *str && *str != ')') *params_num= 1; while (len > 0) { /* Making sure that a bracket is not inside quotes. that's possible for SET or ENUM values */ if (quote_symbol == '\0') { if (!bracket_open && *str == ',') { *str= '\0'; ++(*params_num); } else if (*str == '(') { bracket_open= 1; } else if (*str == ')') { bracket_open= 0; } else if (*str == '"' || *str == '\'') { quote_symbol= *str; } } else if( *str == quote_symbol) { quote_symbol= '\0'; } ++str; --len; } return str_begin; } /** goes to the next token in \0-terminated string sequence @param[in] str parameters string @param[in] str_end end of the sequence Returns pointer to the next token in sequence */ char *proc_param_next_token(char *str, char *str_end) { int end_token= strlen(str); /* return the next string after \0 byte */ if (str + end_token + 1 < str_end) return (char*)(str + end_token + 1); return 0; } /** deletes the list element and moves the pointer forward @param[in] elem item to delete Returns pointer to the next list element */ LIST *list_delete_forward(LIST *elem) { if(elem->prev) elem->prev->next= elem->next; if(elem->next) { elem->next->prev= elem->prev; elem= elem->next; } return elem; } /** Sets row_count in STMT's MYSQL_RES and affected rows property MYSQL object. Primary use is to set number of affected rows for constructed resulsets. Setting mysql.affected_rows is required for SQLRowCount to return correct data for such resultsets. */ void set_row_count(STMT *stmt, my_ulonglong rows) { if (stmt != NULL && stmt->result != NULL) { stmt->result->row_count= rows; stmt->dbc->mysql.affected_rows= rows; } } mysql-connector-odbc-5.1.10-src/driver/CMakeLists.txt100644 15766 12 5044 11707541005 21224 0ustar00cteamstaff# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## SET(DRIVER_SRCS ansi.c catalog.c catalog_no_i_s.c connect.c cursor.c desc.c dll.c error.c execute.c handle.c info.c driver.c options.c prepare.c results.c transact.c unicode.c utility.c) INCLUDE_DIRECTORIES(../util) IF(WIN32) # Headers added for convenience of VS users SET(DRIVER_SRCS ${DRIVER_SRCS} driver.def driver.rc catalog.h driver.h error.h myutil.h) ENDIF(WIN32) IF(APPLE) ADD_LIBRARY(myodbc5 MODULE ${DRIVER_SRCS}) ELSE(APPLE) ADD_LIBRARY(myodbc5 SHARED ${DRIVER_SRCS}) ENDIF(APPLE) INSTALL(TARGETS myodbc5 DESTINATION ${LIB_SUBDIR}) IF(WIN32) TARGET_LINK_LIBRARIES(myodbc5 myodbc3u ${MYSQL_CLIENT_LIBS} ${ODBCINSTLIB} ${SECURE32_LIB}) ELSE(WIN32) SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${ODBC_LINK_FLAGS}") TARGET_LINK_LIBRARIES(myodbc5 mysqlclient_r ${CMAKE_THREAD_LIBS_INIT} m) TARGET_LINK_LIBRARIES(myodbc5 myodbc3u) INCLUDE_DIRECTORIES(${DL_INCLUDES}) #TARGET_LINK_LIBRARIES(myodbc5 ${DL_LIBS}) #SET_TARGET_PROPERTIES(myodbc5 PROPERTIES LINK_FLAGS "${DL_LFLAGS}") ENDIF(WIN32) GET_TARGET_PROPERTY(DRIVER_LOCATION myodbc5 LOCATION) add_custom_command(TARGET myodbc5 POST_BUILD COMMAND ${CMAKE_COMMAND} -DDRIVER_LOCATION=${DRIVER_LOCATION} -DBINARY_DIR=${CMAKE_BINARY_DIR}/test -P ${CMAKE_SOURCE_DIR}/test/cmake/generateinifiles.cmake WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/test") mysql-connector-odbc-5.1.10-src/driver/options.c100644 15766 12 72351 11707541005 20350 0ustar00cteamstaff/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file options.c @brief Functions for handling handle attributes and options. */ #include "driver.h" #include "errmsg.h" /* @type : myodbc3 internal @purpose : sets the common connection/stmt attributes */ static SQLRETURN set_constmt_attr(SQLSMALLINT HandleType, SQLHANDLE Handle, STMT_OPTIONS *options, SQLINTEGER Attribute, SQLPOINTER ValuePtr) { switch (Attribute) { case SQL_ATTR_ASYNC_ENABLE: if (ValuePtr == (SQLPOINTER) SQL_ASYNC_ENABLE_ON) return set_handle_error(HandleType,Handle,MYERR_01S02, "Doesn't support asynchronous, changed to default",0); break; case SQL_ATTR_CURSOR_SENSITIVITY: if (ValuePtr != (SQLPOINTER) SQL_UNSPECIFIED) { return set_handle_error(HandleType,Handle,MYERR_01S02, "Option value changed to default cursor sensitivity(unspecified)",0); } break; case SQL_ATTR_CURSOR_TYPE: if (((STMT FAR*)Handle)->dbc->ds->force_use_of_forward_only_cursors) { options->cursor_type= SQL_CURSOR_FORWARD_ONLY; if (ValuePtr != (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY) return set_handle_error(HandleType,Handle,MYERR_01S02, "Forcing the use of forward-only cursor)",0); } else if (((STMT FAR*)Handle)->dbc->ds->dynamic_cursor) { if (ValuePtr != (SQLPOINTER)SQL_CURSOR_KEYSET_DRIVEN) options->cursor_type= (SQLUINTEGER)(SQLULEN)ValuePtr; else { options->cursor_type= SQL_CURSOR_STATIC; return set_handle_error(HandleType,Handle,MYERR_01S02, "Option value changed to default static cursor",0); } } else { if (ValuePtr == (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY || ValuePtr == (SQLPOINTER)SQL_CURSOR_STATIC) options->cursor_type= (SQLUINTEGER)(SQLULEN)ValuePtr; else { options->cursor_type= SQL_CURSOR_STATIC; return set_handle_error(HandleType,Handle,MYERR_01S02, "Option value changed to default static cursor",0); } } break; case SQL_ATTR_MAX_LENGTH: options->max_length= (SQLULEN) ValuePtr; break; case SQL_ATTR_MAX_ROWS: options->max_rows= (SQLULEN) ValuePtr; break; case SQL_ATTR_METADATA_ID: if (ValuePtr == (SQLPOINTER) SQL_TRUE) return set_handle_error(HandleType,Handle,MYERR_01S02, "Doesn't support SQL_ATTR_METADATA_ID to true, changed to default",0); break; case SQL_ATTR_RETRIEVE_DATA: break; case SQL_ATTR_SIMULATE_CURSOR: if (ValuePtr != (SQLPOINTER) SQL_SC_TRY_UNIQUE) return set_handle_error(HandleType,Handle,MYERR_01S02, "Option value changed to default cursor simulation",0); break; case 1226:/* MS SQL Server Extension */ case 1227: case 1228: break; case SQL_ATTR_FETCH_BOOKMARK_PTR: case SQL_ATTR_USE_BOOKMARKS: return set_handle_error(HandleType,Handle,MYERR_S1C00,NULL,0); case SQL_ATTR_QUERY_TIMEOUT: case SQL_ATTR_KEYSET_SIZE: case SQL_ATTR_CONCURRENCY: case SQL_ATTR_NOSCAN: default: /* ignored */ break; } return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : returns the common connection/stmt attributes */ static SQLRETURN get_constmt_attr(SQLSMALLINT HandleType, SQLHANDLE Handle, STMT_OPTIONS *options, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER *StringLengthPtr __attribute__((unused))) { switch (Attribute) { case SQL_ATTR_ASYNC_ENABLE: *((SQLUINTEGER *) ValuePtr)= SQL_ASYNC_ENABLE_OFF; break; case SQL_ATTR_CURSOR_SENSITIVITY: *((SQLUINTEGER *) ValuePtr)= SQL_UNSPECIFIED; break; case SQL_ATTR_CURSOR_TYPE: *((SQLUINTEGER *) ValuePtr)= options->cursor_type; break; case SQL_ATTR_MAX_LENGTH: *((SQLULEN *) ValuePtr)= options->max_length; break; case SQL_ATTR_MAX_ROWS: *((SQLULEN *) ValuePtr)= options->max_rows; break; case SQL_ATTR_METADATA_ID: *((SQLUINTEGER *) ValuePtr)= SQL_FALSE; break; case SQL_ATTR_QUERY_TIMEOUT: *((SQLUINTEGER *) ValuePtr)= SQL_QUERY_TIMEOUT_DEFAULT; break; case SQL_ATTR_RETRIEVE_DATA: *((SQLUINTEGER *) ValuePtr)= SQL_RD_DEFAULT; break; case SQL_ATTR_SIMULATE_CURSOR: *((SQLUINTEGER *) ValuePtr)= SQL_SC_TRY_UNIQUE; break; case SQL_ATTR_CONCURRENCY: *((SQLUINTEGER *) ValuePtr)= SQL_CONCUR_READ_ONLY; break; case SQL_KEYSET_SIZE: *((SQLUINTEGER *) ValuePtr)= 0L; break; case SQL_NOSCAN: *((SQLUINTEGER *) ValuePtr)= SQL_NOSCAN_ON; break; case SQL_ATTR_FETCH_BOOKMARK_PTR: case SQL_ATTR_USE_BOOKMARKS: return set_handle_error(HandleType,Handle,MYERR_S1C00,NULL,0); case 1226:/* MS SQL Server Extension */ case 1227: case 1228: default: /* ignored */ break; } return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : sets the connection attributes */ SQLRETURN SQL_API MySQLSetConnectAttr(SQLHDBC hdbc, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLengthPtr) { DBC FAR *dbc= (DBC FAR*) hdbc; switch (Attribute) { case SQL_ATTR_ACCESS_MODE: break; case SQL_ATTR_AUTOCOMMIT: if (ValuePtr != (SQLPOINTER) SQL_AUTOCOMMIT_ON) { if (!is_connected(dbc)) { dbc->commit_flag= CHECK_AUTOCOMMIT_OFF; return SQL_SUCCESS; } if (!(trans_supported(dbc)) || dbc->ds->disable_transactions) return set_conn_error(dbc,MYERR_S1C00, "Transactions are not enabled", 4000); if (autocommit_on(dbc)) return odbc_stmt(dbc,"SET AUTOCOMMIT=0"); } else if (!is_connected(dbc)) { dbc->commit_flag= CHECK_AUTOCOMMIT_ON; return SQL_SUCCESS; } else if (trans_supported(dbc) && !(autocommit_on(dbc))) return odbc_stmt(dbc,"SET AUTOCOMMIT=1"); break; case SQL_ATTR_LOGIN_TIMEOUT: { /* we can't change timeout values in post connect state */ if (is_connected(dbc)) { return set_conn_error(dbc, MYERR_S1011, NULL, 0); } else { dbc->login_timeout= (SQLUINTEGER)(SQLULEN)ValuePtr; return SQL_SUCCESS; } } break; case SQL_ATTR_CONNECTION_TIMEOUT: { /* We don't do anything with this, but we pretend that we did to be consistent with Microsoft SQL Server. */ return SQL_SUCCESS; } break; /* If this is done before connect (I would say a function sequence but .NET IDE does this) then we store the value but it is quite likely that it will get replaced by DATABASE in a DSN or connect string. */ case SQL_ATTR_CURRENT_CATALOG: { char ldb[NAME_LEN+1], *db; if (!(db= fix_str((char *)ldb, (char *)ValuePtr, StringLengthPtr))) return set_conn_error(hdbc,MYERR_S1009,NULL, 0); pthread_mutex_lock(&dbc->lock); if (is_connected(dbc)) { if (mysql_select_db(&dbc->mysql,(char*) db)) { set_conn_error(dbc,MYERR_S1000,mysql_error(&dbc->mysql),mysql_errno(&dbc->mysql)); pthread_mutex_unlock(&dbc->lock); return SQL_ERROR; } } x_free(dbc->database); dbc->database= my_strdup(db,MYF(MY_WME)); pthread_mutex_unlock(&dbc->lock); } break; case SQL_ATTR_ODBC_CURSORS: if (dbc->ds->force_use_of_forward_only_cursors && ValuePtr != (SQLPOINTER) SQL_CUR_USE_ODBC) return set_conn_error(hdbc,MYERR_01S02, "Forcing the Driver Manager to use ODBC cursor library",0); break; case SQL_OPT_TRACE: case SQL_OPT_TRACEFILE: case SQL_QUIET_MODE: case SQL_TRANSLATE_DLL: case SQL_TRANSLATE_OPTION: { char buff[100]; sprintf(buff,"Suppose to set this attribute '%d' through driver manager, not by the driver",(int) Attribute); return set_conn_error(hdbc,MYERR_01S02,buff,0); } case SQL_ATTR_PACKET_SIZE: break; case SQL_ATTR_TXN_ISOLATION: if (!is_connected(dbc)) /* no connection yet */ { dbc->txn_isolation= (SQLINTEGER)(SQLLEN)ValuePtr; return SQL_SUCCESS; } if (trans_supported(dbc)) { char buff[80]; const char *level= NULL; if ((SQLLEN)ValuePtr == SQL_TXN_SERIALIZABLE) level="SERIALIZABLE"; else if ((SQLLEN)ValuePtr == SQL_TXN_REPEATABLE_READ) level="REPEATABLE READ"; else if ((SQLLEN)ValuePtr == SQL_TXN_READ_COMMITTED) level="READ COMMITTED"; else if ((SQLLEN)ValuePtr == SQL_TXN_READ_UNCOMMITTED) level="READ UNCOMMITTED"; if (level) { SQLRETURN rc; sprintf(buff,"SET SESSION TRANSACTION ISOLATION LEVEL %s", level); if (SQL_SUCCEEDED(rc = odbc_stmt(dbc,buff))) dbc->txn_isolation= (SQLINTEGER)ValuePtr; return rc; } else { return set_dbc_error(dbc, "HY024", "Invalid attribute value", 0); } } break; case SQL_ATTR_ENLIST_IN_DTC: return set_dbc_error(dbc, "HYC00", "Optional feature not supported", 0); /* 3.x driver doesn't support any statement attributes at connection level, but to make sure all 2.x apps works fine...lets support it..nothing to lose.. */ default: return set_constmt_attr(2, dbc, &dbc->stmt_options, Attribute, ValuePtr); } return SQL_SUCCESS; } /** Retrieve connection attribute values. @param[in] hdbc @param[in] attrib @param[out] char_attr @param[out] num_attr */ SQLRETURN SQL_API MySQLGetConnectAttr(SQLHDBC hdbc, SQLINTEGER attrib, SQLCHAR **char_attr, SQLPOINTER num_attr) { DBC *dbc= (DBC *)hdbc; SQLRETURN result= SQL_SUCCESS; switch (attrib) { case SQL_ATTR_ACCESS_MODE: *((SQLUINTEGER *)num_attr)= SQL_MODE_READ_WRITE; break; case SQL_ATTR_AUTO_IPD: *((SQLUINTEGER *)num_attr)= SQL_FALSE; break; case SQL_ATTR_AUTOCOMMIT: *((SQLUINTEGER *)num_attr)= (autocommit_on(dbc) || (!(trans_supported(dbc)) ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF)); break; case SQL_ATTR_CONNECTION_DEAD: if (mysql_ping(&dbc->mysql) && (mysql_errno(&dbc->mysql) == CR_SERVER_LOST || mysql_errno(&dbc->mysql) == CR_SERVER_GONE_ERROR)) *((SQLUINTEGER *)num_attr)= SQL_CD_TRUE; else *((SQLUINTEGER *)num_attr)= SQL_CD_FALSE; break; case SQL_ATTR_CONNECTION_TIMEOUT: /* We don't support this option, so it is always 0. */ *((SQLUINTEGER *)num_attr)= 0; break; case SQL_ATTR_CURRENT_CATALOG: if (is_connected(dbc) && reget_current_catalog(dbc)) { return set_handle_error(SQL_HANDLE_DBC, hdbc, MYERR_S1000, "Unable to get current catalog", 0); } else { *char_attr= (SQLCHAR *)(dbc->database ? dbc->database : "null"); } break; case SQL_ATTR_LOGIN_TIMEOUT: *((SQLUINTEGER *)num_attr)= dbc->login_timeout; break; case SQL_ATTR_ODBC_CURSORS: if (dbc->ds->force_use_of_forward_only_cursors) *((SQLUINTEGER *)num_attr)= SQL_CUR_USE_ODBC; else *((SQLUINTEGER *)num_attr)= SQL_CUR_USE_IF_NEEDED; break; case SQL_ATTR_PACKET_SIZE: *((SQLUINTEGER *)num_attr)= dbc->mysql.net.max_packet; break; case SQL_ATTR_TXN_ISOLATION: /* If we don't know the isolation level already, we need to ask the server. */ if (!dbc->txn_isolation) { /* Unless we're not connected yet, then we just assume it will be REPEATABLE READ, which is the server default. */ if (!is_connected(dbc)) { *((SQLINTEGER *)num_attr)= SQL_TRANSACTION_REPEATABLE_READ; break; } if (odbc_stmt(dbc, "SELECT @@tx_isolation")) { return set_handle_error(SQL_HANDLE_DBC, hdbc, MYERR_S1000, "Failed to get isolation level", 0); } else { MYSQL_RES *res; MYSQL_ROW row; if ((res= mysql_store_result(&dbc->mysql)) && (row= mysql_fetch_row(res))) { if (strncmp(row[0], "READ-UNCOMMITTED", 16) == 0) { dbc->txn_isolation= SQL_TRANSACTION_READ_UNCOMMITTED; } else if (strncmp(row[0], "READ-COMMITTED", 14) == 0) { dbc->txn_isolation= SQL_TRANSACTION_READ_COMMITTED; } else if (strncmp(row[0], "REPEATABLE-READ", 15) == 0) { dbc->txn_isolation= SQL_TRANSACTION_REPEATABLE_READ; } else if (strncmp(row[0], "SERIALIZABLE", 12) == 0) { dbc->txn_isolation= SQL_TRANSACTION_SERIALIZABLE; } } mysql_free_result(res); } } *((SQLINTEGER *)num_attr)= dbc->txn_isolation; break; default: return set_handle_error(SQL_HANDLE_DBC, hdbc, MYERR_S1092, NULL, 0); } return result; } /* @type : myodbc3 internal @purpose : sets the statement attributes */ SQLRETURN SQL_API MySQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLengthPtr __attribute__((unused))) { STMT *stmt= (STMT *)hstmt; SQLRETURN result= SQL_SUCCESS; STMT_OPTIONS *options= &stmt->stmt_options; CLEAR_STMT_ERROR(stmt); switch (Attribute) { case SQL_ATTR_CURSOR_SCROLLABLE: if (ValuePtr == (SQLPOINTER)SQL_NONSCROLLABLE && options->cursor_type != SQL_CURSOR_FORWARD_ONLY) options->cursor_type= SQL_CURSOR_FORWARD_ONLY; else if (ValuePtr == (SQLPOINTER)SQL_SCROLLABLE && options->cursor_type == SQL_CURSOR_FORWARD_ONLY) options->cursor_type= SQL_CURSOR_STATIC; break; case SQL_ATTR_APP_PARAM_DESC: case SQL_ATTR_APP_ROW_DESC: { DESC *desc= (DESC *) ValuePtr; DESC **dest= NULL; desc_desc_type desc_type; /* reset to implicit if null */ if (desc == SQL_NULL_HANDLE) { if (Attribute == SQL_ATTR_APP_PARAM_DESC) stmt->apd= stmt->imp_apd; else if (Attribute == SQL_ATTR_APP_ROW_DESC) stmt->ard= stmt->imp_ard; break; } if (desc->alloc_type == SQL_DESC_ALLOC_AUTO && desc->stmt != stmt) return set_error(hstmt,MYERR_S1017, "Invalid use of an automatically allocated " "descriptor handle",0); if (desc->alloc_type == SQL_DESC_ALLOC_USER && stmt->dbc != desc->exp.dbc) return set_error(hstmt,MYERR_S1024, "Invalid attribute value",0); if (Attribute == SQL_ATTR_APP_PARAM_DESC) { dest= &stmt->apd; desc_type= DESC_PARAM; } else if (Attribute == SQL_ATTR_APP_ROW_DESC) { dest= &stmt->ard; desc_type= DESC_ROW; } if (desc->desc_type != DESC_UNKNOWN && desc->desc_type != desc_type) { return set_error(hstmt,MYERR_S1024, "Descriptor type mismatch",0); } assert(desc); assert(dest); if (desc->alloc_type == SQL_DESC_ALLOC_AUTO && (*dest)->alloc_type == SQL_DESC_ALLOC_USER) /* If we're setting back the original implicit descriptor, we must disassociate this statement from the explicit descriptor. */ desc_remove_stmt(*dest, stmt); else if (desc->alloc_type == SQL_DESC_ALLOC_USER) { /* otherwise, associate this statement with the desc */ LIST *e= (LIST *) my_malloc(sizeof(LIST), MYF(0)); e->data= stmt; desc->exp.stmts= list_add(desc->exp.stmts, e); } desc->desc_type= desc_type; *dest= desc; } break; case SQL_ATTR_AUTO_IPD: case SQL_ATTR_ENABLE_AUTO_IPD: if (ValuePtr != (SQLPOINTER)SQL_FALSE) return set_error(hstmt,MYERR_S1C00, "Optional feature not implemented",0); break; case SQL_ATTR_IMP_PARAM_DESC: case SQL_ATTR_IMP_ROW_DESC: return set_error(hstmt,MYERR_S1024, "Invalid attribute/option identifier",0); case SQL_ATTR_PARAM_BIND_OFFSET_PTR: return stmt_SQLSetDescField(stmt, stmt->apd, 0, SQL_DESC_BIND_OFFSET_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_PARAM_BIND_TYPE: return stmt_SQLSetDescField(stmt, stmt->apd, 0, SQL_DESC_BIND_TYPE, ValuePtr, SQL_IS_INTEGER); case SQL_ATTR_PARAM_OPERATION_PTR: /* need to support this ....*/ return stmt_SQLSetDescField(stmt, stmt->apd, 0, SQL_DESC_ARRAY_STATUS_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_PARAM_STATUS_PTR: /* need to support this ....*/ return stmt_SQLSetDescField(stmt, stmt->ipd, 0, SQL_DESC_ARRAY_STATUS_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_PARAMS_PROCESSED_PTR: /* need to support this ....*/ return stmt_SQLSetDescField(stmt, stmt->ipd, 0, SQL_DESC_ROWS_PROCESSED_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_PARAMSET_SIZE: return stmt_SQLSetDescField(stmt, stmt->apd, 0, SQL_DESC_ARRAY_SIZE, ValuePtr, SQL_IS_ULEN); case SQL_ATTR_ROW_ARRAY_SIZE: case SQL_ROWSET_SIZE: return stmt_SQLSetDescField(stmt, stmt->ard, 0, SQL_DESC_ARRAY_SIZE, ValuePtr, SQL_IS_ULEN); case SQL_ATTR_ROW_BIND_OFFSET_PTR: return stmt_SQLSetDescField(stmt, stmt->ard, 0, SQL_DESC_BIND_OFFSET_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_ROW_BIND_TYPE: return stmt_SQLSetDescField(stmt, stmt->ard, 0, SQL_DESC_BIND_TYPE, ValuePtr, SQL_IS_INTEGER); case SQL_ATTR_ROW_NUMBER: return set_error(hstmt,MYERR_S1000, "Trying to set read-only attribute",0); case SQL_ATTR_ROW_OPERATION_PTR: return stmt_SQLSetDescField(stmt, stmt->ard, 0, SQL_DESC_ARRAY_STATUS_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_ROW_STATUS_PTR: return stmt_SQLSetDescField(stmt, stmt->ird, 0, SQL_DESC_ARRAY_STATUS_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_ROWS_FETCHED_PTR: return stmt_SQLSetDescField(stmt, stmt->ird, 0, SQL_DESC_ROWS_PROCESSED_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_SIMULATE_CURSOR: options->simulateCursor= (SQLUINTEGER)(SQLULEN)ValuePtr; break; /* 3.x driver doesn't support any statement attributes at connection level, but to make sure all 2.x apps works fine...lets support it..nothing to lose.. */ default: result= set_constmt_attr(3,hstmt,options, Attribute,ValuePtr); } return result; } /* @type : myodbc3 internal @purpose : returns the statement attribute values */ SQLRETURN SQL_API MySQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength __attribute__((unused)), SQLINTEGER *StringLengthPtr) { SQLRETURN result= SQL_SUCCESS; STMT FAR *stmt= (STMT FAR*) hstmt; STMT_OPTIONS *options= &stmt->stmt_options; SQLINTEGER vparam= 0; SQLINTEGER len; if (!ValuePtr) ValuePtr= &vparam; if (!StringLengthPtr) StringLengthPtr= &len; switch (Attribute) { case SQL_ATTR_CURSOR_SCROLLABLE: if (options->cursor_type == SQL_CURSOR_FORWARD_ONLY) *(SQLUINTEGER*)ValuePtr= SQL_NONSCROLLABLE; else *(SQLUINTEGER*)ValuePtr= SQL_SCROLLABLE; break; case SQL_ATTR_AUTO_IPD: *(SQLUINTEGER *)ValuePtr= SQL_FALSE; break; case SQL_ATTR_PARAM_BIND_OFFSET_PTR: *(SQLPOINTER *)ValuePtr= stmt->apd->bind_offset_ptr; break; case SQL_ATTR_PARAM_BIND_TYPE: *(SQLINTEGER *)ValuePtr= stmt->apd->bind_type; break; case SQL_ATTR_PARAM_OPERATION_PTR: /* need to support this ....*/ *(SQLPOINTER *)ValuePtr= stmt->apd->array_status_ptr; break; case SQL_ATTR_PARAM_STATUS_PTR: /* need to support this ....*/ *(SQLPOINTER *)ValuePtr= stmt->ipd->array_status_ptr; break; case SQL_ATTR_PARAMS_PROCESSED_PTR: /* need to support this ....*/ *(SQLPOINTER *)ValuePtr= stmt->ipd->rows_processed_ptr; break; case SQL_ATTR_PARAMSET_SIZE: *(SQLUINTEGER *)ValuePtr= stmt->apd->array_size; break; case SQL_ATTR_ROW_ARRAY_SIZE: case SQL_ROWSET_SIZE: *(SQLUINTEGER *)ValuePtr= stmt->ard->array_size; break; case SQL_ATTR_ROW_BIND_OFFSET_PTR: *((SQLPOINTER *) ValuePtr)= stmt->ard->bind_offset_ptr; break; case SQL_ATTR_ROW_BIND_TYPE: *((SQLINTEGER *) ValuePtr)= stmt->ard->bind_type; break; case SQL_ATTR_ROW_NUMBER: *(SQLUINTEGER *)ValuePtr= stmt->current_row+1; break; case SQL_ATTR_ROW_OPERATION_PTR: /* need to support this ....*/ *(SQLPOINTER *)ValuePtr= stmt->ard->array_status_ptr; break; case SQL_ATTR_ROW_STATUS_PTR: *(SQLPOINTER *)ValuePtr= stmt->ird->array_status_ptr; break; case SQL_ATTR_ROWS_FETCHED_PTR: *(SQLPOINTER *)ValuePtr= stmt->ird->rows_processed_ptr; break; case SQL_ATTR_SIMULATE_CURSOR: *(SQLUINTEGER *)ValuePtr= options->simulateCursor; break; case SQL_ATTR_APP_ROW_DESC: *(SQLPOINTER *)ValuePtr= stmt->ard; *StringLengthPtr= sizeof(SQLPOINTER); break; case SQL_ATTR_IMP_ROW_DESC: *(SQLPOINTER *)ValuePtr= stmt->ird; *StringLengthPtr= sizeof(SQLPOINTER); break; case SQL_ATTR_APP_PARAM_DESC: *(SQLPOINTER *)ValuePtr= stmt->apd; *StringLengthPtr= sizeof(SQLPOINTER); break; case SQL_ATTR_IMP_PARAM_DESC: *(SQLPOINTER *)ValuePtr= stmt->ipd; *StringLengthPtr= sizeof(SQLPOINTER); break; /* 3.x driver doesn't support any statement attributes at connection level, but to make sure all 2.x apps works fine...lets support it..nothing to lose.. */ default: result= get_constmt_attr(3,hstmt,options, Attribute,ValuePtr, StringLengthPtr); } return result; } /* @type : ODBC 3.0 API @purpose : sets the environment attributes */ SQLRETURN SQL_API SQLSetEnvAttr(SQLHENV henv, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength __attribute__((unused))) { if (((ENV FAR *)henv)->connections) return set_env_error(henv, MYERR_S1010, NULL, 0); switch (Attribute) { case SQL_ATTR_ODBC_VERSION: ((ENV FAR *)henv)->odbc_ver= (SQLINTEGER)(SQLLEN)ValuePtr; break; case SQL_ATTR_OUTPUT_NTS: if (ValuePtr == (SQLPOINTER)SQL_TRUE) break; default: return set_env_error(henv,MYERR_S1C00,NULL,0); } return SQL_SUCCESS; } /* @type : ODBC 3.0 API @purpose : returns the environment attributes */ SQLRETURN SQL_API SQLGetEnvAttr(SQLHENV henv, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength __attribute__((unused)), SQLINTEGER *StringLengthPtr __attribute__((unused))) { switch ( Attribute ) { case SQL_ATTR_CONNECTION_POOLING: *(SQLINTEGER*)ValuePtr = SQL_CP_OFF; break; case SQL_ATTR_ODBC_VERSION: *(SQLINTEGER*)ValuePtr= ((ENV FAR *)henv)->odbc_ver; break; case SQL_ATTR_OUTPUT_NTS: *((SQLINTEGER*)ValuePtr)= SQL_TRUE; break; default: return set_env_error(henv,MYERR_S1C00,NULL,0); } return SQL_SUCCESS; } SQLRETURN SQL_API SQLGetStmtOption(SQLHSTMT hstmt,SQLUSMALLINT option, SQLPOINTER param) { return MySQLGetStmtAttr(hstmt, option, param, SQL_NTS, (SQLINTEGER *)NULL); } SQLRETURN SQL_API SQLSetStmtOption(SQLHSTMT hstmt, SQLUSMALLINT option, SQLULEN param) { return MySQLSetStmtAttr(hstmt, option, (SQLPOINTER)param, SQL_NTS); } mysql-connector-odbc-5.1.10-src/driver/info.c100644 15766 12 133565 11707541005 17635 0ustar00cteamstaff/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file info.c @brief Driver information functions. */ #include "driver.h" #define MYINFO_SET_ULONG(val) \ do { \ *((SQLUINTEGER *)num_info)= (val); \ *value_len= sizeof(SQLUINTEGER); \ return SQL_SUCCESS; \ } while(0) #define MYINFO_SET_USHORT(val) \ do { \ *((SQLUSMALLINT *)num_info)= (val); \ *value_len= sizeof(SQLUSMALLINT); \ return SQL_SUCCESS; \ } while(0) #define MYINFO_SET_STR(val) \ do { \ *char_info= (SQLCHAR *)(val); \ return SQL_SUCCESS; \ } while(0) static my_bool myodbc_ov2_inited= 0; /** Return general information about the driver and data source associated with a connection. @param[in] hdbc Handle of database connection @param[in] fInfoType Type of information to retrieve @param[out] char_info Pointer to buffer for returning string @param[out] num_info Pointer to buffer for returning numeric info @param[out] value_len Pointer to buffer for returning length (only used for numeric data) */ SQLRETURN SQL_API MySQLGetInfo(SQLHDBC hdbc, SQLUSMALLINT fInfoType, SQLCHAR **char_info, SQLPOINTER num_info, SQLSMALLINT *value_len) { DBC *dbc= (DBC *)hdbc; SQLSMALLINT dummy; SQLINTEGER dummy_value; if (!value_len) value_len= &dummy; if (!num_info) num_info= &dummy_value; switch (fInfoType) { case SQL_ACTIVE_ENVIRONMENTS: MYINFO_SET_USHORT(0); case SQL_AGGREGATE_FUNCTIONS: MYINFO_SET_ULONG(SQL_AF_ALL | SQL_AF_AVG | SQL_AF_COUNT | SQL_AF_DISTINCT | SQL_AF_MAX | SQL_AF_MIN | SQL_AF_SUM); case SQL_ALTER_DOMAIN: MYINFO_SET_ULONG(0); case SQL_ALTER_TABLE: /** @todo check if we should report more */ MYINFO_SET_ULONG(SQL_AT_ADD_COLUMN | SQL_AT_DROP_COLUMN); case SQL_ASYNC_MODE: MYINFO_SET_ULONG(SQL_AM_NONE); case SQL_BATCH_ROW_COUNT: MYINFO_SET_ULONG(SQL_BRC_EXPLICIT); case SQL_BATCH_SUPPORT: MYINFO_SET_ULONG(SQL_BS_SELECT_EXPLICIT | SQL_BS_ROW_COUNT_EXPLICIT | SQL_BS_SELECT_PROC | SQL_BS_ROW_COUNT_PROC); case SQL_BOOKMARK_PERSISTENCE: MYINFO_SET_ULONG(0); case SQL_CATALOG_LOCATION: MYINFO_SET_USHORT(SQL_CL_START); case SQL_CATALOG_NAME: MYINFO_SET_STR((dbc->ds && dbc->ds->no_catalog) ? "" : "Y"); case SQL_CATALOG_NAME_SEPARATOR: MYINFO_SET_STR((dbc->ds && dbc->ds->no_catalog) ? "" : "."); case SQL_CATALOG_TERM: MYINFO_SET_STR((dbc->ds && dbc->ds->no_catalog) ? "" : "database"); case SQL_CATALOG_USAGE: MYINFO_SET_ULONG((!dbc->ds || !dbc->ds->no_catalog) ? (SQL_CU_DML_STATEMENTS | SQL_CU_PROCEDURE_INVOCATION | SQL_CU_TABLE_DEFINITION | SQL_CU_INDEX_DEFINITION | SQL_CU_PRIVILEGE_DEFINITION) : 0); case SQL_COLLATION_SEQ: MYINFO_SET_STR(dbc->mysql.charset->name); case SQL_COLUMN_ALIAS: MYINFO_SET_STR("Y"); case SQL_CONCAT_NULL_BEHAVIOR: MYINFO_SET_USHORT(SQL_CB_NULL); case SQL_CONVERT_BIGINT: case SQL_CONVERT_BIT: case SQL_CONVERT_CHAR: case SQL_CONVERT_DATE: case SQL_CONVERT_DECIMAL: case SQL_CONVERT_DOUBLE: case SQL_CONVERT_FLOAT: case SQL_CONVERT_INTEGER: case SQL_CONVERT_LONGVARCHAR: case SQL_CONVERT_NUMERIC: case SQL_CONVERT_REAL: case SQL_CONVERT_SMALLINT: case SQL_CONVERT_TIME: case SQL_CONVERT_TIMESTAMP: case SQL_CONVERT_TINYINT: case SQL_CONVERT_VARCHAR: case SQL_CONVERT_WCHAR: case SQL_CONVERT_WVARCHAR: case SQL_CONVERT_WLONGVARCHAR: MYINFO_SET_ULONG(SQL_CVT_CHAR | SQL_CVT_NUMERIC | SQL_CVT_DECIMAL | SQL_CVT_INTEGER | SQL_CVT_SMALLINT | SQL_CVT_FLOAT | SQL_CVT_REAL | SQL_CVT_DOUBLE | SQL_CVT_VARCHAR | SQL_CVT_LONGVARCHAR | SQL_CVT_BIT | SQL_CVT_TINYINT | SQL_CVT_BIGINT | SQL_CVT_DATE | SQL_CVT_TIME | SQL_CVT_TIMESTAMP | SQL_CVT_WCHAR | SQL_CVT_WVARCHAR | SQL_CVT_WLONGVARCHAR); case SQL_CONVERT_BINARY: case SQL_CONVERT_VARBINARY: case SQL_CONVERT_LONGVARBINARY: case SQL_CONVERT_INTERVAL_DAY_TIME: case SQL_CONVERT_INTERVAL_YEAR_MONTH: MYINFO_SET_ULONG(0); case SQL_CONVERT_FUNCTIONS: /* MySQL's CONVERT() and CAST() functions aren't SQL compliant yet. */ MYINFO_SET_ULONG(0); case SQL_CORRELATION_NAME: MYINFO_SET_USHORT(SQL_CN_DIFFERENT); case SQL_CREATE_ASSERTION: case SQL_CREATE_CHARACTER_SET: case SQL_CREATE_COLLATION: case SQL_CREATE_DOMAIN: case SQL_CREATE_SCHEMA: MYINFO_SET_ULONG(0); case SQL_CREATE_TABLE: MYINFO_SET_ULONG(SQL_CT_CREATE_TABLE | SQL_CT_COMMIT_DELETE | SQL_CT_LOCAL_TEMPORARY | SQL_CT_COLUMN_DEFAULT | SQL_CT_COLUMN_COLLATION); case SQL_CREATE_TRANSLATION: MYINFO_SET_ULONG(0); case SQL_CREATE_VIEW: /** @todo SQL_CV_LOCAL ? */ if (is_minimum_version(dbc->mysql.server_version, "5.0", 3)) MYINFO_SET_ULONG(SQL_CV_CREATE_VIEW | SQL_CV_CHECK_OPTION | SQL_CV_CASCADED); else MYINFO_SET_ULONG(0); case SQL_CURSOR_COMMIT_BEHAVIOR: case SQL_CURSOR_ROLLBACK_BEHAVIOR: MYINFO_SET_USHORT(SQL_CB_PRESERVE); #ifdef SQL_CURSOR_SENSITIVITY case SQL_CURSOR_SENSITIVITY: MYINFO_SET_ULONG(SQL_UNSPECIFIED); #endif #ifdef SQL_CURSOR_ROLLBACK_SQL_CURSOR_SENSITIVITY case SQL_CURSOR_ROLLBACK_SQL_CURSOR_SENSITIVITY: MYINFO_SET_ULONG(SQL_UNSPECIFIED); #endif case SQL_DATA_SOURCE_NAME: MYINFO_SET_STR(dbc->ds ? dbc->ds->name8 : NULL); case SQL_DATA_SOURCE_READ_ONLY: MYINFO_SET_STR("N"); case SQL_DATABASE_NAME: if (is_connected(dbc) && reget_current_catalog(dbc)) return set_dbc_error(dbc, "HY000", "SQLGetInfo() failed to return current catalog.", 0); MYINFO_SET_STR(dbc->database ? dbc->database : "null"); case SQL_DATETIME_LITERALS: MYINFO_SET_ULONG(SQL_DL_SQL92_DATE | SQL_DL_SQL92_TIME | SQL_DL_SQL92_TIMESTAMP); case SQL_DBMS_NAME: MYINFO_SET_STR("MySQL"); case SQL_DBMS_VER: /** @todo technically this is not right: should be ##.##.#### */ MYINFO_SET_STR(dbc->mysql.server_version); case SQL_DDL_INDEX: MYINFO_SET_ULONG(SQL_DI_CREATE_INDEX | SQL_DI_DROP_INDEX); case SQL_DEFAULT_TXN_ISOLATION: MYINFO_SET_ULONG(DEFAULT_TXN_ISOLATION); case SQL_DESCRIBE_PARAMETER: MYINFO_SET_STR("N"); case SQL_DRIVER_NAME: #ifdef WIN32 MYINFO_SET_STR("myodbc5.dll"); #else MYINFO_SET_STR("libmyodbc5.so"); #endif case SQL_DRIVER_ODBC_VER: MYINFO_SET_STR("03.51"); /* What standard we implement */ case SQL_DRIVER_VER: MYINFO_SET_STR(DRIVER_VERSION); case SQL_DROP_ASSERTION: case SQL_DROP_CHARACTER_SET: case SQL_DROP_COLLATION: case SQL_DROP_DOMAIN: case SQL_DROP_SCHEMA: case SQL_DROP_TRANSLATION: MYINFO_SET_ULONG(0); case SQL_DROP_TABLE: MYINFO_SET_ULONG(SQL_DT_DROP_TABLE | SQL_DT_CASCADE | SQL_DT_RESTRICT); case SQL_DROP_VIEW: if (is_minimum_version(dbc->mysql.server_version, "5.0", 3)) MYINFO_SET_ULONG(SQL_DV_DROP_VIEW | SQL_DV_CASCADE | SQL_DV_RESTRICT); else MYINFO_SET_ULONG(0); case SQL_DYNAMIC_CURSOR_ATTRIBUTES1: if (dbc->ds && !dbc->ds->force_use_of_forward_only_cursors && dbc->ds->dynamic_cursor) MYINFO_SET_ULONG(SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | SQL_CA1_RELATIVE | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION | SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE | SQL_CA1_POS_REFRESH | SQL_CA1_POSITIONED_UPDATE | SQL_CA1_POSITIONED_DELETE | SQL_CA1_BULK_ADD); else MYINFO_SET_ULONG(0); case SQL_DYNAMIC_CURSOR_ATTRIBUTES2: if (dbc->ds && !dbc->ds->force_use_of_forward_only_cursors && dbc->ds->dynamic_cursor) MYINFO_SET_ULONG(SQL_CA2_SENSITIVITY_ADDITIONS | SQL_CA2_SENSITIVITY_DELETIONS | SQL_CA2_SENSITIVITY_UPDATES | SQL_CA2_MAX_ROWS_SELECT | SQL_CA2_MAX_ROWS_INSERT | SQL_CA2_MAX_ROWS_DELETE | SQL_CA2_MAX_ROWS_UPDATE | SQL_CA2_CRC_EXACT | SQL_CA2_SIMULATE_TRY_UNIQUE); else MYINFO_SET_ULONG(0); case SQL_EXPRESSIONS_IN_ORDERBY: MYINFO_SET_STR("Y"); case SQL_FILE_USAGE: MYINFO_SET_USHORT(SQL_FILE_NOT_SUPPORTED); case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1: MYINFO_SET_ULONG(dbc->ds && dbc->ds->force_use_of_forward_only_cursors ? SQL_CA1_NEXT : SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | SQL_CA1_RELATIVE | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION | SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE | SQL_CA1_POS_REFRESH | SQL_CA1_POSITIONED_UPDATE | SQL_CA1_POSITIONED_DELETE | SQL_CA1_BULK_ADD); case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2: MYINFO_SET_ULONG(SQL_CA2_MAX_ROWS_SELECT | SQL_CA2_MAX_ROWS_INSERT | SQL_CA2_MAX_ROWS_DELETE | SQL_CA2_MAX_ROWS_UPDATE | (dbc->ds && dbc->ds->force_use_of_forward_only_cursors ? 0 : SQL_CA2_CRC_EXACT)); case SQL_GETDATA_EXTENSIONS: MYINFO_SET_ULONG(SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BLOCK | SQL_GD_BOUND); case SQL_GROUP_BY: MYINFO_SET_USHORT(SQL_GB_NO_RELATION); case SQL_IDENTIFIER_CASE: MYINFO_SET_USHORT(SQL_IC_MIXED); case SQL_IDENTIFIER_QUOTE_CHAR: MYINFO_SET_STR("`"); case SQL_INDEX_KEYWORDS: MYINFO_SET_ULONG(SQL_IK_ALL); case SQL_INFO_SCHEMA_VIEWS: /* We have INFORMATION_SCHEMA.SCHEMATA, but we don't report it because the driver exposes databases (schema) as catalogs. */ if (is_minimum_version(dbc->mysql.server_version, "5.1", 3)) MYINFO_SET_ULONG(SQL_ISV_CHARACTER_SETS | SQL_ISV_COLLATIONS | SQL_ISV_COLUMN_PRIVILEGES | SQL_ISV_COLUMNS | SQL_ISV_KEY_COLUMN_USAGE | SQL_ISV_REFERENTIAL_CONSTRAINTS | /* SQL_ISV_SCHEMATA | */ SQL_ISV_TABLE_CONSTRAINTS | SQL_ISV_TABLE_PRIVILEGES | SQL_ISV_TABLES | SQL_ISV_VIEWS); else if (is_minimum_version(dbc->mysql.server_version, "5.0", 3)) MYINFO_SET_ULONG(SQL_ISV_CHARACTER_SETS | SQL_ISV_COLLATIONS | SQL_ISV_COLUMN_PRIVILEGES | SQL_ISV_COLUMNS | SQL_ISV_KEY_COLUMN_USAGE | /* SQL_ISV_SCHEMATA | */ SQL_ISV_TABLE_CONSTRAINTS | SQL_ISV_TABLE_PRIVILEGES | SQL_ISV_TABLES | SQL_ISV_VIEWS); else MYINFO_SET_ULONG(0); case SQL_INSERT_STATEMENT: MYINFO_SET_ULONG(SQL_IS_INSERT_LITERALS | SQL_IS_INSERT_SEARCHED | SQL_IS_SELECT_INTO); case SQL_INTEGRITY: MYINFO_SET_STR("N"); case SQL_KEYSET_CURSOR_ATTRIBUTES1: case SQL_KEYSET_CURSOR_ATTRIBUTES2: MYINFO_SET_ULONG(0); case SQL_KEYWORDS: /* These lists were generated by taking the list of reserved words from the MySQL Reference Manual (which is, in turn, generated from the source) with the pre-reserved ODBC keywords removed. */ if (is_minimum_version(dbc->mysql.server_version, "5.1", 3)) MYINFO_SET_STR("ACCESSIBLE,ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB," "CALL,CHANGE,CONDITION,DATABASE,DATABASES,DAY_HOUR," "DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED," "DETERMINISTIC,DISTINCTROW,DIV,DUAL,EACH,ELSEIF,ENCLOSED," "ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8,FORCE,FULLTEXT," "HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND," "IF,IGNORE,INFILE,INOUT,INT1,INT2,INT3,INT4,INT8,ITERATE," "KEYS,KILL,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCALTIME," "LOCALTIMESTAMP,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP," "LOW_PRIORITY,MASTER_SSL_VERIFY_SERVER_CERT,MEDIUMBLOB," "MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND," "MINUTE_SECOND,MOD,MODIFIES,NO_WRITE_TO_BINLOG,OPTIMIZE," "OPTIONALLY,OUT,OUTFILE,PURGE,RANGE,READ_ONLY,READS," "READ_WRITE,REGEXP,RELEASE,RENAME,REPEAT,REPLACE,REQUIRE," "RETURN,RLIKE,SCHEMAS,SECOND_MICROSECOND,SENSITIVE," "SEPARATOR,SHOW,SPATIAL,SPECIFIC,SQL_BIG_RESULT," "SQL_CALC_FOUND_ROWS,SQLEXCEPTION,SQL_SMALL_RESULT,SSL," "STARTING,STRAIGHT_JOIN,TERMINATED,TINYBLOB,TINYINT," "TINYTEXT,TRIGGER,UNDO,UNLOCK,UNSIGNED,USE,UTC_DATE," "UTC_TIME,UTC_TIMESTAMP,VARBINARY,VARCHARACTER,WHILE,X509," "XOR,YEAR_MONTH,ZEROFILL"); else if (is_minimum_version(dbc->mysql.server_version, "5.0", 3)) MYINFO_SET_STR("ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,CALL,CHANGE," "CONDITION,DATABASE,DATABASES,DAY_HOUR,DAY_MICROSECOND," "DAY_MINUTE,DAY_SECOND,DELAYED,DETERMINISTIC,DISTINCTROW," "DIV,DUAL,EACH,ELSEIF,ENCLOSED,ESCAPED,EXIT,EXPLAIN," "FLOAT4,FLOAT8,FORCE,FULLTEXT,HIGH_PRIORITY," "HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,IF,IGNORE," "INFILE,INOUT,INT1,INT2,INT3,INT4,INT8,ITERATE,KEYS,KILL," "LEAVE,LIMIT,LINES,LOAD,LOCALTIME,LOCALTIMESTAMP,LOCK," "LONG,LONGBLOB,LONGTEXT,LOOP,LOW_PRIORITY,MEDIUMBLOB," "MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND," "MINUTE_SECOND,MOD,MODIFIES,NO_WRITE_TO_BINLOG,OPTIMIZE," "OPTIONALLY,OUT,OUTFILE,PURGE,RAID0,READS,REGEXP,RELEASE," "RENAME,REPEAT,REPLACE,REQUIRE,RETURN,RLIKE,SCHEMAS," "SECOND_MICROSECOND,SENSITIVE,SEPARATOR,SHOW,SONAME," "SPATIAL,SPECIFIC,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS," "SQLEXCEPTION,SQL_SMALL_RESULT,SSL,STARTING,STRAIGHT_JOIN," "TERMINATED,TINYBLOB,TINYINT,TINYTEXT,TRIGGER,UNDO,UNLOCK," "UNSIGNED,USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY," "VARCHARACTER,WHILE,X509,XOR,YEAR_MONTH,ZEROFILL"); else MYINFO_SET_STR("ANALYZE,BEFORE,BIGINT,BINARY,BLOB,CHANGE,COLUMNS," "DATABASE,DATABASES,DAY_HOUR,DAY_MICROSECOND,DAY_MINUTE," "DAY_SECOND,DELAYED,DISTINCTROW,DIV,DUAL,ENCLOSED,ESCAPED," "EXPLAIN,FIELDS,FLOAT4,FLOAT8,FORCE,FULLTEXT," "HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND," "IF,IGNORE,INFILE,INT1,INT2,INT3,INT4,INT8,KEYS,KILL," "LIMIT,LINES,LOAD,LOCALTIME,LOCALTIMESTAMP,LOCK,LONG," "LONGBLOB,LONGTEXT,LOW_PRIORITY,MEDIUMBLOB,MEDIUMINT," "MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND," "MOD,NO_WRITE_TO_BINLOG,OPTIMIZE,OPTIONALLY,OUTFILE,PURGE," "RAID0,REGEXP,RENAME,REPLACE,REQUIRE,RLIKE," "SECOND_MICROSECOND,SEPARATOR,SHOW,SONAME,SPATIAL," "SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT,SSL," "STARTING,STRAIGHT_JOIN,TABLES,TERMINATED,TINYBLOB," "TINYINT,TINYTEXT,UNLOCK,UNSIGNED,USE,UTC_DATE,UTC_TIME," "UTC_TIMESTAMP,VARBINARY,VARCHARACTER,X509,XOR,YEAR_MONTH," "ZEROFILL"); case SQL_LIKE_ESCAPE_CLAUSE: MYINFO_SET_STR("Y"); case SQL_MAX_ASYNC_CONCURRENT_STATEMENTS: MYINFO_SET_ULONG(0); case SQL_MAX_BINARY_LITERAL_LEN: MYINFO_SET_ULONG(0); case SQL_MAX_CATALOG_NAME_LEN: MYINFO_SET_USHORT(NAME_LEN); case SQL_MAX_CHAR_LITERAL_LEN: MYINFO_SET_ULONG(0); case SQL_MAX_COLUMN_NAME_LEN: MYINFO_SET_USHORT(NAME_LEN); case SQL_MAX_COLUMNS_IN_GROUP_BY: MYINFO_SET_USHORT(0); /* No specific limit */ case SQL_MAX_COLUMNS_IN_INDEX: MYINFO_SET_USHORT(32); case SQL_MAX_COLUMNS_IN_ORDER_BY: MYINFO_SET_USHORT(0); /* No specific limit */ case SQL_MAX_COLUMNS_IN_SELECT: MYINFO_SET_USHORT(0); /* No specific limit */ case SQL_MAX_COLUMNS_IN_TABLE: MYINFO_SET_USHORT(0); /* No specific limit */ case SQL_MAX_CONCURRENT_ACTIVITIES: MYINFO_SET_USHORT(0); /* No specific limit */ case SQL_MAX_CURSOR_NAME_LEN: MYINFO_SET_USHORT(MYSQL_MAX_CURSOR_LEN); case SQL_MAX_DRIVER_CONNECTIONS: MYINFO_SET_USHORT(0); /* No specific limit */ case SQL_MAX_IDENTIFIER_LEN: MYINFO_SET_USHORT(NAME_LEN); case SQL_MAX_INDEX_SIZE: if (is_minimum_version(dbc->mysql.server_version, "5.0", 3)) MYINFO_SET_USHORT(3072); else MYINFO_SET_USHORT(1024); case SQL_MAX_PROCEDURE_NAME_LEN: MYINFO_SET_USHORT(NAME_LEN); case SQL_MAX_ROW_SIZE: MYINFO_SET_ULONG(0); /* No specific limit */ case SQL_MAX_ROW_SIZE_INCLUDES_LONG: MYINFO_SET_STR("Y"); case SQL_MAX_SCHEMA_NAME_LEN: MYINFO_SET_USHORT(0); case SQL_MAX_STATEMENT_LEN: MYINFO_SET_ULONG(net_buffer_length); case SQL_MAX_TABLE_NAME_LEN: MYINFO_SET_USHORT(NAME_LEN); case SQL_MAX_TABLES_IN_SELECT: if (is_minimum_version(dbc->mysql.server_version, "5.0", 3)) MYINFO_SET_USHORT(63); else MYINFO_SET_USHORT(31); case SQL_MAX_USER_NAME_LEN: MYINFO_SET_USHORT(USERNAME_LENGTH); case SQL_MULT_RESULT_SETS: MYINFO_SET_STR("Y"); case SQL_MULTIPLE_ACTIVE_TXN: MYINFO_SET_STR("Y"); case SQL_NEED_LONG_DATA_LEN: MYINFO_SET_STR("N"); case SQL_NON_NULLABLE_COLUMNS: MYINFO_SET_USHORT(SQL_NNC_NON_NULL); case SQL_NULL_COLLATION: MYINFO_SET_USHORT(SQL_NC_LOW); case SQL_NUMERIC_FUNCTIONS: MYINFO_SET_ULONG(SQL_FN_NUM_ABS | SQL_FN_NUM_ACOS | SQL_FN_NUM_ASIN | SQL_FN_NUM_ATAN | SQL_FN_NUM_ATAN2 | SQL_FN_NUM_CEILING | SQL_FN_NUM_COS | SQL_FN_NUM_COT | SQL_FN_NUM_EXP | SQL_FN_NUM_FLOOR | SQL_FN_NUM_LOG | SQL_FN_NUM_MOD | SQL_FN_NUM_SIGN | SQL_FN_NUM_SIN | SQL_FN_NUM_SQRT | SQL_FN_NUM_TAN | SQL_FN_NUM_PI | SQL_FN_NUM_RAND | SQL_FN_NUM_DEGREES | SQL_FN_NUM_LOG10 | SQL_FN_NUM_POWER | SQL_FN_NUM_RADIANS | SQL_FN_NUM_ROUND | SQL_FN_NUM_TRUNCATE); case SQL_ODBC_API_CONFORMANCE: MYINFO_SET_USHORT(SQL_OAC_LEVEL1); case SQL_ODBC_INTERFACE_CONFORMANCE: MYINFO_SET_ULONG(SQL_OIC_LEVEL1); case SQL_ODBC_SQL_CONFORMANCE: MYINFO_SET_USHORT(SQL_OSC_CORE); case SQL_OJ_CAPABILITIES: MYINFO_SET_ULONG(SQL_OJ_LEFT | SQL_OJ_RIGHT | SQL_OJ_NESTED | SQL_OJ_NOT_ORDERED | SQL_OJ_INNER | SQL_OJ_ALL_COMPARISON_OPS); case SQL_ORDER_BY_COLUMNS_IN_SELECT: MYINFO_SET_STR("N"); case SQL_PARAM_ARRAY_ROW_COUNTS: MYINFO_SET_ULONG(SQL_PARC_NO_BATCH); case SQL_PARAM_ARRAY_SELECTS: MYINFO_SET_ULONG(SQL_PAS_NO_BATCH); case SQL_PROCEDURE_TERM: if (is_minimum_version(dbc->mysql.server_version, "5.0", 3)) MYINFO_SET_STR("stored procedure"); else MYINFO_SET_STR(""); case SQL_PROCEDURES: if (is_minimum_version(dbc->mysql.server_version, "5.0", 3)) MYINFO_SET_STR("Y"); else MYINFO_SET_STR("N"); case SQL_POS_OPERATIONS: if (dbc->ds && dbc->ds->force_use_of_forward_only_cursors) MYINFO_SET_ULONG(0); else MYINFO_SET_ULONG(SQL_POS_POSITION | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD | SQL_POS_REFRESH); case SQL_QUOTED_IDENTIFIER_CASE: MYINFO_SET_USHORT(SQL_IC_SENSITIVE); case SQL_ROW_UPDATES: MYINFO_SET_STR("N"); case SQL_SCHEMA_TERM: /* This is because we map MySQL database (schema) to catalog. */ MYINFO_SET_STR(""); case SQL_SCHEMA_USAGE: MYINFO_SET_ULONG(0); case SQL_SCROLL_OPTIONS: MYINFO_SET_ULONG(SQL_SO_FORWARD_ONLY | (dbc->ds && dbc->ds->force_use_of_forward_only_cursors ? 0 : SQL_SO_STATIC | (dbc->ds && dbc->ds->dynamic_cursor ? SQL_SO_DYNAMIC : 0))); case SQL_SEARCH_PATTERN_ESCAPE: MYINFO_SET_STR("\\"); case SQL_SERVER_NAME: MYINFO_SET_STR(dbc->mysql.host_info); case SQL_SPECIAL_CHARACTERS: /* We can handle anything but / and \xff. */ MYINFO_SET_STR(" !\"#%&'()*+,-.:;<=>?@[\\]^`{|}~"); case SQL_SQL_CONFORMANCE: MYINFO_SET_ULONG(SQL_SC_SQL92_INTERMEDIATE); case SQL_SQL92_DATETIME_FUNCTIONS: MYINFO_SET_ULONG(SQL_SDF_CURRENT_DATE | SQL_SDF_CURRENT_TIME | SQL_SDF_CURRENT_TIMESTAMP); case SQL_SQL92_FOREIGN_KEY_DELETE_RULE: case SQL_SQL92_FOREIGN_KEY_UPDATE_RULE: MYINFO_SET_ULONG(0); case SQL_SQL92_GRANT: MYINFO_SET_ULONG(SQL_SG_DELETE_TABLE | SQL_SG_INSERT_COLUMN | SQL_SG_INSERT_TABLE | SQL_SG_REFERENCES_TABLE | SQL_SG_REFERENCES_COLUMN | SQL_SG_SELECT_TABLE | SQL_SG_UPDATE_COLUMN | SQL_SG_UPDATE_TABLE | SQL_SG_WITH_GRANT_OPTION); case SQL_SQL92_NUMERIC_VALUE_FUNCTIONS: MYINFO_SET_ULONG(SQL_SNVF_BIT_LENGTH | SQL_SNVF_CHAR_LENGTH | SQL_SNVF_CHARACTER_LENGTH | SQL_SNVF_EXTRACT | SQL_SNVF_OCTET_LENGTH | SQL_SNVF_POSITION); case SQL_SQL92_PREDICATES: MYINFO_SET_ULONG(SQL_SP_BETWEEN | SQL_SP_COMPARISON | SQL_SP_EXISTS | SQL_SP_IN | SQL_SP_ISNOTNULL | SQL_SP_ISNULL | SQL_SP_LIKE /*| SQL_SP_MATCH_FULL |SQL_SP_MATCH_PARTIAL | SQL_SP_MATCH_UNIQUE_FULL | SQL_SP_MATCH_UNIQUE_PARTIAL | SQL_SP_OVERLAPS */ | SQL_SP_QUANTIFIED_COMPARISON /*| SQL_SP_UNIQUE */); case SQL_SQL92_RELATIONAL_JOIN_OPERATORS: MYINFO_SET_ULONG(SQL_SRJO_CROSS_JOIN | SQL_SRJO_INNER_JOIN | SQL_SRJO_LEFT_OUTER_JOIN | SQL_SRJO_NATURAL_JOIN | SQL_SRJO_RIGHT_OUTER_JOIN); case SQL_SQL92_REVOKE: MYINFO_SET_ULONG(SQL_SR_DELETE_TABLE | SQL_SR_INSERT_COLUMN | SQL_SR_INSERT_TABLE | SQL_SR_REFERENCES_TABLE | SQL_SR_REFERENCES_COLUMN | SQL_SR_SELECT_TABLE | SQL_SR_UPDATE_COLUMN | SQL_SR_UPDATE_TABLE); case SQL_SQL92_ROW_VALUE_CONSTRUCTOR: MYINFO_SET_ULONG(SQL_SRVC_VALUE_EXPRESSION | SQL_SRVC_NULL | SQL_SRVC_DEFAULT | SQL_SRVC_ROW_SUBQUERY); case SQL_SQL92_STRING_FUNCTIONS: MYINFO_SET_ULONG(SQL_SSF_CONVERT | SQL_SSF_LOWER | SQL_SSF_UPPER | SQL_SSF_SUBSTRING | SQL_SSF_TRANSLATE | SQL_SSF_TRIM_BOTH | SQL_SSF_TRIM_LEADING | SQL_SSF_TRIM_TRAILING); case SQL_SQL92_VALUE_EXPRESSIONS: MYINFO_SET_ULONG(SQL_SVE_CASE | SQL_SVE_CAST | SQL_SVE_COALESCE | SQL_SVE_NULLIF); case SQL_STANDARD_CLI_CONFORMANCE: MYINFO_SET_ULONG(SQL_SCC_ISO92_CLI); case SQL_STATIC_CURSOR_ATTRIBUTES1: MYINFO_SET_ULONG(SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | SQL_CA1_RELATIVE | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION | SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE | SQL_CA1_POS_REFRESH | SQL_CA1_POSITIONED_UPDATE | SQL_CA1_POSITIONED_DELETE | SQL_CA1_BULK_ADD); case SQL_STATIC_CURSOR_ATTRIBUTES2: MYINFO_SET_ULONG(SQL_CA2_MAX_ROWS_SELECT | SQL_CA2_MAX_ROWS_INSERT | SQL_CA2_MAX_ROWS_DELETE | SQL_CA2_MAX_ROWS_UPDATE | SQL_CA2_CRC_EXACT); case SQL_STRING_FUNCTIONS: MYINFO_SET_ULONG(SQL_FN_STR_ASCII | SQL_FN_STR_BIT_LENGTH | SQL_FN_STR_CHAR | SQL_FN_STR_CHAR_LENGTH | SQL_FN_STR_CONCAT | SQL_FN_STR_INSERT | SQL_FN_STR_LCASE | SQL_FN_STR_LEFT | SQL_FN_STR_LENGTH | SQL_FN_STR_LOCATE | SQL_FN_STR_LOCATE_2 | SQL_FN_STR_LTRIM | SQL_FN_STR_OCTET_LENGTH | SQL_FN_STR_POSITION | SQL_FN_STR_REPEAT | SQL_FN_STR_REPLACE | SQL_FN_STR_RIGHT | SQL_FN_STR_RTRIM | SQL_FN_STR_SOUNDEX | SQL_FN_STR_SPACE | SQL_FN_STR_SUBSTRING | SQL_FN_STR_UCASE); case SQL_SUBQUERIES: MYINFO_SET_ULONG(SQL_SQ_CORRELATED_SUBQUERIES | SQL_SQ_COMPARISON | SQL_SQ_EXISTS | SQL_SQ_IN | SQL_SQ_QUANTIFIED); case SQL_SYSTEM_FUNCTIONS: MYINFO_SET_ULONG(SQL_FN_SYS_DBNAME | SQL_FN_SYS_IFNULL | SQL_FN_SYS_USERNAME); case SQL_TABLE_TERM: MYINFO_SET_STR("table"); case SQL_TIMEDATE_ADD_INTERVALS: case SQL_TIMEDATE_DIFF_INTERVALS: MYINFO_SET_ULONG(0); case SQL_TIMEDATE_FUNCTIONS: MYINFO_SET_ULONG(SQL_FN_TD_CURRENT_DATE | SQL_FN_TD_CURRENT_TIME | SQL_FN_TD_CURRENT_TIMESTAMP | SQL_FN_TD_CURDATE | SQL_FN_TD_CURTIME | SQL_FN_TD_DAYNAME | SQL_FN_TD_DAYOFMONTH | SQL_FN_TD_DAYOFWEEK | SQL_FN_TD_DAYOFYEAR | SQL_FN_TD_EXTRACT | SQL_FN_TD_HOUR | /* SQL_FN_TD_JULIAN_DAY | */ SQL_FN_TD_MINUTE | SQL_FN_TD_MONTH | SQL_FN_TD_MONTHNAME | SQL_FN_TD_NOW | SQL_FN_TD_QUARTER | SQL_FN_TD_SECOND | /*SQL_FN_TD_SECONDS_SINCE_MIDNIGHT | */ SQL_FN_TD_TIMESTAMPADD | SQL_FN_TD_TIMESTAMPDIFF | SQL_FN_TD_WEEK | SQL_FN_TD_YEAR); case SQL_TXN_CAPABLE: if (trans_supported(dbc) && (!dbc->ds || !dbc->ds->disable_transactions)) MYINFO_SET_USHORT(SQL_TC_DDL_COMMIT); else MYINFO_SET_USHORT(SQL_TC_NONE); case SQL_TXN_ISOLATION_OPTION: if (!trans_supported(dbc) || (dbc->ds && dbc->ds->disable_transactions)) MYINFO_SET_ULONG(SQL_TXN_READ_COMMITTED); else MYINFO_SET_ULONG(SQL_TXN_READ_COMMITTED | SQL_TXN_READ_UNCOMMITTED | SQL_TXN_REPEATABLE_READ | SQL_TXN_SERIALIZABLE); case SQL_UNION: MYINFO_SET_ULONG(SQL_U_UNION | SQL_U_UNION_ALL); case SQL_USER_NAME: MYINFO_SET_STR(dbc->ds ? dbc->ds->uid8 : NULL); case SQL_XOPEN_CLI_YEAR: MYINFO_SET_STR("1992"); /* The following aren't listed in the MSDN documentation. */ case SQL_ACCESSIBLE_PROCEDURES: case SQL_ACCESSIBLE_TABLES: MYINFO_SET_STR("N"); case SQL_LOCK_TYPES: MYINFO_SET_ULONG(0); case SQL_OUTER_JOINS: MYINFO_SET_STR("Y"); case SQL_POSITIONED_STATEMENTS: if (dbc->ds && dbc->ds->force_use_of_forward_only_cursors) MYINFO_SET_ULONG(0); else MYINFO_SET_ULONG(SQL_PS_POSITIONED_DELETE | SQL_PS_POSITIONED_UPDATE); case SQL_SCROLL_CONCURRENCY: /** @todo this is wrong. */ MYINFO_SET_ULONG(SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES); case SQL_STATIC_SENSITIVITY: MYINFO_SET_ULONG(SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES); case SQL_FETCH_DIRECTION: if (dbc->ds && dbc->ds->force_use_of_forward_only_cursors) MYINFO_SET_ULONG(SQL_FD_FETCH_NEXT); else MYINFO_SET_ULONG(SQL_FD_FETCH_NEXT | SQL_FD_FETCH_FIRST | SQL_FD_FETCH_LAST | (dbc->ds->user_manager_cursor ? 0 : SQL_FD_FETCH_PRIOR) | SQL_FD_FETCH_ABSOLUTE | SQL_FD_FETCH_RELATIVE); case SQL_ODBC_SAG_CLI_CONFORMANCE: MYINFO_SET_USHORT(SQL_OSCC_COMPLIANT); default: { char buff[80]; sprintf(buff, "Unsupported option: %d to SQLGetInfo", fInfoType); return set_conn_error(hdbc, MYERR_S1C00, buff, 4000); } } return SQL_SUCCESS; } /* Function sets up a result set containing details of the types supported by mysql. */ MYSQL_FIELD SQL_GET_TYPE_INFO_fields[]= { MYODBC_FIELD_STRING("TYPE_NAME", 32, NOT_NULL_FLAG), MYODBC_FIELD_SHORT("DATA_TYPE", NOT_NULL_FLAG), MYODBC_FIELD_LONG("COLUMN_SIZE", 0), MYODBC_FIELD_STRING("LITERAL_PREFIX", 2, 0), MYODBC_FIELD_STRING("LITERAL_SUFFIX", 1, 0), MYODBC_FIELD_STRING("CREATE_PARAMS", 15, 0), MYODBC_FIELD_SHORT("NULLABLE", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("CASE_SENSITIVE", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("SEARCHABLE", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("UNSIGNED_ATTRIBUTE", 0), MYODBC_FIELD_SHORT("FIXED_PREC_SCALE", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("AUTO_UNIQUE_VALUE", 0), MYODBC_FIELD_STRING("LOCAL_TYPE_NAME", 60, 0), MYODBC_FIELD_SHORT("MINIMUM_SCALE", 0), MYODBC_FIELD_SHORT("MAXIMUM_SCALE", 0), MYODBC_FIELD_SHORT("SQL_DATATYPE", NOT_NULL_FLAG), MYODBC_FIELD_SHORT("SQL_DATETIME_SUB", 0), MYODBC_FIELD_LONG("NUM_PREC_RADIX", 0), MYODBC_FIELD_SHORT("INTERVAL_PRECISION", 0), }; const uint SQL_GET_TYPE_INFO_FIELDS= array_elements(SQL_GET_TYPE_INFO_fields); #define MYSQL_DATA_TYPES 52 char sql_searchable[6], sql_unsearchable[6], sql_nullable[6], sql_no_nulls[6], sql_bit[6], sql_tinyint[6], sql_smallint[6], sql_integer[6], sql_bigint[6], sql_float[6], sql_real[6], sql_double[6], sql_char[6], sql_varchar[6], sql_longvarchar[6], sql_timestamp[6], sql_decimal[6], sql_numeric[6], sql_varbinary[6], sql_time[6], sql_date[6], sql_binary[6], sql_longvarbinary[6], sql_datetime[6]; char *SQL_GET_TYPE_INFO_values[MYSQL_DATA_TYPES][19]= { /* SQL_BIT= -7 */ {"bit",sql_bit,"1",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"bit(1)",NULL,NULL,sql_bit,NULL,NULL,NULL}, /* SQL_TINY= -6 */ {"tinyint",sql_tinyint,"3",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"0","0","0","tinyint",NULL,NULL,sql_tinyint,NULL,"10",NULL}, {"tinyint unsigned",sql_tinyint,"3",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"1","0","0","tinyint unsigned",NULL,NULL,sql_tinyint,NULL,"10",NULL}, {"tinyint auto_increment",sql_tinyint,"3",NULL,NULL,NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","tinyint auto_increment",NULL,NULL,sql_tinyint, NULL,"10",NULL}, {"tinyint unsigned auto_increment",sql_tinyint,"3",NULL,NULL,NULL,sql_no_nulls, "0",sql_searchable,"1","0","1","tinyint unsigned auto_increment",NULL,NULL, sql_tinyint,NULL,"10",NULL}, /* SQL_BIGINT= -5 */ {"bigint",sql_bigint,"19",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"0","0","0","bigint",NULL,NULL,sql_bigint,NULL,"10",NULL}, {"bigint unsigned",sql_bigint,"20",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"1","0","0","bigint unsigned",NULL,NULL,sql_bigint,NULL,"10",NULL}, {"bigint auto_increment",sql_bigint,"19",NULL,NULL,NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","bigint auto_increment",NULL,NULL,sql_bigint,NULL,"10",NULL}, {"bigint unsigned auto_increment",sql_bigint,"20",NULL,NULL,NULL,sql_no_nulls, "0",sql_searchable,"1","0","1","bigint unsigned auto_increment",NULL,NULL,sql_bigint, NULL,"10",NULL}, /* SQL_LONGVARBINARY= -4 */ {"long varbinary",sql_longvarbinary,"16777215","0x",NULL,NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"mediumblob",NULL,NULL,sql_longvarbinary,NULL,NULL,NULL}, {"blob",sql_longvarbinary,"65535","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"binary large object (0-65535)",NULL,NULL,sql_longvarbinary,NULL,NULL,NULL}, {"longblob",sql_longvarbinary,"2147483647","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"binary large object, use mediumblob instead",NULL,NULL,sql_longvarbinary,NULL,NULL,NULL}, {"tinyblob",sql_longvarbinary,"255","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"binary large object (0-255)",NULL,NULL,sql_longvarbinary,NULL,NULL,NULL}, {"mediumblob",sql_longvarbinary,"16777215","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"binary large object",NULL,NULL,sql_longvarbinary,NULL,NULL,NULL}, /* SQL_VARBINARY= -3 */ {"varbinary",sql_varbinary,"255","'","'","length",sql_nullable,"0",sql_searchable,NULL,"0",NULL,"varbinary",NULL,NULL,sql_varbinary,NULL,NULL,NULL}, /* SQL_BINARY= -2 */ {"binary",sql_binary,"255","'","'","length",sql_nullable,"0",sql_searchable,NULL,"0",NULL,"binary",NULL,NULL,sql_binary,NULL,NULL,NULL}, /* SQL_LONGVARCHAR= -1 */ {"long varchar",sql_longvarchar,"16777215","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"mediumtext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL}, {"text",sql_longvarchar,"65535","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"text(0-65535)",NULL,NULL,sql_longvarchar,NULL,NULL,NULL}, {"mediumtext",sql_longvarchar,"16777215","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"mediumtext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL}, {"longtext",sql_longvarchar,"2147483647","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"longtext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL}, {"tinytext",sql_longvarchar,"255","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"tinytext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL}, /* SQL_CHAR= 1 */ {"char",sql_char,"255","'","'","length",sql_nullable,"0",sql_searchable,NULL,"0",NULL,"char",NULL,NULL,sql_char,NULL,NULL,NULL}, /* SQL_NUMERIC= 2 */ {"numeric",sql_numeric,"19",NULL,NULL,"precision,scale",sql_nullable,"0",sql_searchable,"0","0","0","numeric","0","19",sql_numeric,NULL,"10",NULL}, /* SQL_DECIMAL= 3 */ {"decimal",sql_decimal,"19",NULL,NULL,"precision,scale",sql_nullable,"0",sql_searchable,"0","0","0","decimal","0","19",sql_decimal,NULL,"10",NULL}, /* SQL_INTEGER= 4 */ {"integer",sql_integer,"10",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"0","0","0","integer",NULL,NULL,sql_integer,NULL,"10",NULL}, {"integer unsigned",sql_integer,"10",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"1","0","0","integer unsigned",NULL,NULL,sql_integer,NULL,"10",NULL}, {"int",sql_integer,"10",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"0","0","0","integer",NULL,NULL,sql_integer,NULL,"10",NULL}, {"int unsigned",sql_integer,"10",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"1","0","0","integer unsigned",NULL,NULL,sql_integer,NULL,"10",NULL}, {"mediumint",sql_integer,"7",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"0","0","0","Medium integer",NULL,NULL,sql_integer,NULL,"10",NULL}, {"mediumint unsigned",sql_integer,"8",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"1","0","0","Medium integer unsigned",NULL,NULL,sql_integer,NULL,"10",NULL}, {"integer auto_increment",sql_integer,"10",NULL,NULL,NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","integer auto_increment",NULL,NULL,sql_integer, NULL,"10",NULL}, {"integer unsigned auto_increment",sql_integer,"10",NULL,NULL,NULL,sql_no_nulls, "0",sql_searchable,"1","0","1","integer unsigned auto_increment",NULL,NULL, sql_integer,NULL,"10",NULL}, {"int auto_increment",sql_integer,"10",NULL,NULL,NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","integer auto_increment",NULL,NULL,sql_integer, NULL,"10",NULL}, {"int unsigned auto_increment",sql_integer,"10",NULL,NULL,NULL,sql_no_nulls,"0",sql_searchable,"1","0","1","integer unsigned auto_increment",NULL,NULL,sql_integer,NULL,"10",NULL}, {"mediumint auto_increment",sql_integer,"7",NULL,NULL,NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","Medium integer auto_increment",NULL,NULL,sql_integer,NULL,"10",NULL}, {"mediumint unsigned auto_increment",sql_integer,"8",NULL,NULL,NULL,sql_no_nulls, "0",sql_searchable,"1","0","1","Medium integer unsigned auto_increment",NULL,NULL, sql_integer,NULL,"10",NULL}, /* SQL_SMALLINT= 5 */ {"smallint",sql_smallint,"5",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"0","0","0","smallint",NULL,NULL,sql_smallint,NULL,"10",NULL}, {"smallint unsigned",sql_smallint,"5",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"1","0","0","smallint unsigned",NULL,NULL,sql_smallint,NULL,"10",NULL}, {"smallint auto_increment",sql_smallint,"5",NULL,NULL,NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","smallint auto_increment",NULL,NULL,sql_smallint,NULL,"10",NULL}, {"smallint unsigned auto_increment",sql_smallint,"5",NULL,NULL,NULL,sql_no_nulls, "0",sql_searchable,"1","0","1","smallint unsigned auto_increment",NULL,NULL, sql_smallint,NULL,"10",NULL}, /* SQL_FLOAT= 6 */ {"double",sql_float,"15",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"0","0","0","double","0","4",sql_float, NULL,"10",NULL}, {"double auto_increment",sql_float,"15",NULL,NULL,NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","double auto_increment","0","4",sql_float,NULL,"10",NULL}, /* SQL_REAL= 7 */ {"float",sql_real,"7",NULL,NULL,NULL,sql_nullable, "0",sql_unsearchable,"0","0","0","float","0","2",sql_real, NULL,"10",NULL}, {"float auto_increment",sql_real,"7",NULL,NULL,NULL,sql_no_nulls,"0",sql_unsearchable,"0","0","1","float auto_increment","0","2",sql_real,NULL,"10",NULL}, /* SQL_DOUBLE= 8 */ {"double",sql_double,"15",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"0","0","0","double","0","4",sql_double,NULL,"10",NULL}, {"double auto_increment",sql_double,"15",NULL,NULL,NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","double auto_increment","0","4",sql_double,NULL,"10",NULL}, /* SQL_TYPE_DATE= 91 */ {"date",sql_date,"10","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"date",NULL,NULL,sql_datetime,sql_date,NULL,NULL}, /* SQL_TYPE_TIME= 92 */ {"time",sql_time,"8","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"time",NULL,NULL,sql_datetime,sql_time,NULL,NULL}, /* YEAR - SQL_SMALLINT */ {"year",sql_smallint,"4",NULL,NULL,NULL,sql_nullable,"0",sql_searchable,"0","0","0","year",NULL,NULL,sql_smallint,NULL,"10",NULL}, /* SQL_TYPE_TIMESTAMP= 93 */ {"datetime",sql_timestamp,"21","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"datetime","0","0",sql_datetime,sql_timestamp,NULL,NULL}, {"timestamp",sql_timestamp,"14","'","'",NULL,sql_no_nulls,"0",sql_searchable,NULL,"0",NULL,"timestamp","0","0",sql_datetime,sql_timestamp,NULL,NULL}, /* SQL_VARCHAR= 12 */ {"varchar",sql_varchar,"255","'","'","length",sql_nullable,"0",sql_searchable,NULL,"0",NULL,"varchar",NULL,NULL,sql_varchar,NULL,NULL,NULL}, /* ENUM and SET are not included -- it confuses some applications. */ }; /** Return information about data types supported by the server. @param[in] hstmt Handle of statement @param[in] fSqlType SQL data type or @c SQL_ALL_TYPES @since ODBC 1.0 @since ISO SQL 92 */ SQLRETURN SQL_API MySQLGetTypeInfo(SQLHSTMT hstmt, SQLSMALLINT fSqlType) { STMT *stmt= (STMT *)hstmt; uint i; my_SQLFreeStmt(hstmt, MYSQL_RESET); /* use ODBC2 types if called with ODBC3 types on an ODBC2 handle */ if (stmt->dbc->env->odbc_ver == SQL_OV_ODBC2) { switch (fSqlType) { case SQL_TYPE_DATE: fSqlType= SQL_DATE; break; case SQL_TYPE_TIME: fSqlType= SQL_TIME; break; case SQL_TYPE_TIMESTAMP: fSqlType= SQL_TIMESTAMP; break; } } /* Set up result Data dictionary. */ stmt->result= (MYSQL_RES *)my_malloc(sizeof(MYSQL_RES), MYF(MY_ZEROFILL)); stmt->fake_result= 1; stmt->result_array= (char **)my_malloc(sizeof(SQL_GET_TYPE_INFO_values), MYF(MY_FAE | MY_ZEROFILL)); if (fSqlType == SQL_ALL_TYPES) { memcpy(stmt->result_array, SQL_GET_TYPE_INFO_values, sizeof(SQL_GET_TYPE_INFO_values)); stmt->result->row_count= MYSQL_DATA_TYPES; } else { for (i= 0 ; i < MYSQL_DATA_TYPES ; ++i) { if (atoi(SQL_GET_TYPE_INFO_values[i][1]) == fSqlType || atoi(SQL_GET_TYPE_INFO_values[i][15]) == fSqlType) { memcpy(&stmt->result_array[stmt->result->row_count++ * SQL_GET_TYPE_INFO_FIELDS], &SQL_GET_TYPE_INFO_values[i][0], sizeof(char *) * SQL_GET_TYPE_INFO_FIELDS); } } } mysql_link_fields(stmt, SQL_GET_TYPE_INFO_fields, SQL_GET_TYPE_INFO_FIELDS); return SQL_SUCCESS; } /** Create strings from some integers for easy initialization of string arrays. @todo get rid of this. it is evil. */ void init_getfunctions(void) { my_int2str(SQL_SEARCHABLE,sql_searchable,-10,0); my_int2str(SQL_UNSEARCHABLE,sql_unsearchable,-10,0); my_int2str(SQL_NULLABLE,sql_nullable,-10,0); my_int2str(SQL_NO_NULLS,sql_no_nulls,-10,0); my_int2str(SQL_BIT,sql_bit,-10,0); my_int2str(SQL_TINYINT,sql_tinyint,-10,0); my_int2str(SQL_SMALLINT,sql_smallint,-10,0); my_int2str(SQL_INTEGER,sql_integer,-10,0); my_int2str(SQL_BIGINT,sql_bigint,-10,0); my_int2str(SQL_DECIMAL,sql_decimal,-10,0); my_int2str(SQL_NUMERIC,sql_numeric,-10,0); my_int2str(SQL_REAL,sql_real,-10,0); my_int2str(SQL_FLOAT,sql_float,-10,0); my_int2str(SQL_DOUBLE,sql_double,-10,0); my_int2str(SQL_CHAR,sql_char,-10,0); my_int2str(SQL_VARCHAR,sql_varchar,-10,0); my_int2str(SQL_LONGVARCHAR,sql_longvarchar,-10,0); my_int2str(SQL_LONGVARBINARY,sql_longvarbinary,-10,0); my_int2str(SQL_VARBINARY,sql_varbinary,-10,0); my_int2str(SQL_BINARY,sql_binary,-10,0); my_int2str(SQL_DATETIME,sql_datetime,-10,0); my_int2str(SQL_TYPE_TIMESTAMP,sql_timestamp,-10,0); my_int2str(SQL_TYPE_DATE,sql_date,-10,0); my_int2str(SQL_TYPE_TIME,sql_time,-10,0); # if (ODBCVER < 0x0300) myodbc_sqlstate2_init(); myodbc_ov2_inited= 1; # endif } /** Fix some initializations based on the ODBC version. */ void myodbc_ov_init(SQLINTEGER odbc_version) { if (odbc_version == SQL_OV_ODBC2) { my_int2str(SQL_TIMESTAMP,sql_timestamp,-10,0); my_int2str(SQL_DATE,sql_date,-10,0); my_int2str(SQL_TIME,sql_time,-10,0); myodbc_sqlstate2_init(); myodbc_ov2_inited= 1; } else { if (!myodbc_ov2_inited) return; myodbc_ov2_inited= 0; my_int2str(SQL_TYPE_TIMESTAMP,sql_timestamp,-10,0); my_int2str(SQL_TYPE_DATE,sql_date,-10,0); my_int2str(SQL_TYPE_TIME,sql_time,-10,0); myodbc_sqlstate3_init(); } } /** List of functions supported in the driver. */ SQLUSMALLINT myodbc3_functions[]= { SQL_API_SQLALLOCCONNECT, SQL_API_SQLALLOCENV, SQL_API_SQLALLOCHANDLE, SQL_API_SQLALLOCSTMT, SQL_API_SQLBINDCOL, /* SQL_API_SQLBINDPARAM */ SQL_API_SQLCANCEL, SQL_API_SQLCLOSECURSOR, SQL_API_SQLCOLATTRIBUTE, SQL_API_SQLCOLUMNS, SQL_API_SQLCONNECT, SQL_API_SQLCOPYDESC, SQL_API_SQLDATASOURCES, SQL_API_SQLDESCRIBECOL, SQL_API_SQLDISCONNECT, SQL_API_SQLENDTRAN, SQL_API_SQLERROR, SQL_API_SQLEXECDIRECT, SQL_API_SQLEXECUTE, SQL_API_SQLFETCH, SQL_API_SQLFETCHSCROLL, SQL_API_SQLFREECONNECT, SQL_API_SQLFREEENV, SQL_API_SQLFREEHANDLE, SQL_API_SQLFREESTMT, SQL_API_SQLGETCONNECTATTR, SQL_API_SQLGETCONNECTOPTION, SQL_API_SQLGETCURSORNAME, SQL_API_SQLGETDATA, SQL_API_SQLGETDESCFIELD, SQL_API_SQLGETDESCREC, SQL_API_SQLGETDIAGFIELD, SQL_API_SQLGETDIAGREC, SQL_API_SQLGETENVATTR, SQL_API_SQLGETFUNCTIONS, SQL_API_SQLGETINFO, SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETSTMTOPTION, SQL_API_SQLGETTYPEINFO, SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLPARAMDATA, SQL_API_SQLPREPARE, SQL_API_SQLPUTDATA, SQL_API_SQLROWCOUNT, SQL_API_SQLSETCONNECTATTR, SQL_API_SQLSETCONNECTOPTION, SQL_API_SQLSETCURSORNAME, SQL_API_SQLSETDESCFIELD, SQL_API_SQLSETDESCREC, SQL_API_SQLSETENVATTR, SQL_API_SQLSETPARAM, SQL_API_SQLSETSTMTATTR, SQL_API_SQLSETSTMTOPTION, SQL_API_SQLSPECIALCOLUMNS, SQL_API_SQLSTATISTICS, SQL_API_SQLTABLES, SQL_API_SQLTRANSACT, /* SQL_API_SQLALLOCHANDLESTD */ SQL_API_SQLBULKOPERATIONS, SQL_API_SQLBINDPARAMETER, SQL_API_SQLBROWSECONNECT, SQL_API_SQLCOLATTRIBUTES, SQL_API_SQLCOLUMNPRIVILEGES , SQL_API_SQLDESCRIBEPARAM, SQL_API_SQLDRIVERCONNECT, SQL_API_SQLDRIVERS, SQL_API_SQLEXTENDEDFETCH, SQL_API_SQLFOREIGNKEYS, SQL_API_SQLMORERESULTS, SQL_API_SQLNATIVESQL, SQL_API_SQLNUMPARAMS, SQL_API_SQLPARAMOPTIONS, SQL_API_SQLPRIMARYKEYS, SQL_API_SQLPROCEDURECOLUMNS, SQL_API_SQLPROCEDURES, SQL_API_SQLSETPOS, SQL_API_SQLSETSCROLLOPTIONS, SQL_API_SQLTABLEPRIVILEGES }; /** Get information on which functions are supported by the driver. @param[in] hdbc Handle of database connection @param[in] fFunction Function to check, @c SQL_API_ODBC3_ALL_FUNCTIONS, or @c SQL_API_ALL_FUNCTIONS @param[out] pfExists Pointer to either one @c SQLUSMALLINT or an array of SQLUSMALLINT for returning results @since ODBC 1.0 @since ISO SQL 92 */ SQLRETURN SQL_API SQLGetFunctions(SQLHDBC hdbc __attribute__((unused)), SQLUSMALLINT fFunction, SQLUSMALLINT *pfExists) { SQLUSMALLINT index, myodbc_func_size; myodbc_func_size= sizeof(myodbc3_functions) / sizeof(myodbc3_functions[0]); if (fFunction == SQL_API_ODBC3_ALL_FUNCTIONS) { /* Clear and set bits in the 4000 bit vector */ memset(pfExists, 0, sizeof(SQLUSMALLINT) * SQL_API_ODBC3_ALL_FUNCTIONS_SIZE); for (index= 0; index < myodbc_func_size; ++index) { SQLUSMALLINT id= myodbc3_functions[index]; pfExists[id >> 4]|= (1 << (id & 0x000F)); } return SQL_SUCCESS; } if (fFunction == SQL_API_ALL_FUNCTIONS) { /* Clear and set elements in the SQLUSMALLINT 100 element array */ memset(pfExists, 0, sizeof(SQLUSMALLINT) * 100); for (index= 0; index < myodbc_func_size; ++index) { if (myodbc3_functions[index] < 100) pfExists[myodbc3_functions[index]]= SQL_TRUE; } return SQL_SUCCESS; } *pfExists= SQL_FALSE; for (index= 0; index < myodbc_func_size; ++index) { if (myodbc3_functions[index] == fFunction) { *pfExists= SQL_TRUE; break; } } return SQL_SUCCESS; } mysql-connector-odbc-5.1.10-src/installer/Makefile.am100644 15766 12 2605 11707541005 21222 0ustar00cteamstaff################################################################### # # BRIEF: # This is used by the GNU auto-build to create a Makefile for # building the myodbcinst command-line utility. # # DESCRIPTION: # This is an optional part of the build. It is possible to build # this if the myodbcinst flag is set during configure (default # is yes) and libltdl is installed on the system. # # NOTE: # This program may need to be built when creating a binary # distribution because it may be used when installing the binary # distribution. This is the case on OSX and Linux for example. # ################################################################### if MYODBCINST INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/util bin_PROGRAMS = myodbc-installer myodbc_installer_SOURCES = myodbc3i.c myodbc_installer_LDADD = ../util/libmyodbc3u.la @DL_LIB@ @MYSQL_LIB@ $(ODBC_DM_LIB) myodbc_installer_DEPENDENCIES = ../util/libmyodbc3u.la # LDFLAGS=@EXTRA_LDFLAGS@ endif ################################################################### # # More files to include in source distro. # ################################################################### EXTRA_DIST = \ CMakeLists.txt mysql-connector-odbc-5.1.10-src/installer/CMakeLists.txt100644 15766 12 3324 11707541005 21725 0ustar00cteamstaff# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/util) IF(DL_INCLUDES) INCLUDE_DIRECTORIES(${DL_INCLUDES}) ENDIF(DL_INCLUDES) ADD_EXECUTABLE(myodbc-installer myodbc3i.c) IF(WIN32) TARGET_LINK_LIBRARIES(myodbc-installer myodbc3u ${ODBCLIB} ${ODBCINSTLIB} ${MYSQL_CLIENT_LIBS}) ELSE(WIN32) SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${ODBC_LINK_FLAGS}") TARGET_LINK_LIBRARIES(myodbc-installer mysqlclient_r myodbc3u ${CMAKE_THREAD_LIBS_INIT} m) ENDIF(WIN32) INSTALL(TARGETS myodbc-installer DESTINATION bin) mysql-connector-odbc-5.1.10-src/installer/myodbc3i.c100644 15766 12 53073 11707541005 21070 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /*! @brief This program will aid installers when installing/uninstalling MyODBC. This program can register/deregister a myodbc driver and create a sample dsn. The key thing here is that it does this with cross-platform code - thanks to the ODBC installer API. This is most useful to those creating installers (apps using myodbc or myodbc itself). For example, this program is used in the postinstall script of the MyODBC for Mac OS X installer package. */ /* * Installer utility application. Handles installing/removing/listing, etc * driver and data source entries in the system. See usage text for details. */ #include "MYODBC_MYSQL.h" #include "installer.h" #include "stringutil.h" #include #include const char *usage = "+--- \n" "| myodbc-installer v" MYODBC_VERSION " \n" "+--- \n" "| \n" "| Description \n" "| \n" "| This program can be used to create, edit or remove a DSN. It \n" "| can also be used to register or deregister a driver. In other \n" "| words - it can be used to manage ODBC system information. \n" "| \n" "| This operates consistently across platforms. This has been created\n" "| specifically for MySQL Connector/ODBC. \n" "| \n" "| Syntax \n" "| \n" "| myodbc-installer [Options] \n" "| \n" "| Object \n" "| \n" "| -d driver \n" "| -s datasource \n" "| \n" "| Action \n" "| \n" "| -l list \n" "| -a add (add/update for data source) \n" "| -r remove \n" "| \n" "| Options \n" "| \n" "| -n \n" "| -t \n" /* Note: These scope values line up with the SQLSetConfigMode constants */ "| -c0 both \n" "| -c1 user data source \n" "| -c2 system data source (default) \n" "| \n" "| Examples \n" "| \n" "| List drivers \n" "| -d -l \n" "| \n" "| Register a driver (UNIX example) \n" "| -d -a -n \"MySQL ODBC 5.1 Driver\" \\ \n" "| -t \"DRIVER=/usr/lib/myodbc5.so;SETUP=/usr/lib/myodbc3S.so\"\n" "| \n" "| Register a driver (Windows example) \n" "| -d -a -n \"MySQL ODBC 5.1 Driver\" \\ \n" "| -t \"DRIVER=myodbc5.dll;SETUP=myodbc5S.dll\" \n" "| \n" "| Add a new data source name \n" "| -s -a -c2 -n \"test\" \\ \n" "| -t \"DRIVER=MySQL ODBC 5.1 Driver;SERVER=localhost;DATABASE=test;UID=myid;PWD=mypwd\"\n" "| \n" "| List data source name attributes for 'test' \n" "| -s -l -c2 -n \"test\" \n" "+--- \n"; /* command line args */ #define OBJ_DRIVER 'd' #define OBJ_DATASOURCE 's' #define ACTION_LIST 'l' #define ACTION_ADD 'a' #define ACTION_REMOVE 'r' #define OPT_NAME 'n' #define OPT_ATTR 't' #define OPT_SCOPE 'c' static char obj= 0; static char action= 0; static UWORD scope= ODBC_SYSTEM_DSN; /* default = system */ /* these two are 'paired' and the char data is converted to the wchar buf. * we check the char * to see if the arg was given then use the wchar in the * installer api. */ static char *name= NULL; static SQLWCHAR *wname; static char *attrstr= NULL; static SQLWCHAR *wattrs; void object_usage() { fprintf(stderr, "[ERROR] Object must be either driver (d) or data source (s)\n"); } void action_usage() { fprintf(stderr, "[ERROR] One action must be specified\n"); } void main_usage() { fprintf(stderr, usage); } /* * Print an error retrieved from SQLInstallerError. */ void print_installer_error() { int msgno; DWORD errcode; char errmsg[256]; for(msgno= 1; msgno < 9; ++msgno) { if (SQLInstallerError(msgno, &errcode, errmsg, 256, NULL) != SQL_SUCCESS) return; fprintf(stderr, "[ERROR] SQLInstaller error %d: %s\n", errcode, errmsg); } } /* * Print a general ODBC error (non-odbcinst). */ void print_odbc_error(SQLHANDLE hnd, SQLSMALLINT type) { SQLCHAR sqlstate[20]; SQLCHAR errmsg[1000]; SQLINTEGER nativeerr; fprintf(stderr, "[ERROR] ODBC error"); if(SQL_SUCCEEDED(SQLGetDiagRec(type, hnd, 1, sqlstate, &nativeerr, errmsg, 1000, NULL))) printf(": [%s] %d -> %s\n", sqlstate, nativeerr, errmsg); printf("\n"); } /* * Handler for "list driver" command (-d -l -n drivername) */ int list_driver_details(Driver *driver) { SQLWCHAR buf[50000]; SQLWCHAR *entries= buf; int rc; /* lookup the driver */ if ((rc= driver_lookup(driver)) < 0) { fprintf(stderr, "[ERROR] Driver not found '%s'\n", ds_get_utf8attr(driver->name, &driver->name8)); return 1; } else if (rc > 0) { print_installer_error(); return 1; } /* print driver details */ printf("FriendlyName: %s\n", ds_get_utf8attr(driver->name, &driver->name8)); printf("DRIVER : %s\n", ds_get_utf8attr(driver->lib, &driver->lib8)); printf("SETUP : %s\n", ds_get_utf8attr(driver->setup_lib, &driver->setup_lib8)); return 0; } /* * Handler for "list drivers" command (-d -l) */ int list_drivers() { char buf[50000]; char *drivers= buf; if (!SQLGetInstalledDrivers(buf, 50000, NULL)) { print_installer_error(); return 1; } while (*drivers) { printf("%s\n", drivers); drivers += strlen(drivers) + 1; } return 0; } /* * Handler for "add driver" command (-d -a -n drivername -t attrs) */ int add_driver(Driver *driver, const SQLWCHAR *attrs) { DWORD usage_count; SQLWCHAR attrs_null[4096]; /* null-delimited pairs */ SQLWCHAR prevpath[256]; /* read driver attributes into object */ if (driver_from_kvpair_semicolon(driver, attrs)) { fprintf(stderr, "[ERROR] Could not parse key-value pair attribute string\n"); return 1; } /* convert to null-delimited for installer API */ if (driver_to_kvpair_null(driver, attrs_null, 4096)) { fprintf(stderr, "[ERROR] Could not create new key-value pair attribute string\n"); return 1; } if (SQLInstallDriverExW(attrs_null, NULL, prevpath, 256, NULL, ODBC_INSTALL_COMPLETE, &usage_count) != TRUE) { print_installer_error(); return 1; } printf("Success: Usage count is %d\n", usage_count); return 0; } /* * Handler for "remove driver" command (-d -a -n drivername) */ int remove_driver(Driver *driver) { DWORD usage_count; if (SQLRemoveDriverW(driver->name, FALSE, &usage_count) != TRUE) { print_installer_error(); return 1; } printf("Success: Usage count is %d\n", usage_count); return 0; } /* * Handle driver actions. We setup a driver object to be used * for all actions. */ int handle_driver_action() { int rc= 0; UWORD config_mode= config_set(scope); Driver *driver= driver_new(); /* check name is given if needed */ switch (action) { case ACTION_ADD: case ACTION_REMOVE: if (!name) { fprintf(stderr, "[ERROR] Name missing to add/remove driver\n"); rc= 1; goto end; } } /* set the driver name */ if (name) sqlwcharncpy(driver->name, wname, ODBCDRIVER_STRLEN); /* perform given action */ switch (action) { case ACTION_LIST: if (name) rc= list_driver_details(driver); else rc= list_drivers(); break; case ACTION_ADD: if (attrstr) rc= add_driver(driver, wattrs); else { fprintf(stderr, "[ERROR] Attribute string missing to add driver\n"); rc= 1; } break; case ACTION_REMOVE: rc= remove_driver(driver); break; } end: driver_delete(driver); config_set(config_mode); return rc; } /* * Handler for "list data source" command (-s -l -n drivername) */ int list_datasource_details(DataSource *ds) { int rc; if ((rc= ds_lookup(ds)) < 0) { fprintf(stderr, "[ERROR] Data source not found '%s'\n", ds_get_utf8attr(ds->name, &ds->name8)); return 1; } else if (rc > 0) { print_installer_error(); return 1; } /* print the data source fields */ printf("Name: %s\n", ds_get_utf8attr(ds->name, &ds->name8)); if (ds->driver ) printf("Driver: %s\n", ds_get_utf8attr(ds->driver, &ds->driver8)); if (ds->description) printf("Description: %s\n", ds_get_utf8attr(ds->description, &ds->description8)); if (ds->server ) printf("Server: %s\n", ds_get_utf8attr(ds->server, &ds->server8)); if (ds->uid ) printf("Uid: %s\n", ds_get_utf8attr(ds->uid, &ds->uid8)); if (ds->pwd ) printf("Pwd: %s\n", ds_get_utf8attr(ds->pwd, &ds->pwd8)); if (ds->database ) printf("Database: %s\n", ds_get_utf8attr(ds->database, &ds->database8)); if (ds->socket ) printf("Socket: %s\n", ds_get_utf8attr(ds->socket, &ds->socket8)); if (ds->initstmt ) printf("Initial statement: %s\n", ds_get_utf8attr(ds->initstmt, &ds->initstmt8)); if (ds->charset ) printf("Character set: %s\n", ds_get_utf8attr(ds->charset, &ds->charset8)); if (ds->sslkey ) printf("SSL key: %s\n", ds_get_utf8attr(ds->sslkey, &ds->sslkey8)); if (ds->sslcert ) printf("SSL cert: %s\n", ds_get_utf8attr(ds->sslcert, &ds->sslcert8)); if (ds->sslca ) printf("SSL CA: %s\n", ds_get_utf8attr(ds->sslca, &ds->sslca8)); if (ds->sslcapath ) printf("SSL CA path: %s\n", ds_get_utf8attr(ds->sslcapath, &ds->sslcapath8)); if (ds->sslcipher ) printf("SSL cipher: %s\n", ds_get_utf8attr(ds->sslcipher, &ds->sslcipher8)); if (ds->sslverify ) printf("Verify SSL cert yes\n"); if (ds->port ) printf("Port: %d\n", ds->port); printf("Options:\n"); if (ds->return_matching_rows) printf("\tFOUND_ROWS\n"); if (ds->allow_big_results) printf("\tBIG_PACKETS\n"); if (ds->dont_prompt_upon_connect) printf("\tNO_PROMPT\n"); if (ds->dynamic_cursor) printf("\tDYNAMIC_CURSOR\n"); if (ds->ignore_N_in_name_table) printf("\tNO_SCHEMA\n"); if (ds->user_manager_cursor) printf("\tNO_DEFAULT_CURSOR\n"); if (ds->dont_use_set_locale) printf("\tNO_LOCALE\n"); if (ds->pad_char_to_full_length) printf("\tPAD_SPACE\n"); if (ds->return_table_names_for_SqlDescribeCol) printf("\tFULL_COLUMN_NAMES\n"); if (ds->use_compressed_protocol) printf("\tCOMPRESSED_PROTO\n"); if (ds->ignore_space_after_function_names) printf("\tIGNORE_SPACE\n"); if (ds->force_use_of_named_pipes) printf("\tNAMED_PIPE\n"); if (ds->change_bigint_columns_to_int) printf("\tNO_BIGINT\n"); if (ds->no_catalog) printf("\tNO_CATALOG\n"); if (ds->read_options_from_mycnf) printf("\tUSE_MYCNF\n"); if (ds->safe) printf("\tSAFE\n"); if (ds->disable_transactions) printf("\tNO_TRANSACTIONS\n"); if (ds->save_queries) printf("\tLOG_QUERY\n"); if (ds->dont_cache_result) printf("\tNO_CACHE\n"); if (ds->force_use_of_forward_only_cursors) printf("\tFORWARD_CURSOR\n"); if (ds->auto_reconnect) printf("\tAUTO_RECONNECT\n"); if (ds->auto_increment_null_search) printf("\tAUTO_IS_NULL\n"); if (ds->zero_date_to_min) printf("\tZERO_DATE_TO_MIN\n"); if (ds->min_date_to_zero) printf("\tMIN_DATE_TO_ZERO\n"); if (ds->allow_multiple_statements) printf("\tMULTI_STATEMENTS\n"); if (ds->limit_column_size) printf("\tCOLUMN_SIZE_S32\n"); if (ds->handle_binary_as_char) printf("\tNO_BINARY_RESULT\n"); if (ds->default_bigint_bind_str) printf("\tDFLT_BIGINT_BIND_STR\n"); return 0; } /* * Handler for "list data sources" command (-s -l) */ int list_datasources() { SQLHANDLE env; SQLRETURN rc; SQLUSMALLINT dir= 0; /* SQLDataSources fetch direction */ SQLCHAR name[256]; SQLCHAR description[256]; /* determine 'direction' to pass to SQLDataSources */ switch (scope) { case ODBC_BOTH_DSN: dir= SQL_FETCH_FIRST; break; case ODBC_USER_DSN: dir= SQL_FETCH_FIRST_USER; break; case ODBC_SYSTEM_DSN: dir= SQL_FETCH_FIRST_SYSTEM; break; } /* allocate handle and set ODBC API v3 */ if ((rc= SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env)) != SQL_SUCCESS) { fprintf(stderr, "[ERROR] Failed to allocate env handle\n"); return 1; } if ((rc= SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER)) != SQL_SUCCESS) { print_odbc_error(env, SQL_HANDLE_ENV); SQLFreeHandle(SQL_HANDLE_ENV, env); return 1; } /* retrieve and print data source */ while ((rc= SQLDataSources(env, dir, name, 256, NULL, description, 256, NULL)) == SQL_SUCCESS) { printf("%-20s - %s\n", name, description); dir= SQL_FETCH_NEXT; } if (rc != SQL_NO_DATA) { print_odbc_error(env, SQL_HANDLE_ENV); SQLFreeHandle(SQL_HANDLE_ENV, env); return 1; } SQLFreeHandle(SQL_HANDLE_ENV, env); return 0; } /* * Handler for "add data source" command (-s -a -n drivername -t attrs) */ int add_datasource(DataSource *ds, const SQLWCHAR *attrs) { /* read datasource object from attributes */ if (ds_from_kvpair(ds, attrs, ';')) { fprintf(stderr, "[ERROR] Could not parse key-value pair attribute string\n"); return 1; } /* validate */ if (!ds->driver) { fprintf(stderr, "[ERROR] Driver must be specified for a data source\n"); return 1; } /* Add it */ if (ds_add(ds)) { print_installer_error(); fprintf(stderr, "[ERROR] Data source entry failed, remove or try again\n"); return 1; } printf("Success\n"); return 0; } /* * Handler for "remove data source" command (-s -r -n drivername) */ int remove_datasource(DataSource *ds) { /* First check that it exists, because SQLRemoveDSNFromIni returns true, even if it doesn't. */ if (ds_exists(ds->name)) { fprintf(stderr, "[ERROR] Data source doesn't exist\n"); return 1; } if (SQLRemoveDSNFromIniW(ds->name) != TRUE) { print_installer_error(); return 1; } printf("Success\n"); return 0; } /* * Handle driver actions. We setup a data source object to be used * for all actions. Config/scope set+restore is wrapped around the * action. */ int handle_datasource_action() { int rc= 0; UWORD config_mode= config_set(scope); DataSource *ds= ds_new(); /* check name is given if needed */ switch (action) { case ACTION_ADD: case ACTION_REMOVE: if (!name) { fprintf(stderr, "[ERROR] Name missing to add/remove data source\n"); rc= 1; goto end; } } /* set name if given */ if (name) ds_set_strattr(&ds->name, wname); /* perform given action */ switch (action) { case ACTION_LIST: if (name) rc= list_datasource_details(ds); else rc= list_datasources(); break; case ACTION_ADD: if (scope == ODBC_BOTH_DSN) { fprintf(stderr, "[ERROR] Adding data source must be either " "user or system scope (not both)\n"); rc= 1; } else if (!attrstr) { fprintf(stderr, "[ERROR] Attribute string missing to add data source\n"); rc= 1; } else { rc= add_datasource(ds, wattrs); } break; case ACTION_REMOVE: rc= remove_datasource(ds); break; } end: ds_delete(ds); config_set(config_mode); return rc; } /* * Entry point, parse args and do first set of validation. */ int main(int argc, char **argv) { char *arg; int i; SQLINTEGER convlen; /* minimum number of args to do anything useful */ if (argc < 3) { fprintf(stderr, "[ERROR] Not enough arguments given\n"); main_usage(); return 1; } /* parse args */ for(i= 1; i < argc; ++i) { arg= argv[i]; if (*arg == '-') arg++; /* we should be left with a single character option (except scope) * all strings are skipped by the respective option below */ if (*arg != OPT_SCOPE && *(arg + 1)) { fprintf(stderr, "[ERROR] Invalid command line option: %s\n", arg); return 1; } switch (*arg) { case OBJ_DRIVER: case OBJ_DATASOURCE: /* make sure we haven't already assigned the object */ if (obj) { object_usage(); return 1; } obj= *arg; break; case ACTION_LIST: case ACTION_ADD: case ACTION_REMOVE: if (action) { action_usage(); return 1; } action= *arg; break; case OPT_NAME: if (i + 1 == argc || *argv[i + 1] == '-') { fprintf(stderr, "[ERROR] Missing name\n"); return 1; } name= argv[++i]; break; case OPT_ATTR: if (i + 1 == argc || *argv[i + 1] == '-') { fprintf(stderr, "[ERROR] Missing attribute string\n"); return 1; } attrstr= argv[++i]; break; case OPT_SCOPE: /* convert to integer */ scope= *(++arg) - '0'; if (scope > 2 || *(arg + 1) /* another char exists */) { fprintf(stderr, "[ERROR] Invalid scope: %s\n", arg); return 1; } break; case 'h': /* print usage if -h is given anywhere */ main_usage(); return 1; default: fprintf(stderr, "[ERROR] Invalid command line option: %s\n", arg); return 1; } } if (!action) { action_usage(); return 1; } /* init libmysqlclient */ my_init(); utf8_charset_info= get_charset_by_csname("utf8", MYF(MY_CS_PRIMARY), MYF(0)); /* convert to SQLWCHAR for installer API */ convlen= SQL_NTS; if (name && !(wname= sqlchar_as_sqlwchar(default_charset_info, (SQLCHAR *)name, &convlen, NULL))) { fprintf(stderr, "[ERROR] Name is invalid\n"); return 1; } convlen= SQL_NTS; if (attrstr && !(wattrs= sqlchar_as_sqlwchar(default_charset_info, (SQLCHAR *)attrstr, &convlen, NULL))) { fprintf(stderr, "[ERROR] Attribute string is invalid\n"); return 1; } /* handle appropriate action */ switch (obj) { case OBJ_DRIVER: return handle_driver_action(); case OBJ_DATASOURCE: return handle_datasource_action(); default: object_usage(); return 1; } } mysql-connector-odbc-5.1.10-src/scripts/macosx/Welcome.html.in100644 15766 12 1352 11707541005 23036 0ustar00cteamstaff Welcome
MySQL Connector/ODBC
@VERSION@

This is the installer for MySQL Connector/ODBC, which allows applications to work with MySQL servers by using the cross-platform, database-agnostic ODBC interface.


Please visit http://www.mysql.com for more information.

mysql-connector-odbc-5.1.10-src/scripts/macosx/myodbc.png100644 15766 12 4013 11707541005 22130 0ustar00cteamstaff‰PNG  IHDR€u†aéa pHYs  šœ½IDATxÚíͪ\E…ó>8tâècˆO!èÌ‘ 8Dt¢ƒàDˆ:“83œ) :Q‰ š`Œ?‰m¾†uÙìÔùëªê>}{-(nß¾§«ûžýÕ®½wÕ9}ac´.øËXÀ2–° €e,`ËXÀ2–° €e,`ËXÀ2–° €e,`Ëœ–nÿýïæâ•¯6ž}ã¬=òÂÛÛçø›8çúìû«[ƒ?ôÜ››Ç^zgûS ð À9–Fÿó^Ùþ~ýæííc`O^ühóÛŸ€óêé9 ƒBœäžxõÒöo¼Ö¹ñã¾üñçE@08ÓD††§84 ‘ÀÈïñÝdÐxù›Ï ‘WxúÒ'  @axŒÉhÇ+ÌÑ·×ol ½Á@`¥º{ïÞv Àµ4êKÆåuüMA#Þ@¥82ýtãÖÙ|çõ—cä JA¥8²´0{Ü=£@Ƽˆ¦’¥Ó‰XQ@ˆÀx1¨“«/e Sô @ã´Ñß5½Ëôž @§i &˜Ëä)ŬXq¾¯qß¼V™EÏuÐXNQÿÔœ?¥˜[ô @§`P+„¤‡µé¥JÉ=âÐIrßµ«ÄŠ+€ŠlÂIF€ñ1Zm‰€´~0VK0+’æï¨Ž X å ¢èœ ÐZsð(ŠZ¦… “4b[-îÄ´ZU @')pk¹ÄKFÀ”ÒrÕÐt¬0R™ Zö+/ÀÔbV*F*Q;Fj]¼¡ï–ÕAÐ Òµ Á5†Ã ¨:ØÂ €=y€XЩ­è1­´ò c —…1^¡ÅnàXªõ s ŠÁãNàÚ(^ýiWñ®åfй w×÷ÉjŠb,AXšu€N’Ë—»ó†£ú—¢©ß¹Ù‡ØÃ4­Ãñ\«Õ=Leq‹ùœÑtTÜÐ!£h“ˆö ´^Ý+í)+€ÎÒÃÌûÀ]3#´×Å ñºE¼ÂPl`(ÖÖK¼±A6tí¢X0þ’ÀmɤËÒ´ˆ”=X‰H ™ jRCbÄk'Ré’ôœy€•Å »–víù>üŽÁ Ï\k| ƒËPŒäÚ>s¿|Ö¨/Y´Ci7ñl–x©Ñ¿tÁ¢FœPNb<©¸VžÇÝÎm[—} QŸ%cª_Žêp0z62Pµ *Þ(_¯8€+âst4Ç äÑŸï¢)÷@<áœP TÛçÐ(ž5:ï¿6²¦‚Īc¾Peœ6å€$~æŸüÜ>=@€QÚŒT Ç‚Q>Ç>È™Â$¸ñHЦ*Uù5@Ä›f¯=€êỉù=#¨k Æ%û@Ìe/Àhšâ¥Ì9ð(¥ƒ‡€#ïS`V  è’´2¯4ïZéí©òêf×6U \ÚJ£°v/@ì;/$ ­6.M]°ä”ÝàˆÛŽrûÂÄ) /oª@Ÿ½8ü Read Me
MySQL Connctor/ODBC
@VERSION@

This package contains a binary release of MySQL Connector/ODBC for Mac OS X.

This package does not require anything special to be installed on your machine - not even other MySQL software. Mac OS X 10.4 and later include the iODBC driver manager.

When the installation is complete you can use the following program to help manage your ODBC connections:

  /Applications/Utilities/ODBC Administrator.app

This installer will:

  1. Copy the ODBC driver and setup libraries to /usr/local/lib/.
  2. Copy command-line utilities to /usr/local/bin/.
  3. Register the driver and setup libraries with the iODBC driver manager.

The library files are;

  • /usr/local/lib/libmyodbc5.*
  • /usr/local/lib/libmyodbc3S.*

The command-line utility file are;

  • /usr/local/bin/myodbc-installer

This package can be manually removed from your system by removing all of the files installed (see above) and by doing the following;

  $ sudo rm -r /Library/Receipts/MyODBC*
mysql-connector-odbc-5.1.10-src/scripts/macosx/License.html.in100644 15766 12 40043 11707541005 23045 0ustar00cteamstaff Connector/ODBC - License

GNU GENERAL PUBLIC LICENSE

Version 2, June 1991

Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA

Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

Preamble

The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.

To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.

Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.

Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.

The precise terms and conditions for copying, distribution and modification follow.

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.

1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.

You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.

2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:


a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.

b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.

c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.

In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:


a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,

b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,

c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.

4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.

6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.

7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.

9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.

10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

NO WARRANTY

11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

mysql-connector-odbc-5.1.10-src/scripts/macosx/postflight100755 15766 12 6736 11707541005 22274 0ustar00cteamstaff#!/bin/sh ############################################################################## # # Post Install file for MySQL Connector ODBC 5.1 # ############################################################################## # ---------------------------------------------------------------------- # ENSURE WE HAVE INI FILES # # Upon a fresh install of OS X these files do not exist. The ODBC Installer # library calls should create these files for us when we do stuff like # request to register a driver but this does not seem to happen and the # request fails. So we we start by making sure we have some, mostly, empty # ini files in place before we make installer calls. # # Also note that there are many places where these ini files *could* go # based upon the search algorithm in the default ODBC system or any other # ODBC system which may get installed. We choose the following because they # seem to be the ones created when we use the standard ODBC Administrator # application. # ---------------------------------------------------------------------- libdir=/usr/local/lib bindir=/usr/local/bin for admdir in ~/Library/ODBC /Library/ODBC do echo "Ensuring $admdir, odbcinst.ini and odbc.ini exists..." if [ ! -d $admdir ] ; then mkdir $admdir chmod 775 $admdir chown root:admin $admdir fi if [ ! -f $admdir/odbc.ini ] ; then echo "[ODBC Data Sources]" > $admdir/odbc.ini echo "" >> $admdir/odbc.ini echo "[ODBC]" >> $admdir/odbc.ini echo "Trace = 0" >> $admdir/odbc.ini echo "TraceAutoStop = 0" >> $admdir/odbc.ini echo "TraceFile =" >> $admdir/odbc.ini echo "TraceLibrary =" >> $admdir/odbc.ini chmod 664 $admdir/odbc.ini chown root:admin $admdir/odbc.ini fi if [ ! -f $admdir/odbcinst.ini ] ; then echo "[ODBC Drivers]" > $admdir/odbcinst.ini echo "" >> $admdir/odbcinst.ini echo "[ODBC Connection Pooling]" >> $admdir/odbcinst.ini echo "PerfMon = 0" >> $admdir/odbcinst.ini echo "Retry Wait = " >> $admdir/odbcinst.ini chmod 664 $admdir/odbcinst.ini chown root:admin $admdir/odbcinst.ini fi done # ---------------------------------------------------------------------- # SET USER PERMISSIONS # # Note that if the Mac OS X "ODBC Administrator" is run before this # script, and unlocked (root rights), it would save files in the user # area with root permissions, causing trouble later when trying to # change user settings without unlocking (root rights). # ---------------------------------------------------------------------- if [ "$USER" -a "$GROUPS" ] ; then chown -R $USER:$GROUPS ~/Library/ODBC fi # ---------------------------------------------------------------------- # REGISTER THE DRIVER # ---------------------------------------------------------------------- echo "Registering driver..." #$bindir/myodbc-installer -a -d -n "MySQL ODBC 5.1 Driver" -t "Driver=$libdir/libmyodbc5.so;Setup=$libdir/libmyodbc3S.so;" $bindir/myodbc-installer -a -d -n "MySQL ODBC 5.1 Driver" -t "Driver=$libdir/libmyodbc5.so;" # ---------------------------------------------------------------------- # CREATE A SAMPLE DSN # ---------------------------------------------------------------------- echo "Ensuring sample data source name (myodbc) exists..." $bindir/myodbc-installer -a -s -n myodbc -t "Driver=MySQL ODBC 5.1 Driver;SERVER=localhost;" mysql-connector-odbc-5.1.10-src/scripts/makerpm.sh100644 15766 12 3571 11707541005 20653 0ustar00cteamstaff#!/bin/sh # # Configure function # function configure_driver { make -f Makefile.bk ./configure \ --prefix=${RPM_BUILD_ROOT}%{prefix} \ --with-mysql-libs=%{MYSQL_LIBS} \ --with-mysql-includes=%{MYSQL_INCLUDES} \ ${ODBC_DM_PATH_ARG} \ --enable-shared=yes \ --enable-static=yes \ --without-debug make dist; } # # mysql-connector-odbc 3.51 rpm build script # RELEASE=@VERSION@ SPEC_FILE=mysql-connector-odbc-@VERSION@.spec ARCH=`uname -m | perl -p -e 's/i[0-9]{3}/i386/'` SOURCE=mysql-connector-odbc # # This script must run from MyODBC top directory # if [ ! -f "./driver/myodbc3.c" ]; then echo "ERROR : You must run this script from the MyODBC top-level directory" exit 1 fi ODBC_DM_PATH_ARG="@ODBC_DM_PATH_ARG@" MYSQL_LIBS="@MYSQL_USED_LIB_PATH@" MYSQL_INCLUDES="@MYSQL_USED_INCLUDE_PATH@" MyODBC_ROOT=`pwd` # # Find the RPM build area # if [ -d /usr/src/redhat ] then RPM_BUILD_AREA=/usr/src/redhat else if [ -d /usr/src/packages ] then RPM_BUILD_AREA=/usr/src/packages fi fi # # setup the src tar ball # SRC_FILE=$SOURCE-${RELEASE} SRC_TAR_ARCH=$SRC_FILE.tar.gz if [ ! -f "$SRC_TAR_ARCH" ]; then echo "Tarball file '$SRC_TAR_ARCH' does not exist, please make one first" exit 1 fi if [ ! -f "./scripts/${SPEC_FILE}" ]; then echo "Spec file '${SPEC_FILE}' does not exist, please make one first" exit 1 fi echo "Building RPM for mysql-connector-odbc version ${RELEASE}" cp ./scripts/${SPEC_FILE} ${RPM_BUILD_AREA}/SPECS/ cp $SRC_TAR_ARCH ${RPM_BUILD_AREA}/SOURCES/ cd ${RPM_BUILD_AREA}/SPECS # # Generate RPMs # echo "Running the spec file from '${RPM_BUILD_AREA}/SPECS'" rpmbuild -ba ${SPEC_FILE} if test $? -eq 0 then ln -s ${RPM_BUILD_AREA}/RPMS/${ARCH}/$SRC_FILE-1.${ARCH}.rpm $SRC_FILE-1.${ARCH}.rpm ln -s ${RPM_BUILD_AREA}/SRPMS/$SRC_FILE-1.src.rpm $SRC_FILE-1.src.rpm else echo "ERROR: rpm build failed for '$SRC_FILE'" fi mysql-connector-odbc-5.1.10-src/scripts/make_binary_distribution.sh100644 15766 12 5351 11707541005 24275 0ustar00cteamstaff#!/bin/sh # The default path should be /usr/local # Get some info from configure # chmod +x ./scripts/setsomevars machine=@MACHINE_TYPE@ system=@SYSTEM_TYPE@ version=@VERSION@ export machine system version SOURCE=`pwd` CP="cp -p" RM=rm MV=mv CHMOD=chmod SED=sed DEBUG=0 TMP=/tmp SILENT=0 # This script must run from MyODBC top directory if ! test -f driver/myodbc3.c then echo "ERROR : You must run this script from the MyODBC top-level directory" exit 1 fi parse_arguments() { for arg do case "$arg" in --debug) DEBUG=1;; --tmp=*) TMP=`echo "$arg" | sed -e "s;--tmp=;;"` ;; --silent) SILENT=1 ;; *) echo "Unknown argument '$arg'" exit 1 ;; esac done } parse_arguments "$@" # This should really be integrated with automake and not duplicate the # installation list. BASE=$TMP/mysql-connector-odbc-3.51 if [ -d $BASE ] ; then rm -r -f $BASE fi mkdir $BASE for i in ChangeLog COPYING EXCEPTIONS README odbc.ini Licenses_for_Third-Party_Components.txt do if [ -f $i ] then $CP $i $BASE fi done for i in driver/.libs/* do if [ -f $i ] then $CP $i $BASE fi done # for i in driver_r/.libs/* # do # if [ -f $i ] # then # $CP $i $BASE # fi # done if test -f $BASE/libmyodbc3-$version.so then $RM -f $BASE/libmyodbc3.so cd $BASE ln -s libmyodbc3-$version.so libmyodbc3.so cd $SOURCE fi if test -f $BASE/libmyodbc3_r-$version.so then $RM -f $BASE/libmyodbc3_r.so cd $BASE ln -s libmyodbc3_r-$version.so libmyodbc3_r.so cd $SOURCE fi # Change the distribution to a long descriptive name NEW_NAME=mysql-connector-odbc-$version-$system-$machine BASE2=$TMP/$NEW_NAME $RM -r -f $BASE2 $MV $BASE $BASE2 BASE=$BASE2 #if we are debugging, do not do tar/gz if [ x$DEBUG = x1 ] ; then echo "Debug mode, no archiving, check the files from $BASE" echo "`ls -al $BASE`" echo "exiting..." exit fi # This is needed to prefere gnu tar instead of tar because tar can't # always handle long filenames PATH_DIRS=`echo $PATH | $SED -e 's/^:/. /' -e 's/:$/ ./' -e 's/::/ . /g' -e 's/:/ /g' ` which_1 () { for cmd do for d in $PATH_DIRS do for file in $d/$cmd do if test -x $file -a ! -d $file then echo $file exit 0 fi done done done exit 1 } # # Create the result tar file # tar=`which_1 gtar` if test "$?" = "1" -o "$tar" = "" then tar=tar fi echo "Using $tar to create archive" cd $TMP OPT=cvf if [ x$SILENT = x1 ] ; then OPT=cf fi if test -f $SOURCE/$NEW_NAME.tar.gz then echo "Cleaning the old file..." $RM -f $SOURCE/$NEW_NAME.tar.gz fi $tar $OPT $SOURCE/$NEW_NAME.tar $NEW_NAME cd $SOURCE echo "Compressing archive" gzip -9 $NEW_NAME.tar echo "Removing temporary directory" rm -r -f $BASE echo "$NEW_NAME.tar.gz created" mysql-connector-odbc-5.1.10-src/scripts/Makefile.am100644 15766 12 6670 11707541005 20722 0ustar00cteamstaff############################################################################### # Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved # # # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # # , like most # # MySQL Connectors. There are special exceptions to the terms and # # conditions of the GPLv2 as it is applied to this software, see the # # FLOSS License Exception # # . # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published # # by the Free Software Foundation; version 2 of the License. # # # # This program is distributed in the hope that it will be useful, but # # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # # for more details. # # # # You should have received a copy of the GNU General Public License along # # with this program; if not, write to the Free Software Foundation, Inc., # # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ############################################################################### ########################################################################## # # # Makefile.am # # # # @description: This is the MyODBC 3.51 driver(scripts) Makefile.am # # # # @author : MySQL AB (monty@mysql.com, venu@mysql.com) # # @date : 2002-10-30 # # @product : myodbc3 # # # ########################################################################## CLEANFILES = make_binary_distribution \ myodbc3.spec \ mysql-connector-odbc-@VERSION@.spec .sh: rm -f $@ $@-t sed \ -e 's!@''VERSION''@!@VERSION@!' \ -e 's!@''NUMERIC_VERSION''@!@NUMERIC_VERSION@!' \ -e 's!@''MACHINE_TYPE''@!@MACHINE_TYPE@!' \ -e 's!@''SYSTEM_TYPE''@!@SYSTEM_TYPE@!' \ -e 's!@''ODBC_DM_PATH_ARG''@!@ODBC_DM_PATH_ARG@!' \ -e 's!@''MYSQL_PATH_ARG''@!@MYSQL_PATH_ARG@!' \ $< > $@-t chmod +x $@-t mv $@-t $@ all: make_binary_distribution \ mysql-connector-odbc-@VERSION@.spec \ makerpm mysql-connector-odbc-@VERSION@.spec: myodbc3.spec rm -f $@ cp myodbc3.spec $@ SUFFIXES = .sh EXTRA_DIST = \ make_binary_distribution.sh \ myodbc3.spec.sh \ makerpm.sh \ macosx/License.html.in \ macosx/ReadMe.html.in \ macosx/Welcome.html.in \ macosx/myodbc.png \ macosx/postflight mysql-connector-odbc-5.1.10-src/scripts/myodbc3.spec.sh100644 15766 12 15622 11707541005 21530 0ustar00cteamstaff# Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to the # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston # MA 02110-1301 USA. ############################################################################## # # mysql-connector-odbc 5.1 RPM specification # ############################################################################## # You can control the license file to include building like # "rpmbuild --with commercial" or "rpm --define '_with_commercial 1'" # (for RPM 3.x). The default is GPL. %{?_with_commercial:%define com_lic 1} %{!?_with_commercial:%define com_lic 0} %if %{com_lic} %define lic_type Commercial %else %define lic_type GPL %endif %define no_odbc_gui 0 ############################################################################## # # Main information section # ############################################################################## %define mysql_vendor Oracle and/or its affiliates %if %{com_lic} Name: mysql-connector-odbc-commercial %else Name: mysql-connector-odbc %endif Summary: An ODBC 5.1 driver for MySQL - driver package Group: Applications/Databases Version: @NUMERIC_VERSION@ Release: 1 Provides: mysqlodbcrpmpack License: Copyright (c) 2000, 2011, %{mysql_vendor}. All rights reserved. Under %{lic_type} license as shown in the Description field. Source0: %{name}-@VERSION@.tar.gz URL: http://www.mysql.com/ Vendor: Oracle Corporation Packager: Oracle Corporation Production Engineering Team BuildRoot: %{_tmppath}/%{name}-@VERSION@-build %if %{no_odbc_gui} %else %package setup Summary: An ODBC 5.1 driver for MySQL - setup library Group: Application/Databases Requires: mysqlodbcrpmpack %endif ############################################################################## # # Documentation # ############################################################################## %description mysql-connector-odbc is an ODBC (3.50) level 0 (with level 1 and level 2 features) driver for connecting an ODBC-aware application to MySQL. mysql-connector-odbc works on Windows NT/2000/XP/2003, and most Unix platforms (incl. OSX and Linux). MySQL is a trademark of %{mysql_vendor} mysql-connector-odbc 5.1 is an enhanced version of MyODBC 2.50 to meet ODBC 3.5 specification. The driver is commonly referred to as 'MySQL ODBC 5.1 Driver'. The MySQL software has Dual Licensing, which means you can use the MySQL software free of charge under the GNU General Public License (http://www.gnu.org/licenses/). You can also purchase commercial MySQL licenses from %{mysql_vendor} if you do not wish to be bound by the terms of the GPL. See the chapter "Licensing and Support" in the manual for further info. The MySQL web site (http://www.mysql.com/) provides the latest news and information about the MySQL software. Also please see the documentation and the manual for more information. %if %{no_odbc_gui} %else %description setup The setup library for the MySQL ODBC package, handles the optional GUI dialog for configuring the driver. %endif ############################################################################## # # Build # ############################################################################## %prep %setup %define ODBC_DM_PATH_ARG @ODBC_DM_PATH_ARG@ %define MYSQL_PATH_ARG @MYSQL_PATH_ARG@ %define EXTRA_XLIBS @EXTRA_XLIBS@ %build mkdir release cd release cmake -G "Unix Makefiles" \ -DRPM_BUILD=1 \ -DCMAKE_INSTALL_PREFIX=%{_prefix} \ %if %{no_odbc_gui} -DDISABLE_GUI=1 \ %endif %{ODBC_DM_PATH_ARG} \ %{MYSQL_PATH_ARG} \ .. # Note that the ".." needs to be last, in case some arguments expands to # the empty string, and then "cmake" thinks is "current directory" make VERBOSE=1 ############################################################################## # # Cleanup # ############################################################################## %clean [ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf $RPM_BUILD_ROOT ############################################################################## # # Install and deinstall scripts # ############################################################################## # ---------------------------------------------------------------------- # Install, but remove the doc files. # The way %doc works, we can't # have these files installed # ---------------------------------------------------------------------- %install cd release make DESTDIR=$RPM_BUILD_ROOT install VERBOSE=1 rm -vf $RPM_BUILD_ROOT%{_prefix}/{ChangeLog,README*,LICENSE.*,COPYING,INSTALL*,Licenses_for_Third-Party_Components.txt} rm -vfr $RPM_BUILD_ROOT%{_prefix}/test # ---------------------------------------------------------------------- # REGISTER DRIVER # Note that "-e" is not working for drivers currently, so we have to # deinstall before reinstall to change anything # ---------------------------------------------------------------------- %post myodbc-installer -a -d -n "MySQL ODBC 5.1 Driver" -t "DRIVER=%{_libdir}/libmyodbc5.so" %if %{no_odbc_gui} %else %post setup myodbc-installer -r -d -n "MySQL ODBC 5.1 Driver" myodbc-installer -a -d -n "MySQL ODBC 5.1 Driver" -t "DRIVER=%{_libdir}/libmyodbc5.so;SETUP=%{_libdir}/libmyodbc3S.so" %endif # ---------------------------------------------------------------------- # DEREGISTER DRIVER # ---------------------------------------------------------------------- # Removing the driver package, we simply orphan any related DSNs %preun myodbc-installer -r -d -n "MySQL ODBC 5.1 Driver" # Removing the setup RPM, downgrade the registration %if %{no_odbc_gui} %else %preun setup myodbc-installer -r -d -n "MySQL ODBC 5.1 Driver" myodbc-installer -a -d -n "MySQL ODBC 5.1 Driver" -t "DRIVER=%{_libdir}/libmyodbc5.so" %endif ############################################################################## # # Listing of files to be in the package # ############################################################################## %files %defattr(-,root,root) %{_bindir}/myodbc-installer %{_libdir}/libmyodbc5.* %doc ChangeLog README README.debug INSTALL INSTALL.win Licenses_for_Third-Party_Components.txt %if %{com_lic} %doc LICENSE.mysql %else %doc COPYING %endif %if %{no_odbc_gui} %else %files setup %{_libdir}/libmyodbc3S* %endif ############################################################################## mysql-connector-odbc-5.1.10-src/setupgui/windows/TabCtrl.cpp100644 15766 12 43371 11707541005 22614 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file TabCtrl.cpp @brief Tab Control Enhanced. Make Creating and modifying Tab Control Property pages a snap. (c) 2006 David MacDermot This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include "resource.h" #include "TabCtrl.h" /** Global defs *************************************************************/ #define CMD_VK_UP 101 #define CMD_VK_DOWN 102 #define CMD_VK_RIGHT 103 #define CMD_VK_LEFT 104 /** Global variables ********************************************************/ static LPTABCTRL This; static BOOL stopTabPageMessageLoop=FALSE; /** Prototypes **************************************************************/ static void TabPageMessageLoop (HWND); static void ResetTabPageMessageLoop (HWND); /** Macroes *****************************************************************/ #define Refresh(A) RedrawWindow(A,NULL,NULL,RDW_ERASE|RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_UPDATENOW); /** Functions **************************************************************/ void TabControl_GetClientRect(HWND hwnd,RECT* prc) { ////////////////////////////////////////////// // The standard GetClientRect doesn't return the // desired rectangle under every possible tab position // // Note: This function does not populate a standard rectangle format but rather // prc.left = left, prc.top = top, prc.right = width, and prc.bottom = height RECT rtab_0; LONG lStyle= GetWindowLongPtr(hwnd,GWL_STYLE); // Calculate the tab control's display area GetWindowRect(hwnd, prc); ScreenToClient(GetParent(hwnd), (POINT*)&prc->left); ScreenToClient(hwnd, (POINT*)&prc->right); TabCtrl_GetItemRect(hwnd,0,&rtab_0); //The tab itself if((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL)) //Tabs to Right { prc->top = prc->top + 6; //x coord prc->left = prc->left + 4; //y coord prc->bottom = prc->bottom - 12; // height prc->right = prc->right - (12 + rtab_0.right-rtab_0.left); // width } else if(lStyle & TCS_VERTICAL) //Tabs to Left { prc->top = prc->top + 6; //x coord prc->left = prc->left + (4 + rtab_0.right-rtab_0.left); //y coord prc->bottom = prc->bottom - 12; // height prc->right = prc->right - (12 + rtab_0.right-rtab_0.left); // width } else if(lStyle & TCS_BOTTOM) //Tabs on Bottom { prc->top = prc->top + 6; //x coord prc->left = prc->left + 4; //y coord prc->bottom = prc->bottom - (16 + rtab_0.bottom-rtab_0.top); // height prc->right = prc->right - 12; // width } else //Tabs on top { prc->top = prc->top + (6 + rtab_0.bottom-rtab_0.top); //x coord prc->left = prc->left + 4; //y coord prc->bottom = prc->bottom - (16 + rtab_0.bottom-rtab_0.top); // height prc->right = prc->right - 12; // width } } BOOL CenterTabPage (HWND hPage) { ///////////////////////////////////////////////////// // Center the tab page in the tab control's display area // RECT rect, rclient; TabControl_GetClientRect(This->hTab, &rect); // left, top, width, height // Get the tab page size GetClientRect(hPage, &rclient); rclient.right=rclient.right-rclient.left;// width rclient.bottom=rclient.bottom-rclient.top;// height rclient.left= rect.left; rclient.top= rect.top; // Center the tab page, or cut it off at the edge of the tab control(bad) if(rclient.righthTab, &rect); // left, top, width, height // Move the child and put it on top return SetWindowPos(hPage, HWND_TOP, rect.left, rect.top, rect.right, rect.bottom, 0); } /**************************************************************************** * * * Function: OnKeyDown * * * * Purpose : Handle key presses in the tab control (but not the tab pages) * * * * History : Date Reason * * 00/00/00 Created * * * ****************************************************************************/ BOOL OnKeyDown(LPARAM lParam) { BOOL verticalTabs; TC_KEYDOWN *tk=(TC_KEYDOWN *)lParam; int itemCount=TabCtrl_GetItemCount(tk->hdr.hwndFrom); int currentSel=TabCtrl_GetCurSel(tk->hdr.hwndFrom); if(itemCount <= 1) return FALSE; // Ignore if only one TabPage verticalTabs= GetWindowLongPtr(This->hTab, GWL_STYLE) & TCS_VERTICAL; if(verticalTabs) { switch (tk->wVKey) { case VK_PRIOR: //select the previous page { if(0==currentSel) return TRUE; TabCtrl_SetCurSel(tk->hdr.hwndFrom, currentSel-1); TabCtrl_SetCurFocus(tk->hdr.hwndFrom,currentSel-1); } return TRUE; case VK_UP: //select the previous page { if(0==currentSel) return TRUE; TabCtrl_SetCurSel(tk->hdr.hwndFrom, currentSel-1); TabCtrl_SetCurFocus(tk->hdr.hwndFrom, currentSel); } return TRUE; case VK_NEXT: //select the next page { TabCtrl_SetCurSel(tk->hdr.hwndFrom, currentSel+1); TabCtrl_SetCurFocus(tk->hdr.hwndFrom, currentSel+1); } return TRUE; case VK_DOWN: //select the next page { TabCtrl_SetCurSel(tk->hdr.hwndFrom, currentSel+1); TabCtrl_SetCurFocus(tk->hdr.hwndFrom,currentSel); } return TRUE; case VK_LEFT: //navagate within selected child tab page { SetFocus(This->hTabPages[currentSel]); // focus to child tab page TabPageMessageLoop (This->hTabPages[currentSel]); //start message loop } return TRUE; case VK_RIGHT: //navagate within selected child tab page { SetFocus(This->hTabPages[currentSel]); TabPageMessageLoop (This->hTabPages[currentSel]); } return TRUE; default: return FALSE; } } // if(verticalTabs) else // horizontal Tabs { switch (tk->wVKey) { case VK_NEXT: //select the previous page { if(0==currentSel) return TRUE; TabCtrl_SetCurSel(tk->hdr.hwndFrom, currentSel-1); TabCtrl_SetCurFocus(tk->hdr.hwndFrom, currentSel-1); } return TRUE; case VK_LEFT: //select the previous page { if(0==currentSel) return TRUE; TabCtrl_SetCurSel(tk->hdr.hwndFrom, currentSel-1); TabCtrl_SetCurFocus(tk->hdr.hwndFrom, currentSel); } return TRUE; case VK_PRIOR: //select the next page { TabCtrl_SetCurSel(tk->hdr.hwndFrom, currentSel+1); TabCtrl_SetCurFocus(tk->hdr.hwndFrom,currentSel+1); } return TRUE; case VK_RIGHT: //select the next page { TabCtrl_SetCurSel(tk->hdr.hwndFrom, currentSel+1); TabCtrl_SetCurFocus(tk->hdr.hwndFrom,currentSel); } return TRUE; case VK_UP: //navagate within selected child tab page { SetFocus(This->hTabPages[currentSel]); TabPageMessageLoop (This->hTabPages[currentSel]); } return TRUE; case VK_DOWN: //navagate within selected child tab page { SetFocus(This->hTabPages[currentSel]); TabPageMessageLoop (This->hTabPages[currentSel]); } return TRUE; default: return FALSE; } } //else // horizontal Tabs } /**************************************************************************** * * * Functions: CreateAccTable & TabPage_OnCommand * * * * Purpose : Get and handle selected key presses within the child tab pages.* * The WM_KEYUP/WM_KEYDOWN messages are not directly accessable * * in a dialog. The method for capturing desired keystrokes is * * to create an Accelerator Table of the desired keys and then * * supplying the table handle to the TranslateAccelerator macro * * of the Tab Page Message Loop. These key strokes are then * * handled in TabPage_OnCommand. * * * * History : Date Reason * * 00/00/00 Created * * * ****************************************************************************/ HACCEL CreateAccTable (void) { static ACCEL aAccel[4]; static HACCEL hAccel; int i; for(i=0;i<4;i++)aAccel[i].fVirt=FVIRTKEY; aAccel[0].key=VK_UP; aAccel[0].cmd=CMD_VK_UP; aAccel[1].key=VK_DOWN; aAccel[1].cmd=CMD_VK_DOWN; aAccel[2].key=VK_RIGHT; aAccel[2].cmd=CMD_VK_RIGHT; aAccel[3].key=VK_LEFT; aAccel[3].cmd=CMD_VK_LEFT; hAccel=CreateAcceleratorTable(aAccel,4); return hAccel; } void TabPage_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { // Handle the Dialog virtual keys // Forward the rest of the commands to ParentProc BOOL verticalTabs= (GetWindowLongPtr(This->hTab, GWL_STYLE) & TCS_VERTICAL); if(verticalTabs) { switch (id) { case CMD_VK_UP: //select the previous control SendMessage(hwnd, WM_NEXTDLGCTL, (WPARAM)0, FALSE); return; case CMD_VK_DOWN: //select the next control SendMessage(hwnd, WM_NEXTDLGCTL, (WPARAM)1, FALSE); return; case CMD_VK_RIGHT: { SetFocus(This->hTab); // focus to tab control stopTabPageMessageLoop=TRUE; // cause message loop to return } return; case CMD_VK_LEFT: { SetFocus(This->hTab); stopTabPageMessageLoop=TRUE; } return; } } else // horizontal Tabs { switch (id) { case CMD_VK_RIGHT: SendMessage(hwnd, WM_NEXTDLGCTL, (WPARAM)0, FALSE); return; case CMD_VK_LEFT: SendMessage(hwnd, WM_NEXTDLGCTL, (WPARAM)1, FALSE); return; case CMD_VK_UP: { SetFocus(This->hTab); stopTabPageMessageLoop=TRUE; } return; case CMD_VK_DOWN: { SetFocus(This->hTab); stopTabPageMessageLoop=TRUE; } return; } } //else // horizontal Tabs //Forward all other commands FORWARD_WM_COMMAND (hwnd,id,hwndCtl,codeNotify,This->ParentProc); // Mouse clicks on a control should engage the Message Loop // If This WM_COMMAND message is a notification to parent window // ie: EN_SETFOCUS being sent when an edit control is initialized // do not engage the Message Loop. if(codeNotify!=0) return; // Toggling WM_NEXTDLGCTL ensures that default focus moves to selected // Control SendMessage(hwnd, WM_NEXTDLGCTL, (WPARAM)0, FALSE); SendMessage(hwnd, WM_NEXTDLGCTL, (WPARAM)1, FALSE); ResetTabPageMessageLoop (hwnd); } void TabPage_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { // If Mouse click in tab page but not on control ResetTabPageMessageLoop (hwnd); } BOOL TabPage_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { // We handle This message so that it is not sent to the main dlg proc // each time a tab page is initialized. return TRUE; } BOOL CALLBACK TabPage_DlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG (hwndDlg, WM_INITDIALOG, TabPage_OnInitDialog); HANDLE_MSG (hwndDlg, WM_COMMAND, TabPage_OnCommand); HANDLE_MSG (hwndDlg, WM_LBUTTONDOWN, TabPage_OnLButtonDown); //// TODO: Add TabPage dialog message crackers here... default: return This->ParentProc (hwndDlg, msg, wParam, lParam); } } /**************************************************************************** * * * Function: Tab Page Message loop * * * * Purpose : Monitor and respond to user keyboard input and system messages * * Note: Send PostQuitMessage(0); from any cancel or exit event. * * Failure to do so will leave the process running even after * * application exit. * * * * History : Date Reason * * 00/00/00 Created * * * ****************************************************************************/ static void TabPageMessageLoop (HWND hwnd) { MSG msg; int status; BOOL handled = FALSE; // Create Accelerator table HACCEL hAccTable = CreateAccTable(); while((status = GetMessage(&msg, NULL, 0, 0 )) != 0 && !stopTabPageMessageLoop) { if (status == -1) // Exception { return; } else { // Dialogs do not have a WM_KEYDOWN message so we will seperate // the desired keyboard events here handled = TranslateAccelerator(hwnd,hAccTable,&msg); // Perform default dialog message processing using IsDialogM. . . if(!handled) handled=IsDialogMessage(hwnd,&msg); // Non dialog message handled in the standard way. if(!handled) { TranslateMessage(&msg); DispatchMessage(&msg); } } } if(stopTabPageMessageLoop) //Reset: do not PostQuitMessage(0) { DestroyAcceleratorTable(hAccTable); stopTabPageMessageLoop = FALSE; return; } // Default: Re-post the Quit message DestroyAcceleratorTable(hAccTable); PostQuitMessage(0); return; } static void ResetTabPageMessageLoop (HWND hwnd) { //Toggle kill sw stopTabPageMessageLoop=TRUE; stopTabPageMessageLoop=FALSE; TabPageMessageLoop(hwnd); } BOOL OnSelChanged(void) { if (This->hTabPages) { /* A tab has been pressed (TCN_SELCHANGE) Using GWL_USERDATA of the tab control to keep the current visible child of the tab control */ HWND hVisible= (HWND)GetWindowLongPtr(This->hTab, GWLP_USERDATA); int iSel= TabCtrl_GetCurSel(This->hTab); // Hide the current child dialog box, if any. ShowWindow(hVisible, FALSE); // Show the new child dialog box. ShowWindow(This->hTabPages[iSel], TRUE); // Save the current child SetWindowLongPtr(This->hTab, GWLP_USERDATA, (LONG_PTR)This->hTabPages[iSel]); } return TRUE; } void TabControl_Select(LPTABCTRL tc) { // When coding more than one tab control use This function to update "This" pointer This=tc; } void TabControl_Destroy(LPTABCTRL tc) { ////////////////////////////////////// // Destroy the tab page dialogs and // free the list of pointers to the dialogs for (int i=0;itabPageCount;i++) DestroyWindow(tc->hTabPages[i]); SendMessage(tc->hTab, WM_QUIT, 0, 0); stopTabPageMessageLoop = TRUE; free (tc->hTabPages); tc->hTabPages = 0; tc->tabPageCount = 0; } extern HINSTANCE ghInstance; void New_TabControl(LPTABCTRL tc, HWND hTab, PWSTR *tabNames, PWSTR *dlgNames, BOOL (*ParentProc)(HWND, UINT, WPARAM, LPARAM), VOID (*TabPage_OnSize)(HWND, UINT, int, int), BOOL fStretch) { static TCITEM tie; This=tc; This->hTab=hTab; This->tabNames=tabNames; This->dlgNames=dlgNames; This->blStretchTabs=fStretch; // Point to external functions This->ParentProc=ParentProc; // Point to internal public functions This->OnKeyDown=&OnKeyDown; This->OnSelChanged=&OnSelChanged; This->StretchTabPage=&StretchTabPage; This->CenterTabPage=&CenterTabPage; // Determine number of tab pages to insert based on DlgNames This->tabPageCount = 0; PWSTR* ptr=This->tabNames; while(*ptr++) This->tabPageCount++; //create array based on number of pages This->hTabPages = (HWND*)malloc(This->tabPageCount * sizeof(HWND*)); // Add a tab for each name in tabnames (list ends with 0) tie.mask = TCIF_TEXT | TCIF_IMAGE; tie.iImage = -1; for (int i = 0; i< This->tabPageCount; i++) { tie.pszText = This->tabNames[i]; TabCtrl_InsertItem(This->hTab, i, &tie); // Add page to each tab This->hTabPages[i] = CreateDialog(ghInstance, This->dlgNames[i], GetParent(This->hTab), (DLGPROC)TabPage_DlgProc); // Set initial tab page position if(This->blStretchTabs) This->StretchTabPage(This->hTabPages[i]); else This->CenterTabPage(This->hTabPages[i]); } // Show first tab ShowWindow(This->hTabPages[0],SW_SHOW); // Save the current child SetWindowLongPtr(This->hTab, GWLP_USERDATA, (LONG_PTR)This->hTabPages[0]); } mysql-connector-odbc-5.1.10-src/setupgui/windows/odbcdialogparams.cpp100644 15766 12 60615 11707541005 24554 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file odbcdialogparams.cpp @brief Defines the entry point for the DLL application. */ #define WIN32_LEAN_AND_MEAN #define CONNECTION_TAB 1 #define METADATA_TAB 2 #define CURSORS_TAB 3 #define DEBUG_TAB 4 #define SSL_TAB 5 #define MISC_TAB 6 #include #include #include #include #include "resource.h" #include "TabCtrl.h" #include #include #include #include #include #include #include "../setupgui.h" #include "odbcdialogparams.h" #include "stringutil.h" extern HINSTANCE ghInstance; static DataSource* pParams= NULL; static PWCHAR pCaption= NULL; static int OkPressed= 0; static int mod= 1; static bool flag= false; static bool BusyIndicator= false; static TABCTRL TabCtrl_1; /* Whether we are in SQLDriverConnect() prompt mode (used to disable fields) */ static BOOL g_isPrompt; /* Variable to keep IDC of control where default value were put. It's reset if user changes value. Used to verify if we can reset that control's value. It won't work if for more than 1 field, but we have only one in visible future. */ static long controlWithDefValue= 0; HelpButtonPressedCallbackType* gHelpButtonPressedCallback = NULL; /* Function that enables/disables groups of controls */ #define SWITCHED_GROUPS 2 #define MAX_GROUP_COTROLS 2 void SwitchTcpOrPipe(HWND hwnd, BOOL usePipe) { /* groups of fields to enable/disable*/ const int switchedFields[SWITCHED_GROUPS][MAX_GROUP_COTROLS]= {{IDC_EDIT_server, IDC_EDIT_port}, {IDC_EDIT_socket, 0}}; /* Default value for enabled empty field */ const LPCWSTR defaultValues[SWITCHED_GROUPS][MAX_GROUP_COTROLS]= {{NULL, NULL}, {L"MySQL", NULL}}; /* Can't be sure that usePipe contains 1 as TRUE*/ long activeIndex= usePipe ? 1L : 0L; for (long i= 0; i < SWITCHED_GROUPS; ++i) for (long j= 0; j < MAX_GROUP_COTROLS && switchedFields[i][j] != 0; ++j) { HWND control= GetDlgItem(hwnd, switchedFields[i][j]); EnableWindow(control, i == activeIndex); if (defaultValues[i][j] != NULL) { if (i == activeIndex) { if (Edit_GetTextLength(control) == 0) { /* remember that we set that value */ Edit_SetText(control, defaultValues[i][j]); controlWithDefValue= switchedFields[i][j]; } } else if (controlWithDefValue == switchedFields[i][j]) { /* we don't want to store the value we set instead of user */ Edit_SetText(control,L""); } } } } #undef SWITCHED_GROUPS #undef MAX_GROUP_COTROLS void InitStaticValues() { BusyIndicator= true; pParams = NULL; pCaption = NULL; OkPressed = 0; mod = 1; flag = false; BusyIndicator= false; gHelpButtonPressedCallback= NULL; } #define Refresh(A) RedrawWindow(A,NULL,NULL,RDW_ERASE|RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_UPDATENOW); BOOL FormMain_DlgProc (HWND, UINT, WPARAM, LPARAM); void DoEvents (void) { MSG Msg; while (PeekMessage(&Msg,NULL,0,0,PM_REMOVE)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } } VOID OnWMNotify(WPARAM wParam, LPARAM lParam); static BOOL FormMain_OnNotify (HWND hwnd, WPARAM wParam, LPARAM lParam) { OnWMNotify(wParam, lParam); int id = (int)wParam; switch(id) { case IDC_TAB1: { TabControl_Select(&TabCtrl_1); //update internal "this" pointer LPNMHDR nm = (LPNMHDR)lParam; switch (nm->code) { case TCN_KEYDOWN: TabCtrl_1.OnKeyDown(lParam); case TCN_SELCHANGE: TabCtrl_1.OnSelChanged(); } } break; } return FALSE; } void getStrFieldData(HWND hwnd, SQLWCHAR **param, int idc) { x_free(*param); *param= NULL; int len = Edit_GetTextLength(GetDlgItem(hwnd,idc)); if (len>0) { *param= (SQLWCHAR *)my_malloc((len + 1) * sizeof(SQLWCHAR), MYF(0)); if (*param) Edit_GetText(GetDlgItem(hwnd,idc), *param, len+1); } } void getStrFieldData(SQLWCHAR **param, unsigned int framenum, int idc ) { assert(TabCtrl_1.hTabPages); HWND tab = TabCtrl_1.hTabPages[framenum-1]; assert(tab); getStrFieldData(tab, param, idc ); } void setUnsignedFieldData(HWND hwnd, unsigned int & param, int idc ) { wchar_t buf[20]; _itow( param, (wchar_t*)buf, 10 ); Edit_SetText(GetDlgItem(hwnd,idc), buf); } void getUnsignedFieldData( HWND hwnd, unsigned int & param, int idc ) { param = 0U; int len = Edit_GetTextLength(GetDlgItem(hwnd,idc)); if(len>0) { SQLWCHAR *tmp1= (SQLWCHAR *)my_malloc((len + 1) * sizeof(SQLWCHAR), MYF(0)); if (tmp1) { Edit_GetText(GetDlgItem(hwnd,idc), tmp1, len+1); param = _wtol(tmp1); x_free(tmp1); } } } bool getBoolFieldData(unsigned int framenum, int idc) { assert(TabCtrl_1.hTabPages); HWND checkbox = GetDlgItem(TabCtrl_1.hTabPages[framenum-1], idc); assert(checkbox); if (checkbox) return !!Button_GetCheck(checkbox); return false; } #define GET_STRING(name) getStrFieldData(hwnd,¶ms.name,IDC_EDIT_##name) #define SET_STRING(name) \ Edit_SetText(GetDlgItem(hwnd,IDC_EDIT_##name), params.name) #define SET_CSTRING(name) \ ComboBox_SetText(GetDlgItem(hwnd,IDC_EDIT_##name), params.name) #define GET_UNSIGNED(name) getUnsignedFieldData(hwnd,params.name,IDC_EDIT_##name) #define SET_UNSIGNED(name) setUnsignedFieldData(hwnd,params.name,IDC_EDIT_##name) #define GET_BOOL(framenum,name) \ params.name = getBoolFieldData(framenum,IDC_CHECK_##name) #define SET_BOOL(framenum,name) \ Button_SetCheck(GetDlgItem(TabCtrl_1.hTabPages[framenum-1],IDC_CHECK_##name), params.name) void syncData(HWND hwnd, DataSource ¶ms) { GET_STRING(name); GET_STRING(description); GET_STRING(server); GET_UNSIGNED(port); GET_STRING(uid); GET_STRING(pwd); GET_STRING(database); GET_STRING(socket); params.force_use_of_named_pipes = !!Button_GetCheck(GetDlgItem(hwnd, IDC_RADIO_pipe)); } void syncForm(HWND hwnd, DataSource ¶ms) { SET_STRING(name); SET_STRING(description); SET_STRING(server); SET_UNSIGNED(port); SET_STRING(uid); SET_STRING(pwd); SET_STRING(database); SET_STRING(socket); if (params.force_use_of_named_pipes) Button_SetCheck(GetDlgItem(hwnd,IDC_RADIO_pipe), TRUE); else Button_SetCheck(GetDlgItem(hwnd,IDC_RADIO_tcp), TRUE); SwitchTcpOrPipe(hwnd, pParams->force_use_of_named_pipes); } void syncTabsData(HWND hwnd, DataSource ¶ms) { /* 1 - Connection */ GET_BOOL(CONNECTION_TAB, allow_big_results); GET_BOOL(CONNECTION_TAB, use_compressed_protocol); GET_BOOL(CONNECTION_TAB, dont_prompt_upon_connect); GET_BOOL(CONNECTION_TAB, auto_reconnect); // GET_BOOL(CONNECTION_TAB, force_use_of_named_pipes); GET_BOOL(CONNECTION_TAB, allow_multiple_statements); GET_BOOL(CONNECTION_TAB, clientinteractive); getStrFieldData(¶ms.charset , CONNECTION_TAB, IDC_EDIT_charset); getStrFieldData(¶ms.initstmt, CONNECTION_TAB, IDC_EDIT_initstmt); /* 2 - Metadata*/ GET_BOOL(METADATA_TAB, change_bigint_columns_to_int); GET_BOOL(METADATA_TAB, handle_binary_as_char); GET_BOOL(METADATA_TAB, return_table_names_for_SqlDescribeCol); GET_BOOL(METADATA_TAB, ignore_N_in_name_table); GET_BOOL(METADATA_TAB, no_catalog); GET_BOOL(METADATA_TAB, limit_column_size); GET_BOOL(METADATA_TAB, no_information_schema); /* 3 - Cursors/Results */ GET_BOOL(CURSORS_TAB, return_matching_rows); GET_BOOL(CURSORS_TAB, auto_increment_null_search); GET_BOOL(CURSORS_TAB, dynamic_cursor); GET_BOOL(CURSORS_TAB, user_manager_cursor); GET_BOOL(CURSORS_TAB, pad_char_to_full_length); GET_BOOL(CURSORS_TAB, dont_cache_result); GET_BOOL(CURSORS_TAB, force_use_of_forward_only_cursors); GET_BOOL(CURSORS_TAB, zero_date_to_min); /* 4 - debug*/ GET_BOOL(DEBUG_TAB,save_queries); /* 5 - ssl related */ getStrFieldData(¶ms.sslkey , SSL_TAB, IDC_EDIT_sslkey); getStrFieldData(¶ms.sslcert , SSL_TAB, IDC_EDIT_sslcert); getStrFieldData(¶ms.sslca , SSL_TAB, IDC_EDIT_sslca); getStrFieldData(¶ms.sslcapath, SSL_TAB, IDC_EDIT_sslcapath); getStrFieldData(¶ms.sslcipher, SSL_TAB, IDC_EDIT_sslcipher); GET_BOOL(SSL_TAB,sslverify); /* 6 - Misc*/ GET_BOOL(MISC_TAB, safe); GET_BOOL(MISC_TAB, dont_use_set_locale); GET_BOOL(MISC_TAB, ignore_space_after_function_names); GET_BOOL(MISC_TAB, read_options_from_mycnf); GET_BOOL(MISC_TAB, disable_transactions); GET_BOOL(MISC_TAB, min_date_to_zero); } void syncTabs(HWND hwnd, DataSource ¶ms) { /* 1 - Connection */ SET_BOOL(CONNECTION_TAB, allow_big_results); SET_BOOL(CONNECTION_TAB, use_compressed_protocol); SET_BOOL(CONNECTION_TAB, dont_prompt_upon_connect); SET_BOOL(CONNECTION_TAB, auto_reconnect); // SET_BOOL(CONNECTION_TAB, force_use_of_named_pipes); SET_BOOL(CONNECTION_TAB, allow_multiple_statements); SET_BOOL(CONNECTION_TAB, clientinteractive); if ( TabCtrl_1.hTabPages[CONNECTION_TAB-1]) { HWND tabHwndMisc = TabCtrl_1.hTabPages[CONNECTION_TAB-1]; HWND charsetCtrl = GetDlgItem(tabHwndMisc,IDC_EDIT_charset); ComboBox_SetText(charsetCtrl, params.charset); Edit_SetText( GetDlgItem( tabHwndMisc, IDC_EDIT_initstmt), params.initstmt); } /* 2 - Metadata*/ SET_BOOL(METADATA_TAB, change_bigint_columns_to_int); SET_BOOL(METADATA_TAB, handle_binary_as_char); SET_BOOL(METADATA_TAB, return_table_names_for_SqlDescribeCol); SET_BOOL(METADATA_TAB, ignore_N_in_name_table); SET_BOOL(METADATA_TAB, no_catalog); SET_BOOL(METADATA_TAB, limit_column_size); SET_BOOL(METADATA_TAB, no_information_schema); /* 3 - Cursors/Results */ SET_BOOL(CURSORS_TAB, return_matching_rows); SET_BOOL(CURSORS_TAB, auto_increment_null_search); SET_BOOL(CURSORS_TAB, dynamic_cursor); SET_BOOL(CURSORS_TAB, user_manager_cursor); SET_BOOL(CURSORS_TAB, pad_char_to_full_length); SET_BOOL(CURSORS_TAB, dont_cache_result); SET_BOOL(CURSORS_TAB, force_use_of_forward_only_cursors); SET_BOOL(CURSORS_TAB, zero_date_to_min); /* 4 - debug*/ SET_BOOL(DEBUG_TAB,save_queries); /* 5 - ssl related */ if ( TabCtrl_1.hTabPages[SSL_TAB-1]) { HWND tabHwnd = TabCtrl_1.hTabPages[SSL_TAB-1]; Edit_SetText( GetDlgItem( tabHwnd, IDC_EDIT_sslkey) , params.sslkey); Edit_SetText( GetDlgItem( tabHwnd, IDC_EDIT_sslcert) , params.sslcert); Edit_SetText( GetDlgItem( tabHwnd, IDC_EDIT_sslca) , params.sslca); Edit_SetText( GetDlgItem( tabHwnd, IDC_EDIT_sslcapath) , params.sslcapath); Edit_SetText( GetDlgItem( tabHwnd, IDC_EDIT_sslcipher) , params.sslcipher); SET_BOOL(SSL_TAB, sslverify); } /* 6 - Misc*/ SET_BOOL(MISC_TAB, safe); SET_BOOL(MISC_TAB, dont_use_set_locale); SET_BOOL(MISC_TAB, ignore_space_after_function_names); SET_BOOL(MISC_TAB, read_options_from_mycnf); SET_BOOL(MISC_TAB, disable_transactions); SET_BOOL(MISC_TAB, min_date_to_zero); } void FillParameters(HWND hwnd, DataSource & params) { syncData(hwnd, params ); if( TabCtrl_1.hTab ) syncTabsData(hwnd, params); } void OnDialogClose(); void FormMain_OnClose(HWND hwnd) { //PostQuitMessage(0);// turn off message loop //Unhooks hook(s) :) OnDialogClose(); TabControl_Destroy(&TabCtrl_1); EndDialog(hwnd, NULL); } /**************************************************************************** * * * Functions: FormMain_OnCommand related event code * * * * Purpose : Handle WM_COMMAND messages: this is the heart of the app. * * * * History : Date Reason * * 00/00/00 Created * * * ****************************************************************************/ void btnDetails_Click (HWND hwnd) { RECT rect; GetWindowRect( hwnd, &rect ); mod *= -1; ShowWindow( GetDlgItem(hwnd,IDC_TAB1), mod > 0? SW_SHOW: SW_HIDE ); if(!flag && mod==1) { static PWSTR tabnames[]= {L"Connection", L"Metadata", L"Cursors/Results", L"Debug", L"SSL", L"Misc", 0}; static PWSTR dlgnames[]= {MAKEINTRESOURCE(IDD_TAB1), MAKEINTRESOURCE(IDD_TAB2), MAKEINTRESOURCE(IDD_TAB3), MAKEINTRESOURCE(IDD_TAB4), MAKEINTRESOURCE(IDD_TAB5), MAKEINTRESOURCE(IDD_TAB6),0}; New_TabControl( &TabCtrl_1, // address of TabControl struct GetDlgItem(hwnd, IDC_TAB1), // handle to tab control tabnames, // text for each tab dlgnames, // dialog id's of each tab page dialog &FormMain_DlgProc, // address of main windows proc NULL, // address of size function TRUE); // stretch tab page to fit tab ctrl flag = true; syncTabs(hwnd, *pParams); } MoveWindow( hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top + 280*mod, TRUE ); } void btnOk_Click (HWND hwnd) { FillParameters(hwnd, *pParams); /* if DS params are valid, close dialog */ if (mytestaccept(hwnd, pParams)) { OkPressed= 1; PostMessage(hwnd, WM_CLOSE, NULL, NULL); } } void btnCancel_Click (HWND hwnd) { PostMessage(hwnd, WM_CLOSE, NULL, NULL); } void btnTest_Click (HWND hwnd) { FillParameters(hwnd, *pParams); wchar_t *testResultMsg= mytest(hwnd, pParams); MessageBoxW(hwnd, testResultMsg, L"Test Result", MB_OK); x_free(testResultMsg); } void btnHelp_Click (HWND hwnd) { ShellExecute(NULL, L"open", L"http://dev.mysql.com/doc/refman/5.1/en/connector-odbc-configuration-dsn-windows.html", NULL, NULL, SW_SHOWNORMAL); } void chooseFile( HWND parent, int hostCtlId ) { OPENFILENAMEW dialog; HWND hostControl = GetDlgItem( parent, hostCtlId ); wchar_t szFile[MAX_PATH]; // buffer for file name Edit_GetText( hostControl, szFile, sizeof(szFile) ); // Initialize OPENFILENAME ZeroMemory(&dialog, sizeof(dialog)); dialog.lStructSize = sizeof(dialog); dialog.lpstrFile = szFile; dialog.lpstrTitle = L"Select File"; dialog.nMaxFile = sizeof(szFile); dialog.lpstrFileTitle = NULL; dialog.nMaxFileTitle = 0; dialog.lpstrInitialDir = NULL; dialog.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST ; dialog.hwndOwner = parent; dialog.lpstrCustomFilter = L"All Files\0*.*\0PEM\0*.pem\0"; dialog.nFilterIndex = 2; if ( GetOpenFileNameW( &dialog ) ) { Edit_SetText( hostControl, dialog.lpstrFile ); } } void choosePath( HWND parent, int hostCtlId ) { HWND hostControl = GetDlgItem( parent, hostCtlId ); BROWSEINFOW dialog; wchar_t path[MAX_PATH]; // buffer for file name Edit_GetText( hostControl, path, sizeof(path) ); ZeroMemory(&dialog,sizeof(dialog)); dialog.lpszTitle = L"Pick a CA Path"; dialog.hwndOwner = parent; dialog.pszDisplayName = path; LPITEMIDLIST pidl = SHBrowseForFolder ( &dialog ); if ( pidl ) { SHGetPathFromIDList ( pidl, path ); Edit_SetText( hostControl, path ); IMalloc * imalloc = 0; if ( SUCCEEDED( SHGetMalloc ( &imalloc )) ) { imalloc->Free ( pidl ); imalloc->Release ( ); } } } #ifndef MAX_VISIBLE_CB_ITEMS #define MAX_VISIBLE_CB_ITEMS 20 #endif /** Adjusting height of dropped list of cbHwnd combobox to fit itemsCount items, but not more than MAX_VISIBLE_CB_ITEMS ComboBox_SetMinVisible not used because it was introduced in XP. */ int adjustDropdownHeight(HWND cbHwnd, unsigned int itemsCount) { COMBOBOXINFO dbcbinfo; RECT ddRect; int newHeight = 0; dbcbinfo.cbSize= sizeof(COMBOBOXINFO); ComboBox_GetDroppedControlRect(cbHwnd, &ddRect); newHeight= ddRect.bottom - ddRect.top; if ( GetComboBoxInfo(cbHwnd, &dbcbinfo) ) { itemsCount= itemsCount < 1 ? 1 : (itemsCount > MAX_VISIBLE_CB_ITEMS ? MAX_VISIBLE_CB_ITEMS : itemsCount ); /* + (itemsCount - 1) - 1 pixel spaces between list items */ newHeight= itemsCount*ComboBox_GetItemHeight(cbHwnd) + (itemsCount - 1); MoveWindow(dbcbinfo.hwndList, ddRect.left, ddRect.top, ddRect.right-ddRect.left, newHeight, FALSE); } return newHeight; } /** Processing commands for dbname combobox (hwndCtl). */ void processDbCombobox(HWND hwnd, HWND hwndCtl, UINT codeNotify) { switch(codeNotify) { /* Loading list and adjust its height if button clicked and on user input */ case(CBN_DROPDOWN): { FillParameters(hwnd, *pParams); LIST *dbs= mygetdatabases(hwnd, pParams); LIST *dbtmp= dbs; ComboBox_ResetContent(hwndCtl); adjustDropdownHeight(hwndCtl,list_length(dbs)); for (; dbtmp; dbtmp= list_rest(dbtmp)) ComboBox_AddString(hwndCtl, (SQLWCHAR *)dbtmp->data); list_free(dbs, 1); ComboBox_SetText(hwndCtl,pParams->database); break; } } } /** Processing commands for charset combobox (hwndCtl). */ void processCharsetCombobox(HWND hwnd, HWND hwndCtl, UINT codeNotify) { switch(codeNotify) { /* Loading list and adjust its height if button clicked and on user input */ case(CBN_DROPDOWN): { //FillParameters(hwnd, *pParams); LIST *csl= mygetcharsets(hwnd, pParams); LIST *cstmp= csl; ComboBox_ResetContent(hwndCtl); adjustDropdownHeight(hwndCtl,list_length(csl)); for (; cstmp; cstmp= list_rest(cstmp)) ComboBox_AddString(hwndCtl, (SQLWCHAR *)cstmp->data); list_free(csl, 1); ComboBox_SetText(hwndCtl,pParams->charset); break; } } } void FormMain_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { if (controlWithDefValue != 0 && id == controlWithDefValue && codeNotify==EN_CHANGE) controlWithDefValue= 0; switch (id) { case IDOK: btnOk_Click(hwnd); break; case IDCANCEL: btnCancel_Click(hwnd); break; case IDC_BUTTON_DETAILS: btnDetails_Click(hwnd); break; case IDC_BUTTON_HELP: btnHelp_Click(hwnd); break; case IDC_BUTTON_TEST: btnTest_Click(hwnd); break; case IDC_SSLKEYCHOOSER: chooseFile(hwnd, IDC_EDIT_sslkey); break; case IDC_SSLCERTCHOOSER: chooseFile(hwnd, IDC_EDIT_sslcert); break; case IDC_SSLCACHOOSER: chooseFile(hwnd, IDC_EDIT_sslca); break; case IDC_SSLCAPATHCHOOSER: choosePath(hwnd, IDC_EDIT_sslcapath); break; case IDC_RADIO_tcp: case IDC_RADIO_pipe: SwitchTcpOrPipe(hwnd, !!Button_GetCheck(GetDlgItem(hwnd, IDC_RADIO_pipe))); break; case IDC_EDIT_name: { if (codeNotify==EN_CHANGE) { int len = Edit_GetTextLength(GetDlgItem(hwnd,IDC_EDIT_name)); Button_Enable(GetDlgItem(hwnd,IDOK), len > 0); Button_Enable(GetDlgItem(hwnd,IDC_BUTTON_TEST), len > 0); RedrawWindow(hwnd,NULL,NULL,RDW_INVALIDATE); } break; } case IDC_EDIT_dbname: processDbCombobox(hwnd, hwndCtl, codeNotify); break; case IDC_EDIT_charset: processCharsetCombobox(hwnd, hwndCtl, codeNotify); } return; } void AlignWindowToBottom(HWND hwnd, int dY); void AdjustLayout(HWND hwnd); void FormMain_OnSize(HWND hwnd, UINT state, int cx, int cy) { AdjustLayout(hwnd); } void AdjustLayout(HWND hwnd) { RECT rc; GetClientRect(hwnd,&rc); BOOL Visible = (mod==-1)?0:1; if(TabCtrl_1.hTab) { EnableWindow( TabCtrl_1.hTab, Visible ); ShowWindow( TabCtrl_1.hTab, Visible ); } PWSTR pButtonCaption = Visible? L"Details <<" : L"Details >>"; SetWindowText( GetDlgItem(hwnd,IDC_BUTTON_DETAILS), pButtonCaption ); const int dY = 20; AlignWindowToBottom( GetDlgItem(hwnd,IDC_BUTTON_DETAILS), dY); AlignWindowToBottom( GetDlgItem(hwnd,IDOK), dY); AlignWindowToBottom( GetDlgItem(hwnd,IDCANCEL), dY); AlignWindowToBottom( GetDlgItem(hwnd,IDC_BUTTON_HELP), dY); Refresh(hwnd); } void AlignWindowToBottom(HWND hwnd, int dY) { if(!hwnd) return; RECT rect; GetWindowRect( hwnd, &rect ); int h, w; RECT rc; GetWindowRect(GetParent(hwnd), &rc); h=rect.bottom-rect.top; w=rect.right-rect.left; rc.top = rc.bottom; MapWindowPoints(HWND_DESKTOP, GetParent(hwnd), (LPPOINT)&rect, 2); MapWindowPoints(HWND_DESKTOP, GetParent(hwnd), (LPPOINT)&rc, 2); MoveWindow(hwnd, rect.left, rc.top -dY-h,w,h,FALSE); } HWND g_hwndDlg; BOOL DoCreateDialogTooltip(void); BOOL FormMain_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { g_hwndDlg = hwnd; SetWindowText(hwnd, pCaption); //----Everything else must follow the above----// btnDetails_Click(hwnd); AdjustLayout(hwnd); //Get the initial Width and height of the dialog //in order to fix the minimum size of dialog syncForm(hwnd,*pParams); /* Disable fields if in prompt mode */ if (g_isPrompt) { EnableWindow(GetDlgItem(hwnd, IDC_EDIT_name), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_EDIT_description), FALSE); } /* if prompting without DSN, don't disable OK button */ /* preserved here old logic + enabled OK if data source name is not NULL when not prompting. I don't know why it should be disabled when prompting and name is not NULL. */ if (g_isPrompt == (pParams->name==NULL)) { Button_Enable(GetDlgItem(hwnd,IDOK), 1); Button_Enable(GetDlgItem(hwnd,IDC_BUTTON_TEST), 1); RedrawWindow(hwnd,NULL,NULL,RDW_INVALIDATE); } BOOL b = DoCreateDialogTooltip(); return 0; } BOOL FormMain_DlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG (hwndDlg, WM_CLOSE, FormMain_OnClose); HANDLE_MSG (hwndDlg, WM_COMMAND, FormMain_OnCommand); HANDLE_MSG (hwndDlg, WM_INITDIALOG, FormMain_OnInitDialog); HANDLE_MSG (hwndDlg, WM_SIZE, FormMain_OnSize); // There is no message cracker for WM_NOTIFY so redirect manually case WM_NOTIFY: return FormMain_OnNotify (hwndDlg,wParam,lParam); default: return FALSE; } } /* Display the DSN dialog @param params DataSource struct, should be pre-populated @param ParentWnd Parent window handle @return 1 if the params were correctly populated and OK was pressed 0 if the dialog was closed or cancelled */ extern "C" int ShowOdbcParamsDialog(DataSource* params, HWND ParentWnd, BOOL isPrompt) { assert(!BusyIndicator); InitStaticValues(); pParams= params; pCaption= L"MySQL Connector/ODBC Data Source Configuration"; g_isPrompt= isPrompt; /* If prompting (with a DSN name), or not prompting (add/edit DSN), we translate the lib path to the actual driver name. */ if (params->name || !isPrompt) { Driver *driver= driver_new(); params->driver && memcpy(driver->lib, params->driver, (sqlwcharlen(params->driver) + 1) * sizeof(SQLWCHAR)); /* TODO Driver lookup is done in driver too, do we really need it there? */ if (!*driver->lib || driver_lookup_name(driver)) { wchar_t msg[256]; swprintf(msg, 256, L"Failure to lookup driver entry at path '%ls'", driver->lib); MessageBox(ParentWnd, msg, L"Cannot find driver entry", MB_OK); driver_delete(driver); return 0; } ds_set_strattr(¶ms->driver, driver->name); driver_delete(driver); } DialogBox(ghInstance, MAKEINTRESOURCE(IDD_DIALOG1), ParentWnd, (DLGPROC)FormMain_DlgProc); BusyIndicator= false; return OkPressed; } mysql-connector-odbc-5.1.10-src/setupgui/windows/resource.h100644 15766 12 14012 11707541005 22543 0ustar00cteamstaff///////////////////////////////////////////////////////////////////////////// // Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. // // The MySQL Connector/ODBC is licensed under the terms of the GPLv2 // , like most // MySQL Connectors. There are special exceptions to the terms and // conditions of the GPLv2 as it is applied to this software, see the // FLOSS License Exception // . // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by odbcdialogparams.rc // #define IDR_RT_MANIFEST1 1 #define IDC_MYICON 2 #define IDCANCEL2 3 #define IDHELP2 11 #define IDD_DIALOG_DIALOG 102 #define IDS_APP_TITLE 103 #define IDD_ABOUTBOX 103 #define IDM_ABOUT 104 #define IDM_EXIT 105 #define IDI_DIALOG 107 #define IDI_SMALL 108 #define IDC_DIALOG 109 #define IDR_MAINFRAME 128 #define IDD_DIALOG1 129 #define IDB_LOGO 130 #define IDD_TAB1 131 #define IDD_TAB2 132 #define IDD_TAB3 133 #define IDD_TAB4 134 #define IDD_TAB5 136 #define IDD_TAB6 137 #define IDC_LOGO 1000 #define IDC_EDIT_DRVNAME 1004 #define IDC_EDIT_DRVDESC 1005 #define IDC_EDIT_SRVNAME 1007 #define IDC_EDIT_PORT 1008 #define IDC_EDIT_USERNAME 1009 #define IDC_EDIT 1010 #define IDC_EDIT_PASSWORD 1010 #define IDC_EDIT_DBNAME 1011 #define IDC_BUTTON_DETAILS 1012 #define IDC_TAB1 1013 #define IDC_COMBO1 1022 #define IDC_SSLKEYCHOOSER 1023 #define IDC_SSLCERTCHOOSER 1025 #define IDC_SSLCACHOOSER 1026 #define IDC_SSLCAPATHCHOOSER 1027 #define IDC_CHECK_sslverify 1028 #define IDC_CHECK_min_date_to_zero 1029 #define IDC_EDIT_drvname 10000 #define IDC_EDIT_name 10000 #define IDC_EDIT_drvdesc 10001 #define IDC_EDIT_description 10001 #define IDC_EDIT_srvname 10002 #define IDC_EDIT_server 10002 #define IDC_EDIT_port 10003 #define IDC_EDIT_username 10004 #define IDC_EDIT_uid 10004 #define IDC_EDIT_password 10005 #define IDC_EDIT_pwd 10005 #define IDC_EDIT_dbname 10006 #define IDC_EDIT_database 10006 #define IDC_CHECK_dont_optimize_column_width 10007 #define IDC_CHECK_return_matching_rows 10008 #define IDC_CHECK_allow_big_results 10009 #define IDC_CHECK_use_compressed_protocol 10010 #define IDC_CHECK_change_bigint_columns_to_int 10011 #define IDC_CHECK_safe 10012 #define IDC_CHECK_auto_reconnect 10013 #define IDC_CHECK_auto_increment_null_search 10014 #define IDC_CHECK_dont_prompt_upon_connect 10015 #define IDC_CHECK_dynamic_cursor 10016 #define IDC_CHECK_ignore_N_in_name_table 10017 #define IDC_CHECK_user_manager_cursor 10018 #define IDC_CHECK_dont_use_set_locale 10019 #define IDC_CHECK_pad_char_to_full_length 10020 #define IDC_CHECK_dont_cache_result 10021 #define IDC_CHECK_return_table_names_for_SqlDescribeCol 10022 #define IDC_CHECK_pad_char_to_full_length2 10022 #define IDC_CHECK_zero_date_to_min 10022 #define IDC_CHECK_ignore_space_after_function_names 10023 //#define IDC_CHECK_force_use_of_named_pipes 10024 #define IDC_CHECK_no_catalog 10025 #define IDC_CHECK_read_options_from_mycnf 10026 #define IDC_CHECK_disable_transactions 10027 #define IDC_CHECK_force_use_of_forward_only_cursors 10028 #define IDC_CHECK_allow_multiple_statements 10029 #define IDC_CHECK_limit_column_size 10030 #define IDC_EDIT_sslca 10031 #define IDC_EDIT_sslcapath 10032 #define IDC_EDIT_sslcert 10033 #define IDC_EDIT_sslkey 10034 #define IDC_EDIT_sslcipher 10035 #define IDC_CHECK_handle_binary_as_char 10036 #define IDC_CHECK_save_queries 10037 #define IDC_EDIT_charset 10038 #define IDC_EDIT_initstmt 10039 #define IDC_CHECK_clientinteractive 10040 #define IDC_CHECK_no_information_schema 10041 #define IDC_EDIT_socket 10042 #define IDC_RADIO_tcp 10043 #define IDC_RADIO_pipe 10044 #define IDC_BUTTON_TEST 11014 #define IDC_BUTTON_HELP 11015 #define IDC_STATIC -1 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 137 #define _APS_NEXT_COMMAND_VALUE 32771 #define _APS_NEXT_CONTROL_VALUE 1030 #define _APS_NEXT_SYMED_VALUE 110 #endif #endif mysql-connector-odbc-5.1.10-src/setupgui/windows/main.cpp100644 15766 12 4245 11707541005 22162 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "resource.h" #include #include #include #include #include "MYODBC_MYSQL.h" #ifdef _MANAGED #pragma managed(push, off) #endif HINSTANCE ghInstance; const LPCWSTR className = L"MySQLSetupLib"; BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { if ( ul_reason_for_call == DLL_PROCESS_ATTACH ) { my_init(); InitCommonControls(); ghInstance = hModule; WNDCLASSEX wcx; // Get system dialog information. wcx.cbSize = sizeof(wcx); if (!GetClassInfoEx(NULL, MAKEINTRESOURCE(32770), &wcx)) return 0; //FindResource(hModule, RT_DIALOG ); // Add our own stuff. wcx.hInstance = hModule; // wcx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_ICO_MAIN)); wcx.lpszClassName = className; if (!RegisterClassEx(&wcx) ) return 0; } else if ( ul_reason_for_call == DLL_PROCESS_DETACH ) { my_end(0); UnregisterClass(className,ghInstance); } return TRUE; } #ifdef _MANAGED #pragma managed(pop) #endif mysql-connector-odbc-5.1.10-src/setupgui/windows/TabCtrl.h100644 15766 12 4446 11707541005 22241 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file TabCtrl.h @brief Tab Control Enhanced. Make Creating and modifying Tab Control Property pages a snap. (c) 2006 David MacDermot This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /** Structs *****************************************************************/ typedef struct TabControl { HWND hTab; HWND* hTabPages; PWSTR *tabNames; PWSTR *dlgNames; int tabPageCount; BOOL blStretchTabs; // Function pointer to Parent Dialog Proc BOOL (*ParentProc)(HWND, UINT, WPARAM, LPARAM); // Pointers to shared functions BOOL (*OnSelChanged)(VOID); BOOL (*OnKeyDown)(LPARAM); BOOL (*StretchTabPage) (HWND); BOOL (*CenterTabPage) (HWND); } TABCTRL, *LPTABCTRL; /** Prototypes **************************************************************/ //typedef BOOL CALLBACK (ParentProcType)(HWND, UINT, WPARAM, LPARAM); void New_TabControl(LPTABCTRL, HWND, PWSTR*, PWSTR*, BOOL (*ParentProc)(HWND, UINT, WPARAM, LPARAM), VOID (*TabPage_OnSize)(HWND, UINT, int, int), BOOL fStretch); void TabControl_Select(LPTABCTRL); void TabControl_Destroy(LPTABCTRL); mysql-connector-odbc-5.1.10-src/setupgui/windows/tooltip.cpp100644 15766 12 30302 11707541005 22741 0ustar00cteamstaffÿþ/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception <http://www.mysql.com/about/legal/licensing/foss-exception.html>. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <windows.h> #include <commctrl.h> #include "resource.h" // DoCreateDialogTooltip - creates a tooltip control for a dialog box, // enumerates the child control windows, and installs a hook // procedure to monitor the message stream for mouse messages posted // to the control windows. // Returns TRUE if successful or FALSE otherwise. // // Global variables // g_hinst - handle of the application instance // g_hwndTT - handle of the tooltip control // g_hwndDlg - handle of the dialog box // g_hhk - handle of the hook procedure HHOOK g_hhk; HWND g_hwndTT; extern HINSTANCE ghInstance; #define g_hinst ghInstance extern HWND g_hwndDlg; BOOL CALLBACK EnumChildProc(HWND hwndCtrl, LPARAM lParam) ; LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) ; BOOL DoCreateDialogTooltip(void) { // Ensure that the common control DLL is loaded, and create // a tooltip control. InitCommonControls(); g_hwndTT = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, g_hwndDlg, NULL, g_hinst, NULL); if (g_hwndTT == NULL) return FALSE; // Enumerate the child windows to register them with the tooltip // control. if (!EnumChildWindows(g_hwndDlg, (WNDENUMPROC) EnumChildProc, 0)) return FALSE; // Install a hook procedure to monitor the message stream for mouse // messages intended for the controls in the dialog box. g_hhk = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, (HINSTANCE) NULL, GetCurrentThreadId()); if (g_hhk == (HHOOK) NULL) return FALSE; return TRUE; } // EmumChildProc - registers control windows with a tooltip control by // using the TTM_ADDTOOL message to pass the address of a // TOOLINFO structure. // Returns TRUE if successful or FALSE otherwise. // hwndCtrl - handle of a control window // lParam - application-defined value (not used) BOOL CALLBACK EnumChildProc(HWND hwndCtrl, LPARAM lParam) { TOOLINFO ti; wchar_t szClass[64]; // Skip static controls. GetClassName(hwndCtrl, szClass, sizeof(szClass)); if (!lstrcmp(szClass, L"Edit")) { ti.cbSize = sizeof(TOOLINFO); ti.uFlags = TTF_IDISHWND; ti.hwnd = g_hwndDlg; ti.uId = (UINT) hwndCtrl; ti.hinst = g_hinst; ti.lpszText = LPSTR_TEXTCALLBACK; int res = SendMessage(g_hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti); int e = GetLastError(); res++; } return TRUE; } // GetMsgProc - monitors the message stream for mouse messages intended // for a control window in the dialog box. // Returns a message-dependent value. // nCode - hook code // wParam - message flag (not used) // lParam - address of an MSG structure LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) { MSG *lpmsg; lpmsg = (MSG *) lParam; if (nCode < 0 || !(IsChild(g_hwndDlg, lpmsg->hwnd))) return (CallNextHookEx(g_hhk, nCode, wParam, lParam)); switch (lpmsg->message) { case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: if (g_hwndTT != NULL) { MSG msg; msg.lParam = lpmsg->lParam; msg.wParam = lpmsg->wParam; msg.message = lpmsg->message; msg.hwnd = lpmsg->hwnd; SendMessage(g_hwndTT, TTM_RELAYEVENT, 0, (LPARAM) (LPMSG) &msg); } break; default: break; } return (CallNextHookEx(g_hhk, nCode, wParam, lParam)); } // OnWMNotify - provides the tooltip control with the appropriate text // to display for a control window. This function is called by // the dialog box procedure in response to a WM_NOTIFY message. // lParam - second message parameter of the WM_NOTIFY message VOID OnWMNotify(WPARAM wParam, LPARAM lParam) { LPTOOLTIPTEXT lpttt; int idCtrl; if ((((LPNMHDR) lParam)->code) == TTN_NEEDTEXT) { idCtrl = GetDlgCtrlID((HWND) ((LPNMHDR) lParam)->idFrom); lpttt = (LPTOOLTIPTEXT) lParam; switch (idCtrl) { #define SET_TIP(edit,tip)\ case IDC_EDIT_##edit: \ lpttt->lpszText = tip; \ return; SET_TIP(drvname, L"An unique name for this data source"); SET_TIP(drvdesc, L"A brief description for this data source"); SET_TIP(srvname, L"The hostname for the MySQL Server"); SET_TIP(port, L"The TCP/IP port to use if server is not localhost"); SET_TIP(username, L"The username used to connect to MySQL"); SET_TIP(password, L"The password for the server user combination"); SET_TIP(dbname, L"The database to be current upon connect"); } } return; } void OnDialogClose() { if (g_hhk != (HHOOK) NULL) { UnhookWindowsHookEx( g_hhk ); } } mysql-connector-odbc-5.1.10-src/setupgui/windows/odbcdialogparams.rc100644 15766 12 27112 11707541005 24371 0ustar00cteamstaff///////////////////////////////////////////////////////////////////////////// // Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. // // The MySQL Connector/ODBC is licensed under the terms of the GPLv2 // , like most // MySQL Connectors. There are special exceptions to the terms and // conditions of the GPLv2 as it is applied to this software, see the // FLOSS License Exception // . // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #define APSTUDIO_HIDDEN_SYMBOLS #include "windows.h" #undef APSTUDIO_HIDDEN_SYMBOLS ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_DIALOG1 DIALOGEX 0, 0, 279, 405 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Dialog" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN GROUPBOX "Connection Parameters",IDC_STATIC,18,53,248,148 EDITTEXT IDC_EDIT_name,98,64,143,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT_description,98,83,143,14,ES_AUTOHSCROLL GROUPBOX "",IDC_STATIC,31,105,59,26,NOT WS_VISIBLE CONTROL "TCP/IP Server:",IDC_RADIO_tcp,"Button",BS_AUTORADIOBUTTON | BS_RIGHT,32,105,60,13 CONTROL "Named Pipe:",IDC_RADIO_pipe,"Button",BS_AUTORADIOBUTTON | BS_RIGHT,32,122,60,13 EDITTEXT IDC_EDIT_server,98,104,85,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT_port,212,104,28,14,ES_AUTOHSCROLL | ES_NUMBER EDITTEXT IDC_EDIT_socket,98,123,85,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT_uid,98,142,85,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT_pwd,98,161,85,14,ES_PASSWORD | ES_AUTOHSCROLL RTEXT "Data Source Name:",IDC_STATIC,23,68,67,8 RTEXT "Description:",IDC_STATIC,23,87,67,8 RTEXT "Port:",IDC_STATIC,187,107,19,8 RTEXT "User:",IDC_STATIC,23,143,67,8 RTEXT "Password:",IDC_STATIC,23,164,67,8 RTEXT "Database:",IDC_STATIC,23,182,67,8 COMBOBOX IDC_EDIT_database,98,180,85,42,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP CONTROL "",IDC_TAB1,"SysTabControl32",WS_TABSTOP,17,214,249,154 DEFPUSHBUTTON "OK",IDOK,111,375,50,15 PUSHBUTTON "&Cancel",IDCANCEL,165,375,50,15 PUSHBUTTON "&Test",IDC_BUTTON_TEST,199,179,41,14 PUSHBUTTON "&Details >>",IDC_BUTTON_DETAILS,17,376,50,14 PUSHBUTTON "&Help",IDC_BUTTON_HELP,217,375,49,15 CONTROL 130,IDC_STATIC,"Static",SS_BITMAP,0,0,279,39 END IDD_TAB1 DIALOGEX 0, 0, 209, 151 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "Allow big result sets",IDC_CHECK_allow_big_results, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,12,79,10 CONTROL "Use compression",IDC_CHECK_use_compressed_protocol, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,27,69,10 CONTROL "Enable automatic reconnect",IDC_CHECK_auto_reconnect, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,42,105,10 CONTROL "Don't prompt when connecting",IDC_CHECK_dont_prompt_upon_connect, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,57,113,10 CONTROL "Allow multiple statements",IDC_CHECK_allow_multiple_statements, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,72,97,10 CONTROL "Interactive Client",IDC_CHECK_clientinteractive, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,87,79,10 RTEXT "Character Set:",IDC_STATIC,12,102,67,8 COMBOBOX IDC_EDIT_charset,90,102,97,8,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP RTEXT "Initial Statement:",IDC_STATIC,12,117,67,8 EDITTEXT IDC_EDIT_initstmt,90,117,97,12,ES_AUTOHSCROLL // Second Column // CONTROL "Interactive Client",IDC_CHECK_clientinteractive, // "Button",BS_AUTOCHECKBOX | WS_TABSTOP,130,12,79,10 END IDD_TAB2 DIALOGEX 0, 0, 209, 151 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "Treat BIGINT columns as INT columns",IDC_CHECK_change_bigint_columns_to_int, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,12,135,10 CONTROL "Always handle binary function results as character data",IDC_CHECK_handle_binary_as_char, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,27,194,10 CONTROL "Ignore schema in column specifications",IDC_CHECK_ignore_N_in_name_table, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,42,139,10 CONTROL "Include table name in SQLDescribeCol()",IDC_CHECK_return_table_names_for_SqlDescribeCol, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,57,141,10 CONTROL "Disable catalog support",IDC_CHECK_no_catalog,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,72,91,10 CONTROL "Limit column size to signed 32-bit range",IDC_CHECK_limit_column_size, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,87,141,10 CONTROL "Don't use INFORMATION_SCHEMA for metadata",IDC_CHECK_no_information_schema, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,102,194,10 END IDD_TAB3 DIALOGEX 0, 0, 209, 151 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "Enable dynamic cursors",IDC_CHECK_dynamic_cursor, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,12,91,10 CONTROL "Disable driver-provided cursor support",IDC_CHECK_user_manager_cursor, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,27,139,10 CONTROL "Don't cache results of forward-only cursors",IDC_CHECK_dont_cache_result, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,42,154,10 CONTROL "Force use of forward-only cursors",IDC_CHECK_force_use_of_forward_only_cursors, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,57,125,10 CONTROL "Return matched rows instead of affected rows",IDC_CHECK_return_matching_rows, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,75,165,10 CONTROL "Enable SQL_AUTO_IS_NULL",IDC_CHECK_auto_increment_null_search, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,90,105,10 CONTROL "Pad CHAR to full length with space",IDC_CHECK_pad_char_to_full_length, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,105,127,10 CONTROL "Return SQL_NULL_DATA for zero date",IDC_CHECK_zero_date_to_min, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,120,138,10 END IDD_TAB4 DIALOGEX 0, 0, 209, 151 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "Log queries to myodbc.sql",IDC_CHECK_save_queries, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,12,148,10 END IDD_TAB5 DIALOGEX 0, 0, 209, 151 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN EDITTEXT IDC_EDIT_sslkey,90,12,97,12,ES_AUTOHSCROLL | WS_GROUP EDITTEXT IDC_EDIT_sslcert,90,30,97,12,ES_AUTOHSCROLL EDITTEXT IDC_EDIT_sslca,90,48,97,12,ES_AUTOHSCROLL EDITTEXT IDC_EDIT_sslcapath,90,66,97,12,ES_AUTOHSCROLL EDITTEXT IDC_EDIT_sslcipher,90,84,97,12,ES_AUTOHSCROLL RTEXT "SSL Key",IDC_STATIC,18,14,67,8 RTEXT "SSL Certificate",IDC_STATIC,6,32,78,11 RTEXT "SSL Certificate Authority",IDC_STATIC,6,50,79,8 RTEXT "SSL CA Path",IDC_STATIC,24,68,61,8 RTEXT "SSL Cipher",IDC_STATIC,18,86,67,8 PUSHBUTTON "...",IDC_SSLKEYCHOOSER,192,12,12,12,BS_CENTER PUSHBUTTON "...",IDC_SSLCERTCHOOSER,192,30,12,12,BS_CENTER PUSHBUTTON "...",IDC_SSLCACHOOSER,192,48,12,12,BS_CENTER PUSHBUTTON "...",IDC_SSLCAPATHCHOOSER,192,66,12,12,BS_CENTER CONTROL "Verify SSL Certificate",IDC_CHECK_sslverify,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,90,109,87,10 END IDD_TAB6 DIALOGEX 0, 0, 209, 151 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "Enable safe options (see documentation)",IDC_CHECK_safe, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,12,147,10 CONTROL "Don't use setlocale()",IDC_CHECK_dont_use_set_locale, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,27,81,10 CONTROL "Ignore space after function names",IDC_CHECK_ignore_space_after_function_names, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,42,127,10 CONTROL "Read options from my.cnf",IDC_CHECK_read_options_from_mycnf, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,57,99,10 CONTROL "Disable transaction support",IDC_CHECK_disable_transactions, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,72,103,10 CONTROL "Bind minimal date as zero date",IDC_CHECK_min_date_to_zero, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,87,138,10 END #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" "#include ""windows.h""\r\n" "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Bitmap // IDB_LOGO BITMAP "connector_odbc_header.bmp" ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN IDD_TAB5, DIALOG BEGIN VERTGUIDE, 85 END END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN IDC_DIALOG "DIALOG" END #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED mysql-connector-odbc-5.1.10-src/setupgui/windows/connector_odbc_header.bmp100644 15766 12 232516 11707541005 25567 0ustar00cteamstaffBMN56(¢?5£v£w£w£w£w¤w¤w¤x¤x¤x¤x¤x¤y¥y¥y¥y¥y¥z¥z¥z¦z¦z¦{¦{ ¦{ ¦{ ¦{ §| §| §| §| §} §} §} ¨} ¨~¨~¨~¨~¨~©©©©©€©€ª€ªªªª«‚«‚«‚«‚«ƒ«ƒ¬ƒ¬ƒ¬„¬„¬„­…­…­…­…­†®†®†®‡®‡®‡ ¯ˆ!¯ˆ!¯ˆ"¯ˆ"¯‰#°‰#°‰$°Š%°Š%°Š&±‹&±‹'±‹(±Œ(²Œ)²Œ)²*²+²+³Ž,³Ž,³Ž-³.´.´/´0´0´1µ‘1µ‘2µ‘3µ’3¶’4¶’5¶“5¶“6·“7·”7·”8·”9·•9¸•:¸–;¸–;¸–<¹—=¹—=¹—>¹˜?º˜?º˜@º™Aº™A»šB»šC»šC»›D¼›E¼›E¼œF¼œG½œH½H½I½žJ¾žJ¾žK¾ŸL¾ŸL¿ M¿ N¿ O¿¡OÀ¡PÀ¡QÀ¢QÀ¢RÁ£SÁ£TÁ£T¤U¤VÂ¥WÂ¥WÃ¥XæYæYçZħ[ħ\Ĩ\Ĩ]Å©^Å©_Å©_ƪ`ƪaƪbÆ«bÇ«cǬdǬeÇ­eÈ­fÈ­gÈ®hÉ®hɯiɯjɯkʰkʰlʱmʱn˱n˲o˲p̳q̳q̳rÌ´sÍ´t͵t͵u͵vζwζwηxÏ·yÏ·zϸzϸ{й|й}к~к~ѺÑ»€Ñ»Ò¼Ò¼‚Ò¼ƒÒ½„Ó½„Ó¾…Ó¾†Ó¾‡Ô¿‡Ô¿ˆÔÀ‰ÕÀŠÕÀŠÕÁ‹ÕÁŒÖÂÖÂÖÂŽÖÃ×Ã×Ä‘×Ä‘ØÄ’ØÅ“ØÅ”ØÆ”ÙÆ•ÙÇ–ÙÇ—ÙÇ—ÚȘÚÈ™ÚÉšÛÉšÛÉ›ÛÊœÛÊÜËÜËžÜËŸÜÌ ÝÌ ÝÍ¡ÝÍ¢ÞÍ¢ÞΣÞΤÞÏ¥ßÏ¥ßϦßЧßШàШàÑ©àѪàÒ«áÒ«áÒ¬áÓ­âÓ­âÔ®âÔ¯âÔ°ãÕ°ãÕ±ãÖ²ãÖ²äÖ³ä×´ä×µä׵娶娷åÙ·åÙ¸æÙ¹æÚ¹æÚºæÚ»çÛ¼çÛ¼çܽçܾèܾèÝ¿èÝÀèÝÀéÞÁéÞÂéÞÂéßÃêßÄêàÄêàÅêàÆëáÆëáÇëáÈëâÈëâÉìâÊìãÊìãËìãÌíäÌíäÍíäÎíåÎîåÏîæÐîæÐîæÑîçÑïçÒïçÓïçÓïèÔðèÔðèÕðéÖðéÖðé×ñê×ñêØñêÙñëÙòëÚòëÚòìÛòìÜòìÜóíÝóíÝóíÞóîßóîßôîàôîàôïáôïáôïâõðâõðãõðäõñäõñåöñåöñæöòæöòçöòç÷òè÷óè÷óé÷óé÷ôê÷ôêøôëøôëøõìøõìøõíùõíùöîùöîùöïùöïù÷ðú÷ðú÷ðú÷ñúøñúøòúøòûøóûùóûùôûùôûùôûúõûúõüúöüúöüúöüû÷üû÷üûøüûøýûøýüùýüùýüúýüúýüúýýûþýûþýûþýüþýüþþüþþýþþýþþýÿþþÿÿþÿÿþÿÿÿ£v£w£w£w£w¤w¤w¤x¤x¤x¤x¤x¤y¥y¥y¥y¥y¥z¥z¥z¦z¦z¦{¦{ ¦{ ¦{ ¦{ §| §| §| §| §} §} §} ¨} ¨~¨~¨~¨~¨~©©©©©€©€ª€ªªªª«‚«‚«‚«‚«ƒ«ƒ¬ƒ¬ƒ¬„¬„¬„­…­…­…­…­†®†®†®‡®‡®‡ ¯ˆ!¯ˆ!¯ˆ"¯ˆ"¯‰#°‰#°‰$°Š%°Š%°Š&±‹&±‹'±‹(±Œ(²Œ)²Œ)²*²+²+³Ž,³Ž,³Ž-³.´.´/´0´0´1µ‘1µ‘2µ‘3µ’3¶’4¶’5¶“5¶“6·“7·”7·”8·”9·•9¸•:¸–;¸–;¸–<¹—=¹—=¹—>¹˜?º˜?º˜@º™Aº™A»šB»šC»šC»›D¼›E¼›E¼œF¼œG½œH½H½I½žJ¾žJ¾žK¾ŸL¾ŸL¿ M¿ N¿ O¿¡OÀ¡PÀ¡QÀ¢QÀ¢RÁ£SÁ£TÁ£T¤U¤VÂ¥WÂ¥WÃ¥XæYæYçZħ[ħ\Ĩ\Ĩ]Å©^Å©_Å©_ƪ`ƪaƪbÆ«bÇ«cǬdǬeÇ­eÈ­fÈ­gÈ®hÉ®hɯiɯjɯkʰkʰlʱmʱn˱n˲o˲p̳q̳q̳rÌ´sÍ´t͵t͵u͵vζwζwηxÏ·yÏ·zϸzϸ{й|й}к~к~ѺÑ»€Ñ»Ò¼Ò¼‚Ò¼ƒÒ½„Ó½„Ó¾…Ó¾†Ó¾‡Ô¿‡Ô¿ˆÔÀ‰ÕÀŠÕÀŠÕÁ‹ÕÁŒÖÂÖÂÖÂŽÖÃ×Ã×Ä‘×Ä‘ØÄ’ØÅ“ØÅ”ØÆ”ÙÆ•ÙÇ–ÙÇ—ÙÇ—ÚȘÚÈ™ÚÉšÛÉšÛÉ›ÛÊœÛÊÜËÜËžÜËŸÜÌ ÝÌ ÝÍ¡ÝÍ¢ÞÍ¢ÞΣÞΤÞÏ¥ßÏ¥ßϦßЧßШàШàÑ©àѪàÒ«áÒ«áÒ¬áÓ­âÓ­âÔ®âÔ¯âÔ°ãÕ°ãÕ±ãÖ²ãÖ²äÖ³ä×´ä×µä׵娶娷åÙ·åÙ¸æÙ¹æÚ¹æÚºæÚ»çÛ¼çÛ¼çܽçܾèܾèÝ¿èÝÀèÝÀéÞÁéÞÂéÞÂéßÃêßÄêàÄêàÅêàÆëáÆëáÇëáÈëâÈëâÉìâÊìãÊìãËìãÌíäÌíäÍíäÎíåÎîåÏîæÐîæÐîæÑîçÑïçÒïçÓïçÓïèÔðèÔðèÕðéÖðéÖðé×ñê×ñêØñêÙñëÙòëÚòëÚòìÛòìÜòìÜóíÝóíÝóíÞóîßóîßôîàôîàôïáôïáôïâõðâõðãõðäõñäõñåöñåöñæöòæöòçöòç÷òè÷óè÷óé÷óé÷ôê÷ôêøôëøôëøõìøõìøõíùõíùöîùöîùöïùöïù÷ðú÷ðú÷ðú÷ñúøñúøòúøòûøóûùóûùôûùôûùôûúõûúõüúöüúöüúöüû÷üû÷üûøüûøýûøýüùýüùýüúýüúýüúýýûþýûþýûþýüþýüþþüþþýþþýþþýÿþþÿÿþÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþýýýúúúõõõòòòïïïìììëëëêêêëëëìììîîîñññõõõùùùüüüþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýýýüüüøøøòòòêêêàààÖÖÖÎÎÎÈÈÈÄÄÄÁÁÁ¿¿¿ÀÀÀÃÃÃÇÇÇÌÌÌÔÔÔÝÝÝèèèððð÷÷÷ûûûýýýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüýüûüûú÷öõíìëàßÞÑÐÐÂÁÁ²²±££¢——–Œ‡‡†‚‚€€€……„‹‹Š““’ŸŸž®®­¾¾½ÍÌÌÜÛÚêéèôóòûúùýüûþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýüüýüüýüüýüüýüüýüüýüüýüüýüüýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûûúùöõôíìëÝÜÜÉÉȲ±°œœ‰ˆˆyyxmmlddc^^^YYYVVVUUUVVVXXX\\\bbajjivvu…„„—––­¬«ÃÃÂØ××éèçôóòúùøüûúýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèá̬D»¤fÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýþýýþýýþýýþýýþýýþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýûýýûýýûýýûýýûýýûýýûýýûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúüüúüüúüüúüüúüüúüüúüüúüüúüüúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùûúøø÷õðïíàßÞÌËɱ°¯••“|{zjihrg]˜y]¯„`ÁŽcÌ”eà jà jà jÓ˜fÀb¶‡_›xYtbRPPOYYXfeewvuŒ©¨§ÄÃÁÛÛÙìëé÷öôûúøüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðëÝÒ™ʸˆÊ¸ˆÊ¸ˆèáÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ̪ʸˆÊ¸ˆÊ¸ˆðëÝÿÿÿÿÿÿÿÿÿèáÌáÖ»÷õîÿÿÿÿÿÿÿÿÿÿÿÿáÖ»áÖ»ÿÿÿ÷õîáÖ»èáÌÿÿÿÿÿÿÿÿÿÿÿÿðëÝáÖ»ðëÝÿÿÿÿÿÿÿÿÿÿÿÿÙ̪ʸˆÊ¸ˆÊ¸ˆáÖ»÷õîÿÿÿÿÿÿÿÿÿÿÿÿÙ̪ʸˆÊ¸ˆÊ¸ˆáÖ»÷õîÿÿÿÿÿÿÿÿÿáÖ»áÖ»áÖ»ðëÝÿÿÿÿÿÿ÷õîÒ™ʸˆÊ¸ˆÒ™÷õîÿÿÿÿÿÿÿÿÿáÖ»áÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–qŽgðëÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðëÝÒ™ʸˆÊ¸ˆÒ™ðëÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÖ»áÖ»áÖ»áÖ»áÖ»áÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÖ»áÖ»áÖ»áÖ»áÖ»áÖ»èáÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õîÙ̪ʸˆÊ¸ˆÊ¸ˆáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýýþýüþýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûüüûüüûüüúüüúüüúüüúüüúüüúüüúüüúüüúüüúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùûûùûûùûûùûûùûûùûúùûúùûúùûúùûúùûúùûúùûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøúù÷öõóëêèÖÕÓ¹¸·š™˜||{€re¢~`Ζfß§rÝ´ƒÛ¼ŒÛ“ÛÅ–ÚËÚÊœÚÉšÚÆ•ÚÀŽÙ¼‰Ú´€ÝªtÕ™g©\xdRRQQ``_uut‘°¯®ÎÍËåäãôóñúù÷ûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õî¬DŽgŽgŽgŽgŽgŽg{"èáÌÿÿÿ÷õ3ŽgŽgŽgŽgŽgÒ™ÿÿÿÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgÿÿÿáÖ»Žg¬DÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽgʸˆÿÿÿÿÿÿáÖ»–qŽgŽgŽgŽgŽgŽgèáÌÿÿÿèáÌ¥…3ŽgŽgŽgŽgŽgʸˆÿÿÿèáÌ–qŽgŽgŽgʸˆÿÿÿèáÌ–qŽgŽgŽgŽg–qèáÌÿÿÿÿÿÿŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¤fŽgʸˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õîîwŽgŽgŽgŽgŽgŽgîw÷õîÿÿÿÿÿÿÿÿÿŽgŽgŽgŽgŽgŽgŽg¥…3ʸˆÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgŽgŽgŽgŽgŽg{"Ù̪ÿÿÿÿÿÿÿÿÿÿÿÿîwŽgŽgŽgŽgŽgŽg–qÒ™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýþýýþýýþýüþýüþýüþýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýûýýûýýûýýûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûüüúüüúüüúüüúüüúüüúüüúüüúüüúüüúüüúüüúüüúüüúüûúüûúüûúüûúüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùûûùûûùûûùûûùûûøûûøûûøûûøûûøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûú÷ûú÷ûú÷ûú÷ûú÷ûú÷ûú÷ûú÷ûú÷ûú÷ûú÷ûú÷ûú÷ûú÷úú÷úú÷úú÷úú÷úù÷úù÷úù÷úù÷úù÷úù÷úù÷úùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöùøõóòïåäâÍÌʬ«©ˆˆ†‹yi´ˆcÞ©tÜ·†ÚŘÚËžÚËžÛËžÛÌžÛËÛËÚÊ›ÚÊ›ÚÉ™ÙÉ™ÙÈ—ØÇ”×Å‘ÖÁ‹Øµ~Û©rÄc†kTQQPcca~~|¡ žÄÃÁáàÝðïìø÷ôúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õ3Žg–qîwÙ̪áֻʸˆ¥…3Žgʸˆÿÿÿ¬DŽg¥…3áÖ»÷õîʸˆŽgŽgáÖ»ÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgÿÿÿáÖ»Žg¬DÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽgʸˆÿÿÿèáÌ–qŽg´šUèáÌÿÿÿðëÝÒ™{"áÖ»÷õî{"Žg¥…3áÖ»ÿÿÿðëÝʸˆÒ™ÿÿÿ»¤fŽg¬DáÖ»áÖ»ðëÝ÷õî–qŽg´šUðëÝðëÝ´šUŽg–q÷õîÿÿÿŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ̪Žg´šUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgŽg»¤fÙ̪Ù̪»¤fŽgŽg¬DÿÿÿÿÿÿÿÿÿŽgŽg»¤fʸˆÊ¸ˆ»¤f{"ŽgŽg¥…3÷õîÿÿÿÿÿÿŽgŽg»¤fʸˆÊ¸ˆÃ®w{"ŽgŽgáÖ»ÿÿÿÿÿÿ»¤fŽgŽg»¤fÒ™áÖ»Ò™´šUŽg¬Dÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýþýýþýýþýýþýýþýýþýüþýüþýüþýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýüüýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüúüüúüüúüüúüüúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûúüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüúùüúùüúùûúùûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûú÷ûù÷ûù÷ûù÷ûù÷ûù÷ûù÷ûù÷ûù÷ûù÷ûù÷ûù÷úù÷úù÷úù÷úù÷úùöúùöúùöúùöúùöúùöúùöúùöúøöúøöúøöúøöúøöúøöúøöúøöúøöúøöúøõúøõúøõúøõúøõúøõúøõúøõúøõúøõúøõúøõùøõùøõùøõùøõùøõùøõù÷õù÷õù÷õù÷õù÷õù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôøöóòðíãáÞÆÅ¢¡Ÿ‘ƒw®‡eÞ¦qÚ»‹ÙÉœÙÊÚÊœÚÊœÚÉ›ÚÉ™ÙɘÙɘÙÈ—ÙÈ–ÙÈ–ÙÈ–ÙÈ•ÙÇ”ØÆ“×Å‘ÖÄÕÂŒÓÀ‡Ô¸~Û§o¹‰`|fSYXWtsr—–”½¼ºÜÚ×ïíê÷õòù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¤fŽg–qèáÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆÒ™èáÌŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿ¬DŽg´šUÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgÿÿÿáÖ»Žg¬DÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽgʸˆÿÿÿ»¤fŽg¬DÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒ™Žg–q÷õîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿʸˆŽg–q÷õîÿÿÿÿÿÿ÷õî–qŽgʸˆÿÿÿŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽgŽg÷õîÿÿÿÿÿÿÿÿÿÿÿÿÒ™ŽgŽgÒ™ÿÿÿÿÿÿÿÿÿÿÿÿÒ™ŽgŽgÒ™ÿÿÿÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÒ™–qŽg»¤fÿÿÿÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿ{"Žg´šUÿÿÿÒ™ŽgŽgÒ™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáֻîwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýþýýþýýþýüþýüþýüþýüþýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüûýüúýüúüüúüüúüüúüüúüüúüûúüûúüûúüûúüûúüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùüûùûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûúøûú÷ûú÷ûú÷ûú÷ûú÷ûù÷ûù÷ûù÷ûù÷ûù÷úù÷úù÷úùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúùöúøöúøöúøõúøõúøõúøõúøõúøõúøõúøõúøõùøõùøõùøõùøõùøõùøôùøôùøôùøôùøôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷óù÷óù÷óù÷óù÷óù÷óù÷óù÷óù÷óù÷óø÷óø÷óø÷óø÷óøöóøöóøöóøöóøöóøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöò÷õññïëáßÛÃÁ¾œš˜ªŠoߣnÙ·„×Ç—ØÈšØÈ˜ØÇ—ØÇ–ׯ”×Å’×Å×Å×Å×Å×ÅØÅ×ÅØÅØÅØÅ×ÄŽ×ÄÖÃŒÔÁ‰Ó¾„лÒ²uÛ¤kžyYUSQlki‘¹·´ÚØÕîìé÷õñøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòøöòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðëÝŽgŽgÒ™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽg{"ÿÿÿÿÿÿÿÿÿÿÿÿÒ™Žg–qÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgÿÿÿáÖ»Žg¬DÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽgʸˆÿÿÿ–qŽgÒ™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´šUŽg»¤fÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿ´šUŽg»¤fÿÿÿÿÿÿÿÿÿÿÿÿ»¤fŽg´šUÿÿÿŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´šUŽgÒ™ÿÿÿÿÿÿÿÿÿÿÿÿ{"Žg»¤fÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¤fŽg{"ÿÿÿÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽgŽgðëÝÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽg¬Dÿÿÿ{"Žg»¤fÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýûýýûýýûýüûýüûýüûýüûýüûüüûüüûüüûüüûüüúüüúüüúüüúüüúüüúüüúüüúüüúüûúüûúüûùüûùüûùüûùûûùûûùûûùûûùûûùûûùûûùûûøûûøûûøûúøûúøûúøûúøûúøûúøûúøûúøúú÷úú÷úú÷úú÷úú÷úú÷úú÷úú÷úù÷úù÷úù÷úùöúùöúùöúùöúùöúùöúùöùùöùùöùùöùùöùùõùùõùùõùøõùøõùøõùøõùøõùøõùøõùøõùøõùøôùøôùøôøøôøøôøøôøøôøøôø÷ôø÷ôø÷ôø÷óø÷óø÷óø÷óø÷óø÷óø÷óø÷óø÷óø÷óø÷óø÷óø÷ó÷÷ò÷÷ò÷÷ò÷öò÷öò÷öò÷öò÷öò÷öò÷öò÷öò÷öò÷öò÷öò÷öñ÷öñ÷öñ÷öñ÷öñ÷öñ÷öñ÷öñ÷öñ÷öñ÷öñ÷öñööñöõñöõñöõñöõñöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðõôïñðëâáÜÄÿ››˜°ŒlݧrÕÀŽÖÅ•ÖÅ”ÖÅ’ÖÄÕÃŽÔÁ‹ÔÁ‰ÔÀ‡ÔÀ…ÔÀ†ÔÁ†ÕÁ†Õ‡ÖˆÖÈ×Ê×Ê×ÊÖÊÖÊÕˆÔÁ‡Ó¿„ѽϺ{̶uÖ§k£|Zl_ShhfŽŠ¸¸´ÛÚÖîíèõôïöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðöõðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ̪ŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽg¬DÿÿÿÿÿÿÿÿÿÿÿÿáÖ»ŽgŽgÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgÿÿÿáÖ»Žg¬DÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽgʸˆÿÿÿŽgŽg¥…3¬D¬D¬D¬D¬D¬DÒ™¬DŽgʸˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿ¬DŽgʸˆÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽg¬DÿÿÿŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒ™Žg´šUÿÿÿÿÿÿÿÿÿ÷õîŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÖ»ŽgŽg÷õîÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgÒ™ÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽg»¤f÷õîŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýþýýþýüþýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýûýýûýüûýüûýüûýüûýüûýüûýüûýüûüüúüüúüüúüüúüüúüüúüüúüûúüûúüûùüûùüûùüûùüûùüûùüûùüûùüûùûûùûûùûûøûûøûúøûúøûúøûúøûúøûúøûú÷ûú÷ûú÷ûú÷ûú÷ûú÷úú÷úù÷úù÷úù÷úùöúùöúùöúùöúùöúùöúùöúùöúùöúùõúùõúøõúøõùøõùøõùøõùøõùøõùøôùøôùøôùøôùøôùøôùøôù÷ôù÷ôù÷óù÷óø÷óø÷óø÷óø÷óø÷óø÷óø÷óø÷òø÷òøöòøöòøöòøöòøöòøöòøöòøöò÷öñ÷öñ÷öñ÷öñ÷öñ÷öñ÷öñ÷õñ÷õñ÷õñ÷õð÷õð÷õð÷õð÷õð÷õð÷õð÷õð÷õðöõðöõðöõðöõïöõïöõïöôïöôïöôïöôïöôïöôïöôïöôïöôïöôîöôîöôîöôîöôîöôîöôîöôîöôîõôîõôîõóîõóîõóîõóîõóîõóíõóíõóíõóíõóíõóíõóíõóíõóíõóíõóíõóíõóíõóíõóíõóíõóíòðêæäÞÊÈèž“´lÚ«vÓÀŽÔÃÔÂÓÁŒÓÀ‰Ò¾…ѽ‚Ѽ€Ñ¼~Ѽ}Ò¼}Ò½~Ó½Ô¿€ÕÀ‚ÕÀƒÖÁ…ÖÁ…׆ֆևևÖÁ…ÔÀ„Ó¾‚Ò½л|θvʳoÑ©hß j€iVjif’‘¾½¸àÞÙðîèôòìõóíõóíõóíõóíõóíõóíõóíõóíõóíõóíõóíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽg¬DÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽg¬DÿÿÿÿÿÿÿÿÿÿÿÿáÖ»ŽgŽgÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgÿÿÿáÖ»Žg¬DÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽgʸˆÿÿÿŽgŽg¥…3¬D¬D¬D¬D{"Žgʸˆ¬DŽgʸˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿ¬DŽgʸˆÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽg¬DÿÿÿŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õîŽg–qÿÿÿÿÿÿÿÿÿáÖ»ŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgáÖ»ÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgʸˆÿÿÿŽgŽgʸˆáÖ»áÖ»Ù̪»¤fŽgŽgèáÌáÖ»ŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýþýýþýüþýüþýüýýüýýüýýüýýüýýüýýüýýüýýüýüûýüûýüûýüûýüûýüûýüûýüûýüûüüúüüúüüúüüúüüúüûúüûúüûúüûúüûúüûùüûùüûùüûùüûùüûùüûùûúùûúøûúøûúøûúøûúøûúøûúøûúøûúøûú÷ûú÷ûú÷ûù÷ûù÷ûù÷úù÷úù÷úùöúùöúùöúùöúùöúùöúøöúøöúøõúøõúøõúøõùøõùøõùøõùøõùøôùøôù÷ôù÷ôù÷ôù÷ôù÷ôù÷ôù÷óù÷óù÷óø÷óø÷óøöóøöóøöóøöóøöòøöòøöòøöòøöòøöòøöòøöòøöñ÷õñ÷õñ÷õñ÷õñ÷õñ÷õñ÷õñ÷õð÷õð÷õð÷õð÷õð÷ôð÷ôð÷ôð÷ôðöôïöôïöôïöôïöôïöôïöôïöôïöôïöóïöóîöóîöóîöóîöóîöóîöóîöóîõóîõóîõóîõóíõóíõóíõóíõòíõòíõòíõòíõòíõòíõòíõòìõòìõòìõòìõòìõòìõòìõòìôòìôòìôòìôñìôñìôñìôñëôñëôñëôñëôñëôñëôñëôñëôñëôñëôñëôñëôñëôñëôñëôñëóðêêèâÓÐ˯¦œ¶oÕ¯yÑ¿‹ÑÀ‹Ñ¿ŠÑ½†Ð¼Ïº}ιyÍ·vηuϸuйvѺwÒ»zÓ½|Ô¾~Õ¿€ÖÀ€ÖÁÖÁƒ×Áƒ×Áƒ×…ׄÖÀƒÕÀƒÔ¿Ò½}Ñ»zθvË´pȰi˨bÞ i†mXonk›—ÈÆÁåâÜñîèôñëôñëôñëôñëôñëôñëôñëôñëôñëôñëôñëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽg¬DÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ̪ŽgŽg÷õîÿÿÿÿÿÿÿÿÿîwŽg¥…3ÿÿÿ¬DŽgʸˆÿÿÿÿÿÿÿÿÿ÷õîŽgŽgÿÿÿáÖ»Žg–qÿÿÿÿÿÿÿÿÿÿÿÿîwŽgʸˆÿÿÿ¥…3Žgîwÿÿÿÿÿÿÿÿÿÿÿÿ»¤fŽgÒ™îwŽg¥…3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿîwŽg¥…3ÿÿÿÿÿÿÿÿÿÿÿÿ¥…3ŽgîwÿÿÿŽgŽgèáÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgÙ̪ÿÿÿÿÿÿáÖ»ŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgáÖ»ÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgʸˆÿÿÿŽgŽgŽgŽgŽgŽgŽg–qáÖ»ÿÿÿáÖ»ŽgŽgÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýüýýüýýüýýüýýüýýüýýüýýüýýüýýüýýûýýûýüûýüûýüûýüûýüûüüûüüûüüûüüúüüúüüúüüúüüúüûúüûúüûúüûùüûùüûùûûùûûùûûùûûùûûùûûøûúøûúøûúøûúøûúøûúøûúøûú÷úú÷úú÷úú÷úù÷úù÷úù÷úùöúùöúùöúùöúùöúùöùùöùùöùøõùøõùøõùøõùøõùøõùøõùøôùøôùøôùøôø÷ôø÷ôø÷ôø÷ôø÷óø÷óø÷óø÷óø÷óø÷óø÷óøöòøöò÷öò÷öò÷öò÷öò÷öò÷öñ÷öñ÷öñ÷öñ÷õñ÷õñ÷õñöõñöõðöõðöõðöõðöõðöõðöõðöôðöôïöôïöôïöôïöôïõôïõôïõôïõôîõôîõôîõóîõóîõóîõóîõóîõóíõóíõóíôóíôóíôóíôóíôóíôòíôòìôòìôòìôòìôòìôòìôòìôòìôòìôòìóòìóòëóòëóñëóñëóñëóñëóñëóñëóñëóñëóñêóñêóñêóñêóñêóñêóñêóñêóñêòñêòðêòðêòðêòðêòðéòðéòðéòðéòðéòðéòðéòðéòðéòðéòðéòðéòðéòðéíëäÛÙÓ·¶°¼–tÔ­vλ„ϼ†Ï»ƒÎº~͸z̵uË´q˳n˳n̵n͵oиsѺvÒ»yÔ½zÔ½|Õ¿~ÖÀ€×Á×Á‚׃׃׃ÖÁ‚ÖÁ‚ÕÀÕ¿€Ó½}ѺzθuÌ´pɰiÅ«aÈ¥\ß i|jYzyvª¨¤ÓÑËéèáñïèòðéòðéòðéòðéòðéòðéòðéòðéòðéòðéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽg¥…3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õî–qŽgʸˆÿÿÿÿÿÿ÷õî–qŽgʸˆÿÿÿ¬DŽg{"÷õîÿÿÿÿÿÿʸˆŽg¥…3ÿÿÿáÖ»ŽgŽgʸˆÿÿÿÿÿÿ÷õî{"ŽgÙ̪ÿÿÿʸˆŽg–qèáÌÿÿÿÿÿÿèáÌ–qŽgðëÝáÖ»ŽgŽgÒ™ÿÿÿÿÿÿÿÿÿ÷õîðëÝÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿáÖ»ŽgŽgáÖ»ÿÿÿÿÿÿáÖ»ŽgŽgáÖ»ÿÿÿŽgŽg´šUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽg»¤fÿÿÿÿÿÿáÖ»ŽgŽg÷õîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õîŽgŽgáÖ»ÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥…3ŽgʸˆÿÿÿŽgŽg»¤fʸˆÊ¸ˆ»¤f–qŽg»¤fÿÿÿáÖ»ŽgŽg÷õîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþýýþýýþýýþýüþýüýýüýýüýýüýýüýýüýýüýýüýýûýüûýüûýüûýüûýüûýüûüüúüüúüüúüüúüüúüüúüûúüûúüûúüûùüûùüûùüûùüûùûûùûûùûúøûúøûúøûúøûúøûúøûúøûú÷ûú÷ûú÷úú÷úù÷úù÷úù÷úùöúùöúùöúùöúùöúùöúøõúøõúøõùøõùøõùøõùøõùøôùøôùøôù÷ôù÷ôù÷ôù÷óø÷óø÷óø÷óø÷óø÷óø÷óøöòøöòøöòøöòøöòøöò÷öñ÷öñ÷öñ÷õñ÷õñ÷õñ÷õñ÷õð÷õð÷õð÷õðöõðöõðöõðöôïöôïöôïöôïöôïöôïöôîöôîöôîöóîõóîõóîõóîõóíõóíõóíõóíõóíõóíõóíõòìõòìôòìôòìôòìôòìôòìôòëôòëôòëôñëôñëôñëôñëôñëóñêóñêóñêóñêóñêóñêóñêóðêóðéóðéóðéóðéóðéóðéóðéóðéòðéòðèòðèòðèòïèòïèòïèòïèòïèòïèòïèòïèòïçòïçòïçòïçòïçòïçñïçñïçñîçñîçñîæñîæñîæñîæñîæñîæñîæñîæñîæñîæñîæñîæñîæñîæðíååâÚÈÆ¿Â|Ö¨q˸͹€Ì¸}˶yÊ´sɲnȰjȰhɯfʱg˳jεnйsѺuÓ¼yÔ¾|Ö¿~×Á€ØÂ‚ÙÄÙÃ…ÙÃ…ÙÄØÃ„ØÂƒÖÁ‚ÖÀÕ¿Ô¾}Ñ»xϸt̵oɱiƬaÁ§XÈ¡V±„^fb]‹‰…¼º³ÞÛÔîëãñîæñîæñîæñîæñîæñîæñîæñîæñîæñîæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÖ»ŽgŽg÷õîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒ™ŽgŽg¬Dîw–qŽg¥…3÷õîÿÿÿ¬DŽgŽg–qîw»¤fŽgŽgÒ™ÿÿÿáÖ»ŽgŽgŽg¬Dʸˆ{"Žg{"ÿÿÿÿÿÿÿÿÿ¥…3Žg–q»¤fîw–qŽgîwÿÿÿÿÿÿîwŽgŽg¬Dʸˆ»¤f–qʸˆ¬D–qŽg¥…3¬D¬DÒ™ÿÿÿ»¤fŽgŽg»¤f»¤fŽgŽg»¤fÿÿÿÿÿÿŽgŽgŽg{"ʸˆ¥…3áÖ»ÿÿÿÿÿÿðëÝŽg–qÿÿÿÿÿÿÿÿÿŽgŽgÙ̪ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ̪ŽgŽgÿÿÿÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õîŽgŽgÙ̪ÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿáÖ»ŽgŽgðëÝÿÿÿŽgŽgÙ̪ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿþþÿþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþýýþýýþýýþýüþýüýýüýýüýýüýýüýýüýýüýýüýüûýüûýüûýüûýüûýüûýüûüüúüüúüüúüûúüûúüûúüûúüûùüûùüûùüûùüûùüûùûúùûúùûúøûúøûúøûúøûúøûú÷ûú÷ûú÷ûù÷ûù÷úù÷úù÷úùöúùöúùöúùöúùöúøöúøöúøõúøõùøõùøõùøõùøõùøôù÷ôù÷ôù÷ôù÷ôù÷ôù÷óø÷óø÷óøöóøöóøöóøöòøöòøöòøöòøöòøöò÷õñ÷õñ÷õñ÷õñ÷õñ÷õñ÷õð÷õð÷ôð÷ôð÷ôðöôðöôïöôïöôïöôïöôïöóïöóîöóîöóîöóîõóîõóîõóîõóíõòíõòíõòíõòíõòìõòìõòìôòìôòìôñìôñìôñëôñëôñëôñëôñëôñëôñêôñêóðêóðêóðêóðêóðêóðêóðéóðéóðéóðéóïéóïéóïéòïèòïèòïèòïèòïèòïèòïèòïèòîçòîçòîçòîçòîçòîçñîçñîçñîæñîæñîæñíæñíæñíæñíæñíæñíæñíåñíåñíåñíåñíåñíåñíåðíåðíåðíåðìåðìäðìäðìäðìäðìäðìäðìäðìäðìäðìäðìäðìäðìäëçàØÔÍŧŒÚ£lȳxɵzɵwɳsȱnÆ®hƬcŬaƬ`È®aʰe̳jÏ·oѹsÓ»wÕ¾{×Á~ØÂ‚ÚÄ„ÛÅ‡ÛÆˆÛƉÛÇ‰ÛÆ‰ÚŇÙĆÙÃ×ÁÕ¿Ô¾|Ò¼yйt͵oʱhƬ`§W¾¡NÎX |\pnj¡ž™ÍÊÃçãÛïëãðìäðìäðìäðìäðìäðìäðìäðìäðìäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õî–qŽgʸˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ̪¥…3ŽgŽg–q»¤f÷õîÿÿÿÿÿÿîw¬DÒ™´šUŽgŽg{"ʸˆÿÿÿÿÿÿèá̬Dîwʸˆ{"ŽgŽg´šUèáÌÿÿÿÿÿÿÿÿÿ÷õîʸˆ{"ŽgŽg{"ʸˆÿÿÿÿÿÿÿÿÿÿÿÿÙ̪¬DŽgŽgŽg¥…3Ù̪¬D–qŽg¥…3¬D¬DÒ™ÿÿÿÿÿÿʸˆ{"ŽgŽg{"ʸˆÿÿÿÿÿÿÿÿÿ¬D¬DÙ̪¥…3ŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿ¥…3ŽgèáÌÿÿÿÿÿÿ¬DŽg¬Dÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽg¬DÿÿÿÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîwŽg–q÷õîÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgáÖ»ÿÿÿ¬DŽg¬Dÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþýýþýýþýüýýüýýüýýüýýüýýüýýüýýûýýûýüûýüûýüûýüûýüûüüûüüúüüúüüúüüúüüúüûúüûúüûùüûùüûùüûùûûùûûùûûøûúøûúøûúøûúøûúøûú÷ûú÷ûú÷úú÷úú÷úù÷úùöúùöúùöúùöúùöúùõúùõùøõùøõùøõùøõùøôùøôùøôùøôù÷ôø÷óø÷óø÷óø÷óø÷óø÷óø÷òø÷òøöòøöòøöò÷öò÷öñ÷öñ÷öñ÷öñ÷õñ÷õñ÷õð÷õðöõðöõðöõðöõïöôïöôïöôïöôïöôîöôîõôîõôîõóîõóíõóíõóíõóíõóíõóíôóìôòìôòìôòìôòìôòëôòëôòëôòëôòëóñëóñêóñêóñêóñêóñêóñêóñéóðéóðéòðéòðéòðéòðèòðèòðèòðèòðèòïèòïçòïçñïçñïçñïçñïçñïçñïæñîæñîæñîæñîæñîæñîåðîåðîåðîåðîåðîåðíåðíåðíäðíäðíäðíäðíäðíäïíäïíãïíãïìãïìãïìãïìãïìãïìãïìãïìãïìâïìâïìâïìâïìâïìâîëâîëâîëâîëáîëáîëáîëáîëáîëáîëáîëáîëáîëáîëáíêàâßÖÈÁ¶ß jƬpƱsDZrưnÅ­hÄ«bè]¨ZèZŪ[È­_˱fεmѸqÓ»vÕ¾{×Á€ÙÄÛňÜÇ‹ÝÈÞÉÞÊÞÊÞÉŽÝÈŒÜÇŠÚŇÙÄ×ÁÕ¿}Ó½yѺvζo˱gÇ­`¦V½¡MºšCØžaŠs]‡†€º·°ÜÙÐìéßîëáîëáîëáîëáîëáîëáîëáîëáîëáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽgŽgÒ™ÿÿÿÿÿÿÿÿÿÿÿÿ÷õfʸˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆŽgîwÿÿÿÿÿÿáÖ»ŽgŽg»¤fÿÿÿÿÿÿÿÿÿÿÿÿ»¤fŽgŽgáÖ»ÿÿÿÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿðëÝîwŽgŽgʸˆÿÿÿÿÿÿŽgŽgáÖ»ÿÿÿÿÿÿÿÿÿʸˆŽgŽgèáÌÿÿÿáÖ»ŽgŽg»¤fÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆ¬Dÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿþþÿþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþýýþýýþýüþýüýýüýýüýýüýýüýýüýýüýüûýüûýüûýüûýüûýüûüüúüüúüüúüüúüûúüûúüûúüûùüûùüûùüûùûûùûûùûûøûúøûúøûúøûúøûúøûú÷ûú÷ûú÷úù÷úù÷úù÷úùöúùöúùöúùöúùöúøõúøõùøõùøõùøõùøõùøôùøôù÷ôù÷ôù÷ôù÷óø÷óø÷óø÷óøöóøöòøöòøöòøöò÷öò÷öò÷öñ÷õñ÷õñ÷õñ÷õñ÷õð÷õð÷õðöôðöôðöôïöôïöôïöôïöôïöôîöóîõóîõóîõóîõóíõóíõóíõòíõòíôòìôòìôòìôòìôòìôòëôñëôñëôñëôñëóñêóñêóñêóñêóðêóðêóðéóðéóðéòðéòðéòïèòïèòïèòïèòïèòïçòïçòïçñîçñîçñîçñîæñîæñîæñîæñîæñíæñíåñíåðíåðíåðíåðíåðíäðíäðìäðìäðìäðìäðìäïìãïìãïìãïìãïìãïëãïëãïëâïëâïëâïëâïëâîëâîëâîëáîëáîêáîêáîêáîêáîêáîêáîêàîêàîêàîêàîêàîêàíêàíéàíéàíéàíéßíéßíéßíéßíéßíéßíéßíéßíéßíéßêæÜÙÕÌ˧†Ð¤jÁ­mÄ®mÄ­iêdÁ¨]À¦XÀ¤UÁ¤SçUÅ©ZÉ®`Ͳh϶nÒºtÔ¾z×Á€ÚÄ…ÜÇŠÞÉŽßË‘àÍ”áΖáΖáΖàÍ”ßË’ÞÊÝÈŒÛňØÃ„ÖÀÔ½zÒ»vÏ·p˲hǬ_çU¾ L¹š@¾–A½Œbqok¤¡šÏÌÃæãÙíéßíéßíéßíéßíéßíéßíéßíéßíéßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¤fŽgŽg¥…3îwʸˆ´šUŽgŽgʸˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬DŽgáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèáÌŽg{"ÿÿÿÿÿÿÿÿÿîwŽgŽg{"îwîw{"ŽgŽgîwÿÿÿÿÿÿÿÿÿŽgŽg¥…3¬D¬D{"ŽgŽgŽg»¤fÿÿÿÿÿÿÿÿÿŽgŽg¥…3¬D¬D{"ŽgŽg¬DÿÿÿÿÿÿÿÿÿÒ™ŽgŽg{"»¤fʸˆ»¤f–qŽg¬Dÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþýýþýýþýýþýüýýüýýüýýüýýüýýüýýüýýûýüûýüûýüûýüûüüûüüúüüúüüúüüúüûúüûúüûúüûùüûùüûùûûùûûùûúøûúøûúøûúøûúøûú÷ûú÷ûú÷úù÷úù÷úùöúùöúùöúùöúùöúùöúøõùøõùøõùøõùøõùøôùøôù÷ôù÷ôø÷óø÷óø÷óø÷óø÷óøöóøöòøöò÷öò÷öò÷öò÷õñ÷õñ÷õñ÷õñ÷õð÷õðöõðöõðöôïöôïöôïöôïöôïöôïõôîõóîõóîõóîõóíõóíõóíõòíôòìôòìôòìôòìôòìôòëôñëôñëóñëóñëóñêóñêóñêóðêóðêóðéòðéòðéòðéòðèòïèòïèòïèòïèòïçñïçñïçñîçñîçñîæñîæñîæñîæðîæðíåðíåðíåðíåðíåðíåðíäðíäðìäïìäïìäïìãïìãïìãïìãïìãïëâïëâïëâîëâîëâîëáîëáîëáîêáîêáîêáîêáîêàíêàíêàíêàíêàíêàíéßíéßíéßíéßíéßíéßìéßìéÞìéÞìéÞìèÞìèÞìèÞìèÞìèÞìèÝìèÝìèÝìèÝìèÝëèÝëèÝëçÝëçÝëçÜëçÜëçÜëçÜëçÜëçÜëçÜëçÜëçÜëçÜåáÖÎÄ·Ý i¿§eÀ©fÁªdÀ¨`¿¥Y¾£S¾¢P¾¢NÀ£P¦TÆ«ZʯbδjйqÔ¼x×ÀÚÄ…ÜÇŠÞËàÍ•âϘãÑ›äÒäÒžäÒãÑ›âϘàÍ•ÞË‘ÜÈ‹ÛŇØÂ‚Ö¿|Ò»wзqͲiÇ­_çU¾ J¸™>³“3КUw`Œ†Á½´ßÛÑêæÛëçÜëçÜëçÜëçÜëçÜëçÜëçÜëçÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʸˆ{"ŽgŽgŽgŽg–q»¤f÷õîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒ™ʸˆðëÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{"ŽgèáÌÿÿÿÿÿÿÿÿÿÙ̪{"ŽgŽgŽgŽg{"Ù̪ÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgŽgŽgŽgŽg¥…3»¤fáÖ»ÿÿÿÿÿÿÿÿÿÿÿÿŽgŽgŽgŽgŽgŽg{"ʸˆ÷õîÿÿÿÿÿÿÿÿÿÿÿÿÙ̪¥…3ŽgŽgŽgŽgŽg´šUèáÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþþÿþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþýýþýýþýüþýüýýüýýüýýüýýüýýüýýûýüûýüûýüûýüûýüûýüûüüúüüúüüúüûúüûùüûùüûùüûùüûùûûùûûøûúøûúøûúøûúøûú÷ûú÷ûù÷ûù÷úù÷úùöúùöúùöúùöúùöúøõúøõùøõùøõùøõùøôùøôù÷ôù÷ôù÷ôø÷óø÷óø÷óø÷óøöòøöòøöòøöòøöñ÷öñ÷õñ÷õñ÷õñ÷õð÷õð÷õð÷ôðöôïöôïöôïöôïöôîöôîöóîõóîõóîõóíõóíõóíõòíõòìõòìôòìôòìôòëôñëôñëôñëôñëóñêóñêóðêóðêóðéóðéóðéóðèóðèòïèòïèòïèòïçòïçòïçòîçñîçñîæñîæñîæñîæñíåñíåñíåñíåðíåðíäðíäðìäðìäðìãðìãðìãïìãïìãïëâïëâïëâïëâïëâïëáîëáîëáîêáîêáîêàîêàîêàîêàîéàîéßíéßíéßíéßíéßíéÞíéÞíèÞíèÞíèÞíèÞìèÝìèÝìèÝìèÝìèÝìçÝìçÜìçÜìçÜìçÜìçÜëçÜëçÜëçÛëçÛëæÛëæÛëæÛëæÛëæÛëæÚëæÚëæÚëæÚêæÚêæÚêæÚêåÚêåÙêåÙêåÙêåÙêåÙêåÙêåÙêåÙêåÙêåÙÞÙÎЭŒË d»¤`½¦`¾¥\½£V¼ O»žK¼žH½ŸJÀ£NçTÇ«[̱eÏ·mÒºuÖ¿{ÙÃÜÇŠÞËáΖãÑ›åÓŸæÕ¢çÖ¤ç×¥çÖ¤æÕ¢åÓŸãÑ›áΖÞË‘ÜÈ‹ÚÄ…×Á~Ó¼xѹrͳjÉ­_çT¾ I¹™=³’0¸Ž4Âd~{u²®¥×ÒÇèã×êåÙêåÙêåÙêåÙêåÙêåÙêåÙêåÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðëÝáÖ»áÖ»èáÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðëÝáÖ»áÖ»ðëÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷õîáÖ»áÖ»áÖ»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþýýþýýþýüýýüýýüýýüýýüýýüýýûýüûýüûýüûýüûüüúüüúüüúüüúüûúüûúüûùüûùüûùüûùûûùûúøûúøûúøûúøûúøûú÷ûú÷ûú÷úù÷úù÷úùöúùöúùöúùöúùöùøõùøõùøõùøõùøôùøôùøôù÷ôù÷óø÷óø÷óø÷óø÷óøöòøöòøöò÷öñ÷öñ÷öñ÷õñ÷õñ÷õð÷õð÷õðöõðöôïöôïöôïöôïöôîöóîõóîõóîõóíõóíõóíõòíõòíôòìôòìôòìôòëôñëôñëôñëóñêóñêóñêóðêóðéóðéóðéòðéòðèòïèòïèòïèòïçòïçñîçñîçñîæñîæñîæñîæñíåðíåðíåðíåðíäðíäðìäðìäðìäïìãïìãïìãïëâïëâïëâïëâîëâîëáîêáîêáîêáîêàîêàîêàíêàíéßíéßíéßíéßíéßíéÞíéÞíèÞìèÞìèÞìèÝìèÝìèÝìèÝìèÝìçÜëçÜëçÜëçÜëçÜëçÛëçÛëæÛëæÛëæÛêæÚêæÚêæÚêæÚêæÚêåÚêåÙêåÙêåÙêåÙêåÙéåÙéåØéåØéäØéäØéäØéäØéäØéä×éä×éä×éä×éä×èä×èã×èãÖèãÖèãÖèãÖèãÖèãÖèãÖèãÖèãÖçâÕÖÐÃß j¸žZ¹¡Y»¢Xº R¹žM¹œG¹›DºœD¼žGÀ¢LçTÉ­^Í´hзpÔ¼w×ÁÛŇÞÉŽàÍ•ãÑ›åÔ ç×¥éÙ¨êÚªêÚ«êÚªéÙ¨ç×¥åÔ ãÑ›àÍ•ÞÉŽÛň×ÂÕ½zѹrεkÊ®`çT¾ H¸˜;².­Š$ÓšXˆxg£Ÿ–ÎɾäßÓèãÖèãÖèãÖèãÖèãÖèãÖèãÖèãÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿþþÿþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþýýþýýþýüýýüýýüýýüýýüýýüýýûýüûýüûýüûüüûüüûüüúüüúüûúüûúüûùüûùüûùüûùûûùûûøûúøûúøûúøûúøûú÷ûú÷úù÷úù÷úù÷úùöúùöúùöúùöúøõùøõùøõùøõùøôùøôù÷ôù÷ôø÷óø÷óø÷óø÷óøöòøöòøöò÷öò÷öñ÷öñ÷õñ÷õñ÷õð÷õðöõðöõðöôïöôïöôïöôïõóîõóîõóîõóîõóíõóíõóíôòìôòìôòìôòìôòëôñëôñëóñêóñêóñêóñêóðéóðéòðéòðéòïèòïèòïèòïèñïçñïçñïçñîæñîæñîæñîæðîåðíåðíåðíåðíäðíäðìäïìäïìãïìãïìãïëâïëâîëâîëâîëáîëáîêáîêáîêàíêàíêàíêàíéßíéßíéßíéßìéÞìéÞìèÞìèÞìèÝìèÝìèÝëèÝëçÜëçÜëçÜëçÜëçÛëçÛëçÛëæÛêæÛêæÚêæÚêæÚêæÚêåÚêåÙêåÙéåÙéåÙéåÙéåØéåØéäØéäØéä×éä×èä×èä×èä×èã×èãÖèãÖèãÖèãÖèãÖçãÖçãÕçãÕçãÕçâÕçâÕçâÕçâÕçâÔçâÔçâÔæâÔæâÔæáÔæáÔæáÔæáÓæáÓæáÓæáÓæáÓæáÓæáÓâÝÐÔ¼£Ðb³›S¶Q·O¸œJ·šE¶˜@·˜?¹›@¹›B¹œFºŸM¿¥Vê`Æ®fɳnÍ·uл|Ò¿ƒÕÂ‰ØÆßÏ›çרëÜ®ìݰíÞ±ìݰëÜ®éÙªç×¥åÓŸâϘßË‘ÜÇŠÙÃԾ{Ì´nÄ«aÀ¥Wº M¸™B´”8±/­Š&¿<®‡d–’‰Ä¿´ßÚÌæáÓæáÓæáÓæáÓæáÓæáÓæáÓæáÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþþÿþþþþþþþþþþþþþþþþþþþýþþýþþýþýýþýýþýýþýüýýüýýüýýüýýüýýûýüûýüûýüûýüûüüûüüúüüúüûúüûúüûùüûùüûùüûùûûùûûùûúøûúøûúøûú÷ûú÷úú÷úù÷úù÷úùöúùöúùöúùöùøõùøõùøõùøõùøôùøôù÷ôø÷ôø÷óø÷óø÷óø÷óøöòøöò÷öò÷öò÷öñ÷õñ÷õñ÷õðöõðöõðöôðöôïöôïöôïõôîõôîõóîõóîõóíõóíõóíôòíôòìôòìôòìôòëôñëóñëóñëóñêóñêóðêóðéòðéòðéòðéòïèòïèòïèñïçñïçñïçñîæñîæðîæðîæðîåðíåðíåðíäðíäïíäïìäïìãïìãïìãïìâîëâîëâîëâîëâîëáîêáíêàíêàíêàíêàíéßíéßíéßìéßìéÞìéÞìèÞìèÞìèÝìèÝëèÝëçÜëçÜëçÜëçÜêçÛêçÛêæÛêæÛêæÚêæÚêæÚêæÚéåÙéåÙéåÙéåÙéåØéåØéäØèäØèäØèä×èä×èä×èä×èãÖçãÖçãÖçãÖçãÕçãÕçâÕçâÕçâÕçâÔçâÔæâÔæâÔæâÔæáÔæáÓæáÓæáÓæáÓåáÓåáÒåáÒåáÒåàÒåàÒåàÒåàÑåàÑåàÑåàÑäàÑäàÑäàÑäßÑäßÐäßÐäßÐäßÐäßÐäßÐäßÐßÚË׬„¾˜W°—L±—H±–D¯“>®8­4ªŒ1§‹1¬“E¯t;ŒÐÁ’ÑÖÓÅšÕÈž×Ê¢ØË¥ÙΩÚЬÜѯÕȞȸ„ÒÁŽàÑ¡áÒ¤áÑ£ßРÝΜÛË—ÙÇ’ÖÄ‹ÓÀ…Ѽ~íl¸¢_Ƶ}Ñ×ÐÁ‘̺…DztÀ§^¸œE±.­ˆ#ß iŒˆ~»·ªÙÕÆäßÐäßÐäßÐäßÐäßÐäßÐäßÐäßÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþþþþþþþþþþþþþþþþýþþýþþýþþýþýýþýüþýüýýüýýüýýüýýüýýûýüûýüûýüûýüûüüúüüúüûúüûúüûùüûùüûùüûùûûùûúøûúøûúøûúøûú÷ûú÷úù÷úù÷úùöúùöúùöúùöúøõúøõùøõùøõùøôù÷ôù÷ôù÷óù÷óø÷óø÷óøöòøöòøöòøöò÷öñ÷õñ÷õñ÷õð÷õð÷õðöôïöôïöôïöôïöóîöóîöóîõóíõóíõóíõòíõòìõòìôòìôòëôñëôñëôñêóñêóðêóðêóðéóðéóïèòïèòïèòïèòïçòïçòîçñîæñîæñîæñíåñíåñíåðíäðìäðìäðìãðìãðìãïëâïëâïëâïëâïëáïêáîêáîêàîêàîêàîéßîéßíéßíéÞíèÞíèÞíèÝìèÝìèÝìèÝìçÜìçÜìçÜìçÜëæÛëæÛëæÛëæÚëæÚëåÚêåÙêåÙêåÙêåÙêäØêäØéäØéäØéä×éä×éã×éãÖéãÖèãÖèãÖèâÕèâÕèâÕèâÕèâÔèâÔçáÔçáÔçáÓçáÓçáÓçáÓçàÒæàÒæàÒæàÒæàÑæàÑæàÑæßÑæßÑåßÐåßÐåßÐåßÐåÞÐåÞÏåÞÏåÞÏåÞÏåÞÏäÞÎäÞÎäÞÎäÝÎäÝÎäÝÎäÝÍäÝÍäÝÍãÝÍãÝÍãÜÍãÜÌãÜÌãÜÌãÜÌãÜÌãÜÌãÜÌãÜÌÛÔÅÝ¢p¯’I¯•H´›N¸ X¼¦bÁ¬nÀ®u¾¯{Ƚ˜ØÒ¾èåÙûùöýüúýüúýüúýüúýüúýüúýüúýüúòðèâÝÎÚÕÁÓʬÕÌ«ÜÓ´àØ¹àÖ¸ßÕµÞÔ²ÜÒ°ÛЭ×˦ÐĜʿ™ØÓ¾ãÞÐóñêüûøöòåêàÃÚɘǯg¶—:ª†Ö›[“„sµ¯¢ÖÏÀãÜÌãÜÌãÜÌãÜÌãÜÌãÜÌãÜÌãÜÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊ·‡ŽgŽgŽgŽgŽgŽgšw»¤fûúöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿþþÿþþþþþþþþþþþþþýþþýþþýþýýþýüþýüýýüýýüýýüýýüýýûýüûýüûýüûýüûüüúüüúüüúüûúüûùüûùüûùûûùûûøûúøûúøûúøûúøûú÷ûù÷úù÷úù÷úùöúùöúøõúøõúøõùøõùøõùøôù÷ôù÷ôù÷óø÷óø÷óøöóøöòøöòøöò÷öñ÷õñ÷õñ÷õð÷õð÷õðöôïöôïöôïöôîöôîõóîõóîõóíõóíõòíõòìõòìôòìôòëôñëôñëôñêóñêóðêóðéóðéóðéóðéòïèòïèòïçòïçòîçñîæñîæñîæñíåñíåðíåðíåðíäðìäðìãðìãïìãïëâïëâïëâïëâîëáîêáîêáîêàîêàíéßíéßíéßíéßíéÞíèÞìèÞìèÝìèÝìçÜìçÜëçÜëçÜëæÛëæÛëæÛëæÚêæÚêåÚêåÚêåÙêåÙêåØéäØéäØéäØéä×éä×éã×èãÖèãÖèãÖèãÕèâÕèâÕçâÕçâÔçâÔçáÔçáÔçáÓçáÓæáÓæàÒæàÒæàÒæàÒæàÑåàÑåßÑåßÑåßÐåßÐåßÐåÞÐäÞÏäÞÏäÞÏäÞÏäÞÏäÝÎäÝÎäÝÎãÝÍãÝÍãÝÍãÝÍãÜÍãÜÌãÜÌãÜÌãÜÌâÜÌâÜËâÛËâÛËâÛËâÛËâÛËâÛÊâÛÊâÛÊâÛÊáÚÊáÚÊáÚÉáÚÉáÚÉáÚÉáÚÉáÚÉáÚÉØÊ³×œc§@¯•F¼¥_͹ÜÎ¥ëâÉ÷òèýû÷ýûøûøóçãÕÕйíéÞûùôûùôûùôûùôûùôûùôåáÒÛÕÂðíäýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøïìãÚÔÀæáÒôïáéÞ¿ÙǕǮf¶–;ª‡Æ“F¨‰l°«žÒ˼áÚÉáÚÉáÚÉáÚÉáÚÉáÚÉáÚÉáÚÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôñçèáÌèáÌèáÌèáÌèáÌäÛò—QŽgÇ´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÐôG£éz½ïüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþþþþþþþþþþþþþýþþýþþýþþýþýýýýüýýüýýüýýüýýüýýûýüûýüûýüûüüûüüúüüúüûúüûùüûùüûùüûùûûùûúøûúøûúøûú÷ûú÷úù÷úù÷úù÷úùöúùöúùöúøõùøõùøõùøõùøôùøôù÷ôø÷óø÷óø÷óøöòøöòøöò÷öò÷öñ÷õñ÷õñ÷õð÷õðöôðöôïöôïöôïöôîõóîõóîõóíõóíõòíôòìôòìôòìôòëôñëôñëóñêóñêóðêóðéóðéòðéòïèòïèòïèòïçñîçñîæñîæñîæñîæñíåðíåðíåðíäðìäïìãïìãïìãïëâïëâïëâîëáîëáîêáîêàîêàíéßíéßíéßíéßìèÞìèÞìèÝìèÝìèÝìçÜëçÜëçÜëçÛëæÛëæÛêæÚêæÚêåÚêåÙêåÙéåÙéåØéäØéäØéä×éä×èã×èãÖèãÖèãÖèãÕçâÕçâÕçâÕçâÔçáÔçáÓæáÓæáÓæáÓæàÒæàÒæàÒåàÑåàÑåßÑåßÑåßÐåßÐäßÐäÞÏäÞÏäÞÏäÞÎäÞÎäÞÎãÝÎãÝÎãÝÍãÝÍãÜÌãÜÌâÜÌâÜÌâÜÌâÜËâÜËâÛËâÛËâÛÊáÛÊáÛÊáÛÊáÚÊáÚÉáÚÉáÚÉáÚÉàÚÈàÚÈàÚÈàÙÈàÙÈàÙÈàÙÇàÙÇàÙÇàÙÇàÙÇߨÇߨÆßØÆßØÆßØÆßØÆßØÆßØÆØÀ¤Ì˜[¤‰9«‘?º¡WɵzØÊçÞÂóîàùöîùöïùöïùöïìç×¾²„ÙΩÞÓ±ßÔ³àÖ¶á׸ÓÈ£Ðǧ÷ôìùöïùöïùöïùöïùöïùöïùöïùöïùöïùöïùöïùöïùöïùöïùöïùöï÷óëɾ—̼оˆÇ°m¼¡O²’2ª†½8¶Žj­¨šÏɏߨÆßØÆßØÆßØÆßØÆßØÆßØÆßØÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïêÛÒ™çßÉÿÿÿÿÿÿÿÿÿÖȢ˹ŠîèØÿÿÿÿÿÿüûùÒ™×É¥ÿÿÿÿÿÿÿÿÿöòêÛϮҙҙҙº¢cŽg½¦iÿÿÿ®Öõ£Ñô£Ñô£Ñô£Ñô£ÑôÃáøôúþÿÿÿÿÿÿÿÿÿÌåù£Ñô£Ñô£Ñô£Ñô}¾ðŒä]®ì÷ûþÿÿÿÿÿÿçóü«Õõ£Ñô£Ñô£Ñô£Ñô£ÑôôúþßïûÑèúéôüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþþþþþþþþþþþþþýþþýþþýþþýþýýþýüýýüýýüýýüýýûýüûýüûýüûýüûüüúüüúüûúüûùüûùüûùüûùüûùûúøûúøûúøûú÷ûú÷ûú÷úù÷úù÷úùöúùöúùöúøõùøõùøôùøôù÷ôù÷ôù÷óø÷óø÷óøöóøöòøöò÷öñ÷öñ÷õñ÷õñ÷õð÷õðöôïöôïöôïöôîöóîöóîõóíõóíõòíõòìõòìôòìôñëôñëôñëôñêóñêóðêóðéóðéóðéòïèòïèòïçòïçòîçñîæñîæñîæñíåñíåðíäðíäðìäðìãðìãïìãïëâïëâïëâïëáîêáîêàîêàîêàîéßíéßíéßíéÞíèÞìèÝìèÝìèÝìçÜìçÜìçÜëçÛëæÛëæÛëæÚëæÚêåÙêåÙêåÙêåØêäØéäØéä×éä×éã×éãÖèãÖèãÖèâÕèâÕèâÕçâÔçáÔçáÓçáÓçáÓæàÒæàÒæàÒæàÑæàÑæßÑæßÑåßÐåßÐåßÏåßÏåÞÏäÞÏäÞÎäÞÎäÝÎäÝÍäÝÍãÝÍãÜÌãÜÌãÜÌãÜÌãÜËâÛËâÛËâÛËâÛÊâÛÊâÛÊáÚÉáÚÉáÚÉáÚÉáÚÈáÚÈáÙÈáÙÈàÙÇàÙÇàÙÇàÙÇàØÇàØÆàØÆàØÆßØÆßØÅß×Åß×Åß×Åß×Åß×Äß×Äß×Äß×ÄÞÖÄÞÖÃÞÖÃÞÖÃÞÖÃÞÖÃÞÖÃÞÖÃÞÖÃÚ¸•Á’Q¡…3£†/¤†+¢ƒ'ž€#™~(Í¡ÖγàØÃóíßõðãõðäÎÀ”²šOÁªcÅ®iȲqÉ´v´¡aîèÖõðäõðäëåÔÚÒºÓʰÌã³£jʺ…ȸ‚Ƕ~ųy¯]Ƚ˜Ó˰ÚÒºíçÖõðäõðäéàÊ¥Œ<µ˜A³”9±0®‹&©†´ˆ,Å”j¬¥—ÎǵÞÖÃÞÖÃÞÖÃÞÖÃÞÖÃÞÖÃÞÖÃÞÖÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×É¥ŽgïxÿÿÿÿÿÿνŽgŽg›yúøóÿÿÿøöðŽg›yÿÿÿòíᣄ0Žg¡€*¥…3¥…3¥…3{!Žg½¦iÿÿÿ]®ìG£éG£éG£éG£é<žèŒä'“æÎçùÿÿÿjµîŒäåG£éD¢éŒäŒäŒä9œèï÷ýñøýL¦êŒä'“æG£éG£éG£éG£éG£éÙìû®Öõ˜ÌóÎçùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþþþþþþþþþþþþþýþþýþþýþþýþýýýýüýýüýýüýýüýýûýüûýüûýüûüüúüüúüüúüûúüûùüûùûûùûûùûûøûúøûúøûúøûú÷úú÷úùöúùöúùöúùöúùöúøõùøõùøõùøôùøôù÷ôø÷óø÷óø÷óøöòøöò÷öò÷öñ÷öñ÷õñ÷õð÷õðöõðöôïöôïöôîöôîõóîõóíõóíõóíõòíôòìôòìôòìôñëóñëóñëóñêóñêóðéóðéòðéòðèòïèòïçòïçñîæñîæñîæñîåðîåðíåðíäðíäïìäïìãïìãïìâïëâïëâîëáîëáîêáîêàíêàíêßíéßíéßíéÞìéÞìèÝìèÝìèÝëçÜëçÜëçÜëçÛëæÛêæÛêæÚêæÚêåÙêåÙéåÙéåØéäØéä×èä×èä×èãÖèãÖèãÖçãÕçãÕçâÕçâÔçâÔæâÓæáÓæáÓæáÒæáÒåàÒåàÒåàÑåàÑåßÐåßÐäßÐäßÏäÞÏäÞÏäÞÎãÞÎãÞÎãÝÍãÝÍãÝÍâÝÌâÝÌâÜÌâÜËâÜËáÜËáÜËáÛÊáÛÊáÛÊáÛÊáÚÉàÚÉàÚÈàÚÈàÚÈàÙÈàÙÇßÙÇßÙÇßÙÇßÙÆßØÆßØÆßØÆÞØÅÞØÅÞØÅÞ×ÅÞ×ÅÞ×ÄÞ×ÄÞ×ÄÝ×ÃÝ×ÃÝÖÃÝÖÃÝÖÃÝÖÂÝÖÂÝÖÂÝÖÂÜÕÂÜÕÂÜÕÁÜÕÁÜÕÁÜÕÁÜÕÁÜÕÁÜÕÁÚ´Ž¹H,¡ƒ)¤…'¦…%¥„"¸ XìäÌìäÑÕ̪¸¦kðê×ðê×çÞ÷žSѹrÕ¾zÙÂԿ̽Šðê×ðê×ìæÐ¶¦oäÝÆïéÖòìÛÏÀæ×¨éÙªç×¥äҞıvòìÛïèÖãÜij¡eíæÑðê×ïéÕº¤]»œD¸˜;³’0®‹&©…¯†$Θj¬¦—ÍÇ´ÜÕÁÜÕÁÜÕÁÜÕÁÜÕÁÜÕÁÜÕÁÜÕÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×É¥Žgïxÿÿÿüûù™u–rº¢cŽgÏ¿“ÿÿÿøöðŽg›yÿÿÿÇ´Žgνÿÿÿÿÿÿÿÿÿÿÿÿ×É¥Žg½¦iÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÐôŒäp·îáðüŒä!åï÷ý÷ûþe²íŒä‹ÅñƒÁðŒä±Øö«ÕõŒäx»ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿþþþþþþþþþþýþþýþþýþþýþþýþýüýýüýýüýýüýýüýýûýüûýüûýüûüüúüüúüüúüûúüûùüûùüûùûûùûûøûúøûúøûú÷ûú÷úù÷úù÷úùöúùöúùöúøõùøõùøõùøôùøôù÷ôù÷ôø÷óø÷óø÷óøöòøöòøöñ÷öñ÷õð÷õð÷õðöôïöôïöôïöôîöôîõóîõóíõóíõóíõòìôòìôòìôòëôñëôñëóñêóñêóðéóðéóðéòïèòïèòïçòïçòïçñîæñîæñîæñíåðíåðíäðíäðìãïìãïìãïìâïëâïëáîëáîêáîêàîêàíêßíéßíéßíéÞíéÞíèÞìèÝìèÝìçÜìçÜëçÜëçÛëæÛëæÚêæÚêåÙêåÙêåÙêåÙéåØéä×éä×éä×éäÖèãÖèãÕèãÕèâÕçâÔçâÔçâÔçáÓçáÓæáÒæáÒæàÒæàÑæàÑåàÑåßÐåßÐåßÐäßÏäÞÏäÞÎäÞÎäÞÎãÝÍãÝÍãÝÍãÝÌãÜÌãÜÌâÜÌâÜËâÜËâÛÊâÛÊáÛÊáÛÉáÚÉáÚÈáÚÈáÚÈàÚÈàÙÇàÙÇàÙÇàÙÆàÙÆßØÆßØÆßØÅߨÅß×Åß×Åß×ÄÞ×ÄÞ×ÄÞ×ÃÞÖÃÞÖÃÞÖÃÝÖÂÝÖÂÝÖÂÝÕÂÝÕÂÝÕÁÝÕÁÜÕÁÜÕÁÜÕÀÜÔÀÜÔÀÜÔÀÜÔÀÜÔ¿ÜÔ¿ÜÔ¿ÛÔ¿ÛÓ¿ÛÓ¿ÛÓ¾ÛÓ¾ÛÓ¾ÛÓ¾ÛÓ¾Ú²Š´‰B™|%ž~#¡!¤ƒ¡€º£\èÞÃîåÐјª•MìãÊìãËçÜÀ´œOзoÔ¼w×ÁÑ»|νŠìãËìãËæÜ¾° cíåÏîæÑîæÑÐÀâÒ ç×¥åÔ àΗųzîæÑîæÑîæÑ¦KæÛ¾ìãËìâʽ¦a¹›A·—9².­Š$¨„ªƒÔ›j®¨—ÍÆ²ÛÓ¾ÛÓ¾ÛÓ¾ÛÓ¾ÛÓ¾ÛÓ¾ÛÓ¾ÛÓ¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×É¥ŽgïxÿÿÿÖÈ¢Žg­uïêÛiž|$þýüøöðŽg›yÿÿÿïxŽg×É¥ÿÿÿÿÿÿÿÿÿÿÿÿ×É¥Žg½¦iÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÚöŒäjµîÖëúŒä4šçÿÿÿï÷ýÑèúÙìûÿÿÿ ÐôŒä®Öõ ÐôŒä“Éòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþþÿþþþþþþþþþþþþþýþþýþþýþýýþýýýýüýýüýýüýýüýüûýüûýüûüüûüüúüüúüûúüûùüûùüûùûûùûúøûúøûúøûú÷ûú÷úù÷úù÷úùöúùöúùöùøõùøõùøôùøôù÷ôù÷ôø÷óø÷óø÷óøöòøöò÷öò÷öñ÷õñ÷õñ÷õðöõðöôïöôïöôïöôîõóîõóîõóíõóíõòíôòìôòìôòìôñëôñëóñêóñêóðéóðéòðéòïèòïèòïèñïçñîçñîæñîæñîæðíåðíäðíäðìäïìãïìãïìãïëâîëâîëáîêáîêáîêàíêàíéßíéßíéßìèÞìèÞìèÝìèÝëçÜëçÜëçÜëæÛêæÛêæÚêæÚêåÚêåÙéåÙéäØéäØéäØèä×èã×èã×èãÖçâÕçâÕçâÕçâÔçáÔæáÔæáÓæáÓæàÒåàÒåàÒåßÑåßÑåßÐäßÐäßÐäÞÏäÞÏãÞÎãÝÎãÝÎãÝÍãÝÍâÜÍâÜÌâÜÌâÛËâÛËáÛËáÛÊáÛÊáÚÊáÚÉàÚÉàÚÉàÙÈàÙÈàÙÈßÙÇߨÇߨÇߨÆßØÆßØÆÞ×ÅÞ×ÅÞ×ÅÞ×ÄÞ×ÄÝÖÄÝÖÃÝÖÃÝÖÃÝÖÃÝÖÃÜÕÂÜÕÂÜÕÂÜÕÁÜÔÁÜÔÁÜÔÀÜÔÀÛÔÀÛÔÀÛÓ¿ÛÓ¿ÛÓ¿ÛÓ¿ÚÓ¾ÚÓ¾ÚÒ¾ÚÒ¾ÚÒ¾ÚÒ¾ÚÒ½ÚÒ½ÙÒ½ÙÒ½ÙѼÙѼÙѼÙѼÙѼÙѼÙѼ۱‰°†=–xšyœ{œ{›{¨>â×µêàÆÒşŵ€çݾçݾ×Éœ·žQ϶lÒºtÖ¿{Õ¿~ðsçݾçݾçݾ½®yãÚ½êáÇäÚ¾¸¨nϾ†Ñ¿‡Ï½„ͺ²Ÿ]âØ»êáÇâÙ¼»«sçݾçݾæÛ¼°—Gº›@¶•6±+¬ˆ"§ƒ©‚Õœk±ª™ÎƲÙѼÙѼÙѼÙѼÙѼÙѼÙѼÙѼÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×É¥Žgïxÿÿÿ¢‚-iòíáÿÿÿ±•NŽgÚÍ«øöðŽg›yÿÿÿïxŽg×É¥ÿÿÿÿÿÿÿÿÿÿÿÿ×É¥Žg½¦iÿÿÿÿÿÿÆãøÈòuºïuºïjµî)”æä«ÕõÖëúŒä4šçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÐôŒä®Öõ ÐôŒä“Éòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþþþþþþþþþþþþþýþþýþþýþþýþýýýýüýýüýýüýýûýýûýüûýüûýüûüüúüüúüüúüûúüûùüûùûûùûûùûúøûúøûúøûú÷úù÷úù÷úùöúùöúùöùøõùøõùøõùøôùøôù÷ôø÷óø÷óø÷óøöòøöòøöò÷öñ÷õñ÷õð÷õðöõðöôïöôïöôïöôîõóîõóîõóíõóíôòìôòìôòìôòëôñëóñêóñêóðêóðéòðéòðèòïèòïçòïçñïçñîæñîæñîæðíåðíåðíäðíäïìãïìãïìãïëâîëâîëáîëáîêàîêàíêàíéßíéßìéÞìéÞìèÞìèÝìèÝëçÜëçÜëçÛëæÛêæÚêæÚêæÚêåÙéåÙéåØéäØéä×éä×èä×èãÖèãÖèãÖçâÕçâÕçâÔçâÔæáÓæáÓæáÓæàÒåàÒåàÑåàÑåßÑåßÐäßÐäßÏäÞÏäÞÏãÞÎãÝÎãÝÍãÝÍãÝÍâÜÌâÜÌâÜÌâÜËáÛËáÛÊáÛÊáÛÊáÚÉàÚÉàÚÉàÚÈàÙÈßÙÇßÙÇßÙÇߨÆßØÆßØÆÞØÅÞ×ÅÞ×ÅÞ×ÄÞ×ÄÞ×ÄÝÖÃÝÖÃÝÖÃÝÖÂÝÖÂÜÕÂÜÕÂÜÕÁÜÕÁÜÔÀÜÔÀÛÔÀÛÔÀÛÔ¿ÛÔ¿ÛÓ¿ÛÓ¿ÚÓ¾ÚÓ¾ÚÓ¾ÚÒ¾ÚÒ½ÚÒ½ÚÒ½ÚÒ½ÙÒ¼ÙѼÙѼÙѼÙÑ»ÙÑ»ÙÑ»ØÑ»ØÑ»ØÐ»ØÐºØÐºØÐºØÐºØÐºØÐºÛ´Œ²…=‘s—x ‚%«7´šK²šR;‘çܽá×¶½­zÐÀ‘âÖ±©“L©@²šN´T· Y¹£^®˜T̽ßӭ̾µˆçݽÛЬŸ‹ÏÀ‘ÖÇšÖÇ™ÕÆ˜ÕÆ—Í¿À²ÜÑ®çݾÐÛȹ‡ÞѪ±y¡†1ªŒ3ª‹,ª‰%¨†¥«‚Òœl¶¯ÐȳØÐºØÐºØÐºØÐºØÐºØÐºØÐºØÐºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×É¥ŽgïxßÔ·Žgµ›WÿÿÿÿÿÿãÙÀŽg§‰9øöðŽg›yÿÿÿïxŽg×É¥ÿÿÿÿÿÿÿÿÿÿÿÿ×É¥Žg½¦iÿÿÿ€¿ðŒä?Ÿèuºïuºïuºï›ÍóÙìûÿÿÿÖëúŒä4šçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÐôŒä®Öõ ÐôŒä“Éòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿþþþþþþþþþþþþþýþþýþýýþýýþýýýýüýýüýýüýýüýýûýüûýüûüüúüüúüüúüûúüûùüûùüûùûûùûúøûúøûú÷ûú÷ûú÷ûú÷úùöúùöúùöúøõùøõùøõùøôù÷ôù÷ôù÷óø÷óø÷óøöòøöòøöòøöñ÷öñ÷õñ÷õð÷õðöôïöôïöôïöôîöóîõóíõóíõóíõòìôòìôòìôñëôñëôñêóñêóñêóðéóðéòðèòïèòïçòïçòîæñîæñîæñîåñíåðíåðíäðìãðìãïìãïìâïëâïëáîëáîêàîêàîêàîêßíéßíéÞíéÞìèÝìèÝìèÜìçÜìçÜëçÛëçÛëæÛëæÚêæÙêåÙêåÙêåØéäØéä×éä×éä×éãÖèãÖèãÕèãÕçâÔçâÔçáÓçáÓçáÒæáÒæáÒæàÑæàÑåàÑåßÐåßÐåßÏåÞÏäÞÏäÞÎäÞÎäÝÍãÝÍãÝÌãÝÌãÜËãÜËâÜËâÛÊâÛÊâÛÊáÛÉáÚÉáÚÈáÚÈáÚÈàÙÇàÙÇàÙÆàÙÆßØÆßØÅߨÅß×Åß×Äß×ÄÞ×ÄÞ×ÃÞÖÃÞÖÃÞÖÂÝÖÂÝÕÁÝÕÁÝÕÁÝÕÁÜÔÀÜÔÀÜÔÀÜÔÀÜÔ¿ÜÔ¿ÜÓ¾ÜÓ¾ÛÓ¾ÛÓ¾ÛÓ½ÛÒ½ÛÒ½ÚÒ¼ÚÒ¼ÚÒ¼ÚѼÚѼÚÑ»ÚÑ»ÙÑ»ÙÑ»ÙѺÙкÙкÙйÙйÙйÙйØÐ¹ØÏ¹ØÏ¸ØÏ¸ØÏ¸ØÏ¸ØÏ¸ØÏ¸Û¶³…=Œm”u¤…+´šIíiѾ†¶¢bÓÅ䨶Úͧ̾”õ‰Ñě䨶䨶䨶䨶䨶ÝѬÏ™ŷ‹ÒÅßÓ°àԱõˆÔÇšßÒªßÒªßÒªßÒªßÒªßҪђʼ‘âÖ³áÔ±ÓÆžÅ·‹ÑÄ›ÛÌ¡ÖÄʲo»žK­Œ)¤®ƒ#Ïœo½µ¡ÓÊ´ØÏ¸ØÏ¸ØÏ¸ØÏ¸ØÏ¸ØÏ¸ØÏ¸ØÏ¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×É¥Žgïx®’HŽgãÙÀÿÿÿÿÿÿÿÿÿ£„0ŽgßÔ·Žg›yÿÿÿÏ¿“¥…3ßÔ·ÿÿÿÿÿÿÿÿÿÿÿÿßÔ·¥…3Ê·‡ÿÿÿ4šçŒäçóüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖëúŒä4šçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÐôŒä®Öõ ÐôŒä“Éòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüûùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿþþþþþþþþþþýþþýþýýþýýþýüþýüýýüýýüýýûýýûýüûýüûüüúüüúüüúüûúüûùüûùüûùûûøûúøûúøûú÷ûú÷úù÷úùöúùöúùöúùöúøõúøõùøôùøôùøôù÷ôø÷óø÷óø÷òøöòøöò÷öñ÷öñ÷õð÷õð÷õðöôïöôïöôïöôîöóîõóíõóíõóìõòìôòìôòëôòëôñêôñêóñêóðéóðéóðèòðèòïèòïçòïçñîæñîæñîåñîåñíåðíäðíäðìãðìãïìâïëâïëáïëáîêàîêàîêàîêßíéßíéÞíéÞíéÞìèÝìèÜìèÜìçÜëçÛëçÛëæÚëæÚêæÙêåÙêåØêåØêåØéä×éä×éäÖèãÖèãÕèãÕèãÕçâÔçâÓçâÓçáÓæáÒæáÒæàÑæàÑæàÑåßÐåßÏåßÏäßÏäÞÎäÞÎäÞÍäÞÍãÝÍãÝÌãÝÌãÜËâÜËâÜËâÛÊâÛÊâÛÉáÚÉáÚÈáÚÈáÚÈáÚÇàÙÇàÙÆàÙÆàÙÆßØÅߨÅߨÅß×Äß×ÄÞ×ÃÞ×ÃÞ×ÃÞÖÂÞÖÂÝÖÂÝÕÁÝÕÁÝÕÁÜÕÀÜÕÀÜÕÀÜÔ¿ÜÔ¿ÜÔ¾ÜÔ¾ÛÓ¾ÛÓ½ÛÓ½ÛÓ½ÛÒ½ÚÒ¼ÚÒ¼ÚÒ¼ÚÒ»ÚÑ»ÚÑ»ÚÑ»ÙѺÙѺÙкÙйÙйÙйÙйØÐ¹ØÐ¸ØÏ¸ØÏ¸ØÏ·ØÏ·ØÏ·ØÏ·×η×η×η×ζ×ζ×ζ×ζ×ζڽš¾‹Gˆh oŸ€$°•BÁ©bκ€ÓÁ¸¦kÛΦáÔ®áÔ®áÔ®áÔ®áÔ®áÔ®áÔ®áÔ®áÔ®áÔ®áÔ®áÔ®áÔ®áԮЗɸ†ÝΣÝΣÝΣÝΣÝΣÝΣÝΣÝΣñ{ÑØáÔ®áÔ®áÔ®áÓ­ÜÍ ÓÀˆÈ¯j¹œG¬‰&¢~¶‡.ÉuÄ»¦ÔÌ´×ζ×ζ×ζ×ζ×ζ×ζ×ζ×ζÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×É¥Žg¯”KŽg£„0ÿÿÿÿÿÿÿÿÿÿÿÿØË¨Žg­EŽg›yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?ŸèŒäáðüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäñüŒä!åßïûÿÿÿÿÿÿÿÿÿÿÿÿr¹îŒä¶Úö ÐôŒä“ÉòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüÒ™òíáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþþÿþþþþþþþþþþþþþýþþýþýýþýüýýüýýüýýüýýüýüûýüûýüûüüúüüúüûúüûùüûùüûùûûùûúøûúøûúøûú÷ûú÷úú÷úùöúùöúùöúùõúøõùøõùøôùøôù÷ôù÷óø÷óø÷óøöòøöò÷öñ÷öñ÷õð÷õð÷õðöõðöôïöôïöôîöóîõóîõóíõóíõòìôòìôòìôñëôñêóñêóñêóðéóðéóðéòðèòïèòïçòïçñîæñîæñîæñîåðíåðíäðíäðìãïìãïìãïëâïëáîëáîêáîêàîêàíéßíéßíéÞíéÞíèÞìèÝìèÜìçÜëçÜëçÛëæÛëæÚêæÚêæÙêåÙêåØéåØéäØéä×éä×èãÖèãÖèãÕèâÕçâÔçâÔçâÓçáÓæáÓæáÒæàÒæàÑåàÑåßÐåßÐåßÏäÞÏäÞÏäÞÎäÞÎãÝÍãÝÍãÝÌãÜÌãÜÌâÜËâÜËâÛÊáÛÊáÛÉáÛÉáÚÈáÚÈàÚÈàÚÈàÙÇàÙÆßÙÆßØÆßØÅߨÅߨÅÞ×ÄÞ×ÄÞ×ÄÞÖÃÞÖÃÝÖÂÝÖÂÝÕÁÝÕÁÜÕÁÜÕÀÜÔÀÜÔÀÜÔ¿ÜÔ¿ÛÓ¿ÛÓ¾ÛÓ¾ÛÓ½ÛÒ½ÚÒ½ÚÒ½ÚÒ¼ÚÒ¼ÚÒ¼ÚÑ»ÙÑ»ÙÑ»ÙÑ»ÙѺÙѺÙкØÐ¹ØÐ¹ØÐ¹ØÏ¸ØÏ¸ØÏ¸ØÏ¸×ϸ×Ï·×η×η×ζ×ζ×ζ×ζÖͶÖͶÖ͵Ö͵Ö͵Ö͵Ö͵Ö͵ØÄ¦Ì“V‚c‡fŠiŒjm‘op ’rµTÖÆ–ßѨßѨßѨßѨßѨßѨßѨßѨßѨßѨßѨßѨ×Ǘ¯r¹¤a»¦d¼§d¼¦d»¦a¹¤_¹£\· YµžU±šO¹£]ÑÀŒßѨßѨßЧÚʚѽƒÅ¬d·™Bª‡# |¿Œ<Å¢~ÉÁªÕÌ´Ö͵Ö͵Ö͵Ö͵Ö͵Ö͵Ö͵Ö͵ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜѱŽgŽgŽgÚÍ«ÿÿÿÿÿÿÿÿÿÿÿÿþýü¢‚-ŽgŽg£„0ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦Òõ!åŒäG£éG£éG£éG£éG£é}¾ðÿÿÿuºïŒäŒäŒäŒäŒäŒäŒäJ¤êôúþ ÐôŒä“ÉòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖÈ¢«ŽBðëÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿþþÿþþþþþþþþþþþþþýþþýþýýþýüþýüýýüýýüýýûýüûýüûýüûüüúüüúüûúüûùüûùüûùûûùûúøûúøûúøûú÷úú÷úùöúùöúùöúùöúùõúøõùøôùøôùøôù÷ôù÷óø÷óø÷òøöòøöñ÷öñ÷õñ÷õñ÷õð÷õðöõïöôïöôïöôîöóîõóíõóíõóìõòìôòìôòëôñëôñêôñêóñêóðéóðéòðèòðèòïçòïçòïçñîæñîæñîåñíåðíäðíäðíãðìãïìâïìâïëâïëáîëáîêàîêàîêßíéßíéÞíéÞíéÝíèÝìèÜìèÜìçÛëçÛëçÛëæÚëæÚêæÙêæÙêåØêåØéå×éä×éäÖéäÖèäÖèãÕèãÕèâÔçâÔçâÓçâÓçáÒæáÒæáÑæàÑæàÑåàÐåßÐåßÏåßÏäßÎäÞÎäÞÍäÞÍãÝÌãÝÌãÝÌãÜËãÜËâÜÊâÜÊâÛÉáÛÉáÛÉáÛÈáÚÈáÚÇàÚÇàÙÇàÙÆàÙÆßÙÅߨÅߨÄߨÄߨÄÞ×ÃÞ×ÃÞ×ÂÞÖÂÞÖÂÝÖÁÝÖÁÝÕÁÝÕÀÜÕÀÜÕ¿ÜÔ¿ÜÔ¿ÜÔ¿ÛÔ¾ÛÓ¾ÛÓ½ÛÓ½ÛÓ½ÛÓ½ÛÒ¼ÚÒ¼ÚÒ»ÚÒ»ÚÒ»ÚѺÙѺÙѺÙѹÙѹÙйÙйØÐ¸ØÐ¸ØÐ¸ØÏ¸ØÏ·ØÏ·ØÏ·×Ï·×϶×ζ×ζ×ζ×ε×εÖεÖ͵Ö͵Ö͵ÖÍ´ÖÍ´ÖÍ´ÖÍ´ÖÍ´Ö˱ܞf‚b„c‹j•s˜u ›v Ÿz ¢~ž|š{¢†,«>®”C°—I³›NµT· Yº¤]»¥a¼§d¾¨f¹¢\¿©b̵q×À~×À~×À~Ö¿|Õ¾zÔ¼wÒºtиpεk̲fÀ¦V²—F©;°—I®”C¬<ª4ª‰,¥ƒ¢Ÿz Í”OìŽÏÇ®ÖÍ´ÖÍ´ÖÍ´ÖÍ´ÖÍ´ÖÍ´ÖÍ´ÖÍ´ÖÍ´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÏ®½¦iÛÏ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðëÞǴȶ„òíáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéôü±Øö£Ñô£Ñô£Ñô£Ñô£Ñô¾ß÷ÿÿÿÿÿÿßïû¦Òõ£Ñô£Ñô£Ñô£ÑôÎçùÿÿÿÿÿÿÙìû£ÑôÔéúÿÿÿÿÿÿÿÿÿÿÿÿþýü³™T²—Qöòêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿþþþþþþþþþþþþþýþþýþþýþýýþýüýýüýýüýýüýýûýüûýüûýüûýüúüüúüüúüûùüûùüûùüûùûûøûúøûúøûú÷ûú÷ûú÷úùöúùöúùöúùõúøõúøõùøôùøôùøôù÷óù÷óø÷òø÷òø÷òøöñøöñ÷öð÷õð÷õð÷õï÷õïöôîöôîöôîöôíöôíõóìõóìõóìõòëõòëôòêôòêôñéôñéóñéóðèóðèóðçòðçòïçòïæòïåòîåñîåñîäñíäñíãðíãðíâðíâðìâïìáïìáïëàïëàîëßîêßîêÞîêÞîêÝíéÝíéÝíéÜíèÜìèÛìèÛìçÚìçÚëçÙëçÙëæØëæØêæ×êå×êåÖêåÖéäÖéäÕéäÕéäÔéãÔèãÓèãÓèâÒèâÒçâÑçáÑçáÑçáÐæáÐæáÏæàÏæàÎåàÎåßÎåßÍåßÌåÞÌäÞÌäÞËäÞËäÝÊãÝÊãÝÉãÜÉãÜÉãÜÈâÜÈâÛÇâÛÇâÛÇáÚÆáÚÆáÚÅáÚÅáÚÄàÙÄàÙÄàÙÃàÙÃàØÃߨÂߨÂߨÂß×Áß×ÁÞ×ÀÞ×ÀÞÖ¿ÞÖ¿ÞÖ¿ÞÖ¿ÝÕ¾ÝÕ¾ÝÕ½ÝÕ½ÝÕ½ÜÔ¼ÜÔ¼ÜÔ¼ÜÔ»ÜÔ»ÜÓ»ÛÓºÛÓºÛÓºÛÓºÛÒ¹ÛÒ¹ÚÒ¹ÚÒ¸ÚÒ¸ÚѸÚѸÚѸÚÑ·ÚÑ·ÙÑ·ÙѶÙжÙжÙеÙеÙеÙеØÏµØÏµØÏ´ØÏ´ØÏ´ØÏ´ØÏ´ØÏ´ØÏ´Þ¬~˜p‚b–xŸ‚+¤†+©Š.©‰)§…"¦‚¨„ª‡"¬‹)°0´”8·™A»ŸH¿£PçWÆ«^È®cɱg϶nѹrÒºtÓ»uÓ»uÓ»uÒºtѹrзo϶lͳh˰cȬ\ħTÀ£L»Dµ•:µ—>µ˜@¶™Cµ˜A­Œ. {žx ÝžfÆ»¢Õ̱ØÏ´ØÏ´ØÏ´ØÏ´ØÏ´ØÏ´ØÏ´ØÏ´ØÏ´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿË¹ŠšwßÔ·óïäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþþþþþþþþþþþþýþþýþþýþþýþþýþýüþýüýýüýýûýýûýýûýüûýüúýüúýüúüüúüüùüûùüûùüûùüûøüûøüûøûú÷ûú÷ûú÷ûúöûúöúùöúùöúùõúùõúùôúøôúøôùøóùøóù÷óù÷óù÷òù÷òø÷ñøöñøöðøöðøöð÷õï÷õï÷õï÷õî÷õî÷ôí÷ôíöôíöôìöóìöóìöóëõóëõòêõòêõòêõòéôñéôñéôñèôñèôñçóðçóðæóðæóðæóïåòïåòïäòïäòîãòîãòîãñîâñíâñíâñíáñíáðìàðìàðìßðëßïëÞïëÞïëÞïêÝïêÝîêÜîêÜîéÜîéÛîéÚíéÚíèÚíèÙíèÙíèÙìçØìç×ìç×ìæ×ìæÖëæÖëæÖëåÕëåÕëåÔêåÔêäÓêäÓêäÓêäÒéãÒéãÒéãÑéãÑéãÐèâÐèâÏèâÏèâÏèáÎèáÎçáÍçáÍçàÌçàÌçàÌæàËæßËæßËæßÊæßÊæßÊåÞÉåÞÉåÞÈåÞÈåÝÈäÝÇäÝÇäÝÇäÜÆäÜÆäÜÆãÜÅãÜÅãÜÅãÛÅãÛÄãÛÃãÛÃâÚÃâÚÃâÚÂâÚÂâÚÂáÙÁáÙÁáÙÁáÙÀáÙÀáÙÀáØÀáØ¿àØ¿àØ¿àØ¿àØ¾à×¾à×¾à×¾à×½ß×½ß×½ß×½ßÖ¼ßÖ¼ßÖ¼ßÖ¼ßÖ»ßÖ»ßÖ»ßÖ»ÞÕ»ÞÕ»ÞÕºÞÕºÞÕºÞÕºÞÕºÞÕºÞպ߾—´ƒ<`–z%¢‡5§Š6ª7®‘:²•?µ™C³•<±2°Ž+±,µ”3¸˜;¼CÀ¢JæQƪXÉ­^˰cͳgδjϵk϶l϶m϶lϵkδjͳg˰cÉ­^ƪXæQÁ¤NÂ¥SèYĪ]§Y¾¢R»ŸM¸œH±“9žy ­€"Õ¥xÔ˱ÝÔ¹ÞÕºÞÕºÞÕºÞÕºÞÕºÞÕºÞÕºÞÕºÞÕºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîèØúøóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúøóÜÑ±Ë¹Š§‰9Ç´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿþþþþþþþýþþýþþýþþýþþýþýüþýüþýüþýüýýûýýûýýûýüûýüûýüúýüúýüúýüúüüùüûùüûùüûùüûøüûøüú÷ûú÷ûú÷ûú÷ûúöûúöûúöûùõûùõúùõúùôúøôúøôúøóúøóùøóù÷óù÷òù÷òù÷òù÷ñùöñøöñøöðøöðøõïøõïøõï÷õî÷õî÷ôî÷ôí÷ôí÷ôìöôìöóìöóëöóëöóëöòêõòêõòêõòéõòéõñèôñèôñçôñçôðçôðæôðæóðæóïåóïåóïäóïäóîäòîãòîãòîâòíâòíáñíáñíáñìàñìàñìßðìßðëßðëÞðëÞðëÝðêÝïêÝïêÜïêÜïéÛïéÛîéÛîéÚîèÚîèÙîèÙíèØíçØíçØíç×íç×íç×ìæÖìæÖìæÕìåÕìåÕìåÔëåÔëäÓëäÓëäÓëäÒëäÒêãÑêãÑêãÑêãÐêâÐéâÐéâÏéâÏéâÎéáÎéáÎèáÍèáÍèàÍèàÌèàÌèàËçßËçßËçßÊçßÊçßÊçßÉæÞÉæÞÉæÞÈæÞÈæÞÈæÝÇæÝÇåÝÇåÝÆåÝÆåÜÆåÜÆåÜÅåÜÅäÜÅäÛÄäÛÄäÛÄäÛÄäÛÃäÛÃäÚÃãÚÂãÚÂãÚÂãÚÂãÚÁãÚÁãÙÁãÙÁãÙÀâÙÀâÙÀâÙÀâØ¿âØ¿âØ¿âØ¿âØ¿âØ¿âØ¾áØ¾áØ¾á×¾á×¾á×½á×½á×½á×½á×½á×½á×½áҶךa~_‘t¦ŒA©A­’A±•C´˜G·œKºŸN½¢S¿¥W¿¤V¾¡NºœD¹š>»œA¾ GÁ£MçSÆ©WȬ\É®_ʯa˰c˰c˰cʯaÉ®_Ȭ\È«\ȯc˱i̵o̵qʲlǯhŬb¨]¿¥W½¢SºŸN¯6šuÇGÒ¶”ÝÔºá×½á×½á×½á×½á×½á×½á×½á×½á×½á×½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÝÆìæÕÿÿÿÔÆŸëäÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒ™¾§lüûùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþþþþþÿþþþþþþþþþþýþþýþþýþþýþþýþýüþýüýýüýýüýýûýýûýýûýüûýüúýüúýüúýüúüüùüüùüûùüûùüûøüûøüûøüû÷ûú÷ûú÷ûúöûúöûúöûùöûùõûùõúùõúùôúùôúøôúøóúøóúøóùøòù÷òù÷òù÷ñù÷ñù÷ñùöðøöðøöðøöïøöïøõïøõî÷õî÷õí÷õí÷ôí÷ôí÷ôì÷ôìöôëöóëöóëöóêöóêõòêõòêõòéõòéõòèõñèôñçôñçôñçôðæôðæôðåóðåóðåóïäóïäóïäóïãòîãòîãòîâòîâòîáòíáñíàñíàñíàñìßñìßñìßðìÞðìÞðëÝðëÝðëÝðëÜïêÜïêÛïêÛïêÛïéÚîéÚîéÚîéÙîéÙîèØîèØîèØíè×íç×íçÖíçÖíçÖìæÕìæÕìæÕìæÔìæÔìåÔìåÓëåÓëåÓëåÒëäÒëäÒëäÑêäÑêãÐêãÐêãÐêãÏêãÏéãÏéâÎéâÎéâÍéâÍéâÍéáÍèáÌèáÌèáËèáËèáËèàËèàÊçàÊçàÊçàÉçßÉçßÉçßÈçßÈçßÈæÞÈæÞÇæÞÇæÞÇæÞÇæÞÆæÝÆæÝÆåÝÅåÝÅåÝÅåÝÅåÝÄåÜÄåÜÄåÜÄåÜÄäÜÃäÜÃäÜÃäÜÃäÛÂäÛÂäÛÂäÛÂäÛÁäÛÁäÛÁãÛÁãÚÁãÚÁãÚÀãÚÀãÚÀãÚÀãÚÀãÚÀãÚÀãÚÀãÚÀ᳇t$‚c§I­”M¯—M³šM¶P¹ŸS¼£W¿¥ZÁ¨^ëbÆ­fȰkʳpɱjÈ®eÇ­aƬ^È®bǬ]Ǭ]Ç­_˲g̳k̳kϸtкxÒ½Ó¿‚Ò½€Ð»|θx̶sʳoȱlÆ­fëcÁ¨^¿¥Z¼£W¤ƒ žvÝžfØÎ´âÙ¿ãÚÀãÚÀãÚÀãÚÀãÚÀãÚÀãÚÀãÚÀãÚÀãÚÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâØ½­Eª?æÝÆÁ«rÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÔ·±•NÓÄœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþþÿþþÿþþÿþþþþþþþýþþýþþýþþýþþýþþüþýüþýüþýüþýüýýûýýûýýûýüûýüúýüúýüúýüúýüùýüùüüùüûùüûøüûøüûøüûøüû÷üú÷ûú÷ûúöûúöûúöûúöûùõûùõûùõûùõúùôúøôúøóúøóúøóúøóúøòúøòù÷òù÷ñù÷ñù÷ñù÷ðùöðùöðøöïøöïøöïøöïøõîøõî÷õí÷õí÷ôí÷ôì÷ôì÷ôì÷ôëöóëöóëöóêöóêöóêöòéöòéõòéõòèõòèõñçõñçõñçõñæôñæôðæôðåôðåôðåôðäôïäóïãóïãóïãóîâóîâóîâòîáòîáòíàòíàòíàòíßòíßñìßñìÞñìÞñìÞñìÝñëÝðëÝðëÜðëÜðëÜðêÛðêÛðêÚïêÚïêÚïéÙïéÙïéÙïéØîèØîè×îè×îè×îè×îèÖîçÖíçÖíçÕíçÕíçÕíæÔíæÔíæÔìæÓìæÓìåÓìåÒìåÒìåÒìåÑìåÑëäÑëäÐëäÐëäÐëäÏëãÏëãÏêãÎêãÎêãÎêãÎêâÍêâÍêâÍêâÍéâÌéâÌéáÌéáËéáËéáËéáËéáÊéàÊéàÊèàÊèàÊèàÉèàÉèàÉèàÈèßÈèßÈèßÈçßÇçßÇçßÇçßÇçßÇçÞÇçÞÆçÞÆçÞÆçÞÆçÞÆçÞÅæÞÅæÞÅæÝÅæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄåήΔW|^™0°›Z³Z·ŸZ¹¢[¼¤]¿§`ªcìfÅ®jǰmɳq˵tÍ·xι{л~Ò½Ó¿„ÔÀ†ÕÁˆÕ‰ÖŠÖÊÖŠÖ‰ÕÁˆÔÀ†Ó¿„ҽл~ι{Í·x˵tɳqǰmÅ®jìfªc¸O˜s¼‰9Ú¶âÚÁæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄæÝÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ž]øöð™u±•NúøóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçßÉŸ~'Á«röòêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿþþþþþþþýþþýþþýþþýþþýþþüþþüþýüþýüþýüþýûþýûýýûýýûýýûýüúýüúýüúýüúýüúýüùýüùýüùüûøüûøüûøüûøüû÷üû÷üû÷üú÷üúöüúöûúöûúöûúõûùõûùõûùôûùôûùôúùóúùóúøóúøòúøòúøòúøòúøòú÷ñù÷ñù÷ñù÷ðù÷ðùöðùöïùöïùöïøöîøöîøõîøõíøõíøõíøõìøõì÷ôì÷ôë÷ôë÷ôë÷ôê÷óê÷óêöóéöóéöóéöòèöòèöòèöòçöòçõòçõñæõñæõñæõñåõñåõðäôðäôðäôðäôðãôïãôïâôïâôïâóïâóîáóîáóîàóîàóîàóíßòíßòíßòíÞòíÞòíÞòìÝòìÝòìÝñìÜñìÜñëÜñëÛñëÛñëÛñëÚñëÚðêÚðêÙðêÙðêÙðêØðéØðéØïé×ïé×ïé×ïéÖïèÖïèÖïèÕïèÕîèÕîèÕîçÔîçÔîçÔîçÓîçÓîæÓíæÒíæÒíæÒíæÒíæÑíåÑíåÑíåÐíåÐíåÐìåÐìåÏìäÏìäÏìäÎìäÎìäÎìäÎìäÎìäÍëãÍëãÍëãÍëãÌëãÌëãÌëãÌëâËëâËëâËêâËêâËêâÊêâÊêâÊêáÊêáÊêáÉêáÉêáÉêáÉêáÉêáÈéáÈéáÈéàÈéàÈéàÈéàÇéàÇéàÇéàÇéàÇéàÇéàÇéàÇéàÇéàÇã·‹t%€c®™Z·£g¹¤f¼§g¿©gÁ«iÄ®mưoDzqÉ´u˶wÍ·yι|л€Ñ½‚Ò¾„Ó¿†ÔÁˆÔÁ‰Õ‹ÖÃŒÖÌ֋ՋՊÔÁ‰ÓÀ‡Ò¿…ѽ‚лι|Í·z˵vÉ´uȲrŰnÄ­l¢œt Üžeã×½è߯éàÇéàÇéàÇéàÇéàÇéàÇéàÇéàÇéàÇéàÇéàÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³™Tÿÿÿ­Eȶ„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥…3äÛÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿþþÿþþÿþþþþþþþýþþýþþýþþýþþýþþüþþüþýüþýüþýüþýüþýûþýûýýûýýûýüúýüúýüúýüúýüúýüùýüùýüùýüùüûøüûøüûøüûøüû÷üû÷üû÷üû÷üúöüúöûúöûúöûúõûúõûúõûùõûùôûùôûùôûùôûùóúùóúøóúøóúøòúøòúøòúøñú÷ñú÷ñù÷ñù÷ðù÷ðù÷ðù÷ïùöïùöïùöîøöîøöîøõíøõíøõíøõìøõìøõìøõì÷ôë÷ôë÷ôë÷ôê÷ôê÷óê÷óé÷óéöóéöóèöóèöòèöòçöòçöòçöòæõòæõñæõñæõñåõñåõñåõñäõðäôðãôðãôðãôðãôðâôïâôïâôïáóïáóïáóïàóîàóîàóîßóîßóîßóîßòíÞòíÞòíÞòíÝòíÝòíÝòìÜòìÜñìÜñìÛñìÛñëÛñëÚñëÚñëÚñëÚðëÙðëÙðêÙðêØðêØðêØðêØðê×ðé×ðé×ïéÖïéÖïéÖïéÖïéÕïèÕïèÕïèÕïèÔîèÔîèÔîèÓîçÓîçÓîçÓîçÓîçÒîçÒîçÒîçÒíæÑíæÑíæÑíæÑíæÑíæÐíæÐíæÐíåÏíåÏíåÏìåÏìåÏìåÎìåÎìåÎìåÎìäÎìäÍìäÍìäÍìäÍìäÍìäÍìäÍëäÌëäÌëäÌëãÌëãÌëãÌëãËëãËëãËëãËëãËëãËëãËëãËëãËëãËèÓ´Õ™_`Œpº¨q½ªr¿¬r®rįsƲuȳwʵyË·{͸~κϼ‚ѽ„Ò¾†ÓÀˆÔÁŠÔ‹ÕÃÖÃŽÖÃŽÖÃŽÖÃŽÖÃŽÕÃÔ‹ÔÁŠÓÀˆÒ¾†Ñ½„ϼ‚κ€Í¸~Ë·{ʶzȳw°”@”oÄŽEབྷéáÉëãËëãËëãËëãËëãËëãËëãËëãËëãËëãËëãËëãËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ZÿÿÿìæÕöòêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâØ½­Eÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþþþýþþýþþýþþýþþýþþýþþýþþüþýüþýüþýüþýüþýûþýûþýûýýûýýûýýúýüúýüúýüúýüúýüùýüùýüùýüùýûøüûøüûøüûøüûøüû÷üû÷üû÷üû÷üúöüúöüúöüúöûúõûúõûùõûùôûùôûùôûùôûùóûùóûùóúøóúøòúøòúøòúøòúøñúøñú÷ñú÷ðú÷ðù÷ðù÷ïù÷ïùöïùöïùöîùöîùöîùöîùöíøõíøõíøõìøõìøõìøõëøôëøôëøôë÷ôê÷ôê÷ôê÷óé÷óé÷óé÷óé÷óè÷óèöóèöòçöòçöòçöòæöòæöòæöñåöñåõñåõñäõñäõñäõñäõðãõðãõðãôðâôðâôðâôïâôïáôïáôïáôïàôïàóîàóîßóîßóîßóîßóîÞóíÞóíÞóíÝóíÝòíÝòíÝòíÜòìÜòìÜòìÛòìÛòìÛòìÛòìÚñëÚñëÚñëÚñëÙñëÙñëÙñëÙñêØñêØñêØñêØðê×ðê×ðê×ðéÖðéÖðéÖðéÖðéÕðéÕðéÕðéÕïèÕïèÔïèÔïèÔïèÔïèÓïèÓïèÓïèÓïçÓïçÒïçÒîçÒîçÒîçÒîçÑîçÑîçÑîçÑîæÑîæÐîæÐîæÐîæÐîæÐîæÐîæÐíæÏíæÏíæÏíåÏíåÏíåÏíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎæÀ˜´ƒ={]…<Á°òųǵÉ·€Ë¸ÌºƒÎ¼…Ͻ‡Ð¾ˆÒ¿ŠÒÀ‹ÓÂÔÃÕÃÖđ֌לׯ“ׯ”ׯ“×Å“ÖÅ’ÖÄ‘ÕÃÔÂŽÓÂÓÀŒÒ¿ŠÑ¿‰Ï½‡Î¼…̺ƒ½¥`“p¨{ à±…êâÌíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎíåÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ž]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«ŽBãÙÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþüþýüþýüþýüþýüþýûþýûþýûþýûýýûýýûýüúýüúýüúýüúýüúýüùýüùýüùýüùýüøýûøýûøüûøüûøüû÷üû÷üû÷üû÷üú÷üúöüúöüúöüúõüúõûúõûúõûùõûùôûùôûùôûùôûùóûùóûùóûøóúøòúøòúøòúøòúøñúøñú÷ñú÷ñú÷ðú÷ðú÷ðù÷ïù÷ïùöïùöïùöîùöîùöîùöîùõíùõíùõíøõìøõìøõìøõìøõëøôëøôëøôëøôêøôê÷ôê÷ôé÷óé÷óé÷óé÷óè÷óè÷óè÷òè÷òçöòçöòçöòæöòæöòæöñæöñåöñåöñåöñäöñäõñäõñäõðãõðãõðãõðãõðâõðâõïâõïâôïáôïáôïáôïàôïàôîàôîàôîßôîßôîßôîßóîÞóíÞóíÞóíÞóíÝóíÝóíÝóíÝóíÜóìÜóìÜòìÜòìÜòìÛòìÛòìÛòìÚòëÚòëÚòëÚòëÚòëÙòëÙñëÙñëÙñêÙñêØñêØñêØñêØñê×ñê×ñê×ñê×ñé×ñéÖñéÖðéÖðéÖðéÖðéÖðéÕðéÕðéÕðéÕðèÕðèÔðèÔðèÔðèÔðèÔðèÔðèÔðèÓïèÓïèÓïçÓïçÓïçÓïçÓïçÓïçÒïçÒïçÒïçÒïçÒïçÒïçÒïçÒïçÒïçÒïçÒïæÑ帛s${]¥KǸ‹È¹‹Êº‹Ì¼ŒÎ¾ŒÏ¾ŒÑÀŽÒÁÓ‘ÔÄ“ÕÄ“Öŕ֯•×Ç—ØÈ˜ØÈ˜ØÈ™ØÈ™ØÈ™ØÈ™ØÈ™ØÈ˜ØÈ˜×Ç–ÖÆ•ÖÅ•ÕÄ“ÔÃ’ÔÃ’ÒÁÑÀŽÂ­m•s™q ÚœcèвîæÑïçÒïçÒïçÒïçÒïçÒïçÒïçÒïçÒïçÒïçÒïçÒïçÒïçÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµ›Wüûùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçßɧ‰9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþþþýþþýþþýþþýþþýþþýþþýþþüþþüþþüþýüþýüþýüþýüþýûþýûþýûþýûþýûýýúýýúýüúýüúýüúýüúýüúýüùýüùýüùýüùýüøýûøýûøýûøüûøüû÷üû÷üû÷üû÷üû÷üúöüúöüúöüúöüúöüúõüúõûúõûúõûùôûùôûùôûùôûùôûùóûùóûùóûøóûøòúøòúøòúøòúøñúøñúøñúøñú÷ðú÷ðú÷ðú÷ðú÷ðú÷ïù÷ïùöïùöïùöîùöîùöîùöîùöíùöíùöíùõíøõìøõìøõìøõìøõëøõëøôëøôëøôêøôêøôêøôê÷ôé÷óé÷óé÷óé÷óè÷óè÷óè÷óè÷óç÷óç÷òç÷òçöòçöòæöòæöòæöòæöñåöñåöñåöñäöñäöñäöñäõñäõñãõðãõðãõðãõðãõðâõðâõðâõðâõïáõïáôïáôïáôïàôïàôïàôïàôîßôîßôîßôîßôîßôîßôîÞóîÞóîÞóîÞóíÝóíÝóíÝóíÝóíÝóíÝóíÜóíÜóíÜóíÜóìÜóìÜóìÛòìÛòìÛòìÛòìÛòìÛòìÚòìÚòìÚòëÚòëÚòëÚòëÙòëÙòëÙòëÙòëÙòëÙòëÙñëØñëØñëØñêØñêØñêØñêØñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×íØ¼ÜfŽj{]«—XÍÀ˜ÎÀ—Ð˜ÑØÓĘÔřկšÖÇ›×ÈœØÉÙÊžÙÊžÙËŸÚË ÚÌ ÛÌ¡ÚÌ ÚÌ ÛÌ¡ÛÌ¡ÚÌ ÚË ÙËŸÙÊžÙÊžØÉ×ÈœÖÇ›ÕÆšÃ¯r”q’mÑ–WçÅ¢ðéÖñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ñê×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïxêâÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹ `ØË¨ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþüþþüþþüþýüþýüþýüþýüþýûþýûþýûþýûþýûýýûýýúýýúýýúýüúýüúýüúýüúýüùýüùýüùýüùýüùýüøýüøýûøýûøýûøüû÷üû÷üû÷üû÷üû÷üûöüúöüúöüúöüúöüúöüúõüúõüúõüúõûúõûùôûùôûùôûùôûùóûùóûùóûùóûùóûùóûøòûøòûøòúøòúøñúøñúøñúøñúøñúøðú÷ðú÷ðú÷ðú÷ïú÷ïú÷ïù÷ïù÷ïùöîùöîùöîùöîùöîùöíùöíùöíùöíùõìùõìùõìøõìøõìøõìøõëøõëøõëøõëøôêøôêøôêøôêøôêøôé÷ôé÷ôé÷óé÷óè÷óè÷óè÷óè÷óè÷óç÷óç÷óç÷óç÷òç÷òæöòæöòæöòæöòæöòåöòåöòåöòåöñåöñåöñäöñäöñäöñäöñãõñãõñãõñãõñãõðãõðâõðâõðâõðâõðâõðâõðáõðáõðáõïáõïáõïáõïàôïàôïàôïàôïàôïàôïàôïßôïßôïßôîßôîßôîßôîÞôîÞôîÞôîÞôîÞôîÞôîÞôîÞôîÞóîÝóîÝóîÝóíÝóíÝóíÝóíÝóíÝóíÝóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜìбךaŠg{]¤ŽKÑÆ¢ÓȤÕȤÖɤ×ʤØË¤ÙÌ¥ÚÍ¥ÛͦÛΧÜϨÜϨÝЩÝЩÝЩÝЩÝЪÝЩÝЩÝЩÝЩÜϨÜϨÜϨÛΧÚÍ¥ÙÌ¥¾©h’nlÌ’QèÞòìÛóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜóíÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóïä²—QþýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèáÌ¥…3þýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþüþþüþþüþþüþýüþýüþýüþýüþýûþýûþýûþýûþýûþýûþýûýýûýýúýýúýüúýüúýüúýüúýüúýüùýüùýüùýüùýüùýüùýüùýüøýûøýûøýûøüûøüû÷üû÷üû÷üû÷üû÷üû÷üûöüûöüúöüúöüúöüúöüúöüúõüúõüúõüúõûúõûúôûùôûùôûùôûùôûùôûùôûùóûùóûùóûùóûùóûøòûøòûøòúøòúøòúøòúøñúøñúøñúøñúøñú÷ðú÷ðú÷ðú÷ðú÷ðú÷ðú÷ïú÷ïù÷ïù÷ïù÷ïùöïùöîùöîùöîùöîùöîùöíùöíùöíùöíùöíùõìùõìøõìøõìøõìøõìøõëøõëøõëøõëøõëøôëøôêøôêøôêøôêøôêøôêøôêøôé÷ôé÷ôé÷ôé÷óé÷óé÷óè÷óè÷óè÷óè÷óè÷óè÷óç÷óç÷óç÷óç÷óç÷òç÷òç÷òæöòæöòæöòæöòæöòæöòæöòæöòåöòåöòåöòåöñåöñåöñåöñåöñäöñäöñäöñäöñäöñäöñäöñäöñãöñãõñãõñãõñãõñãõðãõðãõðãõðãõðãõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâìϱÛe–oz]“z+ÐÄŸÙϰÚϰÛаÛѰÜѰÝÒ°ÞÓ±ÞÓ±ßÓ±ßÔ²àÔ²àÔ²àÕ²àÕ³àÕ³àÕ³àÕ²àÔ²àÔ²ßÔ²ßÓ±ÞÓ±ÞÒ°Ùͨª>Œi–oÑ–YéÄ õðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâõðâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƲ~ëäÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöð§‰9ðëÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþüþþüþþüþþüþýüþýüþýüþýüþýüþýûþýûþýûþýûþýûþýûþýûþýûýýúýýúýýúýýúýüúýüúýüúýüúýüùýüùýüùýüùýüùýüùýüùýüùýüøýüøýüøýüøýûøýûøýûøüû÷üû÷üû÷üû÷üû÷üû÷üû÷üûöüûöüûöüúöüúöüúöüúöüúõüúõüúõüúõüúõüúõüúõûúôûúôûúôûùôûùôûùôûùôûùóûùóûùóûùóûùóûùóûùòûùòûùòûùòûøòûøòûøòúøñúøñúøñúøñúøñúøñúøñúøðúøðúøðúøðú÷ðú÷ðú÷ðú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïù÷îù÷îù÷îù÷îùöîùöîùöîùöíùöíùöíùöíùöíùöíùöíùöíùöìùöìùöìùöìùõìùõìùõìùõìøõëøõëøõëøõëøõëøõëøõëøõëøõêøõêøõêøõêøõêøôêøôêøôêøôêøôêøôéøôéøôéøôéøôéøôéøôéøôéøôéøôé÷ôè÷ôè÷ôè÷ôè÷ôè÷ôè÷óè÷óè÷óè÷óè÷óè÷óè÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óçðÛÂ縨{2y\†k¹¨sÞÖ¼ßÖ¼ßÖ»à×¼áØ¼áØ¼âØ¼âÙ¼ãÙ¼âÙ¼ãÙ¼ãÚ½ãÚ½ãÚ½ãÙ¼ãÚ½ãÙ½ãÙ½ãÙ¼âÙ¼âØ¼É¸…—w‰gžtÚœcìͯ÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óç÷óçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýü±•Nÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûúö±•NßÔ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþüþþüþþüþþüþþüþþüþýüþýüþýüþýüþýüþýûþýûþýûþýûþýûþýûþýûþýûýýûýýúýýúýýúýýúýýúýüúýüúýüúýüúýüúýüùýüùýüùýüùýüùýüùýüùýüùýüøýüøýüøýüøýûøýûøüûøüûøüûøüû÷üû÷üû÷üû÷üû÷üû÷üû÷üû÷üûöüûöüûöüûöüúöüúöüúöüúöüúõüúõüúõüúõûúõûúõûúõûúõûúôûúôûúôûúôûùôûùôûùôûùôûùóûùóûùóûùóûùóûùóûùóûùóûùòûùòûùòúùòúøòúøòúøòúøòúøòúøñúøñúøñúøñúøñúøñúøñúøñúøðúøðúøðúøðúøðú÷ðú÷ðú÷ðú÷ðú÷ïú÷ïú÷ïù÷ïù÷ïù÷ïù÷ïù÷ïù÷ïù÷ïù÷îù÷îù÷îù÷îù÷îùöîùöîùöîùöîùöîùöîùöíùöíùöíùöíùöíùöíùöíùöíùöíùöíùöíùöìøöìøöìøöìøöìøöìøöìøõìøõìøõìøõìøõìøõìøõìøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëõëÜêß»ˆF‚a z]t#ǹäÝÇäÝÈåÞÈäÝÆåÞÇæÞÈæÞÇæÞÇæÞÇçßÈçßÈçßÈçßÇçßÈçßÈçßÈæÞÇ;‘€*ˆf‰g°€3ߟiðÙÁøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëøõëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâØ½Î½ÿÿÿäÛÃÚÍ«ÿÿÿÿÿÿ÷ôí­EÏ¿“ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþýÿþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþüþþüþþüþþüþþüþþüþýüþýüþýüþýüþýüþýüþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýúþýúþýúþýúýýúýýúýüúýüúýüúýüúýüúýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüøýüøýüøýüøýüøýûøýûøýûøýûøýûøýû÷ýû÷ýû÷ýû÷ýû÷üû÷üû÷üû÷üû÷üû÷üûöüûöüûöüûöüûöüúöüúöüúöüúöüúöüúöüúõüúõüúõüúõüúõüúõüúõüúõüúõüúõüúôüúôüúôüúôüúôüùôüùôûùôûùôûùôûùóûùóûùóûùóûùóûùóûùóûùóûùóûùóûùóûùóûùòûùòûùòûùòûùòûùòûøòûøòûøòûøòûøòûøòûøòûøñûøñûøñûøñûøñûøñûøñûøñûøñûøñûøñûøñúøñúøðúøðúøðúøðúøðúøðúøðúøðú÷ðú÷ðú÷ðú÷ðú÷ðú÷ðú÷ðú÷ðú÷ðú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïíÌ­Üf­~7~`{]“z,¾¯~áÚÃéãÒéãÑêäÒêäÒêäÒêäÒêäÒêäÒêäÒêäÒêäÒÞÔ·½ªr/†d„c§z)Õ™^ëÆ¥÷îáú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïú÷ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüûù¯”KøöðÿÿÿïêÛüûùÿÿÿÛÏ®§‰9äÛÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþýüþýüþýüþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýúþýúþýúþýúýýúýýúýýúýýúýýúýýúýýúýüúýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýû÷ýû÷ýû÷ýû÷ýû÷ýû÷ýû÷üû÷üû÷üû÷üû÷üû÷üûöüûöüûöüûöüûöüûöüûöüûöüûöüûöüûöüûöüûöüûõüûõüûõüûõüúõüúõüúõüúõüúõüúõüúõüúõüúõüúõüúõüúôüúôüúôüúôüúôüúôüúôüúôüúôüúôüúôüúôüúôüúôûúôûúóûúóûúóûúóûúóûúóûúóûúóûùóûùóûùóûùóûùóûùóûùóûùóûùóûùóûùóûùóûùóûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòùóèíʪ؛c¥y/ƒb z]|^–~1°b³„ØÎ°ØÎ°ÙΰÙϰƷ‰Ä´„°›[”x"ƒb‚bƒb v#Ï•Wêßöç×ûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòûùòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁ«rÜѱÿÿÿøöðÞÒ´Ì»¡€*³™Tøöðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþüþþüþþüþþüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüúýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýûùýûùýûùýûùýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøýûøüû÷üû÷üû÷üû÷üû÷üû÷üû÷üû÷üû÷üû÷üû÷üû÷üû÷üû÷üú÷üú÷üú÷üú÷üú÷üú÷üú÷üú÷üú÷üú÷üú÷üúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöøíâíÈ©ãªyË’U«}4Šhz\{]|^}^~_~_~__~_†d œs ¸†@Ù›c龘õãÒüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöüúöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýü²—QþýüÒ™Ÿ~'Ê·‡ÖÈ¢ôñçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþýÿþýÿþýÿþýÿþýÿþýÿþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýúþýúþýúþýúþýúþýúþýúþýúþýúþýúþýúþýúþýúþýúþýúþýúþýúþýúþýúýýúýýúýýúýýúýýúýýúýýúýýùýýùýýùýýùýýùýýùýýùýýùýýùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüùýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüø÷êÜîͰ尃ݞhË’U¸†B¶„@¶…@·…@·…@É‘RØšaâ¨v鼕ñ׿ûöîýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøýüøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿº¢c§‰9­uôñçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþþÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýÿþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþþüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýüþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûýûøøêÞóÚÄòØÁòØÁòØÁòØÁ÷èÛüõïþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûþýûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþÿÿþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýþþýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmysql-connector-odbc-5.1.10-src/setupgui/windows/odbcdialogparams.h100644 15766 12 3403 11707541005 24171 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __ODBCPARAMS_H__ #define __ODBCPARAMS_H__ #ifdef __cplusplus extern "C" { #endif // four callbacks: // called when [Help] pressed typedef void HelpButtonPressedCallbackType(HWND dialog); // called when [Test] pressed - show any messages to user here //typedef const wchar_t * TestButtonPressedCallbackType(HWND dialog, DataSource* params); // called when [OK] pressed - show errors here (if any) and return false to prevent dialog close typedef BOOL AcceptParamsCallbackType(HWND dialog, DataSource* params); // called when DataBase combobox Drops Down //typedef const WCHAR** DatabaseNamesCallbackType(HWND dialog, DataSource* params); #ifdef __cplusplus } #endif #endif mysql-connector-odbc-5.1.10-src/setupgui/myodbc5S.def100644 15766 12 70 11707541005 21135 0ustar00cteamstaffLIBRARY MYODBC5S.DLL EXPORTS ConfigDSNW Driver_Prompt ; mysql-connector-odbc-5.1.10-src/setupgui/callbacks.c100644 15766 12 13202 11707541005 21134 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* TODO no L"" */ #include "setupgui.h" #include "stringutil.h" SQLWCHAR **errorMsgs= NULL; SQLHDBC hDBC= SQL_NULL_HDBC; SQLWCHAR *mytest(HWND hwnd, DataSource *params) { SQLHDBC hDbc= hDBC; SQLHENV hEnv= SQL_NULL_HENV; SQLWCHAR *rc; if (SQL_SUCCEEDED(Connect(&hDbc, &hEnv, params))) rc= sqlwchardup(L"Connection successful", SQL_NTS); else { SQLWCHAR state[10]; SQLINTEGER native; SQLSMALLINT len; rc= (SQLWCHAR *) my_malloc(512 * sizeof(SQLWCHAR), MYF(0)); *rc= 0; wcscat(rc, L"Connection Failed"); len= sqlwcharlen(rc); if (SQL_SUCCEEDED(SQLGetDiagRecW(SQL_HANDLE_DBC, hDbc, 1, state, &native, rc + len + 10, 512 - len - 11, &len))) { wcscat(rc, L": ["); len= sqlwcharlen(rc); sqlwcharncpy(rc + len, state, 6); *(rc + sqlwcharlen(rc) + 1)= ' '; *(rc + sqlwcharlen(rc))= ']'; } } Disconnect(hDbc, hEnv); return rc; } BOOL mytestaccept(HWND hwnd, DataSource* params) { /* TODO validation */ return TRUE; } LIST *mygetdatabases(HWND hwnd, DataSource* params) { SQLHENV hEnv= SQL_NULL_HENV; SQLHDBC hDbc= hDBC; SQLHSTMT hStmt; SQLRETURN nReturn; SQLWCHAR szCatalog[MYODBC_DB_NAME_MAX]; SQLLEN nCatalog; LIST *dbs= NULL; SQLWCHAR *preservedDatabase= params->database; BOOL preservedNoCatalog= params->no_catalog; params->database= NULL; params->no_catalog= FALSE; nReturn= Connect(&hDbc, &hEnv, params); params->database= preservedDatabase; params->no_catalog= preservedNoCatalog; if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_DBC, hDbc); if (!SQL_SUCCEEDED(nReturn)) { Disconnect(hDbc,hEnv); return NULL; } nReturn= SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt); if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_DBC, hDbc); if (!SQL_SUCCEEDED(nReturn)) { Disconnect(hDbc,hEnv); return NULL; } nReturn= SQLTablesW(hStmt, (SQLWCHAR*)SQL_ALL_CATALOGS, SQL_NTS, (SQLWCHAR*)L"", SQL_NTS, (SQLWCHAR*)L"", 0, (SQLWCHAR*)L"", 0); if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_STMT, hStmt); if (!SQL_SUCCEEDED(nReturn)) { SQLFreeHandle(SQL_HANDLE_STMT, hStmt); Disconnect(hDbc, hEnv); return NULL; } nReturn= SQLBindCol(hStmt, 1, SQL_C_WCHAR, szCatalog, MYODBC_DB_NAME_MAX, &nCatalog); while (TRUE) { nReturn= SQLFetch(hStmt); if (nReturn == SQL_NO_DATA) break; else if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_STMT, hStmt); if (SQL_SUCCEEDED(nReturn)) dbs= list_cons(sqlwchardup(szCatalog, SQL_NTS), dbs); else break; } SQLFreeHandle(SQL_HANDLE_STMT, hStmt); Disconnect(hDbc, hEnv); return list_reverse(dbs); } LIST *mygetcharsets(HWND hwnd, DataSource* params) { SQLHENV hEnv= SQL_NULL_HENV; SQLHDBC hDbc= hDBC; SQLHSTMT hStmt; SQLRETURN nReturn; SQLWCHAR szCharset[MYODBC_DB_NAME_MAX]; SQLLEN nCharset; LIST *csl= NULL; SQLWCHAR *preservedDatabase= params->database; BOOL preservedNoCatalog= params->no_catalog; params->database= NULL; params->no_catalog= FALSE; nReturn= Connect(&hDbc, &hEnv, params); params->database= preservedDatabase; params->no_catalog= preservedNoCatalog; if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_DBC, hDbc); if (!SQL_SUCCEEDED(nReturn)) { Disconnect(hDbc,hEnv); return NULL; } nReturn= SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt); if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_DBC, hDbc); if (!SQL_SUCCEEDED(nReturn)) { Disconnect(hDbc,hEnv); return NULL; } nReturn = SQLExecDirectW( hStmt, L"SHOW CHARACTER SET", SQL_NTS); if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_STMT, hStmt); if (!SQL_SUCCEEDED(nReturn)) { SQLFreeHandle(SQL_HANDLE_STMT, hStmt); Disconnect(hDbc, hEnv); return NULL; } nReturn= SQLBindCol(hStmt, 1, SQL_C_WCHAR, szCharset, MYODBC_DB_NAME_MAX, &nCharset); while (TRUE) { nReturn= SQLFetch(hStmt); if (nReturn == SQL_NO_DATA) break; else if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_STMT, hStmt); if (SQL_SUCCEEDED(nReturn)) csl= list_cons(sqlwchardup(szCharset, SQL_NTS), csl); else break; } SQLFreeHandle(SQL_HANDLE_STMT, hStmt); Disconnect(hDbc, hEnv); return list_reverse(csl); } mysql-connector-odbc-5.1.10-src/setupgui/utils.c100644 15766 12 10067 11707541005 20363 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "setupgui.h" #include "driver.h" #include "stringutil.h" extern SQLHDBC hDBC; extern SQLWCHAR **errorMsgs; void FreeEnvHandle(SQLHENV hEnv) { if (hDBC == SQL_NULL_HDBC) SQLFreeHandle(SQL_HANDLE_ENV, hEnv); } void Disconnect(SQLHDBC hDbc, SQLHENV hEnv) { SQLDisconnect(hDbc); if (hDBC == SQL_NULL_HDBC) SQLFreeHandle(SQL_HANDLE_DBC, hDbc); FreeEnvHandle(hEnv); } SQLRETURN Connect(SQLHDBC *hDbc, SQLHENV *hEnv, DataSource *params) { SQLRETURN nReturn; SQLWCHAR stringConnectIn[1024]; size_t inlen= 1024; assert(params->driver && *params->driver); /* Blank out DSN name, otherwise it will pull the info from the registry */ ds_set_strattr(¶ms->name, NULL); if (ds_to_kvpair(params, stringConnectIn, 1024, ';') == -1) { /* TODO error message..... */ return SQL_ERROR; } inlen-= sqlwcharlen(stringConnectIn); if (hDBC == SQL_NULL_HDBC) { nReturn= SQLAllocHandle(SQL_HANDLE_ENV, NULL, hEnv); if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_ENV, NULL); if (!SQL_SUCCEEDED(nReturn)) return nReturn; nReturn= SQLSetEnvAttr(*hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_ENV, NULL); if (!SQL_SUCCEEDED(nReturn)) { return nReturn; } nReturn= SQLAllocHandle(SQL_HANDLE_DBC, *hEnv, hDbc); if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_ENV, *hEnv); if (!SQL_SUCCEEDED(nReturn)) { return nReturn; } } nReturn= SQLDriverConnectW(*hDbc, NULL, (SQLWCHAR*)(stringConnectIn), SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT); if (nReturn != SQL_SUCCESS) ShowDiagnostics(nReturn, SQL_HANDLE_DBC, *hDbc); return nReturn; } void ShowDiagnostics(SQLRETURN nReturn, SQLSMALLINT nHandleType, SQLHANDLE h) { BOOL bDiagnostics= FALSE; SQLSMALLINT nRec= 1; SQLWCHAR szSQLState[6]; SQLINTEGER nNative; SQLWCHAR szMessage[SQL_MAX_MESSAGE_LENGTH]; SQLSMALLINT nMessage; if (h) { *szSQLState= '\0'; *szMessage= '\0'; while (SQL_SUCCEEDED(SQLGetDiagRecW(nHandleType, h, nRec, szSQLState, &nNative, szMessage, SQL_MAX_MESSAGE_LENGTH, &nMessage))) { szSQLState[5]= '\0'; szMessage[SQL_MAX_MESSAGE_LENGTH - 1]= '\0'; /*add2list(errorMsgs, szMessage);*/ bDiagnostics= TRUE; nRec++; *szSQLState= '\0'; *szMessage= '\0'; } } switch (nReturn) { case SQL_ERROR: /*strAssign(popupMsg, L"Request returned with SQL_ERROR.");*/ break; case SQL_SUCCESS_WITH_INFO: /*strAssign(popupMsg, L"Request return with SQL_SUCCESS_WITH_INFO.");*/ break; case SQL_INVALID_HANDLE: /*strAssign(popupMsg, L"Request returned with SQL_INVALID_HANDLE.");*/ break; default: /*strAssign(popupMsg, L"Request did not return with SQL_SUCCESS.");*/ break; } } mysql-connector-odbc-5.1.10-src/setupgui/setupgui.h100644 15766 12 4134 11707541005 21053 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SETUPGUI_H #define _SETUPGUI_H #ifdef _WIN32 # include # include #endif #include "MYODBC_MYSQL.h" #include "installer.h" #include #ifdef __cplusplus extern "C" { #endif /* Utility functions. */ void ShowDiagnostics(SQLRETURN nReturn, SQLSMALLINT nHandleType, SQLHANDLE h); void FreeEnvHandle(SQLHENV hEnv); SQLRETURN Connect(SQLHDBC *hDbc, SQLHENV *hEnv, DataSource *params); void Disconnect(SQLHDBC hDbc, SQLHENV hEnv); /* Max DB name len, used when retrieving database list */ #define MYODBC_DB_NAME_MAX 255 /* Callbacks */ wchar_t *mytest(HWND hwnd, DataSource *params); BOOL mytestaccept(HWND hwnd, DataSource *params); LIST *mygetdatabases(HWND hwnd, DataSource *params); LIST *mygetcharsets(HWND hwnd, DataSource *params); /** This is the function implemented by the platform-specific code. @return TRUE if user pressed OK, FALSE if cancelled or closed */ int ShowOdbcParamsDialog(DataSource *params, HWND ParentWnd, BOOL isPrompt); #ifdef __cplusplus } #endif #endif mysql-connector-odbc-5.1.10-src/setupgui/ConfigDSN.c100644 15766 12 10702 11707541005 20771 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "setupgui.h" #include "stringutil.h" /* Entry point for GUI prompting from SQLDriverConnect(). */ BOOL Driver_Prompt(HWND hWnd, SQLWCHAR *instr, SQLUSMALLINT completion, SQLWCHAR *outstr, SQLSMALLINT outmax, SQLSMALLINT *outlen) { DataSource *ds= ds_new(); BOOL rc= FALSE; /* parse the attr string, dsn lookup will have already been done in the driver */ if (instr && *instr) { if (ds_from_kvpair(ds, instr, (SQLWCHAR)';')) { rc= FALSE; goto exit; } } /* Show the dialog and handle result */ if (ShowOdbcParamsDialog(ds, hWnd, TRUE) == 1) { int len; /* serialize to outstr */ if ((len= ds_to_kvpair(ds, outstr, outmax, (SQLWCHAR)';')) == -1) { /* truncated, up to caller to see outmax < *outlen */ if (outlen) *outlen= ds_to_kvpair_len(ds); outstr[outmax]= 0; } else if (outlen) *outlen= len; rc= TRUE; } exit: ds_delete(ds); return rc; } /* Add, edit, or remove a Data Source Name (DSN). This function is called by "Data Source Administrator" on Windows, or similar application on Unix. */ BOOL INSTAPI ConfigDSNW(HWND hWnd, WORD nRequest, LPCWSTR pszDriver, LPCWSTR pszAttributes) { DataSource *ds= ds_new(); BOOL rc= TRUE; Driver *driver= NULL; SQLWCHAR *origdsn= NULL; if (pszAttributes && *pszAttributes) { SQLWCHAR delim= ';'; /* if there's no ;, then it's most likely null-delimited */ if (!sqlwcharchr(pszAttributes, delim)) delim= 0; if (ds_from_kvpair(ds, pszAttributes, delim)) { SQLPostInstallerError(ODBC_ERROR_INVALID_KEYWORD_VALUE, W_INVALID_ATTR_STR); rc= FALSE; goto exitConfigDSN; } if (ds_lookup(ds) && nRequest != ODBC_ADD_DSN) { /* ds_lookup() will already set SQLInstallerError */ rc= FALSE; goto exitConfigDSN; } origdsn= sqlwchardup(ds->name, SQL_NTS); } switch (nRequest) { case ODBC_ADD_DSN: driver= driver_new(); memcpy(driver->name, pszDriver, (sqlwcharlen(pszDriver) + 1) * sizeof(SQLWCHAR)); if (driver_lookup(driver)) { rc= FALSE; break; } if (hWnd) { /* hWnd means we will at least try to prompt, at which point the driver lib will be replaced by the name */ ds_set_strattr(&ds->driver, driver->lib); } else { /* no hWnd is a likely a call from an app w/no prompting so we put the driver name immediately */ ds_set_strattr(&ds->driver, driver->name); } case ODBC_CONFIG_DSN: #ifdef _WIN32 /* for windows, if hWnd is NULL, we try to add the dsn with what information was given */ if (!hWnd || ShowOdbcParamsDialog(ds, hWnd, FALSE) == 1) #elif if (ShowOdbcParamsDialog(ds, hWnd, FALSE) == 1) #endif { /* save datasource */ if (ds_add(ds)) rc= FALSE; /* if the name is changed, remove the old dsn */ if (origdsn && memcmp(origdsn, ds->name, (sqlwcharlen(origdsn) + 1) * sizeof(SQLWCHAR))) SQLRemoveDSNFromIni(origdsn); } break; case ODBC_REMOVE_DSN: if (SQLRemoveDSNFromIni(ds->name) != TRUE) rc= FALSE; break; } exitConfigDSN: x_free(origdsn); ds_delete(ds); if (driver) driver_delete(driver); return rc; } mysql-connector-odbc-5.1.10-src/setupgui/CMakeLists.txt100644 15766 12 4256 11707541005 21602 0ustar00cteamstaff# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## ADD_DEFINITIONS(-DUNICODE -D_UNICODE) SET(SETUP_SRCS ConfigDSN.c callbacks.c setupgui.h utils.c) IF (WIN32) SET(SETUP_SRCS ${SETUP_SRCS} myodbc5S.def windows/main.cpp windows/odbcdialogparams.cpp windows/odbcdialogparams.h windows/odbcdialogparams.rc windows/resource.h windows/TabCtrl.cpp windows/TabCtrl.h windows/tooltip.cpp windows/connector_odbc_header.bmp) SET(PLATFORM_LIBS comctl32) ENDIF (WIN32) IF(APPLE) ADD_LIBRARY(myodbc5S MODULE ${SETUP_SRCS}) ELSE(APPLE) ADD_LIBRARY(myodbc5S SHARED ${SETUP_SRCS}) ENDIF(APPLE) INSTALL(TARGETS myodbc5S DESTINATION ${LIB_SUBDIR}) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/util) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/driver) TARGET_LINK_LIBRARIES(myodbc5S myodbc3u ${ODBCLIB} ${ODBCINSTLIB} ${MYSQL_CLIENT_LIBS} ${PLATFORM_LIBS}) mysql-connector-odbc-5.1.10-src/test/cmake/generateinifiles.cmake100755 15766 12 3555 11707541005 23557 0ustar00cteamstaff# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## MACRO(_ENV_OR_DEFAULT VAR _default) SET(${VAR} $ENV{${VAR}}) IF(NOT ${VAR}) SET(${VAR} "${_default}") ENDIF(NOT ${VAR}) ENDMACRO(_ENV_OR_DEFAULT VAR _key _default) _ENV_OR_DEFAULT(TEST_DRIVER "${DRIVER_LOCATION}") _ENV_OR_DEFAULT(TEST_DATABASE "test") _ENV_OR_DEFAULT(TEST_SERVER "localhost") _ENV_OR_DEFAULT(TEST_UID "root") _ENV_OR_DEFAULT(TEST_PASSWORD "") _ENV_OR_DEFAULT(TEST_SOCKET "") IF(WIN32 AND TEST_SOCKET) SET(TEST_OPTIONS "NAMED_PIPE=1") ENDIF(WIN32 AND TEST_SOCKET) SET(DESCRIPTION "MySQL ODBC 5.1 Driver test" ) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/odbcinst.ini.in ${BINARY_DIR}/odbcinst.ini @ONLY) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/odbc.ini.in ${BINARY_DIR}/odbc.ini @ONLY) mysql-connector-odbc-5.1.10-src/test/odbctap.h100644 15766 12 73347 11707541005 17770 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file odbctap.h A basic interface for writing tests that produces TAP-compliant output. */ /* We don't want ansi calls to be mapped to unicode counterparts, but that does not work */ /* #define SQL_NOUNICODEMAP 1*/ #ifdef HAVE_CONFIG_H # include #endif /* Work around iODBC header bug on Mac OS X 10.3 */ #undef HAVE_CONFIG_H #include #ifdef WIN32 # include # define sleep(x) Sleep(x*1000) # include #else # include # include #endif #include #include #include #include #include /* for clock() */ #include /* Get routines for helping with Unicode conversion. */ #define ODBCTAP typedef unsigned int UTF32; typedef unsigned short UTF16; typedef unsigned char UTF8; #include "../util/unicode_transcode.c" void printMessage(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stdout, "# "); vfprintf(stdout, fmt, ap); fprintf(stdout, "\n"); va_end(ap); } void wprintMessage(const wchar_t *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stdout, "# "); vfwprintf(stdout, fmt, ap); fprintf(stdout, "\n"); va_end(ap); } #define MAX_NAME_LEN 255 #define MAX_COLUMNS 500 #define MAX_ROW_DATA_LEN 1000 #define MYSQL_NAME_LEN 64 SQLCHAR *mydriver= (SQLCHAR *)"{MySQL ODBC 5.1 Driver}"; SQLCHAR *mydsn= (SQLCHAR *)"test"; SQLCHAR *myuid= (SQLCHAR *)"root"; SQLCHAR *mypwd= (SQLCHAR *)""; SQLCHAR *mysock= NULL; int myoption= 0, myport= 0; SQLCHAR *myserver= (SQLCHAR *)"localhost"; SQLCHAR *mydb= (SQLCHAR *)"test"; SQLCHAR *test_db= (SQLCHAR *)"client_odbc_test"; int g_nCursor; char g_szHeader[501]; #ifndef OK # define OK 0 #endif #ifndef FAIL # define FAIL 1 #endif #ifndef SKIP # define SKIP -1 #endif #ifndef FALSE # define FALSE 0 #endif #ifndef TRUE # define TRUE 1 #endif char *SKIP_REASON= NULL; typedef int (*test_func)(SQLHDBC, SQLHSTMT, SQLHENV); static void print_diag(SQLRETURN rc, SQLSMALLINT htype, SQLHANDLE handle, const char *text, const char *file, int line); /* Disable _attribute__ on non-gcc compilers. */ #if !defined(__attribute__) && !defined(__GNUC__) # define __attribute__(arg) #endif /* The parameters may be unused. so we add the attribute to stifle warnings. They may also still be used, and no warning will be generated. */ #define DECLARE_TEST(name) \ int (name)(SQLHDBC hdbc __attribute__((unused)), \ SQLHSTMT hstmt __attribute__((unused)), \ SQLHENV henv __attribute__((unused))) typedef struct { char *name; test_func func; int expect; } my_test; #ifdef WIN32 void test_timeout(int signum); HANDLE halarm= NULL; DWORD WINAPI win32_alarm(LPVOID arg) { DWORD timeout= ((DWORD) arg) * 1000; while (WaitForSingleObject(halarm, timeout) == WAIT_OBJECT_0); test_timeout(0); return 0; } #define ENABLE_ALARMS int do_alarms= !getenv("DISABLE_TIMEOUT") #define RUN_TESTS_SIGNAL halarm= CreateEvent(NULL, FALSE, FALSE, NULL); \ if (do_alarms) \ CreateThread(NULL, 0, win32_alarm, (LPVOID) 30, 0, NULL) #define RUN_TESTS_ALARM (void) SetEvent(halarm) #else #define ENABLE_ALARMS int do_alarms= !getenv("DISABLE_TIMEOUT") #define RUN_TESTS_SIGNAL (void)signal(SIGALRM, test_timeout) #define RUN_TESTS_ALARM if (do_alarms) alarm(30) #endif void mem_debug_init() { #ifdef _WIN32 int dbg = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); dbg |= _CRTDBG_ALLOC_MEM_DF; dbg |= _CRTDBG_CHECK_ALWAYS_DF; dbg |= _CRTDBG_DELAY_FREE_MEM_DF; dbg |= _CRTDBG_LEAK_CHECK_DF; _CrtSetDbgFlag(dbg); #endif } #define BEGIN_TESTS my_test tests[]= { #define ADD_TEST(name) { #name, name, OK }, #define ADD_TODO(name) { #name, name, FAIL }, #ifndef DISGUISE_TOFIX_TESTS # define ADD_TOFIX(name) { #name, name, OK }, #else # define ADD_TOFIX(name) { #name, name, FAIL }, #endif #define END_TESTS }; \ void test_timeout(int signum __attribute__((unused))) \ { \ printf("Bail out! Timeout.\n"); \ exit(1); \ } \ \ int main(int argc, char **argv) \ { \ SQLHENV henv; \ SQLHDBC hdbc; \ SQLHSTMT hstmt; \ int i, num_tests, failcnt= 0; \ ENABLE_ALARMS; \ \ mem_debug_init(); \ \ /* Set from environment, possibly overrided by command line */ \ if (getenv("TEST_DSN")) \ mydsn= (SQLCHAR *)getenv("TEST_DSN"); \ if (getenv("TEST_DRIVER")) \ mydriver= (SQLCHAR *)getenv("TEST_DRIVER"); \ if (getenv("TEST_UID")) \ myuid= (SQLCHAR *)getenv("TEST_UID"); \ if (getenv("TEST_PASSWORD")) \ mypwd= (SQLCHAR *)getenv("TEST_PASSWORD"); \ if (getenv("TEST_SOCKET")) \ mysock= (SQLCHAR *)getenv("TEST_SOCKET"); \ if (getenv("TEST_PORT")) \ myport= atoi(getenv("TEST_PORT")); \ \ if (argc > 1) \ mydsn= (SQLCHAR *)argv[1]; \ if (argc > 2) \ myuid= (SQLCHAR *)argv[2]; \ if (argc > 3) \ mypwd= (SQLCHAR *)argv[3]; \ if (argc > 4) \ mysock= (SQLCHAR *)argv[4]; #define SET_DSN_OPTION(x) \ myoption= (x); #define RUN_TESTS \ setbuf(stdout, NULL); \ num_tests= sizeof(tests) / sizeof(tests[0]); \ printf("1..%d\n", num_tests); \ \ RUN_TESTS_SIGNAL; \ \ if (alloc_basic_handles(&henv, &hdbc, &hstmt) != OK) \ exit(1); \ \ for (i= 0; i < num_tests; i++ ) \ { \ int rc; \ RUN_TESTS_ALARM; \ rc= tests[i].func(hdbc, hstmt, henv); \ printf("%s %d - %s %s%s\n", \ (rc == OK || rc == SKIP) ? "ok" : "not ok", \ i + 1, \ tests[i].name, \ (tests[i].expect == FAIL ? "# TODO" : \ rc == SKIP ? "# SKIP " : ""), \ SKIP_REASON ? SKIP_REASON : ""); \ if ((rc == FAIL) && (FAIL != tests[i].expect)) \ failcnt++; \ SKIP_REASON= NULL; /* Reset SKIP_REASON */ \ /* Re-allocate statement to reset all its properties. */ \ SQLFreeStmt(hstmt, SQL_DROP); \ SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); \ } \ \ (void)free_basic_handles(&henv, &hdbc, &hstmt); \ exit(failcnt); \ } /** Skip a test, giving a reason. */ #define skip(reason) \ do { \ SKIP_REASON= reason; \ return SKIP; \ } while (0) /** Execute an SQL statement and bail out if the execution does not return SQL_SUCCESS or SQL_SUCCESS_WITH_INFO. @param statement Handle for statement object @param query The query to execute */ #define ok_sql(statement, query) \ do { \ SQLRETURN rc= SQLExecDirect((statement), (SQLCHAR *)(query), SQL_NTS); \ print_diag(rc, SQL_HANDLE_STMT, (statement), \ "SQLExecDirect(" #statement ", \"" query "\", SQL_NTS)",\ __FILE__, __LINE__); \ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) \ return FAIL; \ } while (0) /** Verify that the result of an SQL statement call matches an expected result, such as SQL_ERROR. @param statement Handle for statement object @param query The query to execute @param expect The expected result */ #define expect_sql(statement, query, expect) \ do { \ SQLRETURN rc= SQLExecDirect((statement), (SQLCHAR *)(query), SQL_NTS); \ if (rc != (expect)) \ { \ print_diag(rc, SQL_HANDLE_STMT, (statement), \ "SQLExecDirect(" #statement ", \"" query "\", SQL_NTS)",\ __FILE__, __LINE__); \ printf("# Expected %d, but got %d in %s on line %d\n", expect, rc, \ __FILE__, __LINE__); \ return FAIL; \ } \ } while (0) /** Verify that the results of an ODBC function call on a statement handle was SQL_SUCCESS or SQL_SUCCESS_WITH_INFO. @param statement Handle for statement object @param call The function call */ #define ok_stmt(statement, call) \ do { \ SQLRETURN rc= (call); \ print_diag(rc, SQL_HANDLE_STMT, (statement), #call, __FILE__, __LINE__); \ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) \ return FAIL; \ } while (0) /** Verify that the results of an ODBC function call on a descriptor handle was SQL_SUCCESS or SQL_SUCCESS_WITH_INFO. @param desc Handle for descriptor object @param call The function call */ #define ok_desc(desc, call) \ do { \ SQLRETURN rc= (call); \ print_diag(rc, SQL_HANDLE_DESC, (desc), #call, __FILE__, __LINE__); \ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) \ return FAIL; \ } while (0) /** Verify that the result of an ODBC function call matches an expected result, such as SQL_ERROR or SQL_NO_DATA_FOUND. @param hnd Handle for object @param type Type of handle @param call The function call @param expect The expected result */ #define expect_odbc(hnd, type, call, expect) \ do { \ SQLRETURN rc= (call); \ if (rc != (expect)) \ { \ print_diag(rc, (type), (hnd), #call, __FILE__, __LINE__); \ printf("# Expected %d, but got %d in %s on line %d\n", (expect), rc, \ __FILE__, __LINE__); \ return FAIL; \ } \ } while (0) #define expect_env(env, call, expect) \ expect_odbc((env), SQL_HANDLE_STMT, (call), (expect)) #define expect_dbc(dbc, call, expect) \ expect_odbc((dbc), SQL_HANDLE_STMT, (call), (expect)) #define expect_stmt(statement, call, expect) \ expect_odbc((statement), SQL_HANDLE_STMT, (call), (expect)) #define expect_desc(desc, call, expect) \ expect_odbc((desc), SQL_HANDLE_DESC, (call), (expect)) /** Verify that the results of an ODBC function call on an environment handle was SQL_SUCCESS or SQL_SUCCESS_WITH_INFO. @param environ Handle for environment @param call The function call */ #define ok_env(environ, call) \ do { \ SQLRETURN rc= (call); \ print_diag(rc, SQL_HANDLE_ENV, (environ), #call, __FILE__, __LINE__); \ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) \ return FAIL; \ } while (0) /** Verify that the results of an ODBC function call on a connection handle was SQL_SUCCESS or SQL_SUCCESS_WITH_INFO. @param con Handle for database connection @param call The function call */ #define ok_con(con, call) \ do { \ SQLRETURN rc= (call); \ print_diag(rc, SQL_HANDLE_DBC, (con), #call, __FILE__, __LINE__); \ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) \ return FAIL; \ } while (0) /** Verify that a Boolean expression is true. It's recommended to use is_num, is_str, etc macros instead of "is(a==b)", since those will show values being compared, in a log. @param a The expression to check */ #define is(a) \ do { \ if (!(a)) { \ printf("# !(%s) in %s on line %d\n", \ #a, __FILE__, __LINE__); \ return FAIL; \ } \ } while (0); #define myassert(a) is(a) #define my_assert(a) is(a) /** Verify that a string (char *) matches an expected value. @param a The string to compare @param b The string to compare against @param c The number of characters to compare */ #define is_str(a, b, c) \ do { \ char *val_a= (char *)(a), *val_b= (char *)(b); \ int val_len= (int)(c); \ if (strncmp(val_a, val_b, val_len) != 0) { \ printf("# %s ('%*s') != '%*s' in %s on line %d\n", \ #a, val_len, val_a, val_len, val_b, __FILE__, __LINE__); \ return FAIL; \ } \ } while (0); /** Verify that a wide string (wchar_t *) matches an expected value. @param a The string to compare @param b The string to compare against @param c The number of characters to compare */ #define is_wstr(a, b, c) \ do { \ wchar_t *val_a= (a), *val_b= (b); \ int val_len= (int)(c); \ if (memcmp(val_a, val_b, val_len * sizeof(wchar_t)) != 0) { \ printf("# %s ('%*ls') != '%*ls' in %s on line %d\n", \ #a, val_len, val_a, val_len, val_b, __FILE__, __LINE__); \ return FAIL; \ } \ } while (0); /** Verify that a number (long integer) matches an expected value. @param a The number to compare @param b The number to compare against */ #define is_num(a, b) \ do { \ if (a != b) { \ printf("# %s (%lld) != %lld in %s on line %d\n", \ #a, (long long)a, (long long)b, __FILE__, __LINE__); \ return FAIL; \ } \ } while (0); /* convenience macro for using statement */ #define check_sqlstate(stmt, sqlstate) \ check_sqlstate_ex((stmt), SQL_HANDLE_STMT, (sqlstate)) int check_sqlstate_ex(SQLHANDLE hnd, SQLSMALLINT hndtype, char *sqlstate) { SQLCHAR sql_state[6]; SQLINTEGER err_code= 0; SQLCHAR err_msg[SQL_MAX_MESSAGE_LENGTH]= {0}; SQLSMALLINT err_len= 0; memset(err_msg, 'C', SQL_MAX_MESSAGE_LENGTH); SQLGetDiagRec(hndtype, hnd, 1, sql_state, &err_code, err_msg, SQL_MAX_MESSAGE_LENGTH - 1, &err_len); is_str(sql_state, (SQLCHAR *)sqlstate, 5); return OK; } /** */ static void print_diag(SQLRETURN rc, SQLSMALLINT htype, SQLHANDLE handle, const char *text, const char *file, int line) { if (rc != SQL_SUCCESS) { SQLCHAR sqlstate[6], message[SQL_MAX_MESSAGE_LENGTH]; SQLINTEGER native_error; SQLSMALLINT length; SQLRETURN drc; /** @todo map rc to SQL_SUCCESS_WITH_INFO, etc */ printf("# %s = %d\n", text, rc); /** @todo Handle multiple diagnostic records. */ drc= SQLGetDiagRec(htype, handle, 1, sqlstate, &native_error, message, SQL_MAX_MESSAGE_LENGTH - 1, &length); if (SQL_SUCCEEDED(drc)) printf("# [%6s] %*s in %s on line %d\n", sqlstate, length, message, file, line); else printf("# Did not get expected diagnostics from SQLGetDiagRec() = %d" " in file %s on line %d\n", drc, file, line); } } /* UTILITY MACROS */ #define myenv(henv,r) \ do { \ print_diag(r, SQL_HANDLE_ENV, (henv), "myenv(henv,r)", \ __FILE__, __LINE__); \ if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) \ return FAIL; \ } while (0) #define myenv_r(henv,r) \ do { \ print_diag(r, SQL_HANDLE_ENV, (henv), "myenv(henv_r,r)", \ __FILE__, __LINE__); \ if (r != SQL_ERROR) \ return FAIL; \ } while (0) #define myenv_err(henv,r,rc) \ do { \ print_diag(rc, SQL_HANDLE_ENV, (henv), "myenv_err(henv,r)",\ __FILE__, __LINE__); \ if (!r) \ return FAIL; \ } while (0) #define mycon(hdbc,r) \ do { \ print_diag(r, SQL_HANDLE_DBC, (hdbc), "mycon(hdbc,r)", \ __FILE__, __LINE__); \ if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) \ return FAIL; \ } while (0) #define mycon_r(hdbc,r) \ do { \ print_diag(r, SQL_HANDLE_DBC, (hdbc), "mycon_r(hdbc,r)", \ __FILE__, __LINE__); \ if (r != SQL_ERROR) \ return FAIL; \ } while (0) #define mycon_err(hdbc,r,rc) \ do { \ print_diag(rc, SQL_HANDLE_DBC, (hdbc), "mycon_err(hdbc,r)", \ __FILE__, __LINE__); \ if (!r) \ return FAIL; \ } while (0) #define mystmt(hstmt,r) \ do { \ print_diag(r, SQL_HANDLE_STMT, (hstmt), "mystmt(hstmt,r)", \ __FILE__, __LINE__); \ if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) \ return FAIL; \ } while (0) #define mystmt_r(hstmt,r) \ do { \ print_diag(r, SQL_HANDLE_STMT, (hstmt), "mystmt_r(hstmt,r)", \ __FILE__, __LINE__); \ if (r != SQL_ERROR) \ return FAIL; \ } while (0) #define mystmt_err(hstmt,r,rc) \ do { \ print_diag(rc, SQL_HANDLE_STMT, (hstmt), "mystmt_err(hstmt,r)", \ __FILE__, __LINE__); \ if (!r) \ return FAIL; \ } while (0) /** Print resultset dashes */ static int my_print_dashes(SQLHSTMT hstmt, SQLSMALLINT nCol) { SQLLEN disp_size, nullable; SQLCHAR ColName[MAX_NAME_LEN+1]; SQLUSMALLINT field_count, i, j; SQLSMALLINT col_len; field_count= (SQLUSMALLINT)nCol; fprintf(stdout, "# "); fprintf(stdout, "+"); for (i=1; i<= field_count; i++) { nullable = 0; ok_stmt(hstmt, SQLColAttribute(hstmt, i, SQL_DESC_BASE_COLUMN_NAME, &ColName, MAX_NAME_LEN, &col_len, NULL)); ok_stmt(hstmt, SQLColAttribute(hstmt, i, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &disp_size)); ok_stmt(hstmt, SQLColAttribute(hstmt, i, SQL_DESC_NULLABLE, NULL, 0, NULL, &nullable)); if (disp_size < col_len) disp_size = col_len; if (disp_size < 4 && nullable) disp_size = 4; /* We cap disp_size to avoid problems when we have problems with it being very large, such as 64-bit issues in the driver. */ disp_size= (disp_size > 512) ? 512 : disp_size; for (j=1; j < disp_size+3; j++) fprintf(stdout, "-"); fprintf(stdout, "+"); } fprintf(stdout, "\n"); return OK; } static int my_print_data(SQLHSTMT hstmt, SQLUSMALLINT index, SQLCHAR *data, SQLLEN length) { SQLLEN disp_size, nullable= 0; SQLCHAR ColName[MAX_NAME_LEN+1]; SQLSMALLINT col_len; nullable= 0; ok_stmt(hstmt, SQLColAttribute(hstmt, index, SQL_DESC_BASE_COLUMN_NAME, &ColName, MAX_NAME_LEN, &col_len, NULL)); ok_stmt(hstmt, SQLColAttribute(hstmt, index, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &disp_size)); ok_stmt(hstmt, SQLColAttribute(hstmt, index, SQL_DESC_NULLABLE, NULL, 0, NULL, &nullable)); if (disp_size < length) disp_size = length; if (disp_size < col_len) disp_size = col_len; if (disp_size < 4 && nullable) disp_size = 4; /* We cap disp_size to avoid problems when we have problems with it being very large, such as 64-bit issues in the driver. */ disp_size= (disp_size > 512) ? 512 : disp_size; if (length == SQL_NULL_DATA) fprintf(stdout, "%-*s |", (int)disp_size, "NULL"); else fprintf(stdout, "%-*s |", (int)disp_size, data); return OK; } /* If we just return FAIL from my_print_non_format_result - it will considered as 1 row by caller */ #define mystmt_rows(hstmt,r, row) \ do { \ print_diag(r, SQL_HANDLE_STMT, (hstmt), "mystmt_row(hstmt,r,row)", \ __FILE__, __LINE__); \ if (!SQL_SUCCEEDED(r)) \ return row; \ } while (0) /** RESULT SET */ int my_print_non_format_result(SQLHSTMT hstmt) { SQLRETURN rc; SQLUINTEGER nRowCount=0; SQLULEN pcColDef; SQLCHAR szColName[MAX_NAME_LEN+1]; SQLCHAR szData[MAX_COLUMNS][MAX_ROW_DATA_LEN]={{0}}; SQLSMALLINT nIndex,ncol= 0,pfSqlType, pcbScale, pfNullable; SQLLEN ind_strlen; rc = SQLNumResultCols(hstmt,&ncol); mystmt_rows(hstmt,rc,-1); for (nIndex = 1; nIndex <= ncol; ++nIndex) { rc = SQLDescribeCol(hstmt,nIndex,szColName, MAX_NAME_LEN, NULL, &pfSqlType,&pcColDef,&pcbScale,&pfNullable); /* Returning in case of an error -nIndex we will see in the log column# */ mystmt_rows(hstmt,rc,-nIndex); fprintf(stdout, "%s\t", szColName); rc = SQLBindCol(hstmt,nIndex, SQL_C_CHAR, szData[nIndex-1], MAX_ROW_DATA_LEN+1,&ind_strlen); mystmt_rows(hstmt,rc,-nIndex); } fprintf(stdout,"\n"); rc = SQLFetch(hstmt); while (SQL_SUCCEEDED(rc)) { ++nRowCount; for (nIndex=0; nIndex< ncol; ++nIndex) fprintf(stdout, "%s\t", szData[nIndex]); fprintf(stdout, "\n"); rc = SQLFetch(hstmt); } SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); fprintf(stdout, "# Total rows fetched: %d\n", (int)nRowCount); return nRowCount; } /** RESULT SET */ SQLINTEGER myresult(SQLHSTMT hstmt) { SQLRETURN rc; SQLUINTEGER nRowCount; SQLCHAR ColName[MAX_NAME_LEN+1]; SQLCHAR Data[MAX_ROW_DATA_LEN+1]; SQLLEN pcbLength; SQLUSMALLINT nIndex; SQLSMALLINT ncol; rc = SQLNumResultCols(hstmt,&ncol); mystmt(hstmt,rc); if (ncol > 10) return my_print_non_format_result(hstmt); if (my_print_dashes(hstmt, ncol) != OK) return -1; fprintf(stdout, "# |"); for (nIndex = 1; nIndex <= ncol; ++nIndex) { ok_stmt(hstmt, SQLColAttribute(hstmt, nIndex, SQL_DESC_BASE_COLUMN_NAME, ColName, MAX_NAME_LEN, NULL, NULL)); if (my_print_data(hstmt, nIndex, ColName, 0) != OK) return -1; } fprintf(stdout, "\n"); if (my_print_dashes(hstmt, ncol) != OK) return -1; nRowCount= 0; rc = SQLFetch(hstmt); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { nRowCount++; fprintf(stdout, "# |"); for (nIndex=1; nIndex<= ncol; ++nIndex) { rc = SQLGetData(hstmt, nIndex, SQL_C_CHAR, Data, MAX_ROW_DATA_LEN,&pcbLength); mystmt(hstmt,rc); if (my_print_data(hstmt, nIndex, Data, pcbLength) != OK) return -1; } fprintf(stdout,"\n"); rc = SQLFetch(hstmt); } if (my_print_dashes(hstmt, ncol) != OK) return -1; SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); fprintf(stdout, "# Total rows fetched: %d\n", (int)nRowCount); return nRowCount; } /** ROWCOUNT */ SQLUINTEGER myrowcount(SQLHSTMT hstmt) { SQLRETURN rc; SQLUINTEGER nRowCount= 0; rc = SQLFetch(hstmt); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { nRowCount++; rc = SQLFetch(hstmt); } SQLFreeStmt(hstmt,SQL_UNBIND); fprintf(stdout, "# Total rows fetched: %d\n", (int)nRowCount); return(nRowCount); } /** SQLExecDirect */ SQLRETURN tmysql_exec(SQLHSTMT hstmt, char *sql_stmt) { return(SQLExecDirect(hstmt,(SQLCHAR *)sql_stmt,SQL_NTS)); } /** SQLPrepare */ SQLRETURN tmysql_prepare(SQLHSTMT hstmt, char *sql_stmt) { return(SQLPrepare(hstmt, (SQLCHAR *)sql_stmt, SQL_NTS)); } /** return integer data by fetching it */ SQLINTEGER my_fetch_int(SQLHSTMT hstmt, SQLUSMALLINT icol) { SQLINTEGER nData; SQLLEN len; SQLGetData(hstmt, icol, SQL_INTEGER, &nData, 0, &len); printMessage("my_fetch_int: %ld (%ld)", (long int)nData, len); return (len != SQL_NULL_DATA) ? nData : 0; } /** return unsigned integer data by fetching it */ SQLUINTEGER my_fetch_uint(SQLHSTMT hstmt, SQLUSMALLINT icol) { SQLUINTEGER nData; SQLLEN len; SQLGetData(hstmt, icol, SQL_C_ULONG, &nData, 0, &len); printMessage("my_fetch_int: %ld (%ld)", (long int)nData, len); return (len != SQL_NULL_DATA) ? nData : 0; } /** return string data, by fetching it */ const char *my_fetch_str(SQLHSTMT hstmt, SQLCHAR *szData,SQLUSMALLINT icol) { SQLLEN nLen; SQLGetData(hstmt,icol,SQL_CHAR,szData,MAX_ROW_DATA_LEN+1,&nLen); /* If Null value - putting down smth meaningful. also that allows caller to better/(in more easy way) test the value */ if (nLen < 0) { strcpy(szData, "(Null)"); } printMessage(" my_fetch_str: %s(%ld)", szData, nLen); return((const char *)szData); } /** Convert a SQLWCHAR to wchar_t */ wchar_t *sqlwchar_to_wchar_t(SQLWCHAR *in) { static wchar_t buff[2048]; wchar_t *to= buff; if (sizeof(wchar_t) == sizeof(SQLWCHAR)) return (wchar_t *)in; for ( ; *in && to < buff + sizeof(buff) - 2; in++) to+= utf16toutf32((UTF16 *)in, (UTF32 *)to); *to= L'\0'; return buff; } /** return wide string data, by fetching it and possibly converting it to wchar_t */ wchar_t *my_fetch_wstr(SQLHSTMT hstmt, SQLWCHAR *buffer, SQLUSMALLINT icol) { SQLRETURN rc; SQLLEN nLen; rc= SQLGetData(hstmt, icol, SQL_WCHAR, buffer, MAX_ROW_DATA_LEN + 1, &nLen); if (!SQL_SUCCEEDED(rc)) return L""; buffer[nLen/sizeof(SQLWCHAR)]= 0; return sqlwchar_to_wchar_t(buffer); } /* Check if driver supports SQLSetPos */ int driver_supports_setpos(SQLHDBC hdbc) { SQLRETURN rc; SQLUSMALLINT status= TRUE; rc = SQLGetFunctions(hdbc, SQL_API_SQLSETPOS, &status); mycon(hdbc,rc); if (!status) return FALSE; return TRUE; } /* Check for minimal MySQL version */ int mysql_min_version(SQLHDBC hdbc, char *min_version, unsigned int length) { SQLCHAR server_version[MYSQL_NAME_LEN+1]; SQLRETURN rc; rc = SQLGetInfo(hdbc,SQL_DBMS_VER,server_version,MYSQL_NAME_LEN,NULL); mycon(hdbc, rc); if (strncmp((char *)server_version, min_version, length) >= 0) return TRUE; return FALSE; } /* Check if server supports transactions */ int server_supports_trans(SQLHDBC hdbc) { SQLSMALLINT trans; SQLRETURN rc; rc = SQLGetInfo(hdbc,SQL_TXN_CAPABLE,&trans,0,NULL); mycon(hdbc,rc); if (trans != SQL_TC_NONE) return TRUE; return FALSE; } /** DRV CONNECTION */ int mydrvconnect(SQLHENV *henv, SQLHDBC *hdbc, SQLHSTMT *hstmt, SQLCHAR *connIn) { SQLCHAR connOut[MAX_NAME_LEN+1]; SQLSMALLINT len; ok_env(*henv, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, henv)); ok_env(*henv, SQLSetEnvAttr(*henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0)); ok_env(*henv, SQLAllocHandle(SQL_HANDLE_DBC, *henv, hdbc)); ok_con(*hdbc, SQLDriverConnect(*hdbc, NULL, connIn, SQL_NTS, connOut, MAX_NAME_LEN, &len, SQL_DRIVER_NOPROMPT)); ok_con(*hdbc, SQLSetConnectAttr(*hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0)); ok_con(*hdbc, SQLAllocHandle(SQL_HANDLE_STMT, *hdbc, hstmt)); return OK; } int alloc_basic_handles(SQLHENV *henv, SQLHDBC *hdbc, SQLHSTMT *hstmt) { SQLCHAR connIn[MAX_NAME_LEN+1], connOut[MAX_NAME_LEN+1]; SQLSMALLINT len; ok_env(*henv, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, henv)); ok_env(*henv, SQLSetEnvAttr(*henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0)); ok_env(*henv, SQLAllocHandle(SQL_HANDLE_DBC, *henv, hdbc)); sprintf((char *)connIn, "DSN=%s;UID=%s;PWD=%s;DATABASE=test;OPTION=%d", (char *)mydsn, (char *)myuid, (char *)mypwd, myoption); if (mysock && mysock[0]) { strcat((char *)connIn, ";SOCKET="); strcat((char *)connIn, (char *)mysock); } if (myport) { char buff[20]; sprintf(buff, ";PORT=%d", myport); strcat((char *)connIn, buff); } ok_con(*hdbc, SQLDriverConnect(*hdbc, NULL, connIn, SQL_NTS, connOut, MAX_NAME_LEN, &len, SQL_DRIVER_NOPROMPT)); ok_con(*hdbc, SQLSetConnectAttr(*hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0)); ok_con(*hdbc, SQLAllocHandle(SQL_HANDLE_STMT, *hdbc, hstmt)); return OK; } int free_basic_handles(SQLHENV *henv,SQLHDBC *hdbc, SQLHSTMT *hstmt) { /* We don't care if this succeeds, the connection may have gone away. */ (void)SQLEndTran(SQL_HANDLE_DBC, *hdbc, SQL_COMMIT); ok_stmt(*hstmt, SQLFreeStmt(*hstmt, SQL_DROP)); ok_con(*hdbc, SQLDisconnect(*hdbc)); ok_con(*hdbc, SQLFreeConnect(*hdbc)); ok_env(*henv, SQLFreeEnv(*henv)); return OK; } /** Helper for possibly converting a (wchar_t *) to a (SQLWCHAR *). TODO: [almost?] all uses of those macros in tests lead to memore leak, as resulting pointer is not remembered and thus memory never freed */ #define W(string) dup_wchar_t_as_sqlwchar((string), \ sizeof(string) / sizeof(wchar_t)) #define WL(string, len) dup_wchar_t_as_sqlwchar((string), (len)) /** Convert a wchar_t * to a SQLWCHAR * if a wchar_t is not the same as SQLWCHAR. New space is allocated and never freed. Because this is used in short-lived test programs, this is okay, if not ideal. */ SQLWCHAR *dup_wchar_t_as_sqlwchar(wchar_t *from, size_t len) { if (sizeof(wchar_t) == sizeof(SQLWCHAR)) { SQLWCHAR *to= malloc(len * sizeof(SQLWCHAR)); memcpy(to, from, len * sizeof(wchar_t)); return to; } else { size_t i; SQLWCHAR *to= malloc(2 * len * sizeof(SQLWCHAR)); SQLWCHAR *out= to; for (i= 0; i < len; i++) to+= utf32toutf16((UTF32)from[i], (UTF16 *)to); *to= 0; return out; } } /** Helper for converting a (char *) to a (SQLWCHAR *) */ #define WC(string) dup_char_as_sqlwchar((string)) /** Convert a char * to a SQLWCHAR *. New space is allocated and never freed. Because this is used in short-lived test programs, this is okay, if not ideal. */ SQLWCHAR *dup_char_as_sqlwchar(SQLCHAR *from) { SQLWCHAR *to= malloc((strlen((char *)from) + 1) * sizeof(SQLWCHAR)); SQLWCHAR *out= to; while (from && *from) *(to++)= (SQLWCHAR)*(from++); *to= 0; return out; } /** Check if we are using a driver manager for testing. @param[in] hdbc Connection handle @return 0 if the connection is using a driver manager, 1 if not. */ int using_dm(HDBC hdbc) { SQLCHAR val[20]; SQLSMALLINT len; if (SQLGetInfo(hdbc, SQL_DM_VER, val, sizeof(val), &len) == SQL_ERROR) return 0; return 1; } /* Check if we are using the unixODBC version specified */ int using_unixodbc_version(SQLHANDLE henv, SQLCHAR *ver) { #ifdef SQL_ATTR_UNIXODBC_VERSION SQLCHAR buf[10]; if(SQLGetEnvAttr(henv, SQL_ATTR_UNIXODBC_VERSION, buf, 10, NULL) != SQL_SUCCESS) return 0; if(!strcmp(buf, ver)) return 1; #endif /* SQL_ATTR_UNIXODBC_VERSION */ return 0; } mysql-connector-odbc-5.1.10-src/test/my_cursor.c100644 15766 12 307740 11707541005 20406 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" /* perform positioned update and delete */ DECLARE_TEST(my_positioned_cursor) { SQLLEN nRowCount; SQLHSTMT hstmt_pos; SQLCHAR data[10]; ok_sql(hstmt, "DROP TABLE IF EXISTS my_demo_cursor"); ok_sql(hstmt, "CREATE TABLE my_demo_cursor (id INT, name VARCHAR(20))"); ok_sql(hstmt, "INSERT INTO my_demo_cursor VALUES (0,'MySQL0'),(1,'MySQL1')," "(2,'MySQL2'),(3,'MySQL3'),(4,'MySQL4')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC,0)); /* set the cursor name as 'mysqlcur' on hstmt */ ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"mysqlcur", SQL_NTS)); /* Open the resultset of table 'my_demo_cursor' */ ok_sql(hstmt,"SELECT * FROM my_demo_cursor"); /* goto the last row */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_LAST, 1L)); /* create new statement handle */ ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt_pos)); /* now update the name field to 'updated' using positioned cursor */ ok_sql(hstmt_pos, "UPDATE my_demo_cursor SET name='updated' " "WHERE CURRENT OF mysqlcur"); ok_stmt(hstmt, SQLRowCount(hstmt_pos, &nRowCount)); is_num(nRowCount, 1); ok_stmt(hstmt_pos, SQLFreeStmt(hstmt_pos, SQL_CLOSE)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Now delete 2nd row */ ok_sql(hstmt, "SELECT * FROM my_demo_cursor"); /* goto the second row */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 2L)); /* now delete the current row */ ok_sql(hstmt_pos, "DELETE FROM my_demo_cursor WHERE CURRENT OF mysqlcur"); ok_stmt(hstmt, SQLRowCount(hstmt_pos, &nRowCount)); is_num(nRowCount, 1); /* free the statement cursor */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Free the statement 'hstmt_pos' */ ok_stmt(hstmt_pos, SQLFreeHandle(SQL_HANDLE_STMT, hstmt_pos)); /* Now fetch and verify the data */ ok_sql(hstmt, "SELECT * FROM my_demo_cursor"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 0); is_str(my_fetch_str(hstmt, data, 2), "MySQL0", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 2); is_str(my_fetch_str(hstmt, data, 2), "MySQL2", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 3); is_str(my_fetch_str(hstmt, data, 2), "MySQL3", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 4); is_str(my_fetch_str(hstmt, data, 2), "updated", 7); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS my_demo_cursor"); return OK; } /* perform delete and update using SQLSetPos */ DECLARE_TEST(my_setpos_cursor) { SQLLEN nRowCount; SQLINTEGER id; SQLCHAR name[50]; ok_sql(hstmt, "DROP TABLE IF EXISTS my_demo_cursor"); ok_sql(hstmt, "CREATE TABLE my_demo_cursor (id INT, name VARCHAR(20))"); ok_sql(hstmt, "INSERT INTO my_demo_cursor VALUES (0,'MySQL0'),(1,'MySQL1')," "(2,'MySQL2'),(3,'MySQL3'),(4,'MySQL4')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "SELECT * FROM my_demo_cursor"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &id, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, name, sizeof(name),NULL)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 1L)); strcpy((char *)name, "first-row"); /* now update the name field to 'first-row' using SQLSetPos */ ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nRowCount)); is_num(nRowCount, 1); /* position to second row and delete it ..*/ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 2L)); /* now delete the current, second row */ ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nRowCount)); is_num(nRowCount, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Now fetch and verify the data */ ok_sql(hstmt, "SELECT * FROM my_demo_cursor"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 0); is_str(my_fetch_str(hstmt, name, 2), "first-row", 9); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 2); is_str(my_fetch_str(hstmt, name, 2), "MySQL2", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 3); is_str(my_fetch_str(hstmt, name, 2), "MySQL3", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 4); is_str(my_fetch_str(hstmt, name, 2), "MySQL4", 6); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE my_demo_cursor"); return OK; } /** Bug #5853: Using Update with 'WHERE CURRENT OF' with binary data crashes */ DECLARE_TEST(t_bug5853) { SQLRETURN rc; SQLHSTMT hstmt_pos; SQLCHAR nData[4]; SQLLEN nLen= SQL_DATA_AT_EXEC; int i= 0; ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt_pos)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug5853"); ok_sql(hstmt,"CREATE TABLE t_bug5853 (id INT AUTO_INCREMENT PRIMARY KEY, a VARCHAR(3))"); ok_sql(hstmt,"INSERT INTO t_bug5853 (a) VALUES ('abc'),('def')"); ok_stmt(hstmt_pos, SQLPrepare(hstmt_pos, (SQLCHAR *) "UPDATE t_bug5853 SET a = ? WHERE CURRENT OF bug5853", SQL_NTS)); ok_stmt(hstmt_pos, SQLBindParameter(hstmt_pos, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 0, 0, NULL, 0, &nLen)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC,0)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"bug5853", SQL_NTS)); ok_sql(hstmt, "SELECT * FROM t_bug5853"); while ((rc= SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)) != SQL_NO_DATA_FOUND) { char data[2][3] = { "uvw", "xyz" }; expect_stmt(hstmt_pos, SQLExecute(hstmt_pos), SQL_NEED_DATA); rc= SQL_NEED_DATA; while (rc == SQL_NEED_DATA) { SQLPOINTER token; rc= SQLParamData(hstmt_pos, &token); if (rc == SQL_NEED_DATA) { ok_stmt(hstmt_pos, SQLPutData(hstmt_pos, data[i++ % 2], sizeof(data[0]))); } } } ok_stmt(hstmt_pos, SQLFreeStmt(hstmt_pos, SQL_CLOSE)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt,"SELECT * FROM t_bug5853"); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, nData, sizeof(nData), &nLen)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_str(nData, "uvw", 3); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_str(nData, "xyz", 3); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug5853"); return OK; } DECLARE_TEST(t_setpos_del_all) { SQLINTEGER nData[4]; SQLCHAR szData[4][10]; SQLUSMALLINT rgfRowStatus[4]; SQLLEN nlen; ok_sql(hstmt, "DROP TABLE IF EXISTS t_setpos_del_all"); ok_sql(hstmt, "CREATE TABLE t_setpos_del_all (a INT NOT NULL PRIMARY KEY," "b VARCHAR(20))"); ok_sql(hstmt, "INSERT INTO t_setpos_del_all VALUES (100,'MySQL1')," "(200,'MySQL2'),(300,'MySQL3'),(400,'MySQL4')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu", SQL_NTS)); ok_stmt(hstmt, SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, 4)); ok_sql(hstmt, "SELECT * FROM t_setpos_del_all ORDER BY a ASC"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData[0]), NULL)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_FIRST, 1, NULL, rgfRowStatus)); is_num(nData[0], 100); is_str(szData[0], "MySQL1", 6); is_num(nData[1], 200); is_str(szData[1], "MySQL2", 6); is_num(nData[2], 300); is_str(szData[2], "MySQL3", 6); is_num(nData[3], 400); is_str(szData[3], "MySQL4", 6); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLSetPos(hstmt, 0, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); is_num(nlen, 4); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_setpos_del_all"); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, 1)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_setpos_del_all"); return OK; } DECLARE_TEST(t_setpos_upd_decimal) { SQLINTEGER rec; SQLUSMALLINT status; ok_sql(hstmt, "DROP TABLE IF EXISTS t_setpos_upd_decimal"); ok_sql(hstmt, "CREATE TABLE t_setpos_upd_decimal (record DECIMAL(3,0)," "num1 FLOAT, num2 DECIMAL(6,0), num3 DECIMAL(10,3))"); ok_sql(hstmt, "INSERT INTO t_setpos_upd_decimal VALUES (1,12.3,134,0.100)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT record FROM t_setpos_upd_decimal"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &rec, 0, NULL)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, NULL, &status)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); rec= 100; expect_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE), SQL_ERROR); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_setpos_upd_decimal"); return OK; } DECLARE_TEST(t_setpos_position) { SQLINTEGER nData; SQLLEN nlen; SQLCHAR szData[255]; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS t_setpos_position"); ok_sql(hstmt, "CREATE TABLE t_setpos_position (a INT, b VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO t_setpos_position VALUES (100,'MySQL1')," "(200,'MySQL2'),(300,'MySQL3')"); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu", SQL_NTS)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_ROWVER, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0)); ok_sql(hstmt, "SELECT * FROM t_setpos_position"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), &nlen)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &pcrow, &rgfRowStatus)); is_num(nData, 100); is_num(nlen, 6); is_str(szData, "MySQL1", 6); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); nData= 1000; strcpy((char *)szData, "updated"); nlen= 7; expect_stmt(hstmt, SQLSetPos(hstmt, 3, SQL_UPDATE, SQL_LOCK_NO_CHANGE), SQL_ERROR); expect_stmt(hstmt, SQLSetPos(hstmt, 2, SQL_UPDATE, SQL_LOCK_NO_CHANGE), SQL_ERROR); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); is_num(nlen, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_setpos_position"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1000); is_str(my_fetch_str(hstmt, szData, 2), "updated", 7); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 200); is_str(my_fetch_str(hstmt, szData, 2), "MySQL2", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 300); is_str(my_fetch_str(hstmt, szData, 2), "MySQL3", 6); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DELETE FROM t_setpos_position WHERE b = 'updated'"); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); is_num(nlen, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_setpos_position"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 200); is_str(my_fetch_str(hstmt, szData, 2), "MySQL2", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 300); is_str(my_fetch_str(hstmt, szData, 2), "MySQL3", 6); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_setpos_position"); return OK; } DECLARE_TEST(t_pos_column_ignore) { SQLCHAR szData[20]; SQLINTEGER nData; SQLLEN pcbValue, nlen; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_column_ignore"); ok_sql(hstmt, "CREATE TABLE t_pos_column_ignore " "(col1 INT NOT NULL PRIMARY KEY, col2 VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO t_pos_column_ignore VALUES (10,'venu'),(100,'MySQL')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_ROWVER, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_KEYSET_DRIVEN, 0)); /* ignore all columns */ ok_sql(hstmt, "SELECT * FROM t_pos_column_ignore ORDER BY col1 ASC"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, &pcbValue)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), &pcbValue)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &pcrow, &rgfRowStatus)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); nData= 99; strcpy((char *)szData , "updated"); pcbValue= SQL_COLUMN_IGNORE; expect_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE), SQL_ERROR); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_pos_column_ignore ORDER BY col1 ASC"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 10); is_str(my_fetch_str(hstmt, szData, 2), "venu", 4); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* ignore only one column */ ok_sql(hstmt, "SELECT * FROM t_pos_column_ignore ORDER BY col1 ASC"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), &pcbValue)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &pcrow, &rgfRowStatus)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); nData= 99; strcpy((char *)szData , "updated"); pcbValue= SQL_COLUMN_IGNORE; ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); is_num(nlen, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_pos_column_ignore ORDER BY col1 ASC"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 99); is_str(my_fetch_str(hstmt, szData, 2), "venu", 4); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_column_ignore"); return OK; } DECLARE_TEST(t_pos_datetime_delete) { SQLHSTMT hstmt1; SQLINTEGER int_data; SQLLEN row_count; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_datetime_delete"); ok_sql(hstmt, "CREATE TABLE t_pos_datetime_delete (a INT NOT NULL DEFAULT 0," "b VARCHAR(20) NOT NULL DEFAULT '', c DATETIME NOT NULL DEFAULT '2000-01-01')"); ok_sql(hstmt, "INSERT INTO t_pos_datetime_delete VALUES" "(1,'venu','2003-02-10 14:45:39')"); ok_sql(hstmt, "INSERT INTO t_pos_datetime_delete (b) VALUES ('')"); ok_sql(hstmt, "INSERT INTO t_pos_datetime_delete (a) VALUES (2)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_ROWVER, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu_cur", 8)); ok_sql(hstmt, "SELECT * FROM t_pos_datetime_delete"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &int_data, 0, NULL)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, NULL, &rgfRowStatus)); is_num(int_data, 1); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_con(hdbc, SQLAllocStmt(hdbc, &hstmt1)); ok_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_ROWVER, 0)); ok_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0)); ok_sql(hstmt1, "DELETE FROM t_pos_datetime_delete WHERE CURRENT OF venu_cur"); ok_stmt(hstmt1, SQLRowCount(hstmt1, &row_count)); is_num(row_count, 1); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, NULL, &rgfRowStatus)); is_num(int_data, 0); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, NULL, NULL)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_sql(hstmt1, "DELETE FROM t_pos_datetime_delete WHERE CURRENT OF venu_cur"); ok_stmt(hstmt1, SQLRowCount(hstmt1, &row_count)); is_num(row_count, 1); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_pos_datetime_delete"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 0); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_datetime_delete"); return OK; } DECLARE_TEST(t_pos_datetime_delete1) { SQLRETURN rc; SQLHSTMT hstmt1; SQLINTEGER int_data; SQLLEN row_count, cur_type; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_delete"); rc = SQLAllocStmt(hdbc,&hstmt1); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"create table t_pos_delete(id int not null default '0',\ name varchar(20) NOT NULL default '',\ created datetime NOT NULL default '2000-01-01')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_pos_delete values(1,'venu','2003-02-10 14:45:39')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_pos_delete(name) values('')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_pos_delete(id) values(2)"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_pos_delete(id) values(3)"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_pos_delete(id) values(4)"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_pos_delete(id) values(5)"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from t_pos_delete"); mystmt(hstmt,rc); my_assert(6 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER) SQL_CONCUR_ROWVER, 0); SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) SQL_CURSOR_DYNAMIC, 0); SQLSetStmtOption(hstmt,SQL_SIMULATE_CURSOR,SQL_SC_NON_UNIQUE); SQLSetStmtAttr(hstmt1, SQL_ATTR_CONCURRENCY, (SQLPOINTER) SQL_CONCUR_ROWVER, 0); SQLSetStmtAttr(hstmt1, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) SQL_CURSOR_DYNAMIC, 0); SQLSetStmtOption(hstmt1,SQL_SIMULATE_CURSOR,SQL_SC_NON_UNIQUE); rc = SQLSetCursorName(hstmt, (SQLCHAR *)"venu_cur",8); mystmt(hstmt,rc); rc = SQLGetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, &cur_type, 0, NULL); mystmt(hstmt,rc); ok_sql(hstmt,"select * from t_pos_delete"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&int_data,0,NULL); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_ABSOLUTE,3,NULL,&rgfRowStatus); mystmt(hstmt,rc); fprintf(stdout,"current_row: %d\n", int_data); myassert(int_data == 2); rc = SQLSetPos(hstmt,1,SQL_POSITION,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); ok_sql(hstmt1,"DELETE FROM t_pos_delete WHERE CURRENT OF venu_cur"); rc = SQLRowCount(hstmt1,&row_count); mystmt(hstmt1,rc); fprintf(stdout, "rows affected: %d\n", row_count); myassert(row_count == 1); rc = SQLExtendedFetch(hstmt,SQL_FETCH_NEXT,1,NULL,&rgfRowStatus); mystmt(hstmt,rc); fprintf(stdout,"current_row: %d\n", int_data); rc = SQLExtendedFetch(hstmt,SQL_FETCH_NEXT,1,NULL,&rgfRowStatus); mystmt(hstmt,rc); fprintf(stdout,"current_row: %d\n", int_data); /*rc = SQLExtendedFetch(hstmt,SQL_FETCH_NEXT,1,NULL,NULL); mystmt(hstmt,rc);*/ rc = SQLSetPos(hstmt,1,SQL_POSITION,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); ok_sql(hstmt1,"DELETE FROM t_pos_delete WHERE CURRENT OF venu_cur"); rc = SQLRowCount(hstmt1,&row_count); mystmt(hstmt1,rc); fprintf(stdout, "rows affected: %d\n", row_count); myassert(row_count == 1); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); SQLFreeStmt(hstmt1,SQL_CLOSE); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"select * from t_pos_delete"); mystmt(hstmt,rc); my_assert(4 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt1,SQL_DROP); mystmt(hstmt1,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_delete"); return OK; } DECLARE_TEST(t_getcursor) { SQLRETURN rc; SQLHSTMT hstmt1,hstmt2,hstmt3; SQLCHAR curname[50]; SQLSMALLINT nlen; rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt1); mycon(hdbc, rc); rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt2); mycon(hdbc, rc); rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt3); mycon(hdbc, rc); rc = SQLGetCursorName(hstmt1,curname,50,&nlen); if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { fprintf(stdout,"default cursor name : %s(%d)\n",curname,nlen); is_num(nlen, 8); is_str(curname,"SQL_CUR0", 9); rc = SQLGetCursorName(hstmt3,curname,50,&nlen); mystmt(hstmt1,rc); fprintf(stdout,"default cursor name : %s(%d)\n",curname,nlen); rc = SQLGetCursorName(hstmt1,curname,4,&nlen); mystmt_err(hstmt1,rc == SQL_SUCCESS_WITH_INFO, rc); fprintf(stdout,"truncated cursor name: %s(%d)\n",curname,nlen); is_num(nlen, 8); is_str(curname, "SQL", 4); rc = SQLGetCursorName(hstmt1,curname,0,&nlen); mystmt_err(hstmt1,rc == SQL_SUCCESS_WITH_INFO, rc); fprintf(stdout,"untouched cursor name: %s(%d)\n",curname,nlen); myassert(nlen == 8); expect_stmt(hstmt1, SQLGetCursorName(hstmt1, curname, 8, &nlen), SQL_SUCCESS_WITH_INFO); fprintf(stdout,"truncated cursor name: %s(%d)\n",curname,nlen); is_num(nlen, 8); is_str(curname, "SQL_CUR", 8); rc = SQLGetCursorName(hstmt1,curname,9,&nlen); fprintf(stdout,"full cursor name : %s(%d)\n",curname,nlen); is_num(nlen, 8); is_str(curname, "SQL_CUR0", 9); } rc = SQLSetCursorName(hstmt1, (SQLCHAR *)"venucur123",7); mystmt(hstmt1,rc); rc = SQLGetCursorName(hstmt1,curname,8,&nlen); mystmt(hstmt1,rc); is_num(nlen, 7); is_str(curname, "venucur", 8); rc = SQLFreeHandle(SQL_HANDLE_STMT,hstmt1); mystmt(hstmt1,rc); return OK; } DECLARE_TEST(t_getcursor1) { SQLRETURN rc; SQLHSTMT hstmt1; SQLCHAR curname[50]; SQLSMALLINT nlen,index; for(index=0; index < 100; index++) { rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt1); mycon(hdbc, rc); rc = SQLGetCursorName(hstmt1,curname,50,&nlen); if (rc != SQL_SUCCESS) break; fprintf(stdout,"%s(%d) \n",curname,nlen); rc = SQLFreeHandle(SQL_HANDLE_STMT,hstmt1); mystmt(hstmt1,rc); } return OK; } DECLARE_TEST(t_acc_crash) { SQLINTEGER id; SQLCHAR name[20], data[30]; /* Hasn't that actually to be a SQL_DATE_STRUCT */ SQL_TIMESTAMP_STRUCT ts; SQLLEN ind_strlen; ok_sql(hstmt, "DROP TABLE IF EXISTS t_acc_crash"); ok_sql(hstmt, "CREATE TABLE t_acc_crash (a INT NOT NULL AUTO_INCREMENT," "b CHAR(20), c DATE, PRIMARY KEY (a))"); ok_sql(hstmt, "INSERT INTO t_acc_crash (b) VALUES ('venu'),('monty'),('mysql')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, 1)); ok_sql(hstmt, "SELECT * FROM t_acc_crash ORDER BY a ASC"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &id, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, name, sizeof(name), NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_DATE, &ts, 0, &ind_strlen)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 1)); id= 9; strcpy((char *)name, "updated"); ts.year= 2010; ts.month= 9; ts.day= 25; ind_strlen= 0; ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_acc_crash ORDER BY a DESC"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 9); is_str(my_fetch_str(hstmt, data, 2), "updated", 7); is_str(my_fetch_str(hstmt, data, 3), "2010-09-25", 10); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_acc_crash"); return OK; } DECLARE_TEST(tmysql_setpos_del) { SQLINTEGER nData; SQLLEN nlen; SQLCHAR szData[255]; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos_del"); ok_sql(hstmt, "CREATE TABLE tmysql_setpos_del (a INT, b VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO tmysql_setpos_del VALUES (100,'MySQL1')," "(200,'MySQL2'),(300,'MySQL3'),(400,'MySQL4'),(300,'MySQL5')," "(300,'MySQL6'),(300,'MySQL7')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu", SQL_NTS)); ok_sql(hstmt, "SELECT * FROM tmysql_setpos_del"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), &nlen)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 5, &pcrow, &rgfRowStatus)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); is_num(nlen, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM tmysql_setpos_del"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 100); is_str(my_fetch_str(hstmt, szData, 2), "MySQL1", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 200); is_str(my_fetch_str(hstmt, szData, 2), "MySQL2", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 300); is_str(my_fetch_str(hstmt, szData, 2), "MySQL3", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 400); is_str(my_fetch_str(hstmt, szData, 2), "MySQL4", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 300); is_str(my_fetch_str(hstmt, szData, 2), "MySQL6", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 300); is_str(my_fetch_str(hstmt, szData, 2), "MySQL7", 6); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos_del"); return OK; } DECLARE_TEST(tmysql_setpos_del1) { SQLINTEGER nData; SQLLEN nlen; SQLCHAR szData[255]; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos_del1"); ok_sql(hstmt, "CREATE TABLE tmysql_setpos_del1 (a INT, b VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO tmysql_setpos_del1 VALUES (100,'MySQL1')," "(200,'MySQL2'),(300,'MySQL3'),(400,'MySQL4')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu", SQL_NTS)); ok_sql(hstmt, "SELECT * FROM tmysql_setpos_del1"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), &nlen)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 3, &pcrow, &rgfRowStatus)); ok_stmt(hstmt, SQLSetPos(hstmt, 0, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); is_num(nlen, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM tmysql_setpos_del1"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 100); is_str(my_fetch_str(hstmt, szData, 2), "MySQL1", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 200); is_str(my_fetch_str(hstmt, szData, 2), "MySQL2", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 400); is_str(my_fetch_str(hstmt, szData, 2), "MySQL4", 6); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos_del1"); return OK; } DECLARE_TEST(tmysql_setpos_upd) { SQLRETURN rc; SQLINTEGER nData = 500; SQLLEN nlen; SQLCHAR szData[255]={0}; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos"); rc = tmysql_exec(hstmt,"create table tmysql_setpos(col1 int, col2 varchar(30))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos values(100,'MySQL1')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos values(300,'MySQL3')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos values(200,'MySQL2')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos values(300,'MySQL3')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos values(400,'MySQL4')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos values(300,'MySQL3')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); rc = SQLSetCursorName(hstmt, (SQLCHAR *)"venu",SQL_NTS); ok_sql(hstmt,"select * from tmysql_setpos"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,100,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,100,NULL); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_ABSOLUTE,3,&pcrow,&rgfRowStatus); mystmt(hstmt,rc); printMessage(" pcrow:%d\n",pcrow); printMessage(" row1:%d,%s\n",nData,szData); rc = SQLSetPos(hstmt,1,SQL_POSITION,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); nData = 1000; strcpy((char *)szData , "updated"); rc = SQLSetPos(hstmt,3,SQL_UPDATE,SQL_LOCK_NO_CHANGE); mystmt_err(hstmt,rc== SQL_ERROR,rc); rc = SQLSetPos(hstmt,1,SQL_UPDATE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage(" rows affected:%d\n",nlen); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from tmysql_setpos"); mystmt(hstmt,rc); myresult(hstmt); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt,"DELETE FROM tmysql_setpos WHERE col2 = 'updated'"); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage("\n total rows affceted:%d",nlen); my_assert(nlen == 1); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"select * from tmysql_setpos"); mystmt(hstmt,rc); my_assert(5 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos"); return OK; } DECLARE_TEST(tmysql_setpos_add) { SQLRETURN rc; SQLINTEGER nData= 500; SQLLEN nlen; SQLCHAR szData[255]={0}; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos_add"); rc = tmysql_exec(hstmt,"create table tmysql_setpos_add(col1 int, col2 varchar(30))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos_add values(100,'MySQL1')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos_add values(300,'MySQL3')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLSetCursorName(hstmt, (SQLCHAR *)"venu",SQL_NTS); ok_sql(hstmt,"select * from tmysql_setpos_add"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,100,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,100,NULL); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_NEXT,1,&pcrow,&rgfRowStatus); mystmt(hstmt,rc); nData = 1000; strcpy((char *)szData , "insert-new1"); rc = SQLSetPos(hstmt,3,SQL_ADD,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage("rows affected:%d\n",nlen); strcpy((char *)szData , "insert-new2"); rc = SQLSetPos(hstmt,1,SQL_ADD,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage("rows affected:%d\n",nlen); strcpy((char *)szData , "insert-new3"); rc = SQLSetPos(hstmt,0,SQL_ADD,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage("rows affected:%d\n",nlen); strcpy((char *)szData , "insert-new4"); rc = SQLSetPos(hstmt,10,SQL_ADD,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage("rows affected:%d\n",nlen); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from tmysql_setpos_add"); mystmt(hstmt,rc); myassert(6 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos_add"); return OK; } DECLARE_TEST(tmysql_pos_delete) { SQLHSTMT hstmt1; SQLLEN rows; SQLCHAR buff[10]; ok_con(hdbc, SQLAllocStmt(hdbc, &hstmt1)); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_pos_delete"); ok_sql(hstmt, "CREATE TABLE tmysql_pos_delete (a INT, b VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO tmysql_pos_delete VALUES (1,'venu'),(2,'MySQL')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu_cur", SQL_NTS)); ok_sql(hstmt, "SELECT * FROM tmysql_pos_delete"); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, NULL, NULL)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); expect_sql(hstmt1, " DfffELETE FROM tmysql_pos_delete WHERE CURRENT OF venu_cur", SQL_ERROR); expect_sql(hstmt1, " DELETE FROM tmysql_pos_delete WHERE CURRENT OF venu_cur curs", SQL_ERROR); expect_sql(hstmt1, " DELETE FROM tmysql_pos_delete WHERE ONE CURRENT OF venu_cur", SQL_ERROR); ok_sql(hstmt1, " DELETE FROM tmysql_pos_delete WHERE CURRENT OF venu_cur"); ok_stmt(hstmt1, SQLRowCount(hstmt1, &rows)); is_num(rows, 1); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM tmysql_pos_delete"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 2); is_str(my_fetch_str(hstmt, buff, 2), "MySQL", 5); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_pos_delete"); return OK; } DECLARE_TEST(t_pos_update) { SQLHSTMT hstmt1; SQLCHAR szData[10]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_update"); ok_sql(hstmt, "CREATE TABLE t_pos_update (col1 INT, col2 VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO t_pos_update VALUES (100,'venu'),(200,'MySQL')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu_cur", SQL_NTS)); ok_sql(hstmt, "SELECT * FROM t_pos_update"); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 2, NULL, NULL)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_con(hdbc, SQLAllocStmt(hdbc, &hstmt1)); expect_sql(hstmt1, " UPerrDATE t_pos_update SET col1 = 999, col2 = 'update' " "WHERE CURRENT OF venu_cur", SQL_ERROR); expect_sql(hstmt1, " UPDATE t_pos_update SET col1 = 999, col2 = 'update' " "WHERE CURRENT OF", SQL_ERROR); ok_sql(hstmt1, " UPDATE t_pos_update SET col1 = 999, col2 = 'update' " "WHERE CURRENT OF venu_cur"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLFreeHandle(SQL_HANDLE_STMT, hstmt1)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_pos_update"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 100); is_str(my_fetch_str(hstmt, szData, 2), "venu", 4); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 999); is_str(my_fetch_str(hstmt, szData, 2), "update", 5); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_update"); return OK; } DECLARE_TEST(tmysql_pos_update_ex) { SQLHSTMT hstmt1; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; SQLLEN rows; SQLCHAR cursor[30], sql[255], data[]= "tmysql_pos_update_ex"; ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_updex"); ok_sql(hstmt, "CREATE TABLE t_pos_updex (a INT PRIMARY KEY, b VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO t_pos_updex VALUES (100,'venu'),(200,'MySQL')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "SELECT * FROM t_pos_updex"); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 2, &pcrow, &rgfRowStatus)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLGetCursorName(hstmt, cursor, sizeof(cursor), NULL)); ok_con(hdbc, SQLAllocStmt(hdbc, &hstmt1)); ok_stmt(hstmt1, SQLBindParameter(hstmt1, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, data, sizeof(data), NULL)); sprintf((char *)sql, "UPDATE t_pos_updex SET a = 999, b = ? WHERE CURRENT OF %s", cursor); ok_stmt(hstmt1, SQLExecDirect(hstmt1, sql, SQL_NTS)); ok_stmt(hstmt1, SQLRowCount(hstmt1, &rows)); is_num(rows, 1); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_pos_updex"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 100); is_str(my_fetch_str(hstmt, sql, 2), "venu", 4); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 999); is_str(my_fetch_str(hstmt, sql, 2), "tmysql_pos_update_ex", 20); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_updex"); return OK; } DECLARE_TEST(tmysql_pos_update_ex1) { SQLHSTMT hstmt1; SQLULEN pcrow; SQLLEN rows; SQLUSMALLINT rgfRowStatus; SQLCHAR cursor[30], sql[100], data[]= "tmysql_pos_update_ex1"; ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_updex1"); ok_sql(hstmt, "CREATE TABLE t_pos_updex1 (a INT, b VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO t_pos_updex1 VALUES (100,'venu'),(200,'MySQL')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "SELECT * FROM t_pos_updex1"); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 2, &pcrow, &rgfRowStatus)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLGetCursorName(hstmt, cursor, sizeof(cursor), NULL)); ok_con(hdbc, SQLAllocStmt(hdbc, &hstmt1)); ok_stmt(hstmt1, SQLBindParameter(hstmt1, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, data, sizeof(data), NULL)); sprintf((char *)sql, "UPDATE t_pos_updex1 SET a = 999, b = ? WHERE CURRENT OF %s", cursor); ok_stmt(hstmt1, SQLExecDirect(hstmt1, sql, SQL_NTS)); ok_stmt(hstmt1, SQLRowCount(hstmt1, &rows)); is_num(rows, 1); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_pos_updex1"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 100); is_str(my_fetch_str(hstmt, sql, 2), "venu", 4); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 999); is_str(my_fetch_str(hstmt, sql, 2), "tmysql_pos_update_ex1", 21); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_updex1"); return OK; } DECLARE_TEST(tmysql_pos_update_ex3) { SQLHSTMT hstmt1; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; SQLCHAR cursor[30], sql[255]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_updex3"); ok_sql(hstmt, "CREATE TABLE t_pos_updex3 (a INT NOT NULL PRIMARY KEY," " b VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO t_pos_updex3 VALUES (100,'venu'),(200,'MySQL')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "SELECT a, b FROM t_pos_updex3"); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 2, &pcrow, &rgfRowStatus)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLGetCursorName(hstmt, cursor, sizeof(cursor), NULL)); ok_con(hdbc, SQLAllocStmt(hdbc, &hstmt1)); sprintf((char *)sql, "UPDATE t_pos_updex3 SET a = 999, b = ? WHERE CURRENT OF %s", cursor); expect_stmt(hstmt1, SQLExecDirect(hstmt1, sql, SQL_NTS), SQL_ERROR); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_updex3"); return OK; } DECLARE_TEST(tmysql_pos_update_ex4) { SQLULEN pcrow; SQLLEN nlen= SQL_NTS; SQLCHAR data[]= "venu", szData[20]; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_updex4"); ok_sql(hstmt, "CREATE TABLE t_pos_updex4 (a VARCHAR(20) NOT NULL," "b VARCHAR(20) NOT NULL, c VARCHAR(5), PRIMARY KEY (b))"); ok_sql(hstmt, "INSERT INTO t_pos_updex4 (a,b) VALUES ('Monty','Widenius')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "SELECT * FROM t_pos_updex4"); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 1, &pcrow, &rgfRowStatus)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &nlen)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); is_num(nlen, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT a FROM t_pos_updex4"); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, szData, 1), "venu", 4); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_pos_updex4"); return OK; } DECLARE_TEST(tmysql_pos_dyncursor) { SQLHSTMT hstmt1; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; SQLCHAR buff[100]; SQLLEN rows; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_pos_dyncursor"); ok_sql(hstmt, "CREATE TABLE tmysql_pos_dyncursor (a INT, b VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO tmysql_pos_dyncursor VALUES (1,'foo'),(2,'bar')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu_cur", SQL_NTS)); ok_sql(hstmt, "SELECT * FROM tmysql_pos_dyncursor"); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 2, &pcrow, &rgfRowStatus)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_con(hdbc, SQLAllocStmt(hdbc, &hstmt1)); ok_sql(hstmt1, "UPDATE tmysql_pos_dyncursor SET a = 9, b = 'update' " "WHERE CURRENT OF venu_cur"); ok_stmt(hstmt1, SQLRowCount(hstmt1, &rows)); is_num(rows, 1); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM tmysql_pos_dyncursor"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); is_str(my_fetch_str(hstmt, buff, 2), "foo", 3); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 9); is_str(my_fetch_str(hstmt, buff, 2), "update", 6); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_pos_dyncursor"); return OK; } DECLARE_TEST(tmysql_mtab_setpos_del) { SQLRETURN rc; SQLINTEGER nData= 500; SQLLEN nlen; SQLCHAR szData[255]={0}; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_t1, tmysql_t2"); rc = tmysql_exec(hstmt,"create table tmysql_t1(col1 int, col2 varchar(20))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"create table tmysql_t2(col1 int, col2 varchar(20))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_t1 values(1,'t1_one')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_t1 values(2,'t1_two')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_t1 values(3,'t1_three')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_t2 values(2,'t2_one')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_t2 values(3,'t2_two')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_t2 values(4,'t2_three')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); rc = SQLSetCursorName(hstmt, (SQLCHAR *)"venu",SQL_NTS); mystmt(hstmt,rc); /* FULL JOIN */ ok_sql(hstmt,"select tmysql_t1.*,tmysql_t2.* from tmysql_t1,tmysql_t2"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,100,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,100,&nlen); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_ABSOLUTE,3,&pcrow,&rgfRowStatus); mystmt(hstmt,rc); printMessage(" pcrow:%d\n",pcrow); printMessage(" row1:%d,%s\n",nData,szData); rc = SQLSetPos(hstmt,1,SQL_POSITION,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); /* not yet supported..*/ rc = SQLSetPos(hstmt,2,SQL_DELETE,SQL_LOCK_NO_CHANGE); mystmt_r(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_t1, tmysql_t2"); return OK; } DECLARE_TEST(tmysql_setpos_pkdel) { SQLRETURN rc; SQLINTEGER nData= 500; SQLLEN nlen; SQLCHAR szData[255]={0}; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos1"); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"create table tmysql_setpos1(col1 int primary key, col2 varchar(30))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos1 values(100,'MySQL1')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos1 values(200,'MySQL2')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos1 values(300,'MySQL3')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_setpos1 values(400,'MySQL4')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); rc = SQLSetCursorName(hstmt, (SQLCHAR *)"venu",SQL_NTS); mystmt(hstmt,rc); ok_sql(hstmt,"select * from tmysql_setpos1"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,100,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,100,&nlen); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_ABSOLUTE,4,&pcrow,&rgfRowStatus); mystmt(hstmt,rc); printMessage(" pcrow:%d\n",pcrow); printMessage(" row1:%d,%s\n",nData,szData); rc = SQLSetPos(hstmt,1,SQL_POSITION,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLSetPos(hstmt,1,SQL_DELETE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage(" rows affected:%d\n",nlen); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from tmysql_setpos1"); mystmt(hstmt,rc); my_assert( 3 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos1"); return OK; } DECLARE_TEST(t_alias_setpos_pkdel) { SQLINTEGER nData= 500; SQLLEN nlen; SQLCHAR szData[255]= {0}; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS t_alias_setpos_pkdel"); ok_con(hdbc, SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT)); ok_sql(hstmt, "CREATE TABLE t_alias_setpos_pkdel (col1 INT PRIMARY KEY," " col2 VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO t_alias_setpos_pkdel VALUES (100, 'MySQL1')"); ok_sql(hstmt, "INSERT INTO t_alias_setpos_pkdel VALUES (200, 'MySQL2')"); ok_sql(hstmt, "INSERT INTO t_alias_setpos_pkdel VALUES (300, 'MySQL3')"); ok_sql(hstmt, "INSERT INTO t_alias_setpos_pkdel VALUES (400, 'MySQL4')"); ok_con(hdbc, SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu", SQL_NTS)); ok_sql(hstmt,"SELECT col1 AS id, col2 AS name FROM t_alias_setpos_pkdel"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), &nlen)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 1, &pcrow, &rgfRowStatus)); printMessage("pcrow:%d, rgfRowStatus:%d", pcrow, rgfRowStatus); printMessage(" row1:%d, %s", nData, szData); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); printMessage("rows affected:%d",nlen); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_alias_setpos_pkdel"); my_assert(3 == myresult(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_alias_setpos_pkdel"); return OK; } DECLARE_TEST(t_alias_setpos_del) { SQLINTEGER nData= 500; SQLLEN nlen; SQLCHAR szData[255]= {0}; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS t_alias_setpos_del"); ok_con(hdbc, SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT)); ok_sql(hstmt, "CREATE TABLE t_alias_setpos_del (col1 INT, col2 VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO t_alias_setpos_del VALUES (100, 'MySQL1')"); ok_sql(hstmt, "INSERT INTO t_alias_setpos_del VALUES (200, 'MySQL2')"); ok_sql(hstmt, "INSERT INTO t_alias_setpos_del VALUES (300, 'MySQL3')"); ok_sql(hstmt, "INSERT INTO t_alias_setpos_del VALUES (400, 'MySQL4')"); ok_con(hdbc, SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu", SQL_NTS)); ok_sql(hstmt,"SELECT col1 AS id, col2 AS name FROM t_alias_setpos_del"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), &nlen)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 1, &pcrow, &rgfRowStatus)); printMessage("pcrow:%d, rgfRowStatus:%d", pcrow, rgfRowStatus); printMessage(" row1:%d, %s", nData, szData); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); printMessage("rows affected:%d",nlen); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_alias_setpos_del"); my_assert(3 == myresult(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_alias_setpos_del"); return OK; } DECLARE_TEST(tmysql_setpos_pkdel2) { SQLINTEGER nData= 500; SQLLEN nlen; SQLCHAR szData[255]= {0}; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos_pkdel2"); ok_sql(hstmt, "CREATE TABLE tmysql_setpos_pkdel2 (a INT, b INT," "c VARCHAR(30) PRIMARY KEY)"); ok_sql(hstmt, "INSERT INTO tmysql_setpos_pkdel2 VALUES (100,10,'MySQL1')," "(200,20,'MySQL2'),(300,30,'MySQL3'),(400,40,'MySQL4')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu", SQL_NTS)); ok_sql(hstmt, "SELECT b,c FROM tmysql_setpos_pkdel2"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), &nlen)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_ABSOLUTE, 4, &pcrow, &rgfRowStatus)); is_num(pcrow, 1); is_num(nData, 40); is_str(szData, "MySQL4", 6); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); is_num(nlen, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM tmysql_setpos_pkdel2"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 100); is_num(my_fetch_int(hstmt, 2), 10); is_str(my_fetch_str(hstmt, szData, 3), "MySQL1", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 200); is_num(my_fetch_int(hstmt, 2), 20); is_str(my_fetch_str(hstmt, szData, 3), "MySQL2", 6); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 300); is_num(my_fetch_int(hstmt, 2), 30); is_str(my_fetch_str(hstmt, szData, 3), "MySQL3", 6); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_setpos_pkdel2"); return OK; } DECLARE_TEST(t_setpos_upd_bug1) { SQLRETURN rc; SQLINTEGER id; SQLLEN len,id_len,f_len,l_len,ts_len; SQLCHAR fname[21],lname[21],szTable[256]; SQL_TIMESTAMP_STRUCT ts; SQLSMALLINT pccol; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS t_setpos_upd_bug1"); rc = tmysql_exec(hstmt,"create table t_setpos_upd_bug1(id int(11) NOT NULL auto_increment,\ fname char(20) NOT NULL default '',\ lname char(20) NOT NULL default '',\ last_modi timestamp,\ PRIMARY KEY(id)) ENGINE=MyISAM"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_setpos_upd_bug1(fname,lname) values('joshua','kugler')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_setpos_upd_bug1(fname,lname) values('monty','widenius')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_setpos_upd_bug1(fname,lname) values('mr.','venu')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); rc = tmysql_exec(hstmt,"select * from t_setpos_upd_bug1 order by id asc"); mystmt(hstmt,rc); rc = SQLNumResultCols(hstmt,&pccol); mystmt(hstmt,rc); printMessage(" total columns:%d\n",pccol); rc = SQLBindCol(hstmt,1,SQL_C_SLONG,&id,4,&id_len); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,fname,6,&f_len); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,3,SQL_C_CHAR,lname,20,&l_len); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,4,SQL_C_TIMESTAMP,&ts,21,&ts_len); mystmt(hstmt,rc); rc = SQLColAttribute(hstmt,1,SQL_COLUMN_TABLE_NAME,szTable,sizeof(szTable),NULL,NULL); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_FIRST,0,NULL,&rgfRowStatus); mystmt(hstmt,rc); rc = SQLSetStmtOption(hstmt,SQL_QUERY_TIMEOUT,30); mystmt(hstmt,rc); strcpy((char *)fname , "updated"); strcpy((char *)lname , "updated01234567890"); rc = SQLSetPos(hstmt,1,SQL_UPDATE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&len); mystmt(hstmt,rc); printMessage(" rows affected:%d\n",len); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from t_setpos_upd_bug1"); mystmt(hstmt,rc); myresult(hstmt); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt,"DELETE FROM t_setpos_upd_bug1 WHERE fname = 'update'"); rc = SQLRowCount(hstmt,&len); mystmt(hstmt,rc); printMessage("\n total rows affceted:%d",len); my_assert(len == 1); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"select * from t_setpos_upd_bug1"); mystmt(hstmt,rc); my_assert(2 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_setpos_upd_bug1"); return OK; } DECLARE_TEST(my_setpos_upd_pk_order) { SQLRETURN rc; SQLINTEGER nData= 500; SQLLEN nlen; SQLCHAR szData[255]={0}; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; ok_sql(hstmt, "DROP TABLE IF EXISTS my_setpos_upd_pk_order"); rc = tmysql_exec(hstmt,"create table my_setpos_upd_pk_order(col1 int not null, col2 varchar(30) NOT NULL, primary key(col2,col1))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into my_setpos_upd_pk_order values(100,'MySQL1')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into my_setpos_upd_pk_order values(200,'MySQL2')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); rc = SQLSetCursorName(hstmt, (SQLCHAR *)"venu",SQL_NTS); mystmt(hstmt,rc); ok_sql(hstmt,"select * from my_setpos_upd_pk_order"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,0,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,sizeof(szData),NULL); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_ABSOLUTE,2,&pcrow,&rgfRowStatus); mystmt(hstmt,rc); printMessage(" row1:%d,%s\n",nData,szData); nData = 1000; strcpy((char *)szData , "updated"); rc = SQLSetPos(hstmt,1,SQL_UPDATE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage(" rows affected:%d\n",nlen); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from my_setpos_upd_pk_order"); mystmt(hstmt,rc); myresult(hstmt); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DELETE FROM my_setpos_upd_pk_order WHERE col2 = 'updated'"); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage("\n total rows affceted:%d",nlen); my_assert(nlen == 1); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS my_setpos_upd_pk_order"); return OK; } /** In this test, we prove that we can update a row in a table with a multi-part primary key even though we're only updating two parts of the key. */ DECLARE_TEST(my_setpos_upd_pk_order1) { SQLINTEGER nData; SQLCHAR szData[255]; SQLULEN pcrow; SQLUSMALLINT rgfRowStatus; SQLLEN rows; ok_sql(hstmt, "DROP TABLE IF EXISTS my_setpos_upd_pk_order1"); ok_sql(hstmt, "CREATE TABLE my_setpos_upd_pk_order1 (a INT NOT NULL," "b VARCHAR(30) NOT NULL, c INT NOT NULL, PRIMARY KEY (a,b,c))"); ok_sql(hstmt, "INSERT INTO my_setpos_upd_pk_order1 VALUES (100,'MySQL1',1)," "(200,'MySQL2',2)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu", SQL_NTS)); ok_sql(hstmt, "SELECT * FROM my_setpos_upd_pk_order1"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), NULL)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &pcrow, &rgfRowStatus)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &pcrow, &rgfRowStatus)); nData= 1000; strcpy((char *)szData, "updated"); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &rows)); is_num(rows, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM my_setpos_upd_pk_order1"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 100); is_str(my_fetch_str(hstmt, szData, 2), "MySQL1", 6); is_num(my_fetch_int(hstmt, 3), 1); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1000); is_str(my_fetch_str(hstmt, szData, 2), "updated", 7); is_num(my_fetch_int(hstmt, 3), 2); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS my_setpos_upd_pk_order1"); return OK; } DECLARE_TEST(tmy_cursor1) { SQLCHAR getCurName[20]; SQLSMALLINT getLen; ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"MYSQL", 5)); ok_stmt(hstmt, SQLGetCursorName(hstmt, getCurName, 20, &getLen)); is_str(getCurName, "MYSQL", 5); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"MYSQL", 10)); ok_stmt(hstmt, SQLGetCursorName(hstmt, getCurName, 20, &getLen)); is_str(getCurName, "MYSQL", 5); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"MYSQL", 2)); ok_stmt(hstmt, SQLGetCursorName(hstmt, getCurName, 20, &getLen)); is_str(getCurName, "MY", 2); return OK; } DECLARE_TEST(tmy_cursor2) { SQLCHAR getCursor[50]= {0}; SQLSMALLINT getLen; ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"MYODBC", 6)); expect_stmt(hstmt, SQLGetCursorName(hstmt, getCursor, 0, &getLen), SQL_SUCCESS_WITH_INFO); is_str(getCursor, "", 1); is_num(getLen, 6); expect_stmt(hstmt, SQLGetCursorName(hstmt, getCursor, -1, &getLen), SQL_ERROR); expect_stmt(hstmt, SQLGetCursorName(hstmt, getCursor, 4, &getLen), SQL_SUCCESS_WITH_INFO); is_str(getCursor, "MYO", 4); is_num(getLen, 6); expect_stmt(hstmt, SQLGetCursorName(hstmt, getCursor, 6, &getLen), SQL_SUCCESS_WITH_INFO); is_str(getCursor, "MYODB", 6); is_num(getLen, 6); ok_stmt(hstmt, SQLGetCursorName(hstmt, getCursor, 7, &getLen)); is_str(getCursor, "MYODBC", 7); is_num(getLen, 6); return OK; } DECLARE_TEST(tmy_cursor3) { #if IODBC_BUG_FIXED /* iODBC has a bug that forces the ODBCv2 behavior of throwing an error when SQLSetCursorName() has not bee called and there is no open cursor. */ SQLCHAR getCursor[50]; SQLSMALLINT getLen= -1; SQLHSTMT hstmt1; ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"MYSQLODBC", 9)); /* New statement should get its own (generated) cursor name. */ ok_con(hdbc, SQLAllocStmt(hdbc, &hstmt1)); ok_stmt(hstmt1, SQLGetCursorName(hstmt1, getCursor, 20, &getLen)); is_str(getCursor, "SQL_CUR", 7); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); #endif return OK; } DECLARE_TEST(tmysql_pcbvalue) { SQLCHAR szdata[20], sztdata[100]; SQLINTEGER nodata; SQLLEN nlen, slen, tlen; SQLUSMALLINT rgfRowStatus[20]; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_pcbvalue"); ok_sql(hstmt, "CREATE TABLE tmysql_pcbvalue (col1 INT PRIMARY KEY," " col2 VARCHAR(1), col3 TEXT)"); ok_sql(hstmt, "INSERT INTO tmysql_pcbvalue VALUES (100,'venu','mysql')"); ok_sql(hstmt, "INSERT INTO tmysql_pcbvalue VALUES (200,'monty','mysql')"); ok_con(hdbc, SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt,"SELECT * FROM tmysql_pcbvalue"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nodata, 0, &nlen)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szdata, sizeof(szdata), &slen)); ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_CHAR, sztdata, sizeof(sztdata), &tlen)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_FIRST, 1, NULL, rgfRowStatus)); printMessage("row1: %d(%d), %s(%d),%s(%d)\n", nodata, nlen, szdata, slen, sztdata, tlen); strcpy((char *)szdata, "updated-one"); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, NULL, rgfRowStatus)); printMessage("row2: %d(%d), %s(%d),%s(%d)\n", nodata, nlen, szdata, slen, sztdata, tlen); expect_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, NULL, rgfRowStatus), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_con(hdbc, SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT)); ok_sql(hstmt, "SELECT * FROM tmysql_pcbvalue"); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 2, SQL_C_CHAR, szdata, sizeof(szdata), &slen)); printMessage("updated data:%s(%d)\n",szdata,slen); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_pcbvalue"); ok_con(hdbc, SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT)); return OK; } /** Bug #28255: Cursor operations on result sets containing only part of a key are incorrect */ DECLARE_TEST(t_bug28255) { ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug28255"); ok_sql(hstmt, "CREATE TABLE t_bug28255 (a INT, b INT, PRIMARY KEY (a,b))"); ok_sql(hstmt, "INSERT INTO t_bug28255 VALUES (1,3),(1,4),(1,5)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"bug", SQL_NTS)); ok_sql(hstmt, "SELECT a FROM t_bug28255 WHERE b > 3"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); expect_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_DELETE, SQL_LOCK_NO_CHANGE), SQL_ERROR); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_bug28255"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); is_num(my_fetch_int(hstmt, 2), 3); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); is_num(my_fetch_int(hstmt, 2), 4); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); is_num(my_fetch_int(hstmt, 2), 5); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug28255"); return OK; } /** Bug #10563: Update using multicolumn primary key with duplicate indexes fails */ DECLARE_TEST(bug10563) { SQLLEN nlen; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug10563"); ok_sql(hstmt, "CREATE TABLE t_bug10563 (a INT, b INT, PRIMARY KEY (a,b), UNIQUE (b))"); ok_sql(hstmt, "INSERT INTO t_bug10563 VALUES (1,3),(1,4)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"bug", SQL_NTS)); ok_sql(hstmt, "SELECT b FROM t_bug10563 WHERE b > 3"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 4); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); is_num(nlen, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_bug10563"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); is_num(my_fetch_int(hstmt, 2), 3); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug10563"); return OK; } /* * Bug 6741 - SQL_ATTR_ROW_BIND_OFFSET_PTR is not supported * It was supported for use in some batch operations, but not * standard cursor operations. */ #define BUG6741_VALS 5 DECLARE_TEST(bug6741) { int i; SQLLEN offset; struct { SQLINTEGER xval; SQLLEN ylen; } results[BUG6741_VALS]; ok_sql(hstmt, "drop table if exists t_bug6741"); ok_sql(hstmt, "create table t_bug6741 (x int, y int)"); ok_sql(hstmt, "insert into t_bug6741 values (0,0),(1,NULL),(2,2),(3,NULL),(4,4)"); ok_sql(hstmt, "select x,y from t_bug6741 order by x"); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, &offset, SQL_IS_POINTER)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &results[0].xval, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_LONG, NULL, 0, &results[0].ylen)); /* fetch all the data */ for(i = 0; i < BUG6741_VALS; ++i) { offset = i * sizeof(results[0]); ok_stmt(hstmt, SQLFetch(hstmt)); } expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); /* verify it */ for(i = 0; i < BUG6741_VALS; ++i) { printf("xval[%d] = %d\n", i, results[i].xval); printf("ylen[%d] = %ld\n", i, results[i].ylen); is_num(results[i].xval, i); if(i % 2) { is_num(results[i].ylen, SQL_NULL_DATA); } else { is_num(results[i].ylen, sizeof(SQLINTEGER)); } } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_bug6741"); return OK; } /* Test that the ARD (bound) type is used for the update and not the IRD (server-given) type. */ DECLARE_TEST(t_update_type) { SQLUSMALLINT *val= malloc(sizeof(SQLUSMALLINT)); ok_sql(hstmt, "drop table if exists t_update_no_strlen"); ok_sql(hstmt, "create table t_update_no_strlen (x int not null)"); ok_sql(hstmt, "insert into t_update_no_strlen values (0xaaaa)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "select * from t_update_no_strlen"); /* server will use SQL_C_LONG, but we use short */ ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_USHORT, val, 0, NULL)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(*val, 0xaaaa); *val= 0xcccc; ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* verify the right value was updated */ *val= 0; ok_sql(hstmt, "select * from t_update_no_strlen"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(*val, 0xcccc); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_update_no_strlen"); return OK; } /* Test bind offset ptr and bind type for cursor update operations. */ DECLARE_TEST(t_update_offsets) { SQLINTEGER rowcnt= 3; SQLINTEGER row_offset1= 5; /* TODO we should prob allow changing SQL_ATTR_ROW_BIND_OFFSET_PTR between SQLFetch() and SQLSetPos(). Setting a different value here will fail. (must be lower than row_offset1 anyways) */ SQLINTEGER row_offset2= 5; struct { SQLINTEGER id; SQLCHAR name[24]; SQLLEN namelen; } rows[8]; size_t row_size= sizeof(rows[0]); SQLLEN bind_offset= -100000; SQLINTEGER i; SQLCHAR buf[50]; ok_sql(hstmt, "drop table if exists t_update_offsets"); ok_sql(hstmt, "create table t_update_offsets (id int not null, " "name varchar(50), primary key (id))"); ok_sql(hstmt, "insert into t_update_offsets values " "(0, 'name0'),(1,'name1'),(2,'name2')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)rowcnt, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE, (SQLPOINTER)row_size, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, &bind_offset, 0)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &rows[0].id, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, &rows[0].name, 24, &rows[0].namelen)); /* get the first block and verify it */ ok_sql(hstmt, "select id,name from t_update_offsets order by id"); bind_offset= row_size * row_offset1; ok_stmt(hstmt, SQLFetch(hstmt)); for (i= 0; i < rowcnt; ++i) { sprintf((char *)buf, "name%d", i); is_num(rows[row_offset1+i].id, i); is_str(rows[row_offset1+i].name, buf, strlen((char *)buf) + 1); is_num(rows[row_offset1+i].namelen, strlen((char *)buf)); /* change the values here */ rows[row_offset2+i].id= i * 10; sprintf((char *)rows[row_offset2+i].name, "name_%d_%d", i, i * 10); rows[row_offset2+i].namelen= strlen((char *)rows[row_offset2+i].name); } /* update all rows */ bind_offset= row_size * row_offset2; ok_stmt(hstmt, SQLSetPos(hstmt, 0, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* verify updates */ memset(rows, 0, sizeof(rows)); is_num(rows[0].id, 0); ok_sql(hstmt, "select id,name from t_update_offsets order by id"); bind_offset= row_size; ok_stmt(hstmt, SQLFetch(hstmt)); for (i= 0; i < rowcnt; ++i) { sprintf((char *)buf, "name_%d_%d", i, i * 10); is_num(rows[i+1].id, i * 10); is_str(rows[i+1].name, buf, strlen((char *)buf) + 1); is_num(rows[i+1].namelen, strlen((char *)buf)); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_update_offsets"); return OK; } /** Bug #6157: BUG in the alias use with ADO's Object */ DECLARE_TEST(t_bug6157) { SQLINTEGER data; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug6157"); ok_sql(hstmt, "CREATE TABLE t_bug6157(a INT)"); ok_sql(hstmt, "INSERT INTO t_bug6157 VALUES (1)"); ok_sql(hstmt, "SELECT a AS b FROM t_bug6157"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &data, 0, NULL)); ok_stmt(hstmt, SQLFetch(hstmt)); data= 6157; ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); data= 9999; ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_ADD, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT a FROM t_bug6157 ORDER BY a"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &data, 0, NULL)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(data, 6157); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(data, 9999); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug6157"); return OK; } /** Bug #32420: Don't cache results and SQLExtendedFetch ignore SQL_ROWSET_SIZE option */ DECLARE_TEST(t_bug32420) { HDBC hdbc1; HSTMT hstmt1; SQLINTEGER nData[4]; SQLCHAR szData[4][16]; SQLUSMALLINT rgfRowStatus[4]; SQLCHAR conn[256], conn_out[256]; SQLSMALLINT conn_out_len; SQLULEN row_count; /* Don't cache result option in the connection string */ sprintf((char *)conn, "DRIVER=%s;USER=%s;PASSWORD=%s;" "DATABASE=%s;SERVER=%s;OPTION=1048576", mydriver, myuid, mypwd, mydb, myserver); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, sizeof(conn), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_sql(hstmt1, "drop table if exists bug32420"); ok_sql(hstmt1, "CREATE TABLE bug32420 ("\ "tt_int INT PRIMARY KEY auto_increment,"\ "tt_varchar VARCHAR(128) NOT NULL)"); ok_sql(hstmt1, "INSERT INTO bug32420 VALUES "\ "(100, 'string 1'),"\ "(200, 'string 2'),"\ "(300, 'string 3'),"\ "(400, 'string 4'),"\ "(500, 'string 5'),"\ "(600, 'string 6'),"\ "(700, 'string 7'),"\ "(800, 'string 8'),"\ "(900, 'string 9'),"\ "(910, 'string A'),"\ "(920, 'string B')"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLSetStmtOption(hstmt1, SQL_ROWSET_SIZE, 4)); ok_sql(hstmt1, "select * from bug32420"); ok_stmt(hstmt1, SQLBindCol(hstmt1, 1, SQL_C_LONG, nData, 0, NULL)); ok_stmt(hstmt1, SQLBindCol(hstmt1, 2, SQL_C_CHAR, szData, sizeof(szData[0]), NULL)); ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_NEXT, 0, &row_count, rgfRowStatus)); is_num(row_count, 4); is_num(nData[0], 100); is_str(szData[0], "string 1", 8); is_num(nData[1], 200); is_str(szData[1], "string 2", 8); is_num(nData[2], 300); is_str(szData[2], "string 3", 8); is_num(nData[3], 400); is_str(szData[3], "string 4", 8); ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_NEXT, 0, &row_count, rgfRowStatus)); is_num(row_count, 4); is_num(nData[0], 500); is_str(szData[0], "string 5", 8); is_num(nData[1], 600); is_str(szData[1], "string 6", 8); is_num(nData[2], 700); is_str(szData[2], "string 7", 8); is_num(nData[3], 800); is_str(szData[3], "string 8", 8); /* Now checking the last records when the result is shorter than ROWSET_SIZE */ ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_NEXT, 0, &row_count, rgfRowStatus)); is_num(row_count, 3); is_num(nData[0], 900); is_str(szData[0], "string 9", 8); is_num(nData[1], 910); is_str(szData[1], "string A", 8); is_num(nData[2], 920); is_str(szData[2], "string B", 8); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "drop table if exists bug32420"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); /* Result cache is enabled. Need to check that cached results are not broken */ sprintf((char *)conn,"DRIVER=%s;USER=%s;PASSWORD=%s;" "DATABASE=%s;SERVER=%s", mydriver, myuid, mypwd, mydb, myserver); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, sizeof(conn), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) SQL_CURSOR_DYNAMIC, 0)); ok_sql(hstmt1, "drop table if exists bug32420"); ok_sql(hstmt1, "CREATE TABLE bug32420 ("\ "tt_int INT PRIMARY KEY auto_increment,"\ "tt_varchar VARCHAR(128) NOT NULL)"); ok_sql(hstmt1, "INSERT INTO bug32420 VALUES "\ "(100, 'string 1'),"\ "(200, 'string 2'),"\ "(300, 'string 3'),"\ "(400, 'string 4'),"\ "(500, 'string 5'),"\ "(600, 'string 6'),"\ "(700, 'string 7'),"\ "(800, 'string 8'),"\ "(900, 'string 9'),"\ "(910, 'string A'),"\ "(920, 'string B')"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLSetStmtOption(hstmt1, SQL_ROWSET_SIZE, 4)); ok_sql(hstmt1, "select * from bug32420"); ok_stmt(hstmt1, SQLBindCol(hstmt1, 1, SQL_C_LONG, nData, 0, NULL)); ok_stmt(hstmt1, SQLBindCol(hstmt1, 2, SQL_C_CHAR, szData, sizeof(szData[0]), NULL)); ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_NEXT, 0, &row_count, rgfRowStatus)); is_num(row_count, 4); is_num(nData[0], 100); is_str(szData[0], "string 1", 8); is_num(nData[1], 200); is_str(szData[1], "string 2", 8); is_num(nData[2], 300); is_str(szData[2], "string 3", 8); is_num(nData[3], 400); is_str(szData[3], "string 4", 8); ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_NEXT, 0, &row_count, rgfRowStatus)); is_num(row_count, 4); is_num(nData[0], 500); is_str(szData[0], "string 5", 8); is_num(nData[1], 600); is_str(szData[1], "string 6", 8); is_num(nData[2], 700); is_str(szData[2], "string 7", 8); is_num(nData[3], 800); is_str(szData[3], "string 8", 8); /* Now checking the last records when the result is shorter than ROWSET_SIZE */ ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_NEXT, 0, &row_count, rgfRowStatus)); is_num(row_count, 3); is_num(nData[0], 900); is_str(szData[0], "string 9", 8); is_num(nData[1], 910); is_str(szData[1], "string A", 8); is_num(nData[2], 920); is_str(szData[2], "string B", 8); /* Dynamic cursor allows fetching first records */ ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_FIRST, 0, &row_count, rgfRowStatus)); is_num(row_count, 4); is_num(nData[0], 100); is_str(szData[0], "string 1", 8); is_num(nData[1], 200); is_str(szData[1], "string 2", 8); is_num(nData[2], 300); is_str(szData[2], "string 3", 8); is_num(nData[3], 400); is_str(szData[3], "string 4", 8); /* Fetching last records */ ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_LAST, 0, &row_count, rgfRowStatus)); is_num(row_count, 4); is_num(nData[0], 800); is_str(szData[0], "string 8", 8); is_num(nData[1], 900); is_str(szData[1], "string 9", 8); is_num(nData[2], 910); is_str(szData[2], "string A", 8); is_num(nData[3], 920); is_str(szData[3], "string B", 8); /* Fetching with absolute offset */ ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_ABSOLUTE, 3, &row_count, rgfRowStatus)); is_num(row_count, 4); is_num(nData[0], 300); is_str(szData[0], "string 3", 8); is_num(nData[1], 400); is_str(szData[1], "string 4", 8); is_num(nData[2], 500); is_str(szData[2], "string 5", 8); is_num(nData[3], 600); is_str(szData[3], "string 6", 8); /* Fetching with relative offset */ ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_RELATIVE, 2, &row_count, rgfRowStatus)); is_num(row_count, 3); is_num(nData[0], 900); is_str(szData[0], "string 9", 8); is_num(nData[1], 910); is_str(szData[1], "string A", 8); is_num(nData[2], 920); is_str(szData[2], "string B", 8); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "drop table if exists bug32420"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); return OK; } /* Shared between t_cursor_pos_static and t_cursor_pos_dynamic. Tests all the cursor position handling. Cursor type is setup by caller. */ int t_cursor_pos(SQLHANDLE hstmt) { SQLINTEGER i; SQLINTEGER x[3]; SQLINTEGER y[3]; SQLINTEGER remaining_rows[]= {1, 5, 6, 7, 8}; SQLINTEGER remaining_row_count= 5; ok_sql(hstmt, "drop table if exists t_cursor_pos"); ok_sql(hstmt, "create table t_cursor_pos (x int not null, " "y int, primary key (x))"); ok_sql(hstmt, "insert into t_cursor_pos values (0,0),(1,1)," "(2,2),(3,3),(4,4), (5,5),(6,6),(7,7), (8,8)"); ok_sql(hstmt, "select x,y from t_cursor_pos order by 1"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, x, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_LONG, y, 0, NULL)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); /* this covers bug#29765 and bug#33388 */ is_num(x[0], 0); ok_stmt(hstmt, SQLSetPos(hstmt, 0, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(x[0], 1); y[0]++; ok_stmt(hstmt, SQLSetPos(hstmt, 0, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(x[0], 2); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_PRIOR, 0)); is_num(x[0], 1); /* and rowset tests */ ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)3, 0)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); for (i= 0; i < 3; ++i) is_num(x[i], 2 + i); /* delete 2,3,4 */ ok_stmt(hstmt, SQLSetPos(hstmt, 0, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); for (i= 0; i < 3; ++i) { is_num(x[i], 5 + i); y[i]++; } /* update 5,6,7 */ ok_stmt(hstmt, SQLSetPos(hstmt, 0, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); /* set rowset_size back to 1 */ ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(x[0], 8); y[0]++; ok_stmt(hstmt, SQLSetPos(hstmt, 0, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); /* check all rows were updated correctly */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "select x,y from t_cursor_pos order by 1"); for (i= 0; i < remaining_row_count; ++i) { ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(x[0], remaining_rows[i]); is_num(y[0], x[0] + 1); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_cursor_pos"); return OK; } /* Wrapper for t_cursor_pos using static cursor. */ DECLARE_TEST(t_cursor_pos_static) { ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); return t_cursor_pos(hstmt); } /* Wrapper for t_cursor_pos using dynamic cursor. */ DECLARE_TEST(t_cursor_pos_dynamic) { SQLHANDLE henv1, hdbc1, hstmt1; SET_DSN_OPTION(32); alloc_basic_handles(&henv1, &hdbc1, &hstmt1); ok_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0)); is_num(t_cursor_pos(hstmt1), OK); (void) free_basic_handles(&henv1, &hdbc1, &hstmt1); SET_DSN_OPTION(0); return OK; } /* Bug#11846 - DIAG [S1T00] Driver Failed to set the internal dynamic result Dynamic cursors on statements with parameters wasn't supported. */ DECLARE_TEST(t_bug11846) { SQLINTEGER val_in= 4, val_out= 99; SQLHANDLE henv1, hdbc1, hstmt1; SET_DSN_OPTION(32); alloc_basic_handles(&henv1, &hdbc1, &hstmt1); ok_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC,0)); ok_stmt(hstmt1, SQLBindParameter(hstmt1, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &val_in, 0, NULL)); ok_sql(hstmt1, "select ?"); ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_stmt(hstmt1, SQLGetData(hstmt1, 1, SQL_C_LONG, &val_out, 0, NULL)); is_num(val_out, val_in); free_basic_handles(&henv1, &hdbc1, &hstmt1); SET_DSN_OPTION(0); return OK; } /* Basic test of data-at-exec with SQLSetPos() insert. */ typedef struct { SQLINTEGER x, z; SQLCHAR y[11]; SQLLEN ylen; } t_dae_row; DECLARE_TEST(t_dae_setpos_insert) { SQLPOINTER holder= (SQLPOINTER) 0xcfcdcecc; SQLPOINTER paramptr; SQLLEN offset= 0; t_dae_row data[2]; memset(data, 0, 2 * sizeof(t_dae_row)); data[1].x= 20; data[1].z= 40; sprintf(data[1].y, "1234567890"); data[1].ylen= SQL_LEN_DATA_AT_EXEC(10); ok_sql(hstmt, "drop table if exists t_dae"); ok_sql(hstmt, "create table t_dae (x int not null, y varchar(5000), z int, " "primary key (x) )"); ok_sql(hstmt, "select x, y, z from t_dae"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &data[0].x, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, holder, 10, &data[0].ylen)); ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_LONG, &data[0].z, 0, NULL)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, &offset, SQL_IS_POINTER)); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0), SQL_NO_DATA); offset= sizeof(t_dae_row); expect_stmt(hstmt, SQLSetPos(hstmt, 0, SQL_ADD, SQL_LOCK_NO_CHANGE), SQL_NEED_DATA); expect_stmt(hstmt, SQLParamData(hstmt, ¶mptr), SQL_NEED_DATA); is_num(paramptr, ((SQLCHAR *)holder + offset)); ok_stmt(hstmt, SQLPutData(hstmt, data[1].y, 10)); ok_stmt(hstmt, SQLParamData(hstmt, ¶mptr)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); offset= 0; ok_sql(hstmt, "select x, y, z from t_dae"); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, data[0].y, 11, &data[0].ylen)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(20, data[0].x); is_num(40, data[0].z); is_str(data[0].y, data[1].y, 11); is_num(10, data[0].ylen); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_dae"); return OK; } /* Basic test of data-at-exec with SQLSetPos() update. */ DECLARE_TEST(t_dae_setpos_update) { SQLINTEGER x= 20; SQLINTEGER z= 40; SQLCHAR *yval= (SQLCHAR *) "1234567890"; SQLCHAR yout[11]; SQLLEN ylen= SQL_LEN_DATA_AT_EXEC(10); SQLPOINTER holder= (SQLPOINTER) 0xcfcdcecc; SQLPOINTER paramptr; /* setup */ ok_sql(hstmt, "drop table if exists t_dae"); ok_sql(hstmt, "create table t_dae (x int not null, y varchar(5000), z int, " "primary key (x) )"); ok_sql(hstmt, "insert into t_dae values (10, '9876', 30)"); /* create cursor and get first row */ ok_sql(hstmt, "select x, y, z from t_dae"); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); /* bind values for positioned update */ ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &x, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_BINARY, holder, 10, &ylen)); ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_LONG, &z, 0, NULL)); /* perform update and provide data */ expect_stmt(hstmt, SQLSetPos(hstmt, 0, SQL_UPDATE, SQL_LOCK_NO_CHANGE), SQL_NEED_DATA); expect_stmt(hstmt, SQLParamData(hstmt, ¶mptr), SQL_NEED_DATA); is_num(paramptr, holder); ok_stmt(hstmt, SQLPutData(hstmt, yval, 10)); ok_stmt(hstmt, SQLParamData(hstmt, ¶mptr)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); x= 0; z= 0; ylen= 0; ok_sql(hstmt, "select x, y, z from t_dae"); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, yout, 11, &ylen)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(20, x); is_num(40, z); is_str(yval, yout, 11); is_num(10, ylen); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_dae"); return OK; } /* Bug #39951 - Positioned update with SQL_C_NUMERIC loses prec/scale values */ DECLARE_TEST(t_bug39961) { SQL_NUMERIC_STRUCT num; SQLHANDLE ard; SQLCHAR buf[10]; SQLINTEGER id; ok_sql(hstmt, "drop table if exists t_bug39961"); ok_sql(hstmt, "create table t_bug39961(id int not null, m1 decimal(19, 4), " "primary key (id))"); ok_sql(hstmt, "insert into t_bug39961 values (1, 987)"); ok_sql(hstmt, "select id, m1 from t_bug39961"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &id, 0, NULL)); ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &ard, 0, NULL)); ok_stmt(hstmt, SQLSetDescField(ard, 2, SQL_DESC_CONCISE_TYPE, (SQLPOINTER) SQL_C_NUMERIC, SQL_IS_INTEGER)); ok_stmt(hstmt, SQLSetDescField(ard, 2, SQL_DESC_PRECISION, (SQLPOINTER) 19, SQL_IS_INTEGER)); ok_stmt(hstmt, SQLSetDescField(ard, 2, SQL_DESC_SCALE, (SQLPOINTER) 2, SQL_IS_INTEGER)); ok_stmt(hstmt, SQLSetDescField(ard, 2, SQL_DESC_DATA_PTR, &num, SQL_IS_POINTER)); ok_stmt(hstmt, SQLFetch(hstmt)); /* set value to .1 */ num.val[0]= 10; num.val[1]= 0; num.val[2]= 0; ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); /* add a new row */ id++; ok_stmt(hstmt, SQLBulkOperations(hstmt, SQL_ADD)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* fetch both rows, they should have the same decimal value */ ok_sql(hstmt, "select m1 from t_bug39961"); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buf, 1), "0.1000", 4); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buf, 1), "0.1000", 4); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_bug39961"); return OK; } /* Bug #41946: Inserting a row using SQLSetPos does not correspond to DB name in SELECT */ DECLARE_TEST(t_bug41946) { SQLINTEGER nData= 500; SQLCHAR szData[255]={0}; ok_sql(hstmt, "DROP DATABASE IF EXISTS other_test_db"); ok_sql(hstmt, "CREATE DATABASE other_test_db"); ok_sql(hstmt, "CREATE TABLE other_test_db.t_41946(Id int NOT NULL,\ Name varchar(32),\ PRIMARY KEY (Id))"); ok_sql(hstmt, "select * from other_test_db.t_41946"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 5, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, 11, NULL)); expect_stmt(hstmt, SQLFetchScroll(hstmt,SQL_FETCH_NEXT,1), SQL_NO_DATA); nData= 33; strcpy((char *)szData , "insert-new"); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_ADD, SQL_LOCK_NO_CHANGE)); nData= 0; strcpy((char *)szData , "something else"); /* We have to close the cursor before issuing next sql query */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "select * from other_test_db.t_41946"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(nData, 33); is_str(szData, "insert-new", 11); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP DATABASE IF EXISTS other_test_db"); return OK; } BEGIN_TESTS ADD_TEST(my_positioned_cursor) ADD_TEST(my_setpos_cursor) ADD_TEST(t_bug5853) ADD_TEST(t_setpos_del_all) ADD_TEST(t_setpos_upd_decimal) ADD_TEST(t_setpos_position) ADD_TEST(t_pos_column_ignore) ADD_TEST(t_pos_datetime_delete) ADD_TEST(t_pos_datetime_delete1) ADD_TEST(t_getcursor) ADD_TEST(t_getcursor1) ADD_TEST(t_acc_crash) ADD_TEST(tmysql_setpos_del) ADD_TEST(tmysql_setpos_del1) ADD_TEST(tmysql_setpos_upd) ADD_TEST(tmysql_setpos_add) ADD_TEST(tmysql_pos_delete) ADD_TEST(t_pos_update) ADD_TEST(tmysql_pos_update_ex) ADD_TEST(tmysql_pos_update_ex1) ADD_TEST(tmysql_pos_update_ex3) ADD_TEST(tmysql_pos_update_ex4) ADD_TEST(tmysql_pos_dyncursor) ADD_TEST(tmysql_mtab_setpos_del) ADD_TEST(tmysql_setpos_pkdel) ADD_TEST(tmysql_setpos_pkdel2) ADD_TEST(t_alias_setpos_pkdel) ADD_TEST(t_alias_setpos_del) ADD_TEST(t_setpos_upd_bug1) ADD_TEST(my_setpos_upd_pk_order) ADD_TEST(my_setpos_upd_pk_order1) ADD_TEST(tmy_cursor1) ADD_TEST(tmy_cursor2) ADD_TEST(tmy_cursor3) ADD_TEST(tmysql_pcbvalue) ADD_TEST(t_bug28255) ADD_TEST(bug10563) ADD_TEST(bug6741) ADD_TEST(t_update_type) ADD_TEST(t_update_offsets) ADD_TEST(t_bug6157) ADD_TEST(t_cursor_pos_static) ADD_TEST(t_cursor_pos_dynamic) ADD_TEST(t_bug11846) ADD_TEST(t_dae_setpos_insert) ADD_TEST(t_dae_setpos_update) ADD_TEST(t_bug39961) ADD_TEST(t_bug41946) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/CMakeLists.txt100644 15766 12 4304 11707541005 20706 0ustar00cteamstaff# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/test) # put the test exe's in test/ SET(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/test") IF(DISGUISE_TOFIX_TESTS) ADD_DEFINITIONS(-DDISGUISE_TOFIX_TESTS) ENDIF(DISGUISE_TOFIX_TESTS) ENABLE_TESTING() IF(NOT WIN32) SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${ODBC_LINK_FLAGS}") ENDIF(NOT WIN32) FOREACH(T my_basics my_blob my_bulk my_catalog my_curext my_cursor my_datetime my_desc my_dyn_cursor my_error my_info my_keys my_param my_prepare my_relative my_result my_scroll my_tran my_types my_unicode my_unixodbc my_use_result my_bug13766) ADD_EXECUTABLE(${T} ${T}.c) INSTALL(TARGETS ${T} DESTINATION test COMPONENT tests) IF(WIN32) TARGET_LINK_LIBRARIES(${T} ${ODBCLIB} ${ODBCINSTLIB}) ENDIF(WIN32) ADD_TEST(${T} ${T}) ENDFOREACH(T) TARGET_LINK_LIBRARIES(my_basics ${CMAKE_THREAD_LIBS_INIT}) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/CTestTestfile.cmake ${CMAKE_CURRENT_BINARY_DIR}/odbc.ini ${CMAKE_CURRENT_BINARY_DIR}/odbcinst.ini DESTINATION test COMPONENT tests) mysql-connector-odbc-5.1.10-src/test/my_basics.c100644 15766 12 77546 11707541005 20325 0ustar00cteamstaff/* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" DECLARE_TEST(my_basics) { SQLLEN nRowCount; ok_sql(hstmt, "DROP TABLE IF EXISTS t_basic, t_basic_2"); /* create the table 'myodbc3_demo_result' */ ok_sql(hstmt, "CREATE TABLE t_basic (id INT PRIMARY KEY, name VARCHAR(20))"); /* insert 3 rows of data */ ok_sql(hstmt, "INSERT INTO t_basic VALUES (1,'foo'),(2,'bar'),(3,'baz')"); /* update second row */ ok_sql(hstmt, "UPDATE t_basic SET name = 'bop' WHERE id = 2"); /* get the rows affected by update statement */ ok_stmt(hstmt, SQLRowCount(hstmt, &nRowCount)); is_num(nRowCount, 1); /* delete third row */ ok_sql(hstmt, "DELETE FROM t_basic WHERE id = 3"); /* get the rows affected by delete statement */ ok_stmt(hstmt, SQLRowCount(hstmt, &nRowCount)); is_num(nRowCount, 1); /* alter the table 't_basic' to 't_basic_2' */ ok_sql(hstmt,"ALTER TABLE t_basic RENAME t_basic_2"); /* drop the table with the original table name, and it should return error saying 'table not found' */ expect_sql(hstmt, "DROP TABLE t_basic", SQL_ERROR); /* now drop the table, which is altered..*/ ok_sql(hstmt, "DROP TABLE t_basic_2"); /* free the statement cursor */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; } DECLARE_TEST(t_max_select) { SQLINTEGER num; SQLCHAR szData[20]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_max_select"); ok_sql(hstmt, "CREATE TABLE t_max_select (a INT, b VARCHAR(30))"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"INSERT INTO t_max_select VALUES (?,?)", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &num, 0, NULL)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, szData, sizeof(szData), NULL)); for (num= 1; num <= 1000; num++) { sprintf((char *)szData, "MySQL%d", (int)num); ok_stmt(hstmt, SQLExecute(hstmt)); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_max_select"); is_num(myrowcount(hstmt), 1000); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_max_select"); return OK; } /* Simple function to do basic ops with MySQL */ DECLARE_TEST(t_basic) { SQLINTEGER nRowCount= 0, nInData= 1, nOutData; SQLCHAR szOutData[31]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_myodbc"); ok_sql(hstmt, "CREATE TABLE t_myodbc (a INT, b VARCHAR(30))"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* DIRECT INSERT */ ok_sql(hstmt, "INSERT INTO t_myodbc VALUES (10, 'direct')"); /* PREPARE INSERT */ ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "INSERT INTO t_myodbc VALUES (?, 'param')", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &nInData, 0, NULL)); for (nInData= 20; nInData < 100; nInData= nInData+10) { ok_stmt(hstmt, SQLExecute(hstmt)); } /* FREE THE PARAM BUFFERS */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* FETCH RESULT SET */ ok_sql(hstmt, "SELECT * FROM t_myodbc"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nOutData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szOutData, sizeof(szOutData), NULL)); nInData= 10; while (SQLFetch(hstmt) == SQL_SUCCESS) { is_num(nOutData, nInData); is_str(szOutData, nRowCount++ ? "param" : "direct", 5); nInData += 10; } is_num(nRowCount, (nInData - 10) / 10); /* FREE THE OUTPUT BUFFERS */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_myodbc"); return OK; } DECLARE_TEST(t_nativesql) { SQLCHAR out[128], in[]= "SELECT * FROM venu"; SQLINTEGER len; ok_con(hdbc, SQLNativeSql(hdbc, in, SQL_NTS, out, sizeof(out), &len)); is_num(len, (SQLINTEGER) sizeof(in) - 1); /* The second call is to make sure the first didn't screw up the stack. (Bug #28758) */ ok_con(hdbc, SQLNativeSql(hdbc, in, SQL_NTS, out, sizeof(out), &len)); is_num(len, (SQLINTEGER) sizeof(in) - 1); return OK; } /** This just tests that we can connect, disconnect and connect a few times without anything blowing up. */ DECLARE_TEST(t_reconnect) { SQLHDBC hdbc1; long i; for (i= 0; i < 10; i++) { ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnect(hdbc1, mydsn, SQL_NTS, myuid, SQL_NTS, mypwd, SQL_NTS)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); } return OK; } /** Bug #19823: SQLGetConnectAttr with SQL_ATTR_CONNECTION_TIMEOUT works incorrectly */ DECLARE_TEST(t_bug19823) { SQLHDBC hdbc1; SQLINTEGER timeout; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); /* This first connect/disconnect is just to work around a bug in iODBC's implementation of SQLSetConnectAttr. It is fixed in 3.52.6, but Debian/Ubuntu still ships 3.52.5 as of 2007-12-06. */ ok_con(hdbc1, SQLConnect(hdbc1, mydsn, SQL_NTS, myuid, SQL_NTS, mypwd, SQL_NTS)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLSetConnectAttr(hdbc1, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)17, 0)); ok_con(hdbc1, SQLSetConnectAttr(hdbc1, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER)12, 0)); ok_con(hdbc1, SQLConnect(hdbc1, mydsn, SQL_NTS, myuid, SQL_NTS, mypwd, SQL_NTS)); ok_con(hdbc1, SQLGetConnectAttr(hdbc1, SQL_ATTR_LOGIN_TIMEOUT, &timeout, 0, NULL)); is_num(timeout, 17); /* SQL_ATTR_CONNECTION_TIMEOUT is always 0, because the driver does not support it and the driver just silently swallows any value given for it. */ ok_con(hdbc1, SQLGetConnectAttr(hdbc1, SQL_ATTR_CONNECTION_TIMEOUT, &timeout, 0, NULL)); is_num(timeout, 0); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } /** Test that we can connect with UTF8 as our charset, and things work right. */ DECLARE_TEST(charset_utf8) { HDBC hdbc1; HSTMT hstmt1; SQLCHAR conn[256], conn_out[256]; SQLLEN len; SQLSMALLINT conn_out_len; SQLINTEGER str_size; /** Bug #19345: Table column length multiplies on size session character set */ ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug19345"); ok_sql(hstmt, "CREATE TABLE t_bug19345 (a VARCHAR(10), b VARBINARY(10))"); ok_sql(hstmt, "INSERT INTO t_bug19345 VALUES ('abc','def')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); sprintf((char *)conn, "DSN=%s;UID=%s;PWD=%s;CHARSET=utf8", mydsn, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn, pbuff); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, sizeof(conn), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_sql(hstmt1, "SELECT _latin1 0x73E36F207061756C6F"); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_str(my_fetch_str(hstmt1, conn_out, 1), "s\xC3\xA3o paulo", 10); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLColumns(hstmt1, (SQLCHAR *)"test", SQL_NTS, NULL, 0, (SQLCHAR *)"t_bug19345", SQL_NTS, (SQLCHAR *)"%", 1)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_num(my_fetch_int(hstmt1, 7), 10); ok_stmt(hstmt1, SQLGetData(hstmt1, 8, SQL_C_LONG, &str_size, 0, NULL)); /* utf8 mbmaxlen = 3 in libmysql before MySQL 6.0 */ if (str_size == 30) { is_num(my_fetch_int(hstmt1, 8), 30); is_num(my_fetch_int(hstmt1, 16), 30); } else { is_num(my_fetch_int(hstmt1, 8), 40); is_num(my_fetch_int(hstmt1, 16), 40); } ok_stmt(hstmt1, SQLFetch(hstmt1)); is_num(my_fetch_int(hstmt1, 7), 10); is_num(my_fetch_int(hstmt1, 8), 10); is_num(my_fetch_int(hstmt1, 16), 10); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); /* Big5's 0xA4A4 becomes utf8's 0xE4B8AD */ ok_sql(hstmt1, "SELECT _big5 0xA4A4"); ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_stmt(hstmt1, SQLGetData(hstmt1, 1, SQL_C_CHAR, conn, 2, &len)); is_num(conn[0], 0xE4); is_num(len, 3); ok_stmt(hstmt1, SQLGetData(hstmt1, 1, SQL_C_CHAR, conn, 2, &len)); is_num(conn[0], 0xB8); is_num(len, 2); ok_stmt(hstmt1, SQLGetData(hstmt1, 1, SQL_C_CHAR, conn, 2, &len)); is_num(conn[0], 0xAD); is_num(len, 1); expect_stmt(hstmt1, SQLGetData(hstmt1, 1, SQL_C_CHAR, conn, 2, &len), SQL_NO_DATA_FOUND); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug19345"); return OK; } /** GBK is a fun character set -- it contains multibyte characters that can contain 0x5c ('\'). This causes escaping problems if the driver doesn't realize that we're using GBK. (Big5 is another character set with a similar issue.) */ DECLARE_TEST(charset_gbk) { HDBC hdbc1; HSTMT hstmt1; SQLCHAR conn[256], conn_out[256]; /* The fun here is that 0xbf5c is a valid GBK character, and we have 0x27 as the second byte of an invalid GBK character. mysql_real_escape_string() handles this, as long as it knows the character set is GBK. */ SQLCHAR str[]= "\xef\xbb\xbf\x27\xbf\x10"; SQLSMALLINT conn_out_len; sprintf((char *)conn, "DSN=%s;UID=%s;PWD=%s;CHARSET=gbk", mydsn, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn, pbuff); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, sizeof(conn), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLPrepare(hstmt1, (SQLCHAR *)"SELECT ?", SQL_NTS)); ok_stmt(hstmt1, SQLBindParameter(hstmt1, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, str, sizeof(str), NULL)); ok_stmt(hstmt1, SQLExecute(hstmt1)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_str(my_fetch_str(hstmt1, conn_out, 1), str, sizeof(str)); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); return OK; } /** Bug #7445: MyODBC still doesn't support batch statements */ DECLARE_TEST(t_bug7445) { SQLLEN nRowCount; SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1; SET_DSN_OPTION(1 << 26); alloc_basic_handles(&henv1, &hdbc1, &hstmt1); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_bug7445"); /* create the table 'myodbc3_demo_result' */ ok_sql(hstmt1, "CREATE TABLE t_bug7445(name VARCHAR(20))"); /* multi statement insert */ ok_sql(hstmt1, "INSERT INTO t_bug7445 VALUES ('bogdan');" "INSERT INTO t_bug7445 VALUES ('georg');" "INSERT INTO t_bug7445 VALUES ('tonci');" "INSERT INTO t_bug7445 VALUES ('jim')"); ok_sql(hstmt1, "SELECT COUNT(*) FROM t_bug7445"); /* get the rows affected by update statement */ ok_stmt(hstmt1, SQLRowCount(hstmt1, &nRowCount)); is_num(nRowCount, 1); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "DROP TABLE t_bug7445"); free_basic_handles(&henv1, &hdbc1, &hstmt1); SET_DSN_OPTION(0); return OK; } /** Bug #30774: Username argument to SQLConnect used incorrectly */ DECLARE_TEST(t_bug30774) { SQLHDBC hdbc1; SQLHSTMT hstmt1; SQLCHAR username[MAX_ROW_DATA_LEN+1]= {0}; strcat((char *)username, (char *)myuid); strcat((char *)username, "!!!"); ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnect(hdbc1, mydsn, SQL_NTS, username, (SQLSMALLINT)strlen((char *)myuid), mypwd, SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_sql(hstmt1, "SELECT USER()"); ok_stmt(hstmt1, SQLFetch(hstmt1)); my_fetch_str(hstmt1, username, 1); printMessage("username: %s", username); is(!strstr((char *)username, "!!!")); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } /** Bug #30840: FLAG_NO_PROMPT doesn't do anything */ DECLARE_TEST(t_bug30840) { HDBC hdbc1; SQLCHAR conn[256], conn_out[256]; SQLSMALLINT conn_out_len; if (using_dm(hdbc)) skip("test does not work with all driver managers"); sprintf((char *)conn, "DSN=%s;UID=%s;PASSWORD=%s;OPTION=16", mydsn, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn, pbuff); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, (HWND)1, conn, sizeof(conn), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_PROMPT)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); return OK; } /** Bug #30983: SQL Statements limited to 64k */ DECLARE_TEST(t_bug30983) { SQLCHAR buf[(80 * 1024) + 100]; /* ~80k */ SQLCHAR *bufp = buf; SQLLEN buflen; int i, j; bufp+= sprintf((char *)bufp, "select '"); /* fill 1k of each value */ for (i= 0; i < 80; ++i) for (j= 0; j < 512; ++j, bufp += 2) sprintf((char *)bufp, "%02x", i); sprintf((char *)bufp, "' as val"); ok_stmt(hstmt, SQLExecDirect(hstmt, buf, SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, buf, 0, &buflen)); is_num(buflen, 80 * 1024); return OK; } /* Test the output string after calling SQLDriverConnect Note: Windows TODO fix this test create a comparable output string */ DECLARE_TEST(t_driverconnect_outstring) { HDBC hdbc1; SQLCHAR conn[256], conn_out[256], exp_out[256]; SQLSMALLINT conn_out_len, exp_conn_out_len; sprintf((char *)conn, "DSN=%s;UID=%s;PWD=%s;CHARSET=utf8", mydsn, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, sizeof(conn), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); sprintf((char *)exp_out, "DSN=%s;UID=%s", mydsn, myuid); if (mypwd && *mypwd) { strcat((char *)exp_out, ";PWD="); strcat((char *)exp_out, (char *)mypwd); } strcat((char *)exp_out, ";DATABASE="); ok_con(hdbc1, SQLGetConnectAttr(hdbc1, SQL_ATTR_CURRENT_CATALOG, exp_out + strlen((char *)exp_out), 100, NULL)); if (mysock != NULL) { strcat((char *)exp_out, ";SOCKET="); strcat((char *)exp_out, (char *)mysock); } strcat((char *)exp_out, ";PORT=3306;CHARSET=utf8"); printMessage("Output connection string: %s", conn_out); printMessage("Expected output string: %s", exp_out); /* save proper length for later tests */ exp_conn_out_len= conn_out_len; is_num(conn_out_len, strlen((char *)conn_out)); /* TODO is_str(conn_out, exp_out, strlen((char *)conn_out)); */ ok_con(hdbc1, SQLDisconnect(hdbc1)); /* test truncation */ conn_out_len= 999; expect_dbc(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, SQL_NTS, conn_out, 10 * sizeof(SQLWCHAR), &conn_out_len, SQL_DRIVER_NOPROMPT), SQL_SUCCESS_WITH_INFO); is_num(conn_out_len, 9); is_num(check_sqlstate_ex(hdbc1, SQL_HANDLE_DBC, "01004"), OK); ok_con(hdbc1, SQLDisconnect(hdbc1)); /* test truncation on boundary */ conn_out_len= 999; expect_dbc(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, SQL_NTS, conn_out, exp_conn_out_len * sizeof(SQLWCHAR), &conn_out_len, SQL_DRIVER_NOPROMPT), SQL_SUCCESS_WITH_INFO); is_num(conn_out_len, exp_conn_out_len - 1); is_num(check_sqlstate_ex(hdbc1, SQL_HANDLE_DBC, "01004"), OK); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); return OK; } DECLARE_TEST(setnames) { expect_sql(hstmt, "SET NAMES utf8", SQL_ERROR); expect_sql(hstmt, "SeT NamES utf8", SQL_ERROR); expect_sql(hstmt, " set names utf8", SQL_ERROR); expect_sql(hstmt, " set names utf8", SQL_ERROR); return OK; } DECLARE_TEST(setnames_conn) { HDBC hdbc1; SQLCHAR conn[256], conn_out[256]; SQLSMALLINT conn_out_len; sprintf((char *)conn, "DSN=%s;UID=%s;PWD=%s;INITSTMT={set names utf8}", mydsn, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); expect_dbc(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, SQL_NTS, conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT), SQL_ERROR); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); return OK; } /** Bug #15601: SQLCancel does not work to stop a query on the database server */ #ifndef THREAD DECLARE_TEST(sqlcancel) { SQLLEN pcbLength= SQL_LEN_DATA_AT_EXEC(0); ok_stmt(hstmt, SQLPrepare(hstmt, "select ?", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1,SQL_PARAM_INPUT,SQL_C_CHAR, SQL_VARCHAR,0,0,(SQLPOINTER)1,0,&pcbLength)); expect_stmt(hstmt, SQLExecute(hstmt), SQL_NEED_DATA); /* Without SQLCancel we would get "out of sequence" DM error */ ok_stmt(hstmt, SQLCancel(hstmt)); ok_stmt(hstmt, SQLPrepare(hstmt, "select 1", SQL_NTS)); ok_stmt(hstmt, SQLExecute(hstmt)); return OK; } #else #ifdef WIN32 DWORD WINAPI cancel_in_one_second(LPVOID arg) { HSTMT hstmt= (HSTMT)arg; Sleep(1000); if (SQLCancel(hstmt) != SQL_SUCCESS) printMessage("SQLCancel failed!"); return 0; } DECLARE_TEST(sqlcancel) { HANDLE thread; DWORD waitrc; thread= CreateThread(NULL, 0, cancel_in_one_second, hstmt, 0, NULL); /* SLEEP(n) returns 1 when it is killed. */ ok_sql(hstmt, "SELECT SLEEP(5)"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); waitrc= WaitForSingleObject(thread, 10000); is(!(waitrc == WAIT_TIMEOUT)); return OK; } #else void *cancel_in_one_second(void *arg) { HSTMT *hstmt= arg; sleep(1); if (SQLCancel(hstmt) != SQL_SUCCESS) printMessage("SQLCancel failed!"); return NULL; } #include DECLARE_TEST(sqlcancel) { pthread_t thread; pthread_create(&thread, NULL, cancel_in_one_second, hstmt); /* SLEEP(n) returns 1 when it is killed. */ ok_sql(hstmt, "SELECT SLEEP(10)"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); pthread_join(thread, NULL); return OK; } #endif // ifdef WIN32 #endif // ifndef THREAD /** Bug #32014: MyODBC / ADO Unable to open record set using dynamic cursor */ DECLARE_TEST(t_bug32014) { SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1; SQLUINTEGER info; long i=0; SQLSMALLINT value_len; long flags[]= { 0, (131072L << 4) /*FLAG_FORWARD_CURSOR*/, 32 /*FLAG_DYNAMIC_CURSOR*/, (131072L << 4) | 32, 0 }; long expectedInfo[]= { SQL_SO_FORWARD_ONLY|SQL_SO_STATIC, SQL_SO_FORWARD_ONLY, SQL_SO_FORWARD_ONLY|SQL_SO_STATIC|SQL_SO_DYNAMIC, SQL_SO_FORWARD_ONLY }; long expectedCurType[][4]= { {SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_STATIC, SQL_CURSOR_STATIC, SQL_CURSOR_STATIC}, {SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_FORWARD_ONLY}, {SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_STATIC, SQL_CURSOR_DYNAMIC, SQL_CURSOR_STATIC}, {SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_FORWARD_ONLY}}; do { SET_DSN_OPTION(flags[i]); alloc_basic_handles(&henv1, &hdbc1, &hstmt1); printMessage("checking %d (%d)", i, flags[i]); /*Checking that correct info is returned*/ ok_stmt(hstmt1, SQLGetInfo(hdbc1, SQL_SCROLL_OPTIONS, (SQLPOINTER) &info, sizeof(long), &value_len)); is_num(info, expectedInfo[i]); /*Checking that correct cursor type is set*/ ok_stmt(hstmt1, SQLSetStmtOption(hstmt1, SQL_CURSOR_TYPE , SQL_CURSOR_FORWARD_ONLY )); ok_stmt(hstmt1, SQLGetStmtOption(hstmt1, SQL_CURSOR_TYPE, (SQLPOINTER) &info)); is_num(info, expectedCurType[i][SQL_CURSOR_FORWARD_ONLY]); ok_stmt(hstmt1, SQLSetStmtOption(hstmt1, SQL_CURSOR_TYPE, SQL_CURSOR_KEYSET_DRIVEN )); ok_stmt(hstmt1, SQLGetStmtOption(hstmt1, SQL_CURSOR_TYPE, (SQLPOINTER) &info)); is_num(info, expectedCurType[i][SQL_CURSOR_KEYSET_DRIVEN]); ok_stmt(hstmt1, SQLSetStmtOption(hstmt1, SQL_CURSOR_TYPE, SQL_CURSOR_DYNAMIC )); ok_stmt(hstmt1, SQLGetStmtOption(hstmt1, SQL_CURSOR_TYPE, (SQLPOINTER) &info)); is_num(info, expectedCurType[i][SQL_CURSOR_DYNAMIC]); ok_stmt(hstmt1, SQLSetStmtOption(hstmt1, SQL_CURSOR_TYPE, SQL_CURSOR_STATIC )); ok_stmt(hstmt1, SQLGetStmtOption(hstmt1, SQL_CURSOR_TYPE, (SQLPOINTER) &info)); is_num(info, expectedCurType[i][SQL_CURSOR_STATIC]); free_basic_handles(&henv1, &hdbc1, &hstmt1); } while (flags[++i]); SET_DSN_OPTION(0); return OK; } /* Bug #10128 Error in evaluating simple mathematical expression ADO calls SQLNativeSql with a NULL pointer for the result length, but passes a non-NULL result buffer. */ DECLARE_TEST(t_bug10128) { SQLCHAR *query= (SQLCHAR *) "select 1,2,3,4"; SQLCHAR nativesql[1000]; SQLINTEGER nativelen; SQLINTEGER querylen= (SQLINTEGER) strlen((char *)query); ok_con(hdbc, SQLNativeSql(hdbc, query, SQL_NTS, NULL, 0, &nativelen)); is_num(nativelen, querylen); ok_con(hdbc, SQLNativeSql(hdbc, query, SQL_NTS, nativesql, 1000, NULL)); is_str(nativesql, query, querylen + 1); return OK; } /** Bug #32727: Unable to abort distributed transactions enlisted in MSDTC */ DECLARE_TEST(t_bug32727) { is_num(SQLSetConnectAttr(hdbc, SQL_ATTR_ENLIST_IN_DTC, (SQLPOINTER)1, SQL_IS_UINTEGER), SQL_ERROR); return OK; } /* Bug #28820: Varchar Field length is reported as larger than actual */ DECLARE_TEST(t_bug28820) { SQLULEN length; SQLCHAR dummy[20]; SQLSMALLINT i; ok_sql(hstmt, "drop table if exists t_bug28820"); ok_sql(hstmt, "create table t_bug28820 (" "x varchar(90) character set latin1," "y varchar(90) character set big5," "z varchar(90) character set utf8)"); ok_sql(hstmt, "select x,y,z from t_bug28820"); for (i= 0; i < 3; ++i) { length= 0; ok_stmt(hstmt, SQLDescribeCol(hstmt, i+1, dummy, sizeof(dummy), NULL, NULL, &length, NULL, NULL)); is_num(length, 90); } ok_sql(hstmt, "drop table if exists t_bug28820"); return OK; } /* Bug #31959 - Allows dirty reading with SQL_TXN_READ_COMMITTED isolation through ODBC */ DECLARE_TEST(t_bug31959) { SQLCHAR level[50] = "uninitialized"; SQLINTEGER i; SQLINTEGER levelid[] = {SQL_TXN_SERIALIZABLE, SQL_TXN_REPEATABLE_READ, SQL_TXN_READ_COMMITTED, SQL_TXN_READ_UNCOMMITTED}; SQLCHAR *levelname[] = {(SQLCHAR *)"SERIALIZABLE", (SQLCHAR *)"REPEATABLE-READ", (SQLCHAR *)"READ-COMMITTED", (SQLCHAR *)"READ-UNCOMMITTED"}; ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"select @@tx_isolation", SQL_NTS)); /* check all 4 valid isolation levels */ for(i = 3; i >= 0; --i) { ok_con(hdbc, SQLSetConnectAttr(hdbc, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER)levelid[i], 0)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, level, 50, NULL)); is_str(level, levelname[i], strlen((char *)levelname[i])); printMessage("Level = %s\n", level); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); } /* check invalid value (and corresponding SQL state) */ is_num(SQLSetConnectAttr(hdbc, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER)999, 0), SQL_ERROR); { SQLCHAR sql_state[6]; SQLINTEGER err_code= 0; SQLCHAR err_msg[SQL_MAX_MESSAGE_LENGTH]= {0}; SQLSMALLINT err_len= 0; memset(err_msg, 'C', SQL_MAX_MESSAGE_LENGTH); SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, 1, sql_state, &err_code, err_msg, SQL_MAX_MESSAGE_LENGTH - 1, &err_len); is_str(sql_state, (SQLCHAR *)"HY024", 5); } return OK; } /* Bug #41256 - NULL parameters don't work correctly with ADO. The null indicator pointer can be set separately through the descriptor field. This wasn't being checked separately. */ DECLARE_TEST(t_bug41256) { SQLHANDLE apd; SQLINTEGER val= 40; SQLLEN vallen= 19283; SQLLEN ind= SQL_NULL_DATA; SQLLEN reslen= 40; ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_PARAM_DESC, &apd, SQL_IS_POINTER, NULL)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_INTEGER, SQL_C_LONG, 0, 0, &val, 0, &vallen)); ok_desc(apd, SQLSetDescField(apd, 1, SQL_DESC_INDICATOR_PTR, &ind, SQL_IS_POINTER)); ok_sql(hstmt, "select ?"); val= 80; ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_LONG, &val, 0, &reslen)); is_num(SQL_NULL_DATA, reslen); is_num(80, val); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; } DECLARE_TEST(t_bug44971) { /* ok_sql(hstmt, "drop database if exists bug44971"); ok_sql(hstmt, "create database bug44971"); ok_con(hdbc, SQLSetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, "bug44971xxx", 8)); ok_sql(hstmt, "drop database if exists bug44971");*/ return OK; } DECLARE_TEST(t_bug48603) { SQLINTEGER timeout, interactive, diff= 1000; SQLSMALLINT conn_out_len; HDBC hdbc1; HSTMT hstmt1; SQLCHAR conn[256], conn_out[256], query[53]; ok_sql(hstmt, "select @@wait_timeout, @@interactive_timeout"); ok_stmt(hstmt,SQLFetch(hstmt)); timeout= my_fetch_int(hstmt, 1); interactive= my_fetch_int(hstmt, 2); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); if (timeout == interactive) { printMessage("Changing interactive timeout globally as it is equal to wait_timeout"); /* Changing globally interactive timeout to be able to test if INTERACTIVE option works */ sprintf((char *)query, "set GLOBAL interactive_timeout=%d", timeout + diff); if (!SQL_SUCCEEDED(SQLExecDirect(hstmt, query, SQL_NTS))) { printMessage("Don't have rights to change interactive timeout globally - so can't really test if option INTERACTIVE works"); // Let the testcase does not fail diff= 0; //return FAIL; } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); } else { printMessage("Interactive: %d, wait: %d", interactive, timeout); diff= interactive - timeout; } /* INITSTMT={set @@wait_timeout=%d} */ sprintf((char *)conn, "DSN=%s;UID=%s;PWD=%s;CHARSET=utf8;INITSTMT=set @@interactive_timeout=%d;INTERACTIVE=1", mydsn, myuid, mypwd, timeout+diff); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn, pbuff); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, sizeof(conn), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_sql(hstmt1, "select @@wait_timeout"); ok_stmt(hstmt1,SQLFetch(hstmt1)); { SQLINTEGER cur_timeout= my_fetch_int(hstmt1, 1); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); if (timeout == interactive) { /* setting global interactive timeout back if we changed it */ sprintf((char *)query, "set GLOBAL interactive_timeout=%d", timeout); ok_stmt(hstmt, SQLExecDirect(hstmt, query, SQL_NTS)); } is_num(timeout + diff, cur_timeout); } return OK; } /* Bug#45378 - spaces in connection string aren't removed */ DECLARE_TEST(t_bug45378) { HDBC hdbc1; SQLCHAR conn[256], conn_out[256]; SQLSMALLINT conn_out_len; sprintf((char *)conn, "DSN=%s; UID = {%s} ;PWD= %s ", mydsn, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, SQL_NTS, conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); return OK; } BEGIN_TESTS ADD_TEST(my_basics) ADD_TEST(t_max_select) ADD_TEST(t_basic) ADD_TEST(t_nativesql) #ifndef NO_DRIVERMANAGER ADD_TEST(t_reconnect) ADD_TEST(t_bug19823) #endif ADD_TEST(charset_utf8) ADD_TEST(charset_gbk) ADD_TEST(t_bug7445) ADD_TEST(t_bug30774) ADD_TEST(t_bug30840) ADD_TEST(t_bug30983) ADD_TEST(t_driverconnect_outstring) ADD_TEST(setnames) ADD_TEST(setnames_conn) ADD_TEST(sqlcancel) ADD_TEST(t_bug32014) ADD_TEST(t_bug10128) ADD_TEST(t_bug32727) ADD_TEST(t_bug28820) ADD_TEST(t_bug31959) ADD_TEST(t_bug41256) ADD_TEST(t_bug44971) ADD_TEST(t_bug48603) ADD_TEST(t_bug45378) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_tran.c100644 15766 12 13563 11707541005 20012 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" /** Test transaction behavior using InnoDB tables */ DECLARE_TEST(my_transaction) { if (!server_supports_trans(hdbc)) skip("Server does not support transactions."); /* set AUTOCOMMIT to OFF */ ok_con(hdbc, SQLSetConnectAttr(hdbc,SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF,0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t1"); ok_con(hdbc, SQLTransact(NULL,hdbc,SQL_COMMIT)); /* create the table 't1' using InnoDB */ ok_sql(hstmt, "CREATE TABLE t1 (col1 INT, col2 VARCHAR(30))" " ENGINE = InnoDB"); /* insert a row and commit the transaction */ ok_sql(hstmt, "INSERT INTO t1 VALUES(10,'venu')"); ok_con(hdbc, SQLTransact(NULL,hdbc,SQL_COMMIT)); /* now insert the second row, but roll back that transaction */ ok_sql(hstmt,"INSERT INTO t1 VALUES(20,'mysql')"); ok_con(hdbc, SQLTransact(NULL,hdbc,SQL_ROLLBACK)); /* delete first row, but roll it back */ ok_sql(hstmt,"DELETE FROM t1 WHERE col1 = 10"); ok_con(hdbc, SQLTransact(NULL,hdbc,SQL_ROLLBACK)); /* Bug #21588: Incomplete ODBC API implementaion */ /* insert a row, but roll it back using SQLTransact on the environment */ ok_sql(hstmt,"INSERT INTO t1 VALUES(30,'mysql')"); ok_con(hdbc, SQLTransact(henv,NULL,SQL_ROLLBACK)); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); /* test the results now, only one row should exist */ ok_sql(hstmt,"SELECT * FROM t1"); ok_stmt(hstmt, SQLFetch(hstmt)); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); /* now insert some more records to check SQLEndTran */ ok_sql(hstmt,"INSERT INTO t1 " "VALUES (30,'test'),(40,'transaction')"); ok_con(hdbc, SQLTransact(NULL,hdbc,SQL_COMMIT)); /* Commit the transaction using DBC handler */ ok_sql(hstmt,"DELETE FROM t1 WHERE col1 = 30"); ok_con(hdbc, SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT)); /* test the results now, select should not find any data */ ok_sql(hstmt,"SELECT * FROM t1 WHERE col1 = 30"); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); /* Delete a row to check, and commit the transaction using ENV handler */ ok_sql(hstmt,"DELETE FROM t1 WHERE col1 = 40"); ok_con(hdbc, SQLEndTran(SQL_HANDLE_ENV, henv, SQL_COMMIT)); /* test the results now, select should not find any data */ ok_sql(hstmt,"SELECT * FROM t1 WHERE col1 = 40"); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); /* drop the table */ ok_sql(hstmt,"DROP TABLE t1"); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); return OK; } DECLARE_TEST(t_tran) { if (!server_supports_trans(hdbc)) skip("Server does not support transactions."); ok_sql(hstmt, "DROP TABLE IF EXISTS t_tran"); ok_sql(hstmt, "CREATE TABLE t_tran (a INT, b VARCHAR(30)) ENGINE=InnoDB"); ok_con(hdbc, SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF)); ok_sql(hstmt, "INSERT INTO t_tran VALUES (10, 'venu')"); ok_stmt(hstmt, SQLTransact(NULL, hdbc, SQL_COMMIT)); ok_sql(hstmt, "INSERT INTO t_tran VALUES (20, 'mysql')"); ok_stmt(hstmt, SQLTransact(NULL, hdbc, SQL_ROLLBACK)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_tran"); is_num(myrowcount(hstmt), 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_con(hdbc, SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_tran"); return OK; } /** Test retrieval and setting of transaction isolation level. */ DECLARE_TEST(t_isolation) { SQLINTEGER isolation; SQLCHAR tx_isolation[20]; if (!server_supports_trans(hdbc)) skip("Server does not support transactions."); /* Check that the default is REPEATABLE READ. */ ok_con(hdbc, SQLGetConnectAttr(hdbc, SQL_ATTR_TXN_ISOLATION, &isolation, SQL_IS_POINTER, NULL)); is_num(isolation, SQL_TXN_REPEATABLE_READ); /* Change it to READ UNCOMMITTED. */ ok_con(hdbc, SQLSetConnectAttr(hdbc, SQL_ATTR_TXN_ISOLATION, (SQLPOINTER)SQL_TXN_READ_UNCOMMITTED, 0)); /* Check that the driver has rmeembered the new value. */ ok_con(hdbc, SQLGetConnectAttr(hdbc, SQL_ATTR_TXN_ISOLATION, &isolation, SQL_IS_POINTER, NULL)); is_num(isolation, SQL_TXN_READ_UNCOMMITTED); /* Check that it was actually changed on the server. */ ok_sql(hstmt, "SELECT @@tx_isolation"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_CHAR, tx_isolation, sizeof(tx_isolation), NULL)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(tx_isolation, "READ-UNCOMMITTED", 16); return OK; } BEGIN_TESTS ADD_TEST(my_transaction) ADD_TEST(t_tran) ADD_TEST(t_isolation) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_bug13766.c100644 15766 12 17155 11707541005 20233 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Tests for Bug #13766 - transforming dates to/from zero/invalid dates. * This is a separate file so the DSN OPTION doesn't affect other tests. */ #include "odbctap.h" /* * Test the FLAG_ZERO_DATE_TO_MIN transforms the date correctly. * NULL test is in my_datetime.c:t_bug12520() */ DECLARE_TEST(bug13766_result) { int i; SQL_DATE_STRUCT xdate[6]; SQL_TIMESTAMP_STRUCT xts[6]; SQLLEN isNull[12]; ok_sql(hstmt, "select cast('0000-00-00' as date), " "cast('0000-10-00' as date), " "cast('0000-00-10' as date), " "cast('2007-00-00' as date), " "cast('2007-10-00' as date), " "cast('2007-00-10' as date), " "cast('0000-00-00 00:00:00' as datetime), " "cast('0000-10-00 00:00:00' as datetime), " "cast('0000-00-10 00:00:00' as datetime), " "cast('2007-00-00 00:00:00' as datetime), " "cast('2007-10-00 00:00:00' as datetime), " "cast('2007-00-10 00:00:00' as datetime) "); ok_stmt(hstmt, SQLFetch(hstmt)); for (i= 0; i < 6; ++i) { ok_stmt(hstmt, SQLGetData(hstmt, i+1, SQL_C_TYPE_DATE, &xdate[i], 0, &isNull[i])); } for (i= 0; i < 6; ++i) { ok_stmt(hstmt, SQLGetData(hstmt, 6+i+1, SQL_C_TYPE_TIMESTAMP, &xts[i], 0, &isNull[6+i])); } i= 0; /* 0000-00-00 */ is_num(xdate[i].year, 0); is_num(xdate[i].month, 1); is_num(xdate[i].day, 1); is_num(xts[i].year, 0); is_num(xts[i].month, 1); is_num(xts[i].day, 1); i++; /* the server is not consistent in how it handles 0000-xx-xx, it changed within the 5.0 and 5.1 series */ if (isNull[i] == SQL_NULL_DATA) { is_num(isNull[i], SQL_NULL_DATA); is_num(isNull[6+i], SQL_NULL_DATA); i++; is_num(isNull[i], SQL_NULL_DATA); is_num(isNull[6+i], SQL_NULL_DATA); i++; } else { /* 0000-10-00 */ is_num(xdate[i].year, 0); is_num(xdate[i].month, 10); is_num(xdate[i].day, 1); is_num(xts[i].year, 0); is_num(xts[i].month, 10); is_num(xts[i].day, 1); i++; /* 0000-00-10 */ is_num(xdate[i].year, 0); is_num(xdate[i].month, 1); is_num(xdate[i].day, 10); is_num(xts[i].year, 0); is_num(xts[i].month, 1); is_num(xts[i].day, 10); i++; } /* 2007-00-00 */ is_num(xdate[i].year, 2007); is_num(xdate[i].month, 1); is_num(xdate[i].day, 1); is_num(xts[i].year, 2007); is_num(xts[i].month, 1); is_num(xts[i].day, 1); i++; /* 2007-10-00 */ is_num(xdate[i].year, 2007); is_num(xdate[i].month, 10); is_num(xdate[i].day, 1); is_num(xts[i].year, 2007); is_num(xts[i].month, 10); is_num(xts[i].day, 1); i++; /* 2007-00-10 */ is_num(xdate[i].year, 2007); is_num(xdate[i].month, 1); is_num(xdate[i].day, 10); is_num(xts[i].year, 2007); is_num(xts[i].month, 1); is_num(xts[i].day, 10); return OK; } /* * Bug #13766 - Test the FLAG_MIN_DATE_TO_ZERO transforms the date * correctly. */ DECLARE_TEST(bug13766_query) { SQL_DATE_STRUCT xdate; SQL_TIMESTAMP_STRUCT xts; char result[50]; ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"select ?", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TYPE_DATE, SQL_TYPE_DATE, 0, 0, &xdate, 0, NULL)); /* Test that we fix min date -> zero date */ xdate.year= 0; xdate.month= 1; xdate.day= 1; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, result, 50, NULL)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_str(result, "0000-00-00", 10); /* we dont touch dates that are not 0000-01-01 */ xdate.year= 0; xdate.month= 0; xdate.day= 0; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, result, 50, NULL)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_str(result, "0000-00-00", 10); xdate.year= 1; xdate.month= 0; xdate.day= 0; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, result, 50, NULL)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_str(result, "0001-00-00", 10); xdate.year= 0; xdate.month= 1; xdate.day= 0; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, result, 50, NULL)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_str(result, "0000-01-00", 10); xdate.year= 0; xdate.month= 0; xdate.day= 1; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, result, 50, NULL)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_str(result, "0000-00-01", 10); /* same stuff for timestamps */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 0, 0, &xts, 0, NULL)); xts.hour = 19; xts.minute = 22; xts.second = 25; xts.year= 0; xts.month= 1; xts.day= 1; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, result, 50, NULL)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_str(result, "0000-00-00 19:22:25", 19); xts.year= 0; xts.month= 0; xts.day= 0; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, result, 50, NULL)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_str(result, "0000-00-00 19:22:25", 19); xts.year= 1; xts.month= 0; xts.day= 0; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, result, 50, NULL)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_str(result, "0001-00-00 19:22:25", 19); xts.year= 0; xts.month= 1; xts.day= 0; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, result, 50, NULL)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_str(result, "0000-01-00 19:22:25", 19); xts.year= 0; xts.month= 0; xts.day= 1; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, result, 50, NULL)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_str(result, "0000-00-01 19:22:25", 19); return OK; } BEGIN_TESTS ADD_TEST(bug13766_result) ADD_TEST(bug13766_query) END_TESTS /* FLAG_ZERO_DATE_TO_MIN & FLAG_MIN_DATE_TO_ZERO */ SET_DSN_OPTION((1 << 24) | (1 << 25)); RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_curext.c100644 15766 12 22423 11707541005 20353 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" DECLARE_TEST(my_pcbvalue) { SQLRETURN rc; SQLLEN nRowCount; SQLINTEGER nData= 500; SQLLEN int_pcbValue, pcbValue, pcbValue1, pcbValue2; SQLCHAR szData[255]={0}; ok_sql(hstmt, "DROP TABLE IF EXISTS my_pcbValue"); ok_sql(hstmt, "create table my_pcbValue(id int, name varchar(30),\ name1 varchar(30),\ name2 varchar(30))"); ok_sql(hstmt, "insert into my_pcbValue(id,name) values(100,'venu')"); ok_sql(hstmt, "insert into my_pcbValue(id,name) values(200,'monty')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,0,&int_pcbValue); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,15,&pcbValue); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,3,SQL_C_CHAR,szData,3,&pcbValue1); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,4,SQL_C_CHAR,szData,2,&pcbValue2); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY ,(SQLPOINTER)SQL_CONCUR_ROWVER , 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1 , 0); mystmt(hstmt, rc); /* Open the resultset of table 'my_demo_cursor' */ ok_sql(hstmt, "SELECT * FROM my_pcbValue"); /* goto the last row */ rc = SQLFetchScroll(hstmt, SQL_FETCH_LAST, 1L); mystmt(hstmt,rc); /* Now delete the newly updated record */ strcpy((char*)szData,"updated"); nData = 99999; int_pcbValue=2; pcbValue=3; pcbValue1=9; pcbValue2=SQL_NTS; rc = SQLSetPos(hstmt,1,SQL_UPDATE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt, &nRowCount); mystmt(hstmt, rc); printMessage(" total rows updated:%d\n",nRowCount); is_num(nRowCount, 1); /* Free statement cursor resorces */ rc = SQLFreeStmt(hstmt, SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* Now fetch and verify the data */ ok_sql(hstmt, "SELECT * FROM my_pcbValue"); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_LONG,&nData,0,NULL); mystmt(hstmt,rc); is_num(nData, 99999); rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,50,NULL); mystmt(hstmt,rc); is_str(szData, "upd", 4); rc = SQLGetData(hstmt,3,SQL_C_CHAR,szData,50,NULL); mystmt(hstmt,rc); is_str(szData, "updated", 8); rc = SQLGetData(hstmt,4,SQL_C_CHAR,szData,50,NULL); mystmt(hstmt,rc); is_str(szData, "updated", 8); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS my_pcbValue"); return OK; } /* to test the pcbValue on cursor ops **/ DECLARE_TEST(my_pcbvalue_add) { SQLRETURN rc; SQLLEN nRowCount; SQLINTEGER nData= 500; SQLLEN int_pcbValue, pcbValue, pcbValue1, pcbValue2; SQLCHAR szData[255]={0}; ok_sql(hstmt, "DROP TABLE IF EXISTS my_pcbValue_add"); ok_sql(hstmt, "create table my_pcbValue_add(id int, name varchar(30),\ name1 varchar(30),\ name2 varchar(30))"); ok_sql(hstmt,"insert into my_pcbValue_add(id,name) values(100,'venu')"); ok_sql(hstmt,"insert into my_pcbValue_add(id,name) values(200,'monty')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,0,&int_pcbValue); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,15,&pcbValue); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,3,SQL_C_CHAR,szData,3,&pcbValue1); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,4,SQL_C_CHAR,szData,2,&pcbValue2); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY ,(SQLPOINTER)SQL_CONCUR_ROWVER , 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1 , 0); mystmt(hstmt, rc); /* Open the resultset of table 'my_pcbValue_add' */ ok_sql(hstmt, "SELECT * FROM my_pcbValue_add"); mystmt(hstmt,rc); /* goto the last row */ rc = SQLFetchScroll(hstmt, SQL_FETCH_LAST, 1L); mystmt(hstmt,rc); /* Now delete the newly updated record */ strcpy((char*)szData,"inserted"); nData = 99999; int_pcbValue=2; pcbValue=3; pcbValue1=6; pcbValue2=SQL_NTS; rc = SQLSetPos(hstmt,1,SQL_ADD,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt, &nRowCount); mystmt(hstmt, rc); printMessage(" total rows updated:%d\n",nRowCount); is_num(nRowCount, 1); /* Free statement cursor resorces */ rc = SQLFreeStmt(hstmt, SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* Now fetch and verify the data */ ok_sql(hstmt, "SELECT * FROM my_pcbValue_add"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_LONG,&nData,0,NULL); mystmt(hstmt,rc); is_num(nData, 99999); rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,50,NULL); mystmt(hstmt,rc); is_str(szData, "ins", 4); rc = SQLGetData(hstmt,3,SQL_C_CHAR,szData,50,NULL); mystmt(hstmt,rc); is_str(szData, "insert", 7); rc = SQLGetData(hstmt,4,SQL_C_CHAR,szData,50,NULL); mystmt(hstmt,rc); is_str(szData, "inserted", 9); rc = SQLFetch(hstmt); mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS my_pcbValue_add"); return OK; } /* spaces in column names */ DECLARE_TEST(my_columnspace) { SQLRETURN rc; ok_sql(hstmt, "DROP TABLE IF EXISTS TestColNames"); ok_sql(hstmt, "CREATE TABLE `TestColNames`(`Value One` text, `Value Two` text,`Value Three` text)"); ok_sql(hstmt, "INSERT INTO TestColNames VALUES ('venu','anuganti','mysql ab')"); ok_sql(hstmt, "INSERT INTO TestColNames VALUES ('monty','widenius','mysql ab')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM `TestColNames`"); is_num(my_print_non_format_result(hstmt), 2); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT `Value One`,`Value Two`,`Value Three` FROM `TestColNames`"); is_num(my_print_non_format_result(hstmt), 2); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS TestColNames"); return OK; } /* to test the empty string returning NO_DATA */ DECLARE_TEST(my_empty_string) { SQLRETURN rc; SQLLEN pcbValue; SQLCHAR szData[255]={0}; ok_sql(hstmt, "DROP TABLE IF EXISTS my_empty_string"); ok_sql(hstmt, "create table my_empty_string(name varchar(30))"); ok_sql(hstmt, "insert into my_empty_string values('')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); /* Now fetch and verify the data */ ok_sql(hstmt, "SELECT * FROM my_empty_string"); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_CHAR,szData,50,&pcbValue); mystmt(hstmt,rc); printMessage("szData:%s(%d)\n",szData,pcbValue); rc = SQLFetch(hstmt); mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS my_empty_string"); return OK; } BEGIN_TESTS ADD_TEST(my_pcbvalue) ADD_TEST(my_pcbvalue_add) ADD_TEST(my_columnspace) ADD_TEST(my_empty_string) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/odbc.ini.in100644 15766 12 467 11707541005 20151 0ustar00cteamstaff[ODBC Data Sources] data_source_name = myodbc5 [myodbc5] Driver = @TEST_DRIVER@ DATABASE = @TEST_DATABASE@ DESCRIPTION = MySQL ODBC 5.1 Driver test SERVER = @TEST_SERVER@ UID = @TEST_UID@ PASSWORD = @TEST_PASSWORD@ SOCKET = @TEST_SOCKET@ @TEST_OPTIONS@ mysql-connector-odbc-5.1.10-src/test/my_dyn_cursor.c100644 15766 12 51202 11707541005 21225 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" /* perform positioned update and delete */ DECLARE_TEST(my_dynamic_pos_cursor) { SQLRETURN rc; SQLLEN nRowCount; SQLHSTMT hstmt_pos; SQLINTEGER nData = 500; SQLCHAR szData[255]={0}; /* initialize data */ ok_sql(hstmt, "drop table if exists my_dynamic_cursor"); ok_sql(hstmt, "create table my_dynamic_cursor(id int, name varchar(30))"); ok_sql(hstmt, "insert into my_dynamic_cursor values(100,'venu')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(200,'monty')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); /* create new statement handle */ rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt_pos); mycon(hdbc, rc); /* set the cursor name as 'mysqlcur' on hstmt */ rc = SQLSetCursorName(hstmt, (SQLCHAR *)"mysqlcur", SQL_NTS); mystmt(hstmt, rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,0,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,15,NULL); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY ,(SQLPOINTER)SQL_CONCUR_ROWVER , 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1 , 0); mystmt(hstmt, rc); /* Open the resultset of table 'my_demo_cursor' */ ok_sql(hstmt, "SELECT * FROM my_dynamic_cursor"); mystmt(hstmt,rc); /* goto the last row */ rc = SQLFetchScroll(hstmt, SQL_FETCH_LAST, 1L); mystmt(hstmt,rc); /* now update the name field to 'update' using positioned cursor */ ok_sql(hstmt_pos, "UPDATE my_dynamic_cursor SET id=300, name='updated' WHERE CURRENT OF mysqlcur"); rc = SQLRowCount(hstmt_pos, &nRowCount); mystmt(hstmt_pos, rc); printMessage(" total rows updated:%d\n",nRowCount); is_num(nRowCount, 1); /* Now delete the newly updated record */ strcpy((char*)szData,"updated"); nData = 300; rc = SQLSetPos(hstmt,1,SQL_DELETE,SQL_LOCK_UNLOCK); mystmt_err(hstmt,rc==SQL_ERROR,rc); rc = SQLSetPos(hstmt,1,SQL_DELETE,SQL_LOCK_EXCLUSIVE); mystmt_err(hstmt,rc==SQL_ERROR,rc); rc = SQLSetPos(hstmt,1,SQL_DELETE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt, &nRowCount); mystmt(hstmt, rc); printMessage(" total rows deleted:%d\n",nRowCount); is_num(nRowCount, 1); /* Free statement cursor resorces */ rc = SQLFreeStmt(hstmt, SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt_pos, SQL_CLOSE); mystmt(hstmt,rc); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* Free the statement 'hstmt_pos' */ rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt_pos); mystmt(hstmt_pos,rc); /* Now fetch and verify the data */ ok_sql(hstmt, "SELECT * FROM my_dynamic_cursor"); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_LONG,&nData,0,NULL); mystmt(hstmt,rc); is_num(nData, 100); rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,50,NULL); mystmt(hstmt,rc); is_str(szData,"venu", 5); rc = SQLFetch(hstmt); mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS my_dynamic_cursor"); return OK; } /* perform positioned update and delete */ DECLARE_TEST(my_dynamic_pos_cursor1) { SQLRETURN rc; SQLLEN nRowCount; SQLHSTMT hstmt_pos; SQLINTEGER i,nData[15]; char data[30],szData[15][10]={0}; /* initialize data */ ok_sql(hstmt, "drop table if exists my_dynamic_cursor"); ok_sql(hstmt, "create table my_dynamic_cursor(id int, name varchar(30))"); ok_sql(hstmt, "insert into my_dynamic_cursor values(1,'MySQL1')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(2,'MySQL2')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(3,'MySQL3')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(4,'MySQL4')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(5,'MySQL5')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(6,'MySQL6')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(7,'MySQL7')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(8,'MySQL8')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(9,'MySQL9')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(10,'MySQL10')"); SQLFreeStmt(hstmt,SQL_CLOSE); /* create new statement handle */ rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt_pos); mycon(hdbc, rc); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); /* set the cursor name as 'mysqlcur' on hstmt */ rc = SQLSetCursorName(hstmt, (SQLCHAR *)"mysqlcur", SQL_NTS); mystmt(hstmt, rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,0,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,20,NULL); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)3,0); mystmt(hstmt,rc); /* Open the resultset of table 'my_demo_cursor' */ ok_sql(hstmt, "SELECT * FROM my_dynamic_cursor"); /* goto the last row */ rc = SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 5L); mystmt(hstmt,rc); /*rc = SQLSetPos(hstmt,SQL_POSITION,2,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); */ /* now update the name field to 'update' using positioned cursor */ ok_sql(hstmt_pos, "UPDATE my_dynamic_cursor SET id=999, name='updated' WHERE CURRENT OF mysqlcur"); rc = SQLRowCount(hstmt_pos, &nRowCount); mystmt(hstmt_pos, rc); printMessage(" total rows updated:%d\n",nRowCount); is_num(nRowCount, 1); strcpy(szData[1],"updated"); nData[1] = 999; rc = SQLSetPos(hstmt,2,SQL_DELETE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt, &nRowCount); mystmt(hstmt, rc); printMessage(" total rows deleted:%d\n",nRowCount); is_num(nRowCount, 1); /* Free statement cursor resorces */ rc = SQLFreeStmt(hstmt, SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt, SQL_RESET_PARAMS); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt_pos, SQL_CLOSE); mystmt(hstmt,rc); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* Free the statement 'hstmt_pos' */ rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt_pos); mystmt(hstmt_pos,rc); /* Now fetch and verify the data */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)1,0); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM my_dynamic_cursor"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&i,0,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR, data,20,NULL); mystmt(hstmt,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,4L); mystmt(hstmt,rc); is_num(i, 4); is_str(data,"MySQL4", 7); rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,1L); mystmt(hstmt,rc); is_num(i, 999); is_str(data, "updated", 8); rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,1L); mystmt(hstmt,rc); is_num(i, 7); is_str(data, "MySQL7", 7); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,10L); mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS my_dynamic_cursor"); return OK; } /* CURSOR POSITION - rowset size 1 */ DECLARE_TEST(my_position) { SQLRETURN rc; SQLLEN nlen; char szData[255]= {0}; SQLINTEGER nData; SQLLEN nrow; ok_sql(hstmt, "drop table if exists my_position"); ok_sql(hstmt, "create table my_position(col1 int, col2 varchar(30))"); ok_sql(hstmt, "insert into my_position values(100,'MySQL1')"); ok_sql(hstmt, "insert into my_position values(200,'MySQL2')"); ok_sql(hstmt, "insert into my_position values(300,'MySQL3')"); ok_sql(hstmt, "insert into my_position values(400,'MySQL4')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY ,(SQLPOINTER)SQL_CONCUR_ROWVER , 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1 , 0); mystmt(hstmt, rc); ok_sql(hstmt,"select * from my_position"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,0,&nrow); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,10,&nlen); mystmt(hstmt,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,3); mystmt(hstmt,rc); nData = 999; nrow = SQL_COLUMN_IGNORE; strcpy(szData,"update"); rc = SQLSetPos(hstmt,1,SQL_POSITION,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLSetPos(hstmt,1,SQL_UPDATE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage(" rows affected:%d\n",nlen); is_num(nlen, 1); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "select * from my_position"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_LONG,&nData,0,NULL); mystmt(hstmt,rc); rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,10,NULL); mystmt(hstmt,rc); is_num(nData, 300); is_str(szData, "update", 7); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS my_position"); return OK; } /* CURSOR POSITION - rowset size 3 */ DECLARE_TEST(my_position1) { SQLINTEGER nData[15]; SQLLEN nlen[15]= {0}, nrow[15]= {0}; SQLCHAR szData[15][15]= {0}; ok_sql(hstmt, "DROP TABLE IF EXISTS my_position"); ok_sql(hstmt, "CREATE TABLE my_position (col1 INT, col2 VARCHAR(30))"); ok_sql(hstmt, "INSERT INTO my_position VALUES (1,'MySQL1'), (2,'MySQL2')," "(3,'MySQL3'), (4,'MySQL4'), (5,'MySQL5'), (6,'MySQL6'), (7,'MySQL7')," "(8,'MySQL8'), (9,'MySQL9'), (10,'MySQL10'), (11,'MySQL11')," "(12,'MySQL12')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_ROWVER, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)3, 0)); ok_sql(hstmt, "SELECT * FROM my_position"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, nrow)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData[0]), nlen)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 4)); nData[0]= 888; nData[1]= 999; nrow[1]= SQL_COLUMN_IGNORE; nData[2]= 1000; strcpy((char *)szData[0], "updatex"); nlen[0]= 15; strcpy((char *)szData[1], "updatey"); nlen[1]= 15; strcpy((char *)szData[2], "updatez"); nlen[2]= 15; ok_stmt(hstmt, SQLSetPos(hstmt, 2, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLSetPos(hstmt, 3, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM my_position"); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 4)); is_num(nData[0], 4); is_str(szData[0], "MySQL4", 6); is_num(nData[1], 5); is_str(szData[1], "updatey", 7); is_num(nData[2], 1000); is_str(szData[2], "updatez", 7); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS my_position"); return OK; } /* IROW VALUE - 0 */ DECLARE_TEST(my_zero_irow_update) { SQLRETURN rc; SQLLEN nlen[15]= {0}, nrow[15]= {0}; char szData[15][15]={0}; SQLINTEGER nData[15]; ok_sql(hstmt, "drop table if exists my_zero_irow"); ok_sql(hstmt, "create table my_zero_irow(col1 int, col2 varchar(30))"); ok_sql(hstmt, "insert into my_zero_irow values(1,'MySQL1')"); ok_sql(hstmt, "insert into my_zero_irow values(2,'MySQL2')"); ok_sql(hstmt, "insert into my_zero_irow values(3,'MySQL3')"); ok_sql(hstmt, "insert into my_zero_irow values(4,'MySQL4')"); ok_sql(hstmt, "insert into my_zero_irow values(5,'MySQL5')"); ok_sql(hstmt, "insert into my_zero_irow values(6,'MySQL6')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY ,(SQLPOINTER)SQL_CONCUR_ROWVER , 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)3 , 0); mystmt(hstmt, rc); ok_sql(hstmt, "select * from my_zero_irow"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,0,nrow); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,sizeof(szData[0]),nlen); mystmt(hstmt,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,2); mystmt(hstmt,rc); nData[0] = 888; nData[1] = 999; nrow[1] = SQL_COLUMN_IGNORE; nData[2] = 1000; strcpy(szData[0],"updatex"); nlen[0] = 15; strcpy(szData[1],"updatey"); nlen[1] = 15; strcpy(szData[2],"updatez"); nlen[2] = 15; rc = SQLSetPos(hstmt,0,SQL_UPDATE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "select * from my_zero_irow"); mystmt(hstmt,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,2); mystmt(hstmt,rc); is_num(nData[0], 888); is_str(szData[0], "updatex", 8); is_num(nData[1], 3); is_str(szData[1], "updatey", 8); is_num(nData[2], 1000); is_str(szData[2], "updatez", 8); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1 , 0); mystmt(hstmt, rc); ok_sql(hstmt, "DROP TABLE IF EXISTS my_zero_irow"); return OK; } /* IROW VALUE - 0 - DELETE */ DECLARE_TEST(my_zero_irow_delete) { SQLRETURN rc; SQLLEN nlen[15]= {0}, nrow[15]= {0}; char szData[15][15]={0}; SQLINTEGER nData[15]; ok_sql(hstmt, "drop table if exists my_zero_irow"); ok_sql(hstmt, "create table my_zero_irow(col1 int, col2 varchar(30))"); ok_sql(hstmt, "insert into my_zero_irow values(1,'MySQL1')"); ok_sql(hstmt, "insert into my_zero_irow values(2,'MySQL2')"); ok_sql(hstmt, "insert into my_zero_irow values(3,'MySQL3')"); ok_sql(hstmt, "insert into my_zero_irow values(4,'MySQL4')"); ok_sql(hstmt, "insert into my_zero_irow values(5,'MySQL5')"); ok_sql(hstmt, "insert into my_zero_irow values(6,'MySQL6')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY ,(SQLPOINTER)SQL_CONCUR_ROWVER , 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)3 , 0); mystmt(hstmt, rc); ok_sql(hstmt,"select * from my_zero_irow"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,0,nrow); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,sizeof(szData[0]),nlen); mystmt(hstmt,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,2); mystmt(hstmt,rc); rc = SQLSetPos(hstmt,0,SQL_DELETE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "select * from my_zero_irow"); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,1); mystmt(hstmt,rc); is_num(nData[0], 1); is_str(szData[0], "MySQL1", 7); is_num(nData[1], 5); is_str(szData[1], "MySQL5", 7); is_num(nData[2], 6); is_str(szData[2], "MySQL6", 7); rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,1); mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1 , 0); mystmt(hstmt, rc); ok_sql(hstmt, "DROP TABLE IF EXISTS my_zero_irow"); return OK; } /* DYNAMIC CURSOR TESTING */ DECLARE_TEST(my_dynamic_cursor) { SQLRETURN rc; SQLLEN nlen; SQLINTEGER nData = 500; SQLCHAR szData[255]={0}; /* initialize data */ ok_sql(hstmt, "drop table if exists my_dynamic_cursor"); ok_sql(hstmt, "create table my_dynamic_cursor(col1 int, col2 varchar(30))"); ok_sql(hstmt, "insert into my_dynamic_cursor values(100,'venu')"); ok_sql(hstmt, "insert into my_dynamic_cursor values(200,'monty')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY , (SQLPOINTER)SQL_CONCUR_ROWVER , 0); mystmt(hstmt, rc); /* Now, add a row of data */ ok_sql(hstmt, "select * from my_dynamic_cursor"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,0,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,15,NULL); mystmt(hstmt,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,1); mystmt(hstmt,rc); nData = 300; strcpy((char *)szData , "mysql"); rc = SQLSetPos(hstmt,3,SQL_ADD,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage("rows affected:%d\n",nlen); strcpy((char *)szData , "insert-new2"); rc = SQLSetPos(hstmt,1,SQL_ADD,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage("rows affected:%d\n",nlen); strcpy((char *)szData , "insert-new3"); rc = SQLSetPos(hstmt,0,SQL_ADD,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage("rows affected:%d\n",nlen); strcpy((char *)szData , "insert-new4"); rc = SQLSetPos(hstmt,10,SQL_ADD,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&nlen); mystmt(hstmt,rc); printMessage("rows affected:%d\n",nlen); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "select * from my_dynamic_cursor"); mystmt(hstmt,rc); is_num(myresult(hstmt), 6); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS my_dynamic_cursor"); return OK; } BEGIN_TESTS ADD_TEST(my_dynamic_pos_cursor) ADD_TEST(my_dynamic_pos_cursor1) ADD_TEST(my_position) ADD_TEST(my_position1) ADD_TEST(my_zero_irow_update) ADD_TEST(my_zero_irow_delete) ADD_TEST(my_dynamic_cursor) END_TESTS SET_DSN_OPTION(35); RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_use_result.c100644 15766 12 14124 11707541005 21232 0ustar00cteamstaff/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" SQLINTEGER my_max_rows= 100; /* making use of mysql_use_result */ DECLARE_TEST(t_use_result) { SQLINTEGER i, row_count= 0; SQLCHAR ch[]= "MySQL AB"; SQLRETURN rc; ok_sql(hstmt, "DROP TABLE IF EXISTS t_use_result"); ok_sql(hstmt, "CREATE TABLE t_use_result (id INT, name CHAR(10))"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "INSERT INTO t_use_result VALUES (?,?)", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &i, 0, NULL)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, ch, sizeof(ch), NULL)); for (i= 1; i <= my_max_rows; i++) ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_use_result"); rc= SQLFetch(hstmt); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { row_count++; rc= SQLFetch(hstmt); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is_num(row_count, my_max_rows); ok_sql(hstmt, "DROP TABLE IF EXISTS t_use_result"); return OK; } /** Bug #4657: "Don't Cache Results" crashes when using catalog functions */ DECLARE_TEST(t_bug4657) { SQLCHAR name[10]; SQLSMALLINT column_count; SQLLEN name_length; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug4657"); ok_sql(hstmt, "CREATE TABLE t_bug4657 (a INT)"); ok_stmt(hstmt, SQLTables(hstmt, (SQLCHAR *)"", SQL_NTS, (SQLCHAR *)"", SQL_NTS, (SQLCHAR *)"", SQL_NTS, (SQLCHAR *)"UNKNOWN", SQL_NTS)); ok_stmt(hstmt, SQLNumResultCols(hstmt, &column_count)); is_num(column_count, 5); ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_CHAR, name, sizeof(name), &name_length)); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug4657"); return OK; } /** Bug #39878: No error signaled if timeout during fetching data. */ DECLARE_TEST(t_bug39878) { int i; SQLINTEGER row_count= 0; SQLRETURN rc; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY, 0)); printMessage("Creating table t_bug39878"); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug39878"); ok_sql(hstmt, "CREATE TABLE t_bug39878 (a INT)"); // Fill table with data printMessage("Filling table with data..."); ok_sql(hstmt, "INSERT INTO t_bug39878 VALUES (0), (1)"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "INSERT INTO t_bug39878 SELECT a+? FROM t_bug39878", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &row_count, 0, NULL)); for (i=1, row_count= 2; i < 14; ++i, row_count *= 2) ok_stmt(hstmt, SQLExecute(hstmt)); printMessage("inserted %d rows.", row_count); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); printMessage("Setting net_write_timeout to 1"); ok_sql(hstmt, "SET net_write_timeout=1"); // Table scan ok_sql(hstmt, "SELECT * FROM t_bug39878"); printMessage("Started table scan, sleeping 3sec ..."); sleep(3); printMessage("Fetching rows..."); while (SQL_SUCCEEDED(rc= SQLFetch(hstmt))) { row_count--; } print_diag(rc, SQL_HANDLE_STMT, hstmt, "SQLFetch()", __FILE__, __LINE__); printMessage("Scan interrupted, %d rows left in the table.", row_count); { char *rc_name; switch(rc) { case SQL_SUCCESS: rc_name= "SQL_SUCCESS"; break; case SQL_SUCCESS_WITH_INFO: rc_name= "SQL_SUCCESS_WITH_INFO"; break; case SQL_NO_DATA: rc_name= "SQL_NO_DATA"; break; case SQL_STILL_EXECUTING: rc_name= "SQL_STILL_EXECUTING"; break; case SQL_ERROR: rc_name= "SQL_ERROR"; break; case SQL_INVALID_HANDLE: rc_name= "SQL_INVALID_HANDLE"; break; default: rc_name= ""; break; } printMessage("Last SQLFetch() returned: %s", rc_name); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); is(row_count == 0 || rc == SQL_ERROR); // We re-connect to drop the table (as connection might be broken) free_basic_handles(&henv, &hdbc, &hstmt); alloc_basic_handles(&henv, &hdbc, &hstmt); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug39878"); return OK; } BEGIN_TESTS ADD_TEST(t_use_result) ADD_TEST(t_bug4657) ADD_TEST(t_bug39878) END_TESTS SET_DSN_OPTION(1048576); RUN_TESTS mysql-connector-odbc-5.1.10-src/test/Makefile.am100644 15766 12 7720 11707541005 20207 0ustar00cteamstaff# Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Setup to be able to run tests # # The INI filename has to include "odbc.ini" to work around a bug in the # version of iODBC shipped with Mac OS X. # TEST_PREFIX = TEST_ODBCINI = odbc.ini TEST_ODBCINSTINI = odbcinst.ini TEST_DSN = myodbc5 # XXX this is not really correct, but works for testing. TEST_DRIVER = ../driver/.libs/libmyodbc5.so TEST_DATABASE = test TEST_SERVER = localhost TEST_UID = root TEST_PASSWORD = TEST_SOCKET = /tmp/mysql.sock VALGRIND = INCLUDES= -I$(top_srcdir)/test AM_LDFLAGS= @myodbc_test_linklib@ tap_tests= \ my_basics \ my_blob \ my_bulk \ my_catalog \ my_curext \ my_cursor \ my_datetime \ my_desc \ my_dyn_cursor \ my_error \ my_info \ my_keys \ my_param \ my_prepare \ my_relative \ my_result \ my_scroll \ my_tran \ my_types \ my_unicode \ my_unixodbc \ my_use_result \ my_bug13766 noinst_PROGRAMS= $(tap_tests) # This is a cheat -- we use MYSQL_LIB only because we need the threading # library flags, not the MySQL client library itself LDADD= @MYSQL_LIB@ test: make-test-ini $(tap_tests) ODBCSYSINI=. \ ODBCINI=$(TEST_ODBCINI) ODBCINSTINI=$(TEST_ODBCINSTINI) \ TEST_DRIVER=$(TEST_DRIVER) TEST_DSN=$(TEST_DSN) TEST_UID=$(TEST_UID) \ TEST_PASSWORD=$(TEST_PASSWORD) TEST_SOCKET=$(TEST_SOCKET) \ perl $(srcdir)/unit.pl run $(tap_tests) test-bt: make-test-ini $(tap_tests) -for f in $(tap_tests) ; \ do \ echo "******************************************************************************" ; \ echo "Starting test $(TEST_PREFIX)$$f" ; \ ODBCSYSINI=. \ ODBCINI=$(TEST_ODBCINI) ODBCINSTINI=$(TEST_ODBCINSTINI) \ TEST_DRIVER=$(TEST_DRIVER) TEST_DSN=$(TEST_DSN) TEST_UID=$(TEST_UID) \ TEST_PASSWORD=$(TEST_PASSWORD) TEST_SOCKET=$(TEST_SOCKET) \ $(VALGRIND) ./$$f ; \ echo "Ending test $(TEST_PREFIX)$$f" ; \ done ; \ echo "" ; \ echo "******************************************************************************" EXTRA_DIST= \ odbc.ini.in \ odbcinst.ini.in \ odbctap.h \ unit.pl \ CMakeLists.txt \ cmake/generateinifiles.cmake # We want to rebuild the "test.ini" file each time, as it might need an # update from overriding variables on the command line .PHONY: make-test-ini make-test-ini: sed \ -e 's!@''TEST_DSN''@!$(TEST_DSN)!g' \ -e 's!@''TEST_DRIVER''@!$(TEST_DRIVER)!g' \ -e 's!@''TEST_DATABASE''@!$(TEST_DATABASE)!g' \ -e 's!@''TEST_SERVER''@!$(TEST_SERVER)!g' \ -e 's!@''TEST_UID''@!$(TEST_UID)!g' \ -e 's!@''TEST_PASSWORD''@!$(TEST_PASSWORD)!g' \ -e 's!@''TEST_SOCKET''@!$(TEST_SOCKET)!g' \ odbc.ini.in > $(TEST_ODBCINI) sed \ -e 's!@''TEST_DSN''@!$(TEST_DSN)!g' \ -e 's!@''TEST_DRIVER''@!$(TEST_DRIVER)!g' \ -e 's!@''TEST_DATABASE''@!$(TEST_DATABASE)!g' \ -e 's!@''TEST_SERVER''@!$(TEST_SERVER)!g' \ -e 's!@''TEST_UID''@!$(TEST_UID)!g' \ -e 's!@''TEST_PASSWORD''@!$(TEST_PASSWORD)!g' \ -e 's!@''TEST_SOCKET''@!$(TEST_SOCKET)!g' \ odbcinst.ini.in > $(TEST_ODBCINSTINI) mysql-connector-odbc-5.1.10-src/test/my_param.c100644 15766 12 77633 11707541005 20156 0ustar00cteamstaff/* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" /******************************************************** * initialize tables * *********************************************************/ DECLARE_TEST(my_init_table) { SQLRETURN rc; ok_sql(hstmt, "DROP TABLE if exists my_demo_param"); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* create the table 'my_demo_param' */ ok_sql(hstmt, "CREATE TABLE my_demo_param(\ id int,\ auto int primary key auto_increment,\ name varchar(20),\ timestamp timestamp)"); return OK; } DECLARE_TEST(my_param_insert) { SQLRETURN rc; SQLINTEGER id; char name[50]; /* prepare the insert statement with parameters */ rc = SQLPrepare(hstmt, (SQLCHAR *)"INSERT INTO my_demo_param(id,name) VALUES(?,?)",SQL_NTS); mystmt(hstmt,rc); /* now supply data to parameter 1 and 2 */ rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0,0, &id, 0, NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0,0, name, sizeof(name), NULL); mystmt(hstmt,rc); /* now insert 10 rows of data */ for (id = 0; id < 10; id++) { sprintf(name,"MySQL%d",id); rc = SQLExecute(hstmt); mystmt(hstmt,rc); } /* Free statement param resorces */ rc = SQLFreeStmt(hstmt, SQL_RESET_PARAMS); mystmt(hstmt,rc); /* Free statement cursor resorces */ rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* Now fetch and verify the data */ ok_sql(hstmt, "SELECT * FROM my_demo_param"); is(10 == myresult(hstmt)); return OK; } DECLARE_TEST(my_param_update) { SQLRETURN rc; SQLLEN nRowCount; SQLINTEGER id=9; char name[]="update"; /* prepare the insert statement with parameters */ rc = SQLPrepare(hstmt, (SQLCHAR *)"UPDATE my_demo_param set name = ? WHERE id = ?",SQL_NTS); mystmt(hstmt,rc); /* now supply data to parameter 1 and 2 */ rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0,0, name, sizeof(name), NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0,0, &id, 0, NULL); mystmt(hstmt,rc); /* now execute the update statement */ rc = SQLExecute(hstmt); mystmt(hstmt,rc); /* check the rows affected by the update statement */ rc = SQLRowCount(hstmt, &nRowCount); mystmt(hstmt,rc); printMessage("\n total rows updated:%d\n",nRowCount); is( nRowCount == 1); /* Free statement param resorces */ rc = SQLFreeStmt(hstmt, SQL_RESET_PARAMS); mystmt(hstmt,rc); /* Free statement cursor resorces */ rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* Now fetch and verify the data */ ok_sql(hstmt, "SELECT * FROM my_demo_param"); mystmt(hstmt,rc); is(10 == myresult(hstmt)); return OK; } DECLARE_TEST(my_param_delete) { SQLRETURN rc; SQLINTEGER id; SQLLEN nRowCount; /* supply data to parameter 1 */ rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0,0, &id, 0, NULL); mystmt(hstmt,rc); /* execute the DELETE STATEMENT to delete 5th row */ id = 5; ok_sql(hstmt,"DELETE FROM my_demo_param WHERE id = ?"); /* check the rows affected by the update statement */ rc = SQLRowCount(hstmt, &nRowCount); mystmt(hstmt,rc); printMessage(" total rows deleted:%d\n",nRowCount); is( nRowCount == 1); SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_CLOSE); /* execute the DELETE STATEMENT to delete 8th row */ rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0,0, &id, 0, NULL); mystmt(hstmt,rc); id = 8; ok_sql(hstmt,"DELETE FROM my_demo_param WHERE id = ?"); /* check the rows affected by the update statement */ rc = SQLRowCount(hstmt, &nRowCount); mystmt(hstmt,rc); printMessage(" total rows deleted:%d\n",nRowCount); is( nRowCount == 1); /* Free statement param resorces */ rc = SQLFreeStmt(hstmt, SQL_RESET_PARAMS); mystmt(hstmt,rc); /* Free statement cursor resorces */ rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* Now fetch and verify the data */ ok_sql(hstmt, "SELECT * FROM my_demo_param"); is(8 == myresult(hstmt)); /* drop the table */ ok_sql(hstmt,"DROP TABLE my_demo_param"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); return OK; } DECLARE_TEST(tmysql_fix) { SQLRETURN rc; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_err"); ok_sql(hstmt,"CREATE TABLE tmysql_err (\ td date NOT NULL default '0000-00-00',\ node varchar(8) NOT NULL default '',\ tag varchar(10) NOT NULL default '',\ sqlname varchar(8) default NULL,\ fix_err varchar(100) default NULL,\ sql_err varchar(255) default NULL,\ prog_err varchar(100) default NULL\ ) ENGINE=MyISAM"); ok_sql(hstmt,"INSERT INTO tmysql_err VALUES\ ('0000-00-00','0','0','0','0','0','0'),\ ('2001-08-29','FIX','SQLT2','ins1',\ NULL,NULL, 'Error. SQL cmd %s is not terminated or too long.'),\ ('0000-00-00','0','0','0','0','0','0'),('2001-08-29','FIX','SQLT2',\ 'ins1',NULL,NULL,'Error. SQL cmd %s is not terminated or too long.'),\ ('0000-00-00','0','0','0','0','0','0'),('2001-08-29','FIX','SQLT2',\ 'ins1',NULL,NULL,'Error. SQL cmd %s is not terminated or too long.'),\ ('0000-00-00','0','0','0','0','0','0'),('2001-08-29','FIX','SQLT2','ins1',\ NULL,NULL,'Error. SQL cmd %s is not terminated or too long.'),\ ('0000-00-00','0','0','0','0','0','0'),('2001-08-29','FIX','SQLT2',\ 'ins1',NULL,NULL,'Error. SQL cmd %s is not terminated or too long.'),\ ('0000-00-00','0','0','0','0','0','0'),('2001-08-29','FIX','SQLT2',\ 'ins1',NULL,NULL,'Error. SQL cmd %s is not terminated or too long.'),\ ('0000-00-00','0','0','0','0','0','0'),('2001-08-29','FIX','SQLT2',\ 'ins1',NULL,NULL,'Error. SQL cmd %s is not terminated or too long.'),\ ('0000!-00-00','0','0','0','0','0','0'),('2001-08-29','FIX','SQLT2',\ 'ins1',NULL,NULL,'Error. SQL cmd %s is not terminated or too long.'),\ ('0000-00-00','0','0','0','0','0','0'),('2001-08-29','FIX','SQLT2',\ 'ins1',NULL,NULL,'Error. SQL cmd %s is not terminated or too long.')"); /* trace based */ { SQLSMALLINT pcpar,pccol,pfSqlType,pibScale,pfNullable; SQLSMALLINT index; SQLCHAR td[30]="20010830163225"; SQLCHAR node[30]="FIX"; SQLCHAR tag[30]="SQLT2"; SQLCHAR sqlname[30]="ins1"; SQLCHAR sqlerr[30]="error"; SQLCHAR fixerr[30]= "fixerr"; SQLCHAR progerr[30]="progerr"; SQLULEN pcbParamDef; SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLPrepare(hstmt, (SQLCHAR *)"insert into tmysql_err (TD, NODE, TAG, SQLNAME, SQL_ERR, FIX_ERR, PROG_ERR)\ values (?, ?, ?, ?, ?, ?, ?)",200); mystmt(hstmt,rc); rc = SQLNumParams(hstmt,&pcpar); mystmt(hstmt,rc); rc = SQLNumResultCols(hstmt,&pccol); mystmt(hstmt,rc); for (index=1; index <= pcpar; index++) { rc = SQLDescribeParam(hstmt,index,&pfSqlType,&pcbParamDef,&pibScale,&pfNullable); mystmt(hstmt,rc); printMessage("descparam[%d]:%d,%d,%d,%d\n",index,pfSqlType,pcbParamDef,pibScale,pfNullable); } rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,11,12,0,0,td,100,0); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT,1,12,0,0,node,100,0); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,3,SQL_PARAM_INPUT,1,12,0,0,tag,100,0); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,4,SQL_PARAM_INPUT,1,12,0,0,sqlname,100,0); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,5,SQL_PARAM_INPUT,1,12,0,0,sqlerr,0,0); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,6,SQL_PARAM_INPUT,1,12,0,0,fixerr,0,0); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,7,SQL_PARAM_INPUT,1,12,0,0,progerr,0,0); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); } ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_err"); return OK; } /* Test basic handling of SQL_ATTR_PARAM_BIND_OFFSET_PTR */ DECLARE_TEST(t_param_offset) { const SQLINTEGER rowcnt= 5; SQLINTEGER i; struct { SQLINTEGER id; SQLINTEGER x; } rows[25]; size_t row_size= (sizeof(rows) / 25); SQLINTEGER out_id, out_x; SQLULEN bind_offset= 20 * row_size; ok_sql(hstmt, "DROP TABLE IF EXISTS t_param_offset"); ok_sql(hstmt, "CREATE TABLE t_param_offset (id int not null, x int)"); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, &bind_offset, 0)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &rows[0].id, 0, NULL)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &rows[0].x, 0, NULL)); for (i= 0; i < rowcnt; ++i) { rows[20+i].id= i * 10; rows[20+i].x= (i * 1000) % 97; ok_sql(hstmt, "insert into t_param_offset values (?,?)"); bind_offset+= row_size; } /* verify the data */ ok_sql(hstmt, "select id, x from t_param_offset order by 1"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &out_id, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_LONG, &out_x, 0, NULL)); for (i= 0; i < rowcnt; ++i) { ok_stmt(hstmt, SQLFetch(hstmt)); is_num(out_id, rows[20+i].id); is_num(out_id, i * 10); is_num(out_x, rows[20+i].x); is_num(out_x, (i * 1000) % 97); } return OK; } /* Bug 48310 - parameters array support request. Binding by row test */ DECLARE_TEST(paramarray_by_row) { #define ROWS_TO_INSERT 3 #define STR_FIELD_LENGTH 255 typedef struct DataBinding { SQLCHAR bData[5]; SQLINTEGER intField; SQLCHAR strField[STR_FIELD_LENGTH]; SQLLEN indBin; SQLLEN indInt; SQLLEN indStr; } DATA_BINDING; const SQLCHAR *str[]= {"nothing for 1st", "longest string for row 2", "shortest" }; SQLCHAR buff[50]; DATA_BINDING dataBinding[ROWS_TO_INSERT]; SQLUSMALLINT paramStatusArray[ROWS_TO_INSERT]; SQLULEN paramsProcessed, i, nLen; SQLLEN rowsCount; ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS t_bug48310", SQL_NTS)); ok_stmt(hstmt, SQLExecDirect(hstmt, "CREATE TABLE t_bug48310 (id int primary key auto_increment,"\ "bData binary(5) NULL, intField int not null, strField varchar(255) not null)", SQL_NTS)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER)sizeof(DATA_BINDING), 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)ROWS_TO_INSERT, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, paramStatusArray, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, ¶msProcessed, 0)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, 0, 0, dataBinding[0].bData, 0, &dataBinding[0].indBin)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &dataBinding[0].intField, 0, &dataBinding[0].indInt)); ok_stmt(hstmt, SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, dataBinding[0].strField, 0, &dataBinding[0].indStr )); memcpy(dataBinding[0].bData, "\x01\x80\x00\x80\x00", 5); dataBinding[0].intField= 1; memcpy(dataBinding[1].bData, "\x02\x80\x00\x80", 4); dataBinding[1].intField= 0; memcpy(dataBinding[2].bData, "\x03\x80\x00", 3); dataBinding[2].intField= 223322; for (i= 0; i < ROWS_TO_INSERT; ++i) { strcpy(dataBinding[i].strField, str[i]); dataBinding[i].indBin= 5 - i; dataBinding[i].indInt= 0; dataBinding[i].indStr= SQL_NTS; } /* We don't expect errors in paramsets processing, thus we should get SQL_SUCCESS only*/ expect_stmt(hstmt, SQLExecDirect(hstmt, "INSERT INTO t_bug48310 (bData, intField, strField) " \ "VALUES (?,?,?)", SQL_NTS), SQL_SUCCESS); is_num(paramsProcessed, ROWS_TO_INSERT); ok_stmt(hstmt, SQLRowCount(hstmt, &rowsCount)); is_num(rowsCount, ROWS_TO_INSERT); for (i= 0; i < paramsProcessed; ++i) if ( paramStatusArray[i] != SQL_PARAM_SUCCESS && paramStatusArray[i] != SQL_PARAM_SUCCESS_WITH_INFO ) { printMessage("Parameter #%u status isn't successful(0x%X)", i+1, paramStatusArray[i]); return FAIL; } ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)1, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, NULL, 0)); ok_stmt(hstmt, SQLExecDirect(hstmt, "SELECT bData, intField, strField\ FROM t_bug48310\ ORDER BY id", SQL_NTS)); /* Just to make sure RowCount isn't broken */ ok_stmt(hstmt, SQLRowCount(hstmt, &rowsCount)); is_num(rowsCount, ROWS_TO_INSERT); for (i= 0; i < paramsProcessed; ++i) { ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_BINARY, (SQLPOINTER)buff, 50, &nLen)); is(memcmp((const void*) buff, (const void*)dataBinding[i].bData, 5 - i)==0); is_num(my_fetch_int(hstmt, 2), dataBinding[i].intField); is_str(my_fetch_str(hstmt, buff, 3), dataBinding[i].strField, strlen(str[i])); } expect_stmt(hstmt,SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* One more check that RowCount isn't broken. check may get broken if input data changes */ ok_sql(hstmt, "update t_bug48310 set strField='changed' where intField > 1"); ok_stmt(hstmt, SQLRowCount(hstmt, &rowsCount)); is_num(rowsCount, 1); /* Clean-up */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS bug48310", SQL_NTS)); return OK; #undef ROWS_TO_INSERT #undef STR_FIELD_LENGTH } /* Bug 48310 - parameters array support request. Binding by column test */ DECLARE_TEST(paramarray_by_column) { #define ROWS_TO_INSERT 3 #define STR_FIELD_LENGTH 5 SQLCHAR buff[50]; SQLCHAR bData[ROWS_TO_INSERT][STR_FIELD_LENGTH]={{0x01, 0x80, 0x00, 0x80, 0x03}, {0x02, 0x80, 0x00, 0x02}, {0x03, 0x80, 0x01}}; SQLLEN bInd[ROWS_TO_INSERT]= {5,4,3}; const SQLCHAR strField[ROWS_TO_INSERT][STR_FIELD_LENGTH]= {{'\0'}, {'x','\0'}, {'x','x','x','\0'} }; SQLLEN strInd[ROWS_TO_INSERT]= {SQL_NTS, SQL_NTS, SQL_NTS}; SQLINTEGER intField[ROWS_TO_INSERT] = {123321, 1, 0}; SQLLEN intInd[ROWS_TO_INSERT]= {5,4,3}; SQLUSMALLINT paramStatusArray[ROWS_TO_INSERT]; SQLULEN paramsProcessed, i, nLen; ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS t_bug48310", SQL_NTS)); ok_stmt(hstmt, SQLExecDirect(hstmt, "CREATE TABLE t_bug48310 (id int primary key auto_increment,"\ "bData binary(5) NULL, intField int not null, strField varchar(255) not null)", SQL_NTS)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)ROWS_TO_INSERT, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, paramStatusArray, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, ¶msProcessed, 0)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, 0, 0, bData, 5, bInd)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intField, 0, intInd)); ok_stmt(hstmt, SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, (SQLPOINTER)strField, 5, strInd )); /* We don't expect errors in paramsets processing, thus we should get SQL_SUCCESS only*/ expect_stmt(hstmt, SQLExecDirect(hstmt, "INSERT INTO t_bug48310 (bData, intField, strField) " \ "VALUES (?,?,?)", SQL_NTS), SQL_SUCCESS); is_num(paramsProcessed, ROWS_TO_INSERT); for (i= 0; i < paramsProcessed; ++i) if ( paramStatusArray[i] != SQL_PARAM_SUCCESS && paramStatusArray[i] != SQL_PARAM_SUCCESS_WITH_INFO ) { printMessage("Parameter #%u status isn't successful(0x%X)", i+1, paramStatusArray[i]); return FAIL; } ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)1, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, NULL, 0)); ok_stmt(hstmt, SQLExecDirect(hstmt, "SELECT bData, intField, strField\ FROM t_bug48310\ ORDER BY id", SQL_NTS)); for (i= 0; i < paramsProcessed; ++i) { ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_BINARY, (SQLPOINTER)buff, 50, &nLen)); if (memcmp((const void*) buff, bData[i], 5 - i)!=0) { printMessage("Bin data inserted wrongly. Read: 0x%02X%02X%02X%02X%02X Had to be: 0x%02X%02X%02X%02X%02X" , buff[0], buff[1], buff[2], buff[3], buff[4] , bData[i][0], bData[i][1], bData[i][2], bData[i][3], bData[i][4]); return FAIL; } is_num(my_fetch_int(hstmt, 2), intField[i]); is_str(my_fetch_str(hstmt, buff, 3), strField[i], strlen(strField[i])); } /* Clean-up */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS bug48310", SQL_NTS)); return OK; #undef ROWS_TO_INSERT #undef STR_FIELD_LENGTH } /* Bug 48310 - parameters array support request. Ignore paramset test */ DECLARE_TEST(paramarray_ignore_paramset) { #define ROWS_TO_INSERT 4 #define STR_FIELD_LENGTH 5 SQLCHAR buff[50]; SQLCHAR bData[ROWS_TO_INSERT][STR_FIELD_LENGTH]={{0x01, 0x80, 0x00, 0x80, 0x03}, {0x02, 0x80, 0x00, 0x02}, {0x03, 0x80, 0x01}}; SQLLEN bInd[ROWS_TO_INSERT]= {5,4,3}; const SQLCHAR strField[ROWS_TO_INSERT][STR_FIELD_LENGTH]= {{'\0'}, {'x','\0'}, {'x','x','x','\0'} }; SQLLEN strInd[ROWS_TO_INSERT]= {SQL_NTS, SQL_NTS, SQL_NTS}; SQLINTEGER intField[ROWS_TO_INSERT] = {123321, 1, 0}; SQLLEN intInd[ROWS_TO_INSERT]= {5,4,3}; SQLUSMALLINT paramOperationArr[ROWS_TO_INSERT]={0,SQL_PARAM_IGNORE,0,SQL_PARAM_IGNORE}; SQLUSMALLINT paramStatusArr[ROWS_TO_INSERT]; SQLULEN paramsProcessed, i, nLen, rowsInserted= 0; ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS t_bug48310", SQL_NTS)); ok_stmt(hstmt, SQLExecDirect(hstmt, "CREATE TABLE t_bug48310 (id int primary key auto_increment,"\ "bData binary(5) NULL, intField int not null, strField varchar(255) not null)", SQL_NTS)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)ROWS_TO_INSERT, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, paramStatusArr, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_OPERATION_PTR, paramOperationArr, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, ¶msProcessed, 0)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, 0, 0, bData, 5, bInd)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intField, 0, intInd)); ok_stmt(hstmt, SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, (SQLPOINTER)strField, 5, strInd )); /* We don't expect errors in paramsets processing, thus we should get SQL_SUCCESS only*/ expect_stmt(hstmt, SQLExecDirect(hstmt, "INSERT INTO t_bug48310 (bData, intField, strField) " \ "VALUES (?,?,?)", SQL_NTS), SQL_SUCCESS); is_num(paramsProcessed, ROWS_TO_INSERT); for (i= 0; i < paramsProcessed; ++i) { if (paramOperationArr[i] == SQL_PARAM_IGNORE) { is_num(paramStatusArr[i], SQL_PARAM_UNUSED); } else if ( paramStatusArr[i] != SQL_PARAM_SUCCESS && paramStatusArr[i] != SQL_PARAM_SUCCESS_WITH_INFO ) { printMessage("Parameter #%u status isn't successful(0x%X)", i+1, paramStatusArr[i]); return FAIL; } } /* Resetting statements attributes */ ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)1, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, NULL, 0)); ok_stmt(hstmt, SQLExecDirect(hstmt, "SELECT bData, intField, strField\ FROM t_bug48310\ ORDER BY id", SQL_NTS)); i= 0; while(i < paramsProcessed) { if (paramStatusArr[i] == SQL_PARAM_UNUSED) { ++i; continue; } ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_BINARY, (SQLPOINTER)buff, 50, &nLen)); if (memcmp((const void*) buff, bData[i], 5 - i)!=0) { printMessage("Bin data inserted wrongly. Read: 0x%02X%02X%02X%02X%02X Had to be: 0x%02X%02X%02X%02X%02X" , buff[0], buff[1], buff[2], buff[3], buff[4] , bData[i][0], bData[i][1], bData[i][2], bData[i][3], bData[i][4]); return FAIL; } is_num(my_fetch_int(hstmt, 2), intField[i]); is_str(my_fetch_str(hstmt, buff, 3), strField[i], strlen(strField[i])); ++rowsInserted; ++i; } /* Making sure that there is nothing else to fetch ... */ expect_stmt(hstmt,SQLFetch(hstmt), SQL_NO_DATA_FOUND); /* ... and that inserted was less than SQL_ATTR_PARAMSET_SIZE rows */ is( rowsInserted < ROWS_TO_INSERT); /* Clean-up */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS bug48310", SQL_NTS)); return OK; #undef ROWS_TO_INSERT #undef STR_FIELD_LENGTH } /* Bug 48310 - parameters array support request. Select statement. */ DECLARE_TEST(paramarray_select) { #define STMTS_TO_EXEC 3 SQLINTEGER intField[STMTS_TO_EXEC] = {3, 1, 2}; SQLLEN intInd[STMTS_TO_EXEC]= {5,4,3}; SQLUSMALLINT paramStatusArray[STMTS_TO_EXEC]; SQLULEN paramsProcessed, i; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)STMTS_TO_EXEC, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, paramStatusArray, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, ¶msProcessed, 0)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intField, 0, intInd)); /* We don't expect errors in paramsets processing, thus we should get SQL_SUCCESS only*/ expect_stmt(hstmt, SQLExecDirect(hstmt, "SELect ?,'So what'", SQL_NTS), SQL_SUCCESS); is_num(paramsProcessed, STMTS_TO_EXEC); for (i= 0; i < paramsProcessed; ++i) { if ( paramStatusArray[i] != SQL_PARAM_SUCCESS && paramStatusArray[i] != SQL_PARAM_SUCCESS_WITH_INFO ) { printMessage("Parameter #%u status isn't successful(0x%X)", i+1, paramStatusArray[i]); return FAIL; } ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), intField[i]); } /* Clean-up */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; #undef STMTS_TO_EXEC } /* Bug #49029 - Server with sql mode NO_BACKSLASHES_ESCAPE obviously can work incorrectly (at least) with binary parameters */ DECLARE_TEST(t_bug49029) { const SQLCHAR bData[6]= "\x01\x80\x00\x80\x01"; SQLCHAR buff[6]; SQLULEN len= 5; ok_stmt(hstmt, SQLExecDirect(hstmt, "set @@session.sql_mode='NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_BACKSLASH_ESCAPES'", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, 0, 0, (SQLPOINTER)bData, 0, &len)); ok_stmt(hstmt, SQLExecDirect(hstmt, "select ?", SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_BINARY, (SQLPOINTER)buff, 6, &len)); is(memcmp((const void*) buff, (const void*)bData, 5)==0); return OK; } /* Bug #56804 - Server with sql mode NO_BACKSLASHES_ESCAPE obviously can work incorrectly (at least) with binary parameters */ DECLARE_TEST(t_bug56804) { #define PARAMSET_SIZE 10 SQLINTEGER len = 1; int i; SQLINTEGER c1[PARAMSET_SIZE]= {0, 1, 2, 3, 4, 5, 1, 7, 8, 9}; SQLINTEGER c2[PARAMSET_SIZE]= {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; SQLLEN d1[PARAMSET_SIZE]= {4, 4, 4, 4, 4, 4, 4, 4, 4, 4}; SQLLEN d2[PARAMSET_SIZE]= {4, 4, 4, 4, 4, 4, 4, 4, 4, 4}; SQLUSMALLINT status[PARAMSET_SIZE]= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; SQLSMALLINT paramset_size = PARAMSET_SIZE; ok_sql(hstmt, "DROP TABLE IF EXISTS bug56804"); ok_sql(hstmt, "create table bug56804 (c1 int primary key not null, c2 int)"); ok_sql(hstmt, "insert into bug56804 values( 1, 1 ), (9, 9009)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into bug56804 values( ?,? )", SQL_NTS)); ok_stmt(hstmt, SQLSetStmtAttr( hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)paramset_size, SQL_IS_UINTEGER )); ok_stmt(hstmt, SQLSetStmtAttr( hstmt, SQL_ATTR_PARAM_STATUS_PTR, status, SQL_IS_POINTER )); ok_stmt(hstmt, SQLBindParameter( hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_DECIMAL, 4, 0, c1, 4, d1)); ok_stmt(hstmt, SQLBindParameter( hstmt, 2, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_DECIMAL, 4, 0, c2, 4, d2)); expect_stmt(hstmt, SQLExecute(hstmt), SQL_SUCCESS_WITH_INFO); /* Following tests are here to ensure that driver works how it is currently expected to work, and they need to be changed if driver changes smth in the way how it reports errors in paramsets and diagnostics */ for(i = 0; i < PARAMSET_SIZE; ++i ) { printMessage("Paramset #%d (%d, %d)", i, c1[i], c2[i]); switch (i) { case 1: case 6: /* all errors but last have SQL_PARAM_DIAG_UNAVAILABLE */ is_num(status[i], SQL_PARAM_DIAG_UNAVAILABLE); break; case 9: /* Last error - we are supposed to get SQL_PARAM_ERROR for it */ is_num(status[i], SQL_PARAM_ERROR); break; default: is_num(status[i], SQL_PARAM_SUCCESS); } } { SQLCHAR sqlstate[6]= {0}; SQLCHAR message[255]= {0}; SQLINTEGER native_err= 0; SQLSMALLINT msglen= 0; i= 0; while(SQL_SUCCEEDED(SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, ++i, sqlstate, &native_err, message, sizeof(message), &msglen))) { printMessage("%d) [%s] %s %d", i, sqlstate, message, native_err); } /* just to make sure we got 1 diagnostics record ... */ is_num(i, 2); /* ... and what the record is for the last error */ is(strstr(message, "Duplicate entry '9'")); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS bug56804"); return OK; #undef PARAMSET_SIZE } /* Bug 59772 - Column parameter binding makes SQLExecute not to return SQL_ERROR on disconnect */ DECLARE_TEST(t_bug59772) { #define ROWS_TO_INSERT 3 SQLRETURN rc; SQLCHAR buf_kill[50]; SQLINTEGER intField[ROWS_TO_INSERT] = {123321, 1, 0}; SQLLEN intInd[ROWS_TO_INSERT]= {5,4,3}; SQLUSMALLINT paramStatusArray[ROWS_TO_INSERT]; SQLULEN paramsProcessed, i; SQLINTEGER connection_id; SQLHENV henv2; SQLHDBC hdbc2; SQLHSTMT hstmt2; int overall_result= OK; /* Create a new connection that we deliberately will kill */ alloc_basic_handles(&henv2, &hdbc2, &hstmt2); ok_sql(hstmt2, "SELECT connection_id()"); ok_stmt(hstmt2, SQLFetch(hstmt2)); connection_id= my_fetch_int(hstmt2, 1); ok_stmt(hstmt2, SQLFreeStmt(hstmt2, SQL_CLOSE)); ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS t_bug59772", SQL_NTS)); ok_stmt(hstmt, SQLExecDirect(hstmt, "CREATE TABLE t_bug59772 (id int primary key auto_increment,"\ "intField int)", SQL_NTS)); ok_stmt(hstmt2, SQLSetStmtAttr(hstmt2, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0)); ok_stmt(hstmt2, SQLSetStmtAttr(hstmt2, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)ROWS_TO_INSERT, 0)); ok_stmt(hstmt2, SQLSetStmtAttr(hstmt2, SQL_ATTR_PARAM_STATUS_PTR, paramStatusArray, 0)); ok_stmt(hstmt2, SQLSetStmtAttr(hstmt2, SQL_ATTR_PARAMS_PROCESSED_PTR, ¶msProcessed, 0)); ok_stmt(hstmt2, SQLPrepare(hstmt2, "INSERT INTO t_bug59772 (intField) VALUES (?)", SQL_NTS)); ok_stmt(hstmt2, SQLBindParameter(hstmt2, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intField, 0, intInd)); /* From another connection, kill the connection created above */ sprintf(buf_kill, "KILL %d", connection_id); ok_stmt(hstmt, SQLExecDirect(hstmt, (SQLCHAR *)buf_kill, SQL_NTS)); rc= SQLExecute(hstmt2); /* The result should be SQL_ERROR */ if (rc != SQL_ERROR) overall_result= FAIL; for (i= 0; i < paramsProcessed; ++i) /* We expect error statuses for all parameters */ if ( paramStatusArray[i] != ((i + 1 < ROWS_TO_INSERT) ? SQL_PARAM_DIAG_UNAVAILABLE : SQL_PARAM_ERROR) ) { printMessage("Parameter #%u status isn't successful(0x%X)", i+1, paramStatusArray[i]); overall_result= FAIL; } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS t_bug59772", SQL_NTS)); SQLFreeHandle(SQL_HANDLE_STMT, hstmt2); SQLDisconnect(hdbc2); SQLFreeHandle(SQL_HANDLE_DBC, hdbc2); SQLFreeHandle(SQL_HANDLE_ENV, henv2); return overall_result; #undef ROWS_TO_INSERT } BEGIN_TESTS ADD_TEST(my_init_table) ADD_TEST(my_param_insert) ADD_TEST(my_param_update) ADD_TEST(my_param_delete) ADD_TEST(tmysql_fix) ADD_TEST(t_param_offset) ADD_TEST(paramarray_by_row) ADD_TEST(paramarray_by_column) ADD_TEST(paramarray_ignore_paramset) ADD_TEST(paramarray_select) ADD_TEST(t_bug49029) ADD_TEST(t_bug56804) ADD_TEST(t_bug59772) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/odbcinst.ini.in100644 15766 12 433 11707541005 21040 0ustar00cteamstaff[ODBC Drivers] MyODBC = Installed @TEST_DRIVER@ = Installed [MyODBC] Driver = @TEST_DRIVER@ Description = Driver for connecting to MySQL database server Threading = 0 [@TEST_DRIVER@] Driver = @TEST_DRIVER@ Description = Driver for connecting to MySQL database server Threading = 0 mysql-connector-odbc-5.1.10-src/test/my_info.c100644 15766 12 30017 11707541005 17772 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" DECLARE_TEST(t_gettypeinfo) { SQLSMALLINT pccol; ok_stmt(hstmt, SQLGetTypeInfo(hstmt, SQL_ALL_TYPES)); ok_stmt(hstmt, SQLNumResultCols(hstmt, &pccol)); is_num(pccol, 19); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; } DECLARE_TEST(sqlgetinfo) { SQLCHAR rgbValue[100]; SQLSMALLINT pcbInfo; ok_con(hdbc, SQLGetInfo(hdbc, SQL_DRIVER_ODBC_VER, rgbValue, sizeof(rgbValue), &pcbInfo)); is_num(pcbInfo, 5); is_str(rgbValue, "03.51", 5); return OK; } DECLARE_TEST(t_stmt_attr_status) { SQLUSMALLINT rowStatusPtr[3]; SQLULEN rowsFetchedPtr; ok_sql(hstmt, "DROP TABLE IF EXISTS t_stmtstatus"); ok_sql(hstmt, "CREATE TABLE t_stmtstatus (id INT, name CHAR(20))"); ok_sql(hstmt, "INSERT INTO t_stmtstatus VALUES (10,'data1'),(20,'data2')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER)SQL_NONSCROLLABLE, 0)); ok_sql(hstmt, "SELECT * FROM t_stmtstatus"); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowsFetchedPtr, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, rowStatusPtr, 0)); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 2), SQL_ERROR); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER)SQL_SCROLLABLE, 0)); ok_sql(hstmt, "SELECT * FROM t_stmtstatus"); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 2)); is_num(rowsFetchedPtr, 1); is_num(rowStatusPtr[0], 0); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, NULL, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, NULL, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_stmtstatus"); return OK; } DECLARE_TEST(t_msdev_bug) { SQLCHAR catalog[30]; SQLINTEGER len; ok_con(hdbc, SQLGetConnectOption(hdbc, SQL_CURRENT_QUALIFIER, catalog)); is_str(catalog, "test", 4); ok_con(hdbc, SQLGetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, catalog, sizeof(catalog), &len)); is_num(len, 4); is_str(catalog, "test", 4); return OK; } /** Bug #28657: ODBC Connector returns FALSE on SQLGetTypeInfo with DATETIME (wxWindows latest) */ DECLARE_TEST(t_bug28657) { #ifdef WIN32 /* The Microsoft Windows ODBC driver manager automatically maps a request for SQL_DATETIME to SQL_TYPE_DATE, which means our little workaround to get all of the SQL_DATETIME types at once does not work on there. */ skip("test doesn't work with Microsoft Windows ODBC driver manager"); #else ok_stmt(hstmt, SQLGetTypeInfo(hstmt, SQL_DATETIME)); is(myresult(hstmt) > 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; #endif } DECLARE_TEST(t_bug14639) { SQLINTEGER connection_id; SQLUINTEGER is_dead; char buf[100]; SQLHENV henv2; SQLHDBC hdbc2; SQLHSTMT hstmt2; /* Create a new connection that we deliberately will kill */ alloc_basic_handles(&henv2, &hdbc2, &hstmt2); ok_sql(hstmt2, "SELECT connection_id()"); ok_stmt(hstmt2, SQLFetch(hstmt2)); connection_id= my_fetch_int(hstmt2, 1); ok_stmt(hstmt2, SQLFreeStmt(hstmt2, SQL_CLOSE)); /* Check that connection is alive */ ok_con(hdbc2, SQLGetConnectAttr(hdbc2, SQL_ATTR_CONNECTION_DEAD, &is_dead, sizeof(is_dead), 0)); is_num(is_dead, SQL_CD_FALSE) /* From another connection, kill the connection created above */ sprintf(buf, "KILL %d", connection_id); ok_stmt(hstmt, SQLExecDirect(hstmt, (SQLCHAR *)buf, SQL_NTS)); /* Now check that the connection killed returns the right state */ ok_con(hdbc, SQLGetConnectAttr(hdbc2, SQL_ATTR_CONNECTION_DEAD, &is_dead, sizeof(is_dead), 0)); is_num(is_dead, SQL_CD_TRUE) return OK; } /** Bug #31055: Uninitiated memory returned by SQLGetFunctions() with SQL_API_ODBC3_ALL_FUNCTION */ DECLARE_TEST(t_bug31055) { SQLUSMALLINT funcs[SQL_API_ODBC3_ALL_FUNCTIONS_SIZE]; /* The DM will presumably return true for all functions that it can satisfy in place of the driver. This test will only work when linked directly to the driver. */ if (using_dm(hdbc)) return OK; memset(funcs, 0xff, sizeof(SQLUSMALLINT) * SQL_API_ODBC3_ALL_FUNCTIONS_SIZE); ok_con(hdbc, SQLGetFunctions(hdbc, SQL_API_ODBC3_ALL_FUNCTIONS, funcs)); is_num(SQL_FUNC_EXISTS(funcs, SQL_API_SQLALLOCHANDLESTD), 0); return OK; } /* Bug 3780, reading or setting ADODB.Connection.DefaultDatabase is not supported */ DECLARE_TEST(t_bug3780) { HDBC hdbc1; HSTMT hstmt1; SQLCHAR conn[256], conn_out[256]; SQLSMALLINT conn_out_len; SQLCHAR rgbValue[MAX_NAME_LEN]; SQLSMALLINT pcbInfo; SQLINTEGER attrlen; /* The connection string must not include DATABASE. */ sprintf((char *)conn, "DRIVER=%s;SERVER=%s;UID=%s;PASSWORD=%s", mydriver, myserver, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn, pbuff); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, sizeof(conn), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_con(hdbc1, SQLGetInfo(hdbc1, SQL_DATABASE_NAME, rgbValue, MAX_NAME_LEN, &pcbInfo)); is_num(pcbInfo, 4); is_str(rgbValue, "null", pcbInfo); ok_con(hdbc1, SQLGetConnectAttr(hdbc1, SQL_ATTR_CURRENT_CATALOG, rgbValue, MAX_NAME_LEN, &attrlen)); is_num(attrlen, 4); is_str(rgbValue, "null", attrlen); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); return OK; } /* Bug#16653 MyODBC 3 / truncated UID when performing Data Import in MS Excel */ DECLARE_TEST(t_bug16653) { SQLHANDLE hdbc1; SQLCHAR buf[50]; /* Driver managers handle SQLGetConnectAttr before connection in inconsistent ways. */ if (using_dm(hdbc)) return OK; ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); /* this would cause a crash if we arent connected */ ok_con(hdbc1, SQLGetConnectAttr(hdbc1, SQL_ATTR_CURRENT_CATALOG, buf, 50, NULL)); is_str(buf, "null", 1); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); return OK; } /* Bug #30626 - No result record for SQLGetTypeInfo for TIMESTAMP */ DECLARE_TEST(t_bug30626) { SQLHANDLE henv1; SQLHANDLE hdbc1; SQLHANDLE hstmt1; SQLCHAR conn[256]; /* odbc 3 */ ok_stmt(hstmt, SQLGetTypeInfo(hstmt, SQL_TYPE_TIMESTAMP)); is_num(myresult(hstmt), 2); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLGetTypeInfo(hstmt, SQL_TYPE_TIME)); is_num(myresult(hstmt), 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLGetTypeInfo(hstmt, SQL_TYPE_DATE)); is_num(myresult(hstmt), 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* odbc 2 */ sprintf((char *)conn, "DRIVER=%s;SERVER=%s;UID=%s;PASSWORD=%s", mydriver, myserver, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn, pbuff); } ok_env(henv1, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv1)); ok_env(henv1, SQLSetEnvAttr(henv1, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC2, SQL_IS_INTEGER)); ok_env(henv1, SQLAllocHandle(SQL_HANDLE_DBC, henv1, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, (SQLSMALLINT)strlen(conn), NULL, 0, NULL, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLGetTypeInfo(hstmt1, SQL_TIMESTAMP)); is_num(myresult(hstmt1), 2); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLGetTypeInfo(hstmt1, SQL_TIME)); is_num(myresult(hstmt1), 1); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLGetTypeInfo(hstmt1, SQL_DATE)); is_num(myresult(hstmt1), 4); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLFreeHandle(SQL_HANDLE_STMT, hstmt1)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); ok_env(henv1, SQLFreeHandle(SQL_HANDLE_ENV, henv1)); return OK; } /* Bug #43855 - conversion flags not complete */ DECLARE_TEST(t_bug43855) { SQLUINTEGER convFlags; SQLSMALLINT pcbInfo; /* TODO: add other convert checks, we are only interested in CHAR now */ ok_con(hdbc, SQLGetInfo(hdbc, SQL_CONVERT_CHAR, &convFlags, sizeof(convFlags), &pcbInfo)); is_num(pcbInfo, 4); is((convFlags & SQL_CVT_CHAR) && (convFlags & SQL_CVT_NUMERIC) && (convFlags & SQL_CVT_DECIMAL) && (convFlags & SQL_CVT_INTEGER) && (convFlags & SQL_CVT_SMALLINT) && (convFlags & SQL_CVT_FLOAT) && (convFlags & SQL_CVT_REAL) && (convFlags & SQL_CVT_DOUBLE) && (convFlags & SQL_CVT_VARCHAR) && (convFlags & SQL_CVT_LONGVARCHAR) && (convFlags & SQL_CVT_BIT) && (convFlags & SQL_CVT_TINYINT) && (convFlags & SQL_CVT_BIGINT) && (convFlags & SQL_CVT_DATE) && (convFlags & SQL_CVT_TIME) && (convFlags & SQL_CVT_TIMESTAMP) && (convFlags & SQL_CVT_WCHAR) && (convFlags &SQL_CVT_WVARCHAR) && (convFlags & SQL_CVT_WLONGVARCHAR)); return OK; } /* Bug#46910 MyODBC 5 - calling SQLGetConnectAttr before getting all results of "CALL ..." statement */ DECLARE_TEST(t_bug46910) { SQLCHAR catalog[30]; SQLINTEGER len, i; SQLCHAR * initStmt[]= {"DROP PROCEDURE IF EXISTS `spbug46910_1`", "CREATE PROCEDURE `spbug46910_1`()\ BEGIN\ SELECT 1 AS ret;\ END"}; SQLCHAR * cleanupStmt= "DROP PROCEDURE IF EXISTS `spbug46910_1`;"; for (i= 0; i < 2; ++i) ok_stmt(hstmt, SQLExecDirect(hstmt, initStmt[i], SQL_NTS)); SQLExecDirect(hstmt, "CALL spbug46910_1()", SQL_NTS); /* SQLRowCount(hstmt, &i); SQLNumResultCols(hstmt, &i);*/ SQLGetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, catalog, sizeof(catalog), &len); //is_num(len, 4); //is_str(catalog, "test", 4); /*ok_con(hdbc, */ SQLGetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, catalog, sizeof(catalog), &len); ok_stmt(hstmt, SQLExecDirect(hstmt, cleanupStmt, SQL_NTS)); return OK; } BEGIN_TESTS ADD_TEST(sqlgetinfo) ADD_TEST(t_gettypeinfo) ADD_TEST(t_stmt_attr_status) ADD_TEST(t_msdev_bug) ADD_TEST(t_bug28657) ADD_TEST(t_bug14639) ADD_TEST(t_bug31055) ADD_TEST(t_bug3780) ADD_TEST(t_bug16653) ADD_TEST(t_bug30626) ADD_TEST(t_bug43855) ADD_TEST(t_bug46910) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_unixodbc.c100644 15766 12 10572 11707541005 20656 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" DECLARE_TEST(t_odbc3_envattr) { SQLRETURN rc; SQLHENV henv1; SQLHDBC hdbc1; SQLINTEGER ov_version; rc = SQLAllocEnv(&henv1); myenv(henv1,rc); rc = SQLGetEnvAttr(henv1,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)&ov_version,0,0); myenv(henv1,rc); printMessage("default odbc version:%d\n",ov_version); is_num(ov_version, SQL_OV_ODBC2); rc = SQLSetEnvAttr(henv1,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,0); myenv(henv1,rc); rc = SQLAllocConnect(henv1,&hdbc1); myenv(henv1,rc); rc = SQLFreeConnect(hdbc1); mycon(hdbc1,rc); rc = SQLSetEnvAttr(henv1,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,0); myenv(henv1,rc); rc = SQLGetEnvAttr(henv1,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)&ov_version,0,0); myenv(henv1,rc); printMessage("new odbc version:%d\n",ov_version); my_assert(ov_version == SQL_OV_ODBC3); rc = SQLFreeEnv(henv1); myenv(henv1,rc); return OK; } DECLARE_TEST(t_odbc3_handle) { SQLRETURN rc; SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1; SQLINTEGER ov_version; rc = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&henv1); myenv(henv1,rc); /* Verify that we get an error trying to allocate a connection handle before we've set SQL_ATTR_ODBC_VERSION. */ rc = SQLAllocHandle(SQL_HANDLE_DBC,henv1,&hdbc1); myenv_err(henv1,rc == SQL_ERROR,rc); rc = SQLSetEnvAttr(henv1,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,0); myenv(henv1,rc); rc = SQLGetEnvAttr(henv1,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)&ov_version,0,0); myenv(henv1,rc); my_assert(ov_version == SQL_OV_ODBC3); rc = SQLAllocHandle(SQL_HANDLE_DBC,henv1,&hdbc1); myenv(henv1,rc); rc = SQLConnect(hdbc1, mydsn, SQL_NTS, myuid, SQL_NTS, mypwd, SQL_NTS); mycon(hdbc1,rc); rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc1,&hstmt1); mycon(hdbc1, rc); rc = SQLDisconnect(hdbc1); mycon(hdbc1, rc); rc = SQLFreeHandle(SQL_HANDLE_DBC,hdbc1); mycon(hdbc1,rc); rc = SQLFreeHandle(SQL_HANDLE_ENV,henv1); myenv(henv1,rc); return OK; } DECLARE_TEST(t_driver_connect) { SQLHENV henv1; SQLHDBC hdbc1; SQLCHAR conn_in[255], conn_out[255]; SQLSMALLINT conn_out_len; ok_env(henv1, SQLAllocEnv(&henv1)); ok_env(henv1, SQLAllocConnect(henv1,&hdbc1)); sprintf((char *)conn_in, "DRIVER=%s;UID=%s;PWD=%s;DATABASE=%s;SERVER=%s;" "OPTION=3;STMT=use mysql", (char *)mydriver, (char *)myuid, (char *)mypwd, (char *)mydb, (char *)myserver); if (mysock != NULL) { strcat((char *)conn_in, ";SOCKET="); strcat((char *)conn_in, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn_in, pbuff); } ok_con(hdbc1, SQLDriverConnect(hdbc1, (SQLHWND)0, conn_in, sizeof(conn_in), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); printMessage( "output string: `%s`\n", conn_out); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); ok_env(henv1, SQLFreeEnv(henv1)); return OK; } BEGIN_TESTS ADD_TEST(t_odbc3_envattr) #ifndef NO_DRIVERMANAGER ADD_TEST(t_odbc3_handle) #endif ADD_TEST(t_driver_connect) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_unicode.c100644 15766 12 111332 11707541005 20505 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "odbctap.h" #include DECLARE_TEST(sqlconnect) { HDBC hdbc1; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlprepare) { HDBC hdbc1; HSTMT hstmt1; SQLINTEGER data; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLPrepareW(hstmt1, W(L"SELECT '\x30a1' FROM DUAL WHERE 1 = ?"), SQL_NTS)); ok_stmt(hstmt1, SQLBindParameter(hstmt1, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &data, 0, NULL)); data= 0; ok_stmt(hstmt1, SQLExecute(hstmt1)); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); data= 1; ok_stmt(hstmt1, SQLExecute(hstmt1)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_wstr(my_fetch_wstr(hstmt1, wbuff, 1), L"\x30a1", 1); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); /* Some driver managers (like iODBC) will always do the character conversion themselves. */ if (!using_dm(hdbc)) { ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); /* Now try ANSI SQLPrepare. */ ok_stmt(hstmt1, SQLPrepare(hstmt1, (SQLCHAR *)"SELECT '\xe3' FROM DUAL WHERE 1 = ?", SQL_NTS)); data= 0; ok_stmt(hstmt1, SQLExecute(hstmt1)); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); data= 1; ok_stmt(hstmt1, SQLExecute(hstmt1)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_wstr(my_fetch_wstr(hstmt1, wbuff, 1), L"\x00e3", 1); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); } ok_stmt(hstmt, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } /** Test calling SQLPrepareW() on an ANSI connection. Only tested when we're not using a driver manager, otherwise the driver manager does Unicode to ANSI translations. */ DECLARE_TEST(sqlprepare_ansi) { SQLINTEGER data; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; if (using_dm(hdbc)) skip("not relevant when using driver manager"); /* Now try SQLPrepareW with an ANSI connection. */ ok_stmt(hstmt, SQLPrepareW(hstmt, W(L"SELECT '\x00e3' FROM DUAL WHERE 1 = ?"), SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &data, 0, NULL)); data= 0; ok_stmt(hstmt, SQLExecute(hstmt)); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); data= 1; ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); is_wstr(my_fetch_wstr(hstmt, wbuff, 1), L"\x00e3", 1); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Now try SQLPrepareW with a character that doesn't translate. */ expect_stmt(hstmt, SQLPrepareW(hstmt, W(L"SELECT '\x30a1' FROM DUAL WHERE 1 = ?"), SQL_NTS), SQL_ERROR); is(check_sqlstate(hstmt, "22018") == OK); return OK; } DECLARE_TEST(sqlchar) { HDBC hdbc1; HSTMT hstmt1; SQLCHAR data[]= "S\xe3o Paolo", buff[30]; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; wchar_t wcdata[]= L"S\x00e3o Paolo"; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLPrepareW(hstmt1, W(L"SELECT ? FROM DUAL"), SQL_NTS)); ok_stmt(hstmt1, SQLBindParameter(hstmt1, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_WVARCHAR, 0, 0, data, sizeof(data), NULL)); ok_stmt(hstmt1, SQLExecute(hstmt1)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_str(my_fetch_str(hstmt1, buff, 1), data, sizeof(data)); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); /* Do it again so we can try as SQLWCHAR */ ok_stmt(hstmt1, SQLExecute(hstmt1)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_wstr(my_fetch_wstr(hstmt1, wbuff, 1), wcdata, 9); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqldriverconnect) { HDBC hdbc1; HSTMT hstmt1; wchar_t conn_in[512]; wchar_t dummy[256]; SQLWCHAR conn_out[1024]; SQLSMALLINT conn_out_len; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); *conn_in= L'\0'; wcscat(conn_in, L"DRIVER="); mbstowcs(dummy, (char *)mydriver, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); wcscat(conn_in, L";UID="); mbstowcs(dummy, (char *)myuid, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); wcscat(conn_in, L";PWD="); mbstowcs(dummy, (char *)mypwd, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); wcscat(conn_in, L";DATABASE="); mbstowcs(dummy, (char *)mydb, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); wcscat(conn_in, L";SERVER="); mbstowcs(dummy, (char *)myserver, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); if (mysock != NULL) { wcscat(conn_in, L";SOCKET="); mbstowcs(dummy, (char *)mysock, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); mbstowcs(dummy, (char *)pbuff, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); } ok_con(hdbc1, SQLDriverConnectW(hdbc1, NULL, WL(conn_in, wcslen(conn_in)), wcslen(conn_in), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"SELECT 1234"), SQL_NTS)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_num(my_fetch_int(hstmt1, 1), 1234); ok_stmt(hstmt, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlnativesql) { HDBC hdbc1; SQLWCHAR out[128]; wchar_t in[]= L"SELECT * FROM venu"; SQLINTEGER len; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLNativeSqlW(hdbc1, W(in), SQL_NTS, out, sizeof(out), &len)); is_num(len, sizeof(in) / sizeof(wchar_t) - 1); is_wstr(sqlwchar_to_wchar_t(out), in, sizeof(in) / sizeof(wchar_t) - 1); ok_con(hdbc1, SQLNativeSqlW(hdbc1, W(in), SQL_NTS, out, 8, &len)); is_num(len, sizeof(in) / sizeof(wchar_t) - 1); is_wstr(sqlwchar_to_wchar_t(out), in, 7); is(out[7] == 0); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlsetcursorname) { HDBC hdbc1; HSTMT hstmt1, hstmt_pos; SQLLEN nRowCount; SQLCHAR data[10]; ok_sql(hstmt, "DROP TABLE IF EXISTS my_demo_cursor"); ok_sql(hstmt, "CREATE TABLE my_demo_cursor (id INT, name VARCHAR(20))"); ok_sql(hstmt, "INSERT INTO my_demo_cursor VALUES (0,'MySQL0'),(1,'MySQL1')," "(2,'MySQL2'),(3,'MySQL3'),(4,'MySQL4')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLSetStmtAttrW(hstmt1, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0)); ok_stmt(hstmt1, SQLSetCursorNameW(hstmt1, W(L"a\x00e3b"), SQL_NTS)); /* Open the resultset of table 'my_demo_cursor' */ ok_sql(hstmt1, "SELECT * FROM my_demo_cursor"); /* goto the last row */ ok_stmt(hstmt1, SQLFetchScroll(hstmt1, SQL_FETCH_LAST, 1L)); /* create new statement handle */ ok_con(hdbc1, SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt_pos)); /* now update the name field to 'updated' using positioned cursor */ ok_stmt(hstmt_pos, SQLExecDirectW(hstmt_pos, W(L"UPDATE my_demo_cursor SET name='updated' " L"WHERE CURRENT OF a\x00e3b"), SQL_NTS)); ok_stmt(hstmt_pos, SQLRowCount(hstmt_pos, &nRowCount)); is_num(nRowCount, 1); ok_stmt(hstmt_pos, SQLFreeStmt(hstmt_pos, SQL_CLOSE)); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); /* Now delete 2nd row */ ok_sql(hstmt1, "SELECT * FROM my_demo_cursor"); /* goto the second row */ ok_stmt(hstmt1, SQLFetchScroll(hstmt1, SQL_FETCH_ABSOLUTE, 2L)); /* now delete the current row */ ok_stmt(hstmt_pos, SQLExecDirectW(hstmt_pos, W(L"DELETE FROM my_demo_cursor " L"WHERE CURRENT OF a\x00e3b"), SQL_NTS)); ok_stmt(hstmt_pos, SQLRowCount(hstmt_pos, &nRowCount)); is_num(nRowCount, 1); /* free the statement cursor */ ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); /* Free the statement 'hstmt_pos' */ ok_stmt(hstmt_pos, SQLFreeHandle(SQL_HANDLE_STMT, hstmt_pos)); /* Now fetch and verify the data */ ok_sql(hstmt1, "SELECT * FROM my_demo_cursor"); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_num(my_fetch_int(hstmt1, 1), 0); is_str(my_fetch_str(hstmt1, data, 2), "MySQL0", 6); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_num(my_fetch_int(hstmt1, 1), 2); is_str(my_fetch_str(hstmt1, data, 2), "MySQL2", 6); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_num(my_fetch_int(hstmt1, 1), 3); is_str(my_fetch_str(hstmt1, data, 2), "MySQL3", 6); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_num(my_fetch_int(hstmt1, 1), 4); is_str(my_fetch_str(hstmt1, data, 2), "updated", 7); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS my_demo_cursor"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlgetcursorname) { SQLRETURN rc; HDBC hdbc1; SQLHSTMT hstmt1,hstmt2,hstmt3; SQLWCHAR curname[50]; SQLSMALLINT nlen; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt2)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt3)); rc= SQLGetCursorNameW(hstmt1, curname, 50, &nlen); if (SQL_SUCCEEDED(rc)) { is_num(nlen, 8); is_wstr(sqlwchar_to_wchar_t(curname), L"SQL_CUR0", 8); ok_stmt(hstmt3, SQLGetCursorNameW(hstmt3, curname, 50, &nlen)); expect_stmt(hstmt3, SQLGetCursorNameW(hstmt1, curname, 4, &nlen), SQL_SUCCESS_WITH_INFO); is_num(nlen, 8); is_wstr(sqlwchar_to_wchar_t(curname), L"SQL", 4); expect_stmt(hstmt3, SQLGetCursorNameW(hstmt1, curname, 0, &nlen), SQL_SUCCESS_WITH_INFO); rc = SQLGetCursorNameW(hstmt1, curname, 0, &nlen); mystmt_err(hstmt1,rc == SQL_SUCCESS_WITH_INFO, rc); is_num(nlen, 8); expect_stmt(hstmt1, SQLGetCursorNameW(hstmt1, curname, 8, &nlen), SQL_SUCCESS_WITH_INFO); is_num(nlen, 8); is_wstr(sqlwchar_to_wchar_t(curname), L"SQL_CUR", 8); ok_stmt(hstmt1, SQLGetCursorNameW(hstmt1, curname, 9, &nlen)); is_num(nlen, 8); is_wstr(sqlwchar_to_wchar_t(curname), L"SQL_CUR0", 8); } ok_stmt(hstmt1, SQLSetCursorNameW(hstmt1, W(L"venucur123"), 7)); ok_stmt(hstmt1, SQLGetCursorNameW(hstmt1, curname, 8, &nlen)); is_num(nlen, 7); is_wstr(sqlwchar_to_wchar_t(curname), L"venucur", 8); ok_stmt(hstmt3, SQLFreeStmt(hstmt3, SQL_DROP)); ok_stmt(hstmt2, SQLFreeStmt(hstmt2, SQL_DROP)); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlcolattribute) { HDBC hdbc1; HSTMT hstmt1; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; SQLSMALLINT len; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_colattrib"); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"CREATE TABLE t_colattrib (a\x00e3g INT)"), SQL_NTS)); ok_sql(hstmt1, "SELECT * FROM t_colattrib AS b"); ok_stmt(hstmt1, SQLColAttributeW(hstmt1, 1, SQL_DESC_NAME, wbuff, sizeof(wbuff), &len, NULL)); is_num(len, 3 * sizeof(SQLWCHAR)); is_wstr(sqlwchar_to_wchar_t(wbuff), L"a\x00e3g", 4); expect_stmt(hstmt1, SQLColAttributeW(hstmt1, 1, SQL_DESC_BASE_TABLE_NAME, wbuff, 5 * sizeof(SQLWCHAR), &len, NULL), SQL_SUCCESS_WITH_INFO); is_num(len, 11 * sizeof(SQLWCHAR)); is_wstr(sqlwchar_to_wchar_t(wbuff), L"t_co", 5); ok_stmt(hstmt1, SQLColAttributeW(hstmt1, 1, SQL_DESC_TYPE_NAME, wbuff, sizeof(wbuff), &len, NULL)); is_num(len, 7 * sizeof(SQLWCHAR)); is_wstr(sqlwchar_to_wchar_t(wbuff), L"integer", 8); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_colattrib"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqldescribecol) { HDBC hdbc1; HSTMT hstmt1; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; SQLSMALLINT len; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_desc"); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"CREATE TABLE t_desc (a\x00e3g INT)"), SQL_NTS)); ok_sql(hstmt1, "SELECT * FROM t_desc"); ok_stmt(hstmt1, SQLDescribeColW(hstmt1, 1, wbuff, sizeof(wbuff) / sizeof(wbuff[0]), &len, NULL, NULL, NULL, NULL)); is_num(len, 3); is_wstr(sqlwchar_to_wchar_t(wbuff), L"a\x00e3g", 4); expect_stmt(hstmt1, SQLDescribeColW(hstmt1, 1, wbuff, 3, &len, NULL, NULL, NULL, NULL), SQL_SUCCESS_WITH_INFO); is_num(len, 3); is_wstr(sqlwchar_to_wchar_t(wbuff), L"a\x00e3", 3); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_desc"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlgetconnectattr) { HDBC hdbc1; HSTMT hstmt1; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; SQLINTEGER len; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLGetConnectAttrW(hdbc1, SQL_ATTR_CURRENT_CATALOG, wbuff, sizeof(wbuff), &len)); is_num(len, 4 * sizeof(SQLWCHAR)); is_wstr(sqlwchar_to_wchar_t(wbuff), L"test", 5); expect_stmt(hstmt1, SQLGetConnectAttrW(hdbc1, SQL_ATTR_CURRENT_CATALOG, wbuff, 3 * sizeof(SQLWCHAR), &len), SQL_SUCCESS_WITH_INFO); is_num(len, 4 * sizeof(SQLWCHAR)); is_wstr(sqlwchar_to_wchar_t(wbuff), L"te", 3); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlgetdiagrec) { SQLWCHAR sqlstate[6]= {0}; SQLWCHAR message[255]= {0}; SQLINTEGER native_err= 0; SQLSMALLINT msglen= 0; HDBC hdbc1; HSTMT hstmt1; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); expect_sql(hstmt1, "DROP TABLE t_odbc3_non_existent_table", SQL_ERROR); #if UNIXODBC_BUG_FIXED /* This should report no data found, but unixODBC doesn't even pass this down to the driver. */ expect_stmt(hstmt1, SQLGetDiagRecW(SQL_HANDLE_STMT, hstmt1, 2, sqlstate, &native_err, message, 0, &msglen), SQL_NO_DATA_FOUND); #endif ok_stmt(hstmt, SQLGetDiagRecW(SQL_HANDLE_STMT, hstmt1, 1, sqlstate, &native_err, message, 255, &msglen)); expect_stmt(hstmt1, SQLGetDiagRecW(SQL_HANDLE_STMT, hstmt1, 1, sqlstate, &native_err, message, 0, &msglen), SQL_SUCCESS_WITH_INFO); expect_stmt(hstmt1, SQLGetDiagRecW(SQL_HANDLE_STMT, hstmt1, 1, sqlstate, &native_err, message, 10, &msglen), SQL_SUCCESS_WITH_INFO); expect_stmt(hstmt1, SQLGetDiagRecW(SQL_HANDLE_STMT, hstmt1, 1, sqlstate, &native_err, message, -1, &msglen), SQL_ERROR); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlgetdiagfield) { SQLWCHAR message[255]= {0}; SQLSMALLINT len; SQLINTEGER data; HDBC hdbc1; HSTMT hstmt1; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); expect_sql(hstmt1, "DROP TABLE t_odbc3_non_existent_table", SQL_ERROR); ok_stmt(hstmt, SQLGetDiagFieldW(SQL_HANDLE_STMT, hstmt1, 0, SQL_DIAG_NUMBER, &data, 0, NULL)); is_num(data, 1); ok_stmt(hstmt, SQLGetDiagFieldW(SQL_HANDLE_STMT, hstmt1, 1, SQL_DIAG_CLASS_ORIGIN, message, sizeof(message), &len)); is_num(len, 8 * sizeof(SQLWCHAR)); is_wstr(sqlwchar_to_wchar_t(message), L"ISO 9075", 9); expect_stmt(hstmt, SQLGetDiagFieldW(SQL_HANDLE_STMT, hstmt1, 1, SQL_DIAG_SQLSTATE, message, 4 * sizeof(SQLWCHAR), &len), SQL_SUCCESS_WITH_INFO); is_num(len, 5 * sizeof(SQLWCHAR)); is_wstr(sqlwchar_to_wchar_t(message), L"42S", 4); ok_stmt(hstmt, SQLGetDiagFieldW(SQL_HANDLE_STMT, hstmt1, 1, SQL_DIAG_SUBCLASS_ORIGIN, message, sizeof(message), &len)); is_num(len, 8 * sizeof(SQLWCHAR)); is_wstr(sqlwchar_to_wchar_t(message), L"ODBC 3.0", 9); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlcolumns) { HDBC hdbc1; HSTMT hstmt1; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_columns"); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"CREATE TABLE t_columns (a\x00e3g INT)"), SQL_NTS)); ok_stmt(hstmt1, SQLColumnsW(hstmt1, NULL, 0, NULL, 0, W(L"t_columns"), SQL_NTS, W(L"a\x00e3g"), SQL_NTS)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_wstr(my_fetch_wstr(hstmt1, wbuff, 4), L"a\x00e3g", 4); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_columns"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqltables) { HDBC hdbc1; HSTMT hstmt1; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"DROP TABLE IF EXISTS t_a\x00e3g"), SQL_NTS)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"CREATE TABLE t_a\x00e3g (a INT)"), SQL_NTS)); ok_stmt(hstmt1, SQLTablesW(hstmt1, NULL, 0, NULL, 0, W(L"t_a\x00e3g"), SQL_NTS, NULL, 0)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_wstr(my_fetch_wstr(hstmt1, wbuff, 3), L"t_a\x00e3g", 6); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"DROP TABLE IF EXISTS t_a\x00e3g"), SQL_NTS)); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlspecialcolumns) { HDBC hdbc1; HSTMT hstmt1; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_spec"); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"CREATE TABLE t_spec (a\x00e3g INT PRIMARY KEY)"), SQL_NTS)); ok_stmt(hstmt1, SQLSpecialColumnsW(hstmt1, SQL_BEST_ROWID, NULL, 0, NULL, 0, W(L"t_spec"), SQL_NTS, SQL_SCOPE_SESSION, SQL_NULLABLE)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_wstr(my_fetch_wstr(hstmt1, wbuff, 2), L"a\x00e3g", 4); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_spec"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlforeignkeys) { HDBC hdbc1; HSTMT hstmt1; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; /** @todo re-enable this test when I_S based SQLForeignKeys is done. */ if (mysql_min_version(hdbc, "5.1", 3)) skip("can't test foreign keys with 5.1 or later yet"); ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"DROP TABLE IF EXISTS t_fk_\x00e5, t_fk_\x00e3"), SQL_NTS)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"CREATE TABLE t_fk_\x00e3 (a INT PRIMARY KEY) " L"ENGINE=InnoDB"), SQL_NTS)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"CREATE TABLE t_fk_\x00e5 (b INT, parent_id INT," L" FOREIGN KEY (parent_id)" L" REFERENCES" L" t_fk_\x00e3(a)" L" ON DELETE SET NULL)" L" ENGINE=InnoDB"), SQL_NTS)); ok_stmt(hstmt1, SQLForeignKeysW(hstmt1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, W(L"t_fk_\x00e5"), SQL_NTS)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_wstr(my_fetch_wstr(hstmt1, wbuff, 3), L"t_fk_\x00e3", 7); is_wstr(my_fetch_wstr(hstmt1, wbuff, 4), L"a", 2); is_wstr(my_fetch_wstr(hstmt1, wbuff, 7), L"t_fk_\x00e5", 7); is_wstr(my_fetch_wstr(hstmt1, wbuff, 8), L"parent_id", 10); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"DROP TABLE IF EXISTS t_fk_\x00e5, t_fk_\x00e3"), SQL_NTS)); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlprimarykeys) { HDBC hdbc1; HSTMT hstmt1; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"DROP TABLE IF EXISTS t_a\x00e3g"), SQL_NTS)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"CREATE TABLE t_a\x00e3g (a INT PRIMARY KEY)"), SQL_NTS)); ok_stmt(hstmt1, SQLPrimaryKeysW(hstmt1, NULL, 0, NULL, 0, W(L"t_a\x00e3g"), SQL_NTS)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_wstr(my_fetch_wstr(hstmt1, wbuff, 3), L"t_a\x00e3g", 6); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"DROP TABLE IF EXISTS t_a\x00e3g"), SQL_NTS)); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } DECLARE_TEST(sqlstatistics) { HDBC hdbc1; HSTMT hstmt1; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"DROP TABLE IF EXISTS t_a\x00e3g"), SQL_NTS)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"CREATE TABLE t_a\x00e3g (a INT PRIMARY KEY)"), SQL_NTS)); ok_stmt(hstmt1, SQLStatisticsW(hstmt1, NULL, 0, NULL, 0, W(L"t_a\x00e3g"), SQL_NTS, SQL_INDEX_UNIQUE, SQL_QUICK)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_wstr(my_fetch_wstr(hstmt1, wbuff, 3), L"t_a\x00e3g", 6); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"DROP TABLE IF EXISTS t_a\x00e3g"), SQL_NTS)); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } /** Bug #32161: SQLDescribeColW returns UTF-8 column as SQL_VARCHAR instead of SQL_WVARCHAR */ DECLARE_TEST(t_bug32161) { HDBC hdbc1; HSTMT hstmt1; SQLWCHAR wbuff[MAX_ROW_DATA_LEN+1]; SQLSMALLINT nlen; SQLSMALLINT ctype; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnectW(hdbc1, WC(mydsn), SQL_NTS, WC(myuid), SQL_NTS, WC(mypwd), SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_bug32161"); ok_sql(hstmt1, "CREATE TABLE t_bug32161 (" "col1 varchar(32)," "col2 char(32)," "col3 tinytext," "col4 mediumtext," "col5 text," "col6 longtext" ") DEFAULT CHARSET=utf8"); /* Greek word PSARO - FISH */ ok_stmt(hstmt1, SQLExecDirectW(hstmt1, W(L"INSERT INTO t_bug32161 VALUES (" L"\"\x03A8\x0391\x03A1\x039F 1\"," L"\"\x03A8\x0391\x03A1\x039F 2\"," L"\"\x03A8\x0391\x03A1\x039F 3\"," L"\"\x03A8\x0391\x03A1\x039F 4\"," L"\"\x03A8\x0391\x03A1\x039F 5\"," L"\"\x03A8\x0391\x03A1\x039F 6\")"), SQL_NTS)); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "SELECT * FROM t_bug32161"); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_wstr(my_fetch_wstr(hstmt1, wbuff, 1), L"\x03A8\x0391\x03A1\x039F 1", 4); ok_stmt(hstmt1, SQLDescribeColW(hstmt1, 1, wbuff, MAX_ROW_DATA_LEN, &nlen, &ctype, NULL, NULL, NULL)); is_num(ctype, SQL_WVARCHAR); is_wstr(my_fetch_wstr(hstmt1, wbuff, 2), L"\x03A8\x0391\x03A1\x039F 2", 4); ok_stmt(hstmt1, SQLDescribeColW(hstmt1, 2, wbuff, MAX_ROW_DATA_LEN, &nlen, &ctype, NULL, NULL, NULL)); is_num(ctype, SQL_WCHAR); /* All further calls of SQLDescribeColW should return SQL_WLONGVARCHAR */ is_wstr(my_fetch_wstr(hstmt1, wbuff, 3), L"\x03A8\x0391\x03A1\x039F 3", 4); ok_stmt(hstmt1, SQLDescribeColW(hstmt1, 3, wbuff, MAX_ROW_DATA_LEN, &nlen, &ctype, NULL, NULL, NULL)); is_num(ctype, SQL_WLONGVARCHAR); is_wstr(my_fetch_wstr(hstmt1, wbuff, 4), L"\x03A8\x0391\x03A1\x039F 4", 4); ok_stmt(hstmt1, SQLDescribeColW(hstmt1, 4, wbuff, MAX_ROW_DATA_LEN, &nlen, &ctype, NULL, NULL, NULL)); is_num(ctype, SQL_WLONGVARCHAR); is_wstr(my_fetch_wstr(hstmt1, wbuff, 5), L"\x03A8\x0391\x03A1\x039F 5", 4); ok_stmt(hstmt1, SQLDescribeColW(hstmt1, 5, wbuff, MAX_ROW_DATA_LEN, &nlen, &ctype, NULL, NULL, NULL)); is_num(ctype, SQL_WLONGVARCHAR); is_wstr(my_fetch_wstr(hstmt1, wbuff, 6), L"\x03A8\x0391\x03A1\x039F 6", 4); ok_stmt(hstmt1, SQLDescribeColW(hstmt1, 6, wbuff, MAX_ROW_DATA_LEN, &nlen, &ctype, NULL, NULL, NULL)); is_num(ctype, SQL_WLONGVARCHAR); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_bug32161"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } /* Bug#34672 - Unable to insert surrogate pairs into or fetch surrogate pairs from unicode column */ DECLARE_TEST(t_bug34672) { SQLWCHAR chars[3]; SQLINTEGER inchars, i; SQLWCHAR result[3]; SQLLEN reslen; if (sizeof(SQLWCHAR) == 2) { chars[0]= 0xd802; chars[1]= 0xdc60; inchars= 2; } else { chars[0]= (SQLWCHAR) 0x10860; inchars= 1; } ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, 0, 0, chars, inchars * sizeof(SQLWCHAR), NULL)); if (mysql_min_version(hdbc, "6.0.4", 5)) { ok_stmt(hstmt, SQLExecDirect(hstmt, (SQLCHAR *) "select ?", SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, result, sizeof(result), &reslen)); is_num(result[2], 0); for (i= 0; i < inchars; ++i) is_num(result[i], chars[i]); is_num(reslen, inchars * sizeof(SQLWCHAR)); } else { expect_stmt(hstmt, SQLExecDirect(hstmt, (SQLCHAR *) "select ?", SQL_NTS), SQL_ERROR); return check_sqlstate(hstmt, "HY000"); } return OK; } /* Bug#28168 odbc, non 7-bit password, connection failed */ DECLARE_TEST(t_bug28168) { SQLHANDLE hdbc1; wchar_t conn_in[512]= {0}; wchar_t dummy[256]= {0}; wchar_t *wstr; SQLWCHAR errmsgtxt[256]= {0}; SQLWCHAR *grantQuery= W(L"GRANT ALL ON t_bug28168 to " L"'\x03A8\x0391\x03A1\x039F uid'@" L"localhost identified by " L"'\x03A8\x0391\x03A1\x039F pwd'"); SQLSMALLINT errmsglen; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug28168"); ok_sql(hstmt, "CREATE TABLE t_bug28168 (x int)"); if (!SQL_SUCCEEDED(SQLExecDirectW(hstmt, grantQuery, SQL_NTS))) { free(grantQuery); return FAIL; } free(grantQuery); ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); *conn_in= L'\0'; wcscat(conn_in, L"DRIVER="); mbstowcs(dummy, (char *)mydriver, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); wcscat(conn_in, L";UID="); wcscat(conn_in, L"{\x03A8\x0391\x03A1\x039F uid}"); wcscat(conn_in, L";PWD="); wcscat(conn_in, L"{\x03A8\x0391\x03A1\x039F pwd}"); wcscat(conn_in, L";DATABASE="); mbstowcs(dummy, (char *)mydb, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); wcscat(conn_in, L";SERVER="); mbstowcs(dummy, (char *)myserver, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); if (mysock != NULL) { wcscat(conn_in, L";SOCKET="); mbstowcs(dummy, (char *)mysock, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); mbstowcs(dummy, (char *)pbuff, sizeof(dummy)/sizeof(wchar_t)); wcscat(conn_in, dummy); } ok_con(hdbc1, SQLDriverConnectW(hdbc1, NULL, W(conn_in), SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLDisconnect(hdbc1)); /* we change the password in the connection string to test the error msg */ wstr= wcsstr(conn_in, L" pwd}") - 4; *wstr++= 'x'; expect_dbc(hdbc1, SQLDriverConnectW(hdbc1, NULL, W(conn_in), SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT), SQL_ERROR); ok_con(hdbc1, SQLGetDiagFieldW(SQL_HANDLE_DBC, hdbc1, 1, SQL_DIAG_MESSAGE_TEXT, errmsgtxt, 256 * sizeof(SQLWCHAR), &errmsglen)); swprintf(dummy, 256, L"[MySQL][ODBC 5.1 Driver]Access denied for user " L"'\x03A8\x0391\x03A1\x039F uid'@'localhost' " L"(using password: YES)"); if (sizeof(SQLWCHAR) == sizeof(wchar_t)) { wprintMessage(L"expected msg: %ls\n", dummy); wprintMessage(L"actual msg: %ls\n", errmsgtxt); } is(!memcmp(errmsgtxt, W(dummy), wcslen(dummy) * sizeof(SQLWCHAR))); is_num(errmsglen, wcslen(dummy) * sizeof(SQLWCHAR)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); ok_stmt(hstmt, SQLExecDirectW(hstmt, W(L"DROP USER " L"'\x03A8\x0391\x03A1\x039F uid'@localhost"), SQL_NTS)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug28168"); return OK; /* There is error after this test on freeing environment - function sequence error. looks like an error in the DM since all connections are freed. */ } BEGIN_TESTS ADD_TEST(sqlconnect) ADD_TEST(sqlprepare) ADD_TEST(sqlprepare_ansi) ADD_TEST(sqlchar) ADD_TEST(sqldriverconnect) ADD_TEST(sqlnativesql) ADD_TEST(sqlsetcursorname) ADD_TEST(sqlgetcursorname) ADD_TEST(sqlcolattribute) ADD_TEST(sqldescribecol) ADD_TEST(sqlgetconnectattr) ADD_TEST(sqlgetdiagrec) ADD_TEST(sqlgetdiagfield) ADD_TEST(sqlcolumns) ADD_TEST(sqltables) ADD_TEST(sqlspecialcolumns) ADD_TEST(sqlforeignkeys) ADD_TEST(sqlprimarykeys) ADD_TEST(sqlstatistics) ADD_TEST(t_bug32161) ADD_TEST(t_bug34672) ADD_TEST(t_bug28168) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_prepare.c100644 15766 12 66200 11707541005 20500 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" SQLRETURN rc; /* Basic prepared statements - binary protocol test */ DECLARE_TEST(t_prep_basic) { SQLINTEGER id; SQLLEN length1, length2, pcrow; char name[20]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_basic"); ok_sql(hstmt, "create table t_prep_basic(a int, b char(4))"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_prep_basic values(?,'venu')", SQL_NTS)); rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &id, 0, NULL); mystmt(hstmt,rc); id = 100; rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLRowCount(hstmt, &pcrow); mystmt(hstmt,rc); printMessage( "affected rows: %ld\n", pcrow); myassert(pcrow == 1); SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "select * from t_prep_basic"); rc = SQLBindCol(hstmt, 1, SQL_C_LONG, &id, 0, &length1); mystmt(hstmt,rc); rc = SQLBindCol(hstmt, 2, SQL_C_CHAR, name, 5, &length2); mystmt(hstmt,rc); id = 0; rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage( "outdata: %d(%d), %s(%d)\n",id,length1,name,length2); myassert(id == 100 && length1 == sizeof(SQLINTEGER)); myassert(strcmp(name,"venu")==0 && length2 == 4); rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA_FOUND); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_basic"); return OK; } /* to test buffer length */ DECLARE_TEST(t_prep_buffer_length) { SQLLEN length; SQLCHAR buffer[20]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_buffer_length"); ok_sql(hstmt, "create table t_prep_buffer_length(a varchar(20))"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_prep_buffer_length values(?)", SQL_NTS)); length= 0; strcpy((char *)buffer, "abcdefghij"); rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 15, 10, buffer, 4, &length); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); length= 3; rc = SQLExecute(hstmt); mystmt(hstmt,rc); length= 10; rc = SQLExecute(hstmt); mystmt(hstmt,rc); length= 9; rc = SQLExecute(hstmt); mystmt(hstmt,rc); length= SQL_NTS; rc = SQLExecute(hstmt); mystmt(hstmt,rc); SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "select * from t_prep_buffer_length"); rc = SQLBindCol(hstmt, 1, SQL_C_CHAR, buffer, 15, &length); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage( "outdata: %s (%ld)\n", buffer, length); is_num(length, 0); is_num(buffer[0], '\0'); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("outdata: %s (%ld)\n", buffer, length); is_num(length, 3); is_str(buffer, "abc", 10); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("outdata: %s (%ld)\n", buffer, length); is_num(length, 10); is_str(buffer, "abcdefghij", 10); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("outdata: %s (%ld)\n", buffer, length); is_num(length, 9); is_str(buffer, "abcdefghi", 9); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("outdata: %s (%ld)\n", buffer, length); is_num(length, 10); is_str(buffer, "abcdefghij", 10); rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA_FOUND); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_buffer_length"); return OK; } /* For data truncation */ DECLARE_TEST(t_prep_truncate) { SQLLEN length, length1, pcrow; SQLCHAR name[20], bin[10]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_truncate"); ok_sql(hstmt, "create table t_prep_truncate(a int, b char(4), c binary(4))"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_prep_truncate " "values(500,'venu','venu')", SQL_NTS)); strcpy((char *)name,"venu"); rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 10, 10, name, 5, NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_BINARY,SQL_BINARY, 10, 10, name, 5, NULL); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLRowCount(hstmt, &pcrow); mystmt(hstmt,rc); printMessage( "affected rows: %ld\n", pcrow); myassert(pcrow == 1); SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "select b,c from t_prep_truncate"); rc = SQLBindCol(hstmt, 1, SQL_C_CHAR, name, 2, &length); mystmt(hstmt,rc); rc = SQLBindCol(hstmt, 2, SQL_C_BINARY, bin, 4, &length1); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("str outdata: %s(%d)\n",name,length); is_num(length, 4); is_str(bin, "v", 1); bin[4]='M'; printMessage("bin outdata: %s(%d)\n",bin,length1); is_num(length, 4); is_str(bin, "venuM", 5); rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA_FOUND); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_truncate"); return OK; } /* For scrolling */ DECLARE_TEST(t_prep_scroll) { SQLINTEGER i, data, max_rows= 5; ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_scroll"); ok_sql(hstmt, "CREATE TABLE t_prep_scroll (a TINYINT)"); ok_sql(hstmt, "INSERT INTO t_prep_scroll VALUES (1),(2),(3),(4),(5)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "SELECT * FROM t_prep_scroll"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &data, 0, NULL)); for (i= 1; ; i++) { SQLRETURN rc= SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0); if (rc == SQL_NO_DATA) break; is_num(i, data); } is_num(i, max_rows + 1); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 3)); is_num(data, 3); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_PREV, 3)); is_num(data, 2); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 3)); is_num(data, 1); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_PREV, 3), SQL_NO_DATA); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -2), SQL_NO_DATA); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 2)); is_num(data, 2); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_LAST, 3)); is_num(data, 5); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -2)); is_num(data, 3); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 3), SQL_NO_DATA); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 3), SQL_NO_DATA); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -2)); is_num(data, 4); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_scroll"); return OK; } /* For SQLGetData */ DECLARE_TEST(t_prep_getdata) { SQLCHAR name[10]; SQLINTEGER data; SQLLEN length; SQLCHAR tiny; ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_getdata"); ok_sql(hstmt, "create table t_prep_getdata(a tinyint, b int, c char(4))"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_prep_getdata values(?,?,?)", SQL_NTS)); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_LONG,SQL_TINYINT, 0,0,&data,0,NULL); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER, 0,0,&data,0,NULL); rc = SQLBindParameter(hstmt,3,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR, 10,10,name,6,NULL); mystmt(hstmt,rc); sprintf((char *)name,"venu"); data = 10; rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); data= 0; ok_sql(hstmt, "select * from t_prep_getdata"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt, 1,SQL_C_TINYINT, &tiny, 0, NULL); mystmt(hstmt, rc); rc = SQLFetch(hstmt); mystmt(hstmt, rc); rc = SQLFetch(hstmt); mystmt(hstmt, rc); printMessage("record 1 : %d\n", tiny); myassert( tiny == 10); rc = SQLGetData(hstmt,2,SQL_C_LONG,&data,0,NULL); mystmt(hstmt,rc); printMessage("record 2 : %ld\n", data); myassert( data == 10); name[0]= '\0'; rc = SQLGetData(hstmt,3,SQL_C_CHAR,name,5,&length); mystmt(hstmt,rc); printMessage("record 3 : %s(%ld)\n", name, (long)length); is_num(length, 4); is_str(name, "venu", 4); data = 0; rc = SQLGetData(hstmt,1,SQL_C_LONG,&data,0,NULL); mystmt(hstmt,rc); printMessage("record 1 : %ld", data); myassert( data == 10); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_getdata"); return OK; } /* For SQLGetData in truncation */ DECLARE_TEST(t_prep_getdata1) { SQLCHAR data[11]; SQLLEN length; ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_getdata"); ok_sql(hstmt, "create table t_prep_getdata(a char(10), b int)"); ok_sql(hstmt, "insert into t_prep_getdata values('abcdefghij',12345)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "select * from t_prep_getdata"); rc = SQLFetch(hstmt); mystmt(hstmt, rc); data[0]= 'M'; data[1]= '\0'; rc = SQLGetData(hstmt,1,SQL_C_CHAR,data,0,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 10); is_str(data, "M", 1); rc = SQLGetData(hstmt,1,SQL_C_CHAR,data,4,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 10); is_str(data, "abc", 3); rc = SQLGetData(hstmt,1,SQL_C_CHAR,data,4,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 7); is_str(data, "def", 3); rc = SQLGetData(hstmt,1,SQL_C_CHAR,data,4,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 4); is_str(data, "ghi", 3); data[0]= 'M'; rc = SQLGetData(hstmt,1,SQL_C_CHAR,data,0,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 1); is_str(data, "M", 1); rc = SQLGetData(hstmt,1,SQL_C_CHAR,data,1,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 1); is_num(data[0], '\0'); rc = SQLGetData(hstmt,1,SQL_C_CHAR,data,2,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 1); is_str(data, "j", 1); rc = SQLGetData(hstmt,1,SQL_C_CHAR,data,2,&length); myassert(rc == SQL_NO_DATA); data[0]= 'M'; data[1]= '\0'; rc = SQLGetData(hstmt,2,SQL_C_CHAR,data,0,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 5); is_str(data, "M", 2); rc = SQLGetData(hstmt,2,SQL_C_CHAR,data,3,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 5); is_str(data, "12", 2); rc = SQLGetData(hstmt,2,SQL_C_CHAR,data,2,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 3); is_str(data, "3", 1); rc = SQLGetData(hstmt,2,SQL_C_CHAR,data,2,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 2); is_str(data, "4", 1); data[0]= 'M'; rc = SQLGetData(hstmt,2,SQL_C_CHAR,data,0,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 1); is_str(data, "M", 1); rc = SQLGetData(hstmt,2,SQL_C_CHAR,data,1,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); myassert(data[0] == '\0' && length == 1); is_num(length, 1); is_num(data[0], '\0'); rc = SQLGetData(hstmt,2,SQL_C_CHAR,data,2,&length); mystmt(hstmt,rc); printMessage("data: %s (%ld)\n", data, length); is_num(length, 1); is_str(data, "5", 1); rc = SQLGetData(hstmt,2,SQL_C_CHAR,data,2,&length); myassert(rc == SQL_NO_DATA); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_getdata"); return OK; } DECLARE_TEST(t_prep_catalog) { SQLCHAR table[20]; SQLLEN length; ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_catalog"); ok_sql(hstmt, "create table t_prep_catalog(a int default 100)"); rc = SQLTables(hstmt,NULL,0,NULL,0,(SQLCHAR *)"t_prep_catalog",14, (SQLCHAR *)"TABLE",5); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,3,SQL_C_CHAR,table,0,&length); mystmt(hstmt,rc); myassert(length == 14); rc = SQLGetData(hstmt,3,SQL_C_CHAR,table,15,&length); mystmt(hstmt,rc); is_num(length, 14); is_str(table, "t_prep_catalog", 14); rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLColumns(hstmt,NULL,0,NULL,0,(SQLCHAR *)"t_prep_catalog",14,NULL,0); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,3,SQL_C_CHAR,table,15,&length); mystmt(hstmt,rc); is_num(length, 14); is_str(table, "t_prep_catalog", 14); rc = SQLGetData(hstmt,4,SQL_C_CHAR,table,0,&length); mystmt(hstmt,rc); myassert(length == 1); rc = SQLGetData(hstmt,4,SQL_C_CHAR,table,2,&length); mystmt(hstmt,rc); is_num(length, 1); is_str(table, "a", 1); rc = SQLGetData(hstmt,13,SQL_C_CHAR,table,10,&length); mystmt(hstmt,rc); printMessage("table: %s(%d)\n", table, length); is_num(length, 3); is_str(table, "100", 3); rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_prep_catalog"); return OK; } DECLARE_TEST(t_sps) { SQLINTEGER a, a1; SQLLEN length, length1; char b[]= "abcdefghij", b1[10]; if (!mysql_min_version(hdbc, "5.0", 3)) skip("server does not support stored procedures"); ok_sql(hstmt, "DROP TABLE IF EXISTS t_tabsp"); ok_sql(hstmt, "DROP PROCEDURE IF EXISTS t_sp"); ok_sql(hstmt, "create table t_tabsp(a int, b varchar(10))"); ok_sql(hstmt,"create procedure t_sp(x int, y char(10)) " "begin insert into t_tabsp values(x, y); end;"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"call t_sp(?,?)", SQL_NTS)); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER, 0,0,&a,0,NULL); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR, 0,0,b,0,&length); for (length= 0, a= 0; a < 10; a++, length++) { rc = SQLExecute(hstmt); mystmt(hstmt, rc); } SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "select * from t_tabsp"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&a,0,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,b1,11,&length); mystmt(hstmt,rc); for (length1= 0, a1= 0; a1 < 10; a1++, length1++) { rc = SQLFetch(hstmt); mystmt(hstmt, rc); printMessage( "data: %d, %s(%d)\n", a, b1, length); myassert( a == a1); myassert(strncmp(b1,b,length1) == 0 && length1 == length); } SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP PROCEDURE IF EXISTS t_sp"); ok_sql(hstmt, "DROP TABLE IF EXISTS t_tabsp"); return OK; } DECLARE_TEST(t_prepare) { SQLRETURN rc; SQLINTEGER nidata= 200, nodata; SQLLEN nlen; char szodata[20],szidata[20]="MySQL"; SQLSMALLINT pccol; ok_sql(hstmt, "DROP TABLE IF EXISTS t_prepare"); rc = tmysql_exec(hstmt,"create table t_prepare(col1 int primary key, col2 varchar(30), col3 set(\"one\", \"two\"))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_prepare values(100,'venu','one')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_prepare values(200,'MySQL','two')"); mystmt(hstmt,rc); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"select * from t_prepare " "where col2 = ? AND col1 = ?",SQL_NTS)); rc = SQLNumResultCols(hstmt,&pccol); mystmt(hstmt,rc); is_num(pccol, 3); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nodata,0,&nlen); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szodata,200,&nlen); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_CHAR,SQL_VARCHAR, 0,0,szidata,20,&nlen); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT, SQL_C_LONG,SQL_INTEGER, 0,0,&nidata,20,NULL); mystmt(hstmt,rc); nlen= strlen(szidata); rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); fprintf(stdout," outdata: %d, %s(%ld)\n", nodata,szodata,nlen); my_assert(nodata == 200); rc = SQLFetch(hstmt); my_assert(rc == SQL_NO_DATA_FOUND); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_RESET_PARAMS); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_prepare"); return OK; } DECLARE_TEST(t_prepare1) { SQLRETURN rc; SQLINTEGER nidata= 1000; ok_sql(hstmt, "DROP TABLE IF EXISTS t_prepare1"); rc = tmysql_exec(hstmt,"create table t_prepare1(col1 int primary key, col2 varchar(30))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_prepare1 values(100,'venu')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_prepare1 values(200,'MySQL')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_prepare(hstmt,"insert into t_prepare1(col1) values(?)"); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_LONG,SQL_INTEGER, 0,0,&nidata,0,NULL); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_RESET_PARAMS); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM t_prepare1"); myassert(3 == myresult(hstmt));/* unless prepare is supported..*/ rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_prepare1"); return OK; } DECLARE_TEST(tmysql_bindcol) { SQLRETURN rc; SQLINTEGER nodata, nidata = 200; SQLLEN nlen; SQLCHAR szodata[20],szidata[20]="MySQL"; ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_bindcol"); rc = tmysql_exec(hstmt,"create table tmysql_bindcol(col1 int primary key, col2 varchar(30))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_bindcol values(100,'venu')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_bindcol values(200,'MySQL')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_prepare(hstmt,"select * from tmysql_bindcol where col2 = ? AND col1 = ?"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nodata,0,&nlen); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szodata,200,&nlen); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_CHAR,SQL_VARCHAR, 0,0,szidata,5,NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT, SQL_C_LONG,SQL_INTEGER, 0,0,&nidata,20,NULL); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage(" outdata: %d, %s(%d)\n", nodata,szodata,nlen); my_assert(nodata == 200); rc = SQLFetch(hstmt); my_assert(rc == SQL_NO_DATA_FOUND); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_RESET_PARAMS); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_bindcol"); return OK; } DECLARE_TEST(tmysql_bindparam) { SQLRETURN rc; SQLINTEGER nodata, nidata= 200; SQLLEN nlen; SQLCHAR szodata[20],szidata[20]="MySQL"; SQLSMALLINT pccol; tmysql_exec(hstmt,"drop table tmysql_bindparam"); rc = tmysql_exec(hstmt,"create table tmysql_bindparam(col1 int primary key, col2 varchar(30))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_bindparam values(100,'venu')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_bindparam values(200,'MySQL')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_prepare(hstmt,"select * from tmysql_bindparam where col2 = ? AND col1 = ?"); mystmt(hstmt,rc); rc = SQLNumResultCols(hstmt,&pccol); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nodata,0,&nlen); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szodata,200,&nlen); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_CHAR,SQL_VARCHAR, 0,0,szidata,5,NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT, SQL_C_LONG,SQL_INTEGER, 0,0,&nidata,20,NULL); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage(" outdata: %d, %s(%d)\n", nodata,szodata,nlen); my_assert(nodata == 200); rc = SQLFetch(hstmt); my_assert(rc == SQL_NO_DATA_FOUND); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_RESET_PARAMS); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"drop table tmysql_bindparam"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); return OK; } DECLARE_TEST(t_acc_update) { SQLRETURN rc; SQLINTEGER id,id1; SQLLEN pcrow; SQLHSTMT hstmt1; ok_sql(hstmt, "DROP TABLE IF EXISTS t_acc_update"); rc = tmysql_exec(hstmt,"create table t_acc_update(id int)"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_acc_update values(1)"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_acc_update values(2)"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"select id from t_acc_update where id = ?", SQL_NTS)); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,11,0,&id,0,NULL); mystmt(hstmt,rc); id = 2; rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_LONG,&id1,512,NULL); mystmt(hstmt,rc); printMessage("outdata:%d\n",id1); SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_UNBIND); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLSetConnectOption(hdbc,SQL_AUTOCOMMIT,0L); mycon(hdbc,rc); rc = SQLAllocStmt(hdbc,&hstmt1); mycon(hdbc,rc); id = 2; id1=2; rc = SQLBindParameter(hstmt1,1,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER,10,0,&id,0,NULL); mystmt(hstmt1,rc); rc = SQLBindParameter(hstmt1,2,SQL_PARAM_INPUT,SQL_C_DEFAULT,SQL_INTEGER,11,0,&id1,0,NULL); mystmt(hstmt1,rc); ok_sql(hstmt1, "UPDATE t_acc_update SET id = ? WHERE id = ?"); rc = SQLRowCount(hstmt1,&pcrow); mystmt(hstmt1,rc); printMessage("rows affected:%d\n",pcrow); SQLFreeStmt(hstmt1,SQL_RESET_PARAMS); rc = SQLFreeStmt(hstmt1,SQL_DROP); mystmt(hstmt1,rc); rc = SQLTransact(NULL,hdbc,0); mycon(hdbc,rc); rc = SQLSetConnectOption(hdbc,SQL_AUTOCOMMIT,1L); mycon(hdbc,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_acc_update"); return OK; } /** Bug #29871: MyODBC problem with MS Query ('Memory allocation error') */ DECLARE_TEST(t_bug29871) { SQLCHAR *param= (SQLCHAR *)"1"; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug29871"); ok_sql(hstmt, "CREATE TABLE t_bug29871 (a INT)"); /* The bug is related to calling deprecated SQLSetParam */ ok_stmt(hstmt, SQLSetParam(hstmt, 1, SQL_C_CHAR, SQL_INTEGER, 10, 0, param, 0)); ok_sql(hstmt, "INSERT INTO t_bug29871 VALUES (?)"); ok_stmt(hstmt, SQLSetParam(hstmt, 1, SQL_C_CHAR, SQL_INTEGER, 10, 0, param, 0)); ok_sql(hstmt, "SELECT * FROM t_bug29871 WHERE a=?"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE t_bug29871"); return OK; } BEGIN_TESTS ADD_TEST(t_prep_basic) ADD_TEST(t_prep_buffer_length) ADD_TEST(t_prep_truncate) ADD_TEST(t_prep_scroll) ADD_TEST(t_prep_getdata) ADD_TEST(t_prep_getdata1) ADD_TEST(t_prep_catalog) ADD_TEST(t_sps) ADD_TEST(t_prepare) ADD_TEST(t_prepare1) ADD_TEST(tmysql_bindcol) ADD_TEST(tmysql_bindparam) ADD_TEST(t_acc_update) ADD_TEST(t_bug29871) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_scroll.c100644 15766 12 110153 11707541005 20355 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" /* Testing basic scrolling feature */ DECLARE_TEST(t_scroll) { SQLUINTEGER i; ok_sql(hstmt, "DROP TABLE IF EXISTS t_scroll"); ok_sql(hstmt, "CREATE TABLE t_scroll (col1 INT)"); ok_sql(hstmt, "INSERT INTO t_scroll VALUES (1),(2),(3),(4),(5)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "SELECT * FROM t_scroll"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_ULONG, &i, 0, NULL)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_LAST, 0)); /* 5 */ is_num(i, 5); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_PREV, 0));/* 4 */ is_num(i, 4); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -3));/* 1 */ is_num(i, 1); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1), SQL_NO_DATA_FOUND); /* 0 */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_PREV, 1), SQL_NO_DATA_FOUND); /* 0 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, -1));/* 1 */ is_num(i, 1); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 4));/* 4 */ is_num(i, 4); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 2), SQL_NO_DATA_FOUND); /* 6 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_PREV, 2));/* last */ is_num(i, 5); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 2), SQL_NO_DATA_FOUND); /* last + 1 */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, -7), SQL_NO_DATA_FOUND); /* 0 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 2));/* 1 */ is_num(i, 1); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_PREV, 2), SQL_NO_DATA_FOUND); /* 0 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0));/* 1 */ is_num(i, 1); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_PREV, 0), SQL_NO_DATA_FOUND); /* 0 */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1), SQL_NO_DATA_FOUND); /* 0 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 1));/* 1 */ is_num(i, 1); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1), SQL_NO_DATA_FOUND); /* 0 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 1));/* 1 */ is_num(i, 1); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 1));/* 1 */ is_num(i, 2); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -2), SQL_NO_DATA_FOUND); /* 0 */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 6), SQL_NO_DATA_FOUND); /* last + 1 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_PREV, 6));/* 1 */ is_num(i, 5); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_scroll"); return OK; } /* Testing SQL_FETCH_RELATIVE with row_set_size as 10 */ DECLARE_TEST(t_array_relative_10) { SQLRETURN rc; SQLINTEGER iarray[15]; SQLLEN nrows, index; SQLUINTEGER i; char name[21]; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_array_relative_10"); ok_sql(hstmt, "create table t_array_relative_10(id int,name char(20))"); rc = SQLPrepare(hstmt,(SQLCHAR *)"insert into t_array_relative_10 values(?,?)",SQL_NTS); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,0,0,&i,0,NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,20,0,name,20,NULL); mystmt(hstmt,rc); for ( i = 1; i <= 50; i++ ) { sprintf(name,"my%d",i); rc = SQLExecute(hstmt); mystmt(hstmt,rc); } SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLEndTran(SQL_HANDLE_DBC,hdbc,SQL_COMMIT); mycon(hdbc,rc); /* set row size as 10 */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)10,0); mystmt(hstmt,rc); /* According to http://support.microsoft.com/kb/298678, the storage pointed to if SQL_ATTR_ROWS_FETCHED_PTR should be SQLLEN */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROWS_FETCHED_PTR,&nrows,0); mystmt(hstmt,rc); ok_sql(hstmt, "select * from t_array_relative_10"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,iarray,0,NULL); mystmt(hstmt,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0);/* 1-10 */ mystmt(hstmt,rc); printMessage("1-10, total rows:%ld\n",(long)nrows); for (index=1; index<=nrows; index++) { printMessage(" %d ",iarray[index-1]); myassert(iarray[index-1] == index); } rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0);/* 10-20 */ mystmt(hstmt,rc); printMessage("\n10-20, total rows:%ld\n",(long)nrows); for (index=1; index<=nrows; index++) { printMessage(" %d ",iarray[index-1]); myassert(iarray[index-1] == index+10); } rc = SQLFetchScroll(hstmt,SQL_FETCH_PREV,0);/* 1-10 */ mystmt(hstmt,rc); printMessage("\n1-10, total rows:%ld\n",(long)nrows); for (index=1; index<=nrows; index++) { printMessage(" %d ",iarray[index-1]); myassert(iarray[index-1] == index); } rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* 2-11 */ mystmt(hstmt,rc); printMessage("\n2-12, total rows:%ld\n",(long)nrows); for (index=1; index<=nrows; index++) { printMessage(" %d ",iarray[index-1]); myassert(iarray[index-1] == index+1); } rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* 1-10 */ mystmt(hstmt,rc); printMessage("\n1-10, total rows:%ld\n",(long)nrows); for (index=1; index<=nrows; index++) { printMessage(" %d",iarray[index-1]); myassert(iarray[index-1] == index); } rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,0);/* 1-10 */ mystmt(hstmt,rc); printMessage("\n1-10, total rows:%ld\n",(long)nrows); for (index=1; index<=nrows; index++) { printMessage(" %d",iarray[index-1]); myassert(iarray[index-1] == index); } rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* BOF */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* 1-10 */ mystmt(hstmt,rc); printMessage("\n1-10, total rows:%ld\n",(long)nrows); for (index=1; index<=nrows; index++) { printMessage(" %d",iarray[index-1]); myassert(iarray[index-1] == index); } SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)1,0); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_array_relative_10"); return OK; } /* Testing SQL_FETCH_RELATIVE with row_set_size as 1 */ DECLARE_TEST(t_relative_1) { SQLRETURN rc; SQLLEN nrows; SQLUINTEGER i; const SQLUINTEGER max_rows=10; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_relative_1"); ok_sql(hstmt, "create table t_relative_1(id int)"); rc = SQLPrepare(hstmt, (SQLCHAR *)"insert into t_relative_1 values(?)",SQL_NTS); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,0,0,&i,0,NULL); mystmt(hstmt,rc); for ( i = 1; i <= max_rows; i++ ) { rc = SQLExecute(hstmt); mystmt(hstmt,rc); } SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLEndTran(SQL_HANDLE_DBC,hdbc,SQL_COMMIT); mycon(hdbc,rc); /* set row_size as 1 */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)1,0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROWS_FETCHED_PTR,&nrows,0); mystmt(hstmt,rc); ok_sql(hstmt, "select * from t_relative_1"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&i,0,NULL); mystmt(hstmt,rc); /* row 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0);/* 1 */ mystmt(hstmt,rc); my_assert(i==1); /* Before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* jump to last row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,max_rows);/* last row */ mystmt(hstmt,rc); my_assert(i==max_rows); /* jump to last row+1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* after last */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* goto first row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* 1 */ mystmt(hstmt,rc); my_assert(i==1); /* before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* goto fifth row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,5);/* 5 */ mystmt(hstmt,rc); my_assert(i==5); /* goto after end */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,max_rows);/* after last */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* the scenarios from ODBC spec */ /* CASE 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* 1 */ mystmt(hstmt,rc); my_assert(i==1); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* BeforeStart AND FetchOffset <= 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-20);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* case 1: Before start AND FetchOffset > 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* 1 */ mystmt(hstmt,rc); my_assert(i==1); /* CASE 2 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_LAST,1);/* last row */ mystmt(hstmt,rc); my_assert(i==max_rows); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* After end AND FetchOffset >= 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,10);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,20);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,0);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* After end AND FetchOffset < 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* last row */ mystmt(hstmt,rc); my_assert(i==max_rows); /* CASE 3 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(i==1); /* CurrRowsetStart = 1 AND FetchOffset < 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,0);/* first row */ mystmt(hstmt,rc); my_assert(i==1); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 4 */ /* CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | > RowsetSize */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(i==1); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,3);/* fourth row */ mystmt(hstmt,rc); my_assert(i==4); /* the following call satisfies 4 > 1 AND (3-4) < 1 AND |-4| > 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-4);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 5 */ /* 1 <= CurrRowsetStart + FetchOffset <= LastResultRow */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(i==1); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,5);/* sixth row */ mystmt(hstmt,rc); my_assert(i==6); /* 1 <= 6-2 <= 10 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-2);/* fourth row */ mystmt(hstmt,rc); my_assert(i==4); /* CASE 6 */ /* CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | <= RowsetSize */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(i==1); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,3);/* fourth row */ mystmt(hstmt,rc); my_assert(i==4); /* 4 >1 AND 4-4 <1 AND |-4| <=10 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-4);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)1,0); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_relative_1"); return OK; } /* Testing SQL_FETCH_RELATIVE with row_set_size as 2 */ DECLARE_TEST(t_array_relative_2) { SQLRETURN rc; SQLUINTEGER i; SQLLEN nrows; SQLINTEGER iarray[15]; const SQLUINTEGER max_rows=10; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_array_relative_2"); ok_sql(hstmt,"create table t_array_relative_2(id int)"); rc = SQLPrepare(hstmt, (SQLCHAR *)"insert into t_array_relative_2 values(?)",SQL_NTS); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,0,0,&i,0,NULL); mystmt(hstmt,rc); for ( i = 1; i <= max_rows; i++ ) { rc = SQLExecute(hstmt); mystmt(hstmt,rc); } SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLEndTran(SQL_HANDLE_DBC,hdbc,SQL_COMMIT); mycon(hdbc,rc); /* set row_size as 2 */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)2,0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROWS_FETCHED_PTR,&nrows,0); mystmt(hstmt,rc); ok_sql(hstmt, "select * from t_array_relative_2"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,iarray,0,NULL); mystmt(hstmt,rc); /* row 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); /* Before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* jump to last row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,max_rows);/* last row */ mystmt(hstmt,rc); my_assert(nrows == 1); my_assert(iarray[0]==max_rows); /* jump to last row+1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* after last */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* goto first row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); /* before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* goto fifth row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,5);/* 5 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==5); my_assert(iarray[1]==6); /* goto after end */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,max_rows);/* after last */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* the scenarios from ODBC spec */ /* CASE 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* BeforeStart AND FetchOffset <= 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-20);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* case 1: Before start AND FetchOffset > 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); /* CASE 2 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_LAST,1);/* last row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==max_rows-1); my_assert(iarray[1]==max_rows); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* last row */ mystmt(hstmt,rc); my_assert(nrows == 1); my_assert(iarray[0]==max_rows); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* after last row */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* After end AND FetchOffset >= 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,10);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,20);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,0);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* After end AND FetchOffset < 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* last row */ mystmt(hstmt,rc); my_assert(nrows == 1); my_assert(iarray[0]==max_rows); /* CASE 3 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); /* CurrRowsetStart = 1 AND FetchOffset < 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,0);/* first row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 4 */ /* CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | > RowsetSize */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,3);/* fourth row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==4); my_assert(iarray[1]==5); /* the following call satisfies 4 > 1 AND (3-4) < 1 AND |-4| > 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-4);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 5 */ /* 1 <= CurrRowsetStart + FetchOffset <= LastResultRow */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,5);/* sixth row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==6); my_assert(iarray[1]==7); /* 1 <= 6-2 <= 10 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-2);/* fourth row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==4); my_assert(iarray[1]==5); /* CASE 6 */ /* CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | <= RowsetSize */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,3);/* fourth row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==4); my_assert(iarray[1]==5); /* 4 >1 AND 4-4 <1 AND |-4| <=10 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-4);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); /*** for rowset_size > max_rows... */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)25,0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROWS_FETCHED_PTR,&nrows,0); mystmt(hstmt,rc); ok_sql(hstmt, "select * from t_array_relative_2"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,iarray,0,NULL); mystmt(hstmt,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,2);/* 2 */ mystmt(hstmt,rc); my_assert(nrows == max_rows-1); my_assert(iarray[0]==2); my_assert(iarray[max_rows-2]==10); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,1);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == max_rows); my_assert(iarray[0]==1); my_assert(iarray[max_rows-1]==max_rows); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,5);/* 5 */ mystmt(hstmt,rc); my_assert(nrows == max_rows-4); my_assert(iarray[0]==5); my_assert(iarray[max_rows-5]==max_rows); /* CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | > RowsetSize ==> before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-30);/* 1 */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,2);/* 2 */ mystmt(hstmt,rc); my_assert(nrows == max_rows-1); my_assert(iarray[0]==2); my_assert(iarray[max_rows-2]==10); /* CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | <= RowsetSize ==> 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-13);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == max_rows); my_assert(iarray[0]==1); my_assert(iarray[max_rows-1]==max_rows); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)1,0); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_array_relative_2"); return OK; } /* Testing SQL_FETCH_ABSOLUTE with row_set_size as 1 */ DECLARE_TEST(t_absolute_1) { SQLRETURN rc; SQLLEN nrows; SQLUINTEGER i; const SQLUINTEGER max_rows=10; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_absolute_1"); ok_sql(hstmt, "create table t_absolute_1(id int)"); rc = SQLPrepare(hstmt, (SQLCHAR *)"insert into t_absolute_1 values(?)",SQL_NTS); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,0,0,&i,0,NULL); mystmt(hstmt,rc); for ( i = 1; i <= max_rows; i++ ) { rc = SQLExecute(hstmt); mystmt(hstmt,rc); } SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLEndTran(SQL_HANDLE_DBC,hdbc,SQL_COMMIT); mycon(hdbc,rc); /* set row_size as 1 */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)1,0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROWS_FETCHED_PTR,&nrows,0); mystmt(hstmt,rc); ok_sql(hstmt, "select * from t_absolute_1"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&i,0,NULL); mystmt(hstmt,rc); /* row 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0);/* 1 */ mystmt(hstmt,rc); my_assert(i==1); /* Before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-12);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* jump to last row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows);/* last row */ mystmt(hstmt,rc); my_assert(i==max_rows); /* jump to last row+1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows+1);/* after last */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* goto first row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,1);/* 1 */ mystmt(hstmt,rc); my_assert(i==1); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-15);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* goto fifth row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,5);/* 5 */ mystmt(hstmt,rc); my_assert(i==5); /* goto after end */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows+5);/* after last */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* the scenarios from ODBC spec */ /* CASE 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* 1 */ mystmt(hstmt,rc); my_assert(i==1); /* FetchOffset < 0 AND | FetchOffset | <= LastResultRow , ==> should yield LastResultRow + FetchOffset + 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-1); mystmt(hstmt,rc); my_assert(i==(max_rows-1+1)); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-4); mystmt(hstmt,rc); my_assert(i==(max_rows-4+1)); /* CASE 2 : FetchOffset < 0 AND | FetchOffset | > LastResultRow AND | FetchOffset | > RowsetSize ==> Before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-11); mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 4: FetchOffset = 0 ==> before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 5: 1 <= FetchOffset <= LastResultRow ==> FetchOffset */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,2);/* 2 */ mystmt(hstmt,rc); my_assert(i==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,9);/* 9 */ mystmt(hstmt,rc); my_assert(i==9); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,6);/* 6 */ mystmt(hstmt,rc); my_assert(i==6); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,0);/* BOF */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 6: FetchOffset > LastResultRow ==> after end */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows);/* last row */ mystmt(hstmt,rc); my_assert(i==max_rows); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows+1);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows+max_rows);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,5);/* 5 */ mystmt(hstmt,rc); my_assert(i==5); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,12);/* 5 */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)1,0); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_absolute_1"); return OK; } /* Testing SQL_FETCH_ABSOLUTE with row_set_size as 2 */ DECLARE_TEST(t_absolute_2) { SQLRETURN rc; SQLLEN nrows; SQLINTEGER iarray[15]; const SQLUINTEGER max_rows=10; SQLUINTEGER i; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_absolute_2"); ok_sql(hstmt, "create table t_absolute_2(id int)"); rc = SQLPrepare(hstmt, (SQLCHAR *)"insert into t_absolute_2 values(?)",SQL_NTS); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,0,0,&i,0,NULL); mystmt(hstmt,rc); for ( i = 1; i <= max_rows; i++ ) { rc = SQLExecute(hstmt); mystmt(hstmt,rc); } SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLEndTran(SQL_HANDLE_DBC,hdbc,SQL_COMMIT); mycon(hdbc,rc); /* set row_size as 1 */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)2,0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROWS_FETCHED_PTR,&nrows,0); mystmt(hstmt,rc); ok_sql(hstmt, "select * from t_absolute_2"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,iarray,0,NULL); mystmt(hstmt,rc); /* row 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); /* Before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-12);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* jump to last row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows);/* last row */ mystmt(hstmt,rc); my_assert(nrows == 1); my_assert(iarray[0]==max_rows); /* jump to last row+1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows+1);/* after last */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* goto first row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,1);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-15);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* goto fifth row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,5);/* 5 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==5); my_assert(iarray[1]==6); /* goto after end */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows+5);/* after last */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* the scenarios from ODBC spec */ /* CASE 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); /* FetchOffset < 0 AND | FetchOffset | <= LastResultRow , ==> should yield LastResultRow + FetchOffset + 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-1); mystmt(hstmt,rc); my_assert(nrows == 1); my_assert(iarray[0]==(max_rows-1+1)); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-4); mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==(max_rows-4+1)); my_assert(iarray[1]==(max_rows-4+1+1)); /* CASE 2 : FetchOffset < 0 AND | FetchOffset | > LastResultRow AND | FetchOffset | > RowsetSize ==> Before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-11); mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 4: FetchOffset = 0 ==> before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 5: 1 <= FetchOffset <= LastResultRow ==> FetchOffset */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,2);/* 2 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==2); my_assert(iarray[1]==3); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,9);/* 9 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==9); my_assert(iarray[1]==10); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,6);/* 6 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==6); my_assert(iarray[1]==7); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,0);/* BOF */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 6: FetchOffset > LastResultRow ==> after end */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows);/* last row */ mystmt(hstmt,rc); my_assert(nrows == 1); my_assert(iarray[0]==max_rows); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows+1);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,max_rows+max_rows);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,5);/* 5 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==5); my_assert(iarray[1]==6); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,12);/* 5 */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)1,0); mystmt(hstmt,rc); /* for rowset_size > max_rows...*/ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)25,0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROWS_FETCHED_PTR,&nrows,0); mystmt(hstmt,rc); ok_sql(hstmt, "select * from t_absolute_2"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,iarray,0,NULL); mystmt(hstmt,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,2);/* 2 */ mystmt(hstmt,rc); my_assert(nrows == max_rows-1); my_assert(iarray[0]==2); my_assert(iarray[max_rows-2]==10); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,1);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == max_rows); my_assert(iarray[0]==1); my_assert(iarray[max_rows-1]==max_rows); rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,5);/* 5 */ mystmt(hstmt,rc); my_assert(nrows == max_rows-4); my_assert(iarray[0]==5); my_assert(iarray[max_rows-5]==max_rows); /* FetchOffset < 0 AND | FetchOffset | > LastResultRow AND | FetchOffset | <= RowsetSize */ rc = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,-13);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == max_rows); my_assert(iarray[0]==1); my_assert(iarray[max_rows-1]==max_rows); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)1,0); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_absolute_2"); return OK; } BEGIN_TESTS ADD_TEST(t_scroll) ADD_TEST(t_array_relative_10) ADD_TEST(t_array_relative_2) ADD_TEST(t_relative_1) ADD_TEST(t_absolute_1) ADD_TEST(t_absolute_2) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/unit.pl100644 15766 12 5736 11707541005 17474 0ustar00cteamstaff#!/usr/bin/perl # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA use Test::Harness qw(&runtests $verbose); use File::Find; use strict; sub run_cmd (@); my %dispatch = ( "run" => \&run_cmd, ); =head1 NAME unit - Run unit tests in directory =head1 SYNOPSIS unit run =cut my $cmd = shift; if (defined $cmd && exists $dispatch{$cmd}) { $dispatch{$cmd}->(@ARGV); } else { print "Unknown command", (defined $cmd ? " $cmd" : ""), ".\n"; print "Available commands are: ", join(", ", keys %dispatch), "\n"; } =head2 run Run all unit tests in the current directory and all subdirectories. =cut sub _find_test_files (@) { my @dirs = @_; my @files; find sub { $File::Find::prune = 1 if /^SCCS$/; push(@files, $File::Find::name) if -x _ && /-t\z/; }, @dirs; return @files; } sub run_cmd (@) { my @files; # If no directories were supplied, we add all directories in the # current directory except 'mytap' since it is not part of the # test suite. if (@_ == 0) { # Ignore these directories my @ignore = qw(mytap SCCS); # Build an expression from the directories above that tests if a # directory should be included in the list or not. my $ignore = join(' && ', map { '$_ ne ' . "'$_'"} @ignore); # Open and read the directory. Filter out all files, hidden # directories, and directories named above. opendir(DIR, ".") or die "Cannot open '.': $!\n"; @_ = grep { -d $_ && $_ !~ /^\..*/ && eval $ignore } readdir(DIR); closedir(DIR); } print "Running tests: @_\n"; foreach my $name (@_) { push(@files, _find_test_files $name) if -d $name; push(@files, $name) if -f $name; } if (@files > 0) { # Removing the first './' from the file names foreach (@files) { s!^!./! } $ENV{'HARNESS_PERL_SWITCHES'} .= q" -e 'exec @ARGV'"; runtests @files; } } mysql-connector-odbc-5.1.10-src/test/my_keys.c100644 15766 12 37036 11707541005 20022 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" /* UPDATE with no keys ... */ DECLARE_TEST(my_no_keys) { SQLRETURN rc; SQLINTEGER nData; ok_sql(hstmt, "DROP TABLE IF EXISTS my_no_keys"); ok_sql(hstmt, "create table my_no_keys(col1 int,\ col2 varchar(30),\ col3 int,\ col4 int)"); ok_sql(hstmt, "insert into my_no_keys values(100,'MySQL1',1,3000)"); ok_sql(hstmt, "insert into my_no_keys values(200,'MySQL1',2,3000)"); ok_sql(hstmt, "insert into my_no_keys values(300,'MySQL1',3,3000)"); ok_sql(hstmt, "insert into my_no_keys values(400,'MySQL1',4,3000)"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY ,(SQLPOINTER)SQL_CONCUR_ROWVER , 0); mystmt(hstmt, rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1 , 0); mystmt(hstmt, rc); /* UPDATE ROW[2]COL[4] */ ok_sql(hstmt, "select col4 from my_no_keys"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,100,NULL); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_ABSOLUTE,2,NULL,NULL); mystmt(hstmt,rc); nData = 999; rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt,"select * from my_no_keys"); is(4 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt,"select * from my_no_keys"); rc = SQLFetch(hstmt); mystmt(hstmt,rc); is(3000 == my_fetch_int(hstmt,4)); rc = SQLFetch(hstmt); mystmt(hstmt,rc); is(3000 == my_fetch_int(hstmt,4)); rc = SQLFetch(hstmt); mystmt(hstmt,rc); is(3000 == my_fetch_int(hstmt,4)); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS my_no_keys"); return OK; } DECLARE_TEST(my_foreign_keys) { SQLRETURN rc=0; SQLCHAR dbc[255]; ok_sql(hstmt,"DROP DATABASE IF EXISTS test_odbc_fk"); ok_sql(hstmt,"CREATE DATABASE test_odbc_fk"); ok_sql(hstmt,"use test_odbc_fk"); ok_sql(hstmt,"DROP TABLE IF EXISTS test_fkey_c1"); ok_sql(hstmt,"DROP TABLE IF EXISTS test_fkey_3"); ok_sql(hstmt,"DROP TABLE IF EXISTS test_fkey_2"); ok_sql(hstmt,"DROP TABLE IF EXISTS test_fkey_1"); ok_sql(hstmt,"DROP TABLE IF EXISTS test_fkey_p1"); ok_sql(hstmt,"DROP TABLE IF EXISTS test_fkey_comment_f"); ok_sql(hstmt,"DROP TABLE IF EXISTS test_fkey_comment_p"); ok_sql(hstmt, "CREATE TABLE test_fkey1(\ A INTEGER NOT NULL,B INTEGER NOT NULL,C INTEGER NOT NULL,\ D INTEGER,PRIMARY KEY (C,B,A)) ENGINE=InnoDB;"); ok_sql(hstmt, "CREATE TABLE test_fkey_p1(\ A INTEGER NOT NULL,B INTEGER NOT NULL,C INTEGER NOT NULL,\ D INTEGER NOT NULL,E INTEGER NOT NULL,F INTEGER NOT NULL,\ G INTEGER NOT NULL,H INTEGER NOT NULL,I INTEGER NOT NULL,\ J INTEGER NOT NULL,K INTEGER NOT NULL,L INTEGER NOT NULL,\ M INTEGER NOT NULL,N INTEGER NOT NULL,O INTEGER NOT NULL,\ P INTEGER NOT NULL,Q INTEGER NOT NULL,R INTEGER NOT NULL,\ PRIMARY KEY (D,F,G,H,I,J,K,L,M,N,O)) ENGINE=InnoDB;"); ok_sql(hstmt, "CREATE TABLE test_fkey2 (\ E INTEGER NOT NULL,C INTEGER NOT NULL,B INTEGER NOT NULL,\ A INTEGER NOT NULL,PRIMARY KEY (E),\ INDEX test_fkey2_ind(C,B,A),\ FOREIGN KEY (C,B,A) REFERENCES test_fkey1(C,B,A)) ENGINE=InnoDB;"); ok_sql(hstmt, "CREATE TABLE test_fkey3 (\ F INTEGER NOT NULL,C INTEGER NOT NULL,E INTEGER NOT NULL,\ G INTEGER, A INTEGER NOT NULL, B INTEGER NOT NULL,\ PRIMARY KEY (F),\ INDEX test_fkey3_ind(C,B,A),\ INDEX test_fkey3_ind3(E),\ FOREIGN KEY (C,B,A) REFERENCES test_fkey1(C,B,A),\ FOREIGN KEY (E) REFERENCES test_fkey2(E)) ENGINE=InnoDB;"); ok_sql(hstmt, "CREATE TABLE test_fkey_c1(\ A INTEGER NOT NULL,B INTEGER NOT NULL,C INTEGER NOT NULL,\ D INTEGER NOT NULL,E INTEGER NOT NULL,F INTEGER NOT NULL,\ G INTEGER NOT NULL,H INTEGER NOT NULL,I INTEGER NOT NULL,\ J INTEGER NOT NULL,K INTEGER NOT NULL,L INTEGER NOT NULL,\ M INTEGER NOT NULL,N INTEGER NOT NULL,O INTEGER NOT NULL,\ P INTEGER NOT NULL,Q INTEGER NOT NULL,R INTEGER NOT NULL,\ PRIMARY KEY (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O),\ INDEX test_fkey_c1_ind1(D,F,G,H,I,J,K,L,M,N,O),\ INDEX test_fkey_c1_ind2(C,B,A),\ INDEX test_fkey_c1_ind3(E),\ FOREIGN KEY (D,F,G,H,I,J,K,L,M,N,O) REFERENCES \ test_fkey_p1(D,F,G,H,I,J,K,L,M,N,O),\ FOREIGN KEY (C,B,A) REFERENCES test_fkey1(C,B,A),\ FOREIGN KEY (E) REFERENCES test_fkey2(E)) ENGINE=InnoDB;"); ok_sql(hstmt, "CREATE TABLE test_fkey_comment_p ( \ ISP_ID SMALLINT NOT NULL, \ CUSTOMER_ID VARCHAR(10), \ ABBREVIATION VARCHAR(20) NOT NULL, \ NAME VARCHAR(40) NOT NULL, \ SEQUENCE INT NOT NULL, \ PRIMARY KEY PL_ISP_PK (ISP_ID), \ UNIQUE INDEX PL_ISP_CUSTOMER_ID_UK (CUSTOMER_ID), \ UNIQUE INDEX PL_ISP_ABBR_UK (ABBREVIATION) \ ) ENGINE=InnoDB COMMENT='Holds the information of (customers)'"); ok_sql(hstmt, "CREATE TABLE test_fkey_comment_f ( \ CAMPAIGN_ID INT NOT NULL , \ ISP_ID SMALLINT NOT NULL, \ NAME VARCHAR(40) NOT NULL, \ DISPLAY_NAME VARCHAR(255) NOT NULL, \ BILLING_TYPE SMALLINT NOT NULL, \ BROADBAND_OK BOOL, \ EMAIL_OK BOOL, \ PRIMARY KEY PL_ISP_CAMPAIGN_PK (CAMPAIGN_ID), \ UNIQUE INDEX PL_ISP_CAMPAIGN_BT_UK (BILLING_TYPE), \ INDEX PL_ISP_CAMPAIGN_ISP_ID_IX (ISP_ID), \ FOREIGN KEY (ISP_ID) REFERENCES test_fkey_comment_p(ISP_ID) \ ) ENGINE=InnoDB COMMENT='List of campaigns (test comment)'"); SQLFreeStmt(hstmt,SQL_CLOSE); strcpy((char *)dbc, "test_odbc_fk"); printMessage("\n WITH ONLY PK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, /*PK CATALOG*/ NULL, SQL_NTS, /*PK SCHEMA*/ (SQLCHAR *)"test_fkey1", SQL_NTS, /*PK TABLE*/ dbc, SQL_NTS, /*FK CATALOG*/ NULL, SQL_NTS, /*FK SCHEMA*/ NULL, SQL_NTS); /*FK TABLE*/ mystmt(hstmt,rc); is(9 == myresult(hstmt)); printMessage("\n WITH ONLY FK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey1", SQL_NTS); mystmt(hstmt,rc); is(0 == myresult(hstmt)); printMessage("\n WITH ONLY FK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey_c1", SQL_NTS); mystmt(hstmt,rc); is(15 == myresult(hstmt)); printMessage("\n WITH ONLY FK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey2", SQL_NTS); mystmt(hstmt,rc); is(3 == myresult(hstmt)); printMessage("\n WITH ONLY PK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey_p1", SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS); mystmt(hstmt,rc); is(11 == myresult(hstmt)); printMessage("\n WITH ONLY PK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey3", SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS); mystmt(hstmt,rc); is(0 == myresult(hstmt)); printMessage("\n WITH ONLY PK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey2", SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS); mystmt(hstmt,rc); is(2 == myresult(hstmt)); printMessage("\n WITH ONLY PK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey1", SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS); mystmt(hstmt,rc); is(9 == myresult(hstmt)); printMessage("\n WITH ONLY FK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey3", SQL_NTS); mystmt(hstmt,rc); is(4 == myresult(hstmt)); printMessage("\n WITH BOTH PK and FK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey1",SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey3", SQL_NTS); mystmt(hstmt,rc); is(3 == myresult(hstmt)); printMessage("\n WITH BOTH PK and FK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey_p1",SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey_c1", SQL_NTS); mystmt(hstmt,rc); is(11 == myresult(hstmt)); printMessage("\n WITH BOTH PK and FK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey1",SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey2", SQL_NTS); mystmt(hstmt,rc); is(3 == myresult(hstmt)); printMessage("\n WITH BOTH PK and FK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey_p1",SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey2", SQL_NTS); mystmt(hstmt,rc); is(0 == myresult(hstmt)); printMessage("\n WITH BOTH PK and FK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey3", SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey1", SQL_NTS); mystmt(hstmt,rc); is(0 == myresult(hstmt)); printMessage("\n WITH BOTH PK and FK OPTION"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey2", SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey1", SQL_NTS); mystmt(hstmt,rc); is(0 == myresult(hstmt)); printMessage("\n WITH ACTUAL LENGTH INSTEAD OF SQL_NTS"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey1",10, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey2",10); mystmt(hstmt,rc); is(3 == myresult(hstmt)); SQLFreeStmt(hstmt,SQL_CLOSE); printMessage("\n WITH NON-EXISTANT TABLES"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey_junk", SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey_junk", SQL_NTS); mystmt(hstmt,rc); is(0 == myresult(hstmt)); SQLFreeStmt(hstmt,SQL_CLOSE); printMessage("\n WITH COMMENT FIELD"); rc = SQLForeignKeys(hstmt, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey_comment_p", SQL_NTS, dbc, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_fkey_comment_f", SQL_NTS); mystmt(hstmt,rc); is(1 == myresult(hstmt)); { char buff[255]; sprintf(buff,"use %s",mydb); ok_sql(hstmt, "DROP DATABASE test_odbc_fk"); SQLFreeStmt(hstmt, SQL_CLOSE); SQLExecDirect(hstmt, (SQLCHAR *)buff, SQL_NTS); SQLFreeStmt(hstmt, SQL_CLOSE); } return OK; } void t_strstr() { char *string = "'TABLE','VIEW','SYSTEM TABLE'"; char *str=","; char *type; type = strstr((const char *)string,(const char *)str); while (type++) { int len = type - string; printMessage("\n Found '%s' at position '%d[%s]", str, len, type); type = strstr(type,str); } } BEGIN_TESTS ADD_TEST(my_no_keys) ADD_TEST(my_foreign_keys) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_relative.c100644 15766 12 57551 11707541005 20666 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" /* Testing SQL_FETCH_RELATIVE with row_set_size as 10 */ DECLARE_TEST(t_relative) { SQLUINTEGER i, iarray[15]; SQLULEN nrows, index; SQLCHAR name[21]; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_relative"); ok_sql(hstmt, "CREATE TABLE t_relative (id INT, name CHAR(20))"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"INSERT INTO t_relative VALUES (?,?)", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &i, 0, NULL)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 20, 0, name, 20, NULL)); for (i= 1; i <= 50; i++) { sprintf((char *)name, "my%d", i); ok_stmt(hstmt, SQLExecute(hstmt)); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* set row size as 10 */ ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)10, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &nrows, 0)); ok_sql(hstmt, "SELECT * FROM t_relative"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_ULONG, &iarray, 0, NULL)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); /* 1-10 */ is_num(nrows, 10); for (index= 1; index <= nrows; index++) is_num(iarray[index - 1], index); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); /* 10-20 */ is_num(nrows, 10); for (index= 1; index <= nrows; index++) is_num(iarray[index - 1], index + 10); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_PREV, 0)); /* 1-10 */ is_num(nrows, 10); for (index= 1; index <= nrows; index++) is_num(iarray[index - 1], index); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 1)); /* 2-11 */ is_num(nrows, 10); for (index= 1; index <= nrows; index++) is_num(iarray[index - 1], index + 1); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1)); /* 1-10 */ is_num(nrows, 10); for (index= 1; index <= nrows; index++) is_num(iarray[index - 1], index); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 0)); /* 1-10 */ is_num(nrows, 10); for (index= 1; index <= nrows; index++) is_num(iarray[index - 1], index); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1), SQL_NO_DATA_FOUND); /* BOF */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 1)); /* 1-10 */ is_num(nrows, 10); for (index= 1; index <= nrows; index++) is_num(iarray[index - 1], index); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* reset row size */ ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_relative"); return OK; } /* Testing SQL_FETCH_RELATIVE with row_set_size as 1 */ DECLARE_TEST(t_relative1) { SQLULEN nrows; SQLUINTEGER i; const SQLUINTEGER max_rows= 10; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_relative1"); ok_sql(hstmt, "CREATE TABLE t_relative1 (id INT)"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"INSERT INTO t_relative1 VALUES (?)", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &i, 0, NULL)); for (i= 1; i <= max_rows; i++) { ok_stmt(hstmt, SQLExecute(hstmt)); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* set row_size as 1 */ ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &nrows, 0)); ok_sql(hstmt, "SELECT * FROM t_relative1"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &i, 0, NULL)); /* row 1 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(i, 1); /* Before start */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1), SQL_NO_DATA_FOUND); /* jump to last row */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, max_rows)); is_num(i, max_rows); /* jump to last row+1 */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 1), SQL_NO_DATA_FOUND); /* goto first row */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 1)); is_num(i, 1); /* before start */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1), SQL_NO_DATA_FOUND); /* goto fifth row */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 5)); is_num(i, 5); /* goto after end */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, max_rows), SQL_NO_DATA_FOUND); /* the scenarios from ODBC spec */ /* CASE 1 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 1)); is_num(i, 1); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1), SQL_NO_DATA_FOUND); /* BeforeStart AND FetchOffset <= 0 */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -20), SQL_NO_DATA_FOUND); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1), SQL_NO_DATA_FOUND); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 0), SQL_NO_DATA_FOUND); /* case 1: Before start AND FetchOffset > 0 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 1)); is_num(i, 1); /* CASE 2 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_LAST, 1)); is_num(i, max_rows); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 1), SQL_NO_DATA_FOUND); /* After end AND FetchOffset >= 0 */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 10), SQL_NO_DATA_FOUND); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 20), SQL_NO_DATA_FOUND); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 1), SQL_NO_DATA_FOUND); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 0), SQL_NO_DATA_FOUND); /* After end AND FetchOffset < 0 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1)); is_num(i, max_rows); /* CASE 3 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 1)); is_num(i, 1); /* CurrRowsetStart = 1 AND FetchOffset < 0 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 0)); is_num(i, 1); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -1), SQL_NO_DATA_FOUND); /* CASE 4 */ /* CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | > RowsetSize */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 1)); is_num(i, 1); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 3)); is_num(i, 4); /* the following call satisfies 4 > 1 AND (3-4) < 1 AND |-4| > 1 */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -4), SQL_NO_DATA_FOUND); /* CASE 5 */ /* 1 <= CurrRowsetStart + FetchOffset <= LastResultRow */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 1)); is_num(i, 1); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 5)); is_num(i, 6); /* 1 <= 6-2 <= 10 */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -2)); is_num(i, 4); /* CASE 6 */ /* CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | <= RowsetSize */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 1)); is_num(i, 1); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, 3)); is_num(i, 4); /* 4 >1 AND 4-4 <1 AND |-4| <=10 */ expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_RELATIVE, -4), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_relative1"); return OK; } /* Testing SQL_FETCH_RELATIVE with row_set_size as 2 */ DECLARE_TEST(t_relative2) { SQLRETURN rc; SQLULEN nrows; SQLUINTEGER i, iarray[15]; const SQLUINTEGER max_rows=10; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_relative2"); ok_sql(hstmt, "create table t_relative2(id int)"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "insert into t_relative2 values(?)", SQL_NTS)); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,0,0,&i,0,NULL); mystmt(hstmt,rc); for ( i = 1; i <= max_rows; i++ ) { rc = SQLExecute(hstmt); mystmt(hstmt,rc); } SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLEndTran(SQL_HANDLE_DBC,hdbc,SQL_COMMIT); mycon(hdbc,rc); /* set row_size as 2 */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)2,0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROWS_FETCHED_PTR,&nrows,0); mystmt(hstmt,rc); ok_sql(hstmt, "select * from t_relative2"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&iarray,0,NULL); mystmt(hstmt,rc); /* row 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); /* Before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* jump to last row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,max_rows);/* last row */ mystmt(hstmt,rc); my_assert(nrows == 1); my_assert(iarray[0]==max_rows); /* jump to last row+1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* after last */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* goto first row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); /* before start */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* goto fifth row */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,5);/* 5 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==5); my_assert(iarray[1]==6); /* goto after end */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,max_rows);/* after last */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* the scenarios from ODBC spec */ /* CASE 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* BeforeStart AND FetchOffset <= 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-20);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,0);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* case 1: Before start AND FetchOffset > 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* 1 */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); /* CASE 2 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_LAST,1);/* last row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==max_rows-1); my_assert(iarray[1]==max_rows); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* last row */ mystmt(hstmt,rc); my_assert(nrows == 1); my_assert(iarray[0]==max_rows); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* after last row */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* After end AND FetchOffset >= 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,10);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,20);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,1);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,0);/* after end */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* After end AND FetchOffset < 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* last row */ mystmt(hstmt,rc); my_assert(nrows == 1); my_assert(iarray[0]==max_rows); /* CASE 3 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); /* CurrRowsetStart = 1 AND FetchOffset < 0 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,0);/* first row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-1);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 4 */ /* CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | > RowsetSize */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,3);/* fourth row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==4); my_assert(iarray[1]==5); /* the following call satisfies 4 > 1 AND (3-4) < 1 AND |-4| > 1 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-4);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); /* CASE 5 */ /* 1 <= CurrRowsetStart + FetchOffset <= LastResultRow */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,5);/* sixth row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==6); my_assert(iarray[1]==7); /* 1 <= 6-2 <= 10 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-2);/* fourth row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==4); my_assert(iarray[1]==5); /* CASE 6 */ /* CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND | FetchOffset | <= RowsetSize */ rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST,1);/* first row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==1); my_assert(iarray[1]==2); rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,3);/* fourth row */ mystmt(hstmt,rc); my_assert(nrows == 2); my_assert(iarray[0]==4); my_assert(iarray[1]==5); /* 4 >1 AND 4-4 <1 AND |-4| <=10 */ rc = SQLFetchScroll(hstmt,SQL_FETCH_RELATIVE,-4);/* before start */ mystmt_err(hstmt,rc==SQL_NO_DATA_FOUND,rc); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(SQLPOINTER)1,0); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_relative2"); return OK; } DECLARE_TEST(t_rows_fetched_ptr) { SQLRETURN rc; SQLULEN rowsFetched, rowsSize; long i; ok_sql(hstmt, "DROP TABLE IF EXISTS t_rows_fetched_ptr"); ok_sql(hstmt,"create table t_rows_fetched_ptr(a int)"); ok_sql(hstmt,"insert into t_rows_fetched_ptr values(0)"); ok_sql(hstmt,"insert into t_rows_fetched_ptr values(1)"); ok_sql(hstmt,"insert into t_rows_fetched_ptr values(2)"); ok_sql(hstmt,"insert into t_rows_fetched_ptr values(3)"); ok_sql(hstmt,"insert into t_rows_fetched_ptr values(4)"); ok_sql(hstmt,"insert into t_rows_fetched_ptr values(5)"); rowsSize= 1; rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)rowsSize, 0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowsFetched, 0); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM t_rows_fetched_ptr"); i= 0; rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { printMessage("\n total rows fetched: %ld", rowsFetched); myassert(rowsFetched == rowsSize); i++; rowsFetched= 0; rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0); } myassert( i == 6); SQLFreeStmt(hstmt, SQL_CLOSE); rowsSize= 2; rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)rowsSize, 0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowsFetched, 0); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM t_rows_fetched_ptr"); mystmt(hstmt,rc); i= 0; rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { printMessage("\n total rows fetched: %ld", rowsFetched); myassert(rowsFetched == rowsSize); i++;rowsFetched= 0; rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0); } myassert( i == 3); SQLFreeStmt(hstmt, SQL_CLOSE); rowsSize= 3; rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)rowsSize, 0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowsFetched, 0); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM t_rows_fetched_ptr"); mystmt(hstmt,rc); i= 0; rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { printMessage("\n total rows fetched: %ld", rowsFetched); myassert(rowsFetched == rowsSize); i++;rowsFetched= 0; rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0); } myassert( i == 2); SQLFreeStmt(hstmt, SQL_CLOSE); rowsSize= 4; rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)rowsSize, 0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowsFetched, 0); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM t_rows_fetched_ptr"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("\n total rows fetched: %ld", rowsFetched); myassert(rowsFetched == rowsSize); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("\n total rows fetched: %ld", rowsFetched); myassert(rowsFetched == 2); rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA); SQLFreeStmt(hstmt, SQL_CLOSE); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0);/* reset */ mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, NULL, 0); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_rows_fetched_ptr"); return OK; } DECLARE_TEST(t_rows_fetched_ptr1) { SQLRETURN rc; SQLULEN rowsFetched, rowsSize; SQLINTEGER i; ok_sql(hstmt, "DROP TABLE IF EXISTS t_rows_fetched_ptr"); ok_sql(hstmt, "create table t_rows_fetched_ptr(a int)"); ok_sql(hstmt, "insert into t_rows_fetched_ptr values(0)"); ok_sql(hstmt, "insert into t_rows_fetched_ptr values(1)"); ok_sql(hstmt, "insert into t_rows_fetched_ptr values(2)"); ok_sql(hstmt, "insert into t_rows_fetched_ptr values(3)"); ok_sql(hstmt, "insert into t_rows_fetched_ptr values(4)"); ok_sql(hstmt, "insert into t_rows_fetched_ptr values(5)"); rowsSize= 1; rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)rowsSize, 0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowsFetched, 0); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM t_rows_fetched_ptr"); mystmt(hstmt,rc); i= 0; rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { fprintf(stdout,"total rows fetched: %ld\n", rowsFetched); myassert(rowsFetched == rowsSize); i++; rowsFetched= 0; rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0); } myassert( i == 6); SQLFreeStmt(hstmt, SQL_CLOSE); rowsSize= 2; rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)rowsSize, 0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowsFetched, 0); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM t_rows_fetched_ptr"); mystmt(hstmt,rc); i= 0; rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { fprintf(stdout,"total rows fetched: %ld\n", rowsFetched); myassert(rowsFetched == rowsSize); i++;rowsFetched= 0; rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0); } myassert( i == 3); SQLFreeStmt(hstmt, SQL_CLOSE); rowsSize= 3; rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)rowsSize, 0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowsFetched, 0); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM t_rows_fetched_ptr"); mystmt(hstmt,rc); i= 0; rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { printMessage("total rows fetched: %ld\n", rowsFetched); myassert(rowsFetched == rowsSize); i++;rowsFetched= 0; rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0); } myassert( i == 2); SQLFreeStmt(hstmt, SQL_CLOSE); rowsSize= 4; rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)rowsSize, 0); mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &rowsFetched, 0); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT * FROM t_rows_fetched_ptr"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("total rows fetched: %ld\n", rowsFetched); myassert(rowsFetched == rowsSize); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("total rows fetched: %ld\n", rowsFetched); myassert(rowsFetched == 2); rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA); SQLFreeStmt(hstmt, SQL_CLOSE); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0);/* reset */ mystmt(hstmt,rc); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, NULL, 0); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_rows_fetched_ptr"); return OK; } BEGIN_TESTS ADD_TEST(t_relative) ADD_TEST(t_relative1) ADD_TEST(t_relative2) ADD_TEST(t_rows_fetched_ptr) ADD_TEST(t_rows_fetched_ptr1) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_blob.c100644 15766 12 54077 11707541005 17771 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" DECLARE_TEST(t_blob) { SQLRETURN rc; SQLUINTEGER j= 0; SQLINTEGER l; SQLLEN cbValue; SQLCHAR *blobbuf; SQLUINTEGER blobbuf_size = 1024 * 1 * 6L; SQLUINTEGER blob_read; SQLPOINTER token; clock_t start, finish; double duration; SQLUINTEGER blob_size = 1 * 1024L * 5L; rc = SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, 0L); mycon(hdbc,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS TBLOB"); ok_sql(hstmt, "CREATE TABLE TBLOB (I INTEGER NOT NULL PRIMARY KEY," "B LONGBLOB)"); cbValue = 0; ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"INSERT INTO TBLOB VALUES (1, ?)", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, SQL_PARAM_INPUT, 1, SQL_C_BINARY, SQL_LONGVARBINARY, blob_size, 0, NULL, 0, &cbValue)); cbValue = SQL_DATA_AT_EXEC; blobbuf = (SQLCHAR *)malloc(blobbuf_size); memset(blobbuf, 'A', blobbuf_size); start = clock(); expect_stmt(hstmt, SQLExecute(hstmt), SQL_NEED_DATA); expect_stmt(hstmt, SQLParamData(hstmt, &token), SQL_NEED_DATA); { for (j = 0; j < blob_size; ) { SDWORD s; s = (SDWORD)blobbuf_size; if (s + j > blob_size) { s -= (s + j) - blob_size; myassert(s + j == blob_size); } rc = SQLPutData(hstmt, blobbuf, s); mystmt(hstmt,rc); j += (SQLUINTEGER)s; } rc = SQLParamData(hstmt, &token); mystmt(hstmt,rc); } finish = clock(); duration = (finish-start)/CLOCKS_PER_SEC; printMessage("j: %d", j); myassert(j == blob_size); printMessage("Wrote %ld bytes in %3.3lf seconds (%lg bytes/s)", j, duration, duration == 0.0 ? 9.99e99 : j / duration); rc = SQLTransact(NULL, hdbc, SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt, SQL_RESET_PARAMS); mystmt(hstmt,rc); memset(blobbuf, ~0, 100); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"SELECT I, B FROM TBLOB WHERE I = 1", SQL_NTS)); start = clock(); rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_LONG, &l, 0L, &cbValue); mystmt(hstmt,rc); blob_read = 0L; do { rc = SQLGetData(hstmt, 2, SQL_C_BINARY, blobbuf, blobbuf_size, &cbValue); myassert(cbValue > 0); blob_read += (cbValue < blobbuf_size ? cbValue : blobbuf_size); } while (rc == SQL_SUCCESS_WITH_INFO); myassert(rc == SQL_SUCCESS); myassert(blob_read == blob_size); finish = clock(); duration = (finish-start)/CLOCKS_PER_SEC; printMessage("Read %ld bytes in %3.3lf seconds (%lg bytes/s)", blob_read, duration, duration == 0.0 ? 9.99e99 : blob_read / duration); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); free(blobbuf); ok_sql(hstmt, "DROP TABLE IF EXISTS TBLOB"); return OK; } DECLARE_TEST(t_1piecewrite2) { SQLRETURN rc; SQLLEN cbValue,cbValue2; SQLINTEGER l; SQLCHAR* blobbuf; size_t i; ok_sql(hstmt, "DROP TABLE IF EXISTS TBLOB"); ok_sql(hstmt, "CREATE TABLE TBLOB (I INTEGER NOT NULL PRIMARY KEY," "B LONG VARCHAR NOT NULL)"); cbValue = 3510L; blobbuf = (SQLCHAR *)malloc((size_t)cbValue + 1); for (i = 0; i < (size_t)cbValue; i++) { blobbuf[i] = (char)((i % ('z' - 'a' + 1)) + 'a'); } blobbuf[i] = '\0'; l = 1; rc = SQLBindParameter(hstmt,SQL_PARAM_INPUT,1, SQL_C_LONG, SQL_INTEGER, 0, 0, &l,0, NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,SQL_PARAM_INPUT, 2, SQL_C_CHAR, SQL_LONGVARCHAR, 0, 0, blobbuf,cbValue, NULL); mystmt(hstmt,rc); ok_sql(hstmt, "INSERT INTO TBLOB VALUES (1,?)"); mystmt(hstmt,rc); rc = SQLTransact(NULL, hdbc, SQL_COMMIT); mycon(hdbc,rc); memset(blobbuf, 1, (size_t)cbValue); rc = SQLFreeStmt(hstmt, SQL_RESET_PARAMS); mystmt(hstmt,rc); ok_sql(hstmt, "SELECT B FROM TBLOB WHERE I = 1"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_BINARY, blobbuf, cbValue, &cbValue2); mystmt(hstmt,rc); myassert(cbValue2 == cbValue); for (i = 0; i < (size_t)cbValue; i++) { myassert(blobbuf[i] == (char)((i % ('z' - 'a' + 1)) + 'a')); } rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTransact(NULL, hdbc, SQL_COMMIT); mycon(hdbc,rc); free(blobbuf); ok_sql(hstmt, "DROP TABLE IF EXISTS TBLOB"); return OK; } /* Test for a simple SQLPutData and SQLParamData handling for longtext */ DECLARE_TEST(t_putdata) { SQLRETURN rc; SQLLEN pcbLength; SQLINTEGER c1; SQLCHAR data[255]; SQLPOINTER token; ok_sql(hstmt, "DROP TABLE IF EXISTS t_putdata"); ok_sql(hstmt, "CREATE TABLE t_putdata (c1 INT, c2 LONG VARCHAR)"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_putdata values(?,?)", SQL_NTS)); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_LONG, SQL_INTEGER,0,0,&c1,0,NULL); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT,SQL_C_CHAR, SQL_LONGVARCHAR,0,0, (SQLPOINTER)1,0,&pcbLength); pcbLength = SQL_LEN_DATA_AT_EXEC(0); c1 = 10; rc = SQLExecute(hstmt); myassert(rc == SQL_NEED_DATA); rc = SQLParamData(hstmt, &token); myassert(rc == SQL_NEED_DATA); strcpy((char *)data,"mysql ab"); rc = SQLPutData(hstmt,data,6); mystmt(hstmt,rc); strcpy((char *)data,"- the open source database company"); rc = SQLPutData(hstmt,data,strlen((char *)data)); mystmt(hstmt,rc); rc = SQLParamData(hstmt, &token); mystmt(hstmt,rc); SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "select c2 from t_putdata where c1= 10"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); pcbLength= 0; rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &pcbLength); mystmt(hstmt,rc); printMessage("data: %s(%ld)", data, pcbLength); is_str(data, "mysql - the open source database company", 40); myassert(pcbLength == 40); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS t_putdata"); return OK; } /* Test for a simple SQLPutData and SQLParamData handling for longtext */ DECLARE_TEST(t_putdata1) { SQLRETURN rc; SQLLEN pcbLength; SQLINTEGER c1; SQLCHAR data[255]; SQLPOINTER token; ok_sql(hstmt, "DROP TABLE IF EXISTS t_putdata"); ok_sql(hstmt, "CREATE TABLE t_putdata (c1 INT, c2 LONG VARCHAR)"); ok_sql(hstmt, "INSERT INTO t_putdata VALUES (10,'venu')"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"UPDATE t_putdata SET c2= ? WHERE c1 = ?", SQL_NTS)); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_CHAR, SQL_LONGVARCHAR,0,0, (SQLPOINTER)1,0,&pcbLength); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT,SQL_C_LONG, SQL_INTEGER,0,0,&c1,0,NULL); pcbLength = SQL_LEN_DATA_AT_EXEC(0); c1 = 10; rc = SQLExecute(hstmt); myassert(rc == SQL_NEED_DATA); rc = SQLParamData(hstmt, &token); myassert(rc == SQL_NEED_DATA); strcpy((char *)data,"mysql ab"); rc = SQLPutData(hstmt,data,6); mystmt(hstmt,rc); strcpy((char *)data,"- the open source database company"); rc = SQLPutData(hstmt,data,strlen((char *)data)); mystmt(hstmt,rc); rc = SQLParamData(hstmt, &token); mystmt(hstmt,rc); SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "select c2 from t_putdata where c1= 10"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); pcbLength= 0; rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &pcbLength); mystmt(hstmt,rc); printMessage("data: %s(%ld)", data, pcbLength); is_str(data,"mysql - the open source database company", 40); myassert(pcbLength == 40); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS t_putdata"); return OK; } /* Test for a simple SQLPutData and SQLParamData handling for longtext */ DECLARE_TEST(t_putdata2) { SQLRETURN rc; SQLLEN pcbLength; SQLINTEGER c1; SQLCHAR data[255]; SQLPOINTER token; ok_sql(hstmt, "DROP TABLE IF EXISTS t_putdata"); ok_sql(hstmt, "CREATE TABLE t_putdata (c1 INT, c2 LONG VARCHAR," "c3 LONG VARCHAR)"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_putdata values(?,?,?)", SQL_NTS)); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_LONG, SQL_INTEGER,0,0,&c1,0,NULL); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT,SQL_C_CHAR, SQL_LONGVARCHAR,0,0, (SQLPOINTER)1,0,&pcbLength); rc = SQLBindParameter(hstmt,3,SQL_PARAM_INPUT,SQL_C_CHAR, SQL_LONGVARCHAR,0,0, (SQLPOINTER)1,0,&pcbLength); pcbLength = SQL_LEN_DATA_AT_EXEC(0); c1 = 10; rc = SQLExecute(hstmt); myassert(rc == SQL_NEED_DATA); rc = SQLParamData(hstmt, &token); myassert(rc == SQL_NEED_DATA); strcpy((char *)data,"mysql ab"); rc = SQLPutData(hstmt,data,6); mystmt(hstmt,rc); strcpy((char *)data,"- the open source database company"); rc = SQLPutData(hstmt,data,strlen((char *)data)); mystmt(hstmt,rc); rc = SQLParamData(hstmt, &token); myassert(rc == SQL_NEED_DATA); strcpy((char *)data,"MySQL AB"); rc = SQLPutData(hstmt,data, 8); mystmt(hstmt,rc); rc = SQLParamData(hstmt, &token); mystmt(hstmt,rc); SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "select c2,c3 from t_putdata where c1= 10"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); pcbLength= 0; rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &pcbLength); mystmt(hstmt,rc); printMessage("data: %s(%ld)", data, pcbLength); is_str(data, "mysql - the open source database company", 40); myassert(pcbLength == 40); pcbLength= 0; rc = SQLGetData(hstmt, 2, SQL_C_CHAR, data, sizeof(data), &pcbLength); mystmt(hstmt,rc); printMessage("data: %s(%ld)", data, pcbLength); is_str(data, "MySQL AB", 8); myassert(pcbLength == 8); SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS t_putdata"); return OK; } /* Test for a simple SQLPutData and SQLParamData handling bug #1316 */ DECLARE_TEST(t_putdata3) { SQLRETURN rc; SQLINTEGER id, id1, id2, id3; SQLLEN resId, resUTimeSec, resUTimeMSec, resDataLen, resData; SQLCHAR buffer[]= "MySQL - The worlds's most popular open source database"; const int MAX_PART_SIZE = 5; SQLCHAR data[50]; int commonLen= 20; ok_sql(hstmt, "DROP TABLE IF EXISTS t_putdata3"); ok_sql(hstmt, "CREATE TABLE t_putdata3 (id INT, id1 INT, id2 INT, id3 INT, b BLOB)"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "INSERT INTO t_putdata3 VALUES (?, ?, ?, ?, ?)", SQL_NTS)); id= 1, id1= 2, id2= 3, id3= 4; resId= 0; resUTimeSec= resUTimeMSec= 0; resDataLen= 0; resData= SQL_LEN_DATA_AT_EXEC(0); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &id, 0, &resId)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &id1, 0, &resUTimeSec)); ok_stmt(hstmt, SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &id2, 0, &resUTimeMSec)); ok_stmt(hstmt, SQLBindParameter(hstmt, 4, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &id3, 0, &resDataLen)); ok_stmt(hstmt, SQLBindParameter(hstmt, 5, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, 10, 10, (SQLPOINTER)5, 0, &resData)); rc= SQLExecute(hstmt); if (rc == SQL_NEED_DATA) { SQLPOINTER parameter; if (SQLParamData(hstmt, ¶meter) == SQL_NEED_DATA && parameter == (SQLPOINTER)5) { int len= 0, partsize; /* storing long data by parts */ while (len < commonLen) { partsize= commonLen - len; if (partsize > MAX_PART_SIZE) partsize= MAX_PART_SIZE; ok_stmt(hstmt, SQLPutData(hstmt, buffer + len, partsize)); len+= partsize; } if (SQLParamData(hstmt, ¶meter) == SQL_ERROR) { } } } /* end if (rc == SQL_NEED_DATA) */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); if (mysql_min_version(hdbc, "4.0", 3)) { ok_sql(hstmt, "SELECT id, id1, id2, id3, CONVERT(b, CHAR) FROM t_putdata3"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); is_num(my_fetch_int(hstmt, 2), 2); is_num(my_fetch_int(hstmt, 3), 3); is_num(my_fetch_int(hstmt, 4), 4); is_str(my_fetch_str(hstmt, data, 5), buffer, commonLen); } else { ok_sql(hstmt, "SELECT id, id1, id2, id3, b FROM t_putdata3"); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 1), 1); is_num(my_fetch_int(hstmt, 2), 2); is_num(my_fetch_int(hstmt, 3), 3); is_num(my_fetch_int(hstmt, 4), 4); is_str(my_fetch_str(hstmt, data, 5), "4D7953514C202D2054686520776F726C64732773", commonLen); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_putdata3"); return OK; } /* Test the bug when blob size > 8k */ DECLARE_TEST(t_blob_bug) { SQLRETURN rc; SQLCHAR *data; SQLINTEGER i, val; SQLLEN length; const SQLINTEGER max_blob_size=1024*100; ok_sql(hstmt, "DROP TABLE IF EXISTS t_blob"); ok_sql(hstmt, "CREATE TABLE t_blob (blb LONG VARBINARY)"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"INSERT INTO t_blob VALUES (?)",SQL_NTS)); if (!(data = (SQLCHAR *)calloc(max_blob_size,sizeof(SQLCHAR)))) { SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); return FAIL; } rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_VARBINARY, 0,0,data,0,&length); mystmt(hstmt,rc); memset(data,'X',max_blob_size); for (length=1024; length <= max_blob_size; length+= 1024) { rc = SQLExecute(hstmt); mystmt(hstmt,rc); } SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "SELECT length(blb) FROM t_blob"); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&val,0,NULL); mystmt(hstmt,rc); for (i= 1; i <= max_blob_size/1024; i++) { rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("row %d length: %d", i, val); myassert(val == i * 1024); } rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA); free(data); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS t_blob"); return OK; } #define TEST_ODBC_TEXT_LEN 3000 DECLARE_TEST(t_text_fetch) { SQLRETURN rc; SQLINTEGER i; SQLLEN row_count, length; SQLCHAR data[TEST_ODBC_TEXT_LEN+1]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_text_fetch"); ok_sql(hstmt, "CREATE TABLE t_text_fetch(t1 tinytext," "t2 text, t3 mediumtext, t4 longtext)"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_text_fetch values(?,?,?,?)", SQL_NTS)); rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0,0,(char *)data, TEST_ODBC_TEXT_LEN/3, NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0,0,(char *)data, TEST_ODBC_TEXT_LEN/2, NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0,0,(char *)data, (SQLINTEGER)(TEST_ODBC_TEXT_LEN/1.5), NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0,0,(char *)data, TEST_ODBC_TEXT_LEN-1, NULL); mystmt(hstmt,rc); memset(data,'A',TEST_ODBC_TEXT_LEN); data[TEST_ODBC_TEXT_LEN]='\0'; for (i=0; i < 10; i++) { rc = SQLExecute(hstmt); mystmt(hstmt,rc); } SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "SELECT * FROM t_text_fetch"); row_count= 0; rc = SQLFetch(hstmt); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { printf("# row '%ld' (lengths:", row_count); rc = SQLGetData(hstmt,1,SQL_C_CHAR,(char *)data,TEST_ODBC_TEXT_LEN,&length); mystmt(hstmt,rc); printf("%ld", length); myassert(length == 255); rc = SQLGetData(hstmt,2,SQL_C_CHAR,(char *)data,TEST_ODBC_TEXT_LEN,&length); mystmt(hstmt,rc); printf(",%ld", length); myassert(length == TEST_ODBC_TEXT_LEN/2); rc = SQLGetData(hstmt,3,SQL_C_CHAR,(char *)data,TEST_ODBC_TEXT_LEN,&length); mystmt(hstmt,rc); printf(",%ld", length); myassert(length == (SQLINTEGER)(TEST_ODBC_TEXT_LEN/1.5)); rc = SQLGetData(hstmt,4,SQL_C_CHAR,(char *)data,TEST_ODBC_TEXT_LEN,&length); mystmt(hstmt,rc); printf(",%ld)\n", length); myassert(length == TEST_ODBC_TEXT_LEN-1); row_count++; rc = SQLFetch(hstmt); } printMessage("total rows: %ld", row_count); myassert(row_count == i); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP TABLE t_text_fetch"); return OK; } /** Test retrieving the length of a field with a non-null zero-length buffer. This is how ADO does it for long-type fields. */ DECLARE_TEST(getdata_lenonly) { SQLLEN len; SQLCHAR buf[1]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_getdata_lenonly"); ok_sql(hstmt, "CREATE TABLE t_getdata_lenonly (a CHAR(4))"); ok_sql(hstmt, "INSERT INTO t_getdata_lenonly VALUES ('venu')"); ok_sql(hstmt, "SELECT a FROM t_getdata_lenonly"); ok_stmt(hstmt, SQLFetch(hstmt)); expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, buf, 0, &len), SQL_SUCCESS_WITH_INFO); is_num(len, 4); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_getdata_lenonly"); return OK; } /** Bug #9781: returned SQL_Type on WKB query */ DECLARE_TEST(t_bug9781) { SQLSMALLINT name_length, data_type, decimal_digits, nullable; SQLCHAR column_name[SQL_MAX_COLUMN_NAME_LEN]; SQLULEN column_size; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug9781"); ok_sql(hstmt, "CREATE TABLE t_bug9781 (g GEOMETRY)"); ok_sql(hstmt, "INSERT INTO t_bug9781 VALUES (GeomFromText('POINT(0 0)'))"); ok_sql(hstmt, "SELECT AsBinary(g) FROM t_bug9781"); ok_stmt(hstmt, SQLDescribeCol(hstmt, 1, column_name, sizeof(column_name), &name_length, &data_type, &column_size, &decimal_digits, &nullable)); is_num(data_type, SQL_LONGVARBINARY); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug9781"); return OK; } /* * Bug #10562 - Large blobs fail in a cursor */ DECLARE_TEST(t_bug10562) { SQLLEN bsize = 12 * 1024; /* Test to just insert 12k blob */ SQLCHAR *blob = malloc(bsize); SQLCHAR *blobcheck = malloc(bsize); memset(blob, 'X', bsize); ok_sql(hstmt, "drop table if exists t_bug10562"); ok_sql(hstmt, "create table t_bug10562 ( id int not null primary key DEFAULT 0, mb longblob )"); ok_sql(hstmt, "insert into t_bug10562 (mb) values ('zzzzzzzzzz')"); ok_sql(hstmt, "select id, mb from t_bug10562"); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_BINARY, blob, bsize, &bsize)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_UPDATE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Get the data back out to verify */ ok_sql(hstmt, "select mb from t_bug10562"); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_BINARY, blobcheck, bsize, NULL)); is(!memcmp(blob, blobcheck, bsize)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_bug10562"); free(blob); free(blobcheck); return OK; } BEGIN_TESTS ADD_TEST(t_blob) ADD_TEST(t_1piecewrite2) ADD_TEST(t_putdata) ADD_TEST(t_putdata1) ADD_TEST(t_putdata2) ADD_TEST(t_putdata3) ADD_TEST(t_blob_bug) ADD_TEST(t_text_fetch) ADD_TEST(getdata_lenonly) ADD_TEST(t_bug9781) ADD_TEST(t_bug10562) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_bulk.c100644 15766 12 22440 11707541005 17775 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" #define MAX_INSERT_COUNT 800 DECLARE_TEST(t_bulk_insert) { SQLINTEGER i, id[MAX_INSERT_COUNT+1]; SQLCHAR name[MAX_INSERT_COUNT][40], txt[MAX_INSERT_COUNT][60], ltxt[MAX_INSERT_COUNT][70]; SQLDOUBLE dt, dbl[MAX_INSERT_COUNT]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bulk_insert"); ok_sql(hstmt, "CREATE TABLE t_bulk_insert (id INT, v VARCHAR(100)," "txt TEXT, ft FLOAT(10), ltxt LONG VARCHAR)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); dt= 0.23456; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)MAX_INSERT_COUNT, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_ROWVER, 0)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, id, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, name, sizeof(name[0]), NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_CHAR, txt, sizeof(txt[0]), NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 4, SQL_C_DOUBLE, dbl, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 5, SQL_C_CHAR, ltxt, sizeof(ltxt[0]), NULL)); ok_sql(hstmt, "SELECT id, v, txt, ft, ltxt FROM t_bulk_insert"); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0), SQL_NO_DATA_FOUND); for (i= 0; i < MAX_INSERT_COUNT; i++) { id[i]= i; dbl[i]= i + dt; sprintf((char *)name[i], "Varchar%d", i); sprintf((char *)txt[i], "Text%d", i); sprintf((char *)ltxt[i], "LongText, id row:%d", i); } ok_stmt(hstmt, SQLBulkOperations(hstmt, SQL_ADD)); ok_stmt(hstmt, SQLBulkOperations(hstmt, SQL_ADD)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0)); ok_sql(hstmt, "SELECT * FROM t_bulk_insert"); is_num(myrowcount(hstmt), MAX_INSERT_COUNT * 2); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bulk_insert"); return OK; } DECLARE_TEST(t_mul_pkdel) { SQLINTEGER nData; SQLLEN nlen; SQLULEN pcrow; ok_sql(hstmt, "DROP TABLE IF EXISTS t_mul_pkdel"); ok_sql(hstmt, "CREATE TABLE t_mul_pkdel (a INT NOT NULL, b INT," "c VARCHAR(30) NOT NULL, PRIMARY KEY(a, c))"); ok_sql(hstmt, "INSERT INTO t_mul_pkdel VALUES (100,10,'MySQL1')," "(200,20,'MySQL2'),(300,20,'MySQL3'),(400,20,'MySQL4')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu", SQL_NTS)); ok_sql(hstmt, "SELECT a, c FROM t_mul_pkdel"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, NULL)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &pcrow, NULL)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_DELETE, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLRowCount(hstmt, &nlen)); is_num(nlen, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_mul_pkdel"); is_num(myrowcount(hstmt), 3); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_mul_pkdel"); return OK; } /** Bug #24306: SQLBulkOperations always uses indicator varables' values from the first record */ DECLARE_TEST(t_bulk_insert_indicator) { SQLINTEGER id[4], nData; SQLLEN indicator[4], nLen; ok_sql(hstmt, "DROP TABLE IF EXISTS my_bulk"); ok_sql(hstmt, "CREATE TABLE my_bulk (id int default 5)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)3, 0)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, id, 0, indicator)); ok_sql(hstmt, "SELECT id FROM my_bulk"); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0), SQL_NO_DATA_FOUND); id[0]= 1; indicator[0]= SQL_COLUMN_IGNORE; id[1]= 2; indicator[1]= SQL_NULL_DATA; id[2]= 3; indicator[2]= 0; ok_stmt(hstmt, SQLBulkOperations(hstmt, SQL_ADD)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0)); ok_sql(hstmt, "SELECT id FROM my_bulk"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, &nLen)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(nData, 5); my_assert(nLen != SQL_NULL_DATA); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(nLen, SQL_NULL_DATA); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(nData, 3); my_assert(nLen != SQL_NULL_DATA); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS my_bulk"); return OK; } /** Simple structure for a row (just one element) plus an indicator column. */ typedef struct { SQLINTEGER val; SQLLEN ind; } row; /** This is related to the fix for Bug #24306 -- handling of row-wise binding, plus handling of SQL_ATTR_ROW_BIND_OFFSET_PTR, within the context of SQLBulkOperations(hstmt, SQL_ADD). */ DECLARE_TEST(t_bulk_insert_rows) { row rows[3]; SQLINTEGER nData; SQLLEN nLen, offset; ok_sql(hstmt, "DROP TABLE IF EXISTS my_bulk"); ok_sql(hstmt, "CREATE TABLE my_bulk (id int default 5)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)3, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE, (SQLPOINTER)sizeof(row), 0)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &rows[0].val, 0, &rows[0].ind)); ok_sql(hstmt, "SELECT id FROM my_bulk"); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0), SQL_NO_DATA_FOUND); rows[0].val= 1; rows[0].ind= SQL_COLUMN_IGNORE; rows[1].val= 2; rows[1].ind= SQL_NULL_DATA; rows[2].val= 3; rows[2].ind= 0; ok_stmt(hstmt, SQLBulkOperations(hstmt, SQL_ADD)); /* Now re-insert the last row using SQL_ATTR_ROW_BIND_OFFSET_PTR */ ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, (SQLPOINTER)&offset, 0)); offset= 2 * sizeof(row); ok_stmt(hstmt, SQLBulkOperations(hstmt, SQL_ADD)); /* Remove SQL_ATTR_ROW_BIND_OFFSET_PTR */ ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, NULL, 0)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT id FROM my_bulk"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, &nLen)); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(nData, 5); my_assert(nLen != SQL_NULL_DATA); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(nLen, SQL_NULL_DATA); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(nData, 3); my_assert(nLen != SQL_NULL_DATA); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)); is_num(nData, 3); my_assert(nLen != SQL_NULL_DATA); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS my_bulk"); return OK; } BEGIN_TESTS ADD_TEST(t_bulk_insert) ADD_TEST(t_mul_pkdel) ADD_TEST(t_bulk_insert_indicator) ADD_TEST(t_bulk_insert_rows) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_error.c100644 15766 12 34530 11707541005 20174 0ustar00cteamstaff/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" DECLARE_TEST(t_odbc3_error) { SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1; SQLINTEGER ov_version; ok_env(henv1, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv1)); ok_env(henv1, SQLSetEnvAttr(henv1, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0)); ok_env(henv1, SQLAllocHandle(SQL_HANDLE_DBC, henv1, &hdbc1)); ok_env(henv1, SQLGetEnvAttr(henv1, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)&ov_version, 0, 0)); is_num(ov_version, SQL_OV_ODBC3); ok_con(hdbc1, SQLConnect(hdbc1, mydsn, SQL_NTS, myuid, SQL_NTS, mypwd, SQL_NTS)); ok_con(hdbc1, SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1)); expect_sql(hstmt1, "SELECT * FROM non_existing_table", SQL_ERROR); if (check_sqlstate(hstmt1, "42S02") != OK) return FAIL; ok_sql(hstmt1, "DROP TABLE IF EXISTS t_error"); ok_sql(hstmt1, "CREATE TABLE t_error (id INT)"); expect_sql(hstmt1, "CREATE TABLE t_error (id INT)", SQL_ERROR); if (check_sqlstate(hstmt1, "42S01") != OK) return FAIL; ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); expect_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_FETCH_BOOKMARK_PTR, (SQLPOINTER)NULL, 0), SQL_ERROR); if (check_sqlstate(hstmt1, "HYC00") != OK) return FAIL; ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_error"); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); ok_env(henv1, SQLFreeHandle(SQL_HANDLE_ENV, henv1)); return OK; } DECLARE_TEST(t_odbc2_error) { SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1; SQLINTEGER ov_version; ok_env(henv1, SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv1)); ok_env(henv1, SQLSetEnvAttr(henv1, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC2, 0)); ok_env(henv1, SQLAllocHandle(SQL_HANDLE_DBC, henv1, &hdbc1)); ok_env(henv1, SQLGetEnvAttr(henv1, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)&ov_version, 0, 0)); is_num(ov_version, SQL_OV_ODBC2); ok_con(hdbc1, SQLConnect(hdbc1, mydsn, SQL_NTS, myuid, SQL_NTS, mypwd, SQL_NTS)); ok_con(hdbc1, SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1)); expect_sql(hstmt1, "SELECT * FROM non_existing_table", SQL_ERROR); if (check_sqlstate(hstmt1, "S0002") != OK) return FAIL; ok_sql(hstmt1, "DROP TABLE IF EXISTS t_error"); ok_sql(hstmt1, "CREATE TABLE t_error (id INT)"); expect_sql(hstmt1, "CREATE TABLE t_error (id INT)", SQL_ERROR); if (check_sqlstate(hstmt1, "S0001") != OK) return FAIL; ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); expect_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_ENABLE_AUTO_IPD, (SQLPOINTER)SQL_TRUE, 0), SQL_ERROR); if (check_sqlstate(hstmt1, "S1C00") != OK) return FAIL; ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_error"); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); ok_env(henv1, SQLFreeHandle(SQL_HANDLE_ENV, henv1)); return OK; } DECLARE_TEST(t_diagrec) { SQLCHAR sqlstate[6]= {0}; SQLCHAR message[255]= {0}; SQLINTEGER native_err= 0; SQLSMALLINT msglen= 0; expect_sql(hstmt, "DROP TABLE t_odbc3_non_existent_table", SQL_ERROR); #if UNIXODBC_BUG_FIXED /* This should report no data found, but unixODBC doesn't even pass this down to the driver. */ expect_stmt(hstmt, SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 2, sqlstate, &native_err, message, 0, &msglen), SQL_NO_DATA_FOUND); #endif ok_stmt(hstmt, SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, sqlstate, &native_err, message, 255, &msglen)); expect_stmt(hstmt, SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, sqlstate, &native_err, message, 0, &msglen), SQL_SUCCESS_WITH_INFO); expect_stmt(hstmt, SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, sqlstate, &native_err, message, 10, &msglen), SQL_SUCCESS_WITH_INFO); expect_stmt(hstmt, SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, sqlstate, &native_err, message, -1, &msglen), SQL_ERROR); return OK; } DECLARE_TEST(t_warning) { SQLCHAR szData[20]; SQLLEN pcbValue; ok_sql(hstmt, "DROP TABLE IF EXISTS t_warning"); ok_sql(hstmt, "CREATE TABLE t_warning (col2 CHAR(20))"); ok_sql(hstmt, "INSERT INTO t_warning VALUES ('Venu Anuganti')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_ROWVER, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_KEYSET_DRIVEN, 0)); /* ignore all columns */ ok_sql(hstmt, "SELECT * FROM t_warning"); ok_stmt(hstmt, SQLFetch(hstmt)); expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, szData, 4, &pcbValue), SQL_SUCCESS_WITH_INFO); is_str(szData, "Ven", 3); is_num(pcbValue, 13); expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, szData, 4, &pcbValue), SQL_SUCCESS_WITH_INFO); is_str(szData, "u A", 3); is_num(pcbValue, 10); expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, szData, 4, &pcbValue), SQL_SUCCESS_WITH_INFO); is_str(szData, "nug", 3); is_num(pcbValue, 7); expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, szData, 4, &pcbValue), SQL_SUCCESS_WITH_INFO); is_str(szData, "ant", 3); is_num(pcbValue, 4); expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, szData, 4, &pcbValue), SQL_SUCCESS); is_str(szData, "i", 1); is_num(pcbValue, 1); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_warning"); return OK; } DECLARE_TEST(t_bug3456) { SQLINTEGER connection_id; char buf[100]; SQLHENV henv2; SQLHDBC hdbc2; SQLHSTMT hstmt2; /* Create a new connection that we deliberately will kill */ alloc_basic_handles(&henv2, &hdbc2, &hstmt2); ok_sql(hstmt2, "SELECT connection_id()"); ok_stmt(hstmt2, SQLFetch(hstmt2)); connection_id= my_fetch_int(hstmt2, 1); ok_stmt(hstmt2, SQLFreeStmt(hstmt2, SQL_CLOSE)); /* From another connection, kill the connection created above */ sprintf(buf, "KILL %d", connection_id); ok_stmt(hstmt, SQLExecDirect(hstmt, (SQLCHAR *)buf, SQL_NTS)); /* Now check that the connection killed returns the right SQLSTATE */ expect_sql(hstmt2, "SELECT connection_id()", SQL_ERROR); return check_sqlstate(hstmt2, "08S01"); } /* * Bug #16224: Calling SQLGetDiagField with RecNumber 0,DiagIdentifier * NOT 0 returns SQL_ERROR */ DECLARE_TEST(t_bug16224) { SQLINTEGER diagcnt; expect_sql(hstmt, "This is an invalid Query! (odbc test)", SQL_ERROR); ok_stmt(hstmt, SQLGetDiagField(SQL_HANDLE_STMT, hstmt, 0, SQL_DIAG_NUMBER, &diagcnt, SQL_IS_INTEGER, NULL)); is_num(diagcnt, 1); return OK; } /* * Test that binding invalid column returns the appropriate error */ DECLARE_TEST(bind_invalidcol) { SQLCHAR dummy[10]; ok_sql(hstmt, "select 1,2,3,4"); /* test out of range column number */ expect_stmt(hstmt, SQLBindCol(hstmt, 10, SQL_C_CHAR, "", 4, NULL), SQL_ERROR); is_num(check_sqlstate(hstmt, "07009"), OK); /* test (unsupported) bookmark column number */ expect_stmt(hstmt, SQLBindCol(hstmt, 0, SQL_C_BOOKMARK, "", 4, NULL), SQL_ERROR); is_num(check_sqlstate(hstmt, "07009"), OK); /* SQLDescribeCol() */ expect_stmt(hstmt, SQLDescribeCol(hstmt, 0, dummy, sizeof(dummy), NULL, NULL, NULL, NULL, NULL), SQL_ERROR); /* Older versions of iODBC return the wrong result (S1002) here. */ is(check_sqlstate(hstmt, "07009") == OK || check_sqlstate(hstmt, "S1002") == OK); expect_stmt(hstmt, SQLDescribeCol(hstmt, 5, dummy, sizeof(dummy), NULL, NULL, NULL, NULL, NULL), SQL_ERROR); is_num(check_sqlstate(hstmt, "07009"), OK); /* SQLColAttribute() */ expect_stmt(hstmt, SQLColAttribute(hstmt, 0, SQL_DESC_NAME, NULL, 0, NULL, NULL), SQL_ERROR); is_num(check_sqlstate(hstmt, "07009"), OK); expect_stmt(hstmt, SQLColAttribute(hstmt, 7, SQL_DESC_NAME, NULL, 0, NULL, NULL), SQL_ERROR); is_num(check_sqlstate(hstmt, "07009"), OK); return OK; } /* * Test the error given when not enough params are bound to execute * the statement. */ DECLARE_TEST(bind_notenoughparam1) { SQLINTEGER i= 0; ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"select ?, ?", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &i, 0, NULL)); expect_stmt(hstmt, SQLExecute(hstmt), SQL_ERROR); return check_sqlstate(hstmt, "07001"); } /* * Test the error given when not enough params are bound to execute * the statement, also given that a pre-execute happens (due to calling * SQLNumResultCols). */ DECLARE_TEST(bind_notenoughparam2) { SQLINTEGER i= 0; SQLSMALLINT cols= 0; ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"select ?, ?", SQL_NTS)); /* trigger pre-execute */ ok_stmt(hstmt, SQLNumResultCols(hstmt, &cols)); is_num(cols, 2); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &i, 0, NULL)); expect_stmt(hstmt, SQLExecute(hstmt), SQL_ERROR); return check_sqlstate(hstmt, "07001"); } /* * Test that calling SQLGetData() without a nullind ptr * when the data is null returns an error. */ DECLARE_TEST(getdata_need_nullind) { SQLINTEGER i; ok_sql(hstmt, "select 1 as i , null as j "); ok_stmt(hstmt, SQLFetch(hstmt)); /* that that nullind ptr is ok when data isn't null */ ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_LONG, &i, 0, NULL)); /* now that it's an error */ expect_stmt(hstmt, SQLGetData(hstmt, 2, SQL_C_LONG, &i, 0, NULL), SQL_ERROR); return check_sqlstate(hstmt, "22002"); } /* Handle-specific tests for env and dbc diagnostics */ DECLARE_TEST(t_handle_err) { SQLHANDLE henv1, hdbc1; ok_env(henv1, SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv1)); ok_env(henv1, SQLSetEnvAttr(henv1, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER)); ok_env(henv1, SQLAllocHandle(SQL_HANDLE_DBC, henv1, &hdbc1)); /* we have to connect for the DM to pass the calls to the driver */ ok_con(hdbc1, SQLConnect(hdbc1, mydsn, SQL_NTS, myuid, SQL_NTS, mypwd, SQL_NTS)); expect_env(henv1, SQLSetEnvAttr(henv1, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0), SQL_ERROR); is_num(check_sqlstate_ex(henv1, SQL_HANDLE_ENV, "HY010"), OK); expect_dbc(hdbc1, SQLSetConnectAttr(hdbc1, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER), SQL_SUCCESS_WITH_INFO); is_num(check_sqlstate_ex(hdbc1, SQL_HANDLE_DBC, "01S02"), OK); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); ok_env(henv1, SQLFreeHandle(SQL_HANDLE_ENV, henv1)); return OK; } DECLARE_TEST(sqlerror) { SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1]; SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1]; SQLINTEGER error; SQLSMALLINT len; expect_sql(hstmt, "SELECT * FROM tabledoesnotexist", SQL_ERROR); ok_stmt(hstmt, SQLError(henv, hdbc, hstmt, sqlstate, &error, message, sizeof(message), &len)); /* Message has been consumed. */ expect_stmt(hstmt, SQLError(henv, hdbc, hstmt, sqlstate, &error, message, sizeof(message), &len), SQL_NO_DATA_FOUND); /* But should still be available using SQLGetDiagRec. */ ok_stmt(hstmt, SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, sqlstate, &error, message, sizeof(message), &len)); return OK; } /** Bug #27158: MyODBC 3.51/ ADO cmd option adCmdUnknown won't work for tables - regression */ DECLARE_TEST(t_bug27158) { expect_sql(hstmt, "{ CALL test.does_not_exist }", SQL_ERROR); return check_sqlstate(hstmt, "42000"); } DECLARE_TEST(t_bug13542600) { SQLINTEGER i; ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "select 1 as i , null as j "); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_LONG, &i, 0, NULL)); expect_stmt(hstmt, SQLFetch(hstmt), SQL_ERROR); return check_sqlstate(hstmt, "22002"); } BEGIN_TESTS #ifndef NO_DRIVERMANAGER ADD_TEST(t_odbc2_error) ADD_TEST(t_odbc3_error) /* Run twice to test the driver's handling of switching */ ADD_TEST(t_odbc2_error) #endif ADD_TEST(t_diagrec) ADD_TEST(t_warning) ADD_TEST(t_bug3456) ADD_TEST(t_bug16224) ADD_TEST(bind_invalidcol) ADD_TEST(bind_notenoughparam1) ADD_TEST(bind_notenoughparam2) ADD_TEST(getdata_need_nullind) ADD_TEST(t_handle_err) ADD_TEST(sqlerror) ADD_TEST(t_bug27158) ADD_TEST(t_bug13542600) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_result.c100644 15766 12 254074 11707541005 20410 0ustar00cteamstaff/* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" /* result set demo */ DECLARE_TEST(my_resultset) { SQLRETURN rc; SQLUINTEGER nRowCount=0; SQLULEN pcColDef; SQLCHAR szColName[MAX_NAME_LEN+1]; SQLCHAR szData[MAX_ROW_DATA_LEN+1]; SQLSMALLINT nIndex,ncol,pfSqlType, pcbScale, pfNullable; /* drop table 'myodbc3_demo_result' if it already exists */ ok_sql(hstmt, "DROP TABLE if exists myodbc3_demo_result"); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* create the table 'myodbc3_demo_result' */ ok_sql(hstmt,"CREATE TABLE myodbc3_demo_result(" "id int primary key auto_increment,name varchar(20))"); rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* insert 2 rows of data */ ok_sql(hstmt, "INSERT INTO myodbc3_demo_result values(1,'MySQL')"); ok_sql(hstmt, "INSERT INTO myodbc3_demo_result values(2,'MyODBC')"); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* update second row */ ok_sql(hstmt, "UPDATE myodbc3_demo_result set name=" "'MyODBC 3.51' where id=2"); /* commit the transaction */ rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); mycon(hdbc,rc); /* now fetch back..*/ ok_sql(hstmt, "SELECT * from myodbc3_demo_result"); /* get total number of columns from the resultset */ rc = SQLNumResultCols(hstmt,&ncol); mystmt(hstmt,rc); printMessage("total columns in resultset:%d",ncol); /* print the column names and do the row bind */ for (nIndex = 1; nIndex <= ncol; nIndex++) { rc = SQLDescribeCol(hstmt,nIndex,szColName, MAX_NAME_LEN+1, NULL, &pfSqlType,&pcColDef,&pcbScale,&pfNullable); mystmt(hstmt,rc); printf("%s\t",szColName); } printf("\n"); /* now fetch row by row */ rc = SQLFetch(hstmt); while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { nRowCount++; for (nIndex=1; nIndex<= ncol; nIndex++) { rc = SQLGetData(hstmt,nIndex, SQL_C_CHAR, szData, MAX_ROW_DATA_LEN,NULL); mystmt(hstmt,rc); printf("%s\t",szData); } printf("\n"); rc = SQLFetch(hstmt); } SQLFreeStmt(hstmt,SQL_UNBIND); printMessage("total rows fetched:%d",nRowCount); /* free the statement row bind resources */ rc = SQLFreeStmt(hstmt, SQL_UNBIND); mystmt(hstmt,rc); /* free the statement cursor */ rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE myodbc3_demo_result"); return OK; } /* To test a convertion type */ DECLARE_TEST(t_convert_type) { SQLRETURN rc; SQLSMALLINT SqlType, DateType; SQLCHAR ColName[MAX_NAME_LEN]; SQLCHAR DbVersion[MAX_NAME_LEN]; SQLINTEGER OdbcVersion; rc = SQLGetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,&OdbcVersion,0,NULL); myenv(henv,rc); fprintf(stdout,"# odbc version:"); if (OdbcVersion == SQL_OV_ODBC2) { fprintf(stdout," SQL_OV_ODBC2\n"); DateType= SQL_DATE; } else { fprintf(stdout," SQL_OV_ODBC3\n"); DateType= SQL_TYPE_DATE; } rc = SQLGetInfo(hdbc,SQL_DBMS_VER,DbVersion,MAX_NAME_LEN,NULL); mycon(hdbc,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_convert"); ok_sql(hstmt, "CREATE TABLE t_convert(col0 int, col1 date, col2 char(10))"); ok_sql(hstmt, "INSERT INTO t_convert VALUES(10,'2002-10-24','venu')"); ok_sql(hstmt, "INSERT INTO t_convert VALUES(20,'2002-10-23','venu1')"); ok_sql(hstmt, "INSERT INTO t_convert VALUES(30,'2002-10-25','venu2')"); ok_sql(hstmt, "INSERT INTO t_convert VALUES(40,'2002-10-24','venu3')"); ok_sql(hstmt, "SELECT MAX(col0) FROM t_convert"); rc = SQLDescribeCol(hstmt,1,ColName,MAX_NAME_LEN,NULL,&SqlType,NULL,NULL,NULL); mystmt(hstmt,rc); printMessage("MAX(col0): %d", SqlType); myassert(SqlType == SQL_INTEGER); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "SELECT MAX(col1) FROM t_convert"); rc = SQLDescribeCol(hstmt,1,ColName,MAX_NAME_LEN,NULL,&SqlType,NULL,NULL,NULL); mystmt(hstmt,rc); printMessage("MAX(col1): %d", SqlType); myassert(SqlType == DateType); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "SELECT MAX(col2) FROM t_convert"); rc = SQLDescribeCol(hstmt,1,ColName,MAX_NAME_LEN,NULL,&SqlType,NULL,NULL,NULL); mystmt(hstmt,rc); printMessage("MAX(col0): %d", SqlType); SQLFreeStmt(hstmt,SQL_CLOSE); if (strncmp((char *)DbVersion,"4.",2) >= 0) { ok_sql(hstmt, "SELECT CAST(MAX(col1) AS DATE) AS col1 FROM t_convert"); mystmt(hstmt,rc); rc = SQLDescribeCol(hstmt,1,ColName,MAX_NAME_LEN,NULL,&SqlType,NULL,NULL,NULL); mystmt(hstmt,rc); printMessage("CAST(MAX(col1) AS DATE): %d", SqlType); myassert(SqlType == DateType); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "SELECT CONVERT(MAX(col1),DATE) AS col1 FROM t_convert"); mystmt(hstmt,rc); rc = SQLDescribeCol(hstmt,1,ColName,MAX_NAME_LEN,NULL,&SqlType,NULL,NULL,NULL); mystmt(hstmt,rc); printMessage("CONVERT(MAX(col1),DATE): %d", SqlType); myassert(SqlType == DateType); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt,"SELECT CAST(MAX(col1) AS CHAR) AS col1 FROM t_convert"); rc = SQLDescribeCol(hstmt,1,(SQLCHAR *)&ColName,MAX_NAME_LEN,NULL,&SqlType,NULL,NULL,NULL); mystmt(hstmt,rc); printMessage("CAST(MAX(col1) AS CHAR): %d", SqlType); myassert(SqlType == SQL_VARCHAR || SqlType == SQL_LONGVARCHAR || SqlType == SQL_WVARCHAR || SqlType == SQL_WLONGVARCHAR); SQLFreeStmt(hstmt,SQL_CLOSE); } ok_sql(hstmt, "DROP TABLE IF EXISTS t_convert"); return OK; } static SQLINTEGER desc_col_check(SQLHSTMT hstmt, SQLUSMALLINT icol, const char *name, SQLSMALLINT sql_type, SQLULEN col_def, SQLULEN col_def1, SQLSMALLINT scale, SQLSMALLINT nullable) { SQLRETURN rc; SQLSMALLINT pcbColName, pfSqlType, pibScale, pfNullable; SQLULEN pcbColDef; SQLCHAR szColName[MAX_NAME_LEN]; rc = SQLDescribeCol(hstmt, icol, szColName,MAX_NAME_LEN,&pcbColName, &pfSqlType,&pcbColDef,&pibScale,&pfNullable); mystmt(hstmt,rc); printMessage("Column Number'%d':", icol); printMessage(" Column Name : %s", szColName); printMessage(" NameLengh : %d", pcbColName); printMessage(" DataType : %d", pfSqlType); printMessage(" ColumnSize : %d", pcbColDef); printMessage(" DecimalDigits : %d", pibScale); printMessage(" Nullable : %d", pfNullable); is_str(szColName, name, pcbColName); is_num(pfSqlType, sql_type); is(col_def == pcbColDef || col_def1 == pcbColDef); is_num(pibScale, scale); is_num(pfNullable, nullable); return OK; } /* To test SQLDescribeCol */ DECLARE_TEST(t_desc_col) { SQLSMALLINT ColumnCount; ok_sql(hstmt, "DROP TABLE IF EXISTS t_desc_col"); ok_sql(hstmt, "CREATE TABLE t_desc_col(" "c1 integer," "c2 binary(2) NOT NULL," "c3 char(1)," "c4 varchar(5)," "c5 decimal(10,3) NOT NULL," "c6 tinyint," "c7 smallint," "c8 numeric(4,2)," "c9 real," "c10 float(5)," "c11 bigint NOT NULL," "c12 varbinary(12)," "c13 char(20) NOT NULL," "c14 float(10,3)," "c15 tinytext," "c16 text," "c17 mediumtext," "c18 longtext," "c19 tinyblob," "c20 blob," "c21 mediumblob," "c22 longblob," "c23 tinyblob) CHARSET latin1"); ok_sql(hstmt, "SELECT * FROM t_desc_col"); ok_stmt(hstmt, SQLNumResultCols(hstmt, &ColumnCount)); is_num(ColumnCount, 23); is(desc_col_check(hstmt, 1, "c1", SQL_INTEGER, 10, 10, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 2, "c2", SQL_BINARY, 4, 2, 0, SQL_NO_NULLS) == OK); is(desc_col_check(hstmt, 3, "c3", SQL_CHAR, 1, 1, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 4, "c4", SQL_VARCHAR, 5, 5, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 5, "c5", SQL_DECIMAL, 10, 10, 3, SQL_NO_NULLS) == OK); is(desc_col_check(hstmt, 6, "c6", SQL_TINYINT, 3, 4, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 7, "c7", SQL_SMALLINT, 5, 6, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 8, "c8", SQL_DECIMAL, 4, 4, 2, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 9, "c9", SQL_DOUBLE, 15, 15, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 10, "c10", SQL_REAL, 7, 7, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 11, "c11", SQL_BIGINT, 19, 19, 0, SQL_NO_NULLS) == OK); is(desc_col_check(hstmt, 12, "c12", SQL_VARBINARY, 12, 12, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 13, "c13", SQL_CHAR, 20, 20, 0, SQL_NO_NULLS) == OK); is(desc_col_check(hstmt, 14, "c14", SQL_REAL, 7, 7, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 15, "c15", SQL_LONGVARCHAR, 255, 255, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 16, "c16", SQL_LONGVARCHAR, 65535, 65535, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 17, "c17", SQL_LONGVARCHAR, 16777215, 16777215, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 18, "c18", SQL_LONGVARCHAR, 4294967295UL, 16777215 , 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 19, "c19", SQL_LONGVARBINARY, 255, 255, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 20, "c20", SQL_LONGVARBINARY, 65535, 65535, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 21, "c21", SQL_LONGVARBINARY, 16777215, 16777215, 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 22, "c22", SQL_LONGVARBINARY, 4294967295UL, 16777215 , 0, SQL_NULLABLE) == OK); is(desc_col_check(hstmt, 23, "c23", SQL_LONGVARBINARY, 255, 5, 0, SQL_NULLABLE) == OK); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_desc_col"); return OK; } /* Test for misc CONVERT bug #1082 */ DECLARE_TEST(t_convert) { SQLRETURN rc; SQLLEN data_len; SQLCHAR data[50]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_convert"); rc = tmysql_exec(hstmt,"CREATE TABLE t_convert(testing tinytext)"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"INSERT INTO t_convert VALUES('record1')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"INSERT INTO t_convert VALUES('record2')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"SELECT CONCAT(testing, '-must be string') FROM t_convert ORDER BY RAND()"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_CHAR, &data, 100, &data_len); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); myassert(strcmp((char *)data,"record1-must be string") == 0 || strcmp((char *)data,"record2-must be string") == 0); rc = SQLFetch(hstmt); mystmt(hstmt,rc); myassert(strcmp((char *)data,"record1-must be string") == 0 || strcmp((char *)data,"record2-must be string") == 0); rc = SQLFetch(hstmt); myassert( rc == SQL_NO_DATA); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_convert"); return OK; } DECLARE_TEST(t_max_rows) { SQLRETURN rc; SQLUINTEGER i; SQLSMALLINT cc; ok_sql(hstmt, "DROP TABLE IF EXISTS t_max_rows"); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"create table t_max_rows(id int)"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_max_rows values(?)", SQL_NTS)); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_ULONG,SQL_INTEGER,0,0,&i,0,NULL); mystmt(hstmt,rc); for(i=0; i < 10; i++) { rc = SQLExecute(hstmt); mystmt(hstmt,rc); } SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_MAX_ROWS,(SQLPOINTER)0,0); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select count(*) from t_max_rows"); mystmt(hstmt,rc); myassert( 1 == myresult(hstmt) ); SQLFreeStmt(hstmt,SQL_CLOSE); rc = tmysql_exec(hstmt,"select * from t_max_rows"); mystmt(hstmt,rc); myassert( 10 == myresult(hstmt) ); SQLFreeStmt(hstmt,SQL_CLOSE); /* MAX rows through connection attribute */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_MAX_ROWS,(SQLPOINTER)5,0); mystmt(hstmt,rc); /* This query includes leading spaces to act as a regression test for Bug #6609: SQL_ATTR_MAX_ROWS and leading spaces in query result in truncating end of query. */ rc = tmysql_exec(hstmt," select * from t_max_rows"); mystmt(hstmt,rc); myassert( 5 == myrowcount(hstmt)); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_MAX_ROWS,(SQLPOINTER)15,0); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from t_max_rows"); mystmt(hstmt,rc); myassert( 10 == myrowcount(hstmt)); SQLFreeStmt(hstmt,SQL_CLOSE); /* Patch for Bug#46411 uses SQL_ATTR_MAX_ROWS attribute to minimize number of rows to pre-fetch(sets to 1). Following fragment ensures that attribute's value is preserved and works. */ rc = SQLSetStmtAttr(hstmt,SQL_ATTR_MAX_ROWS,(SQLPOINTER)3,0); mystmt(hstmt,rc); rc = tmysql_prepare(hstmt,"select * from t_max_rows where id > ?"); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_ULONG,SQL_INTEGER,0,0,&i,0,NULL); mystmt(hstmt,rc); i= 6; ok_stmt(hstmt, SQLNumResultCols(hstmt, &cc)); rc = SQLExecute(hstmt); mystmt(hstmt,rc); myassert( 3 == myrowcount(hstmt)); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLSetStmtAttr(hstmt,SQL_ATTR_MAX_ROWS,(SQLPOINTER)0,0); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from t_max_rows"); mystmt(hstmt,rc); myassert( 10 == myrowcount(hstmt)); SQLFreeStmt(hstmt,SQL_CLOSE); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_max_rows"); return OK; } DECLARE_TEST(t_multistep) { SQLRETURN rc; SQLCHAR szData[150]; SQLLEN pcbValue; SQLINTEGER id; ok_sql(hstmt, "DROP TABLE IF EXISTS t_multistep"); rc = tmysql_exec(hstmt,"create table t_multistep(col1 int,col2 varchar(200))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_multistep values(10,'MySQL - Open Source Database')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER) SQL_CONCUR_ROWVER, 0); SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) SQL_CURSOR_KEYSET_DRIVEN, 0); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&id,0,NULL); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from t_multistep"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); printMessage("id: %ld",id); myassert(id == 10); rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,0,&pcbValue); myassert(rc == SQL_SUCCESS_WITH_INFO); printMessage("length: %ld", pcbValue); myassert(pcbValue == 28); rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,0,&pcbValue); myassert(rc == SQL_SUCCESS_WITH_INFO); printMessage("length: %ld", pcbValue); myassert(pcbValue == 28); rc = SQLGetData(hstmt,2,SQL_C_BINARY,szData,0,&pcbValue); myassert(rc == SQL_SUCCESS_WITH_INFO); printMessage("length: %ld", pcbValue); myassert(pcbValue == 28); rc = SQLGetData(hstmt,2,SQL_C_BINARY,szData,0,&pcbValue); myassert(rc == SQL_SUCCESS_WITH_INFO); printMessage("length: %ld", pcbValue); myassert(pcbValue == 28); rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,0,&pcbValue); myassert(rc == SQL_SUCCESS_WITH_INFO); printMessage("length: %ld", pcbValue); is_num(pcbValue, 28); rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,10,&pcbValue); myassert(rc == SQL_SUCCESS_WITH_INFO); printMessage("data : %s (%ld)",szData,pcbValue); is_num(pcbValue, 28); is_str(szData, "MySQL - O", 10); pcbValue= 0; rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,5,&pcbValue); myassert(rc == SQL_SUCCESS_WITH_INFO); printMessage("data : %s (%ld)",szData,pcbValue); is_num(pcbValue, 19); is_str(szData, "pen ", 5); pcbValue= 0; szData[0]='A'; rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,0,&pcbValue); myassert(rc == SQL_SUCCESS_WITH_INFO); printMessage("data : %s (%ld)",szData,pcbValue); myassert(pcbValue == 15); myassert(szData[0] == 'A'); rc = SQLGetData(hstmt,2,SQL_C_CHAR,szData,pcbValue+1,&pcbValue); mystmt(hstmt,rc); printMessage("data : %s (%ld)",szData,pcbValue); is_num(pcbValue, 15); is_str(szData,"Source Database", 16); pcbValue= 99; szData[0]='A'; expect_stmt(hstmt, SQLGetData(hstmt, 2, SQL_C_CHAR, szData, 0, &pcbValue), SQL_NO_DATA_FOUND); printMessage("data : %s (%ld)",szData,pcbValue); myassert(pcbValue == 99); myassert(szData[0] == 'A'); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS t_multistep"); return OK; } DECLARE_TEST(t_zerolength) { SQLRETURN rc; SQLCHAR szData[100], bData[100], bData1[100]; SQLLEN pcbValue,pcbValue1,pcbValue2; ok_sql(hstmt, "DROP TABLE IF EXISTS t_zerolength"); rc = tmysql_exec(hstmt,"create table t_zerolength(str varchar(20), bin varbinary(20), blb blob)"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_zerolength values('','','')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_zerolength values('venu','mysql','monty')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER) SQL_CONCUR_ROWVER, 0); SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) SQL_CURSOR_KEYSET_DRIVEN, 0); rc = tmysql_exec(hstmt,"select * from t_zerolength"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); pcbValue= pcbValue1= 99; ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, szData, 0, &pcbValue)); printMessage("length: %d", pcbValue); myassert(pcbValue == 0); bData[0]=bData[1]='z'; ok_stmt(hstmt, SQLGetData(hstmt, 2, SQL_C_BINARY, bData, 0, &pcbValue1)); printMessage("length: %d", pcbValue1); myassert(pcbValue1 == 0); myassert(bData[0] == 'z'); myassert(bData[1] == 'z'); bData1[0]=bData1[1]='z'; ok_stmt(hstmt, SQLGetData(hstmt, 3, SQL_C_BINARY, bData1, 0, &pcbValue2)); printMessage("length: %d", pcbValue2); myassert(pcbValue2 == 0); myassert(bData1[0] == 'z'); myassert(bData1[1] == 'z'); pcbValue= pcbValue1= 99; rc = SQLGetData(hstmt,1,SQL_C_CHAR,szData,1,&pcbValue); mystmt(hstmt,rc); printMessage("data: %s, length: %d", szData, pcbValue); myassert(pcbValue == 0); myassert(szData[0] == '\0'); bData[0]=bData[1]='z'; rc = SQLGetData(hstmt,2,SQL_C_BINARY,bData,1,&pcbValue1); mystmt(hstmt,rc); printMessage("data: %s, length: %d", bData, pcbValue1); myassert(pcbValue1 == 0); bData1[0]=bData1[1]='z'; rc = SQLGetData(hstmt,3,SQL_C_CHAR,bData1,1,&pcbValue2); mystmt(hstmt,rc); printMessage("data: %s, length: %d", bData1, pcbValue2); myassert(pcbValue2 == 0); myassert(bData1[0] == '\0'); myassert(bData1[1] == 'z'); rc = SQLFetch(hstmt); mystmt(hstmt,rc); pcbValue= pcbValue1= 99; szData[0]= bData[0]= 'z'; rc = SQLGetData(hstmt,1,SQL_C_CHAR,szData,0,&pcbValue); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("length: %d", pcbValue); myassert(pcbValue == 4); myassert(szData[0] == 'z'); rc = SQLGetData(hstmt,2,SQL_C_BINARY,bData,0,&pcbValue1); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("length: %d", pcbValue1); myassert(pcbValue1 == 5); myassert(bData[0] == 'z'); bData[0]=bData1[1]='z'; rc = SQLGetData(hstmt,3,SQL_C_BINARY,bData1,0,&pcbValue2); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("length: %d", pcbValue2); myassert(pcbValue2 == 5); pcbValue= pcbValue1= 99; szData[0]= szData[1]= bData[0]= bData[1]= 'z'; rc = SQLGetData(hstmt,1,SQL_C_CHAR,szData,1,&pcbValue); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("data: %s, length: %d", szData,pcbValue); myassert(pcbValue == 4); myassert(szData[0] == '\0'); rc = SQLGetData(hstmt,2,SQL_C_BINARY,bData,1,&pcbValue1); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("data; %s, length: %d", bData, pcbValue1); myassert(pcbValue1 == 5); myassert(bData[0] == 'm'); bData[0]=bData1[1]='z'; rc = SQLGetData(hstmt,3,SQL_C_BINARY,bData1,1,&pcbValue2); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("length: %d", pcbValue2); myassert(pcbValue2 == 5); myassert(bData1[0] == 'm'); myassert(bData1[1] == 'z'); pcbValue= pcbValue1= 99; rc = SQLGetData(hstmt,1,SQL_C_CHAR,szData,4,&pcbValue); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("data: %s, length: %d", szData, pcbValue); is_num(pcbValue, 4); is_str(szData,"ven", 3); rc = SQLGetData(hstmt,2,SQL_C_BINARY,bData,4,&pcbValue1); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("data: %s, length: %d", bData, pcbValue1); is_num(pcbValue1, 5); is_str(bData, "mysq", 4); pcbValue= pcbValue1= 99; rc = SQLGetData(hstmt,1,SQL_C_CHAR,szData,5,&pcbValue); mystmt(hstmt,rc); printMessage("data: %s, length: %d", szData, pcbValue); is_num(pcbValue, 4); is_str(szData, "venu", 4); rc = SQLGetData(hstmt,2,SQL_C_BINARY,bData,5,&pcbValue1); mystmt(hstmt,rc); printMessage("data: %s, length: %d", bData, pcbValue1); is_num(pcbValue1, 5); is_str(bData, "mysql", 5); szData[0]= 'z'; rc = SQLGetData(hstmt,3,SQL_C_CHAR,szData,0,&pcbValue); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("data: %s, length: %d", szData, pcbValue); myassert(pcbValue == 5 || pcbValue == 10); myassert(szData[0] == 'z'); #if TO_BE_FIXED_IN_DRIVER szData[0]=szData[1]='z'; rc = SQLGetData(hstmt,3,SQL_C_CHAR,szData,1,&pcbValue); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("data: %s, length: %d", szData, pcbValue); myassert(pcbValue == 10); myassert(szData[0] == 'm'); myassert(szData[1] == 'z'); rc = SQLGetData(hstmt,3,SQL_C_CHAR,szData,4,&pcbValue); mystmt_err(hstmt,rc == SQL_SUCCESS_WITH_INFO,rc); printMessage("data: %s, length: %d", szData, pcbValue); myassert(pcbValue == 10); myassert(strncmp(szData,"mont",4) == 0); rc = SQLGetData(hstmt,3,SQL_C_CHAR,szData,5,&pcbValue); mystmt(hstmt,rc); printMessage("data: %s, length: %d", szData, pcbValue); myassert(pcbValue == 10); myassert(strncmp(szData,"monty",5) == 0); #endif rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA_FOUND); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS t_zerolength"); return OK; } /* Test the bug when two stmts are used with the don't cache results */ DECLARE_TEST(t_cache_bug) { SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1, hstmt2; SQLCHAR conn[MAX_NAME_LEN]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_cache"); ok_sql(hstmt, "CREATE TABLE t_cache (id INT)"); ok_sql(hstmt, "INSERT INTO t_cache VALUES (1),(2),(3),(4),(5)"); sprintf((char *)conn, "DSN=%s;UID=%s;PWD=%s;DATABASE=%s;OPTION=1048579", mydsn, myuid, mypwd, mydb); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn, pbuff); } is(mydrvconnect(&henv1, &hdbc1, &hstmt1, conn) == OK); ok_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt1, "SELECT * FROM t_cache"); ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_con(hdbc1, SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt2)); ok_stmt(hstmt2, SQLColumns(hstmt2, NULL, 0, NULL, 0, (SQLCHAR *)"t_cache", SQL_NTS, NULL, 0)); ok_stmt(hstmt2, SQLFetch(hstmt2)); ok_stmt(hstmt1, SQLFetch(hstmt1)); expect_stmt(hstmt2, SQLFetch(hstmt2), SQL_NO_DATA); ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_stmt(hstmt2, SQLFreeHandle(SQL_HANDLE_STMT, hstmt2)); ok_stmt(hstmt1, SQLFetch(hstmt1)); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); ok_env(henv1, SQLFreeEnv(henv1)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_cache"); return OK; } /* Test the bug when two stmts are used with the don't cache results */ DECLARE_TEST(t_non_cache_bug) { SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1, hstmt2; SQLCHAR conn[MAX_NAME_LEN]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_cache"); ok_sql(hstmt, "CREATE TABLE t_cache (id INT)"); ok_sql(hstmt, "INSERT INTO t_cache VALUES (1),(2),(3),(4),(5)"); sprintf((char *)conn, "DSN=%s;UID=%s;PWD=%s;DATABASE=%s;OPTION=3", mydsn, myuid, mypwd, mydb); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } is(mydrvconnect(&henv1, &hdbc1, &hstmt1, conn) == OK); ok_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt1, "SELECT * FROM t_cache"); ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_con(hdbc1, SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt2)); ok_stmt(hstmt2, SQLColumns(hstmt2, NULL, 0, NULL, 0, (SQLCHAR *)"t_cache", SQL_NTS, NULL, 0)); ok_stmt(hstmt2, SQLFetch(hstmt2)); ok_stmt(hstmt1, SQLFetch(hstmt1)); expect_stmt(hstmt2, SQLFetch(hstmt2), SQL_NO_DATA); ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_stmt(hstmt2, SQLFreeHandle(SQL_HANDLE_STMT, hstmt2)); ok_stmt(hstmt1, SQLFetch(hstmt1)); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); ok_env(henv1, SQLFreeEnv(henv1)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_cache"); return OK; } DECLARE_TEST(t_empty_str_bug) { SQLRETURN rc; SQLINTEGER id; SQLLEN name_len, desc_len; SQLCHAR name[20], desc[20]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_empty_str_bug"); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"CREATE TABLE t_empty_str_bug(Id int NOT NULL,\ Name varchar(10) default NULL, \ Description varchar(10) default NULL, \ PRIMARY KEY (Id))"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu", SQL_NTS)); rc = tmysql_exec(hstmt,"select * from t_empty_str_bug"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&id,0,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,&name,100,&name_len); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,3,SQL_C_CHAR,&desc,100,&desc_len); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_NEXT,1,NULL,NULL); myassert(rc == SQL_NO_DATA_FOUND); id= 10; strcpy((char *)name,"MySQL AB"); name_len= SQL_NTS; strcpy((char *)desc,""); desc_len= SQL_COLUMN_IGNORE; rc = SQLSetPos(hstmt,1,SQL_ADD,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); rc = SQLRowCount(hstmt,&name_len); mystmt(hstmt,rc); printMessage("rows affected: %d",name_len); myassert(name_len == 1); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from t_empty_str_bug"); mystmt(hstmt,rc); my_assert( 1 == myresult(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); rc = tmysql_exec(hstmt,"select * from t_empty_str_bug"); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_ABSOLUTE,1,NULL,NULL); mystmt(hstmt,rc); name[0]='\0'; my_assert(10 == my_fetch_int(hstmt,1)); my_assert(!strcmp((const char *)"MySQL AB",my_fetch_str(hstmt,name,2))); ok_stmt(hstmt, SQLGetData(hstmt, 3,SQL_CHAR, name, MAX_ROW_DATA_LEN+1, &name_len)); /*Checking that if value is NULL - buffer will not be changed */ is_str("MySQL AB", name, 9); /* NULL */ rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_empty_str_bug"); return OK; } DECLARE_TEST(t_desccol) { SQLRETURN rc; SQLCHAR colname[20]; SQLSMALLINT collen,datatype,decptr,nullable; SQLULEN colsize; ok_sql(hstmt, "DROP TABLE IF EXISTS t_desccol"); tmysql_exec(hstmt,"drop table t_desccol"); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"create table t_desccol(col1 int, col2 varchar(10), col3 text)"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt,"insert into t_desccol values(10,'venu','mysql')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"select * from t_desccol"); mystmt(hstmt,rc); rc = SQLDescribeCol(hstmt,1,colname,20,&collen,&datatype,&colsize,&decptr,&nullable); mystmt(hstmt,rc); printMessage("1: %s,%d,%d,%d,%d,%d",colname,collen,datatype,colsize,decptr,nullable);; rc = SQLDescribeCol(hstmt,2,colname,20,&collen,&datatype,&colsize,&decptr,&nullable); mystmt(hstmt,rc); printMessage("2: %s,%d,%d,%d,%d,%d",colname,collen,datatype,colsize,decptr,nullable);; rc = SQLDescribeCol(hstmt,3,colname,20,&collen,&datatype,&colsize,&decptr,&nullable); mystmt(hstmt,rc); printMessage("3: %s,%d,%d,%d,%d,%d",colname,collen,datatype,colsize,decptr,nullable);; rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_desccol"); return OK; } int desccol(SQLHSTMT hstmt, char *cname, SQLSMALLINT clen, SQLSMALLINT sqltype, SQLULEN size, SQLSMALLINT scale, SQLSMALLINT isNull) { SQLRETURN rc =0; SQLCHAR lcname[254]; SQLSMALLINT lclen; SQLSMALLINT lsqltype; SQLULEN lsize; SQLSMALLINT lscale; SQLSMALLINT lisNull; SQLCHAR select[255]; SQLFreeStmt(hstmt,SQL_CLOSE); sprintf((char *)select,"select %s from t_desccolext",cname); printMessage("%s",select); rc = SQLExecDirect(hstmt,select,SQL_NTS); mystmt(hstmt,rc); rc = SQLDescribeCol( hstmt,1,lcname, sizeof(lcname),&lclen, &lsqltype,&lsize,&lscale,&lisNull); mystmt(hstmt,rc); printMessage("name: %s (%d)",lcname,lclen); printMessage(" sqltype: %d, size: %d, scale: %d, null: %d\n",lsqltype,lsize,lscale,lisNull); is_str(lcname, cname, clen); myassert(lclen == clen); myassert(lsqltype == sqltype); myassert(lsize == size); myassert(lscale == scale); myassert(lisNull == isNull); SQLFreeStmt(hstmt,SQL_CLOSE); return OK; } DECLARE_TEST(t_desccolext) { SQLRETURN rc; ok_sql(hstmt, "DROP TABLE IF EXISTS t_desccolext"); ok_sql(hstmt, "create table t_desccolext\ ( t1 tinyint,\ t2 tinyint(10),\ t3 tinyint unsigned,\ s1 smallint,\ s2 smallint(10),\ s3 smallint unsigned,\ m1 mediumint,\ m2 mediumint(10),\ m3 mediumint unsigned,\ i1 int,\ i2 int(10) not null,\ i3 int unsigned,\ i4 int zerofill,\ b1 bigint,\ b2 bigint(10),\ b3 bigint unsigned,\ f1 float,\ f2 float(10),\ f3 float(24) zerofill,\ f4 float(10,4),\ d1 double,\ d2 double(30,3),\ d3 double precision,\ d4 double precision(30,3),\ r1 real,\ r2 real(30,3),\ dc1 decimal,\ dc2 decimal(10),\ dc3 decimal(10,3),\ n1 numeric,\ n2 numeric(10,3),\ dt date,\ dtime datetime,\ ts timestamp,\ ti time,\ yr1 year,\ yr2 year(2),\ yr3 year(4),\ c1 char(10),\ c2 char(10) binary,\ c3 national char(10),\ v1 varchar(10),\ v2 varchar(10) binary,\ v3 national varchar(10),\ bl1 tinyblob,\ bl2 blob,\ bl3 mediumblob,\ bl4 longblob,\ txt1 tinytext,\ txt2 text,\ txt3 mediumtext,\ txt4 longtext,\ en enum('v1','v2'),\ st set('1','2','3'))"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); is(desccol(hstmt,"t1",2,SQL_TINYINT,3,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"t2",2,SQL_TINYINT,3,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"t3",2,SQL_TINYINT,3,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"s1",2,SQL_SMALLINT,5,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"s2",2,SQL_SMALLINT,5,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"s3",2,SQL_SMALLINT,5,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"m1",2,SQL_INTEGER,8,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"m2",2,SQL_INTEGER,8,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"m3",2,SQL_INTEGER,8,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"i1",2,SQL_INTEGER,10,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"i2",2,SQL_INTEGER,10,0,SQL_NO_NULLS) == OK); is(desccol(hstmt,"i3",2,SQL_INTEGER,10,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"i4",2,SQL_INTEGER,10,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"b1",2,SQL_BIGINT,19,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"b2",2,SQL_BIGINT,19,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"b3",2,SQL_BIGINT,20,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"f1",2,SQL_REAL,7,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"f2",2,SQL_REAL,7,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"f3",2,SQL_REAL,7,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"f4",2,SQL_REAL,7,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"d1",2,SQL_DOUBLE,15,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"d2",2,SQL_DOUBLE,15,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"d3",2,SQL_DOUBLE,15,0,SQL_NULLABLE) == OK); is(desccol(hstmt,"d4",2,SQL_DOUBLE,15,0,SQL_NULLABLE) == OK); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_desccolext"); return OK; } DECLARE_TEST(t_desccol1) { SQLRETURN rc; ok_sql(hstmt, "DROP TABLE IF EXISTS t_desccol1"); rc = SQLExecDirect(hstmt,(SQLCHAR *)"create table t_desccol1\ ( record decimal(8,0),\ title varchar(250),\ num1 float,\ num2 decimal(7,0),\ num3 decimal(12,3),\ code char(3),\ sdate date,\ stime time,\ numer numeric(7,0),\ muner1 numeric(12,5))",SQL_NTS); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from t_desccol1"); mystmt(hstmt,rc); { SQLCHAR ColumnName[255]; SQLSMALLINT ColumnNameSize; SQLSMALLINT ColumnSQLDataType; SQLULEN ColumnSize; SQLSMALLINT ColumnDecimals; SQLSMALLINT ColumnNullable; SQLSMALLINT index, pccol; rc = SQLNumResultCols(hstmt,(SQLSMALLINT *)&pccol); mystmt(hstmt,rc); printMessage("total columns:%d",pccol); printMessage("Name nlen type size decs null"); for ( index = 1; index <= pccol; index++) { rc = SQLDescribeCol(hstmt, index, ColumnName, sizeof(ColumnName), &ColumnNameSize, &ColumnSQLDataType, &ColumnSize, &ColumnDecimals, &ColumnNullable); mystmt(hstmt,rc); printMessage("%-6s %4d %4d %7ld %4d %4d", ColumnName, ColumnNameSize, ColumnSQLDataType, ColumnSize, ColumnDecimals, ColumnNullable); } } rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_desccol1"); return OK; } DECLARE_TEST(t_colattributes) { SQLLEN count, isauto; ok_sql(hstmt, "DROP TABLE IF EXISTS t_colattr"); ok_sql(hstmt, "CREATE TABLE t_colattr (" "t1 TINYINT NOT NULL AUTO_INCREMENT PRIMARY KEY," "t2 TINYINT(10)," "t3 TINYINT UNSIGNED," "s1 SMALLINT," "s2 SMALLINT(10)," "s3 SMALLINT UNSIGNED," "m1 MEDIUMINT," "m2 MEDIUMINT(10)," "m3 MEDIUMINT UNSIGNED," "i1 INT," "i2 INT(10) NOT NULL," "i3 INT UNSIGNED," "i4 INT ZEROFILL," "b1 BIGINT," "b2 BIGINT(10)," "b3 BIGINT UNSIGNED," "f1 FLOAT," "f2 FLOAT(10)," "f3 FLOAT(24) ZEROFILL," "f4 FLOAT(10,4)," "d1 DOUBLE," "d2 DOUBLE(30,3)," "d3 DOUBLE PRECISION," "d4 DOUBLE PRECISION(30,3)," "r1 REAL," "r2 REAL(30,3)," "dc1 DECIMAL," "dc2 DECIMAL(10)," "dc3 DECIMAL(10,3)," "n1 NUMERIC," "n2 NUMERIC(10,3)," "dt DATE," "dtime DATETIME," "ts TIMESTAMP," "ti TIME," "yr1 YEAR," "yr2 YEAR(2)," "yr3 YEAR(4)," "c1 CHAR(10)," "c2 CHAR(10) BINARY," "c3 NATIONAL CHAR(10)," "v1 VARCHAR(10)," "v2 VARCHAR(10) BINARY," "v3 NATIONAL VARCHAR(10)," "bl1 TINYBLOB," "bl2 BLOB," "bl3 MEDIUMBLOB," "bl4 LONGBLOB," "txt1 TINYTEXT," "txt2 TEXT," "txt3 MEDIUMTEXT," "txt4 LONGTEXT," "en ENUM('v1','v2')," "st SET('1','2','3'))"); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_colattr"); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_COLUMN_COUNT, NULL, 0, NULL, &count)); is(count == 54); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_COLUMN_AUTO_INCREMENT, NULL, 0, NULL, &isauto)); is_num(isauto, SQL_TRUE); ok_sql(hstmt, "DROP TABLE IF EXISTS t_colattr"); return OK; } DECLARE_TEST(t_exfetch) { SQLRETURN rc; SQLUINTEGER i; ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_exfetch"); rc = tmysql_exec(hstmt,"create table t_exfetch(col1 int)"); mystmt(hstmt,rc); rc = SQLPrepare(hstmt, (SQLCHAR *)"insert into t_exfetch values (?)", SQL_NTS); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,0,0,&i,0,NULL); mystmt(hstmt,rc); for ( i = 1; i <= 5; i++ ) { rc = SQLExecute(hstmt); mystmt(hstmt,rc); } SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLExecDirect(hstmt, (SQLCHAR *)"select * from t_exfetch",SQL_NTS); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&i,0,NULL); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_LAST,0,NULL,NULL);/* 5 */ mystmt(hstmt,rc); my_assert(i == 5); rc = SQLExtendedFetch(hstmt,SQL_FETCH_PREV,0,NULL,NULL);/* 4 */ mystmt(hstmt,rc); my_assert(i == 4); rc = SQLExtendedFetch(hstmt,SQL_FETCH_RELATIVE,-3,NULL,NULL);/* 1 */ mystmt(hstmt,rc); my_assert(i == 1); rc = SQLExtendedFetch(hstmt,SQL_FETCH_RELATIVE,-1,NULL,NULL);/* 0 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_PREV,1,NULL,NULL); /* 0 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_FIRST,-1,NULL,NULL);/* 0 */ mystmt(hstmt,rc); my_assert(i == 1); rc = SQLExtendedFetch(hstmt,SQL_FETCH_ABSOLUTE,4,NULL,NULL);/* 4 */ mystmt(hstmt,rc); my_assert(i == 4); rc = SQLExtendedFetch(hstmt,SQL_FETCH_RELATIVE,2,NULL,NULL);/* 4 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_PREV,2,NULL,NULL);/* last */ mystmt(hstmt,rc); my_assert(i == 5); rc = SQLExtendedFetch(hstmt,SQL_FETCH_NEXT,2,NULL,NULL);/* last+1 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_ABSOLUTE,-7,NULL,NULL);/* 0 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_FIRST,2,NULL,NULL);/* 1 */ mystmt(hstmt,rc); my_assert(i == 1); rc = SQLExtendedFetch(hstmt,SQL_FETCH_PREV,2,NULL,NULL);/* 0 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_NEXT,0,NULL,NULL);/* 1*/ mystmt(hstmt,rc); my_assert(i == 1); rc = SQLExtendedFetch(hstmt,SQL_FETCH_PREV,0,NULL,NULL);/* 0 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_RELATIVE,-1,NULL,NULL); /* 0 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_RELATIVE,1,NULL,NULL); /* 1 */ mystmt(hstmt,rc); my_assert(i == 1); /* MyODBC .39 returns 2 instead of 1 */ rc = SQLExtendedFetch(hstmt,SQL_FETCH_RELATIVE,-1,NULL,NULL);/* 0 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_RELATIVE,1,NULL,NULL);/* 1 */ mystmt(hstmt,rc); my_assert(i == 1); rc = SQLExtendedFetch(hstmt,SQL_FETCH_RELATIVE,1,NULL,NULL);/* 2 */ mystmt(hstmt,rc); my_assert(i == 2); rc = SQLExtendedFetch(hstmt,SQL_FETCH_RELATIVE,-2,NULL,NULL);/* 0 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_RELATIVE,6,NULL,NULL);/* last+1 */ mystmt_err(hstmt,rc == SQL_NO_DATA_FOUND, rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_PREV,6,NULL,NULL);/* last+1 */ mystmt(hstmt, rc); my_assert(i == 5); SQLFreeStmt(hstmt,SQL_RESET_PARAMS); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS t_exfetch"); return OK; } DECLARE_TEST(tmysql_rowstatus) { SQLRETURN rc; SQLHSTMT hstmt1; SQLULEN pcrow[4]; SQLUSMALLINT rgfRowStatus[6]; SQLINTEGER nData= 555; SQLCHAR szData[255] = "setpos-update"; ok_con(hdbc, SQLAllocStmt(hdbc, &hstmt1)); ok_stmt(hstmt, SQLSetCursorName(hstmt, (SQLCHAR *)"venu_cur", SQL_NTS)); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_rowstatus"); rc = tmysql_exec(hstmt,"create table tmysql_rowstatus(col1 int , col2 varchar(30))"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_rowstatus values(100,'venu')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_rowstatus values(200,'MySQL')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_rowstatus values(300,'MySQL3')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_rowstatus values(400,'MySQL3')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_rowstatus values(500,'MySQL3')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_rowstatus values(600,'MySQL3')"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE ,(SQLPOINTER)1 , 0); mystmt(hstmt, rc); rc = tmysql_exec(hstmt,"select * from tmysql_rowstatus"); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,1,SQL_C_LONG,&nData,0,NULL); mystmt(hstmt,rc); rc = SQLBindCol(hstmt,2,SQL_C_CHAR,szData,sizeof(szData),NULL); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_NEXT,1,pcrow,(SQLUSMALLINT *)&rgfRowStatus); mystmt(hstmt,rc); rc = SQLExtendedFetch(hstmt,SQL_FETCH_NEXT,1,pcrow,(SQLUSMALLINT *)&rgfRowStatus); mystmt(hstmt,rc); rc = SQLSetPos(hstmt,1,SQL_POSITION,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); ok_sql(hstmt1, "UPDATE tmysql_rowstatus SET col1 = 999," "col2 = 'pos-update' WHERE CURRENT OF venu_cur"); rc = SQLExtendedFetch(hstmt,SQL_FETCH_LAST,1,NULL,NULL); mystmt(hstmt,rc); rc = SQLSetPos(hstmt,1,SQL_DELETE,SQL_LOCK_NO_CHANGE); mystmt(hstmt,rc); printMessage("rgfRowStatus[0]:%d",rgfRowStatus[0]); SQLFreeStmt(hstmt,SQL_CLOSE); rc = SQLFreeStmt(hstmt1,SQL_DROP); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"select * from tmysql_rowstatus"); mystmt(hstmt,rc); myassert(5 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_rowstatus"); return OK; } /* TESTING FOR TRUE LENGTH */ DECLARE_TEST(t_true_length) { SQLCHAR data1[25],data2[25]; SQLLEN len1,len2; SQLULEN desc_len; SQLSMALLINT name_len; ok_sql(hstmt, "DROP TABLE IF EXISTS t_true_length"); ok_sql(hstmt, "CREATE TABLE t_true_length (a CHAR(20), b VARCHAR(15))"); ok_sql(hstmt, "INSERT INTO t_true_length VALUES ('venu','mysql')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_true_length"); ok_stmt(hstmt, SQLDescribeCol(hstmt, 1, data1, sizeof(data1), &name_len, NULL, &desc_len, NULL, NULL)); is_num(desc_len, 20); ok_stmt(hstmt, SQLDescribeCol(hstmt, 2, data1, sizeof(data1), &name_len, NULL, &desc_len, NULL, NULL)); is_num(desc_len, 15); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, data1, sizeof(data1), &len1)); is_str(data1, "venu", 4); is_num(len1, 4); ok_stmt(hstmt, SQLGetData(hstmt, 2, SQL_C_CHAR, data2, sizeof(data2), &len2)); is_str(data2, "mysql", 5); is_num(len2, 5); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_true_length"); return OK; } /** Bug #27544: Calling stored procedure causes "Out of sync" and "Lost connection" errors */ DECLARE_TEST(t_bug27544) { ok_sql(hstmt, "DROP TABLE IF EXISTS t1"); ok_sql(hstmt, "CREATE TABLE t1(a int)"); ok_sql(hstmt, "INSERT INTO t1 VALUES (1)"); ok_sql(hstmt, "DROP PROCEDURE IF EXISTS p1"); ok_sql(hstmt, "CREATE PROCEDURE p1() BEGIN" " SELECT a FROM t1; " "END;"); ok_sql(hstmt,"CALL p1()"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP PROCEDURE p1"); ok_sql(hstmt, "DROP TABLE t1"); return OK; } /** Bug #6157: BUG in the alias use with ADO's Object */ DECLARE_TEST(bug6157) { SQLCHAR name[30]; SQLSMALLINT len; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug6157"); ok_sql(hstmt, "CREATE TABLE t_bug6157 (a INT)"); ok_sql(hstmt, "INSERT INTO t_bug6157 VALUES (1)"); ok_sql(hstmt, "SELECT a AS b FROM t_bug6157 AS c"); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_NAME, name, sizeof(name), &len, NULL)); is_str(name, "b", 1); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_BASE_COLUMN_NAME, name, sizeof(name), &len, NULL)); is_str(name, "a", 1); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_TABLE_NAME, name, sizeof(name), &len, NULL)); is_str(name, "c", 1); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_BASE_TABLE_NAME, name, sizeof(name), &len, NULL)); is_str(name, "t_bug6157", 9); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug6157"); return OK; } /** Bug #16817: ODBC doesn't return multiple resultsets */ DECLARE_TEST(t_bug16817) { SQLCHAR name[30]; SQLSMALLINT ncol; ok_sql(hstmt, "DROP PROCEDURE IF EXISTS p_bug16817"); ok_sql(hstmt, "CREATE PROCEDURE p_bug16817 () " "BEGIN " " SELECT 'Marten' FROM DUAL; " " SELECT 'Zack' FROM DUAL; " "END"); ok_sql(hstmt, "CALL p_bug16817()"); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, name, 1), "Marten", 6); ok_stmt(hstmt, SQLMoreResults(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, name, 1), "Zack", 4); ok_stmt(hstmt, SQLMoreResults(hstmt)); ok_stmt(hstmt, SQLNumResultCols(hstmt,&ncol)); is_num(ncol, 0); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP PROCEDURE p_bug16817"); return OK; } DECLARE_TEST(t_binary_collation) { SQLSMALLINT name_length, data_type, decimal_digits, nullable; SQLCHAR column_name[SQL_MAX_COLUMN_NAME_LEN]; SQLULEN column_size; SQLCHAR server_version[MYSQL_NAME_LEN+1]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_binary_collation"); ok_sql(hstmt, "CREATE TABLE t_binary_collation (id INT)"); ok_sql(hstmt, "SHOW CREATE TABLE t_binary_collation"); ok_con(hdbc, SQLGetInfo(hdbc, SQL_DBMS_VER, server_version, MYSQL_NAME_LEN, NULL)); ok_stmt(hstmt, SQLDescribeCol(hstmt, 1, column_name, sizeof(column_name), &name_length, &data_type, &column_size, &decimal_digits, &nullable)); if (mysql_min_version(hdbc, "5.2", 3) || /* 5.0.46 or later in 5.0 series */ (!strncmp("5.0", (char *)server_version, 3) && mysql_min_version(hdbc, "5.0.46", 6)) || /* 5.1.22 or later in 5.1 series */ (!strncmp("5.1", (char *)server_version, 3) && mysql_min_version(hdbc, "5.1.22", 6))) { is_num(data_type, SQL_WVARCHAR); } else { is_num(data_type, SQL_VARBINARY); } ok_sql(hstmt, "DROP TABLE IF EXISTS t_binary_collation"); return OK; } /* * Bug 29239 - Prepare before insert returns wrong result */ DECLARE_TEST(t_bug29239) { SQLHANDLE hstmt2; SQLINTEGER xval = 88; ok_sql(hstmt, "drop table if exists bug29239"); ok_sql(hstmt, "create table bug29239 ( x int )"); /* prepare & bind */ ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"select x from bug29239", SQL_NTS)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &xval, 0, NULL)); /* insert before execute, with a new stmt handle */ ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt2)); ok_sql(hstmt2, "insert into bug29239 values (44)"); ok_stmt(hstmt2, SQLFreeHandle(SQL_HANDLE_STMT, hstmt2)); /* now execute */ ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(xval, 44); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists bug29239"); return OK; } /* Bug 30958, blank "text" fields are not accessible through ADO. This is a result of us not handle SQLGetData() w/a zero-len buffer correctly. */ DECLARE_TEST(t_bug30958) { SQLCHAR outbuf[20]= "bug"; SQLLEN outlen; SQLINTEGER outmax= 0; ok_sql(hstmt, "drop table if exists bug30958"); ok_sql(hstmt, "CREATE TABLE bug30958 (tt_textfield TEXT NOT NULL)"); ok_sql(hstmt, "INSERT INTO bug30958 (tt_textfield) VALUES ('')"); ok_sql(hstmt, "select tt_textfield from bug30958"); ok_stmt(hstmt, SQLFetch(hstmt)); /* check first that we get truncation, with zero bytes available in out buffer, outbuffer should be untouched */ outlen= 99; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, outmax, &outlen), SQL_SUCCESS_WITH_INFO); is_str(outbuf, "bug", 3); is_num(outlen, 0); is(check_sqlstate(hstmt, "01004") == OK); /* expect the same result, and not SQL_NO_DATA */ outlen= 99; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, outmax, &outlen), SQL_SUCCESS_WITH_INFO); is_str(outbuf, "bug", 3); is_num(outlen, 0); is(check_sqlstate(hstmt, "01004") == OK); /* now provide a space to read the data */ outmax= 1; ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, outmax, &outlen)); is_num(outbuf[0], 0); is_num(outlen, 0); /* only now is it unavailable (test with empty and non-empty out buffer) */ outmax= 0; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, outmax, &outlen), SQL_NO_DATA); outmax= 1; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, outmax, &outlen), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists bug30958"); return OK; } /** A variant of the above test, with charset != ansi charset */ DECLARE_TEST(t_bug30958_ansi) { SQLCHAR outbuf[20]= "bug"; SQLLEN outlen; SQLINTEGER outmax= 0; ok_sql(hstmt, "drop table if exists bug30958"); ok_sql(hstmt, "CREATE TABLE bug30958 (tt_textfield TEXT CHARACTER SET latin2 NOT NULL)"); ok_sql(hstmt, "INSERT INTO bug30958 (tt_textfield) VALUES ('')"); ok_sql(hstmt, "select tt_textfield from bug30958"); ok_stmt(hstmt, SQLFetch(hstmt)); /* check first that we get truncation, with zero bytes available in out buffer, outbuffer should be untouched */ outlen= 99; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, outmax, &outlen), SQL_SUCCESS_WITH_INFO); is_str(outbuf, "bug", 3); is_num(outlen, 0); is(check_sqlstate(hstmt, "01004") == OK); /* expect the same result, and not SQL_NO_DATA */ outlen= 99; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, outmax, &outlen), SQL_SUCCESS_WITH_INFO); is_str(outbuf, "bug", 3); is_num(outlen, 0); is(check_sqlstate(hstmt, "01004") == OK); /* now provide a space to read the data */ outmax= 1; ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, outmax, &outlen)); is_num(outbuf[0], 0); is_num(outlen, 0); /* only now is it unavailable (test with empty and non-empty out buffer) */ outmax= 0; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, outmax, &outlen), SQL_NO_DATA); outmax= 1; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, outmax, &outlen), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists bug30958"); return OK; } /** A variant of the above tests using SQLWVARCHAR */ DECLARE_TEST(t_bug30958_wchar) { SQLCHAR outbuf[20]= "bug"; SQLLEN outlen; SQLINTEGER outmax= 0; ok_sql(hstmt, "drop table if exists bug30958"); ok_sql(hstmt, "CREATE TABLE bug30958 (tt_textfield TEXT NOT NULL)"); ok_sql(hstmt, "INSERT INTO bug30958 (tt_textfield) VALUES ('')"); ok_sql(hstmt, "select tt_textfield from bug30958"); ok_stmt(hstmt, SQLFetch(hstmt)); /* check first that we get truncation, with zero bytes available in out buffer, outbuffer should be untouched */ outlen= 99; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, outbuf, outmax, &outlen), SQL_SUCCESS_WITH_INFO); is_str(outbuf, "bug", 3); is_num(outlen, 0); is(check_sqlstate(hstmt, "01004") == OK); /* expect the same result, and not SQL_NO_DATA */ outlen= 99; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, outbuf, outmax, &outlen), SQL_SUCCESS_WITH_INFO); is_str(outbuf, "bug", 3); is_num(outlen, 0); is(check_sqlstate(hstmt, "01004") == OK); /* now provide a space to read the data */ outmax= sizeof(SQLWCHAR); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, outbuf, outmax, &outlen)); is_num(outbuf[0], 0); is_num(outlen, 0); /* only now is it unavailable (test with empty and non-empty out buffer) */ outmax= 0; expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, outbuf, outmax, &outlen), SQL_NO_DATA); outmax= 1; /* outmax greater than 0, but less than sizeof(SQLWCHAR) */ expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, outbuf, outmax, &outlen), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists bug30958"); return OK; } /** Bug #31246: Opening rowset with extra fields leads to incorrect SQL INSERT */ DECLARE_TEST(t_bug31246) { SQLSMALLINT ncol; SQLCHAR *buf= (SQLCHAR *)"Key1"; SQLCHAR field1[20]; SQLINTEGER field2; SQLCHAR field3[20]; SQLRETURN rc; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug31246"); ok_sql(hstmt, "CREATE TABLE t_bug31246 (" "field1 VARCHAR(50) NOT NULL PRIMARY KEY, " "field2 int DEFAULT 10, " "field3 VARCHAR(50) DEFAULT \"Default Text\")"); /* No need to insert any rows in the table, so do SELECT */ ok_sql(hstmt, "SELECT * FROM t_bug31246"); ok_stmt(hstmt, SQLNumResultCols(hstmt,&ncol)); is_num(ncol, 3); /* Bind only one column instead of three ones */ ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_CHAR, buf, strlen((char *)buf), NULL)); /* Expect SQL_NO_DATA_FOUND result from the empty table */ rc= SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, NULL, NULL); is_num(rc, SQL_NO_DATA_FOUND); /* Here was the problem */ ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_ADD, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Check whether the row was inserted with the default values*/ ok_sql(hstmt, "SELECT * FROM t_bug31246 WHERE field1=\"Key1\""); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_CHAR, field1, sizeof(field1), NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_LONG, &field2, sizeof(SQLINTEGER), NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_CHAR, field3, sizeof(field3), NULL)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(field1, buf, strlen((char *)buf) + 1); is_num(field2, 10); is_str(field3, "Default Text", 13); /* Clean-up */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE t_bug31246"); return OK; } /** Bug #13776: Invalid string or buffer length error */ DECLARE_TEST(t_bug13776) { SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1; SQLULEN pcColSz; SQLCHAR szColName[MAX_NAME_LEN]; SQLSMALLINT pfSqlType, pcbScale, pfNullable; SQLLEN display_size, octet_length; SET_DSN_OPTION(1 << 27); /* Establish the new connection */ alloc_basic_handles(&henv1, &hdbc1, &hstmt1); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_bug13776"); ok_sql(hstmt1, "CREATE TABLE t_bug13776(ltext LONGTEXT) CHARSET latin1"); ok_sql(hstmt1, "INSERT INTO t_bug13776 VALUES ('long text test')"); ok_sql(hstmt1, "SELECT * FROM t_bug13776"); ok_stmt(hstmt1, SQLDescribeCol(hstmt1, 1, szColName, MAX_NAME_LEN+1, NULL, &pfSqlType, &pcColSz, &pcbScale, &pfNullable)); /* Size of LONGTEXT should have been capped to 1 << 31. */ is_num(pcColSz, 2147483647L); /* also, check display size and octet length (see bug#30890) */ ok_stmt(hstmt1, SQLColAttribute(hstmt1, 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &display_size)); ok_stmt(hstmt1, SQLColAttribute(hstmt1, 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &octet_length)); is_num(display_size, 2147483647L); is_num(octet_length, 2147483647L); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_bug13776"); free_basic_handles(&henv1, &hdbc1, &hstmt1); SET_DSN_OPTION(0); return OK; } /** Test that FLAG_COLUMN_SIZE_S32 is automatically enabled when ADO library is loaded. */ DECLARE_TEST(t_bug13776_auto) { #ifdef WIN32 SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1; HMODULE ado_dll; SQLULEN pcColSz; SQLCHAR szColName[MAX_NAME_LEN]; SQLSMALLINT pfSqlType, pcbScale, pfNullable; SQLCHAR *env_path= NULL; SQLCHAR szFileToLoad[255]; /** @todo get the full path to the library using getenv */ #ifdef _WIN64 env_path= getenv("CommonProgramW6432"); if (!env_path) { env_path= getenv("CommonProgramFiles"); } #else env_path= getenv("CommonProgramFiles"); #endif if (!env_path) { printf("# No path for CommonProgramFiles in %s on line %d\n", __FILE__, __LINE__); return FAIL; } sprintf(szFileToLoad, "%s\\System\\ado\\msado15.dll", env_path); ado_dll= LoadLibrary(szFileToLoad); if (!ado_dll) { printf("# Could not load %s in %s on line %d\n", szFileToLoad, __FILE__, __LINE__); return FAIL; } /* Establish the new connection */ alloc_basic_handles(&henv1, &hdbc1, &hstmt1); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_bug13776"); ok_sql(hstmt1, "CREATE TABLE t_bug13776(ltext LONGTEXT) CHARSET latin1"); ok_sql(hstmt1, "INSERT INTO t_bug13776 VALUES ('long text test')"); ok_sql(hstmt1, "SELECT * FROM t_bug13776"); ok_stmt(hstmt1, SQLDescribeCol(hstmt1, 1, szColName, MAX_NAME_LEN+1, NULL, &pfSqlType, &pcColSz, &pcbScale, &pfNullable)); /* IF adodb15.dll is loaded SQLDescribeCol should return the length of LONGTEXT columns as 2G instead of 4G */ is_num(pcColSz, 2147483647L); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "DROP TABLE IF EXISTS t_bug13776"); free_basic_handles(&henv1, &hdbc1, &hstmt1); FreeLibrary(ado_dll); #endif return OK; } /* Bug#28617 Gibberish when reading utf8 TEXT column through ADO */ DECLARE_TEST(t_bug28617) { SQLWCHAR outbuf[100]; SQLLEN outlen; ok_sql(hstmt, "select 'qwertyuiop'"); ok_stmt(hstmt, SQLFetch(hstmt)); expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outbuf, 0, &outlen), SQL_SUCCESS_WITH_INFO); is_num(outlen, 10); expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, outbuf, 0, &outlen), SQL_SUCCESS_WITH_INFO); is_num(outlen, 10 * sizeof(SQLWCHAR)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, outbuf, 100, &outlen)); is_num(outlen, 10 * sizeof(SQLWCHAR)); is(!memcmp(outbuf, W(L"qwertyuiop"), 11 * sizeof(SQLWCHAR))); return OK; } /* Bug#32684 - chunked retrieval of SQL_C_WCHAR fails */ DECLARE_TEST(t_bug32684) { SQLWCHAR wbuf[20]; SQLCHAR abuf[20]; SQLLEN wlen, alen; ok_sql(hstmt, "select repeat('x', 100), repeat('y', 100)"); ok_stmt(hstmt, SQLFetch(hstmt)); do { ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, abuf, 20, &alen)); printMessage("data= %s, len=%d\n", abuf, alen); } while(alen > 20); do { ok_stmt(hstmt, SQLGetData(hstmt, 2, SQL_C_WCHAR, wbuf, 20 * sizeof(SQLWCHAR), &wlen)); wprintf(L"# data= %s, len=%d\n\n", wbuf, wlen); } while(wlen > 20 * sizeof(SQLWCHAR)); return OK; } /* Bug #34271 - C/ODBC 5.1 does not list table fields in MSQRY32 */ DECLARE_TEST(t_bug34271) { SQLINTEGER x1= 0, x2= 0; /* execute the query, but bind only the first column */ ok_sql(hstmt, "select 1,2"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &x1, 0, NULL)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(x1, 1); is_num(x2, 0); x1= 0; /* unbind */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); /* execute the query, but bind only the second column */ ok_sql(hstmt, "select 1,2"); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_LONG, &x2, 0, NULL)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(x1, 0); is_num(x2, 2); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; } /* Bug #34429 - SQLGetData gives incorrect data */ DECLARE_TEST(t_bug34429) { SQLWCHAR buf[32]; SQLLEN reslen; ok_sql(hstmt, "drop table if exists t_bug34429"); ok_sql(hstmt, "create table t_bug34429 (x varchar(200))"); ok_sql(hstmt, "insert into t_bug34429 values " "(concat(repeat('x',32),repeat('y',32),repeat('z',16)))"); ok_sql(hstmt, "select x from t_bug34429"); ok_stmt(hstmt, SQLFetch(hstmt)); /* first chunk */ expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, buf, sizeof(buf), &reslen), SQL_SUCCESS_WITH_INFO); printMessage("Chunk 1, len=%d, data=%ls", reslen, buf); is_num(reslen, 80 * sizeof(SQLWCHAR)); is(!memcmp(buf, W(L"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"), 32 * sizeof(SQLWCHAR))); /* second chunk */ expect_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, buf, sizeof(buf), &reslen), SQL_SUCCESS_WITH_INFO); printMessage("Chunk 2, len=%d, data=%ls", reslen, buf); is_num(reslen, 49 * sizeof(SQLWCHAR)); is(!memcmp(buf, W(L"xyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"), 32 * sizeof(SQLWCHAR))); /* third chunk */ ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_WCHAR, buf, sizeof(buf), &reslen)); printMessage("Chunk 3, len=%d, data=%ls", reslen, buf); is_num(reslen, 18 * sizeof(SQLWCHAR)); is(!memcmp(buf, W(L"yyzzzzzzzzzzzzzzzz"), 18 * sizeof(SQLWCHAR))); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_bug34429"); return OK; } /* Bug #32420 - Don't cache results and SQLExtendedFetch work badly together */ DECLARE_TEST(t_bug32420) { SQLHANDLE henv1, hdbc1, hstmt1; SQLINTEGER nData[4]; SQLCHAR szData[4][16]; SQLUSMALLINT rgfRowStatus[4]; SET_DSN_OPTION(1048576); alloc_basic_handles(&henv1, &hdbc1, &hstmt1); ok_sql(hstmt1, "drop table if exists bug32420"); ok_sql(hstmt1, "CREATE TABLE bug32420 ("\ "tt_int INT PRIMARY KEY auto_increment,"\ "tt_varchar VARCHAR(128) NOT NULL)"); ok_sql(hstmt1, "INSERT INTO bug32420 VALUES "\ "(100, 'string 1'),"\ "(200, 'string 2'),"\ "(300, 'string 3'),"\ "(400, 'string 4')"); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLSetStmtOption(hstmt1, SQL_ROWSET_SIZE, 4)); ok_sql(hstmt1, "select * from bug32420 order by 1"); ok_stmt(hstmt1, SQLBindCol(hstmt1, 1, SQL_C_LONG, nData, 0, NULL)); ok_stmt(hstmt1, SQLBindCol(hstmt1, 2, SQL_C_CHAR, szData, sizeof(szData[0]), NULL)); ok_stmt(hstmt1, SQLExtendedFetch(hstmt1, SQL_FETCH_NEXT, 0, NULL, rgfRowStatus)); is_num(nData[0], 100); is_str(szData[0], "string 1", 8); is_num(nData[1], 200); is_str(szData[1], "string 2", 8); is_num(nData[2], 300); is_str(szData[2], "string 3", 8); is_num(nData[3], 400); is_str(szData[3], "string 4", 8); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt1, "drop table if exists bug32420"); free_basic_handles(&henv1, &hdbc1, &hstmt1); SET_DSN_OPTION(1048576); return OK; } /** Bug #34575: SQL_C_CHAR value type and numeric parameter type causes trouble */ DECLARE_TEST(t_bug34575) { SQLCHAR buff[10]; SQLLEN len= 0; ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "SELECT ?", SQL_NTS)); strcpy((char *)buff, "2.0"); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_DECIMAL, 10, 0, buff, sizeof(buff), &len)); /* Note: buff has '2.0', but len is still 0! */ ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 1), "", 1); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); strcpy((char *)buff, "2.0"); len= 3; ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 1), "2.0", 4); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; } /* Bug #24131 SHOW CREATE TABLE result truncated with mysql 3.23 and ODBC driver 3.51.12.00 */ DECLARE_TEST(t_bug24131) { SQLCHAR buff[1024]; SQLLEN boundLen= 0; SQLULEN count; UWORD status; SQLULEN colSize; ok_sql(hstmt, "drop table if exists bug24131"); /* Table definition should be long enough. */ ok_sql(hstmt, "CREATE TABLE `bug24131` (" "`Codigo` int(10) unsigned NOT NULL auto_increment," "`Nombre` varchar(255) default NULL," "`Telefono` varchar(255) default NULL," "`Observaciones` longtext," "`Direccion` varchar(255) default NULL," "`Dni` varchar(255) default NULL," "`CP` int(11) default NULL," "`Provincia` varchar(255) default NULL," "`Poblacion` varchar(255) default NULL," "PRIMARY KEY (`Codigo`)" ") ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"show create table bug24131", SQL_NTS)); ok_stmt(hstmt, SQLDescribeCol(hstmt, 2, buff, sizeof(buff), NULL, NULL, &colSize, NULL, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt,2,SQL_C_BINARY, buff, 1024, &boundLen)); /* Note: buff has '2.0', but len is still 0! */ ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, &count, &status)); if (sizeof(SQLLEN) == 4) printMessage("colSize: %lu, boundLen: %ld", colSize, boundLen); else printMessage("colSize: %llu, boundLen: %lld", colSize, boundLen); is(colSize >= (SQLULEN)boundLen); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists bug24131"); return OK; } /* Bug #36069 - SQLProcedures followed by a SQLFreeStmt causes a crash */ DECLARE_TEST(t_bug36069) { SQLSMALLINT size; ok_stmt(hstmt, SQLProcedures(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"non-existing", SQL_NTS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLNumResultCols(hstmt, &size)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"select ?", SQL_NTS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLNumResultCols(hstmt, &size)); return OK; } /* Bug #41942 - SQLDescribeCol() segfault with non-zero name length and null buffer */ DECLARE_TEST(t_bug41942) { SQLSMALLINT len; ok_sql(hstmt, "select 1 as name"); ok_stmt(hstmt, SQLDescribeCol(hstmt, 1, NULL, 10, &len, NULL, NULL, NULL, NULL)); is_num(len, 4); return OK; } /* Bug 39644 - Binding SQL_C_BIT to an integer column is not working */ DECLARE_TEST(t_bug39644) { char col1 = 0x3f; char col2 = 0xff; char col3 = 0x0; char col4 = 0x1; ok_sql(hstmt, "drop table if exists t_bug39644"); ok_sql(hstmt, "create table t_bug39644(col1 INT, col2 INT,"\ "col3 BIT, col4 BIT)"); ok_sql(hstmt, "insert into t_bug39644 VALUES (5, 0, 1, 0)"); /* Do SELECT */ ok_sql(hstmt, "SELECT * from t_bug39644"); /* Now bind buffers */ ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_BIT, &col1, sizeof(char), 0)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_BIT, &col2, sizeof(char), 0)); ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_BIT, &col3, sizeof(char), 0)); ok_stmt(hstmt, SQLBindCol(hstmt, 4, SQL_C_BIT, &col4, sizeof(char), 0)); /* Fetch and check results */ ok_stmt(hstmt, SQLFetch(hstmt)); is( col1 == 1 ); is( col2 == 0 ); is( col3 == 1 ); is( col4 == 0 ); /* Clean-up */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table t_bug39644"); return OK; } /* Bug#32821(it might be duplicate though): Wrong value if bit field is bound to other than SQL_C_BIT variable */ DECLARE_TEST(t_bug32821) { SQLRETURN rc; SQLUINTEGER b; SQLUSMALLINT c; SQLLEN a_ind, b_ind, c_ind, i, j, k; unsigned char a; SQL_NUMERIC_STRUCT b_numeric; SQLUINTEGER par= sizeof(SQLUSMALLINT)*8+1; SQLUINTEGER beoyndShortBit= 1<<(par-1); SQLLEN sPar= sizeof(SQLUINTEGER); /* 131071 = 0x1ffff - all 1 for field c*/ SQLCHAR * insStmt= "insert into t_bug32821 values (0,0,0),(1,1,1)\ ,(1,255,131071),(1,258,?)"; const unsigned char expected_a[]= {'\0', '\1', '\1', '\1'}; const SQLUINTEGER expected_b[]= {0L, 1L, 255L, 258L}; const SQLUSMALLINT expected_c[]= {0, 1, 65535, 0}; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug32821"); ok_stmt(hstmt, SQLPrepare(hstmt, "CREATE TABLE t_bug32821 (a BIT(1), b BIT(16)\ , c BIT(?))", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG , SQL_INTEGER, 0, 0, &par, 0, &sPar )); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLPrepare(hstmt, insStmt, SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG , SQL_INTEGER, 0, 0, &beoyndShortBit, 0 , &sPar )); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT a,b,c FROM t_bug32821"); ok_stmt( hstmt, SQLBindCol( hstmt, 1, SQL_C_BIT, &a, 0, &a_ind ) ); ok_stmt( hstmt, SQLBindCol( hstmt, 2, SQL_C_ULONG, &b, 0, &b_ind ) ); /*ok_stmt( hstmt, SQLBindCol( hstmt, 1, SQL_C_TYPE_DATE, &d, 0, &b_ind ) );*/ ok_stmt( hstmt, SQLBindCol( hstmt, 3, SQL_C_USHORT, &c, 0, &c_ind ) ); i= 0; while( (rc= SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)) != SQL_NO_DATA_FOUND) { printMessage("testing row #%d", i+1); is_num(a, expected_a[i]); is_num(b, expected_b[i]); is_num(c, expected_c[i]); /* Test of binding to numeric - added later so a bit messy */ for (k= 1; k < 3; ++k) { b_ind= sizeof(SQL_NUMERIC_STRUCT); SQLGetData(hstmt, (SQLUSMALLINT)k, SQL_C_NUMERIC, &b_numeric, 0, &b_ind); b= 0; for(j= 0; j < b_numeric.precision; ++j) { b+= (0xff & b_numeric.val[j]) << 8*j; } switch (k) { case 1: is_num(b, expected_a[i]); break; case 2: is_num(b, expected_b[i]); break; } } ++i; } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug32821"); return OK; } /* Bug 55024 - Wrong type returned by SQLColAttribute(SQL_DESC_PRECISION...) in 64-bit Linux */ DECLARE_TEST(t_bug55024) { SQLSMALLINT len; SQLLEN res; ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS t_test55024", SQL_NTS)); ok_stmt(hstmt, SQLExecDirect(hstmt, "CREATE TABLE t_test55024(col01 LONGTEXT, "\ "col02 BINARY(16),"\ "col03 VARBINARY(16),"\ "col04 LONGBLOB,"\ "col05 BIGINT,"\ "col06 TINYINT,"\ "col07 BIT, col08 DOUBLE"\ ") CHARSET latin1", SQL_NTS)); ok_stmt(hstmt, SQLExecDirect(hstmt, "INSERT INTO t_test55024 VALUES ('a', 'b', 'c', 'd', 999, 111, 1, 3.1415)", SQL_NTS)); ok_stmt(hstmt, SQLExecDirect(hstmt, "SELECT * FROM t_test55024", SQL_NTS)); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_TYPE, NULL, 0, &len, &res)); is_num(res, SQL_LONGVARCHAR); ok_stmt(hstmt, SQLColAttribute(hstmt, 2, SQL_DESC_TYPE, NULL, 0, &len, &res)); is_num(res, SQL_BINARY); ok_stmt(hstmt, SQLColAttribute(hstmt, 3, SQL_DESC_TYPE, NULL, 0, &len, &res)); is_num(res, SQL_VARBINARY); ok_stmt(hstmt, SQLColAttribute(hstmt, 4, SQL_DESC_TYPE, NULL, 0, &len, &res)); is_num(res, SQL_LONGVARBINARY); ok_stmt(hstmt, SQLColAttribute(hstmt, 5, SQL_DESC_TYPE, NULL, 0, &len, &res)); is_num(res, SQL_BIGINT); ok_stmt(hstmt, SQLColAttribute(hstmt, 6, SQL_DESC_TYPE, NULL, 0, &len, &res)); is_num(res, SQL_TINYINT); ok_stmt(hstmt, SQLColAttribute(hstmt, 7, SQL_DESC_TYPE, NULL, 0, &len, &res)); is_num(res, SQL_BIT); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug55024"); return OK; } /* Bug #56677 - SQLNumResultCols() causes the driver to return only first row in the resultset */ DECLARE_TEST(t_bug56677) { SQLINTEGER nData; SQLCHAR szData[16]; SQLSMALLINT colCount; ok_sql(hstmt, "drop table if exists bug56677"); ok_sql(hstmt, "CREATE TABLE bug56677 ("\ "tt_int INT PRIMARY KEY auto_increment,"\ "tt_varchar VARCHAR(128) NOT NULL)"); ok_sql(hstmt, "INSERT INTO bug56677 VALUES "\ "(100, 'string 1'),"\ "(200, 'string 2'),"\ "(300, 'string 3'),"\ "(400, 'string 4')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLPrepare(hstmt, "select * from bug56677", SQL_NTS)); ok_stmt(hstmt, SQLNumResultCols(hstmt, &colCount)); is_num(colCount, 2); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), NULL)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(nData, 100); is_str(szData, "string 1", 8); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(nData, 200); is_str(szData, "string 2", 8); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(nData, 300); is_str(szData, "string 3", 8); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(nData, 400); is_str(szData, "string 4", 8); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists bug56677"); return OK; } /* Test of SQLDescribeCol and SQLColAttribute if they are called before SQLExecute. Bug#56717 */ DECLARE_TEST(t_desccol_before_exec) { SQLINTEGER nData= 200; SQLCHAR szData[128]; SQLSMALLINT colCount; char colname[MYSQL_NAME_LEN]; SQLULEN collen; SQLLEN coltype; ok_sql(hstmt, "drop table if exists desccol_before_exec"); ok_sql(hstmt, "CREATE TABLE desccol_before_exec ("\ "tt_int INT PRIMARY KEY auto_increment,"\ "tt_varchar VARCHAR(128) CHARACTER SET latin1 NOT NULL)"); ok_sql(hstmt, "INSERT INTO desccol_before_exec VALUES "\ "(100, 'string 1'),"\ "(200, 'string 2'),"\ "(300, 'string 3'),"\ "(400, 'string 4')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLPrepare(hstmt, "select tt_varchar from desccol_before_exec where tt_int > ?", SQL_NTS)); ok_stmt(hstmt, SQLDescribeCol(hstmt, 1, colname, sizeof(colname), NULL, NULL, &collen, NULL, NULL)); is_str(colname, "tt_varchar", 11); is_num(collen, 128); /* Just to make sure that SQLNumResultCols still works fine */ ok_stmt(hstmt, SQLNumResultCols(hstmt, &colCount)); is_num(colCount, 1); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_CHAR, szData, sizeof(szData), NULL)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(szData, "string 3", 8); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(szData, "string 4", 8); /* Now doing all the same things with SQLColAttribute */ ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLPrepare(hstmt, "select tt_int, tt_varchar " "from desccol_before_exec " "where tt_int <= ?", SQL_NTS)); ok_stmt(hstmt, SQLColAttribute(hstmt, 2, SQL_DESC_TYPE, NULL, 0, NULL, &coltype)); is_num(coltype, SQL_VARCHAR); /* Just to make sure that SQLNumResultCols still works fine */ ok_stmt(hstmt, SQLNumResultCols(hstmt, &colCount)); is_num(colCount, 2); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &nData, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, szData, sizeof(szData), NULL)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(szData, "string 1", 8); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(szData, "string 2", 8); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists desccol_before_exec"); return OK; } /* Bug #62657 A failure on one stmt causes another stmt to fail */ DECLARE_TEST(t_bug62657) { SQLHSTMT hstmt1; ok_sql(hstmt, "DROP table IF EXISTS b62657"); ok_sql(hstmt, "CREATE table b62657(i int)"); ok_sql(hstmt, "insert into b62657 values(1),(2)"); ok_stmt(hstmt, SQLExecDirect(hstmt, "select * from b62657", SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); /* Any failing query would do the job here */ ok_con(hstmt1, SQLAllocStmt(hdbc, &hstmt1)); expect_sql(hstmt1, "select * from some_ne_rubbish", SQL_ERROR); /* Error of other query before all rows fetched causes next fetch to fail */ ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP table b62657"); return OK; } DECLARE_TEST(t_row_status) { SQLHANDLE ird, ard; SQLUSMALLINT arr1[2], arr2[2], i, j; const SQLUSMALLINT expectedRow1[]= {SQL_ROW_SUCCESS, SQL_ROW_NOROW}, expectedRow2[][2]= { {SQL_ROW_SUCCESS, SQL_ROW_SUCCESS}, {SQL_ROW_SUCCESS_WITH_INFO, SQL_ROW_ERROR} }, expectedFunction2[2]= {SQL_SUCCESS, SQL_SUCCESS_WITH_INFO}; SQLCHAR res[5*2]; ok_sql(hstmt, "DROP table IF EXISTS b_row_status"); ok_sql(hstmt, "CREATE table b_row_status(i int)"); ok_sql(hstmt, "insert into b_row_status values(4),(2),(1),(NULL)"); ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_ROW_DESC, &ird, SQL_IS_POINTER, NULL)); ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &ard, SQL_IS_POINTER, NULL)); ok_desc(ird, SQLSetDescField(ird, 0, SQL_DESC_ARRAY_STATUS_PTR, (SQLPOINTER)arr1, SQL_IS_POINTER)); ok_desc(ird, SQLSetDescField(ard, 0, SQL_DESC_ARRAY_SIZE, (SQLPOINTER)2, SQL_IS_INTEGER)); ok_stmt(hstmt, SQLExecDirect(hstmt, "select * from b_row_status\ where i=1", SQL_NTS)); /* it has to be SQL_SUCCESS here */ expect_stmt(hstmt, SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, NULL, (SQLUSMALLINT*)&arr2), SQL_SUCCESS); /*expect_stmt(hstmt, SQLFetch(hstmt), SQL_SUCCESS);*/ for (i= 0; i<2; ++i) { printMessage("Row %d, Desc %d, Parameter %d", i+1, arr1[i], arr2[i]); is_num(expectedRow1[i], arr1[i]) is_num(arr1[i], arr2[i]); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLExecDirect(hstmt, "select if(i is NULL,NULL,repeat(char(64+i),8/i))\ from b_row_status\ order by i desc", SQL_NTS)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_CHAR, res, 5, NULL)); for (i= 0; i<2; ++i) { expect_stmt(hstmt, SQLFetch(hstmt), expectedFunction2[i]); for (j= 0; j<2; ++j) { printMessage("Set %d Row %d, desc %d, parameter %d", i+1, j+1, arr1[j], arr2[j]); is_num(expectedRow2[i][j], arr1[j]) } } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP table b_row_status"); return OK; } BEGIN_TESTS ADD_TEST(my_resultset) ADD_TEST(t_convert_type) ADD_TEST(t_desc_col) ADD_TEST(t_convert) ADD_TEST(t_max_rows) ADD_TEST(t_multistep) ADD_TEST(t_zerolength) ADD_TEST(t_cache_bug) ADD_TEST(t_non_cache_bug) ADD_TEST(t_empty_str_bug) ADD_TEST(t_desccol) ADD_TEST(t_desccolext) ADD_TEST(t_desccol1) ADD_TEST(t_colattributes) ADD_TEST(t_exfetch) ADD_TEST(tmysql_rowstatus) ADD_TEST(t_true_length) ADD_TEST(t_bug27544) ADD_TEST(t_bug16817) ADD_TEST(bug6157) ADD_TEST(t_binary_collation) ADD_TEST(t_bug29239) ADD_TEST(t_bug30958) ADD_TEST(t_bug30958_ansi) ADD_TEST(t_bug30958_wchar) ADD_TEST(t_bug31246) ADD_TEST(t_bug13776) ADD_TEST(t_bug13776_auto) ADD_TEST(t_bug28617) ADD_TEST(t_bug34429) ADD_TEST(t_bug32420) ADD_TEST(t_bug34575) ADD_TEST(t_bug24131) ADD_TEST(t_bug36069) ADD_TEST(t_bug41942) ADD_TEST(t_bug39644) ADD_TEST(t_bug32821) ADD_TEST(t_bug34271) ADD_TEST(t_bug32684) ADD_TEST(t_bug55024) ADD_TEST(t_bug56677) ADD_TEST(t_desccol_before_exec) ADD_TEST(t_bug62657) ADD_TEST(t_row_status) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_datetime.c100644 15766 12 64435 11707541005 20646 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" DECLARE_TEST(my_ts) { SQLCHAR szTs[50]; TIMESTAMP_STRUCT ts; SQLLEN len; ok_sql(hstmt, "DROP TABLE IF EXISTS my_ts"); ok_sql(hstmt, "CREATE TABLE my_ts (ts TIMESTAMP)"); /* insert using SQL_C_CHAR to SQL_TIMESTAMP */ strcpy((char *)szTs, "2002-01-07 10:20:49.06"); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_TIMESTAMP, 0, 0, szTs, sizeof(szTs), NULL)); ok_sql(hstmt, "INSERT INTO my_ts (ts) VALUES (?)"); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_KEYSET_DRIVEN, 0)); /* insert using SQL_C_TIMESTAMP to SQL_TIMESTAMP */ ts.year= 2002; ts.month= 1; ts.day= 7; ts.hour= 19; ts.minute= 47; ts.second= 59; ts.fraction= 4; ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP, 0, 0, &ts, sizeof(ts), NULL)); ok_sql(hstmt, "INSERT INTO my_ts (ts) VALUES (?)"); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); /* now fetch and verify the results .. */ ok_sql(hstmt, "SELECT * FROM my_ts"); /* now fetch first row */ ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 1)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, szTs, sizeof(szTs), &len)); is_str(szTs, "2002-01-07 10:20:49", len); printf("# row1 using SQL_C_CHAR: %s (%ld)\n", szTs, len); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 1)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_TIMESTAMP, &ts, sizeof(ts), &len)); is_num(ts.year, 2002); is_num(ts.month, 1); is_num(ts.day, 7); is_num(ts.hour, 10); is_num(ts.minute,20); is_num(ts.second,49); printf("# row1 using SQL_C_TIMESTAMP: %d-%d-%d %d:%d:%d.%d (%ld)\n", ts.year, ts.month,ts.day, ts.hour, ts.minute, ts.second, ts.fraction, len); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 2)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, szTs, sizeof(szTs), &len)); is_str(szTs, "2002-01-07 19:47:59", len); printf("# row2 using SQL_C_CHAR: %s(%ld)\n", szTs, len); ok_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 2)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_TIMESTAMP, &ts, sizeof(ts), &len)); is_num(ts.year, 2002); is_num(ts.month, 1); is_num(ts.day, 7); is_num(ts.hour, 19); is_num(ts.minute,47); is_num(ts.second,59); printf("# row2 using SQL_C_TIMESTAMP: %d-%d-%d %d:%d:%d.%d (%ld)\n", ts.year, ts.month,ts.day, ts.hour, ts.minute, ts.second, ts.fraction, len); expect_stmt(hstmt, SQLFetchScroll(hstmt, SQL_FETCH_ABSOLUTE, 3), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS my_ts"); return OK; } DECLARE_TEST(t_tstotime) { SQLRETURN rc; SQL_TIMESTAMP_STRUCT ts; ts.day = 02; ts.month = 8; ts.year = 2001; ts.hour = 18; ts.minute = 20; ts.second = 45; ts.fraction = 05; ok_sql(hstmt, "DROP TABLE IF EXISTS t_tstotime"); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"create table t_tstotime(col1 date ,col2 time, col3 timestamp)"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); /* TIMESTAMP TO DATE, TIME and TS CONVERSION */ rc = SQLPrepare(hstmt, (SQLCHAR *)"insert into t_tstotime(col1,col2,col3) values(?,?,?)",SQL_NTS); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_TIMESTAMP, SQL_DATE,0,0,&ts,sizeof(ts),NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT,SQL_C_TIMESTAMP, SQL_TIME,0,0,&ts,sizeof(ts),NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,3,SQL_PARAM_INPUT,SQL_C_TIMESTAMP, SQL_TIMESTAMP,0,0,&ts,sizeof(ts),NULL); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_RESET_PARAMS); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"select * from t_tstotime"); mystmt(hstmt,rc); my_assert( 1 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_tstotime"); return OK; } DECLARE_TEST(t_tstotime1) { SQLCHAR ts[40]= "2001-08-02 18:20:45.05"; ok_sql(hstmt,"DROP TABLE IF EXISTS t_tstotime1"); ok_sql(hstmt, "CREATE TABLE t_tstotime1(col1 DATE, col2 TIME, col3 TIMESTAMP)"); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); /* TIMESTAMP TO DATE, TIME and TS CONVERSION */ ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"INSERT INTO t_tstotime1 VALUES (?,?,?)", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_CHAR, SQL_DATE,0,0,&ts,sizeof(ts),NULL)); ok_stmt(hstmt, SQLBindParameter(hstmt,2,SQL_PARAM_INPUT,SQL_C_CHAR, SQL_TIME,0,0,&ts,sizeof(ts),NULL)); ok_stmt(hstmt, SQLBindParameter(hstmt,3,SQL_PARAM_INPUT,SQL_C_CHAR, SQL_TIMESTAMP,0,0,&ts,sizeof(ts),NULL)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_con(hdbc, SQLTransact(NULL,hdbc,SQL_COMMIT)); ok_sql(hstmt, "SELECT * FROM t_tstotime1"); my_assert(1 == myresult(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_tstotime1"); return OK; } DECLARE_TEST(t_bug25846) { SQLSMALLINT column_count; SQLLEN my_time_cb; SQLLEN my_date_cb; SQL_TIMESTAMP_STRUCT my_time_ts; SQL_TIMESTAMP_STRUCT my_date_ts; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug25846"); ok_sql(hstmt, "CREATE TABLE t_bug25846 (a TIME, b DATE)"); ok_sql(hstmt, "INSERT INTO t_bug25846 VALUES ('02:56:30', '1969-07-21')"); ok_sql(hstmt, "SELECT * FROM t_bug25846"); ok_stmt(hstmt, SQLNumResultCols(hstmt, &column_count)); is_num(column_count, 2); /* Bind the TIMESTAMP buffer for TIME column */ ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_TIMESTAMP, &my_time_ts, sizeof(my_time_ts), &my_time_cb)); /* Bind the TIMESTAMP buffer for DATE column */ ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_TIMESTAMP, &my_date_ts, sizeof(my_date_ts), &my_date_cb)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_time_ts.hour, 2); is_num(my_time_ts.minute, 56); is_num(my_time_ts.second, 30); is_num(my_date_ts.year, 1969); is_num(my_date_ts.month, 7); is_num(my_date_ts.day, 21); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug25846"); return OK; } DECLARE_TEST(t_time) { SQLRETURN rc; SQL_TIME_STRUCT tm; SQLCHAR str[20]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_time"); rc = tmysql_exec(hstmt,"create table t_time(tm time, ts timestamp)"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_time values (?,?)", SQL_NTS)); rc = SQLBindParameter( hstmt, 1, SQL_PARAM_INPUT, SQL_C_TIME, SQL_TIME, 0, 0, &tm, 0, NULL ); mystmt(hstmt,rc); rc = SQLBindParameter( hstmt, 2, SQL_PARAM_INPUT, SQL_C_TIME, SQL_TIMESTAMP, 0, 0, &tm, 15, NULL ); mystmt(hstmt,rc); tm.hour = 20; tm.minute = 59; tm.second = 45; rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_RESET_PARAMS); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); ok_sql(hstmt, "select tm from t_time"); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_CHAR,&str,100,NULL); mystmt(hstmt,rc); is_str(str, "20:59:45", 9); rc = SQLFetch(hstmt); my_assert(rc == SQL_NO_DATA_FOUND); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_time"); return OK; } /* Test for a simple time struct */ DECLARE_TEST(t_time1) { SQLRETURN rc; SQL_TIME_STRUCT tt; SQLCHAR data[30]; SQLLEN length; ok_sql(hstmt, "DROP TABLE IF EXISTS t_time"); ok_sql(hstmt, "create table t_time(t time, t1 timestamp, t2 datetime, t3 date)"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_time(t) values(?)", SQL_NTS)); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_TYPE_TIME, SQL_TIME,0,0,&tt,0,NULL); tt.hour= 00; tt.minute= 00; tt.second= 03; rc = SQLExecute(hstmt); mystmt(hstmt, rc); tt.hour= 01; tt.minute= 00; tt.second= 00; rc = SQLExecute(hstmt); mystmt(hstmt, rc); tt.hour= 19; tt.minute= 00; tt.second= 00; rc = SQLExecute(hstmt); mystmt(hstmt, rc); tt.hour= 01; tt.minute= 01; tt.second= 00; rc = SQLExecute(hstmt); mystmt(hstmt, rc); tt.hour= 01; tt.minute= 00; tt.second= 01; rc = SQLExecute(hstmt); mystmt(hstmt, rc); tt.hour= 00; tt.minute= 01; tt.second= 00; rc = SQLExecute(hstmt); mystmt(hstmt, rc); tt.hour= 00; tt.minute= 11; tt.second= 12; rc = SQLExecute(hstmt); mystmt(hstmt, rc); tt.hour= 01; tt.minute= 01; tt.second= 01; rc = SQLExecute(hstmt); mystmt(hstmt, rc); tt.hour= 00; tt.minute= 00; tt.second= 00; rc = SQLExecute(hstmt); mystmt(hstmt, rc); tt.hour= 10; tt.minute= 11; tt.second= 12; rc = SQLExecute(hstmt); mystmt(hstmt, rc); SQLFreeStmt(hstmt, SQL_RESET_PARAMS); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "select t from t_time"); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &length); mystmt(hstmt,rc); is_num(length, 8); is_str(data, "00:00:03", 9); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &length); mystmt(hstmt,rc); is_num(length, 8); is_str(data, "01:00:00", 9); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &length); mystmt(hstmt,rc); is_num(length, 8); is_str(data, "19:00:00", 9); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &length); mystmt(hstmt,rc); is_num(length, 8); is_str(data, "01:01:00", 9); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &length); mystmt(hstmt,rc); is_num(length, 8); is_str(data, "01:00:01", 9); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &length); mystmt(hstmt,rc); is_num(length, 8); is_str(data, "00:01:00", 9); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &length); mystmt(hstmt,rc); is_num(length, 8); is_str(data, "00:11:12", 9); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &length); mystmt(hstmt,rc); is_num(length, 8); is_str(data, "01:01:01", 9); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &length); mystmt(hstmt,rc); is_num(length, 8); is_str(data, "00:00:00", 9); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &length); mystmt(hstmt,rc); is_num(length, 8); is_str(data, "10:11:12", 9); rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt,"delete from t_time"); ok_sql(hstmt, "insert into t_time(t1) values('2003-05-12 10:11:12')"); ok_sql(hstmt, "select t1 from t_time"); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_TIME, &tt, sizeof(tt), &length); mystmt(hstmt,rc); is_num(tt.hour, 10); is_num(tt.minute, 11); is_num(tt.second, 12); is_num(length, sizeof(SQL_TIME_STRUCT)); rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt,"delete from t_time"); ok_sql(hstmt,"insert into t_time(t2) values('03-12-28 05:59:59')"); ok_sql(hstmt,"select t2 from t_time"); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_TIME, &tt, sizeof(tt), &length); mystmt(hstmt,rc); is_num(tt.hour, 05); is_num(tt.minute, 59); is_num(tt.second, 59); is_num(length, sizeof(SQL_TIME_STRUCT)); rc = SQLFetch(hstmt); is_num(rc, SQL_NO_DATA); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt,"delete from t_time"); ok_sql(hstmt,"insert into t_time(t3) values('2003-05-12 10:11:12')"); ok_sql(hstmt,"select t3 from t_time"); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt, 1, SQL_C_TIME, &tt, sizeof(tt), &length); mystmt(hstmt,rc); is(tt.hour == 00 || tt.minute == 00 || tt.second == 00); is_num(length, sizeof(SQL_TIME_STRUCT)); rc = SQLFetch(hstmt); myassert(rc == SQL_NO_DATA); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS t_time"); return OK; } /** Bug #12520: DATETIME Default Value 0000-00-00 00:00:00 not returning correct thru ODBC */ DECLARE_TEST(t_bug12520) { SQL_TIMESTAMP_STRUCT my_time_ts; SQLLEN len, my_time_cb; SQLCHAR datetime[50]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug12520"); ok_sql(hstmt, "CREATE TABLE t_bug12520 (a DATETIME DEFAULT '0000-00-00 00:00'," "b DATETIME DEFAULT '0000-00-00 00:00', c INT)"); ok_sql(hstmt, "INSERT INTO t_bug12520 (c) VALUES (1)"); ok_sql(hstmt, "SELECT a, b FROM t_bug12520"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_CHAR, datetime, sizeof(datetime), &len)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_TIMESTAMP, &my_time_ts, 0, &my_time_cb)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(datetime, "0000-00-00 00:00:00", 19); is_num(my_time_cb, SQL_NULL_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug12520"); return OK; } /** Bug #15773: Wrong between results */ DECLARE_TEST(t_bug15773) { SQL_DATE_STRUCT a,b,c,d; SQLLEN len1; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug15773"); ok_sql(hstmt, "CREATE TABLE t_bug15773(" "`a` varchar(255) NOT NULL default ''," "`b` datetime NOT NULL default '0000-00-00 00:00:00'," "`c` datetime NOT NULL default '0000-00-00 00:00:00'" ") ENGINE=InnoDB DEFAULT CHARSET=latin1"); ok_sql(hstmt, "INSERT INTO t_bug15773 VALUES ('a', '2005-12-24 00:00:00', '2008-05-12 00:00:00')"); ok_sql(hstmt, "INSERT INTO t_bug15773 VALUES ('b', '2004-01-01 00:00:00', '2005-01-01 00:00:00')"); ok_sql(hstmt, "INSERT INTO t_bug15773 VALUES ('c', '2004-12-12 00:00:00', '2005-12-12 00:00:00')"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"SELECT * FROM t_bug15773" " WHERE (?) BETWEEN b AND c", SQL_NTS)); d.day= 15; d.month= 12; d.year = 2005; ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TYPE_DATE, SQL_TYPE_DATE, 0, 0, &d, 0, NULL)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_CHAR, &a, 255, &len1)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_TYPE_DATE, &b, 0, &len1)); ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_TYPE_DATE, &c, 0, &len1)); ok_stmt(hstmt, SQLExecute(hstmt)); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug15773"); return OK; } /** Bug #9927: Updating datetime columns */ DECLARE_TEST(t_bug9927) { SQLCHAR col[10]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug9927"); ok_sql(hstmt, "CREATE TABLE t_bug9927 (a TIMESTAMP DEFAULT 0," "b TIMESTAMP ON UPDATE CURRENT_TIMESTAMP)"); ok_stmt(hstmt, SQLSpecialColumns(hstmt,SQL_ROWVER, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug9927", SQL_NTS, 0, SQL_NO_NULLS)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 2), "b", 1); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug9927"); return OK; } /** Bug #30081: Can't distinguish between auto-set TIMESTAMP and auto-updated TIMESTAMP */ DECLARE_TEST(t_bug30081) { if (!mysql_min_version(hdbc, "5.1.23", 6)) skip("necessary feature added in MySQL 5.1.23"); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug30081"); ok_sql(hstmt, "CREATE TABLE t_bug30081 (a TIMESTAMP DEFAULT 0," "b TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); ok_stmt(hstmt, SQLSpecialColumns(hstmt,SQL_ROWVER, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug30081", SQL_NTS, 0, SQL_NO_NULLS)); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug30081"); return OK; } /** Verify that we get correct data for SQL_DATA_TYPE and SQL_DATETIME_SUB from SQLColumns(). Also check SQL_DESC_TYPE from SQLColAttribute(). */ DECLARE_TEST(t_datecolumns) { SQLCHAR col[10]; SQLLEN type; ok_sql(hstmt, "DROP TABLE IF EXISTS t_datecolumns"); ok_sql(hstmt, "CREATE TABLE t_datecolumns(a TIMESTAMP, b DATETIME, c DATE, d TIME)"); ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_datecolumns", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 4), "a", 1); is_num(my_fetch_int(hstmt, 14), SQL_DATETIME); is_num(my_fetch_int(hstmt, 15), SQL_TYPE_TIMESTAMP); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 4), "b", 1); is_num(my_fetch_int(hstmt, 14), SQL_DATETIME); is_num(my_fetch_int(hstmt, 15), SQL_TYPE_TIMESTAMP); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 4), "c", 1); is_num(my_fetch_int(hstmt, 14), SQL_DATETIME); is_num(my_fetch_int(hstmt, 15), SQL_TYPE_DATE); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 4), "d", 1); is_num(my_fetch_int(hstmt, 14), SQL_DATETIME); is_num(my_fetch_int(hstmt, 15), SQL_TYPE_TIME); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_datecolumns"); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_TYPE, NULL, 0, NULL, &type)); is_num(type, SQL_DATETIME); ok_stmt(hstmt, SQLColAttribute(hstmt, 2, SQL_DESC_TYPE, NULL, 0, NULL, &type)); is_num(type, SQL_DATETIME); ok_stmt(hstmt, SQLColAttribute(hstmt, 3, SQL_DESC_TYPE, NULL, 0, NULL, &type)); is_num(type, SQL_DATETIME); ok_stmt(hstmt, SQLColAttribute(hstmt, 4, SQL_DESC_TYPE, NULL, 0, NULL, &type)); is_num(type, SQL_DATETIME); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_datecolumns"); return OK; } /** Bug #14414: SQLColumn() does not return timestamp nullable attribute correctly */ DECLARE_TEST(t_bug14414) { SQLCHAR col[10]; SQLSMALLINT nullable; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug14414"); ok_sql(hstmt, "CREATE TABLE t_bug14414(a TIMESTAMP, b TIMESTAMP NOT NULL," "c TIMESTAMP NULL)"); ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug14414", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 4), "a", 1); is_num(my_fetch_int(hstmt, 11), SQL_NULLABLE); is_str(my_fetch_str(hstmt, col, 18), "YES", 3); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 4), "b", 1); is_num(my_fetch_int(hstmt, 11), SQL_NULLABLE); is_str(my_fetch_str(hstmt, col, 18), "YES", 3); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 4), "c", 1); is_num(my_fetch_int(hstmt, 11), SQL_NULLABLE); is_str(my_fetch_str(hstmt, col, 18), "YES", 3); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /** Bug #26108 MyODBC ADO field attributes reporting adFldMayBeNull for not null columns */ ok_sql(hstmt, "SELECT * FROM t_bug14414"); ok_stmt(hstmt, SQLDescribeCol(hstmt, 1, col, sizeof(col), NULL, NULL, NULL, NULL, &nullable)); is_num(nullable, SQL_NULLABLE); ok_stmt(hstmt, SQLDescribeCol(hstmt, 2, col, sizeof(col), NULL, NULL, NULL, NULL, &nullable)); is_num(nullable, SQL_NULLABLE); ok_stmt(hstmt, SQLDescribeCol(hstmt, 3, col, sizeof(col), NULL, NULL, NULL, NULL, &nullable)); is_num(nullable, SQL_NULLABLE); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug14414"); return OK; } /** Bug #30939: SQLGetTypeInfo returns 6 instead of 8 for COLUMN_SIZE for SQL_TYPE_TIME */ DECLARE_TEST(t_bug30939) { ok_stmt(hstmt, SQLGetTypeInfo(hstmt, SQL_TYPE_TIME)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(my_fetch_int(hstmt, 3), 8); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); return OK; } /** Bug #31009: Wrong SQL_DESC_LITERAL_PREFIX for date-time types */ DECLARE_TEST(t_bug31009) { SQLCHAR data[20]; SQLSMALLINT len; SQLLEN dlen; ok_sql(hstmt, "SELECT CAST('2007-01-13' AS DATE) AS col1"); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_LITERAL_PREFIX, data, sizeof(data), &len, NULL)); is_num(len, 1); is_str(data, "'", 2); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_LITERAL_SUFFIX, data, sizeof(data), &len, NULL)); is_num(len, 1); is_str(data, "'", 2); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, data, sizeof(data), &dlen)); is_num(dlen, 10); is_str(data, "2007-01-13", 11); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); return OK; } /** Bug #37342: ODBC TIMESTAMP string format not handled properly by ODBC driver */ DECLARE_TEST(t_bug37342) { SQLCHAR *date= (SQLCHAR *)"{dt '2007-01-13'}"; SQLCHAR *time= (SQLCHAR *)"194759"; SQLCHAR out[30]; TIMESTAMP_STRUCT ts; SQLLEN len= SQL_NTS; ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_TIMESTAMP, 0, 0, date, 0, &len)); ok_sql(hstmt, "SELECT ? AS foo"); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, out, 1), "2007-01-13", 11); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_TYPE_TIME, 0, 0, time, 0, &len)); ok_sql(hstmt, "SELECT ? AS foo"); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, out, 1), "19:47:59", 9); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TYPE_TIME, 0, 0, &ts, sizeof(ts), NULL)); ts.hour= 19; ts.minute= 47; ts.second= 59; ts.fraction= 4; ok_sql(hstmt, "SELECT ? AS foo"); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, out, 1), "19:47:59", 9); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; } BEGIN_TESTS ADD_TEST(my_ts) ADD_TEST(t_tstotime) ADD_TEST(t_tstotime1) ADD_TEST(t_bug25846) ADD_TEST(t_time) ADD_TEST(t_time1) ADD_TEST(t_bug12520) ADD_TEST(t_bug15773) ADD_TEST(t_bug9927) ADD_TEST(t_bug30081) ADD_TEST(t_datecolumns) ADD_TEST(t_bug14414) ADD_TEST(t_bug30939) ADD_TEST(t_bug31009) ADD_TEST(t_bug37342) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_desc.c100644 15766 12 52107 11707541005 17761 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Tests for descriptors. */ #include "odbctap.h" /* * Tests for paramset: * SQL_ATTR_PARAMSET_SIZE (apd->array_size) * SQL_ATTR_PARAM_STATUS_PTR (ipd->array_status_ptr) * SQL_ATTR_PARAM_OPERATION_PTR (apd->array_status_ptr) * SQL_ATTR_PARAMS_PROCESSED_PTR (apd->rows_processed_ptr) */ DECLARE_TEST(t_desc_paramset) { SQLUINTEGER parsetsize= 4; SQLUSMALLINT parstatus[4]; SQLUSMALLINT parop[4]; /* operation */ SQLULEN pardone; /* processed */ SQLHANDLE ipd, apd; SQLINTEGER params1[4]; SQLINTEGER params2[4]; parop[0]= SQL_PARAM_PROCEED; parop[1]= SQL_PARAM_IGNORE; parop[2]= SQL_PARAM_IGNORE; parop[3]= SQL_PARAM_PROCEED; params1[0]= 0; params1[1]= 1; params1[2]= 2; params1[3]= 3; params2[0]= 100; params2[1]= 101; params2[2]= 102; params2[3]= 103; /* get the descriptors */ ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_PARAM_DESC, &apd, SQL_IS_POINTER, NULL)); ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_PARAM_DESC, &ipd, SQL_IS_POINTER, NULL)); /* set the fields */ ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) parsetsize, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, parstatus, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_OPERATION_PTR, parop, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &pardone, 0)); /* verify the fields */ { SQLPOINTER x_parstatus, x_parop, x_pardone; SQLUINTEGER x_parsetsize; ok_desc(apd, SQLGetDescField(apd, 0, SQL_DESC_ARRAY_SIZE, &x_parsetsize, SQL_IS_UINTEGER, NULL)); ok_desc(ipd, SQLGetDescField(ipd, 0, SQL_DESC_ARRAY_STATUS_PTR, &x_parstatus, SQL_IS_POINTER, NULL)); ok_desc(apd, SQLGetDescField(apd, 0, SQL_DESC_ARRAY_STATUS_PTR, &x_parop, SQL_IS_POINTER, NULL)); ok_desc(ipd, SQLGetDescField(ipd, 0, SQL_DESC_ROWS_PROCESSED_PTR, &x_pardone, SQL_IS_POINTER, NULL)); is_num(x_parsetsize, parsetsize); is(x_parstatus == parstatus); is(x_parop == parop); is(x_pardone == &pardone); } ok_sql(hstmt, "drop table if exists t_paramset"); ok_sql(hstmt, "create table t_paramset(x int, y int)"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"insert into t_paramset " "values (?, ?)", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_INTEGER, SQL_C_LONG, 0, 0, params1, sizeof(SQLINTEGER), NULL)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_INTEGER, SQL_C_LONG, 0, 0, params2, sizeof(SQLINTEGER), NULL)); /* ok_stmt(hstmt, SQLExecute(hstmt)); */ /* TODO, finish test and implement */ return OK; } /* * Test for errors when setting descriptor fields. */ DECLARE_TEST(t_desc_set_error) { SQLHANDLE ird, ard; SQLPOINTER array_status_ptr= (SQLPOINTER) 0xc0c0; ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_ROW_DESC, &ird, SQL_IS_POINTER, NULL)); ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &ard, SQL_IS_POINTER, NULL)); /* Test bad header field permissions */ expect_desc(ard, SQLSetDescField(ard, 0, SQL_DESC_ROWS_PROCESSED_PTR, NULL, SQL_IS_POINTER), SQL_ERROR); is(check_sqlstate_ex(ard, SQL_HANDLE_DESC, "HY091") == OK); /* Test the HY016 error received when setting any field on an IRD * besides SQL_DESC_ARRAY_STATUS_PTR or SQL_DESC_ROWS_PROCESSED_PTR. * * Windows intercepts this and returns HY091 */ ok_desc(ird, SQLSetDescField(ird, 0, SQL_DESC_ARRAY_STATUS_PTR, array_status_ptr, SQL_IS_POINTER)); expect_desc(ird, SQLSetDescField(ird, 0, SQL_DESC_AUTO_UNIQUE_VALUE, (SQLPOINTER) 1, SQL_IS_INTEGER), SQL_ERROR); #ifdef _WIN32 is(check_sqlstate_ex(ird, SQL_HANDLE_DESC, "HY091") == OK); #else is(check_sqlstate_ex(ird, SQL_HANDLE_DESC, "HY016") == OK); #endif /* Test invalid field identifier (will be HY016 on ird, HY091 on others) */ expect_desc(ard, SQLSetDescField(ard, 0, 999, NULL, SQL_IS_POINTER), SQL_ERROR); is(check_sqlstate_ex(ard, SQL_HANDLE_DESC, "HY091") == OK); /* Test bad data type (SQLINTEGER cant be assigned to SQLPOINTER) */ expect_desc(ard, SQLSetDescField(ard, 0, SQL_DESC_BIND_OFFSET_PTR, NULL, SQL_IS_INTEGER), SQL_ERROR); is(check_sqlstate_ex(ard, SQL_HANDLE_DESC, "HY015") == OK); return OK; } /* Implicit Resetting of COUNT Field with SQLBindCol() */ DECLARE_TEST(t_sqlbindcol_count_reset) { SQLHANDLE ard; SQLINTEGER count; SQLCHAR *buf[10]; ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &ard, SQL_IS_POINTER, NULL)); ok_desc(ard, SQLGetDescField(ard, 0, SQL_DESC_COUNT, &count, SQL_IS_INTEGER, NULL)); is_num(count, 0); ok_sql(hstmt, "select 1,2,3,4,5"); ok_desc(ard, SQLGetDescField(ard, 0, SQL_DESC_COUNT, &count, SQL_IS_INTEGER, NULL)); is_num(count, 0); /* bind column 3 -> expand to count = 3 */ ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_CHAR, buf, 10, NULL)); ok_desc(ard, SQLGetDescField(ard, 0, SQL_DESC_COUNT, &count, SQL_IS_INTEGER, NULL)); is_num(count, 3); /* unbind column 3 -> contract to count = 0 */ ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_DEFAULT, NULL, 0, NULL)); ok_desc(ard, SQLGetDescField(ard, 0, SQL_DESC_COUNT, &count, SQL_IS_INTEGER, NULL)); is_num(count, 0); /* bind column 2 -> expand to count = 2 */ ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR, buf, 10, NULL)); ok_desc(ard, SQLGetDescField(ard, 0, SQL_DESC_COUNT, &count, SQL_IS_INTEGER, NULL)); is_num(count, 2); /* bind column 3 -> expand to count = 3 */ ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_CHAR, buf, 10, NULL)); ok_desc(ard, SQLGetDescField(ard, 0, SQL_DESC_COUNT, &count, SQL_IS_INTEGER, NULL)); is_num(count, 3); /* unbind column 3 -> contract to count = 2 */ ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_DEFAULT, NULL, 0, NULL)); ok_desc(ard, SQLGetDescField(ard, 0, SQL_DESC_COUNT, &count, SQL_IS_INTEGER, NULL)); is_num(count, 2); return OK; } /* Test that if no type is given to SQLSetDescField(), that the correct default is used. See Bug#31720. */ DECLARE_TEST(t_desc_default_type) { SQLHANDLE ard, apd; SQLINTEGER inval= 20, outval= 0; ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"select ?", SQL_NTS)); ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_PARAM_DESC, &apd, 0, NULL)); ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &ard, 0, NULL)); ok_desc(apd, SQLSetDescField(apd, 1, SQL_DESC_CONCISE_TYPE, (SQLPOINTER) SQL_C_LONG, 0)); ok_desc(apd, SQLSetDescField(apd, 1, SQL_DESC_DATA_PTR, &inval, 0)); ok_desc(ard, SQLSetDescField(ard, 1, SQL_DESC_CONCISE_TYPE, (SQLPOINTER) SQL_C_LONG, 0)); ok_desc(ard, SQLSetDescField(ard, 1, SQL_DESC_DATA_PTR, &outval, 0)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(outval, inval); return OK; } /* Basic use of explicitly allocated descriptor */ DECLARE_TEST(t_basic_explicit) { SQLHANDLE expapd; SQLINTEGER result; SQLINTEGER impparam= 2; SQLINTEGER expparam= 999; ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "select ?", SQL_NTS)); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &result, 0, NULL)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &impparam, 0, NULL)); ok_stmt(hstmt, SQLExecute(hstmt)); result= 0; ok_stmt(hstmt, SQLFetch(hstmt)); is_num(result, impparam); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* setup a new descriptor */ ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &expapd)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_APP_PARAM_DESC, expapd, 0)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &expparam, 0, NULL)); ok_stmt(hstmt, SQLExecute(hstmt)); result= 0; ok_stmt(hstmt, SQLFetch(hstmt)); is_num(result, expparam); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* free the descriptor, will set apd back to original on hstmt */ ok_desc(expapd, SQLFreeHandle(SQL_HANDLE_DESC, expapd)); ok_stmt(hstmt, SQLExecute(hstmt)); result= 0; ok_stmt(hstmt, SQLFetch(hstmt)); is_num(result, impparam); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; } /* Test the various error scenarios possible with explicitly allocated descriptors */ DECLARE_TEST(t_explicit_error) { SQLHANDLE desc1, desc2; SQLHANDLE expapd; SQLHANDLE hstmt2; /* TODO using an exp from a different dbc */ ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt2)); ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &desc1, 0, NULL)); ok_stmt(hstmt2, SQLGetStmtAttr(hstmt2, SQL_ATTR_APP_ROW_DESC, &desc2, 0, NULL)); /* can't set implicit ard from a different statement */ expect_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, desc2, 0), SQL_ERROR); is(check_sqlstate(hstmt, "HY017") == OK); /* can set it to the same statement */ ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, desc1, 0)); /* can't set implementation descriptors */ expect_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_IMP_ROW_DESC, desc1, 0), SQL_ERROR); is(check_sqlstate(hstmt, "HY024") == OK || check_sqlstate(hstmt, "HY017") == OK); /* can't free implicit descriptors This crashes unixODBC 2.2.11, as it improperly frees the descriptor, and again tries to when freeing the statement handle. */ if (using_unixodbc_version(henv, "2.2.11")) { printMessage("free implicit descriptor test skipped under unixODBC 2.2.11"); } else { expect_desc(desc1, SQLFreeHandle(SQL_HANDLE_DESC, desc1), SQL_ERROR); is(check_sqlstate_ex(desc1, SQL_HANDLE_DESC, "HY017") == OK); } /* can't set apd as ard (and vice-versa) */ ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &expapd)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_APP_PARAM_DESC, expapd, 0)); /* this makes expapd an apd */ expect_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, expapd, 0), SQL_ERROR); is(check_sqlstate(hstmt, "HY024") == OK); /* this exposes a bug in unixODBC (2.2.12 and current as of 2007-12-14). Even though the above call failed, unixODBC saved this value internally and returns it. desc1 should *not* be the same as the explicit apd */ ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &desc1, 0, NULL)); printMessage("explicit apd: %x, stmt's ard: %x", expapd, desc1); return OK; } /* Test that statements are handled correctly when freeing an explicit descriptor associated with multiple statements. */ DECLARE_TEST(t_mult_stmt_free) { #define mult_count 3 SQLHANDLE expard, expapd; #ifndef _WIN32 SQLHANDLE desc; #endif SQLHANDLE stmt[mult_count]; SQLINTEGER i; SQLINTEGER imp_params[mult_count]; SQLINTEGER imp_results[mult_count]; SQLINTEGER exp_param; SQLINTEGER exp_result; ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &expapd)); ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &expard)); for (i= 0; i < mult_count; ++i) { ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &stmt[i])); /* we bind these now, but use at the end */ imp_params[i]= 900 + i; ok_stmt(stmt[i], SQLBindParameter(stmt[i], 1, SQL_PARAM_INPUT, SQL_INTEGER, SQL_C_LONG, 0, 0, &imp_params[i], 0, NULL)); ok_stmt(stmt[i], SQLBindCol(stmt[i], 1, SQL_C_LONG, &imp_results[i], 0, NULL)); ok_stmt(stmt[i], SQLSetStmtAttr(stmt[i], SQL_ATTR_APP_ROW_DESC, expard, 0)); ok_stmt(stmt[i], SQLSetStmtAttr(stmt[i], SQL_ATTR_APP_PARAM_DESC, expapd, 0)); } /* this will work for all */ ok_stmt(stmt[0], SQLBindParameter(stmt[0], 1, SQL_PARAM_INPUT, SQL_INTEGER, SQL_C_LONG, 0, 0, &exp_param, 0, NULL)); ok_stmt(stmt[0], SQLBindCol(stmt[0], 1, SQL_C_LONG, &exp_result, 0, NULL)); /* check that the explicit ard and apd are working */ for (i= 0; i < mult_count; ++i) { exp_param= 200 + i; ok_stmt(stmt[i], SQLExecDirect(stmt[i], (SQLCHAR *)"select ?", SQL_NTS)); ok_stmt(stmt[i], SQLFetch(stmt[i])); is_num(exp_result, exp_param); ok_stmt(stmt[i], SQLFreeStmt(stmt[i], SQL_CLOSE)); } /* Windows ODBC DM has a bug that crashes when using a statement after free-ing an explicitly allocated that was associated with it. (This exact test was run against SQL Server and crashed in the exact same spot.) Tested on Windows 2003 x86 and Windows XP x64 both w/MDAC 2.8. */ #ifndef _WIN32 /* now free the explicit apd+ard and the stmts should go back to their implicit descriptors */ ok_desc(expard, SQLFreeHandle(SQL_HANDLE_DESC, expard)); ok_desc(expapd, SQLFreeHandle(SQL_HANDLE_DESC, expapd)); /* check that the original values worked */ for (i= 0; i < mult_count; ++i) { ok_stmt(stmt[i], SQLExecDirect(stmt[i], (SQLCHAR *)"select ?", SQL_NTS)); ok_stmt(stmt[i], SQLFetch(stmt[i])); ok_stmt(stmt[i], SQLFreeStmt(stmt[i], SQL_CLOSE)); } for (i= 0; i < mult_count; ++i) { is_num(imp_results[i], imp_params[i]); } /* bug in unixODBC - it still returns the explicit descriptor These should *not* be the same */ ok_stmt(hstmt, SQLGetStmtAttr(stmt[0], SQL_ATTR_APP_ROW_DESC, &desc, SQL_IS_POINTER, NULL)); printMessage("explicit ard = %x, stmt[0]'s implicit ard = %x", expard, desc); #endif return OK; #undef mult_count } /* Test that when we set a stmt's ard from an explicit descriptor to null, it uses the implicit descriptor again. Also the statement will disassociate itself from the explicit descriptor. */ DECLARE_TEST(t_set_null_use_implicit) { SQLHANDLE expard, hstmt1; SQLINTEGER imp_result= 0, exp_result= 0; if(using_unixodbc_version(henv, "2.2.11")) skip("unixODBC 2.2.11 doesn't handle resetting statement " "descriptors correctly"); /* we use a separate statement handle to test that it correctly disassociates itself from the descriptor */ ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt1)); ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &expard)); /* this affects the implicit ard */ ok_stmt(hstmt1, SQLBindCol(hstmt1, 1, SQL_C_LONG, &imp_result, 0, NULL)); /* set the explicit ard */ ok_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_APP_ROW_DESC, expard, 0)); /* this affects the expard */ ok_stmt(hstmt1, SQLBindCol(hstmt1, 1, SQL_C_LONG, &exp_result, 0, NULL)); /* set it to null, getting rid of the expard */ ok_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_APP_ROW_DESC, SQL_NULL_HANDLE, 0)); ok_sql(hstmt1, "select 1"); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_num(exp_result, 0); is_num(imp_result, 1); ok_stmt(hstmt1, SQLFreeHandle(SQL_HANDLE_STMT, hstmt1)); /* if stmt disassociation failed, this will crash */ ok_desc(expard, SQLFreeHandle(SQL_HANDLE_DESC, expard)); return OK; } /* Test free-ing a statement that has an explicitely allocated descriptor associated with it. If this test fails, it will crash due to the statement not being disassociated with the descriptor correctly. */ DECLARE_TEST(t_free_stmt_with_exp_desc) { SQLHANDLE expard, hstmt1; SQLINTEGER imp_result= 0, exp_result= 0; ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &expard)); ok_con(hdbc, SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt1)); /* set the explicit ard */ ok_stmt(hstmt1, SQLSetStmtAttr(hstmt1, SQL_ATTR_APP_ROW_DESC, expard, 0)); /* free the statement, THEN the descriptors */ ok_stmt(hstmt1, SQLFreeHandle(SQL_HANDLE_STMT, hstmt1)); ok_desc(expard, SQLFreeHandle(SQL_HANDLE_DESC, expard)); return OK; } /* Bug #41081, Unable to retreive null SQL_NUMERIC with ADO. It's setting SQL_DESC_PRECISION after SQLBindCol(). We were "unbinding" incorrectly, by not only clearing data_ptr, but also octet_length_ptr and indicator_ptr. */ DECLARE_TEST(t_bug41081) { SQLHANDLE ard; SQLINTEGER res; SQLLEN ind; SQLPOINTER data_ptr, octet_length_ptr; ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &ard, SQL_IS_POINTER, NULL)); ok_sql(hstmt, "select 1"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_LONG, &res, 0, &ind)); /* cause to unbind */ ok_desc(ard, SQLSetDescField(ard, 1, SQL_DESC_PRECISION, (SQLPOINTER) 10, SQL_IS_SMALLINT)); /* check proper unbinding */ ok_desc(ard, SQLGetDescField(ard, 1, SQL_DESC_DATA_PTR, &data_ptr, SQL_IS_POINTER, NULL)); ok_desc(ard, SQLGetDescField(ard, 1, SQL_DESC_OCTET_LENGTH_PTR, &octet_length_ptr, SQL_IS_POINTER, NULL)); is(data_ptr == NULL); is(octet_length_ptr == &ind); return OK; } /* Bug #44576 - SQL_DESC_DATETIME_INTERVAL_CODE not set on descriptor */ DECLARE_TEST(t_bug44576) { SQLSMALLINT interval_code; SQLSMALLINT concise_type; SQLHANDLE ird; ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_ROW_DESC, &ird, 0, NULL)); ok_sql(hstmt, "select cast('2000-10-10' as date)"); ok_desc(ird, SQLGetDescField(ird, 1, SQL_DESC_CONCISE_TYPE, &concise_type, SQL_IS_SMALLINT, NULL)); ok_desc(ird, SQLGetDescField(ird, 1, SQL_DESC_DATETIME_INTERVAL_CODE, &interval_code, SQL_IS_SMALLINT, NULL)); is_num(concise_type, SQL_TYPE_DATE); is_num(interval_code, SQL_CODE_DATE); return OK; } /* If no default database is selected for the connection, call of SQLColumns causes error "Unknown database 'null'" */ DECLARE_TEST(t_desc_curcatalog) { SQLHDBC hdbc1; SQLHSTMT hstmt1; SQLCHAR conn_in[256]; SQLHANDLE ird; ok_env(henv, SQLAllocConnect(henv, &hdbc1)); /* Connecting not specifying default db */ sprintf((char *)conn_in, "DRIVER=%s;SERVER=%s;UID=%s;PWD=%s", mydriver, myserver, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn_in, ";SOCKET="); strcat((char *)conn_in, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn_in, pbuff); } ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn_in, sizeof(conn_in), NULL, 0, NULL, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_sql(hstmt1, "select 10 AS no_catalog_column"); ok_stmt(hstmt1, SQLGetStmtAttr(hstmt1, SQL_ATTR_IMP_ROW_DESC, &ird, 0, NULL)); ok_desc(ird, SQLGetDescField(ird, 1, SQL_DESC_CATALOG_NAME, conn_in, sizeof(conn_in), NULL)); is(conn_in==NULL); ok_con(hdbc1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); return OK; } BEGIN_TESTS ADD_TODO(t_desc_paramset) ADD_TEST(t_desc_set_error) ADD_TEST(t_sqlbindcol_count_reset) ADD_TEST(t_desc_default_type) ADD_TEST(t_basic_explicit) ADD_TEST(t_explicit_error) ADD_TEST(t_mult_stmt_free) ADD_TEST(t_set_null_use_implicit) ADD_TEST(t_free_stmt_with_exp_desc) ADD_TEST(t_bug41081) ADD_TEST(t_bug44576) ADD_TODO(t_desc_curcatalog) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_catalog.c100644 15766 12 255037 11707541005 20504 0ustar00cteamstaff/* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" DECLARE_TEST(my_columns_null) { SQLLEN rowCount= 0; /* initialize data */ ok_sql(hstmt, "drop table if exists my_column_null"); ok_sql(hstmt, "create table my_column_null(id int not null, name varchar(30))"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLColumns(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"my_column_null", SQL_NTS, NULL, SQL_NTS)); ok_stmt(hstmt, SQLRowCount(hstmt, &rowCount)); is_num(rowCount, 2); is_num(2, my_print_non_format_result(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS my_column_null"); return OK; } DECLARE_TEST(my_drop_table) { ok_sql(hstmt, "drop table if exists my_drop_table"); ok_sql(hstmt, "create table my_drop_table(id int not null)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"my_drop_table", SQL_NTS, NULL, 0)); is_num(1, my_print_non_format_result(hstmt)); ok_sql(hstmt, "drop table my_drop_table"); return OK; } #define TODBC_BIND_CHAR(n,buf) SQLBindCol(hstmt,n,SQL_C_CHAR,&buf,sizeof(buf),NULL); DECLARE_TEST(my_table_dbs) { SQLCHAR database[100]; SQLRETURN rc; SQLINTEGER nrows; SQLLEN lenOrNull, rowCount= 0; ok_sql(hstmt, "DROP DATABASE IF EXISTS my_all_db_test1"); ok_sql(hstmt, "DROP DATABASE IF EXISTS my_all_db_test2"); ok_sql(hstmt, "DROP DATABASE IF EXISTS my_all_db_test3"); ok_sql(hstmt, "DROP DATABASE IF EXISTS my_all_db_test4"); ok_stmt(hstmt, SQLTables(hstmt,(SQLCHAR *)SQL_ALL_CATALOGS,1,"",0,"",0,NULL,0)); /* Added calls to SQLRowCount just to have tests of it with SQLTAbles. */ ok_stmt(hstmt, SQLRowCount(hstmt, &rowCount)); nrows = my_print_non_format_result(hstmt); is_num(rowCount, nrows) rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTables(hstmt,(SQLCHAR *)SQL_ALL_CATALOGS,SQL_NTS,"",0,"",0, NULL,0); mystmt(hstmt,rc); is_num(nrows, my_print_non_format_result(hstmt)); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTables(hstmt,(SQLCHAR *)"test",4,NULL,0,NULL,0,NULL,0); mystmt(hstmt,rc); ok_stmt(hstmt, SQLRowCount(hstmt, &rowCount)); is_num(rowCount, my_print_non_format_result(hstmt)); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); /* test fails on Win2003 x86 w/DM if len=5, SQL_NTS is used instead */ rc = SQLTables(hstmt,(SQLCHAR *)"mysql",SQL_NTS,NULL,0,NULL,0,NULL,0); mystmt(hstmt,rc); is(my_print_non_format_result(hstmt) != 0); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTables(hstmt,(SQLCHAR *)"%",1,"",0,"",0,NULL,0); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); memset(database,0,100); rc = SQLGetData(hstmt,1,SQL_C_CHAR,database,100,NULL); mystmt(hstmt,rc); printMessage("catalog: %s", database); memset(database,0,100); rc = SQLGetData(hstmt,2,SQL_C_CHAR,database,100,&lenOrNull); mystmt(hstmt,rc); printMessage("schema: %s", database); myassert(lenOrNull == SQL_NULL_DATA); memset(database,0,100); rc = SQLGetData(hstmt,3,SQL_C_CHAR,database,100,&lenOrNull); mystmt(hstmt,rc); printMessage("table: %s", database); myassert(lenOrNull == SQL_NULL_DATA); memset(database,0,100); rc = SQLGetData(hstmt,4,SQL_C_CHAR,database,100,&lenOrNull); mystmt(hstmt,rc); printMessage("type: %s", database); myassert(lenOrNull == SQL_NULL_DATA); memset(database,0,100); rc = SQLGetData(hstmt,5,SQL_C_CHAR, database,100,&lenOrNull); mystmt(hstmt,rc); printMessage("database remark: %s", database); myassert(lenOrNull == SQL_NULL_DATA); SQLFreeStmt(hstmt,SQL_UNBIND); SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "CREATE DATABASE my_all_db_test1"); ok_sql(hstmt, "CREATE DATABASE my_all_db_test2"); ok_sql(hstmt, "CREATE DATABASE my_all_db_test3"); ok_sql(hstmt, "CREATE DATABASE my_all_db_test4"); rc = SQLTables(hstmt, (SQLCHAR *)"%", 1, "", 0, "", 0, "", 0); mystmt(hstmt,rc); nrows += 4; is_num(nrows, my_print_non_format_result(hstmt)); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTables(hstmt,(SQLCHAR *)SQL_ALL_CATALOGS, SQL_NTS, "", 0, "", 0, "", 0); mystmt(hstmt,rc); is_num(my_print_non_format_result(hstmt), nrows); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTables(hstmt, (SQLCHAR *)"my_all_db_test", SQL_NTS, "", 0, "", 0, "", 0); mystmt(hstmt,rc); ok_stmt(hstmt, SQLRowCount(hstmt, &rowCount)); is_num(rowCount, 0); is_num(my_print_non_format_result(hstmt), 0); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTables(hstmt, (SQLCHAR *)"my_all_db_test%", SQL_NTS, "", 0, "", 0, NULL, 0); mystmt(hstmt,rc); is_num(my_print_non_format_result(hstmt), 4); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); /* unknown table should be empty */ rc = SQLTables(hstmt, (SQLCHAR *)"my_all_db_test%", SQL_NTS, NULL, 0, (SQLCHAR *)"xyz", SQL_NTS, NULL, 0); mystmt(hstmt,rc); is_num(my_print_non_format_result(hstmt), 0); rc = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP DATABASE my_all_db_test1"); ok_sql(hstmt, "DROP DATABASE my_all_db_test2"); ok_sql(hstmt, "DROP DATABASE my_all_db_test3"); ok_sql(hstmt, "DROP DATABASE my_all_db_test4"); return OK; } DECLARE_TEST(my_colpriv) { ok_sql(hstmt, "DROP TABLE IF EXISTS test_colprev1"); ok_sql(hstmt, "DROP TABLE IF EXISTS test_colprev2"); ok_sql(hstmt, "DROP TABLE IF EXISTS test_colprev3"); ok_sql(hstmt, "CREATE TABLE test_colprev1(a INT,b INT,c INT, d INT)"); ok_sql(hstmt, "CREATE TABLE test_colprev2(a INT,b INT,c INT, d INT)"); ok_sql(hstmt, "CREATE TABLE test_colprev3(a INT,b INT,c INT, d INT)"); (void)SQLExecDirect(hstmt, (SQLCHAR *)"DROP USER my_colpriv", SQL_NTS); ok_sql(hstmt, "CREATE USER my_colpriv"); ok_sql(hstmt, "GRANT SELECT(a,b),INSERT(d),UPDATE(c) ON test_colprev1 TO my_colpriv"); ok_sql(hstmt, "GRANT SELECT(c,a),UPDATE(a,b) ON test_colprev3 TO my_colpriv"); ok_sql(hstmt, "FLUSH PRIVILEGES"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLColumnPrivileges(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_colprev1", SQL_NTS, (SQLCHAR *)/*NULL*/"%", SQL_NTS)); printMessage("1) Privileges on all columns from test_colprev1"); is_num(4, my_print_non_format_result(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLColumnPrivileges(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_colprev1", SQL_NTS, (SQLCHAR *)"a", SQL_NTS)); printMessage("2) Privileges on column 'a' from test_colprev1"); is_num(my_print_non_format_result(hstmt), 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLColumnPrivileges(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_colprev2", SQL_NTS, (SQLCHAR *)"%", SQL_NTS)); printMessage("3) Privileges on all columns from test_colprev2"); is_num(my_print_non_format_result(hstmt), 0); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLColumnPrivileges(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_colprev3", SQL_NTS, (SQLCHAR *)"%", SQL_NTS)); printMessage("4) Privileges on all columns from test_colprev3"); is_num(my_print_non_format_result(hstmt), 4); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLColumnPrivileges(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"test_%", SQL_NTS, (SQLCHAR *)"%", SQL_NTS)); is_num(my_print_non_format_result(hstmt), 0); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLColumnPrivileges(hstmt, (SQLCHAR *)"mysql", SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"columns_priv", SQL_NTS, NULL, SQL_NTS)); my_print_non_format_result(hstmt); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP USER my_colpriv"); ok_sql(hstmt, "DROP TABLE test_colprev1, test_colprev2, test_colprev3"); return OK; } DECLARE_TEST(t_sqlprocedures) { if (!mysql_min_version(hdbc, "5.0", 3)) skip("server does not support stored procedures"); /* avoid errors in case binary log is activated */ ok_sql(hstmt, "SET GLOBAL log_bin_trust_function_creators = 1"); ok_sql(hstmt, "DROP FUNCTION IF EXISTS t_sqlproc_func"); ok_sql(hstmt, "CREATE FUNCTION t_sqlproc_func (a INT) RETURNS INT RETURN SQRT(a)"); ok_sql(hstmt, "DROP PROCEDURE IF EXISTS t_sqlproc_proc"); ok_sql(hstmt, "CREATE PROCEDURE t_sqlproc_proc (OUT a INT) BEGIN" " SELECT COUNT(*) INTO a FROM t_sqlproc;" "END;"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Try without specifying a catalog. */ ok_stmt(hstmt, SQLProcedures(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_sqlproc%", SQL_NTS)); is_num(my_print_non_format_result(hstmt), 2); /* And try with specifying a catalog. */ ok_stmt(hstmt, SQLProcedures(hstmt, (SQLCHAR *)"test", SQL_NTS, NULL, 0, (SQLCHAR *)"t_sqlproc%", SQL_NTS)); is_num(my_print_non_format_result(hstmt), 2); ok_sql(hstmt, "DROP PROCEDURE t_sqlproc_proc"); ok_sql(hstmt, "DROP FUNCTION t_sqlproc_func"); return OK; } DECLARE_TEST(t_catalog) { SQLRETURN rc; SQLCHAR name[MYSQL_NAME_LEN+1]; SQLUSMALLINT i; SQLSMALLINT ncols, len; SQLCHAR colnames[19][20]= { "TABLE_CAT","TABLE_SCHEM","TABLE_NAME","COLUMN_NAME", "DATA_TYPE","TYPE_NAME","COLUMN_SIZE","BUFFER_LENGTH", "DECIMAL_DIGITS","NUM_PREC_RADIX","NULLABLE","REMARKS", "COLUMN_DEF","SQL_DATA_TYPE","SQL_DATETIME_SUB", "CHAR_OCTET_LENGTH","ORDINAL_POSITION","IS_NULLABLE" }; SQLSMALLINT collengths[18]= { 9,11,10,11,9,9,11,13,14,14,8,7,10,13,16,17,16,11 }; ok_sql(hstmt, "drop table if exists t_catalog"); ok_sql(hstmt,"create table t_catalog(abc tinyint, bcdefghijklmno char(4), uifield int unsigned not null)"); ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_catalog", 9, NULL, 0)); rc = SQLNumResultCols(hstmt, &ncols); mystmt(hstmt,rc); printMessage("total columns: %d", ncols); myassert(ncols == 18); myassert(myresult(hstmt) == 3); SQLFreeStmt(hstmt, SQL_UNBIND); SQLFreeStmt(hstmt, SQL_CLOSE); rc = SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_catalog", 9, NULL, 0); mystmt(hstmt,rc); rc = SQLNumResultCols(hstmt,&ncols); mystmt(hstmt,rc); for (i= 1; i <= (SQLUINTEGER) ncols; i++) { rc = SQLDescribeCol(hstmt, i, name, MYSQL_NAME_LEN+1, &len, NULL, NULL, NULL, NULL); mystmt(hstmt,rc); printMessage("column %d: %s (%d)", i, name, len); is_num(len, collengths[i - 1]); is_str(name, colnames[i - 1], len); } SQLFreeStmt(hstmt,SQL_CLOSE); ok_sql(hstmt, "DROP TABLE IF EXISTS t_catalog"); return OK; } DECLARE_TEST(tmysql_specialcols) { SQLRETURN rc; tmysql_exec(hstmt,"drop table tmysql_specialcols"); rc = tmysql_exec(hstmt,"create table tmysql_specialcols(col1 int primary key, col2 varchar(30), col3 int)"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"create index tmysql_ind1 on tmysql_specialcols(col1)"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_specialcols values(100,'venu',1)"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into tmysql_specialcols values(200,'MySQL',2)"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"select * from tmysql_specialcols"); mystmt(hstmt,rc); myresult(hstmt); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_stmt(hstmt, SQLSpecialColumns(hstmt, SQL_BEST_ROWID, NULL,0, NULL,0, (SQLCHAR *)"tmysql_specialcols",SQL_NTS, SQL_SCOPE_SESSION, SQL_NULLABLE)); myresult(hstmt); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"drop table tmysql_specialcols"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); return OK; } /* To test SQLColumns misc case */ DECLARE_TEST(t_columns) { SQLSMALLINT NumPrecRadix, DataType, Nullable, DecimalDigits; SQLLEN cbColumnSize, cbDecimalDigits, cbNumPrecRadix, cbDataType, cbNullable; SQLINTEGER cbDatabaseName; SQLUINTEGER ColumnSize, i; SQLUINTEGER ColumnCount= 7; SQLCHAR ColumnName[MAX_NAME_LEN], DatabaseName[MAX_NAME_LEN]; SQLINTEGER Values[7][5][2]= { { {5,2}, {5,4}, {0,2}, {10,2}, {1,2}}, { {1,2}, {5,4}, {0,-1}, {10,-1}, {1,2}}, { {12,2}, {20,4}, {0,-1}, {10,-1}, {0,2}}, { {3,2}, {10,4}, {2,2}, {10,2}, {1,2}}, { {-6,2}, {3,4}, {0,2}, {10,2}, {0,2}}, { {4,2}, {10,4}, {0,2}, {10,2}, {0,2}}, { {-6,2}, {3,4}, {0,2}, {10,2}, {1,2}} }; ok_sql(hstmt, "DROP TABLE IF EXISTS t_columns"); ok_sql(hstmt, "CREATE TABLE t_columns (col0 SMALLINT," "col1 CHAR(5), col2 VARCHAR(20) NOT NULL, col3 DECIMAL(10,2)," "col4 TINYINT NOT NULL, col5 INTEGER PRIMARY KEY," "col6 TINYINT NOT NULL UNIQUE AUTO_INCREMENT) CHARSET latin1"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_METADATA_ID, (SQLPOINTER)SQL_FALSE, SQL_IS_UINTEGER)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_con(hdbc, SQLGetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, DatabaseName, MAX_NAME_LEN, &cbDatabaseName)); /* Current Catalog */ for (i= 0; i < ColumnCount; i++) { sprintf((char *)ColumnName, "col%d", (int)i); printMessage("checking column `%s`", (char *)ColumnName); ok_stmt(hstmt, SQLColumns(hstmt, DatabaseName, (SQLSMALLINT)cbDatabaseName, (SQLCHAR *)"", SQL_NTS, (SQLCHAR *)"t_columns", SQL_NTS, ColumnName, SQL_NTS)); /* 5 -- Data type */ ok_stmt(hstmt, SQLBindCol(hstmt, 5, SQL_C_SSHORT, &DataType, 0, &cbDataType)); /* 7 -- Column Size */ ok_stmt(hstmt, SQLBindCol(hstmt, 7, SQL_C_ULONG, &ColumnSize, 0, &cbColumnSize)); /* 9 -- Decimal Digits */ ok_stmt(hstmt, SQLBindCol(hstmt, 9, SQL_C_SSHORT, &DecimalDigits, 0, &cbDecimalDigits)); /* 10 -- Num Prec Radix */ ok_stmt(hstmt, SQLBindCol(hstmt, 10, SQL_C_SSHORT, &NumPrecRadix, 0, &cbNumPrecRadix)); /* 11 -- Nullable */ ok_stmt(hstmt, SQLBindCol(hstmt, 11, SQL_C_SSHORT, &Nullable, 0, &cbNullable)); ok_stmt(hstmt, SQLFetch(hstmt)); /* if you get -8 for col1 here - that's fine. depends on setup. the test probably needs to be changed accordingly */ is_num(DataType, Values[i][0][0]); is_num(cbDataType, Values[i][0][1]); is_num(ColumnSize, Values[i][1][0]); is_num(cbColumnSize, Values[i][1][1]); is_num(DecimalDigits, Values[i][2][0]); is_num(cbDecimalDigits, Values[i][2][1]); is_num(NumPrecRadix, Values[i][3][0]); is_num(cbNumPrecRadix, Values[i][3][1]); is_num(Nullable, Values[i][4][0]); is_num(cbNullable, Values[i][4][1]); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); } ok_sql(hstmt, "DROP TABLE IF EXISTS t_columns"); return OK; } /* Test the bug SQLTables */ typedef struct t_table_bug { SQLCHAR szColName[MAX_NAME_LEN]; SQLSMALLINT pcbColName; SQLSMALLINT pfSqlType; SQLUINTEGER pcbColDef; SQLSMALLINT pibScale; SQLSMALLINT pfNullable; } t_describe_col; t_describe_col t_tables_bug_data[5] = { {"TABLE_CAT", 9, SQL_WVARCHAR, MYSQL_NAME_LEN, 0, SQL_NULLABLE}, {"TABLE_SCHEM",11, SQL_WVARCHAR, MYSQL_NAME_LEN, 0, SQL_NULLABLE}, {"TABLE_NAME", 10, SQL_WVARCHAR, MYSQL_NAME_LEN, 0, SQL_NULLABLE}, {"TABLE_TYPE", 10, SQL_WVARCHAR, MYSQL_NAME_LEN, 0, SQL_NULLABLE}, {"REMARKS", 7, SQL_WVARCHAR, MYSQL_NAME_LEN, 0, SQL_NULLABLE}, }; DECLARE_TEST(t_tables_bug) { SQLSMALLINT i, ColumnCount, pcbColName, pfSqlType, pibScale, pfNullable; SQLULEN pcbColDef; SQLCHAR szColName[MAX_NAME_LEN]; ok_stmt(hstmt, SQLTables(hstmt, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR *)"'TABLE'", SQL_NTS)); ok_stmt(hstmt, SQLNumResultCols(hstmt, &ColumnCount)); is_num(ColumnCount, 5); for (i= 1; i <= ColumnCount; ++i) { ok_stmt(hstmt, SQLDescribeCol(hstmt, (SQLUSMALLINT)i, szColName, MAX_NAME_LEN, &pcbColName, &pfSqlType, &pcbColDef, &pibScale, &pfNullable)); fprintf(stdout, "# Column '%d':\n", i); fprintf(stdout, "# Column Name : %s\n", szColName); fprintf(stdout, "# NameLengh : %d\n", pcbColName); fprintf(stdout, "# DataType : %d\n", pfSqlType); fprintf(stdout, "# ColumnSize : %d\n", pcbColDef); fprintf(stdout, "# DecimalDigits : %d\n", pibScale); fprintf(stdout, "# Nullable : %d\n", pfNullable); is_str(t_tables_bug_data[i-1].szColName, szColName, pcbColName); is_num(t_tables_bug_data[i-1].pcbColName, pcbColName); is_num(t_tables_bug_data[i-1].pfSqlType, pfSqlType); /* This depends on NAME_LEN in mysql_com.h */ #if UNRELIABLE_TEST is(t_tables_bug_data[i-1].pcbColDef == pcbColDef || t_tables_bug_data[i-1].pcbColDef == pcbColDef / 3); #endif is_num(t_tables_bug_data[i-1].pibScale, pibScale); is_num(t_tables_bug_data[i-1].pfNullable, pfNullable); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; } DECLARE_TEST(t_current_catalog) { SQLCHAR cur_db[255], db[255]; SQLRETURN rc; SQLINTEGER len; rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLGetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, db, sizeof(db), &len); mycon(hdbc,rc); fprintf(stdout,"current_catalog: %s (%ld)\n", db, len); is_num(len, 4); is_str(db, "test", 5); rc = SQLSetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, db, SQL_NTS); mycon(hdbc,rc); ok_sql(hstmt, "DROP DATABASE IF EXISTS test_odbc_current"); strcpy((char *)cur_db, "test_odbc_current"); rc = SQLSetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, cur_db, SQL_NTS); mycon_r(hdbc,rc); ok_sql(hstmt, "CREATE DATABASE test_odbc_current"); strcpy((char *)cur_db, "test_odbc_current"); rc = SQLSetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, cur_db, SQL_NTS); mycon(hdbc,rc); rc = SQLGetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, db, 255, &len); mycon(hdbc,rc); fprintf(stdout,"current_catalog: %s (%ld)\n", db, len); is_num(len, 17); is_str(db, cur_db, 18); strcpy((char *)cur_db, "test_odbc_current_12455"); rc = SQLSetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, cur_db, SQL_NTS); mycon_r(hdbc,rc); rc = SQLSetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, cur_db, len); mycon(hdbc,rc); /* reset for further tests */ rc = SQLSetConnectAttr(hdbc, SQL_ATTR_CURRENT_CATALOG, (SQLCHAR *)"test", SQL_NTS); mycon(hdbc,rc); ok_sql(hstmt, "DROP DATABASE test_odbc_current"); return OK; } DECLARE_TEST(tmysql_showkeys) { SQLRETURN rc; tmysql_exec(hstmt,"drop table tmysql_spk"); rc = tmysql_exec(hstmt,"create table tmysql_spk(col1 int primary key)"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"SHOW KEYS FROM tmysql_spk"); mystmt(hstmt,rc); my_assert(1 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS tmysql_spk"); return OK; } DECLARE_TEST(t_sqltables) { SQLRETURN r; SQLINTEGER rows; SQLLEN rowCount; ok_stmt(hstmt, SQLTables(hstmt,NULL,0,NULL,0,NULL,0,NULL,0)); myresult(hstmt); r = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,r); r = SQLTables(hstmt, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR *)"'system table'", SQL_NTS); mystmt(hstmt,r); is_num(myresult(hstmt), 0); r = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,r); r = SQLTables(hstmt, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR *)"TABLE", SQL_NTS); ok_stmt(hstmt, SQLRowCount(hstmt, &rowCount)); mystmt(hstmt,r); is_num(myresult(hstmt), rowCount); r = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,r); r = SQLTables(hstmt, (SQLCHAR *)"TEST", SQL_NTS, (SQLCHAR *)"TEST", SQL_NTS, NULL, 0, (SQLCHAR *)"TABLE", SQL_NTS); mystmt(hstmt,r); myresult(hstmt); r = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,r); r = SQLTables(hstmt, (SQLCHAR *)"%", SQL_NTS, NULL, 0, NULL, 0, NULL, 0); mystmt(hstmt,r); myresult(hstmt); r = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,r); r = SQLTables(hstmt, NULL, 0, (SQLCHAR *)"%", SQL_NTS, NULL, 0, NULL, 0); mystmt(hstmt,r); myresult(hstmt); r = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,r); r = SQLTables(hstmt, "", 0, "", 0, "", 0, (SQLCHAR *)"%", SQL_NTS); mystmt(hstmt,r); rows= myresult(hstmt); is_num(rows, 3); r = SQLFreeStmt(hstmt, SQL_CLOSE); mystmt(hstmt,r); return OK; } DECLARE_TEST(my_information_schema) { SQLCHAR conn[256], conn_out[256]; HDBC hdbc1; HSTMT hstmt1; SQLSMALLINT conn_out_len; SQLRETURN rc; ok_sql(hstmt, "DROP DATABASE IF EXISTS istest__"); ok_sql(hstmt, "DROP DATABASE IF EXISTS istest_1"); ok_sql(hstmt, "DROP DATABASE IF EXISTS istest_2"); ok_sql(hstmt, "CREATE DATABASE istest__"); ok_sql(hstmt, "CREATE DATABASE istest_1"); ok_sql(hstmt, "CREATE DATABASE istest_2"); ok_sql(hstmt, "CREATE TABLE istest__.istab_(a INT,b INT,c INT, d INT)"); ok_sql(hstmt, "CREATE TABLE istest_1.istab1(a INT,b INT,c INT, d INT)"); ok_sql(hstmt, "CREATE TABLE istest_2.istab2(a INT,b INT,c INT, d INT)"); /* We need to have istest__ as the default DB */ sprintf((char *)conn, "DSN=%s;UID=%s;PWD=%s;DATABASE=istest__;OPTION=0", mydsn, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn, pbuff); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, sizeof(conn), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); rc = SQLTables(hstmt1, "istest__", SQL_NTS, "", 0, "istab%", SQL_NTS, NULL, 0); mystmt(hstmt1,rc); /* all tables from all databases should be displayed */ is_num(my_print_non_format_result(hstmt1), 3); rc = SQLFreeStmt(hstmt1, SQL_CLOSE); rc = SQLTables(hstmt1, NULL, 0, NULL, 0, "istab%", SQL_NTS, NULL, 0); mystmt(hstmt1,rc); is_num(my_print_non_format_result(hstmt1), 1); rc = SQLFreeStmt(hstmt1, SQL_CLOSE); mystmt(hstmt1,rc); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); return OK; } /** Bug #4518: SQLForeignKeys returns too many foreign key Bug #27723: SQLForeignKeys does not escape _ and % in the table name arguments The original test case was extended to have a table that would inadvertently get included because of the poor escaping. */ DECLARE_TEST(t_bug4518) { SQLCHAR buff[255]; SQLLEN len; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug4518_c, t_bug4518_c2, t_bug4518ac, " " t_bug4518_p"); ok_sql(hstmt, "CREATE TABLE t_bug4518_p (id INT PRIMARY KEY) ENGINE=InnoDB"); ok_sql(hstmt, "CREATE TABLE t_bug4518_c (id INT, parent_id INT," " FOREIGN KEY (parent_id)" " REFERENCES" " t_bug4518_p(id)" " ON DELETE SET NULL)" " ENGINE=InnoDB"); ok_sql(hstmt, "CREATE TABLE t_bug4518_c2 (id INT, parent_id INT," " FOREIGN KEY (parent_id)" " REFERENCES" " t_bug4518_p(id)" " )" " ENGINE=InnoDB"); ok_sql(hstmt, "CREATE TABLE t_bug4518ac (id INT, parent_id INT," " FOREIGN KEY (parent_id)" " REFERENCES" " t_bug4518_p(id)" " ON DELETE CASCADE" " ON UPDATE NO ACTION" " )" " ENGINE=InnoDB"); ok_stmt(hstmt, SQLForeignKeys(hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug4518_c", SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "t_bug4518_p", 11); is_str(my_fetch_str(hstmt, buff, 4), "id", 2); is_str(my_fetch_str(hstmt, buff, 7), "t_bug4518_c", 11); is_str(my_fetch_str(hstmt, buff, 8), "parent_id", 9); if (mysql_min_version(hdbc, "5.1", 3)) { is_num(my_fetch_int(hstmt, 10), SQL_NO_ACTION); is_num(my_fetch_int(hstmt, 11), SQL_SET_NULL); } else { is_num(my_fetch_int(hstmt, 10), SQL_RESTRICT); is_num(my_fetch_int(hstmt, 11), SQL_RESTRICT); } /* For Bug #19923: Test that schema columns are NULL. */ ok_stmt(hstmt, SQLGetData(hstmt, 2, SQL_C_CHAR, buff, sizeof(buff), &len)); is_num(len, SQL_NULL_DATA); ok_stmt(hstmt, SQLGetData(hstmt, 6, SQL_C_CHAR, buff, sizeof(buff), &len)); is_num(len, SQL_NULL_DATA); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLForeignKeys(hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug4518ac", SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "t_bug4518_p", 11); is_str(my_fetch_str(hstmt, buff, 4), "id", 2); is_str(my_fetch_str(hstmt, buff, 7), "t_bug4518ac", 11); is_str(my_fetch_str(hstmt, buff, 8), "parent_id", 9); if (mysql_min_version(hdbc, "5.1", 3)) { is_num(my_fetch_int(hstmt, 10), SQL_NO_ACTION); is_num(my_fetch_int(hstmt, 11), SQL_CASCADE); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE t_bug4518_c, t_bug4518_c2, t_bug4518ac, t_bug4518_p"); return OK; } /** Tests the non-error code paths in catalog.c that return an empty set to make sure the resulting empty result sets at least indicate the right number of columns. */ DECLARE_TEST(empty_set) { SQLSMALLINT columns; /* SQLTables(): no known table types. */ ok_stmt(hstmt, SQLTables(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"UNKNOWN", SQL_NTS)); ok_stmt(hstmt, SQLNumResultCols(hstmt, &columns)); is_num(columns, 5); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* SQLTables(): no tables found. */ ok_stmt(hstmt, SQLTables(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"no_such_table", SQL_NTS, NULL, SQL_NTS)); ok_stmt(hstmt, SQLNumResultCols(hstmt, &columns)); is_num(columns, 5); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* SQLTables(): empty catalog with existing table */ ok_sql(hstmt, "drop table if exists t_sqltables_empty"); ok_sql(hstmt, "create table t_sqltables_empty (x int)"); ok_stmt(hstmt, SQLTables(hstmt, "", SQL_NTS, NULL, 0, (SQLCHAR *) "t_sqltables_empty", SQL_NTS, NULL, SQL_NTS)); ok_stmt(hstmt, SQLNumResultCols(hstmt, &columns)); is_num(columns, 5); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_sqltables_empty"); return OK; } /** Bug #23031: SQLTables returns inaccurate catalog information on views */ DECLARE_TEST(t_bug23031) { SQLCHAR buff[255]; ok_sql(hstmt, "DROP VIEW IF EXISTS t_bug23031_v"); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug23031_t"); ok_sql(hstmt, "CREATE TABLE t_bug23031_t (a INT) COMMENT 'Whee!'"); ok_sql(hstmt, "CREATE VIEW t_bug23031_v AS SELECT * FROM t_bug23031_t"); /* Get both the table and view. */ ok_stmt(hstmt, SQLTables(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"t_bug23031%", SQL_NTS, NULL, SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "t_bug23031_t", 12); is_str(my_fetch_str(hstmt, buff, 4), "TABLE", 5); is_str(my_fetch_str(hstmt, buff, 5), "Whee!", 5); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "t_bug23031_v", 12); is_str(my_fetch_str(hstmt, buff, 4), "VIEW", 4); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Get just the table. */ ok_stmt(hstmt, SQLTables(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"t_bug23031%", SQL_NTS, (SQLCHAR *)"TABLE", SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "t_bug23031_t", 12); is_str(my_fetch_str(hstmt, buff, 4), "TABLE", 5); is_str(my_fetch_str(hstmt, buff, 5), "Whee!", 5); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Get just the view. */ ok_stmt(hstmt, SQLTables(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"t_bug23031%", SQL_NTS, (SQLCHAR *)"'VIEW'", SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "t_bug23031_v", 12); is_str(my_fetch_str(hstmt, buff, 4), "VIEW", 4); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP VIEW IF EXISTS t_bug23031_v"); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug23031_t"); return OK; } /** Bug #15713: null pointer when use the table qualifier in SQLColumns() */ DECLARE_TEST(bug15713) { HDBC hdbc1; HSTMT hstmt1; SQLCHAR conn[256], conn_out[256]; SQLSMALLINT conn_out_len; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug15713"); ok_sql(hstmt, "CREATE TABLE t_bug15713 (a INT)"); /* The connection strings must not include DATABASE. */ sprintf((char *)conn, "DSN=%s;UID=%s;PWD=%s", mydsn, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn, pbuff); } ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, sizeof(conn), conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLColumns(hstmt1, (SQLCHAR *)"test", SQL_NTS, NULL, 0, (SQLCHAR *)"t_bug15713", SQL_NTS, NULL, 0)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_str(my_fetch_str(hstmt1, conn, 1), "test", 4); is_str(my_fetch_str(hstmt1, conn, 3), "t_bug15713", 10); is_str(my_fetch_str(hstmt1, conn, 4), "a", 1); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug15713"); return OK; } /** Bug #28316: Fatal crash with Crystal Reports and MySQL Server */ DECLARE_TEST(t_bug28316) { ok_stmt(hstmt, SQLProcedures(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS)); return OK; } /** Bug #8860: Generic SQLColumns not supported? */ DECLARE_TEST(bug8860) { SQLCHAR buff[512]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug8860, `t_bug8860_a'b`"); ok_sql(hstmt, "CREATE TABLE t_bug8860 (a INT)"); ok_sql(hstmt, "CREATE TABLE `t_bug8860_a'b` (b INT)"); /* Specifying nothing gets us columns from all of the tables in the current database. */ ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0)); /* We should have at least two rows. There may be more. */ is(myrowcount(hstmt) >= 2); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); if (!using_dm(hdbc)) { /* Specifying "" as the table name gets us nothing. */ /* But iODBC, for one, will convert our "" into a NULL. */ ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"", SQL_NTS, NULL, 0)); is_num(myrowcount(hstmt), 0); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Get the info from just one table. */ ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug8860", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "t_bug8860", 9); is_str(my_fetch_str(hstmt, buff, 4), "a", 1); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Get the info from just one table with a funny name. */ ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug8860_a'b", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "t_bug8860_a'b", 13); is_str(my_fetch_str(hstmt, buff, 4), "b", 1); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE t_bug8860, `t_bug8860_a'b`"); return OK; } /** Bug #26934: SQLTables behavior has changed */ DECLARE_TEST(t_bug26934) { HENV henv1; HDBC hdbc1; HSTMT hstmt1; alloc_basic_handles(&henv1, &hdbc1, &hstmt1); ok_sql(hstmt1, "SET @@wait_timeout = 1"); sleep(2); expect_stmt(hstmt1, SQLTables(hstmt1, (SQLCHAR *)"%", 1, NULL, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS), SQL_ERROR); if (check_sqlstate(hstmt1, "08S01") != OK) return FAIL; free_basic_handles(&henv1, &hdbc1, &hstmt1); return OK; } /** Bug #29888: Crystal wizard throws error on including tables */ DECLARE_TEST(t_bug29888) { ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug29888"); ok_sql(hstmt, "CREATE TABLE t_bug29888 (a INT, b INT)"); ok_stmt(hstmt, SQLColumns(hstmt, mydb, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"t_bug29888", SQL_NTS, (SQLCHAR *)"%", SQL_NTS)); is_num(myrowcount(hstmt), 2); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug29888"); return OK; } /** Bug #14407: SQLColumns gives wrong information of not nulls */ DECLARE_TEST(t_bug14407) { SQLCHAR col[10]; SQLSMALLINT nullable; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug14407"); ok_sql(hstmt, "CREATE TABLE t_bug14407(a INT NOT NULL AUTO_INCREMENT PRIMARY KEY)"); ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug14407", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 4), "a", 1); is_num(my_fetch_int(hstmt, 11), SQL_NULLABLE); is_str(my_fetch_str(hstmt, col, 18), "YES", 3); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /** Bug #26108 MyODBC ADO field attributes reporting adFldMayBeNull for not null columns */ ok_sql(hstmt, "SELECT * FROM t_bug14407"); ok_stmt(hstmt, SQLDescribeCol(hstmt, 1, col, sizeof(col), NULL, NULL, NULL, NULL, &nullable)); is_num(nullable, SQL_NULLABLE); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug14407"); return OK; } /** Bug #19923: MyODBC Foreign key retrieval broken in multiple ways Two issues to test: schema columns should be NULL, not just an empty string, and more than one constraint should be returned. */ DECLARE_TEST(t_bug19923) { SQLCHAR buff[255]; SQLLEN len; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug19923c, t_bug19923b, t_bug19923a"); ok_sql(hstmt, "CREATE TABLE t_bug19923a (a INT PRIMARY KEY) ENGINE=InnoDB"); ok_sql(hstmt, "CREATE TABLE t_bug19923b (b INT PRIMARY KEY) ENGINE=InnoDB"); ok_sql(hstmt, "CREATE TABLE t_bug19923c (a INT, b INT, UNIQUE(a), UNIQUE(b)," "CONSTRAINT `first_constraint` FOREIGN KEY (`b`) REFERENCES `t_bug19923b` (`b`)," "CONSTRAINT `second_constraint` FOREIGN KEY (`a`) REFERENCES `t_bug19923a` (`a`)" ") ENGINE=InnoDB"); ok_stmt(hstmt, SQLForeignKeys(hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug19923c", SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "t_bug19923a", 11); is_str(my_fetch_str(hstmt, buff, 4), "a", 1); is_str(my_fetch_str(hstmt, buff, 7), "t_bug19923c", 11); is_str(my_fetch_str(hstmt, buff, 8), "a", 1); is_str(my_fetch_str(hstmt, buff, 12), "second_constraint", 17); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "t_bug19923b", 11); is_str(my_fetch_str(hstmt, buff, 4), "b", 1); is_str(my_fetch_str(hstmt, buff, 7), "t_bug19923c", 11); is_str(my_fetch_str(hstmt, buff, 8), "b", 1); is_str(my_fetch_str(hstmt, buff, 12), "first_constraint", 16); ok_stmt(hstmt, SQLGetData(hstmt, 2, SQL_C_CHAR, buff, sizeof(buff), &len)); is_num(len, SQL_NULL_DATA); ok_stmt(hstmt, SQLGetData(hstmt, 6, SQL_C_CHAR, buff, sizeof(buff), &len)); is_num(len, SQL_NULL_DATA); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug19923c, t_bug19923b, t_bug19923a"); return OK; } /* Bug #32864 MyODBC /Crystal Reports truncated table names, lost fields when names > 21chars Fails if built with c/c and probably >=5.5 (with everything where SYSTEM_CHARSET_MBMAXLEN != mbmaxlen of UTF8_CHARSET_NUMBER charset) */ DECLARE_TEST(t_bug32864) { SQLLEN dispsize= 0; SQLULEN colsize= 0; SQLCHAR dummy[20]; ok_stmt(hstmt, SQLTables(hstmt, (SQLCHAR *)"%", SQL_NTS, NULL, 0, NULL, 0, NULL, 0)); ok_stmt(hstmt, SQLColAttribute(hstmt, 3, SQL_COLUMN_DISPLAY_SIZE, NULL, 0, NULL, &dispsize)); is_num(dispsize, 64); ok_stmt(hstmt, SQLDescribeCol(hstmt, 3, dummy, sizeof(dummy), NULL, NULL, &colsize, NULL, NULL)); is_num(colsize, 64); return OK; } /* Bug #32989 - Crystal Reports fails if a field has a single quote */ DECLARE_TEST(t_bug32989) { SQLCHAR name[20]; SQLLEN len; ok_sql(hstmt, "drop table if exists t_bug32989"); ok_sql(hstmt, "create table t_bug32989 (`doesn't work` int)"); ok_stmt(hstmt, SQLColumns(hstmt, (SQLCHAR *)"test", SQL_NTS, NULL, 0, (SQLCHAR *)"t_bug32989", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 4, SQL_C_CHAR, name, 20, &len)); is_num(len, 12); is_str(name, "doesn't work", 13); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_bug32989"); return OK; } /** Bug #33298: ADO returns wrong parameter count in the query (always 0) */ DECLARE_TEST(t_bug33298) { SQLRETURN rc= 0; expect_stmt(hstmt, SQLProcedureColumns(hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0), SQL_SUCCESS); rc= SQLFetch(hstmt); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); if (rc == SQL_ERROR) return FAIL; return OK; } /** Bug #12805: ADO failed to retrieve the length of LONGBLOB columns */ DECLARE_TEST(t_bug12805) { SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1; SQLCHAR dummy[10]; SQLULEN length; SQLUINTEGER len2; SET_DSN_OPTION(1 << 27); alloc_basic_handles(&henv1, &hdbc1, &hstmt1); ok_sql(hstmt1, "DROP TABLE IF EXISTS bug12805"); ok_sql(hstmt1, "CREATE TABLE bug12805("\ "id INT PRIMARY KEY auto_increment,"\ "longdata LONGBLOB NULL)"); ok_stmt(hstmt1, SQLColumns(hstmt1, NULL, 0, NULL, 0, (SQLCHAR *)"bug12805", SQL_NTS, (SQLCHAR *)"longdata", SQL_NTS)); ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_stmt(hstmt1, SQLGetData(hstmt1, 7, SQL_C_ULONG, &len2, 0, NULL)); is_num(len2, 2147483647); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); length= 0; ok_sql(hstmt1, "SELECT * FROM bug12805"); ok_stmt(hstmt1, SQLDescribeCol(hstmt1, 2, dummy, sizeof(dummy) - 1, NULL, NULL, &length, NULL, NULL)); is_num(length, 2147483647); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); free_basic_handles(&henv1, &hdbc1, &hstmt1); /* Check without the 32-bit signed flag */ ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"bug12805", SQL_NTS, (SQLCHAR *)"longdata", SQL_NTS)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 7, SQL_C_ULONG, &len2, 0, NULL)); is_num(len2, 4294967295UL); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); length= 0; ok_sql(hstmt, "SELECT * FROM bug12805"); ok_stmt(hstmt, SQLDescribeCol(hstmt, 2, dummy, sizeof(dummy), NULL, NULL, &length, NULL, NULL)); is_num(length, 4294967295UL); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE bug12805"); SET_DSN_OPTION(0); return OK; } /** Bug #30770: Access Violation in myodbc3.dll */ DECLARE_TEST(t_bug30770) { SQLCHAR buff[512]; SQLHENV henv1; SQLHDBC hdbc1; SQLHSTMT hstmt1; SQLCHAR conn[MAX_NAME_LEN]; ok_sql(hstmt, "DROP TABLE IF EXISTS bug30770"); ok_sql(hstmt, "CREATE TABLE bug30770 (a INT)"); /* Connect with no default daabase */ sprintf((char *)conn, "DRIVER=%s;SERVER=%s;" \ "UID=%s;PASSWORD=%s;DATABASE=%s", mydriver, myserver, myuid, mypwd, mydb); if (mysock != NULL) { strcat((char *)conn, ";SOCKET="); strcat((char *)conn, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn, pbuff); } is_num(mydrvconnect(&henv1, &hdbc1, &hstmt1, conn), OK); /* Get the info from just one table. */ ok_stmt(hstmt1, SQLColumns(hstmt1, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"bug30770", SQL_NTS, NULL, 0)); ok_stmt(hstmt1, SQLFetch(hstmt1)); is_str(my_fetch_str(hstmt1, buff, 3), "bug30770", 9); is_str(my_fetch_str(hstmt1, buff, 4), "a", 1); expect_stmt(hstmt1, SQLFetch(hstmt1), SQL_NO_DATA_FOUND); free_basic_handles(&henv1, &hdbc1, &hstmt1); ok_sql(hstmt, "DROP TABLE bug30770"); return OK; } /** Bug #36275: SQLTables buffer overrun */ DECLARE_TEST(t_bug36275) { ok_stmt(hstmt, SQLTables(hstmt, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR *) /* Just a really long literal to blow out the buffer. */ "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789", SQL_NTS)); return OK; } /* Bug #39957 - NULL catalog did not return correct catalog in result */ DECLARE_TEST(t_bug39957) { SQLCHAR buf[50]; ok_sql(hstmt, "drop table if exists t_bug39957"); ok_sql(hstmt, "create table t_bug39957 (x int)"); ok_stmt(hstmt, SQLTables(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug39957", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buf, 3), "t_bug39957", 11); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_bug39957"); return OK; } /* Bug #37621 - SQLDescribeCol returns incorrect values of SQLTables data */ DECLARE_TEST(t_bug37621) { SQLCHAR szColName[128]; SQLSMALLINT iName, iType, iScale, iNullable; SQLULEN uiDef; ok_sql(hstmt, "drop table if exists t_bug37621"); ok_sql(hstmt, "create table t_bug37621 (x int)"); ok_stmt(hstmt, SQLTables(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug37621", SQL_NTS, NULL, 0)); /* Check column properties for the REMARKS column */ ok_stmt(hstmt, SQLDescribeCol(hstmt, 5, szColName, sizeof(szColName), &iName, &iType, &uiDef, &iScale, &iNullable)); is_str(szColName, "REMARKS", 8); is_num(iName, 7); if (iType != SQL_VARCHAR && iType != SQL_WVARCHAR) return FAIL; /* This can fail for the same reason as t_bug32864 */ is_num(uiDef, 80); is_num(iScale, 0); is_num(iNullable, 1); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_bug37621"); return OK; } /* Bug #34272 - SQLColumns returned wrong values for (some) TYPE_NAME and (some) IS_NULLABLE */ DECLARE_TEST(t_bug34272) { SQLCHAR dummy[20]; SQLULEN col6, col18, length; ok_sql(hstmt, "drop table if exists t_bug34272"); ok_sql(hstmt, "create table t_bug34272 (x int unsigned)"); ok_stmt(hstmt, SQLColumns(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"t_bug34272", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLDescribeCol(hstmt, 6, dummy, sizeof(dummy), NULL, NULL, &col6, NULL, NULL)); ok_stmt(hstmt, SQLDescribeCol(hstmt, 18, dummy, sizeof(dummy), NULL, NULL, &col18, NULL, NULL)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 6, SQL_C_CHAR, dummy, col6+1, &length)); is_num(length,16); is_str(dummy, "integer unsigned", length+1); ok_stmt(hstmt, SQLGetData(hstmt, 18, SQL_C_CHAR, dummy, col18+1, &length)); is_num(length,3); is_str(dummy, "YES", length+1); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_bug34272"); return OK; } /* Bug #49660 - constraints with same name for tables with same name but in different db led to doubling of results of SQLForeignKeys */ DECLARE_TEST(t_bug49660) { SQLLEN rowsCount; ok_sql(hstmt, "drop database if exists bug49660"); ok_sql(hstmt, "drop table if exists t_bug49660"); ok_sql(hstmt, "drop table if exists t_bug49660_r"); ok_sql(hstmt, "create database bug49660"); ok_sql(hstmt, "create table bug49660.t_bug49660_r (id int unsigned not null primary key, name varchar(10) not null) ENGINE=InnoDB"); ok_sql(hstmt, "create table bug49660.t_bug49660 (id int unsigned not null primary key, refid int unsigned not null," "foreign key t_bug49660fk (id) references bug49660.t_bug49660_r (id)) ENGINE=InnoDB"); ok_sql(hstmt, "create table t_bug49660_r (id int unsigned not null primary key, name varchar(10) not null) ENGINE=InnoDB"); ok_sql(hstmt, "create table t_bug49660 (id int unsigned not null primary key, refid int unsigned not null," "foreign key t_bug49660fk (id) references t_bug49660_r (id)) ENGINE=InnoDB"); ok_stmt(hstmt, SQLForeignKeys(hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug49660", SQL_NTS)); ok_stmt(hstmt, SQLRowCount(hstmt, &rowsCount)); /* is_num(rowsCount, 1); */ /* Going another way around - sort of more reliable */ ok_stmt(hstmt, SQLFetch(hstmt)); expect_stmt(hstmt,SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt,SQL_CLOSE)); ok_sql(hstmt, "drop database if exists bug49660"); ok_sql(hstmt, "drop table if exists t_bug49660"); ok_sql(hstmt, "drop table if exists t_bug49660_r"); return OK; } /* Bug #51422 - SQLForeignKeys returned keys pointing to unique fields */ DECLARE_TEST(t_bug51422) { SQLLEN rowsCount; ok_sql(hstmt, "drop table if exists t_bug51422"); ok_sql(hstmt, "drop table if exists t_bug51422_r"); ok_sql(hstmt, "create table t_bug51422_r (id int unsigned not null primary key, ukey int unsigned not null," "name varchar(10) not null, UNIQUE KEY uk(ukey)) ENGINE=InnoDB"); ok_sql(hstmt, "create table t_bug51422 (id int unsigned not null primary key, refid int unsigned not null," "foreign key t_bug51422fk (id) references t_bug51422_r (ukey)) ENGINE=InnoDB"); ok_stmt(hstmt, SQLForeignKeys(hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug51422", SQL_NTS)); ok_stmt(hstmt, SQLRowCount(hstmt, &rowsCount)); is_num(rowsCount, 0); ok_sql(hstmt, "drop table if exists t_bug51422"); ok_sql(hstmt, "drop table if exists t_bug51422_r"); return OK; } /* Bug #36441 - SQLPrimaryKeys returns mangled strings */ DECLARE_TEST(t_bug36441) { #define BUF_LEN 24 const SQLCHAR key_column_name[][14]= {"pk_for_table1", "c1_for_table1"}; SQLCHAR catalog[BUF_LEN], schema[BUF_LEN], table[BUF_LEN], column[BUF_LEN]; SQLLEN catalog_len, schema_len, table_len, column_len; SQLCHAR keyname[BUF_LEN]; SQLSMALLINT key_seq, i; SQLLEN keyname_len, key_seq_len, rowCount; ok_sql(hstmt, "drop table if exists t_bug36441_0123456789"); ok_sql(hstmt, "create table t_bug36441_0123456789(" "pk_for_table1 integer not null auto_increment," "c1_for_table1 varchar(128) not null unique," "c2_for_table1 binary(32) null," "unique_key int unsigned not null unique," "primary key(pk_for_table1, c1_for_table1))"); ok_stmt(hstmt, SQLPrimaryKeys(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, "t_bug36441_0123456789", SQL_NTS)); /* Test of SQLRowCount with SQLPrimaryKeys */ ok_stmt(hstmt, SQLRowCount(hstmt, &rowCount)); is_num(rowCount, 2); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_CHAR , catalog, sizeof(catalog), &catalog_len)); ok_stmt(hstmt, SQLBindCol(hstmt, 2, SQL_C_CHAR , schema , sizeof(schema) , &schema_len)); ok_stmt(hstmt, SQLBindCol(hstmt, 3, SQL_C_CHAR , table , sizeof(table) , &table_len)); ok_stmt(hstmt, SQLBindCol(hstmt, 4, SQL_C_CHAR , column , sizeof(column) , &column_len)); ok_stmt(hstmt, SQLBindCol(hstmt, 5, SQL_C_SHORT,&key_seq, sizeof(key_seq), &key_seq_len)); ok_stmt(hstmt, SQLBindCol(hstmt, 6, SQL_C_CHAR , keyname, sizeof(keyname), &keyname_len)); for(i= 0; i < 2; ++i) { ok_stmt(hstmt, SQLFetch(hstmt)); is_num(catalog_len, SQL_NULL_DATA); is_num(schema_len, SQL_NULL_DATA); is_str(table, "t_bug36441_0123456789", 3); is_str(column, key_column_name[i], 4); is_num(key_seq, i+1); is_str(keyname, "PRIMARY", 6); } expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_bug36441_0123456789"); return OK; #undef BUF_LEN } /* Bug #53235 - SQLColumns returns wrong transfer octet length */ DECLARE_TEST(t_bug53235) { int col_size, buf_len; ok_sql(hstmt, "drop table if exists t_bug53235"); ok_sql(hstmt, "create table t_bug53235 (x decimal(10,3))"); ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_bug53235", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLFetch(hstmt)); col_size= my_fetch_int(hstmt, 7); buf_len= my_fetch_int(hstmt, 8); is_num(col_size, 10); is_num(buf_len, 12); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop table if exists t_bug53235"); return OK; } /* Bug #50195 - SQLTablePrivileges requires select priveleges */ DECLARE_TEST(t_bug50195) { SQLHDBC hdbc1; SQLHSTMT hstmt1; const char expected_privs[][12]= {"ALTER", "CREATE", "CREATE VIEW", "DELETE", "DROP", "INDEX", "INSERT", "REFERENCES", "SHOW VIEW", "TRIGGER", "UPDATE"}; int i; SQLCHAR priv[12]; SQLLEN len; (void)SQLExecDirect(hstmt, (SQLCHAR *)"DROP USER bug50195@127.0.0.1", SQL_NTS); (void)SQLExecDirect(hstmt, (SQLCHAR *)"DROP USER bug50195@localhost", SQL_NTS); ok_sql(hstmt, "grant all on *.* to bug50195@127.0.0.1 IDENTIFIED BY 'a'"); ok_sql(hstmt, "grant all on *.* to bug50195@localhost IDENTIFIED BY 'a'"); ok_sql(hstmt, "revoke select on *.* from bug50195@127.0.0.1"); ok_sql(hstmt, "revoke select on *.* from bug50195@localhost"); /* revoking "global" select is enough, but revoking smth from mysql.tables_priv to have not empty result of SQLTablePrivileges */ ok_sql(hstmt, "grant all on mysql.tables_priv to bug50195@127.0.0.1"); ok_sql(hstmt, "grant all on mysql.tables_priv to bug50195@localhost"); ok_sql(hstmt, "revoke select on mysql.tables_priv from bug50195@127.0.0.1"); ok_sql(hstmt, "revoke select on mysql.tables_priv from bug50195@localhost"); ok_sql(hstmt, "FLUSH PRIVILEGES"); ok_env(henv, SQLAllocConnect(henv, &hdbc1)); ok_con(hdbc1, SQLConnect(hdbc1, mydsn, SQL_NTS, "bug50195", SQL_NTS, "a", SQL_NTS)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLTablePrivileges(hstmt1, "mysql", SQL_NTS, 0, 0, "tables_priv", SQL_NTS)); /* Testing SQLTablePrivileges a bit, as we don't have separate test of it */ for(i= 0; i < 11; ++i) { ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_stmt(hstmt1, SQLGetData(hstmt1, 6, SQL_C_CHAR, priv, sizeof(priv), &len)); is_str(priv, expected_privs[i], len); } expect_stmt(hstmt1, SQLFetch(hstmt1),100); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); ok_sql(hstmt, "DROP USER bug50195@127.0.0.1"); ok_sql(hstmt, "DROP USER bug50195@localhost"); return OK; } DECLARE_TEST(t_sqlprocedurecolumns) { SQLRETURN rc= 0; SQLCHAR szName[255]= {0}; typedef struct { char *c01_procedure_cat; char *c02_procedure_schem; char *c03_procedure_name; char *c04_column_name; short c05_column_type; short c06_data_type; char *c07_type_name; unsigned long c08_column_size; unsigned long c09_buffer_length; short c10_decimal_digits; short c11_num_prec_radix; short c12_nullable; char *c13_remarks; char *c14_column_def; short c15_sql_data_type; short c16_sql_datetime_sub; unsigned long c17_char_octet_length; int c18_ordinal_position; char *c19_is_nullable; }sqlproccol; int total_params= 0, iter= 0; sqlproccol data_to_check[] = { /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param1", SQL_PARAM_INPUT, SQL_TINYINT, "tinyint", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 1, 1, 0, 10, SQL_NULLABLE, "", 0, SQL_TINYINT, 0, 0, 1, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param2", SQL_PARAM_OUTPUT, SQL_SMALLINT, "smallint", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 5, 2, 0, 10, SQL_NULLABLE, "", 0, SQL_SMALLINT, 0, 0, 2, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param3", SQL_PARAM_INPUT, SQL_INTEGER, "mediumint", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 8, 3, 0, 10, SQL_NULLABLE, "", 0, SQL_INTEGER, 0, 0, 3, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param 4", SQL_PARAM_INPUT_OUTPUT, SQL_INTEGER, "int", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 10, 4, 0, 10, SQL_NULLABLE, "", 0, SQL_INTEGER, 0, 0, 4, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param5", SQL_PARAM_OUTPUT, SQL_BIGINT, "bigint", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 19, 20, 0, 10, SQL_NULLABLE, "", 0, SQL_BIGINT, 0, 0, 5, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param6", SQL_PARAM_INPUT, SQL_REAL, "float", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 7, 4, 0, 0, SQL_NULLABLE, "", 0, SQL_REAL, 0, 0, 6, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param7", SQL_PARAM_OUTPUT, SQL_DOUBLE, "double", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 15, 8, 0, 0, SQL_NULLABLE, "", 0, SQL_DOUBLE, 0, 0, 7, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param8", SQL_PARAM_INPUT, SQL_DECIMAL, "decimal", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 10, 11, 3, 10, SQL_NULLABLE, "", 0, SQL_DECIMAL, 0, 0, 8, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param9", SQL_PARAM_INPUT, SQL_CHAR, "char", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 32, 32, 0, 0, SQL_NULLABLE, "", 0, SQL_CHAR, 0, 32, 9, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param10", SQL_PARAM_OUTPUT, SQL_VARCHAR, "varchar", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 64, 64, 0, 0, SQL_NULLABLE, "", 0, SQL_VARCHAR, 0, 64, 10, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test1", "re_param11", SQL_PARAM_INPUT, SQL_LONGVARBINARY, "long varbinary", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 16777215, 16777215, 0, 0, SQL_NULLABLE, "", 0, SQL_LONGVARBINARY, 0, 16777215, 12, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramA", SQL_PARAM_INPUT, SQL_LONGVARBINARY, "blob", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 65535, 65535, 0, 0, SQL_NULLABLE, "", 0, SQL_LONGVARBINARY, 0, 65535, 1, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramB", SQL_PARAM_INPUT, SQL_LONGVARBINARY, "longblob", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 4294967295L, 2147483647L, 0, 0, SQL_NULLABLE, "", 0, SQL_LONGVARBINARY, 0, 2147483647L, 2, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramC", SQL_PARAM_INPUT_OUTPUT, SQL_LONGVARBINARY, "tinyblob", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 255, 255, 0, 0, SQL_NULLABLE, "", 0, SQL_LONGVARBINARY, 0, 255, 3, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramD", SQL_PARAM_INPUT, SQL_LONGVARBINARY, "mediumblob", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 16777215, 16777215, 0, 0, SQL_NULLABLE, "", 0, SQL_LONGVARBINARY, 0, 16777215, 4, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramE", SQL_PARAM_INPUT, SQL_VARBINARY, "varbinary", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 128, 128, 0, 0, SQL_NULLABLE, "", 0, SQL_VARBINARY, 0, 128, 5, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramF", SQL_PARAM_OUTPUT, SQL_BINARY, "binary", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 1, 1, 0, 0, SQL_NULLABLE, "", 0, SQL_BINARY, 0, 1, 6, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramG", SQL_PARAM_INPUT, SQL_BINARY, "binary", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 8, 8, 0, 0, SQL_NULLABLE, "", 0, SQL_BINARY, 0, 8, 7, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_param H", SQL_PARAM_INPUT, SQL_LONGVARCHAR, "long varchar", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 16777215, 16777215, 0, 0, SQL_NULLABLE, "", 0, SQL_LONGVARCHAR, 0, 16777215, 8, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramI", SQL_PARAM_INPUT, SQL_LONGVARCHAR, "text", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 65535, 65535, 0, 0, SQL_NULLABLE, "", 0, SQL_LONGVARCHAR, 0, 65535, 9, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramJ", SQL_PARAM_INPUT, SQL_LONGVARCHAR, "mediumtext", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 16777215, 16777215, 0, 0, SQL_NULLABLE, "", 0, SQL_LONGVARCHAR, 0, 16777215, 10, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramK", SQL_PARAM_INPUT_OUTPUT, SQL_LONGVARCHAR, "longtext", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 4294967295L, 2147483647L, 0, 0, SQL_NULLABLE, "", 0, SQL_LONGVARCHAR, 0, 2147483647L, 11, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramL", SQL_PARAM_INPUT, SQL_LONGVARCHAR, "tinytext", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 255, 255, 0, 0, SQL_NULLABLE, "", 0, SQL_LONGVARCHAR, 0, 255, 12, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test2", "re_paramM", SQL_PARAM_INPUT, SQL_NUMERIC, "numeric", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 8, 10, 2, 10, SQL_NULLABLE, "", 0, SQL_NUMERIC, 0, 0, 13, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test3", "re_param_00", SQL_PARAM_INPUT, SQL_TYPE_TIMESTAMP, "datetime", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 19, 16, 0, 10, SQL_NULLABLE, "", 0, SQL_DATETIME, SQL_TYPE_TIMESTAMP, 0, 1, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test3", "re_param_01", SQL_PARAM_OUTPUT, SQL_TYPE_DATE, "date", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 10, 6, 0, 0, SQL_NULLABLE, "", 0, SQL_DATETIME, SQL_TYPE_DATE, 0, 2, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test3", "re_param_02", SQL_PARAM_OUTPUT, SQL_TYPE_TIME, "time", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 8, 6, 0, 0, SQL_NULLABLE, "", 0, SQL_DATETIME, SQL_TYPE_TIME, 0, 3, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test3", "re_param_03", SQL_PARAM_INPUT_OUTPUT, SQL_TYPE_TIMESTAMP, "timestamp", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 19, 16, 0, 0, SQL_NULLABLE, "", 0, SQL_DATETIME, SQL_TYPE_TIMESTAMP, 0, 4, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test3", "re_param_04", SQL_PARAM_INPUT, SQL_SMALLINT, "year", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 4, 1, 0, 10, SQL_NULLABLE, "", 0, SQL_SMALLINT, 0, 0, 5, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test4_func", "RETURN_VALUE", SQL_RETURN_VALUE, SQL_VARCHAR, "varchar", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 32, 32, 0, 0, SQL_NULLABLE, "", 0, SQL_VARCHAR, 0, 32, 0, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test4_func", "re_paramF", SQL_PARAM_INPUT, SQL_INTEGER, "int", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 10, 4, 0, 10, SQL_NULLABLE, "", 0, SQL_INTEGER, 0, 0, 1, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test4_func_noparam", "RETURN_VALUE", SQL_RETURN_VALUE, SQL_VARCHAR, "varchar", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 32, 32, 0, 0, SQL_NULLABLE, "", 0, SQL_VARCHAR, 0, 32, 0, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test5", "re_param_set_01", SQL_PARAM_INPUT, SQL_CHAR, "char", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 14, 14, 0, 0, SQL_NULLABLE, "", 0, SQL_CHAR, 0, 14, 1, "YES"}, /*cat schem proc_name col_name col_type data_type type_name */ {"test", 0, "procedure_columns_test5", "re_param_enum_02", SQL_PARAM_OUTPUT, SQL_CHAR, "char", /*size buf_len dec radix nullable rem def sql_data_type sub octet pos nullable*/ 7, 7, 0, 0, SQL_NULLABLE, "", 0, SQL_CHAR, 0, 7, 2, "YES"}, }; ok_sql(hstmt, "drop procedure if exists procedure_columns_test1"); ok_sql(hstmt, "drop procedure if exists procedure_columns_test2"); ok_sql(hstmt, "drop procedure if exists procedure_columns_test2_noparam"); ok_sql(hstmt, "drop procedure if exists procedure_columns_test3"); ok_sql(hstmt, "drop function if exists procedure_columns_test4_func"); ok_sql(hstmt, "drop function if exists procedure_columns_test4_func_noparam"); ok_sql(hstmt, "drop procedure if exists procedure_columns_test5"); ok_sql(hstmt, "create procedure procedure_columns_test1(IN re_param1 TINYINT, OUT re_param2 SMALLINT," \ "re_param3 MEDIUMINT, INOUT `re_param 4` INT UNSIGNED, OUT re_param5 BIGINT, re_param6 FLOAT(4,2)," \ "OUT re_param7 DOUBLE(5, 3), IN re_param8 DECIMAL(10,3) unSIGned, re_param9 CHAR(32)," \ "Out re_param10 VARCHAR(64) charset utf8, ignore_param INT, re_param11 long VARBINARY)" \ "begin end;" ); ok_sql(hstmt, "create procedure procedure_columns_test2(IN re_paramA bloB," \ "IN re_paramB LONGBLOB, inout re_paramC TinyBlob, re_paramD mediumblob, IN re_paramE varbinary(128)," \ "OUT re_paramF binary, re_paramG binary(8), `re_param H` LONG VARCHAR, IN re_paramI TEXT," \ "re_paramJ mediumtext, INOUT re_paramK longtext, re_paramL tinytext, re_paramM numeric(8,2))" \ "begin end;" ); ok_sql(hstmt, "create procedure procedure_columns_test2_noparam()"\ "begin end;" ); ok_sql(hstmt, "create procedure procedure_columns_test3(IN re_param_00 datetime,"\ "OUT re_param_01 date, OUT re_param_02 time, INOUT re_param_03 timestamp,"\ "re_param_04 year)" \ "begin end;" ); ok_sql(hstmt, "create function procedure_columns_test4_func(re_paramF int) returns varchar(32) deterministic "\ "begin return CONCAT('abc', paramF); end;" ); ok_sql(hstmt, "create function procedure_columns_test4_func_noparam() returns varchar(32) deterministic "\ "begin return 'abc'; end;" ); ok_sql(hstmt, "create procedure procedure_columns_test5(IN re_param_set_01 SET('', \"one\", 'two', 'three'),"\ "OUT re_param_enum_02 ENUM('', \"one\", 'tw)o', 'three m'))" \ "begin end;" ); ok_stmt(hstmt, SQLProcedureColumns(hstmt, NULL, 0, NULL, 0, "procedure_columns_test%", SQL_NTS, "re_%", SQL_NTS)); while(SQLFetch(hstmt) == SQL_SUCCESS) { SQLCHAR buff[255] = {0}, *param_cat, *param_name; SQLUINTEGER col_size, buf_len, octet_len; param_cat= (SQLCHAR*)my_fetch_str(hstmt, buff, 1); is_str(param_cat, data_to_check[iter].c01_procedure_cat, strlen(data_to_check[iter].c01_procedure_cat) + 1); is_str(my_fetch_str(hstmt, buff, 3), data_to_check[iter].c03_procedure_name, strlen(data_to_check[iter].c03_procedure_name) + 1); param_name= (SQLCHAR*)my_fetch_str(hstmt, buff, 4); is_str(param_name, data_to_check[iter].c04_column_name, strlen(data_to_check[iter].c04_column_name) + 1); is_num(my_fetch_int(hstmt, 5), data_to_check[iter].c05_column_type); is_num(my_fetch_int(hstmt, 6), data_to_check[iter].c06_data_type); is_str(my_fetch_str(hstmt, buff, 7), data_to_check[iter].c07_type_name, strlen(data_to_check[iter].c07_type_name) + 1); col_size= my_fetch_uint(hstmt, 8); is_num(col_size, data_to_check[iter].c08_column_size); buf_len= my_fetch_uint(hstmt, 9); is_num(buf_len, data_to_check[iter].c09_buffer_length); is_num(my_fetch_int(hstmt, 10), data_to_check[iter].c10_decimal_digits); is_num(my_fetch_int(hstmt, 11), data_to_check[iter].c11_num_prec_radix); is_num(my_fetch_int(hstmt, 15), data_to_check[iter].c15_sql_data_type); is_num(my_fetch_int(hstmt, 16), data_to_check[iter].c16_sql_datetime_sub); octet_len= my_fetch_uint(hstmt, 17); is_num(octet_len, data_to_check[iter].c17_char_octet_length); is_num(my_fetch_int(hstmt, 18), data_to_check[iter].c18_ordinal_position); is_str(my_fetch_str(hstmt, buff, 19), data_to_check[iter].c19_is_nullable, strlen(data_to_check[iter].c19_is_nullable + 1)); ++iter; } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop procedure if exists procedure_columns_test1"); ok_sql(hstmt, "drop procedure if exists procedure_columns_test2"); ok_sql(hstmt, "drop procedure if exists procedure_columns_test2_noparam"); ok_sql(hstmt, "drop procedure if exists procedure_columns_test3"); ok_sql(hstmt, "drop function if exists procedure_columns_test4_func"); ok_sql(hstmt, "drop function if exists procedure_columns_test4_func_noparam"); ok_sql(hstmt, "drop procedure if exists procedure_columns_test5"); return OK; } DECLARE_TEST(t_bug57182) { SQLLEN nRowCount; SQLCHAR buff[24]; ok_sql(hstmt, "drop procedure if exists bug57182"); ok_sql(hstmt, "CREATE DEFINER=`adb`@`%` PROCEDURE `bug57182`(in id int, in name varchar(20)) " "BEGIN" " insert into simp values (id, name);" "END"); ok_stmt(hstmt, SQLProcedureColumns(hstmt, "test", SQL_NTS, NULL, 0, "bug57182", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLRowCount(hstmt, &nRowCount)); is_num(2, nRowCount) ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "bug57182", 9); is_str(my_fetch_str(hstmt, buff, 4), "id", 3); is_str(my_fetch_str(hstmt, buff, 7), "int", 4); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "bug57182", 9); is_str(my_fetch_str(hstmt, buff, 4), "name", 5); is_str(my_fetch_str(hstmt, buff, 7), "varchar", 8); is_num(my_fetch_int(hstmt, 8), 20); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* Almost the same thing but with column specified */ ok_stmt(hstmt, SQLProcedureColumns(hstmt, "test", SQL_NTS, NULL, 0, "bug57182", SQL_NTS, "id", SQL_NTS)); ok_stmt(hstmt, SQLRowCount(hstmt, &nRowCount)); is_num(1, nRowCount) ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 3), "bug57182", 9); is_str(my_fetch_str(hstmt, buff, 4), "id", 3); is_str(my_fetch_str(hstmt, buff, 7), "int", 4); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* And testing impossible column condition - expecting to get no rows */ ok_stmt(hstmt, SQLProcedureColumns(hstmt, "test", SQL_NTS, NULL, 0, "bug57182", SQL_NTS, "non_existing_column%", SQL_NTS)); ok_stmt(hstmt, SQLRowCount(hstmt, &nRowCount)); is_num(0, nRowCount); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "drop procedure if exists bug57182"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); return OK; } /* SQLRowCount() doesn't work with SQLTables and other functions Testing of that with SQLTables, SQLColumn is incorporated in other testcases */ DECLARE_TEST(t_bug55870) { SQLLEN rowCount; SQLCHAR noI_SconnStr[256], query[256]; HDBC hdbc1; HSTMT hstmt1; ok_sql(hstmt, "drop table if exists bug55870r"); ok_sql(hstmt, "drop table if exists bug55870_2"); ok_sql(hstmt, "drop table if exists bug55870"); ok_sql(hstmt, "create table bug55870(a int not null primary key, " "b varchar(20) not null, c varchar(100) not null, INDEX(b)) ENGINE=InnoDB"); /* There should be no problems with I_S version of SQLTablePrivileges. Thus need connection not using I_S. SQlStatistics doesn't have I_S version, but it ma change at certain point. Thus let's test it on NO_I_S connection too */ ok_env(henv, SQLAllocConnect(henv, &hdbc1)); sprintf((char *)noI_SconnStr, "DSN=%s;UID=%s;PWD=%s; NO_I_S=1", mydsn, myuid, mypwd); if (mysock != NULL) { strcat((char *)noI_SconnStr, ";SOCKET="); strcat((char *)noI_SconnStr, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)noI_SconnStr, pbuff); } sprintf(query, "grant Insert, Select on bug55870 to %s", myuid); ok_stmt(hstmt, SQLExecDirect(hstmt, query, SQL_NTS)); sprintf(query, "grant Insert (c), Select (c), Update (c) on bug55870 to %s", myuid); ok_stmt(hstmt, SQLExecDirect(hstmt, query, SQL_NTS)); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, noI_SconnStr, sizeof(noI_SconnStr), NULL, 0, NULL, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLStatistics(hstmt1, NULL, 0, NULL, 0, "bug55870", SQL_NTS, SQL_INDEX_UNIQUE, SQL_QUICK)); ok_stmt(hstmt1, SQLRowCount(hstmt1, &rowCount)); is_num(rowCount, 1); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLTablePrivileges(hstmt1, "test", SQL_NTS, 0, 0, "bug55870", SQL_NTS)); ok_stmt(hstmt1, SQLRowCount(hstmt1, &rowCount)); is_num(rowCount, my_print_non_format_result(hstmt1)); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_stmt(hstmt1, SQLColumnPrivileges(hstmt1, "test", SQL_NTS, 0, 0, "bug55870", SQL_NTS, "c", SQL_NTS)); ok_stmt(hstmt1, SQLRowCount(hstmt1, &rowCount)); is_num(rowCount, my_print_non_format_result(hstmt1)); ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_sql(hstmt, "create table bug55870_2 (id int not null primary key, value " "varchar(255) not null) ENGINE=InnoDB"); ok_sql(hstmt, "create table bug55870r (id int unsigned not null primary key," "refid int not null, refid2 int not null," "somevalue varchar(20) not null, foreign key b55870fk1 (refid) " "references bug55870 (a), foreign key b55870fk2 (refid2) " "references bug55870_2 (id)) ENGINE=InnoDB"); /* actually... looks like no-i_s version of SQLForeignKeys is broken on latest server versions. comment in "show table status..." contains nothing */ ok_stmt(hstmt1, SQLForeignKeys(hstmt1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, (SQLCHAR *)"bug55870r", SQL_NTS)); ok_stmt(hstmt1, SQLRowCount(hstmt1, &rowCount)); is_num(rowCount, my_print_non_format_result(hstmt1)); /** surprise-surprise - just removing table is not enough to remove related records from tables_priv and columns_priv */ sprintf(query, "revoke select,insert on bug55870 from %s", myuid); ok_stmt(hstmt, SQLExecDirect(hstmt, query, SQL_NTS)); sprintf(query, "revoke select (c),insert (c),update (c) on bug55870 from %s", myuid); ok_stmt(hstmt, SQLExecDirect(hstmt, query, SQL_NTS)); /* ok_sql(hstmt, "drop table if exists bug55870r"); ok_sql(hstmt, "drop table if exists bug55870_2"); ok_sql(hstmt, "drop table if exists bug55870");*/ ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeHandle(SQL_HANDLE_DBC, hdbc1)); return OK; } /** Bug#31067 test. Testing only part of the patch, as the report itself is about Access and can't be tested automatically. The test checks if SQLColumns returns correct default value if table's catalog specified. */ DECLARE_TEST(t_bug31067) { SQLCHAR buff[512]; ok_sql(hstmt, "DROP DATABASE IF EXISTS bug31067"); ok_sql(hstmt, "CREATE DATABASE bug31067"); ok_sql(hstmt, "CREATE TABLE bug31067.a (a varchar(10) not null default 'bug31067')"); /* Get the info from just one table. */ ok_stmt(hstmt, SQLColumns(hstmt, (SQLCHAR *)"bug31067", SQL_NTS, NULL, SQL_NTS, (SQLCHAR *)"a", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 13), "'bug31067'", 11); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP DATABASE bug31067"); return OK; } /* Some catalog functions can return only one row of result if previous prepare statement pre-execution has failed */ DECLARE_TEST(bug12824839) { SQLLEN row_count; SQLSMALLINT col_count; ok_sql(hstmt, "DROP TABLE IF EXISTS b12824839"); ok_sql(hstmt, "DROP TABLE IF EXISTS b12824839a"); ok_sql(hstmt, "CREATE TABLE b12824839 " "(id int primary key, vc_col varchar(32), int_col int)"); ok_sql(hstmt, "CREATE TABLE b12824839a " "(id int, vc_col varchar(32) UNIQUE, int_col int," "primary key(id,int_col))"); ok_stmt(hstmt, SQLPrepare(hstmt, "SELECT * from any_non_existing_table", SQL_NTS)); /* this will fail */ expect_stmt(hstmt, SQLNumResultCols(hstmt, &col_count), SQL_ERROR); ok_stmt(hstmt, SQLColumns(hstmt, "test", SQL_NTS, NULL, 0, "b12824839", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLRowCount(hstmt, &row_count)); is_num(3, row_count); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLPrimaryKeys(hstmt, NULL, SQL_NTS, NULL, SQL_NTS, "b12824839a", SQL_NTS)); ok_stmt(hstmt, SQLRowCount(hstmt, &row_count)); is_num(2, row_count); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); /* SQLColumns was not completely fixed */ ok_stmt(hstmt, SQLColumns(hstmt, "test", SQL_NTS, NULL, 0, NULL, SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLRowCount(hstmt, &row_count)); /* There should be records at least for those 2 tables we've created */ is(row_count >= 6); return OK; } /* If no default database is selected for the connection, call of SQLColumns causes error "Unknown database 'null'" */ DECLARE_TEST(sqlcolumns_nodbselected) { SQLHDBC hdbc1; SQLHSTMT hstmt1; SQLCHAR conn_in[256]; /* Just to make sure we have at least one table in our test db */ ok_sql(hstmt, "DROP TABLE IF EXISTS sqlcolumns_nodbselected"); ok_sql(hstmt, "CREATE TABLE sqlcolumns_nodbselected (id int)"); ok_env(henv, SQLAllocConnect(henv, &hdbc1)); /* Connecting not specifying default db */ sprintf((char *)conn_in, "DRIVER=%s;SERVER=%s;UID=%s;PWD=%s", mydriver, myserver, myuid, mypwd); if (mysock != NULL) { strcat((char *)conn_in, ";SOCKET="); strcat((char *)conn_in, (char *)mysock); } if (myport) { char pbuff[20]; sprintf(pbuff, ";PORT=%d", myport); strcat((char *)conn_in, pbuff); } ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn_in, sizeof(conn_in), NULL, 0, NULL, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_con(hdbc1, SQLGetInfo(hdbc1, SQL_DATABASE_NAME, (SQLPOINTER) conn_in, sizeof(conn_in), NULL)); is_str("null", conn_in, 5); ok_stmt(hstmt1, SQLColumns(hstmt1, mydb, SQL_NTS, NULL, 0, NULL, 0, NULL, 0)); ok_con(hdbc1, SQLFreeStmt(hstmt1, SQL_CLOSE)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); ok_sql(hstmt, "DROP TABLE IF EXISTS sqlcolumns_nodbselected"); return OK; } BEGIN_TESTS ADD_TEST(my_columns_null) ADD_TEST(my_drop_table) ADD_TEST(my_table_dbs) ADD_TEST(my_colpriv) ADD_TEST(t_sqlprocedures) ADD_TEST(t_catalog) ADD_TEST(tmysql_specialcols) ADD_TEST(t_columns) ADD_TEST(t_tables_bug) ADD_TEST(t_current_catalog) ADD_TEST(tmysql_showkeys) ADD_TEST(t_sqltables) ADD_TEST(my_information_schema) ADD_TEST(t_bug4518) ADD_TEST(empty_set) ADD_TEST(t_bug23031) ADD_TEST(bug15713) ADD_TEST(t_bug28316) ADD_TEST(bug8860) ADD_TEST(t_bug26934) ADD_TEST(t_bug29888) ADD_TEST(t_bug14407) ADD_TEST(t_bug19923) ADD_TEST(t_bug32864) ADD_TEST(t_bug32989) ADD_TEST(t_bug33298) ADD_TEST(t_bug12805) ADD_TEST(t_bug30770) ADD_TEST(t_bug36275) ADD_TEST(t_bug39957) ADD_TEST(t_bug37621) ADD_TEST(t_bug34272) ADD_TEST(t_bug49660) ADD_TEST(t_bug51422) ADD_TEST(t_bug36441) ADD_TEST(t_bug53235) ADD_TEST(t_bug50195) ADD_TEST(t_sqlprocedurecolumns) ADD_TEST(t_bug57182) ADD_TEST(t_bug55870) ADD_TEST(t_bug31067) ADD_TEST(bug12824839) ADD_TEST(sqlcolumns_nodbselected) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/test/my_types.c100644 15766 12 110274 11707541005 20227 0ustar00cteamstaff/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "odbctap.h" DECLARE_TEST(t_longlong1) { #if SQLBIGINT_MADE_PORTABLE || defined(_WIN32) SQLBIGINT session_id, ctn; ok_sql(hstmt, "DROP TABLE IF EXISTS t_longlong"); ok_sql(hstmt, "CREATE TABLE t_longlong (a BIGINT, b BIGINT)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_ROWVER, 0)); ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CURSOR_STATIC, 0)); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"INSERT INTO t_longlong VALUES (?,?)", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, 20, 0, &session_id, 20, NULL)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, 20, 0, &ctn, 20, NULL)); for (session_id= 50, ctn= 0; session_id < 100; session_id++) { ctn+= session_id; ok_stmt(hstmt, SQLExecute(hstmt)); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_longlong"); my_assert(50 == myresult(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_longlong"); #endif /* SQLBIGINT_MADE_PORTABLE || defined(_WIN32) */ return OK; } DECLARE_TEST(t_decimal) { SQLCHAR str[20],s_data[]="189.4567"; SQLDOUBLE d_data=189.4567; SQLINTEGER i_data=189, l_data=-23; SQLRETURN rc; ok_sql(hstmt, "DROP TABLE IF EXISTS t_decimal"); rc = tmysql_exec(hstmt,"create table t_decimal(d1 decimal(10,6))"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLPrepare(hstmt, (SQLCHAR *)"insert into t_decimal values (?),(?),(?),(?)",SQL_NTS); mystmt(hstmt,rc); rc = SQLBindParameter( hstmt, 1, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DECIMAL, 10, 4, &d_data, 0, NULL ); mystmt(hstmt,rc); rc = SQLBindParameter( hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_DECIMAL, 10, 4, &i_data, 0, NULL ); mystmt(hstmt,rc); rc = SQLBindParameter( hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_DECIMAL, 10, 4, &s_data, 9, NULL ); mystmt(hstmt,rc); rc = SQLBindParameter( hstmt, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_DECIMAL, 10, 4, &l_data, 0, NULL ); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_RESET_PARAMS); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); ok_sql(hstmt, "select d1 from t_decimal"); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_CHAR,&str,19,NULL); mystmt(hstmt,rc); fprintf(stdout,"decimal(SQL_C_DOUBLE) : %s\n",str); is_str(str, "189.456700", 11); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_CHAR,&str,19,NULL); mystmt(hstmt,rc); fprintf(stdout,"decimal(SQL_C_INTEGER): %s\n",str); is_str(str,"189.000000",11); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_CHAR,&str,19,NULL); mystmt(hstmt,rc); fprintf(stdout,"decimal(SQL_C_CHAR) : %s\n",str); is_str(str,"189.456700",11); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_CHAR,&str,19,NULL); mystmt(hstmt,rc); fprintf(stdout,"decimal(SQL_C_LONG) : %s\n",str); is_str(str, "-23.000000", 11); rc = SQLFetch(hstmt); my_assert(rc == SQL_NO_DATA_FOUND); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_decimal"); return OK; } DECLARE_TEST(t_bigint) { #if SQLBIGINT_MADE_PORTABLE || defined(_WIN32) SQLRETURN rc; SQLLEN nlen = 4; union { /* An union to get 4 byte alignment */ SQLCHAR buf[20]; SQLINTEGER dummy; } id = {"99998888"}; /* Just to get a binary pattern for some 64 bit big int */ tmysql_exec(hstmt,"drop table t_bigint"); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"create table t_bigint(id int(20) NOT NULL auto_increment,name varchar(20), primary key(id))"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); /* TIMESTAMP TO DATE, TIME and TS CONVERSION */ rc = tmysql_prepare(hstmt,"insert into t_bigint values(?,'venuxyz')"); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_LONG, SQL_BIGINT,0,0,&id.buf,sizeof(id.buf),&nlen); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_RESET_PARAMS); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_bigint values(10,'mysql1')"); mystmt(hstmt,rc); rc = tmysql_exec(hstmt,"insert into t_bigint values(20,'mysql2')"); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLSpecialColumns(hstmt,SQL_ROWVER,NULL,SQL_NTS,NULL,SQL_NTS, "t_bigint",SQL_NTS,SQL_SCOPE_TRANSACTION,SQL_NULLABLE); mycon(hdbc,rc); my_assert( 0 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLColumns(hstmt,NULL,SQL_NTS,NULL,SQL_NTS,"t_bigint",SQL_NTS,NULL,SQL_NTS); mycon(hdbc,rc); my_assert( 2 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLStatistics(hstmt,NULL,SQL_NTS,NULL,SQL_NTS,"t_bigint",SQL_NTS,SQL_INDEX_ALL,SQL_QUICK); mycon(hdbc,rc); my_assert( 1 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); #if CATALOG_FUNCTIONS_FIXED rc = SQLGetTypeInfo(hstmt,SQL_BIGINT); mycon(hdbc,rc); my_assert( 4 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLGetTypeInfo(hstmt,SQL_BIGINT); mycon(hdbc,rc); my_assert( 4 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); #endif rc = tmysql_exec(hstmt,"select * from t_bigint"); mystmt(hstmt,rc); rc = SQLFetch(hstmt); mystmt(hstmt,rc); rc = SQLGetData(hstmt,1,SQL_C_DEFAULT,&id.buf,sizeof(id.buf),&nlen); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); #endif /* SQLBIGINT_MADE_PORTABLE || defined(_WIN32) */ return OK; } DECLARE_TEST(t_enumset) { SQLRETURN rc; SQLCHAR szEnum[40]="MYSQL_E1"; SQLCHAR szSet[40]="THREE,ONE,TWO"; ok_sql(hstmt, "DROP TABLE IF EXISTS t_enumset"); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"create table t_enumset(col1 enum('MYSQL_E1','MYSQL_E2'),col2 set('ONE','TWO','THREE'))"); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "insert into t_enumset values('MYSQL_E2','TWO,THREE')"); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "insert into t_enumset values(?,?)", SQL_NTS)); rc = SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,0,0,&szEnum,sizeof(szEnum),NULL); mystmt(hstmt,rc); rc = SQLBindParameter(hstmt,2,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,0,0,&szSet,sizeof(szSet),NULL); mystmt(hstmt,rc); rc = SQLExecute(hstmt); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_RESET_PARAMS); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); rc = SQLTransact(NULL,hdbc,SQL_COMMIT); mycon(hdbc,rc); rc = tmysql_exec(hstmt,"select * from t_enumset"); mystmt(hstmt,rc); my_assert( 2 == myresult(hstmt)); rc = SQLFreeStmt(hstmt,SQL_UNBIND); mystmt(hstmt,rc); rc = SQLFreeStmt(hstmt,SQL_CLOSE); mystmt(hstmt,rc); ok_sql(hstmt, "DROP TABLE IF EXISTS t_enumset"); return OK; } /** Bug #16917: MyODBC doesn't return ASCII 0 characters for TEXT columns */ DECLARE_TEST(t_bug16917) { SQLCHAR buff[255]; SQLLEN len; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug16917"); ok_sql(hstmt, "CREATE TABLE t_bug16917 (a TEXT)"); ok_sql(hstmt, "INSERT INTO t_bug16917 VALUES ('a\\0b')"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT a FROM t_bug16917"); ok_stmt(hstmt, SQLFetch(hstmt)); /* This SQLSetPos() causes the field lengths to get lost. */ ok_stmt(hstmt, SQLSetPos(hstmt, 1, SQL_POSITION, SQL_LOCK_NO_CHANGE)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, buff, 0, &len)); is_num(len, 3); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, buff, sizeof(buff), &len)); is_num(buff[0], 'a'); is_num(buff[1], 0); is_num(buff[2], 'b'); is_num(len, 3); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug16917"); return OK; } /** Bug #16235: ODBC driver doesn't format parameters correctly */ DECLARE_TEST(t_bug16235) { SQLCHAR varchar[]= "a'b", text[]= "c'd", buff[10]; SQLLEN varchar_len= SQL_NTS, text_len= SQL_NTS; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug16235"); ok_sql(hstmt, "CREATE TABLE t_bug16235 (a NVARCHAR(20), b TEXT)"); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "INSERT INTO t_bug16235 VALUES (?,?)", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_WVARCHAR, 0, 0, varchar, sizeof(varchar), &varchar_len)); ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_WLONGVARCHAR, 0, 0, text, sizeof(text), &text_len)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_UNBIND)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_bug16235"); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 1), "a'b", 3); is_str(my_fetch_str(hstmt, buff, 2), "c'd", 3); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug16235"); return OK; } /** Bug #27862: Function return incorrect SQL_COLUMN_SIZE */ DECLARE_TEST(t_bug27862_1) { SQLLEN len; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug27862"); ok_sql(hstmt, "CREATE TABLE t_bug27862 (a VARCHAR(2), b VARCHAR(2)) charset latin1"); ok_sql(hstmt, "INSERT INTO t_bug27862 VALUES ('a','b')"); ok_sql(hstmt, "SELECT CONCAT(a,b) FROM t_bug27862"); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &len)); is_num(len, 4); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_LENGTH, NULL, 0, NULL, &len)); is_num(len, 4); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &len)); /* Octet length shoul *not* include terminanting null character according to ODBC specs */ is_num(len, 4); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug27862"); return OK; } /** Because integers are given the charset 63 (binary) when they are used as strings, functions like CONCAT() return a binary string. This is a server bug that we do not try to work around. */ DECLARE_TEST(t_bug27862_2) { SQLLEN len; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug27862"); ok_sql(hstmt, "CREATE TABLE t_bug27862 (c DATE, d INT)"); ok_sql(hstmt, "INSERT INTO t_bug27862 VALUES ('2007-01-13',5)"); ok_sql(hstmt, "SELECT CONCAT_WS(' - ', DATE_FORMAT(c, '%b-%d-%y'), d) " "FROM t_bug27862"); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &len)); is_num(len, 104); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_LENGTH, NULL, 0, NULL, &len)); is_num(len, 26); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &len)); is_num(len, 27); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug27862"); return OK; } /** SQL_DESC_FIXED_PREC_SCALE was wrong for new DECIMAL types. */ DECLARE_TEST(decimal_scale) { SQLLEN fixed= SQL_FALSE; SQLLEN prec, scale; ok_sql(hstmt, "DROP TABLE IF EXISTS t_decscale"); ok_sql(hstmt, "CREATE TABLE t_decscale (a DECIMAL(5,3))"); ok_sql(hstmt, "SELECT * FROM t_decscale"); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_FIXED_PREC_SCALE, NULL, 0, NULL, &fixed)); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_PRECISION, NULL, 0, NULL, &prec)); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_SCALE, NULL, 0, NULL, &scale)); is_num(fixed, SQL_FALSE); is_num(prec, 5); is_num(scale, 3); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_decscale"); return OK; } /** Wrong value returned for SQL_DESC_LITERAL_SUFFIX for binary field. */ DECLARE_TEST(binary_suffix) { SQLCHAR suffix[10]; SQLSMALLINT len; ok_sql(hstmt, "DROP TABLE IF EXISTS t_binarysuffix"); ok_sql(hstmt, "CREATE TABLE t_binarysuffix (a BINARY(10))"); ok_sql(hstmt, "SELECT * FROM t_binarysuffix"); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_LITERAL_SUFFIX, suffix, 10, &len, NULL)); is_num(len, 0); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_binarysuffix"); return OK; } /** Wrong value returned for SQL_DESC_SCALE for float and double. */ DECLARE_TEST(float_scale) { SQLLEN scale; ok_sql(hstmt, "DROP TABLE IF EXISTS t_floatscale"); ok_sql(hstmt, "CREATE TABLE t_floatscale(a FLOAT, b DOUBLE, c DECIMAL(3,2))"); ok_sql(hstmt, "SELECT * FROM t_floatscale"); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_SCALE, NULL, 0, NULL, &scale)); is_num(scale, 0); ok_stmt(hstmt, SQLColAttribute(hstmt, 2, SQL_DESC_SCALE, NULL, 0, NULL, &scale)); is_num(scale, 0); ok_stmt(hstmt, SQLColAttribute(hstmt, 3, SQL_DESC_SCALE, NULL, 0, NULL, &scale)); is_num(scale, 2); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_floatscale"); return OK; } /** Test the BIT type, which has different behavior for BIT(1) and BIT(n > 1). */ DECLARE_TEST(bit) { SQLCHAR col[10]; SQLLEN type, len; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bit"); ok_sql(hstmt, "CREATE TABLE t_bit (a BIT(1), b BIT(17))"); ok_stmt(hstmt, SQLColumns(hstmt, NULL, 0, NULL, 0, (SQLCHAR *)"t_bit", SQL_NTS, NULL, 0)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 4), "a", 1); is_num(my_fetch_int(hstmt, 5), SQL_BIT); /* DATA_TYPE */ is_num(my_fetch_int(hstmt, 7), 1); /* COLUMN_SIZE */ is_num(my_fetch_int(hstmt, 8), 1); /* BUFFER_LENGTH */ ok_stmt(hstmt, SQLGetData(hstmt, 16, SQL_C_LONG, &type, 0, &len)); is_num(len, SQL_NULL_DATA); /* CHAR_OCTET_LENGTH */ ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, col, 4), "b", 1); is_num(my_fetch_int(hstmt, 5), SQL_BINARY); /* DATA_TYPE */ is_num(my_fetch_int(hstmt, 7), 3); /* COLUMN_SIZE */ is_num(my_fetch_int(hstmt, 8), 3); /* BUFFER_LENGTH */ is_num(my_fetch_int(hstmt, 16), 3); /* CHAR_OCTET_LENGTH */ expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT * FROM t_bit"); ok_stmt(hstmt, SQLColAttribute(hstmt, 1, SQL_DESC_TYPE, NULL, 0, NULL, &type)); is_num(type, SQL_BIT); ok_stmt(hstmt, SQLColAttribute(hstmt, 2, SQL_DESC_TYPE, NULL, 0, NULL, &type)); is_num(type, SQL_BINARY); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bit"); return OK; } /** Bug #32171: ODBC Driver incorrectly parses large Unsigned Integers */ DECLARE_TEST(t_bug32171) { SQLUINTEGER in= 4255080020UL, out; SQLCHAR buff[128]; ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug32171"); ok_sql(hstmt, "CREATE TABLE t_bug32171 (a INT UNSIGNED)"); sprintf((char *)buff, "INSERT INTO t_bug32171 VALUES ('%u')", in); ok_stmt(hstmt, SQLExecDirect(hstmt, buff, SQL_NTS)); ok_sql(hstmt, "SELECT * FROM t_bug32171"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, SQL_C_ULONG, &out, 0, NULL)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(out, in); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug32171"); return OK; } /** Test passing an SQL_C_CHAR to a SQL_WCHAR field. */ DECLARE_TEST(sqlwchar) { /* Note: this is an SQLCHAR, so it is 'ANSI' data. */ SQLCHAR data[]= "S\xe3o Paolo", buff[30]; SQLWCHAR wbuff[30]= {0}; wchar_t wcdata[]= L"S\x00e3o Paolo"; ok_sql(hstmt, "DROP TABLE IF EXISTS t_sqlwchar"); ok_sql(hstmt, "CREATE TABLE t_sqlwchar (a VARCHAR(30)) DEFAULT CHARSET utf8"); ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "INSERT INTO t_sqlwchar VALUES (?)", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_WVARCHAR, 0, 0, data, sizeof(data), NULL)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, 0, 0, W(wcdata), sizeof(wcdata), NULL)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_RESET_PARAMS)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT HEX(a) FROM t_sqlwchar"); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 1), "53C3A36F2050616F6C6F", 20); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 1), "53C3A36F2050616F6C6F", 20); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT a FROM t_sqlwchar"); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 1), data, sizeof(data)); ok_stmt(hstmt, SQLFetch(hstmt)); is_str(my_fetch_str(hstmt, buff, 1), data, sizeof(data)); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "SELECT a FROM t_sqlwchar"); ok_stmt(hstmt, SQLFetch(hstmt)); is_wstr(my_fetch_wstr(hstmt, wbuff, 1), wcdata, 9); ok_stmt(hstmt, SQLFetch(hstmt)); is_wstr(my_fetch_wstr(hstmt, wbuff, 1), wcdata, 9); expect_stmt(hstmt, SQLFetch(hstmt), SQL_NO_DATA_FOUND); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); ok_sql(hstmt, "DROP TABLE IF EXISTS t_sqlwchar"); return OK; } /* This is from the canonical example of retrieving a SQL_NUMERIC_STRUCT: http://support.microsoft.com/default.aspx/kb/222831 */ DECLARE_TEST(t_sqlnum_msdn) { SQLHANDLE ard; SQL_NUMERIC_STRUCT *sqlnum= malloc(sizeof(SQL_NUMERIC_STRUCT)); SQLCHAR exp_data[SQL_MAX_NUMERIC_LEN]= {0x7c, 0x62, 0,0,0,0,0,0,0,0,0,0,0,0,0,0}; sqlnum->sign= sqlnum->precision= sqlnum->scale= 128; ok_sql(hstmt, "select 25.212"); ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &ard, 0, NULL)); ok_desc(ard, SQLSetDescField(ard, 1, SQL_DESC_TYPE, (SQLPOINTER) SQL_C_NUMERIC, SQL_IS_INTEGER)); ok_desc(ard, SQLSetDescField(ard, 1, SQL_DESC_SCALE, (SQLPOINTER) 3, SQL_IS_INTEGER)); ok_desc(ard, SQLSetDescField(ard, 1, SQL_DESC_DATA_PTR, sqlnum, SQL_IS_POINTER)); ok_stmt(hstmt, SQLFetch(hstmt)); is_num(sqlnum->sign, 1); is_num(sqlnum->precision, 38); is_num(sqlnum->scale, 3); is(!memcmp(sqlnum->val, exp_data, SQL_MAX_NUMERIC_LEN)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); free(sqlnum); return OK; } /** Internal function to test retrieving a SQL_NUMERIC_STRUCT value. @todo Printing some additional output (sqlnum->val as hex, dec) @param[in] hstmt Statement handle @param[in] numstr String to retrieve as SQL_NUMERIC_STRUCT @param[in] prec Precision to retrieve @param[in] scale Scale to retrieve @param[in] sign Expected sign (1=+,0=-) @param[in] expdata Expected byte array value (need this or expnum) @param[in] expnum Expected numeric value (if it fits) @param[in] overflow Whether to expect a retrieval failure (22003) @return OK/FAIL just like a test. */ int sqlnum_test_from_str(SQLHANDLE hstmt, const char *numstr, SQLCHAR prec, SQLSCHAR scale, SQLCHAR sign, SQLCHAR *expdata, int expnum, int overflow) { SQL_NUMERIC_STRUCT *sqlnum= malloc(sizeof(SQL_NUMERIC_STRUCT)); SQLCHAR buf[512]; SQLHANDLE ard; unsigned long numval; sprintf((char *)buf, "select %s", numstr); /* ok_sql(hstmt, buf); */ ok_stmt(hstmt, SQLExecDirect(hstmt, buf, SQL_NTS)); ok_stmt(hstmt, SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &ard, 0, NULL)); ok_desc(ard, SQLSetDescField(ard, 1, SQL_DESC_TYPE, (SQLPOINTER) SQL_C_NUMERIC, SQL_IS_INTEGER)); ok_desc(ard, SQLSetDescField(ard, 1, SQL_DESC_PRECISION, (SQLPOINTER)(SQLINTEGER) prec, SQL_IS_INTEGER)); ok_desc(ard, SQLSetDescField(ard, 1, SQL_DESC_SCALE, (SQLPOINTER)(SQLINTEGER) scale, SQL_IS_INTEGER)); ok_desc(ard, SQLSetDescField(ard, 1, SQL_DESC_DATA_PTR, sqlnum, SQL_IS_POINTER)); if (overflow) { expect_stmt(hstmt, SQLFetch(hstmt), SQL_ERROR); return check_sqlstate(hstmt, "22003"); } else ok_stmt(hstmt, SQLFetch(hstmt)); is_num(sqlnum->precision, prec); is_num(sqlnum->scale, scale); is_num(sqlnum->sign, sign); if (expdata) { is(!memcmp(sqlnum->val, expdata, SQL_MAX_NUMERIC_LEN)); } else { /* only use this for <=32bit values */ int i; numval= 0; for (i= 0; i < 8; ++i) numval += sqlnum->val[7 - i] << (8 * (7 - i)); is_num(numval, expnum); } ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); free(sqlnum); return OK; } /* Testing of retrieving SQL_NUMERIC_STRUCT Make sure to use little-endian in SQLCHAR arrays. */ DECLARE_TEST(t_sqlnum_from_str) { char *num1= "25.212"; char *num2= "-101234.0010"; char *num3= "-101230.0010"; /* some basic tests including min-precision and scale changes */ is(sqlnum_test_from_str(hstmt, num1, 5, 3, 1, NULL, 25212, 0) == OK); is(sqlnum_test_from_str(hstmt, num1, 4, 3, 1, NULL, 0, 1) == OK); is(sqlnum_test_from_str(hstmt, num1, 4, 2, 1, NULL, 2521, 0) == OK); is(sqlnum_test_from_str(hstmt, num1, 2, 0, 1, NULL, 25, 0) == OK); is(sqlnum_test_from_str(hstmt, num1, 2, -1, 1, NULL, 0, 1) == OK); /* more comprehensive testing of scale and precision */ {SQLCHAR expdata[]= {0x2a, 0x15, 0x57, 0x3c, 0,0,0,0,0,0,0,0,0,0,0,0}; is(sqlnum_test_from_str(hstmt, num2, 9, 4, 0, expdata, 0, 0) == OK);} is(sqlnum_test_from_str(hstmt, num2, 9, 4, 0, NULL, 1012340010, 0) == OK); is(sqlnum_test_from_str(hstmt, num2, 9, 3, 0, NULL, 101234001, 0) == OK); is(sqlnum_test_from_str(hstmt, num2, 8, 3, 0, NULL, 0, 1) == OK); is(sqlnum_test_from_str(hstmt, num2, 8, 2, 0, NULL, 10123400, 0) == OK); is(sqlnum_test_from_str(hstmt, num2, 7, 2, 0, NULL, 10123400, 0) == OK); is(sqlnum_test_from_str(hstmt, num2, 6, 2, 0, NULL, 10123400, 0) == OK); is(sqlnum_test_from_str(hstmt, num2, 6, 1, 0, NULL, 1012340, 0) == OK); is(sqlnum_test_from_str(hstmt, num2, 6, 0, 0, NULL, 101234, 0) == OK); is(sqlnum_test_from_str(hstmt, num2, 6, -1, 0, NULL, 0, 1) == OK); is(sqlnum_test_from_str(hstmt, num3, 6, -1, 0, NULL, 10123, 0) == OK); is(sqlnum_test_from_str(hstmt, num3, 5, -1, 0, NULL, 10123, 0) == OK); /* Bug#35920 */ is(sqlnum_test_from_str(hstmt, "8000.00", 30, 2, 1, NULL, 800000, 0) == OK); is(sqlnum_test_from_str(hstmt, "1234567.00", 30, 2, 1, NULL, 123456700, 0) == OK); /* some larger numbers */ {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0xD5, 0x50, 0x94, 0x49, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_from_str(hstmt, "1234456789", 10, 0, 1, expdata, 0, 0));} {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0x7c, 0x62, 0,0, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_from_str(hstmt, "25.212", 5, 3, 1, expdata, 0, 0));} {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0xaa, 0x86, 0x1, 0, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_from_str(hstmt, "10.0010", 6, 4, 1, expdata, 0, 0));} {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0x2a, 0x15, 0x57, 0x3c, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_from_str(hstmt, "-101234.0010", 10, 4, 0, expdata, 0, 0));} {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0x72, 0x8b, 0x1, 0, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_from_str(hstmt, "101234", 6, 0, 1, expdata, 0, 0));} {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0x97, 0x03, 0x7C, 0xE3, 0x76, 0x5E, 0xF0, 0x00, 0x24, 0x1A, 0,0,0,0,0,0}; is(OK == sqlnum_test_from_str(hstmt, "123445678999123445678999", 24, 0, 1, expdata, 0, 0));} {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0x95, 0xFA, 0x0B, 0xF1, 0xED, 0x3C, 0x7C, 0xE4, 0x1B, 0x5F, 0x80, 0x1A, 0x16, 0x06, 0,0}; is(OK == sqlnum_test_from_str(hstmt, "123445678999123445678999543216789", 33, 0, 1, expdata, 0, 0));} {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_from_str(hstmt, "12344567899912344567899954321678909876543212", 44, 0, 1, expdata, 0, 1));} /* overflow with dec pt after the overflow */ {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_from_str(hstmt, "1234456789991234456789995432167890987654321.2", 44, 1, 1, expdata, 0, 1));} {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; is(OK == sqlnum_test_from_str(hstmt, "340282366920938463463374607431768211455", 39, 0, 1, expdata, 0, 0)); /* MAX */} {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_from_str(hstmt, "340282366920938463463374607431768211456", 39, 0, 1, expdata, 0, 1)); /* MAX+1 */} {SQLCHAR expdata[SQL_MAX_NUMERIC_LEN]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_from_str(hstmt, "0", 1, 0, 1, expdata, 0, 0));} return OK; } /* Basic test of binding a SQL_NUMERIC_STRUCT as a query parameter */ DECLARE_TEST(t_bindsqlnum_basic) { SQL_NUMERIC_STRUCT *sqlnum= malloc(sizeof(SQL_NUMERIC_STRUCT)); SQLCHAR outstr[20]; memset(sqlnum, 0, sizeof(SQL_NUMERIC_STRUCT)); sqlnum->sign= 1; sqlnum->val[0]= 0x7c; sqlnum->val[1]= 0x62; ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *)"select ?", SQL_NTS)); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_DECIMAL, 5, 3, sqlnum, 0, NULL)); ok_stmt(hstmt, SQLExecute(hstmt)); ok_stmt(hstmt, SQLFetch(hstmt)); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, outstr, 20, NULL)); is_str(outstr, "25.212", 6); is_num(sqlnum->sign, 1); is_num(sqlnum->precision, 5); is_num(sqlnum->scale, 3); return OK; } /** Internal function to test sending a SQL_NUMERIC_STRUCT value. @todo Printing some additional output (sqlnum->val as hex, dec) @param[in] hstmt Statement handle @param[in] numdata Numeric data @param[in] prec Precision to send @param[in] scale Scale to send @param[in] sign Sign (1=+,0=-) @param[in] outstr Expected result string @param[in] exptrunc Expected truncation failure @return OK/FAIL just like a test. */ int sqlnum_test_to_str(SQLHANDLE hstmt, SQLCHAR *numdata, SQLCHAR prec, SQLSCHAR scale, SQLCHAR sign, char *outstr, char *exptrunc) { SQL_NUMERIC_STRUCT *sqlnum= malloc(sizeof(SQL_NUMERIC_STRUCT)); SQLCHAR obuf[30]; SQLRETURN exprc= SQL_SUCCESS; /* TODO until sqlnum errors are supported */ /* if (!strcmp("01S07", exptrunc)) exprc= SQL_SUCCESS_WITH_INFO; else if (!strcmp("22003", exptrunc)) exprc= SQL_ERROR; */ sqlnum->sign= sign; memcpy(sqlnum->val, numdata, SQL_MAX_NUMERIC_LEN); ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_DECIMAL, prec, scale, sqlnum, 0, NULL)); ok_sql(hstmt, "select ?"); expect_stmt(hstmt, SQLFetch(hstmt), exprc); if (exprc != SQL_SUCCESS) { is(check_sqlstate(hstmt, (char *)exptrunc) == OK); } if (exprc == SQL_ERROR) return OK; is_num(sqlnum->precision, prec); is_num(sqlnum->scale, scale); is_num(sqlnum->sign, sign); ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_C_CHAR, obuf, sizeof(obuf), NULL)); is_str(obuf, outstr, strlen((char *)outstr)); is(!memcmp(sqlnum->val, numdata, SQL_MAX_NUMERIC_LEN)); ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); free(sqlnum); return OK; } /* Testing of passing SQL_NUMERIC_STRUCT as query parameters */ DECLARE_TEST(t_sqlnum_to_str) { {SQLCHAR numdata[]= {0xD5, 0x50, 0x94, 0x49, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_to_str(hstmt, numdata, 10, 4, 1, "123445.6789", ""));} /* fractional truncation */ {SQLCHAR numdata[]= {0xD5, 0x50, 0x94, 0x49, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_to_str(hstmt, numdata, 9, 2, 1, "12344567.8", "01S07"));} {SQLCHAR numdata[]= {0xD5, 0x50, 0x94, 0x49, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_to_str(hstmt, numdata, 8, 2, 1, "12344567", "01S07"));} /* whole number truncation - error */ /* TODO need err handling for this test {SQLCHAR numdata[]= {0xD5, 0x50, 0x94, 0x49, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_to_str(hstmt, numdata, 7, 2, 1, "1234456", "22003"));} */ /* negative scale */ {SQLCHAR numdata[]= {0xD5, 0x50, 0x94, 0x49, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_to_str(hstmt, numdata, 10, -2, 1, "123445678900", ""));} {SQLCHAR numdata[]= {0xD5, 0x50, 0x94, 0x49, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_to_str(hstmt, numdata, 10, -2, 0, "-123445678900", ""));} /* scale > prec */ {SQLCHAR numdata[]= {0xD5, 0x50, 0x94, 0x49, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_to_str(hstmt, numdata, 10, 11, 1, "0.01234456789", ""));} {SQLCHAR numdata[]= {0xD5, 0x50, 0x94, 0x49, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_to_str(hstmt, numdata, 10, 11, 0, "-0.01234456789", ""));} {SQLCHAR numdata[]= {0xD5, 0x50, 0x94, 0x49, 0,0,0,0,0,0,0,0,0,0,0,0}; is(OK == sqlnum_test_to_str(hstmt, numdata, 10, 20, 1, "0.00000000001234456789", ""));} return OK; } /* Bug #31220 - SQLFetch or SQLFetchScroll returns negative data length when using SQL_C_WCHAR */ DECLARE_TEST(t_bug31220) { SQLLEN outlen= 999; SQLWCHAR outbuf[5]; /* the call sequence of this test is not allowed under a driver manager */ if (using_dm(hdbc)) return OK; ok_sql(hstmt, "select 1"); ok_stmt(hstmt, SQLBindCol(hstmt, 1, 999 /* unknown type */, outbuf, 5, &outlen)); expect_stmt(hstmt, SQLFetch(hstmt), SQL_ERROR); is(check_sqlstate(hstmt, "07006") == OK); is_num(outlen, 999); return OK; } /** Bug #29402: field type charset 63 problem */ DECLARE_TEST(t_bug29402) { SQLSMALLINT name_length, data_type, decimal_digits, nullable; SQLCHAR column_name[SQL_MAX_COLUMN_NAME_LEN]; SQLCHAR conn[256], conn_out[256]; SQLSMALLINT conn_out_len; SQLULEN column_size; SQLCHAR buf[80]= {0}; SQLLEN buflen= 0; SQLHDBC hdbc1; SQLHSTMT hstmt1; const SQLCHAR *expected= "\x80""100"; ok_env(henv, SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1)); /* First check how the option NO_BINARY_RESULT works */ sprintf((char *)conn, "DSN=%s;UID=%s;PWD=%s;NO_BINARY_RESULT=1;CHARSET=CP1250", mydsn, myuid, mypwd); ok_con(hdbc1, SQLDriverConnect(hdbc1, NULL, conn, SQL_NTS, conn_out, sizeof(conn_out), &conn_out_len, SQL_DRIVER_NOPROMPT)); ok_con(hdbc1, SQLAllocStmt(hdbc1, &hstmt1)); ok_stmt(hstmt1, SQLExecDirect(hstmt1, "SELECT CONCAT(_cp1250 0x80, 100) concated", SQL_NTS)); ok_stmt(hstmt1, SQLDescribeCol(hstmt1, 1, column_name, sizeof(column_name), &name_length, &data_type, &column_size, &decimal_digits, &nullable)); is(data_type == SQL_VARCHAR || data_type == SQL_WVARCHAR); ok_stmt(hstmt1, SQLFetch(hstmt1)); ok_stmt(hstmt1, SQLGetData(hstmt1, 1, SQL_C_CHAR, buf, sizeof(buf), &buflen)); is_num(buflen, 4); if (strncmp(buf, expected, buflen) != 0) { /* Because of this http://msdn.microsoft.com/en-us/library/ms716540%28v=vs.85%29.aspx test can fail. Rather test problem. Hopefully the test is fixed, but keeping this message so far */ printMessage("%s != %s(%#x!=%#x) - this test may fail on some " "platforms - TODO", buf, "\x80""100", buf[0], expected[0]); return FAIL; } ok_stmt(hstmt1, SQLFreeStmt(hstmt1, SQL_DROP)); ok_con(hdbc1, SQLDisconnect(hdbc1)); ok_con(hdbc1, SQLFreeConnect(hdbc1)); /* Check without FLAG_NO_BINARY_RESULT */ ok_sql(hstmt, "SELECT CONCAT('\x80', 100) concated"); ok_stmt(hstmt, SQLDescribeCol(hstmt, 1, column_name, sizeof(column_name), &name_length, &data_type, &column_size, &decimal_digits, &nullable)); /* Fixed in 5.5(tested in 5.5.9), result's type is SQL_VARCHAR */ if (mysql_min_version(hdbc, "5.5", 3)) { /* Depending on server default charset it can be either SQL_VARCHAR or SQL_WVARCHAR. Wee are fine to know if the data_type is one of those */ if(data_type != SQL_VARCHAR && data_type != SQL_WVARCHAR) { return FAIL; } } else { printMessage("Server version is <=5.1"); is_num(data_type, SQL_VARBINARY); } return OK; } BEGIN_TESTS ADD_TEST(t_longlong1) ADD_TEST(t_decimal) ADD_TEST(t_bigint) ADD_TEST(t_enumset) ADD_TEST(t_bug16917) ADD_TEST(t_bug16235) ADD_TEST(t_bug27862_1) ADD_TODO(t_bug27862_2) ADD_TEST(decimal_scale) ADD_TEST(binary_suffix) ADD_TEST(float_scale) ADD_TEST(bit) ADD_TEST(t_bug32171) ADD_TEST(sqlwchar) ADD_TEST(t_sqlnum_msdn) ADD_TEST(t_sqlnum_from_str) ADD_TEST(t_bindsqlnum_basic) ADD_TEST(t_sqlnum_to_str) ADD_TEST(t_bug31220) ADD_TEST(t_bug29402) END_TESTS RUN_TESTS mysql-connector-odbc-5.1.10-src/util/Makefile.am100644 15766 12 611 11707541005 20155 0ustar00cteamstaffINCLUDES = -I$(top_srcdir) noinst_LTLIBRARIES = libmyodbc3u.la # base source list libmyodbc3u_la_SOURCES = \ stringutil.h \ stringutil.c \ installer.h \ installer.c \ odbcinstw.c \ unicode_transcode.c \ MYODBCUtil.h # libmysql lib deps libmyodbc3u_la_LIBADD = $(LTLIBS_DEPS) @DL_LIB@ @MYSQL_LIB@ libmyodbc3u_la_DEPENDENCIES = $(LTLIBS_DEPS) EXTRA_DIST = \ CMakeLists.txt mysql-connector-odbc-5.1.10-src/util/CMakeLists.txt100644 15766 12 3434 11707541005 20707 0ustar00cteamstaff# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## SET(myodbc3u_SRCS stringutil.c stringutil.h unicode_transcode.c installer.c installer.h) IF(NOT WIN32) SET(myodbc3u_SRCS ${myodbc3u_SRCS} odbcinstw.c) ENDIF(NOT WIN32) ADD_LIBRARY(myodbc3u STATIC ${myodbc3u_SRCS}) TARGET_LINK_LIBRARIES(myodbc3u ${ODBCINSTLIB}) IF(NOT WIN32) SET_TARGET_PROPERTIES(myodbc3u PROPERTIES COMPILE_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS}) INCLUDE_DIRECTORIES(${DL_INCLUDES}) TARGET_LINK_LIBRARIES(myodbc3u ${DL_LIBS}) SET_TARGET_PROPERTIES(myodbc3u PROPERTIES LINK_FLAGS "${DL_LFLAGS}") ENDIF(NOT WIN32) mysql-connector-odbc-5.1.10-src/util/MYODBCUtil.h100644 15766 12 23624 11707541005 20156 0ustar00cteamstaff/* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MYODBCUTIL_H #define MYODBCUTIL_H /* We define _GNU_SOURCE to get the definition of strndup() from string.h */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include /* #include "../MYODBC_MYSQL.h" */ #include "../MYODBC_CONF.h" #include "../MYODBC_ODBC.h" #ifdef _WIN32 /* It could be defined in mysql headers - we don't want the warning, but want to redefine it */ # ifdef strcasecmp # undef strcasecmp # endif # define strcasecmp( a, b ) stricmp( a, b ) #endif /* Handle case on OSX where we want to use GetPrivateProfileString (because it actually works properly) instead of SQLGetPrivateProfileString but GetPrivateProfileString does not exist because we are dealing with 10.4 or newer - I admit - this is a bit of a hack. */ #ifndef GetPrivateProfileString #define GetPrivateProfileString SQLGetPrivateProfileString #endif #ifndef FALSE #define FALSE 0 #define TRUE 1 #endif #ifndef myodbc_max #define myodbc_max( a, b ) (((a) > (b)) ? (a) : (b)) #define myodbc_min( a, b ) (((a) < (b)) ? (a) : (b)) #endif /* Most of the installer API functions in iODBC incorrectly reset the config mode, so we need to save and restore it whenever we call those functions. These macros reduce the clutter a little bit. */ #if USE_IODBC # define SAVE_MODE() UWORD config_mode; (void)SQLGetConfigMode(&config_mode) # define RESTORE_MODE() (void)SQLSetConfigMode(config_mode) #else # define SAVE_MODE() # define RESTORE_MODE() #endif /* Could use DRIVER_NAME but trying to keep dependency upon driver specific code to a min */ #define MYODBCINST_DRIVER_NAME L"MySQL ODBC 5.1 Driver" /* max dsn's we can have in odbc sys info - need to get rid of this limit */ #define MYODBCUTIL_MAX_DSN_NAMES 50 #define MYODBC_DB_NAME_MAX 255 /*! \internal \brief Delimiter type used for an attribute string. */ typedef enum tMYODBCUTIL_DELIM { MYODBCUTIL_DELIM_NULL, MYODBCUTIL_DELIM_SEMI, MYODBCUTIL_DELIM_BOTH } MYODBCUTIL_DELIM; /*! \internal \brief Parse state. These are the different states we can be in while parsing an attributes string. */ typedef enum tMYODBCUTIL_ATTR_PARSE_STATE { MYODBCUTIL_ATTR_PARSE_STATE_NAME_START, /* looking for start of name */ MYODBCUTIL_ATTR_PARSE_STATE_NAME, /* looking for end of name */ MYODBCUTIL_ATTR_PARSE_STATE_EQUAL, /* looking for equal sign */ MYODBCUTIL_ATTR_PARSE_STATE_VALUE_START, /* looking for start of value */ MYODBCUTIL_ATTR_PARSE_STATE_VALUE /* looking for end of value */ } MYODBCUTIL_ATTR_PARSE_STATE; /*! Driver fields. This reflects what we can have in odbcinst.ini. */ typedef struct tMYODBCUTIL_DRIVER { char *pszName; /* Friendly name for driver. (ie "MySQL ODBC 3.51 Driver") */ char *pszDRIVER; /* File name - typically complete abs path. (ie "/usr/lib/libmyodbc5.so") */ char *pszSETUP; /* File name - typically complete abs path. (ie "/usr/lib/libmyodbc5S.so") */ } MYODBCUTIL_DRIVER; /*! \brief Used to indicate what is being used to do a SQLDriverConnect. */ typedef enum tMYODBCUTIL_DATASOURCE_CONNECT { MYODBCUTIL_DATASOURCE_CONNECT_DRIVER, MYODBCUTIL_DATASOURCE_CONNECT_DSN } MYODBCUTIL_DATASOURCE_CONNECT; /*! \brief Prompting used during SQLDriverConnect. Could be a SQLUSMALLINT and then standard ODBC values such as SQL_DRIVER_PROMPT but there are worthwhile advantages to creating this type internally. */ typedef enum tMYODBCUTIL_DATASOURCE_PROMPT { MYODBCUTIL_DATASOURCE_PROMPT_PROMPT, MYODBCUTIL_DATASOURCE_PROMPT_COMPLETE, MYODBCUTIL_DATASOURCE_PROMPT_REQUIRED, MYODBCUTIL_DATASOURCE_PROMPT_NOPROMPT } MYODBCUTIL_DATASOURCE_PROMPT; /*! \brief DSN edit mode. Reason why we have created a MYODBCUTIL_DATASOURCE. */ typedef enum tMYODBCUTIL_DATASOURCE_MODE { MYODBCUTIL_DATASOURCE_MODE_DSN_ADD, /* For ConfigDSN() - ODBC_ADD_DSN. */ MYODBCUTIL_DATASOURCE_MODE_DSN_EDIT, /* For ConfigDSN() - ODBC_CONFIG_DSN. */ MYODBCUTIL_DATASOURCE_MODE_DSN_VIEW, /* For command-line tools to list DSN details. */ MYODBCUTIL_DATASOURCE_MODE_DRIVER_CONNECT /* For SQLDriverConnect(). */ } MYODBCUTIL_DATASOURCE_MODE; /*! DSN fields. This reflects what we can have in odbc.ini and also includes information used during an SQLDriverConnect(). */ typedef struct tMYODBCUTIL_DATASOURCE { char *pszDriverFileName; /* As used in DSN; often the abs path to driver. (ie "/usr/lib/libmyodbc5.so") */ char *pszDSN; /* The name of our data source. (ie "test") */ char *pszDRIVER; /* Friendly driver name as used in DSN and connect str. (ie "MySQL ODBC 3.51 Driver (32 bit)") */ char *pszDESCRIPTION; /* General description or long name of DSN. */ char *pszSERVER; /* The hostname of the MySQL server. */ char *pszUSER; /* The username used to connect to MySQL. */ char *pszPASSWORD; /* The password for the server user combination. */ char *pszDATABASE; /* The default database. */ char *pszPORT; /* The TCP/IP port to use if SERVER is not localhost. */ char *pszSOCKET; /* Unix socket file or Windows named pipe to connect to. */ char *pszSTMT; /* Statement that will be exec when connecting to MySQL. */ char *pszOPTION; /* Options that specify how MyODBC should work. */ BOOL bINTERACTIVE; /* ODBC option for client_interactive connection option */ char *pszSSLKEY; /* pathname to SSL key file */ char *pszSSLCERT; /* pathname to SSL certificate file */ char *pszSSLCA; /* pathname to SSL certificate authority file */ char *pszSSLCAPATH; /* pathname to a directory that contains SSL ceritificate authority files */ char *pszSSLCIPHER; /* pathname to a list of allowable ciphers */ char *pszSSLVERIFY; /* verify server certificate (see --ssl-verify-server-cert */ char *pszCHARSET; /* default charset to use for connection */ char *pszREADTIMEOUT; /* connection read timeout (see mysql_options / MYSQL_OPT_READ_TIMEOUT */ char *pszWRITETIMEOUT; /* connection write timeout (see mysql_options / MYSQL_OPT_WRITE_TIMEOUT */ MYODBCUTIL_DATASOURCE_MODE nMode; /* ConfigDSN mode or SQLDriverConnect mode. */ MYODBCUTIL_DATASOURCE_CONNECT nConnect; /* SQLDriverConnect() using a DSN or a DRIVER to connect. */ MYODBCUTIL_DATASOURCE_PROMPT nPrompt; /* SQLDriverConnect() kind of prompting (if any). */ BOOL bSaveFileDSN; /* Flag specifying that SQLConnect was invoked as part of creation of a file DSN */ } MYODBCUTIL_DATASOURCE; #ifdef __cplusplus extern "C" { #endif #if defined(__APPLE__) && 0 int GetPrivateProfileString( LPCSTR lpszSection, LPCSTR lpszEntry, LPCSTR lpszDefault, LPSTR lpszRetBuffer, int cbRetBuffer, LPCSTR lpszFilename ); #endif #if defined(WIN32) char *strglobaldup( const char *s); char *strnglobaldup( const char *s, size_t n); # define _global_strdup(s) strglobaldup(s) # define _global_strndup(s, n) strnglobaldup(s, n) # define _global_alloc(n) GlobalAlloc(GMEM_FIXED, (n)) # define _global_free(p) GlobalFree(p) #else # define _global_strdup(s) strdup(s) # ifdef HAVE_STRNDUP # define myodbc_strndup(s, n) strndup(s, n) # define _global_strndup(s, n) strndup(s, n) # else char *myodbc_strndup( const char *s, size_t n ); # define _global_strndup(s, n) myodbc_strndup(s, n) # endif # define _global_alloc malloc # define _global_free(p) free(p) #endif #ifdef __cplusplus } #endif #endif mysql-connector-odbc-5.1.10-src/util/installer.c100644 15766 12 127074 11707541005 20357 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Installer wrapper implementations. * * How to add a data-source parameter: * Search for DS_PARAM, and follow the code around it * Add an extra field to the DataSource struct (installer.h) * Add a default value in ds_new() * Initialize/destroy them in ds_new/ds_delete * Print the field in myodbc3i.c * Add to the configuration GUIs * */ #include "stringutil.h" #include "installer.h" /* SQLGetPrivateProfileStringW is buggy in all releases of unixODBC as of 2007-12-03, so always use our replacement. */ #if USE_UNIXODBC # define SQLGetPrivateProfileStringW MySQLGetPrivateProfileStringW #endif /* Most of the installer API functions in iODBC incorrectly reset the config mode, so we need to save and restore it whenever we call those functions. These macros reduce the clutter a little bit. */ #if USE_IODBC # define SAVE_MODE() UWORD config_mode= config_get() # define RESTORE_MODE() config_set(config_mode) #else # define SAVE_MODE() # define RESTORE_MODE() #endif /* ODBC Installer Config Wrapper */ /* a few constants */ static SQLWCHAR W_EMPTY[]= {0}; static SQLWCHAR W_ODBCINST_INI[]= {'O', 'D', 'B', 'C', 'I', 'N', 'S', 'T', '.', 'I', 'N', 'I', 0}; static SQLWCHAR W_ODBC_INI[]= {'O', 'D', 'B', 'C', '.', 'I', 'N', 'I', 0}; static SQLWCHAR W_CANNOT_FIND_DRIVER[]= {'C', 'a', 'n', 'n', 'o', 't', ' ', 'f', 'i', 'n', 'd', ' ', 'd', 'r', 'i', 'v', 'e', 'r', 0}; static SQLWCHAR W_DSN[]= {'D', 'S', 'N', 0}; static SQLWCHAR W_DRIVER[]= {'D', 'r', 'i', 'v', 'e', 'r', 0}; static SQLWCHAR W_DESCRIPTION[]= {'D', 'E', 'S', 'C', 'R', 'I', 'P', 'T', 'I', 'O', 'N', 0}; static SQLWCHAR W_SERVER[]= {'S', 'E', 'R', 'V', 'E', 'R', 0}; static SQLWCHAR W_UID[]= {'U', 'I', 'D', 0}; static SQLWCHAR W_USER[]= {'U', 'S', 'E', 'R', 0}; static SQLWCHAR W_PWD[]= {'P', 'W', 'D', 0}; static SQLWCHAR W_PASSWORD[]= {'P', 'A', 'S', 'S', 'W', 'O', 'R', 'D', 0}; static SQLWCHAR W_DB[]= {'D', 'B', 0}; static SQLWCHAR W_DATABASE[]= {'D', 'A', 'T', 'A', 'B', 'A', 'S', 'E', 0}; static SQLWCHAR W_SOCKET[]= {'S', 'O', 'C', 'K', 'E', 'T', 0}; static SQLWCHAR W_INITSTMT[]= {'I', 'N', 'I', 'T', 'S', 'T', 'M', 'T', 0}; static SQLWCHAR W_OPTION[]= {'O', 'P', 'T', 'I', 'O', 'N', 0}; static SQLWCHAR W_CHARSET[]= {'C', 'H', 'A', 'R', 'S', 'E', 'T', 0}; static SQLWCHAR W_SSLKEY[]= {'S', 'S', 'L', 'K', 'E', 'Y', 0}; static SQLWCHAR W_SSLCERT[]= {'S', 'S', 'L', 'C', 'E', 'R', 'T', 0}; static SQLWCHAR W_SSLCA[]= {'S', 'S', 'L', 'C', 'A', 0}; static SQLWCHAR W_SSLCAPATH[]= {'S', 'S', 'L', 'C', 'A', 'P', 'A', 'T', 'H', 0}; static SQLWCHAR W_SSLCIPHER[]= {'S', 'S', 'L', 'C', 'I', 'P', 'H', 'E', 'R', 0}; static SQLWCHAR W_SSLVERIFY[]= {'S', 'S', 'L', 'V', 'E', 'R', 'I', 'F', 'Y', 0}; static SQLWCHAR W_PORT[]= {'P', 'O', 'R', 'T', 0}; static SQLWCHAR W_SETUP[]= {'S', 'E', 'T', 'U', 'P', 0}; static SQLWCHAR W_READTIMEOUT[]= {'R','E','A','D','T','I','M','E','O','U','T',0}; static SQLWCHAR W_WRITETIMEOUT[]= {'W','R','I','T','E','T','I','M','E','O','U','T',0}; static SQLWCHAR W_FOUND_ROWS[]= {'F','O','U','N','D','_','R','O','W','S',0}; static SQLWCHAR W_BIG_PACKETS[]= {'B','I','G','_','P','A','C','K','E','T','S',0}; static SQLWCHAR W_NO_PROMPT[]= {'N','O','_','P','R','O','M','P','T',0}; static SQLWCHAR W_DYNAMIC_CURSOR[]= {'D','Y','N','A','M','I','C','_','C','U','R','S','O','R',0}; static SQLWCHAR W_NO_SCHEMA[]= {'N','O','_','S','C','H','E','M','A',0}; static SQLWCHAR W_NO_DEFAULT_CURSOR[]= {'N','O','_','D','E','F','A','U','L','T','_','C','U','R','S','O','R',0}; static SQLWCHAR W_NO_LOCALE[]= {'N','O','_','L','O','C','A','L','E',0}; static SQLWCHAR W_PAD_SPACE[]= {'P','A','D','_','S','P','A','C','E',0}; static SQLWCHAR W_FULL_COLUMN_NAMES[]= {'F','U','L','L','_','C','O','L','U','M','N','_','N','A','M','E','S',0}; static SQLWCHAR W_COMPRESSED_PROTO[]= {'C','O','M','P','R','E','S','S','E','D','_','P','R','O','T','O',0}; static SQLWCHAR W_IGNORE_SPACE[]= {'I','G','N','O','R','E','_','S','P','A','C','E',0}; static SQLWCHAR W_NAMED_PIPE[]= {'N','A','M','E','D','_','P','I','P','E',0}; static SQLWCHAR W_NO_BIGINT[]= {'N','O','_','B','I','G','I','N','T',0}; static SQLWCHAR W_NO_CATALOG[]= {'N','O','_','C','A','T','A','L','O','G',0}; static SQLWCHAR W_USE_MYCNF[]= {'U','S','E','_','M','Y','C','N','F',0}; static SQLWCHAR W_SAFE[]= {'S','A','F','E',0}; static SQLWCHAR W_NO_TRANSACTIONS[]= {'N','O','_','T','R','A','N','S','A','C','T','I','O','N','S',0}; static SQLWCHAR W_LOG_QUERY[]= {'L','O','G','_','Q','U','E','R','Y',0}; static SQLWCHAR W_NO_CACHE[]= {'N','O','_','C','A','C','H','E',0}; static SQLWCHAR W_FORWARD_CURSOR[]= {'F','O','R','W','A','R','D','_','C','U','R','S','O','R',0}; static SQLWCHAR W_AUTO_RECONNECT[]= {'A','U','T','O','_','R','E','C','O','N','N','E','C','T',0}; static SQLWCHAR W_AUTO_IS_NULL[]= {'A','U','T','O','_','I','S','_','N','U','L','L',0}; static SQLWCHAR W_ZERO_DATE_TO_MIN[]= {'Z','E','R','O','_','D','A','T','E','_','T','O','_','M','I','N',0}; static SQLWCHAR W_MIN_DATE_TO_ZERO[]= {'M','I','N','_','D','A','T','E','_','T','O','_','Z','E','R','O',0}; static SQLWCHAR W_MULTI_STATEMENTS[]= {'M','U','L','T','I','_','S','T','A','T','E','M','E','N','T','S',0}; static SQLWCHAR W_COLUMN_SIZE_S32[]= {'C','O','L','U','M','N','_','S','I','Z','E','_','S','3','2',0}; static SQLWCHAR W_NO_BINARY_RESULT[]= {'N','O','_','B','I','N','A','R','Y','_','R','E','S','U','L','T',0}; static SQLWCHAR W_DFLT_BIGINT_BIND_STR[]= {'D','F','L','T','_','B','I','G','I','N','T','_','B','I','N','D','_','S','T','R',0}; static SQLWCHAR W_CLIENT_INTERACTIVE[]= {'I','N','T','E','R','A','C','T','I','V','E',0}; static SQLWCHAR W_NO_I_S[]= {'N','O','_','I','_','S',0}; /* DS_PARAM */ /* externally used strings */ const SQLWCHAR W_DRIVER_PARAM[]= {';', 'D', 'R', 'I', 'V', 'E', 'R', '=', 0}; const SQLWCHAR W_DRIVER_NAME[]= {'M', 'y', 'S', 'Q', 'L', ' ', 'O', 'D', 'B', 'C', ' ', '5', '.', '1', ' ', 'D', 'r', 'i', 'v', 'e', 'r', 0}; const SQLWCHAR W_INVALID_ATTR_STR[]= {'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ', 'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', ' ', 's', 't', 'r', 'i', 'n', 'g', 0}; /* List of all DSN params, used when serializing to string */ static const SQLWCHAR *dsnparams[]= {W_DSN, W_DRIVER, W_DESCRIPTION, W_SERVER, W_UID, W_PWD, W_DATABASE, W_SOCKET, W_INITSTMT, W_PORT, W_OPTION, W_CHARSET, W_SSLKEY, W_SSLCERT, W_SSLCA, W_SSLCAPATH, W_SSLCIPHER, W_SSLVERIFY, W_READTIMEOUT, W_WRITETIMEOUT, W_FOUND_ROWS, W_BIG_PACKETS, W_NO_PROMPT, W_DYNAMIC_CURSOR, W_NO_SCHEMA, W_NO_DEFAULT_CURSOR, W_NO_LOCALE, W_PAD_SPACE, W_FULL_COLUMN_NAMES, W_COMPRESSED_PROTO, W_IGNORE_SPACE, W_NAMED_PIPE, W_NO_BIGINT, W_NO_CATALOG, W_USE_MYCNF, W_SAFE, W_NO_TRANSACTIONS, W_LOG_QUERY, W_NO_CACHE, W_FORWARD_CURSOR, W_AUTO_RECONNECT, W_AUTO_IS_NULL, W_ZERO_DATE_TO_MIN, W_MIN_DATE_TO_ZERO, W_MULTI_STATEMENTS, W_COLUMN_SIZE_S32, W_NO_BINARY_RESULT, W_DFLT_BIGINT_BIND_STR, W_CLIENT_INTERACTIVE, W_NO_I_S}; static const int dsnparamcnt= sizeof(dsnparams) / sizeof(SQLWCHAR *); /* DS_PARAM */ /* convenience macro to append a single character */ #define APPEND_SQLWCHAR(buf, ctr, c) {\ if (ctr) { \ *((buf)++)= (c); \ if (--(ctr)) \ *(buf)= 0; \ } \ } /* * Check whether a parameter value needs escaping. */ static int value_needs_escaped(SQLWCHAR *str) { SQLWCHAR c; while (str && (c= *str++)) { if (c >= '0' && c <= '9') continue; else if (c >= 'a' && c <= 'z') continue; else if (c >= 'A' && c <= 'Z') continue; /* other non-alphanumeric characters that don't need escaping */ switch (c) { case '_': case ' ': case '.': continue; } return 1; } return 0; } /* * Convenience function to get the current config mode. */ UWORD config_get() { UWORD mode; SQLGetConfigMode(&mode); return mode; } /* * Convenience function to set the current config mode. Returns the * mode in use when the function was called. */ UWORD config_set(UWORD mode) { UWORD current= config_get(); SQLSetConfigMode(mode); return current; } /* ODBC Installer Driver Wrapper */ /* * Create a new driver object. All string data is pre-allocated. */ Driver *driver_new() { Driver *driver= (Driver *)my_malloc(sizeof(Driver), MYF(0)); if (!driver) return NULL; driver->name= (SQLWCHAR *)my_malloc(ODBCDRIVER_STRLEN * sizeof(SQLWCHAR), MYF(0)); if (!driver->name) { x_free(driver); return NULL; } driver->lib= (SQLWCHAR *)my_malloc(ODBCDRIVER_STRLEN * sizeof(SQLWCHAR), MYF(0)); if (!driver->lib) { x_free(driver); x_free(driver->name); return NULL; } driver->setup_lib= (SQLWCHAR *)my_malloc(ODBCDRIVER_STRLEN * sizeof(SQLWCHAR), MYF(0)); if (!driver->setup_lib) { x_free(driver); x_free(driver->name); x_free(driver->lib); return NULL; } /* init to empty strings */ driver->name[0]= 0; driver->lib[0]= 0; driver->setup_lib[0]= 0; driver->name8= NULL; driver->lib8= NULL; driver->setup_lib8= NULL; return driver; } /* * Delete an existing driver object. */ void driver_delete(Driver *driver) { x_free(driver->name); x_free(driver->lib); x_free(driver->setup_lib); x_free(driver->name8); x_free(driver->lib8); x_free(driver->setup_lib8); x_free(driver); } #ifdef _WIN32 /* * Utility function to duplicate path and remove "(x86)" chars. */ SQLWCHAR *remove_x86(SQLWCHAR *path, SQLWCHAR *loc) { /* Program Files (x86) * 13^ 19^ */ size_t chars = sqlwcharlen(loc) - 18 /* need +1 for sqlwcharncat2() */; SQLWCHAR *news= sqlwchardup(path, SQL_NTS); news[(loc-path)+13] = 0; sqlwcharncat2(news, loc + 19, &chars); return news; } /* * Compare two library paths taking into account different * locations of "Program Files" for 32 and 64 bit applications * on Windows. This is done by removing the "(x86)" from "Program * Files" if present in either path. * * Note: wcs* functions are used as this only needs to support * Windows where SQLWCHAR is wchar_t. */ int Win64CompareLibs(SQLWCHAR *lib1, SQLWCHAR *lib2) { int free1= 0, free2= 0; int rc; SQLWCHAR *llib1, *llib2; /* perform necessary transformations */ if (llib1= wcsstr(lib1, L"Program Files (x86)")) { llib1= remove_x86(lib1, llib1); free1= 1; } else llib1= lib1; if (llib2= wcsstr(lib2, L"Program Files (x86)")) { llib2= remove_x86(lib2, llib2); free2= 1; } else llib2= lib2; /* perform the comparison */ rc= sqlwcharcasecmp(llib1, llib2); if (free1) free(llib1); if (free2) free(llib2); return rc; } #endif /* _WIN32 */ /* * Lookup a driver given only the filename of the driver. This is used: * * 1. When prompting for additional DSN info upon connect when the * driver uses an external setup library. * * 2. When testing a connection when adding/editing a DSN. */ int driver_lookup_name(Driver *driver) { SQLWCHAR drivers[16384]; SQLWCHAR *pdrv= drivers; SQLWCHAR driverinfo[1024]; int len; short slen; /* WORD needed for windows */ SAVE_MODE(); /* get list of drivers */ #ifdef _WIN32 if (!SQLGetInstalledDriversW(pdrv, 16383, &slen) || !(len= slen)) #else if (!(len = SQLGetPrivateProfileStringW(NULL, NULL, W_EMPTY, pdrv, 16383, W_ODBCINST_INI))) #endif return -1; RESTORE_MODE(); /* check the lib of each driver for one that matches the given lib name */ while (len > 0) { if (SQLGetPrivateProfileStringW(pdrv, W_DRIVER, W_EMPTY, driverinfo, 1023, W_ODBCINST_INI)) { RESTORE_MODE(); #ifdef _WIN32 if (!Win64CompareLibs(driverinfo, driver->lib)) #else if (!sqlwcharcasecmp(driverinfo, driver->lib)) #endif { sqlwcharncpy(driver->name, pdrv, ODBCDRIVER_STRLEN); return 0; } } RESTORE_MODE(); len -= sqlwcharlen(pdrv) + 1; pdrv += sqlwcharlen(pdrv) + 1; } return -1; } /* * Lookup a driver in the system. The driver name is read from the given * object. If greater-than zero is returned, additional information * can be obtained from SQLInstallerError(). A less-than zero return code * indicates that the driver could not be found. */ int driver_lookup(Driver *driver) { SQLWCHAR buf[4096]; SQLWCHAR *entries= buf; SQLWCHAR *dest; SAVE_MODE(); /* if only the filename is given, we must get the driver's name */ if (!*driver->name && *driver->lib) { if (driver_lookup_name(driver)) return -1; } /* get entries and make sure the driver exists */ if (SQLGetPrivateProfileStringW(driver->name, NULL, W_EMPTY, buf, 4096, W_ODBCINST_INI) < 1) { SQLPostInstallerErrorW(ODBC_ERROR_INVALID_NAME, W_CANNOT_FIND_DRIVER); return -1; } RESTORE_MODE(); /* read the needed driver attributes */ while (*entries) { dest= NULL; if (!sqlwcharcasecmp(W_DRIVER, entries)) dest= driver->lib; else if (!sqlwcharcasecmp(W_SETUP, entries)) dest= driver->setup_lib; else { /* unknown/unused entry */ } /* get the value if it's one we're looking for */ if (dest && SQLGetPrivateProfileStringW(driver->name, entries, W_EMPTY, dest, ODBCDRIVER_STRLEN, W_ODBCINST_INI) < 1) { RESTORE_MODE(); return 1; } RESTORE_MODE(); entries += sqlwcharlen(entries) + 1; } return 0; } /* * Read the semi-colon delimited key-value pairs the attributes * necessary to popular the driver object. */ int driver_from_kvpair_semicolon(Driver *driver, const SQLWCHAR *attrs) { const SQLWCHAR *split; const SQLWCHAR *end; SQLWCHAR attribute[100]; SQLWCHAR *dest; while (*attrs) { dest= NULL; /* invalid key-value pair if no equals */ if ((split= sqlwcharchr(attrs, '=')) == NULL) return 1; /* get end of key-value pair */ if ((end= sqlwcharchr(attrs, ';')) == NULL) end= attrs + sqlwcharlen(attrs); /* pull out the attribute name */ memcpy(attribute, attrs, (split - attrs) * sizeof(SQLWCHAR)); attribute[split - attrs]= 0; /* add null term */ ++split; /* if its one we want, copy it over */ if (!sqlwcharcasecmp(W_DRIVER, attribute)) dest= driver->lib; else if (!sqlwcharcasecmp(W_SETUP, attribute)) dest= driver->setup_lib; else { /* unknown/unused attribute */ } if (dest) { memcpy(dest, split, (end - split) * sizeof(SQLWCHAR)); dest[end - split]= 0; /* add null term */ } /* advanced to next attribute */ attrs= end; if (*end) ++attrs; } return 0; } /* * Write the attributes of the driver object into key-value pairs * separated by single NULL chars. */ int driver_to_kvpair_null(Driver *driver, SQLWCHAR *attrs, size_t attrslen) { *attrs= 0; attrs+= sqlwcharncat2(attrs, driver->name, &attrslen); /* append NULL-separator */ APPEND_SQLWCHAR(attrs, attrslen, 0); attrs+= sqlwcharncat2(attrs, W_DRIVER, &attrslen); APPEND_SQLWCHAR(attrs, attrslen, '='); attrs+= sqlwcharncat2(attrs, driver->lib, &attrslen); /* append NULL-separator */ APPEND_SQLWCHAR(attrs, attrslen, 0); if (*driver->setup_lib) { attrs+= sqlwcharncat2(attrs, W_SETUP, &attrslen); APPEND_SQLWCHAR(attrs, attrslen, '='); attrs+= sqlwcharncat2(attrs, driver->setup_lib, &attrslen); /* append NULL-separator */ APPEND_SQLWCHAR(attrs, attrslen, 0); } if (attrslen--) *attrs= 0; return !(attrslen > 0); } /* ODBC Installer Data Source Wrapper */ /* * Create a new data source object. */ DataSource *ds_new() { DataSource *ds= (DataSource *)my_malloc(sizeof(DataSource), MYF(0)); if (!ds) return NULL; memset(ds, 0, sizeof(DataSource)); /* non-zero DataSource defaults here */ ds->port= 3306; /* DS_PARAM */ return ds; } /* * Delete an existing data source object. */ void ds_delete(DataSource *ds) { x_free(ds->name); x_free(ds->driver); x_free(ds->description); x_free(ds->server); x_free(ds->uid); x_free(ds->pwd); x_free(ds->database); x_free(ds->socket); x_free(ds->initstmt); x_free(ds->charset); x_free(ds->sslkey); x_free(ds->sslcert); x_free(ds->sslca); x_free(ds->sslcapath); x_free(ds->sslcipher); x_free(ds->name8); x_free(ds->driver8); x_free(ds->description8); x_free(ds->server8); x_free(ds->uid8); x_free(ds->pwd8); x_free(ds->database8); x_free(ds->socket8); x_free(ds->initstmt8); x_free(ds->charset8); x_free(ds->sslkey8); x_free(ds->sslcert8); x_free(ds->sslca8); x_free(ds->sslcapath8); x_free(ds->sslcipher8); x_free(ds); } /* * Set a string attribute of a given data source object. The string * will be copied into the object. */ int ds_set_strattr(SQLWCHAR **attr, const SQLWCHAR *val) { x_free(*attr); if (val && *val) *attr= sqlwchardup(val, SQL_NTS); else *attr= NULL; return *attr || 0; } /* * Same as ds_set_strattr, but allows truncating the given string. If * charcount is 0 or SQL_NTS, it will act the same as ds_set_strattr. */ int ds_set_strnattr(SQLWCHAR **attr, const SQLWCHAR *val, size_t charcount) { x_free(*attr); if (charcount == SQL_NTS) charcount= sqlwcharlen(val); if (!charcount) { *attr= NULL; return 1; } if (val && *val) *attr= sqlwchardup(val, charcount); else *attr= NULL; return *attr || 0; } /* * Internal function to map a parameter name of the data source object * to the pointer needed to set the parameter. Only one of strdest or * intdest will be set. strdest and intdest will be set for populating * string (SQLWCHAR *) or int parameters. */ void ds_map_param(DataSource *ds, const SQLWCHAR *param, SQLWCHAR ***strdest, unsigned int **intdest, BOOL **booldest) { *strdest= NULL; *intdest= NULL; *booldest= NULL; /* parameter aliases can be used here, see W_UID, W_USER */ if (!sqlwcharcasecmp(W_DSN, param)) *strdest= &ds->name; else if (!sqlwcharcasecmp(W_DRIVER, param)) *strdest= &ds->driver; else if (!sqlwcharcasecmp(W_DESCRIPTION, param)) *strdest= &ds->description; else if (!sqlwcharcasecmp(W_SERVER, param)) *strdest= &ds->server; else if (!sqlwcharcasecmp(W_UID, param)) *strdest= &ds->uid; else if (!sqlwcharcasecmp(W_USER, param)) *strdest= &ds->uid; else if (!sqlwcharcasecmp(W_PWD, param)) *strdest= &ds->pwd; else if (!sqlwcharcasecmp(W_PASSWORD, param)) *strdest= &ds->pwd; else if (!sqlwcharcasecmp(W_DB, param)) *strdest= &ds->database; else if (!sqlwcharcasecmp(W_DATABASE, param)) *strdest= &ds->database; else if (!sqlwcharcasecmp(W_SOCKET, param)) *strdest= &ds->socket; else if (!sqlwcharcasecmp(W_INITSTMT, param)) *strdest= &ds->initstmt; else if (!sqlwcharcasecmp(W_CHARSET, param)) *strdest= &ds->charset; else if (!sqlwcharcasecmp(W_SSLKEY, param)) *strdest= &ds->sslkey; else if (!sqlwcharcasecmp(W_SSLCERT, param)) *strdest= &ds->sslcert; else if (!sqlwcharcasecmp(W_SSLCA, param)) *strdest= &ds->sslca; else if (!sqlwcharcasecmp(W_SSLCAPATH, param)) *strdest= &ds->sslcapath; else if (!sqlwcharcasecmp(W_SSLCIPHER, param)) *strdest= &ds->sslcipher; else if (!sqlwcharcasecmp(W_PORT, param)) *intdest= &ds->port; else if (!sqlwcharcasecmp(W_SSLVERIFY, param)) *intdest= &ds->sslverify; else if (!sqlwcharcasecmp(W_READTIMEOUT, param)) *intdest= &ds->readtimeout; else if (!sqlwcharcasecmp(W_WRITETIMEOUT, param)) *intdest= &ds->writetimeout; else if (!sqlwcharcasecmp(W_CLIENT_INTERACTIVE, param)) *intdest= &ds->clientinteractive; else if (!sqlwcharcasecmp(W_FOUND_ROWS, param)) *booldest= &ds->return_matching_rows; else if (!sqlwcharcasecmp(W_BIG_PACKETS, param)) *booldest= &ds->allow_big_results; else if (!sqlwcharcasecmp(W_NO_PROMPT, param)) *booldest= &ds->dont_prompt_upon_connect; else if (!sqlwcharcasecmp(W_DYNAMIC_CURSOR, param)) *booldest= &ds->dynamic_cursor; else if (!sqlwcharcasecmp(W_NO_SCHEMA, param)) *booldest= &ds->ignore_N_in_name_table; else if (!sqlwcharcasecmp(W_NO_DEFAULT_CURSOR, param)) *booldest= &ds->user_manager_cursor; else if (!sqlwcharcasecmp(W_NO_LOCALE, param)) *booldest= &ds->dont_use_set_locale; else if (!sqlwcharcasecmp(W_PAD_SPACE, param)) *booldest= &ds->pad_char_to_full_length; else if (!sqlwcharcasecmp(W_FULL_COLUMN_NAMES, param)) *booldest= &ds->return_table_names_for_SqlDescribeCol; else if (!sqlwcharcasecmp(W_COMPRESSED_PROTO, param)) *booldest= &ds->use_compressed_protocol; else if (!sqlwcharcasecmp(W_IGNORE_SPACE, param)) *booldest= &ds->ignore_space_after_function_names; else if (!sqlwcharcasecmp(W_NAMED_PIPE, param)) *booldest= &ds->force_use_of_named_pipes; else if (!sqlwcharcasecmp(W_NO_BIGINT, param)) *booldest= &ds->change_bigint_columns_to_int; else if (!sqlwcharcasecmp(W_NO_CATALOG, param)) *booldest= &ds->no_catalog; else if (!sqlwcharcasecmp(W_USE_MYCNF, param)) *booldest= &ds->read_options_from_mycnf; else if (!sqlwcharcasecmp(W_SAFE, param)) *booldest= &ds->safe; else if (!sqlwcharcasecmp(W_NO_TRANSACTIONS, param)) *booldest= &ds->disable_transactions; else if (!sqlwcharcasecmp(W_LOG_QUERY, param)) *booldest= &ds->save_queries; else if (!sqlwcharcasecmp(W_NO_CACHE, param)) *booldest= &ds->dont_cache_result; else if (!sqlwcharcasecmp(W_FORWARD_CURSOR, param)) *booldest= &ds->force_use_of_forward_only_cursors; else if (!sqlwcharcasecmp(W_AUTO_RECONNECT, param)) *booldest= &ds->auto_reconnect; else if (!sqlwcharcasecmp(W_AUTO_IS_NULL, param)) *booldest= &ds->auto_increment_null_search; else if (!sqlwcharcasecmp(W_ZERO_DATE_TO_MIN, param)) *booldest= &ds->zero_date_to_min; else if (!sqlwcharcasecmp(W_MIN_DATE_TO_ZERO, param)) *booldest= &ds->min_date_to_zero; else if (!sqlwcharcasecmp(W_MULTI_STATEMENTS, param)) *booldest= &ds->allow_multiple_statements; else if (!sqlwcharcasecmp(W_COLUMN_SIZE_S32, param)) *booldest= &ds->limit_column_size; else if (!sqlwcharcasecmp(W_NO_BINARY_RESULT, param)) *booldest= &ds->handle_binary_as_char; else if (!sqlwcharcasecmp(W_DFLT_BIGINT_BIND_STR, param)) *booldest= &ds->default_bigint_bind_str; else if (!sqlwcharcasecmp(W_NO_I_S, param)) *booldest= &ds->no_information_schema; /* DS_PARAM */ } /* * Lookup a data source in the system. The name will be read from * the object and the rest of the details will be populated. * * If greater-than zero is returned, additional information * can be obtained from SQLInstallerError(). A less-than zero return code * indicates that the driver could not be found. */ int ds_lookup(DataSource *ds) { SQLWCHAR buf[8192]; SQLWCHAR *entries= buf; SQLWCHAR **dest; SQLWCHAR val[256]; int size, used; int rc= 0; UWORD config_mode= config_get(); unsigned int *intdest; BOOL *booldest; /* No need for SAVE_MODE() because we always call config_get() above. */ #ifdef _WIN32 /* We must do this to detect the WinXP bug mentioned below */ memset(buf, 0xff, sizeof(buf)); #endif /* get entries and check if data source exists */ if ((size= SQLGetPrivateProfileStringW(ds->name, NULL, W_EMPTY, buf, 8192, W_ODBC_INI)) < 1) { rc= -1; goto end; } RESTORE_MODE(); /* Debug code to print the entries returned, char by char */ #ifdef DEBUG_MYODBC_DS_LOOKUP { int i; char dbuf[100]; OutputDebugString("Dumping SQLGetPrivateProfileStringW result"); for (i= 0; i < size; ++i) { sprintf(dbuf, "[%d] = %wc - 0x%x\n", i, (entries[i] < 0x7f && isalpha(entries[i]) ? entries[i] : 'X'), entries[i]); OutputDebugString(dbuf); } } #endif #ifdef _WIN32 /* * In Windows XP, there is a bug in SQLGetPrivateProfileString * when mode is ODBC_BOTH_DSN and we are looking for a system * DSN. In this case SQLGetPrivateProfileString will find the * system dsn but return a corrupt list of attributes. * * See debug code above to print the exact data returned. * See also: http://support.microsoft.com/kb/909122/ */ if (config_mode == ODBC_BOTH_DSN && /* two null chars or a null and some bogus character */ *(entries + sqlwcharlen(entries)) == 0 && (*(entries + sqlwcharlen(entries) + 1) > 0x7f || *(entries + sqlwcharlen(entries) + 1) == 0)) { /* revert to system mode and try again */ config_set(ODBC_SYSTEM_DSN); if ((size= SQLGetPrivateProfileStringW(ds->name, NULL, W_EMPTY, buf, 8192, W_ODBC_INI)) < 1) { rc= -1; goto end; } } #endif for (used= 0; used < size; used += sqlwcharlen(entries) + 1, entries += sqlwcharlen(entries) + 1) { int valsize; ds_map_param(ds, entries, &dest, &intdest, &booldest); if ((valsize= SQLGetPrivateProfileStringW(ds->name, entries, W_EMPTY, val, ODBCDATASOURCE_STRLEN, W_ODBC_INI)) < 0) { rc= 1; goto end; } else if (!valsize) /* skip blanks */; else if (dest && !*dest) ds_set_strnattr(dest, val, valsize); else if (intdest) *intdest= sqlwchartoul(val, NULL); else if (booldest) *booldest= sqlwchartoul(val, NULL) > 0; else if (!sqlwcharcasecmp(W_OPTION, entries)) ds_set_options(ds, ds_get_options(ds) | sqlwchartoul(val, NULL)); RESTORE_MODE(); } end: config_set(config_mode); return rc; } /* * Read an attribute list (key/value pairs) into a data source * object. Delimiter should probably be 0 or ';'. */ int ds_from_kvpair(DataSource *ds, const SQLWCHAR *attrs, SQLWCHAR delim) { const SQLWCHAR *split; const SQLWCHAR *end; SQLWCHAR **dest; SQLWCHAR attribute[100]; int len; unsigned int *intdest; BOOL *booldest; while (*attrs) { if ((split= sqlwcharchr(attrs, (SQLWCHAR)'=')) == NULL) return 1; /* remove leading spaces on attribute */ while (*attrs == ' ') ++attrs; len = split - attrs; memcpy(attribute, attrs, len * sizeof(SQLWCHAR)); attribute[len]= 0; /* remove trailing spaces on attribute */ --len; while (attribute[len] == ' ') { attribute[len] = 0; --len; } /* remove leading and trailing spaces on value */ while (*(++split) == ' '); /* check for an "escaped" value */ if ((*split == '{' && (end= sqlwcharchr(attrs, '}')) == NULL) || /* or a delimited value */ (*split != '{' && (end= sqlwcharchr(attrs, delim)) == NULL)) /* otherwise, take the rest of the string */ end= attrs + sqlwcharlen(attrs); /* remove trailing spaces on value (not escaped part) */ len = end - split - 1; while (end > split && split[len] == ' ' && split[len+1] != '}') { --len; --end; } /* handle deprecated options as an exception */ if (!sqlwcharcasecmp(W_OPTION, attribute)) { ds_set_options(ds, sqlwchartoul(split, NULL)); } else { ds_map_param(ds, attribute, &dest, &intdest, &booldest); if (dest) { if (*split == '{' && *end == '}') { ds_set_strnattr(dest, split + 1, end - split - 1); ++end; } else ds_set_strnattr(dest, split, end - split); } else if (intdest) { /* we know we have a ; or NULL at the end so we just let it go */ *intdest= sqlwchartoul(split, NULL); } else if (booldest) { *booldest= sqlwchartoul(split, NULL) > 0; } } attrs= end; /* If delim is NULL then double-NULL is the end of key-value pairs list */ while ((delim && *attrs == delim) || *attrs == ' ') ++attrs; } return 0; } /* * Copy data source details into an attribute string. Use attrslen * to limit the number of characters placed into the string. * * Return -1 for an error or truncation, otherwise the number of * characters written. */ int ds_to_kvpair(DataSource *ds, SQLWCHAR *attrs, size_t attrslen, SQLWCHAR delim) { int i; SQLWCHAR **strval; unsigned int *intval; BOOL *boolval; int origchars= attrslen; SQLWCHAR numbuf[21]; if (!attrslen) return -1; *attrs= 0; for (i= 0; i < dsnparamcnt; ++i) { ds_map_param(ds, dsnparams[i], &strval, &intval, &boolval); /* We skip the driver if dsn name is given */ if (!sqlwcharcasecmp(W_DRIVER, dsnparams[i]) && ds->name && *ds->name) continue; if (strval && *strval && **strval) { attrs+= sqlwcharncat2(attrs, dsnparams[i], &attrslen); APPEND_SQLWCHAR(attrs, attrslen, '='); if (value_needs_escaped(*strval)) { APPEND_SQLWCHAR(attrs, attrslen, '{'); attrs+= sqlwcharncat2(attrs, *strval, &attrslen); APPEND_SQLWCHAR(attrs, attrslen, '}'); } else attrs+= sqlwcharncat2(attrs, *strval, &attrslen); APPEND_SQLWCHAR(attrs, attrslen, delim); } /* only write out int values if they're non-zero */ else if (intval && *intval) { attrs+= sqlwcharncat2(attrs, dsnparams[i], &attrslen); APPEND_SQLWCHAR(attrs, attrslen, '='); sqlwcharfromul(numbuf, *intval); attrs+= sqlwcharncat2(attrs, numbuf, &attrslen); APPEND_SQLWCHAR(attrs, attrslen, delim); } else if (boolval && *boolval) { attrs+= sqlwcharncat2(attrs, dsnparams[i], &attrslen); APPEND_SQLWCHAR(attrs, attrslen, '='); APPEND_SQLWCHAR(attrs, attrslen, '1'); APPEND_SQLWCHAR(attrs, attrslen, delim); } if (!attrslen) /* we don't have enough room */ return -1; } /* always ends in delimiter, so overwrite it */ *(attrs - 1)= 0; return origchars - attrslen; } /* * Calculate the length of serializing this DataSource to a string * including null terminator. Given in characters. */ size_t ds_to_kvpair_len(DataSource *ds) { size_t len= 0; int i; SQLWCHAR **strval; unsigned int *intval; BOOL *boolval; SQLWCHAR numbuf[21]; for (i= 0; i < dsnparamcnt; ++i) { ds_map_param(ds, dsnparams[i], &strval, &intval, &boolval); /* We skip the driver if dsn name is given */ if (!sqlwcharcasecmp(W_DRIVER, dsnparams[i]) && ds->name && *ds->name) continue; if (strval && *strval && **strval) { len+= sqlwcharlen(dsnparams[i]); len+= sqlwcharlen(*strval); if (value_needs_escaped(*strval)) len += 2; /* for escape braces */ len+= 2; /* for = and delimiter */ } else if (intval && *intval) { len+= sqlwcharlen(dsnparams[i]); sqlwcharfromul(numbuf, *intval); len+= sqlwcharlen(numbuf); len+= 2; /* for = and delimiter */ } else if (boolval && *boolval) { len+= sqlwcharlen(dsnparams[i]); len+= 3; /* for = and delimiter and '1' */ } } /* delimiter is always counted at the end, so we don't add one for the null terminator */ return len; } /* * Utility method for ds_add() to add a single string * property via the installer api. */ int ds_add_strprop(const SQLWCHAR *name, const SQLWCHAR *propname, const SQLWCHAR *propval) { /* don't write if its null or empty string */ if (propval && *propval) { BOOL rc; SAVE_MODE(); rc= SQLWritePrivateProfileStringW(name, propname, propval, W_ODBC_INI); if (rc) RESTORE_MODE(); return !rc; } return 0; } /* * Utility method for ds_add() to add a single integer * property via the installer api. */ int ds_add_intprop(const SQLWCHAR *name, const SQLWCHAR *propname, int propval) { SQLWCHAR buf[21]; sqlwcharfromul(buf, propval); return ds_add_strprop(name, propname, buf); } /* * Add the given datasource to system. Call SQLInstallerError() to get * further error details if non-zero is returned. ds->driver should be * the driver name. */ int ds_add(DataSource *ds) { Driver *driver= NULL; int rc= 1; SAVE_MODE(); /* Validate data source name */ if (!SQLValidDSNW(ds->name)) goto error; RESTORE_MODE(); /* remove if exists, FYI SQLRemoveDSNFromIni returns true * even if the dsn isnt found, false only if there is a failure */ if (!SQLRemoveDSNFromIniW(ds->name)) goto error; RESTORE_MODE(); /* Get the actual driver info (not just name) */ driver= driver_new(); memcpy(driver->name, ds->driver, (sqlwcharlen(ds->driver) + 1) * sizeof(SQLWCHAR)); if (driver_lookup(driver)) { SQLPostInstallerErrorW(ODBC_ERROR_INVALID_KEYWORD_VALUE, W_CANNOT_FIND_DRIVER); goto error; } /* "Create" section for data source */ if (!SQLWriteDSNToIniW(ds->name, driver->name)) goto error; RESTORE_MODE(); /* write all fields (util method takes care of skipping blank fields) */ if (ds_add_strprop(ds->name, W_DRIVER , driver->lib )) goto error; if (ds_add_strprop(ds->name, W_DESCRIPTION, ds->description)) goto error; if (ds_add_strprop(ds->name, W_SERVER , ds->server )) goto error; if (ds_add_strprop(ds->name, W_UID , ds->uid )) goto error; if (ds_add_strprop(ds->name, W_PWD , ds->pwd )) goto error; if (ds_add_strprop(ds->name, W_DATABASE , ds->database )) goto error; if (ds_add_strprop(ds->name, W_SOCKET , ds->socket )) goto error; if (ds_add_strprop(ds->name, W_INITSTMT , ds->initstmt )) goto error; if (ds_add_strprop(ds->name, W_CHARSET , ds->charset )) goto error; if (ds_add_strprop(ds->name, W_SSLKEY , ds->sslkey )) goto error; if (ds_add_strprop(ds->name, W_SSLCERT , ds->sslcert )) goto error; if (ds_add_strprop(ds->name, W_SSLCA , ds->sslca )) goto error; if (ds_add_strprop(ds->name, W_SSLCAPATH , ds->sslcapath )) goto error; if (ds_add_strprop(ds->name, W_SSLCIPHER , ds->sslcipher )) goto error; if (ds_add_intprop(ds->name, W_SSLVERIFY , ds->sslverify )) goto error; if (ds_add_intprop(ds->name, W_PORT , ds->port )) goto error; if (ds_add_intprop(ds->name, W_READTIMEOUT, ds->readtimeout)) goto error; if (ds_add_intprop(ds->name, W_WRITETIMEOUT, ds->writetimeout)) goto error; if (ds_add_intprop(ds->name, W_CLIENT_INTERACTIVE, ds->clientinteractive)) goto error; if (ds_add_intprop(ds->name, W_FOUND_ROWS, ds->return_matching_rows)) goto error; if (ds_add_intprop(ds->name, W_BIG_PACKETS, ds->allow_big_results)) goto error; if (ds_add_intprop(ds->name, W_NO_PROMPT, ds->dont_prompt_upon_connect)) goto error; if (ds_add_intprop(ds->name, W_DYNAMIC_CURSOR, ds->dynamic_cursor)) goto error; if (ds_add_intprop(ds->name, W_NO_SCHEMA, ds->ignore_N_in_name_table)) goto error; if (ds_add_intprop(ds->name, W_NO_DEFAULT_CURSOR, ds->user_manager_cursor)) goto error; if (ds_add_intprop(ds->name, W_NO_LOCALE, ds->dont_use_set_locale)) goto error; if (ds_add_intprop(ds->name, W_PAD_SPACE, ds->pad_char_to_full_length)) goto error; if (ds_add_intprop(ds->name, W_FULL_COLUMN_NAMES, ds->return_table_names_for_SqlDescribeCol)) goto error; if (ds_add_intprop(ds->name, W_COMPRESSED_PROTO, ds->use_compressed_protocol)) goto error; if (ds_add_intprop(ds->name, W_IGNORE_SPACE, ds->ignore_space_after_function_names)) goto error; if (ds_add_intprop(ds->name, W_NAMED_PIPE, ds->force_use_of_named_pipes)) goto error; if (ds_add_intprop(ds->name, W_NO_BIGINT, ds->change_bigint_columns_to_int)) goto error; if (ds_add_intprop(ds->name, W_NO_CATALOG, ds->no_catalog)) goto error; if (ds_add_intprop(ds->name, W_USE_MYCNF, ds->read_options_from_mycnf)) goto error; if (ds_add_intprop(ds->name, W_SAFE, ds->safe)) goto error; if (ds_add_intprop(ds->name, W_NO_TRANSACTIONS, ds->disable_transactions)) goto error; if (ds_add_intprop(ds->name, W_LOG_QUERY, ds->save_queries)) goto error; if (ds_add_intprop(ds->name, W_NO_CACHE, ds->dont_cache_result)) goto error; if (ds_add_intprop(ds->name, W_FORWARD_CURSOR, ds->force_use_of_forward_only_cursors)) goto error; if (ds_add_intprop(ds->name, W_AUTO_RECONNECT, ds->auto_reconnect)) goto error; if (ds_add_intprop(ds->name, W_AUTO_IS_NULL, ds->auto_increment_null_search)) goto error; if (ds_add_intprop(ds->name, W_ZERO_DATE_TO_MIN, ds->zero_date_to_min)) goto error; if (ds_add_intprop(ds->name, W_MIN_DATE_TO_ZERO, ds->min_date_to_zero)) goto error; if (ds_add_intprop(ds->name, W_MULTI_STATEMENTS, ds->allow_multiple_statements)) goto error; if (ds_add_intprop(ds->name, W_COLUMN_SIZE_S32, ds->limit_column_size)) goto error; if (ds_add_intprop(ds->name, W_NO_BINARY_RESULT, ds->handle_binary_as_char)) goto error; if (ds_add_intprop(ds->name, W_DFLT_BIGINT_BIND_STR, ds->default_bigint_bind_str)) goto error; if (ds_add_intprop(ds->name, W_NO_I_S, ds->no_information_schema)) goto error; /* DS_PARAM */ rc= 0; error: if (driver) driver_delete(driver); return rc; } /* * Convenience method to check if data source exists. Set the dsn * scope before calling this to narrow the check. */ int ds_exists(SQLWCHAR *name) { SQLWCHAR buf[100]; SAVE_MODE(); /* get entries and check if data source exists */ if (SQLGetPrivateProfileStringW(name, NULL, W_EMPTY, buf, 100, W_ODBC_INI)) return 0; RESTORE_MODE(); return 1; } /* * Get a copy of an attribute in UTF-8. You can use the attr8 style * pointer and it will be freed when the data source is deleted. * * ex. char *username= ds_get_utf8attr(ds->uid, &ds->uid8); */ char *ds_get_utf8attr(SQLWCHAR *attrw, SQLCHAR **attr8) { SQLINTEGER len= SQL_NTS; x_free(*attr8); *attr8= sqlwchar_as_utf8(attrw, &len); return (char *)*attr8; } /* * Assign a data source attribute from a UTF-8 string. */ int ds_setattr_from_utf8(SQLWCHAR **attr, SQLCHAR *val8) { size_t len= strlen((char *)val8); x_free(*attr); if (!(*attr= (SQLWCHAR *)my_malloc((len + 1) * sizeof(SQLWCHAR), MYF(0)))) return -1; utf8_as_sqlwchar(*attr, len, val8, len); return 0; } /* * Set DataSource member flags from deprecated options value. */ void ds_set_options(DataSource *ds, ulong options) { ds->return_matching_rows= (options & FLAG_FOUND_ROWS) > 0; ds->allow_big_results= (options & FLAG_BIG_PACKETS) > 0; ds->dont_prompt_upon_connect= (options & FLAG_NO_PROMPT) > 0; ds->dynamic_cursor= (options & FLAG_DYNAMIC_CURSOR) > 0; ds->ignore_N_in_name_table= (options & FLAG_NO_SCHEMA) > 0; ds->user_manager_cursor= (options & FLAG_NO_DEFAULT_CURSOR) > 0; ds->dont_use_set_locale= (options & FLAG_NO_LOCALE) > 0; ds->pad_char_to_full_length= (options & FLAG_PAD_SPACE) > 0; ds->return_table_names_for_SqlDescribeCol=(options & FLAG_FULL_COLUMN_NAMES) > 0; ds->use_compressed_protocol= (options & FLAG_COMPRESSED_PROTO) > 0; ds->ignore_space_after_function_names= (options & FLAG_IGNORE_SPACE) > 0; ds->force_use_of_named_pipes= (options & FLAG_NAMED_PIPE) > 0; ds->change_bigint_columns_to_int= (options & FLAG_NO_BIGINT) > 0; ds->no_catalog= (options & FLAG_NO_CATALOG) > 0; ds->read_options_from_mycnf= (options & FLAG_USE_MYCNF) > 0; ds->safe= (options & FLAG_SAFE) > 0; ds->disable_transactions= (options & FLAG_NO_TRANSACTIONS) > 0; ds->save_queries= (options & FLAG_LOG_QUERY) > 0; ds->dont_cache_result= (options & FLAG_NO_CACHE) > 0; ds->force_use_of_forward_only_cursors= (options & FLAG_FORWARD_CURSOR) > 0; ds->auto_reconnect= (options & FLAG_AUTO_RECONNECT) > 0; ds->auto_increment_null_search= (options & FLAG_AUTO_IS_NULL) > 0; ds->zero_date_to_min= (options & FLAG_ZERO_DATE_TO_MIN) > 0; ds->min_date_to_zero= (options & FLAG_MIN_DATE_TO_ZERO) > 0; ds->allow_multiple_statements= (options & FLAG_MULTI_STATEMENTS) > 0; ds->limit_column_size= (options & FLAG_COLUMN_SIZE_S32) > 0; ds->handle_binary_as_char= (options & FLAG_NO_BINARY_RESULT) > 0; ds->no_information_schema= (options & FLAG_NO_INFORMATION_SCHEMA) > 0; ds->default_bigint_bind_str= (options & FLAG_DFLT_BIGINT_BIND_STR) > 0; } /* * Get deprecated options value from DataSource member flags. */ ulong ds_get_options(DataSource *ds) { ulong options= 0; if (ds->return_matching_rows) options|= FLAG_FOUND_ROWS; if (ds->allow_big_results) options|= FLAG_BIG_PACKETS; if (ds->dont_prompt_upon_connect) options|= FLAG_NO_PROMPT; if (ds->dynamic_cursor) options|= FLAG_DYNAMIC_CURSOR; if (ds->ignore_N_in_name_table) options|= FLAG_NO_SCHEMA; if (ds->user_manager_cursor) options|= FLAG_NO_DEFAULT_CURSOR; if (ds->dont_use_set_locale) options|= FLAG_NO_LOCALE; if (ds->pad_char_to_full_length) options|= FLAG_PAD_SPACE; if (ds->return_table_names_for_SqlDescribeCol) options|= FLAG_FULL_COLUMN_NAMES; if (ds->use_compressed_protocol) options|= FLAG_COMPRESSED_PROTO; if (ds->ignore_space_after_function_names) options|= FLAG_IGNORE_SPACE; if (ds->force_use_of_named_pipes) options|= FLAG_NAMED_PIPE; if (ds->change_bigint_columns_to_int) options|= FLAG_NO_BIGINT; if (ds->no_catalog) options|= FLAG_NO_CATALOG; if (ds->read_options_from_mycnf) options|= FLAG_USE_MYCNF; if (ds->safe) options|= FLAG_SAFE; if (ds->disable_transactions) options|= FLAG_NO_TRANSACTIONS; if (ds->save_queries) options|= FLAG_LOG_QUERY; if (ds->dont_cache_result) options|= FLAG_NO_CACHE; if (ds->no_information_schema) options|= FLAG_NO_INFORMATION_SCHEMA; if (ds->force_use_of_forward_only_cursors) options|= FLAG_FORWARD_CURSOR; if (ds->auto_reconnect) options|= FLAG_AUTO_RECONNECT; if (ds->auto_increment_null_search) options|= FLAG_AUTO_IS_NULL; if (ds->zero_date_to_min) options|= FLAG_ZERO_DATE_TO_MIN; if (ds->min_date_to_zero) options|= FLAG_MIN_DATE_TO_ZERO; if (ds->allow_multiple_statements) options|= FLAG_MULTI_STATEMENTS; if (ds->limit_column_size) options|= FLAG_COLUMN_SIZE_S32; if (ds->handle_binary_as_char) options|= FLAG_NO_BINARY_RESULT; if (ds->default_bigint_bind_str) options|= FLAG_DFLT_BIGINT_BIND_STR; return options; } mysql-connector-odbc-5.1.10-src/util/unicode_transcode.c100644 15766 12 12747 11707541005 22032 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file unicode_transcode.c @brief Unicode transcoding functions. Raw conversions. */ #ifndef ODBCTAP # include "stringutil.h" #endif /** Convert UTF-16 code unit(s) to a UTF-32 character. For characters in the Basic Multilingual Plane, one UTF-16 code unit maps to one UTF-32 character, but characters in other planes may require two UTF-16 code units. @param[in] i Pointer to UTF-16 code units @param[in] u Pointer to UTF-32 character @return Number of UTF-16 code units consumed. */ int utf16toutf32(UTF16 *i, UTF32 *u) { if (*i >= 0xd800 && *i <= 0xdbff) { *u= 0x10000 | ((*i++ & 0x3ff) << 10); if (*i < 0xdc00 || *i > 0xdfff) /* invalid */ return 0; *u|= *i & 0x3ff; return 2; } else { *u= *i; return 1; } } /** Convert UTF-32 character to UTF-16 code unit(s). @param[in] i UTF-32 character @param[in] u Pointer to UTF-16 code units @return Number of UTF-16 code units produced. */ int utf32toutf16(UTF32 i, UTF16 *u) { if (i < 0xffff) { *u= (UTF16)(i & 0xffff); return 1; } else if(i < 0x10ffff) { i-= 0x10000; *u++= 0xd800 | (i >> 10); *u= 0xdc00 | (i & 0x3ff); return 2; } return 0; } /** Convert UTF-8 octets to a UTF-32 character. It may take up to four UTF-8 octets to encode one UTF-32 character. @param[in] i Pointer to UTF-8 octets @param[in] u Pointer to UTF-32 character @return Number of UTF-8 octets consumed, or 0 if an invalid character was encountered. */ int utf8toutf32(UTF8 *i, UTF32 *u) { int len, x; if (*i < 0x80) { *u= *i; return 1; } else if (*i < 0xe0) { len= 2; *u= *i & 0x1f; } else if (*i < 0xf0) { len= 3; *u= *i & 0x0f; } else { len= 4; *u= *i & 0x07; } x= len; while (--x) { *u<<= 6; *u|= *++i & 0x3f; if (*i >> 6 != 2) /* invalid */ return 0; } return len; } /** Convert a UTF-32 character into UTF-8 octets. It may take four UTF-8 octets to encode one UTF-32 character. @param[in] i UTF-32 characer @param[in] u Pointer to UTF-8 octets @return Number of UTF-8 octets produced. */ int utf32toutf8(UTF32 i, UTF8 *c) { int len= 0, x; if (i < 0x80) { *c= (UTF8)(i & 0x7f); return 1; } else if (i < 0x800) { *c++= (3 << 6) | (i >> 6); len= 2; } else if (i < 0x10000) { *c++= (7 << 5) | (i >> 12); len= 3; } else if (i < 0x10ffff) { *c++= (0xf << 4) | (i >> 18); len= 4; } x= len; if (x) while (--x) { *c++= (1 << 7) | ((i >> (6 * (x - 1))) & 0x3f); } return len; } #ifdef UCTEST #include #include #include typedef struct { UTF8 u8[4]; UTF32 u32; int cnt; } t_8_32; typedef struct { UTF16 u16[2]; UTF32 u32; int cnt; } t_16_32; void t1() { int i, j; t_8_32 t1[]= { {{0, 0, 0, 0}, 0, 1}, {{0x3c, 0, 0, 0}, 0x3c, 1}, {{0xc3, 0xbe, 0, 0}, 0xfe, 2}, {{0xe0, 0xa4, 0x96, 0}, 0x916, 3}, {{0xf0, 0x90, 0x85, 0xad}, 0x1016d, 4} }; printf("***** T1 -> utf32<->utf8 *****\n"); for (i= 0; i < sizeof(t1) / sizeof(t_8_32); ++i) { int cnt; t_8_32 t= t1[i]; UTF8 res[4]; UTF32 resu; memset(res, 0, 4); printf("Convert %x\n", t.u32); cnt= utf32toutf8(t.u32, res); assert(cnt == t.cnt); for (j= 0; j < 4; ++j) { printf("Res[%d] = 0x%x (expect 0x%x)\n", j, res[j], t.u8[j]); assert(res[j] == t.u8[j]); } printf("Ok. Now back\n"); cnt= utf8toutf32(t.u8, &resu); printf("ResU = %x\n", resu); assert(cnt == t.cnt); assert(resu == t.u32); } } void t2() { int i, j; t_16_32 t1[]= { {{0, 0}, 0, 1}, {{0x7a, 0}, 0x7a, 1}, {{0x6c34, 0}, 0x6c34, 1}, {{0xd834, 0xdd1e}, 0x1d11e, 2} }; printf("***** T2 -> utf32<->utf16 *****\n"); for (i= 0; i < sizeof(t1) / sizeof(t_16_32); ++i) { int cnt; t_16_32 t= t1[i]; UTF16 res[2]; UTF32 resu; memset(res, 0, 2 * 2); printf("Convert %x\n", t.u32); cnt= utf32toutf16(t.u32, res); assert(cnt == t.cnt); for (j = 0; j < 2; ++j) { printf("Res[%d] = 0x%x (expect 0x%x)\n", j, res[j], t.u16[j]); assert(res[j] == t.u16[j]); } printf("Ok. Now back\n"); cnt= utf16toutf32(t.u16, &resu); printf("ResU = %x\n", resu); assert(cnt == t.cnt); assert(resu == t.u32); } } int main(int argc, char **argv) { t1(); t2(); exit(0); } #endif /* UCTEST */ mysql-connector-odbc-5.1.10-src/util/installer.h100644 15766 12 16146 11707541005 20341 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Function prototypes and structures for installer-wrapper functionality. */ #ifndef _INSTALLER_H #define _INSTALLER_H #include "../MYODBC_CONF.h" #include "../MYODBC_ODBC.h" #ifdef __cplusplus extern "C" { #endif /* the different modes used when calling MYODBCSetupDataSourceConfig */ #define CONFIG_ADD 1 #define CONFIG_EDIT 2 #define CONFIG_VIEW 3 #define CONFIG_DRIVER_CONNECT 4 UWORD config_get(); UWORD config_set(UWORD mode); typedef struct { SQLWCHAR *name; SQLWCHAR *lib; SQLWCHAR *setup_lib; SQLCHAR *name8; SQLCHAR *lib8; SQLCHAR *setup_lib8; } Driver; /* SQL_MAX_OPTION_STRING_LENGTH = 256, should be ok */ #define ODBCDRIVER_STRLEN SQL_MAX_OPTION_STRING_LENGTH #define ODBCDATASOURCE_STRLEN SQL_MAX_OPTION_STRING_LENGTH Driver *driver_new(); void driver_delete(Driver *driver); int driver_lookup_name(Driver *driver); int driver_lookup(Driver *driver); int driver_from_kvpair_semicolon(Driver *driver, const SQLWCHAR *attrs); int driver_to_kvpair_null(Driver *driver, SQLWCHAR *attrs, size_t attrslen); typedef struct { SQLWCHAR *name; SQLWCHAR *driver; /* driver filename */ SQLWCHAR *description; SQLWCHAR *server; SQLWCHAR *uid; SQLWCHAR *pwd; SQLWCHAR *database; SQLWCHAR *socket; SQLWCHAR *initstmt; SQLWCHAR *charset; SQLWCHAR *sslkey; SQLWCHAR *sslcert; SQLWCHAR *sslca; SQLWCHAR *sslcapath; SQLWCHAR *sslcipher; unsigned int port; unsigned int readtimeout; unsigned int writetimeout; unsigned int clientinteractive; SQLCHAR *name8; SQLCHAR *driver8; SQLCHAR *description8; SQLCHAR *server8; SQLCHAR *uid8; SQLCHAR *pwd8; SQLCHAR *database8; SQLCHAR *socket8; SQLCHAR *initstmt8; SQLCHAR *charset8; SQLCHAR *sslkey8; SQLCHAR *sslcert8; SQLCHAR *sslca8; SQLCHAR *sslcapath8; SQLCHAR *sslcipher8; /* flags 1 */ BOOL return_matching_rows; BOOL allow_big_results; BOOL use_compressed_protocol; BOOL change_bigint_columns_to_int; BOOL safe; BOOL auto_reconnect; BOOL auto_increment_null_search; BOOL handle_binary_as_char; /* flags 2 */ BOOL dont_prompt_upon_connect; BOOL dynamic_cursor; BOOL ignore_N_in_name_table; BOOL user_manager_cursor; BOOL dont_use_set_locale; BOOL pad_char_to_full_length; BOOL dont_cache_result; /* flags 3 */ BOOL return_table_names_for_SqlDescribeCol; BOOL ignore_space_after_function_names; BOOL force_use_of_named_pipes; BOOL no_catalog; BOOL read_options_from_mycnf; BOOL disable_transactions; BOOL force_use_of_forward_only_cursors; BOOL allow_multiple_statements; BOOL limit_column_size; BOOL min_date_to_zero; BOOL zero_date_to_min; BOOL default_bigint_bind_str; /* debug */ BOOL save_queries; BOOL no_information_schema; /* SSL */ unsigned int sslverify; } DataSource; typedef struct{ SQLCHAR *type_name; SQLINTEGER name_length; SQLSMALLINT sql_type; SQLSMALLINT mysql_type; SQLUINTEGER type_length; BOOL binary; }SQLTypeMap; #define TYPE_MAP_SIZE 32 DataSource *ds_new(); void ds_delete(DataSource *ds); int ds_set_strattr(SQLWCHAR **attr, const SQLWCHAR *val); int ds_set_strnattr(SQLWCHAR **attr, const SQLWCHAR *val, size_t charcount); int ds_lookup(DataSource *ds); int ds_from_kvpair(DataSource *ds, const SQLWCHAR *attrs, SQLWCHAR delim); int ds_to_kvpair(DataSource *ds, SQLWCHAR *attrs, size_t attrslen, SQLWCHAR delim); size_t ds_to_kvpair_len(DataSource *ds); int ds_add(DataSource *ds); int ds_exists(SQLWCHAR *name); char *ds_get_utf8attr(SQLWCHAR *attrw, SQLCHAR **attr8); int ds_setattr_from_utf8(SQLWCHAR **attr, SQLCHAR *val8); void ds_set_options(DataSource *ds, ulong options); ulong ds_get_options(DataSource *ds); extern const SQLWCHAR W_DRIVER_PARAM[]; extern const SQLWCHAR W_DRIVER_NAME[]; extern const SQLWCHAR W_INVALID_ATTR_STR[]; /* * Deprecated connection parameters */ #define FLAG_FOUND_ROWS 2 /* Access can't handle affected_rows */ #define FLAG_BIG_PACKETS 8 /* Allow BIG packets. */ #define FLAG_NO_PROMPT 16 /* Don't prompt on connection */ #define FLAG_DYNAMIC_CURSOR 32 /* Enables the dynamic cursor */ #define FLAG_NO_SCHEMA 64 /* Ignore the schema defination */ #define FLAG_NO_DEFAULT_CURSOR 128 /* No default cursor */ #define FLAG_NO_LOCALE 256 /* No locale specification */ #define FLAG_PAD_SPACE 512 /* Pad CHAR:s with space to max length */ #define FLAG_FULL_COLUMN_NAMES 1024 /* Extends SQLDescribeCol */ #define FLAG_COMPRESSED_PROTO 2048 /* Use compressed protocol */ #define FLAG_IGNORE_SPACE 4096 /* Ignore spaces after function names */ #define FLAG_NAMED_PIPE 8192 /* Force use of named pipes */ #define FLAG_NO_BIGINT 16384 /* Change BIGINT to INT */ #define FLAG_NO_CATALOG 32768 /* No catalog support */ #define FLAG_USE_MYCNF 65536L /* Read my.cnf at start */ #define FLAG_SAFE 131072L /* Try to be as safe as possible */ #define FLAG_NO_TRANSACTIONS (FLAG_SAFE << 1) /* Disable transactions */ #define FLAG_LOG_QUERY (FLAG_SAFE << 2) /* Query logging, debug */ #define FLAG_NO_CACHE (FLAG_SAFE << 3) /* Don't cache the resultset */ /* Force use of forward-only cursors */ #define FLAG_FORWARD_CURSOR (FLAG_SAFE << 4) /* Force auto-reconnect */ #define FLAG_AUTO_RECONNECT (FLAG_SAFE << 5) #define FLAG_AUTO_IS_NULL (FLAG_SAFE << 6) /* 8388608 Enables SQL_AUTO_IS_NULL */ #define FLAG_ZERO_DATE_TO_MIN (1 << 24) /* Convert XXXX-00-00 date to ODBC min date on results */ #define FLAG_MIN_DATE_TO_ZERO (1 << 25) /* Convert ODBC min date to 0000-00-00 on query */ #define FLAG_MULTI_STATEMENTS (1 << 26) /* Allow multiple statements in a query */ #define FLAG_COLUMN_SIZE_S32 (1 << 27) /* Limit column size to a signed 32-bit value (automatically set for ADO) */ #define FLAG_NO_BINARY_RESULT (1 << 28) /* Disables charset 63 for columns with empty org_table */ /* When binding SQL_BIGINT as SQL_C_DEFAULT, treat it as a string (automatically set for MS Access) see bug#24535 */ #define FLAG_DFLT_BIGINT_BIND_STR (1 << 29) /* Use SHOW TABLE STATUS instead Information_Schema DB for table metadata */ #define FLAG_NO_INFORMATION_SCHEMA (1 << 30) #ifdef __cplusplus } #endif #endif /* _INSTALLER_H */ mysql-connector-odbc-5.1.10-src/util/stringutil.h100644 15766 12 7751 11707541005 20532 0ustar00cteamstaff/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file stringutil.c @brief Function prototypes from stringutil.c. */ #ifndef _STRINGUTIL_H #define _STRINGUTIL_H #ifdef __cplusplus extern "C" { #endif #include "../MYODBC_MYSQL.h" #include #include #include #ifndef x_free # if MYSQL_VERSION_ID >= 50500 # define x_free(A) { void *tmp= (A); if (tmp) my_free((char *) tmp); } # else # define x_free(A) { void *tmp= (A); if (tmp) my_free((char *) tmp,MYF(MY_WME+MY_FAE)); } # endif #endif #define myodbc_min(a, b) ((a) < (b) ? (a) : (b)) #define myodbc_max(a, b) ((a) > (b) ? (a) : (b)) #define MAX_BYTES_PER_UTF8_CP 4 /* max 4 bytes per utf8 codepoint */ /* Unicode transcoding */ typedef unsigned int UTF32; typedef unsigned short UTF16; typedef unsigned char UTF8; extern CHARSET_INFO *utf8_charset_info; /** Determine whether a charset number represents a UTF-8 collation. */ #define is_utf8_charset(number) \ (number == 33 || number == 83 || \ (number >= 192 && number <= 211) || number == 253 || \ number == 45 || number == 46 || \ (number >= 224 && number <= 243)) int utf16toutf32(UTF16 *i, UTF32 *u); int utf32toutf16(UTF32 i, UTF16 *u); int utf8toutf32(UTF8 *i, UTF32 *u); int utf32toutf8(UTF32 i, UTF8 *c); /* Conversions */ SQLWCHAR *sqlchar_as_sqlwchar(CHARSET_INFO *charset_info, SQLCHAR *str, SQLINTEGER *len, uint *errors); SQLCHAR *sqlwchar_as_sqlchar(CHARSET_INFO *charset_info, SQLWCHAR *str, SQLINTEGER *len, uint *errors); SQLCHAR *sqlwchar_as_utf8(const SQLWCHAR *str, SQLINTEGER *len); SQLSMALLINT utf8_as_sqlwchar(SQLWCHAR *out, SQLINTEGER out_max, SQLCHAR *in, SQLINTEGER in_len); SQLCHAR *sqlchar_as_sqlchar(CHARSET_INFO *from_charset, CHARSET_INFO *to_charset, SQLCHAR *str, SQLINTEGER *len, uint *errors); SQLINTEGER sqlwchar_as_sqlchar_buf(CHARSET_INFO *charset_info, SQLCHAR *out, SQLINTEGER out_bytes, SQLWCHAR *str, SQLINTEGER len, uint *errors); uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, const char *from, uint32 from_length, CHARSET_INFO *from_cs, uint32 *used_bytes, uint32 *used_chars, uint *errors); /* wcs* replacements */ int sqlwcharcasecmp(const SQLWCHAR *s1, const SQLWCHAR *s2); const SQLWCHAR *sqlwcharchr(const SQLWCHAR *wstr, SQLWCHAR wchr); size_t sqlwcharlen(const SQLWCHAR *wstr); SQLWCHAR *sqlwchardup(const SQLWCHAR *wstr, size_t charlen); unsigned long sqlwchartoul(const SQLWCHAR *wstr, const SQLWCHAR **endptr); void sqlwcharfromul(SQLWCHAR *wstr, unsigned long v); size_t sqlwcharncat2(SQLWCHAR *dest, const SQLWCHAR *src, size_t *n); SQLWCHAR *sqlwcharncpy(SQLWCHAR *dest, const SQLWCHAR *src, size_t n); char * myodbc_strlwr(char *target, size_t len); #ifdef __cplusplus } #endif #endif /* _STRINGUTIL_H */ mysql-connector-odbc-5.1.10-src/util/stringutil.c100644 15766 12 37100 11707541005 20534 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file stringutil.c @brief String utility functions, mostly focused on SQLWCHAR and charset manipulations. */ #include "stringutil.h" CHARSET_INFO *utf8_charset_info= NULL; /** Duplicate a SQLCHAR in the specified character set as a SQLWCHAR. @param[in] charset_info Character set to convert into @param[in] str String to convert @param[in,out] len Pointer to length of source (in bytes) or destination string (in chars) @param[out] errors Pointer to count of errors in conversion @return Pointer to a newly allocated SQLWCHAR, or @c NULL */ SQLWCHAR *sqlchar_as_sqlwchar(CHARSET_INFO *charset_info, SQLCHAR *str, SQLINTEGER *len, uint *errors) { SQLCHAR *pos, *str_end; SQLWCHAR *out; SQLINTEGER i, out_bytes; my_bool free_str= FALSE; if (str && *len == SQL_NTS) *len= strlen((char *)str); if (!str || *len == 0) { *len= 0; return NULL; } if (!is_utf8_charset(charset_info->number)) { uint32 used_bytes, used_chars; size_t u8_max= (*len / charset_info->mbminlen * utf8_charset_info->mbmaxlen + 1); SQLCHAR *u8= (SQLCHAR *)my_malloc(u8_max, MYF(0)); if (!u8) { *len= -1; return NULL; } *len= copy_and_convert((char *)u8, u8_max, utf8_charset_info, (char *)str, *len, charset_info, &used_bytes, &used_chars, errors); str= u8; free_str= TRUE; } str_end= str + *len; out_bytes= (*len + 1) * sizeof(SQLWCHAR); out= (SQLWCHAR *)my_malloc(out_bytes, MYF(0)); if (!out) { *len= -1; return NULL; } for (pos= str, i= 0; *pos && pos < str_end; ) { if (sizeof(SQLWCHAR) == 4) { int consumed= utf8toutf32(pos, (UTF32 *)(out + i++)); pos+= consumed; if (!consumed) { *errors+= 1; break; } } else { UTF32 u32; int consumed= utf8toutf32(pos, &u32); pos+= consumed; if (!consumed) { *errors+= 1; break; } i+= utf32toutf16(u32, (UTF16 *)(out + i)); } } *len= i; out[i]= 0; if (free_str) x_free(str); return out; } /** Duplicate a SQLWCHAR as a SQLCHAR in the specified character set. @param[in] charset_info Character set to convert into @param[in] str String to convert @param[in,out] len Pointer to length of source (in chars) or destination string (in bytes) @param[out] errors Pointer to count of errors in conversion @return Pointer to a newly allocated SQLCHAR, or @c NULL */ SQLCHAR *sqlwchar_as_sqlchar(CHARSET_INFO *charset_info, SQLWCHAR *str, SQLINTEGER *len, uint *errors) { SQLWCHAR *str_end; SQLCHAR *out; SQLINTEGER i, u8_len, out_bytes; UTF8 u8[MAX_BYTES_PER_UTF8_CP + 1]; uint32 used_bytes, used_chars; *errors= 0; if (is_utf8_charset(charset_info->number)) return sqlwchar_as_utf8(str, len); if (*len == SQL_NTS) *len= sqlwcharlen(str); if (!str || *len == 0) { *len= 0; return NULL; } out_bytes= *len * charset_info->mbmaxlen * sizeof(SQLCHAR) + 1; out= (SQLCHAR *)my_malloc(out_bytes, MYF(0)); if (!out) { *len= -1; return NULL; } str_end= str + *len; for (i= 0; str < str_end; ) { if (sizeof(SQLWCHAR) == 4) { u8_len= utf32toutf8((UTF32)*str++, u8); } else { UTF32 u32; int consumed= utf16toutf32((UTF16 *)str, &u32); str+= consumed; if (!consumed) { *errors+= 1; break; } u8_len= utf32toutf8(u32, u8); } i+= copy_and_convert((char *)out + i, out_bytes - i, charset_info, (char *)u8, u8_len, utf8_charset_info, &used_bytes, &used_chars, errors); } *len= i; out[i]= '\0'; return out; } /** Duplicate a SQLWCHAR as a SQLCHAR encoded as UTF-8. @param[in] str String to convert @param[in,out] len Pointer to length of source (in chars) or destination string (in bytes) @return Pointer to a newly allocated SQLCHAR, or @c NULL */ SQLCHAR *sqlwchar_as_utf8(const SQLWCHAR *str, SQLINTEGER *len) { const SQLWCHAR *str_end; UTF8 *u8; SQLINTEGER i; if (*len == SQL_NTS) *len= sqlwcharlen(str); if (!str || *len == 0) { *len= 0; return NULL; } u8= (UTF8 *)my_malloc(sizeof(UTF8) * MAX_BYTES_PER_UTF8_CP * *len + 1, MYF(0)); if (!u8) { *len= -1; return NULL; } str_end= str + *len; if (sizeof(SQLWCHAR) == 4) { for (i= 0; str < str_end; ) i+= utf32toutf8((UTF32)*str++, u8 + i); } else { for (i= 0; str < str_end; ) { UTF32 u32; int consumed= utf16toutf32((UTF16 *)str, &u32); str+= consumed; if (!consumed) break; i+= utf32toutf8(u32, u8 + i); } } *len= i; u8[i]= '\0'; return u8; } /** Convert a SQLCHAR encoded as UTF-8 into a SQLWCHAR. @param[out] out Pointer to SQLWCHAR buffer @param[in] out_max Length of @c out buffer @param[in] in Pointer to SQLCHAR string (utf-8 encoded) @param[in] in_len Length of @c in (in bytes) @return Number of characters stored in the @c out buffer */ SQLSMALLINT utf8_as_sqlwchar(SQLWCHAR *out, SQLINTEGER out_max, SQLCHAR *in, SQLINTEGER in_len) { SQLINTEGER i; SQLWCHAR *pos, *out_end; for (i= 0, pos= out, out_end= out + out_max; i < in_len && pos < out_end; ) { if (sizeof(SQLWCHAR) == 4) { int consumed= utf8toutf32(in + i, (UTF32 *)pos++); i+= consumed; if (!consumed) break; } else { UTF32 u32; int consumed= utf8toutf32(in + i, &u32); i+= consumed; if (!consumed) break; pos+= utf32toutf16(u32, (UTF16 *)pos); } } if (pos) *pos= 0; return pos - out; } /** Duplicate a SQLCHAR as a SQLCHAR in the specified character set. @param[in] from_charset Character set to convert from @param[in] to_charset Character set to convert into @param[in] str String to convert @param[in,out] len Pointer to length of source (in chars) or destination string (in bytes) @param[out] errors Pointer to count of errors in conversion @return Pointer to a newly allocated SQLCHAR, or @c NULL */ SQLCHAR *sqlchar_as_sqlchar(CHARSET_INFO *from_charset, CHARSET_INFO *to_charset, SQLCHAR *str, SQLINTEGER *len, uint *errors) { uint32 used_bytes, used_chars, bytes; SQLCHAR *conv; if (*len == SQL_NTS) *len= strlen((char *)str); bytes= (*len / from_charset->mbminlen * to_charset->mbmaxlen); conv= (SQLCHAR *)my_malloc(bytes + 1, MYF(0)); if (!conv) { *len= -1; return NULL; } *len= copy_and_convert((char *)conv, bytes, to_charset, (char *)str, *len, from_charset, &used_bytes, &used_chars, errors); conv[*len]= '\0'; return conv; } /** Convert a SQLWCHAR to a SQLCHAR in the specified character set. This variation uses a pre-allocated buffer. @param[in] charset_info Character set to convert into @param[out] out Pointer to SQLWCHAR buffer @param[in] out_bytes Length of @c out buffer @param[in] str String to convert @param[in] len Length of @c in (in SQLWCHAR's) @param[out] errors Pointer to count of errors in conversion @return Number of characters stored in the @c out buffer */ SQLINTEGER sqlwchar_as_sqlchar_buf(CHARSET_INFO *charset_info, SQLCHAR *out, SQLINTEGER out_bytes, SQLWCHAR *str, SQLINTEGER len, uint *errors) { SQLWCHAR *str_end; SQLINTEGER i, u8_len; UTF8 u8[MAX_BYTES_PER_UTF8_CP + 1]; uint32 used_bytes, used_chars; *errors= 0; if (len == SQL_NTS) len= sqlwcharlen(str); if (!str || len == 0) return 0; str_end= str + len; for (i= 0; str < str_end; ) { if (sizeof(SQLWCHAR) == 4) { u8_len= utf32toutf8((UTF32)*str++, u8); } else { UTF32 u32; int consumed= utf16toutf32((UTF16 *)str, &u32); str+= consumed; if (!consumed) { *errors+= 1; break; } u8_len= utf32toutf8(u32, u8); } i+= copy_and_convert((char *)out + i, out_bytes - i, charset_info, (char *)u8, u8_len, utf8_charset_info, &used_bytes, &used_chars, errors); } out[i]= '\0'; return i; } /** Copy a string from one character set to another. Taken from sql_string.cc in the MySQL Server source code, since we don't export this functionality in libmysql yet. @c to must be at least as big as @c from_length * @c to_cs->mbmaxlen @param[in,out] to Store result here @param[in] to_cs Character set of result string @param[in] from Copy from here @param[in] from_length Length of string in @c from (in bytes) @param[in] from_cs Character set of string in @c from @param[out] used_bytes Buffer for returning number of bytes consumed from @c from @param[out] used_chars Buffer for returning number of chars consumed from @c from @param[in,out] errors Pointer to value where number of errors encountered is added. @retval Length of bytes copied to @c to */ uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, const char *from, uint32 from_length, CHARSET_INFO *from_cs, uint32 *used_bytes, uint32 *used_chars, uint *errors) { int from_cnvres, to_cnvres; my_wc_t wc; const uchar *from_end= (const uchar*) from+from_length; char *to_start= to; uchar *to_end= (uchar*) to+to_length; int (*mb_wc)(struct charset_info_st *, my_wc_t *, const uchar *, const uchar *)= from_cs->cset->mb_wc; int (*wc_mb)(struct charset_info_st *, my_wc_t, uchar *s, uchar *e)= to_cs->cset->wc_mb; uint error_count= 0; *used_bytes= *used_chars= 0; while (1) { if ((from_cnvres= (*mb_wc)(from_cs, &wc, (uchar*) from, from_end)) > 0) from+= from_cnvres; else if (from_cnvres == MY_CS_ILSEQ) { ++error_count; ++from; wc= '?'; } else if (from_cnvres > MY_CS_TOOSMALL) { /* A correct multibyte sequence detected But it doesn't have Unicode mapping. */ ++error_count; from+= (-from_cnvres); wc= '?'; } else break; /* Not enough characters */ outp: if ((to_cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0) to+= to_cnvres; else if (to_cnvres == MY_CS_ILUNI && wc != '?') { ++error_count; wc= '?'; goto outp; } else break; *used_bytes+= from_cnvres; *used_chars+= 1; } if (errors) *errors+= error_count; return (uint32) (to - to_start); } /* * Compare two SQLWCHAR strings ignoring case. This is only * case-insensitive over the ASCII range of characters. * * @return 0 if the strings are the same, 1 if they are not. */ int sqlwcharcasecmp(const SQLWCHAR *s1, const SQLWCHAR *s2) { SQLWCHAR c1, c2; while (*s1 && *s2) { c1= *s1; c2= *s2; /* capitalize both strings */ if (c1 >= 'a') c1 -= ('a' - 'A'); if (c2 >= 'a') c2 -= ('a' - 'A'); if (c1 != c2) return 1; ++s1; ++s2; } /* one will be null, so both must be */ return *s1 != *s2; } /* * Locate a SQLWCHAR in a SQLWCHAR string. * * @return Position of char if found, otherwise NULL. */ const SQLWCHAR *sqlwcharchr(const SQLWCHAR *wstr, SQLWCHAR wchr) { while (*wstr) { if (*wstr == wchr) { return wstr; } ++wstr; } return NULL; } /* * Calculate the length of a SQLWCHAR string. * * @return The number of SQLWCHAR units in the string. */ size_t sqlwcharlen(const SQLWCHAR *wstr) { size_t len= 0; while (wstr && *wstr++) ++len; return len; } /* * Duplicate a SQLWCHAR string. Memory is allocated with my_malloc() * and should be freed with my_free() or the x_free() macro. * * @return A pointer to a new string. */ SQLWCHAR *sqlwchardup(const SQLWCHAR *wstr, size_t charlen) { size_t chars= charlen == SQL_NTS ? sqlwcharlen(wstr) : charlen; SQLWCHAR *res= (SQLWCHAR *)my_malloc((chars + 1) * sizeof(SQLWCHAR), MYF(0)); if (!res) return NULL; memcpy(res, wstr, chars * sizeof(SQLWCHAR)); res[chars]= 0; return res; } /* * Convert a SQLWCHAR string to a long integer. * * @return The integer result of the conversion or 0 if the * string could not be parsed. */ unsigned long sqlwchartoul(const SQLWCHAR *wstr, const SQLWCHAR **endptr){ unsigned long res= 0; SQLWCHAR c; if (!wstr) return 0; while (c= *wstr) { if (c < '0' || c > '9') break; res*= 10; res+= c - '0'; ++wstr; } if (endptr) *endptr= wstr; return res; } /* * Convert a long integer to a SQLWCHAR string. */ void sqlwcharfromul(SQLWCHAR *wstr, unsigned long v) { int chars; unsigned long v1; for(chars= 0, v1= v; v1 > 0; ++chars, v1 /= 10); wstr[chars]= (SQLWCHAR)0; for(v1= v; v1 > 0; v1 /= 10) wstr[--chars]= (SQLWCHAR)('0' + (v1 % 10)); } /* * Concatenate two strings. This differs from the convential * strncat() in that the parameter n is reduced by the number * of characters used. * * Returns the number of characters copied. */ size_t sqlwcharncat2(SQLWCHAR *dest, const SQLWCHAR *src, size_t *n) { SQLWCHAR *orig_dest; if (!n || !*n) return 0; orig_dest= (dest += sqlwcharlen(dest)); while (*src && *n && (*n)--) *dest++= *src++; if (*n) *dest= 0; else *(dest - 1)= 0; return dest - orig_dest; } /* * Copy up to 'n' characters (including NULL) from src to dest. */ SQLWCHAR *sqlwcharncpy(SQLWCHAR *dest, const SQLWCHAR *src, size_t n) { if (!dest || !src) return NULL; while (*src && n--) *dest++= *src++; if (n) *dest= 0; else *(dest - 1)= 0; return dest; } /* Converts all characters in string to lowercase */ char * myodbc_strlwr(char *target, size_t len) { unsigned char *c= (unsigned char *)target; if (len <= 0) len= strlen(target); while (len-- > 0) *c++= tolower(*c); return target; } mysql-connector-odbc-5.1.10-src/util/odbcinstw.c100644 15766 12 16566 11707541005 20341 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file odbcinstw.c @brief Installer API Unicode wrapper functions. unixODBC 2.2.11 does not include the Unicode versions of the ODBC installer API. As of November 2007, this was still the current version shipped with Debian and Ubuntu Linux. SQLGetPrivateProfileString() also has a few bugs in unixODBC 2.2.11 that our version of SQLGetPrivateProfileStringW() will work around. */ #include "stringutil.h" #include "../MYODBC_CONF.h" #include "../MYODBC_ODBC.h" #include #include #define INSTAPI #ifndef FALSE # define FALSE 0 #endif #ifdef HAVE_LPCWSTR # define MyODBC_LPCWSTR LPCWSTR #else # define MyODBC_LPCWSTR LPWSTR #endif #if !defined(HAVE_SQLGETPRIVATEPROFILESTRINGW) || defined(USE_UNIXODBC) int INSTAPI MySQLGetPrivateProfileStringW(const MyODBC_LPCWSTR lpszSection, const MyODBC_LPCWSTR lpszEntry, const MyODBC_LPCWSTR lpszDefault, LPWSTR lpszRetBuffer, int cbRetBuffer, const MyODBC_LPCWSTR lpszFilename) { SQLINTEGER len; int rc; char *section, *entry, *def, *ret, *filename; len= SQL_NTS; section= (char *)sqlwchar_as_utf8(lpszSection, &len); len= SQL_NTS; entry= (char *)sqlwchar_as_utf8(lpszEntry, &len); len= SQL_NTS; def= (char *)sqlwchar_as_utf8(lpszDefault, &len); len= SQL_NTS; filename= (char *)sqlwchar_as_utf8(lpszFilename, &len); if (lpszRetBuffer && cbRetBuffer) ret= malloc(cbRetBuffer + 1); else ret= NULL; /* unixODBC 2.2.11 can't handle NULL for default, so pass "" instead. */ rc= SQLGetPrivateProfileString(section, entry, def ? def : "", ret, cbRetBuffer, filename); if (rc > 0 && lpszRetBuffer) { /* unixODBC 2.2.11 returns the wrong value from SQLGetPrivateProfileString when getting the list of sections or entries in a section, so we have to re-calculate the correct length by walking the list of values. */ if (!entry || !section) { char *pos= ret; while (*pos && pos < ret + cbRetBuffer) pos+= strlen(pos) + 1; rc= pos - ret; } /** @todo error handling */ utf8_as_sqlwchar(lpszRetBuffer, cbRetBuffer, (SQLCHAR *)ret, rc); } x_free(section); x_free(entry); x_free(def); x_free(ret); x_free(filename); return rc; } #endif #ifndef HAVE_SQLGETPRIVATEPROFILESTRINGW int INSTAPI SQLGetPrivateProfileStringW(const MyODBC_LPCWSTR lpszSection, const MyODBC_LPCWSTR lpszEntry, const MyODBC_LPCWSTR lpszDefault, LPWSTR lpszRetBuffer, int cbRetBuffer, const MyODBC_LPCWSTR lpszFilename) { return MySQLGetPrivateProfileStringW(lpszSection, lpszEntry, lpszDefault, lpszRetBuffer, cbRetBuffer, lpszFilename); } /** The version of iODBC that shipped with Mac OS X 10.4 does not implement the Unicode versions of various installer API functions, so we have to do it ourselves. */ BOOL INSTAPI SQLInstallDriverExW(const MyODBC_LPCWSTR lpszDriver, const MyODBC_LPCWSTR lpszPathIn, LPWSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount) { const SQLWCHAR *pos; SQLINTEGER len; BOOL rc; char *driver, *pathin, *pathout; WORD out; if (!pcbPathOut) pcbPathOut= &out; /* Calculate length of double-\0 terminated string */ pos= lpszDriver; while (*pos) pos+= sqlwcharlen(pos) + 1; len= pos - lpszDriver + 1; driver= (char *)sqlwchar_as_utf8(lpszDriver, &len); len= SQL_NTS; pathin= (char *)sqlwchar_as_utf8(lpszPathIn, &len); if (cbPathOutMax > 0) pathout= (char *)malloc(cbPathOutMax * 4 + 1); /* 4 = max utf8 charlen */ rc= SQLInstallDriverEx(driver, pathin, pathout, cbPathOutMax * 4, pcbPathOut, fRequest, lpdwUsageCount); if (rc == TRUE && cbPathOutMax) *pcbPathOut= utf8_as_sqlwchar(lpszPathOut, cbPathOutMax, (SQLCHAR *)pathout, *pcbPathOut); x_free(driver); x_free(pathin); x_free(pathout); return rc; } BOOL INSTAPI SQLValidDSNW(const MyODBC_LPCWSTR lpszDSN) { BOOL ret; SQLINTEGER len= SQL_NTS; char *dsn= (char *)sqlwchar_as_utf8(lpszDSN, &len); ret= SQLValidDSN(dsn); x_free(dsn); return ret; } BOOL INSTAPI SQLRemoveDSNFromIniW(const MyODBC_LPCWSTR lpszDSN) { BOOL ret; SQLINTEGER len= SQL_NTS; char *dsn= (char *)sqlwchar_as_utf8(lpszDSN, &len); ret= SQLRemoveDSNFromIni(dsn); x_free(dsn); return ret; } BOOL INSTAPI SQLWriteDSNToIniW(const MyODBC_LPCWSTR lpszDSN, const MyODBC_LPCWSTR lpszDriver) { BOOL ret; SQLINTEGER len; char *dsn= NULL, *driver= NULL; len= SQL_NTS; dsn= (char *)sqlwchar_as_utf8(lpszDSN, &len), len= SQL_NTS; driver= (char *)sqlwchar_as_utf8(lpszDriver, &len); ret= SQLWriteDSNToIni(dsn, driver); x_free(dsn); x_free(driver); return ret; } RETCODE INSTAPI SQLPostInstallerErrorW(DWORD fErrorCode, MyODBC_LPCWSTR szErrorMsg) { RETCODE ret; SQLINTEGER len= SQL_NTS; char *msg= (char *)sqlwchar_as_utf8(szErrorMsg, &len); ret= SQLPostInstallerError(fErrorCode, msg); /* We have to leak memory here, because iODBC does not make a copy of the message. */ return ret; } BOOL INSTAPI SQLRemoveDriverW(const MyODBC_LPCWSTR lpszDriver, BOOL fRemoveDSN, LPDWORD lpdwUsageCount) { BOOL ret; SQLINTEGER len= SQL_NTS; char *driver= (char *)sqlwchar_as_utf8(lpszDriver, &len); ret= SQLRemoveDriver(driver, fRemoveDSN, lpdwUsageCount); x_free(driver); return ret; } BOOL INSTAPI SQLWritePrivateProfileStringW(const MyODBC_LPCWSTR lpszSection, const MyODBC_LPCWSTR lpszEntry, const MyODBC_LPCWSTR lpszString, const MyODBC_LPCWSTR lpszFilename) { BOOL ret; SQLINTEGER len; char *section= NULL, *entry= NULL, *string= NULL, *filename= NULL; len= SQL_NTS; section= (char *)sqlwchar_as_utf8(lpszSection, &len), len= SQL_NTS; entry= (char *)sqlwchar_as_utf8(lpszEntry, &len), len= SQL_NTS; string= (char *)sqlwchar_as_utf8(lpszString, &len), len= SQL_NTS; filename= (char *)sqlwchar_as_utf8(lpszFilename, &len), ret= SQLWritePrivateProfileString(section, entry, string, filename); x_free(section); x_free(entry); x_free(string); x_free(filename); return ret; } #endif mysql-connector-odbc-5.1.10-src/wix/cmake/getodbcversion.c100644 15766 12 3463 11707541005 22244 0ustar00cteamstaff/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "..\..\VersionInfo.h" int main(int argc, char *argv[]) { FILE *fp; int v1, v2, v3; if (argc ==1) exit (1); if (!(fp = fopen("myodbc_version.xml", "w"))) exit (2); fprintf(fp, "\n"); sscanf(SETUP_VERSION, "%d.%d.%d", &v1, &v2, &v3); fprintf(fp, "\n", v1, v2); fprintf(fp, "\n", v1, v2, v3); fprintf(fp, "\n", v1, v2, v3-1); fprintf(fp, "\n", v1, v2, v3+1); fclose(fp); if (!(fp = fopen("myodbc_version.cmake", "w"))) exit (2); fprintf(fp, "SET(ODBC_VERSION \"%d.%d.%d\")", v1,v2,v3); fclose(fp); } mysql-connector-odbc-5.1.10-src/wix/cmake/FindWix.cmake100644 15766 12 3652 11707541005 21435 0ustar00cteamstaff#-------------------------------------------------------- # Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## #-------------- FIND WIX_DIR ------------------ IF(DEFINED $ENV{WIX_DIR}) SET(WIX_DIR "$ENV{WIX_DIR}") ENDIF(DEFINED $ENV{WIX_DIR}) # Wix installer creates WIX environment variable FIND_PATH(WIX_DIR candle.exe $ENV{WIX_DIR}/bin $ENV{WIX}/bin "$ENV{ProgramFiles}/wix/bin" "$ENV{ProgramFiles}/Windows Installer */bin") #----------------- FIND MYSQL_LIB_DIR ------------------- IF (WIX_DIR) MESSAGE(STATUS "Wix found in ${WIX_DIR}") ELSE (WIX_DIR) IF ($ENV{WIX_DIR}) MESSAGE(FATAL_ERROR "Cannot find Wix in $ENV{WIX_DIR}") ELSE ($ENV{WIX_DIR}) MESSAGE(FATAL_ERROR "Cannot find Wix. Please set environment variable WIX_DIR which points to the wix installation directory") ENDIF ($ENV{WIX_DIR}) ENDIF (WIX_DIR) mysql-connector-odbc-5.1.10-src/wix/resources/binary/binary13100644 15766 12 1376 11707541005 22655 0ustar00cteamstaff è( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆˆˆˆˆˆˆˆˆˆˆ€ÿÿÿÿÿÿÿÿÿÿ€ÿ³úˆˆ€ÿÿÿÿÿÿ€ÿÿˆ°ˆÿÿÿ€ÿÿÿÿÿÿ€ÿÿˆøˆˆˆÿÿÿÿÿÿ€ÿÿÿðˆÿÿÿÿÿ€ÿøÿøð`ÿÿðÿÿ€ÿÿøÿðæoÿÿÿ€ÿøÿøðÿÿÿÿÿÿ€ÿÿøðˆÿÿÿÿÿÿÿÿÿ€ÿøÿøˆˆˆÿÿÿÿÿÿ€ÿÿøðˆ€ÿÿÿÿÿÿÿÿÿ€ÿøÿø€ÿÿÿÿÿÿÿÿÿ€ÿÿøÿÿ€ÿÿÿÿÿÿÿÿÿ€ˆˆpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€€€€€€¨ ªªmysql-connector-odbc-5.1.10-src/wix/resources/binary/binary14100644 15766 12 1376 11707541005 22656 0ustar00cteamstaff è( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆˆˆˆˆˆˆˆˆˆˆ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿ€øˆˆˆˆˆˆ€ÿÿÿÿÿÿ€ÿøÿÿÿÿ÷ˆÿÿÿÿ€øxˆˆˆˆ‡ˆÿðÿÿ€ÿøwwwwwwˆÿÿ€øwwwww'ˆÿÿÿÿÿ€ÿøÿÿÿÿÿÿˆÿÿÿÿÿ€ÿ‡wwwwwxÿÿÿÿÿ€ÿÿøˆˆˆˆˆˆÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€€€€€ªªªªmysql-connector-odbc-5.1.10-src/wix/resources/binary/binary16100644 15766 12 1376 11707541005 22660 0ustar00cteamstaff è( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆˆˆˆˆˆˆˆˆˆˆ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿ °ÿÿÿÿÿÿ€ÿ °ˆˆˆˆ€ÿÿÿÿÿÿ€ÿÿ °ÿÿÿ÷ˆÿÿÿÿ€ð °ˆˆˆ‡ˆÿðÿÿ€ÿð»°wwwwˆÿÿ€ð °www'ˆÿÿÿÿÿ€ÿÿÿÿÿÿˆÿÿÿÿÿ€ÿ‡wwwwwxÿÿÿÿÿ€ÿÿøˆˆˆˆˆˆÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€€€€€€ªªªªmysql-connector-odbc-5.1.10-src/wix/resources/binary/binary17100644 15766 12 1376 11707541005 22661 0ustar00cteamstaff è( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ€‡wwwwwwwwwwwwww€‡wwwwwwwwwwwwww€‡wwwwwww€‡xˆˆˆˆˆˆ€wwwwww€‡xÿÿÿÿ÷ˆwwww€‡xxˆˆˆˆ‡ˆwpww€‡xwwwwwwˆww€‡xwwwww'ˆwwwww€‡xÿÿÿÿÿÿˆwwwww€‡w‡wwwwwxwwwww€‡wxˆˆˆˆˆˆwwwwww€‡wwwwwwwwwwwwww€‡wwwwwwwwwwwwww€ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmysql-connector-odbc-5.1.10-src/wix/resources/binary/binary18100644 15766 12 1376 11707541005 22662 0ustar00cteamstaff è( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿððÿÿððÿðÿÿðÿÿÿÿÿÿÿð ÿÿÿÿÿð ÿÿˆˆˆˆˆÿðÿÿùÿÿxˆˆˆ˜ðwwwwywwy™™™™ÿÿÿÿù™ˆ‡www——˜ˆˆ‰ˆ˜‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿøÿÿøÿÿðÿÿýðÿÿüáÿÿüaÿÿüÿÿüÿÿü?ÿüÿüÿþüÿÿüÿðÿàÿàÿàà?ààÿðÿø?ÿÿýßÿÿÝ÷ÿÿýÿÿÿ}ÿÿÿýÿÿÿýÿÿmysql-connector-odbc-5.1.10-src/wix/resources/binary/binary2100644 15766 12 476 11707541005 22553 0ustar00cteamstaff(( €€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ»»»»°»»»»°»»»»°»»»»°»»»»°»»»»° »°ÿÿ€€€€€ € €€À×á«ÿ}ÿïÿÿÿïÿÿmysql-connector-odbc-5.1.10-src/wix/resources/binary/binary3100644 15766 12 476 11707541005 22554 0ustar00cteamstaff(( €€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ»»»»»»°»»»»»»°»»»°»» »»»°»» »»»°» »»°»°»»»°»» »»»°»»»»»»° »»ÿÿ€€€€€€€€€€€Ààÿÿÿÿÿmysql-connector-odbc-5.1.10-src/wix/resources/binary/binary4100644 15766 12 1376 11707541005 22575 0ustar00cteamstaff è( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆˆˆˆˆˆˆˆˆwˆˆˆˆˆˆˆˆˆˆˆˆˆ‡0ˆˆ»»»»»»»»»»»»pˆˆ;»»»»»»»»»»»»·ˆ;»»»»»»»»»»»»»ˆ;»»»»»·{»»»»»€;»»»»»° »»»»·€»»»»»° »»»»°ˆ»»»»»·{»»»»pˆ;»»»»»»»»»»»€;»»»»» »»»»·€»»»»·»»»»°ˆ»»»»³»»»»pˆ;»»»°»»»»€;»»»p{»»·€»»»0;»»°ˆ»»» »»pˆ;»» »»€;»» »·€»» »°ˆ»» »pˆ;»p{»€;»»»»·€»»»»°ˆ»»»»pˆ;»»»€;»»·€»»°ˆ»»p€;·3øðÀ€€€ÀÀààððø?ø?üüþÿþÿÿÿÿÿÿ€ÿÿ€ÿÿÀÿÿÀÿÿàÿÿàÿÿðÿÿøÿÿmysql-connector-odbc-5.1.10-src/wix/resources/binary/binary6100644 15766 12 10666 11707541005 22621 0ustar00cteamstaff00¨& èÎ(0`€ €€€€€€€€€ÀÀÀÀÜÀðʦ """)))UUUMMMBBB999€|ÿPPÿ“ÖÿìÌÆÖïÖçç©­3f™Ì3333f3™3Ì3ÿff3fff™fÌfÿ™™3™f™™™Ì™ÿÌÌ3ÌfÌ™ÌÌÌÿÿfÿ™ÿÌ3333f3™3Ì3ÿ3333333f33™33Ì33ÿ3f3f33ff3f™3fÌ3fÿ3™3™33™f3™™3™Ì3™ÿ3Ì3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿff3fff™fÌfÿf3f33f3ff3™f3Ìf3ÿffff3fffff™ffÌf™f™3f™ff™™f™Ìf™ÿfÌfÌ3fÌ™fÌÌfÌÿfÿfÿ3fÿ™fÿÌÌÿÿÌ™™™3™™™™Ì™™33™f™3Ì™ÿ™f™f3™3f™f™™fÌ™3ÿ™™3™™f™™™™™Ì™™ÿ™Ì™Ì3fÌf™Ì™™ÌÌ™Ìÿ™ÿ™ÿ3™Ìf™ÿ™™ÿÌ™ÿÿÌ™3ÌfÌ™ÌÌ™3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌfÌf3™ffÌf™ÌfÌ™fÿ̙̙3Ì™fÌ™™Ì™ÌÌ™ÿÌÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÿÌÿ3™ÿfÌÿ™ÌÿÌÌÿÿÌ3ÿfÿ™Ì3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3Ìffÿf™ÿfÌÌfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿ3Ìÿfÿÿ™ÿÿÌffÿfÿffÿÿÿffÿfÿÿÿf!¥___www†††–––ËË˲²²×××ÝÝÝãããêêêñññøøøðûÿ¤  €€€ÿÿÿÿÿÿÿÿÿÿÿÿøøøøøûûûúøÿÿøÿÿûûúúúÿÿÿÿÿøÿÿøøøÿÿûûúúúÿÿÿÿÿøÿÿøøøøÿûúúÿÿÿÿÿøÿÿøøøøøÿûúþþÿÿÿÿÿøÿÿøøøøøúþþþþÿÿÿÿÿøÿÿøøøøøÿÿþþþøÿÿøøþþÿÿøøøøÿÿøøþþúþøøøøÿÿÿÿøøþþúþþûÿÿøøøÿÿÿùÿøÿÿÿÿøøøúþþþûÿÿøøÿÿùùùøÿÿÿÿøøúþþþûûÿÿøÿùùùùøÿÿÿÿøþþûûûÿÿøùùùÿùùøÿÿÿÿøøûûûøøøøøøÿùÿÿÿùøÿÿÿÿÿÿøøøøøøÿÿÿÿÿÿÿÿÿøùøøøøøøøøøøøøøøøøøøøøøøùøøøøøùøÿÿÿÿÿøøøÿÿÿÿÿùøøøøøøøøøøÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿÿÿÿÿøøøøøøÿÿøÿÿÿÿÿøøøøøøÿþüüüÿøÿÿÿÿÿøÿÿÿÿøÿÿþüüÿøÿÿÿÿÿøøøøøÿþÿþüÿøøÿÿþÿþÿøøÿþÿþÿþÿøøÿþÿþÿþÿøÿÿÿùÿøÿþÿþÿþÿøÿÿùùùøÿþÿþÿþÿøÿùùùùøÿÿøùùùÿùùøÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿùÿÿÿùøøøøøøøøøøøøøøøøùùùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà|ÿà?à8à?à?à?à?à?àà?àà?àà?àÿÿàà?àà?àà?àà?à`?à`?à`àïà`7Àà=€à?üà?þà?ÿà?ÿ€à?ÿüÿÿÿüà?ÿüà?ÿüà?ÿüà?ÿüà?ÿüà?ÿþàÿÿÿÿÿïÿÿÿÿÿ÷ÿÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆffh‡{» ‡÷ðfw»ª ÿÿðp€hx÷ûºªpÿÿð‡÷ðx‡wº§pÿÿðp‡ˆxx÷ºwpÿÿð‡÷ðx‡‡‡€ ~àÿÿðp‡ˆxxðîà‡÷ðx‡wîð‡€p‡‡îzàxpÿùðÿðxŽç®ëÿ‡€ÿ™ÿð‡ˆzîë÷øpù™ÿðxx®î»€™Ÿ™ÿðwwŽë»w÷ðùÿù€ÿðww‹»wpÿðwwøˆˆð‰ˆˆ€wwˆˆˆˆ€ÿÿð€ˆˆ€wwÿÿðpÿÿøwwwwwpÿÿðˆˆˆ€wÿÿÿÿðÿÿðˆˆôDDD@ÿÿðˆˆ€ôçvf`ÿÿôþwf`ˆˆ€ôïçv`ÿùðôþþw`ÿ™ôïïçpù™ô~þþp™Ÿ™ôwïïàùÿùôw~þðôDDDDDOx ÿÿÿÿÿÿÿxˆˆˆˆˆˆˆˆÿøÿàðøüÿàÿàÿàÿàÿàÿàÿàÿðmysql-connector-odbc-5.1.10-src/wix/resources/binary/binary7100644 15766 12 1376 11707541005 22600 0ustar00cteamstaff è( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆ€ˆw»º÷÷{ºª÷÷‡»ª f`€ˆx÷{ªpª`÷÷x‡‡{§pª`ˆxxx§àª`÷÷x‡‡€ÿàª`ˆw~àÿpª`÷÷x~箇€ª`ÿÿˆîzî¿øpª`ÿÿx‡®î¿€ª`ÿÿ‡Šîë·÷ðª`wpÿÿwxî»·pª`xÿÿwpˆ»·wª`‡xÿÿwpÿˆˆˆðª`ÿøˆˆwpˆˆˆˆ€ª ˆxˆˆwpÿÿ€wp‡wwwww€ˆˆˆˆpÿÿÿÿ÷€ˆˆ€pD@@€ˆˆN€‡ˆ€ÿðOx‡wwˆˆðˆˆN÷wwwOðÿÿÿðNðpGïïçvd÷€G~þþwd÷€Gwïïçt÷€DDDDDD÷€ÿÿÿÿÿÿ÷€ˆˆˆˆˆˆˆ€ÿüÿðÿ€à€@?€?€?€?€?€?€?€?€€€€€€€ðøüþÿðÿðÿðÿðÿðÿðÿðÿømysql-connector-odbc-5.1.10-src/wix/resources/binary/binary8100644 15766 12 1376 11707541006 22602 0ustar00cteamstaff è( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ‡wwxˆˆÿÿ÷÷ˆ€ÿÿÿx€‡wÿÿÿ÷÷wx€ˆÿÿwvl‡x€‡wÿÿÿ~ggfl‡x€ˆÿÿ~væffÈwt‡wÿÿÿ~îggwÌwx@ˆÿÿ~ææw÷|‡x@‡wÿÿÿ~îîfwÇx@ˆÿÿ~îfw÷wÇx@‡wÿÿÿ~æwÇx@ˆÿÿ~g÷÷wwwx@‡wÿÿÿvÿwDDGx@ˆÿÿ÷ç÷÷|ÌGx@‡wÿÿÿ÷çwwÌGx@ˆÿÿîww|lGx@‡wÿÿÿÿ~nfÆÌGx@ˆÿÿ÷æflÇGx@‡wÿÿÿÿwfÆwGx@ˆwwwwwwwwwwGx@‡wÿÿÿÿ÷wwwwwx@‡÷„EȈwÿwwwwx@„Dw€ˆˆ÷xwx@D‡wzwˆ‡÷xx@@‡w®€‡wwˆ÷ˆ@ô‡z®·wwwˆø@‡xªë·w÷pwwx‡@ˆ®ë·wp‡wˆ‡@Ž»·ww ††‡„‹·wˆ‡‡‡ˆ€ˆˆˆˆˆˆˆ€üÿÿðÿàÿÀÿÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀàðøþÿ€ÿÀmysql-connector-odbc-5.1.10-src/wix/resources/binary/binary9100644 15766 12 23626 11707541006 22625 0ustar00cteamstaff(fhŽ èö ¨Þ 00¨†00h.!( À€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿp€ÿ€w‰ˆ€xˆ »€x‹ˆð{‹ßˆð‡»ßøˆ€ðˆ}Øw€pˆwwww€÷ˆxˆˆˆpwxr""/pÿr""/pwr""/pr¢"/prªª¯pÿÿÿpŒ€€€€€€€€€àðüüü( @€€€€€€€€€ÀÀÀÀÜÀ¦Êð """)))UUUMMMBBB999ÿ|€ÿPPÖ“ÌìÿïÖÆççÖ­©3f™Ì333f3™3Ì3ÿ3f3fff™fÌfÿf™3™f™™™Ì™ÿ™Ì3ÌfÌ™ÌÌÌÿÌfÿ™ÿÌÿ333f3™3Ì3ÿ333333f33™33Ì33ÿ33f33f3ff3™f3Ìf3ÿf3™33™3f™3™™3Ì™3ÿ™3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿ3f3fff™fÌfÿf3f33ff3f™3fÌ3fÿ3fff3fffff™ffÌff™f3™ff™f™™fÌ™fÿ™fÌf3Ìf™ÌfÌÌfÿÌfÿf3ÿf™ÿfÌÿfÿÌÌÿ™™™3™™™Ì™™33™f™Ì3™ÿ™f™3f™f3™™f™Ìf™ÿ3™3™™f™™™™™Ì™™ÿ™™Ì™3Ì™fÌf™Ì™ÌÌ™ÿÌ™ÿ™3ÿ™fÌ™™ÿ™Ìÿ™ÿÿ™Ì3™fÌ™ÌÌÌ3™33Ìf3Ì™3ÌÌ3Ìÿ3ÌfÌ3fÌff™™fÌÌfÌÿf™™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÌÿÌ3ÿÌfÿ™™ÿÌÌÿÌÿÿÌ3Ìfÿ™ÿ3Ì33ÿf3ÿ™3ÿÌ3ÿÿ3ÿfÿ3fÿffÌ™fÿÌfÿÿfÌ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿ™ÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÌÿ3ÿÿfÿÌ™ÿÿÌÿÿÿfffÿfÿÿfffÿÿfÿfÿÿ¥!___www†††–––ËË˲²²×××ÝÝÝãããêêêñññøøøÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿììÿÿþúììÿúììúûûììûìÿìûûþÿìÿìûûþÿÿìÿììììììììììììììÿÿììììììììozR1MLÿììÿÿìoåzR1MÿììììoååzR1ÿììozååzRÿììoLLLLLÿììððððððð쌀€€€€€€€€àðüüü( @€€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿwxÝxøðˆÝ™€p‡øýÙ™ˆxøð‡xˆÙ˜ˆ€xw‡‡øÙˆ‹°xøð‡xxxp ‹»°€xw‡‡»¸€xøð‡xˆ»xxp€xx»‰°‡‡€ÿð‡{¸›½ÿxxpÿðxw‰»½ø÷‡ÿð‡‡›»Ýxˆˆÿðˆˆ{½Ýˆøðˆpÿðˆˆ}݈€wwˆpÿðˆˆ÷wwÿÿÿÿpwwpˆˆwwwwwwwxpwwpˆˆ€ÿÿ÷ˆˆˆˆˆˆˆˆ‡wwwwpˆÿÿÿÿÿÿÿ‡wwò"""""/‡wwpò¸ƒ3:ª/‡ÿÿòûˆ33ª/‡wwpò¿¸ƒ3:/‡òûûˆ33/‡ò¿¿¸ƒ3/‡ò‹ûûˆ3/‡òˆ¿¿¸ƒ/‡òˆ‹ûûˆ/‡ò"""""/‡ÿÿÿÿÿÿÿ‡wwwwwwwwÿþÿÀøÿÀpÿÀ ÀÀ?À?À`?À`?À?À?ÀÀÀÀÀÀÀ€øüþÿÿøÿøÿøÿøÿøÿøÿøÿü( @€€€€€€€€€€ÀÀÀÀÜÀ¦Êð """)))UUUMMMBBB999ÿ|€ÿPPÖ“ÌìÿïÖÆççÖ­©3f™Ì333f3™3Ì3ÿ3f3fff™fÌfÿf™3™f™™™Ì™ÿ™Ì3ÌfÌ™ÌÌÌÿÌfÿ™ÿÌÿ333f3™3Ì3ÿ333333f33™33Ì33ÿ33f33f3ff3™f3Ìf3ÿf3™33™3f™3™™3Ì™3ÿ™3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿ3f3fff™fÌfÿf3f33ff3f™3fÌ3fÿ3fff3fffff™ffÌff™f3™ff™f™™fÌ™fÿ™fÌf3Ìf™ÌfÌÌfÿÌfÿf3ÿf™ÿfÌÿfÿÌÌÿ™™™3™™™Ì™™33™f™Ì3™ÿ™f™3f™f3™™f™Ìf™ÿ3™3™™f™™™™™Ì™™ÿ™™Ì™3Ì™fÌf™Ì™ÌÌ™ÿÌ™ÿ™3ÿ™fÌ™™ÿ™Ìÿ™ÿÿ™Ì3™fÌ™ÌÌÌ3™33Ìf3Ì™3ÌÌ3Ìÿ3ÌfÌ3fÌff™™fÌÌfÌÿf™™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÌÿÌ3ÿÌfÿ™™ÿÌÌÿÌÿÿÌ3Ìfÿ™ÿ3Ì33ÿf3ÿ™3ÿÌ3ÿÿ3ÿfÿ3fÿffÌ™fÿÌfÿÿfÌ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿ™ÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÌÿ3ÿÿfÿÌ™ÿÿÌÿÿÿfffÿfÿÿfffÿÿfÿfÿÿ¥!___www†††–––ËË˲²²×××ÝÝÝãããêêêñññøøøÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿêêêCCCêê»»áÂX1CCì¼ÿ¼ÿê»»áÂX10ŸCìÿ¼ÿ¼ìêï»áX10ŸŸŸCì¼ÿ¼ÿ÷ìëïï»á10ŸŸòòCìÿ¼ÿ¼ì÷êïïïï»»0ŸòòòòòCì¼ÿ¼ÿ÷ìê¼ïïïïòòòïïïCìÿ¼ÿ¼ì÷ê¼¼¼ïïìììïïCì¼ÿ¼ÿ÷ìê’’’’’ððìììCìÿ¼ÿ¼ì÷êòòŸŸXXï÷ÿðïïCìÿÿÿÿ÷ìêòŸXXRssºï÷ÿððCìÿÿÿÿì÷ìêXXRsxáºïï÷ÿCìÿÿÿÿ÷ì÷êXRRsáẺï÷÷Cìÿÿÿÿ÷÷÷÷êRsxẺºïï÷Cïììÿÿÿÿ÷÷÷÷ëëxẺºïCCìììììììÿÿÿÿ÷÷÷÷ÿÿííííëëÿÿÿÿÿÿÿÿÿìììììì÷÷÷÷ììììììììììììììììïìêêêêê÷÷÷÷ï ìÿÿÿÿÿê÷÷÷ìììììììììêê÷÷ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿììêêêê÷ìÿÿììêêêêìÿûzz1111MMMÿììÿÿÿÿìÿÃ^zz1111MMÿììììììÿ^Ã^zz1111MÿììÿÃ^Ã^zz1111ÿììÿ^Ã^Ã^zz111ÿììÿz^Ã^Ã^zz11ÿììÿzz^Ã^Ã^zz1ÿììÿzzz^Ã^ÃûzzÿììÿÿììÿÿÿÿÿÿÿÿÿÿÿÿÿÿìëëëëëëëëëëëëëëëëÿþÿÀøÿÀpÿÀ ÀÀ?À?À`?À`?À?À?ÀÀÀÀÀÀÀ€øüþÿÿøÿøÿøÿøÿøÿøÿøÿü(0`€ €€€€€€€€€ÀÀÀÀÜÀ¦Êð """)))UUUMMMBBB999ÿ|€ÿPPÖ“ÌìÿïÖÆççÖ­©3f™Ì333f3™3Ì3ÿ3f3fff™fÌfÿf™3™f™™™Ì™ÿ™Ì3ÌfÌ™ÌÌÌÿÌfÿ™ÿÌÿ333f3™3Ì3ÿ333333f33™33Ì33ÿ33f33f3ff3™f3Ìf3ÿf3™33™3f™3™™3Ì™3ÿ™3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿ3f3fff™fÌfÿf3f33ff3f™3fÌ3fÿ3fff3fffff™ffÌff™f3™ff™f™™fÌ™fÿ™fÌf3Ìf™ÌfÌÌfÿÌfÿf3ÿf™ÿfÌÿfÿÌÌÿ™™™3™™™Ì™™33™f™Ì3™ÿ™f™3f™f3™™f™Ìf™ÿ3™3™™f™™™™™Ì™™ÿ™™Ì™3Ì™fÌf™Ì™ÌÌ™ÿÌ™ÿ™3ÿ™fÌ™™ÿ™Ìÿ™ÿÿ™Ì3™fÌ™ÌÌÌ3™33Ìf3Ì™3ÌÌ3Ìÿ3ÌfÌ3fÌff™™fÌÌfÌÿf™™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÌÿÌ3ÿÌfÿ™™ÿÌÌÿÌÿÿÌ3Ìfÿ™ÿ3Ì33ÿf3ÿ™3ÿÌ3ÿÿ3ÿfÿ3fÿffÌ™fÿÌfÿÿfÌ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿ™ÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÌÿ3ÿÿfÿÌ™ÿÿÌÿÿÿfffÿfÿÿfffÿÿfÿfÿÿ¥!___www†††–––ËË˲²²×××ÝÝÝãããêêêñññøøøÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿ¼áÁâ»ï ÁááÁ™yëðððððð÷áÁáá˜xR1ëððððððííí÷áÁáᘘWRXyëððððððíí÷÷ííïÁáÁ˜xRRyyxëððððððíííï÷’íï ÁáÁ˜xRyyŸŸëððððððííííð¼ï÷’íï¼¼áÁáÁxRXyŸŸÂÂëððððððííííôð¼ïï÷’ï¼áÁÁRRRyŸÂÂÂÂëððððððíííôÿôñð¼ï÷ïïÁÁÁRXŸŸÂÂÂëððððððíííõõõòñð¼÷ïsyñòòÿëððððððííí÷ïïï¼¼¼¼¼¼¼¼ðëððððððííí’’÷÷÷÷ïí’’÷÷÷÷÷ïëððððððííí÷ïïïïïŸÂx’’’’’’÷ëððððððííí¼ðððŸxR˜’ï¼ï’’’÷’ëððððððíííòŸyRx˜Á»Á»ïïïðð¼÷÷’ëññññññïííퟟWRRWÁáÁÁ»ï÷ï¼ñôÿò’ëòòòòòòíïíퟟŸXRRWxÁáÜÁ»ï÷ï¼ñõÿÿëôôôôôôïíïííxXXRRx˜ÁááÁ»»ï÷ïðòÿëõõõõõõïïíïíXXRRxx˜ÁáÁÁ»»ïï÷ï¼ôðìëÿÿÿÿÿÿïïïíïíRRRx˜˜ÁááÁ»º»ïïïìëÿÿÿÿÿÿïïïïíïíRxx˜ÁáááÁ»»»ïïï÷ìëÿÿÿÿÿÿïïïïïíï혘Áá Á»º»»÷ïìëÿÿÿÿÿÿïïïïïïíïÿ»áÁÁốììììììììëÿÿÿÿÿÿïïïïïïïíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìëÿÿÿÿÿÿïïïïïïïïëììììììììììììììììììììììììïìëëëëëëëïïïïïïïïï ïïïïïïïïììëÿÿÿÿÿÿÿïïïïïïïìÿõõõõõõõõõõõõõõõõõõõõõõìëÿÿÿÿÿÿÿÿïïïïïïìÿÿÿõõõõõõõõõõõõõõõõõõÿõìëëëëëëëëëëïïïïïìÿÿÿõìëïïïïìÿÿzz^zzz111111MMMMMÿõìëïïïìÿÿÃzz^zzz111111MMMMÿõìëïïìÿÿ^Ãzz^zzz111111MMMõõìëïìÿÿÃ^Ãzz^zzz111111MMõõìëìÿÿ^Ã^Ãzz^zzz111111Mõõìëÿÿÿÿÿÿìÿÿ^^Ã^Ãzz^zzz111111õõìëëëëëëëÿÿ^^^Ã^Ãzz^zzz11111õõììÿÿz^^^Ã^Ãzz^zzz1111õõììÿÿzz^^^Ã^Ãzz^zzz111õõììÿÿzzz^^^Ã^Ãzz^zzz11õõììÿÿzzzz^^^Ã^Ãzz^zzz1õõììÿÿzzzzz^^^Ã^Ãzz^zzzõõììÿÿzzzzzz^^^Ã^Ãzz^zzõõììÿÿõõììÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõììÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõïìëëëëëëëëëëëëëëëëëëëëëëëëÿÿþÿÿÿÿøÿààÿàÀÿà€ÿàÿàÿàÿàÿàÿàÿà8ÿà8ÿà8ÿàÿàÿàÿàààààààààààÀ€þÿÿ€ÿÀÿàÿðÿøÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿø(0`€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿß…Xؘ€ˆˆˆxˆXؘs0ˆˆˆw‡ˆUÙ˜3¸ˆˆˆpwxxUýÙ‡3‰ˆˆˆwww‡…]Ù‡8™€ˆˆˆwpˆwwxu]Øs¹˜ˆˆˆwpøøww‡]Ó3™ˆøˆˆˆwÿˆwx]“¹Ÿ€ˆˆˆwÿøø‡wøÿÿðˆˆˆwˆˆˆˆpˆˆˆˆ€ˆˆˆwwwww€wxˆˆ€ˆˆˆwˆˆˆ˜pˆwww€ˆˆˆwˆˆ™{ƒˆˆwwpøøøwø‰˜³=•‡xøˆwpwp‰™»3Ý•ˆxÿ÷ÿÿÿ‡p™›³7Ý•ˆwÿÿÿÿxw»3}Ù•ˆ‡xÿðˆˆˆˆÿÿÿ‡‡ ³7}™Uˆˆw€ˆˆˆˆpÿÿÿˆxp37Ý™UXˆwˆˆˆˆˆpÿÿÿˆ‡‡wÝ™UXˆ‡pˆˆˆˆˆpÿÿÿˆˆxp Ù•UXˆ€€ˆpÿÿÿˆˆ‡€ð •UXpˆ‡wwwˆpÿÿÿˆˆˆpÿðÿÿÿÿÿÿÿpÿÿÿˆˆˆ€wwwwwwwwwwwwxpwwwˆˆˆ€€wwwˆˆˆ€xˆˆˆˆˆˆˆˆˆˆˆ‡ÿÿÿpˆˆˆ€ÿÿÿÿÿÿÿÿÿÿÿ‡ÿÿÿÿwˆˆ€ÿÿÿÿÿÿÿÿÿÿÿ‡wwwwwwpˆˆ€ò"""""""""ÿ‡wwwˆ€òªªªªªªªª¢ÿ‡wwwpˆ€òªªªªªª""¢ÿ‡www€òªªªªªªª¢¢ÿ‡wwwp€òªªªªªªª¢¢ÿ‡wwwòªªªªªªª¢¢ÿ‡ÿÿðòªªªªªªªª¢ÿ‡wwwòªªªªªªªª¢ÿ‡òªªªªªªªª¢ÿ‡òªªªªªªªª¢ÿ‡òªªªªªªªª¢ÿ‡òªúªªªªªª¢ÿ‡òªªªªªªªª¢ÿ‡òªªªªªªªª¢ÿ‡ò"""""""""ÿ‡ÿÿÿÿÿÿÿÿÿÿÿ‡ÿÿÿÿÿÿÿÿÿÿÿ‡wwwwwwwwwwwpÿÿþÿÿÿÿøÿààÿàÀÿà€ÿàÿàÿàÿàÿàÿàÿà8ÿà8ÿà8ÿàÿàÿàÿàààààààààààÀ€þÿÿ€ÿÀÿàÿðÿøÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿømysql-connector-odbc-5.1.10-src/wix/resources/commercial_license.rtf100644 15766 12 250 11707541006 24317 0ustar00cteamstaff{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fnil\fcharset0 Courier New;}} \par Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.\par } mysql-connector-odbc-5.1.10-src/wix/resources/ConnBackground.jpg100644 15766 12 46333 11707541006 23440 0ustar00cteamstaffÿØÿàJFIFddÿìDuckyPÿîAdobedÀÿÛ„      ÿÀ8óÿÄÕ     a!1AQ"2q´‘¡BRb²#³v ±Ñr3Ó$eÁ‚’¢Ccs“4ñTdt”ÔVfSƒD¤%5Uu•Å&  !1AQaq‘±Á"2r3ð¡ÑáBR‚’¢Ò4²Â#SñbC$“5TÿÚ ?‰ºy¬üñCO PÓÈ4ò Fäè¡t’"ü­2áë˜ó¶ÿ/wLÄyaåô/‡ë}£»É;k|>_¢áöÅ#®>ÿÁóäÇéGßø=Yddÿì7›Er¯ÂÖÖÅ ×äeJÂå\‘1=SÈž‰‰òþ4RÖÙî6ÕbWÐOG©Ö'Kš×§µ®TÁÉš&&7°¿Ö{ÑEžF,(iä y†ž@¡§(iä y†ž@¡§(iä :«û²µüßòmŸÿlqž%úÖü>¹z‡¥Éñú¡Õ“œvàÊöžGª¿=yÓÈ šK}UtÚ,Ò#\÷"tF±‰‹žç.Ö¢uUUÁ±;™[dÝ4‡´SжºžÑ`·I½w L‰<6WRj/ƒcŽ4Ij‹‚µ¿ËAuöÙŸÈßm–ÄűWuFïÆRwgúWä åOMSÉ{­»RÎì$kÐ5’Jˆ¾KJÊhUSÏßwß&%>§œDl²+÷C Ór,Ùb'5ÜÕÿûÒSnúSàkq}/oUnJ˜°þ·t®™UËíXéÝKò+ »ù–¢íÓNèüW8¹‹ûféí™õRÚpg¤?Gÿv–^Ïn›»üqþs¿»íš¿œÔ~œ¤ÿ¦h©O•kÜ>•xù©K·ª¶ÝL¿ûUªºtT\£©tñ'ÈŒ7ãæz›wÍ{ãð¢&nE¡ÉºÙ¶{&}u„mÝÞ”·ÖÎŽ¦·Œw_ë¹U_&Þ«FA;›ìXäWSO‡á#rj–Únqlì¾8~øûyÔz®AŸ L࿊:§dþ÷#%Cé›]QhÜö©6}ö™úsȱJØ'N•ÎE’/n1âžÆ`[Ç ñXóÆåÝoÛ|p]m±¾<že%m¶¢‚DŽv5["wAQ‘ñJÅð|r7¹«íE0º&7±ºÉ·zO#ãO yÓÈž@4ò§ <€êoîÍolœÝü³ü7cñ'Ö·áõËÓü ý6OÕªã¶þ[4ò=N¯Ï´4ò(¨¥¢uTŠÄsbŽ6:Zš‡® Š&&.{×É?ˆûlU•–qM44·mûxfÍÙQ>+\ÏÆ®®_ɬѱßínL{co‹X˜à¿|왲ێÚôz[ñÙv[¾^/?®{âÍ´ø¾ÞØíP¶²ùPÎۖᙩ¯*ø«ã§> jÿ)Uzœö£-ùçnî§W¢ÓâÒ[ìí»¦z$7$wÕwÝý²?ÉLJ±—¬pÅßl|£ù‡º^? í”|÷êÞï¾N£å=äëÎ ñ|GÊ?˜jŽKØ{W“-Ëæ™°] b¶Û~…é/’*ôïf>,wOf Ô•¥Í~ž}ÝJý~—²Ú]¿¢zcòv9ý{²ß8ÞéQµ·-;«ì“9ecUìT^‰SH÷|/ûæ¯Âï%N—[sYXÝèqÙ8ô—ü¬›mûm·zÕYoZU†HämU[5h+™Š2Xñê9£š½Qzï¶m–w[M±¶'t¨ôò1«y ”4ò(iä*PÓÈT¡§©CO!RŽ¢þí6öÍ͉øgønÇâO­oÃë—¦øúlŸªN9×jü½iž£W€Qú‘*ª""ª¯DDÝ×Y,+Í·'|Ë,n¾¾4÷å«\;)sl*¸*yÉkMÓ1O?Û±–k¸#åÆþžþ¯'¥(8ÒÅE²,Œ¥ë­j6[µZuW?‘µ~õ˜àŸ:ù•9ærÝ^Ž…¶’#Oe:g{nÓ]ÕU_ÿ!¯å$0¾ÓÜœ¸`ãÆÎ3/PÖ=Þf3cdd\Põó1áen¨v#…÷G-S“ÍPû1œ‹D÷7yL¾[\åkMûd·ï+4ÖÚÔkjcÆKmb¦.†\¬Â’ãhšVÆßkŒãlŒÑÙ-þ•0HÛ%C¿¸'Ùv3†[#UoFÖGMz©~T™¹ê¿i ' u¶Æ¦gt/q\®K‚ýOg½ðšç­±žþ¨U~’«Oç(Øïä¹SøQOŸ*:Ùüûº”²Ý#ê’ÓÉiï'÷íc±œñÓ 5MTã¥+\¿{à¿apS(²a„ä‰Ý,F¾¡ÌÇ©¶ÛQïÈŽœnŠZøëãj#ªØ©6oÇçEDù‹4û4Rk­ö¸ºÖ9ÞµÔ´Gu𡮥¯_5©¦F¢½ÒFæ9WÍÝÆ‹iuzÓñ_óqEý;§¾?"—O";í þŸ'Åê‡PŽyÙ€3zy›WƒQ`Þ|6;E1ÿâ“VT¢y¶©+þå$âg¾~Þ¶½Tðãˆëšù¾Òõãú8e©|S±îlÊÔ^Çv¯»ŽKí3˲4óu%3v‡íëË#YÝ]r&:S¢/ÙV)QŸW}›&“—b˾¾vÜ‹ÓÞÊkQé=ÛQÉüâÕ#—ühÔ…þ©—±kƒOÛçPÜ8b[Loª±VIsŠW:ßVÖ¤îDLpŽHÑ­Uö"µ>SeœÊ»/†œ¼Ž-ŠãšöKâÙµ¯SÃQ ™îŠVâÕÖ§ÇØ¨©©ÑQz*y)Ú»:ÚìåÙi»ÐÈ#Ûw†§[,Ÿë þ×:«zÛ£A“©öí»wÃÿ’Ëþºéó6õ¾Îƒ'SÊÙ²ë/Ñ%Sœ–Û{•R90l³KÚªŠæ`ªÄjáî»cã†LoÖE»!–.Y7í»d.’pþÞ™?/Ur•ÞoYcjý†DÔûF¯çòv7Ï'Ã;ëçb[‡ŠlVêw¾ ‹„˜&(ÙgG'âbHí¾éÛDMO+ÅŽ6WΈ\ƒld$Q£‘‘*ö÷/rõÿ½Ó]XrZû"&ÖT-T¡½Ò¯ù‚¾$ü(¤H\‰ò²uUþI·QÇ3ÔûÊæ³}q_7ý^É(‹íB S(ýÓÈUò†žB¥ <…Jy ”4ò(éOîçomg4'ù³ø×s’ñÕ·»ÖôÿO“âõC§g>ì€?šÍ#Òêðª,›¶‘fýÓ†Û‚|¯©ëøÄ»'e½Þ¹h×FÈîõÊß·«jmS6Jxâs‘|%jª}§4‘u‘t*,É6Ma%vŸ.nërFÚZ+;‘0ÃVž¡ß‹RÒ»7/²íó+½78Ëu¶ýÿŠiquÃzï› ×ûÌôVÊI%txíÔ®f¦Š«e’U¨’|[ßî¢7·á^½Pç5¶cà µžº»nW—6«̾""wR?–Ƕ:in5¶:ô…nt0ESßû²Á2½“±qV/tjŠÕUÉT‹tÒ+–6En›g|,׊ivÕŵÑ>®†úõGÀǵšUlb¹\Øt‘\Sã÷JlÅþ&Êí†GøZV'ÒüŽáQ"b–9“ÿ[ñ›'õ´Æ¦ÙüÙ~,Wʺ{2ÛßGM2,÷)]#ÝO¢,HÇùÇ9¿ƒÜa±«f9ùÓÃN¶Gxklô”ë ’¢®xè­´ÎrFÇM'Fµ]‚öµWUÁ:"®i¶x¥+%¼ß²+å¿tÛ¬Õ·;}e=UÊŽ5ÔScLøØÏc×6^îÜ{Udê¾]M˜î²ë¢'sNk2YŽn¶“1æGmÃȦ¦6H­éŠ::iÛŠ/ËTâëŠÈé—-©æynŠL[÷þ(Ç»ª*n2I%K"k±Uü“\Ôûnqq†Ø·sšÕ_7Íe« ‹¶ªâÏ'Úî*¿,t’Êßñ˜„Œ»qÝÝ>†<³f¦Øë¯¢_´mÔ¦…þÖ¡WlìYßm&U:FUcCHT¡¤*PÒ(i ”t÷w·¶»™ÓüÆÙük¹ÊxƒêÛÝëz'ƒ>†O‹Ôé¡Ï»æ÷K#Òjðê>.´I·Zj1þ¨æ;%Ž¢fáö ]²Þï\°×YXŽæOœØaæN‰Øç®ŠKhmø‘U†«å+:±éå(®ü[dŠZµyj¨®0§‹%Iß+qþ\r1ÿ9ÂóJÙ¨º½;^³áþº;)ÑXžú¯Ôû6ªÉÈ»‡tW9CÞ( ŠŽvµÏFJ‰’G'j/b7èèäUè½ê˜ã‰¦íL]†Û#|JMš±êoË>ìÄSíäûÔ›ú¶Ð¬°SE]’­{ªÚä\#ŠžV9z~­Cf޼S=<Ó‡‚Øõõ-Tu6µDƶþù —\¬¶ÕêËUip&¥t mM ÚÇ«“è¤k°ùU«ó533o•a¡ˆŒ“Ó !>‚àË%%F¤ÜwÍ*ÆÖ²)1Áý½SEUù¼¾8òðn¬OâÙÍ,¦¾7[1>¯Z껡“±Ìmº6#ÑZ½ÏWxå‚_è´ßÝùP£œEÛ¬ûÿ#MCÄVšx)êî•ÎlQ¶>èt˜«ÚÜ1÷˜òÊfmܪ³–aÉïLý߃Ú?NÜqTäZÔ¹Ö"øµõHÔ^˜’‹™®uy-܋Ú+½èº|¿…ª?M<+/êzÔK$RÂ÷Íp¯v1Í¢‘½©P÷šåO>†‹õÙæ&8¶Od-4Þåøî‹íǶ;nüh‡œßÅY¹oolƒee$[–ô%¶5zÄÉ\÷Á*«œ®TkR=Gª¯DUS,ZŽs7t(¹·)ÿÜ·(¤]øOâ›?ðõv;m¶¢Û[Ot¤¤Š ½ÁEW,SÔHÆ¢>gE"Ë+Šô"Žy¦¢Û¦bvuSí.ž|/¢ºÈ¶mšÄo‰šÏoL}È{ê ö—Ãhš×½*n•÷Éž”[n®š5´ñ§å*QØÞÔrµ¨šIÜ«ÓÁpµåüÂýLÌM´§Oäü®WžrLZ‰¶ù™ÖÌm§]"0édZÕÍÐÒÈT¡¥©GE?w³{nËÿWÛ?w9^õ-îõ½ ÁßC'Åêt´ vΆ‘èÕxÖˆ]n®¢TÆk}Kœ‰þj¡¨­é“˜ï²l²ïgº}?ô–Üøø±Ä½6½’•÷t޲Ž*†,Ÿ ¬kÓì9‘“$ðì•F 1ó6Å]âž9Ø·iÖ³eØjÕQ1Y­Ô²~4jrÚíV[w]>yz*Ð`º"¸íŸ$%ŧaY6ËS³l6­»=CYôêZ:X© «FcÚ“$ Oy¸¯kðULW¢§B‡&¢ìžüÌ÷Í]~<1þ±otDW̬ºî­½·`dû¦gmÔ‘Ý‘-KUì‘é÷1>$z9|ÑNå—H›UE !R†©GB¿wó{n\ÈŸôm³ø÷s—çßRÞï[¿ðÑÉñz%(]xóÉ£‘è•x­Ôl³n:êÙo¹§Ðk^¾ Y4ž¸àˆ‰"&+䊧Ëf—RwNÏÃïû’ðG³k)Ü–ªë{Öª†yiâ‰ÊÕûD¬7Äì¹O«Åu“[v<öÿ"oÛcÛ.ó½Ñ£z"C]30ûCfM÷ÙähÃÌõXýÜ—G–[¾Éʼ‹RÖ²~BÜk“OÒµIÓ%lˆ©ó/åÚxüË|зÅε“¿-þyfÔ«*>]W=ºDÂJê¹_Q;“زÊç=~É®q[dRؤ7Æ{²Oó3=s5lkmr"7Þ#ßjf;Ù5ÉÞ4M‰väUKsEoÄc2œˆÛÊ»z’ñºøæé‡eU¿uY§‚v*¶F9•Ю-{psW§Š)Yü9žÄîYž™­ŽØK®LÛü“Æû¦ƒmÝa«}E’QºEV¾ ¸Vz"w"w±;ºcÛŽ”Zl³‹%·G[¸¾ÚÄÕ ¶Ç£.y¼2Š®ùº6Öß·ÕFÙ¥SQ_RÄr"¦Å awEò˜µËαY33O·™¦ÜQXÜÝÖODTT¬wë?$Þ.ò*û‹k¦‚ÞÔObë-j¯ËЇ=¾}Ûb;öþ ãM ©lôÕÆ])`–çQ aô‹…UDýÙº%zB¿à®æ¹îüêw2ŒÇC9£Ú6‹ z8«"ž¡b*¹Vœù:"}ég‹’ë²ÛÇnøzøf-óÍ!YŸœèp]Ã~k"éÙN(âý˜Û÷ W%oŽä)]¸8ÒóQW{©z>±ÔÖù騪û—Þ‘éRÈ×ù÷5«Ý是qmʯÏû߶§ˆ¬Ò_Ô»¹ß¬v8°W½úÍ:/k%jùª& v}|Źfc‹¦=ïTþ=½éšó¼lX+¨VÓS¹m’®µ¦žÉ)årûñ«¤\4Ýñ&Z¸ôT_v¯UËqën‹¢è²ïίOnΟO¦V›˜dÑ[6ͳ}½èìÛÑèôZî±Ê÷"Yv‡n®>óîSÔ\×·&”H‹ó¯ÎmÃÈ46Oø™/¿áˆ³ÓÆÑ›žëïðñYgÅ3£¯¯ü¹uÖuÛ’ª-tr§¿Cg¤¥£k=½“i>¡?Ö–Ø4ܳ Gºc¦éºïº±oîªsêy¦iž,ólu[m±÷ÌMß¼×W(žùu—ÇÝw,NMJzÝÇ_UXotlò;·Ø¨Ôo±KM79໇Om–|ſن‹|1—YÏ}÷Çý÷]wš&e‹×lº|6úO¡P6¦ÀºT¸v£Ú­_L|K^ F¦Ùâºé¯wñ~ Ø|'¦ÃNˆžÈGŠn¿mZV¦ÞžpAcjÒNìîcz¹«þK*¿O>ýž¹·÷™k9[öÛt]÷KŠoËÍGU”uÔÎì©£‹‘»Øæ¹P‰Y‰¤ì—3›Ov)¥ÑIVéd}i¡¥(ž~ƒÛÛvæöÒmŸÎ^kž{ö÷;Ÿ },Ÿz(ÝhôNþ¯¡¢‚¥ÿrZ.–úºmË·*Cy¶»Ržvx/µOºk“¢¡¢þ+gŠÝéú\ÑÍÛ¥¾6÷Ûü±m’‚®8í;ÂÞÄmÒÏ"¢*¯†¬8áÜÇ/ØðS,y+VtoŽ›'ož«EG~éût¨ï["²Û3ßníEÇ¢ñj¢ç;¨Ð]dì[)&ª£r#ÚäÀß4”hâµ™ÐßÔj9TÓv4›32Ho­TO|Õ8Ûã3Ý×ÄTèóçËeó˜eæäé·ÄN¸~²Ú~»˜[L7wJÏ“_]MñéTì ½~Û~¨šÛí¢ªÔêêÝÍSBµ19žh+ÜÙ"r§kÚ¨äêÕSÒyÂó8o‹¸mÃI­&/³dõO{›äZú_[¬›kvYŠÆø›/ÛqÜûŽùkÛÃ-ÉÒ'ÒUÍ‚8˜¯s•¨Š¾ÄO5<žø£GÉ1Û~¦gÚ­"جÍ7ötôÌ=Ï“øsUÍïºÍæZ™˜|¬b;ñ;ÖÿÌvFÍ.šg¶û¢?vÞ/í;­'üQtíÔj";,¶¿½wöYÿXo|T»“pG[~ ®ÚȽÉß»_<½êîæ1Þë[àç¢÷{¬Vº÷Ã> æÜÞÉÔê&Û1n¶Û"œ]³uÓuÔÛ&+5èŠMG?䯕ß0E×ä·]tÖ‘Åg¶&‘ÛºHn­±OYh™”Ñ"MUÊåêçª'‹—ÅTî9f®päŠî•öÖ;xÒ:’ä±¹½«Þ©ë¼·$_ލÆÕ¾†wnnÍsä1^Fãw±ÕÖèã¥Ý–öw[+þ×èó¯›]ä«ð¯_ QyÍ~š&+oý?'£º¨ší º«?îè”X´Ô­dOdѺ ªwº*ªw§k㑊­{\‹Õ0T*,¾®>ÇtÄ®ú&ui¢súoeç—“þ…¶9x9ÎwïÛÜíü)ô²wÇ¡ÐÒÕ€pƒG#¼«Èhhä*Qðúd‘ª×7_"‹wl ¤¨†÷bžKmâ…é5m:«$cÛÕÙ1ÌO³I…–›UÃìݶ~Ïõ7p°,vc²MYØÍÝCs‘¸øÔSµ1èžlǽCÔ[3íû3×ï–:<•øS®Ó[–+fÞÉßçü|é'd¨ãŽC¥úvÐܶû«_R·V5wܾ5TsW%BU™2DV=¨ë¿ôòªóòëkIŠOj¢£ŽgUcLPÎÝl!_Ë&ÍÙÌ\;TÏù¨kÿOºÑlʄÿ¡Œê¡º a›¾+%‚ó±¾åKKQ>åµEM’µ®|®¬‰Ö¢®*ª¾D-}×]†f›(¹äú^öÓ®º¬RÍ,°Ç,´ÑLúYÔs£s¢sÌUêÕV¹SòUC’Å}ÖÍ"f"i^ݵÛåÚô[ì¶í³cwfÊlòlk.U¤ŽXì±Èäb÷Îæªø.Äþéæßò¬|Ìzk{oŸºÔÞCâì¾ëóÙuÖä¤{4¬R³ºi_;I\(´(åtmG=S¶4O7/DCÇðifü‘lFÙš=o–x¿—s\sv›4]1›w]öÎß.îÔúØòÙvý‚Ýg…Éhae+_‡G$ §Ýýú¢½srŸ©ôœ¢í& 1Y-ˆ7O•ãú­Tê3]–í÷LÏ‘ßw-¦Õ5|*ÊùÕR*J691–Wc‚/±\ì‘pE^†Ëp]Å1F«-âšBÝy+Ž'¼ÜßwØ7ýðÛL²7r]¬°¹)(äLVH›…D=Ήª&*‰âåS¤Ã›]e±òòðÇD}£ÒѨ¿“Ã;×:Ë&Ô©¥´n]‰v[ÆÕÜQ¬´“¤?¯t/îF¹U0r`©Švª;ªb·œ·eÍ\Zˆ¥ñÓ×ù{¶KUØitn—¤6ÕDDVü¤Ì™âv1ˆAT³3lóÖžiÓnZ8/,>’GIËòºHõþQI7‰?nÏS‘çÚx·'tí[ôr6ÕÎQ6ý7²÷Ë©í¡Û?¼÷:÷íî—iáo§“¾=‚”ލ†š9Õ^KCG!R†ŽB¥‹*`©Š ”b×½›k½DöTS±\äñÁ Wã‹›ñæºÍÍ zàéij–áaªžÛVÌV*šYóèö**}’$é¦Ù­³IìZbæ5Š]¶T·ÏQ{M £Ýf§‰0Hjû*ÑS7NÇ»íŸg6¢7Í{â.ûæ*ß4÷ôSºf= ÷s?©ÔjÆ·ØUUȺŸB‹¹0Å0ðÃÄÇùœß£o™—Ÿ·Î²WnïR[Ÿº*ýóp¦‚L#I4Ȉ¾8:8Ñÿãæ53ºb;¢#ï¥Jé­è¯|Ì­›c‹oòîÃq«ºÖÓî›4«U[4“Éî×B\¿l‰ªÃ}ÖÍ×ÌÌÓ¦j•£ÖÛ9-¶ØˆŠÆç}kçþ¥V˜ÿ‘“ñT¢³|:ÉÜÕþ ®Ih¦ÚÎWv¬óÕ5?½lkýÓ‚ÿ‘­ãѾïD8OÝÁf.ùõ#…Vík)Y3¤ÅHÉ™1È«ü™òøŒz›/ÑtOš\®ƒW8³[|LÄÄÆØM¯°1Y"+zö¹¢¦+Õ3ö4]kÖæå]“uÑ¥úÙ|ŒZy¤thé:£^ö9¬û.ToÎkæ¶ùÃÅl{³YîÚݧÍ]·©»¯¼]iÜ›í²/×w%MÆM½¸-4n«¦¸C],’Dé¥bþNV÷àö»ÁϦ0íÉnJL]×JÍNŽì—V:Ygl«¥»hÚ¬DÓª¥š{¥Â‰)þdp÷&)Ý×™ØcSªùy8íîZcÇÁ†,ÿ’å6Øtxö*.YÍk½‡œ~£‘ŽæË%J’ƒnDµJß¹tµµjÖ®}­G|Š„Œ¾mÕ‡/âñŒ_G"®N‰Ÿè¥½·î[΃lþvðsüçÞ·º]…ýÌðŸ…+©âVŽGoW”ÐÑÈT¡£©CG!R†ŽB¥šø ©G“èaÇ ]ò¡ñõN¶z%\V™Ÿ`Rk/F[i™ðÓµ>a±ók½Â‘ßvBµ¨Ü7¯ë‘5³þ÷,¹To|:(—iÝ´•Ѧ¬ÐÉôj¸}èfDj¯EOÁ<å­ÞôY†‚õßz–ÁjãibUn½uÅ®Ãðcºq^6·.û½O9ÿ.ùxð÷Ýè„ÛÛ¢ÿ¸ÚúEª¾÷3“µ`¢§–¥ø¯—lmrž{C“%Þų3Ù>‡¤¿.K©e³töDÏ¡:øûqn)ö…ºsÙë¬WÛTiIWG_ à–FDˆØ¦F½Ø=˜cŠ|]ÇêoêïÕhqÆkfÜ–Ä[1;&±‰ýhŠü\Oeå™räÓ[9m›nˆ¤ÄÅ7t®URT¿åTU=Ìql%̶…’÷z¬£‚‚ºã-M,X$m“µÎDðýÍW/Χ/¯å:n9¾Ûi=›¼ß…ìÉt$ÕeV¦AG`ìr¬Øb®{—îs—ràˆ˜ªär:Ü7ö$[5zî[ͳnÙlVê}<µ5u³®ÃMWÉ+ÿLWÛàU ɲk±”Ý—õUÈÛóur-LSÁ|ªD³ÒËñÃANÆÁHÇaÓ¹"cUøx¹\¾gA¢ÅÁkƒæÚŸ’e–èäNªž‰…èɽ›ƒ–Sû?lþvòPózÞéuþc'|zÓ̦tà^ÑÈí*òÚ9 ”4r(hä*PÑÈT¡£©CG!R†ŽB¥ …J0-øË…% âÙ*kl•P\) “ÇÉM#ek]Ú¨¸*·ÁHú›xì˜MÐ_òòÅÝR‘Ü/ÎÛ3–¡«£µÌí­¼-ðÈýÅÇõÎE‘¨TZŠ7. 4]Ëñ51Nˆö·ǘ¾É²êKаê-Íma8·}ƒbÜié®›æÕc®¤±+夯¾ÃM$4‹&÷±õ(­Uˆª˜r`Ç–œvÅÔÝX‰§œÔè°g¤å²Û©»Š"iÝV€Üþ­}3ì(oíWIi‘Y ŸjÄû·¼ž,k­ñÉ<ûÞß²KÇŽb)lla7âʼnˆŽÄ8äßXVíÙ3fã>&¿ÖÕĪ‘]¯sÁmSÏò0}1Ïjû¬_³Ñ_ŸOžXè˜êŸ¶Íðƒ—_†:Y.Äæ ·¹iéÛ‰Û"ü­jU[.OD§WªuЪTkÜz'wk¿ï4¼îÌ–Òí“ÛHŸ>ÈŸºíjǩœt¤õŽX]RÃ#e‰ýY$kÜ×&JÜQLuí»¯Í?‚]°Èkù¯`ñÔ2¿qîZ:IØÅT¶$šµTòe,]ó9qéðaíTñ9}.Ý¿íåevk1ûÒ…\«ËÛ·ž«ÏEIS¶øâY+­óö¶®äèÜŽcªûæ²691lMr¦>óÜ÷#{aàÓm¬¨95ãŽ7)mÖ¨môÑÓÂÄkXˆ 8Øæ®™ºj®ÑÈûV4KoGlìÜ\®žÛnÙüõ䣿ûíòºÏ û¹;ãÖe;¦ã¦‰ÙUæ44EJ"¥ R†ˆ©CDT¡¢*PÑ(hŠ”yMEDnŠV#˜äÁQO±±¢·g Á_w£Ü6 ©ì—Û|©=Ö‰î†xdOºdŒTr/‘6–ÛÖZm}ø–Zž ¹îªöÝ7þè¼o;ƒ\®JËåuE|ˆ«ìuCÞ©óìÐÛk~^m’þ–ópþÕ´5‰J­ü$Û‚Ø@¿U}Ý,ò—o[(Ú‚Ž6¢x{¦Øˆ„yºeó]·-µìVOJÅEü1[tÛ‹l2,$m—¤­j`ŽOb¢xøšþT$FªøéW[¸æÁnr:*HñOtûí†gºæg4íFC1©àˆ†Èh¯mí_(hŠ”JŸHŒìܼªžÛfÙüõ䥿ÛíòúO‡=ÜžOZqZ'_W›PÑ(hŠ”4EJ"¥ R†ˆ©CDT¡¢*PÑ(hŠ”4EJ"¥ R†ˆ©CDT¡¢*PÑ(hŠ”4EJ$ç¤æön~SOm¯lþ~òSó]öù}N›ÃÛ²y=i²T:0Oèäu•yÕ …J9 ”4r(hä*PÑÈT¡£©CG!R†ŽB¥ …J9 ”4r(hä*PÑÈT¡£©CG!R†ŽB¥ …J9 ”4r(’•ÙÙº¹E=¶­³ùûÉSÍ?7Ëêt|ƒÿ'“ÖšK¢å®ŽGSWŸPÑÈT¡£©CG!R†ŽB¥ …J9 ”4r(hä*PÑÈT¡£©CG!R†ŽB¥ …J9 ”4r(hä*PÑÈT¡£©CG!R‰é½›³“Óû#lþ~ôUó?Íòú"ÿÉäõ¦9Tè@9•¢tõpT4EJ"¥ R†ˆ©CDT¡¢*PÑ(hŠ”4EJ"¥ R†ˆ©CDT¡¢*PÑ(hŠ”4EJ"¥ Rï饛¿“SÛgÛ_X½œËó|¾¥ï#ß“õ}i|Uºph%\5 R†ˆ©CDT¡¢*PÑ(hŠ”4EJ"¥ R†ˆ©CDT¡¢*PÑ(hŠ”4EJ"¥ R†ˆ©CDT£uzsgfñä´öÙ¶×Ö/Ew1Ýo—Ô»ä»ïý_ï%©X¾ç®ŽGEWCG!R†ŽB¥ …J9 ”4r(hä*PÑÈT¡£©CG!R†ŽB¥ …J9 ”4r(hä*PÑÈT¡£©CG!R†ŽB¥ …J6ï§¶öïNIOì]µõ›Ñ_Ì7[åõ.97½u¿ÞJÒµz´r/êãèhä*PÑÈT¡£©CG!R†ŽB¥ …J9 ”4r(hä*PÑÈT¡£©CG!R†ŽB¥ …J9 ”4r(hä*PÑÈT¡£©FÑàVöonHOmm}föB×û¶ù}KNQïßÝo÷’­^Aí‹Ê¹>G!S„ÑÈTá4r8M…NG!S„ÑÈTá4r8M…NG!S„ÑÈTá4r8M…NG!S„ÑÈTá4r8M…NG!S„ÑÈTá4r8[ƒ[Ù¾9?°¶×ÖodMw»o|ú–\ª=»û­þòM•Ë e£‘uW/CG!R†ŽB¥ …J9 ”4r(hä*PÑÈT¡£©CG!R†ŽB¥ …J9 ”4r(hä*PÑÈT¡£©CG!R†ŽB¥ …J3®gfûäDöØv×Ö¯d]g»o|ú“ù_Ô¿ºßMÉ&W®@"6‘oW7CHT¡¤*PÒ(i ”4…JB¥ !R†©CHT¡¤*PÒ(i ”4…JB¥ !R†©CHT¡¤*PÒ(˸y½»÷“Û`Û_Z½‘õžå½óêLå¿Rþë}7$Ypt‹Z¹êB¥ !R†©CHT¡¤*PÒ(i ”4…JB¥ !R†©CHT¡¤*PÒ(i ”4…JB¥ !R†©FGÄÍíßü€Ÿùm}jöhÕû–÷Ï©+—}[û­ôÜd¸lÒȳªŠ†–B¥ ,…JY ”4²(id*PÒÈT¡¥©CK!R†–B¥ ,…JY ”4²(id*PÒÈT¡¥©CK!R†–B¥ ,…JY ”^8½½¼ƒ¿³ÛÛkëw³V«Ü·¾º‘ ú·ü6úno² Ô?iSPÒ(i ”4…JB¥ !R†©CHT¡¤*PÒ(i ”4…J"eΗ’w*rݶòÅw_¶ÛhëvFÞm5M¶¶Ñ,,îª|r·ºuZ”|r.?“\ʲpâÁeÓŽ/‰¬]5˜˜»«³fî·7’ÝN}VK-Í8î¶“lR&Ù¶›öïö«Ô¼Pò×(mÍiå¿^ë¨$F.ìØÔ¿¤­ÕpaÒ¡!tŒš5ûæà«ç‚ti…Ú,}¬9""z/Ù1ÙÔÙg2Õ`öun™ÎÇVÌuÓ|6¦Âå]ÉM«fÒ¿2²ánés²TG%-}2¢àº´Óµ’""ôW"+qé‰S£Ë§§lÓ¾'Ë 1Óêëòî¬ÆøÝ1ä­¤Eª} !R†©CHT¡¤*Q[ÆÍíä=úŸùwm}nøa©úv÷Ï¢ÖÝÖ¿á·Ó{z–€?HUM !R†©CHT¡¤*PÒ(i ”4…JB¥ !R†©CHT£Csí–¢‹mRò•†Ë»¸¢t½Ð˺ùí­sRéE"ùÇ-/zª}óZ©c˲Dß8n÷oÙåüÙòJŸœâ›qÆ¢Èöñ{QÛoçÛÝ6Õ°êÛ~¿M±·ÏÜE·¦•*¯´3BÉ"¸[*é•Ñ>);FJÇö9¯Fª+»ÑÞî­à³ŽÛíöº;&'ЛwIÇ~+£‡|Çé[1ç®êyjÅy'…6ß ÉO{§–m¥¿­XI`ßÖŸÈ×ÓÈÔÁ­•Íí׋ÉXõðÅ­ÄÝ¥×߃ٟjÉßlîü’¯åXõT¾=œ‘ºøßŒvKâÞN¿Ôn*î%åzZ{G'Ù úE m?»E¸(Jê,Q0v U’<:`ªˆ˜9¬Ù«ÒYFl;qÏžÙêŸTý§G/æ'$éµ1–6Äô_oé[ëË ôŠÚ®¨i ”4…JB¥||ÞÞEß³›këwÃGÓ·¾ïE¬ô^ÿ†ßMíÚCY€j 2eU”4ÅJb¥ 1R†˜©CLT¡¦*PÓ(iŠ”4ÅJb¥ 1RŠ8jàž–¦OMS¢¨‚DG5ìz+\×"ôTT\ûRk—[E'sAp2Ü-ä.3YÖë¶ø²öËFÒ¾¿¹du$ôì«J UØ÷>…²¶.ä^­íè˜<Æ—peÝuñYŽÚÒ¿­½MÉø¬ùš}ö㻆Ù옯êÖ‰¦VÕuFõ Æõ»¿hǹ¶«–‡’8æE¿l‹¬-ü²ËNòÒ~*ÞÞÕè®íǦ%-ÕF,œí²ý—G¯È¦çZ³âù˜öeÇí[=ÛãÊÏ8«|ÑòwímóDÆD—Ú&ÉYJÅîH*£UЦUëƒ%cš˜ø§R>³O:|×cž‰ûº9v®5š{3GçGšzcÎØZdj¦ÐÓ(iŠ”xlVöò>úý›Û_\¾3ý;{îôZû¤ú÷ü6úonb"ÈXi’ª¯¡¦*PÓ(iŠ”4ÅJb¥ 1R†˜©CLT¡¦*PÓ(iŠ”4ÅJ"fÆß–^3ân]ä Æè}û»&¼SF¨“MZë«éééÛƒž‹ˆ˜/‚:=ÚŒøñÛÓe´î¥f}.sI¬³I¤ÍšþŒ—×¿Š‘ˆIm±}¶îÝ»cÝi’¢×¸(`¯ •cˆö¢û1ÁSÉzYqÝŠù²íñ4_ió[Ÿ¹-ÝtDÇ•}Ó5Õ¶ˆƒÆIþç¹×{píJýir.¦óã,}Øã¨v)r Ë«Uíb|,f>/.õûZ[sǽg³w÷eÍh?ôuÙ4³îdöìïüë}têŽÔ¾Ó)*éhiŠ”4ÅJ-û1½¼“¾?f¶××/‡ÜßJÞû½¾i~½ÿ ž›Û|ˆ±×úy*‡CO!R†žB¥ <…Jy ”4ò(iä*PÓÈT¡§©CO!R†žB¥ <…Jy ”B¾Vôÿ¿÷⯱í:‹[¸··U¯tïzJÉM¾²µµ®§b#›++­ñIîZª¥î™bÇd]}~e¶Í¶ÓtÄî¯U=S˜ò]F\“f9•’ûoº»í˜÷©×Å³ËØÜ{·îW»ì9#t¼}|­¨ºñÍÁòvé*ž²ÕYdûÄlŠéiÕz9®s1îb"ÂÔäM‘“óâ)wm7]ꕞ‹ Ú,—aÿÇtÌÙ?£]³gŸm½•ކôÓȯªâsÈÜK²¹VßCA¼-rT¾Ñ:ÕY.”³ËKYE:¢"ÉO<.kš«‚b‹‹WÅ%iu¹4Ó3dïßâ{á]Ëpëm‹rÅi¶&&“Ù0ÕñÛ¹g…ݰÜn¼ÝƱô­¤¬ì“vZcOò°ÊÄb\˜‰ñ1È’øvwxø°jú#ýžåßÃèWÅš¾_ºg6.šýK{côã³Þêo½»~²îÛ%·qíË„W[-Þž‚¾{^ÅUEEEDs\ÕEkšäEj¢¢¢*ÙqÝŠé¶è¤Âãk3Ù,šÛ;¥{ÓÈ×Vê,ÛI½¼•½ÿfv××/†Y~•½÷z-kÓÿQÃg¦öÙ"¬0Ý<ÕE£Hsg=lN¶Ú+·Š\*êoòMžÑl…’ÔMôtbÌÿÊÉmk5Šªü}äÁŸ åÙu—LYM›æU<ל`å¶Û9k3vèû7õ-êK¹å×j=±}¦ùe³ÖØîŒ“:ÎìIâtRH×±¨×uEjªbQWf¿•åÑÒo¤ÄôÃ_)çº~eXÇXº7ÄÓw\R© §‘YUÕoš=YñŸ î(6ê–ëÜ+ u5ôˆázQÅ*c³çš$G½¾ò51\0UÁ¸Ûèy>m]œvÌDvô¹þkâM7/ÉòíóMõ˜mN$åŸÍ[Sõ¿fIUôª¤¡®¢®‰!©¦©¬‘ÑÊÆºFãÙ#\Š×9ÇHzÝM&N ›÷ìܰå¼Ë0ÅóqV•¤×|KhiäDªÂ†žB¥ <…J5&ãæƒ¶ëê-•óÜ+i¬ªŠ†-Tí\Š÷+ªžhЏx/S’æ>7åš“Šëæë£dűZOUvE|».‡ÂZý^8Ém±m³»ŠiXë¦ÙcŸñÇÿókÇýž/éŠßÿHåY?f?‰;ý‰¯ë³Ï?ÂζO%mÍý-Æ+$u‘:×rU:®6F²+‘0V½ÿz¸—¼“ÅNo7ƺ8"&x¢#}{g©Qͼ?¨å‘lå›gŠf"“]ÞHcw~wã»MT”Œ­©»>'+d–‚H‘SØ÷º6»ån(Vë<Êô÷Í‘u×Ӧجyæb'ÉTí/ƒ9†{x¦Ø¶¿¥4Ÿ4VžU§þ"øÿþmwÿ³Åý1ÿÒ9gVOÙâIÿbkúìóÏð¶>ÙÞ[C)jg­Ž¿EêËtìVJΨ­rÆôꈨ˜9¸¦>x?'ñšDΚúÌo×G’z;b°çù§$Ôè&#=”‰Ý;â|¿i[ö‡&í½ëw¯²Ùâ®e]¶ÍPꘘÆ+Y#c^ÕlŽU\\žDNOâ'4Ï~ 1w±35ˆˆÙ1sÖ•Ìü;©åømË–máºišÎêõCcéätuQQÍmñÆ´¼wɇqóÎÞܼÁÄ×Ú§ÏaÞ̹\ªÿW"•ËßMq·Ã;Q`b;z"·µ:5\½ê´ú©Ï†Ût÷[$o¶‘]±4Þá5zÒênɬ¶ü¸n—Véù}—[»·þ‘=8ú˱,ûNÕÑZé6}dLµ-¡ôY›7U•®f=êï7*ªûNwS“-Ù'æÌñt×{±ÑbÁ|ˆˆ²vÅ7méfºyê•F=¶ÛÉ›Ûöcm}vølËô­ø®ôZÕƒú‹þ=7¶™<Ó6´QÊ_Þ\ÞÚÞÎ ÷ã[οÂÛ²þ¯­ç^<÷°~·÷ZÛqÒÉé[ÔÎÅß”qº›coJJ[•Tq¢¤B¸ÆØ®°"'EX%U™N‰ù2V)ÿQÑ_Ž}ûfcËïŸw=³É¹–<ѳñä»eñä±ävrîk.ÔÚ·­åuªklV;t×Jª¨ÜŽGA k'äׯDÁ¨‹ÕU08¬X®É|YæhôÌú‹0â»-ÓìÛò8…³öÍךiýIóþìƒV -–åSB×ûÑ¥ÎàÇ6£UN­¤¦Å͸Ƨ{Ÿ-ºIÁ¦³¦cÍŒúÞM¥ÓÝÌ#U­ÉÑlÌ|Wný›}IGèŸqÔìßL¼Û¼(颬«Ú•÷{Å5$êäŽWÐÙ©êÇ«p\¬Áp*yö(Ë­Ådîº"<÷L: gœ·>XŠÍ³tù­‰I_JÜ÷|çý»ºo7ËŠkÆ(! |¯l’EsµUW^ ¾oË­Ñ_m¶ÌÍb»W¾çóŠ5Ȩæ¬mTT_TÀõ‰ÁŽb“ly¡ç–øÛOxÞ– ¨ ÑCCi(â’étÑ'kÄ‘Nˆˆ¨˜!ä~Çnç³RØœ‘HÝJîîzW=¾ì¼‡÷Ínö&³¾´Þ§ôìÜyv'ö}GÖâ5ÿÇŸý¦‚ïíÚÏÆßý~Š?³)¡¦{3˨üX‘È­r"¢¦ ‹à¨*ùF/·v]“iÍsý]§[M¶ë/Òf°Áƒhb©rªË=<(˜D²ãŒf s“¿·½Ïs¶åÏvXŽ-³=4íë§B> %˜&x"‘;iÑ^¸ŽŠôôtï­r3RM¶Þoo&ïOÙµõÛá³'Ò·â»Ñj>êoø,ôÞÙÄdð 7a›UŸýæm·†¿Ð_¿ÞvÝ—õ}o9ñcõ¿º¬8•y'Ó¶ëo¦Y÷'Ûi¯¶®ÔÅï¦e3¾óÁЦ¦ Õ]P®äúÏåõ“îÝ4žúìûý+¯òßæùt]l{Xâ.Žê{QæÛä„Þž¦«·w¦ ‡ÃK<»¦*ïÑ{¢F£•ÓZ­zo·10ø–U|m^ª¸À¸üHtX9TbÖߟóiXïÿnתç×gå˜ô±ïÖ—vÛo»çÙû=©éWÅ,áïD;ËiÍE{“jVÜ÷KÓÅ×*ÈÑó5U:.’vÄ‹æÖ!Ï[¬þk™[GDwGÚ®Âî]ü%¿ûÜ7|S¿Í»ÈÐþ’­õ·_GÞ£­Öêi+kë“pAEG UòK,–ZÆ1©Õ\åTDDñRÜÝó 3;£‡ûJ Ù7òM¶Åfxéûùýß|—Ç›7kr§xï{&Ò¯ªºÒÕÒCy®‚&‡AcWFú‡Æ×ª90TEÅ=ƒÄš\¹o²ë-›¢“º+è<¯ÓàŒܹ-¶fb}©‹z;]›8Aa•™62ª±Èˆ›†Ûìÿ¬ärýM~•ÿ³?ƒ´žo¢§×Çûvþ.n~íTÇzòoÿd¢úËŽŸÅ?O|ú/>¶_†=.žÜ7GÝQ¬ºîM©rl*ë(fFüˆ÷®–ê9§'ϳ.\|WY>™{&_ÌðíÇ-½Ö߈a[ª§‚ß·îú“m Þ”“h6‰hŸS©Ø½ºI2wcáÚSsL¾6JΞ}™§ÝZlááö«Ü´åøùÔg²‘š6Åx¸¸i^ž-”ïiN¤¹ÖìNd¦§Ši᪳ºZŠ­}Kéjš­by¹QX‹‡àäqž Å—'.æÛ18é×tÛ~îÝßs¨ñUøìÖè˜¾³ðñ[¿³Þ÷ôݺ¶íû²Šùy¤³MpJ9(¥­•°Äô‡YïV·ïNн|Ÿñß4Òé'=™ò[dÝÃ1Å14â®Ù¤tÆÆ6åùõ1†ìVMñoxb³¶”Ùz=@ÃjÝŸ ·%²[3ÚÍÉd­JèQ®_rEFcÓï›ñ'±|ïÿßÖàÖ|E¶N?ó1ßÇ“³ïñÕ=4ÿìÙÍ¥ù¸fî?Ð¾Þ í¿tî–ÅU0ßyët^-jê«dËs¨Ž­­TjÅ$¨Ö?ªtG*¦”žËn«Äy³bÛdüɯdÎÉò­|AŽtü,›.ާlFß2ÍË^Îä½Õ鮊Äé ª£I*×M‰;j£r±ÏwFôjª*ô!ø;Y‡–ó\ñ©º1Ö.·ÚÙ\Q4¯FäŸérk¹v)Álß¶ÙÙ·g íûÒëýàlüq·ÿü/ô‡¬¯òïÿ§ÿ%¿‹Î¿Ñµßädý‹¿¢ï-¹SE[Ud¼Û÷Ôi:–‚®—¾w¤P£Ö7?±÷"w*tñò2žs¥¿×aÉfI¶›-º'mÓKkIšDÏKåz‹o¶Ü¶]dMvÝlÆÈŠÍ+ZGB×´·„ûŠ¥ij­®£Ya’zI“á{aHFª.>I"ûÝqFáÖ7*çk.áºÎÄÌOw ·m'§nȦÝüǕۦ·ŠÛ«¶"|¼TþÄìèÙ¶jØ…ꢌ>Ƙr~óý—Û_]¾›¯ú6üWz-FÅýMÿž›Û$ŒœCÙ‘•XQ©¹7‚¸·˜ßf“’6·ëöójg_§WQè¥RƳ±ÔAÝݤߋ0é‡Rf“˜gÒ×å]JïÙ»¾%]¯å]ϳ‹†´Û1¿~鎦Ч ¦¤£‚‚Q´tжžUÈ‘±¨ÆµUتôL:‘&陯J}¶E±Ã‘úÍé/ÓõƒwSïkWÒRß(ê’¶…>“VúH*ZîöËæt V»«QÚÕÁZ‰‚WóUøþ\ß4ÝÑ_=*¦Åá½,¿6ÜQDÖ6Í"{«O»gCvî}¯dÞ[~ïµ·%é ú™ôwZ Y!Õ†DÁÌÔ…Ì‘¸ûZäR,×b¾/¶i1¹k¨ÓÙŸØòEmº)1Ùäk½½±xÇÓ¦ÂÝõ»CnÍaÚö¨+w-úŠž¢¦ºY’—ºgFµ³È½ËˆÞôn)助'.£6»-±}ÕºilnóÙ¨X4znU‚ùÅo ‘tÅfwFßzg¢üÝ~ =ï[„÷mÇÃ÷Z»[•õuÔö¸h¥™ê¸«äu%|*÷*ø¹Øª[Í1E-Éﯦ^£œò-EÓuøffzxb=C¹òwîüŽßXëWnÛ’Bô ¥s§…¯™S#¤[«»S1^ÕTO_EšNkX®[iöÿµ&¿E³Ã‚éžÿÄÛ»£ŒwžÝý|Þû†ÃUd²ß©h¨l/®‰ÐKV±I$²Ëˆå¸µ;°Áʸ7Õ‰µxïàÇlÖb³4èYx#A›ÌÍ}³m·DDWe"q¯ñ:ªªí>«Õ¯×ÿÞ'ÿcòoò~ÿâ{û³™ÿ›û¶ ê>âˆÞ×·i5\ßumk“çk§TSí¾äñ5ù½ñ>OŠùœÅ>oîÛü-›k³[,”PÛlöø-´5KNĉŠàž*¾j½TèôÚ\Zlq±m±ÑHQçÏ“Q|ß’éºéé¬óÜm~¬–¾åµ)ŸW;•óKO$Ô½î^ªç%<‘¢ªªâª©Ô¥ÕøO•ꯛòaŽ)ßI›kû3 ]7ˆ¹†žÈ²Ì³H눻ûQ+$þŸ¸ªX'Š-¶úY&ÌeTuµn|jäÁÄ’g·ñLZ©‘ ÿò{­˜ŒT™ñuõŽØ­ÓxJ·Å¼Î.‰œ•§G »|Ñ÷¬_Æ;ço7h«¬—m£r›ºUzË Á¨ÞŒ‘0ÍUDñbÈ­ö*u!xoúþKžûbìwàº{c'dû³«ÅN©„¾{Îô|Ó·M·ÛšØì›;cÞûøk×VÏÜ\o²7dΩ¿íºJú·5úÄGC;‘E–1ë‚xb½˜r ¾î,ø­ºî½×yí¤ýê-9Öhã‡Iˆêßi¬1O÷ÄßøOÿ¯ÿ¼ì~MþGïßüK÷g4ÿ7÷lþI·¸¿cíf\ã±Ø[Iâ§¹Fùê'l±¦85RydDø—ÃÇAáÎ_¡‹ã:EñK¶Ýucõ¦PµœïY«›g.JͳXÙIòD2 NØ´Y$–j g¶y˜Ø<ÓK;Ò6"#X×J÷ª50Obcà„ý//æ™›"k1JÌÍÓHÝÔÎÍÞhêCÔk2爋çdm¤DFÞ½‘i•ó³"mQhÂm ‡(o/ÙmµõÛéºÿ£oÅw¢Ô\_ÔßðYé½±é ]™_Ì€vd³ ™òøY#ŒGÆôV½ŽLQQz**/Š(©1UèKGÿJ£ÿQñ|˺å‡Ê³ª<ϸíÈžÙ"¶ÒÇ#É ȩਨßtô‘ŠØè2»³#gf@;2ÙÌ€vd³ ™ìÈf@;2Ù%±0åãû-¶¾»}7Ýômø®ôZ‡ú«þ =7¶4ÿÙmysql-connector-odbc-5.1.10-src/wix/resources/ConnHeader.jpg100644 15766 12 6136 11707541006 22526 0ustar00cteamstaffÿØÿàJFIFddÿìDuckyPÿîAdobedÀÿÛ„      ÿÀ:óÿÄš !AB1"aÒ#Qq‘2‚3ð¡S!1AQq¡ða±ÑÁ2‘áñ"b²BR#ÿÚ ?ýüÅŸ™Ì:ËGg™HFÉË|ÊBK\î+T,‘½ÌµY®Ž½Ü^¬·Eßî-V[¢o÷«%Ñâõd»–nˆÀú1ä-érã‘”q5Ó[°÷.l0FŠ÷*¯ >&ýµ­S½òÁ¯­Zsž=Ý¿À¡²½ÕÜ‘6Î;Ûyq´¥þÔûƒ'_2§âê­lò·ú´¹Úið›M¼=§âæ›=άf4ñå1\e©Ï{gî–"ØÈûy5ê¬×Ô±€É×ÉJ‰§mG²¼¯þ’š{½­øDÍ|§æŽ¶ÃqHÌéæ?ÆbÞ\%Ææô®6Ë©H³:’ª_§# ªÊÕÑRzïD{4^è­×™²tf#1Æ\âÙÇg>øñ„¾b®—³â¥ažÍOÌwMõV~Rþ†³ó9‡Yhìó)Ù9o™HBÉkÅj…’7¹–«5Ñ×»‹Õ–è»ýŪËtMþâõd»€{±¾“fã!ŠŒM»¸óR}¶ ‚ñê‘tE‘ÉÃÊÝSó]†º§«±ÛÅó{~˜óŸäò=Cwô+ÃõO&?µ¸¬~Õt™üœÍÎoœ¢uew ú=ñêŸØ­ª}8Úœ<¨šþZ59ÝMõç.æ}¥ë¡ýÓÆóÎ(z¶ìžDEGêb´CÑôË5Û–tn½Go~ò\/ݵŠÞ(ÜÅW7¼±í׸!N—¹4ôliýÈÜœ<Ⱥ~Z¢ú=KèOÓÜò·õ®¿÷Ç Ç)ü§¾D¼ÇO^k/¯öWéLµs˜äO,3ñé{<|’hº'%EO 5õutâ1jò—‡ú•™Æ&8LwOÊQv|TëÙ©ùŽé¾ªÏÊ_ÐÀ~g0ë-že!'-ó)Y-s¸­P²F÷2Õfº:÷qz²Ý¸µYn‰¿Ü^¬—~}ï½Óï-´c¾†~ÃÕ¯CUó»DEâ¯s¸üúºiFž”Wº?œ¾Ô7©»Ävp‡¨0ÑoÙéAr½ü:G*}&Í-„r®š¢hª'î§™©¾Ò¬âb^¦¤î/^¨µ|Ô˜í©»¡žÅKtíºÅ7#gH] 3ÌšµÍU{UZ©àº~)âŠuÖ”ÆaÚ» ÄN%º›¸bôcJW’[26õ…Èç»Á?›¸pUUÓ‚j§O¸Ó[ìõýì|Ÿ¶{Îh%‘Óâš‘·ªF$Óu·Ÿ!ÓSŠïtóÊ\êz^¾33oî\\õ·Ê35L¼KF~t|ÎO î)ÊV³þ)ïèbúx|Äf›®™ÿ— ü¼ðãs¹Ôzx95OÔg%ã _ÌwIõV~Rþ†³ó9‡Yhìó)Ù9o™HBÉkÅj…’7¹–«5Ñ×»‹Õ–è»ýŪËtMþâõd»ÁÙ-³iÛ›1b¼3>Ä÷ìHÏE¯^¹ɧOg×N¬fsË/ÞhZu§ò÷úÓ².åsÁ¸ñÙ$Èý¥wmïòñZd*Ýeõ¥DN¤w§Ô¬^¤M4æ|׬n)Y¯DÆ;q‡×þÙÙê^·ú‘lðÇV}ü¼ë'³÷SÚ^¶+-þz̃rZ£–7« óêæ*µÉë.­_Ã_‰æin)ÕlÌc³/ws³Õè¦"z»qáólp{G!Füö±Y™dm8ÙŠ’ÌoV²GºOWN¥F¢®‘ñ^_©Æ¾âœ:fö›=LÏ]g8áŸo0÷GbÛÃí½¥(Z~ë’.¬÷ø˜ìJªÏCë¾g@Šª‹?OJ¿Çͧ3VÃq[jNf:{2ó}cezh×=}¸Ïw^÷çöå¡,Ÿ–,3G®çúèæ½¨ÙZª®ëâš|O²ÚÚ&#ŸžßNßqXž}Qñy¶Rz‘L‰£doSSà«ÀÍ¥9¬J»¨Æ¤ø±>b̯ª³ò—ô0ÅŸ™Ì:ËGg™HFÉË|ÊBK\î+T,‘½ÌµY®Ž½Ü^¬·Eßî-V[¢o÷«%Üm´d©œµrœq:íy:ÚÉZ®j¢ÿÑ®jé§Äö#SêiÄÏ„øÇÎ?7‰¹ÑšêuÖ«ïï¸qR‡ >/mÅ^O·¸ÚVÒÌNNøäûíïŠ4ÁHѼõu[ËäÛ§ûu§^Žšxâsñ_à}ÎÞ²·[;ÆüŠõW9•U^MêU9q#NÑŽUø´izÞæyÞ„|”÷}ÁÜÏ®æ³wd#r§±µäTãâ•ÉG§é×âÑoXÜcõÏ—É%÷»zâ(Û«Z´°‹÷7mS²û2.š"¾FÝj*§/)JzN”Îs>_$u?qn+YŒV}óŸ‹óƒÞÍÑe«”ÍÚôY”ËuãñuáEb-‹Msìkžç}(ÕÒkªñF¢øŸE G9áÊ-¡KkîgZü«Çñìyõ±z¡‡ÿ8Ñ?讜b"^µº­2ÁùŠ ú«?)C YùœÃ¬´vy”„lœ·Ì¤!dµÎâµBÉÜËUšèëÝÅêËt]þâÕeº&ÿqz²]Ë76ëXƒ5ŒbIv†½u•U3ò×C±5È¿9¹²–·.rM:òåtïáàˆ¯UÑ’'4ií³n«ÌÚ}ï3uêw½zi1îm¬µ¬N–¢5¨š"!èUá^r™³â¥ažÍOÌwMõV~Rþ†³œÃ¬´vy”„l·Ì¤!d­ÎâµBÉÜËUšèëÝÅêËt]þâÕeº'!Ü^¬—Dd;‹Õ’è›åªÉtUþâõeº.ÿwê^n»â¥jËt¾e¡žé{>*VìÕ|ÇtßÿÙmysql-connector-odbc-5.1.10-src/wix/resources/dest_dir_folder.ico100644 15766 12 14566 11707541006 23670 0ustar00cteamstaff ¨&  ¨Î( @€  !!!"""###$$$%%%&&&'''((()))***+++...766===CBCHGHKKLMMOOOPOPQPQSPRTPSTQSUPTVPUWQVXOX[MZ_M\bN^dQafUbg[dhafhdghegiehjehjeikglnfmq`nsRmwBq8s‡+x’!žˆ®’¾ –Ä—È™ÊšË›ÌœÍ Í žÎ ŸÏŸÐ Ð¡Ð£Ñ¤Ò¥Ò¦Ó#¨Õ'«Ö,¬×0¯Ø5®Ö>¬ÐK¦Äcœ°{“›†Ž’Œ‘’’”–”•™—— ž¤¢£¨¦§««­«­°©²·¨·¾¨¾Æ¥ÅÐ¢ËØ§ÓàªÚç®ßë²ãî´åï¶çñ¶èò¸éó½êóÁìôÇîõÎñöÕôøÚöúáøûæúüêúüìûüíüüîýþïþþïþþíþþéþþäýþàýþÚýþÒûþÌúýÉúýÄùüÀ÷ü½öü»õü¹öü¶÷üµùý´úý³üý²üþ°üþ®üþ¬üþªüþ§ûý¤ûý¡ùý öü õüõüš÷ü˜÷ü—øü–÷ü•÷ü“øü’÷ü‘÷üöüõüóû‘ðû‘îú‘íúëùêùéøèøæøŽå÷Šåø‰åø‰åø‡äø„ãøƒã÷â÷€á÷ßöÝô€Úñ€Ùï~Øïz×ðtÖòqÖôoÕôoÕõnÕõlÔökÒõjÑõjÒôiÓógÔñeÓïbÑì_Ïë\Îê]ËèYÈæUÅæVÄãXÂßZ¿Ý^½Úb¼Ùeº×e¹Öd¹Õb¸Õ_·ÔY´ÓU³ÒS±ÒS¯ÑXªÑbžÑrŽÓ˜iÙË5ãó ìüîýîýîýîýîýîÿÿÿlJlnsvíVRP?48CIlosv{\h¿ÏÔÔÕÖÖÖàgcZURP@4¶Ýÿ•âùÿu×÷ÿlÔ÷ÿkÓ÷ÿkÒ÷ÿkÑ÷ÿkÑöÿjÐöÿUÅîÿ:¶ãÿ©Øÿ žÏÿ˜Êþ‹º÷r™è?TÈ£—ˆt];™ÌH ©Öÿ›Íÿ±éøÿªèùÿÞøÿtØ÷ÿnÕ÷ÿmÔ÷ÿlÓ÷ÿkÓ÷ÿkÒ÷ÿkÑöÿkÐöÿjÏöÿ]ÉòÿD¼çÿ+¯Ýÿ¡Òÿ™Ìÿ‘Áú|¥îUqÖ®: ™ÌT(®Ùÿ/±Ûÿ’ÛðÿÀïûÿ¨èùÿŠáøÿ~Ýøÿ{ÜøÿwÚ÷ÿs×÷ÿoÕ÷ÿmÔ÷ÿlÓ÷ÿkÒ÷ÿkÑ÷ÿkÑöÿjÐöÿjÏöÿdÌõÿLÀëÿ3³áÿ¥Õÿ›Íÿ{¤èg™Ì`/²Ûÿ[ÈèÿO½ßÿÁñûÿ²ëúÿšæùÿˆãøÿ„âøÿƒáøÿßøÿÞøÿ{ÜøÿxÚ÷ÿtØ÷ÿpÕ÷ÿlÔ÷ÿkÓ÷ÿkÒ÷ÿkÑ÷ÿjÐöÿjÐöÿiÏöÿaËóÿ›Ìþ'›= ™Ìo8·Þÿ€Ýôÿ žÏÿºïúÿ¹ïúÿ¥êùÿæùÿˆäùÿ‡ãùÿ†ãùÿ…âøÿ„áøÿƒàøÿßøÿÞøÿ|ÜøÿyÚ÷ÿu×÷ÿqÖ÷ÿlÓ÷ÿkÒ÷ÿkÒ÷ÿfÎôÿ+¬Øó`€×g™Ì{A½àÿŠãöÿ4´Ûÿ”ßñÿ¾ñûÿ¯îúÿ˜êùÿŒèùÿŠçùÿ‰æùÿˆåùÿ‡äùÿ‡ãùÿ…ãøÿ„âøÿ„áøÿƒàøÿ€ßøÿÝøÿ|Û÷ÿvØ÷ÿmÔ÷ÿgÑõÿPÃéì–Æú—= ™ÌŠKÃäÿç÷ÿgÐëÿP¿àÿÀôüÿ·ñûÿ¡îúÿ’ëúÿŽêúÿêùÿŒéùÿ‹çùÿŠçùÿ‰æùÿˆåùÿ‡äùÿ†ãùÿ…âøÿ„áøÿƒàøÿÞøÿvÙøÿiÓõÿVÈëì4ŸÁÀ^~Öe™Ì–TÉæÿ“ëùÿ”è÷ÿ žÏÿ¼ôüÿ¿ôüÿ­ñûÿ˜ïûÿ’íúÿ‘ìúÿìúÿëúÿêúÿŒéùÿ‹èùÿŠçùÿ‰æùÿˆåùÿˆäùÿ†ãùÿƒáùÿ}ÝøÿoÖöÿWÊëìƒàù‘–Èû ’< ™Ì¥_Ðêÿ˜ïúÿœïúÿ/µÜÿ”áñÿÆ÷üÿ¹ôüÿ£òûÿ–ðûÿ”ïûÿ“ïûÿ’îúÿ‘íúÿìúÿëúÿŽëúÿéúÿŒèùÿ‹èùÿ‰çùÿ†åùÿáùÿuÛöÿYËëìêÿ‘/žÁÆ[zÓe™Ì±j×íÿòûÿ òûÿ[ÑêÿYÄâÿÎùýÿÉ÷ýÿ·õüÿ¦ôüÿ óüÿœòûÿ™ñûÿ–ðûÿ”ïûÿ’îûÿ‘íúÿ‘ìúÿëúÿŽëúÿêúÿ‰çùÿ…åùÿ{àöÿ\Íëììÿ‘àö’—Èü ; ™ÌÀuÞðÿ¡õüÿ£ôüÿ†îøÿ žÏÿÊõûÿÝûþÿÕùýÿÈøüÿÀ÷üÿºöüÿ´õüÿ®ôüÿ§óüÿŸòûÿ™ðûÿ•ðûÿ“ïûÿ’îûÿ‘íúÿŽëúÿ‰èúÿ€â÷ÿaÐëì“íÿ‘’íÿ‘,žÁÈXuÑd™ÌÌåóÿ¥øýÿ¦÷ýÿŽôûÿGÉåÿI»ÝÿèüþÿêüþÿåüþÿàüþÿÞûþÿÛúþÿÖùýÿÍøýÿ½öüÿ«ôüÿóüÿ—òûÿ•ñûÿ”ðûÿ‘îûÿŒëûÿƒæøÿgÓëì—ðÿ‘”ïÿ‘àõ’˜Êý Ž; ™ÌØ‹ìöÿ©úýÿ§úýÿ÷ýÿŒöüÿ;ÀàÿX¿àÿŽÖëÿªãñÿ¿ëõÿÎòùÿâùýÿëüþÿèüþÿÜúýÿÆøýÿ°õüÿ¢õüÿôüÿ™óüÿ•ñüÿîûÿ‡èøÿkÕëì¢öÿ‘–ñÿ‘–ðÿ‘)ÀÌTpÏd™Ìç—ôùÿ­ýþÿªüþÿ“úþÿúýÿùýÿhÞðÿKËåÿ8¾ßÿ)´Úÿ«Öÿ ŸÏÿ™ÌÿnÉåÿçüþÿßûþÿÐùýÿÂøýÿ¸÷üÿ±õüÿ¨óüÿ ñüÿ•ëøÿt×ëì¬úÿ‘šôÿ‘—òÿ‘€àò“™ËþŒ:™Ìó¢úýÿ°þÿÿ¬ýÿÿ•ýþÿ”üþÿ“üþÿ’ûþÿ‘úýÿùýÿøýÿŽøýÿ÷üÿ‹öüÿ9¿àÿ‹ÖëÿêýþÿèüþÿâüþÿÜûþÿÖúþÿÏùþÿÆöýÿ¹ðúÿ™ÜîîÛÿÿ—´þÿ‘›õÿ‘˜óÿ‘'¢ÇÉ\z´DšÍÿ±ÿÿÿ¶ÿÿÿ¯ÿÿÿ™ÿÿÿ˜þÿÿ—þÿÿ–ýþÿ•ýþÿ”üþÿ“ûþÿ’ûþÿ‘úýÿùýÿ‡òúÿ:Ààÿ‚Ñèÿ²åòÿÑòùÿèýþÿêýÿÿçüÿÿäüþÿÝøüÿÌìööøÿÿÂñÿÿ»Îþÿ©¡øÿ‘åõ™Ìý & ŸÏÿ¸ÿÿÿ¾ÿÿÿ¶ÿÿÿ¡ÿÿÿŸÿÿÿÿÿÿšÿÿÿ™ÿÿÿ˜þÿÿ—þÿÿ–ýþÿ•ýþÿ”üþÿ“ûþÿùýÿTÑèÿ6½Þÿ¬Öÿ›Íÿ,­ÕÿW¿àÿ€Ñèÿ¦àðÿ½åòúíùûÝùÿÿÛóÿÿØÛÿÿʲúÿ¡'¯Ø½”ÅT¥ÒÿÁÿÿÿÇÿÿÿ¾ÿÿÿªÿÿÿ§ÿÿÿ¥ÿÿÿ£ÿÿÿ¡ÿÿÿ ÿÿÿ¢ÿÿÿ¨ÿÿÿ²ÿÿÿ¸þÿÿ¸þÿÿ³þþÿªýþÿ¡üþÿšûþÿ”úýÿ|í÷ÿbÜðÿGËèÿ(µÜÿ–Èú—Êí¢ÐùB´ÚñuÊååÚíÒjÏ輙̄"ªÔÿÎÿÿÿÓÿÿÿÉÿÿÿ´ÿÿÿ®ÿÿÿ¬ÿÿÿªÿÿÿ©ÿÿÿ«ÿÿÿ³ÿÿÿÃÿÿÿÕÿÿÿãÿÿÿèÿÿÿçÿÿÿáÿÿÿÙÿÿÿÏþÿÿÅþþÿ¶ýþÿùþÿóþÿgáôÿ޽ã™Ì™Ì6™Ìf™Ì™Ì„™Ì$ªÔÿÞÿÿÿåÿÿÿáÿÿÿËÿÿÿ¾ÿÿÿºÿÿÿµÿÿÿ²ÿÿÿ³ÿÿÿ·ÿÿÿÄÿÿÿÙÿÿÿêÿÿÿóÿÿÿöÿÿÿõÿÿÿôÿÿÿòÿÿÿïÿÿÿèÿÿÿ¿þÿÿùþÿkåõÿ”Åڙ̺­ãñÿöÿÿÿùÿÿÿïÿÿÿæÿÿÿáÿÿÿÜÿÿÿÒÿÿÿÁÿÿÿ´ÿÿÿ’èôÿ žÎÿ/­Öÿ^Áàÿ‚Ïçÿ§ßïÿÍî÷ÿõÿÿÿõÿÿÿóÿÿÿáÿÿÿ«ÿÿÿvêöÿ˜ËÓ™ÌQ4¯×ÿýÿÿÿÿÿÿÿúÿÿÿöÿÿÿöÿÿÿòÿÿÿæÿÿÿÌÿÿÿ¯úýÿ§Óÿ—Ê^™Ì3™Ìc™Ì‡™Ì®™ÌÕ™Ìÿ+«ÕÿP»ÝÿoÉäÿ‚ÙìÿlÜîÿ™Ì¨™Ì–lÄâÿ˜Öëÿ§ÝîÿÅëõÿÖòøÿôÿÿÿëÿÿÿØÿÿÿLÁàÿ™ÌŠ™Ì-™ÌT™Ìx™ÌŸ™Ì¨™Ì?™Ì™Ìl™Ì™™Ì«™ÌÌ™ÌÞ™ÌÿŸÏÿ-­Öÿ™Ì–™Ì™Ì3ÇÿÿÿÀÿÿÀÿÿ€?ÿÿ???€à€ÿÿÿ?ÿÿÿÿÿÿmysql-connector-odbc-5.1.10-src/wix/resources/help.ico100644 15766 12 1376 11707541006 21443 0ustar00cteamstaff è( @€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿwwwpwˆÿø‡wxÿÿÿÿÿˆwxÿÿÿÿÿÿÿˆpÿÿˆDDDHÿÿ‡ÿøDÇwÄÄHÿøpÿøDLxø|LDOÿwxÿ„ÄÄÿtÄÄÄÿ÷pøLLLxø|LLLOøp÷DÄÄÇwÄÄÄÄHÿwÿ„ÌLLLLLÌLLD‡ÿtÄÌÌȈ|LÄÄÄ÷xøLÌÄÄÈÿ|ÌLÌLO÷pøLÌÌÌÈÿ|ÌÌLÌOøpøLÌÌÌÈÿôÌÌÌÌOøpøLÌÌÌÌÿLÌÌÌOøpøLÌÌÌÌÇÿôÌÌÌOøpøLÌÌÌÌÌÿLÌÌOøpxøLÌÌÌÌÌÿ|ÌÌOøpÿ„ÌÇw|Ìxÿ|ÌÄ÷ÿ„ÌÇÿŒÌÿ|ÌćøLÇÿøwÿLÌHÿ‡ÿLÌÿÿÿøÌÌOøpxÿ„ÌÇÿÿÿwÌÄÿøpÿÿDÌwwwÌÄOÿ‡ÿˆDÌÌÌÄHÿÿpÿÿˆDDDHÿ‡xÿÿÿÿÿÿÿøpxÿÿÿÿÿøwwˆÿø‡wwwwpÿðÿÿ€ÿþÿüø?ðàÀÀ€€€€€€ÀÀàðø?üþÿÿ€ÿÿðÿÿÿÿÿmysql-connector-odbc-5.1.10-src/wix/resources/MySQLConnector.ico100644 15766 12 41646 11707541006 23357 0ustar00cteamstaff ¨F hî  ¨V 00 ¨%þ( @€       #(- 0"4%8(:*;+=,?-A.B0E1 G3!I4"M7#Q:%U<'X?)[A*]C+`E,bF-fI,jM)mO&tUyZ_ ‚a ƒc „c †d ‡e‰fŠfŒg(h0h2“l.”n(•o"–p•q–r—s™tšu›vœxyŸ|ž|Ÿ~#¡€(¢+£ƒ*¤„(¥„'§…&¨†%©†%ª‡&«ˆ(­‰*®Š,¯‹.°Œ0±Œ1±Œ2°Œ4±Œ5²‹8°9¯Ž:¯;¯=®‘=®‘=¯’=°’=±“=³”=µ•>¶•@·•A¹•B¹•C¹”D¹–F·—Gµ—I´•J²“L¯N­ŒO¯P°S°”T±˜U±™W²›X³œY³Z´ž[µž[·ŸZ¸ [¹¡Z¼¢X½£W¾¤W¿¢UÀ¡TàUÆVÉ›WÌšXΙYЙZј\Ñ—]Ó˜^Ô™^Õš`Öšb×›dÚœeÜgÝžhÞžhÞŸiߟiß jÞ jÞ¢lÜ¥nÛ§oتoÔ¬mÏ®i̯hʯgÈ®fÇ®gÆ­gÅ­fìhÁ«iÀ«jÀ«mÀ¬oÀ®t¯tİtűtɳuÊ´v˶wÌ·y̸zÌ·{Ì·{̸|̹~̹€ÍºÌ¹‚λƒÐ»„л…Ï»†Ï»‡Ï»ˆÏ»‰Ï¼‹Ð¾ŒÏ¾ÐÀŽÑÀÒÁÓÂÔÃŽÖÃ×ÄØÅØÆ’ØÇ”ØÇ–×Ç™ÖÇ›ÖÇœ×È×ÉžÙÊ ÚË ÛÌ¡ÜÌ ÜÍ ÜÍ¡ÝΠÞΟàÏ àСáÑ¢âÒ¤ãÓ¥äÔ¦ãÔ¨áÓªßÓ­ÞÒ¯ÝÒ°ÞÓ±ßÕµáØ»ãÚÀåÝÄæÞÅèàÉéâÌëäÐìçÕïëÜñíàóðåöóê÷õíÿÿÿ z”¤¤¥¥¤£” |¥ÇØààáàÝ×Ë¥£ z¤ÊÜàãäåååáà×ÓÆ¦¢ z¥ÖØÝàãåææææäàÖÒĸ¢ |ºÏÓרàáäæçççæäàÔÆ»©™ |¥ÃÎÑÔÖ×àäåæççæääÖÒ¾·Š˜ |£¸ÀÄÇÑÓÕ×äåçèèèæäÖÔĹ­Œš ¡±¶¹¾ÄÄÒÔÕäæéêêêéåÖÔĺ¬Œr| |™¯®®®®¶»ÒÕäèêëëëêæÒ¿¸«Šu`’  …Š®¼ÊÐÙʃ¯¸ÀÈÍÏÍ´²ÐÞÛй‹^cz |˜xˆ¹ìúþ÷óúùûüüýýüúúó÷þýë«`[ž ”Žjv®Îðßóþþþþþþþþþþþþóïõ×­`Z‘ ™ubjlli~ýýôïʃÀdzÊïôýý††Œt`ZsA ›eS]amm¶úö²öõ¼êëÌõö³÷úÏ­q^Xdz ›ROSW\^‚÷ö³óÚƒ¿Á°Üô³ö÷³‹Œn^Wdz ›RHMVk…~ÂÀÙÚÂâììâÂÚÙÂʆ´Šn\Ncz œR;H]¯ÑâÐÚâ³âííííâ³âÛÏãà»rXNsB œq:H`ŠÅàÝÆ°gw„…†‡‚¶ÏÞã×·nNM“  •9;PSSQPUmŒ¬©§§º§§ŠvigjpdNM˜ ŸE:SeluŒ‹‹ŒŠ¬ª©©©©©ª®‹uaL`| œ{7QwvŒ‰Š­©·¹»»¾¾»¹·©­Š‰UI— ŸE:~†ˆ¯®«·¹º¾¿¿¿¾º¹·«®uHc”  —;E²²µ·¹¼¿ÃÄÄÅÄÄÿ¼¹¯HNž ž7RÂÂÇÉËÎÏÐÐÐÐÐÏÎË¿KI™”  :RÍÚÚÚÛÜÝÝÝÝÝÜÛÀHG—•  •F=³ïïïïðððððß~;Qœ˜   s=EƒÏïôôÞÊyÿšzÿ¦‰/ÿ±œZÿǸˆÿ²ƒÿ²}ÿĵÿÁ³ƒÿÆ·‡ÿº¨mÿ­:ÿ¤ÿ¶„2ÿЕc‘Ó—di·…BÿŽm ÿµ›KÿƳyÿðwÿ­˜Tÿ¾«mÿÁ­qÿ°œ[ÿǵ~ÿȵ~ÿÀ§]ÿyÿ¾‰AÿÓ—diЕcÔ˜_ÿ…h ÿ¡†3ÿ¦Š6ÿ©Œ5ÿ±”>ÿ¶™Dÿ¸Iÿ¶›Iÿ°”Bÿ¬‘?ÿ¬‘=ÿ”q ÿÖ™_ÿЕcÚœg—¡v-ÿ¤Hÿ¸¢^ÿ¿¨cÿÆ®kÿȱoÿʳrÿɲqÿÆ®kÿ¿¨cÿ¬‘Bÿ¨z,ÿÚœgšÛœh+Üfÿ’q&ÿºªwÿɹ‹ÿͼ‹ÿϾŒÿÑÀŽÿϾŒÿͼ‹ÿ¾¬tÿ–sÿÜfÿÛœh.Ýži^Üfÿ¡{7ÿ²£qÿ×βÿÜÓ¸ÿÜÓ¸ÿÖÌ­ÿ­š_ÿ¡x-ÿÜfÿÝži^à j0ÞŸišÒ—^ÿª<ÿ¬‹Oÿ­ŒOÿ©|7ÿÒ—^ÿÞŸišà j0à jà jxà j™à j™à j{à jàÿÿÀÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿ€ÿÿÀÿÿàÿÿøÿÿ( @ € %)($ &7GU^cb\RD3"'A^x>-¡A.°pP5ÇbF/Æ=,¹*¯™ˆrW;! 6Z\B, ŸrKØÑ•cøÞ§rÿÞ¨sÿݯ{ÿÝ­yÿÞ§rÿÞ¤nÿÄŒ]÷„^>á2$½˜wQ. @vT8š¸ƒWãݪuÿÛ¹‡ÿÛÇ™ÿÛÌ ÿÜÌ ÿÜÍ ÿÛÌžÿÚÊšÿØÈ–ÿؾˆÿÙ°xÿݤmÿ“iEé8(¿a7 C¨xOÄݦqÿÙ¼ŠÿÙÉœÿÛÌ ÿÝ΢ÿÞΣÿÞϤÿÞÏ£ÿÞÏ¢ÿÝÍ ÿÜËœÿÚÈ–ÿ×ÄÿÓ¿†ÿÕ®tÿÞ¢jÿ„^>à˜h8kM3^«zQÄÙ­xÿÕÄ”ÿØÈ™ÿÚÊœÿÜÌŸÿÝΡÿÞÏ¢ÿßУÿàФÿàУÿàТÿÞΟÿÜÌšÿÚÈ”ÿÖËÿÒ½ÿÍ´rÿÚ£iÿfDæ™c1K5#?°}S¿Õ°zÿÓÁÿÕÄ’ÿØÇ–ÿÚÉ™ÿÜË›ÿÝÍžÿÞΟÿàТÿáÑ£ÿáÑ£ÿáÑ¢ÿàСÿßÎÿݢÿÙÆÿÕÀ†ÿϹxÿȰiÿÔ¢cÿgDäW% ¸„W¶Ôªrÿλ„ÿÒÀŠÿÕÂŽÿÖÅ‘ÿÙÇ“ÿÛÉ–ÿÝÌšÿßÎÿßÏžÿáРÿáÑ¡ÿáÑ ÿáРÿàÎÿÞÌ™ÿÛÉ“ÿ×ÊÿÒ¼|ÿ˳mÿé\ÿÖŸaÿ‰bAÜ}A¹„X•פlÿÈ´xÿ͹ÿмƒÿÓ¿‡ÿÕŠÿØÄÿÚÇ’ÿÝË–ÿÞ͘ÿàÏÿâÑŸÿâÒ¢ÿãÒ¢ÿâÒ¡ÿâОÿßÍšÿÜÊ“ÿÙÅ‹ÿÔ¾€ÿ͵oÿŪ]ÿ¿ŸMÿÚžcÿbF/¿`( Y?*&Þ jÿĪlÿDzsÿ˵wÿθ{ÿлÿÓ¾‚ÿ׈ÿÚÇŽÿÜÊ’ÿßÍ—ÿáМÿäÓ¢ÿåÕ¤ÿæÖ¦ÿåÕ¥ÿäÓ¢ÿáÏ›ÿÞË”ÿÚÆ‹ÿÕÀÿÏ·qÿÆ«^ÿ½ŸJÿ¿—Cÿ³€Uì|;À‰[ŒÍ dÿÀ©eÿ¬hÿìiÿ¬iÿíjÿƱpÿ͸zÿ×ÇÿÜÉ‘ÿàÍ—ÿãÒŸÿæÖ¥ÿèØªÿéÙ«ÿèÙªÿçÖ¦ÿáМÿÖÊÿϺ~ÿÊ´sÿÅ®gÿÁ¦Wÿ¼žGÿ²’2ÿÒ™WÿX?*°N0#ß jÿºžXÿ½¦^ÿílÿɵ|ÿͽ‹ÿЖÿÒÅ›ÿÊ»ÿ´ cÿº¦gÿÇ´{ÿ˹ÿνˆÿÐÀÿÑÁÿÐÀÿÁ±zÿº©pÿÏ—ÿ×˦ÿÕÈŸÿÒ“ÿ˶wÿ¿¢Nÿ³‘/ÿ»Ž7ÿ¬{Qâ^%½‡YGÓœbÿ²™Lÿ»¢Wÿ˶zÿÞÑ©ÿðêÖÿù÷ðÿæâÓÿÜØÅÿëèÝÿêæØÿðìàÿóðåÿôñæÿõòçÿõòèÿòîãÿìèÛÿëéÝÿÜØÅÿåâÓÿûúöÿ÷óæÿãÕ­ÿÇ®cÿ´“2ÿ­‰$ÿÞŸgÿ o- Γb‚Ä—Vÿ­“BÿµšJÿìgÿÓÀŒÿàÔ±ÿÕ̬ÿàÚÄÿûùõÿüúöÿüúöÿüúöÿüúöÿüúöÿüúöÿüúöÿüúöÿüúöÿüúöÿüúöÿßÙÄÿ×ϱÿçÞÁÿÙÉ—ÿŪ\ÿ´“3ÿ¬ˆ"ÿÏ—SÿfI0˜2 Øšf®¶ŽGÿ¨Œ6ÿ¯’:ÿ°“;ÿ°“>ÿ®‘?ÿ¬™Tÿöòçÿ÷óéÿâÛÅÿÕͲÿøÿ³¡dÿȸÿË»‡ÿº«sÿĹ‘ÿÕͲÿâÛÆÿ÷óéÿöòçÿ¶£gÿ¸ Vÿ¼¡PÿºœDÿ´“2ÿ«ˆ!ÿÆ’Eÿ‹cB®3 ÜhÁ¬‡;ÿ¢„*ÿª‹.ÿ¯3ÿ´”9ÿ³•<ÿųyÿñëÙÿèàÆÿ·¦oÿèàÈÿèÞÄÿǵ{ÿèצÿëÛ¬ÿξŠÿèßÅÿèàÈÿ½­{ÿéâÉÿñëÙÿÐÁ‘ÿĪ`ÿÅ©Vÿ¼Bÿ³‘/ÿª†ÿ¿;ÿ¢sL»1 ÜhÆ¥€1ÿ›|ÿ£‚ ÿ§‡%ÿ«‹+ÿ«Œ0ÿ³WÿëâÉÿêáÇÿ»¬xÿæÜ¿ÿÔÇ ÿ²Ÿ_ÿʸ~ÿ̺ÿ¶¥jÿÕÉ¢ÿçÝÀÿ¾¯~ÿêáÈÿëâÉÿÀ­rÿ»¢Uÿ½ Lÿ¸˜<ÿ°Ž+ÿ¨„ÿ½9ÿ§wO¹*Ühº£|,ÿ“sÿœyÿ§†&ÿ°’;ÿ·žQÿ®™SÿƶƒÿôÿÐÛÿÓÅžÿŶ„ÿÛΦÿÝѪÿÞѪÿÜϨÿŶ†ÿÓÆŸÿÑÄ›ÿŶ…ÿʼÿ¸¤gÿïnÿ¿¥Yÿ·˜=ÿ®‹&ÿ¦ÿ¿Œ;ÿ¨xO¬!Ýži˜©}0ÿŠiÿ•rÿª‹-ÿ¨`ÿÔÂÿÜͤÿЖÿÕÆœÿÛÍ¥ÿ¾­xÿÜϦÿàÓ¬ÿàÓ¬ÿàÓ¬ÿàÓ¬ÿÛΤÿ¿¯{ÿÛͤÿÖÈÿÏÁ•ÿÝϦÿÜÌ ÿθxÿº›Cÿª†ÿ£~ÿÇ‘Fÿ˜mH‰Ýžid¹†Aÿ…dÿnÿ«2ÿÀ¦]ÿѾ…ÿÛÌŸÿÚÊÿϼ…ÿÁ¬kÿ¥?ÿ¬•JÿµžUÿ· Zÿ¸¢\ÿ¹£]ÿ³Wÿ³œUÿƲvÿÒÁŽÿÚËžÿÝΣÿÙÈ—ÿʳoÿ¶—=ÿ¦‚ÿŸzÿÓ˜XÿtS7Wà jÔ˜^ÿ‚bÿ‹jÿŸ$ÿ¢…*ÿ£†-ÿž'ÿŸ%ÿ¥ˆ+ÿ²”:ÿÁ£NÿÇ«Zÿ˱dÿδjÿ϶mÿзnÿ϶mÿδjÿÀ¦Yÿ´šIÿ­‘@ÿ«>ÿ­’Aÿ°•Aÿ°‘6ÿ¢}ÿ£{ÿÖ™eë ߟj¾–oÿ„dÿ£†1ÿ«Ž7ÿ±”=ÿ¶˜Cÿ»žLÿ¾¢Qÿ¾¢OÿÀ¢NÿçUÿÄ©WÿÆ«ZÿÉ®_ÿʯbÿ˱eÿʰdÿʱgÿʱiÿȯeÿé]ÿ¿£Tÿ»žKÿ°3ÿx ÿ¸‡3ÿ¿ˆZŸÝžiUÄMÿ`ÿš}(ÿ¯•Iÿ´šKÿºŸRÿ½¢VÿÁ§]ÿÄ«cÿȰjÿ˳pÿͶuÿϹwÿкzÿѺ|ÿк{ÿϹxÿͶuÿ˳pÿȯiÿÄ«cÿÁ§]ÿ½£Wÿ¦„ÿ™tÿØ›`ÿ}Y;6ߟjµ˜pÿ„eÿ¯˜Sÿ¸¢]ÿ¼¥`ÿÁ©dÿĬhÿǰmÿɲqÿ̵uÿÍ·xÿϹ{ÿϺ}ÿϺ}ÿϺ}ÿϹ{ÿÍ·xÿ̵uÿɲpÿưlÿÄ­iÿµ™Gÿ–rÿ¹‡7ÿÉ_ à jKÖ™`ÿ†d ÿ‘tÿ½ªqÿÁ­qÿİtÿdzuÿʵzÿÌ·|ÿ͹~ÿÏ»ÿмƒÿѽ„ÿѽ…ÿнƒÿм‚ÿÏ»ÿ͹~ÿÌ·|ÿʵzÿÀ©cÿ—rÿ¤yÿÞŸhÿ‚]>ÞŸi|Ì“Vÿ_ÿœ‚6ÿÇ·‡ÿɸ‡ÿÌ»‡ÿν‰ÿп‹ÿÑÀÿÒÁŽÿÓÂÿÔÂÿÓÂÿÔÂÿÓÂÿÒÁŽÿÑÀÿп‹ÿÊ·ÿ™xÿ™qÿÙ›aÿÊ`]à jÉ‘Tÿ„b ÿ™€4ÿÍ¿—ÿÒÅÿÔÆÿÕÇÿÖÈžÿ×ÉŸÿØÊ ÿÙÊ ÿØÊŸÿÙÊ ÿØÊ ÿ×ÉŸÿÖÈžÿɶ€ÿ—vÿ›sÿÖš_ÿÓ—dyà j~Ó˜^ÿkÿ‡kÿ½¬yÿÚвÿÛѲÿÜѲÿÝÒ²ÿÝÓ²ÿÞÓ²ÿÞÓ²ÿÞÓ²ÿÝÓ²ÿØÌ¨ÿ°™Rÿ‹jÿ§{(ÿÝžfÿÕ˜eeà jZà jþ‰Hÿ‰gÿŒr ÿ±ždÿÍÁšÿÛÒµÿãÜÅÿäÜÅÿ×Ë©ÿȹ‹ÿ©‘Jÿ‹lÿšrÿΔVÿÜh¢Õ˜e9à jlà jÕÍ“Wÿ¬~6ÿ‘lÿjÿ€`ÿ…d ÿ‘lÿ™qÿ¶„=ÿÕ™_ÿߟj¾ÝžiUà j-à j~à jÃà jÌà jöà jêà jÌà j·à jlà jÿÿüøðàÀÀ€€€€ÀÀàðø?üÿÿÿÀÿ(0` €%   (2;BGJJGB<3)  &8J\lx„††…xm]L:( '>Xo‚‘¥0#¶=+½=+½2$»¬©¥’„qZ@(  8Vs‹+«€\=Ò´UëØšfüà jÿߦqÿÞ¨sÿÞ¨sÿߦqÿà jÿÚœgýµVî~Z;Û.!¿« ŽvX: 'Fj ‹wU8ÇË\ñߥpÿÝ´‚ÿÜÀ‘ÿÛÊÿÛÌŸÿÛÌŸÿÛÌžÿÛËÿÚÊ›ÿÚÉ™ÿØÆ”ÿÙ¼‰ÿÛ±{ÿÞ¥oÿÊ`öxV9Ù ´¡‹mI)-Pv‚]>Åà¡kÿܰ~ÿÚÓÿÚËžÿÛÌ ÿÛÌ ÿÜÌŸÿÜÌŸÿÛÌžÿÜÌžÿÛËÿÛËœÿÛÊšÿÚÉ™ÿÙÇ–ÿØÆ“ÿÖÃŽÿÖ¼…ÿÙ­uÿß¡kÿ~Z<Ú­™yT/.UwU9®¿ˆZéÜ­yÿÙ“ÿÙÊœÿÚÊÿÚËÿÛËÿÛÊ›ÿÚÊšÿÚÊšÿÚÊ™ÿÛÉ™ÿÛʘÿÚʘÿÛÉ—ÿÚÉ—ÿÙÈ•ÿÙÆ“ÿ×ÅÿÖÃŒÿÓÀ‡ÿÓ¹~ÿÙ©pÿÄŒ]ôrR6Ô Y1+U”iF¿Þ¤nÿ×¼‹ÿ×Ç—ÿØÈ˜ÿÙÈ™ÿØÈ˜ÿÙÇ–ÿØÇ•ÿØÇ“ÿØÆ’ÿÙÇ’ÿÙÆ’ÿÙÇ’ÿÙÇ’ÿÚÇ’ÿÚÇ’ÿÚÇ’ÿÙÇ‘ÿÙÆ‘ÿØÅÿÖÄŒÿÕÁˆÿÓ¾ƒÿк}ÿϲrÿÝ¢jÿ‰bAݤƒX. %N£tMÆÝ¦qÿÔ¿ÿÕÅ”ÿÖÅ”ÿÖÅ’ÿÖÅ‘ÿÖÃŽÿÖÃŒÿÕ‹ÿÕ‰ÿÖÊÿ×Êÿ×ÄŠÿØÄŒÿØÅÿÙÆÿÙÆŽÿÙÆŽÿÙÆŽÿÙÅÿØÅŒÿ×ÊÿÕˆÿÓ¿ƒÿѼÿθwÿ˱oÿÙ¤iÿ“iEᤀR( CªzQÅÙªuÿѾ‹ÿÓÂÿÔÂŽÿÔÁŒÿÓÁ‰ÿÓ¿†ÿÒ¾ƒÿÒ¾‚ÿÓ¾ÿÔ¿‚ÿÕÀƒÿÕÁ…ÿׯÿ×ÇÿØÄˆÿÙÄŠÿÙÅŠÿÙÅ‹ÿÙÅ‹ÿÙÅ‹ÿØÅŠÿ×ÉÿÖ‡ÿÔÀƒÿÒ¼~ÿϹxÿË´qÿȯhÿÔ¤dÿ•kGáŸwG2ªyPºÙ§qÿϼ†ÿоˆÿѾ‡ÿѽ…ÿмÿл~ÿϺ|ÿкzÿѺzÿÒ¼{ÿÓ½}ÿÕ¿€ÿÖÁÿ×Áƒÿ×Ã…ÿØÃ‡ÿÙćÿÙĉÿÙʼnÿÙʼnÿÙʼnÿØÄˆÿØÄ‡ÿ׆ÿÕ¿‚ÿÓ½~ÿйxÿͶqÿɰiÿŪ`ÿÓ¢bÿ’hEÝ–h7!§wO¡Û£mÿÌ·ÿͺ‚ÿκÿι~ÿ͸zÿÍ·vÿͶsÿͶrÿζsÿиuÿÒ»xÿÓ½{ÿÕ¿~ÿÖÁÿØÂƒÿØÃ†ÿÙćÿÚʼnÿÚÆŠÿÛÆ‹ÿÚÅŠÿÚʼnÿÙĈÿØÃ†ÿׄÿÖÀ‚ÿÔ¾}ÿѺxÿζqÿʱiÿÆ«`ÿÂ¥UÿÖŸbÿ‡a@Õ†S% „^>fÞ¡jÿʱvÿ˶{ÿË·{ÿ˶xÿË´sÿʳpÿʲlÿʲkÿ˳kÿ͵oÿиsÿÓ»wÿÔ½{ÿÖ¿ÿ×Á‚ÿÙĆÿÛÆˆÿÜÈ‹ÿÝÈÿÝÈÿÝÉŽÿÜÈÿÜÇ‹ÿÚÆŠÿÙćÿØÂ…ÿÖÁ‚ÿÕ¾~ÿÒ»xÿηqÿʱiÿƬ`ÿÁ¦Vÿ¿ŸMÿÚŸdÿaE.¼p; ¿‰[ÁÍ©mÿƲtÿȳtÿȲrÿDZmÿǯiÿÇ®fÿÈ®eÿȯeÿ˲iÿεmÿѸsÿÒ¼wÿÕ¿|ÿ×ÁÿÚĆÿÜÇŠÿÞÉŽÿßË‘ÿàÌ’ÿàÌ“ÿàÌ“ÿßË’ÿÞÊÿÝÈÿÛÆŠÿÙĆÿ׃ÿÕ¿~ÿÓ¼yÿзrÿ̲iÿƬ_ÿ¦Tÿ½žHÿÁšIÿ³€UéˆS#pJoÙ¡iÿÁ¬kÿÅ®nÿůlÿÅ­hÿÄ«cÿÄ«_ÿĪ]ÿÆ«^ÿÈ®aÿ˲gÿ϶nÿÒºtÿÔ½zÿ×À€ÿÙÄ…ÿÜÇ‹ÿÞÊÿàÍ“ÿáΗÿâЙÿãКÿâКÿáϘÿàΕÿßË’ÿÝÉŽÿÛÆ‰ÿØÃ„ÿÖÀÿÔ½yÿѹtÿ̳jÿÇ­_ÿÂ¥Tÿ¼žGÿ·—;ÿÓ›ZÿuS7Ãj4Ì’`ÉÆ¤dÿÀ©fÿ«gÿªcÿ¨^ÿÁ¦Zÿ§XÿçXÿŪZÿÈ®`ÿ̲gÿзoÿÓ»vÿÕ¿}ÿÙÄÿÜÇŠÿßËÿáΖÿãÑ›ÿåÓžÿæÔ¡ÿæÕ¡ÿæÔ¡ÿåÓžÿãÑœÿáÏ—ÿàÌ’ÿÝÈÿÚŇÿ×ÁÿÔ¾zÿѺsÿͳkÿÈ­^ÿ¦Sÿ¼žGÿ·–9ÿ¹‘8ÿß iÿ€E£tMWÕ fÿº¤^ÿ½¦`ÿ½¤\ÿ»¡Vÿ·žPÿ¶šIÿ³˜Gÿ³™Fÿ¶›Kÿ¹ Rÿ¾¥ZÿɰhÿÓ¼wÿ×Á€ÿÛÆˆÿÞÊÿáΖÿäÒœÿæÕ¡ÿè×¥ÿ騍ÿéÙ©ÿ騍ÿè×¥ÿæÕ¡ÿäÒœÿÝÊ’ÿϼÿɵuÿƲoÿÄ­hÿÁªcÿÁ¨_ÿ¾¦Wÿ½ MÿºœBÿµ•6ÿ¯Œ)ÿΗQÿiK2²U# Í’a±Ä]ÿ¸ Wÿ¼£ZÿÁ¨cÿưnÿ˸~ÿÑÁŽÿ×É ÿÜЮÿÞÓ³ÿßÕ¶ÿÞÓ²ÿʺ†ÿµŸ\ÿ­iÿ˶xÿͺ}ÿϽ„ÿÒÁ‰ÿÔÃŽÿÖÅ‘ÿׯ”ÿ×Ç•ÿׯ”ÿÖÅ‘ÿÕÄÿʸÿƶÿÞÔ³ÿåÝÃÿäÛÁÿãÚ¿ÿâØºÿÝÑ«ÿÖÅ’ÿ̵tÿÁ¤Rÿ¶–7ÿ°Ž*ÿ¹Ž4ÿ¸ƒWãc- [A+Þ iÿ³™PÿµœOÿº TÿëgÿκÿÚÌŸÿçÞÀÿòîÞÿûúõÿýýûÿùùõÿèæÚÿØÓ¿ÿØÑºÿÎÄŸÿÝÔ¸ÿæßÆÿçàÈÿèáËÿéâÌÿêãÎÿêäÏÿêäÏÿêäÏÿæßÉÿÔ˪ÿÕͱÿÛ×ÅÿßÛËÿïíåÿýýûÿýýûÿüû÷ÿôðàÿèݼÿØÆÿÇ­`ÿ¸—:ÿ±,ÿ¬ˆ"ÿÛžcÿ'z4‹\SЛ`ÿ¯•Hÿ²˜Hÿ·œLÿ¿§^ÿ˶yÿØÈ˜ÿåÚºÿñëØÿù÷îÿíëßÿÕкÿéæÙÿûùõÿýüùÿýüùÿýüùÿýüùÿýüùÿýüùÿýüùÿýüùÿýüùÿýüùÿýüùÿýüùÿýüùÿýüùÿýüùÿóñêÿÛ×ÃÿåâÓÿûúõÿúøòÿóíÜÿçܹÿØÅŽÿÆ­aÿ¸˜;ÿ±-ÿ¬ˆ"ÿÌ•MÿyW9¦:Ñ•cŠÃ–Uÿ«@ÿ°”@ÿ³—DÿºŸRÿëfÿκÿØÈ›ÿáÕ²ÿâØ½ÿÊ¿›ÿïëÞÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿúøñÿù÷ïÿØÐ¶ÿÙÒµÿëäÍÿæÛ¼ÿÝΡÿѽÿÅ©Zÿ¹˜;ÿ²-ÿ¬‰"ÿ¾Ž:ÿ§wOÉ=Øšf®¸Jÿ¨Œ9ÿ«8ÿ¯‘7ÿ­Ž4ÿ¬1ÿ«‹0ÿ«2ÿ«Ž5ÿ¢‡3ÿÑÅœÿöòçÿöòçÿöòçÿîéÚÿàÚÄÿàÙÄÿÕÌ­ÿÐÛÿÚͨÿÛϪÿÜÑ­ÿÝÒ°ÿÜѯÿÎÄŸÿßÙÄÿàÙÄÿåßÌÿõðåÿöòçÿöòçÿòíÞÿ®›[ÿ»¤]ÿ½¤Zÿ½¤Uÿ½ MÿºDÿ·—9ÿ±-ÿ¬ˆ"ÿ´‰,ÿÁŠ[ß?ÜhЬŠ=ÿ¤‡1ÿ¨Š0ÿ«‹0ÿ­/ÿ¯Ž/ÿ±1ÿµ”6ÿ¸˜<ÿ¬‘=ÿïêÖÿòíÜÿòíÜÿÚЯÿȼ–ÿäÝÇÿäÝÈÿÞÕºÿ¿­pÿÚÉ•ÿÜÌšÿßÏŸÿàÑ¢ÿÔÅ”ÿÏÁ˜ÿäÝÈÿäÝÈÿß׿ÿƺŽÿòíÜÿòíÜÿòíÜÿÔÇ›ÿÄ­fÿεjÿÉ®_ÿæQÿ½žDÿ·–8ÿ±,ÿ«ˆ!ÿ«… ÿÕ˜eó>ÞŸi㣃2ÿ¡ƒ*ÿ¤…(ÿ¨ˆ(ÿª‰(ÿ¬‹(ÿ¯Ž,ÿ³’2ÿ´”7ÿ³™OÿîæÑÿîæÑÿîæÑÿ¾¬rÿÍ¿‘ÿðéÕÿðéÕÿïèÔÿ°tÿæÕ¡ÿèØ§ÿêÚ«ÿìÜ®ÿÚʘÿÝÑ­ÿðéÕÿðéÕÿïèÓÿ®œ\ÿçÞÃÿîæÑÿîæÑÿÛΨÿÀ©`ÿͳhÿȬ\ÿ¤Oÿ¼œBÿ¶•6ÿ°Ž*ÿª‡ ÿ§‚ÿÜhú<ߟjîœ}(ÿœ}"ÿ  ÿ¤ƒ!ÿ§…"ÿªˆ$ÿ­‹(ÿ±.ÿ³“4ÿ­’CÿêàÆÿêàÆÿêàÆÿƵÿɹŠÿìäÍÿìäÍÿéàÆÿðsÿäÒœÿæÕ¡ÿè×¥ÿ騍ÿÙÉ–ÿØË¢ÿìäÍÿìäÍÿêàÈÿ¯`ÿèÝÂÿêàÆÿêàÆÿÕÆ™ÿÀ¨^ÿ̲eÿƪXÿÀ¢Kÿº›?ÿ´“3ÿ¯Œ(ÿ©†ÿ¤€ÿß iÿ 9ߟjåšy#ÿ—wÿœ{ÿ ~ÿ¤ÿ¦ƒÿ©‡!ÿ¬Š(ÿ±/ÿ¤‡.ÿØÉžÿæÛ»ÿæÛ»ÿÔÆ›ÿÎÁ›ÿéßÄÿéßÄÿÌ¿”ÿ­šWÿÁ®pÿ°tÿIJvÿŲyÿ½¬pÿ¸§qÿÚϯÿéßÄÿâØºÿÁ³‚ÿåÚºÿæÛ»ÿæÛ»ÿ¿¬mÿª_ÿŪ\ÿÁ¥Qÿ¼ŸFÿ·—;ÿ³‘/ÿ­Š%ÿ¨„ÿ£~ÿà jÿ0 ߟjÙ›v!ÿ“rÿ—vÿœzÿ¡~ÿ¤ƒ!ÿ§‡*ÿ©Œ3ÿ­‘>ÿ¨Ž?ÿ¬–RÿÒ•ÿÇ·…ÿõˆÿÜЭÿæÛ¼ÿÖÊ¥ÿŵƒÿâÕ±ÿâÕ±ÿâÕ±ÿâÕ±ÿâÕ±ÿâÕ±ÿÙÌ£ÿ´‡ÿåÚºÿæÛ¼ÿÓÇ¡ÿÁ²ÿÏ¿’ÿɹ‡ÿ°›Xÿ¼¦bÿ»¤[ÿº Rÿ¸›Fÿµ•8ÿ±+ÿ«ˆ!ÿ¦‚ÿ¥ÿÜhõ( ߟj¸¡y'ÿm ÿ’p ÿ™wÿ§†&ÿµ™CÿëdÿмƒÿÚËÿáÕ¯ÿØË¥ÿʽ’ÿÓÆžÿàÔ°ÿã×´ÿÙ̦ÿ¾¯}ÿÙÊŸÿßÑ©ÿßÑ©ÿßÑ©ÿßÑ©ÿßÑ©ÿßÑ©ÿßÑ©ÿŵ‚ÿÐÄ›ÿã×´ÿã×´ÿÜϪÿÎÁ—ÿÐÄ›ÿÞÒ­ÿâÖ²ÿÝ΢ÿÔÁˆÿȰhÿ»Eÿ¯)ÿ©†ÿ¤€ÿ«ÿÓ—dâà j“ª|/ÿ‡fÿjÿ”qÿ£ÿ³•>ÿÁ¨^ÿι}ÿØÇ–ÿßѨÿáÓ­ÿáÓ­ÿáÓ­ÿáÓ­ÿáÓ¬ÿ»«wÿͽŠÿÝΣÿÝΣÿÝΣÿÝΣÿÝΣÿÝΣÿÝΣÿÝΣÿÜÍ¡ÿ¼«sÿÙË£ÿáÓ­ÿáÓ­ÿáÓ­ÿáÓ­ÿáÓ­ÿàÒ«ÿÛÊ›ÿÒ½ÿƬaÿ¸™@ÿ­Š$ÿ§ƒÿ¢~ÿµ†,ÿÈ_¼à j`»ˆBÿ„cÿŠgÿ‘oÿ¢‚ÿ²•?ÿ¿¥[ÿ̶wÿÖÄÿÝÍ¢ÿßЧÿßЧÿßЧÿÛËŸÿɵ{ÿ¬”Fÿ°—Lÿ´Rÿ¶ŸVÿ· Yÿ¸¢\ÿ¹£^ÿ¹£_ÿ¹¤_ÿ¹£_ÿ¸¢]ÿ¶ Zÿ­nÿÖÅ•ÿßЧÿßЧÿßЧÿßЧÿÞÏ¥ÿÙÇ•ÿϹ{ÿè\ÿµ–:ÿª†ÿ¤€ÿ {ÿÀŒ<ÿ¼†Y à jÔ˜^ÿaÿ‡eÿiÿžÿ£„'ÿ¨Š0ÿ¨‹3ÿ§‹3ÿ¦Š4ÿ¨9ÿ«>ÿ®”Dÿ¨‹4ÿ³—BÿÁ¥SÿÉ­^ÿ̲fÿεjÿзnÿѹrÿÒºtÿÓ»uÿÓ»vÿÓ»uÿÒºtÿѹrÿÆ®dÿ¹¡Uÿ¶ŸUÿ¸ Yÿ¶žTÿ³›Oÿ²™Jÿ³šJÿµ™Hÿ´™Dÿ±4ÿ¦‚ÿ¢}ÿžy ÿÒ—VÿœpJIà jÏ‘lÿ„cÿŠgÿ~ÿ£ƒ&ÿ§‡*ÿª‹.ÿ­3ÿ¯‘8ÿ°4ÿ°1ÿ°0ÿ¸˜;ÿ¼CÿÀ¢KÿħSÿÇ«ZÿÉ®`ÿ̲eÿͳhÿεjÿϵlÿ϶lÿϵlÿεjÿͳhÿ̲eÿÉ®`ÿÄ©XÿÀ¤Rÿ¿£Qÿ¾£Sÿ¿£Tÿ¼ Lÿ¹œFÿ·—?ÿ°’5ÿ£ÿŸzÿ¢zÿߟiÿ(à jo´ƒ<ÿaÿ‡eÿ›|!ÿ¥ˆ/ÿªŒ2ÿ®7ÿ²“;ÿµ—@ÿ¸›Eÿ»ŸLÿ¾¡Oÿ¾ Mÿ¾ Jÿ¾ Iÿ¿¡HÿÁ¤NÿħSÿƪXÿȬ\ÿÉ®_ÿʯ`ÿʯaÿʯ`ÿÉ®_ÿʯaÿɯaÿʰeÿ˲jÿʲiÿÈ®dÿŪ]ÿ¦Xÿ¿£Qÿ»ŸLÿ¸šDÿ¯1ÿ {ÿœw ÿ¹ˆ4ÿΓa“à j Ûeÿˆf ÿƒcÿ‘rÿ¨Œ:ÿ¬;ÿ¯“>ÿ´—Cÿ·šGÿ¹Kÿ¼ Pÿ¿¤UÿÁ§ZÿĪ^ÿÇ­dÿɱiÿɰhÿʲiÿ˲kÿʰgÿ̳jÿÏ·sÿÏ·sÿÑ»zÿÑ»yÿйxÿηtÿͶrÿ̳nÿɱiÿÇ®eÿŪ_ÿ§[ÿ¿£Tÿ¼ Pÿ¹Jÿ¨†"ÿx ÿ›u ÿÙ›`ÿ¤uN)à j{¶„>ÿ€`ÿ…eÿ¦‹=ÿ®”Gÿ²—Hÿµ›Kÿ¸žOÿ» Sÿ½£VÿÀ¦Zÿ¨^ÿŬcÿǯhÿɰjÿʳnÿ̵rÿÍ·uÿϸwÿϹxÿϺzÿкzÿкzÿϹxÿϸwÿÍ·uÿ̵rÿ˳oÿɰjÿÇ®gÿÅ«bÿé_ÿÀ¦Zÿ¾¤Wÿ·›Hÿy ÿ™tÿº‡5ÿÒ–c“à jߟiÿŠgÿaÿ‘rÿ²šUÿ´Uÿ¸ Vÿ»¢Yÿ¾¥\ÿÀ§_ÿªbÿĬeÿÆ®hÿȰlÿɲoÿË´rÿ̶tÿÍ·wÿθyÿϹzÿϺ{ÿк{ÿϺ{ÿϹzÿθyÿÍ·wÿ̶tÿË´rÿɲoÿȰlÿÆ®hÿĬeÿªbÿÀ§_ÿ¨‡'ÿ™tÿ›u ÿÜžeÿ³€Uà jrÄŽMÿ}_ÿ‚bÿ£Š>ÿ¸£cÿº¤bÿ½¦cÿÀ©fÿ«hÿÄ®lÿƯnÿDZpÿɳrÿʵuÿ̶wÿÍ·yÿθ{ÿι|ÿϺ}ÿϺÿлÿϺ~ÿϺ~ÿϺ}ÿι{ÿÍ·zÿ̶wÿʵtÿɳrÿDZpÿƯmÿÄ®lÿµšHÿ˜sÿ”oÿÂŒBÿךfƒà j–®7ÿ~_ÿ„dÿ²\ÿ¾ªpÿÀ¬pÿ®qÿŰrÿƲtÿÉ´wÿʵyÿË·zÿ̸|ÿ͹}ÿκÿÏ»ÿϼ‚ÿмƒÿмƒÿнƒÿмƒÿмƒÿϼ‚ÿÏ»ÿκÿ͹}ÿ̸|ÿË·zÿʵxÿÉ´wÿ¾§aÿšv ÿ“oÿ±-ÿÚœgšà jà j¥žt%ÿ~_ÿˆj ÿ¼©rÿIJ~ÿÆ´~ÿǵ~ÿÉ·ÿ˸ÿ̺ƒÿλ„ÿϼ„ÿϽ‡ÿоˆÿÑ¿‰ÿÑ¿‰ÿÒÀŠÿÒÀŠÿÒÀ‹ÿÒÀŠÿÒÀŠÿÑ¿‰ÿÑ¿‰ÿн‡ÿϽ‡ÿϼ…ÿλ„ÿͺ„ÿÄ®oÿ™v ÿ‘mÿ¤yÿߟiÿÕ˜e,à j<Üžfÿ’mÿ}_ÿ‹mÿ¾­xÿÊ»ÿË»Œÿ̽ŒÿξŒÿÏ¿ÿÑÀÿÑÁÿÓ‘ÿÓÑÿÔÄ“ÿÔÓÿÕÄ“ÿÕÄ“ÿÕÄ”ÿÕÄ“ÿÕÄ“ÿÔÓÿÔÄ“ÿÓÑÿÓ‘ÿÑÁÿÑÀŽÿŲvÿ˜wÿlÿ›sÿÛdÿÝžiRà jWÝžhÿ¡v)ÿ|^ÿ…g ÿ¼«uÿÐÛÿÑÛÿÒÄ›ÿÓÅ›ÿÔÆ›ÿÕÇœÿÖÈÿ×Èÿ×ÈžÿØÉžÿØÉŸÿØÉžÿØÉžÿØÊŸÿØÉŸÿØÉžÿ×Èžÿ×ÈÿÖÈÿÕÇœÿ½§dÿ‘oÿiÿ¦z#ÿÝžfÿÞŸijà j3à jœ´ƒ=ÿ|^ÿ`ÿ©“QÿÓǤÿÖʨÿ×˨ÿØÌ¨ÿØÍ¨ÿÙͨÿÚͨÿÚΨÿÚΩÿÛΩÿÛÏ©ÿÛÏ©ÿÛÏ©ÿÛΩÿÚΩÿÚΨÿÚͨÿÑ–ÿª@ÿŒiÿŠgÿ²3ÿߟj¬ÜhCà jà j“Ê‘Tÿ’mÿ|^ÿŠmÿ¸¦oÿ×̬ÿÜÒµÿÝÓ¶ÿÝÓµÿÝÓµÿÞÓµÿÞÔµÿÞÔµÿÞÔµÿÞÔµÿÞÔµÿÞÔµÿÞÔµÿØÌ¨ÿ±™Sÿkÿˆfÿ˜pÿÉ‘Pÿà j–à jà jWà jÀÁŒKÿŠgÿ|^ÿ‚cÿŸ‡>ÿó„ÿ×Í­ÿâÚÂÿâÚÂÿâÚÁÿãÚÂÿãÚÂÿãÚÂÿÙέÿ¼©pÿ¡†7ÿ†eÿ…dÿ‘lÿÀ‹Fÿà jÌà jcà jlà jÕÆPÿ©|2ÿ‡fÿ|^ÿ~_ÿ`ÿŽsÿ•z)ÿ–{)ÿŽqÿ‚bÿ‚bÿ‚bÿŒh ÿ«}/ÿÆMÿà jÛà jrà j?à j‡à jÞÑ–[ÿ»ˆEÿ·…@ÿ¨{/ÿ£x*ÿ¤x*ÿ¨{.ÿ¸†@ÿ¼ˆCÿÑ–Zÿà jáà jà jEà j$à jZà jfà jà j™à j™à jà jfà j]à j'ÿÿÿÿÿÿÿøÿÿÿÿàÿÿÿÿÀÿÿÿÿÿÿÿþÿÿü?ÿÿøÿÿøÿÿðÿÿàÿÿàÿÿÀÿÿÀÿÿÀÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿÀÿÿÀÿÿÀÿÿàÿÿàÿÿðÿÿø?ÿÿøÿÿüÿÿÿþÿÿÿÿÿÿÿÿ€ÿÿÿÿàÿÿÿÿøÿÿÿÿþÿÿÿÿÿÿÀÿÿÿÿmysql-connector-odbc-5.1.10-src/wix/resources/Setup.exe100644 15766 12 162000 11707541006 21652 0ustar00cteamstaffMZPÿÿ¸@º´ Í!¸LÍ!This program must be run under Win32 $7PEL^B*àŽ ŠVT– @@@ÀÚ6ðl àCODE ‰Š `DATA$ Ž@ÀBSSQ°”À.idataÚÀ ”@À.tlsОÀ.rdataàž@P.relocl ð @P.rsrc66®@P@ä@P@ StringX@X@h8@t8@x8@|8@p8@47@P7@l7@TObjectÿ%HÁ@‹Àÿ%DÁ@‹Àÿ%@Á@‹Àÿ%<Á@‹Àÿ%8Á@‹Àÿ%4Á@‹Àÿ%0Á@‹Àÿ%,Á@‹Àÿ%(Á@‹Àÿ%$Á@‹Àÿ% Á@‹Àÿ%Á@‹Àÿ%\Á@‹Àÿ%Á@‹Àÿ%XÁ@‹Àÿ%Á@‹Àÿ%Á@‹Àÿ% Á@‹Àÿ%Á@‹Àÿ%Á@‹Àÿ%Á@‹Àÿ%üÀ@‹Àÿ%øÀ@‹Àÿ%ôÀ@‹Àÿ%ðÀ@‹Àÿ%ìÀ@‹Àÿ%èÀ@‹Àÿ%TÁ@‹Àÿ%äÀ@‹Àÿ%àÀ@‹Àÿ%lÁ@‹Àÿ%hÁ@‹Àÿ%dÁ@‹Àÿ%ÜÀ@‹Àÿ%xÁ@‹Àÿ%tÁ@‹Àÿ%ØÀ@‹Àÿ%ÔÀ@‹ÀSƒÄ¼» TèÿÿÿöD$,t·\$0‹ÃƒÄD[ËÀÿ%ÐÀ@‹Àÿ%ÌÀ@‹Àÿ%ÈÀ@‹Àÿ%ÄÀ@‹Àÿ%ÀÀ@‹Àÿ%¼À@‹Àÿ%¸À@‹Àÿ%´À@‹ÀSƒÄô»àµ@ƒ;uYhDjè¦ÿÿÿ‰D$ƒ|$u3À‰$ëP‹D$‹ܵ@‰‹D$£Üµ@3À‹ÐÒ‹L$TщT$‹T$‹ ‰ ‹T$‰@ƒøduÜ‹‰D$‹D$‹‰‹D$‰$‹$ƒÄ [ɉ@ËÀSVƒÄø‹ò‹Øèfÿÿÿ‰D$ƒ|$u3Àë:‹‹T$‰B‹F‹T$‰B ‹‰$‹D$‹$‰‹D$‰X‹$‹T$‰P‹D$‰°YZ^[ÃƒÄø‹P‰$‹‰T$‹$‹L$‰ ‹T$‹ $‰J‹àµ@‰£àµ@YZËÀSVWUƒÄø‹Ù‹ð‹ü‹‰‹‰‹B‰C‹‹‰D$‹‹R‹Ê‹/M ‹;Èu‹èÿÿÿ‹‹@‰‹‹@ CëC;Ðu‹èqÿÿÿ‹‹@ C‹D$‰;7u®‹Ó‹Æèúþÿÿ„Àu3À‰YZ]_^[Ã@SVWUƒÄð‰$‹ô‹‰D$ ‹ ‹‹@;È‚†‹Ø‹>_ ‹ùz;ßrv;Èu!‹B‹A‹B‹)B ‹ƒx uV‹èðþÿÿëM‹Ø‹>_ ‹ùz;ßu ‹B‹)B ë3‹Z‰\$‹>‹‹.} +û‰|$+È‹‰H T$‹èMþÿÿ„Àu3Àë°ë‹‹‰‹;D$ …Yÿÿÿ3ÀƒÄ]_^[ÃSVW‹Ú‹ðþ}¾ë Æÿÿæÿÿ‰sjh Vjè4ýÿÿ‹ø‰;…ÿt#‹Ó¸äµ@èÜýÿÿ„Àuh€j‹Pèýÿÿ3À‰_^[ÃSVWU‹Ù‹ò‹èÇCjh hUèáüÿÿ‹ø‰;…ÿuÆÿÿæÿÿ‰sjh VUè¼üÿÿ‰ƒ;t#‹Ó¸äµ@èeýÿÿ„Àuh€j‹Pèžüÿÿ3À‰]_^[ÃSVWUƒÄè‹ù‹ôÇD$ÿÿÿÿ3ɉL$ ‰D$T$‰T$¡äµ@‰ëk‹‹‰D$‹‹X;\$rR‹Ã‹B ;D$wE;\$s‰\$‹‹h‹h ;l$ v‰l$ h€j‹‹@Pèüÿÿ…Àu ÇÀµ@‹èýÿÿ‹D$‰¸äµ@;uŒ3À‰ƒ|$ t‹D$‰‹D$ +D$‰GƒÄ]_^[ËÀSVWUƒÄè‹Ù‰$t$|$l$ ‹Ð‹Êáðÿÿ‰L$$Âÿâðÿÿ‰T$‹D$‰‹D$+D$‰C¡äµ@‰ë[‹‹@‰‹‹@ ‰E‹;D$s‹D$‰‹E;D$v‹D$‰E‹;Esjh‹E+P‹Pè&ûÿÿ…Àu3À‰ë‹‹‰¸äµ@;uœƒÄ]_^[ÃSVWUƒÄè‰$t$|$\$ ‹Ð‹êÅÿåðÿÿ‰l$$âðÿÿ‰T$‹D$‰‹D$+D$‰A¡äµ@‰ëX‹‹@‰‹‹@ ‰‹;D$s‹D$‰‹;D$v‹D$‰‹;s h@‹+P‹Pèwúÿÿ…Àu ÇÀµ@‹‹‰¸äµ@;uŸƒÄ]_^[ËÀSVWUƒÄô‹Ú‹ð‹ü½ôµ@Æÿ?æÀÿÿ‹E‰ëA‹;p 4‹Ë‹‹@‹ÖèJþÿÿƒ;t_‹C‹B‹C‹)B ‹ƒx uG‹èûÿÿë>‹‹‰;/u»‹Ó‹Æèmüÿÿƒ;t&L$‹Ó‹Åèûÿÿƒ|$u’L$‹S‹è"ýÿÿ3À‰ƒÄ ]_^[ËÀSVWUƒÄè‰ $‹ú‹Øt$½ôµ@Çÿ?çÀÿÿ‹E‰ë‹‹‰;.t‹;Xuï‹;Xu_‹;x Žœ‹‹×+P ‹‹@‹A L$è5üÿÿƒ|$t3L$T$‹Åèoúÿÿƒ|$uŸL$‹T$ ‹D$èüÿÿ‹$3Ò‰éšL$‹×‹Ãèîûÿÿƒ|$t4L$T$‹Åè(úÿÿƒ|$…TÿÿÿL$‹T$ ‹D$è4üÿÿ‹$3Ò‰ëR‹‹h;ÝuB‹;x ;‹ $‹Å‹×è×üÿÿ‹$ƒ8t.‹$‹@‹B‹$‹@‹)B ‹ƒx u‹è†ùÿÿë‹$3Ò‰ƒÄ]_^[ÃSƒÄè‹Ùˆÿ?áÀÿÿ‰ $ÐâÀÿÿ‰T$‹D$;$v_‹Ë‹T$+$‹$èýÿÿL$‹Ó¸ôµ@è]ùÿÿ‹\$…ÛtL$‹T$ ‹Ãènûÿÿ‹D$‰D$‹D$‰D$ ƒ|$tT$¸ôµ@è©ùÿÿë3À‰ƒÄ[ËÀU‹ìQ3ÒUhÄ@dÿ2d‰"hĵ@è¼÷ÿÿ€=E°@t hĵ@è±÷ÿÿ¸äµ@èCøÿÿ¸ôµ@è9øÿÿ¸ ¶@è/øÿÿhøjè_÷ÿÿ£¶@ƒ=¶@t@¸‹¶@3ɉL‚ô@=uìÇEü¶@‹Eü‹Uü‰P‹Eü‹Uü‰‹Eü£¶@Ƽµ@3ÀZYYd‰hË@€=E°@t hĵ@è!÷ÿÿÃéo ëå ¼µ@Y]ÃU‹ìƒÄø€=¼µ@„è3ÀUhÈ@dÿ0d‰ €=E°@t hĵ@èÔöÿÿƼµ@¡¶@Pè¢öÿÿ3À£¶@¡äµ@‰Eøëh€j‹Eø‹@PèŽöÿÿ‹Eø‹‰Eø¸äµ@;EøuÛ¸äµ@è÷ÿÿ¸ôµ@è÷ÿÿ¸ ¶@è ÷ÿÿ¡Üµ@‰Eüƒ}üt!‹Eü‹£Üµ@‹EüPè-öÿÿ¡Üµ@‰Eüƒ}üuß3ÀZYYd‰hÏ@€=E°@t hĵ@è'öÿÿhĵ@è%öÿÿÃékëÛYY]ÃSƒÄø;¶@u ‹P‰¶@‹P‰$‹PúN;$u…ÒyƒÂÁú¡¶@3ɉLôëK…ÒyƒÂÁú‹ ¶@‹$‰\‘ô‹‰D$‹$‹T$‰‹D$‹$‰P닉D$‹$‹T$‰‹D$‹$‰PYZ[Ã@SQ‹Ì‹ ¶@‰ë‹‹R;Âr ‹S ;Âr‹‹‰º ¶@;ußÇÀµ@3À‰‹Z[ËÀSQ‹Êƒé‰$ƒú|‹$Ç€‹ÑèÚZ[Ãú|‹ÊÉ€‰‹$‰Z[Ãÿ¬µ@‹Ðƒê‹âüÿÿƒê°µ@èûËÀƒú |ƒÊ‰ƒÀèÊÿÿÿÃú| ‹ÊÉ€‰ƒ þÃSVQ‹Ðƒê‹‹Êá€ù€t ÇÀµ@‹Úãüÿÿ+ËÈ3÷Âþÿÿÿt ÇÀµ@öt)‹Ðƒê ‹r+Ɖ$‹$;pt ÇÀµ@‹$è0þÿÿÞ‹ÃZ^[ÃSVQ‹Ø3ö‹©€t %üÿÿðØ‹¨u‰$‹$èþýÿÿ‹$‹@ð؃#þ‹ÆZ^[Ã@SVWUƒÄô‹ò‹è3Û‹Åèhþÿÿ‰D$ƒ|$„‹D$‹x‹Ç‹T$B ‹Ð .+уú ‹ð+õ‹Å+ǃø }‹D$‹Õ+PÖ‹Ì‹Çèûÿÿë‹Ì‹ÖƒêEè ûÿÿ‹<$…ÿt:‹×+Õ‹Åè=þÿÿ‹T$‹R‹L$Q ‹ÇD$;Ðv .+Ðè{þÿÿ‹Ô‹D$èüôÿÿ³‹ÃƒÄ ]_^[ÃSVƒÄô‹Ú‹ð‰4$‹$‰X‹$Ãƒè ‰Xûv‹Ã…ÀyƒÀÁø‹¶@‹T‚ô‰T$ƒ|$u#‹¶@‹ $‰L‚ô‹$‹$‰P‹$‹$‰鈋D$‹‰D$‹$‹T$‰P‹$‹T$‰‹D$‹$‰‹D$‹$‰PëVû<| ‹Ó‹Æè˜þÿÿ„ÀuA¡¶@‰D$‹$£¶@‹D$‹‰D$‹$‹T$‰P‹$‹T$‰‹D$‹$‰‹D$‹$‰PƒÄ ^[Ã=¶@~@ƒ=¶@ } ÇÀµ@ë+¡¶@ƒÈ‹¶@‰¡¶@ƒÀè ýÿÿ3À£¶@3À£¶@ËÀSVWƒÄð‹ð<$¥¥‹üè ÿÿÿL$‹×¸ ¶@èóÿÿ‹\$…Ûu3ÀëR‹;Øs è ýÿÿ)G‹G‹ót$ ;ÆsèlýÿÿG‹G;ðuƒèºèSüÿÿƒo‹£¶@‹G£¶@°ƒÄ_^[Ã@SƒÄø‹Ø‹ÔCèôöÿÿƒ<$t ‹ÄèWÿÿÿ„Àu3Àë°YZ[ÃSVƒÄø‹ò‹Ø‹ÌV‹Ãèk÷ÿÿƒ<$t ‹Äè&ÿÿÿ„Àu3Àë°YZ^[Ã@Q‹Ô3ɉ …ÀyƒÀÁø=‹ ¶@‹Lô‰ ƒ:u@=uç‹ZÃ@SVWUƒÄô‹Øt$¿¶@½¶@¡¶@‰‹;XŽ«‹‰‹‹@;ØŽš‹‰Z‹‹R‰‹;Zò‹‰B‹;t‹‰ëvû‹Ãè[ÿÿÿ‰ƒ>u`‹Ãèíþÿÿ„Àu 3À‰$é´;]‰)]ƒ} }]3À‰E¡¶@‰D$¶@‹ÃƒÈ‹T$‰‹D$ƒÀ‰$ÿ¬µ@ƒë°µ@ëi‹èéùÿÿ‹‹P‹Â+Ãø | ‹Ó’è¥üÿÿë‹Ú‹;u‹‹@‰‹ÉD$‹D$ƒ þ‹‰D$‹ÃƒÈ‹T$‰‹D$ƒÀ‰$ÿ¬µ@ƒë°µ@‹$ƒÄ ]_^[ÃU‹ìƒÄìS‹Ø€=¼µ@u è÷ÿÿ„Àtûøÿÿ~ 3À‰Eüév3ÒUhô#@dÿ2d‰"€=E°@t hĵ@è6ïÿÿƒÃƒãüƒû }» û¬‹Ã…ÀyƒÀÁø‹¶@‹T‚ô‰Uøƒ}ø„‰‹UøÓ‰Uì‹Uìƒ"þ‹Uø‹R‰Uð‹Uð;Uøu‹¶@3ɉL‚ôë&‹¶@‹Mð‰L‚ô‹Eø‹‰Eô‹Eô‹Uð‰P‹Eð‹Uô‰‹Eø‰Eì‹Eø‹@ƒÈ‹U쉋EìƒÀ‰Eüÿ¬µ@ƒë°µ@è³é;¶@S)¶@ƒ=¶@ } ¶@3À£¶@¡¶@‰Eì¶@‹ÃƒÈ‹U쉋EìƒÀ‰Eüÿ¬µ@ƒë°µ@èUë2‹Ãè@ýÿÿ‰Eü3ÀZYYd‰hû#@€=E°@t hĵ@èñíÿÿÃé?ëå‹Eü[‹å]ÃU‹ìƒÄðS‹Ø3À£Àµ@€=¼µ@uèÚõÿÿ„ÀuÇÀµ@ÇEüé–3ÒUhË%@dÿ2d‰"€=E°@t hĵ@èíÿÿ‰]ø‹Eøƒè‰Eø‹Eø‹öÃuÇÀµ@ é ÿ ¬µ@‹Ã%üÿÿƒè)°µ@öÃtS‹Eøƒè ‹@ƒø |©€tÇÀµ@ éá‹Uø+ЉUð‹Uð;BtÇÀµ@ éÂØ‹Eð‰Eø‹Eðèäöÿÿãüÿÿ‹EøÉEô‹Eô;¶@u,)¶@¶@=¶@<~èzúÿÿ3À‰Eüèìé‹Eô‹¨t%üÿÿƒø} ÇÀµ@ ëN‹Eôƒë<‹Eô‰Eð‹Eðƒxt‹Eðƒ8t ‹Eðƒx } ÇÀµ@ ë‹Eð‹@Ø‹Eðè<öÿÿ‹Ó‹Eøèùÿÿ¡Àµ@‰Eü3ÀZYYd‰hÒ%@€=E°@t hĵ@èìÿÿÃéhëå‹Eü[‹å]ËÀSVWUƒÄô‹ÚƒÃƒãüƒû }» ƒè‰$‹$‹0æüÿÿ‹$ƉD$;óu°é½;óŽ‹î+ë‹T$;¶@u,)-¶@-¶@ƒ=¶@ o-¶@)-¶@‹Þé\‰D$‹D$öu‹D$‰D$‹D$h‹D$èQõÿÿƒý |#‹$ÉD$ƒÍ‹D$‰(‹D$ƒÀèBöÿÿé‹Þé‹û+þ‹D$;¶@ue;=¶@O)=¶@=¶@ƒ=¶@ }¡¶@¶@¶@3À£¶@‹Ã+ưµ@‹$‹%€ Ø‹$‰°é¾è‚øÿÿ‹$ƉD$‹D$öuO‹D$‰D$‹T$‹j;ý~ ʼnD$+ýë2‹D$è~ôÿÿ+ïƒý |‹$ËÕè?÷ÿÿëOÝ‹$ÉD$‹D$ƒ þë;‹D$‹©€t*%üÿÿD$‰D$‹×‹D$èùÿÿ„Àt‹$ƉD$éüþÿÿ3Àë‹Ã+ưµ@‹$‹%€ Ø‹$‰°ƒÄ ]_^[ÃU‹ìƒÄøSV‹ò‹Ø€=¼µ@uèòÿÿ„Àu 3À‰Eüé˜3ÒUh™(@dÿ2d‰"€=E°@t hĵ@è³éÿÿ‹Ö‹Ãèªýÿÿ„Àt‰]üë=‹Æèúÿÿ‰Eø‹Ãƒè‹%üÿÿƒè;ð}‹Æƒ}øt‹Uø‹Ë‘è¡‹Ãè’ûÿÿ‹Eø‰Eü3ÀZYYd‰h (@€=E°@t hĵ@èLéÿÿÃéšëå‹Eü^[YY]Ã@SQ‹Ø…Û~‹Ãÿ0 @‰$ƒ<$u°èØë3À‰$‹$Z[Ã@S…Àtÿ4 @‹Ø…Ût °è°ë3Û‹Ã[Ë…Ét2…ÒtP‰Èÿ8 @Y Àt‰ð醉‰Èÿ4 @ Àuëðép…ÒtP‰Ðÿ0 @Y Àtç‰Ã@‰ @èÉÃSV‹ò‹Ø€ãƒ=°@t ‹Ö‹Ãÿ°@„Ûu è(‹˜ë€ûw 3ÀŠÃŠ˜< @3ÀŠÃ‹Öè­ÿÿÿ^[ËÀƒà‹$é©ÿÿÿÃPRQèÈ'ƒ¸YZXuÃ1ÀéØÿÿÿÃ@S‹Øè¨'‰˜[ÃèçÿÿèæÿÿÿÃSèŽ'‹˜èƒ'3Ò‰‹Ã[Ã@VW‰Æ‰×‰È9÷wt/Áùx*ó¥‰Áƒáó¤_^Ãt1ü|9üÁùxýó¥‰ÁƒáƒÆƒÇó¤ü_^ÃSVWUƒÄè‰T$‰$‹ô|$ë ‹PèVæÿÿ‰‹Š„Ût€û vé‹€8"u ‹€x"uƒëß3틉D$ ë[‹€8"u@‹Pèæÿÿ‰ë‹Pèæÿÿ‰‹+苉‹Š„Ût€û"uß‹€8t ‹Pèæåÿÿ‰ë‹PèÚåÿÿ‰‹+苉‹€8 wž‹D$‹Õèô‹D$ ‰‹D$‹‰D$3íë{‹€8"uP‹Pè™åÿÿ‰ë$‹Pèåÿÿ‰‹;s‹Š‹T$ˆ*ÿE‹;r싊„Ût€û"uÏ‹€8t0‹PèUåÿÿ‰ë$‹PèIåÿÿ‰‹;s‹Š‹T$ˆ*ÿE‹;rì‹€8 ‡zÿÿÿ‹‰D$‹D$ƒÄ]_^[ÃSVÄôþÿÿ‹Ú‹ð‹Ãèu…öu hD$Pjè0åÿÿ‹ÈT$‹ÃèCë!èåÿÿ‰$‹Ó‹$èSþÿÿ‰$…ötƒ;tNëçÄ ^[Ã@f£ @ÛâÙ- @ÃjÙ<$XÃS‹Ø3À‰C3À‰C jCP‹CP‹CP‹PèAäÿÿ…Àuè äÿÿƒømu3À[Ã3À[Ã@3ÀÃSVQ‹Ø‹s …öu3Àë&jD$PV‹CP‹Pè(äÿÿ…Àuè_äÿÿë3À3Ò‰S Z^[ËÀS‹ØSè¯ãÿÿH”À[ÃS‹ØfÇC°×‹èàÿÿÿ„Àuè'äÿÿ[Ã3À[ÃV‰Æ1À‰F ‰Ff‹F-±×t Ht Ht.ég¸€º¹ÇF,,@ë'¸@º¹ë¸Àº¹ÇFl,@ÇF$¸,@ÇF h,@€~H„²jh€QjRPFHPèãÿÿƒøÿ„‰f~³×…ÃfÿNjÿ6èïâÿÿ@„æ-s1ÀjjPÿ6èãÿÿ@„Êj‰âjRh€–LRÿ6èÊâÿÿZH…¨1À9Ðsk€¼Lt@ëïjj)ÐPÿ6è¹âÿÿ@„€ÿ6è£âÿÿHuvë=†LÇF€ÇF$h,@‰Ff~²×tjöëþà³@ujôëjõèHâÿÿƒøÿt9‰f~±×tÿ6è"âÿÿ…ÀtƒøuÇF l,@1À^Ãÿ6è÷áÿÿfÇF°×¸iëêfÇF°×èwâÿÿëÝÃSV‹ò‹Ø‹Ã3ɺLè(ƒL‰CfÇC°×3À $ @f‰CÇC€ÇCØ,@‹ÆèåP‹ÆèÝSHYè4ûÿÿ‹ÆèÍÆDH3À^[Ã@Sf‹Hfé±×tIfƒés ‹ÚÿÓ‹Øë3Ûë=²@t=à³@u3Ûë»g…Ût‹Ãè£úÿÿ‹Ã[Ã@‹Pè°ÿÿÿÃ@ÿ%0Á@‹Àÿ%Á@‹ÀU‹ìQSVW‹ñ‹ú‹Ø‹E·S#Ð;ÂuXjEüP‹C÷îPW‹PÿU …ÀuèráÿÿèEúÿÿ3À‰Eüë?‹Eü3Ò÷s‰Eü‹E…Àt ‹E‹Uü‰ë#;uüt‹Eèúÿÿ3À‰Eüë¸gèúÿÿ3À‰Eü‹Eü_^[Y]ÂU‹ìS‹]Sh±×h@/@jdè[ÿÿÿ[]‹ÀU‹ìS‹]Sh²×hH/@jeè;ÿÿÿ[]‹ÀSV‹Ø3öf‹Cf=±×r/f=³×w)f%²×f=²×u‹ÃÿS‹ð…öu‹ÃÿS$‹ð…öt‹ÆèwùÿÿëûH°@t ¸gècùÿÿ‹Æ^[ËÀSV‹ØƒÎÿf‹Cf=°×v)f=³×w#j‹Pèãßÿÿ‹ðƒþÿuè?ùÿÿë‹Æ3Ò÷s‹ðë ¸gèùÿÿ‹Æ^[Ã@W‰ÇˆÍ‰ÈÁàf‰È‰ÑÁùx ó«‰Ñƒáóª_ÃSV‹ØfÇC°×3ö‹èµûÿÿ„Àu èäøÿÿ¾‹Æ^[ËÀSVW‰Ö‰Ï1Ò‰Ãf‹Pê°×tƒú‡¢ÿS$…ÀtèœøÿÿfÇC³×‰sÇC$à0@ÇCh,@€{Ht`¸ÀŠ @ƒâpÁê‹’X @¹ƒït!¹Gt¸@GfÇC²×t ¸€fÇC±×jh€QjRPCHPèÀÞÿÿƒøÿt$‰ë0ÇC$h,@ƒÿtjöëjõè¸ÞÿÿëÞ¸fë fÇC°×èßÿÿèï÷ÿÿ_^[Ã@Š  @€á€ùv±áÿèÿÿÿùèúþÿÿÉÁ·@-±×ƒøwj‹A÷âjPÿ1èwÞÿÿ@„¨÷ÿÿøgé÷ÿÿÃSVW‰ÆP…Àtl1À1Û¿ÌÌÌ ŠF€û tøµ€û-tb€û+t_€û$t_€ûxtZ€ûXtU€û0uŠF€ûxtH€ûXtC„Ût ë„Ût-€ë0€û w%9øw!€ÀØŠF„ÛuæþÍt …À}Të Fë÷Ø~KxI[)ÞëGþÅŠF뜿ÿÿÿŠF„Ût߀ûar€ë €ë0€û v €ë€ûwЀà 9øwÉÁàØŠF„ÛuÕþÍu÷ØY1ö‰2_^[Ã@VW‰Æ‰×áÿó¦_^Ã@éËÀS1Û…À|M„š=‰Âƒâ’Û¬Sç3@ÞÉÁèty‰Âƒât ’Û¬S5@ÞÉÁèta€Û¬C³5@ÞÉëS÷Ø=}F‰Âƒâ’Û¬Sç3@ÞùÁèt4‰Âƒât ’Û¬S5@ÞùÁèt€Û¬C³5@ÞùëÝØÛ«Ý3@ëÝØÙî[Àÿ€ÿ? @È@ú@@œ @PÃ@$ô@€–˜@ ¼¾@(kî@ù• @@·Cº#@¥Ôè&@*ç„‘*@€ô æµ-@ 1©_ã0@¿ÉŽ4@Å.¼¢±7@@v:k Þ:@è‰#ÇŠ>@b¬Åëx­A@€z·&רD@¬n2x†‡H@´W ?h©K@¡íÌÎÂÓN@ „@aQY„R@È¥¹¥o¥U@: ô'ËÎX@„ ”øx9?\@å ¹6ס_@ßNgÍÉòÉb@–"E@|oüe@žµp+¨­Åi@Õ¦ÏÿIxÂÓ@£›Å«³ï=AàŒé€ÉGº“¨Aªæ+¡¶BkU'9÷pà|B0É<ãÿ–RŠçBŽÞùûë~ªQCŒ/j\ü&Ò»CvãÌò)/„&DÒ Û'¤ŸDªø®ãÅÄúDYœ°éœŠòdEÔó÷ëáJz•ÏEb¢•ÜØ>¸9FÇ‘¦® ã£F u†uvÉHMä§“9;5¸²íSå]=Å];‹ž’Z¦ð¡ ÀT¥Œ7a‹Z‹Ø%]‰ùÛgøó'¿¢È]Ý€n›— ŠR`Ä%uðYÕnb5®Ê{Ãÿ%PÁ@‹ÀS3Ûjèîÿÿÿƒøujèâÿÿÿ%ÿ= t=u³‹Ã[ÃU‹ìƒÄô· @‰EøEüPjjhè6@h€èáÚÿÿ…ÀuM3ÀUhÁ6@dÿ0d‰ ÇEôEôPEøPjjh7@‹EüPè¶Úÿÿ3ÀZYYd‰hÈ6@‹EüPèÚÿÿÃérëïf¡ @f%Àÿf‹Uøfƒâ?f Âf£ @‹å]ÃSOFTWARE\Borland\Delphi\RTLFPUMaskValueÛã›Ù- @ËÀVW‰×‹pÔ1ÉŠAó¤_^Ã@S‹Ø‹Ãè&èiñÿÿ‹Ð‹Ãè<[ËÀS‹Ø‹Ã膋Ãè{ñÿÿ[ÃÀ؋ËÀ蛄Ò~èzÃ…Àt²‹ÿQüÃSVW‰Ã‰×«‹KØ1ÀQÁéIó«Yƒáóª‰Ð‰â‹K¸…ÉtQ‹[Ü…Ût‹ëí9Ôt[‹ ƒÃ‹s…öt‹{‰4ƒÃIuí9Ôuã_^[ËÀSV‰Ã‰Æ‹6‹VÀ‹vÜ…Òtè]‰Ø…öué^[ÃSV‹ò‹Ø…Ût ‹Ö‹èA„Àu3À^[ð^[Ã@W–ë‹6‹~Ð…ÿt ·QƒÇòf¯t Y‹vÜ…öuã_ÃXÀ)È‹tGü_ËÀë‹9Ðt‹@Ü…Àuóðøÿÿ€Ã‹ÀÃ@Ã@Ã@Vf‹2f ötfþÀsP‹èÿÿÿXt‰ñ^ÿá^‹ÿaðÃRQS„Ò|ÿPô1ÒL$d‹‰‰iÇAÍ8@‰A d‰ [YZÃé:‹D$,‹@ …Àt‹²PÿQüXè è¨Ã@‹ÿRøÃ‹ÀS‹Ø‹Ã‹ÿRä‹Ã[ËÀ„ÒÃPR‹ÿRèZXÀ= @vjjjhßúíÿ°@À= @tPPRTjjhäúíÿ°@ƒÄXÃ@Tjjhàúíÿ°@ƒÄXÃ@€= @vPSéØÿÿÿÃ@…Ét‹A€9ét €9ëu ¾ÀAAëƒÁÁËÀ€= @vPRQèÏÿÿÿQTjjháúíÿ°@YYZXÀ= @vRTjjhâúíÿ°@ZÃPR€= @vTjjhãúíÿ°@ZXËÀ‹D$÷@…8Þúí‹P‹Htnüèãüÿÿ‹°@…Ò„ñÿÒ…À„ç‹T$ ‹L$9Îúït7èÚþÿÿ€= @v)€= @w L$PQè5ÖÿÿƒøX„«‰Â‹D$‹H ë0‰Â‹D$‹H €= @v€= @wPD$RQPèøÕÿÿƒøYZXtpƒHS1ÛVWUd‹SPRQ‹T$(jPhä:@Rÿ°@‹|$(è“ÿ°‰ ‹o‹_ÇG;@ƒÃèfþÿÿÿãé#èf‹ˆ‹‰‹AéLüÿÿ¸ËÀ‹D$‹T$÷@t‹JÇBh;@SVWU‹jƒÁèFþÿÿÿÑ]_^[¸ËÀ Àu ¸Øè®ZTUWVSPRTjjhÞúíRÿ%°@ËD$0Ç@ã;@èØ‹‹ ‰ˆ‹B ƒ`ý8Þúít ‹Bè¯ûÿÿèJýÿÿ1ÀƒÄd‹Y‹‰]_^[¸Ã@è‹‹ ‰ˆ‹BèuûÿÿZ‹d$,1ÀYd‰X]è·ýÿÿÿâÃ1Ò‹L$‹D$ƒÁd‰ÿÑ ËÀU‹ì‹U‹=’À,t\=ŽÀtW-Àt\-‡t=HtNë`qÿÿ?ƒèr6t0ëR=–Àt=-“Àt.HtHt$ë:-ýÀt/ƒè=t&ë,°Èë*°Éë&°Íë"°Ïë°Èë°×ë°Îë°Øë°Úë °Ùë°Êë°ÿ%ÿ‹R è|ìÿÿ]‹D$÷@…‰€= @wD$Pè¸Óÿÿƒøtq‹D$üè úÿÿ‹T$jPh=@Rÿ°@‹\$;Þúí‹S‹Ct‹°@…Ò„úþÿÿ‰ØÿÒ…À„îþÿÿ‹S èžüÿÿ‹ °@…ÉtÿÑ‹L$¸Ù‹Q‰$é¾1ÀÃ@1ÒEôd‹ d‰‰Ç@Ô<@‰h£4¶@Ã@1Ò¡4¶@…Àtd‹ 9Èu‹d‰Ë ƒùÿt9uõ‹‰ÃU‹ìƒÄøSVW¾0¶@‹F…ÀtT‹^ ‹@‰Eü3ÀUh>@dÿ0d‰ …Û~K‰^ ‹Eü‹D؉Eøƒ}øtÿUø…Ûå3ÀZYYd‰ëéöûÿÿè¡ÿÿÿèxýÿÿèÇýÿÿ_^[YY]ÃU‹ìƒÄøSVW¿0¶@‹G…ÀtT‹03Û‹@‰Eü3ÀUh>@dÿ0d‰ ;ó~‹Eü‹؉EøC‰_ ƒ}øtÿUø;óæ3ÀZYYd‰ëé†ûÿÿè1ÿÿÿèýÿÿèWýÿÿ_^[YY]Ãǰ@ˆ@ǰ@˜@£8¶@3À£<¶@‰@¶@‹B£,°@è¥þÿÿÆ4°@èQÿÿÿÃS1ÛWV‹<t‹F‹‹ÚèƒÆOuë^_[Ã@S1ÛWV‹<t‹F‹‹F‰ƒÆ Ouì^_[ÃSVW¾€ @±‹ @‹Ã¿ ™÷ÿ€Â03ÀŠÁˆ‹Ã» ™÷û‹ØI…ÛuÛ±¡ @‹ÐƒâŠ’  @3ÛŠÙˆÁèI…Àuæ_^[ËÀ1À‡ @÷ØÀ@¿0¶@‹_‹oÿwÿw ‹7¹ ó¥_^É ËÀQ€=D°@tWf=²@²×uƒ= ²@v ¸²@ÿ0²@jD$Pjh€ @jõèŽÐÿÿPèÀÐÿÿjD$Pjh<@@jõèsÐÿÿPè¥ÐÿÿZÀ=  @ujhx @h€ @jèŸÐÿÿZÃÿÿÿÿ SVWUQ»0¶@¾@°@€{(uƒ>t‹‰$3À‰ÿ$ƒ>uïƒ= @tè¯þÿÿè6ÿÿÿ3À£ @€{(uƒ= @u3À‰C èýÿÿ€{(v ƒ= @t#‹{…ÿt‹Çè½ ‹k‹u;ut …ötVèÐÿÿè¿üÿÿ€{(uÿS$€{(tè›þÿÿƒ;uƒ=$°@tÿ$°@¡ @PèÀÏÿÿ‹‹ð‹û¹ ó¥ékÿÿÿZ]_^[ã @èÿÿÿà@ééÿÿÿË…ÒtÇ‹JøI|ðÿJøu PBøèƒçÿÿXÃSV‰Ã‰Ö‹…ÒtÇ‹JøI|ðÿJøuBøèZçÿÿƒÃNuÚ^[Ã…Òt$‹JøAPR‹Büè\‰ÂXR‹Hüè`èÿÿZXëðÿBø‡…Òt‹JøI|ðÿJøuBøèçÿÿÃ…Òt ‹JøA~ðÿBø‡…Òt‹JøI|ðÿJøuBøèäæÿÿÃ@…À~$PƒÀ ƒàþPèŸæÿÿZfÇDþƒÀZ‰PüÇ@øÃ1ÀÃSVW‰Ã‰Ö‰Ï‰øèÄÿÿÿ‰ù‰Ç…öt ‰Â‰ðèÃçÿÿ‰Øèèþÿÿ‰;_^[ËÀU‹ìjjRP‹EPQj¡¸µ@PèõÎÿÿ]ÂSVWUÄðÿÿPƒÄü‹ñ‰$‹ø…ö ‹Çè þÿÿë_nýÿ}(VD$‹L$ºÿè ÿÿÿ‹Ø…Û|T$‹Ç‹Ëè]ÿÿÿë,‹ÝÛ‹Ç‹ÓèV‹‹L$‹Óèpÿÿÿ‹Ø…Û}3Û‹Ç‹ÓèÿÄ]_^[ÃR‰â¹èÿÿÿZÃ1É…Òt!R: t:Jt:Jt :JtƒÂëèBBB‰ÑZ)ÑéèþÿÿÃ@1É…Òt-Rf; t f;Jtf;Jtf;JtƒÂëäƒÂƒÂƒÂ‰ÑZ)ÑÑééüþÿÿÃ@WPQ‰×1Àò®u÷ÑXÁX_é‘þÿÿÃ1É…Òt‹JüÑééÐþÿÿÃ@…Àt‹@üÃ…Òt?‹…É„ÊýÿÿSVW‰Ã‰Ö‹yü‹Vüú9Îtè‰ð‹Nü‹úè$æÿÿ_^[Ã苉ùëèÃ…Òta…É„ˆýÿÿ;t\;tPQèyýÿÿZXéšÿÿÿSVW‰Ó‰ÎP‹CüFüèÏýÿÿ‰Ç‰Â‰Ø‹KüèÑåÿÿ‰ú‰ð‹NüSüèÂåÿÿX‰ú…ÿtÿOøè3ýÿÿ_^[ÉÊé(ýÿÿ‰ÊéIÿÿÿÃSVWRP‰Ó1ÿ‹L”…Ét 9u‰Ï‹AüJë1À‹L”…Ét Aü9Ïu1ÿJuì…ÿt‰Â‹$‹wüèH‹<$ÿ77Këè=ýÿÿP‰Æ‹Dœ‰ò…Àt ‹HüÎè6åÿÿKuéZX…ÿu …ÒtÿJøè¡üÿÿZ_^[X$”ÿàËÀSVW‰Æ‰×9Є…öth…ÿtk‹Fü‹Wü)ÐwÂRÁêt&‹‹9ÙuXJt‹N‹_9ÙuKƒÆƒÇJuâëƒÆƒÇZƒât"‹‹8ÙuAJt8ýu:Jtãÿáÿ9Ùu'Àë#‹Wü)Ðë‹Fü)ÐëZ8Ùu8ýu ÁéÁë8Ùu8ý_^[ËÀ…Àt ‹PøB~ðÿ@øÃ…Àtø±E@ÃS…Àt-‹Xü…Ût&J|9Ú})Ó…É|9Ù‹D$èHüÿÿë1Òëå‰Ùëë‹D$èEûÿÿ[ÂÃSVW‰Ã‰Ö1ÿ…Ò~H‹…Àt#ƒxøuƒèƒÂ P‰àèÝâÿÿXƒÀ‰‰püÆë(‰ÐèÇûÿÿ‰Ç‹…Àt‰ú‹Hü9ñ|‰ñè¿ãÿÿ‰Øèäúÿÿ‰;_^[ËÀ°éAãÿÿË…ÒtÇPRèËÿÿXÃ@SV‰Ã‰Ö‹…Àt ÇPèäÊÿÿƒÃNuè^[Ã@…Àtø¦F@ËÀSVW‰Æ‰×9Ðtr…ötQ…ÿtT‹Fü‹Wü)ÐwÂRÁêt&‹‹9ÙuAJt‹N‹_9Ùu4ƒÆƒÇJuâëƒÆƒÇZƒât f‹f‹f9Ùu!Àë‹Wü)Ðë‹Fü)ÐëZf9Ùu ÁéÁëf9Ù_^[Ã@‹…ÒtP‹JüÑéQRè)ÊÿÿZ…À„ ÿÿÿ‰Ã1ÉSŠJVW‰Ãt ‹|‹‹FØ‹¹è'ƒÆOç‰Ø_^[Ã@ƒ=  @tÿ  @ðèâÿÿÃù„àPSVW‰Ã‰Ö‰Ï1ÒŠŠV< t%< t>< tQ< t\<tv<„„<„‹é—ƒù‰Ø èIùÿÿ鑉Êèaùÿÿé…ƒù‰ØèXþÿÿëw‰Êègþÿÿën‰ØƒÃègÿÿÿOóë_U‰Õ‹T. ‰Ø\.‹L.‹èbÿÿÿOè]ëAU‰Õ‰Ø\.‰òèÿÿÿOð]ë+‰ØƒÃè˜Oóë‰Ø‰òƒÃèCOñë _^[X°éáÿÿ_^[XËÀ°éáÿÿÃRP‹D$÷$$‰Á‹D$÷d$ Á‹$÷d$ ÊYYÂË…Ét3ÇðÿIøu'P‰È1ÉŠJ‹T…Òt‹Hü…Ét‹è¸þÿÿƒèèìßÿÿXËÀSVÄøþÿÿ‹Øƒ{u+hD$P‹CPèñÇÿÿ‹Ä²èh‹ð‰s…öu‹C‰C‹CÄ^[ÃSVWQ‹Ø‹ô¡( @‰ƒ>t+‹;Xt‹;Xt‹;X u ‹èˆÿÿÿ‹øë ‹‹‰ƒ>uÕ‹û‹ÇZ_^[ÃU‹ìQSVuü‰ë ‹Pè'Çÿÿ‰‹Š„Ût€û\ué‹^[Y]ÃU‹ìĤýÿÿSVW‰Uø‰Eü‹Eü‰EôhdK@è>Çÿÿ‹ð…ötDhtK@Vè5Çÿÿ‰Eèƒ}èt0h…¥ýÿÿP‹EüPÿUè…Àt‹EøP…¥ýÿÿP‹EüPè)Çÿÿé<‹Eü€8\uA‹Eü€x\…'‹EüƒÀèHÿÿÿ‰Eð‹Eð€8„ ‹Eð@è0ÿÿÿ‰Eð‹Eð€8„õë ‹EüƒÀ‰Eð‹]ð+]üCP‹EüP…¥ýÿÿPè¸Æÿÿé«‹Eð@èîþÿÿ‰Eì‹}ì+}ð‹ÇÃ@=©GW‹EðP…¥ýÿÿÃPè}Æÿÿ…ªþÿÿP…¥ýÿÿPè Æÿÿ‹ðƒþÿt{VèõÅÿÿ…ÖþÿÿPèYÆÿÿSÂ@=\Æ„¥ýÿÿ\¸+ÃHP…ÖþÿÿP…¥ýÿÿÃ@PèÆÿÿ…ÖþÿÿPèÆÿÿ@Ø‹Eì‰Eð‹Eð€8…Iÿÿÿ‹EøP…¥ýÿÿP‹EüPèèÅÿÿ‹Eô_^[‹å]Ãkernel32.dllGetLongPathNameAU‹ìÄÜþÿÿS‰Eüh…ßþÿÿPjè`ÅÿÿÆEîEøPhjhÌM@h€è’Åÿÿ…Àt@EøPhjhÌM@h€ètÅÿÿ…Àt"EøPhjhèM@h€èVÅÿÿ…À…‰3ÀUhŒL@dÿ0d‰ ÇEè…ßþÿÿºèýÿÿEèPEîPjj…ßþÿÿP‹EøPèÅÿÿ…Àt"EèPEîPjjhN@‹EøPè÷Äÿÿ…ÀtÆEîÆEò3ÀZYYd‰h“L@‹EøPèÅÄÿÿÃé§îÿÿëïh‹EüP…ßþÿÿPè˜ÄÿÿjEóPjèsÄÿÿPèEÄÿÿ3Û€½ßþÿÿ„ù€}óu €}î„é…ßþÿÿPèbÄÿÿ•ßþÿÿ‰EäëÿMä‹Eä€8.t …ßþÿÿ;Eäuê…ßþÿÿ;Eä„«ÿEä€}ît,‹Uä+и+ÂPEîP‹EäPèÄÿÿjj…ßþÿÿPèåÃÿÿ‹Ø…Ûur€}ótl…ßþÿÿ‹Uä+и+ÂPEóP‹EäPèÉÃÿÿjj…ßþÿÿPè©Ãÿÿ‹Ø…Ûu6ÆEõ…ßþÿÿ‹Uä+и+ÂPEóP‹EäPèÃÿÿjj…ßþÿÿPèoÃÿÿ‹Ø‹Ã[‹å]ÃSoftware\Borland\LocalesSoftware\Borland\Delphi\LocalesU‹ìƒÄøSVW‰Eü¡, @‰Eøƒ}øt93ÀUhGN@dÿ0d‰ ‹]ø‹EüÿS3ÀZYYd‰ë éÀëÿÿè›íÿÿ‹Eø‹‰Eøƒ}øuÇ_^[YY]ËÀ‹( @‰£( @ËÀU‹ìƒÄø‰Eü3ÀUhñN@dÿ0d‰ ‹Eü‹@èrÿÿÿ3ÀZYYd‰høN@‹Eü;( @u ‹Eü‹£( @ë2¡( @‰Eøƒ}øt$‹Eø‹;Eüu ‹Eü‹‹Uø‰ë‹Eø‹‰Eøƒ}øuÜÃéBìÿÿë¯YY]Ë…ÒtÇPR‹ÿPXÃ@SVÄðÿÿP‹ò‹Ø…Ût={}*hD$P‹CP‹‹è÷ùÿÿPèíÁÿÿ‹È‹Ô‹ÆèÒòÿÿë ‹Æ‹Sè²óÿÿÄ^[ÃU‹ìƒÄðS3Ò‰Uð3ÒUhÒO@dÿ2d‰"jUõRhPèiÁÿÿEðUõ¹èÙóÿÿ‹EðUüè’âÿÿ‹Øƒ}üt3Û3ÀZYYd‰hÙO@EðègñÿÿÃéaëÿÿëð‹Ã[‹å]ÃU‹ì3ÀUh*P@dÿ0d‰ ÿ´µ@u#¸H°@èàÿÿ¸²@èàÿÿ¸à³@èàÿÿè¸Êÿÿ3ÀZYYd‰h1P@Ãé ëÿÿëø]Ã-´µ@ƒÅÆ @ǰ@ˆ@ǰ@˜@ÆF°@ǰ@ŒH@è®åÿÿ„ÀtèÕåÿÿè”æÿÿfÇL°@°×fDz@°×fÇä³@°×èPÀÿÿ£<°@èæÀÿÿ£8°@èÔÀÿÿ%€=€t-èÃÀÿÿ%ÿfƒøv Ǹµ@ë èGÀÿÿè†þÿÿ£¸µ@ëè6Àÿÿèuþÿÿ£¸µ@èÀÿÿ£0°@Ãÿ%ŒÁ@‹Àÿ%ˆÁ@‹Àÿ%„Á@‹Àÿ%€Á@‹ÀPj@èàÿÿÿÃ@¸ËÀSèòÿÿÿ‹Ø…Ût6ƒ=° @ÿu ¸âèÑïÿÿ‹ÃèÆÿÿÿ…Àu ¸âè¼ïÿÿë P¡° @Pè¢ÿÿÿ[Ê \¶@¡° @„Éu&d‹,‹‚Ãèÿÿÿ¡° @Pènÿÿÿ…Àtáh¶@ÃPè]ÿÿÿ…ÀtÛø´ @èžüÿÿÃS‹Ø3À£° @jè+ÿÿÿ£`¶@¡`¶@£¸ @3À£¼ @3À£À @èÁÿÿÿº´ @‹Ãè‘ìÿÿ[Ã@U‹ì3ÀUh5R@dÿ0d‰ ÿd¶@3ÀZYYd‰hÚÿÿ_^[ËÀÉu Ày÷Øè°-ANˆù RV1Ò÷ñN€Â0€ú:r€Âˆ ÀuêYZ)ñ)ÊvѰ0)Öëˆ2JuúˆÃÉu0¹ ÷@€t"ÿpÿ0‰à÷$ƒT$÷\$è ÆFÿ-NAƒÄÃVƒìÙ|$Ù<$f $Ù,$f‰ $Ùè÷@€t'ÿpÿ0d$ÿÿÿhÿÿÿhÿÿÿÿßl$ß,$ØÂÞÁƒÄëß(ß$ÙÁNÙøß$ÜùŠ$0<:rˆÙÁØÓ›ßàžsáÙl$ƒÄÝÃÝÂÝÁÝÀY)ñ)Êv)Ö°0Ñëˆ2JuúˆÃU‹ìV‰æƒì 1ÉP1ÒEèÿÿÿ‰òXèºßÿÿƒÄ ^]‹ÀSQ‹Ú‹ÔèÁÏÿÿƒ<$t‹ÃZ[ËÀÿÿÿÿ0ÿÿÿÿ-1U‹ìÄ´þÿÿS‹Ø…´þÿÿP‹ÃèêâÿÿPèàïÿÿƒøÿt4PèÍïÿÿö…´þÿÿu%EôP…ÈþÿÿPè¬ïÿÿEüPEþPEôPè“ïÿÿ…ÀuÇEüÿÿÿÿ‹Eü[‹å]ÃS‹Ø‹ÃèŽÿÿÿ@•À[ÃSVWQ‹ò‹Ø‹Æè}àÿÿ‹ø‹Ãètâÿÿ‰$…ÿ~)Š\>ÿ„Ût‹Ó‹$èw…Àt‹×‹Æè¾<uOO…ÿ׋ÇZ_^[ÃSVW‹ú‹Ø‹Ó¸¤c@èÿÿÿ‹ðWV¹ÿÿÿ‹Ãè#âÿÿ_^[Ãÿÿÿÿ\:U‹ìƒÄäS‹E…Àt‰Eäë3À‰EäEðPEôPEøPEüP‹EäPèïîÿÿ‹Ø‹Eü÷mø3Ò‰Eè‰Uì‹Eô3ÒRP‹Eè‹Uìè™äÿÿ‹M ‰‰Q‹Eð3ÒRP‹Eè‹Uìèäÿÿ‹M‰‰Q‹Ã[‹å]‹À‰ú‰Ç¹ÿÿÿÿ2Àò®¸þÿÿÿ)ȉ×ÃQ‰$’èÂÅÿÿ‹$ZÃWVS‰Æ‰×‰Ë2À…Étò®uA)ˉ÷‰Ö‰ú‰ÙÁéó¥‰Ùƒá󤪉Ð[^_Ã@WVS‰×‰Æ‰Ë1À Ét/ò®)ˉى×1Òó¦t!ŠFÿt:ŠEûª÷Ù°0óªë¹B¬Àtªâø¬ÀtˆÄŠEûf«¬Àtªëø°0óª Òt1ÀëÃè•ÿÿÿè‡ÿÿÿŠeûf«‹M Ièyÿÿÿªâø´+‹Mƒùv1ɰEŠ]׿UÔJè!þÿÿÃè^ÿÿÿ‹Uƒúrº¿MÔ É°0ªë*1Û€}t ‰ÈH³öóˆãCè$ÿÿÿªItKuôŠEú„Àtíª³ëè ÒtŠEû„Àtªã °0ªJt AuùèõþÿÿªJu÷Ã1ÛŠ]ó¹€}ÖtŠ]ò¹8ËvˆË뜛½l@]칊<@tQS<$t<*t ªë è ëèMÿÿÿ[YCâÜÃV‹uô…öt‹Nüó¤^Ã$*@@@*$@@@$ *@@* $@@($*)@-$*@@$-*@@$*-@@(*$)@-*$@@*-$@@*$-@@-* $@-$ *@* $-@$ *-@$ -*@*- $@($ *)(* $)‹å] U‹ìƒÄàWVS‰Ã‰ÖÇEü€ùt è1éèéöf‹F‰Â%ÿt=ÿu"f÷F€tƒ>u ~€t@1ÒÆCéÍÛ.-ÿ?iÀMÁø@‰Eø¸+EøÙáS‹]üèzÅÿÿ[Ùü‹}üÛ¯ ¡@ØÙ›Ý}ö›f÷EöAt Ú·,¡@ÿEøßuè{º ›ŠD*çˆÄÀè€äf00f«Juë2Àª‹}ø}y1Àéwÿÿÿ;} r‹} ƒÿs'€|;5r%ÆD;Ox þD;€|;9wíëfÇC1ÿEøë¿ÆD;Ox€|;0tñf‹V‹EøfÁêf‰ˆSÃ1Òëî dè'‹‹V‰Á Ñ„· Òy÷Ú÷؃Ú1É‹} ÿ}1ÿƒÿ|¿A-d§Ú³¶à sòId§Ò³¶à ‰Eà‰Uäßmà‰ú¸)Ðt ‹}üÚ´‡cn@ßuè{› Éu¹ ŠD)çˆÄÀèuˆà$uIuìë9ˆÈ0ª¹ ŠD)çˆÄÀè0ªˆà$0ªIuê‰øL)ÈÆO€?0t÷‹VÁêë1À1ÒˆCf‰ˆSÃ[^_‹å]ÂSÄÿÿÿ‹ÚjhT$RjPjh2è8ãÿÿëH…À~ŠTÿ€ê!rò€ê tí‹Ô‹Ë‘è‘ÒÿÿÄ[ÃU‹ìÄÿÿÿSV‹ñ‹]hÿÿÿQRPèãÿÿ…À~‹ÈI•ÿÿÿ‹ÃèQÒÿÿë ‹Ã‹ÖèªÑÿÿ^[‹å]‹ÀSVWQ‹Ù‹ò‹øjD$PVWèÔâÿÿ…À~Š$ë‹ÃZ_^[ÃU‹ìQSVW‰Mü‹ú‹ð‹]S‹E‹@ü3É‹Öèkÿÿÿƒ;u ‹Eü‹¸‹ÓèÍÞÿÿ_^[Y]ÂU‹ì3ÉQQQQQQSVW3ÀUhcq@dÿ0d‰ èŸâÿÿ‰Eü»¾ ¶@¿Ð¶@Uj EôP¹0¡@‹ÓJCDHèyÿÿÿY‹Uô‹ÆèæÐÿÿUj EðP¹`¡@‹ÓJC8HèVÿÿÿY‹Uð‹ÇèÃÐÿÿCƒÇƒÆƒû u®»¾·@¿·@C¹™÷ù‰UøUjEìP¹¡@‹ÓJ‹EøƒÀ1èÿÿÿY‹Uì‹ÆèuÐÿÿUjEèP¹¬¡@‹ÓJ‹EøƒÀ*èãþÿÿY‹Uè‹ÇèPÐÿÿCƒÇƒÆƒûuœ3ÀZYYd‰hjq@EèºèúÏÿÿÃéÐÉÿÿëë_^[‹å]Ã@U‹ìSV3ö»ëƒûtCƒÇÿÿ‹…¬þÿÿ•°þÿÿèçÿÿ‹…°þÿÿ‰…¼þÿÿÆ…Àþÿÿ ‹Eü‰…ÄþÿÿÆ…Èþÿÿ ‹Eø‰…ÌþÿÿÆ…Ðþÿÿ…´þÿÿPj•¨þÿÿ¡Ô£@èuÒÿÿ‹¨þÿÿ²¡€]@è÷úÿÿ‹Øë]‹C ‰…þÿÿÆ…”þÿÿ‹Eü‰…˜þÿÿÆ…œþÿÿ ‹Eø‰… þÿÿÆ…¤þÿÿ…þÿÿPj•Œþÿÿ¡°£@èÒÿÿ‹Œþÿÿ²¡€]@è˜úÿÿ‹Ø3ÀZYYd‰hJ}@…ŒþÿÿèÄÿÿ…¨þÿÿºè"ÄÿÿEüèöÃÿÿÃéð½ÿÿëÕ‹Ã[‹å]Ã@U‹ìƒÄðS3Ò‰Uð‰Eü3ÀUh~@dÿ0d‰ ‹Eüèoýÿÿ%ÿ‹ÐƒÂýƒêrtJƒê s"Åè¡@‹H‹²èÉùÿÿ‹Øë;UèÓýÿÿY‹Øë0‹Eü‹‰EôÆEøEôPjUð¡Ø¢@èCÑÿÿ‹Mð²¡ðX@èÈùÿÿ‹Ø‹Ã‹˜X@èºÿÿ„Àt‹Eü‰C 3ÀZYYd‰h~@Eðè*ÃÿÿÃé$½ÿÿëð‹Ã[‹å]Ã@èøÿÿ¸èñÂÿÿË £@²¡äW@èæùÿÿ£Œ·@‹ ”£@²¡l\@èÏùÿÿ£·@¡à¢@Ǥy@¡4£@Ç ~@¡ü¢@‹ W@‰¡,£@Çh{@¡8£@ÇT}@¸´z@‹¤£@‰¸Ðz@‹Ô¢@‰ËÀƒ=Œ·@t¡Œ·@Æ@ ¡Œ·@‹ÿRø3À£Œ·@ƒ=·@t¡·@Æ@ ¡·@艸ÿÿ3À£·@¡à¢@3Ò‰¡4£@3Ò‰¡ü¢@3Ò‰¡,£@3Ò‰¡8£@3Ò‰¡¤£@3Ò‰Ã@ÄlÿÿÿÇ$”TèÑÓÿÿ…ÀtP‹D$£Ô @‹D$£Ø @‹D$£Ü @ƒ=Ô @u‹D$ %ÿÿ£à @ë ‹D$ £à @¸ä @T$¹€èåÃÿÿĔËÀVW‹ú3Ò…Àtf€<8t`…ÿuŠ%ÿ£è @sL²ëH‹÷NëN…ö|Š 0áÿ£ è @ré‹Ï+Îá€yIƒÉþA…Éu²ëŠ8%ÿ£è @s²‹Â_^Ã@SV‹ò‹Ø3À€=D·@t‹ÃèxÅÿÿ‹ÖJèhÿÿÿ^[Ã3É€=D·@tèTÿÿÿ‹È‹ÁÃ@S‹Ø€=D·@t SèÖÒÿÿ+Ã[ø[Ã@SV‹ò‹Ø¸€=D·@t"ŠT3ÿâÿ£è @s‹ÃèÅÿÿÆHèªÿÿÿ^[Ã@SV‹Ú‹ð…Û~‹ÆèçÂÿÿ;Ø€|ÿ\u ‹Ó‹ÆèCÿÿÿ„Àt3À^[ð^[ÃSV‹Ú‹ð‹Ã‹Öè•Àÿÿ‹è®Âÿÿ‹Ð‹è­ÿÿÿ„Àu ‹Ãº$@èÂÿÿ^[Ãÿÿÿÿ\SVƒÄø‹Ú‰$‹ô‹Ó‹è3‰‹‰D$„Ûtƒ>t‹‰D$ÿ‹Ó‹è‰ƒ>uè‹D$YZ^[ÃSVWQ‹Ú‹ð‹ü‹Ó‹ÆèEãÿÿ‰ƒ?t'‹+Ö‹Æè«þÿÿ,rtëÿÿ‹Ó‹èãÿÿ‰ƒ?uÙ‹Z_^[ÃU‹ìƒÄôS3Ò‰Uô3ÒUh‚@dÿ2d‰"jUùRhPèõÐÿÿEôUù¹èÁÿÿ‹EôPè¼Ðÿÿ‹ÐXètàÿÿ‹Ø3ÀZYYd‰h#‚@Eôè¿ÿÿÃé¹ÿÿëð‹Ã[‹å]ËÀU‹ìQSVW‹}ƒÇìW¡8·@ètÿÿÿPèvÐÿÿ3öë)ŠD7Š\7*ØrCˆEÿŠEÿ%ÿ«è @þEÿþËuêƒÆƒþ } ŠD7 D7uÈ_^[Y]á<·@ƒøw£°¢@’ÀÃ@U‹ìÄdþÿÿSVWÇ8·@ Ç<·@ Ç@·@è=Ðÿÿ…Àt£8·@f…Àt‹Ðfâÿ·Ò‰<·@·ÀÁè £@·@¾üƒ@¿è @¹ó¥ƒ=Ô @…­èmÿÿÿ„ÀtÆE·@ÆD·@é¿UèðþÿÿY¸è @ºüƒ@± èʯÿÿ•ÈD·@„Ût ÆE·@鎸€•hÿÿÿˆ@B=uõ…hÿÿÿ‰Eè…fþÿÿPh€‹EèPj¡8·@Pè]Ïÿÿ¸€•fþÿÿfƒ:”Áˆ E·@„Éu6ƒÂHuéë.jJè¢Ïÿÿ…À•À¢E·@j*è‘Ïÿÿ…À•ÈD·@„ÛtUè8þÿÿY_^[‹å]ÃU‹ì¹jjIuùS3ÀUhç†@dÿ0d‰ èeþÿÿè ìÿÿ€=D·@tèÖíÿÿè¹Îÿÿ‹ØEðP3ɺ‹Ãè5ëÿÿ‹Uð¸x¶@è½ÿÿEìP¹ü†@º‹Ãèëÿÿ‹Eì3ÒèáÝÿÿ¢|¶@EèP¹ü†@º‹Ãèïêÿÿ‹Eè3Òè½Ýÿÿ¢}¶@±,º‹Ãèëÿÿ¢~¶@±.º‹Ãè ëÿÿ¢¶@EäP¹ü†@º‹Ãè¥êÿÿ‹Eä3ÒèsÝÿÿ¢€¶@±/º‹ÃèÔêÿÿ¢¶@EÜP¹‡@º‹Ãènêÿÿ‹EÜUàè›íÿÿ‹Uค¶@èB¼ÿÿEÔP¹‡@º ‹ÃèAêÿÿ‹EÔUØèníÿÿ‹Uظˆ¶@è¼ÿÿ±:º‹Ãègêÿÿ¢Œ¶@EÐP¹0‡@º(‹Ãèêÿÿ‹Uи¶@èà»ÿÿEÌP¹<‡@º)‹Ãèßéÿÿ‹U̸”¶@è¾»ÿÿEøèb»ÿÿEôèZ»ÿÿEÈP¹ü†@º%‹Ãè­éÿÿ‹EÈ3Òè{Üÿÿ…ÀuEüºH‡@è»ÿÿë EüºT‡@è³»ÿÿEÄP¹ü†@º#‹Ãènéÿÿ‹EÄ3Òè<Üÿÿ…Àu?EÀP¹ü†@º‹ÃèKéÿÿ‹EÀ3ÒèÜÿÿ…ÀuEôº`‡@è`»ÿÿë Eøºp‡@èQ»ÿÿÿuøÿuüh€‡@ÿuô¸˜¶@ºèнÿÿÿuøÿuühŒ‡@ÿuô¸œ¶@ºè³½ÿÿ±,º ‹Ãè%éÿÿ¢F·@3ÀZYYd‰hî†@EÀºèvºÿÿÃéL´ÿÿëë[‹å]Ãÿÿÿÿ0ÿÿÿÿm/d/yyÿÿÿÿ mmmm d, yyyyÿÿÿÿamÿÿÿÿpmÿÿÿÿhÿÿÿÿhhÿÿÿÿ AMPMÿÿÿÿAMPM ÿÿÿÿ:mmÿÿÿÿ:mm:ssSVQ»´¢@‹ô닉‹‹‰º‹è&¡ÿÿƒ;uåZ^[ÃShø‡@èËÿÿ‹Ø…Ûthˆ@SèËÿÿ£ ¡@ƒ= ¡@u ¸¨c@£ ¡@[Ãkernel32.dllGetDiskFreeSpaceExA‹3ɉ‹ÂèS¯ÿÿËÀ¬¢@¨£@¤¢@d£@œ¢@Ø¢@”¢@¼£@Œ¢@У@„¢@£@|¢@\£@t¢@¼¢@l¢@l£@d¢@x£@\¢@@£@T¢@H£@L¢@ô£@D¢@D£@<¢@¤@4¢@œ£@,¢@Œ£@$¢@¤@¢@¤@¢@T£@ ¢@ø£@¢@ü£@ü¡@£@ô¡@ì¢@ì¡@ˆ£@ä¡@¸£@Ü¡@À¢@Ô¡@ð£@Ì¡@P£@(Ä¡@è£@À¡@Ü¢@¼¡@´£@¸¡@Ì¢@´¡@ø¢@°¡@ð¢@¬¡@ä¢@¨¡@L£@¤¡@˜£@ ¡@ô¢@œ¡@„£@˜¡@Ì£@”¡@Ä£@¡@ £@Œ¡@<£@ˆ¡@(£@„¡@È¢@€¡@À£@|¡@t£@x¡@ £@t¡@h£@p¡@£@l¡@p£@h¡@0£@d¡@Ä¢@`¡@`£@\¡@¤@X¡@ä£@T¡@€£@P¡@ £@L¡@Т@H¡@Ü£@D¡@ ¤@@¡@è¢@<¡@¸¢@8¡@ì£@4¡@|£@0¡@¤@¡@X£@¡@£@U‹ì3ÀUhqŒ@dÿ0d‰ ÿˆ·@…J¸”·@èùüÿÿèlüÿÿè‡óÿÿ¸¢@¹‹„y@è^¼ÿÿ¸È¡@¹‹y@èI¼ÿÿ¸¡@¹‹@è4¼ÿÿ¸¡@èµÿÿ¸„·@‹ä`@è2½ÿÿ¸€·@‹À`@è"½ÿÿ¸H·@¹‹@èõ»ÿÿ¸·@¹‹@èà»ÿÿ¸·@¹‹@èË»ÿÿ¸Ð¶@¹ ‹@è¶»ÿÿ¸ ¶@¹ ‹@è¡»ÿÿ¸œ¶@è/µÿÿ¸˜¶@è%µÿÿ¸”¶@èµÿÿ¸¶@èµÿÿ¸ˆ¶@èµÿÿ¸„¶@èý´ÿÿ¸x¶@èó´ÿÿ¸ä @èé´ÿÿ¸Ð @è ºÿÿ¸Ì @èÕ´ÿÿ3ÀZYYd‰hxŒ@Ãé®ÿÿëø]ËÀU‹ì3ÀUhäŒ@dÿ0d‰ ƒ-ˆ·@s@¸,ˆ@è<²ÿÿ¸‰@èZ²ÿÿ€=]¶@t¸¡@ºøŒ@èÊ´ÿÿèiñÿÿèhòÿÿèïúÿÿèF÷ÿÿ3ÀZYYd‰hëŒ@ÃéO®ÿÿëø]Ãÿÿÿÿ0xU‹ì3ÀUh!@dÿ0d‰ ÿ˜·@3ÀZYYd‰h(@Ãé®ÿÿëø]ËÀƒ-˜·@ÃU‹ìjSVW‹Ù…Ûx Áë‹4šKVyù‹Ô‹ø3ÀUhœ@dÿ0d‰ ‹uN…ö| F‹Ú€;vEüŠ茵ÿÿ‹Uü‹Çè2¶ÿÿCNuã3ÀZYYd‰h£@Eüè³ÿÿÃé—­ÿÿëð‹}ð‹uô‹]ø‹å]‹ÀU‹ìÄDõÿÿSV3Û‰Dõÿÿ‰Hõÿÿ‰]ä‹Ù‰Uø‰Eü‹Eüè¼·ÿÿ‹Eøè´·ÿÿ3ÀUhC@dÿ0d‰ ÆE÷‹Uü…˜þÿÿè| ÿÿ¡Ø£@¶‰Eð¡Ø£@Æ3ÒUh @dÿ2d‰"º…˜þÿÿ诣ÿÿèr›ÿÿ3ÒUhé@dÿ2d‰"…˜þÿÿè¢ÿÿèT›ÿÿ‹ð‹Öƒê\…˜þÿÿ袣ÿÿè=›ÿÿj‹Ó¹\…˜þÿÿèU¡ÿÿè$›ÿÿ…HõÿÿST¹èá´ÿÿ‹…Hõÿÿº\@èI¶ÿÿ…‹+ðƒî\‹Ð…˜þÿÿèG£ÿÿèâšÿÿj SEä¹èTþÿÿ…Dõÿÿ‹Mä‹Uøèµÿÿ‹•Dõÿÿ…Lýÿÿ肟ÿÿº…Lýÿÿèò¢ÿÿ虚ÿÿ3ÀUhÀ@dÿ0d‰ 3Ûƒ;ð} EìP‹Î+Ë•Lõÿÿ…˜þÿÿè’ ÿÿèašÿÿëEìP•Lõÿÿ¹…˜þÿÿèq ÿÿè@šÿÿEèP•Lõÿÿ‹Mì…Lýÿÿèt ÿÿè#šÿÿ]ì;ótƒ}ìt‹Eè;Eìt…3ÀZYYd‰hË@…Lýÿÿèb ÿÿèñ™ÿÿÃés«ÿÿëèÆE÷3ÀZYYd‰hð@…˜þÿÿè9 ÿÿèÈ™ÿÿÃéJ«ÿÿëè3ÀZYYd‰h@ŠEð‹Ø£@ˆÃé*«ÿÿëí3ÀZYYd‰hJ@…Dõÿÿºè/±ÿÿEäè±ÿÿEøºè±ÿÿÃéðªÿÿëÓŠE÷^[‹å]ÃÿÿÿÿEB1.0U‹ìƒÄœSˆM÷‰Uø‰Eü‹Eüè µÿÿEøè°¶ÿÿ3ÀUh-‘@dÿ0d‰ ³‹Eø3Òè¶ÿÿt ‹Eøèøµÿÿ‰Eðë3À‰EðE¬3ɺDèÿŸÿÿÇE¬DÇEØ€}÷tfÇEÜëfÇEÜEœPE¬P‹EðPjjjÿjj‹Eüè«´ÿÿPjèwÁÿÿ…Àu3Û3ÀZYYd‰h4‘@Eøè@µÿÿEüè °ÿÿÃéªÿÿëè‹Ã[‹å]ÃU‹ìÄôþÿÿS3Ò‰•ôþÿÿ‹Ø3ÀUh½‘@dÿ0d‰ …ûþÿÿ3ɺèPŸÿÿ…ûþÿÿPhè‡Áÿÿ…ôþÿÿ•ûþÿÿ¹èé±ÿÿ‹…ôþÿÿ‹ÓèDïÿÿ3ÀZYYd‰hÄ‘@…ôþÿÿè|¯ÿÿÃév©ÿÿëí[‹å]Ã@¡(¸@PèÁÿÿ¡$¸@PèvÁÿÿ¡,¸@PèKÁÿÿ¡0¸@Pè@ÁÿÿÃ@èËÿÿÿjè˜ÁÿÿÃ@U‹ìSV‹u‹] ‹Ãƒèt-u¡(¸@™3Â+Â;ðu èÄÿÿÿëè½ÿÿÿV‹EPS‹EPèÁÿÿ^[]ÂU‹ìƒÄøSVW‹Ù‰Uø‰Eü‹Eüè/³ÿÿ‹Eøè'³ÿÿ¾ø·@3ÀUh”@dÿ0d‰ hœ”@j"jjjjjjjjjjjjõè„Àÿÿ£,¸@hœ”@j"jjjjjjjh¼jjjjôèXÀÿÿ£0¸@¡`¶@‰FÇ€h¤”@‹FPè‡Àÿÿ‰FÇF ’@ÇF¸°”@‰F$hjèZÀÿÿ‰FVè‰ÀÿÿjèBÀÿÿ-‚ÑøyƒÐPjè.Àÿÿ3ÒŠÓ‹úÁç<¿ƒÇx+ÇÑøyƒÐƒè2Ph‚Wjj¡`¶@Pj‹Eüè4²ÿÿ‹Ð¹Ê‹F$èQÀÿÿ£ ¸@jjhhj,¡ ¸@Pj¡`¶@Pj‹Eøèý±ÿÿ‹Ð¸Ä”@¹PèÀÿÿ£$¸@j¡,¸@Pj0¡$¸@PèÞ¿ÿÿhjPjdj¡ ¸@Pj¡`¶@PjºÌ”@¸Ð”@¹@èÏ¿ÿÿ£(¸@j¡0¸@Pj0¡(¸@Pè•¿ÿÿhjj¡ ¸@Pèq¿ÿÿ„Ût7j¡(¸@Pèx¿ÿÿëh4¸@èt¿ÿÿh4¸@è¿ÿÿjjjh4¸@èú¾ÿÿ…ÀuØ3ÀZYYd‰h””@EøºèЬÿÿÃ馦ÿÿëë_^[YY]ÃTahomaMAINICONPopupWindow ClassStaticOKButtonU‹ìQSVW‹Ù‰Uü‹Eüè°°ÿÿ¿4¸@3ÀUhÖ•@dÿ0d‰ ‹Eü襰ÿÿPjj ¡$¸@P衾ÿÿhjj¡ ¸@Pè}¾ÿÿ„Û„jÿj7hhjj¡$¸@PèM¾ÿÿjÿ3ÀŠÃ‹ðÁæ4¶ƒÆxVh‚jè¾ÿÿ+ÆÑøyƒÐƒè2Pjèó½ÿÿ-‚ÑøyƒÐP¡ ¸@Pè¾ÿÿj¡(¸@Pè¾ÿÿë Wè¾ÿÿWè°½ÿÿjjjW謽ÿÿ…Àuä3ÀZYYd‰hÝ•@Eüèc«ÿÿÃé]¥ÿÿëð_^[Y]ÃU‹ì3ÀUh–@dÿ0d‰ 3ÀZYYd‰h –@Ãé0¥ÿÿëø]Ö@@R@R@4P@àO@W@èV@xR@HR@`T@0T@|Œ@üŠ@,@üŒ@ä•@U‹ì¹jjIuùQSVW¸ –@è[»ÿÿ3ÀUh˜@dÿ0d‰ 3ÀUhã—@dÿ0d‰ 3ɺ ˜@¸X˜@è¶ûÿÿUì3Àè•ÿÿ‹EìPEèèˆúÿÿ‹U蹜·@Xèòöÿÿ„À„ùEäèjúÿÿEäPEຠ·@¹ 蘬ÿÿ‹UàXèìÿÿ‹EäèÌÿÿ„À„¯Eܺ ·@¹ èm¬ÿÿƒ}Ü„“hl˜@EÔèúÿÿÿuÔEк ·@¹ èA¬ÿÿÿuÐEغè­ÿÿ‹Eر3Òè ùÿÿ„Àuuh€˜@èg»ÿÿUÈèã×ÿÿÿuÈhì˜@èR»ÿÿ3ÒRPEÄèÆÊÿÿÿuÄhø˜@E̺èͬÿÿ‹U̱¸™@è*ýÿÿë$±º ™@¸™@èýÿÿ뱺T™@¸™@èýÿÿè#úÿÿ3ÀZYYd‰ë é$¢ÿÿèÿ£ÿÿ3ÀZYYd‰h˜@Eĺ èU©ÿÿÃé+£ÿÿëë_^[è)¨ÿÿÿÿÿÿ.Extracting installation files. Please wait...ÿÿÿÿ MySQL Setupÿÿÿÿ msiexec /i ÿÿÿÿbThe Microsoft Installer cannot be started. Please make sure you have at least MSI 2.0 installed. ÿÿÿÿ (ÿÿÿÿ)ÿÿÿÿMySQL Setup Errorÿÿÿÿ)The attached file could not be extracted.ÿÿÿÿHThe following Error occured: The attached file could not be extracted.@2‹À@@@@X"@$@à'@ËÌÈÉ×ÏÈÍÎÛØÚÙÊÜÝÞßàáãäå@Error‹ÀRuntime error at 00000000‹À0123456789ABCDEFÿÿÿÿa@˜b@¤b@0U@8U@@v:k Þ:@‹À ¸U@ÀU@ÈU@ÐU@ØU@àU@èU@ðU@øU@V@V@V@V@ V@(V@0V@8V@@V@HV@PV@XV@`V@hV@pV@xV@€V@ˆV@V@˜V@ V@¨V@°V@¸V@ÀV@ÈV@ÐV@ØV@àV@dejôY@LZ@¤Z@X[@°[@\@`\@]@Ì]@,^@à^@„^@8_@8_@8_@8_@8_@8_@”_@ð>Y?e?¢?«?´?¿?È?Ï?Þ?å? Ø0`0h0ò01#1(1°1º1Õ1Þ1@2I2c2‰2•22Ð233W3`3p3x3~3‡3Ž3“3œ3µ3¾3Ü3â3ê344(4@4L4T4u4„4”4´4Ó45 555J5~5£5³5¹5Á5,646:6@6M6S6¼6Ä6Ì6Ò6Ø6à6æ6ì6ó6ý6À7î7 88 88‡88¸8ã8 9$9=9N9c9p99<< =4=;=B=>'>Z>ª>¼> ??B?J?ê?0¬ 0]0@1G1X1d1´1ê1W3o3€3œ3´3Å3Ñ36Y6i666³6Ê6ß67¿8929:9P9h9v9ª9Æ9Ò9æ9ð9:3:`:i:›:¤:Ù:à:;O;“;Ÿ;ç<==.=P=„=Œ=—=Æ=Þ=6>O>ž>¢>¨>¬>±>¸>¾>Æ>Ñ>,?4?`?k?ˆ?’?·?Á?Ë?Ó?Ù?ç?@`00!0&0F0K0m000¦0ð0ù0þ0!1.1i2³5©6Š7“7E9É9Ù9¸;Ö;ô;<\<~<>*>j>q>…>£>¬>¸>¿>|?Å?ç?ó?ú?P˜00%060C0J0N0T0X0^0e0i0ƒ0Œ0•0¡0«0Ò0ç0ø01 111"1J1t1‚1‡1 1°1Á1Ò1Þ1ã1è1ï1ö122#202B2O2[2h2z2‚2Š2’2š2¢2ª2²2º2Â2Ê2Ò2Ú2â2ê2ò2ú23 333"3*323:3B3J3R3Z3b3j3r3z3‚3Š3’3š3¢3ª3²3º3Â3Ê3Ò374C4P4b4h4p4x4€4ˆ44˜4 4¨4°4¸4À4È4Ð4Ø4à4è4ð4ø45555 5(50585@5H5P5X5`5h5p5x5€5ˆ55˜5 5¨5°5¸5À5È5Ð5Ø5à5è5ð5ø56666 6(60686@6H6P6X6`6h6p6x6€6ˆ66˜6 6¨6°6¸6À6È6Ð6Ø6à6ï6û677 7,7@7H7L7P7T7X7\7`7d7h7v7ˆ7¨7°7´7¸7¼7À7Ä7È7Ì7Ð7ä78 88888 8$8(8,8@8`8h8l8p8t8x8|8€8„8ˆ8˜8¸8À8Ä8È8Ì8Ð8Ô8Ø8Ü8à8ð8999 9$9(9,9094989P9p9x9|9€9„9ˆ9Œ99”9˜9¨9È9Ð9Ô9Ø9Ü9à9ä9è9ì9ð9: :(:,:0:4:8:<:@:D:H:X:x:€:„:ˆ:Œ::”:˜:œ: :´:Ô:Ü:à:ä:è:ì:ð:ô:ø:ü: ;,;4;8;<;@;D;H;L;P;T;d;„;Œ;;”;˜;œ; ;¤;¨;¬;¼;Ü;ä;è;ì;ð;ô;ø;ü;<<<4<<<@> >>>>> >$>(>8>X>`>d>h>l>p>t>x>|>€>”>´>¼>À>Ä>È>Ì>Ð>Ô>Ø>Ü>ì> ???? ?$?(?,?0?4?H?h?p?t?x?|?€?„?ˆ?Œ??¨?È?Ð?Ô?Ø?Ü?à?ä?è?ì?ð?`˜0$0,0004080<0@0D0H0L0`0€0ˆ0Œ00”0˜0œ0 0¤0¨0À0Í0Õ0ä0ñ0ù0$1z3.5C5N5½6Á6Å6É6Í6Ñ6Õ6Ù6Ý6á6å6é6í6ñ6õ6ù6ý67Í7Ô7–85:a:i:q:y::æ:;0;4;8;<;@;}<º=Ï=Ú>pb0z00‹0®0Û0à0ú01Q1‹1•1»1×1ö1222F2w2†2 2²2í2 353Ÿ3Ô3í3ú34"4;4V44b5¢5ª5µ5ä5ø56¹6Ã6 7(717Æ7õ7„8¾899=9K9R9j9q9„9œ9»9Ã9Ñ9:1:\:k::Ò:Ù:z;«;Å;Ô;–<¨<õ<==g=“=È=×=æ=>2>9>C>I>P>Z>_>e>j>p>u>{>‚>ˆ>>“>˜>ž>¥>«>¶>¾>Ç>Ó>Ù>á>ê>ö>û>? ???(?P?Y?b?h?y?„?‰?Â?â?€$ 0&0D0]0…0™01È12;2i22™2²2¼2Æ2Ø2í2ø2ý233%3,3>3C3S3]3™3·3Ô3æ304F4o4}4˜4¡4¼4Ï4â4ë455"5A5O5n5†55£5±5Å5ã566"6E6g6v6†6Ž6£6«6È6Õ6˜7Â7Ò7Ý7ã7ë7ð7084888<8@8D8H8L8P8T8X8\8`8d8h8l8p8t8x8|8€8„8ˆ8Œ88”8˜8œ8 8¤8¨8¬8°8´8¸8¼8À8Ä8È8Ì8Ð8Ô8Ø8Ü8à8ä8è8ì8ð8ô8ø8ü8999 9999 9(9,94989@9D9L9P9X9\9d9h9p9t9|9€9ˆ9Œ9”9˜9 9¤9¬9°9¸9¼9Ä9È9Ð9Ô9Ü9à9è9ì9ô9ø9:: ::::$:(:0:4:<:@:H:L:T:X:`:d:l:p:x:|:„:ˆ::”:œ: :¨:¬:´:¸:À:Ä:Ì:Ð:Ø:Ü:ä:è:ð:ô:;;;.;9;C;N;X;c;m;w;};‡;;—;¢;¬;·;Á;Ì;Ö;á;ë;ö;< <<<(<2<<>>B>¦>?«?Ô?ù?¸00ˆ01T1­1Í1Ø1ã1î1&2v2~2‰2­2²2Ù2Þ2ì2ÿ2 3i3ˆ3˜3 3²3Á3È3Ð3æ3î3ö3û3 444-4>4K4U4e4{4í4õ4 5!5A5‹5˜5É5ë5þ56666 6$6(6,6064686<6@6D6H6P6h6u6ƒ66•6¸6Ú67717`7u7Ž7¥7³7¸7Æ7Ë7ö7 H00408011111014181<1@1D1H1L1P1T1X1\1`1d1h1l1p1t1x1|1€1„1ˆ1Œ11”1˜1œ1 1¤1¨1¬1°1´1¸1¼1À1Ä12222 2(20282@2H2P2X2`2h2p2x2€2ˆ22˜2 2¨2¸2¼2À2Ä2È2Ì2Ð2Ô2Ø2Ü2à2ä2è2ì2ð2ô2ø2ü2333 33333 3$3(3,3034383<3@3D3H3L3P3T3X3\3`3d3h3l3p3t3x3|3€3„3ˆ3Œ33”3˜3œ3 3¤3¨3¬3°3´3¸3¼3À3Ä3È3Ì3Ð3Ô3Ø3Ü3à3ä3è3ì3ð3ô3ø3ü3444 44444 4à000 0ck/10€p€ ¨€È€ck/1à€ø€€(€@€X€ck/1üp€ýˆ€þ €ÿ¸€Ð€ck/1€è€€€ck/16€€ck/1 0ck/1 @ck/1 Pck/1 `ck/1 pck/1 €ck/1ck/1 ck/1°ck/1Àck/1Ðck/1àck/1ðck/1 H(phØ èÀ ¨hhШx*ðh+Ø@,` .|2 ¼4Ì4\(5ZDVCLAL PACKAGEINFOMAINICON( À€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿp€ÿ€w‰ˆ€xˆ »€x‹ˆð{‹ßˆð‡»ßøˆ€ðˆ}Øw€pˆwwww€÷ˆxˆˆˆpwxr""/pÿr""/pwr""/pr¢"/prªª¯pÿÿÿpŒ€€€€€€€€€àðüüü( @€€€€€€€€€ÀÀÀÀÜÀ¦Êð """)))UUUMMMBBB999ÿ|€ÿPPÖ“ÌìÿïÖÆççÖ­©3f™Ì333f3™3Ì3ÿ3f3fff™fÌfÿf™3™f™™™Ì™ÿ™Ì3ÌfÌ™ÌÌÌÿÌfÿ™ÿÌÿ333f3™3Ì3ÿ333333f33™33Ì33ÿ33f33f3ff3™f3Ìf3ÿf3™33™3f™3™™3Ì™3ÿ™3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿ3f3fff™fÌfÿf3f33ff3f™3fÌ3fÿ3fff3fffff™ffÌff™f3™ff™f™™fÌ™fÿ™fÌf3Ìf™ÌfÌÌfÿÌfÿf3ÿf™ÿfÌÿfÿÌÌÿ™™™3™™™Ì™™33™f™Ì3™ÿ™f™3f™f3™™f™Ìf™ÿ3™3™™f™™™™™Ì™™ÿ™™Ì™3Ì™fÌf™Ì™ÌÌ™ÿÌ™ÿ™3ÿ™fÌ™™ÿ™Ìÿ™ÿÿ™Ì3™fÌ™ÌÌÌ3™33Ìf3Ì™3ÌÌ3Ìÿ3ÌfÌ3fÌff™™fÌÌfÌÿf™™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÌÿÌ3ÿÌfÿ™™ÿÌÌÿÌÿÿÌ3Ìfÿ™ÿ3Ì33ÿf3ÿ™3ÿÌ3ÿÿ3ÿfÿ3fÿffÌ™fÿÌfÿÿfÌ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿ™ÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÌÿ3ÿÿfÿÌ™ÿÿÌÿÿÿfffÿfÿÿfffÿÿfÿfÿÿ¥!___www†††–––ËË˲²²×××ÝÝÝãããêêêñññøøøÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿììÿÿþúììÿúììúûûììûìÿìûûþÿìÿìûûþÿÿìÿììììììììììììììÿÿììììììììozR1MLÿììÿÿìoåzR1MÿììììoååzR1ÿììozååzRÿììoLLLLLÿììððððððð쌀€€€€€€€€àðüüü( @€€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿwxÝxøðˆÝ™€p‡øýÙ™ˆxøð‡xˆÙ˜ˆ€xw‡‡øÙˆ‹°xøð‡xxxp ‹»°€xw‡‡»¸€xøð‡xˆ»xxp€xx»‰°‡‡€ÿð‡{¸›½ÿxxpÿðxw‰»½ø÷‡ÿð‡‡›»Ýxˆˆÿðˆˆ{½Ýˆøðˆpÿðˆˆ}݈€wwˆpÿðˆˆ÷wwÿÿÿÿpwwpˆˆwwwwwwwxpwwpˆˆ€ÿÿ÷ˆˆˆˆˆˆˆˆ‡wwwwpˆÿÿÿÿÿÿÿ‡wwò"""""/‡wwpò¸ƒ3:ª/‡ÿÿòûˆ33ª/‡wwpò¿¸ƒ3:/‡òûûˆ33/‡ò¿¿¸ƒ3/‡ò‹ûûˆ3/‡òˆ¿¿¸ƒ/‡òˆ‹ûûˆ/‡ò"""""/‡ÿÿÿÿÿÿÿ‡wwwwwwwwÿþÿÀøÿÀpÿÀ ÀÀ?À?À`?À`?À?À?ÀÀÀÀÀÀÀ€øüþÿÿøÿøÿøÿøÿøÿøÿøÿü( @€€€€€€€€€€ÀÀÀÀÜÀ¦Êð """)))UUUMMMBBB999ÿ|€ÿPPÖ“ÌìÿïÖÆççÖ­©3f™Ì333f3™3Ì3ÿ3f3fff™fÌfÿf™3™f™™™Ì™ÿ™Ì3ÌfÌ™ÌÌÌÿÌfÿ™ÿÌÿ333f3™3Ì3ÿ333333f33™33Ì33ÿ33f33f3ff3™f3Ìf3ÿf3™33™3f™3™™3Ì™3ÿ™3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿ3f3fff™fÌfÿf3f33ff3f™3fÌ3fÿ3fff3fffff™ffÌff™f3™ff™f™™fÌ™fÿ™fÌf3Ìf™ÌfÌÌfÿÌfÿf3ÿf™ÿfÌÿfÿÌÌÿ™™™3™™™Ì™™33™f™Ì3™ÿ™f™3f™f3™™f™Ìf™ÿ3™3™™f™™™™™Ì™™ÿ™™Ì™3Ì™fÌf™Ì™ÌÌ™ÿÌ™ÿ™3ÿ™fÌ™™ÿ™Ìÿ™ÿÿ™Ì3™fÌ™ÌÌÌ3™33Ìf3Ì™3ÌÌ3Ìÿ3ÌfÌ3fÌff™™fÌÌfÌÿf™™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÌÿÌ3ÿÌfÿ™™ÿÌÌÿÌÿÿÌ3Ìfÿ™ÿ3Ì33ÿf3ÿ™3ÿÌ3ÿÿ3ÿfÿ3fÿffÌ™fÿÌfÿÿfÌ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿ™ÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÌÿ3ÿÿfÿÌ™ÿÿÌÿÿÿfffÿfÿÿfffÿÿfÿfÿÿ¥!___www†††–––ËË˲²²×××ÝÝÝãããêêêñññøøøÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿêêêCCCêê»»áÂX1CCì¼ÿ¼ÿê»»áÂX10ŸCìÿ¼ÿ¼ìêï»áX10ŸŸŸCì¼ÿ¼ÿ÷ìëïï»á10ŸŸòòCìÿ¼ÿ¼ì÷êïïïï»»0ŸòòòòòCì¼ÿ¼ÿ÷ìê¼ïïïïòòòïïïCìÿ¼ÿ¼ì÷ê¼¼¼ïïìììïïCì¼ÿ¼ÿ÷ìê’’’’’ððìììCìÿ¼ÿ¼ì÷êòòŸŸXXï÷ÿðïïCìÿÿÿÿ÷ìêòŸXXRssºï÷ÿððCìÿÿÿÿì÷ìêXXRsxáºïï÷ÿCìÿÿÿÿ÷ì÷êXRRsáẺï÷÷Cìÿÿÿÿ÷÷÷÷êRsxẺºïï÷Cïììÿÿÿÿ÷÷÷÷ëëxẺºïCCìììììììÿÿÿÿ÷÷÷÷ÿÿííííëëÿÿÿÿÿÿÿÿÿìììììì÷÷÷÷ììììììììììììììììïìêêêêê÷÷÷÷ï ìÿÿÿÿÿê÷÷÷ìììììììììêê÷÷ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿììêêêê÷ìÿÿììêêêêìÿûzz1111MMMÿììÿÿÿÿìÿÃ^zz1111MMÿììììììÿ^Ã^zz1111MÿììÿÃ^Ã^zz1111ÿììÿ^Ã^Ã^zz111ÿììÿz^Ã^Ã^zz11ÿììÿzz^Ã^Ã^zz1ÿììÿzzz^Ã^ÃûzzÿììÿÿììÿÿÿÿÿÿÿÿÿÿÿÿÿÿìëëëëëëëëëëëëëëëëÿþÿÀøÿÀpÿÀ ÀÀ?À?À`?À`?À?À?ÀÀÀÀÀÀÀ€øüþÿÿøÿøÿøÿøÿøÿøÿøÿü(0`€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿß…Xؘ€ˆˆˆxˆXؘs0ˆˆˆw‡ˆUÙ˜3¸ˆˆˆpwxxUýÙ‡3‰ˆˆˆwww‡…]Ù‡8™€ˆˆˆwpˆwwxu]Øs¹˜ˆˆˆwpøøww‡]Ó3™ˆøˆˆˆwÿˆwx]“¹Ÿ€ˆˆˆwÿøø‡wøÿÿðˆˆˆwˆˆˆˆpˆˆˆˆ€ˆˆˆwwwww€wxˆˆ€ˆˆˆwˆˆˆ˜pˆwww€ˆˆˆwˆˆ™{ƒˆˆwwpøøøwø‰˜³=•‡xøˆwpwp‰™»3Ý•ˆxÿ÷ÿÿÿ‡p™›³7Ý•ˆwÿÿÿÿxw»3}Ù•ˆ‡xÿðˆˆˆˆÿÿÿ‡‡ ³7}™Uˆˆw€ˆˆˆˆpÿÿÿˆxp37Ý™UXˆwˆˆˆˆˆpÿÿÿˆ‡‡wÝ™UXˆ‡pˆˆˆˆˆpÿÿÿˆˆxp Ù•UXˆ€€ˆpÿÿÿˆˆ‡€ð •UXpˆ‡wwwˆpÿÿÿˆˆˆpÿðÿÿÿÿÿÿÿpÿÿÿˆˆˆ€wwwwwwwwwwwwxpwwwˆˆˆ€€wwwˆˆˆ€xˆˆˆˆˆˆˆˆˆˆˆ‡ÿÿÿpˆˆˆ€ÿÿÿÿÿÿÿÿÿÿÿ‡ÿÿÿÿwˆˆ€ÿÿÿÿÿÿÿÿÿÿÿ‡wwwwwwpˆˆ€ò"""""""""ÿ‡wwwˆ€òªªªªªªªª¢ÿ‡wwwpˆ€òªªªªªª""¢ÿ‡www€òªªªªªªª¢¢ÿ‡wwwp€òªªªªªªª¢¢ÿ‡wwwòªªªªªªª¢¢ÿ‡ÿÿðòªªªªªªªª¢ÿ‡wwwòªªªªªªªª¢ÿ‡òªªªªªªªª¢ÿ‡òªªªªªªªª¢ÿ‡òªªªªªªªª¢ÿ‡òªúªªªªªª¢ÿ‡òªªªªªªªª¢ÿ‡òªªªªªªªª¢ÿ‡ò"""""""""ÿ‡ÿÿÿÿÿÿÿÿÿÿÿ‡ÿÿÿÿÿÿÿÿÿÿÿ‡wwwwwwwwwwwpÿÿþÿÿÿÿøÿààÿàÀÿà€ÿàÿàÿàÿàÿàÿàÿà8ÿà8ÿà8ÿàÿàÿàÿàààààààààààÀ€þÿÿ€ÿÀÿàÿðÿøÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿø(0`€ €€€€€€€€€ÀÀÀÀÜÀ¦Êð """)))UUUMMMBBB999ÿ|€ÿPPÖ“ÌìÿïÖÆççÖ­©3f™Ì333f3™3Ì3ÿ3f3fff™fÌfÿf™3™f™™™Ì™ÿ™Ì3ÌfÌ™ÌÌÌÿÌfÿ™ÿÌÿ333f3™3Ì3ÿ333333f33™33Ì33ÿ33f33f3ff3™f3Ìf3ÿf3™33™3f™3™™3Ì™3ÿ™3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿ3f3fff™fÌfÿf3f33ff3f™3fÌ3fÿ3fff3fffff™ffÌff™f3™ff™f™™fÌ™fÿ™fÌf3Ìf™ÌfÌÌfÿÌfÿf3ÿf™ÿfÌÿfÿÌÌÿ™™™3™™™Ì™™33™f™Ì3™ÿ™f™3f™f3™™f™Ìf™ÿ3™3™™f™™™™™Ì™™ÿ™™Ì™3Ì™fÌf™Ì™ÌÌ™ÿÌ™ÿ™3ÿ™fÌ™™ÿ™Ìÿ™ÿÿ™Ì3™fÌ™ÌÌÌ3™33Ìf3Ì™3ÌÌ3Ìÿ3ÌfÌ3fÌff™™fÌÌfÌÿf™™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÌÿÌ3ÿÌfÿ™™ÿÌÌÿÌÿÿÌ3Ìfÿ™ÿ3Ì33ÿf3ÿ™3ÿÌ3ÿÿ3ÿfÿ3fÿffÌ™fÿÌfÿÿfÌ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿ™ÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÌÿ3ÿÿfÿÌ™ÿÿÌÿÿÿfffÿfÿÿfffÿÿfÿfÿÿ¥!___www†††–––ËË˲²²×××ÝÝÝãããêêêñññøøøÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿ¼áÁâ»ï ÁááÁ™yëðððððð÷áÁáá˜xR1ëððððððííí÷áÁáᘘWRXyëððððððíí÷÷ííïÁáÁ˜xRRyyxëððððððíííï÷’íï ÁáÁ˜xRyyŸŸëððððððííííð¼ï÷’íï¼¼áÁáÁxRXyŸŸÂÂëððððððííííôð¼ïï÷’ï¼áÁÁRRRyŸÂÂÂÂëððððððíííôÿôñð¼ï÷ïïÁÁÁRXŸŸÂÂÂëððððððíííõõõòñð¼÷ïsyñòòÿëððððððííí÷ïïï¼¼¼¼¼¼¼¼ðëððððððííí’’÷÷÷÷ïí’’÷÷÷÷÷ïëððððððííí÷ïïïïïŸÂx’’’’’’÷ëððððððííí¼ðððŸxR˜’ï¼ï’’’÷’ëððððððíííòŸyRx˜Á»Á»ïïïðð¼÷÷’ëññññññïííퟟWRRWÁáÁÁ»ï÷ï¼ñôÿò’ëòòòòòòíïíퟟŸXRRWxÁáÜÁ»ï÷ï¼ñõÿÿëôôôôôôïíïííxXXRRx˜ÁááÁ»»ï÷ïðòÿëõõõõõõïïíïíXXRRxx˜ÁáÁÁ»»ïï÷ï¼ôðìëÿÿÿÿÿÿïïïíïíRRRx˜˜ÁááÁ»º»ïïïìëÿÿÿÿÿÿïïïïíïíRxx˜ÁáááÁ»»»ïïï÷ìëÿÿÿÿÿÿïïïïïíï혘Áá Á»º»»÷ïìëÿÿÿÿÿÿïïïïïïíïÿ»áÁÁốììììììììëÿÿÿÿÿÿïïïïïïïíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìëÿÿÿÿÿÿïïïïïïïïëììììììììììììììììììììììììïìëëëëëëëïïïïïïïïï ïïïïïïïïììëÿÿÿÿÿÿÿïïïïïïïìÿõõõõõõõõõõõõõõõõõõõõõõìëÿÿÿÿÿÿÿÿïïïïïïìÿÿÿõõõõõõõõõõõõõõõõõõÿõìëëëëëëëëëëïïïïïìÿÿÿõìëïïïïìÿÿzz^zzz111111MMMMMÿõìëïïïìÿÿÃzz^zzz111111MMMMÿõìëïïìÿÿ^Ãzz^zzz111111MMMõõìëïìÿÿÃ^Ãzz^zzz111111MMõõìëìÿÿ^Ã^Ãzz^zzz111111Mõõìëÿÿÿÿÿÿìÿÿ^^Ã^Ãzz^zzz111111õõìëëëëëëëÿÿ^^^Ã^Ãzz^zzz11111õõììÿÿz^^^Ã^Ãzz^zzz1111õõììÿÿzz^^^Ã^Ãzz^zzz111õõììÿÿzzz^^^Ã^Ãzz^zzz11õõììÿÿzzzz^^^Ã^Ãzz^zzz1õõììÿÿzzzzz^^^Ã^Ãzz^zzzõõììÿÿzzzzzz^^^Ã^Ãzz^zzõõììÿÿõõììÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõììÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõïìëëëëëëëëëëëëëëëëëëëëëëëëÿÿþÿÿÿÿøÿààÿàÀÿà€ÿàÿàÿàÿàÿàÿàÿà8ÿà8ÿà8ÿàÿàÿàÿàààààààààààÀ€þÿÿ€ÿÀÿàÿðÿøÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿøNovemberDecemberSunMonTueWedThuFriSatSundayMondayTuesday WednesdayThursdayFridaySaturdayJulAugSepOctNovDecJanuaryFebruaryMarchAprilMayJuneJulyAugust SeptemberOctoberInvalid variant type conversionInvalid variant operationInvalid argumentExternal exception %xAssertion failedInterface not supportedException in safecall method%s (%s, line %d)Abstract Error?Access violation at address %p in module '%s'. %s of address %pJanFebMarAprMayJunInvalid pointer operationInvalid class typecast0Access violation at address %p. %s of address %pAccess violationStack overflow Control-C hitPrivileged instruction(Exception %s in module %s at %p. %s%s Application Error1Format '%s' invalid or incompatible with argumentNo argument for format '%s'"Variant method calls not supportedReadWrite$Error creating variant or safe array)Variant or safe array index out of bounds Out of memory I/O error %dFile not foundInvalid filenameToo many open filesFile access deniedRead beyond end of file Disk fullInvalid numeric inputDivision by zeroRange check errorInteger overflow Invalid floating point operationFloating point division by zeroFloating point overflowFloating point underflow&=O8‚7¸ó$B›:ƒÌqSetupUTypesÇSystemSysInitSysConst 3Messages KWindowsSysUtils(h è ¨00h00¨mysql-connector-odbc-5.1.10-src/wix/resources/setup_type_complete.ico100644 15766 12 32166 11707541006 24625 0ustar00cteamstaff00¨&00 ¨%Î(0`€ ÿÿÿãßàâÞßVUæJIŠˆù–•ÕŸï|{¥¬©éÊÇè×Ö㇆ïîòÂÁÄÔÓÕåâæèÕ迼¿üúüîìîòñòäãäçÙæøäöÿïýßÊÚðêîÊÇÉÇÄÆÓÀËçÐÝÛÄÏUSTôòóçåæÚÑÕÝÒÖнμ¹º¸µ¶›˜™×ÔÕÎËÌÉÆÇŠ†‡¨¤¥—“”ÇÃÄÅÁÂÿÀ­©ªáÝÞàÜÝßÛÜÞÚÛÝÙÚÜØÙÛרÚÖ×ÙÕÖ×ÓÔÖÒÓÕÑÒÔÐÑãÝÞÆ¸ºÈÁÂÑÅÆÖ¹»ñÝÞñ§¨ï®¯ð»¼ÿÿ  ÿ¥¥ÿ©©ø¥¥ÿ­­ù®®ÿ´´ë¦¦ÿ»»ð´´þÃÃýËËÿÒÒîÄÄÀ¥¥ôØØþââçÑÑÿêêùééþïïôççÍÃÃÖÌÌøîîéááßÙÙôïï¶²²±®®mkkâßßáÞÞßÜÜÑÎΠžžÿýýëéééççÿþþîÍÌôÔÒê×Ö†_\½šàº·èÜÛžuq£|xÔ§¢þÝÙãÙØ¦‰…·†­›˜ÞØ×Ë¡˜ïêéâÍÈïçåäàß䞌è¸þ÷õðÎÄÎÁ½ëáÞqYQ£“¸©¤ÖÊÆÙÐÍ|{ðäßýÞÐÚØ×Û¸ Ÿ‡wàÒÉÜÏÆüÜÅÚÖÓ¸œ†ÏÀ´È¸©‹rZ±”wzfRüÙ·¼£ŠÝÈ´ÍÉÅÞÛØ zO®Žl•|aÓ²­–|ïb/›vK¨ŠhòΣé˦ÛÅ«êÓ·ïâÒÎxÛ“3ݵèÆ™ûÖ¦îÛÃÖ¤\ûÔ™ÞÝÛäáÙæåâêèàÿÿøôôíþÿó¢§…ëìèÆÌ¹èêç±É§Ÿ»•ÑÝÍm¯hy¯w•0­A=Ç^KØuWæŒG» QÌÇýÿÿÛÝÝïùüøûü©®°àåçÆÎÒíõùëñôÝßàL£í.•ô4Žß‚°Ú®ÍêçíóöúþçëïåéíäçꦼÓÞàäרÚÕÚô¾ÀÓ!!û¨¨¹ÑÑÖááâßßàÞÞßÛÛÜÙÙÚ×רÕÕÖÐÐÑüüüùùùõõõÝÝÝÖÖÖ¡        ¡ rrrrrrrr ?o-¨oo?,,,, sÈ rrrr o.*ii*)42-B,?B-.22-o,¡s  rrr ,o4i0p+++00jiD42.¨.34*ijÛjii)4.o, s   rr o4j+1••k••/(+p05jjj50p+(((((+p5i.B¡Â   r A2i+p40/kk••//(((//••}‘y[‘ƒ•//+0*3¡ö à r -D0júE.0/kkkkkk‚~VVKKZZuG1•(+jƒúO–oCK›sFJ›sFJvJJ‰X"H"ÆÃÆü¼·½ÿÉÇÊÿÏÏÐÿÈÀÂÿïíðÿïìïÿpµjÿ{¹uÿ¦Ì¡ÿÑÝÍÿîéèÿèäâÿÞÙ×ÿÎÉÆÿ¹³°ÿÙ¥¤ÿí®¯ÿþ§§ÿÿ¥¥ÿÿ¥¥ÿÿ¨¨ÿÿ¬¬ÿÿ³³ÿÿººÿÿÁÁÿÿÈÈÿÿÐÐÿÿØØÿÿêêÿþïïÿýïïÿóçèÿéßàûKHH–eI. #ÉÇÉûÐÐÑÿÆÄÇÿ¾¼¿ÿÉÁÂÿïìïÿïëíÿ2š*ÿ ‹ÿ ÿŽÿ•ÿH£Dÿj¨eÿ‚¥|ÿ¿¤™ÿí³³ÿþ««ÿÿ¨¨ÿÿ¦¦ÿÿ¥¥ÿÿ¦¦ÿÿªªÿÿ±±ÿÿ¹¹ÿÿÁÁÿÿÉÉÿÿÒÒÿÿààÿÿïïÿýïïÿûïïÿùïïÿîææÿÜÖÖòF4!ÈQ=&ºeL/®}\:£ŒhAœ˜rG—žwJ”ŸwJ”ŸwJ”ŠY#Ž#ËÉËû¿¼Àÿ¿½ÀÿÐÏÑÿÊÁÃÿïëíÿïëëÿ9£6ÿ—ÿ™ÿœÿœÿ™"ÿ#ÿ2‡3ÿêµ²ÿû´´ÿÿ¯¯ÿÿ¬¬ÿÿ¨¨ÿÿ¦¦ÿÿ¥¥ÿÿ¨¨ÿÿ®®ÿÿ¸¸ÿÿÁÁÿÿËËÿÿÕÕÿÿêêÿþïïÿüïïÿùïïÿ÷ïïÿõïïÿæááÿ§ ›éqqqÅ•––·»»»«ØØØ£ïïïûûûšÿÿÿ™ÿÿÿ™¡yM•"ÆÄÆüÉÇÊÿÔÔÕÿÔÔÕÿËÂÃÿïêëÿïêéÿB°Gÿ §)ÿ"ª.ÿ%­3ÿ'¬6ÿ&¦8ÿ%š8ÿ¢§…ÿð¼½ÿÿ¹¹ÿÿµµÿÿ±±ÿÿ¬¬ÿÿ¨¨ÿÿ¥¥ÿÿ¦¦ÿÿ««ÿÿ¶¶ÿÿÁÁÿÿÍÍÿÿÜÜÿÿïïÿüïïÿùïïÿöïïÿôïïÿóîîÿïëëÿäÞßþttvÐ……½Ž§°nlΦèèèŸøøø›ÿÿÿ™ÿÿÿ™ŸwJ”"ÑÐÑúÕÕÖÿÕÕÖÿÕÕÖÿÌÃÃÿïêèÿïéçÿM¿[ÿ-¹Cÿ0½Hÿ2¾Kÿ2ºLÿ/®Iÿ5 IÿìÂÀÿüÂÂÿÿ¿¿ÿÿ¼¼ÿÿ¸¸ÿÿ³³ÿÿ­­ÿÿ¨¨ÿõ§¦ÿáÌÈÿìâßÿëááÿÒ¿Â÷ÓÀÃäýïïÿùïïÿöïïÿóïïÿòîîÿñììÿðëëÿãÝÞÿ¬©ªçiiwÁ¡³Å¨€ã ööö›þþþ™ÿÿÿ™ŸwJ”"ÒÑÒú×רÿ×××ÿÖÖ×ÿÍÃÃÿïéæÿïèåÿVÊlÿ6ÆTÿ8ÈWÿ8ÈYÿ8ÂWÿ5µSÿo®sÿðÆÆÿÿÈÈÿÿÇÇÿÿÄÄÿÿÁÁÿÿ¼¼ÿÿ¶¶ÿð¯°ÿöæåÿûîíÿêßÛÿÞÏÌÿÚÅËûÜÃÍÿɶ¾ÚõïïÿòîîÿñììÿðêêÿïééÿïèèÿéààÿÖÍÌøpÅ™¶&&½ªÜ¢Š‡óœþþþ™ÿÿÿ™ŸwJ”"ÓÒÓúØØÙÿØØÙÿØØØÿÎÅÃÿïèäÿïçãÿ\Òvÿ>Ðbÿ?Òdÿ?Ñeÿ>Édÿ:»^ÿŸ»•ÿðÉÊÿÿÍÍÿÿÌÌÿÿÊÊÿÿÉÉÿÿÇÇÿÿÂÂÿåÓÛÿþîúÿÁ¤ ÿ¡vsÿ¨€{ÿŸtsüĪ´âÝÃÑÿÙÏÓãïêêÿïèèÿïååÿðââÿñßßÿòÝÝÿçÒÓþ&$pÊTT¸ª¬³«+,Ú¢òœŽŒþ™ÿÿÿ™ŸvJ”"ÖÕÖùÚÚÚÿÙÙÚÿÙÙÚÿÐÆÄÿïçâÿïçàÿaÚ€ÿDÙoÿFÛqÿFÚrÿCÑoÿ?Áhÿ»Æ¬ÿñÎÎÿÿÓÓÿÿÒÒÿÿÑÑÿÿÐÐÿÿÏÏÿèÄÈÿÿïÿÿÚÆÍÿ£|wÿо¨ÿà͵ÿš}sÙstðßÇØýκÇÙòÝÝÿôØØÿõÕÕÿöÓÓÿöÓÓÿ÷ÒÒÿêÍÍÿxpxÐ‰Šˆ¹´´­¬ÅÇУð+,ü™ÿÿÿ™ŸvJ”"×Ö×ùÛÛÜÿÛÛÜÿÚÚÛÿÑÇÅÿïæßÿïæÞÿfáŠÿKâ{ÿLä}ÿLá}ÿIÙyÿDÉrÿÆÌ¹ÿôÔÔÿÿÙÙÿÿÙÙÿÿÙÙÿÿÙÙÿÿÙÙÿÓÀÊÿøäöÿÁ¦©ÿªŽ‚ÿÌ»£ÿÚÇ®ÿ”ƒsÈ™lkøÝÇÚñÕ¿ÏáùÎÎÿùÎÎÿùÎÎÿùÎÎÿùÎÎÿùÎÎÿìÊÊÿ‡}|Ò‡ˆ†¸ª­®«ÒÒË£ÌÏèœyyû™ÿÿÿ™uH”"ÙØÙùÝÝÝÿÜÜÝÿÜÜÝÿÓÈÅÿïåÝÿïåÛÿYá–ÿPê…ÿQë‡ÿQê‡ÿOâ„ÿG» ÿ¿ÃÒÿóÙÙÿÿààÿÿááÿÿââÿÿããÿÿääÿÝÐ×ÿèÍßÿ¿¤©ÿ |uÿ½¬•ÿʸžÿŒuhÖ vvùèÕèÿѺÊÞüÆÆÿüÇÇÿûÈÈÿûÉÉÿûÉÉÿûÊÊÿíÇÇÿƒ}Ï‹Ž‘¶°´¶ªÎÓ×¢ìïðœþþþ™ÿÿÿ™žwK•"ÚÙÚùÞÞßÿÞÞßÿÞÞÞÿÔÉÅÿïäÚÿïäØÿVäŸÿUðŽÿUí–ÿOѽÿF¬áÿ?–ßÿ¤¹×ÿðÝÞÿÿììÿÿññÿÿööÿÿüüÿÿÿÿÿÿÿÿÿѹÆÿÞÄÔÿ¨ƒ‚ÿŸxrÿ¥ƒxÿ›pnüËÁÄãðãðÿر·áÿ¾¾ÿþÁÁÿýÂÂÿýÄÄÿýÅÅÿüÆÆÿîÄÄÿR>*Â]F+°uW4¤‡d=œ”nC—šsF”vJ”‰X"Ž!ÜÛÜùààáÿßßàÿßßàÿÕÊÆÿïã×ÿïãÖÿ[ÔÀÿIÀØÿA§öÿA¤ùÿ@Ÿñÿ<—ãÿ~¯ÜÿòììÿÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØÑÔÿáÍÜÿçÙæÿξÀÿÏÀ¼ÿäáßùÿÿÿÿÜÑÕçý¶¶þÿ¹¹ÿÿ¼¼ÿÿ¾¾ÿþ¿¿ÿþÁÁÿ÷ÁÂÿà³³ë[:!!ÞÝÝùááâÿááâÿááâÿÖËÆÿïâÕÿïâÓÿW©ñÿ7øÿ9žùÿ:žøÿ:œóÿ8–çÿ?×ÿíìïÿþýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãßàÿÕÉÏÿòéòÿüúüÿôóóýж¶êý¬¬þÿ±±ÿÿ´´ÿÿ··ÿÿººÿÿ¼¼ÿÿ½½ÿï¾¾ÿ²†}ïcJ.×yZ9Ћg@Ë–pFÈœvIÆŸwJÅŸwJÅŠY#¾!àßàùããäÿããäÿå϶ÿØÌÆÿïáÒÿïáÐÿP£ðÿ/—÷ÿ0˜÷ÿ1˜öÿ2˜ôÿ1“ëÿ/‹ÝÿºÔëÿòððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîÿÿÈÈÿû­­ÿì žÿÿ  ÿÿ¤¤ÿÿ©©ÿÿ­­ÿÿ±±ÿÿ´´ÿÿ¶¶ÿÿ¸¸ÿýººÿñ¼½ÿ©››å³´´ÙÐÐÐÔçççÐ÷÷÷ÍþþþÌÿÿÿÌÿÿÿÌ¡yMÆ âââøé•ÿí“)ÿí™6ÿÚÎÇÿïàÏÿïàÍÿIŸîÿ'‘öÿ(’öÿ)“öÿ+“ôÿ+‘ðÿ*ŠåÿP˜ÛÿïíïÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøÿÿÚÚÿÿ¿¿ÿÿ««ÿÿœœÿÿŸŸÿÿ££ÿÿ¦¦ÿÿªªÿÿ®®ÿÿ±±ÿÿ³³ÿÿ¶¶ÿð¹¹ÿÏ—‘õ¡£§ÛÄĿֶ·ÖÒ}îÏúúúÍþþþÌÿÿÿÌÿÿÿÌŸwJÅ æÒ»ùïÿïÿí™6ÿÜÏÈÿïßÌÿïßÊÿÆÎÒÿ¸Üÿc¦æÿ6•ðÿ#ôÿ#ñÿ#‰êÿ"ƒÞÿ¢ÅèÿîììÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýÿÿééÿÿËËÿÿ¹¹ÿÿ¨¨ÿÿœœÿÿžžÿÿ¡¡ÿÿ¥¥ÿÿ¨¨ÿÿ««ÿÿ®®ÿÿ±±ÿ÷´µÿð³´ùyXBÞµµ¸Ø¶·ÍÓæÐ÷ÍŽýÌÿÿÿÌÿÿÿÌÿÿÿÌŸwJÅ èÔ¼ùïÿïÿî™7ÿÝÐÈÿïÞÉÿïÝÆÿîÜÄÿîÛÂÿîÛÀÿïÚ¾ÿÖÑÃÿ§¾Îÿw¨ÖÿD‘Úÿ%ÙÿÎßðÿïìíÿÿÿÿÿÿÿÿÿÿòòÿÿÚÚÿÿÅÅÿÿµµÿÿ¦¦ÿÿœœÿÿžžÿÿ¡¡ÿÿ¤¤ÿÿ¦¦ÿÿ©©ÿÿ¬¬ÿú¯¯ÿñ´´ÿ›mmlQ1ÓÏÏÊÔ66áÐóÎ12ùÍþÌ‘ŽÿÌÿÿÿÌÿÿÿÌŸwJÅ éÕ½ùïÿïÿîš7ÿßÑÉÿïÜÄÿîÙ¿ÿí׺ÿìÔµÿìÓ³ÿíÕ´ÿíÕ´ÿíÔ³ÿëÓ²ÿçέÿ×çÿ©°°ÿÚàçÿïìíÿýööÿÿææÿÿÍÍÿÿ¿¿ÿÿ±±ÿÿ¥¥ÿÿœœÿÿžžÿÿ  ÿÿ££ÿÿ¥¥ÿÿ¨¨ÿö­­ÿñ°°ü¥qq<{[7Îáâ×Ñ56éÏŒŒðÍçéóÌ24þÌÿÌÿÌÿÿÿÌŸvJÅ ëÓ·ùïÿïÿîš8ÿÞÑÉÿîÙ¾ÿëÓ³ÿèÌ¥ÿæÅšÿæÄ—ÿèÈšÿéÊžÿêË¢ÿêÌ¢ÿçË¡ÿäÈžÿÛ¿—ÿÒ¸’ÿäØÉÿðáâÿóÔÔÿÿÈÈÿÿ»»ÿÿ®®ÿÿ¤¤ÿÿœœÿÿžžÿÿ  ÿÿ¢¢ÿú¦§ÿשּׂÿí¥¥îWWq7%†c>ÊÛßãÏÊÉãÍîðëÌ÷÷íÌèêõÌÿÌ+,ýÌÿÿÿÌŸvJÅ íÒ³úïÿï–-ÿî×¼ÿÞÐÆÿëÓ²ÿçÇÿà·ÿÛ¨dÿÚ¥]ÿܪbÿ߯jÿà³sÿâ¸yÿã»ÿ⼂ÿἄÿÚ¶€ÿÓ±}ÿÓ³Œÿê¶ÿðÁÂÿð¹ºÿñ°±ÿò©©ÿö¡¡ÿñ¥¥ÿð§§ÿð¨©ÿí££õ¶vv !E. ŽjAÈÜäìÍåèäÌçèæÌèëîÌ÷÷ïÌ×ÚõÌyyüÌÿÿÿÌuHÅ í׿ùðÂŽÿïïðÿïïðÿßÏÅÿêΧÿ㼇ÿèâ×ÿéèàÿãÒ·ÿÙ¶€ÿÒ”=ÿЈÿÔ+ÿ×–3ÿÙ›=ÿÚŸEÿÚ¢Mÿ×£TÿÔ£YÿÍ \ÿËŸeÿ×£~ÿ㢋ÿãŠÿçœÿ̆{ð¨iiÈmEE“U>* •qGÇçóýÌâêòÌèíóÌìðôÌïôúÌøûüÌÿÿÿÌÿÿÿÌžwKÆ íííøññòÿáàâÿÌÊÍÿàÏÅÿéË¡ÿèÝÊÿïùüÿïùüÿïùüÿïùüÿïùüÿîöøÿêèàÿãÑ·ÿܰ}ÿÒ‰3ÿÎuÿÑ|ÿÑ‚ÿІÿ͉ÿÌ*ÿÉ3ÿÉ•CÿÊœSÿ‚hD݇gJ1‡X"¾—rGÅ—qEÅ—qEÅ™qDÅ™qEÅ›sFÅ›sFÅvJʼnX"¾"æææùÊÈËÿ¿¼ÀÿßÞàÿãÓÇÿëÛÂÿïøûÿïùüÿïùüÿïùüÿîøûÿî÷úÿíöùÿìõøÿëô÷ÿëóõÿêñôÿèðñÿãàÛÿÝÇ´ÿÖ¤}ÿÍ|9ÿËf ÿÊhÿÐ~ÿÖ–1ÿ…d9àˆcE+ &ÔÓÕûÓÒÔÿïïðÿòòóÿÜÑÌÿíõ÷ÿïùüÿîøûÿî÷úÿíöùÿìõ÷ÿëóöÿêòôÿéñóÿèïñÿçîðÿæìîÿåêìÿãéêÿâçéÿàäæÿßãäÿÞáâÿÛ×ÓÿÙŲÿÛ»ˆÿ§lí'''•`A'ŠY"íŸvJöuHöŸvJöŸvJöŸwJöŸwJöŸwJöŸwJöŠY#í-îîîøòòóÿíííÿ×ÓÓÿ¬››ÿzXWÿ£ŒÿȾ»ÿÝÛÙÿçëìÿèðòÿçîðÿæíïÿåëíÿäêëÿãèéÿâæèÿàåæÿßãäÿÞáâÿÝàáÿÜÞßÿÛÝÞÿÚÜÜÿÙÚÛÿØÙÙÿרØÿ×××ÿÓÐÐûÁ¼»Ê)vKøÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡yMø 4êêê÷ßÙÙÿÝÝÝÿÚÚÙÿŒheÿ…^[ÿ…^[ÿ…^[ÿ…^[ÿ…]Zÿ…]Zÿ¢‡†ÿ³°ÿÕÎÊÿÞÝÛÿßáâÿÞáâÿÝßàÿÛÞÞÿÚÜÝÿÚÛÛÿÙÚÚÿØÙÙÿרØÿ×××ÿÖÖÖÿÖÖÖÿÖÖÖÿÎÈÆôXSQS ›sGöõúÿÿÿÿ÷ÿÒÓ÷ÿˆ†ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸwJö!@âàßúäääÿâââÿ˾½ÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžupÿª‰…ÿ¾©¦ÿνÿ×ÒÏÿÙØ×ÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÁ·´êZ6 ›sFö÷ùüÿÚÛöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸwJö%÷©¥ìììÿéééÿçççÿàĬÿÔ¬ÿÇŸÿ¼”‰ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ»š•ÿÛÛÛÿáááÿâââÿáÞÛÿÞÚ×ÿÚØ×ÿ¿®ŸêwU6 ™rEöÿÿùÿ<<ûÿÿÿ23þÿÿÿ‘ŽÿÿÿÿÿÿÿÿÿÿŸwJö%ëäÞïñññÿïïïÿîëåÿûÓ•ÿûÔ—ÿûÔ›ÿûÕŸÿùÔ£ÿòÌ¥ÿìÆ§ÿçÁ¨ÿß¹¦ÿ×°¢ÿѪŸÿË£šÿË£šÿË£šÿË£šÿË£šÿέ¥ÿáááÿéæçÿñããÿõáàÿùççÿùééÿ±¤£ÓvV7™qDöþÿóÿ9:úÿöÿèêôÿ24þÿÿÿÿÿÿÿÿÿŸvJöœ™™JøøøÿöööÿôôôÿöäÈÿûÓ–ÿûÔšÿûÕžÿûÕ¢ÿüÖ§ÿü׬ÿüزÿüÙ·ÿüÚ¼ÿüÚÂÿüÛÇÿýÜËÿõÓÇÿðÎÅÿìÊÁÿåÁ¹ÿß¾·üèÎÍóüßßÿúääÿúççÿùééÿøëëÿ÷êêþLIIŠV9!—pEöêïóÿÐÏéÿðòíÿøøîÿèêõÿÿÿ+,ýÿÿÿÿÿŸvJö ëãá¿üüüÿû÷ñÿúêÐÿû× ÿûÔ™ÿûÕÿûÕ¡ÿüÖ¦ÿü׫ÿüرÿüÙ¶ÿüÙ»ÿüÚÁÿüÛÆÿýÜÊÿýÝÏÿýÝÓÿýÞÖÿýÞÙÿýÞÙÿüÝØþ Œ‹¤ûããÿúççÿùèèÿùêêÿøììÿ÷ííÿá×׿ T7 —qEöâëóÿçêæÿèéçÿèëîÿ÷÷ïÿ×ÚõÿyyüÿÿÿÿÿuHö@@@ ëâà«ðÕ±ÙûÓ“ÿûÓ•ÿûÔ˜ÿûÔœÿûÕ¡ÿüÖ¥ÿüתÿüذÿüصÿüÙºÿüÚÀÿüÛÅÿýÜÊÿýÜÎÿýÝÒÿýÞÖÿýÞØÿýÞÙÿýÞÙÿñÒÍðLëÖÕäùèèÿùêêÿøëëÿøííÿ÷ïïÿ÷ððÿ²¯¯“/˜sHøéõÿÿãëóÿèíóÿìðôÿïôúÿøûüÿÿÿÿÿÿÿÿÿžwKø ñÊ•àûÓ•ÿûÔ˜ÿûÔ›ÿûÕ ÿûÖ¤ÿüשÿü×®ÿüØ´ÿüÙ¹ÿüÚ¿ÿüÛÄÿýÜÉÿýÜÍÿýÝÑÿýÞÕÿýÞØÿýÞÙÿýÞÙÿýÞÙÿáÄ¿Ç5œ‘‘^ùééÿøëëÿøííÿ÷îîÿ÷ððÿöññÿôððùttt9 ˆX"í˜rGö—qEö—qEö™qDö™qEö›sFö›sFövJö‰X"ííÇ‘‰ïÉ•·îÇ™ÉôÎäôÏ¡ëøÒ§õü×­ÿüسÿüÙ¸ÿüÚ½ÿüÛÃÿýÛÈÿýÜÌÿýÝÑÿýÝÔÿýÞ×ÿýÞÙÿýÞÙÿýÞÙÿýÞÙÿδ°ƒ ëÛÚÄøììÿ÷îîÿ÷ððÿöññÿöññþîçæàÐÏÏc  ³›…7½£‘D¨™JàÁ³ƒàÁ¶‘çÇ¿¥íÍÆÉìÍÇÉôÔÏçöÖÐí÷×ÒòýÞÙÿ»¤ E§ŸŸ(ôéèóîàßÜâÕӦĿ¾T    ÿÿÿÿÿÿÿÿøÿÿüÿÿð?ÿÿÀÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿøÿÿÿÿmysql-connector-odbc-5.1.10-src/wix/resources/setup_type_custom.ico100644 15766 12 32166 11707541006 24327 0ustar00cteamstaff00¨&00 ¨%Î(0`€ ÿÿÿus´¥£ÈÀ¼èˆ† lgŠïîô¤¢¬ïîñãâäßÞàÓÒÔîìïÃÁľ»¾üúüíìíÝÜÝÖÕÖüëúÊÇÉÇÄÆãÍÚÓÀËäÝáÛÄÏUST¥£¤•“”€÷õöçåæ¶´µ«©ªðëíнÃyvwŽˆ…†º·¸²¯°¯¬­¨¥¦š—˜éæçæãä×ÔÕÓÐÑÎËÌÊÆÇÇÃÄÅÁÂ×ÓÔÖÒÓÕÑÒ•ŒŽÜÓÕïêëÆ¸ºÈÁ›‘’ñ§¨ù´µï®¯ð»¼ÿÿ  ÿ¥¥ÿ©©ø¥¥ÿ­­ÿ±±ù®®ë¦¦ÿµµÿ¹¹ÿ½½ð´´þÁÁþÆÆÿÉÉÿÍÍûÉÉÿÒÒîÄÄùÎÎÿÙÙþààôØØþääÿêêòÞÞùééþïïôççÍÃÃÖÌÌøîîÏÇÇþ÷÷öððßÙÙ¶²²óïïêææŸœœkiiÑÎο¿¢  ‹‰‰ÿýýíëëëééÿþþÙØØzXW›poàŸÚ¹¸íÍÌêµ²ôÔÒ†_\Ö¥¡ÒÆÅžvq£}x¶‡¦‰…¼™¯¨§èáàïéèņýÞØ¼©¥Ë¡—«™•âÍÈ䞌è¸ðÎÄÎÁ½ÚÐÍÖÊÆïãßéÝÙýõò ‘‹ÞÙ×Ûµ¤“„}ïèåýÞÏõ®Æ±£ÜÏÆáÞÜüÜÅáÓÉ˺¬tjbÏÀ´…kTÆ·ªüÙ·ÝÈ´µ¦—œvK¡zOðÂŽŽtW©Šh®Žl¢„dÓ³}tjïî˜3b/•zZòÏ¥éÉ£ÛÅ«êÓ·ïâÒÎuݵðÌžûÖ¦¯˜yêΪͻ£îÛÃÒ‘7Ö¤\åřψûÔ™¤‰ëèàøøïÿÿøþÿóÆÌ¹±É§Ÿ»•ÑÝÍm¯hy¯w•çèç0­A=Ç^KØuWæŒG» QÌÇýÿÿïùüíöùøûüèëìëòõèîñàäæÞâäåêíáëòÛÞàãçêL£í/–ô3àöúþ£»ÚÓÖÚÅÉÑÏ×ñèêõ‹‘ìÿ-.ýÚÚÜÏÏÑòòóááâÙÙÚÕÕÖÔÔÕüüüùùùØØØÖÖÖ¢¤¤¤¤¤¤¤¤¢5l21105////¤rrrrrrrr¤l(g!!m3l0/57112321l/¤- ---rrr¤/1mg+j,jj+*!(m224;())**)g(ml/¢ . ---rr¤lm)n'%%%'o,n+*)g)*n,&&&n„g0¦ø . ---r¤7gj,-n'kkk%'o&&&'%™‚ƒw‹‚o+!4‰õŸ . --r¤l;+*  2+o%k%kkkkzNAAUUyy$=oj)©öþŸ.. -r¤/2(ö< # :…<,%vJ@EHKMOPRT{\’',–1t . r¤/1<  #qiø5Ή}@EDGGKLOPTTZ^_”8'™!ï Õ-r¤64(l< ÒÓÐÑr.—cg}@EDDDGHLOQRW[^b_\'£™É‰œ¢¢¤¤¢740m< ÖÔÔÔÔÖÒÉwzGEEDDEHLOQTX^^ebš9&*m0fø7m1ö<:qÔÔÔÔÔÔÔÔz?GGDDDEGLORT[^^beb…+£²É©¦¢¢74ïûû<:†ÒÔÖÖÖÖÖÉALKHGEDDGKORX^e^ehhq',!öŸrqq¢740úúú`††ÒÖÖ×ÖÖÖUOOLLHGD>’“…x^behh†#Ÿ*&f.qq¢640þÿþ`†šØ××××ÖÓUQPPOMKIZ^”‘$e#h†:†…aòÕrq¢64ûîþþΓšÒ×××××ÐyRRQQPOw€;:š_\\\òíqq¢64ùù~šÊÙØØØØ×ÎyTTTTTU€Âº‚€2\Y{{{{yòòq¢64ÿõù~“ÊÓØØÙØÚÎ{WWWWWñ‰‚¹ÉvVVVVVVy&n;öòíq¢64tç~“»ÛÙÙÙÙÚïYXXZZZ5w€©Â™€PSSSSVU–nm6ðq¢64ù ’»»ÙÙÙÛêé\[••p$€€vñ@MOOOPOUo"î qq¢53õø ’“»Ûééééëí#s9\$…9LLMMOOUz™–©‰Â¢¢54øøø’»»éêêéêêë ü…ñ÷;GHKKLLMAw,!9…Ÿ53 º’»»éêêêêëë÷^QIJCDEGHKKLLM¥¥¶°®¯¯¯«53ø˜´´žÃ»éêêêêêëé üdWMGBCCDEGHKKA‡)çqýý¯53º³³´ž»ÃÑíÛêêêêëíqp[QLEBBDDEEHH?N­ö à÷ýý¯/3º³³´‘ÃÃÃÃÃÃÎíòéëð•WPKDBCCDDEEINwu1tè÷ýý¯/3º³³´‘çúºº§ººÁ¹ÐädZRMHDBBCDDEI@„"­î Õ e÷ý¯/3º³³´¡ºº¸Æ¬ÆÆ¸¸¸ÆÆ±¡\{PMGDBBCDF@J„)m¶ç Õq ÷ý¯/º³´ÃžºÆ½ÅÅŽ½¬½¬½±½±ŽUAN>JJ>>>w*(3l°ääãàý¯/3ì ž¸¬”ÊνÄÇÇÄÄÅ´ÅÅÅÀ½‡€‚"(20°äçããà ý¯53÷ø1Œ¸ÃÝÝÝÝÝÞʨ½Ä¼¼ÇÇÇÇÄÄŶk'n!20®æãèñâÞ÷ý¯74  ¡ºÝÝÝÝÝÞÞáááá⟨½Ç¼¼³Ç­ko©c«®°®®®®®¯«0m ÷‘ÞÝÝÞÞÞÞááââååèèããä—¹ÆÀ%o"ll( ÷6‹u–;ùåâåâååèèããç ççùõþþÿ0340µ«ª««««««µ92grfù|||||||8œžäø çõùþþÿÿÿÿcg36«Ü«63*…øø;‚‰‘tÿÿÿÿÿÿ(,)/ªìÍðò«7mÎààÕ¸±±‡ƒùøøŸ——%g/«ßñôóò«lm”÷†ÈÈÈ¿··¸¸˜˜}wŠŠŠw}ø-_\]]g%)5ªÌôóôóò«71ý Èȿȿ¿§§     Ž¨yXZ]]b]8)47ªÍôòñôóò«/üË ¿ÈÈÈ¿¿§¿§§ T››ˆˆˆW*\]_]]b9,)37ªhËñóô«§ÈÈȿȿ¿§§    ››ˆˆXˆ{j”]b]bbe3(25ªæàÕñËñòª¾ÈÈÈÈ¿¿§¿§§  ››ˆˆˆˆˆŽg<]]bbeee2lªÞæââÞß«ºÁÁ·¿·§§§§  ››ˆˆˆˆˆˆŒ32”bbee/µªªªªª«ª«µ/7a0l‘aŒŒyyy{{{ˆ‘06_…/17—/555/ÿÿÿÿüÿÿüÿáüÿÿøÿÿàÿÿàÿÿÀÿÿÀÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ ÿÿ€ÿÿ€ÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿ@ÿÿÀÿÿÀ<ÿÿÀ¼ÿÿüHÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(0` €% ‰Y#HžvJJuHJuHJŸvJJŸvJJŸwJJŸwJJŸwJJŠY#H   œvJKþÿÿMýþÿMÿÿÿMÿÿÿMÿÿÿMÿÿÿMÿÿÿMÿÿÿM¡yMK '.331+%    ˜rEKõùüMñòöMöô÷M÷÷÷MýùýMÿýÿMÿÿÿMÿÿÿMŸwJJ $3ALRSOIA:2+% "(.38:;74.% ‘lBMíò÷NëëëNðòõMöõ÷Mø÷÷MýúýMÿüÿMÿÿÿMŸwJJ %8L++,lmuvsld[RIB;879=CLRX]_^ZTL@4(…c;QÜáçPæèêNëìëNðòõM÷ôøM÷÷÷MýúýMÿÿÿMŸwJJ4Nonn˜Â½ÀóçæíüþÄê–’–ÎWWY¬Œ…}ume_]\_fnwfHHžhhµ®Æ¼ŽŽÏ˜vvºˆll¯)!!„nbSC1"tV4YÅÍÓUØÙÛPæèêNëìëNñòöM÷õøM÷÷÷MÿÿÿMŸvJJ *HŸœÃÑÑÒÿÇ¿ÂÿïîõÿïîôÿïîóÿïíòÿíëïÿÉÄÆïœ˜šÓdcd²“ˆ„„‡‹š¯uuÔæ¨¨øð´µÿñ¸¹ÿð½¾ÿñÀÁÿðÄÄÿðÈÉÿñÍÍÿÖ··åvjj¨r`L7`F*d­²µ\ÁÅÈUÙÚÜPçéêNïïðMñòöMöô÷MþÿÿMuHJ OOOH¼¹ºâÐÐÑÿÈÅÉÿÇ¿ÃÿïîôÿïíóÿïíòÿïíñÿïìðÿïìïÿïìíÿîêëÿéååÿÅ¿¿ô‹‹ÛUSRòŠYYÓ릧ýð®®ÿü¬¬ÿÿ°°ÿÿ¶¶ÿÿ¼¼ÿÿÁÁÿÿÇÇÿÿÍÍÿÿÓÓÿõÖ×ÿïÝÞÿ×ÉÉå744‹gPL9"r•—f®¯°[ÄÈÌTØÚÜPçéëNïððMðòõMýÿÿMuHJ ÈÆÇñÌÊÍÿ¾¹¿ÿÃÀÄÿÈÀÂÿïíòÿïíñÿïìðÿïìïÿïìîÿïëíÿîêêÿëææÿåàßÿÙÔÓÿÈÃÀÿ³¬ªÿØ Ÿÿí¬­ÿû§§ÿÿ§§ÿÿªªÿÿ®®ÿÿ´´ÿÿ»»ÿÿÁÁÿÿÇÇÿÿÎÎÿÿÕÕÿÿããÿÿîîÿðããÿæÛÛ÷WRRœi<-ƒw€„r–žcº¿ÃXÐØÝRåëðNñõûMõùüMþÿÿMvKK"ÆÃÆü¼·½ÿÉÇÊÿÏÏÐÿÈÀÂÿïíðÿïìïÿpµjÿ{¹uÿ¦Ì¡ÿÑÝÍÿîéèÿèäâÿÞÙ×ÿÎÉÆÿ¹³°ÿÙ¥¤ÿí®¯ÿþ§§ÿÿ¥¥ÿÿ¥¥ÿÿ¨¨ÿÿ¬¬ÿÿ³³ÿÿººÿÿÁÁÿÿÈÈÿÿÐÐÿÿØØÿÿêêÿþïïÿýïïÿóçèÿéßàûKHH–) @0~T?&kmQ0\€_;Sk@M–qEK›sFJœvJJ‰Y#H#ÉÇÉûÐÐÑÿÆÄÇÿ¾¼¿ÿÉÁÂÿïìïÿïëíÿ2š*ÿ ‹ÿ ÿŽÿ•ÿH£Dÿj¨eÿ‚¥|ÿ¿¤™ÿí³³ÿþ««ÿÿ¨¨ÿÿ¦¦ÿÿ¥¥ÿÿ¦¦ÿÿªªÿÿ±±ÿÿ¹¹ÿÿÁÁÿÿÉÉÿÿÒÒÿÿààÿÿïïÿýïïÿûïïÿùïïÿîææÿÜÖÖò!!!‚[=$#ËÉËû¿¼Àÿ¿½ÀÿÐÏÑÿÊÁÃÿïëíÿïëëÿ9£6ÿ—ÿ™ÿœÿœÿ™"ÿ#ÿ2‡3ÿêµ²ÿû´´ÿÿ¯¯ÿÿ¬¬ÿÿ¨¨ÿÿ¦¦ÿÿ¥¥ÿÿ¨¨ÿÿ®®ÿÿ¸¸ÿÿÁÁÿÿËËÿÿÕÕÿÿêêÿþïïÿüïïÿùïïÿ÷ïïÿõïïÿæááÿ§ œÝ3& G5!ŠaI.wyZ9iŽjB`œuHZŸwJYŸwJYŠY#V"ÆÄÆüÉÇÊÿÔÔÕÿÔÔÕÿËÂÃÿïêëÿïêéÿB°Gÿ §)ÿ"ª.ÿ%­3ÿ'¬6ÿ&¦8ÿ%š8ÿ¢§…ÿð¼½ÿÿ¹¹ÿÿµµÿÿ±±ÿÿ¬¬ÿÿ¨¨ÿÿ¥¥ÿÿ¦¦ÿÿ««ÿÿ¶¶ÿÿÁÁÿÿÍÍÿÿÜÜÿÿïïÿüïïÿùïïÿöïïÿôïïÿóîîÿïëëÿåßßýa``³dee–ŽŽŽ€···pÛÛÛeóóó_ÿÿÿ\ÿÿÿ\¡yMY"ÑÐÑúÕÕÖÿÕÕÖÿÕÕÖÿÌÃÃÿïêèÿïéçÿM¿[ÿ-¹Cÿ0½Hÿ2¾Kÿ2ºLÿ/®Iÿ5 IÿìÂÀÿüÂÂÿÿ¿¿ÿÿ¼¼ÿÿ¸¸ÿÿ³³ÿÿ­­ÿÿ¨¨ÿõ§¦ÿáÌÈÿìâßÿëááÿÒ¿Â÷ÓÀÃäýïïÿùïïÿöïïÿóïïÿòîîÿñììÿðëëÿãÝÞÿ§¤¥Ø[[Xœkk~†\Z¬tÒÒÒhîîî`ûûû]ÿÿÿ\ŸwJY"ÒÑÒú×רÿ×××ÿÖÖ×ÿÍÃÃÿïéæÿïèåÿVÊlÿ6ÆTÿ8ÈWÿ8ÈYÿ8ÂWÿ5µSÿo®sÿðÆÆÿÿÈÈÿÿÇÇÿÿÄÄÿÿÁÁÿÿ¼¼ÿÿ¶¶ÿð¯°ÿöæåÿûîíÿêßÛÿÞÏÌÿÚÅËûÜÃÍÿɶ¾ÚõïïÿòîîÿñììÿðêêÿïééÿïèèÿéààÿÕËËôGGP¢ x‹¤wrrÊjííí`ûûû]ÿÿÿ\ŸwJY"ÓÒÓúØØÙÿØØÙÿØØØÿÎÅÃÿïèäÿïçãÿ\Òvÿ>Ðbÿ?Òdÿ?Ñeÿ>Édÿ:»^ÿŸ»•ÿðÉÊÿÿÍÍÿÿÌÌÿÿÊÊÿÿÉÉÿÿÇÇÿÿÂÂÿåÓÛÿþîúÿÁ¤ ÿ¡vsÿ¨€{ÿŸtsüĪ´âÝÃÑÿÙÏÓãïêêÿïèèÿïååÿðââÿñßßÿòÝÝÿçÒÓþ" W©t žyÇk…ƒêaûûû]ÿÿÿ\ŸwJY"ÖÕÖùÚÚÚÿÙÙÚÿÙÙÚÿÐÆÄÿïçâÿïçàÿaÚ€ÿDÙoÿFÛqÿFÚrÿCÑoÿ?Áhÿ»Æ¬ÿñÎÎÿÿÓÓÿÿÒÒÿÿÑÑÿÿÐÐÿÿÏÏÿèÄÈÿÿïÿÿÚÆÍÿ£|wÿо¨ÿà͵ÿš}sÙstðßÇØýκÇÙòÝÝÿôØØÿõÕÕÿöÓÓÿöÓÓÿ÷ÒÒÿêÍÍÿF>kµ@@nŽŽ–z'(ÆkçbŒŠû]ÿÿÿ\ŸvJY"×Ö×ùÛÛÜÿÛÛÜÿÚÚÛÿÑÇÅÿïæßÿïæÞÿfáŠÿKâ{ÿLä}ÿLá}ÿIÙyÿDÉrÿÆÌ¹ÿôÔÔÿÿÙÙÿÿÙÙÿÿÙÙÿÿÙÙÿÿÙÙÿÓÀÊÿøäöÿÁ¦©ÿªŽ‚ÿÌ»£ÿÚÇ®ÿ”ƒsÈ™lkøÝÇÚñÕ¿ÏáùÎÎÿùÎÎÿùÎÎÿùÎÎÿùÎÎÿùÎÎÿìÊÊÿtgm·mnkšš”y´¶¿kêa*+ù]ÿÿÿ\ŸvJY"ÙØÙùÝÝÝÿÜÜÝÿÜÜÝÿÓÈÅÿïåÝÿïåÛÿYá–ÿPê…ÿQë‡ÿQê‡ÿOâ„ÿG» ÿ¿ÃÒÿóÙÙÿÿààÿÿááÿÿââÿÿããÿÿääÿÝÐ×ÿèÍßÿ¿¤©ÿ |uÿ½¬•ÿʸžÿŒuhÖ vvùèÕèÿѺÊÞüÆÆÿüÇÇÿûÈÈÿûÉÉÿûÉÉÿûÊÊÿíÇÇÿuji±nnm‹•—™wÄĽjÈËä`xxø]ÿÿÿ\uHY"ÚÙÚùÞÞßÿÞÞßÿÞÞÞÿÔÉÅÿïäÚÿïäØÿVäŸÿUðŽÿUí–ÿOѽÿF¬áÿ?–ßÿ¤¹×ÿðÝÞÿÿììÿÿññÿÿööÿÿüüÿÿÿÿÿÿÿÿÿѹÆÿÞÄÔÿ¨ƒ‚ÿŸxrÿ¥ƒxÿ›pnüËÁÄãðãðÿر·áÿ¾¾ÿþÁÁÿýÂÂÿýÄÄÿýÅÅÿüÆÆÿîÄÄÿ\[]¢uwz‡ž £uÄÈÍhèêë`ûûû]ÿÿÿ\žwKY!ÜÛÜùààáÿßßàÿßßàÿÕÊÆÿïã×ÿïãÖÿ[ÔÀÿIÀØÿA§öÿA¤ùÿ@Ÿñÿ<—ãÿ~¯ÜÿòììÿÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØÑÔÿáÍÜÿçÙæÿξÀÿÏÀ¼ÿäáßùÿÿÿÿÜÑÕçý¶¶þÿ¹¹ÿÿ¼¼ÿÿ¾¾ÿþ¿¿ÿþÁÁÿ÷ÁÂÿÞ°°ò:,”R=%kO0n_:c”mC\›sFYvJY‰X"V!ÞÝÝùááâÿááâÿááâÿÖËÆÿïâÕÿïâÓÿW©ñÿ7øÿ9žùÿ:žøÿ:œóÿ8–çÿ?×ÿíìïÿþýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãßàÿÕÉÏÿòéòÿüúüÿôóóýж¶êý¬¬þÿ±±ÿÿ´´ÿÿ··ÿÿººÿÿ¼¼ÿÿ½½ÿï¾¾ÿ¶‹‹½O1 !àßàùããäÿããäÿå϶ÿØÌÆÿïáÒÿïáÐÿP£ðÿ/—÷ÿ0˜÷ÿ1˜öÿ2˜ôÿ1“ëÿ/‹ÝÿºÔëÿòððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîÿÿÈÈÿû­­ÿì žÿÿ  ÿÿ¤¤ÿÿ©©ÿÿ­­ÿÿ±±ÿÿ´´ÿÿ¶¶ÿÿ¸¸ÿýººÿñ¼½ÿ|\HânR2Ô€`:ÎkCÉ™rGÇžwJÅŸwJÅŸwJÅŠY#¾ âââøé•ÿí“)ÿí™6ÿÚÎÇÿïàÏÿïàÍÿIŸîÿ'‘öÿ(’öÿ)“öÿ+“ôÿ+‘ðÿ*ŠåÿP˜ÛÿïíïÿüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøÿÿÚÚÿÿ¿¿ÿÿ««ÿÿœœÿÿŸŸÿÿ££ÿÿ¦¦ÿÿªªÿÿ®®ÿÿ±±ÿÿ³³ÿÿ¶¶ÿð¹¹ÿÏ—‘õ¦§§ÛÂÃÄÖÝÝÝÒîîîÏúúúÍþþþÌÿÿÿÌÿÿÿÌ¡yMÆ æÒ»ùïÿïÿí™6ÿÜÏÈÿïßÌÿïßÊÿÆÎÒÿ¸Üÿc¦æÿ6•ðÿ#ôÿ#ñÿ#‰êÿ"ƒÞÿ¢ÅèÿîììÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýÿÿééÿÿËËÿÿ¹¹ÿÿ¨¨ÿÿœœÿÿžžÿÿ¡¡ÿÿ¥¥ÿÿ¨¨ÿÿ««ÿÿ®®ÿÿ±±ÿ÷´µÿð³´ùyXBÞ³µ¸ØÉÊÍÓàÞáÐïïïÍû÷ûÌÿýÿÌÿÿÿÌÿÿÿÌŸwJÅ èÔ¼ùïÿïÿî™7ÿÝÐÈÿïÞÉÿïÝÆÿîÜÄÿîÛÂÿîÛÀÿïÚ¾ÿÖÑÃÿ§¾Îÿw¨ÖÿD‘Úÿ%ÙÿÎßðÿïìíÿÿÿÿÿÿÿÿÿÿòòÿÿÚÚÿÿÅÅÿÿµµÿÿ¦¦ÿÿœœÿÿžžÿÿ¡¡ÿÿ¤¤ÿÿ¦¦ÿÿ©©ÿÿ¬¬ÿú¯¯ÿñ´´ÿ›mmlQ1ÓÃÇÌÔÖÖÖÐåçéÎñðòÍ÷ööÌýúýÌÿüÿÌÿÿÿÌŸwJÅ éÕ½ùïÿïÿîš7ÿßÑÉÿïÜÄÿîÙ¿ÿí׺ÿìÔµÿìÓ³ÿíÕ´ÿíÕ´ÿíÔ³ÿëÓ²ÿçέÿ×çÿ©°°ÿÚàçÿïìíÿýööÿÿææÿÿÍÍÿÿ¿¿ÿÿ±±ÿÿ¥¥ÿÿœœÿÿžžÿÿ  ÿÿ££ÿÿ¥¥ÿÿ¨¨ÿö­­ÿñ°°ü¥qqÊÕÜãÏßáãÍéëíÌîïîÌñòöÌ÷õøÌ÷÷÷ÌÿÿÿÌŸvJÅ íÒ³úïÿï–-ÿî×¼ÿÞÐÆÿëÓ²ÿçÇÿà·ÿÛ¨dÿÚ¥]ÿܪbÿ߯jÿà³sÿâ¸yÿã»ÿ⼂ÿἄÿÚ¶€ÿÓ±}ÿÓ³Œÿê¶ÿðÁÂÿð¹ºÿñ°±ÿò©©ÿö¡¡ÿñ¥¥ÿð§§ÿð¨©ÿí££õ¶vv !E. Žh?ÈÝãçÍÝáåÌåæèÌëíîÌïïðÌñòöÌöô÷ÌþÿÿÌuHÅ í׿ùðÂŽÿïïðÿïïðÿßÏÅÿêΧÿ㼇ÿèâ×ÿéèàÿãÒ·ÿÙ¶€ÿÒ”=ÿЈÿÔ+ÿ×–3ÿÙ›=ÿÚŸEÿÚ¢Mÿ×£TÿÔ£YÿÍ \ÿËŸeÿ×£~ÿ㢋ÿãŠÿçœÿ̆{ð¨iiÈmEE“U>* ’nBÆÛåçÌÝàáÌßãçÌåçéÌëíïÌïððÌðòõÌýÿÿÌuHÅ íííøññòÿáàâÿÌÊÍÿàÏÅÿéË¡ÿèÝÊÿïùüÿïùüÿïùüÿïùüÿïùüÿîöøÿêèàÿãÑ·ÿܰ}ÿÒ‰3ÿÎuÿÑ|ÿÑ‚ÿІÿ͉ÿÌ*ÿÉ3ÿÉ•CÿÊœSÿ‚hD݇gJ1–rFÆÞïöÌÝçéÌãéîÌäìóÌêðõÌñõûÌõùüÌþÿÿÌvKÆ"æææùÊÈËÿ¿¼ÀÿßÞàÿãÓÇÿëÛÂÿïøûÿïùüÿïùüÿïùüÿîøûÿî÷úÿíöùÿìõøÿëô÷ÿëóõÿêñôÿèðñÿãàÛÿÝÇ´ÿÖ¤}ÿÍ|9ÿËf ÿÊhÿÐ~ÿÖ–1ÿ…d9àˆcE+ ˆX!¾—qEÅ•pCÅ—pBÅ—pEŘqDÅ™rFÅ›sFÅœvJʼnY#¾&ÔÓÕûÓÒÔÿïïðÿòòóÿÜÑÌÿíõ÷ÿïùüÿîøûÿî÷úÿíöùÿìõ÷ÿëóöÿêòôÿéñóÿèïñÿçîðÿæìîÿåêìÿãéêÿâçéÿàäæÿßãäÿÞáâÿÛ×ÓÿÙŲÿÛ»ˆÿ§lí'''•`A'-îîîøòòóÿíííÿ×ÓÓÿ¬››ÿzXWÿ£ŒÿȾ»ÿÝÛÙÿçëìÿèðòÿçîðÿæíïÿåëíÿäêëÿãèéÿâæèÿàåæÿßãäÿÞáâÿÝàáÿÜÞßÿÛÝÞÿÚÜÜÿÙÚÛÿØÙÙÿרØÿ×××ÿÓÐÐûÁ¼»Ê)ŠY"íŸvJöuHöŸvJöŸvJöŸwJöŸwJöŸwJöŸwJöŠY#í 4êêê÷ßÙÙÿÝÝÝÿÚÚÙÿŒheÿ…^[ÿ…^[ÿ…^[ÿ…^[ÿ…]Zÿ…]Zÿ¢‡†ÿ³°ÿÕÎÊÿÞÝÛÿßáâÿÞáâÿÝßàÿÛÞÞÿÚÜÝÿÚÛÛÿÙÚÚÿØÙÙÿרØÿ×××ÿÖÖÖÿÖÖÖÿÖÖÖÿÎÈÆôXSQS vKøÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡yMø!@âßßûäääÿâââÿ˾½ÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžvqÿžupÿª‰…ÿ¾©¦ÿνÿ×ÒÏÿÙØ×ÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÁ·´êZ6 ›sGöõúÿÿÿÿ÷ÿÒÓ÷ÿˆ†ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸwJö%÷©¥ìììÿéééÿçççÿàĬÿÕ­ÿÇŸÿ½•Šÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ¶Ž‡ÿ»š•ÿÛÛÛÿáááÿâââÿáÞÛÿÞÚ×ÿÚØ×ÿ¿®ŸêwU6 ›sFö÷ùüÿÚÛöÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸwJö%ëäÞïñññÿïïïÿîëåÿûÓ•ÿûÔ—ÿûÔ›ÿûÕŸÿùÔ£ÿòÌ¥ÿìÆ§ÿçÁ¨ÿß¹¦ÿ×°¢ÿѪŸÿË£šÿË£šÿË£šÿË£šÿË£šÿέ¥ÿáááÿéæçÿñããÿõáàÿùççÿùééÿ±¤£ÓvV7™rEöÿÿùÿ<<ûÿÿÿ23þÿÿÿ‘ŽÿÿÿÿÿÿÿÿÿÿŸwJöœ™™JøøøÿöööÿôôôÿöäÈÿûÓ–ÿûÔšÿûÕžÿûÕ¢ÿüÖ§ÿü׬ÿüزÿüÙ·ÿüÚ¼ÿüÚÂÿüÛÇÿýÜËÿõÓÇÿðÎÅÿìÊÁÿåÁ¹ÿß¾·üèÎÍóüßßÿúääÿúççÿùééÿøëëÿ÷êêþLIIŠV9!™qDöþÿóÿ9:úÿöÿèêôÿ24þÿÿÿÿÿÿÿÿÿŸvJö ëãá¿üüüÿû÷ñÿúêÐÿû× ÿûÔ™ÿûÕÿûÕ¡ÿüÖ¦ÿü׫ÿüرÿüÙ¶ÿüÙ»ÿüÚÁÿüÛÆÿýÜÊÿýÝÏÿýÝÓÿýÞÖÿýÞÙÿýÞÙÿüÝØþ Œ‹¤ûããÿúççÿùèèÿùêêÿøììÿ÷ííÿá×׿ T7 —pEöêïóÿÐÏéÿðòíÿøøîÿèêõÿÿÿ+,ýÿÿÿÿÿŸvJö@@@ ëâà«ðÕ±ÙûÓ“ÿûÓ•ÿûÔ˜ÿûÔœÿûÕ¡ÿüÖ¥ÿüתÿüذÿüصÿüÙºÿüÚÀÿüÛÅÿýÜÊÿýÜÎÿýÝÒÿýÞÖÿýÞØÿýÞÙÿýÞÙÿñÒÍðLëÖÕäùèèÿùêêÿøëëÿøííÿ÷ïïÿ÷ððÿ²¯¯“/—qEöâëóÿçêæÿèéçÿèëîÿ÷÷ïÿ×ÚõÿyyüÿÿÿÿÿuHö ñÊ•àûÓ•ÿûÔ˜ÿûÔ›ÿûÕ ÿûÖ¤ÿüשÿü×®ÿüØ´ÿüÙ¹ÿüÚ¿ÿüÛÄÿýÜÉÿýÜÍÿýÝÑÿýÞÕÿýÞØÿýÞÙÿýÞÙÿýÞÙÿáÄ¿Ç5œ‘‘^ùééÿøëëÿøííÿ÷îîÿ÷ððÿöññÿôððùttt9 ˜sHøéõÿÿãëóÿèíóÿìðôÿïôúÿøûüÿÿÿÿÿÿÿÿÿžwKøíÇ‘‰ïÉ•·îÇ™ÉôÎäôÏ¡ëøÒ§õü×­ÿüسÿüÙ¸ÿüÚ½ÿüÛÃÿýÛÈÿýÜÌÿýÝÑÿýÝÔÿýÞ×ÿýÞÙÿýÞÙÿýÞÙÿýÞÙÿδ°ƒ ëÛÚÄøììÿ÷îîÿ÷ððÿöññÿöññþîçæàÐÏÏc ˆX"í˜rGö—qEö—qEö™qDö™qEö›sFö›sFövJö‰X"í ³›…7½£‘D¨™JàÁ³ƒàÁ¶‘çÇ¿¥íÍÆÉìÍÇÉôÔÏçöÖÐí÷×ÒòýÞÙÿ»¤ E§ŸŸ(ôéèóîàßÜâÕӦĿ¾T    ÿÿÿÿÿÿÿÿð?ÿÿÀÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿøÿÿÿÿÿÿÿÿÿÿÿÿmysql-connector-odbc-5.1.10-src/wix/resources/setup_type_typical.ico100644 15766 12 32166 11707541006 24462 0ustar00cteamstaff00¨&00 ¨%Î(0`€ & S x†”# +-«=2µJ9¾V@ÇdCÊlFÍ|DÖ@µ²;ªÉ2˜ß/”ç0”ì4—î9šìCåM Ý\¦Ón«Â‚¯µŽ²­“±Ÿ—«›™¢˜šš–˜””–‘’Œ’ŠŠ“‡†—…‚™~™€{{ˆ}z…yx„vs†po‰ni„kd†i\ˆhT˜lF¯s5É{Úâ‚ â…à‰Ü+Ú“4×–>Ô™JÒXÑ¡d΢qÉ y܃»™Œ¶™‘±š–¬œš¨Ÿž¦Ÿ ¤  ¦¡¡§¢¢ª££¯£¢¶£¡¼£ŸÁ¢žÃ£žÄ¢Â£Ÿ¿¥¡º¨¥´«¨±¬©¯¯«¯°¬°±­¯²®²²°´³²·´³ºµ¶½µ·½·¸¾¸¹¾¹º¾º»¿»¼À½½Â¾¾ÂÀÀÃÀÀÅÀÁÅÀÂÆÁÃÆÂÄÇÃÅÇÃÅÈÄÆÉÄÇÉÅÇÊÆÇËÇÇËÇÈËÈÊËÉËÍËÍÐÌÎÑÎÏÑÎÏÓÐÑÔÑÒÕÒÓÕÓÔÕÔÖÕÔÖÕÔÖÕÓÕÖÒÓ×ÎÏÖËÍÖÇÉÖÃÅ×ÂÁØÀ¼ØÀ¸Ø¾³Ú½¯Ú»¨Ý¼¡Þ½šß¼•Ồḓ崔譚ð¦œó£Ÿø¡Ÿû  ý¡¡ý¢¢ý¢¢û¥¥û¨¨ù©©øªªù««û­­ú¯¯ù±±ú²²ú³³û´´ûµµü¶¶û··ú¸¸ú»»ù½½ù¿¿ùÂÀøÃÀöžóɸò̲ðέðШõÒ¢øÓŸùÔúÔ úÕ¥ùÖ©ùÖ®ùײø×·ö׺ôÖ½ðÕÁðÓÅïÓÇðÑÉóÎË÷ÎÌûÍÌûÍÍûÏÏûÓÒüÕÔüØÔýÚÓüÜÒüÝÔüÞÖûÞÕøÝÒôÞÕñÞÖïßÔíßÔçÝÕâÜ×ÞÛÙÞÜÛÞÜÜßÞÝáßÞãáàãâãããääãäæããëããîåäñæåóçæóèèðééðéêÿÿÿðêëñëìòëíñìíóìíõìíöîïöîï÷ïïôïðñïòïñôðóöòõ÷ö÷ùúúûüüüýýýýýý††zuompvz|~~~†{oe_ZYZ_agmuz|~~zupkiiimuz~zi^L$&&&$#MY^cgkppmie`\YNMMNY^ciw|{iZ##K.00/-(&$#MNYYNK#%&(---(&$KY`lzp^#Nxøø‚`%/20/--(((-./*EŽW$--&#Zfw†{fNfxøøø÷÷÷Ýe#.22223+˜¦©­¯±³ÆÇÆc(-$Ncv†wfu|hxø÷÷÷ðððíêà‡N.2˜£¢£¥«®±ÉÊÌÏÖåM.&Neyrwxcmx÷÷ðððïîíäÛ{cQ¢¢Ÿ ¢¤©®±ÉËÍçóòåa.%Yh{pndyzw÷ð !kæß‡iW¥£ŸŸŸ ¤¨®±ÉËÎòõõòåY-#^p†ou|pcuðð /¥¦£ ŸŸŸ¢¦­±ÉÌÒõõõõñä%(Me{ough|xïî ,­¨¥¤ ŸŸ ¤­±ÉÍòõõõõöæ‚.$^pprw‚‚yíí ®®­ª¦¤ ŸŸ£«±ÊÒõõõõöñðßK(Ni|p{‚‚‚yêê !²±°®­¨¤ ¢Ùæå‹Šõõõöñðíäw-Kczp|‚‚‚zêê ³Éɲ±®«ŽòóÙ‡‰ŠmöñðíêêçÚ-$`wp|ÛÛ‚{çæ  ÆËÊÉÉÉɰöóF+++gЇíêæåÖÖÙ$%^rpÛÛÛ|ææ ÇÌÌÌÌËËŠþb+´)+ÛŠÖÕÍÌÌÌÇI%\pp€ÝÝ܇ææÆÏÏÏÏÏÏróFEµ$0‰‰ÈÈÈÈÈÈÇQ%\pp‚ÝÝÝææÖÒÒÒÒççmßF*T)0ኲÉÉÉÉÉÅP$_vp‚ÞÞÞ†æ×Öéòõûýþ㉊++)0b닯±±²²²²G#`yp‚àß߆××áùþþþþþþià‡WVˆþ檭®¯°±±±%Mf{pÛààà××ozðþþþþþþþråïýþ£¦©¬®®¯¯®&ZmpÜââà‚××ðþþþþþþþ󱙟¡¤¦©«­®¯T#axpÝà•;Û×ׂ÷þþþþþüϰ£››Ÿ¢¤¦¨«¬­$Yk~rÚ:99Û××zðüþþþþéÉ­ ››œŸ £¤¦¨­TMcyrÅ999Ú××ÅÅÄ|híþþþõϲªŸ››œŸŸ¡¤¤©£K`rrÅ999Ü×ÄÃÃÂÁÁÁÂc|ðüüèʰ¦Ÿ›››Ÿ ¢¦£L_o~rÅ999ßÃÃö¶··¶¶¶·‘’ÃîçÏÉ®¤Ÿ›››Ÿ¡¥˜N`o|uÙ99=à¶’”•”””“““““•‘ÅDZ­¤›››Ÿ¢¡TYeu~uÙ:”÷൒•“?>??@AAABBBB•—¨¦¡™˜ŸTRM_iy†råç÷áà·”êûûøÙ¶B;8;<=>>??@@AB12-#^l{pêðzfà·ÙûûûûûûúúøÙB78888:>BHPX_fikjf_WK>1#6P“‘’»ËÈÊüïï÷ÿïïöÿÙÕÛ÷±¬°ássuº323ˆ€zqjfdekr}oLL¥´~~ÌÚ¢¢èܨ¨ìß®®îܯ¯ìÅÖss³)""„n^M;* *...R¶´µÞÑÑÒÿÌÈÊÿïîõÿïîôÿïîôÿïíóÿïíòÿïíñÿàÛßúºµ·æ‡…†Ã@??¤‹Œ‘ š—ccË즦úð±²ÿð´µÿõ¸¸ÿø½½ÿøÁÁÿ÷ÅÅÿðÈÉÿðÍÍÿñÑÑÿ۰×A==ŽkWA- }}~_ÇÅÆóÏÎÐÿÁ½ÂÿÌÈÊÿïîôÿïîóÿïíòÿïíñÿïìðÿïìïÿïìîÿîêìÿëçèÿæááÿÕÏÎü¥žìkggÕ`KKÏâšš÷שּׂÿøª«ÿÿ««ÿÿ°°ÿÿ¶¶ÿÿ¼¼ÿÿÁÁÿÿÇÇÿÿÍÍÿÿÓÓÿýÛÛÿðÞßÿðáâý“‹‹ºr[B+  ÈÆÇñÉÇÊÿ¼·½ÿÄÀÅÿÌÈÊÿïíòÿïíñÿïíðÿïìïÿïìîÿïëíÿïëìÿîêêÿèääÿàÛÙÿÑÌÊÿ¼¸µÿ½¡Ÿÿðª©ÿóªªÿÿ¥¥ÿÿ§§ÿÿªªÿÿ®®ÿÿ´´ÿÿ»»ÿÿÁÁÿÿÇÇÿÿÎÎÿÿÕÕÿÿããÿÿîîÿùëëÿìàáÿ³««ÏsY>'  ÃÀÃø½¸¾ÿËÉÌÿÌËÍÿËÇÈÿïíñÿïìðÿ‘ÃÿC¡;ÿn´gÿ™Æ“ÿÃ×½ÿëæåÿåßÝÿØÒÏÿľ¼ÿ»§¤ÿð®®ÿö«¬ÿÿ¦¦ÿÿ¥¥ÿÿ¥¥ÿÿ¨¨ÿÿ¬¬ÿÿ³³ÿÿººÿÿÁÁÿÿÈÈÿÿÐÐÿÿØØÿÿêêÿþïïÿýïïÿùííÿéàáÿ ››ÃpR7!ÇÅÆõÐÏÑÿÄÃÅÿº·»ÿÊÆÈÿïìïÿïìîÿƒ½}ÿ ‰ÿ ‰ÿ ‰ÿ ˆÿ †ÿ5“,ÿY™Qÿy–mÿ꯭ÿô±±ÿÿ««ÿÿ¨¨ÿÿ¦¦ÿÿ¥¥ÿÿ¦¦ÿÿªªÿÿ±±ÿÿ¹¹ÿÿÁÁÿÿÉÉÿÿÒÒÿÿààÿÿïïÿýïïÿûïïÿùïïÿôììÿèââÿjhh¥eF+ !ÇÅÇõ¾»¿ÿ¿½ÀÿÐÏÑÿÌÈÉÿïëíÿïëìÿƒ½|ÿ ‹ÿ ÿÿ ÿŽÿ‰ÿ}ÿ–lÿ︸ÿÿ³³ÿÿ¯¯ÿÿ¬¬ÿÿ¨¨ÿÿ¦¦ÿÿ¥¥ÿÿ¨¨ÿÿ®®ÿÿ¸¸ÿÿÁÁÿÿËËÿÿÕÕÿÿêêÿþïïÿüïïÿùïïÿ÷ïïÿõïïÿëççÿÛÖÖñxV7 ÆÃÅöÈÇÉÿÔÔÕÿÔÔÕÿÍÉÊÿïëëÿïêêÿ‡Â‚ÿ—ÿ™ÿÿÿ›"ÿ“$ÿ0‹3ÿ껺ÿû¼¼ÿÿ¹¹ÿÿµµÿÿ±±ÿÿ¬¬ÿÿ¨¨ÿÿ¥¥ÿÿ¦¦ÿÿ««ÿÿ¶¶ÿÿÁÁÿÿÍÍÿÿÜÜÿÿïïÿüïïÿùïïÿöïïÿôïïÿóîîÿñííÿäßßÿˆ……µdB& ÎÌÍñÕÕÖÿÕÕÖÿÕÕÖÿÎÉÊÿïêéÿïéèÿŒÈ‹ÿ §)ÿ"ª.ÿ%®3ÿ'¯7ÿ'ª9ÿ&ž9ÿ‹©zÿïÃÃÿÿÂÂÿÿ¿¿ÿÿ¼¼ÿÿ¸¸ÿÿ³³ÿÿ­­ÿÿ¨¨ÿ睊ÿåÔÐÿïæäÿìâãÿÑ¿ÂöнÁãýïïÿùïïÿöïïÿóïïÿòîîÿñììÿðëëÿèããÿËÆÆçnL.  ÏÍÎñ×רÿ×××ÿÖÖ×ÿÏËËÿïéçÿïéæÿ’Еÿ-¹Cÿ0½Hÿ2¿Lÿ3½Mÿ1´Kÿ-¥Fÿ˼«ÿõÇÇÿÿÈÈÿÿÇÇÿÿÄÄÿÿÁÁÿÿ¼¼ÿÿ¶¶ÿã´´ÿûëëÿûîíÿèÝÙÿÞÐÍÿÛÆÌûÜÃÍÿɵ½ÚõïïÿòîîÿñììÿðêêÿïééÿïèèÿïææÿâØÙþ" €S3  ÐÎÏñØØÙÿØØÙÿØØØÿÐÌÌÿïèåÿïèäÿ—מÿ6ÆTÿ8ÈWÿ9ÉZÿ9ÆYÿ7¼Wÿ5ªRÿîÊÉÿþÎÎÿÿÍÍÿÿÌÌÿÿÊÊÿÿÉÉÿÿÇÇÿõ¿Áÿôäðÿúëõÿº™•ÿ¢xtÿ¨zÿ¡uuûÇ­¸äÝÃÑÿ×ËÐÞïêêÿïèèÿïååÿðââÿñßßÿòÝÝÿçÓÓÿzll§X7  ÒÐÑñÚÚÚÿÙÙÚÿÙÙÚÿÓÎÎÿïèãÿïçáÿ›Û£ÿ>Ðbÿ?Òdÿ@Ógÿ@Ïgÿ=ÄbÿO¶iÿñÎÏÿÿÓÓÿÿÓÓÿÿÒÒÿÿÑÑÿÿÐÐÿÿÏÏÿѾÈÿÿïÿÿ̳·ÿ¦€zÿÕíÿäѸÿ”wnÕŸvwðßÇØýмÉÙòÝÝÿôØØÿõÕÕÿöÓÓÿöÓÓÿ÷ÒÒÿêÍÍÿœ……¹Z9  ÓÑÒñÛÛÜÿÛÛÜÿÚÚÛÿÔÎÏÿïçàÿïæßÿžß¨ÿDÙoÿFÛqÿGÜtÿFØrÿBÌmÿ_¿yÿðÒÒÿÿÙÙÿÿÙÙÿÿÙÙÿÿÙÙÿÿÙÙÿÿÙÙÿȼËÿøäöÿ°Žÿ²˜‹ÿÒÀ©ÿß˱ÿ‹|l¼™lkøÚÅ×ìØÃÔæùÎÎÿùÎÎÿùÎÎÿùÎÎÿùÎÎÿùÎÎÿìÊÊÿ®ÁY8  ÕÓÔñÝÝÝÿÜÜÝÿÜÜÝÿÕÑÐÿïæÞÿïåÜÿ‹Û±ÿKâ{ÿLä}ÿMäÿLá}ÿHÏ‚ÿ\«µÿðÖ×ÿÿààÿÿààÿÿááÿÿââÿÿããÿÿääÿĺÇÿèÍßÿ²‘’ÿ¦‡}ÿij›ÿѾ¤ÿƒpcÉqpýãÐãöÕ¿ÐáüÆÆÿüÇÇÿûÈÈÿûÉÉÿûÉÉÿûÊÊÿíÇÇÿ©ˆˆ»U5  ÖÕÕðÞÞßÿÞÞßÿÞÞÞÿÖÒÑÿïåÛÿïäÚÿy׸ÿPê…ÿQë‡ÿQç”ÿKȼÿCŸäÿF“ÕÿñÛÛÿÿèèÿÿììÿÿññÿÿööÿÿüüÿÿÿÿÿååêÿàÄÕÿÒ¹ÅÿŸsrÿ¥„zÿ­€ÿ—mjø¿¯±àðãðÿÓ´¼Ýÿ¾¾ÿþÁÁÿýÂÂÿýÄÄÿýÅÅÿüÆÆÿîÄÄÿ—wwªO1  Ø××ðààáÿßßàÿßßàÿØÓÒÿïäÙÿïã×ÿ~×ÀÿOÙ¯ÿG¼ÚÿA¦ùÿA¢öÿ>œëÿ:Ùÿàáèÿùóôÿÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿȸ¾ÿåÑáÿÜÍ×ÿ¾§¦ÿÀ§¤ÿÔÌÊõÿÿÿÿëåéðù´µùÿ¹¹ÿÿ¼¼ÿÿ¾¾ÿþ¿¿ÿþÁÁÿþÂÂÿîÁÂÿdNN‰F* ÚÙØðááâÿááâÿááâÿÚÔÔÿïãÖÿïâÕÿ˜Âäÿ7øÿ9žùÿ:Ÿùÿ:žöÿ9šîÿ7‘ßÿ¬ÌçÿðîîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüýÿÍÃÆÿêÞéÿòéòÿüúüÿÿÿÿÿÛÓÓí着öÿ±±ÿÿ´´ÿÿ··ÿÿººÿÿ¼¼ÿÿ½½ÿù¾¾ÿ뺺ø^<" ÜÛÛðããäÿããäÿãáÞÿÜÖÕÿïâÔÿïáÒÿ”¾âÿ/—÷ÿ0˜÷ÿ1™÷ÿ3™÷ÿ3—ñÿ1åÿ_¡ÜÿïííÿÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîîÿñÁÁÿÚ®­ÿÚ¯«ÿç¡¡ûÿ¤¤ÿÿ©©ÿÿ­­ÿÿ±±ÿÿ´´ÿÿ¶¶ÿÿ¸¸ÿÿººÿï¼½ÿÀÁM0  ÞÝÝðåáÞÿ鹂ÿî ÿÝØ×ÿïáÑÿïàÏÿ»áÿ'‘öÿ(’öÿ)“öÿ+“õÿ+“óÿ+êÿ)…ÛÿÄØëÿóñòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøÿÿÚÚÿÿ¿¿ÿÿ««ÿÿœœÿÿŸŸÿÿ££ÿÿ¦¦ÿÿªªÿÿ®®ÿÿ±±ÿÿ³³ÿÿ¶¶ÿû·¸ÿí¸¸û@//k=$ âØÎñî‰ÿïÿï‡ÿàÚØÿïàÎÿïßÍÿ¹ÊÖÿ`¥èÿ3”ñÿ#Žõÿ#Žõÿ$Žóÿ$‹îÿ#†äÿK•ÚÿïíïÿûúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýÿÿééÿÿËËÿÿ¹¹ÿÿ¨¨ÿÿœœÿÿžžÿÿ¡¡ÿÿ¥¥ÿÿ¨¨ÿÿ««ÿÿ®®ÿÿ±±ÿÿ³³ÿí¸¸ÿ¾‰‰¶F-  åÕÅòïÿïÿï‡ÿâÜÙÿïßÌÿïÞÊÿïÞÈÿïÝÆÿïÝÅÿÑÐÊÿ£½ÔÿrªÞÿ@–èÿ…éÿ~Ýÿ‰¸åÿíêëÿþýýÿÿÿÿÿÿÿÿÿÿòòÿÿÚÚÿÿÅÅÿÿµµÿÿ¦¦ÿÿœœÿÿžžÿÿ¡¡ÿÿ¤¤ÿÿ¦¦ÿÿ©©ÿÿ¬¬ÿÿ®®ÿð´µÿ禧å O3 åÖÇñïÿïÿï‡ÿäÝÛÿïÞÉÿïÜÅÿîÛÁÿîÙ¿ÿíØ¼ÿîØ»ÿîØ»ÿîÙ»ÿíÖ¹ÿÞθÿ©·½ÿv¾ÿ¸ÏãÿïìíÿûúúÿÿøøÿÿææÿÿÍÍÿÿ¿¿ÿÿ±±ÿÿ¥¥ÿÿœœÿÿžžÿÿ  ÿÿ££ÿÿ¥¥ÿÿ¨¨ÿÿªªÿï±²ÿꨨìJ33]4!  çØÈðïÿïÿï‡ÿåßÝÿîÜÄÿíØ¾ÿìÔµÿêЮÿêΪÿêϪÿëЫÿìÑ®ÿìÒ­ÿêÑ­ÿæÎ©ÿÞÆ¢ÿÓ¼˜ÿáÕÃÿðëìÿóääÿÿÚÚÿÿÈÈÿÿ»»ÿÿ®®ÿÿ¤¤ÿÿœœÿÿžžÿÿ  ÿÿ¢¢ÿÿ¤¤ÿù©©ÿÿãœØ8&&R1!  éÚÊðïÿïÿï›8ÿåàÝÿíÙ½ÿëѰÿæÇÿ⻈ÿà¶~ÿâºÿ侈ÿæÁÿèÅ”ÿçÇ–ÿæÇ–ÿäÄ•ÿÞ¿’ÿÕ·‹ÿÖ¿ŸÿîÕÎÿïËËÿðÁÂÿû¸¸ÿÿ¬¬ÿÿ££ÿÿœœÿÿÿþ  ÿô¦§ÿ磌ÿï§§øº{{œ@,  êÛËðï‰ÿïÃÿïïðÿèàÝÿìÔ³ÿçÇœÿß¹„ÿݽÿÔ˜GÿÓ”:ÿךGÿØ OÿÛ¥Vÿݪ]ÿß®dÿà²kÿß³pÿܳsÿÖ°tÿΩrÿÖ®Šÿæ²¢ÿñµ³ÿð°°ÿñ©©ÿñ¤¥ÿð¥¦ÿò¦¦ÿÙŽŽÞ°qq¥/R4% êäÞïñêäÿññòÿââãÿèàÝÿêЪÿäÀÿêëæÿïùüÿïùüÿíóóÿèäØÿáÌ­ÿÙ­nÿÑ‹ ÿÑ‚ ÿÓˆÿÖ#ÿØ•.ÿÖ—7ÿÔš@ÿЛHÿÌ›OÿÇšRÿÉ›^ÿÍšhÿÍškÿ‹fOÚ‡iN7#  ëéêïììíÿÌÊÍÿ¼º½ÿçàÝÿëÏ©ÿéâÖÿïùüÿïùüÿïùüÿïøûÿîøûÿî÷úÿíöùÿìõøÿëñòÿçâÚÿßȱÿÙ¦xÿÑ€-ÿÍoÿÎuÿÎ|ÿÍ€ ÿÍ…ÿÏ0ÿÑžKÿpV5Õ†dF- "ÜÚÜô¼¹½ÿÎÍÏÿíìîÿëäáÿìÞÊÿï÷úÿîøûÿî÷úÿíöùÿìõøÿìô÷ÿëóöÿêòõÿêñôÿéðòÿèïñÿçîðÿæíïÿäêìÿãéëÿßÙÔÿÙ¿­ÿÓœwÿÍy7ÿÑy ÿÚ“%ÿoQ(ׇbD+ %ÞÜÞòæåçÿòòóÿððñÿÛ×ÖÿÛ×Õÿéîðÿìô÷ÿëóöÿêòõÿêñôÿéðòÿèïñÿçîðÿæíïÿåëíÿäêìÿãéêÿâçéÿâæèÿáåæÿàäåÿßâãÿÞáâÿÜßàÿÛÞßÿÛÙÑÿʾ±ùž—–Õ~}—LLLV' -êèéïññòÿâßÞÿÖÕÕÿ± Ÿÿ~XVÿ}WUÿ}WUÿ ‡‡ÿŸµÿÙÕÒÿâããÿäéëÿãéêÿâçéÿâæèÿáåæÿàäåÿßâãÿÞáâÿÝàáÿÜßàÿÛÞÞÿÛÝÝÿÚÜÜÿÙÛÛÿÙÚÚÿØÙÙÿרØÿ×××ÿÈÃÂÛ999- 4ÜÙÙæÝÛÚÿÝÝÝÿÛÛÛÿ |ÿ“lhÿ“lhÿ“lhÿ“lhÿ“lhÿ“lhÿ“lhÿ“kgÿ£…ÿ½ª§ÿÑÇÃÿÚ×ÔÿÜÞÞÿÛÞÞÿÛÝÝÿÚÜÜÿÙÛÛÿÙÚÚÿØÙÙÿרØÿ×××ÿÖ××ÿÖÖÖÿÖÖÖÿº¯¬Ó7!2/)GãáàýäääÿâââÿÔÌËÿ¨{ÿ¨{ÿ¨{ÿ¨{ÿ¨{ÿ¨{ÿ¨{ÿ¨{ÿ¨{ÿ¨{ÿ¨{ÿ¨{ÿ¨{ÿ¨zÿ¬ˆƒÿ¼¥¡ÿÓÊÄÿÛ×ÓÿÙØÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÖÖÿÖÔÒÿ™Œ‰ÃR4 %Æ»¯ªìììÿéééÿçççÿìÕµÿç¿”ÿ๕ÿر•ÿ̤’ÿÄœÿ½•ÿ½•ÿ½•ÿ½•ÿ½•ÿ½•ÿ½•ÿ½•ÿ½•ÿ½•ÿ½•ÿɱ­ÿÛÛÛÿáááÿççèÿèæçÿåàßÿàÛÙÿ‘sÌvT5 %ìæáññññÿïïïÿíìêÿúÕšÿûÓ–ÿûÔšÿûÕžÿûÕ¢ÿüÖ§ÿûÖ¬ÿóέÿíÈ­ÿè®ÿຫÿÚ³§ÿÕ­¢ÿÎ¥œÿÎ¥œÿÎ¥œÿÎ¥œÿѺ´ýâááÿíààÿôÜÛÿúæåÿùççÿùééÿƸ·ßvV7¬¨§SøøøÿöööÿôôôÿõçÐÿûÓ–ÿûÔ™ÿûÔÿûÕ¡ÿüÖ¦ÿüתÿüذÿüصÿüÙºÿüÚ¿ÿüÛÄÿýÜÉÿýÜÍÿýÝÑÿöÕÌÿòÐÉÿíÊÃÿÙ»·ïø××üüßßÿúääÿúççÿùééÿøëëÿøììÿlhh—W:! íåãÊüüüÿûôéÿúæÇÿûןÿûÔ˜ÿûÔœÿûÕ ÿûÖ¤ÿüÖ©ÿü×®ÿüسÿüÙ¹ÿüÚ¾ÿüÛÃÿýÛÈÿýÜÌÿýÝÐÿýÝÔÿýÞ×ÿýÞÙÿýÞÙÿæÈÃèÀ¨§¸ûããÿúççÿùèèÿùêêÿøììÿ÷ííÿëââñX7!@@@ êâà–ìË¡ÈûÓ“ÿûÓ”ÿûÔ—ÿûÔ›ÿûÕŸÿûÕ£ÿüÖ¨ÿü×­ÿüزÿüÙ·ÿüÚ¼ÿüÚÁÿüÛÆÿýÜËÿýÝÏÿýÝÓÿýÞÖÿýÞÙÿýÞÙÿýÞÙÿγ¯¾&##Oôàßõùèèÿùêêÿøëëÿøííÿ÷ïïÿ÷ððÿÉÅŬ0 ðÉ•ÚûÓ“ÿûÓ–ÿûÔšÿûÕÿûÕ¢ÿüÖ¦ÿü׫ÿüذÿüÙ¶ÿüÙ»ÿüÚÀÿüÛÅÿýÜÊÿýÜÎÿýÝÒÿýÞÕÿýÞØÿýÞÙÿýÞÙÿýÞÙÿª•’}1Á²²}ùééÿøëëÿøííÿ÷îîÿ÷ððÿöññÿõòòþ›ššL 侎iïÈ“«îÈ—½îÈšÎôΞåõÏ£íùÔ©øüׯÿüØ´ÿüÙºÿüÚ¿ÿüÛÄÿýÜÉÿýÜÍÿýÝÑÿýÞÕÿýÞØÿýÞÙÿýÞÙÿýÞÙÿýÞÙÿ€pnAñáàáøììÿ÷îîÿ÷ððÿöññÿõïïüíåäÛÔÓÓh  À¦?½¤‘D˰ RáÁ´‹àÁµ‘êÊÁ´íÍÆÉíÍÇËöÖÐíöÖÐíùÙÔ÷ûÜÖó ÓÈÇIôééõíßÝ×áÔÑœ±®®C    üÿÿÿÿÿÿð€ÿÿÿàÿÿÿÀ?ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€?ÿÿ€ÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿàÿÿÿÿþ?ÿÿÿmysql-connector-odbc-5.1.10-src/wix/resources/webpage.ico100644 15766 12 61176 11707541006 22151 0ustar00cteamstaff 00h– èþ(æ 00¨ ¨¶h^"00 ¨%Æ'  ¨nM h^(0`€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ‡wwwwwwwwwwwwwwwwpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpÌÌÌDÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpÌAÌÿÿÿÿÿÿÿÿÿÿÿÿpÄ€DDLDDÿÿÿÿÿÿÿÿÿÿÿpÄDÄÄÌÌDÿÿÿÿÿÿÿÿÿÿpÄDLÌÌÌÌÌOÿÿÿÿÿÿÿÿÿpÌADLLÌÌÌÌÌÄÿÿÿÿÿÿÿÿÿpÌÀDDÄÌÌÌÌÌÌÌOÿÿÿÿÿÿÿÿp<ÄDLLÌDDDLÌÌÄÿÿÿÿÿÿÿÿp|ÄDDÌÄÿÿÿÌÌÌOÿÿÿÿÿÿÿp<ÌDÄÌOÿÿÿðDDDOÿÿÿÿÿÿÿpsÌÄLÌOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp7ÌÄÌÌÿÿÿÿÿÿÿp7<ÌLÌÌÌÌÌÌÌÌÌOÿÿÿÿÿÿÿp3¼ÌÌÌÌÌÌÌÌÌÌÌOÿÿÿÿÿÿÿp;³ÌÌÌÌÌÌÌÌÌÌÌOÿÿÿÿÿÿÿpC¿ÌÌÌDDDDDÌÌÌOÿÿÿÿÿÿÿp;¼ÌÌOÿÿÿÌÌÌOÿÿÿÿÿÿÿpCûÌÌOÿÿÿÌÌÄÿÿÿÿÿÿÿÿpD;¼ÌÄÿÿðLÌÌÄÿÿÿÿÿÿÿÿpDÃûÌÌOÿÌÌÌÄÿÿÿÿÿÿÿÿpL;¼ÌÄDLÌÌÌOÿÿÿÿÿÿÿÿpLÃ;¼ÌÌÌÌÌÌOÿÿÿÿÿÿÿÿpÌÌ;<ÌÌÌÌÄÿüÿÿÿÿÿÿÿpÌÌÌÌÌÌÌÌOÿüÿÿÿÿÿÿÿpÌÌÌÌÌÌDÿÿüÿÿÿÿÿÿÿpüÌÌÌÄÿÿÿüÿÿÿwwwwpÿÿÿÌÌÿÿÿÌÿÿøˆˆˆˆ€ÿÿÿÿüÌÿüÏÿÿÿÿÿÿÿüÌÌÿÿÿÿÿÿxÿÿÿÿÿÿÿÿÿÿÿÿÿ÷€ÿÿÿÿÿÿÿÿÿÿÿÿÿxÿÿÿÿÿÿÿÿÿÿÿÿ÷€ÿÿÿÿÿÿÿÿÿÿÿÿxÿÿÿÿÿÿÿÿÿÿÿ÷€ÿÿÿÿÿÿÿÿÿÿÿxÿÿÿÿÿÿÿÿÿÿÿ‡€ÿÿÿÿÿÿÿÿÿÿÿˆÿÿÿÿÿÿÿÿÿÿÿ€ˆˆˆˆˆˆˆˆˆˆˆˆÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿÀðàÇÀÏÀÏ€Ž„€€€€€€€ÀÀÀààððøüþÿÿÀÿÀÿÀÿÀÿÀÿÀÿÀÿÀ?ÿÀÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿ( @€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ‡wwwwwwwwwwpÿÿÿÿÿÿÿÿÿÿpÿÿÿÿÿÿÿÿÿÿpÿÿÿÿÿÿÿÿÿÿpÌÏÿÿÿÿÿÿÿÿÿÿpLŒÿÿÿÿÿÿpLDÌÌÄOÿÿÿÿÿpÄDLÌÌÌÄÿÿÿÿÿpÄDÌDLÌÌOÿÿÿÿp¡p.ËŽ>ûÄzïëæÿüøõψ"â•'æ™)å–)ê,ç™,Ó‹(ÿ«6ñ¥7ó©:ù­>Ù–9ý¹Sâª\¶•eýóäÿýúäš&æ)Ø“(é /ßœ5ô®Cú´Gë¬Iê¹lÿçÂþõçú¬(ê¤*ç¡)àŸ)ë¨0ù´5ó¬4ÅŒ*õ±;ã±VÔ»ìª+ó³5ü»;ì±;ï°/÷¼;Ú¬LúËgÛ¶gñ¶/óº6øÉYáÄ{ßĆûæ´ûñÚöÄAøÉDã¹KüÖsìÒˆûà•ïÚ£óæÃèÂNåÄ\íØ–àΚëâÅÿþûæÉ`ëÐvüá„ùæ¡øÔMö×WþéñìÙûå`ôÝeýí›ÿö´ÿõ•ÿö©üópÿþ²ÿþ¾ýýýúúúõõõpppÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ))))))))Rq<;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÿ)ƒªÃ–š„45(toCq<<<<><><;;;;;;;;;;;;;;;;;;;; ÿªÂ¬mÿ‹4ln(´o*****+8q<<=;;;==;;=;;;;;;==; ÿ¿Â¬(ÿlŒ56n777)(9:o+q<>===@==@==@===== ÿ¾Â  ÿ7¡‹4‹¡¡‹ln6n9p+q>?@@?@@??@????@ÿ¾Â  ÿfiн²²ØÔààñâÙm6'9oC>?A??A?AA?A???ÿ¾Â  ÿm±ŠŠŠ‰Š¯¼½²Øàíõøç´")pCBDADDAADAAAArÿ¾ÂÉÿÿ¡ŠŠ‰‰ˆˆˆˆˆ®¯¼²ÔàñööÚ6'CBDEEEEEEEEEEÿË»&ÿÿ»°®œˆ††gggg†…œ¯¼ØÔíøûøn'CDEEEEEEEEEEÿ³ÂÏk!±°¯®œ…†‚dggggdg†›®Ê²ÔíøûãnnoFGGGGGGGGGGÿ³ÍÏɄȯ®œ™™‚‚d3fffeg…®¯½ÔíøûÚ&9+HGGGGGGGGGÿºÇÏÏÈ®œº«—–ƒ„4´Žm$fg†œ¯½Øíøö´'psIIIIIIIII ÿºÇÏÎÈÆºª«©––ƒ4:CGII+43g†œ¯½Ôñöîn:CGIIIIIIIIÿºÊÏÒÈÆº¨¨–§§„9CIJJJJ+„3ed‡œ¼ÔâøŽ:CIJJJJJJJJÿËÒÒÎÆÄ¸©§§–kKLLLMLsŽ44444‹ŒŽosJLLuLuLLLÿËÐÒ×Ñø·§§§&:ONNNNNNNMMMMNMMNNNNNNMNNNMNNNÿÕÒ×ÎÍÄ···”%&6666666""""""""""6)p,wwwwwwwwÿÕÐÝÎÑÍĸ¸§¦•••¬¬¬ffffff3hhiŸË¿5‘,--------ÿÕÝÝ×ÑÃÃÄŸ¸ªªªª™™™™™††…ˆ‰ŠØà5,PPPPPPPPÿÕßÞÝ×ÑÍÄÄ···¨«««««——‚‚g†ˆ‰Š²ÔnOPPPPPPPPÿÙËåïÝ×ÑÍŦ•¦¦¦•••”˜‚‚‚††ˆ‰²²nRQSSSSSSSSÿÙçÙØïÝ×ÑÅ mmmmmmm‹˜—‚—™ˆŠ½¾)z.UUUUUUUUÿÙñçÕðïÝ×Å‘,....Qym¨«———™ˆŠ½¡9yUWWWWWWWWÿÙõñãëðïÞ×j#zWXWWTx•«¨©—«™œ¯½4vTWXXXXWXXXÿÙáúñãæðïÞÉ!#xyVRv¸¨¨¨««™œ°(R{YXYYYXXXXÿÙøúñçìðïÝÉž&')7 ¹¸¨¨¨«ª›¯°¡‘VXYYYYYYYYYÿÙîûú÷çìðïÞÆÉ„„¬ÅÃĸ¨¸ªº®Ê¯(z|/~~~/////~ÿÙøûúøàáôïïÝÖÖÍÍÍÃÄĸºÆÊÊ‹x0~ÿÙöûúøíçéçôðïÝ×ÖÑÍÍÃÆÆÈÊÊ¡|y0ZZZZZZZZÿÙöûûú÷îãâóóïÞ×ÖÑÑÑÑÎÇб0Zµ¢R|[[[[[[[[ÿÙîûûúø÷ñùùóïÞ××××ÒÒÐŒx\^]]qoy]]]]]]]]ÿÙÙøúúúø÷ùùóðïÞÞÝåÓp}1____Àz________ÿÙÙÙÙõõñíóðïïðÓ´R’^222_2sz2_““22_Zÿ*òäÙÙÙÙÙÙÙÙØmtvR’a`a2aÌŽz22ÿ*þ````2```Üâðæ‘vxz\£}ËŽ}`*ý`bbbbbbbbb[ãóØÕ‘‘‘‘Ó½R£b²¥¥¥€€$*ý¤b¤¤¤¤¤¤¤b¤¤“ÛíàÔÔØÔy1ab8²½­­h*ý¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶Áܵ¶¶¶¶¤<²²°Œ8ý cc ¶¶¶¶¶¶¶¶ê ¶ êê¶c ê¶¶;²²³8ücc ccc ccccc cccccccc c Û¢8    cc û8üü ýýýýýþþþþþþþþ££Ì8>¡p.ËŽ>ûÄzïëæÿüøõψ"â•'æ™)å–)ê,ç™,Ó‹(ÿ«6ñ¥7ó©:ù­>Ù–9ý¹Sâª\¶•eýóäÿýúäš&æ)Ø“(é /ßœ5ô®Cú´Gë¬Iê¹lÿçÂþõçú¬(ê¤*ç¡)àŸ)ë¨0ù´5ó¬4ÅŒ*õ±;ã±VÔ»ìª+ó³5ü»;ì±;ï°/÷¼;Ú¬LúËgÛ¶gñ¶/óº6øÉYáÄ{ßĆûæ´ûñÚöÄAøÉDã¹KüÖsìÒˆûà•ïÚ£óæÃèÂNåÄ\íØ–àΚëâÅÿþûæÉ`ëÐvüá„ùæ¡øÔMö×WþéñìÙûå`ôÝeýí›ÿö´ÿõ•ÿö©üópÿþ²ÿþ¾ýýýúúúõõõpppÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ   ÿ{.QLQIIIKGKGKGKGKGKGÿ9Rqqq¡p.ËŽ>ûÄzïëæÿüøõψ"â•'æ™)å–)ê,ç™,Ó‹(ÿ«6ñ¥7ó©:ù­>Ù–9ý¹Sâª\¶•eýóäÿýúäš&æ)Ø“(é /ßœ5ô®Cú´Gë¬Iê¹lÿçÂþõçú¬(ê¤*ç¡)àŸ)ë¨0ù´5ó¬4ÅŒ*õ±;ã±VÔ»ìª+ó³5ü»;ì±;ï°/÷¼;Ú¬LúËgÛ¶gñ¶/óº6øÉYáÄ{ßĆûæ´ûñÚöÄAøÉDã¹KüÖsìÒˆûà•ïÚ£óæÃèÂNåÄ\íØ–àΚëâÅÿþûæÉ`ëÐvüá„ùæ¡øÔMö×WþéñìÙûå`ôÝeýí›ÿö´ÿõ•ÿö©üópÿþ²ÿþ¾ýýýúúúõõõpppÿÿÿÿÿÿÿÿÿÿÿÿ    ÿ+yKLÿp¡k%%n,Iÿ»¡ ˆ†¯ì¿!ÌHGÿm± ©‚4+šššnsJÿpà ©k8ÀÀÀLNCN ÿpÞ·kkkkkk5yPÿmpÖ¦¾¾h…yWÿmp +„±:{~ÿ´ŽäÆÉÉ·Ã[[ÿmmñùÞ׎¿12ÿoŽmŽmm×o1VoosŸocŽooooooooð9àˆŠ€½ íÚ@psIII€@nn Šˆ€g…ÀÔñà'àJJàIà(0` #/////////////////////////////////# #kk#’__ÿ]]ÿ\\ÿŽ[[ÿZZÿŒYYÿ‹XXÿ‰VVÿ‡TTÿ‡TTÿ†SSÿ„QQÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿƒPPÿ‡VVÿ‡VVÿ‡VVÿ‡VVÿ/”aaÿòÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿÌÌÿÿΨÿ‡VVÿ/S, _6Jk?urD¢n@²f<¾^5ŸX0‹O+uS0R†VNÿ¿¹´ÿøÑªÿýÖ¯ÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿÍ©ÿ‰XWÿ/QnÕ‹&ôïž(ÿó (ÿå”(ÿÌ…)ÿÅ,ÿ¯o)ýŽX"äh;·nD4ÿ›‹}ÿÖ®ˆÿïÇŸÿúÓ«ÿýÕ®ÿüÔ®ÿüÔ®ÿüÔ®ÿþÖ¯ÿþÖ¯ÿþ×°ÿþ×°ÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿرÿÿΩÿŠZXÿ/§m(5êš'÷ü«&ÿÑŠ#÷žbÀŠRV¿t&:¾v)MÃz.}Å1»¾z1û¥q?ÿ…gMÿœuSÿ¸‘mÿÖ®‹ÿ⻕ÿà»”ÿߺ“ÿá»”ÿá½–ÿçšÿïÈ¡ÿõΧÿúÔ­ÿýÖ°ÿÿÙ²ÿÿزÿÿزÿÿزÿÿÙ²ÿÿÙ²ÿÿزÿÿزÿÿÙ²ÿÿزÿÿزÿÿزÿÿزÿÿزÿÿزÿÿÙ²ÿÿÙ²ÿÿزÿÿͪÿŒ[Zÿ/Ø+±ú«'ÿÒŒ$úlA´L,1ê·ÿÒ‘Fÿšl;ÿuS6ÿ†cDÿ”qQÿ’oOÿmNÿ“oOÿ—uRÿ ~\ÿ¯Œjÿ¿yÿÒ¯ŠÿæÂœÿõЩÿûÖ°ÿÿײÿÿÙ³ÿÿÙ³ÿÿÙ³ÿÿÙ´ÿÿÙ³ÿÿÙ³ÿÿÙ´ÿÿÙ³ÿÿÙ³ÿÿÙ´ÿÿÙ³ÿÿÙ³ÿÿÙ³ÿÿÙ³ÿÿÙ³ÿÿΫÿ]\ÿ/æœ(Òû¬'ÿŸgì=‚M, R(O( -L-WŸŠ~ÿ•oIÿÎŒ<ÿÃ5ÿ®r0ÿ¾‚8ÿÆŒ=ÿÃŽBÿ¼ŒCÿ±…Eÿœv@ÿƒc;ÿzZ;ÿcEÿ¤„cÿÇ¥ƒÿ㿚ÿóЫÿýرÿÿÚµÿÿÙµÿÿÙµÿÿÚµÿÿÙµÿÿÙµÿÿÚµÿÿÚ´ÿÿÙµÿÿÚµÿÿÚ´ÿÿÚµÿÿÚµÿÿÙµÿÿÍ­ÿŽ_]ÿ/éž%Ùû¬&ÿ˜c!í9 ‡P- S-N+Ee:•’Z#Ñ΄4ÿä”:ÿô£Aÿþ±Mÿÿ¶Oÿÿ½QÿÿÇXÿÿÏbÿÿ×pÿÿß}ÿÿæÿÿßÿåÄ}ÿ«‰Uÿ}\5ÿw[>ÿ¢„dÿЮ‰ÿî˦ÿü׳ÿÿÚ¶ÿÿÛ¶ÿÿÚ¶ÿÿÚ¶ÿÿÛ¶ÿÿÚ¶ÿÿÛ¶ÿÿÛ¶ÿÿÚ¶ÿÿÛ¶ÿÿÚ¶ÿÿÚ¶ÿÿÚ¶ÿÿÍ­ÿa_ÿ/è$Øû­'ÿ¡l#ò; ±V0"V,O-_d&ÊÚŽ5÷ò >ÿù¥?ÿö¢>ÿôŸ<ÿôŸ=ÿó¡<ÿô§<ÿõ¬@ÿö´Gÿø¼QÿùÆ\ÿûÓoÿýà…ÿÿîÿÿó©ÿëØ˜ÿ»šcÿoR1ÿ‡jNÿ¢ÿëɤÿûصÿþÚ·ÿÿÛ·ÿÿÛ¸ÿÿÛ¸ÿÿÛ·ÿÿÛ·ÿÿÛ¸ÿÿÛ·ÿÿÛ·ÿÿÛ·ÿÿÛ·ÿÿÜ·ÿÿͯÿ’caÿ/äš'Õú¯)ÿ†(ùE&ÎM+_U6+`8tɃ1ìò¥<ÿö¨?ÿó£;ÿòŸ9ÿñ8ÿðš7ÿï˜6ÿï—6ÿï™6ÿðž6ÿñ¢7ÿò¨:ÿô¯Bÿ÷½QÿùËbÿûØwÿþç’ÿÿø®ÿÿ÷·ÿáňÿ{^5ÿ~aFÿ¼ž{ÿèÇ¢ÿû×µÿþܸÿÿܹÿÿܹÿÿݹÿÿܹÿÿܹÿÿݹÿÿܹÿÿݹÿÿݹÿÿܺÿÿ̯ÿ“ecÿ/Û—+Äù®*ÿÝž1ýY5ãC#œf?à˜5üü¯@ÿ÷©=ÿò£9ÿïž7ÿí›5ÿì—2ÿë•2ÿë“2ÿì“2ÿì’2ÿë’2ÿí—2ÿïœ3ÿð 4ÿò©9ÿõ´Eÿ÷ÂVÿùÐlÿüà‡ÿþ÷«ÿÿÿÃÿÿò®ÿ„h<ÿz_Dÿ½ž~ÿìÊ©ÿýÛ¸ÿÿÝ»ÿÿÝ»ÿÿÝ»ÿÿÝ»ÿÿÝ»ÿÿÝ»ÿÿÝ»ÿÿÝ»ÿÿÝ»ÿÿÝ»ÿÿ̱ÿ•geÿ/Ë2¡õ«*ÿû¸:ÿ‹\õ?"ÔÖ’,øù­<ÿôª:ÿñ¥7ÿïž5ÿìš2ÿê–0ÿè”.ÿè‘/ÿé’/ÿê“1ÿë‘1ÿé1ÿé1ÿê“1ÿí˜2ÿïŸ3ÿò¦7ÿô°>ÿ÷½NÿúÏgÿüáˆÿþó§ÿÿÿÃÿòÝžÿ†g?ÿƒhLÿίŒÿõÔ²ÿÿÞ¼ÿÿÞ½ÿÿÞ½ÿÿÞ½ÿÿÞ½ÿÿÞ½ÿÿÞ½ÿÿÞ½ÿÿÞ½ÿÿÞ½ÿÿ̱ÿ—igÿ/½ƒ1oï¨,ÿÿ½9ÿÈ/ý³v!ûõ«3ÿó¨7ÿñ¦6ÿî¡4ÿë›0ÿé˜/ÿè•.ÿæ“,ÿæ’-ÿè’.ÿå.ÿÞ‡-ÿ×*ÿÕ~*ÿ×€+ÿ߉-ÿé“0ÿíœ2ÿð£5ÿó¬<ÿ÷ºIÿùÎeÿüá…ÿþó©ÿÿý½ÿàÄŠÿmN0ÿ ‚dÿââÿûÛ»ÿÿß¿ÿÿß¿ÿÿß¿ÿÿß¾ÿÿß¿ÿÿß¾ÿÿÞ¿ÿÿÞ¿ÿÿÞ¿ÿÿ̲ÿ™khÿ/®w*æ 0ÿú¶5ÿý¾?ÿûº:ÿõ¬4ÿñ¨6ÿî£3ÿëŸ0ÿè›-ÿç–,ÿå“*ÿæ’+ÿå’,ÿÛŠ+ÿÄv%ÿ±t;ÿ¿Œ_ÿÆ•hÿ¼Kÿºk%ÿÓ~)ÿç’0ÿìš1ÿïŸ4ÿò©:ÿöµCÿúÉ_ÿüß…ÿÿö¬ÿÿúºÿ¼šcÿx[?ÿÃ¥…ÿôÖµÿÿàÁÿÿàÁÿÿàÁÿÿàÁÿÿàÁÿÿàÀÿÿàÁÿÿàÁÿÿàÀÿÿΫÿ]\ÿ/ÄŽ<¬ú´3ÿý¸9ÿ÷²6ÿñ¬5ÿî§1ÿë¡0ÿêœ-ÿç™+ÿå–*ÿä“)ÿå”*ÿÝŒ*ÿ²r/ÿ¶’pÿêË­ÿüÞÀÿÿáÃÿÿáÃÿåÁ¡ÿ¿x:ÿÚ„,ÿê“1ÿí˜1ÿðŸ4ÿô¨9ÿøµDÿûÌeÿþåÿÿø²ÿüä¡ÿ†d@ÿ¨ŠkÿèÊ«ÿþÞÀÿÿáÃÿÿáÃÿÿáÃÿÿáÃÿÿáÃÿÿáÃÿÿáÃÿÿáÃÿÿÍ­ÿŽ_]ÿ/šj$7ñ±;ÿû¼<ÿ÷¸:ÿñ®4ÿì¦0ÿé .ÿè›*ÿæ˜)ÿä”)ÿã•'ÿä•)ÿ½w%ÿŸcÿéÌ®ÿýßÂÿÿâÅÿÿâÅÿÿâÅÿÿâÅÿäÀžÿÉt&ÿÞ‡.ÿà‰/ÿã0ÿç–2ÿíŸ6ÿò¯FÿùËlÿýßÿÿð©ÿÇž`ÿ­ŽoÿçȪÿþáÂÿÿâÅÿÿâÅÿÿâÅÿÿãÅÿÿâÅÿÿâÆÿÿâÆÿÿâÅÿÿÍ­ÿa_ÿ/‚YÛ 4Õ÷º=ÿø¼;ÿó²6ÿí¨0ÿé +ÿåœ(ÿä˜(ÿã”&ÿä•'ÿæ•(ÿ‰Z,ÿºžƒÿ÷Û¿ÿÿãÈÿÿãÇÿÿãÇÿÿäÈÿÿãÇÿïÓ·ÿÇ‘aÿ¶r2ÿµq4ÿµq2ÿ·t4ÿ¼z5ÿÀ€8ÿÅŽJÿË^ÿÕ«rÿÓ©oÿήŽÿíгÿýáÄÿÿãÈÿÿãÇÿÿäÇÿÿãÇÿÿäÇÿÿãÈÿÿãÈÿÿãÇÿÿͯÿ’caÿ/·(lí²7ÿø½<ÿõ·7ÿð­1ÿê¥,ÿç)ÿã™&ÿâ—&ÿä—'ÿà‘'ÿlK/ÿ«ŽuÿùÞÃÿÿäÊÿÿåÊÿÿäÊÿÿåÊÿÿäÊÿÿåÊÿÿäÊÿÿäÉÿÿäÉÿÿäÉÿÿåÉÿÿåÊÿÿäÉÿÿäÉÿÿäÊÿÿäÊÿÿäÊÿÿäÊÿÿåÉÿÿäÊÿÿåÉÿÿåÊÿÿåÊÿÿäÊÿÿäÉÿÿäÊÿÿäÊÿÿäÊÿÿ̯ÿ“ecÿ/¨v$ Ϙ1¼õ»<ÿö»9ÿñ²2ÿíª-ÿè£*ÿäœ&ÿâ˜%ÿåš%ÿØŒ$ÿ`<ÿqN.ÿwS3ÿyT4ÿyT4ÿyT4ÿyT4ÿyT4ÿyR4ÿrP4ÿpO4ÿpO4ÿpO4ÿpO4ÿqO4ÿqO5ÿqN4ÿrO3ÿsQ5ÿtS8ÿŒnPÿƨ‹ÿóÖ½ÿÿæÌÿÿæÌÿÿæÌÿÿæÌÿÿåÌÿÿæËÿÿæËÿÿæÌÿÿ̱ÿ•geÿ/©|)bè°<ü÷Á>ÿó¸6ÿï°0ÿì¨+ÿç£(ÿæž'ÿåœ(ÿá–'ÿЈ#ÿÏ…$ÿц&ÿÒˆ'ÿÓˆ(ÿÔ‡)ÿÕˆ*ÿÔ†*ÿÕ†,ÿÕ†+ÿÕ†+ÿÕƒ+ÿÖ…-ÿÙ†/ÿÛ‰2ÿÝŒ5ÿá7ÿãš>ÿé´Zÿï¿kÿ“i@ÿ¶™~ÿïÕºÿÿçÏÿÿæÏÿÿæÎÿÿçÏÿÿæÏÿÿæÎÿÿæÎÿÿçÎÿÿ̱ÿ—igÿ/»•C º‰* ÏŸ:ÅõÂFÿöÂ?ÿò¸6ÿî¯.ÿê¨+ÿè¥*ÿç *ÿå(ÿæœ(ÿçœ)ÿéœ*ÿé›-ÿê-ÿêœ-ÿëœ/ÿë›/ÿê™/ÿë˜0ÿë˜0ÿì—2ÿì—2ÿï˜3ÿñœ6ÿó:ÿö¢=ÿù¦CÿýÅ]ÿÿÓqÿ’g@ÿ·›‚ÿð׿ÿÿèÐÿÿèÑÿÿçÑÿÿèÑÿÿèÐÿÿèÑÿÿèÑÿÿèÑÿÿ̲ÿ™khÿ/àNJЦ\TÔ5 â·GùøÈKÿöÄBÿò¹7ÿî¯0ÿë©*ÿè¦)ÿæ£)ÿåž(ÿäš(ÿä›)ÿæ›*ÿæš+ÿç™,ÿç™,ÿç˜-ÿç˜-ÿæ•-ÿæ–.ÿè•.ÿé•.ÿê•1ÿì—3ÿïš6ÿòž:ÿõ¢>ÿúºRÿÿËcÿ‰c?ÿ¨‘ÿõÝÆÿÿéÓÿÿéÓÿÿéÓÿÿéÓÿÿéÓÿÿéÓÿÿéÓÿÿéÓÿÿÌ´ÿ›mkÿ/Å¥L3â»iæÒ°\"Ì£?eèÂNÿøÐNÿöÆBÿò¹7ÿï°0ÿìª,ÿè¤)ÿÙ–%ÿÏŠ"ÿΆ#ÿ͆"ÿ·"ÿΆ"ÿÏ…$ÿ΄$ÿÐ…%ÿÙŠ'ÿã”,ÿè•-ÿè”-ÿè•-ÿê–0ÿë˜3ÿðœ6ÿô¡<ÿú¶Lÿý¾Yÿ‡dGÿи£ÿøâÍÿÿêÕÿÿêÕÿÿêÕÿÿêÖÿÿêÖÿÿêÖÿÿêÖÿÿêÕÿÿ˵ÿœolÿ/»œFìÌ{ÿæÂkµÏ­TÊ¥B¬ðÌRÿ÷ÓOÿöÇBÿòº7ÿð²1ÿàž(ÿšj)ÿ©{Dÿ¸‰TÿºŠWÿº‹Wÿº‹Wÿ»ŠXÿ½ŒYÿºŠVÿÀ€5ÿä”+ÿæ—,ÿæ”-ÿè”-ÿè—.ÿê—1ÿî›5ÿó¡;ÿþ¶IÿíªLÿˆlUÿÛÅ®ÿûæÓÿÿëØÿÿëØÿÿëØÿÿëØÿÿëØÿÿìØÿÿìØÿÿëØÿÿ˵ÿžqmÿ/·–?ÞÀsðýå‹üà¹d¥»“6Ø´KÕòÐUÿøÕOÿöÈBÿó½8ÿà *ÿeI2ÿ¯˜ÿíÖÀÿúåÒÿúåÒÿúåÒÿúæÓÿöâÍÿã͸ÿ¾‹Qÿç™+ÿæ™+ÿæ–+ÿæ–,ÿè–-ÿê˜0ÿîœ5ÿô¤;ÿÿµFÿÏ<ÿ™~iÿäлÿþëÙÿÿìÚÿÿíÛÿÿíÚÿÿíÛÿÿíÚÿÿíÛÿÿíÛÿÿíÚÿÿË·ÿ spÿ/˱d¢ýì›ÿÿêÿÜ·c‡Í§D4ãÃPçóÓUÿ÷ÔPÿ÷ËDÿò¶5ÿuN"ÿˆq\ÿØÃ­ÿþíÜÿÿîÝÿÿîÝÿþíÛÿðÝÊÿË´ÿ̈*ÿèš+ÿæ˜*ÿæ—*ÿæ—,ÿè˜-ÿêš1ÿïž5ÿö§<ÿÿ³Eÿ°x<ÿ´œ‡ÿðÝÊÿýìÚÿÿîÝÿÿîÝÿÿíÝÿÿîÝÿÿîÜÿÿîÝÿÿîÝÿÿîÝÿÿ˸ÿ¡uqÿ/¾ QKí×üÿÿ¯ÿÿêŽÿä½gžÞ¹ZÙ¹KIãÄPïô×Wÿ÷ÖPÿúÌBÿÆŒ'ÿ\@+ÿ‹s_ÿdzŸÿâϼÿæÔÀÿÔÁ¬ÿ¶¢Œÿ®}@ÿé)ÿæš)ÿæ˜*ÿæ˜*ÿæ™,ÿè™-ÿëœ0ÿð¡6ÿ÷«?ÿøªAÿœuQÿмªÿùéØÿÿðßÿÿïßÿÿïàÿÿïàÿÿïàÿÿïßÿÿïßÿÿïßÿÿïßÿÿ˹ÿ£wsÿ/Ö¼q¼þö¬ÿÿÿ±ÿþìÿçÅh©à¿ZÞ¿NCçÊeìòÕUÿ÷ØOÿôÄ?ÿ¾‡'ÿ{S!ÿmM1ÿw]Iÿ…kTÿŒkIÿ¡s1ÿØ”%ÿå(ÿåš(ÿæš)ÿæ™*ÿçš+ÿéœ-ÿí 2ÿô©:ÿù¯@ÿË‹;ÿ®•}ÿéØÇÿýîßÿÿñáÿÿñâÿÿðáÿÿñáÿÿðâÿÿðáÿÿðáÿÿðáÿÿñáÿÿ˺ÿ¤yuÿ/ɯ\S÷âœ÷ÿÿ¹ÿÿÿ²ÿýò•ÿëËpÅà¾^ èÏ]!ìÖwÿóÖZÿ÷ÖNÿöÊCÿãª1ÿÈŽ&ÿ·~$ÿ½$ÿÏ&ÿä¢(ÿé¤*ÿæ )ÿå)ÿåš(ÿæœ)ÿèž,ÿì¡/ÿñ§6ÿö°=ÿí§<ÿ¥TÿѾ®ÿôæÕÿÿñäÿÿòäÿÿòäÿÿòäÿÿñäÿÿñäÿÿñåÿÿñäÿÿñäÿÿòäÿÿʺÿ¥{vÿ/ØÃo£ÿñ«ÿÿÿ¾ÿÿÿµÿÿû¡ÿöØzýÞ¿NtöêËÿêÓƒÿóÙcÿö×Oÿ÷ÐGÿöÆ=ÿó»4ÿî²/ÿì®+ÿë¬*ÿê¨*ÿé¥)ÿç¢*ÿçŸ)ÿçž*ÿë¢.ÿï§3ÿ÷­:ÿñ¬<ÿÃŒCÿÁ­œÿðàÒÿÿóæÿÿóçÿÿóæÿÿóçÿÿóçÿÿóæÿÿòæÿÿòæÿÿòæÿÿòæÿÿóçÿÿÊ»ÿ§|xÿ/âσËþõ²ÿÿÿ¿ÿÿÿ¹ÿÿÿ§ÿüæ‡ÿë×’ÿëâÅÿïÙ‘ÿõàgÿøÞWÿ÷ÔIÿöÇ?ÿò¼5ÿðµ/ÿî¯+ÿë¬*ÿì©+ÿë¨*ÿë§-ÿì§.ÿñª3ÿô¯8ÿ÷³<ÿÍ”?ÿ½¥ÿðãÕÿÿôçÿá̳ÿíÞÏÿþóæÿÿôèÿÿóèÿÿôéÿÿôéÿÿôèÿÿóéÿÿóéÿÿôéÿÿ˼ÿ©~yÿ/ïÜ‹Âþõ³ÿÿÿ¿ÿÿÿ¼ÿÿÿ¯ÿÿö—ÿ÷äžÿðܤÿ÷ã˜ÿ÷éfÿúäZÿø×MÿöÈ?ÿô¾6ÿò¶/ÿî±,ÿî®,ÿî®.ÿð°1ÿô³5ÿ÷µ:ÿó²;ÿØœ=ÿ¼¥ŽÿíàÔÿýóéÿýòåÿöÇÿÒ¾©ÿðâÔÿÿôëÿÿôëÿÿõëÿÿõêÿÿõëÿÿõëÿÿõëÿÿõëÿÿ˽ÿ©zÿ/åÑz¤úì§ÿÿý¶ÿÿÿ»ÿÿÿµÿÿü§ÿÿó–ÿüí“ÿúñrÿüñhÿúæ]ÿùÚPÿ÷ÌAÿõÁ8ÿô¼3ÿò¹3ÿôº6ÿ÷¼;ÿø¼>ÿï°;ÿ•MÿŲ›ÿðåÙÿûòèÿÿöíÿÿöíÿüÙ¡ÿѱ†ÿßμÿÿöíÿÿöíÿÿöíÿÿöíÿÿöíÿÿöíÿÿöíÿÿöíÿÿʾÿª€{ÿ/âËm_îÜ’ëÿò«ÿÿý±ÿÿþ±ÿÿþ­ÿÿþ¢ÿÿú’ÿþ÷|ÿýókÿüêaÿûÝSÿùÒHÿùÊBÿøÉCÿùÅFÿô¾FÿÝ©CÿÇ©|ÿáÔÅÿöìãÿÿ÷ïÿÿ÷ïÿÿ÷ïÿÿ÷ïÿÿçÂÿׯsÿÙÇ´ÿÿ÷ïÿÿ÷ïÿÿ÷ïÿÿ÷ïÿÿ÷ðÿÿ÷ïÿÿ÷ïÿÿøðÿÿʾÿ¬|ÿ/иe|ìÒˆÿìÒˆÿìÒˆÿûíœÿüî™ÿýíÿýè|ÿüãgÿøÛZÿ÷ÐNÿüÕOÿ÷ÓVÿÔ°Rÿ²•aÿʼ§ÿêàÕÿùðèÿÿøñÿÿøñÿÿøñÿÿøñÿÿùòÿçÏ®ÿׯmÿÓ°ÿÿøòÿÿøñÿÿøñÿÿùñÿÿøñÿÿøñÿÿøñÿÿøñÿÿʾÿ¬‚}ÿ/岊ÿñìÙÿóæÃÿìÒˆÿìÒˆÿìÒˆÿìÒˆÿìÒˆÿìÒˆÿìÒˆÿëÅ^ÿòËSÿ¯”[ÿ˜†pÿ­ž‹ÿÉ»©ÿåÜÑÿÿùóÿÿùóÿÿúóÿÿùóÿÿùóÿؼ‘ÿͤbÿÒóÿÿùôÿÿùóÿÿ¼½ÿÿ¸·ÿÿ³³ÿÿ®®ÿÿ©©ÿÿ¤¤ÿÿ  ÿ¬‚}ÿw#ç´ŽÿöööÿÿúõÿÿúõÿÿúõÿÿúôÿÿùóÿÿúôÿÿúõÿÿúõÿúðÝÿ÷ÞšÿúÓXÿà¾\ÿÀ¦vÿ¦•€ÿ«›‹ÿ³¢ÿÖʼÿîçÞÿñêâÿÜÑÅÿá¯TÿžkÿÜÐÄÿÿúõÿ›jiÿ›jiÿ›jiÿ›jiÿ›jiÿ›jiÿ›jiÿ›jiÿ›jiÿ; ê·ÿøøøÿÿú÷ÿÿû÷ÿÿû÷ÿÿûöÿÿû÷ÿÿû÷ÿÿû÷ÿÿû÷ÿÿû÷ÿÿû÷ÿüöëÿîÙ¢ÿþÜ]ÿøÐ]ÿ×´gÿ²™wÿ­™ÿ®š‚ÿ±š{ÿÜ«Oÿò·DÿÎÀ²ÿîçàÿÿû÷ÿ¥rlÿÿ´Rÿÿ™ÿõÿë…ÿàzÿÖpÿ¿fÿ; íºÿùùùÿÿüùÿÿûøÿÿüøÿÿüøÿÿüøÿÿüøÿÿüøÿÿüøÿÿüùÿÿûøÿÿüùÿÿüùÿûõîÿöá°ÿúÛ…ÿÿÖpÿúÎjÿöÉaÿûÊ\ÿôÉoÿàйÿóîéÿýøôÿÿûøÿ¨unÿÿÌ™ÿÿ´Rÿÿ°Dÿÿ«6ÿÿ¨-ÿÙŒ9ÿ; ð½‘ÿúúúÿÿüúÿÿýúÿÿüúÿÿüúÿÿýúÿÿüúÿÿüúÿÿüúÿÿüúÿÿýúÿÿýúÿÿýúÿÿýúÿÿýúÿÿýúÿÿýúÿýõæÿüñ×ÿüóãÿÿüúÿÿüúÿÿüúÿÿýúÿÿüúÿ«xoÿÿÖ£ÿÿ·[ÿÿ´Rÿÿ°DÿÛ“Iÿ; óÀ’ÿûûûÿÿýüÿÿþüÿÿþüÿÿýüÿÿýûÿÿýûÿÿýûÿÿýûÿÿýûÿÿýûÿÿýûÿÿýûÿÿþûÿÿýüÿÿýûÿÿýüÿÿþûÿÿþûÿÿýûÿÿþüÿÿýüÿÿþûÿÿýûÿÿýûÿ®{pÿÿà­ÿÿ»gÿÿ·[ÿÝ›[ÿ; õ“ÿýýýÿÿþýÿÿþýÿÿýýÿÿþýÿÿþýÿÿþýÿÿýýÿÿþüÿÿþýÿÿþýÿÿþýÿÿþýÿÿýýÿÿþýÿÿþýÿÿþýÿÿþüÿÿþýÿÿþýÿÿþýÿÿþýÿÿýýÿÿþýÿÿýýÿ°}qÿÿë¸ÿÿÀtÿÞ¡jÿ; ÷Ä•ÿþþþÿÿÿþÿÿÿþÿÿþþÿÿþþÿÿþþÿÿÿþÿÿþþÿÿþþÿÿþþÿÿÿþÿÿÿþÿÿþþÿÿþþÿÿþþÿÿþþÿÿþþÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿþþÿÿÿþÿÿÿþÿ²sÿÿõÂÿà¨{ÿ; úÇ—ÿÿÿÿÿÿÿÿÿþþþÿýýýÿüüüÿüüüÿûûûÿúúúÿúúúÿùùùÿøøøÿ÷÷÷ÿ÷÷÷ÿöööÿõõõÿõõõÿôôôÿóóóÿóòòÿòñðÿñðïÿðîìÿðíëÿïìéÿîëèÿ¶ƒtÿйŽÿ; þË™ÿüÖ±ÿúÔ°ÿøÑ®ÿõÏ­ÿóÍ«ÿñ˪ÿîÈ©ÿìŨÿêæÿèÁ¥ÿ忤ÿã½¢ÿມÿÞ·ŸÿÛµžÿÚ³ÿ×±›ÿÕ®šÿÒ¬™ÿЪ˜ÿΧ–ÿË¥•ÿÉ£”ÿÆ ’ÿÄž‘ÿ¸…uÿ ÿÿÿÿÿÿÿàÿàÿàÿà€€à€€€ÀÀà ˆ„„ÂÀààðøüþÿÿÀÿàÿàÿàÿàÿàÿàÿàÿà?ÿàÿàÿ( @ #/////////////////////# #kk#«|ÿ©~zÿ¨|xÿ¥zvÿ¢wtÿ uqÿžqnÿ›njÿ˜khÿ–heÿ“dbÿa_ÿ^\ÿa_ÿa_ÿ^\ÿ‹[Zÿ‰XWÿa_ÿ^\ÿ‹[Zÿ‰XWÿ†UVÿ/•bbÿüá×ÿÿàÐÿÿßÏÿÿÞÍÿÿÝËÿÿÜÉÿÿÛÈÿÿÛÆÿþ×Ãÿþ×Ãÿþ×Ãÿþ×Ãÿþ×Ãÿþ×Ãÿþ×Ãÿþ×Ãÿþ×Ãÿþ×Ãÿþ×Ãÿþ×ÃÿÿΩÿˆWUÿ/¹›rÿŒT½~J¤oA‰«|ÿÁºµÿóϪÿöÑ­ÿöÑ­ÿöÒ®ÿøÔ°ÿüصÿÿܸÿÿÛ¸ÿÿÛ·ÿÿÛ¸ÿÿÛ¸ÿÿÛ¸ÿÿÛ¸ÿÿÛ¸ÿÿÛ·ÿÿܸÿÿÛ¸ÿÿÛ¸ÿÿÛ·ÿÿΩÿˆWUÿ/¾y"òÝ#îц'ÂÈ~,·Ã|/·c;ÿfÿ´mÿ¸“sÿ¹–sÿ¹—sÿ¾œyÿή‹ÿã ÿôÓ¯ÿþÛ¹ÿÿݹÿÿܺÿÿݹÿÿݹÿÿܺÿÿݹÿÿܺÿÿݹÿÿܺÿÿݹÿÿΩÿˆWUÿ/•_îþ¸5ÿÄ-D‡R -㯊ÿ_1ÿyN&ÿxP+ÿ~U-ÿ~X.ÿyV0ÿ~^=ÿpRÿ´–tÿ࿜ÿúضÿÿݼÿÿݼÿÿݼÿÿݼÿÿÞ½ÿÿÞ¼ÿÿݼÿÿÞ¼ÿÿݼÿÿΩÿˆWUÿ/¶x ôÏŒ&ëxIR/M[5µzI"ÿ°p.ÿ×;ÿÝ—>ÿå¤Dÿå­Qÿá±^ÿÌ¢\ÿœyFÿwZ8ÿˆlMÿˬ‹ÿôÔ³ÿþÞ½ÿÿß¾ÿÿÞ¾ÿÿÞ¿ÿÿß¾ÿÿß¿ÿÿß¿ÿÿß¾ÿÿΩÿ‰XWÿ/·z íÔ&õi?kR.ZuGÒ¼y-þëš9ÿóŸ=ÿ÷¡=ÿõ¡<ÿ÷©>ÿ÷´FÿûÂYÿþÒrÿùÛŽÿß‚ÿpBÿx^Aÿæ…ÿòÓµÿþàÀÿÿàÁÿÿáÁÿÿàÂÿÿàÁÿÿàÁÿÿàÁÿÿΩÿ‹ZZÿ/¤n#Òç¡*üi@ÏqDÜÜ“4ÿõ©<ÿõ¥:ÿï›5ÿí–4ÿì”3ÿî•3ÿðš3ÿò£5ÿöµCÿûÐhÿÿè”ÿúç¥ÿ£…TÿdGÿϳ”ÿøÜ¼ÿÿâÄÿÿâÄÿÿáÄÿÿâÃÿÿâÃÿÿáÄÿÿΩÿ]\ÿ/_ õ¯3þ³z&üʉ+ÿ÷¬9ÿñ¤5ÿëš0ÿè”/ÿè“.ÿé‘0ÿ²r/ÿ²r/ÿê’/ÿï2ÿô¬;ÿùÇXÿÿê‘ÿûé©ÿmAÿ›aÿçʬÿþâÆÿÿãÆÿÿãÇÿÿãÇÿÿãÆÿÿãÆÿÿΩÿ_^ÿ/€S[Ùœ4õþ¸5ÿõ­5ÿï¥3ÿêœ.ÿå—+ÿå”*ÿÛŒ-ÿ²r/ÿæÄ¡ÿä¿›ÿÚ„,ÿäŒ.ÿïš1ÿó§6ÿøÁSÿÿê—ÿãËŒÿoFÿÔ¸™ÿûáÅÿÿåÊÿÿäÉÿÿåÉÿÿäÉÿÿåÉÿÿΩÿ‘c`ÿ/b>¬z+Çü½<ÿô±5ÿê¢.ÿæš+ÿå•'ÿÛ(ÿ²r/ÿÿæÌÿÿæÍÿÿæÌÿðÕºÿµq2ÿµq2ÿ·t4ÿÀ€8ÿÅŽJÿÕ«rÿÕ«rÿÝÁ¤ÿúáÇÿÿæÌÿÿæÌÿÿæÌÿÿæÍÿÿæÌÿÿΪÿ”ebÿ/[7—h!zç®8û÷¸8ÿë¥,ÿä›'ÿä–&ÿʃ'ÿ‰Z,ÿüãÉÿüãÉÿüãÉÿüãÉÿüãÉÿüãÉÿüãÉÿüãÉÿüãÉÿüãÉÿüãÉÿüãÉÿüãÉÿüãÉÿÿçÏÿÿçÏÿÿèÐÿÿçÏÿÿÍ«ÿ•gfÿ/sM ¼Š+ÑùÀ=ÿï®0ÿç¡(ÿå›&ÿЉ#ÿqO4ÿqO4ÿqO4ÿqO4ÿqO4ÿqO4ÿqO4ÿqO4ÿqO4ÿqO4ÿqO4ÿqO4ÿ¶™~ÿÿçÏÿÿçÏÿÿéÒÿÿéÓÿÿéÓÿÿéÒÿÿÍ­ÿ˜khÿ/‹c"[Þ°AöøÁ=ÿí¬-ÿè¥)ÿä(ÿß•&ÿß”(ÿâ•*ÿã•+ÿä”,ÿã‘,ÿá-ÿâ.ÿç’3ÿãš>ÿï¿kÿ‰c?ÿµ˜ÿÿéÓÿÿêÖÿÿêÕÿÿêÕÿÿêÖÿÿëÕÿÿ̰ÿšmjÿ/¤}/žõÍOÿ÷Â>ÿî¯.ÿå¢)ÿº‹Wÿº‹Wÿº‹Wÿº‹Wÿº‹Wÿº‹Wÿé˜.ÿë˜/ÿí™3ÿù¦CÿÿÓqÿ‰c?ÿµ™ÿÿêÖÿÿíÙÿÿìØÿÿìÙÿÿìÙÿÿìØÿÿ̱ÿpmÿ/¦:àÄ{ÿëÒ›ÿ¼˜;ÒøÔRÿöÄ>ÿÔ™+ÿšj)ÿÿíÜÿÿíÜÿÿíÜÿÿíÜÿ¶{5ÿæ˜+ÿç–+ÿè–.ÿó¡;ÿý¾Yÿ¨s9ÿŬ–ÿÿíÙÿÿíÜÿÿîÜÿÿíÜÿÿíÜÿÿîÛÿÿ̳ÿŸroÿ/àÄ{ÿôÓ}ÿîÑ€ÿʪDáø×QÿÞ¬5ÿˆq\ÿÿïßÿÿïßÿÿïÞÿÓ½¦ÿ»~,ÿçš*ÿæ˜*ÿç—,ÿó¡;ÿíªLÿ¥wFÿׯÿüíÜÿÿïßÿÿïßÿÿïßÿÿïÞÿÿïßÿÿ̵ÿ¡uqÿ/¼˜UãÍÉÿñ›ÿîÑ€ÿÓ¹bÿóÐLÿË—5ÿ…kTÿ¶¢Œÿ¶¢Œÿ«x2ÿáš(ÿç›(ÿæ™*ÿéœ-ÿô©9ÿÒ7ÿ´”sÿîÝÌÿþïßÿÿñáÿÿðâÿÿñâÿÿñáÿÿðâÿÿ˸ÿ£wsÿ/æÙ£ÿøå“ûÿó ÿîÑ€ÿöêËÿÒ¹lÿðÍOÿÝ©2ÿŠ%ÿÎ$ÿä¡(ÿé¤)ÿæ(ÿæœ)ÿñ¥3ÿö¯<ÿ®€JÿØÆ³ÿòãÑÿûíÞÿÿòäÿÿòäÿÿòäÿÿòäÿÿòäÿÿ˺ÿ¤zuÿ/æÙ£ÿöä”ÿÿù¨ÿïÒ€ÿöêËÿâÎ{ÿôÛWÿóÈ?ÿï¶0ÿì®*ÿë©*ÿë¨+ÿñ¬1ÿó°8ÿº†?ÿϹ¤ÿèÓ¼ÿÛêÿñáÑÿýñåÿÿóçÿÿôçÿÿóçÿÿôçÿÿË»ÿ§|wÿ/ʰfæÙ£ÿôá“ÿÿý©ÿÿð”ÿýî•ÿþõxÿüê\ÿúÒFÿö¿6ÿò¸3ÿòµ6ÿç®8ÿ¼ŽFÿÔ¿§ÿøìáÿóÙ³ÿÓ³ŠÿáϺÿÿóçÿÿõêÿÿõêÿÿõéÿÿôêÿÿʽÿ¨~yÿ/ìÒˆÿìÒˆÿïå¡ÿ÷ç‘ÿøê‚ÿøçoÿó×XÿõÌHÿüÑLÿ¾›Jÿ¾ pÿéÝÏÿÿöìÿÿöíÿùäÄÿÔ²€ÿÙDZÿÿõêÿÿöìÿÿöìÿÿöìÿÿöìÿÿʾÿªzÿ/جÿìÒˆÿìÒˆÿìÒˆÿìÒˆÿìÒˆÿÒ·}ÿÖ¯Qÿ˜†pÿ»¨ˆÿÝпÿÿôêÿÿôêÿéѤÿÄ kÿÜ̸ÿÿöìÿÿ÷ïÿÿ÷ïÿÿ÷ïÿÿ÷ïÿÿʾÿ«|ÿ/㯊ÿöö÷ÿÿøñÿÿùñÿÿøñÿÿøïÿ÷ðèÿæ×½ÿе‚ÿÒ­\ÿÑ­aÿˬtÿË«lÿÓ¤KÿºšmÿêÞÑÿþ÷ïÿùÚÎÿ÷³³ÿ÷¬¬ÿö¡¢ÿÿ¢¡ÿ¬‚}ÿw#ç´ÿøøøÿÿúóÿÿùóÿÿùóÿÿùóÿÿùóÿÿùòÿïçÛÿÛʰÿÅ©|ÿ¼–Xÿ¸Nÿ¯cÿÒ®ÿÿùóÿþùòÿ›jiÿ›jiÿ›jiÿ›jiÿ›jiÿ›jiÿ; ë¸ÿùúùÿÿúöÿÿûõÿÿúõÿÿûõÿÿûõÿÿûõÿÿûõÿÿúöÿÿûõÿÿûõÿÿúõÿÿûõÿÿûõÿÿûõÿÿúõÿ®{pÿÿ½`ÿÿ©1ÿü¢"ÿÅ•Mÿ; î»ÿúúûÿÿûøÿÿû÷ÿÿû÷ÿÿüøÿÿû÷ÿÿû÷ÿÿü÷ÿÿü÷ÿÿü÷ÿÿû÷ÿÿü÷ÿÿü÷ÿÿü÷ÿÿü÷ÿÿû÷ÿ®{pÿÿÎ…ÿþµTÿË£cÿ; ò¾“ÿüüüÿÿüùÿÿýøÿÿüøÿÿýùÿÿüùÿÿüøÿÿüùÿÿüùÿÿüùÿÿüùÿÿýøÿÿüùÿÿüøÿÿýøÿÿýùÿ²sÿÿØ–ÿÈ£pÿ; õ•ÿýýýÿýýýÿüüüÿüüüÿûûûÿúùúÿúùøÿùøøÿø÷öÿ÷öõÿöõôÿöôóÿôóòÿôòðÿóñîÿòðîÿ²sÿйŽÿ; úÔ°ÿúÔ°ÿøÒ¯ÿöЮÿôάÿñ˪ÿîÈ©ÿêĦÿçÁ¤ÿã½¢ÿß¹ ÿÛµŸÿײœÿÔ®šÿЫ˜ÿͧ—ÿÊ¥•ÿ²sÿ# üüüüÀ€€Ààˆ„Àààøüüüüüüüü?(  kkkkkkkkkkkk‰XWÿ‰XWÿ‰XWÿ‰XWÿ‰XWÿ‰XWÿ‰XWÿ‰XWÿ‰XWÿ‰XWÿ‰XWÿ‰XWÿk”Y Û›CnÓ–DºÞ¨tÿýÖÈÿá¹¥ÿÞ·¢ÿìųÿüÕÆÿûÕÆÿÿØÉÿÿØÊÿÿØÊÿÿΩÿ†UTÿkûÎ|ÿ’[ŒÄŠHÿ\1ÿiC!ÿeC%ÿZ<'ÿˆkJÿØ´ŸÿöÐÁÿþ×ÉÿÿØÊÿÿΩÿƒQQÿk´ˆJÿi?kR.Z¬k)ÿê˜:ÿì˜2ÿ÷©>ÿëÄmÿëÄmÿ^D(ÿÖ·”ÿûÛºÿÿß¿ÿÿÍ«ÿƒQQÿk´ˆJÿÚ <ÿ±w>ÿìœ2ÿí•0ÿ²r/ÿæÄ¡ÿ͈1ÿ͈1ÿ͈1ÿ‰kGÿðÒ´ÿÿâÄÿÿÍ­ÿ‡UUÿkƒVeø·<ªòª0ÿå–(ÿš_ÿæÄ¡ÿÿåÌÿÿåÌÿÿåÌÿÿåÌÿÿåÌÿãÆ¨ÿÿåÌÿÿͰÿ‹[ZÿkÇ®~ÿùÁJÿåœ%ÿš_ÿš_ÿš_ÿš_ÿš_ÿš_ÿ‰c?ÿãλÿÿéÓÿÿÌ´ÿb`ÿk¼–XÿÇ®~ÿð´2ÿÒ‘"ÿæ¦Hÿå¦PÿØ1ÿì˜2ÿþªBÿ¥wFÿãλÿÿíÛÿÿÌ·ÿ—igÿk¼˜U¼–XÿÇ®~ÿžpÿæÄ¡ÿÿÿÿÿÄz)ÿþªBÿÙ‘4ÿ´”sÿøêÛÿÿòäÿÿʺÿžqnÿkÕ˜ÿ¼–XÿöêËÿÝ©2ÿŠ%ÿŠ%ÿæŸ'ÿë¢.ÿ®€Jÿÿÿÿÿÿöìÿÿõìÿÿʽÿ£wsÿk”s2Õ˜ÿ¼–Xÿüé”ÿÿójÿþË<ÿï¹:ÿÀ•TÿÿÿÿÿæÁlÿöìáÿÿùóÿÿʾÿ§}yÿkÜ©‡ÿ¼–Xÿ¼–Xÿ¼–Xÿ¼–Xÿ¼–Xÿÿÿÿÿîº>ÿœlbÿ›jiÿ›jiÿ«|ÿkÜ©‡ÿÿÿÿÿÿÿÿÿ÷ðèÿæ×½ÿе‚ÿе‚ÿ騶ÿ²€pÿÿʾÿä•Aÿ; Ü©‡ÿÿþýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²sÿÈ£pÿ; Ü©‡ÿÜ©‡ÿÜ©‡ÿÜ©‡ÿÜ©‡ÿÜ©‡ÿÞ«ˆÿÖ£„ÿ²sÿ# ð íà&õk ZÒ-þ9ÿ€=ÿ@=ÿ <ÿ€>ÿ€FÿàYÿàrÿàŽÿà‚ÿmysql-connector-odbc-5.1.10-src/wix/mysql-connector-odbc-msi-arpprops.xml100644 15766 12 2726 11707541006 25223 0ustar00cteamstaff mysql-connector-odbc-5.1.10-src/wix/mysql_odbc.xml.in100644 15766 12 25303 11707541006 21304 0ustar00cteamstaff Change No AllUsers 1 1 installs installed Installing install MySQL Connector/ODBC $(var.odbc_ver_long) MySQL Connector-ODBC o Yes omus r Typical 1 NEWERPRODUCTFOUND NEWERPRODUCTFOUND Not Installed And (Not PATCH Or IS_MAJOR_UPGRADE) Installed And (RESUME Or Preselected) And Not PATCH Installed And Not RESUME And Not Preselected And Not PATCH mysql-connector-odbc-5.1.10-src/wix/mysql_common_ui.xml100644 15766 12 275404 11707541006 22006 0ustar00cteamstaff SetupErrorDialog Tahoma8 <error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here> 1 1 1 1 1 1 1 1 {&TahomaBold10}Wizard Completed 1 NOT UpdateStarted 1 1 NOT UpdateStarted UpdateStarted 1 1 NOT UpdateStarted UpdateStarted UpdateStarted Your system has not been modified. To install this program at a later time, please run the installation again. UpdateStarted NOT UpdateStarted Click Finish to exit the wizard. UpdateStarted NOT UpdateStarted You can either keep any existing installed elements on your system to continue this installation at a later time or you can restore your system to its original state prior to the installation. NOT UpdateStarted UpdateStarted Click Restore or Continue Later to exit the wizard. NOT UpdateStarted UpdateStarted The wizard was interrupted before [ProductName] could be completely installed. {&TahomaBold10}Wizard Completed 1 NOT UpdateStarted 1 1 NOT UpdateStarted UpdateStarted 1 1 NOT UpdateStarted UpdateStarted UpdateStarted Your system has not been modified. To complete installation at another time, please run setup again. UpdateStarted NOT UpdateStarted Click Finish to exit the wizard. UpdateStarted NOT UpdateStarted You can either keep any existing installed elements on your system to continue this installation at a later time or you can restore your system to its original state prior to the installation. NOT UpdateStarted UpdateStarted Click Restore or Continue Later to exit the wizard. NOT UpdateStarted UpdateStarted The wizard was interrupted before [ProductName] could be completely installed. {&TahomaBold10}[ProductName] Setup Wizard ended prematurely 1 1 Are you sure you want to cancel [ProductName] installation? 1 1 The Setup Wizard will install [ProductName] release [ProductVersion] on your computer. To continue, click Next. {&TahomaBold10}Welcome to the Setup Wizard for [ProductName] 1 1 The Setup Wizard will allow you to modify, repair, or remove [ProductName]. To continue, click Next. {&TahomaBold10}Welcome to the Setup Wizard for [ProductName] 1 1 This Wizard will create a server image of [ProductName] at a specified network location. To continue, click Next. {&TahomaBold10}Welcome to the Setup Wizard for [ProductName] 1 [ProductName] Setup is preparing the Setup Wizard which will guide you through the program setup process. Please wait... {&TahomaBold10}Welcome to the Setup Wizard for [ProductName] 1 OutOfNoRbDiskSpace = 1 OutOfNoRbDiskSpace <> 1 1 The Setup Wizard will complete the installation of [ProductName] on your computer. To continue, click Next. RESUME NOT RESUME The Setup Wizard will complete the suspended installation of [ProductName] on your computer. To continue, click Next. NOT RESUME RESUME {&TahomaBold10}Resuming the Setup Wizard for [ProductName] NOT Installed SetupType = "Custom" SetupType <> "Custom" SetupType = "Typical" SetupType = "Complete" SetupType = "Custom" SetupType="Typical" SetupType="Complete" SetupType="Custom" 1 1 Choose the setup type that best suits your needs. {&MSSansBold8}Setup Type Please select a setup type. 1 Installed 1 NOT Installed Installed OutOfNoRbDiskSpace = 1 1 0 AND &ODBC <> !ODBC]]> 1 Select the program features you want installed. {&MSSansBold8}Custom Setup Click on an icon in the list below to change how a feature is installed. Installed Multiline description of the currently selected item <selected feature path> OutOfNoRbDiskSpace = 1 OutOfNoRbDiskSpace <> 1 1 1 1 1 SetupType <> "Custom" SetupType = "Custom" The wizard is ready to begin installation. 1 OutOfNoRbDiskSpace = 1 OutOfNoRbDiskSpace <> 1 1 1 1 Specify a network location for the server image of the product. {&MSSansBold8}Network Location Enter the network location or click Change to browse to a location. Click Install to create a server image of [ProductName] at the specified network location or click Cancel to exit the wizard. 1 1 1 1 1 1 Browse to the destination folder. {&MSSansBold8}Change Current Destination Folder ACTION <> "ADMIN" ACTION = "ADMIN" Disk space required for the installation exceeds available disk space. {&MSSansBold8}Out of Disk Space The highlighted volumes do not have enough disk space available for the currently selected features. You can remove files from the highlighted volumes, choose to install less features onto local drives, or select different destination drives. 1 Custom Setup allows you to selectively install program features. {&MSSansBold8}Custom Setup Tips 1 _IsMaintenance = "Reinstall" _IsMaintenance = "Change" _IsMaintenance = "Change" _IsMaintenance = "Reinstall" _IsMaintenance = "Reinstall" _IsMaintenance = "Change" _IsMaintenance = "Change" _IsMaintenance = "Reinstall" _IsMaintenance = "Reinstall" _IsMaintenance = "Reinstall" _IsMaintenance = "Remove" _IsMaintenance = "Change" _IsMaintenance = "Reinstall" 1 Modify, repair, or remove the program. {&MSSansBold8}Program Maintenance 1 1 1 1 1 1 Browse to the destination folder. {&MSSansBold8}Change Current Destination Folder 1 The disk space required for the installation of the selected features. {&MSSansBold8}Disk Space Requirements The highlighted volumes do not have enough disk space available for the currently selected features. You can remove files from the highlighted volumes, choose to install less features onto local drives, or select different destination drives. 1 1 1 Some files that need to be updated are currently in use. {&MSSansBold8}Files in Use The following applications are using files that need to be updated by this setup. Close these applications and click Retry to continue. 1 1 1 1 OutOfNoRbDiskSpace = 1 OutOfNoRbDiskSpace <> 1 1 1 You have chosen to remove the program from your system. {&MSSansBold8}Remove the Program Click Remove to remove [ProductName] from your computer. After removal, this program will no longer be available for use. Fldr|New Folder bytes GB KB MB This feature will not be available. This feature will be installed when required. This feature, and all subfeatures, will be installed to run from the CD. This feature, and all subfeatures, will be installed on local hard drive. This feature, and all subfeatures, will be installed to run from the network. This feature will be installed to run from CD. This feature will be installed on local hard drive. This feature will be installed to run from network. This feature will remain uninstalled. This feature will be set to be installed when required. This feature will be installed to run from CD. This feature will be installed on the local hard drive. This feature will be installed to run from the network. This feature will become unavailable. Will be installed when required. This feature will be available to run from CD. This feature will be installed on your local hard drive. This feature will be available to run from the network. This feature will be uninstalled completely, and you won't be able to run it from CD. This feature was run from the CD but will be set to be installed when required. This feature will continue to be run from the CD This feature was run from the CD but will be installed on the local hard drive. This feature frees up [1] on your hard drive. This feature requires [1] on your hard drive. Compiling cost for this feature... This feature will be completely removed. This feature will be removed from your local hard drive but will be set to be installed when required. This feature will be removed from your local hard drive but will still be available to run from CD. This feature will remain on your local hard drive. This feature will be removed from your local hard drive, but will be still available to run from the network. This feature will be uninstalled completely, and you won't be able to run it from the network. This feature was run from the network but will be installed when required. This feature was run from the network but will be installed on the local hard drive. This feature will continue to be run from the network This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive. This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive. This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive. This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive. Time remaining: {[1] min }[2] sec Available Differences Required Disk Size Volume Computing space requirements Searching for installed applications Binding executables Searching for qualifying products Computing space requirements Computing space requirements Copying files to the network Copying new files Validating install Creating shortcuts Publishing qualified components Publishing product features Publishing product information Registering class servers Registering extension servers Registering MIME info Registering program identifiers Registering type libraries Allocating registry space Creating folders Deleting services Creating duplicate files Searching for related applications Installing ODBC components Installing new services Evaluating launch conditions Migrating feature states from related applications Moving files Patching files Updating component registration Registering COM+ Applications and Components Registering fonts Registering product Registering user Removing duplicated files Updating environment strings Removing applications Removing files Removing folders Removing INI file entries Removing ODBC components Removing system registry values Removing shortcuts Searching for qualifying products Registering modules Unregistering modules Initializing ODBC directories Starting services Stopping services Unpublishing Qualified Components Unpublishing product features Unregister class servers Unregistering COM+ Applications and Components Unregistering extension servers Unregistering fonts Unregistering MIME info Unregistering program identifiers Unregistering type libraries Updating environment strings Writing INI file values Writing system registry values Advertising application Generating script operations for action: Installing system catalog Rolling back action: Removing backup files Removing moved files Unpublishing product information {{Fatal error: }} Error [1]. Warning [1]. Info [1]. Internal Error [1]. [2]{, [3]}{, [4]} {{Disk full: }} Action [Time]: [1]. [2] [ProductName] {[2]}{, [3]}{, [4]} Message type: [1], Argument: [2] === Logging started: [Date] [Time] === === Logging stopped: [Date] [Time] === Action start [Time]: [1]. Action ended [Time]: [1]. Return value [2]. Time remaining: {[1] minutes }{[2] seconds} Out of memory. Shut down other applications before retrying. Installer is no longer responding. Installer terminated prematurely. Please wait while Windows configures [ProductName] Gathering required information... Removing older versions of this application Preparing to remove older versions of this application {[ProductName] }Setup completed successfully. {[ProductName] }Setup failed. Error reading from file: [2]. {{ System error [3].}} Verify that the file exists and that you can access it. Cannot create the file [3]. A directory with this name already exists. Cancel the installation and try installing to a different location. Please insert the disk: [2] The installer has insufficient privileges to access this directory: [2]. The installation cannot continue. Log on as an administrator or contact your system administrator. Error writing to file [2]. Verify that you have access to that directory. Error reading from file [2]. Verify that the file exists and that you can access it. Another application has exclusive access to the file [2]. Please shut down all other applications, then click Retry. There is not enough disk space to install the file [2]. Free some disk space and click Retry, or click Cancel to exit. Source file not found: [2]. Verify that the file exists and that you can access it. Error reading from file: [3]. {{ System error [2].}} Verify that the file exists and that you can access it. Error writing to file: [3]. {{ System error [2].}} Verify that you have access to that directory. Source file not found{{(cabinet)}}: [2]. Verify that the file exists and that you can access it. Cannot create the directory [2]. A file with this name already exists. Please rename or remove the file and click Retry, or click Cancel to exit. The volume [2] is currently unavailable. Please select another. The specified path [2] is unavailable. Unable to write to the specified folder [2]. A network error occurred while attempting to read from the file [2] An error occurred while attempting to create the directory [2] A network error occurred while attempting to create the directory [2] A network error occurred while attempting to open the source file cabinet [2]. The specified path is too long [2]. The Installer has insufficient privileges to modify the file [2]. A portion of the path [2] exceeds the length allowed by the system. The path [2] contains words that are not valid in folders. The path [2] contains an invalid character. [2] is not a valid short file name. Error getting file security: [3] GetLastError: [2] Invalid Drive: [2] Error applying patch to file [2]. It has probably been updated by other means, and can no longer be modified by this patch. For more information contact your patch vendor. {{System Error: [3]}} Could not create key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. Could not open key: [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. Could not delete value [2] from key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. Could not delete key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. Could not read value [2] from key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. Could not write value [2] to key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. Could not get value names for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. Could not get sub key names for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. Could not read security information for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. Could not increase the available registry space. [2] KB of free registry space is required for the installation of this application. Another installation is in progress. You must complete that installation before continuing this one. Error accessing secured data. Please make sure the Windows Installer is configured properly and try the installation again. User [2] has previously initiated an installation for product [3]. That user will need to run that installation again before using that product. Your current installation will now continue. User [2] has previously initiated an installation for product [3]. That user will need to run that installation again before using that product. Out of disk space -- Volume: '[2]'; required space: [3] KB; available space: [4] KB. Free some disk space and retry. Are you sure you want to cancel? The file [2][3] is being held in use{ by the following process: Name: [4], ID: [5], Window Title: [6]}. Close that application and retry. The product [2] is already installed, preventing the installation of this product. The two products are incompatible. Out of disk space -- Volume: [2]; required space: [3] KB; available space: [4] KB. If rollback is disabled, enough space is available. Click Cancel to quit, Retry to check available disk space again, or Ignore to continue without rollback. Could not access network location [2]. [2] Could not find any previously installed compliant products on the machine for installing this product. The key [2] is not valid. Verify that you entered the correct key. The installer must restart your system before configuration of [2] can continue. Click Yes to restart now or No if you plan to restart later. You must restart your system for the configuration changes made to [2] to take effect. Click Yes to restart now or No if you plan to restart later. An installation for [2] is currently suspended. You must undo the changes made by that installation to continue. Do you want to undo those changes? A previous installation for this product is in progress. You must undo the changes made by that installation to continue. Do you want to undo those changes? No valid source could be found for product [2]. The Windows Installer cannot continue. Installation operation completed successfully. Installation operation failed. Product: [2] -- [3] You may either restore your computer to its previous state or continue the installation later. Would you like to restore? An error occurred while writing installation information to disk. Check to make sure enough disk space is available, and click Retry, or Cancel to end the installation. One or more of the files required to restore your computer to its previous state could not be found. Restoration will not be possible. [2] cannot install one of its required products. Contact your technical support group. {{System Error: [3].}} The older version of [2] cannot be removed. Contact your technical support group. {{System Error [3].}} The path [2] is not valid. Please specify a valid path. Out of memory. Shut down other applications before retrying. There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to go back to the previously selected volume. There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to return to the browse dialog and select a different volume. The folder [2] does not exist. Please enter a path to an existing folder. You have insufficient privileges to read this folder. A valid destination folder for the installation could not be determined. Error attempting to read from the source installation database: [2]. Scheduling reboot operation: Renaming file [2] to [3]. Must reboot to complete operation. Scheduling reboot operation: Deleting file [2]. Must reboot to complete operation. Module [2] failed to register. HRESULT [3]. Contact your support personnel. Module [2] failed to unregister. HRESULT [3]. Contact your support personnel. Failed to cache package [2]. Error: [3]. Contact your support personnel. Could not register font [2]. Verify that you have sufficient permissions to install fonts, and that the system supports this font. Could not unregister font [2]. Verify that you have sufficient permissions to remove fonts. Could not create shortcut [2]. Verify that the destination folder exists and that you can access it. Could not remove shortcut [2]. Verify that the shortcut file exists and that you can access it. Could not register type library for file [2]. Contact your support personnel. Could not unregister type library for file [2]. Contact your support personnel. Could not update the INI file [2][3]. Verify that the file exists and that you can access it. Could not schedule file [2] to replace file [3] on reboot. Verify that you have write permissions to file [3]. Error removing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel. Error installing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel. Error removing ODBC driver [4], ODBC error [2]: [3]. Verify that you have sufficient privileges to remove ODBC drivers. Error installing ODBC driver [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it. Error configuring ODBC data source [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it. Service [2] ([3]) failed to start. Verify that you have sufficient privileges to start system services. Service [2] ([3]) could not be stopped. Verify that you have sufficient privileges to stop system services. Service [2] ([3]) could not be deleted. Verify that you have sufficient privileges to remove system services. Service [2] ([3]) could not be installed. Verify that you have sufficient privileges to install system services. Could not update environment variable [2]. Verify that you have sufficient privileges to modify environment variables. You do not have sufficient privileges to complete this installation for all users of the machine. Log on as an administrator and then retry this installation. Could not set file security for file [3]. Error: [2]. Verify that you have sufficient privileges to modify the security permissions for this file. Component Services (COM+ 1.0) are not installed on this computer. This installation requires Component Services in order to complete successfully. Component Services are available on Windows 2000. Error registering COM+ application. Contact your support personnel for more information. Error unregistering COM+ application. Contact your support personnel for more information. The description for service '[2]' ([3]) could not be changed. The Windows Installer service cannot update the system file [2] because the file is protected by Windows. You may need to update your operating system for this program to work correctly. {{Package version: [3], OS Protected version: [4]}} The Windows Installer service cannot update the protected Windows file [2]. {{Package version: [3], OS Protected version: [4], SFP Error: [5]}} This setup requires Internet Information Server 4.0 or higher for configuring IIS Virtual Roots. Please make sure that you have IIS 4.0 or higher. This setup requires Administrator privileges for configuring IIS Virtual Roots. mysql-connector-odbc-5.1.10-src/wix/CMakeLists.txt100644 15766 12 12715 11707541006 20564 0ustar00cteamstaff# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## cmake_minimum_required(VERSION 2.4.6) PROJECT(MYODBC_MSI_INSTALLER) #-------------- wix installer ------------------------ IF(WIN32) INCLUDE(${CMAKE_SOURCE_DIR}/cmake/FindWix.cmake) # ----------- generate version information --------- IF(WIX_DIR) FILE(REMOVE "myodbc_version.xml") TRY_RUN(VERSION_OUT VERSION_TEST "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/cmake/getodbcversion.c" ARGS "myodbc_version.xml") IF(NOT VERSION_OUT STREQUAL "0") MESSAGE(ERROR "Can't create myodbc_version.xml") ENDIF(NOT VERSION_OUT STREQUAL "0") ENDIF(WIX_DIR) # -------------------------------------------------- # -------------- find binary resources ------------- FIND_PATH(WIX_RESOURCE MySQLConnector.ico ./resources $ENV{WIX_RESOURCE} ${CMAKE_SOURCE_DIR}/../../../wix-installer/resources) IF(NOT WIX_RESOURCE) MESSAGE(ERROR "Can't find binary MySQL resource files. Please specify WIX_RESOURCE") ENDIF(NOT WIX_RESOURCE) MESSAGE(STATUS "Wix resources found in ${WIX_RESOURCE}") # -------------------------------------------------- # -------------- add wix variables --- ------------- WRITE_FILE("myodbc_version.xml" "" APPEND) IF(${MSI_64}) WRITE_FILE("myodbc_version.xml" "" APPEND) SET(MSI_VERSION "winx64") ELSE(${MSI_64}) WRITE_FILE("myodbc_version.xml" "" APPEND) SET(MSI_VERSION "win32") ENDIF(${MSI_64}) IF(${PDB}) WRITE_FILE("myodbc_version.xml" "" APPEND) ELSE(${PDB}) WRITE_FILE("myodbc_version.xml" "" APPEND) ENDIF(${PDB}) IF(${LICENSE}) WRITE_FILE("myodbc_version.xml" "" APPEND) ELSE(${LICENSE}) WRITE_FILE("myodbc_version.xml" "" APPEND) ENDIF(${LICENSE}) WRITE_FILE("myodbc_version.xml" "" APPEND) # -------------------------------------------------- INCLUDE(${CMAKE_SOURCE_DIR}/myodbc_version.cmake) IF(${LICENSE}) SET(MSI_PACKAGE "mysql-connector-odbc-commercial-${ODBC_VERSION}-${MSI_VERSION}.msi") ELSE(${LICENSE}) SET(MSI_PACKAGE "mysql-connector-odbc-${ODBC_VERSION}-${MSI_VERSION}.msi") ENDIF(${LICENSE}) # Generate GUID EXECUTE_PROCESS(COMMAND uuidgen OUTPUT_VARIABLE CONNECTOR_PKG_ID1) STRING(STRIP ${CONNECTOR_PKG_ID1} CONNECTOR_PKG_ID1) EXECUTE_PROCESS(COMMAND uuidgen OUTPUT_VARIABLE CONNECTOR_PKG_ID2) STRING(STRIP ${CONNECTOR_PKG_ID2} CONNECTOR_PKG_ID2) ENDIF(WIN32) #----------------------------------------------------- IF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/doc/LICENSE.mysql.txt") SET(LICENSE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/doc/LICENSE.mysql.txt") SET(LICENSE_RTF "${CMAKE_CURRENT_BINARY_DIR}/resources/commercial_license.rtf") ELSE() SET(LICENSE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/doc/COPYING.txt") SET(LICENSE_RTF "${CMAKE_CURRENT_BINARY_DIR}/resources/License.rtf") ENDIF() FILE(READ ${LICENSE_FILE} CONTENTS) STRING(REGEX REPLACE "\n" "\\\\par\n" CONTENTS "${CONTENTS}") STRING(REGEX REPLACE "\t" "\\\\tab" CONTENTS "${CONTENTS}") FILE(WRITE "${LICENSE_RTF}" "{\\rtf1\\ansi\\deff0{\\fonttbl{\\f0\\fnil\\fcharset0 Courier New;}}\\viewkind4\\uc1\\pard\\lang1031\\f0\\fs15") FILE(APPEND "${LICENSE_RTF}" "${CONTENTS}") FILE(APPEND "${LICENSE_RTF}" "\n}\n") #----------------------------------------------------- CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/mysql_odbc.xml.in ${CMAKE_CURRENT_SOURCE_DIR}/mysql-odbc-${ODBC_VERSION}-${MSI_VERSION}.xml @ONLY) ADD_CUSTOM_TARGET( MSI_INSTALLER ALL DEPENDS ${MSI_PACKAGE}) ADD_CUSTOM_COMMAND( OUTPUT ${MSI_PACKAGE} DEPENDS mysql_odbc.wixobj mysql_odbc_fragment.wixobj COMMAND ${WIX_DIR}/light.exe mysql_odbc.wixobj mysql_odbc_fragment.wixobj -o ${MSI_PACKAGE}) ADD_CUSTOM_COMMAND( OUTPUT mysql_odbc_fragment.wixobj DEPENDS mysql_odbc_fragment.xml COMMAND ${WIX_DIR}/candle.exe mysql_odbc_fragment.xml -o mysql_odbc_fragment.wixobj) ADD_CUSTOM_COMMAND( OUTPUT mysql_odbc.wixobj DEPENDS mysql_odbc.xml.in mysql_common_ui.xml COMMAND ${WIX_DIR}/candle.exe mysql-odbc-${ODBC_VERSION}-${MSI_VERSION}.xml -o mysql_odbc.wixobj) mysql-connector-odbc-5.1.10-src/wix/mysql_odbc_fragment.xml100644 15766 12 15303 11707541006 22561 0ustar00cteamstaff mysql-connector-odbc-5.1.10-src/wix/README.TXT100644 15766 12 2616 11707541006 17341 0ustar00cteamstaffMSI build instructions 1) Prerequisites - Wix 3.0 or above (http://wix.codeplex.com/) - Resources from connectors-svnroot/wix-installer - Microsoft Visual Studio environment - CMake 2.4.6 (http://www.cmake.org) 2) Environment variables - WIX_DIR If Wix executables are not in your default path, you need to specify WIX_DIR environment variable which points to your Wix installation directory. - WIX_RESOURCE Resource directory from svn+ssh://@bk-internal.mysql.com/connectors-svnroot/wix-installer 3) Copying files Create the directors "doc" (for license files), and "x86" (for 32-bit binaries). To create a 64-bit package you create a "x64" bit directory as well, i.e. a 64-bit package contains both 32-bit and 64-bit binaries. Copy the required files: doc/README.txt doc/COPYING.txt doc/Licenses_for_Third-Party_Components.txt x86/myodbc5.lib x86/myodbc5.dll x86/myodbc5S.lib x86/myodbc5S.dll x86/myodbc-installer.exe and in addition if a 64-bit package x64/myodbc5.lib x64/myodbc5.dll x64/myodbc5S.lib x64/myodbc5S.dll x64/myodbc-installer.exe 4) CMake There are two CMake options you can use to control the build -DMSI_64=1 build a 64-bit package -DPDB=1 include .pdb files In the simple 32-bit case you just run cmake -G "NMake Makefiles" 5) Build MSI Start nmake for building MyODBC msi file mysql-connector-odbc-5.1.10-src/Licenses_for_Third-Party_Components.txt100644 15766 12 311371 11707541006 25045 0ustar00cteamstaffAppendix A. Licenses for Third-Party Components The following is a list of the libraries we have included with the MySQL Server source and components used to test MySQL. We are thankful to all individuals that have created these. Some of the components require that their licensing terms be included in the documentation of products that include them. Cross references to these licensing terms are given with the applicable items in the list. * GroupLens Research Project The MySQL Quality Assurance team would like to acknowledge the use of the MovieLens Data Sets (10 million ratings and 100,000 tags for 10681 movies by 71567 users) to help test MySQL products and to thank the GroupLens Research Project at the University of Minnesota for making the data sets available. MySQL 5.5 * Section A.3, "dtoa.c License" * Section A.4, "Editline Library (libedit) License" * Section A.5, "FindGTest.cmake License" * Section A.6, "Fred Fish's Dbug Library License" * Section A.7, "getarg License" * Section A.9, "GNU General Public License Version 2.0, June 1991" * Section A.11, "GNU Libtool License" * Section A.12, "GNU Readline License" * Section A.13, "Google Controlling Master Thread I/O Rate Patch License" * Section A.14, "Google Perftools (TCMalloc utility) License" * Section A.15, "Google SMP Patch License" * Section A.16, "lib_sql.cc License" * Section A.17, "libevent License" * Section A.18, "Linux-PAM License" * Section A.22, "md5 (Message-Digest Algorithm 5) License" * Section A.23, "nt_servc (Windows NT Service class library) License" * Section A.24, "OpenPAM License" * Section A.26, "Percona Multiple I/O Threads Patch License" * Section A.27, "RegEX-Spencer Library License" * Section A.28, "RFC 3174 - US Secure Hash Algorithm 1 (SHA1) License" * Section A.29, "Richard A. O'Keefe String Library License" * Section A.30, "SHA-1 in C License" * Section A.32, "zlib License" MySQL Connector/C * Section A.6, "Fred Fish's Dbug Library License" * Section A.27, "RegEX-Spencer Library License" * Section A.28, "RFC 3174 - US Secure Hash Algorithm 1 (SHA1) License" * Section A.32, "zlib License" MySQL Connector/CPP * Section A.2, "Boost Library License" MySQL Connector/J * Section A.1, "Ant-Contrib License" * Section A.31, "Simple Logging Facade for Java (SLF4J) License" MySQL Connector/Net * Section A.28, "RFC 3174 - US Secure Hash Algorithm 1 (SHA1) License" * Section A.32, "zlib License" * Section A.33, "ZLIB.NET License" MySQL Proxy * Section A.8, "GLib License (for MySQL Proxy)" * Section A.10, "GNU Lesser General Public License Version 2.1, February 1999" * Section A.17, "libevent License" * Section A.19, "LPeg Library License" * Section A.20, "Lua (liblua) License" * Section A.21, "LuaFileSystem Library License" * Section A.25, "PCRE License" A.1. Ant-Contrib License The following software may be included in this product: Ant-Contrib Ant-Contrib Copyright (c) 2001-2003 Ant-Contrib project. All rights reserved. Licensed under the Apache 1.1 License Agreement, a copy of which is r eproduced below. The Apache Software License, Version 1.1 Copyright (c) 2001-2003 Ant-Contrib project. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowlegement: "This product includes software developed by the Ant-Contrib project (http://sourceforge.net/projects/ant-cont rib)." Alternately, this acknowlegement may appear in the software itsel f, if and wherever such third-party acknowlegements normally appear. 4. The name Ant-Contrib must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact ant-contrib-developers@lists.sourceforge.net. 5. Products derived from this software may not be called "Ant-Contri b" nor may "Ant-Contrib" appear in their names without prior written permission of the Ant-Contrib project. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ANT-CONTRIB PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. A.2. Boost Library License The following software may be included in this product: Boost C++ Libraries Use of any of this software is governed by the terms of the license below: Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. A.3. dtoa.c License The following software may be included in this product: dtoa.c The author of this software is David M. Gay. Copyright (c) 1991, 2000, 2001 by Lucent Technologies. Permission to use, copy, modify, and distribute this software for any purpose without fee is hereby granted, provided that this entire notice is included in all copies of any software which is or includes a copy or modification of this software and in all copies of the supporting documentation for such software. THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. A.4. Editline Library (libedit) License The following software may be included in this product: Editline Library (libedit) Some files are: Copyright (c) 1992, 1993 The Regents of the University of California. All rights reserved. This code is derived from software contributed to Berkeley by Christos Zoulas of Cornell University. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Some files are: Copyright (c) 2001 The NetBSD Foundation, Inc. All rights reserved. This code is derived from software contributed to The NetBSD Foundati on by Anthony Mallet. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Some files are: Copyright (c) 1997 The NetBSD Foundation, Inc. All rights reserved. This code is derived from software contributed to The NetBSD Foundati on by Jaromir Dolecek. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Some files are: Copyright (c) 1998 Todd C. Miller Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. A.5. FindGTest.cmake License The following software may be included in this product: FindGTest.cmake helper script (part of CMake) Copyright 2009 Kitware, Inc. Copyright 2009 Philip Lowman Copyright 2009 Daniel Blezek Distributed under the OSI-approved BSD License (the "License"); see accompanying file Copyright.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPO SE. See the License for more information. ===================================================================== ===== (To distributed this file outside of CMake, substitute the full License text for the above reference.) Thanks to Daniel Blezek for the GTEST_ADD_TESTS code Text of Copyright.txt mentioned above: CMake - Cross Platform Makefile Generator Copyright 2000-2009 Kitware, Inc., Insight Software Consortium All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution . * Neither the names of Kitware, Inc., the Insight Software Consortium , nor the names of their contributors may be used to endorse or promo te products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL , SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. A.6. Fred Fish's Dbug Library License The following software may be included in this product: Fred Fish's Dbug Library N O T I C E Copyright Abandoned, 1987, Fred Fish This previously copyrighted work has been placed into the publi c domain by the author and may be freely used for any purpose , private or commercial. Because of the number of inquiries I was receiving about the us e of this product in commercially developed works I have decided t o simply make it public domain to further its unrestricted use. I specifically would be most happy to see this material become a part of the standard Unix distributions by AT&T and the Berkele y Computer Science Research Group, and a standard part of the GN U system from the Free Software Foundation. I would appreciate it, as a courtesy, if this notice is left i n all copies and derivative works. Thank you. The author makes no warranty of any kind with respect to thi s product and explicitly disclaims any implied warranties of mer - chantability or fitness for any particular purpose. The dbug_analyze.c file is subject to the following notice: Copyright June 1987, Binayak Banerjee All rights reserved. This program may be freely distributed under the same terms and conditions as Fred Fish's Dbug package. A.7. getarg License The following software may be included in this product: getarg Function (getarg.h, getarg.c files) Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan (Royal Institute of Technology, Stockholm, Sweden). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the Institute nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. A.8. GLib License (for MySQL Proxy) The following software may be included in this product: GLib You are receiving a copy of the GLib library in both source and object code in the following [proxy install dir]/lib/ and [proxy install dir]/licenses/lgpl folders. The terms of the Oracle license do NOT apply to the GLib library; it is licensed under the following license, separately from the Oracle programs you receive. If you do not wish to install this library, you may create an "exclude" file and run tar with the X option, as in the following example, but the Oracle program might not operate properly or at all without the library: tar -xvfX where the exclude-file contains, e.g.: /lib/libglib-2.0.so.0.1600.6 /lib/libglib-2.0.so.0 ... Example: tar -xvfX mysql-proxy-0.8.1-solaris10-x86-64bit.tar.gz Exclude Exclude File: mysql-proxy-0.8.1-solaris10-x86-64bit/lib/libglib-2.0.so mysql-proxy-0.8.1-solaris10-x86-64bit/lib/libglib-2.0.so.0 mysql-proxy-0.8.1-solaris10-x86-64bit/lib/libglib-2.0.so.0.1600.6 mysql-proxy-0.8.1-solaris10-x86-64bit/lib/libgmodule-2.0.so mysql-proxy-0.8.1-solaris10-x86-64bit/lib/libgmodule-2.0.so.0 mysql-proxy-0.8.1-solaris10-x86-64bit/lib/libgmodule-2.0.so.0.1600.6 mysql-proxy-0.8.1-solaris10-x86-64bit/lib/libgthread-2.0.so mysql-proxy-0.8.1-solaris10-x86-64bit/lib/libgthread-2.0.so.0 mysql-proxy-0.8.1-solaris10-x86-64bit/lib/libgthread-2.0.so.0.1600.6 mysql-proxy-0.8.1-solaris10-x86-64bit/licenses/lgpl/glib-2.16.6.tar.g z This component is licensed under Section A.10, "GNU Lesser General Public License Version 2.1, February 1999." A.9. GNU General Public License Version 2.0, June 1991 The following applies to all products licensed under the GNU General Public License, Version 2.0: You may not use the identified files except in compliance with the GNU General Public License, Version 2.0 (the "License.") You may obtain a copy of the License at http://www.gnu.org/licenses/gpl-2.0.txt. A copy of the license is also reproduced below. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change fre e software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit t o using it. (Some other Free Software Foundation software is covered b y the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that yo u have the freedom to distribute copies of free software (and charge fo r this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights . These restrictions translate to certain responsibilities for you if y ou distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certai n that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the origina l, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making th e program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at al l. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below , refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation i n the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Progra m is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty ; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fe e. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that i n whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provid e a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Progra m with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following : a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Section s 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License . However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject t o these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein . You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do no t excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under thi s License and any other pertinent obligations, then as a consequence yo u may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program b y all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended t o apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willin g to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Progra m specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published b y the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by t he Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by t he two goals of preserving the free status of all derivatives of our fre e software and of promoting the sharing and reuse of software generally . NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIN D, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLI ED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUC H DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make i t free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safes t to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper ma il. If the program is interactive, make it output a short notice like thi s when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free software, and you are welcome to redistribute it under certain conditions; type 'show c' for details. The hypothetical commands 'show w' and 'show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than 'show w' and 'show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or yo ur school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program 'Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your progra m into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. A.10. GNU Lesser General Public License Version 2.1, February 1999 The following applies to all products licensed under the GNU Lesser General Public License, Version 2.1: You may not use the identified files except in compliance with the GNU Lesser General Public License, Version 2.1 (the "License"). You may obtain a copy of the License at http://www.gnu.org/licenses/lgpl-2.1.html. A copy of the license is also reproduced below. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also count s as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whethe r this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations bel ow. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure tha t you have the freedom to distribute copies of free software (and charg e for this service if you wish); that you receive source code or can ge t it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender thes e rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether grati s or for a fee, you must give the recipients all the rights that we gav e you. You must make sure that they, too, receive or can get the sourc e code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence o f any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or usin g a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantage s are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certai n special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of th e users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter mus t be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms o f this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translate d straightforwardly into another language. (Hereinafter, translation i s included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code mean s all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are no t covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output fro m such a program is covered only if its contents constitute a work base d on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that , in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Librar y with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Publi c License instead of this License to a given copy of the Library. To d o this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2 , instead of to this License. (If a newer version than version 2 of th e ordinary GNU General Public License has appeared, then you can specif y that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, whic h must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled o r linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header fil e that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivativ e work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do on e of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in th e Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system , rather than copying library functions into the executable, and (2 ) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with . c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the abov e specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you canno t use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combine d library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies , or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on th e Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Librar y subject to these terms and conditions. You may not impose any furthe r restrictions on the recipients' exercise of the rights granted herein . You are not responsible for enforcing compliance by third parties wit h this License. 11. If, as a consequence of a court judgment or allegation of paten t infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do no t excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under thi s License and any other pertinent obligations, then as a consequence yo u may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library b y all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willin g to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Librar y specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published b y the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free statu s of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUC H DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version . This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Softwa re Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper ma il. You should also get your employer (if you work as a programmer) or yo ur school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! A.11. GNU Libtool License The following software may be included in this product: GNU Libtool (The GNU Portable Library Tool) If you are receiving a copy of the Oracle software in source code, you are also receiving a copy of two files (ltmain.sh and ltdl.h) generated by the GNU Libtool in source code. If you received the Oracle software under a license other than a commercial (non-GPL) license, then the terms of the Oracle license do NOT apply to these files from GNU Libtool; they are licensed under the following licenses, separately from the Oracle programs you receive. Oracle elects to use GNU General Public License version 2 (GPL) for any software where a choice of GPL or GNU Lesser/Library General Public License (LGPL) license versions are made available with the language indicating that GPL/LGPL or any later version may be used, or where a choice of which version of the GPL/LGPL is applied is unspecified. From GNU Libtool: ltmain.sh - Provide generalized library-building support services. NOTE: Changing this file will not affect anything until you rerun configure. Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Originally by Gordon Matzigkeit, 1996 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception to the GNU General Public License, if you distribute this file as part of a program that contains a configuration script generated by Autoconf, you may include it under the same distribution terms that you use for the rest of that program. This component is licensed under Section A.9, "GNU General Public License Version 2.0, June 1991" A.12. GNU Readline License The following software may be included in this product: GNU Readline Library GNU Readline Library With respect to MySQL Server/Cluster software licensed under GNU General Public License, you are receiving a copy of the GNU Readline Library in source code. The terms of any Oracle license that might accompany the Oracle programs do NOT apply to the GNU Readline Library; it is licensed under the following license, separately from the Oracle programs you receive. Oracle elects to use GNU General Public License version 2 (GPL) for any software where a choice of GPL license versions are made available with the language indicating that GPLv2 or any later version may be used, or where a choice of which version of the GPL is applied is unspecified. This component is licensed under Section A.9, "GNU General Public License Version 2.0, June 1991" A.13. Google Controlling Master Thread I/O Rate Patch License The following software may be included in this product: Google Controlling master thread I/O rate patch Copyright (c) 2009, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in th e documentation and/or other materials provided with the distributio n. * Neither the name of the Google Inc. nor the names of its contribut ors may be used to endorse or promote products derived from this softw are without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. A.14. Google Perftools (TCMalloc utility) License The following software may be included in this product: Google Perftools (TCMalloc utility) Copyright (c) 1998-2006, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. A.15. Google SMP Patch License The following software may be included in this product: Google SMP Patch Google SMP patch Copyright (c) 2008, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in th e documentation and/or other materials provided with the distributio n. * Neither the name of the Google Inc. nor the names of its contribut ors may be used to endorse or promote products derived from this softw are without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. A.16. lib_sql.cc License The following software may be included in this product: lib_sql.cc Copyright (c) 2000 SWsoft company This material is provided "as is", with absolutely no warranty expressed or implied. Any use is at your own risk. Permission to use or copy this software for any purpose is hereby granted without fee, provided the above notices are retained on all copies. Permission to modify the code and to distribute modified code is granted, provided the above notices are retained, and a notice that the code was modified is included with the above copyrigh t notice. This code was modified by the MySQL team. A.17. libevent License The following software may be included in this product: libevent Copyright (c) 2000-2007 Niels Provos All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in th e documentation and/or other materials provided with the distributio n. 3. The name of the author may not be used to endorse or promote produ cts derived from this software without specific prior written permissi on. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRAN TIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIME D. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE == Parts developed by Adam Langley == == log.c Based on err.c, which was adapted from OpenBSD libc *err*warncode. Copyright (c) 2005 Nick Mathewson Copyright (c) 2000 Dug Song Copyright (c) 1993 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. == == min_heap.h Copyright (c) 2006 Maxim Yegorushkin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. == == win32.c Copyright 2000-2002 Niels Provos Copyright 2003 Michael A. Davis All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. == A.18. Linux-PAM License The following software may be included in this product: Linux-PAM (pam-devel, Pluggable authentication modules for Linux) Copyright Theodore Ts'o, 1996. All rights reserved. (For the avoidance of doubt, Oracle uses and distributes this component under the terms below and elects not to do so under the GPL even though the GPL is referenced as an option below.) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, and the entire permission notice in its entirety, including the disclaimer of warranties. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. ALTERNATIVELY, this product may be distributed under the terms of the GNU Public License, in which case the provisions of the GPL are required INSTEAD OF the above restrictions. (This clause is necessary due to a potential bad interaction between the GPL and the restrictions contained in a BSD-style copyright.) THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. A.19. LPeg Library License The following software may be included in this product: LPeg Use of any of this software is governed by the terms of the license b elow: Copyright (c) 2008 Lua.org, PUC-Rio. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the ri ghts to use, copy, modify, merge, publish, distribute, sublicense, and/or sel l copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be includ ed in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRE SS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILI TY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE A UTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFT WARE. A.20. Lua (liblua) License The following software may be included in this product: Lua (liblua) Copyright (c) 1994-2008 Lua.org, PUC-Rio. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. A.21. LuaFileSystem Library License The following software may be included in this product: LuaFileSystem Copyright (c) 2003 Kepler Project. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. A.22. md5 (Message-Digest Algorithm 5) License The following software may be included in this product: md5 (Message-Digest Algorithm 5) This code implements the MD5 message-digest algorithm. The algorithm is due to Ron Rivest. This code was written by Colin Plumb in 1993, no copyright is claimed. This code is in the public domain; do with it what you wish. Equivalent code is available from RSA Data Security, Inc. This code has been tested against that, and is equivalent, except that you don't need to include two pages of legalese with every copy. The code has been modified by Mikael Ronstroem to handle calculating a hash value of a key that is always a multiple of 4 bytes long. Word 0 of the calculated 4-word hash value is returned as the hash value. A.23. nt_servc (Windows NT Service class library) License The following software may be included in this product: nt_servc (Windows NT Service class library) Windows NT Service class library Copyright Abandoned 1998 Irena Pancirov - Irnet Snc This file is public domain and comes with NO WARRANTY of any kind A.24. OpenPAM License The following software may be included in this product: OpenPAM Copyright (c) 2002-2003 Networks Associates Technology, Inc. Copyright (c) 2004-2007 Dag-Erling Smørgrav All rights reserved. This software was developed for the FreeBSD Project by ThinkSec AS and Network Associates Laboratories, the Security Research Division of Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research program. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. A.25. PCRE License The following software may be included in this product: PCRE (Perl Compatible Regular Expressions) Library PCRE LICENCE PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. Release 7 of PCRE is distributed under the terms of the "BSD" licence, as specified below. The documentation for PCRE, supplied in the "doc" directory, is distributed under the same terms as the software itself. The basic library functions are written in C and are freestanding. Also included in the distribution is a set of C++ wrapper functions. THE BASIC LIBRARY FUNCTIONS --------------------------- Written by: Philip Hazel Email local part: ph10 Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. Phone: +44 1223 334714. Copyright (c) 1997-2006 University of Cambridge All rights reserved. THE C++ WRAPPER FUNCTIONS ------------------------- Contributed by: Google Inc. Copyright (c) 2006, Google Inc. All rights reserved. THE "BSD" LICENCE ----------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of Cambridge nor the name of Google Inc. nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. End A.26. Percona Multiple I/O Threads Patch License The following software may be included in this product: Percona Multiple I/O threads patch Copyright (c) 2008, 2009 Percona Inc All rights reserved. Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in th e documentation and/or other materials provided with the distributio n. * Neither the name of Percona Inc. nor the names of its contributors may be used to endorse or promote products derived from this softw are without specific prior written permission of Percona Inc. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. A.27. RegEX-Spencer Library License The following software may be included in this product: Henry Spencer's Regular-Expression Library (RegEX-Spencer) Copyright 1992, 1993, 1994 Henry Spencer. All rights reserved. This software is not subject to any license of the American Telephone and Telegraph Company or of the Regents of the University of Californ ia. Permission is granted to anyone to use this software for any purpose on any computer system, and to alter it and redistribute it, subject to the following restrictions: 1. The author is not responsible for the consequences of use of this software, no matter how awful, even if they arise from flaws in it . 2. The origin of this software must not be misrepresented, either by explicit claim or by omission. Since few users ever read sources, credits must appear in the documentation. 3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. Since few users ever read sources, credits must appear in the documentation. 4. This notice may not be removed or altered. A.28. RFC 3174 - US Secure Hash Algorithm 1 (SHA1) License The following software may be included in this product: RFC 3174 - US Secure Hash Algorithm 1 (SHA1) RFC 3174 - US Secure Hash Algorithm 1 (SHA1) Copyright (C) The Internet Society (2001). All Rights Reserved. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations, except as needed for the purpose of developing Internet standards in which case the procedures for copyrights defined in the Internet Standards process must be followed, or as required to translate it into languages other than English. The limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assigns. This document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Acknowledgement Funding for the RFC Editor function is currently provided by the Internet Society. A.29. Richard A. O'Keefe String Library License The following software may be included in this product: Richard A. O'Keefe String Library The Richard O'Keefe String Library is subject to the following notice : These files are in the public domain. This includes getopt.c, which is the work of Henry Spencer, University of Toronto Zoology, who says of it "None of this software is derived from Bell software. I had no access to the source for Bell's versions at the time I wrote it. This software is hereby explicitly placed in the public domain. It may be used for any purpose on any machine by anyone." I would greatly prefer it if *my* material received no military use. The t_ctype.h file is subject to the following notice: Copyright (C) 1998, 1999 by Pruet Boonma, all rights reserved. Copyright (C) 1998 by Theppitak Karoonboonyanan, all rights reserved. Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies. Smaphan Raruenrom and Pruet Boonma makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. A.30. SHA-1 in C License The following software may be included in this product: SHA-1 in C SHA-1 in C By Steve Reid 100% Public Domain A.31. Simple Logging Facade for Java (SLF4J) License The following software may be included in this product: Simple Logging Facade for Java (SLF4J) Copyright (c) 2004-2008 QOS.ch All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. A.32. zlib License The following software may be included in this product: zlib Oracle gratefully acknowledges the contributions of Jean-loup Gailly and Mark Adler in creating the zlib general purpose compression library which is used in this product. zlib.h -- interface of the 'zlib' general purpose compression library Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler zlib.h -- interface of the 'zlib' general purpose compression library version 1.2.3, July 18th, 2005 Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler zlib.h -- interface of the 'zlib' general purpose compression library version 1.2.5, April 19th, 2010 Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied war ranty. In no event will the authors be held liable for any damages arising f rom the use of this software. Permission is granted to anyone to use this sof tware for any purpose,including commercial applications, and to alter it an d redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must n ot claim that you wrote the original software. If you use this softwa re in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must n ot be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribu tion. Jean-loup Gailly jloup@gzip.org Mark Adler madler@alumni.caltech.edu A.33. ZLIB.NET License The following software may be included in this product: ZLIB.NET Copyright (c) 2006-2007, ComponentAce http://www.componentace.com All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mysql-connector-odbc-5.1.10-src/Makefile.am100644 15766 12 6400 11707541006 17223 0ustar00cteamstaff# Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## # # # Makefile.am # # # # @description: This is the MyODBC 5.1 driver Makefile.am # # # # @author : MySQL AB (monty@mysql.com, venu@mysql.com) # # @date : 2001-Aug-15 # # @product : myodbc3 # # # ########################################################################## AUTOMAKE_OPTIONS=foreign INCLUDES = @MYSQL_INCLUDES@ LDFLAGS=@EXTRA_LDFLAGS@ build="mysql-connector-odbc-@VERSION@-@SYSTEM_TYPE@-@MACHINE_TYPE@" SUBDIRS = \ util \ driver \ installer \ @myodbc_test_dir@ \ scripts \ dltest DIST_SUBDIRS = \ util \ driver \ installer \ test \ scripts \ dltest readmedir = $(pkgdatadir) readme_DATA = \ ChangeLog \ README \ README.debug \ INSTALL \ COPYING \ Licenses_for_Third-Party_Components.txt # # Create the binary distribution # bin-dist: all $(top_builddir)/scripts/make_binary_distribution bin-tar: all mkdir $(build) $(MAKE) install prefix=`pwd`/$(build) cp $(pkgdata_DATA) $(build)/ tar cvf $(build).tar $(build) gzip -9vf $(build).tar rm -rf $(build) .PHONY: test test: (cd test; $(MAKE) test) # # Create a source snapshot # snapshot: $(MAKE) dist distdir=mysql-connector-odbc-@VERSION@-`date +"%Y%m%d"` # Note that directories will be copied recursively EXTRA_DIST = \ $(readme_DATA) \ INSTALL.win \ BUILD.unix \ BUILD.win \ resource.h \ VersionInfo.h \ MYODBC_CONF.h \ MYODBC_MYSQL.h \ MYODBC_ODBC.h \ mysql.bmp \ CreateBinaryMsi.bat \ CreateBinaryZip.bat \ CreateSourceZip.bat \ Install.bat \ Uninstall.bat \ Upgrade.bat \ CMakeLists.txt \ cmake \ setupgui \ wix # Remove Subversion ".svn" subdirectories from source distribution dist-hook: rm -rf `find $(distdir) -type d -name .svn -print` mysql-connector-odbc-5.1.10-src/MYODBC_ODBC.h100644 15766 12 3435 11707541006 17111 0ustar00cteamstaff/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MYODBC_ODBC_H #define MYODBC_ODBC_H #define ODBCVER 0x0351 #ifdef _UNIX_ # include # ifdef HAVE_LIBDL # include # endif # include # include # ifdef USE_IODBC # include # else # include # endif # ifndef SYSTEM_ODBC_INI # define BOTH_ODBC_INI ODBC_BOTH_DSN # define USER_ODBC_INI ODBC_USER_DSN # define SYSTEM_ODBC_INI ODBC_SYSTEM_DSN # endif /* If SQL_API is not defined, define it, unixODBC doesn't have this */ # if !defined(SQL_API) # define SQL_API # endif #else # include # ifndef RC_INVOKED # pragma pack(1) # endif # include # include # include #endif #endif /* !MYODBC_ODBC_H */ mysql-connector-odbc-5.1.10-src/Makefile.svn100644 15766 12 1032 11707541006 17430 0ustar00cteamstaffall: @echo "" @echo "This is used to build configure; useful when building" @echo "from a sources retreived from source code repository." @echo "It requires the GNU autotools, including:" @echo "- libtool" @echo "- automake" @echo "- autoheader" @echo "- autoconf" @echo "" @autoheader @aclocal @if [ -f /usr/bin/glibtoolize ]; then \ glibtoolize --automake --force; \ else \ libtoolize --automake --force; \ fi @automake --add-missing --force-missing @autoconf @echo "Done!" @echo "Now run ./configure --help" mysql-connector-odbc-5.1.10-src/CreateBinaryMsi.bat100644 15766 12 12446 11707541006 20727 0ustar00cteamstaff@ECHO OFF REM Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. REM REM The MySQL Connector/ODBC is licensed under the terms of the GPLv2 REM , like most REM MySQL Connectors. There are special exceptions to the terms and REM conditions of the GPLv2 as it is applied to this software, see the REM FLOSS License Exception REM . REM REM This program is free software; you can redistribute it and/or modify REM it under the terms of the GNU General Public License as published REM by the Free Software Foundation; version 2 of the License. REM REM This program is distributed in the hope that it will be useful, but REM WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY REM or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License REM for more details. REM REM You should have received a copy of the GNU General Public License along REM with this program; if not, write to the Free Software Foundation, Inc., REM 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA REM REM ######################################################### REM REM \brief Create binary distribution (with installer). REM REM Creates binary distributions with installer. It will REM create a GPL and a Commercial version. REM 1) MSI REM 2) setup.exe (zipped) REM REM \sa CreateBinaryZip.bat REM CreateSourceZip.bat REM REM ######################################################### IF "%1"=="" GOTO doSyntax IF "%2"=="" GOTO doSyntax REM Building... call Build.bat REM Copying files to wix stage area... IF NOT EXIST ..\wix-installer\bin\mysql-connector-odbc-%1-win32 ( mkdir ..\wix-installer\bin\mysql-connector-odbc-%1-win32 ) IF NOT EXIST ..\wix-installer\bin\mysql-connector-odbc-%1-win32\Windows ( mkdir ..\wix-installer\bin\mysql-connector-odbc-%1-win32\Windows ) IF NOT EXIST ..\wix-installer\bin\mysql-connector-odbc-%1-win32\Windows\System32 ( mkdir ..\wix-installer\bin\mysql-connector-odbc-%1-win32\Windows\System32 ) copy bin\* ..\wix-installer\bin\mysql-connector-odbc-%1-win32\Windows\System32 copy lib\* ..\wix-installer\bin\mysql-connector-odbc-%1-win32\Windows\System32 copy Licenses_for_Third-Party_Components.txt ..\wix-installer\bin\mysql-connector-odbc-%1-win32\Windows\System32 copy COPYING ..\wix-installer\bin\mysql-connector-odbc-%1-win32\Windows\System32 copy LICENSE.* ..\wix-installer\bin\mysql-connector-odbc-%1-win32\Windows\System32 REM Creating Commercial msi... cd ..\wix-installer copy bin\mysql-connector-odbc-%1-win32\Windows\System32\LICENSE.mysql bin\mysql-connector-odbc-%1-win32\Windows\System32\myodbc3-license.rtf copy bin\mysql-connector-odbc-%1-win32\Windows\System32\LICENSE.mysql resources\commercial_license.rtf call OdbcMakeSetup.bat %1 %2 commercial REM Creating GPL msi... move /Y bin\dist\mysql-connector-odbc-%1-win32.msi bin\dist\mysql-connector-odbc-commercial-%1-win32.msi move /Y bin\dist\mysql-connector-odbc-%1-win32.msi bin\dist\mysql-connector-odbc-commercial-%1-win32.msi move /Y bin\dist\mysql-connector-odbc-%1-win32.zip bin\dist\mysql-connector-odbc-commercial-%1-win32.zip move /Y bin\dist\mysql-connector-odbc-%1-win32.msi.md5 bin\dist\mysql-connector-odbc-commercial-%1-win32.msi.md5 move /Y bin\dist\mysql-connector-odbc-%1-win32.zip.md5 bin\dist\mysql-connector-odbc-commercial-%1-win32.zip.md5 copy bin\mysql-connector-odbc-%1-win32\Windows\System32\COPYING bin\mysql-connector-odbc-%1-win32\Windows\System32\myodbc3-license.rtf call OdbcMakeSetup.bat %1 %2 gpl cd ..\*odbc3 EXIT /B 0 :doSyntax ECHO "+-----------------------------------------------------+" ECHO "| CreateBinaryMsi.bat |" ECHO "+-----------------------------------------------------+" ECHO "| |" ECHO "| DESCRIPTION |" ECHO "| |" ECHO "| Use this to build sources and create a MSI based |" ECHO "| installers for commercial and GPL. |" ECHO "| |" ECHO "| SYNTAX |" ECHO "| |" ECHO "| CreateBinaryMsi |" ECHO "| |" ECHO "| must be a 3 number version |" ECHO "| |" ECHO "| must be; |" ECHO "| a - alpha |" ECHO "| b - beta |" ECHO "| r - release candidate |" ECHO "| p - production |" ECHO "| i - internal |" ECHO "| |" ECHO "| EXAMPLE |" ECHO "| |" ECHO "| CreateBinaryMsi 3.51.12 p |" ECHO "| |" ECHO "+-----------------------------------------------------+" mysql-connector-odbc-5.1.10-src/README.dist100644 15766 12 4633 11707541006 17017 0ustar00cteamstaff+-------------------------------------------------------------+ | Connector/ODBC | | Making Distributions | +-------------------------------------------------------------+ In all cases below you will want to start by ensuring that the proper version is set in the various locations (yet to be fully documented) within the source. For example; - VersionInfo.h - used by driver and setup rc files - myodbc3.h - driver.pro - remove version (use myodbc3.h) - myodbc3.def - remove version? - myodbc3S.def - remove version? - configure.in - defines.pri - not needed? Linux ===== Source (tar-ball) - svn co svn+ssh://svn.mysql.com/connectors-svnroot/connector-odbc3 - change current ver to new ver in source/config files - make -f Makefile.svn - ./configure - make dist - source tar-ball will be in current dir Binary (RPM) - svn co svn+ssh://svn.mysql.com/connectors-svnroot/connector-odbc3 - change current ver to new ver in source/config files - make -f Makefile.svn - ./configure - make dist - cd scripts - make - cd .. - su - scripts/makerpm - source RPM will be in /usr/src/packages/SRPMS - binary RPM will be in /usr/src/packages/RPMS/i586 Binary (tar-ball) - make bin-tar (or 'make tar-ball') - change name of tar-ball (inside and out) for example; mysql-connector-odbc-3.51.11-suse-linux-i686.tar.gz to... mysql-connector-odbc-3.51.11-1-pc-linux-i686.tar.gz XP == Some scripts expect pkzipc (the command-line version of pkzip) somewhere in your path. Binary distributions may need to include files from outside of the myodbc build tree. For example; builds done with Visual Studio probably want to include msvcr*.dll (Microsoft C runtime library). If its a debug build one may want to include the debug version of the supporting libraries such as; msvcr*d.dll Source (zip) Simply execute CreateSourceZip.bat from the source root directory then look for the result in the source root dir. Binary (zip) Simply execute CreateBinaryZip.bat from the source root directory then look for the result in the source root dir. Binary (msi) In this case you will want the wix-installer source repo checked out in parallel dir of MyODBC. The wix-installer is not distributed as part of MyODBC. Execute CreateBinaryMsi.bat from the source root directory then look for the result in the source root dir. mysql-connector-odbc-5.1.10-src/CreateBinaryZip.bat100644 15766 12 12044 11707541006 20733 0ustar00cteamstaff@ECHO OFF REM Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. REM REM The MySQL Connector/ODBC is licensed under the terms of the GPLv2 REM , like most REM MySQL Connectors. There are special exceptions to the terms and REM conditions of the GPLv2 as it is applied to this software, see the REM FLOSS License Exception REM . REM REM This program is free software; you can redistribute it and/or modify REM it under the terms of the GNU General Public License as published REM by the Free Software Foundation; version 2 of the License. REM REM This program is distributed in the hope that it will be useful, but REM WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY REM or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License REM for more details. REM REM You should have received a copy of the GNU General Public License along REM with this program; if not, write to the Free Software Foundation, Inc., REM 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA REM REM ######################################################### REM REM \brief Create binary distribution (without installer). REM REM Creates binary distribution - without installer. REM This may be useful for those wishing to incorporate REM myodbc into their own installer. REM REM \note To do this you are going to need pkzipc (the command-line REM version of pkzip) somewhere in your path. REM REM \sa CreateBinaryMsi.bat REM CreateSourceZip.bat REM REM ######################################################### IF "%1"=="" GOTO doSyntax ECHO Building... call Build.bat ECHO Clean any existing stage area... IF EXIST .\mysql-connector-odbc-noinstall-%1-win32.zip ( del mysql-connector-odbc-noinstall-%1-win32.zip ) IF EXIST .\mysql-connector-odbc-noinstall-%1-win32 ( rmdir /S /Q mysql-connector-odbc-noinstall-%1-win32 ) IF EXIST .\mysql-connector-odbc-commercial-noinstall-%1-win32.zip ( del mysql-connector-odbc-commercial-noinstall-%1-win32.zip ) IF EXIST .\mysql-connector-odbc-commercial-noinstall-%1-win32 ( rmdir /S /Q mysql-connector-odbc-commercial-noinstall-%1-win32 ) ECHO GPL: Create stage area and populate... mkdir mysql-connector-odbc-noinstall-%1-win32 mkdir mysql-connector-odbc-noinstall-%1-win32\bin mkdir mysql-connector-odbc-noinstall-%1-win32\lib xcopy /E /Y bin\* mysql-connector-odbc-noinstall-%1-win32\bin xcopy /E /Y lib\* mysql-connector-odbc-noinstall-%1-win32\lib copy Install.bat mysql-connector-odbc-noinstall-%1-win32 copy Uninstall.bat mysql-connector-odbc-noinstall-%1-win32 copy ChangeLog mysql-connector-odbc-noinstall-%1-win32\ChangeLog.rtf copy COPYING mysql-connector-odbc-noinstall-%1-win32\COPYING.rtf copy README mysql-connector-odbc-noinstall-%1-win32\README.rtf copy INSTALL mysql-connector-odbc-noinstall-%1-win32\INSTALL.rtf copy INSTALL.win mysql-connector-odbc-noinstall-%1-win32\INSTALL-win.rtf copy Licenses_for_Third-Party_Components.txt mysql-connector-odbc-noinstall-%1-win32 ECHO Zipping... pkzipc -add -maximum -recurse -path=current mysql-connector-odbc-noinstall-%1-win32.zip mysql-connector-odbc-noinstall-%1-win32\*.* ECHO COMMERCIAL: Create stage area and populate... move mysql-connector-odbc-noinstall-%1-win32 mysql-connector-odbc-commercial-noinstall-%1-win32 copy LICENSE.mysql mysql-connector-odbc-commercial-noinstall-%1-win32\LICENSE.rtf ECHO Zipping... pkzipc -add -maximum -recurse -path=current mysql-connector-odbc-commercial-noinstall-%1-win32.zip mysql-connector-odbc-commercial-noinstall-%1-win32/*.* IF EXIST .\mysql-connector-odbc-commercial-noinstall-%1-win32 ( rmdir /S /Q mysql-connector-odbc-commercial-noinstall-%1-win32 ) EXIT /B 0 :doSyntax ECHO "+-----------------------------------------------------+" ECHO "| CreateBinaryZip.bat |" ECHO "+-----------------------------------------------------+" ECHO "| |" ECHO "| DESCRIPTION |" ECHO "| |" ECHO "| Use this to build sources and create a ZIP based |" ECHO "| noinstaller distribution for commercial and GPL. |" ECHO "| |" ECHO "| SYNTAX |" ECHO "| |" ECHO "| CreateBinaryZip |" ECHO "| |" ECHO "| must be a 3 number version |" ECHO "| |" ECHO "| EXAMPLE |" ECHO "| |" ECHO "| CreateBinaryZip 3.51.12 |" ECHO "| |" ECHO "+-----------------------------------------------------+" mysql-connector-odbc-5.1.10-src/CreateSourceZip.bat100644 15766 12 12077 11707541006 20755 0ustar00cteamstaff@ECHO OFF REM Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. REM REM The MySQL Connector/ODBC is licensed under the terms of the GPLv2 REM , like most REM MySQL Connectors. There are special exceptions to the terms and REM conditions of the GPLv2 as it is applied to this software, see the REM FLOSS License Exception REM . REM REM This program is free software; you can redistribute it and/or modify REM it under the terms of the GNU General Public License as published REM by the Free Software Foundation; version 2 of the License. REM REM This program is distributed in the hope that it will be useful, but REM WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY REM or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License REM for more details. REM REM You should have received a copy of the GNU General Public License along REM with this program; if not, write to the Free Software Foundation, Inc., REM 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA REM REM ######################################################### REM REM \brief Create source distribution. REM REM Creates source distribution for windows. REM REM \note To do this you are going to need pkzipc (the command-line REM version of pkzip) somewhere in your path. REM REM \sa CreateBinaryMsi.bat REM CreateBinaryZip.bat REM REM ######################################################### IF "%1"=="" GOTO doSyntax ECHO Clean any existing stage area... IF EXIST mysql-connector-odbc-%1-win-src.zip ( del mysql-connector-odbc-%1-win-src.zip ) IF EXIST mysql-connector-odbc-%1-win-src ( rmdir /S /Q mysql-connector-odbc-%1-win-src ) ECHO Create stage area and populate... mkdir mysql-connector-odbc-%1-win-src REM Its easier to copy specific files then try REM to clean out garbage (in this case). REM xcopy /E /Y * mysql-connector-odbc-%1-win-src REM root mkdir mysql-connector-odbc-%1-win-src copy ChangeLog mysql-connector-odbc-%1-win-src\ChangeLog.rtf copy COPYING mysql-connector-odbc-%1-win-src\COPYING.rtf copy Build.bat mysql-connector-odbc-%1-win-src copy Install.bat mysql-connector-odbc-%1-win-src copy Uninstall.bat mysql-connector-odbc-%1-win-src copy CreateMakefiles.bat mysql-connector-odbc-%1-win-src copy CreateVisualStudioProjects.bat mysql-connector-odbc-%1-win-src copy config.pri mysql-connector-odbc-%1-win-src copy defines.pri mysql-connector-odbc-%1-win-src copy mysql.pri mysql-connector-odbc-%1-win-src copy odbc.pri mysql-connector-odbc-%1-win-src copy root.pro mysql-connector-odbc-%1-win-src copy README mysql-connector-odbc-%1-win-src\README.rtf copy BUILD mysql-connector-odbc-%1-win-src\BUILD.rtf copy BUILD.win mysql-connector-odbc-%1-win-src\BUILD-win.rtf copy INSTALL mysql-connector-odbc-%1-win-src\INSTALL.rtf copy INSTALL.win mysql-connector-odbc-%1-win-src\INSTALL-win.rtf copy Licenses_for_Third-Party_Components.txt mysql-connector-odbc-%1-win-src copy resource.h mysql-connector-odbc-%1-win-src copy VersionInfo.h mysql-connector-odbc-%1-win-src copy mysql.bmp mysql-connector-odbc-%1-win-src REM dltest mkdir mysql-connector-odbc-%1-win-src\dltest copy dltest\*.pro mysql-connector-odbc-%1-win-src\dltest copy dltest\*.c mysql-connector-odbc-%1-win-src\dltest REM util mkdir mysql-connector-odbc-%1-win-src\util copy util\*.pro mysql-connector-odbc-%1-win-src\util copy util\*.h mysql-connector-odbc-%1-win-src\util copy util\*.c mysql-connector-odbc-%1-win-src\util REM setup mkdir mysql-connector-odbc-%1-win-src\setup copy setup\*.pro mysql-connector-odbc-%1-win-src\setup copy setup\*.def mysql-connector-odbc-%1-win-src\setup copy setup\*.xpm mysql-connector-odbc-%1-win-src\setup copy setup\*.rc mysql-connector-odbc-%1-win-src\setup copy setup\*.c mysql-connector-odbc-%1-win-src\setup copy setup\*.cpp mysql-connector-odbc-%1-win-src\setup copy setup\*.h mysql-connector-odbc-%1-win-src\setup REM installer mkdir mysql-connector-odbc-%1-win-src\installer copy installer\*.pro mysql-connector-odbc-%1-win-src\installer copy installer\*.c mysql-connector-odbc-%1-win-src\installer REM driver mkdir mysql-connector-odbc-%1-win-src\driver copy driver\*.pro mysql-connector-odbc-%1-win-src\driver copy driver\*.c mysql-connector-odbc-%1-win-src\driver copy driver\*.h mysql-connector-odbc-%1-win-src\driver copy driver\*.rc mysql-connector-odbc-%1-win-src\driver copy driver\*.def mysql-connector-odbc-%1-win-src\driver ECHO Zipping... pkzipc -add -maximum -recurse -path=current mysql-connector-odbc-%1-win-src.zip mysql-connector-odbc-%1-win-src/*.* IF EXIST mysql-connector-odbc-%1-win-src ( rmdir /S /Q mysql-connector-odbc-%1-win-src ) ECHO **** ECHO * Hopefully things went well and you have a fresh new source distribution ECHO * in the source root directory. ECHO **** EXIT /B 0 :doSyntax ECHO CreateSourceZip ECHO =============== ECHO . ECHO Usage: ECHO . ECHO %0 VERSION ECHO . ECHO VERSION must be; a 3 number version code ECHO . ECHO Examples: ECHO . ECHO %0 3.51.12 mysql-connector-odbc-5.1.10-src/BUILD.unix100644 15766 12 7120 11707541006 16733 0ustar00cteamstaff+-------------------------------------------------------------+ | Connector/ODBC | | UNIX Platforms | +-------------------------------------------------------------+ INTRODUCTION --------------------------------------------------------------- In this document, we provide a general and brief explaination of how to build the MyODBC driver on UNIX platforms. NOTE: This information is fairly generic. You may find other README files with more detailed information about building MyODBC with specific flavours of UNIX. WHAT YOU NEED --------------------------------------------------------------- UNIX ---- UNIX or a UNIX-like operating system such as; - Solaris - Linux - OSX ODBC ---- You need an ODBC SDK, and you probably want an ODBC Driver Manager. ODBC has not, traditionally, been a standard part of any UNIX or UNIX-like platform. This causes a number of vendors to provide ODBC solutions which can be added to UNIX. In theory this should not be a problem, but in practice this can cause compatability and portability issues. Recently; Linux distributions have been including unixODBC as a standard option, and Apple OSX now has ODBC by default. We recommend, and use, unixODBC on all UNIX-like platforms except OSX where we use the default ODBC. See: http://www.unixodbc.org/ Compiler Tools -------------- We try to build using the native compiler tool set for the target platform. For example, this is SunStudio on Solaris. But the GNU tool-chain is a common compiler that works across all platforms. Source Code ----------- You can get the source code from the bzr source repository (for 'bleeding edge' code) or more commonly, simply download the zip or tarball of the source. Source: bzr branch lp:myodbc https://code.launchpad.net/~myodbc-developers Downloads: http://dev.mysql.com/downloads/connector/odbc/ BUILD --------------------------------------------------------------- cmake ------------- CMake is the build system used to create configure files for your system. Instructions are as follows: cd into the MyODBC source directory, then run cmake. For example: $ cd /path/to/myodbc/source $ cmake -G "Unix Makefiles" . CMake will look for iODBC by default. Alternatively, if you are using unixODBC instead of iODBC, then you'll need to pass in the appropriate parameter, such as: $ cmake -G "Unix Makefiles" -DWITH_UNIXODBC=1 If the driver manager headers and/or libraries cannot be found, then you may specify their location by passing in these additional parameters: -DODBC_INCLUDES=/path/to/headers -DODBC_LIB_DIR=/path/to/libs And you might need to help cmake find the MySQL headers, by setting the environment variable MYSQL_DIR to the appropriate location. For example: $ export MYSQL_DIR=/usr/local/mysql Upon success, CMake will create the appropriate make files. Then, execute make: $ make INSTALL --------------------------------------------------------------- You probably want to be the root user, or use sudo, because installation will probably require these extra privileges. To install the MyODBC library and tools, execute one of the following: $ sudo make install # make install That will copy all of the MyODBC files to their appropriate locations. Next, you probably want to register the driver with the ODBC on your system. See your ODBC documentation for more about how to do this. For unixODBC, you could run ODBCConfig from a GUI as root, or use the odbcinst command line tool. You may also want to look at the myodbc-installer command-line tool for this. mysql-connector-odbc-5.1.10-src/Install.bat100644 15766 12 14217 11707541006 17312 0ustar00cteamstaff@ECHO OFF REM ######################################################### REM REM \brief Register Connector/ODBC driver with ODBC system REM REM This exists for those working with the Windows REM source distribution or with installer-less REM binary distribution. REM REM Name under which the driver should be registered REM can be specified as first parameter. It should be REM a single word (no spaces). REM REM \sa README.win REM REM ######################################################### REM # SETLOCAL prevents the variables set in this script to REM # be exported to the environment and pollute it SETLOCAL SET driver_name=MySQL ODBC 5.1 Driver SET driver_lib=myodbc5 SET driver_lib_setup=myodbc5S SET installer=myodbc-installer IF "%1" == "" GOTO :doFindDriver SET driver_name=%1 :doFindDriver REM # Find driver location SET libdir=none FOR %%G IN (. lib lib\release lib\relwithdebinfo lib\debug) DO CALL :subCheckLibDir %%G IF "%libdir%" == "none" GOTO :errorNoDrivers REM ECHO "libdir = %libdir%" REM # Find the installer utility REM # Try to find it in the build location CALL :subFindBinDir "%libdir%" SET myodbc_installer=%bindir%\%installer%.exe IF EXIST "%myodbc_installer%" GOTO :doRegister REM # Try some other reasonable locations SET myodbc_installer=bin\%installer%.exe IF EXIST "%myodbc_installer%" GOTO :doRegister SET myodbc_installer=.\%installer%.exe IF EXIST "%myodbc_installer%" GOTO :doRegister REM # Try if it is in the path SET myodbc_installer=%installer%.exe %myodbc_installer% >nul 2>nul REM # "Command not found" generates error 9009 IF NOT ERRORLEVEL 9000 GOTO :doRegister GOTO :errorNoInstaller :doRegister REM ECHO myodbc_installer: %myodbc_installer% REM # Abort if driver is already registered %myodbc_installer% -d -l -n "%driver_name%" 2>nul: IF NOT ERRORLEVEL 1 GOTO :errorDriverInstalled ECHO Registering %driver_name% %myodbc_installer% -d -a -n "%driver_name%" -t "DRIVER=%libdir%\%driver_lib%.dll;SETUP=%libdir%\%driver_lib_setup%.dll" IF ERRORLEVEL 1 GOTO :errorRegisterDriver GOTO :doSuccess REM ###### REM # A subroutine to check if given location REM # (relative to working dir) contains the drivers. REM ###### :subCheckLibDir REM # Skip check if a good libdir was already found IF NOT "%libdir%" == "none" GOTO :eof SET libdir=%CD%\%1 IF NOT EXIST "%libdir%\%driver_lib%.dll" GOTO :wrongLibDir IF NOT EXIST "%libdir%\%driver_lib_setup%.dll" GOTO :wrongLibDir REM ECHO Libdir (%libdir%) is OK. GOTO :eof :wrongLibDir REM ECHO Libdir (%libdir%) is wrong. SET libdir=none GOTO :eof REM ###### REM # A subroutine to compute bindir of the form REM # C:\current\working\directory\bin\XXX where XXX is REM # the last component of libdir, such as Release, Debug etc. REM # The libdir should be given as the first argument %1. REM # Construct %~n1 is used which returns the last component REM # ("file name") of the path stored in %1. REM ###### :subFindBinDir SET bindir=%CD%\bin\%~n1 GOTO :eof :doSuccess ECHO ^+-----------------------------------------------------^+ ECHO ^| DONE ^| ECHO ^+-----------------------------------------------------^+ ECHO ^| ^| ECHO ^| Hopefully things went well; the Connector/ODBC ^| ECHO ^| driver has been registered. ^| ECHO ^| ^| ECHO ^| Connector/ODBC is ready to use. ^| ECHO ^| ^| ECHO ^| The most common thing to do next is to go to the ^| ECHO ^| Control Panel and find the ODBC Administrator - ^| ECHO ^| then use it to create a Data Source Name (DSN) ^| ECHO ^| so you (and your application) can connect to a ^| ECHO ^| MySQL server. ^| ECHO ^| ^| ECHO ^| Alternatively you can use the MyODBC Installer ^| ECHO ^| utility to define data sources. ^| ECHO ^| ^| ECHO ^+-----------------------------------------------------^+ EXIT /B 0 :errorNoDrivers ECHO ^+-----------------------------------------------------^+ ECHO ^| ERROR ^| ECHO ^+-----------------------------------------------------^+ ECHO ^| ^| ECHO ^| Could not find Connector/ODBC drivers. Have you run ^| ECHO ^| this script from the installation directory? ^| ECHO ^| ^| ECHO ^+-----------------------------------------------------^+ EXIT /B 1 :errorNoInstaller ECHO ^+-----------------------------------------------------^+ ECHO ^| ERROR ^| ECHO ^+-----------------------------------------------------^+ ECHO ^| ^| ECHO ^| Could not find the MyODBC Installer utility. Run ^| ECHO ^| this script from the installation directory. ^| ECHO ^| ^| ECHO ^+-----------------------------------------------------^+ EXIT /B 1 :errorDriverInstalled ECHO ^+-----------------------------------------------------^+ ECHO ^| ERROR ^| ECHO ^+-----------------------------------------------------^+ ECHO ^| ^| ECHO ^| Existing Connector/ODBC installed. Request ignored. ^| ECHO ^| ^| ECHO ^+-----------------------------------------------------^+ EXIT /B 1 :errorRegisterDriver ECHO ^+-----------------------------------------------------^+ ECHO ^| ERROR ^| ECHO ^+-----------------------------------------------------^+ ECHO ^| ^| ECHO ^| Could not register the driver. ^| ECHO ^| ^| ECHO ^+-----------------------------------------------------^+ EXIT /B 1 mysql-connector-odbc-5.1.10-src/BUILD.win100644 15766 12 5234 11707541006 16551 0ustar00cteamstaff+-------------------------------------------------------------+ | MySQL Connector/ODBC | | Build on Microsoft Windows | +-------------------------------------------------------------+ WHAT YOU NEED --------------------------------------------------------------- Windows ODBC has been a standard part of Windows since 3.1 but we do not build for very old versions of Windows. Consider building on and for Windows XP or newer. Windows 2000 should be fine also. Compiler Tools We regularly build using Microsoft Visual Studio 7 and 8. Other compilers may work, but are untested. You also need CMake 2.4, http://www.cmake.org Source Code The source code is the main thing. You can get it from the source repository if you want the 'bleeding edge' code but most people will simply download the zip of the source. MySQL We usually build with the latest stable release of MySQL - using a static client lib and other development files. BUILD --------------------------------------------------------------- You need to have the environment variables set for the Visual Studio toolchain. Visual Studio includes a batch file to set these for you, and installs a shortcut into the Start menu to open a command prompt with these variables set. You need to set MYSQL_DIR to point to where the MySQL server is installed, using the short-style filenames: set MYSQL_DIR=C:\PROGRA~1\MySQL\MYSQLS~1.0 Build Connector/ODBC using the "cmake" command-line tool by doing the following from the source root directory (in a command prompt window); cmake -G "Visual Studio 8 2005" This produces a project file that you can open with Visual Studio or build from the command line with either of: devenv.com MySQL_Connector_ODBC.sln /build Release devenv.com MySQL_Connector_ODBC.sln /build RelWithDebInfo To compile the "Debug" build, you must run set the cmake build type so the correct version of the MySQL client libraries are used: cmake -G "Visual Studio 8 2005" -DCMAKE_BUILD_TYPE=Debug devenv.com MySQL_Connector_ODBC.sln /build Debug Upon completion; you will find the executables in the subdirectories of the "bin" and "lib" directories. INSTALL --------------------------------------------------------------- Before installing over an existing installation, run the Uninstall.bat script. To install, execute the Install.bat script to copy the files to your system directory and to register the driver. NOTE: The uninstall script does not actually honour the usage count - it will remove the files regardless of the value of the usage counter. See the INSTALL file for more details. mysql-connector-odbc-5.1.10-src/acinclude.m4100644 15766 12 7740 11707541006 17370 0ustar00cteamstaffdnl --------------------------------------------------------- dnl dnl Macro: dnl AC_FIND_FILE dnl dnl Arguments: dnl dnl Description: dnl Find file(s) in given directories. dnl dnl --------------------------------------------------------- AC_DEFUN([AC_FIND_FILE], [ $3=NO for i in $2; do for j in $1; do if test -r "$i/$j"; then $3=$i break 2 fi done done ]) dnl --------------------------------------------------------- dnl dnl Macro: dnl AC_CHECK_ODBC_TYPE dnl dnl Arguments: dnl dnl Description: dnl Checks if $1 is a valid type in the ODBC environment, dnl and #defines it to $2 if not dnl dnl --------------------------------------------------------- AC_DEFUN([AC_CHECK_ODBC_TYPE], [ AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(ac_cv_odbc_$1, [ echo > conftest.c for i in $odbc_headers do echo "#include <$i>" >> conftest.c done echo "int main(void) { $1 x; return 0; }" >> conftest.c if $CC -c $CFLAGS $CPPFLAGS conftest.c > /dev/null 2> /dev/null then eval ac_cv_odbc_$1=yes else eval ac_cv_odbc_$2=no fi rm -f conftest* ]) eval ac_odbc_check_res=$ac_cv_odbc_$1 if test "x$ac_odbc_check_res" = "xyes" then AC_MSG_RESULT(yes) else AC_MSG_RESULT([no ($2)]) AC_DEFINE($1,$2,[Define if $1 is undefined]) fi ]) dnl --------------------------------------------------------- dnl dnl Macro: dnl AC_CHECK_IODBC dnl dnl Arguments: dnl $1=includedir dnl $2=libdir dnl dnl Description: dnl Checks for iODBC. If found configure for it. dnl dnl --------------------------------------------------------- AC_DEFUN([AC_CHECK_IODBC], [ check_iobc_inc_path="$1" check_iobc_lib_path="$2" CPPFLAGS="$CPPFLAGS -I$check_iobc_inc_path" AC_CHECK_HEADERS([isql.h isqlext.h isqltypes.h], [iodbc_ok=yes;odbc_headers="$odbc_headers $ac_hdr"],[iodbc_ok=no; break]) if test "x$iodbc_ok" != "xyes" then AC_MSG_ERROR([Unable to find the iodbc headers in '$check_iobc_inc_path']) fi # new autoconf tools doesn't detect through ac_hdr, so define # odbc_headers manually to make AC_CHECK_ODBC_TYPE to work if test "x$odbc_headers" = "x" then odbc_headers="isql.h isqlext.h isqltypes.h" fi AC_CHECK_HEADERS(iodbcinst.h) if test "x$ac_cv_header_iodbcinst_h" = "xyes" then odbc_headers="$odbc_headers iodbcinst.h" save_LDFLAGS="$LDFLAGS" LDFLAGS="-L$check_iobc_lib_path $LDFLAGS" if test "x$enable_odbcinstlink" = "xyes" ; then AC_CHECK_LIB(iodbcinst,SQLGetPrivateProfileString, [AC_DEFINE(HAVE_SQLGETPRIVATEPROFILESTRING,1,[Define if SQLGetPrivateProfileString is defined]) LIBS="$LIBS -L$check_iobc_lib_path -liodbcinst" ; have_iodbcinst=yes], []) fi LDFLAGS="$save_LDFLAGS" fi ]) dnl --------------------------------------------------------- dnl dnl Macro: dnl AC_CHECK_UNIXODBC dnl dnl Arguments: dnl $1=includedir dnl $2=libdir dnl dnl Description: dnl Check for unixODBC. If found configure for it. dnl dnl --------------------------------------------------------- AC_DEFUN([AC_CHECK_UNIXODBC], [ check_iobc_inc_path="$1" check_iobc_lib_path="$2" CPPFLAGS="$CPPFLAGS -I$check_iobc_inc_path" AC_CHECK_HEADERS([sql.h sqlext.h sqltypes.h], [unixODBC_ok=yes;odbc_headers="$odbc_headers $ac_hdr"],[unixODBC_ok=no; break]) if test "x$unixODBC_ok" != "xyes" then AC_MSG_ERROR([Unable to find the unixODBC headers in '$check_iobc_inc_path']) fi # new autoconf tools doesn't detect through ac_hdr, so define # odbc_headers manually to make AC_CHECK_ODBC_TYPE to work if test "x$odbc_headers" = "x " then odbc_headers="sql.h sqlext.h sqltypes.h" fi AC_CHECK_HEADERS(odbcinst.h) if test "x$ac_cv_header_odbcinst_h" = "xyes" then odbc_headers="$odbc_headers odbcinst.h" save_LDFLAGS="$LDFLAGS" LDFLAGS="-L$check_iobc_lib_path $LDFLAGS" if test "x$enable_odbcinstlink" = "xyes" ; then AC_CHECK_LIB(odbcinst,SQLGetPrivateProfileString, [AC_DEFINE(HAVE_SQLGETPRIVATEPROFILESTRING,1,[Define if SQLGetPrivateProfileString is defined]) LIBS="$LIBS -L$check_iobc_lib_path -lodbcinst" ; have_odbcinst=yes], []) fi LDFLAGS="$save_LDFLAGS" fi ]) mysql-connector-odbc-5.1.10-src/README.debug100644 15766 12 5664 11707541006 17147 0ustar00cteamstaff+-------------------------------------------------------------+ | Connector/ODBC | | Debug | +-------------------------------------------------------------+ INTRODUCTION --------------------------------------------------------------- This brief document describes some methods to debug a problem in the driver - it is hoped to be useful to experienced programmers - particularly driver developers. Debugging the driver can be done in a number of ways but is not usually done by stepping through the source code in debug mode of gdb or an IDE for example. The most common method is to get the driver manager to produce trace information showing the calls being made, the return codes and other useful information. COMPILER DEBUG INFORMATION --------------------------------------------------------------- As usual; the driver can be built with or without compiler generated debugging information. In practice; this is not the most common method to debug problems with the driver. This is independent of any of the trace options listed here. ODBC TRACE --------------------------------------------------------------- If you are using a Driver Manager (DM) - and most people do - then you can turn on ODBC Trace. An ODBC Trace will produce a file which shows all of the ODBC calls being made and some very useful details. ODBC Trace is particularly useful to see the interaction between the application and the DM. In some cases an application may be using software layered on top of ODBC (ADO for example) and in this case ODBC Trace is the best way to see how that intermediate layer is interacting with ODBC. ODBC Trace can be turned on/off using the ODBC Administrator. MS Windows ---------- Invoke the ODBC Administrator from the Start menu; Start -> Control Panel -> Administrative Tools -> ODBC Administrator Use the options on the Trace tab to turn tracing on/off. NOTE: You should close the ODBC Administrator program to ensure that the changes have taken affect - Apply/Ok is not always enough. unixODBC -------- Invoke the ODBCConfig GUI. This can be done from the command-line in a shell. Turn ODBC Trace on/off and close the application. FLAG_LOG_QUERY -------------- This is a DSN config option which can be set to turn on feature to log queries to a file. The location of the trace output is dependent upon the DRIVER_QUERY_LOGFILE const. This option may be presented as "Save Queries to" in the DSN edit. This is meaningless if DBUG_OFF has been set at build-time. DRIVER_QUERY_LOGFILE -------------------- This is a hardcoded file location/name for the query log. WIN : "c:\\myodbc.sql" UNIX: "/tmp/myodbc.sql" SQL_OPT_TRACE ------------- This connection attribute is implemented in the Driver Manager. SQL_OPT_TRACEFILE ----------------- This connection attribute is implemented in the Driver Manager. mysql-connector-odbc-5.1.10-src/Uninstall.bat100644 15766 12 7501 11707541006 17633 0ustar00cteamstaff@ECHO OFF REM ######################################################### REM REM \brief Deregister Connector/ODBC driver registered with REM Install.bat REM REM This exists for those working with the Windows REM source distribution or with installer-less REM binary distribution. REM REM If driver was registerd under non-default name, REM the name used should be specified as first REM parameter of this script. REM REM Note that you should manually remove all data REM sources using a driver before uninstalling that REM driver. REM REM \sa README.win REM REM ######################################################### REM # SETLOCAL prevents the variables set in this script to REM # be exported to the environment and pollute it SETLOCAL SET driver_name=MySQL ODBC 5.1 Driver SET installer=myodbc-installer SET driver_found=no IF "%1" == "" GOTO :doFindInstaller SET driver_name=%1 :doFindInstaller REM # Find the installer utility SET bindir=none FOR %%G IN (. bin bin\release bin\relwithdebinfo bin\debug) DO CALL :subFindInstaller %%G SET myodbc_installer=%bindir%\%installer%.exe IF NOT "%bindir%" == "none" GOTO :doDeregister REM # Try if it is in the path SET myodbc_installer=%installer%.exe "%myodbc_installer%" >nul 2>nul REM # "Command not found" generates error 9009 IF NOT ERRORLEVEL 9000 GOTO :doDeregister GOTO :errorNoInstaller REM ###### REM # A subroutine to check if given location REM # (relative to working dir) contains myodbc REM # installer utility. REM ###### :subFindInstaller REM # Skip check if a good libdir was already found IF NOT "%bindir%" == "none" GOTO :eof SET bindir=%CD%\%1 IF NOT EXIST "%bindir%\%installer%.exe" GOTO :wrongBinDir REM ECHO Bindir (%bindir%) is OK. GOTO :eof :wrongBinDir REM ECHO Bindir (%bindir%) is wrong. SET bindir=none GOTO :eof :doDeregister ECHO "installer = %myodbc_installer%" REM # Check if driver is registered "%myodbc_installer%" -d -l -n "%driver_name%" 2>nul IF ERRORLEVEL 1 GOTO :errorNotRegistered SET driver_found=yes ECHO Deregistering %driver_name% "%myodbc_installer%" -d -r -n "%driver_name%" :doSuccess ECHO ^+-----------------------------------------------------^+ ECHO ^| DONE ^| ECHO ^+-----------------------------------------------------^+ ECHO ^| ^| ECHO ^| Hopefully things went well; the Connector/ODBC ^| ECHO ^| driver has been deregistered. ^| ECHO ^| ^| ECHO ^+-----------------------------------------------------^+ EXIT /B 0 :errorNoInstaller ECHO ^+-----------------------------------------------------^+ ECHO ^| ERROR ^| ECHO ^+-----------------------------------------------------^+ ECHO ^| ^| ECHO ^| Could not find the MyODBC Installer utility. Run ^| ECHO ^| this script from the installation directory. ^| ECHO ^| ^| ECHO ^+-----------------------------------------------------^+ EXIT /B 1 :errorNotRegistered ECHO ^+-----------------------------------------------------^+ ECHO ^| ERROR ^| ECHO ^+-----------------------------------------------------^+ ECHO ^| ^| ECHO ^| Connector/ODBC does not appear to be registered. ^| ECHO ^| Was it registered under non-default name which ^| ECHO ^| then should be specified as the first parameter of ^| ECHO ^| this script? ^| ECHO ^| ^| ECHO ^+-----------------------------------------------------^+ EXIT /B 1 mysql-connector-odbc-5.1.10-src/resource.h100644 15766 12 5561 11707541006 17176 0ustar00cteamstaff///////////////////////////////////////////////////////////////////////////// // Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. // // The MySQL Connector/ODBC is licensed under the terms of the GPLv2 // , like most // MySQL Connectors. There are special exceptions to the terms and // conditions of the GPLv2 as it is applied to this software, see the // FLOSS License Exception // . // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA //{{NO_DEPENDENCIES}} // // 99% not needed - phase file out or purge uneeded stuff // // Used by myodbc3.rc // #define IDOPTIONS 3 #define DRIVERCONNECT 104 #define MYODBCDLG 163 #define MYOPTIONDLG 164 #define MYSQL_LOGO 165 #define IDC_DSNAME 400 #define IDC_DSNAMETEXT 401 #define IDC_SERVER 402 #define IDC_PASSWORDTEXT 403 #define IDC_DESC 404 #define IDC_USER 405 #define IDC_USERTEXT 406 #define IDC_PASSWORD 407 #define IDC_SERVERTEXT 408 #define IDC_DESCRIPTION 409 #define IDS_MSGTITLE 500 #define IDS_BADDSN 501 #define IDC_PORT 1000 #define IDC_STMT 1001 #define IDC_DB 1005 #define IDC_FLAGTEXT 1007 #define IDC_FLAG 1009 #define IDC_PORTTEXT 1010 #define IDC_CHECK1 1024 #define IDC_CHECK2 1025 #define IDC_CHECK3 1026 #define IDC_CHECK4 1027 #define IDC_CHECK5 1028 #define IDC_CHECK6 1029 #define IDC_CHECK7 1030 #define IDC_CHECK8 1031 #define IDC_CHECK9 1032 #define IDC_CHECK10 1033 #define IDC_CHECK11 1034 #define IDC_CHECK12 1035 #define IDC_CHECK13 1036 #define IDC_CHECK14 1037 #define IDC_CHECK15 1038 #define IDC_CHECK16 1039 #define IDC_CHECK17 1040 #define IDC_CHECK18 1041 #define IDC_CHECK19 1042 #define IDC_CHECK20 1043 #define IDC_CHECK21 1044 #define IDC_CHECK22 1045 #define IDTEST 1063 #define IDC_LOGO 1090 #define IDBACK 1100 #define IDC_STATIC -1 // Next default values for new objects // #if defined (APSTUDIO_INVOKED) && !defined(APSTUDIO_READONLY_SYMBOLS) #define _APS_NEXT_RESOURCE_VALUE 167 #define _APS_NEXT_COMMAND_VALUE 40013 #define _APS_NEXT_CONTROL_VALUE 1107 #define _APS_NEXT_SYMED_VALUE 101 #endif /* defined (APSTUDIO_INVOKED) && !defined(APSTUDIO_READONLY_SYMBOLS) */ mysql-connector-odbc-5.1.10-src/INSTALL100644 15766 12 12361 11707541006 16243 0ustar00cteamstaff+-------------------------------------------------------------+ | MySQL Connector/ODBC | | Install | +-------------------------------------------------------------+ INTRODUCTION --------------------------------------------------------------- In this document we explain how to install MySQL Connector/ODBC. This document does not describe platform specific details - see INSTALL. for details on your specific platform. REQUIREMENTS --------------------------------------------------------------- Platforms Binary Distributions * MS Windows 2000 or newer * Solaris 10 or newer * OSX 10.5 or newer * Linux (various flavors) * HPUX 11.31 or newer * Free BSD 7 or newer Source Distributions Connector/ODBC is designed to be portable and can be built for a wide variety of platforms without any changes or, in some cases, with minor changes. ODBC Systems Strictly speaking, the driver can be used without any ODBC system but in practice this is seldom done. Depending upon your system - one of the following is needed and is usually available with your operating system (and is often installed by default). * unixODBC * Apple iODBC * iODBC * Microsoft ODBC DISTRIBUTIONS --------------------------------------------------------------- You will need either a source or binary distribution which is compatible with your platform (machine and operating system). Various types of distributions are available. Some distribution types are not supported on some platforms. See downloads at www.mysql.com. Binary With Installer This is the recommended distribution type. Some platforms have package systems which manage the install and uninstall of software packages. MS Windows - msi or setup.exe Solaris - pkg OSX - pkg Linux - rpm See INSTALL. for details on how to install and uninstall a package and any special considerations for doing so. Binary Without Installer Some platforms do not have a commonly accepted package system or for some other reason we have elected not to support it. In this case we provide a binary without any installer. This may also be useful for those wishing more control over the install process. This is either provided as; MS Windows - zip Linux - source rpm, tar-ball others - tar-ball Source With Installer We provide a source distribution with installer on one platform, linux, as this is common practice on rpm based linux platforms. Source Without Installer We provide source distributions which can be built on a variety of platforms. Source distributions are derived from the source repository but is cleaned up to make using it easier. This type of distributions is as; MS Windows - zip others - tar-ball Source Repository The bazaar source repository is keept on Launchpad and can be accessed using name lp:myodbc. For example: $ bzr branch lp:myodbc myodbc-source-tree INSTALLING --------------------------------------------------------------- Connector/ODBC is installed by doing the following steps; 1. optionally copy the driver to a desired location in the filesystem 2. register the driver with system's ODBC (possibly with provided myodbc-installer utility) 3. define data sources which use the driver (possibly with provided myodbc-installer utility) Binary With Installer Both install steps are done for you when you use a binary distribution which includes an installer. See INSTALL. for details. Binary Without Installer In this case one must first extract the files to a desired location and then manually register the driver with the ODBC system. The provided myodbc-installer utility can be used for that purpose (run it without options to see usage information). Once driver is registered, data sources which use that driver can be defined either using ODBC tools provided with the operating system or again with myodbc-installer. In some cases there may be install/uninstall scripts to aid in this process. See INSTALL. for details. Source In this case one must first build the driver and programs and then install them. The install is the same as 'Binary Without Installer' except there is no need to extract files as they will be in place after a build. RESOURCES --------------------------------------------------------------- For more information about MySQL, see http://www.mysql.com For more information about MySQL Connector/ODBC, including installation instructions, please visit; http://dev.mysql.com/doc/en/connector-odbc.html mysql-connector-odbc-5.1.10-src/Upgrade.bat100644 15766 12 1044 11707541006 17245 0ustar00cteamstaff@ECHO OFF REM ######################################################### REM REM \brief Upgrade an existing install. REM REM Often when testing you want to Uninstall and Install. This REM script does this. REM REM Use this upgrade an existing install. This just REM calls Uninstall/Install so it has nothing to do REM with MS installed software thingy in Control Panel. REM REM \sa README.win REM REM ######################################################### CALL Uninstall.bat %1 CALL Install.bat %1 mysql-connector-odbc-5.1.10-src/COPYING100644 15766 12 43312 11707541006 16245 0ustar00cteamstaff GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free software, and you are welcome to redistribute it under certain conditions; type 'show c' for details. The hypothetical commands 'show w' and 'show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than 'show w' and 'show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program 'Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. mysql-connector-odbc-5.1.10-src/README100644 15766 12 12741 11707541006 16074 0ustar00cteamstaffMySQL Connector/ODBC 5.1 This is a release of MySQL Connector/ODBC (formerly MyODBC), Oracle's dual-license ODBC Driver for MySQL. For the avoidance of doubt, this particular copy of the software is released under the version 2 of the GNU General Public License. MySQL Connector/ODBC is brought to you by Oracle. Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. License information can be found in the COPYING file. MySQL FOSS License Exception We want free and open source software applications under certain licenses to be able to use the GPL-licensed MySQL Connector/ODBC (specified GPL-licensed MySQL client libraries) despite the fact that not all such FOSS licenses are compatible with version 2 of the GNU General Public License. Therefore there are special exceptions to the terms and conditions of the GPLv2 as applied to these client libraries, which are identified and described in more detail in the FOSS License Exception at This distribution may include materials developed by third parties. For license and attribution notices for these materials, please refer to the documentation that accompanies this distribution (see the "Licenses for Third-Party Components" appendix) or view the online documentation at GPLv2 Disclaimer For the avoidance of doubt, except that if any license choice other than GPL or LGPL is available it will apply instead, Oracle elects to use only the General Public License version 2 (GPLv2) at this time for any software where a choice of GPL license versions is made available with the language indicating that GPLv2 or any later version may be used, or where a choice of which version of the GPL is applied is otherwise unspecified. CONTENTS * Introduction * Building Source * Installing * Binary Files * Connector/ODBC SDK * Resources INTRODUCTION ------------------------------------------------------------ This is the source or binary distribution of ODBC for MySQL. This software is distributed with restrictions - please see the license information for details. Open Database Connectivity (ODBC) is a widely accepted standard for reading/writing data. ODBC is designed in a similar manner to the MS Windows printing system in that it has a Driver Manager and Drivers. MySQL Connector/ODBC is a driver for the ODBC system which allows applications to communicate with the MySQL Server using the ODBC standard. ODBC implementations exist on all popular platforms and MySQL Connector/ODBC is also available on those platforms. Oracle provides MySQL support for ODBC by means of the MySQL Connector/ODBC driver and programs. MySQL Connector/ODBC is the ODBC driver for MySQL which is ODBC 3.5x compliant; MyODBC is ODBC 2.5x compliant. To get all functionality from the ODBC 5.1 driver (e.g. transaction support) you should use it against MySQL Database Server 4.1 or later. BUILDING SOURCE --------------------------------------------------------------- Please see the BUILD file for details on building the source code. This is included with the source distribution. INSTALLING --------------------------------------------------------------- Please see the INSTALL file for details on installing. BINARY FILES --------------------------------------------------------------- The following, key, files are provided by MySQL Connector/ODBC (the MS Windows names are included for reference but the file names for other platforms would be similar). Driver (myodbc3.dll or myodbc5.dll) The most important part of Connector/ODBC is the driver. Application developers can link directly to the driver but more often use the driver indirectly, via a Driver Manager such as the one provided by Microsoft or unixODBC. Many options can be provided to the driver to adjust its behavior. Setup Library (myodbc3S.dll or myodbc5S.dll) The setup library provides a graphical user interface needed during certain activities provided by the driver. For example; it provides the GUI when you use the ODBC Administrator to create/edit a Data Source Name (DSN). Installer (myodbc3i.exe or myodbc-installer.exe) This command-line utility program can be used to manage ODBC system information. For example it can be used to register or deregister a driver. It can also manage data source names. This was created to test the ODBC system information abstraction and portability layer within Connector/ODBC but is also useful for installing and uninstalling Connector/ODBC. Invoke myodbc3i, in a command shell and without options, to get help on its use. Library Loading Tester (dltest.exe) This command-line utility is useful for debugging problems related to loading a library and resolving symbols in it. Connector/ODBC SDK --------------------------------------------------------------- MySQL Connector/ODBC provides an SDK for developers in the form of several reusable libraries. These libraries can be used to save development time/effort and to increase stability of resulting software. myodbc3u This library provides the ODBC system information abstraction and portability used by Connector/ODBC. RESOURCES --------------------------------------------------------------- For more information about MySQL, see http://www.mysql.com For more information about MySQL Connector/ODBC, including installation instructions, please visit; http://www.mysql.com/products/myodbc/index.html mysql-connector-odbc-5.1.10-src/INSTALL.win100644 15766 12 10301 11707541006 17027 0ustar00cteamstaff+-------------------------------------------------------------+ | MySQL Connector/ODBC | | Install on MS Windows | +-------------------------------------------------------------+ INTRODUCTION --------------------------------------------------------------- In this document we explain how to install MySQL Connector/ODBC on MS Windows. Please read INSTALL if you have not done so already. DISTRIBUTIONS --------------------------------------------------------------- The following distributions are available for MS Windows; MSI This is a typical installer type for MS Windows which will ensure that the files are copied properly and that the driver is registered with the ODBC system. This will work on any recent version of MS Windows and is the preferred method to install Connector/ODBC on MS Windows. Simply double-click the msi file and follow installer instructions. ZIP (with installer) This is an older style installer which comes in the form of an exe called setup.exe. This is zipped to reduce its size and to prevent it from being accidently executed upon download or to otherwise trip firewall and antivirus software. This may work on some older MS Windows when the MSI method is not supported. Extract the setup.exe from the zip file and double-click it to invoke the installer. ZIP (no installer) In this case you want to do the following; 1) Extract the files to the desired location using pkzip or some compatible program. 2) Register driver by running Install.bat script from the location where drivers were unziped. 3) Alternatively, you can register drivers manually using the provided myodbc-installer utility. Run it without arguments to see usage instructions. NOTE: Do not try to edit the registery or odbc ini files manually to register the driver unless you are intimate with how to do this properly. myodbc3i has been created for this purpose. ZIP (source) In this case you will want to do the following; 1) Extract the files (source code). 2) Build the source (see BUILD file for details). 2) Copy the files to the desired location and register them as described above. NOTE: If you need to reinstall and you are using Install.bat then please consider using Uninstall.bat first. This will ensure that the usage counter is not incremented beyond the 1 you probably want. POST INSTALL --------------------------------------------------------------- Verify Driver Registered Find the ODBC Administrator program in the Start -> Control Panel and execute it. Go to the Drivers tab and note that it has the MySQL ODBC driver listed - this means that the driver is registered with the ODBC system. Alternatively you can run > myodbc-installer -d -l and see if the driver is listed. Run > myodbc-installer -d -l -n "" to see deatailed information about the driver. Create A Data Source Name Data sources can be created with the myodbc-installer utility. Run it without arguments to see usage instructions and examples. Alternatively, you can define a data source with Windows 'ODBC Administrator' appliocation. Go to the 'User DSN' tab of the 'ODBC Administrator' and create a new DSN. During this request you will use a MySQL Connector/ODBC DSN window to edit the connection information. This window has a 'Test' button which allows you to Test your settings. Save the DSN and exit the ODBC Administrator. You are now ready to use MySQL Connector/ODBC. Note: Prior to uninstalling a driver all data sources which use that driver should be removed. It is not possible to remove a data source after its driver has been uninstalled.mysql-connector-odbc-5.1.10-src/MYODBC_CONF.h100644 15766 12 2447 11707541006 17131 0ustar00cteamstaff/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MYODBC_CONF_H # define MYODBC_CONF_H #include "VersionInfo.h" # ifdef HAVE_CONFIG_H # include "driver/myconf.h" /* Work around iODBC header bug on Mac OS X 10.3 */ # undef HAVE_CONFIG_H # endif #endif mysql-connector-odbc-5.1.10-src/mysql.bmp100644 15766 12 373066 11707541006 17113 0ustar00cteamstaffBM6ö6(€ÿÿÿ   ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿÒÙÿlÿlÿlÿ›|ÿÒÙÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÝÄÿ›|ÿ”sÿ ƒ+ÿ”sÿlÿlÿ”sÿÒÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÙÿlÿðyÿÿÿÿÿõñèÿðyÿ ƒ+ÿlÿlÿ±™PÿøöïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÝÄÿ”sÿ±™Pÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿðyÿlÿlÿlÿlÿ”sÿ¦Š8ÿðyÿåÝÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿ±™Pÿlÿȸ…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÝÄÿ¦Š8ÿ›|ÿlÿlÿlÿlÿlÿ ƒ+ÿåÝÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÝÄÿ”sÿ›|ÿÛÏ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿàÖºÿ±™Pÿlÿlÿ”sÿ»¦hÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÏ­ÿlÿ¦Š8ÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíè×ÿ»¦hÿlÿlÿ ƒ+ÿÛÏ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿȸ…ÿlÿ»¦hÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿàÖºÿÛÏ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿàÖºÿ ƒ+ÿlÿ”sÿðyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿ¦Š8ÿ›|ÿåÝÄÿÿÿÿÿÿÿÿÿøöïÿðyÿlÿðyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿ»¦hÿlÿ”sÿ±™PÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÙÿlÿ»¦hÿÿÿÿÿÿÿÿÿÿÿÿÿíè×ÿ ƒ+ÿ¦Š8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÛÏ­ÿlÿlÿ¦Š8ÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÝÄÿ›|ÿ¦Š8ÿûùõÿÿÿÿÿÿÿÿÿýýüÿàÖºÿȸ…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÛÏ­ÿlÿlÿ¦Š8ÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿ»¦hÿ”sÿåÝÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÛÏ­ÿlÿlÿ±™PÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÙÿlÿðyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÛÏ­ÿlÿlÿ»¦hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíè×ÿ ƒ+ÿ¦Š8ÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿȸ…ÿlÿ”sÿÒÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿðyÿlÿȸ…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿ»¦hÿlÿ›|ÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÝÄÿ”sÿ ƒ+ÿòíáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿ›|ÿlÿ±™Pÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿȸ…ÿlÿ±™PÿøöïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÒÙÿlÿ”sÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿ±™Pÿ”sÿÛÏ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿ±™Pÿlÿ¦Š8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿ»¦hÿ”sÿÛÏ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿàÖºÿlÿ›|ÿåÝÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíè×ÿ ƒ+ÿ ƒ+ÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿ›|ÿlÿ»¦hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÖºÿlÿðyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿȸ…ÿlÿ›|ÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÙÿlÿÛÏ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿ”sÿ”sÿðyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿðyÿ›|ÿíè×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿȸ…ÿlÿ ƒ+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿ±™Pÿ ƒ+ÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿlÿ”sÿÒÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿ¦Š8ÿ ƒ+ÿõñèÿÿÿÿÿÿÿÿÿõñèÿȸ…ÿÒÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿ»¦hÿlÿ ƒ+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿ±™Pÿ ƒ+ÿõñèÿÿÿÿÿýýüÿÒÙÿlÿ±™Pÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíè×ÿlÿ”sÿÒÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿðyÿ”sÿåÝÄÿÿÿÿÿíè×ÿ¦Š8ÿlÿ ƒ+ÿåÝÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿ±™Pÿlÿ ƒ+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÙÿlÿÒÙÿÿÿÿÿåÝÄÿlÿlÿ”sÿðyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÝÄÿlÿ”sÿ»¦hÿøöïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÖºÿlÿ±™PÿÿÿÿÿÛÏ­ÿlÿlÿ›|ÿ ƒ+ÿòíáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿ»¦hÿlÿlÿ”sÿ»¦hÿíè×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíè×ÿ¦Š8ÿ ƒ+ÿåÝÄÿÒÙÿlÿ±™PÿàÖºÿlÿ±™PÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿÒÙÿ ƒ+ÿlÿlÿlÿ±™PÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÙÿlÿ¦Š8ÿ»¦hÿlÿ±™Pÿûùõÿ¦Š8ÿ”sÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿõñèÿȸ…ÿ ƒ+ÿlÿ”sÿÒÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿ»¦hÿlÿlÿ”sÿȸ…ÿÿÿÿÿåÝÄÿ”sÿ¦Š8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÛÏ­ÿlÿlÿ±™Pÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿðyÿ›|ÿ»¦hÿõñèÿÿÿÿÿÿÿÿÿÒÙÿ”sÿðyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿ±™Pÿlÿ ƒ+ÿíè×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÛÏ­ÿ›|ÿåÝÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÒÙÿ”sÿ›|ÿÛÏ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÝÄÿ±™PÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿàÖºÿÛÏ­ÿ”sÿ”sÿíè×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿðyÿòíáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿÒÙÿ±™Pÿ”sÿlÿlÿlÿlÿlÿ±™PÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿÒÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿ›|ÿlÿlÿlÿ›|ÿ±™Pÿȸ…ÿȸ…ÿíè×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿðyÿlÿ”sÿðyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿ ƒ+ÿlÿ”sÿÒÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿðyÿ¦Š8ÿlÿlÿ”sÿ»¦hÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÝÄÿ¦Š8ÿlÿlÿlÿ±™PÿàÖºÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿ©Ð÷ÿºòÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð÷þÿ©Ð÷ÿºòÿk¯ðÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿR¢ìÿºòÿŽÂôÿÓçüÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÃßúÿ7•éÿR¢ìÿR¢ìÿk¯ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿ ƒ+ÿlÿlÿ¦Š8ÿåÝÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦Š8ÿlÿlÿlÿlÿlÿlÿ±™PÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÖºÿ›|ÿlÿlÿlÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð÷þÿk¯ðÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ&Œæÿ©Ð÷ÿÿÿÿÿÿÿÿÿð÷þÿŽÂôÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ&Œæÿ©Ð÷ÿÿÿÿÿÿÿÿÿð÷þÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿ»¦hÿlÿlÿlÿÒÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿlÿlÿlÿlÿÛÏ­ÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦Š8ÿlÿlÿlÿlÿlÿlÿlÿ»¦hÿøöïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿk¯ðÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ&ŒæÿŽÂôÿÿÿÿÿð÷þÿºòÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ&ŒæÿŽÂôÿÿÿÿÿýýüÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿàÖºÿ¦Š8ÿlÿlÿ»¦hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿlÿlÿlÿlÿ±™PÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÖºÿ›|ÿlÿlÿlÿlÿlÿlÿlÿ±™Pÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ7•éÿ‡åÿ‡åÿ‡åÿR¢ìÿ©Ð÷ÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿÓçüÿæñüÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿR¢ìÿ©Ð÷ÿÓçüÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿºòÿ&Œæÿ‡åÿ‡åÿ‡åÿ7•éÿð÷þÿýýüÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíè×ÿ»¦hÿlÿȸ…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿ¦Š8ÿ”sÿlÿlÿlÿíè×ÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦Š8ÿlÿlÿlÿ ƒ+ÿ”sÿlÿlÿlÿ»¦hÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ&Œæÿ‡åÿ‡åÿ7•éÿÓçüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ&ŒæÿŽÂôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓçüÿ‡åÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÛÏ­ÿ”sÿÛÏ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿÛÏ­ÿ”sÿlÿlÿlÿ»¦hÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíè×ÿ›|ÿlÿlÿlÿÒÙÿ›|ÿlÿlÿlÿ±™Pÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ&Œæÿ‡åÿ‡åÿR¢ìÿÓçüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿR¢ìÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿ ƒ+ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿ»¦hÿlÿlÿlÿlÿòíáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ”sÿøöïÿ¦Š8ÿlÿlÿlÿ»¦hÿõñèÿÿÿÿÿÛÏ­ÿ›|ÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ»¦hÿûùõÿÿÿÿÿ©Ð÷ÿ&Œæÿ‡åÿ‡åÿR¢ìÿÓçüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿk¯ðÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿõñèÿ”sÿlÿlÿlÿÒÙÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿ›|ÿlÿlÿlÿ»¦hÿÿÿÿÿ¦Š8ÿlÿlÿlÿ±™PÿõñèÿÿÿÿÿÛÏ­ÿ›|ÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ»¦hÿûùõÿÿÿÿÿ©Ð÷ÿ&Œæÿ‡åÿ‡åÿ‡åÿ©Ð÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿk¯ðÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿÿÿÿÿ»¦hÿlÿlÿlÿ”sÿøöïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðyÿ”sÿlÿlÿlÿåÝÄÿÿÿÿÿ ƒ+ÿlÿlÿlÿ»¦hÿõñèÿÿÿÿÿÛÏ­ÿ›|ÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ»¦hÿýýüÿÿÿÿÿÃßúÿ7•éÿ‡åÿ‡åÿ‡åÿ&ŒæÿR¢ìÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿÓçüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿk¯ðÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿÿÿÿÿåÝÄÿ”sÿlÿlÿlÿÛÏ­ÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿûùõÿ›|ÿlÿlÿlÿ±™Pÿøöïÿÿÿÿÿ ƒ+ÿlÿlÿlÿ±™PÿõñèÿÿÿÿÿÛÏ­ÿ›|ÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ»¦hÿûùõÿÿÿÿÿÿÿÿÿÃßúÿ7•éÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿR¢ìÿ©Ð÷ÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿk¯ðÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿýýüÿÿÿÿÿ¦Š8ÿlÿlÿlÿ¦Š8ÿøöïÿÿÿÿÿÿÿÿÿÿÿÿÿÒÙÿ”sÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ»¦hÿõñèÿÿÿÿÿÛÏ­ÿ›|ÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ»¦hÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿŽÂôÿR¢ìÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ&ŒæÿŽÂôÿÿÿÿÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿk¯ðÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿýýüÿÿÿÿÿÛÏ­ÿ”sÿlÿlÿlÿåÝÄÿÿÿÿÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ±™Pÿõñèÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ±™PÿõñèÿÿÿÿÿÛÏ­ÿ›|ÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ»¦hÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓçüÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿÃßúÿŽÂôÿ7•éÿ‡åÿ‡åÿ‡åÿR¢ìÿÓçüÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿk¯ðÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿýýüÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ±™PÿûùõÿÿÿÿÿàÖºÿ”sÿlÿlÿlÿÒÙÿýýüÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ»¦hÿõñèÿÿÿÿÿÛÏ­ÿ›|ÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ»¦hÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿk¯ðÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿýýüÿÿÿÿÿÿÿÿÿÒÙÿ”sÿlÿlÿlÿíè×ÿÿÿÿÿ¦Š8ÿlÿlÿlÿ ƒ+ÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ±™PÿõñèÿÿÿÿÿÛÏ­ÿ›|ÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ»¦hÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃßúÿ&Œæÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿð÷þÿÃßúÿºòÿŽÂôÿŽÂôÿŽÂôÿÃßúÿÿÿÿÿÿÿÿÿæñüÿk¯ðÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿk¯ðÿk¯ðÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿýýüÿÿÿÿÿÿÿÿÿýýüÿ›|ÿlÿlÿlÿȸ…ÿàÖºÿ›|ÿlÿlÿlÿÒÙÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ»¦hÿõñèÿÿÿÿÿÛÏ­ÿ›|ÿlÿlÿlÿàÖºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ»¦hÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃßúÿ&Œæÿ‡åÿ‡åÿ7•éÿÓçüÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿÿÿÿÿæñüÿR¢ìÿ‡åÿ‡åÿ‡åÿ‡åÿºòÿÿÿÿÿæñüÿR¢ìÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿºòÿ&Œæÿºòÿºòÿ&ŒæÿºòÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿȸ…ÿ”sÿlÿlÿ›|ÿ±™Pÿlÿlÿlÿ”sÿòíáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ±™PÿõñèÿÿÿÿÿÛÏ­ÿ›|ÿlÿlÿlÿÛÏ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿlÿlÿlÿ»¦hÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿºòÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ‡åÿk¯ðÿÿÿÿÿÿÿÿÿÿÿÿÿð÷þÿºòÿ‡åÿ‡åÿ‡åÿ‡åÿºòÿÓçüÿ&Œæÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓçüÿ&ŒæÿºòÿR¢ìÿR¢ìÿºòÿ&ŒæÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿ”sÿlÿlÿlÿlÿlÿlÿlÿȸ…ÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ»¦hÿõñèÿÿÿÿÿÿÿÿÿ¦Š8ÿlÿlÿlÿ”sÿ»¦hÿȸ…ÿȸ…ÿȸ…ÿȸ…ÿȸ…ÿȸ…ÿȸ…ÿȸ…ÿȸ…ÿ¦Š8ÿlÿlÿlÿ»¦hÿýýüÿÿÿÿÿÓçüÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿºòÿR¢ìÿ‡åÿ‡åÿ‡åÿ‡åÿR¢ìÿæñüÿÿÿÿÿÃßúÿ&Œæÿ‡åÿ‡åÿ‡åÿ‡åÿR¢ìÿŽÂôÿŽÂôÿŽÂôÿºòÿ7•éÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ7•éÿð÷þÿÿÿÿÿæñüÿ‡åÿ‡åÿ‡åÿ‡åÿ&ŒæÿR¢ìÿºòÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿæñüÿÃßúÿR¢ìÿŽÂôÿ&Œæÿ&ŒæÿÃßúÿR¢ìÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»¦hÿ”sÿlÿlÿlÿlÿlÿlÿíè×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ±™PÿõñèÿÿÿÿÿÿÿÿÿåÝÄÿ ƒ+ÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿ»¦hÿûùõÿÿÿÿÿ©Ð÷ÿ&Œæÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ7•éÿ©Ð÷ÿÿÿÿÿÿÿÿÿÿÿÿÿŽÂôÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ7•éÿŽÂôÿÿÿÿÿÿÿÿÿÿÿÿÿ©Ð÷ÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ&ŒæÿÓçüÿð÷þÿ&ŒæÿºòÿŽÂôÿ7•éÿÃßúÿ&ŒæÿÿÿÿÿÿÿÿÿÿÿÿÿlÿlÿlÿlÿàÖºÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøöïÿ ƒ+ÿlÿlÿlÿlÿlÿÒÙÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ƒ+ÿlÿlÿlÿ»¦hÿõñèÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿȸ…ÿ ƒ+ÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿ»¦hÿýýüÿÿÿÿÿ©Ð÷ÿ&Œæÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ&Œæÿºòÿæñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿ©Ð÷ÿ7•éÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ&Œæÿ©Ð÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓçüÿR¢ìÿ&Œæÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ‡åÿ&ŒæÿÓçüÿýýüÿºòÿ&Œæÿ©Ð÷ÿŽÂôÿ‡åÿºòÿÿÿÿÿÿÿÿÿÿÿÿÿȸ…ÿȸ…ÿȸ…ÿðyÿòíáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÒÙÿ±™Pÿ¦Š8ÿðyÿàÖºÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÙÿðyÿȸ…ÿðyÿÛÏ­ÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿåÝÄÿàÖºÿåÝÄÿåÝÄÿåÝÄÿåÝÄÿåÝÄÿåÝÄÿåÝÄÿíè×ÿ±™Pÿlÿlÿlÿ»¦hÿûùõÿÿÿÿÿÓçüÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿ©Ð÷ÿÓçüÿð÷þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæñüÿ©Ð÷ÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿ©Ð÷ÿ©Ð÷ÿ&Œæÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýüÿÓçüÿÃßúÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿŽÂôÿæñüÿÿÿÿÿýýüÿ©Ð÷ÿk¯ðÿºòÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±™Pÿlÿlÿlÿ»¦hÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃßúÿ&Œæÿ‡åÿ‡åÿ‡åÿ7•éÿÃßúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÙÿ›|ÿlÿlÿlÿðyÿýýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓçüÿ7•éÿ‡åÿ‡åÿ‡åÿ&Œæÿ©Ð÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿ»¦hÿ¦Š8ÿ±™Pÿ¦Š8ÿ±™Pÿ¦Š8ÿ±™Pÿ¦Š8ÿ±™Pÿ¦Š8ÿ±™Pÿ¦Š8ÿ¦Š8ÿ”sÿlÿlÿlÿlÿ”sÿÛÏ­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð÷þÿÃßúÿÃßúÿÃßúÿÃßúÿÓçüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõñèÿ ƒ+ÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿ”sÿÒÙÿûùõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòíáÿ ƒ+ÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿlÿ ƒ+ÿȸ…ÿòíáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmysql-connector-odbc-5.1.10-src/MYODBC_MYSQL.h100644 15766 12 5065 11707541006 17310 0ustar00cteamstaff/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MYODBC_MYSQL_H #define MYODBC_MYSQL_H #define DONT_DEFINE_VOID #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define MIN_MYSQL_VERSION 40100L #if MYSQL_VERSION_ID < MIN_MYSQL_VERSION # error "Connector/ODBC requires v4.1 (or later) of the MySQL client library" #endif #ifdef THREAD #include #else # ifdef pthread_mutex_lock # undef pthread_mutex_lock # undef pthread_mutex_unlock # undef pthread_mutex_init # undef pthread_mutex_destroy # undef pthread_mutex_trylock # endif #define pthread_mutex_lock(A) #define pthread_mutex_unlock(A) #define pthread_mutex_init(A,B) #define pthread_mutex_destroy(A) #define pthread_mutex_trylock(A) (0) #endif /* Get rid of defines from my_config.h that conflict with our myconf.h */ #ifdef VERSION # undef VERSION #endif #ifdef PACKAGE # undef PACKAGE #endif /* It doesn't matter to us what SIZEOF_LONG means to MySQL's headers, but its value matters a great deal to unixODBC, which calculates it differently. This causes problems where an application linked against unixODBC thinks SIZEOF_LONG == 4, and the driver was compiled thinking SIZEOF_LONG == 8, such as on Solaris x86_64 using Sun C 5.8. This stems from unixODBC's use of silly platform macros to guess SIZEOF_LONG instead of just using sizeof(long). */ #ifdef SIZEOF_LONG # undef SIZEOF_LONG #endif #ifdef __cplusplus } #endif #endif mysql-connector-odbc-5.1.10-src/VersionInfo.h100644 15766 12 2714 11707541006 17605 0ustar00cteamstaff/* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/ODBC is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception . This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* When changing, also change configure.in and driver/driver.def */ #define SETUP_VERSION "5.01.0010" #define DRIVER_VERSION "0" SETUP_VERSION #define MYODBC_VERSION SETUP_VERSION #define MYODBC_FILEVER 5,1,10,0 #define MYODBC_PRODUCTVER MYODBC_FILEVER #define MYODBC_STRFILEVER "5, 1, 10, 0\0" #define MYODBC_STRPRODUCTVER MYODBC_STRFILEVER mysql-connector-odbc-5.1.10-src/configure.in100644 15766 12 61343 11707541006 17527 0ustar00cteamstaff# Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA AC_INIT(driver/driver.c) AC_PREREQ(2.12)dnl Minimum Autoconf version required. AC_CANONICAL_SYSTEM # When changing, also change VersionInfo.h and driver/driver.def AM_INIT_AUTOMAKE(mysql-connector-odbc, 5.1.10) # Version but without "-alpha", "-beta", i.e. numeric part NUMERIC_VERSION=`echo "$VERSION" | sed 's/-.*//'` ################################################################### # # # See the libtool docs for information on how to do shared lib # # versions. # # # ################################################################### SHARED_LIB_VERSION=1:0:0 AC_CONFIG_HEADERS(driver/myconf.h) AC_SUBST(NUMERIC_VERSION) # Canonicalize the configuration name. SYSTEM_TYPE="$host_vendor-$host_os" MACHINE_TYPE="$host_cpu" AC_SUBST(SYSTEM_TYPE) AC_SUBST(MACHINE_TYPE) dnl Checks for programs. AC_PROG_CC AC_PROG_CPP # Fix for sgi gcc / sgiCC which tries to emulate gcc if test "$CC" = "sgicc" then ac_cv_prog_gcc="no" fi # Only build shared libraries by default AM_ENABLE_SHARED AM_DISABLE_STATIC # define _UNIX_, always true if running this configure script AC_DEFINE(_UNIX_,1,[Define if we are using unix build environment]) AM_PROG_LIBTOOL AC_CHECK_LIB(z,compress) ################################################################### # # Dynamic loading, only "libdl" is supported # ################################################################### LT_LIB_DLLOAD DL_LIB="$LIBADD_DLOPEN" AC_SUBST(DL_LIB) ################################################################### # # --enable-myodbc-installer # ################################################################### AC_ARG_ENABLE( myodbc-installer, [AC_HELP_STRING([--enable-myodbc-installer],[Build myodbc installer command-line interface [default=yes]])], [ case "${enableval}" in yes) myodbc_installer=true ;; no) myodbc_installer=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-myodbc-installer) ;; esac],[myodbc_installer=true]) AM_CONDITIONAL(MYODBCINST, test "x$myodbc_installer" = "xtrue" ) AC_SUBST(CC) AC_SUBST(CFLAGS) AC_SUBST(LD) AC_SUBST(INSTALL_SCRIPT) export CC CFLAGS LD LDFLAGS ################################################################### # # # Check if localtime_r exists in libc or not # # # ################################################################### AC_CHECK_FUNCS(localtime_r) ################################################################### # # # Check for the client libraries and compile options # # Options are taken from the output of mysql_config # # # ################################################################### AC_ARG_WITH(mysql-path, [ --with-mysql-path=DIR Path where MySQL is installed], mysql_path=$withval, mysql_path="") mysql_config="" building_against_source="" MYSQL_PATH_ARG="" if test "$mysql_path" != "" then MYSQL_PATH_ARG="--with-mysql-path=$mysql_path" if test -f $mysql_path/libmysql_r/libmysqlclient_r.la then building_against_source="true" CFLAGS="$CFLAGS -I$mysql_path/include" MYSQL_LIB="$mysql_path/libmysql_r/libmysqlclient_r.la" else mysql_config="$mysql_path/bin/mysql_config" if test ! -x $mysql_config then AC_MSG_ERROR([File $mysql_config doesn't exists or isn't executable. Please specify with --with-mysql-path where bin/mysql_config can be found]); fi fi else # Search where mysql_config is installed TEST_PATHS="/opt/mysql/mysql /usr/local/mysql /usr" for i in $TEST_PATHS do mysql_config="$i/bin/mysql_config" if test -x $mysql_config then MYSQL_PATH_ARG="--with-mysql-path=$TEST_PATHS" break; fi mysql_config="" done if test "$mysql_config" = "" then AC_MSG_ERROR([Can't find mysql_config in $TEST_PATHS]); fi fi AC_SUBST(MYSQL_PATH_ARG) if test "x$building_against_source" = x then OPT=`$mysql_config --cflags` if test $? != "0" then AC_MSG_ERROR([Could not execute $mysql_config. Please check your installation]) fi # We have to remove any ' around paths as this confuses configure OPT=`echo $OPT | sed -e "s;';;g"` CFLAGS="$OPT $CFLAGS" OPT=`$mysql_config --libs_r` if test $? != "0" then # mysql_config that is before 4.0.17 # In this case assume we can use the same compile options for the # thread safe library as for the normal OPT=`$mysql_config --libs | sed -e 's;lmysqlclient;lmysqlclient_r;'` else OPT=`echo $OPT | sed -e "s;';;g"` fi MYSQL_LIB="$OPT" SAVE_LIBS=$LIBS LIBS="$LIBS $MYSQL_LIB" AC_CHECK_FUNC(mysql_real_query,[], [ AC_MSG_ERROR([Could not use the single thread MySQL client library, please check config.log for errors]) ]) LIBS=$SAVE_LIBS fi AC_SUBST(MYSQL_LIB) ################################################################### # # # Link driver against odbcinst library? # # If no, this builds the "dmless" driver. # # # ################################################################### AC_MSG_CHECKING([if driver should be linked against odbcinst library]) AC_ARG_ENABLE(odbcinstlink, [AC_HELP_STRING([--enable-odbcinstlink],[Enable linking of driver against odbcinst library [default=yes]])], [ case "${enableval}" in yes) enable_odbcinstlink=yes ;; no) enable_odbcinstlink=no CFLAGS="$CFLAGS -DNO_DRIVERMANAGER" ;; *) AC_MSG_ERROR(bad value '${enableval}' for --enable-odbcinstlink);; esac ], [enable_odbcinstlink=yes]) AC_MSG_RESULT([$enable_odbcinstlink]) AM_CONDITIONAL(ODBCINSTLINK, [test "x$enable_odbcinstlink" = "xyes"]) dm_type= dm_prefix="/usr" dnl Default to /usr iodbc_conf= AC_ARG_WITH(iODBC, [ --with-iODBC[=DIR] Use iODBC located in DIR], [ dm_type="iODBC" if test "x$withval" != "xyes"; then dm_prefix="$withval" fi ]) AC_ARG_WITH(unixODBC, [ --with-unixODBC[=DIR] Use unixODBC located in DIR], [ dm_type="unixODBC" if test "x$withval" != "xyes"; then dm_prefix="$withval" fi ]) if test "x$dm_type" = "x" then AC_PATH_PROGS(iodbc_conf, iodbc-config, no, [$dm_prefix/bin:$PATH]) if test "x$iodbc_conf" = "xno" then AC_MSG_CHECKING(for SQL_ATTR_UNIXODBC_VERSION in sqlext.h) AC_PREPROC_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[printf("%d", SQL_ATTR_UNIXODBC_VERSION)]])], [ dm_type="unixODBC" AC_MSG_RESULT(found) ], [AC_MSG_RESULT(not found)]) else dm_type="iODBC" fi fi if test "x$dm_type" = "x" then AC_MSG_ERROR(no suitable driver manager selected or found) fi if test "$dm_type" = "iODBC" then AC_DEFINE([USE_IODBC], [1], [use iODBC]) if test "x$iodbc_conf" = "x" then AC_PATH_PROGS(iodbc_conf, iodbc-config, no, [$dm_prefix/bin:$PATH]) fi AC_ARG_WITH(iodbc-includes, [ --with-iodbc-includes=DIR Find iODBC headers in DIR], iodbc_includes="$withval", iodbc_includes="$dm_prefix/include") AC_ARG_WITH(iodbc-libs, [ --with-iodbc-libs=DIR Find iODBC libraries in DIR], iodbc_libs="$withval", iodbc_libs="$dm_prefix/lib") AC_MSG_CHECKING([for iODBC version]) if test "$iodbc_conf" != "no" then iodbc_version=`$iodbc_conf --version` iodbc_cflags=`$iodbc_conf --cflags` if test "x$iodbc_cflags" != "x"; then CFLAGS="$CFLAGS $iodbc_cflags" fi ODBC_DM_LIB=`$iodbc_conf --libs` else iodbc_version="unknown" ODBC_DM_LIB="-liodbc" fi AC_MSG_RESULT([$iodbc_version]) AC_SUBST(ODBC_DM_LIB) AC_CHECK_IODBC($iodbc_includes, $iodbc_libs) AC_ARG_WITH(odbc-ini, [ --with-odbc-ini=PATH Location of system ODBC.INI [IODBCDIR/etc/odbc.ini]], odbc_ini="$withval", odbc_ini="$dm_prefix/etc/odbc.ini") if test "x$have_iodbcinst" != "xyes" then AC_DEFINE_UNQUOTED(SYSTEM_ODBC_INI,"$odbc_ini", [Define path to system ODBC.INI file]) fi ODBC_DM_PATH_ARG="--with-iodbc=$dm_prefix" AC_SUBST(ODBC_DM_PATH_ARG) else # unixODBC AC_DEFINE([USE_UNIXODBC], [1], [use unixODBC]) AC_MSG_CHECKING([for unixODBC version]) AC_PATH_PROGS(isql, isql, no, [$dm_prefix/bin:$PATH]) if test "$isql" != "no" then unixodbc_version=`$isql --version` else unixodbc_version="unknown" fi AC_MSG_RESULT([$unixodbc_version]) AC_ARG_WITH(unixODBC-includes, [ --with-unixODBC-includes=DIR Find unixODBC headers in DIR], unixODBC_includes="$withval", unixODBC_includes="$dm_prefix/include") AC_ARG_WITH(unixODBC-libs, [ --with-unixODBC-libs=DIR Find unixODBC libraries in DIR], unixODBC_libs="$withval", unixODBC_libs="$dm_prefix/lib") AC_CHECK_UNIXODBC($unixODBC_includes, $unixODBC_libs) AC_ARG_WITH(odbc-ini, [ --with-odbc-ini=PATH Location of system ODBC.INI [UnixODBCDIR/etc/odbc.ini]], odbc_ini="$withval", odbc_ini="$unixODBC/etc/odbc.ini") if test "x$have_odbcinst" != "xyes" then AC_DEFINE_UNQUOTED(SYSTEM_ODBC_INI,"$odbc_ini", [Define path to system ODBC.INI file]) fi ODBC_DM_PATH_ARG="--with-unixODBC=$dm_prefix" AC_SUBST(ODBC_DM_PATH_ARG) ODBC_DM_LIB="-lodbc" AC_SUBST(ODBC_DM_LIB) fi ################################################################### # if we're working in an older odbc environment, # # we don't have the SQL* types, so we need to fall # # back to the old versions # ################################################################### AC_CHECK_ODBC_TYPE(SQLHENV,HENV) AC_CHECK_ODBC_TYPE(SQLHDBC,HDBC) AC_CHECK_ODBC_TYPE(SQLHSTMT,HSTMT) AC_CHECK_ODBC_TYPE(SQLINTEGER,SDWORD) AC_CHECK_ODBC_TYPE(SQLUINTEGER,UDWORD) AC_CHECK_ODBC_TYPE(SQLSMALLINT,SWORD) AC_CHECK_ODBC_TYPE(SQLUSMALLINT,UWORD) AC_CHECK_ODBC_TYPE(SQLPOINTER,PTR) AC_CHECK_ODBC_TYPE(SQLHWND,HWND) AC_CHECK_ODBC_TYPE(SQLRETURN,RETCODE) AC_CHECK_ODBC_TYPE(SQLCHAR,UCHAR) ################################################################### # # # Check and validate for odbc.ini # # # ################################################################### if test "x$iodbc_ok" = "xyes" && test "x$have_iodbcinst" != "xyes" then cat < #include SQLRETURN SQL_API SQLParamOptions( SQLHSTMT hstmt, SQLULEN crow, SQLULEN *pirow ) { return 1; } ]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE(USE_SQLPARAMOPTIONS_SQLULEN_PTR, 1, [Define if SQLParamOptions() 2nd and 3rd arg is compatible with SQLULEN]) ], [ AC_MSG_RESULT([no]) AC_MSG_CHECKING([if SQLParamOptions() 2nd and 3rd arg is compatible with SQLUINTEGER]) AC_COMPILE_IFELSE( [ AC_LANG_SOURCE( [ #include #include SQLRETURN SQL_API SQLParamOptions( SQLHSTMT hstmt, SQLUINTEGER crow, SQLUINTEGER *pirow ) { return 1; } ]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE(USE_SQLPARAMOPTIONS_SQLUINTEGER_PTR, 1, [Define if SQLParamOptions() 2nd and 3rd arg is compatible with SQLUINTEGER]) ], [ AC_MSG_RESULT([no]) ]) ]) # Microsoft changed declaration for 64 bits to SQLLEN*, from SQLPOINTER AC_MSG_CHECKING([if SQLColAttribute() last arg is compatible with SQLLEN*]) AC_COMPILE_IFELSE( [ AC_LANG_SOURCE( [ #include #include SQLRETURN SQL_API SQLColAttribute( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLLEN * NumericAttributePtr ) { return 1; } ]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE(USE_SQLCOLATTRIBUTE_SQLLEN_PTR, 1, [Define if SQLColAttribute() last arg is compatible with SQLLEN*]) ], [ AC_MSG_RESULT([no]) AC_MSG_CHECKING([if SQLColAttribute() last arg is compatible with SQLPOINTER]) AC_COMPILE_IFELSE( [ AC_LANG_SOURCE( [ #include #include SQLRETURN SQL_API SQLColAttribute( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLPOINTER NumericAttributePtr ) { return 1; } ]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE(USE_SQLCOLATTRIBUTE_SQLPOINTER, 1, [Define if SQLColAttribute() last arg is compatible with SQLPOINTER]) ], [ AC_MSG_RESULT([no]) ]) ]) ################################################################### # # # Generate Makefiles # # # ################################################################### AC_OUTPUT([ Makefile util/Makefile driver/Makefile installer/Makefile test/Makefile scripts/Makefile dltest/Makefile ]) echo echo "Success!!" echo " -------------------------------------------------------------------- " echo "| Remember to check the Connector/ODBC documentation for detailed |" echo "| installation and setup instructions at: |" echo "| http://dev.mysql.com/doc/refman/5.1/en/connector-odbc.html |" echo "| |" echo "| Connector/ODBC product information: |" echo "| http://www.mysql.com/products/myodbc/ |" echo "| |" echo "| Connector/ODBC mailing list archive: |" echo "| http://lists.mysql.com/myodbc |" echo "| |" echo "| If you have any questions or improvements to the driver, then |" echo "| send a detailed mail to 'myodbc@lists.mysql.com' |" echo " -------------------------------------------------------------------- " echo mysql-connector-odbc-5.1.10-src/CMakeLists.txt100644 15766 12 26237 11707541006 17761 0ustar00cteamstaff# # Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. # # The MySQL Connector/ODBC is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FLOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ########################################################################## PROJECT(MySQL_Connector_ODBC) SET(CONNECTOR_MAJOR "5") SET(CONNECTOR_MINOR "1") SET(CONNECTOR_PATCH "10") SET(CONNECTOR_LEVEL "") CMAKE_MINIMUM_REQUIRED(VERSION 2.6.2 FATAL_ERROR) if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) #SET(CMAKE_VERBOSE_MAKEFILE ON) IF(NOT WIN32) SET(DISABLE_GUI 1) ENDIF(NOT WIN32) #-------------- find mysql -------------------- INCLUDE(${CMAKE_SOURCE_DIR}/cmake/FindMySQL.cmake) #----------------------------------------------------- #-------------- unixodbc/iodbc/win ------------------- IF(WIN32) SET(ODBCLIB odbc32) SET(ODBCINSTLIB odbccp32) ELSE(WIN32) IF(WITH_UNIXODBC) SET(ODBCLIB odbc) SET(ODBCINSTLIB odbcinst) ELSE(WITH_UNIXODBC) SET(ODBCLIB iodbc) SET(ODBCINSTLIB iodbcinst) ENDIF(WITH_UNIXODBC) # FindODBC uses ODBCLIB and ODBCINSTLIB in some cases INCLUDE(cmake/FindODBC.cmake) IF(WITH_UNIXODBC) TRY_COMPILE(COMPILE_RESULT ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/havelpcwstr.c CMAKE_FLAGS "-DINCLUDE_DIRECTORIES='${ODBC_INCLUDES} ${ODBC_INCLUDE_DIR}'") MESSAGE(STATUS "Checking if LPCWSTR type is present - ${COMPILE_RESULT}") IF(COMPILE_RESULT) ADD_DEFINITIONS(-DHAVE_LPCWSTR) ADD_DEFINITIONS(-DUSE_UNIXODBC) ENDIF(COMPILE_RESULT) ELSE(WITH_UNIXODBC) ADD_DEFINITIONS(-DHAVE_SQLGETPRIVATEPROFILESTRINGW) ENDIF(WITH_UNIXODBC) INCLUDE(CheckFunctionExists) CHECK_FUNCTION_EXISTS(dlopen DLOPEN_IN_LIBC) SET(DL_INCLUDES) SET(DL_LFLAGS) SET(DL_LIBS) IF(NOT DLOPEN_IN_LIBC) FIND_LIBRARY(DL_LIBS dl) ENDIF(NOT DLOPEN_IN_LIBC) ENDIF(WIN32) #----------------------------------------------------- #-------- configuring paths to odbc heders for compatibility checks --------- CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/sqlcolattrib1.c.cmake ${CMAKE_BINARY_DIR}/cmake/sqlcolattrib1.c @ONLY) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/sqlcolattrib2.c.cmake ${CMAKE_BINARY_DIR}/cmake/sqlcolattrib2.c @ONLY) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/sqlparamopt1.c.cmake ${CMAKE_BINARY_DIR}/cmake/sqlparamopt1.c @ONLY) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake/sqlparamopt2.c.cmake ${CMAKE_BINARY_DIR}/cmake/sqlparamopt2.c @ONLY) #----------------------------------------------------- #------------------ check compatibility--------------- TRY_COMPILE(COMPILE_RESULT ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/cmake/sqlcolattrib1.c) MESSAGE(STATUS "Checking if SQLColAttribute last arg is compatible with SQLLEN* - ${COMPILE_RESULT}") IF(COMPILE_RESULT) ADD_DEFINITIONS(-DUSE_SQLCOLATTRIBUTE_SQLLEN_PTR) ELSE(COMPILE_RESULT) TRY_COMPILE(COMPILE_RESULT1 ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/cmake/sqlcolattrib2.c) MESSAGE(STATUS "Checking if SQLColAttribute last arg is compatible with SQLPOINTER - ${COMPILE_RESULT1}") IF(COMPILE_RESULT1) ADD_DEFINITIONS(-DUSE_SQLCOLATTRIBUTE_SQLPOINTER) ELSE(COMPILE_RESULT1) # By default using SQLLEN parameter ADD_DEFINITIONS(-DUSE_SQLCOLATTRIBUTE_SQLLEN_PTR) ENDIF(COMPILE_RESULT1) ENDIF(COMPILE_RESULT) TRY_COMPILE(COMPILE_RESULT ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/cmake/sqlparamopt1.c) MESSAGE(STATUS "Checking if SQLParamOptions() 2nd and 3rd arg is compatible with SQLULEN - ${COMPILE_RESULT}") IF(COMPILE_RESULT) ADD_DEFINITIONS(-DUSE_SQLPARAMOPTIONS_SQLULEN_PTR) ELSE(COMPILE_RESULT) TRY_COMPILE(COMPILE_RESULT1 ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/cmake/sqlparamopt2.c) MESSAGE(STATUS "Checking if SQLParamOptions() 2nd and 3rd arg is compatible with SQLUINTEGER - ${COMPILE_RESULT1}") IF(COMPILE_RESULT1) ADD_DEFINITIONS(-DUSE_SQLPARAMOPTIONS_SQLUINTEGER_PTR) ELSE(COMPILE_RESULT1) # SQLULEN is a default MESSAGE(STATUS "Apparently odbc headers could not be found. 2nd and 3rd parameters assumed to be (*)SQLULEN by default") ADD_DEFINITIONS(-DUSE_SQLPARAMOPTIONS_SQLULEN_PTR) ENDIF(COMPILE_RESULT1) ENDIF(COMPILE_RESULT) #----------------------------------------------------- IF(NOT NO_THREADS) MESSAGE(STATUS "Enabling threads support") ADD_DEFINITIONS(-DTHREAD) ENDIF(NOT NO_THREADS) #------------ build options for windows -------------- IF(WIN32) REMOVE_DEFINITIONS(-DUNICODE) ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) ADD_DEFINITIONS(-DENGLISH -DMYODBC_EXPORTS -D_USERDLL) ADD_DEFINITIONS(-D_WIN32 -DWIN32 -D_WINDOWS -D__WIN__) #Since 5.5.13 libmysql introduces dependency on Secur32.lib FILE(STRINGS "${MYSQL_INCLUDE_DIR}\\mysql_version.h" mysql_version REGEX "^\#define[ \t]+MYSQL_VERSION_ID") STRING(REGEX REPLACE "^\#define[ \t]+MYSQL_VERSION_ID[ \t]+([0-9]+)" "\\1" MYSQL_CLIENT_VERSION "${mysql_version}") IF(MYSQL_CLIENT_VERSION GREATER 50512) MESSAGE(STATUS "MySQL client lib(version ${MYSQL_CLIENT_VERSION}) requires Secure32.lib - TRUE") SET(SECURE32_LIB Secur32) ENDIF(MYSQL_CLIENT_VERSION GREATER 50512) # edits for all config build flags FOREACH(TYPE C CXX) # makefiles use blank configuration FOREACH(CFG "_DEBUG" "_MINSIZEREL" "_RELEASE" "_RELWITHDEBINFO") SET(NEW_FLAGS "${CMAKE_${TYPE}_FLAGS${CFG}}") # fix up static libc flags STRING(REPLACE "/MD" "/MT" NEW_FLAGS "${NEW_FLAGS}") # Add some additional help for debug builds IF(CMAKE_BUILD_TYPE STREQUAL "Debug") STRING(REPLACE "/Zi" "/ZI" NEW_FLAGS "${NEW_FLAGS}") SET(NEW_FLAGS "${NEW_FLAGS} /RTC1 /RTCc") ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug") # *FORCE* to override whats already placed into the cache SET(CMAKE_${TYPE}_FLAGS${CFG} "${NEW_FLAGS}" CACHE STRING "CMAKE_${TYPE}_FLAGS${CFG} (overwritten for odbc)" FORCE) ENDFOREACH(CFG) ENDFOREACH(TYPE) ELSE(WIN32) ADD_DEFINITIONS(-D_UNIX_) ENDIF(WIN32) #----------------------------------------------------- SET(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin") SET(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}/lib") SET(LIB_SUBDIR "lib") IF(RPM_BUILD AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") SET(LIB_SUBDIR "lib64") ENDIF() INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) ADD_SUBDIRECTORY(util) ADD_SUBDIRECTORY(driver) IF(NOT DISABLE_GUI) ADD_SUBDIRECTORY(setupgui) ENDIF(NOT DISABLE_GUI) ADD_SUBDIRECTORY(dltest) ADD_SUBDIRECTORY(installer) ADD_SUBDIRECTORY(test) ############################################################################## # # Packaging # ############################################################################## SET(CONNECTOR_NUMERIC_VERSION "${CONNECTOR_MAJOR}.${CONNECTOR_MINOR}.${CONNECTOR_PATCH}") SET(CONNECTOR_VERSION "${CONNECTOR_NUMERIC_VERSION}${EXTRA_VERSION_SUFFIX}${CONNECTOR_LEVEL}") SET(CPACK_PACKAGE_VERSION_MAJOR ${CONNECTOR_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${CONNECTOR_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${CONNECTOR_PATCH}) IF(NOT EXTRA_NAME_SUFFIX) SET(EXTRA_NAME_SUFFIX "") ENDIF(NOT EXTRA_NAME_SUFFIX) SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Connector/ODBC 5.1, a library for connecting to MySQL servers.") SET(CPACK_PACKAGE_NAME "mysql-connector-odbc${EXTRA_NAME_SUFFIX}") IF(EXISTS "${CMAKE_SOURCE_DIR}/COPYING") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING") ELSE(EXISTS "${CMAKE_SOURCE_DIR}/COPYING") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE.mysql") ENDIF(EXISTS "${CMAKE_SOURCE_DIR}/COPYING") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README") SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CONNECTOR_VERSION}-src") SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}-${CONNECTOR_VERSION}-${CONNECTOR_PLATFORM}") IF(WIN32) SET(CPACK_GENERATOR "ZIP") SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-noinstall-${CONNECTOR_VERSION}-${CONNECTOR_PLATFORM}") ELSE(WIN32) SET(CPACK_GENERATOR "TGZ") SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY}") ENDIF(WIN32) SET(CPACK_SOURCE_IGNORE_FILES \\\\.bzr/ \\\\.bzr-mysql \\\\.bzrignore CMakeCache\\\\.txt CPackSourceConfig\\\\.cmake CPackConfig\\\\.cmake /cmake_install\\\\.cmake /CTestTestfile\\\\.cmake /CMakeFiles/ /_CPack_Packages/ Makefile$ cmake/sql.*\\\\.c$ ) #------------ Installation --------------------------- IF(WIN32) # For conveninence copy (un)install.bat scripts to the build # directory to ease testing. CONFIGURE_FILE(Install.bat ${CMAKE_BINARY_DIR}/Install.bat COPYONLY) CONFIGURE_FILE(Uninstall.bat ${CMAKE_BINARY_DIR}/Uninstall.bat COPYONLY) # TODO: line-ending conversions unix->dos INSTALL(FILES ChangeLog DESTINATION . RENAME ChangeLog.txt) INSTALL(FILES README DESTINATION . RENAME README.txt) INSTALL(FILES README.debug DESTINATION . RENAME README.debug.txt) INSTALL(FILES INSTALL DESTINATION . RENAME INSTALL.txt) INSTALL(FILES INSTALL.win DESTINATION . RENAME INSTALL.win.txt) IF(EXISTS "${CMAKE_SOURCE_DIR}/COPYING") INSTALL(FILES COPYING DESTINATION . RENAME COPYING.txt) ELSE() INSTALL(FILES LICENSE.mysql DESTINATION . RENAME LICENSE.mysql.txt) ENDIF() INSTALL(FILES Install.bat DESTINATION .) INSTALL(FILES Uninstall.bat DESTINATION .) ELSE(WIN32) INSTALL(FILES ChangeLog DESTINATION .) INSTALL(FILES README DESTINATION .) INSTALL(FILES README.debug DESTINATION .) INSTALL(FILES INSTALL DESTINATION .) IF(EXISTS "${CMAKE_SOURCE_DIR}/COPYING") INSTALL(FILES COPYING DESTINATION .) ELSE() INSTALL(FILES LICENSE.mysql DESTINATION .) ENDIF() ENDIF(WIN32) INSTALL(FILES Licenses_for_Third-Party_Components.txt DESTINATION .) INCLUDE(CPack) mysql-connector-odbc-5.1.10-src/ChangeLog100644 15766 12 127514 11707541006 17013 0ustar00cteamstaff5.1.10 Functionality added or changed: Bugs fixed: * In some cases TIMESTAMP field could be described as SQL_NO_NULLS. (Bug #13532987) * SQLFetch has to return error if indicator pointer is NULL for NULL value. (Bug #13542600) * A failure on one stmt causes another stmt to fail. (Bug #13097201/#62657) ---- 5.1.9 (04-Oct-2011) Functionality added or changed: * Support of windows authentication. Bugs fixed: * SQLFetch() did not return SQL_ERROR if connection was dropped due to a timeout. (Bug #39878) * MS Access with VARCHAR NOT NULL columns. (Bug #31067) * sqlwcharchr might read one SQLWCHAR after end of string. (Bug #61586) * Column parameter binding makes SQLExecute not to return SQL_ERROR on * If pre-execution failed some catalog functions called right after that would return only one row. (Bug #12824839) * MyODBC driver does not call mysql_thread_end() when the thread ends causing error messages such as this: Error in my_thread_global_end(): 1 threads didn't exit (Bug #57727) Built using MySQL 5.5.16. ---- 5.1.8 (08-Nov-2010) Functionality added or changed: Bugs fixed: * MySQL ODBC Connector on Windows 2008 64bit. (Bug #56233) * SQLNumResultCols() causes the driver to return only first row in the resultset. (Bug #56677) * Connector/ODBC 5.1 series requires uninstall prior to installing a new version. (Bug #54314) * SQLDescribeCol and SQLColAttribute cannot be called before SQLExecute. (Bug #56717) * Errors would not be reported in bulk upload. (Bug #56804) * MSI installer does not set InstallLocation value in registry. (Bug #56978) * SQLProcedureColumns doesn't work with certain data/parameters combinations. (Bug #57182) * SQLRowCount return wrong result row count for SQLTables and some other catalog functions. (Bug #57182) Built using MySQL 5.1.46sp1. ---- 5.1.7 (24-Aug-2010) Functionality added or changed: * Options in the GUI are groupped on named tabs. * Added connection option INTERACTIVE that tells driver that client is ineractive and interactive_timeout has to be used. (Bug #48603) * Added parameters arrays support. (Bug #48310) * SQLTables uses now INFORMATION_SCHEMA. Added connection option to use old code. (Bug #43644) * Added GUI options for MIN_DATE_TO_ZERO and ZERO_DATE_TO_MIN connection options. (Bug #30539) * Function SQLProcedureColumns() implemented. (Bug #50400) Bugs fixed: * If NO_BACKSLASH_ESCAPES mode is used on a server, escaping binary data can lead to server query parsing errors. (Bug #49029) * Error if unsupported client character set is encountered(for wchar data). (Bug #36996) * Binding bit field to a numeric types doesn't work. (Bug #32821) * Conversion flags are not complete. (Bug #43855) * SQL_ATTR_MAX_ROWS make some SELECT statement invalid. (Bug #49726) * Certain column attributes aren't correct for date columns. (Bug #44576) * SQLPrepare causes Prefetch of table. (Bug #46411) * If there are foreign key constraints with same name for tables with same names in 2 schemas, SQLForeignKeys can return rows for both schemas in query about one of those tables. (Bug #49660) * SQLForeignKeys included in results rows for foreign keys pointing to unique fields (Bug #51422) * SQLPrimaryKeys returns mangled strings. (Bug #36441) * Spaces in connection string aren't removed (Bug #45378) * SQLColumns returns wrong transfer octet length. (Bug #53235) * Retrieving of current catalog at the moment when connection is not ready for that(broken, not all pending results processed) leads to application crash. (Bug #46910) * SQLForeignKeys unacceptable performance getting imported FK. (Bug #39562) * SQLTablePrivileges requires SELECT privilege on mysql database. (Bug #50195) * Column octet length includes terminating null byte. (Bug #54206) * Wrong type returned by SQLColAttribute(SQL_DESC_PRECISION...) in 64-bit systems. (Bug #55024) ---- 5.1.6 (09-Nov-2009) Functionality added or changed: * Providing an empty string as the catalog argument to SQLTables() will return an empty result set. A catalog must have a name. * Remove use of "old" SQLLEN/SQLULEN types aliases. They are not supported in unixODBC 2.2.13 and later 64-bit builds. Removed SQLROWCOUNT, SQLROWSETSIZE, SQLTRANSID, SQLROWOFFSET. * Connection parameters can be specified individually instead of using OPTIONS bitflags. (See connection parameters documentation) * Edit controls for INITSTMT and CHARSET DSN options are added to the native Windows GUI. (Bug #40932) * Length/Indicator pointer is now stored and used internally as pointer to SQLLEN and not to SQLINTEGER. Using SQLINTEGER pointers as StrLen_or_IndPtr parameter of SQLBindParameter/SQLBindCol may cause crash on 64bit platforms. * Query log is not overwritten, but appended instead. (Bug #44965) * Fixed tab order in Windows datasource config dialog. (Bug #42905) Bugs fixed: * Add support for data-at-execution with positioned insert/update (Bug #37649) * Output string length from SQLDriverConnect() includes NULL-term (Bug #38949) * SQLGetInfo() returns 0 for SQL_CATALOG_USAGE info (Bug #39560) * SQLDriverConnect() may truncate output string erroneously (Bug #37278) * SQLTables() doesn't properly handle empty strings to list catalogs and tables. (Bug #39561) * Calling SQLDriverConnect() with no output buffer will crash if not prompting. (Bug #40316) * SQLTables() doesn't return the catalog name if the table is given and the catalog argument is NULL. (Bug #39957) * Driver crashes when attempting to retrieve data in a character set not compiled into libmysql. (Bug #39831) * SQLGetTypeInfo() doesn't return any rows SQL_TIMESTAMP on an ODBC v2 connection. (Bug #30626) * Positioned update with SQL_C_NUMERIC loses prec/scale values (Bug #39961) * ADO adUseServer cursor is lost after updating adLongVarWChar field (Bug #26950) * Calling SQLDescribeCol() with a NULL buffer and non-zero buffer length causes a crash. (Bug #41942) * NULL parameters don't work correctly with ADO. (Bug #41256) * Unable to retrieve null DECIMAL fields in ADO. (Bug #41081) * Fix positioned update using data-at-execution, bind offsets and row-wise binding. (Bug #36071) * SQLConfigDataSource may fail with: Cannot find driver (Bug #41796) * FLAG_NO_BIGINT still returns bigint for SQLDescribeCol() (Bug #17679) * Random access violation exceptions (0xC0000005) in ASP scripts in the SQLSetConnectAttrW. (Bug #44971) * Binding SQL_C_BIT to an integer column didn't work. (Bug #39644) * Inserting a new record using SQLSetPos if the table is from different than current catalog. (Bug #41946) Includes changes from Connector/ODBC 3.51.27. Built using MySQL 5.1.34sp1. ---- 5.1.5 (18-Aug-2008) Functionality added or changed: * Added FLAG_NO_BINARY_RESULT connection option to always handle binary function results as character data. (Bug #29402) Bugs fixed: * Some catalog functions used fixed-sized buffers for handling arguments that could be overrun, and misinterpreted some arguments as patterns when they should be treated as identifiers. (Bug #36275) * SQLDriverConnect() returned SQL_ERROR when the user cancelled the dialog box instead of SQL_NO_DATA. (Bug #36293) * System DSN lookup (using ODBC_BOTH_DSN) fails on Windows XP. (Bug #36203) * SQLProcedures() followed by SQLFreeStmt() crashes (Bug #36069) * ADO adUseServer cursor is lost after updating adLongVarWChar field (Bug #26950) * SQL_TYPE_TIMESTAMP and SQL_TYPE_TIME parameters were incorrectly included when parameters were expanded. (Bug #37342) * DSN-less connection prompting cannot look up driver entry (Bug #37254) Includes changes from Connector/ODBC 3.51.26. Built using MySQL 5.0.60sp1. ---- 5.1.4 (15-Apr-2008) Bugs fixed: * SQLGetDiagRec() sometimes returned SQL_SUCCESS but no error message. (Bug #33910) * Driver installer (myodbc-installer.exe) fails to create a new DSN (Bug #35776) * Get wrong result with decimal(8,2) field type (Bug #35920) Includes changes from Connector/ODBC 3.51.25. Built using MySQL 5.0.56sp1. ---- 5.1.3 (26-Mar-2008) Functionality added or changed: * Added SSLVERIFY connection option to verify server certificate. By default certificate is not verified now. (Bug #34648) * Database list height is autoadjusted in Windows GUI. (Bug #33918) Bugs fixed: * Recordset-based update fails if blob field is queried. (Bug #19065) * Descriptor records were not cleared correctly when calling SQLFreeStmt(SQL_UNBIND). (Bug #34271) * The driver incorrectly reported that the SQL standard CAST() and CONVERT() functions were supported. (Bug #33808) * Unresolved symbols "min" and "max" in libmyodbc3.so w/gcc 4.2. (Bug #34256) * Notorious #DELETED problem when linking tables in Access and BIGINT PK (Bug #24535) * MyODBC 51/Access unable to use DBEngine.RegisterDatabase to create a DSN (Bug #33825) * Unable to use surrogate pairs into with unicode column. (Bug #34672) * SQLGetData w/SQL_C_WCHAR gives incorrect data. (Bug #34429) Includes changes from Connector/ODBC 3.51.24. Built using MySQL 5.0.52. ---- 5.1.2 (12-Feb-2008) Functionality added or changed: * SQLForeignKeys uses INFORMATION_SCHEMA when it is available on the server, which allows more complete information to be returned. * Disabled MYSQL_OPT_SSL_VERIFY_SERVER_CERT when using an SSL connection. * Explicit descriptors are implemented. (Bug #32064) * Changed SQL_ATTR_PARAMSET_SIZE to return an error until support for it is implemented. Bugs fixed: * Tried to use the already-entered database when connecting to get list of databases in Windows setup library. (Bug #33615) * SQLForeignKeys returned an empty string for the schema columns instead of a NULL. (Bug #19923) * SQLGetInfo() reported characters for SQL_SPECIAL_CHARACTERS that were not encoded correctly. (Bug #33130) * Adding or updating a row using SQLSetPos() on a result set with aliased columns would fail. (Bug #6157) * Changing the DSN name when editing a DSN left behind the DSN under the old name in addition to creating the new entry. Fixed for native Windows GUI. (Bug #31165, fixed for Qt GUI in 3.51.23) * Numeric values (such as OPTIONS or PORT) were not read correctly from a connection string if they were not the last parameter. (Bug #33822) * The SSLCIPHER option was saved incorrectly on Windows. (Bug #33897) * The cursor position was incorrect after rows were deleted from a static cursor. (Bug #33388) * Dynamic cursors on statements with parameters were not supported. (Bug #11846) * FLAG_COLUMN_SIZE_S32 did not limit the octet length or display size reported for fields, causing problems with Microsoft Visual FoxPro. (Bug #30890) * Retrieving SQL_C_WCHAR data with SQLGetData() could crash due to incorrect handling of the buffer length. (Bug #32684) Includes changes from Connector/ODBC 3.51.23. Built using MySQL 5.0.52. ---- 5.1.1 (12-Dec-2007) Functionality added or changed: * Added MSI installer for Windows 64-bit. (Bug #31510) * Implemented support for SQLCancel(). (Bug #15601) * Added wrappers for missing ODBC driver manager installer functions. This makes it possible to use the driver with unixODBC 2.2.11, which is the version shipped with Debian and Ubuntu. (Bug #32685) * Disallow 'SET NAMES' in initial statement and in executed statements. * Replaced the internal library which handles creation and loading of DSN information. The new library, which was originally a part of Connector/ODBC 5.0, supports Unicode option values. * Implemented native Windows setup library. * Removed monitor (myodbc3m) and dsn-editor (myodbc3c). * Replaced myodbc3i (now myodbc-installer) with Connector/ODBC 5.0 version. * Added support for SQL_NUMERIC_STRUCT (Bug #3028, #24920). * Removed non-threadsafe configuration of the driver. The driver is now always built against the threadsafe version of libmysql. Bugs fixed: * SQL statements were limited to 64k. (Bug #30983) * Diagnostics were not correctly cleared on connection and environment handles. * SQLCopyDesc() did not correctly copy all records. * Freeing a statement resulted in a memory leak due to descriptor records not being freed. (Bug #31115) * SQL_ODBC_SQL_CONFORMANCE was not handled by SQLGetInfo(). * NULL pointers passed to SQLGetInfo() could result in a crash. * Passwords with ';' were not handled correctly. (Bug #16178) * Binding of columns between calling prepare and execute caused premature statement execution. (Bug #29239) * ADO could not open a recordset that has a DECIMAL field (Bug #31720) * SQLError() incorrectly cleared the error information, making it unavailable from subsequent calls to SQLGetDiagRec(). * ADO was unable to open record set using dynamic cursor. (Bug #32014) * SQLSetConnectAttr() did not clear previous errors, possibly confusing SQLError(). * SQLDescribeColW returned UTF-8 column as SQL_VARCHAR instead of SQL_WVARCHAR. (Bug #32161) * Fixed SQL_ATTR_PARAM_BIND_OFFSET, and fixed row offsets to work with updatable cursors. * SQLSetPos w/SQL_DELETE advances dynamic cursor incorrectly. (Bug #29765) * Recordset Update() fails in 5.1 ODBC connector when using adUseClient cursor. (Bug #26985) * MyODBC 5/ ADO Not possible to update a client side cursor. (Bug #27961) * Intermixing of SQLGetData() using SQL_C_CHAR and SQL_C_WCHAR on the same field value was incorrect. (Bug #28617) * SQLNativeSql() didn't properly handle the output length pointer. (Bug #10128 & Bug #31049) Includes changes from Connector/ODBC 3.51.21 and 3.51.22. Built using MySQL 5.0.52. ---- 5.1.0 (8-Sep-2007) Functionality added or changed: * Added support for SQL_C_WCHAR. * Added support for Unicode functions (SQLConnectW, etc). * Added descriptor support (SQLGetDescField, SQLGetDescRec, etc). ---- 3.51.27 (20-Nov-2008) Bugs fixed: * Cannot use SSL (Bug #29955) * Enable auto reconnect doesn't work (Bug #37179) * Add read and write timeouts on the connection (Bug #40407) ---- 3.51.26 (7-Jul-2008) Bugs fixed: * Access Violation in myodbc3.dll (Bug #30770) * Truncation of "SHOW CREATE TABLE" result (Bug #24131) * Added a new connection option FLAG_NO_BINARY_RESULT. Fixed field type charset 63 problem. (Bug #29402) ---- 3.51.25 (11-Apr-2008) Bugs fixed: * SQL_DESC_FIXED_PREC_SCALE was possibly being reported as true, but it's never supported. (Bug #35581) * ADO failed to retrieve the length of LONGBLOB columns. (Bug #12805) ---- 3.51.24 (14-Mar-2008) Bugs fixed: * ConfigDSN() returned FALSE when the dialog was cancelled by the user. * Static cursor was unable to be used through ADO when dynamic cursors were enabled. (Bug #27351) * Driver would crash when requesting the current catalog before connecting. (Bug #16653) * Catalog data was truncated due to NAME_LEN only allocating a single byte for characters. (Bug #32864) * SSL connections could not be established properly. Added the server certificate verification flag. (Bug #29955) * Added deprecated SQLSetParam function. (Bug #29871) * Recordset-based update fails if blob field is queried. (Bug #19065) * Allows dirty reading with SQL_TXN_READ_COMMITTED isolation through ODBC (Bug #31959) * Don't cache results and SQLExtendedFetch work badly together. (Bug #32420) * SQLFetch or SQLFetchScroll returns negative data length using SQL_C_WCHAR. (Bug #31220) * An SQLSTATE of HY000 was returned when a stored procedure was not found, instead of 42000. This caused problems with ADO's adCmdUnknown option. (Bug #27158) * Identifiers were quoted incorrectly by SQLColumns(). (Bug #32989) * Worked around bug in iODBC installer library that made it impossible to create a system DSN, particularly on Mac OS X, where the installer created the /Library/ODBC/*.ini files with insufficient permissions. (Bug #31495) * Unresolved symbols "min" and "max" in libmyodbc3.so w/gcc 4.2. (Bug #34256) * Fixed some incorrect information returned by SQLGetTypeInfo(). (Bug #30918) * Values bound using the SQL_C_CHAR value type but with numeric parameter type were not correctly escaped. (Bug #34575) * The driver reported that it does not support SQLProcedureColumns(), which causes problems for ADO. (Bug #33298) ---- 3.51.23 (9-Jan-2008) Bugs fixed: * Allowed connections to be enlisted in distributed transactions, even though the driver doesn't support them. (Bug #32727) * Cleaning up environment handles in multithread environments could result in a five (or more) second delay. (Bug #32366) * SQLAllocStmt() and SQLFreeStmt() did not synchronize access to the list of statements associated with a connection. (Bug #32857) * SQLGetInfo() returned the wrong value for SQL_DATABASE_NAME when no database was selected. (Bug #3780) * Changing the DSN name when editing a DSN left behind the DSN under the old name in addition to creating the new entry. (Bug #31165) ---- 3.51.22 (13-Nov-2007) Functionality added or changed: * Removed workaround for bug #10491 in the server, which has now been fixed in MySQL Server 5.0.48 and 5.1.21. * Added FLAG_COLUMN_SIZE_S32 to limit the reported column size to a signed 32-bit integer. This option is automatically enabled for ADO applications, in order to work around a bug in ADO. (Bug #13776) Bugs fixed: * SQLGetInfo() reported that UNION was not supported. (Bug #32253) * The non-portable "English" locale was used for handling of decimal and floating-point values instead of "C". (Bug #32294) * Unsigned integer values greater than the maximum value of a signed integer were handled incorrectly. (Bug #32171) * The wrong result was returned by SQLGetData() when the data was an empty string and a zero-sized buffer was specified. (Bug #30958) ---- 3.51.21 (5-Oct-2007) Bugs fixed: * The wrong value was returned for SQL_DESC_LITERAL_PREFIX and SQL_DESC_LITERAL_SUFFIX for date-time fields. (Bug #31009) * The wrong SQLSTATE was reported when the connection to the server was lost. (Bug #3456) * SQLDescribeCol() incorrectly reported whether auto-increment and some timestamp fields were nullable. (Bug #26108) * SQLGetTypeInfo() reported the wrong column size for the SQL_TYPE_TIME type. (Bug #30939) * Empty selection for database and character set comboboxes in setup were set to " " instead of an empty string. (Bug #30568) * Fixed incorrect input requirement in the setup dialog. (Bug #30499) * Added SQLSetParam function and fixed handling of buffer length in SQLBindParameter. (Bug #29871) * Fixed SQLSetPos that generated incorrect INSERT statement for result columns without bound data buffers. (Bug #31246) ---- 3.51.20 (7-Sep-2007) Bugs fixed: * The FLAG_NO_PROMPT option was not handled by SQLDriverConnect(). (Bug #30840) * Removed checkbox in setup dialog for FLAG_FIELD_LENGTH ("Don't Optimize Column Width"), which was removed from the driver in 3.51.18. * The wrong column size was returned for binary data. (Bug #30547) * The specified length of the username and authentication parameters to SQLConnect() were not being honored. (Bug #30774) * SQLGetData() will now always return SQL_NO_DATA_FOUND on second call when no data left, even if requested size is 0. (Bug#30520) * SQLSetParam() caused memory allocation errors due to driver manager's mapping of deprecated functions (buffer length -1). (Bug#29871) * SQLGetConnectAttr() did not reflect the connection state correctly. (Bug#14639) ---- 3.51.19 (8-Aug-2007) Functionality added or changed: * Because of Bug #10491 in the server, character string results were sometimes incorrectly identified as SQL_VARBINARY. Until this server bug is corrected, the driver will identify all variable-length strings as SQL_VARCHAR. ---- 3.51.18 (6-Aug-2007) Functionality added or changed: * An experimental binary package, without an installer, is available for Microsoft Windows x64 Edition. * Binary packages as disk images with installers are now available for Mac OS X. * Binary packages for Sun Solaris are available as PKG packages. * Added FLAG_MULTI_STATEMENTS to allow issuing queries that contain multiple statements. Also added to the setup GUI. (Bug #7445) * Removed support for the TRACE and TRACEFILE DSN options. The standard ODBC logging should be used. * Added support for SQL_ATTR_ROW_BIND_OFFSET_PTR in normal cursors. (Bug #6741) * Added SSL options to the GUI setup dialog. Bugs fixed: * SQLColumns() incorrectly reported that an auto-incrementing field was not nullable. (Bug #14407) * SQLColumns() incorrectly reported that an auto-updating timestamp field was not nullable. (Bug #14414) * Lengths returned by SQLColumns(), SQLDescribeCol(), and SQLColAttribute() were often incorrect. These lengths should now conform to the ODBC specification. FLAG_FIELD_LENGTH no longer has any effect. The default behavior was incorrect. (Bug #27862) * The SQL_DATA_TYPE column in SQLColumns() results did not report the correct value for date and time types. * The SQL_DATETIME_SUB column in SQLColumns() was not correctly set for date and time types. * The value for SQL_DESC_FIXED_PREC_SCALE was not returned correctly for decimal values in MySQL 5.0 and later. * The wrong value from SQL_DESC_LITERAL_SUFFIX was returned for binary fields. * The wrong value for SQL_DESC_TYPE was returned for date and time types. * The wrong value for DECIMAL_DIGITS in SQLColumns() was reported for FLOAT and DOUBLE fields, as well as the wrong value for the scale parameter to SQLDescribeCol(), and the SQL_DESC_SCALE attribute from SQLColAttribute(). * MySQL BIT(n) fields were always treated as SQL_BIT data. When n > 1, they are now treated as binary data. * If the connection character set was set to a multibyte character set, such as UTF-8, the wrong column size was reported. (Bug #19345) * SQLSpecialColumns() returned all TIMESTAMP fields when queried for SQL_ROWVER, not just an auto-updating TIMESTAMP field. (Bug #9927, still limited by Bug #30081 in the server.) * SQLConnect() and SQLDriverConnect() were rewritten to eliminate duplicate code and ensure all options were supported using both connection methods. SQLDriverConnect() now only requires the setup library to be present when the call requires it. * SQLColumns() failed when a catalog was specified due to an incorrectly-generated query. (Bug #29888) * SQLGetTypeInfo() returned incorrect information for date and time fields, and would not return results when queried for the SQL_DATETIME type. (Bug #28657) * myodbc3i did not honor the 's' and 'u' modifier to the -d option for installing the driver as a system or user driver. (Bug #29964) * Tables from the mysql database (catalog) were listed as SYSTEM TABLES by SQLTables() even when a different catalog was being queried. This also introduced errors due to the fix for Bug #26934. (Bug #28662) ---- 3.51.17 (13-Jul-2007) Functionality added or changed: * The setup library has been split into its own RPM package, to allow installing the driver itself with no GUI dependencies. * Added an option (CHARSET) for specifying the default character set for a connection. This must be used instead of a "SET NAMES" statement. Also available from the GUI setup dialog. (Related to Bug #6667 and Bug #9498.) * Dis-allow NULL ptr for null indicator when calling SQLGetData() if value is null. Now returns SQL_ERROR w/state 22002. * Fixed calling convention ptr and wrong free in myodbc3i, and fixed the null terminating (was only one, not two) when writing DSN to string. Bugs fixed: * myodbc3i did not correctly format driver info, which could cause the installation to fail. (Bug #29709) * Multiple result sets were not correctly flushed when a statement handle was closed. (Bug #16817) * SQLProcedures() did not handle NULL parameters, which could lead to crashes (Bug #28316) * SQLColumns() did not handle many of its parameters correctly, which could lead to incorrect results. The table name argument was not handled as a pattern value, and most arguments were not escaped correctly when they contained non-alphanumeric characters. (Bug #8860) * If there was more than one unique key on a table, the correct fields were not used in handling SQLSetPos(). (Bug #10563) * SQLColAttribute() returned the wrong value for SQL_DESC_BASE_COLUMN_NAME and SQL_DESC_BASE_TABLE_NAME for aliased fields. (Bug #6197) * Calling SQLGetDiagField with RecNumber 0,DiagIdentifier NOT 0 returns SQL_ERROR (Bug #16224) * Correctly return error if SQLBindCol is called with an invalid column * Fixed error handling of OOM and bad connections in catalog functions. This might raise errors in code paths that had not seen them in the past. Gathered the logic for internal result sets into one place. (Bug #26934) * Fixed bad use of memory related to setup/util/GUI. (Bug #27315) * Added a new DSN "OPTION" (FLAG_ZERO_DATE_TO_MIN) to retrieve XXXX-00-00 dates as the minimum allowed ODBC date (XXXX-01-01). Added another option (FLAG_MIN_DATE_TO_ZERO) to mirror this but for bound parameters. FLAG_MIN_DATE_TO_ZERO only changes 0000-01-01 to 0000-00-00. (Bug #13766) * Fixed possible crash if SQLBindCol() was not called before SQLSetPos(). Fixed use of MYSQL structure pertaining to updating large blobs in cursors. (Bug #10562) ---- 3.51.16 (14-Jun-2007) Functionality added or changed: * Added support for using SSL. This is not yet exposed in the setup GUI, but must be enabled through configuration files or the DSN. (Bug #12918) Bugs fixed: * Statements that return multiple result sets (such as calls to stored procedures) would not free all results when closed. (Bug #27544) * SQL_C_TYPE_DATE, SQL_C_TYPE_TIME, and SQL_C_TYPE_TIMESTAMP were formatted without seperators, which could cause them to get interpreted incorrectly in some cases due to server bugs. (Bug #15773) * Calls to SQLNativeSql() could cause stack corruption due to an incorrect pointer cast. (Bug #28758) * SQLSetPos() could update or delete the wrong rows when the original result set did not contain all columns of a multi-part primary key. (Bug #28255) * SQLTables() did not distinguish tables from views. (Bug #23031) * The wrong function was used for freeing the artificial result sets that are created by some catalog functions. (Bug #22797) * Accessing the results of catalog functions could cause a crash when the "Don't cache results" option was set and a forward-only cursor was being used. (Bug #4657) * SQL_WVARCHAR and SQL_WLONGVARCHAR parameters were not properly quoted and escaped. (Bug #16235) * SQLForeignKeys() did not properly escape wildcard characters in its table name parameters when retrieving information. (Bug #27723) * Calls to SQLSetPos() could cause the driver to incorrectly calculate the length of some fields. (Bug #16917) ---- 3.51.15 (04-May-2007) Bugs fixed: * SQLGetFunctions() reported that the driver supported SQLProcedureColumns(), even though it does not. (Bug #27591) * The row status array given to SQLExtendedFetch() was being stored as SQL_ATTR_ROW_STATUS_PTR, which could cause it to be used when it should not. * SQLSetPos() would not update a row unless all columns were bound, but it should only require that at least one column is bound and not set to be ignored. * SQLBulkOperations() and SQLSetPos() used the wrong indicator variable values when batch-inserting rows. (Bug #24306) * SQLGetConnectAttr() would report an incorrect isolation level if it was not explicitly set using SQLSetConnectAttr(). (Bug #27589) * The last argument of SQLColAttribute() and SQLColAttributes() was always being treated as a pointer to an SQLINTEGER even though it is sometimes a pointer to an SQLLEN. * SQLForeignKeys would return keys from the wrong tables due to improper handling of table names. (Bug #4518) * Changed the behavior of myodbc3i utility, so it loads the driver library prior to the setup library when creating a new DSN (Bug #27220) * SQLProcedures returned incomplete and incorrect information. (Bug #23033) * Statements that used "WHERE CURRENT OF" for positioned updates could not be re-executed or used with parameters that were provided using SQLPutData() and SQLParamData(). (Bug #5853) * SQLTransact() did not commit or rollback all transactions in the environment when no database connection was specified. (Bug #21588) * Updated use of FIELD_TYPE_* to MYSQL_TYPE_*, which has been preferred since the 3.23 days. (And FIELD_TYPE_* may finally disappear in 5.2.) ---- 3.51.14 (08-Mar-2007) Functionality added or changed: * Added auto-reconnect option because automatic reconnect is now disabled by default in libmysql * Added auto is null option (Bug #10910) * Added support for SQLMoreResults * Checking SQL_ATTR_CONNECTION_DEAD now always calls mysql_ping() to check for a dead connection. * Remove ODBC escape syntax { and } that begin and terminate statements, allowing {CALL (?)} for procedures that don't require OUT or INOUT parameters * Improved regression tests * Added --with-separate-debug-driver ./configure option Bugs fixed: * SQL_ATTR_CONNECTION_TIMEOUT was inappropriately mapped to MYSQL_OPT_CONNECT_TIMEOUT. (Bug #19823) * Prepared statements using the embedded MySQL server would fail due to an uninitialized variable. (Bug #16535) * Fix positioned update and delete on statements derived from one table with no primary key but including all columns (Bug #22796) * Only initialize the ODBC version when the old-style SQLAllocEnv() is called. If SQLAllocHandle() is used, the ODBC version must be explicitly specified using a call to SQLSetEnvAttr() * On 64-bit systems, some types would be incorrectly returned. (Bug #26024) * Using DataAdapter, Connector/ODBC may continually consume memory when reading the same records within a loop (Windows Server 2003 SP1/SP2 only). (Bug #20459) * Fix truncation of queries with leading spaces when SQL_ATTR_MAX_ROWS is set to a non-zero value (Bug #6609) * Fixed problem with memory allocation in Windows for BLOB and long queries that need re-allocation of net->buff * Using Connector/ODBC, with SQLBindCol and binding the length to the return value from SQL_LEN_DATA_AT_EXEC fails with a memory allocation error. (Bug #20547) * Fixed Windows-specific problems with the GUI for SQL_DRIVER_PROMPT * Fixed issues with numeric and decimal rounding * Fixed memory leak in SQLDriverConnect() * Fixed serious memory leak in SQLSpecialColumns() (up to 8KB/call) * Fixed handle leak in LibMain (Bug #21385, thanks to Leandro Becker) * Fixed field lengths reported by SQLDescribeCol() when using functions such as COMPRESS() in SELECT queries (Bug #20208) * Fixed 64-bit issues related to SQLINTEGER columns defined as int, but later cast to long. * TIME fields were incorrectly converted into date/time values. (Bug #25846, Bug #24867, and Bug #25432) * Fixed #Deleted issue in Microsoft Access by changing the default behavior of MyODBC driver regarding SQL_AUTO_IS_NULL variable. Added new option to the GUI and driver * Fixed memory leak in SQLSpecialColumns() * Corrected return size of SQLGetStmtAttr() when getting desc handles * Fixed SQLGetInfo() of SQL_TXN_CAPABLE to return SQL_TC_DDL_COMMIT * For SQL_C_TIME, handle conversion from the different time-related MySQL types, instead of always assuming it is just a TIME. (DATETIME, TIMESTAMP, and DATE were all handled incorrectly.) * Fix crash when default database is not set * Fix handling of lost connections (Bug #14639) * File DSNs could not be saved. (Bug #12019) * Nullability of auto_increment fields is incorrect. (Bug #10130) * Connector/ODBC may insert the wrong parameter values when using prepared statements under 64-bit Linux. (Bug #22446) * The SQLDriverConnect() ODBC method did not work with recent Connector/ODBC releases. (Bug #12393) * Some properties returned by SQLGetInfo() were set using the wrong data type, resulting in possible memory overruns. (Bug #20995) * Ending a transaction using SQLEndTran() on a environment handle was not supported. (Bug #21588) ---- 3.51.13 (never officially released, changes listed with those for 3.51.14) ---- 3.51.12 (25-Aug-2005) Functionality added or changed: * Improved README and other documentation * Improved source distribution for Microsoft Windows * Renamed package from 'MyODBC' to 'mysql-connector-odbc' * Improved configure script * Added -w option to myodbc3i to disable GUI popups * Redid help screen in myodbc3i * Added support for ODBCINI environment variable in SQLConnect() * Added CLIENT_MULTI_RESULTS client option * Added support for FIELD_TYPE_NEWDECIMAL * Moved to WiX-based installer for Windows * Made implied insert more intelligent (minimized the number of columns used) * Improved time/timestamp handling Bugs fixed: * Fixed problem with positioned update * Fixed SQLForeignKeys() to support more than 18 foreign keys * Fixed handling of quoting in SQLForeignKeys() * Fixed problem with SQLBulkOperations() * Fixed problems with SQL_C_BIT data type * Fixed problems with SQLGetTypeInfo() and date/time/timestamp * Fixed problems when SOCKET was something other than 'MySQL' Binaries were made using MySQL 4.1.13 and unixODBC 2.2.11 (where applicable). ---- 3.51.11-2 (11-Feb-2005) Bugs fixed: * Resources for last thread now freed * Rolled back a fix for Crystal Reports (force v3) because it caused problems for Microsoft Access (wants v2) * Fixed binary RPM spec to no longer create sample DSN. * Fixed myodbcinst to exit with proper exit code. ---- 3.51.11 (28-Jan-2005) Functionality added or changed: * Enhanced SQLGetTypeInfo(), mostly for blob types * Allow alternate keywords for DSN for read, silently converted upon write Bugs fixed: * Corrected SQLDriverConnect() to always prompt when SQL_DRIVER_PROMPT is specified * Fixed a number of problems with connecting using SQLDriverConnect ---- 3.51.10a (15-Dec-2004) Functionality added or changed: * SQLDriverConnect() heavily reworked to support prompting * Port information now saved by GUI Bugs fixed: * Fixes for working with Crystal Reports ---- 3.51.10 (25-Oct-2004) Functionality added or changed: * Qt-based setup library added * MyODBC now built against 4.1 client library Bugs fixed: * (Bug #3456) ---- 3.51.07 (10-Oct-2003) Functionality added or changed: * MyODBC now built against 4.0 client library * ./configure now uses mysql_config to get library and include options * Compile as a thread-safe library by default * Disable SIGPIPE if not compiling threadsafe * Added test suite (run as user 'odbc' and no password) * Cleaned up code to conform to MySQL coding standards * Added bin-tar target to Makefile.am for building binary distribution * Added make_win_src_distribution script for building Windows source archive * Moved Windows-specific files into win32 directory Bugs fixed: * Fixed compiler warnings * Fixed crash when SQLSetPos() is called with SQL_ADD to insert a record with an empty value (pcbValue is SQL_COLUMN_IGNORE) * Fixed SQLFetch() to return and update SQL_ATTR_ROWS_FETCHED_PTR and SQL_ATTR_ROW_STATUS_PTR statement attribute pointers * Fixed SQLFetch() to properly handle SQL_ROWSET_SIZE * Fixed .NET crash when creating the MFC database project (Bug #913) * Added search pattern for all catalog APIs for all input parameters * Added support for SQLProcedures when connected to MySQL 5.0 server * Fixed SQL_ATTR_CURRENT_CATALOG to take a correct length pointer * Portability fixes ---- 3.51.06 (27-Feb-2003) Bugs fixed: * Fixed handling long text field with 0 length data (for ADO) * Fixed syntax error due to error message overflow * Fixed problems when a dynamic cursor is used for cursor-based updated and deletes and there ar eno key fields and the table schema is changed * Fixed to refresh the initialized SQLSTATE values in the driver when the ODBC version is changed and when multiple instances of the driver are loaded * Fixed a problem with bulk inserts are performed using SQLBulkOperations() or SQLSetPos() with SQL_ADD and the wrong pcbValue is given ---- 3.51.05 Release Candidate (27-Feb-2003) Functionality added or changed: * Return the DRIVER name when no DSN is specified * Added support for new autoconf tools * Added scripts for producing binary distributions and source snapshots Bugs fixed: * Fixed core dumps when driver is built with debug options and the thread-safe client library is used * Return a valid list of catalog names in SQLTables() (for OpenOffice.org) * Fixed SQLTablePrivileges() and SQLColumnPrivileges() to return each privilege type in a distinct row * Fixed the time conversion format error from SQL_TIME_STRUCT (thanks to Ocke Janssen from Sun) * Fixed core dump on HP-UX in SQLSpecialColumns() * Fixed SQLGetTypeInfo to return individual rows for auto_increment status for all numeric types (requested by Gerry Sweeney) * Return an error when converting from SQL_C_NUMERIC. The driver is not able to handle SQL_NUMERIC_STRUCT conversion due to the lack of descriptors support. (Requested by Michael Thomas of Microsoft's ODBC team) * Added missing ROWS_FETCH_PTR and PARAM_PROCESSED_PTR status updates to SQLFetch() and SQLExtendedFetch() * Fixed failure to set the default attributes for a new DSN when they were not set through the config APIs on Microsoft Windows * Cleaned up configure scripts to work on all UNIX platforms and gcc 3.x * Minor fixes for non-gcc compilers ---- 3.51.04 (30-Sep-2002) Functionality added or changed: * Support for handling large tables without caching the entire result set. Now one can force the driver to use mysql_use_result() instead mysql_store_result(). Note that this can be used only with forward-only cursors. * Added options to ./configure to control whether to use the thread-safe client library * Cleaned up thread-safe code (debug calls) * Added checks for attempts to set pre-connection attributes (AUTOCOMMIT, TXN_ISOLATION) values after the connection instead of returning error when they are set before the connection * Added two new options, 'Force use of forward-only cursors' and 'Don't cache results', to help large-scale applications * Made improvements to driver logging calls (myodbc.log and myodbc.sql) Bugs fixed: * Fixed SQLTables() to not return an data related to SYSTEM TABLES * Fixed core dumps on HP-UX and AIX * Portability changes for MySQL 4.0.3 and 4.1 * Fixed SQLForeignKeys() when the CREATE TABLE had a COMMENT syntax * Fixed rs.update when server-side cursors are used ---- 3.51.03 (29-May-2002) Functionality added or changed: * Added new DSN configuration dialog window * Added support for SQLForeignKeys() for InnoDB tables * Added support for new Query Log eoption, which allows the driver to log all SQL queries that it processes * Added code for one-time initialization when ODBC version is specified as SQL_OV_ODBC2 so that subsequent calls will not check ODBC version * Added MY_SQL_PRIMARY_KEY(1212) option to SQLColAttribute to make rs.resync work from ADO (info from Dan Perik) Bugs fixed: * Fixed multi-step generated errors when server-side cursor is used (Thanks to vrincon_dtt@hotmail.com and AdoAnywhere) * Fixed time conversion using SQL_C_TIME when hour has three digits (Reported by Michael Thomas) * Fixed ADO/DAO empty string issue (reported by Tom DeGerlia) * Fixed BLOB data reading from ADO/DAO (reported by Justin Goodwin) * Made SQLSetPos() return an error when float fields are used (info from Jim Dickenson) * Fixed errors generated during export of tables with VARCHAR/TEXT from Microsoft Access and Microsoft DTS to MySQL using MyODBC. * Fixed to handle SQL_C_BINARY properly (info from Spilka) * Updated SQLSTATE to return correct truncated data in warning cases (info from akbar ali) * Fixed type casting issues in SQLGetInfo to make OpenOffice.org work (reported by Ocke Janssen) * Fixed SQLFetch() when it is called after SQLFetchScroll() with SQL_FETCH_FIRST (reported by Simone Tregnago) * Mapped SQL_ATTR_CONNECTION_TIMEOUT option to mysql_options() with MYSQL_OPT_CONNECT_TIMEOUT ---- 3.51.02 (05-Mar-2002) Functionality added or changed: * Added support for SQLTablePrivileges() and SQLColumnPrivileges() Bugs fixed: * Fixed SQLDriverConnect() to return correct output string. This is the main fix for 'Reserved error (-7778)' from all Windows ODBC applications. * Fixed SQLError() to clear the error buffers on the first call when the ODBC version is SQL_OV_ODBC3, so subsequent calls will return SQL_NO_DATA. * Fixed SQL_UPDATE and SQL_ADD from SQLSetPos(). * Fixed SQLSetPos() and SQLBulkOperations() to handle when the application passes SQL_NTS as pcbValue * Fixed SQLColumns() to return the correct Nullability * Fixed SQL_ATTR_CONNECTION_DEAD. This should fix the issues from SQLExpress for XBase++ and .NET connection closed issues * Fixed SQLGetTypeInfo() to return correct values for SQL_DATE, SQL_TYPE_DATE, SQL_TIME, SQL_TYPE_TIME, SQL_TIMESTAMP, and SQL_TYPE_TIMESTAMP. * Fixed tab ordering from DSN configuration dialog * Added default values for new DSN creation * Fixed export of tables from Microsoft Access 97 or Microsoft DTS when one of the field types are TEXT or VARCHAR ---- 3.51.01 (30-Jan-2002) Functionality added or changed: * Added support for the following functions: - SQLAllocHandle() - SQLFreeHandle() - SQLSetEnvAttr() - SQLGetEnvAttr() - SQLSetConnectAttr - SQLGetConnectAttr - SQLSetStmtAttr - SQLGetStmtAttr - SQLFetchScroll - SQLBulkOperations with SQL_ADD (bulk inserts) - SQLGetDiagRec - SQLGetDiagField - SQLCloseCursor - SQLEndTran * SQLGetInfo() and SQLGetFunctions() were completely rewritten * Completely changed error handling, and driver returns SQLSTATE values based on the ODBC version * Enhanced cursor handling * Changed SQLGetTypeInfo() to return column headers as per ODBC 3.50 specification * Changed all catalog functions to return column headers as per ODBC 3.50 specification * Added support for dynamic cursor types * Added a feature called 'Test Data Source' in the DSN setup catalog so that DSN settings can be tested while configuring * Added 'Help' button to the DSN setup dialog * Added SOCKET option to the SQLConnect() interface * Added support for the deprecated SQLParamOptions() function so one can specify batch parameter processing with ODBC 2.x applications * Added server name and version to the statement error prefix * Added FILEDSN support Bugs fixed: * Fixed SQL_FETCH_RELATIVE from SQLExtendedFetch() or SQLFetchScroll() to handle the following cases: - BeforeStart AND FetchOffset <= 0 - CurrRowsetStart = 1 AND FetchOffset < 0 - CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND |FetchOffset| > RowsetSize - CurrRowsetStart > 1 AND CurrRowsetStart + FetchOffset < 1 AND |FetchOffset| <= RowsetSize * Fixed SQL_FETCH_RELATIVE from SQLExtendedFetch() or SQLFetchScroll() to handle the case when: - FetchOffset < 0 AND |FetchOffset| > LastResultRow AND |FetchOffset| > RowsetSize * Fixed memory leaks when linked against iODBC or unixODBC vim: ft=text