This commit is contained in:
Vladislav Vaintroub 2010-11-12 02:38:19 +01:00
commit 5a8b8b6493
56 changed files with 76 additions and 19809 deletions

View File

@ -75,4 +75,40 @@ CALL p1 ();
ERROR HY000: Trigger does not exist
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Bug#54375: Error in stored procedure leaves connection
# in different default schema
#
SET @@SQL_MODE = 'STRICT_ALL_TABLES';
DROP DATABASE IF EXISTS db1;
CREATE DATABASE db1;
USE db1;
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (c1 int NOT NULL PRIMARY KEY);
INSERT INTO t1 VALUES (1);
CREATE FUNCTION f1 (
some_value int
)
RETURNS smallint
DETERMINISTIC
BEGIN
INSERT INTO t1 SET c1 = some_value;
RETURN(LAST_INSERT_ID());
END$$
DROP DATABASE IF EXISTS db2;
CREATE DATABASE db2;
USE db2;
SELECT DATABASE();
DATABASE()
db2
SELECT db1.f1(1);
ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
SELECT DATABASE();
DATABASE()
db2
USE test;
DROP FUNCTION db1.f1;
DROP TABLE db1.t1;
DROP DATABASE db1;
DROP DATABASE db2;
End of 5.1 tests

View File

@ -1,20 +0,0 @@
# Check for IBM i 6.1 or later
--disable_query_log
system uname -rv > $MYSQLTEST_VARDIR/tmp/version;
--disable_warnings
drop table if exists uname_vr;
--enable_warnings
create temporary table uname_vr (r int, v int);
--disable_warnings
eval LOAD DATA INFILE "$MYSQLTEST_VARDIR/tmp/version" into table uname_vr fields terminated by ' ';
--enable_warnings
let $ok = `select count(*) from uname_vr where v = 5 and r = 4`;
drop table uname_vr;
remove_file $MYSQLTEST_VARDIR/tmp/version;
--enable_query_log
if (!$ok)
{
skip "Need IBM i 5.4 or later";
}

View File

@ -1,20 +0,0 @@
# Check for IBM i 6.1 or later
--disable_query_log
system uname -rv > $MYSQLTEST_VARDIR/tmp/version;
--disable_warnings
drop table if exists uname_vr;
--enable_warnings
create temporary table uname_vr (r int, v int);
--disable_warnings
eval LOAD DATA INFILE "$MYSQLTEST_VARDIR/tmp/version" into table uname_vr fields terminated by ' ';
--enable_warnings
let $ok = `select count(*) from uname_vr where v > 5`;
drop table uname_vr;
remove_file $MYSQLTEST_VARDIR/tmp/version;
--enable_query_log
if (!$ok)
{
skip "Need IBM i 6.1 or later";
}

View File

@ -1,6 +0,0 @@
if (!`SELECT count(*) FROM information_schema.engines WHERE
(support = 'YES' OR support = 'DEFAULT') AND
engine = 'ibmdb2i'`)
{
skip Need ibmdb2i engine;
}

View File

@ -1,11 +0,0 @@
create schema `A12345_@$#`;
create table `A12345_@$#`.t1 (i int) engine=ibmdb2i;
show create table `A12345_@$#`.t1;
Table Create Table
t1 CREATE TABLE `t1` (
`i` int(11) DEFAULT NULL
) ENGINE=IBMDB2I DEFAULT CHARSET=latin1
select * from `A12345_@$#`.t1;
i
drop table `A12345_@$#`.t1;
drop schema `A12345_@$#`;

View File

@ -1,4 +0,0 @@
create table t1 (c char(10) collate utf8_swedish_ci, index(c)) engine=ibmdb2i;
drop table t1;
create table t1 (c char(10) collate ucs2_swedish_ci, index(c)) engine=ibmdb2i;
drop table t1;

View File

@ -1,4 +0,0 @@
create table t1 (c char(1) character set armscii8) engine=ibmdb2i;
ERROR HY000: Can't create table 'test.t1' (errno: 2504)
create table t1 (c char(1) character set eucjpms ) engine=ibmdb2i;
ERROR HY000: Can't create table 'test.t1' (errno: 2504)

View File

@ -1,18 +0,0 @@
create table ABC (i int) engine=ibmdb2i;
drop table ABC;
create table `1234567890ABC` (i int) engine=ibmdb2i;
drop table `1234567890ABC`;
create table `!@#$%` (i int) engine=ibmdb2i;
drop table `!@#$%`;
create table `ABCD#########` (i int) engine=ibmdb2i;
drop table `ABCD#########`;
create table `_` (i int) engine=ibmdb2i;
drop table `_`;
create table `abc##def` (i int) engine=ibmdb2i;
drop table `abc##def`;
set names utf8;
create table İ (s1 int) engine=ibmdb2i;
drop table İ;
create table İİ (s1 int) engine=ibmdb2i;
drop table İİ;
set names latin1;

View File

@ -1,33 +0,0 @@
drop table if exists t1;
create table t1 (c char(10), index(c)) collate ucs2_czech_ci engine=ibmdb2i;
insert into t1 values ("ch"),("h"),("i");
select * from t1 order by c;
c
h
ch
i
drop table t1;
create table t1 (c char(10), index(c)) collate utf8_czech_ci engine=ibmdb2i;
insert into t1 values ("ch"),("h"),("i");
select * from t1 order by c;
c
h
ch
i
drop table t1;
create table t1 (c char(10), index(c)) collate ucs2_danish_ci engine=ibmdb2i;
insert into t1 values("abc"),("abcd"),("aaaa");
select c from t1 order by c;
c
abc
abcd
aaaa
drop table t1;
create table t1 (c char(10), index(c)) collate utf8_danish_ci engine=ibmdb2i;
insert into t1 values("abc"),("abcd"),("aaaa");
select c from t1 order by c;
c
abc
abcd
aaaa
drop table t1;

View File

@ -1,7 +0,0 @@
drop table if exists t1;
create table t1 (c char(10), index(c)) charset macce engine=ibmdb2i;
insert into t1 values ("test");
select * from t1 order by c;
c
test
drop table t1;

View File

@ -1,20 +0,0 @@
set ibmdb2i_create_index_option=1;
drop schema if exists test1;
create schema test1;
use test1;
CREATE TABLE t1 (f int primary key, index(f)) engine=ibmdb2i;
drop table t1;
CREATE TABLE t1 (f char(10) collate utf8_bin primary key, index(f)) engine=ibmdb2i;
drop table t1;
CREATE TABLE t1 (f char(10) collate latin1_swedish_ci primary key, index(f)) engine=ibmdb2i;
drop table t1;
CREATE TABLE t1 (f char(10) collate latin1_swedish_ci primary key, i int, index i(i,f)) engine=ibmdb2i;
drop table t1;
create table fd (SQSSEQ CHAR(10)) engine=ibmdb2i;
select * from fd;
SQSSEQ
*HEX
*HEX
*HEX
*HEX
drop table fd;

View File

@ -1,9 +0,0 @@
create table ABC (i int) engine=ibmdb2i;
insert into ABC values(1);
create table abc (i int) engine=ibmdb2i;
insert into abc values (2);
select * from ABC;
i
1
drop table ABC;
drop table abc;

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +0,0 @@
source suite/ibmdb2i/include/have_ibmdb2i.inc;
source include/have_case_sensitive_file_system.inc;
create schema `A12345_@$#`;
create table `A12345_@$#`.t1 (i int) engine=ibmdb2i;
show create table `A12345_@$#`.t1;
select * from `A12345_@$#`.t1;
drop table `A12345_@$#`.t1;
drop schema `A12345_@$#`;

View File

@ -1,9 +0,0 @@
source suite/ibmdb2i/include/have_ibmdb2i.inc;
source suite/ibmdb2i/include/have_i61.inc;
create table t1 (c char(10) collate utf8_swedish_ci, index(c)) engine=ibmdb2i;
drop table t1;
create table t1 (c char(10) collate ucs2_swedish_ci, index(c)) engine=ibmdb2i;
drop table t1;

View File

@ -1,8 +0,0 @@
--source suite/ibmdb2i/include/have_ibmdb2i.inc
--source suite/ibmdb2i/include/have_i54.inc
--error 1005
create table t1 (c char(1) character set armscii8) engine=ibmdb2i;
--error 1005
create table t1 (c char(1) character set eucjpms ) engine=ibmdb2i;

View File

@ -1,28 +0,0 @@
source suite/ibmdb2i/include/have_ibmdb2i.inc;
# Test RCDFMT generation for a variety of kinds of table names
create table ABC (i int) engine=ibmdb2i;
drop table ABC;
create table `1234567890ABC` (i int) engine=ibmdb2i;
drop table `1234567890ABC`;
create table `!@#$%` (i int) engine=ibmdb2i;
drop table `!@#$%`;
create table `ABCD#########` (i int) engine=ibmdb2i;
drop table `ABCD#########`;
create table `_` (i int) engine=ibmdb2i;
drop table `_`;
create table `abc##def` (i int) engine=ibmdb2i;
drop table `abc##def`;
set names utf8;
create table İ (s1 int) engine=ibmdb2i;
drop table İ;
create table İİ (s1 int) engine=ibmdb2i;
drop table İİ;
set names latin1;

View File

@ -1,26 +0,0 @@
source suite/ibmdb2i/include/have_ibmdb2i.inc;
source suite/ibmdb2i/include/have_i61.inc;
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (c char(10), index(c)) collate ucs2_czech_ci engine=ibmdb2i;
insert into t1 values ("ch"),("h"),("i");
select * from t1 order by c;
drop table t1;
create table t1 (c char(10), index(c)) collate utf8_czech_ci engine=ibmdb2i;
insert into t1 values ("ch"),("h"),("i");
select * from t1 order by c;
drop table t1;
create table t1 (c char(10), index(c)) collate ucs2_danish_ci engine=ibmdb2i;
insert into t1 values("abc"),("abcd"),("aaaa");
select c from t1 order by c;
drop table t1;
create table t1 (c char(10), index(c)) collate utf8_danish_ci engine=ibmdb2i;
insert into t1 values("abc"),("abcd"),("aaaa");
select c from t1 order by c;
drop table t1;

View File

@ -1,11 +0,0 @@
source suite/ibmdb2i/include/have_ibmdb2i.inc;
source suite/ibmdb2i/include/have_i61.inc;
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (c char(10), index(c)) charset macce engine=ibmdb2i;
insert into t1 values ("test");
select * from t1 order by c;
drop table t1;

View File

@ -1,47 +0,0 @@
source suite/ibmdb2i/include/have_ibmdb2i.inc;
# Confirm that ibmdb2i_create_index_option causes additional *HEX sorted indexes to be created for all non-binary keys.
set ibmdb2i_create_index_option=1;
--disable_warnings
drop schema if exists test1;
create schema test1;
use test1;
--enable_warnings
--disable_abort_on_error
--error 0,255
exec system "DLTF QGPL/FDOUT" > /dev/null;
--enable_abort_on_error
#No additional index because no string fields in key
CREATE TABLE t1 (f int primary key, index(f)) engine=ibmdb2i;
--error 255
exec system "DSPFD FILE(\"test1\"/PRIM0001) TYPE(*SEQ) OUTPUT(*OUTFILE) OUTFILE(QGPL/FDOUT) OUTMBR(*FIRST *ADD)" > /dev/null;
--error 255
exec system "DSPFD FILE(\"test1\"/\"f___H_t1\") TYPE(*SEQ) OUTPUT(*OUTFILE) OUTFILE(QGPL/FDOUT) OUTMBR(*FIRST *ADD)" > /dev/null;
drop table t1;
#No additional index because binary sorting
CREATE TABLE t1 (f char(10) collate utf8_bin primary key, index(f)) engine=ibmdb2i;
--error 255
exec system "DSPFD FILE(\"test1\"/PRIM0001) TYPE(*SEQ) OUTPUT(*OUTFILE) OUTFILE(QGPL/FDOUT) OUTMBR(*FIRST *ADD)" > /dev/null;
--error 255
exec system "DSPFD FILE(\"test1\"/\"f___H_t1\") TYPE(*SEQ) OUTPUT(*OUTFILE) OUTFILE(QGPL/FDOUT) OUTMBR(*FIRST *ADD)" > /dev/null;
drop table t1;
CREATE TABLE t1 (f char(10) collate latin1_swedish_ci primary key, index(f)) engine=ibmdb2i;
exec system "DSPFD FILE(\"test1\"/PRIM0001) TYPE(*SEQ) OUTPUT(*OUTFILE) OUTFILE(QGPL/FDOUT) OUTMBR(*FIRST *ADD)" > /dev/null;
exec system "DSPFD FILE(\"test1\"/\"f___H_t1\") TYPE(*SEQ) OUTPUT(*OUTFILE) OUTFILE(QGPL/FDOUT) OUTMBR(*FIRST *ADD)" > /dev/null;
drop table t1;
CREATE TABLE t1 (f char(10) collate latin1_swedish_ci primary key, i int, index i(i,f)) engine=ibmdb2i;
exec system "DSPFD FILE(\"test1\"/PRIM0001) TYPE(*SEQ) OUTPUT(*OUTFILE) OUTFILE(QGPL/FDOUT) OUTMBR(*FIRST *ADD)" > /dev/null;
exec system "DSPFD FILE(\"test1\"/\"i___H_t1\") TYPE(*SEQ) OUTPUT(*OUTFILE) OUTFILE(QGPL/FDOUT) OUTMBR(*FIRST *ADD)" > /dev/null;
drop table t1;
create table fd (SQSSEQ CHAR(10)) engine=ibmdb2i;
system system "CPYF FROMFILE(QGPL/FDOUT) TOFILE(\"test1\"/\"fd\") mbropt(*replace) fmtopt(*drop *map)" > /dev/null;
select * from fd;
drop table fd;

View File

@ -1,10 +0,0 @@
source suite/ibmdb2i/include/have_ibmdb2i.inc;
source include/have_case_sensitive_file_system.inc;
create table ABC (i int) engine=ibmdb2i;
insert into ABC values(1);
create table abc (i int) engine=ibmdb2i;
insert into abc values (2);
select * from ABC;
drop table ABC;
drop table abc;

View File

@ -1,44 +0,0 @@
source suite/ibmdb2i/include/have_ibmdb2i.inc;
source suite/ibmdb2i/include/have_i61.inc;
--disable_warnings
drop table if exists t1, ffd, fd;
--enable_warnings
--disable_abort_on_error
--error 0,255
exec system "DLTF QGPL/FFDOUT" > /dev/null;
--error 0,255
exec system "DLTF QGPL/FDOUT" > /dev/null;
--enable_abort_on_error
let $count= query_get_value(select count(*) from information_schema.COLLATIONS where COLLATION_NAME <> "binary", count(*),1);
while ($count)
{
let $collation = query_get_value(select COLLATION_NAME from information_schema.COLLATIONS where COLLATION_NAME <> "binary" order by COLLATION_NAME desc, COLLATION_NAME, $count);
error 0,1005,2504,2028;
eval CREATE TABLE t1 ($collation integer, c char(10), v varchar(20), index(c), index(v)) collate $collation engine=ibmdb2i;
if (!$mysql_errno)
{
insert into t1 (c,v) values ("abc","def"),("abcd", "def"),("abcde","defg"),("aaaa","bbbb");
insert into t1 select * from t1;
explain select c,v from t1 force index(c) where c like "ab%";
explain select c,v from t1 force index(v) where v like "de%";
drop table t1;
eval create table t1 ($collation char(10) primary key) collate $collation engine=ibmdb2i;
system system "DSPFFD FILE(\"test\"/\"t1\") OUTPUT(*OUTFILE) OUTFILE(QGPL/FFDOUT) OUTMBR(*FIRST *ADD)" > /dev/null;
system system "DSPFD FILE(\"test\"/\"t1\") TYPE(*SEQ) OUTPUT(*OUTFILE) OUTFILE(QGPL/FDOUT) OUTMBR(*FIRST *ADD)" > /dev/null;
drop table t1;
}
dec $count;
}
create table ffd (WHCHD1 CHAR(20), WHCSID decimal(5,0)) engine=ibmdb2i;
system system "CPYF FROMFILE(QGPL/FFDOUT) TOFILE(\"test\"/\"ffd\") mbropt(*replace) fmtopt(*drop *map)" > /dev/null;
create table fd (SQSSEQ CHAR(10)) engine=ibmdb2i;
system system "CPYF FROMFILE(QGPL/FDOUT) TOFILE(\"test\"/\"fd\") mbropt(*replace) fmtopt(*drop *map)" > /dev/null;
create temporary table intermed (row integer key auto_increment, cs char(30), ccsid integer);
insert into intermed (cs, ccsid) select * from ffd;
create temporary table intermed2 (row integer key auto_increment, srtseq char(10));
insert into intermed2 (srtseq) select * from fd;
select ccsid, cs, srtseq from intermed inner join intermed2 on intermed.row = intermed2.row;
drop table ffd, fd;

View File

@ -101,4 +101,41 @@ CALL p1 ();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Bug#54375: Error in stored procedure leaves connection
--echo # in different default schema
--echo #
--disable_warnings
SET @@SQL_MODE = 'STRICT_ALL_TABLES';
DROP DATABASE IF EXISTS db1;
CREATE DATABASE db1;
USE db1;
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (c1 int NOT NULL PRIMARY KEY);
INSERT INTO t1 VALUES (1);
DELIMITER $$;
CREATE FUNCTION f1 (
some_value int
)
RETURNS smallint
DETERMINISTIC
BEGIN
INSERT INTO t1 SET c1 = some_value;
RETURN(LAST_INSERT_ID());
END$$
DELIMITER ;$$
DROP DATABASE IF EXISTS db2;
CREATE DATABASE db2;
--enable_warnings
USE db2;
SELECT DATABASE();
--error ER_DUP_ENTRY
SELECT db1.f1(1);
SELECT DATABASE();
USE test;
DROP FUNCTION db1.f1;
DROP TABLE db1.t1;
DROP DATABASE db1;
DROP DATABASE db2;
--echo End of 5.1 tests

View File

@ -1510,7 +1510,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
If the DB has changed, the pointer has changed too, but the
original thd->db will then have been freed
*/
if (cur_db_changed && !thd->killed)
if (cur_db_changed && thd->killed != THD::KILL_CONNECTION)
{
/*
Force switching back to the saved current database, because it may be

View File

@ -1,39 +0,0 @@
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
#
# 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
CHECK_INCLUDE_FILES(qlgusr.h HAVE_PASE_ENVIRONMENT)
IF(HAVE_PASE_ENVIRONMENT)
INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
${CMAKE_SOURCE_DIR}/regex
${CMAKE_SOURCE_DIR}/sql
${CMAKE_CURRENT_SOURCE_DIR}
/afs/rchland.ibm.com/lande/shadow/dev2000/osxpf/v5r4m0f.xpf/cur/cmvc/base.pgm/my.xpf/apis
/afs/rchland.ibm.com/lande/shadow/dev2000/osxpf/v5r4m0.xpf/bld/cmvc/base.pgm/lg.xpf
/afs/rchland.ibm.com/lande/shadow/dev2000/osxpf/v5r4m0.xpf/bld/cmvc/base.pgm/tq.xpf
)
SET (IBMDB2I_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)
MYSQL_ADD_PLUGIN(ibmdb2i ${IBMDB2I_SOURCES} STORAGE_ENGINE LINK_LIBRARIES iconv)
ENDIF(HAVE_PASE_ENVIRONMENT)

View File

@ -1,52 +0,0 @@
#
# 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_iconv.h db2i_myconv.h db2i_safeString.h db2i_sqlStatementStream.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_CXXFLAGS) -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_CXXFLAGS)
libibmdb2i_a_CFLAGS = $(AM_CFLAGS)
libibmdb2i_a_SOURCES= $(ha_ibmdb2i_la_SOURCES)
EXTRA_DIST = CMakeLists.txt plug.in

View File

@ -1,107 +0,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_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();
}

View File

@ -1,151 +0,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_BLOBCOLLECTION_H
#define DB2I_BLOBCOLLECTION_H
#include "db2i_global.h"
#include "db2i_file.h"
/**
@class ProtectedBuffer
@brief Implements memory management for (optionally) protected buffers.
Buffers created with the protection option will have a guard page set on the
page following requested allocation size. The side effect is that the actual
allocation is up to 2*4096-1 bytes larger than the size requested by the
using code.
*/
class ProtectedBuffer
{
public:
ProtectedBuffer() : protectBuf(false)
{;}
void Malloc(size_t size, bool protect = false)
{
protectBuf = protect;
bufptr.alloc(size + (protectBuf ? 0x1fff : 0x0));
if ((void*)bufptr != NULL)
{
len = size;
if (protectBuf)
mprotect(protectedPage(), 0x1000, PROT_NONE);
#ifndef DBUG_OFF
// Prevents a problem with DBUG_PRINT over-reading in recent versions of
// MySQL
*((char*)protectedPage()-1) = 0;
#endif
}
}
void Free()
{
if ((void*)bufptr != NULL)
{
if (protectBuf)
mprotect(protectedPage(), 0x1000, PROT_READ | PROT_WRITE);
bufptr.dealloc();
}
}
~ProtectedBuffer()
{
Free();
}
ValidatedPointer<char>& ptr() {return bufptr;}
bool isProtected() const {return protectBuf;}
size_t allocLen() const {return len;}
private:
void* protectedPage()
{
return (void*)(((address64_t)(void*)bufptr + len + 0x1000) & ~0xfff);
}
ValidatedPointer<char> bufptr;
size_t len;
bool protectBuf;
};
/**
@class BlobCollection
@brief Manages memory allocation for reading blobs associated with a table.
Allocations are done on-demand and are protected with a guard page if less
than the max possible size is allocated.
*/
class BlobCollection
{
public:
BlobCollection(db2i_table* db2Table, uint32 defaultAllocSize) :
defaultAllocation(defaultAllocSize), table(db2Table)
{
buffers = new ProtectedBuffer[table->getBlobCount()];
}
~BlobCollection()
{
delete[] buffers;
}
ValidatedPointer<char>& getBufferPtr(int fieldIndex)
{
int blobIndex = table->getBlobIdFromField(fieldIndex);
if ((char*)buffers[blobIndex].ptr() == NULL)
generateBuffer(fieldIndex);
return buffers[blobIndex].ptr();
}
ValidatedPointer<char>& reallocBuffer(int fieldIndex, size_t size);
private:
uint32 getSizeToAllocate(int fieldIndex, bool& shouldProtect);
void generateBuffer(int fieldIndex);
db2i_table* table; // The table being read
ProtectedBuffer* buffers; // The buffers
uint32 defaultAllocation;
/* The default size to use when first allocating a buffer */
};
#endif

View File

@ -1,826 +0,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_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
{
uint32 direction; // These are uint32s to avoid garbage data in the key from compiler padding
uint32 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));
my_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));
my_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);
my_hash_free(&textDescMapHash);
pthread_mutex_destroy(&iconvMapHashMutex);
my_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 (strcmp("1148", in) == 0)
{
strcpy(out, "IBM-1148");
DBUG_RETURN(0);
}
else if (unlikely(strcmp("1153", in) == 0))
{
strcpy(out, "IBM-1153");
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))
{
if (arguments->base.result.s_int32.r_int32 == Qlg_InDescriptorNotFound)
{
DBUG_RETURN(DB2I_ERR_UNSUPP_CHARSET);
}
else
{
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 (strcmp("macce", inDescOverride) == 0)
{
strcpy(outDesc,"IBM-1282");
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 *) my_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 (unlikely(rc))
{
if (rc == DB2I_ERR_UNSUPP_CHARSET)
getErrTxt(DB2I_ERR_UNSUPP_CHARSET, parmIANADesc);
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);
}
else if ((inCcsid == 1250) && (inEncodingScheme == 0x1100))
{
*outCcsid = 1153;
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 (unlikely(rc))
{
if (rc == DB2I_ERR_UNSUPP_CHARSET)
getErrTxt(DB2I_ERR_UNSUPP_CHARSET, mysqlCSName);
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 (unlikely(rc))
{
if (rc == DB2I_ERR_UNSUPP_CHARSET)
getErrTxt(DB2I_ERR_UNSUPP_CHARSET, mysqlCSName);
DBUG_RETURN(rc);
}
/* Call iconv to open the conversion. */
if (direction == toDB2)
{
newConversion = iconv_open(db2Aix41Desc, mysqlAix41Desc);
}
else
{
newConversion = iconv_open(mysqlAix41Desc, db2Aix41Desc);
}
if (unlikely(newConversion == (iconv_t) -1))
{
getErrTxt(DB2I_ERR_UNSUPP_CHARSET, mysqlCSName);
DBUG_RETURN(DB2I_ERR_UNSUPP_CHARSET);
}
/* Insert the new conversion into the cache. */
IconvMap* mapping = (IconvMap*)alloc_root(&iconvMapMemroot, sizeof(IconvMap));
if (!mapping)
{
my_error(ER_OUTOFMEMORY, MYF(0), sizeof(IconvMap));
DBUG_RETURN( HA_ERR_OUT_OF_MEM);
}
memcpy(&(mapping->hashKey), hashKey, sizeof(mapping->hashKey));
mapping->iconvDesc = newConversion;
pthread_mutex_lock(&iconvMapHashMutex);
my_hash_insert(&iconvMapHash, (const uchar*)mapping);
pthread_mutex_unlock(&iconvMapHashMutex);
DBUG_RETURN(0);
}
/**
Open an iconv conversion between a MySQL charset and the respective IBM i CCSID
@param direction The direction of the conversion
@param cs The MySQL character set
@param db2CCSID The IBM i CCSID
@param[out] newConversion The iconv descriptor
@return 0 if successful. Failure otherwise
*/
int32 getConversion(enum_conversionDirection direction, const CHARSET_INFO* cs, uint16 db2CCSID, iconv_t& conversion)
{
DBUG_ENTER("db2i_charsetSupport::getConversion");
int32 rc;
/* Build the hash key */
IconvMap::HashKey hashKey;
hashKey.direction= direction;
hashKey.myCharset= cs;
hashKey.db2CCSID= db2CCSID;
/* Look for the conversion in the cache and add it if it is not there. */
IconvMap *mapping;
if (!(mapping= (IconvMap *) my_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;
}

View File

@ -1,65 +0,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

View File

@ -1,355 +0,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_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 87
static const char* mySQLCollation[MAX_COLLATION] =
{
{"ascii_general"},
{"ascii"},
{"big5_chinese"},
{"big5"},
{"cp1250_croatian"},
{"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_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_swedish"},
{"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_swedish"},
{"utf8_turkish"},
{"utf8_unicode"},
{"utf8"}
};
static const char* mySqlSortSequence[MAX_COLLATION] =
{
{"QALA101F4"},
{"QBLA101F4"},
{"QACHT04B0"},
{"QBCHT04B0"},
{"QALA20481"},
{"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"},
{"QELA20366"},
{"QFLA20366"},
{"QGLA20366"},
{"QATRK0402"},
{"QBTRK0402"},
{"QHLA20366"},
{"QILA20366"},
{"QCJPN04B0"},
{"QDJPN04B0"},
{"QATHA0346"},
{"QBTHA0346"},
{"ACS_CZ"},
{"ADA_DK"},
{"AEO"},
{"AET"},
{"QAUCS04B0"},
{"AHU"},
{"AIS"},
{"ALV"},
{"ALT"},
{"AFA"},
{"APL"},
{"ARO"},
{"ASK"},
{"ASL"},
{"AES"},
{"ASW"},
{"ATR"},
{"AEN"},
{"*HEX"},
{"QEJPN04B0"},
{"QFJPN04B0"},
{"ACS_CZ"},
{"ADA_DK"},
{"AEO"},
{"AET"},
{"QAUCS04B0"},
{"AHU"},
{"AIS"},
{"ALV"},
{"ALT"},
{"AFA"},
{"APL"},
{"ARO"},
{"ASK"},
{"ASL"},
{"AES"},
{"ASW"},
{"ATR"},
{"AEN"},
{"*HEX"}
};
/**
Get the IBM i sort sequence that corresponds to the given MySQL collation.
@param fieldCharSet The collated character set
@param[out] rtnSortSequence The corresponding sort sequence
@return 0 if successful. Failure otherwise
*/
static int32 getAssociatedSortSequence(const CHARSET_INFO *fieldCharSet, const char** rtnSortSequence)
{
DBUG_ENTER("ha_ibmdb2i::getAssociatedSortSequence");
if (strcmp(fieldCharSet->csname,"binary") != 0)
{
int collationSearchLen = strlen(fieldCharSet->name);
if (fieldCharSet->state & MY_CS_BINSORT)
collationSearchLen -= 4;
else
collationSearchLen -= 3;
uint16 loopCnt = 0;
for (loopCnt; loopCnt < MAX_COLLATION; ++loopCnt)
{
if ((strlen(mySQLCollation[loopCnt]) == collationSearchLen) &&
(strncmp((char*)mySQLCollation[loopCnt], fieldCharSet->name, collationSearchLen) == 0))
break;
}
if (loopCnt == MAX_COLLATION) // Did not find associated sort sequence
{
getErrTxt(DB2I_ERR_SRTSEQ);
DBUG_RETURN(DB2I_ERR_SRTSEQ);
}
*rtnSortSequence = mySqlSortSequence[loopCnt];
}
DBUG_RETURN(0);
}
/**
Update sort sequence information for a key.
This function accumulates information about a key as it is called for each
field composing the key. The caller should invoke the function for each field
and (with the exception of the charset parm) preserve the values for the
parms across invocations, until a particular key has been evaluated. Once
the last field in the key has been evaluated, the fileSortSequence and
fileSortSequenceLibrary parms will contain the correct information for
creating the corresponding DB2 key.
@param charset The character set under consideration
@param[in, out] fileSortSequenceType The type of the current key's sort seq
@param[in, out] fileSortSequence The IBM i identifier for the DB2 sort sequence
that corresponds
@return 0 if successful. Failure otherwise
*/
int32 updateAssociatedSortSequence(const CHARSET_INFO* charset,
char* fileSortSequenceType,
char* fileSortSequence,
char* fileSortSequenceLibrary)
{
DBUG_ENTER("ha_ibmdb2i::updateAssociatedSortSequence");
DBUG_ASSERT(charset);
if (strcmp(charset->csname,"binary") != 0)
{
char newSortSequence[11] = "";
char newSortSequenceType = ' ';
const char* foundSortSequence;
int rc = getAssociatedSortSequence(charset, &foundSortSequence);
if (rc) DBUG_RETURN (rc);
switch(foundSortSequence[0])
{
case '*': // Binary
strcat(newSortSequence,foundSortSequence);
newSortSequenceType = 'B';
break;
case 'Q': // Non-ICU sort sequence
strcat(newSortSequence,foundSortSequence);
if ((charset->state & MY_CS_BINSORT) != 0)
{
strcat(newSortSequence,"U");
}
else if ((charset->state & MY_CS_CSSORT) != 0)
{
strcat(newSortSequence,"U");
}
else
{
strcat(newSortSequence,"S");
}
newSortSequenceType = 'N';
break;
default: // ICU sort sequence
{
if ((charset->state & MY_CS_CSSORT) == 0)
{
if (osVersion.v >= 6)
strcat(newSortSequence,"I34"); // ICU 3.4
else
strcat(newSortSequence,"I26"); // ICU 2.6.1
}
strcat(newSortSequence,foundSortSequence);
newSortSequenceType = 'I';
}
break;
}
if (*fileSortSequenceType == ' ') // If no sort sequence has been set yet
{
// Set associated sort sequence
strcpy(fileSortSequence,newSortSequence);
strcpy(fileSortSequenceLibrary,"QSYS");
*fileSortSequenceType = newSortSequenceType;
}
else if (strcmp(fileSortSequence,newSortSequence) != 0)
{
// Only one sort sequence/collation is supported for each DB2 index.
getErrTxt(DB2I_ERR_MIXED_COLLATIONS);
DBUG_RETURN(DB2I_ERR_MIXED_COLLATIONS);
}
}
DBUG_RETURN(0);
}

View File

@ -1,48 +0,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 CHARSET_INFO* charset,
char* fileSortSequenceType,
char* fileSortSequence,
char* fileSortSequenceLibrary);
#endif

View File

@ -1,672 +0,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 "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.str)
{
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.str, 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.str, 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.str) == 0)
{
int rc = updateAssociatedSortSequence((*field)->charset(),
fileSortSequenceType,
fileSortSequence,
fileSortSequenceLibrary);
if (unlikely(rc)) return rc;
}
} while (*(++field));
}
firstTime = true;
appendHere.append(STRING_WITH_LEN(") REFERENCES "));
if (fk->ref_table->db.str)
{
convertMySQLNameToDB2Name(fk->ref_table->db.str, db2LibName, sizeof(db2LibName));
}
else
{
db2i_table::getDB2LibNameFromPath(path, db2LibName);
}
if (lower_case_table_names == 1)
my_casedn_str(files_charset_info, db2LibName);
appendHere.append(db2LibName);
appendHere.append('.');
char db2FileName[MAX_DB2_FILENAME_LENGTH+1];
convertMySQLNameToDB2Name(fk->ref_table->table.str, db2FileName, sizeof(db2FileName));
if (lower_case_table_names)
my_casedn_str(files_charset_info, db2FileName);
appendHere.append(db2FileName);
if (!fk->ref_columns.is_empty())
{
List_iterator<Key_part_spec> ref(fk->ref_columns);
Key_part_spec* curRef;
appendHere.append(STRING_WITH_LEN(" ("));
while (curRef = ref++)
{
if (!firstTime)
{
appendHere.append(',');
}
firstTime = false;
convertMySQLNameToDB2Name(curRef->field_name.str, 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(",\n 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)
{
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);
}
DBUG_VOID_RETURN;
}
/***********************************************************************
This method returns to MySQL a list, with one entry in the list describing
each foreign key constraint.
***********************************************************************/
int ha_ibmdb2i::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
{
DBUG_ENTER("ha_ibmdb2i::get_foreign_key_list");
int rc = 0;
uint32 constraintSpaceLength; // Length of space passed to DB2
ValidatedPointer<char> constraintSpace; // Space pointer passed to DB2
uint16 rtnCode; // Return code from DB2
uint32 neededLen; // Bytes needed to contain DB2 constraint info
uint32 cstCnt; // Number of constraints returned by DB2
uint32 fld;
constraint_hdr* cstHdr; // Pointer to a cst header structure
FK_constraint* FKCstDef; // Pointer to definition of foreign key constraint
cst_name* fieldName; // Pointer to field name structure
const char *method;
ulong methodLen;
char* tempPtr; // Temp pointer for traversing constraint space
char convName[128];
if (!(share = get_share(table_share->path.str, table)))
DBUG_RETURN(0);
// Allocate space to retrieve the DB2 constraint information.
constraintSpaceLength = 5000; // Try allocating 5000 bytes and see if enough.
constraintSpace.alloc(constraintSpaceLength);
rc = bridge()->expectErrors(QMY_ERR_NEED_MORE_SPACE)
->constraints(db2Table->dataFile()->getMasterDefnHandle(),
constraintSpace,
constraintSpaceLength,
&neededLen,
&cstCnt);
if (unlikely(rc == QMY_ERR_NEED_MORE_SPACE))
{
constraintSpaceLength = neededLen; // Get length of space that's needed
constraintSpace.realloc(constraintSpaceLength);
rc = bridge()->expectErrors(QMY_ERR_NEED_MORE_SPACE)
->constraints(db2Table->dataFile()->getMasterDefnHandle(),
constraintSpace,
constraintSpaceLength,
&neededLen,
&cstCnt);
}
/* If constraint information was returned by DB2, build a text string */
/* to return to MySQL. */
if ((rc == 0) && (cstCnt > 0))
{
tempPtr = constraintSpace;
cstHdr = (constraint_hdr_t*)(void*)constraintSpace; // Address first constraint definition
for (int i = 0; i < cstCnt; ++i)
{
if (cstHdr->CstType[0] == QMY_CST_FK) // If this is a foreign key constraint
{
FOREIGN_KEY_INFO f_key_info;
LEX_STRING *name= 0;
tempPtr = (char*)(tempPtr + cstHdr->CstDefOff);
FKCstDef = (FK_constraint_t*)tempPtr;
/* Process the constraint name. */
convFromEbcdic(FKCstDef->CstName.Name, convName,FKCstDef->CstName.Len);
if (convName[0] == '"') // If quoted, exclude quotes.
f_key_info.foreign_id = thd_make_lex_string(thd, 0,
convName + 1, (uint) (FKCstDef->CstName.Len - 2), 1);
else // Not quoted
f_key_info.foreign_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);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,297 +0,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"},
{"The %-.64s character set is not supported."},
{"Auto_increment is not allowed for a partitioned table"},
{"Character set conversion error due to unknown encoding scheme %d"},
{""},
{"Table '%-.128s' was not found by the storage engine"},
{"Could not resolve to %-.128s in library %-.10s type %-.10s (errno = %d)"},
{"Error on _PGMCALL for program %-.10s in library %-.10s (error = %d)"},
{"Error on _ILECALL for API '%.128s' (error = %d)"},
{"Error in iconv() function during character set conversion (errno = %d)"},
{"Error from Get Encoding Scheme (QTQGESP) API: %d, %d, %d"},
{"Error from Get Related Default CCSID (QTQGRDC) API: %d, %d, %d"},
{"Data out of range for column '%.192s'"},
{"Schema name '%.128s' exceeds maximum length of %d characters"},
{"Multiple collations not supported in a single index or constraint"},
{"Sort sequence was not found"},
{"One or more characters in column %.128s were substituted during conversion"},
{"A decimal column exceeded the maximum precision. Data may be truncated."},
{"Some data returned by DB2 for table %s could not be converted for MySQL"},
{""},
{"Column %.128s contains characters that cannot be converted"},
{"An invalid name was specified for ibmdb2i_rdb_name."},
{"A duplicate key was encountered for index '%.128s'"},
{"A table with the same name exists but has incompatible column definitions."},
{"The created table was discovered as an existing DB2 object."},
{"Some attribute(s) defined for column '%.128s' may not be honored by accesses from DB2."},
};
/*
The following strings are associated with errors that can be returned
by the operating system via the QMY_* APIs. Most are very uncommon and
indicate a bug somewhere.
*/
static const char* systemErrors[MAX_MSGSTRING] =
{
{"Thread ID is too long"},
{"Error creating a SPACE memory object"},
{"Error creating a FILE memory object"},
{"Error creating a SPACE synchronization token"},
{"Error creating a FILE synchronization token"},
{"See message %-.7s in joblog for job %-.6s/%-.10s/%-.10s."},
{"Error unlocking a synchronization token when closing a connection"},
{"Invalid action specified for an 'object lock' request"},
{"Invalid action specified for a savepoint request"},
{"Partial keys are not supported with an ICU sort sequence"},
{"Error retrieving an ICU sort key"},
{"Error converting single-byte sort sequence to UCS-2"},
{"An unsupported collation was specified"},
{"Validation failed for referenced table of foreign key constraint"},
{"Error extracting table for constraint information"},
{"Error extracting referenced table for constraint information"},
{"Invalid action specified for a 'commitment control' request"},
{"Invalid commitment control isolation level specified on 'open' request"},
{"Invalid file handle"},
{" "},
{"Invalid option specified for returning data on 'read' request"},
{"Invalid orientation specified for 'read' request"},
{"Invalid option type specified for 'read' request"},
{"Invalid isolation level for starting commitment control"},
{"Error unlocking a synchronization token in module QMYALC"},
{"Length of space for returned format is not long enough"},
{"SQL XA transactions are currently unsupported by this interface"},
{"The associated QSQSRVR job was killed or ended unexpectedly."},
{"Error unlocking a synchronization token in module QMYSEI"},
{"Error unlocking a synchronization token in module QMYSPO"},
{"Error converting input CCSID from short form to long form"},
{" "},
{"Error getting associated CCSID for CCSID conversion"},
{"Error converting a string from one CCSID to another"},
{"Error unlocking a synchronization token"},
{"Error destroying a synchronization token"},
{"Error locking a synchronization token"},
{"Error recreating a synchronization token"},
{"A space handle was not specified for a constraint request"},
{"An SQL cursor was specified for a delete request"},
{" "},
{"Error on delete request because current UFCB for connection is not open"},
{"An SQL cursor was specified for an object initialization request"},
{"An SQL cursor was specified for an object override request"},
{"A space handle was not specified for an object override request"},
{"An SQL cursor was specified for an information request"},
{"An SQL cursor was specified for an object lock request"},
{"An SQL cursor was specified for an optimize request"},
{"A data handle was not specified for a read request"},
{"A row number handle was not specified for a read request"},
{"A key handle was not specified for a read request"},
{"An SQL cursor was specified for an row estimation request"},
{"A space handle was not specified for a row estimation request"},
{"An SQL cursor was specified for a release record request"},
{"A statement handle was not specified for an 'execute immediate' request"},
{"A statement handle was not specified for a 'prepare open' request"},
{"An SQL cursor was specified for an update request"},
{"The UFCB was not open for read"},
{"Error on update request because current UFCB for connection is not open"},
{"A data handle was not specified for an update request"},
{"An SQL cursor was specified for a write request"},
{"A data handle was not specified for a write request"},
{"An unknown function was specified on a process request"},
{"A share definition was not specified for an 'allocate share' request"},
{"A share handle was not specified for an 'allocate share' request"},
{"A use count handle was not specified for an 'allocate share' request"},
{"A 'records per key' handle was not specified for an information request"},
{"Error resolving LOB addresss"},
{"Length of a LOB space is too small"},
{"An unknown function was specified for a server request"},
{"Object authorization failed. See message %-.7s in joblog for job %-.6s/%-.10s/%-.10s. for more information."},
{" "},
{"Error locking mutex on server"},
{"Error unlocking mutex on server"},
{"Error checking for RDB name in RDB Directory"},
{"Error creating mutex on server"},
{"A table with that name already exists"},
{" "},
{"Error unlocking mutex"},
{"Error connecting to server job"},
{"Error connecting to server job"},
{" "},
{"Function check occurred while registering parameter spaces. See joblog."},
{" "},
{" "},
{"End of block"},
{"The file has changed and might not be compatible with the MySQL table definition"},
{"Error giving pipe to server job"},
{"There are open object locks when attempting to deallocate"},
{"There is no open lock"},
{" "},
{" "},
{"The maximum value for the auto_increment data type was exceeded"},
{"Error occurred closing the pipe "},
{"Error occurred taking a descriptor for the pipe"},
{"Error writing to pipe "},
{"Server was interrupted "},
{"No pipe descriptor exists for reuse "},
{"Error occurred during an SQL prepare statement "},
{"Error occurred during an SQL open "},
{" "},
{" "},
{" "},
{" "},
{" "},
{" "},
{"An unspecified error was returned from the system."},
{" "}
};
/**
This function builds the text string for an error code, and substitutes
a variable number of replacement variables into the string.
*/
void getErrTxt(int errCode, ...)
{
va_list args;
va_start(args,errCode);
char* buffer = db2i_ileBridge::getBridgeForThread()->getErrorStorage();
const char* msg;
if (errCode >= QMY_ERR_MIN && errCode <= QMY_ERR_SQ_OPEN)
msg = systemErrors[errCode - QMY_ERR_MIN];
else
{
DBUG_ASSERT(errCode >= DB2I_FIRST_ERR && errCode <= DB2I_LAST_ERR);
msg = engineErrors[errCode - DB2I_FIRST_ERR];
}
(void) my_vsnprintf (buffer, MYSQL_ERRMSG_SIZE, msg, args);
va_end(args);
fprintf(stderr,"ibmdb2i error %d: %s\n",errCode,buffer);
DBUG_PRINT("error", ("ibmdb2i error %d: %s",errCode,buffer));
}
static inline void trimSpace(char* str)
{
char* end = strchr(str, ' ');
if (end) *end = 0;
}
/**
Generate the error text specific to an API error returned by a QMY_* API.
@parm errCode The error value
@parm errInfo The structure containing the message and job identifiers.
*/
void reportSystemAPIError(int errCode, const Qmy_Error_output *errInfo)
{
if (errCode >= QMY_ERR_MIN && errCode <= QMY_ERR_SQ_OPEN)
{
switch(errCode)
{
case QMY_ERR_MSGID:
case QMY_ERR_NOT_AUTH:
{
DBUG_ASSERT(errInfo);
char jMsg[8]; // Error message ID
char jName[11]; // Job name
char jUser[11]; // Job user
char jNbr[7]; // Job number
memset(jMsg, 0, sizeof(jMsg));
memset(jName, 0, sizeof(jMsg));
memset(jUser, 0, sizeof(jMsg));
memset(jMsg, 0, sizeof(jMsg));
convFromEbcdic(errInfo->MsgId,jMsg,sizeof(jMsg)-1);
convFromEbcdic(errInfo->JobName,jName,sizeof(jName)-1);
trimSpace(jName);
convFromEbcdic(errInfo->JobUser,jUser,sizeof(jUser)-1);
trimSpace(jUser);
convFromEbcdic(errInfo->JobNbr,jNbr,sizeof(jNbr)-1);
getErrTxt(errCode,jMsg,jNbr,jUser,jName);
}
break;
case QMY_ERR_RTNFMT:
{
getErrTxt(QMY_ERR_LVLID_MISMATCH);
}
break;
default:
getErrTxt(errCode);
break;
}
}
}
/**
Generate a warning for the specified error.
*/
void warning(THD *thd, int errCode, ...)
{
va_list args;
va_start(args,errCode);
char buffer[MYSQL_ERRMSG_SIZE];
const char* msg;
DBUG_ASSERT(errCode >= DB2I_FIRST_ERR && errCode <= DB2I_LAST_ERR);
msg = engineErrors[errCode - DB2I_FIRST_ERR];
(void) my_vsnprintf (buffer, MYSQL_ERRMSG_SIZE, msg, args);
va_end(args);
DBUG_PRINT("warning", ("ibmdb2i warning %d: %s",errCode,buffer));
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, errCode, buffer);
}

View File

@ -1,93 +0,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_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_UNSUPP_CHARSET,
DB2I_ERR_PART_AUTOINC,
DB2I_ERR_UNKNOWN_ENCODING,
DB2I_ERR_RESERVED,
DB2I_ERR_TABLE_NOT_FOUND,
DB2I_ERR_RESOLVE_OBJ,
DB2I_ERR_PGMCALL,
DB2I_ERR_ILECALL,
DB2I_ERR_ICONV,
DB2I_ERR_QTQGESP,
DB2I_ERR_QTQGRDC,
DB2I_ERR_INVALID_COL_VALUE,
DB2I_ERR_TOO_LONG_SCHEMA,
DB2I_ERR_MIXED_COLLATIONS,
DB2I_ERR_SRTSEQ,
DB2I_ERR_SUB_CHARS,
DB2I_ERR_PRECISION,
DB2I_ERR_INVALID_DATA,
DB2I_ERR_RESERVED2,
DB2I_ERR_ILL_CHAR,
DB2I_ERR_BAD_RDB_NAME,
DB2I_ERR_UNKNOWN_IDX,
DB2I_ERR_DISCOVERY_MISMATCH,
DB2I_ERR_WARN_CREATE_DISCOVER,
DB2I_ERR_WARN_COL_ATTRS,
DB2I_LAST_ERR = DB2I_ERR_WARN_COL_ATTRS
};
void getErrTxt(int errcode, ...);
void reportSystemAPIError(int errCode, const Qmy_Error_output *errInfo);
void warning(THD *thd, int errCode, ...);
const char* DB2I_SQL0350 = "\xE2\xD8\xD3\xF0\xF3\xF5\xF0"; // SQL0350 in EBCDIC
const char* DB2I_CPF503A = "\xC3\xD7\xC6\xF5\xF0\xF3\xC1"; // CPF503A in EBCDIC
const char* DB2I_SQL0538 = "\xE2\xD8\xD3\xF0\xF5\xF3\xF8"; // SQL0538 in EBCDIC
#endif

View File

@ -1,553 +0,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_file.h"
#include "db2i_charsetSupport.h"
#include "db2i_collationSupport.h"
#include "db2i_misc.h"
#include "db2i_errors.h"
#include "my_dir.h"
db2i_table::db2i_table(const TABLE_SHARE* myTable, const char* path) :
mysqlTable(myTable),
db2StartId(0),
blobFieldCount(0),
blobFields(NULL),
blobFieldActualSizes(NULL),
logicalFiles(NULL),
physicalFile(NULL),
db2TableNameSQLAscii(NULL),
db2LibNameSQLAscii(NULL)
{
char asciiLibName[MAX_DB2_SCHEMANAME_LENGTH + 1];
getDB2LibNameFromPath(path, asciiLibName, ASCII_NATIVE);
char asciiFileName[MAX_DB2_FILENAME_LENGTH + 1];
getDB2FileNameFromPath(path, asciiFileName, ASCII_NATIVE);
size_t libNameLen = strlen(asciiLibName);
size_t fileNameLen = strlen(asciiFileName);
db2LibNameEbcdic=(char *)
my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
&db2LibNameEbcdic, libNameLen+1,
&db2LibNameAscii, libNameLen+1,
&db2LibNameSQLAscii, libNameLen*2 + 1,
&db2TableNameEbcdic, fileNameLen+1,
&db2TableNameAscii, fileNameLen+1,
&db2TableNameSQLAscii, fileNameLen*2 + 1,
NullS);
if (likely(db2LibNameEbcdic))
{
memcpy(db2LibNameAscii, asciiLibName, libNameLen);
convertNativeToSQLName(db2LibNameAscii, db2LibNameSQLAscii);
convToEbcdic(db2LibNameAscii, db2LibNameEbcdic, libNameLen);
memcpy(db2TableNameAscii, asciiFileName, fileNameLen);
convertNativeToSQLName(db2TableNameAscii, db2TableNameSQLAscii);
convToEbcdic(db2TableNameAscii, db2TableNameEbcdic, fileNameLen);
}
conversionDefinitions[toMySQL] = NULL;
conversionDefinitions[toDB2] = NULL;
isTemporaryTable = (strstr(mysqlTable->path.str, mysql_tmpdir) == mysqlTable->path.str);
}
int32 db2i_table::initDB2Objects(const char* path)
{
uint fileObjects = 1 + mysqlTable->keys;
ValidatedPointer<ShrDef> fileDefnSpace(sizeof(ShrDef) * fileObjects);
physicalFile = new db2i_file(this);
physicalFile->fillILEDefn(&fileDefnSpace[0], true);
logicalFileCount = mysqlTable->keys;
if (logicalFileCount > 0)
{
logicalFiles = new db2i_file*[logicalFileCount];
for (int k = 0; k < logicalFileCount; k++)
{
logicalFiles[k] = new db2i_file(this, k);
logicalFiles[k]->fillILEDefn(&fileDefnSpace[k+1], false);
}
}
ValidatedPointer<FILE_HANDLE> fileDefnHandles(sizeof(FILE_HANDLE) * fileObjects);
size_t formatSpaceLen = sizeof(format_hdr_t) + mysqlTable->fields * sizeof(DB2Field);
formatSpace.alloc(formatSpaceLen);
int rc = db2i_ileBridge::getBridgeForThread()->
expectErrors(QMY_ERR_RTNFMT)->
allocateFileDefn(fileDefnSpace,
fileDefnHandles,
fileObjects,
db2LibNameEbcdic,
strlen(db2LibNameEbcdic),
formatSpace,
formatSpaceLen);
if (rc)
{
// We have to handle a format space error as a special case of a FID
// mismatch. We should only get the space error if columns have been added
// to the DB2 table without MySQL's knowledge, which is effectively a
// FID problem.
if (rc == QMY_ERR_RTNFMT)
{
rc = QMY_ERR_LVLID_MISMATCH;
getErrTxt(rc);
}
return rc;
}
convFromEbcdic(((format_hdr_t*)formatSpace)->FilLvlId, fileLevelID, sizeof(fileLevelID));
if (!doFileIDsMatch(path))
{
getErrTxt(QMY_ERR_LVLID_MISMATCH);
return QMY_ERR_LVLID_MISMATCH;
}
physicalFile->setMasterDefnHandle(fileDefnHandles[0]);
for (int k = 0; k < mysqlTable->keys; k++)
{
logicalFiles[k]->setMasterDefnHandle(fileDefnHandles[k+1]);
}
db2StartId = (uint64)(((format_hdr_t*)formatSpace)->StartIdVal);
db2Fields = (DB2Field*)((char*)(void*)formatSpace + ((format_hdr_t*)formatSpace)->ColDefOff);
uint fields = mysqlTable->fields;
for (int i = 0; i < fields; ++i)
{
if (db2Field(i).isBlob())
{
blobFieldCount++;
}
}
if (blobFieldCount)
{
blobFieldActualSizes = (uint*)my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
&blobFieldActualSizes, blobFieldCount * sizeof(uint),
&blobFields, blobFieldCount * sizeof(uint16),
NullS);
int b = 0;
for (int i = 0; i < fields; ++i)
{
if (db2Field(i).isBlob())
{
blobFields[b++] = i;
}
}
}
my_multi_malloc(MYF(MY_WME),
&conversionDefinitions[toMySQL], fields * sizeof(iconv_t),
&conversionDefinitions[toDB2], fields * sizeof(iconv_t),
NullS);
for (int i = 0; i < fields; ++i)
{
conversionDefinitions[toMySQL][i] = (iconv_t)(-1);
conversionDefinitions[toDB2][i] = (iconv_t)(-1);
}
return 0;
}
int db2i_table::fastInitForCreate(const char* path)
{
ValidatedPointer<ShrDef> fileDefnSpace(sizeof(ShrDef));
physicalFile = new db2i_file(this);
physicalFile->fillILEDefn(fileDefnSpace, true);
ValidatedPointer<FILE_HANDLE> fileDefnHandles(sizeof(FILE_HANDLE));
size_t formatSpaceLen = sizeof(format_hdr_t) +
mysqlTable->fields * sizeof(DB2Field);
formatSpace.alloc(formatSpaceLen);
int rc = db2i_ileBridge::getBridgeForThread()->allocateFileDefn(fileDefnSpace,
fileDefnHandles,
1,
db2LibNameEbcdic,
strlen(db2LibNameEbcdic),
formatSpace,
formatSpaceLen);
if (rc)
return rc;
convFromEbcdic(((format_hdr_t*)formatSpace)->FilLvlId, fileLevelID, sizeof(fileLevelID));
doFileIDsMatch(path);
return 0;
}
bool db2i_table::doFileIDsMatch(const char* path)
{
char name_buff[FN_REFLEN];
fn_format(name_buff, path, "", FID_EXT, (MY_REPLACE_EXT | MY_UNPACK_FILENAME));
File fd = my_open(name_buff, O_RDONLY, MYF(0));
if (fd == -1)
{
if (errno == ENOENT)
{
fd = my_create(name_buff, 0, O_WRONLY, MYF(MY_WME));
if (fd == -1)
{
// TODO: Report errno here
return false;
}
my_write(fd, (uchar*)fileLevelID, sizeof(fileLevelID), MYF(MY_WME));
my_close(fd, MYF(0));
return true;
}
else
{
// TODO: Report errno here
return false;
}
}
char diskFID[sizeof(fileLevelID)];
bool match = false;
if (my_read(fd, (uchar*)diskFID, sizeof(diskFID), MYF(MY_WME)) == sizeof(diskFID) &&
(memcmp(diskFID, fileLevelID, sizeof(diskFID)) == 0))
match = true;
my_close(fd, MYF(0));
return match;
}
void db2i_table::deleteAssocFiles(const char* name)
{
char name_buff[FN_REFLEN];
fn_format(name_buff, name, "", FID_EXT, (MY_REPLACE_EXT | MY_UNPACK_FILENAME));
my_delete(name_buff, MYF(0));
}
void db2i_table::renameAssocFiles(const char* from, const char* to)
{
rename_file_ext(from, to, FID_EXT);
}
db2i_table::~db2i_table()
{
my_free(blobFieldActualSizes);
my_free(conversionDefinitions[toMySQL]);
if (logicalFiles)
{
for (int k = 0; k < logicalFileCount; ++k)
{
delete logicalFiles[k];
}
delete[] logicalFiles;
}
delete physicalFile;
my_free(db2LibNameEbcdic);
}
void db2i_table::getDB2QualifiedName(char* to)
{
strcat(to, getDB2LibName(ASCII_SQL));
strcat(to, ".");
strcat(to, getDB2TableName(ASCII_SQL));
}
void db2i_table::getDB2QualifiedNameFromPath(const char* path, char* to)
{
getDB2LibNameFromPath(path, to);
strcat(to, ".");
getDB2FileNameFromPath(path, strend(to));
}
size_t db2i_table::smartFilenameToTableName(const char *in, char* out, size_t outlen)
{
if (strchr(in, '@') == NULL)
{
return filename_to_tablename(in, out, outlen);
}
char* test = (char*) my_malloc(outlen, MYF(MY_WME));
filename_to_tablename(in, test, outlen);
char* cur = test;
while (*cur)
{
if ((*cur <= 0x20) || (*cur >= 0x80))
{
strncpy(out, in, outlen);
my_free(test);
return min(outlen, strlen(out));
}
++cur;
}
strncpy(out, test, outlen);
my_free(test);
return min(outlen, strlen(out));
}
void db2i_table::filenameToTablename(const char* in, char* out, size_t outlen)
{
if (strchr(in, '#') == NULL)
{
smartFilenameToTableName(in, out, outlen);
return;
}
char* temp = (char*)sql_alloc(outlen);
const char* part1, *part2, *part3, *part4;
part1 = in;
part2 = strstr(part1, "#P#");
if (part2);
{
part3 = part2 + 3;
part4 = strchr(part3, '#');
if (!part4)
part4 = strend(in);
}
memcpy(temp, part1, min(outlen, part2 - part1));
temp[min(outlen-1, part2-part1)] = 0;
int32 accumLen = smartFilenameToTableName(temp, out, outlen);
if (part2 && (accumLen + 4 < outlen))
{
strcat(out, "#P#");
accumLen += 4;
memset(temp, 0, min(outlen, part2-part1));
memcpy(temp, part3, min(outlen, part4-part3));
temp[min(outlen-1, part4-part3)] = 0;
accumLen += smartFilenameToTableName(temp, strend(out), outlen-accumLen);
if (part4 && (accumLen + (strend(in) - part4 + 1) < outlen))
{
strcat(out, part4);
}
}
}
void db2i_table::getDB2LibNameFromPath(const char* path, char* lib, NameFormatFlags format)
{
if (strstr(path, mysql_tmpdir) == path)
{
strcpy(lib, DB2I_TEMP_TABLE_SCHEMA);
}
else
{
const char* c = strend(path) - 1;
while (c > path && *c != '\\' && *c != '/')
--c;
if (c != path)
{
const char* dbEnd = c;
do {
--c;
} while (c >= path && *c != '\\' && *c != '/');
if (c >= path)
{
const char* dbStart = c+1;
char fileName[FN_REFLEN];
memcpy(fileName, dbStart, dbEnd - dbStart);
fileName[dbEnd-dbStart] = 0;
char dbName[MAX_DB2_SCHEMANAME_LENGTH+1];
filenameToTablename(fileName, dbName , sizeof(dbName));
convertMySQLNameToDB2Name(dbName, lib, sizeof(dbName), true, (format==ASCII_SQL) );
}
else
DBUG_ASSERT(0); // This should never happen!
}
}
}
void db2i_table::getDB2FileNameFromPath(const char* path, char* file, NameFormatFlags format)
{
const char* fileEnd = strend(path);
const char* c = fileEnd;
while (c > path && *c != '\\' && *c != '/')
--c;
if (c != path)
{
const char* fileStart = c+1;
char fileName[FN_REFLEN];
memcpy(fileName, fileStart, fileEnd - fileStart);
fileName[fileEnd - fileStart] = 0;
char db2Name[MAX_DB2_FILENAME_LENGTH+1];
filenameToTablename(fileName, db2Name, sizeof(db2Name));
convertMySQLNameToDB2Name(db2Name, file, sizeof(db2Name), true, (format==ASCII_SQL) );
}
}
// Generates the DB2 index name when given the MySQL index and table names.
int32 db2i_table::appendQualifiedIndexFileName(const char* indexName,
const char* tableName,
String& to,
NameFormatFlags format,
enum_DB2I_INDEX_TYPE type)
{
char generatedName[MAX_DB2_FILENAME_LENGTH+1];
strncpy(generatedName, indexName, DB2I_INDEX_NAME_LENGTH_TO_PRESERVE);
generatedName[DB2I_INDEX_NAME_LENGTH_TO_PRESERVE] = 0;
char* endOfGeneratedName;
if (type == typeDefault)
{
strcat(generatedName, DB2I_DEFAULT_INDEX_NAME_DELIMITER);
endOfGeneratedName = strend(generatedName);
}
else if (type != typeNone)
{
strcat(generatedName, DB2I_ADDL_INDEX_NAME_DELIMITER);
endOfGeneratedName = strend(generatedName);
*(endOfGeneratedName-2) = char(type);
}
uint lenWithoutFile = endOfGeneratedName - generatedName;
char strippedTableName[MAX_DB2_FILENAME_LENGTH+1];
if (format == ASCII_SQL)
{
strcpy(strippedTableName, tableName);
stripExtraQuotes(strippedTableName+1, sizeof(strippedTableName));
tableName = strippedTableName;
}
if (strlen(tableName) > (MAX_DB2_FILENAME_LENGTH-lenWithoutFile))
return -1;
strncat(generatedName,
tableName+1,
min(strlen(tableName), (MAX_DB2_FILENAME_LENGTH-lenWithoutFile))-2 );
char finalName[MAX_DB2_FILENAME_LENGTH+1];
convertMySQLNameToDB2Name(generatedName, finalName, sizeof(finalName), true, (format==ASCII_SQL));
to.append(finalName);
return 0;
}
void db2i_table::findConversionDefinition(enum_conversionDirection direction, uint16 fieldID)
{
getConversion(direction,
mysqlTable->field[fieldID]->charset(),
db2Field(fieldID).getCCSID(),
conversionDefinitions[direction][fieldID]);
}
db2i_file::db2i_file(db2i_table* table) : db2Table(table)
{
commonCtorInit();
DBUG_ASSERT(table->getMySQLTable()->table_name.length <= MAX_DB2_FILENAME_LENGTH-2);
db2FileName = (char*)table->getDB2TableName(db2i_table::EBCDIC_NATIVE);
}
db2i_file::db2i_file(db2i_table* table, int index) : db2Table(table)
{
commonCtorInit();
if ((index == table->getMySQLTable()->primary_key) && !table->isTemporary())
{
db2FileName = (char*)table->getDB2TableName(db2i_table::EBCDIC_NATIVE);
}
else
{
// Generate the index name (in index___table form); quote and EBCDICize it.
String qualifiedPath;
qualifiedPath.length(0);
const char* asciiFileName = table->getDB2TableName(db2i_table::ASCII_NATIVE);
db2i_table::appendQualifiedIndexFileName(table->getMySQLTable()->key_info[index].name,
asciiFileName,
qualifiedPath,
db2i_table::ASCII_NATIVE,
typeDefault);
db2FileName = (char*)my_malloc(qualifiedPath.length()+1, MYF(MY_WME | MY_ZEROFILL));
convToEbcdic(qualifiedPath.ptr(), db2FileName, qualifiedPath.length());
}
}
void db2i_file::commonCtorInit()
{
masterDefn = 0;
memset(&formats, 0, maxRowFormats*sizeof(RowFormat));
}
void db2i_file::fillILEDefn(ShrDef* defn, bool readInArrivalSeq)
{
defn->ObjNamLen = strlen(db2FileName);
DBUG_ASSERT(defn->ObjNamLen <= sizeof(defn->ObjNam));
memcpy(defn->ObjNam, db2FileName, defn->ObjNamLen);
defn->ArrSeq[0] = (readInArrivalSeq ? QMY_YES : QMY_NO);
}

View File

@ -1,446 +0,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_FILE_H
#define DB2I_FILE_H
#include "db2i_global.h"
#include "db2i_ileBridge.h"
#include "db2i_validatedPointer.h"
#include "my_atomic.h"
#include "db2i_iconv.h"
#include "db2i_charsetSupport.h"
const char FID_EXT[] = ".FID";
class db2i_file;
#pragma pack(1)
struct DB2LobField
{
char reserved1;
uint32 length;
char reserved2[4];
uint32 ordinal;
ILEMemHandle dataHandle;
char reserved3[8];
};
#pragma pack(pop)
class DB2Field
{
public:
uint16 getType() const { return *(uint16*)(&definition.ColType); }
uint16 getByteLengthInRecord() const { return definition.ColLen; }
uint16 getDataLengthInRecord() const
{
return (getType() == QMY_VARCHAR || getType() == QMY_VARGRAPHIC ? definition.ColLen - 2 : definition.ColLen);
}
uint16 getCCSID() const { return *(uint16*)(&definition.ColCCSID); }
bool isBlob() const
{
uint16 type = getType();
return (type == QMY_BLOBCLOB || type == QMY_DBCLOB);
}
uint16 getBufferOffset() const { return definition.ColBufOff; }
uint16 calcBlobPad() const
{
DBUG_ASSERT(isBlob());
return getByteLengthInRecord() - sizeof (DB2LobField);
}
DB2LobField* asBlobField(char* buf) const
{
DBUG_ASSERT(isBlob());
return (DB2LobField*)(buf + getBufferOffset() + calcBlobPad());
}
private:
col_def_t definition;
};
/**
@class db2i_table
@details
This class describes the logical SQL table provided by DB2.
It stores "table-scoped" information such as the name of the
DB2 schema, BLOB descriptions, and the corresponding MySQL table definition.
Only one instance exists per SQL table.
*/
class db2i_table
{
public:
enum NameFormatFlags
{
ASCII_SQL,
ASCII_NATIVE,
EBCDIC_NATIVE
};
db2i_table(const TABLE_SHARE* myTable, const char* path = NULL);
~db2i_table();
int32 initDB2Objects(const char* path);
const TABLE_SHARE* getMySQLTable() const
{
return mysqlTable;
}
uint64 getStartId() const
{
return db2StartId;
}
void updateStartId(uint64 newStartId)
{
db2StartId = newStartId;
}
bool hasBlobs() const
{
return (blobFieldCount > 0);
}
uint16 getBlobCount() const
{
return blobFieldCount;
}
uint getBlobFieldActualSize(uint fieldIndex) const
{
return blobFieldActualSizes[getBlobIdFromField(fieldIndex)];
}
void updateBlobFieldActualSize(uint fieldIndex, uint32 newSize)
{
// It's OK that this isn't threadsafe, since this is just an advisory
// value. If a race condition causes the lesser of two values to be stored,
// that's OK.
uint16 blobID = getBlobIdFromField(fieldIndex);
DBUG_ASSERT(blobID < blobFieldCount);
if (blobFieldActualSizes[blobID] < newSize)
{
blobFieldActualSizes[blobID] = newSize;
}
}
const char* getDB2LibName(NameFormatFlags format = EBCDIC_NATIVE)
{
switch (format)
{
case EBCDIC_NATIVE:
return db2LibNameEbcdic; break;
case ASCII_NATIVE:
return db2LibNameAscii; break;
case ASCII_SQL:
return db2LibNameSQLAscii; break;
default:
DBUG_ASSERT(0);
}
return NULL;
}
const char* getDB2TableName(NameFormatFlags format = EBCDIC_NATIVE) const
{
switch (format)
{
case EBCDIC_NATIVE:
return db2TableNameEbcdic; break;
case ASCII_NATIVE:
return db2TableNameAscii; break;
case ASCII_SQL:
return db2TableNameAscii; break;
break;
default:
DBUG_ASSERT(0);
}
return NULL;
}
DB2Field& db2Field(int fieldID) const { return db2Fields[fieldID]; }
DB2Field& db2Field(const Field* field) const { return db2Field(field->field_index); }
void processFormatSpace();
void* getFormatSpace(size_t& spaceNeeded)
{
DBUG_ASSERT(formatSpace == NULL);
spaceNeeded = sizeof(format_hdr_t) + mysqlTable->fields * sizeof(DB2Field);
formatSpace.alloc(spaceNeeded);
return (void*)formatSpace;
}
bool isTemporary() const
{
return isTemporaryTable;
}
void getDB2QualifiedName(char* to);
static void getDB2LibNameFromPath(const char* path, char* lib, NameFormatFlags format=ASCII_SQL);
static void getDB2FileNameFromPath(const char* path, char* file, NameFormatFlags format=ASCII_SQL);
static void getDB2QualifiedNameFromPath(const char* path, char* to);
static int32 appendQualifiedIndexFileName(const char* indexName,
const char* tableName,
String& to,
NameFormatFlags format=ASCII_SQL,
enum_DB2I_INDEX_TYPE type=typeDefault);
uint16 getBlobIdFromField(uint16 fieldID) const
{
for (int i = 0; i < blobFieldCount; ++i)
{
if (blobFields[i] == fieldID)
return i;
}
DBUG_ASSERT(0);
return 0;
}
iconv_t& getConversionDefinition(enum_conversionDirection direction,
uint16 fieldID)
{
if (conversionDefinitions[direction][fieldID] == (iconv_t)(-1))
findConversionDefinition(direction, fieldID);
return conversionDefinitions[direction][fieldID];
}
const db2i_file* dataFile() const
{
return physicalFile;
}
const db2i_file* indexFile(uint idx) const
{
return logicalFiles[idx];
}
const char* getFileLevelID() const
{
return fileLevelID;
}
static void deleteAssocFiles(const char* name);
static void renameAssocFiles(const char* from, const char* to);
int fastInitForCreate(const char* path);
int initDiscoveredTable(const char* path);
uint16* blobFields;
private:
void findConversionDefinition(enum_conversionDirection direction, uint16 fieldID);
static void filenameToTablename(const char* in, char* out, size_t outlen);
static size_t smartFilenameToTableName(const char *in, char* out, size_t outlen);
void convertNativeToSQLName(const char* input,
char* output)
{
output[0] = input[0];
uint o = 1;
uint i = 1;
do
{
output[o++] = input[i];
if (input[i] == '"' && input[i+1])
output[o++] = '"';
} while (input[++i]);
output[o] = 0; // This isn't the most user-friendly way to handle overflows,
// but at least its safe.
}
bool doFileIDsMatch(const char* path);
ValidatedPointer<format_hdr_t> formatSpace;
DB2Field* db2Fields;
uint64 db2StartId; // Starting value for identity column
uint16 blobFieldCount; // Count of LOB fields in the DB2 table
uint* blobFieldActualSizes; // Array of LOB field lengths (actual vs. allocated).
// This is updated as LOBs are read and will contain
// the length of the longest known LOB in that field.
iconv_t* conversionDefinitions[2];
const TABLE_SHARE* mysqlTable;
uint16 logicalFileCount;
char* db2LibNameEbcdic; // Quoted and in EBCDIC
char* db2LibNameAscii;
char* db2TableNameEbcdic;
char* db2TableNameAscii;
char* db2TableNameSQLAscii;
char* db2LibNameSQLAscii;
db2i_file* physicalFile;
db2i_file** logicalFiles;
bool isTemporaryTable;
char fileLevelID[13];
};
/**
@class db2i_file
@details This class describes a file object underlaying a particular SQL
table. Both "physical files" (data) and "logical files" (indices) are
described by this class. Only one instance of the class exists per DB2 file
object. The single instance is responsible for de/allocating the multiple
handles used by the handlers.
*/
class db2i_file
{
public:
struct RowFormat
{
uint16 readRowLen;
uint16 readRowNullOffset;
uint16 writeRowLen;
uint16 writeRowNullOffset;
char inited;
};
public:
// Construct an instance for a physical file.
db2i_file(db2i_table* table);
// Construct an instance for a logical file.
db2i_file(db2i_table* table, int index);
~db2i_file()
{
if (masterDefn)
db2i_ileBridge::getBridgeForThread()->deallocateFile(masterDefn);
if (db2FileName != (char*)db2Table->getDB2TableName(db2i_table::EBCDIC_NATIVE))
my_free(db2FileName);
}
// This is roughly equivalent to an "open". It tells ILE to allocate a descriptor
// for the file. The associated handle is returned to the caller.
int allocateNewInstance(FILE_HANDLE* newHandle, ILEMemHandle inuseSpace) const
{
int rc;
rc = db2i_ileBridge::getBridgeForThread()->allocateFileInstance(masterDefn,
inuseSpace,
newHandle);
if (rc) *newHandle = 0;
return rc;
}
// This obtains the row layout associated with a particular access intent for
// an open instance of the file.
int obtainRowFormat(FILE_HANDLE instanceHandle,
char intent,
char commitLevel,
const RowFormat** activeFormat) const
{
DBUG_ENTER("db2i_file::obtainRowFormat");
RowFormat* rowFormat;
if (intent == QMY_UPDATABLE)
rowFormat = &(formats[readWrite]);
else if (intent == QMY_READ_ONLY)
rowFormat = &(formats[readOnly]);
if (unlikely(!rowFormat->inited))
{
int rc = db2i_ileBridge::getBridgeForThread()->
initFileForIO(instanceHandle,
intent,
commitLevel,
&(rowFormat->writeRowLen),
&(rowFormat->writeRowNullOffset),
&(rowFormat->readRowLen),
&(rowFormat->readRowNullOffset));
if (rc) DBUG_RETURN(rc);
rowFormat->inited = 1;
}
*activeFormat = rowFormat;
DBUG_RETURN(0);
}
const char* getDB2FileName() const
{
return db2FileName;
}
void fillILEDefn(ShrDef* defn, bool readInArrivalSeq);
void setMasterDefnHandle(FILE_HANDLE handle)
{
masterDefn = handle;
}
FILE_HANDLE getMasterDefnHandle() const
{
return masterDefn;
}
private:
enum RowFormats
{
readOnly = 0,
readWrite,
maxRowFormats
};
mutable RowFormat formats[maxRowFormats];
void commonCtorInit();
char* db2FileName; // Quoted and in EBCDIC
db2i_table* db2Table; // The logical SQL table contained by this file.
bool db2CanSort;
FILE_HANDLE masterDefn;
};
#endif

View File

@ -1,138 +0,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_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*)));
}
}
#endif

View File

@ -1,51 +0,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.
*/
/**
@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

File diff suppressed because it is too large Load Diff

View File

@ -1,499 +0,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_ILEBRIDGE_H
#define DB2I_ILEBRIDGE_H
#include "db2i_global.h"
#include "mysql_priv.h"
#include "as400_types.h"
#include "as400_protos.h"
#include "qmyse.h"
#include "db2i_errors.h"
typedef uint64_t FILE_HANDLE;
typedef my_thread_id CONNECTION_HANDLE;
const char SAVEPOINT_NAME[] = {0xD4,0xE2,0xD7,0xC9,0xD5,0xE3,0xC5,0xD9,0xD5,0x0};
const uint32 TACIT_ERRORS_SIZE=2;
enum db2i_InfoRequestSpec
{
objLength = 1,
rowCount = 2,
deletedRowCount = 4,
rowsPerKey = 8,
meanRowLen = 16,
lastModTime = 32,
createTime = 64,
ioCount = 128
};
extern handlerton *ibmdb2i_hton;
struct IBMDB2I_SHARE;
const uint32 db2i_ileBridge_MAX_INPARM_SIZE = 512;
const uint32 db2i_ileBridge_MAX_OUTPARM_SIZE = 512;
extern pthread_key(IleParms*, THR_ILEPARMS);
struct IleParms
{
char inParms[db2i_ileBridge_MAX_INPARM_SIZE];
char outParms[db2i_ileBridge_MAX_OUTPARM_SIZE];
};
/**
@class db2i_ileBridge
Implements a connection-based interface to the QMY_* APIs
@details Each client connection that touches an IBMDB2I table has a "bridge"
associated with it. This bridge is constructed on first use and provides a
more C-like interface to the APIs. As well, it is reponsible for tracking
connection scoped information such as statement transaction state and error
message text. The bridge is destroyed when the connection ends.
*/
class db2i_ileBridge
{
enum ileFuncs
{
funcRegisterParameterSpaces,
funcRegisterSpace,
funcUnregisterSpace,
funcProcessRequest,
funcListEnd
};
static db2i_ileBridge* globalBridge;
public:
static int setup();
static void takedown();
/**
Obtain a pointer to the bridge for the current connection.
If a MySQL client connection is on the stack, we get the associated brideg.
Otherwise, we use the globalBridge.
*/
static db2i_ileBridge* getBridgeForThread()
{
THD* thd = current_thd;
if (likely(thd))
return getBridgeForThread(thd);
return globalBridge;
}
/**
Obtain a pointer to the bridge for the specified connection.
If a bridge exists already, we return it immediately. Otherwise, prepare
a new bridge for the connection.
*/
static db2i_ileBridge* getBridgeForThread(const THD* thd)
{
void* thdData = *thd_ha_data(thd, ibmdb2i_hton);
if (likely(thdData != NULL))
return (db2i_ileBridge*)(thdData);
db2i_ileBridge* newBridge = createNewBridge(thd->thread_id);
*thd_ha_data(thd, ibmdb2i_hton) = (void*)newBridge;
return newBridge;
}
static void destroyBridgeForThread(const THD* thd);
static void registerPtr(const void* ptr, ILEMemHandle* receiver);
static void unregisterPtr(ILEMemHandle handle);
int32 allocateFileDefn(ILEMemHandle definitionSpace,
ILEMemHandle handleSpace,
uint16 fileCount,
const char* schemaName,
uint16 schemaNameLength,
ILEMemHandle formatSpace,
uint32 formatSpaceLen);
int32 allocateFileInstance(FILE_HANDLE defnHandle,
ILEMemHandle inuseSpace,
FILE_HANDLE* instance);
int32 deallocateFile(FILE_HANDLE fileHandle,
bool postDropTable=FALSE);
int32 read(FILE_HANDLE rfileHandle,
ILEMemHandle buf,
char accessIntent,
char commitLevel,
char orientation,
bool asyncRead = FALSE,
ILEMemHandle rrn = 0,
ILEMemHandle key = 0,
uint32 keylen = 0,
uint16 keyParts = 0,
int pipeFD = -1);
int32 readByRRN(FILE_HANDLE rfileHandle,
ILEMemHandle buf,
uint32 inRRN,
char accessIntent,
char commitLevel);
int32 writeRows(FILE_HANDLE rfileHandle,
ILEMemHandle buf,
char commitLevel,
int64* outIdVal,
bool* outIdGen,
uint32* dupKeyRRN,
char** dupKeyName,
uint32* dupKeyNameLen,
uint32* outIdIncrement);
uint32 execSQL(const char* statement,
uint32 statementCount,
uint8 commitLevel,
bool autoCreateSchema = FALSE,
bool dropSchema = FALSE,
bool noCommit = FALSE,
FILE_HANDLE fileHandle = 0);
int32 prepOpen(const char* statement,
FILE_HANDLE* rfileHandle,
uint32* recLength);
int32 deleteRow(FILE_HANDLE rfileHandle,
uint32 rrn);
int32 updateRow(FILE_HANDLE rfileHandle,
uint32 rrn,
ILEMemHandle buf,
uint32* dupKeyRRN,
char** dupKeyName,
uint32* dupKeyNameLen);
int32 commitmentControl(uint8 function);
int32 savepoint(uint8 function,
const char* savepointName);
int32 recordsInRange(FILE_HANDLE rfileHandle,
ILEMemHandle inSpc,
uint32 inKeyCnt,
uint32 inLiteralCnt,
uint32 inBoundsOff,
uint32 inLitDefOff,
uint32 inLiteralsOff,
uint32 inCutoff,
uint32 inSpcLen,
uint16 inEndByte,
uint64* outRecCnt,
uint16* outRtnCode);
int32 rrlslck(FILE_HANDLE rfileHandle,
char accessIntent);
int32 lockObj(FILE_HANDLE rfileHandle,
uint64 inTimeoutVal,
char inAction,
char inLockType,
char inTimeout);
int32 constraints(FILE_HANDLE rfileHandle,
ILEMemHandle inSpc,
uint32 inSpcLen,
uint32* outLen,
uint32* outCnt);
int32 optimizeTable(FILE_HANDLE rfileHandle);
static int32 initILE(const char* aspName,
uint16* traceCtlPtr);
int32 initFileForIO(FILE_HANDLE rfileHandle,
char accessIntent,
char commitLevel,
uint16* inRecSize,
uint16* inRecNullOffset,
uint16* outRecSize,
uint16* outRecNullOffset);
int32 readInterrupt(FILE_HANDLE fileHandle);
static int32 exitILE();
int32 objectOverride(FILE_HANDLE rfileHandle,
ILEMemHandle buf,
uint32 recordWidth = 0);
int32 retrieveTableInfo(FILE_HANDLE rfileHandle,
uint16 dataRequested,
ha_statistics& stats,
ILEMemHandle inSpc = NULL);
int32 retrieveIndexInfo(FILE_HANDLE rfileHandle,
uint64* outPageCnt);
int32 closeConnection(CONNECTION_HANDLE conn);
int32 quiesceFileInstance(FILE_HANDLE rfileHandle);
/**
Mark the beginning of a "statement transaction"
@detail MySQL "statement transactions" (see sql/handler.cc) are implemented
as DB2 savepoints having a predefined name.
@return 0 if successful; error otherwise
*/
uint32 beginStmtTx()
{
DBUG_ENTER("db2i_ileBridge::beginStmtTx");
if (stmtTxActive)
DBUG_RETURN(0);
stmtTxActive = true;
DBUG_RETURN(savepoint(QMY_SET_SAVEPOINT, SAVEPOINT_NAME));
}
/**
Commit a "statement transaction"
@return 0 if successful; error otherwise
*/
uint32 commitStmtTx()
{
DBUG_ENTER("db2i_ileBridge::commitStmtTx");
DBUG_ASSERT(stmtTxActive);
stmtTxActive = false;
DBUG_RETURN(savepoint(QMY_RELEASE_SAVEPOINT, SAVEPOINT_NAME));
}
/**
Roll back a "statement transaction"
@return 0 if successful; error otherwise
*/
uint32 rollbackStmtTx()
{
DBUG_ENTER("db2i_ileBridge::rollbackStmtTx");
DBUG_ASSERT(stmtTxActive);
stmtTxActive = false;
DBUG_RETURN(savepoint(QMY_ROLLBACK_SAVEPOINT, SAVEPOINT_NAME));
}
/**
Provide storage for generating error messages.
This storage must persist until the error message is retrieved from the
handler instance. It is for this reason that we associate it with the bridge.
@return Pointer to heap storage of MYSQL_ERRMSG_SIZE bytes
*/
char* getErrorStorage()
{
if (!connErrText)
{
connErrText = (char*)my_malloc(MYSQL_ERRMSG_SIZE, MYF(MY_WME));
if (connErrText) connErrText[0] = 0;
}
return connErrText;
}
/**
Free storage for generating error messages.
*/
void freeErrorStorage()
{
if (likely(connErrText))
{
my_free(connErrText);
connErrText = NULL;
}
}
/**
Store a file handle for later retrieval.
If deallocateFile encounters a lock when trying to perform its operation,
the file remains allocated but must be deallocated later. This function
provides a way for the connection to "remember" that this deallocation is
still needed.
@param newname The name of the file to be added
@param newhandle The handle associated with newname
*/
void preserveHandle(const char* newname, FILE_HANDLE newhandle, IBMDB2I_SHARE* share)
{
pendingLockedHandles.add(newname, newhandle, share);
}
/**
Retrieve a file handle stored by preserveHandle().
@param name The name of the file to be retrieved.
@return The handle associated with name
*/
FILE_HANDLE findAndRemovePreservedHandle(const char* name, IBMDB2I_SHARE** share)
{
FILE_HANDLE hdl = pendingLockedHandles.findAndRemove(name, share);
return hdl;
}
/**
Indicate which error messages should be suppressed on the next API call
These functions are useful for ensuring that the provided error numbers
are returned if a failure occurs but do not cause a spurious error message
to be returned.
@return A pointer to this instance
*/
db2i_ileBridge* expectErrors(int32 er1)
{
tacitErrors[0]=er1;
return this;
}
db2i_ileBridge* expectErrors(int32 er1, int32 er2)
{
tacitErrors[0]=er1;
tacitErrors[1]=er2;
return this;
}
/**
Obtain the IBM i system message that accompanied the last API failure.
@return A pointer to the 7 character message ID.
*/
static const char* getErrorMsgID()
{
return ((Qmy_Error_output_t*)parms()->outParms)->MsgId;
}
/**
Convert an API error code into the equivalent MySQL error code (if any)
@param rc The QMYSE API error code
@return If an equivalent exists, the MySQL error code; else rc
*/
static int32 translateErrorCode(int32 rc)
{
if (likely(rc == 0))
return 0;
switch (rc)
{
case QMY_ERR_KEY_NOT_FOUND:
return HA_ERR_KEY_NOT_FOUND;
case QMY_ERR_DUP_KEY:
return HA_ERR_FOUND_DUPP_KEY;
case QMY_ERR_END_OF_FILE:
return HA_ERR_END_OF_FILE;
case QMY_ERR_LOCK_TIMEOUT:
return HA_ERR_LOCK_WAIT_TIMEOUT;
case QMY_ERR_CST_VIOLATION:
return HA_ERR_NO_REFERENCED_ROW;
case QMY_ERR_TABLE_NOT_FOUND:
return HA_ERR_NO_SUCH_TABLE;
case QMY_ERR_NON_UNIQUE_KEY:
return ER_DUP_ENTRY;
case QMY_ERR_MSGID:
{
if (memcmp(getErrorMsgID(), DB2I_CPF503A, 7) == 0)
return HA_ERR_ROW_IS_REFERENCED;
if (memcmp(getErrorMsgID(), DB2I_SQL0538, 7) == 0)
return HA_ERR_CANNOT_ADD_FOREIGN;
}
}
return rc;
}
private:
static db2i_ileBridge* createNewBridge(CONNECTION_HANDLE connID);
static void destroyBridge(db2i_ileBridge* bridge);
static int registerParmSpace(char* in, char* out);
static int32 doIt();
int32 doItWithLog();
static _ILEpointer *functionSymbols; ///< Array of ILE function pointers
CONNECTION_HANDLE cachedConnectionID; ///< The associated connection
bool stmtTxActive; ///< Inside statement transaction
char *connErrText; ///< Storage for error message
int32 tacitErrors[TACIT_ERRORS_SIZE]; ///< List of errors to be suppressed
static IleParms* initParmsForThread();
/**
Get space for passing parameters to the QMY_* APIs
@details A fixed-length parameter passing space is associated with each
pthread. This space is allocated and registered by initParmsForThread()
the first time a pthread works with a bridge. The space is cached away
and remains available until the pthread ends. It became necessary to
disassociate the parameter space from the bridge in order to support
future enhancements to MySQL that sever the one-to-one relationship between
pthreads and user connections. The QMY_* APIs scope a registered parameter
space to the thread that executes the register operation.
*/
static IleParms* parms()
{
IleParms* p = my_pthread_getspecific_ptr(IleParms*, THR_ILEPARMS);
if (likely(p))
return p;
return initParmsForThread();
}
class PreservedHandleList
{
friend db2i_ileBridge* db2i_ileBridge::createNewBridge(CONNECTION_HANDLE);
public:
void add(const char* newname, FILE_HANDLE newhandle, IBMDB2I_SHARE* share);
FILE_HANDLE findAndRemove(const char* fileName, IBMDB2I_SHARE** share);
private:
struct NameHandlePair
{
char name[FN_REFLEN];
FILE_HANDLE handle;
IBMDB2I_SHARE* share;
NameHandlePair* next;
}* head;
} pendingLockedHandles;
#ifndef DBUG_OFF
bool cachedStateIsCoherent()
{
return (current_thd->thread_id == cachedConnectionID);
}
friend void db2i_ileBridge::unregisterPtr(ILEMemHandle);
friend void db2i_ileBridge::registerPtr(const void*, ILEMemHandle*);
static uint32 registeredPtrs;
#endif
};
#endif

View File

@ -1,332 +0,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_ioBuffers.h"
/**
Request another block of rows
Request the next set of rows from DB2. This must only be called after
newReadRequest().
@param orientation The direction to use when reading through the table.
*/
void IOAsyncReadBuffer::loadNewRows(char orientation)
{
rewind();
maxRows() = rowsToBlock;
DBUG_PRINT("db2i_ioBuffers::loadNewRows", ("Requesting %d rows, async = %d", rowsToBlock, readIsAsync));
rc = getBridge()->expectErrors(QMY_ERR_END_OF_BLOCK, QMY_ERR_LOB_SPACE_TOO_SMALL)
->read(file,
ptr(),
accessIntent,
commitLevel,
orientation,
readIsAsync,
rrnList,
0,
0,
0);
DBUG_PRINT("db2i_ioBuffers::loadNewRows", ("recordsRead: %d, rc: %d", (uint32)rowCount(), rc));
*releaseRowNeeded = true;
if (rc == QMY_ERR_END_OF_BLOCK)
{
// This is really just an informational error, so we ignore it.
rc = 0;
DBUG_PRINT("db2i_ioBuffers::loadNewRows", ("End of block signalled"));
}
else if (rc == QMY_ERR_END_OF_FILE)
{
// If we reach EOF or end-of-key, DB2 guarantees that no rows will be locked.
rc = HA_ERR_END_OF_FILE;
*releaseRowNeeded = false;
}
else if (rc == QMY_ERR_KEY_NOT_FOUND)
{
rc = HA_ERR_KEY_NOT_FOUND;
*releaseRowNeeded = false;
}
if (rc) closePipe();
}
/**
Empty the message pipe to prepare for another read.
*/
void IOAsyncReadBuffer::drainPipe()
{
DBUG_ASSERT(pipeState == PendingFullBufferMsg);
PipeRpy_t msg[32];
int bytes;
PipeRpy_t* lastMsg;
while ((bytes = read(msgPipe, msg, sizeof(msg))) > 0)
{
DBUG_PRINT("db2i_ioBuffers::drainPipe",("Pipe returned %d bytes", bytes));
lastMsg = &msg[bytes / (sizeof(msg[0]))-1];
if (lastMsg->CumRowCnt == maxRows() ||
lastMsg->RtnCod != 0)
{
pipeState = ConsumedFullBufferMsg;
break;
}
}
DBUG_PRINT("db2i_ioBuffers::drainPipe",("rc = %d, rows = %d, max = %d", lastMsg->RtnCod, lastMsg->CumRowCnt, (uint32)maxRows()));
}
/**
Poll the message pipe for async read messages
Only valid in async
@param orientation The direction to use when reading through the table.
*/
void IOAsyncReadBuffer::pollNextRow(char orientation)
{
DBUG_ASSERT(readIsAsync);
// Handle the case in which the buffer is full.
if (rowCount() == maxRows())
{
// If we haven't read to the end, exit here.
if (readCursor < rowCount())
return;
if (pipeState == PendingFullBufferMsg)
drainPipe();
if (pipeState == ConsumedFullBufferMsg)
loadNewRows(orientation);
}
if (!rc)
{
PipeRpy_t* lastMsg = NULL;
while (true)
{
PipeRpy_t msg[32];
int bytes = read(msgPipe, msg, sizeof(msg));
DBUG_PRINT("db2i_ioBuffers::pollNextRow",("Pipe returned %d bytes", bytes));
if (unlikely(bytes < 0))
{
DBUG_PRINT("db2i_ioBuffers::pollNextRow", ("Error"));
rc = errno;
break;
}
else if (bytes == 0)
break;
DBUG_ASSERT(bytes % sizeof(msg[0]) == 0);
lastMsg = &msg[bytes / (sizeof(msg[0]))-1];
if (lastMsg->RtnCod || (lastMsg->CumRowCnt == usedRows()))
{
rc = lastMsg->RtnCod;
break;
}
}
*releaseRowNeeded = true;
if (rc == QMY_ERR_END_OF_BLOCK)
rc = 0;
else if (rc == QMY_ERR_END_OF_FILE)
{
// If we reach EOF or end-of-key, DB2 guarantees that no rows will be locked.
rc = HA_ERR_END_OF_FILE;
*releaseRowNeeded = false;
}
else if (rc == QMY_ERR_KEY_NOT_FOUND)
{
rc = HA_ERR_KEY_NOT_FOUND;
*releaseRowNeeded = false;
}
if (lastMsg)
DBUG_PRINT("db2i_ioBuffers::pollNextRow", ("Good data: rc=%d; rows=%d; usedRows=%d", lastMsg->RtnCod, lastMsg->CumRowCnt, (uint32)usedRows()));
if (lastMsg && likely(!rc))
{
if (lastMsg->CumRowCnt < maxRows())
pipeState = PendingFullBufferMsg;
else
pipeState = ConsumedFullBufferMsg;
DBUG_ASSERT(lastMsg->CumRowCnt <= usedRows());
}
DBUG_ASSERT(rowCount() <= getRowCapacity());
}
DBUG_PRINT("db2i_ioBuffers::pollNextRow", ("filledRows: %d, rc: %d", rowCount(), rc));
if (rc) closePipe();
}
/**
Prepare for the destruction of the row buffer storage.
*/
void IOAsyncReadBuffer::prepForFree()
{
interruptRead();
rewind();
IORowBuffer::prepForFree();
}
/**
Initialize the newly allocated storage.
@param sizeChanged Indicates whether the storage capacity is being changed.
*/
void IOAsyncReadBuffer::initAfterAllocate(bool sizeChanged)
{
rewind();
if (sizeChanged || ((void*)rrnList == NULL))
rrnList.realloc(getRowCapacity() * sizeof(uint32));
}
/**
Send an initial read request
@param infile The file (table/index) being read from
@param orientation The orientation to use for this read request
@param rowsToBuffer The number of rows to request each time
@param useAsync Whether reads should be performed asynchronously.
@param key The key to use (if any)
@param keyLength The length of key (if any)
@param keyParts The number of columns in the key (if any)
*/
void IOAsyncReadBuffer::newReadRequest(FILE_HANDLE infile,
char orientation,
uint32 rowsToBuffer,
bool useAsync,
ILEMemHandle key,
int keyLength,
int keyParts)
{
DBUG_ENTER("db2i_ioBuffers::newReadRequest");
DBUG_ASSERT(rowsToBuffer <= getRowCapacity());
#ifndef DBUG_OFF
if (readCursor < rowCount())
DBUG_PRINT("PERF:",("Wasting %d buffered rows!\n", rowCount() - readCursor));
#endif
int fildes[2];
int ileDescriptor = QMY_REUSE;
interruptRead();
if (likely(useAsync))
{
if (rowsToBuffer == 1)
{
// Async provides little or no benefit for single row reads, so we turn it off
DBUG_PRINT("db2i_ioBuffers::newReadRequest", ("Disabling async"));
useAsync = false;
}
else
{
rc = pipe(fildes);
if (rc) DBUG_VOID_RETURN;
// Translate the pipe write descriptor into the equivalent ILE descriptor
rc = fstatx(fildes[1], (struct stat*)&ileDescriptor, sizeof(ileDescriptor), STX_XPFFD_PASE);
if (rc)
{
close(fildes[0]);
close(fildes[1]);
DBUG_VOID_RETURN;
}
pipeState = Untouched;
msgPipe = fildes[0];
DBUG_PRINT("db2i_ioBuffers::newReadRequest", ("Opened pipe %d", fildes[0]));
}
}
file = infile;
readIsAsync = useAsync;
rowsToBlock = rowsToBuffer;
rewind();
maxRows() = 1;
rc = getBridge()->expectErrors(QMY_ERR_END_OF_BLOCK, QMY_ERR_LOB_SPACE_TOO_SMALL)
->read(file,
ptr(),
accessIntent,
commitLevel,
orientation,
useAsync,
rrnList,
key,
keyLength,
keyParts,
ileDescriptor);
// Having shared the pipe with ILE, we relinquish our claim on the write end
// of the pipe.
if (useAsync)
close(fildes[1]);
// If we reach EOF or end-of-key, DB2 guarantees that no rows will be locked.
if (rc == QMY_ERR_END_OF_FILE)
{
rc = HA_ERR_END_OF_FILE;
*releaseRowNeeded = false;
}
else if (rc == QMY_ERR_KEY_NOT_FOUND)
{
if (rowCount())
rc = HA_ERR_END_OF_FILE;
else
rc = HA_ERR_KEY_NOT_FOUND;
*releaseRowNeeded = false;
}
else
*releaseRowNeeded = true;
DBUG_VOID_RETURN;
}

View File

@ -1,416 +0,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.
*/
/**
@file db2i_ioBuffers.h
@brief Buffer classes used for interacting with QMYSE read/write buffers.
*/
#include "db2i_validatedPointer.h"
#include "mysql_priv.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <as400_types.h>
// Needed for compilers which do not include fstatx in standard headers.
extern "C" int fstatx(int, struct stat *, int, int);
/**
Basic row buffer
Provides the basic structure and methods needed for communicating
with QMYSE I/O APIs.
@details All QMYSE I/O apis use a buffer that is structured as two integer
row counts (max and used) and storage for some number of rows. The row counts
are both input and output for the API, and their usage depends on the
particular API invoked. This class encapsulates that buffer definition.
*/
class IORowBuffer
{
public:
IORowBuffer() : allocSize(0), rowLength(0) {;}
~IORowBuffer() { freeBuf(); }
ValidatedPointer<char>& ptr() { return data; }
/**
Sets up the buffer to hold the size indicated.
@param rowLen length of the rows that will be stored in this buffer
@param nullMapOffset position of null map within each row
@param size buffer size requested
*/
void allocBuf(uint32 rowLen, uint16 nullMapOffset, uint32 size)
{
nullOffset = nullMapOffset;
uint32 newSize = size + sizeof(BufferHdr_t);
// If the internal structure of the row is changing, we need to
// remember this and notify the subclasses via initAfterAllocate();
bool formatChanged = ((size/rowLen) != rowCapacity);
if (newSize > allocSize)
{
this->freeBuf();
data.alloc(newSize);
if (likely((void*)data))
allocSize = newSize;
}
if (likely((void*)data))
{
DBUG_ASSERT((uint64)(void*)data % 16 == 0);
rowLength = rowLen;
rowCapacity = size / rowLength;
initAfterAllocate(formatChanged);
}
else
{
allocSize = 0;
rowCapacity = 0;
}
DBUG_PRINT("db2i_ioBuffers::allocBuf",("rowCapacity = %d", rowCapacity));
}
void zeroBuf()
{
memset(data, 0, allocSize);
}
void freeBuf()
{
if (likely(allocSize))
{
prepForFree();
DBUG_PRINT("IORowBuffer::freeBuf",("Freeing 0x%p", (char*)data));
data.dealloc();
}
}
char* getRowN(uint32 n)
{
if (unlikely(n >= getRowCapacity()))
return NULL;
return (char*)data + sizeof(BufferHdr_t) + (rowLength * n);
};
uint32 getRowCapacity() const {return rowCapacity;}
uint32 getRowNullOffset() const {return nullOffset;}
uint32 getRowLength() const {return rowLength;}
protected:
/**
Called prior to freeing buffer storage so that subclasses can do
any required cleanup
*/
virtual void prepForFree()
{
allocSize = 0;
rowCapacity = 0;
}
/**
Called after buffer storage so that subclasses can do any required setup.
*/
virtual void initAfterAllocate(bool sizeChanged) { return;}
ValidatedPointer<char> data;
uint32 allocSize;
uint32 rowCapacity;
uint32 rowLength;
uint16 nullOffset;
uint32& usedRows() const { return ((BufferHdr_t*)(char*)data)->UsedRowCnt; }
uint32& maxRows() const {return ((BufferHdr_t*)(char*)data)->MaxRowCnt; }
};
/**
Write buffer
Implements methods for inserting data into a row buffer for use with the
QMY_WRITE and QMY_UPDATE APIs.
@details The max row count defines how many rows are in the buffer. The used
row count is updated by QMYSE to indicate how many rows have been
successfully written.
*/
class IOWriteBuffer : public IORowBuffer
{
public:
bool endOfBuffer() const {return (maxRows() == getRowCapacity());}
char* addRow()
{
return getRowN(maxRows()++);
}
void resetAfterWrite()
{
maxRows() = 0;
}
void deleteRow()
{
--maxRows();
}
uint32 rowCount() const {return maxRows();}
uint32 rowsWritten() const {return usedRows()-1;}
private:
void initAfterAllocate(bool sizeChanged) {maxRows() = 0; usedRows() = 0;}
};
/**
Read buffer
Implements methods for reading data from and managing a row buffer for use
with the QMY_READ APIs. This is primarily for use with metainformation queries.
*/
class IOReadBuffer : public IORowBuffer
{
public:
IOReadBuffer() {;}
IOReadBuffer(uint32 rows, uint32 rowLength)
{
allocBuf(rows, 0, rows * rowLength);
maxRows() = rows;
}
uint32 rowCount() {return usedRows();}
void setRowsToProcess(uint32 rows) { maxRows() = rows; }
};
/**
Read buffer
Implements methods for reading data from and managing a row buffer for use
with the QMY_READ APIs.
@details This class supports both sync and async read modes. The max row
count defines the number of rows that are requested to be read. The used row
count defines how many rows have been read. Sync mode is reasonably
straightforward, but async mode has a complex system of communicating with
QMYSE that is optimized for low latency. In async mode, the used row count is
updated continuously by QMYSE as rows are read. At the same time, messages are
sent to the associated pipe indicating that a row has been read. As long as
the internal read cursor lags behind the used row count, the pipe is never
consulted. But if the internal read cursor "catches up to" the used row count,
then we block on the pipe until we find a message indicating that a new row
has been read or that an error has occurred.
*/
class IOAsyncReadBuffer : public IOReadBuffer
{
public:
IOAsyncReadBuffer() :
file(0), readIsAsync(false), msgPipe(QMY_REUSE), bridge(NULL)
{
}
~IOAsyncReadBuffer()
{
interruptRead();
rrnList.dealloc();
}
/**
Signal read operation complete
Indicates that the storage engine requires no more data from the table.
Must be called between calls to newReadRequest().
*/
void endRead()
{
#ifndef DBUG_OFF
if (readCursor < rowCount())
DBUG_PRINT("PERF:",("Wasting %d buffered rows!\n", rowCount() - readCursor));
#endif
interruptRead();
file = 0;
bridge = NULL;
}
/**
Update data that may change on each read operation
*/
void update(char newAccessIntent,
bool* newReleaseRowNeeded,
char commitLvl)
{
accessIntent = newAccessIntent;
releaseRowNeeded = newReleaseRowNeeded;
commitLevel = commitLvl;
}
/**
Read the next row in the table.
Return a pointer to the next row in the table, where "next" is defined
by the orientation.
@param orientaton
@param[out] rrn The relative record number of the row returned. Not reliable
if NULL is returned by this function.
@return Pointer to the row. Null if no more rows are available or an error
occurred.
*/
char* readNextRow(char orientation, uint32& rrn)
{
DBUG_PRINT("db2i_ioBuffers::readNextRow", ("readCursor: %d, filledRows: %d, rc: %d", readCursor, rowCount(), rc));
while (readCursor >= rowCount() && !rc)
{
if (!readIsAsync)
loadNewRows(orientation);
else
pollNextRow(orientation);
}
if (readCursor >= rowCount())
return NULL;
rrn = rrnList[readCursor];
return getRowN(readCursor++);
}
/**
Retrieve the return code generated by the last operation.
@return The return code, translated to the appropriate HA_ERR_*
value if possible.
*/
int32 lastrc()
{
return db2i_ileBridge::translateErrorCode(rc);
}
void rewind()
{
readCursor = 0;
rc = 0;
usedRows() = 0;
}
bool reachedEOD() { return EOD; }
void newReadRequest(FILE_HANDLE infile,
char orientation,
uint32 rowsToBuffer,
bool useAsync,
ILEMemHandle key,
int keyLength,
int keyParts);
private:
/**
End any running async read operation.
*/
void interruptRead()
{
closePipe();
if (file && readIsAsync && (rc == 0) && (rowCount() < getRowCapacity()))
{
DBUG_PRINT("IOReadBuffer::interruptRead", ("PERF: Interrupting %d", (uint32)file));
getBridge()->readInterrupt(file);
}
}
void closePipe()
{
if (msgPipe != QMY_REUSE)
{
DBUG_PRINT("db2i_ioBuffers::closePipe", ("Closing pipe %d", msgPipe));
close(msgPipe);
msgPipe = QMY_REUSE;
}
}
/**
Get a pointer to the active ILE bridge.
Getting the bridge pointer is (relatively) expensive, so we cache
it off for each operation.
*/
db2i_ileBridge* getBridge()
{
if (unlikely(bridge == NULL))
{
bridge = db2i_ileBridge::getBridgeForThread();
}
return bridge;
}
void drainPipe();
void pollNextRow(char orientation);
void prepForFree();
void initAfterAllocate(bool sizeChanged);
void loadNewRows(char orientation);
uint32 readCursor; // Read position within buffer
int32 rc; // Last return code received
ValidatedPointer<uint32> rrnList; // Receiver for list of rrns
char accessIntent; // The access intent for this read
char commitLevel; // What isolation level should be used
char EOD; // Whether end-of-data was hit
char readIsAsync; // Are reads to be done asynchronously?
bool* releaseRowNeeded;
/* Does the caller need to release the current row when finished reading */
FILE_HANDLE file; // The file to be read
int msgPipe;
/* The read descriptor of the pipe used to pass messages during async reads */
db2i_ileBridge* bridge; // Cached pointer to bridge
uint32 rowsToBlock; // Number of rows to request
enum
{
ConsumedFullBufferMsg,
PendingFullBufferMsg,
Untouched
} pipeState;
/* The state of the async read message pipe */
};

View File

@ -1,129 +0,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_MISC_H
#define DB2I_MISC_H
/**
Undelimit quote-delimited DB2 names in-place
*/
void stripExtraQuotes(char* name, uint maxLen)
{
char* oldName = (char*)sql_strdup(name);
uint i = 0;
uint j = 0;
do
{
name[j] = oldName[i];
if (oldName[i] == '"' && oldName[i+1] == '"')
++i;
} while (++j < maxLen && oldName[++i]);
if (j == maxLen)
--j;
name[j] = 0;
}
/**
Convert a MySQL identifier name into a DB2 compatible format
@parm input The MySQL name
@parm output The DB2 name
@parm outlen The amount of space allocated for output
@parm delimit Should delimiting quotes be placed around the converted name?
@parm delimitQuotes Should quotes in the MySQL be delimited with additional quotes?
@return FALSE if output was too small and name was truncated; TRUE otherwise
*/
bool convertMySQLNameToDB2Name(const char* input,
char* output,
size_t outlen,
bool delimit = true,
bool delimitQuotes = true)
{
uint o = 0;
if (delimit)
output[o++] = '"';
uint i = 0;
do
{
output[o] = input[i];
if (delimitQuotes && input[i] == '"')
output[++o] = '"';
} while (++o < outlen-2 && input[++i]);
if (delimit)
output[o++] = '"';
output[min(o, outlen-1)] = 0; // This isn't the most user-friendly way to handle overflows,
// but at least its safe.
return (o <= outlen-1);
}
bool isOrdinaryIdentifier(const char* s)
{
while (*s)
{
if (my_isupper(system_charset_info, *s) ||
my_isdigit(system_charset_info, *s) ||
(*s == '_') ||
(*s == '@') ||
(*s == '$') ||
(*s == '#') ||
(*s == '"'))
++s;
else
return false;
}
return true;
}
/**
Fill memory with a 16-bit word.
@param p Pointer to space to fill.
@param v Value to fill
@param l Length of space (in 16-bit words)
*/
void memset16(void* p, uint16 v, size_t l)
{
uint16* p2=(uint16*)p;
while (l--)
{
*(p2++) = v;
}
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,686 +0,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 "ha_ibmdb2i.h"
/* Helper function for records_in_range.
Input: Bitmap of used key parts.
Output: Number of used key parts. */
static inline int getKeyCntFromMap(key_part_map keypart_map)
{
int cnt = 0;
while (keypart_map)
{
keypart_map = keypart_map >> 1;
cnt++;
}
return (cnt);
}
/**
@brief
Given a starting key and an ending key, estimate the number of rows that
will exist between the two keys.
INPUT
inx Index to use
min_key Min key. Is NULL if no min range
max_key Max key. Is NULL if no max range
NOTES
min_key.flag can have one of the following values:
HA_READ_KEY_EXACT Include the key in the range
HA_READ_AFTER_KEY Don't include key in range
max_key.flag can have one of the following values:
HA_READ_BEFORE_KEY Don't include key in range
HA_READ_AFTER_KEY Include all 'end_key' values in the range
RETURN
HA_POS_ERROR Error or the storage engine cannot estimate the number of rows
1 There are no matching keys in the given range
n > 0 There are approximately n rows in the range
*/
ha_rows ha_ibmdb2i::records_in_range(uint inx,
key_range *min_key,
key_range *max_key)
{
DBUG_ENTER("ha_ibmdb2i::records_in_range");
int rc = 0; // Return code
ha_rows rows = 0; // Row count returned to caller of this method
uint32 spcLen; // Length of space passed to DB2
uint32 keyCnt; // Number of fields in the key composite
uint32 literalCnt = 0; // Number of literals
uint32 boundsOff; // Offset from beginning of space to range bounds
uint32 litDefOff; // Offset from beginning of space to literal definitions
uint32 literalsOff; // Offset from beginning of space to literal values
uint32 cutoff = 0; // Early exit cutoff (currently not used)
uint64 recCnt; // Row count from DB2
uint16 rtnCode; // Return code from DB2
Bounds* boundsPtr; // Pointer to a pair of range bounds
Bound* boundPtr; // Pointer to a single (high or low) range bound
LitDef* litDefPtr; // Pointer to a literal definition
char* literalsPtr; // Pointer to the start of all literal values
char* literalPtr; // Pointer to the start of this literal value
char* tempPtr; // Temporary pointer
char* tempMinPtr; // Temporary pointer into min_key
int minKeyCnt = 0; // Number of fields in the min_key composite
int maxKeyCnt = 0; // Number of fields in the max_key composite
size_t tempLen = 0; // Temporary length
uint16 DB2FieldWidth = 0; // DB2 field width
uint32 workFieldLen = 0; // Length of workarea needed for CCSID conversions
bool overrideInclusion; // Indicator for inclusion/exclusion
char* endOfLiteralPtr; // Pointer to the end of this literal
char* endOfMinPtr; // Pointer to end of min_key
uint16 endByte = 0; // End byte of char or graphic literal (padding not included)
bool reuseLiteral; // Indicator that hi and lo bounds use same literal
char* minPtr = NULL; // Work pointer for traversing min_key
char* maxPtr = NULL; // Work pointer for traversing max_key
/*
Handle the special case of 'x < null' anywhere in the key range. There are
no values less than null, but return 1 so that MySQL does not assume
the empty set for the query.
*/
if (min_key != NULL && max_key != NULL &&
min_key->flag == HA_READ_AFTER_KEY && max_key->flag == HA_READ_BEFORE_KEY &&
min_key->length == max_key->length &&
(memcmp((uchar*)min_key->key,(uchar*)max_key->key,min_key->length)==0))
{
DBUG_PRINT("ha_ibmdb2i::records_in_range",("Estimate 1 row for key %d; special case: < null", inx));
DBUG_RETURN((ha_rows) 1 );
}
/*
Determine the number of fields in the key composite.
*/
if (min_key)
{
minKeyCnt = getKeyCntFromMap(min_key->keypart_map);
minPtr = (char*)min_key->key;
}
if (max_key)
{
maxKeyCnt = getKeyCntFromMap(max_key->keypart_map);
maxPtr = (char*)max_key->key;
}
keyCnt = maxKeyCnt >= minKeyCnt ? maxKeyCnt : minKeyCnt;
/*
Handle the special case where MySQL does not pass either a min or max
key range. In this case, set the key count to 1 (knowing that there
is at least one key field) to flow through and create one bounds structure.
When both the min and max key ranges are nil, the bounds structure will
specify positive and negative infinity and DB2 will estimate the total
number of rows. */
if (keyCnt == 0)
keyCnt = 1;
/*
Allocate the space needed to pass range information to DB2. The
space must be large enough to store the following:
- one pair of bounds (high and low) per field in the key composite
- one literal definition per literal value
- the literal values
- work area for literal CCSID conversions
Since we don't know yet how many of these structures are needed,
allocate enough space for the maximum that we will possibly need.
The workarea for the literal conversion must be big enough to hold the
largest of the DB2 key fields.
*/
KEY& curKey = table->key_info[inx];
for (int i = 0; i < keyCnt; i++)
{
DB2FieldWidth =
db2Table->db2Field(curKey.key_part[i].field->field_index).getByteLengthInRecord();
if (DB2FieldWidth > workFieldLen)
workFieldLen = DB2FieldWidth; // Get length of largest DB2 field
tempLen = tempLen + DB2FieldWidth; // Tally the DB2 field lengths
}
spcLen = (sizeof(Bounds)*keyCnt) + (sizeof(LitDef)*keyCnt*2) + (tempLen*2) + workFieldLen;
ValidatedPointer<char> spcPtr(spcLen); // Pointer to space passed to DB2
memset(spcPtr, 0, spcLen); // Clear the allocated space
/*
Set addressability to the various sections of the DB2 interface space.
*/
boundsOff = 0; // Range bounds are at the start of the space
litDefOff = sizeof(Bounds) * keyCnt; // Literal defs follow all the range bounds
literalsOff = litDefOff + (sizeof(LitDef) * keyCnt * 2); // Literal values are last
boundsPtr = (Bounds_t*)(void*)spcPtr; // Address first bounds structure
tempPtr = (char*)((char*)spcPtr + litDefOff);
litDefPtr = (LitDef_t*)tempPtr; // Address first literal definition
tempPtr = (char*)((char*)spcPtr + literalsOff);
literalsPtr = (char*)tempPtr; // Address start of literal values
literalPtr = literalsPtr; // Address first literal value
/*
For each key part, build the low (min) and high (max) DB2 range bounds.
If literals are specified in the MySQL range, build DB2 literal
definitions and store the literal values for access by DB2.
If no value is specified for a key part, assume infinity. Negative
infinity will cause processing to start at the first index entry.
Positive infinity will cause processing to end at the last index entry.
When infinity is specified in a bound, inclusion/exclusion and position
are ignored, and there is no literal definition or literal value for
the bound.
If the keypart value is null, the null indicator is set in the range
bound and the other fields in the bound are ignored. When the bound is
null, only index entries with the null value will be included in the
estimate. If one bound is null, both bounds must be null. When the bound
is not null, the data offset and length must be set, and the literal
value stored for access by DB2.
*/
for (int partsInUse = 0; partsInUse < keyCnt; ++partsInUse)
{
Field *field= curKey.key_part[partsInUse].field;
overrideInclusion = false;
reuseLiteral = false;
endOfLiteralPtr = NULL;
/*
Build the low bound for the key range.
*/
if ((partsInUse + 1) > minKeyCnt) // if no min_key info for this part
boundsPtr->LoBound.Infinity[0] = QMY_NEG_INFINITY; // select...where 3 between x and y
else
{
if ((curKey.key_part[partsInUse].null_bit) && (char*)minPtr[0])
{ // min_key is null
if (max_key == NULL ||
((partsInUse + 1) > maxKeyCnt)) // select...where x='ab' and y=null and z != 'c'
boundsPtr->LoBound.Infinity[0] = QMY_NEG_INFINITY; // select...where x not null or
// select...where x > null
else // max_key is not null
{
if (min_key->flag == HA_READ_KEY_EXACT)
boundsPtr->LoBound.IsNull[0] = QMY_YES; // select...where x is null
else
{
if ((char*)maxPtr[0])
boundsPtr->LoBound.IsNull[0] = QMY_YES; // select...where a = null and b < 5 (max-before)
// select...where a='a' and b is null and c !='a' (max-after)
else
boundsPtr->LoBound.Infinity[0] = QMY_NEG_INFINITY; // select...where x < y
}
} // end min_key is null
}
else // min_key is not null
{
if (literalCnt) litDefPtr = litDefPtr + 1;
literalCnt = literalCnt + 1;
boundsPtr->LoBound.Position = literalCnt;
/*
Determine inclusion or exclusion.
*/
if (min_key->flag == HA_READ_KEY_EXACT || //select...where a like 'this%'
/* An example for the following conditions is 'select...where a = 5 and b > null'. */
(max_key &&
(memcmp((uchar*)minPtr,(uchar*)maxPtr,
curKey.key_part[partsInUse].store_length)==0)))
{
if ((min_key->flag != HA_READ_KEY_EXACT) ||
(max_key &&
(memcmp((uchar*)minPtr,(uchar*)maxPtr,
curKey.key_part[partsInUse].store_length)==0)))
overrideInclusion = true; // Need inclusion for both min and max
}
else
boundsPtr->LoBound.Embodiment[0] = QMY_EXCLUSION;
litDefPtr->FieldNbr = field->field_index + 1;
DB2Field& db2Field = db2Table->db2Field(field->field_index);
litDefPtr->DataType = db2Field.getType();
/*
Convert the literal to DB2 format
*/
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))
{
// Most of the code is required by the considerable wrangling needed
// to prepare partial keys for use by DB2
// 1. UTF8 (CCSID 1208) data can be copied across unmodified if it is
// utf8_bin. Otherwise, we need to convert the min and max
// characters into the min and max characters employed
// by the DB2 sort sequence. This is complicated by the fact that
// the character widths are not always equal.
// 2. Likewise, UCS2 (CCSID 13488) data can be copied across unmodified
// if it is ucs2_bin or ucs2_general_ci. Otherwise, we need to
// convert the min and max characters into the min and max characters
// employed by the DB2 sort sequence.
// 3. All other data will use standard iconv conversions. If an
// unconvertible character is encountered, we assume it is the min
// char and fill the remainder of the DB2 key with 0s. This may not
// always be accurate, but it is probably sufficient for range
// estimations.
const char* keyData = minPtr+((curKey.key_part[partsInUse].null_bit)? 1 : 0);
char* db2Data = literalPtr;
uint16 outLen = db2Field.getByteLengthInRecord();
uint16 inLen;
if (litDefPtr->DataType == QMY_VARCHAR ||
litDefPtr->DataType == QMY_VARGRAPHIC)
{
inLen = *(uint8*)keyData + ((*(uint8*)(keyData+1)) << 8);
keyData += 2;
outLen -= sizeof(uint16);
db2Data += sizeof(uint16);
}
else
{
inLen = field->max_display_length();
}
size_t convertedBytes = 0;
if (db2Field.getCCSID() == 1208)
{
DBUG_ASSERT(inLen <= outLen);
if (strcmp(field->charset()->name, "utf8_bin"))
{
const char* end = keyData+inLen;
const char* curKey = keyData;
char* curDB2 = db2Data;
uint32 min = field->charset()->min_sort_char;
while ((curKey < end) && (curDB2 < db2Data+outLen-3))
{
my_wc_t temp;
int len = field->charset()->cset->mb_wc(field->charset(),
&temp,
(const uchar*)curKey,
(const uchar*)end);
if (temp != min)
{
DBUG_ASSERT(len <= 3);
switch (len)
{
case 3: *(curDB2+2) = *(curKey+2);
case 2: *(curDB2+1) = *(curKey+1);
case 1: *(curDB2) = *(curKey);
}
curDB2 += len;
}
else
{
*(curDB2++) = 0xEF;
*(curDB2++) = 0xBF;
*(curDB2++) = 0xBF;
}
curKey += len;
}
convertedBytes = curDB2 - db2Data;
}
else
{
memcpy(db2Data, keyData, inLen);
convertedBytes = inLen;
}
rc = 0;
}
else if (db2Field.getCCSID() == 13488)
{
DBUG_ASSERT(inLen <= outLen);
if (strcmp(field->charset()->name, "ucs2_bin") &&
strcmp(field->charset()->name, "ucs2_general_ci"))
{
const char* end = keyData+inLen;
const uint16* curKey = (uint16*)keyData;
uint16* curDB2 = (uint16*)db2Data;
uint16 min = field->charset()->min_sort_char;
while (curKey < (uint16*)end)
{
if (*curKey != min)
*curDB2 = *curKey;
else
*curDB2 = 0xFFFF;
++curKey;
++curDB2;
}
}
else
{
memcpy(db2Data, keyData, inLen);
}
convertedBytes = inLen;
rc = 0;
}
else
{
rc = convertFieldChars(toDB2,
field->field_index,
keyData,
db2Data,
inLen,
outLen,
&convertedBytes,
true);
if (rc == DB2I_ERR_ILL_CHAR)
{
// If an illegal character is encountered, we fill the remainder
// of the key with 0x00. This was implemented as a corollary to
// Bug#45012, though it should probably remain even after that
// bug is fixed.
memset(db2Data+convertedBytes, 0x00, outLen-convertedBytes);
convertedBytes = outLen;
rc = 0;
}
}
if (!rc &&
(litDefPtr->DataType == QMY_VARGRAPHIC ||
litDefPtr->DataType == QMY_VARCHAR))
{
*(uint16*)(db2Data-sizeof(uint16)) =
convertedBytes / (litDefPtr->DataType == QMY_VARGRAPHIC ? 2 : 1);
}
}
else // Non-character fields
{
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();
literalPtr = literalPtr + litDefPtr->Length; // Bump pointer for next literal
}
/* If there is a max_key value for this field, and if the max_key value is
the same as the min_key value, then the low bound literal can be reused
for the high bound literal. This eliminates the overhead of copying and
converting the same value twice. */
if (max_key && ((partsInUse + 1) <= maxKeyCnt) &&
(memcmp((uchar*)minPtr,(uchar*)maxPtr,
curKey.key_part[partsInUse].store_length)==0 || endOfLiteralPtr))
reuseLiteral = true;
minPtr += curKey.key_part[partsInUse].store_length;
}
/*
Build the high bound for the key range.
*/
if (max_key == NULL || ((partsInUse + 1) > maxKeyCnt))
boundsPtr->HiBound.Infinity[0] = QMY_POS_INFINITY;
else
{
if ((curKey.key_part[partsInUse].null_bit) && (char*)maxPtr[0])
{
if (min_key == NULL)
boundsPtr->HiBound.Infinity[0] = QMY_POS_INFINITY;
else
boundsPtr->HiBound.IsNull[0] = QMY_YES; // select...where x is null
}
else // max_key field is not null
{
if (boundsPtr->LoBound.IsNull[0] == QMY_YES) // select where x < 10 or x is null
{
rc = HA_POS_ERROR;
break;
}
if (!reuseLiteral)
{
if (literalCnt)
litDefPtr = litDefPtr + 1;
literalCnt = literalCnt + 1;
litDefPtr->FieldNbr = field->field_index + 1;
DB2Field& db2Field = db2Table->db2Field(field->field_index);
litDefPtr->DataType = db2Field.getType();
/*
Convert the literal to DB2 format
*/
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))
{
// We need to handle char fields in a special way in order to account
// for partial keys. Refer to the note above for a description of the
// basic design.
char* keyData = maxPtr+((curKey.key_part[partsInUse].null_bit)? 1 : 0);
char* db2Data = literalPtr;
uint16 outLen = db2Field.getByteLengthInRecord();
uint16 inLen;
if (litDefPtr->DataType == QMY_VARCHAR ||
litDefPtr->DataType == QMY_VARGRAPHIC)
{
inLen = *(uint8*)keyData + ((*(uint8*)(keyData+1)) << 8);
keyData += 2;
outLen -= sizeof(uint16);
db2Data += sizeof(uint16);
}
else
{
inLen = field->max_display_length();
}
size_t convertedBytes;
if (db2Field.getCCSID() == 1208)
{
if (strcmp(field->charset()->name, "utf8_bin"))
{
const char* end = keyData+inLen;
const char* curKey = keyData;
char* curDB2 = db2Data;
uint32 max = field->charset()->max_sort_char;
while (curKey < end && (curDB2 < db2Data+outLen-3))
{
my_wc_t temp;
int len = field->charset()->cset->mb_wc(field->charset(), &temp, (const uchar*)curKey, (const uchar*)end);
if (temp != max)
{
DBUG_ASSERT(len <= 3);
switch (len)
{
case 3: *(curDB2+2) = *(curKey+2);
case 2: *(curDB2+1) = *(curKey+1);
case 1: *(curDB2) = *(curKey);
}
curDB2 += len;
}
else
{
*(curDB2++) = 0xE4;
*(curDB2++) = 0xB6;
*(curDB2++) = 0xBF;
}
curKey += len;
}
convertedBytes = curDB2 - db2Data;
}
else
{
DBUG_ASSERT(inLen <= outLen);
memcpy(db2Data, keyData, inLen);
convertedBytes = inLen;
}
rc = 0;
}
else if (db2Field.getCCSID() == 13488)
{
if (strcmp(field->charset()->name, "ucs2_bin") &&
strcmp(field->charset()->name, "ucs2_general_ci"))
{
char* end = keyData+inLen;
uint16* curKey = (uint16*)keyData;
uint16* curDB2 = (uint16*)db2Data;
uint16 max = field->charset()->max_sort_char;
while (curKey < (uint16*)end)
{
if (*curKey != max)
*curDB2 = *curKey;
else
*curDB2 = 0x4DBF;
++curKey;
++curDB2;
}
}
else
{
memcpy(db2Data, keyData, outLen);
}
rc = 0;
}
else
{
size_t substituteChars = 0;
rc = convertFieldChars(toDB2,
field->field_index,
keyData,
db2Data,
inLen,
outLen,
&convertedBytes,
true,
&substituteChars);
if (rc == DB2I_ERR_ILL_CHAR)
{
// If an illegal character is encountered, we fill the remainder
// of the key with 0xFF. This was implemented to work around
// Bug#45012, though it should probably remain even after that
// bug is fixed.
memset(db2Data+convertedBytes, 0xFF, outLen-convertedBytes);
rc = 0;
}
else if ((substituteChars &&
(litDefPtr->DataType == QMY_VARCHAR ||
litDefPtr->DataType == QMY_CHAR)) ||
strcmp(field->charset()->name, "cp1251_bulgarian_ci") == 0)
{
// When iconv translates the max_sort_char with a substitute
// character, we have no way to know whether this affects
// the sort order of the key. Therefore, to be safe, when
// we know that substitute characters have been used in a
// single-byte string, we traverse the translated key
// in reverse, replacing substitue characters with 0xFF, which
// always sorts with the greatest weight in DB2 sort sequences.
// cp1251_bulgarian_ci is also handled this way because the
// max_sort_char is a control character which does not sort
// equivalently in DB2.
DBUG_ASSERT(inLen == outLen);
char* tmpKey = keyData + inLen - 1;
char* tmpDB2 = db2Data + outLen - 1;
while (*tmpKey == field->charset()->max_sort_char &&
*tmpDB2 != 0xFF)
{
*tmpDB2 = 0xFF;
--tmpKey;
--tmpDB2;
}
}
}
if (!rc &&
(litDefPtr->DataType == QMY_VARGRAPHIC ||
litDefPtr->DataType == QMY_VARCHAR))
{
*(uint16*)(db2Data-sizeof(uint16)) =
outLen / (litDefPtr->DataType == QMY_VARGRAPHIC ? 2 : 1);
}
}
else
{
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();
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);
}

View File

@ -1,98 +0,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_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

View File

@ -1,86 +0,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_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;
}

View File

@ -1,151 +0,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_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

View File

@ -1,162 +0,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_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

File diff suppressed because it is too large Load Diff

View File

@ -1,822 +0,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.
*/
/** @file ha_ibmdb2i.h
@brief
@note
@see
*/
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
#include "as400_types.h"
#include "as400_protos.h"
#include "db2i_global.h"
#include "db2i_ileBridge.h"
#include "builtins.h"
#include "db2i_misc.h"
#include "db2i_file.h"
#include "db2i_blobCollection.h"
#include "db2i_collationSupport.h"
#include "db2i_validatedPointer.h"
#include "db2i_ioBuffers.h"
#include "db2i_errors.h"
#include "db2i_sqlStatementStream.h"
/** @brief
IBMDB2I_SHARE is a structure that will be shared among all open handlers.
It is used to describe the underlying table definition, and it caches
table statistics.
*/
struct IBMDB2I_SHARE {
char *table_name;
uint table_name_length,use_count;
pthread_mutex_t mutex;
THR_LOCK lock;
db2i_table* db2Table;
class CStats
{
public:
void cacheUpdateTime(time_t time)
{update_time = time; initFlag |= lastModTime;}
time_t getUpdateTime() const
{return update_time;}
void cacheRowCount(ha_rows rows)
{records = rows; initFlag |= rowCount;}
ha_rows getRowCount() const
{return records;}
void cacheDelRowCount(ha_rows rows)
{deleted = rows; initFlag |= deletedRowCount;}
ha_rows getDelRowCount() const
{return deleted;}
void cacheMeanLength(ulong len)
{mean_rec_length = len; initFlag |= meanRowLen;}
ulong getMeanLength()
{return mean_rec_length;}
void cacheAugmentedDataLength(ulong len)
{data_file_length = len; initFlag |= ioCount;}
ulong getAugmentedDataLength()
{return data_file_length;}
bool isInited(uint flags)
{return initFlag & flags;}
void invalidate(uint flags)
{initFlag &= ~flags;}
private:
uint initFlag;
time_t update_time;
ha_rows records;
ha_rows deleted;
ulong mean_rec_length;
ulong data_file_length;
} cachedStats;
};
class ha_ibmdb2i: public handler
{
THR_LOCK_DATA lock; ///< MySQL lock
IBMDB2I_SHARE *share; ///< Shared lock info
// The record we are positioned on, together with the handle used to get
// i.
uint32 currentRRN;
uint32 rrnAssocHandle;
// Dup key values needed by info()
uint32 lastDupKeyRRN;
uint32 lastDupKeyID;
bool returnDupKeysImmediately;
// Dup key value need by update()
bool onDupUpdate;
db2i_table* db2Table;
// The file handle of the PF or LF being accessed by the current operation.
FILE_HANDLE activeHandle;
// The file handle of the underlying PF
FILE_HANDLE dataHandle;
// Array of file handles belonging to the underlying LFs
FILE_HANDLE* indexHandles;
// Flag to indicate whether a call needs to be made to unlock a row when
// a read operation has ended. DB2 will handle row unlocking as we move
// through rows, but if an operation ends before we reach the end of a file,
// DB2 needs to know to unlock the last row read.
bool releaseRowNeeded;
// Pointer to a definition of the layout of the row buffer for the file
// described by activeHandle
const db2i_file::RowFormat* activeFormat;
IORowBuffer keyBuf;
uint32 keyLen;
IOWriteBuffer multiRowWriteBuf;
IOAsyncReadBuffer multiRowReadBuf;
IOAsyncReadBuffer* activeReadBuf;
IOWriteBuffer* activeWriteBuf;
BlobCollection* blobReadBuffers; // Dynamically allocated per query and used
// to manage the buffers used for reading LOBs
ValidatedPointer<char>* blobWriteBuffers;
// Return codes are not used/honored by rnd_init and start_bulk_insert
// so we need a way to signal the failure "downstream" to subsequent
// functions.
int last_rnd_init_rc;
int last_index_init_rc;
int last_start_bulk_insert_rc;
// end_bulk_insert may get called twice for a single start_bulk_insert
// This is our way to do cleanup only once.
bool outstanding_start_bulk_insert;
// Auto_increment 'increment by' value needed by write_row()
uint32 incrementByValue;
bool default_identity_value;
// Flags and values used during write operations for auto_increment processing
bool autoIncLockAcquired;
bool got_auto_inc_values;
uint64 next_identity_value;
// The access intent indicated by the last external_locks() call.
// May be either QMY_READ or QMY_UPDATABLE
char accessIntent;
char readAccessIntent;
ha_rows* indexReadSizeEstimates;
MEM_ROOT conversionBufferMemroot;
bool forceSingleRowRead;
bool readAllColumns;
bool invalidDataFound;
db2i_ileBridge* cachedBridge;
ValidatedObject<volatile uint32> curConnection;
uint16 activeReferences;
public:
ha_ibmdb2i(handlerton *hton, TABLE_SHARE *table_arg);
~ha_ibmdb2i();
const char *table_type() const { return "IBMDB2I"; }
const char *index_type(uint inx) { return "RADIX"; }
const key_map *keys_to_use_for_scanning() { return &key_map_full; }
const char **bas_ext() const;
ulonglong table_flags() const
{
return HA_NULL_IN_KEY | HA_REC_NOT_IN_SEQ | HA_AUTO_PART_KEY |
HA_PARTIAL_COLUMN_READ |
HA_DUPLICATE_POS | HA_NO_PREFIX_CHAR_KEYS |
HA_HAS_RECORDS | HA_BINLOG_ROW_CAPABLE | HA_REQUIRES_KEY_COLUMNS_FOR_DELETE |
HA_CAN_INDEX_BLOBS;
}
ulong index_flags(uint inx, uint part, bool all_parts) const;
// Note that we do not implement max_supported_record_length.
// We'll let create fail accordingly if the row is
// too long. This allows us to hide the fact that varchars > 32K are being
// implemented as DB2 LOBs.
uint max_supported_keys() const { return 4000; }
uint max_supported_key_parts() const { return MAX_DB2_KEY_PARTS; }
uint max_supported_key_length() const { return 32767; }
uint max_supported_key_part_length() const { return 32767; }
double read_time(uint index, uint ranges, ha_rows rows);
double scan_time();
int open(const char *name, int mode, uint test_if_locked);
int close(void);
int write_row(uchar * buf);
int update_row(const uchar * old_data, uchar * new_data);
int delete_row(const uchar * buf);
int index_init(uint idx, bool sorted);
int index_read(uchar * buf, const uchar * key,
uint key_len, enum ha_rkey_function find_flag);
int index_next(uchar * buf);
int index_read_last(uchar * buf, const uchar * key, uint key_len);
int index_next_same(uchar *buf, const uchar *key, uint keylen);
int index_prev(uchar * buf);
int index_first(uchar * buf);
int index_last(uchar * buf);
int rnd_init(bool scan);
int rnd_end();
int rnd_next(uchar *buf);
int rnd_pos(uchar * buf, uchar *pos);
void position(const uchar *record);
int info(uint);
ha_rows records();
int extra(enum ha_extra_function operation);
int external_lock(THD *thd, int lock_type);
int delete_all_rows(void);
ha_rows records_in_range(uint inx, key_range *min_key,
key_range *max_key);
int delete_table(const char *from);
int rename_table(const char * from, const char * to);
int create(const char *name, TABLE *form,
HA_CREATE_INFO *create_info);
int updateFrm(TABLE *table_def, File file);
int openTableDef(TABLE *table_def);
int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys);
int prepare_drop_index(TABLE *table_arg, uint *key_num, uint num_of_keys);
int final_drop_index(TABLE *table_arg) {return 0;}
void get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong nb_desired_values,
ulonglong *first_value,
ulonglong *nb_reserved_values);
int reset_auto_increment(ulonglong value);
void restore_auto_increment(ulonglong prev_insert_id) {return;}
void update_create_info(HA_CREATE_INFO *create_info);
int getNextIdVal(ulonglong *value);
int analyze(THD* thd,HA_CHECK_OPT* check_opt);
int optimize(THD* thd, HA_CHECK_OPT* check_opt);
bool can_switch_engines();
void free_foreign_key_create_info(char* str);
char* get_foreign_key_create_info();
int get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list);
uint referenced_by_foreign_key();
bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
virtual bool get_error_message(int error, String *buf);
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type);
bool low_byte_first() const { return 0; }
void unlock_row();
int index_end();
int reset();
static int doCommit(handlerton *hton, THD *thd, bool all);
static int doRollback(handlerton *hton, THD *thd, bool all);
void start_bulk_insert(ha_rows rows);
int end_bulk_insert();
int start_stmt(THD *thd, thr_lock_type lock_type);
void initBridge(THD* thd = NULL)
{
if (thd == NULL) thd = ha_thd();
DBUG_PRINT("ha_ibmdb2i::initBridge",("Initing bridge. Conn ID=%d", thd->thread_id));
cachedBridge = db2i_ileBridge::getBridgeForThread(thd);
}
db2i_ileBridge* bridge() {DBUG_ASSERT(cachedBridge); return cachedBridge;}
static uint8 autoCommitIsOn(THD* thd)
{ return (thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ? QMY_NO : QMY_YES); }
uint8 getCommitLevel();
uint8 getCommitLevel(THD* thd);
static int doSavepointSet(THD* thd, char* name)
{
return db2i_ileBridge::getBridgeForThread(thd)->savepoint(QMY_SET_SAVEPOINT,
name);
}
static int doSavepointRollback(THD* thd, char* name)
{
return db2i_ileBridge::getBridgeForThread(thd)->savepoint(QMY_ROLLBACK_SAVEPOINT,
name);
}
static int doSavepointRelease(THD* thd, char* name)
{
return db2i_ileBridge::getBridgeForThread(thd)->savepoint(QMY_RELEASE_SAVEPOINT,
name);
}
// We can't guarantee that the rows we know about when this is called
// will be the same number of rows that read returns (since DB2 activity
// may insert additional rows). Therefore, we do as the Federated SE and
// return the max possible.
ha_rows estimate_rows_upper_bound()
{
return HA_POS_ERROR;
}
private:
enum enum_TimeFormat
{
TIME_OF_DAY,
DURATION
};
enum enum_BlobMapping
{
AS_BLOB,
AS_VARCHAR
};
enum enum_ZeroDate
{
NO_SUBSTITUTE,
SUBSTITUTE_0001_01_01
};
enum enum_YearFormat
{
CHAR4,
SMALLINT
};
enum_ZeroDate cachedZeroDateOption;
IBMDB2I_SHARE *get_share(const char *table_name, TABLE *table);
int free_share(IBMDB2I_SHARE *share);
int32 mungeDB2row(uchar* record, const char* dataPtr, const char* nullMapPtr, bool skipLOBs);
int prepareRowForWrite(char* data, char* nulls, bool honorIdentCols);
int prepareReadBufferForLobs();
int32 prepareWriteBufferForLobs();
uint32 adjustLobBuffersForRead();
bool lobFieldsRequested();
int convertFieldChars(enum_conversionDirection direction,
uint16 fieldID,
const char* input,
char* output,
size_t ilen,
size_t olen,
size_t* outDataLen,
bool tacitErrors=FALSE,
size_t* substChars=NULL);
/**
Fast integer log2 function
*/
uint64 log_2(uint64 val)
{
uint64 exp = 0;
while( (val >> exp) != 0)
{
exp++;
}
DBUG_ASSERT(exp-1 == (uint64)log2(val));
return exp-1;
}
void bumpInUseCounter(uint16 amount)
{
activeReferences += amount;
DBUG_PRINT("ha_ibmdb2i::bumpInUseCounter", ("activeReferences = %d", activeReferences));
if (activeReferences)
curConnection = (uint32)(ha_thd()->thread_id);
else
curConnection = 0;
}
int useDataFile()
{
DBUG_ENTER("ha_ibmdb2i::useDataFile");
int rc = 0;
if (!dataHandle)
rc = db2Table->dataFile()->allocateNewInstance(&dataHandle, curConnection);
else if (activeHandle == dataHandle)
DBUG_RETURN(0);
DBUG_ASSERT(activeHandle == 0);
if (likely(rc == 0))
{
activeHandle = dataHandle;
bumpInUseCounter(1);
}
DBUG_RETURN(rc);
}
void releaseAnyLockedRows()
{
if (releaseRowNeeded)
{
DBUG_PRINT("ha_ibmdb2i::releaseAnyLockedRows", ("Releasing rows"));
db2i_ileBridge::getBridgeForThread()->rrlslck(activeHandle, accessIntent);
releaseRowNeeded = FALSE;
}
}
void releaseDataFile()
{
DBUG_ENTER("ha_ibmdb2i::releaseDataFile");
releaseAnyLockedRows();
bumpInUseCounter(-1);
DBUG_ASSERT((volatile int)activeReferences >= 0);
activeHandle = 0;
DBUG_VOID_RETURN;
}
int useIndexFile(int idx);
void releaseIndexFile(int idx)
{
DBUG_ENTER("ha_ibmdb2i::releaseIndexFile");
releaseAnyLockedRows();
bumpInUseCounter(-1);
DBUG_ASSERT((volatile int)activeReferences >= 0);
activeHandle = 0;
DBUG_VOID_RETURN;
}
FILE_HANDLE allocateFileHandle(char* database, char* table, int* activityReference, bool hasBlobs);
int updateBuffers(const db2i_file::RowFormat* format, uint rowsToRead, uint rowsToWrite);
int flushWrite(FILE_HANDLE fileHandle, uchar* buf = NULL);
int alterStartWith();
int buildDB2ConstraintString(LEX* lex,
String& appendHere,
const char* database,
Field** fields,
char* fileSortSequenceType,
char* fileSortSequence,
char* fileSortSequenceLibrary);
void releaseWriteBuffer();
void setIndexReadEstimate(uint index, ha_rows rows)
{
if (!indexReadSizeEstimates)
{
indexReadSizeEstimates = (ha_rows*)my_malloc(sizeof(ha_rows) * table->s->keys, MYF(MY_WME | MY_ZEROFILL));
}
indexReadSizeEstimates[index] = rows;
}
ha_rows getIndexReadEstimate(uint index)
{
if (indexReadSizeEstimates)
return max(indexReadSizeEstimates[index], 1);
return 10000; // Assume index scan if no estimate exists.
}
void quiesceAllFileHandles()
{
db2i_ileBridge* bridge = db2i_ileBridge::getBridgeForThread();
if (dataHandle)
{
bridge->quiesceFileInstance(dataHandle);
}
for (int idx = 0; idx < table_share->keys; ++idx)
{
if (indexHandles[idx] != 0)
{
bridge->quiesceFileInstance(indexHandles[idx]);
}
}
}
int32 buildCreateIndexStatement(SqlStatementStream& sqlStream,
KEY& key,
bool isPrimary,
const char* db2LibName,
const char* db2FileName);
int32 buildIndexFieldList(String& appendHere,
const KEY& key,
bool isPrimary,
char* fileSortSequenceType,
char* fileSortSequence,
char* fileSortSequenceLibrary);
// Specify NULL for data when using the data pointed to by field
int32 convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char* db2Buf, const uchar* data = NULL);
int32 convertDB2toMySQL(const DB2Field& db2Field, Field* field, const char* buf);
int getFieldTypeMapping(Field* field,
String& mapping,
enum_TimeFormat timeFormate,
enum_BlobMapping blobMapping,
enum_ZeroDate zeroDateHandling,
bool propagateDefaults,
enum_YearFormat yearFormat);
int getKeyFromName(const char* name, size_t len);
void releaseActiveHandle()
{
if (activeHandle == dataHandle)
releaseDataFile();
else
releaseIndexFile(active_index);
}
int32 finishBulkInsert();
void doInitialRead(char orientation,
uint32 rowsToBuffer,
ILEMemHandle key = 0,
int keyLength = 0,
int keyParts = 0);
int32 readFromBuffer(uchar* destination, char orientation)
{
char* row;
int32 rc = 0;
row = activeReadBuf->readNextRow(orientation, currentRRN);
if (unlikely(!row))
{
rc = activeReadBuf->lastrc();
if (rc == QMY_ERR_LOB_SPACE_TOO_SMALL)
{
rc = handleLOBReadOverflow();
if (rc == 0)
{
DBUG_ASSERT(activeReadBuf->rowCount() == 1);
row = activeReadBuf->readNextRow(orientation, currentRRN);
if (unlikely(!row))
rc = activeReadBuf->lastrc();
}
}
}
if (likely(rc == 0))
{
rrnAssocHandle = activeHandle;
rc = mungeDB2row(destination, row, row+activeReadBuf->getRowNullOffset(), false);
}
return rc;
}
int32 handleLOBReadOverflow();
char* getCharacterConversionBuffer(int fieldId, int length)
{
if (unlikely(!alloc_root_inited(&conversionBufferMemroot)))
init_alloc_root(&conversionBufferMemroot, 8192, 0);
return (char*)alloc_root(&conversionBufferMemroot, length);;
}
void resetCharacterConversionBuffers()
{
if (alloc_root_inited(&conversionBufferMemroot))
{
free_root(&conversionBufferMemroot, MYF(MY_MARK_BLOCKS_FREE));
}
}
void tweakReadSet()
{
THD* thd = ha_thd();
int command = thd_sql_command(thd);
if ((command == SQLCOM_UPDATE ||
command == SQLCOM_UPDATE_MULTI) ||
((command == SQLCOM_DELETE ||
command == SQLCOM_DELETE_MULTI) &&
thd->options & OPTION_BIN_LOG))
readAllColumns = TRUE;
else
readAllColumns = FALSE;
}
/**
*/
int useFileByHandle(char intent,
FILE_HANDLE handle)
{
DBUG_ENTER("ha_ibmdb2i::useFileByHandle");
const db2i_file* file;
if (handle == dataHandle)
file = db2Table->dataFile();
else
{
for (uint i = 0; i < table_share->keys; ++i)
{
if (indexHandles[i] == handle)
{
file = db2Table->indexFile(i);
active_index = i;
}
}
}
int rc = file->obtainRowFormat(handle, intent, getCommitLevel(), &activeFormat);
if (likely(rc == 0))
{
activeHandle = handle;
bumpInUseCounter(1);
}
DBUG_RETURN(rc);
}
const db2i_file* getFileForActiveHandle() const
{
if (activeHandle == dataHandle)
return db2Table->dataFile();
else
for (uint i = 0; i < table_share->keys; ++i)
if (indexHandles[i] == activeHandle)
return db2Table->indexFile(i);
DBUG_ASSERT(0);
return NULL;
}
int prepReadBuffer(ha_rows rowsToRead, const db2i_file* file, char intent);
int prepWriteBuffer(ha_rows rowsToWrite, const db2i_file* file);
void invalidateCachedStats()
{
share->cachedStats.invalidate(rowCount | deletedRowCount | objLength |
meanRowLen | ioCount);
}
void warnIfInvalidData()
{
if (unlikely(invalidDataFound))
{
warning(ha_thd(), DB2I_ERR_INVALID_DATA, table->alias);
}
}
/**
Calculate the maximum value that a particular field can hold.
This is used to anticipate overflows in the auto_increment processing.
@param field The Field to be analyzed
@return The maximum value
*/
static uint64 maxValueForField(const Field* field)
{
uint64 maxValue=0;
switch (field->type())
{
case MYSQL_TYPE_TINY:
if (((const Field_num*)field)->unsigned_flag)
maxValue = (1 << 8) - 1;
else
maxValue = (1 << 7) - 1;
break;
case MYSQL_TYPE_SHORT:
if (((const Field_num*)field)->unsigned_flag)
maxValue = (1 << 16) - 1;
else
maxValue = (1 << 15) - 1;
break;
case MYSQL_TYPE_INT24:
if (((const Field_num*)field)->unsigned_flag)
maxValue = (1 << 24) - 1;
else
maxValue = (1 << 23) - 1;
break;
case MYSQL_TYPE_LONG:
if (((const Field_num*)field)->unsigned_flag)
maxValue = (1LL << 32) - 1;
else
maxValue = (1 << 31) - 1;
break;
case MYSQL_TYPE_LONGLONG:
if (((const Field_num*)field)->unsigned_flag)
maxValue = ~(0LL);
else
maxValue = 1 << 63 - 1;
break;
}
return maxValue;
}
void cleanupBuffers()
{
if (blobReadBuffers)
{
delete blobReadBuffers;
blobReadBuffers = NULL;
}
if (blobWriteBuffers)
{
delete[] blobWriteBuffers;
blobWriteBuffers = NULL;
}
if (alloc_root_inited(&conversionBufferMemroot))
{
free_root(&conversionBufferMemroot, MYF(0));
}
}
/**
Generate a valid RCDFMT name based on the name of the table.
The RCDFMT name is devised by munging the name of the table,
uppercasing all ascii alpha-numeric characters and replacing all other
characters with underscores until up to ten characters have been generated.
@param tableName The name of the table, as given on the MySQL
CREATE TABLE statement
@param[out] query The string to receive the generated RCDFMT name
*/
static void generateAndAppendRCDFMT(const char* tableName, String& query)
{
char rcdfmt[11];
// The RCDFMT name must begin with an alpha character.
// We enforce this by skipping to the first alpha character in the table
// name. If no alpha character exists, we use 'X' for the RCDFMT name;
while (*tableName &&
(!my_isascii(*tableName) ||
!my_isalpha(system_charset_info, *tableName)))
{
tableName += my_mbcharlen(system_charset_info, *tableName);
}
if (unlikely(!(*tableName)))
{
rcdfmt[0]= 'X';
rcdfmt[1]= 0;
}
else
{
int r= 0;
while ((r < sizeof(rcdfmt)-1) && *tableName)
{
if (my_isascii(*tableName) &&
my_isalnum(system_charset_info, *tableName))
rcdfmt[r] = my_toupper(system_charset_info, *tableName);
else
rcdfmt[r] = '_';
++r;
tableName += my_mbcharlen(system_charset_info, *tableName);
}
rcdfmt[r]= 0;
}
query.append(STRING_WITH_LEN(" RCDFMT "));
query.append(rcdfmt);
}
int32 generateShadowIndex(SqlStatementStream& stream,
const KEY& key,
const char* libName,
const char* fileName,
const String& fieldDefinition);
};

View File

@ -1,12 +0,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])

View File

@ -1,114 +1,8 @@
Windows building readme
======================================
----------------IMPORTANT----------------------------
This readme outlines the instructions for building
MySQL for Windows staring from version 5.1.
This readme does not apply to MySQL versions 5.0
or ealier.
-----------------------------------------------------
The Windows build system uses a tool named CMake to generate build files for
a variety of project systems. This tool is combined with a set of jscript
files to enable building of MySQL for Windows directly out of a bzr clone.
For relevant information, please refer to http://forge.mysql.com/wiki/CMake
The steps required are below.
Step 1:
-------
Install a Windows C++ compiler. If you don't have one, you can use
the free compiler "Visual C++ 2005 express edition", which from Cmake
point of view is same as Visual studio 8:
http://msdn.microsoft.com/vstudio/express/
Step 2
------
Download and install CMake. It can be downloaded from http://www.cmake.org.
Once it is installed, modify your path to make sure you can execute
the cmake binary.
Step 3
------
Download and install bison for Windows. It can be downloaded from
http://gnuwin32.sourceforge.net/packages/bison.htm. Please download using
the link named "Complete package, excluding sources". This includes an
installer that will install bison. After the installer finishes, modify
your path so that you can execute bison.
(As an alternative you can take the sql_yacc.yy and sql_yacc.h files from a
matching mysql tar distribution and drop them into the sql directory just
before you start the build)
Step 4
------
One of the nice CMake features is "out-of-source" build support, which
means not building in the source directory, but in dedicated build
directory. This keeps the source directory clean and allows for more than
single build tree for the same source tree (e.g debug and release, 32 and
64 bit etc). We'll create subdirectory "bld" in the source directory for
this purpose. Clone your bzr tree to any location you like.
Step 5
------
From the root of your installation directory use cmake . -L to see the
various configuration parameters.
So the command line could look like:
cmake .. -G "target" -DWITH_INNOBASE_STORAGE_ENGINE=1
The recommended way of configuring would be to use -DBUILD_CONFIG=mysql_release
to build binaries exactly the same as the official MySQL releases.
Step 6
------
From the root of your installation directory/bzr clone, you can
use cmake to compile the sources. Use cmake --help when necessary.
Before you start building the sources, please remove the old build area
created from an earlier run and start afresh.
C:\> del bld
C:\> md bld
C:\> cd bld
C:\> cmake .. -G "target name" -DBUILD_CONFIG=mysql_release
For Example:
To generate the Win64 project files using Visual Studio 9, you would run
cmake .. -G "Visual Studio 9 2008 Win64"
Other target names supported using CMake 2.6 patch 4 are:
Visual Studio 7 "Visual Studio 7 .NET 2003"
Visual Studio 8 "Visual Studio 8 2005"
Visual Studio 8 (64 bit) "Visual Studio 8 2005 Win64"
Visual Studio 9 "Visual Studio 9 2008"
Visual Studio 9 (64 bit) "Visual Studio 9 2008 Win64"
For generating project files using Visual Studio 10, you need CMake 2.8
or higher and corresponding target names are
Visual Studio 10 "Visual Studio 10"
Visual Studio 10 (64 bit) "Visual Studio 10 Win64"
Step 7
------
From the root of your bzr clone, start your build.
For Visual Studio, execute mysql.sln. This will start the IDE
and you can click the build solution menu option.
Alternatively, you could start the build from command line as follows
devenv mysql.sln /build relwithdebinfo
Current issues
--------------
1. After changing configuration (eg. adding or removing a storage engine), it
may be necessary to clean the build tree to remove any stale objects.
2. To use Visual C++ Express Edition you also need to install the Platform SDK.
Please see this link: http://msdn.microsoft.com/vstudio/express/visualc/usingpsdk/
At step 5 you only need to add the libraries advapi32.lib and user32.lib to
the file "corewin_express.vsprops" in order to avoid link errors.
For relevant information and/or for building binaries from source distribution,
please refer to http://forge.mysql.com/wiki/CMake