summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
Diffstat (limited to 'storage')
-rw-r--r--storage/Makefile.am1
-rw-r--r--storage/ibmdb2i/CMakeLists.txt25
-rw-r--r--storage/ibmdb2i/Makefile.am53
-rw-r--r--storage/ibmdb2i/db2i_blobCollection.cc107
-rw-r--r--storage/ibmdb2i/db2i_blobCollection.h151
-rw-r--r--storage/ibmdb2i/db2i_charsetSupport.cc793
-rw-r--r--storage/ibmdb2i/db2i_charsetSupport.h65
-rw-r--r--storage/ibmdb2i/db2i_collationSupport.cc359
-rw-r--r--storage/ibmdb2i/db2i_collationSupport.h48
-rw-r--r--storage/ibmdb2i/db2i_constraints.cc698
-rw-r--r--storage/ibmdb2i/db2i_conversion.cc1446
-rw-r--r--storage/ibmdb2i/db2i_errors.cc297
-rw-r--r--storage/ibmdb2i/db2i_errors.h93
-rw-r--r--storage/ibmdb2i/db2i_file.cc556
-rw-r--r--storage/ibmdb2i/db2i_file.h446
-rw-r--r--storage/ibmdb2i/db2i_global.h138
-rw-r--r--storage/ibmdb2i/db2i_iconv.h51
-rw-r--r--storage/ibmdb2i/db2i_ileBridge.cc1342
-rw-r--r--storage/ibmdb2i/db2i_ileBridge.h499
-rw-r--r--storage/ibmdb2i/db2i_ioBuffers.cc332
-rw-r--r--storage/ibmdb2i/db2i_ioBuffers.h416
-rw-r--r--storage/ibmdb2i/db2i_misc.h107
-rw-r--r--storage/ibmdb2i/db2i_myconv.cc1498
-rw-r--r--storage/ibmdb2i/db2i_myconv.h3200
-rw-r--r--storage/ibmdb2i/db2i_rir.cc465
-rw-r--r--storage/ibmdb2i/db2i_safeString.h98
-rw-r--r--storage/ibmdb2i/db2i_sqlStatementStream.cc86
-rw-r--r--storage/ibmdb2i/db2i_sqlStatementStream.h151
-rw-r--r--storage/ibmdb2i/db2i_validatedPointer.h162
-rw-r--r--storage/ibmdb2i/ha_ibmdb2i.cc3286
-rw-r--r--storage/ibmdb2i/ha_ibmdb2i.h750
-rw-r--r--storage/ibmdb2i/plug.in12
-rw-r--r--storage/innobase/btr/btr0sea.c12
-rw-r--r--storage/innobase/dict/dict0load.c8
-rw-r--r--storage/innobase/handler/ha_innodb.cc53
-rw-r--r--storage/innobase/include/rem0rec.h11
-rw-r--r--storage/innobase/include/rem0rec.ic19
-rw-r--r--storage/innobase/include/trx0roll.h14
-rw-r--r--storage/innobase/lock/lock0lock.c15
-rw-r--r--storage/innobase/trx/trx0roll.c51
-rw-r--r--storage/innobase/trx/trx0trx.c4
-rw-r--r--storage/maria/ha_maria.cc8
-rw-r--r--storage/maria/ma_loghandler.c1
-rw-r--r--storage/maria/ma_recovery.c3
-rwxr-xr-xstorage/maria/ma_test_force_start.pl3
-rw-r--r--storage/maria/maria_chk.c1
-rw-r--r--storage/maria/trnman.c21
-rw-r--r--storage/myisam/ha_myisam.cc17
-rw-r--r--storage/myisam/mi_locking.c2
-rw-r--r--storage/myisam/myisamchk.c1
-rw-r--r--storage/myisam/myisamdef.h1
-rw-r--r--storage/myisammrg/ha_myisammrg.cc89
-rw-r--r--storage/myisammrg/ha_myisammrg.h2
-rw-r--r--storage/myisammrg/myrg_open.c32
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) {