summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimothy Smith <timothy.smith@sun.com>2009-02-15 03:18:30 +0100
committerTimothy Smith <timothy.smith@sun.com>2009-02-15 03:18:30 +0100
commit338aefcb386a4011fcbd671f717b9bd973c94745 (patch)
treecf85c3ae81a37ac48ffc9915543a46da249940c7
parent6bd93f670271eaf2bd79bd7fa538e9baaa7dcb0f (diff)
downloadmariadb-git-338aefcb386a4011fcbd671f717b9bd973c94745.tar.gz
Add the IBM DB2 for i storage engine.
Modify plugins.m4 configuration framework so that plugins which are not built still get added to the source distribution during make dist. This came up now because we can only build ibmdb2i on i5/OS, and we can't bootstrap our source dist on that platform. The solution is to specify DIST_SUBDIRS containing all plugins, separate from SUBDIRS which contains the plugins which are actually built. This ibmdb2i code is from the ibmdb2i-ga3-src.zip file, with a patch to plug.in to disable the plugin if the PASE environment isn't available.
-rw-r--r--config/ac-macros/plugins.m472
-rw-r--r--plugin/Makefile.am2
-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.h146
-rw-r--r--storage/ibmdb2i/db2i_charsetSupport.cc793
-rw-r--r--storage/ibmdb2i/db2i_charsetSupport.h65
-rw-r--r--storage/ibmdb2i/db2i_collationSupport.cc360
-rw-r--r--storage/ibmdb2i/db2i_collationSupport.h45
-rw-r--r--storage/ibmdb2i/db2i_constraints.cc699
-rw-r--r--storage/ibmdb2i/db2i_conversion.cc1168
-rw-r--r--storage/ibmdb2i/db2i_errors.cc296
-rw-r--r--storage/ibmdb2i/db2i_errors.h91
-rw-r--r--storage/ibmdb2i/db2i_file.cc513
-rw-r--r--storage/ibmdb2i/db2i_file.h441
-rw-r--r--storage/ibmdb2i/db2i_global.h138
-rw-r--r--storage/ibmdb2i/db2i_iconv.h51
-rw-r--r--storage/ibmdb2i/db2i_ileBridge.cc1331
-rw-r--r--storage/ibmdb2i/db2i_ileBridge.h488
-rw-r--r--storage/ibmdb2i/db2i_ioBuffers.cc332
-rw-r--r--storage/ibmdb2i/db2i_ioBuffers.h411
-rw-r--r--storage/ibmdb2i/db2i_misc.h95
-rw-r--r--storage/ibmdb2i/db2i_myconv.cc1498
-rw-r--r--storage/ibmdb2i/db2i_myconv.h3200
-rw-r--r--storage/ibmdb2i/db2i_rir.cc441
-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.cc3171
-rw-r--r--storage/ibmdb2i/ha_ibmdb2i.h727
-rw-r--r--storage/ibmdb2i/plug.in12
34 files changed, 17247 insertions, 22 deletions
diff --git a/config/ac-macros/plugins.m4 b/config/ac-macros/plugins.m4
index 8dfb698709f..e1da6fd11f5 100644
--- a/config/ac-macros/plugins.m4
+++ b/config/ac-macros/plugins.m4
@@ -302,7 +302,9 @@ AC_DEFUN([MYSQL_CONFIGURE_PLUGINS],[
_MYSQL_CONFIGURE_PLUGINS(m4_bpatsubst(__mysql_plugin_list__, :, [,]))
_MYSQL_EMIT_PLUGIN_ACTIONS(m4_bpatsubst(__mysql_plugin_list__, :, [,]))
AC_SUBST([mysql_se_dirs])
+ AC_SUBST([mysql_se_distdirs])
AC_SUBST([mysql_pg_dirs])
+ AC_SUBST([mysql_pg_distdirs])
AC_SUBST([mysql_se_unittest_dirs])
AC_SUBST([mysql_pg_unittest_dirs])
AC_SUBST([condition_dependent_plugin_modules])
@@ -354,6 +356,24 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[
fi
AC_MSG_RESULT([no])
],[
+
+ # Plugin is not disabled, determine if it should be built,
+ # or only distributed
+
+ m4_ifdef([$6], [
+ if test ! -d "$srcdir/$6"; then
+ # Plugin directory was removed after autoconf was run; treat
+ # this as a disabled plugin
+ if test "X[$with_plugin_]$2" = Xyes; then
+ AC_MSG_RESULT([error])
+ AC_MSG_ERROR([disabled])
+ fi
+
+ # The result message will be printed below
+ [with_plugin_]$2=no
+ fi
+ ])
+
m4_ifdef([$9],[
if test "X[$with_plugin_]$2" = Xno; then
AC_MSG_RESULT([error])
@@ -372,6 +392,8 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[
;;
esac
])
+
+
if test "X[$with_plugin_]$2" = Xno; then
AC_MSG_RESULT([no])
else
@@ -448,28 +470,36 @@ dnl Although this is "pretty", it breaks libmysqld build
condition_dependent_plugin_includes="$condition_dependent_plugin_includes -I[\$(top_srcdir)]/$6/m4_bregexp($11, [^.+[/$]], [\&])"
])
fi
- m4_ifdef([$6],[
- if test -n "$mysql_use_plugin_dir" ; then
- mysql_plugin_dirs="$mysql_plugin_dirs $6"
- m4_syscmd(test -f "$6/configure")
- ifelse(m4_sysval, 0,
- [AC_CONFIG_SUBDIRS($6)],
- [AC_CONFIG_FILES($6/Makefile)]
- )
- ifelse(m4_substr($6, 0, 8), [storage/],
- [
- [mysql_se_dirs="$mysql_se_dirs ]m4_substr($6, 8)"
- mysql_se_unittest_dirs="$mysql_se_unittest_dirs ../$6"
- ],
- m4_substr($6, 0, 7), [plugin/],
- [
- [mysql_pg_dirs="$mysql_pg_dirs ]m4_substr($6, 7)"
- mysql_pg_unittest_dirs="$mysql_pg_unittest_dirs ../$6"
- ],
- [AC_FATAL([don't know how to handle plugin dir ]$6)])
- fi
- ])
fi
+
+ m4_ifdef([$6], [
+ if test -d "$srcdir/$6"; then
+ # Even if we don't build a plugin, we bundle its source into the dist
+ # file. So its Makefile (and Makefiles for any subdirs) must be
+ # generated for 'make dist' to work.
+ m4_syscmd(test -f "$6/configure")
+ ifelse(m4_sysval, 0,
+ [AC_CONFIG_SUBDIRS($6)],
+ [AC_CONFIG_FILES($6/Makefile)]
+ )
+
+ ifelse(
+ m4_substr($6, 0, 8), [storage/], [
+ mysql_se_distdirs="$mysql_se_distdirs m4_substr($6, 8)"
+ if test -n "$mysql_use_plugin_dir" ; then
+ mysql_se_dirs="$mysql_se_dirs m4_substr($6, 8)"
+ mysql_se_unittest_dirs="$mysql_se_unittest_dirs ../$6"
+ fi],
+
+ m4_substr($6, 0, 7), [plugin/], [
+ mysql_pg_distdirs="$mysql_pg_distdirs m4_substr($6, 7)"
+ if test -n "$mysql_use_plugin_dir" ; then
+ mysql_pg_dirs="$mysql_pg_dirs m4_substr($6, 7)"
+ mysql_pg_unittest_dirs="$mysql_pg_unittest_dirs ../$6"
+ fi],
+ [AC_FATAL([don't know how to handle plugin dir ]$6)])
+ fi
+ ])
])
])
diff --git a/plugin/Makefile.am b/plugin/Makefile.am
index 22f6bfd88b2..68f1f939836 100644
--- a/plugin/Makefile.am
+++ b/plugin/Makefile.am
@@ -22,7 +22,7 @@ AUTOMAKE_OPTIONS = foreign
EXTRA_DIST = fulltext/configure.in
SUBDIRS = @mysql_pg_dirs@
-DIST_SUBDIRS = daemon_example fulltext
+DIST_SUBDIRS = @mysql_pg_distdirs@
# Don't update the files from bitkeeper
%::SCCS/s.%
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..35cfacbf42a
--- /dev/null
+++ b/storage/ibmdb2i/db2i_blobCollection.h
@@ -0,0 +1,146 @@
+/*
+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);
+ }
+ }
+
+ 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..41f7ef0e32f
--- /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::convChars");
+
+ 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..a6ffd661b81
--- /dev/null
+++ b/storage/ibmdb2i/db2i_collationSupport.cc
@@ -0,0 +1,360 @@
+/*
+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 curField 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 curField The field 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 Field *curField,
+ char* fileSortSequenceType,
+ char* fileSortSequence,
+ char* fileSortSequenceLibrary)
+{
+ DBUG_ENTER("ha_ibmdb2i::updateAssociatedSortSequence");
+ DBUG_ASSERT(curField);
+ CHARSET_INFO* fieldCharSet = curField->charset();
+ if (strcmp(fieldCharSet->csname,"binary") != 0)
+ {
+ char newSortSequence[11] = "";
+ char newSortSequenceType = ' ';
+ const char* foundSortSequence;
+ int rc = getAssociatedSortSequence(fieldCharSet, &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 ((fieldCharSet->state & MY_CS_BINSORT) != 0)
+ {
+ strcat(newSortSequence,"U");
+ }
+ else if ((fieldCharSet->state & MY_CS_CSSORT) != 0)
+ {
+ strcat(newSortSequence,"U");
+ }
+ else
+ {
+ strcat(newSortSequence,"S");
+ }
+ newSortSequenceType = 'N';
+ break;
+ default: // ICU sort sequence
+ {
+ if ((fieldCharSet->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..315d6ae4403
--- /dev/null
+++ b/storage/ibmdb2i/db2i_collationSupport.h
@@ -0,0 +1,45 @@
+/*
+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 Field *curField, 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..d219aa05737
--- /dev/null
+++ b/storage/ibmdb2i/db2i_constraints.cc
@@ -0,0 +1,699 @@
+/*
+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),
+ 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;
+ bool gotShare = FALSE; // Indicator for local get_share
+ 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(0);
+
+ 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..229d84c3b40
--- /dev/null
+++ b/storage/ibmdb2i/db2i_conversion.cc
@@ -0,0 +1,1168 @@
+/*
+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"
+
+/**
+ 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;
+ ilen = min(ilen, olen); // Handle partial translation
+ 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);
+}
+
+
+/**
+ 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)
+{
+ char stringBuildBuffer[257];
+ uint32 fieldLength;
+
+ 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"));
+ break;
+ case MYSQL_TYPE_TIME:
+ if (timeFormat == TIME_OF_DAY)
+ mapping.append(STRING_WITH_LEN("TIME"));
+ else
+ mapping.append(STRING_WITH_LEN("INTEGER"));
+ break;
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATETIME:
+ mapping.append(STRING_WITH_LEN("TIMESTAMP"));
+ break;
+ case MYSQL_TYPE_YEAR:
+ mapping.append(STRING_WITH_LEN("CHAR(4) CCSID 1208"));
+ 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
+ {
+ 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 &&
+ get_blob_type_from_length(fieldLength) == MYSQL_TYPE_BLOB)
+ {
+ sprintf(stringBuildBuffer, "LONG VARBINARY ", max(fieldLength, 1));
+ }
+*/
+ else
+ {
+ fieldLength = min(MAX_BLOB_LENGTH, fieldLength);
+ sprintf(stringBuildBuffer, "BLOB(%d)", max(fieldLength, 1));
+ }
+ }
+ mapping.append(stringBuildBuffer);
+ }
+ else
+ {
+ uint16 db2Ccsid = 0; // No override CCSID
+ 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 &&
+ get_blob_type_from_length(fieldLength) == MYSQL_TYPE_BLOB)
+ {
+ 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;
+
+ }
+
+ 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);
+ const char* ZERO_VALUE = "0000-00-00 00:00:00";
+ 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_VALUE,strlen(ZERO_VALUE)) == 0)
+ {
+ getErrTxt(DB2I_ERR_INVALID_COL_VALUE,ZERO_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:
+ {
+ const char* ZERO_VALUE = "0000-00-00";
+ 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_VALUE,strlen(ZERO_VALUE)) == 0)
+ {
+ getErrTxt(DB2I_ERR_INVALID_COL_VALUE,ZERO_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 (data == NULL)
+ {
+ field->val_str(&tempString, (String*)NULL);
+ }
+ else
+ {
+ field->val_str(&tempString, data);
+ }
+ memcpy(db2Buf, tempString.ptr(), 4);
+ }
+ break;
+ case MYSQL_TYPE_BIT:
+ {
+ int bytesToCopy = (db2Field.getByteLengthInRecord()-1) / 8 + 1;
+
+ 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:
+ {
+ DBUG_ASSERT(data == NULL);
+ bytesToStore = ((Field_blob*)field)->get_length();
+ bytesToPad = maxDisplayLength - bytesToStore;
+ ((Field_blob*)field)->get_ptr((uchar**)&dataToStore);
+ }
+ 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);
+
+ 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));
+
+ storeRC = field->store(value);
+ }
+ break;
+ case MYSQL_TYPE_YEAR:
+ {
+ storeRC = field->store(bufPtr, 4, &my_charset_bin);
+ }
+ break;
+ case MYSQL_TYPE_BIT:
+ {
+ uint64 temp= 0;
+ int bytesToCopy= (db2Field.getByteLengthInRecord()-1) / 8 + 1;
+ 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..cb8b4986099
--- /dev/null
+++ b/storage/ibmdb2i/db2i_errors.cc
@@ -0,0 +1,296 @@
+/*
+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"},
+ {"Invalid value '%-.128s' for column '%.192s'"},
+ {"Schema name '%.128s' exceeds maximum length of %d characters"},
+ {"Multiple collations not supported in a single index"},
+ {"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."},
+};
+
+/*
+ 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..9be2364a7b5
--- /dev/null
+++ b/storage/ibmdb2i/db2i_errors.h
@@ -0,0 +1,91 @@
+/*
+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_LAST_ERR = DB2I_ERR_WARN_CREATE_DISCOVER
+};
+
+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
+
+
+#endif
diff --git a/storage/ibmdb2i/db2i_file.cc b/storage/ibmdb2i/db2i_file.cc
new file mode 100644
index 00000000000..2ce78f18d21
--- /dev/null
+++ b/storage/ibmdb2i/db2i_file.cc
@@ -0,0 +1,513 @@
+/*
+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);
+
+ if (fileObjects > 1)
+ {
+ logicalFiles = new db2i_file*[fileObjects - 1];
+ for (int k = 0; k < mysqlTable->keys; 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()->allocateFileDefn(fileDefnSpace,
+ fileDefnHandles,
+ fileObjects,
+ db2LibNameEbcdic,
+ strlen(db2LibNameEbcdic),
+ formatSpace,
+ formatSpaceLen);
+
+ if (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 < mysqlTable->keys; ++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));
+}
+
+
+void db2i_table::filenameToTablename(const char* in, char* out, size_t outlen)
+{
+ if (strchr(in, '#') == NULL)
+ {
+ filename_to_tablename(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 = filename_to_tablename(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 += filename_to_tablename(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..4be6558a95d
--- /dev/null
+++ b/storage/ibmdb2i/db2i_file.h
@@ -0,0 +1,441 @@
+/*
+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);
+ 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;
+ 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
+{
+ enum RowFormats
+ {
+ readOnly = 0,
+ readWrite,
+ maxRowFormats
+ };
+
+public:
+ mutable struct RowFormat
+ {
+ uint16 readRowLen;
+ uint16 readRowNullOffset;
+ uint16 writeRowLen;
+ uint16 writeRowNullOffset;
+ char inited;
+ } formats[maxRowFormats];
+
+ // 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 useFile(FILE_HANDLE instanceHandle,
+ char intent,
+ char commitLevel,
+ const RowFormat** activeFormat) const
+ {
+ DBUG_ENTER("db2i_file::useFile");
+ RowFormat* rowFormat;
+
+ if (intent == QMY_UPDATABLE)
+ rowFormat = &(formats[readWrite]);
+ else if (intent == QMY_READ_ONLY)
+ rowFormat = &(formats[readOnly]);
+ else
+ DBUG_ASSERT(0);
+
+ if (!rowFormat->inited)
+ {
+ int rc;
+ 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:
+ 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..356c837cd00
--- /dev/null
+++ b/storage/ibmdb2i/db2i_ileBridge.cc
@@ -0,0 +1,1331 @@
+/*
+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;
+}
+
+/**
+ 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)
+{
+ // 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;
+ }
+
+ 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));
+
+ 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);
+ }
+
+ 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)
+{
+ NameHandlePair *newPair = (NameHandlePair*)my_malloc(sizeof(NameHandlePair), MYF(MY_WME));
+
+ newPair->next = head;
+ head = newPair;
+
+ strcpy(newPair->name, newname);
+ newPair->handle = newhandle;
+ DBUG_PRINT("db2i_ileBridge", ("Added handle %d for %s", uint32(newhandle), newname));
+}
+
+
+FILE_HANDLE db2i_ileBridge::PreservedHandleList::findAndRemove(const char* fileName)
+{
+ NameHandlePair* current = head;
+ NameHandlePair* prev = NULL;
+
+ while (current)
+ {
+ NameHandlePair* next = current->next;
+ if (strcmp(fileName, current->name) == 0)
+ {
+ FILE_HANDLE tmp = current->handle;
+ 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..7e4e8216cfc
--- /dev/null
+++ b/storage/ibmdb2i/db2i_ileBridge.h
@@ -0,0 +1,488 @@
+/*
+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;
+
+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);
+ 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)
+ {
+ pendingLockedHandles.add(newname, newhandle);
+ }
+
+ /**
+ 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)
+ {
+ return pendingLockedHandles.findAndRemove(name);
+ }
+
+ /**
+ 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.
+ */
+ 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;
+ }
+ 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);
+ FILE_HANDLE findAndRemove(const char* fileName);
+
+ private:
+ struct NameHandlePair
+ {
+ char name[FN_REFLEN];
+ FILE_HANDLE handle;
+ 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..e503bd0e9f1
--- /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;
+
+ closePipe();
+
+ 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..40c88725ef9
--- /dev/null
+++ b/storage/ibmdb2i/db2i_ioBuffers.h
@@ -0,0 +1,411 @@
+/*
+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 size buffer size requested
+ */
+ void allocBuf(uint32 rowLen, uint32 size)
+ {
+ 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;}
+
+ 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;
+ 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, 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..1b6f0bc3968
--- /dev/null
+++ b/storage/ibmdb2i/db2i_misc.h
@@ -0,0 +1,95 @@
+/*
+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);
+}
+
+#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..acae6da3085
--- /dev/null
+++ b/storage/ibmdb2i/db2i_rir.cc
@@ -0,0 +1,441 @@
+/*
+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;
+
+ /*
+ 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;
+
+ if (litDefPtr->DataType == QMY_CHAR || litDefPtr->DataType == QMY_VARCHAR ||
+ (strncmp(fieldCharSet->csname, "utf8", sizeof("utf8")) == 0))
+ {
+ endOfMinPtr = (char*)memchr(tempMinPtr,field->charset()->min_sort_char,tempLen);
+ if (endOfMinPtr)
+ endOfLiteralPtr = tempPtr + (((uint32_t)(endOfMinPtr - tempMinPtr)) *
+ (litDefPtr->DataType == QMY_CHAR || litDefPtr->DataType == QMY_VARCHAR ? 1 : 2));
+ }
+ else
+ {
+ 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 (!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..9a65e41021b
--- /dev/null
+++ b/storage/ibmdb2i/ha_ibmdb2i.cc
@@ -0,0 +1,3171 @@
+/*
+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,
+ "True auto-commit mode.",
+ 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(create_time_columns_as_TOD,
+ 0,
+ "Control how new TIME columns should be defined in DB2. 1=time-of-day (default), 0=duration.",
+ NULL,
+ NULL,
+ TRUE);
+
+static MYSQL_THDVAR_UINT(map_blob_to_varchar,
+ 0,
+ "Control how new TEXT columns should be defined in DB2. 0=CLOB (default), 1=VARCHAR",
+ NULL,
+ NULL,
+ 0,
+ 0,
+ 1,
+ 1);
+
+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);
+
+
+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);
+ 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 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()
+{
+ 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();
+
+ if (!(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);
+
+ dataHandle = bridge()->findAndRemovePreservedHandle(name);
+
+ DBUG_RETURN(0);
+}
+
+
+
+
+int ha_ibmdb2i::close(void)
+{
+ DBUG_ENTER("ha_ibmdb2i::close");
+ int32 rc = 0;
+
+ 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);
+ dataHandle = 0;
+ }
+
+ for (int idx = 0; idx < table_share->keys; ++idx)
+ {
+ if (indexHandles[idx] != 0)
+ {
+ bridge->deallocateFile(indexHandles[idx], FALSE);
+ }
+ }
+
+ cleanupBuffers();
+
+ free_share(share);
+
+ 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;
+
+ bool fileHandleNeedsRelease = false;
+
+ if (!activeHandle)
+ {
+ rc = useDataFile(QMY_UPDATABLE);
+ if (rc) DBUG_RETURN(rc);
+ fileHandleNeedsRelease = true;
+ }
+
+ if (!outstanding_start_bulk_insert)
+ prepWriteBuffer(1);
+
+ if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
+ table->timestamp_field->set_time();
+
+ char* writeBuffer = activeWriteBuf->addRow();
+ rc = prepareRowForWrite(writeBuffer,
+ writeBuffer+activeFormat->writeRowNullOffset,
+ 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+activeFormat->writeRowNullOffset,
+ 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(accessIntent, idx);
+ if (accessIntent != QMY_READ_ONLY)
+ prepWriteBuffer(1);
+
+ 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);
+ if (unlikely(rc)) DBUG_RETURN(rc);
+
+ DBUG_ASSERT(activeReadBuf);
+
+ keyBuf.allocBuf(activeFormat->readRowLen, activeFormat->readRowLen);
+ keyBuf.zeroBuf();
+
+ char* db2KeyBufPtr = keyBuf.ptr();
+ char* nullKeyMap = db2KeyBufPtr + activeFormat->readRowNullOffset;
+
+ 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);
+
+ 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);
+
+ 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(accessIntent);
+
+ if (rc == 0)
+ {
+ if (accessIntent != QMY_READ_ONLY)
+ prepWriteBuffer(1);
+ rc = prepReadBuffer(rowsToBlockOnRead);
+
+ if (rc == 0 && 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);
+
+ 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 + activeFormat->readRowNullOffset, 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(),
+ getCommitLevel(),
+ FALSE,
+ FALSE,
+ 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)
+ {
+ 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));
+
+ }
+
+
+ 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);
+ if (savedHandle)
+ bridge->deallocateFile(savedHandle, TRUE);
+ }
+
+ 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)
+ {
+ 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 = (THDVAR(thd, create_time_columns_as_TOD) ? TIME_OF_DAY : DURATION);
+ enum_BlobMapping blobMapping = (enum_BlobMapping)(THDVAR(thd, map_blob_to_varchar));
+
+ 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))
+ 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(") "));
+ }
+
+ }
+ }
+
+ String primaryKeyQuery;
+ primaryKeyQuery.length(0);
+ if (table_arg->s->primary_key != MAX_KEY && !isTemporary)
+ {
+ KEY& curKey = table_arg->key_info[table_arg->s->primary_key];
+ primaryKeyQuery.append(STRING_WITH_LEN(" PRIMARY KEY( "));
+ for (int j = 0; j < curKey.key_parts; ++j)
+ {
+ if (j != 0)
+ {
+ primaryKeyQuery.append( STRING_WITH_LEN(" , ") );
+ }
+ Field* field = curKey.key_part[j].field;
+ convertMySQLNameToDB2Name(field->field_name, colName, sizeof(colName));
+ primaryKeyQuery.append(colName);
+ rc = updateAssociatedSortSequence(field,
+ &fileSortSequenceType,
+ fileSortSequence,
+ fileSortSequenceLibrary);
+ if (rc) DBUG_RETURN (rc);
+ }
+ primaryKeyQuery.append(STRING_WITH_LEN(" ) "));
+ }
+
+ bool needAlterForPrimaryKey = FALSE;
+ if ((fileSortSequence[0] != '*') && (fileSortSequence[0] != 'Q')) // An ICU sort sequence
+ {
+ needAlterForPrimaryKey = TRUE;
+ }
+ else
+ {
+ if (primaryKeyQuery.length() > 0)
+ {
+ query.append(STRING_WITH_LEN(" , "));
+ query.append(primaryKeyQuery);
+ }
+ }
+
+ 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);
+
+
+
+ if (needAlterForPrimaryKey == TRUE && !isTemporary)
+ {
+ rc = buildCreateIndexStatement(sqlStream,
+ table_arg->key_info[table_arg->s->primary_key],
+ true,
+ libName,
+ fileName);
+ if (rc) DBUG_RETURN (rc);
+ }
+
+ uint i = 0;
+
+ 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(QMY_UPDATABLE);
+ if (last_start_bulk_insert_rc == 0)
+ prepWriteBuffer(rows);
+ }
+
+ 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)
+{
+ DBUG_ENTER("ha_ibmdb2i::prepReadBuffer");
+ DBUG_ASSERT((accessIntent == QMY_READ_ONLY || accessIntent == QMY_UPDATABLE) && rowsToRead > 0);
+
+ int rc = 0;
+
+ if (lobFieldsRequested())
+ {
+ forceSingleRowRead = true;
+ rowsToRead = 1;
+ }
+
+ rowsToRead = min(stats.records+1,min(rowsToRead, DEFAULT_MAX_ROWS_TO_BUFFER));
+
+ THD* thd = ha_thd();
+
+ uint bufSize = min((activeFormat->readRowLen * rowsToRead), THDVAR(thd, max_read_buffer_size));
+ multiRowReadBuf.allocBuf(activeFormat->readRowLen, bufSize);
+ activeReadBuf = &multiRowReadBuf;
+
+ if (db2Table->hasBlobs())
+ {
+ if (!blobReadBuffers)
+ blobReadBuffers = new BlobCollection(db2Table, THDVAR(thd, lob_alloc_size));
+ rc = prepareReadBufferForLobs();
+ if (rc) DBUG_RETURN(rc);
+ }
+ activeReadBuf->update(accessIntent, &releaseRowNeeded, getCommitLevel(thd));
+
+ DBUG_RETURN(rc);
+}
+
+
+void ha_ibmdb2i::prepWriteBuffer(ha_rows rowsToWrite)
+{
+ DBUG_ENTER("ha_ibmdb2i::prepWriteBuffer");
+ DBUG_ASSERT(accessIntent == QMY_UPDATABLE && rowsToWrite > 0);
+
+ rowsToWrite = min(rowsToWrite, DEFAULT_MAX_ROWS_TO_BUFFER);
+
+ uint bufSize = min((activeFormat->writeRowLen * rowsToWrite), THDVAR(ha_thd(), max_write_buffer_size));
+ multiRowWriteBuf.allocBuf(activeFormat->writeRowLen, bufSize);
+ activeWriteBuf = &multiRowWriteBuf;
+
+ if (!blobWriteBuffers && db2Table->hasBlobs())
+ {
+ blobWriteBuffers = new ValidatedPointer<char>[db2Table->getBlobCount()];
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+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 + activeFormat->writeRowNullOffset,
+ 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(),
+ activeFormat->readRowLen);
+ 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,&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(char intent, 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)
+ {
+ rc = db2Table->indexFile(idx)->useFile(indexHandles[idx],
+ intent,
+ getCommitLevel(),
+ &activeFormat);
+
+ 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(create_time_columns_as_TOD),
+ MYSQL_SYSVAR(assume_exclusive_use),
+ MYSQL_SYSVAR(map_blob_to_varchar),
+ MYSQL_SYSVAR(create_index_option),
+ MYSQL_SYSVAR(discovery_mode),
+ 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_PROPRIETARY,
+ 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..321951bfe3c
--- /dev/null
+++ b/storage/ibmdb2i/ha_ibmdb2i.h
@@ -0,0 +1,727 @@
+/*
+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.
+*/
+typedef struct st_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;
+
+} IBMDB2I_SHARE;
+
+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;
+
+ // Pointer to a definition of the layout of the row buffer for the file
+ // described by activeHandle
+ const db2i_file::RowFormat* activeFormat;
+
+ // 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;
+
+
+ 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;
+
+ 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
+ };
+
+ 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(char intent)
+ {
+ DBUG_ENTER("ha_ibmdb2i::useDataFile");
+ DBUG_PRINT("ha_ibmdb2i::useDataFile", ("Intent: %d", intent));
+
+ 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))
+ {
+ rc = db2Table->dataFile()->useFile(dataHandle,
+ intent,
+ getCommitLevel(),
+ &activeFormat);
+
+ 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(char intent, 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);
+
+ 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 (likely(rc == 0))
+ {
+ rrnAssocHandle = activeHandle;
+ rc = mungeDB2row(destination, row, row+activeFormat->readRowNullOffset, 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->useFile(handle, intent, getCommitLevel(), &activeFormat);
+ if (likely(rc == 0))
+ {
+ activeHandle = handle;
+ bumpInUseCounter(1);
+ }
+
+ DBUG_RETURN(rc);
+ }
+
+ int prepReadBuffer(ha_rows rowsToRead);
+ void prepWriteBuffer(ha_rows rowsToWrite);
+
+ 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])