diff --git a/.secignore b/.secignore new file mode 100644 index 0000000..f9f30d6 --- /dev/null +++ b/.secignore @@ -0,0 +1,113 @@ +########################################################## +# OBFlow Secret Scan Ignore List # +########################################################## +# Above the segmentation lines there are suspected privacy information # +# Please use the file name as the first line and the igored information # +# should be started with tab. # +# Under the segmentation lines there are the folders which you need to ignore # +########################################################## +** + http://www.gnu.org/licenses/ + http://www.gnu.org/licenses + http://license.coscl.org.cn/MulanPubL-2.0 + https://open.oceanbase.com/ + https://open.oceanbase.com + https://github.com/oceanbase/oceanbase + https://github.com/oceanbase/oceanbase/issues + http://www.mysql.com/downloads + http://dev.mysql.com/doc/refman/5.7/en/source-installation.html + http://dev.mysql.com/doc/refman/5.7/en/ + http://www.gnu.org/licenses/ + http://oceanbase.com/ + https://gw.alipayobjects.com/zos/bmw-prod/d6c1a0b7-c714-4429-8a33-2b394a5c1bf1.svg + https://www.oceanbase.com/) + https://www.oceanbase.com/docs) + http://tools.ietf.org/html/rfc1950 + http://zlib.net/ + http://zlib.net/zlib_faq.html + http://marknelson.us/1997/01/01/zlib-engine/ + http://java.sun.com/developer/technicalArticles/Programming/compression/ + http://search.cpan.org/~pmqs/IO-Compress-Zlib/ + http://docs.python.org/library/zlib.html + http://wiki.tcl.tk/4610 + http://palmzlib.sourceforge.net/ + http://tools.ietf.org/html/... + http://treebuild.metux.de/) + http://www.zlib.org/advisory-2002-03-11.txt + http://www.muppetlabs.com/~breadbox/software/assembly.html + http://www.winimage.com/zLibDll/unzip.html + http://marknelson.us/1997/01/01/zlib-engine/ + http://zlib.net/zlib_how.html + http://www.pdflib.com/ + http://sourceforge.net/projects/acroformtool/ + http://www.sunfreeware.com + http://www.gzip.org/#faq11 + http://www.ijs.si/software/snprintf/ + http://www.gnupg.org/ + http://www.info-zip.org/ + http://tools.ietf.org/html/rfc1951 + http://mariadb.mirrors.ovh.net/MariaDB/mariadb-%DB%/winx64-packages/mariadb-%DB%-winx64.msi + http://timestamp.verisign.com/scripts/timstamp.dll) + http://tools.ietf.org/html/rfc1950 + http://msdn2.microsoft.com/en-us/library/ms776446.aspx + http://www.faqs.org/rfcs/rfc2781.html + http://forums.belution.com/ja/vc/000/234/78s.shtml + http://nienie.com/~masapico/api_ImageDirectoryEntryToData.html + http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx + https://mariadb.com/kb/en/library/eof_packet/ + https://mariadb.com/kb/en/library/2-binlog-event-header/ + http://www.php.net + http://www.php.net/software + http://www.netlib.org/fp/dtoa.c + http://www.php.net/license/3_01.txt + https://mariadb.com/kb/en/mariadb/configuring-mariadb-with-mycnf/ + https://dev.mysql.com/doc/refman/8.0/en/datetime.html + https://jira.mariadb.org + http://schemas.microsoft.com/wix/2006/wi + https://facebook.github.io/watchman/) + http://www.freedesktop.org/wiki/Software/pkg-config/) + http://schemas.microsoft.com/office/word/2003/wordml + http://www.php.net/software/ + jloup@gzip.org + zlib@gzip.org + info@winimage.com + markn@ieee.org + pmqs@cpan.org + amk@amk.ca + madler@alumni.caltech.edu + rbrown64@csc.com.au + kevin@rodin.wustl.edu + Tyge.Lovset@cmr.no + paag@tid.es + c_rios@sonda.cl + michael.bell@web.de + toscano.pino@tiscali.it + malbrech@eso.org + vince@kyllikki.org + georg@mariadb.com + martinsc@uol.com.br + sinisa@coresinc.com + license@php.net + georg@mysql.com + andrey@mysql.com + uwendel@mysql.com + kpv@research.att.com + gsf@research.att.com + chongo@toad.com + andrey@php.net + someUser +**/*.c + http://www.gnu.org/licenses/ + http://www.gnu.org/licenses + http://www.php.net + http://www.php.net/software +-------------------------------------------------------- +# Should use GLOB wildcard to config and analysis the ignored folder +# The root patch shold start with '/' +/BUILD/** +/.secignore +-------------------------------------------------------- +# Config the ignored fold to escape the Chinese scan by GLOB wildcard +**/*.md +/tools/** +/test/test.conf diff --git a/CMakeLists.txt b/CMakeLists.txt index 4718499..d2748ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ IF(COMMAND CMAKE_POLICY) ENDIF() -PROJECT(mariadb-connector-c C) +PROJECT(ob-connector-c C) # Is C/C built as subproject? get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY) @@ -52,8 +52,8 @@ ENDMACRO() ### Options ### IF(NOT WIN32) - #ADD_OPTION(WITH_MYSQLCOMPAT "creates libmysql* symbolic links" OFF) - ADD_OPTION(WITH_MYSQLCOMPAT "creates libmysql* symbolic links" ON) + ADD_OPTION(WITH_MYSQLCOMPAT "creates libmysql* symbolic links" OFF) + #ADD_OPTION(WITH_MYSQLCOMPAT "creates libmysql* symbolic links" ON) ELSE() ADD_OPTION(WITH_MSI "Build MSI installation package" OFF) ADD_OPTION(WITH_SIGNCODE "digitally sign files" OFF) @@ -409,33 +409,68 @@ IF(CLIENT_DOCS) COMPONENT SharedLibraries) ENDIF() - IF(WIN32 AND WITH_MSI AND CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") ADD_SUBDIRECTORY(win/packaging) ENDIF() MESSAGE1(SYSTEM_PROCESSOR "SYSTEM processor: ${CMAKE_SYSTEM_PROCESSOR}") SET(CPACK_PACKAGE_VENDOR "MariaDB Corporation Ab") SET(CPACK_PACKAGE_DESCRIPTION "MariaDB Connector/C. A library for connecting to MariaDB and MySQL servers") -SET(CPACK_PACKAGE_NAME "mariadb_connector_c") +SET(CPACK_PACKAGE_NAME "ob_connector_c") STRING(TOLOWER ${CMAKE_SYSTEM_NAME} system_name) SET(CPACK_RESOURCE_FILE_LICENSE "${CC_SOURCE_DIR}/COPYING.LIB") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CC_SOURCE_DIR}/README") INCLUDE(cmake/ConnectorName.cmake) -IF(NOT PACKAGE_STATUS_SUFFIX) - SET(CPACK_SOURCE_PACKAGE_FILE_NAME "mariadb-connector-c-${CPACK_PACKAGE_VERSION}-src") - IF(PACKAGE_PLATFORM_SUFFIX) - SET(CPACK_PACKAGE_FILE_NAME "mariadb-connector-c-${CPACK_PACKAGE_VERSION}-${PACKAGE_PLATFORM_SUFFIX}") + +IF(NOT WIN32) + IF(NOT PACKAGE_STATUS_SUFFIX) + SET(CPACK_SOURCE_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION}-src") + IF(PACKAGE_PLATFORM_SUFFIX) + SET(CPACK_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION}-${PACKAGE_PLATFORM_SUFFIX}") + ELSE() + SET(CPACK_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION}-${system_name}-${CMAKE_SYSTEM_PROCESSOR}") + ENDIF() ELSE() - SET(CPACK_PACKAGE_FILE_NAME "mariadb-connector-c-${CPACK_PACKAGE_VERSION}-${system_name}-${CMAKE_SYSTEM_PROCESSOR}") + SET(CPACK_SOURCE_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION}-${PACKAGE_STATUS_SUFFIX}-src") + IF(PACKAGE_PLATFORM_SUFFIX) + SET(CPACK_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION}-${PACKAGE_STATUS_SUFFIX}-${PACKAGE_PLATFORM_SUFFIX}") + ELSE() + SET(CPACK_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION}-${PACKAGE_STATUS_SUFFIX}-${system_name}-${CMAKE_SYSTEM_PROCESSOR}") + ENDIF() ENDIF() ELSE() - SET(CPACK_SOURCE_PACKAGE_FILE_NAME "mariadb-connector-c-${CPACK_PACKAGE_VERSION}-${PACKAGE_STATUS_SUFFIX}-src") - IF(PACKAGE_PLATFORM_SUFFIX) - SET(CPACK_PACKAGE_FILE_NAME "mariadb-connector-c-${CPACK_PACKAGE_VERSION}-${PACKAGE_STATUS_SUFFIX}-${PACKAGE_PLATFORM_SUFFIX}") + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(PLATFORM "win64") ELSE() - SET(CPACK_PACKAGE_FILE_NAME "mariadb-connector-c-${CPACK_PACKAGE_VERSION}-${PACKAGE_STATUS_SUFFIX}-${system_name}-${CMAKE_SYSTEM_PROCESSOR}") + SET(PLATFORM "win32") + ENDIF() + + string(TIMESTAMP COMPILE_TIME %Y%m%d%H%M%S) + MESSAGE(STATUS ${COMPILE_TIME}) + + SET(CPACK_PACKAGE_VERSION_RPM ${CPACK_PACKAGE_VERSION}) + file(STRINGS ./rpm/libobclient-VER.txt STRLIST LIMIT_COUNT 1) + foreach(STR IN LISTS STRLIST) + SET(CPACK_PACKAGE_VERSION_RPM ${STR}) + endforeach(STR) + + IF(NOT PACKAGE_STATUS_SUFFIX) + SET(CPACK_SOURCE_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION_RPM}-src") + IF(PACKAGE_PLATFORM_SUFFIX) + SET(CPACK_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION_RPM}-${PACKAGE_PLATFORM_SUFFIX}") + ELSE() + SET(CPACK_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION_RPM}-${system_name}-${PLATFORM}-${COMPILE_TIME}") + ENDIF() + ELSE() + SET(CPACK_SOURCE_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION_RPM}-${PACKAGE_STATUS_SUFFIX}-src") + IF(PACKAGE_PLATFORM_SUFFIX) + SET(CPACK_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION_RPM}-${PACKAGE_STATUS_SUFFIX}-${PACKAGE_PLATFORM_SUFFIX}") + ELSE() + SET(CPACK_PACKAGE_FILE_NAME "ob-connector-c-${CPACK_PACKAGE_VERSION_RPM}-${PACKAGE_STATUS_SUFFIX}-${system_name}-${PLATFORM}-${COMPILE_TIME}") + ENDIF() ENDIF() ENDIF() + + # Build source packages IF(GIT_BUILD_SRCPKG) # get branch name diff --git a/build.sh b/build.sh index 9593c84..f40f015 100755 --- a/build.sh +++ b/build.sh @@ -26,4 +26,8 @@ cmake . \ -DDEFAULT_COLLATION=utf8_general_ci \ -DWITHOUT_TOKUDB=1 -make -j `cat /proc/cpuinfo | grep processor| wc -l` \ No newline at end of file +#-DCMAKE_BUILD_TYPE=DEBUG \ +#-DCMAKE_C_FLAGS_DEBUG="-g -O0" \ +#-DCMAKE_CXX_FLAGS_DEBUG="-g -O0" \ + +make -j `cat /proc/cpuinfo | grep processor| wc -l` diff --git a/cmake/install.cmake b/cmake/install.cmake index 6f0733f..3da81c6 100644 --- a/cmake/install.cmake +++ b/cmake/install.cmake @@ -61,12 +61,12 @@ ENDIF() # SET(INSTALL_BINDIR_DEFAULT "bin") -SET(INSTALL_LIBDIR_DEFAULT "lib/mariadb") +SET(INSTALL_LIBDIR_DEFAULT "lib") SET(INSTALL_PCDIR_DEFAULT "lib/pkgconfig") -SET(INSTALL_INCLUDEDIR_DEFAULT "include/mariadb") +SET(INSTALL_INCLUDEDIR_DEFAULT "include") SET(INSTALL_DOCDIR_DEFAULT "docs") IF(NOT IS_SUBPROJECT) - SET(INSTALL_PLUGINDIR_DEFAULT "lib/mariadb/plugin") + SET(INSTALL_PLUGINDIR_DEFAULT "lib/plugin") ELSE() ENDIF() SET(LIBMARIADB_STATIC_DEFAULT "obclnt") @@ -75,13 +75,13 @@ SET(LIBMARIADB_STATIC_DEFAULT "obclnt") # SET(INSTALL_BINDIR_RPM "bin") IF((CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "s390x") AND CMAKE_SIZEOF_VOID_P EQUAL 8) - SET(INSTALL_LIBDIR_RPM "lib64/mariadb") + SET(INSTALL_LIBDIR_RPM "lib64") SET(INSTALL_PCDIR_RPM "lib64/pkgconfig") - SET(INSTALL_PLUGINDIR_RPM "lib64/mariadb/plugin") + SET(INSTALL_PLUGINDIR_RPM "lib64/plugin") ELSE() - SET(INSTALL_LIBDIR_RPM "lib/mariadb") + SET(INSTALL_LIBDIR_RPM "lib") SET(INSTALL_PCDIR_RPM "lib/pkgconfig") - SET(INSTALL_PLUGINDIR_RPM "lib/mariadb/plugin") + SET(INSTALL_PLUGINDIR_RPM "lib/plugin") ENDIF() SET(INSTALL_INCLUDEDIR_RPM "include") SET(INSTALL_DOCDIR_RPM "docs") @@ -94,8 +94,8 @@ SET(INSTALL_BINDIR_DEB "bin") SET(INSTALL_LIBDIR_DEB "lib/${CMAKE_LIBRARY_ARCHITECTURE}") SET(INSTALL_PCDIR_DEB "lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig") SET(INSTALL_PLUGINDIR_DEB "${INSTALL_LIBDIR_DEB}/libmariadb${CPACK_PACKAGE_VERSION_MAJOR}/plugin") -SET(INSTALL_INCLUDEDIR_DEB "include/mariadb") -SET(LIBMARIADB_STATIC_DEB "mariadb") +SET(INSTALL_INCLUDEDIR_DEB "include") +SET(LIBMARIADB_STATIC_DEB "obclnt") IF(INSTALL_LAYOUT MATCHES "DEB") SET(INSTALL_PLUGINDIR_CLIENT ${INSTALL_PLUGINDIR_DEB}) diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index b9eed53..e6e34bb 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -5,9 +5,23 @@ SET(MARIADB_CLIENT_INCLUDES ${CC_SOURCE_DIR}/include/mariadb_com.h ${CC_SOURCE_DIR}/include/ma_tls.h ${CC_BINARY_DIR}/include/mariadb_version.h ${CC_SOURCE_DIR}/include/ma_list.h + ${CC_SOURCE_DIR}/include/ma_hash.h + ${CC_SOURCE_DIR}/include/ma_global.h + ${CC_SOURCE_DIR}/include/ma_string.h + ${CC_SOURCE_DIR}/include/ma_config.h + ${CC_SOURCE_DIR}/include/ma_sys.h ${CC_SOURCE_DIR}/include/errmsg.h ${CC_SOURCE_DIR}/include/mariadb_dyncol.h ${CC_SOURCE_DIR}/include/mariadb_ctype.h + ${CC_SOURCE_DIR}/include/ob_oralce_format_models.h + ${CC_SOURCE_DIR}/include/ob_complex.h + ${CC_SOURCE_DIR}/include/ob_thread.h + ${CC_SOURCE_DIR}/include/ob_rwlock.h + ${CC_SOURCE_DIR}/include/ob_cond.h + ${CC_SOURCE_DIR}/include/ob_thread_key.h + ${CC_SOURCE_DIR}/include/ob_protocol20.h + ${CC_SOURCE_DIR}/include/ob_object.h + ${CC_SOURCE_DIR}/include/ob_full_link_trace.h ${CC_SOURCE_DIR}/include/mariadb_rpl.h ) IF(NOT IS_SUBPROJECT) diff --git a/include/errmsg.h b/include/errmsg.h index 7ca3238..5c4f5fd 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -64,6 +64,7 @@ extern const char *mariadb_client_errors[]; /* Error messages */ #define CR_NET_PACKET_TOO_LARGE 2020 #define CR_SSL_CONNECTION_ERROR 2026 #define CR_MALFORMED_PACKET 2027 +#define CR_NULL_POINTER 2029 #define CR_NO_PREPARE_STMT 2030 #define CR_PARAMS_NOT_BOUND 2031 #define CR_INVALID_PARAMETER_NO 2034 diff --git a/include/ma_common.h b/include/ma_common.h index 62cf71c..f1bff2c 100644 --- a/include/ma_common.h +++ b/include/ma_common.h @@ -1,5 +1,5 @@ /* Copyright (C) 2013 by MontyProgram AB - + Copyright (c) 2021 OceanBase. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either @@ -54,7 +54,7 @@ struct st_mysql_options_extension { char *ssl_crlpath; char *server_public_key_path; struct mysql_async_context *async_context; - HASH connect_attrs; + OB_HASH connect_attrs; size_t connect_attrs_len; void (*report_progress)(const MYSQL *mysql, unsigned int stage, @@ -74,7 +74,7 @@ struct st_mysql_options_extension { my_bool read_only; char *connection_handler; my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value); - HASH userdata; + OB_HASH userdata; char *server_public_key; char *proxy_header; size_t proxy_header_len; diff --git a/include/ma_global.h b/include/ma_global.h index e8ca7df..2352de8 100644 --- a/include/ma_global.h +++ b/include/ma_global.h @@ -1,5 +1,5 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + Copyright (c) 2021 OceanBase. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either @@ -25,7 +25,12 @@ #include #include #include +#ifndef strcasecmp #define strcasecmp _stricmp +#endif +#ifndef strncasecmp +#define strncasecmp _strnicmp +#endif #define sleep(x) Sleep(1000*(x)) #ifdef _MSC_VER #define inline __inline @@ -550,7 +555,7 @@ extern double my_atof(const char*); Max size that must be added to a so that we know Size to make addressable obj. */ -typedef long my_ptrdiff_t; +typedef long ma_ptrdiff_t; #define MY_ALIGN(A,L) (((A) + (L) - 1) & ~((L) - 1)) #define ALIGN_SIZE(A) MY_ALIGN((A),sizeof(double)) /* Size to make addressable obj. */ @@ -558,7 +563,7 @@ typedef long my_ptrdiff_t; /* Offset of filed f in structure t */ #define OFFSET(t, f) ((size_t)(char *)&((t *)0)->f) #define ADD_TO_PTR(ptr,size,type) (type) ((unsigned char*) (ptr)+size) -#define PTR_BYTE_DIFF(A,B) (my_ptrdiff_t) ((unsigned char*) (A) - (unsigned char*) (B)) +#define PTR_BYTE_DIFF(A,B) (ma_ptrdiff_t) ((unsigned char*) (A) - (unsigned char*) (B)) #define NullS (char *) 0 /* Nowadays we do not support MessyDos */ @@ -682,8 +687,16 @@ typedef short int15; /* Most effective integer 0 <= x <= 32767 */ typedef char *my_string; /* String of characters */ typedef unsigned long size_s; /* Size of strings (In string-funcs) */ typedef int myf; /* Type of MyFlags in my_funcs */ -typedef char my_bool; /* Small bool */ +#ifndef TYPEDEF_MY_BOOL +#define TYPEDEF_MY_BOOL +typedef char my_bool; /* Small bool */ + +#endif + +#ifndef TYPEDEF_MY_ULONGLONG +#define TYPEDEF_MY_ULONGLONG typedef unsigned long long my_ulonglong; +#endif #if !defined(bool) && !defined(bool_defined) && (!defined(HAVE_BOOL) || !defined(__cplusplus)) typedef char bool; /* Ordinary boolean values 0 1 */ #endif @@ -1091,4 +1104,62 @@ typedef unsigned long long intptr; #define RTLD_NOW 1 #endif +#ifndef OB_MACRO +#define OB_MACRO + +#define OB_SUCCESS 0 +#define OB_ERROR -4000 +#define UNUSED(x) (void)x + +#define OB_LIKELY(x) __builtin_expect(!!(x),1) +#define OB_UNLIKELY(x) __builtin_expect(!!(x),0) +#define OB_ISNULL(statement) (OB_UNLIKELY(NULL == (statement))) +#define OB_NOT_NULL(statement) (OB_LIKELY(NULL != (statement))) + + +#define OB_SUCC(statement) (OB_LIKELY(OB_SUCCESS == (ret = (statement)))) +#define OB_FAIL(statement) (OB_UNLIKELY(OB_SUCCESS != (ret = (statement)))) +#define OB_INLINE inline __attribute__((always_inline)) + +#define OB_SIZE_OVERFLOW -4019 +#define OB_DESERIALIZE_ERROR -4034 + +#define OB_MAX_V1B (__UINT64_C(1) << 7) - 1 +#define OB_MAX_V2B (__UINT64_C(1) << 14) - 1 +#define OB_MAX_V3B (__UINT64_C(1) << 21) - 1 +#define OB_MAX_V4B (__UINT64_C(1) << 28) - 1 +#define OB_MAX_V5B (__UINT64_C(1) << 35) - 1 +#define OB_MAX_V6B (__UINT64_C(1) << 42) - 1 +#define OB_MAX_V7B (__UINT64_C(1) << 49) - 1 +#define OB_MAX_V8B (__UINT64_C(1) << 56) - 1 +#define OB_MAX_V9B (__UINT64_C(1) << 63) - 1 + + +#define OB_MAX_1B (__UINT64_C(1) << 8) - 1 +#define OB_MAX_2B (__UINT64_C(1) << 16) - 1 +#define OB_MAX_3B (__UINT64_C(1) << 24) - 1 +#define OB_MAX_4B (__UINT64_C(1) << 32) - 1 +#define OB_MAX_5B (__UINT64_C(1) << 40) - 1 +#define OB_MAX_6B (__UINT64_C(1) << 48) - 1 +#define OB_MAX_7B (__UINT64_C(1) << 56) - 1 +#define OB_MAX_8B UINT64_MAX; + + + +#define OB_MAX_INT_1B (__UINT64_C(23)) +#define OB_MAX_INT_2B (__UINT64_C(1) << 8) - 1 +#define OB_MAX_INT_3B (__UINT64_C(1) << 16) - 1 +#define OB_MAX_INT_4B (__UINT64_C(1) << 24) - 1 +#define OB_MAX_INT_5B (__UINT64_C(1) << 32) - 1 +#define OB_MAX_INT_7B (__UINT64_C(1) << 48) - 1 +#define OB_MAX_INT_9B UINT64_MAX + + +#define OB_MAX_1B_STR_LEN (__INT64_C(55)) +#define OB_MAX_2B_STR_LEN (__INT64_C(1) << 8) - 1 +#define OB_MAX_3B_STR_LEN (__INT64_C(1) << 16) - 1 +#define OB_MAX_4B_STR_LEN (__INT64_C(1) << 24) - 1 +#define OB_MAX_5B_STR_LEN (__INT64_C(1) << 32) - 1 + +#endif #endif /* _global_h */ diff --git a/include/ma_hash.h b/include/ma_hash.h index 163f182..af83fef 100644 --- a/include/ma_hash.h +++ b/include/ma_hash.h @@ -1,7 +1,7 @@ /************************************************************************************ Copyright (C) 2000, 2012 MySQL AB & MySQL Finland AB & TCX DataKonsult AB, Monty Program AB - + Copyright (c) 2021 OceanBase. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either @@ -26,19 +26,20 @@ #ifdef __cplusplus extern "C" { #endif - +#include "ma_global.h" +#include "ma_sys.h" typedef uchar *(*hash_get_key)(const uchar *,uint*,my_bool); typedef void (*hash_free_key)(void *); /* flags for hash_init */ #define HASH_CASE_INSENSITIVE 1 -typedef struct st_hash_info { +typedef struct ob_st_hash_info { uint next; /* index to next key */ uchar *data; /* data for current entry */ -} HASH_LINK; +} OB_HASH_LINK; -typedef struct st_hash { +struct ob_st_hash { uint key_offset,key_length; /* Length of key if const length */ uint records,blength,current_record; uint flags; @@ -46,20 +47,23 @@ typedef struct st_hash { hash_get_key get_key; void (*free)(void *); uint (*calc_hashnr)(const uchar *key,uint length); -} HASH; - +} ; +#ifndef TYPE_DEFINE_OB_HASH +#define TYPE_DEFINE_OB_HASH +typedef struct ob_st_hash OB_HASH; +#endif #define hash_init(A,B,C,D,E,F,G) _hash_init(A,B,C,D,E,F,G CALLER_INFO) -my_bool _hash_init(HASH *hash,uint default_array_elements, uint key_offset, +my_bool _hash_init(OB_HASH *hash,uint default_array_elements, uint key_offset, uint key_length, hash_get_key get_key, - void (*free_element)(void*), uint flags CALLER_INFO_PROTO); -void hash_free(HASH *tree); -uchar *hash_element(HASH *hash,uint idx); -void * hash_search(HASH *info,const uchar *key,uint length); -void * hash_next(HASH *info,const uchar *key,uint length); -my_bool hash_insert(HASH *info,const uchar *data); -my_bool hash_delete(HASH *hash,uchar *record); -my_bool hash_update(HASH *hash,uchar *record,uchar *old_key,uint old_key_length); -my_bool hash_check(HASH *hash); /* Only in debug library */ + void (*free_element)(void*), uint flags); +void hash_free(OB_HASH *tree); +uchar *hash_element(OB_HASH *hash,uint idx); +void * hash_search(OB_HASH *info,const uchar *key,uint length); +void * hash_next(OB_HASH *info,const uchar *key,uint length); +my_bool hash_insert(OB_HASH *info,const uchar *data); +my_bool hash_delete(OB_HASH *hash,uchar *record); +my_bool hash_update(OB_HASH *hash,uchar *record,uchar *old_key,uint old_key_length); +my_bool hash_check(OB_HASH *hash); /* Only in debug library */ #define hash_clear(H) memset((char*) (H), 0,sizeof(*(H))) #define hash_inited(H) ((H)->array.buffer != 0) diff --git a/include/ma_string.h b/include/ma_string.h index 1032538..3d50187 100644 --- a/include/ma_string.h +++ b/include/ma_string.h @@ -1,6 +1,6 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB 2012 by MontyProgram AB - + Copyright (c) 2021 OceanBase. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either @@ -22,7 +22,7 @@ #define _ma_string_h_ #include - +#include "ma_global.h" typedef enum { MY_GCVT_ARG_FLOAT, MY_GCVT_ARG_DOUBLE diff --git a/include/mariadb_com.h b/include/mariadb_com.h index f37f5fa..c5c6e32 100644 --- a/include/mariadb_com.h +++ b/include/mariadb_com.h @@ -28,7 +28,6 @@ #ifndef _mysql_com_h #define _mysql_com_h - #define NAME_CHAR_LEN 64 #define NAME_LEN 256 /* Field/table name length */ #define HOSTNAME_LENGTH 60 @@ -93,11 +92,29 @@ enum enum_server_command COM_DAEMON= 29, COM_UNSUPPORTED= 30, COM_RESET_CONNECTION = 31, + COM_STMT_PREPARE_EXECUTE = 0xa1, + COM_STMT_SEND_PIECE_DATA = 0xa2, + COM_STMT_GET_PIECE_DATA = 0xa3, COM_STMT_BULK_EXECUTE = 250, COM_RESERVED_1 = 254, /* former COM_MULTI, now removed */ COM_END }; +enum enum_nls_time_type +{ + NLS_DATE_FORMAT, + NLS_TIMESTAMP_FORMAT, + NLS_TIMESTAMP_TZ_FORMAT, + NLS_TIME_MAX +}; + +enum enum_prepare_execute_extend_flag +{ + PRE_EXE_EXTEND_FLAG_RETURNING= 1, + PRE_EXE_EXTEND_FLAG_ADD_USER_FIELD= 1<<1, + PRE_EXE_EXTEND_FLAG_PLOUT= 1<<2, + PRE_EXE_EXTEND_END=1<<31 +}; #define NOT_NULL_FLAG 1 /* Field can't be NULL */ #define PRI_KEY_FLAG 2 /* Field is part of a primary key */ @@ -162,10 +179,15 @@ enum enum_server_command #define CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS (1UL << 22) #define CLIENT_SESSION_TRACKING (1UL << 23) +/* Client no longer needs EOF packet */ +#define CLIENT_DEPRECATE_EOF (1UL << 24) + #define CLIENT_SUPPORT_ORACLE_MODE (1UL << 27) #define CLIENT_RETURN_HIDDEN_ROWID (1UL << 28) -#define CLIENT_PROGRESS (1UL << 29) /* client supports progress indicator */ +#define CLIENT_USE_LOB_LOCATOR (1UL << 29) +// #define CLIENT_PROGRESS (1UL << 29) /* client supports progress indicator */ +#define CLIENT_PROGRESS (1UL << 32) /* client supports progress indicator */ #define CLIENT_PROGRESS_OBSOLETE CLIENT_PROGRESS #define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30) #define CLIENT_REMEMBER_OPTIONS (1UL << 31) @@ -273,7 +295,7 @@ typedef struct st_ma_pvio MARIADB_PVIO; #define MAX_BIGINT_WIDTH 20 struct st_ma_connection_plugin; - +struct st_ob20protocol; typedef struct st_net { MARIADB_PVIO *pvio; @@ -292,7 +314,7 @@ typedef struct st_net { char unused_1; my_bool unused_2; my_bool compress; - my_bool unused_3; + my_bool use_ob20protocol; void *unused_4; unsigned int last_errno; unsigned char error; @@ -301,6 +323,7 @@ typedef struct st_net { char last_error[MYSQL_ERRMSG_SIZE]; char sqlstate[SQLSTATE_LENGTH+1]; struct st_mariadb_net_extension *extension; + struct st_ob20protocol *ob20protocol; } NET; #define packet_error ((unsigned int) -1) @@ -312,6 +335,9 @@ enum enum_mysql_set_option MYSQL_OPTION_MULTI_STATEMENTS_OFF }; +#define CURSOR_TYPE_ARRAY_BIND 8 +#define CURSOR_TYPE_SAVE_EXCEPTION 16 + enum enum_session_state_type { SESSION_TRACK_SYSTEM_VARIABLES= 0, @@ -334,6 +360,70 @@ enum enum_session_state_type TODO: Remove this after server fixes */ #define SESSION_TRACK_TRANSACTION_TYPE SESSION_TRACK_TRANSACTION_STATE +// for obproxy and observer compatibility +enum ObCapabilityFlagShift +{ + OB_CAP_PARTITION_TABLE_SHIFT = 0, + OB_CAP_CHANGE_USER_SHIFT, + OB_CAP_READ_WEAK_SHIFT, + OB_CAP_CHECKSUM_SHIFT, + OB_CAP_SAFE_WEAK_READ_SHIFT, + OB_CAP_PRIORITY_HIT_SHIFT, + OB_CAP_CHECKSUM_SWITCH_SHIFT, + OB_CAP_OCJ_ENABLE_EXTRA_OK_PACKET_SHIFT, + OB_CAP_OB_PROTOCOL_V2_SHIFT, + OB_CAP_EXTRA_OK_PACKET_FOR_STATISTICS_SHIFT, + OB_CAP_ABUNDANT_FEEDBACK, + OB_CAP_PL_ROUTE_SHIFT, + OB_CAP_PROXY_REROUTE_SHIFT, + OB_CAP_PROXY_SESSION_SYNC_SHIFT, + OB_CAP_FULL_LINK_TRACE_SHIFT, + OB_CAP_PROXY_NEW_EXTRA_INFO_SHIFT +}; + +#define OB_TEST_CAPABILITY(cap, tg_cap) (((cap) & (tg_cap)) == (tg_cap)) +#define OB_CAP_GET_TYPE(i) (1LL << i) +#define OB_CAP_PARTITION_TABLE OB_CAP_GET_TYPE(OB_CAP_PARTITION_TABLE_SHIFT) +#define OB_CAP_CHANGE_USER OB_CAP_GET_TYPE(OB_CAP_CHANGE_USER_SHIFT) +#define OB_CAP_READ_WEAK OB_CAP_GET_TYPE(OB_CAP_READ_WEAK_SHIFT) +#define OB_CAP_CHECKSUM OB_CAP_GET_TYPE(OB_CAP_CHECKSUM_SHIFT) +#define OB_CAP_SAFE_WEAK_READ OB_CAP_GET_TYPE(OB_CAP_SAFE_WEAK_READ_SHIFT) +#define OB_CAP_PRIORITY_HIT OB_CAP_GET_TYPE(OB_CAP_PRIORITY_HIT_SHIFT) +#define OB_CAP_CHECKSUM_SWITCH OB_CAP_GET_TYPE(OB_CAP_CHECKSUM_SWITCH_SHIFT) +#define OB_CAP_OB_PROTOCOL_V2 OB_CAP_GET_TYPE(OB_CAP_OB_PROTOCOL_V2_SHIFT) +#define OB_CAP_EXTRA_OK_PACKET_FOR_STATISTICS OB_CAP_GET_TYPE(OB_CAP_EXTRA_OK_PACKET_FOR_STATISTICS_SHIFT) +#define OB_CAP_PL_ROUTE OB_CAP_GET_TYPE(OB_CAP_PL_ROUTE_SHIFT) +#define OB_CAP_PROXY_REROUTE OB_CAP_GET_TYPE(OB_CAP_PROXY_REROUTE_SHIFT) +#define OB_CAP_PROXY_SESSION_SYNC OB_CAP_GET_TYPE(OB_CAP_PROXY_SESSION_SYNC_SHIFT) +#define OB_CAP_FULL_LINK_TRACE OB_CAP_GET_TYPE(OB_CAP_FULL_LINK_TRACE_SHIFT) +#define OB_CAP_PROXY_NEW_EXTRA_INFO OB_CAP_GET_TYPE(OB_CAP_PROXY_NEW_EXTRA_INFO_SHIFT) + +static const unsigned long OBPROXY_DEFAULT_CAPABILITY_FLAG = + (OB_CAP_PARTITION_TABLE + | OB_CAP_CHANGE_USER + | OB_CAP_READ_WEAK + | OB_CAP_CHECKSUM + | OB_CAP_SAFE_WEAK_READ + | OB_CAP_CHECKSUM_SWITCH + | OB_CAP_OB_PROTOCOL_V2 + | OB_CAP_EXTRA_OK_PACKET_FOR_STATISTICS + | OB_CAP_PL_ROUTE + | OB_CAP_PROXY_REROUTE + | OB_CAP_FULL_LINK_TRACE + | OB_CAP_PROXY_NEW_EXTRA_INFO); + +static const long OB_MAX_UINT32_BUF_LEN = 11; // string length of max uint32_t(2**32 - 1) +static const long OB_MAX_UINT64_BUF_LEN = 22; // string length of max uint64_t(2**64 - 1) +static const long OB_MAX_VERSION_BUF_LEN = 22; // string length of (xxx.xxx.xxx.xxx.xxx) +static const long OB_MAX_IP_BUF_LEN = 20; // string length of (xxx.xxx.xxx.xxx.xxx) + +static const char *const OB_MYSQL_CAPABILITY_FLAG = "__proxy_capability_flag"; +static const char *const OB_MYSQL_CLIENT_MODE = "__mysql_client_type"; +static const char *const OB_MYSQL_CLIENT_OBPROXY_MODE = "__ob_proxy"; +static const char *const OB_MYSQL_CONNECTION_ID = "__connection_id"; +static const char *const OB_MYSQL_PROXY_CONNECTION_ID = "__proxy_connection_id"; +static const char *const OB_MYSQL_GLOBAL_VARS_VERSION = "__global_vars_version"; + typedef enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE, @@ -356,6 +446,7 @@ typedef enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, MYSQL_TYPE_ARRAY = 161, //0xa1 MYSQL_TYPE_STRUCT = 162, //0xa2 MYSQL_TYPE_CURSOR = 163, //0xa3 + MYSQL_TYPE_PLARRAY = 164, //0xa4 MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE = 200, MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE = 201, MYSQL_TYPE_OB_TIMESTAMP_NANO = 202, @@ -464,6 +555,12 @@ typedef struct st_udf_init /* Constants when using compression */ #define NET_HEADER_SIZE 4 /* standard header size */ #define COMP_HEADER_SIZE 3 /* compression header extra size */ +#define PIECE_HEADER_SIZE 16 /* COM_STMT_SEND_PIECE_DATA header size */ +#define OB20_HEADER_SIZE 24 /* ob 20 protocol header size */ +#define OB20_TAILER_SIZE 4 /* ob 20 protocol tailer size */ +#define OB20_PROTOCOL_MAGIC_NUM 0x20AB /* ob 20 protocol header magic num */ +#define OB20_PROTOCOL_VERSION_VALUE 20 /* ob 20 protocol header version */ +#define OB20_EXTRAINFO_LENGTH_SIZE 4 /* ob 20 protocol extra_info size */ /* Prototypes to password functions */ #define native_password_plugin_name "mysql_native_password" diff --git a/include/mariadb_stmt.h b/include/mariadb_stmt.h index 6b93b04..0a53dd4 100644 --- a/include/mariadb_stmt.h +++ b/include/mariadb_stmt.h @@ -1,5 +1,5 @@ /************************************************************************ - + Copyright (c) 2021 OceanBase. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either @@ -22,6 +22,11 @@ *************************************************************************/ #include +#ifdef _WIN32 +typedef unsigned int uint; +typedef unsigned long ulong; +#endif + #define MYSQL_NO_DATA 100 #define MYSQL_DATA_TRUNCATED 101 #define MYSQL_DEFAULT_PREFETCH_ROWS (unsigned long) 1 @@ -55,7 +60,6 @@ do { \ #define MYSQL_PS_SKIP_RESULT_STR -2 #define STMT_ID_LENGTH 4 - typedef struct st_mysql_stmt MYSQL_STMT; typedef MYSQL_RES* (*mysql_stmt_use_or_store_func)(MYSQL_STMT *); @@ -65,6 +69,7 @@ enum enum_stmt_attr_type STMT_ATTR_UPDATE_MAX_LENGTH, STMT_ATTR_CURSOR_TYPE, STMT_ATTR_PREFETCH_ROWS, + STMT_ATTR_ARRAY_BIND, /* MariaDB only */ STMT_ATTR_PREBIND_PARAMS=200, @@ -84,6 +89,9 @@ enum enum_cursor_type CURSOR_TYPE_SCROLLABLE= 4 }; +#define CURSOR_TYPE_ARRAY_BIND 8 +#define CURSOR_TYPE_SAVE_EXCEPTION 16 + enum enum_indicator_type { STMT_INDICATOR_NTS=-1, @@ -94,6 +102,12 @@ enum enum_indicator_type STMT_INDICATOR_IGNORE_ROW=4 }; +enum enum_prepare_execute_request_ext_flag +{ + STMT_PRE_EXE_EFLAG_DEFAULT=0, + STMT_PRE_EXE_EFLAG_NEED_ARRAYBIND_RES= 2 //need resultset for arraybinding for all types(dml/pl etc.) +}; + /* bulk PS flags */ @@ -137,8 +151,14 @@ typedef struct st_mysql_bind my_bool error_value; /* used if error is 0 */ my_bool is_unsigned; /* set if integer type is unsigned */ my_bool long_data_used; /* If used with mysql_send_long_data */ + my_bool piece_data_used; /* If used with mysql_send_piece_data */ my_bool is_null_value; /* Used if is_null is 0 */ + my_bool is_handle_returning_into; /* is handling the result of returning into*/ + my_bool no_need_to_parser_result; /* skip to parse the column for this result */ + unsigned long last_offset; void *extension; + MYSQL *mysql; + MA_MEM_ROOT bind_alloc; /* use for complex type */ } MYSQL_BIND; //add for oboracle complex type @@ -173,6 +193,14 @@ typedef struct st_mysql_complex_bind_object COMPLEX_OBJECT_HEADER; } MYSQL_COMPLEX_BIND_OBJECT; +// add for support send pl array maxrarr_len +typedef struct st_mysql_complex_bind_plarray +{ + COMPLEX_OBJECT_HEADER; + unsigned long maxrarr_len; + enum enum_field_types elem_type; +} MYSQL_COMPLEX_BIND_PLARRAY; + typedef struct st_mysql_complex_bind_object MYSQL_COMPLEX_BIND_ARRAY; //end add for oboracle complex type #define MAX_OB_LOB_LOCATOR_HEADER_LENGTH 40 @@ -269,6 +297,7 @@ struct st_mysql_stmt unsigned char send_types_to_server; MYSQL_BIND *params; MYSQL_BIND *bind; + MYSQL_FIELD *param_fields; /* result set metadata */ MYSQL_DATA result; /* we don't use mysqlnd's result set logic */ MYSQL_ROWS *result_cursor; my_bool bind_result_done; @@ -297,7 +326,131 @@ struct st_mysql_stmt void *user_data; ps_result_callback result_callback; ps_param_callback param_callback; + /*add for support prepare_execute protocol*/ + unsigned int bind_size; + unsigned int iteration_count; + unsigned int execute_mode; + unsigned int check_sum; + my_bool use_prepare_execute; + /* + * Added for RETURNING...INTO to handle the resultset + **/ + my_bool is_handle_returning_into; /* is handling the result of returning into */ + my_bool has_added_user_fields; /* will be set if user's field info has added */ + my_bool is_pl_out_resultset; /* will be set for resultset of PL out parameters. OBServer will be set since 3.2.2 */ + /*end add for support prepare_execute protocol*/ + unsigned short orientation; /* i.e. OCI_FETCH_ABSOLUTE - Fetches the row number (specified by fetch_offset parameter) in the result set using absolute positioning.*/ + int fetch_offset; /* The offset to be used with the orientation parameter for changing the current row position */ + unsigned long ext_flag; /* prepare_execute extend flag which need to be send to OBServer */ + MA_MEM_ROOT param_fields_mem_root; /* param root allocations */ }; +/*add for support send PLArray maxrarr_len*/ +enum enum_mysql_send_plarray_maxrarrlen_flag +{ + SEND_PLARRAY_MAXRARRLEN_FORCE_CLOSE = 0, + SEND_PLARRAY_MAXRARRLEN_AUTO_OPEN, + SEND_PLARRAY_MAXRARRLEN_FORCE_OPEN, + SEND_PLARRAY_MAXRARRLEN_FLAG_MAX, +}; +my_bool determine_send_plarray_maxrarr_len(MYSQL *mysql); +my_bool get_support_send_plarray_maxrarr_len(MYSQL *mysql); +/*end for support send PLArray maxrarr_len*/ + +/*add for support plarray bindbyname */ +enum enum_mysql_plarray_bindbyname +{ + PLARRAY_BINDBYNAME_FORCE_CLOSE = 0, + PLARRAY_BINDBYNAME_AUTO_OPEN, + PLARRAY_BINDBYNAME_FORCE_OPEN, + PLARRAY_BINDBYNAME_FLAG_MAX, +}; +my_bool determine_plarray_bindbyname(MYSQL *mysql); +my_bool get_support_plarray_bindbyname(MYSQL *mysql); +/*end for support plarray bindbyname */ + +/*add for support protocol ob20*/ +enum enum_ob20_protocol +{ + PROTOCOL_OB20_FORCE_CLOSE = 0, + PROTOCOL_OB20_AUTO_OPEN, + PROTOCOL_OB20_FORCE_OPEN, + PROTOCOL_OB20_FLAY_MAX +}; +my_bool determine_protocol_ob20(MYSQL *mysql); +my_bool get_use_protocol_ob20(MYSQL *mysql); + +enum enum_full_link_trace +{ + PROTOCOL_FLT_FORCE_CLOSE = 0, + PROTOCOL_FLT_AUTO_OPEN, + PROTOCOL_FLT_FORCE_OPEN, + PROTOCOL_FLT_FLAY_MAX +}; +my_bool determine_full_link_trace(MYSQL *mysql); +my_bool get_use_full_link_trace(MYSQL *mysql); + +uint32_t ob_crc32(uint64_t crc, const char *buf, int64_t len); +uint64_t ob_crc64(uint64_t crc, const char *buf, int64_t len); +/*end for support protocol ob20*/ + +/* add for support bindbyname for plarray */ +struct prepare_extend_args_t +{ + unsigned int params_count; // pass params_ount from caller +}; +typedef struct prepare_extend_args_t PREPARE_EXTEND_ARGS; +// todo: add a switch to accept args +/* end for support bindbyname for plarray */ + +enum enum_mysql_prepare_execute_flag +{ + PREPARE_EXECUTE_FORCE_CLOSE = 0, + PREPARE_EXECUTE_AUTO_OPEN, + PREPARE_EXECUTE_FORCE_OPEN, + PREPARE_EXECUTE_FLAG_MAX +}; +/*add for support prepare_execute protocol*/ +my_bool determine_use_prepare_execute(MYSQL *mysql); +my_bool get_support_send_fetch_flag(MYSQL *mysql); +my_bool get_use_prepare_execute(MYSQL* msyql); +my_bool get_use_preapre_execute(MYSQL* msyql); +/*end add for support prepare_execute protocol*/ +/*add for support new prepare_execute mode*/ +//same value as OCI +#define EXECUTE_BATCH_MODE 0x00000001 /* batch the oci stmt for exec */ +#define EXECUTE_EXACT_FETCH 0x00000002 /* fetch exact rows specified */ +/* #define 0x00000004 available */ +#define EXECUTE_STMT_SCROLLABLE_READONLY \ + 0x00000008 /* if result set is scrollable */ +#define EXECUTE_DESCRIBE_ONLY 0x00000010 /* only describe the statement */ +#define EXECUTE_COMMIT_ON_SUCCESS 0x00000020 /* commit, if successful exec */ +#define EXECUTE_NON_BLOCKING 0x00000040 /* non-blocking */ +#define EXECUTE_BATCH_ERRORS 0x00000080 /* batch errors in array dmls */ +#define EXECUTE_PARSE_ONLY 0x00000100 /* only parse the statement */ +#define EXECUTE_EXACT_FETCH_RESERVED_1 0x00000200 /* reserved */ +/*add for support new prepare_execute mode*/ + +/*add for support fetch flag*/ +#define FETCH_RETURN_EXTRA_OK 0x00000001L +#define FETCH_HAS_PIECE_COLUMN 0x00000002L +//same as oci fetch orientation +#define CURSOR_FETCH_DEFAULT 0x00000000 +#define CURSOR_FETCH_CURRENT 0x00000001 /* refetching current position */ +#define CURSOR_FETCH_NEXT 0x00000002 /* next row */ +#define CURSOR_FETCH_FIRST 0x00000004 /* first row of the result set */ +#define CURSOR_FETCH_LAST 0x00000008 /* the last row of the result set */ +#define CURSOR_FETCH_PRIOR 0x00000010 /* previous row relative to current */ +#define CURSOR_FETCH_ABSOLUTE 0x00000020 /* absolute offset from first */ +#define CURSOR__FETCH_RELATIVE 0x00000040 /* offset relative to current */ +#define CURSOR__FETCH_RESERVED_1 0x00000080 /* reserved */ +#define CURSOR__FETCH_RESERVED_2 0x00000100 /* reserved */ +#define CURSOR__FETCH_RESERVED_3 0x00000200 /* reserved */ +#define CURSOR__FETCH_RESERVED_4 0x00000400 /* reserved */ +#define CURSOR__FETCH_RESERVED_5 0x00000800 /* reserved */ +#define CURSOR__FETCH_RESERVED_6 0x00001000 /* reserved */ +/*end add for support fetch flag*/ + +#define NEED_DATA_AT_EXEC_FLAG 0x00000001 typedef void (*ps_field_fetch_func)(MYSQL_BIND *r_param, const MYSQL_FIELD * field, unsigned char **row); typedef struct st_mysql_perm_bind { @@ -307,7 +460,7 @@ typedef struct st_mysql_perm_bind { unsigned long max_len; } MYSQL_PS_CONVERSION; -extern MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 1]; +extern MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 2]; unsigned long ma_net_safe_read(MYSQL *mysql); void mysql_init_ps_subsystem(void); unsigned long net_field_length(unsigned char **packet); @@ -319,9 +472,38 @@ int ma_simple_command(MYSQL *mysql,enum enum_server_command command, const char MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql); int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned long length); int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt); +int STDCALL mysql_stmt_prepare_v2(MYSQL_STMT *stmt, const char *query, unsigned long length, void* extend_arg); +int STDCALL mysql_stmt_execute_v2(MYSQL_STMT *stmt, const char *query, unsigned long length, unsigned int iteration_count, int execute_mode, void* extend_arg); int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt); int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind_arg, unsigned int column, unsigned long offset); int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt); + +my_bool STDCALL mysql_stmt_send_piece_data(MYSQL_STMT *stmt, unsigned int param_number, + const char *data, unsigned long length, + char piece_type, char is_null); +my_bool STDCALL mysql_stmt_read_piece_data(MYSQL_STMT *stmt, unsigned int param_number, + unsigned short orientation, int scroll_offset, + unsigned long data_len, unsigned char *piece_type, unsigned long *ret_data_len); +/* + * add for RETURNING INTO resultset's flag + **/ +my_bool STDCALL is_returning_result(MYSQL_STMT *stmt); +my_bool STDCALL has_added_user_fields(MYSQL_STMT *stmt); +/* add pl out resultset flag, observer since 3.2.2 */ +my_bool STDCALL is_pl_out_result(MYSQL_STMT *stmt); + +unsigned long STDCALL stmt_pre_exe_req_ext_flag_get(MYSQL_STMT *stmt); +void STDCALL stmt_pre_exe_req_ext_flag_set(MYSQL_STMT *stmt, unsigned long flag); + +/* + * add oracle_mode fetch method + */ +int STDCALL mysql_stmt_fetch_oracle_cursor(MYSQL_STMT *stmt); +int STDCALL mysql_stmt_fetch_oracle_implicit_cursor(MYSQL_STMT *stmt, my_bool is_need_fetch_from_server); +int STDCALL mysql_stmt_fetch_oracle_buffered_result(MYSQL_STMT *stmt); +/* + * end add oracle_mode fetch method + */ unsigned long STDCALL mysql_stmt_param_count(MYSQL_STMT * stmt); my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, const void *attr); my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, void *attr); @@ -347,3 +529,5 @@ int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt); my_bool STDCALL mysql_stmt_more_results(MYSQL_STMT *stmt); int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt, const char *stmt_str, size_t length); MYSQL_FIELD * STDCALL mariadb_stmt_fetch_fields(MYSQL_STMT *stmt); +void end_server(MYSQL *mysql); +void free_old_query(MYSQL *mysql); diff --git a/include/mariadb_version.h.in b/include/mariadb_version.h.in index a82dbb7..e40cbcc 100644 --- a/include/mariadb_version.h.in +++ b/include/mariadb_version.h.in @@ -11,7 +11,9 @@ #else #define PROTOCOL_VERSION @PROTOCOL_VERSION@ #define MARIADB_CLIENT_VERSION_STR "@MARIADB_CLIENT_VERSION@" +#ifndef MARIADB_BASE_VERSION #define MARIADB_BASE_VERSION "@MARIADB_BASE_VERSION@" +#endif #define MARIADB_VERSION_ID @MARIADB_VERSION_ID@ #define MARIADB_PORT @MARIADB_PORT@ #define MARIADB_UNIX_ADDR "@MARIADB_UNIX_ADDR@" @@ -24,7 +26,9 @@ #define MYSQL_CONFIG_NAME "my" #define MYSQL_VERSION_ID @MARIADB_VERSION_ID@ +#ifndef MYSQL_SERVER_VERSION #define MYSQL_SERVER_VERSION "@MARIADB_CLIENT_VERSION@-MariaDB" +#endif #define MARIADB_PACKAGE_VERSION "@CPACK_PACKAGE_VERSION@" #define MARIADB_PACKAGE_VERSION_ID @MARIADB_PACKAGE_VERSION_ID@ diff --git a/include/mysql.h b/include/mysql.h index b8cbb1b..b516bf8 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -36,8 +36,17 @@ extern "C" { #if !defined (_global_h) && !defined (MY_GLOBAL_INCLUDED) /* If not standard header */ #include -typedef char my_bool; + +#ifndef TYPEDEF_MY_BOOL +#define TYPEDEF_MY_BOOL +typedef char my_bool; /* Small bool */ + +#endif + +#ifndef TYPEDEF_MY_ULONGLONG +#define TYPEDEF_MY_ULONGLONG typedef unsigned long long my_ulonglong; +#endif #if !defined(_WIN32) #define STDCALL @@ -60,6 +69,7 @@ typedef int my_socket; #include "mariadb_version.h" #include "ma_list.h" #include "mariadb_ctype.h" +// #include "ma_hash.h" typedef struct st_ma_const_string @@ -96,10 +106,19 @@ extern unsigned int mariadb_deinitialize_ssl; #define IS_PRI_KEY(n) ((n) & PRI_KEY_FLAG) #define IS_NOT_NULL(n) ((n) & NOT_NULL_FLAG) #define IS_BLOB(n) ((n) & BLOB_FLAG) -#define IS_NUM(t) (((t) <= MYSQL_TYPE_INT24 && (t) != MYSQL_TYPE_TIMESTAMP) || (t) == MYSQL_TYPE_YEAR || (t) == MYSQL_TYPE_NEWDECIMAL) +#define IS_NUM(t) (((t) <= MYSQL_TYPE_INT24 && (t) != MYSQL_TYPE_TIMESTAMP) || (t) == MYSQL_TYPE_YEAR || (t) == MYSQL_TYPE_NEWDECIMAL || (t) == MYSQL_TYPE_OB_NUMBER_FLOAT) +#define IS_LONGDATA(t) (((t) >= MYSQL_TYPE_TINY_BLOB && (t) <= MYSQL_TYPE_STRING) || ((t) == MYSQL_TYPE_OB_NVARCHAR2 || (t) == MYSQL_TYPE_OB_NCHAR)) #define IS_NUM_FIELD(f) ((f)->flags & NUM_FLAG) #define INTERNAL_NUM_FIELD(f) (((f)->type <= MYSQL_TYPE_INT24 && ((f)->type != MYSQL_TYPE_TIMESTAMP || (f)->length == 14 || (f)->length == 8)) || (f)->type == MYSQL_TYPE_YEAR || (f)->type == MYSQL_TYPE_NEWDECIMAL || (f)->type == MYSQL_TYPE_DECIMAL) +#define IS_NUM_TERMINAL(t) (((t) <= MYSQL_TYPE_INT24 && (t) != MYSQL_TYPE_TIMESTAMP) || (t) == MYSQL_TYPE_YEAR || (t) == MYSQL_TYPE_NEWDECIMAL || (t) == MYSQL_TYPE_OB_NUMBER_FLOAT) +#define IS_NUM_BINARY_TERMINAL(t) ((t) == MYSQL_TYPE_FLOAT || (t) == MYSQL_TYPE_DOUBLE) +enum ObRoutineParamInOut +{ + SP_PARAM_IN = 1, + SP_PARAM_OUT = 2, + SP_PARAM_INOUT = 3, +}; typedef struct st_mysql_field { char *name; /* Name of column */ char *org_name; /* Name of original column (added after 3.23.58) */ @@ -121,8 +140,22 @@ extern unsigned int mariadb_deinitialize_ssl; /***********************/ unsigned int flags; /* Div flags */ unsigned int decimals; /* Number of decimals in field */ + unsigned int precision; + enum ObRoutineParamInOut ob_routine_param_inout; + my_bool is_implicit_rowid; unsigned int charsetnr; /* char set number (added in 4.1) */ + unsigned char *owner_name; + unsigned char *type_name; + unsigned long version; + unsigned int owner_name_length; + unsigned int type_name_length; enum enum_field_types type; /* Type of field. Se mysql_com.h for types */ + unsigned char *elem_owner_name; + unsigned char *elem_type_name; + unsigned long elem_version; + unsigned int elem_owner_name_length; + unsigned int elem_type_name_length; + enum enum_field_types elem_type; /* Type of array elem. Se mysql_com.h for types */ void *extension; /* added in 4.1 */ } MYSQL_FIELD; @@ -369,7 +402,47 @@ struct st_mysql_options { my_bool *unbuffered_fetch_owner; char *info_buffer; struct st_mariadb_extension *extension; + void * ob_extension; + my_bool can_use_prepare_execute; + my_bool can_send_plarray_maxrarr_len; + my_bool can_plarray_bindbyname; + my_bool can_use_protocol_ob20; + my_bool can_use_full_link_trace; + unsigned long ob_server_version; + unsigned long ob_proxy_version; + unsigned long capability; // system varaiable } MYSQL; +struct st_mysql_trace_info; +struct ob_st_hash; +#ifndef TYPE_DEFINE_OB_HASH +#define TYPE_DEFINE_OB_HASH +typedef struct ob_st_hash OB_HASH; +#endif +typedef struct st_mysql_extension { + MYSQL *mysql; + OB_HASH *complex_type_hash; +#define MAX_NLS_FORMAT_STR_LEN 256 + unsigned char nls_date_format[MAX_NLS_FORMAT_STR_LEN]; + unsigned char nls_timestamp_format[MAX_NLS_FORMAT_STR_LEN]; + unsigned char nls_timestamp_tz_format[MAX_NLS_FORMAT_STR_LEN]; + // struct st_mysql_trace_info *trace_data; + // struct st_session_track_info state_change; +} MYSQL_EXTENSION; +/* "Constructor/destructor" for MYSQL extension structure. */ +struct st_mysql_extension* mysql_extension_init(struct st_mysql*); +void mysql_extension_free(struct st_mysql_extension*); + +/* + Note: Allocated extension structure is freed in mysql_close_free() + called by mysql_close(). +*/ +#define MYSQL_EXTENSION_PTR(H) \ +( \ + (struct st_mysql_extension*) \ + ( (H)->ob_extension ? \ + (H)->ob_extension : ((H)->ob_extension= mysql_extension_init(H)) \ + ) \ +) typedef struct st_mysql_res { unsigned long long row_count; @@ -384,6 +457,8 @@ typedef struct st_mysql_res { MYSQL *handle; /* for unbuffered reads */ my_bool eof; /* Used my mysql_fetch_row */ my_bool is_ps; + MYSQL_FIELD *param_fields; + unsigned int param_count; } MYSQL_RES; typedef struct @@ -512,6 +587,7 @@ my_bool STDCALL mysql_eof(MYSQL_RES *res); MYSQL_FIELD *STDCALL mysql_fetch_field_direct(MYSQL_RES *res, unsigned int fieldnr); MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res); +MYSQL_FIELD * STDCALL mysql_fetch_params(MYSQL_RES *res); MYSQL_ROWS * STDCALL mysql_row_tell(MYSQL_RES *res); unsigned int STDCALL mysql_field_tell(MYSQL_RES *res); @@ -742,6 +818,7 @@ int STDCALL mysql_stmt_send_long_data_start(my_bool *ret, MYSQL_STMT *stmt, int STDCALL mysql_stmt_send_long_data_cont(my_bool *ret, MYSQL_STMT *stmt, int status); int STDCALL mysql_reset_connection(MYSQL *mysql); +my_bool get_local_ip_port(my_socket fd, char *ip, int iplen, int *port); /* API function calls (used by dynamic plugins) */ struct st_mariadb_api { diff --git a/include/ob_bitmap.h b/include/ob_bitmap.h new file mode 100644 index 0000000..5b3872e --- /dev/null +++ b/include/ob_bitmap.h @@ -0,0 +1,50 @@ +/************************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*************************************************************************************/ + +#ifndef _ob_bitmap_h_ +#define _ob_bitmap_h_ + +#include + +typedef uint32 ob_bitmap_map; + +typedef struct st_bitmap +{ + ob_bitmap_map *bitmap; + uint n_bits; +} OB_BITMAP; + +#ifdef __cplusplus +extern "C" { +#endif +my_bool ob_bitmap_init(OB_BITMAP *map, uint n_bits); +void ob_bitmap_free(OB_BITMAP *map); +void ob_bitmap_set_bit(OB_BITMAP *map, uint bit); +void ob_bitmap_clear_bit(OB_BITMAP *map, uint bit); +my_bool ob_bitmap_is_set(const OB_BITMAP *map, uint bit); +void ob_bitmap_clear_all(OB_BITMAP *map); +void ob_bitmap_set_all(OB_BITMAP *map); + +#ifdef __cplusplus +} +#endif + +#endif /* _ob_bitmap_h_ */ diff --git a/include/ob_complex.h b/include/ob_complex.h new file mode 100644 index 0000000..46f8de1 --- /dev/null +++ b/include/ob_complex.h @@ -0,0 +1,83 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ + +#ifndef _OB_COMPLEX_H +#define _OB_COMPLEX_H + +#include "mysql.h" +#include "ma_hash.h" +#include "ob_rwlock.h" + +typedef struct st_complex_hash { + unsigned char *hash_key; + OB_HASH *hash; + ob_rw_lock_t rwlock; +} COMPLEX_HASH; + +typedef enum enum_types { + TYPE_NUMBER, + TYPE_VARCHAR2, + TYPE_CHAR, + TYPE_DATE, + TYPE_OBJECT, + TYPE_COLLECTION, + TYPE_RAW, + TYPE_LONG, + TYPE_LONGLONG, + TYPE_TINY, + TYPE_SHORT, + TYPE_FLOAT, + TYPE_DOUBLE, + TYPE_UNKNOW, + TYPE_MAX +} enum_types; + +typedef struct st_complex_type { + enum_types type; + unsigned char owner_name[128]; + unsigned char type_name[128]; + unsigned int version; + my_bool is_valid; +} COMPLEX_TYPE; + +typedef struct st_child_type { + enum_types type; + COMPLEX_TYPE *object; +} CHILD_TYPE; + +typedef struct st_complex_type_object { + COMPLEX_TYPE header; + unsigned int attr_no; + unsigned int init_attr_no; + CHILD_TYPE *child; +} COMPLEX_TYPE_OBJECT; + +typedef struct st_complex_type_collection { + COMPLEX_TYPE header; + CHILD_TYPE child; +} COMPLEX_TYPE_COLLECTION; + +COMPLEX_TYPE* STDCALL get_complex_type(MYSQL *mysql, unsigned char *owner_name, unsigned char *type_name); +COMPLEX_TYPE* STDCALL get_complex_type_with_local(MYSQL *mysql, unsigned char *type_name); + +#endif /* _OB_COMPLEX_H */ diff --git a/include/ob_cond.h b/include/ob_cond.h new file mode 100644 index 0000000..29e9390 --- /dev/null +++ b/include/ob_cond.h @@ -0,0 +1,51 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ + +#ifndef _OB_COND_H +#define _OB_COND_H + +#include "ob_thread.h" +#include "ob_rwlock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +typedef CONDITION_VARIABLE ob_cond_t; +#else +typedef pthread_cond_t ob_cond_t; +#endif + +int ob_cond_init(ob_cond_t *cond); +int ob_cond_destroy(ob_cond_t *cond); +int ob_cond_timedwait(ob_cond_t *cond, ob_mutex_t *mutex, const struct timespec *abstime); +int ob_cond_wait(ob_cond_t *cond, ob_mutex_t *mutex); +int ob_cond_signal(ob_cond_t *cond); +int ob_cond_broadcast(ob_cond_t *cond); + +#ifdef __cplusplus +} +#endif + +#endif /* _OB_COND_H */ diff --git a/include/ob_full_link_trace.h b/include/ob_full_link_trace.h new file mode 100644 index 0000000..b4d27f8 --- /dev/null +++ b/include/ob_full_link_trace.h @@ -0,0 +1,439 @@ +/* + Copyright (c) 2000, 2018, Oracle and/or its affiliates. + Copyright (c) 2009, 2019, MariaDB Corporation. + Copyright (c) 2021 OceanBase. + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef _ob_full_link_trace_h +#define _ob_full_link_trace_h + +#include +#include "ob_object.h" +#include "ma_list.h" +#include "ob_thread_key.h" + +// #define DEBUG_OB20 + +#define UUID4_LEN 37 +#define UUID4_SERIALIZE_LEN 16 + +#define FLT_DECLARE \ + Ob20Protocol *ob20protocol = mysql->net.ob20protocol; \ + FLTInfo *flt = NULL; \ + ObSpanCtx *span; \ + ObTrace *trace; \ + char ip_buffer[50] = {0}; \ + char host_buffer[50] = {0}; \ + int port = 0 + +#define FLT_BEFORE_COMMAND(span_level, tag_level, tag_str) \ + do \ + { \ + if (get_use_full_link_trace(mysql) && OB_NOT_NULL(ob20protocol)) { \ + flt = ob20protocol->flt; \ + if (OB_NOT_NULL(flt)) { \ + if (mysql->server_status & SERVER_STATUS_IN_TRANS) { \ + /* 当前事务中每次 begin 之前需要 reset,防止 span 不够用*/ \ + reset_span(flt->trace_); \ + } else { \ + /* 不在事务中再重新 begin trace */ \ + BEGIN_TRACE(flt); \ + } \ + span = BEGIN_SPAN(flt, span_level); \ + SET_TAG(flt, tag_level, tag_str); \ + if (NULL != mysql && get_local_ip_port(mysql->net.fd, ip_buffer, 50, &port)) { \ + snprintf(host_buffer, 50, "\"%s:%d\"", ip_buffer, port); \ + SET_TAG(flt, FLT_TAG_CLIENT_HOST, host_buffer); \ + } \ + if (flt_build_request(mysql, flt)) { \ + /* error , but do nothing */ \ + } \ + } \ + } \ + } while (0) + +#define FLT_AFTER_COMMAND \ + do \ + { \ + ob20protocol = mysql->net.ob20protocol; \ + if (get_use_full_link_trace(mysql) && OB_NOT_NULL(ob20protocol)) { \ + flt = ob20protocol->flt; \ + trace = OBTRACE(flt); \ + if (NULL != trace) { \ + END_SPAN(flt, span); \ + if (TRUE == trace->force_print_) { \ + FLUSH_TRACE(flt); \ + } else if (TRUE == trace->slow_query_print_) { \ + trace->slow_query_print_ = FALSE; /* 每次请求就slow query置为FALSE */ \ + flush_first_span(trace); \ + } \ + if (mysql->server_status & SERVER_STATUS_IN_TRANS) { \ + /* do nothing */ \ + } else { \ + /* 不在事务中需要结束trace */ \ + END_TRACE(flt); \ + } \ + } \ + } \ + } while (0) + +#define DEFINE_FLT_SERIALIZE_FUNC(type) \ + int flt_serialize_##type(char *buf, const int64_t len, int64_t *pos, void *flt_info); \ + int flt_get_serialize_size_##type(int32_t *size, void *flt_info); \ + int flt_deserialize_field_##type(FullLinkTraceExtraInfoId extra_id, const int64_t v_len, \ + const char *buf, const int64_t len, int64_t *pos, void *flt_info) + +#define FLT_SERIALIZE_FUNC(type) \ + { \ + flt_deserialize_field_##type, \ + flt_serialize_##type, \ + flt_get_serialize_size_##type \ + } + +#define DEFINE_TO_STRING_FUNC_FOR(type) \ + int tostring_##type(char *buf, const int64_t buf_len, int64_t *pos, type *src) + +// 当前维护每个trace大小为4k, 这个大小是整个trace期间分配的所有空间大小 +#define OBTRACE_DEFAULT_BUFFER_SIZE (1L << 12) +// 当前驱动free span为4个 +#define SPAN_CACHE_COUNT (1L << 2) +// 由于当前最多就一个SPAN,最大的LOG buffer为 1k +#define MAX_TRACE_LOG_SIZE (1L << 10) +// 当前全链路部分序列化后最大大小为 2k +#define MAX_FLT_SERIALIZE_SIZE (1L << 11) +#define INIT_OFFSET (MAX_TRACE_LOG_SIZE + MAX_FLT_SERIALIZE_SIZE + SPAN_CACHE_COUNT * (sizeof(LIST) + sizeof(ObSpanCtx))) + +#define OBTRACE(flt) get_trace_instance(flt) +#define BEGIN_TRACE(flt) (begin_trace(OBTRACE(flt))) +#define END_TRACE(flt) \ + do \ + { \ + end_trace(OBTRACE(flt)); \ + } while (0) +#define BEGIN_SPAN(flt, span_type) BEGIN_CHILD_SPAN(flt, span_type) +#define BEGIN_CHILD_SPAN(flt, span_type) (begin_span(OBTRACE(flt), span_type, 1, FALSE)) +#define BEGIN_FOLLOW_SPAN(flt, span_type) (begin_span(OBTRACE(flt), span_type, 1, TRUE)) +#define END_SPAN(flt, span_id) \ + do \ + { \ + ObTrace *trace = OBTRACE(flt); \ + (end_span(trace, span_id)); \ + } while (0) +#define SET_TAG(flt, tag_type, value) \ + do \ + { \ + ObSpanCtx *span = OBTRACE(flt)->last_active_span_; \ + if (OB_NOT_NULL(span)) { \ + append_tag(OBTRACE(flt), span, tag_type, value); \ + } \ + } while (0) +#define SET_TRACE_LEVEL(trace, level) (trace->level_ = level) +#define SET_AUTO_FLUSH(trace, value) (trace->auto_flush_ = value) +#define FLUSH_TRACE(flt) \ + do \ + { \ + flush_trace(OBTRACE(flt)); \ + } while (0) + + +typedef struct st_obtrace ObTrace; +/* +0 ~ 999用于ob client 私有 +1000~1999用于ob proxy私有 +2000~65535用于全链路共有 +*/ +typedef enum enum_fulllinktraceextrainfoid +{ + FLT_DRIVER_SPAN = 1, + FLT_DRIVER_END = 1000, + // APP_INFO + FLT_CLIENT_IDENTIFIER = 2001, + FLT_MODULE, + FLT_ACTION, + FLT_CLIENT_INFO, + FLT_APPINFO_TYPE, + // QUERY_INFO + FLT_QUERY_START_TIMESTAMP = 2010, + FLT_QUERY_END_TIMESTAMP, + // CONTROL_INFO + FLT_LEVEL = 2020, + FLT_SAMPLE_PERCENTAGE, + FLT_RECORD_POLICY, + FLT_PRINT_SAMPLE_PCT, + FLT_SLOW_QUERY_THRES, + // SPAN_INFO + FLT_TRACE_ENABLE = 2030, + FLT_FORCE_PRINT, + FLT_TRACE_ID, + FLT_REF_TYPE, + FLT_SPAN_ID, + FLT_EXTRA_INFO_ID_END +} FullLinkTraceExtraInfoId; + +/* +0 ~ 999用于ob client 私有 +1000~1999用于ob proxy私有 +2000~65535用于全链路共有 +*/ +typedef enum enum_fulllinktraceextrainfotype +{ + FLT_DRIVER_SPAN_INFO = 1, + FLT_EXTRA_INFO_DRIVE_END = 1000, + + FLT_APP_INFO = 2001, + FLT_QUERY_INFO, + FLT_CONTROL_INFO, + FLT_SPAN_INFO, + FLT_EXTRA_INFO_TYPE_END +} FullLinkTraceExtraInfoType; + +typedef enum enum_recordpolicy { + RP_ALL = 1, + RP_ONLY_SLOW_QUERY, + RP_SAMPLE_AND_SLOW_QUERY, + MAX_RECORD_POLICY +} RecordPolicy; + +typedef struct st_fltinfobase +{ + FullLinkTraceExtraInfoType type_; +} FLTInfoBase; + +typedef struct st_fltcontrolinfo +{ + FullLinkTraceExtraInfoType type_; + int8_t level_; + double sample_pct_; + RecordPolicy rp_; + double print_sample_pct_; + int64_t slow_query_threshold_; +} FLTControlInfo; + +typedef struct st_fltappinfo +{ + FullLinkTraceExtraInfoType type_; + const char *identifier_; + const char *module_; + const char *action_; + const char *client_info_; +} FLTAppInfo; + +typedef struct st_fltqueryinfo +{ + FullLinkTraceExtraInfoType type_; + int64_t query_start_timestamp_; + int64_t query_end_timestamp_; +} FLTQueryInfo; + +typedef struct st_fltspaninfo +{ + FullLinkTraceExtraInfoType type_; + int8_t trace_enable_; + int8_t force_print_; + const char *trace_id_; + int8_t ref_type_; + const char *span_id_; +} FLTSpanInfo; + +typedef struct st_fltdriverspaninfo +{ + FullLinkTraceExtraInfoType type_; + const char *client_span_; +} FLTDriverSpanInfo; + +typedef struct st_fltvaluedata +{ + void *value_data_; + size_t length; +} FLTValueData; + +typedef struct st_fltinfo +{ + FLTDriverSpanInfo client_span_; + FLTControlInfo control_info_; + FLTAppInfo app_info_; + FLTQueryInfo query_info_; + FLTSpanInfo span_info_; + + FLTValueData flt_value_data_; // for set extra info + + my_bool in_trans_; + char trace_id_[UUID4_SERIALIZE_LEN]; + char span_id_[UUID4_SERIALIZE_LEN]; + ObTrace *trace_; +} FLTInfo; + +typedef int (*flt_deserialize_field_func)(FullLinkTraceExtraInfoId extra_id, const int64_t v_len, + const char *buf, const int64_t len, int64_t *pos, void *flt_info); +typedef int (*flt_serialize_func)(char *buf, const int64_t len, int64_t *pos, void *flt_info); +typedef int (*flt_get_serialize_size_func)(int32_t *size, void *flt_info); + +typedef struct st_fltfunc +{ + flt_deserialize_field_func deserialize_field_func; + flt_serialize_func serialize_func; + flt_get_serialize_size_func get_serialize_size_func; +} FLTFunc; + +typedef struct st_uuid +{ + union { + struct { + uint64_t low_; + uint64_t high_; + }; + struct { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; + }; + }; +} UUID; +DEFINE_TO_STRING_FUNC_FOR(UUID); + +enum enum_flt_tagtype{ + FLT_TAG_COMMAND_NAME = 0, + FLT_TAG_CLIENT_HOST = 1, + FLT_TAG_MAX_TYPE +}; + +typedef struct st_obtagctx ObTagCtx; +struct st_obtagctx +{ + ObTagCtx *next_; + uint16_t tag_type_; + const char *data_; +}; +DEFINE_TO_STRING_FUNC_FOR(ObTagCtx); + +typedef struct st_obspanctx +{ + uint16_t span_type_; + UUID span_id_; + struct st_obspanctx *source_span_; + my_bool is_follow_; + int64_t start_ts_; + int64_t end_ts_; + ObTagCtx *tags_; +} ObSpanCtx; +DEFINE_TO_STRING_FUNC_FOR(ObSpanCtx); + +struct st_obtrace +{ + uint64_t buffer_size_; + uint64_t offset_; + uint64_t seq_; + my_bool in_trans_; + my_bool trace_enable_; + my_bool force_print_; + my_bool slow_query_print_; + FLTInfo *flt; // point to flt struct + uint64_t uuid_random_seed[2]; + UUID trace_id_; + UUID root_span_id_; + LIST *current_span_list_; + LIST *free_span_list_; + ObSpanCtx *last_active_span_; + union { + uint8_t policy_; + struct { + uint8_t level_ : 7; + uint8_t auto_flush_ : 1; + }; + }; + uint64_t log_buf_offset_; + char *log_buf_; // 大小为:MAX_TRACE_LOG_SIZE + char *flt_serialize_buf_; // 大小为:MAX_FLT_SERIALIZE_SIZE + char data_[0]; // 多分配的空间存储free span以及方便后续打tag时使用 +}; + +int trace_init(); +void trace_end(); +void begin_trace(ObTrace *trace); +void end_trace(ObTrace *trace); +ObSpanCtx* begin_span(ObTrace *trace, uint32_t span_type, uint8_t level, my_bool is_follow); +void end_span(ObTrace *trace, ObSpanCtx *span); +// 当前在sql开始的时候需要调用reset接口将span清空, 防止事务中span过多 +void reset_span(ObTrace *trace); +void append_tag(ObTrace *trace, ObSpanCtx *span, uint16_t tag_type, const char *str); +ObTrace *get_trace_instance(FLTInfo *flt); +void flush_first_span(ObTrace *trace); +void flush_trace(ObTrace *trace); + +DEFINE_FLT_SERIALIZE_FUNC(appinfo); // FLT_APP_INFO +DEFINE_FLT_SERIALIZE_FUNC(queryinfo); // FLT_QUERY_INFO +DEFINE_FLT_SERIALIZE_FUNC(controlinfo); // FLT_CONTROL_INFO +DEFINE_FLT_SERIALIZE_FUNC(spaninfo); // FLT_SPAN_INFO +DEFINE_FLT_SERIALIZE_FUNC(driverspaninfo); // FLT_DRIVER_SPAN_INFO +DEFINE_FLT_SERIALIZE_FUNC(nosupport); // FLT_EXTRA_INFO_TYPE_END + +// 判断是否是合法的控制信息,如果不合法,不发送extra info +my_bool flt_is_vaild(FLTInfo *flt); + +int serialize_UUID(char *buf, const int64_t buf_len, int64_t* pos, UUID *uuid); +int deserialize_UUID(const char *buf, const int64_t buf_len, int64_t *pos, UUID *uuid); + +int uuid4_init(uint64_t *seed, size_t seed_size); +UUID uuid4_generate(uint64_t *seed); + +int flt_init(FLTInfo *flt); +void flt_end(FLTInfo *flt); +int flt_build_request(MYSQL *mysql, FLTInfo *flt); +void flt_set_send_trans_flag(FLTInfo *flt, my_bool send_flag); + +int flt_deserialize_extra_info(const char *buf, const int64_t len, int64_t *pos, FullLinkTraceExtraInfoType type, void *flt_info); +int flt_serialize_extra_info(char *buf, const int64_t len, int64_t *pos, void *flt_info); +int flt_get_serialize_size_extra_info(int32_t *size, void *flt_info); + +// for encode +int flt_store_type_and_len(char *buf, int64_t len, int64_t *pos, int16_t type, int32_t v_len); +int flt_store_str(char *buf, int64_t len, int64_t *pos, const char *str, const uint64_t str_len, int16_t type); + //@{Serialize integer data, save the data in v to the position of buf+pos, and update pos +int flt_store_int1(char *buf, int64_t len, int64_t *pos, int8_t v, int16_t type); +int flt_store_int2(char *buf, int64_t len, int64_t *pos, int16_t v, int16_t type); +int flt_store_int3(char *buf, int64_t len, int64_t *pos, int32_t v, int16_t type); +int flt_store_int4(char *buf, int64_t len, int64_t *pos, int32_t v, int16_t type); +int flt_store_int5(char *buf, int64_t len, int64_t *pos, int64_t v, int16_t type); +int flt_store_int6(char *buf, int64_t len, int64_t *pos, int64_t v, int16_t type); +int flt_store_int8(char *buf, int64_t len, int64_t *pos, int64_t v, int16_t type); +int flt_store_double(char *buf, const int64_t len, int64_t *pos, double val, int16_t type); +int flt_store_float(char *buf, const int64_t len, int64_t *pos, float val, int16_t type); + +int flt_get_store_str_size(const uint64_t str_len); +int flt_get_store_int1_size(); +int flt_get_store_int2_size(); +int flt_get_store_int3_size(); +int flt_get_store_int4_size(); +int flt_get_store_int5_size(); +int flt_get_store_int6_size(); +int flt_get_store_int8_size(); +int flt_get_store_double_size(); +int flt_get_store_float_size(); + +// for decode +int flt_resolve_type_and_len(const char *buf, int64_t len, int64_t *pos, int16_t *type, int32_t *v_len); +int flt_get_str(const char *buf, int64_t len, int64_t *pos, int64_t str_len, char **str); +//@{ Signed integer in reverse sequence, write the result to v, and update pos +int flt_get_int1(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int8_t *val); +int flt_get_int2(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int16_t *val); +int flt_get_int3(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int32_t *val); +int flt_get_int4(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int32_t *val); +int flt_get_int8(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int64_t *val); +int flt_get_double(const char *buf, int64_t len, int64_t *pos, int64_t v_len, double *val); +int flt_get_float(const char *buf, int64_t len, int64_t *pos, int64_t v_len, float *val); +#endif \ No newline at end of file diff --git a/include/ob_object.h b/include/ob_object.h new file mode 100644 index 0000000..fd0b8d0 --- /dev/null +++ b/include/ob_object.h @@ -0,0 +1,285 @@ +/* + Copyright (c) 2000, 2018, Oracle and/or its affiliates. + Copyright (c) 2009, 2019, MariaDB Corporation. + Copyright (c) 2021 OceanBase. + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef _ob_object_h +#define _ob_object_h + +#include +#include + +typedef enum enum_obobjtype +{ + ObNullType = 0, // 空类型 + + ObTinyIntType=1, // int8, aka mysql boolean type + ObSmallIntType=2, // int16 + ObMediumIntType=3, // int24 + ObInt32Type=4, // int32 + ObIntType=5, // int64, aka bigint + + ObUTinyIntType=6, // uint8 + ObUSmallIntType=7, // uint16 + ObUMediumIntType=8, // uint24 + ObUInt32Type=9, // uint32 + ObUInt64Type=10, // uint64 + + ObFloatType=11, // single-precision floating point + ObDoubleType=12, // double-precision floating point + + ObUFloatType=13, // unsigned single-precision floating point + ObUDoubleType=14, // unsigned double-precision floating point + + ObNumberType=15, // aka decimal/numeric + ObUNumberType=16, + + ObDateTimeType=17, + ObTimestampType=18, + ObDateType=19, + ObTimeType=20, + ObYearType=21, + + ObVarcharType=22, // charset: utf8mb4 or binary + ObCharType=23, // charset: utf8mb4 or binary + + ObHexStringType = 24, // hexadecimal literal, e.g. X'42', 0x42, b'1001', 0b1001 + + ObExtendType = 25, // Min, Max, NOP etc. + ObUnknownType = 26, // For question mark(?) in prepared statement, no need to serialize + // @note future new types to be defined here !!! + + ObTinyTextType = 27, + ObTextType = 28, + ObMediumTextType = 29, + ObLongTextType = 30, + + ObBitType = 31, + ObEnumType = 32, + ObSetType = 33, + ObEnumInnerType = 34, + ObSetInnerType = 35, + + ObTimestampTZType = 36, // timestamp with time zone for oracle + ObTimestampLTZType = 37, // timestamp with local time zone for oracle + ObTimestampNanoType = 38, // timestamp nanosecond for oracle + ObRawType = 39, // raw type for oracle + ObIntervalYMType = 40, // interval year to month + ObIntervalDSType = 41, // interval day to second + ObNumberFloatType = 42, // oracle float, subtype of NUMBER + ObNVarchar2Type = 43, // nvarchar2 + ObNCharType = 44, // nchar + ObURowIDType = 45, // UROWID + ObLobType = 46, // Oracle Lob + ObMaxType // invalid type, or count of obj type +} ObObjType; + +enum enum_obcollationtype +{ + CS_TYPE_INVALID = 0, + CS_TYPE_GBK_CHINESE_CI = 28, + CS_TYPE_UTF8MB4_GENERAL_CI = 45, + CS_TYPE_UTF8MB4_BIN = 46, + CS_TYPE_UTF16_GENERAL_CI = 54, + CS_TYPE_UTF16_BIN = 55, + CS_TYPE_BINARY = 63, + CS_TYPE_GBK_BIN = 87, + CS_TYPE_UTF16_UNICODE_CI = 101, + CS_TYPE_UTF8MB4_UNICODE_CI = 224, + CS_TYPE_GB18030_CHINESE_CI = 248, + CS_TYPE_GB18030_BIN = 249, + CS_TYPE_MAX, +} ObCollationType; + +enum enum_obcollationlevel +{ + CS_LEVEL_EXPLICIT = 0, + CS_LEVEL_NONE = 1, + CS_LEVEL_IMPLICIT = 2, + CS_LEVEL_SYSCONST = 3, + CS_LEVEL_COERCIBLE = 4, + CS_LEVEL_NUMERIC = 5, + CS_LEVEL_IGNORABLE = 6, + CS_LEVEL_INVALID, // here we didn't define CS_LEVEL_INVALID as 0, + // since 0 is a valid value for CS_LEVEL_EXPLICIT in mysql 5.6. + // fortunately we didn't need to use it to define array like charset_arr, + // and we didn't persist it on storage. +} ObCollationLevel; + +typedef struct st_obmysqltypemap +{ + /* oceanbase::common::ObObjType ob_type; */ + enum_field_types mysql_type; + uint16_t flags; /* flags if Field */ + uint64_t length; /* other than varchar type */ +} ObMySQLTypeMap; + +static const ObMySQLTypeMap type_maps_[ObMaxType] = +{ + /* ObMinType */ + {MYSQL_TYPE_NULL, BINARY_FLAG, 0}, /* ObNullType */ + {MYSQL_TYPE_TINY, BINARY_FLAG, 0}, /* ObTinyIntType */ + {MYSQL_TYPE_SHORT, BINARY_FLAG, 0}, /* ObSmallIntType */ + {MYSQL_TYPE_INT24, BINARY_FLAG, 0}, /* ObMediumIntType */ + {MYSQL_TYPE_LONG, BINARY_FLAG, 0}, /* ObInt32Type */ + {MYSQL_TYPE_LONGLONG, BINARY_FLAG, 0}, /* ObIntType */ + {MYSQL_TYPE_TINY, BINARY_FLAG | UNSIGNED_FLAG, 0}, /* ObUTinyIntType */ + {MYSQL_TYPE_SHORT, BINARY_FLAG | UNSIGNED_FLAG, 0}, /* ObUSmallIntType */ + {MYSQL_TYPE_INT24, BINARY_FLAG | UNSIGNED_FLAG, 0}, /* ObUMediumIntType */ + {MYSQL_TYPE_LONG, BINARY_FLAG | UNSIGNED_FLAG, 0}, /* ObUInt32Type */ + {MYSQL_TYPE_LONGLONG, BINARY_FLAG | UNSIGNED_FLAG, 0}, /* ObUInt64Type */ + {MYSQL_TYPE_FLOAT, BINARY_FLAG, 0}, /* ObFloatType */ + {MYSQL_TYPE_DOUBLE, BINARY_FLAG, 0}, /* ObDoubleType */ + {MYSQL_TYPE_FLOAT, BINARY_FLAG | UNSIGNED_FLAG, 0}, /* ObUFloatType */ + {MYSQL_TYPE_DOUBLE, BINARY_FLAG | UNSIGNED_FLAG, 0}, /* ObUDoubleType */ + {MYSQL_TYPE_NEWDECIMAL,BINARY_FLAG, 0}, /* ObNumberType */ + {MYSQL_TYPE_NEWDECIMAL,BINARY_FLAG | UNSIGNED_FLAG, 0}, /* ObUNumberType */ + {MYSQL_TYPE_DATETIME, BINARY_FLAG, 0}, /* ObDateTimeType */ + {MYSQL_TYPE_TIMESTAMP, BINARY_FLAG | TIMESTAMP_FLAG, 0}, /* ObTimestampType */ + {MYSQL_TYPE_DATE, BINARY_FLAG, 0}, /* ObDateType */ + {MYSQL_TYPE_TIME, BINARY_FLAG, 0}, /* ObTimeType */ + {MYSQL_TYPE_YEAR, UNSIGNED_FLAG | ZEROFILL_FLAG, 0}, /* ObYearType */ + {MYSQL_TYPE_VAR_STRING, 0, 0}, /* ObVarcharType */ + {MYSQL_TYPE_STRING, 0, 0}, /* ObCharType */ + {MYSQL_TYPE_VAR_STRING,BINARY_FLAG, 0}, /* ObHexStringType */ + {MAX_NO_FIELD_TYPES, 0, 0}, /* ObExtendType */ + {MAX_NO_FIELD_TYPES, 0, 0}, /* ObUnknownType */ + /* ObMaxType */ +}; + +int get_mysql_type(ObObjType ob_type, enum_field_types *mysql_type); + +int get_ob_type(ObObjType *ob_type, enum_field_types mysql_type); + +typedef struct st_obobjmeta +{ + uint8_t type_; + uint8_t cs_level_; // collation level + uint8_t cs_type_; // collation type + int8_t scale_; // scale +} ObObjMeta; + +typedef union un_obobjvalue +{ + int64_t int64_; + uint64_t uint64_; + + float float_; + double double_; + + const char *string_; + + uint32_t *nmb_digits_; + + int64_t datetime_; + int32_t date_; + int64_t time_; + uint8_t year_; + + int64_t ext_; + int64_t unknown_; +} ObObjValue; + +typedef struct st_obobj +{ + ObObjMeta meta_; // sizeof = 4 + // union + // { + // int32_t val_len_; + // number::ObNumber::Desc nmb_desc_; + // ObOTimestampData::UnionTZCtx time_ctx_; + // }; + int32_t val_len_; // sizeof = 4 + ObObjValue v_; // sizeof = 8 + +} ObObj; + +/* obj serialize/deserialize function */ +typedef int (*ob_obj_value_serialize)(const ObObj *obj, char *buf, const int64_t buf_len, int64_t *pos); +typedef int (*ob_obj_value_deserialize)(ObObj *obj, const char *buf, const int64_t data_len, int64_t *pos); +typedef int64_t (*ob_obj_value_get_serialize_size)(const ObObj *obj); + +typedef struct obobjtypefuncs +{ + ob_obj_value_serialize serialize; + ob_obj_value_deserialize deserialize; + ob_obj_value_get_serialize_size get_serialize_size; +} ObObjTypeFuncs; + + +#define DECLAR_SERIALIZE_FUNCS(OBJTYPE) \ + int obj_val_serialize_##OBJTYPE(const ObObj *obj, char *buf, const int64_t buf_len, int64_t *pos); \ + int obj_val_deserialize_##OBJTYPE(ObObj *obj, const char *buf, const int64_t data_len, int64_t *pos); \ + int64_t obj_val_get_serialize_size_##OBJTYPE(const ObObj *obj); \ + +DECLAR_SERIALIZE_FUNCS(ObNullType); +// ObTinyIntType=1, // int8, aka mysql boolean type +DECLAR_SERIALIZE_FUNCS(ObTinyIntType); +// ObSmallIntType=2, // int16 +DECLAR_SERIALIZE_FUNCS(ObSmallIntType); +// ObMediumIntType=3, // int24 +DECLAR_SERIALIZE_FUNCS(ObMediumIntType); +// ObInt32Type=4, // int32 +DECLAR_SERIALIZE_FUNCS(ObInt32Type); +// ObIntType=5, // int64, aka bigint +DECLAR_SERIALIZE_FUNCS(ObIntType); +// ObUTinyIntType=6, // uint8 +DECLAR_SERIALIZE_FUNCS(ObUTinyIntType); +// ObUSmallIntType=7, // uint16 +DECLAR_SERIALIZE_FUNCS(ObUSmallIntType); +// ObUMediumIntType=8, // uint24 +DECLAR_SERIALIZE_FUNCS(ObUMediumIntType); +// ObUInt32Type=9, // uint32 +DECLAR_SERIALIZE_FUNCS(ObUInt32Type); +// ObUInt64Type=10, // uint64 +DECLAR_SERIALIZE_FUNCS(ObUInt64Type); +// ObFloatType=11, // single-precision floating point +DECLAR_SERIALIZE_FUNCS(ObFloatType); +// ObDoubleType=12, // double-precision floating point +DECLAR_SERIALIZE_FUNCS(ObDoubleType); +// ObUFloatType=13, // unsigned single-precision floating point +DECLAR_SERIALIZE_FUNCS(ObUFloatType); +// ObUDoubleType=14, // unsigned double-precision floating point +DECLAR_SERIALIZE_FUNCS(ObUDoubleType); +// ObVarcharType=22 // charset: utf8mb4 or binary +DECLAR_SERIALIZE_FUNCS(ObVarcharType) + +void set_tinyint(ObObj *obj, const int8_t value); +void set_tinyint_value(ObObj *obj, const int8_t value); +void set_smallint(ObObj *obj, const int16_t value); +void set_smallint_value(ObObj *obj, const int16_t value); +void set_mediumint(ObObj *obj, const int32_t value); +void set_int32(ObObj *obj, const int32_t value); +void set_int32_value(ObObj *obj, const int32_t value); +void set_int(ObObj *obj, const int64_t value); +void set_int_value(ObObj *obj, const int64_t value); +void set_utinyint(ObObj *obj, const uint8_t value); +void set_usmallint(ObObj *obj, const uint16_t value); +void set_umediumint(ObObj *obj, const uint32_t value); +void set_uint32(ObObj *obj, const uint32_t value); +void set_uint64(ObObj *obj, const uint64_t value); +void set_float(ObObj *obj, const float value); +void set_float_value(ObObj *obj, const float value); +void set_ufloat(ObObj *obj, const float value); +void set_double(ObObj *obj, const double value); +void set_double_value(ObObj *obj, const double value); +void set_udouble(ObObj *obj, const double value); +void set_varchar(ObObj *obj, const char *ptr, int32_t size); + +int serialize_ObObj(const ObObj *obj, char *buf, const int64_t buf_len, int64_t *pos); +int deserialize_ObObj(ObObj *obj, const char *buf, const int64_t data_len, int64_t *pos); +int64_t get_serialize_size_ObObj(const ObObj *obj); +#endif \ No newline at end of file diff --git a/include/ob_oralce_format_models.h b/include/ob_oralce_format_models.h new file mode 100644 index 0000000..1f9f0e0 --- /dev/null +++ b/include/ob_oralce_format_models.h @@ -0,0 +1,244 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ +#include "mysql.h" +#include + +#define DATE_PART_CNT 3 +#define TIME_PART_CNT 4 +#define OTHER_PART_CNT 4 +#define DATETIME_PART_CNT (DATE_PART_CNT + TIME_PART_CNT) +#define ORACLE_DATE_PART_CNT (DATE_PART_CNT + TIME_PART_CNT - 1) +#define TOTAL_PART_CNT (DATETIME_PART_CNT + OTHER_PART_CNT) +#define DT_YEAR 0 +#define DT_MON 1 +#define DT_MDAY 2 + +#define DT_HOUR 3 +#define DT_MIN 4 +#define DT_SEC 5 +#define DT_USEC 6 + +#define DT_DATE 7 +#define DT_YDAY 8 +#define DT_WDAY 9 +#define DT_OFFSET_MIN 10 + +#define OB_MAX_TZ_ABBR_LEN 32 +#define OB_MAX_TZ_NAME_LEN 64 + +//18446744073709551615 +#define MAX_UINT64_STR_LEN 20 +#define MAX_DIGITS10_STR_SIZE MAX_UINT64_STR_LEN + 3 + +#define DT_TYPE_DATE (1UL << 0) +#define DT_TYPE_TIME (1UL << 1) +#define DT_WEEK_SUN_BEGIN (1UL << 5) // sunday is the first day of week, otherwise monday. +#define DT_WEEK_ZERO_BEGIN (1UL << 6) // week num will begin with 0, otherwise 1. +#define DT_WEEK_GE_4_BEGIN (1UL << 7) // week which has 4 or more days is week 1, otherwise has +#define DT_TYPE_ORACLE (1UL << 8) //oracle timestamp to nanosecond (nano, tz, ltz) + // the first sunday of monday. +#define DT_TYPE_TIMEZONE (1UL << 10) //oracle timestamp with time zone (tz) + +#define DT_TYPE_DATETIME (DT_TYPE_DATE | DT_TYPE_TIME) + +#define IS_SUN_BEGIN(mode) ((DT_WEEK_SUN_BEGIN & (mode)) ? 1 : 0) +#define IS_ZERO_BEGIN(mode) ((DT_WEEK_ZERO_BEGIN & (mode)) ? 1 : 0) +#define IS_GE_4_BEGIN(mode) ((DT_WEEK_GE_4_BEGIN & (mode)) ? 1 : 0) +#define HAS_TYPE_ORACLE(mode) ((DT_TYPE_ORACLE & (mode)) ? 1 : 0) +#define HAS_TYPE_TIMEZONE(mode) ((DT_TYPE_TIMEZONE & (mode)) ? 1 : 0) + +#define DAYS_PER_WEEK 7 +#define MONS_PER_YEAR 12 +#define SECS_PER_MIN 60 +#define MINS_PER_HOUR 60 + +typedef uint64_t ObDTMode; + +enum ElementFlag +{ + INVALID_FLAG = -1, + AD = 0, + AD2, //A.D. + BC, + BC2, //B.C. + CC, + SCC, + D, + DAY, + DD, + DDD, + DY, + FF1, + FF2, + FF3, + FF4, + FF5, + FF6, + FF7, + FF8, + FF9, + FF, + HH, + HH24, + HH12, + IW, + I, + IY, + IYY, + IYYY, + MI, + MM, + MONTH, + MON, + AM, + AM2, //A.M. + PM, + PM2, //P.M. + Q, + RR, + RRRR, + SS, + SSSSS, + WW, + W, + YGYYY, + YEAR, + SYEAR, + YYYY, + SYYYY, + YYY, + YY, + Y, + DS, + DL, + TZH, + TZM, + TZD, + TZR, + X, + J, + ///<<< !!!add any flag before this line!!! + MAX_FLAG_NUMBER +}; + +enum ElementGroup +{ + RUNTIME_CONFLICT_SOLVE_GROUP = -2, + NON_CONFLICT_GROUP = -1, + ///<<< conflict in group, before this line, will be ignored + NEVER_APPEAR_GROUP = 0, //the element should never appear + YEAR_GROUP, //include : SYYYY YYYY YYY YY Y YGYYY RR RRRR + MERIDIAN_INDICATOR_GROUP, //include : AM PM + WEEK_OF_DAY_GROUP, //include : D Day Dy + ERA_GROUP, //include : AD BC + HOUR_GROUP, //include : HH HH12 HH24 + MONTH_GROUP, //include : MONTH MON MM + DAY_OF_YEAR_GROUP, //include : DDD, J + ///<<< !!!add any flag before this line!!! + MAX_CONFLICT_GROUP_NUMBER +}; + +struct ObTimeConstStr { + const char *ptr_; + int32_t len_; +}; + +struct ObDFMParseCtx +{ + const char* fmt_str_; + const char* cur_ch_; + int64_t remain_len_; + + //the following values are only used in function str_to_ob_time_oracle_dfm + int64_t expected_elem_flag_; + my_bool is_matching_by_expected_len_; //only used for match_int_value +}; + +enum UpperCaseMode { + NON_CHARACTER, + ONLY_FIRST_CHARACTER, + ALL_CHARACTER +}; + +struct ObDFMElem +{ + int64_t elem_flag_; //flag from enum ObDFMFlag + int64_t offset_; //offset in origin format string + my_bool is_single_dot_before_; //for the dot before FF + enum UpperCaseMode upper_case_mode_; +}; + +struct ObTime +{ + uint64_t mode_; + int32_t parts_[TOTAL_PART_CNT]; + // year: [1000, 9999]. + // month: [1, 12]. + // day: [1, 31]. + // hour: [0, 23] or [0, 838] if it is a time. + // minute: [0, 59]. + // second: [0, 59]. + // usecond: [0, 1000000], 1000000 can only valid after str_to_ob_time, for round. + // nanosecond: [0, 1000000000], when HAS_TYPE_ORACLE(mode_) + // date: date value, day count since 1970-1-1. + // year day: [1, 366]. + // week day: [1, 7], 1 means monday, 7 means sunday. + // offset minute: [-12*60, 14*60]. + + char tz_name_[OB_MAX_TZ_NAME_LEN]; + char tzd_abbr_[OB_MAX_TZ_ABBR_LEN];//the abbr of time zone region with Daylight Saving Time + int32_t time_zone_id_; + uint8_t nano_scale_; + my_bool is_tz_name_valid_; +}; + +struct ObFastFormatInt +{ + char buf_[MAX_DIGITS10_STR_SIZE]; + char *ptr_; + int64_t len_; +}; + +struct ObOracleTimeLimiter +{ + int32_t MIN; + int32_t MAX; + int ERROR_CODE; +}; +longlong strtoll10(const char *nptr, char **endptr, int *error); +int calculate_str_oracle_dfm_length(const struct ObTime *ob_time, + const char *fmt_str, const int64_t fmt_len, + int16_t scale, int64_t *len); + +int ob_time_to_str_oracle_dfm(const struct ObTime *ob_time, + const char *fmt_str, const int64_t fmt_len, + int16_t scale, + char *buf, int64_t buf_len, + int64_t *pos); + +int32_t ob_time_to_date(struct ObTime *ob_time); + +int str_to_ob_time_oracle_dfm(const char *str, const int64_t str_len, + const char *fmt_str, const int64_t fmt_len, + struct ObTime *ob_time, + int16_t scale); diff --git a/include/ob_protocol20.h b/include/ob_protocol20.h new file mode 100644 index 0000000..8971444 --- /dev/null +++ b/include/ob_protocol20.h @@ -0,0 +1,155 @@ +/* + Copyright (c) 2000, 2018, Oracle and/or its affiliates. + Copyright (c) 2009, 2019, MariaDB Corporation. + Copyright (c) 2021 OceanBase. + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ +#ifndef _OB_PROTOCOL20_H +#define _OB_PROTOCOL20_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEFINE_OB20_EXTRAINFO_SERIALIZE_FUNC(type) \ + int extrainfo_serialize_##type(char *buf, const int64_t len, int64_t *pos, void *data); \ + int extrainfo_deserialize_##type(const char *buf, const int64_t len, int64_t *pos, void *data, const int64_t v_len);\ + int64_t extrainfo_get_serialize_size_##type(void *data) + +#define OB20_EXTRAINFO_SERIALIZE_FUNC(type) \ + { \ + extrainfo_serialize_##type, \ + extrainfo_deserialize_##type, \ + extrainfo_get_serialize_size_##type \ + } + +typedef union st_ob20protocol_flags +{ + uint32_t flags; + struct Protocol20Flags + { + uint32_t OB_EXTRA_INFO_EXIST: 1; + uint32_t OB_IS_LAST_PACKET: 1; + uint32_t OB_IS_PROXY_REROUTE: 1; + uint32_t OB_IS_NEW_EXTRA_INFO: 1; + uint32_t OB_FLAG_RESERVED_NOT_USE: 28; + } st_flags; +} Ob20ProtocolFlags; + +typedef struct st_ob20protocol_header +{ + uint16_t magic_num; + uint16_t version; + uint32_t connection_id; + uint32_t request_id; + uint8_t pkt_seq; + uint32_t payload_len; + Ob20ProtocolFlags flag; + uint16_t reserved; + uint16_t header_checksum; +} Ob20ProtocolHeader; + +typedef struct st_ob20protocol_extra_info_list +{ + LIST *list, + *current; +} Ob20ProtocolExtraInfoList; + +/** +0 ~ 999用于ob driver 私有 +1001~1999用于ob proxy私有 +2001~65535用于server, driver, proxy共有 + */ +typedef enum em_extrainfokeytype { + OB20_DRIVER_END = 1000, + OB20_PROXY_END = 2000, + TRACE_INFO = 2001, + SESS_INFO = 2002, + FULL_TRC = 2003, + OB20_SVR_END, +} ExtraInfoKeyType; + +typedef struct st_ob20protocol_extra_info +{ + ExtraInfoKeyType key; + void *value; +} Ob20ProtocolExtraInfo; + +typedef struct st_ob20protocol +{ + Ob20ProtocolHeader header; + Ob20ProtocolExtraInfoList extra_info_list; + unsigned int checksum; + + // ma_net write buffer + uchar *real_write_buffer; + uint32_t real_write_buffer_length; + + // extra info + FLTInfo *flt; // full link trace +} Ob20Protocol; + +typedef int (*extrainfo_serialize_func)(char *buf, const int64_t len, int64_t *pos, void *data); +typedef int (*extrainfo_deserialize_func)(const char *buf, const int64_t len, int64_t *pos, void *data, const int64_t v_len); +typedef int64_t (*extrainfo_get_serialize_size_func)(void *data); + +typedef struct st_extrainfo_serialize_func +{ + extrainfo_serialize_func serialize_func; + extrainfo_deserialize_func deserialize_func; + extrainfo_get_serialize_size_func get_serialize_size_func; +} ExtraInfoSerializeFunc; + +DEFINE_OB20_EXTRAINFO_SERIALIZE_FUNC(flt); // full link trace + +void update_request_id(uint32_t *request_id); + +int ob20_init(Ob20Protocol *ob20protocol, unsigned long conid, my_bool use_flt); +void ob20_end(Ob20Protocol *ob20protocol); + +void clear_extra_info(Ob20Protocol *ob20protocol); + +size_t get_protocol20_extra_info_length(Ob20Protocol *ob20protocol); + +uchar *fill_protocol20_extra_info(Ob20Protocol *ob20protocol, uchar *buffer, size_t buffer_len); + +void init_protocol20_header(Ob20Protocol *ob20protocol); + +void update_protocol20_header(Ob20Protocol *ob20protocol); + +uchar *fill_protocol20_header(Ob20Protocol *ob20protocol, size_t len, size_t pkt_nr, size_t complen,uchar *buffer); + +int decode_protocol20_header(Ob20Protocol *ob20protocol, uchar *buffer, uint32_t pkt_len, uint32_t pkt_nr, uint32_t complen); + +int decode_protocol20_extra_info(Ob20Protocol *ob20protocol, uchar *buffer); + +int ob20_set_extra_info(MYSQL *mysql, ExtraInfoKeyType key, void *value); + +int flt_set_module(MYSQL *mysql, const char *module_name); +int flt_set_action(MYSQL *mysql, const char *action_name); +int flt_set_client_info(MYSQL *mysql, const char *client_info); +int flt_set_identifier(MYSQL *mysql, const char *identifier); + +int flt_get_control_level(MYSQL *mysql, int *level); +int flt_get_control_sample_pct(MYSQL *mysql, double *sample_pct); +int flt_get_control_record_policy(MYSQL *mysql, int *rp); +int flt_get_control_print_spct(MYSQL *mysql, double *sample_pct); +int flt_get_control_slow_threshold(MYSQL *mysql, long int *slow_threshold); +#endif \ No newline at end of file diff --git a/include/ob_rwlock.h b/include/ob_rwlock.h new file mode 100644 index 0000000..3cedc16 --- /dev/null +++ b/include/ob_rwlock.h @@ -0,0 +1,87 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ + +#ifndef _OB_RWLOCK_H +#define _OB_RWLOCK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#include + +typedef CRITICAL_SECTION ob_mutex_t; +typedef int ob_mutexattr_t; +typedef struct st_my_rw_lock_t +{ + SRWLOCK ob_srwlock; + BOOL is_exclusive; +} ob_rw_lock_t; +#define OB_RW_INITIALIZER {SRWLOCK_INIT, FALSE} +#else +#include +typedef pthread_mutexattr_t ob_mutexattr_t; +typedef pthread_mutex_t ob_mutex_t; +typedef pthread_rwlock_t ob_rw_lock_t; +#define OB_RW_INITIALIZER PTHREAD_RWLOCK_INITIALIZER +#endif +typedef ob_mutex_t native_mutex_t; +typedef ob_mutexattr_t native_mutexattr_t; +typedef ob_rw_lock_t native_rw_lock_t; + +#ifndef NATIVE_RW_INITIALIZER +#define NATIVE_RW_INITIALIZER OB_RW_INITIALIZER +#endif +/* Compatible with previous functions */ +#define native_mutex_init ob_mutex_init +#define native_mutex_lock ob_mutex_lock +#define native_mutex_trylock ob_mutex_trylock +#define native_mutex_unlock ob_mutex_unlock +#define native_mutex_destroy ob_mutex_destroy +#define native_rw_init ob_rw_init +#define native_rw_rdlock ob_rw_rdlock +#define native_rw_tryrdlock ob_rw_tryrdlock +#define native_rw_wrlock ob_rw_wrlock +#define native_rw_trywrlock ob_rw_trywrlock +#define native_rw_destroy ob_rw_destroy +#define native_rw_unlock ob_rw_unlock + +int ob_mutex_init(ob_mutex_t *mutex, const ob_mutexattr_t *attr); +int ob_mutex_lock(ob_mutex_t *mutex); +int ob_mutex_trylock(ob_mutex_t *mutex); +int ob_mutex_unlock(ob_mutex_t *mutex); +int ob_mutex_destroy(ob_mutex_t *mutex); +int ob_rw_init(ob_rw_lock_t *rwp); +int ob_rw_destroy(ob_rw_lock_t *rwp); +int ob_rw_rdlock(ob_rw_lock_t *rwp); +int ob_rw_tryrdlock(ob_rw_lock_t *rwp); +int ob_rw_wrlock(ob_rw_lock_t *rwp); +int ob_rw_trywrlock(ob_rw_lock_t *rwp); +int ob_rw_unlock(ob_rw_lock_t *rwp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/ob_serialize.h b/include/ob_serialize.h new file mode 100644 index 0000000..060b544 --- /dev/null +++ b/include/ob_serialize.h @@ -0,0 +1,151 @@ +/* + Copyright (c) 2000, 2018, Oracle and/or its affiliates. + Copyright (c) 2009, 2019, MariaDB Corporation. + Copyright (c) 2021 OceanBase. + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef _ob_serialize_h +#define _ob_serialize_h + +#include +#include +#include +#include + +#include "ma_global.h" + + +inline int encode_i64(char *buf, const int64_t buf_len, int64_t *ppos, int64_t val); +inline int decode_i64(const char *buf, const int64_t data_len, int64_t *ppos, int64_t *val); + +inline int64_t encoded_length_vi64(int64_t val); + +/** + * @brief Encode a integer (up to 64bit) in variable length encoding + * + * @param buf pointer to the destination buffer + * @param end the end pointer to the destination buffer + * @param val value to encode + * + * @return true - success, false - failed + */ +inline int encode_vi64(char *buf, const int64_t buf_len, int64_t *ppos, int64_t val); + +inline int decode_vi64(const char *buf, const int64_t data_len, int64_t *ppos, int64_t *val); + +inline int64_t encoded_length_vi32(int32_t val); + +/** + * @brief Encode a integer (up to 32bit) in variable length encoding + * + * @param buf pointer to the destination buffer + * @param end the end pointer to the destination buffer + * @param val value to encode + * + * @return true - success, false - failed + */ + +inline int encode_vi32(char *buf, const int64_t buf_len, int64_t *ppos, int32_t val); + +inline int decode_vi32(const char *buf, const int64_t data_len, int64_t *ppos, int32_t *val); + +inline int encode_i8(char *buf, const int64_t buf_len, int64_t *ppos, int8_t val); + +inline int decode_i8(const char *buf, const int64_t data_len, int64_t *ppos, int8_t *val); + +inline int encode_int64_t(char *buf, const int64_t buf_len, int64_t *pos, int64_t val); +inline int encode_uint64_t(char *buf, const int64_t buf_len, int64_t *pos, uint64_t val); +inline int encode_int32_t(char *buf, const int64_t buf_len, int64_t *pos, int32_t val); +inline int encode_uint32_t(char *buf, const int64_t buf_len, int64_t *pos, uint32_t val); +inline int encode_int16_t(char *buf, const int64_t buf_len, int64_t *pos, int16_t val); +inline int encode_uint16_t(char *buf, const int64_t buf_len, int64_t *pos, uint16_t val); +inline int encode_int8_t(char *buf, const int64_t buf_len, int64_t *pos, int8_t val); +inline int encode_uint8_t(char *buf, const int64_t buf_len, int64_t *pos, uint8_t val); + +inline int decode_int8_t(const char *buf, const int64_t data_len, int64_t *pos, int8_t *val); +inline int decode_uint8_t(const char *buf, const int64_t data_len, int64_t *pos, uint8_t *val); +inline int decode_int16_t(const char *buf, const int64_t data_len, int64_t *pos, int16_t *val); +inline int decode_uint16_t(const char *buf, const int64_t data_len, int64_t *pos, uint16_t *val); +inline int decode_int32_t(const char *buf, const int64_t data_len, int64_t *pos, int32_t *val); +inline int decode_uint32_t(const char *buf, const int64_t data_len, int64_t *pos, uint32_t *val); +inline int decode_int64_t(const char *buf, const int64_t data_len, int64_t *pos, int64_t *val); +inline int decode_uint64_t(const char *buf, const int64_t data_len, int64_t *pos, uint64_t *val); + +inline int64_t encoded_length_int64_t(int64_t val); +inline int64_t encoded_length_uint64_t(uint64_t val); +inline int64_t encoded_length_int32_t(int32_t val); +inline int64_t encoded_length_uint32_t(uint32_t val); +inline int64_t encoded_length_int16_t(int16_t val); +inline int64_t encoded_length_uint16_t(uint16_t val); +inline int64_t encoded_length_int8_t(int8_t unused); +inline int64_t encoded_length_uint8_t(uint8_t unused); + +inline int64_t encoded_length_float(float val); +inline int encode_float(char *buf, const int64_t buf_len, int64_t *pos, float val); +inline int decode_float(const char *buf, const int64_t data_len, int64_t *pos, float *val); +inline int64_t encoded_length_double(double val); +inline int encode_double(char *buf, const int64_t buf_len, int64_t *pos, double val); +inline int decode_double(const char *buf, const int64_t data_len, int64_t *pos, double *val); + +/** + * @brief Computes the encoded length of vstr(int64,data,null) + * + * @param len string length + * + * @return the encoded length of str + */ +inline int64_t encoded_length_vstr_with_len(int64_t len); +inline int64_t encoded_length_vstr(const char *str); + +/** + * @brief get the decoded length of data len of vstr + * won't change the pos + * @return the length of data + */ +inline int64_t decoded_length_vstr(const char *buf, const int64_t data_len, int64_t pos); + +/** + * @brief Encode a buf as vstr(int64,data,null) + * + * @param buf pointer to the destination buffer + * @param vbuf pointer to the start of the input buffer + * @param len length of the input buffer + */ +inline int encode_vstr_with_len(char *buf, const int64_t buf_len, int64_t *ppos, const void *vbuf, + int64_t len); + +inline int encode_vstr(char *buf, const int64_t buf_len, int64_t *ppos, const char *s); + +inline const char *decode_vstr_nocopy(const char *buf, const int64_t data_len, int64_t *ppos, int64_t *lenp); + +inline const char *decode_vstr(const char *buf, const int64_t data_len, int64_t *ppos, + char *dest, int64_t buf_len, int64_t *lenp); + +#define OB_UNIS_ENCODE(obj, type) \ + if (OB_SUCC(ret)) { \ + if (OB_FAIL(encode_##type(buf, buf_len, pos, obj))) { \ + } \ + } + +#define OB_UNIS_DECODE(obj, type) \ + if (OB_SUCC(ret) && *pos < data_len) { \ + if (OB_FAIL(decode_##type(buf, data_len, pos, &obj))) { \ + } \ + } + +#define OB_UNIS_ADD_LEN(obj, type) \ + len += encoded_length_##type(obj) + +#endif \ No newline at end of file diff --git a/include/ob_thread.h b/include/ob_thread.h new file mode 100644 index 0000000..fbaedf9 --- /dev/null +++ b/include/ob_thread.h @@ -0,0 +1,115 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ + +#ifndef _OB_THREAD_H +#define _OB_THREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#define NATIVE_RW_INITIALIZER {SRWLOCK_INIT, FALSE} +#include +//#include +//#include +#else +#include +#ifndef NATIVE_RW_INITIALIZER +#define NATIVE_RW_INITIALIZER PTHREAD_RWLOCK_INITIALIZER +#endif +#endif + +/* Compatible with previous thread defines*/ +#define MY_THREAD_CREATE_JOINABLE OB_THREAD_CREATE_JOINABLE +#define MY_THREAD_CREATE_DETACHED OB_THREAD_CREATE_DETACHED +#define MY_THREAD_ONCE_INIT OB_THREAD_ONCE_INIT +#define MY_THREAD_ONCE_INPREGRESS OB_THREAD_ONCE_INPROGRESS +#define MY_THREAD_ONCE_DONE OB_THREAD_ONCE_DONE +#define my_thread_self ob_thread_self + +#ifdef _WIN32 +typedef DWORD ob_thread_t; +typedef volatile LONG ob_thread_once_t; +typedef struct thread_attr +{ + DWORD ob_thread_stack_size; + int ob_thead_state; +} ob_thread_attr_t; + +typedef void * (__cdecl *ob_start_routine)(void *); + +struct ob_thread_start_param +{ + ob_start_routine func; + void *arg; +}; + +unsigned int __stdcall ob_win_thread_start(void *p); +#define OB_THREAD_CREATE_JOINABLE 0 +#define OB_THREAD_CREATE_DETACHED 1 +#define OB_THREAD_ONCE_INIT 0 +#define OB_THREAD_ONCE_INPROGRESS 1 +#define OB_THREAD_ONCE_DONE 2 +#else +typedef pthread_once_t ob_thread_once_t; +typedef pthread_t ob_thread_t; +typedef pthread_attr_t ob_thread_attr_t; +#define OB_THREAD_CREATE_JOINABLE PTHREAD_CREATE_JOINABLE +#define OB_THREAD_CREATE_DETACHED PTHREAD_CREATE_DETACHED +typedef void *(*ob_start_routine)(void *); +#define OB_THREAD_ONCE_INIT PTHREAD_ONCE_INIT +#endif + +typedef ob_thread_once_t my_thread_once_t; +typedef ob_thread_t my_thread_t; +typedef ob_thread_attr_t my_thread_attr_t; + +typedef struct st_ob_thread_handle +{ +#ifdef _WIN32 + HANDLE handle; +#endif + ob_thread_t thread; +} ob_thread_handle; + +ob_thread_t ob_thread_self(); + +int ob_thread_equal(ob_thread_t ob_thread1, ob_thread_t ob_thread2); +int ob_thread_attr_init(ob_thread_attr_t *ob_thread_attr); +int ob_thread_attr_destroy(ob_thread_attr_t *ob_thread_attr); +int ob_thread_attr_setstacksize(ob_thread_attr_t *ob_thread_attr, size_t ob_stacksize); +int ob_thread_attr_setdetachstate(ob_thread_attr_t *ob_thread_attr, int ob_detachstate); +int ob_thread_attr_getstacksize(ob_thread_attr_t *ob_thread_attr, size_t *ob_stacksize); +void ob_thread_yield(); + +int ob_thread_once(ob_thread_once_t *ob_once_control, void (*ob_routine)(void)); +int ob_thread_create(ob_thread_handle *ob_thread, const ob_thread_attr_t *ob_thread_attr, ob_start_routine ob_thread_func, void *ob_thread_arg); +int ob_thread_join(ob_thread_handle *ob_thread, void **ob_join_ptr); +int ob_thread_cancel(ob_thread_handle *ob_thread); +void ob_thread_exit(void *ob_thread_exit_ptr); + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/include/ob_thread_key.h b/include/ob_thread_key.h new file mode 100644 index 0000000..413cdc9 --- /dev/null +++ b/include/ob_thread_key.h @@ -0,0 +1,48 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ + +#ifndef _OB_THREAD_KEY_H +#define _OB_THREAD_KEY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#include +typedef DWORD ob_thread_key_t; +#else +#include +typedef pthread_key_t ob_thread_key_t; +#endif + +int ob_create_thread_key(ob_thread_key_t *ob_key, void (*ob_destructor)(void *)); +int ob_delete_thread_key(ob_thread_key_t ob_key); +void *ob_get_thread_key(ob_thread_key_t ob_key); +int ob_set_thread_key(ob_thread_key_t ob_key, void *value); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index ab48d29..d47d760 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -56,6 +56,7 @@ SET(MYSQL_LIB_SYMBOLS mysql_fetch_field mysql_fetch_field_direct mysql_fetch_fields + mysql_fetch_params mysql_fetch_lengths mysql_fetch_row mysql_field_count @@ -131,8 +132,22 @@ SET(MYSQL_LIB_SYMBOLS mysql_stmt_errno mysql_stmt_error mysql_stmt_execute + get_support_send_fetch_flag + get_support_plarray_bindbyname + get_use_prepare_execute + get_use_preapre_execute + mysql_stmt_prepare_v2 + mysql_stmt_execute_v2 + has_added_user_fields + is_returning_result + stmt_pre_exe_req_ext_flag_set + is_pl_out_result + ob_time_to_date mysql_stmt_fetch mysql_stmt_fetch_column + mysql_stmt_fetch_oracle_cursor + mysql_stmt_fetch_oracle_implicit_cursor + mysql_stmt_fetch_oracle_buffered_result mysql_stmt_field_count mysql_stmt_free_result mysql_stmt_init @@ -148,6 +163,8 @@ SET(MYSQL_LIB_SYMBOLS mysql_stmt_row_seek mysql_stmt_row_tell mysql_stmt_send_long_data + mysql_stmt_send_piece_data + mysql_stmt_read_piece_data mysql_stmt_sqlstate mysql_stmt_store_result mysql_store_result @@ -255,6 +272,71 @@ SET(MARIADB_NONBLOCK_SYMBOLS mysql_stmt_store_result_start mysql_store_result_cont mysql_store_result_start +#oci use + skip_param_complex + convert_type_to_complex + ob_time_to_date + ob_time_to_str_oracle_dfm + str_to_ob_time_oracle_dfm + hash_delete + hash_insert + hash_search + hash_element + hash_free + mysql_extension_init + _hash_init +# thread + ob_thread_self + ob_thread_equal + ob_thread_attr_init + ob_thread_attr_destroy + ob_thread_attr_setstacksize + ob_thread_attr_setdetachstate + ob_thread_attr_getstacksize + ob_thread_yield + ob_thread_create + ob_thread_join + ob_thread_once + ob_thread_cancel + ob_thread_exit +# ob20 + ob20_set_extra_info +# full_link_trace + flt_set_module + flt_set_action + flt_set_client_info + flt_set_identifier + flt_get_control_level + flt_get_control_sample_pct + flt_get_control_record_policy + flt_get_control_print_spct + flt_get_control_slow_threshold + + flt_deserialize_extra_info + flt_serialize_extra_info + flt_get_serialize_size_extra_info + ob_cond_init + ob_cond_destroy + ob_cond_timedwait + ob_cond_wait + ob_cond_signal + ob_cond_broadcast + ob_mutex_init + ob_mutex_lock + ob_mutex_trylock + ob_mutex_unlock + ob_mutex_destroy + ob_rw_init + ob_rw_destroy + ob_rw_rdlock + ob_rw_tryrdlock + ob_rw_wrlock + ob_rw_trywrlock + ob_rw_unlock + ob_create_thread_key + ob_delete_thread_key + ob_get_thread_key + ob_set_thread_key ) # handle static plugins @@ -294,6 +376,18 @@ ma_stmt_codec.c ma_string.c ma_dtoa.c mariadb_rpl.c +ob_protocol20.c +ob_bitmap.c +ob_complex.c +ob_strtoll10.c +ob_oracle_format_models.c +ob_thread.c +ob_object.c +ob_full_link_trace.c +ob_serialize.c +ob_rwlock.c +ob_cond.c +ob_thread_key.c ${CC_BINARY_DIR}/libmariadb/ma_client_plugin.c ma_io.c ${SSL_SOURCES} diff --git a/libmariadb/ma_hash.c b/libmariadb/ma_hash.c index a79130d..e73c2c5 100644 --- a/libmariadb/ma_hash.c +++ b/libmariadb/ma_hash.c @@ -1,7 +1,7 @@ /************************************************************************************ Copyright (C) 2000, 2012 MySQL AB & MySQL Finland AB & TCX DataKonsult AB, Monty Program AB, 2016 MariaDB Corporation AB - + Copyright (c) 2021 OceanBase. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either @@ -38,18 +38,18 @@ #define HIGHUSED 8 static uint hash_mask(uint hashnr,uint buffmax,uint maxlength); -static void movelink(HASH_LINK *array,uint pos,uint next_link,uint newlink); +static void movelink(OB_HASH_LINK *array,uint pos,uint next_link,uint newlink); static uint calc_hashnr(const uchar *key,uint length); static uint calc_hashnr_caseup(const uchar *key,uint length); -static int hashcmp(HASH *hash,HASH_LINK *pos,const uchar *key,uint length); +static int hashcmp(OB_HASH *hash, OB_HASH_LINK *pos,const uchar *key,uint length); -my_bool _hash_init(HASH *hash,uint size,uint key_offset,uint key_length, +my_bool _hash_init(OB_HASH *hash,uint size,uint key_offset,uint key_length, hash_get_key get_key, void (*free_element)(void*),uint flags CALLER_INFO_PROTO) { hash->records=0; - if (ma_init_dynamic_array_ci(&hash->array,sizeof(HASH_LINK),size,0)) + if (ma_init_dynamic_array_ci(&hash->array,sizeof(OB_HASH_LINK),size,0)) { hash->free=0; /* Allow call to hash_free */ return(TRUE); @@ -69,12 +69,12 @@ my_bool _hash_init(HASH *hash,uint size,uint key_offset,uint key_length, } -void hash_free(HASH *hash) +void hash_free(OB_HASH *hash) { if (hash->free) { uint i,records; - HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*); + OB_HASH_LINK *data=dynamic_element(&hash->array,0, OB_HASH_LINK*); for (i=0,records=hash->records ; i < records ; i++) (*hash->free)(data[i].data); hash->free=0; @@ -92,7 +92,7 @@ void hash_free(HASH *hash) */ static inline char* -hash_key(HASH *hash,const uchar *record,uint *length,my_bool first) +hash_key(OB_HASH *hash,const uchar *record,uint *length,my_bool first) { if (hash->get_key) return (char *)(*hash->get_key)(record,(uint *)length,first); @@ -108,7 +108,7 @@ static uint hash_mask(uint hashnr,uint buffmax,uint maxlength) return (hashnr & ((buffmax >> 1) -1)); } -static uint hash_rec_mask(HASH *hash,HASH_LINK *pos,uint buffmax, +static uint hash_rec_mask(OB_HASH *hash,OB_HASH_LINK *pos,uint buffmax, uint maxlength) { uint length; @@ -191,7 +191,7 @@ uint calc_hashnr_caseup(const uchar *key, uint len) #ifndef __SUNPRO_C /* SUNPRO can't handle this */ static inline #endif -unsigned int rec_hashnr(HASH *hash,const uchar *record) +unsigned int rec_hashnr(OB_HASH *hash,const uchar *record) { uint length; uchar *key= (uchar*) hash_key(hash,record,&length,0); @@ -202,9 +202,9 @@ unsigned int rec_hashnr(HASH *hash,const uchar *record) /* Search after a record based on a key */ /* Sets info->current_ptr to found record */ -void* hash_search(HASH *hash,const uchar *key,uint length) +void* hash_search(OB_HASH *hash,const uchar *key,uint length) { - HASH_LINK *pos; + OB_HASH_LINK *pos; uint flag,idx; flag=1; @@ -215,7 +215,7 @@ void* hash_search(HASH *hash,const uchar *key,uint length) hash->blength,hash->records); do { - pos= dynamic_element(&hash->array,idx,HASH_LINK*); + pos= dynamic_element(&hash->array,idx,OB_HASH_LINK*); if (!hashcmp(hash,pos,key,length)) { hash->current_record= idx; @@ -237,14 +237,14 @@ void* hash_search(HASH *hash,const uchar *key,uint length) /* Get next record with identical key */ /* Can only be called if previous calls was hash_search */ -void *hash_next(HASH *hash,const uchar *key,uint length) +void *hash_next(OB_HASH *hash,const uchar *key,uint length) { - HASH_LINK *pos; + OB_HASH_LINK *pos; uint idx; if (hash->current_record != NO_RECORD) { - HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*); + OB_HASH_LINK *data=dynamic_element(&hash->array,0, OB_HASH_LINK*); for (idx=data[hash->current_record].next; idx != NO_RECORD ; idx=pos->next) { pos=data+idx; @@ -262,9 +262,9 @@ void *hash_next(HASH *hash,const uchar *key,uint length) /* Change link from pos to new_link */ -static void movelink(HASH_LINK *array,uint find,uint next_link,uint newlink) +static void movelink(OB_HASH_LINK *array,uint find,uint next_link,uint newlink) { - HASH_LINK *old_link; + OB_HASH_LINK *old_link; do { old_link=array+next_link; @@ -276,7 +276,7 @@ static void movelink(HASH_LINK *array,uint find,uint next_link,uint newlink) /* Compare a key in a record to a whole key. Return 0 if identical */ -static int hashcmp(HASH *hash,HASH_LINK *pos,const uchar *key,uint length) +static int hashcmp(OB_HASH *hash, OB_HASH_LINK *pos,const uchar *key,uint length) { uint rec_keylength; uchar *rec_key= (uchar*) hash_key(hash,pos->data,&rec_keylength,1); @@ -287,22 +287,22 @@ static int hashcmp(HASH *hash,HASH_LINK *pos,const uchar *key,uint length) /* Write a hash-key to the hash-index */ -my_bool hash_insert(HASH *info,const uchar *record) +my_bool hash_insert(OB_HASH *info,const uchar *record) { int flag; uint halfbuff,hash_nr,first_index,idx; uchar *ptr_to_rec= NULL,*ptr_to_rec2= NULL; - HASH_LINK *data,*empty,*gpos= NULL,*gpos2 = NULL,*pos; + OB_HASH_LINK *data,*empty,*gpos= NULL,*gpos2 = NULL,*pos; LINT_INIT(gpos); LINT_INIT(gpos2); LINT_INIT(ptr_to_rec); LINT_INIT(ptr_to_rec2); flag=0; - if (!(empty=(HASH_LINK*) ma_alloc_dynamic(&info->array))) + if (!(empty=(OB_HASH_LINK*) ma_alloc_dynamic(&info->array))) return(TRUE); /* No more memory */ info->current_record= NO_RECORD; - data=dynamic_element(&info->array,0,HASH_LINK*); + data=dynamic_element(&info->array,0, OB_HASH_LINK*); halfbuff= info->blength >> 1; idx=first_index=info->records-halfbuff; @@ -421,15 +421,15 @@ my_bool hash_insert(HASH *info,const uchar *record) ** if there is a free-function it's called for record if found ******************************************************************************/ -my_bool hash_delete(HASH *hash,uchar *record) +my_bool hash_delete(OB_HASH *hash,uchar *record) { uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index; - HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty; + OB_HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty; if (!hash->records) return(1); blength=hash->blength; - data=dynamic_element(&hash->array,0,HASH_LINK*); + data=dynamic_element(&hash->array,0, OB_HASH_LINK*); /* Search after record with key */ pos=data+ hash_mask(rec_hashnr(hash,record),blength,hash->records); gpos = 0; @@ -508,12 +508,12 @@ exit: This is much more efficient than using a delete & insert. */ -my_bool hash_update(HASH *hash,uchar *record,uchar *old_key,uint old_key_length) +my_bool hash_update(OB_HASH *hash,uchar *record,uchar *old_key,uint old_key_length) { uint idx,new_index,new_pos_index,blength,records,empty; - HASH_LINK org_link,*data,*previous,*pos; + OB_HASH_LINK org_link,*data,*previous,*pos; - data=dynamic_element(&hash->array,0,HASH_LINK*); + data=dynamic_element(&hash->array,0,OB_HASH_LINK*); blength=hash->blength; records=hash->records; /* Search after record with key */ @@ -572,10 +572,10 @@ my_bool hash_update(HASH *hash,uchar *record,uchar *old_key,uint old_key_length) } -uchar *hash_element(HASH *hash,uint idx) +uchar *hash_element(OB_HASH *hash,uint idx) { if (idx < hash->records) - return dynamic_element(&hash->array,idx,HASH_LINK*)->data; + return dynamic_element(&hash->array,idx,OB_HASH_LINK*)->data; return 0; } diff --git a/libmariadb/ma_net.c b/libmariadb/ma_net.c index 680369b..01b0761 100644 --- a/libmariadb/ma_net.c +++ b/libmariadb/ma_net.c @@ -38,8 +38,14 @@ #ifndef _WIN32 #include #endif +#include #define MAX_PACKET_LENGTH (256L*256L*256L-1) +#define MAX_PACKET_LENGTH_WITH_OB20 (MAX_PACKET_LENGTH - OB20_HEADER_SIZE - OB20_TAILER_SIZE - OB20_EXTRAINFO_LENGTH_SIZE) + +#ifndef HAVE_COMPRESS +#define HAVE_COMPRESS +#endif /* net_buffer_length and max_allowed_packet are defined in mysql.h See bug conc-57 @@ -51,6 +57,7 @@ ulong max_allowed_packet=1024L * 1024L * 1024L; ulong net_read_timeout= NET_READ_TIMEOUT; ulong net_write_timeout= NET_WRITE_TIMEOUT; ulong net_buffer_length= 8192; /* Default length. Enlarged if necessary */ +// ulong net_buffer_length= 10L * 1024L * 1024L; /* 10M:change for ob20 */ #if !defined(_WIN32) #include @@ -66,7 +73,6 @@ ulong net_buffer_length= 8192; /* Default length. Enlarged if necessary */ #endif #endif - /* ** Give error if a too big packet is found ** The server can change this with the -O switch, but because the client @@ -100,6 +106,8 @@ int ma_net_init(NET *net, MARIADB_PVIO* pvio) net->where_b = net->remain_in_buf=0; net->last_errno=0; + net->use_ob20protocol = 0; + if (pvio != 0) /* If real connection */ { ma_pvio_get_handle(pvio, &net->fd); @@ -113,6 +121,13 @@ void ma_net_end(NET *net) { free(net->buff); net->buff=0; + if (net->ob20protocol) { + ob20_end(net->ob20protocol); + free(net->ob20protocol); + net->ob20protocol = 0; + net->use_ob20protocol = 0; + net->compress = 0; + } } /* Realloc the packet buffer */ @@ -186,6 +201,10 @@ int ma_net_flush(NET *net) int ma_net_write(NET *net, const uchar *packet, size_t len) { uchar buff[NET_HEADER_SIZE]; + // update ob20 request id + if (net->use_ob20protocol && OB_NOT_NULL(net->ob20protocol)) { + update_request_id(&net->ob20protocol->header.request_id); + } while (len >= MAX_PACKET_LENGTH) { const ulong max_len= MAX_PACKET_LENGTH; @@ -218,6 +237,11 @@ int ma_net_write_command(NET *net, uchar command, buff[NET_HEADER_SIZE]= 0; buff[4]=command; + // update ob20 request id + if (net->use_ob20protocol && OB_NOT_NULL(net->ob20protocol)) { + update_request_id(&net->ob20protocol->header.request_id); + } + if (length >= MAX_PACKET_LENGTH) { len= MAX_PACKET_LENGTH - 1; @@ -249,15 +273,45 @@ int ma_net_write_command(NET *net, uchar command, static int ma_net_write_buff(NET *net,const char *packet, size_t len) { size_t left_length; + size_t extra_info_length = 0; if (!len) return 0; - if (net->max_packet > MAX_PACKET_LENGTH && - net->compress) - left_length= (size_t)(MAX_PACKET_LENGTH - (net->write_pos - net->buff)); - else + // 传入ma_net_real_write的buffer必须为MAX_PACKET_LENGTH - extra_info_length + if (net->use_ob20protocol) { + extra_info_length = get_protocol20_extra_info_length(net->ob20protocol); + // 1.如果extra info length大于能存放的最大长度, 直接报错 + // 2.如果extra info length + 当前buffer里存放的packet大于最大长度,单独发一个extra info包 + // 3.如果extra info length + 当前buffer里存放的packet + 参数传入的packet length大于最大长度, + // 将当前buffer中的buffer和extra info发送出去 + // 4.如果所有的都能放下,直接走到下面的流程 + if (extra_info_length > MAX_PACKET_LENGTH_WITH_OB20) { + return 1; //error + } else if (extra_info_length + (size_t)(net->write_pos - net->buff) > MAX_PACKET_LENGTH_WITH_OB20) { + if (ma_net_real_write(net,(char*) net->buff, 0)) { // buffer为0,只写20头和extra info + return 1; // error + } + } else if (extra_info_length + (size_t)(net->write_pos - net->buff) + len > MAX_PACKET_LENGTH_WITH_OB20) { + // 将当前的buffer和extra info写入到网络包中 + if (ma_net_real_write(net,(char*) net->buff, (size_t)(net->write_pos - net->buff))) { + return 1; + } + net->write_pos= net->buff; + } else { + // 当前所有都能放入一个包中, 这个分支会走到最后memcpy,然后结束 + } + } + + if (net->max_packet > MAX_PACKET_LENGTH && net->compress) { + if (net->use_ob20protocol) { + left_length= (size_t)(MAX_PACKET_LENGTH_WITH_OB20 - (net->write_pos - net->buff)); + } else { + left_length= (size_t)(MAX_PACKET_LENGTH - (net->write_pos - net->buff)); + } + } else { left_length=(size_t) (net->buff_end - net->write_pos); + } if (len > left_length) { @@ -273,9 +327,13 @@ static int ma_net_write_buff(NET *net,const char *packet, size_t len) } if (net->compress) { - /* uncompressed length is stored in 3 bytes,so - packet can't be > 0xFFFFFF */ - left_length= MAX_PACKET_LENGTH; + if (net->use_ob20protocol) { + left_length= MAX_PACKET_LENGTH_WITH_OB20; + } else { + /* uncompressed length is stored in 3 bytes,so + packet can't be > 0xFFFFFF */ + left_length= MAX_PACKET_LENGTH; + } while (len > left_length) { if (ma_net_real_write(net, packet, left_length)) @@ -310,25 +368,97 @@ int ma_net_real_write(NET *net, const char *packet, size_t len) { size_t complen; uchar *b; + uchar *check_result; uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE; - if (!(b=(uchar*) malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1))) - { - net->last_errno=ER_OUT_OF_RESOURCES; - net->error=2; - net->reading_or_writing=0; - return(1); - } - memcpy(b+header_length,packet,len); - if (_mariadb_compress((unsigned char*) b+header_length,&len,&complen)) + if (net->use_ob20protocol) { - complen=0; + // use ob20 protocol + uint32_t ob20_header_length = header_length + OB20_HEADER_SIZE; + uint32_t extra_info_length = 0; + uint32_t ob20_packet_len = 0; + uint32_t real_write_buffer_length = 0; + uint32_t crc32 = 0; + + extra_info_length = get_protocol20_extra_info_length(net->ob20protocol); + if (0 != extra_info_length) { + ob20_packet_len = len + extra_info_length + OB20_HEADER_SIZE + OB20_TAILER_SIZE + OB20_EXTRAINFO_LENGTH_SIZE; + } else { + ob20_packet_len = len + extra_info_length + OB20_HEADER_SIZE + OB20_TAILER_SIZE; + } + + real_write_buffer_length = ob20_packet_len + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1; + if (net->ob20protocol->real_write_buffer_length < real_write_buffer_length) { + net->ob20protocol->real_write_buffer = realloc(net->ob20protocol->real_write_buffer, real_write_buffer_length); + if (OB_ISNULL(net->ob20protocol->real_write_buffer)) { + net->last_errno=ER_OUT_OF_RESOURCES; + net->error=2; + net->reading_or_writing=0; + return(1); + } else { + net->ob20protocol->real_write_buffer_length = real_write_buffer_length; + } + } + b = net->ob20protocol->real_write_buffer; + + // store extra info + if (0 != extra_info_length) { + int4store(b + ob20_header_length, extra_info_length); + + check_result = fill_protocol20_extra_info(net->ob20protocol, b + ob20_header_length + OB20_EXTRAINFO_LENGTH_SIZE, extra_info_length); + net->ob20protocol->header.flag.st_flags.OB_IS_NEW_EXTRA_INFO = 1; + net->ob20protocol->header.flag.st_flags.OB_EXTRA_INFO_EXIST = 1; + net->ob20protocol->header.flag.st_flags.OB_IS_LAST_PACKET = 0; + if (check_result != b + ob20_header_length + OB20_EXTRAINFO_LENGTH_SIZE + extra_info_length) { + return 1; + } else { + extra_info_length += OB20_EXTRAINFO_LENGTH_SIZE; // storing extra_info_length use 4 bytes + } + // clear extra info + clear_extra_info(net->ob20protocol); + } else { + net->ob20protocol->header.flag.st_flags.OB_EXTRA_INFO_EXIST = 0; + net->ob20protocol->header.flag.st_flags.OB_IS_NEW_EXTRA_INFO = 0; + } + + // store basic info + memcpy(b + ob20_header_length + extra_info_length, packet, len); + + // store crc64 checksum + crc32 = ob_crc32(crc32, (char *)b + ob20_header_length, len + extra_info_length); + int4store(b + ob20_header_length + extra_info_length + len, crc32); + + net->ob20protocol->header.payload_len = len + extra_info_length; + check_result = fill_protocol20_header(net->ob20protocol, ob20_packet_len, net->compress_pkt_nr++, 0, b); + if (check_result != b + ob20_header_length) { + return 1; + } + + len = ob20_packet_len + NET_HEADER_SIZE + COMP_HEADER_SIZE; + packet = (char *)b; + } + else + { + if (!(b=(uchar*) malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1))) + { + net->last_errno=ER_OUT_OF_RESOURCES; + net->error=2; + net->reading_or_writing=0; + return(1); + } + memcpy(b+header_length,packet,len); + + if (_mariadb_compress((unsigned char*) b+header_length,&len,&complen)) + { + complen=0; + } + + int3store(&b[NET_HEADER_SIZE],complen); + int3store(b,len); + b[3]=(uchar) (net->compress_pkt_nr++); + len+= header_length; + packet= (char*) b; } - int3store(&b[NET_HEADER_SIZE],complen); - int3store(b,len); - b[3]=(uchar) (net->compress_pkt_nr++); - len+= header_length; - packet= (char*) b; } #endif /* HAVE_COMPRESS */ @@ -349,8 +479,8 @@ int ma_net_real_write(NET *net, const char *packet, size_t len) pos+=length; } #ifdef HAVE_COMPRESS - if (net->compress) - free((char*) packet); + if (net->compress && !net->use_ob20protocol) + free((char*) packet); // 2.0协议buffer自管理 #endif net->reading_or_writing=0; return(((int) (pos != end))); @@ -554,7 +684,93 @@ ulong ma_net_read(NET *net) if ((packet_length = ma_real_read(net,(size_t *)&complen)) == packet_error) return packet_error; - if (_mariadb_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen)) + if (net->use_ob20protocol) + { + /* + OceanBase 2.0 Protocol Format: + +   0 1 2 3 4 Byte + +-----------------+----------------------+ + | Magic Num | Version | + +-----------------+----------------------+ + | Connection Id | + +-----------------------------+----------+ + | Request Id | Seq | + +-----------------------------+----------+ + | PayLoad Length | + +----------------------------------------+ + | Flag | + +-----------------+----------------------+ + | Reserved |Header Checksum(CRC16)| + +-----------------+----------------------+ + | ... PayLoad Data ... |----------+ + +----------------------------------------+ | + | Tailer PayLoad Checksum (CRC32) | | + +----------------------------------------+ | + | + | + +--------------------------+ + | + | + v + +-------------------+-------------------+-------------------------------------+ + | Extra Len(4Byte) | Extra Info(K/V) | Basic Info(Standard MySQL Packet) | + +-------------------+-------------------+-------------------------------------+ + */ + + unsigned char *ob20_current = 0; + unsigned char *ob20_start = 0; + size_t ob20_extra_info_size = 0; + uint32_t crc32 = 0; + uint32_t packet_crc32; + ob20_current = net->buff + net->where_b; + ob20_start = ob20_current; + + if (decode_protocol20_header(net->ob20protocol, ob20_current, packet_length, net->pkt_nr - 1, 0)) { + net->error=2; /* caller will close socket */ + // 这里考虑定义一个新的错误码 + net->last_errno=ER_NET_READ_ERROR; + return packet_error; + } + + ob20_current += OB20_HEADER_SIZE; + + // first check checksum + packet_crc32 = uint4korr(&ob20_current[net->ob20protocol->header.payload_len]); + if (packet_crc32) { + crc32 = ob_crc32(crc32, (char *)ob20_current, net->ob20protocol->header.payload_len); + if (crc32 != packet_crc32) { + net->error=2; /* caller will close socket */ + net->last_errno=ER_NET_READ_ERROR; + return packet_error; + } + } else { + // 如果server回包的crc32是0,当前不检查crc,后续加上开关 + } + // clear last extra info + clear_extra_info(net->ob20protocol); + + // for extra info + if (1 == net->ob20protocol->header.flag.st_flags.OB_EXTRA_INFO_EXIST){ + // get extra info size + ob20_extra_info_size = uint4korr(ob20_current); +#ifdef DEBUG_OB20 + printf("get extra info and ob20_extra_info size is %lu\n", ob20_extra_info_size); +#endif + // decode extra info + if (1 == net->ob20protocol->header.flag.st_flags.OB_IS_NEW_EXTRA_INFO + && decode_protocol20_extra_info(net->ob20protocol, ob20_current)) { + } + ob20_extra_info_size += OB20_EXTRAINFO_LENGTH_SIZE; + ob20_current += ob20_extra_info_size; + } + + // change buffer to raw mysql packet, same as compress protocol + memmove(ob20_start, ob20_current, net->ob20protocol->header.payload_len - ob20_extra_info_size); + // update complen , length of raw mysql packet length + complen = net->ob20protocol->header.payload_len - ob20_extra_info_size; + } + else if (_mariadb_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen)) { net->error=2; /* caller will close socket */ net->last_errno=ER_NET_UNCOMPRESS_ERROR; diff --git a/libmariadb/ma_stmt_codec.c b/libmariadb/ma_stmt_codec.c index bbdbe18..c0e648e 100644 --- a/libmariadb/ma_stmt_codec.c +++ b/libmariadb/ma_stmt_codec.c @@ -1,7 +1,6 @@ /**************************************************************************** Copyright (C) 2012 Monty Program AB Copyright (c) 2021 OceanBase. - This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either @@ -51,7 +50,7 @@ #include "mysql.h" #include /* ceil() */ #include - +#include "ob_complex.h" #ifdef WIN32 #include #endif @@ -87,7 +86,7 @@ #define YY_PART_YEAR 70 -MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 1]; +MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 2]; my_bool mysql_ps_subsystem_initialized= 0; @@ -531,6 +530,7 @@ static void convert_froma_string(MYSQL_BIND *r_param, char *buffer, size_t len) } break; case MYSQL_TYPE_LONG: + case MYSQL_TYPE_CURSOR: { longlong val= my_atoll(buffer, buffer + len, &error); *r_param->error=error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32) || error > 0; @@ -578,6 +578,7 @@ static void convert_froma_string(MYSQL_BIND *r_param, char *buffer, size_t len) case MYSQL_TYPE_BLOB: case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_OB_UROWID: default: { if (len >= r_param->offset) @@ -618,6 +619,7 @@ static void convert_from_long(MYSQL_BIND *r_param, const MYSQL_FIELD *field, lon r_param->buffer_length= 2; break; case MYSQL_TYPE_LONG: + case MYSQL_TYPE_CURSOR: longstore(r_param->buffer, (int32)val); *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32); r_param->buffer_length= 4; @@ -757,6 +759,7 @@ void ps_fetch_int32(MYSQL_BIND *r_param, const MYSQL_FIELD * const field, break; */ case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: + case MYSQL_TYPE_CURSOR: ps_fetch_from_1_to_8_bytes(r_param, field, row, 4); break; default: @@ -834,6 +837,7 @@ static void convert_from_float(MYSQL_BIND *r_param, const MYSQL_FIELD *field, fl } break; case MYSQL_TYPE_LONG: + case MYSQL_TYPE_CURSOR: { if (r_param->is_unsigned) { @@ -933,6 +937,7 @@ static void convert_from_double(MYSQL_BIND *r_param, const MYSQL_FIELD *field, d } break; case MYSQL_TYPE_LONG: + case MYSQL_TYPE_CURSOR: { if (r_param->is_unsigned) { @@ -1117,6 +1122,8 @@ void ps_fetch_oracle_timestamp(MYSQL_BIND *param, uint buffer_length = 0; uint length = net_field_length(row); + UNUSED(field); + if (length > 16) { buffer_length = length - 16 + sizeof(ORACLE_TIME) + 2; } else { @@ -1256,6 +1263,701 @@ void ps_fetch_datetime(MYSQL_BIND *r_param, const MYSQL_FIELD * field, } /* }}} */ +/*fetch ob_lob*/ +static void fill_ob_lob_locator(OB_LOB_LOCATOR *ob_lob_locator, uchar *row) +{ + ob_lob_locator->magic_code_ = uint4korr(row); + ob_lob_locator->version_ = uint4korr(row + 4); + ob_lob_locator->snapshot_version_ = sint8korr(row + 8); + ob_lob_locator->table_id_ = uint8korr(row + 16); + ob_lob_locator->column_id_ = uint4korr(row + 24); + ob_lob_locator->mode_ = uint2korr(row + 28); + ob_lob_locator->option_ = uint2korr(row + 30); + ob_lob_locator->payload_offset_ = uint4korr(row + 32); + ob_lob_locator->payload_size_ = uint4korr(row + 36); +} + +static void fetch_result_ob_lob(MYSQL_BIND *param, + const MYSQL_FIELD *field __attribute__((unused)), + uchar **row) +{ + ulong length= net_field_length(row); + + if (param->buffer_length <= 0 + || param->buffer_length < MAX_OB_LOB_LOCATOR_HEADER_LENGTH + || length < MAX_OB_LOB_LOCATOR_HEADER_LENGTH) { + *param->error= 1; + } else { + OB_LOB_LOCATOR *ob_lob_locator = (OB_LOB_LOCATOR *)param->buffer; + ulong copy_length = MIN(param->buffer_length - MAX_OB_LOB_LOCATOR_HEADER_LENGTH, length - MAX_OB_LOB_LOCATOR_HEADER_LENGTH); + fill_ob_lob_locator(ob_lob_locator, *row); + memcpy(ob_lob_locator->data_, (*row) + MAX_OB_LOB_LOCATOR_HEADER_LENGTH, copy_length); + *param->error= copy_length + MAX_OB_LOB_LOCATOR_HEADER_LENGTH < length; + } + + *param->length= length; + *row += length; +} + +/*fetch_complex_type*/ +static void fetch_result_complex(MYSQL_BIND *param, void *buffer, + CHILD_TYPE *child, uchar **row); +static void *fetch_result_complex_alloc_space(MYSQL_COMPLEX_BIND_BASIC *header, + MYSQL_BIND *param, + ulong length); +static void fill_complex_type(MYSQL_BIND *param, void *buffer, + CHILD_TYPE *child); +static ulong get_complex_header_length(enum_types type) { + switch (type) { + case TYPE_OBJECT: + return sizeof(MYSQL_COMPLEX_BIND_OBJECT); + case TYPE_COLLECTION: + return sizeof(MYSQL_COMPLEX_BIND_ARRAY); + case TYPE_VARCHAR2: + case TYPE_CHAR: + case TYPE_RAW: + return sizeof(MYSQL_COMPLEX_BIND_STRING); + case TYPE_NUMBER: + return sizeof(MYSQL_COMPLEX_BIND_DECIMAL); + case TYPE_LONG: + case TYPE_LONGLONG: + case TYPE_TINY: + case TYPE_SHORT: + case TYPE_FLOAT: + case TYPE_DOUBLE: + return sizeof(MYSQL_COMPLEX_BIND_BASIC); + default: + return sizeof(MYSQL_COMPLEX_BIND_BASIC); + } +} +static void fetch_result_str_complex(MYSQL_COMPLEX_BIND_STRING *header, + MYSQL_BIND *param, + uchar **row) +{ + void *buffer; + ulong length; + + length = net_field_length(row); + + //include end '\0' + buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, length + 1); + if (NULL == buffer) { + return; + } + + memcpy(buffer, (char *)*row, length); + ((uchar *)buffer)[length]= '\0'; + header->length = length; + *row+= length; + return; +} +static void fetch_result_long_complex(MYSQL_COMPLEX_BIND_BASIC *header, + MYSQL_BIND *param, + uchar **row) +{ + void *buffer; + + buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 4); + if (NULL == buffer) { + return; + } + longstore(buffer, ((uint32)sint4korr(*row))); + *row += 4; + + return; +} +static void fetch_result_longlong_complex(MYSQL_COMPLEX_BIND_BASIC *header, + MYSQL_BIND *param, + uchar **row) +{ + void *buffer; + ulonglong val; + + buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 8); + if (NULL == buffer) { + return; + } + + val= (ulonglong)sint8korr(*row); + longlongstore(buffer, val); + *row += 8; + + return; +} +static void fetch_result_short_complex(MYSQL_COMPLEX_BIND_BASIC *header, + MYSQL_BIND *param, + uchar **row) +{ + void *buffer; + + buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 2); + if (NULL == buffer) { + return; + } + shortstore(buffer, ((ushort)sint2korr(*row))); + *row += 2; + + return; +} +static void fetch_result_tiny_complex(MYSQL_COMPLEX_BIND_BASIC *header, + MYSQL_BIND *param, + uchar **row) +{ + void *buffer; + + buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 1); + if (NULL == buffer) { + return; + } + *(uchar *)buffer= **row; + *row += 1; + + return; +} +static void fetch_result_float_complex(MYSQL_COMPLEX_BIND_BASIC *header, + MYSQL_BIND *param, + uchar **row) +{ + void *buffer; + float *value; + + buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 4); + if (NULL == buffer) { + return; + } + value= (float *)buffer; + float4get(*value, *row); + + *row += 4; + return; +} +static void fetch_result_double_complex(MYSQL_COMPLEX_BIND_BASIC *header, + MYSQL_BIND *param, + uchar **row) +{ + void *buffer; + double *value; + + buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 8); + if (NULL == buffer) { + return; + } + value= (double *)buffer; + float8get(*value, *row); + + *row += 8; + return; +} + + +static void fetch_result_object_complex(MYSQL_COMPLEX_BIND_OBJECT *header, + MYSQL_BIND *param, + uchar **row) +{ + void *buffer = NULL; + ulong length = 0; + uint i = 0; + COMPLEX_TYPE_OBJECT *object = NULL; + struct st_complex_type *complex_type = NULL; + uchar *null_ptr, bit; + + complex_type = get_complex_type(param->mysql, header->owner_name, header->type_name); + + if (complex_type == NULL) { + *param->error= 1; + return; + } + + object = (COMPLEX_TYPE_OBJECT *)complex_type; + + for (i = 0; i < object->attr_no; i++) { + length += get_complex_header_length(object->child[i].type); + } + + buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_BASIC *)header, param, length); + if (NULL == buffer) { + return; + } + + null_ptr = *row; + *row += (object->attr_no + 9)/8; /* skip null bits */ + bit = 4; /* first 2 bits are reserved */ + + for (i = 0; i < object->attr_no; i++) { + fill_complex_type(param, buffer, &(object->child[i])); + + if (*null_ptr & bit) { + ((MYSQL_COMPLEX_BIND_BASIC *)buffer)->is_null = 1; + } else { + fetch_result_complex(param, buffer, &(object->child[i]), row); + } + + buffer = (char*)buffer + get_complex_header_length(object->child[i].type); + + if (!((bit<<=1) & 255)) { + bit= 1; /* To next uchar */ + null_ptr++; + } + } + + header->length = (char*)buffer - (char*)header->buffer; + return; +} +static void read_binary_datetime(MYSQL_TIME *tm, uchar **pos) +{ + uint length= net_field_length(pos); + + if (length) + { + uchar *to= *pos; + + tm->neg= 0; + tm->year= (uint) sint2korr(to); + tm->month= (uint) to[2]; + tm->day= (uint) to[3]; + + if (length > 4) + { + tm->hour= (uint) to[4]; + tm->minute= (uint) to[5]; + tm->second= (uint) to[6]; + } + else + tm->hour= tm->minute= tm->second= 0; + tm->second_part= (length > 7) ? (ulong) sint4korr(to+7) : 0; + tm->time_type= MYSQL_TIMESTAMP_DATETIME; + + *pos+= length; + } + else + { + memset(tm, 0, sizeof(*tm)); + tm->time_type= MYSQL_TIMESTAMP_DATETIME; + } +} +static void fetch_result_datetime_complex(MYSQL_COMPLEX_BIND_BASIC *header, + MYSQL_BIND *param, + uchar **row) +{ + MYSQL_TIME *tm = NULL; + + tm = fetch_result_complex_alloc_space(header, param, sizeof(MYSQL_TIME)); + if (NULL == tm) { + return; + } + + read_binary_datetime(tm, row); + return; +} + +static void fetch_result_bin_complex(MYSQL_COMPLEX_BIND_STRING *header, + MYSQL_BIND *param, + uchar **row) +{ + void *buffer; + ulong length; + + length = net_field_length(row); + + buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, length); + if (NULL == buffer) { + return; + } + + memcpy(buffer, (char *)*row, length); + header->length = length; + *row+= length; + return; +} +static void fetch_result_array_complex(MYSQL_COMPLEX_BIND_ARRAY *header, + MYSQL_BIND *param, + uchar **row, + COMPLEX_TYPE *complex_type) +{ + void *buffer = NULL; + ulong length = 0; + uint num = 0; + uint i = 0; + COMPLEX_TYPE_COLLECTION *object = NULL; + uchar *null_ptr, bit; + + if (NULL == complex_type) { + complex_type = get_complex_type(param->mysql, header->owner_name, header->type_name); + + if (complex_type == NULL) { + *param->error= 1; + return; + } + } else {/* 匿名数组本地存储信息 */} + + object = (COMPLEX_TYPE_COLLECTION *)complex_type; + + num = net_field_length(row); + length = num * get_complex_header_length(object->child.type); + + buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_BASIC *)header, param, length); + if (NULL == buffer) { + return; + } + + null_ptr = *row; + *row += (num + 9)/8; /* skip null bits */ + bit = 4; /* first 2 bits are reserved */ + + for (i = 0; i < num; i++) { + fill_complex_type(param, buffer, &(object->child)); + + if (*null_ptr & bit) { + ((MYSQL_COMPLEX_BIND_BASIC *)buffer)->is_null = 1; + } else { + fetch_result_complex(param, buffer, &(object->child), row); + } + + buffer = (char*)buffer + get_complex_header_length(object->child.type); + + if (!((bit<<=1) & 255)) { + bit= 1; /* To next uchar */ + null_ptr++; + } + } + + header->length = num; + return; +} +static void fetch_result_complex(MYSQL_BIND *param, void *buffer, + CHILD_TYPE *child, uchar **row) +{ + switch (child->type) { + case TYPE_NUMBER: + { + fetch_result_str_complex((MYSQL_COMPLEX_BIND_STRING *)buffer, param, row); + break; + } + case TYPE_VARCHAR2: + case TYPE_CHAR: + { + fetch_result_str_complex((MYSQL_COMPLEX_BIND_STRING *)buffer, param, row); + break; + } + case TYPE_RAW: + { + fetch_result_bin_complex((MYSQL_COMPLEX_BIND_STRING *)buffer, param, row); + break; + } + case TYPE_DATE: + { + fetch_result_datetime_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row); + break; + } + case TYPE_OBJECT: + { + fetch_result_object_complex((MYSQL_COMPLEX_BIND_OBJECT *)buffer, param, row); + break; + } + case TYPE_COLLECTION: + { + fetch_result_array_complex((MYSQL_COMPLEX_BIND_ARRAY *)buffer, param, row, NULL); + break; + } + case TYPE_LONG: + { + fetch_result_long_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row); + break; + } + case TYPE_LONGLONG: + { + fetch_result_longlong_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row); + break; + } + case TYPE_TINY: + { + fetch_result_tiny_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row); + break; + } + case TYPE_SHORT: + { + fetch_result_short_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row); + break; + } + case TYPE_FLOAT: + { + fetch_result_float_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row); + break; + } + case TYPE_DOUBLE: + { + fetch_result_double_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row); + break; + } + default: + *param->error= 1; + break; + } + return; +} +static void *fetch_result_complex_alloc_space(MYSQL_COMPLEX_BIND_BASIC *header, + MYSQL_BIND *param, + ulong length) +{ + void *buffer = NULL; + (*param->length) += length; + if (1 == *param->error) { + buffer = (void *)ma_alloc_root(¶m->bind_alloc, length); + } else if (param->offset + length > param->buffer_length) { + *param->error= 1; + buffer = (void *)ma_alloc_root(¶m->bind_alloc, length); + } else { + buffer = (char*)param->buffer + param->offset; + } + + header->buffer = buffer; + if (header->buffer) { + memset(header->buffer, 0, length); + param->offset += length; + } + + return (header->buffer); +} +static uint mysql_type_to_object_type(uint mysql_type) +{ + enum_types object_type; + switch ((enum_field_types)mysql_type) { + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_CURSOR: + { + object_type = TYPE_LONG; + break; + } + case MYSQL_TYPE_LONGLONG: + { + object_type = TYPE_LONGLONG; + break; + } + case MYSQL_TYPE_TINY: + { + object_type = TYPE_TINY; + break; + } + case MYSQL_TYPE_SHORT: + { + object_type = TYPE_SHORT; + break; + } + case MYSQL_TYPE_FLOAT: + { + object_type = TYPE_FLOAT; + break; + } + case MYSQL_TYPE_DOUBLE: + { + object_type = TYPE_DOUBLE; + break; + } + case MYSQL_TYPE_NEWDECIMAL: + { + object_type = TYPE_NUMBER; + break; + } + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + { + object_type = TYPE_VARCHAR2; + break; + } + case MYSQL_TYPE_OB_RAW: + { + object_type = TYPE_RAW; + break; + } + case MYSQL_TYPE_DATETIME: + { + object_type = TYPE_DATE; + break; + } + case MYSQL_TYPE_OBJECT: + { + object_type = TYPE_OBJECT; + break; + } + case MYSQL_TYPE_ARRAY: + { + object_type = TYPE_COLLECTION; + break; + } + default: + object_type = TYPE_UNKNOW; + break; + } + return object_type; +} +static void fill_complex_type(MYSQL_BIND *param, void *buffer, + CHILD_TYPE *child) +{ + + switch (child->type) { + case TYPE_NUMBER: + { + MYSQL_COMPLEX_BIND_DECIMAL *header = (MYSQL_COMPLEX_BIND_DECIMAL *)buffer; + header->buffer_type = MYSQL_TYPE_NEWDECIMAL; + break; + } + case TYPE_VARCHAR2: + case TYPE_CHAR: + { + MYSQL_COMPLEX_BIND_STRING *header = (MYSQL_COMPLEX_BIND_STRING *)buffer; + header->buffer_type = MYSQL_TYPE_VARCHAR; + break; + } + case TYPE_RAW: + { + MYSQL_COMPLEX_BIND_STRING *header = (MYSQL_COMPLEX_BIND_STRING *)buffer; + header->buffer_type = MYSQL_TYPE_OB_RAW; + break; + } + case TYPE_DATE: + { + MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer; + header->buffer_type = MYSQL_TYPE_DATETIME; + break; + } + case TYPE_LONG: + { + MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer; + header->buffer_type = MYSQL_TYPE_LONG; + break; + } + case TYPE_LONGLONG: + { + MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer; + header->buffer_type = MYSQL_TYPE_LONGLONG; + break; + } + case TYPE_TINY: + { + MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer; + header->buffer_type = MYSQL_TYPE_TINY; + break; + } + case TYPE_SHORT: + { + MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer; + header->buffer_type = MYSQL_TYPE_SHORT; + break; + } + case TYPE_FLOAT: + { + MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer; + header->buffer_type = MYSQL_TYPE_FLOAT; + break; + } + case TYPE_DOUBLE: + { + MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer; + header->buffer_type = MYSQL_TYPE_DOUBLE; + break; + } + case TYPE_OBJECT: + { + MYSQL_COMPLEX_BIND_OBJECT *header = (MYSQL_COMPLEX_BIND_OBJECT *)buffer; + header->buffer_type = MYSQL_TYPE_OBJECT; + header->type_name = child->object->type_name; + header->owner_name = child->object->owner_name; + break; + } + case TYPE_COLLECTION: + { + MYSQL_COMPLEX_BIND_ARRAY *header = (MYSQL_COMPLEX_BIND_ARRAY *)buffer; + header->buffer_type = MYSQL_TYPE_ARRAY; + header->type_name = child->object->type_name; + header->owner_name = child->object->owner_name; + break; + } + default: + *param->error= 1; + break; + } + return; +} +static void fetch_result_type_complex(MYSQL_COMPLEX_BIND_OBJECT *header, + MYSQL_BIND *param, + uchar **row, + COMPLEX_TYPE *complex_type) +{ + if (NULL == complex_type) { + complex_type = get_complex_type(param->mysql, header->owner_name, header->type_name); + if (NULL == complex_type) { + *param->error= 1; + return; + } + } else { /*匿名数组,meta信息不能通过server查询,本地传入 */ } + + if (TYPE_OBJECT == complex_type->type) { + MYSQL_COMPLEX_BIND_OBJECT *buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_BASIC *)header, + param, sizeof(MYSQL_COMPLEX_BIND_OBJECT)); + + if (NULL == buffer) { + return; + } + + buffer->owner_name = header->owner_name; + buffer->type_name = header->type_name; + buffer->buffer_type = MYSQL_TYPE_OBJECT; + + fetch_result_object_complex(buffer, param, row); + } else if (TYPE_COLLECTION == complex_type->type) { + MYSQL_COMPLEX_BIND_ARRAY *buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_BASIC *)header, + param, sizeof(MYSQL_COMPLEX_BIND_ARRAY)); + + if (NULL == buffer) { + return; + } + + buffer->owner_name = header->owner_name; + buffer->type_name = header->type_name; + buffer->buffer_type = MYSQL_TYPE_ARRAY; + + fetch_result_array_complex(buffer, param, row, complex_type); + } + + return; +} + +static void ps_fetch_result_type(MYSQL_BIND *param, + const MYSQL_FIELD *field, + uchar **row) +{ + MYSQL_COMPLEX_BIND_OBJECT header; + + (*param->length) = 0; + param->offset = 0; + ma_init_alloc_root(¶m->bind_alloc, 2048, 0); + + header.owner_name = field->owner_name; + header.type_name = field->type_name; + + if (NULL == header.owner_name && NULL == header.type_name) { + COMPLEX_TYPE_COLLECTION complex_type; + complex_type.header.type = TYPE_COLLECTION; + complex_type.header.owner_name[0] = '\0'; + complex_type.header.type_name[0] = '\0'; + complex_type.header.version = field->version; + complex_type.header.is_valid = TRUE; + complex_type.child.type = (enum_types)mysql_type_to_object_type(field->elem_type); + + if (TYPE_UNKNOW == complex_type.child.type) { + *param->error = 1; + } else { + fetch_result_type_complex(&header, param, row, (COMPLEX_TYPE *)&complex_type); + } + } else { + fetch_result_type_complex(&header, param, row, NULL); + } + + ma_free_root(¶m->bind_alloc, MYF(0)); + return; +} + /* {{{ ps_fetch_string */ static void ps_fetch_string(MYSQL_BIND *r_param, @@ -1304,6 +2006,19 @@ void ps_fetch_bin(MYSQL_BIND *r_param, } /* }}} */ +/* {{{ ps_fetch_result_skip_direct */ +static +void ps_fetch_result_skip_direct(MYSQL_BIND *r_param, + const MYSQL_FIELD *field, + unsigned char **row) +{ + if (NULL != r_param) { + (*r_param->skip_result)(r_param, (MYSQL_FIELD *)field, row); + *r_param->error= 0; + } +} +/* }}} */ + /* {{{ _mysqlnd_init_ps_subsystem */ void mysql_init_ps_subsystem(void) { @@ -1332,6 +2047,10 @@ void mysql_init_ps_subsystem(void) mysql_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len = 4; mysql_ps_fetch_functions[MYSQL_TYPE_LONG].max_len = 11; + mysql_ps_fetch_functions[MYSQL_TYPE_CURSOR].func = ps_fetch_int32; /* parse cursor it with type of INT32 */ + mysql_ps_fetch_functions[MYSQL_TYPE_CURSOR].pack_len = 4; + mysql_ps_fetch_functions[MYSQL_TYPE_CURSOR].max_len = 11; + mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func = ps_fetch_int64; mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8; mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].max_len = 21; @@ -1421,6 +2140,18 @@ void mysql_init_ps_subsystem(void) mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQL_PS_SKIP_RESULT_STR; mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].max_len = -1; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_NUMBER_FLOAT].func = ps_fetch_string; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_NUMBER_FLOAT].pack_len = MYSQL_PS_SKIP_RESULT_STR; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_NUMBER_FLOAT].max_len = -1; + + mysql_ps_fetch_functions[MYSQL_TYPE_OB_NVARCHAR2].func = ps_fetch_string; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_NVARCHAR2].pack_len = MYSQL_PS_SKIP_RESULT_STR; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_NVARCHAR2].max_len = -1; + + mysql_ps_fetch_functions[MYSQL_TYPE_OB_NCHAR].func = ps_fetch_string; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_NCHAR].pack_len = MYSQL_PS_SKIP_RESULT_STR; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_NCHAR].max_len = -1; + mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string; mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQL_PS_SKIP_RESULT_STR; mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].max_len = -1; @@ -1429,9 +2160,35 @@ void mysql_init_ps_subsystem(void) mysql_ps_fetch_functions[MYSQL_TYPE_SET].pack_len = MYSQL_PS_SKIP_RESULT_STR; mysql_ps_fetch_functions[MYSQL_TYPE_SET].max_len = -1; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_UROWID].func = ps_fetch_string; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_UROWID].pack_len = MYSQL_PS_SKIP_RESULT_STR; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_UROWID].max_len = -1; + + mysql_ps_fetch_functions[MYSQL_TYPE_ORA_BLOB].func = fetch_result_ob_lob; + mysql_ps_fetch_functions[MYSQL_TYPE_ORA_BLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR; + mysql_ps_fetch_functions[MYSQL_TYPE_ORA_BLOB].max_len = -1; + + + mysql_ps_fetch_functions[MYSQL_TYPE_ORA_CLOB].func = fetch_result_ob_lob; + mysql_ps_fetch_functions[MYSQL_TYPE_ORA_CLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR; + mysql_ps_fetch_functions[MYSQL_TYPE_ORA_CLOB].max_len = -1; + + mysql_ps_fetch_functions[MYSQL_TYPE_OB_RAW].func = ps_fetch_string; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_RAW].pack_len = MYSQL_PS_SKIP_RESULT_STR; + mysql_ps_fetch_functions[MYSQL_TYPE_OB_RAW].max_len = -1; + + mysql_ps_fetch_functions[MYSQL_TYPE_OBJECT].func = ps_fetch_result_type; + mysql_ps_fetch_functions[MYSQL_TYPE_OBJECT].pack_len = MYSQL_PS_SKIP_RESULT_STR; + mysql_ps_fetch_functions[MYSQL_TYPE_OBJECT].max_len = -1; + mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func = ps_fetch_string; mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQL_PS_SKIP_RESULT_STR; mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].max_len = -1; + + //It will be used in returning into + mysql_ps_fetch_functions[MAX_NO_FIELD_TYPES].func = ps_fetch_result_skip_direct; + mysql_ps_fetch_functions[MAX_NO_FIELD_TYPES].pack_len= MYSQL_PS_SKIP_RESULT_STR; + mysql_ps_fetch_functions[MAX_NO_FIELD_TYPES].max_len = -1; mysql_ps_subsystem_initialized= 1; } diff --git a/libmariadb/mariadb_dyncol.c b/libmariadb/mariadb_dyncol.c index 34b933d..c6f9819 100644 --- a/libmariadb/mariadb_dyncol.c +++ b/libmariadb/mariadb_dyncol.c @@ -3064,7 +3064,7 @@ dynamic_column_update_move_left(DYNAMIC_COLUMN *str, PLAN *plan, return ER_DYNCOL_OK; } -#ifdef UNUSED +#if 0 static enum enum_dyncol_func_result dynamic_column_update_move_right(DYNAMIC_COLUMN *str, PLAN *plan, size_t offset_size, diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 2ba1d76..8214938 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -74,10 +74,12 @@ #endif #include #ifdef _WIN32 -#include #include "shlwapi.h" +#include #define strncasecmp _strnicmp #endif +#include +#include #define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) #define MA_RPL_VERSION_HACK "5.5.5-" @@ -145,15 +147,6 @@ struct st_mariadb_methods MARIADB_DEFAULT_METHODS; #define IS_CONNHDLR_ACTIVE(mysql)\ ((mysql)->extension && (mysql)->extension->conn_hdlr) -static void end_server(MYSQL *mysql); -static void mysql_close_memory(MYSQL *mysql); -void read_user_name(char *name); -my_bool STDCALL mariadb_reconnect(MYSQL *mysql); -static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length); - -extern int mysql_client_plugin_init(); -extern void mysql_client_plugin_deinit(); - static my_bool ma_set_connect_attrs_extend(MYSQL *mysql); @@ -166,8 +159,8 @@ my_bool get_local_ip_port(my_socket fd, char *ip, int iplen, int *port) void *tmp = &(sin->sin_addr); //just IPV4 #if defined(_WIN32) /* _WIN32 */ - char *ip_str = inet_ntoa(sin->sin_addr); - if (NULL != ip_str) } + char *ip_str = inet_ntoa(sin->sin_addr); //winsock2.h + if (NULL != ip_str) { memcpy(ip, ip_str, strlen(ip_str)); } else { return FALSE; @@ -184,6 +177,69 @@ my_bool get_local_ip_port(my_socket fd, char *ip, int iplen, int *port) } } +// static void end_server(MYSQL *mysql); +static void mysql_close_memory(MYSQL *mysql); +void read_user_name(char *name); +my_bool STDCALL mariadb_reconnect(MYSQL *mysql); +static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length); + +extern int mysql_client_plugin_init(); +extern void mysql_client_plugin_deinit(); + +#include "ob_oralce_format_models.h" + + +#define EXT_NLS_DATE_FORMAT(M) \ + (NULL != (M) ? (MYSQL_EXTENSION_PTR(M)->nls_date_format) : NULL) +#define EXT_NLS_TIMESTAMP_FORMAT(M) \ + (NULL != (M) ? (MYSQL_EXTENSION_PTR(M)->nls_timestamp_format) : NULL) +#define EXT_NLS_TIMESTAMP_TZ_FORMAT(M) \ + (NULL != (M) ? (MYSQL_EXTENSION_PTR(M)->nls_timestamp_tz_format) : NULL) + +/* + MYSQL::extension handling (see sql_common.h for declaration + of st_mysql_extension structure). +*/ + +MYSQL_EXTENSION* mysql_extension_init(MYSQL *mysql __attribute__((unused))) +{ + MYSQL_EXTENSION *ext; + const char *nls_date_format = "YYYY-MM-DD HH24:MI:SS"; + const char *nls_timestamp_format = "YYYY-MM-DD HH24:MI:SS.FF"; + const char *nls_timestamp_tz_format = "YYYY-MM-DD HH24:MI:SS.FF TZR TZD"; + + ext= calloc(1, sizeof(MYSQL_EXTENSION)); //need free + memcpy(ext->nls_date_format, nls_date_format, strlen(nls_date_format)); + memcpy(ext->nls_timestamp_format, nls_timestamp_format, strlen(nls_timestamp_format)); + memcpy(ext->nls_timestamp_tz_format, nls_timestamp_tz_format, strlen(nls_timestamp_tz_format)); + return ext; +} + + +void mysql_extension_free(struct st_mysql_extension* ext) +{ + if (!ext) + return; + + if (ext->mysql) { + mysql_close(ext->mysql); + } + + // if (ext->complex_type_hash) { + // my_hash_free(ext->complex_type_hash); + // my_free(ext->complex_type_hash); + // } + + // if (ext->trace_data) + // my_free(ext->trace_data); + + // // free state change related resources. + // free_state_change_info(ext); + + free(ext); +} + + /* net_get_error */ void net_get_error(char *buf, size_t buf_len, char *error, size_t error_len, @@ -391,6 +447,7 @@ void free_rows(MYSQL_DATA *cur) } } + int mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, size_t length, my_bool skipp_check, void *opt_arg) @@ -518,7 +575,7 @@ int ma_multi_command(MYSQL *mysql, enum enum_multi_status status) } } -static void free_old_query(MYSQL *mysql) +void free_old_query(MYSQL *mysql) { if (mysql->fields) ma_free_root(&mysql->field_alloc,MYF(0)); @@ -577,8 +634,7 @@ void read_user_name(char *name) /************************************************************************** ** Shut down connection **************************************************************************/ - -static void +void end_server(MYSQL *mysql) { /* if net->error 2 and reconnect is activated, we need to inforn @@ -889,13 +945,16 @@ unpack_fields(const MYSQL *mysql, { MYSQL_ROWS *row; MYSQL_FIELD *field,*result; - char *p; + uchar *p; /* use unsigned char for data to avoid convert error for char to uint. */ unsigned int i, field_count= sizeof(rset_field_offsets)/sizeof(size_t)/2; + uchar *complex_type; + ulong len; field=result=(MYSQL_FIELD*) ma_alloc_root(alloc,sizeof(MYSQL_FIELD)*fields); if (!result) return(0); + memset(field, 0, sizeof(MYSQL_FIELD)*fields); for (row=data->data; row ; row = row->next,field++) { if (field >= result + fields) @@ -925,7 +984,7 @@ unpack_fields(const MYSQL *mysql, i++; } - p= (char *)row->data[i]; + p= (uchar *)row->data[i]; /* filler */ field->charsetnr= uint2korr(p); p+= 2; @@ -937,22 +996,85 @@ unpack_fields(const MYSQL *mysql, p+= 2; field->decimals= (uint) p[0]; p++; - - /* filler */ - p+= 2; + //use the filler to support oracle precision + field->precision= (uint) p[0]; + p++; + field->ob_routine_param_inout= (enum ObRoutineParamInOut) (p[0] & 0x3); + field->is_implicit_rowid= (p[0] & 0x4) > 0 ? TRUE : FALSE; + p++; if (INTERNAL_NUM_FIELD(field)) field->flags|= NUM_FLAG; i++; - /* This is used by deprecated function mysql_list_fields only, - however the reported length is not correct, so we always zero it */ - if (default_value && row->data[i]) - field->def=ma_strdup_root(alloc,(char*) row->data[i]); - else - field->def=0; - field->def_length= 0; + if (MYSQL_TYPE_OBJECT == field->type) { + complex_type = (uchar*)row->data[i]; + + len=(ulong) net_field_length(&complex_type); + field->owner_name_length = len; + if (0 == len) { + field->owner_name = NULL; + } else { + field->owner_name = (unsigned char *)ma_memdup_root(alloc, (char*)complex_type, len); + complex_type += len; + } + + len=(ulong) net_field_length(&complex_type); + field->type_name_length = len; + if (0 == len) { + field->type_name = NULL; + } else { + field->type_name = (unsigned char *)ma_memdup_root(alloc, (char*)complex_type, len); + complex_type += len; + } + + field->version = (ulong) net_field_length(&complex_type); + + if (0 == len) { + field->elem_type = (enum enum_field_types)uint1korr(complex_type); + complex_type++; + + if (MYSQL_TYPE_OBJECT == field->elem_type) { + len=(ulong) net_field_length(&complex_type); + field->elem_owner_name_length = len; + if (0 == len) { + field->elem_owner_name = NULL; + } + else + { + field->elem_owner_name = (unsigned char *)ma_memdup_root(alloc, (char *)complex_type, len); + complex_type += len; + } + + len=(ulong) net_field_length(&complex_type); + field->elem_type_name_length = len; + if (0 == len) { + field->elem_type_name = NULL; + } else { + field->elem_type_name = (unsigned char *)ma_memdup_root(alloc, (char*)complex_type, len); + complex_type += len; + } + + field->elem_version = (ulong) net_field_length(&complex_type); + } + } + + len = (ulong) net_field_length(&complex_type); + + if (default_value && len > 0) { + field->def=ma_memdup_root(alloc, (char*)complex_type, len); + } else { + field->def=0; + } + } else { + if (default_value && row->data[i]) { + field->def=ma_strdup_root(alloc,(char*) row->data[i]); + } else { + field->def=0; + } + } + field->def_length= 0; field->max_length= 0; } if (field < result + fields) @@ -964,8 +1086,484 @@ error: ma_free_root(alloc, MYF(0)); return(0); } +/* Read all rows (data) from server */ +static const char digits[]= "0123456789abcdef"; + +char *my_safe_ultoa(int base, ulong val, char *buf, uint length, my_bool need_zero) +{ + char *start = buf; + + if (length <= 0) { + return NULL; + } + + buf = start + length - 1; + do { + *buf--= digits[val % base]; + } while ((val /= base) != 0 && buf >= start); + + while (need_zero && buf >= start) { + *buf--= '0'; + } + + return buf + 1; +} + +char *my_safe_ltoa(int base, long val, char *buf, uint length, my_bool need_zero) +{ + const my_bool is_neg= (val < 0); + + if (length <= 1) { + return NULL; + } + + if (is_neg) { + val= -val; + } + + buf = my_safe_ultoa(base, (ulong)val, buf + 1, length - 1, need_zero); + buf--; + + if (is_neg) { + *buf--= '-'; + } else { + *buf--= '+'; + } + + return buf + 1; +} + +uchar* get_nls_format(MYSQL *mysql, enum_field_types type) +{ + uchar *mysql_nls = NULL; + + if (type == MYSQL_TYPE_DATETIME) { + mysql_nls = EXT_NLS_DATE_FORMAT(mysql); + } else if (type == MYSQL_TYPE_OB_TIMESTAMP_NANO || type == MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE) { + mysql_nls = EXT_NLS_TIMESTAMP_FORMAT(mysql); + } else if (type == MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE) { + mysql_nls = EXT_NLS_TIMESTAMP_TZ_FORMAT(mysql); + } + + return mysql_nls; +} +//2019-01-01 12:12:12 +void oracle_date_to_ob_time(struct ObTime *ob_time, uchar *cp, ulong len) +{ + char *start = (char *)cp; + char *end = (char *)cp + len; + char *pos = NULL; + + ob_time->time_zone_id_ = -1; + + if (len > 0) { + pos = end; + ob_time->parts_[DT_YEAR] = strtol(start, &pos, 10); + if (*pos == '-' && end - pos > 1) { + start = pos + 1; + pos = end; + ob_time->parts_[DT_MON] = strtol(start, &pos, 10); + if (*pos == '-' && end - pos > 1) { + start = pos + 1; + pos = end; + ob_time->parts_[DT_MDAY] = strtol(start, &pos, 10); + if (*pos == ' ' && end - pos > 1) { + start = pos + 1; + pos = end; + ob_time->parts_[DT_HOUR] = strtol(start, &pos, 10); + if (*pos == ':' && end - pos > 1) { + start = pos + 1; + pos = end; + ob_time->parts_[DT_MIN] = strtol(start, &pos, 10); + if (*pos == ':' && end - pos > 1) { + int error; + start = pos + 1; + pos = end; + ob_time->parts_[DT_SEC] = strtoll10(start, &pos, &error); + } + } + } + } + } + } +} + +void oracle_timestamp_to_ob_time(struct ObTime *ob_time, uchar *cp, enum_field_types type) +{ + uint century_offset = 0; + uint year_offset = 1; + uint mon_offset = 2; + + uint nano_max_len = 9; //nano only need 9 digit, example: 000000001 + uint nano_offset = 7; + uint nano_scale_offset = 11; + + uint hour_offset = 12; + uint min_offset = 13; + + uint tz_name_offset = 14; + uint tz_m = 0; + uint tz_n = 0; + + int8_t sign = 1; + uint i = 0; + + if (*(char*)(cp + century_offset) < 0 || *(char*)(cp + year_offset) < 0) { + sign = -1; + } + ob_time->parts_[DT_YEAR] = sign * (abs((*(char *)(cp + century_offset))) * 100 + abs(*(char *)(cp + year_offset))); + + for (; i <= 5; i++) { + ob_time->parts_[DT_MON + i] = *(char *)(cp + mon_offset + i); + } + + ob_time->parts_[DT_USEC] = uint4korr(cp + nano_offset); + ob_time->parts_[DT_DATE] = ob_time_to_date(ob_time); + + //uint trans to string and fill zero, example: 000110000 + ob_time->nano_scale_ = *(cp + nano_scale_offset); + + //calculate space for nano, include zero before nano + if (ob_time->nano_scale_ > nano_max_len) { + ob_time->nano_scale_ = nano_max_len; + } + + ob_time->time_zone_id_ = -1; + if (type == MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE) { + sign = 1; + if (*(char*)(cp + hour_offset) < 0 || *(char*)(cp + min_offset) < 0) { + sign = -1; + } + ob_time->parts_[DT_OFFSET_MIN] = sign * (abs((*(int8_t *)(cp + hour_offset))) * 60 + abs(*(int8_t *)(cp + min_offset))); + + tz_m = *(cp + tz_name_offset); + tz_n = *(cp + tz_name_offset + 1 + tz_m); + if (tz_m > 0) { + ob_time->time_zone_id_ = 0; + memcpy(ob_time->tz_name_, cp + tz_name_offset + 1, tz_m); + if (tz_n > 0) { + memcpy(ob_time->tzd_abbr_, cp + tz_name_offset + 1 + tz_m + 1, tz_n); + } + } + } +} + +ulong calculate_new_time_length_with_nls(MYSQL *mysql, uchar *cp, ulong len, enum_field_types type) +{ + struct ObTime ob_time; + uchar *mysql_nls = NULL; + int64_t length = 0; + + memset(&ob_time, 0, sizeof(struct ObTime)); + ob_time.mode_ |= DT_TYPE_ORACLE; + + if (type == MYSQL_TYPE_OB_TIMESTAMP_NANO + || type == MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE + || type == MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE) { + oracle_timestamp_to_ob_time(&ob_time, cp, type); + } else { + oracle_date_to_ob_time(&ob_time, cp, len); + } + + if (type == MYSQL_TYPE_DATETIME) { + mysql_nls = EXT_NLS_DATE_FORMAT(mysql); + } else if (type == MYSQL_TYPE_OB_TIMESTAMP_NANO || type == MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE) { + mysql_nls = EXT_NLS_TIMESTAMP_FORMAT(mysql); + } else if (type == MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE) { + mysql_nls = EXT_NLS_TIMESTAMP_TZ_FORMAT(mysql); + } + + if (mysql_nls) { + calculate_str_oracle_dfm_length(&ob_time, (char *)mysql_nls, strlen((char *)mysql_nls), ob_time.nano_scale_, &length); + } + + return length; +} + +//The search starts with a short gallop favoring small numbers, +//after which it goes into a hand-woven binary search. +inline uint fast_digits10(ulong v) +{ + static const ulong MAX_INTEGER[13] = { + 0, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + }; + uint ret_int = 0; + if (v < MAX_INTEGER[1]) { + ret_int = 1; + } else if (v < MAX_INTEGER[2]) { + ret_int = 2; + } else if (v < MAX_INTEGER[3]) { + ret_int = 3; + } else if (v < MAX_INTEGER[12]) { + if (v < MAX_INTEGER[8]) { + if (v < MAX_INTEGER[6]) { + if (v < MAX_INTEGER[4]) { + ret_int = 4; + } else { + ret_int = 5 + (v >= MAX_INTEGER[5]); + } + } else { + ret_int = 7 + (v >= MAX_INTEGER[7]); + } + } else if (v < MAX_INTEGER[10]) { + ret_int = 9 + (v >= MAX_INTEGER[9]); + } else { + ret_int = 11 + (v >= MAX_INTEGER[11]); + } + } else { + ret_int = 12 + fast_digits10(v / MAX_INTEGER[12]); + } + return ret_int; +} + + +ulong calculate_interval_length(uchar *cp, enum_field_types type) +{ + ulong convert_len = 0; + + const ulong MIN_YM_LEN = 7; + const ulong MIN_DS_LEN = 14; + const ulong MIN_YS_FORMAT_LEN = 6 - 2;//%c%0?d-%02d + const ulong MIN_DS_FORMAT_LEN_PART1 = 12 - 2;//%c%0?d %02d:%02d:%02d + const ulong MIN_DS_FORMAT_LEN_PART2 = 3 - 2;//.%0?d + + if (type == MYSQL_TYPE_OB_INTERVAL_YM) { + const uint32 year_digit_cnt = fast_digits10(uint4korr(cp + 1)); + const uint8_t year_scale = cp[6]; + if (year_digit_cnt > year_scale) { + convert_len = MIN_YS_FORMAT_LEN + year_digit_cnt; + } else { + convert_len = MIN_YS_FORMAT_LEN + year_scale; + } + if (convert_len < MIN_YM_LEN) { + convert_len = MIN_YM_LEN; + } + } else { + const uint32 day_digit_cnt = fast_digits10(uint4korr(cp + 1)); + const uint32 fs_digit_cnt = fast_digits10(uint4korr(cp + 8)); + const uint8_t day_scale = cp[12]; + const int8_t fs_scale = cp[13]; + if (day_digit_cnt > (uint32)day_scale) { + convert_len = MIN_DS_FORMAT_LEN_PART1 + day_digit_cnt; + } else { + convert_len = MIN_DS_FORMAT_LEN_PART1 + day_scale; + } + if (fs_scale > 0) { + convert_len += MIN_DS_FORMAT_LEN_PART2; + if (fs_digit_cnt > (uint32)fs_scale) { + convert_len += fs_digit_cnt; + } else { + convert_len += fs_scale; + } + } + if (convert_len < MIN_DS_LEN) { + convert_len = MIN_DS_LEN; + } + } + return convert_len; +} + +ulong rewrite_new_time_with_nls(MYSQL *mysql, uchar *cp, ulong len, char *to, int64_t buf_len, enum_field_types type) +{ + struct ObTime ob_time; + uchar *mysql_nls = NULL; + size_t mysql_nls_len = 0; + int64_t pos = 0; + + memset(&ob_time, 0, sizeof(struct ObTime)); + ob_time.mode_ |= DT_TYPE_ORACLE; + + if (type == MYSQL_TYPE_OB_TIMESTAMP_NANO + || type == MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE + || type == MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE) { + oracle_timestamp_to_ob_time(&ob_time, cp, type); + } else { + oracle_date_to_ob_time(&ob_time, cp, len); + } + + if (type == MYSQL_TYPE_DATETIME) { + mysql_nls = EXT_NLS_DATE_FORMAT(mysql); + } else if (type == MYSQL_TYPE_OB_TIMESTAMP_NANO || type == MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE) { + mysql_nls = EXT_NLS_TIMESTAMP_FORMAT(mysql); + } else if (type == MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE) { + mysql_nls = EXT_NLS_TIMESTAMP_TZ_FORMAT(mysql); + } + + if (mysql_nls) { + mysql_nls_len = strlen((char *)mysql_nls); + } + ob_time_to_str_oracle_dfm(&ob_time, (char *)mysql_nls, mysql_nls_len, ob_time.nano_scale_, to, buf_len, &pos); + + return pos; +} +static const char separator[]= "-- ::."; + +ulong rewrite_new_time(uchar *cp, char *to, enum_field_types type) +{ + ulong convert_len = 20; //2012-12-02 12:34:56. + + char nano_buffer[10] = "\0"; + uint nano_max_len = 9; //nano only need 9 digit, example: 000000001 + uint nano_offset = 7; + uint nano_scale; + uint nano_scale_offset = 11; + + uint tz_name_offset = 14; + uint tz_m = 0; + uint tz_n = 0; + + uint hour_offset = 12; + uint min_offset = 13; + + uint i; + + //rewrite the fix data, example: 2012-12-02 12:34:56. + my_safe_ultoa(10, *cp, to, 2, TRUE); + to += 2; + + for (i = 0; i <= 5; i++) { + my_safe_ultoa(10, *(cp + 1 + i), to, 2, TRUE); + to += 2; + memcpy(to++, separator + i, 1); + } + + //rewrite nano + nano_scale = *(cp + nano_scale_offset); + if (0 != nano_scale) { + if (nano_scale > nano_max_len) { + nano_scale = nano_max_len; + } + + my_safe_ultoa(10, uint4korr(cp + nano_offset), nano_buffer, nano_max_len, TRUE); + memcpy(to, nano_buffer, nano_scale); + convert_len += nano_scale; + to += nano_scale; + } + + if (type == MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE) { + memcpy(to++, " ", 1); + convert_len += 1; + + tz_m = *(cp + tz_name_offset); + tz_n = *(cp + tz_name_offset + 1 + tz_m); + if (tz_m > 0) { + memcpy(to, cp + tz_name_offset + 1, tz_m); + convert_len += tz_m; + to += tz_m; + if (tz_n > 0) { + memcpy(to++, " ", 1); + convert_len += 1; + + memcpy(to, cp + tz_name_offset + 1 + tz_m + 1, tz_n); + convert_len += tz_n; + to += tz_n; + } + } else { + // +/-08:00 + if (*(char*)(cp + hour_offset) < 0 || *(char*)(cp + min_offset) < 0) { + memcpy(to++, "-", 1); + } else { + memcpy(to++, "+", 1); + } + convert_len += 1; + + my_safe_ultoa(10, abs(*(char*)(cp + hour_offset)), to, 2, TRUE); + convert_len += 2; + to += 2; + + memcpy(to++, ":", 1); + convert_len += 1; + + my_safe_ultoa(10, abs(*(char*)(cp + min_offset)), to, 2, TRUE); + convert_len += 2; + to += 2; + } + } + + return convert_len; +} + + +int rewrite_interval(uchar *cp, char *to, const uint to_size, ulong *convert_len, enum_field_types type) +{ + int wlen = 0; + *convert_len = 0; + + if (type == MYSQL_TYPE_OB_INTERVAL_YM) { + char format_str[] = "%02u"; + const int scale_idx = 2; + + to[0] = (cp[0] > 0 ? '-' : '+'); + *convert_len = 1; + + format_str[scale_idx] = (char)('0' + cp[6]); + wlen = snprintf(to + *convert_len, to_size, format_str, + uint4korr(cp + 1)); + if (wlen <= 0) { + return 0; + } else { + (*convert_len) += wlen; + to[*convert_len] = '-'; + my_safe_ultoa(10, cp[5], to + *convert_len + 1, 2, TRUE); + (*convert_len) += 3; + } + } else { + char format_str_part1[] = "%02u"; + const int day_scale_idx = 2; + int8 fs_scale = 0; + + to[0] = (cp[0] > 0 ? '-' : '+'); + *convert_len = 1; + + format_str_part1[day_scale_idx] = (char)('0' + cp[12]); + wlen = snprintf(to + *convert_len, to_size, format_str_part1, + uint4korr(cp + 1)); + if (wlen <= 0) { + return 0; + } else { + (*convert_len) += wlen; + to[*convert_len] = ' '; + my_safe_ultoa(10, cp[5], to + *convert_len + 1, 2, TRUE); + to[*convert_len + 3] = ':'; + my_safe_ultoa(10, cp[6], to + *convert_len + 4, 2, TRUE); + to[*convert_len + 6] = ':'; + my_safe_ultoa(10, cp[7], to + *convert_len + 7, 2, TRUE); + (*convert_len) += 9; + } + + fs_scale = cp[13]; + if (fs_scale > 0) { + char nano_buffer[10] = {0}; + const int8 nano_max_len = 9; //nano only need 9 digit, example: 000000001 + + to[*convert_len] = '.'; + (*convert_len) += 1; + + if (fs_scale > nano_max_len) { + fs_scale = nano_max_len; + } + + my_safe_ultoa(10, uint4korr(cp + 8), nano_buffer, nano_max_len, TRUE); + memcpy(to + *convert_len, nano_buffer, fs_scale); + (*convert_len) += fs_scale; + } + } + return 1; +} /* Read all rows (fields or data) from server */ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, @@ -980,6 +1578,8 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, MYSQL_ROWS **prev_ptr,*cur; NET *net = &mysql->net; + ulong convert_len; + if ((pkt_len= ma_net_safe_read(mysql)) == packet_error) return(0); if (!(result=(MYSQL_DATA*) calloc(1, sizeof(MYSQL_DATA)))) @@ -995,6 +1595,35 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, while (*(cp=net->read_pos) != 254 || pkt_len >= 8) { + if (mysql_fields) { + for (field=0 ; field < fields ; field++) { + if ((len=(ulong) net_field_length(&cp)) == NULL_LENGTH) { + continue; + } + + //calculate the space for new type + if (mysql->oracle_mode + && (mysql_fields[field].type == MYSQL_TYPE_DATETIME + || mysql_fields[field].type == MYSQL_TYPE_OB_TIMESTAMP_NANO + || mysql_fields[field].type == MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE + || mysql_fields[field].type == MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE)) { + convert_len = calculate_new_time_length_with_nls(mysql, cp, len, mysql_fields[field].type); + //already include '/0' in the end + pkt_len = pkt_len - len + convert_len; + } else if (mysql_fields[field].type == MYSQL_TYPE_OB_RAW) { + convert_len = len * 2; + //already include '/0' in the end + pkt_len = pkt_len - len + convert_len; + } else if (mysql_fields[field].type == MYSQL_TYPE_OB_INTERVAL_YM + || mysql_fields[field].type == MYSQL_TYPE_OB_INTERVAL_DS) { + convert_len = calculate_interval_length(cp, mysql_fields[field].type); + //already include '/0' in the end + pkt_len = pkt_len - len + convert_len; + } + cp+=len; + } + } + cp = net->read_pos; result->rows++; if (!(cur= (MYSQL_ROWS*) ma_alloc_root(&result->alloc, sizeof(MYSQL_ROWS))) || @@ -1018,23 +1647,71 @@ MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, } else { + int is_done = 0; + cur->data[field] = to; - if (len > (ulong)(end_to - to) || to > end_to) + if ((len > (ulong)(end_to - to) || to > end_to) && !(mysql->oracle_mode && mysql_fields + && (mysql_fields[field].type == MYSQL_TYPE_DATETIME /* FOR oracle mode with mysql_fields==true + (parse the result row not fields' info).The datas of these types need to extend space to convert + them to the format we need. So it could be skip if len > end_to - to, which pkt_len maybe become + less than it compare with before convert. */ + || mysql_fields[field].type == MYSQL_TYPE_OB_TIMESTAMP_NANO + || mysql_fields[field].type == MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE + || mysql_fields[field].type == MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE))) { free_rows(result); SET_CLIENT_ERROR(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0); return(0); } - memcpy(to,(char*) cp,len); to[len]=0; - to+=len+1; - cp+=len; + if (mysql_fields) { + if (mysql->oracle_mode + && (mysql_fields[field].type == MYSQL_TYPE_DATETIME + || mysql_fields[field].type == MYSQL_TYPE_OB_TIMESTAMP_NANO + || mysql_fields[field].type == MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE + || mysql_fields[field].type == MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE)) { + convert_len = rewrite_new_time_with_nls(mysql, cp, len, to, end_to - to + 1, mysql_fields[field].type); + to[convert_len]=0; + to+=convert_len+1; + cp+=len; + len = convert_len; + is_done = 1; + } else if (mysql_fields[field].type == MYSQL_TYPE_OB_RAW) { + uchar *end = cp + len; + for(; cp < end; cp++, to+=2) { + sprintf(to, "%02X", *((uchar*)cp)); + } + + (*to++)=0; + len *= 2; + is_done = 1; + } else if (mysql_fields[field].type == MYSQL_TYPE_OB_INTERVAL_YM + || mysql_fields[field].type == MYSQL_TYPE_OB_INTERVAL_DS) { + if (!rewrite_interval(cp, to, (uint)(end_to - to), &convert_len, mysql_fields[field].type)) { + free_rows(result); + return (0); + } + to[convert_len]=0; + to+=convert_len+1; + cp+=len; + len = convert_len; + is_done = 1; + } + } + if (!is_done) { + memcpy(to,(char*) cp,len); to[len]=0; + to+=len+1; + cp+=len; + is_done = 1; + } + if (mysql_fields) { if (mysql_fields[field].max_length < len) mysql_fields[field].max_length=len; - } + } } } + memcpy(to, (char *)cp, (ulong)(end_to - to)); cur->data[field]=to; /* End of last field */ if ((pkt_len=ma_net_safe_read(mysql)) == packet_error) { @@ -1133,6 +1810,14 @@ mysql_init(MYSQL *mysql) mysql->net.extension= 0; } + /* Initialize extensions. */ + if (!(mysql->ob_extension= mysql_extension_init(mysql))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto error; + return 0; + } + if (!(mysql->net.extension= (struct st_mariadb_net_extension *) calloc(1, sizeof(struct st_mariadb_net_extension))) || !(mysql->extension= (struct st_mariadb_extension *) @@ -1215,7 +1900,6 @@ char *ma_send_connect_attr(MYSQL *mysql, unsigned char *buffer) { size_t len; uchar *p= hash_element(&mysql->options.extension->connect_attrs, i); - len= strlen((char *)p); buffer= mysql_net_store_length(buffer, len); memcpy(buffer, p, len); @@ -1231,28 +1915,35 @@ char *ma_send_connect_attr(MYSQL *mysql, unsigned char *buffer) return (char *)buffer; } -/** Set some default attributes in extend, it will be called when - * the connection has been built */ + +/** set some ob default attributes */ static my_bool -ma_set_connect_attrs_extend(MYSQL *mysql) +ma_set_ob_connect_attrs(MYSQL *mysql) { - char ip_buffer[100] = {0}; - char port_buffer[10] = {0}; - int port = 0; - int rc= 0; + int rc = 0; + uint64_t cap = OBPROXY_DEFAULT_CAPABILITY_FLAG; + char cap_buf[OB_MAX_UINT64_BUF_LEN]; - rc= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "__client_ip") + - mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "__client_port"); - - if (NULL != mysql && get_local_ip_port(mysql->net.fd, ip_buffer, 100, &port)) { - snprintf(port_buffer, 10, "%d", port); + if (mysql->can_use_protocol_ob20) { + cap |= OB_CAP_OB_PROTOCOL_V2; + cap |= OB_CAP_PROXY_NEW_EXTRA_INFO; + if (mysql->can_use_full_link_trace) { + cap |= OB_CAP_FULL_LINK_TRACE; + } else { + cap &= ~OB_CAP_FULL_LINK_TRACE; + } } else { - snprintf(ip_buffer, 100, "%s", "invalid"); - snprintf(port_buffer, 10, "%s", "invalid"); + cap &= ~OB_CAP_OB_PROTOCOL_V2; + cap &= ~OB_CAP_FULL_LINK_TRACE; + cap &= ~OB_CAP_PROXY_NEW_EXTRA_INFO; } - rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "__client_ip", ip_buffer); - rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "__client_port", port_buffer); - return(test(rc>0)); + + snprintf(cap_buf, OB_MAX_UINT64_BUF_LEN, "%lu", cap); + + rc += mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, OB_MYSQL_CAPABILITY_FLAG, cap_buf); + rc += mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, OB_MYSQL_CLIENT_MODE, "__ob_libobclient"); + + return rc; } /** set some default attributes */ @@ -1292,6 +1983,39 @@ ma_set_connect_attrs(MYSQL *mysql, const char *host) return(test(rc>0)); } +/** set some default attributes for extend, + * it will be called when after the connection has been built */ +static my_bool +ma_set_connect_attrs_extend(MYSQL *mysql) +{ + char ip_buffer[100] = {0}; + char port_buffer[10]= {0}; + int port = 0; + int rc= 0; + + if (!mysql) { + return rc; + } + + rc= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "__client_ip") + + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "__client_port"); + + if (get_local_ip_port(mysql->net.fd, ip_buffer, 100, &port)) { + snprintf(port_buffer, 10, "%d", port); + } else { + snprintf(ip_buffer, 100, "%s", "invalid"); + snprintf(port_buffer, 10, "%s", "invalid"); + } + rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "__client_ip", ip_buffer); + rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "__client_port", port_buffer); + + rc += ma_set_ob_connect_attrs(mysql); + + return(test(rc>0)); +} + + + /* ** Note that the mysql argument must be initialized with mysql_init() ** before calling mysql_real_connect ! @@ -1387,6 +2111,50 @@ mysql_real_connect(MYSQL *mysql, const char *host, const char *user, #endif } +static unsigned long mysql_get_version_tool(const char* version_str) +{ + unsigned long major= 0, minor= 0, version= 0; + + if (version_str) + { + char *pos= (char*)version_str, *end_pos; + major= strtoul(pos, &end_pos, 10); pos=end_pos+1; + minor= strtoul(pos, &end_pos, 10); pos=end_pos+1; + version= strtoul(pos, &end_pos, 10); + } + else + { + // set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); + } + return major*10000 + minor*100 + version; +} +static int get_ob_server_version(MYSQL *con) +{ + /* Only one thread calls this, so no synchronization is needed */ + MYSQL_RES *result; + + /* "limit 1" is protection against SQL_SELECT_LIMIT=0 */ + char const *sql = con->oracle_mode ? "select @@version_comment, @@version from dual where rownum <= 1" : "select @@version_comment, @@version limit 1"; + if (!mysql_query(con, sql) && + (result = mysql_use_result(con))) + { + MYSQL_ROW cur = mysql_fetch_row(result); + if (cur && cur[0] && cur[1]) + { + // only get ob server version + if (strlen(cur[0]) > 9 && strncasecmp(cur[0], "OceanBase", 9) == 0) + { + con->ob_server_version = mysql_get_version_tool(cur[1]); + } + } + mysql_free_result(result); + } else { + // error + return 1; + } + return 0; +} + MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, uint port, const char *unix_socket, unsigned long client_flag) @@ -1704,6 +2472,10 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, mysql->client_flag|= mysql->options.client_flag; mysql->client_flag|= CLIENT_CAPABILITIES; + // check env variable to determine if use ob20 + determine_protocol_ob20(mysql); + determine_full_link_trace(mysql); + if (run_plugin_auth(mysql, scramble_data, scramble_len, scramble_plugin, db)) goto error; @@ -1711,6 +2483,19 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, if (mysql->client_flag & CLIENT_COMPRESS) net->compress= 1; + // printf("determine end , use ob20 is %d, use full link trace is %d\n", get_use_protocol_ob20(mysql), get_use_full_link_trace(mysql)); + if (get_use_protocol_ob20(mysql)) { + if (!(net->ob20protocol=(Ob20Protocol *) malloc(sizeof(Ob20Protocol)))) + goto error; + net->compress = 1; + net->use_ob20protocol = TRUE; + if (ob20_init(net->ob20protocol, mysql->thread_id, get_use_full_link_trace(mysql))) { + goto error; + } + } else { + // printf("connect ob with normal protocol\n"); + } + /* last part: select default db */ if (!(mysql->server_capabilities & CLIENT_CONNECT_WITH_DB) && (db && !mysql->db)) @@ -1724,7 +2509,13 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, goto error; } } - + if (get_ob_server_version(mysql)) { + // select version errro + goto error; + } + determine_use_prepare_execute(mysql); + determine_send_plarray_maxrarr_len(mysql); + determine_plarray_bindbyname(mysql); if (mysql->options.init_command) { char **begin= (char **)mysql->options.init_command->buffer; @@ -2071,6 +2862,14 @@ static void mysql_close_memory(MYSQL *mysql) free(mysql->db); free(mysql->unix_socket); free(mysql->server_version); + if (mysql->extension) { + mysql_extension_free((struct st_mysql_extension *)mysql->extension); + mysql->extension = NULL; + } + if (mysql->ob_extension) { + free(mysql->ob_extension); + mysql->ob_extension = NULL; + } mysql->host_info= mysql->host= mysql->unix_socket= mysql->server_version=mysql->user=mysql->passwd=mysql->db=0; } @@ -2208,6 +3007,8 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) { uchar *end= mysql->net.read_pos+length; size_t item_len; + enum enum_nls_time_type nls_time_type; + uchar *mysql_nls = NULL; mysql->affected_rows= net_field_length_ll(&pos); mysql->insert_id= net_field_length_ll(&pos); mysql->server_status=uint2korr(pos); @@ -2300,9 +3101,26 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) else if (si_type == SESSION_TRACK_SYSTEM_VARIABLES) { my_bool set_charset= 0; + my_bool get_capability_flag = 0; + + // char buffer[100] = {0}; + // memcpy(buffer, str->str, str->length); + // printf("system variables length is %u is %.*s\n", str->length, str->length, buffer); /* make sure that we update charset in case it has changed */ if (!strncmp(str->str, "character_set_client", str->length)) set_charset= 1; + if (!strncmp(str->str, "ob_capability_flag", str->length)) { + get_capability_flag = 1; + } + if (!strncmp(str->str, "nls_date_format", str->length)) { + nls_time_type = NLS_DATE_FORMAT; + } else if(!strncmp(str->str, "nls_timestamp_format", str->length)) { + nls_time_type = NLS_TIMESTAMP_FORMAT; + } else if (!strncmp(str->str, "nls_timestamp_tz_format", str->length)) { + nls_time_type = NLS_TIMESTAMP_TZ_FORMAT; + } else { + nls_time_type = NLS_TIME_MAX; + } plen= net_field_length(&pos); if (pos + plen > end) goto corrupted; @@ -2328,6 +3146,35 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) if ((cs_info = mysql_find_charset_name(cs_name))) mysql->charset= cs_info; } + if (1 == get_capability_flag) { + #define MAX_INT64_STORE_LEN 31 + char int_buf[MAX_INT64_STORE_LEN + 1] = {0}; + char *end_ptr = NULL; + if (MAX_INT64_STORE_LEN < plen) { + // error + goto corrupted; + } + memcpy(int_buf, str->str, plen); + int_buf[plen + 1] = '\0'; + mysql->capability = strtoull(int_buf, &end_ptr, 10); + if (('\0' != *int_buf ) && ('\0' == *end_ptr)) { + // succ, do nothing + } else { + // error + goto corrupted; + } + } + if (nls_time_type >= NLS_DATE_FORMAT && nls_time_type < NLS_TIME_MAX) { + if (nls_time_type == NLS_DATE_FORMAT) { + mysql_nls = EXT_NLS_DATE_FORMAT(mysql); + } else if (nls_time_type == NLS_TIMESTAMP_FORMAT) { + mysql_nls = EXT_NLS_TIMESTAMP_FORMAT(mysql); + } else if (nls_time_type == NLS_TIMESTAMP_TZ_FORMAT) { + mysql_nls = EXT_NLS_TIMESTAMP_TZ_FORMAT(mysql); + } + memcpy(mysql_nls, str->str, str->length); + mysql_nls[str->length] = '\0'; + } } break; default: @@ -2439,17 +3286,27 @@ int STDCALL mysql_real_query(MYSQL *mysql, const char *query, unsigned long length) { my_bool skip_result= OPT_EXT_VAL(mysql, multi_command); + int ret = 0; + FLT_DECLARE; + + // 全链路begin span相关 + FLT_BEFORE_COMMAND(0, FLT_TAG_COMMAND_NAME, "\"mysql_real_query\""); if (length == (unsigned long)-1) length= (unsigned long)strlen(query); free_old_query(mysql); - if (ma_simple_command(mysql, COM_QUERY,query,length,1,0)) - return(-1); - if (!skip_result) - return(mysql->methods->db_read_query_result(mysql)); - return(0); + if (ma_simple_command(mysql, COM_QUERY,query,length,1,0)) { + ret = -1; + } else if (!skip_result) { + ret = (mysql->methods->db_read_query_result(mysql)); + } + + // 全链路end span相关 + FLT_AFTER_COMMAND; + + return ret; } /************************************************************************** @@ -3595,6 +4452,11 @@ MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res) return (res)->fields; } +MYSQL_FIELD * STDCALL mysql_fetch_params(MYSQL_RES *res) +{ + return (res)->param_fields; +} + MYSQL_ROWS * STDCALL mysql_row_tell(MYSQL_RES *res) { return res->data_cursor; @@ -3949,7 +4811,7 @@ mysql_get_server_name(MYSQL *mysql) if (mysql->options.extension && mysql->options.extension->db_driver != NULL) return mysql->options.extension->db_driver->name; - return mariadb_connection(mysql) ? "MariaDB" : "obclient"; + return mariadb_connection(mysql) ? "MariaDB" : "MySQL"; } static my_socket mariadb_get_socket(MYSQL *mysql) diff --git a/libmariadb/mariadb_stmt.c b/libmariadb/mariadb_stmt.c index b7cf71c..54d3796 100644 --- a/libmariadb/mariadb_stmt.c +++ b/libmariadb/mariadb_stmt.c @@ -56,8 +56,11 @@ #include #include #include "ma_priv.h" +#include "ob_complex.h" +#include "ob_protocol20.h" +#include "ob_full_link_trace.h" - +#define DBUG_RETURN(a) return (a) #define UPDATE_STMT_ERROR(stmt)\ SET_CLIENT_STMT_ERROR((stmt), (stmt)->mysql->net.last_errno, (stmt)->mysql->net.sqlstate, (stmt)->mysql->net.last_error) @@ -78,16 +81,28 @@ SET_CLIENT_STMT_ERROR((stmt), (stmt)->mysql->net.last_errno, (stmt)->mysql->net. #define MAX_ORACLE_TIMESTAMP_REP_LENGTH 13 #define MAX_DOUBLE_STRING_REP_LENGTH 331 +#define SUPPORT_PREPARE_EXECUTE_VERSION 20276 +#define SUPPORT_SEND_FETCH_FLAG_VERSION 20276 +#define SUPPORT_SEND_PLARRAY_MAXRARR_LEN 30201 +#define SUPPORT_PLARRAY_BINDBYNAME 20207 +static const char SINGLE_QUOTE = '\''; +static const char DOUBLE_QUOTE = '"'; +#define MY_TEST(a) ((a) ? 1 : 0) typedef struct { MA_MEM_ROOT fields_ma_alloc_root; + MA_MEM_ROOT binds_ma_alloc_root; //as for oracle mode fetch return meta, mem root should seperate } MADB_STMT_EXTENSION; static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove); +static int madb_alloc_stmt_fields(MYSQL_STMT *stmt); static my_bool is_not_null= 0; static my_bool is_null= 1; +extern int mthd_my_read_query_result(MYSQL *mysql); +extern int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length); + void stmt_set_error(MYSQL_STMT *stmt, unsigned int error_nr, const char *sqlstate, @@ -139,6 +154,22 @@ my_bool mthd_supported_buffer_type(enum enum_field_types type) case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_OBJECT: + case MYSQL_TYPE_ARRAY: + case MYSQL_TYPE_STRUCT: + case MYSQL_TYPE_CURSOR: + case MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE: + case MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE: + case MYSQL_TYPE_OB_TIMESTAMP_NANO: + case MYSQL_TYPE_OB_INTERVAL_YM: + case MYSQL_TYPE_OB_INTERVAL_DS: + case MYSQL_TYPE_OB_NUMBER_FLOAT: + case MYSQL_TYPE_OB_NVARCHAR2: + case MYSQL_TYPE_OB_NCHAR: + case MYSQL_TYPE_OB_UROWID: + case MYSQL_TYPE_ORA_BLOB: + case MYSQL_TYPE_ORA_CLOB: + case MYSQL_TYPE_OB_RAW: return 1; break; default: @@ -305,21 +336,34 @@ int mthd_stmt_read_all_rows(MYSQL_STMT *stmt) stmt->mysql->net.last_error); return(1); } - +// static int reinit_stmt_field(MYSQL_STMT *stmt); +static int madb_reinit_result_set_metadata(MYSQL_STMT *stmt); +static int +do_stmt_read_row_from_oracle_implicit_cursor(MYSQL_STMT *stmt, unsigned char **row, my_bool is_need_fetch_from_server); static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row) { uchar buf[STMT_ID_LENGTH + 4]; MYSQL_DATA *result= &stmt->result; - + int back_status = 0; if (stmt->state < MYSQL_STMT_USE_OR_STORE_CALLED) { SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); return(1); } - + if (stmt->mysql == NULL) + { + SET_CLIENT_STMT_ERROR(stmt, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0); + return(1); + } + if (stmt->mysql->oracle_mode) + { + return do_stmt_read_row_from_oracle_implicit_cursor(stmt, row, FALSE); + } + back_status = stmt->mysql->status; /* do we have some prefetched rows available ? */ if (stmt->result_cursor) return(stmt_buffered_fetch(stmt, row)); + //if no scroll cursor, when last row will return no data ,and next request will fetch to server if (stmt->upsert_status.server_status & SERVER_STATUS_LAST_ROW_SENT) stmt->upsert_status.server_status&= ~SERVER_STATUS_LAST_ROW_SENT; else @@ -337,7 +381,22 @@ static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row) ma_free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); result->data= 0; result->rows= 0; - + if (stmt->mysql->oracle_mode) + { + //oracle mode return mata info + if (mthd_my_read_query_result(stmt->mysql)) + { + SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, + stmt->mysql->net.last_error); + return (1); + } else if (madb_reinit_result_set_metadata(stmt)) //TODO need to check if could skip next + { + DBUG_RETURN(1); + }else { + //the status will change in mthd_my_read_query_result, reset it here + stmt->mysql->status = back_status; + } + } if (stmt->mysql->methods->db_stmt_read_all_rows(stmt)) return(1); @@ -348,6 +407,384 @@ static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row) return(MYSQL_NO_DATA); } +static void skip_result_fixed(MYSQL_BIND *param, + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) + +{ + (*row)+= param->pack_length; +} + + +static void skip_result_with_length(MYSQL_BIND *param __attribute__((unused)), + MYSQL_FIELD *field __attribute__((unused)), + uchar **row) + +{ + ulong length= net_field_length(row); + (*row)+= length; +} + + +static void skip_result_string(MYSQL_BIND *param __attribute__((unused)), + MYSQL_FIELD *field, + uchar **row) + +{ + ulong length= net_field_length(row); + (*row)+= length; + if (field->max_length < length) + field->max_length= length; +} + +static int +do_stmt_read_row_from_oracle_cursor(MYSQL_STMT *stmt, unsigned char **row, my_bool is_need_buffer_row) +{ + int back_status = stmt->mysql->status; + if (stmt->result_cursor) + return(stmt_buffered_fetch(stmt, row)); + //no scroll cursor when last row return no_data + if (stmt->upsert_status.server_status & SERVER_STATUS_LAST_ROW_SENT) + stmt->upsert_status.server_status &= ~SERVER_STATUS_LAST_ROW_SENT; + else + { + MYSQL_DATA *result= &stmt->result; + MYSQL* mysql = stmt->mysql; +// int ret = 0; + uchar* buf = NULL; + uchar* p = NULL; + ulong buf_len = 0; + ulong buf_offset = 0; + ulong null_byte_offset = 0; + ulong pkt_len = 0; + + UNUSED(null_byte_offset); + UNUSED(buf_offset); + UNUSED(pkt_len); + UNUSED(is_need_buffer_row); + + if (!mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + return(1); + } + buf_len = STMT_ID_LENGTH /*statement id*/ + + 4 /*number of rows to fetch*/; + + if (NULL == (p = buf = malloc(buf_len))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(1); + } + memset(buf, 0, buf_len); + int4store(p, stmt->stmt_id); + int4store(p + STMT_ID_LENGTH, stmt->prefetch_rows); + + if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH, (char *)buf, buf_len, 1, stmt)) + { + UPDATE_STMT_ERROR(stmt); + free(buf); + return(1); + } + + /* free previously allocated buffer */ + ma_free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); + result->data= 0; + result->rows= 0; + if (stmt->mysql->oracle_mode) + { + //oracle mode return mata info + if (mthd_my_read_query_result(stmt->mysql)) + { + SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, + stmt->mysql->net.last_error); + free(buf); + return (1); + } + /** Not need to reinit fields for stmt, for it cannot update the MYSQL_RES's fileds which has returned before. + * If need get last the fields info for fetch, it need open it and get newest MYSQL_RES. */ + else if (madb_alloc_stmt_fields(stmt)) + { + DBUG_RETURN(1); + } + else { + //the status will change in mthd_my_read_query_result, reset it here + stmt->mysql->status = back_status; + } + } + if (stmt->mysql->methods->db_stmt_read_all_rows(stmt)) /* mthd_stmt_read_all_rows */ + { + free(buf); + return(1); + } + stmt->upsert_status.server_status = mysql->server_status; + //if (ret = stmt_buffered_fetch(stmt, row)) { + // free(buf); + // return (ret); + //} + free(buf); + return 0; + } + /* no more cursor data available */ + *row= NULL; + return(MYSQL_NO_DATA); +} + +/* Interface used by Cursor data of PL/NON-Block's out parameters for OBCI or same situation. + * The result juse be stored to stmt->result_cursor. */ +int STDCALL mysql_stmt_fetch_oracle_cursor(MYSQL_STMT *stmt) +{ + int rc; + uchar *row; + // enter ("mysql_stmt_fetch"); + + if ((rc= do_stmt_read_row_from_oracle_cursor(stmt, &row, FALSE))) + { + stmt->state= MYSQL_STMT_FETCH_DONE; + stmt->mysql->status= MYSQL_STATUS_READY; + /* to fetch data again, stmt must be executed again */ + return(rc); + } + //rc= stmt->mysql->methods->db_stmt_fetch_to_bind(stmt, row); + stmt->state= MYSQL_STMT_USER_FETCHING; + CLEAR_CLIENT_ERROR(stmt->mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); + return rc; +// return mysql_stmt_fetch(stmt); +} + +static int +do_stmt_read_row_from_oracle_implicit_cursor(MYSQL_STMT *stmt, unsigned char **row, my_bool is_need_fetch_from_server) +{ + int back_status = stmt->mysql->status; + if (stmt->result_cursor && !is_need_fetch_from_server) + return(stmt_buffered_fetch(stmt, row)); + //no scroll cursor when last row return no_data + if (!(stmt->execute_mode & EXECUTE_STMT_SCROLLABLE_READONLY) && + (stmt->upsert_status.server_status & SERVER_STATUS_LAST_ROW_SENT)) + stmt->upsert_status.server_status &= ~SERVER_STATUS_LAST_ROW_SENT; + else if ((stmt->execute_mode & EXECUTE_STMT_SCROLLABLE_READONLY) && + (stmt->upsert_status.server_status & SERVER_STATUS_LAST_ROW_SENT) && + (stmt->orientation == CURSOR_FETCH_DEFAULT|| + stmt->orientation == CURSOR_FETCH_NEXT)) + { + //scroll cursor when last_row_sent will return no_data + } + else + { + MYSQL_BIND *param, *end; + uint pos = 0; + MYSQL_DATA *result= &stmt->result; + MYSQL* mysql = stmt->mysql; + int ret = 0; + int extend_flag = 0; + uchar* buf = NULL; + uchar* p = NULL; + ulong buf_len = 0; + ulong buf_offset = 0; + ulong null_byte_offset = 0; + my_bool send_fetch_flag = FALSE; + ulong pkt_len = 0; + my_bool column_flag = FALSE; + ulong bind_count; + + if (!mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + return(1); + } + buf_len = STMT_ID_LENGTH /*statement id*/ + + 4 /*number of rows to fetch*/ + + 2 /*orientation*/ + + 4 /*offset to be used change the current row position */ + + 4 /*extend flag*/ + + 1 /*prealloc for null bitmap */; + if (NULL == (p = buf = malloc(buf_len))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(1); + } + memset(buf, 0, buf_len); + int4store(p, stmt->stmt_id); + int4store(p + STMT_ID_LENGTH, stmt->prefetch_rows); + int2store(p + 8, stmt->orientation); + int4store(p + 10, stmt->fetch_offset); + if (stmt->execute_mode & EXECUTE_STMT_SCROLLABLE_READONLY) + { + if (get_support_send_fetch_flag(stmt->mysql)) + { + send_fetch_flag = TRUE; + //int4store(buf + 14, FETCH_RETURN_EXTRA_OK); + extend_flag = FETCH_RETURN_EXTRA_OK; + } + } + + for (param = stmt->bind, end = param + stmt->field_count; param < end; ++param) + { + if (param->piece_data_used) + { + extend_flag |= FETCH_HAS_PIECE_COLUMN; + column_flag = TRUE; + break; + } + } + + int4store(p + 14, extend_flag); + p += 18; + + if (stmt->field_count && column_flag) + { + uint null_count = 0; + + /* Reserve place for null-marker bytes */ + null_count= (stmt->field_count+7) /8; + buf_offset = p - buf; + null_byte_offset = buf_offset; + /* realloc memory if space if not enough for null_count(all field) */ + if (null_count > buf_len - buf_offset) + { + buf_len = buf_offset + null_count; /* get new length */ + if (NULL == (buf = (uchar*)realloc(buf, buf_len))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(1); + } + p = buf + buf_offset; + memset(p, 0, null_count); //null bitmap + p += null_count; + } + + bind_count = stmt->field_count; + end = param + bind_count; + for (pos = 0; pos < stmt->field_count; pos++) + { + if (stmt->bind[pos].piece_data_used) + { + (buf + null_byte_offset)[pos/8] |= (uchar) (1 << (pos & 7)); + } + } + } + + if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH, (char *)buf, buf_len, 1, stmt)) + { + UPDATE_STMT_ERROR(stmt); + free(buf); + return(1); + } + + /* free previously allocated buffer */ + ma_free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); + result->data= 0; + result->rows= 0; + if (stmt->mysql->oracle_mode) + { + //oracle mode return mata info + if (mthd_my_read_query_result(stmt->mysql)) + { + SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, + stmt->mysql->net.last_error); + free(buf); + return (1); + } + /** Not need to reinit fields for stmt, for it cannot update the MYSQL_RES's fileds which has returned before. + * If need get last the fields info for fetch, it need open it and get newest MYSQL_RES. + else if (reinit_stmt_field(stmt)) + { + DBUG_RETURN(1); + } */ + else { + //the status will change in mthd_my_read_query_result, reset it here + stmt->mysql->status = back_status; + } + } + if (stmt->mysql->methods->db_stmt_read_all_rows(stmt)) + { + free(buf); + return(1); + } + if (send_fetch_flag) { + //extra ok packet + if (mysql->server_status & SERVER_MORE_RESULTS_EXIST) + { + if ((pkt_len= ma_net_safe_read(mysql)) == packet_error) + { + free(buf); + return (1); + } + mysql->server_status &= ~(SERVER_MORE_RESULTS_EXIST); + if (mysql->net.read_pos[0] == 0) { + ma_read_ok_packet(mysql, &mysql->net.read_pos[1], pkt_len);//extra ok packet + stmt->upsert_status.affected_rows= stmt->mysql->affected_rows; + } + else + { + end_server(mysql); + SET_CLIENT_ERROR(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); + snprintf(mysql->net.last_error, MYSQL_ERRMSG_SIZE, "Wrong packet: unexpect pkt"); + free(buf); + return (1); + } + } + } + stmt->upsert_status.server_status = mysql->server_status; + if ((ret = stmt_buffered_fetch(stmt, row))) { + free(buf); + return (ret); + } + free(buf); + return 0; + } + /* no more cursor data available */ + *row= NULL; + return(MYSQL_NO_DATA); +} +int STDCALL mysql_stmt_fetch_oracle_implicit_cursor(MYSQL_STMT *stmt, my_bool is_need_fetch_from_server) +{ + uchar *row; + int rc = 0; + if (stmt->state <= MYSQL_STMT_EXECUTED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + return(1); + } + if ((rc = do_stmt_read_row_from_oracle_implicit_cursor(stmt, &row, is_need_fetch_from_server))) + { + stmt->state= MYSQL_STMT_FETCH_DONE; + stmt->mysql->status= MYSQL_STATUS_READY; + /* to fetch data again, stmt must be executed again */ + return(rc); + } + rc= stmt->mysql->methods->db_stmt_fetch_to_bind(stmt, row); + stmt->state= MYSQL_STMT_USER_FETCHING; + CLEAR_CLIENT_ERROR(stmt->mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); + return rc; +} + +int STDCALL mysql_stmt_fetch_oracle_buffered_result(MYSQL_STMT *stmt) +{ + uchar *row; + int rc = 0; + if (stmt->state <= MYSQL_STMT_EXECUTED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + return(1); + } + /* just read the buffered result for some situation, not to fetch the result from ObServer any more */ + if (rc = stmt_buffered_fetch(stmt, &row)) + { + stmt->state= MYSQL_STMT_FETCH_DONE; + stmt->mysql->status= MYSQL_STATUS_READY; + /* to fetch data again, stmt must be executed again */ + return(rc); + } + rc= stmt->mysql->methods->db_stmt_fetch_to_bind(stmt, row); + stmt->state= MYSQL_STMT_USER_FETCHING; + CLEAR_CLIENT_ERROR(stmt->mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); + return rc; +} + /* flush one result set */ void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt) { @@ -407,6 +844,7 @@ int mthd_stmt_fetch_to_bind(MYSQL_STMT *stmt, unsigned char *row) } } else { + stmt->bind[i].mysql = stmt->mysql; stmt->bind[i].u.row_ptr= row; if (!stmt->bind_result_done || stmt->bind[i].flags & MADB_BIND_DUMMY) @@ -433,7 +871,12 @@ int mthd_stmt_fetch_to_bind(MYSQL_STMT *stmt, unsigned char *row) if (!stmt->bind[i].is_null) stmt->bind[i].is_null= &stmt->bind[i].is_null_value; *stmt->bind[i].is_null= 0; - mysql_ps_fetch_functions[stmt->fields[i].type].func(&stmt->bind[i], &stmt->fields[i], &row); + if (stmt->bind[i].no_need_to_parser_result) { + //skip directly + mysql_ps_fetch_functions[MAX_NO_FIELD_TYPES].func(&stmt->bind[i], &stmt->fields[i], &row); + } else { + mysql_ps_fetch_functions[stmt->fields[i].type].func(&stmt->bind[i], &stmt->fields[i], &row); + } if (stmt->mysql->options.report_data_truncation) truncations+= *stmt->bind[i].error; } @@ -463,8 +906,14 @@ MYSQL_RES *_mysql_stmt_use_result(MYSQL_STMT *stmt) CLEAR_CLIENT_STMT_ERROR(stmt); stmt->state = MYSQL_STMT_USE_OR_STORE_CALLED; - if (!stmt->cursor_exists) - stmt->fetch_row_func= stmt_unbuffered_fetch; //mysql_stmt_fetch_unbuffered_row; + if (!stmt->cursor_exists) { + if (stmt->use_prepare_execute) { + //new protocol should all cursor, but aray binding batch error not cursor + stmt->fetch_row_func = stmt_buffered_fetch; + } else{ + stmt->fetch_row_func= stmt_unbuffered_fetch; //mysql_stmt_fetch_unbuffered_row; + } + } else stmt->fetch_row_func= stmt_cursor_fetch; @@ -538,6 +987,108 @@ static void *ma_get_buffer_offset(MYSQL_STMT *stmt, enum enum_field_types type, return buffer; } +static void store_param_object_type(MYSQL *mysql, unsigned char **pos, MYSQL_COMPLEX_BIND_OBJECT *param) +{ + ulong len; + struct st_complex_type *complex_type = get_complex_type_with_local(mysql, param->type_name); + + if (NULL != complex_type) { + //schema_name + len= strlen((char *)complex_type->owner_name); + *pos = mysql_net_store_length(*pos, len); + memcpy(*pos, complex_type->owner_name, len); + *pos += len; + + //type_name + len= strlen((char *)complex_type->type_name); + *pos = mysql_net_store_length(*pos, len); + memcpy(*pos, complex_type->type_name, len); + *pos += len; + + //version + *pos = mysql_net_store_length(*pos, complex_type->version); + } else { + //schema_name + *pos = mysql_net_store_length(*pos, 0); + + //type_name + len= strlen((char *)param->type_name); + *pos = mysql_net_store_length(*pos, len); + memcpy(*pos, param->type_name, len); + *pos += len; + + //version + *pos = mysql_net_store_length(*pos, 0); + } +} + +static void store_param_plarray_type(MYSQL *mysql, unsigned char **pos, MYSQL_COMPLEX_BIND_PLARRAY *param); + +static void store_param_array_type(MYSQL *mysql, unsigned char **pos, MYSQL_COMPLEX_BIND_ARRAY *param) +{ + MYSQL_COMPLEX_BIND_HEADER *header = (MYSQL_COMPLEX_BIND_HEADER *)param->buffer; + + //shcema_name + *pos = mysql_net_store_length(*pos, 0); + //type_name + *pos = mysql_net_store_length(*pos, 0); + //version + *pos = mysql_net_store_length(*pos, 0); + + if (MYSQL_TYPE_OBJECT == header->buffer_type) { + **pos = MYSQL_TYPE_OBJECT; + (*pos)++; + + store_param_object_type(mysql, pos, (MYSQL_COMPLEX_BIND_OBJECT *)header); + } else if (MYSQL_TYPE_ARRAY == header->buffer_type) { + **pos = MYSQL_TYPE_OBJECT; + (*pos)++; + + store_param_array_type(mysql, pos, (MYSQL_COMPLEX_BIND_ARRAY *) header); + } else if (MYSQL_TYPE_PLARRAY == header->buffer_type) { + **pos = MYSQL_TYPE_OBJECT; + (*pos)++; + + store_param_plarray_type(mysql, pos, (MYSQL_COMPLEX_BIND_PLARRAY *) header); + } else { + **pos = (uchar) header->buffer_type; + (*pos)++; + } +} + +// plarray can have no elements and cannot be treated like array +static void store_param_plarray_type(MYSQL *mysql, unsigned char **pos, MYSQL_COMPLEX_BIND_PLARRAY *param) +{ + MYSQL_COMPLEX_BIND_HEADER *header = (MYSQL_COMPLEX_BIND_HEADER *)param->buffer; + + //shcema_name + *pos = mysql_net_store_length(*pos, 0); + //type_name + *pos = mysql_net_store_length(*pos, 0); + //version + *pos = mysql_net_store_length(*pos, 0); + + if (MYSQL_TYPE_OBJECT == param->elem_type) { + **pos = MYSQL_TYPE_OBJECT; + (*pos)++; + + store_param_object_type(mysql, pos, (MYSQL_COMPLEX_BIND_OBJECT *)header); + } else if (MYSQL_TYPE_ARRAY == param->elem_type) { + **pos = MYSQL_TYPE_OBJECT; + (*pos)++; + + store_param_array_type(mysql, pos, (MYSQL_COMPLEX_BIND_ARRAY *) header); + } else if (MYSQL_TYPE_PLARRAY == param->elem_type) { + **pos = MYSQL_TYPE_OBJECT; + (*pos)++; + + store_param_plarray_type(mysql, pos, (MYSQL_COMPLEX_BIND_PLARRAY *) header); + } else { + **pos = (uchar) param->elem_type; + (*pos)++; + } +} + static void store_param_type(MYSQL *mysql, unsigned char **pos, MYSQL_BIND *param) { uint typecode = param->buffer_type | (param->is_unsigned ? 32768 : 0); @@ -547,7 +1098,11 @@ static void store_param_type(MYSQL *mysql, unsigned char **pos, MYSQL_BIND *para if (MYSQL_TYPE_OBJECT == param->buffer_type) { MYSQL_COMPLEX_BIND_OBJECT *header = (MYSQL_COMPLEX_BIND_OBJECT *)param->buffer; if (NULL == header->type_name) { - store_param_array_type(mysql, pos, (MYSQL_COMPLEX_BIND_ARRAY *)header); + if (MYSQL_TYPE_PLARRAY == header->buffer_type) { + store_param_plarray_type(mysql, pos, (MYSQL_COMPLEX_BIND_PLARRAY *)header); + } else { + store_param_array_type(mysql, pos, (MYSQL_COMPLEX_BIND_ARRAY *)header); + } } else { store_param_object_type(mysql, pos, header); } @@ -685,8 +1240,8 @@ static void store_param_oracle_timestamp_tz_complex(unsigned char **pos, MYSQL_C store_param_oracle_timestamp_nano_complex(pos, param); - *(*pos++) = (char) tm->offset_hour; - *(*pos++) = (char) tm->offset_minute; + *((*pos)++) = (char) tm->offset_hour; + *((*pos)++) = (char) tm->offset_minute; if (tm->tz_name != NULL) { tz_length = strlen(tm->tz_name); @@ -720,13 +1275,13 @@ static void store_param_str_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_STRI *pos = to+length; } -static void store_param_str(MYSQL_STMT *stmt, int column, unsigned char **pos, unsigned long row_nr) +static void store_param_str(MYSQL_STMT *stmt, int column, unsigned char **pos, unsigned long row_nr, ulong len) { MYSQL_COMPLEX_BIND_STRING header; void *buf= ma_get_buffer_offset(stmt, stmt->params[column].buffer_type, stmt->params[column].buffer, row_nr); header.buffer = buf; - header.length = stmt->params[column].length; + header.length = len; store_param_str_complex(pos, &header); } @@ -764,10 +1319,10 @@ static void store_param_ob_lob(MYSQL_STMT *stmt, int column, unsigned char **pos store_param_ob_lob_complex(pos, &header); } -static void store_param_object_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_OBJECT *param); -static void store_param_array_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_ARRAY *param); +static void store_param_object_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_OBJECT *param, my_bool is_plarray_send_maxrarrlen); +static void store_param_array_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_ARRAY *param, my_bool is_plarray_send_maxrarrlen); -static void store_param_all_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_HEADER *header) +static void store_param_all_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_HEADER *header, my_bool is_plarray_send_maxrarrlen) { switch (header->buffer_type) { case MYSQL_TYPE_TINY: @@ -828,16 +1383,16 @@ static void store_param_all_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_HEAD store_param_str_complex(pos, (MYSQL_COMPLEX_BIND_STRING *)header); break; case MYSQL_TYPE_ARRAY: - store_param_array_complex(pos, (MYSQL_COMPLEX_BIND_ARRAY *)header); + store_param_array_complex(pos, (MYSQL_COMPLEX_BIND_ARRAY *)header, is_plarray_send_maxrarrlen); break; case MYSQL_TYPE_OBJECT: - store_param_object_complex(pos, (MYSQL_COMPLEX_BIND_OBJECT *)header); + store_param_object_complex(pos, (MYSQL_COMPLEX_BIND_OBJECT *)header, is_plarray_send_maxrarrlen); break; default: break; } } -static void store_param_object_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_OBJECT *param) +static void store_param_object_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_OBJECT *param, my_bool is_plarray_send_maxrarrlen) { unsigned int count = 0; unsigned int i = 0; @@ -862,19 +1417,32 @@ static void store_param_object_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_O if (header->is_null) { null_buff[i/8]|= (uchar) (1 << (i & 7)); } else { - store_param_all_complex(pos, header); + store_param_all_complex(pos, header, is_plarray_send_maxrarrlen); } skip_param_complex(&header); } } -static void store_param_array_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_ARRAY *param) +static void store_param_array_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_ARRAY *param, my_bool is_plarray_send_maxrarrlen) { unsigned int i = 0; unsigned char *null_buff = NULL; uint null_count; MYSQL_COMPLEX_BIND_HEADER *header = (MYSQL_COMPLEX_BIND_HEADER *) param->buffer; - - *pos = mysql_net_store_length(*pos, param->length); + MYSQL_COMPLEX_BIND_PLARRAY *plarray = (MYSQL_COMPLEX_BIND_PLARRAY *) param; + + if (TRUE == is_plarray_send_maxrarrlen && MYSQL_TYPE_PLARRAY == param->buffer_type + && plarray->length != plarray->maxrarr_len) { + // 这里本来是lenenc_int, 如果是PLArray,并且length != maxrarr_len, 默认第一字节为fe,后面会有8字节 + // 高位4字节为maxrarr_len, 低位4字节为length + int1store((*pos), 0xfe); + *pos += 1; + int4store((*pos), plarray->length); + *pos += 4; + int4store((*pos), plarray->maxrarr_len); + *pos += 4; + } else { + *pos = mysql_net_store_length(*pos, param->length); + } null_buff = *pos; null_count = (param->length + 7) /8; memset(null_buff, 0, null_count); @@ -885,22 +1453,29 @@ static void store_param_array_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_AR if (header->is_null) { null_buff[i/8]|= (uchar) (1 << (i & 7)); } else { - store_param_all_complex(pos, header); + store_param_all_complex(pos, header, is_plarray_send_maxrarrlen); } skip_param_complex(&header); } } +static void store_param_piece_array_complex(unsigned char **pos, MYSQL_COMPLEX_BIND_ARRAY *param) +{ + // server协商:对于piece分段的数据,只传数组长度 + *pos = mysql_net_store_length(*pos, param->length); +} + static void store_param_object(MYSQL_STMT *stmt, int column, unsigned char **pos, unsigned long row_nr) { void *buf= ma_get_buffer_offset(stmt, stmt->params[column].buffer_type, stmt->params[column].buffer, row_nr); MYSQL_COMPLEX_BIND_OBJECT *header = (MYSQL_COMPLEX_BIND_OBJECT *)buf; + my_bool is_send_plarray_maxrarrlen = get_support_send_plarray_maxrarr_len(stmt->mysql); - if (MYSQL_TYPE_ARRAY == header->buffer_type) { - store_param_array_complex(pos, (MYSQL_COMPLEX_BIND_ARRAY *)header); - } else if (MYSQL_TYPE_OBJECT == header->buffer_type){ - store_param_object_complex(pos, header); + if (MYSQL_TYPE_ARRAY == header->buffer_type || MYSQL_TYPE_PLARRAY == header->buffer_type) { + store_param_array_complex(pos, (MYSQL_COMPLEX_BIND_ARRAY *)header, is_send_plarray_maxrarrlen); + } else if (MYSQL_TYPE_OBJECT == header->buffer_type) { + store_param_object_complex(pos, header, is_send_plarray_maxrarrlen); } } @@ -915,15 +1490,15 @@ static void store_param_##name(MYSQL_STMT *stmt, int column, unsigned char **pos store_param_##name##_complex(pos, &header); \ } -STORE_PARAM(tinyint) -STORE_PARAM(short) +// STORE_PARAM(tinyint) // unused +// STORE_PARAM(short) // unused STORE_PARAM(int32) -STORE_PARAM(int64) -STORE_PARAM(float) -STORE_PARAM(double) -STORE_PARAM(time) -STORE_PARAM(date) -STORE_PARAM(datetime) +// STORE_PARAM(int64) // unused +// STORE_PARAM(float) // unused +// STORE_PARAM(double) // unused +// STORE_PARAM(time) // unused +// STORE_PARAM(date) // unused +// STORE_PARAM(datetime) // unused STORE_PARAM(oracle_timestamp_nano) STORE_PARAM(oracle_timestamp_tz) @@ -1040,6 +1615,7 @@ int store_param(MYSQL_STMT *stmt, int column, unsigned char **p, unsigned long r case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_OB_RAW: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: @@ -1081,7 +1657,6 @@ int store_param(MYSQL_STMT *stmt, int column, unsigned char **p, unsigned long r case MYSQL_TYPE_ORA_CLOB: store_param_ob_lob(stmt, column, p, row_nr); break; - case MYSQL_TYPE_OB_RAW: case MYSQL_TYPE_OB_NVARCHAR2: case MYSQL_TYPE_OB_NCHAR: case MYSQL_TYPE_OB_NUMBER_FLOAT: @@ -1094,8 +1669,7 @@ int store_param(MYSQL_STMT *stmt, int column, unsigned char **p, unsigned long r len= ma_get_length(stmt, column, row_nr); if (len == (ulong)-1) len= (ulong)strlen((char *)buf); - stmt->params->length = len; - store_param_str(stmt, column, p, row_nr); + store_param_str(stmt, column, p, row_nr, len); } break; case MYSQL_TYPE_OBJECT: @@ -1224,6 +1798,29 @@ void skip_param_complex(MYSQL_COMPLEX_BIND_HEADER **param) } } +static ulong calculate_param_plarray_len(MYSQL_COMPLEX_BIND_PLARRAY *param) +{ + ulong len = 0; + uint i = 0; + MYSQL_COMPLEX_BIND_HEADER *header = (MYSQL_COMPLEX_BIND_HEADER *) param->buffer; + + //这里按照协议本来是lenenc_int类型,为了扩展这里默认9字节,高位4字节存放maxrarr_len,低位4字节存放length + //只有maxrarr_len不等于length的时候才需要都穿,否则只传一个 + if (param->maxrarr_len != param->length) { + len += 9; + } else { + len += mysql_store_length_size(param->length); + } + len += (param->length + 7) /8; + + for (i = 0; i < param->length; i++) { + len += calculate_param_complex_len(header); + skip_param_complex(&header); + } + + return len; +} + static ulong calculate_param_array_len(MYSQL_COMPLEX_BIND_ARRAY *param) { ulong len = 0; @@ -1354,8 +1951,12 @@ static ulong calculate_param_len(MYSQL_BIND *param) { if (MYSQL_TYPE_OBJECT == param->buffer_type) { MYSQL_COMPLEX_BIND_OBJECT *header = (MYSQL_COMPLEX_BIND_OBJECT *)param->buffer; - if (MYSQL_TYPE_ARRAY == header->buffer_type) { - return calculate_param_array_len((MYSQL_COMPLEX_BIND_ARRAY *)header); + if (MYSQL_TYPE_ARRAY == header->buffer_type || MYSQL_TYPE_PLARRAY == header->buffer_type) { + if (TRUE == get_support_send_plarray_maxrarr_len(param->mysql)) { + return calculate_param_plarray_len((MYSQL_COMPLEX_BIND_PLARRAY *)header); + } else { + return calculate_param_array_len((MYSQL_COMPLEX_BIND_ARRAY *)header); + } } else { return calculate_param_object_len(header); } @@ -1452,11 +2053,7 @@ unsigned char* mysql_stmt_execute_generate_simple_request(MYSQL_STMT *stmt, size } for (i = 0; i < stmt->param_count; i++) { - /* this differs from mysqlnd, c api supports unsigned !! */ - uint buffer_type= stmt->params[i].buffer_type | (stmt->params[i].is_unsigned ? 32768 : 0); - /* check if parameter requires indicator variable */ - int2store(p, buffer_type); - p+= 2; + store_param_type(stmt->mysql, &p, &stmt->params[i]); } } @@ -1472,12 +2069,31 @@ unsigned char* mysql_stmt_execute_generate_simple_request(MYSQL_STMT *stmt, size stmt->params[i].long_data_used= 0; } + //NULL value + if (!stmt->params[i].buffer || + (stmt->params[i].is_null && *stmt->params[i].is_null)) + { + has_data= FALSE; + } + if (has_data) { switch (stmt->params[i].buffer_type) { case MYSQL_TYPE_NULL: has_data= FALSE; break; + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_CURSOR: + /* type len has definitly defined in mysql_ps_fetch_functions */ + size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len; + break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: @@ -1493,6 +2109,14 @@ unsigned char* mysql_stmt_execute_generate_simple_request(MYSQL_STMT *stmt, size case MYSQL_TYPE_ENUM: case MYSQL_TYPE_BIT: case MYSQL_TYPE_SET: + case MYSQL_TYPE_OB_RAW: + case MYSQL_TYPE_OB_TIMESTAMP_NANO: + case MYSQL_TYPE_OB_UROWID: + case MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: size+= 5; /* max 8 bytes for size */ size+= (size_t)ma_get_length(stmt, i, 0); break; @@ -1505,7 +2129,8 @@ unsigned char* mysql_stmt_execute_generate_simple_request(MYSQL_STMT *stmt, size break; //end add to support oboracle type default: - size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len; + //get buffer len from param buffer + size += (size_t)ma_get_length(stmt, i, 0); break; } } @@ -1817,6 +2442,24 @@ my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, enum enum_stmt_attr_type a else stmt->prefetch_rows= *(long *)value; break; + case STMT_ATTR_ARRAY_BIND: + { + ulong array_bind_type; + array_bind_type = value ? *(ulong*) value : 0UL; + + if (CURSOR_TYPE_ARRAY_BIND & array_bind_type) { + stmt->flags |= CURSOR_TYPE_ARRAY_BIND; + } else { + stmt->flags &= ~CURSOR_TYPE_ARRAY_BIND; + } + + if (CURSOR_TYPE_SAVE_EXCEPTION & array_bind_type) { + stmt->flags |= CURSOR_TYPE_SAVE_EXCEPTION; + } else { + stmt->flags &= ~CURSOR_TYPE_SAVE_EXCEPTION; + } + break; + } case STMT_ATTR_PREBIND_PARAMS: if (stmt->state > MYSQL_STMT_INITTED) { @@ -2002,9 +2645,9 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) if (stmt->field_count && !stmt->bind) { - MA_MEM_ROOT *fields_ma_alloc_root= - &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; - if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + MA_MEM_ROOT *binds_ma_alloc_root = + &((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root; + if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(binds_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) { SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); return(1); @@ -2029,36 +2672,77 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) if (!stmt->bind[i].error) stmt->bind[i].error= &stmt->bind[i].error_value; + stmt->bind[i].skip_result= skip_result_fixed; /* set length values for numeric types */ switch(bind[i].buffer_type) { case MYSQL_TYPE_NULL: *stmt->bind[i].length= stmt->bind[i].length_value= 0; + stmt->bind[i].pack_length = 0; break; case MYSQL_TYPE_TINY: *stmt->bind[i].length= stmt->bind[i].length_value= 1; + stmt->bind[i].pack_length = 1; break; case MYSQL_TYPE_SHORT: case MYSQL_TYPE_YEAR: *stmt->bind[i].length= stmt->bind[i].length_value= 2; + stmt->bind[i].pack_length = 2; break; case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: case MYSQL_TYPE_FLOAT: *stmt->bind[i].length= stmt->bind[i].length_value= 4; + stmt->bind[i].pack_length = 4; break; case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_DOUBLE: *stmt->bind[i].length= stmt->bind[i].length_value= 8; + stmt->bind[i].pack_length = 8; break; case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATE: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP: *stmt->bind[i].length= stmt->bind[i].length_value= sizeof(MYSQL_TIME); + stmt->bind[i].skip_result= skip_result_with_length; + break; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_OB_NUMBER_FLOAT: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_ORA_BLOB: + case MYSQL_TYPE_ORA_CLOB: + case MYSQL_TYPE_OB_RAW: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_OB_NVARCHAR2: + case MYSQL_TYPE_OB_NCHAR: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_JSON: + case MYSQL_TYPE_OBJECT: + case MYSQL_TYPE_OB_TIMESTAMP_NANO: + case MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE: + case MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE: + case MYSQL_TYPE_OB_UROWID: + stmt->bind[i].skip_result= skip_result_string; break; default: break; } + /* not need to change buffer_type to MAX_NO_FIELD_TYPES, it will be set in + * mysql_stmt_fetch_oracle_cursor to skip if stmt->bind[i].no_need_to_parser_result set + */ + //if (stmt->bind[i].no_need_to_parser_result) { + // /* skip field directly not need to parser the result for this field */ + // stmt->bind[i].buffer_type= MAX_NO_FIELD_TYPES; + //} } stmt->bind_result_done= 1; CLEAR_CLIENT_STMT_ERROR(stmt); @@ -2070,11 +2754,14 @@ static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove) { char stmt_id[STMT_ID_LENGTH]; MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + MA_MEM_ROOT *binds_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root; /* clear memory */ ma_free_root(&stmt->result.alloc, MYF(0)); /* allocated in mysql_stmt_store_result */ ma_free_root(&stmt->mem_root,MYF(0)); + ma_free_root(&stmt->param_fields_mem_root,MYF(0)); /* allocted in mysql_stmt_init */ ma_free_root(fields_ma_alloc_root, MYF(0)); + ma_free_root(binds_ma_alloc_root, MYF(0)); if (stmt->mysql) { @@ -2149,6 +2836,10 @@ const char * STDCALL mysql_stmt_error(MYSQL_STMT *stmt) int mthd_stmt_fetch_row(MYSQL_STMT *stmt, unsigned char **row) { + if (stmt->mysql->oracle_mode && stmt->fetch_row_func == NULL) + { + stmt->fetch_row_func= stmt_cursor_fetch; + } return stmt->fetch_row_func(stmt, row); } @@ -2186,11 +2877,24 @@ int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt) rc= stmt->mysql->methods->db_stmt_fetch_to_bind(stmt, row); stmt->state= MYSQL_STMT_USER_FETCHING; + /* Different with 1.x + * Important mariadb will clean all error for stmt->mysql if get correct result. It is different for us + * to get error message when ResultSet + Error Packet is get + */ CLEAR_CLIENT_ERROR(stmt->mysql); CLEAR_CLIENT_STMT_ERROR(stmt); return(rc); } +static void fetch_result_with_piece(MYSQL_BIND *bind, MYSQL_FIELD *field, uchar **row) +{ + ulong length = *bind->length; + ulong copy_length = MIN(length, bind->buffer_length); + UNUSED(field); + memcpy(bind->buffer, (char *)*row, copy_length); + *bind->error = copy_length < length; +} + int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset) { if (stmt->state < MYSQL_STMT_USER_FETCHING || column >= stmt->field_count || @@ -2221,7 +2925,10 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned *bind[0].error= 0; bind[0].offset= offset; save_ptr= stmt->bind[column].u.row_ptr; - mysql_ps_fetch_functions[stmt->fields[column].type].func(&bind[0], &stmt->fields[column], &stmt->bind[column].u.row_ptr); + if (bind->piece_data_used) + fetch_result_with_piece(&bind[0], &stmt->fields[column], &stmt->bind[column].u.row_ptr); + else + mysql_ps_fetch_functions[stmt->fields[column].type].func(&bind[0], &stmt->fields[column], &stmt->bind[column].u.row_ptr); stmt->bind[column].u.row_ptr= save_ptr; } return(0); @@ -2267,9 +2974,12 @@ MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql) /* set default */ stmt->prefetch_rows= 1; + //need to check to free ma_init_alloc_root(&stmt->mem_root, 2048, 2048); ma_init_alloc_root(&stmt->result.alloc, 4096, 4096); + ma_init_alloc_root(&stmt->param_fields_mem_root, 2048, 2048); ma_init_alloc_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, 2048, 2048); + ma_init_alloc_root(&((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root, 2048, 2048); return(stmt); } @@ -2307,12 +3017,13 @@ my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt) my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt) { MYSQL_DATA *result; - + MA_MEM_ROOT *param_fields_mem_root= &stmt->param_fields_mem_root; if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7 + ma_extended_type_info_rows(stmt->mysql)))) return(1); - - free_rows(result); + if (!(stmt->param_fields= unpack_fields(stmt->mysql, result, param_fields_mem_root, + stmt->param_count, 0))) + return(1); return(0); } @@ -2340,7 +3051,8 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned lon MYSQL *mysql= stmt->mysql; int rc= 1; my_bool is_multi= 0; - + FLT_DECLARE; + if (!stmt->mysql) { SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); @@ -2364,14 +3076,16 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned lon reset stmt and free all buffers and close the statement on server side. Statement handle will get a new stmt_id */ - if (!is_multi) + if (!is_multi && FALSE == get_use_protocol_ob20(mysql)) ma_multi_command(mysql, COM_MULTI_ENABLED); if (mysql_stmt_internal_reset(stmt, 1)) goto fail; ma_free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + ma_free_root(&stmt->param_fields_mem_root, MYF(MY_KEEP_PREALLOC)); ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, MYF(0)); + ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root, MYF(0)); stmt->param_count= 0; stmt->field_count= 0; @@ -2382,10 +3096,13 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned lon sizeof(stmt_id), 1, stmt)) goto fail; } + + FLT_BEFORE_COMMAND(0, FLT_TAG_COMMAND_NAME, "\"mysql_stmt_prepare\""); + if (mysql->methods->db_command(mysql, COM_STMT_PREPARE, query, length, 1, stmt)) goto fail; - if (!is_multi && mysql->net.extension->multi_status == COM_MULTI_ENABLED) + if (!is_multi && mysql->net.extension->multi_status == COM_MULTI_ENABLED && FALSE == get_use_protocol_ob20(mysql)) ma_multi_command(mysql, COM_MULTI_END); if (mysql->net.extension->multi_status > COM_MULTI_OFF) @@ -2430,8 +3147,8 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned lon /* allocated bind buffer for result */ if (stmt->field_count) { - MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; - if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + MA_MEM_ROOT *binds_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root; + if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(binds_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) { SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); goto fail; @@ -2439,6 +3156,10 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned lon memset(stmt->bind, 0, sizeof(MYSQL_BIND) * stmt->field_count); } stmt->state = MYSQL_STMT_PREPARED; + + // end trace + FLT_AFTER_COMMAND; + return(0); fail: @@ -2509,6 +3230,7 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) SERVER_PS_OUT_PARAMS set */ if (last_server_status & SERVER_PS_OUT_PARAMS && + !(stmt->mysql->oracle_mode) && /* It not have any resultsets returned in Oracle mode */ !(stmt->mysql->server_status & SERVER_MORE_RESULTS_EXIST)) stmt->mysql->server_status|= SERVER_MORE_RESULTS_EXIST; @@ -2532,20 +3254,24 @@ static int madb_alloc_stmt_fields(MYSQL_STMT *stmt) { uint i; MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + MA_MEM_ROOT *binds_ma_alloc_root = &((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root; if (stmt->mysql->field_count) { ma_free_root(fields_ma_alloc_root, MYF(0)); + ma_free_root(binds_ma_alloc_root, MYF(0)); if (!(stmt->fields= (MYSQL_FIELD *)ma_alloc_root(fields_ma_alloc_root, sizeof(MYSQL_FIELD) * stmt->mysql->field_count))) { SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); return(1); } + memset(stmt->fields, 0, sizeof(MYSQL_FIELD) * stmt->mysql->field_count); stmt->field_count= stmt->mysql->field_count; for (i=0; i < stmt->field_count; i++) { + memcpy(&stmt->fields[i], &stmt->mysql->fields[i], sizeof(MYSQL_FIELD)); if (stmt->mysql->fields[i].db) stmt->fields[i].db= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].db); if (stmt->mysql->fields[i].table) @@ -2559,19 +3285,19 @@ static int madb_alloc_stmt_fields(MYSQL_STMT *stmt) if (stmt->mysql->fields[i].catalog) stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].catalog); stmt->fields[i].def= stmt->mysql->fields[i].def ? ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].def) : NULL; - stmt->fields[i].type= stmt->mysql->fields[i].type; - stmt->fields[i].length= stmt->mysql->fields[i].length; - stmt->fields[i].flags= stmt->mysql->fields[i].flags; - stmt->fields[i].decimals= stmt->mysql->fields[i].decimals; - stmt->fields[i].charsetnr= stmt->mysql->fields[i].charsetnr; - stmt->fields[i].max_length= stmt->mysql->fields[i].max_length; stmt->fields[i].extension= stmt->mysql->fields[i].extension ? ma_field_extension_deep_dup(fields_ma_alloc_root, stmt->mysql->fields[i].extension) : NULL; + if (MYSQL_TYPE_OBJECT == stmt->fields[i].type) { + if (stmt->mysql->fields[i].owner_name) + stmt->fields[i].owner_name = (unsigned char *)ma_strdup_root(fields_ma_alloc_root, (char *)(stmt->mysql->fields[i].owner_name)); + if (stmt->mysql->fields[i].type_name) + stmt->fields[i].owner_name = (unsigned char *)ma_strdup_root(fields_ma_alloc_root, (char *)(stmt->mysql->fields[i].type_name)); + } } - if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(binds_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) { SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); return(1); @@ -2582,94 +3308,146 @@ static int madb_alloc_stmt_fields(MYSQL_STMT *stmt) return(0); } +static int mthd_my_read_prepare_execute_result(MYSQL *mysql) +{ + // uchar *pos; + ulong field_count; + MYSQL_DATA *fields; + field_count = mysql->field_count; + free_old_query(mysql); /* Free old result */ +// get_info: + // pos=(uchar*) mysql->net.read_pos; + if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT)) + mysql->server_status|= SERVER_STATUS_IN_TRANS; + + if (!(fields=mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0, + 7 + ma_extended_type_info_rows(mysql)))) + return(-1); + if (!(mysql->fields=unpack_fields(mysql, fields, &mysql->field_alloc, + (uint) field_count, 1))) + return(-1); + mysql->status=MYSQL_STATUS_GET_RESULT; + mysql->field_count=field_count; + return(0); +} +static int stmt_read_prepare_execute_response(MYSQL_STMT* stmt); int stmt_read_execute_response(MYSQL_STMT *stmt) { MYSQL *mysql= stmt->mysql; int ret; + my_bool is_exact_fetch_error = FALSE; if (!mysql) return(1); - - ret= test((mysql->methods->db_read_stmt_result && + if (stmt->use_prepare_execute) { + ret = stmt_read_prepare_execute_response(stmt); + } else { + ret= test((mysql->methods->db_read_stmt_result && mysql->methods->db_read_stmt_result(mysql))); + } /* if a reconnect occurred, our connection handle is invalid */ if (!stmt->mysql) return(1); /* update affected rows, also if an error occurred */ stmt->upsert_status.affected_rows= stmt->mysql->affected_rows; - + //as for use_prepare_execute exact fetch may error after result + // we should handle it specially if (ret) { SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, mysql->net.last_error); - stmt->state= MYSQL_STMT_PREPARED; - return(1); + if (stmt->use_prepare_execute && (mysql->net.last_errno == 1403|| mysql->net.last_errno == 1422)) + is_exact_fetch_error = TRUE; + if (stmt->is_handle_returning_into) /* not handing result with returning ... into (maybe has result)*/ + is_exact_fetch_error = TRUE; + else if (stmt->has_added_user_fields) /* handling result with added_user_fields, no matter it is ERR_PKT */ + is_exact_fetch_error = TRUE; + else if (!is_exact_fetch_error) + stmt->state= MYSQL_STMT_PREPARED; + else + stmt->state= MYSQL_STMT_EXECUTED; + if (stmt->use_prepare_execute) { + //when use_preaprae_execute status may be changed to MYSQL_STATUS_GET_RESULT in read_query_result + mysql->status = MYSQL_STATUS_READY; + } + if (!is_exact_fetch_error) + return(1); } stmt->upsert_status.last_insert_id= mysql->insert_id; stmt->upsert_status.server_status= mysql->server_status; stmt->upsert_status.warning_count= mysql->warning_count; - CLEAR_CLIENT_ERROR(mysql); - CLEAR_CLIENT_STMT_ERROR(stmt); + if (ret == 0) + { + CLEAR_CLIENT_ERROR(mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); - stmt->execute_count++; - stmt->send_types_to_server= 0; + stmt->execute_count++; + stmt->send_types_to_server= 0; - stmt->state= MYSQL_STMT_EXECUTED; + stmt->state= MYSQL_STMT_EXECUTED; + } if (mysql->field_count) { - if (!stmt->field_count || - mysql->server_status & SERVER_MORE_RESULTS_EXIST) /* fix for ps_bug: test_misc */ - { - MA_MEM_ROOT *fields_ma_alloc_root= - &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; - uint i; - - ma_free_root(fields_ma_alloc_root, MYF(0)); - if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, - sizeof(MYSQL_BIND) * mysql->field_count)) || - !(stmt->fields= (MYSQL_FIELD *)ma_alloc_root(fields_ma_alloc_root, - sizeof(MYSQL_FIELD) * mysql->field_count))) - { - SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); - return(1); - } - memset(stmt->bind, 0, sizeof(MYSQL_BIND) * mysql->field_count); - stmt->field_count= mysql->field_count; - - for (i=0; i < stmt->field_count; i++) - { - memcpy(&stmt->fields[i], &mysql->fields[i], sizeof(MYSQL_FIELD)); - - /* since all pointers will be incorrect if another statement will - be executed, so we need to allocate memory and copy the - information */ - if (mysql->fields[i].db) - stmt->fields[i].db= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].db); - if (mysql->fields[i].table) - stmt->fields[i].table= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].table); - if (mysql->fields[i].org_table) - stmt->fields[i].org_table= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].org_table); - if (mysql->fields[i].name) - stmt->fields[i].name= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].name); - if (mysql->fields[i].org_name) - stmt->fields[i].org_name= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].org_name); - if (mysql->fields[i].catalog) - stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].catalog); - if (mysql->fields[i].def) - stmt->fields[i].def= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].def); - stmt->fields[i].extension= - mysql->fields[i].extension ? - ma_field_extension_deep_dup(fields_ma_alloc_root, - mysql->fields[i].extension) : - NULL; - } - } + /*force update stmt->fields by execute reponse, for it not incorrect fields info returned by STMT_PREPARE */ + //TODO check to update field info for stmt for it has been set by OUT parma field info in mysql_stmt_prepare + //if (!stmt->field_count || + // mysql->server_status & SERVER_MORE_RESULTS_EXIST) /* fix for ps_bug: test_misc */ + //{ + if (madb_reinit_result_set_metadata(stmt)) + return 1; +// MA_MEM_ROOT *fields_ma_alloc_root= +// &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; +// MA_MEM_ROOT *binds_ma_alloc_root= +// &((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root; +// uint i; +// +// ma_free_root(fields_ma_alloc_root, MYF(0)); +// ma_free_root(binds_ma_alloc_root, MYF(0)); +// if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(binds_ma_alloc_root, +// sizeof(MYSQL_BIND) * mysql->field_count)) || +// !(stmt->fields= (MYSQL_FIELD *)ma_alloc_root(fields_ma_alloc_root, +// sizeof(MYSQL_FIELD) * mysql->field_count))) +// { +// SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); +// return(1); +// } +// memset(stmt->bind, 0, sizeof(MYSQL_BIND) * mysql->field_count); +// stmt->field_count= mysql->field_count; +// +// for (i=0; i < stmt->field_count; i++) +// { +// memcpy(&stmt->fields[i], &mysql->fields[i], sizeof(MYSQL_FIELD)); +// +// /* since all pointers will be incorrect if another statement will +// be executed, so we need to allocate memory and copy the +// information */ +// if (mysql->fields[i].db) +// stmt->fields[i].db= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].db); +// if (mysql->fields[i].table) +// stmt->fields[i].table= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].table); +// if (mysql->fields[i].org_table) +// stmt->fields[i].org_table= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].org_table); +// if (mysql->fields[i].name) +// stmt->fields[i].name= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].name); +// if (mysql->fields[i].org_name) +// stmt->fields[i].org_name= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].org_name); +// if (mysql->fields[i].catalog) +// stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].catalog); +// if (mysql->fields[i].def) +// stmt->fields[i].def= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].def); +// stmt->fields[i].extension= +// mysql->fields[i].extension ? +// ma_field_extension_deep_dup(fields_ma_alloc_root, +// mysql->fields[i].extension) : +// NULL; +// } + //} if ((stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) && - (stmt->flags & CURSOR_TYPE_READ_ONLY)) + ((stmt->flags & CURSOR_TYPE_READ_ONLY) || stmt->use_prepare_execute)) { stmt->cursor_exists = TRUE; mysql->status = MYSQL_STATUS_READY; @@ -2697,30 +3475,68 @@ int stmt_read_execute_response(MYSQL_STMT *stmt) { /* preferred is unbuffered read */ stmt->default_rset_handler = _mysql_stmt_use_result; - stmt->mysql->status= MYSQL_STATUS_STMT_RESULT; + if (stmt->use_prepare_execute) { + //here set this state to skip default_rset_handler + stmt->state = MYSQL_STMT_USE_OR_STORE_CALLED; + if (!stmt->cursor_exists) { + stmt->fetch_row_func = stmt_buffered_fetch; + } else + stmt->fetch_row_func= stmt_cursor_fetch; + stmt->mysql->status = MYSQL_STATUS_READY; + } else { + stmt->mysql->status= MYSQL_STATUS_STMT_RESULT; + } } stmt->state= MYSQL_STMT_WAITING_USE_OR_STORE; /* in certain cases parameter types can change: For example see bug 4026 (SELECT ?), so we need to update field information */ - if (mysql->field_count == stmt->field_count) - { - uint i; - for (i=0; i < stmt->field_count; i++) - { - stmt->fields[i].type= mysql->fields[i].type; - stmt->fields[i].length= mysql->fields[i].length; - stmt->fields[i].flags= mysql->fields[i].flags; - stmt->fields[i].decimals= mysql->fields[i].decimals; - stmt->fields[i].charsetnr= mysql->fields[i].charsetnr; - stmt->fields[i].max_length= mysql->fields[i].max_length; - } - } else - { - /* table was altered, see test_wl4166_2 */ - SET_CLIENT_STMT_ERROR(stmt, CR_NEW_STMT_METADATA, SQLSTATE_UNKNOWN, 0); - return(1); - } +// if (mysql->field_count == stmt->field_count) +// { +// uint i; +// if (!stmt->use_prepare_execute) { +// for (i=0; i < stmt->field_count; i++) +// { +// stmt->fields[i].type= mysql->fields[i].type; +// stmt->fields[i].length= mysql->fields[i].length; +// stmt->fields[i].flags= mysql->fields[i].flags; +// stmt->fields[i].decimals= mysql->fields[i].decimals; +// stmt->fields[i].charsetnr= mysql->fields[i].charsetnr; +// stmt->fields[i].max_length= mysql->fields[i].max_length; +// } +// } +// +// } else +// { +// /* table was altered, see test_wl4166_2 */ +// SET_CLIENT_STMT_ERROR(stmt, CR_NEW_STMT_METADATA, SQLSTATE_UNKNOWN, 0); +// return(1); +// } } + return(ret); +} + +my_bool cli_read_piece_result(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + ulong len=0; + + + if ((len = ma_net_safe_read(mysql)) == packet_error || len == 0) + { + end_server(mysql); + return(1); + } + + if (mysql->net.read_pos[0] == 0) + { + ma_read_ok_packet(mysql, &mysql->net.read_pos[1], len);//ok packet + } else + { + end_server(mysql); + SET_CLIENT_STMT_ERROR(stmt, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, "Wrong packet: unexpect pkt"); + return(1); + } + return(0); } @@ -2730,6 +3546,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) char *request; int ret; size_t request_len= 0; + FLT_DECLARE; if (!stmt->mysql) { @@ -2781,22 +3598,29 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) if (!request) return 1; - ret= stmt->mysql->methods->db_command(mysql, - stmt->array_size > 0 ? COM_STMT_BULK_EXECUTE : COM_STMT_EXECUTE, - request, request_len, 1, stmt); - if (request) - free(request); - if (ret) - { - UPDATE_STMT_ERROR(stmt); - return(1); + FLT_BEFORE_COMMAND(0, FLT_TAG_COMMAND_NAME, "\"mysql_stmt_execute\""); + + if (0 == ret) { + ret= stmt->mysql->methods->db_command(mysql, + stmt->array_size > 0 ? COM_STMT_BULK_EXECUTE : COM_STMT_EXECUTE, + request, request_len, 1, stmt); + if (request) + free(request); + + if (ret) { + UPDATE_STMT_ERROR(stmt); + ret = 1; + } else if (mysql->net.extension->multi_status > COM_MULTI_OFF) { + ret = 0; + } else { + ret = (stmt_read_execute_response(stmt)); + } } - if (mysql->net.extension->multi_status > COM_MULTI_OFF) - return(0); + FLT_AFTER_COMMAND; - return(stmt_read_execute_response(stmt)); + return ret; } static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) @@ -2872,8 +3696,12 @@ static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) { ulonglong i; for (i=0; i < stmt->param_count; i++) + { if (stmt->params[i].long_data_used) stmt->params[i].long_data_used= 0; + if (stmt->params[i].piece_data_used) + stmt->params[i].piece_data_used= 0; + } } } @@ -3026,6 +3854,58 @@ my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, return(0); } +my_bool STDCALL mysql_stmt_send_piece_data(MYSQL_STMT *stmt, unsigned int param_number, + const char *data, unsigned long length, char piece_type, char is_null) +{ + + int ret = 0; + size_t packet_len; + uchar *cmd_buff; + + if (param_number >= stmt->param_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0); + return(1); + } + + /* + Send long data packet if there is data or we're sending long data + for the first time. + */ + /** + * Packet: HEADER + PAYLOAD + * HEADER: + * stmt-id : 4B + * param-no : 2B + * piece-type : 1B + * is-null : 1B + * data-len : 8B (fix length) + * PAYLOAD: + * data : ${data-len}Bytes + */ + packet_len= PIECE_HEADER_SIZE + length; /* PIECE_HEADER_SIZE:16 */ + cmd_buff= NULL; + if (NULL == (cmd_buff = (uchar *)calloc(1, packet_len))) { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(1); + } + int4store(cmd_buff, stmt->stmt_id); + int2store(cmd_buff + STMT_ID_LENGTH, param_number); + cmd_buff[STMT_ID_LENGTH + 2]= piece_type; + cmd_buff[STMT_ID_LENGTH + 3]= is_null; + int8store(cmd_buff + STMT_ID_LENGTH + 4, length); + memcpy(cmd_buff + PIECE_HEADER_SIZE, data, length); + + // stmt->params[param_number].piece_data_used= 1; + ret= stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_SEND_PIECE_DATA, + (char *)cmd_buff, packet_len, 1, stmt); + if (ret || cli_read_piece_result(stmt)) + UPDATE_STMT_ERROR(stmt); + free(cmd_buff); + return(ret); +// } +} + unsigned long long STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt) { return stmt->upsert_status.last_insert_id; @@ -3038,10 +3918,97 @@ unsigned long long STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) MYSQL_RES* STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt __attribute__((unused))) { - /* server doesn't deliver any information yet, - so we just return NULL - */ - return(NULL); + MYSQL_RES *res; + + if (!stmt->param_count) + return(NULL); + + /* aloocate result set structutr and copy stmt information */ + if (!(res= (MYSQL_RES *)calloc(1, sizeof(MYSQL_RES)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return(NULL); + } + + res->eof= 1; + res->param_fields = stmt->param_fields; + res->param_count = stmt->param_count; + return(res); +} +/* Store type of parameter in network buffer. */ +static ulong calculate_param_object_type_len(MYSQL *mysql, MYSQL_COMPLEX_BIND_OBJECT *param) +{ + ulong retval; + ulong len; + struct st_complex_type *complex_type = get_complex_type(mysql, param->owner_name, param->type_name); + + if (NULL != complex_type) { + //schema_name + len = strlen((char *)complex_type->owner_name); + len += mysql_store_length_size(len); + retval = len; + + //type_name + len = strlen((char *)complex_type->type_name); + len += mysql_store_length_size(len); + retval += len; + + //version + len = mysql_store_length_size(complex_type->version); + retval += len; + } else { + //schema_name + retval = 1; + + //type_name + len = strlen((char *)param->type_name); + len += mysql_store_length_size(len); + retval += len; + + //version + retval += 1; + } + + return retval; +} + +static ulong calculate_param_array_type_len(MYSQL *mysql, MYSQL_COMPLEX_BIND_ARRAY *param) +{ + ulong len; + MYSQL_COMPLEX_BIND_HEADER *header = (MYSQL_COMPLEX_BIND_HEADER *)param->buffer; + + //shcema_name + //type_name + //version + len = 3; + + //elem_type + len += 1; + if (MYSQL_TYPE_OBJECT == header->buffer_type) { + MYSQL_COMPLEX_BIND_OBJECT sub_header; + sub_header.owner_name = ((MYSQL_COMPLEX_BIND_OBJECT*)header)->owner_name; + sub_header.type_name = ((MYSQL_COMPLEX_BIND_OBJECT*)header)->type_name; + len += calculate_param_object_type_len(mysql, &sub_header); + } else if (MYSQL_TYPE_ARRAY == header->buffer_type) { + MYSQL_COMPLEX_BIND_ARRAY sub_header; + sub_header.buffer = header->buffer; + len += calculate_param_array_type_len(mysql, &sub_header); + } + + return len; +} + +static ulong calculate_param_type_len(MYSQL *mysql, MYSQL_BIND *param) +{ + ulong len; + MYSQL_COMPLEX_BIND_OBJECT *header = (MYSQL_COMPLEX_BIND_OBJECT *)param->buffer; + if (NULL == header->type_name) { + len = calculate_param_array_type_len(mysql, (MYSQL_COMPLEX_BIND_ARRAY *)header); + } else { + len = calculate_param_object_type_len(mysql, header); + } + + return len; } my_bool STDCALL mysql_stmt_more_results(MYSQL_STMT *stmt) @@ -3169,7 +4136,9 @@ int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt, goto fail; ma_free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + ma_free_root(&stmt->param_fields_mem_root, MYF(MY_KEEP_PREALLOC)); ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, MYF(0)); + ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root, MYF(0)); stmt->field_count= 0; stmt->param_count= 0; stmt->params= 0; @@ -3223,8 +4192,8 @@ int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt, /* allocated bind buffer for result */ if (stmt->field_count) { - MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; - if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + MA_MEM_ROOT *binds_ma_alloc_root = &((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root; + if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(binds_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) { SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); goto fail; @@ -3254,3 +4223,1527 @@ MYSQL_FIELD * STDCALL mariadb_stmt_fetch_fields(MYSQL_STMT *stmt) return stmt->fields; return NULL; } + + +/* + * from obproxy to compute crc checksum + */ +#if defined(__GNUC__) && defined(__x86_64__) +/* opcodes taken from objdump of "crc32b (%%rdx), %%rcx" +for RHEL4 support (GCC 3 doesn't support this instruction) */ +#define crc32_sse42_byte \ + asm(".byte 0xf2, 0x48, 0x0f, 0x38, 0xf0, 0x0a" \ + : "=c"(crc) : "c"(crc), "d"(buf)); \ + len--, buf++ + +/* opcodes taken from objdump of "crc32q (%%rdx), %%rcx" +for RHEL4 support (GCC 3 doesn't support this instruction) */ +#define crc32_sse42_quadword \ + asm(".byte 0xf2, 0x48, 0x0f, 0x38, 0xf1, 0x0a" \ + : "=c"(crc) : "c"(crc), "d"(buf)); \ + len -= 8, buf += 8 + +inline static uint64_t crc64_sse42(uint64_t uCRC64, + const char *buf, int64_t len) +{ + uint64_t crc = uCRC64; + + if (NULL != buf && len > 0) { + while (len && ((uint64_t) buf & 7)) { + crc32_sse42_byte; + } + + while (len >= 32) { + crc32_sse42_quadword; + crc32_sse42_quadword; + crc32_sse42_quadword; + crc32_sse42_quadword; + } + + while (len >= 8) { + crc32_sse42_quadword; + } + + while (len) { + crc32_sse42_byte; + } + } + + return crc; +} +#endif /* defined(__GNUC__) && defined(__x86_64__) */ +/* + * for arm + */ +#if 0 +#if defined(__GNUC__) && defined(__aarch64__) +#define CRC32CX(crc, value) __asm__("crc32cx %w[c], %w[c], %x[v]":[c]"+r"(crc):[v]"r"(value)) +#define CRC32CW(crc, value) __asm__("crc32cw %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value)) +#define CRC32CH(crc, value) __asm__("crc32ch %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value)) +#define CRC32CB(crc, value) __asm__("crc32cb %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value)) +static uint64_t crc64_arm64(uint64_t crc, const char* p, uint64_t len) +{ + int64_t length = len; + while ((length -= sizeof(uint64_t)) >= 0) { + CRC32CX(crc, *((uint64_t*)p)); + p += sizeof(uint64_t); + } + if (length & sizeof(uint32_t)) { + CRC32CW(crc, *((uint32_t*)p)); + p += sizeof(uint32_t); + } + if (length & sizeof(uint16_t)) { + CRC32CH(crc, *((uint16_t*)p)); + p += sizeof(uint16_t); + } + if (length & sizeof(uint8_t)) { + CRC32CB(crc, *((uint8_t*)p)); + p += sizeof(uint8_t); + } + return crc; +} +#endif +#endif + +static uint64_t crc64_sse42_manually(uint64_t crc, const char *buf, int64_t len) +{ + /** + * crc32tab is generated by: + * // bit-reversed poly 0x1EDC6F41 + * const uint32_t poly = 0x82f63b78; + * for (int n = 0; n < 256; n++) { + * uint32_t c = (uint32_t)n; + * for (int k = 0; k < 8; k++) + * c = c & 1 ? poly ^ (c >> 1) : c >> 1; + * crc32tab[n] = c; + * } + */ + int64_t i = 0; + static const uint32_t crc32tab[] = + { + 0x00000000L, 0xf26b8303L, 0xe13b70f7L, 0x1350f3f4L, 0xc79a971fL, + 0x35f1141cL, 0x26a1e7e8L, 0xd4ca64ebL, 0x8ad958cfL, 0x78b2dbccL, + 0x6be22838L, 0x9989ab3bL, 0x4d43cfd0L, 0xbf284cd3L, 0xac78bf27L, + 0x5e133c24L, 0x105ec76fL, 0xe235446cL, 0xf165b798L, 0x030e349bL, + 0xd7c45070L, 0x25afd373L, 0x36ff2087L, 0xc494a384L, 0x9a879fa0L, + 0x68ec1ca3L, 0x7bbcef57L, 0x89d76c54L, 0x5d1d08bfL, 0xaf768bbcL, + 0xbc267848L, 0x4e4dfb4bL, 0x20bd8edeL, 0xd2d60dddL, 0xc186fe29L, + 0x33ed7d2aL, 0xe72719c1L, 0x154c9ac2L, 0x061c6936L, 0xf477ea35L, + 0xaa64d611L, 0x580f5512L, 0x4b5fa6e6L, 0xb93425e5L, 0x6dfe410eL, + 0x9f95c20dL, 0x8cc531f9L, 0x7eaeb2faL, 0x30e349b1L, 0xc288cab2L, + 0xd1d83946L, 0x23b3ba45L, 0xf779deaeL, 0x05125dadL, 0x1642ae59L, + 0xe4292d5aL, 0xba3a117eL, 0x4851927dL, 0x5b016189L, 0xa96ae28aL, + 0x7da08661L, 0x8fcb0562L, 0x9c9bf696L, 0x6ef07595L, 0x417b1dbcL, + 0xb3109ebfL, 0xa0406d4bL, 0x522bee48L, 0x86e18aa3L, 0x748a09a0L, + 0x67dafa54L, 0x95b17957L, 0xcba24573L, 0x39c9c670L, 0x2a993584L, + 0xd8f2b687L, 0x0c38d26cL, 0xfe53516fL, 0xed03a29bL, 0x1f682198L, + 0x5125dad3L, 0xa34e59d0L, 0xb01eaa24L, 0x42752927L, 0x96bf4dccL, + 0x64d4cecfL, 0x77843d3bL, 0x85efbe38L, 0xdbfc821cL, 0x2997011fL, + 0x3ac7f2ebL, 0xc8ac71e8L, 0x1c661503L, 0xee0d9600L, 0xfd5d65f4L, + 0x0f36e6f7L, 0x61c69362L, 0x93ad1061L, 0x80fde395L, 0x72966096L, + 0xa65c047dL, 0x5437877eL, 0x4767748aL, 0xb50cf789L, 0xeb1fcbadL, + 0x197448aeL, 0x0a24bb5aL, 0xf84f3859L, 0x2c855cb2L, 0xdeeedfb1L, + 0xcdbe2c45L, 0x3fd5af46L, 0x7198540dL, 0x83f3d70eL, 0x90a324faL, + 0x62c8a7f9L, 0xb602c312L, 0x44694011L, 0x5739b3e5L, 0xa55230e6L, + 0xfb410cc2L, 0x092a8fc1L, 0x1a7a7c35L, 0xe811ff36L, 0x3cdb9bddL, + 0xceb018deL, 0xdde0eb2aL, 0x2f8b6829L, 0x82f63b78L, 0x709db87bL, + 0x63cd4b8fL, 0x91a6c88cL, 0x456cac67L, 0xb7072f64L, 0xa457dc90L, + 0x563c5f93L, 0x082f63b7L, 0xfa44e0b4L, 0xe9141340L, 0x1b7f9043L, + 0xcfb5f4a8L, 0x3dde77abL, 0x2e8e845fL, 0xdce5075cL, 0x92a8fc17L, + 0x60c37f14L, 0x73938ce0L, 0x81f80fe3L, 0x55326b08L, 0xa759e80bL, + 0xb4091bffL, 0x466298fcL, 0x1871a4d8L, 0xea1a27dbL, 0xf94ad42fL, + 0x0b21572cL, 0xdfeb33c7L, 0x2d80b0c4L, 0x3ed04330L, 0xccbbc033L, + 0xa24bb5a6L, 0x502036a5L, 0x4370c551L, 0xb11b4652L, 0x65d122b9L, + 0x97baa1baL, 0x84ea524eL, 0x7681d14dL, 0x2892ed69L, 0xdaf96e6aL, + 0xc9a99d9eL, 0x3bc21e9dL, 0xef087a76L, 0x1d63f975L, 0x0e330a81L, + 0xfc588982L, 0xb21572c9L, 0x407ef1caL, 0x532e023eL, 0xa145813dL, + 0x758fe5d6L, 0x87e466d5L, 0x94b49521L, 0x66df1622L, 0x38cc2a06L, + 0xcaa7a905L, 0xd9f75af1L, 0x2b9cd9f2L, 0xff56bd19L, 0x0d3d3e1aL, + 0x1e6dcdeeL, 0xec064eedL, 0xc38d26c4L, 0x31e6a5c7L, 0x22b65633L, + 0xd0ddd530L, 0x0417b1dbL, 0xf67c32d8L, 0xe52cc12cL, 0x1747422fL, + 0x49547e0bL, 0xbb3ffd08L, 0xa86f0efcL, 0x5a048dffL, 0x8ecee914L, + 0x7ca56a17L, 0x6ff599e3L, 0x9d9e1ae0L, 0xd3d3e1abL, 0x21b862a8L, + 0x32e8915cL, 0xc083125fL, 0x144976b4L, 0xe622f5b7L, 0xf5720643L, + 0x07198540L, 0x590ab964L, 0xab613a67L, 0xb831c993L, 0x4a5a4a90L, + 0x9e902e7bL, 0x6cfbad78L, 0x7fab5e8cL, 0x8dc0dd8fL, 0xe330a81aL, + 0x115b2b19L, 0x020bd8edL, 0xf0605beeL, 0x24aa3f05L, 0xd6c1bc06L, + 0xc5914ff2L, 0x37faccf1L, 0x69e9f0d5L, 0x9b8273d6L, 0x88d28022L, + 0x7ab90321L, 0xae7367caL, 0x5c18e4c9L, 0x4f48173dL, 0xbd23943eL, + 0xf36e6f75L, 0x0105ec76L, 0x12551f82L, 0xe03e9c81L, 0x34f4f86aL, + 0xc69f7b69L, 0xd5cf889dL, 0x27a40b9eL, 0x79b737baL, 0x8bdcb4b9L, + 0x988c474dL, 0x6ae7c44eL, 0xbe2da0a5L, 0x4c4623a6L, 0x5f16d052L, + 0xad7d5351L + }; + + for (i = 0; i < len; ++i) + { + crc = crc32tab[(crc ^ buf[i]) & 0xff] ^ (crc >> 8); + } + + return crc; +} +typedef uint64_t (*ObCRC64Func)(uint64_t, const char *, int64_t); +uint64_t crc64_sse42_dispatch(uint64_t crc, const char *buf, int64_t len) +{ + uint32_t a = 0; + uint32_t b = 0; + uint32_t c = 0; + uint32_t d = 0; + ObCRC64Func ob_crc64_sse42_func; +#if defined(__GNUC__) && defined(__x86_64__) + asm("cpuid": "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(1)); + if (c & (1 << 20)) { + ob_crc64_sse42_func = &crc64_sse42; + } else { + ob_crc64_sse42_func = &crc64_sse42_manually; + } +// #elif defined(__GNUC__) && defined(__aarch64__) +// ob_crc64_sse42_func = &crc64_arm64; +#else + ob_crc64_sse42_func = &crc64_sse42_manually; +#endif + return (*ob_crc64_sse42_func)(crc, buf, len); +} +uint32 ob_crc32(uint64_t crc, const char *buf, int64_t len) +{ + uint64_t crc64 = crc64_sse42_dispatch(crc, buf, len); + return crc64 & 0xffffffff; +} + +uint64 ob_crc64(uint64_t crc, const char *buf, int64_t len) +{ + uint64_t crc64 = crc64_sse42_dispatch(crc, buf, len); + return crc64; +} + +typedef enum enum_sql_char_state +{ + NORMAL_CHAR_STATE = 0, + SINGLE_QUOTE_STATE, //find single quote + DOUBLE_QUOTE_STATE, //find double quote + SINGLE_LINE_COMMENT_STATE, //find -- ,single line comment + MULTI_LINE_COMMENT_STATE //find /* multi line comment +} sql_char_state; + +static unsigned int find_sql_param_count(const char *sql, int sql_len) +{ + int param_count = 0; + int i = 0; + int char_state = NORMAL_CHAR_STATE; + int is_line_first = 1; + if (sql == NULL) + { + return param_count; + } + for (i = 0; i < sql_len; i++) + { + switch(char_state) + { + case NORMAL_CHAR_STATE: + { + if (sql[i] == SINGLE_QUOTE) + { + char_state = SINGLE_QUOTE_STATE; + } + else if (sql[i] == DOUBLE_QUOTE) + { + char_state = DOUBLE_QUOTE_STATE; + } + else if (is_line_first && sql[i] == '-' && i+1 < sql_len && sql[i+1] == '-') // line begin with -- + { + char_state = SINGLE_LINE_COMMENT_STATE; + ++i; + } + else if (sql[i] == '/' && i+1 < sql_len && sql[i+1] == '*') // /* + { + char_state = MULTI_LINE_COMMENT_STATE; + ++i; + } + else if (sql[i] == '?') + { + ++param_count; + } + //Not need to handle like .. escape '/' escpically, it count be '/" in Oracle. +// else if ((sql[i] == 'l' || sql[i] == 'L') && i > 0 && sql_len - i > 4 +// //&& (sql[i-1] == ' ' || sql[i-1] == '\r' || sql[i-1] == '\n') +// && 0 == strncasecmp((char *)(&(sql[i])), "like", 4) +// && (sql[i+1] == ' ' || sql[i+1] == '\r' || sql[i+1] == '\n') +// ) { // check and handle the like/LIKE +// +// } + break; + } + case SINGLE_QUOTE_STATE: + { + /** Important! In oracle escape character for ' only is ', such as: + * --select '''' from dual; + * output: ' + * --select 'name'||'''' from dual; + * output: name'' + * \ name is not the escape character of '(quote) and "(double quote). + * */ + //if (sql[i] == SINGLE_QUOTE && sql[i - 1] != '\\') + if (sql[i] == SINGLE_QUOTE) + { + char_state = NORMAL_CHAR_STATE; + } + break; + } + case DOUBLE_QUOTE_STATE: + { + //if (sql[i] == DOUBLE_QUOTE && sql[i - 1] != '\\') + if (sql[i] == DOUBLE_QUOTE) + { + char_state = NORMAL_CHAR_STATE; + } + break; + } + case SINGLE_LINE_COMMENT_STATE: + { + if (sql[i] == '\n') + { + char_state = NORMAL_CHAR_STATE; + } + break; + } + case MULTI_LINE_COMMENT_STATE: + { + if (sql[i] == '*' && i+1 < sql_len && sql[i+1] == '/')// */ + { + char_state = NORMAL_CHAR_STATE; + ++i; + } + break; + } + default: + break; + } + is_line_first = 0; + if (sql[i] == '\n') + { + is_line_first = 1; + } + } + return param_count; +} +/* + * judge where can send plarray maxrarr len + * use version to judge + */ +my_bool determine_send_plarray_maxrarr_len(MYSQL *mysql) +{ + my_bool bret = FALSE; + int ival = SEND_PLARRAY_MAXRARRLEN_AUTO_OPEN; //default is AUTO + int tmp_val = 0; + char* env = getenv("ENABLE_PLARRAY_MAXRARRLEN"); + if (env) { + tmp_val = atoi(env); + if (tmp_val >= 0 && tmp_val < SEND_PLARRAY_MAXRARRLEN_FLAG_MAX) + { + ival = tmp_val; + } + } + if (ival == SEND_PLARRAY_MAXRARRLEN_FORCE_OPEN) + { + bret = TRUE; + } + else if (ival == SEND_PLARRAY_MAXRARRLEN_FORCE_CLOSE) + { + bret = FALSE; + } + else if (NULL != mysql) + { + if (!mysql->oracle_mode) + { + // only oracle mode use new protocol + } + else + { + if (mysql->ob_server_version >= SUPPORT_SEND_PLARRAY_MAXRARR_LEN) + { + bret = TRUE; + } + } + } + if (mysql) + { + mysql->can_send_plarray_maxrarr_len = bret; + } + DBUG_RETURN(bret); +} + +my_bool get_support_send_plarray_maxrarr_len(MYSQL *mysql) +{ + if (mysql) + { + return mysql->can_send_plarray_maxrarr_len; + } + return FALSE; +} +/* + * judge where can use PL bindbyname + * use version to judge + */ +my_bool determine_plarray_bindbyname(MYSQL *mysql) +{ + my_bool bret = FALSE; + int ival = PLARRAY_BINDBYNAME_AUTO_OPEN; //default is AUTO + int tmp_val = 0; + char* env = getenv("ENABLE_PLARRAY_BINDBYNAME"); + if (env) { + tmp_val = atoi(env); + if (tmp_val >= 0 && tmp_val < PLARRAY_BINDBYNAME_FLAG_MAX) + { + ival = tmp_val; + } + } + if (ival == PLARRAY_BINDBYNAME_FORCE_OPEN) + { + bret = TRUE; + } + else if (ival == PLARRAY_BINDBYNAME_FORCE_CLOSE) + { + bret = FALSE; + } + else if (NULL != mysql) + { + if (!mysql->oracle_mode) + { + // only oracle mode use new protocol + } + else + { + if (mysql->ob_server_version >= SUPPORT_PLARRAY_BINDBYNAME) + { + bret = TRUE; + } + } + } + if (mysql) + { + mysql->can_plarray_bindbyname = bret; + } + DBUG_RETURN(bret); +} + +my_bool get_support_plarray_bindbyname(MYSQL *mysql) +{ + if (mysql) + { + return mysql->can_plarray_bindbyname; + } + return FALSE; +} + +/* + * judge where can use protocol ob20 + * use capability flag to judge + */ +my_bool determine_protocol_ob20(MYSQL *mysql) +{ + my_bool bret = TRUE; + int ival = PROTOCOL_OB20_AUTO_OPEN; //default is AUTO + int tmp_val = 0; + char* env = getenv("ENABLE_PROTOCOL_OB20"); + if (env) { + tmp_val = atoi(env); + if (tmp_val >= 0 && tmp_val < PROTOCOL_OB20_FLAY_MAX) + { + ival = tmp_val; + } + } + if (ival == PROTOCOL_OB20_FORCE_OPEN) + { + bret = TRUE; + } + else if (ival == PROTOCOL_OB20_FORCE_CLOSE) + { + bret = FALSE; + } + if (mysql) + { + mysql->can_use_protocol_ob20 = bret; + } + DBUG_RETURN(bret); +} + +my_bool get_use_protocol_ob20(MYSQL *mysql) +{ + my_bool bret = FALSE; + if (mysql && (mysql->capability & OB_CAP_OB_PROTOCOL_V2)) { + bret = TRUE; + } + return bret; +} + +my_bool determine_full_link_trace(MYSQL *mysql) +{ + my_bool bret = TRUE; + int ival = PROTOCOL_FLT_AUTO_OPEN; //default is AUTO + int tmp_val = 0; + char* env = getenv("ENABLE_FLT"); + if (env) { + tmp_val = atoi(env); + if (tmp_val >= 0 && tmp_val < PROTOCOL_FLT_FLAY_MAX) + { + ival = tmp_val; + } + } + if (ival == PROTOCOL_FLT_FORCE_OPEN) + { + bret = TRUE; + } + else if (ival == PROTOCOL_FLT_FORCE_CLOSE) + { + bret = FALSE; + } + if (mysql) + { + mysql->can_use_full_link_trace = bret; + } + DBUG_RETURN(bret); +} + +my_bool get_use_full_link_trace(MYSQL *mysql) +{ + my_bool bret = FALSE; + if (mysql && (mysql->capability & OB_CAP_FULL_LINK_TRACE)) { + bret = TRUE; + if (mysql->capability & OB_CAP_PROXY_NEW_EXTRA_INFO) { + // do nothing, bret = TRUE; + } else { + bret = FALSE; + } + } + return bret; +} +/* + * judge where can use prepare_execute protocol + * use version to judge + */ +my_bool determine_use_prepare_execute(MYSQL *mysql) +{ + my_bool bret = FALSE; + int ival = PREPARE_EXECUTE_AUTO_OPEN; //default is AUTO + int tmp_val = 0; + char* env = getenv("ENABLE_PREPARE_EXECUTE"); + //default use client_cursor; + if (env) { + tmp_val = atoi(env); + if (tmp_val >= 0 && tmp_val < PREPARE_EXECUTE_FLAG_MAX) + { + ival = tmp_val; + } + } + if (ival == PREPARE_EXECUTE_FORCE_OPEN) + { + bret = TRUE; + } + else if (ival == PREPARE_EXECUTE_FORCE_CLOSE) + { + bret = FALSE; + } + else if (NULL != mysql) + { + if (!mysql->oracle_mode) + { + // only oracle mode use new protocol + } + else + { + if (mysql->ob_server_version >= SUPPORT_PREPARE_EXECUTE_VERSION) + { + bret = TRUE; + } + } + } + if (mysql) + { + mysql->can_use_prepare_execute = bret; + } + DBUG_RETURN(bret); +} +my_bool get_use_prepare_execute(MYSQL* mysql) +{ + if (mysql) + { + return mysql->can_use_prepare_execute; + } + return FALSE; +} +my_bool get_use_preapre_execute(MYSQL* mysql) +{ + if (mysql) + { + return mysql->can_use_prepare_execute; + } + return FALSE; +} +my_bool get_support_send_fetch_flag(MYSQL *mysql) +{ + my_bool bret = FALSE; + if (mysql) + { + if (mysql->oracle_mode && mysql->ob_server_version >= SUPPORT_SEND_FETCH_FLAG_VERSION) + { + bret = TRUE; + } + } + DBUG_RETURN(bret); +} + +my_bool STDCALL is_returning_result(MYSQL_STMT *stmt) +{ + return stmt != NULL ? stmt->is_handle_returning_into : FALSE; +} + +my_bool STDCALL has_added_user_fields(MYSQL_STMT *stmt) +{ + return stmt != NULL ? stmt->has_added_user_fields : FALSE; +} + +my_bool STDCALL is_pl_out_result(MYSQL_STMT *stmt) +{ + return stmt != NULL ? stmt->is_pl_out_resultset : FALSE; +} + +unsigned long STDCALL stmt_pre_exe_req_ext_flag_get(MYSQL_STMT *stmt) +{ + return stmt != NULL ? stmt->ext_flag : 0; +} + +void STDCALL stmt_pre_exe_req_ext_flag_set(MYSQL_STMT *stmt, unsigned long flag) +{ + if (NULL != stmt) { + stmt->ext_flag = flag; + } +} + +/* + * mysql_stmt_prepare_v2 prepare local + */ +int STDCALL +mysql_stmt_prepare_v2(MYSQL_STMT *stmt, const char *query, + unsigned long length, + void* extend_arg) +{ + MYSQL *mysql= stmt->mysql; + PREPARE_EXTEND_ARGS *args = (PREPARE_EXTEND_ARGS *)extend_arg; + int rc= 1; + if (!mysql) + { + /* mysql can be reset in mysql_close called from mysql_reconnect */ + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); + DBUG_RETURN(1); + } + + if (!mysql->can_use_prepare_execute) + { + //not support will use old api + DBUG_RETURN(mysql_stmt_prepare(stmt, query, length)); + } + /* + Reset the last error in any case: that would clear the statement + if the previous prepare failed. + */ + if (length == (unsigned long) -1) + length= (unsigned long)strlen(query); + + /* clear flags */ + CLEAR_CLIENT_STMT_ERROR(stmt); + CLEAR_CLIENT_ERROR(stmt->mysql); + stmt->upsert_status.affected_rows= mysql->affected_rows= (unsigned long long) ~0; + stmt->use_prepare_execute = TRUE; + if ((int) stmt->state > (int) MYSQL_STMT_INITTED) + { + /* This is second prepare with another statement */ + if (mysql_stmt_internal_reset(stmt, 1)) + DBUG_RETURN(1); + /* + These members must be reset for API to + function in case of error or misuse. + */ + stmt->bind_param_done= stmt->bind_result_done= FALSE; + stmt->param_count= stmt->field_count= 0; + ma_free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, MYF(MY_KEEP_PREALLOC)); + ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root, MYF(MY_KEEP_PREALLOC)); + ma_free_root(&stmt->param_fields_mem_root, MYF(MY_KEEP_PREALLOC)); + stmt->param_fields = 0; + stmt->field_count = 0; + stmt->params = 0; + + /* + Close statement in server + + If there was a 'use' result from another statement, or from + mysql_use_result it won't be freed in mysql_stmt_free_result and + we should get 'Commands out of sync' here. + */ + if (stmt->stmt_id > 0) { + char stmt_id[STMT_ID_LENGTH]; + int4store(stmt_id, stmt->stmt_id); + if (mysql->methods->db_command(mysql, COM_STMT_CLOSE, stmt_id, + sizeof(stmt_id), 1, stmt)) + goto fail; + } + } + /*add for support prepare_execute protocol*/ + stmt->stmt_id = 0; + stmt->check_sum = ob_crc32(0, query, length); + if (NULL == args) { + stmt->param_count = find_sql_param_count(query, length); + } else { + stmt->param_count = args->params_count; + } + /* + alloc_root will return valid address even in case when param_count + and field_count are zero. Thus we should never rely on stmt->bind + or stmt->params when checking for existence of placeholders or + result set. +*/ + if (!(stmt->params= (MYSQL_BIND *) ma_alloc_root(&stmt->mem_root, + sizeof(MYSQL_BIND)* + (stmt->param_count + + stmt->field_count)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); + DBUG_RETURN(1); + } + stmt->bind = NULL; + stmt->state= MYSQL_STMT_PREPARED; + DBUG_RETURN(0); +fail: + stmt->state= MYSQL_STMT_INITTED; + UPDATE_STMT_ERROR(stmt); + return(rc); +} +static int +mysql_stmt_execute_describe_only(MYSQL_STMT *stmt, const char *query, ulong length) +{ + MYSQL *mysql= stmt->mysql; + unsigned int check_sum = 0; + int rc = 1; + int ret = 0; + FLT_DECLARE; + + if (!mysql) + { + /* mysql can be reset in mysql_close called from mysql_reconnect */ + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); + DBUG_RETURN(1); + } + + /* + Reset the last error in any case: that would clear the statement + if the previous prepare failed. + */ + if (length == (unsigned long) -1) + length= (unsigned long)strlen(query); + + /* clear flags */ + CLEAR_CLIENT_STMT_ERROR(stmt); + CLEAR_CLIENT_ERROR(mysql); + stmt->upsert_status.affected_rows= mysql->affected_rows= (unsigned long long) ~0; + stmt->last_errno= 0; + stmt->last_error[0]= '\0'; + stmt->use_prepare_execute = TRUE; + check_sum = ob_crc32(0, query, length); + if (check_sum != stmt->check_sum) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); + snprintf(stmt->last_error, MYSQL_ERRMSG_SIZE, "not same sql with prepare"); + DBUG_RETURN(1); + } + if ((int) stmt->state > (int) MYSQL_STMT_INITTED) + { + /* This is second prepare with another statement */ /* 4 bytes - stmt id */ + if (mysql_stmt_internal_reset(stmt, 1)) + goto fail; + /* + These members must be reset for API to + function in case of error or misuse. + */ + stmt->bind_param_done= stmt->bind_result_done= FALSE; + stmt->param_count= stmt->field_count= 0; + ma_free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, MYF(MY_KEEP_PREALLOC)); + ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->binds_ma_alloc_root, MYF(MY_KEEP_PREALLOC)); + ma_free_root(&stmt->param_fields_mem_root, MYF(MY_KEEP_PREALLOC)); + stmt->param_fields = 0; + stmt->field_count= 0; + stmt->params= 0; + + /* + Close statement in server + + If there was a 'use' result from another statement, or from + mysql_use_result it won't be freed in mysql_stmt_free_result and + we should get 'Commands out of sync' here. + */ + if (stmt->stmt_id > 0) { + char stmt_id[STMT_ID_LENGTH]; + int4store(stmt_id, stmt->stmt_id); + if (mysql->methods->db_command(mysql, COM_STMT_CLOSE, stmt_id, + sizeof(stmt_id), 1, stmt)) + goto fail; + } + } + + FLT_BEFORE_COMMAND(0, FLT_TAG_COMMAND_NAME, "\"mysql_stmt_execute_describe_only\""); + + if (mysql->methods->db_command(mysql, COM_STMT_PREPARE, query, length, 1, stmt)) + ret = 1; + + if (0 == ret) { + if (mysql->methods->db_read_prepare_response && mysql->methods->db_read_prepare_response(stmt)) + ret = 1; + } + + if (0 == ret) { + if (stmt->param_count && mysql->methods->db_stmt_get_param_metadata(stmt)) + ret =1; + } + + FLT_AFTER_COMMAND; + + if (1 == ret) { + goto fail; + } + + /* allocated bind buffer for parameters */ + if (stmt->field_count && + mysql->methods->db_stmt_get_result_metadata(stmt)) + { + goto fail; + } + if (stmt->param_count) + { + if (stmt->prebind_params) + { + if (stmt->prebind_params != stmt->param_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0); + goto fail; + } + } else { + if (!(stmt->params= (MYSQL_BIND *)ma_alloc_root(&stmt->mem_root, stmt->param_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto fail; + } + memset(stmt->params, '\0', stmt->param_count * sizeof(MYSQL_BIND)); + } + } + stmt->bind_size = 0; + stmt->bind= 0; + stmt->state= MYSQL_STMT_PREPARED; + DBUG_RETURN(0); +fail: + stmt->state= MYSQL_STMT_INITTED; + UPDATE_STMT_ERROR(stmt); + return(rc); +} +unsigned char* mysql_stmt_prepare_execute_generate_request(MYSQL_STMT* stmt, const char *query, + unsigned long query_length, size_t* request_len) +{ + size_t length= 1024; + size_t free_bytes= 0; + size_t null_byte_offset= 0; + size_t offset = 0; + uint i; + uchar *start= NULL, *p; + size_t null_count = 0; + MYSQL* mysql = stmt->mysql; + /* preallocate length bytes */ + /* check: gr */ + if (!(start= p= (uchar *)malloc(length))) + goto mem_error; + int4store(p, stmt->stmt_id); + p += STMT_ID_LENGTH; + /* flags is 4 bytes, we store just 1 */ + int1store(p, (unsigned char) stmt->flags); + p++; + int4store(p, stmt->iteration_count); + p += 4; + p = mysql_net_store_length(p, query_length); + offset = p - start; + free_bytes = length - offset; + if (free_bytes < query_length + 4 + 20) { //store query and param_count + length = offset + query_length + length;// alloc more + if (!(start = (uchar*) realloc(start, length))) { + goto mem_error; + } + p = start + offset; + } + memcpy(p, query, query_length); + p += query_length; + int4store(p, stmt->param_count); + p +=4; + + if (stmt->param_count) { + ulong extra_length = 0; + for (i = 0; i < stmt->param_count; i++) + { + MYSQL_BIND* param = &stmt->params[i]; + if (MYSQL_TYPE_OBJECT == param->buffer_type) + { + extra_length += calculate_param_type_len(mysql, param); + } + } + null_count = (stmt->param_count + 7)/8; + offset = p - start; + free_bytes = length - offset; + if (null_count + 20 > free_bytes) + { + length = offset + null_count + 20 + length; + if (!(start = (uchar*) realloc(start, length))) + goto mem_error; + p = start + offset; + } + null_byte_offset = p - start; + memset(p, 0, null_count); //null bitmap + p += null_count; + int1store(p, stmt->send_types_to_server); + p++; + offset = p - start; + free_bytes = length - offset; + /* + Store type information: + 2 bytes per type + */ + if (stmt->send_types_to_server) + { + if (free_bytes < stmt->param_count * 2 + 20 + extra_length) + { + length = offset + stmt->param_count * 2 + 20 + length + extra_length; + if (!(start = (uchar*) realloc(start, length))) + goto mem_error; + p = start + offset; + } + for (i = 0; i < stmt->param_count; i++) + { + store_param_type(mysql, &p, &stmt->params[i]); + } + } + /* calculate data size */ + for (i=0; i < stmt->param_count; i++) + { + size_t size= 0; + my_bool has_data= TRUE; + + if (stmt->params[i].long_data_used) + { + has_data= FALSE; + stmt->params[i].long_data_used= 0; + } + + if (!stmt->params[i].buffer || + (stmt->params[i].is_null && *stmt->params[i].is_null)) + { + has_data= FALSE; + } + + if (has_data) + { + switch (stmt->params[i].buffer_type) { + case MYSQL_TYPE_NULL: + has_data= FALSE; + break; + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_CURSOR: + /* type len has definitly defined in mysql_ps_fetch_functions */ + size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len; + break; + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_JSON: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_OB_RAW: + case MYSQL_TYPE_OB_TIMESTAMP_NANO: + case MYSQL_TYPE_OB_UROWID: + case MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + size+= 5; /* max 8 bytes for size */ + size+= (size_t)ma_get_length(stmt, i, 0); + break; + //add to support oboracle type + case MYSQL_TYPE_ORA_BLOB: + case MYSQL_TYPE_ORA_CLOB: + case MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE: + case MYSQL_TYPE_OBJECT: + size += (size_t) calculate_param_len(&stmt->params[i]); + break; + //end add to support oboracle type + default: + //size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len; + size += (size_t)ma_get_length(stmt, i, 0); + break; + } + } + free_bytes= length - (p - start); + if (free_bytes < size + 20) + { + size_t offset= p - start; + length= MAX(2 * length, offset + size + 20); + if (!(start= (uchar *)realloc(start, length))) + goto mem_error; + p= start + offset; + } + if (((stmt->params[i].is_null && *stmt->params[i].is_null) || + stmt->params[i].buffer_type == MYSQL_TYPE_NULL || + !stmt->params[i].buffer)) + { + has_data= FALSE; + /*Skip set NULL flat for piece_data_used when param->buffer is NULL. */ + if (!(!stmt->params[i].buffer && stmt->params[i].piece_data_used)) + { + (start + null_byte_offset)[i/8] |= (unsigned char) (1 << (i & 7)); + } + } + + if (has_data) + { + if (stmt->params[i].piece_data_used && MYSQL_TYPE_OBJECT == stmt->params[i].buffer_type) + { + /* Communicate with OBServer: send array lenth if it is ARRAY BINDING for piece data */ + MYSQL_COMPLEX_BIND_OBJECT *header = (MYSQL_COMPLEX_BIND_OBJECT *)stmt->params[i].buffer; + if (NULL != header && MYSQL_TYPE_ARRAY == header->buffer_type) + { + store_param_piece_array_complex(&p, (MYSQL_COMPLEX_BIND_ARRAY *)header); + } + } else { + store_param(stmt, i, &p, 0); + } + } + } + } + + free_bytes= length - (p - start); + if (free_bytes < 16) // 16 bytes in the end + { + size_t offset= p - start; + length= offset + 16; + if (!(start= (uchar *)realloc(start, length))) + goto mem_error; + p= start + offset; + } + + //execute_mode + int4store(p, stmt->execute_mode); + p += 4; + //num_close_stmt_count, now always is 0 + int4store(p, 0); + p += 4; + //check_sum + if (stmt->check_sum == 0) + { + //maybe after describe,has stmt_id but no checksum + stmt->check_sum = ob_crc32(0, query, query_length); + } + int4store(p, stmt->check_sum); + p += 4; + //extend_flag + int4store(p, stmt->ext_flag); + p += 4; + stmt->send_types_to_server= 0; + *request_len = (size_t)(p - start); + return start; +mem_error: + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + free(start); + *request_len= 0; + return NULL; +} + +static int madb_update_stmt_fields(MYSQL_STMT *stmt) // same as update_stmt_fields +{ + MYSQL_FIELD *field= stmt->mysql->fields; + MYSQL_FIELD *field_end= field + stmt->field_count; + MYSQL_FIELD *stmt_field= stmt->fields; + + MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + + if (stmt->field_count != stmt->mysql->field_count) + { + if (is_returning_result(stmt) && has_added_user_fields(stmt)) { + /* + The result field has changed due to that it executed prepared_stmt + with iter 1 at first and execute prepared_stmt with iter >1 next, + and the result has the flag PRE_EXE_EXTEND_FLAG_RETURNING/ + PRE_EXE_EXTEND_FLAG_ADD_USER_FIELD. eg. + user and result fields' count is 5 in iter 1, user fields' count is 5 + and result fields' count is 8 in iter >1(sql_no/error_code/error_msg is added). + */ + madb_alloc_stmt_fields(stmt); //need to remalloc it + + field_end= field + stmt->field_count; + stmt_field= stmt->fields; + } else { + /* + The tables used in the statement were altered, + and the query now returns a different number of columns. + There is no way to continue without reallocating the bind + array: + - if the number of columns increased, mysql_stmt_fetch() + will write beyond allocated memory + - if the number of columns decreased, some user-bound + buffers will be left unassigned without user knowing + that. + */ + SET_CLIENT_STMT_ERROR(stmt, CR_NEW_STMT_METADATA, SQLSTATE_UNKNOWN, 0); + return 1; + } + } + + for (; field < field_end; ++field, ++stmt_field) + { + memcpy(stmt_field, field, sizeof(MYSQL_FIELD)); +/* + * Not need to set one by one + stmt_field->charsetnr= field->charsetnr; + stmt_field->length = field->length; + stmt_field->type = field->type; + stmt_field->flags = field->flags; + stmt_field->decimals = field->decimals; + stmt_field->max_length = field->max_length; +*/ + + /* 判断 owner_name 是否为空, 避免每次都申请内存, 如果一直 execute, 不重新 prepare, 内存就释放不了 */ + if (MYSQL_TYPE_OBJECT == field->type && NULL == stmt_field->owner_name && NULL != field->owner_name) { + stmt_field->owner_name= (unsigned char *)ma_memdup_root(fields_ma_alloc_root, + (char*)field->owner_name, + field->owner_name_length); + stmt_field->type_name= (unsigned char *)ma_memdup_root(fields_ma_alloc_root, + (char*)field->type_name, + field->type_name_length); + } + } + return 0; +} + +static int madb_reinit_result_set_metadata(MYSQL_STMT *stmt) +{ + /* Server has sent result set metadata */ + if (stmt->field_count == 0) + { + /* + This is 'SHOW'/'EXPLAIN'-like query. Current implementation of + prepared statements can't send result set metadata for these queries + on prepare stage. Read it now. + */ + + stmt->field_count= stmt->mysql->field_count; + + return madb_alloc_stmt_fields(stmt); + } + else + { + /* + Update result set metadata if it for some reason changed between + prepare and execute, i.e.: + - in case of 'SELECT ?' we don't know column type unless data was + supplied to mysql_stmt_execute, so updated column type is sent + now. + - if data dictionary changed between prepare and execute, for + example a table used in the query was altered. + Note, that now (4.1.3) we always send metadata in reply to + COM_STMT_EXECUTE (even if it is not necessary), so either this or + previous branch always works. + TODO: send metadata only when it's really necessary and add a warning + 'Metadata changed' when it's sent twice. + */ + return madb_update_stmt_fields(stmt); + } + + return 0; +} + +static int stmt_read_prepare_execute_response(MYSQL_STMT* stmt) +{ + MYSQL *mysql= stmt->mysql; + NET *net; + ulong pkt_len; + + if (!mysql) + return(1); + net= &mysql->net; + pkt_len = 0; + if ((pkt_len= ma_net_safe_read(mysql)) == packet_error) + return(1); + if (net->read_pos[0] != 0) + { + end_server(mysql); + SET_CLIENT_ERROR(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + else if (pkt_len < 17) + { + end_server(mysql); + SET_CLIENT_ERROR(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); + snprintf(net->last_error, MYSQL_ERRMSG_SIZE, "Wrong packet len:%lu", pkt_len); + DBUG_RETURN(1); + } + else + { + uchar *pos = (uchar*) mysql->net.read_pos; + uint field_count, param_count; + uint extend_flag; + my_bool has_result_set = FALSE; + stmt->stmt_id= uint4korr(pos+1); pos+= 5; + field_count= uint2korr(pos); pos+= 2; + param_count= uint2korr(pos); pos+= 2; + mysql->warning_count= uint2korr(pos+1); //skip reserved + pos +=3; + extend_flag= uint4korr(pos); //extend_flag + /** + * PRE_EXE_EXTEND_FLAG_RETURNING= 1; + * PRE_EXE_EXTEND_FLAG_ADD_USER_FIELD= 1<<1; + * PRE_EXE_EXTEND_FLAG_PLOUT= 1<<2; + * */ + stmt->is_handle_returning_into = extend_flag & PRE_EXE_EXTEND_FLAG_RETURNING; + stmt->has_added_user_fields = extend_flag & PRE_EXE_EXTEND_FLAG_ADD_USER_FIELD; + stmt->is_pl_out_resultset = extend_flag & PRE_EXE_EXTEND_FLAG_PLOUT; + pos +=4; + has_result_set = (pos[0] == 0 ? FALSE: TRUE); + pos +=1; + if (stmt->stmt_id == 0) + { + end_server(mysql); + SET_CLIENT_ERROR(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); + snprintf(net->last_error, MYSQL_ERRMSG_SIZE, "Wrong packet invalid stmt_id:%lu", stmt->stmt_id); + DBUG_RETURN(1); + } + if (param_count != 0) + { + if (stmt->is_handle_returning_into && stmt->field_count + stmt->param_count == param_count) + { + /* It needs to exec prepare operation for the sql with piece data send and returning ... into, Like: + * UPDATE TABLE ta SET c1 = :v1, c2 =:v2, c3 = :v3 RETURNING c0 INTO :c0 + * IN STMT_PREPARE: + * returning stmt->param_count = 3, and field_count = 1 + * IN STMT_PREPARE_EXECUTE(V2): + * returning param_count = 4, and filed_count = 1/4(single is 1, and arraybinding is 4) + * (decide by has_added_user_fields flag) + * So we need update lastest param count for returning ... into, to read and parse all the param meta + * */ + stmt->param_count = param_count; + } + if (param_count != stmt->param_count) + { + end_server(mysql); + SET_CLIENT_ERROR(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); + snprintf(net->last_error, MYSQL_ERRMSG_SIZE, "Wrong packet: client_param_count:%d, resp_param_count:%d", + stmt->param_count, param_count); + DBUG_RETURN(1); + } + //prepare会返回参数信息,PL执行时也会返回,释放避免内存重复分配 + if (stmt->param_fields) + { + ma_free_root(&stmt->param_fields_mem_root, MYF(MY_KEEP_PREALLOC)); + stmt->param_fields = 0; + } + if (mysql->methods->db_stmt_get_param_metadata(stmt)) + DBUG_RETURN(1); + } + free_old_query(mysql); + if (field_count != 0) + { + //stmt->field_count = mysql->field_count = (uint) field_count; + mysql->field_count = (uint) field_count; + if (mthd_my_read_prepare_execute_result(mysql)) + // if (mthd_my_read_query_result(stmt->mysql)) + { + DBUG_RETURN(1); + } + ///** Not need to reinit fields for stmt, for it cannot update the MYSQL_RES's fileds which has returned before. + // * If need get last the fields info for fetch, it need open it and get newest MYSQL_RES. + else if (madb_reinit_result_set_metadata(stmt)) + { + DBUG_RETURN(1); + } + //*/ + } + if (has_result_set) { + if (mthd_stmt_read_all_rows(stmt)) + { + DBUG_RETURN(1); + } + // stmt->data_cursor = stmt->result.data; + } + if ((pkt_len= ma_net_safe_read(mysql)) == packet_error) + DBUG_RETURN(1); + if (mysql->net.read_pos[0] == 0) { + ma_read_ok_packet(mysql, &mysql->net.read_pos[1], pkt_len);//extra ok packet + stmt->upsert_status.affected_rows= stmt->mysql->affected_rows; + } + else + { + end_server(mysql); + SET_CLIENT_ERROR(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} +int STDCALL mysql_stmt_execute_v2(MYSQL_STMT *stmt, + const char *query, + unsigned long length, + unsigned int iteration_count, + int execute_mode, + void* extend_arg) +{ + MYSQL *mysql= stmt->mysql; + int ret = 0; + char *request; + size_t request_len= 0; + int arg = 0; + FLT_DECLARE; + + if (!mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + if (!mysql->can_use_prepare_execute) { + DBUG_RETURN(mysql_stmt_execute(stmt)); + } + if (!stmt->use_prepare_execute) { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate, NULL); + snprintf(stmt->last_error, MYSQL_ERRMSG_SIZE, "must use prepare_v2 first"); + DBUG_RETURN(1); + } + + if (stmt->state < MYSQL_STMT_PREPARED) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + return(1); + } + + stmt->execute_mode = execute_mode; + stmt->iteration_count = iteration_count; + if (extend_arg != NULL) + { + arg = *(int*)extend_arg & NEED_DATA_AT_EXEC_FLAG; + } + if (execute_mode & 0x00000010 || arg)//describe only + { + DBUG_RETURN(mysql_stmt_execute_describe_only(stmt, query, length)); + } + if (stmt->param_count && !stmt->bind_param_done) + { + SET_CLIENT_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, SQLSTATE_UNKNOWN, 0); + return(1); + } + if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE) + { + stmt->default_rset_handler = _mysql_stmt_use_result; + stmt->default_rset_handler(stmt); + } + if (stmt->state > MYSQL_STMT_WAITING_USE_OR_STORE && stmt->state < MYSQL_STMT_FETCH_DONE && !stmt->result.data) + { + if (!stmt->cursor_exists) + do { + stmt->mysql->methods->db_stmt_flush_unbuffered(stmt); + } while(mysql_stmt_more_results(stmt)); + stmt->state= MYSQL_STMT_PREPARED; + stmt->mysql->status= MYSQL_STATUS_READY; + } + + /* clear data, in case mysql_stmt_store_result was called */ + if (stmt->result.data) + { + ma_free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC)); + stmt->result_cursor= stmt->result.data= 0; + } + /* CONC-344: set row count to zero */ + stmt->result.rows= 0; + request= (char *)mysql_stmt_prepare_execute_generate_request(stmt,query, length, &request_len); + + if (!request) + return 1; + + FLT_BEFORE_COMMAND(0, FLT_TAG_COMMAND_NAME, "\"mysql_stmt_execute_v2\""); + + if (0 == ret) { + ret= stmt->mysql->methods->db_command(mysql, + COM_STMT_PREPARE_EXECUTE, + request, request_len, 1, stmt); + if (request) + { + free(request); + request = 0; + } + + if (ret) { + UPDATE_STMT_ERROR(stmt); + ret = 1; + } else if (mysql->net.extension->multi_status > COM_MULTI_OFF) { + ret = 0; + } else { + ret = (stmt_read_execute_response(stmt)); + } + } + + // end trace + FLT_AFTER_COMMAND; + + return ret; +} + +my_bool read_piece_data_from_server(MYSQL_STMT* stmt, uint param_number, + uchar* piece_type, uchar* is_null, ulong* piece_data_len, uchar** row) +{ + // int rc = 0; + MYSQL *mysql= stmt->mysql; + NET *net= &mysql->net; + ulong len=0; + + UNUSED(param_number); + + if (net->pvio != 0) + len=ma_net_safe_read(stmt->mysql); //done + + if (len == packet_error || len == 0) + { + end_server(mysql); + return(1); + } + + if (len < 10) + { + end_server(mysql); + SET_CLIENT_STMT_ERROR(stmt, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); + snprintf(stmt->last_error, MYSQL_ERRMSG_SIZE, "Wrong packet len:%lu", len); + return(1); + } + else + { + uchar *pos = (uchar*) mysql->net.read_pos; + *piece_type = pos[0]; + *is_null = pos[1]; + *piece_data_len = uint8korr(pos + 2); pos += 10; + *row = pos; + //rc = stmt_fetch_column_row(stmt, row, param_number); + } + + return(0); +} + +my_bool STDCALL +mysql_stmt_read_piece_data(MYSQL_STMT *stmt, unsigned int param_number, + unsigned short orientation, int scroll_offset, unsigned long data_len, + unsigned char *piece_type, unsigned long *ret_data_len) +{ + uchar *row; + MYSQL_BIND *param; + MYSQL_DATA *result= &stmt->result; + uchar is_null = 0; + int truncation_count= 0; + MYSQL *mysql; + uchar buff[4 /* statement id */ + + 2 /* orientation */ + + 4 /* offset to be used change the current row position */ + + 2 /* column id*/ + + 8 /* piece data size*/]; + + if (NULL == piece_type || NULL == ret_data_len) { + SET_CLIENT_STMT_ERROR(stmt, CR_NULL_POINTER, unknown_sqlstate, NULL); + return(1); + } + + /* + We only need to check for stmt->param_count, if it's not null + prepare was done. + */ + if (param_number >= stmt->field_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate, NULL); + return(1); + } + + param = stmt->bind + param_number; + + /* + Send long data packet if there is data or we're sending long data + for the first time. + */ + + mysql= stmt->mysql; + /* Packet header: stmt id (4 bytes), param no (2 bytes) */ + ma_free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); + result->data= 0; + result->rows= 0; + memset(buff, 0, sizeof(buff)); + int4store(buff, stmt->stmt_id); + int2store(buff + 4, orientation); + int4store(buff + 6, scroll_offset); + int2store(buff + 10, param_number); + int8store(buff + 12, data_len); + + /* + Note that we don't get any ok packet from the server in this case + This is intentional to save bandwidth. + */ + if ((stmt->mysql->methods->db_command)(mysql, COM_STMT_GET_PIECE_DATA, (char *)buff, sizeof(buff), 1, stmt)) + { + /* + Don't set stmt error if stmt->mysql is NULL, as the error in this case + has already been set by mysql_prune_stmt_list(). + */ + if (stmt->mysql) + UPDATE_STMT_ERROR(stmt); + return(1); + } + + if (read_piece_data_from_server(stmt, param_number, piece_type, &is_null, ret_data_len, &row)) + return(1); + + /* need change the state to MYSQL_STMT_USER_FETCHING, to read the buffer data */ + stmt->state= MYSQL_STMT_USER_FETCHING; + CLEAR_CLIENT_ERROR(stmt->mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); + + if (is_null == 1) { + *param->is_null = 1; + } else { + *param->is_null = 0; + param->u.row_ptr = row; + param->mysql = stmt->mysql; + if (param->buffer_length <= 0) { + *param->error = 1; + } else { + ulong copy_length = MIN(*ret_data_len, param->buffer_length); + memcpy(param->buffer, (char *)*row, copy_length); + *param->error = copy_length < *ret_data_len; + } + *param->length = *ret_data_len; + truncation_count += *param->error; + } + if (truncation_count && stmt->mysql->options.report_data_truncation) //TODO need to check same as stmt->bind_result_done & REPORT_DATA_TRUNCATION + return MYSQL_DATA_TRUNCATED; + + return(0); +} diff --git a/libmariadb/ob_bitmap.c b/libmariadb/ob_bitmap.c new file mode 100644 index 0000000..5f2968f --- /dev/null +++ b/libmariadb/ob_bitmap.c @@ -0,0 +1,80 @@ +/************************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*************************************************************************************/ + +#include "ob_bitmap.h" +#include "string.h" + +#define n_bytes_in_map(map) (((map)->n_bits + 7)/8) + +my_bool ob_bitmap_init(OB_BITMAP *map, uint n_bits) +{ + uint size_in_bytes; + + map->n_bits = n_bits; + size_in_bytes = n_bytes_in_map(map); + + if (!(map->bitmap = (ob_bitmap_map *)malloc(size_in_bytes))) { + return (1); + } + + ob_bitmap_clear_all(map); + return (0); +} + +void ob_bitmap_set_bit(OB_BITMAP *map, uint bit) +{ + if (bit < map->n_bits) { + ((uchar*)map->bitmap)[bit / 8] |= (1 << (bit & 7)); + } +} + +void ob_bitmap_clear_bit(OB_BITMAP *map, uint bit) +{ + if (bit < map->n_bits) { + ((uchar*)map->bitmap)[bit / 8] &= ~(1 << (bit & 7)); + } +} + +my_bool ob_bitmap_is_set(const OB_BITMAP *map, uint bit) +{ + if (bit < map->n_bits) { + return ((uchar*)map->bitmap)[bit / 8] & (1 << (bit & 7)); + } else { + return 0; + } +} + +void ob_bitmap_clear_all(OB_BITMAP *map) +{ + memset(map->bitmap, 0, n_bytes_in_map(map)); +} + +void ob_bitmap_set_all(OB_BITMAP *map) +{ + memset(map->bitmap, 0xFF, n_bytes_in_map(map)); +} + +void ob_bitmap_free(OB_BITMAP *map) +{ + free(map->bitmap); + map->bitmap = NULL; + map->n_bits = 0; +} \ No newline at end of file diff --git a/libmariadb/ob_complex.c b/libmariadb/ob_complex.c new file mode 100644 index 0000000..2f01751 --- /dev/null +++ b/libmariadb/ob_complex.c @@ -0,0 +1,636 @@ +/* + Copyright (c) 2000, 2018, Oracle and/or its affiliates. + Copyright (c) 2009, 2019, MariaDB Corporation. + Copyright (c) 2021 OceanBase Technology Co.,Ltd. + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ +#include "ma_common.h" +#include "errmsg.h" +#include "ob_complex.h" +#include "ma_string.h" + +static OB_HASH *global_hash = NULL; +static ob_rw_lock_t global_rwlock = OB_RW_INITIALIZER; +static void global_hash_free(void *record); +// #define UNUSED(x) ((void)x) +#define COMPLEX_TYPE_MAX_SQL_LENGTH 32 * 1024 +#define COMPLEX_TYPE_MYSQL(M) (MYSQL_EXTENSION_PTR(M)->mysql) +#define COMPLEX_TYPE_HASH(M) (MYSQL_EXTENSION_PTR(M)->complex_type_hash) +#define COMPLEX_TYPE_USER_TYPES_SQL \ +"SELECT "\ +" 0 DEPTH, "\ +" NULL PARENT_OWNER, "\ +" NULL PARENT_TYPE, "\ +" TYPE_NAME CHILD_TYPE, "\ +" 0 ATTR_NO, "\ +" SYS_CONTEXT('USERENV', 'CURRENT_USER') CHILD_TYPE_OWNER, "\ +" A.TYPECODE ATTR_TYPE_CODE, "\ +" NULL LENGTH, "\ +" NULL NUMBER_PRECISION, "\ +" NULL SCALE, "\ +" NULL CHARACTER_SET_NAME "\ +"FROM "\ +" USER_TYPES A WHERE TYPE_NAME = '%s' "\ +"UNION "\ +"( "\ +" WITH "\ +" CTE_RESULT(PARENT_OWNER, PARENT_TYPE, CHILD_TYPE, ATTR_NO, CHILD_TYPE_OWNER, ATTR_TYPE_CODE, LENGTH, NUMBER_PRECISION, SCALE, CHARACTER_SET_NAME) "\ +" AS ( "\ +" SELECT "\ +" SYS_CONTEXT('USERENV','CURRENT_USER') PARENT_OWNER, "\ +" B.TYPE_NAME PARENT_TYPE, "\ +" B.ELEM_TYPE_NAME CHILD_TYPE, "\ +" 0 ATTR_NO, "\ +" B.ELEM_TYPE_OWNER CHILD_TYPE_OWNER, "\ +" NVL(A.TYPECODE, B.ELEM_TYPE_NAME) AS ATTR_TYPE_CODE, "\ +" B.LENGTH LENGTH, "\ +" B.NUMBER_PRECISION NUMBER_PRECISION, "\ +" B.SCALE SCALE, "\ +" B.CHARACTER_SET_NAME CHARACTER_SET_NAME "\ +" FROM "\ +" USER_COLL_TYPES B LEFT JOIN USER_TYPES A ON A.TYPE_NAME = B.ELEM_TYPE_NAME "\ +" UNION "\ +" SELECT "\ +" SYS_CONTEXT('USERENV','CURRENT_USER') PARENT_OWNER, "\ +" B.TYPE_NAME PARENT_TYPE, "\ +" B.ATTR_TYPE_NAME CHILD_TYPE, "\ +" B.ATTR_NO ATTR_NO, "\ +" B.ATTR_TYPE_OWNER CHILD_TYPE_OWNER, "\ +" NVL(A.TYPECODE, B.ATTR_TYPE_NAME) AS ATTR_TYPE_CODE, "\ +" B.LENGTH LENGTH, "\ +" B.NUMBER_PRECISION NUMBER_PRECISION, "\ +" B.SCALE SCALE, "\ +" B.CHARACTER_SET_NAME CHARACTER_SET_NAME "\ +" FROM USER_TYPE_ATTRS B LEFT JOIN USER_TYPES A ON B.ATTR_TYPE_NAME = A.TYPE_NAME ORDER BY ATTR_NO "\ +" ) , "\ +" CTE(DEPTH, PARENT_OWNER, PARENT_TYPE, CHILD_TYPE, ATTR_NO, CHILD_TYPE_OWNER, ATTR_TYPE_CODE, LENGTH, NUMBER_PRECISION, SCALE, CHARACTER_SET_NAME) "\ +" AS ( "\ +" SELECT "\ +" 1 DEPTH, "\ +" PARENT_OWNER, "\ +" PARENT_TYPE, "\ +" CHILD_TYPE, "\ +" ATTR_NO, "\ +" CHILD_TYPE_OWNER, "\ +" ATTR_TYPE_CODE, "\ +" LENGTH, "\ +" NUMBER_PRECISION, "\ +" SCALE, CHARACTER_SET_NAME "\ +" FROM CTE_RESULT WHERE PARENT_TYPE = '%s' "\ +" UNION ALL "\ +" SELECT "\ +" DEPTH + 1 DEPTH, "\ +" CTE_RESULT.PARENT_OWNER, "\ +" CTE_RESULT.PARENT_TYPE, "\ +" CTE_RESULT.CHILD_TYPE, "\ +" CTE_RESULT.ATTR_NO, "\ +" CTE_RESULT.CHILD_TYPE_OWNER, "\ +" CTE_RESULT.ATTR_TYPE_CODE, "\ +" CTE_RESULT.LENGTH, "\ +" CTE_RESULT.NUMBER_PRECISION, "\ +" CTE_RESULT.SCALE, "\ +" CTE_RESULT.CHARACTER_SET_NAME "\ +" FROM CTE_RESULT INNER JOIN CTE ON CTE_RESULT.PARENT_TYPE = CTE.CHILD_TYPE "\ +" ) "\ +" SELECT * FROM CTE "\ +"); "\ + +#define COMPLEX_TYPE_ALL_TYPES_SQL \ +"SELECT "\ +" 0 DEPTH, "\ +" NULL PRAENT_OWNER, "\ +" NULL PARENT_TYPE, "\ +" to_char(TYPE_NAME) CHILD_TYPE, "\ +" 0 ATTR_NO, "\ +" OWNER CHILD_TYPE_OWNER, "\ +" A.TYPECODE ATTR_TYPE_CODE, "\ +" NULL LENGTH, "\ +" NULL NUMBER_PRECISION, "\ +" NULL SCALE, "\ +" NULL CHARACTER_SET_NAME "\ +" FROM "\ +" ALL_TYPES A WHERE TYPE_NAME = '%s' AND OWNER = '%s' "\ +" UNION "\ +" ( "\ +" WITH "\ +" CTE_RESULT(PARENT_OWNER, PARENT_TYPE, CHILD_TYPE, ATTR_NO, CHILD_TYPE_OWNER, ATTR_TYPE_CODE, LENGTH, NUMBER_PRECISION, SCALE, CHARACTER_SET_NAME) "\ +" AS ( "\ +" SELECT "\ +" B.OWNER PARENT_OWNER, "\ +" B.TYPE_NAME PARENT_TYPE, "\ +" B.ELEM_TYPE_NAME CHILD_TYPE, "\ +" 0 ATTR_NO, "\ +" B.ELEM_TYPE_OWNER CHILD_TYPE_OWNER, "\ +" NVL(A.TYPECODE, B.ELEM_TYPE_NAME) AS ATTR_TYPE_CODE, "\ +" B.LENGTH LENGTH, "\ +" B.NUMBER_PRECISION NUMBER_PRECISION, "\ +" B.SCALE SCALE, "\ +" B.CHARACTER_SET_NAME CHARACTER_SET_NAME "\ +" FROM "\ +" ALL_COLL_TYPES B LEFT JOIN ALL_TYPES A ON A.TYPE_NAME = B.ELEM_TYPE_NAME AND A.OWNER = B.ELEM_TYPE_OWNER "\ +" UNION "\ +" SELECT "\ +" B.OWNER PARENT_OWNER, "\ +" B.TYPE_NAME PARENT_TYPE, "\ +" B.ATTR_TYPE_NAME CHILD_TYPE, "\ +" B.ATTR_NO ATTR_NO, "\ +" B.ATTR_TYPE_OWNER CHILD_TYPE_OWNER, "\ +" NVL(A.TYPECODE, B.ATTR_TYPE_NAME) AS ATTR_TYPE_CODE, "\ +" B.LENGTH LENGTH, "\ +" B.NUMBER_PRECISION NUMBER_PRECISION, "\ +" B.SCALE SCALE, "\ +" B.CHARACTER_SET_NAME CHARACTER_SET_NAME "\ +" FROM ALL_TYPE_ATTRS B LEFT JOIN ALL_TYPES A ON A.TYPE_NAME = B.ATTR_TYPE_NAME AND A.OWNER = B.ATTR_TYPE_OWNER ORDER BY ATTR_NO "\ +" ) , "\ +" CTE(DEPTH, PARENT_OWNER, PARENT_TYPE, CHILD_TYPE, ATTR_NO, CHILD_TYPE_OWNER, ATTR_TYPE_CODE, LENGTH, NUMBER_PRECISION, SCALE, CHARACTER_SET_NAME) "\ +" AS ( "\ +" SELECT "\ +" 1 DEPTH, "\ +" PARENT_OWNER, "\ +" PARENT_TYPE, "\ +" CHILD_TYPE, "\ +" ATTR_NO, "\ +" CHILD_TYPE_OWNER, "\ +" ATTR_TYPE_CODE, "\ +" LENGTH, "\ +" NUMBER_PRECISION, "\ +" SCALE, CHARACTER_SET_NAME "\ +" FROM CTE_RESULT WHERE PARENT_TYPE = '%s' AND PARENT_OWNER = '%s' "\ +" UNION ALL "\ +" SELECT "\ +" DEPTH + 1 DEPTH, "\ +" CTE_RESULT.PARENT_OWNER, "\ +" CTE_RESULT.PARENT_TYPE, "\ +" CTE_RESULT.CHILD_TYPE, "\ +" CTE_RESULT.ATTR_NO, "\ +" CTE_RESULT.CHILD_TYPE_OWNER, "\ +" CTE_RESULT.ATTR_TYPE_CODE, "\ +" CTE_RESULT.LENGTH, "\ +" CTE_RESULT.NUMBER_PRECISION, "\ +" CTE_RESULT.SCALE, "\ +" CTE_RESULT.CHARACTER_SET_NAME "\ +" FROM CTE_RESULT INNER JOIN CTE ON CTE_RESULT.PARENT_TYPE = CTE.CHILD_TYPE AND CTE_RESULT.PARENT_OWNER = CTE.CHILD_TYPE_OWNER "\ +" ) "\ +" SELECT * FROM CTE "\ +"); "\ + +#define DEPTH_INDEX 0 +#define PARENT_TYPE_INDEX 2 +#define CHILD_TYPE_INDEX 3 +#define ATTR_NO_INDEX 4 +#define CHILD_OWNER_INDEX 5 +#define ATTR_TYPE_INDEX 6 + +static enum_types convert_type(const char *type) { + if (!strcmp(type, "COLLECTION")) { + return TYPE_COLLECTION; + } else if (!strcmp(type, "OBJECT")) { + return TYPE_OBJECT; + } else if (!strcmp(type, "NUMBER")) { + return TYPE_NUMBER; + } else if (!strcmp(type, "VARCHAR2")) { + return TYPE_VARCHAR2; + } else if (!strcmp(type, "CHAR")) { + return TYPE_CHAR; + } else if (!strcmp(type, "DATE")) { + return TYPE_DATE; + } else if (!strcmp(type, "RAW")) { + return TYPE_RAW; + } else { + return TYPE_MAX; + } +} + +static int STDCALL +parser_complex(MYSQL_RES *result, OB_HASH *hash) +{ + MYSQL_ROW cur; + + COMPLEX_TYPE *complex_type; + unsigned char *parent_name; + COMPLEX_TYPE *parent; + while ((cur = mysql_fetch_row(result))) { + unsigned int type_size; + enum_types type = convert_type(cur[ATTR_TYPE_INDEX]); + + if (TYPE_OBJECT == type || TYPE_COLLECTION == type) { + complex_type = (COMPLEX_TYPE *) hash_search(hash, (unsigned char*)cur[CHILD_TYPE_INDEX], strlen(cur[CHILD_TYPE_INDEX])); + + if (!complex_type) { + type_size = TYPE_COLLECTION == type ? sizeof(COMPLEX_TYPE_COLLECTION) : sizeof(COMPLEX_TYPE_OBJECT); + complex_type = (COMPLEX_TYPE *) calloc(1, type_size); + + if (!complex_type) { + return (1); + } + + complex_type->is_valid = 0; + complex_type->type = type; + if (TYPE_OBJECT == complex_type->type) { + ((COMPLEX_TYPE_OBJECT *)complex_type)->attr_no = 0; + } + /* name 被初始化成了 0, 所以不需要拷贝最后的 NULL 结束符 */ + memcpy(complex_type->type_name, cur[CHILD_TYPE_INDEX], strlen(cur[CHILD_TYPE_INDEX])); + memcpy(complex_type->owner_name, cur[CHILD_OWNER_INDEX], strlen(cur[CHILD_OWNER_INDEX])); + + if (hash_insert(hash, (unsigned char *)complex_type)) { + return (1); + } + } else { + //ReInit node's info in the query + complex_type->is_valid = 0; + if (TYPE_OBJECT == complex_type->type) { + ((COMPLEX_TYPE_OBJECT *)complex_type)->attr_no = 0; + } + } + } + + if (atoi(cur[DEPTH_INDEX]) != 0) { + parent_name = (unsigned char *)cur[PARENT_TYPE_INDEX]; + parent = (COMPLEX_TYPE *) hash_search(hash, parent_name, strlen((char *)parent_name)); + + if (parent == NULL) { + return (1); + } + + if (TYPE_OBJECT == parent->type && + ((COMPLEX_TYPE_OBJECT *)parent)->attr_no < strtoul(cur[ATTR_NO_INDEX], NULL, 10)) { + ((COMPLEX_TYPE_OBJECT *)parent)->attr_no = strtoul(cur[ATTR_NO_INDEX], NULL, 10); + } + } + } + + result->data_cursor = result->data->data; + + while ((cur = mysql_fetch_row(result))) { + enum_types child_type; + COMPLEX_TYPE_OBJECT *parent_object; + COMPLEX_TYPE_COLLECTION *parent_collection; + + if (atoi(cur[DEPTH_INDEX]) != 0) { + parent_name = (unsigned char *)cur[PARENT_TYPE_INDEX]; + parent = (COMPLEX_TYPE *) hash_search(hash, parent_name, strlen((char *)parent_name)); + + if (parent == NULL) { + return (1); + } + + child_type = convert_type(cur[ATTR_TYPE_INDEX]); + + if (TYPE_OBJECT == parent->type) { + parent_object = (COMPLEX_TYPE_OBJECT *)parent; + + if (parent_object->child == NULL) { + parent_object->child = (CHILD_TYPE *) calloc(1, parent_object->attr_no * sizeof(CHILD_TYPE)); + + if (parent_object->child == NULL) { + return (1); + } + } + + parent_object->child[atoi(cur[ATTR_NO_INDEX]) - 1].type = child_type; + + if (TYPE_COLLECTION == child_type || TYPE_OBJECT == child_type) { + complex_type = (COMPLEX_TYPE *)hash_search(hash, + (unsigned char *)cur[CHILD_TYPE_INDEX], strlen(cur[CHILD_TYPE_INDEX])); + + if (complex_type == NULL) { + return (1); + } + + parent_object->child[atoi(cur[ATTR_NO_INDEX]) - 1].object = complex_type; + } + + parent_object->init_attr_no++; + + if (parent_object->init_attr_no == parent_object->attr_no) { + parent->is_valid = 1; + } + + } else if (TYPE_COLLECTION == parent->type) { + parent_collection = (COMPLEX_TYPE_COLLECTION *)parent; + + parent_collection->child.type = child_type; + + if (TYPE_COLLECTION == child_type || TYPE_OBJECT == child_type) { + complex_type = (COMPLEX_TYPE *)hash_search(hash, + (unsigned char *)cur[CHILD_TYPE_INDEX], strlen(cur[CHILD_TYPE_INDEX])); + + if (complex_type == NULL) { + return (1); + } + + parent_collection->child.object = complex_type; + } + + parent->is_valid = 1; + } + } + } + return (0); +} + +static int STDCALL +get_type_from_server(MYSQL *mysql, unsigned char *owner_name, unsigned char *type_name, COMPLEX_HASH *complex_hash) +{ + int retval; + MYSQL_RES *result; + char sql[COMPLEX_TYPE_MAX_SQL_LENGTH] = {'\0'}; + + if (NULL != owner_name) { + snprintf(sql, COMPLEX_TYPE_MAX_SQL_LENGTH, COMPLEX_TYPE_ALL_TYPES_SQL, type_name, owner_name, type_name, owner_name); + } else { + /* 如果 owner 为空, 先从 user_types 视图中获取 */ + snprintf(sql, COMPLEX_TYPE_MAX_SQL_LENGTH, COMPLEX_TYPE_USER_TYPES_SQL, type_name, type_name); + } + + if (mysql_real_query(mysql, sql, strlen(sql))) { + return (1); + } + + if (!(result = mysql_store_result(mysql))) { + return (1); + /* 如果 user_types 视图中没有该 type, 在再从 all_types 视图中获取 */ + } else if (0 == mysql_num_rows(result) && NULL == owner_name) { + mysql_free_result(result); + snprintf(sql, COMPLEX_TYPE_MAX_SQL_LENGTH, COMPLEX_TYPE_ALL_TYPES_SQL, type_name, "SYS", type_name, "SYS"); + + if (mysql_real_query(mysql, sql, strlen(sql))) { + return (1); + } + + if (!(result = mysql_store_result(mysql))) { + return (1); + } + } + + ob_rw_wrlock(&(complex_hash->rwlock)); + retval = parser_complex(result, complex_hash->hash); + ob_rw_unlock(&(complex_hash->rwlock)); + + mysql_free_result(result); + + return (retval); +} + +static int STDCALL +complex_mysql_init(MYSQL *mysql, MYSQL **complex_mysql) +{ + int ret = 0; + + *complex_mysql = NULL; + + if (!(*complex_mysql = mysql_init(*complex_mysql))) { + ret = 1; + } else if (!mysql_real_connect(*complex_mysql, mysql->host, mysql->user, mysql->passwd, + mysql->db, mysql->port, mysql->unix_socket, 0)) + { + ret = 1; + } + + if (ret != 0 && *complex_mysql != NULL) { + mysql_close(*complex_mysql); + *complex_mysql = NULL; + } + + return (ret); +} + +static void complex_hash_free(void *record) +{ + COMPLEX_TYPE *header = (COMPLEX_TYPE *)record; + if (header->type == TYPE_OBJECT) { + free(((COMPLEX_TYPE_OBJECT *)header)->child); + } + + free(header); +} + +static unsigned char *complex_get_hash_key(const unsigned char *record, uint *length, + my_bool not_used) +{ + COMPLEX_TYPE *type = (COMPLEX_TYPE *)record; + *length= strlen((char *)type->type_name); + UNUSED(not_used); + return type->type_name; +} + +static int STDCALL +complex_hash_init(MYSQL *mysql, unsigned char *hash_key, COMPLEX_HASH **complex_hash) +{ + int ret = 0; + + *complex_hash = NULL; + + if (!(*complex_hash = calloc(1, sizeof(COMPLEX_HASH)))) { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + ret = 1; + } else if(ob_rw_init(&((*complex_hash)->rwlock))) { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + ret = 1; + } else if (!((*complex_hash)->hash = calloc(1, sizeof(OB_HASH)))) { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + ret = 1; + } else if (hash_init((*complex_hash)->hash, 0, 0, 0, (hash_get_key) complex_get_hash_key, + complex_hash_free, HASH_CASE_INSENSITIVE)) { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + ret = 1; + } else if (!((*complex_hash)->hash_key = calloc(1, strlen((char *)hash_key) + 1))) { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + ret = 1; + } else { + //memset((*complex_hash)->hash_key, 0, strlen((char *)hash_key) + 1); + memcpy((*complex_hash)->hash_key, hash_key, strlen((char *)hash_key)); + (*complex_hash)->hash_key[strlen((char *)hash_key)] = '\0'; + if (hash_insert(global_hash, (unsigned char *)(*complex_hash))) { + SET_CLIENT_ERROR(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0); + ret = 1; + } + } + + if (ret && *complex_hash != NULL) { + global_hash_free((void *)(*complex_hash)); + *complex_hash = NULL; + } + + return (ret); +} + +static void global_hash_free(void *record) +{ + COMPLEX_HASH *complex_hash = (COMPLEX_HASH *)record; + if (NULL != complex_hash->hash_key) { + free(complex_hash->hash_key); + } + + if (NULL != complex_hash->hash) { + hash_free(complex_hash->hash); + free(complex_hash->hash); + } + + ob_rw_destroy(&(complex_hash->rwlock)); + free(complex_hash); +} + +static unsigned char *global_get_hash_key(const unsigned char *record, uint *length, + my_bool not_used) +{ + COMPLEX_HASH *complex_hash = (COMPLEX_HASH *)record; + *length= strlen((char *)complex_hash->hash_key); + UNUSED(not_used); + return complex_hash->hash_key; +} + +static int STDCALL +global_hash_init(MYSQL *mysql) +{ + if (!(global_hash = calloc(1, sizeof(OB_HASH)))) { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return 1; + } else if (hash_init(global_hash, 0, 0, 0, (hash_get_key) global_get_hash_key, + global_hash_free, HASH_CASE_INSENSITIVE)) { + hash_free(global_hash); + free(global_hash); + SET_CLIENT_ERROR(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0); + return 1; + } + + return 0; +} + +static const char digits[]= "0123456789abcdef"; + +char *ma_safe_utoa(int base, ulonglong val, char *buf) +{ + *buf--= 0; + do { + *buf--= digits[val % base]; + } while ((val /= base) != 0); + return buf + 1; +} +static int STDCALL +get_complex_hash(MYSQL *mysql, unsigned char *hash_key, int buffer_len, COMPLEX_HASH **complex_hash) +{ + int ret = 0; + + *complex_hash = NULL; + + if (NULL == global_hash) { + ob_rw_wrlock(&global_rwlock); + if (NULL == global_hash && global_hash_init(mysql)) { + ret = 1; + } + ob_rw_unlock(&global_rwlock); + } + if (ret == 0) { + unsigned int hash_length = 0; + char port[10] = {'\0'}; + char *port_ptr = NULL; + port_ptr = ma_safe_utoa(10, mysql->port, &port[sizeof(port)-1]); + if (strlen(mysql->host) + strlen(port_ptr) + strlen(mysql->user) + 1 >= (size_t)buffer_len) { + ret = 1; + } else if ((NULL != mysql->db) && (strlen(mysql->host) + strlen(port_ptr) + strlen(mysql->user) + + strlen(mysql->db) + 1 >= (size_t)buffer_len)) { + ret = 1; + } else { + memcpy(hash_key + hash_length, mysql->host, strlen(mysql->host)); + hash_length += strlen(mysql->host); + + memcpy(hash_key + hash_length, port_ptr, strlen(port_ptr)); + hash_length += strlen(port_ptr); + + memcpy(hash_key + hash_length, mysql->user, strlen(mysql->user)); + hash_length += strlen(mysql->user); + if (NULL != mysql->db) { + memcpy(hash_key + hash_length, mysql->db, strlen(mysql->db)); + hash_length += strlen(mysql->db); + } + hash_key[hash_length] = '\0'; + ob_rw_rdlock(&global_rwlock); + *complex_hash = (COMPLEX_HASH *)hash_search(global_hash, hash_key, strlen((char *)hash_key)); + ob_rw_unlock(&global_rwlock); + + if (NULL == *complex_hash) { + ob_rw_wrlock(&global_rwlock); + if (NULL == *complex_hash) { + if (complex_hash_init(mysql, hash_key, complex_hash)) { + ret = 1; + } + } + ob_rw_unlock(&global_rwlock); + } + } + } + + return (ret); +} + +COMPLEX_TYPE* STDCALL +get_complex_type(MYSQL *mysql, unsigned char *owner_name, unsigned char *type_name) +{ + COMPLEX_TYPE *type = NULL; + COMPLEX_HASH *complex_hash = NULL; + MYSQL *complex_mysql = NULL; + unsigned char hash_key[200] = {'\0'}; + + if (get_complex_hash(mysql, hash_key, 200, &complex_hash)) { + } else { + ob_rw_rdlock(&(complex_hash->rwlock)); + type = (COMPLEX_TYPE *)hash_search(complex_hash->hash, type_name, strlen((char *)type_name)); + ob_rw_unlock(&(complex_hash->rwlock)); + + if (NULL == type || type->is_valid != 1) { + if (complex_mysql_init(mysql, &complex_mysql)) { + } else if (get_type_from_server(complex_mysql, owner_name, type_name, complex_hash)) { + } else { + ob_rw_rdlock(&(complex_hash->rwlock)); + type = (COMPLEX_TYPE *)hash_search(complex_hash->hash, type_name, strlen((char *)type_name)); + ob_rw_unlock(&(complex_hash->rwlock)); + } + } + } + + if (complex_mysql != NULL) { + mysql_close(complex_mysql); + complex_mysql = NULL; + } + + if (type && type->is_valid != 1) { + return (NULL); + } + + return (type); +} + +COMPLEX_TYPE* STDCALL +get_complex_type_with_local(MYSQL *mysql, unsigned char *type_name) +{ + COMPLEX_TYPE *type = NULL; + COMPLEX_HASH *complex_hash = NULL; + unsigned char hash_key[200] = {'\0'}; + memset(hash_key, 0, 200); + + if (get_complex_hash(mysql, hash_key, 200, &complex_hash)) { + } else { + ob_rw_rdlock(&(complex_hash->rwlock)); + type = (COMPLEX_TYPE *)hash_search(complex_hash->hash, type_name, strlen((char *)type_name)); + ob_rw_unlock(&(complex_hash->rwlock)); + } + + if (type && type->is_valid != 1) { + return (NULL); + } + + return (type); +} diff --git a/libmariadb/ob_cond.c b/libmariadb/ob_cond.c new file mode 100644 index 0000000..6956a7f --- /dev/null +++ b/libmariadb/ob_cond.c @@ -0,0 +1,140 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ + +#include "ob_cond.h" + +#ifdef _WIN32 +#include + +union ft64 { + FILETIME ft; + __int64 i64; +}; + +DWORD ob_get_milliseconds(const struct timespec *ob_timespec) +{ + DWORD ret; + union ft64 ob_now_time; + long long ob_millis_time; + unsigned __int64 obtime; + static const unsigned __int64 EPOCH = ((unsigned __int64)116444736000000000ULL); + + if (NULL == ob_timespec) { + ret = INFINITE; + } else { + obtime = ob_timespec->tv_sec * 10000000L + EPOCH; + GetSystemTimeAsFileTime(&ob_now_time.ft); + ob_millis_time = (obtime - ob_now_time.i64) / 10000; + if (ob_millis_time < 0) { + ret = 0; + } else { + if (ob_millis_time > UINT_MAX) { + ob_millis_time= UINT_MAX; + } + ret = ob_millis_time; + } + } + + return ret; +} + +int ob_cond_init(ob_cond_t *ob_cond) +{ + InitializeConditionVariable(ob_cond); + return 0; +} + +int ob_cond_destroy(ob_cond_t *ob_cond) +{ + // do nothing + return 0; +} + +int ob_cond_timedwait(ob_cond_t *ob_cond, ob_mutex_t *ob_mutex, const struct timespec *ob_timespec) +{ + int ret; + DWORD timeout= ob_get_milliseconds(ob_timespec); + if (!SleepConditionVariableCS(ob_cond, ob_mutex, timeout)) { + ret = ETIMEDOUT; + } else { + ret = 0; + } + return ret; +} + +int ob_cond_wait(ob_cond_t *ob_cond, ob_mutex_t *ob_mutex) +{ + int ret; + if (!SleepConditionVariableCS(ob_cond, ob_mutex, INFINITE)) { + ret = ETIMEDOUT; + } else { + ret = 0; + } + return ret; +} + +int ob_cond_signal(ob_cond_t *ob_cond) +{ + WakeConditionVariable(ob_cond); + return 0; +} + +int ob_cond_broadcast(ob_cond_t *ob_cond) +{ + WakeAllConditionVariable(ob_cond); + return 0; +} + +#else + +int ob_cond_init(ob_cond_t *ob_cond) +{ + return pthread_cond_init(ob_cond, NULL); +} + +int ob_cond_destroy(ob_cond_t *ob_cond) +{ + return pthread_cond_destroy(ob_cond); +} + +int ob_cond_timedwait(ob_cond_t *ob_cond, ob_mutex_t *ob_mutex, const struct timespec *ob_timespec) +{ + return pthread_cond_timedwait(ob_cond, ob_mutex, ob_timespec); +} + +int ob_cond_wait(ob_cond_t *ob_cond, ob_mutex_t *ob_mutex) +{ + return pthread_cond_wait(ob_cond, ob_mutex); +} + +int ob_cond_signal(ob_cond_t *ob_cond) +{ + return pthread_cond_signal(ob_cond); +} + +int ob_cond_broadcast(ob_cond_t *ob_cond) +{ + return pthread_cond_broadcast(ob_cond); +} + +#endif \ No newline at end of file diff --git a/libmariadb/ob_full_link_trace.c b/libmariadb/ob_full_link_trace.c new file mode 100644 index 0000000..da7a408 --- /dev/null +++ b/libmariadb/ob_full_link_trace.c @@ -0,0 +1,2002 @@ +#include "ob_full_link_trace.h" + +#include +#include +#include +#if defined(_WIN32) +#include +#include +#else +#include +#endif + +#include "ob_rwlock.h" +#include "ma_global.h" +#include "mariadb_com.h" +#include "ob_protocol20.h" +#include "ob_serialize.h" + +#define TYPE_LENGTH 2 +#define LEN_LENGTH 4 +#define FLT_SIZE sizeof(float) +#define DBL_SIZE sizeof(double) + +#define UUID_PATTERN "%8.8lx-%4.4lx-%4.4lx-%4.4lx-%12.12lx" +#define TRACE_PATTERN "{\"trace_id\":\""UUID_PATTERN"\",\"name\":\"%s\",\"id\":\""UUID_PATTERN"\",\"start_ts\":%ld,\"end_ts\":%ld,\"parent_id\":\""UUID_PATTERN"\",\"is_follow\":%s" +#define UUID_TOSTRING(uuid) \ +((uuid).high_ >> 32), ((uuid).high_ >> 16 & 0xffff), ((uuid).high_ & 0xffff), \ +((uuid).low_ >> 48), ((uuid).low_ & 0xffffffffffff) +#define INIT_SPAN(trace, span) \ +if (OB_NOT_NULL(span) && 0 == span->span_id_.high_) { \ + span->span_id_.low_ = xorshift128plus(trace->uuid_random_seed); \ + span->span_id_.high_ = span->start_ts_; \ +} + +#define sint1korr(A) (*((int8_t*)(A))) + +#define FLT_EXTRA_INFO_DEF(id, type) (id2type[id] = type) +#define FLT_EXTRA_INFO_INIT() \ + do \ + { \ + /* FLT_DRIVER_SPAN_INFO */ \ + FLT_EXTRA_INFO_DEF(FLT_DRIVER_SPAN , MYSQL_TYPE_VAR_STRING); \ + /* APP_INFO */ \ + FLT_EXTRA_INFO_DEF(FLT_CLIENT_IDENTIFIER, MYSQL_TYPE_VAR_STRING); \ + FLT_EXTRA_INFO_DEF(FLT_MODULE, MYSQL_TYPE_VAR_STRING); \ + FLT_EXTRA_INFO_DEF(FLT_ACTION, MYSQL_TYPE_VAR_STRING); \ + FLT_EXTRA_INFO_DEF(FLT_CLIENT_INFO, MYSQL_TYPE_VAR_STRING); \ + /* QUERY_INFO */ \ + FLT_EXTRA_INFO_DEF(FLT_QUERY_START_TIMESTAMP, MYSQL_TYPE_LONGLONG); \ + FLT_EXTRA_INFO_DEF(FLT_QUERY_END_TIMESTAMP, MYSQL_TYPE_LONGLONG); \ + /* CONTROL_INFO */ \ + FLT_EXTRA_INFO_DEF(FLT_LEVEL, MYSQL_TYPE_TINY); \ + FLT_EXTRA_INFO_DEF(FLT_SAMPLE_PERCENTAGE, MYSQL_TYPE_DOUBLE); \ + FLT_EXTRA_INFO_DEF(FLT_RECORD_POLICY, MYSQL_TYPE_TINY); \ + FLT_EXTRA_INFO_DEF(FLT_PRINT_SAMPLE_PCT, MYSQL_TYPE_DOUBLE); \ + FLT_EXTRA_INFO_DEF(FLT_SLOW_QUERY_THRES, MYSQL_TYPE_LONGLONG); \ + /* SPAN_INFO */ \ + FLT_EXTRA_INFO_DEF(FLT_TRACE_ENABLE, MYSQL_TYPE_TINY); \ + FLT_EXTRA_INFO_DEF(FLT_FORCE_PRINT, MYSQL_TYPE_TINY); \ + FLT_EXTRA_INFO_DEF(FLT_TRACE_ID, MYSQL_TYPE_VAR_STRING); \ + FLT_EXTRA_INFO_DEF(FLT_REF_TYPE, MYSQL_TYPE_TINY); \ + FLT_EXTRA_INFO_DEF(FLT_SPAN_ID, MYSQL_TYPE_VAR_STRING); \ + FLT_EXTRA_INFO_DEF(FLT_EXTRA_INFO_ID_END, MAX_NO_FIELD_TYPES); \ + } while (0); +#define FLT_SERIALIZE_FUNC_SET(id, funcname) (flt_funcs[id] = (FLTFunc)FLT_SERIALIZE_FUNC(funcname)) +#define FLT_SERIALIZE_FUNC_INIT() \ + do \ + { \ + /* FLT_DRIVER_SPAN_INFO */ \ + FLT_SERIALIZE_FUNC_SET(FLT_DRIVER_SPAN_INFO, driverspaninfo); \ + /* APP_INFO */ \ + FLT_SERIALIZE_FUNC_SET(FLT_APP_INFO, appinfo); \ + /* QUERY_INFO */ \ + FLT_SERIALIZE_FUNC_SET(FLT_QUERY_INFO, queryinfo); \ + /* CONTROL_INFO */ \ + FLT_SERIALIZE_FUNC_SET(FLT_CONTROL_INFO, controlinfo); \ + /* SPAN_INFO */ \ + FLT_SERIALIZE_FUNC_SET(FLT_SPAN_INFO, spaninfo); \ + } while (0); + +const char *tag_str[FLT_TAG_MAX_TYPE] = +{ + "command_name", + "client_host" +}; + +static ob_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; +static int init_flag = 0; +static enum_field_types id2type[FLT_EXTRA_INFO_ID_END + 1]; +static FLTFunc flt_funcs[FLT_EXTRA_INFO_TYPE_END + 1]; + +static uint64_t xorshift128plus(uint64_t *s); + +inline void flt_set_send_trans_flag(FLTInfo *flt, my_bool flag) +{ + if (OB_NOT_NULL(flt)) { + flt->in_trans_ = flag; + } +} + +static inline int64_t get_current_time_us() +{ + struct timeval tv; + // todo: 这里要对windows做兼容 + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +static inline double flt_get_pct(uint64_t *seed) +{ + uint64_t rand64; + /* 使用uuid中生成随机数的方法得到随机数 */ + rand64 = xorshift128plus(seed); + + return (double)rand64 / UINT64_MAX; +} + +int flt_init(FLTInfo *flt) +{ + int ret = OB_SUCCESS; + + if (OB_NOT_NULL(flt)) { + if (!init_flag) { + ob_mutex_lock(&init_mutex); + if (!init_flag) { + FLT_EXTRA_INFO_INIT(); // init id2type + FLT_SERIALIZE_FUNC_INIT(); // init flt_funcs + init_flag = 1; + } + ob_mutex_unlock(&init_mutex); + } + + memset(flt, 0, sizeof(*flt)); + + flt->client_span_.type_ = FLT_DRIVER_SPAN_INFO; + flt->app_info_.type_ = FLT_APP_INFO; + flt->query_info_.type_ = FLT_QUERY_INFO; + flt->span_info_.type_ = FLT_SPAN_INFO; + flt->control_info_.type_ = FLT_CONTROL_INFO; + flt->in_trans_ = FALSE; + + // 初始化control info, 这时候为非法值,不会build request + flt->control_info_.level_ = -1; + flt->control_info_.sample_pct_ = -1; + flt->control_info_.rp_ = MAX_RECORD_POLICY; + flt->control_info_.print_sample_pct_ = -1; + flt->control_info_.slow_query_threshold_ = -1; + + if (OB_FAIL(trace_init(flt))) { + // trace init error; + } + } + + return ret; +} + +void flt_end(FLTInfo *flt) +{ + trace_end(flt); +} + +my_bool flt_is_vaild(FLTInfo *flt) +{ + my_bool ret = FALSE; + if (OB_ISNULL(flt)) { + // do nothing + } else { + FLTControlInfo *control = &flt->control_info_; + if (control->level_ > 0 && (control->sample_pct_ >= 0 && control->sample_pct_ <= 1) && control->rp_ < MAX_RECORD_POLICY && + (control->print_sample_pct_ >= 0 && control->print_sample_pct_ <= 1)) { + ret = TRUE; + } + } + return ret; +} + +int flt_build_request(MYSQL *mysql, FLTInfo *flt) +{ + int ret = OB_SUCCESS; + ObTrace *trace = OBTRACE(flt); + ObSpanCtx *span; + + if (OB_ISNULL(trace) || OB_ISNULL(flt)) { + ret = OB_ERROR; + } else { + // build request + int32_t serialize_size = 0; + int32_t span_info_serialize_size = 0; + int32_t client_log_serialize_size = 0; + int32_t app_info_serialize_size = 0; + int64_t tmp_trace_id_pos = 0; + int64_t tmp_span_id_pos = 0; + + span = trace->last_active_span_; + + if (TRUE == trace->trace_enable_ && NULL != span) { + INIT_SPAN(trace, span); + // trace enable 为true的情况下需要发span info + if (OB_FAIL(serialize_UUID(flt->trace_id_, UUID4_SERIALIZE_LEN, &tmp_trace_id_pos, &trace->trace_id_))) { + // error + } else if (OB_FAIL(serialize_UUID(flt->span_id_, UUID4_SERIALIZE_LEN, &tmp_span_id_pos, &span->span_id_))) { + // error + } else { + flt->span_info_.trace_id_ = flt->trace_id_; + flt->span_info_.span_id_ = flt->span_id_; + flt->span_info_.type_ = FLT_SPAN_INFO; + flt->span_info_.trace_enable_ = trace->trace_enable_; + flt->span_info_.force_print_ = trace->force_print_; + if (trace->force_print_) { + FLUSH_TRACE(flt); + } + } + } else { + flt->span_info_.trace_enable_ = FALSE; + } + + if (OB_SUCC(ret)) { + if (0 != trace->log_buf_offset_) { + flt->client_span_.client_span_ = trace->log_buf_; + } else { + flt->client_span_.client_span_ = NULL; + } + + if (OB_FAIL(flt_get_serialize_size_extra_info(&app_info_serialize_size, &flt->app_info_))) { + // error + } else if (OB_FAIL(flt_get_serialize_size_extra_info(&client_log_serialize_size, &flt->client_span_))) { + // error + } else if (OB_FAIL(flt_get_serialize_size_extra_info(&span_info_serialize_size, &flt->span_info_))) { + // error + } else { + serialize_size += app_info_serialize_size; + serialize_size += client_log_serialize_size; + serialize_size += span_info_serialize_size; +#ifdef DEBUG_OB20 + printf("spaninfo ssize is %d, log ssize is %d, appinfo ssize %d\n", span_info_serialize_size, client_log_serialize_size, app_info_serialize_size); + if (0 != client_log_serialize_size) { + printf("log:%s\n", flt->client_span_.client_span_); + } +#endif + } + + if (OB_SUCC(ret) && 0 != serialize_size) { + if (MAX_FLT_SERIALIZE_SIZE < serialize_size) { + // MAX_FLT_SERIALIZE_SIZE 大小不够 + ret = OB_ERROR; + } else { + int64_t pos = 0; + + flt->flt_value_data_.length = serialize_size; + flt->flt_value_data_.value_data_ = trace->flt_serialize_buf_; + + if (OB_FAIL(flt_serialize_extra_info(flt->flt_value_data_.value_data_, serialize_size, &pos, &flt->span_info_))) { + // error + } else if (pos != span_info_serialize_size) { + ret = OB_ERROR; + } else if (0 != client_log_serialize_size && OB_FAIL(flt_serialize_extra_info(flt->flt_value_data_.value_data_, serialize_size, &pos, &flt->client_span_))) { + // error + } else if (pos != span_info_serialize_size + client_log_serialize_size) { + ret = OB_ERROR; + } else if (0 != app_info_serialize_size && OB_FAIL(flt_serialize_extra_info(flt->flt_value_data_.value_data_, serialize_size, &pos, &flt->app_info_))) { + // error + } else if (pos != serialize_size) { + ret = OB_ERROR; + } else { + if (0 != app_info_serialize_size) { + // clear app info + memset(&flt->app_info_, 0, sizeof(flt->app_info_)); + flt->app_info_.type_ = FLT_APP_INFO; + } + // reset the log buf offset + trace->log_buf_offset_ = 0; + flt->client_span_.client_span_ = NULL; + } + if (OB_SUCC(ret)) { + if (OB_FAIL(ob20_set_extra_info(mysql, FULL_TRC, &flt->flt_value_data_))) { + // error + } + } + } + } + } + } + + return ret; +} + +int flt_deserialize_extra_info(const char *buf, const int64_t len, int64_t *pos, FullLinkTraceExtraInfoType type, void *flt_info) +{ + int ret = OB_SUCCESS; + while (OB_SUCC(ret) && *pos < len) { + int32_t val_len = 0; + int16_t extra_id; + if (OB_FAIL(flt_resolve_type_and_len(buf, len, pos, &extra_id, &val_len))) { + ret = OB_ERROR; + } else if (OB_FAIL(flt_funcs[type].deserialize_field_func((FullLinkTraceExtraInfoId)(extra_id), val_len, buf, len, pos, flt_info))) { + ret = OB_ERROR; + } else { + // do nothing + } + } + return ret; +} + +int flt_serialize_extra_info(char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + FullLinkTraceExtraInfoType type = ((FLTInfoBase *)flt_info)->type_; + + ret = flt_funcs[type].serialize_func(buf, len, pos, flt_info); + + return ret; +} + +int flt_get_serialize_size_extra_info(int32_t *size, void *flt_info) +{ + int ret = OB_SUCCESS; + FullLinkTraceExtraInfoType type = ((FLTInfoBase *)flt_info)->type_; + + ret = flt_funcs[type].get_serialize_size_func(size, flt_info); + + return ret; +} + +// for control info +int flt_get_serialize_size_controlinfo(int32_t *size, void *flt_info) +{ + int ret = OB_SUCCESS; + int32_t local_size = 0; + UNUSED(flt_info); + + local_size += TYPE_LENGTH + LEN_LENGTH; + local_size += flt_get_store_int1_size(); + local_size += flt_get_store_double_size(); + local_size += flt_get_store_int1_size(); + local_size += flt_get_store_double_size(); + local_size += flt_get_store_int8_size(); + + *size = local_size; + return ret; +} + +int flt_serialize_controlinfo(char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + int64_t org_pos = *pos; + FLTControlInfo *control_info = (FLTControlInfo *)flt_info; + // resrver for type and len + if (*pos + 6 > len) { + ret = OB_SIZE_OVERFLOW; + } else { + *pos += 6; + } + if (OB_FAIL(ret)) { + // do nothing + } else if (OB_FAIL(flt_store_int1(buf, len, pos, control_info->level_, FLT_LEVEL))) { + ret = OB_ERROR; + } else if (OB_FAIL(flt_store_double(buf, len, pos, control_info->sample_pct_, FLT_SAMPLE_PERCENTAGE))) { + ret = OB_ERROR; + } else if (OB_FAIL(flt_store_int1(buf, len, pos, control_info->rp_, FLT_RECORD_POLICY))) { + ret = OB_ERROR; + } else if (OB_FAIL(flt_store_double(buf, len, pos, control_info->print_sample_pct_, FLT_PRINT_SAMPLE_PCT))) { + ret = OB_ERROR; + } else if (OB_FAIL(flt_store_int8(buf, len, pos, control_info->slow_query_threshold_, FLT_SLOW_QUERY_THRES))) { + ret = OB_ERROR; + } else { + // fill type and len in the head + int32_t total_len = *pos - org_pos - 6; + if (OB_FAIL(flt_store_type_and_len(buf, len, &org_pos, control_info->type_, total_len))) { + ret = OB_ERROR; + } else { + // do nothing + } + } + return ret; +} + +int flt_deserialize_field_controlinfo(FullLinkTraceExtraInfoId extra_id, const int64_t v_len, + const char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + FLTControlInfo *flt_control_info = (FLTControlInfo *)flt_info; + switch(extra_id) { + case FLT_LEVEL: { + if (OB_FAIL(flt_get_int1(buf, len, pos, v_len, &flt_control_info->level_))) { + ret = OB_ERROR; + } else { + // do nothing +#ifdef DEBUG_OB20 + printf("level is %hhd\n", flt_control_info->level_); +#endif + } + break; + } + case FLT_SAMPLE_PERCENTAGE: { + if (OB_FAIL(flt_get_double(buf, len, pos, v_len, &flt_control_info->sample_pct_))) { + ret = OB_ERROR; + } else { + // do nothing +#ifdef DEBUG_OB20 + printf("sample_pct is %f\n", flt_control_info->sample_pct_); +#endif + } + break; + } + case FLT_RECORD_POLICY: { + int8_t v = 0; + if (OB_FAIL(flt_get_int1(buf, len, pos, v_len, &v))) { + ret = OB_ERROR; + } else { + flt_control_info->rp_ = v; +#ifdef DEBUG_OB20 + printf("rp is %d\n", flt_control_info->rp_); +#endif + } + break; + } + case FLT_PRINT_SAMPLE_PCT: { + if (OB_FAIL(flt_get_double(buf, len, pos, v_len, &flt_control_info->print_sample_pct_))) { + ret = OB_ERROR; + } else { + // do nothing +#ifdef DEBUG_OB20 + printf("print_sample_pct is %f\n", flt_control_info->print_sample_pct_); +#endif + } + break; + } + case FLT_SLOW_QUERY_THRES: { + if (OB_FAIL(flt_get_int8(buf, len, pos, v_len, &flt_control_info->slow_query_threshold_))) { + ret = OB_ERROR; + } else { + // do nothing +#ifdef DEBUG_OB20 + printf("slow_query_threshold is %ld\n", flt_control_info->slow_query_threshold_); +#endif + } + break; + } + default: { + // 这里碰到不能识别的key直接跳过 + *pos += *pos + v_len; + break; + } + } + return ret; +} +// for control info + +// for app info +int flt_get_serialize_size_appinfo(int32_t *size, void *flt_info) +{ + int ret = OB_SUCCESS; + int32_t local_size = 0; + FLTAppInfo *app_info = (FLTAppInfo *)flt_info; + + if (OB_NOT_NULL(app_info->identifier_)) { + local_size += flt_get_store_str_size(strlen(app_info->identifier_)); + } + if (OB_NOT_NULL(app_info->module_)) { + local_size += flt_get_store_str_size(strlen(app_info->module_)); + } + if (OB_NOT_NULL(app_info->action_)) { + local_size += flt_get_store_str_size(strlen(app_info->action_)); + } + if (OB_NOT_NULL(app_info->client_info_)) { + local_size += flt_get_store_str_size(strlen(app_info->client_info_)); + } + if (0 != local_size) { + local_size += TYPE_LENGTH + LEN_LENGTH; + } + + *size = local_size; + return ret; +} + +int flt_serialize_appinfo(char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + int64_t org_pos = *pos; + FLTAppInfo *app_info = (FLTAppInfo *)flt_info; + // resrver for type and len + if (*pos + 6 > len) { + ret = OB_SIZE_OVERFLOW; + } else { + *pos += 6; + } + if (OB_FAIL(ret)) { + // do nothing + } else if (OB_NOT_NULL(app_info->identifier_) && OB_FAIL(flt_store_str(buf, len, pos, app_info->identifier_, strlen(app_info->identifier_), FLT_CLIENT_IDENTIFIER))) { + ret = OB_ERROR; + } else if (OB_NOT_NULL(app_info->module_) && OB_FAIL(flt_store_str(buf, len, pos, app_info->module_, strlen(app_info->module_), FLT_MODULE))) { + ret = OB_ERROR; + } else if (OB_NOT_NULL(app_info->action_) && OB_FAIL(flt_store_str(buf, len, pos, app_info->action_, strlen(app_info->action_), FLT_ACTION))) { + ret = OB_ERROR; + } else if (OB_NOT_NULL(app_info->client_info_) && OB_FAIL(flt_store_str(buf, len, pos, app_info->client_info_, strlen(app_info->client_info_), FLT_CLIENT_INFO))) { + ret = OB_ERROR; + } else { + // fill type and len in the head + int32_t total_len = *pos - org_pos - 6; + if (OB_FAIL(flt_store_type_and_len(buf, len, &org_pos, app_info->type_, total_len))) { + ret = OB_ERROR; + } else { + // do nothing + } + } + return ret; +} + +int flt_deserialize_field_appinfo(FullLinkTraceExtraInfoId extra_id, const int64_t v_len, + const char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + FLTAppInfo *app_info = (FLTAppInfo *)flt_info; + switch(extra_id) { + case FLT_CLIENT_IDENTIFIER: { + if (OB_FAIL(flt_get_str(buf, len, pos, v_len, (char **)&app_info->identifier_))) { + ret = OB_ERROR; + } else { + // do nothing + } + break; + } + case FLT_MODULE: { + if (OB_FAIL(flt_get_str(buf, len, pos, v_len, (char **)&app_info->module_))) { + ret = OB_ERROR; + } else { + // do nothing + } + break; + } + case FLT_ACTION: { + if (OB_FAIL(flt_get_str(buf, len, pos, v_len, (char **)&app_info->action_))) { + ret = OB_ERROR; + } else { + // do nothing + } + break; + } + case FLT_CLIENT_INFO: { + if (OB_FAIL(flt_get_str(buf, len, pos, v_len, (char **)&app_info->client_info_))) { + ret = OB_ERROR; + } else { + // do nothing + } + break; + } + default: { + // 这里碰到不能识别的key直接跳过 + *pos += *pos + v_len; + break; + } + } + return ret; +} +// for app info + +// for queryinfo +int flt_get_serialize_size_queryinfo(int32_t *size, void *flt_info) +{ + int ret = OB_SUCCESS; + int32_t local_size = 0; + UNUSED(flt_info); + + local_size += TYPE_LENGTH + LEN_LENGTH; + local_size += flt_get_store_int8_size(); + local_size += flt_get_store_int8_size(); + + *size = local_size; + return ret; +} + +int flt_serialize_queryinfo(char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + int64_t org_pos = *pos; + FLTQueryInfo *query_info = (FLTQueryInfo *)flt_info; + // resrver for type and len + if (*pos + 6 > len) { + ret = OB_SIZE_OVERFLOW; + } else { + *pos += 6; + } + if (OB_FAIL(ret)) { + // do nothing + } else if (OB_FAIL(flt_store_int8(buf, len, pos, query_info->query_start_timestamp_, FLT_QUERY_START_TIMESTAMP))) { + ret = OB_ERROR; + } else if (OB_FAIL(flt_store_int8(buf, len, pos, query_info->query_end_timestamp_, FLT_QUERY_END_TIMESTAMP))) { + ret = OB_ERROR; + } else { + // fill type and len in the head + int32_t total_len = *pos - org_pos - 6; + if (OB_FAIL(flt_store_type_and_len(buf, len, &org_pos, query_info->type_, total_len))) { + ret = OB_ERROR; + } else { + // do nothing + } + } + return ret; +} +int flt_deserialize_field_queryinfo(FullLinkTraceExtraInfoId extra_id, const int64_t v_len, + const char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + FLTQueryInfo *query_info = (FLTQueryInfo *)flt_info; + switch(extra_id) { + case FLT_QUERY_START_TIMESTAMP: { + if (OB_FAIL(flt_get_int8(buf, len, pos, v_len, &query_info->query_start_timestamp_))) { + ret = OB_ERROR; + } else { + // printf("get start timestamp is %ld\n", query_info->query_start_timestamp_); + // do nothing + } + break; + } + case FLT_QUERY_END_TIMESTAMP: { + if (OB_FAIL(flt_get_int8(buf, len, pos, v_len, &query_info->query_end_timestamp_))) { + ret = OB_ERROR; + } else { + // printf("get end timestamp is %ld\n", query_info->query_end_timestamp_); + // do nothing + } + break; + } + default: { + // 这里碰到不能识别的key直接跳过 + *pos += *pos + v_len; + break; + } + } + return ret; +} +// for queryinfo + +// for spaninfo +int flt_get_serialize_size_spaninfo(int32_t *size, void *flt_info) +{ + int ret = OB_SUCCESS; + int32_t local_size = 0; + FLTSpanInfo *span_info = (FLTSpanInfo *)flt_info; + // FLTSpanInfo *span_info = (FLTSpanInfo *)flt_info; + + // 如果开启trace, 才需要发其他字段 + if (TRUE == span_info->trace_enable_) { + local_size += TYPE_LENGTH + LEN_LENGTH; + local_size += flt_get_store_int1_size(); + local_size += flt_get_store_int1_size(); + local_size += flt_get_store_str_size(UUID4_SERIALIZE_LEN); + local_size += flt_get_store_int1_size(); + local_size += flt_get_store_str_size(UUID4_SERIALIZE_LEN); + } + + *size = local_size; + return ret; +} + +int flt_serialize_spaninfo(char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + int64_t org_pos = *pos; + FLTSpanInfo *span_info = (FLTSpanInfo *)flt_info; + // resrver for type and len + if (TRUE == span_info->trace_enable_) { + if (*pos + 6 > len) { + ret = OB_SIZE_OVERFLOW; + } else { + *pos += 6; + } + if (OB_FAIL(ret)) { + // do nothing + } else if (OB_FAIL(flt_store_int1(buf, len, pos, span_info->trace_enable_, FLT_TRACE_ENABLE))) { + ret = OB_ERROR; + } else if (OB_FAIL(flt_store_int1(buf, len, pos, span_info->force_print_, FLT_FORCE_PRINT))) { + ret = OB_ERROR; + } else if (OB_FAIL(flt_store_str(buf, len, pos, span_info->trace_id_, UUID4_SERIALIZE_LEN, FLT_TRACE_ID))) { + ret = OB_ERROR; + } else if (OB_FAIL(flt_store_int1(buf, len, pos, span_info->ref_type_, FLT_REF_TYPE))) { + ret = OB_ERROR; + } else if (OB_FAIL(flt_store_str(buf, len, pos, span_info->span_id_, UUID4_SERIALIZE_LEN, FLT_SPAN_ID))) { + ret = OB_ERROR; + } else { + // success + } + if (OB_SUCC(ret)) { + // fill type and len in the head + int32_t total_len = *pos - org_pos - 6; + if (OB_FAIL(flt_store_type_and_len(buf, len, &org_pos, span_info->type_, total_len))) { + ret = OB_ERROR; + } else { + // do nothing + } + } + } + return ret; +} + +int flt_deserialize_field_spaninfo(FullLinkTraceExtraInfoId extra_id, const int64_t v_len, + const char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + FLTSpanInfo *span_info = (FLTSpanInfo *)flt_info; + switch(extra_id) { + case FLT_TRACE_ENABLE: { + if (OB_FAIL(flt_get_int1(buf, len, pos, v_len, &span_info->trace_enable_))) { + ret = OB_ERROR; + } else { + // do nothing + } + break; + } + case FLT_FORCE_PRINT: { + if (OB_FAIL(flt_get_int1(buf, len, pos, v_len, &span_info->force_print_))) { + ret = OB_ERROR; + } else { + // do nothing + } + break; + } + case FLT_TRACE_ID: { + if (OB_FAIL(flt_get_str(buf, len, pos, v_len, (char **)&span_info->trace_id_))) { + ret = OB_ERROR; + } else { + // do nothing + } + break; + } + case FLT_REF_TYPE: { + if (OB_FAIL(flt_get_int1(buf, len, pos, v_len, &span_info->ref_type_))) { + ret = OB_ERROR; + } else { + // do nothing + } + break; + } + case FLT_SPAN_ID: { + if (OB_FAIL(flt_get_str(buf, len, pos, v_len, (char **)&span_info->span_id_))) { + ret = OB_ERROR; + } else { + // do nothing + } + break; + } + default: { + // 这里碰到不能识别的key直接跳过 + *pos += *pos + v_len; + break; + } + } + return ret; +} +// for spaninfo + +// for driverspaninfo info +int flt_get_serialize_size_driverspaninfo(int32_t *size, void *flt_info) +{ + int ret = OB_SUCCESS; + int32_t local_size = 0; + FLTDriverSpanInfo *client_info = (FLTDriverSpanInfo *)flt_info; + + if (NULL != client_info->client_span_) { + local_size += TYPE_LENGTH + LEN_LENGTH; + local_size += flt_get_store_str_size(strlen(client_info->client_span_) + 1); + } + + *size = local_size; + return ret; +} + +int flt_serialize_driverspaninfo(char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + int64_t org_pos = *pos; + FLTDriverSpanInfo *client_info = (FLTDriverSpanInfo *)flt_info; + // resrver for type and len + if (*pos + 6 > len) { + ret = OB_SIZE_OVERFLOW; + } else { + *pos += 6; + } + if (OB_FAIL(ret)) { + // do nothing + } else if (OB_FAIL(flt_store_str(buf, len, pos, client_info->client_span_, strlen(client_info->client_span_) + 1, FLT_DRIVER_SPAN))) { + ret = OB_ERROR; + } else { + // fill type and len in the head + int32_t total_len = *pos - org_pos - 6; + if (OB_FAIL(flt_store_type_and_len(buf, len, &org_pos, client_info->type_, total_len))) { + ret = OB_ERROR; + } else { + // do nothing + } + } + return ret; +} + +int flt_deserialize_field_driverspaninfo(FullLinkTraceExtraInfoId extra_id, const int64_t v_len, + const char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + int ret = OB_SUCCESS; + FLTDriverSpanInfo *client_info = (FLTDriverSpanInfo *)flt_info; + switch(extra_id) { + case FLT_CLIENT_IDENTIFIER: { + if (OB_FAIL(flt_get_str(buf, len, pos, v_len, (char **)&client_info->client_span_))) { + ret = OB_ERROR; + } else { + // do nothing + } + break; + } + default: { + // 这里碰到不能识别的key直接跳过 + *pos += *pos + v_len; + break; + } + } + return ret; +} +// for driverspaninfo info + +// for no support +int flt_deserialize_field_nosupport(FullLinkTraceExtraInfoId extra_id, const int64_t v_len, + const char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + UNUSED(extra_id); + UNUSED(v_len); + UNUSED(buf); + UNUSED(len); + UNUSED(pos); + UNUSED(flt_info); + return OB_ERROR; +} +int flt_serialize_nosupport(char *buf, const int64_t len, int64_t *pos, void *flt_info) +{ + UNUSED(buf); + UNUSED(len); + UNUSED(pos); + UNUSED(flt_info); + return OB_ERROR; +} +int flt_get_serialize_size_nosupport(int32_t *size, void *flt_info) +{ + UNUSED(size); + UNUSED(flt_info); + return OB_ERROR; +} + +// for encode +static int flt_store_int(char *buf, int64_t len, int64_t *ppos, int64_t v, int16_t type, int64_t v_len) +{ + int ret = OB_SUCCESS; + int64_t pos = *ppos; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if (len < pos + TYPE_LENGTH + LEN_LENGTH + v_len) { + ret = OB_ERROR; + } else { + int2store(buf + pos, type); + pos += 2; + int4store(buf + pos, v_len); + pos += 4; + + if (1 == v_len) { + int1store(buf + pos, v); + } else if (2 == v_len) { + int2store(buf + pos, v); + } else if (3 == v_len) { + int3store(buf + pos, v); + } else if (4 == v_len) { + int4store(buf + pos, v); + } else if (5 == v_len) { + int5store(buf + pos, v); + } else if (6 == v_len) { + int6store(buf + pos, v); + } else if (8 == v_len) { + int8store(buf + pos, v); + } + pos += v_len; + + *ppos = pos; + } + + return ret; +} + + +int flt_store_type_and_len(char *buf, int64_t len, int64_t *ppos, int16_t type, int32_t v_len) +{ + int ret = OB_SUCCESS; + int64_t pos = *ppos; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if (len < pos + TYPE_LENGTH + LEN_LENGTH) { + ret = OB_ERROR; + } else { + int2store(buf + pos, type); + pos += 2; + int4store(buf + pos, v_len); + pos += 4; + + *ppos = pos; + } + + return ret; +} + +int flt_store_str(char *buf, int64_t len, int64_t *ppos, const char *str, const uint64_t str_len, int16_t type) +{ + int ret = OB_SUCCESS; + int64_t pos = *ppos; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if (len < pos + TYPE_LENGTH + LEN_LENGTH + (int64_t)str_len) { + ret = OB_ERROR; + } else { + int2store(buf + pos, type); + pos += 2; + int4store(buf + pos, str_len); + pos += 4; + + memcpy(buf + pos, str, str_len); + pos += str_len; + + *ppos = pos; + } + + return ret; +} + +int flt_store_int1(char *buf, int64_t len, int64_t *pos, int8_t v, int16_t type) +{ + return flt_store_int(buf, len, pos, v, type, 1); +} +int flt_store_int2(char *buf, int64_t len, int64_t *pos, int16_t v, int16_t type) +{ + return flt_store_int(buf, len, pos, v, type, 2); +} +int flt_store_int3(char *buf, int64_t len, int64_t *pos, int32_t v, int16_t type) +{ + return flt_store_int(buf, len, pos, v, type, 3); +} +int flt_store_int4(char *buf, int64_t len, int64_t *pos, int32_t v, int16_t type) +{ + return flt_store_int(buf, len, pos, v, type, 4); +} +int flt_store_int5(char *buf, int64_t len, int64_t *pos, int64_t v, int16_t type) +{ + return flt_store_int(buf, len, pos, v, type, 5); +} +int flt_store_int6(char *buf, int64_t len, int64_t *pos, int64_t v, int16_t type) +{ + return flt_store_int(buf, len, pos, v, type, 6); +} +int flt_store_int8(char *buf, int64_t len, int64_t *pos, int64_t v, int16_t type) +{ + return flt_store_int(buf, len, pos, v, type, 7); +} + +int flt_store_double(char *buf, const int64_t len, int64_t *ppos, double val, int16_t type) +{ + int ret = OB_SUCCESS; + int v_len = DBL_SIZE; + int64_t pos = *ppos; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if ((uint64_t)len < pos + DBL_SIZE + TYPE_LENGTH + LEN_LENGTH) { + ret = OB_ERROR; + } else { + int2store(buf + pos, type); + pos += 2; + int4store(buf + pos, v_len); + pos += 4; + + memcpy(buf + pos, &val, DBL_SIZE); + pos += DBL_SIZE; + + *ppos = pos; + } + return ret; +} + +int flt_store_float(char *buf, const int64_t len, int64_t *ppos, float val, int16_t type) +{ + int ret = OB_SUCCESS; + int v_len = FLT_SIZE; + int64_t pos = *ppos; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if ((uint64_t)len < pos + FLT_SIZE + TYPE_LENGTH + LEN_LENGTH) { + ret = OB_ERROR; + } else { + int2store(buf + pos, type); + pos += 2; + int4store(buf + pos, v_len); + pos += 4; + + memcpy(buf + pos, &val, FLT_SIZE); + pos += FLT_SIZE; + + *ppos = pos; + } + return ret; +} + +inline int flt_get_store_str_size(const uint64_t str_len) +{ + return TYPE_LENGTH + LEN_LENGTH + str_len; +} +int flt_get_store_int1_size() +{ + return TYPE_LENGTH + LEN_LENGTH + 1; +} +int flt_get_store_int2_size() +{ + return TYPE_LENGTH + LEN_LENGTH + 2; +} +int flt_get_store_int3_size() +{ + return TYPE_LENGTH + LEN_LENGTH + 3; +} +int flt_get_store_int4_size() +{ + return TYPE_LENGTH + LEN_LENGTH + 4; +} +int flt_get_store_int5_size() +{ + return TYPE_LENGTH + LEN_LENGTH + 5; +} +int flt_get_store_int6_size() +{ + return TYPE_LENGTH + LEN_LENGTH + 6; +} +int flt_get_store_int8_size() +{ + return TYPE_LENGTH + LEN_LENGTH + 8; +} +int flt_get_store_double_size() +{ + return TYPE_LENGTH + LEN_LENGTH + DBL_SIZE; +} +int flt_get_store_float_size() +{ + return TYPE_LENGTH + LEN_LENGTH + FLT_SIZE; +} + +static int flt_get_int(const char *buf, int64_t len, int64_t *ppos, int64_t v_len, void *val) +{ + int ret = OB_SUCCESS; + int64_t pos = *ppos; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if (pos + v_len > len) { + ret = OB_ERROR; + } else { + if (1 == v_len) { + *(int8_t *)val = sint1korr(buf + pos); + } else if (2 == v_len) { + *(int16_t *)val = sint2korr(buf + pos); + } else if (3 == v_len) { + *(int32_t *)val = sint3korr(buf + pos); + } else if (4 == v_len) { + *(int32_t *)val = sint4korr(buf + pos); + } else if (8 == v_len) { + *(int64_t *)val = sint8korr(buf + pos); + } + + pos += v_len; + *ppos = pos; + } + + return ret; +} + +int flt_resolve_type_and_len(const char *buf, int64_t len, int64_t *ppos, int16_t *type, int32_t *v_len) +{ + int ret = OB_SUCCESS; + int64_t pos = *ppos; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if (pos + TYPE_LENGTH + LEN_LENGTH > len) { + ret = OB_ERROR; + } else { + *type = sint2korr(buf + pos); + pos += 2; + *v_len = sint4korr(buf + pos); + pos += 4; + + *ppos = pos; + } + return ret; +} + +int flt_get_str(const char *buf, int64_t len, int64_t *ppos, int64_t str_len, char **str) +{ + int ret = OB_SUCCESS; + int64_t pos = *ppos; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if (pos + str_len > len) { + ret = OB_ERROR; + } else { + *str = (char *)buf; + + pos += str_len; + *ppos = pos; + } + return ret; +} + +//@{ Signed integer in reverse sequence, write the result to v, and update pos +int flt_get_int1(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int8_t *val) +{ + int ret = OB_SUCCESS; + + if (1 != v_len) { + ret = OB_ERROR; + } else { + ret = flt_get_int(buf, len, pos, v_len, val); + } + + return ret; +} +int flt_get_int2(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int16_t *val) +{ + int ret = OB_SUCCESS; + + if (2 != v_len) { + ret = OB_ERROR; + } else { + ret = flt_get_int(buf, len, pos, v_len, val); + } + + return ret; +} +int flt_get_int3(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int32_t *val) +{ + int ret = OB_SUCCESS; + + if (3 != v_len) { + ret = OB_ERROR; + } else { + ret = flt_get_int(buf, len, pos, v_len, val); + } + + return ret; +} +int flt_get_int4(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int32_t *val) +{ + int ret = OB_SUCCESS; + + if (4 != v_len) { + ret = OB_ERROR; + } else { + ret = flt_get_int(buf, len, pos, v_len, val); + } + + return ret; +} +int flt_get_int8(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int64_t *val) +{ + int ret = OB_SUCCESS; + + if (8 != v_len) { + ret = OB_ERROR; + } else { + ret = flt_get_int(buf, len, pos, v_len, val); + } + + return ret; +} + +int flt_get_double(const char *buf, int64_t len, int64_t *ppos, int64_t v_len, double *val) +{ + int ret = OB_SUCCESS; + int64_t pos = *ppos; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if (pos + v_len > len || sizeof(double) != v_len) { + ret = OB_ERROR; + } else { + *val = (*((double *)(buf + pos))); + + pos += v_len; + *ppos = pos; + } + return ret; +} + +int flt_get_float(const char *buf, int64_t len, int64_t *ppos, int64_t v_len, float *val) +{ + int ret = OB_SUCCESS; + int64_t pos = *ppos; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if (pos + v_len > len || sizeof(float) != v_len) { + ret = OB_ERROR; + } else { + *val = (*((float *)(buf + pos))); + + pos += v_len; + *ppos = pos; + } + return ret; +} + +static uint64_t xorshift128plus(uint64_t *s) { + uint64_t s1 = s[0]; + const uint64_t s0 = s[1]; + s[0] = s0; + s1 ^= s1 << 23; + s[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); + return s[1] + s0; +} + +int uuid4_init(uint64_t *seed, size_t seed_size) { +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) + size_t res; + FILE *fp = fopen("/dev/urandom", "rb"); + if (!fp) { + return OB_ERROR; + } + res = fread(seed, 1, seed_size, fp); + fclose(fp); + if ( res != seed_size ) { + return OB_ERROR; + } + +#elif defined(_WIN32) + int res; + HCRYPTPROV hCryptProv; + res = CryptAcquireContext( + &hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + if (!res) { + return OB_ERROR; + } + res = CryptGenRandom(hCryptProv, (DWORD) seed_size, (PBYTE) seed); + CryptReleaseContext(hCryptProv, 0); + if (!res) { + return OB_ERROR; + } + +#else + #error "unsupported platform" +#endif + return OB_SUCCESS; +} + + +UUID uuid4_generate(uint64_t *seed) { + uint64_t word; + UUID ret; + /* get random */ + word = xorshift128plus(seed); + // word[1] = xorshift128plus(seed); + + ret.high_ = get_current_time_us(); + ret.low_ = word; + return ret; +} + +ObTrace *get_trace_instance(FLTInfo *flt) +{ + return flt->trace_; +} + +int trace_init(FLTInfo *flt) +{ + int index; + int ret = OB_SUCCESS; + if (OB_ISNULL(flt)) { + ret = OB_ERROR; + } else { + ObTrace *trace = (ObTrace *)malloc(OBTRACE_DEFAULT_BUFFER_SIZE); + if (OB_ISNULL(trace)) { + // malloc error + ret = OB_ERROR; + } else { + memset(trace, 0, sizeof(*trace)); + trace->buffer_size_ = OBTRACE_DEFAULT_BUFFER_SIZE - sizeof(*trace); + trace->log_buf_ = trace->data_; // 大小为MAX_TRACE_LOG_SIZE + trace->flt_serialize_buf_ = trace->data_ + MAX_TRACE_LOG_SIZE; // 大小为MAX_FLT_SERIALIZE_SIZE + trace->offset_ = INIT_OFFSET; + trace->auto_flush_ = 1; + trace->trace_enable_ = FALSE; + trace->force_print_ = FALSE; + trace->slow_query_print_ = FALSE; + trace->root_span_id_.low_ = 0; + trace->root_span_id_.high_ = 0; + trace->flt = flt; + flt->trace_ = trace; + for (index = 0; index < SPAN_CACHE_COUNT; ++index) { + char *begin_addr = trace->data_ + MAX_TRACE_LOG_SIZE + MAX_FLT_SERIALIZE_SIZE + (index * (sizeof(LIST) + sizeof(ObSpanCtx))); + LIST *list = (LIST *)begin_addr; + list->data = (ObSpanCtx *)(begin_addr + sizeof(LIST)); + trace->free_span_list_ = list_add(trace->free_span_list_, list); + } + if (OB_FAIL(uuid4_init(trace->uuid_random_seed, sizeof(trace->uuid_random_seed)))) { + // init uuid seed error + } + } + } + return ret; +} + +void trace_end(FLTInfo *flt) +{ + if (OB_NOT_NULL(flt)) { + free(flt->trace_); + flt->trace_ = NULL; + } + return; +} + +void begin_trace(ObTrace *trace) +{ + if (OB_NOT_NULL(trace)) { + FLTInfo *flt = trace->flt; + + if (OB_ISNULL(flt)) { + // error, do not entable trace + trace->trace_enable_ = FALSE; + } else if (FALSE == flt_is_vaild(flt)) { + // error, invalid trace + trace->trace_enable_ = FALSE; + } else { + double trace_pct = flt_get_pct(trace->uuid_random_seed); + + if (trace_pct <= flt->control_info_.sample_pct_) { + // trace enable + trace->trace_enable_ = TRUE; + } else { + trace->trace_enable_ = FALSE; + } + } + + if (TRUE == trace->trace_enable_) { + trace->trace_id_ = uuid4_generate(trace->uuid_random_seed); + trace->level_ = flt->control_info_.level_; + // 如果trace命中,后续计算打印策略 + if (RP_ALL == flt->control_info_.rp_) { + // 所有命中trace的都打印 + trace->force_print_ = TRUE; + } else if (RP_SAMPLE_AND_SLOW_QUERY == flt->control_info_.rp_) { + // 命中打印采样的需要打印 + double print_pct = flt_get_pct(trace->uuid_random_seed); + if (print_pct <= flt->control_info_.print_sample_pct_) { + trace->force_print_ = TRUE; + } else { + trace->force_print_ = FALSE; + } + } else if (RP_ONLY_SLOW_QUERY == flt->control_info_.rp_) { + // 中打印slow query + trace->force_print_ = FALSE; + } else { + // 其他策略,当前不打印 + trace->force_print_ = FALSE; + } + } else { + // 设置trace level为0,后续span都不会处理 + trace->level_ = 0; + trace->force_print_ = FALSE; + trace->trace_id_.low_ = 0; + trace->trace_id_.high_ = 0; + } + +#ifdef DEBUG_OB20 + printf("trace enable is %d, force print is %d\n", trace->trace_enable_, trace->force_print_); +#endif + } +} + +void end_trace(ObTrace *trace) +{ + if (OB_NOT_NULL(trace)) { + LIST *span_elem; + ObSpanCtx *span; + while(OB_NOT_NULL(trace->current_span_list_)) { + span_elem = trace->current_span_list_; + span = (ObSpanCtx *)span_elem->data; + if (0 == span->end_ts_) { + span->end_ts_ = get_current_time_us(); + } + trace->current_span_list_ = trace->current_span_list_->next; + // add all elem to free span list + trace->free_span_list_ = list_add(trace->free_span_list_, span_elem); + } + trace->offset_ = INIT_OFFSET; + trace->policy_ = 0; + trace->last_active_span_ = NULL; + } +} + +// used by slow query +void flush_first_span(ObTrace *trace) +{ + LIST *span_list; + ObSpanCtx *span; + int64_t pos = 0; + int ret = OB_SUCCESS; + + if (OB_ISNULL(trace) || (0 == trace->trace_id_.low_ && 0 == trace->trace_id_.high_)) { + // do nothing + } else if (OB_ISNULL(span_list = trace->current_span_list_)) { + // do nothing + } else if (OB_ISNULL(span = (ObSpanCtx *)span_list->data)) { + // do nothing + } else { + ObTagCtx *tag = span->tags_; + my_bool first = TRUE; + char buf[MAX_TRACE_LOG_SIZE]; + pos = 0; + INIT_SPAN(trace, span); + while (OB_SUCC(ret) && OB_NOT_NULL(tag)) { + if (pos + 10 >= MAX_TRACE_LOG_SIZE) { + ret = OB_ERROR; + } else { + buf[pos++] = ','; + if (first) { + strcpy(buf + pos, "\"tags\":["); + pos += 8; + first = FALSE; + } + ret = tostring_ObTagCtx(buf, MAX_TRACE_LOG_SIZE, &pos, tag); + tag = tag->next_; + } + } + if (0 != pos) { + if (pos + 1 < MAX_TRACE_LOG_SIZE) { + buf[pos++] = ']'; + buf[pos++] = 0; + } else { + buf[MAX_TRACE_LOG_SIZE - 2] = ']'; + buf[MAX_TRACE_LOG_SIZE - 1] = 0; + } + } + if (OB_SUCC(ret)) { + INIT_SPAN(trace, span->source_span_); + ret = snprintf(trace->log_buf_ + trace->log_buf_offset_, + MAX_TRACE_LOG_SIZE - trace->log_buf_offset_, + TRACE_PATTERN "%s}", + UUID_TOSTRING(trace->trace_id_), + "obclient", + UUID_TOSTRING(span->span_id_), + span->start_ts_, + span->end_ts_, + UUID_TOSTRING(OB_ISNULL(span->source_span_) ? trace->root_span_id_ : span->source_span_->span_id_), + span->is_follow_ ? "true" : "false", + buf); + if (ret < 0 || trace->log_buf_offset_ + ret >= MAX_TRACE_LOG_SIZE) { + ret = OB_ERROR; + } else { + trace->log_buf_offset_ += ret; + ret = OB_SUCCESS; + } + } + if (OB_SUCC(ret) && 0 != span->end_ts_) { + trace->current_span_list_ = list_delete(trace->current_span_list_, span_list); + trace->free_span_list_ = list_add(trace->free_span_list_, span_list); + } + span->tags_ = 0; + } +} + +void flush_trace(ObTrace *trace) +{ + LIST *next; + LIST *span_list; + ObSpanCtx *span; + int64_t pos = 0; + int ret = OB_SUCCESS; + + if (OB_ISNULL(trace) || (0 == trace->trace_id_.low_ && 0 == trace->trace_id_.high_)) { + // do nothing + } else if (OB_ISNULL(span_list = trace->current_span_list_)) { + // do nothing + } else { + while (OB_NOT_NULL(span_list)) { + span = (ObSpanCtx *)span_list->data; + next = span_list->next; + if (OB_NOT_NULL(span)) { + ObTagCtx *tag = span->tags_; + my_bool first = TRUE; + char buf[MAX_TRACE_LOG_SIZE] = {0}; + pos = 0; + INIT_SPAN(trace, span); + while (OB_SUCC(ret) && OB_NOT_NULL(tag)) { + if (pos + 10 >= MAX_TRACE_LOG_SIZE) { + ret = OB_ERROR; + } else { + buf[pos++] = ','; + if (first) { + strcpy(buf + pos, "\"tags\":["); + pos += 8; + first = FALSE; + } + ret = tostring_ObTagCtx(buf, MAX_TRACE_LOG_SIZE, &pos, tag); + tag = tag->next_; + } + } + if (0 != pos) { + if (pos + 1 < MAX_TRACE_LOG_SIZE) { + buf[pos++] = ']'; + buf[pos++] = 0; + } else { + buf[MAX_TRACE_LOG_SIZE - 2] = ']'; + buf[MAX_TRACE_LOG_SIZE - 1] = 0; + } + } + if (OB_SUCC(ret)) { + INIT_SPAN(trace, span->source_span_); + ret = snprintf(trace->log_buf_ + trace->log_buf_offset_, + MAX_TRACE_LOG_SIZE - trace->log_buf_offset_, + TRACE_PATTERN "%s}", + UUID_TOSTRING(trace->trace_id_), + "obclient", + UUID_TOSTRING(span->span_id_), + span->start_ts_, + span->end_ts_, + UUID_TOSTRING(OB_ISNULL(span->source_span_) ? trace->root_span_id_ : span->source_span_->span_id_), + span->is_follow_ ? "true" : "false", + buf); + if (ret < 0 || trace->log_buf_offset_ + ret >= MAX_TRACE_LOG_SIZE) { + ret = OB_ERROR; + } else { + trace->log_buf_offset_ += ret; + ret = OB_SUCCESS; + } + } + if (OB_SUCC(ret) && 0 != span->end_ts_) { + trace->current_span_list_ = list_delete(trace->current_span_list_, span_list); + trace->free_span_list_ = list_add(trace->free_span_list_, span_list); + } + span->tags_ = 0; + } + span_list = next; + } + trace->offset_ = INIT_OFFSET; + } +} + +ObSpanCtx* begin_span(ObTrace *trace, uint32_t span_type, uint8_t level, my_bool is_follow) +{ + ObSpanCtx *new_span = NULL; +#ifdef DEBUG_OB20 + printf("//////////////////////call begin_span ////////////////////////////////\n"); +#endif + if (OB_ISNULL(trace)) { + // do nothing + } else if (trace->trace_id_.low_ == 0 && trace->trace_id_.high_ == 0) { + // trace is not enable + } else if (level > trace->level_) { + // do nothing + } else { + if (OB_ISNULL(trace->free_span_list_)) { + FLUSH_TRACE(trace->flt); + } + if (OB_NOT_NULL(trace->free_span_list_)) { + LIST *span_elem = trace->free_span_list_; + trace->free_span_list_ = trace->free_span_list_->next; + if (OB_NOT_NULL(span_elem)) { + new_span = (ObSpanCtx *)span_elem->data; + new_span->span_type_ = span_type; + new_span->span_id_.high_ = 0; + new_span->span_id_.low_ = ++trace->seq_; + new_span->source_span_ = trace->last_active_span_; + new_span->is_follow_ = is_follow; + new_span->start_ts_ = get_current_time_us(); + new_span->end_ts_ = 0; + new_span->tags_ = NULL; + trace->last_active_span_ = new_span; + trace->current_span_list_ = list_add(trace->current_span_list_, span_elem); + } + } + } + return new_span; +} + +void end_span(ObTrace *trace, ObSpanCtx *span) +{ +#ifdef DEBUG_OB20 + printf("//////////////////////call end_span ////////////////////////////////\n"); +#endif + if (OB_ISNULL(trace) || OB_ISNULL(trace->flt)) { + // error + } else if ((trace->trace_id_.low_ == 0 && trace->trace_id_.high_ == 0) + || (span->span_id_.low_ == 0 && span->span_id_.high_ == 0)) { + // trace id or span id is not inited + } else { + FLTInfo *flt = trace->flt; + int64_t slow_query_threshold = flt->control_info_.slow_query_threshold_; + int64_t use_time; + RecordPolicy rp = flt->control_info_.rp_; + + span->end_ts_ = get_current_time_us(); + use_time = span->end_ts_ - span->start_ts_; + trace->last_active_span_ = span->source_span_; + // 当前驱动只有一个span,所以直接计算这个span的结束时间然后算慢查询 + // todo: 检查span的type,只有交互的span需要记录慢查询 + if (RP_ONLY_SLOW_QUERY == rp || RP_SAMPLE_AND_SLOW_QUERY == rp) { + if (slow_query_threshold != -1 && slow_query_threshold <= use_time) { + // 发现慢查询,只需要开启log打印,将这个span的log发送到server打印就行 +#ifdef DEBUG_OB20 + printf("get slow query, use_time is %ld, slow_query_threshold is %ld\n", use_time, slow_query_threshold); +#endif + trace->slow_query_print_ = TRUE; + } + } +#ifdef DEBUG_OB20 + printf("end span spanid:[%lu:%lu]\n", span->span_id_.high_, span->span_id_.low_); +#endif + } +} + +void reset_span(ObTrace *trace) +{ + if (OB_ISNULL(trace) || OB_ISNULL(trace->flt)) { + // error + } else if (trace->trace_id_.low_ == 0 && trace->trace_id_.high_ == 0) { + // trace is not inited + } else { + ObSpanCtx *span; + LIST *next; + LIST *span_elem = trace->current_span_list_; + while(OB_NOT_NULL(span_elem)) { + span = (ObSpanCtx *)span_elem->data; + next = span_elem->next; + if (0 != span->end_ts_) { + trace->current_span_list_ = list_delete(trace->current_span_list_, span_elem); + trace->free_span_list_ = list_add(trace->free_span_list_, span_elem); + } + span->tags_ = NULL; + span_elem = next; + } + } +} + +void append_tag(ObTrace *trace, ObSpanCtx *span, uint16_t tag_type, const char *str) +{ + if (OB_ISNULL(trace)) { + // do nothing + } else if (OB_ISNULL(str)) { + // do nothing + } else if (trace->offset_ + sizeof(ObTagCtx) > trace->buffer_size_) { + // do nothing + } else { + ObTagCtx *tag = (ObTagCtx *)(trace->data_ + trace->offset_); + tag->next_ = span->tags_; + span->tags_ = tag; + tag->tag_type_ = tag_type; + tag->data_ = str; + trace->offset_ += sizeof(ObTagCtx); + } +} + +int serialize_UUID(char *buf, const int64_t buf_len, int64_t *pos, UUID *uuid) +{ + int ret = OB_SUCCESS; + if (OB_FAIL(encode_i64(buf, buf_len, pos, uuid->high_))) { + // LOG_WARN("deserialize failed", K(ret), K(buf), K(buf_len), K(pos)); + } else if (OB_FAIL(encode_i64(buf, buf_len, pos, uuid->low_))) { + // LOG_WARN("deserialize failed", K(ret), K(buf), K(buf_len), K(pos)); + } + return ret; +} + +int deserialize_UUID(const char *buf, const int64_t buf_len, int64_t *pos, UUID *uuid) +{ + int ret = OB_SUCCESS; + if (OB_FAIL(decode_i64(buf, buf_len, pos, (int64_t *)(&uuid->high_)))) { + // LOG_WARN("deserialize failed", K(ret), K(buf), K(buf_len), K(pos)); + } else if (OB_FAIL(decode_i64(buf, buf_len, pos, (int64_t *)(&uuid->low_)))) { + // LOG_WARN("deserialize failed", K(ret), K(buf), K(buf_len), K(pos)); + } + return ret; +} + +DEFINE_TO_STRING_FUNC_FOR(UUID) +{ + int ret = OB_SUCCESS; + int64_t tmp_pos = *pos; + const char *transfer = "0123456789abcdef"; + if (tmp_pos + 37 > buf_len) { + ret = OB_ERROR; + } else { + buf[tmp_pos++] = transfer[src->high_ >> 60 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 56 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 52 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 48 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 44 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 40 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 36 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 32 & 0xf]; + buf[tmp_pos++] = '-'; + buf[tmp_pos++] = transfer[src->high_ >> 28 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 24 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 20 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 16 & 0xf]; + buf[tmp_pos++] = '-'; + buf[tmp_pos++] = transfer[src->high_ >> 12 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 8 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 4 & 0xf]; + buf[tmp_pos++] = transfer[src->high_ >> 0 & 0xf]; + buf[tmp_pos++] = '-'; + buf[tmp_pos++] = transfer[src->low_ >> 60 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 56 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 52 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 48 & 0xf]; + buf[tmp_pos++] = '-'; + buf[tmp_pos++] = transfer[src->low_ >> 44 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 40 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 36 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 32 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 28 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 24 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 20 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 16 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 12 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 8 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 4 & 0xf]; + buf[tmp_pos++] = transfer[src->low_ >> 0 & 0xf]; + buf[tmp_pos ] = '\0'; + } + + *pos = tmp_pos; + return ret; +} + +DEFINE_TO_STRING_FUNC_FOR(ObTagCtx) +{ + int ret = OB_SUCCESS; + char from[] = "\"\n\r\\"; + const char *to[] = { "\\\"", "\\n", "\\r", "\\\\"}; + int l = strlen(tag_str[src->tag_type_]); + int64_t tmp_pos = *pos; + const char *c = src->data_; + const char *toc; + my_bool conv = FALSE; + int i; + if (tmp_pos + l + 7 >= buf_len) { + buf[buf_len - 1] = '\0'; + ret = OB_ERROR; + } else { + buf[tmp_pos++] = '{'; + buf[tmp_pos++] = '\"'; + strcpy(buf + tmp_pos, tag_str[src->tag_type_]); + tmp_pos += l; + buf[tmp_pos++] = '\"'; + buf[tmp_pos++] = ':'; + buf[tmp_pos] = '\0'; + for (; *c; ++c) { + if (c != src->data_ && *(c + 1)) { + // ignore the first one and the last one + for (i = 0; i < 4; ++i) { + if (from[i] == *c) { + for (toc = to[i]; *toc; ++toc) { + if (tmp_pos < buf_len) { + buf[tmp_pos++] = *toc; + } else { + ret = OB_ERROR; + } + } + conv = TRUE; + break; + } + } + } + if (!conv && tmp_pos < buf_len) { + buf[tmp_pos++] = *c; + } + } + } + if (OB_FAIL(ret) || tmp_pos + 1 >= buf_len) { + buf[buf_len - 1] = 0; + } else { + buf[tmp_pos++] = '}'; + buf[tmp_pos] = 0; + } + if (OB_SUCC(ret)) { + *pos = tmp_pos; + } + return ret; +} + +// DEFINE_TO_STRING_FUNC_FOR(ObSpanCtx) +// { +// int ret = 0; +// char span_id[UUID4_LEN]; +// char source_span_id[UUID4_LEN] = {0}; +// int64_t span_id_pos = 0; +// int64_t source_span_id_pos = 0; +// if (OB_FAIL(tostring_UUID(span_id, UUID4_LEN, &span_id_pos, &src->span_id_))) { +// // error +// } else if (OB_NOT_NULL(src->source_span_id_) && (tostring_UUID(source_span_id, UUID4_LEN, &source_span_id_pos, src->source_span_id_))) { +// // error +// } else { +// ret = snprintf(buf + *pos, buf_len - *pos, +// "\"name\":\"%s\",\"id\":\"%s\",\"start_ts\":%ld,\"end_ts\":%ld,\"parent_id\":\"%s\",\"is_follow\":%s", +// "ObClient", +// span_id, +// src->start_ts_, +// src->end_ts_, +// source_span_id, +// src->is_follow_ ? "true" : "false"); +// if (ret < 0 || *pos + ret >= buf_len) { +// ret = OB_ERROR; +// } else { +// ObTagCtx* tag = src->tags_; +// *pos = *pos + ret; +// ret = OB_SUCCESS; +// while (OB_SUCC(ret) && OB_NOT_NULL(tag)) { +// ret = snprintf(buf + *pos, buf_len - *pos, ","); +// if (ret < 0 || *pos + ret >= buf_len) { +// ret = OB_ERROR; +// } else { +// *pos = *pos + ret; +// ret = tostring_ObTagCtx(buf, buf_len, pos, tag); +// tag = tag->next_; +// } +// } +// } +// } +// return ret; +// } + +int flt_set_module(MYSQL *mysql, const char *module_name) +{ + int ret = 0; + if (OB_ISNULL(mysql) || FALSE == get_use_full_link_trace(mysql)) { + // error + ret = 1; + } else if (OB_ISNULL(mysql->net.ob20protocol)) { + ret = 1; + } else { + FLTInfo *flt = mysql->net.ob20protocol->flt; + if (OB_ISNULL(flt)) { + ret = 1; + } else { + flt->app_info_.module_ = module_name; + } + } + return ret; +} + +int flt_set_action(MYSQL *mysql, const char *action_name) +{ + int ret = 0; + if (OB_ISNULL(mysql) || FALSE == get_use_full_link_trace(mysql)) { + // error + ret = 1; + } else if (OB_ISNULL(mysql->net.ob20protocol)) { + ret = 1; + } else { + FLTInfo *flt = mysql->net.ob20protocol->flt; + if (OB_ISNULL(flt)) { + ret = 1; + } else { + flt->app_info_.action_ = action_name; + } + } + return ret; +} + +int flt_set_client_info(MYSQL *mysql, const char *client_info) +{ + int ret = 0; + if (OB_ISNULL(mysql) || FALSE == get_use_full_link_trace(mysql)) { + // error + ret = 1; + } else if (OB_ISNULL(mysql->net.ob20protocol)) { + ret = 1; + } else { + FLTInfo *flt = mysql->net.ob20protocol->flt; + if (OB_ISNULL(flt)) { + ret = 1; + } else { + flt->app_info_.client_info_ = client_info; + } + } + return ret; +} + +int flt_set_identifier(MYSQL *mysql, const char *identifier) +{ + int ret = 0; + if (OB_ISNULL(mysql) || FALSE == get_use_full_link_trace(mysql)) { + // error + ret = 1; + } else if (OB_ISNULL(mysql->net.ob20protocol)) { + ret = 1; + } else { + FLTInfo *flt = mysql->net.ob20protocol->flt; + if (OB_ISNULL(flt)) { + ret = 1; + } else { + flt->app_info_.identifier_ = identifier; + } + } + return ret; +} + +int flt_get_control_level(MYSQL *mysql, int *level) +{ + int ret = 0; + if (OB_ISNULL(mysql) || FALSE == get_use_full_link_trace(mysql)) { + // error + ret = 1; + } else if (OB_ISNULL(mysql->net.ob20protocol)) { + ret = 1; + } else { + FLTInfo *flt = mysql->net.ob20protocol->flt; + if (OB_ISNULL(flt)) { + ret = 1; + } else { + *level = flt->control_info_.level_; + } + } + return ret; +} + +int flt_get_control_sample_pct(MYSQL *mysql, double *sample_pct) +{ + int ret = 0; + if (OB_ISNULL(mysql) || FALSE == get_use_full_link_trace(mysql)) { + // error + ret = 1; + } else if (OB_ISNULL(mysql->net.ob20protocol)) { + ret = 1; + } else { + FLTInfo *flt = mysql->net.ob20protocol->flt; + if (OB_ISNULL(flt)) { + ret = 1; + } else { + *sample_pct = flt->control_info_.sample_pct_; + } + } + return ret; +} + +int flt_get_control_record_policy(MYSQL *mysql, int *rp) +{ + int ret = 0; + if (OB_ISNULL(mysql) || FALSE == get_use_full_link_trace(mysql)) { + // error + ret = 1; + } else if (OB_ISNULL(mysql->net.ob20protocol)) { + ret = 1; + } else { + FLTInfo *flt = mysql->net.ob20protocol->flt; + if (OB_ISNULL(flt)) { + ret = 1; + } else { + *rp = flt->control_info_.rp_; + } + } + return ret; +} + +int flt_get_control_print_spct(MYSQL *mysql, double *sample_pct) +{ + int ret = 0; + if (OB_ISNULL(mysql) || FALSE == get_use_full_link_trace(mysql)) { + // error + ret = 1; + } else if (OB_ISNULL(mysql->net.ob20protocol)) { + ret = 1; + } else { + FLTInfo *flt = mysql->net.ob20protocol->flt; + if (OB_ISNULL(flt)) { + ret = 1; + } else { + *sample_pct = flt->control_info_.print_sample_pct_; + } + } + return ret; +} + +int flt_get_control_slow_threshold(MYSQL *mysql, long int *slow_threshold) +{ + int ret = 0; + if (OB_ISNULL(mysql) || FALSE == get_use_full_link_trace(mysql)) { + // error + ret = 1; + } else if (OB_ISNULL(mysql->net.ob20protocol)) { + ret = 1; + } else { + FLTInfo *flt = mysql->net.ob20protocol->flt; + if (OB_ISNULL(flt)) { + ret = 1; + } else { + *slow_threshold = flt->control_info_.slow_query_threshold_; + } + } + return ret; +} diff --git a/libmariadb/ob_object.c b/libmariadb/ob_object.c new file mode 100644 index 0000000..6bb7afb --- /dev/null +++ b/libmariadb/ob_object.c @@ -0,0 +1,524 @@ +#include +#include +#include "ob_serialize.h" + +static int get_map(ObObjType ob_type, const ObMySQLTypeMap **map) +{ + int ret = 0; + if (ob_type >= ObMaxType) { + ret = 1; + } + + if (OB_SUCC(ret)) { + *map = type_maps_ + ob_type; + } + + return ret; +} + +int get_mysql_type(ObObjType ob_type, enum_field_types *mysql_type) +{ + const ObMySQLTypeMap *map = NULL; + int ret = 0; + + if ((ret = get_map(ob_type, &map)) == 0) { + *mysql_type = map->mysql_type; + } + return ret; +} + +int get_ob_type(ObObjType *ob_type, enum_field_types mysql_type) +{ + int ret = 0; + switch (mysql_type) { + case MYSQL_TYPE_NULL: + *ob_type = ObNullType; + break; + case MYSQL_TYPE_TINY: + *ob_type = ObTinyIntType; + break; + case MYSQL_TYPE_SHORT: + *ob_type = ObSmallIntType; + break; + case MYSQL_TYPE_INT24: + *ob_type = ObMediumIntType; + break; + case MYSQL_TYPE_LONG: + *ob_type = ObInt32Type; + break; + case MYSQL_TYPE_LONGLONG: + *ob_type = ObIntType; + break; + case MYSQL_TYPE_FLOAT: + *ob_type = ObFloatType; + break; + case MYSQL_TYPE_DOUBLE: + *ob_type = ObDoubleType; + break; + case MYSQL_TYPE_TIMESTAMP: + *ob_type = ObTimestampType; + break; + case MYSQL_TYPE_DATETIME: + *ob_type = ObDateTimeType; + break; + case MYSQL_TYPE_TIME: + *ob_type = ObTimeType; + break; + case MYSQL_TYPE_DATE: + *ob_type = ObDateType; + break; + case MYSQL_TYPE_YEAR: + *ob_type = ObYearType; + break; + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + *ob_type = ObVarcharType; + break; + case MYSQL_TYPE_TINY_BLOB: + *ob_type = ObTinyTextType; + break; + case MYSQL_TYPE_BLOB: + *ob_type = ObTextType; + break; + case MYSQL_TYPE_MEDIUM_BLOB: + *ob_type = ObMediumTextType; + break; + case MYSQL_TYPE_LONG_BLOB: + *ob_type = ObLongTextType; + break; + case MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE: + *ob_type = ObTimestampTZType; + break; + case MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE: + *ob_type = ObTimestampLTZType; + break; + case MYSQL_TYPE_OB_TIMESTAMP_NANO: + *ob_type = ObTimestampNanoType; + break; + case MYSQL_TYPE_OB_RAW: + *ob_type = ObRawType; + break; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + *ob_type = ObNumberType; + break; + case MYSQL_TYPE_BIT: + *ob_type = ObBitType; + break; + case MYSQL_TYPE_ENUM: + *ob_type = ObEnumType; + break; + case MYSQL_TYPE_SET: + *ob_type = ObSetType; + break; + case MYSQL_TYPE_OBJECT: + *ob_type = ObExtendType; + break; + default: + ret = 1; + } + return ret; +} + +/* obj get/set function */ +OB_INLINE int8_t get_tinyint(const ObObj *obj) { return (int8_t)(obj->v_.int64_); } +OB_INLINE int16_t get_smallint(const ObObj *obj) { return (int16_t)(obj->v_.int64_); } +OB_INLINE int32_t get_mediumint(const ObObj *obj) { return (int32_t)(obj->v_.int64_); } +OB_INLINE int32_t get_int32(const ObObj *obj) { return (int32_t)(obj->v_.int64_); } +OB_INLINE int64_t get_int(const ObObj *obj) { return (int64_t)(obj->v_.int64_); } + +OB_INLINE uint8_t get_utinyint(const ObObj *obj) { return (uint8_t)(obj->v_.uint64_); } +OB_INLINE uint16_t get_usmallint(const ObObj *obj) { return (uint16_t)(obj->v_.uint64_); } +OB_INLINE uint32_t get_umediumint(const ObObj *obj) { return (uint32_t)(obj->v_.uint64_); } +OB_INLINE uint32_t get_uint32(const ObObj *obj) { return (uint32_t)(obj->v_.uint64_); } +OB_INLINE uint64_t get_uint64(const ObObj *obj) { return (uint64_t)(obj->v_.uint64_); } + +OB_INLINE float get_float(const ObObj *obj) { return obj->v_.float_; } +OB_INLINE double get_double(const ObObj *obj) { return obj->v_.double_; } +OB_INLINE float get_ufloat(const ObObj *obj) { return obj->v_.float_; } +OB_INLINE double get_udouble(const ObObj *obj) { return obj->v_.double_; } + +OB_INLINE ObObjType get_type(ObObjMeta *meta) { return (ObObjType)meta->type_; } + +OB_INLINE void set_null_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObNullType); meta->cs_level_ = CS_LEVEL_IGNORABLE; meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_tinyint_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObTinyIntType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_smallint_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObSmallIntType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_mediumint_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObMediumIntType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_int32_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObInt32Type); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_int_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObIntType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_utinyint_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObUTinyIntType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_usmallint_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObUSmallIntType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_umediumint_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObUMediumIntType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_uint32_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObUInt32Type); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_uint64_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObUInt64Type); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_float_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObFloatType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_double_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObDoubleType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_ufloat_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObUFloatType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_udouble_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObUDoubleType); meta->cs_level_ = CS_LEVEL_NUMERIC;meta->cs_type_ = CS_TYPE_BINARY; } +OB_INLINE void set_varchar_meta(ObObjMeta *meta) { meta->type_ = (uint8_t)(ObVarcharType); } + +inline void set_tinyint(ObObj *obj, const int8_t value) +{ + set_tinyint_meta(&obj->meta_); + obj->v_.int64_ = (int64_t)(value); +} + +inline void set_tinyint_value(ObObj *obj, const int8_t value) +{ + obj->v_.int64_ = (int64_t)(value); +} + +inline void set_smallint(ObObj *obj, const int16_t value) +{ + set_smallint_meta(&obj->meta_); + obj->v_.int64_ = (int64_t)(value); +} + +inline void set_smallint_value(ObObj *obj, const int16_t value) +{ + obj->v_.int64_ = (int64_t)(value); +} + +inline void set_mediumint(ObObj *obj, const int32_t value) +{ + set_mediumint_meta(&obj->meta_); + obj->v_.int64_ = (int64_t)(value); +} + +inline void set_int32(ObObj *obj, const int32_t value) +{ + set_int32_meta(&obj->meta_); + obj->v_.int64_ = (int64_t)(value); +} + +inline void set_int32_value(ObObj *obj, const int32_t value) +{ +// meta_.set_int32(); + obj->v_.int64_ = (int64_t)(value); +} + +inline void set_int(ObObj *obj, const int64_t value) +{ + set_int_meta(&obj->meta_); + obj->v_.int64_ = value; +} + +inline void set_int_value(ObObj *obj, const int64_t value) +{ + obj->v_.int64_ = value; +} + +inline void set_utinyint(ObObj *obj, const uint8_t value) +{ + set_utinyint_meta(&obj->meta_); + obj->v_.uint64_ = (uint64_t)(value); +} + +inline void set_usmallint(ObObj *obj, const uint16_t value) +{ + set_usmallint_meta(&obj->meta_); + obj->v_.uint64_ = (uint64_t)(value); +} + +inline void set_umediumint(ObObj *obj, const uint32_t value) +{ + set_umediumint_meta(&obj->meta_); + obj->v_.uint64_ = (uint64_t)(value); +} + +inline void set_uint32(ObObj *obj, const uint32_t value) +{ + set_uint32_meta(&obj->meta_); + obj->v_.uint64_ = (uint64_t)(value); +} + +inline void set_uint64(ObObj *obj, const uint64_t value) +{ + set_uint64_meta(&obj->meta_); + obj->v_.uint64_ = value; +} + +inline void set_float(ObObj *obj, const float value) +{ + set_float_meta(&obj->meta_); + obj->v_.float_ = value; +} + +inline void set_float_value(ObObj *obj, const float value) +{ +// meta_.set_float(); + obj->v_.float_ = value; +} + +inline void set_ufloat(ObObj *obj, const float value) +{ + set_ufloat_meta(&obj->meta_); + obj->v_.float_ = value; +} + +inline void set_double(ObObj *obj, const double value) +{ + set_double_meta(&obj->meta_); + obj->v_.double_ = value; +} + +inline void set_double_value(ObObj *obj, const double value) +{ + obj->v_.double_ = value; +} + +inline void set_udouble(ObObj *obj, const double value) +{ + set_udouble_meta(&obj->meta_); + obj->v_.double_ = value; +} + +inline void set_varchar(ObObj *obj, const char *ptr, int32_t size) +{ + set_varchar_meta(&obj->meta_); + obj->meta_.cs_level_ = CS_LEVEL_IMPLICIT; + obj->v_.string_ = ptr; + obj->val_len_ = size; +} +/* obj get/set function */ + +// general serialization functions generator +#define DEF_SERIALIZE_FUNCS(OBJTYPE, TYPE, VTYPE) \ + int obj_val_serialize_##OBJTYPE(const ObObj *obj, char *buf, const int64_t buf_len, int64_t *pos) \ + { \ + int ret = OB_SUCCESS; \ + OB_UNIS_ENCODE(get_##TYPE(obj), VTYPE); \ + return ret; \ + } \ + \ + int obj_val_deserialize_##OBJTYPE(ObObj *obj, const char *buf, const int64_t data_len, int64_t *pos) \ + { \ + int ret = OB_SUCCESS; \ + VTYPE v; \ + OB_UNIS_DECODE(v, VTYPE); \ + if (OB_SUCC(ret)) { \ + set_##TYPE(obj, v); \ + } \ + return ret; \ + } \ + \ + int64_t obj_val_get_serialize_size_##OBJTYPE(const ObObj *obj) \ + { \ + int64_t len = 0; \ + OB_UNIS_ADD_LEN(get_##TYPE(obj), VTYPE); \ + return len; \ + } + +// general generator for numeric types +#define DEF_NUMERIC_FUNCS(OBJTYPE, TYPE, VTYPE, SQL_FORMAT, STR_FORMAT, HTYPE) \ + DEF_SERIALIZE_FUNCS(OBJTYPE, TYPE, VTYPE) + +// ObTinyIntType=1, // int8, aka mysql boolean type +DEF_NUMERIC_FUNCS(ObTinyIntType, tinyint, int8_t, "%hhd", "'%hhd'", int64_t); +// ObSmallIntType=2, // int16 +DEF_NUMERIC_FUNCS(ObSmallIntType, smallint, int16_t, "%hd", "'%hd'", int64_t); +// ObMediumIntType=3, // int24 +DEF_NUMERIC_FUNCS(ObMediumIntType, mediumint, int32_t, "%d", "'%d'", int64_t); +// ObInt32Type=4, // int32 +DEF_NUMERIC_FUNCS(ObInt32Type, int32, int32_t, "%d", "'%d'", int64_t); +// ObIntType=5, // int64, aka bigint +DEF_NUMERIC_FUNCS(ObIntType, int, int64_t, "%ld", "'%ld'", int64_t); +// ObUTinyIntType=6, // uint8 +DEF_NUMERIC_FUNCS(ObUTinyIntType, utinyint, uint8_t, "%hhu", "'%hhu'", uint64_t); +// ObUSmallIntType=7, // uint16 +DEF_NUMERIC_FUNCS(ObUSmallIntType, usmallint, uint16_t, "%hu", "'%hu'", uint64_t); +// ObUMediumIntType=8, // uint24 +DEF_NUMERIC_FUNCS(ObUMediumIntType, umediumint, uint32_t, "%u", "'%u'", uint64_t); +// ObUInt32Type=9, // uint32 +DEF_NUMERIC_FUNCS(ObUInt32Type, uint32, uint32_t, "%u", "'%u'", uint64_t); +// ObUInt64Type=10, // uint64 +DEF_NUMERIC_FUNCS(ObUInt64Type, uint64, uint64_t, "%lu", "'%lu'", uint64_t); +// ObFloatType=11, // single-precision floating point +DEF_NUMERIC_FUNCS(ObFloatType, float, float, "%2f", "'%2f'", double); +// ObDoubleType=12, // double-precision floating point +DEF_NUMERIC_FUNCS(ObDoubleType, double, double, "%2lf", "'%2lf'", double); +// ObUFloatType=13, // unsigned single-precision floating point +DEF_NUMERIC_FUNCS(ObUFloatType, ufloat, float, "%2f", "'%2f'", double); +// ObUDoubleType=14, // unsigned double-precision floating point +DEF_NUMERIC_FUNCS(ObUDoubleType, udouble, double, "%2lf", "'%2lf'", double); + +// for obstring +int obj_val_serialize_ObVarcharType(const ObObj *obj, char *buf, const int64_t buf_len, int64_t *pos) +{ + int ret = OB_SUCCESS; + encode_vstr_with_len(buf, buf_len, pos, obj->v_.string_, obj->val_len_); + return ret; +} + +int obj_val_deserialize_ObVarcharType(ObObj *obj, const char *buf, const int64_t data_len, int64_t *pos) +{ + int ret = OB_SUCCESS; + int64_t len = 0; + const int64_t MINIMAL_NEEDED_SIZE = 2; //at least need two bytes + if (OB_ISNULL(buf) || OB_UNLIKELY((data_len - *pos) < MINIMAL_NEEDED_SIZE)) { + ret = OB_ERROR; + } else { + obj->v_.string_ = (char *)(decode_vstr_nocopy(buf, data_len, pos, &len)); + if (OB_ISNULL(obj->v_.string_)) { + ret = OB_ERROR; + } else { + obj->val_len_ = len; + } + } + return ret; +} + +int64_t obj_val_get_serialize_size_ObVarcharType(const ObObj *obj) +{ + return encoded_length_vstr_with_len(obj->val_len_); +} + +// for null type +int obj_val_serialize_ObNullType(const ObObj *obj, char *buf, const int64_t buf_len, int64_t *pos) +{ + int ret = OB_ERROR; + UNUSED(obj); + UNUSED(buf); + UNUSED(buf_len); + UNUSED(pos); + return ret; +} + +int obj_val_deserialize_ObNullType(ObObj *obj, const char *buf, const int64_t data_len, int64_t *pos) +{ + int ret = OB_ERROR; + UNUSED(obj); + UNUSED(buf); + UNUSED(data_len); + UNUSED(pos); + return ret; +} + +int64_t obj_val_get_serialize_size_ObNullType(const ObObj *obj) +{ + UNUSED(obj); + return 0; +} + +int serialize_ObObjMeta(const ObObjMeta *objmeta, char *buf, const int64_t buf_len, int64_t *pos) +{ + int ret = OB_SUCCESS; + encode_uint8_t(buf, buf_len, pos, objmeta->type_); + encode_uint8_t(buf, buf_len, pos, objmeta->cs_level_); + encode_uint8_t(buf, buf_len, pos, objmeta->cs_type_); + encode_int8_t(buf, buf_len, pos, objmeta->scale_); + return ret; +} +int deserialize_ObObjMeta(ObObjMeta *objmeta,const char *buf, const int64_t data_len, int64_t *pos) +{ + int ret = OB_SUCCESS; + decode_uint8_t(buf, data_len, pos, &objmeta->type_); + decode_uint8_t(buf, data_len, pos, &objmeta->cs_level_); + decode_uint8_t(buf, data_len, pos, &objmeta->cs_type_); + decode_int8_t(buf, data_len, pos, &objmeta->scale_); + return ret; +} + +int64_t get_serialize_size_ObObjMeta(const ObObjMeta *objmeta) +{ + int64_t len = 0; + len += encoded_length_uint8_t(objmeta->type_); + len += encoded_length_uint8_t(objmeta->cs_level_); + len += encoded_length_uint8_t(objmeta->cs_type_); + len += encoded_length_int8_t(objmeta->scale_); + return len; +} + +#define DEF_FUNC_ENTRY(OBJTYPE) \ + { \ + obj_val_serialize_##OBJTYPE, \ + obj_val_deserialize_##OBJTYPE, \ + obj_val_get_serialize_size_##OBJTYPE, \ + } + +ObObjTypeFuncs OBJ_FUNCS[ObMaxType] = +{ + DEF_FUNC_ENTRY(ObNullType), // 0 + DEF_FUNC_ENTRY(ObTinyIntType), // 1 + DEF_FUNC_ENTRY(ObSmallIntType), // 2 + DEF_FUNC_ENTRY(ObMediumIntType), // 3 + DEF_FUNC_ENTRY(ObInt32Type), // 4 + DEF_FUNC_ENTRY(ObIntType), // 5 + DEF_FUNC_ENTRY(ObUTinyIntType), // 6 + DEF_FUNC_ENTRY(ObUSmallIntType), // 7 + DEF_FUNC_ENTRY(ObUMediumIntType), // 8 + DEF_FUNC_ENTRY(ObUInt32Type), // 9 + DEF_FUNC_ENTRY(ObUInt64Type), // 10 + DEF_FUNC_ENTRY(ObFloatType), // 11 + DEF_FUNC_ENTRY(ObDoubleType), // 12 + DEF_FUNC_ENTRY(ObUFloatType), // 13 + DEF_FUNC_ENTRY(ObUDoubleType), // 14 + DEF_FUNC_ENTRY(ObNullType), // 15 + DEF_FUNC_ENTRY(ObNullType), // 16: unumber is the same as number + DEF_FUNC_ENTRY(ObNullType), // 17 + DEF_FUNC_ENTRY(ObNullType), // 18 + DEF_FUNC_ENTRY(ObNullType), // 19 + DEF_FUNC_ENTRY(ObNullType), // 20 + DEF_FUNC_ENTRY(ObNullType), // 21 + DEF_FUNC_ENTRY(ObVarcharType), // 22, varchar + DEF_FUNC_ENTRY(ObNullType), // 23, char + DEF_FUNC_ENTRY(ObNullType), // 24, hex_string + DEF_FUNC_ENTRY(ObNullType), // 25, ext + DEF_FUNC_ENTRY(ObNullType), // 26, unknown + DEF_FUNC_ENTRY(ObNullType), // 27 + DEF_FUNC_ENTRY(ObNullType), // 28 + DEF_FUNC_ENTRY(ObNullType), // 29 + DEF_FUNC_ENTRY(ObNullType), // 30 + DEF_FUNC_ENTRY(ObNullType), // 31 + DEF_FUNC_ENTRY(ObNullType), // 32 + DEF_FUNC_ENTRY(ObNullType), // 33 + DEF_FUNC_ENTRY(ObNullType), // 34 + DEF_FUNC_ENTRY(ObNullType), // 35 + DEF_FUNC_ENTRY(ObNullType), // 36, timestamp with time zone + DEF_FUNC_ENTRY(ObNullType), // 37, timestamp with local time zone + DEF_FUNC_ENTRY(ObNullType), // 38, timestamp (9) + DEF_FUNC_ENTRY(ObNullType), // 39, raw + DEF_FUNC_ENTRY(ObNullType), // 40 + DEF_FUNC_ENTRY(ObNullType), // 41 + DEF_FUNC_ENTRY(ObNullType), // 42 + DEF_FUNC_ENTRY(ObNullType), // 43, nvarchar2 + DEF_FUNC_ENTRY(ObNullType), // 44, nchar +}; + +int serialize_ObObj(const ObObj *obj, char *buf, const int64_t buf_len, int64_t *pos) +{ + int ret = OB_SUCCESS; + serialize_ObObjMeta(&obj->meta_, buf, buf_len, pos); + if (OB_SUCC(ret)) { + if (obj->meta_.type_ <= ObMaxType) { + ret = OBJ_FUNCS[get_type((ObObjMeta *)&obj->meta_)].serialize(obj, buf, buf_len, pos); + } else { + ret = OB_ERROR; + } + } + return ret; +} + +int deserialize_ObObj(ObObj *obj, const char *buf, const int64_t data_len, int64_t *pos) +{ + int ret = OB_SUCCESS; + deserialize_ObObjMeta(&obj->meta_, buf, data_len, pos); + if (OB_SUCC(ret)) { + if (obj->meta_.type_ <= ObMaxType) { + ret = OBJ_FUNCS[get_type((ObObjMeta *)&obj->meta_)].deserialize(obj, buf, data_len, pos); + } else { + ret = OB_ERROR; + } + } + return ret; +} + +int64_t get_serialize_size_ObObj(const ObObj *obj) +{ + int64_t len = 0; + len += get_serialize_size_ObObjMeta(&obj->meta_); + len += OBJ_FUNCS[get_type((ObObjMeta *)&obj->meta_)].get_serialize_size(obj); + return len; +} +/* obj serialize/deserialize function */ diff --git a/libmariadb/ob_oracle_format_models.c b/libmariadb/ob_oracle_format_models.c new file mode 100644 index 0000000..931e7e2 --- /dev/null +++ b/libmariadb/ob_oracle_format_models.c @@ -0,0 +1,2512 @@ +/* + Copyright (c) 2000, 2018, Oracle and/or its affiliates. + Copyright (c) 2009, 2019, MariaDB Corporation. + Copyright (c) 2021 OceanBase Technology Co.,Ltd. + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ +#include +#include +#include "ob_oralce_format_models.h" +#include "mariadb_ctype.h" +#include +#include "ob_bitmap.h" + +int32_t calc_max_name_length(const struct ObTimeConstStr names[], const int64_t size); + +#define FALSE_IT(stmt) ({ (stmt); FALSE; }) + +#define INT32_MAX_DIGITS_LEN 10 +#define EPOCH_YEAR4 1970 +#define EPOCH_WDAY 4 // 1970-1-1 is thursday. +#define LEAP_YEAR_COUNT(y) ((y) / 4 - (y) / 100 + (y) / 400) +#define IS_LEAP_YEAR(y) ((((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) ? 1 : 0) + +#define DAYS_PER_NYEAR 365 +#define DAYS_PER_LYEAR 366 + +static const int32_t MIN_OFFSET_MINUTES = -15 * 60 - 59; +static const int32_t MAX_OFFSET_MINUTES = 15 * 60 + 59; +static const int32_t ZERO_DATE = (int32_t)(-106751991); +static const int64_t ZERO_TIME = 0; +static const int64_t MAX_VARCHAR_LENGTH = 1024L * 1024L; // unit is byte +static const int64_t COMMON_ELEMENT_NUMBER = 10; +static const int16_t MAX_SCALE_FOR_ORACLE_TEMPORAL = 9; +static const int16_t DEFAULT_SCALE_FOR_ORACLE_FRACTIONAL_SECONDS = 6; +static const int64_t UNKNOWN_LENGTH_OF_ELEMENT = 20; + +static const int64_t power_of_10[INT32_MAX_DIGITS_LEN] = { + 1L, + 10L, + 100L, + 1000L, + 10000L, + 100000L, + 1000000L, + 10000000L, + 100000000L, + 1000000000L, + //2147483647 +}; + +const int64_t TZ_PART_MIN[DATETIME_PART_CNT] = { 1, 1, 1, 0, 0, 0, 0}; +const int64_t TZ_PART_MAX[DATETIME_PART_CNT] = {9999, 12, 31, 23, 59, 59, 1000000000}; + +static const int8_t DAYS_PER_MON[2][12 + 1] = { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +}; + +static const int32_t DAYS_UNTIL_MON[2][12 + 1] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} +}; + +/** + * 3 days after wday 5 is wday 1, so [3][5] = 1. + * 5 days before wday 2 is wday 4, so [-5][2] = 4. + * and so on, max wday is 7, min offset is -6, max offset days is 6. + */ +static const int8_t WDAY_OFFSET_ARR[DAYS_PER_WEEK * 2 - 1][DAYS_PER_WEEK + 1] = { + {0, 2, 3, 4, 5, 6, 7, 1}, + {0, 3, 4, 5, 6, 7, 1, 2}, + {0, 4, 5, 6, 7, 1, 2, 3}, + {0, 5, 6, 7, 1, 2, 3, 4}, + {0, 6, 7, 1, 2, 3, 4, 5}, + {0, 7, 1, 2, 3, 4, 5, 6}, // offset = -1, wday = 1/2/3/4/5/6/7. + {0, 1, 2, 3, 4, 5, 6, 7}, // offset = 0, wday = 1/2/3/4/5/6/7. + {0, 2, 3, 4, 5, 6, 7, 1}, // offset = 1, wday = 1/2/3/4/5/6/7. + {0, 3, 4, 5, 6, 7, 1, 2}, + {0, 4, 5, 6, 7, 1, 2, 3}, + {0, 5, 6, 7, 1, 2, 3, 4}, + {0, 6, 7, 1, 2, 3, 4, 5}, + {0, 7, 1, 2, 3, 4, 5, 6} +}; + +static const int8_t (*WDAY_OFFSET)[DAYS_PER_WEEK + 1] = &WDAY_OFFSET_ARR[6]; + +/* + * if wday of yday 1 is 4, not SUN_BEGIN, not GE_4_BEGIN, yday of fitst day of week 1 is 5, so [4][0][0] = 5. + * if wday of yday 1 is 2, SUN_BEGIN, GE_4_BEGIN, yday of fitst day of week 1 is 5, so [2][1][1] = 1. + * and so on, max wday is 7, and other two is 1. + * ps: if the first week is not full week(GE_4_BEGIN), the yday maybe zero or neg, such as 0 means + * the last day of prev year, and so on. + */ +static const int8_t YDAY_WEEK1[DAYS_PER_WEEK + 1][2][2] = { + {{0, 0}, {0, 0}}, + {{1, 1}, {7, 0}}, // wday of day 1 is 1. + {{7, 0}, {6, -1}}, // 2. + {{6, -1}, {5, -2}}, // 3. + {{5, -2}, {4, 4}}, // 4. + {{4, 4}, {3, 3}}, // 5. + {{3, 3}, {2, 2}}, // 6. + {{2, 2}, {1, 1}} // 7. +}; + +#define WEEK_MODE_CNT 8 +static const ObDTMode WEEK_MODE[WEEK_MODE_CNT] = { + DT_WEEK_SUN_BEGIN | DT_WEEK_ZERO_BEGIN , + DT_WEEK_ZERO_BEGIN | DT_WEEK_GE_4_BEGIN , + DT_WEEK_SUN_BEGIN , + DT_WEEK_GE_4_BEGIN , //ISO-8601 standard week + DT_WEEK_SUN_BEGIN | DT_WEEK_ZERO_BEGIN | DT_WEEK_GE_4_BEGIN , + DT_WEEK_ZERO_BEGIN , + DT_WEEK_SUN_BEGIN | DT_WEEK_GE_4_BEGIN , + 0 +}; + +static const int32_t DAYS_PER_YEAR[2]= +{ + DAYS_PER_NYEAR, DAYS_PER_LYEAR +}; + +static const struct ObTimeConstStr WDAY_NAMES[DAYS_PER_WEEK + 1] = { + {"null", 4}, + {"Monday", 6}, {"Tuesday", 7}, {"Wednesday", 9}, {"Thursday", 8}, {"Friday", 6}, {"Saturday", 8}, {"Sunday", 6} +}; + +static const int32_t MAX_WDAY_NAME_LENGTH = 9; + +static const struct ObTimeConstStr WDAY_ABBR_NAMES[DAYS_PER_WEEK + 1] = { + {"null", 4}, + {"Mon", 3}, {"Tue", 3}, {"Wed", 3}, {"Thu", 3}, {"Fri", 3}, {"Sat", 3}, {"Sun", 3} +}; +static const struct ObTimeConstStr MON_NAMES[12 + 1] = { + {"null", 4}, + {"January", 7}, {"February", 8}, {"March", 5}, {"April", 5}, {"May", 3}, {"June", 4}, + {"July", 4}, {"August", 6}, {"September", 9}, {"October", 7}, {"November", 8}, {"December", 8} +}; + +static const int32_t MAX_MON_NAME_LENGTH = 9; + +static const struct ObTimeConstStr MON_ABBR_NAMES[12 + 1] = { + {"null", 4}, + {"Jan", 3}, {"Feb", 3}, {"Mar", 3}, {"Apr", 3}, {"May", 3}, {"Jun", 3}, + {"Jul", 3}, {"Aug", 3}, {"Sep", 3}, {"Oct", 3}, {"Nov", 3}, {"Dec", 3} +}; + +static int64_t CONFLICT_GROUP_MAP[MAX_FLAG_NUMBER] = +{ + /**AD*/ ERA_GROUP, + /**AD2*/ ERA_GROUP, + /**BC*/ ERA_GROUP, + /**BC2*/ ERA_GROUP, + /**CC,*/ NEVER_APPEAR_GROUP, + /**SCC*/ NEVER_APPEAR_GROUP, + /**D*/ WEEK_OF_DAY_GROUP, + /**DAY*/ WEEK_OF_DAY_GROUP, + /**DD*/ NON_CONFLICT_GROUP, + /**DDD*/ DAY_OF_YEAR_GROUP, + /**DY*/ WEEK_OF_DAY_GROUP, + /**FF1*/ NON_CONFLICT_GROUP, + /**FF2*/ NON_CONFLICT_GROUP, + /**FF3*/ NON_CONFLICT_GROUP, + /**FF4*/ NON_CONFLICT_GROUP, + /**FF5*/ NON_CONFLICT_GROUP, + /**FF6*/ NON_CONFLICT_GROUP, + /**FF7*/ NON_CONFLICT_GROUP, + /**FF8*/ NON_CONFLICT_GROUP, + /**FF9*/ NON_CONFLICT_GROUP, + /**FF*/ NON_CONFLICT_GROUP, + /**HH*/ HOUR_GROUP, + /**HH24*/ HOUR_GROUP, + /**HH12*/ HOUR_GROUP, + /**IW*/ NEVER_APPEAR_GROUP, + /**I*/ NEVER_APPEAR_GROUP, + /**IY*/ NEVER_APPEAR_GROUP, + /**IYY*/ NEVER_APPEAR_GROUP, + /**IYYY*/ NEVER_APPEAR_GROUP, + /**MI*/ NON_CONFLICT_GROUP, + /**MM*/ MONTH_GROUP, + /**MONTH*/ MONTH_GROUP, + /**MON*/ MONTH_GROUP, + /**AM*/ MERIDIAN_INDICATOR_GROUP, + /**AM2*/ MERIDIAN_INDICATOR_GROUP, + /**PM*/ MERIDIAN_INDICATOR_GROUP, + /**PM2*/ MERIDIAN_INDICATOR_GROUP, + /**Q*/ NON_CONFLICT_GROUP, + /**RR*/ YEAR_GROUP, + /**RRRR*/ YEAR_GROUP, + /**SS*/ NON_CONFLICT_GROUP, + /**SSSSS*/ RUNTIME_CONFLICT_SOLVE_GROUP, + /**WW*/ NEVER_APPEAR_GROUP, + /**W*/ NEVER_APPEAR_GROUP, + /**YGYYY*/ YEAR_GROUP, + /**YEAR*/ NEVER_APPEAR_GROUP, + /**SYEAR*/ NEVER_APPEAR_GROUP, + /**YYYY*/ YEAR_GROUP, + /**SYYYY*/ YEAR_GROUP, + /**YYY*/ YEAR_GROUP, + /**YY*/ YEAR_GROUP, + /**Y*/ YEAR_GROUP, + /**DS*/ RUNTIME_CONFLICT_SOLVE_GROUP, + /**DL*/ RUNTIME_CONFLICT_SOLVE_GROUP, + /**TZH*/ RUNTIME_CONFLICT_SOLVE_GROUP, + /**TZM*/ RUNTIME_CONFLICT_SOLVE_GROUP, + /**TZD*/ RUNTIME_CONFLICT_SOLVE_GROUP, + /**TZR*/ RUNTIME_CONFLICT_SOLVE_GROUP, + /**X*/ NON_CONFLICT_GROUP, + /**J*/ DAY_OF_YEAR_GROUP +}; + +//define the length ,may be less ,but should not max than this +static int64_t EXPECTED_MATCHING_LENGTH[MAX_FLAG_NUMBER] = +{ + /**AD*/ 2, + /**AD2*/ 4, + /**BC*/ 2, + /**BC2*/ 4, + /**CC,*/ 0, //never used + /**SCC*/ 0, //never used + /**D*/ 1, + /**DAY*/ 0, //non-numeric, ignored + /**DD*/ 2, + /**DDD*/ 3, + /**DY*/ 0, //non-numeric, ignored + /**FF1*/ 1, + /**FF2*/ 2, + /**FF3*/ 3, + /**FF4*/ 4, + /**FF5*/ 5, + /**FF6*/ 6, + /**FF7*/ 7, + /**FF8*/ 8, + /**FF9*/ 9, + /**FF*/ 9, + /**HH*/ 2, + /**HH24*/ 2, + /**HH12*/ 2, + /**IW*/ 0, //never used + /**I*/ 0, //never used + /**IY*/ 0, //never used + /**IYY*/ 0, //never used + /**IYYY*/ 0, //never used + /**MI*/ 2, + /**MM*/ 2, + /**MONTH*/ 0, //non-numeric, ignored + /**MON*/ 0, //non-numeric, ignored + /**AM*/ 2, + /**AM2*/ 4, + /**PM*/ 2, + /**PM2*/ 4, + /**Q*/ 0, //never used + /**RR*/ 0, //special case + /**RRRR*/ 0, //special case + /**SS*/ 2, + /**SSSSS*/ 5, + /**WW*/ 0, //never used + /**W*/ 0, //never used + /**YGYYY*/ 5, + /**YEAR*/ 0, //never used + /**SYEAR*/ 0, //never used + /**YYYY*/ 4, + /**SYYYY*/ 4, + /**YYY*/ 3, + /**YY*/ 2, + /**Y*/ 1, + /**DS*/ 0, //todo + /**DL*/ 0, //todo + /**TZH*/ 2, //todo + /**TZM*/ 2, //todo + /**TZD*/ 0, //non-numeric, ignored + /**TZR*/ 0, //non-numeric, ignored + /**X*/ 0, //non-numeric, ignored + /**J*/ 7 +}; + +int32_t calc_max_name_length(const struct ObTimeConstStr names[], const int64_t size) +{ + int32_t res = 0; + int64_t i; + for (i = 1; i <= size; ++i) { + if (res < names[i].len_) { + res = names[i].len_; + } + } + return res; +} + +int databuff_vprintf(char *buf, const int64_t buf_len, int64_t *pos, const char *fmt, va_list args) +{ + int ret = 0; + if (NULL != buf && 0 <= *pos && *pos < buf_len) { + int len = vsnprintf(buf + *pos, buf_len - *pos, fmt, args); + if (len < 0) { + ret = 1; + } else if (len < buf_len - *pos) { + *pos += len; + } else { + *pos = buf_len - 1; //skip '\0' written by vsnprintf + ret = 1; + } + } else { + ret = 1; + } + return ret; +} + +int databuff_printf(char *buf, const int64_t buf_len, int64_t *pos, const char *fmt, ...) +{ + int ret = 0; + va_list args; + va_start(args, fmt); + if (0 != (ret = databuff_vprintf(buf, buf_len, pos, fmt, args))) { + } else {} + va_end(args); + return ret; +} + +//PATTERN in windows(wingdi.h)Duplicate definition,So change PATTERN to PATTERNEX +//do not define duplicate pattern +const struct ObTimeConstStr PATTERNEX[MAX_FLAG_NUMBER] = +{ +#define ObTimeConstStr(x) {x, sizeof(x) - 1} + ObTimeConstStr("AD"), + ObTimeConstStr("A.D."), + ObTimeConstStr("BC"), + ObTimeConstStr("B.C."), + ObTimeConstStr("CC"), + ObTimeConstStr("SCC"), + ObTimeConstStr("D"), + ObTimeConstStr("DAY"), + ObTimeConstStr("DD"), + ObTimeConstStr("DDD"), + ObTimeConstStr("DY"), + ObTimeConstStr("FF1"), + ObTimeConstStr("FF2"), + ObTimeConstStr("FF3"), + ObTimeConstStr("FF4"), + ObTimeConstStr("FF5"), + ObTimeConstStr("FF6"), + ObTimeConstStr("FF7"), + ObTimeConstStr("FF8"), + ObTimeConstStr("FF9"), + ObTimeConstStr("FF"), + ObTimeConstStr("HH"), + ObTimeConstStr("HH24"), + ObTimeConstStr("HH12"), + ObTimeConstStr("IW"), + ObTimeConstStr("I"), + ObTimeConstStr("IY"), + ObTimeConstStr("IYY"), + ObTimeConstStr("IYYY"), + ObTimeConstStr("MI"), + ObTimeConstStr("MM"), + ObTimeConstStr("MONTH"), + ObTimeConstStr("MON"), + ObTimeConstStr("AM"), + ObTimeConstStr("A.M."), + ObTimeConstStr("PM"), + ObTimeConstStr("P.M."), + ObTimeConstStr("Q"), + ObTimeConstStr("RR"), + ObTimeConstStr("RRRR"), + ObTimeConstStr("SS"), + ObTimeConstStr("SSSSS"), + ObTimeConstStr("WW"), + ObTimeConstStr("W"), + ObTimeConstStr("Y,YYY"), + ObTimeConstStr("YEAR"), + ObTimeConstStr("SYEAR"), + ObTimeConstStr("YYYY"), + ObTimeConstStr("SYYYY"), + ObTimeConstStr("YYY"), + ObTimeConstStr("YY"), + ObTimeConstStr("Y"), + ObTimeConstStr("DS"), + ObTimeConstStr("DL"), + ObTimeConstStr("TZH"), + ObTimeConstStr("TZM"), + ObTimeConstStr("TZD"), + ObTimeConstStr("TZR"), + ObTimeConstStr("X"), +#undef ObTimeConstStr +}; + +static const struct ObOracleTimeLimiter LIMITER_YEAR = {1, 9999, 1}; +static const struct ObOracleTimeLimiter LIMITER_MONTH = {1, 12, 1}; +static const struct ObOracleTimeLimiter LIMITER_MONTH_DAY = {1, 31, 1}; +static const struct ObOracleTimeLimiter LIMITER_WEEK_DAY = {1, 7, 1}; +static const struct ObOracleTimeLimiter LIMITER_YEAR_DAY = {1, 366, 1}; +static const struct ObOracleTimeLimiter LIMITER_HOUR12 = {1, 12, 1}; +static const struct ObOracleTimeLimiter LIMITER_HOUR24 = {0, 23, 1}; +static const struct ObOracleTimeLimiter LIMITER_MINUTE = {0, 59, 1}; +static const struct ObOracleTimeLimiter LIMITER_SECOND = {0, 59, 1}; +static const struct ObOracleTimeLimiter LIMITER_SECS_PAST_MIDNIGHT = {0, 86399, 1}; +static const struct ObOracleTimeLimiter LIMITER_TIMEZONE_HOUR_ABS = {0, 15, 1}; //ORA-01874: time zone hour must be between -15 and 15 +static const struct ObOracleTimeLimiter LIMITER_TIMEZONE_MIN_ABS = {0, 59, 1}; //ORA-01875: time zone minute must be between -59 and 59 +static const struct ObOracleTimeLimiter LIMITER_JULIAN_DATE = {1, 5373484,1}; // -4712-01-01 ~ 9999-12-31 + +static inline int check_validate(const struct ObOracleTimeLimiter *limiter, int32_t value) +{ + int ret = 0; + if (value < limiter->MIN || value > limiter->MAX) { + ret = limiter->ERROR_CODE; + } + return ret; +} + +int get_day_and_month_from_year_day(const int32_t yday, const int32_t year, int32_t *month, int32_t *day) +{ + int ret = 0; + int32_t leap_year = IS_LEAP_YEAR(year); + if (yday > DAYS_UNTIL_MON[leap_year][12]) { + ret = 1; + } else { + my_bool stop_flag = FALSE; + int32_t i = LIMITER_MONTH.MIN; + for (; !stop_flag && i <= LIMITER_MONTH.MAX; ++i) { + if (yday <= DAYS_UNTIL_MON[leap_year][i]) { + *month = i; + *day = yday - DAYS_UNTIL_MON[leap_year][i - 1]; + stop_flag = TRUE; + } + } + } + return ret; +} + +int validate_oracle_date(const struct ObTime *ob_time) +{ + const int32_t *parts = ob_time->parts_; + int ret = 0; + int i = 0; + for (; 0 == ret && i < ORACLE_DATE_PART_CNT; ++i) { + if (parts[i] < TZ_PART_MIN[i] || parts[i] > TZ_PART_MAX[i]) { + ret = 1; + } + } + if (0 == ret) { + int is_leap = IS_LEAP_YEAR(parts[DT_YEAR]); + if (parts[DT_MDAY] > DAYS_PER_MON[is_leap][parts[DT_MON]]) { + ret = 1; + } + } + return ret; +} + +/* + * if format elements contains TZR + * hour minuts seconds and fracial second can not omit + * Because, I guess, the daylight-saving time may be uncertain + * if the time part is omitted. + * The day + */ +inline my_bool is_element_can_omit(const struct ObDFMElem *elem) +{ + int ret_bool = TRUE; + int64_t flag = elem->elem_flag_; + int64_t conf_group = CONFLICT_GROUP_MAP[flag]; + if (YEAR_GROUP == conf_group + || WEEK_OF_DAY_GROUP == conf_group + || MONTH_GROUP == conf_group + || DD == flag + || DS == flag + || DL == flag) { + ret_bool = FALSE; + } else { + //return true + } + return ret_bool; +} + +static inline my_bool is_sign_char(const char ch) { + return '-' == ch || '+' == ch; +} + +static inline my_bool is_split_char(const char ch) +{ + int ret_bool = FALSE; + if (ch == '\n' || ch == '\t' + || ((ch >= 0x20 && ch <= 0x7E) && + !((ch >= '0' && ch <= '9') + || (ch >='a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z')))) { + ret_bool = TRUE; + } + return ret_bool; +} + +static int date_to_ob_time(int32_t value, struct ObTime *ob_time) +{ + int ret = 0; + int32_t *parts = ob_time->parts_; + if (!HAS_TYPE_ORACLE(ob_time->mode_) && ZERO_DATE == value) { + memset(parts, 0, sizeof(*parts) * DATETIME_PART_CNT); + parts[DT_DATE] = ZERO_DATE; + } else { + int32_t days = value; + int32_t leap_year = 0; + int32_t year = EPOCH_YEAR4; + const int32_t *cur_days_until_mon = NULL; + int32_t month = 1; + parts[DT_DATE] = value; + // year. + while (days < 0 || days >= DAYS_PER_YEAR[leap_year = IS_LEAP_YEAR(year)]) { + int32_t new_year = year + days / DAYS_PER_NYEAR; + new_year -= (days < 0); + days -= (new_year - year) * DAYS_PER_NYEAR + LEAP_YEAR_COUNT(new_year - 1) - LEAP_YEAR_COUNT(year - 1); + year = new_year; + } + parts[DT_YEAR] = year; + parts[DT_YDAY] = days + 1; + parts[DT_WDAY] = WDAY_OFFSET[value % DAYS_PER_WEEK][EPOCH_WDAY]; + // month. + cur_days_until_mon = DAYS_UNTIL_MON[leap_year]; + for (; month < MONS_PER_YEAR && days >= cur_days_until_mon[month]; ++month) {} + parts[DT_MON] = month; + days -= cur_days_until_mon[month - 1]; + // day. + parts[DT_MDAY] = days + 1; + } + return ret; +} + +int set_ob_time_part_directly(struct ObTime *ob_time, int64_t *conflict_bitset, const int64_t part_offset, const int32_t part_value) +{ + int ret = 0; + if (part_offset >= TOTAL_PART_CNT) { + ret = 1; + } else { + ob_time->parts_[part_offset] = part_value; + *conflict_bitset |= (1 << part_offset); + } + return ret; +} + +// DT_YEAR use this interface ,as it should treat special: +// select to_date('5', 'YY') from dual; the result for oracle is 2005-09-01 +// +int set_ob_time_year_may_conflict(struct ObTime *ob_time, int32_t *julian_year_value, + int32_t check_year, int32_t set_year, + my_bool overwrite) +{ + int ret = 0; + + if (ZERO_DATE != *julian_year_value) { + if (*julian_year_value != check_year) { + ret = 1; + } else if (overwrite) { + ob_time->parts_[DT_YEAR] = set_year; + } + } else { + ob_time->parts_[DT_YEAR] = set_year; + *julian_year_value = check_year; + } + return (ret); +} + +/* + * element group may cause conflict on parts in ob_time + * 1. SSSSS vs HH, HH24, HH12, MI, SS + * 2. DDD vs DD MM/Mon/Month + * + * while call this function, the part_value must be the final value + */ +int set_ob_time_part_may_conflict(struct ObTime *ob_time, int64_t *conflict_bitset, const int64_t part_offset, const int32_t part_value) +{ + int ret = 0; + + if (part_offset >= TOTAL_PART_CNT) { + ret = 1; + } else { + if (0 != (*conflict_bitset & (1 << part_offset))) { + //already has data in ob_time.part_[part_name], validate it + if (part_value != ob_time->parts_[part_offset]) { + ret = 1; + } + } else { + *conflict_bitset |= (1 << part_offset); + ob_time->parts_[part_offset] = part_value; + } + } + + return (ret); +} + +int check_int_value_length(const struct ObDFMParseCtx *ctx, + const int64_t expected_len, + const int64_t real_data_len) +{ + int ret = 0; + /* + * format need separate chars but input omit separate chars like: + * to_date('20181225', 'YYYY-MM-DD') input omit '-' + * in this situation, the numeric value are matched in fixed length mode. + * which means real_data_len should be equal to element expected length, or will return with an error + */ + if (0 == ret && ctx->is_matching_by_expected_len_) { //is true only in only in str_to_ob_time_oracle_dfm + my_bool legal = TRUE; + if (RR == ctx->expected_elem_flag_ || RRRR == ctx->expected_elem_flag_) { //one special case + legal = (2 == real_data_len || 4 == real_data_len); + } else if (expected_len > 0) { //usual case, for numeric value + legal = (real_data_len == expected_len); + } + + if (!legal) { + ret = 1; + } + } + return ret; +} + +static int match_int_value_with_comma(struct ObDFMParseCtx *ctx, + const int64_t expected_len, + int64_t *value_len, + int32_t *result) +{ + int ret = 0; + int32_t temp_value = 0; + int64_t real_data_len = 0; + int64_t digits_len = 0; + int64_t continuous_comma_count = 0; + my_bool stop_flag = FALSE; + + + if (!(ctx->cur_ch_ != NULL && ctx->remain_len_ > 0)) { + ret = 1; + } + while (0 == ret && !stop_flag + && real_data_len < ctx->remain_len_ && digits_len < expected_len) { //look digits by # of value_len + char cur_char = *(ctx->cur_ch_ + real_data_len); + if (',' == cur_char) { + continuous_comma_count++; + if (continuous_comma_count == 2) { + --real_data_len; + stop_flag = TRUE; + } else { + ++real_data_len; + } + } else { + continuous_comma_count = 0; + if (is_split_char(cur_char)) { + stop_flag = TRUE; + } else { + if (!isdigit(cur_char)) { + ret = 1; //ORA-01858: a non-numeric character was found where a numeric was expected + } else { + temp_value *= 10; + temp_value += cur_char - '0'; + ++real_data_len; + ++digits_len; + } + } + } + } + if (0 == ret) { + if (0 != (ret = check_int_value_length(ctx, expected_len, real_data_len))) { + } else { + *value_len = real_data_len; + *result = temp_value; + } + } + return (ret); +} + +int match_int_value_with_sign(struct ObDFMParseCtx *ctx, + const int64_t expected_len, + int64_t *value_len, + int32_t *result, + int32_t value_sign) +{ + //only unsigned int + int ret = 0; + int32_t temp_value = 0; + int64_t real_data_len = 0; + int64_t date_max_len = 0; + + + if (!(ctx->cur_ch_ != NULL && ctx->remain_len_ > 0) || (expected_len < 0) + || (value_sign != -1 && value_sign != 1)) { + ret = 1; + } else if (!isdigit(ctx->cur_ch_[0])) { //check the first char + ret = 1; //ORA-01858: a non-numeric character was found where a numeric was expected + } + + date_max_len = MIN(ctx->remain_len_, expected_len); + + while (0 == ret + && real_data_len < date_max_len + && isdigit(ctx->cur_ch_[real_data_len])) { + int32_t cur_digit = (int32_t)(ctx->cur_ch_[real_data_len] - '0'); + + if (temp_value * 10LL > INT32_MAX - cur_digit) { + ret = 1; + } else { + temp_value = temp_value * 10 + cur_digit; + ++real_data_len; + } + } + + if (0 == ret) { + if (0 != (ret = check_int_value_length(ctx, expected_len, real_data_len))) { + } else { + *value_len = real_data_len; + *result = temp_value * value_sign; + } + } + + return (ret); +} + +int match_int_value(struct ObDFMParseCtx *ctx, + const int64_t expected_len, + int64_t *value_len, + int32_t *result) +{ + return match_int_value_with_sign(ctx, expected_len, value_len, result, 1); +} + + +int match_chars_until_space(struct ObDFMParseCtx *ctx, char **result, int64_t *result_len, int64_t *value_len) +{ + int ret = 0; + int32_t str_len = 0; + + + if (0 == ctx->remain_len_) { + ret = 1; + } + while (0 == ret && str_len < ctx->remain_len_ && !isspace(ctx->cur_ch_[str_len])) { + if (str_len >= *value_len) { + ret = 1; + } else { + ++str_len; + } + } + if (0 == ret) { + *result = (char *)ctx->cur_ch_; + *result_len = str_len; + *value_len = str_len; + } + + return (ret); +} + +int32_t ob_time_to_date(struct ObTime *ob_time) +{ + int32_t value = 0; + if (ZERO_DATE == ob_time->parts_[DT_DATE] && !HAS_TYPE_ORACLE(ob_time->mode_)) { + value = ZERO_DATE; + } else { + int32_t days_of_years; + int32_t leap_year_count; + int32_t *parts = ob_time->parts_; + parts[DT_YDAY] = DAYS_UNTIL_MON[IS_LEAP_YEAR(parts[DT_YEAR])][parts[DT_MON] - 1] + parts[DT_MDAY]; + days_of_years = (parts[DT_YEAR] - EPOCH_YEAR4) * DAYS_PER_NYEAR; + leap_year_count = LEAP_YEAR_COUNT(parts[DT_YEAR] - 1) - LEAP_YEAR_COUNT(EPOCH_YEAR4 - 1); + value = (int32_t)(days_of_years + leap_year_count + parts[DT_YDAY] - 1); + parts[DT_WDAY] = WDAY_OFFSET[value % DAYS_PER_WEEK ][EPOCH_WDAY]; + } + return value; +} + +static inline my_bool match_pattern_ignore_case(struct ObDFMParseCtx *ctx, const struct ObTimeConstStr *pattern) +{ + my_bool ret_bool = FALSE; + if (ctx->remain_len_ >= pattern->len_) { + ret_bool = (0 == strncasecmp(ctx->cur_ch_, pattern->ptr_, pattern->len_)); + } else { + //false + } + return ret_bool; +} + +static inline my_bool elem_has_meridian_indicator(OB_BITMAP *flag_bitmap) +{ + return ob_bitmap_is_set(flag_bitmap, AM) || ob_bitmap_is_set(flag_bitmap, PM) + || ob_bitmap_is_set(flag_bitmap, AM2) || ob_bitmap_is_set(flag_bitmap, PM2); +} + +int64_t skip_separate_chars_with_limit(struct ObDFMParseCtx *ctx, const int64_t limit, const int64_t except_char) +{ + int64_t sep_len = 0; + while (sep_len < ctx->remain_len_ && sep_len < limit + && is_split_char(ctx->cur_ch_[sep_len]) + && (int64_t)(ctx->cur_ch_[sep_len]) != except_char) { + sep_len++; + } + ctx->cur_ch_ += sep_len; + ctx->remain_len_ -= sep_len; + return sep_len; +} + +int64_t skip_separate_chars(struct ObDFMParseCtx *ctx) +{ + return skip_separate_chars_with_limit(ctx, MAX_VARCHAR_LENGTH, INT64_MAX); +} + +int64_t skip_blank_chars(struct ObDFMParseCtx *ctx) +{ + int64_t blank_char_len = 0; + while (blank_char_len < ctx->remain_len_ + && ' ' == ctx->cur_ch_[blank_char_len]) { + blank_char_len++; + } + ctx->cur_ch_ += blank_char_len; + ctx->remain_len_ -= blank_char_len; + return blank_char_len; +} + +const char *find_first_separator(struct ObDFMParseCtx *ctx) +{ + const char *result = NULL; + int64_t i = 0; + for (; NULL == result && i < ctx->remain_len_; i++) { + if (is_split_char(ctx->cur_ch_[i])) { + result = ctx->cur_ch_ + i; + } + } + return result; +} + +static int check_semantic(const DYNAMIC_ARRAY *dfm_elements, OB_BITMAP *flag_bitmap, uint64_t mode) +{ + int ret = 0; + int64_t i = 0; + int64_t conflict_group_bitset = 0; + + ob_bitmap_clear_all(flag_bitmap); + + for (; 0 == ret && i < dfm_elements->elements; ++i) { + struct ObDFMElem *elem = dynamic_element(dfm_elements, i, struct ObDFMElem *); + int64_t flag = elem->elem_flag_; + if (!(flag > INVALID_FLAG && flag < MAX_FLAG_NUMBER)) { + ret = 1; + } + //The following datetime format elements can be used in timestamp and interval format models, + //but not in the original DATE format model: FF, TZD, TZH, TZM, and TZR + if (0 == ret) { + if (flag >= FF1 && flag <= FF && !HAS_TYPE_ORACLE(mode)) { + ret = 1; + } else if (!HAS_TYPE_TIMEZONE(mode) && + (TZD == flag || TZR ==flag + || TZH == flag || TZM == flag)) { + ret = 1; + } + } + + //check no duplicate elem first + if (0 == ret) { + if ( ob_bitmap_is_set(flag_bitmap, flag)) { + ret = 1; //ORA-01810: format code appears twice + } else { + ob_bitmap_set_bit(flag_bitmap, flag); + } + } + //check conflict in group which the element belongs to + if (0 == ret) { + int64_t conf_group = CONFLICT_GROUP_MAP[flag]; + if (conf_group >= 0) { + if (0 != (conflict_group_bitset & (1 << conf_group))) { + ret = 1; + } else { + conflict_group_bitset |= (1 << conf_group); + } + } + }//end if + }//end for + + if (0 == ret) { + if ( ob_bitmap_is_set(flag_bitmap, TZM) + && ! ob_bitmap_is_set(flag_bitmap, TZH)) { + ret = 1; + } + } + + return (ret); +} + +/* search matched pattern */ +int parse_one_elem(struct ObDFMParseCtx *ctx, struct ObDFMElem *elem) +{ + int ret = 0; + + if (!(ctx->cur_ch_ != NULL && ctx->remain_len_ > 0)) { + ret = 1; + } else { + int64_t winner_flag = INVALID_FLAG; + int64_t max_matched_len = 0; + int64_t flag = 0; + for (;flag < MAX_FLAG_NUMBER; ++flag) { + const struct ObTimeConstStr *pattern = &PATTERNEX[flag]; + if (max_matched_len < pattern->len_ && match_pattern_ignore_case(ctx, pattern)) { + winner_flag = flag; + max_matched_len = pattern->len_; + } + } + + //uppercase adjust + if (0 == ret) { + if (winner_flag != INVALID_FLAG) { + elem->elem_flag_ = winner_flag; + elem->offset_ = ctx->cur_ch_ - ctx->fmt_str_; + switch (winner_flag) { + case MON: + case MONTH: + case DAY: + case DY: + case AM: + case PM: + case AD: + case BC: { + if (ctx->remain_len_ < 2) { + ret = 1; + } else if (isupper(ctx->cur_ch_[0]) && isupper(ctx->cur_ch_[1])) { + elem->upper_case_mode_ = ALL_CHARACTER; + } else if (isupper(ctx->cur_ch_[0])) { + elem->upper_case_mode_ = ONLY_FIRST_CHARACTER; + } else { + elem->upper_case_mode_ = NON_CHARACTER; + } + break; + } + + case AM2: + case PM2: + case AD2: + case BC2: { + if (ctx->remain_len_ < 4) { + ret = 1; + } else if (isupper(ctx->cur_ch_[0])) { + elem->upper_case_mode_ = ALL_CHARACTER; + } else { + elem->upper_case_mode_ = NON_CHARACTER; + } + } + default: + //do nothing + break; + } + ctx->cur_ch_ += max_matched_len; + ctx->remain_len_ -= max_matched_len; + } else { + ret = 1; + } + } + } + + return (ret); +} + +int parse_datetime_format_string(const char *fmt_str, const int64_t fmt_len, DYNAMIC_ARRAY *array) +{ + int ret = 0; + + if (fmt_str == NULL || fmt_len == 0) { + //do nothing + } else { + int64_t skipped_len = 0; + struct ObDFMParseCtx parse_ctx; + parse_ctx.fmt_str_ = fmt_str; + parse_ctx.cur_ch_ = fmt_str; + parse_ctx.remain_len_ = fmt_len; + parse_ctx.expected_elem_flag_ = INVALID_FLAG; + parse_ctx.is_matching_by_expected_len_ = FALSE; + + while ((0 == ret) && parse_ctx.remain_len_ > 0) { + //skip separate chars + skipped_len = skip_separate_chars(&parse_ctx); + //parse one element from head + if (parse_ctx.remain_len_ > 0) { + struct ObDFMElem value_elem; + value_elem.elem_flag_ = INVALID_FLAG; + value_elem.offset_ = -1; + value_elem.is_single_dot_before_ = FALSE; + value_elem.upper_case_mode_ = NON_CHARACTER; + + if((int64_t)(parse_ctx.cur_ch_ - parse_ctx.fmt_str_) > 0) { + value_elem.is_single_dot_before_ = (skipped_len == 1 && '.' == parse_ctx.cur_ch_[-1]); + } + + if (0 != (ret = (parse_one_elem(&parse_ctx, &value_elem)))) { + } else if (ma_insert_dynamic(array, &value_elem)) { + ret = 1; + } + } + } + } + + return (ret); +} + +int special_mode_sprintf(char *buf, const int64_t buf_len, int64_t *pos, + const struct ObTimeConstStr *str, const enum UpperCaseMode mode, int64_t padding) +{ + int ret = 0; + + if (str->len_ <= 0 || NULL == str->ptr_ || NULL == buf + || (padding > 0 && padding < str->len_)) { + ret = 1; + } else if (*pos + (padding > 0 ? padding : str->len_) >= buf_len) { + ret = 1; + } else { + int64_t i = 0; + for (i = 0; 0 == ret && i < str->len_; ++i) { + char cur_char = str->ptr_[i]; + if ((cur_char >= 'a' && cur_char <= 'z') + || (cur_char >= 'A' && cur_char <= 'Z')) { + switch (mode) { + case ALL_CHARACTER: { + buf[(*pos)++] = (char)(toupper(cur_char)); + break; + } + case ONLY_FIRST_CHARACTER: { + if (i == 0) { + buf[(*pos)++] = (char)(toupper(cur_char)); + } else { + buf[(*pos)++] = (char)(tolower(cur_char)); + } + break; + } + case NON_CHARACTER: { + buf[(*pos)++] = (char)(tolower(cur_char)); + break; + } + default: { + ret = 1; + break; + } + } + } else { + buf[(*pos)++] = cur_char; + }//end if + }//end for + + for (i = str->len_; i < padding; ++i) { + buf[(*pos)++] = ' '; + } + } + + return (ret); +} + +static const char DIGITS[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + +char *format_unsigned(struct ObFastFormatInt *ffi, uint64_t value) +{ + char *ptr = ffi->buf_ + (MAX_DIGITS10_STR_SIZE - 1); + uint32_t index = 0; + while (value >= 100) { + index = (uint32_t)(value % 100) << 1; + value /= 100; + *--ptr = DIGITS[index + 1]; + *--ptr = DIGITS[index]; + } + if (value < 10) { + *--ptr = (char)('0' + value); + } else { + index = (uint32_t)(value) << 1; + *--ptr = DIGITS[index + 1]; + *--ptr = DIGITS[index]; + } + return ptr; +} + +static void format_signed(struct ObFastFormatInt *ffi, int64_t value) +{ + uint64_t abs_value = (uint64_t)(value); + if (value < 0) { + abs_value = ~abs_value + 1; + ffi->ptr_ = format_unsigned(ffi, abs_value); + *--(ffi->ptr_) = '-'; + } else { + ffi->ptr_ = format_unsigned(ffi, abs_value); + } + ffi->len_ = ffi->buf_ - ffi->ptr_ + MAX_DIGITS10_STR_SIZE - 1; +} + +int data_fmt_nd(char *buffer, int64_t buf_len, int64_t *pos, const int64_t n, int64_t target) +{ + int ret = 0; + if (n <= 0 || target < 0 || target > 999999999) { + ret = 1; + } else if (n > buf_len - *pos) { + ret = 1; + } else { + struct ObFastFormatInt ffi; + memset(&ffi, 0, sizeof(ffi)); + format_signed(&ffi, target); + + if (ffi.len_ > n) { + ret = 1; + } else { + memset(buffer + *pos, '0', n - ffi.len_); + memcpy(buffer + *pos + n - ffi.len_, ffi.ptr_, ffi.len_); + *pos += n; + } + } + return (ret); +} + +static int32_t ob_time_to_week(const struct ObTime *ob_time, ObDTMode mode) +{ + const int32_t *parts = ob_time->parts_; + int32_t is_sun_begin = IS_SUN_BEGIN(mode); + int32_t is_zero_begin = IS_ZERO_BEGIN(mode); + int32_t is_ge_4_begin = IS_GE_4_BEGIN(mode); + int32_t week = 0; + if (parts[DT_YDAY] > DAYS_PER_NYEAR - 3 && is_ge_4_begin && !is_zero_begin) { + // maybe the day is in week 1 of next year. + int32_t days_cur_year = DAYS_PER_YEAR[IS_LEAP_YEAR(parts[DT_YEAR])]; + int32_t wday_next_yday1 = WDAY_OFFSET[days_cur_year - parts[DT_YDAY] + 1][parts[DT_WDAY]]; + int32_t yday_next_week1 = YDAY_WEEK1[wday_next_yday1][is_sun_begin][is_ge_4_begin]; + if (parts[DT_YDAY] >= days_cur_year + yday_next_week1) { + week = 1; + } + } + if (0 == week) { + int32_t wday_cur_yday1 = WDAY_OFFSET[(1 - parts[DT_YDAY]) % DAYS_PER_WEEK][parts[DT_WDAY]]; + int32_t yday_cur_week1 = YDAY_WEEK1[wday_cur_yday1][is_sun_begin][is_ge_4_begin]; + if (parts[DT_YDAY] < yday_cur_week1 && !is_zero_begin) { + // the day is in last week of prev year. + int32_t days_prev_year = DAYS_PER_YEAR[IS_LEAP_YEAR(parts[DT_YEAR] - 1)]; + int32_t wday_prev_yday1 = WDAY_OFFSET[(1 - days_prev_year - parts[DT_YDAY]) % DAYS_PER_WEEK][parts[DT_WDAY]]; + int32_t yday_prev_week1 = YDAY_WEEK1[wday_prev_yday1][is_sun_begin][is_ge_4_begin]; + week = (days_prev_year + parts[DT_YDAY] - yday_prev_week1 + DAYS_PER_WEEK) / DAYS_PER_WEEK; + } else { + week = (parts[DT_YDAY] - yday_cur_week1 + DAYS_PER_WEEK) / DAYS_PER_WEEK; + } + } + return week; +} + +int calculate_str_oracle_dfm_length(const struct ObTime *ob_time, + const char *fmt_str, const int64_t fmt_len, + int16_t scale, int64_t *len) +{ + int ret = 0; + if (NULL == fmt_str || fmt_len <=0 + || scale > MAX_SCALE_FOR_ORACLE_TEMPORAL) { + ret = 1; + } else { + int64_t last_elem_end_pos = 0; + int64_t i = 0; + int64_t length = 0; + DYNAMIC_ARRAY dfm_elems; + + if (scale < 0 ) { + scale = DEFAULT_SCALE_FOR_ORACLE_FRACTIONAL_SECONDS; + } + + //1. parse element from format string + if (ma_init_dynamic_array(&dfm_elems, sizeof(struct ObDFMElem), COMMON_ELEMENT_NUMBER, 0)) { + ret = 1; + } else if (0 != (ret = parse_datetime_format_string(fmt_str, fmt_len, &dfm_elems))) { + } + + //2. print each element + for (; 0 == ret && i < dfm_elems.elements; ++i) { + struct ObDFMElem *elem = dynamic_element(&dfm_elems, i, struct ObDFMElem *); + + //element is valid + if (!(elem->elem_flag_ > INVALID_FLAG && elem->elem_flag_ < MAX_FLAG_NUMBER && elem->offset_ >= 0)) { + ret = 1; + } + + //print separate chars between elements + if (0 == ret) { + int64_t separate_chars_len = elem->offset_ - last_elem_end_pos; + if (separate_chars_len > 0) { + length += separate_chars_len; + } + last_elem_end_pos = elem->offset_ + PATTERNEX[elem->elem_flag_].len_; + } + + //print current elem + if (0 == ret) { + switch (elem->elem_flag_) { + case AD: + case BC: { //TODO wjh: NLS_LANGUAGE + const struct ObTimeConstStr *target_str = ob_time->parts_[DT_YEAR] > 0 ? &PATTERNEX[AD] : &PATTERNEX[BC]; + length += target_str->len_; + break; + } + case AD2: + case BC2: { //TODO wjh: NLS_LANGUAGE + const struct ObTimeConstStr *str = ob_time->parts_[DT_YEAR] > 0 ? &PATTERNEX[AD2] : &PATTERNEX[BC2]; + length += str->len_; + break; + } + case CC: { + length += 2; + break; + } + case SCC: { + length += 2; + break; + } + case D: { + length += 1; + break; + } + case DAY: { //TODO wjh: NLS_LANGUAGE + length += MAX_WDAY_NAME_LENGTH; + break; + } + case DD: { + length += 2; + break; + } + case DDD: { + length += 3; + break; + } + case DY: { //TODO wjh: 1. NLS_LANGUAGE + const struct ObTimeConstStr *day_str = &WDAY_ABBR_NAMES[ob_time->parts_[DT_WDAY]]; + length += day_str->len_; + break; + } + case DS: { //TODO wjh: 1. NLS_TERRITORY 2. NLS_LANGUAGE + length += 2 + 1 + 2 + 1 + 1; + break; + } + case DL: { //TODO wjh: 1. NLS_DATE_FORMAT 2. NLS_TERRITORY 3. NLS_LANGUAGE + const struct ObTimeConstStr *wday_str = &WDAY_NAMES[ob_time->parts_[DT_WDAY]]; + const struct ObTimeConstStr *mon_str = &MON_NAMES[ob_time->parts_[DT_MON]]; + length += wday_str->len_ + 2 + mon_str->len_ + 1 + 2 + 2 + 1; + break; + } + case FF: { + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else if (0 == scale) { + //print nothing + } else { + length += scale; + } + break; + } + case FF1: + case FF2: + case FF3: + case FF4: + case FF5: + case FF6: + case FF7: + case FF8: + case FF9: { + int64_t scale = elem->elem_flag_ - FF1 + 1; + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else { + length += scale; + } + break; + } + case HH: + case HH12: { + length += 2; + break; + } + case HH24: { + length += 2; + break; + } + case IW: { + length += 2; + break; + } + case MI: { + length += 2; + break; + } + case MM: { + length += 2; + break; + } + case MONTH: { + length += MAX_MON_NAME_LENGTH; + break; + } + case MON: { + const struct ObTimeConstStr *mon_str = &MON_ABBR_NAMES[ob_time->parts_[DT_MON]]; + length += mon_str->len_; + break; + } + case AM: + case PM: { + const struct ObTimeConstStr *str = ob_time->parts_[DT_HOUR] >= 12 ? + &PATTERNEX[PM] : &PATTERNEX[AM]; + length += str->len_; + break; + } + case AM2: + case PM2: { + const struct ObTimeConstStr *str = ob_time->parts_[DT_HOUR] >= 12 ? + &PATTERNEX[PM2] : &PATTERNEX[AM2]; + length += str->len_; + break; + } + case Q: { + length += 1; + break; + } + case RR: { + length += 2; + break; + } + case RRRR: { + length += 4; + break; + } + case SS: { + length += 2; + break; + } + case SSSSS: { + length += 5; + break; + } + case WW: { //the first complete week of a year + length += 2; + break; + } + case W: { //the first complete week of a month + length += 1; + break; + } + case YGYYY: { + length += 1 + 1 + 3; + break; + } + case YEAR: { + ret = 1; + break; + } + case SYEAR: { + ret = 1; + break; + } + case SYYYY: { + length += 5; + break; + } + case YYYY: + case IYYY: { + length += 4; + break; + break; + } + case YYY: + case IYY: { + length += 3; + break; + } + case YY: + case IY: { + length += 2; + break; + } + case Y: + case I: { + length += 1; + break; + } + case TZD: { + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else if (ob_time->time_zone_id_ != -1) { + length += strlen(ob_time->tzd_abbr_); + } else { + //do nothing + } + break; + } + case TZR: { + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else if (ob_time->time_zone_id_ != -1) { + length += strlen(ob_time->tz_name_); + } else { + length += 6; + } + break; + } + case TZH: { + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else { + length += 3; + } + break; + } + + case TZM: { + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else { + length += 2; + } + break; + } + case X: { + length += 1; + break; + } + + default: { + ret = 1; + break; + } + } + } //end if + }//end for + + if (0 == ret) { + //print the rest separate chars + int64_t separate_chars_len = fmt_len - last_elem_end_pos; + if (separate_chars_len > 0) { + length += separate_chars_len; + } + } + + ma_delete_dynamic(&dfm_elems); + *len = length; + }//end if + + return (ret); +} + +int ob_time_to_str_oracle_dfm(const struct ObTime *ob_time, + const char *fmt_str, const int64_t fmt_len, + int16_t scale, + char *buf, int64_t buf_len, + int64_t *pos) +{ + int ret = 0; + + if (NULL == buf || buf_len <= 0 + || NULL == fmt_str || fmt_len <=0 + || scale > MAX_SCALE_FOR_ORACLE_TEMPORAL) { + ret = 1; + } else { + int64_t last_elem_end_pos = 0; + int64_t i = 0; + DYNAMIC_ARRAY dfm_elems; + + if (scale < 0 ) { + scale = DEFAULT_SCALE_FOR_ORACLE_FRACTIONAL_SECONDS; + } + + //1. parse element from format string + if (ma_init_dynamic_array(&dfm_elems, sizeof(struct ObDFMElem), COMMON_ELEMENT_NUMBER, 0)) { + ret = 1; + } else if (0 != (ret = parse_datetime_format_string(fmt_str, fmt_len, &dfm_elems))) { + } + + //2. print each element + for (; 0 == ret && i < dfm_elems.elements; ++i) { + struct ObDFMElem *elem = dynamic_element(&dfm_elems, i, struct ObDFMElem *); + + //element is valid + if (!(elem->elem_flag_ > INVALID_FLAG && elem->elem_flag_ < MAX_FLAG_NUMBER && elem->offset_ >= 0)) { + ret = 1; + } + + //print separate chars between elements + if (0 == ret) { + int64_t separate_chars_len = elem->offset_ - last_elem_end_pos; + if (separate_chars_len > 0) { + ret = databuff_printf(buf, buf_len, pos, "%.*s", (int32_t)(separate_chars_len), + fmt_str + last_elem_end_pos); + } + last_elem_end_pos = elem->offset_ + PATTERNEX[elem->elem_flag_].len_; + } + + //print current elem + if (0 == ret) { + switch (elem->elem_flag_) { + case AD: + case BC: { //TODO wjh: NLS_LANGUAGE + const struct ObTimeConstStr *target_str = ob_time->parts_[DT_YEAR] > 0 ? &PATTERNEX[AD] : &PATTERNEX[BC]; + ret = special_mode_sprintf(buf, buf_len, pos, target_str, elem->upper_case_mode_, -1); + break; + } + case AD2: + case BC2: { //TODO wjh: NLS_LANGUAGE + const struct ObTimeConstStr *str = ob_time->parts_[DT_YEAR] > 0 ? &PATTERNEX[AD2] : &PATTERNEX[BC2]; + ret = special_mode_sprintf(buf, buf_len, pos, str, elem->upper_case_mode_, -1); + break; + } + case CC: { + ret = databuff_printf(buf, buf_len, pos, "%02d", (abs(ob_time->parts_[DT_YEAR]) + 99) / 100); + break; + } + case SCC: { + char symbol = ob_time->parts_[DT_YEAR] > 0 ? ' ' : '-'; + ret = databuff_printf(buf, buf_len, pos, "%c%02d", symbol, (abs(ob_time->parts_[DT_YEAR]) + 99) / 100); + break; + } + case D: { + ret = databuff_printf(buf, buf_len, pos, "%d", ob_time->parts_[DT_WDAY] % 7 + 1); + break; + } + case DAY: { //TODO wjh: NLS_LANGUAGE + const struct ObTimeConstStr *day_str = &WDAY_NAMES[ob_time->parts_[DT_WDAY]]; + ret = special_mode_sprintf(buf, buf_len, pos, day_str, elem->upper_case_mode_, MAX_WDAY_NAME_LENGTH); + break; + } + case DD: { + ret = databuff_printf(buf, buf_len, pos, "%02d", ob_time->parts_[DT_MDAY]); + break; + } + case DDD: { + ret = databuff_printf(buf, buf_len, pos, "%03d", ob_time->parts_[DT_YDAY]); + break; + } + case DY: { //TODO wjh: 1. NLS_LANGUAGE + const struct ObTimeConstStr *day_str = &WDAY_ABBR_NAMES[ob_time->parts_[DT_WDAY]]; + ret = special_mode_sprintf(buf, buf_len, pos, day_str, elem->upper_case_mode_, -1); + break; + } + case DS: { //TODO wjh: 1. NLS_TERRITORY 2. NLS_LANGUAGE + ret = databuff_printf(buf, buf_len, pos, "%02d/%02d/%d", + ob_time->parts_[DT_MON], ob_time->parts_[DT_MDAY], ob_time->parts_[DT_YEAR]); + break; + } + case DL: { //TODO wjh: 1. NLS_DATE_FORMAT 2. NLS_TERRITORY 3. NLS_LANGUAGE + const struct ObTimeConstStr *wday_str = &WDAY_NAMES[ob_time->parts_[DT_WDAY]]; + const struct ObTimeConstStr *mon_str = &MON_NAMES[ob_time->parts_[DT_MON]]; + ret = databuff_printf(buf, buf_len, pos, "%.*s, %.*s %02d, %d", + wday_str->len_, wday_str->ptr_, + mon_str->len_, mon_str->ptr_, + ob_time->parts_[DT_MDAY], ob_time->parts_[DT_YEAR]); + break; + } + case FF: { + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else if (0 == scale) { + //print nothing + } else { + ret = data_fmt_nd(buf, buf_len, pos, scale, + ob_time->parts_[DT_USEC] / power_of_10[MAX_SCALE_FOR_ORACLE_TEMPORAL - scale]); + } + break; + } + case FF1: + case FF2: + case FF3: + case FF4: + case FF5: + case FF6: + case FF7: + case FF8: + case FF9: { + int64_t scale = elem->elem_flag_ - FF1 + 1; + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else { + ret = data_fmt_nd(buf, buf_len, pos, scale, + ob_time->parts_[DT_USEC] / power_of_10[MAX_SCALE_FOR_ORACLE_TEMPORAL - scale]); + } + break; + } + case HH: + case HH12: { + int32_t h = ob_time->parts_[DT_HOUR] % 12; + if (0 == h) { + h = 12; + } + ret = databuff_printf(buf, buf_len, pos, "%02d", h); + break; + } + case HH24: { + int32_t h = ob_time->parts_[DT_HOUR]; + ret = databuff_printf(buf, buf_len, pos, "%02d", h); + break; + } + case IW: { + ret = databuff_printf(buf, buf_len, pos, "%02d", ob_time_to_week(ob_time, WEEK_MODE[3])); + break; + } + case J: { + const int32_t base_julian_day = 2378497; // julian day of 1800-01-01 + const int32_t base_date = -62091; //ob_time.parts_[DT_DATE] of 1800-01-01 + if (ob_time->parts_[DT_DATE] < base_date) { + ret = 1; + } else { + int32_t julian_day = base_julian_day + ob_time->parts_[DT_DATE] - base_date; + ret = databuff_printf(buf, buf_len, pos, "%07d", julian_day); + } + break; + } + case MI: { + ret = databuff_printf(buf, buf_len, pos, "%02d", ob_time->parts_[DT_MIN]); + break; + } + case MM: { + ret = databuff_printf(buf, buf_len, pos, "%02d", ob_time->parts_[DT_MON]); + break; + } + case MONTH: { + const struct ObTimeConstStr *mon_str = &MON_NAMES[ob_time->parts_[DT_MON]]; + ret = special_mode_sprintf(buf, buf_len, pos, mon_str, elem->upper_case_mode_, MAX_MON_NAME_LENGTH); + break; + } + case MON: { + const struct ObTimeConstStr *mon_str = &MON_ABBR_NAMES[ob_time->parts_[DT_MON]]; + ret = special_mode_sprintf(buf, buf_len, pos, mon_str, elem->upper_case_mode_, -1); + break; + } + case AM: + case PM: { + const struct ObTimeConstStr *str = ob_time->parts_[DT_HOUR] >= 12 ? &PATTERNEX[PM] : &PATTERNEX[AM]; + ret = special_mode_sprintf(buf, buf_len, pos, str, elem->upper_case_mode_, -1); + break; + } + case AM2: + case PM2: { + const struct ObTimeConstStr *str = ob_time->parts_[DT_HOUR] >= 12 ? &PATTERNEX[PM2] : &PATTERNEX[AM2]; + ret = special_mode_sprintf(buf, buf_len, pos, str, elem->upper_case_mode_, -1); + break; + } + case Q: { + ret = databuff_printf(buf, buf_len, pos, "%d", (ob_time->parts_[DT_MON] + 2) / 3); + break; + } + case RR: { + ret = databuff_printf(buf, buf_len, pos, "%02d", (ob_time->parts_[DT_YEAR]) % 100); + break; + } + case RRRR: { + ret = databuff_printf(buf, buf_len, pos, "%04d", ob_time->parts_[DT_YEAR]); + break; + } + case SS: { + ret = databuff_printf(buf, buf_len, pos, "%02d", ob_time->parts_[DT_SEC]); + break; + } + case SSSSS: { + ret = databuff_printf(buf, buf_len, pos, "%05d", + ob_time->parts_[DT_HOUR] * 3600 + ob_time->parts_[DT_MIN] * 60 + ob_time->parts_[DT_SEC]); + break; + } + case WW: { //the first complete week of a year + ret = databuff_printf(buf, buf_len, pos, "%02d", (ob_time->parts_[DT_YDAY] - 1) / 7 + 1); + break; + } + case W: { //the first complete week of a month + ret = databuff_printf(buf, buf_len, pos, "%d", (ob_time->parts_[DT_MDAY] - 1) / 7 + 1); + break; + } + case YGYYY: { + ret = databuff_printf(buf, buf_len, pos, "%d,%03d", + abs(ob_time->parts_[DT_YEAR]) / 1000, abs(ob_time->parts_[DT_YEAR]) % 1000); + break; + } + case YEAR: { + ret = 1; + break; + } + case SYEAR: { + ret = 1; + break; + } + case SYYYY: { + const char* fmt_str = ob_time->parts_[DT_YEAR] < 0 ? "-%04d" : " %04d"; + ret = databuff_printf(buf, buf_len, pos, fmt_str, abs(ob_time->parts_[DT_YEAR])); + break; + } + case YYYY: + case IYYY: { + ret = databuff_printf(buf, buf_len, pos, "%04d", abs(ob_time->parts_[DT_YEAR])); + break; + } + case YYY: + case IYY: { + ret = databuff_printf(buf, buf_len, pos, "%03d", abs(ob_time->parts_[DT_YEAR] % 1000)); + break; + } + case YY: + case IY: { + ret = databuff_printf(buf, buf_len, pos, "%02d", abs(ob_time->parts_[DT_YEAR] % 100)); + break; + } + case Y: + case I: { + ret = databuff_printf(buf, buf_len, pos, "%01d", abs(ob_time->parts_[DT_YEAR] % 10)); + break; + } + case TZD: { + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else if (ob_time->time_zone_id_ != -1) { + ret = databuff_printf(buf, buf_len, pos, "%.*s", (int)strlen(ob_time->tzd_abbr_), ob_time->tzd_abbr_); + } else { + //do nothing + } + break; + } + case TZR: { + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else if (ob_time->time_zone_id_ != -1) { + ret = databuff_printf(buf, buf_len, pos, "%.*s", (int)strlen(ob_time->tz_name_), ob_time->tz_name_); + } else { + const char* fmt_str = ob_time->parts_[DT_OFFSET_MIN] < 0 ? "-%02d:%02d" : "+%02d:%02d"; + ret = databuff_printf(buf, buf_len, pos, fmt_str, + abs(ob_time->parts_[DT_OFFSET_MIN]) / 60, abs(ob_time->parts_[DT_OFFSET_MIN]) % 60); + } + break; + } + case TZH: { + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else { + const char* fmt_str = ob_time->parts_[DT_OFFSET_MIN] < 0 ? "-%02d" : "+%02d"; + ret = databuff_printf(buf, buf_len, pos, fmt_str, abs(ob_time->parts_[DT_OFFSET_MIN]) / 60); + } + break; + } + case TZM: { + if (!HAS_TYPE_ORACLE(ob_time->mode_)) { + ret = 1; + } else { + ret = databuff_printf(buf, buf_len, pos, "%02d", abs(ob_time->parts_[DT_OFFSET_MIN]) % 60); + } + break; + } + case X: { + ret = databuff_printf(buf, buf_len, pos, "."); + break; + } + default: { + ret = 1; + break; + } + } //end switch + if (0 != ret) { + } + } //end if + }//end for + + if (0 == ret) { + //print the rest separate chars + int64_t separate_chars_len = fmt_len - last_elem_end_pos; + if (separate_chars_len > 0) { + if (0 != (ret = databuff_printf(buf, buf_len, pos, "%.*s", (int32_t)(separate_chars_len), + fmt_str + last_elem_end_pos))) { + } + } + } + + ma_delete_dynamic(&dfm_elems); + }//end if + + return (ret); +} + +//TODO be delete, not to use +/** + * @brief convert string to ob_time struct according to oracle datetime format model + * @param in: str input string + * @param in: format format string + * @param out: ob_time memory struct of datetime + * @param out: scale scale of fractional seconds + */ +int str_to_ob_time_oracle_dfm(const char *str, const int64_t str_len, + const char *fmt_str, const int64_t fmt_len, + struct ObTime *ob_time, + int16_t scale) +{ + int ret = 0; + + if (NULL == str || str_len <= 0 + || NULL == fmt_str || fmt_len <=0 + || scale > MAX_SCALE_FOR_ORACLE_TEMPORAL) { + ret = 1; + } else { + memset(ob_time, 0, sizeof(struct ObTime)); + ob_time->mode_ |= DT_TYPE_DATETIME; + ob_time->mode_ |= DT_TYPE_ORACLE; + ob_time->mode_ |= DT_TYPE_TIMEZONE; + } + + if (0 == ret) { + DYNAMIC_ARRAY dfm_elems; + OB_BITMAP elem_flags = {.bitmap = NULL, .n_bits = 0}; + int32_t temp_tzh_value = -1; //positive value is legal + int32_t temp_tzm_value = -1; //positive value is legal + int32_t temp_tz_factor = 0; + + int32_t tz_hour = 0; //will be negetive when time zone offset < 0 + int32_t tz_min = 0; //will be negetive when time zone offset < 0 + + //1. parse element from format string + if (ma_init_dynamic_array(&dfm_elems, sizeof(struct ObDFMElem), COMMON_ELEMENT_NUMBER, 0)) { + ret = 1; + } else if (ob_bitmap_init(&elem_flags, MAX_FLAG_NUMBER)) { + ret = 1; + } else if (0 != (ret = parse_datetime_format_string(fmt_str, fmt_len, &dfm_elems))) { + } else if (0 != (ret = check_semantic(&dfm_elems, &elem_flags, ob_time->mode_))) { + } else { + //3. go through each element, set and check conflict for ob_time parts: Year, Month, Day, Hour, Minute, Second + int64_t last_elem_end_pos = 0; + int64_t conflict_part_bitset = 0; + int64_t elem_idx = 0; + int64_t input_sep_len = 0; + int64_t part_blank1_len = 0; + int64_t part_sep_len = 0; + int64_t part_blank2_len = 0; + int64_t format_sep_len = 0; + //static_assert((1 << TOTAL_PART_CNT) < INT64_MAX, "for time_part_conflict_bitset"); + int32_t yday_temp_value = ZERO_DATE; //as invalid value + int32_t wday_temp_value = ZERO_DATE; //as invalid value + int32_t date_temp_value = ZERO_DATE; //as invalid value + int32_t hh12_temp_value = ZERO_TIME; + int32_t julian_year_value = ZERO_DATE; //as invalid value + my_bool is_after_noon = FALSE; + my_bool is_before_christ = FALSE; + my_bool has_digit_tz_in_TZR = FALSE; + int64_t first_non_space_sep_char = INT64_MAX; + int64_t ignore_fs_flag = FALSE; + + struct ObDFMParseCtx ctx; + ctx.fmt_str_ = str; + ctx.cur_ch_ = str; + ctx.remain_len_ = str_len; + ctx.expected_elem_flag_ = INVALID_FLAG; + ctx.is_matching_by_expected_len_ = FALSE; + + for (elem_idx = 0; 0 == ret && elem_idx < dfm_elems.elements; ++elem_idx) { + struct ObDFMElem *elem = dynamic_element(&dfm_elems, elem_idx, struct ObDFMElem *); + + //element is valid + if (!(elem->elem_flag_ > INVALID_FLAG && elem->elem_flag_ < MAX_FLAG_NUMBER && elem->offset_ >= 0)) { + ret = 1; + } else { + } + + //1. check separate chars and skip blank chars first + if (0 == ret) { + //get format sep chars len + format_sep_len = elem->offset_ - last_elem_end_pos; + last_elem_end_pos = elem->offset_ + PATTERNEX[elem->elem_flag_].len_; + //parse input string and skip them + part_blank1_len = skip_blank_chars(&ctx); + //The # of skipped non-blank chars is according to format_str + first_non_space_sep_char = (0 == ctx.remain_len_) ? INT64_MAX : ctx.cur_ch_[0]; + if (X == elem->elem_flag_) { + /* 特殊情况, 不考虑X前面的非空白分隔符 */ + part_sep_len = 0; + } else { + part_sep_len = skip_separate_chars_with_limit(&ctx, format_sep_len, INT64_MAX); + } + part_blank2_len = skip_blank_chars(&ctx); + input_sep_len = part_blank1_len + part_sep_len + part_blank2_len; + } + + if (0 == ret && (0 == ctx.remain_len_)) { + break; //if all the input chars has beeen processed, break this loop + } + + //2. next, parse the current element + if (0 == ret) { + int64_t parsed_elem_len = 0; + const int64_t expected_elem_len = EXPECTED_MATCHING_LENGTH[elem->elem_flag_]; + ctx.expected_elem_flag_ = elem->elem_flag_; + ctx.is_matching_by_expected_len_ = (format_sep_len > 0 && input_sep_len == 0); + + switch (elem->elem_flag_) { + case AD: + case BC: + case AD2: + case BC2: { //TODO wjh: NLS_LANGUAGE + my_bool is_with_dot = (AD2 == elem->elem_flag_|| BC2 == elem->elem_flag_); + my_bool is_ad = FALSE; + my_bool is_bc = FALSE; + parsed_elem_len = is_with_dot ? PATTERNEX[AD2].len_ : PATTERNEX[AD].len_; + is_ad = match_pattern_ignore_case(&ctx, is_with_dot ? + &PATTERNEX[AD2] : &PATTERNEX[AD]); + is_bc = match_pattern_ignore_case(&ctx, is_with_dot ? + &PATTERNEX[BC2] : &PATTERNEX[BC]); + if (!is_ad && !is_bc) { + ret = 1; + } else { + is_before_christ = is_bc; + } + break; + } + case D: { + int32_t wday = 0; + if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &wday))) { + } else if (0 != (ret = check_validate(&LIMITER_WEEK_DAY, wday))) { + } else { + //oracle numbered sunday as 1 in territory of CHINA + //TODO wjh: hard code for now, need look up NLS_TERRITORIES + wday_temp_value = (wday + 5) % 7 + 1; + } + break; + } + case DY: + case DAY: { //TODO wjh: NLS_LANGUAGE NLS_TERRITORIES + int32_t wday = 0; + for (wday = 1; wday <= DAYS_PER_WEEK; ++wday) { + const struct ObTimeConstStr *day_str = (elem->elem_flag_ == DAY) ? &WDAY_NAMES[wday] : &WDAY_ABBR_NAMES[wday]; + if (match_pattern_ignore_case(&ctx, day_str)) { + parsed_elem_len = day_str->len_; + break; + } + } + if (0 != (ret = check_validate(&LIMITER_WEEK_DAY, wday))) { + } else { + wday_temp_value = wday; + } + break; + } + case DD: { + int32_t mday = 0; + if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &mday))) { + } else if (0 != (ret = check_validate(&LIMITER_MONTH_DAY, mday))) { + } else { + //may conflict with DDD + ret = set_ob_time_part_directly(ob_time, &conflict_part_bitset, DT_MDAY, mday); + } + break; + } + case DDD: { + int32_t yday = 0; + if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &yday))) { + } else if (0 != (ret = check_validate(&LIMITER_YEAR_DAY, yday))) { + } else { + yday_temp_value = yday; + } + break; + } + case DS: { //TODO wjh: impl it NEED NLS_DATE_FORMAT NLS_TERRITORY NLS_LANGUAGE + ret = 1; + break; + } + case DL: { //TODO wjh: impl it NEED NLS_DATE_FORMAT NLS_TERRITORY NLS_LANGUAGE + ret = 1; + break; + } + case FF: + case FF1: + case FF2: + case FF3: + case FF4: + case FF5: + case FF6: + case FF7: + case FF8: + case FF9: { + int32_t usec = 0; + //format string has '.' or 'X', but input string does not contain '.' + //do nothing, skip element FF and revert ctx by the length of parsed chars + if (ignore_fs_flag) { + ctx.cur_ch_ -= (part_blank1_len + part_sep_len + part_blank2_len); + ctx.remain_len_ += (part_blank1_len + part_sep_len + part_blank2_len); + } else if (elem->is_single_dot_before_ && '.' != first_non_space_sep_char) { + ctx.cur_ch_ -= (part_sep_len + part_blank2_len); + ctx.remain_len_ += (part_sep_len + part_blank2_len); + } else if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &usec))) { + } else { + scale = (int16_t)(parsed_elem_len); + usec = (int32_t)(usec * power_of_10[MAX_SCALE_FOR_ORACLE_TEMPORAL - parsed_elem_len]); + ob_time->parts_[DT_USEC] = usec; + } + break; + } + + case TZH: + case TZM: { + if ( ob_bitmap_is_set(&elem_flags, TZR) || ob_bitmap_is_set(&elem_flags, TZD)) { + ret = 1; + } else { + int32_t value = 0; + int32_t local_tz_factor = 1; + /* + * SQL> alter session set NLS_TIMESTAMP_TZ_FORMAT='DD-MON-RR HH.MI.SS AM TZH:TZM'; + * SQL> alter session set time_zone='Asia/Shanghai'; + * SQL> select cast('01-SEP-20 11.11.11' as timestamp with time zone) from dual; + * 01-SEP-20 11.11.11 AM +08:00 + */ + ob_time->is_tz_name_valid_ = FALSE; + if (TZH == elem->elem_flag_) { + if (is_sign_char(ctx.cur_ch_[0])) { + local_tz_factor = ('-' == ctx.cur_ch_[0] ? -1 : 1); + ctx.cur_ch_ += 1; + ctx.remain_len_ -= 1; + } else { + if (((int64_t)(ctx.cur_ch_ - ctx.fmt_str_)) > 0 && input_sep_len > format_sep_len) { + //if the input valid separate chars > format separate chars + //the superfluous '-' will be regarded as minus sign + local_tz_factor = ((int64_t)('-') == ctx.cur_ch_[-1] ? -1 : 1); + } + } + temp_tz_factor = local_tz_factor; //1 or -1, but never be 0 + } + + if (0 == ctx.remain_len_) { + //do nothing + } else if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &value))) { + } else if (0 != (ret = (elem->elem_flag_ == TZH ? check_validate(&LIMITER_TIMEZONE_HOUR_ABS, value) : check_validate(&LIMITER_TIMEZONE_MIN_ABS, value)))) { + } else { + if (elem->elem_flag_ == TZH) { + temp_tzh_value = value; + } else { + temp_tzm_value = value; + } + } + } + break; + } + + case TZR: { + if ( ob_bitmap_is_set(&elem_flags, TZH) || ob_bitmap_is_set(&elem_flags, TZM)) { + ret = 1; + } else { + int32_t local_tz_factor = 1; + if (isdigit(ctx.cur_ch_[0]) || is_sign_char(ctx.cur_ch_[0])) { //case1: digits + int32_t tmp_tz_hour = 0; + int32_t tmp_tz_min = 0; + char *digits_timezone; + int64_t digits_timezone_len; + if (is_sign_char(ctx.cur_ch_[0])) { + local_tz_factor = ('-' == ctx.cur_ch_[0] ? -1 : 1); + ctx.cur_ch_ += 1; + ctx.remain_len_ -= 1; + } else if (part_blank1_len + part_sep_len > format_sep_len + && is_sign_char(ctx.cur_ch_[-1])) { + local_tz_factor = ('-' == ctx.cur_ch_[-1] ? -1 : 1); + } + + parsed_elem_len = UNKNOWN_LENGTH_OF_ELEMENT; + if (!(0 == ctx.remain_len_)) { + ob_time->is_tz_name_valid_ = FALSE; + } + if (0 == ctx.remain_len_) { + //do nothing + } else if (0 != (ret = match_chars_until_space(&ctx, &digits_timezone, &digits_timezone_len, &parsed_elem_len))) { + //do nothing + } else { + const char *local_sep = NULL; + struct ObDFMParseCtx local_ctx; + local_ctx.fmt_str_ = digits_timezone; + local_ctx.cur_ch_ = digits_timezone; + local_ctx.remain_len_ = digits_timezone_len; + local_ctx.expected_elem_flag_ = INVALID_FLAG; + local_ctx.is_matching_by_expected_len_ = FALSE; + + local_sep = find_first_separator(&local_ctx); + + if (NULL == local_sep) { + } else { + int64_t hour_expected_len = local_sep - digits_timezone; + int64_t local_parsed_len = 0; + if (0 != (ret = match_int_value_with_sign(&local_ctx, hour_expected_len, &local_parsed_len, + &tmp_tz_hour, local_tz_factor))) { + } else if (0 != (ret = check_validate(&LIMITER_TIMEZONE_HOUR_ABS, tmp_tz_hour))) { + } else if (hour_expected_len != local_parsed_len) { + ret = 1; //invalid time zone + } else { + local_ctx.cur_ch_ += hour_expected_len + 1; + local_ctx.remain_len_ -= hour_expected_len + 1; + if (0 == local_ctx.remain_len_) { + has_digit_tz_in_TZR = TRUE; + tz_hour = tmp_tz_hour; + tz_min = tmp_tz_min; + } else if (0 != (ret = match_int_value_with_sign(&local_ctx, parsed_elem_len - hour_expected_len - 1, + &local_parsed_len, &tmp_tz_min, local_tz_factor))) { + } else if (0 != (ret = check_validate(&LIMITER_TIMEZONE_MIN_ABS, tmp_tz_min))) { + } else if (parsed_elem_len != hour_expected_len + local_parsed_len + 1) { + ret = 1; //invalid time zone + } else { + has_digit_tz_in_TZR = TRUE; + tz_hour = tmp_tz_hour; + tz_min = tmp_tz_min; + } + } + } + } + } else { //case2: strings + char *tzr_str; + int64_t tzr_str_len; + parsed_elem_len = OB_MAX_TZ_NAME_LEN - 1; + if (0 != (ret = match_chars_until_space(&ctx, &tzr_str, &tzr_str_len, &parsed_elem_len))) { + } else { + memcpy(ob_time->tz_name_, tzr_str, tzr_str_len); + ob_time->tz_name_[tzr_str_len] = '\0'; + ob_time->is_tz_name_valid_ = TRUE; + } + } + } + break; + } + + case TZD: { + if ( ob_bitmap_is_set(&elem_flags, TZH) || ob_bitmap_is_set(&elem_flags, TZM)) { + ret = 1; + } else if (has_digit_tz_in_TZR) { + ret = 1; + } else { + char *tzd_str; + int64_t tzd_str_len; + parsed_elem_len = OB_MAX_TZ_ABBR_LEN - 1; + if (0 != (ret = match_chars_until_space(&ctx, &tzd_str, &tzd_str_len, &parsed_elem_len))) { + } else { + memcpy(ob_time->tzd_abbr_, tzd_str, tzd_str_len); + ob_time->tzd_abbr_[tzd_str_len] = '\0'; + } + } + break; + } + + case J: { + const int32_t base_julian_day = 2378497; // julian day of 1800-01-01 + const int32_t base_date = -62091; //ob_time.parts_[DT_DATE] of 1800-01-01 + int32_t julian_day = 0; + int32_t ob_time_date = 0; + struct ObTime tmp_ob_time; + if (0 != (ret = match_int_value(&ctx, expected_elem_len, + &parsed_elem_len, &julian_day))) { + } else if (0 != (ret = check_validate(&LIMITER_JULIAN_DATE, julian_day))) { + } else if (julian_day < base_julian_day) { + ret = 1; + } else { + ob_time_date = julian_day - base_julian_day + base_date; + if (0 != (ret = date_to_ob_time(ob_time_date, &tmp_ob_time))) { + } else if (0 != (ret = set_ob_time_year_may_conflict(ob_time, &julian_year_value, + tmp_ob_time.parts_[DT_YEAR], + tmp_ob_time.parts_[DT_YEAR], + TRUE /* overwrite */))) { + } else { + yday_temp_value = tmp_ob_time.parts_[DT_YDAY]; + } + } + break; + } + + case HH: + case HH12: { + int32_t hour = 0; + if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &hour))) { + } else if (0 != (ret = check_validate(&LIMITER_HOUR12, hour))) { + } else if (!elem_has_meridian_indicator(&elem_flags)) { + ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_HOUR, hour); + } else { + hh12_temp_value = hour; + } + break; + } + case HH24: { + int32_t hour = 0; + if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &hour))) { + } else if (0 != (ret = check_validate(&LIMITER_HOUR24, hour))) { + } else if (elem_has_meridian_indicator(&elem_flags)) { + ret = 1; + } else { + //may conflict with SSSSS + ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_HOUR, hour); + } + break; + } + case MI: { + int32_t min = 0; + if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &min))) { + } else if (0 != (ret = check_validate(&LIMITER_MINUTE, min))) { + } else { + //may conflict with SSSSS + ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_MIN, min); + } + break; + } + case MM: { + int32_t mon = 0; + if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &mon))) { + } else if (0 != (ret = check_validate(&LIMITER_MONTH_DAY, mon))) { + } else { + //may conflict with DDD + ret = set_ob_time_part_directly(ob_time, &conflict_part_bitset, DT_MON, mon); + } + break; + } + case MON: + case MONTH: { + int32_t mon = 0; + for (mon = LIMITER_MONTH.MIN; mon <= LIMITER_MONTH.MAX; ++mon) { + const struct ObTimeConstStr *mon_str = (elem->elem_flag_ == MONTH) ? &MON_NAMES[mon] : &MON_ABBR_NAMES[mon]; + if (match_pattern_ignore_case(&ctx, mon_str)) { + parsed_elem_len = mon_str->len_; + break; + } + } + if (0 != (ret = check_validate(&LIMITER_MONTH, mon))) { + } else { + //may conflict with DDD + ret = set_ob_time_part_directly(ob_time, &conflict_part_bitset, DT_MON, mon); + } + break; + } + case AM: + case PM: + case AM2: + case PM2: { + my_bool is_with_dot = (AM2 == elem->elem_flag_|| PM2 == elem->elem_flag_); + my_bool is_am = FALSE; + my_bool is_pm = FALSE; + parsed_elem_len = is_with_dot ? PATTERNEX[AM2].len_ : PATTERNEX[AM].len_; + is_am = match_pattern_ignore_case(&ctx, + is_with_dot ? &PATTERNEX[AM2] : &PATTERNEX[AM]); + is_pm = match_pattern_ignore_case(&ctx, + is_with_dot ? &PATTERNEX[PM2] : &PATTERNEX[PM]); + if (!is_am && !is_pm) { + ret = 1; + } else { + is_after_noon = is_pm; + } + break; + } + case RR: + case RRRR: { + int32_t round_year = 0; + int32_t conflict_check_year = 0; + if (0 != (ret = match_int_value(&ctx, 4, &parsed_elem_len, &round_year))) { + } else if (parsed_elem_len > 2) { + conflict_check_year = round_year; + //do nothing + } else { + int32_t first_two_digits_of_current_year = (ob_time->parts_[DT_YEAR] / 100) % 100; + int32_t last_two_digits_of_current_year = ob_time->parts_[DT_YEAR] % 100; + conflict_check_year = round_year; + if (round_year < 50) { //0~49 + if (last_two_digits_of_current_year < 50) { + round_year += first_two_digits_of_current_year * 100; + } else { + round_year += (first_two_digits_of_current_year + 1) * 100; + } + } else if (round_year < 100) { //50~99 + if (last_two_digits_of_current_year < 50) { + round_year += (first_two_digits_of_current_year - 1) * 100; + } else { + round_year += first_two_digits_of_current_year * 100; + } + } + } + if (0 == ret) { + if (0 != (ret = check_validate(&LIMITER_YEAR, round_year))) { //TODO wjh: negetive year number + } else if (0 != (ret = set_ob_time_year_may_conflict(ob_time, &julian_year_value, + conflict_check_year, round_year, + FALSE /* overwrite */))) { + } + } + break; + } + case SS: { + int32_t sec = 0; + if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &sec))) { + } else if (0 != (ret = check_validate(&LIMITER_SECOND, sec))) { + } else { + ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_SEC, sec); + } + + break; + } + case SSSSS: { + int32_t sec_past_midnight = 0; + if (0 != (ret = match_int_value(&ctx, expected_elem_len, &parsed_elem_len, &sec_past_midnight))) { + } else if (0 != (ret = check_validate(&LIMITER_SECS_PAST_MIDNIGHT, sec_past_midnight))) { + } else { + int32_t secs = sec_past_midnight % (int32_t)(SECS_PER_MIN); + int32_t mins = (sec_past_midnight / (int32_t)(SECS_PER_MIN)) % (int32_t)(MINS_PER_HOUR); + int32_t hours = sec_past_midnight / (int32_t)(SECS_PER_MIN) / (int32_t)(MINS_PER_HOUR); + if (0 != (ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_SEC, secs))) { + } else if (0 != (ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_MIN, mins))) { + } else if (0 != (ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_HOUR, hours))) { + } + } + break; + } + case YGYYY: { + int32_t years = 0; + if (0 != (ret = match_int_value_with_comma(&ctx, expected_elem_len, &parsed_elem_len, &years))) { + } else if (0 != (ret = check_validate(&LIMITER_YEAR, years))) { + } else if (0 != (ret = set_ob_time_year_may_conflict(ob_time, &julian_year_value, + years, years, FALSE /* overwrite */))) { + } + break; + } + case SYYYY: + case YYYY: + case YYY: + case YY: + case Y: { + int32_t years = 0; + int32_t conflict_check_year = 0; + int32_t sign = 1; + if (SYYYY == elem->elem_flag_) { + if (is_sign_char(ctx.cur_ch_[0])) { + sign = ('-' == ctx.cur_ch_[0] ? -1 : 1); + ctx.cur_ch_ += 1; + ctx.remain_len_ -= 1; + } else if (part_blank1_len + part_sep_len > format_sep_len + && is_sign_char(ctx.cur_ch_[-1])) { + sign = ('-' == ctx.cur_ch_[-1] ? -1 : 1); + } + } + if (!(ctx.cur_ch_ != NULL && ctx.remain_len_ > 0)) { + } else if (0 != (ret = match_int_value_with_sign(&ctx, expected_elem_len, &parsed_elem_len, &years, sign))) { + } + if (0 == ret) { + conflict_check_year = years; + if (expected_elem_len < 4) { + years += (ob_time->parts_[DT_YEAR] / (int32_t)(power_of_10[parsed_elem_len])) + * (int32_t)(power_of_10[parsed_elem_len]); + } + if (0 != (ret = check_validate(&LIMITER_YEAR, years))) { + } else if (0 != (ret = set_ob_time_year_may_conflict(ob_time, &julian_year_value, + conflict_check_year, years, + FALSE /* overwrite */))) { + } + } + break; + } + case X: { + if ('.' != ctx.cur_ch_[0]) { + ignore_fs_flag = TRUE; + ctx.cur_ch_ -= (part_blank1_len + part_sep_len + part_blank2_len); + ctx.remain_len_ += (part_blank1_len + part_sep_len + part_blank2_len); + } else { + parsed_elem_len = 1; + } + break; + } + + case CC: + case SCC: + case IW: + case W: + case WW: + case YEAR: + case SYEAR: + case Q: + case I: + case IY: + case IYY: + case IYYY: { + ret = 1; + break; + } + default: { + ret = 1; + break; + } + } //end switch + + if (0 == ret) { + ctx.cur_ch_ += parsed_elem_len; + ctx.remain_len_ -= parsed_elem_len; + } + }//end if + + if (0 != ret) { + } else { + } + } //end for + + //check if the unprocessed elems has permission to be omitted + if (0 == ret) { + for (; elem_idx < dfm_elems.elements; ++elem_idx) { + struct ObDFMElem *elem = dynamic_element(&dfm_elems, elem_idx, struct ObDFMElem *); + if (!is_element_can_omit(elem)) { + ret = 1; + } + } + } + + if (0 == ret) { + //all elems has finished, is there anything else in str? the rest must be separators, do check. + int64_t str_remain_sep_len = 0; + while (str_remain_sep_len < ctx.remain_len_ + && is_split_char(ctx.cur_ch_[str_remain_sep_len])) { + str_remain_sep_len++; + } + ctx.cur_ch_ += str_remain_sep_len; + ctx.remain_len_ -= str_remain_sep_len; + if (ctx.remain_len_ > 0) { + ret = 1; + } + } + + //after noon conflict: AM PM vs HH12 HH + if (0 == ret) { + if (hh12_temp_value != ZERO_TIME) { + //when hour value, varied by meridian indicators + //if HH12 = 12, when meridian indicator 'AM' exists, the real time is hour = 0 + //if HH12 = 12, when meridian indicator 'PM' exists, the real time is hour = 12 + if (is_after_noon) { + ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_HOUR, hh12_temp_value % 12 + 12); + } else { + ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_HOUR, hh12_temp_value % 12); + } + } + } + + //before christ conflict: BC AD vs YEAR //TODO wjh: change this when ob_time support negetive years + if (0 == ret) { + if (is_before_christ) { + ret = 1; + } + } + + //year cannot changed after this line + //feed/validate yday + YEAR to MON and DAY + if (0 == ret) { + if (yday_temp_value != ZERO_DATE) { + int32_t month = 0; + int32_t day = 0; + if (0 != (ret = get_day_and_month_from_year_day(yday_temp_value, ob_time->parts_[DT_YEAR], &month, &day))) { + } else if (0 != (ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_MON, month))) { + } else if (0 != (ret = set_ob_time_part_may_conflict(ob_time, &conflict_part_bitset, DT_MDAY, day))) { + } + } + } + + //calc and validate: YDAY WDAY vs YEAR MON DAY + if (0 == ret) { + if (0 != (ret = validate_oracle_date(ob_time))) { + } else { + //ob_time_to_date func is to calc YDAY and WDAY and return DATE + date_temp_value = ob_time_to_date(ob_time); //TODO: shanting + if (yday_temp_value != ZERO_DATE && ob_time->parts_[DT_YDAY] != yday_temp_value) { + ret = 1; + } else if (wday_temp_value != ZERO_DATE && ob_time->parts_[DT_WDAY] != wday_temp_value) { + ret = 1; + } else { + ob_time->parts_[DT_DATE] = date_temp_value; + } + } + } + + //for time zone info + if (0 == ret && !ob_time->is_tz_name_valid_) { + //B. timezone defined by time zone hour and minute + int32_t tz_offset_value = 0; + + if ( ob_bitmap_is_set(&elem_flags, TZH)) { + my_bool has_tzh_value = (temp_tzh_value >= 0); + my_bool has_tzm_value = (temp_tzm_value >= 0); + if (!has_tzh_value && has_tzm_value) { + ret = 1; + } else if (!has_tzh_value && !has_tzm_value) { + //do nothing + } else if (has_tzh_value && !has_tzm_value) { + tz_hour = temp_tz_factor * temp_tzh_value; + tz_min = temp_tz_factor * abs(tz_min); + } else { + tz_hour = temp_tz_factor * temp_tzh_value; + tz_min = temp_tz_factor * temp_tzm_value; + } + } else if ( ob_bitmap_is_set(&elem_flags, TZR)) { + //do nothing + } else { + //no time zone info in elem_flags + } + + //calc offset + if (0 == ret) { + if (tz_hour * tz_min < 0) { + ret = 1; + } else { + tz_offset_value = (int32_t)(tz_hour * MINS_PER_HOUR + tz_min); + } + } + + //final validate + if (0 == ret) { + if (!(MIN_OFFSET_MINUTES <= tz_offset_value && tz_offset_value <= MAX_OFFSET_MINUTES)) { + ret = 1; + } else { + ob_time->parts_[DT_OFFSET_MIN] = tz_offset_value; + } + } + } + + if (0 != ret) { + } else { + } + }//end if + ob_bitmap_free(&elem_flags); + ma_delete_dynamic(&dfm_elems); + } + + return ret; +} diff --git a/libmariadb/ob_protocol20.c b/libmariadb/ob_protocol20.c new file mode 100644 index 0000000..5978f33 --- /dev/null +++ b/libmariadb/ob_protocol20.c @@ -0,0 +1,702 @@ +#include "ob_protocol20.h" + +#include +#include + +#include "mysql.h" +#include "ma_global.h" +#include "ob_object.h" +#include "ob_full_link_trace.h" +#include "ob_rwlock.h" + +#define UINT24_MAX (16777215U) +#define EXTRA_TYPE_LENGTH 2 +#define EXTRA_LEN_LENGTH 4 + +#define OB20_SERIALIZE_FUNC_SET(id, funcname) (extra_serialize_funcs[id] = (ExtraInfoSerializeFunc)OB20_EXTRAINFO_SERIALIZE_FUNC(funcname)) +#define OB20_SERIALIZE_FUNC_INIT() \ + do \ + { \ + /* FULL_TRACE */ \ + OB20_SERIALIZE_FUNC_SET(FULL_TRC, flt); \ + } while (0) + +extern void *ma_multi_malloc(myf MyFlags, ...); + +static ob_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; +static int init_flag = 0; +static ExtraInfoSerializeFunc extra_serialize_funcs[OB20_SVR_END + 1]; + +// CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) +uint16_t const ob_crc16_table[256] = {0x0000, + 0xC0C1, + 0xC181, + 0x0140, + 0xC301, + 0x03C0, + 0x0280, + 0xC241, + 0xC601, + 0x06C0, + 0x0780, + 0xC741, + 0x0500, + 0xC5C1, + 0xC481, + 0x0440, + 0xCC01, + 0x0CC0, + 0x0D80, + 0xCD41, + 0x0F00, + 0xCFC1, + 0xCE81, + 0x0E40, + 0x0A00, + 0xCAC1, + 0xCB81, + 0x0B40, + 0xC901, + 0x09C0, + 0x0880, + 0xC841, + 0xD801, + 0x18C0, + 0x1980, + 0xD941, + 0x1B00, + 0xDBC1, + 0xDA81, + 0x1A40, + 0x1E00, + 0xDEC1, + 0xDF81, + 0x1F40, + 0xDD01, + 0x1DC0, + 0x1C80, + 0xDC41, + 0x1400, + 0xD4C1, + 0xD581, + 0x1540, + 0xD701, + 0x17C0, + 0x1680, + 0xD641, + 0xD201, + 0x12C0, + 0x1380, + 0xD341, + 0x1100, + 0xD1C1, + 0xD081, + 0x1040, + 0xF001, + 0x30C0, + 0x3180, + 0xF141, + 0x3300, + 0xF3C1, + 0xF281, + 0x3240, + 0x3600, + 0xF6C1, + 0xF781, + 0x3740, + 0xF501, + 0x35C0, + 0x3480, + 0xF441, + 0x3C00, + 0xFCC1, + 0xFD81, + 0x3D40, + 0xFF01, + 0x3FC0, + 0x3E80, + 0xFE41, + 0xFA01, + 0x3AC0, + 0x3B80, + 0xFB41, + 0x3900, + 0xF9C1, + 0xF881, + 0x3840, + 0x2800, + 0xE8C1, + 0xE981, + 0x2940, + 0xEB01, + 0x2BC0, + 0x2A80, + 0xEA41, + 0xEE01, + 0x2EC0, + 0x2F80, + 0xEF41, + 0x2D00, + 0xEDC1, + 0xEC81, + 0x2C40, + 0xE401, + 0x24C0, + 0x2580, + 0xE541, + 0x2700, + 0xE7C1, + 0xE681, + 0x2640, + 0x2200, + 0xE2C1, + 0xE381, + 0x2340, + 0xE101, + 0x21C0, + 0x2080, + 0xE041, + 0xA001, + 0x60C0, + 0x6180, + 0xA141, + 0x6300, + 0xA3C1, + 0xA281, + 0x6240, + 0x6600, + 0xA6C1, + 0xA781, + 0x6740, + 0xA501, + 0x65C0, + 0x6480, + 0xA441, + 0x6C00, + 0xACC1, + 0xAD81, + 0x6D40, + 0xAF01, + 0x6FC0, + 0x6E80, + 0xAE41, + 0xAA01, + 0x6AC0, + 0x6B80, + 0xAB41, + 0x6900, + 0xA9C1, + 0xA881, + 0x6840, + 0x7800, + 0xB8C1, + 0xB981, + 0x7940, + 0xBB01, + 0x7BC0, + 0x7A80, + 0xBA41, + 0xBE01, + 0x7EC0, + 0x7F80, + 0xBF41, + 0x7D00, + 0xBDC1, + 0xBC81, + 0x7C40, + 0xB401, + 0x74C0, + 0x7580, + 0xB541, + 0x7700, + 0xB7C1, + 0xB681, + 0x7640, + 0x7200, + 0xB2C1, + 0xB381, + 0x7340, + 0xB101, + 0x71C0, + 0x7080, + 0xB041, + 0x5000, + 0x90C1, + 0x9181, + 0x5140, + 0x9301, + 0x53C0, + 0x5280, + 0x9241, + 0x9601, + 0x56C0, + 0x5780, + 0x9741, + 0x5500, + 0x95C1, + 0x9481, + 0x5440, + 0x9C01, + 0x5CC0, + 0x5D80, + 0x9D41, + 0x5F00, + 0x9FC1, + 0x9E81, + 0x5E40, + 0x5A00, + 0x9AC1, + 0x9B81, + 0x5B40, + 0x9901, + 0x59C0, + 0x5880, + 0x9841, + 0x8801, + 0x48C0, + 0x4980, + 0x8941, + 0x4B00, + 0x8BC1, + 0x8A81, + 0x4A40, + 0x4E00, + 0x8EC1, + 0x8F81, + 0x4F40, + 0x8D01, + 0x4DC0, + 0x4C80, + 0x8C41, + 0x4400, + 0x84C1, + 0x8581, + 0x4540, + 0x8701, + 0x47C0, + 0x4680, + 0x8641, + 0x8201, + 0x42C0, + 0x4380, + 0x8341, + 0x4100, + 0x81C1, + 0x8081, + 0x4040}; + +inline uint16_t ob_crc16_byte(uint16_t crc, const uint8_t data) +{ + return (uint16_t)((crc >> 8) ^ ob_crc16_table[(crc ^ data) & 0xff]); +} + +/** + * ob_crc16 - compute the CRC-16 for the data buffer + * @crc: previous CRC value + * @buffer: data pointer + * @len: number of bytes in the buffer + * + * Returns the updated CRC value. + */ +inline uint16_t ob_crc16(uint16_t crc, uint8_t const *buffer, int64_t len) +{ + while (len--) { + crc = ob_crc16_byte(crc, *buffer++); + } + return crc; +} + + +int ob20_init(Ob20Protocol *ob20protocol, unsigned long conid, my_bool use_flt) +{ + int ret = OB_SUCCESS; + + if (OB_ISNULL(ob20protocol)) { + ret = OB_ERROR; + } else { + if (!init_flag) { + ob_mutex_lock(&init_mutex); + if (!init_flag) { + OB20_SERIALIZE_FUNC_INIT(); // init extra info serialize func + init_flag = 1; + } + ob_mutex_unlock(&init_mutex); + } + memset(ob20protocol, 0, sizeof(*(ob20protocol))); + + ob20protocol->header.magic_num = OB20_PROTOCOL_MAGIC_NUM; + ob20protocol->header.version = OB20_PROTOCOL_VERSION_VALUE; + ob20protocol->header.request_id = rand() % UINT_MAX24; + ob20protocol->extra_info_list.list = NULL; + ob20protocol->extra_info_list.current = NULL; + ob20protocol->header.connection_id = conid; + ob20protocol->flt = NULL; + if (use_flt) { + ob20protocol->flt = malloc(sizeof(FLTInfo)); + if (OB_ISNULL(ob20protocol->flt)) { + // malloc error + ret = OB_ERROR; + } else if (OB_FAIL(flt_init(ob20protocol->flt))) { + // error + } else { + // printf("connect with ob20 and full link trace, connection id is %lu\n", conid); + } + } + } + + return ret; +} + +void ob20_end(Ob20Protocol *ob20protocol) +{ + if (OB_NOT_NULL(ob20protocol)) { + clear_extra_info(ob20protocol); + if (OB_NOT_NULL(ob20protocol->real_write_buffer)) { + free(ob20protocol->real_write_buffer); + ob20protocol->real_write_buffer = NULL; + ob20protocol->real_write_buffer_length = 0; + } + if (OB_NOT_NULL(ob20protocol->flt)) { + flt_end(ob20protocol->flt); + free(ob20protocol->flt); + ob20protocol->flt = NULL; + } + } +} + +void clear_extra_info(Ob20Protocol *ob20protocol) +{ + if (OB_NOT_NULL(ob20protocol)) { + ob20protocol->extra_info_list.current = ob20protocol->extra_info_list.list; + while (OB_NOT_NULL(ob20protocol->extra_info_list.list)) { + ob20protocol->extra_info_list.list = ob20protocol->extra_info_list.list->next; + list_free(ob20protocol->extra_info_list.current, 0); + ob20protocol->extra_info_list.current = ob20protocol->extra_info_list.list; + } + memset(&ob20protocol->extra_info_list, 0, sizeof(ob20protocol->extra_info_list)); + } +} + +void update_request_id(uint32_t *request_id) +{ + *request_id = *request_id + 1; + if (*request_id > UINT24_MAX) { + *request_id = 0; + } +} + +uchar *fill_protocol20_header(Ob20Protocol *ob20protocol, size_t len, size_t pkt_nr, size_t complen,uchar *buffer) +{ + size_t pos = 0; + + // printf("write request %u\n", ob20protocol->header.request_id); + + int3store(&buffer[NET_HEADER_SIZE], complen); + int3store(&buffer[pos], len); + buffer[3]=(uchar) (pkt_nr); + pos += NET_HEADER_SIZE + COMP_HEADER_SIZE; + + int2store(&buffer[pos], ob20protocol->header.magic_num); + pos += 2; + int2store(&buffer[pos], ob20protocol->header.version); + pos += 2; + int4store(&buffer[pos], ob20protocol->header.connection_id); + pos += 4; + int3store(&buffer[pos], ob20protocol->header.request_id); + pos += 3; + ob20protocol->header.pkt_seq = pkt_nr; + int1store(&buffer[pos], ob20protocol->header.pkt_seq); + pos += 1; + int4store(&buffer[pos], ob20protocol->header.payload_len); + pos += 4; + int4store(&buffer[pos], ob20protocol->header.flag.flags); + pos += 4; + int2store(&buffer[pos], ob20protocol->header.reserved); + pos += 2; + ob20protocol->header.header_checksum = ob_crc16(0, (uint8_t *)buffer, pos); + int2store(&buffer[pos], ob20protocol->header.header_checksum); + pos += 2; + + return buffer + pos; +} + + +// decode protocol20 header, pass the params pkt_len , pktnr and complen for checksum +int decode_protocol20_header(Ob20Protocol *ob20protocol, uchar *buffer, uint32_t pkt_len, uint32_t pkt_nr, uint32_t complen) +{ + int ret = 0; + size_t pos = 0; + uint16_t magic_num; + uint16_t version; + uint32_t connection_id; + uint32_t request_id; + uint8_t pkt_seq; + uint32_t payload_len; + Ob20ProtocolFlags flag; + uint16_t reserved; + uint16_t header_checksum; + uint16_t crc16 = 0; + char header_buffer[NET_HEADER_SIZE + COMP_HEADER_SIZE]; + + // decode + magic_num = uint2korr(&buffer[pos]); + pos += 2; + version = uint2korr(&buffer[pos]); + pos += 2; + connection_id = uint4korr(&buffer[pos]); + pos += 4; + request_id = uint3korr(&buffer[pos]); + pos += 3; + pkt_seq = uint1korr(&buffer[pos]); + pos += 1; + payload_len = uint4korr(&buffer[pos]); + pos += 4; + flag.flags = uint4korr(&buffer[pos]); + pos += 4; + reserved = uint2korr(&buffer[pos]); + pos += 2; + header_checksum = uint2korr(&buffer[pos]); + + // checksum + if (0 != header_checksum) { + int3store(header_buffer, pkt_len); + header_buffer[3] = pkt_nr; + int3store(&header_buffer[NET_HEADER_SIZE], complen); + crc16 = ob_crc16(crc16, (uint8_t *)header_buffer, NET_HEADER_SIZE + COMP_HEADER_SIZE); + crc16 = ob_crc16(crc16, (uint8_t *)buffer, pos); + } else { + crc16 = 0; + } + +#ifdef DEBUG_OB20 + printf("////////////////begin ob20 pkt [%u]//////////////////////////\n", pkt_nr); + printf("mysql pkt_len is %u\n", pkt_len); + printf("mysql pkt_nr is %u\n", pkt_nr); + printf("complen is %u\n", complen); + printf("magic_num is %u\n", magic_num); + printf("version is %u\n", version); + printf("connection_id is %u\n", connection_id); + printf("request_id is %u\n", request_id); + printf("ob20 pkt_seq is %u\n", pkt_seq); + printf("ob20 payload_len is %u\n", payload_len); + printf("flag is %u\n", flag.flags); + printf("header checksum is %u\n", header_checksum); + printf("////////////////end ob20 pkt [%u]//////////////////////////\n", pkt_nr); +#endif + + if (crc16 != header_checksum) { + ret = 1; // error + } else if (magic_num != ob20protocol->header.magic_num) { + ret = 1; // error + } else if (version != ob20protocol->header.version) { + ret = 1; // error + } else if (connection_id != ob20protocol->header.connection_id) { + ret = 1; // error + } else if (request_id != ob20protocol->header.request_id) { + ret = 1; // error + } else { + //check request id and pkt_seq + if (pkt_seq != ob20protocol->header.pkt_seq + 1) { + // printf("pkt_seq check error, local is %u, header is %u\n", ob20protocol->header.pkt_seq, pkt_seq); + ret = 1; // error + } else { + ob20protocol->header.flag = flag; + ob20protocol->header.payload_len = payload_len; + ob20protocol->header.pkt_seq = pkt_seq; + ob20protocol->header.reserved = reserved; + ob20protocol->header.header_checksum = header_checksum; + } + } + + return ret; +} + +int extrainfo_serialize_flt(char *buf, const int64_t len, int64_t *ppos, void *data) +{ + int ret = OB_SUCCESS; + int64_t pos = *ppos; + FLTValueData *flt_data = (FLTValueData *)data; + // data是flt_build_request中生成,函数中需要保证生成string类型 + int64_t v_len = flt_data->length; + + if (OB_ISNULL(buf)) { + ret = OB_ERROR; + } else if (len < pos + EXTRA_TYPE_LENGTH + EXTRA_LEN_LENGTH + v_len) { + ret = OB_ERROR; + } else { + int2store(buf + pos, FULL_TRC); + pos += 2; + int4store(buf + pos, v_len); + pos += 4; + + memcpy(buf + pos, flt_data->value_data_, v_len); + pos += v_len; + + *ppos = pos; + } + return ret; +} + +int extrainfo_deserialize_flt(const char *buf, const int64_t len, int64_t *ppos, void *data, const int64_t v_len) +{ + int ret = OB_SUCCESS; + int64_t pos = *ppos; + Ob20Protocol *ob20protocol = (Ob20Protocol *)data; + UNUSED(ob20protocol); + UNUSED(v_len); + while (OB_SUCC(ret) && pos < len) { + int32_t tmp_v_len = 0; + int64_t tmp_v_pos = 0; + int16_t type = FLT_EXTRA_INFO_ID_END; + void *flt_info = NULL; + + if (OB_FAIL(flt_resolve_type_and_len(buf, len, &pos, &type, &tmp_v_len))) { + ret = OB_ERROR; + } else { + my_bool know_flt_info = TRUE; + + if (FLT_CONTROL_INFO == type) { + flt_info = &ob20protocol->flt->control_info_; + } else if (FLT_APP_INFO == type) { + flt_info = &ob20protocol->flt->app_info_; + } else if (FLT_QUERY_INFO == type) { + flt_info = &ob20protocol->flt->query_info_; + } else if (FLT_SPAN_INFO == type) { + flt_info = &ob20protocol->flt->control_info_; + } else { + know_flt_info = FALSE; + } + if (TRUE == know_flt_info) { + if (OB_ISNULL(flt_info)) { + ret = OB_ERROR; + } else { + ((FLTInfoBase *)flt_info)->type_ = (FullLinkTraceExtraInfoType)type; + if (OB_FAIL(flt_deserialize_extra_info(buf + pos, tmp_v_len, &tmp_v_pos, (FullLinkTraceExtraInfoType)type, flt_info))) { + ret = OB_ERROR; + } else { + pos += tmp_v_len; + } + } + } else { + // 对于不识别的key,需要跳过 + pos += tmp_v_len; + } + } + } + if (OB_SUCC(ret)) { + *ppos = pos; // 最后设置好pos + } + return ret; +} + +int64_t extrainfo_get_serialize_size_flt(void *data) +{ + int64_t ret = 0; + FLTValueData *flt_data = (FLTValueData *)data; + if (OB_NOT_NULL(flt_data)) { + ret = EXTRA_TYPE_LENGTH + EXTRA_LEN_LENGTH + flt_data->length; + } + return ret; +} + +uchar *fill_protocol20_extra_info(Ob20Protocol *ob20protocol, uchar *buffer, size_t buffer_len) +{ + char *packet = (char *)buffer; + int64_t pos = 0; + int ret = OB_SUCCESS; + + if (OB_ISNULL(ob20protocol) || OB_ISNULL(ob20protocol->extra_info_list.list)) { + // do nothing + } else { + Ob20ProtocolExtraInfo *extra_info_item; + + ob20protocol->extra_info_list.current = ob20protocol->extra_info_list.list; + + while (NULL != ob20protocol->extra_info_list.current) { + extra_info_item = ob20protocol->extra_info_list.current->data; + if (OB_ISNULL(extra_serialize_funcs[extra_info_item->key].serialize_func)) { + // 函数指针没有设置, do nothing, 跳到下一个key + } if (OB_FAIL(extra_serialize_funcs[extra_info_item->key].serialize_func(packet + pos, buffer_len, &pos, extra_info_item->value))) { + return buffer; + } + ob20protocol->extra_info_list.current = ob20protocol->extra_info_list.current->next; + } + } + + return (uchar *)(packet + pos); +} + +size_t get_protocol20_extra_info_length(Ob20Protocol *ob20protocol) +{ + size_t ret = 0; + size_t item_size; + Ob20ProtocolExtraInfo *extra_info_item; + ob20protocol->extra_info_list.current = ob20protocol->extra_info_list.list; + + while(NULL != ob20protocol->extra_info_list.current) { + extra_info_item = (Ob20ProtocolExtraInfo *)ob20protocol->extra_info_list.current->data; + if (OB_ISNULL(extra_serialize_funcs[extra_info_item->key].get_serialize_size_func)) { + item_size = 0; + } else { + item_size = extra_serialize_funcs[extra_info_item->key].get_serialize_size_func(extra_info_item->value); + } + + ob20protocol->extra_info_list.current = ob20protocol->extra_info_list.current->next; + ret += item_size; + } + return ret; +} + +int decode_protocol20_extra_info(Ob20Protocol *ob20protocol, uchar *buffer) +{ + int ret = OB_SUCCESS; + int64_t extra_info_len = 0; + int64_t pos = 0, check_pos; + ExtraInfoKeyType key_type; + const char *row = (char *)buffer; + size_t value_len; + + extra_info_len = uint4korr(row); + row += 4; + + while (OB_SUCC(ret) && pos < extra_info_len) { + // key is 2 bytes + key_type = uint2korr(row + pos); + pos += 2; + value_len = uint4korr(row + pos); + pos += 4; + + check_pos = pos; + if (OB_ISNULL(extra_serialize_funcs[key_type].deserialize_func)) { + // 如果函数指针没有设置,直接跳过value不解析 + pos += value_len; + } else if (OB_FAIL(extra_serialize_funcs[key_type].deserialize_func(row, extra_info_len, &pos, ob20protocol, sizeof(*ob20protocol)))) { + // error + } else if (pos != check_pos + (int64_t)value_len) { + // error + ret = OB_ERROR; + } + } + return ret; +} + +int ob20_set_extra_info(MYSQL *mysql, ExtraInfoKeyType key, void *value) +{ + int ret = OB_SUCCESS; + Ob20Protocol *ob20protocol = mysql->net.ob20protocol; + LIST *extra_info_list_item = NULL; + Ob20ProtocolExtraInfo *extra_info_item = NULL; + + if (!(extra_info_list_item = ma_multi_malloc(0, + &extra_info_list_item, sizeof(LIST), + &extra_info_item, sizeof(Ob20ProtocolExtraInfo), + NULL))) { + ret = OB_ERROR; + } else { + extra_info_item->key = key; + extra_info_item->value = value; + extra_info_list_item->data = extra_info_item; + ob20protocol->extra_info_list.list = list_add(ob20protocol->extra_info_list.list, extra_info_list_item); + } + return ret; +} \ No newline at end of file diff --git a/libmariadb/ob_rwlock.c b/libmariadb/ob_rwlock.c new file mode 100644 index 0000000..8f1cf76 --- /dev/null +++ b/libmariadb/ob_rwlock.c @@ -0,0 +1,189 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ + +#include "ob_rwlock.h" + +#ifdef _WIN32 + +int ob_mutex_init(ob_mutex_t *ob_mutex, const ob_mutexattr_t *attr) +{ + InitializeCriticalSection(ob_mutex); + return 0; +} + +int ob_mutex_lock(ob_mutex_t *ob_mutex) +{ + EnterCriticalSection(ob_mutex); + return 0; +} + +int ob_mutex_trylock(ob_mutex_t *ob_mutex) +{ + int ret = 0; + + if (!TryEnterCriticalSection(ob_mutex)) { + ret = EBUSY; + } else if (ob_mutex->RecursionCount > 1) { + LeaveCriticalSection(ob_mutex); + ret = EBUSY; + } else { + ret = 0; + } + + return ret; +} + +int ob_mutex_unlock(ob_mutex_t *ob_mutex) +{ + LeaveCriticalSection(ob_mutex); + return 0; +} + +int ob_mutex_destroy(ob_mutex_t *ob_mutex) +{ + DeleteCriticalSection(ob_mutex); + return 0; +} + +int ob_rw_init(ob_rw_lock_t *ob_rwp) +{ + InitializeSRWLock(&ob_rwp->ob_srwlock); + ob_rwp->is_exclusive = FALSE; + return 0; +} + +int ob_rw_destroy(ob_rw_lock_t *ob_rwp) +{ + // do nothing + return 0; +} + +int ob_rw_rdlock(ob_rw_lock_t *ob_rwp) +{ + AcquireSRWLockShared(&ob_rwp->ob_srwlock); + return 0; +} + +int ob_rw_tryrdlock(ob_rw_lock_t *ob_rwp) +{ + int ret = 0; + if (!TryAcquireSRWLockShared(&ob_rwp->ob_srwlock)) { + ret = EBUSY; + } else { + ret = 0; + } + return ret; +} + +int ob_rw_wrlock(ob_rw_lock_t *ob_rwp) +{ + AcquireSRWLockExclusive(&ob_rwp->ob_srwlock); + ob_rwp->is_exclusive= TRUE; + return 0; +} + +int ob_rw_trywrlock(ob_rw_lock_t *ob_rwp) +{ + int ret = 0;; + if (!TryAcquireSRWLockExclusive(&ob_rwp->ob_srwlock)) { + ret = EBUSY; + } else { + ob_rwp->is_exclusive= TRUE; + ret = 0; + } + return ret; +} + +int ob_rw_unlock(ob_rw_lock_t *ob_rwp) +{ + if (ob_rwp->is_exclusive) { + ob_rwp->is_exclusive= FALSE; + ReleaseSRWLockExclusive(&ob_rwp->ob_srwlock); + } else { + ReleaseSRWLockShared(&ob_rwp->ob_srwlock); + } + return 0; +} +#else + +int ob_mutex_init(ob_mutex_t *ob_mutex, const ob_mutexattr_t *attr) +{ + return pthread_mutex_init(ob_mutex, (pthread_mutexattr_t *)attr); +} + +int ob_mutex_lock(ob_mutex_t *ob_mutex) +{ + return pthread_mutex_lock(ob_mutex); +} + +int ob_mutex_trylock(ob_mutex_t *ob_mutex) +{ + return pthread_mutex_trylock(ob_mutex); +} + +int ob_mutex_unlock(ob_mutex_t *ob_mutex) +{ + return pthread_mutex_unlock(ob_mutex); +} + +int ob_mutex_destroy(ob_mutex_t *ob_mutex) +{ + return pthread_mutex_destroy(ob_mutex); +} + +int ob_rw_init(ob_rw_lock_t *ob_rwp) +{ + return pthread_rwlock_init(ob_rwp, NULL); +} + +int ob_rw_destroy(ob_rw_lock_t *ob_rwp) +{ + return pthread_rwlock_destroy(ob_rwp); +} + +int ob_rw_rdlock(ob_rw_lock_t *ob_rwp) +{ + return pthread_rwlock_rdlock(ob_rwp); +} + +int ob_rw_tryrdlock(ob_rw_lock_t *ob_rwp) +{ + return pthread_rwlock_tryrdlock(ob_rwp); +} + +int ob_rw_wrlock(ob_rw_lock_t *ob_rwp) +{ + return pthread_rwlock_wrlock(ob_rwp); +} + +int ob_rw_trywrlock(ob_rw_lock_t *ob_rwp) +{ + return pthread_rwlock_trywrlock(ob_rwp); +} + +int ob_rw_unlock(ob_rw_lock_t *ob_rwp) +{ + return pthread_rwlock_unlock(ob_rwp); +} +#endif + diff --git a/libmariadb/ob_serialize.c b/libmariadb/ob_serialize.c new file mode 100644 index 0000000..4dffaf4 --- /dev/null +++ b/libmariadb/ob_serialize.c @@ -0,0 +1,510 @@ +#include "ob_serialize.h" + +inline int64_t encoded_length_vi64(int64_t val) +{ + uint64_t __v = (uint64_t)(val); + int64_t need_bytes = 0; + if (__v <= OB_MAX_V1B) { + need_bytes = 1; + } else if (__v <= OB_MAX_V2B) { + need_bytes = 2; + } else if (__v <= OB_MAX_V3B) { + need_bytes = 3; + } else if (__v <= OB_MAX_V4B) { + need_bytes = 4; + } else if (__v <= OB_MAX_V5B) { + need_bytes = 5; + } else if (__v <= OB_MAX_V6B) { + need_bytes = 6; + } else if (__v <= OB_MAX_V7B) { + need_bytes = 7; + } else if (__v <= OB_MAX_V8B) { + need_bytes = 8; + } else if (__v <= OB_MAX_V9B) { + need_bytes = 9; + } else { + need_bytes = 10; + } + return need_bytes; +} + +/** + * @brief Encode a integer (up to 64bit) in variable length encoding + * + * @param buf pointer to the destination buffer + * @param end the end pointer to the destination buffer + * @param val value to encode + * + * @return true - success, false - failed + */ +inline int encode_vi64(char *buf, const int64_t buf_len, int64_t *ppos, int64_t val) +{ + int64_t pos = *ppos; + uint64_t __v = (uint64_t)(val); + int ret = ((NULL != buf) && + ((buf_len - pos) >= encoded_length_vi64(__v))) ? OB_SUCCESS : OB_SIZE_OVERFLOW; + if (OB_SUCC(ret)) { + while (__v > OB_MAX_V1B) { + *(buf + pos++) = (int8_t)((__v) | 0x80); + __v >>= 7; + } + if (__v <= OB_MAX_V1B) { + *(buf + pos++) = (int8_t)((__v) & 0x7f); + } + *ppos = pos; + } + return ret; +} + +inline int decode_vi64(const char *buf, const int64_t data_len, int64_t *ppos, int64_t *val) +{ + uint64_t __v = 0; + uint32_t shift = 0; + int64_t pos = *ppos; + int64_t tmp_pos = pos; + int ret = OB_SUCCESS; + while (OB_SUCC(ret) && ((*(buf + tmp_pos)) & 0x80)) { + if (data_len - tmp_pos < 1) { + ret = OB_DESERIALIZE_ERROR; + break; + } + __v |= ((uint64_t)(*(buf + tmp_pos++)) & 0x7f) << shift; + shift += 7; + } + if (OB_SUCC(ret)) { + if (data_len - tmp_pos < 1) { + ret = OB_DESERIALIZE_ERROR; + } else { + __v |= (((uint64_t)(*(buf + tmp_pos++)) & 0x7f) << shift); + *val = (int64_t)(__v); + pos = tmp_pos; + } + *ppos = pos; + } + return ret; +} + +inline int64_t encoded_length_vi32(int32_t val) +{ + uint32_t __v = (uint64_t)(val); + int64_t need_bytes = 0; + if (__v <= OB_MAX_V1B) { + need_bytes = 1; + } else if (__v <= OB_MAX_V2B) { + need_bytes = 2; + } else if (__v <= OB_MAX_V3B) { + need_bytes = 3; + } else if (__v <= OB_MAX_V4B) { + need_bytes = 4; + } else { + need_bytes = 5; + } + return need_bytes; + +} + +/** + * @brief Encode a integer (up to 32bit) in variable length encoding + * + * @param buf pointer to the destination buffer + * @param end the end pointer to the destination buffer + * @param val value to encode + * + * @return true - success, false - failed + */ + +inline int encode_vi32(char *buf, const int64_t buf_len, int64_t *ppos, int32_t val) +{ + int64_t pos = *ppos; + uint32_t __v = (uint32_t)(val); + int ret = ((NULL != buf) && + ((buf_len - pos) >= encoded_length_vi32(val))) ? OB_SUCCESS : OB_SIZE_OVERFLOW; + if (OB_SUCC(ret)) { + while (__v > OB_MAX_V1B) { + *(buf + pos++) = (int8_t)((__v) | 0x80); + __v >>= 7; + } + if (__v <= OB_MAX_V1B) { + *(buf + pos++) = (int8_t)((__v) & 0x7f); + } + *ppos = pos; + } + return ret; + +} + +inline int decode_vi32(const char *buf, const int64_t data_len, int64_t *ppos, int32_t *val) +{ + uint32_t __v = 0; + uint32_t shift = 0; + int ret = OB_SUCCESS; + int64_t pos = *ppos; + int64_t tmp_pos = pos; + while (OB_SUCC(ret) && ((*(buf + tmp_pos)) & 0x80)) { + if (data_len - tmp_pos < 1) { + ret = OB_DESERIALIZE_ERROR; + break; + } + __v |= ((uint32_t)(*(buf + tmp_pos++)) & 0x7f) << shift; + shift += 7; + } + if (OB_SUCC(ret)) { + if (data_len - tmp_pos < 1) { + ret = OB_DESERIALIZE_ERROR; + } else { + __v |= ((uint32_t)(*(buf + tmp_pos++)) & 0x7f) << shift; + *val = (int32_t)(__v); + pos = tmp_pos; + } + *ppos = pos; + } + return ret; +} + +inline int64_t encoded_length_i8(int8_t val) +{ + return (int64_t)(sizeof(val)); +} + +inline int encode_i8(char *buf, const int64_t buf_len, int64_t *ppos, int8_t val) +{ + int64_t pos = *ppos; + int ret = ((NULL != buf) && + ((buf_len - pos) >= (int64_t)(sizeof(val)))) ? OB_SUCCESS : OB_SIZE_OVERFLOW; + if (OB_SUCC(ret)) { + *(buf + pos++) = val; + *ppos = pos; + } + return ret; +} + +inline int decode_i8(const char *buf, const int64_t data_len, int64_t *ppos, int8_t *val) +{ + int pos = *ppos; + int ret = (NULL != buf && data_len - pos >= 1) ? OB_SUCCESS : OB_DESERIALIZE_ERROR; + if (OB_SUCC(ret)) { + *val = *(buf + pos++); + *ppos = pos; + } + return ret; +} + +inline int encode_i64(char *buf, const int64_t buf_len, int64_t *ppos, int64_t val) +{ + int pos = *ppos; + int ret = ((NULL != buf) && + ((buf_len - pos) >= (int64_t)(sizeof(val)))) ? OB_SUCCESS : OB_ERROR; + if (OB_SUCC(ret)) { + *(buf + pos++) = (char)(((val) >> 56) & 0xff); + *(buf + pos++) = (char)(((val) >> 48) & 0xff); + *(buf + pos++) = (char)(((val) >> 40) & 0xff); + *(buf + pos++) = (char)(((val) >> 32) & 0xff); + *(buf + pos++) = (char)(((val) >> 24) & 0xff); + *(buf + pos++) = (char)(((val) >> 16) & 0xff); + *(buf + pos++) = (char)(((val) >> 8) & 0xff); + *(buf + pos++) = (char)((val) & 0xff); + + *ppos = pos; + } + return ret; +} + +inline int decode_i64(const char *buf, const int64_t data_len, int64_t *ppos, int64_t *val) +{ + int pos = *ppos; + int ret = (NULL != buf && data_len - pos >= 8) ? OB_SUCCESS : OB_ERROR; + if (OB_SUCC(ret)) { + *val = (((int64_t)((*(buf + pos++))) & 0xff)) << 56; + *val |= (((int64_t)((*(buf + pos++))) & 0xff)) << 48; + *val |= (((int64_t)((*(buf + pos++))) & 0xff)) << 40; + *val |= (((int64_t)((*(buf + pos++))) & 0xff)) << 32; + *val |= (((int64_t)((*(buf + pos++))) & 0xff)) << 24; + *val |= (((int64_t)((*(buf + pos++))) & 0xff)) << 16; + *val |= (((int64_t)((*(buf + pos++))) & 0xff)) << 8; + *val |= (((int64_t)((*(buf + pos++))) & 0xff)); + } + return ret; +} + +inline int encode_int64_t(char *buf, const int64_t buf_len, int64_t *pos, int64_t val) +{ + return encode_vi64(buf, buf_len, pos, val); +} +inline int encode_uint64_t(char *buf, const int64_t buf_len, int64_t *pos, uint64_t val) +{ + return encode_vi64(buf, buf_len, pos, (int64_t)(val)); +} +inline int encode_int32_t(char *buf, const int64_t buf_len, int64_t *pos, int32_t val) +{ + return encode_vi32(buf, buf_len, pos, val); +} +inline int encode_uint32_t(char *buf, const int64_t buf_len, int64_t *pos, uint32_t val) +{ + return encode_vi32(buf, buf_len, pos, (int32_t)(val)); +} +inline int encode_int16_t(char *buf, const int64_t buf_len, int64_t *pos, int16_t val) +{ + return encode_vi32(buf, buf_len, pos, (int32_t)(val)); +} +inline int encode_uint16_t(char *buf, const int64_t buf_len, int64_t *pos, uint16_t val) +{ + return encode_vi32(buf, buf_len, pos, (int32_t)(val)); +} +inline int encode_int8_t(char *buf, const int64_t buf_len, int64_t *pos, int8_t val) +{ + return encode_i8(buf, buf_len, pos, val); +} +inline int encode_uint8_t(char *buf, const int64_t buf_len, int64_t *pos, uint8_t val) +{ + return encode_i8(buf, buf_len, pos, (int8_t)(val)); +} + + +inline int decode_int8_t(const char *buf, const int64_t data_len, int64_t *pos, int8_t *val) +{ + *val = 0; + return decode_i8(buf, data_len, pos, val); +} +inline int decode_uint8_t(const char *buf, const int64_t data_len, int64_t *pos, uint8_t *val) +{ + *val = 0; + return decode_i8(buf, data_len, pos, (int8_t*)val); +} +inline int decode_int16_t(const char *buf, const int64_t data_len, int64_t *pos, int16_t *val) +{ + int32_t v = 0; + int ret = decode_vi32(buf, data_len, pos, &v); + *val = (int16_t)(v); + return ret; +} +inline int decode_uint16_t(const char *buf, const int64_t data_len, int64_t *pos, uint16_t *val) +{ + int32_t v = 0; + int ret = decode_vi32(buf, data_len, pos, &v); + *val = (uint16_t)(v); + return ret; +} +inline int decode_int32_t(const char *buf, const int64_t data_len, int64_t *pos, int32_t *val) +{ + *val = 0; + return decode_vi32(buf, data_len, pos, val); +} +inline int decode_uint32_t(const char *buf, const int64_t data_len, int64_t *pos, uint32_t *val) +{ + *val = 0; + return decode_vi32(buf, data_len, pos, (int32_t *)val); +} +inline int decode_int64_t(const char *buf, const int64_t data_len, int64_t *pos, int64_t *val) +{ + *val = 0; + return decode_vi64(buf, data_len, pos, val); +} +inline int decode_uint64_t(const char *buf, const int64_t data_len, int64_t *pos, uint64_t *val) +{ + *val = 0; + return decode_vi64(buf, data_len, pos, (int64_t *)val); +} + +inline int64_t encoded_length_int64_t(int64_t val) +{ + return encoded_length_vi64(val); +} +inline int64_t encoded_length_uint64_t(uint64_t val) +{ + return encoded_length_vi64((int64_t)val); +} +inline int64_t encoded_length_int32_t(int32_t val) +{ + return encoded_length_vi32(val); +} +inline int64_t encoded_length_uint32_t(uint32_t val) +{ + return encoded_length_vi32((int32_t)val); +} +inline int64_t encoded_length_int16_t(int16_t val) +{ + return encoded_length_vi32(val); +} +inline int64_t encoded_length_uint16_t(uint16_t val) +{ + return encoded_length_vi32((int32_t)val); +} +inline int64_t encoded_length_int8_t(int8_t unused) +{ + UNUSED(unused); + return 1; +} +inline int64_t encoded_length_uint8_t(uint8_t unused) +{ + UNUSED(unused); + return 1; +} + +inline int64_t encoded_length_float(float val) +{ + int32_t tmp = 0; + memcpy(&tmp, &val, sizeof(tmp)); + return encoded_length_vi32(tmp); +} + +inline int encode_float(char *buf, const int64_t buf_len, int64_t *pos, float val) +{ + int32_t tmp = 0; + memcpy(&tmp, &val, sizeof(tmp)); + return encode_vi32(buf, buf_len, pos, tmp); +} + +inline int decode_float(const char *buf, const int64_t data_len, int64_t *pos, float *val) +{ + int32_t tmp = 0; + int ret = OB_SUCCESS; + if ((ret = decode_vi32(buf, data_len, pos, &tmp)) == 0) { + memcpy(val, &tmp, sizeof(*val)); + } + return ret; +} + +inline int64_t encoded_length_double(double val) +{ + int64_t tmp = 0; + memcpy(&tmp, &val, sizeof(tmp)); + return encoded_length_vi64(tmp); +} + +inline int encode_double(char *buf, const int64_t buf_len, int64_t *pos, double val) +{ + int64_t tmp = 0; + memcpy(&tmp, &val, sizeof(tmp)); + return encode_vi64(buf, buf_len, pos, tmp); +} + +inline int decode_double(const char *buf, const int64_t data_len, int64_t *pos, double *val) +{ + int64_t tmp = 0; + int ret = OB_SUCCESS; + if ((ret = decode_vi64(buf, data_len, pos, &tmp)) == 0) { + memcpy(val, &tmp, sizeof(*val)); + } + return ret; +} + + +/** + * @brief Computes the encoded length of vstr(int64,data,null) + * + * @param len string length + * + * @return the encoded length of str + */ +inline int64_t encoded_length_vstr_with_len(int64_t len) +{ + return encoded_length_vi64(len) + len + 1; +} + +inline int64_t encoded_length_vstr(const char *str) +{ + return encoded_length_vstr_with_len(str ? (int64_t)(strlen(str) + 1) : 0); +} + +/** + * @brief get the decoded length of data len of vstr + * won't change the pos + * @return the length of data + */ +inline int64_t decoded_length_vstr(const char *buf, const int64_t data_len, int64_t pos) +{ + int64_t len = -1; + int64_t tmp_pos = pos; + if (NULL == buf || data_len < 0 || pos < 0) { + len = -1; + } else if (decode_vi64(buf, data_len, &tmp_pos, &len) != 0) { + len = -1; + } + return len; +} + +/** + * @brief Encode a buf as vstr(int64,data,null) + * + * @param buf pointer to the destination buffer + * @param vbuf pointer to the start of the input buffer + * @param len length of the input buffer + */ +inline int encode_vstr_with_len(char *buf, const int64_t buf_len, int64_t *ppos, const void *vbuf, + int64_t len) +{ + int64_t pos = *ppos; + int ret = ((NULL != buf) && (len >= 0) + && ((buf_len - pos) >= (int32_t)(encoded_length_vstr_with_len(len)))) + ? OB_SUCCESS : OB_SIZE_OVERFLOW; + if (OB_SUCC(ret)) { + /** + * even through it's a null string, we can serialize it with + * lenght 0, and following a '\0' + */ + ret = encode_vi64(buf, buf_len, &pos, len); + if (OB_SUCCESS == ret && len > 0 && NULL != vbuf) { + memcpy(buf + pos, vbuf, len); + pos += len; + } + *(buf + pos++) = 0; + *ppos = pos; + } + return ret; +} + +inline int encode_vstr(char *buf, const int64_t buf_len, int64_t *ppos, const char *s) +{ + return encode_vstr_with_len(buf, buf_len, ppos, s, s ? strlen(s) + 1 : 0); +} + +inline const char *decode_vstr_nocopy(const char *buf, const int64_t data_len, int64_t *ppos, int64_t *lenp) +{ + int64_t pos = *ppos; + const char *str = 0; + int64_t tmp_len = 0; + int64_t tmp_pos = pos; + + if ((NULL == buf) || (data_len < 0) || (pos < 0) || (NULL == lenp)) { + //just _return_; + } else if (decode_vi64(buf, data_len, &tmp_pos, &tmp_len) != OB_SUCCESS) { + *lenp = -1; + } else if (tmp_len >= 0) { + if (data_len - tmp_pos >= tmp_len) { + str = buf + tmp_pos; + *lenp = tmp_len++; + tmp_pos += tmp_len; + pos = tmp_pos; + } else { + *lenp = -1; + } + } + *ppos = pos; + return str; +} + +inline const char *decode_vstr(const char *buf, const int64_t data_len, int64_t *ppos, + char *dest, int64_t buf_len, int64_t *lenp) +{ + int64_t pos = *ppos; + const char *str = 0; + int64_t tmp_len = 0; + int64_t tmp_pos = pos; + if ((NULL == buf) || (data_len < 0) || (pos < 0) || (0 == dest) || buf_len < 0 || (NULL == lenp)) { + //just _return_; + } else if (decode_vi64(buf, data_len, &tmp_pos, &tmp_len) != 0 || tmp_len > buf_len) { + *lenp = -1; + } else if (tmp_len >= 0) { + if (data_len - tmp_pos >= tmp_len) { + str = buf + tmp_pos; + *lenp = tmp_len++; + memcpy(dest, str, *lenp); + tmp_pos += tmp_len; + pos = tmp_pos; + } else { + *lenp = -1; + } + } + *ppos = pos; + return str; +} \ No newline at end of file diff --git a/libmariadb/ob_strtoll10.c b/libmariadb/ob_strtoll10.c new file mode 100644 index 0000000..23d33db --- /dev/null +++ b/libmariadb/ob_strtoll10.c @@ -0,0 +1,38 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ + +#include +#include /* Needed for MY_ERRNO_ERANGE */ +#include +#include +#include +#define MY_ERRNO_EDOM 33 +#define MY_ERRNO_ERANGE 34 + +longlong strtoll10(const char *nptr, char **endptr, int *error) +{ + longlong ret = 0; + ret = strtoll(nptr, endptr, 10); + *error = errno; + return ret; +} \ No newline at end of file diff --git a/libmariadb/ob_thread.c b/libmariadb/ob_thread.c new file mode 100644 index 0000000..d6af287 --- /dev/null +++ b/libmariadb/ob_thread.c @@ -0,0 +1,254 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ +#include "ob_thread.h" + +#ifdef _WIN32 +#include +#include + +unsigned int __stdcall ob_win_thread_start(void *win_start_param) +{ + struct ob_thread_start_param *ob_start_param= (struct ob_thread_start_param *)win_start_param; + ob_start_routine ob_start_func= ob_start_param->func; + void *ob_start_arg= ob_start_param->arg; + free(win_start_param); + (*ob_start_func)(ob_start_arg); + return 0; +} + +ob_thread_t ob_thread_self() +{ + return GetCurrentThreadId(); +} + +int ob_thread_equal(ob_thread_t ob_thread1, ob_thread_t ob_thread2) +{ + return (ob_thread1 == ob_thread2); +} + +int ob_thread_attr_init(ob_thread_attr_t *ob_thread_attr) +{ + ob_thread_attr->ob_thead_state= OB_THREAD_CREATE_JOINABLE; + ob_thread_attr->ob_thread_stack_size= 0; + return 0; +} + +int ob_thread_attr_destroy(ob_thread_attr_t *ob_thread_attr) +{ + ob_thread_attr->ob_thead_state= OB_THREAD_CREATE_JOINABLE; + ob_thread_attr->ob_thread_stack_size= 0; + return 0; +} + +int ob_thread_attr_setstacksize(ob_thread_attr_t *ob_thread_attr, size_t ob_stacksize) +{ + ob_thread_attr->ob_thread_stack_size= (DWORD)ob_stacksize; + return 0; +} + +int ob_thread_attr_setdetachstate(ob_thread_attr_t *ob_thread_attr, int ob_ob_thead_state) +{ + ob_thread_attr->ob_thead_state= ob_ob_thead_state; + return 0; +} + +int ob_thread_attr_getstacksize(ob_thread_attr_t *ob_thread_attr, size_t *ob_stacksize) +{ + *ob_stacksize= (size_t)ob_thread_attr->ob_thread_stack_size; + return 0; +} + +void ob_thread_yield() +{ + SwitchToThread(); +} + +int ob_thread_once(ob_thread_once_t *ob_once_control, void (*ob_routine)(void)) +{ + int ret = 0; + LONG ob_thread_state; + + if (OB_THREAD_ONCE_DONE == *ob_once_control) { + ret = 0; + } else { + ob_thread_state = InterlockedCompareExchange(ob_once_control, OB_THREAD_ONCE_INPROGRESS, OB_THREAD_ONCE_INIT); + + switch(ob_thread_state) + { + case OB_THREAD_ONCE_DONE: + { + // do nothing + break; + } + case OB_THREAD_ONCE_INPROGRESS: + { + while(OB_THREAD_ONCE_INPROGRESS == *ob_once_control) { + Sleep(1); // sleep + } + break; + } + case OB_THREAD_ONCE_INIT: + { + (*ob_routine)(); + *ob_once_control = OB_THREAD_ONCE_DONE; + break; + } + } + } + return ret; +} + +int ob_thread_create(ob_thread_handle *ob_thread, const ob_thread_attr_t *ob_thread_attr, + ob_start_routine ob_thread_func, void *ob_thread_arg) +{ + int ret = 0; + unsigned int ob_thread_stack_size; + struct ob_thread_start_param *ob_thread_param; + + if (NULL == (ob_thread_param = (struct ob_thread_start_param *)malloc(sizeof(*ob_thread_param)))) { + ret = 1; // error + } else { + ob_thread_param->arg = ob_thread_arg; + ob_thread_param->func = ob_thread_func; + ob_thread_stack_size = ob_thread_attr ? ob_thread_attr->ob_thread_stack_size : 0; + ob_thread->handle= (HANDLE)_beginthreadex(NULL, ob_thread_stack_size, ob_win_thread_start, ob_thread_param, 0, &ob_thread->thread); + if (NULL == ob_thread->handle) { + // error, free param + free(ob_thread_param); + ret = 1; + } else if (ob_thread_attr && OB_THREAD_CREATE_DETACHED == ob_thread_attr->ob_thead_state) { + CloseHandle(ob_thread->handle); + ob_thread->handle= NULL; + } + } + if (0 != ret) { + ob_thread->handle= NULL; + ob_thread->thread= 0; + } + return ret; +} + +int ob_thread_join(ob_thread_handle *ob_thread, void **ob_join_ptr) +{ + int ret = 0; + if (WAIT_OBJECT_0 != WaitForSingleObject(ob_thread->handle, INFINITE)) { + ret = 1; + } else if (ob_thread->handle) { + CloseHandle(ob_thread->handle); + } + if (0 != ret) { + ob_thread->thread= 0; + ob_thread->handle= NULL; + } + return ret; +} + +int ob_thread_cancel(ob_thread_handle *ob_thread) +{ + int ret = 0; + BOOL terminate_ret = FALSE; + if (ob_thread->handle) { + if (FALSE == (terminate_ret = TerminateThread(ob_thread->handle, 0))) { + errno= EINVAL; + ret = -1; + } else { + CloseHandle(ob_thread->handle); + } + } + return ret; +} + +void ob_thread_exit(void *ob_thread_exit_ptr) +{ + _endthreadex(0); +} +#else +// for unix + +ob_thread_t ob_thread_self() +{ + return pthread_self(); +} + +int ob_thread_equal(ob_thread_t ob_thread1, ob_thread_t ob_thread2) +{ + return pthread_equal(ob_thread1, ob_thread2); +} + +int ob_thread_attr_init(ob_thread_attr_t *ob_thread_attr) +{ + return pthread_attr_init(ob_thread_attr); +} + +int ob_thread_attr_destroy(ob_thread_attr_t *ob_thread_attr) +{ + return pthread_attr_destroy(ob_thread_attr); +} + +int ob_thread_attr_setstacksize(ob_thread_attr_t *ob_thread_attr, size_t ob_stacksize) +{ + return pthread_attr_setstacksize(ob_thread_attr, ob_stacksize); +} + +int ob_thread_attr_setdetachstate(ob_thread_attr_t *ob_thread_attr, int ob_ob_thead_state) +{ + return pthread_attr_setdetachstate(ob_thread_attr, ob_ob_thead_state); +} + +int ob_thread_attr_getstacksize(ob_thread_attr_t *ob_thread_attr, size_t *ob_stacksize) +{ + return pthread_attr_getstacksize(ob_thread_attr, ob_stacksize); +} + +void ob_thread_yield() +{ + sched_yield(); +} + +int ob_thread_once(ob_thread_once_t *ob_once_control, void (*ob_routine)(void)) +{ + return pthread_once(ob_once_control, ob_routine); +} + +int ob_thread_create(ob_thread_handle *ob_thread, const ob_thread_attr_t *ob_thread_attr, + ob_start_routine ob_thread_func, void *ob_thread_arg) +{ + return pthread_create(&ob_thread->thread, ob_thread_attr, ob_thread_func, ob_thread_arg); +} + +int ob_thread_join(ob_thread_handle *ob_thread, void **ob_join_ptr) +{ + return pthread_join(ob_thread->thread, ob_join_ptr); +} + +int ob_thread_cancel(ob_thread_handle *ob_thread) +{ + return pthread_cancel(ob_thread->thread); +} + +void ob_thread_exit(void *ob_thread_exit_ptr) +{ + pthread_exit(ob_thread_exit_ptr); +} + +#endif \ No newline at end of file diff --git a/libmariadb/ob_thread_key.c b/libmariadb/ob_thread_key.c new file mode 100644 index 0000000..1e5eeca --- /dev/null +++ b/libmariadb/ob_thread_key.c @@ -0,0 +1,76 @@ +/************************************************************************ + Copyright (c) 2021 OceanBase. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02111-1301, USA + + Part of this code includes code from PHP's mysqlnd extension + (written by Andrey Hristov, Georg Richter and Ulf Wendel), freely + available from http://www.php.net/software + +*************************************************************************/ + +#include "ob_thread_key.h" + +#ifndef UNUSED +#define UNUSED(x) ((void)(x)) +#endif + +#ifdef _WIN32 + +int ob_create_thread_key(ob_thread_key_t *ob_key, void (*ob_destructor)(void *)) +{ + UNUSED(ob_destructor); + *ob_key = TlsAlloc(); + return (TLS_OUT_OF_INDEXES == *ob_key); +} + +int ob_delete_thread_key(ob_thread_key_t ob_key) +{ + return !TlsFree(ob_key); +} + +void *ob_get_thread_key(ob_thread_key_t ob_key) +{ + return TlsGetValue(ob_key); +} + +int ob_set_thread_key(ob_thread_key_t ob_key, void *value) +{ + return !TlsSetValue(ob_key, value); +} + +#else + +int ob_create_thread_key(ob_thread_key_t *ob_key, void (*ob_destructor)(void *)) +{ + return pthread_key_create(ob_key, ob_destructor); +} + +int ob_delete_thread_key(ob_thread_key_t ob_key) +{ + return pthread_key_delete(ob_key); +} + +void *ob_get_thread_key(ob_thread_key_t ob_key) +{ + return pthread_getspecific(ob_key); +} + +int ob_set_thread_key(ob_thread_key_t ob_key, void *value) +{ + return pthread_setspecific(ob_key, value); +} + +#endif \ No newline at end of file diff --git a/mariadb_config/libmariadb.pc.in b/mariadb_config/libmariadb.pc.in index 968181a..2ee4454 100644 --- a/mariadb_config/libmariadb.pc.in +++ b/mariadb_config/libmariadb.pc.in @@ -10,11 +10,11 @@ prefix=@CMAKE_INSTALL_PREFIX@ includedir=${prefix}/@INSTALL_INCLUDEDIR@/@SUFFIX_INSTALL_DIR@ libdir=${prefix}/@INSTALL_LIBDIR@/@SUFFIX_INSTALL_DIR@ -Name: libmariadb +Name: libobclnt Version: @CPACK_PACKAGE_VERSION@ Description: MariaDB Connector/C dynamic library Cflags: -I${includedir} -Libs: -L${libdir} -lmariadb +Libs: -L${libdir} -lobclnt Libs.private: @extra_dynamic_LDFLAGS@ diff --git a/mariadb_config/mariadb_config.c.in b/mariadb_config/mariadb_config.c.in index 5574943..6a4eb9c 100644 --- a/mariadb_config/mariadb_config.c.in +++ b/mariadb_config/mariadb_config.c.in @@ -28,7 +28,7 @@ static char *mariadb_progname; #endif #define INCLUDE "-I%s/@INSTALL_INCLUDEDIR@ -I%s/@INSTALL_INCLUDEDIR@/mysql" -#define LIBS "-L%s/@INSTALL_LIBDIR@/ -lmariadb" +#define LIBS "-L%s/@INSTALL_LIBDIR@/ -lobclnt" #define LIBS_SYS "@extra_dynamic_LDFLAGS@" #define CFLAGS INCLUDE #define VERSION "@MARIADB_CLIENT_VERSION@" diff --git a/plugins/auth/my_auth.c b/plugins/auth/my_auth.c index ea33e0c..b6fbc02 100644 --- a/plugins/auth/my_auth.c +++ b/plugins/auth/my_auth.c @@ -257,6 +257,10 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, mysql->client_flag&= ~CLIENT_COMPRESS; #endif + if (FALSE == mysql->can_use_protocol_ob20) { + mysql->client_flag&= ~CLIENT_COMPRESS; + } + if (mysql->client_flag & CLIENT_PROTOCOL_41) { /* 4.1 server and 4.1 client has a 32 byte option flag */ diff --git a/rpm/libobclient-VER.txt b/rpm/libobclient-VER.txt index e9307ca..ccbccc3 100644 --- a/rpm/libobclient-VER.txt +++ b/rpm/libobclient-VER.txt @@ -1 +1 @@ -2.0.2 +2.2.0 diff --git a/rpm/libobclient-build.sh b/rpm/libobclient-build.sh index 07e139e..148db51 100644 --- a/rpm/libobclient-build.sh +++ b/rpm/libobclient-build.sh @@ -3,10 +3,23 @@ # Usage: obclient-build.sh # Usage: obclient-build.sh -TOP_DIR=$1 -PACKAGE=$2 -VERSION=$3 -RELEASE=$4 +REDHAT=`cat /etc/redhat-release|cut -d " " -f 7|cut -d "." -f 1` + +if [ $# -ne 4 ] +then + TOP_DIR=`pwd`/../ + PACKAGE=$(basename $0 -build.sh) + VERSION=`cat ${PACKAGE}-VER.txt` + RELEASE="1" +#RELEASE="dev.el${REDHAT}" +else + TOP_DIR=$1 + PACKAGE=$2 + VERSION=$3 + RELEASE="$4.el${REDHAT}" + export AONE_BUILD_NUMBER=${4} +fi +echo "[BUILD] args: TOP_DIR=${TOP_DIR} PACKAGE=${PACKAGE} VERSION=${VERSION} RELEASE=${RELEASE} AONE_BUILD_NUMBER=${AONE_BUILD_NUMBER}" TMP_DIR=${TOP_DIR}/${PACKAGE}-tmp.$$ BOOST_DIR=${TMP_DIR}/BOOST diff --git a/rpm/libobclient.spec b/rpm/libobclient.spec index fcdc55f..4c66d99 100644 --- a/rpm/libobclient.spec +++ b/rpm/libobclient.spec @@ -37,11 +37,11 @@ do done mkdir -p $RPM_BUILD_ROOT%{prefix} mv $RPM_BUILD_ROOT%{file_dir}/* $RPM_BUILD_ROOT%{prefix} -mv $RPM_BUILD_ROOT%{prefix}/include/mariadb $RPM_BUILD_ROOT%{prefix}/include/mariadb_bak -mv $RPM_BUILD_ROOT%{prefix}/include/mariadb_bak/* $RPM_BUILD_ROOT%{prefix}/include/ -rm -rf $RPM_BUILD_ROOT%{prefix}/include/mariadb_bak -mv $RPM_BUILD_ROOT%{prefix}/lib/mariadb/* $RPM_BUILD_ROOT%{prefix}/lib/ -rm -rf $RPM_BUILD_ROOT%{prefix}/lib/mariadb +#mv $RPM_BUILD_ROOT%{prefix}/include/mariadb $RPM_BUILD_ROOT%{prefix}/include/mariadb_bak +#mv $RPM_BUILD_ROOT%{prefix}/include/mariadb_bak/* $RPM_BUILD_ROOT%{prefix}/include/ +#rm -rf $RPM_BUILD_ROOT%{prefix}/include/mariadb_bak +#mv $RPM_BUILD_ROOT%{prefix}/lib/mariadb/* $RPM_BUILD_ROOT%{prefix}/lib/ +#rm -rf $RPM_BUILD_ROOT%{prefix}/lib/mariadb %clean rm -rf $RPM_BUILD_ROOT diff --git a/unittest/my_test/Makefile b/unittest/my_test/Makefile index eb064b7..6e0760b 100644 --- a/unittest/my_test/Makefile +++ b/unittest/my_test/Makefile @@ -1,16 +1,35 @@ CFLAGS= -I../../include CCFLAGS= -I../../include LDFLAGS= -g -L ../../libmariadb/ -lobclnt -lz -lpthread -lgtest -BIN=ob_mysql_ps_test ob_mysql_text_test ob_oracle_ps_test +BIN=ob_mysql_ps_test ob_mysql_ps_cursor_test ob_mysql_text_test ob_oracle_ps_test \ +ob_oracle_datetype_test ob_new_ps_test ob_new_ps_select_notable ob_new_ps_select_test \ +ob_mysql_ssl_test ob_oracle_fetch_cursor_test ob_oracle_array_binding_test \ +ob_oracle_pl_test EXEC=env LD_LIBRARY_PATH=../../libmariadb/ all:${BIN} run .PHONY:clean +ob_mysql_ps_cursor_test:ob_mysql_ps_cursor_test.c ob_mysql_ps_test:ob_mysql_ps_test.c ob_mysql_text_test:ob_mysql_text_test.c ob_oracle_ps_test:ob_oracle_ps_test.c +ob_oracle_datetype_test:ob_oracle_datetype_test.c +ob_new_ps_test:ob_new_ps_test.c +ob_new_ps_select_notable:ob_new_ps_select_notable.c +ob_new_ps_select_test:ob_new_ps_select_test.c +ob_mysql_ssl_test:ob_mysql_ssl_test.c +ob_oracle_fetch_cursor_test:ob_oracle_fetch_cursor_test.c +ob_oracle_array_binding_test:ob_oracle_array_binding_test.c +ob_oracle_pl_test:ob_oracle_pl_test.c run: $(EXEC) ./ob_mysql_ps_test + $(EXEC) ./ob_mysql_ps_cursor_test $(EXEC) ./ob_mysql_text_test $(EXEC) ./ob_oracle_ps_test + $(EXEC) ./ob_oracle_datetype_test + $(EXEC) ./ob_new_ps_test + $(EXEC) ./ob_new_ps_select_notable + $(EXEC) ./ob_new_ps_select_test + $(EXEC) ./ob_oracle_fetch_cursor_test + $(EXEC) ./ob_oracle_array_binding_test clean: rm -rf ${BIN} *.o diff --git a/unittest/my_test/ob_mysql_ps_cursor_test.c b/unittest/my_test/ob_mysql_ps_cursor_test.c new file mode 100644 index 0000000..05efe72 --- /dev/null +++ b/unittest/my_test/ob_mysql_ps_cursor_test.c @@ -0,0 +1,129 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" +/* + * + */ +int do_stmt_select_with_cursor(MYSQL* mysql) { + my_log("=========do_stmt_select_with_cursor=========="); + const char* pStatement = "select * from person where id > ?"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int param_count; + int filed_count; + //prepare data + int i = 0; + char insert_buf[100] = {0}; + for (i = 0; i < 10; i++) + { + snprintf(insert_buf, 100, "insert into person values('%d', '%d', 'test_%d', '2021-04-06 10:21:00');", i, i, i); + // my_log("buf is %s", insert_buf); + if (mysql_real_query(mysql, insert_buf, strlen(insert_buf))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + } + } + my_ulonglong affected_rows; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + return -1; + } + if (mysql_stmt_prepare(stmt, pStatement, strlen(pStatement))) { + my_log("mysql_stmt_prepare failed:%s", mysql_error(mysql)); + return -1; + } + filed_count = mysql_stmt_field_count(stmt); + param_count = mysql_stmt_param_count(stmt); + ASSERT_EQ(param_count, 1, "mysql_stmt_param_count"); + int id, num; + id = 0; + memset(bind, 0, sizeof(bind)); + { + //id + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = (char*)&id; + bind[0].buffer_length = 0; + bind[0].is_null = 0; + bind[0].length = 0; + // bind[0].is_unsigned = 0; + } + if (mysql_stmt_bind_param(stmt, bind)) { + my_log("bind failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(0, -1, "mysql_stmt_bind_param failed"); + return -1; + } + ulong cursor_type = CURSOR_TYPE_READ_ONLY; + ASSERT_EQ(0, mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &cursor_type), "mysql_stmt_attr_set"); + unsigned int iteration_count = 1; + //stmt->iteration_count = 0; + if (mysql_stmt_execute(stmt)) { + my_log("mysql_stmt_execute select failed:%s", mysql_stmt_error(stmt)); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + ASSERT_EQ(filed_count, 4, "mysql_stmt_field_count"); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + // ASSERT_EQ(row_count, 9, "mysql_stmt_num_rows"); + memset(bind, 0, sizeof(bind)); + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + return -1; + } + i = 1; + while (mysql_stmt_fetch(stmt) == 0) { + if (id != i) { + ASSERT_EQ(id, i, "check value"); + } + ++i; + // my_log("%d\t%d\t%s\t%s\n", id, num, name, birthday); + } + mysql_stmt_close(stmt); + return 0; +} + +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + unsigned int timeout = 3000; + init_conn_info(OB_MYSQL_MODE); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + int ret = 0; + mysql_query(mysql, "drop table person"); + ret = mysql_query(mysql, "create table person(id int, num int, name varchar(20), birthday varchar(20))"); + if (ret != 0) + { + my_log("create table error, %s", mysql_error(mysql)); + return ret; + } + do_stmt_select_with_cursor(mysql); + return 0; +} diff --git a/unittest/my_test/ob_mysql_ps_test.c b/unittest/my_test/ob_mysql_ps_test.c index 5039f22..ef395c3 100644 --- a/unittest/my_test/ob_mysql_ps_test.c +++ b/unittest/my_test/ob_mysql_ps_test.c @@ -133,7 +133,7 @@ int do_stmt_insert(MYSQL* mysql) { } int do_stmt_select(MYSQL* mysql) { my_log("=========do_stmt_select=========="); - const char* pStatement = "select * from person"; + const char* pStatement = "select * from person where id > ?"; MYSQL_STMT *stmt; MYSQL_BIND bind[4]; int param_count; @@ -150,6 +150,24 @@ int do_stmt_select(MYSQL* mysql) { } filed_count = mysql_stmt_field_count(stmt); param_count = mysql_stmt_param_count(stmt); + ASSERT_EQ(param_count, 1, "mysql_stmt_param_count"); + int id, num; + id = 0; + memset(bind, 0, sizeof(bind)); + { + //id + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = (char*)&id; + bind[0].buffer_length = 0; + bind[0].is_null = 0; + bind[0].length = 0; + // bind[0].is_unsigned = 0; + } + if (mysql_stmt_bind_param(stmt, bind)) { + my_log("bind failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(0, -1, "mysql_stmt_bind_param failed"); + return -1; + } unsigned int iteration_count = 1; //stmt->iteration_count = 0; if (mysql_stmt_execute(stmt)) { @@ -165,7 +183,6 @@ int do_stmt_select(MYSQL* mysql) { my_ulonglong row_count = mysql_stmt_num_rows(stmt); ASSERT_EQ(row_count, 9, "mysql_stmt_field_count"); memset(bind, 0, sizeof(bind)); - int id, num; char name[20]; char birthday[20]; bind[0].buffer_type = MYSQL_TYPE_LONG; diff --git a/unittest/my_test/ob_mysql_ssl_test.c b/unittest/my_test/ob_mysql_ssl_test.c new file mode 100644 index 0000000..f1393d3 --- /dev/null +++ b/unittest/my_test/ob_mysql_ssl_test.c @@ -0,0 +1,97 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" +/* + * + */ +inline long long get_cur_time() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +} +int do_stmt_select_query(MYSQL* mysql) { + my_log("=========do_stmt_select_query=========="); + const char* select_query = "select * from test;"; + const char* insert_query = "insert into test values(1, 'test1')"; + int filed_count; + my_ulonglong affected_rows; + int64_t t_start, t_end; + t_start = get_cur_time(); + if (mysql_real_query(mysql, insert_query, strlen(insert_query))) { + my_log("mysql_real_query insert_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_real_query"); + return -1; + } + if (mysql_real_query(mysql, select_query, strlen(select_query))) { + my_log("mysql_real_query select failed:%s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_real_query"); + return -1; + } + t_end = get_cur_time(); + // printf("mysql_real_query cost time %ld us\n", t_end - t_start); + t_start = t_end; + MYSQL_RES * result = mysql_use_result(mysql); + t_end = get_cur_time(); + // printf("store result cost time %ld us\n", t_end - t_start); + t_start = t_end; + if (!result) { + my_log("unexpect null result"); + ASSERT_EQ(0, -1, "mysql_use_result get null"); + return -1; + } + filed_count = mysql_num_fields(result); + ASSERT_EQ(filed_count, 2, "mysql_num_fields"); + int i = 0; + MYSQL_FIELD *fields = mysql_fetch_fields(result); + for(i = 0; i < filed_count; i++) + { + printf("%s\t", fields[i].name); + if (i == 0 && strcasecmp(fields[i].name, "ID") != 0) { + ASSERT_EQ(0, -1, "invalid field"); + } else if (i == 1 && strcasecmp(fields[i].name, "NAME") != 0) { + ASSERT_EQ(0, -1, "invalid field"); + } + } + printf("\n"); + int result_count = 0; + MYSQL_ROW sql_row; + while (sql_row = mysql_fetch_row(result)) { + result_count++; + for (i = 0; i < filed_count; i++) + { + // + } + } + t_end = get_cur_time(); + // printf("fetch %d cost time %ld us\n", result_count, t_end - t_start); + mysql_free_result(result); + return 0; +} +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + unsigned int timeout = 3000; + init_conn_info(OB_MYSQL_MODE); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + int ret = 0; + mysql_real_query(mysql, "drop table test", strlen("drop table test")); + ret = mysql_query(mysql, "create table test(id int, name varchar(20))"); + if (ret != 0){ + printf("create table failed, %s\n", mysql_error(mysql)); + return ret; + } + do_stmt_select_query(mysql); + return 0; +} diff --git a/unittest/my_test/ob_new_ps_insert_test.c b/unittest/my_test/ob_new_ps_insert_test.c new file mode 100644 index 0000000..8c2bc75 --- /dev/null +++ b/unittest/my_test/ob_new_ps_insert_test.c @@ -0,0 +1,214 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" + +int stmt_insert_long_data_test(MYSQL* mysql) { + my_log("stmt_insert_test"); + const char* pStatement = "insert into test_new_ps(id,name) values(?,?)"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[2]; + int param_count; + int id = 8; + int num = 10; + int ret = 0; + int i = 0; + // char table[30] = "test.person"; + // char name[30] = "jh"; + // char birthday[20] = "2018-01-01"; + char name[20] = "data long than"; + my_ulonglong affected_rows; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s\n", mysql_error(mysql)); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), NULL)) { + my_log("mysql_stmt_prepare_v2 failed:%s\n", mysql_error(mysql)); + return -1; + } else { + ASSERT_EQ(ret , 0, "mysql_stmt_prepare_v2"); + } + + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + ASSERT_EQ(param_count, 2, "mysql_stmt_param_count"); + memset(bind, 0, sizeof(bind)); + { + //id + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = (char*)&id; + bind[0].buffer_length = 0; + bind[0].is_null = 0; + bind[0].length = 0; + // bind[0].is_unsigned = 0; + } + { + //name + unsigned long str_len = strlen(name); + my_log("str_len for name is %d", str_len); + bind[1].buffer_type = MYSQL_TYPE_STRING; + bind[1].buffer = (char*)name; + bind[1].buffer_length = str_len; + bind[1].is_null = 0; + bind[1].length = &str_len; + // bind[2].is_unsigned = 0; + } + if (ret = mysql_stmt_bind_param(stmt, bind)) { + my_log("bind failed:%s", mysql_stmt_error(stmt)); + return -1; + } else { + ASSERT_EQ(ret, 0, "mysql_stmt_bind_param"); + } + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 1; + for (i = 0; i < 2; i++) { + if (mysql_stmt_execute_v2(stmt,pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 insert failed:%s", mysql_stmt_error(stmt)); + return -1; + } + affected_rows = mysql_stmt_affected_rows(stmt); + my_log("insert %d affected rows :%lu", iteration_count,affected_rows); + ASSERT_EQ(affected_rows, 0, "mysql_stmt_affected_rows"); + snprintf(name, 20, "short"); + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement),iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 insert failed:%s", mysql_stmt_error(stmt)); + return -1; + } else { + ASSERT_EQ(ret, 0, "mysql_stmt_execute_v2"); + } + affected_rows = mysql_stmt_affected_rows(stmt); + my_log("insert %d affected rows :%lu", iteration_count, affected_rows); + ASSERT_EQ(affected_rows, 1, "mysql_stmt_affected_rows"); + } + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faield:%s", mysql_stmt_error(stmt)); + return -1; + } else { + ASSERT_EQ(ret, 0, "mysql_stmt_close"); + } + return 0; +} +int stmt_insert_data_test(MYSQL* mysql) { + my_log("stmt_insert_test"); + const char* pStatement = "insert into test_new_ps(id,name) values(?,?)"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[2]; + int param_count; + int id = 8; + int num = 10; + int ret = 0; + int i = 0; + // char table[30] = "test.person"; + // char name[30] = "jh"; + // char birthday[20] = "2018-01-01"; + char name[20] = "data1"; + my_ulonglong affected_rows; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s\n", mysql_error(mysql)); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), NULL)) { + my_log("mysql_stmt_prepare_v2 failed:%s\n", mysql_error(mysql)); + return -1; + } else { + ASSERT_EQ(ret , 0, "mysql_stmt_prepare_v2"); + } + + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + ASSERT_EQ(param_count, 2, "mysql_stmt_param_count"); + memset(bind, 0, sizeof(bind)); + { + //id + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = (char*)&id; + bind[0].buffer_length = 0; + bind[0].is_null = 0; + bind[0].length = 0; + // bind[0].is_unsigned = 0; + } + { + //name + unsigned long str_len = strlen(name); + my_log("str_len for name is %d", str_len); + bind[1].buffer_type = MYSQL_TYPE_STRING; + bind[1].buffer = (char*)name; + bind[1].buffer_length = str_len; + bind[1].is_null = 0; + bind[1].length = &str_len; + // bind[2].is_unsigned = 0; + } + if (ret = mysql_stmt_bind_param(stmt, bind)) { + my_log("bind failed:%s", mysql_stmt_error(stmt)); + return -1; + } else { + ASSERT_EQ(ret, 0, "mysql_stmt_bind_param"); + } + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 1; + for (i = 0; i < 2; i++) + { + if (mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 insert failed:%s", mysql_stmt_error(stmt)); + return -1; + } + affected_rows = mysql_stmt_affected_rows(stmt); + my_log("insert %d affected rows :%lu", iteration_count,affected_rows); + ASSERT_EQ(affected_rows, 1, "mysql_stmt_affected_rows"); + snprintf(name, 20, "short"); + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement),iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 insert failed:%s", mysql_stmt_error(stmt)); + return -1; + } else { + ASSERT_EQ(ret, 0, "mysql_stmt_execute_v2"); + } + affected_rows = mysql_stmt_affected_rows(stmt); + my_log("insert %d affected rows :%lu", iteration_count, affected_rows); + ASSERT_EQ(affected_rows, 1, "mysql_stmt_affected_rows"); + } + + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faield:%s", mysql_stmt_error(stmt)); + return -1; + } else { + ASSERT_EQ(ret, 0, "mysql_stmt_close"); + } + return 0; +} +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + unsigned int timeout = 3000; + init_conn_info(OB_ORACLE_MODE); + + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s\n", mysql_error(mysql)); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect to %s:%d using %s success", DBHOST, DBPORT, DBUSER); + } + int ret = mysql_query(mysql, "SET NAMES utf8"); + if (ret != 0) + { + printf("set names error, %s\n", mysql_error(mysql)); + return ret; + } + mysql_query(mysql, "drop table test_new_ps"); + ret = mysql_query(mysql, "create table test_new_ps(id int, name varchar2(10))"); + if (ret != 0) + { + my_log("create table error, %s\n", mysql_error(mysql)); + return ret; + } + stmt_insert_long_data_test(mysql); + stmt_insert_data_test(mysql); + return 0; +} \ No newline at end of file diff --git a/unittest/my_test/ob_new_ps_select_notable.c b/unittest/my_test/ob_new_ps_select_notable.c new file mode 100644 index 0000000..8074f0d --- /dev/null +++ b/unittest/my_test/ob_new_ps_select_notable.c @@ -0,0 +1,101 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" + + +int stmt_select_test_not_exist_table(MYSQL* mysql) { + my_log("stmt_select_test_not_exist_table"); + const char* pStatement = "select * from test_new_ps"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int ret = 0; + int param_count; + int filed_count; + my_ulonglong affected_rows; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(1, ret, "mysql_stmt_execute_v2"); + } else { + ASSERT_EQ(1, ret, "mysql_stmt_execute_v2"); + } + return 0; +} +int stmt_select_test_no_table(MYSQL* mysql) { + my_log("stmt_select_test_no_table"); + const char* pStatement = "select c1,c2,c3,c4"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int ret = 0; + int param_count; + int filed_count; + my_ulonglong affected_rows; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(1, ret, "mysql_stmt_execute_v2"); + } else { + ASSERT_EQ(1, ret, "mysql_stmt_init"); + } + return 0; +} +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + unsigned int timeout = 3000; + init_conn_info(OB_ORACLE_MODE); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s\n", mysql_error(mysql)); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect to %s:%d using %s success", DBHOST, DBPORT, DBUSER); + } + int ret = mysql_query(mysql, "SET NAMES utf8"); + if (ret != 0) + { + printf("set names failed, %s\n", mysql_error(mysql)); + return ret; + } + mysql_query(mysql, "drop table test_new_ps"); + stmt_select_test_not_exist_table(mysql); + stmt_select_test_no_table(mysql); + return 0; +} \ No newline at end of file diff --git a/unittest/my_test/ob_new_ps_select_test.c b/unittest/my_test/ob_new_ps_select_test.c new file mode 100644 index 0000000..8002ebf --- /dev/null +++ b/unittest/my_test/ob_new_ps_select_test.c @@ -0,0 +1,605 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" + +int stmt_select_test_no_param_no_result(MYSQL* mysql) { + my_log("stmt_select_test_no_param_no_result"); + const char* pStatement = "select * from test_new_ps"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int ret = 0; + int param_count; + int filed_count; + my_ulonglong affected_rows; + const char* query = "delete from test_new_ps"; + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (ret = mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_bind_result"); + } + while (mysql_stmt_fetch(stmt) == 0) { + printf("%d\t%d\t%s\t%s\n", id, num, name, birthday); + } + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faled:%s", mysql_stmt_error(stmt)); + } + ASSERT_EQ(0, 0, "stmt_select_test_no_param_no_result"); + return 0; +} +int stmt_select_test_with_param_no_result(MYSQL* mysql) { + my_log("stmt_select_test_with_param_no_result"); + const char* pStatement = "select * from test_new_ps where id = ? and num = ? and name = ?"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + MYSQL_BIND param[4]; + int ret = 0; + int param_count; + int filed_count; + my_ulonglong affected_rows; + const char* query = "delete from test_new_ps"; + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + int input_id = 0, input_num = 0; + char input_name[20] = "test"; + unsigned long str_len; + { + //id + param[0].buffer_type = MYSQL_TYPE_LONG; + param[0].buffer = (char*)&input_id; + param[0].buffer_length = 0; + param[0].is_null = 0; + param[0].length = 0; + // bind[0].is_unsigned = 0; + } + { + //num + param[1].buffer_type = MYSQL_TYPE_LONG; + param[1].buffer = (char*)&input_num; + param[1].buffer_length = 0; + param[1].is_null = 0; + param[1].length = 0; + // bind[1].is_unsigned = 0; + } + { + //name + str_len = strlen(input_name); + my_log("str_len for name is %d", str_len); + param[2].buffer_type = MYSQL_TYPE_STRING; + param[2].buffer = (char*)input_name; + param[2].buffer_length = str_len; + param[2].is_null = 0; + param[2].length = &str_len; + // bind[2].is_unsigned = 0; + } + if (mysql_stmt_bind_param(stmt, param)) { + my_log("bind failed:%s", mysql_stmt_error(stmt)); + return -1; + } + unsigned int iteration_count = 0; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_execute_v2"); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (ret = mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_bind_result"); + } + while (mysql_stmt_fetch(stmt) == 0) { + my_log("%d\t%d\t%s\t%s", id, num, name, birthday); + } + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faled:%s", mysql_stmt_error(stmt)); + } + ASSERT_EQ(0, 0, "stmt_select_test_with_param_no_result"); + return 0; +} +int stmt_select_test_no_param_with_result(MYSQL* mysql) { + my_log("stmt_select_test"); + const char* pStatement = "select * from test_new_ps"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int ret = 0; + int param_count; + int filed_count; + my_ulonglong affected_rows; + int i = 0; + char query[1000] = {0}; + snprintf(query, 1000, "delete from test_new_ps"); + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + for (i = 0; i < 10; i++) + { + snprintf(query, 1000, "insert into test_new_ps(id, num, name, birthday) values(%d,%d, 'name_%d', 'birth_%d')", i ,i, i, i); + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + } + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (ret = mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_bind_result"); + } + i = 0; + while (mysql_stmt_fetch(stmt) == 0) { + i++; + printf("%d\t%d\t%s\t%s\n", id, num, name, birthday); + } + ASSERT_EQ(10, i, "mysql_stmt_fetch"); + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faled:%s", mysql_stmt_error(stmt)); + } + ASSERT_EQ(0, 0, "stmt_select_test_no_param_with_result"); + return 0; +} +int stmt_select_test_with_param_with_result(MYSQL* mysql) { + my_log("stmt_select_test_with_param_with_result"); + const char* pStatement = "select * from test_new_ps where id = ? and num = ? and name = ?"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + MYSQL_BIND param[4]; + int ret = 0; + int param_count; + int filed_count; + int i = 0; + my_ulonglong affected_rows; + char query [400]; + snprintf(query, 400, "delete from test_new_ps"); + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + for (i = 0; i < 10; i++) + { + snprintf(query, 1000, "insert into test_new_ps(id, num, name, birthday) values(%d,%d, 'name_%d', 'birth_%d')", i ,i, i, i); + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + } + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + int input_id = 0, input_num = 0; + char input_name[20] = "name_0"; + unsigned long str_len; + { + //id + param[0].buffer_type = MYSQL_TYPE_LONG; + param[0].buffer = (char*)&input_id; + param[0].buffer_length = 0; + param[0].is_null = 0; + param[0].length = 0; + // bind[0].is_unsigned = 0; + } + { + //num + param[1].buffer_type = MYSQL_TYPE_LONG; + param[1].buffer = (char*)&input_num; + param[1].buffer_length = 0; + param[1].is_null = 0; + param[1].length = 0; + // bind[1].is_unsigned = 0; + } + { + //name + str_len = strlen(input_name); + my_log("str_len for name is %d", str_len); + param[2].buffer_type = MYSQL_TYPE_STRING; + param[2].buffer = (char*)input_name; + param[2].buffer_length = str_len; + param[2].is_null = 0; + param[2].length = &str_len; + // bind[2].is_unsigned = 0; + } + if (mysql_stmt_bind_param(stmt, param)) { + my_log("bind failed:%s", mysql_stmt_error(stmt)); + return -1; + } + unsigned int iteration_count = 0; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_execute_v2"); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (ret = mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_bind_result"); + } + while (mysql_stmt_fetch(stmt) == 0) { + my_log("%d\t%d\t%s\t%s", id, num, name, birthday); + } + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faled:%s", mysql_stmt_error(stmt)); + } + ASSERT_EQ(0, 0, "stmt_select_test_with_param_with_result"); + return 0; +} +int stmt_select_test_no_param_no_result_exact_not_match(MYSQL* mysql) { + my_log("stmt_select_test_no_param_no_result"); + const char* pStatement = "select * from test_new_ps"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int ret = 0; + int param_count; + int filed_count; + my_ulonglong affected_rows; + const char* query = "delete from test_new_ps"; + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + unsigned int iteration_count = 2; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0x00000002, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(1, ret, "mysql_stmt_execute_v2"); + return -1; + } else { + ASSERT_EQ(-1, ret, "mysql_stmt_execute_v2"); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (ret = mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_bind_result"); + } + while (mysql_stmt_fetch(stmt) == 0) { + printf("%d\t%d\t%s\t%s\n", id, num, name, birthday); + } + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faled:%s", mysql_stmt_error(stmt)); + } + ASSERT_EQ(-1, 0, "stmt_select_test_no_param_no_result"); + return 0; +} +int stmt_select_test_no_param_with_result_exact_not_match(MYSQL* mysql) { + my_log("stmt_select_test_no_param_with_result_exact_not_match"); + const char* pStatement = "select * from test_new_ps"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int ret = 0; + int param_count = 0; + int filed_count = 0; + my_ulonglong affected_rows; + int i = 0; + char query[1000] = {0}; + snprintf(query, 1000, "delete from test_new_ps"); + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + for (i = 0; i < 10; i++) + { + snprintf(query, 1000, "insert into test_new_ps(id, num, name, birthday) values(%d,%d, 'name_%d', 'birth_%d')", i ,i, i, i); + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + } + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + unsigned int iteration_count = 2; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0x00000002, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(1, ret, "mysql_stmt_execute_v2 with 2"); + } else { + ASSERT_EQ(1, ret, "mysql_stmt_execute_v2 with 2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + ASSERT_EQ(filed_count, 4, "mysql_stmt_field_count"); + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), 12, 0x00000002, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(1, ret, "mysql_stmt_execute_v2 with 12"); + } else { + ASSERT_EQ(-1, ret, "mysql_stmt_execute_v2 with 12"); + } + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), 10, 0x00000002, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(0, ret, "mysql_stmt_execute_v2 with 10"); + } else { + ASSERT_EQ(0, ret, "mysql_stmt_execute_v2 with 10"); + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (ret = mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_bind_result"); + } + i = 0; + while (mysql_stmt_fetch(stmt) == 0) { + i++; + printf("%d\t%d\t%s\t%s\n", id, num, name, birthday); + } + ASSERT_EQ(10, i, "mysql_stmt_fetch"); + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faled:%s", mysql_stmt_error(stmt)); + } + ASSERT_EQ(0, 0, "stmt_select_test_no_param_with_result_exact_not_match"); + return 0; +} +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + unsigned int timeout = 3000; + init_conn_info(OB_ORACLE_MODE); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s\n", mysql_error(mysql)); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect to %s:%d using %s success", DBHOST, DBPORT, DBUSER); + } + int ret = mysql_query(mysql, "SET NAMES utf8"); + if (ret != 0) + { + printf("set names failed, %s\n", mysql_error(mysql)); + return ret; + } + mysql_query(mysql, "drop table test_new_ps"); + ret = mysql_query(mysql, "create table test_new_ps(id int, num int, name varchar2(20), birthday varchar2(20))"); + if (ret != 0) + { + my_log("create table error, %s\n", mysql_error(mysql)); + return ret; + } + stmt_select_test_no_param_no_result(mysql); + stmt_select_test_no_param_with_result(mysql); + stmt_select_test_with_param_no_result(mysql); + stmt_select_test_with_param_with_result(mysql); + stmt_select_test_no_param_no_result_exact_not_match(mysql); + stmt_select_test_no_param_with_result_exact_not_match(mysql); + return 0; +} \ No newline at end of file diff --git a/unittest/my_test/ob_new_ps_test.c b/unittest/my_test/ob_new_ps_test.c new file mode 100644 index 0000000..6edf882 --- /dev/null +++ b/unittest/my_test/ob_new_ps_test.c @@ -0,0 +1,249 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" + +/* + * simple delete + */ +int stmt_delete_test(MYSQL* mysql) { + my_log("stmt_delete_test"); + const char* pStatement = "delete from person"; + MYSQL_STMT *stmt; + my_ulonglong affected_rows; + int ret = 0; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s\n", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), NULL)) { + my_log("mysql_stmt_prepare_v2 failed:%s\n", mysql_error(mysql)); + return -1; + } + int param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + ASSERT_EQ(param_count, 0, "mysql_stmt_param_count"); + //stmt->iteration_count = 1; + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 delete failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_execute_v2"); + return -1; + } else { + ASSERT_EQ(0, 0, "mysql_stmt_execute_v2"); + } + affected_rows = mysql_stmt_affected_rows(stmt); + my_log("affected rows :%lu\n", affected_rows); + ASSERT_EQ(0, 0, "mysql_stmt_affected_rows"); + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 delete failed:%s\n", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_execute_v2"); + } else { + ASSERT_EQ(0, 0, "mysql_stmt_execute_v2"); + } + if (ret = mysql_stmt_close(stmt)) { + printf("mysql_stmt_close faield:%s\n", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_close"); + return -1; + } else { + ASSERT_EQ(ret, 0, "mysql_stmt_close"); + } + return 0; +} +int stmt_insert_test(MYSQL* mysql) { + my_log("stmt_insert_test"); + const char* pStatement = "insert into person(id,num,name,birthday) values(?,?,?,?)"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[6]; + int param_count; + int id = 8; + int num = 10; + // char table[30] = "test.person"; + // char name[30] = "jh"; + // char birthday[20] = "2018-01-01"; + char* name = "test"; + char* birthday = "2021-04-06"; + my_ulonglong affected_rows; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s\n", mysql_error(mysql)); + return -1; + } + if (mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), NULL)) { + my_log("mysql_stmt_prepare_v2 failed:%s\n", mysql_error(mysql)); + return -1; + } + + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + memset(bind, 0, sizeof(bind)); + { + //id + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = (char*)&id; + bind[0].buffer_length = 0; + bind[0].is_null = 0; + bind[0].length = 0; + // bind[0].is_unsigned = 0; + } + { + //num + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = (char*)# + bind[1].buffer_length = 0; + bind[1].is_null = 0; + bind[1].length = 0; + // bind[1].is_unsigned = 0; + } + { + //name + unsigned long str_len = strlen(name); + printf("str_len for name is %d\n", str_len); + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = (char*)name; + bind[2].buffer_length = str_len; + bind[2].is_null = 0; + bind[2].length = &str_len; + // bind[2].is_unsigned = 0; + } + { + //birthday + unsigned long str_len = strlen(birthday); + printf("str_len for birthday is %d\n", str_len); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = (char*)birthday; + bind[3].buffer_length = str_len; + bind[3].is_null = 0; + // bind[3].length = &str_len; + // bind[3].is_unsigned = 0; + } + if (mysql_stmt_bind_param(stmt, bind)) { + printf("bind failed:%s\n", mysql_stmt_error(stmt)); + return -1; + } + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 1; + int i = 0; + for (i = 1; i < 10; i++) { + id = i; + if (mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), 1, 0, 0)) { + printf("mysql_stmt_execute_v2 insert failed:%s, i is %d\n", mysql_stmt_error(stmt), i); + return -1; + } + affected_rows = mysql_stmt_affected_rows(stmt); + printf("insert %d affected rows :%lu\n", i, affected_rows); + + } + if (mysql_commit(mysql)) { + printf("mysql_commit faield:%s\n", mysql_stmt_error(stmt)); + } + if (mysql_stmt_close(stmt)) { + printf("mysql_stmt_close faield:%s\n", mysql_stmt_error(stmt)); + return -1; + } + return 0; +} +int stmt_select_test(MYSQL* mysql) { + my_log("stmt_select_test"); + const char* pStatement = "select * from person"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int param_count; + int filed_count; + my_ulonglong affected_rows; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + printf("msyql_stmt_init failed: %s\n", mysql_error(mysql)); + return -1; + } + if (mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + printf("mysql_stmt_prepare_v2 failed:%s\n", mysql_error(mysql)); + return -1; + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), 0, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (mysql_stmt_bind_result(stmt, bind)) { + printf("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + return -1; + } + row_count = 0; + while (mysql_stmt_fetch(stmt) == 0) { + printf("%d\t%d\t%s\t%s\n", id, num, name, birthday); + row_count++; + } + ASSERT_EQ(row_count, 9, "row_count"); + mysql_stmt_close(stmt); + + return 0; +} +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + unsigned int timeout = 3000; + init_conn_info(OB_ORACLE_MODE); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + int ret = mysql_query(mysql, "SET NAMES utf8"); + if (ret != 0) + { + printf("set names error, %s\n", mysql_error(mysql)); + return ret; + } + mysql_query(mysql, "drop table person"); + ret = mysql_query(mysql, "create table person(id int, num int, name varchar2(20), birthday varchar2(20))"); + if (ret != 0) + { + my_log("create table error, %s\n", mysql_error(mysql)); + return ret; + } + stmt_delete_test(mysql); + stmt_insert_test(mysql); + stmt_select_test(mysql); + return 0; +} \ No newline at end of file diff --git a/unittest/my_test/ob_oracle_array_binding_test.c b/unittest/my_test/ob_oracle_array_binding_test.c new file mode 100644 index 0000000..9e6aa2b --- /dev/null +++ b/unittest/my_test/ob_oracle_array_binding_test.c @@ -0,0 +1,193 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" + +#define check_error(mysql, status ) \ + check_error_internal(__FILE__, __LINE__, mysql, status) +int check_error_internal(const char* file_name, int line_no, MYSQL* mysql, int status ) { + if (status != 0) { + printf("[%s:%d] (error:%d, msg:%s)\n", file_name, line_no, mysql_errno(mysql), mysql_error(mysql)); + } + return 0; +} +#define check_stmt_error(stmt, status) \ + check_stmt_error_internal(__FILE__, __LINE__, stmt, status) +int check_stmt_error_internal(const char* file_name, int line_no, MYSQL_STMT* stmt, int status) { + if (status != 0) { + printf("[%s:%d] (error:%d, msg:%s)\n", file_name, line_no, mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + } + return 0; +} +int stmt_insert_array_test(MYSQL* mysql) +{ + my_log("stmt_insert_array_test"); + int status = 0; + MYSQL_STMT *stmt; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s\n", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + MYSQL_BIND ps_params[3]; + int int_data[3]; + my_bool is_null[3]; + printf("===========testPlArrayIn===========\n"); + /* set up stored procedure */ + check_error(mysql, mysql_query(mysql, "DROP TABLE TEST_ARRAY")); + check_error(mysql, mysql_query(mysql, "CREATE TABLE TEST_ARRAY (c1 int)")); + check_error(mysql, mysql_query(mysql, "set autocommit = 0")); + const char *insert_sql = "insert into TEST_ARRAY values(?)"; + check_stmt_error(stmt, mysql_stmt_prepare(stmt, insert_sql, strlen(insert_sql))); + /* initialize parameters: p_in, p_out, p_inout (all INT) */ + memset(ps_params, 0, sizeof (ps_params)); + + //MYSQL_COMPLEX_BIND_BASIC number_struct[3]; + MYSQL_COMPLEX_BIND_STRING number_struct[3]; + int number[3] = {2, 3, 4}; + char str[3][20]; + int i = 0; + for (i = 0; i < 3; i++) { + number_struct[i].buffer_type = MYSQL_TYPE_NEWDECIMAL; + snprintf(str[i], 20, "%d", i * 10 + 3); + number_struct[i].buffer = &str[i]; + number_struct[i].length = strlen(str[i]); + number_struct[i].is_null = 0; + } + MYSQL_COMPLEX_BIND_ARRAY array_struct; + array_struct.buffer_type = MYSQL_TYPE_ARRAY; + // type_name null will use anonymous array + // array_struct.type_name = "int_array"; + array_struct.type_name = NULL; + array_struct.buffer = number_struct; + array_struct.length = 3; + array_struct.is_null = 0; + + ps_params[0].buffer_type = MYSQL_TYPE_OBJECT; + ps_params[0].buffer = (char *) &array_struct; + ps_params[0].buffer_length = sizeof(MYSQL_COMPLEX_BIND_ARRAY); + ps_params[0].is_null = 0; + + /* bind parameters */ + check_stmt_error(stmt, status = mysql_stmt_bind_param(stmt, ps_params)); + + /* assign values to parameters and execute statement */ + int_data[0] = 10; /* p_in */ + int_data[1] = 20; /* p_out */ + int_data[2] = 30; /* p_inout */ + + unsigned long type; + const int ARRAY_TYPE_BINDING = 0x08; + const int BATCH_ERROR_MODE = 0x16; + + type = (unsigned long) ARRAY_TYPE_BINDING; + stmt->flags = ARRAY_TYPE_BINDING; + // check_stmt_error(stmt, status = mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type)); + check_stmt_error(stmt, status = mysql_stmt_execute(stmt)); + int affected_rows = mysql_stmt_affected_rows(stmt); + ASSERT_EQ(affected_rows, 3, "mysql_stmt_affected_rows"); + stmt->flags = 0; + check_stmt_error(stmt, mysql_stmt_prepare(stmt, "commit", strlen("commit"))); + check_stmt_error(stmt, mysql_stmt_execute(stmt)); + return status; +} +int stmt_select_test(MYSQL* mysql) { + my_log("stmt_select_test"); + const char* pStatement = "select * from person"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int param_count; + int filed_count; + my_ulonglong affected_rows; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + printf("msyql_stmt_init failed: %s\n", mysql_error(mysql)); + return -1; + } + if (mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + printf("mysql_stmt_prepare_v2 failed:%s\n", mysql_error(mysql)); + return -1; + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), 0, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (mysql_stmt_bind_result(stmt, bind)) { + printf("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + return -1; + } + row_count = 0; + while (mysql_stmt_fetch(stmt) == 0) { + printf("%d\t%d\t%s\t%s\n", id, num, name, birthday); + row_count++; + } + ASSERT_EQ(row_count, 9, "row_count"); + mysql_stmt_close(stmt); + + return 0; +} +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + unsigned int timeout = 3000; + init_conn_info(OB_ORACLE_MODE); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + int ret = mysql_query(mysql, "SET NAMES utf8"); + if (ret != 0) + { + printf("set names error, %s\n", mysql_error(mysql)); + return ret; + } + mysql_query(mysql, "drop table person"); + ret = mysql_query(mysql, "create table person(id int, num int, name varchar2(20), birthday varchar2(20))"); + if (ret != 0) + { + my_log("create table error, %s\n", mysql_error(mysql)); + return ret; + } + stmt_insert_array_test(mysql); + // stmt_select_test(mysql); + return 0; +} \ No newline at end of file diff --git a/unittest/my_test/ob_oracle_datetype_test.c b/unittest/my_test/ob_oracle_datetype_test.c new file mode 100644 index 0000000..a4a1943 --- /dev/null +++ b/unittest/my_test/ob_oracle_datetype_test.c @@ -0,0 +1,295 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" +/* + * + */ +int show_query_result(MYSQL* mysql, const char* query, int expect_field_count) +{ + int filed_count = 0; + if (mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query select failed:%s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_real_query"); + return -1; + } + MYSQL_RES * result = mysql_store_result(mysql); + if (!result) { + my_log("unexpect null result"); + ASSERT_EQ(0, -1, "mysql_use_result get null"); + return -1; + } + filed_count = mysql_num_fields(result); + if (expect_field_count > 0) { + ASSERT_EQ(filed_count, expect_field_count, "mysql_num_fields"); + } + int i = 0; + MYSQL_FIELD *fields = mysql_fetch_fields(result); + for(i = 0; i < filed_count; i++) + { + printf("%s\t", fields[i].name); + } + printf("\n"); + int result_count = 0; + MYSQL_ROW sql_row; + while (sql_row = mysql_fetch_row(result)) { + result_count++; + for (i = 0; i < filed_count; i++) + { + printf("%s,", sql_row[i]); + } + printf("\n"); + } + mysql_free_result(result); + return 0; +} +int do_text_time_test1(MYSQL* mysql) { + my_log("=========do_text_time_test1=========="); + const char* select_query = "select * from timetest;"; + // const char* insert_query = "insert into timetest values(sysdate,sysdate,sysdate,sysdate)"; + const char* insert_query = "insert into timetest values(to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS')"\ + ",to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS')" + ",to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS')" + ",to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS'))"; + my_ulonglong affected_rows; + mysql_real_query(mysql, "delete from timetest", strlen("delete from timetest")); + mysql_query(mysql, "ALTER SESSION SET NLS_DATE_FORMAT='DD-MON-RR';"); + mysql_query(mysql, "ALTER SESSION SET NLS_TIMESTAMP_FORMAT='DD-MON-RR HH.MI.SSXFF AM';"); + mysql_query(mysql, "ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT='DD-MON-RR HH.MI.SSXFF AM TZR';"); + if (mysql_real_query(mysql, insert_query, strlen(insert_query))) { + my_log("mysql_real_query insert_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_real_query"); + return -1; + } + show_query_result(mysql, "show variables like '%nls_date_format%'", 0); + int filed_count = 0; + if (mysql_real_query(mysql, select_query, strlen(select_query))) { + my_log("mysql_real_query select failed:%s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_real_query"); + return -1; + } + MYSQL_RES * result = mysql_store_result(mysql); + if (!result) { + my_log("unexpect null result"); + ASSERT_EQ(0, -1, "mysql_use_result get null"); + return -1; + } + filed_count = mysql_num_fields(result); + ASSERT_EQ(filed_count, 4, "mysql_num_fields"); + int i = 0; + MYSQL_FIELD *fields = mysql_fetch_fields(result); + for(i = 0; i < filed_count; i++) + { + printf("%s\t", fields[i].name); + } + printf("\n"); + int result_count = 0; + MYSQL_ROW sql_row; + while (sql_row = mysql_fetch_row(result)) { + result_count++; + for (i = 0; i < filed_count; i++) + { + printf("%s,", sql_row[i]); + switch(i){ + case 0: + ASSERT_EQ(strncmp(sql_row[i], "29-MAR-21", strlen("29-MAR-21")), 0, "DATE_FORMAT"); + break; + case 1: + ASSERT_EQ(strncmp(sql_row[i], "29-MAR-21 02.32.18.000 AM", strlen("29-MAR-21 02.32.18.000 AM")), 0, "TIMESTAMP_FORMAT"); + break; + case 2: + ASSERT_EQ(strncmp(sql_row[i], "29-MAR-21 02.32.18.000 AM +08:00", strlen("29-MAR-21 02.32.18.000 AM +08:00")), + 0, "TIMESTAMP_WITH_TIMEEZONE_FORMAT"); + break; + case 3: + ASSERT_EQ(strncmp(sql_row[i], "29-MAR-21 02.32.18.000 AM", strlen("29-MAR-21 02.32.18.000 AM")), + 0, "TIMESTAMP_WITH_LOCAL_TIMEEZONE_FORMAT"); + break; + default: + ASSERT_EQ(0, -1, "unexpect index"); + break; + } + } + // printf("\n"); + } + mysql_free_result(result); + mysql_real_query(mysql, "commit", strlen("commit")); + return 0; +} +int do_text_time_test2(MYSQL* mysql) { + my_log("=========do_text_time_test1=========="); + const char* select_query = "select * from timetest;"; + // const char* insert_query = "insert into timetest values(sysdate,sysdate,sysdate,sysdate)"; + const char* insert_query = "insert into timetest values(to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS')"\ + ",to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS')" + ",to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS')" + ",to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS'))"; + my_ulonglong affected_rows; + mysql_real_query(mysql, "delete from timetest", strlen("delete from timetest")); + mysql_query(mysql, "ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD';"); + mysql_query(mysql, "ALTER SESSION SET NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS.FF';"); + mysql_query(mysql, "ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT='YYYY-MM-DD HH24:MI:SS.FF TZR TZD';"); + if (mysql_real_query(mysql, insert_query, strlen(insert_query))) { + my_log("mysql_real_query insert_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_real_query"); + return -1; + } + show_query_result(mysql, "show variables like '%nls_date_format%'", 0); + int filed_count = 0; + if (mysql_real_query(mysql, select_query, strlen(select_query))) { + my_log("mysql_real_query select failed:%s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_real_query"); + return -1; + } + MYSQL_RES * result = mysql_store_result(mysql); + if (!result) { + my_log("unexpect null result"); + ASSERT_EQ(0, -1, "mysql_use_result get null"); + return -1; + } + filed_count = mysql_num_fields(result); + ASSERT_EQ(filed_count, 4, "mysql_num_fields"); + int i = 0; + MYSQL_FIELD *fields = mysql_fetch_fields(result); + for(i = 0; i < filed_count; i++) + { + printf("%s\t", fields[i].name); + } + printf("\n"); + int result_count = 0; + MYSQL_ROW sql_row; + while (sql_row = mysql_fetch_row(result)) { + result_count++; + for (i = 0; i < filed_count; i++) + { + printf("%s,", sql_row[i]); + switch(i){ + case 0: + ASSERT_EQ(strncmp(sql_row[i], "2021-03-29", strlen("2021-03-29")), 0, "DATE_FORMAT"); + break; + case 1: + ASSERT_EQ(strncmp(sql_row[i], "2021-03-29 02:32:18.000", strlen("2021-03-29 02:32:18.000")), 0, "TIMESTAMP_FORMAT"); + break; + case 2: + ASSERT_EQ(strncmp(sql_row[i], "2021-03-29 02:32:18.000 +08:00", strlen("2021-03-29 02:32:18.000 +08:00")), + 0, "TIMESTAMP_WITH_TIMEEZONE_FORMAT"); + break; + case 3: + ASSERT_EQ(strncmp(sql_row[i], "2021-03-29 02:32:18.000", strlen("2021-03-29 02:32:18.000")), + 0, "TIMESTAMP_WITH_LOCAL_TIMEEZONE_FORMAT"); + break; + default: + ASSERT_EQ(0, -1, "unexpect index"); + break; + } + } + // printf("\n"); + } + mysql_free_result(result); + mysql_real_query(mysql, "commit", strlen("commit")); + return 0; +} +int do_ps_time_test1(MYSQL* mysql) { + my_log("=========do_text_time_test1=========="); + const char* select_query = "select * from timetest;"; + // const char* insert_query = "insert into timetest values(sysdate,sysdate,sysdate,sysdate)"; + const char* insert_query = "insert into timetest values(to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS')"\ + ",to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS')" + ",to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS')" + ",to_date('2021-03-29 02.32.18', 'YYYY-mm-dd HH.MI.SS'))"; + my_ulonglong affected_rows; + mysql_real_query(mysql, "delete from timetest", strlen("delete from timetest")); + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int filed_count = 0; + int param_count = 0; + int i = 0; + mysql_query(mysql, "ALTER SESSION SET NLS_DATE_FORMAT='DD-MON-RR';"); + mysql_query(mysql, "ALTER SESSION SET NLS_TIMESTAMP_FORMAT='DD-MON-RR HH.MI.SSXFF AM';"); + mysql_query(mysql, "ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT='DD-MON-RR HH.MI.SSXFF AM TZR';"); + if (mysql_real_query(mysql, insert_query, strlen(insert_query))) { + my_log("mysql_real_query insert_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_real_query"); + return -1; + } + show_query_result(mysql, "show variables like '%nls_date_format%'", 0); + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + return -1; + } + if (mysql_stmt_prepare(stmt, select_query, strlen(select_query))) { + my_log("mysql_stmt_prepare failed:%s", mysql_error(mysql)); + return -1; + } + filed_count = mysql_stmt_field_count(stmt); + param_count = mysql_stmt_param_count(stmt); + ASSERT_EQ(filed_count, 4, "mysql_stmt_field_count"); + ASSERT_EQ(param_count, 0, "mysql_stmt_param_count"); + unsigned int iteration_count = 1; + //stmt->iteration_count = 0; + if (mysql_stmt_execute(stmt)) { + my_log("mysql_stmt_execute select failed:%s", mysql_stmt_error(stmt)); + return -1; + } + if (mysql_stmt_store_result(stmt)) { + my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + return -1; + } + filed_count = mysql_stmt_field_count(stmt); + ASSERT_EQ(filed_count, 4, "mysql_stmt_field_count"); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + ASSERT_EQ(row_count, 1, "mysql_stmt_field_count"); + memset(bind, 0, sizeof(bind)); + char buffer[4][128]; + memset(buffer, 0, 4* 128); + for (i = 0; i < 4; i++) + { + bind[i].buffer = buffer[i]; + bind[i].buffer_length = 128; + bind[i].buffer_type = MYSQL_TYPE_STRING; + } + if (mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + return -1; + } + while (mysql_stmt_fetch(stmt) == 0) { + for (i =0; i < filed_count; i++) + { + my_log("col[%d]:%s", i, buffer[i]); + } + } + mysql_stmt_close(stmt); + mysql_real_query(mysql, "commit", strlen("commit")); + return 0; +} +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + unsigned int timeout = 3000; + init_conn_info(OB_ORACLE_MODE); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + int ret = 0; + mysql_real_query(mysql, "drop table timetest", strlen("drop table timetest")); + ret = mysql_query(mysql, "create table timetest(tme date,tmestp timestamp(3),"\ + "tmestp_tz timestamp(3) with time zone,"\ + "tmpstp_tzl timestamp(3) with local time zone);"); + if (ret != 0){ + printf("create table failed, %s\n", mysql_error(mysql)); + return ret; + } + do_text_time_test1(mysql); + do_text_time_test2(mysql); + do_ps_time_test1(mysql); + return 0; +} diff --git a/unittest/my_test/ob_oracle_fetch_cursor_test.c b/unittest/my_test/ob_oracle_fetch_cursor_test.c new file mode 100644 index 0000000..74abd8a --- /dev/null +++ b/unittest/my_test/ob_oracle_fetch_cursor_test.c @@ -0,0 +1,428 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" + +int stmt_oracle_cursor_test_with_param_no_result(MYSQL* mysql) { + my_log("stmt_oracle_cursor_test_with_param_no_result"); + const char* pStatement = "select * from test_new_ps where id = ? and num = ? and name = ?"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + MYSQL_BIND param[4]; + int ret = 0; + int param_count; + int filed_count; + my_ulonglong affected_rows; + const char* query = "delete from test_new_ps"; + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + int input_id = 0, input_num = 0; + char input_name[20] = "test"; + unsigned long str_len; + { + //id + param[0].buffer_type = MYSQL_TYPE_LONG; + param[0].buffer = (char*)&input_id; + param[0].buffer_length = 0; + param[0].is_null = 0; + param[0].length = 0; + // bind[0].is_unsigned = 0; + } + { + //num + param[1].buffer_type = MYSQL_TYPE_LONG; + param[1].buffer = (char*)&input_num; + param[1].buffer_length = 0; + param[1].is_null = 0; + param[1].length = 0; + // bind[1].is_unsigned = 0; + } + { + //name + str_len = strlen(input_name); + my_log("str_len for name is %d", str_len); + param[2].buffer_type = MYSQL_TYPE_STRING; + param[2].buffer = (char*)input_name; + param[2].buffer_length = str_len; + param[2].is_null = 0; + param[2].length = &str_len; + // bind[2].is_unsigned = 0; + } + if (mysql_stmt_bind_param(stmt, param)) { + my_log("bind failed:%s", mysql_stmt_error(stmt)); + return -1; + } + unsigned int iteration_count = 0; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_execute_v2"); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (ret = mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_bind_result"); + } + while (mysql_stmt_fetch_oracle_implicit_cursor(stmt, 1) == 0) { + my_log("%d\t%d\t%s\t%s", id, num, name, birthday); + } + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faled:%s", mysql_stmt_error(stmt)); + } + ASSERT_EQ(0, 0, "stmt_oracle_cursor_test_with_param_no_result"); + return 0; +} +int stmt_oracle_cursor_test_no_param_with_result(MYSQL* mysql) { + my_log("stmt_oracle_cursor_test"); + const char* pStatement = "select * from test_new_ps"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int ret = 0; + int param_count; + int filed_count; + my_ulonglong affected_rows; + int i = 0; + char query[1000] = {0}; + snprintf(query, 1000, "delete from test_new_ps"); + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + for (i = 0; i < 10; i++) + { + snprintf(query, 1000, "insert into test_new_ps(id, num, name, birthday) values(%d,%d, 'name_%d', 'birth_%d')", i ,i, i, i); + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + } + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + unsigned int iteration_count = 1; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (ret = mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_bind_result"); + } + i = 0; + while (mysql_stmt_fetch_oracle_implicit_cursor(stmt, 0) == 0) { + i++; + printf("%d\t%d\t%s\t%s\n", id, num, name, birthday); + } + ASSERT_EQ(10, i, "mysql_stmt_fetch"); + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faled:%s", mysql_stmt_error(stmt)); + } + ASSERT_EQ(0, 0, "stmt_oracle_cursor_test_no_param_with_result"); + return 0; +} +int stmt_oracle_cursor_test_with_param_with_result(MYSQL* mysql) { + my_log("stmt_oracle_cursor_test_with_param_with_result"); + const char* pStatement = "select * from test_new_ps where id = ? and num = ? and name = ?"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + MYSQL_BIND param[4]; + int ret = 0; + int param_count; + int filed_count; + int i = 0; + my_ulonglong affected_rows; + char query [400]; + snprintf(query, 400, "delete from test_new_ps"); + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + for (i = 0; i < 10; i++) + { + snprintf(query, 1000, "insert into test_new_ps(id, num, name, birthday) values(%d,%d, 'name_%d', 'birth_%d')", i ,i, i, i); + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + } + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + int input_id = 0, input_num = 0; + char input_name[20] = "name_0"; + unsigned long str_len; + { + //id + param[0].buffer_type = MYSQL_TYPE_LONG; + param[0].buffer = (char*)&input_id; + param[0].buffer_length = 0; + param[0].is_null = 0; + param[0].length = 0; + // bind[0].is_unsigned = 0; + } + { + //num + param[1].buffer_type = MYSQL_TYPE_LONG; + param[1].buffer = (char*)&input_num; + param[1].buffer_length = 0; + param[1].is_null = 0; + param[1].length = 0; + // bind[1].is_unsigned = 0; + } + { + //name + str_len = strlen(input_name); + my_log("str_len for name is %d", str_len); + param[2].buffer_type = MYSQL_TYPE_STRING; + param[2].buffer = (char*)input_name; + param[2].buffer_length = str_len; + param[2].is_null = 0; + param[2].length = &str_len; + // bind[2].is_unsigned = 0; + } + if (mysql_stmt_bind_param(stmt, param)) { + my_log("bind failed:%s", mysql_stmt_error(stmt)); + return -1; + } + unsigned int iteration_count = 0; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_execute_v2"); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (ret = mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_bind_result"); + } + while (mysql_stmt_fetch_oracle_implicit_cursor(stmt, 1) == 0) { + my_log("%d\t%d\t%s\t%s", id, num, name, birthday); + } + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faled:%s", mysql_stmt_error(stmt)); + } + ASSERT_EQ(0, 0, "stmt_oracle_cursor_test_with_param_with_result"); + return 0; +} +int stmt_oracle_cursor_test_no_param_no_result_exact_not_match(MYSQL* mysql) { + my_log("stmt_oracle_cursor_test_no_param_no_result"); + const char* pStatement = "select * from test_new_ps"; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + int ret = 0; + int param_count; + int filed_count; + my_ulonglong affected_rows; + const char* query = "delete from test_new_ps"; + if (ret = mysql_real_query(mysql, query, strlen(query))) { + my_log("mysql_real_query failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_real_query"); + } + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(-1, 0, "mysql_stmt_init"); + return -1; + } + if (ret = mysql_stmt_prepare_v2(stmt, pStatement, strlen(pStatement), 0)) { + my_log("mysql_stmt_prepare_v2 failed:%s", mysql_error(mysql)); + ASSERT_EQ(ret, 0, "mysql_stmt_prepare_v2"); + } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + param_count = mysql_stmt_param_count(stmt); + my_log("param_count is %d", param_count); + unsigned int iteration_count = 2; + // mysql_stmt_attr_set(stmt, STMT_ATTR_ITERRATION_COUNT, &iteration_count); + //stmt->iteration_count = 0; + if (ret = mysql_stmt_execute_v2(stmt, pStatement, strlen(pStatement), iteration_count, 0x00000002, 0)) { + my_log("mysql_stmt_execute_v2 select failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(1, ret, "mysql_stmt_execute_v2"); + return -1; + } else { + ASSERT_EQ(-1, ret, "mysql_stmt_execute_v2"); + return -1; + } + // if (mysql_stmt_store_result(stmt)) { + // my_log("mysql_stmt_store_result failed :%s", mysql_stmt_error(stmt)); + // return -1; + // } + filed_count = mysql_stmt_field_count(stmt); + my_log("filed_count is %u", filed_count); + my_ulonglong row_count = mysql_stmt_num_rows(stmt); + my_log("row count is %u", row_count); + memset(bind, 0, sizeof(bind)); + int id, num; + char name[20]; + char birthday[20]; + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = &id; + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = # + bind[2].buffer_type = MYSQL_TYPE_STRING; + bind[2].buffer = name; + bind[2].buffer_length = sizeof(name); + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = birthday; + bind[3].buffer_length = sizeof(birthday); + + if (ret = mysql_stmt_bind_result(stmt, bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(ret, 0, "mysql_stmt_bind_result"); + } + while (mysql_stmt_fetch_oracle_implicit_cursor(stmt, 1) == 0) { + printf("%d\t%d\t%s\t%s\n", id, num, name, birthday); + } + if (ret = mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faled:%s", mysql_stmt_error(stmt)); + } + ASSERT_EQ(-1, 0, "stmt_oracle_cursor_test_no_param_no_result"); + return 0; +} +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + unsigned int timeout = 3000; + init_conn_info(OB_ORACLE_MODE); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s\n", mysql_error(mysql)); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect to %s:%d using %s success", DBHOST, DBPORT, DBUSER); + } + int ret = mysql_query(mysql, "SET NAMES utf8"); + if (ret != 0) + { + printf("set names failed, %s\n", mysql_error(mysql)); + return ret; + } + mysql_query(mysql, "drop table test_new_ps"); + ret = mysql_query(mysql, "create table test_new_ps(id int, num int, name varchar2(20), birthday varchar2(20))"); + if (ret != 0) + { + my_log("create table error, %s\n", mysql_error(mysql)); + return ret; + } + stmt_oracle_cursor_test_no_param_with_result(mysql); + stmt_oracle_cursor_test_with_param_no_result(mysql); + stmt_oracle_cursor_test_with_param_with_result(mysql); + return 0; +} \ No newline at end of file diff --git a/unittest/my_test/ob_oracle_pl_test.c b/unittest/my_test/ob_oracle_pl_test.c new file mode 100644 index 0000000..5e36329 --- /dev/null +++ b/unittest/my_test/ob_oracle_pl_test.c @@ -0,0 +1,125 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" +/* + * + */ +int do_pl_test(MYSQL* mysql) { + my_log("=========do_pl_test=========="); + const char* create_pl = "create or replace procedure pl_test(x in number, y in out varchar2) "\ + "is begin "\ + " y:='this is a pl test';"\ + "END"; + int ret = 0; + if (mysql_real_query(mysql, create_pl, strlen(create_pl))) { + my_log("mysql_real_query failed: %s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_real_query failed"); + return -1; + } + const char* pStatement = "call pl_test(?, ?)"; + MYSQL_STMT *stmt; + MYSQL_BIND param_bind[2]; + MYSQL_BIND res_bind[2]; + int param_count; + int id = 8; + char name[20] = "jh"; + my_ulonglong affected_rows; + stmt = mysql_stmt_init(mysql); + if (!stmt) { + my_log("msyql_stmt_init failed: %s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_stmt_init failed"); + return -1; + } + if (mysql_stmt_prepare(stmt, pStatement, strlen(pStatement))) { + my_log("mysql_stmt_prepare failed:%s", mysql_error(mysql)); + ASSERT_EQ(0, -1, "mysql_stmt_prepare failed"); + return -1; + } + param_count = mysql_stmt_param_count(stmt); + ASSERT_EQ(param_count, 2, "mysql_stmt_param_count"); + MYSQL_RES *param_result = NULL; + MYSQL_FIELD *params = NULL; + if (NULL == (param_result = mysql_stmt_param_metadata(stmt))) { + my_log("mysql_stmt_param_metadata failed:%s", mysql_stmt_error(stmt)); + } else if (NULL == (params = mysql_fetch_params(param_result))) { + my_log("mysql_fetch_params failed:%s", mysql_stmt_error(stmt)); + } else { + ASSERT_EQ(params[1].ob_routine_param_inout, 3, "ob_routine_param_inout"); + } + memset(param_bind, 0, sizeof(param_bind)); + memset(res_bind, 0, sizeof(res_bind)); + { + //id + param_bind[0].buffer_type = MYSQL_TYPE_LONG; + param_bind[0].buffer = (char*)&id; + param_bind[0].buffer_length = 0; + param_bind[0].is_null = 0; + param_bind[0].length = 0; + } + { + //name + unsigned long str_len = strlen(name); + param_bind[1].buffer_type = MYSQL_TYPE_STRING; + param_bind[1].buffer = (char*)name; + param_bind[1].buffer_length = str_len; + param_bind[1].is_null = 0; + param_bind[1].length = &str_len; + // bind[1].is_unsigned = 0; + } + if (mysql_stmt_bind_param(stmt, param_bind)) { + my_log("bind failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(0, -1, "mysql_stmt_bind_param failed"); + return -1; + } + if (mysql_stmt_execute(stmt)) { + my_log("mysql_stmt_execute insert failed:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(0, -1, "mysql_stmt_execute failed"); + return -1; + } + char out_buf[20] = {0}; + res_bind[0].buffer_type = MYSQL_TYPE_STRING; + res_bind[0].buffer = out_buf; + res_bind[0].buffer_length = sizeof(out_buf); + + if (mysql_stmt_bind_result(stmt, res_bind)) { + my_log("mysql_stmt_bind_result failed:%s", mysql_stmt_error(stmt)); + return -1; + } + int i = 1; + while (mysql_stmt_fetch(stmt) == 0) { + // my_log("out_buf is %s", out_buf); + if (strcmp(out_buf, "this is a pl test") == 0) { + ASSERT_EQ(0, 0, "mysql_stmt_fetch"); + } else { + ASSERT_EQ(0, -1, "mysql_stmt_fetch"); + } + } + if (mysql_stmt_close(stmt)) { + my_log("mysql_stmt_close faield:%s", mysql_stmt_error(stmt)); + ASSERT_EQ(0, -1, "mysql_stmt_error"); + return -1; + } + return 0; +} + +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + unsigned int timeout = 3000; + init_conn_info(OB_ORACLE_MODE); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + do_pl_test(mysql); + return 0; +} diff --git a/unittest/my_test/ob_oracle_ps_test.c b/unittest/my_test/ob_oracle_ps_test.c index cc7f5fb..700748f 100644 --- a/unittest/my_test/ob_oracle_ps_test.c +++ b/unittest/my_test/ob_oracle_ps_test.c @@ -163,7 +163,7 @@ int do_stmt_select(MYSQL* mysql) { filed_count = mysql_stmt_field_count(stmt); ASSERT_EQ(filed_count, 4, "mysql_stmt_field_count"); my_ulonglong row_count = mysql_stmt_num_rows(stmt); - ASSERT_EQ(row_count, 9, "mysql_stmt_field_count"); + ASSERT_EQ(row_count, 9, "mysql_stmt_num_rows"); memset(bind, 0, sizeof(bind)); int id, num; char name[20]; diff --git a/unittest/my_test/set_env.sh b/unittest/my_test/set_env.sh deleted file mode 100644 index 7e8810b..0000000 --- a/unittest/my_test/set_env.sh +++ /dev/null @@ -1,15 +0,0 @@ -#mysql-mode -#obclient -h100.88.109.130 -P18815 -uadmin@mysql -padmin -Doceanbase -A -export LD_LIBRARY_PATH=../../libmariadb/:$LD_LIBRARY_PATH -export OB_MYSQL_SERVER_HOST=100.88.109.130 -export OB_MYSQL_SERVER_PORT=18815 -export OB_MYSQL_SERVER_DBNAME="test" -export OB_MYSQL_SERVER_USERNAME="admin@mysql" -export OB_MYSQL_SERVER_PASSWORD="admin" -#oracle-mode -#obclient -h100.88.105.197 -P30035 -utest@tt3 -ptest -export OB_ORACLE_SERVER_HOST=100.88.105.197 -export OB_ORACLE_SERVER_PORT=30035 -export OB_ORACLE_SERVER_DBNAME="test" -export OB_ORACLE_SERVER_USERNAME="test@tt3" -export OB_ORACLE_SERVER_PASSWORD="test" diff --git a/unittest/my_test/test_util.h b/unittest/my_test/test_util.h index 4c9d6d7..b59085c 100644 --- a/unittest/my_test/test_util.h +++ b/unittest/my_test/test_util.h @@ -3,12 +3,11 @@ #include #include #include -//obclient -h 11.158.31.35 -usys@oracle -P27701 -const char* DBHOST = "11.158.31.35"; -const char* DBUSER = "sys@oracle"; +const char* DBHOST = ""; +const char* DBUSER = ""; const char* DBPASS = ""; -unsigned int DBPORT = 27701; -const char* DBNAME = "sys"; +unsigned int DBPORT = 0; +const char* DBNAME = ""; const char* DBSOCK = NULL; //"/var/lib/mysql/mysql.sock" const unsigned long DBPCNT = 0; #ifdef __cplusplus diff --git a/unittest/ob20_test/Makefile b/unittest/ob20_test/Makefile new file mode 100644 index 0000000..dd3561a --- /dev/null +++ b/unittest/ob20_test/Makefile @@ -0,0 +1,17 @@ +CFLAGS= -I../../include +CCFLAGS= -I../../include +LDFLAGS= -g -L ../../libmariadb/ -lobclnt -lz -lpthread -lgtest +BIN=ob20_appinfo_test ob20_trans_test ob20_clientinfo_test ob20_slowquery_test ob20_bigpacket_response_test ob20_bigpacket_request_test +EXEC=env LD_LIBRARY_PATH=../../libmariadb/:${LD_LIBRARY_PATH} +all:${BIN} +.PHONY:clean +ob20_appinfo_test:ob20_appinfo_test.c +ob20_trans_test:ob20_trans_test.c +ob20_clientinfo_test:ob20_clientinfo_test.c +ob20_slowquery_test:ob20_slowquery_test.c +ob20_bigpacket_response_test:ob20_bigpacket_response_test.c +ob20_bigpacket_request_test:ob20_bigpacket_request_test.c +# run: +# $(EXEC) ./ob_oracle_array_binding_test +clean: + rm -rf ${BIN} *.o diff --git a/unittest/ob20_test/ob20_appinfo_test.c b/unittest/ob20_test/ob20_appinfo_test.c new file mode 100644 index 0000000..6b5c7d6 --- /dev/null +++ b/unittest/ob20_test/ob20_appinfo_test.c @@ -0,0 +1,73 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" +#include "ob_protocol20.h" + +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + MYSQL_RES *RES; + unsigned int timeout = 3000; + if (6 == argc) { + DBHOST = argv[1]; + DBUSER = argv[2]; + DBPASS = argv[3]; + DBNAME = argv[4]; + DBPORT = atoi(argv[5]); + + printf("host is %s, user is %s, pass is %s, name is %s, port is %u\n", DBHOST, DBUSER, DBPASS, DBNAME, DBPORT); + } + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + int ret = 0; + + { + const char *module = "mod"; + const char *action = "act"; + flt_set_module(mysql, module); + flt_set_action(mysql, action); + if (mysql_query(mysql, "select 1 from dual where 1 = 0;")) { + printf("query one error\n"); + return 0; + } else { + RES = mysql_store_result(mysql); + } + } + + { + // const char *module = "module"; + const char *action = "act1"; + // flt_set_module(mysql, module); + flt_set_action(mysql, action); + if (mysql_query(mysql, "select 1 from dual where 1 = 0;")) { + printf("query two error\n"); + return 0; + } else { + RES = mysql_store_result(mysql); + } + } + + { + const char *identifier = "id1"; + flt_set_identifier(mysql, identifier); + if (mysql_query(mysql, "select 1 from dual where 1 = 0;")) { + printf("query four error\n"); + return 0; + } else { + RES = mysql_store_result(mysql); + } + } + + mysql_library_end(); + return 0; +} diff --git a/unittest/ob20_test/ob20_bigpacket_request_test.c b/unittest/ob20_test/ob20_bigpacket_request_test.c new file mode 100644 index 0000000..6d81ca0 --- /dev/null +++ b/unittest/ob20_test/ob20_bigpacket_request_test.c @@ -0,0 +1,61 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" +#include "ob_object.h" +#include "ob_protocol20.h" +#include "ob_full_link_trace.h" + +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + MYSQL_RES *RES; + unsigned int timeout = 3000; + char *big_query; + size_t big_query_size = 256 * 256 * 256 - 1; // 16M请求 + + // init_conn_info(OB_MYSQL_MODE); + // mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + if (6 == argc) { + DBHOST = argv[1]; + DBUSER = argv[2]; + DBPASS = argv[3]; + DBNAME = argv[4]; + DBPORT = atoi(argv[5]); + + printf("host is %s, user is %s, pass is %s, name is %s, port is %u\n", DBHOST, DBUSER, DBPASS, DBNAME, DBPORT); + } + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + + // malloc 16M size query + big_query = (char *)malloc(big_query_size); + if (NULL == big_query) { + printf("malloc error\n"); + return 0; + } else { + memset(big_query, 0, big_query_size); + memcpy(big_query, "select 1 from dual where 1 = 0;", sizeof("select 1 from dual where 1 = 0;")); + } + + { + if (mysql_real_query(mysql, big_query, big_query_size)) { + printf("query one error, %d, %s\n", mysql_errno(mysql), mysql_error(mysql)); + return 0; + } else { + RES = mysql_store_result(mysql); + } + } + + mysql_library_end(); + return 0; +} diff --git a/unittest/ob20_test/ob20_bigpacket_response_test.c b/unittest/ob20_test/ob20_bigpacket_response_test.c new file mode 100644 index 0000000..8ac0b42 --- /dev/null +++ b/unittest/ob20_test/ob20_bigpacket_response_test.c @@ -0,0 +1,57 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" +#include "ob_object.h" +#include "ob_protocol20.h" +#include "ob_full_link_trace.h" + +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + MYSQL_RES *RES; + unsigned int timeout = 3000; + // init_conn_info(OB_MYSQL_MODE); + // mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + if (6 == argc) { + DBHOST = argv[1]; + DBUSER = argv[2]; + DBPASS = argv[3]; + DBNAME = argv[4]; + DBPORT = atoi(argv[5]); + + printf("host is %s, user is %s, pass is %s, name is %s, port is %u\n", DBHOST, DBUSER, DBPASS, DBNAME, DBPORT); + } + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + + { + if (mysql_query(mysql, "select * from gv$sql_audit;")) { + printf("query one error\n"); + return 0; + } else { + RES = mysql_store_result(mysql); + } + } + + { + if (mysql_query(mysql, "select 1 from dual where 1 = 0;")) { + printf("query four error\n"); + return 0; + } else { + RES = mysql_store_result(mysql); + } + } + + mysql_library_end(); + return 0; +} diff --git a/unittest/ob20_test/ob20_clientinfo_test.c b/unittest/ob20_test/ob20_clientinfo_test.c new file mode 100644 index 0000000..dff56f6 --- /dev/null +++ b/unittest/ob20_test/ob20_clientinfo_test.c @@ -0,0 +1,51 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" +#include "ob_object.h" +#include "ob_protocol20.h" +#include "ob_full_link_trace.h" + +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + MYSQL_RES *RES; + unsigned int timeout = 3000; + // init_conn_info(OB_MYSQL_MODE); + // mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + if (6 == argc) { + DBHOST = argv[1]; + DBUSER = argv[2]; + DBPASS = argv[3]; + DBNAME = argv[4]; + DBPORT = atoi(argv[5]); + + printf("host is %s, user is %s, pass is %s, name is %s, port is %u\n", DBHOST, DBUSER, DBPASS, DBNAME, DBPORT); + } + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + + { + const char *client_info = "cli"; + flt_set_client_info(mysql, client_info); + if (mysql_query(mysql, "select 1 from dual where 1 = 0;")) { + printf("query one error\n"); + return 0; + } else { + RES = mysql_store_result(mysql); + sleep(10); + } + } + + mysql_library_end(); + return 0; +} diff --git a/unittest/ob20_test/ob20_slowquery_test.c b/unittest/ob20_test/ob20_slowquery_test.c new file mode 100644 index 0000000..54af0e5 --- /dev/null +++ b/unittest/ob20_test/ob20_slowquery_test.c @@ -0,0 +1,55 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" +#include "ob_object.h" +#include "ob_protocol20.h" +#include "ob_full_link_trace.h" + +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + MYSQL_RES *RES; + unsigned int timeout = 3000; + if (6 == argc) { + DBHOST = argv[1]; + DBUSER = argv[2]; + DBPASS = argv[3]; + DBNAME = argv[4]; + DBPORT = atoi(argv[5]); + + printf("host is %s, user is %s, pass is %s, name is %s, port is %u\n", DBHOST, DBUSER, DBPASS, DBNAME, DBPORT); + } + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + + { + if (mysql_query(mysql, "select sleep(2) from dual")) { + printf("query one error, %s\n", mysql_error(mysql)); + return 0; + } else { + RES = mysql_store_result(mysql); + } + } + + { + if (mysql_query(mysql, "commit;")) { + printf("query four error, %s\n", mysql_error(mysql)); + return 0; + } else { + RES = mysql_store_result(mysql); + } + } + + mysql_library_end(); + return 0; +} diff --git a/unittest/ob20_test/ob20_trans_test.c b/unittest/ob20_test/ob20_trans_test.c new file mode 100644 index 0000000..1f9259e --- /dev/null +++ b/unittest/ob20_test/ob20_trans_test.c @@ -0,0 +1,72 @@ +#include +#include +#include "mysql.h" +#include "test_util.h" +#include "ob_object.h" +#include "ob_protocol20.h" +#include "ob_full_link_trace.h" + +int main(int argc, char** argv) { + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + MYSQL_RES *RES; + unsigned int timeout = 3000; + if (6 == argc) { + DBHOST = argv[1]; + DBUSER = argv[2]; + DBPASS = argv[3]; + DBNAME = argv[4]; + DBPORT = atoi(argv[5]); + + printf("host is %s, user is %s, pass is %s, name is %s, port is %u\n", DBHOST, DBUSER, DBPASS, DBNAME, DBPORT); + } + + if (mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, DBSOCK, DBPCNT) == NULL) + { + my_log("connect failed: %s, host:%s, port:%d, user:%s, pass:%s, dbname:%s", + mysql_error(mysql),DBHOST, DBPORT, DBUSER, DBPASS, DBNAME); + mysql_close(mysql); + mysql_library_end(); + return 0; + } else { + my_log("connect %s:%d using %s succ", DBHOST, DBPORT, DBUSER); + } + + if (mysql_query(mysql, "create table test_trace(c1 number);")) { + printf("query 0 error\n"); + return 0; + } else { + RES = mysql_store_result(mysql); + } + + { + int i = 0; + for (; i < 20; ++i) { + if (mysql_query(mysql, "insert into test_trace values (1);")) { + printf("query one error\n"); + return 0; + } else { + RES = mysql_store_result(mysql); + } + } + } + + { + if (mysql_query(mysql, "commit")) { + printf("commit error\n"); + return 0; + } else { + RES = mysql_store_result(mysql); + } + } + if (mysql_query(mysql, "drop table test_trace;")) { + printf("query one error\n"); + return 0; + } else { + RES = mysql_store_result(mysql); + } + + mysql_close(mysql); + mysql_library_end(); + return 0; +} diff --git a/unittest/ob20_test/run.sh b/unittest/ob20_test/run.sh new file mode 100644 index 0000000..172870d --- /dev/null +++ b/unittest/ob20_test/run.sh @@ -0,0 +1,2 @@ +export LD_LIBRARY_PATH=../../libmariadb/:${LD_LIBRARY_PATH} +./$1 diff --git a/unittest/ob20_test/test_util.h b/unittest/ob20_test/test_util.h new file mode 100644 index 0000000..34f4a3d --- /dev/null +++ b/unittest/ob20_test/test_util.h @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +const char* DBHOST = ""; +const char* DBUSER = ""; +const char* DBPASS = ""; +unsigned int DBPORT = 0; +const char* DBNAME = "SYS"; +const char* DBSOCK = NULL; //"/var/lib/mysql/mysql.sock" +const unsigned long DBPCNT = 0; +#ifdef __cplusplus +extern "C" { +#endif +enum DB_MODE +{ + OB_MYSQL_MODE = 0, + OB_ORACLE_MODE = 1 +}; +void init_conn_info(int mode) +{ + const char* env_name = (mode == OB_ORACLE_MODE) ? "OB_ORACLE_SERVER_USERNAME": "OB_MYSQL_SERVER_USERNAME"; + const char* tmp = getenv(env_name); + if (tmp != NULL) + { + DBUSER = tmp; + } + env_name = (mode == OB_ORACLE_MODE) ? "OB_ORACLE_SERVER_PASSWORD" : "OB_MYSQL_SERVER_PASSWORD"; + tmp = getenv(env_name); + if (tmp != NULL) + { + DBPASS = tmp; + } + env_name = (mode == OB_ORACLE_MODE) ? "OB_ORACLE_SERVER_HOST" : "OB_MYSQL_SERVER_HOST"; + tmp = getenv(env_name); + if (tmp != NULL) + { + DBHOST = tmp; + } + env_name = (mode == OB_ORACLE_MODE) ? "OB_ORACLE_SERVER_PORT" : "OB_MYSQL_SERVER_PORT"; + tmp = getenv(env_name); + if (tmp != NULL) + { + DBPORT = atoi(tmp); + } + env_name = (mode == OB_ORACLE_MODE) ? "OB_ORACLE_SERVER_DBNAME" : "OB_MYSQL_SERVER_DBNAME"; + tmp = getenv(env_name); + if (tmp != NULL) + { + DBNAME = tmp; + } +} +const char * getRelativeFileName(const char* file_name) +{ + int j = 0; + size_t len = strlen(file_name); + if (len <= 2) { + printf("too short \n"); + return file_name; + } + const char *ptr = file_name; + for (j = len - 2; j >=0; j--) { + if (ptr[j] == '/') + { + break; + } + } + return &ptr[j+1]; +} +#define ASSERT_EQ(a, b, info) assert_internal(__FILE__, __LINE__, __FUNCTION__, a, b, info) +void assert_internal(const char* file, int lineno, const char* func, int a, int b, const char* info) +{ + if(a == b){ + printf("[%s:%s:%d]: %-40s ------- \e[1;0;32;1m PASS \e[0m\n", getRelativeFileName(file), func, lineno, info); + } + else { + printf("[%s:%s:%d]: %-40s------- \e[1;0;31;1m FAILED \e[0m, expected:%d,real:%d\n", + getRelativeFileName(file), func, lineno, info, a, b); + } +} +#define my_log(fmt, ...) \ + my_log_internal(stdout, __FILE__, __LINE__, fmt, ##__VA_ARGS__) + +static void my_log_internal(FILE* fp, const char* file_name, int lineno, const char* fmt, ...) { + time_t now = time(NULL); + struct tm* local; + local = localtime(&now); + fprintf(fp, "[%04d-%02d-%02d %02d:%02d:%02d][%s:%d] ", 1900 + local->tm_year, local->tm_mon + 1, + local->tm_mday, local->tm_hour, local->tm_min, local->tm_sec, getRelativeFileName(file_name), lineno); + va_list ap; + va_start(ap, fmt); + vfprintf(fp, fmt, ap); + fprintf(fp, "\n"); + va_end(ap); + fflush(fp); +} +#ifdef __cplusplus +} +#endif diff --git a/zlib/compress.c b/zlib/compress.c index e2db404..33c32fa 100644 --- a/zlib/compress.c +++ b/zlib/compress.c @@ -7,6 +7,7 @@ #define ZLIB_INTERNAL #include "zlib.h" +#include "string.h" /* =========================================================================== Compresses the source buffer into the destination buffer. The level @@ -34,6 +35,8 @@ int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) left = *destLen; *destLen = 0; + memset(&stream, 0, sizeof(stream)); + stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; stream.opaque = (voidpf)0; diff --git a/zlib/gzguts.h b/zlib/gzguts.h index 990a4d2..807ca63 100644 --- a/zlib/gzguts.h +++ b/zlib/gzguts.h @@ -33,6 +33,8 @@ #ifdef _WIN32 # include +#else +#include #endif #if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) diff --git a/zlib/uncompr.c b/zlib/uncompr.c index f03a1a8..67ca10e 100644 --- a/zlib/uncompr.c +++ b/zlib/uncompr.c @@ -7,6 +7,7 @@ #define ZLIB_INTERNAL #include "zlib.h" +#include "string.h" /* =========================================================================== Decompresses the source buffer into the destination buffer. *sourceLen is @@ -36,6 +37,8 @@ int ZEXPORT uncompress2 (dest, destLen, source, sourceLen) uLong len, left; Byte buf[1]; /* for detection of incomplete stream when *destLen == 0 */ + memset(&stream, 0, sizeof(stream)); + len = *sourceLen; if (*destLen) { left = *destLen;