diff options
Diffstat (limited to 'storage')
54 files changed, 17995 insertions, 104 deletions
diff --git a/storage/Makefile.am b/storage/Makefile.am index b978453d29d..4f19be3a361 100644 --- a/storage/Makefile.am +++ b/storage/Makefile.am @@ -20,6 +20,7 @@ AUTOMAKE_OPTIONS = foreign # These are built from source in the Docs directory EXTRA_DIST = SUBDIRS = @mysql_se_dirs@ +DIST_SUBDIRS = @mysql_se_distdirs@ # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/storage/ibmdb2i/CMakeLists.txt b/storage/ibmdb2i/CMakeLists.txt new file mode 100644 index 00000000000..11cc4300569 --- /dev/null +++ b/storage/ibmdb2i/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (C) 2006 MySQL AB +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") +SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/extra/yassl/include) +ADD_LIBRARY(ibmdb2i ha_ibmdb2i.cc db2i_ileBridge.cc db2i_conversion.cc + db2i_blobCollection.cc db2i_file.cc db2i_charsetSupport.cc + db2i_collationSupport.cc db2i_errors.cc db2i_constraints.cc + db2i_rir.cc db2i_sqlStatementStream.cc db2i_ioBuffers.cc db2i_myconv.cc) diff --git a/storage/ibmdb2i/Makefile.am b/storage/ibmdb2i/Makefile.am new file mode 100644 index 00000000000..2436a764429 --- /dev/null +++ b/storage/ibmdb2i/Makefile.am @@ -0,0 +1,53 @@ +# +# Copyright (c) 2007, 2008, IBM Corporation. +# All rights reserved. +# +# + +#called from the top level Makefile + +MYSQLDATAdir = $(localstatedir) +MYSQLSHAREdir = $(pkgdatadir) +MYSQLBASEdir= $(prefix) +MYSQLLIBdir= $(pkglibdir) +pkgplugindir = $(pkglibdir)/plugin +INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \ + -I$(top_srcdir)/regex \ + -I$(top_srcdir)/sql \ + -I$(srcdir) \ + -I$ /afs/rchland.ibm.com/lande/shadow/dev2000/osxpf/v5r4m0f.xpf/cur/cmvc/base.pgm/my.xpf/apis \ + -I$ /afs/rchland.ibm.com/lande/shadow/dev2000/osxpf/v5r4m0.xpf/bld/cmvc/base.pgm/lg.xpf \ + -I$ /afs/rchland.ibm.com/lande/shadow/dev2000/osxpf/v5r4m0.xpf/bld/cmvc/base.pgm/tq.xpf +WRAPLIBS= + +LDADD = + +DEFS = @DEFS@ + +noinst_HEADERS = ha_ibmdb2i.h db2i_collationSupport.h db2i_file.h \ + db2i_ioBuffers.h db2i_blobCollection.h \ + db2i_global.h db2i_misc.h db2i_charsetSupport.h db2i_errors.h \ + db2i_ileBridge.h db2i_validatedPointer.h + +EXTRA_LTLIBRARIES = ha_ibmdb2i.la +pkgplugin_LTLIBRARIES = @plugin_ibmdb2i_shared_target@ +ha_ibmdb2i_la_LIBADD = -liconv +ha_ibmdb2i_la_LDFLAGS = -module -rpath $(MYSQLLIBdir) +ha_ibmdb2i_la_CXXFLAGS= $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN +ha_ibmdb2i_la_CFLAGS = $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN +ha_ibmdb2i_la_SOURCES = ha_ibmdb2i.cc db2i_ileBridge.cc db2i_conversion.cc \ + db2i_blobCollection.cc db2i_file.cc db2i_charsetSupport.cc \ + db2i_collationSupport.cc db2i_errors.cc db2i_constraints.cc \ + db2i_rir.cc db2i_sqlStatementStream.cc db2i_ioBuffers.cc \ + db2i_myconv.cc + +EXTRA_LIBRARIES = libibmdb2i.a +noinst_LIBRARIES = @plugin_ibmdb2i_static_target@ +libibmdb2i_a_CXXFLAGS = $(AM_CFLAGS) +libibmdb2i_a_CFLAGS = $(AM_CFLAGS) +libibmdb2i_a_SOURCES= $(ha_ibmdb2i_la_SOURCES) + + +EXTRA_DIST = CMakeLists.txt plug.in +# Don't update the files from bitkeeper +%::SCCS/s.% diff --git a/storage/ibmdb2i/db2i_blobCollection.cc b/storage/ibmdb2i/db2i_blobCollection.cc new file mode 100644 index 00000000000..17101c9c0a4 --- /dev/null +++ b/storage/ibmdb2i/db2i_blobCollection.cc @@ -0,0 +1,107 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#include "db2i_blobCollection.h" + +/** + Return the size to use when allocating space for blob reads. + + @param fieldIndex The field to allocate for + @param[out] shouldProtect Indicates whether storage protection should be + applied to the space, because the size returned is + smaller than the maximum possible size. +*/ + +uint32 +BlobCollection::getSizeToAllocate(int fieldIndex, bool& shouldProtect) +{ + Field* field = table->getMySQLTable()->field[fieldIndex]; + uint fieldLength = field->max_display_length(); + + if (fieldLength <= MAX_FULL_ALLOCATE_BLOB_LENGTH) + { + shouldProtect = false; + return fieldLength; + } + + shouldProtect = true; + + uint curMaxSize = table->getBlobFieldActualSize(fieldIndex); + + uint defaultAllocSize = min(defaultAllocation, fieldLength); + + return max(defaultAllocSize, curMaxSize); + +} + +void +BlobCollection::generateBuffer(int fieldIndex) +{ + DBUG_ASSERT(table->db2Field(fieldIndex).isBlob()); + + bool protect; + buffers[table->getBlobIdFromField(fieldIndex)].Malloc(getSizeToAllocate(fieldIndex, protect), protect); + + return; +} + +/** + Realloc the read buffer associated with a blob field. + + This is used when the previous allocation for a blob field is found to be + too small (this is discovered when QMY_READ trips over the protected boundary + page). + + @param fieldIndex The field to be reallocated + @param size The size of buffer to allocate for this field. +*/ + +ValidatedPointer<char>& +BlobCollection::reallocBuffer(int fieldIndex, size_t size) +{ + ProtectedBuffer& buf = buffers[table->getBlobIdFromField(fieldIndex)]; + if (size <= buf.allocLen()) + return buf.ptr(); + + table->updateBlobFieldActualSize(fieldIndex, size); + + DBUG_PRINT("BlobCollection::reallocBuffer",("PERF: reallocing %d to %d: ", fieldIndex, size)); + + bool protect; + buf.Free(); + buf.Malloc(getSizeToAllocate(fieldIndex, protect), protect); + return buf.ptr(); +} diff --git a/storage/ibmdb2i/db2i_blobCollection.h b/storage/ibmdb2i/db2i_blobCollection.h new file mode 100644 index 00000000000..6a60394555f --- /dev/null +++ b/storage/ibmdb2i/db2i_blobCollection.h @@ -0,0 +1,151 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#ifndef DB2I_BLOBCOLLECTION_H +#define DB2I_BLOBCOLLECTION_H + +#include "db2i_global.h" +#include "db2i_file.h" + +/** + @class ProtectedBuffer + @brief Implements memory management for (optionally) protected buffers. + + Buffers created with the protection option will have a guard page set on the + page following requested allocation size. The side effect is that the actual + allocation is up to 2*4096-1 bytes larger than the size requested by the + using code. +*/ + +class ProtectedBuffer +{ +public: + ProtectedBuffer() : protectBuf(false) + {;} + + void Malloc(size_t size, bool protect = false) + { + protectBuf = protect; + bufptr.alloc(size + (protectBuf ? 0x1fff : 0x0)); + if ((void*)bufptr != NULL) + { + len = size; + if (protectBuf) + mprotect(protectedPage(), 0x1000, PROT_NONE); +#ifndef DBUG_OFF + // Prevents a problem with DBUG_PRINT over-reading in recent versions of + // MySQL + *((char*)protectedPage()-1) = 0; +#endif + } + } + + void Free() + { + if ((void*)bufptr != NULL) + { + if (protectBuf) + mprotect(protectedPage(), 0x1000, PROT_READ | PROT_WRITE); + bufptr.dealloc(); + } + } + + ~ProtectedBuffer() + { + Free(); + } + + ValidatedPointer<char>& ptr() {return bufptr;} + bool isProtected() const {return protectBuf;} + size_t allocLen() const {return len;} +private: + void* protectedPage() + { + return (void*)(((address64_t)(void*)bufptr + len + 0x1000) & ~0xfff); + } + + ValidatedPointer<char> bufptr; + size_t len; + bool protectBuf; + +}; + + +/** + @class BlobCollection + @brief Manages memory allocation for reading blobs associated with a table. + + Allocations are done on-demand and are protected with a guard page if less + than the max possible size is allocated. +*/ +class BlobCollection +{ + public: + BlobCollection(db2i_table* db2Table, uint32 defaultAllocSize) : + defaultAllocation(defaultAllocSize), table(db2Table) + { + buffers = new ProtectedBuffer[table->getBlobCount()]; + } + + ~BlobCollection() + { + delete[] buffers; + } + + ValidatedPointer<char>& getBufferPtr(int fieldIndex) + { + int blobIndex = table->getBlobIdFromField(fieldIndex); + if ((char*)buffers[blobIndex].ptr() == NULL) + generateBuffer(fieldIndex); + + return buffers[blobIndex].ptr(); + } + + ValidatedPointer<char>& reallocBuffer(int fieldIndex, size_t size); + + + private: + + uint32 getSizeToAllocate(int fieldIndex, bool& shouldProtect); + void generateBuffer(int fieldIndex); + + db2i_table* table; // The table being read + ProtectedBuffer* buffers; // The buffers + uint32 defaultAllocation; + /* The default size to use when first allocating a buffer */ +}; + +#endif diff --git a/storage/ibmdb2i/db2i_charsetSupport.cc b/storage/ibmdb2i/db2i_charsetSupport.cc new file mode 100644 index 00000000000..2609d42887e --- /dev/null +++ b/storage/ibmdb2i/db2i_charsetSupport.cc @@ -0,0 +1,793 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + + +#include "db2i_charsetSupport.h" +#include "as400_types.h" +#include "as400_protos.h" +#include "db2i_ileBridge.h" +#include "qlgusr.h" +#include "db2i_errors.h" + + +/* + The following arrays define a mapping between IANA-style text descriptors and + IBM i CCSID text descriptors. The mapping is a 1-to-1 correlation between + corresponding array slots. +*/ +#define MAX_IANASTRING 23 +static const char ianaStringType[MAX_IANASTRING][10] = +{ + {"ascii"}, + {"Big5"}, //big5 + {"cp1250"}, + {"cp1251"}, + {"cp1256"}, + {"cp850"}, + {"cp852"}, + {"cp866"}, + {"IBM943"}, //cp932 + {"EUC-KR"}, //euckr + {"IBM1381"}, //gb2312 + {"IBM1386"}, //gbk + {"greek"}, + {"hebrew"}, + {"latin1"}, + {"latin2"}, + {"latin5"}, + {"macce"}, + {"tis620"}, + {"Shift_JIS"}, //sjis + {"ucs2"}, + {"EUC-JP"}, //ujis + {"utf8"} +}; +static const char ccsidType[MAX_IANASTRING][6] = +{ + {"367"}, //ascii + {"950"}, //big5 + {"1250"}, //cp1250 + {"1251"}, //cp1251 + {"1256"}, //cp1256 + {"850"}, //cp850 + {"852"}, //cp852 + {"866"}, //cp866 + {"943"}, //cp932 + {"970"}, //euckr + {"1381"}, //gb2312 + {"1386"}, //gbk + {"813"}, //greek + {"916"}, //hebrew + {"923"}, //latin1 + {"912"}, //latin2 + {"920"}, //latin5 + {"1282"}, //macce + {"874"}, //tis620 + {"943"}, //sjis + {"13488"},//ucs2 + {"5050"}, //ujis + {"1208"} //utf8 +}; + +static _ILEpointer *QlgCvtTextDescToDesc_sym; + +/* We keep a cache of the mapping for text descriptions obtained via + QlgTextDescToDesc. The following structures implement this cache. */ +static HASH textDescMapHash; +static MEM_ROOT textDescMapMemroot; +static pthread_mutex_t textDescMapHashMutex; +struct TextDescMap +{ + struct HashKey + { + int32 inType; + int32 outType; + char inDesc[Qlg_MaxDescSize]; + } hashKey; + char outDesc[Qlg_MaxDescSize]; +}; + +/* We keep a cache of the mapping for open iconv descriptors. The following + structures implement this cache. */ +static HASH iconvMapHash; +static MEM_ROOT iconvMapMemroot; +static pthread_mutex_t iconvMapHashMutex; +struct IconvMap +{ + struct HashKey + { + uint16 direction; // This is a uint16 instead of a uchar to avoid garbage data in the key from compiler padding + uint16 db2CCSID; + const CHARSET_INFO* myCharset; + } hashKey; + iconv_t iconvDesc; +}; + + +/** + Initialize the static structures used by this module. + + This must only be called once per plugin instantiation. + + @return 0 if successful. Failure otherwise +*/ +int32 initCharsetSupport() +{ + DBUG_ENTER("initCharsetSupport"); + + int actmark = _ILELOAD("QSYS/QLGUSR", ILELOAD_LIBOBJ); + if ( actmark == -1 ) + { + DBUG_PRINT("initCharsetSupport", ("conversion srvpgm activation failed")); + DBUG_RETURN(1); + } + + QlgCvtTextDescToDesc_sym = (ILEpointer*)malloc_aligned(sizeof(ILEpointer)); + if (_ILESYM(QlgCvtTextDescToDesc_sym, actmark, "QlgCvtTextDescToDesc") == -1) + { + DBUG_PRINT("initCharsetSupport", + ("resolve of QlgCvtTextDescToDesc failed")); + DBUG_RETURN(errno); + } + + VOID(pthread_mutex_init(&textDescMapHashMutex,MY_MUTEX_INIT_FAST)); + hash_init(&textDescMapHash, &my_charset_bin, 10, offsetof(TextDescMap, hashKey), sizeof(TextDescMap::hashKey), 0, 0, HASH_UNIQUE); + + VOID(pthread_mutex_init(&iconvMapHashMutex,MY_MUTEX_INIT_FAST)); + hash_init(&iconvMapHash, &my_charset_bin, 10, offsetof(IconvMap, hashKey), sizeof(IconvMap::hashKey), 0, 0, HASH_UNIQUE); + + init_alloc_root(&textDescMapMemroot, 2048, 0); + init_alloc_root(&iconvMapMemroot, 256, 0); + + initMyconv(); + + DBUG_RETURN(0); +} + +/** + Cleanup the static structures used by this module. + + This must only be called once per plugin instantiation and only if + initCharsetSupport() was successful. +*/ +void doneCharsetSupport() +{ + cleanupMyconv(); + + free_root(&textDescMapMemroot, 0); + free_root(&iconvMapMemroot, 0); + + pthread_mutex_destroy(&textDescMapHashMutex); + hash_free(&textDescMapHash); + pthread_mutex_destroy(&iconvMapHashMutex); + hash_free(&iconvMapHash); + free_aligned(QlgCvtTextDescToDesc_sym); +} + + +/** + Convert a text description from one type to another. + + This function is just a wrapper for the IBM i QlgTextDescToDesc function plus + some overrides for conversions that the API does not handle correctly and + support for caching the computed conversion. + + @param inType The type of descriptor pointed to by "in". + @param outType The type of descriptor requested for "out". + @param in The descriptor to be convereted. + @param[out] out The equivalent descriptor + @param hashKey The hash key to be used for caching the conversion result. + + @return 0 if successful. Failure otherwise +*/ +static int32 getNewTextDesc(const int32 inType, + const int32 outType, + const char* in, + char* out, + const TextDescMap::HashKey* hashKey) +{ + DBUG_ENTER("db2i_charsetSupport::getNewTextDesc"); + const arg_type_t signature[] = { ARG_INT32, ARG_INT32, ARG_MEMPTR, ARG_INT32, ARG_MEMPTR, ARG_INT32, ARG_INT32, ARG_END }; + struct ArgList + { + ILEarglist_base base; + int32 CRDIInType; + int32 CRDIOutType; + ILEpointer CRDIDesc; + int32 CRDIDescSize; + ILEpointer CRDODesc; + int32 CRDODescSize; + int32 CTDCCSID; + } *arguments; + + if ((inType == Qlg_TypeIANA) && (outType == Qlg_TypeAix41)) + { + // Override non-standard charsets + if (unlikely(strcmp("IBM1381", in) == 0)) + { + strcpy(out, "IBM-1381"); + DBUG_RETURN(0); + } + } + else if ((inType == Qlg_TypeAS400CCSID) && (outType == Qlg_TypeAix41)) + { + // Override non-standard charsets + if (unlikely(strcmp("1148", in) == 0)) + { + strcpy(out, "IBM-1148"); + DBUG_RETURN(0); + } + } + + char argBuf[sizeof(ArgList)+15]; + arguments = (ArgList*)roundToQuadWordBdy(argBuf); + + arguments->CRDIInType = inType; + arguments->CRDIOutType = outType; + arguments->CRDIDesc.s.addr = (address64_t) in; + arguments->CRDIDescSize = Qlg_MaxDescSize; + arguments->CRDODesc.s.addr = (address64_t) out; + arguments->CRDODescSize = Qlg_MaxDescSize; + arguments->CTDCCSID = 819; + _ILECALL(QlgCvtTextDescToDesc_sym, + &arguments->base, + signature, + RESULT_INT32); + if (unlikely(arguments->base.result.s_int32.r_int32 < 0)) + { + getErrTxt(DB2I_ERR_ILECALL,"QlgCvtTextDescToDesc",arguments->base.result.s_int32.r_int32); + DBUG_RETURN(DB2I_ERR_ILECALL); + } + + // Store the conversion information into a cache entry + TextDescMap* mapping = (TextDescMap*)alloc_root(&textDescMapMemroot, sizeof(TextDescMap)); + if (unlikely(!mapping)) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + memcpy(&(mapping->hashKey), hashKey, sizeof(hashKey)); + strcpy(mapping->outDesc, out); + pthread_mutex_lock(&textDescMapHashMutex); + my_hash_insert(&textDescMapHash, (const uchar*)mapping); + pthread_mutex_unlock(&textDescMapHashMutex); + + DBUG_RETURN(0); +} + + +/** + Convert a text description from one type to another. + + This function takes a text description in one representation and converts + it into another representation. Although the OS provides some facilities for + doing this, the support is not complete, nor does MySQL always use standard + identifiers. Therefore, there are a lot of hardcoded overrides required. + There is probably some room for optimization here, but this should not be + called frequently under most circumstances. + + @param inType The type of descriptor pointed to by "in". + @param outType The type of descriptor requested for "out". + @param in The descriptor to be convereted. + @param[out] out The equivalent descriptor + + @return 0 if successful. Failure otherwise +*/ +static int32 convertTextDesc(const int32 inType, const int32 outType, const char* inDesc, char* outDesc) +{ + DBUG_ENTER("db2i_charsetSupport::convertTextDesc"); + const char* inDescOverride; + + if (inType == Qlg_TypeIANA) + { + // Override non-standard charsets + if (strcmp("big5", inDesc) == 0) + inDescOverride = "Big5"; + else if (strcmp("cp932", inDesc) == 0) + inDescOverride = "IBM943"; + else if (strcmp("euckr", inDesc) == 0) + inDescOverride = "EUC-KR"; + else if (strcmp("gb2312", inDesc) == 0) + inDescOverride = "IBM1381"; + else if (strcmp("gbk", inDesc) == 0) + inDescOverride = "IBM1386"; + else if (strcmp("sjis", inDesc) == 0) + inDescOverride = "Shift_JIS"; + else if (strcmp("ujis", inDesc) == 0) + inDescOverride = "EUC-JP"; + else + inDescOverride = inDesc; + + // Hardcode non-standard charsets + if (outType == Qlg_TypeAix41) + { + if (strcmp("Big5", inDescOverride) == 0) + { + strcpy(outDesc,"big5"); + DBUG_RETURN(0); + } + else if (strcmp("IBM1386", inDescOverride) == 0) + { + strcpy(outDesc,"GBK"); + DBUG_RETURN(0); + } + else if (strcmp("Shift_JIS", inDescOverride) == 0 || + strcmp("IBM943", inDescOverride) == 0) + { + strcpy(outDesc,"IBM-943"); + DBUG_RETURN(0); + } + else if (strcmp("tis620", inDescOverride) == 0) + { + strcpy(outDesc,"TIS-620"); + DBUG_RETURN(0); + } + else if (strcmp("ucs2", inDescOverride) == 0) + { + strcpy(outDesc,"UCS-2"); + DBUG_RETURN(0); + } + else if (strcmp("cp1250", inDescOverride) == 0) + { + strcpy(outDesc,"IBM-1250"); + DBUG_RETURN(0); + } + else if (strcmp("cp1251", inDescOverride) == 0) + { + strcpy(outDesc,"IBM-1251"); + DBUG_RETURN(0); + } + else if (strcmp("cp1256", inDescOverride) == 0) + { + strcpy(outDesc,"IBM-1256"); + DBUG_RETURN(0); + } + } + else if (outType == Qlg_TypeAS400CCSID) + { + // See if we can fast path the convert + for (int loopCnt = 0; loopCnt < MAX_IANASTRING; ++loopCnt) + { + if (strcmp((char*)ianaStringType[loopCnt],inDescOverride) == 0) + { + strcpy(outDesc,ccsidType[loopCnt]); + DBUG_RETURN(0); + } + } + } + } + else + inDescOverride = inDesc; + + // We call getNewTextDesc for all other conversions and cache the result. + TextDescMap *mapping; + TextDescMap::HashKey hashKey; + hashKey.inType= inType; + hashKey.outType= outType; + uint32 len = strlen(inDescOverride); + memcpy(hashKey.inDesc, inDescOverride, len); + memset(hashKey.inDesc+len, 0, sizeof(hashKey.inDesc) - len); + + if (!(mapping=(TextDescMap *) hash_search(&textDescMapHash, + (const uchar*)&hashKey, + sizeof(hashKey)))) + { + DBUG_RETURN(getNewTextDesc(inType, outType, inDescOverride, outDesc, &hashKey)); + } + else + { + strcpy(outDesc, mapping->outDesc); + } + DBUG_RETURN(0); +} + + +/** + Convert an IANA character set name into a DB2 for i CCSID value. + + @param parmIANADesc An IANA character set name + @param[out] db2Ccsid The equivalent CCSID value + + @return 0 if successful. Failure otherwise +*/ +int32 convertIANAToDb2Ccsid(const char* parmIANADesc, uint16* db2Ccsid) +{ + int32 rc; + uint16 aixCcsid; + char aixCcsidString[Qlg_MaxDescSize]; + int aixEncodingScheme; + int db2EncodingScheme; + rc = convertTextDesc(Qlg_TypeIANA, Qlg_TypeAS400CCSID, parmIANADesc, aixCcsidString); + if (rc != 0) + return rc; + aixCcsid = atoi(aixCcsidString); + rc = getEncodingScheme(aixCcsid, aixEncodingScheme); + if (rc != 0) + return rc; + switch(aixEncodingScheme) { // Select on encoding scheme + case 0x1100: // EDCDIC SBCS + case 0x2100: // ASCII SBCS + case 0x4100: // AIX SBCS + case 0x4105: // MS Windows + case 0x5100: // ISO 7 bit ASCII + db2EncodingScheme = 0x1100; + break; + case 0x1200: // EDCDIC DBCS + case 0x2200: // ASCII DBCS + db2EncodingScheme = 0x1200; + break; + case 0x1301: // EDCDIC Mixed + case 0x2300: // ASCII Mixed + case 0x4403: // EUC (ISO 2022) + db2EncodingScheme = 0x1301; + break; + case 0x7200: // UCS2 + db2EncodingScheme = 0x7200; + break; + case 0x7807: // UTF-8 + db2EncodingScheme = 0x7807; + break; + case 0x7500: // UTF-32 + db2EncodingScheme = 0x7500; + break; + default: // Unknown + { + getErrTxt(DB2I_ERR_UNKNOWN_ENCODING,aixEncodingScheme); + return DB2I_ERR_UNKNOWN_ENCODING; + } + break; + } + if (aixEncodingScheme == db2EncodingScheme) + { + *db2Ccsid = aixCcsid; + } + else + { + rc = getAssociatedCCSID(aixCcsid, db2EncodingScheme, db2Ccsid); // EDCDIC SBCS + if (rc != 0) + return rc; + } + + return 0; +} + + +/** + Obtain the encoding scheme of a CCSID. + + @param inCcsid An IBM i CCSID + @param[out] outEncodingScheme The associated encoding scheme + + @return 0 if successful. Failure otherwise +*/ +int32 getEncodingScheme(const uint16 inCcsid, int32& outEncodingScheme) +{ + DBUG_ENTER("db2i_charsetSupport::getEncodingScheme"); + + static bool ptrInited = FALSE; + static char ptrSpace[sizeof(ILEpointer) + 15]; + static ILEpointer* ptrToPtr = (ILEpointer*)roundToQuadWordBdy(ptrSpace); + int rc; + + if (!ptrInited) + { + rc = _RSLOBJ2(ptrToPtr, RSLOBJ_TS_PGM, "QTQGESP", "QSYS"); + + if (rc) + { + getErrTxt(DB2I_ERR_RESOLVE_OBJ,"QTQGESP","QSYS","*PGM",errno); + DBUG_RETURN(DB2I_ERR_RESOLVE_OBJ); + } + ptrInited = TRUE; + } + + DBUG_ASSERT(inCcsid != 0); + + int GESPCCSID = inCcsid; + int GESPLen = 32; + int GESPNbrVal = 0; + int32 GESPES; + int GESPCSCPL[32]; + int GESPFB[3]; + void* ILEArgv[7]; + ILEArgv[0] = &GESPCCSID; + ILEArgv[1] = &GESPLen; + ILEArgv[2] = &GESPNbrVal; + ILEArgv[3] = &GESPES; + ILEArgv[4] = &GESPCSCPL; + ILEArgv[5] = &GESPFB; + ILEArgv[6] = NULL; + + rc = _PGMCALL(ptrToPtr, (void**)&ILEArgv, 0); + + if (rc) + { + getErrTxt(DB2I_ERR_PGMCALL,"QTQGESP","QSYS",rc); + DBUG_RETURN(DB2I_ERR_PGMCALL); + } + if (GESPFB[0] != 0 || + GESPFB[1] != 0 || + GESPFB[2] != 0) + { + getErrTxt(DB2I_ERR_QTQGESP,GESPFB[0],GESPFB[1],GESPFB[2]); + DBUG_RETURN(DB2I_ERR_QTQGESP); + } + outEncodingScheme = GESPES; + + DBUG_RETURN(0); +} + + +/** + Get the best fit equivalent CCSID. (Wrapper for QTQGRDC API) + + @param inCcsid An IBM i CCSID + @param inEncodingScheme The encoding scheme + @param[out] outCcsid The equivalent CCSID + + @return 0 if successful. Failure otherwise +*/ +int32 getAssociatedCCSID(const uint16 inCcsid, const int inEncodingScheme, uint16* outCcsid) +{ + DBUG_ENTER("db2i_charsetSupport::getAssociatedCCSID"); + static bool ptrInited = FALSE; + static char ptrSpace[sizeof(ILEpointer) + 15]; + static ILEpointer* ptrToPtr = (ILEpointer*)roundToQuadWordBdy(ptrSpace); + int rc; + + // Override non-standard charsets + if ((inCcsid == 923) && (inEncodingScheme == 0x1100)) + { + *outCcsid = 1148; + DBUG_RETURN(0); + } + + if (!ptrInited) + { + rc = _RSLOBJ2(ptrToPtr, RSLOBJ_TS_PGM, "QTQGRDC", "QSYS"); + + if (rc) + { + getErrTxt(DB2I_ERR_RESOLVE_OBJ,"QTQGRDC","QSYS","*PGM",errno); + DBUG_RETURN(DB2I_ERR_RESOLVE_OBJ); + } + ptrInited = TRUE; + } + + int GRDCCCSID = inCcsid; + int GRDCES = inEncodingScheme; + int GRDCSel = 0; + int GRDCAssCCSID; + int GRDCFB[3]; + void* ILEArgv[7]; + ILEArgv[0] = &GRDCCCSID; + ILEArgv[1] = &GRDCES; + ILEArgv[2] = &GRDCSel; + ILEArgv[3] = &GRDCAssCCSID; + ILEArgv[4] = &GRDCFB; + ILEArgv[5] = NULL; + + rc = _PGMCALL(ptrToPtr, (void**)&ILEArgv, 0); + + if (rc) + { + getErrTxt(DB2I_ERR_PGMCALL,"QTQGRDC","QSYS",rc); + DBUG_RETURN(DB2I_ERR_PGMCALL); + } + if (GRDCFB[0] != 0 || + GRDCFB[1] != 0 || + GRDCFB[2] != 0) + { + getErrTxt(DB2I_ERR_QTQGRDC,GRDCFB[0],GRDCFB[1],GRDCFB[2]); + DBUG_RETURN(DB2I_ERR_QTQGRDC); + } + + *outCcsid = GRDCAssCCSID; + + DBUG_RETURN(0); +} + +/** + Open an iconv conversion between a MySQL charset and the respective IBM i CCSID + + @param direction The direction of the conversion + @param mysqlCSName Name of the MySQL character set + @param db2CCSID The IBM i CCSID + @param hashKey The key to use for inserting the opened conversion into the cache + @param[out] newConversion The iconv descriptor + + @return 0 if successful. Failure otherwise +*/ +static int32 openNewConversion(enum_conversionDirection direction, + const char* mysqlCSName, + uint16 db2CCSID, + IconvMap::HashKey* hashKey, + iconv_t& newConversion) +{ + DBUG_ENTER("db2i_charsetSupport::openNewConversion"); + + char mysqlAix41Desc[Qlg_MaxDescSize]; + char db2Aix41Desc[Qlg_MaxDescSize]; + char db2CcsidString[6] = ""; + int32 rc; + + /* + First we have to convert the MySQL IANA-like name and the DB2 CCSID into + there equivalent iconv descriptions. + */ + rc = convertTextDesc(Qlg_TypeIANA, Qlg_TypeAix41, mysqlCSName, mysqlAix41Desc); + if (rc) + DBUG_RETURN(rc); + CHARSET_INFO *cs= &my_charset_bin; + (uint)(cs->cset->long10_to_str)(cs,db2CcsidString,sizeof(db2CcsidString), 10, db2CCSID); + rc = convertTextDesc(Qlg_TypeAS400CCSID, Qlg_TypeAix41, db2CcsidString, db2Aix41Desc); + if (rc) + DBUG_RETURN(rc); + + /* Call iconv to open the conversion. */ + if (direction == toDB2) + { + newConversion = iconv_open(db2Aix41Desc, mysqlAix41Desc); + if (newConversion == (iconv_t) -1) + { + getErrTxt(DB2I_ERR_ICONV_OPEN, mysqlAix41Desc, db2Aix41Desc, errno); + DBUG_RETURN(DB2I_ERR_ICONV_OPEN); + } + } + else + { + newConversion = iconv_open(mysqlAix41Desc, db2Aix41Desc); + if (newConversion == (iconv_t) -1) + { + getErrTxt(DB2I_ERR_ICONV_OPEN, db2Aix41Desc, mysqlAix41Desc, errno); + DBUG_RETURN(DB2I_ERR_ICONV_OPEN); + } + } + + /* Insert the new conversion into the cache. */ + IconvMap* mapping = (IconvMap*)alloc_root(&iconvMapMemroot, sizeof(IconvMap)); + if (!mapping) + { + my_error(ER_OUTOFMEMORY, MYF(0), sizeof(IconvMap)); + DBUG_RETURN( HA_ERR_OUT_OF_MEM); + } + memcpy(&(mapping->hashKey), hashKey, sizeof(mapping->hashKey)); + mapping->iconvDesc = newConversion; + pthread_mutex_lock(&iconvMapHashMutex); + my_hash_insert(&iconvMapHash, (const uchar*)mapping); + pthread_mutex_unlock(&iconvMapHashMutex); + + DBUG_RETURN(0); +} + + +/** + Open an iconv conversion between a MySQL charset and the respective IBM i CCSID + + @param direction The direction of the conversion + @param cs The MySQL character set + @param db2CCSID The IBM i CCSID + @param[out] newConversion The iconv descriptor + + @return 0 if successful. Failure otherwise +*/ +int32 getConversion(enum_conversionDirection direction, const CHARSET_INFO* cs, uint16 db2CCSID, iconv_t& conversion) +{ + DBUG_ENTER("db2i_charsetSupport::getConversion"); + + int32 rc; + + /* Build the hash key */ + IconvMap::HashKey hashKey; + hashKey.direction= direction; + hashKey.myCharset= cs; + hashKey.db2CCSID= db2CCSID; + + /* Look for the conversion in the cache and add it if it is not there. */ + IconvMap *mapping; + if (!(mapping= (IconvMap *) hash_search(&iconvMapHash, + (const uchar*)&hashKey, + sizeof(hashKey)))) + { + DBUG_PRINT("getConversion", ("Hash miss for direction=%d, cs=%s, ccsid=%d", direction, cs->name, db2CCSID)); + rc= openNewConversion(direction, cs->csname, db2CCSID, &hashKey, conversion); + if (rc) + DBUG_RETURN(rc); + } + else + { + conversion= mapping->iconvDesc; + } + + DBUG_RETURN(0); +} + +/** + Fast-path conversion from ASCII to EBCDIC for use in converting + identifiers to be sent to the QMY APIs. + + @param input ASCII data + @param[out] ouput EBCDIC data + @param ilen Size of input buffer and output buffer +*/ +int convToEbcdic(const char* input, char* output, size_t ilen) +{ + static bool inited = FALSE; + static iconv_t ic; + + if (ilen == 0) + return 0; + + if (!inited) + { + ic = iconv_open( "IBM-037", "ISO8859-1" ); + inited = TRUE; + } + size_t substitutedChars; + size_t olen = ilen; + if (iconv( ic, (char**)&input, &ilen, &output, &olen, &substitutedChars ) == -1) + return errno; + + return 0; +} + + +/** + Fast-path conversion from EBCDIC to ASCII for use in converting + data received from the QMY APIs. + + @param input EBCDIC data + @param[out] ouput ASCII data + @param ilen Size of input buffer and output buffer +*/ +int convFromEbcdic(const char* input, char* output, size_t ilen) +{ + static bool inited = FALSE; + static iconv_t ic; + + if (ilen == 0) + return 0; + + if (!inited) + { + ic = iconv_open("ISO8859-1", "IBM-037"); + inited = TRUE; + } + + size_t substitutedChars; + size_t olen = ilen; + if (iconv( ic, (char**)&input, &ilen, &output, &olen, &substitutedChars) == -1) + return errno; + + return 0; +} diff --git a/storage/ibmdb2i/db2i_charsetSupport.h b/storage/ibmdb2i/db2i_charsetSupport.h new file mode 100644 index 00000000000..77051e1e0db --- /dev/null +++ b/storage/ibmdb2i/db2i_charsetSupport.h @@ -0,0 +1,65 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#ifndef DB2I_CHARSETSUPPORT_H +#define DB2I_CHARSETSUPPORT_H + +#include "db2i_global.h" +#include "mysql_priv.h" +#include <mysql/plugin.h> +#include "db2i_iconv.h" + +/** + @enum enum_conversionDirection + + Conversion directions for getConversion() +*/ +enum enum_conversionDirection +{ + toMySQL, + toDB2 +}; + +int initCharsetSupport(); +void doneCharsetSupport(); +int32 convertIANAToDb2Ccsid(const char* parmIANADesc, uint16* db2Ccsid); +int32 getEncodingScheme(const uint16 inCcsid, int32& outEncodingScheme); +int32 getAssociatedCCSID(const uint16 inCcsid, const int inEncodingScheme, uint16* outCcsid); +int convToEbcdic(const char* input, char* output, size_t ilen); +int convFromEbcdic(const char* input, char* output, size_t ilen); +int32 getConversion(enum_conversionDirection direction, const CHARSET_INFO* cs, uint16 db2CCSID, iconv_t& conversion); + +#endif diff --git a/storage/ibmdb2i/db2i_collationSupport.cc b/storage/ibmdb2i/db2i_collationSupport.cc new file mode 100644 index 00000000000..26ad0c9dee6 --- /dev/null +++ b/storage/ibmdb2i/db2i_collationSupport.cc @@ -0,0 +1,359 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#include "db2i_collationSupport.h" +#include "db2i_errors.h" + + +/* + The following arrays define a mapping between MySQL collation names and + corresponding IBM i sort sequences. The mapping is a 1-to-1 correlation + between corresponding array slots but is incomplete without case-sensitivity + markers dynamically added to the mySqlSortSequence names. +*/ +#define MAX_COLLATION 89 +static const char* mySQLCollation[MAX_COLLATION] = +{ + {"ascii_general"}, + {"ascii"}, + {"big5_chinese"}, + {"big5"}, + {"cp1250_croatian"}, + {"cp1250_czech"}, + {"cp1250_general"}, + {"cp1250_polish"}, + {"cp1250"}, + {"cp1251_bulgarian"}, + {"cp1251_general"}, + {"cp1251"}, + {"cp1256_general"}, + {"cp1256"}, + {"cp850_general"}, + {"cp850"}, + {"cp852_general"}, + {"cp852"}, + {"cp932_japanese"}, + {"cp932"}, + {"euckr_korean"}, + {"euckr"}, + {"gb2312_chinese"}, + {"gb2312"}, + {"gbk_chinese"}, + {"gbk"}, + {"greek_general"}, + {"greek"}, + {"hebrew_general"}, + {"hebrew"}, + {"latin1_danish"}, + {"latin1_general"}, + {"latin1_german1"}, + {"latin1_spanish"}, + {"latin1_swedish"}, + {"latin1"}, + {"latin2_croatian"}, + {"latin2_czech"}, + {"latin2_general"}, + {"latin2_hungarian"}, + {"latin2"}, + {"latin5_turkish"}, + {"latin5"}, + {"macce_general"}, + {"macce"}, + {"sjis_japanese"}, + {"sjis"}, + {"tis620_thai"}, + {"tis620"}, + {"ucs2_czech"}, + {"ucs2_danish"}, + {"ucs2_esperanto"}, + {"ucs2_estonian"}, + {"ucs2_general"}, + {"ucs2_hungarian"}, + {"ucs2_icelandic"}, + {"ucs2_latvian"}, + {"ucs2_lithuanian"}, + {"ucs2_persian"}, + {"ucs2_polish"}, + {"ucs2_romanian"}, + {"ucs2_slovak"}, + {"ucs2_slovenian"}, + {"ucs2_spanish"}, + {"ucs2_spanish2"}, + {"ucs2_turkish"}, + {"ucs2_unicode"}, + {"ucs2"}, + {"ujis_japanese"}, + {"ujis"}, + {"utf8_czech"}, + {"utf8_danish"}, + {"utf8_esperanto"}, + {"utf8_estonian"}, + {"utf8_general"}, + {"utf8_hungarian"}, + {"utf8_icelandic"}, + {"utf8_latvian"}, + {"utf8_lithuanian"}, + {"utf8_persian"}, + {"utf8_polish"}, + {"utf8_romanian"}, + {"utf8_slovak"}, + {"utf8_slovenian"}, + {"utf8_spanish"}, + {"utf8_spanish2"}, + {"utf8_turkish"}, + {"utf8_unicode"}, + {"utf8"} +}; + + +static const char* mySqlSortSequence[MAX_COLLATION] = +{ + {"QALA101F4"}, + {"QBLA101F4"}, + {"QACHT04B0"}, + {"QBCHT04B0"}, + {"QALA20481"}, + {"QBLA20481"}, + {"QCLA20481"}, + {"QDLA20481"}, + {"QELA20481"}, + {"QACYR0401"}, + {"QBCYR0401"}, + {"QCCYR0401"}, + {"QAARA01A4"}, + {"QBARA01A4"}, + {"QCLA101F4"}, + {"QDLA101F4"}, + {"QALA20366"}, + {"QBLA20366"}, + {"QAJPN04B0"}, + {"QBJPN04B0"}, + {"QAKOR04B0"}, + {"QBKOR04B0"}, + {"QACHS04B0"}, + {"QBCHS04B0"}, + {"QCCHS04B0"}, + {"QDCHS04B0"}, + {"QAELL036B"}, + {"QBELL036B"}, + {"QAHEB01A8"}, + {"QBHEB01A8"}, + {"QALA1047C"}, + {"QBLA1047C"}, + {"QCLA1047C"}, + {"QDLA1047C"}, + {"QELA1047C"}, + {"QFLA1047C"}, + {"QCLA20366"}, + {"QDLA20366"}, + {"QELA20366"}, + {"QFLA20366"}, + {"QGLA20366"}, + {"QATRK0402"}, + {"QBTRK0402"}, + {"QHLA20366"}, + {"QILA20366"}, + {"QCJPN04B0"}, + {"QDJPN04B0"}, + {"QATHA0346"}, + {"QBTHA0346"}, + {"ACS"}, + {"ADA"}, + {"AEO"}, + {"AET"}, + {"QAUCS04B0"}, + {"AHU"}, + {"AIS"}, + {"ALV"}, + {"ALT"}, + {"AFA"}, + {"APL"}, + {"ARO"}, + {"ASK"}, + {"ASL"}, + {"AES"}, + {"AES__TRADIT"}, + {"ATR"}, + {"AEN"}, + {"*HEX"}, + {"QEJPN04B0"}, + {"QFJPN04B0"}, + {"ACS"}, + {"ADA"}, + {"AEO"}, + {"AET"}, + {"QAUCS04B0"}, + {"AHU"}, + {"AIS"}, + {"ALV"}, + {"ALT"}, + {"AFA"}, + {"APL"}, + {"ARO"}, + {"ASK"}, + {"ASL"}, + {"AES"}, + {"AES__TRADIT"}, + {"ATR"}, + {"AEN"}, + {"*HEX"} +}; + + +/** + Get the IBM i sort sequence that corresponds to the given MySQL collation. + + @param fieldCharSet The collated character set + @param[out] rtnSortSequence The corresponding sort sequence + + @return 0 if successful. Failure otherwise +*/ +static int32 getAssociatedSortSequence(const CHARSET_INFO *fieldCharSet, const char** rtnSortSequence) +{ + DBUG_ENTER("ha_ibmdb2i::getAssociatedSortSequence"); + + if (strcmp(fieldCharSet->csname,"binary") != 0) + { + int collationSearchLen = strlen(fieldCharSet->name); + if (fieldCharSet->state & MY_CS_BINSORT) + collationSearchLen -= 4; + else + collationSearchLen -= 3; + + uint16 loopCnt = 0; + for (loopCnt; loopCnt < MAX_COLLATION; ++loopCnt) + { + if ((strlen(mySQLCollation[loopCnt]) == collationSearchLen) && + (strncmp((char*)mySQLCollation[loopCnt], fieldCharSet->name, collationSearchLen) == 0)) + break; + } + if (loopCnt == MAX_COLLATION) // Did not find associated sort sequence + { + getErrTxt(DB2I_ERR_SRTSEQ); + DBUG_RETURN(DB2I_ERR_SRTSEQ); + } + *rtnSortSequence = mySqlSortSequence[loopCnt]; + } + + DBUG_RETURN(0); +} + + +/** + Update sort sequence information for a key. + + This function accumulates information about a key as it is called for each + field composing the key. The caller should invoke the function for each field + and (with the exception of the charset parm) preserve the values for the + parms across invocations, until a particular key has been evaluated. Once + the last field in the key has been evaluated, the fileSortSequence and + fileSortSequenceLibrary parms will contain the correct information for + creating the corresponding DB2 key. + + @param charset The character set under consideration + @param[in, out] fileSortSequenceType The type of the current key's sort seq + @param[in, out] fileSortSequence The IBM i identifier for the DB2 sort sequence + that corresponds + + @return 0 if successful. Failure otherwise +*/ +int32 updateAssociatedSortSequence(const CHARSET_INFO* charset, + char* fileSortSequenceType, + char* fileSortSequence, + char* fileSortSequenceLibrary) +{ + DBUG_ENTER("ha_ibmdb2i::updateAssociatedSortSequence"); + DBUG_ASSERT(charset); + if (strcmp(charset->csname,"binary") != 0) + { + char newSortSequence[11] = ""; + char newSortSequenceType = ' '; + const char* foundSortSequence; + int rc = getAssociatedSortSequence(charset, &foundSortSequence); + if (rc) DBUG_RETURN (rc); + switch(foundSortSequence[0]) + { + case '*': // Binary + strcat(newSortSequence,foundSortSequence); + newSortSequenceType = 'B'; + break; + case 'Q': // Non-ICU sort sequence + strcat(newSortSequence,foundSortSequence); + if ((charset->state & MY_CS_BINSORT) != 0) + { + strcat(newSortSequence,"U"); + } + else if ((charset->state & MY_CS_CSSORT) != 0) + { + strcat(newSortSequence,"U"); + } + else + { + strcat(newSortSequence,"S"); + } + newSortSequenceType = 'N'; + break; + default: // ICU sort sequence + { + if ((charset->state & MY_CS_CSSORT) == 0) + { + if (osVersion.v >= 6) + strcat(newSortSequence,"I34"); // ICU 3.4 + else + strcat(newSortSequence,"I26"); // ICU 2.6.1 + } + strcat(newSortSequence,foundSortSequence); + newSortSequenceType = 'I'; + } + break; + } + if (*fileSortSequenceType == ' ') // If no sort sequence has been set yet + { + // Set associated sort sequence + strcpy(fileSortSequence,newSortSequence); + strcpy(fileSortSequenceLibrary,"QSYS"); + *fileSortSequenceType = newSortSequenceType; + } + else if (strcmp(fileSortSequence,newSortSequence) != 0) + { + // Only one sort sequence/collation is supported for each DB2 index. + getErrTxt(DB2I_ERR_MIXED_COLLATIONS); + DBUG_RETURN(DB2I_ERR_MIXED_COLLATIONS); + } + } + + DBUG_RETURN(0); +} diff --git a/storage/ibmdb2i/db2i_collationSupport.h b/storage/ibmdb2i/db2i_collationSupport.h new file mode 100644 index 00000000000..b2ce09de1ea --- /dev/null +++ b/storage/ibmdb2i/db2i_collationSupport.h @@ -0,0 +1,48 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#ifndef DB2I_COLLATIONSUPPORT_H +#define DB2I_COLLATIONSUPPORT_H + +#include "db2i_global.h" +#include "mysql_priv.h" + +int32 updateAssociatedSortSequence(const CHARSET_INFO* charset, + char* fileSortSequenceType, + char* fileSortSequence, + char* fileSortSequenceLibrary); + +#endif diff --git a/storage/ibmdb2i/db2i_constraints.cc b/storage/ibmdb2i/db2i_constraints.cc new file mode 100644 index 00000000000..9a96eda1173 --- /dev/null +++ b/storage/ibmdb2i/db2i_constraints.cc @@ -0,0 +1,698 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + + +#include "ha_ibmdb2i.h" +#include "db2i_safeString.h" + +// This function is called when building the CREATE TABLE information for +// foreign key constraints. It converts a constraint, table, schema, or +// field name from EBCDIC to ASCII. If the DB2 name is quoted, it removes +// those quotes. It then adds the appropriate quotes for a MySQL identifier. + +static void convNameForCreateInfo(THD *thd, SafeString& info, char* fromName, int len) +{ + int quote; + char cquote; // Quote character + char convName[MAX_DB2_FILENAME_LENGTH]; // Converted name + + memset(convName, 0, sizeof(convName)); + convFromEbcdic(fromName, convName, len); + quote = get_quote_char_for_identifier(thd, convName, len); + cquote = (char) quote; + if (quote != EOF) + info.strcat(cquote); + if (convName[0] == '"') // If DB2 name was quoted, remove quotes + { + if (strstr(convName, "\"\"")) + stripExtraQuotes(convName+1, len-1); + info.strncat((char*)(convName+1), len-2); + } + else // DB2 name was not quoted + info.strncat(convName, len); + if (quote != EOF) + info.strcat(cquote); +} + +/** + Evaluate the parse tree to build foreign key constraint clauses + + @parm lex The parse tree + @parm appendHere The DB2 string to receive the constraint clauses + @parm path The path to the table under consideration + @parm fields Pointer to the table's list of field pointers + @parm[in, out] fileSortSequenceType The sort sequence type associated with the table + @parm[in, out] fileSortSequence The sort sequence associated with the table + @parm[in, out] fileSortSequenceLibrary The sort sequence library associated with the table + + @return 0 if successful; HA_ERR_CANNOT_ADD_FOREIGN otherwise +*/ +int ha_ibmdb2i::buildDB2ConstraintString(LEX* lex, + String& appendHere, + const char* path, + Field** fields, + char* fileSortSequenceType, + char* fileSortSequence, + char* fileSortSequenceLibrary) +{ + List_iterator<Key> keyIter(lex->alter_info.key_list); + char colName[MAX_DB2_COLNAME_LENGTH+1]; + + Key* curKey; + + while (curKey = keyIter++) + { + if (curKey->type == Key::FOREIGN_KEY) + { + appendHere.append(STRING_WITH_LEN(", ")); + + Foreign_key* fk = (Foreign_key*)curKey; + + char db2LibName[MAX_DB2_SCHEMANAME_LENGTH+1]; + if (fk->name) + { + char db2FKName[MAX_DB2_FILENAME_LENGTH+1]; + appendHere.append(STRING_WITH_LEN("CONSTRAINT ")); + if (fk->ref_table->db.str) + { + convertMySQLNameToDB2Name(fk->ref_table->db.str, db2LibName, sizeof(db2LibName)); + } + else + { + db2i_table::getDB2LibNameFromPath(path, db2LibName); + } + if (lower_case_table_names == 1) + my_casedn_str(files_charset_info, db2LibName); + appendHere.append(db2LibName); + + appendHere.append('.'); + + convertMySQLNameToDB2Name(fk->name, db2FKName, sizeof(db2FKName)); + appendHere.append(db2FKName); + } + + appendHere.append(STRING_WITH_LEN(" FOREIGN KEY (")); + + bool firstTime = true; + + List_iterator<Key_part_spec> column(fk->columns); + Key_part_spec* curColumn; + + while (curColumn = column++) + { + if (!firstTime) + { + appendHere.append(','); + } + firstTime = false; + + convertMySQLNameToDB2Name(curColumn->field_name, colName, sizeof(colName)); + appendHere.append(colName); + + // DB2 requires that the sort sequence on the child table match the parent table's + // sort sequence. We ensure that happens by updating the sort sequence according + // to the constrained fields. + Field** field = fields; + do + { + if (strcmp((*field)->field_name, curColumn->field_name) == 0) + { + int rc = updateAssociatedSortSequence((*field)->charset(), + fileSortSequenceType, + fileSortSequence, + fileSortSequenceLibrary); + + if (unlikely(rc)) return rc; + } + } while (*(++field)); + } + + firstTime = true; + + appendHere.append(STRING_WITH_LEN(") REFERENCES ")); + + if (fk->ref_table->db.str) + { + convertMySQLNameToDB2Name(fk->ref_table->db.str, db2LibName, sizeof(db2LibName)); + } + else + { + db2i_table::getDB2LibNameFromPath(path, db2LibName); + } + if (lower_case_table_names == 1) + my_casedn_str(files_charset_info, db2LibName); + appendHere.append(db2LibName); + appendHere.append('.'); + + char db2FileName[MAX_DB2_FILENAME_LENGTH+1]; + convertMySQLNameToDB2Name(fk->ref_table->table.str, db2FileName, sizeof(db2FileName)); + if (lower_case_table_names) + my_casedn_str(files_charset_info, db2FileName); + appendHere.append(db2FileName); + + + if (!fk->ref_columns.is_empty()) + { + List_iterator<Key_part_spec> ref(fk->ref_columns); + Key_part_spec* curRef; + appendHere.append(STRING_WITH_LEN(" (")); + + + while (curRef = ref++) + { + if (!firstTime) + { + appendHere.append(','); + } + firstTime = false; + + convertMySQLNameToDB2Name(curRef->field_name, colName, sizeof(colName)); + appendHere.append(colName); + } + + appendHere.append(STRING_WITH_LEN(") ")); + } + + if (fk->delete_opt != Foreign_key::FK_OPTION_UNDEF) + { + appendHere.append(STRING_WITH_LEN("ON DELETE ")); + switch (fk->delete_opt) + { + case Foreign_key::FK_OPTION_RESTRICT: + appendHere.append(STRING_WITH_LEN("RESTRICT ")); break; + case Foreign_key::FK_OPTION_CASCADE: + appendHere.append(STRING_WITH_LEN("CASCADE ")); break; + case Foreign_key::FK_OPTION_SET_NULL: + appendHere.append(STRING_WITH_LEN("SET NULL ")); break; + case Foreign_key::FK_OPTION_NO_ACTION: + appendHere.append(STRING_WITH_LEN("NO ACTION ")); break; + case Foreign_key::FK_OPTION_DEFAULT: + appendHere.append(STRING_WITH_LEN("SET DEFAULT ")); break; + default: + return HA_ERR_CANNOT_ADD_FOREIGN; break; + } + } + + if (fk->update_opt != Foreign_key::FK_OPTION_UNDEF) + { + appendHere.append(STRING_WITH_LEN("ON UPDATE ")); + switch (fk->update_opt) + { + case Foreign_key::FK_OPTION_RESTRICT: + appendHere.append(STRING_WITH_LEN("RESTRICT ")); break; + case Foreign_key::FK_OPTION_NO_ACTION: + appendHere.append(STRING_WITH_LEN("NO ACTION ")); break; + default: + return HA_ERR_CANNOT_ADD_FOREIGN; break; + } + } + + } + + } + + return 0; +} + + +/*********************************************************************** +Get the foreign key information in the form of a character string so +that it can be inserted into a CREATE TABLE statement. This is used by +the SHOW CREATE TABLE statement. The string will later be freed by the +free_foreign_key_create_info() method. +************************************************************************/ + +char* ha_ibmdb2i::get_foreign_key_create_info(void) +{ + DBUG_ENTER("ha_ibmdb2i::get_foreign_key_create_info"); + int rc = 0; + char* infoBuffer = NULL; // Pointer to string returned to MySQL + uint32 constraintSpaceLength;// Length of space passed to DB2 + ValidatedPointer<char> constraintSpace; // Space pointer passed to DB2 + uint32 neededLen; // Length returned from DB2 + uint32 cstCnt; // Number of foreign key constraints from DB2 + uint32 fld; // + constraint_hdr* cstHdr; // Pointer to constraint header structure + FK_constraint* FKCstDef; // Pointer to constraint definition structure + cst_name* fieldName; // Pointer to field name structure + char* tempPtr; // Temp pointer for traversing constraint space + char convName[128]; + + /* Allocate space to retrieve the DB2 constraint information. */ + + if (!(share = get_share(table_share->path.str, table))) + DBUG_RETURN(NULL); + + constraintSpaceLength = 5000; // Try allocating 5000 bytes and see if enough. + + initBridge(); + + constraintSpace.alloc(constraintSpaceLength); + rc = bridge()->expectErrors(QMY_ERR_NEED_MORE_SPACE) + ->constraints(db2Table->dataFile()->getMasterDefnHandle(), + constraintSpace, + constraintSpaceLength, + &neededLen, + &cstCnt); + + if (unlikely(rc == QMY_ERR_NEED_MORE_SPACE)) + { + constraintSpaceLength = neededLen; // Get length of space that's needed + constraintSpace.realloc(constraintSpaceLength); + rc = bridge()->expectErrors(QMY_ERR_NEED_MORE_SPACE) + ->constraints(db2Table->dataFile()->getMasterDefnHandle(), + constraintSpace, + constraintSpaceLength, + &neededLen, + &cstCnt); + } + + /* If constraint information was returned by DB2, build a text string */ + /* to return to MySQL. */ + + if ((rc == 0) && (cstCnt > 0)) + { + THD* thd = ha_thd(); + infoBuffer = (char*) my_malloc(MAX_FOREIGN_LEN + 1, MYF(MY_WME)); + if (infoBuffer == NULL) + { + free_share(share); + DBUG_RETURN(NULL); + } + + SafeString info(infoBuffer, MAX_FOREIGN_LEN + 1); + + /* Loop through the DB2 constraints and build a text string for each foreign */ + /* key constraint that is found. */ + + tempPtr = constraintSpace; + cstHdr = (constraint_hdr_t*)(void*)constraintSpace; // Address first constraint definition + for (int i = 0; i < cstCnt && !info.overflowed(); ++i) + { + if (cstHdr->CstType[0] == QMY_CST_FK) // If this is a foreign key constraint + { + tempPtr = (char*)(tempPtr + cstHdr->CstDefOff); + FKCstDef = (FK_constraint_t*)tempPtr; + + /* Process the constraint name. */ + + info.strncat(STRING_WITH_LEN(" CONSTRAINT ")); + convNameForCreateInfo(thd, info, + FKCstDef->CstName.Name, FKCstDef->CstName.Len); + + /* Process the names of the foreign keys. */ + + info.strncat(STRING_WITH_LEN(" FOREIGN KEY (")); + tempPtr = (char*)(tempPtr + FKCstDef->KeyColOff); + fieldName= (cst_name_t*)tempPtr; + for (fld = 0; fld < FKCstDef->KeyCnt; ++fld) + { + convNameForCreateInfo(thd, info, fieldName->Name, fieldName->Len); + if ((fld + 1) < FKCstDef->KeyCnt) + { + info.strncat(STRING_WITH_LEN(", ")); + fieldName = fieldName + 1; + } + } + + /* Process the schema-name and name of the referenced table. */ + + info.strncat(STRING_WITH_LEN(") REFERENCES ")); + convNameForCreateInfo(thd, info, + FKCstDef->RefSchema.Name, FKCstDef->RefSchema.Len); + info.strcat('.'); + convNameForCreateInfo(thd, info, + FKCstDef->RefTable.Name, FKCstDef->RefTable.Len); + info.strncat(STRING_WITH_LEN(" (")); + + /* Process the names of the referenced keys. */ + + tempPtr = (char*)FKCstDef; + tempPtr = (char*)(tempPtr + FKCstDef->RefColOff); + fieldName= (cst_name_t*)tempPtr; + for (fld = 0; fld < FKCstDef->RefCnt; ++fld) + { + convNameForCreateInfo(thd, info, fieldName->Name, fieldName->Len); + if ((fld + 1) < FKCstDef->RefCnt) + { + info.strncat(STRING_WITH_LEN(", ")); + fieldName = fieldName + 1; + } + } + + /* Process the ON UPDATE and ON DELETE rules. */ + + info.strncat(STRING_WITH_LEN(") ON UPDATE ")); + switch(FKCstDef->UpdMethod) + { + case QMY_NOACTION: info.strncat(STRING_WITH_LEN("NO ACTION")); break; + case QMY_RESTRICT: info.strncat(STRING_WITH_LEN("RESTRICT")); break; + default: break; + } + info.strncat(STRING_WITH_LEN(" ON DELETE ")); + switch(FKCstDef->DltMethod) + { + case QMY_CASCADE: info.strncat(STRING_WITH_LEN("CASCADE")); break; + case QMY_SETDFT: info.strncat(STRING_WITH_LEN("SET DEFAULT")); break; + case QMY_SETNULL: info.strncat(STRING_WITH_LEN("SET NULL")); break; + case QMY_NOACTION: info.strncat(STRING_WITH_LEN("NO ACTION")); break; + case QMY_RESTRICT: info.strncat(STRING_WITH_LEN("RESTRICT")); break; + default: break; + } + } + + /* Address the next constraint, if any. */ + + if ((i+1) < cstCnt) + { + info.strcat(','); + tempPtr = (char*)cstHdr + cstHdr->CstLen; + cstHdr = (constraint_hdr_t*)(tempPtr); + } + } + } + + /* Cleanup and return */ + free_share(share); + + DBUG_RETURN(infoBuffer); +} + +/*********************************************************************** +Free the foreign key create info (for a table) that was acquired by the +get_foreign_key_create_info() method. +***********************************************************************/ + +void ha_ibmdb2i::free_foreign_key_create_info(char* info) +{ + DBUG_ENTER("ha_ibmdb2i::free_foreign_key_create_info"); + + if (info) + { + my_free(info, MYF(0)); + } + DBUG_VOID_RETURN; +} + +/*********************************************************************** +This method returns to MySQL a list, with one entry in the list describing +each foreign key constraint. +***********************************************************************/ + +int ha_ibmdb2i::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list) +{ + DBUG_ENTER("ha_ibmdb2i::get_foreign_key_list"); + int rc = 0; + uint32 constraintSpaceLength; // Length of space passed to DB2 + ValidatedPointer<char> constraintSpace; // Space pointer passed to DB2 + uint16 rtnCode; // Return code from DB2 + uint32 neededLen; // Bytes needed to contain DB2 constraint info + uint32 cstCnt; // Number of constraints returned by DB2 + uint32 fld; + constraint_hdr* cstHdr; // Pointer to a cst header structure + FK_constraint* FKCstDef; // Pointer to definition of foreign key constraint + cst_name* fieldName; // Pointer to field name structure + const char *method; + ulong methodLen; + char* tempPtr; // Temp pointer for traversing constraint space + char convName[128]; + + if (!(share = get_share(table_share->path.str, table))) + DBUG_RETURN(0); + + // Allocate space to retrieve the DB2 constraint information. + constraintSpaceLength = 5000; // Try allocating 5000 bytes and see if enough. + + constraintSpace.alloc(constraintSpaceLength); + rc = bridge()->expectErrors(QMY_ERR_NEED_MORE_SPACE) + ->constraints(db2Table->dataFile()->getMasterDefnHandle(), + constraintSpace, + constraintSpaceLength, + &neededLen, + &cstCnt); + + if (unlikely(rc == QMY_ERR_NEED_MORE_SPACE)) + { + constraintSpaceLength = neededLen; // Get length of space that's needed + constraintSpace.realloc(constraintSpaceLength); + rc = bridge()->expectErrors(QMY_ERR_NEED_MORE_SPACE) + ->constraints(db2Table->dataFile()->getMasterDefnHandle(), + constraintSpace, + constraintSpaceLength, + &neededLen, + &cstCnt); + } + + /* If constraint information was returned by DB2, build a text string */ + /* to return to MySQL. */ + if ((rc == 0) && (cstCnt > 0)) + { + tempPtr = constraintSpace; + cstHdr = (constraint_hdr_t*)(void*)constraintSpace; // Address first constraint definition + for (int i = 0; i < cstCnt; ++i) + { + if (cstHdr->CstType[0] == QMY_CST_FK) // If this is a foreign key constraint + { + FOREIGN_KEY_INFO f_key_info; + LEX_STRING *name= 0; + tempPtr = (char*)(tempPtr + cstHdr->CstDefOff); + FKCstDef = (FK_constraint_t*)tempPtr; + + /* Process the constraint name. */ + + convFromEbcdic(FKCstDef->CstName.Name, convName,FKCstDef->CstName.Len); + if (convName[0] == '"') // If quoted, exclude quotes. + f_key_info.forein_id = thd_make_lex_string(thd, 0, + convName + 1, (uint) (FKCstDef->CstName.Len - 2), 1); + else // Not quoted + f_key_info.forein_id = thd_make_lex_string(thd, 0, + convName, (uint) FKCstDef->CstName.Len, 1); + + /* Process the names of the foreign keys. */ + + + tempPtr = (char*)(tempPtr + FKCstDef->KeyColOff); + fieldName = (cst_name_t*)tempPtr; + for (fld = 0; fld < FKCstDef->KeyCnt; ++fld) + { + convFromEbcdic(fieldName->Name, convName, fieldName->Len); + if (convName[0] == '"') // If quoted, exclude quotes. + name = thd_make_lex_string(thd, name, + convName + 1, (uint) (fieldName->Len - 2), 1); + else + name = thd_make_lex_string(thd, name, convName, (uint) fieldName->Len, 1); + f_key_info.foreign_fields.push_back(name); + if ((fld + 1) < FKCstDef->KeyCnt) + fieldName = fieldName + 1; + } + + /* Process the schema and name of the referenced table. */ + + convFromEbcdic(FKCstDef->RefSchema.Name, convName, FKCstDef->RefSchema.Len); + if (convName[0] == '"') // If quoted, exclude quotes. + f_key_info.referenced_db = thd_make_lex_string(thd, 0, + convName + 1, (uint) (FKCstDef->RefSchema.Len -2), 1); + else + f_key_info.referenced_db = thd_make_lex_string(thd, 0, + convName, (uint) FKCstDef->RefSchema.Len, 1); + convFromEbcdic(FKCstDef->RefTable.Name, convName, FKCstDef->RefTable.Len); + if (convName[0] == '"') // If quoted, exclude quotes. + f_key_info.referenced_table = thd_make_lex_string(thd, 0, + convName +1, (uint) (FKCstDef->RefTable.Len -2), 1); + else + f_key_info.referenced_table = thd_make_lex_string(thd, 0, + convName, (uint) FKCstDef->RefTable.Len, 1); + + /* Process the names of the referenced keys. */ + + tempPtr = (char*)FKCstDef; + tempPtr = (char*)(tempPtr + FKCstDef->RefColOff); + fieldName= (cst_name_t*)tempPtr; + for (fld = 0; fld < FKCstDef->RefCnt; ++fld) + { + convFromEbcdic(fieldName->Name, convName, fieldName->Len); + if (convName[0] == '"') // If quoted, exclude quotes. + name = thd_make_lex_string(thd, name, + convName + 1, (uint) (fieldName->Len -2), 1); + else + name = thd_make_lex_string(thd, name, convName, (uint) fieldName->Len, 1); + f_key_info.referenced_fields.push_back(name); + if ((fld + 1) < FKCstDef->RefCnt) + fieldName = fieldName + 1; + } + + /* Process the ON UPDATE and ON DELETE rules. */ + + switch(FKCstDef->UpdMethod) + { + case QMY_NOACTION: + { + method = "NO ACTION"; + methodLen=9; + } + break; + case QMY_RESTRICT: + { + method = "RESTRICT"; + methodLen = 8; + } + break; + default: break; + } + f_key_info.update_method = thd_make_lex_string( + thd, f_key_info.update_method, method, methodLen, 1); + switch(FKCstDef->DltMethod) + { + case QMY_CASCADE: + { + method = "CASCADE"; + methodLen = 7; + } + break; + case QMY_SETDFT: + { + method = "SET DEFAULT"; + methodLen = 11; + } + break; + case QMY_SETNULL: + { + method = "SET NULL"; + methodLen = 8; + } + break; + case QMY_NOACTION: + { + method = "NO ACTION"; + methodLen = 9; + } + break; + case QMY_RESTRICT: + { + method = "RESTRICT"; + methodLen = 8; + } + break; + default: break; + } + f_key_info.delete_method = thd_make_lex_string( + thd, f_key_info.delete_method, method, methodLen, 1); + f_key_info.referenced_key_name= thd_make_lex_string(thd, 0, (char *)"", 1, 1); + FOREIGN_KEY_INFO *pf_key_info = (FOREIGN_KEY_INFO *) + thd_memdup(thd, &f_key_info, sizeof(FOREIGN_KEY_INFO)); + f_key_list->push_back(pf_key_info); + } + + /* Address the next constraint, if any. */ + + if ((i+1) < cstCnt) + { + tempPtr = (char*)cstHdr + cstHdr->CstLen; + cstHdr = (constraint_hdr_t*)(tempPtr); + } + } + } + + /* Cleanup and return. */ + + free_share(share); + DBUG_RETURN(0); +} + +/*********************************************************************** +Checks if the table is referenced by a foreign key. +Returns: 0 if not referenced (or error occurs), + > 0 if is referenced +***********************************************************************/ + +uint ha_ibmdb2i::referenced_by_foreign_key(void) +{ + DBUG_ENTER("ha_ibmdb2i::referenced_by_foreign_key"); + + int rc = 0; + FILE_HANDLE queryFile = 0; + uint32 resultRowLen; + uint32 count = 0; + + const char* libName = db2Table->getDB2LibName(db2i_table::ASCII_SQL); + const char* fileName = db2Table->getDB2TableName(db2i_table::ASCII_SQL); + + String query(128); + query.append(STRING_WITH_LEN(" SELECT COUNT(*) FROM SYSIBM.SQLFOREIGNKEYS WHERE PKTABLE_SCHEM = '")); + query.append(libName+1, strlen(libName)-2); // parent library name + query.append(STRING_WITH_LEN("' AND PKTABLE_NAME = '")); + query.append(fileName+1, strlen(fileName)-2); // parent file name + query.append(STRING_WITH_LEN("'")); + + SqlStatementStream sqlStream(query); + + rc = bridge()->prepOpen(sqlStream.getPtrToData(), + &queryFile, + &resultRowLen); + if (rc == 0) + { + IOReadBuffer rowBuffer(1, resultRowLen); + rc = bridge()->read(queryFile, rowBuffer.ptr(), QMY_READ_ONLY, QMY_NONE, QMY_FIRST); + if (!rc) count = *((uint32*)rowBuffer.getRowN(0)); + bridge()->deallocateFile(queryFile); + } + DBUG_RETURN(count); +} + + +bool ha_ibmdb2i::check_if_incompatible_data(HA_CREATE_INFO *info, + uint table_changes) +{ + DBUG_ENTER("ha_ibmdb2i::check_if_incompatible_data"); + uint i; + /* Check that auto_increment value and field definitions were + not changed. */ + if ((info->used_fields & HA_CREATE_USED_AUTO && + info->auto_increment_value != 0) || + table_changes != IS_EQUAL_YES) + DBUG_RETURN(COMPATIBLE_DATA_NO); + /* Check if any fields were renamed. */ + for (i= 0; i < table->s->fields; i++) + { + Field *field= table->field[i]; + if (field->flags & FIELD_IS_RENAMED) + { + DBUG_PRINT("info", ("Field has been renamed, copy table")); + DBUG_RETURN(COMPATIBLE_DATA_NO); + } + } + DBUG_RETURN(COMPATIBLE_DATA_YES); +} diff --git a/storage/ibmdb2i/db2i_conversion.cc b/storage/ibmdb2i/db2i_conversion.cc new file mode 100644 index 00000000000..f746be6ab50 --- /dev/null +++ b/storage/ibmdb2i/db2i_conversion.cc @@ -0,0 +1,1446 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + + +#include "db2i_ileBridge.h" +#include "mysql_priv.h" +#include "db2i_charsetSupport.h" +#include "ctype.h" +#include "ha_ibmdb2i.h" +#include "db2i_errors.h" +#include "wchar.h" + +const char ZERO_DATETIME_VALUE[] = "0000-00-00 00:00:00"; +const char ZERO_DATETIME_VALUE_SUBST[] = "0001-01-01 00:00:00"; +const char ZERO_DATE_VALUE[] = "0000-00-00"; +const char ZERO_DATE_VALUE_SUBST[] = "0001-01-01"; + + +/** + Put a BCD digit into a BCD string. + + @param[out] bcdString The BCD string to be modified + @param pos The position within the string to be updated. + @param val The value to be assigned into the string at pos. +*/ +static inline void bcdAssign(char* bcdString, uint pos, uint val) +{ + bcdString[pos/2] |= val << ((pos % 2) ? 0 : 4); +} + +/** + Read a BCD digit from a BCD string. + + @param[out] bcdString The BCD string to be read + @param pos The position within the string to be read. + + @return bcdGet The value of the BCD digit at pos. +*/ +static inline uint bcdGet(const char* bcdString, uint pos) +{ + return (bcdString[pos/2] >> ((pos % 2) ? 0 : 4)) & 0xf; +} + +/** + In-place convert a number in ASCII represenation to EBCDIC representation. + + @param string The string of ASCII characters + @param len The length of string +*/ +static inline void convertNumericToEbcdicFast(char* string, int len) +{ + for (int i = 0; i < len; ++i, ++string) + { + switch(*string) + { + case '-': + *string = 0x60; break; + case ':': + *string = 0x7A; break; + case '.': + *string = 0x4B; break; + default: + DBUG_ASSERT(isdigit(*string)); + *string += 0xF0 - '0'; + break; + } + } +} + + +/** + atoi()-like function for a 4-character EBCDIC string. + + @param string The EBCDIC string + @return a4toi_ebcdic The decimal value of the EBCDIC string +*/ +static inline uint16 a4toi_ebcdic(const uchar* string) +{ + return ((string[0]-0xF0) * 1000 + + (string[1]-0xF0) * 100 + + (string[2]-0xF0) * 10 + + (string[3]-0xF0)); +}; + + +/** + atoi()-like function for a 4-character EBCDIC string. + + @param string The EBCDIC string + @return a4toi_ebcdic The decimal value of the EBCDIC string +*/ +static inline uint8 a2toi_ebcdic(const uchar* string) +{ + return ((string[0]-0xF0) * 10 + + (string[1]-0xF0)); +}; + +/** + Perform character conversion for textual field data. +*/ +int ha_ibmdb2i::convertFieldChars(enum_conversionDirection direction, + uint16 fieldID, + const char* input, + char* output, + size_t ilen, + size_t olen, + size_t* outDataLen) +{ + DBUG_PRINT("ha_ibmdb2i::convertFieldChars",("Direction: %d; length = %d", direction, ilen)); + + if (unlikely(ilen == 0)) + { + if (outDataLen) *outDataLen = 0; + return (0); + } + + iconv_t& conversion = db2Table->getConversionDefinition(direction, fieldID); + + if (unlikely(conversion == (iconv_t)(-1))) + { + return (DB2I_ERR_ICONV_OPEN); + } + + size_t initOLen= olen; + size_t substitutedChars = 0; + int rc = iconv(conversion, (char**)&input, &ilen, &output, &olen, &substitutedChars ); + if (unlikely(rc < 0)) + { + int er = errno; + if (er == EILSEQ) + { + getErrTxt(DB2I_ERR_ILL_CHAR, table->field[fieldID]->field_name); + return (DB2I_ERR_ILL_CHAR); + } + else + { + getErrTxt(DB2I_ERR_ICONV,er); + return (DB2I_ERR_ICONV); + } + } + if (unlikely(substitutedChars)) + { + warning(ha_thd(), DB2I_ERR_SUB_CHARS, table->field[fieldID]->field_name); + } + + if (outDataLen) *outDataLen = initOLen - olen; + + return (0); +} + +/** + Append the appropriate default value clause onto a CREATE TABLE definition + + This was inspired by get_field_default_value in sql/sql_show.cc. + + @param field The field whose value is to be obtained + @param statement The string to receive the DEFAULT clause + @param quoteIt Does the data type require single quotes around the value? + @param ccsid The ccsid of the field value (if a string type); 0 if no conversion needed +*/ +static void get_field_default_value(Field *field, + String &statement, + bool quoteIt, + uint32 ccsid, + bool substituteZeroDates) +{ + if ((field->type() != FIELD_TYPE_BLOB && + !(field->flags & NO_DEFAULT_VALUE_FLAG) && + field->unireg_check != Field::NEXT_NUMBER)) + { + my_ptrdiff_t old_ptr= (my_ptrdiff_t) (field->table->s->default_values - field->table->record[0]); + field->move_field_offset(old_ptr); + + String defaultClause(64); + defaultClause.length(0); + defaultClause.append(" DEFAULT "); + if (!field->is_null()) + { + my_bitmap_map *old_map = tmp_use_all_columns(field->table, field->table->read_set); + char tmp[MAX_FIELD_WIDTH]; + + if (field->real_type() == MYSQL_TYPE_ENUM || + field->real_type() == MYSQL_TYPE_SET) + { + CHARSET_INFO *cs= &my_charset_bin; + uint len = (uint)(cs->cset->longlong10_to_str)(cs,tmp,sizeof(tmp), 10, field->val_int()); + tmp[len]=0; + defaultClause.append(tmp); + } + else + { + String type(tmp, sizeof(tmp), field->charset()); + field->val_str(&type); + if (type.length()) + { + if (field->type() == MYSQL_TYPE_DATE && + memcmp(type.ptr(), STRING_WITH_LEN(ZERO_DATE_VALUE)) == 0) + { + if (substituteZeroDates) + type.set(STRING_WITH_LEN(ZERO_DATE_VALUE_SUBST), field->charset()); + else + { + warning(current_thd, DB2I_ERR_WARN_COL_ATTRS, field->field_name); + return; + } + } + else if ((field->type() == MYSQL_TYPE_DATETIME || + field->type() == MYSQL_TYPE_TIMESTAMP) && + memcmp(type.ptr(), STRING_WITH_LEN(ZERO_DATETIME_VALUE)) == 0) + { + if (substituteZeroDates) + type.set(STRING_WITH_LEN(ZERO_DATETIME_VALUE_SUBST), field->charset()); + else + { + warning(current_thd, DB2I_ERR_WARN_COL_ATTRS, field->field_name); + return; + } + } + + + if (field->type() != MYSQL_TYPE_STRING && + field->type() != MYSQL_TYPE_VARCHAR && + field->type() != MYSQL_TYPE_BLOB && + field->type() != MYSQL_TYPE_BIT) + { + if (quoteIt) + defaultClause.append('\''); + defaultClause.append(type); + if (quoteIt) + defaultClause.append('\''); + } + else + { + int length; + char* out; + + // If a ccsid is specified, we need to make sure that the DEFAULT + // string is converted to that encoding. + if (ccsid != 0) + { + iconv_t iconvD; + if (getConversion(toDB2, field->charset(), ccsid, iconvD)) + { + warning(current_thd, DB2I_ERR_WARN_COL_ATTRS, field->field_name); + return; + } + + size_t ilen = type.length(); + size_t olen = 6 * ilen; + size_t origOlen = olen; + const char* in = type.ptr(); + const char* tempIn = in; + out = (char*)my_malloc(olen, MYF(MY_WME)); + char* tempOut = out; + size_t substitutedChars; + + if (iconv(iconvD, (char**)&tempIn, &ilen, &tempOut, &olen, &substitutedChars) < 0) + { + warning(current_thd, DB2I_ERR_WARN_COL_ATTRS, field->field_name); + my_free(out, MYF(0)); + return; + } + // Now we process the converted string to represent it as + // hexadecimal values. + + length = origOlen - olen; + } + else + { + length = type.length(); + out = (char*)my_malloc(length*2, MYF(MY_WME)); + memcpy(out, (char*)type.ptr(), length); + } + + if (length > 16370) + { + warning(current_thd, DB2I_ERR_WARN_COL_ATTRS, field->field_name); + my_free(out, MYF(0)); + return; + } + + if (ccsid == 1200) + defaultClause.append("ux'"); + else if (ccsid == 13488) + defaultClause.append("gx'"); + else if (field->charset() == &my_charset_bin) + defaultClause.append("binary(x'"); + else + defaultClause.append("x'"); + + const char hexMap[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + for (int c = length-1; c >= 0; --c) + { + out[c*2+1] = hexMap[out[c] & 0xF]; + out[c*2] = hexMap[out[c] >> 4]; + } + + defaultClause.append(out, length*2); + defaultClause.append('\''); + if (field->charset() == &my_charset_bin) + defaultClause.append(")"); + + my_free(out, MYF(0)); + } + } + else + defaultClause.length(0); + } + tmp_restore_column_map(field->table->read_set, old_map); + } + else if (field->maybe_null()) + defaultClause.append(STRING_WITH_LEN("NULL")); + + if (old_ptr) + field->move_field_offset(-old_ptr); + + statement.append(defaultClause); + } +} + + + + +/** + Convert a MySQL field definition into its corresponding DB2 type. + + The result will be appended to mapping as a DB2 SQL phrase. + + @param field The MySQL field to be evaluated + @param[out] mapping The receiver for the DB2 SQL syntax + @param timeFormat The format to be used for mapping the TIME type +*/ +int ha_ibmdb2i::getFieldTypeMapping(Field* field, + String& mapping, + enum_TimeFormat timeFormat, + enum_BlobMapping blobMapping, + enum_ZeroDate zeroDateHandling, + bool propagateDefaults, + enum_YearFormat yearFormat) +{ + char stringBuildBuffer[257]; + uint32 fieldLength; + bool defaultNeedsQuotes = false; + uint16 db2Ccsid = 0; + + CHARSET_INFO* fieldCharSet = field->charset(); + switch (field->type()) + { + case MYSQL_TYPE_NEWDECIMAL: + { + uint precision= ((Field_new_decimal*)field)->precision; + uint scale= field->decimals(); + + if (precision <= MAX_DEC_PRECISION) + { + sprintf(stringBuildBuffer,"DECIMAL(%d, %d)",precision,scale); + } + else + { + if (scale > precision - MAX_DEC_PRECISION) + { + scale = scale - (precision - MAX_DEC_PRECISION); + precision = MAX_DEC_PRECISION; + sprintf(stringBuildBuffer,"DECIMAL(%d, %d)",precision,scale); + } + else + { + return HA_ERR_UNSUPPORTED; + } + warning(ha_thd(), DB2I_ERR_PRECISION); + } + + mapping.append(stringBuildBuffer); + } + break; + case MYSQL_TYPE_TINY: + mapping.append(STRING_WITH_LEN("SMALLINT")); + break; + case MYSQL_TYPE_SHORT: + if (((Field_num*)field)->unsigned_flag) + mapping.append(STRING_WITH_LEN("INT")); + else + mapping.append(STRING_WITH_LEN("SMALLINT")); + break; + case MYSQL_TYPE_LONG: + if (((Field_num*)field)->unsigned_flag) + mapping.append(STRING_WITH_LEN("BIGINT")); + else + mapping.append(STRING_WITH_LEN("INT")); + break; + case MYSQL_TYPE_FLOAT: + mapping.append(STRING_WITH_LEN("REAL")); + break; + case MYSQL_TYPE_DOUBLE: + mapping.append(STRING_WITH_LEN("DOUBLE")); + break; + case MYSQL_TYPE_LONGLONG: + if (((Field_num*)field)->unsigned_flag) + mapping.append(STRING_WITH_LEN("DECIMAL(20,0)")); + else + mapping.append(STRING_WITH_LEN("BIGINT")); + break; + case MYSQL_TYPE_INT24: + mapping.append(STRING_WITH_LEN("INTEGER")); + break; + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + mapping.append(STRING_WITH_LEN("DATE")); + defaultNeedsQuotes = true; + break; + case MYSQL_TYPE_TIME: + if (timeFormat == TIME_OF_DAY) + { + mapping.append(STRING_WITH_LEN("TIME")); + defaultNeedsQuotes = true; + } + else + mapping.append(STRING_WITH_LEN("INTEGER")); + break; + case MYSQL_TYPE_DATETIME: + mapping.append(STRING_WITH_LEN("TIMESTAMP")); + defaultNeedsQuotes = true; + break; + case MYSQL_TYPE_TIMESTAMP: + mapping.append(STRING_WITH_LEN("TIMESTAMP")); + + if (table_share->timestamp_field == field && propagateDefaults) + { + switch (((Field_timestamp*)field)->get_auto_set_type()) + { + case TIMESTAMP_NO_AUTO_SET: + break; + case TIMESTAMP_AUTO_SET_ON_INSERT: + mapping.append(STRING_WITH_LEN(" DEFAULT CURRENT_TIMESTAMP")); + break; + case TIMESTAMP_AUTO_SET_ON_UPDATE: + if (osVersion.v >= 6 && + !field->is_null()) + { + mapping.append(STRING_WITH_LEN(" GENERATED BY DEFAULT FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP")); + warning(ha_thd(), DB2I_ERR_WARN_COL_ATTRS, field->field_name); + } + else + warning(ha_thd(), DB2I_ERR_WARN_COL_ATTRS, field->field_name); + break; + case TIMESTAMP_AUTO_SET_ON_BOTH: + if (osVersion.v >= 6 && + !field->is_null()) + mapping.append(STRING_WITH_LEN(" GENERATED BY DEFAULT FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP")); + else + { + mapping.append(STRING_WITH_LEN(" DEFAULT CURRENT_TIMESTAMP")); + warning(ha_thd(), DB2I_ERR_WARN_COL_ATTRS, field->field_name); + } + break; + } + } + else + defaultNeedsQuotes = true; + break; + case MYSQL_TYPE_YEAR: + if (yearFormat == CHAR4) + { + mapping.append(STRING_WITH_LEN("CHAR(4) CCSID 1208")); + defaultNeedsQuotes = true; + } + else + { + mapping.append(STRING_WITH_LEN("SMALLINT")); + defaultNeedsQuotes = false; + } + break; + case MYSQL_TYPE_BIT: + sprintf(stringBuildBuffer, "BINARY(%d)", (field->max_display_length() / 8) + 1); + mapping.append(stringBuildBuffer); + break; + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_STRING: + { + if (field->real_type() == MYSQL_TYPE_ENUM || + field->real_type() == MYSQL_TYPE_SET) + { + mapping.append(STRING_WITH_LEN("BIGINT")); + } + else + { + defaultNeedsQuotes = true; + + fieldLength = field->max_display_length(); // Get field byte length + + if (fieldCharSet == &my_charset_bin) + { + if (field->type() == MYSQL_TYPE_STRING) + { + sprintf(stringBuildBuffer, "BINARY(%d)", max(fieldLength, 1)); + } + else + { + if (fieldLength <= MAX_VARCHAR_LENGTH) + { + sprintf(stringBuildBuffer, "VARBINARY(%d)", max(fieldLength, 1)); + } + else if (blobMapping == AS_VARCHAR && + (field->flags & PART_KEY_FLAG)) + { + sprintf(stringBuildBuffer, "LONG VARBINARY "); + } + else + { + fieldLength = min(MAX_BLOB_LENGTH, fieldLength); + sprintf(stringBuildBuffer, "BLOB(%d)", max(fieldLength, 1)); + } + } + mapping.append(stringBuildBuffer); + } + else + { + if (field->type() == MYSQL_TYPE_STRING) + { + if (fieldLength > MAX_CHAR_LENGTH) + return 1; + if (fieldCharSet->mbmaxlen > 1) + { + if (strncmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")) == 0 ) // UCS2 + { + sprintf(stringBuildBuffer, "GRAPHIC(%d)", max(fieldLength / fieldCharSet->mbmaxlen, 1)); // Number of characters + db2Ccsid = 13488; + } + else if (strncmp(fieldCharSet->name, "utf8_", sizeof("utf8_")) == 0 && + strcmp(fieldCharSet->name, "utf8_general_ci") != 0) + { + sprintf(stringBuildBuffer, "CHAR(%d)", max(fieldLength, 1)); // Number of bytes + db2Ccsid = 1208; + } + else + { + sprintf(stringBuildBuffer, "GRAPHIC(%d)", max(fieldLength / fieldCharSet->mbmaxlen, 1)); // Number of characters + db2Ccsid = 1200; + } + } + else + { + sprintf(stringBuildBuffer, "CHAR(%d)", max(fieldLength, 1)); + } + mapping.append(stringBuildBuffer); + } + else + { + if (fieldLength <= MAX_VARCHAR_LENGTH) + { + if (fieldCharSet->mbmaxlen > 1) + { + if (strncmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")) == 0 ) // UCS2 + { + sprintf(stringBuildBuffer, "VARGRAPHIC(%d)", max(fieldLength / fieldCharSet->mbmaxlen, 1)); // Number of characters + db2Ccsid = 13488; + } + else if (strncmp(fieldCharSet->name, "utf8_", sizeof("utf8_")) == 0 && + strcmp(fieldCharSet->name, "utf8_general_ci") != 0) + { + sprintf(stringBuildBuffer, "VARCHAR(%d)", max(fieldLength, 1)); // Number of bytes + db2Ccsid = 1208; + } + else + { + sprintf(stringBuildBuffer, "VARGRAPHIC(%d)", max(fieldLength / fieldCharSet->mbmaxlen, 1)); // Number of characters + db2Ccsid = 1200; + } + } + else + { + sprintf(stringBuildBuffer, "VARCHAR(%d)", max(fieldLength, 1)); + } + } + else if (blobMapping == AS_VARCHAR && + (field->flags & PART_KEY_FLAG)) + { + if (fieldCharSet->mbmaxlen > 1) + { + if (strncmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")) == 0 ) // UCS2 + { + sprintf(stringBuildBuffer, "LONG VARGRAPHIC "); + db2Ccsid = 13488; + } + else if (strncmp(fieldCharSet->name, "utf8_", sizeof("utf8_")) == 0 && + strcmp(fieldCharSet->name, "utf8_general_ci") != 0) + { + sprintf(stringBuildBuffer, "LONG VARCHAR "); + db2Ccsid = 1208; + } + else + { + sprintf(stringBuildBuffer, "LONG VARGRAPHIC "); + db2Ccsid = 1200; + } + } + else + { + sprintf(stringBuildBuffer, "LONG VARCHAR "); + } + } + else + { + fieldLength = min(MAX_BLOB_LENGTH, fieldLength); + + if (fieldCharSet->mbmaxlen > 1) + { + if (strncmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")) == 0 ) // UCS2 + { + sprintf(stringBuildBuffer, "DBCLOB(%d)", max(fieldLength / fieldCharSet->mbmaxlen, 1)); // Number of characters + db2Ccsid = 13488; + } + else if (strncmp(fieldCharSet->name, "utf8_", sizeof("utf8_")) == 0 && + strcmp(fieldCharSet->name, "utf8_general_ci") != 0) + { + sprintf(stringBuildBuffer, "CLOB(%d)", max(fieldLength, 1)); // Number of bytes + db2Ccsid = 1208; + } + else + { + sprintf(stringBuildBuffer, "DBCLOB(%d)", max(fieldLength / fieldCharSet->mbmaxlen, 1)); // Number of characters + db2Ccsid = 1200; + } + } + else + { + sprintf(stringBuildBuffer, "CLOB(%d)", max(fieldLength, 1)); // Number of characters + } + } + + mapping.append(stringBuildBuffer); + } + if (db2Ccsid == 0) // If not overriding CCSID + { + int32 rtnCode = convertIANAToDb2Ccsid(fieldCharSet->csname, &db2Ccsid); + if (rtnCode) + return rtnCode; + } + sprintf(stringBuildBuffer, " CCSID %d ", db2Ccsid); + mapping.append(stringBuildBuffer); + } + } + } + break; + + } + + if (propagateDefaults) + get_field_default_value(field, + mapping, + defaultNeedsQuotes, + db2Ccsid, + (zeroDateHandling==SUBSTITUTE_0001_01_01)); + + return 0; +} + + +/** + Convert MySQL field data into the equivalent DB2 format + + @param field The MySQL field to be converted + @param db2Field The corresponding DB2 field definition + @param db2Buf The buffer to receive the converted data + @param data NULL if field points to the correct data; otherwise, + the data to be converted (for use with keys) +*/ +int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char* db2Buf, const uchar* data) +{ + enum_field_types fieldType = field->type(); + switch (fieldType) + { + case MYSQL_TYPE_NEWDECIMAL: + { + uint precision= ((Field_new_decimal*)field)->precision; + uint scale= field->decimals(); + uint db2Precision = min(precision, MAX_DEC_PRECISION); + uint truncationAmount = precision - db2Precision; + + if (scale >= truncationAmount) + { + String tempString(precision+2); + + if (data == NULL) + { + field->val_str((String*)&tempString, (String*)(NULL)); + } + else + { + field->val_str(&tempString, data); + } + const char* temp = tempString.ptr(); + char packed[32]; + memset(&packed, 0, sizeof(packed)); + + int bcdPos = db2Precision - (db2Precision % 2 ? 1 : 0); + bcdAssign(packed, bcdPos+1, (temp[0] == '-' ? 0xD : 0xF)); + + int strPos=tempString.length() - 1 - truncationAmount; + + for (;strPos >= 0 && bcdPos >= 0; strPos--) + { + if (my_isdigit(&my_charset_latin1, temp[strPos])) + { + bcdAssign(packed, bcdPos, temp[strPos]-'0'); + --bcdPos; + } + } + memcpy(db2Buf, &packed, (db2Precision/2)+1); + } + + } + break; + case MYSQL_TYPE_TINY: + { + int16 temp = (data == NULL ? field->val_int() : field->val_int(data)); + memcpy(db2Buf , &temp, sizeof(temp)); + } + break; + case MYSQL_TYPE_SHORT: + { + if (((Field_num*)field)->unsigned_flag) + { + memset(db2Buf, 0, 2); + memcpy(db2Buf+2, (data == NULL ? field->ptr : data), 2); + } + else + { + memcpy(db2Buf, (data == NULL ? field->ptr : data), 2); + } + } + break; + case MYSQL_TYPE_LONG: + { + if (((Field_num*)field)->unsigned_flag) + { + memset(db2Buf, 0, 4); + memcpy(db2Buf+4, (data == NULL ? field->ptr : data), 4); + } + else + { + memcpy(db2Buf, (data == NULL ? field->ptr : data), 4); + } + } + break; + case MYSQL_TYPE_FLOAT: + { + memcpy(db2Buf, (data == NULL ? field->ptr : data), 4); + } + break; + case MYSQL_TYPE_DOUBLE: + { + memcpy(db2Buf, (data == NULL ? field->ptr : data), 8); + } + break; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + { + String tempString(27); + if (data == NULL) + { + field->val_str(&tempString, &tempString); + } + else + { + field->val_str(&tempString, data); + } + memset(db2Buf, '0', 26); + memcpy(db2Buf, tempString.ptr(), tempString.length()); + if (strncmp(db2Buf,ZERO_DATETIME_VALUE,strlen(ZERO_DATETIME_VALUE)) == 0) + { + if (cachedZeroDateOption == SUBSTITUTE_0001_01_01) + memcpy(db2Buf, ZERO_DATETIME_VALUE_SUBST, sizeof(ZERO_DATETIME_VALUE_SUBST)); + else + { + getErrTxt(DB2I_ERR_INVALID_COL_VALUE, field->field_name); + return(DB2I_ERR_INVALID_COL_VALUE); + } + } + (db2Buf)[10] = '-'; + (db2Buf)[13] = (db2Buf)[16] = (db2Buf)[19] = '.'; + + convertNumericToEbcdicFast(db2Buf, 26); + } + break; + case MYSQL_TYPE_LONGLONG: + { + if (((Field_num*)field)->unsigned_flag) + { + char temp[23]; + String tempString(temp, sizeof(temp), &my_charset_latin1); + + if (data == NULL) + { + field->val_str((String*)&tempString, (String*)(NULL)); + } + else + { + field->val_str(&tempString, data); + } + char packed[11]; + memset(packed, 0, sizeof(packed)); + bcdAssign(packed, 21, (temp[0] == '-' ? 0xD : 0xF)); + int strPos=tempString.length()-1; + int bcdPos=20; + + for (;strPos >= 0; strPos--) + { + if (my_isdigit(&my_charset_latin1, temp[strPos])) + { + bcdAssign(packed, bcdPos, temp[strPos]-'0'); + --bcdPos; + } + } + memcpy(db2Buf, &packed, 11); + } + else + { + *(uint64*)db2Buf = *(uint64*)(data == NULL ? field->ptr : data); + } + } + break; + case MYSQL_TYPE_INT24: + { + int32 temp= (data == NULL ? field->val_int() : field->val_int(data)); + memcpy(db2Buf , &temp, sizeof(temp)); + } + break; + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + { + String tempString(11); + if (data == NULL) + { + field->val_str(&tempString, (String*)NULL); + } + else + { + field->val_str(&tempString, data); + } + memcpy(db2Buf, tempString.ptr(), 10); + if (strncmp(db2Buf,ZERO_DATE_VALUE,strlen(ZERO_DATE_VALUE)) == 0) + { + if (cachedZeroDateOption == SUBSTITUTE_0001_01_01) + memcpy(db2Buf, ZERO_DATE_VALUE_SUBST, sizeof(ZERO_DATE_VALUE_SUBST)); + else + { + getErrTxt(DB2I_ERR_INVALID_COL_VALUE,field->field_name); + return(DB2I_ERR_INVALID_COL_VALUE); + } + } + + convertNumericToEbcdicFast(db2Buf,10); + } + break; + case MYSQL_TYPE_TIME: + { + if (db2Field.getType() == QMY_TIME) + { + String tempString(10); + if (data == NULL) + { + field->val_str(&tempString, (String*)NULL); + } + else + { + field->val_str(&tempString, data); + } + memcpy(db2Buf, tempString.ptr(), 8); + (db2Buf)[2]=(db2Buf)[5] = '.'; + + convertNumericToEbcdicFast(db2Buf, 8); + } + else + { + int32 temp = sint3korr(data == NULL ? field->ptr : data); + memcpy(db2Buf, &temp, sizeof(temp)); + } + } + break; + case MYSQL_TYPE_YEAR: + { + String tempString(5); + if (db2Field.getType() == QMY_CHAR) + { + if (data == NULL) + { + field->val_str(&tempString, (String*)NULL); + } + else + { + field->val_str(&tempString, data); + } + memcpy(db2Buf, tempString.ptr(), 4); + } + else + { + uint8 temp = *(uint8*)(data == NULL ? field->ptr : data); + *(uint16*)(db2Buf) = (temp ? temp + 1900 : 0); + } + } + break; + case MYSQL_TYPE_BIT: + { + int bytesToCopy = db2Field.getByteLengthInRecord(); + + if (data == NULL) + { + uint64 temp = field->val_int(); + memcpy(db2Buf, + ((char*)&temp) + (sizeof(temp) - bytesToCopy), + bytesToCopy); + } + else + { + memcpy(db2Buf, + data, + bytesToCopy); + } + } + break; + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_BLOB: + { + if (field->real_type() == MYSQL_TYPE_ENUM || + field->real_type() == MYSQL_TYPE_SET) + { + int64 temp= (data == NULL ? field->val_int() : field->val_int(data)); + *(int64*)db2Buf = temp; + } + else + { + const uchar* dataToStore; + uint32 bytesToStore; + uint32 bytesToPad = 0; + CHARSET_INFO* fieldCharSet = field->charset(); + uint32 maxDisplayLength = field->max_display_length(); + switch (fieldType) + { + case MYSQL_TYPE_STRING: + { + bytesToStore = maxDisplayLength; + if (data == NULL) + dataToStore = field->ptr; + else + dataToStore = data; + } + break; + case MYSQL_TYPE_VARCHAR: + { + + if (data == NULL) + { + bytesToStore = field->data_length(); + dataToStore = field->ptr + ((Field_varstring*)field)->length_bytes; + } + else + { + // Key lens are stored little-endian + bytesToStore = *(uint8*)data + ((*(uint8*)(data+1)) << 8); + dataToStore = data + 2; + } + bytesToPad = maxDisplayLength - bytesToStore; + } + break; + case MYSQL_TYPE_BLOB: + { + if (data == NULL) + { + bytesToStore = ((Field_blob*)field)->get_length(); + bytesToPad = maxDisplayLength - bytesToStore; + ((Field_blob*)field)->get_ptr((uchar**)&dataToStore); + } + else + { + // Key lens are stored little-endian + bytesToStore = *(uint8*)data + ((*(uint8*)(data+1)) << 8); + dataToStore = data + 2; + } + } + break; + } + + int32 rc; + uint16 db2FieldType = db2Field.getType(); + switch(db2FieldType) + { + case QMY_CHAR: + if (maxDisplayLength == 0) + bytesToPad = 1; + case QMY_VARCHAR: + if (db2FieldType == QMY_VARCHAR) + { + db2Buf += sizeof(uint16); + bytesToPad = 0; + } + + if (bytesToStore > db2Field.getDataLengthInRecord()) + { + bytesToStore = db2Field.getDataLengthInRecord(); + field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + } + + if (fieldCharSet == &my_charset_bin) // If binary + { + if (bytesToStore) + memcpy(db2Buf, dataToStore, bytesToStore); + if (bytesToPad) + memset(db2Buf + bytesToStore, 0x00, bytesToPad); + } + else if (db2Field.getCCSID() == 1208) // utf8 + { + if (bytesToStore) + memcpy(db2Buf, dataToStore, bytesToStore); + if (bytesToPad) + memset(db2Buf + bytesToStore, ' ', bytesToPad); + } + else // single-byte ASCII to EBCDIC + { + DBUG_ASSERT(fieldCharSet->mbmaxlen == 1); + if (bytesToStore) + { + rc = convertFieldChars(toDB2, field->field_index, (char*)dataToStore, db2Buf, bytesToStore, bytesToStore, NULL); + if (rc) + return rc; + } + if (bytesToPad) + memset(db2Buf + bytesToStore, 0x40, bytesToPad); + } + + if (db2FieldType == QMY_VARCHAR) + *(uint16*)(db2Buf - sizeof(uint16)) = bytesToStore; + break; + case QMY_VARGRAPHIC: + db2Buf += sizeof(uint16); + bytesToPad = 0; + case QMY_GRAPHIC: + if (maxDisplayLength == 0 && db2FieldType == QMY_GRAPHIC) + bytesToPad = 2; + + if (db2Field.getCCSID() == 13488) + { + if (bytesToStore) + memcpy(db2Buf, dataToStore, bytesToStore); + if (bytesToPad) + wmemset((wchar_t*)(db2Buf + bytesToStore), 0x0020, bytesToPad/2); + } + else + { + size_t db2BytesToStore; + size_t maxDb2BytesToStore; + + if (maxDisplayLength == 0 && db2FieldType == QMY_GRAPHIC) + maxDb2BytesToStore = 2; + else + maxDb2BytesToStore = min(((bytesToStore * 2) / fieldCharSet->mbminlen), + ((maxDisplayLength * 2) / fieldCharSet->mbmaxlen)); + + if (bytesToStore == 0) + db2BytesToStore = 0; + else + { + rc = convertFieldChars(toDB2, field->field_index, (char*)dataToStore, db2Buf, bytesToStore, maxDb2BytesToStore, &db2BytesToStore); + if (rc) + return rc; + bytesToStore = db2BytesToStore; + } + if (db2BytesToStore < maxDb2BytesToStore) // If need to pad + wmemset((wchar_t*)(db2Buf + db2BytesToStore), 0x0020, (maxDb2BytesToStore - db2BytesToStore)/2); + } + + if (db2FieldType == QMY_VARGRAPHIC) + *(uint16*)(db2Buf-sizeof(uint16)) = bytesToStore/2; + break; + case QMY_BLOBCLOB: + case QMY_DBCLOB: + { + DBUG_ASSERT(data == NULL); + DB2LobField* lobField = (DB2LobField*)(db2Buf + db2Field.calcBlobPad()); + + if ((fieldCharSet == &my_charset_bin) || // binary or + (db2Field.getCCSID()==13488) || + (db2Field.getCCSID()==1208)) // binary UTF8 + { + } + else + { + char* temp; + int32 rc; + size_t db2BytesToStore; + if (fieldCharSet->mbmaxlen == 1) // single-byte ASCII to EBCDIC + { + temp = getCharacterConversionBuffer(field->field_index, bytesToStore); + rc = convertFieldChars(toDB2, field->field_index, (char*)dataToStore,temp,bytesToStore, bytesToStore, NULL); + if (rc) + return (rc); + } + else // Else Far East, special UTF8 or non-special UTF8/UCS2 + { + size_t maxDb2BytesToStore; + maxDb2BytesToStore = min(((bytesToStore * 2) / fieldCharSet->mbminlen), + ((maxDisplayLength * 2) / fieldCharSet->mbmaxlen)); + temp = getCharacterConversionBuffer(field->field_index, maxDb2BytesToStore); + rc = convertFieldChars(toDB2, field->field_index, (char*)dataToStore,temp,bytesToStore, maxDb2BytesToStore, &db2BytesToStore); + if (rc) + return (rc); + bytesToStore = db2BytesToStore; + } + dataToStore = (uchar*)temp; + } + + uint16 blobID = db2Table->getBlobIdFromField(field->field_index); + if (blobWriteBuffers[blobID] != (char*)dataToStore) + blobWriteBuffers[blobID].reassign((char*)dataToStore); + if ((void*)blobWriteBuffers[blobID]) + lobField->dataHandle = (ILEMemHandle)blobWriteBuffers[blobID]; + else + lobField->dataHandle = 0; + lobField->length = bytesToStore / (db2FieldType == QMY_DBCLOB ? 2 : 1); + } + break; + } + } + } + break; + default: + DBUG_ASSERT(0); + break; + } + + return (ha_thd()->is_error()); +} + + +/** + Convert DB2 field data into the equivalent MySQL format + + @param db2Field The DB2 field definition + @param field The MySQL field to receive the converted data + @param buf The DB2 data to be converted +*/ +int32 ha_ibmdb2i::convertDB2toMySQL(const DB2Field& db2Field, Field* field, const char* buf) +{ + int32 storeRC = 0; // Result of the field->store() operation + + const char* bufPtr = buf + db2Field.getBufferOffset(); + + switch (field->type()) + { + case MYSQL_TYPE_NEWDECIMAL: + { + uint precision= ((Field_new_decimal*)field)->precision; + uint scale= field->decimals(); + uint db2Precision = min(precision, MAX_DEC_PRECISION); + uint decimalPlace = precision-scale+1; + char temp[80]; + + if (precision <= MAX_DEC_PRECISION || + scale > precision - MAX_DEC_PRECISION) + { + uint numNibbles = db2Precision + (db2Precision % 2 ? 0 : 1); + + temp[0] = (bcdGet(bufPtr, numNibbles) == 0xD ? '-' : ' '); + int strPos=1; + int bcdPos=(db2Precision % 2 ? 0 : 1); + + for (;bcdPos < numNibbles; bcdPos++, strPos++) + { + if (strPos == decimalPlace) + { + temp[strPos] = '.'; + strPos++; + } + + temp[strPos] = bcdGet(bufPtr, bcdPos) + '0'; + } + + temp[strPos] = 0; + + storeRC = field->store(temp, strPos, &my_charset_latin1); + } + } + break; + case MYSQL_TYPE_TINY: + { + storeRC = field->store(*(int16*)bufPtr, ((Field_num*)field)->unsigned_flag); + } + break; + case MYSQL_TYPE_SHORT: + { + if (((Field_num*)field)->unsigned_flag) + { + storeRC = field->store(*(int32*)bufPtr, TRUE); + } + else + { + storeRC = field->store(*(int16*)bufPtr, FALSE); + } + } + break; + case MYSQL_TYPE_LONG: + { + if (((Field_num*)field)->unsigned_flag) + { + storeRC = field->store(*(int64*)bufPtr, TRUE); + } + else + { + storeRC = field->store(*(int32*)bufPtr, FALSE); + } + } + break; + case MYSQL_TYPE_FLOAT: + { + storeRC = field->store(*(float*)bufPtr); + } + break; + case MYSQL_TYPE_DOUBLE: + { + storeRC = field->store(*(double*)bufPtr); + } + break; + case MYSQL_TYPE_LONGLONG: + { + char temp[23]; + if (((Field_num*)field)->unsigned_flag) + { + temp[0] = (bcdGet(bufPtr, 21) == 0xD ? '-' : ' '); + int strPos=1; + int bcdPos=0; + + for (;bcdPos <= 20; bcdPos++, strPos++) + { + temp[strPos] = bcdGet(bufPtr, bcdPos) + '0'; + } + + temp[strPos] = 0; + + storeRC = field->store(temp, strPos, &my_charset_latin1); + } + else + { + storeRC = field->store(*(int64*)bufPtr, FALSE); + } + } + break; + case MYSQL_TYPE_INT24: + { + storeRC = field->store(*(int32*)bufPtr, ((Field_num*)field)->unsigned_flag); + } + break; + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + { + longlong value= a4toi_ebcdic((uchar*)bufPtr) * 10000 + + a2toi_ebcdic((uchar*)bufPtr+5) * 100 + + a2toi_ebcdic((uchar*)bufPtr+8); + + if (cachedZeroDateOption == SUBSTITUTE_0001_01_01 && + value == (10000 + 100 + 1)) + value = 0; + + storeRC = field->store(value); + } + break; + case MYSQL_TYPE_TIME: + { + if (db2Field.getType() == QMY_TIME) + { + longlong value= a2toi_ebcdic((uchar*)bufPtr) * 10000 + + a2toi_ebcdic((uchar*)bufPtr+3) * 100 + + a2toi_ebcdic((uchar*)bufPtr+6); + + storeRC = field->store(value); + } + else + storeRC = field->store(*((int32*)bufPtr)); + } + break; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATETIME: + { + longlong value= (a4toi_ebcdic((uchar*)bufPtr) * 10000 + + a2toi_ebcdic((uchar*)bufPtr+5) * 100 + + a2toi_ebcdic((uchar*)bufPtr+8)) * 1000000LL + + (a2toi_ebcdic((uchar*)bufPtr+11) * 10000 + + a2toi_ebcdic((uchar*)bufPtr+14) * 100 + + a2toi_ebcdic((uchar*)bufPtr+17)); + + if (cachedZeroDateOption == SUBSTITUTE_0001_01_01 && + value == (10000 + 100 + 1) * 1000000LL) + value = 0; + + storeRC = field->store(value); + } + break; + case MYSQL_TYPE_YEAR: + { + if (db2Field.getType() == QMY_CHAR) + { + storeRC = field->store(bufPtr, 4, &my_charset_bin); + } + else + { + storeRC = field->store(*((uint16*)bufPtr)); + } + } + break; + case MYSQL_TYPE_BIT: + { + uint64 temp= 0; + int bytesToCopy= db2Field.getByteLengthInRecord(); + memcpy(((char*)&temp) + (sizeof(temp) - bytesToCopy), bufPtr, bytesToCopy); + storeRC = field->store(temp, TRUE); + } + break; + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_BLOB: + { + if (field->real_type() == MYSQL_TYPE_ENUM || + field->real_type() == MYSQL_TYPE_SET) + { + storeRC = field->store(*(int64*)bufPtr); + } + else + { + + const char* dataToStore = NULL; + uint32 bytesToStore = 0; + CHARSET_INFO* fieldCharSet = field->charset(); + switch(db2Field.getType()) + { + case QMY_CHAR: + case QMY_GRAPHIC: + { + bytesToStore = db2Field.getByteLengthInRecord(); + if (bytesToStore == 0) + bytesToStore = 1; + dataToStore = bufPtr; + } + break; + case QMY_VARCHAR: + { + bytesToStore = *(uint16*)bufPtr; + dataToStore = bufPtr+sizeof(uint16); + } + break; + case QMY_VARGRAPHIC: + { + /* For VARGRAPHIC, convert the number of double-byte characters + to the number of bytes. */ + bytesToStore = (*(uint16*)bufPtr)*2; + dataToStore = bufPtr+sizeof(uint16); + } + break; + case QMY_DBCLOB: + case QMY_BLOBCLOB: + { + DB2LobField* lobField = (DB2LobField* )(bufPtr + db2Field.calcBlobPad()); + bytesToStore = lobField->length * (db2Field.getType() == QMY_DBCLOB ? 2 : 1); + dataToStore = (char*)blobReadBuffers->getBufferPtr(field->field_index); + } + break; + + } + + if ((fieldCharSet != &my_charset_bin) && // not binary & + (db2Field.getCCSID() != 13488) && // not UCS2 & + (db2Field.getCCSID() != 1208)) + { + char* temp; + size_t db2BytesToStore; + int rc; + if (fieldCharSet->mbmaxlen > 1) + { + size_t maxDb2BytesToStore = ((bytesToStore / 2) * fieldCharSet->mbmaxlen); // Worst case for number of bytes + temp = getCharacterConversionBuffer(field->field_index, maxDb2BytesToStore); + rc = convertFieldChars(toMySQL, field->field_index, dataToStore, temp, bytesToStore, maxDb2BytesToStore, &db2BytesToStore); + bytesToStore = db2BytesToStore; + } + else // single-byte ASCII to EBCDIC + { + temp = getCharacterConversionBuffer(field->field_index, bytesToStore); + rc = convertFieldChars(toMySQL, field->field_index, dataToStore, temp, bytesToStore, bytesToStore, NULL); + } + if (rc) + return (rc); + dataToStore = temp; + } + + if ((field)->flags & BLOB_FLAG) + ((Field_blob*)(field))->set_ptr(bytesToStore, (uchar*)dataToStore); + else + storeRC = field->store(dataToStore, bytesToStore, &my_charset_bin); + } + } + break; + default: + DBUG_ASSERT(0); + break; + + } + + if (storeRC) + { + invalidDataFound = true; + } + + return 0; +} diff --git a/storage/ibmdb2i/db2i_errors.cc b/storage/ibmdb2i/db2i_errors.cc new file mode 100644 index 00000000000..43dd539447f --- /dev/null +++ b/storage/ibmdb2i/db2i_errors.cc @@ -0,0 +1,297 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#include "db2i_errors.h" +#include "db2i_ileBridge.h" +#include "db2i_charsetSupport.h" +#include "mysql_priv.h" +#include "stdarg.h" + +#define MAX_MSGSTRING 109 + +/* + The following strings are associated with errors that can be produced + within the storage engine proper. +*/ +static const char* engineErrors[MAX_MSGSTRING] = +{ + {""}, + {"Error opening codeset conversion from %.64s to %.64s (errno = %d)"}, + {"Invalid %-.10s name '%-.128s'"}, + {"Unsupported move from '%-.128s' to '%-.128s' on RENAME TABLE statement"}, + {"Unsupported schema '%-.128s' specified on RENAME TABLE statement"}, + {"Auto_increment is not allowed for a partitioned table"}, + {"Character set conversion error due to unknown encoding scheme %d"}, + {""}, + {"Table '%-.128s' was not found by the storage engine"}, + {"Could not resolve to %-.128s in library %-.10s type %-.10s (errno = %d)"}, + {"Error on _PGMCALL for program %-.10s in library %-.10s (error = %d)"}, + {"Error on _ILECALL for API '%.128s' (error = %d)"}, + {"Error in iconv() function during character set conversion (errno = %d)"}, + {"Error from Get Encoding Scheme (QTQGESP) API: %d, %d, %d"}, + {"Error from Get Related Default CCSID (QTQGRDC) API: %d, %d, %d"}, + {"Data out of range for column '%.192s'"}, + {"Schema name '%.128s' exceeds maximum length of %d characters"}, + {"Multiple collations not supported in a single index or constraint"}, + {"Sort sequence was not found"}, + {"One or more characters in column %.128s were substituted during conversion"}, + {"A decimal column exceeded the maximum precision. Data may be truncated."}, + {"Some data returned by DB2 for table %s could not be converted for MySQL"}, + {""}, + {"Column %.128s contains characters that cannot be converted"}, + {"An invalid name was specified for ibmdb2i_rdb_name."}, + {"A duplicate key was encountered for index '%.128s'"}, + {"A table with the same name exists but has incompatible column definitions."}, + {"The created table was discovered as an existing DB2 object."}, + {"Some attribute(s) defined for column '%.128s' may not be honored by accesses from DB2."}, +}; + +/* + The following strings are associated with errors that can be returned + by the operating system via the QMY_* APIs. Most are very uncommon and + indicate a bug somewhere. +*/ +static const char* systemErrors[MAX_MSGSTRING] = +{ + {"Thread ID is too long"}, + {"Error creating a SPACE memory object"}, + {"Error creating a FILE memory object"}, + {"Error creating a SPACE synchronization token"}, + {"Error creating a FILE synchronization token"}, + {"See message %-.7s in joblog for job %-.6s/%-.10s/%-.10s."}, + {"Error unlocking a synchronization token when closing a connection"}, + {"Invalid action specified for an 'object lock' request"}, + {"Invalid action specified for a savepoint request"}, + {"Partial keys are not supported with an ICU sort sequence"}, + {"Error retrieving an ICU sort key"}, + {"Error converting single-byte sort sequence to UCS-2"}, + {"An unsupported collation was specified"}, + {"Validation failed for referenced table of foreign key constraint"}, + {"Error extracting table for constraint information"}, + {"Error extracting referenced table for constraint information"}, + {"Invalid action specified for a 'commitment control' request"}, + {"Invalid commitment control isolation level specified on 'open' request"}, + {"Invalid file handle"}, + {" "}, + {"Invalid option specified for returning data on 'read' request"}, + {"Invalid orientation specified for 'read' request"}, + {"Invalid option type specified for 'read' request"}, + {"Invalid isolation level for starting commitment control"}, + {"Error unlocking a synchronization token in module QMYALC"}, + {"Length of space for returned format is not long enough"}, + {"SQL XA transactions are currently unsupported by this interface"}, + {"The associated QSQSRVR job was killed or ended unexpectedly."}, + {"Error unlocking a synchronization token in module QMYSEI"}, + {"Error unlocking a synchronization token in module QMYSPO"}, + {"Error converting input CCSID from short form to long form"}, + {" "}, + {"Error getting associated CCSID for CCSID conversion"}, + {"Error converting a string from one CCSID to another"}, + {"Error unlocking a synchronization token"}, + {"Error destroying a synchronization token"}, + {"Error locking a synchronization token"}, + {"Error recreating a synchronization token"}, + {"A space handle was not specified for a constraint request"}, + {"An SQL cursor was specified for a delete request"}, + {" "}, + {"Error on delete request because current UFCB for connection is not open"}, + {"An SQL cursor was specified for an object initialization request"}, + {"An SQL cursor was specified for an object override request"}, + {"A space handle was not specified for an object override request"}, + {"An SQL cursor was specified for an information request"}, + {"An SQL cursor was specified for an object lock request"}, + {"An SQL cursor was specified for an optimize request"}, + {"A data handle was not specified for a read request"}, + {"A row number handle was not specified for a read request"}, + {"A key handle was not specified for a read request"}, + {"An SQL cursor was specified for an row estimation request"}, + {"A space handle was not specified for a row estimation request"}, + {"An SQL cursor was specified for a release record request"}, + {"A statement handle was not specified for an 'execute immediate' request"}, + {"A statement handle was not specified for a 'prepare open' request"}, + {"An SQL cursor was specified for an update request"}, + {"The UFCB was not open for read"}, + {"Error on update request because current UFCB for connection is not open"}, + {"A data handle was not specified for an update request"}, + {"An SQL cursor was specified for a write request"}, + {"A data handle was not specified for a write request"}, + {"An unknown function was specified on a process request"}, + {"A share definition was not specified for an 'allocate share' request"}, + {"A share handle was not specified for an 'allocate share' request"}, + {"A use count handle was not specified for an 'allocate share' request"}, + {"A 'records per key' handle was not specified for an information request"}, + {"Error resolving LOB addresss"}, + {"Length of a LOB space is too small"}, + {"An unknown function was specified for a server request"}, + {"Object authorization failed. See message %-.7s in joblog for job %-.6s/%-.10s/%-.10s. for more information."}, + {" "}, + {"Error locking mutex on server"}, + {"Error unlocking mutex on server"}, + {"Error checking for RDB name in RDB Directory"}, + {"Error creating mutex on server"}, + {"A table with that name already exists"}, + {" "}, + {"Error unlocking mutex"}, + {"Error connecting to server job"}, + {"Error connecting to server job"}, + {" "}, + {"Function check occurred while registering parameter spaces. See joblog."}, + {" "}, + {" "}, + {"End of block"}, + {"The file has changed and might not be compatible with the MySQL table definition"}, + {"Error giving pipe to server job"}, + {"There are open object locks when attempting to deallocate"}, + {"There is no open lock"}, + {" "}, + {" "}, + {"The maximum value for the auto_increment data type was exceeded"}, + {"Error occurred closing the pipe "}, + {"Error occurred taking a descriptor for the pipe"}, + {"Error writing to pipe "}, + {"Server was interrupted "}, + {"No pipe descriptor exists for reuse "}, + {"Error occurred during an SQL prepare statement "}, + {"Error occurred during an SQL open "}, + {" "}, + {" "}, + {" "}, + {" "}, + {" "}, + {" "}, + {"An unspecified error was returned from the system."}, + {" "} +}; + +/** + This function builds the text string for an error code, and substitutes + a variable number of replacement variables into the string. +*/ +void getErrTxt(int errCode, ...) +{ + va_list args; + va_start(args,errCode); + char* buffer = db2i_ileBridge::getBridgeForThread()->getErrorStorage(); + const char* msg; + + if (errCode >= QMY_ERR_MIN && errCode <= QMY_ERR_SQ_OPEN) + msg = systemErrors[errCode - QMY_ERR_MIN]; + else + { + DBUG_ASSERT(errCode >= DB2I_FIRST_ERR && errCode <= DB2I_LAST_ERR); + msg = engineErrors[errCode - DB2I_FIRST_ERR]; + } + + (void) my_vsnprintf (buffer, MYSQL_ERRMSG_SIZE, msg, args); + va_end(args); + fprintf(stderr,"ibmdb2i error %d: %s\n",errCode,buffer); + DBUG_PRINT("error", ("ibmdb2i error %d: %s",errCode,buffer)); +} + +static inline void trimSpace(char* str) +{ + char* end = strchr(str, ' '); + if (end) *end = 0; +} + + +/** + Generate the error text specific to an API error returned by a QMY_* API. + + @parm errCode The error value + @parm errInfo The structure containing the message and job identifiers. +*/ +void reportSystemAPIError(int errCode, const Qmy_Error_output *errInfo) +{ + if (errCode >= QMY_ERR_MIN && errCode <= QMY_ERR_SQ_OPEN) + { + switch(errCode) + { + case QMY_ERR_MSGID: + case QMY_ERR_NOT_AUTH: + { + DBUG_ASSERT(errInfo); + char jMsg[8]; // Error message ID + char jName[11]; // Job name + char jUser[11]; // Job user + char jNbr[7]; // Job number + memset(jMsg, 0, sizeof(jMsg)); + memset(jName, 0, sizeof(jMsg)); + memset(jUser, 0, sizeof(jMsg)); + memset(jMsg, 0, sizeof(jMsg)); + + convFromEbcdic(errInfo->MsgId,jMsg,sizeof(jMsg)-1); + convFromEbcdic(errInfo->JobName,jName,sizeof(jName)-1); + trimSpace(jName); + convFromEbcdic(errInfo->JobUser,jUser,sizeof(jUser)-1); + trimSpace(jUser); + convFromEbcdic(errInfo->JobNbr,jNbr,sizeof(jNbr)-1); + getErrTxt(errCode,jMsg,jNbr,jUser,jName); + } + break; + case QMY_ERR_RTNFMT: + { + getErrTxt(QMY_ERR_LVLID_MISMATCH); + } + break; + default: + getErrTxt(errCode); + break; + } + } +} + + +/** + Generate a warning for the specified error. +*/ +void warning(THD *thd, int errCode, ...) +{ + va_list args; + va_start(args,errCode); + char buffer[MYSQL_ERRMSG_SIZE]; + const char* msg; + + DBUG_ASSERT(errCode >= DB2I_FIRST_ERR && errCode <= DB2I_LAST_ERR); + msg = engineErrors[errCode - DB2I_FIRST_ERR]; + + (void) my_vsnprintf (buffer, MYSQL_ERRMSG_SIZE, msg, args); + va_end(args); + DBUG_PRINT("warning", ("ibmdb2i warning %d: %s",errCode,buffer)); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, errCode, buffer); +} + + diff --git a/storage/ibmdb2i/db2i_errors.h b/storage/ibmdb2i/db2i_errors.h new file mode 100644 index 00000000000..0f6fbef33f6 --- /dev/null +++ b/storage/ibmdb2i/db2i_errors.h @@ -0,0 +1,93 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#ifndef DB2I_ERRORS_H +#define DB2I_ERRORS_H + +#include "qmyse.h" +class THD; + +/** + @enum DB2I_errors + + @brief These are the errors that can be returned by the storage engine proper + and that are specific to the engine. Refer to db2i_errors.cc for text + descriptions of the errors. +*/ + +enum DB2I_errors +{ + DB2I_FIRST_ERR = 2500, + DB2I_ERR_ICONV_OPEN, + DB2I_ERR_INVALID_NAME, + DB2I_ERR_RENAME_MOVE, + DB2I_ERR_RENAME_QTEMP, + DB2I_ERR_PART_AUTOINC, + DB2I_ERR_UNKNOWN_ENCODING, + DB2I_ERR_RESERVED, + DB2I_ERR_TABLE_NOT_FOUND, + DB2I_ERR_RESOLVE_OBJ, + DB2I_ERR_PGMCALL, + DB2I_ERR_ILECALL, + DB2I_ERR_ICONV, + DB2I_ERR_QTQGESP, + DB2I_ERR_QTQGRDC, + DB2I_ERR_INVALID_COL_VALUE, + DB2I_ERR_TOO_LONG_SCHEMA, + DB2I_ERR_MIXED_COLLATIONS, + DB2I_ERR_SRTSEQ, + DB2I_ERR_SUB_CHARS, + DB2I_ERR_PRECISION, + DB2I_ERR_INVALID_DATA, + DB2I_ERR_RESERVED2, + DB2I_ERR_ILL_CHAR, + DB2I_ERR_BAD_RDB_NAME, + DB2I_ERR_UNKNOWN_IDX, + DB2I_ERR_DISCOVERY_MISMATCH, + DB2I_ERR_WARN_CREATE_DISCOVER, + DB2I_ERR_WARN_COL_ATTRS, + DB2I_LAST_ERR = DB2I_ERR_WARN_COL_ATTRS +}; + +void getErrTxt(int errcode, ...); +void reportSystemAPIError(int errCode, const Qmy_Error_output *errInfo); +void warning(THD *thd, int errCode, ...); + +const char* DB2I_SQL0350 = "\xE2\xD8\xD3\xF0\xF3\xF5\xF0"; // SQL0350 in EBCDIC +const char* DB2I_CPF503A = "\xC3\xD7\xC6\xF5\xF0\xF3\xC1"; // CPF503A in EBCDIC +const char* DB2I_SQL0538 = "\xE2\xD8\xD3\xF0\xF5\xF3\xF8"; // SQL0538 in EBCDIC + +#endif diff --git a/storage/ibmdb2i/db2i_file.cc b/storage/ibmdb2i/db2i_file.cc new file mode 100644 index 00000000000..a16aa927527 --- /dev/null +++ b/storage/ibmdb2i/db2i_file.cc @@ -0,0 +1,556 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + + +#include "db2i_file.h" +#include "db2i_charsetSupport.h" +#include "db2i_collationSupport.h" +#include "db2i_misc.h" +#include "db2i_errors.h" +#include "my_dir.h" + +db2i_table::db2i_table(const TABLE_SHARE* myTable, const char* path) : + mysqlTable(myTable), + db2StartId(0), + blobFieldCount(0), + blobFields(NULL), + blobFieldActualSizes(NULL), + logicalFiles(NULL), + physicalFile(NULL), + db2TableNameSQLAscii(NULL), + db2LibNameSQLAscii(NULL) +{ + char asciiLibName[MAX_DB2_SCHEMANAME_LENGTH + 1]; + getDB2LibNameFromPath(path, asciiLibName, ASCII_NATIVE); + + char asciiFileName[MAX_DB2_FILENAME_LENGTH + 1]; + getDB2FileNameFromPath(path, asciiFileName, ASCII_NATIVE); + + size_t libNameLen = strlen(asciiLibName); + size_t fileNameLen = strlen(asciiFileName); + + db2LibNameEbcdic=(char *) + my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + &db2LibNameEbcdic, libNameLen+1, + &db2LibNameAscii, libNameLen+1, + &db2LibNameSQLAscii, libNameLen*2 + 1, + &db2TableNameEbcdic, fileNameLen+1, + &db2TableNameAscii, fileNameLen+1, + &db2TableNameSQLAscii, fileNameLen*2 + 1, + NullS); + + if (likely(db2LibNameEbcdic)) + { + memcpy(db2LibNameAscii, asciiLibName, libNameLen); + convertNativeToSQLName(db2LibNameAscii, db2LibNameSQLAscii); + convToEbcdic(db2LibNameAscii, db2LibNameEbcdic, libNameLen); + memcpy(db2TableNameAscii, asciiFileName, fileNameLen); + convertNativeToSQLName(db2TableNameAscii, db2TableNameSQLAscii); + convToEbcdic(db2TableNameAscii, db2TableNameEbcdic, fileNameLen); + } + + conversionDefinitions[toMySQL] = NULL; + conversionDefinitions[toDB2] = NULL; + + isTemporaryTable = (strstr(mysqlTable->path.str, mysql_tmpdir) == mysqlTable->path.str); +} + + +int32 db2i_table::initDB2Objects(const char* path) +{ + uint fileObjects = 1 + mysqlTable->keys; + ValidatedPointer<ShrDef> fileDefnSpace(sizeof(ShrDef) * fileObjects); + + physicalFile = new db2i_file(this); + physicalFile->fillILEDefn(&fileDefnSpace[0], true); + + logicalFileCount = mysqlTable->keys; + if (logicalFileCount > 0) + { + logicalFiles = new db2i_file*[logicalFileCount]; + for (int k = 0; k < logicalFileCount; k++) + { + logicalFiles[k] = new db2i_file(this, k); + logicalFiles[k]->fillILEDefn(&fileDefnSpace[k+1], false); + } + } + + ValidatedPointer<FILE_HANDLE> fileDefnHandles(sizeof(FILE_HANDLE) * fileObjects); + size_t formatSpaceLen = sizeof(format_hdr_t) + mysqlTable->fields * sizeof(DB2Field); + formatSpace.alloc(formatSpaceLen); + + int rc = db2i_ileBridge::getBridgeForThread()-> + expectErrors(QMY_ERR_RTNFMT)-> + allocateFileDefn(fileDefnSpace, + fileDefnHandles, + fileObjects, + db2LibNameEbcdic, + strlen(db2LibNameEbcdic), + formatSpace, + formatSpaceLen); + + if (rc) + { + // We have to handle a format space error as a special case of a FID + // mismatch. We should only get the space error if columns have been added + // to the DB2 table without MySQL's knowledge, which is effectively a + // FID problem. + if (rc == QMY_ERR_RTNFMT) + { + rc = QMY_ERR_LVLID_MISMATCH; + getErrTxt(rc); + } + return rc; + } + + convFromEbcdic(((format_hdr_t*)formatSpace)->FilLvlId, fileLevelID, sizeof(fileLevelID)); + + if (!doFileIDsMatch(path)) + { + getErrTxt(QMY_ERR_LVLID_MISMATCH); + return QMY_ERR_LVLID_MISMATCH; + } + + physicalFile->setMasterDefnHandle(fileDefnHandles[0]); + for (int k = 0; k < mysqlTable->keys; k++) + { + logicalFiles[k]->setMasterDefnHandle(fileDefnHandles[k+1]); + } + + db2StartId = (uint64)(((format_hdr_t*)formatSpace)->StartIdVal); + db2Fields = (DB2Field*)((char*)(void*)formatSpace + ((format_hdr_t*)formatSpace)->ColDefOff); + + uint fields = mysqlTable->fields; + for (int i = 0; i < fields; ++i) + { + if (db2Field(i).isBlob()) + { + blobFieldCount++; + } + } + + if (blobFieldCount) + { + blobFieldActualSizes = (uint*)my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + &blobFieldActualSizes, blobFieldCount * sizeof(uint), + &blobFields, blobFieldCount * sizeof(uint16), + NullS); + + int b = 0; + for (int i = 0; i < fields; ++i) + { + if (db2Field(i).isBlob()) + { + blobFields[b++] = i; + } + } + } + + my_multi_malloc(MYF(MY_WME), + &conversionDefinitions[toMySQL], fields * sizeof(iconv_t), + &conversionDefinitions[toDB2], fields * sizeof(iconv_t), + NullS); + for (int i = 0; i < fields; ++i) + { + conversionDefinitions[toMySQL][i] = (iconv_t)(-1); + conversionDefinitions[toDB2][i] = (iconv_t)(-1); + } + + return 0; +} + +int db2i_table::fastInitForCreate(const char* path) +{ + ValidatedPointer<ShrDef> fileDefnSpace(sizeof(ShrDef)); + + physicalFile = new db2i_file(this); + physicalFile->fillILEDefn(fileDefnSpace, true); + + ValidatedPointer<FILE_HANDLE> fileDefnHandles(sizeof(FILE_HANDLE)); + + size_t formatSpaceLen = sizeof(format_hdr_t) + + mysqlTable->fields * sizeof(DB2Field); + formatSpace.alloc(formatSpaceLen); + + int rc = db2i_ileBridge::getBridgeForThread()->allocateFileDefn(fileDefnSpace, + fileDefnHandles, + 1, + db2LibNameEbcdic, + strlen(db2LibNameEbcdic), + formatSpace, + formatSpaceLen); + + if (rc) + return rc; + + convFromEbcdic(((format_hdr_t*)formatSpace)->FilLvlId, fileLevelID, sizeof(fileLevelID)); + doFileIDsMatch(path); + + return 0; +} + +bool db2i_table::doFileIDsMatch(const char* path) +{ + char name_buff[FN_REFLEN]; + + fn_format(name_buff, path, "", FID_EXT, (MY_REPLACE_EXT | MY_UNPACK_FILENAME)); + + File fd = my_open(name_buff, O_RDONLY, MYF(0)); + + if (fd == -1) + { + if (errno == ENOENT) + { + fd = my_create(name_buff, 0, O_WRONLY, MYF(MY_WME)); + + if (fd == -1) + { + // TODO: Report errno here + return false; + } + my_write(fd, (uchar*)fileLevelID, sizeof(fileLevelID), MYF(MY_WME)); + my_close(fd, MYF(0)); + return true; + } + else + { + // TODO: Report errno here + return false; + } + } + + char diskFID[sizeof(fileLevelID)]; + + bool match = false; + + if (my_read(fd, (uchar*)diskFID, sizeof(diskFID), MYF(MY_WME)) == sizeof(diskFID) && + (memcmp(diskFID, fileLevelID, sizeof(diskFID)) == 0)) + match = true; + + my_close(fd, MYF(0)); + + return match; +} + +void db2i_table::deleteAssocFiles(const char* name) +{ + char name_buff[FN_REFLEN]; + fn_format(name_buff, name, "", FID_EXT, (MY_REPLACE_EXT | MY_UNPACK_FILENAME)); + my_delete(name_buff, MYF(0)); +} + +void db2i_table::renameAssocFiles(const char* from, const char* to) +{ + rename_file_ext(from, to, FID_EXT); +} + + +db2i_table::~db2i_table() +{ + if (blobFieldActualSizes) + my_free(blobFieldActualSizes, MYF(0)); + + if (conversionDefinitions[toMySQL]) + my_free(conversionDefinitions[toMySQL], MYF(0)); + + if (logicalFiles) + { + for (int k = 0; k < logicalFileCount; ++k) + { + delete logicalFiles[k]; + } + + delete[] logicalFiles; + } + delete physicalFile; + + my_free(db2LibNameEbcdic, 0); +} + +void db2i_table::getDB2QualifiedName(char* to) +{ + strcat(to, getDB2LibName(ASCII_SQL)); + strcat(to, "."); + strcat(to, getDB2TableName(ASCII_SQL)); +} + + +void db2i_table::getDB2QualifiedNameFromPath(const char* path, char* to) +{ + getDB2LibNameFromPath(path, to); + strcat(to, "."); + getDB2FileNameFromPath(path, strend(to)); +} + + +size_t db2i_table::smartFilenameToTableName(const char *in, char* out, size_t outlen) +{ + if (strchr(in, '@') == NULL) + { + return filename_to_tablename(in, out, outlen); + } + + char* test = (char*) my_malloc(outlen, MYF(MY_WME)); + + filename_to_tablename(in, test, outlen); + + char* cur = test; + + while (*cur) + { + if ((*cur <= 0x20) || (*cur >= 0x80)) + { + strncpy(out, in, outlen); + my_free(test, MYF(0)); + return min(outlen, strlen(out)); + } + ++cur; + } + + strncpy(out, test, outlen); + my_free(test, MYF(0)); + return min(outlen, strlen(out)); +} + +void db2i_table::filenameToTablename(const char* in, char* out, size_t outlen) +{ + if (strchr(in, '#') == NULL) + { + smartFilenameToTableName(in, out, outlen); + return; + } + + char* temp = (char*)sql_alloc(outlen); + + const char* part1, *part2, *part3, *part4; + part1 = in; + part2 = strstr(part1, "#P#"); + if (part2); + { + part3 = part2 + 3; + part4 = strchr(part3, '#'); + if (!part4) + part4 = strend(in); + } + + memcpy(temp, part1, min(outlen, part2 - part1)); + temp[min(outlen-1, part2-part1)] = 0; + + int32 accumLen = smartFilenameToTableName(temp, out, outlen); + + if (part2 && (accumLen + 4 < outlen)) + { + strcat(out, "#P#"); + accumLen += 4; + + memset(temp, 0, min(outlen, part2-part1)); + memcpy(temp, part3, min(outlen, part4-part3)); + temp[min(outlen-1, part4-part3)] = 0; + + accumLen += smartFilenameToTableName(temp, strend(out), outlen-accumLen); + + if (part4 && (accumLen + (strend(in) - part4 + 1) < outlen)) + { + strcat(out, part4); + } + } +} + +void db2i_table::getDB2LibNameFromPath(const char* path, char* lib, NameFormatFlags format) +{ + if (strstr(path, mysql_tmpdir) == path) + { + strcpy(lib, DB2I_TEMP_TABLE_SCHEMA); + } + else + { + const char* c = strend(path) - 1; + while (c > path && *c != '\\' && *c != '/') + --c; + + if (c != path) + { + const char* dbEnd = c; + do { + --c; + } while (c >= path && *c != '\\' && *c != '/'); + + if (c >= path) + { + const char* dbStart = c+1; + char fileName[FN_REFLEN]; + memcpy(fileName, dbStart, dbEnd - dbStart); + fileName[dbEnd-dbStart] = 0; + + char dbName[MAX_DB2_SCHEMANAME_LENGTH+1]; + filenameToTablename(fileName, dbName , sizeof(dbName)); + + convertMySQLNameToDB2Name(dbName, lib, sizeof(dbName), true, (format==ASCII_SQL) ); + } + else + DBUG_ASSERT(0); // This should never happen! + } + } +} + +void db2i_table::getDB2FileNameFromPath(const char* path, char* file, NameFormatFlags format) +{ + const char* fileEnd = strend(path); + const char* c = fileEnd; + while (c > path && *c != '\\' && *c != '/') + --c; + + if (c != path) + { + const char* fileStart = c+1; + char fileName[FN_REFLEN]; + memcpy(fileName, fileStart, fileEnd - fileStart); + fileName[fileEnd - fileStart] = 0; + char db2Name[MAX_DB2_FILENAME_LENGTH+1]; + filenameToTablename(fileName, db2Name, sizeof(db2Name)); + convertMySQLNameToDB2Name(db2Name, file, sizeof(db2Name), true, (format==ASCII_SQL) ); + } +} + +// Generates the DB2 index name when given the MySQL index and table names. +int32 db2i_table::appendQualifiedIndexFileName(const char* indexName, + const char* tableName, + String& to, + NameFormatFlags format, + enum_DB2I_INDEX_TYPE type) +{ + char generatedName[MAX_DB2_FILENAME_LENGTH+1]; + strncpy(generatedName, indexName, DB2I_INDEX_NAME_LENGTH_TO_PRESERVE); + generatedName[DB2I_INDEX_NAME_LENGTH_TO_PRESERVE] = 0; + char* endOfGeneratedName; + + if (type == typeDefault) + { + strcat(generatedName, DB2I_DEFAULT_INDEX_NAME_DELIMITER); + endOfGeneratedName = strend(generatedName); + } + else if (type != typeNone) + { + strcat(generatedName, DB2I_ADDL_INDEX_NAME_DELIMITER); + endOfGeneratedName = strend(generatedName); + *(endOfGeneratedName-2) = char(type); + } + + uint lenWithoutFile = endOfGeneratedName - generatedName; + + char strippedTableName[MAX_DB2_FILENAME_LENGTH+1]; + if (format == ASCII_SQL) + { + strcpy(strippedTableName, tableName); + stripExtraQuotes(strippedTableName+1, sizeof(strippedTableName)); + tableName = strippedTableName; + } + + if (strlen(tableName) > (MAX_DB2_FILENAME_LENGTH-lenWithoutFile)) + return -1; + + strncat(generatedName, + tableName+1, + min(strlen(tableName), (MAX_DB2_FILENAME_LENGTH-lenWithoutFile))-2 ); + + char finalName[MAX_DB2_FILENAME_LENGTH+1]; + convertMySQLNameToDB2Name(generatedName, finalName, sizeof(finalName), true, (format==ASCII_SQL)); + to.append(finalName); + + return 0; +} + + +void db2i_table::findConversionDefinition(enum_conversionDirection direction, uint16 fieldID) +{ + getConversion(direction, + mysqlTable->field[fieldID]->charset(), + db2Field(fieldID).getCCSID(), + conversionDefinitions[direction][fieldID]); +} + + +db2i_file::db2i_file(db2i_table* table) : db2Table(table) +{ + commonCtorInit(); + + DBUG_ASSERT(table->getMySQLTable()->table_name.length <= MAX_DB2_FILENAME_LENGTH-2); + + db2FileName = (char*)table->getDB2TableName(db2i_table::EBCDIC_NATIVE); +} + +db2i_file::db2i_file(db2i_table* table, int index) : db2Table(table) +{ + commonCtorInit(); + + if ((index == table->getMySQLTable()->primary_key) && !table->isTemporary()) + { + db2FileName = (char*)table->getDB2TableName(db2i_table::EBCDIC_NATIVE); + } + else + { + // Generate the index name (in index___table form); quote and EBCDICize it. + String qualifiedPath; + qualifiedPath.length(0); + + const char* asciiFileName = table->getDB2TableName(db2i_table::ASCII_NATIVE); + + db2i_table::appendQualifiedIndexFileName(table->getMySQLTable()->key_info[index].name, + asciiFileName, + qualifiedPath, + db2i_table::ASCII_NATIVE, + typeDefault); + + db2FileName = (char*)my_malloc(qualifiedPath.length()+1, MYF(MY_WME | MY_ZEROFILL)); + convToEbcdic(qualifiedPath.ptr(), db2FileName, qualifiedPath.length()); + } +} + +void db2i_file::commonCtorInit() +{ + masterDefn = 0; + memset(&formats, 0, maxRowFormats*sizeof(RowFormat)); +} + + +void db2i_file::fillILEDefn(ShrDef* defn, bool readInArrivalSeq) +{ + defn->ObjNamLen = strlen(db2FileName); + DBUG_ASSERT(defn->ObjNamLen <= sizeof(defn->ObjNam)); + memcpy(defn->ObjNam, db2FileName, defn->ObjNamLen); + defn->ArrSeq[0] = (readInArrivalSeq ? QMY_YES : QMY_NO); +} + diff --git a/storage/ibmdb2i/db2i_file.h b/storage/ibmdb2i/db2i_file.h new file mode 100644 index 00000000000..ff35a473b05 --- /dev/null +++ b/storage/ibmdb2i/db2i_file.h @@ -0,0 +1,446 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#ifndef DB2I_FILE_H +#define DB2I_FILE_H + +#include "db2i_global.h" +#include "db2i_ileBridge.h" +#include "db2i_validatedPointer.h" +#include "my_atomic.h" +#include "db2i_iconv.h" +#include "db2i_charsetSupport.h" + +const char FID_EXT[] = ".FID"; + +class db2i_file; + +#pragma pack(1) +struct DB2LobField +{ + char reserved1; + uint32 length; + char reserved2[4]; + uint32 ordinal; + ILEMemHandle dataHandle; + char reserved3[8]; +}; +#pragma pack(pop) + +class DB2Field +{ + public: + uint16 getType() const { return *(uint16*)(&definition.ColType); } + uint16 getByteLengthInRecord() const { return definition.ColLen; } + uint16 getDataLengthInRecord() const + { + return (getType() == QMY_VARCHAR || getType() == QMY_VARGRAPHIC ? definition.ColLen - 2 : definition.ColLen); + } + uint16 getCCSID() const { return *(uint16*)(&definition.ColCCSID); } + bool isBlob() const + { + uint16 type = getType(); + return (type == QMY_BLOBCLOB || type == QMY_DBCLOB); + } + uint16 getBufferOffset() const { return definition.ColBufOff; } + uint16 calcBlobPad() const + { + DBUG_ASSERT(isBlob()); + return getByteLengthInRecord() - sizeof (DB2LobField); + } + DB2LobField* asBlobField(char* buf) const + { + DBUG_ASSERT(isBlob()); + return (DB2LobField*)(buf + getBufferOffset() + calcBlobPad()); + } + private: + col_def_t definition; +}; + + +/** + @class db2i_table + + @details + This class describes the logical SQL table provided by DB2. + It stores "table-scoped" information such as the name of the + DB2 schema, BLOB descriptions, and the corresponding MySQL table definition. + Only one instance exists per SQL table. +*/ +class db2i_table +{ + public: + enum NameFormatFlags + { + ASCII_SQL, + ASCII_NATIVE, + EBCDIC_NATIVE + }; + + db2i_table(const TABLE_SHARE* myTable, const char* path = NULL); + + ~db2i_table(); + + int32 initDB2Objects(const char* path); + + const TABLE_SHARE* getMySQLTable() const + { + return mysqlTable; + } + + uint64 getStartId() const + { + return db2StartId; + } + + void updateStartId(uint64 newStartId) + { + db2StartId = newStartId; + } + + bool hasBlobs() const + { + return (blobFieldCount > 0); + } + + uint16 getBlobCount() const + { + return blobFieldCount; + } + + uint getBlobFieldActualSize(uint fieldIndex) const + { + return blobFieldActualSizes[getBlobIdFromField(fieldIndex)]; + } + + void updateBlobFieldActualSize(uint fieldIndex, uint32 newSize) + { + // It's OK that this isn't threadsafe, since this is just an advisory + // value. If a race condition causes the lesser of two values to be stored, + // that's OK. + uint16 blobID = getBlobIdFromField(fieldIndex); + DBUG_ASSERT(blobID < blobFieldCount); + + if (blobFieldActualSizes[blobID] < newSize) + { + blobFieldActualSizes[blobID] = newSize; + } + } + + + + const char* getDB2LibName(NameFormatFlags format = EBCDIC_NATIVE) + { + switch (format) + { + case EBCDIC_NATIVE: + return db2LibNameEbcdic; break; + case ASCII_NATIVE: + return db2LibNameAscii; break; + case ASCII_SQL: + return db2LibNameSQLAscii; break; + default: + DBUG_ASSERT(0); + } + return NULL; + } + + const char* getDB2TableName(NameFormatFlags format = EBCDIC_NATIVE) const + { + switch (format) + { + case EBCDIC_NATIVE: + return db2TableNameEbcdic; break; + case ASCII_NATIVE: + return db2TableNameAscii; break; + case ASCII_SQL: + return db2TableNameAscii; break; + break; + default: + DBUG_ASSERT(0); + } + return NULL; + } + + DB2Field& db2Field(int fieldID) const { return db2Fields[fieldID]; } + DB2Field& db2Field(const Field* field) const { return db2Field(field->field_index); } + + void processFormatSpace(); + + void* getFormatSpace(size_t& spaceNeeded) + { + DBUG_ASSERT(formatSpace == NULL); + spaceNeeded = sizeof(format_hdr_t) + mysqlTable->fields * sizeof(DB2Field); + formatSpace.alloc(spaceNeeded); + return (void*)formatSpace; + } + + bool isTemporary() const + { + return isTemporaryTable; + } + + void getDB2QualifiedName(char* to); + static void getDB2LibNameFromPath(const char* path, char* lib, NameFormatFlags format=ASCII_SQL); + static void getDB2FileNameFromPath(const char* path, char* file, NameFormatFlags format=ASCII_SQL); + static void getDB2QualifiedNameFromPath(const char* path, char* to); + static int32 appendQualifiedIndexFileName(const char* indexName, + const char* tableName, + String& to, + NameFormatFlags format=ASCII_SQL, + enum_DB2I_INDEX_TYPE type=typeDefault); + + uint16 getBlobIdFromField(uint16 fieldID) const + { + for (int i = 0; i < blobFieldCount; ++i) + { + if (blobFields[i] == fieldID) + return i; + } + DBUG_ASSERT(0); + return 0; + } + + iconv_t& getConversionDefinition(enum_conversionDirection direction, + uint16 fieldID) + { + if (conversionDefinitions[direction][fieldID] == (iconv_t)(-1)) + findConversionDefinition(direction, fieldID); + + return conversionDefinitions[direction][fieldID]; + } + + const db2i_file* dataFile() const + { + return physicalFile; + } + + const db2i_file* indexFile(uint idx) const + { + return logicalFiles[idx]; + } + + const char* getFileLevelID() const + { + return fileLevelID; + } + + static void deleteAssocFiles(const char* name); + static void renameAssocFiles(const char* from, const char* to); + + int fastInitForCreate(const char* path); + int initDiscoveredTable(const char* path); + + uint16* blobFields; + +private: + + void findConversionDefinition(enum_conversionDirection direction, uint16 fieldID); + static void filenameToTablename(const char* in, char* out, size_t outlen); + static size_t smartFilenameToTableName(const char *in, char* out, size_t outlen); + void convertNativeToSQLName(const char* input, + char* output) + { + + output[0] = input[0]; + + uint o = 1; + uint i = 1; + do + { + output[o++] = input[i]; + if (input[i] == '"' && input[i+1]) + output[o++] = '"'; + } while (input[++i]); + + output[o] = 0; // This isn't the most user-friendly way to handle overflows, + // but at least its safe. + } + + bool doFileIDsMatch(const char* path); + + ValidatedPointer<format_hdr_t> formatSpace; + DB2Field* db2Fields; + uint64 db2StartId; // Starting value for identity column + uint16 blobFieldCount; // Count of LOB fields in the DB2 table + uint* blobFieldActualSizes; // Array of LOB field lengths (actual vs. allocated). + // This is updated as LOBs are read and will contain + // the length of the longest known LOB in that field. + iconv_t* conversionDefinitions[2]; + + const TABLE_SHARE* mysqlTable; + uint16 logicalFileCount; + char* db2LibNameEbcdic; // Quoted and in EBCDIC + char* db2LibNameAscii; + char* db2TableNameEbcdic; + char* db2TableNameAscii; + char* db2TableNameSQLAscii; + char* db2LibNameSQLAscii; + + db2i_file* physicalFile; + db2i_file** logicalFiles; + + bool isTemporaryTable; + char fileLevelID[13]; +}; + +/** + @class db2i_file + + @details This class describes a file object underlaying a particular SQL + table. Both "physical files" (data) and "logical files" (indices) are + described by this class. Only one instance of the class exists per DB2 file + object. The single instance is responsible for de/allocating the multiple + handles used by the handlers. +*/ +class db2i_file +{ + +public: + struct RowFormat + { + uint16 readRowLen; + uint16 readRowNullOffset; + uint16 writeRowLen; + uint16 writeRowNullOffset; + char inited; + }; + +public: + + // Construct an instance for a physical file. + db2i_file(db2i_table* table); + + // Construct an instance for a logical file. + db2i_file(db2i_table* table, int index); + + ~db2i_file() + { + if (masterDefn) + db2i_ileBridge::getBridgeForThread()->deallocateFile(masterDefn); + + if (db2FileName != (char*)db2Table->getDB2TableName(db2i_table::EBCDIC_NATIVE)) + my_free(db2FileName, MYF(0)); + } + + // This is roughly equivalent to an "open". It tells ILE to allocate a descriptor + // for the file. The associated handle is returned to the caller. + int allocateNewInstance(FILE_HANDLE* newHandle, ILEMemHandle inuseSpace) const + { + int rc; + + rc = db2i_ileBridge::getBridgeForThread()->allocateFileInstance(masterDefn, + inuseSpace, + newHandle); + + if (rc) *newHandle = 0; + + return rc; + } + + // This obtains the row layout associated with a particular access intent for + // an open instance of the file. + int obtainRowFormat(FILE_HANDLE instanceHandle, + char intent, + char commitLevel, + const RowFormat** activeFormat) const + { + DBUG_ENTER("db2i_file::obtainRowFormat"); + RowFormat* rowFormat; + + if (intent == QMY_UPDATABLE) + rowFormat = &(formats[readWrite]); + else if (intent == QMY_READ_ONLY) + rowFormat = &(formats[readOnly]); + + if (unlikely(!rowFormat->inited)) + { + int rc = db2i_ileBridge::getBridgeForThread()-> + initFileForIO(instanceHandle, + intent, + commitLevel, + &(rowFormat->writeRowLen), + &(rowFormat->writeRowNullOffset), + &(rowFormat->readRowLen), + &(rowFormat->readRowNullOffset)); + if (rc) DBUG_RETURN(rc); + rowFormat->inited = 1; + } + + *activeFormat = rowFormat; + DBUG_RETURN(0); + } + + const char* getDB2FileName() const + { + return db2FileName; + } + + void fillILEDefn(ShrDef* defn, bool readInArrivalSeq); + + void setMasterDefnHandle(FILE_HANDLE handle) + { + masterDefn = handle; + } + + FILE_HANDLE getMasterDefnHandle() const + { + return masterDefn; + } + +private: + enum RowFormats + { + readOnly = 0, + readWrite, + maxRowFormats + }; + + mutable RowFormat formats[maxRowFormats]; + + void commonCtorInit(); + + char* db2FileName; // Quoted and in EBCDIC + + db2i_table* db2Table; // The logical SQL table contained by this file. + + bool db2CanSort; + + FILE_HANDLE masterDefn; +}; + + +#endif diff --git a/storage/ibmdb2i/db2i_global.h b/storage/ibmdb2i/db2i_global.h new file mode 100644 index 00000000000..d201fbd8124 --- /dev/null +++ b/storage/ibmdb2i/db2i_global.h @@ -0,0 +1,138 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#ifndef DB2I_GLOBAL_H +#define DB2I_GLOBAL_H + +#define MYSQL_SERVER 1 + +#include "my_global.h" +#include "my_sys.h" + +const uint MAX_DB2_KEY_PARTS=120; +const int MAX_DB2_V5R4_LIBNAME_LENGTH = 10; +const int MAX_DB2_V6R1_LIBNAME_LENGTH = 30; +const int MAX_DB2_SCHEMANAME_LENGTH=258; +const int MAX_DB2_FILENAME_LENGTH=258; +const int MAX_DB2_COLNAME_LENGTH=128; +const int MAX_DB2_SAVEPOINTNAME_LENGTH=128; +const int MAX_DB2_QUALIFIEDNAME_LENGTH=MAX_DB2_V6R1_LIBNAME_LENGTH + 1 + MAX_DB2_FILENAME_LENGTH; +const uint32 MAX_CHAR_LENGTH = 32765; +const uint32 MAX_VARCHAR_LENGTH = 32739; +const uint32 MAX_DEC_PRECISION = 63; +const uint32 MAX_BLOB_LENGTH = 2147483646; +const uint32 MAX_BINARY_LENGTH = MAX_CHAR_LENGTH; +const uint32 MAX_VARBINARY_LENGTH = MAX_VARCHAR_LENGTH; +const uint32 MAX_FULL_ALLOCATE_BLOB_LENGTH = 65536; +const uint32 MAX_FOREIGN_LEN = 64000; +const char* DB2I_TEMP_TABLE_SCHEMA = "QTEMP"; +const char DB2I_ADDL_INDEX_NAME_DELIMITER[5] = {'_','_','_','_','_'}; +const char DB2I_DEFAULT_INDEX_NAME_DELIMITER[3] = {'_','_','_'}; +const int DB2I_INDEX_NAME_LENGTH_TO_PRESERVE = 110; + +enum enum_DB2I_INDEX_TYPE +{ + typeNone = 0, + typeDefault = 'D', + typeHex = 'H', + typeAscii = 'A' +}; + +void* roundToQuadWordBdy(void* ptr) +{ + return (void*)(((uint64)(ptr)+0xf) & ~0xf); +} + +typedef uint64_t ILEMemHandle; + +struct OSVersion +{ + uint8 v; + uint8 r; +}; +extern OSVersion osVersion; + + +/** + Allocate 16-byte aligned space using the MySQL heap allocator + + @details Many of the spaces used by the QMY_* APIS are required to be + aligned on 16 byte boundaries. The standard system malloc will do this + alignment by default. However, in order to use the heap debug and tracking + features of the mysql allocator, we chose to implement an aligning wrapper + around my_malloc. Essentially, we overallocate the storage space, find the + first aligned address in the space, store a pointer to the true malloc + allocation in the bytes immediately preceding the aligned address, and return + the aligned address to the caller. + + @parm size The size of heap storage needed + + @return A 16-byte aligned pointer to the storage requested. +*/ +void* malloc_aligned(size_t size) +{ + char* p; + char* base; + base = (char*)my_malloc(size + sizeof(void*) + 15, MYF(MY_WME)); + if (likely(base)) + { + p = (char*)roundToQuadWordBdy(base + sizeof(void*)); + char** p2 = (char**)(p - sizeof(void*)); + *p2 = base; + } + else + p = NULL; + + return p; +} + +/** + Free a 16-byte aligned space alloced by malloc_aligned + + @details We know that a pointer to the true malloced storage immediately + precedes the aligned address, so we pull that out and call my_free(). + + @parm p A 16-byte aligned pointer generated by malloc_aligned +*/ +void free_aligned(void* p) +{ + if (likely(p)) + { + my_free(*(char**)((char*)p-sizeof(void*)), MYF(0)); + } +} + +#endif diff --git a/storage/ibmdb2i/db2i_iconv.h b/storage/ibmdb2i/db2i_iconv.h new file mode 100644 index 00000000000..9fc6e4ed636 --- /dev/null +++ b/storage/ibmdb2i/db2i_iconv.h @@ -0,0 +1,51 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +/** + @file + + @brief Used to redefine iconv symbols to the optimized "myconv" ones +*/ + +#ifndef DB2I_ICONV_H +#define DB2I_ICONV_H + +#include "db2i_myconv.h" +#define iconv_open(A, B) myconv_open(A, B, CONVERTER_DMAP) +#define iconv_close myconv_close +#define iconv myconv_dmap +#define iconv_t myconv_t + +#endif diff --git a/storage/ibmdb2i/db2i_ileBridge.cc b/storage/ibmdb2i/db2i_ileBridge.cc new file mode 100644 index 00000000000..68ae2c2bb72 --- /dev/null +++ b/storage/ibmdb2i/db2i_ileBridge.cc @@ -0,0 +1,1342 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + + +#include "db2i_ileBridge.h" +#include "my_dbug.h" +#include "db2i_global.h" +#include "db2i_charsetSupport.h" +#include "db2i_errors.h" + + +// static class member data +ILEpointer* db2i_ileBridge::functionSymbols; +db2i_ileBridge* db2i_ileBridge::globalBridge; +#ifndef DBUG_OFF +uint32 db2i_ileBridge::registeredPtrs; +#endif + +pthread_key(IleParms*, THR_ILEPARMS); + +static void ileParmsDtor(void* parmsToFree) +{ + if (parmsToFree) + { + free_aligned(parmsToFree); + DBUG_PRINT("db2i_ileBridge", ("Freeing space for parms")); + } +} + + +/** + Convert a timestamp in ILE time format into a unix time_t +*/ +static inline time_t convertILEtime(const ILE_time_t& input) +{ + tm temp; + + temp.tm_sec = input.Second; + temp.tm_min = input.Minute; + temp.tm_hour = input.Hour; + temp.tm_mday = input.Day; + temp.tm_mon = input.Month-1; + temp.tm_year = input.Year - 1900; + temp.tm_isdst = -1; + + return mktime(&temp); +} + +/** + Allocate and intialize a new bridge structure +*/ +db2i_ileBridge* db2i_ileBridge::createNewBridge(CONNECTION_HANDLE connID) +{ + DBUG_PRINT("db2i_ileBridge::createNewBridge",("Building new bridge...")); + db2i_ileBridge* newBridge = (db2i_ileBridge*)my_malloc(sizeof(db2i_ileBridge), MYF(MY_WME)); + + if (unlikely(newBridge == NULL)) + return NULL; + + newBridge->stmtTxActive = false; + newBridge->connErrText = NULL; + newBridge->pendingLockedHandles.head = NULL; + newBridge->cachedConnectionID = connID; + + return newBridge; +} + + +void db2i_ileBridge::destroyBridge(db2i_ileBridge* bridge) +{ + bridge->freeErrorStorage(); + my_free(bridge, MYF(0)); +} + + +void db2i_ileBridge::destroyBridgeForThread(const THD* thd) +{ + void* thdData = *thd_ha_data(thd, ibmdb2i_hton); + if (thdData != NULL) + { + destroyBridge((db2i_ileBridge*)thdData); + } +} + + +void db2i_ileBridge::registerPtr(const void* ptr, ILEMemHandle* receiver) +{ + static const arg_type_t ileSignature[] = { ARG_MEMPTR, ARG_END }; + + if (unlikely(ptr == NULL)) + { + *receiver = 0; + return; + } + + struct ArgList + { + ILEarglist_base base; + ILEpointer ptr; + } *arguments; + + char argBuf[sizeof(ArgList)+15]; + arguments = (ArgList*)roundToQuadWordBdy(argBuf); + + arguments->ptr.s.addr = (address64_t)(ptr); + + _ILECALL(&functionSymbols[funcRegisterSpace], + &arguments->base, + ileSignature, + RESULT_INT64); + +#ifndef DBUG_OFF + uint32 truncHandle = arguments->base.result.r_uint64; + DBUG_PRINT("db2i_ileBridge::registerPtr",("Register 0x%p with handle %d", ptr, truncHandle)); + getBridgeForThread()->registeredPtrs++; +#endif + + *receiver = arguments->base.result.r_uint64; + return; +} + +void db2i_ileBridge::unregisterPtr(ILEMemHandle handle) +{ + static const arg_type_t ileSignature[] = { ARG_UINT64, ARG_END }; + + if (unlikely(handle == NULL)) + return; + + struct ArgList + { + ILEarglist_base base; + uint64 handle; + } *arguments; + + char argBuf[sizeof(ArgList)+15]; + arguments = (ArgList*)roundToQuadWordBdy(argBuf); + + arguments->handle = (uint64)(handle); + + _ILECALL(&functionSymbols[funcUnregisterSpace], + &arguments->base, + ileSignature, + RESULT_VOID); + +#ifndef DBUG_OFF + DBUG_PRINT("db2i_ileBridge::unregisterPtr",("Unregister handle %d", (uint32)handle)); + getBridgeForThread()->registeredPtrs--; +#endif +} + + + +/** + Initialize the bridge component + + @details Resolves srvpgm and function names of the APIs. If this fails, + the approrpiate operating system support (PTFs) is probably not installed. + + WARNING: + Must be called before any other functions in this class are used!!!! + Can only be called by a single thread! +*/ +int db2i_ileBridge::setup() +{ + static const char funcNames[db2i_ileBridge::funcListEnd][32] = + { + {"QmyRegisterParameterSpaces"}, + {"QmyRegisterSpace"}, + {"QmyUnregisterSpace"}, + {"QmyProcessRequest"} + }; + + DBUG_ENTER("db2i_ileBridge::setup"); + + int actmark = _ILELOAD("QSYS/QMYSE", ILELOAD_LIBOBJ); + if ( actmark == -1 ) + { + DBUG_PRINT("db2i_ileBridge::setup", ("srvpgm activation failed")); + DBUG_RETURN(1); + } + + functionSymbols = (ILEpointer*)malloc_aligned(sizeof(ILEpointer) * db2i_ileBridge::funcListEnd); + + for (int i = 0; i < db2i_ileBridge::funcListEnd; i++) + { + if (_ILESYM(&functionSymbols[i], actmark, funcNames[i]) == -1) + { + DBUG_PRINT("db2i_ileBridge::setup", + ("resolve of %s failed", funcNames[i])); + DBUG_RETURN(errno); + } + } + + pthread_key_create(&THR_ILEPARMS, &ileParmsDtor); + +#ifndef DBUG_OFF + registeredPtrs = 0; +#endif + + globalBridge = createNewBridge(0); + + DBUG_RETURN(0); +} + +/** + Cleanup any resources before shutting down plug-in +*/ +void db2i_ileBridge::takedown() +{ + if (globalBridge) + destroyBridge(globalBridge); + free_aligned(functionSymbols); +} + +/** + Call off to QmyProcessRequest to perform the API that the caller prepared +*/ +inline int32 db2i_ileBridge::doIt() +{ + static const arg_type_t ileSignature[] = {ARG_END}; + + struct ArgList + { + ILEarglist_base base; + } *arguments; + + char argBuf[sizeof(ArgList)+15]; + arguments = (ArgList*)roundToQuadWordBdy(argBuf); + + _ILECALL(&functionSymbols[funcProcessRequest], + &arguments->base, + ileSignature, + RESULT_INT32); + + return translateErrorCode(arguments->base.result.s_int32.r_int32); +} + +/** + Call off to QmyProcessRequest to perform the API that the caller prepared and + log any errors that may occur. +*/ +inline int32 db2i_ileBridge::doItWithLog() +{ + int32 rc = doIt(); + + if (unlikely(rc)) + { + // Only report errors that we weren't expecting + if (rc != tacitErrors[0] && + rc != tacitErrors[1] && + rc != QMY_ERR_END_OF_BLOCK) + reportSystemAPIError(rc, (Qmy_Error_output_t*)parms()->outParms); + } + memset(tacitErrors, 0, sizeof(tacitErrors)); + + return rc; +} + + +/** + Interface to QMY_ALLOCATE_SHARE API + + See QMY_ALLOCATE_SHARE documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::allocateFileDefn(ILEMemHandle definitionSpace, + ILEMemHandle handleSpace, + uint16 fileCount, + const char* schemaName, + uint16 schemaNameLength, + ILEMemHandle formatSpace, + uint32 formatSpaceLen) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + + IleParms* parmBlock = parms(); + Qmy_MAOS0100 *input = (Qmy_MAOS0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_ALLOCATE_SHARE; + input->ShrDefSpcHnd = definitionSpace; + input->ShrHndSpcHnd = handleSpace; + input->ShrDefCnt = fileCount; + input->FmtSpcHnd = formatSpace; + input->FmtSpcLen = formatSpaceLen; + + if (schemaNameLength > sizeof(input->SchNam)) + { + // This should never happen! + DBUG_ASSERT(0); + return HA_ERR_GENERIC; + } + + memcpy(input->SchNam, schemaName, schemaNameLength); + input->SchNamLen = schemaNameLength; + + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + return rc; +} + + +/** + Interface to QMY_ALLOCATE_INSTANCE API + + See QMY_ALLOCATE_INSTANCE documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::allocateFileInstance(FILE_HANDLE defnHandle, + ILEMemHandle inuseSpace, + FILE_HANDLE* instance) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + + IleParms* parmBlock = parms(); + Qmy_MAOI0100 *input = (Qmy_MAOI0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_ALLOCATE_INSTANCE; + input->ShrHnd = defnHandle; + input->CnnHnd = cachedConnectionID; + input->UseSpcHnd = inuseSpace; + + int32 rc = doItWithLog(); + + if (likely(rc == 0)) + { + Qmy_MAOI0100_output* output = (Qmy_MAOI0100_output*)parmBlock->outParms; + DBUG_ASSERT(instance); + *instance = output->ObjHnd; + } + + return rc; +} + + +/** + Interface to QMY_DEALLOCATE_OBJECT API + + See QMY_DEALLOCATE_OBJECT documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::deallocateFile(FILE_HANDLE rfileHandle, + bool postDropTable) +{ + IleParms* parmBlock = parms(); + Qmy_MDLC0100 *input = (Qmy_MDLC0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_DEALLOCATE_OBJECT; + input->ObjHnd = rfileHandle; + input->ObjDrp[0] = (postDropTable ? QMY_YES : QMY_NO); + + DBUG_PRINT("db2i_ileBridge::deallocateFile", ("Deallocating %d", (uint32)rfileHandle)); + + int32 rc = doItWithLog(); + + return rc; +} + + +/** + Interface to QMY_OBJECT_INITIALIZATION API + + See QMY_OBJECT_INITIALIZATION documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::initFileForIO(FILE_HANDLE rfileHandle, + char accessIntent, + char commitLevel, + uint16* inRecSize, + uint16* inRecNullOffset, + uint16* outRecSize, + uint16* outRecNullOffset) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MOIX0100 *input = (Qmy_MOIX0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_OBJECT_INITIALIZATION; + input->CmtLvl[0] = commitLevel; + input->Intent[0] = accessIntent; + input->ObjHnd = rfileHandle; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + if (likely(rc == 0)) + { + Qmy_MOIX0100_output* output = (Qmy_MOIX0100_output*)parmBlock->outParms; + *inRecSize = output->InNxtRowOff; + *inRecNullOffset = output->InNullMapOff; + *outRecSize = output->OutNxtRowOff; + *outRecNullOffset = output->OutNullMapOff; + } + + return rc; +} + + +/** + Interface to QMY_READ_ROWS API for reading a row with a specific RRN. + + See QMY_READ_ROWS documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::readByRRN(FILE_HANDLE rfileHandle, + ILEMemHandle buf, + uint32 inRRN, + char accessIntent, + char commitLevel) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MRDX0100 *input = (Qmy_MRDX0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_READ_ROWS; + input->CmtLvl[0] = commitLevel; + input->ObjHnd = rfileHandle; + input->Intent[0] = accessIntent; + input->OutSpcHnd = (uint64)buf; + input->RelRowNbr = inRRN; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + if (rc == QMY_ERR_END_OF_BLOCK) + { + rc = 0; + DBUG_PRINT("db2i_ileBridge::readByRRN", ("End of block signalled")); + } + + return rc; +} + + +/** + Interface to QMY_WRITE_ROWS API. + + See QMY_WRITE_ROWS documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::writeRows(FILE_HANDLE rfileHandle, + ILEMemHandle buf, + char commitLevel, + int64* outIdVal, + bool* outIdGen, + uint32* dupKeyRRN, + char** dupKeyName, + uint32* dupKeyNameLen, + uint32* outIdIncrement) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MWRT0100 *input = (Qmy_MWRT0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_WRITE_ROWS; + input->CmtLvl[0] = commitLevel; + + input->ObjHnd = rfileHandle; + input->InSpcHnd = (uint64_t) buf; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + Qmy_MWRT0100_output_t* output = (Qmy_MWRT0100_output_t*)parmBlock->outParms; + if (likely(rc == 0 || rc == HA_ERR_FOUND_DUPP_KEY)) + { + DBUG_ASSERT(dupKeyRRN && dupKeyName && dupKeyNameLen && outIdGen && outIdIncrement && outIdVal); + *dupKeyRRN = output->DupRRN; + *dupKeyName = (char*)parmBlock->outParms + output->DupObjNamOff; + *dupKeyNameLen = output->DupObjNamLen; + *outIdGen = (output->NewIdGen[0] == QMY_YES ? TRUE : FALSE); + if (*outIdGen == TRUE) + { + *outIdIncrement = output->IdIncrement; + *outIdVal = output->NewIdVal; + } + } + + return rc; + +} + +/** + Interface to QMY_EXECUTE_IMMEDIATE API. + + See QMY_EXECUTE_IMMEDIATE documentation for more information about + parameters and return codes. +*/ +uint32 db2i_ileBridge::execSQL(const char* statement, + uint32 statementCount, + uint8 commitLevel, + bool autoCreateSchema, + bool dropSchema, + bool noCommit, + FILE_HANDLE fileHandle) + +{ + IleParms* parmBlock = parms(); + Qmy_MSEI0100 *input = (Qmy_MSEI0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_EXECUTE_IMMEDIATE; + + registerPtr(statement, &input->StmtsSpcHnd); + + input->NbrStmts = statementCount; + *(uint16*)(&input->StmtCCSID) = 850; + input->AutoCrtSchema[0] = (autoCreateSchema == TRUE ? QMY_YES : QMY_NO); + input->DropSchema[0] = (dropSchema == TRUE ? QMY_YES : QMY_NO); + input->CmtLvl[0] = commitLevel; + if ((commitLevel == QMY_NONE && statementCount == 1) || noCommit) + { + input->CmtBefore[0] = QMY_NO; + input->CmtAfter[0] = QMY_NO; + } + else + { + input->CmtBefore[0] = QMY_YES; + input->CmtAfter[0] = QMY_YES; + } + input->CnnHnd = current_thd->thread_id; + input->ObjHnd = fileHandle; + + int32 rc = doItWithLog(); + + unregisterPtr(input->StmtsSpcHnd); + + return rc; +} + +/** + Interface to QMY_PREPARE_OPEN_CURSOR API. + + See QMY_PREPARE_OPEN_CURSOR documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::prepOpen(const char* statement, + FILE_HANDLE* rfileHandle, + uint32* recLength) +{ + IleParms* parmBlock = parms(); + Qmy_MSPO0100 *input = (Qmy_MSPO0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_PREPARE_OPEN_CURSOR; + + registerPtr(statement, &input->StmtsSpcHnd ); + *(uint16*)(&input->StmtCCSID) = 850; + input->CnnHnd = current_thd->thread_id; + + int32 rc = doItWithLog(); + + if (likely(rc == 0)) + { + Qmy_MSPO0100_output* output = (Qmy_MSPO0100_output*)parmBlock->outParms; + *rfileHandle = output->ObjHnd; + *recLength = max(output->InNxtRowOff, output->OutNxtRowOff); + } + + + unregisterPtr(input->StmtsSpcHnd); + + return rc; +} + + +/** + Interface to QMY_DELETE_ROW API. + + See QMY_DELETE_ROW documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::deleteRow(FILE_HANDLE rfileHandle, + uint32 rrn) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MDLT0100 *input = (Qmy_MDLT0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_DELETE_ROW; + input->ObjHnd = rfileHandle; + input->RelRowNbr = rrn; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + return rc; +} + + +/** + Interface to QMY_UPDATE_ROW API. + + See QMY_UPDATE_ROW documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::updateRow(FILE_HANDLE rfileHandle, + uint32 rrn, + ILEMemHandle buf, + uint32* dupKeyRRN, + char** dupKeyName, + uint32* dupKeyNameLen) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MUPD0100 *input = (Qmy_MUPD0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_UPDATE_ROW; + input->ObjHnd = rfileHandle; + input->InSpcHnd = (uint64)buf; + input->RelRowNbr = rrn; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + if (rc == HA_ERR_FOUND_DUPP_KEY) + { + Qmy_MUPD0100_output* output = (Qmy_MUPD0100_output*)parmBlock->outParms; + DBUG_ASSERT(dupKeyRRN && dupKeyName && dupKeyNameLen); + *dupKeyRRN = output->DupRRN; + *dupKeyName = (char*)parmBlock->outParms + output->DupObjNamOff; + *dupKeyNameLen = output->DupObjNamLen; + } + + return rc; +} + +/** + Interface to QMY_DESCRIBE_RANGE API. + + See QMY_DESCRIBE_RANGE documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::recordsInRange(FILE_HANDLE defnHandle, + ILEMemHandle inSpc, + uint32 inKeyCnt, + uint32 inLiteralCnt, + uint32 inBoundsOff, + uint32 inLitDefOff, + uint32 inLiteralsOff, + uint32 inCutoff, + uint32 inSpcLen, + uint16 inEndByte, + uint64* outRecCnt, + uint16* outRtnCode) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + + IleParms* parmBlock = parms(); + Qmy_MDRG0100 *input = (Qmy_MDRG0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_DESCRIBE_RANGE; + input->ShrHnd = defnHandle; + input->SpcHnd = (uint64)inSpc; + input->KeyCnt = inKeyCnt; + input->LiteralCnt = inLiteralCnt; + input->BoundsOff = inBoundsOff; + input->LitDefOff = inLitDefOff; + input->LiteralsOff = inLiteralsOff; + input->Cutoff = inCutoff; + input->SpcLen = inSpcLen; + input->EndByte = inEndByte; + input->CnnHnd = cachedConnectionID; + + int rc = doItWithLog(); + + if (likely(rc == 0)) + { + Qmy_MDRG0100_output* output = (Qmy_MDRG0100_output*)parmBlock->outParms; + DBUG_ASSERT(outRecCnt && outRtnCode); + *outRecCnt = output->RecCnt; + *outRtnCode = output->RtnCode; + } + + return rc; +} + + +/** + Interface to QMY_RELEASE_ROW API. + + See QMY_RELEASE_ROW documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::rrlslck(FILE_HANDLE rfileHandle, char accessIntent) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + + IleParms* parmBlock = parms(); + Qmy_MRRX0100 *input = (Qmy_MRRX0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_RELEASE_ROW; + + input->ObjHnd = rfileHandle; + input->CnnHnd = cachedConnectionID; + input->Intent[0] = accessIntent; + + int32 rc = doItWithLog(); + + return rc; +} + +/** + Interface to QMY_LOCK_OBJECT API. + + See QMY_LOCK_OBJECT documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::lockObj(FILE_HANDLE defnHandle, + uint64 lockVal, + char lockAction, + char lockType, + char lockTimeout) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MOLX0100 *input = (Qmy_MOLX0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_LOCK_OBJECT; + input->ShrHnd = defnHandle; + input->LckTimeoutVal = lockVal; + input->Action[0] = lockAction; + input->LckTyp[0] = lockType; + input->LckTimeout[0] = lockTimeout; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + return rc; +} + +/** + Interface to QMY_DESCRIBE_CONSTRAINTS API. + + See QMY_DESCRIBE_CONSTRAINTS documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::constraints(FILE_HANDLE defnHandle, + ILEMemHandle inSpc, + uint32 inSpcLen, + uint32* outLen, + uint32* outCnt) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MDCT0100 *input = (Qmy_MDCT0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_DESCRIBE_CONSTRAINTS; + input->ShrHnd = defnHandle; + input->CstSpcHnd = (uint64)inSpc; + input->CstSpcLen = inSpcLen; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + if (likely(rc == 0)) + { + Qmy_MDCT0100_output* output = (Qmy_MDCT0100_output*)parmBlock->outParms; + DBUG_ASSERT(outLen && outCnt); + *outLen = output->NeededLen; + *outCnt = output->CstCnt; + } + + return rc; +} + + +/** + Interface to QMY_REORGANIZE_TABLE API. + + See QMY_REORGANIZE_TABLE documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::optimizeTable(FILE_HANDLE defnHandle) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MRGX0100 *input = (Qmy_MRGX0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_REORGANIZE_TABLE; + input->ShrHnd = defnHandle; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + return rc; +} + + +/** + Interface to QMY_PROCESS_COMMITMENT_CONTROL API. + + See QMY_PROCESS_COMMITMENT_CONTROL documentation for more information about + parameters and return codes. +*/ +int32 db2i_ileBridge::commitmentControl(uint8 function) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MCCX0100 *input = (Qmy_MCCX0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_PROCESS_COMMITMENT_CONTROL; + input->Function[0] = function; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + return rc; +} + + +/** + Interface to QMY_PROCESS_SAVEPOINT API. + + See QMY_PROCESS_SAVEPOINT documentation for more information about parameters and + return codes. +*/ +int32 db2i_ileBridge::savepoint(uint8 function, + const char* savepointName) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + DBUG_PRINT("db2i_ileBridge::savepoint",("%d %s", (uint32)function, savepointName)); + + IleParms* parmBlock = parms(); + Qmy_MSPX0100 *input = (Qmy_MSPX0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + char* savPtNam = (char*)(input+1); + + input->Format = QMY_PROCESS_SAVEPOINT; + + if (strlen(savepointName) > MAX_DB2_SAVEPOINTNAME_LENGTH) + { + DBUG_ASSERT(0); + return HA_ERR_GENERIC; + } + strcpy(savPtNam, savepointName); + + input->Function[0] = function; + input->SavPtNamOff = savPtNam - (char*)(input); + input->SavPtNamLen = strlen(savepointName); + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + return rc; +} + +static ILEMemHandle traceSpcHandle; +/** + Do initialization for the QMY_* APIs. + + @parm aspName The name of the relational database to use for all + connections. + + @return 0 if successful; error otherwise +*/ +int32 db2i_ileBridge::initILE(const char* aspName, + uint16* traceCtlPtr) +{ + // We forego the typical thread-based parms space because MySQL doesn't + // allow us to clean it up before checking for memory leaks. As a result + // we get a complaint about leaked memory on server shutdown. + int32 rc; + char inParms[db2i_ileBridge_MAX_INPARM_SIZE]; + char outParms[db2i_ileBridge_MAX_OUTPARM_SIZE]; + if (rc = registerParmSpace(inParms, outParms)) + { + reportSystemAPIError(rc, NULL); + return rc; + } + + registerPtr(traceCtlPtr, &traceSpcHandle); + + struct ParmBlock + { + Qmy_MINI0100 parms; + } *parmBlock = (ParmBlock*)inParms; + + memset(inParms, 0, sizeof(ParmBlock)); + + parmBlock->parms.Format = QMY_INITIALIZATION; + + char paddedName[18]; + if (strlen(aspName) > sizeof(paddedName)) + { + getErrTxt(DB2I_ERR_BAD_RDB_NAME); + return DB2I_ERR_BAD_RDB_NAME; + } + + memset(paddedName, ' ', sizeof(paddedName)); + memcpy(paddedName, aspName, strlen(aspName)); + convToEbcdic(paddedName, parmBlock->parms.RDBName, strlen(paddedName)); + + parmBlock->parms.RDBNamLen = strlen(paddedName); + parmBlock->parms.TrcSpcHnd = traceSpcHandle; + + rc = doIt(); + + if (rc) + { + reportSystemAPIError(rc, (Qmy_Error_output_t*)outParms); + } + + return rc; +} + +/** + Signal to the QMY_ APIs to perform any cleanup they need to do. +*/ +int32 db2i_ileBridge::exitILE() +{ + IleParms* parmBlock = parms(); + Qmy_MCLN0100 *input = (Qmy_MCLN0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_CLEANUP; + + int32 rc = doIt(); + + if (rc) + { + reportSystemAPIError(rc, (Qmy_Error_output_t*)parmBlock->outParms); + } + + unregisterPtr(traceSpcHandle); + + DBUG_PRINT("db2i_ileBridge::exitILE", ("Registered ptrs remaining: %d", registeredPtrs)); +#ifndef DBUG_OFF + if (registeredPtrs != 0) + printf("Oh no! IBMDB2I left some pointers registered. Count was %d.\n", registeredPtrs); +#endif + + // This is needed to prevent SAFE_MALLOC from complaining at process termination. + my_pthread_setspecific_ptr(THR_ILEPARMS, NULL); + free_aligned(parmBlock); + + return rc; + +} + + +/** + Designate the specified addresses as parameter passing buffers. + + @parm in Input to the API will go here; format is defined by the individual API + @parm out Output from the API will be; format is defined by the individual API + + @return 0 if success; error otherwise +*/ +int db2i_ileBridge::registerParmSpace(char* in, char* out) +{ + static const arg_type_t ileSignature[] = { ARG_MEMPTR, ARG_MEMPTR, ARG_END }; + + struct ArgList + { + ILEarglist_base base; + ILEpointer input; + ILEpointer output; + } *arguments; + + char argBuf[sizeof(ArgList)+15]; + arguments = (ArgList*)roundToQuadWordBdy(argBuf); + + arguments->input.s.addr = (address64_t)(in); + arguments->output.s.addr = (address64_t)(out); + + _ILECALL(&functionSymbols[funcRegisterParameterSpaces], + &arguments->base, + ileSignature, + RESULT_INT32); + + return arguments->base.result.s_int32.r_int32; +} + + +/** + Interface to QMY_OBJECT_OVERRIDE API. + + See QMY_OBJECT_OVERRIDE documentation for more information about parameters and + return codes. +*/ +int32 db2i_ileBridge::objectOverride(FILE_HANDLE rfileHandle, + ILEMemHandle buf, + uint32 recordWidth) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MOOX0100 *input = (Qmy_MOOX0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_OBJECT_OVERRIDE; + input->ObjHnd = rfileHandle; + input->OutSpcHnd = (uint64)buf; + input->NxtRowOff = recordWidth; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + return rc; +} + +/** + Interface to QMY_DESCRIBE_OBJECT API for obtaining table stats. + + See QMY_DESCRIBE_OBJECT documentation for more information about parameters and + return codes. +*/ +int32 db2i_ileBridge::retrieveTableInfo(FILE_HANDLE defnHandle, + uint16 dataRequested, + ha_statistics& stats, + ILEMemHandle inSpc) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MDSO0100 *input = (Qmy_MDSO0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_DESCRIBE_OBJECT; + input->ShrHnd = defnHandle; + input->CnnHnd = cachedConnectionID; + + if (dataRequested & objLength) + input->RtnObjLen[0] = QMY_YES; + if (dataRequested & rowCount) + input->RtnRowCnt[0] = QMY_YES; + if (dataRequested & deletedRowCount) + input->RtnDltRowCnt[0] = QMY_YES; + if (dataRequested & rowsPerKey) + { + input->RowKeyHnd = (uint64)inSpc; + input->RtnRowKey[0] = QMY_YES; + } + if (dataRequested & meanRowLen) + input->RtnMeanRowLen[0] = QMY_YES; + if (dataRequested & lastModTime) + input->RtnModTim[0] = QMY_YES; + if (dataRequested & createTime) + input->RtnCrtTim[0] = QMY_YES; + if (dataRequested & ioCount) + input->RtnEstIoCnt[0] = QMY_YES; + + int32 rc = doItWithLog(); + + if (likely(rc == 0)) + { + Qmy_MDSO0100_output* output = (Qmy_MDSO0100_output*)parmBlock->outParms; + if (dataRequested & objLength) + stats.data_file_length = output->ObjLen; + if (dataRequested & rowCount) + stats.records= output->RowCnt; + if (dataRequested & deletedRowCount) + stats.deleted = output->DltRowCnt; + if (dataRequested & meanRowLen) + stats.mean_rec_length = output->MeanRowLen; + if (dataRequested & lastModTime) + stats.update_time = convertILEtime(output->ModTim); + if (dataRequested & createTime) + stats.create_time = convertILEtime(output->CrtTim); + if (dataRequested & ioCount) + stats.data_file_length = output->EstIoCnt; + } + + return rc; +} + +/** + Interface to QMY_DESCRIBE_OBJECT API for finding index size. + + See QMY_DESCRIBE_OBJECT documentation for more information about parameters and + return codes. +*/ +int32 db2i_ileBridge::retrieveIndexInfo(FILE_HANDLE defnHandle, + uint64* outPageCnt) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MDSO0100 *input = (Qmy_MDSO0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_DESCRIBE_OBJECT; + input->ShrHnd = defnHandle; + input->CnnHnd = cachedConnectionID; + input->RtnPageCnt[0] = QMY_YES; + + int32 rc = doItWithLog(); + + if (likely(rc == 0)) + { + Qmy_MDSO0100_output* output = (Qmy_MDSO0100_output*)parmBlock->outParms; + *outPageCnt = output->PageCnt; + } + + return rc; +} + + +/** + Interface to QMY_CLOSE_CONNECTION API + + See QMY_CLOSE_CONNECTION documentation for more information about parameters and + return codes. +*/ +int32 db2i_ileBridge::closeConnection(CONNECTION_HANDLE conn) +{ + IleParms* parmBlock = parms(); + Qmy_MCCN0100 *input = (Qmy_MCCN0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_CLOSE_CONNECTION; + input->CnnHnd = conn; + + int32 rc = doItWithLog(); + + return rc; +} + + +/** + Interface to QMY_INTERRUPT API + + See QMY_INTERRUPT documentation for more information about parameters and + return codes. +*/ +int32 db2i_ileBridge::readInterrupt(FILE_HANDLE fileHandle) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MINT0100 *input = (Qmy_MINT0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_INTERRUPT; + input->CnnHnd = cachedConnectionID; + input->ObjHnd = fileHandle; + + int32 rc = doItWithLog(); + + if (rc == QMY_ERR_END_OF_BLOCK) + { + rc = 0; + DBUG_PRINT("db2i_ileBridge::readInterrupt", ("End of block signalled")); + } + + return rc; +} + +/** + Interface to QMY_READ_ROWS API + + See QMY_READ_ROWS documentation for more information about parameters and + return codes. +*/ +int32 db2i_ileBridge::read(FILE_HANDLE rfileHandle, + ILEMemHandle buf, + char accessIntent, + char commitLevel, + char orientation, + bool asyncRead, + ILEMemHandle rrn, + ILEMemHandle key, + uint32 keylen, + uint16 keyParts, + int pipeFD) +{ + DBUG_ASSERT(cachedStateIsCoherent()); + IleParms* parmBlock = parms(); + Qmy_MRDX0100 *input = (Qmy_MRDX0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_READ_ROWS; + input->CmtLvl[0] = commitLevel; + + input->ObjHnd = rfileHandle; + input->Intent[0] = accessIntent; + input->OutSpcHnd = (uint64)buf; + input->OutRRNSpcHnd = (uint64)rrn; + input->RtnData[0] = QMY_RETURN_DATA; + + if (key) + { + input->KeySpcHnd = (uint64)key; + input->KeyColsLen = keylen; + input->KeyColsNbr = keyParts; + } + + input->Async[0] = (asyncRead ? QMY_YES : QMY_NO); + input->PipeDesc = pipeFD; + input->Orientation[0] = orientation; + input->CnnHnd = cachedConnectionID; + + int32 rc = doItWithLog(); + + // QMY_ERR_END_OF_BLOCK is informational only, so we ignore it. + if (rc == QMY_ERR_END_OF_BLOCK) + { + rc = 0; + DBUG_PRINT("db2i_ileBridge::read", ("End of block signalled")); + } + + return rc; +} + + +/** + Interface to QMY_QUIESCE_OBJECT API + + See QMY_QUIESCE_OBJECT documentation for more information about parameters and + return codes. +*/ +int32 db2i_ileBridge::quiesceFileInstance(FILE_HANDLE rfileHandle) +{ + IleParms* parmBlock = parms(); + Qmy_MQSC0100 *input = (Qmy_MQSC0100*)&(parmBlock->inParms); + memset(input, 0, sizeof(*input)); + + input->Format = QMY_QUIESCE_OBJECT; + input->ObjHnd = rfileHandle; + + int32 rc = doItWithLog(); + +#ifndef DBUG_OFF + if (unlikely(rc)) + { + DBUG_ASSERT(0); + } +#endif + + return rc; +} + +void db2i_ileBridge::PreservedHandleList::add(const char* newname, FILE_HANDLE newhandle, IBMDB2I_SHARE* share) +{ + NameHandlePair *newPair = (NameHandlePair*)my_malloc(sizeof(NameHandlePair), MYF(MY_WME)); + + newPair->next = head; + head = newPair; + + strcpy(newPair->name, newname); + newPair->handle = newhandle; + newPair->share = share; + DBUG_PRINT("db2i_ileBridge", ("Added handle %d for %s", uint32(newhandle), newname)); +} + + +FILE_HANDLE db2i_ileBridge::PreservedHandleList::findAndRemove(const char* fileName, IBMDB2I_SHARE** share) +{ + NameHandlePair* current = head; + NameHandlePair* prev = NULL; + + while (current) + { + NameHandlePair* next = current->next; + if (strcmp(fileName, current->name) == 0) + { + FILE_HANDLE tmp = current->handle; + *share = current->share; + if (prev) + prev->next = next; + if (current == head) + head = next; + my_free(current, MYF(0)); + DBUG_PRINT("db2i_ileBridge", ("Found handle %d for %s", uint32(tmp), fileName)); + return tmp; + } + prev = current; + current = next; + } + + return 0; +} + + +IleParms* db2i_ileBridge::initParmsForThread() +{ + + IleParms* p = (IleParms*)malloc_aligned(sizeof(IleParms)); + DBUG_ASSERT((uint64)(&(p->outParms))% 16 == 0); // Guarantee that outParms are aligned correctly + + if (likely(p)) + { + int32 rc = registerParmSpace((p->inParms), (p->outParms)); + if (likely(rc == 0)) + { + my_pthread_setspecific_ptr(THR_ILEPARMS, p); + DBUG_PRINT("db2i_ileBridge", ("Inited space for parms")); + return p; + } + else + reportSystemAPIError(rc, NULL); + } + + return NULL; +} + diff --git a/storage/ibmdb2i/db2i_ileBridge.h b/storage/ibmdb2i/db2i_ileBridge.h new file mode 100644 index 00000000000..10b9820d983 --- /dev/null +++ b/storage/ibmdb2i/db2i_ileBridge.h @@ -0,0 +1,499 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#ifndef DB2I_ILEBRIDGE_H +#define DB2I_ILEBRIDGE_H + +#include "db2i_global.h" +#include "mysql_priv.h" +#include "as400_types.h" +#include "as400_protos.h" +#include "qmyse.h" +#include "db2i_errors.h" + +typedef uint64_t FILE_HANDLE; +typedef my_thread_id CONNECTION_HANDLE; +const char SAVEPOINT_NAME[] = {0xD4,0xE2,0xD7,0xC9,0xD5,0xE3,0xC5,0xD9,0xD5,0x0}; +const uint32 TACIT_ERRORS_SIZE=2; + +enum db2i_InfoRequestSpec +{ + objLength = 1, + rowCount = 2, + deletedRowCount = 4, + rowsPerKey = 8, + meanRowLen = 16, + lastModTime = 32, + createTime = 64, + ioCount = 128 +}; + +extern handlerton *ibmdb2i_hton; +struct IBMDB2I_SHARE; + +const uint32 db2i_ileBridge_MAX_INPARM_SIZE = 512; +const uint32 db2i_ileBridge_MAX_OUTPARM_SIZE = 512; + +extern pthread_key(IleParms*, THR_ILEPARMS); +struct IleParms +{ + char inParms[db2i_ileBridge_MAX_INPARM_SIZE]; + char outParms[db2i_ileBridge_MAX_OUTPARM_SIZE]; +}; + +/** + @class db2i_ileBridge + + Implements a connection-based interface to the QMY_* APIs + + @details Each client connection that touches an IBMDB2I table has a "bridge" + associated with it. This bridge is constructed on first use and provides a + more C-like interface to the APIs. As well, it is reponsible for tracking + connection scoped information such as statement transaction state and error + message text. The bridge is destroyed when the connection ends. +*/ +class db2i_ileBridge +{ + enum ileFuncs + { + funcRegisterParameterSpaces, + funcRegisterSpace, + funcUnregisterSpace, + funcProcessRequest, + funcListEnd + }; + + static db2i_ileBridge* globalBridge; +public: + + + static int setup(); + static void takedown(); + + /** + Obtain a pointer to the bridge for the current connection. + + If a MySQL client connection is on the stack, we get the associated brideg. + Otherwise, we use the globalBridge. + */ + static db2i_ileBridge* getBridgeForThread() + { + THD* thd = current_thd; + if (likely(thd)) + return getBridgeForThread(thd); + + return globalBridge; + } + + /** + Obtain a pointer to the bridge for the specified connection. + + If a bridge exists already, we return it immediately. Otherwise, prepare + a new bridge for the connection. + */ + static db2i_ileBridge* getBridgeForThread(const THD* thd) + { + void* thdData = *thd_ha_data(thd, ibmdb2i_hton); + if (likely(thdData != NULL)) + return (db2i_ileBridge*)(thdData); + + db2i_ileBridge* newBridge = createNewBridge(thd->thread_id); + *thd_ha_data(thd, ibmdb2i_hton) = (void*)newBridge; + return newBridge; + } + + static void destroyBridgeForThread(const THD* thd); + static void registerPtr(const void* ptr, ILEMemHandle* receiver); + static void unregisterPtr(ILEMemHandle handle); + int32 allocateFileDefn(ILEMemHandle definitionSpace, + ILEMemHandle handleSpace, + uint16 fileCount, + const char* schemaName, + uint16 schemaNameLength, + ILEMemHandle formatSpace, + uint32 formatSpaceLen); + int32 allocateFileInstance(FILE_HANDLE defnHandle, + ILEMemHandle inuseSpace, + FILE_HANDLE* instance); + int32 deallocateFile(FILE_HANDLE fileHandle, + bool postDropTable=FALSE); + int32 read(FILE_HANDLE rfileHandle, + ILEMemHandle buf, + char accessIntent, + char commitLevel, + char orientation, + bool asyncRead = FALSE, + ILEMemHandle rrn = 0, + ILEMemHandle key = 0, + uint32 keylen = 0, + uint16 keyParts = 0, + int pipeFD = -1); + int32 readByRRN(FILE_HANDLE rfileHandle, + ILEMemHandle buf, + uint32 inRRN, + char accessIntent, + char commitLevel); + int32 writeRows(FILE_HANDLE rfileHandle, + ILEMemHandle buf, + char commitLevel, + int64* outIdVal, + bool* outIdGen, + uint32* dupKeyRRN, + char** dupKeyName, + uint32* dupKeyNameLen, + uint32* outIdIncrement); + uint32 execSQL(const char* statement, + uint32 statementCount, + uint8 commitLevel, + bool autoCreateSchema = FALSE, + bool dropSchema = FALSE, + bool noCommit = FALSE, + FILE_HANDLE fileHandle = 0); + int32 prepOpen(const char* statement, + FILE_HANDLE* rfileHandle, + uint32* recLength); + int32 deleteRow(FILE_HANDLE rfileHandle, + uint32 rrn); + int32 updateRow(FILE_HANDLE rfileHandle, + uint32 rrn, + ILEMemHandle buf, + uint32* dupKeyRRN, + char** dupKeyName, + uint32* dupKeyNameLen); + int32 commitmentControl(uint8 function); + int32 savepoint(uint8 function, + const char* savepointName); + int32 recordsInRange(FILE_HANDLE rfileHandle, + ILEMemHandle inSpc, + uint32 inKeyCnt, + uint32 inLiteralCnt, + uint32 inBoundsOff, + uint32 inLitDefOff, + uint32 inLiteralsOff, + uint32 inCutoff, + uint32 inSpcLen, + uint16 inEndByte, + uint64* outRecCnt, + uint16* outRtnCode); + int32 rrlslck(FILE_HANDLE rfileHandle, + char accessIntent); + int32 lockObj(FILE_HANDLE rfileHandle, + uint64 inTimeoutVal, + char inAction, + char inLockType, + char inTimeout); + int32 constraints(FILE_HANDLE rfileHandle, + ILEMemHandle inSpc, + uint32 inSpcLen, + uint32* outLen, + uint32* outCnt); + int32 optimizeTable(FILE_HANDLE rfileHandle); + static int32 initILE(const char* aspName, + uint16* traceCtlPtr); + int32 initFileForIO(FILE_HANDLE rfileHandle, + char accessIntent, + char commitLevel, + uint16* inRecSize, + uint16* inRecNullOffset, + uint16* outRecSize, + uint16* outRecNullOffset); + int32 readInterrupt(FILE_HANDLE fileHandle); + static int32 exitILE(); + + int32 objectOverride(FILE_HANDLE rfileHandle, + ILEMemHandle buf, + uint32 recordWidth = 0); + + int32 retrieveTableInfo(FILE_HANDLE rfileHandle, + uint16 dataRequested, + ha_statistics& stats, + ILEMemHandle inSpc = NULL); + + int32 retrieveIndexInfo(FILE_HANDLE rfileHandle, + uint64* outPageCnt); + + int32 closeConnection(CONNECTION_HANDLE conn); + int32 quiesceFileInstance(FILE_HANDLE rfileHandle); + + /** + Mark the beginning of a "statement transaction" + + @detail MySQL "statement transactions" (see sql/handler.cc) are implemented + as DB2 savepoints having a predefined name. + + @return 0 if successful; error otherwise + */ + uint32 beginStmtTx() + { + DBUG_ENTER("db2i_ileBridge::beginStmtTx"); + if (stmtTxActive) + DBUG_RETURN(0); + + stmtTxActive = true; + + DBUG_RETURN(savepoint(QMY_SET_SAVEPOINT, SAVEPOINT_NAME)); + } + + /** + Commit a "statement transaction" + + @return 0 if successful; error otherwise + */ + uint32 commitStmtTx() + { + DBUG_ENTER("db2i_ileBridge::commitStmtTx"); + DBUG_ASSERT(stmtTxActive); + stmtTxActive = false; + DBUG_RETURN(savepoint(QMY_RELEASE_SAVEPOINT, SAVEPOINT_NAME)); + } + + /** + Roll back a "statement transaction" + + @return 0 if successful; error otherwise + */ + uint32 rollbackStmtTx() + { + DBUG_ENTER("db2i_ileBridge::rollbackStmtTx"); + DBUG_ASSERT(stmtTxActive); + stmtTxActive = false; + DBUG_RETURN(savepoint(QMY_ROLLBACK_SAVEPOINT, SAVEPOINT_NAME)); + } + + + /** + Provide storage for generating error messages. + + This storage must persist until the error message is retrieved from the + handler instance. It is for this reason that we associate it with the bridge. + + @return Pointer to heap storage of MYSQL_ERRMSG_SIZE bytes + */ + char* getErrorStorage() + { + if (!connErrText) + { + connErrText = (char*)my_malloc(MYSQL_ERRMSG_SIZE, MYF(MY_WME)); + if (connErrText) connErrText[0] = 0; + } + + return connErrText; + } + + /** + Free storage for generating error messages. + */ + void freeErrorStorage() + { + if (likely(connErrText)) + { + my_free(connErrText, MYF(0)); + connErrText = NULL; + } + } + + + /** + Store a file handle for later retrieval. + + If deallocateFile encounters a lock when trying to perform its operation, + the file remains allocated but must be deallocated later. This function + provides a way for the connection to "remember" that this deallocation is + still needed. + + @param newname The name of the file to be added + @param newhandle The handle associated with newname + + */ + void preserveHandle(const char* newname, FILE_HANDLE newhandle, IBMDB2I_SHARE* share) + { + pendingLockedHandles.add(newname, newhandle, share); + } + + /** + Retrieve a file handle stored by preserveHandle(). + + @param name The name of the file to be retrieved. + + @return The handle associated with name + */ + FILE_HANDLE findAndRemovePreservedHandle(const char* name, IBMDB2I_SHARE** share) + { + FILE_HANDLE hdl = pendingLockedHandles.findAndRemove(name, share); + return hdl; + } + + /** + Indicate which error messages should be suppressed on the next API call + + These functions are useful for ensuring that the provided error numbers + are returned if a failure occurs but do not cause a spurious error message + to be returned. + + @return A pointer to this instance + */ + db2i_ileBridge* expectErrors(int32 er1) + { + tacitErrors[0]=er1; + return this; + } + + db2i_ileBridge* expectErrors(int32 er1, int32 er2) + { + tacitErrors[0]=er1; + tacitErrors[1]=er2; + return this; + } + + /** + Obtain the IBM i system message that accompanied the last API failure. + + @return A pointer to the 7 character message ID. + */ + static const char* getErrorMsgID() + { + return ((Qmy_Error_output_t*)parms()->outParms)->MsgId; + } + + /** + Convert an API error code into the equivalent MySQL error code (if any) + + @param rc The QMYSE API error code + + @return If an equivalent exists, the MySQL error code; else rc + */ + static int32 translateErrorCode(int32 rc) + { + if (likely(rc == 0)) + return 0; + + switch (rc) + { + case QMY_ERR_KEY_NOT_FOUND: + return HA_ERR_KEY_NOT_FOUND; + case QMY_ERR_DUP_KEY: + return HA_ERR_FOUND_DUPP_KEY; + case QMY_ERR_END_OF_FILE: + return HA_ERR_END_OF_FILE; + case QMY_ERR_LOCK_TIMEOUT: + return HA_ERR_LOCK_WAIT_TIMEOUT; + case QMY_ERR_CST_VIOLATION: + return HA_ERR_NO_REFERENCED_ROW; + case QMY_ERR_TABLE_NOT_FOUND: + return HA_ERR_NO_SUCH_TABLE; + case QMY_ERR_NON_UNIQUE_KEY: + return ER_DUP_ENTRY; + case QMY_ERR_MSGID: + { + if (memcmp(getErrorMsgID(), DB2I_CPF503A, 7) == 0) + return HA_ERR_ROW_IS_REFERENCED; + if (memcmp(getErrorMsgID(), DB2I_SQL0538, 7) == 0) + return HA_ERR_CANNOT_ADD_FOREIGN; + } + } + return rc; + } + +private: + + static db2i_ileBridge* createNewBridge(CONNECTION_HANDLE connID); + static void destroyBridge(db2i_ileBridge* bridge); + static int registerParmSpace(char* in, char* out); + static int32 doIt(); + int32 doItWithLog(); + + static _ILEpointer *functionSymbols; ///< Array of ILE function pointers + CONNECTION_HANDLE cachedConnectionID; ///< The associated connection + bool stmtTxActive; ///< Inside statement transaction + char *connErrText; ///< Storage for error message + int32 tacitErrors[TACIT_ERRORS_SIZE]; ///< List of errors to be suppressed + + static IleParms* initParmsForThread(); + + /** + Get space for passing parameters to the QMY_* APIs + + @details A fixed-length parameter passing space is associated with each + pthread. This space is allocated and registered by initParmsForThread() + the first time a pthread works with a bridge. The space is cached away + and remains available until the pthread ends. It became necessary to + disassociate the parameter space from the bridge in order to support + future enhancements to MySQL that sever the one-to-one relationship between + pthreads and user connections. The QMY_* APIs scope a registered parameter + space to the thread that executes the register operation. + */ + static IleParms* parms() + { + IleParms* p = my_pthread_getspecific_ptr(IleParms*, THR_ILEPARMS); + if (likely(p)) + return p; + + return initParmsForThread(); + } + + class PreservedHandleList + { + friend db2i_ileBridge* db2i_ileBridge::createNewBridge(CONNECTION_HANDLE); + public: + void add(const char* newname, FILE_HANDLE newhandle, IBMDB2I_SHARE* share); + FILE_HANDLE findAndRemove(const char* fileName, IBMDB2I_SHARE** share); + + private: + struct NameHandlePair + { + char name[FN_REFLEN]; + FILE_HANDLE handle; + IBMDB2I_SHARE* share; + NameHandlePair* next; + }* head; + } pendingLockedHandles; + + +#ifndef DBUG_OFF + bool cachedStateIsCoherent() + { + return (current_thd->thread_id == cachedConnectionID); + } + + friend void db2i_ileBridge::unregisterPtr(ILEMemHandle); + friend void db2i_ileBridge::registerPtr(const void*, ILEMemHandle*); + static uint32 registeredPtrs; +#endif +}; + + + +#endif diff --git a/storage/ibmdb2i/db2i_ioBuffers.cc b/storage/ibmdb2i/db2i_ioBuffers.cc new file mode 100644 index 00000000000..9525a6e34b5 --- /dev/null +++ b/storage/ibmdb2i/db2i_ioBuffers.cc @@ -0,0 +1,332 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#include "db2i_ioBuffers.h" + +/** + Request another block of rows + + Request the next set of rows from DB2. This must only be called after + newReadRequest(). + + @param orientation The direction to use when reading through the table. +*/ +void IOAsyncReadBuffer::loadNewRows(char orientation) +{ + rewind(); + maxRows() = rowsToBlock; + + DBUG_PRINT("db2i_ioBuffers::loadNewRows", ("Requesting %d rows, async = %d", rowsToBlock, readIsAsync)); + + rc = getBridge()->expectErrors(QMY_ERR_END_OF_BLOCK, QMY_ERR_LOB_SPACE_TOO_SMALL) + ->read(file, + ptr(), + accessIntent, + commitLevel, + orientation, + readIsAsync, + rrnList, + 0, + 0, + 0); + + DBUG_PRINT("db2i_ioBuffers::loadNewRows", ("recordsRead: %d, rc: %d", (uint32)rowCount(), rc)); + + + *releaseRowNeeded = true; + + if (rc == QMY_ERR_END_OF_BLOCK) + { + // This is really just an informational error, so we ignore it. + rc = 0; + DBUG_PRINT("db2i_ioBuffers::loadNewRows", ("End of block signalled")); + } + else if (rc == QMY_ERR_END_OF_FILE) + { + // If we reach EOF or end-of-key, DB2 guarantees that no rows will be locked. + rc = HA_ERR_END_OF_FILE; + *releaseRowNeeded = false; + } + else if (rc == QMY_ERR_KEY_NOT_FOUND) + { + rc = HA_ERR_KEY_NOT_FOUND; + *releaseRowNeeded = false; + } + + if (rc) closePipe(); +} + + +/** + Empty the message pipe to prepare for another read. +*/ +void IOAsyncReadBuffer::drainPipe() +{ + DBUG_ASSERT(pipeState == PendingFullBufferMsg); + PipeRpy_t msg[32]; + int bytes; + PipeRpy_t* lastMsg; + while ((bytes = read(msgPipe, msg, sizeof(msg))) > 0) + { + DBUG_PRINT("db2i_ioBuffers::drainPipe",("Pipe returned %d bytes", bytes)); + lastMsg = &msg[bytes / (sizeof(msg[0]))-1]; + if (lastMsg->CumRowCnt == maxRows() || + lastMsg->RtnCod != 0) + { + pipeState = ConsumedFullBufferMsg; + break; + } + + } + DBUG_PRINT("db2i_ioBuffers::drainPipe",("rc = %d, rows = %d, max = %d", lastMsg->RtnCod, lastMsg->CumRowCnt, (uint32)maxRows())); +} + + +/** + Poll the message pipe for async read messages + + Only valid in async + + @param orientation The direction to use when reading through the table. +*/ +void IOAsyncReadBuffer::pollNextRow(char orientation) +{ + DBUG_ASSERT(readIsAsync); + + // Handle the case in which the buffer is full. + if (rowCount() == maxRows()) + { + // If we haven't read to the end, exit here. + if (readCursor < rowCount()) + return; + + if (pipeState == PendingFullBufferMsg) + drainPipe(); + if (pipeState == ConsumedFullBufferMsg) + loadNewRows(orientation); + } + + if (!rc) + { + PipeRpy_t* lastMsg = NULL; + while (true) + { + PipeRpy_t msg[32]; + int bytes = read(msgPipe, msg, sizeof(msg)); + DBUG_PRINT("db2i_ioBuffers::pollNextRow",("Pipe returned %d bytes", bytes)); + + if (unlikely(bytes < 0)) + { + DBUG_PRINT("db2i_ioBuffers::pollNextRow", ("Error")); + rc = errno; + break; + } + else if (bytes == 0) + break; + + DBUG_ASSERT(bytes % sizeof(msg[0]) == 0); + lastMsg = &msg[bytes / (sizeof(msg[0]))-1]; + + if (lastMsg->RtnCod || (lastMsg->CumRowCnt == usedRows())) + { + rc = lastMsg->RtnCod; + break; + } + } + + *releaseRowNeeded = true; + + if (rc == QMY_ERR_END_OF_BLOCK) + rc = 0; + else if (rc == QMY_ERR_END_OF_FILE) + { + // If we reach EOF or end-of-key, DB2 guarantees that no rows will be locked. + rc = HA_ERR_END_OF_FILE; + *releaseRowNeeded = false; + } + else if (rc == QMY_ERR_KEY_NOT_FOUND) + { + rc = HA_ERR_KEY_NOT_FOUND; + *releaseRowNeeded = false; + } + + if (lastMsg) + DBUG_PRINT("db2i_ioBuffers::pollNextRow", ("Good data: rc=%d; rows=%d; usedRows=%d", lastMsg->RtnCod, lastMsg->CumRowCnt, (uint32)usedRows())); + if (lastMsg && likely(!rc)) + { + if (lastMsg->CumRowCnt < maxRows()) + pipeState = PendingFullBufferMsg; + else + pipeState = ConsumedFullBufferMsg; + + DBUG_ASSERT(lastMsg->CumRowCnt <= usedRows()); + + } + DBUG_ASSERT(rowCount() <= getRowCapacity()); + } + DBUG_PRINT("db2i_ioBuffers::pollNextRow", ("filledRows: %d, rc: %d", rowCount(), rc)); + if (rc) closePipe(); +} + + +/** + Prepare for the destruction of the row buffer storage. +*/ +void IOAsyncReadBuffer::prepForFree() +{ + interruptRead(); + rewind(); + IORowBuffer::prepForFree(); +} + + +/** + Initialize the newly allocated storage. + + @param sizeChanged Indicates whether the storage capacity is being changed. +*/ +void IOAsyncReadBuffer::initAfterAllocate(bool sizeChanged) +{ + rewind(); + + if (sizeChanged || ((void*)rrnList == NULL)) + rrnList.realloc(getRowCapacity() * sizeof(uint32)); +} + + +/** + Send an initial read request + + @param infile The file (table/index) being read from + @param orientation The orientation to use for this read request + @param rowsToBuffer The number of rows to request each time + @param useAsync Whether reads should be performed asynchronously. + @param key The key to use (if any) + @param keyLength The length of key (if any) + @param keyParts The number of columns in the key (if any) + +*/ +void IOAsyncReadBuffer::newReadRequest(FILE_HANDLE infile, + char orientation, + uint32 rowsToBuffer, + bool useAsync, + ILEMemHandle key, + int keyLength, + int keyParts) +{ + DBUG_ENTER("db2i_ioBuffers::newReadRequest"); + DBUG_ASSERT(rowsToBuffer <= getRowCapacity()); +#ifndef DBUG_OFF + if (readCursor < rowCount()) + DBUG_PRINT("PERF:",("Wasting %d buffered rows!\n", rowCount() - readCursor)); +#endif + + int fildes[2]; + int ileDescriptor = QMY_REUSE; + + interruptRead(); + + if (likely(useAsync)) + { + if (rowsToBuffer == 1) + { + // Async provides little or no benefit for single row reads, so we turn it off + DBUG_PRINT("db2i_ioBuffers::newReadRequest", ("Disabling async")); + useAsync = false; + } + else + { + rc = pipe(fildes); + if (rc) DBUG_VOID_RETURN; + + // Translate the pipe write descriptor into the equivalent ILE descriptor + rc = fstatx(fildes[1], (struct stat*)&ileDescriptor, sizeof(ileDescriptor), STX_XPFFD_PASE); + if (rc) + { + close(fildes[0]); + close(fildes[1]); + DBUG_VOID_RETURN; + } + pipeState = Untouched; + msgPipe = fildes[0]; + + DBUG_PRINT("db2i_ioBuffers::newReadRequest", ("Opened pipe %d", fildes[0])); + } + } + + file = infile; + readIsAsync = useAsync; + rowsToBlock = rowsToBuffer; + + rewind(); + maxRows() = 1; + rc = getBridge()->expectErrors(QMY_ERR_END_OF_BLOCK, QMY_ERR_LOB_SPACE_TOO_SMALL) + ->read(file, + ptr(), + accessIntent, + commitLevel, + orientation, + useAsync, + rrnList, + key, + keyLength, + keyParts, + ileDescriptor); + + // Having shared the pipe with ILE, we relinquish our claim on the write end + // of the pipe. + if (useAsync) + close(fildes[1]); + + // If we reach EOF or end-of-key, DB2 guarantees that no rows will be locked. + if (rc == QMY_ERR_END_OF_FILE) + { + rc = HA_ERR_END_OF_FILE; + *releaseRowNeeded = false; + } + else if (rc == QMY_ERR_KEY_NOT_FOUND) + { + if (rowCount()) + rc = HA_ERR_END_OF_FILE; + else + rc = HA_ERR_KEY_NOT_FOUND; + *releaseRowNeeded = false; + } + else + *releaseRowNeeded = true; + + DBUG_VOID_RETURN; +} diff --git a/storage/ibmdb2i/db2i_ioBuffers.h b/storage/ibmdb2i/db2i_ioBuffers.h new file mode 100644 index 00000000000..350d854f055 --- /dev/null +++ b/storage/ibmdb2i/db2i_ioBuffers.h @@ -0,0 +1,416 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +/** + @file db2i_ioBuffers.h + + @brief Buffer classes used for interacting with QMYSE read/write buffers. + +*/ + + +#include "db2i_validatedPointer.h" +#include "mysql_priv.h" +#include <sys/stat.h> +#include <fcntl.h> +#include <as400_types.h> + +// Needed for compilers which do not include fstatx in standard headers. +extern "C" int fstatx(int, struct stat *, int, int); + +/** + Basic row buffer + + Provides the basic structure and methods needed for communicating + with QMYSE I/O APIs. + + @details All QMYSE I/O apis use a buffer that is structured as two integer + row counts (max and used) and storage for some number of rows. The row counts + are both input and output for the API, and their usage depends on the + particular API invoked. This class encapsulates that buffer definition. +*/ +class IORowBuffer +{ + public: + IORowBuffer() : allocSize(0), rowLength(0) {;} + ~IORowBuffer() { freeBuf(); } + ValidatedPointer<char>& ptr() { return data; } + + /** + Sets up the buffer to hold the size indicated. + + @param rowLen length of the rows that will be stored in this buffer + @param nullMapOffset position of null map within each row + @param size buffer size requested + */ + void allocBuf(uint32 rowLen, uint16 nullMapOffset, uint32 size) + { + nullOffset = nullMapOffset; + uint32 newSize = size + sizeof(BufferHdr_t); + // If the internal structure of the row is changing, we need to + // remember this and notify the subclasses via initAfterAllocate(); + bool formatChanged = ((size/rowLen) != rowCapacity); + + if (newSize > allocSize) + { + this->freeBuf(); + data.alloc(newSize); + if (likely((void*)data)) + allocSize = newSize; + } + + if (likely((void*)data)) + { + DBUG_ASSERT((uint64)(void*)data % 16 == 0); + rowLength = rowLen; + rowCapacity = size / rowLength; + initAfterAllocate(formatChanged); + } + else + { + allocSize = 0; + rowCapacity = 0; + } + + DBUG_PRINT("db2i_ioBuffers::allocBuf",("rowCapacity = %d", rowCapacity)); + } + + void zeroBuf() + { + memset(data, 0, allocSize); + } + + void freeBuf() + { + if (likely(allocSize)) + { + prepForFree(); + DBUG_PRINT("IORowBuffer::freeBuf",("Freeing 0x%p", (char*)data)); + data.dealloc(); + } + } + + char* getRowN(uint32 n) + { + if (unlikely(n >= getRowCapacity())) + return NULL; + return (char*)data + sizeof(BufferHdr_t) + (rowLength * n); + }; + + uint32 getRowCapacity() const {return rowCapacity;} + uint32 getRowNullOffset() const {return nullOffset;} + uint32 getRowLength() const {return rowLength;} + + protected: + /** + Called prior to freeing buffer storage so that subclasses can do + any required cleanup + */ + virtual void prepForFree() + { + allocSize = 0; + rowCapacity = 0; + } + + /** + Called after buffer storage so that subclasses can do any required setup. + */ + virtual void initAfterAllocate(bool sizeChanged) { return;} + + ValidatedPointer<char> data; + uint32 allocSize; + uint32 rowCapacity; + uint32 rowLength; + uint16 nullOffset; + uint32& usedRows() const { return ((BufferHdr_t*)(char*)data)->UsedRowCnt; } + uint32& maxRows() const {return ((BufferHdr_t*)(char*)data)->MaxRowCnt; } +}; + + +/** + Write buffer + + Implements methods for inserting data into a row buffer for use with the + QMY_WRITE and QMY_UPDATE APIs. + + @details The max row count defines how many rows are in the buffer. The used + row count is updated by QMYSE to indicate how many rows have been + successfully written. +*/ +class IOWriteBuffer : public IORowBuffer +{ + public: + bool endOfBuffer() const {return (maxRows() == getRowCapacity());} + + char* addRow() + { + return getRowN(maxRows()++); + } + + void resetAfterWrite() + { + maxRows() = 0; + } + + void deleteRow() + { + --maxRows(); + } + + uint32 rowCount() const {return maxRows();} + + uint32 rowsWritten() const {return usedRows()-1;} + + private: + void initAfterAllocate(bool sizeChanged) {maxRows() = 0; usedRows() = 0;} +}; + + +/** + Read buffer + + Implements methods for reading data from and managing a row buffer for use + with the QMY_READ APIs. This is primarily for use with metainformation queries. +*/ +class IOReadBuffer : public IORowBuffer +{ + public: + + IOReadBuffer() {;} + IOReadBuffer(uint32 rows, uint32 rowLength) + { + allocBuf(rows, 0, rows * rowLength); + maxRows() = rows; + } + + uint32 rowCount() {return usedRows();} + void setRowsToProcess(uint32 rows) { maxRows() = rows; } +}; + + +/** + Read buffer + + Implements methods for reading data from and managing a row buffer for use + with the QMY_READ APIs. + + @details This class supports both sync and async read modes. The max row + count defines the number of rows that are requested to be read. The used row + count defines how many rows have been read. Sync mode is reasonably + straightforward, but async mode has a complex system of communicating with + QMYSE that is optimized for low latency. In async mode, the used row count is + updated continuously by QMYSE as rows are read. At the same time, messages are + sent to the associated pipe indicating that a row has been read. As long as + the internal read cursor lags behind the used row count, the pipe is never + consulted. But if the internal read cursor "catches up to" the used row count, + then we block on the pipe until we find a message indicating that a new row + has been read or that an error has occurred. +*/ +class IOAsyncReadBuffer : public IOReadBuffer +{ + public: + IOAsyncReadBuffer() : + file(0), readIsAsync(false), msgPipe(QMY_REUSE), bridge(NULL) + { + } + + ~IOAsyncReadBuffer() + { + interruptRead(); + rrnList.dealloc(); + } + + + /** + Signal read operation complete + + Indicates that the storage engine requires no more data from the table. + Must be called between calls to newReadRequest(). + */ + void endRead() + { +#ifndef DBUG_OFF + if (readCursor < rowCount()) + DBUG_PRINT("PERF:",("Wasting %d buffered rows!\n", rowCount() - readCursor)); +#endif + interruptRead(); + + file = 0; + bridge = NULL; + } + + /** + Update data that may change on each read operation + */ + void update(char newAccessIntent, + bool* newReleaseRowNeeded, + char commitLvl) + { + accessIntent = newAccessIntent; + releaseRowNeeded = newReleaseRowNeeded; + commitLevel = commitLvl; + } + + /** + Read the next row in the table. + + Return a pointer to the next row in the table, where "next" is defined + by the orientation. + + @param orientaton + @param[out] rrn The relative record number of the row returned. Not reliable + if NULL is returned by this function. + + @return Pointer to the row. Null if no more rows are available or an error + occurred. + */ + char* readNextRow(char orientation, uint32& rrn) + { + DBUG_PRINT("db2i_ioBuffers::readNextRow", ("readCursor: %d, filledRows: %d, rc: %d", readCursor, rowCount(), rc)); + + while (readCursor >= rowCount() && !rc) + { + if (!readIsAsync) + loadNewRows(orientation); + else + pollNextRow(orientation); + } + + if (readCursor >= rowCount()) + return NULL; + + rrn = rrnList[readCursor]; + return getRowN(readCursor++); + } + + /** + Retrieve the return code generated by the last operation. + + @return The return code, translated to the appropriate HA_ERR_* + value if possible. + */ + int32 lastrc() + { + return db2i_ileBridge::translateErrorCode(rc); + } + + void rewind() + { + readCursor = 0; + rc = 0; + usedRows() = 0; + } + + bool reachedEOD() { return EOD; } + + void newReadRequest(FILE_HANDLE infile, + char orientation, + uint32 rowsToBuffer, + bool useAsync, + ILEMemHandle key, + int keyLength, + int keyParts); + + private: + + /** + End any running async read operation. + */ + void interruptRead() + { + closePipe(); + if (file && readIsAsync && (rc == 0) && (rowCount() < getRowCapacity())) + { + DBUG_PRINT("IOReadBuffer::interruptRead", ("PERF: Interrupting %d", (uint32)file)); + getBridge()->readInterrupt(file); + } + } + + void closePipe() + { + if (msgPipe != QMY_REUSE) + { + DBUG_PRINT("db2i_ioBuffers::closePipe", ("Closing pipe %d", msgPipe)); + close(msgPipe); + msgPipe = QMY_REUSE; + } + } + + /** + Get a pointer to the active ILE bridge. + + Getting the bridge pointer is (relatively) expensive, so we cache + it off for each operation. + */ + db2i_ileBridge* getBridge() + { + if (unlikely(bridge == NULL)) + { + bridge = db2i_ileBridge::getBridgeForThread(); + } + return bridge; + } + + void drainPipe(); + void pollNextRow(char orientation); + void prepForFree(); + void initAfterAllocate(bool sizeChanged); + void loadNewRows(char orientation); + + + uint32 readCursor; // Read position within buffer + int32 rc; // Last return code received + ValidatedPointer<uint32> rrnList; // Receiver for list of rrns + char accessIntent; // The access intent for this read + char commitLevel; // What isolation level should be used + char EOD; // Whether end-of-data was hit + char readIsAsync; // Are reads to be done asynchronously? + bool* releaseRowNeeded; + /* Does the caller need to release the current row when finished reading */ + FILE_HANDLE file; // The file to be read + int msgPipe; + /* The read descriptor of the pipe used to pass messages during async reads */ + db2i_ileBridge* bridge; // Cached pointer to bridge + uint32 rowsToBlock; // Number of rows to request + enum + { + ConsumedFullBufferMsg, + PendingFullBufferMsg, + Untouched + } pipeState; + /* The state of the async read message pipe */ +}; + diff --git a/storage/ibmdb2i/db2i_misc.h b/storage/ibmdb2i/db2i_misc.h new file mode 100644 index 00000000000..1cc3f962cfc --- /dev/null +++ b/storage/ibmdb2i/db2i_misc.h @@ -0,0 +1,107 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#ifndef DB2I_MISC_H +#define DB2I_MISC_H + +/** + Undelimit quote-delimited DB2 names in-place +*/ +void stripExtraQuotes(char* name, uint maxLen) +{ + char* oldName = (char*)sql_strdup(name); + uint i = 0; + uint j = 0; + do + { + name[j] = oldName[i]; + if (oldName[i] == '"' && oldName[i+1] == '"') + ++i; + } while (++j < maxLen && oldName[++i]); + + if (j == maxLen) + --j; + name[j] = 0; +} + +/** + Convert a MySQL identifier name into a DB2 compatible format + + @parm input The MySQL name + @parm output The DB2 name + @parm outlen The amount of space allocated for output + @parm delimit Should delimiting quotes be placed around the converted name? + @parm delimitQuotes Should quotes in the MySQL be delimited with additional quotes? + + @return FALSE if output was too small and name was truncated; TRUE otherwise +*/ +bool convertMySQLNameToDB2Name(const char* input, + char* output, + size_t outlen, + bool delimit = true, + bool delimitQuotes = true) +{ + uint o = 0; + if (delimit) + output[o++] = '"'; + + uint i = 0; + do + { + output[o] = input[i]; + if (delimitQuotes && input[i] == '"') + output[++o] = '"'; + } while (++o < outlen-2 && input[++i]); + + if (delimit) + output[o++] = '"'; + output[min(o, outlen-1)] = 0; // This isn't the most user-friendly way to handle overflows, + // but at least its safe. + return (o <= outlen-1); +} + +bool isUpperOrQuote(const CHARSET_INFO* cs, const char* s) +{ + while (*s) + { + if (my_isupper(cs, *s) || (*s == '"')) + ++s; + else + return false; + } + return true; +} + +#endif diff --git a/storage/ibmdb2i/db2i_myconv.cc b/storage/ibmdb2i/db2i_myconv.cc new file mode 100644 index 00000000000..7be6e1236cd --- /dev/null +++ b/storage/ibmdb2i/db2i_myconv.cc @@ -0,0 +1,1498 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +/** + @file + + @brief A direct map optimization of iconv and related functions + This was show to significantly reduce character conversion cost + for short strings when compared to calling iconv system code. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <wchar.h> +#include <errno.h> +#include <iconv.h> +#include <ctype.h> +#include <stdarg.h> +#include <string.h> +#include <as400_protos.h> + +#include "db2i_myconv.h" +#include "db2i_global.h" + +int32_t myconvDebug=0; + +static char szGetTimeString[20]; +static char * GetTimeString(time_t now) +{ + struct tm * tm; + + now = time(&now); + tm = (struct tm *) localtime(&now); + sprintf(szGetTimeString, "%04d/%02d/%02d %02d:%02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return szGetTimeString; +} + +static MEM_ROOT dmapMemRoot; + +void initMyconv() +{ + init_alloc_root(&dmapMemRoot, 0x200, 0); +} + +void cleanupMyconv() +{ + free_root(&dmapMemRoot,0); +} + + +#ifdef DEBUG +/* type: */ +#define STDOUT_WITH_TIME -1 /* to stdout with time */ +#define STDERR_WITH_TIME -2 /* to stderr with time */ +#define STDOUT_WO_TIME 1 /* : to stdout */ +#define STDERR_WO_TIME 2 /* : to stderr */ + + +static void MyPrintf(long type, + char * fmt, ...) +{ + char StdoutFN[256]; + va_list ap; + char * p; + time_t now; + FILE * fd=stderr; + + if (type < 0) + { + now = time(&now); + fprintf(fd, "%s ", GetTimeString(now)); + } + va_start(ap, fmt); + vfprintf(fd, fmt, ap); + va_end(ap); +} +#endif + + + + +#define MAX_CONVERTER 128 + +mycstoccsid(const char* pname) +{ + if (strcmp(pname, "UTF-16")==0) + return 1200; + else if (strcmp(pname, "big5")==0) + return 950; + else + return cstoccsid(pname); +} +#define cstoccsid mycstoccsid + +static struct __myconv_rec myconv_rec [MAX_CONVERTER]; +static struct __dmap_rec dmap_rec [MAX_CONVERTER]; + +static int dmap_open(const char * to, + const char * from, + const int32_t idx) +{ + if (myconvIsSBCS(from) && myconvIsSBCS(to)) { + dmap_rec[idx].codingSchema = DMAP_S2S; + if ((dmap_rec[idx].dmapS2S = (uchar *) alloc_root(&dmapMemRoot, 0x100)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_S2S, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapS2S, 0x00, 0x100); + myconv_rec[idx].allocatedSize=0x100; + + { + char dmapSrc[0x100]; + iconv_t cd; + int32_t i; + size_t inBytesLeft=0x100; + size_t outBytesLeft=0x100; + size_t len; + char * inBuf=dmapSrc; + char * outBuf=(char *) dmap_rec[idx].dmapS2S; + + if ((cd = iconv_open(to, from)) == (iconv_t) -1) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed with iconv_open(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); +#endif + return -1; + } + + inBytesLeft = 0x100; + for (i = 0; i < inBytesLeft; ++i) + dmapSrc[i]=i; + + do { + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d: iconv() returns %d, errno = %d in %s at %d\n", + to, from, idx, DMAP_S2S, len, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WITH_TIME, + "inBytesLeft = %d, inBuf - dmapSrc = %d\n", inBytesLeft, inBuf-dmapSrc); + MyPrintf(STDERR_WITH_TIME, + "outBytesLeft = %d, outBuf - dmapS2S = %d\n", outBytesLeft, outBuf-(char *) dmap_rec[idx].dmapS2S); + } + if ((inBytesLeft == 86 || inBytesLeft == 64 || inBytesLeft == 1) && + memcmp(from, "IBM-1256", 9) == 0 && + memcmp(to, "IBM-420", 8) == 0) { + /* Known problem for IBM-1256_IBM-420 */ + --inBytesLeft; + ++inBuf; + *outBuf=0x00; + ++outBuf; + --outBytesLeft; + continue; + } else if ((inBytesLeft == 173 || inBytesLeft == 172 || + inBytesLeft == 74 || inBytesLeft == 73 || + inBytesLeft == 52 || inBytesLeft == 50 || + inBytesLeft == 31 || inBytesLeft == 20 || + inBytesLeft == 6) && + memcmp(to, "IBM-1256", 9) == 0 && + memcmp(from, "IBM-420", 8) == 0) { + /* Known problem for IBM-420_IBM-1256 */ + --inBytesLeft; + ++inBuf; + *outBuf=0x00; + ++outBuf; + --outBytesLeft; + continue; + } else if ((128 >= inBytesLeft) && + memcmp(to, "IBM-037", 8) == 0 && + memcmp(from, "IBM-367", 8) == 0) { + /* Known problem for IBM-367_IBM-037 */ + --inBytesLeft; + ++inBuf; + *outBuf=0x00; + ++outBuf; + --outBytesLeft; + continue; + } else if (((1 <= inBytesLeft && inBytesLeft <= 4) || (97 <= inBytesLeft && inBytesLeft <= 128)) && + memcmp(to, "IBM-838", 8) == 0 && + memcmp(from, "TIS-620", 8) == 0) { + /* Known problem for TIS-620_IBM-838 */ + --inBytesLeft; + ++inBuf; + *outBuf=0x00; + ++outBuf; + --outBytesLeft; + continue; + } + iconv_close(cd); + return -1; +#else + /* Tolerant to undefined conversions for any converter */ + --inBytesLeft; + ++inBuf; + *outBuf=0x00; + ++outBuf; + --outBytesLeft; + continue; +#endif + } + } while (inBytesLeft > 0); + + if (myconvIsISO(to)) + myconv_rec[idx].subS=0x1A; + else if (myconvIsASCII(to)) + myconv_rec[idx].subS=0x7F; + else if (myconvIsEBCDIC(to)) + myconv_rec[idx].subS=0x3F; + + if (myconvIsISO(from)) + myconv_rec[idx].srcSubS=0x1A; + else if (myconvIsASCII(from)) + myconv_rec[idx].srcSubS=0x7F; + else if (myconvIsEBCDIC(from)) + myconv_rec[idx].srcSubS=0x3F; + + iconv_close(cd); + } + } else if (((myconvIsSBCS(from) && myconvIsUnicode2(to)) && (dmap_rec[idx].codingSchema = DMAP_S2U)) || + ((myconvIsSBCS(from) && myconvIsUTF8(to)) && (dmap_rec[idx].codingSchema = DMAP_S28))) { + int i; + + /* single byte mapping */ + if ((dmap_rec[idx].dmapD12U = (UniChar *) alloc_root(&dmapMemRoot, 0x100 * 2)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_S2U, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapD12U, 0x00, 0x100 * 2); + myconv_rec[idx].allocatedSize=0x100 * 2; + + + { + char dmapSrc[2]; + iconv_t cd; + int32_t i; + size_t inBytesLeft; + size_t outBytesLeft; + size_t len; + char * inBuf; + char * outBuf; + char SS=0x1A; +#ifdef support_surrogate + if ((cd = iconv_open("UTF-16", from)) == (iconv_t) -1) { +#else + if ((cd = iconv_open("UCS-2", from)) == (iconv_t) -1) { +#endif +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed with iconv_open(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); +#endif + return -1; + } + + for (i = 0; i < 0x100; ++i) { + dmapSrc[0]=i; + inBuf=dmapSrc; + inBytesLeft=1; + outBuf=(char *) &(dmap_rec[idx].dmapD12U[i]); + outBytesLeft=2; + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { + if ((errno == EILSEQ || errno == EINVAL) && + inBytesLeft == 1 && + outBytesLeft == 2) { + continue; + } else { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(cd,%02x,%d,%02x%02x,%d), errno = %d in %s at %d\n", + to, from, idx, dmapSrc[0], 1, + (&dmap_rec[idx].dmapD12U[i])[0],(&dmap_rec[idx].dmapD12U[i])[1], 2, + errno, __FILE__,__LINE__); + MyPrintf(STDERR_WITH_TIME, + "inBytesLeft=%d, outBytesLeft=%d, %02x%02x\n", + inBytesLeft, outBytesLeft, + (&dmap_rec[idx].dmapD12U[i])[0],(&dmap_rec[idx].dmapD12U[i])[1]); + } +#endif + iconv_close(cd); + return -1; + } + dmap_rec[idx].dmapD12U[i]=0x0000; + } + if (dmap_rec[idx].dmapE02U[i] == 0x001A && /* pick the first one */ + myconv_rec[idx].srcSubS == 0x00) { + myconv_rec[idx].srcSubS=i; + } + } + iconv_close(cd); + } + myconv_rec[idx].subS=0x1A; + myconv_rec[idx].subD=0xFFFD; + + + } else if (((myconvIsUCS2(from) && myconvIsSBCS(to)) && (dmap_rec[idx].codingSchema = DMAP_U2S)) || + ((myconvIsUTF16(from) && myconvIsSBCS(to)) && (dmap_rec[idx].codingSchema = DMAP_T2S)) || + ((myconvIsUTF8(from) && myconvIsSBCS(to)) && (dmap_rec[idx].codingSchema = DMAP_82S))) { + /* UTF-16 -> SBCS, the direct map a bit of waste of space, + * binary search may be reasonable alternative + */ + if ((dmap_rec[idx].dmapU2S = (uchar *) alloc_root(&dmapMemRoot, 0x10000 * 2)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_U2S, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapU2S, 0x00, 0x10000); + myconv_rec[idx].allocatedSize=(0x10000 * 2); + + { + iconv_t cd; + int32_t i; + +#ifdef support_surrogate + if ((cd = iconv_open(to, "UTF-16")) == (iconv_t) -1) { +#else + if ((cd = iconv_open(to, "UCS-2")) == (iconv_t) -1) { +#endif +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed with iconv_open(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); +#endif + return -1; + } + + for (i = 0; i < 0x100; ++i) { + UniChar dmapSrc[0x100]; + int32_t j; + for (j = 0; j < 0x100; ++j) { + dmapSrc[j]=i * 0x100 + j; + } + char * inBuf=(char *) dmapSrc; + char * outBuf=(char *) &(dmap_rec[idx].dmapU2S[i*0x100]); + size_t inBytesLeft=sizeof(dmapSrc); + size_t outBytesLeft=0x100; + size_t len; + + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { + if (inBytesLeft == 0 && outBytesLeft == 0) { /* a number of substitution returns */ + continue; + } +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), errno = %d in %s at %d\n", + from, to, idx, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WITH_TIME, + "iconv() retuns %d, errno=%d, InBytesLeft=%d, OutBytesLeft=%d\n", + len, errno, inBytesLeft, outBytesLeft, __FILE__,__LINE__); + } +#endif + iconv_close(cd); + return -1; + } + } + iconv_close(cd); + + myconv_rec[idx].subS = dmap_rec[idx].dmapU2S[0x1A]; + myconv_rec[idx].subD = dmap_rec[idx].dmapU2S[0xFFFD]; + myconv_rec[idx].srcSubS = 0x1A; + myconv_rec[idx].srcSubD = 0xFFFD; + } + + + + } else if (((myconvIsDBCS(from) && myconvIsUnicode2(to)) && (dmap_rec[idx].codingSchema = DMAP_D2U)) || + ((myconvIsDBCS(from) && myconvIsUTF8(to)) && (dmap_rec[idx].codingSchema = DMAP_D28))) { + int i; + /* single byte mapping */ + if ((dmap_rec[idx].dmapD12U = (UniChar *) alloc_root(&dmapMemRoot, 0x100 * 2)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_D2U, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapD12U, 0x00, 0x100 * 2); + + /* double byte mapping, assume 7 bit ASCII is not use as the first byte of DBCS. */ + if ((dmap_rec[idx].dmapD22U = (UniChar *) alloc_root(&dmapMemRoot, 0x8000 * 2)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_D2U, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapD22U, 0x00, 0x8000 * 2); + + myconv_rec[idx].allocatedSize=(0x100 + 0x8000) * 2; + + + { + char dmapSrc[2]; + iconv_t cd; + int32_t i; + size_t inBytesLeft; + size_t outBytesLeft; + size_t len; + char * inBuf; + char * outBuf; + char SS=0x1A; + +#ifdef support_surrogate + if ((cd = iconv_open("UTF-16", from)) == (iconv_t) -1) { +#else + if ((cd = iconv_open("UCS-2", from)) == (iconv_t) -1) { +#endif +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed with iconv_open(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); +#endif + return -1; + } + + for (i = 0; i < 0x100; ++i) { + dmapSrc[0]=i; + inBuf=dmapSrc; + inBytesLeft=1; + outBuf=(char *) (&dmap_rec[idx].dmapD12U[i]); + outBytesLeft=2; + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { + if ((errno == EILSEQ || errno == EINVAL) && + inBytesLeft == 1 && + outBytesLeft == 2) { + continue; + } else { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(cd,%02x,%d,%02x%02x,%d), errno = %d in %s at %d\n", + to, from, idx, dmapSrc[0], 1, + (&dmap_rec[idx].dmapD12U[i])[0],(&dmap_rec[idx].dmapD12U[i])[1], 2, + errno, __FILE__,__LINE__); + MyPrintf(STDERR_WITH_TIME, + "inBytesLeft=%d, outBytesLeft=%d, %02x%02x\n", + inBytesLeft, outBytesLeft, + (&dmap_rec[idx].dmapD12U[i])[0],(&dmap_rec[idx].dmapD12U[i])[1]); + } +#endif + iconv_close(cd); + return -1; + } + dmap_rec[idx].dmapD12U[i]=0x0000; + } + if (dmap_rec[idx].dmapD12U[i] == 0x001A && /* pick the first one */ + myconv_rec[idx].srcSubS == 0x00) { + myconv_rec[idx].srcSubS=i; + } + } + + + for (i = 0x80; i < 0x100; ++i) { + int j; + if (dmap_rec[idx].dmapD12U[i] != 0x0000) + continue; + for (j = 0x01; j < 0x100; ++j) { + dmapSrc[0]=i; + dmapSrc[1]=j; + int offset = i-0x80; + offset<<=8; + offset+=j; + + inBuf=dmapSrc; + inBytesLeft=2; + outBuf=(char *) &(dmap_rec[idx].dmapD22U[offset]); + outBytesLeft=2; + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { + if (inBytesLeft == 2 && outBytesLeft == 2 && (errno == EILSEQ || errno == EINVAL)) { + ; /* invalid DBCS character, dmapDD2U[offset] remains 0x0000 */ + } else { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(cd,%p,2,%p,2), errno = %d in %s at %d\n", + to, from, idx, + dmapSrc, &(dmap_rec[idx].dmapD22U[offset]), + errno, __FILE__,__LINE__); + MyPrintf(STDERR_WO_TIME, + "iconv(cd,0x%02x%02x,2,0x%04x,2) returns %d, inBytesLeft=%d, outBytesLeft=%d\n", + dmapSrc[0], dmapSrc[1], + dmap_rec[idx].dmapD22U[offset], + len, inBytesLeft, outBytesLeft); + } +#endif + iconv_close(cd); + return -1; + } + } else { +#ifdef TRACE_DMAP + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), rc=%d, errno=%d in %s at %d\n", + to, from, idx, len, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WITH_TIME, + "%04X: src=%04X%04X, inBuf=0x%02X%02X, inBytesLeft=%d, outBuf=%02X%02X%02X, outBytesLeft=%d\n", + i, dmapSrc[0], dmapSrc[1], inBuf[0], inBuf[1], + inBytesLeft, outBuf[-2], outBuf[-1], outBuf[0], outBytesLeft); + MyPrintf(STDERR_WITH_TIME, + "&dmapSrc=%p, inBuf=%p, %p, outBuf=%p\n", + dmapSrc, inBuf, dmap_rec[idx].dmapU2M3 + (i - 0x80) * 2, outBuf); + } +#endif + } + } + if (dmap_rec[idx].dmapD12U[i] == 0xFFFD) { /* pick the last one */ + myconv_rec[idx].srcSubD=i* 0x100 + j; + } + } + iconv_close(cd); + } + + myconv_rec[idx].subS=0x1A; + myconv_rec[idx].subD=0xFFFD; + myconv_rec[idx].srcSubD=0xFCFC; + + + } else if (((myconvIsUCS2(from) && myconvIsDBCS(to)) && (dmap_rec[idx].codingSchema = DMAP_U2D)) || + ((myconvIsUTF16(from) && myconvIsDBCS(to)) && (dmap_rec[idx].codingSchema = DMAP_T2D)) || + ((myconvIsUTF8(from) && myconvIsDBCS(to)) && (dmap_rec[idx].codingSchema = DMAP_82D))) { + /* UTF-16 -> DBCS single/double byte */ + /* A single table will cover all characters, assuming no second byte is 0x00. */ + if ((dmap_rec[idx].dmapU2D = (uchar *) alloc_root(&dmapMemRoot, 0x10000 * 2)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_U2D, errno, __FILE__,__LINE__); +#endif + return -1; + } + + memset(dmap_rec[idx].dmapU2D, 0x00, 0x10000 * 2); + myconv_rec[idx].allocatedSize=(0x10000 * 2); + + { + UniChar dmapSrc[1]; + iconv_t cd; + int32_t i; + size_t inBytesLeft; + size_t outBytesLeft; + size_t len; + char * inBuf; + char * outBuf; + +#ifdef support_surrogate + if ((cd = iconv_open(to, "UTF-16")) == (iconv_t) -1) { +#else + if ((cd = iconv_open(to, "UCS-2")) == (iconv_t) -1) { +#endif +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed with iconv_open(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); +#endif + return -1; + } + + /* easy implementation, convert 1 Unicode character at one time. */ + /* If the open performance is an issue, convert a chunk such as 128 chracters. */ + /* if the converted length is not the same as the original, convert one by one. */ + (dmap_rec[idx].dmapU2D)[0x0000]=0x00; + for (i = 1; i < 0x10000; ++i) { + dmapSrc[0]=i; + inBuf=(char *) dmapSrc; + inBytesLeft=2; + outBuf=(char *) &((dmap_rec[idx].dmapU2D)[2*i]); + outBytesLeft=2; + do { + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { + if (len == 1 && inBytesLeft == 0 && outBytesLeft == 1 && (dmap_rec[idx].dmapU2D)[2*i] == 0x1A) { + /* UCS-2_TIS-620:0x0080 => 0x1A, converted to SBCS replacement character */ + (dmap_rec[idx].dmapU2D)[2*i+1]=0x00; + break; + } else if (len == 1 && inBytesLeft == 0 && outBytesLeft == 0) { + break; + } + if (errno == EILSEQ || errno == EINVAL) { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WO_TIME, + "iconv(cd,%04x,2,%02x%02x,2) returns inBytesLeft=%d, outBytesLeft=%d\n", + dmapSrc[0], + (dmap_rec[idx].dmapU2D)[2*i], (dmap_rec[idx].dmapU2D)[2*i+1], + inBytesLeft, outBytesLeft); + if (outBuf - (char *) dmap_rec[idx].dmapU2M2 > 1) + MyPrintf(STDERR_WO_TIME, "outBuf[-2..2]=%02X%02X%02X%02X%02X\n", outBuf[-2],outBuf[-1],outBuf[0],outBuf[1],outBuf[2]); + else + MyPrintf(STDERR_WO_TIME, "outBuf[0..2]=%02X%02X%02X\n", outBuf[0],outBuf[1],outBuf[2]); + } +#endif + inBuf+=2; + inBytesLeft-=2; + memcpy(outBuf, (char *) &(myconv_rec[idx].subD), 2); + outBuf+=2; + outBytesLeft-=2; + } else { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "[%d] dmap_open(%s,%s,%d) failed to initialize with iconv(), errno = %d in %s at %d\n", + i, to, from, idx, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WO_TIME, + "iconv(cd,%04x,2,%02x%02x,2) returns %d inBytesLeft=%d, outBytesLeft=%d\n", + dmapSrc[0], + (dmap_rec[idx].dmapU2D)[2*i], + (dmap_rec[idx].dmapU2D)[2*i+1], + len, inBytesLeft,outBytesLeft); + if (i == 1) { + MyPrintf(STDERR_WO_TIME, + " inBuf [-1..2]=%02x%02x%02x%02x\n", + inBuf[-1],inBuf[0],inBuf[1],inBuf[2]); + MyPrintf(STDERR_WO_TIME, + " outBuf [-1..2]=%02x%02x%02x%02x\n", + outBuf[-1],outBuf[0],outBuf[1],outBuf[2]); + } else { + MyPrintf(STDERR_WO_TIME, + " inBuf [-2..2]=%02x%02x%02x%02x%02x\n", + inBuf[-2],inBuf[-1],inBuf[0],inBuf[1],inBuf[2]); + MyPrintf(STDERR_WO_TIME, + " outBuf [-2..2]=%02x%02x%02x%02x%02x\n", + outBuf[-2],outBuf[-1],outBuf[0],outBuf[1],outBuf[2]); + } +#endif + iconv_close(cd); + return -1; + } + if (len == 0 && inBytesLeft == 0 && outBytesLeft == 1) { /* converted to SBCS */ + (dmap_rec[idx].dmapU2D)[2*i+1]=0x00; + break; + } + } + } while (inBytesLeft > 0); + } + iconv_close(cd); + myconv_rec[idx].subS = dmap_rec[idx].dmapU2D[2*0x1A]; + myconv_rec[idx].subD = dmap_rec[idx].dmapU2D[2*0xFFFD] * 0x100 + + dmap_rec[idx].dmapU2D[2*0xFFFD+1]; + myconv_rec[idx].srcSubS = 0x1A; + myconv_rec[idx].srcSubD = 0xFFFD; + } + + + } else if (((myconvIsEUC(from) && myconvIsUnicode2(to)) && (dmap_rec[idx].codingSchema = DMAP_E2U)) || + ((myconvIsEUC(from) && myconvIsUTF8(to)) && (dmap_rec[idx].codingSchema = DMAP_E28))) { + int i; + /* S0: 0x00 - 0x7F */ + if ((dmap_rec[idx].dmapE02U = (UniChar *) alloc_root(&dmapMemRoot, 0x100 * 2)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_E2U, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapE02U, 0x00, 0x100 * 2); + + /* S1: 0xA0 - 0xFF, 0xA0 - 0xFF */ + if ((dmap_rec[idx].dmapE12U = (UniChar *) alloc_root(&dmapMemRoot, 0x60 * 0x60 * 2)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_E2U, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapE12U, 0x00, 0x60 * 0x60 * 2); + + /* SS2: 0x8E + 0xA0 - 0xFF, 0xA0 - 0xFF */ + if ((dmap_rec[idx].dmapE22U = (UniChar *) alloc_root(&dmapMemRoot, 0x60 * 0x61 * 2)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_E2U, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapE22U, 0x00, 0x60 * 0x61 * 2); + + /* SS3: 0x8F + 0xA0 - 0xFF, 0xA0 - 0xFF */ + if ((dmap_rec[idx].dmapE32U = (UniChar *) alloc_root(&dmapMemRoot, 0x60 * 0x61 * 2)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_E2U, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapE32U, 0x00, 0x60 * 0x61 * 2); + + myconv_rec[idx].allocatedSize=(0x100 + 0x60 * 0x60 + 0x60 * 0x61* 2) * 2; + + + { + char dmapSrc[0x60 * 0x60 * 3]; + iconv_t cd; + int32_t i; + size_t inBytesLeft; + size_t outBytesLeft; + size_t len; + char * inBuf; + char * outBuf; + char SS=0x8E; + +#ifdef support_surrogate + if ((cd = iconv_open("UTF-16", from)) == (iconv_t) -1) { +#else + if ((cd = iconv_open("UCS-2", from)) == (iconv_t) -1) { +#endif +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed with iconv_open(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); +#endif + return -1; + } + + for (i = 0; i < 0x100; ++i) { + dmapSrc[0]=i; + inBuf=dmapSrc; + inBytesLeft=1; + outBuf=(char *) (&dmap_rec[idx].dmapE02U[i]); + outBytesLeft=2; + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); + } +#endif + dmap_rec[idx].dmapE02U[i]=0x0000; + } + if (dmap_rec[idx].dmapE02U[i] == 0x001A && /* pick the first one */ + myconv_rec[idx].srcSubS == 0x00) { + myconv_rec[idx].srcSubS=i; + } + } + + + inBuf=dmapSrc; + for (i = 0; i < 0x60; ++i) { + int j; + for (j = 0; j < 0x60; ++j) { + *inBuf=i+0xA0; + ++inBuf; + *inBuf=j+0xA0; + ++inBuf; + } + } + inBuf=dmapSrc; + inBytesLeft=0x60 * 0x60 * 2; + outBuf=(char *) dmap_rec[idx].dmapE12U; + outBytesLeft=0x60 * 0x60 * 2; + do { + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { + if (errno == EILSEQ) { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WO_TIME, "inBytesLeft=%d, outBytesLeft=%d\n", inBytesLeft, outBytesLeft); + if (inBuf - dmapSrc > 1 && inBuf - dmapSrc <= sizeof(dmapSrc) - 2) + MyPrintf(STDERR_WO_TIME, "inBuf[-2..2]=%02X%02X%02X%02X%02X\n", inBuf[-2],inBuf[-1],inBuf[0],inBuf[1],inBuf[2]); + else + MyPrintf(STDERR_WO_TIME, "inBuf[0..2]=%02X%02X%02X\n", inBuf[0],inBuf[1],inBuf[2]); + if (outBuf - (char *) dmap_rec[idx].dmapE12U > 1) + MyPrintf(STDERR_WO_TIME, "outBuf[-2..2]=%02X%02X%02X%02X%02X\n", outBuf[-2],outBuf[-1],outBuf[0],outBuf[1],outBuf[2]); + else + MyPrintf(STDERR_WO_TIME, "outBuf[0..2]=%02X%02X%02X\n", outBuf[0],outBuf[1],outBuf[2]); + } +#endif + inBuf+=2; + inBytesLeft-=2; + outBuf[0]=0x00; + outBuf[1]=0x00; + outBuf+=2; + outBytesLeft-=2; + } else { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); +#endif + iconv_close(cd); + return -1; + } + } + } while (inBytesLeft > 0); + + /* SS2: 0x8E + 1 or 2 bytes */ + /* SS3: 0x8E + 1 or 2 bytes */ + while (SS != 0x00) { + int32_t numSuccess=0; + for (i = 0; i < 0x60; ++i) { + inBuf=dmapSrc; + inBuf[0]=SS; + inBuf[1]=i+0xA0; + inBytesLeft=2; + if (SS == 0x8E) + outBuf=(char *) &(dmap_rec[idx].dmapE22U[i]); + else + outBuf=(char *) &(dmap_rec[idx].dmapE32U[i]); + outBytesLeft=2; + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { + if (SS == 0x8E) + dmap_rec[idx].dmapE22U[i]=0x0000; + else + dmap_rec[idx].dmapE32U[i]=0x0000; + } else { + ++numSuccess; + } + } + if (numSuccess == 0) { /* SS2 is 2 bytes */ + inBuf=dmapSrc; + for (i = 0; i < 0x60; ++i) { + int j; + for (j = 0; j < 0x60; ++j) { + *inBuf=SS; + ++inBuf; + *inBuf=i+0xA0; + ++inBuf; + *inBuf=j+0xA0; + ++inBuf; + } + } + inBuf=dmapSrc; + inBytesLeft=0x60 * 0x60 * 3; + if (SS == 0x8E) + outBuf=(char *) &(dmap_rec[idx].dmapE22U[0x60]); + else + outBuf=(char *) &(dmap_rec[idx].dmapE32U[0x60]); + outBytesLeft=0x60 * 0x60 * 2; + do { + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "%02X:dmap_open(%s,%s,%d) failed to initialize with iconv(), errno = %d in %s at %d\n", + SS, to, from, idx, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WO_TIME, "inBytesLeft=%d, outBytesLeft=%d\n", inBytesLeft, outBytesLeft); + if (inBuf - dmapSrc > 1 && inBuf - dmapSrc <= sizeof(dmapSrc) - 2) + MyPrintf(STDERR_WO_TIME, "inBuf[-2..2]=%02X%02X%02X%02X%02X\n", inBuf[-2],inBuf[-1],inBuf[0],inBuf[1],inBuf[2]); + else + MyPrintf(STDERR_WO_TIME, "inBuf[0..2]=%02X%02X%02X\n", inBuf[0],inBuf[1],inBuf[2]); + } +#endif + if (errno == EILSEQ || errno == EINVAL) { + inBuf+=3; + inBytesLeft-=3; + outBuf[0]=0x00; + outBuf[1]=0x00; + outBuf+=2; + outBytesLeft-=2; + } else { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "%02X:dmap_open(%s,%s,%d) failed to initialize with iconv(), errno = %d in %s at %d\n", + SS, to, from, idx, errno, __FILE__,__LINE__); +#endif + iconv_close(cd); + return -1; + } + } + } while (inBytesLeft > 0); + } + if (SS == 0x8E) + SS=0x8F; + else + SS = 0x00; + } + iconv_close(cd); + + myconv_rec[idx].subS=0x1A; + myconv_rec[idx].subD=0xFFFD; + for (i = 0; i < 0x80; ++i) { + if (dmap_rec[idx].dmapE02U[i] == 0x001A) { + myconv_rec[idx].srcSubS=i; /* pick the first one */ + break; + } + } + + for (i = 0; i < 0x60 * 0x60; ++i) { + if (dmap_rec[idx].dmapE12U[i] == 0xFFFD) { + uchar byte1=i / 0x60; + uchar byte2=i % 0x60; + myconv_rec[idx].srcSubD=(byte1 + 0xA0) * 0x100 + (byte2 + 0xA0); /* pick the last one */ + } + } + + } + + } else if (((myconvIsUCS2(from) && myconvIsEUC(to)) && (dmap_rec[idx].codingSchema = DMAP_U2E)) || + ((myconvIsUTF16(from) && myconvIsEUC(to)) && (dmap_rec[idx].codingSchema = DMAP_T2E)) || + ((myconvIsUTF8(from) && myconvIsEUC(to)) && (dmap_rec[idx].codingSchema = DMAP_82E))) { + /* S0: 0x00 - 0xFF */ + if ((dmap_rec[idx].dmapU2S = (uchar *) alloc_root(&dmapMemRoot, 0x100)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_U2E, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapU2S, 0x00, 0x100); + + /* U0080 - UFFFF -> S1: 0xA0 - 0xFF, 0xA0 - 0xFF */ + if ((dmap_rec[idx].dmapU2M2 = (uchar *) alloc_root(&dmapMemRoot, 0xFF80 * 2)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_U2E, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapU2M2, 0x00, 0xFF80 * 2); + + /* U0080 - UFFFF -> SS2: 0x8E + 0xA0 - 0xFF, 0xA0 - 0xFF + * SS3: 0x8F + 0xA0 - 0xFF, 0xA0 - 0xFF */ + if ((dmap_rec[idx].dmapU2M3 = (uchar *) alloc_root(&dmapMemRoot, 0xFF80 * 3)) == NULL) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d), CS=%d failed with malloc(), errno = %d in %s at %d\n", + to, from, idx, DMAP_U2E, errno, __FILE__,__LINE__); +#endif + return -1; + } + memset(dmap_rec[idx].dmapU2M3, 0x00, 0xFF80 * 3); + myconv_rec[idx].allocatedSize=(0x100 + 0xFF80 * 2 + 0xFF80 * 3); + + { + UniChar dmapSrc[0x80]; + iconv_t cd; + int32_t i; + size_t inBytesLeft; + size_t outBytesLeft; + size_t len; + char * inBuf; + char * outBuf; + +#ifdef support_surrogate + if ((cd = iconv_open(to, "UTF-16")) == (iconv_t) -1) { +#else + if ((cd = iconv_open(to, "UCS-2")) == (iconv_t) -1) { +#endif +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed with iconv_open(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); +#endif + return -1; + } + + for (i = 0; i < 0x80; ++i) + dmapSrc[i]=i; + inBuf=(char *) dmapSrc; + inBytesLeft=0x80 * 2; + outBuf=(char *) dmap_rec[idx].dmapU2S; + outBytesLeft=0x80; + do { + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { +#ifdef DEBUG + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); +#endif + iconv_close(cd); + return -1; + } + } while (inBytesLeft > 0); + + myconv_rec[idx].srcSubS = 0x1A; + myconv_rec[idx].srcSubD = 0xFFFD; + myconv_rec[idx].subS = dmap_rec[idx].dmapU2S[0x1A]; + + outBuf=(char *) &(myconv_rec[idx].subD); + dmapSrc[0]=0xFFFD; + inBuf=(char *) dmapSrc; + inBytesLeft=2; + outBytesLeft=2; + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), rc=%d, errno=%d in %s at %d\n", + to, from, idx, len, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WO_TIME, "iconv(0x1A,1,%p,1) returns outBuf=%p, outBytesLeft=%d\n", + dmapSrc, outBuf, outBytesLeft); + } +#endif + if (outBytesLeft == 0) { + /* UCS-2_IBM-eucKR returns error. + myconv(iconv) rc=1, error=0, InBytesLeft=0, OutBytesLeft=18 + myconv(iconvRev) rc=-1, error=116, InBytesLeft=2, OutBytesLeft=20 + iconv: 0xFFFD => 0xAFFE => 0x rc=1,-1 sub=0,0 + */ + ; + } else { + iconv_close(cd); + return -1; + } + } + + for (i = 0x80; i < 0xFFFF; ++i) { + uchar eucBuf[3]; + dmapSrc[0]=i; + inBuf=(char *) dmapSrc; + inBytesLeft=2; + outBuf=(char *) eucBuf; + outBytesLeft=sizeof(eucBuf); + errno=0; + if ((len = iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft)) != (size_t) 0) { + if (len == 1 && errno == 0 && inBytesLeft == 0 && outBytesLeft == 1) { /* substitution occurred. */ continue; + } + + if (errno == EILSEQ) { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), errno = %d in %s at %d\n", + to, from, idx, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WO_TIME, "inBytesLeft=%d, outBytesLeft=%d\n", inBytesLeft, outBytesLeft); + if (inBuf - (char *) dmapSrc > 1 && inBuf - (char *) dmapSrc <= sizeof(dmapSrc) - 2) + MyPrintf(STDERR_WO_TIME, "inBuf[-2..2]=%02X%02X%02X%02X%02X\n", inBuf[-2],inBuf[-1],inBuf[0],inBuf[1],inBuf[2]); + else + MyPrintf(STDERR_WO_TIME, "inBuf[0..2]=%02X%02X%02X\n", inBuf[0],inBuf[1],inBuf[2]); + if (outBuf - (char *) dmap_rec[idx].dmapU2M2 > 1) + MyPrintf(STDERR_WO_TIME, "outBuf[-2..2]=%02X%02X%02X%02X%02X\n", outBuf[-2],outBuf[-1],outBuf[0],outBuf[1],outBuf[2]); + else + MyPrintf(STDERR_WO_TIME, "outBuf[0..2]=%02X%02X%02X\n", outBuf[0],outBuf[1],outBuf[2]); + } +#endif + inBuf+=2; + inBytesLeft-=2; + memcpy(outBuf, (char *) &(myconv_rec[idx].subD), 2); + outBuf+=2; + outBytesLeft-=2; + } else { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), rc = %d, errno = %d in %s at %d\n", + to, from, idx, len, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WITH_TIME, + "%04X: src=%04X%04X, inBuf=0x%02X%02X, inBytesLeft=%d, outBuf[-2..0]=%02X%02X%02X, outBytesLeft=%d\n", + i, dmapSrc[0], dmapSrc[1], inBuf[0], inBuf[1], + inBytesLeft, outBuf[-2], outBuf[-1], outBuf[0], outBytesLeft); + MyPrintf(STDERR_WITH_TIME, + "&dmapSrc=%p, inBuf=%p, dmapU2M2 + %d = %p, outBuf=%p\n", + dmapSrc, inBuf, (i - 0x80) * 2, dmap_rec[idx].dmapU2M2 + (i - 0x80) * 2, outBuf); + } +#endif + iconv_close(cd); + return -1; + } + } + if (sizeof(eucBuf) - outBytesLeft == 1) { + if (i < 0x100) { + (dmap_rec[idx].dmapU2S)[i]=eucBuf[0]; + } else { + dmap_rec[idx].dmapU2M2[(i - 0x80) * 2] = eucBuf[0]; + dmap_rec[idx].dmapU2M2[(i - 0x80) * 2 + 1] = 0x00; + } + } else if (sizeof(eucBuf) - outBytesLeft == 2) { /* 2 bytes */ + dmap_rec[idx].dmapU2M2[(i - 0x80) * 2] = eucBuf[0]; + dmap_rec[idx].dmapU2M2[(i - 0x80) * 2 + 1] = eucBuf[1]; + } else if (sizeof(eucBuf) - outBytesLeft == 3) { /* 3 byte SS2/SS3 */ + dmap_rec[idx].dmapU2M3[(i - 0x80) * 3] = eucBuf[0]; + dmap_rec[idx].dmapU2M3[(i - 0x80) * 3 + 1] = eucBuf[1]; + dmap_rec[idx].dmapU2M3[(i - 0x80) * 3 + 2] = eucBuf[2]; + } else { +#ifdef DEBUG + if (myconvDebug) { + MyPrintf(STDERR_WITH_TIME, + "dmap_open(%s,%s,%d) failed to initialize with iconv(), rc=%d, errno=%d in %s at %d\n", + to, from, idx, len, errno, __FILE__,__LINE__); + MyPrintf(STDERR_WITH_TIME, + "%04X: src=%04X%04X, inBuf=0x%02X%02X, inBytesLeft=%d, outBuf=%02X%02X%02X, outBytesLeft=%d\n", + i, dmapSrc[0], dmapSrc[1], inBuf[0], inBuf[1], + inBytesLeft, outBuf[-2], outBuf[-1], outBuf[0], outBytesLeft); + MyPrintf(STDERR_WITH_TIME, + "&dmapSrc=%p, inBuf=%p, %p, outBuf=%p\n", + dmapSrc, inBuf, dmap_rec[idx].dmapU2M3 + (i - 0x80) * 2, outBuf); + } +#endif + return -1; + } + + } + iconv_close(cd); + } + + } else if (myconvIsUTF16(from) && myconvIsUTF8(to)) { + dmap_rec[idx].codingSchema = DMAP_T28; + + } else if (myconvIsUCS2(from) && myconvIsUTF8(to)) { + dmap_rec[idx].codingSchema = DMAP_U28; + + } else if (myconvIsUTF8(from) && myconvIsUnicode2(to)) { + dmap_rec[idx].codingSchema = DMAP_82U; + + } else if (myconvIsUnicode2(from) && myconvIsUnicode2(to)) { + dmap_rec[idx].codingSchema = DMAP_U2U; + + } else { + + return -1; + } + myconv_rec[idx].cnv_dmap=&(dmap_rec[idx]); + return 0; +} + + + +static int bins_open(const char * to, + const char * from, + const int32_t idx) +{ + return -1; +} + + + +static int32_t dmap_close(const int32_t idx) +{ + if (dmap_rec[idx].codingSchema == DMAP_S2S) { + if (dmap_rec[idx].dmapS2S != NULL) { + dmap_rec[idx].dmapS2S=NULL; + } + } else if (dmap_rec[idx].codingSchema = DMAP_E2U) { + if (dmap_rec[idx].dmapE02U != NULL) { + dmap_rec[idx].dmapE02U=NULL; + } + if (dmap_rec[idx].dmapE12U != NULL) { + dmap_rec[idx].dmapE12U=NULL; + } + if (dmap_rec[idx].dmapE22U != NULL) { + dmap_rec[idx].dmapE22U=NULL; + } + if (dmap_rec[idx].dmapE32U != NULL) { + dmap_rec[idx].dmapE32U=NULL; + } + } + + return 0; +} + + +static int32_t bins_close(const int32_t idx) +{ + return 0; +} + + +myconv_t myconv_open(const char * toCode, + const char * fromCode, + int32_t converter) +{ + int32 i; + for (i = 0; i < MAX_CONVERTER; ++i) { + if (myconv_rec[i].converterType == 0) + break; + } + if (i >= MAX_CONVERTER) + return ((myconv_t) -1); + + myconv_rec[i].converterType = converter; + myconv_rec[i].index=i; + myconv_rec[i].fromCcsid=cstoccsid(fromCode); + if (myconv_rec[i].fromCcsid == 0 && memcmp(fromCode, "big5",5) == 0) + myconv_rec[i].fromCcsid=950; + myconv_rec[i].toCcsid=cstoccsid(toCode); + if (myconv_rec[i].toCcsid == 0 && memcmp(toCode, "big5",5) == 0) + myconv_rec[i].toCcsid=950; + strncpy(myconv_rec[i].from, fromCode, sizeof(myconv_rec[i].from)-1); + strncpy(myconv_rec[i].to, toCode, sizeof(myconv_rec[i].to)-1); + + if (converter == CONVERTER_ICONV) { + if ((myconv_rec[i].cnv_iconv=iconv_open(toCode, fromCode)) == (iconv_t) -1) { + return ((myconv_t) -1); + } + myconv_rec[i].allocatedSize = -1; + myconv_rec[i].srcSubS=myconvGetSubS(fromCode); + myconv_rec[i].srcSubD=myconvGetSubD(fromCode); + myconv_rec[i].subS=myconvGetSubS(toCode); + myconv_rec[i].subD=myconvGetSubD(toCode); + return &(myconv_rec[i]); + } else if (converter == CONVERTER_DMAP && + dmap_open(toCode, fromCode, i) != -1) { + return &(myconv_rec[i]); + } + return ((myconv_t) -1); +} + + + +int32_t myconv_close(myconv_t cd) +{ + int32_t ret=0; + + if (cd->converterType == CONVERTER_ICONV) { + ret=iconv_close(cd->cnv_iconv); + } else if (cd->converterType == CONVERTER_DMAP) { + ret=dmap_close(cd->index); + } + memset(&(myconv_rec[cd->index]), 0x00, sizeof(myconv_rec[cd->index])); + return ret; +} + + + + +/* reference: http://www-306.ibm.com/software/globalization/other/es.jsp */ +/* systemCL would be expensive, and myconvIsXXXXX is called frequently. + need to cache entries */ +#define MAX_CCSID 256 +static int ccsidList [MAX_CCSID]; +static int esList [MAX_CCSID]; +int32 getEncodingScheme(const uint16 inCcsid, int32& outEncodingScheme); +EXTERN int myconvGetES(CCSID ccsid) +{ + /* call QtqValidateCCSID in ILE to get encoding schema */ + /* return QtqValidateCCSID(ccsid); */ + int i; + for (i = 0; i < MAX_CCSID; ++i) { + if (ccsidList[i] == ccsid) + return esList[i]; + if (ccsidList[i] == 0x00) + break; + } + + if (i >= MAX_CCSID) { + i=MAX_CCSID-1; + } + + { + ccsidList[i]=ccsid; + getEncodingScheme(ccsid, esList[i]); +#ifdef DEBUG_PASE + if (myconvDebug) { + fprintf(stderr, "CCSID=%d, ES=0x%04X\n", ccsid, esList[i]); + } +#endif + return esList[i]; + } + return 0; +} + + +EXTERN int myconvIsEBCDIC(const char * pName) +{ + int es = myconvGetES(cstoccsid(pName)); + if (es == 0x1100 || + es == 0x1200 || + es == 0x6100 || + es == 0x6200 || + es == 0x1301 ) { + return TRUE; + } + return FALSE; +} + + +EXTERN int myconvIsISO(const char * pName) +{ + int es = myconvGetES(cstoccsid(pName)); + if (es == 0x4100 || + es == 0x4105 || + es == 0x4155 || + es == 0x5100 || + es == 0x5150 || + es == 0x5200 || + es == 0x5404 || + es == 0x5409 || + es == 0x540A || + es == 0x5700) { + return TRUE; + } + return FALSE; +} + + +EXTERN int myconvIsASCII(const char * pName) +{ + int es = myconvGetES(cstoccsid(pName)); + if (es == 0x2100 || + es == 0x3100 || + es == 0x8100 || + es == 0x2200 || + es == 0x3200 || + es == 0x9200 || + es == 0x2300 || + es == 0x2305 || + es == 0x3300 || + es == 0x2900 || + es == 0x2A00) { + return TRUE; + } else if (memcmp(pName, "big5", 5) == 0) { + return TRUE; + } + return FALSE; +} + + + +EXTERN int myconvIsUCS2(const char * pName) +{ + if (cstoccsid(pName) == 13488) { + return TRUE; + } + return FALSE; +} + + +EXTERN int myconvIsUTF16(const char * pName) +{ + if (cstoccsid(pName) == 1200) { + return TRUE; + } + return FALSE; +} + + +EXTERN int myconvIsUnicode2(const char * pName) +{ + int es = myconvGetES(cstoccsid(pName)); + if (es == 0x7200 || + es == 0x720B || + es == 0x720F) { + return TRUE; + } + return FALSE; +} + + +EXTERN int myconvIsUTF8(const char * pName) +{ + int es = myconvGetES(cstoccsid(pName)); + if (es == 0x7807) { + return TRUE; + } + return FALSE; +} + + +EXTERN int myconvIsUnicode(const char * pName) +{ + int es = myconvGetES(cstoccsid(pName)); + if (es == 0x7200 || + es == 0x720B || + es == 0x720F || + es == 0x7807) { + return TRUE; + } + return FALSE; +} + + +EXTERN int myconvIsEUC(const char * pName) +{ + int es = myconvGetES(cstoccsid(pName)); + if (es == 0x4403) { + return TRUE; + } + return FALSE; +} + + +EXTERN int myconvIsDBCS(const char * pName) +{ + int es = myconvGetES(cstoccsid(pName)); + if (es == 0x1200 || + es == 0x2200 || + es == 0x2300 || + es == 0x2305 || + es == 0x2A00 || + es == 0x3200 || + es == 0x3300 || + es == 0x5200 || + es == 0x6200 || + es == 0x9200) { + return TRUE; + } else if (memcmp(pName, "big5", 5) == 0) { + return TRUE; + } + return FALSE; +} + + +EXTERN int myconvIsSBCS(const char * pName) +{ + int es = myconvGetES(cstoccsid(pName)); + if (es == 0x1100 || + es == 0x2100 || + es == 0x3100 || + es == 0x4100 || + es == 0x4105 || + es == 0x5100 || + es == 0x5150 || + es == 0x6100 || + es == 0x8100) { + return TRUE; + } + return FALSE; +} + + + +EXTERN char myconvGetSubS(const char * code) +{ + if (myconvIsEBCDIC(code)) { + return 0x3F; + } else if (myconvIsASCII(code)) { + return 0x1A; + } else if (myconvIsISO(code)) { + return 0x1A; + } else if (myconvIsEUC(code)) { + return 0x1A; + } else if (myconvIsUCS2(code)) { + return 0x00; + } else if (myconvIsUTF8(code)) { + return 0x1A; + } + return 0x00; +} + + +EXTERN UniChar myconvGetSubD(const char * code) +{ + if (myconvIsEBCDIC(code)) { + return 0xFDFD; + } else if (myconvIsASCII(code)) { + return 0xFCFC; + } else if (myconvIsISO(code)) { + return 0x00; + } else if (myconvIsEUC(code)) { + return 0x00; + } else if (myconvIsUCS2(code)) { + return 0xFFFD; + } else if (myconvIsUTF8(code)) { + return 0x00; + } + return 0x00; +} + diff --git a/storage/ibmdb2i/db2i_myconv.h b/storage/ibmdb2i/db2i_myconv.h new file mode 100644 index 00000000000..a9e87474505 --- /dev/null +++ b/storage/ibmdb2i/db2i_myconv.h @@ -0,0 +1,3200 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +/** + @file + + @brief A direct map optimization of iconv and related functions + This was show to significantly reduce character conversion cost + for short strings when compared to calling iconv system code. +*/ + +#ifndef DB2I_MYCONV_H +#define DB2I_MYCONV_H + + +#include <sys/time.h> +#include <stdlib.h> +#include <stdio.h> +#include <wchar.h> +#include <errno.h> +#include <iconv.h> +#include <ctype.h> +#include <time.h> +#include <stdarg.h> +#include <string.h> + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifdef __cplusplus +#define INTERN inline +#define EXTERN extern "C" +#else +#define INTERN static +#define EXTERN extern +#endif + + +/* ANSI integer data types */ +#if defined(__OS400_TGTVRM__) +/* for DTAMDL(*P128), datamodel(P128): int/long/pointer=4/4/16 */ +/* LLP64:4/4/8 is used for teraspace ?? */ +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +#elif defined(PASE) +/* PASE uses IPL32: int/long/pointer=4/4/4 + long long */ +#elif defined(__64BIT__) +/* AIX 64 bit uses LP64: int/long/pointer=4/8/8 */ +#endif + +#define CONVERTER_ICONV 1 +#define CONVERTER_DMAP 2 + +#define DMAP_S2S 10 +#define DMAP_S2U 20 +#define DMAP_D2U 30 +#define DMAP_E2U 40 +#define DMAP_U2S 120 +#define DMAP_T2S 125 +#define DMAP_U2D 130 +#define DMAP_T2D 135 +#define DMAP_U2E 140 +#define DMAP_T2E 145 +#define DMAP_S28 220 +#define DMAP_D28 230 +#define DMAP_E28 240 +#define DMAP_82S 310 +#define DMAP_82D 320 +#define DMAP_82E 330 +#define DMAP_U28 410 +#define DMAP_82U 420 +#define DMAP_T28 425 +#define DMAP_U2U 510 + + +typedef struct __dmap_rec *dmap_t; + +struct __dmap_rec +{ + uint32_t codingSchema; + unsigned char * dmapS2S; /* SBCS -> SBCS */ + /* The following conversion needs be followed by conversion from UCS-2/UTF-16 to UTF-8 */ + UniChar * dmapD12U; /* DBCS(non-EUC) -> UCS-2/UTF-16 */ + UniChar * dmapD22U; /* DBCS(non-EUC) -> UCS-2/UTF-16 */ + UniChar * dmapE02U; /* EUC/SS0 -> UCS-2/UTF-16 */ + UniChar * dmapE12U; /* EUC/SS1 -> UCS-2/UTF-16 */ + UniChar * dmapE22U; /* EUC/0x8E + SS2 -> UCS-2/UTF-16 */ + UniChar * dmapE32U; /* EUC/0x8F + SS3 -> UCS-2/UTF-16 */ + uchar * dmapU2D; /* UCS-2 -> DBCS */ + uchar * dmapU2S; /* UCS-2 -> EUC SS0 */ + uchar * dmapU2M2; /* UCS-2 -> EUC SS1 */ + uchar * dmapU2M3; /* UCS-2 -> EUC SS2/SS3 */ + /* All of these pointers/tables are not used at the same time. + * You may be able save some space if you consolidate them. + */ + uchar * dmapS28; /* SBCS -> UTF-8 */ + uchar * dmapD28; /* DBCS -> UTF-8 */ +}; + +typedef struct __myconv_rec *myconv_t; +struct __myconv_rec +{ + uint32_t converterType; + uint32_t index; /* for close */ + union { + iconv_t cnv_iconv; + dmap_t cnv_dmap; + }; + int32_t allocatedSize; + int32_t fromCcsid; + int32_t toCcsid; + UniChar subD; /* DBCS substitution char */ + char subS; /* SBCS substitution char */ + UniChar srcSubD; /* DBCS substitution char of src codepage */ + char srcSubS; /* SBCS substitution char of src codepage */ + char from [41+1]; /* codepage name is up to 41 bytes */ + char to [41+1]; /* codepage name is up to 41 bytes */ +#ifdef __64BIT__ + char reserved[10]; /* align 128 */ +#else + char reserved[14]; /* align 128 */ +#endif +}; + + +EXTERN int32_t myconvDebug; + + + +EXTERN int myconvGetES(CCSID); +EXTERN int myconvIsEBCDIC(const char *); +EXTERN int myconvIsASCII(const char *); +EXTERN int myconvIsUnicode(const char *); /* UTF-8, UTF-16, or UCS-2 */ +EXTERN int myconvIsUnicode2(const char *); /* 2 byte Unicode */ +EXTERN int myconvIsUCS2(const char *); +EXTERN int myconvIsUTF16(const char *); +EXTERN int myconvIsUTF8(const char *); +EXTERN int myconvIsEUC(const char *); +EXTERN int myconvIsISO(const char *); +EXTERN int myconvIsSBCS(const char *); +EXTERN int myconvIsDBCS(const char *); +EXTERN char myconvGetSubS(const char *); +EXTERN UniChar myconvGetSubD(const char *); + + +EXTERN myconv_t myconv_open(const char*, const char*, int32_t); +EXTERN int myconv_close(myconv_t); + +INTERN size_t myconv_iconv(myconv_t cd , + char** inBuf, + size_t* inBytesLeft, + char** outBuf, + size_t* outBytesLeft, + size_t* numSub) +{ + return iconv(cd->cnv_iconv, inBuf, inBytesLeft, outBuf, outBytesLeft); +} + +INTERN size_t myconv_dmap(myconv_t cd, + char** inBuf, + size_t* inBytesLeft, + char** outBuf, + size_t* outBytesLeft, + size_t* numSub) +{ + if (cd->cnv_dmap->codingSchema == DMAP_S2S) { + register unsigned char * dmapS2S=cd->cnv_dmap->dmapS2S; + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register char subS=cd->subS; + register size_t numS=0; + while (0 < inLen) { + if (pLastOutBuf < pOut) + break; + if (*pIn == 0x00) { + *pOut=0x00; + } else { + *pOut=dmapS2S[*pIn]; + if (*pOut == 0x00) { + *outBytesLeft-=(*inBytesLeft-inLen); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + if (*pOut == subS) { + if ((*pOut=dmapS2S[*pIn]) == subS) { + if (*pIn != cd->srcSubS) + ++numS; + } + } + } + ++pIn; + --inLen; + ++pOut; + } + *outBytesLeft-=(*inBytesLeft-inLen); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_E2U) { + /* use uchar * instead of UniChar to avoid memcpy */ + register uchar * dmapE02U=(uchar *) (cd->cnv_dmap->dmapE02U); + register uchar * dmapE12U=(uchar *) (cd->cnv_dmap->dmapE12U); + register uchar * dmapE22U=(uchar *) (cd->cnv_dmap->dmapE22U); + register uchar * dmapE32U=(uchar *) (cd->cnv_dmap->dmapE32U); + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register int offset; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register size_t numS=0; + while (0 < inLen) { + if (pLastOutBuf < pOut) + break; + if (*pIn == 0x00) { + *pOut=0x00; + ++pOut; + *pOut=0x00; + ++pOut; + ++pIn; + --inLen; + } else { + if (*pIn == 0x8E) { /* SS2 */ + if (inLen < 2) { + if (cd->fromCcsid == 33722 || /* IBM-eucJP */ + cd->fromCcsid == 964) /* IBM-eucTW */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + ++pIn; + if (*pIn < 0xA0) { + if (cd->fromCcsid == 964) /* IBM-eucTW */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + offset=(*pIn - 0xA0); + offset<<=1; + if (dmapE22U[offset] == 0x00 && + dmapE22U[offset+1] == 0x00) { /* 2 bytes */ + if (inLen < 3) { + if (cd->fromCcsid == 964) /* IBM-eucTW */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + offset=(*pIn - 0xA0) * 0x60 + 0x60; + ++pIn; + if (*pIn < 0xA0) { + if (cd->fromCcsid == 964) /* IBM-eucTW */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + return -1; + } + offset+=(*pIn - 0xA0); + offset<<=1; + if (dmapE22U[offset] == 0x00 && + dmapE22U[offset+1] == 0x00) { + if (cd->fromCcsid == 964) /* IBM-eucTW */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + return -1; + } + *pOut=dmapE22U[offset]; + ++pOut; + *pOut=dmapE22U[offset+1]; + ++pOut; + if (dmapE22U[offset] == 0xFF && + dmapE22U[offset+1] == 0xFD) { + if (pIn[-2] * 0x100 + pIn[-1] != cd->srcSubD) + ++numS; + } + ++pIn; + inLen-=3; + } else { /* 1 bytes */ + *pOut=dmapE22U[offset]; + ++pOut; + *pOut=dmapE22U[offset+1]; + ++pOut; + ++pIn; + inLen-=2; + } + } else if (*pIn == 0x8F) { /* SS3 */ + if (inLen < 2) { + if (cd->fromCcsid == 33722) /* IBM-eucJP */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + ++pIn; + if (*pIn < 0xA0) { + if (cd->fromCcsid == 970 || /* IBM-eucKR */ + cd->fromCcsid == 964 || /* IBM-eucTW */ + cd->fromCcsid == 1383 || /* IBM-eucCN */ + (cd->fromCcsid == 33722 && 3 <= inLen)) /* IBM-eucJP */ + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + offset=(*pIn - 0xA0); + offset<<=1; + if (dmapE32U[offset] == 0x00 && + dmapE32U[offset+1] == 0x00) { /* 0x8F + 2 bytes */ + if (inLen < 3) { + if (cd->fromCcsid == 33722) + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + offset=(*pIn - 0xA0) * 0x60 + 0x60; + ++pIn; + if (*pIn < 0xA0) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + return -1; + } + offset+=(*pIn - 0xA0); + offset<<=1; + if (dmapE32U[offset] == 0x00 && + dmapE32U[offset+1] == 0x00) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + return -1; + } + *pOut=dmapE32U[offset]; + ++pOut; + *pOut=dmapE32U[offset+1]; + ++pOut; + if (dmapE32U[offset] == 0xFF && + dmapE32U[offset+1] == 0xFD) { + if (pIn[-2] * 0x100 + pIn[-1] != cd->srcSubD) + ++numS; + } + ++pIn; + inLen-=3; + } else { /* 0x8F + 1 bytes */ + *pOut=dmapE32U[offset]; + ++pOut; + *pOut=dmapE32U[offset+1]; + ++pOut; + ++pIn; + inLen-=2; + } + + } else { + offset=*pIn; + offset<<=1; + if (dmapE02U[offset] == 0x00 && + dmapE02U[offset+1] == 0x00) { /* SS1 */ + if (inLen < 2) { + if ((cd->fromCcsid == 33722 && (*pIn == 0xA0 || (0xA9 <= *pIn && *pIn <= 0xAF) || *pIn == 0xFF)) || + (cd->fromCcsid == 970 && (*pIn == 0xA0 || *pIn == 0xAD || *pIn == 0xAE || *pIn == 0xAF || *pIn == 0xFF)) || + (cd->fromCcsid == 964 && (*pIn == 0xA0 || (0xAA <= *pIn && *pIn <= 0xC1) || *pIn == 0xC3 || *pIn == 0xFE || *pIn == 0xFF)) || + (cd->fromCcsid == 1383 && (*pIn == 0xA0 || *pIn == 0xFF))) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + if (*pIn < 0xA0) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + offset=(*pIn - 0xA0) * 0x60; + ++pIn; + if (*pIn < 0xA0) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + offset+=(*pIn - 0xA0); + offset<<=1; + if (dmapE12U[offset] == 0x00 && + dmapE12U[offset+1] == 0x00) { /* undefined mapping */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + *pOut=dmapE12U[offset]; + ++pOut; + *pOut=dmapE12U[offset+1]; + ++pOut; + if (dmapE12U[offset] == 0xFF && + dmapE12U[offset+1] == 0xFD) { + if (pIn[-1] * 0x100 + pIn[0] != cd->srcSubD) + ++numS; + } + ++pIn; + inLen-=2; + } else { + *pOut=dmapE02U[offset]; + ++pOut; + *pOut=dmapE02U[offset+1]; + ++pOut; + if (dmapE02U[offset] == 0x00 && + dmapE02U[offset+1] == 0x1A) { + if (*pIn != cd->srcSubS) + ++numS; + } + ++pIn; + --inLen; + } + } + } + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + + } else if (cd->cnv_dmap->codingSchema == DMAP_E28) { + /* use uchar * instead of UniChar to avoid memcpy */ + register uchar * dmapE02U=(uchar *) (cd->cnv_dmap->dmapE02U); + register uchar * dmapE12U=(uchar *) (cd->cnv_dmap->dmapE12U); + register uchar * dmapE22U=(uchar *) (cd->cnv_dmap->dmapE22U); + register uchar * dmapE32U=(uchar *) (cd->cnv_dmap->dmapE32U); + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register int offset; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register size_t numS=0; + register UniChar in; /* copy part of U28 */ + register UniChar ucs2; + while (0 < inLen) { + if (pLastOutBuf < pOut) + break; + if (*pIn == 0x00) { + *pOut=0x00; + ++pOut; + ++pIn; + --inLen; + } else { + if (*pIn == 0x8E) { /* SS2 */ + if (inLen < 2) { + if (cd->fromCcsid == 33722 || /* IBM-eucJP */ + cd->fromCcsid == 964) /* IBM-eucTW */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + ++pIn; + if (*pIn < 0xA0) { + if (cd->fromCcsid == 964) /* IBM-eucTW */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + offset=(*pIn - 0xA0); + offset<<=1; + if (dmapE22U[offset] == 0x00 && + dmapE22U[offset+1] == 0x00) { /* 2 bytes */ + if (inLen < 3) { + if (cd->fromCcsid == 964) /* IBM-eucTW */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + offset=(*pIn - 0xA0) * 0x60 + 0x60; + ++pIn; + if (*pIn < 0xA0) { + if (cd->fromCcsid == 964) /* IBM-eucTW */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + return -1; + } + offset+=(*pIn - 0xA0); + offset<<=1; + if (dmapE22U[offset] == 0x00 && + dmapE22U[offset+1] == 0x00) { + if (cd->fromCcsid == 964) /* IBM-eucTW */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + return -1; + } + in=dmapE22U[offset]; + in<<=8; + in+=dmapE22U[offset+1]; + if (dmapE22U[offset] == 0xFF && + dmapE22U[offset+1] == 0xFD) { + if (pIn[-2] * 0x100 + pIn[-1] != cd->srcSubD) + ++numS; + } + ++pIn; + inLen-=3; + } else { /* 1 bytes */ + in=dmapE22U[offset]; + in<<=8; + in+=dmapE22U[offset+1]; + ++pIn; + inLen-=2; + } + } else if (*pIn == 0x8F) { /* SS3 */ + if (inLen < 2) { + if (cd->fromCcsid == 33722) /* IBM-eucJP */ + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + ++pIn; + if (*pIn < 0xA0) { + if (cd->fromCcsid == 970 || /* IBM-eucKR */ + cd->fromCcsid == 964 || /* IBM-eucTW */ + cd->fromCcsid == 1383 || /* IBM-eucCN */ + (cd->fromCcsid == 33722 && 3 <= inLen)) /* IBM-eucJP */ + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + offset=(*pIn - 0xA0); + offset<<=1; + if (dmapE32U[offset] == 0x00 && + dmapE32U[offset+1] == 0x00) { /* 0x8F + 2 bytes */ + if (inLen < 3) { + if (cd->fromCcsid == 33722) + errno=EINVAL; /* 22 */ + else + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + offset=(*pIn - 0xA0) * 0x60 + 0x60; + ++pIn; + if (*pIn < 0xA0) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + return -1; + } + offset+=(*pIn - 0xA0); + offset<<=1; + if (dmapE32U[offset] == 0x00 && + dmapE32U[offset+1] == 0x00) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + return -1; + } + in=dmapE32U[offset]; + in<<=8; + in+=dmapE32U[offset+1]; + if (dmapE32U[offset] == 0xFF && + dmapE32U[offset+1] == 0xFD) { + if (pIn[-2] * 0x100 + pIn[-1] != cd->srcSubD) + ++numS; + } + ++pIn; + inLen-=3; + } else { /* 0x8F + 1 bytes */ + in=dmapE32U[offset]; + in<<=8; + in+=dmapE32U[offset+1]; + ++pIn; + inLen-=2; + } + + } else { + offset=*pIn; + offset<<=1; + if (dmapE02U[offset] == 0x00 && + dmapE02U[offset+1] == 0x00) { /* SS1 */ + if (inLen < 2) { + if ((cd->fromCcsid == 33722 && (*pIn == 0xA0 || (0xA9 <= *pIn && *pIn <= 0xAF) || *pIn == 0xFF)) || + (cd->fromCcsid == 970 && (*pIn == 0xA0 || *pIn == 0xAD || *pIn == 0xAE || *pIn == 0xAF || *pIn == 0xFF)) || + (cd->fromCcsid == 964 && (*pIn == 0xA0 || (0xAA <= *pIn && *pIn <= 0xC1) || *pIn == 0xC3 || *pIn == 0xFE || *pIn == 0xFF)) || + (cd->fromCcsid == 1383 && (*pIn == 0xA0 || *pIn == 0xFF))) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + if (*pIn < 0xA0) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + offset=(*pIn - 0xA0) * 0x60; + ++pIn; + if (*pIn < 0xA0) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + offset+=(*pIn - 0xA0); + offset<<=1; + if (dmapE12U[offset] == 0x00 && + dmapE12U[offset+1] == 0x00) { /* undefined mapping */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + in=dmapE12U[offset]; + in<<=8; + in+=dmapE12U[offset+1]; + if (dmapE12U[offset] == 0xFF && + dmapE12U[offset+1] == 0xFD) { + if (pIn[-1] * 0x100 + pIn[0] != cd->srcSubD) + ++numS; + } + ++pIn; + inLen-=2; + } else { + in=dmapE02U[offset]; + in<<=8; + in+=dmapE02U[offset+1]; + if (dmapE02U[offset] == 0x00 && + dmapE02U[offset+1] == 0x1A) { + if (*pIn != cd->srcSubS) + ++numS; + } + ++pIn; + --inLen; + } + } + ucs2=in; + if ((in & 0xFF80) == 0x0000) { /* U28: in & 0b1111111110000000 == 0x0000 */ + *pOut=in; + ++pOut; + } else if ((in & 0xF800) == 0x0000) { /* in & 0b1111100000000000 == 0x0000 */ + register uchar byte; + in>>=6; + in&=0x001F; /* 0b0000000000011111 */ + in|=0x00C0; /* 0b0000000011000000 */ + *pOut=in; + ++pOut; + byte=ucs2; /* dmapD12U[offset+1]; */ + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + } else if ((in & 0xFC00) == 0xD800) { + *pOut=0xEF; + ++pOut; + *pOut=0xBF; + ++pOut; + *pOut=0xBD; + ++pOut; + } else { + register uchar byte; + register uchar work; + byte=(ucs2>>8); /* dmapD12U[offset]; */ + byte>>=4; + byte|=0xE0; /* 0b11100000; */ + *pOut=byte; + ++pOut; + + byte=(ucs2>>8); /* dmapD12U[offset]; */ + byte<<=2; + work=ucs2; /* dmapD12U[offset+1]; */ + work>>=6; + byte|=work; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + + byte=ucs2; /* dmapD12U[offset+1]; */ + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + } + /* end of U28 */ + } + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_U2E) { + register uchar * dmapU2S=cd->cnv_dmap->dmapU2S; + register uchar * dmapU2M2=cd->cnv_dmap->dmapU2M2 - 0x80 * 2; + register uchar * dmapU2M3=cd->cnv_dmap->dmapU2M3 - 0x80 * 3; + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register char subS=cd->subS; + register char * pSubD=(char *) &(cd->subD); + register size_t numS=0; + register size_t rc=0; + while (0 < inLen) { + register uint32_t in; + if (inLen == 1) { + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + if (pLastOutBuf < pOut) + break; + in=pIn[0]; + in<<=8; + in+=pIn[1]; + if (in == 0x0000) { + *pOut=0x00; + ++pOut; + } else if (in < 0x100 && dmapU2S[in] != 0x0000) { + if ((*pOut=dmapU2S[in]) == subS) { + if (in != cd->srcSubS) + ++numS; + } + ++pOut; + } else { + in<<=1; + if (dmapU2M2[in] == 0x00) { /* not found in dmapU2M2 */ + in*=1.5; + if (dmapU2M3[in] == 0x00) { /* not found in dmapU2M3*/ + *pOut=pSubD[0]; + ++pOut; + *pOut=pSubD[1]; + ++pOut; + ++numS; + ++rc; + } else { + *pOut=dmapU2M3[in]; + ++pOut; + *pOut=dmapU2M3[1+in]; + ++pOut; + *pOut=dmapU2M3[2+in]; + ++pOut; + } + } else { + *pOut=dmapU2M2[in]; + ++pOut; + if (dmapU2M2[1+in] == 0x00) { + if (*pOut == subS) { + in>>=1; + if (in != cd->srcSubS) + ++numS; + } + } else { + *pOut=dmapU2M2[1+in]; + ++pOut; + if (memcmp(pOut-2, pSubD, 2) == 0) { + in>>=1; + if (in != cd->srcSubD) { + ++numS; + ++rc; + } + } + } + } + } + pIn+=2; + inLen-=2; + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return rc; /* compatibility to iconv() */ + + } else if (cd->cnv_dmap->codingSchema == DMAP_T2E) { + register uchar * dmapU2S=cd->cnv_dmap->dmapU2S; + register uchar * dmapU2M2=cd->cnv_dmap->dmapU2M2 - 0x80 * 2; + register uchar * dmapU2M3=cd->cnv_dmap->dmapU2M3 - 0x80 * 3; + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register char subS=cd->subS; + register char * pSubD=(char *) &(cd->subD); + register size_t numS=0; + register size_t rc=0; + while (0 < inLen) { + register uint32_t in; + if (inLen == 1) { + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen-1; + *outBuf=pOut; + *inBuf=pIn; + ++numS; + *numSub+=numS; + return 0; + } + if (pLastOutBuf < pOut) + break; + in=pIn[0]; + in<<=8; + in+=pIn[1]; + if (in == 0x0000) { + *pOut=0x00; + ++pOut; + } else if (0xD800 <= in && in <= 0xDBFF) { /* first byte of surrogate */ + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-2; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn+2; + ++numS; + *numSub+=numS; + return -1; + + } else if (0xDC00 <= in && in <= 0xDFFF) { /* second byte of surrogate */ + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-1; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + ++numS; + *numSub+=numS; + return -1; + + } else if (in < 0x100 && dmapU2S[in] != 0x0000) { + if ((*pOut=dmapU2S[in]) == subS) { + if (in != cd->srcSubS) + ++numS; + } + ++pOut; + } else { + in<<=1; + if (dmapU2M2[in] == 0x00) { /* not found in dmapU2M2 */ + in*=1.5; + if (dmapU2M3[in] == 0x00) { /* not found in dmapU2M3*/ + *pOut=pSubD[0]; + ++pOut; + *pOut=pSubD[1]; + ++pOut; + ++numS; + ++rc; + } else { + *pOut=dmapU2M3[in]; + ++pOut; + *pOut=dmapU2M3[1+in]; + ++pOut; + *pOut=dmapU2M3[2+in]; + ++pOut; + } + } else { + *pOut=dmapU2M2[in]; + ++pOut; + if (dmapU2M2[1+in] == 0x00) { + if (*pOut == subS) { + in>>=1; + if (in != cd->srcSubS) + ++numS; + } + } else { + *pOut=dmapU2M2[1+in]; + ++pOut; + if (memcmp(pOut-2, pSubD, 2) == 0) { + in>>=1; + if (in != cd->srcSubD) { + ++numS; + ++rc; + } + } + } + } + } + pIn+=2; + inLen-=2; + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_82E) { + register uchar * dmapU2S=cd->cnv_dmap->dmapU2S; + register uchar * dmapU2M2=cd->cnv_dmap->dmapU2M2 - 0x80 * 2; + register uchar * dmapU2M3=cd->cnv_dmap->dmapU2M3 - 0x80 * 3; + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register char subS=cd->subS; + register char * pSubD=(char *) &(cd->subD); + register size_t numS=0; + register size_t rc=0; + while (0 < inLen) { + register uint32_t in; + uint32_t in2; + if (pLastOutBuf < pOut) + break; + /* convert from UTF-8 to UCS-2 */ + if (*pIn == 0x00) { + in=0x0000; + ++pIn; + --inLen; + } else { /* 82U: */ + register uchar byte1=*pIn; + if ((byte1 & 0x80) == 0x00) { /* if (byte1 & 0b10000000 == 0b00000000) { */ + /* 1 bytes sequence: 0xxxxxxx => 00000000 0xxxxxxx*/ + in=byte1; + ++pIn; + --inLen; + } else if ((byte1 & 0xE0) == 0xC0) { /* (byte1 & 0b11100000 == 0b11000000) { */ + if (inLen < 2) { + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + if (byte1 == 0xC0 || byte1 == 0xC1) { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + /* 2 bytes sequence: + 110yyyyy 10xxxxxx => 00000yyy yyxxxxxx */ + register uchar byte2; + ++pIn; + byte2=*pIn; + if ((byte2 & 0xC0) == 0x80) { /* byte2 & 0b11000000 == 0b10000000) { */ + register uchar work=byte1; + work<<=6; + byte2&=0x3F; /* 0b00111111; */ + byte2|=work; + + byte1&=0x1F; /* 0b00011111; */ + byte1>>=2; + in=byte1; + in<<=8; + in+=byte2; + inLen-=2; + ++pIn; + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + *numSub+=numS; + return -1; + } + } else if ((byte1 & 0xF0) == 0xE0) { /* byte1 & 0b11110000 == 0b11100000 */ + /* 3 bytes sequence: + 1110zzzz 10yyyyyy 10xxxxxx => zzzzyyyy yyxxxxxx */ + register uchar byte2; + register uchar byte3; + if (inLen < 3) { + if (inLen == 2 && (pIn[1] & 0xC0) != 0x80) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + ++pIn; + byte2=*pIn; + ++pIn; + byte3=*pIn; + if ((byte2 & 0xC0) != 0x80 || + (byte3 & 0xC0) != 0x80 || + (byte1 == 0xE0 && byte2 < 0xA0)) { /* invalid sequence, only 0xA0-0xBF allowed after 0xE0 */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + *numSub+=numS; + return -1; + } + { + register uchar work=byte2; + work<<=6; + byte3&=0x3F; /* 0b00111111; */ + byte3|=work; + + byte2&=0x3F; /* 0b00111111; */ + byte2>>=2; + + byte1<<=4; + in=byte1 | byte2;; + in<<=8; + in+=byte3; + inLen-=3; + ++pIn; + } + } else if ((0xF0 <= byte1 && byte1 <= 0xF4)) { /* (bytes1 & 11111000) == 0x1110000 */ + /* 4 bytes sequence + 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx => 110110ww wwzzzzyy 110111yy yyxxxxxx + where uuuuu = wwww + 1 */ + register uchar byte2; + register uchar byte3; + register uchar byte4; + if (inLen < 4) { + if ((inLen >= 2 && (pIn[1] & 0xC0) != 0x80) || + (inLen >= 3 && (pIn[2] & 0xC0) != 0x80) || + (cd->toCcsid == 13488) ) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + ++pIn; + byte2=*pIn; + ++pIn; + byte3=*pIn; + ++pIn; + byte4=*pIn; + if ((byte2 & 0xC0) == 0x80 && /* byte2 & 0b11000000 == 0b10000000 */ + (byte3 & 0xC0) == 0x80 && /* byte3 & 0b11000000 == 0b10000000 */ + (byte4 & 0xC0) == 0x80) { /* byte4 & 0b11000000 == 0b10000000 */ + register uchar work=byte2; + if (byte1 == 0xF0 && byte2 < 0x90) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + /* iconv() returns 0 for 0xF4908080 and convert to 0x00 + } else if (byte1 == 0xF4 && byte2 > 0x8F) { + errno=EINVAL; + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + */ + } + + work&=0x30; /* 0b00110000; */ + work>>=4; + byte1&=0x07; /* 0b00000111; */ + byte1<<=2; + byte1+=work; /* uuuuu */ + --byte1; /* wwww */ + + work=byte1 & 0x0F; + work>>=2; + work+=0xD8; /* 0b11011011; */ + in=work; + in<<=8; + + byte1<<=6; + byte2<<=2; + byte2&=0x3C; /* 0b00111100; */ + work=byte3; + work>>=4; + work&=0x03; /* 0b00000011; */ + work|=byte1; + work|=byte2; + in+=work; + + work=byte3; + work>>=2; + work&=0x03; /* 0b00000011; */ + work|=0xDC; /* 0b110111xx; */ + in2=work; + in2<<=8; + + byte3<<=6; + byte4&=0x3F; /* 0b00111111; */ + byte4|=byte3; + in2+=byte4; + inLen-=4; + ++pIn; +#ifdef match_with_GBK + if ((0xD800 == in && in2 < 0xDC80) || + (0xD840 == in && in2 < 0xDC80) || + (0xD880 == in && in2 < 0xDC80) || + (0xD8C0 == in && in2 < 0xDC80) || + (0xD900 == in && in2 < 0xDC80) || + (0xD940 == in && in2 < 0xDC80) || + (0xD980 == in && in2 < 0xDC80) || + (0xD9C0 == in && in2 < 0xDC80) || + (0xDA00 == in && in2 < 0xDC80) || + (0xDA40 == in && in2 < 0xDC80) || + (0xDA80 == in && in2 < 0xDC80) || + (0xDAC0 == in && in2 < 0xDC80) || + (0xDB00 == in && in2 < 0xDC80) || + (0xDB40 == in && in2 < 0xDC80) || + (0xDB80 == in && in2 < 0xDC80) || + (0xDBC0 == in && in2 < 0xDC80)) { +#else + if ((0xD800 <= in && in <= 0xDBFF) && + (0xDC00 <= in2 && in2 <= 0xDFFF)) { +#endif + *pOut=subS; + ++pOut; + ++numS; + continue; + } + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + } + } else if (0xF5 <= byte1 && byte1 <= 0xFF) { /* minic iconv() behavior */ + if (inLen < 4 || + (inLen >= 4 && byte1 == 0xF8 && pIn[1] < 0x90) || + pIn[1] < 0x80 || 0xBF < pIn[1] || + pIn[2] < 0x80 || 0xBF < pIn[2] || + pIn[3] < 0x80 || 0xBF < pIn[3] ) { + if (inLen == 1) + errno=EINVAL; /* 22 */ + else if (inLen == 2 && (pIn[1] & 0xC0) != 0x80) + errno=EILSEQ; /* 116 */ + else if (inLen == 3 && ((pIn[1] & 0xC0) != 0x80 || (pIn[2] & 0xC0) != 0x80)) + errno=EILSEQ; /* 116 */ + else if (inLen >= 4 && (byte1 == 0xF8 || (pIn[1] & 0xC0) != 0x80 || (pIn[2] & 0xC0) != 0x80 || (pIn[3] & 0xC0) != 0x80)) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } else if ((pIn[1] == 0x80 || pIn[1] == 0x90 || pIn[1] == 0xA0 || pIn[1] == 0xB0) && + pIn[2] < 0x82) { + *pOut=subS; /* Though returns replacement character, which iconv() does not return. */ + ++pOut; + ++numS; + pIn+=4; + inLen-=4; + continue; + } else { + *pOut=pSubD[0]; /* Though returns replacement character, which iconv() does not return. */ + ++pOut; + *pOut=pSubD[1]; + ++pOut; + ++numS; + pIn+=4; + inLen-=4; + continue; + /* iconv() returns 0 with strange 1 byte converted values */ + } + + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + } + /* end of UTF-8 to UCS-2 */ + if (in == 0x0000) { + *pOut=0x00; + ++pOut; + } else if (in < 0x100 && dmapU2S[in] != 0x0000) { + if ((*pOut=dmapU2S[in]) == subS) { + if (in != cd->srcSubS) + ++numS; + } + ++pOut; + } else { + in<<=1; + if (dmapU2M2[in] == 0x00) { /* not found in dmapU2M2 */ + in*=1.5; + if (dmapU2M3[in] == 0x00) { /* not found in dmapU2M3*/ + *pOut=pSubD[0]; + ++pOut; + *pOut=pSubD[1]; + ++pOut; + ++numS; + ++rc; + } else { + *pOut=dmapU2M3[in]; + ++pOut; + *pOut=dmapU2M3[1+in]; + ++pOut; + *pOut=dmapU2M3[2+in]; + ++pOut; + } + } else { + *pOut=dmapU2M2[in]; + ++pOut; + if (dmapU2M2[1+in] == 0x00) { + if (*pOut == subS) { + in>>=1; + if (in != cd->srcSubS) + ++numS; + } + } else { + *pOut=dmapU2M2[1+in]; + ++pOut; + if (memcmp(pOut-2, pSubD, 2) == 0) { + in>>=1; + if (in != cd->srcSubD) { + ++numS; + ++rc; + } + } + } + } + } + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_S2U) { + /* use uchar * instead of UniChar to avoid memcpy */ + register uchar * dmapD12U=(uchar *) (cd->cnv_dmap->dmapD12U); + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register int offset; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register size_t numS=0; + while (0 < inLen) { + if (pLastOutBuf < pOut) + break; + if (*pIn == 0x00) { + *pOut=0x00; + ++pOut; + *pOut=0x00; + ++pOut; + ++pIn; + --inLen; + } else { + offset=*pIn; + offset<<=1; + *pOut=dmapD12U[offset]; + ++pOut; + *pOut=dmapD12U[offset+1]; + ++pOut; + if (dmapD12U[offset] == 0x00) { + if (dmapD12U[offset+1] == 0x1A) { + if (*pIn != cd->srcSubS) + ++numS; + } else if (dmapD12U[offset+1] == 0x00) { + pOut-=2; + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + } + ++pIn; + --inLen; + } + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_S28) { + /* use uchar * instead of UniChar to avoid memcpy */ + register uchar * dmapD12U=(uchar *) (cd->cnv_dmap->dmapD12U); + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register int offset; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register size_t numS=0; + register UniChar in; /* copy part of U28 */ + while (0 < inLen) { + if (pLastOutBuf < pOut) + break; + if (*pIn == 0x00) { + *pOut=0x00; + ++pOut; + ++pIn; + --inLen; + } else { + offset=*pIn; + offset<<=1; + in=dmapD12U[offset]; + in<<=8; + in+=dmapD12U[offset+1]; + if ((in & 0xFF80) == 0x0000) { /* U28: in & 0b1111111110000000 == 0x0000 */ + if (in == 0x000) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + *pOut=in; + ++pOut; + } else if ((in & 0xF800) == 0x0000) { /* in & 0b1111100000000000 == 0x0000 */ + register uchar byte; + in>>=6; + in&=0x001F; /* 0b0000000000011111 */ + in|=0x00C0; /* 0b0000000011000000 */ + *pOut=in; + ++pOut; + byte=dmapD12U[offset+1]; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + } else if ((in & 0xFC00) == 0xD800) { /* There should not be no surrogate character in SBCS. */ + *pOut=0xEF; + ++pOut; + *pOut=0xBF; + ++pOut; + *pOut=0xBD; + ++pOut; + } else { + register uchar byte; + register uchar work; + byte=dmapD12U[offset]; + byte>>=4; + byte|=0xE0; /* 0b11100000; */ + *pOut=byte; + ++pOut; + + byte=dmapD12U[offset]; + byte<<=2; + work=dmapD12U[offset+1]; + work>>=6; + byte|=work; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + + byte=dmapD12U[offset+1]; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + } + /* end of U28 */ + if (dmapD12U[offset] == 0x00) { + if (dmapD12U[offset+1] == 0x1A) { + if (*pIn != cd->srcSubS) + ++numS; + } + } + ++pIn; + --inLen; + } + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_U2S) { + register uchar * dmapU2S=cd->cnv_dmap->dmapU2S; + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register char subS=cd->subS; + register size_t numS=0; + while (0 < inLen) { + register uint32_t in; + if (inLen == 1) { + errno=EINVAL; /* 22 */ + + *inBytesLeft=inLen; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + if (pLastOutBuf < pOut) + break; + in=pIn[0]; + in<<=8; + in+=pIn[1]; + if (in == 0x0000) { + *pOut=0x00; + } else { + if ((*pOut=dmapU2S[in]) == 0x00) { + *pOut=subS; + ++numS; + errno=EINVAL; /* 22 */ + } else if (*pOut == subS) { + if (in != cd->srcSubS) + ++numS; + } + } + ++pOut; + pIn+=2; + inLen-=2; + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return numS; + + } else if (cd->cnv_dmap->codingSchema == DMAP_T2S) { + register uchar * dmapU2S=cd->cnv_dmap->dmapU2S; + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register char subS=cd->subS; + register size_t numS=0; + while (0 < inLen) { + register uint32_t in; + if (inLen == 1) { + errno=EINVAL; /* 22 */ + + *inBytesLeft=inLen-1; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + ++numS; + *numSub+=numS; + return 0; + } + if (pLastOutBuf < pOut) + break; + in=pIn[0]; + in<<=8; + in+=pIn[1]; + if (in == 0x0000) { + *pOut=0x00; + + } else if (0xD800 <= in && in <= 0xDFFF) { /* 0xD800-0xDFFF, surrogate first and second values */ + if (0xDC00 <= in ) { + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-1; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + return -1; + + } else if (inLen < 4) { + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-2; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn+2; + return -1; + + } else { + register uint32_t in2; + in2=pIn[2]; + in2<<=8; + in2+=pIn[3]; + if (0xDC00 <= in2 && in2 <= 0xDFFF) { /* second surrogate character =0xDC00 - 0xDFFF*/ + *pOut=subS; + ++numS; + pIn+=4; + } else { + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-1; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + } + } else { + if ((*pOut=dmapU2S[in]) == 0x00) { + *pOut=subS; + ++numS; + errno=EINVAL; /* 22 */ + } else if (*pOut == subS) { + if (in != cd->srcSubS) + ++numS; + } + } + ++pOut; + pIn+=2; + inLen-=2; + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_82S) { + register uchar * dmapU2S=cd->cnv_dmap->dmapU2S; + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register char subS=cd->subS; + register size_t numS=0; + while (0 < inLen) { + register uint32_t in; + uint32_t in2; /* The second surrogate value */ + if (pLastOutBuf < pOut) + break; + /* convert from UTF-8 to UCS-2 */ + if (*pIn == 0x00) { + in=0x0000; + ++pIn; + --inLen; + } else { /* 82U: */ + register uchar byte1=*pIn; + if ((byte1 & 0x80) == 0x00) { /* if (byte1 & 0b10000000 == 0b00000000) { */ + /* 1 bytes sequence: 0xxxxxxx => 00000000 0xxxxxxx*/ + in=byte1; + ++pIn; + --inLen; + } else if ((byte1 & 0xE0) == 0xC0) { /* (byte1 & 0b11100000 == 0b11000000) { */ + if (inLen < 2) { + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + if (byte1 == 0xC0 || byte1 == 0xC1) { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + /* 2 bytes sequence: + 110yyyyy 10xxxxxx => 00000yyy yyxxxxxx */ + register uchar byte2; + ++pIn; + byte2=*pIn; + if ((byte2 & 0xC0) == 0x80) { /* byte2 & 0b11000000 == 0b10000000) { */ + register uchar work=byte1; + work<<=6; + byte2&=0x3F; /* 0b00111111; */ + byte2|=work; + + byte1&=0x1F; /* 0b00011111; */ + byte1>>=2; + in=byte1; + in<<=8; + in+=byte2; + inLen-=2; + ++pIn; + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + *numSub+=numS; + return -1; + } + } else if ((byte1 & 0xF0) == 0xE0) { /* byte1 & 0b11110000 == 0b11100000 */ + /* 3 bytes sequence: + 1110zzzz 10yyyyyy 10xxxxxx => zzzzyyyy yyxxxxxx */ + register uchar byte2; + register uchar byte3; + if (inLen < 3) { + if (inLen == 2 && (pIn[1] & 0xC0) != 0x80) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + ++pIn; + byte2=*pIn; + ++pIn; + byte3=*pIn; + if ((byte2 & 0xC0) != 0x80 || + (byte3 & 0xC0) != 0x80 || + (byte1 == 0xE0 && byte2 < 0xA0)) { /* invalid sequence, only 0xA0-0xBF allowed after 0xE0 */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + *numSub+=numS; + return -1; + } + { + register uchar work=byte2; + work<<=6; + byte3&=0x3F; /* 0b00111111; */ + byte3|=work; + + byte2&=0x3F; /* 0b00111111; */ + byte2>>=2; + + byte1<<=4; + in=byte1 | byte2;; + in<<=8; + in+=byte3; + inLen-=3; + ++pIn; + } + } else if ((0xF0 <= byte1 && byte1 <= 0xF4) || /* (bytes1 & 11111000) == 0x1110000 */ + ((byte1&=0xF7) && 0xF0 <= byte1 && byte1 <= 0xF4)) { /* minic iconv() behavior */ + /* 4 bytes sequence + 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx => 110110ww wwzzzzyy 110111yy yyxxxxxx + where uuuuu = wwww + 1 */ + register uchar byte2; + register uchar byte3; + register uchar byte4; + if (inLen < 4) { + if ((inLen >= 2 && (pIn[1] & 0xC0) != 0x80) || + (inLen >= 3 && (pIn[2] & 0xC0) != 0x80) || + (cd->toCcsid == 13488) ) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + ++pIn; + byte2=*pIn; + ++pIn; + byte3=*pIn; + ++pIn; + byte4=*pIn; + if ((byte2 & 0xC0) == 0x80 && /* byte2 & 0b11000000 == 0b10000000 */ + (byte3 & 0xC0) == 0x80 && /* byte3 & 0b11000000 == 0b10000000 */ + (byte4 & 0xC0) == 0x80) { /* byte4 & 0b11000000 == 0b10000000 */ + register uchar work=byte2; + if (byte1 == 0xF0 && byte2 < 0x90) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + /* iconv() returns 0 for 0xF4908080 and convert to 0x00 + } else if (byte1 == 0xF4 && byte2 > 0x8F) { + errno=EINVAL; + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + */ + } + + work&=0x30; /* 0b00110000; */ + work>>=4; + byte1&=0x07; /* 0b00000111; */ + byte1<<=2; + byte1+=work; /* uuuuu */ + --byte1; /* wwww */ + + work=byte1 & 0x0F; + work>>=2; + work+=0xD8; /* 0b11011011; */ + in=work; + in<<=8; + + byte1<<=6; + byte2<<=2; + byte2&=0x3C; /* 0b00111100; */ + work=byte3; + work>>=4; + work&=0x03; /* 0b00000011; */ + work|=byte1; + work|=byte2; + in+=work; + + work=byte3; + work>>=2; + work&=0x03; /* 0b00000011; */ + work|=0xDC; /* 0b110111xx; */ + in2=work; + in2<<=8; + + byte3<<=6; + byte4&=0x3F; /* 0b00111111; */ + byte4|=byte3; + in2+=byte4; + inLen-=4; + ++pIn; + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + } + } else if ((byte1 & 0xF0) == 0xF0) { /* minic iconv() behavior */ + if (inLen < 4 || + pIn[1] < 0x80 || 0xBF < pIn[1] || + pIn[2] < 0x80 || 0xBF < pIn[2] || + pIn[3] < 0x80 || 0xBF < pIn[3] ) { + if (inLen == 1) + errno=EINVAL; /* 22 */ + else if (inLen == 2 && (pIn[1] & 0xC0) != 0x80) + errno=EILSEQ; /* 116 */ + else if (inLen == 3 && ((pIn[1] & 0xC0) != 0x80 || (pIn[2] & 0xC0) != 0x80)) + errno=EILSEQ; /* 116 */ + else if (inLen >= 4 && ((pIn[1] & 0xC0) != 0x80 || (pIn[2] & 0xC0) != 0x80 || (pIn[3] & 0xC0) != 0x80)) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } else { + *pOut=subS; /* Though returns replacement character, which iconv() does not return. */ + ++pOut; + ++numS; + pIn+=4; + inLen-=4; + /* UTF-8_IBM-850 0xF0908080 : converted value does not match, iconv=0x00, dmap=0x7F + UTF-8_IBM-850 0xF0908081 : converted value does not match, iconv=0x01, dmap=0x7F + UTF-8_IBM-850 0xF0908082 : converted value does not match, iconv=0x02, dmap=0x7F + UTF-8_IBM-850 0xF0908083 : converted value does not match, iconv=0x03, dmap=0x7F + .... + UTF-8_IBM-850 0xF09081BE : converted value does not match, iconv=0x7E, dmap=0x7F + UTF-8_IBM-850 0xF09081BF : converted value does not match, iconv=0x1C, dmap=0x7F + UTF-8_IBM-850 0xF09082A0 : converted value does not match, iconv=0xFF, dmap=0x7F + UTF-8_IBM-850 0xF09082A1 : converted value does not match, iconv=0xAD, dmap=0x7F + .... + */ + continue; + /* iconv() returns 0 with strange 1 byte converted values */ + } + + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + } + /* end of UTF-8 to UCS-2 */ + if (in == 0x0000) { + *pOut=0x00; + } else { + if ((*pOut=dmapU2S[in]) == 0x00) { + *pOut=subS; + ++numS; + errno=EINVAL; /* 22 */ + } else if (*pOut == subS) { + if (in != cd->srcSubS) { + ++numS; + } + } + } + ++pOut; + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_D2U) { + /* use uchar * instead of UniChar to avoid memcpy */ + register uchar * dmapD12U=(uchar *) (cd->cnv_dmap->dmapD12U); + register uchar * dmapD22U=(uchar *) (cd->cnv_dmap->dmapD22U); + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register int offset; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register size_t numS=0; + while (0 < inLen) { + if (pLastOutBuf < pOut) + break; + if (*pIn == 0x00) { + *pOut=0x00; + ++pOut; + *pOut=0x00; + ++pOut; + ++pIn; + --inLen; + } else { + offset=*pIn; + offset<<=1; + if (dmapD12U[offset] == 0x00 && + dmapD12U[offset+1] == 0x00) { /* DBCS */ + if (inLen < 2) { + if (*pIn == 0x80 || *pIn == 0xFF || + (cd->fromCcsid == 943 && (*pIn == 0x85 || *pIn == 0x86 || *pIn == 0xA0 || *pIn == 0xEB || *pIn == 0xEC || *pIn == 0xEF || *pIn == 0xFD || *pIn == 0xFE)) || + (cd->fromCcsid == 932 && (*pIn == 0x85 || *pIn == 0x86 || *pIn == 0x87 || *pIn == 0xEB || *pIn == 0xEC || *pIn == 0xED || *pIn == 0xEE || *pIn == 0xEF)) || + (cd->fromCcsid == 1381 && ((0x85 <= *pIn && *pIn <= 0x8B) || (0xAA <= *pIn && *pIn <= 0xAF) || (0xF8 <= *pIn && *pIn <= 0xFE)))) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + offset-=0x100; + ++pIn; + offset<<=8; + offset+=(*pIn * 2); + if (dmapD22U[offset] == 0x00 && + dmapD22U[offset+1] == 0x00) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + *pOut=dmapD22U[offset]; + ++pOut; + *pOut=dmapD22U[offset+1]; + ++pOut; + if (dmapD22U[offset] == 0xFF && + dmapD22U[offset+1] == 0xFD) { + if (pIn[-1] * 0x100 + pIn[0] != cd->srcSubD) + ++numS; + } + ++pIn; + inLen-=2; + } else { /* SBCS */ + *pOut=dmapD12U[offset]; + ++pOut; + *pOut=dmapD12U[offset+1]; + ++pOut; + if (dmapD12U[offset] == 0x00 && + dmapD12U[offset+1] == 0x1A) { + if (*pIn != cd->srcSubS) + ++numS; + } + ++pIn; + --inLen; + } + } + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_D28) { + /* use uchar * instead of UniChar to avoid memcpy */ + register uchar * dmapD12U=(uchar *) (cd->cnv_dmap->dmapD12U); + register uchar * dmapD22U=(uchar *) (cd->cnv_dmap->dmapD22U); + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register int offset; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register size_t numS=0; + register UniChar in; /* copy part of U28 */ + register UniChar ucs2; + while (0 < inLen) { + if (pLastOutBuf < pOut) + break; + if (*pIn == 0x00) { + *pOut=0x00; + ++pOut; + ++pIn; + --inLen; + } else { + offset=*pIn; + offset<<=1; + if (dmapD12U[offset] == 0x00 && + dmapD12U[offset+1] == 0x00) { /* DBCS */ + if (inLen < 2) { + if (*pIn == 0x80 || *pIn == 0xFF || + (cd->fromCcsid == 943 && (*pIn == 0x85 || *pIn == 0x86 || *pIn == 0xA0 || *pIn == 0xEB || *pIn == 0xEC || *pIn == 0xEF || *pIn == 0xFD || *pIn == 0xFE)) || + (cd->fromCcsid == 932 && (*pIn == 0x85 || *pIn == 0x86 || *pIn == 0x87 || *pIn == 0xEB || *pIn == 0xEC || *pIn == 0xED || *pIn == 0xEE || *pIn == 0xEF)) || + (cd->fromCcsid == 1381 && ((0x85 <= *pIn && *pIn <= 0x8B) || (0xAA <= *pIn && *pIn <= 0xAF) || (0xF8 <= *pIn && *pIn <= 0xFE)))) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + offset-=0x100; + ++pIn; + offset<<=8; + offset+=(*pIn * 2); + if (dmapD22U[offset] == 0x00 && + dmapD22U[offset+1] == 0x00) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + return -1; + } + in=dmapD22U[offset]; + in<<=8; + in+=dmapD22U[offset+1]; + ucs2=in; + if (dmapD22U[offset] == 0xFF && + dmapD22U[offset+1] == 0xFD) { + if (in != cd->srcSubD) + ++numS; + } + ++pIn; + inLen-=2; + } else { /* SBCS */ + in=dmapD12U[offset]; + in<<=8; + in+=dmapD12U[offset+1]; + ucs2=in; + if (dmapD12U[offset] == 0x00 && + dmapD12U[offset+1] == 0x1A) { + if (in != cd->srcSubS) + ++numS; + } + ++pIn; + --inLen; + } + if ((in & 0xFF80) == 0x0000) { /* U28: in & 0b1111111110000000 == 0x0000 */ + *pOut=in; + ++pOut; + } else if ((in & 0xF800) == 0x0000) { /* in & 0b1111100000000000 == 0x0000 */ + register uchar byte; + in>>=6; + in&=0x001F; /* 0b0000000000011111 */ + in|=0x00C0; /* 0b0000000011000000 */ + *pOut=in; + ++pOut; + byte=ucs2; /* dmapD12U[offset+1]; */ + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + } else if ((in & 0xFC00) == 0xD800) { /* There should not be no surrogate character in SBCS. */ + *pOut=0xEF; + ++pOut; + *pOut=0xBF; + ++pOut; + *pOut=0xBD; + ++pOut; + } else { + register uchar byte; + register uchar work; + byte=(ucs2>>8); /* dmapD12U[offset]; */ + byte>>=4; + byte|=0xE0; /* 0b11100000; */ + *pOut=byte; + ++pOut; + + byte=(ucs2>>8); /* dmapD12U[offset]; */ + byte<<=2; + work=ucs2; /* dmapD12U[offset+1]; */ + work>>=6; + byte|=work; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + + byte=ucs2; /* dmapD12U[offset+1]; */ + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + } + /* end of U28 */ + } + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_U2D) { + register uchar * dmapU2D=cd->cnv_dmap->dmapU2D; + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register char subS=cd->subS; + register char * pSubD=(char *) &(cd->subD); + register size_t numS=0; + while (0 < inLen) { + register uint32_t in; + if (inLen == 1) { + errno=EINVAL; /* 22 */ + + *inBytesLeft=inLen; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + if (pLastOutBuf < pOut) + break; + in=pIn[0]; + in<<=8; + in+=pIn[1]; + if (in == 0x0000) { + *pOut=0x00; + ++pOut; + } else { + in<<=1; + *pOut=dmapU2D[in]; + ++pOut; + if (dmapU2D[in+1] == 0x00) { /* SBCS */ + if (*pOut == subS) { + if (in != cd->srcSubS) + ++numS; + } + } else { + *pOut=dmapU2D[in+1]; + ++pOut; + if (dmapU2D[in] == pSubD[0] && + dmapU2D[in+1] == pSubD[1]) { + in>>=1; + if (in != cd->srcSubD) + ++numS; + } + } + } + pIn+=2; + inLen-=2; + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return numS; /* to minic iconv() behavior */ + + } else if (cd->cnv_dmap->codingSchema == DMAP_T2D) { + register uchar * dmapU2D=cd->cnv_dmap->dmapU2D; + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register char subS=cd->subS; + register char * pSubD=(char *) &(cd->subD); + register size_t numS=0; + while (0 < inLen) { + register uint32_t in; + if (inLen == 1) { + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-1; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + ++numS; + *numSub+=numS; + return 0; + } + if (pLastOutBuf < pOut) + break; + in=pIn[0]; + in<<=8; + in+=pIn[1]; + if (in == 0x0000) { + *pOut=0x00; + ++pOut; + } else if (0xD800 <= in && in <= 0xDBFF) { /* first byte of surrogate */ + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-2; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn+2; + ++numS; + *numSub+=numS; + return -1; + + } else if (0xDC00 <= in && in <= 0xDFFF) { /* second byte of surrogate */ + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-1; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + ++numS; + *numSub+=numS; + return -1; + + } else { + in<<=1; + *pOut=dmapU2D[in]; + ++pOut; + if (dmapU2D[in+1] == 0x00) { /* SBCS */ + if (*pOut == subS) { + if (in != cd->srcSubS) + ++numS; + } + } else { + *pOut=dmapU2D[in+1]; + ++pOut; + if (dmapU2D[in] == pSubD[0] && + dmapU2D[in+1] == pSubD[1]) { + in>>=1; + if (in != cd->srcSubD) + ++numS; + } + } + } + pIn+=2; + inLen-=2; + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; /* to minic iconv() behavior */ + + } else if (cd->cnv_dmap->codingSchema == DMAP_82D) { + register uchar * dmapU2D=cd->cnv_dmap->dmapU2D; + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register char subS=cd->subS; + register char * pSubD=(char *) &(cd->subD); + register size_t numS=0; + while (0 < inLen) { + register uint32_t in; + uint32_t in2; + if (pLastOutBuf < pOut) + break; + /* convert from UTF-8 to UCS-2 */ + if (*pIn == 0x00) { + in=0x0000; + ++pIn; + --inLen; + } else { /* 82U: */ + register uchar byte1=*pIn; + if ((byte1 & 0x80) == 0x00) { /* if (byte1 & 0b10000000 == 0b00000000) { */ + /* 1 bytes sequence: 0xxxxxxx => 00000000 0xxxxxxx*/ + in=byte1; + ++pIn; + --inLen; + } else if ((byte1 & 0xE0) == 0xC0) { /* (byte1 & 0b11100000 == 0b11000000) { */ + if (inLen < 2) { + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + if (byte1 == 0xC0 || byte1 == 0xC1) { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + /* 2 bytes sequence: + 110yyyyy 10xxxxxx => 00000yyy yyxxxxxx */ + register uchar byte2; + ++pIn; + byte2=*pIn; + if ((byte2 & 0xC0) == 0x80) { /* byte2 & 0b11000000 == 0b10000000) { */ + register uchar work=byte1; + work<<=6; + byte2&=0x3F; /* 0b00111111; */ + byte2|=work; + + byte1&=0x1F; /* 0b00011111; */ + byte1>>=2; + in=byte1; + in<<=8; + in+=byte2; + inLen-=2; + ++pIn; + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + *numSub+=numS; + return -1; + } + } else if ((byte1 & 0xF0) == 0xE0) { /* byte1 & 0b11110000 == 0b11100000 */ + /* 3 bytes sequence: + 1110zzzz 10yyyyyy 10xxxxxx => zzzzyyyy yyxxxxxx */ + register uchar byte2; + register uchar byte3; + if (inLen < 3) { + if (inLen == 2 && (pIn[1] & 0xC0) != 0x80) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + ++pIn; + byte2=*pIn; + ++pIn; + byte3=*pIn; + if ((byte2 & 0xC0) != 0x80 || + (byte3 & 0xC0) != 0x80 || + (byte1 == 0xE0 && byte2 < 0xA0)) { /* invalid sequence, only 0xA0-0xBF allowed after 0xE0 */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + *numSub+=numS; + return -1; + } + { + register uchar work=byte2; + work<<=6; + byte3&=0x3F; /* 0b00111111; */ + byte3|=work; + + byte2&=0x3F; /* 0b00111111; */ + byte2>>=2; + + byte1<<=4; + in=byte1 | byte2;; + in<<=8; + in+=byte3; + inLen-=3; + ++pIn; + } + } else if ((0xF0 <= byte1 && byte1 <= 0xF4)) { /* (bytes1 & 11111000) == 0x1110000 */ + /* 4 bytes sequence + 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx => 110110ww wwzzzzyy 110111yy yyxxxxxx + where uuuuu = wwww + 1 */ + register uchar byte2; + register uchar byte3; + register uchar byte4; + if (inLen < 4) { + if ((inLen >= 2 && (pIn[1] & 0xC0) != 0x80) || + (inLen >= 3 && (pIn[2] & 0xC0) != 0x80) || + (cd->toCcsid == 13488) ) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + ++pIn; + byte2=*pIn; + ++pIn; + byte3=*pIn; + ++pIn; + byte4=*pIn; + if ((byte2 & 0xC0) == 0x80 && /* byte2 & 0b11000000 == 0b10000000 */ + (byte3 & 0xC0) == 0x80 && /* byte3 & 0b11000000 == 0b10000000 */ + (byte4 & 0xC0) == 0x80) { /* byte4 & 0b11000000 == 0b10000000 */ + register uchar work=byte2; + if (byte1 == 0xF0 && byte2 < 0x90) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + /* iconv() returns 0 for 0xF4908080 and convert to 0x00 + } else if (byte1 == 0xF4 && byte2 > 0x8F) { + errno=EINVAL; + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + */ + } + + work&=0x30; /* 0b00110000; */ + work>>=4; + byte1&=0x07; /* 0b00000111; */ + byte1<<=2; + byte1+=work; /* uuuuu */ + --byte1; /* wwww */ + + work=byte1 & 0x0F; + work>>=2; + work+=0xD8; /* 0b11011011; */ + in=work; + in<<=8; + + byte1<<=6; + byte2<<=2; + byte2&=0x3C; /* 0b00111100; */ + work=byte3; + work>>=4; + work&=0x03; /* 0b00000011; */ + work|=byte1; + work|=byte2; + in+=work; + + work=byte3; + work>>=2; + work&=0x03; /* 0b00000011; */ + work|=0xDC; /* 0b110111xx; */ + in2=work; + in2<<=8; + + byte3<<=6; + byte4&=0x3F; /* 0b00111111; */ + byte4|=byte3; + in2+=byte4; + inLen-=4; + ++pIn; +#ifdef match_with_GBK + if ((0xD800 == in && in2 < 0xDC80) || + (0xD840 == in && in2 < 0xDC80) || + (0xD880 == in && in2 < 0xDC80) || + (0xD8C0 == in && in2 < 0xDC80) || + (0xD900 == in && in2 < 0xDC80) || + (0xD940 == in && in2 < 0xDC80) || + (0xD980 == in && in2 < 0xDC80) || + (0xD9C0 == in && in2 < 0xDC80) || + (0xDA00 == in && in2 < 0xDC80) || + (0xDA40 == in && in2 < 0xDC80) || + (0xDA80 == in && in2 < 0xDC80) || + (0xDAC0 == in && in2 < 0xDC80) || + (0xDB00 == in && in2 < 0xDC80) || + (0xDB40 == in && in2 < 0xDC80) || + (0xDB80 == in && in2 < 0xDC80) || + (0xDBC0 == in && in2 < 0xDC80)) { +#else + if ((0xD800 <= in && in <= 0xDBFF) && + (0xDC00 <= in2 && in2 <= 0xDFFF)) { +#endif + *pOut=subS; + ++pOut; + ++numS; + continue; + } + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + } + } else if (0xF5 <= byte1 && byte1 <= 0xFF) { /* minic iconv() behavior */ + if (inLen < 4 || + (inLen >= 4 && byte1 == 0xF8 && pIn[1] < 0x90) || + pIn[1] < 0x80 || 0xBF < pIn[1] || + pIn[2] < 0x80 || 0xBF < pIn[2] || + pIn[3] < 0x80 || 0xBF < pIn[3] ) { + if (inLen == 1) + errno=EINVAL; /* 22 */ + else if (inLen == 2 && (pIn[1] & 0xC0) != 0x80) + errno=EILSEQ; /* 116 */ + else if (inLen == 3 && ((pIn[1] & 0xC0) != 0x80 || (pIn[2] & 0xC0) != 0x80)) + errno=EILSEQ; /* 116 */ + else if (inLen >= 4 && (byte1 == 0xF8 || (pIn[1] & 0xC0) != 0x80 || (pIn[2] & 0xC0) != 0x80 || (pIn[3] & 0xC0) != 0x80)) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } else if ((pIn[1] == 0x80 || pIn[1] == 0x90 || pIn[1] == 0xA0 || pIn[1] == 0xB0) && + pIn[2] < 0x82) { + *pOut=subS; /* Though returns replacement character, which iconv() does not return. */ + ++pOut; + ++numS; + pIn+=4; + inLen-=4; + continue; + } else { + *pOut=pSubD[0]; /* Though returns replacement character, which iconv() does not return. */ + ++pOut; + *pOut=pSubD[1]; + ++pOut; + ++numS; + pIn+=4; + inLen-=4; + continue; + /* iconv() returns 0 with strange 1 byte converted values */ + } + + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + } + /* end of UTF-8 to UCS-2 */ + if (in == 0x0000) { + *pOut=0x00; + ++pOut; + } else { + in<<=1; + *pOut=dmapU2D[in]; + ++pOut; + if (dmapU2D[in+1] == 0x00) { /* SBCS */ + if (dmapU2D[in] == subS) { + in>>=1; + if (in != cd->srcSubS) + ++numS; + } + } else { + *pOut=dmapU2D[in+1]; + ++pOut; + if (dmapU2D[in] == pSubD[0] && + dmapU2D[in+1] == pSubD[1]) { + in>>=1; + if (in != cd->srcSubD) + ++numS; + } + } + } + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_82U) { + /* See http://unicode.org/versions/corrigendum1.html */ + /* convert from UTF-8 to UTF-16 can cover all conversion from UTF-8 to UCS-2 */ + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + register size_t numS=0; + while (0 < inLen) { + if (pLastOutBuf < pOut) + break; + if (*pIn == 0x00) { + *pOut=0x00; + ++pOut; + *pOut=0x00; + ++pOut; + ++pIn; + --inLen; + } else { /* 82U: */ + register uchar byte1=*pIn; + if ((byte1 & 0x80) == 0x00) { /* if (byte1 & 0b10000000 == 0b00000000) { */ + /* 1 bytes sequence: 0xxxxxxx => 00000000 0xxxxxxx*/ + *pOut=0x00; + ++pOut; + *pOut=byte1; + ++pOut; + ++pIn; + --inLen; + } else if ((byte1 & 0xE0) == 0xC0) { /* (byte1 & 0b11100000 == 0b11000000) { */ + if (inLen < 2) { + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + if (byte1 == 0xC0 || byte1 == 0xC1) { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + /* 2 bytes sequence: + 110yyyyy 10xxxxxx => 00000yyy yyxxxxxx */ + register uchar byte2; + ++pIn; + byte2=*pIn; + if ((byte2 & 0xC0) == 0x80) { /* byte2 & 0b11000000 == 0b10000000) { */ + register uchar work=byte1; + work<<=6; + byte2&=0x3F; /* 0b00111111; */ + byte2|=work; + + byte1&=0x1F; /* 0b00011111; */ + byte1>>=2; + *pOut=byte1; + ++pOut; + *pOut=byte2; + ++pOut; + inLen-=2; + ++pIn; + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-1; + *numSub+=numS; + return -1; + } + } else if ((byte1 & 0xF0) == 0xE0) { /* byte1 & 0b11110000 == 0b11100000 */ + /* 3 bytes sequence: + 1110zzzz 10yyyyyy 10xxxxxx => zzzzyyyy yyxxxxxx */ + register uchar byte2; + register uchar byte3; + if (inLen < 3) { + if (inLen == 2 && (pIn[1] & 0xC0) != 0x80) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + ++pIn; + byte2=*pIn; + ++pIn; + byte3=*pIn; + if ((byte2 & 0xC0) != 0x80 || + (byte3 & 0xC0) != 0x80 || + (byte1 == 0xE0 && byte2 < 0xA0)) { /* invalid sequence, only 0xA0-0xBF allowed after 0xE0 */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-2; + *numSub+=numS; + return -1; + } + { + register uchar work=byte2; + work<<=6; + byte3&=0x3F; /* 0b00111111; */ + byte3|=work; + + byte2&=0x3F; /* 0b00111111; */ + byte2>>=2; + + byte1<<=4; + *pOut=byte1 | byte2;; + ++pOut; + *pOut=byte3; + ++pOut; + inLen-=3; + ++pIn; + } + } else if ((0xF0 <= byte1 && byte1 <= 0xF4) || /* (bytes1 & 11111000) == 0x1110000 */ + ((byte1&=0xF7) && 0xF0 <= byte1 && byte1 <= 0xF4)) { /* minic iconv() behavior */ + /* 4 bytes sequence + 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx => 110110ww wwzzzzyy 110111yy yyxxxxxx + where uuuuu = wwww + 1 */ + register uchar byte2; + register uchar byte3; + register uchar byte4; + if (inLen < 4 || cd->toCcsid == 13488) { + if ((inLen >= 2 && (pIn[1] & 0xC0) != 0x80) || + (inLen >= 3 && (pIn[2] & 0xC0) != 0x80) || + (cd->toCcsid == 13488) ) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + ++pIn; + byte2=*pIn; + ++pIn; + byte3=*pIn; + ++pIn; + byte4=*pIn; + if ((byte2 & 0xC0) == 0x80 && /* byte2 & 0b11000000 == 0b10000000 */ + (byte3 & 0xC0) == 0x80 && /* byte3 & 0b11000000 == 0b10000000 */ + (byte4 & 0xC0) == 0x80) { /* byte4 & 0b11000000 == 0b10000000 */ + register uchar work=byte2; + if (byte1 == 0xF0 && byte2 < 0x90) { + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + } else if (byte1 == 0xF4 && byte2 > 0x8F) { + errno=EINVAL; /* 22 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + } + + work&=0x30; /* 0b00110000; */ + work>>=4; + byte1&=0x07; /* 0b00000111; */ + byte1<<=2; + byte1+=work; /* uuuuu */ + --byte1; /* wwww */ + + work=byte1 & 0x0F; + work>>=2; + work+=0xD8; /* 0b11011011; */ + *pOut=work; + ++pOut; + + byte1<<=6; + byte2<<=2; + byte2&=0x3C; /* 0b00111100; */ + work=byte3; + work>>=4; + work&=0x03; /* 0b00000011; */ + work|=byte1; + work|=byte2; + *pOut=work; + ++pOut; + + work=byte3; + work>>=2; + work&=0x03; /* 0b00000011; */ + work|=0xDC; /* 0b110111xx; */ + *pOut=work; + ++pOut; + + byte3<<=6; + byte4&=0x3F; /* 0b00111111; */ + byte4|=byte3; + *pOut=byte4; + ++pOut; + inLen-=4; + ++pIn; + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn-3; + *numSub+=numS; + return -1; + } + } else if ((byte1 & 0xF0) == 0xF0) { + if (cd->toCcsid == 13488) { + errno=EILSEQ; /* 116 */ + } else { + if (inLen == 1) + errno=EINVAL; /* 22 */ + else if (inLen == 2 && (pIn[1] & 0xC0) != 0x80) + errno=EILSEQ; /* 116 */ + else if (inLen == 3 && ((pIn[1] & 0xC0) != 0x80 || (pIn[2] & 0xC0) != 0x80)) + errno=EILSEQ; /* 116 */ + else if (inLen >= 4 && ((pIn[1] & 0xC0) != 0x80 || (pIn[2] & 0xC0) != 0x80 || (pIn[3] & 0xC0) != 0x80)) + errno=EILSEQ; /* 116 */ + else + errno=EINVAL; /* 22 */ + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + + } else { /* invalid sequence */ + errno=EILSEQ; /* 116 */ + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return -1; + } + } + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + *numSub+=numS; + return 0; + } else if (cd->cnv_dmap->codingSchema == DMAP_U28) { + /* See http://unicode.org/versions/corrigendum1.html */ + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + // register size_t numS=0; + while (0 < inLen) { + register uint32_t in; + if (inLen == 1) { + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + return -1; + } + if (pLastOutBuf < pOut) + break; + in=pIn[0]; + in<<=8; + in+=pIn[1]; + if (in == 0x0000) { + *pOut=0x00; + ++pOut; + } else if ((in & 0xFF80) == 0x0000) { /* U28: in & 0b1111111110000000 == 0x0000 */ + *pOut=in; + ++pOut; + } else if ((in & 0xF800) == 0x0000) { /* in & 0b1111100000000000 == 0x0000 */ + register uchar byte; + in>>=6; + in&=0x001F; /* 0b0000000000011111 */ + in|=0x00C0; /* 0b0000000011000000 */ + *pOut=in; + ++pOut; + byte=pIn[1]; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + } else { + register uchar byte; + register uchar work; + byte=pIn[0]; + byte>>=4; + byte|=0xE0; /* 0b11100000; */ + *pOut=byte; + ++pOut; + + byte=pIn[0]; + byte<<=2; + work=pIn[1]; + work>>=6; + byte|=work; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + + byte=pIn[1]; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + } + pIn+=2; + inLen-=2; + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + // *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_T28) { /* UTF-16_UTF-8 */ + /* See http://unicode.org/versions/corrigendum1.html */ + register int inLen=*inBytesLeft; + register char * pOut=*outBuf; + register char * pIn=*inBuf; + register char * pLastOutBuf = *outBuf + *outBytesLeft - 1; + // register size_t numS=0; + while (0 < inLen) { + register uint32_t in; + if (inLen == 1) { + errno=EINVAL; /* 22 */ + *inBytesLeft=0; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + return 0; + } + if (pLastOutBuf < pOut) + break; + in=pIn[0]; + in<<=8; + in+=pIn[1]; + if (in == 0x0000) { + *pOut=0x00; + ++pOut; + } else if ((in & 0xFF80) == 0x0000) { /* U28: in & 0b1111111110000000 == 0x0000 */ + *pOut=in; + ++pOut; + } else if ((in & 0xF800) == 0x0000) { /* in & 0b1111100000000000 == 0x0000 */ + register uchar byte; + in>>=6; + in&=0x001F; /* 0b0000000000011111 */ + in|=0x00C0; /* 0b0000000011000000 */ + *pOut=in; + ++pOut; + byte=pIn[1]; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + } else if ((in & 0xFC00) == 0xD800) { /* in & 0b1111110000000000 == 0b1101100000000000, first surrogate character */ + if (0xDC00 <= in ) { + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-1; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + return -1; + + } else if (inLen < 4) { + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-2; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn+2; + return -1; + + } else if ((pIn[2] & 0xFC) != 0xDC) { /* pIn[2] & 0b11111100 == 0b11011100, second surrogate character */ + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-2; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn+2; + return -1; + + } else { + register uchar byte; + register uchar work; + in>>=6; + in&=0x000F; /* 0b0000000000001111 */ + byte=in; /* wwww */ + ++byte; /* uuuuu */ + work=byte; /* save uuuuu */ + byte>>=2; + byte|=0xF0; /* 0b11110000; */ + *pOut=byte; + ++pOut; + + byte=work; + byte&=0x03; /* 0b00000011; */ + byte<<=4; + byte|=0x80; /* 0b10000000; */ + work=pIn[1]; + work&=0x3C; /* 0b00111100; */ + work>>=2; + byte|=work; + *pOut=byte; + ++pOut; + + byte=pIn[1]; + byte&=0x03; /* 0b00000011; */ + byte<<=4; + byte|=0x80; /* 0b10000000; */ + work=pIn[2]; + work&=0x03; /* 0b00000011; */ + work<<=2; + byte|=work; + work=pIn[3]; + work>>=6; + byte|=work; + *pOut=byte; + ++pOut; + + byte=pIn[3]; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + pIn+=2; + inLen-=2; + } + } else if ((in & 0xFC00) == 0xDC00) { /* in & 0b11111100 == 0b11011100, second surrogate character */ + errno=EINVAL; /* 22 */ + *inBytesLeft=inLen-1; + *outBytesLeft-=(pOut-*outBuf); + *outBuf=pOut; + *inBuf=pIn; + return -1; + + } else { + register uchar byte; + register uchar work; + byte=pIn[0]; + byte>>=4; + byte|=0xE0; /* 0b11100000; */ + *pOut=byte; + ++pOut; + + byte=pIn[0]; + byte<<=2; + work=pIn[1]; + work>>=6; + byte|=work; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + + byte=pIn[1]; + byte&=0x3F; /* 0b00111111; */ + byte|=0x80; /* 0b10000000; */ + *pOut=byte; + ++pOut; + } + pIn+=2; + inLen-=2; + } + *outBytesLeft-=(pOut-*outBuf); + *inBytesLeft=inLen; + *outBuf=pOut; + *inBuf=pIn; + // *numSub+=numS; + return 0; + + } else if (cd->cnv_dmap->codingSchema == DMAP_U2U) { /* UTF-16_UCS-2 */ + register int inLen=*inBytesLeft; + register int outLen=*outBytesLeft; + if (inLen <= outLen) { + memcpy(*outBuf, *inBuf, inLen); + (*outBytesLeft)-=inLen; + (*inBuf)+=inLen; + (*outBuf)+=inLen; + *inBytesLeft=0; + return 0; + } + memcpy(*outBuf, *inBuf, outLen); + (*outBytesLeft)=0; + (*inBuf)+=outLen; + (*outBuf)+=outLen; + *inBytesLeft-=outLen; + return (*inBytesLeft); + + } else { + return -1; + } + return 0; +} + + +#ifdef DEBUG +inline size_t myconv(myconv_t cd , + char** inBuf, + size_t* inBytesLeft, + char** outBuf, + size_t* outBytesLeft, + size_t* numSub) +{ + if (cd->converterType == CONVERTER_ICONV) { + return myconv_iconv(cd,inBuf,inBytesLeft,outBuf,outBytesLeft,numSub); + } else if (cd->converterType == CONVERTER_DMAP) { + return myconv_dmap(cd,inBuf,inBytesLeft,outBuf,outBytesLeft,numSub); + } + return -1; +} + +inline char * converterName(int32_t type) +{ + if (type == CONVERTER_ICONV) + return "iconv"; + else if (type == CONVERTER_DMAP) + return "dmap"; + + return "?????"; +} +#else +#define myconv(a,b,c,d,e,f) \ +(((a)->converterType == CONVERTER_ICONV)? myconv_iconv((a),(b),(c),(d),(e),(f)): (((a)->converterType == CONVERTER_DMAP)? myconv_dmap((a),(b),(c),(d),(e),(f)): -1)) + + +#define converterName(a) \ +(((a) == CONVERTER_ICONV)? "iconv": ((a) == CONVERTER_DMAP)? "dmap": "?????") +#endif + +void initMyconv(); +void cleanupMyconv(); + +#endif diff --git a/storage/ibmdb2i/db2i_rir.cc b/storage/ibmdb2i/db2i_rir.cc new file mode 100644 index 00000000000..a80a181c9ac --- /dev/null +++ b/storage/ibmdb2i/db2i_rir.cc @@ -0,0 +1,465 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#include "ha_ibmdb2i.h" + +/* Helper function for records_in_range. + Input: Bitmap of used key parts. + Output: Number of used key parts. */ + +static inline int getKeyCntFromMap(key_part_map keypart_map) +{ + int cnt = 0; + while (keypart_map) + { + keypart_map = keypart_map >> 1; + cnt++; + } + return (cnt); +} + + +/** + @brief + Given a starting key and an ending key, estimate the number of rows that + will exist between the two keys. + + INPUT + inx Index to use + min_key Min key. Is NULL if no min range + max_key Max key. Is NULL if no max range + + NOTES + min_key.flag can have one of the following values: + HA_READ_KEY_EXACT Include the key in the range + HA_READ_AFTER_KEY Don't include key in range + + max_key.flag can have one of the following values: + HA_READ_BEFORE_KEY Don't include key in range + HA_READ_AFTER_KEY Include all 'end_key' values in the range + + RETURN + HA_POS_ERROR Error or the storage engine cannot estimate the number of rows + 1 There are no matching keys in the given range + n > 0 There are approximately n rows in the range +*/ +ha_rows ha_ibmdb2i::records_in_range(uint inx, + key_range *min_key, + key_range *max_key) +{ + DBUG_ENTER("ha_ibmdb2i::records_in_range"); + int rc = 0; // Return code + ha_rows rows = 0; // Row count returned to caller of this method + uint32 spcLen; // Length of space passed to DB2 + uint32 keyCnt; // Number of fields in the key composite + uint32 literalCnt = 0; // Number of literals + uint32 boundsOff; // Offset from beginning of space to range bounds + uint32 litDefOff; // Offset from beginning of space to literal definitions + uint32 literalsOff; // Offset from beginning of space to literal values + uint32 cutoff = 0; // Early exit cutoff (currently not used) + uint64 recCnt; // Row count from DB2 + uint16 rtnCode; // Return code from DB2 + Bounds* boundsPtr; // Pointer to a pair of range bounds + Bound* boundPtr; // Pointer to a single (high or low) range bound + LitDef* litDefPtr; // Pointer to a literal definition + char* literalsPtr; // Pointer to the start of all literal values + char* literalPtr; // Pointer to the start of this literal value + char* tempPtr; // Temporary pointer + char* tempMinPtr; // Temporary pointer into min_key + int minKeyCnt = 0; // Number of fields in the min_key composite + int maxKeyCnt = 0; // Number of fields in the max_key composite + size_t tempLen = 0; // Temporary length + uint16 DB2FieldWidth = 0; // DB2 field width + uint32 workFieldLen = 0; // Length of workarea needed for CCSID conversions + bool overrideInclusion; // Indicator for inclusion/exclusion + char* endOfLiteralPtr; // Pointer to the end of this literal + char* endOfMinPtr; // Pointer to end of min_key + uint16 endByte = 0; // End byte of char or graphic literal (padding not included) + bool reuseLiteral; // Indicator that hi and lo bounds use same literal + char* minPtr = NULL; // Work pointer for traversing min_key + char* maxPtr = NULL; // Work pointer for traversing max_key + /* + Handle the special case of 'x < null' anywhere in the key range. There are + no values less than null, but return 1 so that MySQL does not assume + the empty set for the query. + */ + if (min_key != NULL && max_key != NULL && + min_key->flag == HA_READ_AFTER_KEY && max_key->flag == HA_READ_BEFORE_KEY && + min_key->length == max_key->length && + (memcmp((uchar*)min_key->key,(uchar*)max_key->key,min_key->length)==0)) + { + DBUG_PRINT("ha_ibmdb2i::records_in_range",("Estimate 1 row for key %d; special case: < null", inx)); + DBUG_RETURN((ha_rows) 1 ); + } + /* + Determine the number of fields in the key composite. + */ + + if (min_key) + { + minKeyCnt = getKeyCntFromMap(min_key->keypart_map); + minPtr = (char*)min_key->key; + } + if (max_key) + { + maxKeyCnt = getKeyCntFromMap(max_key->keypart_map); + maxPtr = (char*)max_key->key; + } + keyCnt = maxKeyCnt >= minKeyCnt ? maxKeyCnt : minKeyCnt; + + /* + Handle the special case where MySQL does not pass either a min or max + key range. In this case, set the key count to 1 (knowing that there + is at least one key field) to flow through and create one bounds structure. + When both the min and max key ranges are nil, the bounds structure will + specify positive and negative infinity and DB2 will estimate the total + number of rows. */ + + if (keyCnt == 0) + keyCnt = 1; + + /* + Allocate the space needed to pass range information to DB2. The + space must be large enough to store the following: + - one pair of bounds (high and low) per field in the key composite + - one literal definition per literal value + - the literal values + - work area for literal CCSID conversions + Since we don't know yet how many of these structures are needed, + allocate enough space for the maximum that we will possibly need. + The workarea for the literal conversion must be big enough to hold the + largest of the DB2 key fields. + */ + KEY& curKey = table->key_info[inx]; + + for (int i = 0; i < keyCnt; i++) + { + DB2FieldWidth = + db2Table->db2Field(curKey.key_part[i].field->field_index).getByteLengthInRecord(); + if (DB2FieldWidth > workFieldLen) + workFieldLen = DB2FieldWidth; // Get length of largest DB2 field + tempLen = tempLen + DB2FieldWidth; // Tally the DB2 field lengths + } + spcLen = (sizeof(Bounds)*keyCnt) + (sizeof(LitDef)*keyCnt*2) + (tempLen*2) + workFieldLen; + + ValidatedPointer<char> spcPtr(spcLen); // Pointer to space passed to DB2 + memset(spcPtr, 0, spcLen); // Clear the allocated space + /* + Set addressability to the various sections of the DB2 interface space. + */ + boundsOff = 0; // Range bounds are at the start of the space + litDefOff = sizeof(Bounds) * keyCnt; // Literal defs follow all the range bounds + literalsOff = litDefOff + (sizeof(LitDef) * keyCnt * 2); // Literal values are last + boundsPtr = (Bounds_t*)(void*)spcPtr; // Address first bounds structure + tempPtr = (char*)((char*)spcPtr + litDefOff); + litDefPtr = (LitDef_t*)tempPtr; // Address first literal definition + tempPtr = (char*)((char*)spcPtr + literalsOff); + literalsPtr = (char*)tempPtr; // Address start of literal values + literalPtr = literalsPtr; // Address first literal value + /* + For each key part, build the low (min) and high (max) DB2 range bounds. + If literals are specified in the MySQL range, build DB2 literal + definitions and store the literal values for access by DB2. + + If no value is specified for a key part, assume infinity. Negative + infinity will cause processing to start at the first index entry. + Positive infinity will cause processing to end at the last index entry. + When infinity is specified in a bound, inclusion/exclusion and position + are ignored, and there is no literal definition or literal value for + the bound. + + If the keypart value is null, the null indicator is set in the range + bound and the other fields in the bound are ignored. When the bound is + null, only index entries with the null value will be included in the + estimate. If one bound is null, both bounds must be null. When the bound + is not null, the data offset and length must be set, and the literal + value stored for access by DB2. + */ + for (int partsInUse = 0; partsInUse < keyCnt; ++partsInUse) + { + Field *field= curKey.key_part[partsInUse].field; + overrideInclusion = false; + reuseLiteral = false; + endOfLiteralPtr = NULL; + /* + Build the low bound for the key range. + */ + if ((partsInUse + 1) > minKeyCnt) // if no min_key info for this part + boundsPtr->LoBound.Infinity[0] = QMY_NEG_INFINITY; // select...where 3 between x and y + else + { + if ((curKey.key_part[partsInUse].null_bit) && (char*)minPtr[0]) + { // min_key is null + if (max_key == NULL || + ((partsInUse + 1) > maxKeyCnt)) // select...where x='ab' and y=null and z != 'c' + boundsPtr->LoBound.Infinity[0] = QMY_NEG_INFINITY; // select...where x not null or + // select...where x > null + else // max_key is not null + { + if (min_key->flag == HA_READ_KEY_EXACT) + boundsPtr->LoBound.IsNull[0] = QMY_YES; // select...where x is null + else + { + if ((char*)maxPtr[0]) + boundsPtr->LoBound.IsNull[0] = QMY_YES; // select...where a = null and b < 5 (max-before) + // select...where a='a' and b is null and c !='a' (max-after) + else + boundsPtr->LoBound.Infinity[0] = QMY_NEG_INFINITY; // select...where x < y + } + } // end min_key is null + } + else // min_key is not null + { + if (literalCnt) litDefPtr = litDefPtr + 1; + literalCnt = literalCnt + 1; + boundsPtr->LoBound.Position = literalCnt; + /* + Determine inclusion or exclusion. + */ + if (min_key->flag == HA_READ_KEY_EXACT || //select...where a like 'this%' + + /* An example for the following conditions is 'select...where a = 5 and b > null'. */ + + (max_key && + (memcmp((uchar*)minPtr,(uchar*)maxPtr, + curKey.key_part[partsInUse].store_length)==0))) + + { + if ((min_key->flag != HA_READ_KEY_EXACT) || + (max_key && + (memcmp((uchar*)minPtr,(uchar*)maxPtr, + curKey.key_part[partsInUse].store_length)==0))) + overrideInclusion = true; // Need inclusion for both min and max + } + else + boundsPtr->LoBound.Embodiment[0] = QMY_EXCLUSION; + litDefPtr->FieldNbr = field->field_index + 1; + DB2Field& db2Field = db2Table->db2Field(field->field_index); + litDefPtr->DataType = db2Field.getType(); + /* + Convert the literal to DB2 format. + */ + rc = convertMySQLtoDB2(field, + db2Field, + literalPtr, + (uchar*)minPtr+((curKey.key_part[partsInUse].null_bit)? 1 : 0)); + if (rc != 0) break; + litDefPtr->Offset = (uint32_t)(literalPtr - literalsPtr); + litDefPtr->Length = db2Field.getByteLengthInRecord(); + tempLen = litDefPtr->Length; + /* + Do additional conversion of a character or graphic value. + */ + CHARSET_INFO* fieldCharSet = field->charset(); + if ((field->type() != MYSQL_TYPE_BIT) && // Don't do conversion on BIT data + (field->charset() != &my_charset_bin) && // Don't do conversion on BINARY data + (litDefPtr->DataType == QMY_CHAR || litDefPtr->DataType == QMY_VARCHAR || + litDefPtr->DataType == QMY_GRAPHIC || litDefPtr->DataType == QMY_VARGRAPHIC)) + { + if (litDefPtr->DataType == QMY_VARCHAR || + litDefPtr->DataType == QMY_VARGRAPHIC) + tempPtr = literalPtr + sizeof(uint16); + else + tempPtr = literalPtr; + /* The following code checks to determine if MySQL is passing a + partial key. DB2 will accept a partial field value, but only + in the last field position of the key composite (and only if + there is no ICU sort sequence on the index). */ + tempMinPtr = (char*)minPtr+((curKey.key_part[partsInUse].null_bit)? 1 : 0); + if (field->type() == MYSQL_TYPE_VARCHAR) + { + /* MySQL always stores key lengths as 2 bytes, little-endian. */ + tempLen = *(uint8*)tempMinPtr + ((*(uint8*)(tempMinPtr+1)) << 8); + tempMinPtr = (char*)((char*)tempMinPtr + 2); + } + else + tempLen = field->field_length; + + /* Determine if we are dealing with a partial key and if so, find the end of the partial key. */ + if (litDefPtr->DataType == QMY_CHAR || litDefPtr->DataType == QMY_VARCHAR ) + { /* Char or varchar. If UTF8, no conversion is done to DB2 graphic.) */ + endOfMinPtr = (char*)memchr(tempMinPtr,field->charset()->min_sort_char,tempLen); + if (endOfMinPtr) + endOfLiteralPtr = tempPtr + ((uint32_t)(endOfMinPtr - tempMinPtr)); + } + else + { + if (strncmp(fieldCharSet->csname, "utf8", sizeof("utf8")) == 0) + { /* The MySQL charset is UTF8 but we are converting to graphic on DB2. Divide number of UTF8 bytes + by 3 to get the number of characters, then multiple by 2 for double-byte graphic.*/ + endOfMinPtr = (char*)memchr(tempMinPtr,field->charset()->min_sort_char,tempLen); + if (endOfMinPtr) + endOfLiteralPtr = tempPtr + (((uint32_t)((endOfMinPtr - tempMinPtr)) / 3) * 2); + } + else + { /* The DB2 data type is graphic or vargraphic, and we are not converting from UTF8 to graphic. */ + endOfMinPtr = (char*)wmemchr((wchar_t*)tempMinPtr,field->charset()->min_sort_char,tempLen/2); + if (endOfMinPtr) + endOfLiteralPtr = tempPtr + (endOfMinPtr - tempMinPtr); + } + } + /* Enforce here that a partial is only allowed on the last field position + of the key composite */ + if (endOfLiteralPtr) + { + if ((partsInUse + 1) < minKeyCnt) + { + rc = HA_POS_ERROR; + break; + } + endByte = endOfLiteralPtr - tempPtr; + /* We're making an assumption that if MySQL gives us a partial key, + the length of the partial is the same for both the min_key and max_key. */ + } + } + literalPtr = literalPtr + litDefPtr->Length; // Bump pointer for next literal + } + /* If there is a max_key value for this field, and if the max_key value is + the same as the min_key value, then the low bound literal can be reused + for the high bound literal. This eliminates the overhead of copying and + converting the same value twice. */ + if (max_key && ((partsInUse + 1) <= maxKeyCnt) && + (memcmp((uchar*)minPtr,(uchar*)maxPtr, + curKey.key_part[partsInUse].store_length)==0 || endOfLiteralPtr)) + reuseLiteral = true; + minPtr += curKey.key_part[partsInUse].store_length; + } + /* + Build the high bound for the key range. + */ + if (max_key == NULL || ((partsInUse + 1) > maxKeyCnt)) + boundsPtr->HiBound.Infinity[0] = QMY_POS_INFINITY; + else + { + if ((curKey.key_part[partsInUse].null_bit) && (char*)maxPtr[0]) + { + if (min_key == NULL) + boundsPtr->HiBound.Infinity[0] = QMY_POS_INFINITY; + else + boundsPtr->HiBound.IsNull[0] = QMY_YES; // select...where x is null + } + else // max_key field is not null + { + if (boundsPtr->LoBound.IsNull[0] == QMY_YES) // select where x < 10 or x is null + { + rc = HA_POS_ERROR; + break; + } + if (!reuseLiteral) + { + if (literalCnt) + litDefPtr = litDefPtr + 1; + literalCnt = literalCnt + 1; + litDefPtr->FieldNbr = field->field_index + 1; + DB2Field& db2Field = db2Table->db2Field(field->field_index); + litDefPtr->DataType = db2Field.getType(); + /* + Convert the literal to DB2 format + */ + rc = convertMySQLtoDB2(field, + db2Field, + literalPtr, + (uchar*)maxPtr+((curKey.key_part[partsInUse].null_bit)? 1 : 0)); + if (rc != 0) break; + litDefPtr->Offset = (uint32_t)(literalPtr - literalsPtr); + litDefPtr->Length = db2Field.getByteLengthInRecord(); + tempLen = litDefPtr->Length; + /* + Now convert a character or graphic value. + */ + if ((field->type() != MYSQL_TYPE_BIT) && + (litDefPtr->DataType == QMY_CHAR || litDefPtr->DataType == QMY_VARCHAR || + litDefPtr->DataType == QMY_GRAPHIC || litDefPtr->DataType == QMY_VARGRAPHIC)) + { + if (litDefPtr->DataType == QMY_VARCHAR || litDefPtr->DataType == QMY_VARGRAPHIC) + { + tempPtr = literalPtr + sizeof(uint16); + } + else + tempPtr = literalPtr; + } + literalPtr = literalPtr + litDefPtr->Length; // Bump pointer for next literal + } + boundsPtr->HiBound.Position = literalCnt; + if (max_key->flag == HA_READ_BEFORE_KEY && !overrideInclusion) + boundsPtr->HiBound.Embodiment[0] = QMY_EXCLUSION; + } + maxPtr += curKey.key_part[partsInUse].store_length; + } + /* + Bump to the next field in the key composite. + */ + + if ((partsInUse+1) < keyCnt) + boundsPtr = boundsPtr + 1; + } + + /* + Call DB2 to estimate the number of rows in the key range. + */ + if (rc == 0) + { + rc = db2i_ileBridge::getBridgeForThread()->recordsInRange((indexHandles[inx] ? indexHandles[inx] : db2Table->indexFile(inx)->getMasterDefnHandle()), + spcPtr, + keyCnt, + literalCnt, + boundsOff, + litDefOff, + literalsOff, + cutoff, + (uint32_t)(literalPtr - (char*)spcPtr), + endByte, + &recCnt, + &rtnCode); + } + /* + Set the row count and return. + Beware that if this method returns a zero row count, MySQL assumes the + result set for the query is zero; never return a zero row count. + */ + if ((rc == 0) && (rtnCode == QMY_SUCCESS || rtnCode == QMY_EARLY_EXIT)) + { + rows = recCnt ? (ha_rows)recCnt : 1; + } + + rows = (rows > 0 ? rows : HA_POS_ERROR); + + setIndexReadEstimate(inx, rows); + + DBUG_PRINT("ha_ibmdb2i::recordsInRange",("Estimate %d rows for key %d", uint32(rows), inx)); + + DBUG_RETURN(rows); +} diff --git a/storage/ibmdb2i/db2i_safeString.h b/storage/ibmdb2i/db2i_safeString.h new file mode 100644 index 00000000000..e353316c8fc --- /dev/null +++ b/storage/ibmdb2i/db2i_safeString.h @@ -0,0 +1,98 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + + +#ifndef DB2I_SAFESTRING_H +#define DB2I_SAFESTRING_H + + +#include <my_global.h> +#include <string.h> + +/** + @class SafeString + + This class was designed to provide safe, but lightweight, concatenation + operations C strings inside pre-allocated buffers. +*/ +class SafeString +{ +public: + SafeString(char* buffer, size_t size) : + allocSize(size), curPos(0), buf(buffer) + { + DBUG_ASSERT(size > 0); + buf[allocSize - 1] = 0xFF; // Set an overflow indicator + } + + char* ptr() { return buf; } + operator char*() { return buf; } + + SafeString& strcat(const char* str) + { + return this->strncat(str, strlen(str)); + } + + SafeString& strcat(char one) + { + if (curPos < allocSize - 2) + { + buf[curPos++] = one; + } + buf[curPos] = 0; + + return *this; + } + + SafeString& strncat(const char* str, size_t len) + { + uint64 amountToCopy = min((allocSize-1) - curPos, len); + memcpy(buf + curPos, str, amountToCopy); + curPos += amountToCopy; + buf[curPos] = 0; + return *this; + } + + bool overflowed() const { return (buf[allocSize - 1] == 0);} + +private: + char* buf; + uint64 curPos; + size_t allocSize; +}; + + +#endif diff --git a/storage/ibmdb2i/db2i_sqlStatementStream.cc b/storage/ibmdb2i/db2i_sqlStatementStream.cc new file mode 100644 index 00000000000..92a8b03fd00 --- /dev/null +++ b/storage/ibmdb2i/db2i_sqlStatementStream.cc @@ -0,0 +1,86 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#include "db2i_sqlStatementStream.h" +#include "as400_types.h" + +/** + Add a statement to the statement stream, allocating additional memory as needed. + + @parm stmt The statement text + @parm length The length of the statement text + @parm fileSortSequence The DB2 sort sequence identifier, in EBCDIC + @parm fileSortSequenceLibrary The DB2 sort sequence library, in EBCDIC + + @return Reference to this object +*/ +SqlStatementStream& SqlStatementStream::addStatementInternal(const char* stmt, + uint32 length, + const char* fileSortSequence, + const char* fileSortSequenceLibrary) +{ + uint32 storageNeeded = length + sizeof(StmtHdr_t); + storageNeeded = (storageNeeded + 3) & ~3; // We have to be 4-byte aligned. + if (storageNeeded > storageRemaining()) + { + // We overallocate new storage to reduce number of times reallocation is + // needed. + int newSize = curSize + 2 * storageNeeded; + DBUG_PRINT("SqlStatementStream::addStatementInternal", + ("PERF: Had to realloc! Old size=%d. New size=%d", curSize, newSize)); + char* old_space = block; + char* new_space = (char*)getNewSpace(newSize); + memcpy(new_space, old_space, curSize); + ptr = new_space + (ptr - old_space); + curSize = newSize; + } + + DBUG_ASSERT((address64_t)ptr % 4 == 0); + + memcpy(((StmtHdr_t*)ptr)->SrtSeqNam, + fileSortSequence, + sizeof(((StmtHdr_t*)ptr)->SrtSeqNam)); + memcpy(((StmtHdr_t*)ptr)->SrtSeqSch, + fileSortSequenceLibrary, + sizeof(((StmtHdr_t*)ptr)->SrtSeqSch)); + ((StmtHdr_t*)ptr)->Length = length; + memcpy(ptr + sizeof(StmtHdr_t), stmt, length); + + ptr += storageNeeded; + ++statements; + + return *this; +} diff --git a/storage/ibmdb2i/db2i_sqlStatementStream.h b/storage/ibmdb2i/db2i_sqlStatementStream.h new file mode 100644 index 00000000000..11db41a6c5d --- /dev/null +++ b/storage/ibmdb2i/db2i_sqlStatementStream.h @@ -0,0 +1,151 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +#ifndef DB2I_SQLSTATEMENTSTREAM_H +#define DB2I_SQLSTATEMENTSTREAM_H + +#include "db2i_charsetSupport.h" +#include "qmyse.h" + +/** + @class SqlStatementStream + + This class handles building the stream of SQL statements expected by the + QMY_EXECUTE_IMMEDIATE and QMY_PREPARE_OPEN_CURSOR APIs. + Memory allocation is handled internally. +*/ +class SqlStatementStream +{ + public: + /** + ctor to be used when multiple strings may be appended. + */ + SqlStatementStream(uint32 firstStringSize) : statements(0) + { + curSize = firstStringSize + sizeof(StmtHdr_t); + curSize = (curSize + 3) & ~3; + ptr = (char*) getNewSpace(curSize); + if (ptr == NULL) + curSize = 0; + } + + /** + ctor to be used when only a single statement will be executed. + */ + SqlStatementStream(const String& statement) : statements(0), block(NULL), curSize(0), ptr(0) + { + addStatement(statement); + } + + /** + ctor to be used when only a single statement will be executed. + */ + SqlStatementStream(const char* statement) : statements(0), block(NULL), curSize(0), ptr(0) + { + addStatement(statement); + } + + /** + Append an SQL statement, specifiying the DB2 sort sequence under which + the statement should be executed. This is important for CREATE TABLE + and CREATE INDEX statements. + */ + SqlStatementStream& addStatement(const String& append, const char* fileSortSequence, const char* fileSortSequenceLibrary) + { + char sortSeqEbcdic[10]; + char sortSeqLibEbcdic[10]; + + DBUG_ASSERT(strlen(fileSortSequence) <= 10 && + strlen(fileSortSequenceLibrary) <= 10); + memset(sortSeqEbcdic, 0x40, 10); + memset(sortSeqLibEbcdic, 0x40, 10); + convToEbcdic(fileSortSequence, sortSeqEbcdic, strlen(fileSortSequence)); + convToEbcdic(fileSortSequenceLibrary, sortSeqLibEbcdic, strlen(fileSortSequenceLibrary)); + + return addStatementInternal(append.ptr(), append.length(), sortSeqEbcdic, sortSeqLibEbcdic); + } + + /** + Append an SQL statement using default (*HEX) sort sequence. + */ + SqlStatementStream& addStatement(const String& append) + { + const char splatHEX[] = {0x5C, 0xC8, 0xC5, 0xE7, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}; // *HEX + const char blanks[] = {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}; // + + return addStatementInternal(append.ptr(), append.length(), splatHEX, blanks); + } + + /** + Append an SQL statement using default (*HEX) sort sequence. + */ + SqlStatementStream& addStatement(const char* stmt) + { + const char splatHEX[] = {0x5C, 0xC8, 0xC5, 0xE7, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}; // *HEX + const char blanks[] = {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}; // + + return addStatementInternal(stmt, strlen(stmt), splatHEX, blanks); + } + + char* getPtrToData() const { return block; } + uint32 getStatementCount() const { return statements; } + private: + SqlStatementStream& addStatementInternal(const char* stmt, + uint32 length, + const char* fileSortSequence, + const char* fileSortSequenceLibrary); + + uint32 storageRemaining() const + { + return (block == NULL ? 0 : curSize - (ptr - block)); + } + + char* getNewSpace(size_t size) + { + allocBase = (char*)sql_alloc(size + 15); + block = (char*)roundToQuadWordBdy(allocBase); + return block; + } + + uint32 curSize; // The size of the usable memory. + char* allocBase; // The allocated memory (with padding for aligment) + char* block; // The usable memory chunck (aligned for ILE) + char* ptr; // The current position within block. + uint32 statements; // The number of statements that have been appended. +}; + +#endif + diff --git a/storage/ibmdb2i/db2i_validatedPointer.h b/storage/ibmdb2i/db2i_validatedPointer.h new file mode 100644 index 00000000000..c4e31d1f11b --- /dev/null +++ b/storage/ibmdb2i/db2i_validatedPointer.h @@ -0,0 +1,162 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +#ifndef DB2I_VALIDATEDPOINTER_H +#define DB2I_VALIDATEDPOINTER_H + +#include "db2i_ileBridge.h" + +/** + @class ValidatedPointer + @brief Encapsulates a pointer registered for usage by the QMYSE APIs + + @details As a performance optimization, to prevent pointer validation each + time a particular pointer is thunked across to ILE, QMYSE allows us to + "register" a pointer such that it is validated once and then subsequently + referenced on QMYSE APIs by means of a handle value. This class should be + used to manage memory allocation/registration/unregistration of these + pointers. Using the alloc function guarantees that the resulting storage is + 16-byte aligned, a requirement for many pointers passed to QMYSE. +*/ +template <class T> +class ValidatedPointer +{ +public: + ValidatedPointer<T>() : address(NULL), handle(NULL) {;} + + ValidatedPointer<T>(size_t size) + { + alloc(size); + } + + ValidatedPointer<T>(T* ptr) + { + assign(ptr); + } + + operator T*() + { + return address; + }; + + operator T*() const + { + return address; + }; + + operator void*() + { + return address; + }; + + operator ILEMemHandle() + { + return handle; + } + + void alloc(size_t size) + { + address = (T*)malloc_aligned(size); + if (address) + db2i_ileBridge::registerPtr(address, &handle); + mallocedHere = 1; + } + + void assign(T* ptr) + { + address = ptr; + db2i_ileBridge::registerPtr((void*)ptr, &handle); + mallocedHere = 0; + } + + void realloc(size_t size) + { + dealloc(); + alloc(size); + } + + void reassign(T* ptr) + { + dealloc(); + assign(ptr); + } + + void dealloc() + { + if (address) + { + db2i_ileBridge::unregisterPtr(handle); + + if (mallocedHere) + free_aligned((void*)address); + } + address = NULL; + handle = 0; + } + + ~ValidatedPointer() + { + dealloc(); + } + +private: + // Disable copy ctor and assignment operator, as these would break + // the registration guarantees provided by the class. + ValidatedPointer& operator= (const ValidatedPointer newVal); + ValidatedPointer(ValidatedPointer& newCopy); + + ILEMemHandle handle; + T* address; + char mallocedHere; +}; + + +/** + @class ValidatedObject + @brief This class allows users to instantiate and register a particular + object in a single step. +*/ +template<class T> +class ValidatedObject : public ValidatedPointer<T> +{ + public: + ValidatedObject<T>() : ValidatedPointer<T>(&value) {;} + + T& operator= (const T newVal) { value = newVal; return value; } + + private: + T value; +}; +#endif diff --git a/storage/ibmdb2i/ha_ibmdb2i.cc b/storage/ibmdb2i/ha_ibmdb2i.cc new file mode 100644 index 00000000000..6c7ce12ded1 --- /dev/null +++ b/storage/ibmdb2i/ha_ibmdb2i.cc @@ -0,0 +1,3286 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + + +/** + @file ha_ibmdb2i.cc + + @brief + The ha_ibmdb2i storage engine provides an interface from MySQL to IBM DB2 for i. + +*/ + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#include "ha_ibmdb2i.h" +#include "mysql_priv.h" +#include <mysql/plugin.h> +#include "db2i_ileBridge.h" +#include "db2i_charsetSupport.h" +#include <sys/utsname.h> +#include "db2i_safeString.h" + +static const char __NOT_NULL_VALUE_EBCDIC = 0xF0; // '0' +static const char __NULL_VALUE_EBCDIC = 0xF1; // '1' +static const char __DEFAULT_VALUE_EBCDIC = 0xC4; // 'D' +static const char BlankASPName[19] = " "; +static const int DEFAULT_MAX_ROWS_TO_BUFFER = 4096; + +static const char SAVEPOINT_PREFIX[] = {0xD4, 0xE8, 0xE2, 0xD7}; // MYSP (in EBCDIC) + +OSVersion osVersion; + + +// ================================================================ +// ================================================================ +// System variables +static char* ibmdb2i_rdb_name; +static MYSQL_SYSVAR_STR(rdb_name, ibmdb2i_rdb_name, + PLUGIN_VAR_MEMALLOC | PLUGIN_VAR_READONLY, + "The name of the RDB to use", + NULL, + NULL, + BlankASPName); + +static MYSQL_THDVAR_BOOL(transaction_unsafe, + 0, + "Disable support for commitment control", + NULL, + NULL, + FALSE); + +static MYSQL_THDVAR_UINT(lob_alloc_size, + 0, + "Baseline allocation for lob read buffer", + NULL, + NULL, + 2*1024*1024, + 64*1024, + 128*1024*1024, + 1); + +static MYSQL_THDVAR_UINT(max_read_buffer_size, + 0, + "Maximum size of buffers used for read-ahead.", + NULL, + NULL, + 1*1024*1024, + 32*1024, + 16*1024*1024, + 1); + +static MYSQL_THDVAR_UINT(max_write_buffer_size, + 0, + "Maximum size of buffers used for bulk writes.", + NULL, + NULL, + 8*1024*1024, + 32*1024, + 64*1024*1024, + 1); + +static MYSQL_THDVAR_BOOL(compat_opt_time_as_duration, + 0, + "Control how new TIME columns should be defined in DB2. 0=time-of-day (default), 1=duration.", + NULL, + NULL, + FALSE); + +static MYSQL_THDVAR_UINT(compat_opt_year_as_int, + 0, + "Control how new YEAR columns should be defined in DB2. 0=CHAR(4) (default), 1=SMALLINT.", + NULL, + NULL, + 0, + 0, + 1, + 1); + +static MYSQL_THDVAR_UINT(compat_opt_blob_cols, + 0, + "Control how new TEXT and BLOB columns should be defined in DB2. 0=CLOB/BLOB (default), 1=VARCHAR/VARBINARY", + NULL, + NULL, + 0, + 0, + 1, + 1); + +static MYSQL_THDVAR_UINT(compat_opt_allow_zero_date_vals, + 0, + "Allow substitute values to be used when storing a column with a 0000-00-00 date component. 0=No substitution (default), 1=Substitute '0001-01-01'", + NULL, + NULL, + 0, + 0, + 1, + 1); + +static MYSQL_THDVAR_BOOL(propagate_default_col_vals, + 0, + "Should DEFAULT column values be propagated to the DB2 table definition.", + NULL, + NULL, + TRUE); + +static my_bool ibmdb2i_assume_exclusive_use; +static MYSQL_SYSVAR_BOOL(assume_exclusive_use, ibmdb2i_assume_exclusive_use, + 0, + "Can MySQL assume that this process is the only one modifying the DB2 tables. ", + NULL, + NULL, + FALSE); + +static MYSQL_THDVAR_BOOL(async_enabled, + 0, + "Should reads be done asynchronously when possible", + NULL, + NULL, + TRUE); + +static MYSQL_THDVAR_UINT(create_index_option, + 0, + "Control whether additional indexes are created. 0=No (default), 1=Create additional *HEX-based index", + NULL, + NULL, + 0, + 0, + 1, + 1); + +/* static MYSQL_THDVAR_UINT(discovery_mode, + 0, + "Unsupported", + NULL, + NULL, + 0, + 0, + 1, + 1); */ + +static uint32 ibmdb2i_system_trace; +static MYSQL_SYSVAR_UINT(system_trace_level, ibmdb2i_system_trace, + 0, + "Set system tracing level", + NULL, + NULL, + 0, + 0, + 63, + 1); + + +inline uint8 ha_ibmdb2i::getCommitLevel(THD* thd) +{ + if (!THDVAR(thd, transaction_unsafe)) + { + switch (thd_tx_isolation(thd)) + { + case ISO_READ_UNCOMMITTED: + return (accessIntent == QMY_READ_ONLY ? QMY_READ_UNCOMMITTED : QMY_REPEATABLE_READ); + case ISO_READ_COMMITTED: + return (accessIntent == QMY_READ_ONLY ? QMY_READ_COMMITTED : QMY_REPEATABLE_READ); + case ISO_REPEATABLE_READ: + return QMY_REPEATABLE_READ; + case ISO_SERIALIZABLE: + return QMY_SERIALIZABLE; + } + } + + return QMY_NONE; +} + +inline uint8 ha_ibmdb2i::getCommitLevel() +{ + return getCommitLevel(ha_thd()); +} + +//===================================================================== + +static handler *ibmdb2i_create_handler(handlerton *hton, + TABLE_SHARE *table, + MEM_ROOT *mem_root); +static void ibmdb2i_drop_database(handlerton *hton, char* path); +static int ibmdb2i_savepoint_set(handlerton *hton, THD* thd, void *sv); +static int ibmdb2i_savepoint_rollback(handlerton *hton, THD* thd, void *sv); +static int ibmdb2i_savepoint_release(handlerton *hton, THD* thd, void *sv); +static uint ibmdb2i_alter_table_flags(uint flags); + +handlerton *ibmdb2i_hton; +static bool was_ILE_inited; + +/* Tracks the number of open tables */ +static HASH ibmdb2i_open_tables; + +/* Mutex used to synchronize initialization of the hash */ +static pthread_mutex_t ibmdb2i_mutex; + + +/** + Create hash key for tracking open tables. +*/ + +static uchar* ibmdb2i_get_key(IBMDB2I_SHARE *share,size_t *length, + bool not_used __attribute__((unused))) +{ + *length=share->table_name_length; + return (uchar*) share->table_name; +} + + +int ibmdb2i_close_connection(handlerton* hton, THD *thd) +{ + DBUG_PRINT("ha_ibmdb2i::close_connection", ("Closing %d", thd->thread_id)); + db2i_ileBridge::getBridgeForThread(thd)->closeConnection(thd->thread_id); + db2i_ileBridge::destroyBridgeForThread(thd); + + return 0; +} + + +static int ibmdb2i_init_func(void *p) +{ + DBUG_ENTER("ibmdb2i_init_func"); + + utsname tempName; + uname(&tempName); + osVersion.v = atoi(tempName.version); + osVersion.r = atoi(tempName.release); + + was_ILE_inited = false; + ibmdb2i_hton= (handlerton *)p; + VOID(pthread_mutex_init(&ibmdb2i_mutex,MY_MUTEX_INIT_FAST)); + (void) hash_init(&ibmdb2i_open_tables,system_charset_info,32,0,0, + (hash_get_key) ibmdb2i_get_key,0,0); + + ibmdb2i_hton->state= SHOW_OPTION_YES; + ibmdb2i_hton->create= ibmdb2i_create_handler; + ibmdb2i_hton->drop_database= ibmdb2i_drop_database; + ibmdb2i_hton->commit= ha_ibmdb2i::doCommit; + ibmdb2i_hton->rollback= ha_ibmdb2i::doRollback; + ibmdb2i_hton->savepoint_offset= 0; + ibmdb2i_hton->savepoint_set= ibmdb2i_savepoint_set; + ibmdb2i_hton->savepoint_rollback= ibmdb2i_savepoint_rollback; + ibmdb2i_hton->savepoint_release= ibmdb2i_savepoint_release; + ibmdb2i_hton->alter_table_flags=ibmdb2i_alter_table_flags; + ibmdb2i_hton->close_connection=ibmdb2i_close_connection; + + int rc; + + rc = initCharsetSupport(); + + if (!rc) + rc = db2i_ileBridge::setup(); + + if (!rc) + { + int nameLen = strlen(ibmdb2i_rdb_name); + for (int i = 0; i < nameLen; ++i) + { + ibmdb2i_rdb_name[i] = my_toupper(system_charset_info, (uchar)ibmdb2i_rdb_name[i]); + } + + rc = db2i_ileBridge::initILE(ibmdb2i_rdb_name, (uint16*)(((char*)&ibmdb2i_system_trace)+2)); + if (rc == 0) + { + was_ILE_inited = true; + } + } + + DBUG_RETURN(rc); +} + + +static int ibmdb2i_done_func(void *p) +{ + int error= 0; + DBUG_ENTER("ibmdb2i_done_func"); + + if (ibmdb2i_open_tables.records) + error= 1; + + if (was_ILE_inited) + db2i_ileBridge::exitILE(); + + db2i_ileBridge::takedown(); + + doneCharsetSupport(); + + hash_free(&ibmdb2i_open_tables); + pthread_mutex_destroy(&ibmdb2i_mutex); + + DBUG_RETURN(0); +} + + +IBMDB2I_SHARE *ha_ibmdb2i::get_share(const char *table_name, TABLE *table) +{ + IBMDB2I_SHARE *share; + uint length; + char *tmp_name; + + pthread_mutex_lock(&ibmdb2i_mutex); + length=(uint) strlen(table_name); + + if (!(share=(IBMDB2I_SHARE*) hash_search(&ibmdb2i_open_tables, + (uchar*)table_name, + length))) + { + if (!(share=(IBMDB2I_SHARE *) + my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + &share, sizeof(*share), + &tmp_name, length+1, + NullS))) + { + pthread_mutex_unlock(&ibmdb2i_mutex); + return NULL; + } + + share->use_count=0; + share->table_name_length=length; + share->table_name=tmp_name; + strmov(share->table_name,table_name); + if (my_hash_insert(&ibmdb2i_open_tables, (uchar*) share)) + goto error; + thr_lock_init(&share->lock); + pthread_mutexattr_t mutexattr = MY_MUTEX_INIT_FAST; + pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&share->mutex, &mutexattr); + + share->db2Table = new db2i_table(table->s, table_name); + int32 rc = share->db2Table->initDB2Objects(table_name); + + if (rc) + { + delete share->db2Table; + hash_delete(&ibmdb2i_open_tables, (uchar*) share); + thr_lock_delete(&share->lock); + my_errno = rc; + goto error; + } + + memset(&share->cachedStats, 0, sizeof(share->cachedStats)); + } + share->use_count++; + pthread_mutex_unlock(&ibmdb2i_mutex); + + db2Table = share->db2Table; + + return share; + +error: + pthread_mutex_destroy(&share->mutex); + my_free((uchar*) share, MYF(0)); + pthread_mutex_unlock(&ibmdb2i_mutex); + + return NULL; +} + + + +int ha_ibmdb2i::free_share(IBMDB2I_SHARE *share) +{ + pthread_mutex_lock(&ibmdb2i_mutex); + if (!--share->use_count) + { + delete share->db2Table; + db2Table = NULL; + + hash_delete(&ibmdb2i_open_tables, (uchar*) share); + thr_lock_delete(&share->lock); + pthread_mutex_destroy(&share->mutex); + my_free(share, MYF(0)); + pthread_mutex_unlock(&ibmdb2i_mutex); + return 1; + } + pthread_mutex_unlock(&ibmdb2i_mutex); + + return 0; +} + +static handler* ibmdb2i_create_handler(handlerton *hton, + TABLE_SHARE *table, + MEM_ROOT *mem_root) +{ + return new (mem_root) ha_ibmdb2i(hton, table); +} + +static void ibmdb2i_drop_database(handlerton *hton, char* path) +{ + DBUG_ENTER("ha_ibmdb2i::ibmdb2i_drop_database"); + int rc = 0; + char queryBuffer[200]; + String query(queryBuffer, sizeof(queryBuffer), system_charset_info); + query.length(0); + query.append(STRING_WITH_LEN(" DROP SCHEMA \"")); + query.append(path+2, strchr(path+2, '/')-(path+2)); + query.append('"'); + + SqlStatementStream sqlStream(query); + + rc = db2i_ileBridge::getBridgeForThread()->execSQL(sqlStream.getPtrToData(), + sqlStream.getStatementCount(), + QMY_NONE, + FALSE, + TRUE); + DBUG_VOID_RETURN; +} + +inline static void genSavepointName(const void* sv, char* out) +{ + *(uint32*)out = *(uint32*)SAVEPOINT_PREFIX; + DBUG_ASSERT(sizeof(SAVEPOINT_PREFIX) == 4); + out += sizeof(SAVEPOINT_PREFIX); + + longlong2str((longlong)sv, out, 10); + while (*out) + { + out += 0xF0; + ++out; + } +} + + +/********************************************************************* +Sets a transaction savepoint. */ +static int ibmdb2i_savepoint_set(handlerton* hton, THD* thd, void* sv) +{ + DBUG_ENTER("ibmdb2i_savepoint_set"); + int rc = 0; + if (!THDVAR(thd ,transaction_unsafe)) + { + char name[64]; + genSavepointName(sv, name); + DBUG_PRINT("ibmdb2i_savepoint_set",("Setting %s", name)); + rc = ha_ibmdb2i::doSavepointSet(thd, name); + } + DBUG_RETURN(rc); +} + + +/********************************************************************* +Rollback a savepoint. */ +static int ibmdb2i_savepoint_rollback(handlerton* hton, THD* thd, void* sv) +{ + DBUG_ENTER("ibmdb2i_savepoint_rollback"); + int rc = 0; + if (!THDVAR(thd,transaction_unsafe)) + { + char name[64]; + genSavepointName(sv, name); + DBUG_PRINT("ibmdb2i_savepoint_rollback",("Rolling back %s", name)); + rc = ha_ibmdb2i::doSavepointRollback(thd, name); + } + DBUG_RETURN(rc); +} + + +/********************************************************************* +Release a savepoint. */ +static int ibmdb2i_savepoint_release(handlerton* hton, THD* thd, void* sv) +{ + DBUG_ENTER("ibmdb2i_savepoint_release"); + int rc = 0; + if (!THDVAR(thd,transaction_unsafe)) + { + char name[64]; + genSavepointName(sv, name); + DBUG_PRINT("ibmdb2i_savepoint_release",("Releasing %s", name)); + rc = ha_ibmdb2i::doSavepointRelease(thd, name); + } + DBUG_RETURN(rc); +} + +/* Thse flags allow for the online add and drop of an index via the CREATE INDEX, + DROP INDEX, and ALTER TABLE statements. These flags indicate that MySQL is not + required to lock the table before calling the storage engine to add or drop the + index(s). */ +static uint ibmdb2i_alter_table_flags(uint flags) +{ + return (HA_ONLINE_ADD_INDEX | HA_ONLINE_DROP_INDEX | + HA_ONLINE_ADD_UNIQUE_INDEX | HA_ONLINE_DROP_UNIQUE_INDEX | + HA_ONLINE_ADD_PK_INDEX | HA_ONLINE_DROP_PK_INDEX); +} + +ha_ibmdb2i::ha_ibmdb2i(handlerton *hton, TABLE_SHARE *table_arg) + :share(NULL), handler(hton, table_arg), + activeHandle(0), dataHandle(0), + activeReadBuf(NULL), activeWriteBuf(NULL), + blobReadBuffers(NULL), accessIntent(QMY_UPDATABLE), currentRRN(0), + releaseRowNeeded(FALSE), + indexReadSizeEstimates(NULL), + outstanding_start_bulk_insert(false), + last_rnd_init_rc(0), + last_index_init_rc(0), + last_start_bulk_insert_rc(0), + autoIncLockAcquired(false), + got_auto_inc_values(false), + next_identity_value(0), + indexHandles(0), + returnDupKeysImmediately(false), + onDupUpdate(false), + blobWriteBuffers(NULL), + forceSingleRowRead(false) + { + activeReferences = 0; + ref_length = sizeof(currentRRN); + if (table_share && table_share->keys > 0) + { + indexHandles = (FILE_HANDLE*)my_malloc(table_share->keys * sizeof(FILE_HANDLE), MYF(MY_WME | MY_ZEROFILL)); + } + clear_alloc_root(&conversionBufferMemroot); + } + + +ha_ibmdb2i::~ha_ibmdb2i() +{ + DBUG_ASSERT(activeReferences == 0 || outstanding_start_bulk_insert); + + if (indexHandles) + my_free(indexHandles, MYF(0)); + if (indexReadSizeEstimates) + my_free(indexReadSizeEstimates, MYF(0)); + + cleanupBuffers(); +} + + +static const char *ha_ibmdb2i_exts[] = { + FID_EXT, + NullS +}; + +const char **ha_ibmdb2i::bas_ext() const +{ + return ha_ibmdb2i_exts; +} + + +int ha_ibmdb2i::open(const char *name, int mode, uint test_if_locked) +{ + DBUG_ENTER("ha_ibmdb2i::open"); + + initBridge(); + + dataHandle = bridge()->findAndRemovePreservedHandle(name, &share); + + if (share) + db2Table = share->db2Table; + + if (!share && (!(share = get_share(name, table)))) + DBUG_RETURN(my_errno); + thr_lock_data_init(&share->lock,&lock,NULL); + + info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE); + + + DBUG_RETURN(0); +} + + + + +int ha_ibmdb2i::close(void) +{ + DBUG_ENTER("ha_ibmdb2i::close"); + int32 rc = 0; + bool preserveShare = false; + + db2i_ileBridge* bridge = db2i_ileBridge::getBridgeForThread(); + + if (dataHandle) + { + if (bridge->expectErrors(QMY_ERR_PEND_LOCKS)->deallocateFile(dataHandle, FALSE) == QMY_ERR_PEND_LOCKS) + { + bridge->preserveHandle(share->table_name, dataHandle, share); + preserveShare = true; + } + dataHandle = 0; + } + + for (int idx = 0; idx < table_share->keys; ++idx) + { + if (indexHandles[idx] != 0) + { + bridge->deallocateFile(indexHandles[idx], FALSE); + } + } + + cleanupBuffers(); + + if (!preserveShare) + { + if (free_share(share)) + share = NULL; + } + + DBUG_RETURN(rc); +} + + + +int ha_ibmdb2i::write_row(uchar * buf) +{ + + DBUG_ENTER("ha_ibmdb2i::write_row"); + + if (last_start_bulk_insert_rc) + DBUG_RETURN( last_start_bulk_insert_rc ); + + ha_statistic_increment(&SSV::ha_write_count); + int rc = 0; + + bool fileHandleNeedsRelease = false; + + if (!activeHandle) + { + rc = useDataFile(); + if (rc) DBUG_RETURN(rc); + fileHandleNeedsRelease = true; + } + + if (!outstanding_start_bulk_insert) + rc = prepWriteBuffer(1, getFileForActiveHandle()); + + if (!rc) + { + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) + table->timestamp_field->set_time(); + + char* writeBuffer = activeWriteBuf->addRow(); + rc = prepareRowForWrite(writeBuffer, + writeBuffer+activeWriteBuf->getRowNullOffset(), + true); + if (rc == 0) + { + // If we are doing block inserts, if the MI is supposed to generate an auto_increment + // (i.e. identity column) value for this record, and if this is not the first record in + // the block, then store the value (that the MI will generate for the identity column) + // into the MySQL write buffer. We can predetermine the value because the file is locked. + + if ((autoIncLockAcquired) && (default_identity_value) && (got_auto_inc_values)) + { + if (unlikely((next_identity_value - 1) == + maxValueForField(table->next_number_field))) + { + rc = QMY_ERR_MAXVALUE; + } + else + { + rc = table->next_number_field->store((longlong) next_identity_value, TRUE); + next_identity_value = next_identity_value + incrementByValue; + } + } + // If the buffer is full, or if we locked the file and this is the first or last row + // of a blocked insert, then flush the buffer. + if (!rc && (activeWriteBuf->endOfBuffer()) || + ((autoIncLockAcquired) && + ((!got_auto_inc_values))) || + (returnDupKeysImmediately)) + rc = flushWrite(activeHandle, buf); + } + else + activeWriteBuf->deleteRow(); + } + + if (fileHandleNeedsRelease) + releaseActiveHandle(); + + DBUG_RETURN(rc); +} + +/** + @brief + Helper function used by write_row and update_row to prepare the MySQL + row for insertion into DB2. +*/ +int ha_ibmdb2i::prepareRowForWrite(char* data, char* nulls, bool honorIdentCols) +{ + int rc = 0; + + // set null map all to non nulls + memset(nulls,__NOT_NULL_VALUE_EBCDIC, table->s->fields); + default_identity_value = FALSE; + + ulong sql_mode = ha_thd()->variables.sql_mode; + + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + for (Field **field = table->field; *field && !rc; ++field) + { + int fieldIndex = (*field)->field_index; + if ((*field)->Field::is_null()) + { + nulls[fieldIndex] = __NULL_VALUE_EBCDIC; + } + if (honorIdentCols && ((*field)->flags & AUTO_INCREMENT_FLAG) && + *field == table->next_number_field) +// && ((!autoIncLockAcquired) || (!got_auto_inc_values))) + { + if (sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO) + { + if (!table->auto_increment_field_not_null) + { + nulls[fieldIndex] = __DEFAULT_VALUE_EBCDIC; + default_identity_value = TRUE; + } + } + else if ((*field)->val_int() == 0) + { + nulls[fieldIndex] = __DEFAULT_VALUE_EBCDIC; + default_identity_value = TRUE; + } + } + + DB2Field& db2Field = db2Table->db2Field(fieldIndex); + if (nulls[fieldIndex] == __NOT_NULL_VALUE_EBCDIC || + db2Field.isBlob()) + { + rc = convertMySQLtoDB2(*field, db2Field, data + db2Field.getBufferOffset()); + } + } + + if (!rc && db2Table->hasBlobs()) + rc = db2i_ileBridge::getBridgeForThread()->objectOverride(activeHandle, + activeWriteBuf->ptr()); + + dbug_tmp_restore_column_map(table->read_set, old_map); + + return rc; +} + + + +int ha_ibmdb2i::update_row(const uchar * old_data, uchar * new_data) +{ + DBUG_ENTER("ha_ibmdb2i::update_row"); + ha_statistic_increment(&SSV::ha_update_count); + int rc; + + bool fileHandleNeedsRelease = false; + + if (!activeHandle) + { + rc = useFileByHandle(QMY_UPDATABLE, rrnAssocHandle); + if (rc) DBUG_RETURN(rc); + fileHandleNeedsRelease = true; + } + + if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) + table->timestamp_field->set_time(); + + char* writeBuf = activeWriteBuf->addRow(); + rc = prepareRowForWrite(writeBuf, + writeBuf+activeWriteBuf->getRowNullOffset(), + onDupUpdate); + + char* lastDupKeyNamePtr = NULL; + uint32 lastDupKeyNameLen = 0; + + if (!rc) + { + rc = db2i_ileBridge::getBridgeForThread()->updateRow(activeHandle, + currentRRN, + activeWriteBuf->ptr(), + &lastDupKeyRRN, + &lastDupKeyNamePtr, + &lastDupKeyNameLen); + } + + if (lastDupKeyNameLen) + { + lastDupKeyID = getKeyFromName(lastDupKeyNamePtr, lastDupKeyNameLen); + rrnAssocHandle = activeHandle; + } + + if (fileHandleNeedsRelease) + releaseActiveHandle(); + + activeWriteBuf->resetAfterWrite(); + + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::delete_row(const uchar * buf) +{ + DBUG_ENTER("ha_ibmdb2i::delete_row"); + ha_statistic_increment(&SSV::ha_delete_count); + + bool needReleaseFile = false; + int rc = 0; + + if (!activeHandle) // In some circumstances, MySQL comes here after + { // closing the active handle. We need to re-open. + rc = useFileByHandle(QMY_UPDATABLE, rrnAssocHandle); + needReleaseFile = true; + } + + if (likely(!rc)) + { + rc = db2i_ileBridge::getBridgeForThread()->deleteRow(activeHandle, + currentRRN); + invalidateCachedStats(); + if (needReleaseFile) + releaseActiveHandle(); + } + + DBUG_RETURN(rc); +} + + + +int ha_ibmdb2i::index_init(uint idx, bool sorted) +{ + DBUG_ENTER("ha_ibmdb2i::index_init"); + + int& rc = last_index_init_rc; + rc = 0; + + invalidDataFound=false; + tweakReadSet(); + + active_index=idx; + + rc = useIndexFile(idx); + + if (!rc) + { +// THD* thd = ha_thd(); +// if (accessIntent == QMY_UPDATABLE && +// thd_tx_isolation(thd) == ISO_REPEATABLE_READ && +// !THDVAR(thd, transaction_unsafe)) +// { +// readAccessIntent = QMY_READ_ONLY; +// } +// else +// { + readAccessIntent = accessIntent; +// } + + if (!rc && accessIntent != QMY_READ_ONLY) + rc = prepWriteBuffer(1, db2Table->indexFile(idx)); + + if (rc) + releaseIndexFile(idx); + } + + DBUG_RETURN(rc); +} + + + +int ha_ibmdb2i::index_read(uchar * buf, const uchar * key, + uint key_len, + enum ha_rkey_function find_flag) +{ + DBUG_ENTER("ha_ibmdb2i::index_read"); + + if (unlikely(last_index_init_rc)) DBUG_RETURN(last_index_init_rc); + + int rc; + + ha_rows estimatedRows = getIndexReadEstimate(active_index); + rc = prepReadBuffer(estimatedRows, db2Table->indexFile(active_index), readAccessIntent); + if (unlikely(rc)) DBUG_RETURN(rc); + + DBUG_ASSERT(activeReadBuf); + + keyBuf.allocBuf(activeReadBuf->getRowLength(), + activeReadBuf->getRowNullOffset(), + activeReadBuf->getRowLength()); + keyBuf.zeroBuf(); + + char* db2KeyBufPtr = keyBuf.ptr(); + char* nullKeyMap = db2KeyBufPtr + activeReadBuf->getRowNullOffset(); + + const uchar* keyBegin = key; + int partsInUse; + + KEY& curKey = table->key_info[active_index]; + + for (partsInUse = 0; partsInUse < curKey.key_parts, key - keyBegin < key_len; ++partsInUse) + { + Field* field = curKey.key_part[partsInUse].field; + if ((curKey.key_part[partsInUse].null_bit) && + (char*)key[0]) + { + if (field->flags & AUTO_INCREMENT_FLAG) + { + table->status = STATUS_NOT_FOUND; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + else + { + nullKeyMap[partsInUse] = __NULL_VALUE_EBCDIC; + } + } + else + { + nullKeyMap[partsInUse] = __NOT_NULL_VALUE_EBCDIC; + convertMySQLtoDB2(field, + db2Table->db2Field(field->field_index), + db2KeyBufPtr, + (uchar*)key+((curKey.key_part[partsInUse].null_bit)? 1 : 0) ); // + (curKey.key_parts+7) / 8); + } + + db2KeyBufPtr += db2Table->db2Field(field->field_index).getByteLengthInRecord(); + key += curKey.key_part[partsInUse].store_length; + } + + keyLen = db2KeyBufPtr - (char*)keyBuf.ptr(); + + DBUG_PRINT("ha_ibmdb2i::index_read", ("find_flag: %d", find_flag)); + + char readDirection = QMY_NEXT; + + switch (find_flag) + { + case HA_READ_AFTER_KEY: + doInitialRead(QMY_AFTER_EQUAL, estimatedRows, + keyBuf.ptr(), keyLen, partsInUse); + break; + case HA_READ_BEFORE_KEY: + doInitialRead(QMY_BEFORE_EQUAL, estimatedRows, + keyBuf.ptr(), keyLen, partsInUse); + break; + case HA_READ_KEY_OR_NEXT: + doInitialRead(QMY_AFTER_OR_EQUAL, estimatedRows, + keyBuf.ptr(), keyLen, partsInUse); + break; + case HA_READ_KEY_OR_PREV: + DBUG_ASSERT(0); // This function is unused + doInitialRead(QMY_BEFORE_OR_EQUAL, estimatedRows, + keyBuf.ptr(), keyLen, partsInUse); + break; + case HA_READ_PREFIX_LAST_OR_PREV: + doInitialRead(QMY_LAST_PREVIOUS, estimatedRows, + keyBuf.ptr(), keyLen, partsInUse); + readDirection = QMY_PREVIOUS; + break; + case HA_READ_PREFIX_LAST: + doInitialRead(QMY_PREFIX_LAST, estimatedRows, + keyBuf.ptr(), keyLen, partsInUse); + readDirection = QMY_PREVIOUS; + break; + case HA_READ_KEY_EXACT: + doInitialRead(QMY_EQUAL, estimatedRows, keyBuf.ptr(), keyLen, partsInUse); + break; + default: + DBUG_ASSERT(0); + return HA_ERR_GENERIC; + break; + } + + ha_statistic_increment(&SSV::ha_read_key_count); + rc = readFromBuffer(buf, readDirection); + + table->status= (rc ? STATUS_NOT_FOUND: 0); + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::index_next(uchar * buf) +{ + DBUG_ENTER("ha_ibmdb2i::index_next"); + ha_statistic_increment(&SSV::ha_read_next_count); + + int rc = readFromBuffer(buf, QMY_NEXT); + + table->status= (rc ? STATUS_NOT_FOUND: 0); + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::index_next_same(uchar *buf, const uchar *key, uint keylen) +{ + DBUG_ENTER("ha_ibmdb2i::index_next_same"); + ha_statistic_increment(&SSV::ha_read_next_count); + + int rc = readFromBuffer(buf, QMY_NEXT_EQUAL); + + if (rc == HA_ERR_KEY_NOT_FOUND) + { + rc = HA_ERR_END_OF_FILE; + } + + table->status= (rc ? STATUS_NOT_FOUND: 0); + DBUG_RETURN(rc); +} + +int ha_ibmdb2i::index_read_last(uchar * buf, const uchar * key, uint key_len) +{ + DBUG_ENTER("ha_ibmdb2i::index_read_last"); + DBUG_RETURN(index_read(buf, key, key_len, HA_READ_PREFIX_LAST)); +} + + + +int ha_ibmdb2i::index_prev(uchar * buf) +{ + DBUG_ENTER("ha_ibmdb2i::index_prev"); + ha_statistic_increment(&SSV::ha_read_prev_count); + + int rc = readFromBuffer(buf, QMY_PREVIOUS); + + table->status= (rc ? STATUS_NOT_FOUND: 0); + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::index_first(uchar * buf) +{ + DBUG_ENTER("ha_ibmdb2i::index_first"); + + if (unlikely(last_index_init_rc)) DBUG_RETURN(last_index_init_rc); + + int rc = prepReadBuffer(DEFAULT_MAX_ROWS_TO_BUFFER, + db2Table->indexFile(active_index), + readAccessIntent); + + if (rc == 0) + { + doInitialRead(QMY_FIRST, DEFAULT_MAX_ROWS_TO_BUFFER); + ha_statistic_increment(&SSV::ha_read_first_count); + rc = readFromBuffer(buf, QMY_NEXT); + } + + table->status= (rc ? STATUS_NOT_FOUND: 0); + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::index_last(uchar * buf) +{ + DBUG_ENTER("ha_ibmdb2i::index_last"); + + if (unlikely(last_index_init_rc)) DBUG_RETURN(last_index_init_rc); + + int rc = prepReadBuffer(DEFAULT_MAX_ROWS_TO_BUFFER, + db2Table->indexFile(active_index), + readAccessIntent); + + if (rc == 0) + { + doInitialRead(QMY_LAST, DEFAULT_MAX_ROWS_TO_BUFFER); + ha_statistic_increment(&SSV::ha_read_last_count); + rc = readFromBuffer(buf, QMY_PREVIOUS); + } + + table->status= (rc ? STATUS_NOT_FOUND: 0); + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::rnd_init(bool scan) +{ + DBUG_ENTER("ha_ibmdb2i::rnd_init"); + + int& rc = last_rnd_init_rc; + rc = 0; + + tweakReadSet(); + invalidDataFound=false; + + uint32 rowsToBlockOnRead; + + if (!scan) + { + rowsToBlockOnRead = 1; + } + else + { + rowsToBlockOnRead = DEFAULT_MAX_ROWS_TO_BUFFER; + } + + rc = useDataFile(); + + if (!rc) + { +// THD* thd = ha_thd(); +// if (accessIntent == QMY_UPDATABLE && +// thd_tx_isolation(thd) == ISO_REPEATABLE_READ && +// !THDVAR(thd, transaction_unsafe)) +// { +// readAccessIntent = QMY_READ_ONLY; +// } +// else +// { + readAccessIntent = accessIntent; +// } + + rc = prepReadBuffer(rowsToBlockOnRead, db2Table->dataFile(), readAccessIntent); + + if (!rc && accessIntent != QMY_READ_ONLY) + rc = prepWriteBuffer(1, db2Table->dataFile()); + + if (!rc && scan) + doInitialRead(QMY_FIRST, rowsToBlockOnRead); + + if (rc) + releaseDataFile(); + } + + DBUG_RETURN(0); // MySQL sometimes does not check the return code, causing + // an assert in ha_rnd_end later on if we return a non-zero + // value here. +} + +int ha_ibmdb2i::rnd_end() +{ + DBUG_ENTER("ha_ibmdb2i::rnd_end"); + + warnIfInvalidData(); + if (likely(activeReadBuf)) + activeReadBuf->endRead(); + if (last_rnd_init_rc == 0) + releaseActiveHandle(); + last_rnd_init_rc = 0; + DBUG_RETURN(0); +} + + +int32 ha_ibmdb2i::mungeDB2row(uchar* record, const char* dataPtr, const char* nullMapPtr, bool skipLOBs) +{ + DBUG_ASSERT(dataPtr); + + my_bitmap_map *old_write_map= dbug_tmp_use_all_columns(table, table->write_set); + my_bitmap_map *old_read_map; + + if (unlikely(readAllColumns)) + old_read_map = tmp_use_all_columns(table, table->read_set); + + resetCharacterConversionBuffers(); + + my_ptrdiff_t old_ptr= (my_ptrdiff_t) (record - table->record[0]); + int fieldIndex = 0; + for (Field **field = table->field; *field; ++field, ++fieldIndex) + { + if (unlikely(old_ptr)) + (*field)->move_field_offset(old_ptr); + if (nullMapPtr[fieldIndex] == __NULL_VALUE_EBCDIC || + (!bitmap_is_set(table->read_set, fieldIndex)) || + (skipLOBs && db2Table->db2Field(fieldIndex).isBlob())) + { + (*field)->set_null(); + } + else + { + (*field)->set_notnull(); + convertDB2toMySQL(db2Table->db2Field(fieldIndex), *field, dataPtr); + } + if (unlikely(old_ptr)) + (*field)->move_field_offset(-old_ptr); + + } + + if (unlikely(readAllColumns)) + tmp_restore_column_map(table->read_set, old_read_map); + dbug_tmp_restore_column_map(table->write_set, old_write_map); + + return 0; +} + + +int ha_ibmdb2i::rnd_next(uchar *buf) +{ + DBUG_ENTER("ha_ibmdb2i::rnd_next"); + + if (unlikely(last_rnd_init_rc)) DBUG_RETURN(last_rnd_init_rc); + ha_statistic_increment(&SSV::ha_read_rnd_next_count); + + int rc; + + rc = readFromBuffer(buf, QMY_NEXT); + + table->status= (rc ? STATUS_NOT_FOUND: 0); + DBUG_RETURN(rc); +} + + +void ha_ibmdb2i::position(const uchar *record) +{ + DBUG_ENTER("ha_ibmdb2i::position"); + my_store_ptr(ref, ref_length, currentRRN); + DBUG_VOID_RETURN; +} + + +int ha_ibmdb2i::rnd_pos(uchar * buf, uchar *pos) +{ + DBUG_ENTER("ha_ibmdb2i::rnd_pos"); + if (unlikely(last_rnd_init_rc)) DBUG_RETURN( last_rnd_init_rc); + ha_statistic_increment(&SSV::ha_read_rnd_count); + + currentRRN = my_get_ptr(pos, ref_length); + + tweakReadSet(); + + int rc = 0; + + if (activeHandle != rrnAssocHandle) + { + if (activeHandle) releaseActiveHandle(); + rc = useFileByHandle(QMY_UPDATABLE, rrnAssocHandle); + } + + if (likely(rc == 0)) + { + rc = prepReadBuffer(1, getFileForActiveHandle(), accessIntent); + + if (likely(rc == 0) && accessIntent == QMY_UPDATABLE) + rc = prepWriteBuffer(1, getFileForActiveHandle()); + + if (likely(rc == 0)) + { + rc = db2i_ileBridge::getBridgeForThread()->readByRRN(activeHandle, + activeReadBuf->ptr(), + currentRRN, + accessIntent, + getCommitLevel()); + + if (likely(rc == 0)) + { + rrnAssocHandle = activeHandle; + const char* readBuf = activeReadBuf->getRowN(0); + rc = mungeDB2row(buf, readBuf, readBuf + activeReadBuf->getRowNullOffset(), false); + releaseRowNeeded = TRUE; + } + } + } + + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::info(uint flag) +{ + DBUG_ENTER("ha_ibmdb2i::info"); + + uint16 infoRequested = 0; + ValidatedPointer<char> rowKeySpcPtr; // Space pointer passed to DB2 + uint32 rowKeySpcLen; // Length of space passed to DB2 + THD* thd = ha_thd(); + int command = thd_sql_command(thd); + + if (flag & HA_STATUS_AUTO) + stats.auto_increment_value = (ulonglong) 0; + + if (flag & HA_STATUS_ERRKEY) + { + errkey = lastDupKeyID; + my_store_ptr(dup_ref, ref_length, lastDupKeyRRN); + } + + if (flag & HA_STATUS_TIME) + { + if ((flag & HA_STATUS_NO_LOCK) && + ibmdb2i_assume_exclusive_use && + share && + (share->cachedStats.isInited(lastModTime))) + stats.update_time = share->cachedStats.getUpdateTime(); + else + infoRequested |= lastModTime; + } + + if (flag & HA_STATUS_CONST) + { + stats.block_size=4096; + infoRequested |= createTime; + + if (table->s->keys) + { + infoRequested |= rowsPerKey; + rowKeySpcLen = (table->s->keys) * MAX_DB2_KEY_PARTS * sizeof(uint64); + rowKeySpcPtr.alloc(rowKeySpcLen); + memset(rowKeySpcPtr, 0, rowKeySpcLen); // Clear the allocated space + } + } + + if (flag & HA_STATUS_VARIABLE) + { + if ((flag & HA_STATUS_NO_LOCK) && + (command != SQLCOM_SHOW_TABLE_STATUS) && + ibmdb2i_assume_exclusive_use && + share && + (share->cachedStats.isInited(rowCount | deletedRowCount | meanRowLen | ioCount)) && + (share->cachedStats.getRowCount() >= 2)) + { + stats.records = share->cachedStats.getRowCount(); + stats.deleted = share->cachedStats.getDelRowCount(); + stats.mean_rec_length = share->cachedStats.getMeanLength(); + stats.data_file_length = share->cachedStats.getAugmentedDataLength(); + } + else + { + infoRequested |= rowCount | deletedRowCount | meanRowLen; + if (command == SQLCOM_SHOW_TABLE_STATUS) + infoRequested |= objLength; + else + infoRequested |= ioCount; + } + } + + int rc = 0; + + if (infoRequested) + { + DBUG_PRINT("ha_ibmdb2i::info",("Retrieving fresh stats %d", flag)); + + initBridge(thd); + rc = bridge()->retrieveTableInfo((dataHandle ? dataHandle : db2Table->dataFile()->getMasterDefnHandle()), + infoRequested, + stats, + rowKeySpcPtr); + + if (!rc) + { + if ((flag & HA_STATUS_VARIABLE) && + (command != SQLCOM_SHOW_TABLE_STATUS)) + stats.data_file_length = stats.data_file_length * IO_SIZE; + + if ((ibmdb2i_assume_exclusive_use) && + (share) && + (command != SQLCOM_SHOW_TABLE_STATUS)) + { + if (flag & HA_STATUS_VARIABLE) + { + share->cachedStats.cacheRowCount(stats.records); + share->cachedStats.cacheDelRowCount(stats.deleted); + share->cachedStats.cacheMeanLength(stats.mean_rec_length); + share->cachedStats.cacheAugmentedDataLength(stats.data_file_length); + } + + if (flag & HA_STATUS_TIME) + { + share->cachedStats.cacheUpdateTime(stats.update_time); + } + } + + if (flag & HA_STATUS_CONST) + { + ulong i; // Loop counter for indexes + ulong j; // Loop counter for key parts + RowKey* rowKeyPtr; // Pointer to 'number of unique rows' array for this index + + rowKeyPtr = (RowKey_t*)(void*)rowKeySpcPtr; // Address first array of DB2 row counts + for (i = 0; i < table->s->keys; i++) // Do for each index, including primary + { + for (j = 0; j < table->key_info[i].key_parts; j++) + { + table->key_info[i].rec_per_key[j]= rowKeyPtr->RowKeyArray[j]; + } + rowKeyPtr = rowKeyPtr + 1; // Address next array of DB2 row counts + } + } + } + else if (rc == HA_ERR_LOCK_WAIT_TIMEOUT && share) + { + // If we couldn't retrieve the info because the object was locked, + // we'll do our best by returning the most recently cached data. + if ((infoRequested & rowCount) && + share->cachedStats.isInited(rowCount)) + stats.records = share->cachedStats.getRowCount(); + if ((infoRequested & deletedRowCount) && + share->cachedStats.isInited(deletedRowCount)) + stats.deleted = share->cachedStats.getDelRowCount(); + if ((infoRequested & meanRowLen) && + share->cachedStats.isInited(meanRowLen)) + stats.mean_rec_length = share->cachedStats.getMeanLength(); + if ((infoRequested & lastModTime) && + share->cachedStats.isInited(lastModTime)) + stats.update_time = share->cachedStats.getUpdateTime(); + + rc = 0; + } + } + + DBUG_RETURN(rc); +} + + +ha_rows ha_ibmdb2i::records() +{ + DBUG_ENTER("ha_ibmdb2i::records"); + int rc; + rc = bridge()->retrieveTableInfo((dataHandle ? dataHandle : db2Table->dataFile()->getMasterDefnHandle()), + rowCount, + stats); + + if (unlikely(rc)) + { + if (rc == HA_ERR_LOCK_WAIT_TIMEOUT && + share && + (share->cachedStats.isInited(rowCount))) + DBUG_RETURN(share->cachedStats.getRowCount()); + else + DBUG_RETURN(HA_POS_ERROR); + } + else if (share) + { + share->cachedStats.cacheRowCount(stats.records); + } + + DBUG_RETURN(stats.records); +} + + +int ha_ibmdb2i::extra(enum ha_extra_function operation) +{ + DBUG_ENTER("ha_ibmdb2i::extra"); + + switch(operation) + { + // Can these first five flags be replaced by attending to HA_EXTRA_WRITE_CACHE? + case HA_EXTRA_NO_IGNORE_DUP_KEY: + case HA_EXTRA_WRITE_CANNOT_REPLACE: + { + returnDupKeysImmediately = false; + onDupUpdate = false; + } + break; + case HA_EXTRA_INSERT_WITH_UPDATE: + { + returnDupKeysImmediately = true; + onDupUpdate = true; + } + break; + case HA_EXTRA_IGNORE_DUP_KEY: + case HA_EXTRA_WRITE_CAN_REPLACE: + returnDupKeysImmediately = true; + break; + case HA_EXTRA_FLUSH_CACHE: + if (outstanding_start_bulk_insert) + finishBulkInsert(); + break; + } + + + DBUG_RETURN(0); +} + +/** + @brief + The DB2 storage engine will ignore a MySQL generated value and will generate + a new value in SLIC. We arbitrarily set first_value to 1, and set the + interval to infinity for better performance on multi-row inserts. +*/ +void ha_ibmdb2i::get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values) +{ + DBUG_ENTER("ha_ibmdb2i::get_auto_increment"); + *first_value= 1; + *nb_reserved_values= ULONGLONG_MAX; +} + + + +void ha_ibmdb2i::update_create_info(HA_CREATE_INFO *create_info) +{ + DBUG_ENTER("ha_ibmdb2i::update_create_info"); + + if ((!(create_info->used_fields & HA_CREATE_USED_AUTO)) && + (table->found_next_number_field != NULL)) + { + initBridge(); + + create_info->auto_increment_value= 1; + + ha_rows rowCount = records(); + + if (rowCount == 0) + { + create_info->auto_increment_value = db2Table->getStartId(); + DBUG_VOID_RETURN; + } + else if (rowCount == HA_POS_ERROR) + { + DBUG_VOID_RETURN; + } + + getNextIdVal(&create_info->auto_increment_value); + } + DBUG_VOID_RETURN; +} + + +int ha_ibmdb2i::getNextIdVal(ulonglong *value) +{ + DBUG_ENTER("ha_ibmdb2i::getNextIdVal"); + + char queryBuffer[MAX_DB2_COLNAME_LENGTH + MAX_DB2_QUALIFIEDNAME_LENGTH + 64]; + strcpy(queryBuffer, " SELECT CAST(MAX( "); + convertMySQLNameToDB2Name(table->found_next_number_field->field_name, + strend(queryBuffer), + MAX_DB2_COLNAME_LENGTH+1); + strcat(queryBuffer, ") AS BIGINT) FROM "); + db2Table->getDB2QualifiedName(strend(queryBuffer)); + DBUG_ASSERT(strlen(queryBuffer) < sizeof(queryBuffer)); + + SqlStatementStream sqlStream(queryBuffer); + DBUG_PRINT("ha_ibmdb2i::getNextIdVal", ("Sent to DB2: %s",queryBuffer)); + + int rc = 0; + FILE_HANDLE fileHandle2; + uint32 db2RowDataLen2; + rc = bridge()->prepOpen(sqlStream.getPtrToData(), + &fileHandle2, + &db2RowDataLen2); + if (likely(rc == 0)) + { + IOReadBuffer rowBuffer(1, db2RowDataLen2); + rc = bridge()->read(fileHandle2, + rowBuffer.ptr(), + QMY_READ_ONLY, + QMY_NONE, + QMY_FIRST); + + if (likely(rc == 0)) + { + /* This check is here for the case where the table is not empty, + but the auto_increment starting value has been changed since + the last record was written. */ + + longlong maxIdVal = *(longlong*)(rowBuffer.getRowN(0)); + if ((maxIdVal + 1) > db2Table->getStartId()) + *value = maxIdVal + 1; + else + *value = db2Table->getStartId(); + } + + bridge()->deallocateFile(fileHandle2); + } + DBUG_RETURN(rc); +} + + +/* + Updates index cardinalities. +*/ +int ha_ibmdb2i::analyze(THD* thd, HA_CHECK_OPT *check_opt) +{ + DBUG_ENTER("ha_ibmdb2i::analyze"); + info(HA_STATUS_TIME | HA_STATUS_CONST | HA_STATUS_VARIABLE); + DBUG_RETURN(0); +} + +int ha_ibmdb2i::optimize(THD* thd, HA_CHECK_OPT *check_opt) +{ + DBUG_ENTER("ha_ibmdb2i::optimize"); + + initBridge(thd); + + if (unlikely(records() == 0)) + DBUG_RETURN(0); // DB2 doesn't like to reorganize a table with no data. + + quiesceAllFileHandles(); + + int32 rc = bridge()->optimizeTable(db2Table->dataFile()->getMasterDefnHandle()); + info(HA_STATUS_TIME | HA_STATUS_CONST | HA_STATUS_VARIABLE); + + DBUG_RETURN(rc); +} + + +/** + @brief + Determines if an ALTER TABLE is allowed to switch the storage engine + for this table. If the table has a foreign key or is referenced by a + foreign key, then it cannot be switched. +*/ +bool ha_ibmdb2i::can_switch_engines(void) +/*=================================*/ +{ + DBUG_ENTER("ha_ibmdb2i::can_switch_engines"); + + int rc = 0; + FILE_HANDLE queryFile = 0; + uint32 resultRowLen; + uint count = 0; + bool can_switch = FALSE; // 1 if changing storage engine is allowed + + const char* libName = db2Table->getDB2LibName(db2i_table::ASCII_SQL); + const char* fileName = db2Table->getDB2TableName(db2i_table::ASCII_SQL); + + String query(256); + query.append(STRING_WITH_LEN(" SELECT COUNT(*) FROM SYSIBM.SQLFOREIGNKEYS WHERE ((PKTABLE_SCHEM = '")); + query.append(libName+1, strlen(libName)-2); // Remove quotes from parent schema name + query.append(STRING_WITH_LEN("' AND PKTABLE_NAME = '")); + query.append(fileName+1,strlen(fileName)-2); // Remove quotes from file name + query.append(STRING_WITH_LEN("') OR (FKTABLE_SCHEM = '")); + query.append(libName+1,strlen(libName)-2); // Remove quotes from child schema + query.append(STRING_WITH_LEN("' AND FKTABLE_NAME = '")); + query.append(fileName+1,strlen(fileName)-2); // Remove quotes from child name + query.append(STRING_WITH_LEN("'))")); + + SqlStatementStream sqlStream(query); + + rc = bridge()->prepOpen(sqlStream.getPtrToData(), + &queryFile, + &resultRowLen); + if (rc == 0) + { + IOReadBuffer rowBuffer(1, resultRowLen); + + rc = bridge()->read(queryFile, + rowBuffer.ptr(), + QMY_READ_ONLY, + QMY_NONE, + QMY_FIRST); + if (!rc) + { + count = *(uint*)(rowBuffer.getRowN(0)); + if (count == 0) + can_switch = TRUE; + } + + bridge()->deallocateFile(queryFile); + } + DBUG_RETURN(can_switch); +} + + + +bool ha_ibmdb2i::check_if_incompatible_data(HA_CREATE_INFO *info, + uint table_changes) +{ + DBUG_ENTER("ha_ibmdb2i::check_if_incompatible_data"); + uint i; + /* Check that auto_increment value and field definitions were + not changed. */ + if ((info->used_fields & HA_CREATE_USED_AUTO && + info->auto_increment_value != 0) || + table_changes != IS_EQUAL_YES) + DBUG_RETURN(COMPATIBLE_DATA_NO); + /* Check if any fields were renamed. */ + for (i= 0; i < table->s->fields; i++) + { + Field *field= table->field[i]; + if (field->flags & FIELD_IS_RENAMED) + { + DBUG_PRINT("info", ("Field has been renamed, copy table")); + DBUG_RETURN(COMPATIBLE_DATA_NO); + } + } + DBUG_RETURN(COMPATIBLE_DATA_YES); +} + +int ha_ibmdb2i::reset_auto_increment(ulonglong value) + { + DBUG_ENTER("ha_ibmdb2i::reset_auto_increment"); + + int rc = 0; + + quiesceAllFileHandles(); + + const char* libName = db2Table->getDB2LibName(db2i_table::ASCII_SQL); + const char* fileName = db2Table->getDB2TableName(db2i_table::ASCII_SQL); + + String query(512); + query.append(STRING_WITH_LEN(" ALTER TABLE ")); + query.append(libName); + query.append('.'); + query.append(fileName); + query.append(STRING_WITH_LEN(" ALTER COLUMN ")); + char colName[MAX_DB2_COLNAME_LENGTH+1]; + convertMySQLNameToDB2Name(table->found_next_number_field->field_name, + colName, + sizeof(colName)); + query.append(colName); + + char restart_value[22]; + CHARSET_INFO *cs= &my_charset_bin; + uint len = (uint)(cs->cset->longlong10_to_str)(cs,restart_value,sizeof(restart_value), 10, value); + restart_value[len] = 0; + + query.append(STRING_WITH_LEN(" RESTART WITH ")); + query.append(restart_value); + + SqlStatementStream sqlStream(query); + DBUG_PRINT("ha_ibmdb2i::reset_auto_increment", ("Sent to DB2: %s",query.c_ptr())); + + rc = db2i_ileBridge::getBridgeForThread()->execSQL(sqlStream.getPtrToData(), + sqlStream.getStatementCount(), + QMY_NONE, //getCommitLevel(), + FALSE, + FALSE, + TRUE, //FALSE, + dataHandle); + if (rc == 0) + db2Table->updateStartId(value); + + DBUG_RETURN(rc); +} + + +/** + @brief + This function receives an error code that was previously set by the handler. + It returns to MySQL the error string associated with that error. +*/ +bool ha_ibmdb2i::get_error_message(int error, String *buf) +{ + DBUG_ENTER("ha_ibmdb2i::get_error_message"); + if ((error >= DB2I_FIRST_ERR && error <= DB2I_LAST_ERR) || + (error >= QMY_ERR_MIN && error <= QMY_ERR_MAX)) + { + db2i_ileBridge* bridge = db2i_ileBridge::getBridgeForThread(ha_thd()); + char* errMsg = bridge->getErrorStorage(); + buf->copy(errMsg, strlen(errMsg),system_charset_info); + bridge->freeErrorStorage(); + } + DBUG_RETURN(FALSE); +} + + +int ha_ibmdb2i::delete_all_rows() +{ + DBUG_ENTER("ha_ibmdb2i::delete_all_rows"); + int rc = 0; + char queryBuffer[MAX_DB2_QUALIFIEDNAME_LENGTH + 64]; + strcpy(queryBuffer, " DELETE FROM "); + db2Table->getDB2QualifiedName(strend(queryBuffer)); + DBUG_ASSERT(strlen(queryBuffer) < sizeof(queryBuffer)); + + SqlStatementStream sqlStream(queryBuffer); + DBUG_PRINT("ha_ibmdb2i::delete_all_rows", ("Sent to DB2: %s",queryBuffer)); + rc = bridge()->execSQL(sqlStream.getPtrToData(), + sqlStream.getStatementCount(), + getCommitLevel(), + false, + false, + true, + dataHandle); + + /* If this method was called on behalf of a TRUNCATE TABLE statement, and if */ + /* the table has an auto_increment field, then reset the starting value for */ + /* the auto_increment field to 1. + */ + if (rc == 0 && thd_sql_command(ha_thd()) == SQLCOM_TRUNCATE && + table->found_next_number_field ) + rc = reset_auto_increment(1); + + invalidateCachedStats(); + + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::external_lock(THD *thd, int lock_type) +{ + int rc = 0; + + DBUG_ENTER("ha_ibmdb2i::external_lock"); + DBUG_PRINT("ha_ibmdb2i::external_lock",("Lock type: %d", lock_type)); + + if (lock_type == F_RDLCK) + accessIntent = QMY_READ_ONLY; + else if (lock_type == F_WRLCK) + accessIntent = QMY_UPDATABLE; + + initBridge(thd); + int command = thd_sql_command(thd); + + if (!THDVAR(thd,transaction_unsafe)) + { + if (lock_type != F_UNLCK) + { + if (autoCommitIsOn(thd) == QMY_YES) + { + trans_register_ha(thd, FALSE, ibmdb2i_hton); + } + else + { + trans_register_ha(thd, TRUE, ibmdb2i_hton); + if (likely(command != SQLCOM_CREATE_TABLE)) + { + trans_register_ha(thd, FALSE, ibmdb2i_hton); + bridge()->beginStmtTx(); + } + } + } + } + + if (command == SQLCOM_LOCK_TABLES || + command == SQLCOM_ALTER_TABLE || + command == SQLCOM_UNLOCK_TABLES || + (accessIntent == QMY_UPDATABLE && + (command == SQLCOM_UPDATE || + command == SQLCOM_UPDATE_MULTI || + command == SQLCOM_DELETE || + command == SQLCOM_DELETE_MULTI || + command == SQLCOM_REPLACE || + command == SQLCOM_REPLACE_SELECT) && + getCommitLevel(thd) == QMY_NONE)) + { + char action; + char type; + if (lock_type == F_UNLCK) + { + action = QMY_UNLOCK; + type = accessIntent == QMY_READ_ONLY ? QMY_LSRD : QMY_LENR; + } + else + { + action = QMY_LOCK; + type = lock_type == F_RDLCK ? QMY_LSRD : QMY_LENR; + } + + DBUG_PRINT("ha_ibmdb2i::external_lock",("%socking table", action==QMY_LOCK ? "L" : "Unl")); + + if (!dataHandle) + rc = db2Table->dataFile()->allocateNewInstance(&dataHandle, curConnection); + + rc = bridge()->lockObj(dataHandle, + 0, + action, + type, + (command == SQLCOM_LOCK_TABLES ? QMY_NO : QMY_YES)); + + } + + // Cache this away so we don't have to access it on each row operation + cachedZeroDateOption = (enum_ZeroDate)THDVAR(thd, compat_opt_allow_zero_date_vals); + + DBUG_RETURN(rc); +} + + +THR_LOCK_DATA **ha_ibmdb2i::store_lock(THD *thd, + THR_LOCK_DATA **to, + enum thr_lock_type lock_type) +{ + if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) + { + if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && + lock_type <= TL_WRITE) && !(thd->in_lock_tables && thd_sql_command(thd) == SQLCOM_LOCK_TABLES)) + lock_type= TL_WRITE_ALLOW_WRITE; + lock.type=lock_type; + } + *to++= &lock; + return to; +} + + +int ha_ibmdb2i::delete_table(const char *name) +{ + DBUG_ENTER("ha_ibmdb2i::delete_table"); + THD* thd = ha_thd(); + db2i_ileBridge* bridge = db2i_ileBridge::getBridgeForThread(thd); + + char db2Name[MAX_DB2_QUALIFIEDNAME_LENGTH]; + db2i_table::getDB2QualifiedNameFromPath(name, db2Name); + + String query(128); + query.append(STRING_WITH_LEN(" DROP TABLE ")); + query.append(db2Name); + + if (thd_sql_command(thd) == SQLCOM_DROP_TABLE && + thd->lex->drop_mode == DROP_RESTRICT) + query.append(STRING_WITH_LEN(" RESTRICT ")); + DBUG_PRINT("ha_ibmdb2i::delete_table", ("Sent to DB2: %s",query.c_ptr())); + + SqlStatementStream sqlStream(query); + + db2i_table::getDB2LibNameFromPath(name, db2Name); + bool isTemporary = (strcmp(db2Name, DB2I_TEMP_TABLE_SCHEMA) == 0 ? TRUE : FALSE); + + int rc = bridge->execSQL(sqlStream.getPtrToData(), + sqlStream.getStatementCount(), + (isTemporary ? QMY_NONE : getCommitLevel(thd)), + FALSE, + FALSE, + isTemporary); + + if (rc == HA_ERR_NO_SUCH_TABLE) + { + warning(thd, DB2I_ERR_TABLE_NOT_FOUND, name); + rc = 0; + } + + if (rc == 0) + { + db2i_table::deleteAssocFiles(name); + } + + FILE_HANDLE savedHandle = bridge->findAndRemovePreservedHandle(name, &share); + while (savedHandle) + { + bridge->deallocateFile(savedHandle, TRUE); + DBUG_ASSERT(share); + if (free_share(share)) + share = NULL; + savedHandle = bridge->findAndRemovePreservedHandle(name, &share); + } + + my_errno = rc; + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::rename_table(const char * from, const char * to) +{ + DBUG_ENTER("ha_ibmdb2i::rename_table "); + + char db2FromFileName[MAX_DB2_FILENAME_LENGTH + 1]; + char db2ToFileName[MAX_DB2_FILENAME_LENGTH+1]; + char db2FromLibName[MAX_DB2_SCHEMANAME_LENGTH+1]; + char db2ToLibName[MAX_DB2_SCHEMANAME_LENGTH+1]; + + db2i_table::getDB2LibNameFromPath(from, db2FromLibName); + db2i_table::getDB2LibNameFromPath(to, db2ToLibName); + + if (strcmp(db2FromLibName, db2ToLibName) != 0 ) + { + getErrTxt(DB2I_ERR_RENAME_MOVE,from,to); + DBUG_RETURN(DB2I_ERR_RENAME_MOVE); + } + + db2i_table::getDB2FileNameFromPath(from, db2FromFileName, db2i_table::ASCII_NATIVE); + db2i_table::getDB2FileNameFromPath(to, db2ToFileName); + + char escapedFromFileName[2 * MAX_DB2_FILENAME_LENGTH + 1]; + + uint o = 0; + uint i = 1; + do + { + escapedFromFileName[o++] = db2FromFileName[i]; + if (db2FromFileName[i] == '+') + escapedFromFileName[o++] = '+'; + } while (db2FromFileName[++i]); + escapedFromFileName[o-1] = 0; + + + int rc = 0; + + char queryBuffer[sizeof(db2FromLibName) + 2 * sizeof(db2FromFileName) + 256]; + SafeString selectQuery(queryBuffer, sizeof(queryBuffer)); + selectQuery.strncat(STRING_WITH_LEN("SELECT CAST(INDEX_NAME AS VARCHAR(128) CCSID 1208) FROM QSYS2.SYSINDEXES WHERE INDEX_NAME LIKE '%+_+_+_%")); + selectQuery.strcat(escapedFromFileName); + selectQuery.strncat(STRING_WITH_LEN("' ESCAPE '+' AND TABLE_NAME='")); + selectQuery.strncat(db2FromFileName+1, strlen(db2FromFileName)-2); + selectQuery.strncat(STRING_WITH_LEN("' AND TABLE_SCHEMA='")); + selectQuery.strncat(db2FromLibName+1, strlen(db2FromLibName)-2); + selectQuery.strcat('\''); + DBUG_ASSERT(!selectQuery.overflowed()); + + SqlStatementStream indexQuery(selectQuery.ptr()); + + FILE_HANDLE queryFile = 0; + uint32 resultRowLen; + + initBridge(); + rc = bridge()->prepOpen(indexQuery.getPtrToData(), + &queryFile, + &resultRowLen); + + if (unlikely(rc)) + DBUG_RETURN(rc); + + IOReadBuffer rowBuffer(1, resultRowLen); + + int tableNameLen = strlen(db2FromFileName) - 2; + + SqlStatementStream renameQuery(64); + String query; + while (rc == 0) + { + query.length(0); + + rc = bridge()->read(queryFile, + rowBuffer.ptr(), + QMY_READ_ONLY, + QMY_NONE, + QMY_NEXT); + + if (!rc) + { + const char* rowData = rowBuffer.getRowN(0); + char indexFileName[MAX_DB2_FILENAME_LENGTH]; + memset(indexFileName, 0, sizeof(indexFileName)); + + uint16 fileNameLen = *(uint16*)(rowData); + strncpy(indexFileName, rowData + sizeof(uint16), fileNameLen); + + int bytesToRetain = fileNameLen - tableNameLen; + if (bytesToRetain <= 0) + /* We can't handle index names in which the MySQL index name and + the table name together are longer than the max index name. */ + { + getErrTxt(DB2I_ERR_INVALID_NAME,"index","*generated*"); + DBUG_RETURN(DB2I_ERR_INVALID_NAME); + } + char indexName[MAX_DB2_FILENAME_LENGTH]; + memset(indexName, 0, sizeof(indexName)); + + strncpy(indexName, + indexFileName, + bytesToRetain); + + char db2IndexName[MAX_DB2_FILENAME_LENGTH+1]; + + convertMySQLNameToDB2Name(indexFileName, db2IndexName, sizeof(db2IndexName)); + + query.append(STRING_WITH_LEN("RENAME INDEX ")); + query.append(db2FromLibName); + query.append('.'); + query.append(db2IndexName); + query.append(STRING_WITH_LEN(" TO ")); + if (db2i_table::appendQualifiedIndexFileName(indexName, db2ToFileName, query, db2i_table::ASCII_SQL, typeNone) == -1) + { + getErrTxt(DB2I_ERR_INVALID_NAME,"index","*generated*"); + DBUG_RETURN(DB2I_ERR_INVALID_NAME ); + } + renameQuery.addStatement(query); + DBUG_PRINT("ha_ibmdb2i::rename_table", ("Sent to DB2: %s",query.c_ptr_safe())); + } + } + + + if (queryFile) + bridge()->deallocateFile(queryFile); + + if (rc != HA_ERR_END_OF_FILE) + DBUG_RETURN(rc); + + char db2Name[MAX_DB2_QUALIFIEDNAME_LENGTH]; + + /* Rename the table */ + query.length(0); + query.append(STRING_WITH_LEN(" RENAME TABLE ")); + db2i_table::getDB2QualifiedNameFromPath(from, db2Name); + query.append(db2Name); + query.append(STRING_WITH_LEN(" TO ")); + query.append(db2ToFileName); + DBUG_PRINT("ha_ibmdb2i::rename_table", ("Sent to DB2: %s",query.c_ptr_safe())); + renameQuery.addStatement(query); + rc = bridge()->execSQL(renameQuery.getPtrToData(), + renameQuery.getStatementCount(), + getCommitLevel()); + + if (!rc) + db2i_table::renameAssocFiles(from, to); + + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::create(const char *name, TABLE *table_arg, + HA_CREATE_INFO *create_info) +{ + DBUG_ENTER("ha_ibmdb2i::create"); + + int rc; + char fileSortSequence[11] = "*HEX"; + char fileSortSequenceLibrary[11] = ""; + char fileSortSequenceType = ' '; + char libName[MAX_DB2_SCHEMANAME_LENGTH+1]; + char fileName[MAX_DB2_FILENAME_LENGTH+1]; + char colName[MAX_DB2_COLNAME_LENGTH+1]; + bool isTemporary; + ulong auto_inc_value; + + db2i_table::getDB2LibNameFromPath(name, libName); + db2i_table::getDB2FileNameFromPath(name, fileName); + + if (osVersion.v < 6) + { + if (strlen(libName) > + MAX_DB2_V5R4_LIBNAME_LENGTH + (isUpperOrQuote(system_charset_info, libName) ? 2 : 0)) + { + getErrTxt(DB2I_ERR_TOO_LONG_SCHEMA,libName, MAX_DB2_V5R4_LIBNAME_LENGTH); + DBUG_RETURN(DB2I_ERR_TOO_LONG_SCHEMA); + } + } + else if (strlen(libName) > MAX_DB2_V6R1_LIBNAME_LENGTH) + { + getErrTxt(DB2I_ERR_TOO_LONG_SCHEMA,libName, MAX_DB2_V6R1_LIBNAME_LENGTH); + DBUG_RETURN(DB2I_ERR_TOO_LONG_SCHEMA); + } + + String query(256); + + if (strcmp(libName, DB2I_TEMP_TABLE_SCHEMA)) + { + query.append(STRING_WITH_LEN("CREATE TABLE ")); + query.append(libName); + query.append('.'); + query.append(fileName); + isTemporary = FALSE; + } + else + { + query.append(STRING_WITH_LEN("DECLARE GLOBAL TEMPORARY TABLE ")); + query.append(fileName); + isTemporary = TRUE; + } + query.append(STRING_WITH_LEN(" (")); + + THD* thd = ha_thd(); + enum_TimeFormat timeFormat = (enum_TimeFormat)(THDVAR(thd, compat_opt_time_as_duration)); + enum_YearFormat yearFormat = (enum_YearFormat)(THDVAR(thd, compat_opt_year_as_int)); + enum_BlobMapping blobMapping = (enum_BlobMapping)(THDVAR(thd, compat_opt_blob_cols)); + enum_ZeroDate zeroDate = (enum_ZeroDate)(THDVAR(thd, compat_opt_allow_zero_date_vals)); + bool propagateDefaults = THDVAR(thd, propagate_default_col_vals); + + Field **field; + for (field= table_arg->field; *field; field++) + { + if ( field != table_arg->field ) // Not the first one + query.append(STRING_WITH_LEN(" , ")); + + if (!convertMySQLNameToDB2Name((*field)->field_name, colName, sizeof(colName))) + { + getErrTxt(DB2I_ERR_INVALID_NAME,"field",(*field)->field_name); + DBUG_RETURN(DB2I_ERR_INVALID_NAME ); + } + + query.append(colName); + query.append(' '); + + if (rc = getFieldTypeMapping(*field, + query, + timeFormat, + blobMapping, + zeroDate, + propagateDefaults, + yearFormat)) + DBUG_RETURN(rc); + + if ( (*field)->flags & NOT_NULL_FLAG ) + { + query.append(STRING_WITH_LEN(" NOT NULL ")); + } + if ( (*field)->flags & AUTO_INCREMENT_FLAG ) + { +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (table_arg->part_info) + { + getErrTxt(DB2I_ERR_PART_AUTOINC); + DBUG_RETURN(DB2I_ERR_PART_AUTOINC); + } +#endif + query.append(STRING_WITH_LEN(" GENERATED BY DEFAULT AS IDENTITY ") ); + if (create_info->auto_increment_value != 0) + { + /* Query was ALTER TABLE...AUTO_INCREMENT = x; or + CREATE TABLE ...AUTO_INCREMENT = x; Set the starting + value for the auto_increment column. */ + char stringValue[22]; + CHARSET_INFO *cs= &my_charset_bin; + uint len = (uint)(cs->cset->longlong10_to_str)(cs,stringValue,sizeof(stringValue), 10, create_info->auto_increment_value); + stringValue[len] = 0; + query.append(STRING_WITH_LEN(" (START WITH ")); + query.append(stringValue); + + uint64 maxValue=maxValueForField(*field); + + if (maxValue) + { + len = (uint)(cs->cset->longlong10_to_str)(cs,stringValue,sizeof(stringValue), 10, maxValue); + stringValue[len] = 0; + query.append(STRING_WITH_LEN(" MAXVALUE ")); + query.append(stringValue); + } + + query.append(STRING_WITH_LEN(") ")); + } + + } + } + + bool primaryHasStringField = false; + + if (table_arg->s->primary_key != MAX_KEY && !isTemporary) + { + KEY& curKey = table_arg->key_info[table_arg->s->primary_key]; + query.append(STRING_WITH_LEN(", PRIMARY KEY( ")); + for (int j = 0; j < curKey.key_parts; ++j) + { + if (j != 0) + { + query.append( STRING_WITH_LEN(" , ") ); + } + Field* field = curKey.key_part[j].field; + convertMySQLNameToDB2Name(field->field_name, colName, sizeof(colName)); + query.append(colName); + enum_field_types type = field->real_type(); + if (type == MYSQL_TYPE_VARCHAR || type == MYSQL_TYPE_BLOB || + type == MYSQL_TYPE_STRING) + { + rc = updateAssociatedSortSequence(field->charset(), + &fileSortSequenceType, + fileSortSequence, + fileSortSequenceLibrary); + if (rc) DBUG_RETURN (rc); + primaryHasStringField = true; + } + } + query.append(STRING_WITH_LEN(" ) ")); + } + + rc = buildDB2ConstraintString(thd->lex, + query, + name, + table_arg->field, + &fileSortSequenceType, + fileSortSequence, + fileSortSequenceLibrary); + if (rc) DBUG_RETURN (rc); + + query.append(STRING_WITH_LEN(" ) ")); + + if (isTemporary) + query.append(STRING_WITH_LEN(" ON COMMIT PRESERVE ROWS ")); + + DBUG_PRINT("ha_ibmdb2i::create", ("Sent to DB2: %s",query.c_ptr())); + SqlStatementStream sqlStream(query.length()); + sqlStream.addStatement(query,fileSortSequence,fileSortSequenceLibrary); + + for (uint i = 0; i < table_arg->s->keys; ++i) + { + if (i != table_arg->s->primary_key || isTemporary) + { + rc = buildCreateIndexStatement(sqlStream, + table_arg->key_info[i], + false, + libName, + fileName); + if (rc) DBUG_RETURN (rc); + } + } + + bool noCommit = isTemporary || ((!autoCommitIsOn(thd)) && (thd_sql_command(thd) == SQLCOM_ALTER_TABLE)); + + initBridge(); + +// if (THDVAR(thd, discovery_mode) == 1) +// bridge()->expectErrors(QMY_ERR_TABLE_EXISTS); + + rc = bridge()->execSQL(sqlStream.getPtrToData(), + sqlStream.getStatementCount(), + (isTemporary ? QMY_NONE : getCommitLevel(thd)), + TRUE, + FALSE, + noCommit ); + + if (unlikely(rc == QMY_ERR_MSGID) && + memcmp(bridge()->getErrorMsgID(), DB2I_SQL0350, 7) == 0) + { + my_error(ER_BLOB_USED_AS_KEY, MYF(0), "*unknown*"); + rc = ER_BLOB_USED_AS_KEY; + } +/* else if (unlikely(rc == QMY_ERR_TABLE_EXISTS) && + THDVAR(thd, discovery_mode) == 1) + { + db2i_table* temp = new db2i_table(table_arg->s, name); + int32 rc = temp->fastInitForCreate(name); + delete temp; + + if (!rc) + warning(thd, DB2I_ERR_WARN_CREATE_DISCOVER); + + DBUG_RETURN(rc); + } +*/ + + if (!rc && !isTemporary) + { + db2i_table* temp = new db2i_table(table_arg->s, name); + int32 rc = temp->fastInitForCreate(name); + delete temp; + if (rc) + delete_table(name); + } + + DBUG_RETURN(rc); +} + + +/** + @brief + Add an index on-line to a table. This method is called on behalf of + a CREATE INDEX or ALTER TABLE statement. + It is implemented via a composed DDL statement passed to DB2. +*/ +int ha_ibmdb2i::add_index(TABLE *table_arg, + KEY *key_info, + uint num_of_keys) +{ + DBUG_ENTER("ha_ibmdb2i::add_index"); + + int rc; + SqlStatementStream sqlStream(256); + const char* libName = db2Table->getDB2LibName(db2i_table::ASCII_SQL); + const char* fileName = db2Table->getDB2TableName(db2i_table::ASCII_SQL); + + quiesceAllFileHandles(); + + uint primaryKey = MAX_KEY; + if (table_arg->s->primary_key >= MAX_KEY && !db2Table->isTemporary()) + { + for (int i = 0; i < num_of_keys; ++i) + { + if (strcmp(key_info[i].name, "PRIMARY") == 0) + { + primaryKey = i; + break; + } + else if (primaryKey == MAX_KEY && + key_info[i].flags & HA_NOSAME) + { + primaryKey = i; + for (int j=0 ; j < key_info[i].key_parts ;j++) + { + uint fieldnr= key_info[i].key_part[j].fieldnr; + if (table_arg->s->field[fieldnr]->null_ptr || + table_arg->s->field[fieldnr]->key_length() != + key_info[i].key_part[j].length) + { + primaryKey = MAX_KEY; + break; + } + } + } + } + } + + + for (int i = 0; i < num_of_keys; ++i) + { + KEY& curKey= key_info[i]; + rc = buildCreateIndexStatement(sqlStream, + curKey, + (i == primaryKey), + libName, + fileName); + if (rc) DBUG_RETURN (rc); + } + + rc = bridge()->execSQL(sqlStream.getPtrToData(), + sqlStream.getStatementCount(), + getCommitLevel(), + FALSE, + FALSE, + FALSE, + dataHandle); + + /* Handle the case where a unique index is being created but an error occurs + because the file contains duplicate key values. */ + if (rc == ER_DUP_ENTRY) + print_keydup_error(MAX_KEY,ER(ER_DUP_ENTRY_WITH_KEY_NAME)); + + DBUG_RETURN(rc); +} + +/** + @brief + Drop an index on-line from a table. This method is called on behalf of + a DROP INDEX or ALTER TABLE statement. + It is implemented via a composed DDL statement passed to DB2. +*/ +int ha_ibmdb2i::prepare_drop_index(TABLE *table_arg, + uint *key_num, uint num_of_keys) +{ + DBUG_ENTER("ha_ibmdb2i::prepare_drop_index"); + int rc; + int i = 0; + String query(64); + SqlStatementStream sqlStream(64 * num_of_keys); + SqlStatementStream shadowStream(64 * num_of_keys); + + quiesceAllFileHandles(); + + const char* libName = db2Table->getDB2LibName(db2i_table::ASCII_SQL); + const char* fileName = db2Table->getDB2TableName(db2i_table::ASCII_SQL); + + while (i < num_of_keys) + { + query.length(0); + DBUG_PRINT("info", ("ha_ibmdb2i::prepare_drop_index %u", key_num[i])); + KEY& curKey= table_arg->key_info[key_num[i]]; + if (key_num[i] == table->s->primary_key && !db2Table->isTemporary()) + { + query.append(STRING_WITH_LEN("ALTER TABLE ")); + query.append(libName); + query.append(STRING_WITH_LEN(".")); + query.append(fileName); + query.append(STRING_WITH_LEN(" DROP PRIMARY KEY")); + } + else + { + query.append(STRING_WITH_LEN("DROP INDEX ")); + query.append(libName); + query.append(STRING_WITH_LEN(".")); + db2i_table::appendQualifiedIndexFileName(curKey.name, fileName, query); + } + DBUG_PRINT("ha_ibmdb2i::prepare_drop_index", ("Sent to DB2: %s",query.c_ptr_safe())); + sqlStream.addStatement(query); + + query.length(0); + query.append(STRING_WITH_LEN("DROP INDEX ")); + query.append(libName); + query.append(STRING_WITH_LEN(".")); + db2i_table::appendQualifiedIndexFileName(curKey.name, fileName, query, db2i_table::ASCII_SQL, typeHex); + + DBUG_PRINT("ha_ibmdb2i::prepare_drop_index", ("Sent to DB2: %s",query.c_ptr_safe())); + shadowStream.addStatement(query); + + ++i; + } + + rc = bridge()->execSQL(sqlStream.getPtrToData(), + sqlStream.getStatementCount(), + getCommitLevel(), + FALSE, + FALSE, + FALSE, + dataHandle); + + if (rc == 0) + bridge()->execSQL(shadowStream.getPtrToData(), + shadowStream.getStatementCount(), + getCommitLevel()); + + DBUG_RETURN(rc); +} + + +void +ha_ibmdb2i::unlock_row() +{ + DBUG_ENTER("ha_ibmdb2i::unlock_row"); + DBUG_VOID_RETURN; +} + +int +ha_ibmdb2i::index_end() +{ + DBUG_ENTER("ha_ibmdb2i::index_end"); + warnIfInvalidData(); + last_index_init_rc = 0; + if (likely(activeReadBuf)) + activeReadBuf->endRead(); + if (likely(!last_index_init_rc)) + releaseIndexFile(active_index); + active_index= MAX_KEY; + DBUG_RETURN (0); +} + +int ha_ibmdb2i::doCommit(handlerton *hton, THD *thd, bool all) +{ + if (!THDVAR(thd, transaction_unsafe)) + { + if (all || autoCommitIsOn(thd)) + { + DBUG_PRINT("ha_ibmdb2i::doCommit",("Committing all")); + return (db2i_ileBridge::getBridgeForThread(thd)->commitmentControl(QMY_COMMIT)); + } + else + { + DBUG_PRINT("ha_ibmdb2i::doCommit",("Committing stmt")); + return (db2i_ileBridge::getBridgeForThread(thd)->commitStmtTx()); + } + } + + return (0); +} + + +int ha_ibmdb2i::doRollback(handlerton *hton, THD *thd, bool all) +{ + if (!THDVAR(thd,transaction_unsafe)) + { + if (all || autoCommitIsOn(thd)) + { + DBUG_PRINT("ha_ibmdb2i::doRollback",("Rolling back all")); + return ( db2i_ileBridge::getBridgeForThread(thd)->commitmentControl(QMY_ROLLBACK)); + } + else + { + DBUG_PRINT("ha_ibmdb2i::doRollback",("Rolling back stmt")); + return (db2i_ileBridge::getBridgeForThread(thd)->rollbackStmtTx()); + } + } + return (0); +} + + +void ha_ibmdb2i::start_bulk_insert(ha_rows rows) +{ + DBUG_ENTER("ha_ibmdb2i::start_bulk_insert"); + DBUG_PRINT("ha_ibmdb2i::start_bulk_insert",("Rows hinted %d", rows)); + int rc; + THD* thd = ha_thd(); + int command = thd_sql_command(thd); + + if (db2Table->hasBlobs() || + (command == SQLCOM_REPLACE || command == SQLCOM_REPLACE_SELECT)) + rows = 1; + else if (rows == 0) + rows = DEFAULT_MAX_ROWS_TO_BUFFER; // Shoot the moon + + // If we're doing a multi-row insert, binlogging is active, and the table has an + // auto_increment column, then we'll attempt to lock the file while we perform a 'fast path' blocked + // insert. If we can't get the lock, then we'll do a row-by-row 'slow path' insert instead. The reason is + // because the MI generates the auto_increment (identity value), and if we can't lock the file, + // then we can't predetermine what that value will be for insertion into the MySQL write buffer. + + if ((rows > 1) && // Multi-row insert + (thd->options & OPTION_BIN_LOG) && // Binlogging is on + (table->found_next_number_field)) // Table has an auto_increment column + { + if (!dataHandle) + rc = db2Table->dataFile()->allocateNewInstance(&dataHandle, curConnection); + + rc = bridge()->lockObj(dataHandle, 1, QMY_LOCK, QMY_LEAR, QMY_YES); + if (rc==0) // Got the lock + { + autoIncLockAcquired = TRUE; + got_auto_inc_values = FALSE; + } + else // Didn't get the lock + rows = 1; // No problem, but don't block inserts + } + + if (activeHandle == 0) + { + last_start_bulk_insert_rc = useDataFile(); + if (last_start_bulk_insert_rc == 0) + last_start_bulk_insert_rc = prepWriteBuffer(rows, db2Table->dataFile()); + } + + if (last_start_bulk_insert_rc == 0) + outstanding_start_bulk_insert = true; + else + { + if (autoIncLockAcquired == TRUE) + { + bridge()->lockObj(dataHandle, 0, QMY_UNLOCK, QMY_LEAR, QMY_YES); + autoIncLockAcquired = FALSE; + } + } + + DBUG_VOID_RETURN; +} + + +int ha_ibmdb2i::end_bulk_insert() +{ + DBUG_ENTER("ha_ibmdb2i::end_bulk_insert"); + int rc = 0; + + if (outstanding_start_bulk_insert) + { + rc = finishBulkInsert(); + } + + my_errno = rc; + + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::prepReadBuffer(ha_rows rowsToRead, const db2i_file* file, char intent) +{ + DBUG_ENTER("ha_ibmdb2i::prepReadBuffer"); + DBUG_ASSERT(rowsToRead > 0); + + THD* thd = ha_thd(); + char cmtLvl = getCommitLevel(thd); + + const db2i_file::RowFormat* format; + int rc = file->obtainRowFormat(activeHandle, intent, cmtLvl, &format); + + if (unlikely(rc)) DBUG_RETURN(rc); + + if (lobFieldsRequested()) + { + forceSingleRowRead = true; + rowsToRead = 1; + } + + rowsToRead = min(stats.records+1,min(rowsToRead, DEFAULT_MAX_ROWS_TO_BUFFER)); + + uint bufSize = min((format->readRowLen * rowsToRead), THDVAR(thd, max_read_buffer_size)); + multiRowReadBuf.allocBuf(format->readRowLen, format->readRowNullOffset, bufSize); + activeReadBuf = &multiRowReadBuf; + + if (db2Table->hasBlobs()) + { + if (!blobReadBuffers) + blobReadBuffers = new BlobCollection(db2Table, THDVAR(thd, lob_alloc_size)); + rc = prepareReadBufferForLobs(); + if (rc) DBUG_RETURN(rc); + } + +// if (accessIntent == QMY_UPDATABLE && +// thd_tx_isolation(thd) == ISO_REPEATABLE_READ && +// !THDVAR(thd, transaction_unsafe)) +// activeReadBuf->update(QMY_READ_ONLY, &releaseRowNeeded, QMY_REPEATABLE_READ); +// else + activeReadBuf->update(intent, &releaseRowNeeded, cmtLvl); + + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::prepWriteBuffer(ha_rows rowsToWrite, const db2i_file* file) +{ + DBUG_ENTER("ha_ibmdb2i::prepWriteBuffer"); + DBUG_ASSERT(accessIntent == QMY_UPDATABLE && rowsToWrite > 0); + + const db2i_file::RowFormat* format; + int rc = file->obtainRowFormat(activeHandle, + QMY_UPDATABLE, + getCommitLevel(ha_thd()), + &format); + + if (unlikely(rc)) DBUG_RETURN(rc); + + rowsToWrite = min(rowsToWrite, DEFAULT_MAX_ROWS_TO_BUFFER); + + uint bufSize = min((format->writeRowLen * rowsToWrite), THDVAR(ha_thd(), max_write_buffer_size)); + multiRowWriteBuf.allocBuf(format->writeRowLen, format->writeRowNullOffset, bufSize); + activeWriteBuf = &multiRowWriteBuf; + + if (!blobWriteBuffers && db2Table->hasBlobs()) + { + blobWriteBuffers = new ValidatedPointer<char>[db2Table->getBlobCount()]; + } + DBUG_RETURN(rc); +} + + +int ha_ibmdb2i::flushWrite(FILE_HANDLE fileHandle, uchar* buf ) +{ + DBUG_ENTER("ha_ibmdb2i::flushWrite"); + int rc; + int64 generatedIdValue = 0; + bool IdValueWasGenerated = FALSE; + char* lastDupKeyNamePtr = NULL; + uint32 lastDupKeyNameLen = 0; + int loopCnt = 0; + bool retry_dup = FALSE; + + while (loopCnt == 0 || retry_dup == TRUE) + { + rc = bridge()->writeRows(fileHandle, + activeWriteBuf->ptr(), + getCommitLevel(), + &generatedIdValue, + &IdValueWasGenerated, + &lastDupKeyRRN, + &lastDupKeyNamePtr, + &lastDupKeyNameLen, + &incrementByValue); + loopCnt++; + retry_dup = FALSE; + invalidateCachedStats(); + if (lastDupKeyNameLen) + { + rrnAssocHandle = fileHandle; + + int command = thd_sql_command(ha_thd()); + + if (command == SQLCOM_REPLACE || + command == SQLCOM_REPLACE_SELECT) + lastDupKeyID = 0; + else + { + lastDupKeyID = getKeyFromName(lastDupKeyNamePtr, lastDupKeyNameLen); + + if (likely(lastDupKeyID != MAX_KEY)) + { + uint16 failedRow = activeWriteBuf->rowsWritten()+1; + + if (buf && (failedRow != activeWriteBuf->rowCount())) + { + const char* badRow = activeWriteBuf->getRowN(failedRow-1); + bool savedReadAllColumns = readAllColumns; + readAllColumns = true; + mungeDB2row(buf, + badRow, + badRow + activeWriteBuf->getRowNullOffset(), + true); + readAllColumns = savedReadAllColumns; + + if (table->found_next_number_field) + { + table->next_number_field->store(next_identity_value - (incrementByValue * (activeWriteBuf->rowCount() - (failedRow - 1)))); + } + } + + if (default_identity_value && // Table has ID colm and generating a value + (!autoIncLockAcquired || !got_auto_inc_values) && + // Writing first or only row in block + loopCnt == 1 && // Didn't already retry + lastDupKeyID == table->s->next_number_index) // Autoinc column is in failed index + { + if (alterStartWith() == 0) // Reset next Identity value to max+1 + retry_dup = TRUE; // Rtry the write operation + } + } + else + { + char unknownIndex[MAX_DB2_FILENAME_LENGTH+1]; + convFromEbcdic(lastDupKeyNamePtr, unknownIndex, min(lastDupKeyNameLen, MAX_DB2_FILENAME_LENGTH)); + unknownIndex[min(lastDupKeyNameLen, MAX_DB2_FILENAME_LENGTH)] = 0; + getErrTxt(DB2I_ERR_UNKNOWN_IDX, unknownIndex); + } + } + } + } + + if ((rc == 0 || rc == HA_ERR_FOUND_DUPP_KEY) + && default_identity_value && IdValueWasGenerated && + (!autoIncLockAcquired || !got_auto_inc_values)) + { + /* Save the generated identity value for the MySQL last_insert_id() function. */ + insert_id_for_cur_row = generatedIdValue; + + /* Store the value into MySQL's buf for row-based replication + or for an 'on duplicate key update' clause. */ + table->next_number_field->store((longlong) generatedIdValue, TRUE); + if (autoIncLockAcquired) + { + got_auto_inc_values = TRUE; + next_identity_value = generatedIdValue + incrementByValue; + } + } + else + { + if (!autoIncLockAcquired) // Don't overlay value for first row of a block + insert_id_for_cur_row = 0; + } + + + activeWriteBuf->resetAfterWrite(); + DBUG_RETURN(rc); +} + +int ha_ibmdb2i::alterStartWith() +{ + DBUG_ENTER("ha_ibmdb2i::alterStartWith"); + int rc = 0; + ulonglong nextIdVal; + if (!dataHandle) + rc = db2Table->dataFile()->allocateNewInstance(&dataHandle, curConnection); + if (!rc) {rc = bridge()->lockObj(dataHandle, 1, QMY_LOCK, QMY_LENR, QMY_YES);} + if (!rc) + { + rc = getNextIdVal(&nextIdVal); + if (!rc) {rc = reset_auto_increment(nextIdVal);} + bridge()->lockObj(dataHandle, 0, QMY_UNLOCK, QMY_LENR, QMY_YES); + } + DBUG_RETURN(rc); +} + +bool ha_ibmdb2i::lobFieldsRequested() +{ + if (!db2Table->hasBlobs()) + { + DBUG_PRINT("ha_ibmdb2i::lobFieldsRequested",("No LOBs")); + return (false); + } + + if (readAllColumns) + { + DBUG_PRINT("ha_ibmdb2i::lobFieldsRequested",("All cols requested")); + return (true); + } + + for (int i = 0; i < db2Table->getBlobCount(); ++i) + { + if (bitmap_is_set(table->read_set, db2Table->blobFields[i])) + { + DBUG_PRINT("ha_ibmdb2i::lobFieldsRequested",("LOB requested")); + return (true); + } + } + + DBUG_PRINT("ha_ibmdb2i::lobFieldsRequested",("No LOBs requested")); + return (false); +} + + +int ha_ibmdb2i::prepareReadBufferForLobs() +{ + DBUG_ENTER("ha_ibmdb2i::prepareReadBufferForLobs"); + DBUG_ASSERT(db2Table->hasBlobs()); + + uint32 activeLobFields = 0; + DB2LobField* lobField; + uint16 blobCount = db2Table->getBlobCount(); + + char* readBuf = activeReadBuf->getRowN(0); + + for (int i = 0; i < blobCount; ++i) + { + int fieldID = db2Table->blobFields[i]; + DB2Field& db2Field = db2Table->db2Field(fieldID); + lobField = db2Field.asBlobField(readBuf); + if (readAllColumns || + bitmap_is_set(table->read_set, fieldID)) + { + lobField->dataHandle = (ILEMemHandle)blobReadBuffers->getBufferPtr(fieldID); + activeLobFields++; + } + else + { + lobField->dataHandle = NULL; + } + } + + if (activeLobFields == 0) + { + for (int i = 0; i < blobCount; ++i) + { + DB2Field& db2Field = db2Table->db2Field(db2Table->blobFields[i]); + uint16 offset = db2Field.getBufferOffset() + db2Field.calcBlobPad(); + + for (int r = 1; r < activeReadBuf->getRowCapacity(); ++r) + { + lobField = (DB2LobField*)(activeReadBuf->getRowN(r) + offset); + lobField->dataHandle = NULL; + } + } + } + + activeReadBuf->setRowsToProcess((activeLobFields ? 1 : activeReadBuf->getRowCapacity())); + int rc = bridge()->objectOverride(activeHandle, + activeReadBuf->ptr(), + activeReadBuf->getRowLength()); + DBUG_RETURN(rc); +} + + +uint32 ha_ibmdb2i::adjustLobBuffersForRead() +{ + DBUG_ENTER("ha_ibmdb2i::adjustLobBuffersForRead"); + + char* readBuf = activeReadBuf->getRowN(0); + + for (int i = 0; i < db2Table->getBlobCount(); ++i) + { + DB2Field& db2Field = db2Table->db2Field(db2Table->blobFields[i]); + DB2LobField* lobField = db2Field.asBlobField(readBuf); + if (readAllColumns || + bitmap_is_set(table->read_set, db2Table->blobFields[i])) + { + lobField->dataHandle = (ILEMemHandle)blobReadBuffers->reallocBuffer(db2Table->blobFields[i], lobField->length); + + if (lobField->dataHandle == NULL) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + else + { + lobField->dataHandle = 0; + } + } + + int32 rc = bridge()->objectOverride(activeHandle, + activeReadBuf->ptr()); + DBUG_RETURN(rc); +} + + + +int ha_ibmdb2i::reset() +{ + DBUG_ENTER("ha_ibmdb2i::reset"); + + if (outstanding_start_bulk_insert) + { + finishBulkInsert(); + } + + if (activeHandle != 0) + { + releaseActiveHandle(); + } + + cleanupBuffers(); + + db2i_ileBridge::getBridgeForThread(ha_thd())->freeErrorStorage(); + + last_rnd_init_rc = last_index_init_rc = last_start_bulk_insert_rc = 0; + + returnDupKeysImmediately = false; + onDupUpdate = false; + forceSingleRowRead = false; + +#ifndef DBUG_OFF + cachedBridge=NULL; +#endif + + DBUG_RETURN(0); +} + + +int32 ha_ibmdb2i::buildCreateIndexStatement(SqlStatementStream& sqlStream, + KEY& key, + bool isPrimary, + const char* db2LibName, + const char* db2FileName) +{ + DBUG_ENTER("ha_ibmdb2i::buildCreateIndexStatement"); + + char fileSortSequence[11] = "*HEX"; + char fileSortSequenceLibrary[11] = ""; + char fileSortSequenceType = ' '; + String query(256); + query.length(0); + int rc = 0; + + if (isPrimary) + { + query.append(STRING_WITH_LEN("ALTER TABLE ")); + query.append(db2LibName); + query.append('.'); + query.append(db2FileName); + query.append(STRING_WITH_LEN(" ADD PRIMARY KEY ")); + } + else + { + query.append(STRING_WITH_LEN("CREATE")); + + if (key.flags & HA_NOSAME) + query.append(STRING_WITH_LEN(" UNIQUE WHERE NOT NULL")); + + query.append(STRING_WITH_LEN(" INDEX ")); + + query.append(db2LibName); + query.append('.'); + if (db2i_table::appendQualifiedIndexFileName(key.name, db2FileName, query)) + { + getErrTxt(DB2I_ERR_INVALID_NAME,"index","*generated*"); + DBUG_RETURN(DB2I_ERR_INVALID_NAME ); + } + + query.append(STRING_WITH_LEN(" ON ")); + + query.append(db2LibName); + query.append('.'); + query.append(db2FileName); + } + + String fieldDefinition(128); + fieldDefinition.length(0); + fieldDefinition.append(STRING_WITH_LEN(" ( ")); + for (int j = 0; j < key.key_parts; ++j) + { + char colName[MAX_DB2_COLNAME_LENGTH+1]; + if (j != 0) + { + fieldDefinition.append(STRING_WITH_LEN(" , ")); + } + Field* field = key.key_part[j].field; + convertMySQLNameToDB2Name(field->field_name, colName, sizeof(colName)); + fieldDefinition.append(colName); + rc = updateAssociatedSortSequence(field->charset(), + &fileSortSequenceType, + fileSortSequence, + fileSortSequenceLibrary); + if (rc) DBUG_RETURN (rc); + } + fieldDefinition.append(STRING_WITH_LEN(" ) ")); + + query.append(fieldDefinition); + + if ((THDVAR(ha_thd(), create_index_option)==1) && + (fileSortSequenceType != 'B')) + { + String shadowQuery(256); + shadowQuery.length(0); + + shadowQuery.append(STRING_WITH_LEN("CREATE INDEX ")); + + shadowQuery.append(db2LibName); + shadowQuery.append('.'); + if (db2i_table::appendQualifiedIndexFileName(key.name, db2FileName, shadowQuery, db2i_table::ASCII_SQL, typeHex)) + { + getErrTxt(DB2I_ERR_INVALID_NAME,"index","*generated*"); + DBUG_RETURN(DB2I_ERR_INVALID_NAME ); + } + + shadowQuery.append(STRING_WITH_LEN(" ON ")); + + shadowQuery.append(db2LibName); + shadowQuery.append('.'); + shadowQuery.append(db2FileName); + shadowQuery.append(fieldDefinition); + DBUG_PRINT("ha_ibmdb2i::buildCreateIndexStatement", ("Sent to DB2: %s",shadowQuery.c_ptr_safe())); + sqlStream.addStatement(shadowQuery,"*HEX","QSYS"); + } + + DBUG_PRINT("ha_ibmdb2i::buildCreateIndexStatement", ("Sent to DB2: %s",query.c_ptr_safe())); + sqlStream.addStatement(query,fileSortSequence,fileSortSequenceLibrary); + + DBUG_RETURN(0); +} + + +void ha_ibmdb2i::doInitialRead(char orientation, + uint32 rowsToBuffer, + ILEMemHandle key, + int keyLength, + int keyParts) +{ + DBUG_ENTER("ha_ibmdb2i::doInitialRead"); + + if (forceSingleRowRead) + rowsToBuffer = 1; + else + rowsToBuffer = min(rowsToBuffer, activeReadBuf->getRowCapacity()); + + activeReadBuf->newReadRequest(activeHandle, + orientation, + rowsToBuffer, + THDVAR(ha_thd(), async_enabled), + key, + keyLength, + keyParts); + DBUG_VOID_RETURN; +} + + +int ha_ibmdb2i::start_stmt(THD *thd, thr_lock_type lock_type) +{ + DBUG_ENTER("ha_ibmdb2i::start_stmt"); + initBridge(thd); + if (!THDVAR(thd, transaction_unsafe)) + { + trans_register_ha(thd, FALSE, ibmdb2i_hton); + + if (!autoCommitIsOn(thd)) + { + bridge()->beginStmtTx(); + } + } + + DBUG_RETURN(0); +} + +int32 ha_ibmdb2i::handleLOBReadOverflow() +{ + DBUG_ENTER("ha_ibmdb2i::handleLOBReadOverflow"); + DBUG_ASSERT(db2Table->hasBlobs() && (activeReadBuf->getRowCapacity() == 1)); + + int32 rc = adjustLobBuffersForRead(); + + if (!rc) + { + activeReadBuf->rewind(); + rc = bridge()->expectErrors(QMY_ERR_END_OF_BLOCK) + ->read(activeHandle, + activeReadBuf->ptr(), + accessIntent, + getCommitLevel(), + QMY_SAME); + releaseRowNeeded = TRUE; + + } + DBUG_RETURN(rc); +} + + +int32 ha_ibmdb2i::finishBulkInsert() +{ + int32 rc = 0; + + if (activeWriteBuf->rowCount() && activeHandle) + rc = flushWrite(activeHandle, table->record[0]); + + if (activeHandle) + releaseActiveHandle(); + + if (autoIncLockAcquired == TRUE) + { + // We could check the return code on the unlock, but beware not + // to overlay the return code from the flushwrite or we will mask + // duplicate key errors.. + bridge()->lockObj(dataHandle, 0, QMY_UNLOCK, QMY_LEAR, QMY_YES); + autoIncLockAcquired = FALSE; + } + outstanding_start_bulk_insert = false; + multiRowWriteBuf.freeBuf(); + last_start_bulk_insert_rc = 0; + + resetCharacterConversionBuffers(); + + return rc; +} + +int ha_ibmdb2i::getKeyFromName(const char* name, size_t len) +{ + for (int i = 0; i < table_share->keys; ++i) + { + const char* indexName = db2Table->indexFile(i)->getDB2FileName(); + if ((strncmp(name, indexName, len) == 0) && + (strlen(indexName) == len)) + { + return i; + } + } + return MAX_KEY; +} + +/* +Determine the number of I/O's it takes to read through the table. + */ +double ha_ibmdb2i::scan_time() + { + DBUG_ENTER("ha_ibmdb2i::scan_time"); + DBUG_RETURN(ulonglong2double((stats.data_file_length)/IO_SIZE)); + } + + +/** + Estimate the number of I/O's it takes to read a set of ranges through + an index. + + @param index + @param ranges + @param rows + + @return The estimate number of I/Os +*/ + +double ha_ibmdb2i::read_time(uint index, uint ranges, ha_rows rows) +{ + DBUG_ENTER("ha_ibmdb2i::read_time"); + int rc; + uint64 idxPageCnt = 0; + double cost; + + if (unlikely(rows == HA_POS_ERROR)) + DBUG_RETURN(double(rows) + ranges); + + rc = bridge()->retrieveIndexInfo(db2Table->indexFile(index)->getMasterDefnHandle(), + &idxPageCnt); + if (!rc) + { + if ((idxPageCnt == 1) || // Retrieving rows in requested order or + (ranges == rows)) // 'Sweep' full records retrieval + cost = idxPageCnt/4; + else + { + uint64 totalRecords = stats.records + 1; + double dataPageCount = stats.data_file_length/IO_SIZE; + + cost = (rows * dataPageCount / totalRecords) + + min(idxPageCnt, (log_2(idxPageCnt) * ranges + + rows * (log_2(idxPageCnt) + log_2(rows) - log_2(totalRecords)))); + } + } + else + { + cost = rows2double(ranges+rows); // Use default costing + } + DBUG_RETURN(cost); +} + +int ha_ibmdb2i::useIndexFile(int idx) +{ + DBUG_ENTER("ha_ibmdb2i::useIndexFile"); + + if (activeHandle) + releaseActiveHandle(); + + int rc = 0; + + if (!indexHandles[idx]) + rc = db2Table->indexFile(idx)->allocateNewInstance(&indexHandles[idx], curConnection); + + if (rc == 0) + { + activeHandle = indexHandles[idx]; + bumpInUseCounter(1); + } + + DBUG_RETURN(rc); +} + + +ulong ha_ibmdb2i::index_flags(uint inx, uint part, bool all_parts) const +{ + return HA_READ_NEXT | HA_READ_PREV | HA_KEYREAD_ONLY | HA_READ_ORDER | HA_READ_RANGE; +} + + +static struct st_mysql_sys_var* ibmdb2i_system_variables[] = { + MYSQL_SYSVAR(rdb_name), + MYSQL_SYSVAR(transaction_unsafe), + MYSQL_SYSVAR(lob_alloc_size), + MYSQL_SYSVAR(max_read_buffer_size), + MYSQL_SYSVAR(max_write_buffer_size), + MYSQL_SYSVAR(async_enabled), + MYSQL_SYSVAR(assume_exclusive_use), + MYSQL_SYSVAR(compat_opt_blob_cols), + MYSQL_SYSVAR(compat_opt_time_as_duration), + MYSQL_SYSVAR(compat_opt_allow_zero_date_vals), + MYSQL_SYSVAR(compat_opt_year_as_int), + MYSQL_SYSVAR(propagate_default_col_vals), + MYSQL_SYSVAR(create_index_option), +// MYSQL_SYSVAR(discovery_mode), + MYSQL_SYSVAR(system_trace_level), + NULL +}; + + +struct st_mysql_storage_engine ibmdb2i_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION }; + +mysql_declare_plugin(ibmdb2i) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &ibmdb2i_storage_engine, + "IBMDB2I", + "The IBM development team in Rochester, Minnesota", + "IBM DB2 for i Storage Engine", + PLUGIN_LICENSE_GPL, + ibmdb2i_init_func, /* Plugin Init */ + ibmdb2i_done_func, /* Plugin Deinit */ + 0x0100 /* 1.0 */, + NULL, /* status variables */ + ibmdb2i_system_variables, /* system variables */ + NULL /* config options */ +} +mysql_declare_plugin_end; diff --git a/storage/ibmdb2i/ha_ibmdb2i.h b/storage/ibmdb2i/ha_ibmdb2i.h new file mode 100644 index 00000000000..e90f152919c --- /dev/null +++ b/storage/ibmdb2i/ha_ibmdb2i.h @@ -0,0 +1,750 @@ +/* +Licensed Materials - Property of IBM +DB2 Storage Engine Enablement +Copyright IBM Corporation 2007,2008 +All rights reserved + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + (a) Redistributions of source code must retain this list of conditions, the + copyright notice in section {d} below, and the disclaimer following this + list of conditions. + (b) Redistributions in binary form must reproduce this list of conditions, the + copyright notice in section (d) below, and the disclaimer following this + list of conditions, in the documentation and/or other materials provided + with the distribution. + (c) The name of IBM may not be used to endorse or promote products derived from + this software without specific prior written permission. + (d) The text of the required copyright notice is: + Licensed Materials - Property of IBM + DB2 Storage Engine Enablement + Copyright IBM Corporation 2007,2008 + All rights reserved + +THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. +*/ + +/** @file ha_ibmdb2i.h + + @brief + + @note + + @see +*/ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +#include "as400_types.h" +#include "as400_protos.h" +#include "db2i_global.h" +#include "db2i_ileBridge.h" +#include "builtins.h" +#include "db2i_misc.h" +#include "db2i_file.h" +#include "db2i_blobCollection.h" +#include "db2i_collationSupport.h" +#include "db2i_validatedPointer.h" +#include "db2i_ioBuffers.h" +#include "db2i_errors.h" +#include "db2i_sqlStatementStream.h" + +/** @brief + IBMDB2I_SHARE is a structure that will be shared among all open handlers. + It is used to describe the underlying table definition, and it caches + table statistics. +*/ +struct IBMDB2I_SHARE { + char *table_name; + uint table_name_length,use_count; + pthread_mutex_t mutex; + THR_LOCK lock; + + db2i_table* db2Table; + + class CStats + { + public: + void cacheUpdateTime(time_t time) + {update_time = time; initFlag |= lastModTime;} + time_t getUpdateTime() const + {return update_time;} + void cacheRowCount(ha_rows rows) + {records = rows; initFlag |= rowCount;} + ha_rows getRowCount() const + {return records;} + void cacheDelRowCount(ha_rows rows) + {deleted = rows; initFlag |= deletedRowCount;} + ha_rows getDelRowCount() const + {return deleted;} + void cacheMeanLength(ulong len) + {mean_rec_length = len; initFlag |= meanRowLen;} + ulong getMeanLength() + {return mean_rec_length;} + void cacheAugmentedDataLength(ulong len) + {data_file_length = len; initFlag |= ioCount;} + ulong getAugmentedDataLength() + {return data_file_length;} + bool isInited(uint flags) + {return initFlag & flags;} + void invalidate(uint flags) + {initFlag &= ~flags;} + + private: + uint initFlag; + time_t update_time; + ha_rows records; + ha_rows deleted; + ulong mean_rec_length; + ulong data_file_length; + } cachedStats; + +}; + +class ha_ibmdb2i: public handler +{ + THR_LOCK_DATA lock; ///< MySQL lock + IBMDB2I_SHARE *share; ///< Shared lock info + + // The record we are positioned on, together with the handle used to get + // i. + uint32 currentRRN; + uint32 rrnAssocHandle; + + // Dup key values needed by info() + uint32 lastDupKeyRRN; + uint32 lastDupKeyID; + + bool returnDupKeysImmediately; + + // Dup key value need by update() + bool onDupUpdate; + + + db2i_table* db2Table; + + // The file handle of the PF or LF being accessed by the current operation. + FILE_HANDLE activeHandle; + + // The file handle of the underlying PF + FILE_HANDLE dataHandle; + + // Array of file handles belonging to the underlying LFs + FILE_HANDLE* indexHandles; + + // Flag to indicate whether a call needs to be made to unlock a row when + // a read operation has ended. DB2 will handle row unlocking as we move + // through rows, but if an operation ends before we reach the end of a file, + // DB2 needs to know to unlock the last row read. + bool releaseRowNeeded; + + // Pointer to a definition of the layout of the row buffer for the file + // described by activeHandle + const db2i_file::RowFormat* activeFormat; + + IORowBuffer keyBuf; + uint32 keyLen; + + IOWriteBuffer multiRowWriteBuf; + IOAsyncReadBuffer multiRowReadBuf; + + IOAsyncReadBuffer* activeReadBuf; + IOWriteBuffer* activeWriteBuf; + + BlobCollection* blobReadBuffers; // Dynamically allocated per query and used + // to manage the buffers used for reading LOBs + ValidatedPointer<char>* blobWriteBuffers; + + // Return codes are not used/honored by rnd_init and start_bulk_insert + // so we need a way to signal the failure "downstream" to subsequent + // functions. + int last_rnd_init_rc; + int last_index_init_rc; + int last_start_bulk_insert_rc; + + // end_bulk_insert may get called twice for a single start_bulk_insert + // This is our way to do cleanup only once. + bool outstanding_start_bulk_insert; + + // Auto_increment 'increment by' value needed by write_row() + uint32 incrementByValue; + bool default_identity_value; + + // Flags and values used during write operations for auto_increment processing + bool autoIncLockAcquired; + bool got_auto_inc_values; + uint64 next_identity_value; + + // The access intent indicated by the last external_locks() call. + // May be either QMY_READ or QMY_UPDATABLE + char accessIntent; + char readAccessIntent; + + ha_rows* indexReadSizeEstimates; + + MEM_ROOT conversionBufferMemroot; + + bool forceSingleRowRead; + + bool readAllColumns; + + bool invalidDataFound; + + db2i_ileBridge* cachedBridge; + + ValidatedObject<volatile uint32> curConnection; + uint16 activeReferences; + +public: + + ha_ibmdb2i(handlerton *hton, TABLE_SHARE *table_arg); + ~ha_ibmdb2i(); + + const char *table_type() const { return "IBMDB2I"; } + const char *index_type(uint inx) { return "RADIX"; } + const key_map *keys_to_use_for_scanning() { return &key_map_full; } + const char **bas_ext() const; + + ulonglong table_flags() const + { + return HA_NULL_IN_KEY | HA_REC_NOT_IN_SEQ | HA_AUTO_PART_KEY | + HA_PARTIAL_COLUMN_READ | + HA_DUPLICATE_POS | HA_NO_PREFIX_CHAR_KEYS | + HA_HAS_RECORDS | HA_BINLOG_ROW_CAPABLE | HA_REQUIRES_KEY_COLUMNS_FOR_DELETE | + HA_CAN_INDEX_BLOBS; + } + + ulong index_flags(uint inx, uint part, bool all_parts) const; + +// Note that we do not implement max_supported_record_length. +// We'll let create fail accordingly if the row is +// too long. This allows us to hide the fact that varchars > 32K are being +// implemented as DB2 LOBs. + + uint max_supported_keys() const { return 4000; } + uint max_supported_key_parts() const { return MAX_DB2_KEY_PARTS; } + uint max_supported_key_length() const { return 32767; } + uint max_supported_key_part_length() const { return 32767; } + double read_time(uint index, uint ranges, ha_rows rows); + double scan_time(); + int open(const char *name, int mode, uint test_if_locked); + int close(void); + int write_row(uchar * buf); + int update_row(const uchar * old_data, uchar * new_data); + int delete_row(const uchar * buf); + int index_init(uint idx, bool sorted); + int index_read(uchar * buf, const uchar * key, + uint key_len, enum ha_rkey_function find_flag); + int index_next(uchar * buf); + int index_read_last(uchar * buf, const uchar * key, uint key_len); + int index_next_same(uchar *buf, const uchar *key, uint keylen); + int index_prev(uchar * buf); + int index_first(uchar * buf); + int index_last(uchar * buf); + int rnd_init(bool scan); + int rnd_end(); + int rnd_next(uchar *buf); + int rnd_pos(uchar * buf, uchar *pos); + void position(const uchar *record); + int info(uint); + ha_rows records(); + int extra(enum ha_extra_function operation); + int external_lock(THD *thd, int lock_type); + int delete_all_rows(void); + ha_rows records_in_range(uint inx, key_range *min_key, + key_range *max_key); + int delete_table(const char *from); + int rename_table(const char * from, const char * to); + int create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info); + int updateFrm(TABLE *table_def, File file); + int openTableDef(TABLE *table_def); + int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys); + int prepare_drop_index(TABLE *table_arg, uint *key_num, uint num_of_keys); + int final_drop_index(TABLE *table_arg) {return 0;} + void get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values); + int reset_auto_increment(ulonglong value); + void restore_auto_increment(ulonglong prev_insert_id) {return;} + void update_create_info(HA_CREATE_INFO *create_info); + int getNextIdVal(ulonglong *value); + int analyze(THD* thd,HA_CHECK_OPT* check_opt); + int optimize(THD* thd, HA_CHECK_OPT* check_opt); + bool can_switch_engines(); + void free_foreign_key_create_info(char* str); + char* get_foreign_key_create_info(); + int get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list); + uint referenced_by_foreign_key(); + bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes); + virtual bool get_error_message(int error, String *buf); + + THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, + enum thr_lock_type lock_type); + + bool low_byte_first() const { return 0; } + void unlock_row(); + int index_end(); + int reset(); + static int doCommit(handlerton *hton, THD *thd, bool all); + static int doRollback(handlerton *hton, THD *thd, bool all); + void start_bulk_insert(ha_rows rows); + int end_bulk_insert(); + int start_stmt(THD *thd, thr_lock_type lock_type); + + void initBridge(THD* thd = NULL) + { + if (thd == NULL) thd = ha_thd(); + DBUG_PRINT("ha_ibmdb2i::initBridge",("Initing bridge. Conn ID=%d", thd->thread_id)); + cachedBridge = db2i_ileBridge::getBridgeForThread(thd); + } + + db2i_ileBridge* bridge() {DBUG_ASSERT(cachedBridge); return cachedBridge;} + + static uint8 autoCommitIsOn(THD* thd) + { return (thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ? QMY_NO : QMY_YES); } + + uint8 getCommitLevel(); + uint8 getCommitLevel(THD* thd); + + static int doSavepointSet(THD* thd, char* name) + { + return db2i_ileBridge::getBridgeForThread(thd)->savepoint(QMY_SET_SAVEPOINT, + name); + } + + static int doSavepointRollback(THD* thd, char* name) + { + return db2i_ileBridge::getBridgeForThread(thd)->savepoint(QMY_ROLLBACK_SAVEPOINT, + name); + } + + static int doSavepointRelease(THD* thd, char* name) + { + return db2i_ileBridge::getBridgeForThread(thd)->savepoint(QMY_RELEASE_SAVEPOINT, + name); + } + + // We can't guarantee that the rows we know about when this is called + // will be the same number of rows that read returns (since DB2 activity + // may insert additional rows). Therefore, we do as the Federated SE and + // return the max possible. + ha_rows estimate_rows_upper_bound() + { + return HA_POS_ERROR; + } + + +private: + + enum enum_TimeFormat + { + TIME_OF_DAY, + DURATION + }; + + enum enum_BlobMapping + { + AS_BLOB, + AS_VARCHAR + }; + + enum enum_ZeroDate + { + NO_SUBSTITUTE, + SUBSTITUTE_0001_01_01 + }; + + enum enum_YearFormat + { + CHAR4, + SMALLINT + }; + + enum_ZeroDate cachedZeroDateOption; + + IBMDB2I_SHARE *get_share(const char *table_name, TABLE *table); + int free_share(IBMDB2I_SHARE *share); + int32 mungeDB2row(uchar* record, const char* dataPtr, const char* nullMapPtr, bool skipLOBs); + int prepareRowForWrite(char* data, char* nulls, bool honorIdentCols); + int prepareReadBufferForLobs(); + int32 prepareWriteBufferForLobs(); + uint32 adjustLobBuffersForRead(); + bool lobFieldsRequested(); + int convertFieldChars(enum_conversionDirection direction, uint16 fieldID, const char* input, char* output, size_t ilen, size_t olen, size_t* outDataLen); + + /** + Fast integer log2 function + */ + uint64 log_2(uint64 val) + { + uint64 exp = 0; + while( (val >> exp) != 0) + { + exp++; + } + DBUG_ASSERT(exp-1 == (uint64)log2(val)); + return exp-1; + } + + void bumpInUseCounter(uint16 amount) + { + activeReferences += amount; + DBUG_PRINT("ha_ibmdb2i::bumpInUseCounter", ("activeReferences = %d", activeReferences)); + if (activeReferences) + curConnection = (uint32)(ha_thd()->thread_id); + else + curConnection = 0; + } + + + int useDataFile() + { + DBUG_ENTER("ha_ibmdb2i::useDataFile"); + + int rc = 0; + if (!dataHandle) + rc = db2Table->dataFile()->allocateNewInstance(&dataHandle, curConnection); + else if (activeHandle == dataHandle) + DBUG_RETURN(0); + + DBUG_ASSERT(activeHandle == 0); + + if (likely(rc == 0)) + { + activeHandle = dataHandle; + bumpInUseCounter(1); + } + + DBUG_RETURN(rc); + } + + void releaseAnyLockedRows() + { + if (releaseRowNeeded) + { + DBUG_PRINT("ha_ibmdb2i::releaseAnyLockedRows", ("Releasing rows")); + db2i_ileBridge::getBridgeForThread()->rrlslck(activeHandle, accessIntent); + releaseRowNeeded = FALSE; + } + } + + + void releaseDataFile() + { + DBUG_ENTER("ha_ibmdb2i::releaseDataFile"); + releaseAnyLockedRows(); + bumpInUseCounter(-1); + DBUG_ASSERT((volatile int)activeReferences >= 0); + activeHandle = 0; + DBUG_VOID_RETURN; + } + + int useIndexFile(int idx); + + void releaseIndexFile(int idx) + { + DBUG_ENTER("ha_ibmdb2i::releaseIndexFile"); + releaseAnyLockedRows(); + bumpInUseCounter(-1); + DBUG_ASSERT((volatile int)activeReferences >= 0); + activeHandle = 0; + DBUG_VOID_RETURN; + } + + FILE_HANDLE allocateFileHandle(char* database, char* table, int* activityReference, bool hasBlobs); + + int updateBuffers(const db2i_file::RowFormat* format, uint rowsToRead, uint rowsToWrite); + + int flushWrite(FILE_HANDLE fileHandle, uchar* buf = NULL); + + int alterStartWith(); + + int buildDB2ConstraintString(LEX* lex, + String& appendHere, + const char* database, + Field** fields, + char* fileSortSequenceType, + char* fileSortSequence, + char* fileSortSequenceLibrary); + + void releaseWriteBuffer(); + + void setIndexReadEstimate(uint index, ha_rows rows) + { + if (!indexReadSizeEstimates) + { + indexReadSizeEstimates = (ha_rows*)my_malloc(sizeof(ha_rows) * table->s->keys, MYF(MY_WME | MY_ZEROFILL)); + } + indexReadSizeEstimates[index] = rows; + } + + ha_rows getIndexReadEstimate(uint index) + { + if (indexReadSizeEstimates) + return max(indexReadSizeEstimates[index], 1); + + return 10000; // Assume index scan if no estimate exists. + } + + + void quiesceAllFileHandles() + { + db2i_ileBridge* bridge = db2i_ileBridge::getBridgeForThread(); + if (dataHandle) + { + bridge->quiesceFileInstance(dataHandle); + } + + for (int idx = 0; idx < table_share->keys; ++idx) + { + if (indexHandles[idx] != 0) + { + bridge->quiesceFileInstance(indexHandles[idx]); + } + } + } + + int32 buildCreateIndexStatement(SqlStatementStream& sqlStream, + KEY& key, + bool isPrimary, + const char* db2LibName, + const char* db2FileName); + + // Specify NULL for data when using the data pointed to by field + int32 convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char* db2Buf, const uchar* data = NULL); + + int32 convertDB2toMySQL(const DB2Field& db2Field, Field* field, const char* buf); + int getFieldTypeMapping(Field* field, + String& mapping, + enum_TimeFormat timeFormate, + enum_BlobMapping blobMapping, + enum_ZeroDate zeroDateHandling, + bool propagateDefaults, + enum_YearFormat yearFormat); + + int getKeyFromName(const char* name, size_t len); + + void releaseActiveHandle() + { + if (activeHandle == dataHandle) + releaseDataFile(); + else + releaseIndexFile(active_index); + } + + + int32 finishBulkInsert(); + + void doInitialRead(char orientation, + uint32 rowsToBuffer, + ILEMemHandle key = 0, + int keyLength = 0, + int keyParts = 0); + + + int32 readFromBuffer(uchar* destination, char orientation) + { + char* row; + int32 rc = 0; + row = activeReadBuf->readNextRow(orientation, currentRRN); + + if (unlikely(!row)) + { + rc = activeReadBuf->lastrc(); + if (rc == QMY_ERR_LOB_SPACE_TOO_SMALL) + { + rc = handleLOBReadOverflow(); + if (rc == 0) + { + DBUG_ASSERT(activeReadBuf->rowCount() == 1); + row = activeReadBuf->readNextRow(orientation, currentRRN); + + if (unlikely(!row)) + rc = activeReadBuf->lastrc(); + } + } + } + + if (likely(rc == 0)) + { + rrnAssocHandle = activeHandle; + rc = mungeDB2row(destination, row, row+activeReadBuf->getRowNullOffset(), false); + } + return rc; + } + + int32 handleLOBReadOverflow(); + + char* getCharacterConversionBuffer(int fieldId, int length) + { + if (unlikely(!alloc_root_inited(&conversionBufferMemroot))) + init_alloc_root(&conversionBufferMemroot, 8192, 0); + + return (char*)alloc_root(&conversionBufferMemroot, length);; + } + + void resetCharacterConversionBuffers() + { + if (alloc_root_inited(&conversionBufferMemroot)) + { + free_root(&conversionBufferMemroot, MYF(MY_MARK_BLOCKS_FREE)); + } + } + + void tweakReadSet() + { + THD* thd = ha_thd(); + int command = thd_sql_command(thd); + if ((command == SQLCOM_UPDATE || + command == SQLCOM_UPDATE_MULTI) || + ((command == SQLCOM_DELETE || + command == SQLCOM_DELETE_MULTI) && + thd->options & OPTION_BIN_LOG)) + readAllColumns = TRUE; + else + readAllColumns = FALSE; + } + + /** + + */ + int useFileByHandle(char intent, + FILE_HANDLE handle) + { + DBUG_ENTER("ha_ibmdb2i::useFileByHandle"); + + const db2i_file* file; + if (handle == dataHandle) + file = db2Table->dataFile(); + else + { + for (uint i = 0; i < table_share->keys; ++i) + { + if (indexHandles[i] == handle) + { + file = db2Table->indexFile(i); + active_index = i; + } + } + } + + int rc = file->obtainRowFormat(handle, intent, getCommitLevel(), &activeFormat); + if (likely(rc == 0)) + { + activeHandle = handle; + bumpInUseCounter(1); + } + + DBUG_RETURN(rc); + } + + const db2i_file* getFileForActiveHandle() const + { + if (activeHandle == dataHandle) + return db2Table->dataFile(); + else + for (uint i = 0; i < table_share->keys; ++i) + if (indexHandles[i] == activeHandle) + return db2Table->indexFile(i); + DBUG_ASSERT(0); + return NULL; + } + + int prepReadBuffer(ha_rows rowsToRead, const db2i_file* file, char intent); + int prepWriteBuffer(ha_rows rowsToWrite, const db2i_file* file); + + void invalidateCachedStats() + { + share->cachedStats.invalidate(rowCount | deletedRowCount | objLength | + meanRowLen | ioCount); + } + + void warnIfInvalidData() + { + if (unlikely(invalidDataFound)) + { + warning(ha_thd(), DB2I_ERR_INVALID_DATA, table->alias); + } + } + + /** + Calculate the maximum value that a particular field can hold. + + This is used to anticipate overflows in the auto_increment processing. + + @param field The Field to be analyzed + + @return The maximum value + */ + static uint64 maxValueForField(const Field* field) + { + uint64 maxValue=0; + switch (field->type()) + { + case MYSQL_TYPE_TINY: + if (((const Field_num*)field)->unsigned_flag) + maxValue = (1 << 8) - 1; + else + maxValue = (1 << 7) - 1; + break; + case MYSQL_TYPE_SHORT: + if (((const Field_num*)field)->unsigned_flag) + maxValue = (1 << 16) - 1; + else + maxValue = (1 << 15) - 1; + break; + case MYSQL_TYPE_INT24: + if (((const Field_num*)field)->unsigned_flag) + maxValue = (1 << 24) - 1; + else + maxValue = (1 << 23) - 1; + break; + case MYSQL_TYPE_LONG: + if (((const Field_num*)field)->unsigned_flag) + maxValue = (1LL << 32) - 1; + else + maxValue = (1 << 31) - 1; + break; + case MYSQL_TYPE_LONGLONG: + if (((const Field_num*)field)->unsigned_flag) + maxValue = ~(0LL); + else + maxValue = 1 << 63 - 1; + break; + } + + return maxValue; + } + + void cleanupBuffers() + { + if (blobReadBuffers) + { + delete blobReadBuffers; + blobReadBuffers = NULL; + } + if (blobWriteBuffers) + { + delete[] blobWriteBuffers; + blobWriteBuffers = NULL; + } + if (alloc_root_inited(&conversionBufferMemroot)) + { + free_root(&conversionBufferMemroot, MYF(0)); + } + } + +}; diff --git a/storage/ibmdb2i/plug.in b/storage/ibmdb2i/plug.in new file mode 100644 index 00000000000..0913d72aabf --- /dev/null +++ b/storage/ibmdb2i/plug.in @@ -0,0 +1,12 @@ +MYSQL_STORAGE_ENGINE([ibmdb2i], [], [IBM DB2 for i Storage Engine], + [IBM DB2 for i Storage Engine], [max,max-no-ndb]) +MYSQL_PLUGIN_DYNAMIC([ibmdb2i], [ha_ibmdb2i.la]) + +AC_CHECK_HEADER([qlgusr.h], + # qlgusr.h is just one of the headers from the i5/OS PASE environment; the + # EBCDIC headers are in /QIBM/include, and have to be converted to ASCII + # before cpp gets to them + [:], + # Missing PASE environment, can't build this engine + [mysql_plugin_ibmdb2i=no + with_plugin_ibmdb2i=no]) diff --git a/storage/innobase/btr/btr0sea.c b/storage/innobase/btr/btr0sea.c index 3482e16497a..84ad0e27110 100644 --- a/storage/innobase/btr/btr0sea.c +++ b/storage/innobase/btr/btr0sea.c @@ -1105,12 +1105,20 @@ btr_search_drop_page_hash_when_freed( page = buf_page_get_gen(space, page_no, RW_S_LATCH, NULL, BUF_GET_IF_IN_POOL, __FILE__, __LINE__, &mtr); + /* Because the buffer pool mutex was released by + buf_page_peek_if_search_hashed(), it is possible that the + block was removed from the buffer pool by another thread + before buf_page_get_gen() got a chance to acquire the buffer + pool mutex again. Thus, we must check for a NULL return. */ + + if (UNIV_LIKELY(page != NULL)) { #ifdef UNIV_SYNC_DEBUG - buf_page_dbg_add_level(page, SYNC_TREE_NODE_FROM_HASH); + buf_page_dbg_add_level(page, SYNC_TREE_NODE_FROM_HASH); #endif /* UNIV_SYNC_DEBUG */ - btr_search_drop_page_hash_index(page); + btr_search_drop_page_hash_index(page); + } mtr_commit(&mtr); } diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index f594e64f517..65f1c9536bd 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -868,11 +868,11 @@ err_exit: of the error condition, since the user may want to dump data from the clustered index. However we load the foreign key information only if all indexes were loaded. */ - if (err != DB_SUCCESS && !srv_force_recovery) { - dict_mem_table_free(table); - table = NULL; - } else if (err == DB_SUCCESS) { + if (err == DB_SUCCESS) { err = dict_load_foreigns(table->name, TRUE); + } else if (!srv_force_recovery) { + dict_table_remove_from_cache(table); + table = NULL; } #if 0 if (err != DB_SUCCESS && table != NULL) { diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 502a1da7b4d..034a190f7de 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -953,6 +953,12 @@ innobase_next_autoinc( /* Should never be 0. */ ut_a(increment > 0); + /* According to MySQL documentation, if the offset is greater than + the increment then the offset is ignored. */ + if (offset > increment) { + offset = 0; + } + if (max_value <= current) { next_value = max_value; } else if (offset <= 1) { @@ -3322,8 +3328,8 @@ build_template( goto include_field; } - if (bitmap_is_set(table->read_set, i) || - bitmap_is_set(table->write_set, i)) { + if (bitmap_is_set(table->read_set, i) || + bitmap_is_set(table->write_set, i)) { /* This field is needed in the query */ goto include_field; @@ -3415,7 +3421,7 @@ skip_field: } /************************************************************************ -Get the upper limit of the MySQL integral type. */ +Get the upper limit of the MySQL integral and floating-point type. */ ulonglong ha_innobase::innobase_get_int_col_max_value( @@ -3426,7 +3432,7 @@ ha_innobase::innobase_get_int_col_max_value( switch(field->key_type()) { /* TINY */ - case HA_KEYTYPE_BINARY: + case HA_KEYTYPE_BINARY: max_value = 0xFFULL; break; case HA_KEYTYPE_INT8: @@ -3440,7 +3446,7 @@ ha_innobase::innobase_get_int_col_max_value( max_value = 0x7FFFULL; break; /* MEDIUM */ - case HA_KEYTYPE_UINT24: + case HA_KEYTYPE_UINT24: max_value = 0xFFFFFFULL; break; case HA_KEYTYPE_INT24: @@ -3454,12 +3460,20 @@ ha_innobase::innobase_get_int_col_max_value( max_value = 0x7FFFFFFFULL; break; /* BIG */ - case HA_KEYTYPE_ULONGLONG: + case HA_KEYTYPE_ULONGLONG: max_value = 0xFFFFFFFFFFFFFFFFULL; break; case HA_KEYTYPE_LONGLONG: max_value = 0x7FFFFFFFFFFFFFFFULL; break; + case HA_KEYTYPE_FLOAT: + /* We use the maximum as per IEEE754-2008 standard, 2^24 */ + max_value = 0x1000000ULL; + break; + case HA_KEYTYPE_DOUBLE: + /* We use the maximum as per IEEE754-2008 standard, 2^53 */ + max_value = 0x20000000000000ULL; + break; default: ut_error; } @@ -3587,7 +3601,8 @@ ha_innobase::write_row( /* out: error code */ uchar* record) /* in: a row in MySQL format */ { - int error = 0; + ulint error = 0; + int error_result= 0; ibool auto_inc_used= FALSE; ulint sql_command; trx_t* trx = thd_to_trx(user_thd); @@ -3703,6 +3718,7 @@ no_commit: } /* MySQL errors are passed straight back. */ + error_result = (int) error; goto func_exit; } @@ -3780,7 +3796,7 @@ no_commit: will be 0 if get_auto_increment() was not called.*/ if (auto_inc <= col_max_value - && auto_inc > prebuilt->autoinc_last_value) { + && auto_inc >= prebuilt->autoinc_last_value) { set_max_autoinc: ut_a(prebuilt->autoinc_increment > 0); @@ -3796,7 +3812,7 @@ set_max_autoinc: err = innobase_set_max_autoinc(auto_inc); if (err != DB_SUCCESS) { - error = (int) err; + error = err; } } break; @@ -3806,12 +3822,12 @@ set_max_autoinc: innodb_srv_conc_exit_innodb(prebuilt->trx); report_error: - error = convert_error_code_to_mysql(error, user_thd); + error_result = convert_error_code_to_mysql((int) error, user_thd); func_exit: innobase_active_small(); - DBUG_RETURN(error); + DBUG_RETURN(error_result); } /************************************************************************** @@ -4260,7 +4276,6 @@ convert_search_mode_to_innobase( case HA_READ_MBR_WITHIN: case HA_READ_MBR_DISJOINT: case HA_READ_MBR_EQUAL: - my_error(ER_TABLE_CANT_HANDLE_SPKEYS, MYF(0)); return(PAGE_CUR_UNSUPP); /* do not use "default:" in order to produce a gcc warning: enumeration value '...' not handled in switch @@ -5811,7 +5826,7 @@ ha_innobase::records_in_range( mode2); } else { - n_rows = 0; + n_rows = HA_POS_ERROR; } dtuple_free_for_mysql(heap1); @@ -7659,11 +7674,13 @@ ha_innobase::get_auto_increment( prebuilt->autoinc_last_value = next_value; - ut_a(prebuilt->autoinc_last_value >= *first_value); - - /* Update the table autoinc variable */ - dict_table_autoinc_update_if_greater( - prebuilt->table, prebuilt->autoinc_last_value); + if (prebuilt->autoinc_last_value < *first_value) { + *first_value = (~(ulonglong) 0); + } else { + /* Update the table autoinc variable */ + dict_table_autoinc_update_if_greater( + prebuilt->table, prebuilt->autoinc_last_value); + } } else { /* This will force write_row() into attempting an update of the table's AUTOINC counter. */ diff --git a/storage/innobase/include/rem0rec.h b/storage/innobase/include/rem0rec.h index 43ef6650e90..abc204bb583 100644 --- a/storage/innobase/include/rem0rec.h +++ b/storage/innobase/include/rem0rec.h @@ -365,8 +365,9 @@ rec_set_field_extern_bits( /*************************************************************** This is used to modify the value of an already existing field in a record. The previous value must have exactly the same size as the new value. If len -is UNIV_SQL_NULL then the field is treated as an SQL null for old-style -records. For new-style records, len must not be UNIV_SQL_NULL. */ +is UNIV_SQL_NULL then the field is treated as an SQL null. +For records in ROW_FORMAT=COMPACT (new-style records), len must not be +UNIV_SQL_NULL unless the field already is SQL null. */ UNIV_INLINE void rec_set_nth_field( @@ -375,11 +376,7 @@ rec_set_nth_field( const ulint* offsets,/* in: array returned by rec_get_offsets() */ ulint n, /* in: index number of the field */ const void* data, /* in: pointer to the data if not SQL null */ - ulint len); /* in: length of the data or UNIV_SQL_NULL. - If not SQL null, must have the same - length as the previous value. - If SQL null, previous value must be - SQL null. */ + ulint len); /* in: length of the data or UNIV_SQL_NULL */ /************************************************************** The following function returns the data size of an old-style physical record, that is the sum of field lengths. SQL null fields diff --git a/storage/innobase/include/rem0rec.ic b/storage/innobase/include/rem0rec.ic index 5a4a0a0b5df..d91fb4c4391 100644 --- a/storage/innobase/include/rem0rec.ic +++ b/storage/innobase/include/rem0rec.ic @@ -1219,8 +1219,9 @@ rec_get_nth_field_size( /*************************************************************** This is used to modify the value of an already existing field in a record. The previous value must have exactly the same size as the new value. If len -is UNIV_SQL_NULL then the field is treated as an SQL null for old-style -records. For new-style records, len must not be UNIV_SQL_NULL. */ +is UNIV_SQL_NULL then the field is treated as an SQL null. +For records in ROW_FORMAT=COMPACT (new-style records), len must not be +UNIV_SQL_NULL unless the field already is SQL null. */ UNIV_INLINE void rec_set_nth_field( @@ -1230,11 +1231,7 @@ rec_set_nth_field( ulint n, /* in: index number of the field */ const void* data, /* in: pointer to the data if not SQL null */ - ulint len) /* in: length of the data or UNIV_SQL_NULL. - If not SQL null, must have the same - length as the previous value. - If SQL null, previous value must be - SQL null. */ + ulint len) /* in: length of the data or UNIV_SQL_NULL */ { byte* data2; ulint len2; @@ -1242,9 +1239,11 @@ rec_set_nth_field( ut_ad(rec); ut_ad(rec_offs_validate(rec, NULL, offsets)); - if (len == UNIV_SQL_NULL) { - ut_ad(!rec_offs_comp(offsets)); - rec_set_nth_field_sql_null(rec, n); + if (UNIV_UNLIKELY(len == UNIV_SQL_NULL)) { + if (!rec_offs_nth_sql_null(offsets, n)) { + ut_a(!rec_offs_comp(offsets)); + rec_set_nth_field_sql_null(rec, n); + } return; } diff --git a/storage/innobase/include/trx0roll.h b/storage/innobase/include/trx0roll.h index 25546430ba0..c1eca3d5753 100644 --- a/storage/innobase/include/trx0roll.h +++ b/storage/innobase/include/trx0roll.h @@ -15,6 +15,8 @@ Created 3/26/1996 Heikki Tuuri #include "mtr0mtr.h" #include "trx0sys.h" +#define trx_roll_free_all_savepoints(s) trx_roll_savepoints_free((s), NULL) + /*********************************************************************** Returns a transaction savepoint taken at this point in time. */ @@ -237,7 +239,17 @@ trx_release_savepoint_for_mysql( const char* savepoint_name); /* in: savepoint name */ /*********************************************************************** -Frees savepoint structs. */ +Frees a single savepoint struct. */ + +void +trx_roll_savepoint_free( +/*=====================*/ + trx_t* trx, /* in: transaction handle */ + trx_named_savept_t* savep); /* in: savepoint to free */ + +/*********************************************************************** +Frees savepoint structs starting from savep, if savep == NULL then +free all savepoints. */ void trx_roll_savepoints_free( diff --git a/storage/innobase/lock/lock0lock.c b/storage/innobase/lock/lock0lock.c index 173d074cb82..d9bc8ba09e4 100644 --- a/storage/innobase/lock/lock0lock.c +++ b/storage/innobase/lock/lock0lock.c @@ -681,7 +681,10 @@ lock_is_table_exclusive( lock_t* lock; ibool ok = FALSE; - ut_ad(table && trx); + ut_ad(table); + ut_ad(trx); + + lock_mutex_enter_kernel(); for (lock = UT_LIST_GET_FIRST(table->locks); lock; @@ -689,7 +692,7 @@ lock_is_table_exclusive( if (lock->trx != trx) { /* A lock on the table is held by some other transaction. */ - return(FALSE); + goto not_ok; } if (!(lock_get_type(lock) & LOCK_TABLE)) { @@ -706,11 +709,16 @@ lock_is_table_exclusive( auto_increment lock. */ break; default: +not_ok: /* Other table locks than LOCK_IX are not allowed. */ - return(FALSE); + ok = FALSE; + goto func_exit; } } +func_exit: + lock_mutex_exit_kernel(); + return(ok); } @@ -3664,6 +3672,7 @@ lock_table_has_to_wait_in_queue( dict_table_t* table; lock_t* lock; + ut_ad(mutex_own(&kernel_mutex)); ut_ad(lock_get_wait(wait_lock)); table = wait_lock->un_member.tab_lock.table; diff --git a/storage/innobase/trx/trx0roll.c b/storage/innobase/trx/trx0roll.c index 91dcf035f96..8934fe87c7e 100644 --- a/storage/innobase/trx/trx0roll.c +++ b/storage/innobase/trx/trx0roll.c @@ -185,7 +185,25 @@ trx_rollback_last_sql_stat_for_mysql( } /*********************************************************************** -Frees savepoint structs. */ +Frees a single savepoint struct. */ + +void +trx_roll_savepoint_free( +/*=====================*/ + trx_t* trx, /* in: transaction handle */ + trx_named_savept_t* savep) /* in: savepoint to free */ +{ + ut_a(savep != NULL); + ut_a(UT_LIST_GET_LEN(trx->trx_savepoints) > 0); + + UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); + mem_free(savep->name); + mem_free(savep); +} + +/*********************************************************************** +Frees savepoint structs starting from savep, if savep == NULL then +free all savepoints. */ void trx_roll_savepoints_free( @@ -206,9 +224,7 @@ trx_roll_savepoints_free( while (savep != NULL) { next_savep = UT_LIST_GET_NEXT(trx_savepoints, savep); - UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); - mem_free(savep->name); - mem_free(savep); + trx_roll_savepoint_free(trx, savep); savep = next_savep; } @@ -343,8 +359,8 @@ trx_savepoint_for_mysql( } /*********************************************************************** -Releases a named savepoint. Savepoints which -were set after this savepoint are deleted. */ +Releases only the named savepoint. Savepoints which were set after this +savepoint are left as is. */ ulint trx_release_savepoint_for_mysql( @@ -360,31 +376,16 @@ trx_release_savepoint_for_mysql( savep = UT_LIST_GET_FIRST(trx->trx_savepoints); + /* Search for the savepoint by name and free if found. */ while (savep != NULL) { if (0 == ut_strcmp(savep->name, savepoint_name)) { - /* Found */ - break; + trx_roll_savepoint_free(trx, savep); + return(DB_SUCCESS); } savep = UT_LIST_GET_NEXT(trx_savepoints, savep); } - if (savep == NULL) { - - return(DB_NO_SAVEPOINT); - } - - /* We can now free all savepoints strictly later than this one */ - - trx_roll_savepoints_free(trx, savep); - - /* Now we can free this savepoint too */ - - UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); - - mem_free(savep->name); - mem_free(savep); - - return(DB_SUCCESS); + return(DB_NO_SAVEPOINT); } /*********************************************************************** diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index 1fceaa3562c..43456865903 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -930,8 +930,8 @@ trx_commit_off_kernel( mutex_enter(&kernel_mutex); } - /* Free savepoints */ - trx_roll_savepoints_free(trx, NULL); + /* Free all savepoints */ + trx_roll_free_all_savepoints(trx); trx->conc_state = TRX_NOT_STARTED; trx->rseg = NULL; diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 7fcdeaf69b2..8a0d579a3ac 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -1575,7 +1575,7 @@ int ha_maria::preload_keys(THD * thd, HA_CHECK_OPT *check_opt) if ((error= maria_preload(file, map, table_list->ignore_leaves))) { - char buf[ERRMSGSIZE+20]; + char buf[MYSQL_ERRMSG_SIZE+20]; const char *errmsg; switch (error) { @@ -1586,7 +1586,7 @@ int ha_maria::preload_keys(THD * thd, HA_CHECK_OPT *check_opt) errmsg= "Failed to allocate buffer"; break; default: - my_snprintf(buf, ERRMSGSIZE, + my_snprintf(buf, sizeof(buf), "Failed to read from index file (errno: %d)", my_errno); errmsg= buf; } @@ -2229,6 +2229,10 @@ int ha_maria::extra(enum ha_extra_function operation) { if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_KEYREAD) return 0; +#ifdef NOT_USED + if (operation == HA_EXTRA_MMAP && !opt_maria_use_mmap) + return 0; +#endif /* We have to set file->trn here because in some cases we call diff --git a/storage/maria/ma_loghandler.c b/storage/maria/ma_loghandler.c index 6fc15186fcd..e9076f3f207 100644 --- a/storage/maria/ma_loghandler.c +++ b/storage/maria/ma_loghandler.c @@ -6150,6 +6150,7 @@ my_bool translog_write_record(LSN *lsn, LSN dummy_lsn; LEX_CUSTRING log_array[TRANSLOG_INTERNAL_PARTS + 1]; uchar log_data[6]; + DBUG_ASSERT(trn->undo_lsn == LSN_IMPOSSIBLE); int6store(log_data, trn->trid); log_array[TRANSLOG_INTERNAL_PARTS + 0].str= log_data; log_array[TRANSLOG_INTERNAL_PARTS + 0].length= sizeof(log_data); diff --git a/storage/maria/ma_recovery.c b/storage/maria/ma_recovery.c index 08aba4fdcd6..54639b596d9 100644 --- a/storage/maria/ma_recovery.c +++ b/storage/maria/ma_recovery.c @@ -2031,7 +2031,8 @@ prototype_redo_exec_hook(DEBUG_INFO) data= log_record_buffer.str + 1; switch (debug_info) { case LOGREC_DEBUG_INFO_QUERY: - tprint(tracef, "Query: %s\n", (char*) data); + tprint(tracef, "Query: %.*s\n", rec->record_length - 1, + (char*) data); break; default: DBUG_ASSERT(0); diff --git a/storage/maria/ma_test_force_start.pl b/storage/maria/ma_test_force_start.pl index 3aa6da5f5dc..7ab8190a738 100755 --- a/storage/maria/ma_test_force_start.pl +++ b/storage/maria/ma_test_force_start.pl @@ -42,7 +42,8 @@ my $sql_name= "./var/tmp/create_table.sql"; my $error_log_name= "./var/log/master.err"; my @cmd_output; my $whatever; # garbage data -my $base_server_cmd= "perl mysql-test-run.pl --mysqld=--maria-force-start-after-recovery-failures=$force_after maria-recover ";
+$ENV{MTR_VERSION} = 1; # MTR2 does not have --start-and-exit +my $base_server_cmd= "perl mysql-test-run.pl --mysqld=--maria-force-start-after-recovery-failures=$force_after --suite=maria maria.maria-recover ";
if ($^O =~ /^mswin/i) { print <<EOF; diff --git a/storage/maria/maria_chk.c b/storage/maria/maria_chk.c index 282a7db19b0..ab3c641b55f 100644 --- a/storage/maria/maria_chk.c +++ b/storage/maria/maria_chk.c @@ -428,6 +428,7 @@ static void usage(void) -#, --debug=... Output debug log. Often this is 'd:t:o,filename'.\n"); #endif printf("\ + -H, --HELP Display this help and exit.\n\ -?, --help Display this help and exit.\n\ -O, --set-variable var=option.\n\ Change the value of a variable. Please note that\n\ diff --git a/storage/maria/trnman.c b/storage/maria/trnman.c index 06b3b828d50..43fac68806f 100644 --- a/storage/maria/trnman.c +++ b/storage/maria/trnman.c @@ -712,11 +712,6 @@ my_bool trnman_collect_transactions(LEX_STRING *str_act, LEX_STRING *str_com, ptr+= TRANSID_SIZE; for (trn= active_list_min.next; trn != &active_list_max; trn= trn->next) { - /* - trns with a short trid of 0 are not even initialized, we can ignore - them. trns with undo_lsn==0 have done no writes, we can ignore them - too. XID not needed now. - */ uint sid; LSN rec_lsn, undo_lsn, first_undo_lsn; pthread_mutex_lock(&trn->state_lock); @@ -732,16 +727,24 @@ my_bool trnman_collect_transactions(LEX_STRING *str_act, LEX_STRING *str_com, */ continue; } - /* needed for low-water mark calculation */ + /* needed for low-water mark calculation */ if (((rec_lsn= lsn_read_non_atomic(trn->rec_lsn)) > 0) && (cmp_translog_addr(rec_lsn, minimum_rec_lsn) < 0)) minimum_rec_lsn= rec_lsn; /* - trn may have logged REDOs but not yet UNDO, that's why we read rec_lsn - before deciding to ignore if undo_lsn==0. + If trn has not logged LOGREC_LONG_TRANSACTION_ID, this trn will be + discovered when seeing that log record which is for sure located after + checkpoint_start_log_horizon. */ - if ((undo_lsn= trn->undo_lsn) == 0) /* trn can be forgotten */ + if ((LSN_WITH_FLAGS_TO_FLAGS(trn->first_undo_lsn) & + TRANSACTION_LOGGED_LONG_ID) == 0) continue; + /* + On the other hand, if undo_lsn is LSN_IMPOSSIBLE, trn may later log + records; so we must include trn in the checkpoint now, because we cannot + count on LOGREC_LONG_TRANSACTION_ID (as we are already past it). + */ + undo_lsn= trn->undo_lsn; stored_transactions++; int2store(ptr, sid); ptr+= 2; diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index a6e6d4ea9cb..d20148f5be5 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -1272,7 +1272,7 @@ int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt) ulonglong map; TABLE_LIST *table_list= table->pos_in_table_list; my_bool ignore_leaves= table_list->ignore_leaves; - char buf[ERRMSGSIZE+20]; + char buf[MYSQL_ERRMSG_SIZE]; DBUG_ENTER("ha_myisam::preload_keys"); @@ -1300,7 +1300,7 @@ int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt) errmsg= "Failed to allocate buffer"; break; default: - my_snprintf(buf, ERRMSGSIZE, + my_snprintf(buf, sizeof(buf), "Failed to read from index file (errno: %d)", my_errno); errmsg= buf; } @@ -1775,7 +1775,7 @@ int ha_myisam::info(uint flag) if (share->key_parts) memcpy((char*) table->key_info[0].rec_per_key, (char*) misam_info.rec_per_key, - sizeof(table->key_info[0].rec_per_key)*share->key_parts); + sizeof(table->key_info[0].rec_per_key[0])*share->key_parts); if (share->tmp_table == NO_TMP_TABLE) pthread_mutex_unlock(&share->mutex); @@ -1811,6 +1811,8 @@ int ha_myisam::extra(enum ha_extra_function operation) { if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_KEYREAD) return 0; + if (operation == HA_EXTRA_MMAP && !opt_myisam_use_mmap) + return 0; return mi_extra(file, operation, 0); } @@ -2195,6 +2197,15 @@ my_bool ha_myisam::register_query_cache_table(THD *thd, char *table_name, } } + /* + This query execution might have started after the query cache was flushed + by a concurrent INSERT. In this case, don't cache this statement as the + data file length difference might not be visible yet if the tables haven't + been unlocked by the concurrent insert thread. + */ + if (file->state->uncacheable) + DBUG_RETURN(FALSE); + /* It is ok to try to cache current statement. */ DBUG_RETURN(TRUE); } diff --git a/storage/myisam/mi_locking.c b/storage/myisam/mi_locking.c index 2edeefad45f..f3d9934ed8c 100644 --- a/storage/myisam/mi_locking.c +++ b/storage/myisam/mi_locking.c @@ -299,6 +299,8 @@ void mi_get_status(void* param, my_bool concurrent_insert) info->save_state=info->s->state.state; info->state= &info->save_state; info->append_insert_at_end= concurrent_insert; + if (concurrent_insert) + info->s->state.state.uncacheable= TRUE; DBUG_VOID_RETURN; } diff --git a/storage/myisam/myisamchk.c b/storage/myisam/myisamchk.c index 6fbc67dbc70..5edc2cf9828 100644 --- a/storage/myisam/myisamchk.c +++ b/storage/myisam/myisamchk.c @@ -360,6 +360,7 @@ static void usage(void) -#, --debug=... Output debug log. Often this is 'd:t:o,filename'.\n"); #endif printf("\ + -H, --HELP Display this help and exit.\n\ -?, --help Display this help and exit.\n\ -O, --set-variable var=option.\n\ Change the value of a variable. Please note that\n\ diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h index 6ab7451196a..f311202e47a 100644 --- a/storage/myisam/myisamdef.h +++ b/storage/myisam/myisamdef.h @@ -39,6 +39,7 @@ typedef struct st_mi_status_info my_off_t key_file_length; my_off_t data_file_length; ha_checksum checksum; + my_bool uncacheable; /* Active concurrent insert */ } MI_STATUS_INFO; typedef struct st_mi_state_info diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 392a8600da3..55c46b2ac10 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -116,7 +116,7 @@ static handler *myisammrg_create_handler(handlerton *hton, */ ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg) - :handler(hton, table_arg), file(0) + :handler(hton, table_arg), file(0), is_cloned(0) {} @@ -415,7 +415,28 @@ int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), /* retrieve children table list. */ my_errno= 0; - if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this))) + if (is_cloned) + { + /* + Open and attaches the MyISAM tables,that are under the MERGE table + parent, on the MyISAM storage engine interface directly within the + MERGE engine. The new MyISAM table instances, as well as the MERGE + clone itself, are not visible in the table cache. This is not a + problem because all locking is handled by the original MERGE table + from which this is cloned of. + */ + if (!(file= myrg_open(table->s->normalized_path.str, table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED))) + { + DBUG_PRINT("error", ("my_errno %d", my_errno)); + DBUG_RETURN(my_errno ? my_errno : -1); + } + + file->children_attached= TRUE; + + info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); + } + else if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this))) { DBUG_PRINT("error", ("my_errno %d", my_errno)); DBUG_RETURN(my_errno ? my_errno : -1); @@ -424,6 +445,55 @@ int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), DBUG_RETURN(0); } +/** + Returns a cloned instance of the current handler. + + @return A cloned handler instance. + */ +handler *ha_myisammrg::clone(MEM_ROOT *mem_root) +{ + MYRG_TABLE *u_table,*newu_table; + ha_myisammrg *new_handler= + (ha_myisammrg*) get_new_handler(table->s, mem_root, table->s->db_type()); + if (!new_handler) + return NULL; + + /* Inform ha_myisammrg::open() that it is a cloned handler */ + new_handler->is_cloned= TRUE; + /* + Allocate handler->ref here because otherwise ha_open will allocate it + on this->table->mem_root and we will not be able to reclaim that memory + when the clone handler object is destroyed. + */ + if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2))) + { + delete new_handler; + return NULL; + } + + if (new_handler->ha_open(table, table->s->normalized_path.str, table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED)) + { + delete new_handler; + return NULL; + } + + /* + Iterate through the original child tables and + copy the state into the cloned child tables. + We need to do this because all the child tables + can be involved in delete. + */ + newu_table= new_handler->file->open_tables; + for (u_table= file->open_tables; u_table < file->end_table; u_table++) + { + newu_table->table->state= u_table->table->state; + newu_table++; + } + + return new_handler; + } + /** @brief Attach children to a MERGE table. @@ -615,9 +685,10 @@ int ha_myisammrg::close(void) DBUG_ENTER("ha_myisammrg::close"); /* Children must not be attached here. Unless the MERGE table has no - children. In this case children_attached is always true. + children or the handler instance has been cloned. In these cases + children_attached is always true. */ - DBUG_ASSERT(!this->file->children_attached || !this->file->tables); + DBUG_ASSERT(!this->file->children_attached || !this->file->tables || this->is_cloned); rc= myrg_close(file); file= 0; DBUG_RETURN(rc); @@ -803,6 +874,16 @@ int ha_myisammrg::info(uint flag) table->s->crashed= 1; #endif stats.data_file_length= mrg_info.data_file_length; + if (mrg_info.errkey >= (int) table_share->keys) + { + /* + If value of errkey is higher than the number of keys + on the table set errkey to MAX_KEY. This will be + treated as unknown key case and error message generator + won't try to locate key causing segmentation fault. + */ + mrg_info.errkey= MAX_KEY; + } errkey= mrg_info.errkey; table->s->keys_in_use.set_prefix(table->s->keys); stats.mean_rec_length= mrg_info.reclength; diff --git a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h index 13d64588b06..3434aa2f4ed 100644 --- a/storage/myisammrg/ha_myisammrg.h +++ b/storage/myisammrg/ha_myisammrg.h @@ -25,6 +25,7 @@ class ha_myisammrg: public handler { MYRG_INFO *file; + my_bool is_cloned; /* This instance has been cloned */ public: TABLE_LIST *next_child_attach; /* next child to attach */ @@ -60,6 +61,7 @@ class ha_myisammrg: public handler int open(const char *name, int mode, uint test_if_locked); int attach_children(void); int detach_children(void); + virtual handler *clone(MEM_ROOT *mem_root); int close(void); int write_row(uchar * buf); int update_row(const uchar * old_data, uchar * new_data); diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c index b5002116164..14ba2853b22 100644 --- a/storage/myisammrg/myrg_open.c +++ b/storage/myisammrg/myrg_open.c @@ -33,7 +33,6 @@ myrg_attach_children(). Please duplicate changes in these functions or make common sub-functions. */ -/* purecov: begin deadcode */ /* not used in MySQL server */ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) { @@ -47,6 +46,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) MI_INFO *isam=0; uint found_merge_insert_method= 0; size_t name_buff_length; + my_bool bad_children= FALSE; DBUG_ENTER("myrg_open"); LINT_INIT(key_parts); @@ -97,13 +97,13 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) fn_format(buff, buff, "", "", 0); if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0)))) { - my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; if (handle_locking & HA_OPEN_FOR_REPAIR) { myrg_print_wrong_table(buff); + bad_children= TRUE; continue; } - goto err; + goto bad_children; } if (!m_info) /* First file */ { @@ -128,13 +128,13 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) files++; if (m_info->reclength != isam->s->base.reclength) { - my_errno=HA_ERR_WRONG_MRG_TABLE_DEF; if (handle_locking & HA_OPEN_FOR_REPAIR) { myrg_print_wrong_table(buff); + bad_children= TRUE; continue; } - goto err; + goto bad_children; } m_info->options|= isam->s->options; m_info->records+= isam->state->records; @@ -147,8 +147,8 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) m_info->tables); } - if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF) - goto err; + if (bad_children) + goto bad_children; if (!m_info && !(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO), MYF(MY_WME | MY_ZEROFILL)))) goto err; @@ -178,12 +178,14 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) pthread_mutex_unlock(&THR_LOCK_open); DBUG_RETURN(m_info); +bad_children: + my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; err: save_errno=my_errno; switch (errpos) { case 3: while (files) - mi_close(m_info->open_tables[--files].table); + (void) mi_close(m_info->open_tables[--files].table); my_free((char*) m_info,MYF(0)); /* Fall through */ case 2: @@ -195,7 +197,6 @@ err: my_errno=save_errno; DBUG_RETURN (NULL); } -/* purecov: end */ /** @@ -392,6 +393,7 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, uint child_nr; uint key_parts; uint min_keys; + my_bool bad_children= FALSE; DBUG_ENTER("myrg_attach_children"); DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking)); @@ -424,10 +426,11 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, if (!m_info->rec_per_key_part) { if(!(m_info->rec_per_key_part= (ulong*) - my_malloc(key_parts * sizeof(long), MYF(MY_WME|MY_ZEROFILL)))) + my_malloc(key_parts * sizeof(long), MYF(MY_WME)))) goto err; /* purecov: inspected */ errpos= 1; } + bzero((char*) m_info->rec_per_key_part, key_parts * sizeof(long)); } /* Add MyISAM table info. */ @@ -441,13 +444,13 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, DBUG_PRINT("error", ("definition mismatch table: '%s' repair: %d", myisam->filename, (handle_locking & HA_OPEN_FOR_REPAIR))); - my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; if (handle_locking & HA_OPEN_FOR_REPAIR) { myrg_print_wrong_table(myisam->filename); + bad_children= TRUE; continue; } - goto err; + goto bad_children; } m_info->options|= myisam->s->options; @@ -462,6 +465,9 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, child_nr++; } + if (bad_children) + goto bad_children; + /* Note: callback() resets my_errno, so it is safe to check it here */ if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF) goto err; if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L) @@ -477,6 +483,8 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, pthread_mutex_unlock(&m_info->mutex); DBUG_RETURN(0); +bad_children: + my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; err: save_errno= my_errno; switch (errpos) { |