Commit 9c3c97db authored by Timothy Smith's avatar Timothy Smith

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.
parent 7da390e3
...@@ -302,7 +302,9 @@ AC_DEFUN([MYSQL_CONFIGURE_PLUGINS],[ ...@@ -302,7 +302,9 @@ AC_DEFUN([MYSQL_CONFIGURE_PLUGINS],[
_MYSQL_CONFIGURE_PLUGINS(m4_bpatsubst(__mysql_plugin_list__, :, [,])) _MYSQL_CONFIGURE_PLUGINS(m4_bpatsubst(__mysql_plugin_list__, :, [,]))
_MYSQL_EMIT_PLUGIN_ACTIONS(m4_bpatsubst(__mysql_plugin_list__, :, [,])) _MYSQL_EMIT_PLUGIN_ACTIONS(m4_bpatsubst(__mysql_plugin_list__, :, [,]))
AC_SUBST([mysql_se_dirs]) AC_SUBST([mysql_se_dirs])
AC_SUBST([mysql_se_distdirs])
AC_SUBST([mysql_pg_dirs]) AC_SUBST([mysql_pg_dirs])
AC_SUBST([mysql_pg_distdirs])
AC_SUBST([mysql_se_unittest_dirs]) AC_SUBST([mysql_se_unittest_dirs])
AC_SUBST([mysql_pg_unittest_dirs]) AC_SUBST([mysql_pg_unittest_dirs])
AC_SUBST([condition_dependent_plugin_modules]) AC_SUBST([condition_dependent_plugin_modules])
...@@ -354,6 +356,24 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[ ...@@ -354,6 +356,24 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[
fi fi
AC_MSG_RESULT([no]) 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],[ m4_ifdef([$9],[
if test "X[$with_plugin_]$2" = Xno; then if test "X[$with_plugin_]$2" = Xno; then
AC_MSG_RESULT([error]) AC_MSG_RESULT([error])
...@@ -372,6 +392,8 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[ ...@@ -372,6 +392,8 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[
;; ;;
esac esac
]) ])
if test "X[$with_plugin_]$2" = Xno; then if test "X[$with_plugin_]$2" = Xno; then
AC_MSG_RESULT([no]) AC_MSG_RESULT([no])
else else
...@@ -448,28 +470,36 @@ dnl Although this is "pretty", it breaks libmysqld build ...@@ -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, [^.+[/$]], [\&])" condition_dependent_plugin_includes="$condition_dependent_plugin_includes -I[\$(top_srcdir)]/$6/m4_bregexp($11, [^.+[/$]], [\&])"
]) ])
fi 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 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
])
]) ])
]) ])
......
...@@ -22,7 +22,7 @@ AUTOMAKE_OPTIONS = foreign ...@@ -22,7 +22,7 @@ AUTOMAKE_OPTIONS = foreign
EXTRA_DIST = fulltext/configure.in EXTRA_DIST = fulltext/configure.in
SUBDIRS = @mysql_pg_dirs@ SUBDIRS = @mysql_pg_dirs@
DIST_SUBDIRS = daemon_example fulltext DIST_SUBDIRS = @mysql_pg_distdirs@
# Don't update the files from bitkeeper # Don't update the files from bitkeeper
%::SCCS/s.% %::SCCS/s.%
...@@ -20,6 +20,7 @@ AUTOMAKE_OPTIONS = foreign ...@@ -20,6 +20,7 @@ AUTOMAKE_OPTIONS = foreign
# These are built from source in the Docs directory # These are built from source in the Docs directory
EXTRA_DIST = EXTRA_DIST =
SUBDIRS = @mysql_se_dirs@ SUBDIRS = @mysql_se_dirs@
DIST_SUBDIRS = @mysql_se_distdirs@
# Don't update the files from bitkeeper # Don't update the files from bitkeeper
%::SCCS/s.% %::SCCS/s.%
# 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)
#
# 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.%
/*
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();
}
/*
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
/*
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;
}
/*
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
/*
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);
}
/*
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
/*
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);
}
/*
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;
}
/*
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);
}
/*
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
/*
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);
}
/*
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
/*
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
/*
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
/*
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;
}
/*
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
/*
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;
}
/*
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 */
};
/*
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
/*
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;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
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);
}
/*
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
/*
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;
}
/*
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
/*
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
/*
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;
/*
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));
}
}
};
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])
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment