Commit c61fab93 authored by Timothy Smith's avatar Timothy Smith

Import the ibmdb2i-ga4-src snapshot from IBM

parent 9c3c97db
...@@ -65,6 +65,11 @@ class ProtectedBuffer ...@@ -65,6 +65,11 @@ class ProtectedBuffer
len = size; len = size;
if (protectBuf) if (protectBuf)
mprotect(protectedPage(), 0x1000, PROT_NONE); 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
} }
} }
......
...@@ -703,7 +703,7 @@ static int32 openNewConversion(enum_conversionDirection direction, ...@@ -703,7 +703,7 @@ static int32 openNewConversion(enum_conversionDirection direction,
*/ */
int32 getConversion(enum_conversionDirection direction, const CHARSET_INFO* cs, uint16 db2CCSID, iconv_t& conversion) int32 getConversion(enum_conversionDirection direction, const CHARSET_INFO* cs, uint16 db2CCSID, iconv_t& conversion)
{ {
DBUG_ENTER("db2i_charsetSupport::convChars"); DBUG_ENTER("db2i_charsetSupport::getConversion");
int32 rc; int32 rc;
......
...@@ -277,33 +277,32 @@ static int32 getAssociatedSortSequence(const CHARSET_INFO *fieldCharSet, const c ...@@ -277,33 +277,32 @@ static int32 getAssociatedSortSequence(const CHARSET_INFO *fieldCharSet, const c
This function accumulates information about a key as it is called for each 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 field composing the key. The caller should invoke the function for each field
and (with the exception of the curField parm) preserve the values for the and (with the exception of the charset parm) preserve the values for the
parms across invocations, until a particular key has been evaluated. Once parms across invocations, until a particular key has been evaluated. Once
the last field in the key has been evaluated, the fileSortSequence and the last field in the key has been evaluated, the fileSortSequence and
fileSortSequenceLibrary parms will contain the correct information for fileSortSequenceLibrary parms will contain the correct information for
creating the corresponding DB2 key. creating the corresponding DB2 key.
@param curField The field under consideration @param charset The character set under consideration
@param[in, out] fileSortSequenceType The type of the current key's sort seq @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 @param[in, out] fileSortSequence The IBM i identifier for the DB2 sort sequence
that corresponds that corresponds
@return 0 if successful. Failure otherwise @return 0 if successful. Failure otherwise
*/ */
int32 updateAssociatedSortSequence(const Field *curField, int32 updateAssociatedSortSequence(const CHARSET_INFO* charset,
char* fileSortSequenceType, char* fileSortSequenceType,
char* fileSortSequence, char* fileSortSequence,
char* fileSortSequenceLibrary) char* fileSortSequenceLibrary)
{ {
DBUG_ENTER("ha_ibmdb2i::updateAssociatedSortSequence"); DBUG_ENTER("ha_ibmdb2i::updateAssociatedSortSequence");
DBUG_ASSERT(curField); DBUG_ASSERT(charset);
CHARSET_INFO* fieldCharSet = curField->charset(); if (strcmp(charset->csname,"binary") != 0)
if (strcmp(fieldCharSet->csname,"binary") != 0)
{ {
char newSortSequence[11] = ""; char newSortSequence[11] = "";
char newSortSequenceType = ' '; char newSortSequenceType = ' ';
const char* foundSortSequence; const char* foundSortSequence;
int rc = getAssociatedSortSequence(fieldCharSet, &foundSortSequence); int rc = getAssociatedSortSequence(charset, &foundSortSequence);
if (rc) DBUG_RETURN (rc); if (rc) DBUG_RETURN (rc);
switch(foundSortSequence[0]) switch(foundSortSequence[0])
{ {
...@@ -313,11 +312,11 @@ int32 updateAssociatedSortSequence(const Field *curField, ...@@ -313,11 +312,11 @@ int32 updateAssociatedSortSequence(const Field *curField,
break; break;
case 'Q': // Non-ICU sort sequence case 'Q': // Non-ICU sort sequence
strcat(newSortSequence,foundSortSequence); strcat(newSortSequence,foundSortSequence);
if ((fieldCharSet->state & MY_CS_BINSORT) != 0) if ((charset->state & MY_CS_BINSORT) != 0)
{ {
strcat(newSortSequence,"U"); strcat(newSortSequence,"U");
} }
else if ((fieldCharSet->state & MY_CS_CSSORT) != 0) else if ((charset->state & MY_CS_CSSORT) != 0)
{ {
strcat(newSortSequence,"U"); strcat(newSortSequence,"U");
} }
...@@ -329,7 +328,7 @@ int32 updateAssociatedSortSequence(const Field *curField, ...@@ -329,7 +328,7 @@ int32 updateAssociatedSortSequence(const Field *curField,
break; break;
default: // ICU sort sequence default: // ICU sort sequence
{ {
if ((fieldCharSet->state & MY_CS_CSSORT) == 0) if ((charset->state & MY_CS_CSSORT) == 0)
{ {
if (osVersion.v >= 6) if (osVersion.v >= 6)
strcat(newSortSequence,"I34"); // ICU 3.4 strcat(newSortSequence,"I34"); // ICU 3.4
......
...@@ -40,6 +40,9 @@ OF SUCH DAMAGE. ...@@ -40,6 +40,9 @@ OF SUCH DAMAGE.
#include "db2i_global.h" #include "db2i_global.h"
#include "mysql_priv.h" #include "mysql_priv.h"
int32 updateAssociatedSortSequence(const Field *curField, char* fileSortSequenceType, char* fileSortSequence, char* fileSortSequenceLibrary); int32 updateAssociatedSortSequence(const CHARSET_INFO* charset,
char* fileSortSequenceType,
char* fileSortSequence,
char* fileSortSequenceLibrary);
#endif #endif
...@@ -150,7 +150,7 @@ int ha_ibmdb2i::buildDB2ConstraintString(LEX* lex, ...@@ -150,7 +150,7 @@ int ha_ibmdb2i::buildDB2ConstraintString(LEX* lex,
{ {
if (strcmp((*field)->field_name, curColumn->field_name) == 0) if (strcmp((*field)->field_name, curColumn->field_name) == 0)
{ {
int rc = updateAssociatedSortSequence((*field), int rc = updateAssociatedSortSequence((*field)->charset(),
fileSortSequenceType, fileSortSequenceType,
fileSortSequence, fileSortSequence,
fileSortSequenceLibrary); fileSortSequenceLibrary);
...@@ -447,14 +447,13 @@ int ha_ibmdb2i::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_lis ...@@ -447,14 +447,13 @@ int ha_ibmdb2i::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_lis
cst_name* fieldName; // Pointer to field name structure cst_name* fieldName; // Pointer to field name structure
const char *method; const char *method;
ulong methodLen; ulong methodLen;
bool gotShare = FALSE; // Indicator for local get_share
char* tempPtr; // Temp pointer for traversing constraint space char* tempPtr; // Temp pointer for traversing constraint space
char convName[128]; char convName[128];
// Allocate space to retrieve the DB2 constraint information.
if (!(share = get_share(table_share->path.str, table))) if (!(share = get_share(table_share->path.str, table)))
DBUG_RETURN(0); DBUG_RETURN(0);
// Allocate space to retrieve the DB2 constraint information.
constraintSpaceLength = 5000; // Try allocating 5000 bytes and see if enough. constraintSpaceLength = 5000; // Try allocating 5000 bytes and see if enough.
constraintSpace.alloc(constraintSpaceLength); constraintSpace.alloc(constraintSpaceLength);
......
...@@ -43,6 +43,12 @@ OF SUCH DAMAGE. ...@@ -43,6 +43,12 @@ OF SUCH DAMAGE.
#include "db2i_errors.h" #include "db2i_errors.h"
#include "wchar.h" #include "wchar.h"
const char ZERO_DATETIME_VALUE[] = "0000-00-00 00:00:00";
const char ZERO_DATETIME_VALUE_SUBST[] = "0001-01-01 00:00:00";
const char ZERO_DATE_VALUE[] = "0000-00-00";
const char ZERO_DATE_VALUE_SUBST[] = "0001-01-01";
/** /**
Put a BCD digit into a BCD string. Put a BCD digit into a BCD string.
...@@ -149,7 +155,6 @@ int ha_ibmdb2i::convertFieldChars(enum_conversionDirection direction, ...@@ -149,7 +155,6 @@ int ha_ibmdb2i::convertFieldChars(enum_conversionDirection direction,
} }
size_t initOLen= olen; size_t initOLen= olen;
ilen = min(ilen, olen); // Handle partial translation
size_t substitutedChars = 0; size_t substitutedChars = 0;
int rc = iconv(conversion, (char**)&input, &ilen, &output, &olen, &substitutedChars ); int rc = iconv(conversion, (char**)&input, &ilen, &output, &olen, &substitutedChars );
if (unlikely(rc < 0)) if (unlikely(rc < 0))
...@@ -176,6 +181,178 @@ int ha_ibmdb2i::convertFieldChars(enum_conversionDirection direction, ...@@ -176,6 +181,178 @@ int ha_ibmdb2i::convertFieldChars(enum_conversionDirection direction,
return (0); return (0);
} }
/**
Append the appropriate default value clause onto a CREATE TABLE definition
This was inspired by get_field_default_value in sql/sql_show.cc.
@param field The field whose value is to be obtained
@param statement The string to receive the DEFAULT clause
@param quoteIt Does the data type require single quotes around the value?
@param ccsid The ccsid of the field value (if a string type); 0 if no conversion needed
*/
static void get_field_default_value(Field *field,
String &statement,
bool quoteIt,
uint32 ccsid,
bool substituteZeroDates)
{
if ((field->type() != FIELD_TYPE_BLOB &&
!(field->flags & NO_DEFAULT_VALUE_FLAG) &&
field->unireg_check != Field::NEXT_NUMBER))
{
my_ptrdiff_t old_ptr= (my_ptrdiff_t) (field->table->s->default_values - field->table->record[0]);
field->move_field_offset(old_ptr);
String defaultClause(64);
defaultClause.length(0);
defaultClause.append(" DEFAULT ");
if (!field->is_null())
{
my_bitmap_map *old_map = tmp_use_all_columns(field->table, field->table->read_set);
char tmp[MAX_FIELD_WIDTH];
if (field->real_type() == MYSQL_TYPE_ENUM ||
field->real_type() == MYSQL_TYPE_SET)
{
CHARSET_INFO *cs= &my_charset_bin;
uint len = (uint)(cs->cset->longlong10_to_str)(cs,tmp,sizeof(tmp), 10, field->val_int());
tmp[len]=0;
defaultClause.append(tmp);
}
else
{
String type(tmp, sizeof(tmp), field->charset());
field->val_str(&type);
if (type.length())
{
if (field->type() == MYSQL_TYPE_DATE &&
memcmp(type.ptr(), STRING_WITH_LEN(ZERO_DATE_VALUE)) == 0)
{
if (substituteZeroDates)
type.set(STRING_WITH_LEN(ZERO_DATE_VALUE_SUBST), field->charset());
else
{
warning(current_thd, DB2I_ERR_WARN_COL_ATTRS, field->field_name);
return;
}
}
else if ((field->type() == MYSQL_TYPE_DATETIME ||
field->type() == MYSQL_TYPE_TIMESTAMP) &&
memcmp(type.ptr(), STRING_WITH_LEN(ZERO_DATETIME_VALUE)) == 0)
{
if (substituteZeroDates)
type.set(STRING_WITH_LEN(ZERO_DATETIME_VALUE_SUBST), field->charset());
else
{
warning(current_thd, DB2I_ERR_WARN_COL_ATTRS, field->field_name);
return;
}
}
if (field->type() != MYSQL_TYPE_STRING &&
field->type() != MYSQL_TYPE_VARCHAR &&
field->type() != MYSQL_TYPE_BLOB &&
field->type() != MYSQL_TYPE_BIT)
{
if (quoteIt)
defaultClause.append('\'');
defaultClause.append(type);
if (quoteIt)
defaultClause.append('\'');
}
else
{
int length;
char* out;
// If a ccsid is specified, we need to make sure that the DEFAULT
// string is converted to that encoding.
if (ccsid != 0)
{
iconv_t iconvD;
if (getConversion(toDB2, field->charset(), ccsid, iconvD))
{
warning(current_thd, DB2I_ERR_WARN_COL_ATTRS, field->field_name);
return;
}
size_t ilen = type.length();
size_t olen = 6 * ilen;
size_t origOlen = olen;
const char* in = type.ptr();
const char* tempIn = in;
out = (char*)my_malloc(olen, MYF(MY_WME));
char* tempOut = out;
size_t substitutedChars;
if (iconv(iconvD, (char**)&tempIn, &ilen, &tempOut, &olen, &substitutedChars) < 0)
{
warning(current_thd, DB2I_ERR_WARN_COL_ATTRS, field->field_name);
my_free(out, MYF(0));
return;
}
// Now we process the converted string to represent it as
// hexadecimal values.
length = origOlen - olen;
}
else
{
length = type.length();
out = (char*)my_malloc(length*2, MYF(MY_WME));
memcpy(out, (char*)type.ptr(), length);
}
if (length > 16370)
{
warning(current_thd, DB2I_ERR_WARN_COL_ATTRS, field->field_name);
my_free(out, MYF(0));
return;
}
if (ccsid == 1200)
defaultClause.append("ux'");
else if (ccsid == 13488)
defaultClause.append("gx'");
else if (field->charset() == &my_charset_bin)
defaultClause.append("binary(x'");
else
defaultClause.append("x'");
const char hexMap[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
for (int c = length-1; c >= 0; --c)
{
out[c*2+1] = hexMap[out[c] & 0xF];
out[c*2] = hexMap[out[c] >> 4];
}
defaultClause.append(out, length*2);
defaultClause.append('\'');
if (field->charset() == &my_charset_bin)
defaultClause.append(")");
my_free(out, MYF(0));
}
}
else
defaultClause.length(0);
}
tmp_restore_column_map(field->table->read_set, old_map);
}
else if (field->maybe_null())
defaultClause.append(STRING_WITH_LEN("NULL"));
if (old_ptr)
field->move_field_offset(-old_ptr);
statement.append(defaultClause);
}
}
/** /**
Convert a MySQL field definition into its corresponding DB2 type. Convert a MySQL field definition into its corresponding DB2 type.
...@@ -189,10 +366,15 @@ int ha_ibmdb2i::convertFieldChars(enum_conversionDirection direction, ...@@ -189,10 +366,15 @@ int ha_ibmdb2i::convertFieldChars(enum_conversionDirection direction,
int ha_ibmdb2i::getFieldTypeMapping(Field* field, int ha_ibmdb2i::getFieldTypeMapping(Field* field,
String& mapping, String& mapping,
enum_TimeFormat timeFormat, enum_TimeFormat timeFormat,
enum_BlobMapping blobMapping) enum_BlobMapping blobMapping,
enum_ZeroDate zeroDateHandling,
bool propagateDefaults,
enum_YearFormat yearFormat)
{ {
char stringBuildBuffer[257]; char stringBuildBuffer[257];
uint32 fieldLength; uint32 fieldLength;
bool defaultNeedsQuotes = false;
uint16 db2Ccsid = 0;
CHARSET_INFO* fieldCharSet = field->charset(); CHARSET_INFO* fieldCharSet = field->charset();
switch (field->type()) switch (field->type())
...@@ -257,19 +439,69 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, ...@@ -257,19 +439,69 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field,
case MYSQL_TYPE_DATE: case MYSQL_TYPE_DATE:
case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_NEWDATE:
mapping.append(STRING_WITH_LEN("DATE")); mapping.append(STRING_WITH_LEN("DATE"));
defaultNeedsQuotes = true;
break; break;
case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIME:
if (timeFormat == TIME_OF_DAY) if (timeFormat == TIME_OF_DAY)
{
mapping.append(STRING_WITH_LEN("TIME")); mapping.append(STRING_WITH_LEN("TIME"));
defaultNeedsQuotes = true;
}
else else
mapping.append(STRING_WITH_LEN("INTEGER")); mapping.append(STRING_WITH_LEN("INTEGER"));
break; break;
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_DATETIME:
mapping.append(STRING_WITH_LEN("TIMESTAMP")); mapping.append(STRING_WITH_LEN("TIMESTAMP"));
defaultNeedsQuotes = true;
break;
case MYSQL_TYPE_TIMESTAMP:
mapping.append(STRING_WITH_LEN("TIMESTAMP"));
if (table_share->timestamp_field == field && propagateDefaults)
{
switch (((Field_timestamp*)field)->get_auto_set_type())
{
case TIMESTAMP_NO_AUTO_SET:
break;
case TIMESTAMP_AUTO_SET_ON_INSERT:
mapping.append(STRING_WITH_LEN(" DEFAULT CURRENT_TIMESTAMP"));
break;
case TIMESTAMP_AUTO_SET_ON_UPDATE:
if (osVersion.v >= 6 &&
!field->is_null())
{
mapping.append(STRING_WITH_LEN(" GENERATED BY DEFAULT FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP"));
warning(ha_thd(), DB2I_ERR_WARN_COL_ATTRS, field->field_name);
}
else
warning(ha_thd(), DB2I_ERR_WARN_COL_ATTRS, field->field_name);
break;
case TIMESTAMP_AUTO_SET_ON_BOTH:
if (osVersion.v >= 6 &&
!field->is_null())
mapping.append(STRING_WITH_LEN(" GENERATED BY DEFAULT FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP"));
else
{
mapping.append(STRING_WITH_LEN(" DEFAULT CURRENT_TIMESTAMP"));
warning(ha_thd(), DB2I_ERR_WARN_COL_ATTRS, field->field_name);
}
break;
}
}
else
defaultNeedsQuotes = true;
break; break;
case MYSQL_TYPE_YEAR: case MYSQL_TYPE_YEAR:
if (yearFormat == CHAR4)
{
mapping.append(STRING_WITH_LEN("CHAR(4) CCSID 1208")); mapping.append(STRING_WITH_LEN("CHAR(4) CCSID 1208"));
defaultNeedsQuotes = true;
}
else
{
mapping.append(STRING_WITH_LEN("SMALLINT"));
defaultNeedsQuotes = false;
}
break; break;
case MYSQL_TYPE_BIT: case MYSQL_TYPE_BIT:
sprintf(stringBuildBuffer, "BINARY(%d)", (field->max_display_length() / 8) + 1); sprintf(stringBuildBuffer, "BINARY(%d)", (field->max_display_length() / 8) + 1);
...@@ -286,6 +518,8 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, ...@@ -286,6 +518,8 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field,
} }
else else
{ {
defaultNeedsQuotes = true;
fieldLength = field->max_display_length(); // Get field byte length fieldLength = field->max_display_length(); // Get field byte length
if (fieldCharSet == &my_charset_bin) if (fieldCharSet == &my_charset_bin)
...@@ -300,12 +534,11 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, ...@@ -300,12 +534,11 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field,
{ {
sprintf(stringBuildBuffer, "VARBINARY(%d)", max(fieldLength, 1)); sprintf(stringBuildBuffer, "VARBINARY(%d)", max(fieldLength, 1));
} }
/* else if (blobMapping == AS_VARCHAR && else if (blobMapping == AS_VARCHAR &&
get_blob_type_from_length(fieldLength) == MYSQL_TYPE_BLOB) (field->flags & PART_KEY_FLAG))
{ {
sprintf(stringBuildBuffer, "LONG VARBINARY ", max(fieldLength, 1)); sprintf(stringBuildBuffer, "LONG VARBINARY ");
} }
*/
else else
{ {
fieldLength = min(MAX_BLOB_LENGTH, fieldLength); fieldLength = min(MAX_BLOB_LENGTH, fieldLength);
...@@ -316,7 +549,6 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, ...@@ -316,7 +549,6 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field,
} }
else else
{ {
uint16 db2Ccsid = 0; // No override CCSID
if (field->type() == MYSQL_TYPE_STRING) if (field->type() == MYSQL_TYPE_STRING)
{ {
if (fieldLength > MAX_CHAR_LENGTH) if (fieldLength > MAX_CHAR_LENGTH)
...@@ -375,7 +607,7 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, ...@@ -375,7 +607,7 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field,
} }
} }
else if (blobMapping == AS_VARCHAR && else if (blobMapping == AS_VARCHAR &&
get_blob_type_from_length(fieldLength) == MYSQL_TYPE_BLOB) (field->flags & PART_KEY_FLAG))
{ {
if (fieldCharSet->mbmaxlen > 1) if (fieldCharSet->mbmaxlen > 1)
{ {
...@@ -447,6 +679,13 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, ...@@ -447,6 +679,13 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field,
} }
if (propagateDefaults)
get_field_default_value(field,
mapping,
defaultNeedsQuotes,
db2Ccsid,
(zeroDateHandling==SUBSTITUTE_0001_01_01));
return 0; return 0;
} }
...@@ -552,7 +791,6 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char ...@@ -552,7 +791,6 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char
case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_DATETIME:
{ {
String tempString(27); String tempString(27);
const char* ZERO_VALUE = "0000-00-00 00:00:00";
if (data == NULL) if (data == NULL)
{ {
field->val_str(&tempString, &tempString); field->val_str(&tempString, &tempString);
...@@ -563,11 +801,16 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char ...@@ -563,11 +801,16 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char
} }
memset(db2Buf, '0', 26); memset(db2Buf, '0', 26);
memcpy(db2Buf, tempString.ptr(), tempString.length()); memcpy(db2Buf, tempString.ptr(), tempString.length());
if (strncmp(db2Buf,ZERO_VALUE,strlen(ZERO_VALUE)) == 0) if (strncmp(db2Buf,ZERO_DATETIME_VALUE,strlen(ZERO_DATETIME_VALUE)) == 0)
{ {
getErrTxt(DB2I_ERR_INVALID_COL_VALUE,ZERO_VALUE,field->field_name); if (cachedZeroDateOption == SUBSTITUTE_0001_01_01)
memcpy(db2Buf, ZERO_DATETIME_VALUE_SUBST, sizeof(ZERO_DATETIME_VALUE_SUBST));
else
{
getErrTxt(DB2I_ERR_INVALID_COL_VALUE, field->field_name);
return(DB2I_ERR_INVALID_COL_VALUE); return(DB2I_ERR_INVALID_COL_VALUE);
} }
}
(db2Buf)[10] = '-'; (db2Buf)[10] = '-';
(db2Buf)[13] = (db2Buf)[16] = (db2Buf)[19] = '.'; (db2Buf)[13] = (db2Buf)[16] = (db2Buf)[19] = '.';
...@@ -620,7 +863,6 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char ...@@ -620,7 +863,6 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char
case MYSQL_TYPE_DATE: case MYSQL_TYPE_DATE:
case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_NEWDATE:
{ {
const char* ZERO_VALUE = "0000-00-00";
String tempString(11); String tempString(11);
if (data == NULL) if (data == NULL)
{ {
...@@ -631,11 +873,16 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char ...@@ -631,11 +873,16 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char
field->val_str(&tempString, data); field->val_str(&tempString, data);
} }
memcpy(db2Buf, tempString.ptr(), 10); memcpy(db2Buf, tempString.ptr(), 10);
if (strncmp(db2Buf,ZERO_VALUE,strlen(ZERO_VALUE)) == 0) if (strncmp(db2Buf,ZERO_DATE_VALUE,strlen(ZERO_DATE_VALUE)) == 0)
{
if (cachedZeroDateOption == SUBSTITUTE_0001_01_01)
memcpy(db2Buf, ZERO_DATE_VALUE_SUBST, sizeof(ZERO_DATE_VALUE_SUBST));
else
{ {
getErrTxt(DB2I_ERR_INVALID_COL_VALUE,ZERO_VALUE,field->field_name); getErrTxt(DB2I_ERR_INVALID_COL_VALUE,field->field_name);
return(DB2I_ERR_INVALID_COL_VALUE); return(DB2I_ERR_INVALID_COL_VALUE);
} }
}
convertNumericToEbcdicFast(db2Buf,10); convertNumericToEbcdicFast(db2Buf,10);
} }
...@@ -668,6 +915,8 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char ...@@ -668,6 +915,8 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char
case MYSQL_TYPE_YEAR: case MYSQL_TYPE_YEAR:
{ {
String tempString(5); String tempString(5);
if (db2Field.getType() == QMY_CHAR)
{
if (data == NULL) if (data == NULL)
{ {
field->val_str(&tempString, (String*)NULL); field->val_str(&tempString, (String*)NULL);
...@@ -678,10 +927,16 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char ...@@ -678,10 +927,16 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char
} }
memcpy(db2Buf, tempString.ptr(), 4); memcpy(db2Buf, tempString.ptr(), 4);
} }
else
{
uint8 temp = *(uint8*)(data == NULL ? field->ptr : data);
*(uint16*)(db2Buf) = (temp ? temp + 1900 : 0);
}
}
break; break;
case MYSQL_TYPE_BIT: case MYSQL_TYPE_BIT:
{ {
int bytesToCopy = (db2Field.getByteLengthInRecord()-1) / 8 + 1; int bytesToCopy = db2Field.getByteLengthInRecord();
if (data == NULL) if (data == NULL)
{ {
...@@ -745,11 +1000,19 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char ...@@ -745,11 +1000,19 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char
break; break;
case MYSQL_TYPE_BLOB: case MYSQL_TYPE_BLOB:
{ {
DBUG_ASSERT(data == NULL); if (data == NULL)
{
bytesToStore = ((Field_blob*)field)->get_length(); bytesToStore = ((Field_blob*)field)->get_length();
bytesToPad = maxDisplayLength - bytesToStore; bytesToPad = maxDisplayLength - bytesToStore;
((Field_blob*)field)->get_ptr((uchar**)&dataToStore); ((Field_blob*)field)->get_ptr((uchar**)&dataToStore);
} }
else
{
// Key lens are stored little-endian
bytesToStore = *(uint8*)data + ((*(uint8*)(data+1)) << 8);
dataToStore = data + 2;
}
}
break; break;
} }
...@@ -1028,6 +1291,10 @@ int32 ha_ibmdb2i::convertDB2toMySQL(const DB2Field& db2Field, Field* field, cons ...@@ -1028,6 +1291,10 @@ int32 ha_ibmdb2i::convertDB2toMySQL(const DB2Field& db2Field, Field* field, cons
a2toi_ebcdic((uchar*)bufPtr+5) * 100 + a2toi_ebcdic((uchar*)bufPtr+5) * 100 +
a2toi_ebcdic((uchar*)bufPtr+8); a2toi_ebcdic((uchar*)bufPtr+8);
if (cachedZeroDateOption == SUBSTITUTE_0001_01_01 &&
value == (10000 + 100 + 1))
value = 0;
storeRC = field->store(value); storeRC = field->store(value);
} }
break; break;
...@@ -1055,18 +1322,29 @@ int32 ha_ibmdb2i::convertDB2toMySQL(const DB2Field& db2Field, Field* field, cons ...@@ -1055,18 +1322,29 @@ int32 ha_ibmdb2i::convertDB2toMySQL(const DB2Field& db2Field, Field* field, cons
a2toi_ebcdic((uchar*)bufPtr+14) * 100 + a2toi_ebcdic((uchar*)bufPtr+14) * 100 +
a2toi_ebcdic((uchar*)bufPtr+17)); a2toi_ebcdic((uchar*)bufPtr+17));
if (cachedZeroDateOption == SUBSTITUTE_0001_01_01 &&
value == (10000 + 100 + 1) * 1000000LL)
value = 0;
storeRC = field->store(value); storeRC = field->store(value);
} }
break; break;
case MYSQL_TYPE_YEAR: case MYSQL_TYPE_YEAR:
{
if (db2Field.getType() == QMY_CHAR)
{ {
storeRC = field->store(bufPtr, 4, &my_charset_bin); storeRC = field->store(bufPtr, 4, &my_charset_bin);
} }
else
{
storeRC = field->store(*((uint16*)bufPtr));
}
}
break; break;
case MYSQL_TYPE_BIT: case MYSQL_TYPE_BIT:
{ {
uint64 temp= 0; uint64 temp= 0;
int bytesToCopy= (db2Field.getByteLengthInRecord()-1) / 8 + 1; int bytesToCopy= db2Field.getByteLengthInRecord();
memcpy(((char*)&temp) + (sizeof(temp) - bytesToCopy), bufPtr, bytesToCopy); memcpy(((char*)&temp) + (sizeof(temp) - bytesToCopy), bufPtr, bytesToCopy);
storeRC = field->store(temp, TRUE); storeRC = field->store(temp, TRUE);
} }
......
...@@ -63,9 +63,9 @@ static const char* engineErrors[MAX_MSGSTRING] = ...@@ -63,9 +63,9 @@ static const char* engineErrors[MAX_MSGSTRING] =
{"Error in iconv() function during character set conversion (errno = %d)"}, {"Error in iconv() function during character set conversion (errno = %d)"},
{"Error from Get Encoding Scheme (QTQGESP) API: %d, %d, %d"}, {"Error from Get Encoding Scheme (QTQGESP) API: %d, %d, %d"},
{"Error from Get Related Default CCSID (QTQGRDC) API: %d, %d, %d"}, {"Error from Get Related Default CCSID (QTQGRDC) API: %d, %d, %d"},
{"Invalid value '%-.128s' for column '%.192s'"}, {"Data out of range for column '%.192s'"},
{"Schema name '%.128s' exceeds maximum length of %d characters"}, {"Schema name '%.128s' exceeds maximum length of %d characters"},
{"Multiple collations not supported in a single index"}, {"Multiple collations not supported in a single index or constraint"},
{"Sort sequence was not found"}, {"Sort sequence was not found"},
{"One or more characters in column %.128s were substituted during conversion"}, {"One or more characters in column %.128s were substituted during conversion"},
{"A decimal column exceeded the maximum precision. Data may be truncated."}, {"A decimal column exceeded the maximum precision. Data may be truncated."},
...@@ -76,6 +76,7 @@ static const char* engineErrors[MAX_MSGSTRING] = ...@@ -76,6 +76,7 @@ static const char* engineErrors[MAX_MSGSTRING] =
{"A duplicate key was encountered for index '%.128s'"}, {"A duplicate key was encountered for index '%.128s'"},
{"A table with the same name exists but has incompatible column definitions."}, {"A table with the same name exists but has incompatible column definitions."},
{"The created table was discovered as an existing DB2 object."}, {"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."},
}; };
/* /*
......
...@@ -78,7 +78,8 @@ enum DB2I_errors ...@@ -78,7 +78,8 @@ enum DB2I_errors
DB2I_ERR_UNKNOWN_IDX, DB2I_ERR_UNKNOWN_IDX,
DB2I_ERR_DISCOVERY_MISMATCH, DB2I_ERR_DISCOVERY_MISMATCH,
DB2I_ERR_WARN_CREATE_DISCOVER, DB2I_ERR_WARN_CREATE_DISCOVER,
DB2I_LAST_ERR = DB2I_ERR_WARN_CREATE_DISCOVER DB2I_ERR_WARN_COL_ATTRS,
DB2I_LAST_ERR = DB2I_ERR_WARN_COL_ATTRS
}; };
void getErrTxt(int errcode, ...); void getErrTxt(int errcode, ...);
...@@ -86,6 +87,7 @@ void reportSystemAPIError(int errCode, const Qmy_Error_output *errInfo); ...@@ -86,6 +87,7 @@ void reportSystemAPIError(int errCode, const Qmy_Error_output *errInfo);
void warning(THD *thd, int errCode, ...); void warning(THD *thd, int errCode, ...);
const char* DB2I_SQL0350 = "\xE2\xD8\xD3\xF0\xF3\xF5\xF0"; // SQL0350 in EBCDIC 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 #endif
...@@ -97,10 +97,11 @@ int32 db2i_table::initDB2Objects(const char* path) ...@@ -97,10 +97,11 @@ int32 db2i_table::initDB2Objects(const char* path)
physicalFile = new db2i_file(this); physicalFile = new db2i_file(this);
physicalFile->fillILEDefn(&fileDefnSpace[0], true); physicalFile->fillILEDefn(&fileDefnSpace[0], true);
if (fileObjects > 1) logicalFileCount = mysqlTable->keys;
if (logicalFileCount > 0)
{ {
logicalFiles = new db2i_file*[fileObjects - 1]; logicalFiles = new db2i_file*[logicalFileCount];
for (int k = 0; k < mysqlTable->keys; k++) for (int k = 0; k < logicalFileCount; k++)
{ {
logicalFiles[k] = new db2i_file(this, k); logicalFiles[k] = new db2i_file(this, k);
logicalFiles[k]->fillILEDefn(&fileDefnSpace[k+1], false); logicalFiles[k]->fillILEDefn(&fileDefnSpace[k+1], false);
...@@ -111,7 +112,9 @@ int32 db2i_table::initDB2Objects(const char* path) ...@@ -111,7 +112,9 @@ int32 db2i_table::initDB2Objects(const char* path)
size_t formatSpaceLen = sizeof(format_hdr_t) + mysqlTable->fields * sizeof(DB2Field); size_t formatSpaceLen = sizeof(format_hdr_t) + mysqlTable->fields * sizeof(DB2Field);
formatSpace.alloc(formatSpaceLen); formatSpace.alloc(formatSpaceLen);
int rc = db2i_ileBridge::getBridgeForThread()->allocateFileDefn(fileDefnSpace, int rc = db2i_ileBridge::getBridgeForThread()->
expectErrors(QMY_ERR_RTNFMT)->
allocateFileDefn(fileDefnSpace,
fileDefnHandles, fileDefnHandles,
fileObjects, fileObjects,
db2LibNameEbcdic, db2LibNameEbcdic,
...@@ -120,7 +123,18 @@ int32 db2i_table::initDB2Objects(const char* path) ...@@ -120,7 +123,18 @@ int32 db2i_table::initDB2Objects(const char* path)
formatSpaceLen); formatSpaceLen);
if (rc) 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; return rc;
}
convFromEbcdic(((format_hdr_t*)formatSpace)->FilLvlId, fileLevelID, sizeof(fileLevelID)); convFromEbcdic(((format_hdr_t*)formatSpace)->FilLvlId, fileLevelID, sizeof(fileLevelID));
...@@ -274,7 +288,7 @@ db2i_table::~db2i_table() ...@@ -274,7 +288,7 @@ db2i_table::~db2i_table()
if (logicalFiles) if (logicalFiles)
{ {
for (int k = 0; k < mysqlTable->keys; ++k) for (int k = 0; k < logicalFileCount; ++k)
{ {
delete logicalFiles[k]; delete logicalFiles[k];
} }
...@@ -302,11 +316,40 @@ void db2i_table::getDB2QualifiedNameFromPath(const char* path, char* to) ...@@ -302,11 +316,40 @@ void db2i_table::getDB2QualifiedNameFromPath(const char* path, char* to)
} }
size_t db2i_table::smartFilenameToTableName(const char *in, char* out, size_t outlen)
{
if (strchr(in, '@') == NULL)
{
return filename_to_tablename(in, out, outlen);
}
char* test = (char*) my_malloc(outlen, MYF(MY_WME));
filename_to_tablename(in, test, outlen);
char* cur = test;
while (*cur)
{
if ((*cur <= 0x20) || (*cur >= 0x80))
{
strncpy(out, in, outlen);
my_free(test, MYF(0));
return min(outlen, strlen(out));
}
++cur;
}
strncpy(out, test, outlen);
my_free(test, MYF(0));
return min(outlen, strlen(out));
}
void db2i_table::filenameToTablename(const char* in, char* out, size_t outlen) void db2i_table::filenameToTablename(const char* in, char* out, size_t outlen)
{ {
if (strchr(in, '#') == NULL) if (strchr(in, '#') == NULL)
{ {
filename_to_tablename(in, out, outlen); smartFilenameToTableName(in, out, outlen);
return; return;
} }
...@@ -326,7 +369,7 @@ void db2i_table::filenameToTablename(const char* in, char* out, size_t outlen) ...@@ -326,7 +369,7 @@ void db2i_table::filenameToTablename(const char* in, char* out, size_t outlen)
memcpy(temp, part1, min(outlen, part2 - part1)); memcpy(temp, part1, min(outlen, part2 - part1));
temp[min(outlen-1, part2-part1)] = 0; temp[min(outlen-1, part2-part1)] = 0;
int32 accumLen = filename_to_tablename(temp, out, outlen); int32 accumLen = smartFilenameToTableName(temp, out, outlen);
if (part2 && (accumLen + 4 < outlen)) if (part2 && (accumLen + 4 < outlen))
{ {
...@@ -337,7 +380,7 @@ void db2i_table::filenameToTablename(const char* in, char* out, size_t outlen) ...@@ -337,7 +380,7 @@ void db2i_table::filenameToTablename(const char* in, char* out, size_t outlen)
memcpy(temp, part3, min(outlen, part4-part3)); memcpy(temp, part3, min(outlen, part4-part3));
temp[min(outlen-1, part4-part3)] = 0; temp[min(outlen-1, part4-part3)] = 0;
accumLen += filename_to_tablename(temp, strend(out), outlen-accumLen); accumLen += smartFilenameToTableName(temp, strend(out), outlen-accumLen);
if (part4 && (accumLen + (strend(in) - part4 + 1) < outlen)) if (part4 && (accumLen + (strend(in) - part4 + 1) < outlen))
{ {
......
...@@ -270,6 +270,7 @@ class db2i_table ...@@ -270,6 +270,7 @@ class db2i_table
void findConversionDefinition(enum_conversionDirection direction, uint16 fieldID); void findConversionDefinition(enum_conversionDirection direction, uint16 fieldID);
static void filenameToTablename(const char* in, char* out, size_t outlen); 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, void convertNativeToSQLName(const char* input,
char* output) char* output)
{ {
...@@ -301,6 +302,7 @@ class db2i_table ...@@ -301,6 +302,7 @@ class db2i_table
iconv_t* conversionDefinitions[2]; iconv_t* conversionDefinitions[2];
const TABLE_SHARE* mysqlTable; const TABLE_SHARE* mysqlTable;
uint16 logicalFileCount;
char* db2LibNameEbcdic; // Quoted and in EBCDIC char* db2LibNameEbcdic; // Quoted and in EBCDIC
char* db2LibNameAscii; char* db2LibNameAscii;
char* db2TableNameEbcdic; char* db2TableNameEbcdic;
...@@ -326,22 +328,18 @@ class db2i_table ...@@ -326,22 +328,18 @@ class db2i_table
*/ */
class db2i_file class db2i_file
{ {
enum RowFormats
{
readOnly = 0,
readWrite,
maxRowFormats
};
public: public:
mutable struct RowFormat struct RowFormat
{ {
uint16 readRowLen; uint16 readRowLen;
uint16 readRowNullOffset; uint16 readRowNullOffset;
uint16 writeRowLen; uint16 writeRowLen;
uint16 writeRowNullOffset; uint16 writeRowNullOffset;
char inited; char inited;
} formats[maxRowFormats]; };
public:
// Construct an instance for a physical file. // Construct an instance for a physical file.
db2i_file(db2i_table* table); db2i_file(db2i_table* table);
...@@ -375,25 +373,23 @@ class db2i_file ...@@ -375,25 +373,23 @@ class db2i_file
// This obtains the row layout associated with a particular access intent for // This obtains the row layout associated with a particular access intent for
// an open instance of the file. // an open instance of the file.
int useFile(FILE_HANDLE instanceHandle, int obtainRowFormat(FILE_HANDLE instanceHandle,
char intent, char intent,
char commitLevel, char commitLevel,
const RowFormat** activeFormat) const const RowFormat** activeFormat) const
{ {
DBUG_ENTER("db2i_file::useFile"); DBUG_ENTER("db2i_file::obtainRowFormat");
RowFormat* rowFormat; RowFormat* rowFormat;
if (intent == QMY_UPDATABLE) if (intent == QMY_UPDATABLE)
rowFormat = &(formats[readWrite]); rowFormat = &(formats[readWrite]);
else if (intent == QMY_READ_ONLY) else if (intent == QMY_READ_ONLY)
rowFormat = &(formats[readOnly]); rowFormat = &(formats[readOnly]);
else
DBUG_ASSERT(0);
if (!rowFormat->inited) if (unlikely(!rowFormat->inited))
{ {
int rc; int rc = db2i_ileBridge::getBridgeForThread()->
rc = db2i_ileBridge::getBridgeForThread()->initFileForIO(instanceHandle, initFileForIO(instanceHandle,
intent, intent,
commitLevel, commitLevel,
&(rowFormat->writeRowLen), &(rowFormat->writeRowLen),
...@@ -426,6 +422,15 @@ class db2i_file ...@@ -426,6 +422,15 @@ class db2i_file
} }
private: private:
enum RowFormats
{
readOnly = 0,
readWrite,
maxRowFormats
};
mutable RowFormat formats[maxRowFormats];
void commonCtorInit(); void commonCtorInit();
char* db2FileName; // Quoted and in EBCDIC char* db2FileName; // Quoted and in EBCDIC
......
...@@ -894,6 +894,7 @@ int32 db2i_ileBridge::savepoint(uint8 function, ...@@ -894,6 +894,7 @@ int32 db2i_ileBridge::savepoint(uint8 function,
return rc; return rc;
} }
static ILEMemHandle traceSpcHandle;
/** /**
Do initialization for the QMY_* APIs. Do initialization for the QMY_* APIs.
...@@ -902,7 +903,8 @@ int32 db2i_ileBridge::savepoint(uint8 function, ...@@ -902,7 +903,8 @@ int32 db2i_ileBridge::savepoint(uint8 function,
@return 0 if successful; error otherwise @return 0 if successful; error otherwise
*/ */
int32 db2i_ileBridge::initILE(const char* aspName) int32 db2i_ileBridge::initILE(const char* aspName,
uint16* traceCtlPtr)
{ {
// We forego the typical thread-based parms space because MySQL doesn't // We forego the typical thread-based parms space because MySQL doesn't
// allow us to clean it up before checking for memory leaks. As a result // allow us to clean it up before checking for memory leaks. As a result
...@@ -916,6 +918,8 @@ int32 db2i_ileBridge::initILE(const char* aspName) ...@@ -916,6 +918,8 @@ int32 db2i_ileBridge::initILE(const char* aspName)
return rc; return rc;
} }
registerPtr(traceCtlPtr, &traceSpcHandle);
struct ParmBlock struct ParmBlock
{ {
Qmy_MINI0100 parms; Qmy_MINI0100 parms;
...@@ -936,6 +940,9 @@ int32 db2i_ileBridge::initILE(const char* aspName) ...@@ -936,6 +940,9 @@ int32 db2i_ileBridge::initILE(const char* aspName)
memcpy(paddedName, aspName, strlen(aspName)); memcpy(paddedName, aspName, strlen(aspName));
convToEbcdic(paddedName, parmBlock->parms.RDBName, strlen(paddedName)); convToEbcdic(paddedName, parmBlock->parms.RDBName, strlen(paddedName));
parmBlock->parms.RDBNamLen = strlen(paddedName);
parmBlock->parms.TrcSpcHnd = traceSpcHandle;
rc = doIt(); rc = doIt();
if (rc) if (rc)
...@@ -964,6 +971,8 @@ int32 db2i_ileBridge::exitILE() ...@@ -964,6 +971,8 @@ int32 db2i_ileBridge::exitILE()
reportSystemAPIError(rc, (Qmy_Error_output_t*)parmBlock->outParms); reportSystemAPIError(rc, (Qmy_Error_output_t*)parmBlock->outParms);
} }
unregisterPtr(traceSpcHandle);
DBUG_PRINT("db2i_ileBridge::exitILE", ("Registered ptrs remaining: %d", registeredPtrs)); DBUG_PRINT("db2i_ileBridge::exitILE", ("Registered ptrs remaining: %d", registeredPtrs));
#ifndef DBUG_OFF #ifndef DBUG_OFF
if (registeredPtrs != 0) if (registeredPtrs != 0)
...@@ -1267,7 +1276,7 @@ int32 db2i_ileBridge::quiesceFileInstance(FILE_HANDLE rfileHandle) ...@@ -1267,7 +1276,7 @@ int32 db2i_ileBridge::quiesceFileInstance(FILE_HANDLE rfileHandle)
return rc; return rc;
} }
void db2i_ileBridge::PreservedHandleList::add(const char* newname, FILE_HANDLE newhandle) void db2i_ileBridge::PreservedHandleList::add(const char* newname, FILE_HANDLE newhandle, IBMDB2I_SHARE* share)
{ {
NameHandlePair *newPair = (NameHandlePair*)my_malloc(sizeof(NameHandlePair), MYF(MY_WME)); NameHandlePair *newPair = (NameHandlePair*)my_malloc(sizeof(NameHandlePair), MYF(MY_WME));
...@@ -1276,11 +1285,12 @@ void db2i_ileBridge::PreservedHandleList::add(const char* newname, FILE_HANDLE n ...@@ -1276,11 +1285,12 @@ void db2i_ileBridge::PreservedHandleList::add(const char* newname, FILE_HANDLE n
strcpy(newPair->name, newname); strcpy(newPair->name, newname);
newPair->handle = newhandle; newPair->handle = newhandle;
newPair->share = share;
DBUG_PRINT("db2i_ileBridge", ("Added handle %d for %s", uint32(newhandle), newname)); DBUG_PRINT("db2i_ileBridge", ("Added handle %d for %s", uint32(newhandle), newname));
} }
FILE_HANDLE db2i_ileBridge::PreservedHandleList::findAndRemove(const char* fileName) FILE_HANDLE db2i_ileBridge::PreservedHandleList::findAndRemove(const char* fileName, IBMDB2I_SHARE** share)
{ {
NameHandlePair* current = head; NameHandlePair* current = head;
NameHandlePair* prev = NULL; NameHandlePair* prev = NULL;
...@@ -1291,6 +1301,7 @@ FILE_HANDLE db2i_ileBridge::PreservedHandleList::findAndRemove(const char* fileN ...@@ -1291,6 +1301,7 @@ FILE_HANDLE db2i_ileBridge::PreservedHandleList::findAndRemove(const char* fileN
if (strcmp(fileName, current->name) == 0) if (strcmp(fileName, current->name) == 0)
{ {
FILE_HANDLE tmp = current->handle; FILE_HANDLE tmp = current->handle;
*share = current->share;
if (prev) if (prev)
prev->next = next; prev->next = next;
if (current == head) if (current == head)
......
...@@ -62,6 +62,7 @@ enum db2i_InfoRequestSpec ...@@ -62,6 +62,7 @@ enum db2i_InfoRequestSpec
}; };
extern handlerton *ibmdb2i_hton; extern handlerton *ibmdb2i_hton;
struct IBMDB2I_SHARE;
const uint32 db2i_ileBridge_MAX_INPARM_SIZE = 512; const uint32 db2i_ileBridge_MAX_INPARM_SIZE = 512;
const uint32 db2i_ileBridge_MAX_OUTPARM_SIZE = 512; const uint32 db2i_ileBridge_MAX_OUTPARM_SIZE = 512;
...@@ -220,7 +221,8 @@ class db2i_ileBridge ...@@ -220,7 +221,8 @@ class db2i_ileBridge
uint32* outLen, uint32* outLen,
uint32* outCnt); uint32* outCnt);
int32 optimizeTable(FILE_HANDLE rfileHandle); int32 optimizeTable(FILE_HANDLE rfileHandle);
static int32 initILE(const char* aspName); static int32 initILE(const char* aspName,
uint16* traceCtlPtr);
int32 initFileForIO(FILE_HANDLE rfileHandle, int32 initFileForIO(FILE_HANDLE rfileHandle,
char accessIntent, char accessIntent,
char commitLevel, char commitLevel,
...@@ -336,9 +338,9 @@ class db2i_ileBridge ...@@ -336,9 +338,9 @@ class db2i_ileBridge
@param newhandle The handle associated with newname @param newhandle The handle associated with newname
*/ */
void preserveHandle(const char* newname, FILE_HANDLE newhandle) void preserveHandle(const char* newname, FILE_HANDLE newhandle, IBMDB2I_SHARE* share)
{ {
pendingLockedHandles.add(newname, newhandle); pendingLockedHandles.add(newname, newhandle, share);
} }
/** /**
...@@ -348,9 +350,10 @@ class db2i_ileBridge ...@@ -348,9 +350,10 @@ class db2i_ileBridge
@return The handle associated with name @return The handle associated with name
*/ */
FILE_HANDLE findAndRemovePreservedHandle(const char* name) FILE_HANDLE findAndRemovePreservedHandle(const char* name, IBMDB2I_SHARE** share)
{ {
return pendingLockedHandles.findAndRemove(name); FILE_HANDLE hdl = pendingLockedHandles.findAndRemove(name, share);
return hdl;
} }
/** /**
...@@ -380,7 +383,7 @@ class db2i_ileBridge ...@@ -380,7 +383,7 @@ class db2i_ileBridge
@return A pointer to the 7 character message ID. @return A pointer to the 7 character message ID.
*/ */
const char* getErrorMsgID() static const char* getErrorMsgID()
{ {
return ((Qmy_Error_output_t*)parms()->outParms)->MsgId; return ((Qmy_Error_output_t*)parms()->outParms)->MsgId;
} }
...@@ -413,6 +416,13 @@ class db2i_ileBridge ...@@ -413,6 +416,13 @@ class db2i_ileBridge
return HA_ERR_NO_SUCH_TABLE; return HA_ERR_NO_SUCH_TABLE;
case QMY_ERR_NON_UNIQUE_KEY: case QMY_ERR_NON_UNIQUE_KEY:
return ER_DUP_ENTRY; 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; return rc;
} }
...@@ -458,14 +468,15 @@ class db2i_ileBridge ...@@ -458,14 +468,15 @@ class db2i_ileBridge
{ {
friend db2i_ileBridge* db2i_ileBridge::createNewBridge(CONNECTION_HANDLE); friend db2i_ileBridge* db2i_ileBridge::createNewBridge(CONNECTION_HANDLE);
public: public:
void add(const char* newname, FILE_HANDLE newhandle); void add(const char* newname, FILE_HANDLE newhandle, IBMDB2I_SHARE* share);
FILE_HANDLE findAndRemove(const char* fileName); FILE_HANDLE findAndRemove(const char* fileName, IBMDB2I_SHARE** share);
private: private:
struct NameHandlePair struct NameHandlePair
{ {
char name[FN_REFLEN]; char name[FN_REFLEN];
FILE_HANDLE handle; FILE_HANDLE handle;
IBMDB2I_SHARE* share;
NameHandlePair* next; NameHandlePair* next;
}* head; }* head;
} pendingLockedHandles; } pendingLockedHandles;
......
...@@ -257,7 +257,7 @@ void IOAsyncReadBuffer::newReadRequest(FILE_HANDLE infile, ...@@ -257,7 +257,7 @@ void IOAsyncReadBuffer::newReadRequest(FILE_HANDLE infile,
int fildes[2]; int fildes[2];
int ileDescriptor = QMY_REUSE; int ileDescriptor = QMY_REUSE;
closePipe(); interruptRead();
if (likely(useAsync)) if (likely(useAsync))
{ {
......
...@@ -73,10 +73,12 @@ class IORowBuffer ...@@ -73,10 +73,12 @@ class IORowBuffer
Sets up the buffer to hold the size indicated. Sets up the buffer to hold the size indicated.
@param rowLen length of the rows that will be stored in this buffer @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 @param size buffer size requested
*/ */
void allocBuf(uint32 rowLen, uint32 size) void allocBuf(uint32 rowLen, uint16 nullMapOffset, uint32 size)
{ {
nullOffset = nullMapOffset;
uint32 newSize = size + sizeof(BufferHdr_t); uint32 newSize = size + sizeof(BufferHdr_t);
// If the internal structure of the row is changing, we need to // If the internal structure of the row is changing, we need to
// remember this and notify the subclasses via initAfterAllocate(); // remember this and notify the subclasses via initAfterAllocate();
...@@ -129,6 +131,8 @@ class IORowBuffer ...@@ -129,6 +131,8 @@ class IORowBuffer
}; };
uint32 getRowCapacity() const {return rowCapacity;} uint32 getRowCapacity() const {return rowCapacity;}
uint32 getRowNullOffset() const {return nullOffset;}
uint32 getRowLength() const {return rowLength;}
protected: protected:
/** /**
...@@ -150,6 +154,7 @@ class IORowBuffer ...@@ -150,6 +154,7 @@ class IORowBuffer
uint32 allocSize; uint32 allocSize;
uint32 rowCapacity; uint32 rowCapacity;
uint32 rowLength; uint32 rowLength;
uint16 nullOffset;
uint32& usedRows() const { return ((BufferHdr_t*)(char*)data)->UsedRowCnt; } uint32& usedRows() const { return ((BufferHdr_t*)(char*)data)->UsedRowCnt; }
uint32& maxRows() const {return ((BufferHdr_t*)(char*)data)->MaxRowCnt; } uint32& maxRows() const {return ((BufferHdr_t*)(char*)data)->MaxRowCnt; }
}; };
...@@ -207,7 +212,7 @@ class IOReadBuffer : public IORowBuffer ...@@ -207,7 +212,7 @@ class IOReadBuffer : public IORowBuffer
IOReadBuffer() {;} IOReadBuffer() {;}
IOReadBuffer(uint32 rows, uint32 rowLength) IOReadBuffer(uint32 rows, uint32 rowLength)
{ {
allocBuf(rows, rows * rowLength); allocBuf(rows, 0, rows * rowLength);
maxRows() = rows; maxRows() = rows;
} }
......
...@@ -92,4 +92,16 @@ bool convertMySQLNameToDB2Name(const char* input, ...@@ -92,4 +92,16 @@ bool convertMySQLNameToDB2Name(const char* input,
return (o <= outlen-1); return (o <= outlen-1);
} }
bool isUpperOrQuote(const CHARSET_INFO* cs, const char* s)
{
while (*s)
{
if (my_isupper(cs, *s) || (*s == '"'))
++s;
else
return false;
}
return true;
}
#endif #endif
...@@ -140,6 +140,17 @@ ha_rows ha_ibmdb2i::records_in_range(uint inx, ...@@ -140,6 +140,17 @@ ha_rows ha_ibmdb2i::records_in_range(uint inx,
} }
keyCnt = maxKeyCnt >= minKeyCnt ? maxKeyCnt : minKeyCnt; 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 Allocate the space needed to pass range information to DB2. The
space must be large enough to store the following: space must be large enough to store the following:
...@@ -197,7 +208,6 @@ ha_rows ha_ibmdb2i::records_in_range(uint inx, ...@@ -197,7 +208,6 @@ ha_rows ha_ibmdb2i::records_in_range(uint inx,
is not null, the data offset and length must be set, and the literal is not null, the data offset and length must be set, and the literal
value stored for access by DB2. value stored for access by DB2.
*/ */
for (int partsInUse = 0; partsInUse < keyCnt; ++partsInUse) for (int partsInUse = 0; partsInUse < keyCnt; ++partsInUse)
{ {
Field *field= curKey.key_part[partsInUse].field; Field *field= curKey.key_part[partsInUse].field;
...@@ -298,20 +308,29 @@ ha_rows ha_ibmdb2i::records_in_range(uint inx, ...@@ -298,20 +308,29 @@ ha_rows ha_ibmdb2i::records_in_range(uint inx,
else else
tempLen = field->field_length; tempLen = field->field_length;
if (litDefPtr->DataType == QMY_CHAR || litDefPtr->DataType == QMY_VARCHAR || /* Determine if we are dealing with a partial key and if so, find the end of the partial key. */
(strncmp(fieldCharSet->csname, "utf8", sizeof("utf8")) == 0)) if (litDefPtr->DataType == QMY_CHAR || litDefPtr->DataType == QMY_VARCHAR )
{ { /* Char or varchar. If UTF8, no conversion is done to DB2 graphic.) */
endOfMinPtr = (char*)memchr(tempMinPtr,field->charset()->min_sort_char,tempLen); endOfMinPtr = (char*)memchr(tempMinPtr,field->charset()->min_sort_char,tempLen);
if (endOfMinPtr) if (endOfMinPtr)
endOfLiteralPtr = tempPtr + (((uint32_t)(endOfMinPtr - tempMinPtr)) * endOfLiteralPtr = tempPtr + ((uint32_t)(endOfMinPtr - tempMinPtr));
(litDefPtr->DataType == QMY_CHAR || litDefPtr->DataType == QMY_VARCHAR ? 1 : 2));
} }
else else
{ {
if (strncmp(fieldCharSet->csname, "utf8", sizeof("utf8")) == 0)
{ /* The MySQL charset is UTF8 but we are converting to graphic on DB2. Divide number of UTF8 bytes
by 3 to get the number of characters, then multiple by 2 for double-byte graphic.*/
endOfMinPtr = (char*)memchr(tempMinPtr,field->charset()->min_sort_char,tempLen);
if (endOfMinPtr)
endOfLiteralPtr = tempPtr + (((uint32_t)((endOfMinPtr - tempMinPtr)) / 3) * 2);
}
else
{ /* The DB2 data type is graphic or vargraphic, and we are not converting from UTF8 to graphic. */
endOfMinPtr = (char*)wmemchr((wchar_t*)tempMinPtr,field->charset()->min_sort_char,tempLen/2); endOfMinPtr = (char*)wmemchr((wchar_t*)tempMinPtr,field->charset()->min_sort_char,tempLen/2);
if (endOfMinPtr) if (endOfMinPtr)
endOfLiteralPtr = tempPtr + (endOfMinPtr - tempMinPtr); endOfLiteralPtr = tempPtr + (endOfMinPtr - tempMinPtr);
} }
}
/* Enforce here that a partial is only allowed on the last field position /* Enforce here that a partial is only allowed on the last field position
of the key composite */ of the key composite */
if (endOfLiteralPtr) if (endOfLiteralPtr)
...@@ -354,6 +373,11 @@ ha_rows ha_ibmdb2i::records_in_range(uint inx, ...@@ -354,6 +373,11 @@ ha_rows ha_ibmdb2i::records_in_range(uint inx,
} }
else // max_key field is not 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 (!reuseLiteral)
{ {
if (literalCnt) if (literalCnt)
......
...@@ -78,7 +78,7 @@ static MYSQL_SYSVAR_STR(rdb_name, ibmdb2i_rdb_name, ...@@ -78,7 +78,7 @@ static MYSQL_SYSVAR_STR(rdb_name, ibmdb2i_rdb_name,
static MYSQL_THDVAR_BOOL(transaction_unsafe, static MYSQL_THDVAR_BOOL(transaction_unsafe,
0, 0,
"True auto-commit mode.", "Disable support for commitment control",
NULL, NULL,
NULL, NULL,
FALSE); FALSE);
...@@ -113,16 +113,26 @@ static MYSQL_THDVAR_UINT(max_write_buffer_size, ...@@ -113,16 +113,26 @@ static MYSQL_THDVAR_UINT(max_write_buffer_size,
64*1024*1024, 64*1024*1024,
1); 1);
static MYSQL_THDVAR_BOOL(create_time_columns_as_TOD, static MYSQL_THDVAR_BOOL(compat_opt_time_as_duration,
0, 0,
"Control how new TIME columns should be defined in DB2. 1=time-of-day (default), 0=duration.", "Control how new TIME columns should be defined in DB2. 0=time-of-day (default), 1=duration.",
NULL, NULL,
NULL, NULL,
TRUE); FALSE);
static MYSQL_THDVAR_UINT(compat_opt_year_as_int,
0,
"Control how new YEAR columns should be defined in DB2. 0=CHAR(4) (default), 1=SMALLINT.",
NULL,
NULL,
0,
0,
1,
1);
static MYSQL_THDVAR_UINT(map_blob_to_varchar, static MYSQL_THDVAR_UINT(compat_opt_blob_cols,
0, 0,
"Control how new TEXT columns should be defined in DB2. 0=CLOB (default), 1=VARCHAR", "Control how new TEXT and BLOB columns should be defined in DB2. 0=CLOB/BLOB (default), 1=VARCHAR/VARBINARY",
NULL, NULL,
NULL, NULL,
0, 0,
...@@ -130,6 +140,23 @@ static MYSQL_THDVAR_UINT(map_blob_to_varchar, ...@@ -130,6 +140,23 @@ static MYSQL_THDVAR_UINT(map_blob_to_varchar,
1, 1,
1); 1);
static MYSQL_THDVAR_UINT(compat_opt_allow_zero_date_vals,
0,
"Allow substitute values to be used when storing a column with a 0000-00-00 date component. 0=No substitution (default), 1=Substitute '0001-01-01'",
NULL,
NULL,
0,
0,
1,
1);
static MYSQL_THDVAR_BOOL(propagate_default_col_vals,
0,
"Should DEFAULT column values be propagated to the DB2 table definition.",
NULL,
NULL,
TRUE);
static my_bool ibmdb2i_assume_exclusive_use; static my_bool ibmdb2i_assume_exclusive_use;
static MYSQL_SYSVAR_BOOL(assume_exclusive_use, ibmdb2i_assume_exclusive_use, static MYSQL_SYSVAR_BOOL(assume_exclusive_use, ibmdb2i_assume_exclusive_use,
0, 0,
...@@ -155,7 +182,7 @@ static MYSQL_THDVAR_UINT(create_index_option, ...@@ -155,7 +182,7 @@ static MYSQL_THDVAR_UINT(create_index_option,
1, 1,
1); 1);
static MYSQL_THDVAR_UINT(discovery_mode, /* static MYSQL_THDVAR_UINT(discovery_mode,
0, 0,
"Unsupported", "Unsupported",
NULL, NULL,
...@@ -163,6 +190,17 @@ static MYSQL_THDVAR_UINT(discovery_mode, ...@@ -163,6 +190,17 @@ static MYSQL_THDVAR_UINT(discovery_mode,
0, 0,
0, 0,
1, 1,
1); */
static uint32 ibmdb2i_system_trace;
static MYSQL_SYSVAR_UINT(system_trace_level, ibmdb2i_system_trace,
0,
"Set system tracing level",
NULL,
NULL,
0,
0,
63,
1); 1);
...@@ -276,7 +314,7 @@ static int ibmdb2i_init_func(void *p) ...@@ -276,7 +314,7 @@ static int ibmdb2i_init_func(void *p)
ibmdb2i_rdb_name[i] = my_toupper(system_charset_info, (uchar)ibmdb2i_rdb_name[i]); ibmdb2i_rdb_name[i] = my_toupper(system_charset_info, (uchar)ibmdb2i_rdb_name[i]);
} }
rc = db2i_ileBridge::initILE(ibmdb2i_rdb_name); rc = db2i_ileBridge::initILE(ibmdb2i_rdb_name, (uint16*)(((char*)&ibmdb2i_system_trace)+2));
if (rc == 0) if (rc == 0)
{ {
was_ILE_inited = true; was_ILE_inited = true;
...@@ -386,6 +424,8 @@ int ha_ibmdb2i::free_share(IBMDB2I_SHARE *share) ...@@ -386,6 +424,8 @@ int ha_ibmdb2i::free_share(IBMDB2I_SHARE *share)
thr_lock_delete(&share->lock); thr_lock_delete(&share->lock);
pthread_mutex_destroy(&share->mutex); pthread_mutex_destroy(&share->mutex);
my_free(share, MYF(0)); my_free(share, MYF(0));
pthread_mutex_unlock(&ibmdb2i_mutex);
return 1;
} }
pthread_mutex_unlock(&ibmdb2i_mutex); pthread_mutex_unlock(&ibmdb2i_mutex);
...@@ -528,6 +568,8 @@ ha_ibmdb2i::ha_ibmdb2i(handlerton *hton, TABLE_SHARE *table_arg) ...@@ -528,6 +568,8 @@ ha_ibmdb2i::ha_ibmdb2i(handlerton *hton, TABLE_SHARE *table_arg)
ha_ibmdb2i::~ha_ibmdb2i() ha_ibmdb2i::~ha_ibmdb2i()
{ {
DBUG_ASSERT(activeReferences == 0 || outstanding_start_bulk_insert);
if (indexHandles) if (indexHandles)
my_free(indexHandles, MYF(0)); my_free(indexHandles, MYF(0));
if (indexReadSizeEstimates) if (indexReadSizeEstimates)
...@@ -554,13 +596,17 @@ int ha_ibmdb2i::open(const char *name, int mode, uint test_if_locked) ...@@ -554,13 +596,17 @@ int ha_ibmdb2i::open(const char *name, int mode, uint test_if_locked)
initBridge(); initBridge();
if (!(share = get_share(name, table))) dataHandle = bridge()->findAndRemovePreservedHandle(name, &share);
if (share)
db2Table = share->db2Table;
if (!share && (!(share = get_share(name, table))))
DBUG_RETURN(my_errno); DBUG_RETURN(my_errno);
thr_lock_data_init(&share->lock,&lock,NULL); thr_lock_data_init(&share->lock,&lock,NULL);
info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE); info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE);
dataHandle = bridge()->findAndRemovePreservedHandle(name);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -572,13 +618,17 @@ int ha_ibmdb2i::close(void) ...@@ -572,13 +618,17 @@ int ha_ibmdb2i::close(void)
{ {
DBUG_ENTER("ha_ibmdb2i::close"); DBUG_ENTER("ha_ibmdb2i::close");
int32 rc = 0; int32 rc = 0;
bool preserveShare = false;
db2i_ileBridge* bridge = db2i_ileBridge::getBridgeForThread(); db2i_ileBridge* bridge = db2i_ileBridge::getBridgeForThread();
if (dataHandle) if (dataHandle)
{ {
if (bridge->expectErrors(QMY_ERR_PEND_LOCKS)->deallocateFile(dataHandle, FALSE) == QMY_ERR_PEND_LOCKS) if (bridge->expectErrors(QMY_ERR_PEND_LOCKS)->deallocateFile(dataHandle, FALSE) == QMY_ERR_PEND_LOCKS)
bridge->preserveHandle(share->table_name, dataHandle); {
bridge->preserveHandle(share->table_name, dataHandle, share);
preserveShare = true;
}
dataHandle = 0; dataHandle = 0;
} }
...@@ -592,7 +642,11 @@ int ha_ibmdb2i::close(void) ...@@ -592,7 +642,11 @@ int ha_ibmdb2i::close(void)
cleanupBuffers(); cleanupBuffers();
free_share(share); if (!preserveShare)
{
if (free_share(share))
share = NULL;
}
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
...@@ -608,26 +662,28 @@ int ha_ibmdb2i::write_row(uchar * buf) ...@@ -608,26 +662,28 @@ int ha_ibmdb2i::write_row(uchar * buf)
DBUG_RETURN( last_start_bulk_insert_rc ); DBUG_RETURN( last_start_bulk_insert_rc );
ha_statistic_increment(&SSV::ha_write_count); ha_statistic_increment(&SSV::ha_write_count);
int rc; int rc = 0;
bool fileHandleNeedsRelease = false; bool fileHandleNeedsRelease = false;
if (!activeHandle) if (!activeHandle)
{ {
rc = useDataFile(QMY_UPDATABLE); rc = useDataFile();
if (rc) DBUG_RETURN(rc); if (rc) DBUG_RETURN(rc);
fileHandleNeedsRelease = true; fileHandleNeedsRelease = true;
} }
if (!outstanding_start_bulk_insert) if (!outstanding_start_bulk_insert)
prepWriteBuffer(1); rc = prepWriteBuffer(1, getFileForActiveHandle());
if (!rc)
{
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
table->timestamp_field->set_time(); table->timestamp_field->set_time();
char* writeBuffer = activeWriteBuf->addRow(); char* writeBuffer = activeWriteBuf->addRow();
rc = prepareRowForWrite(writeBuffer, rc = prepareRowForWrite(writeBuffer,
writeBuffer+activeFormat->writeRowNullOffset, writeBuffer+activeWriteBuf->getRowNullOffset(),
true); true);
if (rc == 0) if (rc == 0)
{ {
...@@ -659,7 +715,7 @@ int ha_ibmdb2i::write_row(uchar * buf) ...@@ -659,7 +715,7 @@ int ha_ibmdb2i::write_row(uchar * buf)
} }
else else
activeWriteBuf->deleteRow(); activeWriteBuf->deleteRow();
}
if (fileHandleNeedsRelease) if (fileHandleNeedsRelease)
releaseActiveHandle(); releaseActiveHandle();
...@@ -748,7 +804,7 @@ int ha_ibmdb2i::update_row(const uchar * old_data, uchar * new_data) ...@@ -748,7 +804,7 @@ int ha_ibmdb2i::update_row(const uchar * old_data, uchar * new_data)
char* writeBuf = activeWriteBuf->addRow(); char* writeBuf = activeWriteBuf->addRow();
rc = prepareRowForWrite(writeBuf, rc = prepareRowForWrite(writeBuf,
writeBuf+activeFormat->writeRowNullOffset, writeBuf+activeWriteBuf->getRowNullOffset(),
onDupUpdate); onDupUpdate);
char* lastDupKeyNamePtr = NULL; char* lastDupKeyNamePtr = NULL;
...@@ -819,9 +875,28 @@ int ha_ibmdb2i::index_init(uint idx, bool sorted) ...@@ -819,9 +875,28 @@ int ha_ibmdb2i::index_init(uint idx, bool sorted)
active_index=idx; active_index=idx;
rc = useIndexFile(accessIntent, idx); rc = useIndexFile(idx);
if (accessIntent != QMY_READ_ONLY)
prepWriteBuffer(1); if (!rc)
{
// THD* thd = ha_thd();
// if (accessIntent == QMY_UPDATABLE &&
// thd_tx_isolation(thd) == ISO_REPEATABLE_READ &&
// !THDVAR(thd, transaction_unsafe))
// {
// readAccessIntent = QMY_READ_ONLY;
// }
// else
// {
readAccessIntent = accessIntent;
// }
if (!rc && accessIntent != QMY_READ_ONLY)
rc = prepWriteBuffer(1, db2Table->indexFile(idx));
if (rc)
releaseIndexFile(idx);
}
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
...@@ -839,16 +914,18 @@ int ha_ibmdb2i::index_read(uchar * buf, const uchar * key, ...@@ -839,16 +914,18 @@ int ha_ibmdb2i::index_read(uchar * buf, const uchar * key,
int rc; int rc;
ha_rows estimatedRows = getIndexReadEstimate(active_index); ha_rows estimatedRows = getIndexReadEstimate(active_index);
rc = prepReadBuffer(estimatedRows); rc = prepReadBuffer(estimatedRows, db2Table->indexFile(active_index), readAccessIntent);
if (unlikely(rc)) DBUG_RETURN(rc); if (unlikely(rc)) DBUG_RETURN(rc);
DBUG_ASSERT(activeReadBuf); DBUG_ASSERT(activeReadBuf);
keyBuf.allocBuf(activeFormat->readRowLen, activeFormat->readRowLen); keyBuf.allocBuf(activeReadBuf->getRowLength(),
activeReadBuf->getRowNullOffset(),
activeReadBuf->getRowLength());
keyBuf.zeroBuf(); keyBuf.zeroBuf();
char* db2KeyBufPtr = keyBuf.ptr(); char* db2KeyBufPtr = keyBuf.ptr();
char* nullKeyMap = db2KeyBufPtr + activeFormat->readRowNullOffset; char* nullKeyMap = db2KeyBufPtr + activeReadBuf->getRowNullOffset();
const uchar* keyBegin = key; const uchar* keyBegin = key;
int partsInUse; int partsInUse;
...@@ -990,7 +1067,9 @@ int ha_ibmdb2i::index_first(uchar * buf) ...@@ -990,7 +1067,9 @@ int ha_ibmdb2i::index_first(uchar * buf)
if (unlikely(last_index_init_rc)) DBUG_RETURN(last_index_init_rc); if (unlikely(last_index_init_rc)) DBUG_RETURN(last_index_init_rc);
int rc = prepReadBuffer(DEFAULT_MAX_ROWS_TO_BUFFER); int rc = prepReadBuffer(DEFAULT_MAX_ROWS_TO_BUFFER,
db2Table->indexFile(active_index),
readAccessIntent);
if (rc == 0) if (rc == 0)
{ {
...@@ -1010,7 +1089,9 @@ int ha_ibmdb2i::index_last(uchar * buf) ...@@ -1010,7 +1089,9 @@ int ha_ibmdb2i::index_last(uchar * buf)
if (unlikely(last_index_init_rc)) DBUG_RETURN(last_index_init_rc); if (unlikely(last_index_init_rc)) DBUG_RETURN(last_index_init_rc);
int rc = prepReadBuffer(DEFAULT_MAX_ROWS_TO_BUFFER); int rc = prepReadBuffer(DEFAULT_MAX_ROWS_TO_BUFFER,
db2Table->indexFile(active_index),
readAccessIntent);
if (rc == 0) if (rc == 0)
{ {
...@@ -1045,18 +1126,29 @@ int ha_ibmdb2i::rnd_init(bool scan) ...@@ -1045,18 +1126,29 @@ int ha_ibmdb2i::rnd_init(bool scan)
rowsToBlockOnRead = DEFAULT_MAX_ROWS_TO_BUFFER; rowsToBlockOnRead = DEFAULT_MAX_ROWS_TO_BUFFER;
} }
rc = useDataFile(accessIntent); rc = useDataFile();
if (rc == 0) if (!rc)
{ {
if (accessIntent != QMY_READ_ONLY) // THD* thd = ha_thd();
prepWriteBuffer(1); // if (accessIntent == QMY_UPDATABLE &&
rc = prepReadBuffer(rowsToBlockOnRead); // thd_tx_isolation(thd) == ISO_REPEATABLE_READ &&
// !THDVAR(thd, transaction_unsafe))
// {
// readAccessIntent = QMY_READ_ONLY;
// }
// else
// {
readAccessIntent = accessIntent;
// }
if (rc == 0 && scan) rc = prepReadBuffer(rowsToBlockOnRead, db2Table->dataFile(), readAccessIntent);
{
if (!rc && accessIntent != QMY_READ_ONLY)
rc = prepWriteBuffer(1, db2Table->dataFile());
if (!rc && scan)
doInitialRead(QMY_FIRST, rowsToBlockOnRead); doInitialRead(QMY_FIRST, rowsToBlockOnRead);
}
if (rc) if (rc)
releaseDataFile(); releaseDataFile();
...@@ -1167,7 +1259,10 @@ int ha_ibmdb2i::rnd_pos(uchar * buf, uchar *pos) ...@@ -1167,7 +1259,10 @@ int ha_ibmdb2i::rnd_pos(uchar * buf, uchar *pos)
if (likely(rc == 0)) if (likely(rc == 0))
{ {
rc = prepReadBuffer(1); rc = prepReadBuffer(1, getFileForActiveHandle(), accessIntent);
if (likely(rc == 0) && accessIntent == QMY_UPDATABLE)
rc = prepWriteBuffer(1, getFileForActiveHandle());
if (likely(rc == 0)) if (likely(rc == 0))
{ {
...@@ -1181,7 +1276,7 @@ int ha_ibmdb2i::rnd_pos(uchar * buf, uchar *pos) ...@@ -1181,7 +1276,7 @@ int ha_ibmdb2i::rnd_pos(uchar * buf, uchar *pos)
{ {
rrnAssocHandle = activeHandle; rrnAssocHandle = activeHandle;
const char* readBuf = activeReadBuf->getRowN(0); const char* readBuf = activeReadBuf->getRowN(0);
rc = mungeDB2row(buf, readBuf, readBuf + activeFormat->readRowNullOffset, false); rc = mungeDB2row(buf, readBuf, readBuf + activeReadBuf->getRowNullOffset(), false);
releaseRowNeeded = TRUE; releaseRowNeeded = TRUE;
} }
} }
...@@ -1450,7 +1545,9 @@ int ha_ibmdb2i::getNextIdVal(ulonglong *value) ...@@ -1450,7 +1545,9 @@ int ha_ibmdb2i::getNextIdVal(ulonglong *value)
char queryBuffer[MAX_DB2_COLNAME_LENGTH + MAX_DB2_QUALIFIEDNAME_LENGTH + 64]; char queryBuffer[MAX_DB2_COLNAME_LENGTH + MAX_DB2_QUALIFIEDNAME_LENGTH + 64];
strcpy(queryBuffer, " SELECT CAST(MAX( "); strcpy(queryBuffer, " SELECT CAST(MAX( ");
convertMySQLNameToDB2Name(table->found_next_number_field->field_name, strend(queryBuffer), MAX_DB2_COLNAME_LENGTH+1); convertMySQLNameToDB2Name(table->found_next_number_field->field_name,
strend(queryBuffer),
MAX_DB2_COLNAME_LENGTH+1);
strcat(queryBuffer, ") AS BIGINT) FROM "); strcat(queryBuffer, ") AS BIGINT) FROM ");
db2Table->getDB2QualifiedName(strend(queryBuffer)); db2Table->getDB2QualifiedName(strend(queryBuffer));
DBUG_ASSERT(strlen(queryBuffer) < sizeof(queryBuffer)); DBUG_ASSERT(strlen(queryBuffer) < sizeof(queryBuffer));
...@@ -1621,7 +1718,9 @@ int ha_ibmdb2i::reset_auto_increment(ulonglong value) ...@@ -1621,7 +1718,9 @@ int ha_ibmdb2i::reset_auto_increment(ulonglong value)
query.append(fileName); query.append(fileName);
query.append(STRING_WITH_LEN(" ALTER COLUMN ")); query.append(STRING_WITH_LEN(" ALTER COLUMN "));
char colName[MAX_DB2_COLNAME_LENGTH+1]; char colName[MAX_DB2_COLNAME_LENGTH+1];
convertMySQLNameToDB2Name(table->found_next_number_field->field_name, colName, sizeof(colName)); convertMySQLNameToDB2Name(table->found_next_number_field->field_name,
colName,
sizeof(colName));
query.append(colName); query.append(colName);
char restart_value[22]; char restart_value[22];
...@@ -1637,10 +1736,10 @@ int ha_ibmdb2i::reset_auto_increment(ulonglong value) ...@@ -1637,10 +1736,10 @@ int ha_ibmdb2i::reset_auto_increment(ulonglong value)
rc = db2i_ileBridge::getBridgeForThread()->execSQL(sqlStream.getPtrToData(), rc = db2i_ileBridge::getBridgeForThread()->execSQL(sqlStream.getPtrToData(),
sqlStream.getStatementCount(), sqlStream.getStatementCount(),
getCommitLevel(), QMY_NONE, //getCommitLevel(),
FALSE,
FALSE, FALSE,
FALSE, FALSE,
TRUE, //FALSE,
dataHandle); dataHandle);
if (rc == 0) if (rc == 0)
db2Table->updateStartId(value); db2Table->updateStartId(value);
...@@ -1657,7 +1756,8 @@ int ha_ibmdb2i::reset_auto_increment(ulonglong value) ...@@ -1657,7 +1756,8 @@ int ha_ibmdb2i::reset_auto_increment(ulonglong value)
bool ha_ibmdb2i::get_error_message(int error, String *buf) bool ha_ibmdb2i::get_error_message(int error, String *buf)
{ {
DBUG_ENTER("ha_ibmdb2i::get_error_message"); DBUG_ENTER("ha_ibmdb2i::get_error_message");
if (error >= DB2I_FIRST_ERR && error <= DB2I_LAST_ERR) if ((error >= DB2I_FIRST_ERR && error <= DB2I_LAST_ERR) ||
(error >= QMY_ERR_MIN && error <= QMY_ERR_MAX))
{ {
db2i_ileBridge* bridge = db2i_ileBridge::getBridgeForThread(ha_thd()); db2i_ileBridge* bridge = db2i_ileBridge::getBridgeForThread(ha_thd());
char* errMsg = bridge->getErrorStorage(); char* errMsg = bridge->getErrorStorage();
...@@ -1774,6 +1874,8 @@ int ha_ibmdb2i::external_lock(THD *thd, int lock_type) ...@@ -1774,6 +1874,8 @@ int ha_ibmdb2i::external_lock(THD *thd, int lock_type)
} }
// Cache this away so we don't have to access it on each row operation
cachedZeroDateOption = (enum_ZeroDate)THDVAR(thd, compat_opt_allow_zero_date_vals);
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
...@@ -1834,9 +1936,16 @@ int ha_ibmdb2i::delete_table(const char *name) ...@@ -1834,9 +1936,16 @@ int ha_ibmdb2i::delete_table(const char *name)
if (rc == 0) if (rc == 0)
{ {
db2i_table::deleteAssocFiles(name); db2i_table::deleteAssocFiles(name);
FILE_HANDLE savedHandle = bridge->findAndRemovePreservedHandle(name); }
if (savedHandle)
FILE_HANDLE savedHandle = bridge->findAndRemovePreservedHandle(name, &share);
while (savedHandle)
{
bridge->deallocateFile(savedHandle, TRUE); bridge->deallocateFile(savedHandle, TRUE);
DBUG_ASSERT(share);
if (free_share(share))
share = NULL;
savedHandle = bridge->findAndRemovePreservedHandle(name, &share);
} }
my_errno = rc; my_errno = rc;
...@@ -2012,7 +2121,8 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg, ...@@ -2012,7 +2121,8 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg,
if (osVersion.v < 6) if (osVersion.v < 6)
{ {
if (strlen(libName) > MAX_DB2_V5R4_LIBNAME_LENGTH) if (strlen(libName) >
MAX_DB2_V5R4_LIBNAME_LENGTH + (isUpperOrQuote(system_charset_info, libName) ? 2 : 0))
{ {
getErrTxt(DB2I_ERR_TOO_LONG_SCHEMA,libName, MAX_DB2_V5R4_LIBNAME_LENGTH); getErrTxt(DB2I_ERR_TOO_LONG_SCHEMA,libName, MAX_DB2_V5R4_LIBNAME_LENGTH);
DBUG_RETURN(DB2I_ERR_TOO_LONG_SCHEMA); DBUG_RETURN(DB2I_ERR_TOO_LONG_SCHEMA);
...@@ -2043,8 +2153,11 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg, ...@@ -2043,8 +2153,11 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg,
query.append(STRING_WITH_LEN(" (")); query.append(STRING_WITH_LEN(" ("));
THD* thd = ha_thd(); THD* thd = ha_thd();
enum_TimeFormat timeFormat = (THDVAR(thd, create_time_columns_as_TOD) ? TIME_OF_DAY : DURATION); enum_TimeFormat timeFormat = (enum_TimeFormat)(THDVAR(thd, compat_opt_time_as_duration));
enum_BlobMapping blobMapping = (enum_BlobMapping)(THDVAR(thd, map_blob_to_varchar)); enum_YearFormat yearFormat = (enum_YearFormat)(THDVAR(thd, compat_opt_year_as_int));
enum_BlobMapping blobMapping = (enum_BlobMapping)(THDVAR(thd, compat_opt_blob_cols));
enum_ZeroDate zeroDate = (enum_ZeroDate)(THDVAR(thd, compat_opt_allow_zero_date_vals));
bool propagateDefaults = THDVAR(thd, propagate_default_col_vals);
Field **field; Field **field;
for (field= table_arg->field; *field; field++) for (field= table_arg->field; *field; field++)
...@@ -2061,7 +2174,13 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg, ...@@ -2061,7 +2174,13 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg,
query.append(colName); query.append(colName);
query.append(' '); query.append(' ');
if (rc = getFieldTypeMapping(*field, query, timeFormat, blobMapping)) if (rc = getFieldTypeMapping(*field,
query,
timeFormat,
blobMapping,
zeroDate,
propagateDefaults,
yearFormat))
DBUG_RETURN(rc); DBUG_RETURN(rc);
if ( (*field)->flags & NOT_NULL_FLAG ) if ( (*field)->flags & NOT_NULL_FLAG )
...@@ -2106,42 +2225,34 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg, ...@@ -2106,42 +2225,34 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg,
} }
} }
String primaryKeyQuery; bool primaryHasStringField = false;
primaryKeyQuery.length(0);
if (table_arg->s->primary_key != MAX_KEY && !isTemporary) if (table_arg->s->primary_key != MAX_KEY && !isTemporary)
{ {
KEY& curKey = table_arg->key_info[table_arg->s->primary_key]; KEY& curKey = table_arg->key_info[table_arg->s->primary_key];
primaryKeyQuery.append(STRING_WITH_LEN(" PRIMARY KEY( ")); query.append(STRING_WITH_LEN(", PRIMARY KEY( "));
for (int j = 0; j < curKey.key_parts; ++j) for (int j = 0; j < curKey.key_parts; ++j)
{ {
if (j != 0) if (j != 0)
{ {
primaryKeyQuery.append( STRING_WITH_LEN(" , ") ); query.append( STRING_WITH_LEN(" , ") );
} }
Field* field = curKey.key_part[j].field; Field* field = curKey.key_part[j].field;
convertMySQLNameToDB2Name(field->field_name, colName, sizeof(colName)); convertMySQLNameToDB2Name(field->field_name, colName, sizeof(colName));
primaryKeyQuery.append(colName); query.append(colName);
rc = updateAssociatedSortSequence(field, enum_field_types type = field->real_type();
if (type == MYSQL_TYPE_VARCHAR || type == MYSQL_TYPE_BLOB ||
type == MYSQL_TYPE_STRING)
{
rc = updateAssociatedSortSequence(field->charset(),
&fileSortSequenceType, &fileSortSequenceType,
fileSortSequence, fileSortSequence,
fileSortSequenceLibrary); fileSortSequenceLibrary);
if (rc) DBUG_RETURN (rc); if (rc) DBUG_RETURN (rc);
primaryHasStringField = true;
} }
primaryKeyQuery.append(STRING_WITH_LEN(" ) "));
}
bool needAlterForPrimaryKey = FALSE;
if ((fileSortSequence[0] != '*') && (fileSortSequence[0] != 'Q')) // An ICU sort sequence
{
needAlterForPrimaryKey = TRUE;
}
else
{
if (primaryKeyQuery.length() > 0)
{
query.append(STRING_WITH_LEN(" , "));
query.append(primaryKeyQuery);
} }
query.append(STRING_WITH_LEN(" ) "));
} }
rc = buildDB2ConstraintString(thd->lex, rc = buildDB2ConstraintString(thd->lex,
...@@ -2162,20 +2273,6 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg, ...@@ -2162,20 +2273,6 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg,
SqlStatementStream sqlStream(query.length()); SqlStatementStream sqlStream(query.length());
sqlStream.addStatement(query,fileSortSequence,fileSortSequenceLibrary); sqlStream.addStatement(query,fileSortSequence,fileSortSequenceLibrary);
if (needAlterForPrimaryKey == TRUE && !isTemporary)
{
rc = buildCreateIndexStatement(sqlStream,
table_arg->key_info[table_arg->s->primary_key],
true,
libName,
fileName);
if (rc) DBUG_RETURN (rc);
}
uint i = 0;
for (uint i = 0; i < table_arg->s->keys; ++i) for (uint i = 0; i < table_arg->s->keys; ++i)
{ {
if (i != table_arg->s->primary_key || isTemporary) if (i != table_arg->s->primary_key || isTemporary)
...@@ -2193,8 +2290,8 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg, ...@@ -2193,8 +2290,8 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg,
initBridge(); initBridge();
if (THDVAR(thd, discovery_mode) == 1) // if (THDVAR(thd, discovery_mode) == 1)
bridge()->expectErrors(QMY_ERR_TABLE_EXISTS); // bridge()->expectErrors(QMY_ERR_TABLE_EXISTS);
rc = bridge()->execSQL(sqlStream.getPtrToData(), rc = bridge()->execSQL(sqlStream.getPtrToData(),
sqlStream.getStatementCount(), sqlStream.getStatementCount(),
...@@ -2209,7 +2306,7 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg, ...@@ -2209,7 +2306,7 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg,
my_error(ER_BLOB_USED_AS_KEY, MYF(0), "*unknown*"); my_error(ER_BLOB_USED_AS_KEY, MYF(0), "*unknown*");
rc = ER_BLOB_USED_AS_KEY; rc = ER_BLOB_USED_AS_KEY;
} }
else if (unlikely(rc == QMY_ERR_TABLE_EXISTS) && /* else if (unlikely(rc == QMY_ERR_TABLE_EXISTS) &&
THDVAR(thd, discovery_mode) == 1) THDVAR(thd, discovery_mode) == 1)
{ {
db2i_table* temp = new db2i_table(table_arg->s, name); db2i_table* temp = new db2i_table(table_arg->s, name);
...@@ -2221,6 +2318,7 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg, ...@@ -2221,6 +2318,7 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg,
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
*/
if (!rc && !isTemporary) if (!rc && !isTemporary)
{ {
...@@ -2483,9 +2581,9 @@ void ha_ibmdb2i::start_bulk_insert(ha_rows rows) ...@@ -2483,9 +2581,9 @@ void ha_ibmdb2i::start_bulk_insert(ha_rows rows)
if (activeHandle == 0) if (activeHandle == 0)
{ {
last_start_bulk_insert_rc = useDataFile(QMY_UPDATABLE); last_start_bulk_insert_rc = useDataFile();
if (last_start_bulk_insert_rc == 0) if (last_start_bulk_insert_rc == 0)
prepWriteBuffer(rows); last_start_bulk_insert_rc = prepWriteBuffer(rows, db2Table->dataFile());
} }
if (last_start_bulk_insert_rc == 0) if (last_start_bulk_insert_rc == 0)
...@@ -2519,12 +2617,18 @@ int ha_ibmdb2i::end_bulk_insert() ...@@ -2519,12 +2617,18 @@ int ha_ibmdb2i::end_bulk_insert()
} }
int ha_ibmdb2i::prepReadBuffer(ha_rows rowsToRead) int ha_ibmdb2i::prepReadBuffer(ha_rows rowsToRead, const db2i_file* file, char intent)
{ {
DBUG_ENTER("ha_ibmdb2i::prepReadBuffer"); DBUG_ENTER("ha_ibmdb2i::prepReadBuffer");
DBUG_ASSERT((accessIntent == QMY_READ_ONLY || accessIntent == QMY_UPDATABLE) && rowsToRead > 0); DBUG_ASSERT(rowsToRead > 0);
int rc = 0; THD* thd = ha_thd();
char cmtLvl = getCommitLevel(thd);
const db2i_file::RowFormat* format;
int rc = file->obtainRowFormat(activeHandle, intent, cmtLvl, &format);
if (unlikely(rc)) DBUG_RETURN(rc);
if (lobFieldsRequested()) if (lobFieldsRequested())
{ {
...@@ -2534,10 +2638,8 @@ int ha_ibmdb2i::prepReadBuffer(ha_rows rowsToRead) ...@@ -2534,10 +2638,8 @@ int ha_ibmdb2i::prepReadBuffer(ha_rows rowsToRead)
rowsToRead = min(stats.records+1,min(rowsToRead, DEFAULT_MAX_ROWS_TO_BUFFER)); rowsToRead = min(stats.records+1,min(rowsToRead, DEFAULT_MAX_ROWS_TO_BUFFER));
THD* thd = ha_thd(); uint bufSize = min((format->readRowLen * rowsToRead), THDVAR(thd, max_read_buffer_size));
multiRowReadBuf.allocBuf(format->readRowLen, format->readRowNullOffset, bufSize);
uint bufSize = min((activeFormat->readRowLen * rowsToRead), THDVAR(thd, max_read_buffer_size));
multiRowReadBuf.allocBuf(activeFormat->readRowLen, bufSize);
activeReadBuf = &multiRowReadBuf; activeReadBuf = &multiRowReadBuf;
if (db2Table->hasBlobs()) if (db2Table->hasBlobs())
...@@ -2547,28 +2649,42 @@ int ha_ibmdb2i::prepReadBuffer(ha_rows rowsToRead) ...@@ -2547,28 +2649,42 @@ int ha_ibmdb2i::prepReadBuffer(ha_rows rowsToRead)
rc = prepareReadBufferForLobs(); rc = prepareReadBufferForLobs();
if (rc) DBUG_RETURN(rc); if (rc) DBUG_RETURN(rc);
} }
activeReadBuf->update(accessIntent, &releaseRowNeeded, getCommitLevel(thd));
// if (accessIntent == QMY_UPDATABLE &&
// thd_tx_isolation(thd) == ISO_REPEATABLE_READ &&
// !THDVAR(thd, transaction_unsafe))
// activeReadBuf->update(QMY_READ_ONLY, &releaseRowNeeded, QMY_REPEATABLE_READ);
// else
activeReadBuf->update(intent, &releaseRowNeeded, cmtLvl);
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
void ha_ibmdb2i::prepWriteBuffer(ha_rows rowsToWrite) int ha_ibmdb2i::prepWriteBuffer(ha_rows rowsToWrite, const db2i_file* file)
{ {
DBUG_ENTER("ha_ibmdb2i::prepWriteBuffer"); DBUG_ENTER("ha_ibmdb2i::prepWriteBuffer");
DBUG_ASSERT(accessIntent == QMY_UPDATABLE && rowsToWrite > 0); DBUG_ASSERT(accessIntent == QMY_UPDATABLE && rowsToWrite > 0);
const db2i_file::RowFormat* format;
int rc = file->obtainRowFormat(activeHandle,
QMY_UPDATABLE,
getCommitLevel(ha_thd()),
&format);
if (unlikely(rc)) DBUG_RETURN(rc);
rowsToWrite = min(rowsToWrite, DEFAULT_MAX_ROWS_TO_BUFFER); rowsToWrite = min(rowsToWrite, DEFAULT_MAX_ROWS_TO_BUFFER);
uint bufSize = min((activeFormat->writeRowLen * rowsToWrite), THDVAR(ha_thd(), max_write_buffer_size)); uint bufSize = min((format->writeRowLen * rowsToWrite), THDVAR(ha_thd(), max_write_buffer_size));
multiRowWriteBuf.allocBuf(activeFormat->writeRowLen, bufSize); multiRowWriteBuf.allocBuf(format->writeRowLen, format->writeRowNullOffset, bufSize);
activeWriteBuf = &multiRowWriteBuf; activeWriteBuf = &multiRowWriteBuf;
if (!blobWriteBuffers && db2Table->hasBlobs()) if (!blobWriteBuffers && db2Table->hasBlobs())
{ {
blobWriteBuffers = new ValidatedPointer<char>[db2Table->getBlobCount()]; blobWriteBuffers = new ValidatedPointer<char>[db2Table->getBlobCount()];
} }
DBUG_VOID_RETURN; DBUG_RETURN(rc);
} }
...@@ -2621,7 +2737,7 @@ int ha_ibmdb2i::flushWrite(FILE_HANDLE fileHandle, uchar* buf ) ...@@ -2621,7 +2737,7 @@ int ha_ibmdb2i::flushWrite(FILE_HANDLE fileHandle, uchar* buf )
readAllColumns = true; readAllColumns = true;
mungeDB2row(buf, mungeDB2row(buf,
badRow, badRow,
badRow + activeFormat->writeRowNullOffset, badRow + activeWriteBuf->getRowNullOffset(),
true); true);
readAllColumns = savedReadAllColumns; readAllColumns = savedReadAllColumns;
...@@ -2770,7 +2886,7 @@ int ha_ibmdb2i::prepareReadBufferForLobs() ...@@ -2770,7 +2886,7 @@ int ha_ibmdb2i::prepareReadBufferForLobs()
activeReadBuf->setRowsToProcess((activeLobFields ? 1 : activeReadBuf->getRowCapacity())); activeReadBuf->setRowsToProcess((activeLobFields ? 1 : activeReadBuf->getRowCapacity()));
int rc = bridge()->objectOverride(activeHandle, int rc = bridge()->objectOverride(activeHandle,
activeReadBuf->ptr(), activeReadBuf->ptr(),
activeFormat->readRowLen); activeReadBuf->getRowLength());
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
...@@ -2898,7 +3014,10 @@ int32 ha_ibmdb2i::buildCreateIndexStatement(SqlStatementStream& sqlStream, ...@@ -2898,7 +3014,10 @@ int32 ha_ibmdb2i::buildCreateIndexStatement(SqlStatementStream& sqlStream,
Field* field = key.key_part[j].field; Field* field = key.key_part[j].field;
convertMySQLNameToDB2Name(field->field_name, colName, sizeof(colName)); convertMySQLNameToDB2Name(field->field_name, colName, sizeof(colName));
fieldDefinition.append(colName); fieldDefinition.append(colName);
rc = updateAssociatedSortSequence(field,&fileSortSequenceType,fileSortSequence,fileSortSequenceLibrary); rc = updateAssociatedSortSequence(field->charset(),
&fileSortSequenceType,
fileSortSequence,
fileSortSequenceLibrary);
if (rc) DBUG_RETURN (rc); if (rc) DBUG_RETURN (rc);
} }
fieldDefinition.append(STRING_WITH_LEN(" ) ")); fieldDefinition.append(STRING_WITH_LEN(" ) "));
...@@ -3098,7 +3217,7 @@ double ha_ibmdb2i::read_time(uint index, uint ranges, ha_rows rows) ...@@ -3098,7 +3217,7 @@ double ha_ibmdb2i::read_time(uint index, uint ranges, ha_rows rows)
DBUG_RETURN(cost); DBUG_RETURN(cost);
} }
int ha_ibmdb2i::useIndexFile(char intent, int idx) int ha_ibmdb2i::useIndexFile(int idx)
{ {
DBUG_ENTER("ha_ibmdb2i::useIndexFile"); DBUG_ENTER("ha_ibmdb2i::useIndexFile");
...@@ -3110,19 +3229,11 @@ int ha_ibmdb2i::useIndexFile(char intent, int idx) ...@@ -3110,19 +3229,11 @@ int ha_ibmdb2i::useIndexFile(char intent, int idx)
if (!indexHandles[idx]) if (!indexHandles[idx])
rc = db2Table->indexFile(idx)->allocateNewInstance(&indexHandles[idx], curConnection); rc = db2Table->indexFile(idx)->allocateNewInstance(&indexHandles[idx], curConnection);
if (rc == 0)
{
rc = db2Table->indexFile(idx)->useFile(indexHandles[idx],
intent,
getCommitLevel(),
&activeFormat);
if (rc == 0) if (rc == 0)
{ {
activeHandle = indexHandles[idx]; activeHandle = indexHandles[idx];
bumpInUseCounter(1); bumpInUseCounter(1);
} }
}
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
...@@ -3141,11 +3252,15 @@ static struct st_mysql_sys_var* ibmdb2i_system_variables[] = { ...@@ -3141,11 +3252,15 @@ static struct st_mysql_sys_var* ibmdb2i_system_variables[] = {
MYSQL_SYSVAR(max_read_buffer_size), MYSQL_SYSVAR(max_read_buffer_size),
MYSQL_SYSVAR(max_write_buffer_size), MYSQL_SYSVAR(max_write_buffer_size),
MYSQL_SYSVAR(async_enabled), MYSQL_SYSVAR(async_enabled),
MYSQL_SYSVAR(create_time_columns_as_TOD),
MYSQL_SYSVAR(assume_exclusive_use), MYSQL_SYSVAR(assume_exclusive_use),
MYSQL_SYSVAR(map_blob_to_varchar), MYSQL_SYSVAR(compat_opt_blob_cols),
MYSQL_SYSVAR(compat_opt_time_as_duration),
MYSQL_SYSVAR(compat_opt_allow_zero_date_vals),
MYSQL_SYSVAR(compat_opt_year_as_int),
MYSQL_SYSVAR(propagate_default_col_vals),
MYSQL_SYSVAR(create_index_option), MYSQL_SYSVAR(create_index_option),
MYSQL_SYSVAR(discovery_mode), // MYSQL_SYSVAR(discovery_mode),
MYSQL_SYSVAR(system_trace_level),
NULL NULL
}; };
...@@ -3160,7 +3275,7 @@ mysql_declare_plugin(ibmdb2i) ...@@ -3160,7 +3275,7 @@ mysql_declare_plugin(ibmdb2i)
"IBMDB2I", "IBMDB2I",
"The IBM development team in Rochester, Minnesota", "The IBM development team in Rochester, Minnesota",
"IBM DB2 for i Storage Engine", "IBM DB2 for i Storage Engine",
PLUGIN_LICENSE_PROPRIETARY, PLUGIN_LICENSE_GPL,
ibmdb2i_init_func, /* Plugin Init */ ibmdb2i_init_func, /* Plugin Init */
ibmdb2i_done_func, /* Plugin Deinit */ ibmdb2i_done_func, /* Plugin Deinit */
0x0100 /* 1.0 */, 0x0100 /* 1.0 */,
......
...@@ -65,7 +65,7 @@ OF SUCH DAMAGE. ...@@ -65,7 +65,7 @@ OF SUCH DAMAGE.
It is used to describe the underlying table definition, and it caches It is used to describe the underlying table definition, and it caches
table statistics. table statistics.
*/ */
typedef struct st_ibmdb2i_share { struct IBMDB2I_SHARE {
char *table_name; char *table_name;
uint table_name_length,use_count; uint table_name_length,use_count;
pthread_mutex_t mutex; pthread_mutex_t mutex;
...@@ -110,7 +110,7 @@ typedef struct st_ibmdb2i_share { ...@@ -110,7 +110,7 @@ typedef struct st_ibmdb2i_share {
ulong data_file_length; ulong data_file_length;
} cachedStats; } cachedStats;
} IBMDB2I_SHARE; };
class ha_ibmdb2i: public handler class ha_ibmdb2i: public handler
{ {
...@@ -143,16 +143,15 @@ class ha_ibmdb2i: public handler ...@@ -143,16 +143,15 @@ class ha_ibmdb2i: public handler
// Array of file handles belonging to the underlying LFs // Array of file handles belonging to the underlying LFs
FILE_HANDLE* indexHandles; FILE_HANDLE* indexHandles;
// Pointer to a definition of the layout of the row buffer for the file
// described by activeHandle
const db2i_file::RowFormat* activeFormat;
// Flag to indicate whether a call needs to be made to unlock a row when // 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 // 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, // 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. // DB2 needs to know to unlock the last row read.
bool releaseRowNeeded; 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; IORowBuffer keyBuf;
uint32 keyLen; uint32 keyLen;
...@@ -190,6 +189,7 @@ class ha_ibmdb2i: public handler ...@@ -190,6 +189,7 @@ class ha_ibmdb2i: public handler
// The access intent indicated by the last external_locks() call. // The access intent indicated by the last external_locks() call.
// May be either QMY_READ or QMY_UPDATABLE // May be either QMY_READ or QMY_UPDATABLE
char accessIntent; char accessIntent;
char readAccessIntent;
ha_rows* indexReadSizeEstimates; ha_rows* indexReadSizeEstimates;
...@@ -361,6 +361,20 @@ class ha_ibmdb2i: public handler ...@@ -361,6 +361,20 @@ class ha_ibmdb2i: public handler
AS_VARCHAR 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); IBMDB2I_SHARE *get_share(const char *table_name, TABLE *table);
int free_share(IBMDB2I_SHARE *share); int free_share(IBMDB2I_SHARE *share);
int32 mungeDB2row(uchar* record, const char* dataPtr, const char* nullMapPtr, bool skipLOBs); int32 mungeDB2row(uchar* record, const char* dataPtr, const char* nullMapPtr, bool skipLOBs);
...@@ -396,10 +410,9 @@ class ha_ibmdb2i: public handler ...@@ -396,10 +410,9 @@ class ha_ibmdb2i: public handler
} }
int useDataFile(char intent) int useDataFile()
{ {
DBUG_ENTER("ha_ibmdb2i::useDataFile"); DBUG_ENTER("ha_ibmdb2i::useDataFile");
DBUG_PRINT("ha_ibmdb2i::useDataFile", ("Intent: %d", intent));
int rc = 0; int rc = 0;
if (!dataHandle) if (!dataHandle)
...@@ -409,20 +422,11 @@ class ha_ibmdb2i: public handler ...@@ -409,20 +422,11 @@ class ha_ibmdb2i: public handler
DBUG_ASSERT(activeHandle == 0); DBUG_ASSERT(activeHandle == 0);
if (likely(rc == 0))
{
rc = db2Table->dataFile()->useFile(dataHandle,
intent,
getCommitLevel(),
&activeFormat);
if (likely(rc == 0)) if (likely(rc == 0))
{ {
activeHandle = dataHandle; activeHandle = dataHandle;
bumpInUseCounter(1); bumpInUseCounter(1);
} }
}
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
...@@ -448,7 +452,7 @@ class ha_ibmdb2i: public handler ...@@ -448,7 +452,7 @@ class ha_ibmdb2i: public handler
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
int useIndexFile(char intent, int idx); int useIndexFile(int idx);
void releaseIndexFile(int idx) void releaseIndexFile(int idx)
{ {
...@@ -526,7 +530,10 @@ class ha_ibmdb2i: public handler ...@@ -526,7 +530,10 @@ class ha_ibmdb2i: public handler
int getFieldTypeMapping(Field* field, int getFieldTypeMapping(Field* field,
String& mapping, String& mapping,
enum_TimeFormat timeFormate, enum_TimeFormat timeFormate,
enum_BlobMapping blobMapping); enum_BlobMapping blobMapping,
enum_ZeroDate zeroDateHandling,
bool propagateDefaults,
enum_YearFormat yearFormat);
int getKeyFromName(const char* name, size_t len); int getKeyFromName(const char* name, size_t len);
...@@ -564,6 +571,9 @@ class ha_ibmdb2i: public handler ...@@ -564,6 +571,9 @@ class ha_ibmdb2i: public handler
{ {
DBUG_ASSERT(activeReadBuf->rowCount() == 1); DBUG_ASSERT(activeReadBuf->rowCount() == 1);
row = activeReadBuf->readNextRow(orientation, currentRRN); row = activeReadBuf->readNextRow(orientation, currentRRN);
if (unlikely(!row))
rc = activeReadBuf->lastrc();
} }
} }
} }
...@@ -571,7 +581,7 @@ class ha_ibmdb2i: public handler ...@@ -571,7 +581,7 @@ class ha_ibmdb2i: public handler
if (likely(rc == 0)) if (likely(rc == 0))
{ {
rrnAssocHandle = activeHandle; rrnAssocHandle = activeHandle;
rc = mungeDB2row(destination, row, row+activeFormat->readRowNullOffset, false); rc = mungeDB2row(destination, row, row+activeReadBuf->getRowNullOffset(), false);
} }
return rc; return rc;
} }
...@@ -631,7 +641,7 @@ class ha_ibmdb2i: public handler ...@@ -631,7 +641,7 @@ class ha_ibmdb2i: public handler
} }
} }
int rc = file->useFile(handle, intent, getCommitLevel(), &activeFormat); int rc = file->obtainRowFormat(handle, intent, getCommitLevel(), &activeFormat);
if (likely(rc == 0)) if (likely(rc == 0))
{ {
activeHandle = handle; activeHandle = handle;
...@@ -641,12 +651,25 @@ class ha_ibmdb2i: public handler ...@@ -641,12 +651,25 @@ class ha_ibmdb2i: public handler
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
int prepReadBuffer(ha_rows rowsToRead); const db2i_file* getFileForActiveHandle() const
void prepWriteBuffer(ha_rows rowsToWrite); {
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() void invalidateCachedStats()
{ {
share->cachedStats.invalidate(rowCount | deletedRowCount | objLength | meanRowLen | ioCount); share->cachedStats.invalidate(rowCount | deletedRowCount | objLength |
meanRowLen | ioCount);
} }
void warnIfInvalidData() void warnIfInvalidData()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment