diff options
Diffstat (limited to 'storage')
31 files changed, 857 insertions, 293 deletions
diff --git a/storage/federated/ha_federated.cc b/storage/federated/ha_federated.cc index 2fdf1bc7900..8deb8693960 100644 --- a/storage/federated/ha_federated.cc +++ b/storage/federated/ha_federated.cc @@ -2828,34 +2828,32 @@ int ha_federated::info(uint flag) if (!(row= mysql_fetch_row(result))) goto error; - if (flag & (HA_STATUS_VARIABLE | HA_STATUS_CONST)) - { - /* - deleted is set in ha_federated::info - */ - /* - need to figure out what this means as far as federated is concerned, - since we don't have a "file" + /* + deleted is set in ha_federated::info + */ + /* + need to figure out what this means as far as federated is concerned, + since we don't have a "file" - data_file_length = ? - index_file_length = ? - delete_length = ? - */ - if (row[4] != NULL) - stats.records= (ha_rows) my_strtoll10(row[4], (char**) 0, + data_file_length = ? + index_file_length = ? + delete_length = ? + */ + if (row[4] != NULL) + stats.records= (ha_rows) my_strtoll10(row[4], (char**) 0, &error); - if (row[5] != NULL) - stats.mean_rec_length= (ulong) my_strtoll10(row[5], (char**) 0, &error); + if (row[5] != NULL) + stats.mean_rec_length= (ulong) my_strtoll10(row[5], (char**) 0, &error); - stats.data_file_length= stats.records * stats.mean_rec_length; + stats.data_file_length= stats.records * stats.mean_rec_length; - if (row[12] != NULL) - stats.update_time= (ulong) my_strtoll10(row[12], (char**) 0, + if (row[12] != NULL) + stats.update_time= (ulong) my_strtoll10(row[12], (char**) 0, &error); - if (row[13] != NULL) - stats.check_time= (ulong) my_strtoll10(row[13], (char**) 0, + if (row[13] != NULL) + stats.check_time= (ulong) my_strtoll10(row[13], (char**) 0, &error); - } + /* size of IO operations (This is based on a good guess, no high science involved) diff --git a/storage/heap/ha_heap.cc b/storage/heap/ha_heap.cc index 19863d83874..fb7c13e4e41 100644 --- a/storage/heap/ha_heap.cc +++ b/storage/heap/ha_heap.cc @@ -419,6 +419,14 @@ int ha_heap::delete_all_rows() return 0; } + +int ha_heap::reset_auto_increment(ulonglong value) +{ + file->s->auto_increment= value; + return 0; +} + + int ha_heap::external_lock(THD *thd, int lock_type) { return 0; // No external locking diff --git a/storage/heap/ha_heap.h b/storage/heap/ha_heap.h index 5c5ad43658e..22722129f4c 100644 --- a/storage/heap/ha_heap.h +++ b/storage/heap/ha_heap.h @@ -98,6 +98,7 @@ public: int reset(); int external_lock(THD *thd, int lock_type); int delete_all_rows(void); + int reset_auto_increment(ulonglong value); int disable_indexes(uint mode); int enable_indexes(uint mode); int indexes_are_disabled(void); diff --git a/storage/ibmdb2i/db2i_charsetSupport.cc b/storage/ibmdb2i/db2i_charsetSupport.cc index 1479776d71a..83bf1b9448b 100644 --- a/storage/ibmdb2i/db2i_charsetSupport.cc +++ b/storage/ibmdb2i/db2i_charsetSupport.cc @@ -245,11 +245,16 @@ static int32 getNewTextDesc(const int32 inType, else if ((inType == Qlg_TypeAS400CCSID) && (outType == Qlg_TypeAix41)) { // Override non-standard charsets - if (unlikely(strcmp("1148", in) == 0)) + 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]; @@ -379,6 +384,11 @@ static int32 convertTextDesc(const int32 inType, const int32 outType, const char 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) { @@ -583,6 +593,11 @@ int32 getAssociatedCCSID(const uint16 inCcsid, const int inEncodingScheme, uint1 *outCcsid = 1148; DBUG_RETURN(0); } + else if ((inCcsid == 1250) && (inEncodingScheme == 0x1100)) + { + *outCcsid = 1153; + DBUG_RETURN(0); + } if (!ptrInited) { diff --git a/storage/ibmdb2i/db2i_collationSupport.cc b/storage/ibmdb2i/db2i_collationSupport.cc index a41f211a689..65a17fd2452 100644 --- a/storage/ibmdb2i/db2i_collationSupport.cc +++ b/storage/ibmdb2i/db2i_collationSupport.cc @@ -44,7 +44,7 @@ OF SUCH DAMAGE. between corresponding array slots but is incomplete without case-sensitivity markers dynamically added to the mySqlSortSequence names. */ -#define MAX_COLLATION 89 +#define MAX_COLLATION 87 static const char* mySQLCollation[MAX_COLLATION] = { {"ascii_general"}, @@ -52,7 +52,6 @@ static const char* mySQLCollation[MAX_COLLATION] = {"big5_chinese"}, {"big5"}, {"cp1250_croatian"}, - {"cp1250_czech"}, {"cp1250_general"}, {"cp1250_polish"}, {"cp1250"}, @@ -84,7 +83,6 @@ static const char* mySQLCollation[MAX_COLLATION] = {"latin1_swedish"}, {"latin1"}, {"latin2_croatian"}, - {"latin2_czech"}, {"latin2_general"}, {"latin2_hungarian"}, {"latin2"}, @@ -146,7 +144,6 @@ static const char* mySqlSortSequence[MAX_COLLATION] = {"QACHT04B0"}, {"QBCHT04B0"}, {"QALA20481"}, - {"QBLA20481"}, {"QCLA20481"}, {"QDLA20481"}, {"QELA20481"}, @@ -178,7 +175,6 @@ static const char* mySqlSortSequence[MAX_COLLATION] = {"QELA1047C"}, {"QFLA1047C"}, {"QCLA20366"}, - {"QDLA20366"}, {"QELA20366"}, {"QFLA20366"}, {"QGLA20366"}, @@ -190,8 +186,8 @@ static const char* mySqlSortSequence[MAX_COLLATION] = {"QDJPN04B0"}, {"QATHA0346"}, {"QBTHA0346"}, - {"ACS"}, - {"ADA"}, + {"ACS_CZ"}, + {"ADA_DK"}, {"AEO"}, {"AET"}, {"QAUCS04B0"}, @@ -211,8 +207,8 @@ static const char* mySqlSortSequence[MAX_COLLATION] = {"*HEX"}, {"QEJPN04B0"}, {"QFJPN04B0"}, - {"ACS"}, - {"ADA"}, + {"ACS_CZ"}, + {"ADA_DK"}, {"AEO"}, {"AET"}, {"QAUCS04B0"}, diff --git a/storage/ibmdb2i/db2i_conversion.cc b/storage/ibmdb2i/db2i_conversion.cc index bdb8085d937..9a85eb01c9b 100644 --- a/storage/ibmdb2i/db2i_conversion.cc +++ b/storage/ibmdb2i/db2i_conversion.cc @@ -137,7 +137,9 @@ int ha_ibmdb2i::convertFieldChars(enum_conversionDirection direction, char* output, size_t ilen, size_t olen, - size_t* outDataLen) + size_t* outDataLen, + bool tacitErrors, + size_t* substChars) { DBUG_PRINT("ha_ibmdb2i::convertFieldChars",("Direction: %d; length = %d", direction, ilen)); @@ -157,26 +159,26 @@ int ha_ibmdb2i::convertFieldChars(enum_conversionDirection direction, size_t initOLen= olen; size_t substitutedChars = 0; int rc = iconv(conversion, (char**)&input, &ilen, &output, &olen, &substitutedChars ); + if (outDataLen) *outDataLen = initOLen - olen; + if (substChars) *substChars = substitutedChars; if (unlikely(rc < 0)) { int er = errno; if (er == EILSEQ) { - getErrTxt(DB2I_ERR_ILL_CHAR, table->field[fieldID]->field_name); + if (!tacitErrors) getErrTxt(DB2I_ERR_ILL_CHAR, table->field[fieldID]->field_name); return (DB2I_ERR_ILL_CHAR); } else { - getErrTxt(DB2I_ERR_ICONV,er); + if (!tacitErrors) getErrTxt(DB2I_ERR_ICONV,er); return (DB2I_ERR_ICONV); } } - if (unlikely(substitutedChars)) + if (unlikely(substitutedChars) && (!tacitErrors)) { warning(ha_thd(), DB2I_ERR_SUB_CHARS, table->field[fieldID]->field_name); } - - if (outDataLen) *outDataLen = initOLen - olen; return (0); } @@ -555,12 +557,12 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, return 1; if (fieldCharSet->mbmaxlen > 1) { - if (strncmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")) == 0 ) // UCS2 + if (memcmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")-1) == 0 ) // UCS2 { sprintf(stringBuildBuffer, "GRAPHIC(%d)", max(fieldLength / fieldCharSet->mbmaxlen, 1)); // Number of characters db2Ccsid = 13488; } - else if (strncmp(fieldCharSet->name, "utf8_", sizeof("utf8_")) == 0 && + else if (memcmp(fieldCharSet->name, "utf8_", sizeof("utf8_")-1) == 0 && strcmp(fieldCharSet->name, "utf8_general_ci") != 0) { sprintf(stringBuildBuffer, "CHAR(%d)", max(fieldLength, 1)); // Number of bytes @@ -584,12 +586,12 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, { if (fieldCharSet->mbmaxlen > 1) { - if (strncmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")) == 0 ) // UCS2 + if (memcmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")-1) == 0 ) // UCS2 { sprintf(stringBuildBuffer, "VARGRAPHIC(%d)", max(fieldLength / fieldCharSet->mbmaxlen, 1)); // Number of characters db2Ccsid = 13488; } - else if (strncmp(fieldCharSet->name, "utf8_", sizeof("utf8_")) == 0 && + else if (memcmp(fieldCharSet->name, "utf8_", sizeof("utf8_")-1) == 0 && strcmp(fieldCharSet->name, "utf8_general_ci") != 0) { sprintf(stringBuildBuffer, "VARCHAR(%d)", max(fieldLength, 1)); // Number of bytes @@ -611,12 +613,12 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, { if (fieldCharSet->mbmaxlen > 1) { - if (strncmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")) == 0 ) // UCS2 + if (memcmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")-1) == 0 ) // UCS2 { sprintf(stringBuildBuffer, "LONG VARGRAPHIC "); db2Ccsid = 13488; } - else if (strncmp(fieldCharSet->name, "utf8_", sizeof("utf8_")) == 0 && + else if (memcmp(fieldCharSet->name, "utf8_", sizeof("utf8_")-1) == 0 && strcmp(fieldCharSet->name, "utf8_general_ci") != 0) { sprintf(stringBuildBuffer, "LONG VARCHAR "); @@ -639,12 +641,12 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, if (fieldCharSet->mbmaxlen > 1) { - if (strncmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")) == 0 ) // UCS2 + if (memcmp(fieldCharSet->name, "ucs2_", sizeof("ucs2_")-1) == 0 ) // UCS2 { sprintf(stringBuildBuffer, "DBCLOB(%d)", max(fieldLength / fieldCharSet->mbmaxlen, 1)); // Number of characters db2Ccsid = 13488; } - else if (strncmp(fieldCharSet->name, "utf8_", sizeof("utf8_")) == 0 && + else if (memcmp(fieldCharSet->name, "utf8_", sizeof("utf8_")-1) == 0 && strcmp(fieldCharSet->name, "utf8_general_ci") != 0) { sprintf(stringBuildBuffer, "CLOB(%d)", max(fieldLength, 1)); // Number of bytes @@ -671,11 +673,15 @@ int ha_ibmdb2i::getFieldTypeMapping(Field* field, return rtnCode; } - // Check whether there is a character conversion available. - iconv_t temp; - int32 rc = getConversion(toDB2, fieldCharSet, db2Ccsid, temp); - if (unlikely(rc)) - return rc; + if (db2Ccsid != 1208 && + db2Ccsid != 13488) + { + // Check whether there is a character conversion available. + iconv_t temp; + int32 rc = getConversion(toDB2, fieldCharSet, db2Ccsid, temp); + if (unlikely(rc)) + return rc; + } sprintf(stringBuildBuffer, " CCSID %d ", db2Ccsid); mapping.append(stringBuildBuffer); @@ -1085,7 +1091,7 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char if (bytesToStore) memcpy(db2Buf, dataToStore, bytesToStore); if (bytesToPad) - wmemset((wchar_t*)(db2Buf + bytesToStore), 0x0020, bytesToPad/2); + memset16((db2Buf + bytesToStore), 0x0020, bytesToPad/2); } else { @@ -1108,7 +1114,7 @@ int32 ha_ibmdb2i::convertMySQLtoDB2(Field* field, const DB2Field& db2Field, char bytesToStore = db2BytesToStore; } if (db2BytesToStore < maxDb2BytesToStore) // If need to pad - wmemset((wchar_t*)(db2Buf + db2BytesToStore), 0x0020, (maxDb2BytesToStore - db2BytesToStore)/2); + memset16((db2Buf + db2BytesToStore), 0x0020, (maxDb2BytesToStore - db2BytesToStore)/2); } if (db2FieldType == QMY_VARGRAPHIC) diff --git a/storage/ibmdb2i/db2i_misc.h b/storage/ibmdb2i/db2i_misc.h index 9e20f01208b..f0b527aaad0 100644 --- a/storage/ibmdb2i/db2i_misc.h +++ b/storage/ibmdb2i/db2i_misc.h @@ -109,5 +109,21 @@ bool isOrdinaryIdentifier(const char* s) } 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 diff --git a/storage/ibmdb2i/db2i_myconv.h b/storage/ibmdb2i/db2i_myconv.h index a9e87474505..98032748148 100644 --- a/storage/ibmdb2i/db2i_myconv.h +++ b/storage/ibmdb2i/db2i_myconv.h @@ -220,6 +220,7 @@ INTERN size_t myconv_dmap(myconv_t cd, } else { *pOut=dmapS2S[*pIn]; if (*pOut == 0x00) { + errno=EILSEQ; /* 116 */ *outBytesLeft-=(*inBytesLeft-inLen); *inBytesLeft=inLen; *outBuf=pOut; diff --git a/storage/ibmdb2i/db2i_rir.cc b/storage/ibmdb2i/db2i_rir.cc index a80a181c9ac..091c4d98383 100644 --- a/storage/ibmdb2i/db2i_rir.cc +++ b/storage/ibmdb2i/db2i_rir.cc @@ -51,7 +51,6 @@ static inline int getKeyCntFromMap(key_part_map keypart_map) return (cnt); } - /** @brief Given a starting key and an ending key, estimate the number of rows that @@ -270,81 +269,163 @@ ha_rows ha_ibmdb2i::records_in_range(uint inx, DB2Field& db2Field = db2Table->db2Field(field->field_index); litDefPtr->DataType = db2Field.getType(); /* - Convert the literal to DB2 format. - */ - rc = convertMySQLtoDB2(field, - db2Field, - literalPtr, - (uchar*)minPtr+((curKey.key_part[partsInUse].null_bit)? 1 : 0)); - if (rc != 0) break; - litDefPtr->Offset = (uint32_t)(literalPtr - literalsPtr); - litDefPtr->Length = db2Field.getByteLengthInRecord(); - tempLen = litDefPtr->Length; - /* - Do additional conversion of a character or graphic value. - */ - CHARSET_INFO* fieldCharSet = field->charset(); + 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)) + (litDefPtr->DataType == QMY_CHAR || + litDefPtr->DataType == QMY_VARCHAR || + litDefPtr->DataType == QMY_GRAPHIC || + litDefPtr->DataType == QMY_VARGRAPHIC)) { - if (litDefPtr->DataType == QMY_VARCHAR || - litDefPtr->DataType == QMY_VARGRAPHIC) - tempPtr = literalPtr + sizeof(uint16); - else - tempPtr = literalPtr; - /* The following code checks to determine if MySQL is passing a - partial key. DB2 will accept a partial field value, but only - in the last field position of the key composite (and only if - there is no ICU sort sequence on the index). */ - tempMinPtr = (char*)minPtr+((curKey.key_part[partsInUse].null_bit)? 1 : 0); - if (field->type() == MYSQL_TYPE_VARCHAR) - { - /* MySQL always stores key lengths as 2 bytes, little-endian. */ - tempLen = *(uint8*)tempMinPtr + ((*(uint8*)(tempMinPtr+1)) << 8); - tempMinPtr = (char*)((char*)tempMinPtr + 2); - } - else - tempLen = field->field_length; - - /* Determine if we are dealing with a partial key and if so, find the end of the partial key. */ - if (litDefPtr->DataType == QMY_CHAR || litDefPtr->DataType == QMY_VARCHAR ) - { /* Char or varchar. If UTF8, no conversion is done to DB2 graphic.) */ - endOfMinPtr = (char*)memchr(tempMinPtr,field->charset()->min_sort_char,tempLen); - if (endOfMinPtr) - endOfLiteralPtr = tempPtr + ((uint32_t)(endOfMinPtr - tempMinPtr)); - } - else - { - if (strncmp(fieldCharSet->csname, "utf8", sizeof("utf8")) == 0) - { /* The MySQL charset is UTF8 but we are converting to graphic on DB2. Divide number of UTF8 bytes - by 3 to get the number of characters, then multiple by 2 for double-byte graphic.*/ - endOfMinPtr = (char*)memchr(tempMinPtr,field->charset()->min_sort_char,tempLen); - if (endOfMinPtr) - endOfLiteralPtr = tempPtr + (((uint32_t)((endOfMinPtr - tempMinPtr)) / 3) * 2); - } - else - { /* The DB2 data type is graphic or vargraphic, and we are not converting from UTF8 to graphic. */ - endOfMinPtr = (char*)wmemchr((wchar_t*)tempMinPtr,field->charset()->min_sort_char,tempLen/2); - if (endOfMinPtr) - endOfLiteralPtr = tempPtr + (endOfMinPtr - tempMinPtr); + // 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; } - } - /* Enforce here that a partial is only allowed on the last field position - of the key composite */ - if (endOfLiteralPtr) - { - if ((partsInUse + 1) < minKeyCnt) - { - rc = HA_POS_ERROR; - break; - } - endByte = endOfLiteralPtr - tempPtr; - /* We're making an assumption that if MySQL gives us a partial key, - the length of the partial is the same for both the min_key and max_key. */ - } + } + 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 @@ -389,28 +470,168 @@ ha_rows ha_ibmdb2i::records_in_range(uint inx, /* Convert the literal to DB2 format */ - rc = convertMySQLtoDB2(field, - db2Field, - literalPtr, - (uchar*)maxPtr+((curKey.key_part[partsInUse].null_bit)? 1 : 0)); + 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(); - tempLen = litDefPtr->Length; - /* - Now convert a character or graphic value. - */ - if ((field->type() != MYSQL_TYPE_BIT) && - (litDefPtr->DataType == QMY_CHAR || litDefPtr->DataType == QMY_VARCHAR || - litDefPtr->DataType == QMY_GRAPHIC || litDefPtr->DataType == QMY_VARGRAPHIC)) - { - if (litDefPtr->DataType == QMY_VARCHAR || litDefPtr->DataType == QMY_VARGRAPHIC) - { - tempPtr = literalPtr + sizeof(uint16); - } - else - tempPtr = literalPtr; - } literalPtr = literalPtr + litDefPtr->Length; // Bump pointer for next literal } boundsPtr->HiBound.Position = literalCnt; diff --git a/storage/ibmdb2i/ha_ibmdb2i.cc b/storage/ibmdb2i/ha_ibmdb2i.cc index 5cf9568be67..0fc2d1e83dc 100644 --- a/storage/ibmdb2i/ha_ibmdb2i.cc +++ b/storage/ibmdb2i/ha_ibmdb2i.cc @@ -2230,34 +2230,19 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg, } } - bool primaryHasStringField = false; - + String fieldDefinition(128); + if (table_arg->s->primary_key != MAX_KEY && !isTemporary) { - KEY& curKey = table_arg->key_info[table_arg->s->primary_key]; - query.append(STRING_WITH_LEN(", PRIMARY KEY( ")); - for (int j = 0; j < curKey.key_parts; ++j) - { - if (j != 0) - { - query.append( STRING_WITH_LEN(" , ") ); - } - Field* field = curKey.key_part[j].field; - convertMySQLNameToDB2Name(field->field_name, colName, sizeof(colName)); - query.append(colName); - enum_field_types type = field->real_type(); - if (type == MYSQL_TYPE_VARCHAR || type == MYSQL_TYPE_BLOB || - type == MYSQL_TYPE_STRING) - { - rc = updateAssociatedSortSequence(field->charset(), - &fileSortSequenceType, - fileSortSequence, - fileSortSequenceLibrary); - if (rc) DBUG_RETURN (rc); - primaryHasStringField = true; - } - } - query.append(STRING_WITH_LEN(" ) ")); + query.append(STRING_WITH_LEN(", PRIMARY KEY ")); + rc = buildIndexFieldList(fieldDefinition, + table_arg->key_info[table_arg->s->primary_key], + true, + &fileSortSequenceType, + fileSortSequence, + fileSortSequenceLibrary); + if (rc) DBUG_RETURN(rc); + query.append(fieldDefinition); } rc = buildDB2ConstraintString(thd->lex, @@ -2283,6 +2268,19 @@ int ha_ibmdb2i::create(const char *name, TABLE *table_arg, SqlStatementStream sqlStream(query.length()); sqlStream.addStatement(query,fileSortSequence,fileSortSequenceLibrary); + if (table_arg->s->primary_key != MAX_KEY && + !isTemporary && + (THDVAR(thd, create_index_option)==1) && + (fileSortSequenceType != 'B') && + (fileSortSequenceType != ' ')) + { + rc = generateShadowIndex(sqlStream, + table_arg->key_info[table_arg->s->primary_key], + libName, + fileName, + fieldDefinition); + if (rc) DBUG_RETURN(rc); + } for (uint i = 0; i < table_arg->s->keys; ++i) { if (i != table_arg->s->primary_key || isTemporary) @@ -3012,61 +3010,126 @@ int32 ha_ibmdb2i::buildCreateIndexStatement(SqlStatementStream& sqlStream, } String fieldDefinition(128); - fieldDefinition.length(0); - fieldDefinition.append(STRING_WITH_LEN(" ( ")); + rc = buildIndexFieldList(fieldDefinition, + key, + isPrimary, + &fileSortSequenceType, + fileSortSequence, + fileSortSequenceLibrary); + + if (rc) DBUG_RETURN(rc); + + query.append(fieldDefinition); + + if ((THDVAR(ha_thd(), create_index_option)==1) && + (fileSortSequenceType != 'B') && + (fileSortSequenceType != ' ')) + { + rc = generateShadowIndex(sqlStream, + key, + db2LibName, + db2FileName, + fieldDefinition); + if (rc) DBUG_RETURN(rc); + } + + DBUG_PRINT("ha_ibmdb2i::buildCreateIndexStatement", ("Sent to DB2: %s",query.c_ptr_safe())); + sqlStream.addStatement(query,fileSortSequence,fileSortSequenceLibrary); + + DBUG_RETURN(0); +} + +/** + Generate the SQL syntax for the list of fields to be assigned to the + specified key. The corresponding sort sequence is also calculated. + + @param[out] appendHere The string to receive the generated SQL + @param key The key to evaluate + @param isPrimary True if this is being generated on behalf of the primary key + @param[out] fileSortSequenceType The type of the associated sort sequence + @param[out] fileSortSequence The name of the associated sort sequence + @param[out] fileSortSequenceLibrary The library of the associated sort sequence + + @return 0 if successful; error value otherwise +*/ +int32 ha_ibmdb2i::buildIndexFieldList(String& appendHere, + const KEY& key, + bool isPrimary, + char* fileSortSequenceType, + char* fileSortSequence, + char* fileSortSequenceLibrary) +{ + DBUG_ENTER("ha_ibmdb2i::buildIndexFieldList"); + appendHere.append(STRING_WITH_LEN(" ( ")); for (int j = 0; j < key.key_parts; ++j) { char colName[MAX_DB2_COLNAME_LENGTH+1]; if (j != 0) { - fieldDefinition.append(STRING_WITH_LEN(" , ")); + appendHere.append(STRING_WITH_LEN(" , ")); } - Field* field = key.key_part[j].field; - convertMySQLNameToDB2Name(field->field_name, colName, sizeof(colName)); - fieldDefinition.append(colName); + + KEY_PART_INFO& kpi = key.key_part[j]; + Field* field = kpi.field; + + convertMySQLNameToDB2Name(field->field_name, + colName, + sizeof(colName)); + appendHere.append(colName); + + int32 rc; rc = updateAssociatedSortSequence(field->charset(), - &fileSortSequenceType, + fileSortSequenceType, fileSortSequence, fileSortSequenceLibrary); if (rc) DBUG_RETURN (rc); } - fieldDefinition.append(STRING_WITH_LEN(" ) ")); - - query.append(fieldDefinition); - - if ((THDVAR(ha_thd(), create_index_option)==1) && - (fileSortSequenceType != 'B')) - { - String shadowQuery(256); - shadowQuery.length(0); - - shadowQuery.append(STRING_WITH_LEN("CREATE INDEX ")); - - shadowQuery.append(db2LibName); - shadowQuery.append('.'); - if (db2i_table::appendQualifiedIndexFileName(key.name, db2FileName, shadowQuery, db2i_table::ASCII_SQL, typeHex)) - { - getErrTxt(DB2I_ERR_INVALID_NAME,"index","*generated*"); - DBUG_RETURN(DB2I_ERR_INVALID_NAME ); - } - - shadowQuery.append(STRING_WITH_LEN(" ON ")); - - shadowQuery.append(db2LibName); - shadowQuery.append('.'); - shadowQuery.append(db2FileName); - shadowQuery.append(fieldDefinition); - DBUG_PRINT("ha_ibmdb2i::buildCreateIndexStatement", ("Sent to DB2: %s",shadowQuery.c_ptr_safe())); - sqlStream.addStatement(shadowQuery,"*HEX","QSYS"); - } - DBUG_PRINT("ha_ibmdb2i::buildCreateIndexStatement", ("Sent to DB2: %s",query.c_ptr_safe())); - sqlStream.addStatement(query,fileSortSequence,fileSortSequenceLibrary); - + appendHere.append(STRING_WITH_LEN(" ) ")); + DBUG_RETURN(0); } +/** + Generate an SQL statement that defines a *HEX sorted index to implement + the ibmdb2i_create_index. + + @param[out] stream The stream to append the generated statement to + @param key The key to evaluate + @param[out] libName The library containg the table + @param[out] fileName The DB2-compatible name of the table + @param[out] fieldDefinition The list of the fields in the index, in SQL syntax + + @return 0 if successful; error value otherwise +*/ +int32 ha_ibmdb2i::generateShadowIndex(SqlStatementStream& stream, + const KEY& key, + const char* libName, + const char* fileName, + const String& fieldDefinition) +{ + String shadowQuery(256); + shadowQuery.length(0); + shadowQuery.append(STRING_WITH_LEN("CREATE INDEX ")); + shadowQuery.append(libName); + shadowQuery.append('.'); + if (db2i_table::appendQualifiedIndexFileName(key.name, fileName, shadowQuery, db2i_table::ASCII_SQL, typeHex)) + { + getErrTxt(DB2I_ERR_INVALID_NAME,"index","*generated*"); + return DB2I_ERR_INVALID_NAME; + } + shadowQuery.append(STRING_WITH_LEN(" ON ")); + shadowQuery.append(libName); + shadowQuery.append('.'); + shadowQuery.append(fileName); + shadowQuery.append(fieldDefinition); + DBUG_PRINT("ha_ibmdb2i::generateShadowIndex", ("Sent to DB2: %s",shadowQuery.c_ptr_safe())); + stream.addStatement(shadowQuery,"*HEX","QSYS"); + return 0; +} + + void ha_ibmdb2i::doInitialRead(char orientation, uint32 rowsToBuffer, ILEMemHandle key, diff --git a/storage/ibmdb2i/ha_ibmdb2i.h b/storage/ibmdb2i/ha_ibmdb2i.h index 2a8d65825bf..b2a43232f2d 100644 --- a/storage/ibmdb2i/ha_ibmdb2i.h +++ b/storage/ibmdb2i/ha_ibmdb2i.h @@ -383,7 +383,15 @@ private: 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); + 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 @@ -522,6 +530,13 @@ private: 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); @@ -798,4 +813,10 @@ private: 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); }; diff --git a/storage/innobase/Makefile.am b/storage/innobase/Makefile.am index 7410bf7e591..3df9a6bf988 100644 --- a/storage/innobase/Makefile.am +++ b/storage/innobase/Makefile.am @@ -50,13 +50,14 @@ noinst_HEADERS= include/btr0btr.h include/btr0btr.ic \ include/eval0eval.h include/eval0eval.ic \ include/eval0proc.h include/eval0proc.ic \ include/fil0fil.h include/fsp0fsp.h \ - include/fsp0fsp.ic include/fut0fut.h \ + include/fsp0fsp.ic include/fsp0types.h \ + include/fut0fut.h \ include/fut0fut.ic include/fut0lst.h \ include/fut0lst.ic include/ha0ha.h \ include/ha0ha.ic include/hash0hash.h \ include/hash0hash.ic include/ibuf0ibuf.h \ include/ibuf0ibuf.ic include/ibuf0types.h \ - include/lock0iter.h \ + include/lock0iter.h \ include/lock0lock.h include/lock0lock.ic \ include/lock0priv.h include/lock0priv.ic \ include/lock0types.h include/log0log.h \ diff --git a/storage/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c index 54acdf73db6..d2a2e4d2157 100644 --- a/storage/innobase/btr/btr0cur.c +++ b/storage/innobase/btr/btr0cur.c @@ -23,6 +23,7 @@ Created 10/16/1994 Heikki Tuuri #include "btr0cur.ic" #endif +#include "mtr0log.h" #include "page0page.h" #include "rem0rec.h" #include "rem0cmp.h" diff --git a/storage/innobase/fil/fil0fil.c b/storage/innobase/fil/fil0fil.c index c63d67cae60..42e5166c9e4 100644 --- a/storage/innobase/fil/fil0fil.c +++ b/storage/innobase/fil/fil0fil.c @@ -2745,7 +2745,7 @@ fil_open_single_table_tablespace( "InnoDB: and MySQL removed the .ibd file for this.\n" "InnoDB: Please refer to\n" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" - "innodb-troubleshooting.html\n" + "innodb-troubleshooting-datadict.html\n" "InnoDB: for how to resolve the issue.\n", stderr); mem_free(filepath); @@ -2786,7 +2786,7 @@ fil_open_single_table_tablespace( " IMPORT TABLESPACE?\n" "InnoDB: Please refer to\n" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" - "innodb-troubleshooting.html\n" + "innodb-troubleshooting-datadict.html\n" "InnoDB: for how to resolve the issue.\n", (ulong) space_id, (ulong) id); @@ -3477,7 +3477,7 @@ fil_space_for_table_exists_in_mem( error_exit: fputs("InnoDB: Please refer to\n" "InnoDB: http://dev.mysql.com/doc/refman/5.1/en/" - "innodb-troubleshooting.html\n" + "innodb-troubleshooting-datadict.html\n" "InnoDB: for how to resolve the issue.\n", stderr); mem_free(path); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index f06b42cc654..0c937eee3c8 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -166,6 +166,20 @@ static handler *innobase_create_handler(handlerton *hton, static const char innobase_hton_name[]= "InnoDB"; +/** @brief Initialize the default value of innodb_commit_concurrency. + +Once InnoDB is running, the innodb_commit_concurrency must not change +from zero to nonzero. (Bug #42101) + +The initial default value is 0, and without this extra initialization, +SET GLOBAL innodb_commit_concurrency=DEFAULT would set the parameter +to 0, even if it was initially set to nonzero at the command line +or configuration file. */ +static +void +innobase_commit_concurrency_init_default(void); +/*==========================================*/ + /***************************************************************** Check for a valid value of innobase_commit_concurrency. */ static @@ -1870,6 +1884,8 @@ innobase_init( (char*)"latin1_swedish_ci")); memcpy(srv_latin1_ordering, my_charset_latin1.sort_order, 256); + innobase_commit_concurrency_init_default(); + /* Since we in this module access directly the fields of a trx struct, and due to different headers and flags it might happen that mutex_t has a different size in this module and in InnoDB @@ -8256,6 +8272,97 @@ innobase_set_cursor_view( } +/*********************************************************************** +Check whether any of the given columns is being renamed in the table. */ +static +bool +column_is_being_renamed( +/*====================*/ + /* out: true if any of col_names is + being renamed in table */ + TABLE* table, /* in: MySQL table */ + uint n_cols, /* in: number of columns */ + const char** col_names) /* in: names of the columns */ +{ + uint j; + uint k; + Field* field; + const char* col_name; + + for (j = 0; j < n_cols; j++) { + col_name = col_names[j]; + for (k = 0; k < table->s->fields; k++) { + field = table->field[k]; + if ((field->flags & FIELD_IS_RENAMED) + && innobase_strcasecmp(field->field_name, + col_name) == 0) { + return(true); + } + } + } + + return(false); +} + +/*********************************************************************** +Check whether a column in table "table" is being renamed and if this column +is part of a foreign key, either part of another table, referencing this +table or part of this table, referencing another table. */ +static +bool +foreign_key_column_is_being_renamed( +/*================================*/ + /* out: true if a column that + participates in a foreign key definition + is being renamed */ + row_prebuilt_t* prebuilt, /* in: InnoDB prebuilt struct */ + TABLE* table) /* in: MySQL table */ +{ + dict_foreign_t* foreign; + + /* check whether there are foreign keys at all */ + if (UT_LIST_GET_LEN(prebuilt->table->foreign_list) == 0 + && UT_LIST_GET_LEN(prebuilt->table->referenced_list) == 0) { + /* no foreign keys involved with prebuilt->table */ + + return(false); + } + + row_mysql_lock_data_dictionary(prebuilt->trx); + + /* Check whether any column in the foreign key constraints which refer + to this table is being renamed. */ + for (foreign = UT_LIST_GET_FIRST(prebuilt->table->referenced_list); + foreign != NULL; + foreign = UT_LIST_GET_NEXT(referenced_list, foreign)) { + + if (column_is_being_renamed(table, foreign->n_fields, + foreign->referenced_col_names)) { + + row_mysql_unlock_data_dictionary(prebuilt->trx); + return(true); + } + } + + /* Check whether any column in the foreign key constraints in the + table is being renamed. */ + for (foreign = UT_LIST_GET_FIRST(prebuilt->table->foreign_list); + foreign != NULL; + foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) { + + if (column_is_being_renamed(table, foreign->n_fields, + foreign->foreign_col_names)) { + + row_mysql_unlock_data_dictionary(prebuilt->trx); + return(true); + } + } + + row_mysql_unlock_data_dictionary(prebuilt->trx); + + return(false); +} + bool ha_innobase::check_if_incompatible_data( HA_CREATE_INFO* info, uint table_changes) @@ -8272,6 +8379,13 @@ bool ha_innobase::check_if_incompatible_data( return COMPATIBLE_DATA_NO; } + /* Check if a column participating in a foreign key is being renamed. + There is no mechanism for updating InnoDB foreign key definitions. */ + if (foreign_key_column_is_being_renamed(prebuilt, table)) { + + return COMPATIBLE_DATA_NO; + } + /* Check that row format didn't change */ if ((info->used_fields & HA_CREATE_USED_ROW_FORMAT) && get_row_type() != info->row_type) { @@ -8559,3 +8673,21 @@ mysql_declare_plugin(innobase) NULL /* reserved */ } mysql_declare_plugin_end; + +/** @brief Initialize the default value of innodb_commit_concurrency. + +Once InnoDB is running, the innodb_commit_concurrency must not change +from zero to nonzero. (Bug #42101) + +The initial default value is 0, and without this extra initialization, +SET GLOBAL innodb_commit_concurrency=DEFAULT would set the parameter +to 0, even if it was initially set to nonzero at the command line +or configuration file. */ +static +void +innobase_commit_concurrency_init_default(void) +/*==========================================*/ +{ + MYSQL_SYSVAR_NAME(commit_concurrency).def_val + = innobase_commit_concurrency; +} diff --git a/storage/innobase/include/fsp0fsp.h b/storage/innobase/include/fsp0fsp.h index 82e95a2e920..17bfbeec2c1 100644 --- a/storage/innobase/include/fsp0fsp.h +++ b/storage/innobase/include/fsp0fsp.h @@ -15,29 +15,7 @@ Created 12/18/1995 Heikki Tuuri #include "fut0lst.h" #include "ut0byte.h" #include "page0types.h" - -/* If records are inserted in order, there are the following -flags to tell this (their type is made byte for the compiler -to warn if direction and hint parameters are switched in -fseg_alloc_free_page): */ -#define FSP_UP ((byte)111) /* alphabetically upwards */ -#define FSP_DOWN ((byte)112) /* alphabetically downwards */ -#define FSP_NO_DIR ((byte)113) /* no order */ - -/* File space extent size in pages */ -#define FSP_EXTENT_SIZE 64 - -/* On a page of any file segment, data may be put starting from this offset: */ -#define FSEG_PAGE_DATA FIL_PAGE_DATA - -/* File segment header which points to the inode describing the file segment */ -typedef byte fseg_header_t; - -#define FSEG_HDR_SPACE 0 /* space id of the inode */ -#define FSEG_HDR_PAGE_NO 4 /* page number of the inode */ -#define FSEG_HDR_OFFSET 8 /* byte offset of the inode */ - -#define FSEG_HEADER_SIZE 10 +#include "fsp0types.h" /************************************************************************** Initializes the file space system. */ @@ -350,40 +328,6 @@ fseg_print( fseg_header_t* header, /* in: segment header */ mtr_t* mtr); /* in: mtr */ -/* Flags for fsp_reserve_free_extents */ -#define FSP_NORMAL 1000000 -#define FSP_UNDO 2000000 -#define FSP_CLEANING 3000000 - -/* Number of pages described in a single descriptor page: currently each page -description takes less than 1 byte; a descriptor page is repeated every -this many file pages */ -#define XDES_DESCRIBED_PER_PAGE UNIV_PAGE_SIZE - -/* The space low address page map */ -/*--------------------------------------*/ - /* The following two pages are repeated - every XDES_DESCRIBED_PER_PAGE pages in - every tablespace. */ -#define FSP_XDES_OFFSET 0 /* extent descriptor */ -#define FSP_IBUF_BITMAP_OFFSET 1 /* insert buffer bitmap */ - /* The ibuf bitmap pages are the ones whose - page number is the number above plus a - multiple of XDES_DESCRIBED_PER_PAGE */ - -#define FSP_FIRST_INODE_PAGE_NO 2 /* in every tablespace */ - /* The following pages exist - in the system tablespace (space 0). */ -#define FSP_IBUF_HEADER_PAGE_NO 3 /* in tablespace 0 */ -#define FSP_IBUF_TREE_ROOT_PAGE_NO 4 /* in tablespace 0 */ - /* The ibuf tree root page number in - tablespace 0; its fseg inode is on the page - number FSP_FIRST_INODE_PAGE_NO */ -#define FSP_TRX_SYS_PAGE_NO 5 /* in tablespace 0 */ -#define FSP_FIRST_RSEG_PAGE_NO 6 /* in tablespace 0 */ -#define FSP_DICT_HDR_PAGE_NO 7 /* in tablespace 0 */ -/*--------------------------------------*/ - #ifndef UNIV_NONINL #include "fsp0fsp.ic" #endif diff --git a/storage/innobase/include/fsp0types.h b/storage/innobase/include/fsp0types.h new file mode 100644 index 00000000000..6756d9d285c --- /dev/null +++ b/storage/innobase/include/fsp0types.h @@ -0,0 +1,89 @@ +/***************************************************************************** + +Copyright (c) 1995, 2009, Innobase Oy. 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., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA + +*****************************************************************************/ + +/****************************************************** +File space management types + +Created May 26, 2009 Vasil Dimov +*******************************************************/ + +#ifndef fsp0types_h +#define fsp0types_h + +#include "univ.i" + +#include "fil0fil.h" /* for FIL_PAGE_DATA */ + +/* If records are inserted in order, there are the following +flags to tell this (their type is made byte for the compiler +to warn if direction and hint parameters are switched in +fseg_alloc_free_page): */ +#define FSP_UP ((byte)111) /* alphabetically upwards */ +#define FSP_DOWN ((byte)112) /* alphabetically downwards */ +#define FSP_NO_DIR ((byte)113) /* no order */ + +/* File space extent size in pages */ +#define FSP_EXTENT_SIZE 64 + +/* On a page of any file segment, data may be put starting from this offset: */ +#define FSEG_PAGE_DATA FIL_PAGE_DATA + +/* File segment header which points to the inode describing the file segment */ +typedef byte fseg_header_t; + +#define FSEG_HDR_SPACE 0 /* space id of the inode */ +#define FSEG_HDR_PAGE_NO 4 /* page number of the inode */ +#define FSEG_HDR_OFFSET 8 /* byte offset of the inode */ + +#define FSEG_HEADER_SIZE 10 + +/* Flags for fsp_reserve_free_extents */ +#define FSP_NORMAL 1000000 +#define FSP_UNDO 2000000 +#define FSP_CLEANING 3000000 + +/* Number of pages described in a single descriptor page: currently each page +description takes less than 1 byte; a descriptor page is repeated every +this many file pages */ +#define XDES_DESCRIBED_PER_PAGE UNIV_PAGE_SIZE + +/* The space low address page map */ +/*--------------------------------------*/ + /* The following two pages are repeated + every XDES_DESCRIBED_PER_PAGE pages in + every tablespace. */ +#define FSP_XDES_OFFSET 0 /* extent descriptor */ +#define FSP_IBUF_BITMAP_OFFSET 1 /* insert buffer bitmap */ + /* The ibuf bitmap pages are the ones whose + page number is the number above plus a + multiple of XDES_DESCRIBED_PER_PAGE */ + +#define FSP_FIRST_INODE_PAGE_NO 2 /* in every tablespace */ + /* The following pages exist + in the system tablespace (space 0). */ +#define FSP_IBUF_HEADER_PAGE_NO 3 /* in tablespace 0 */ +#define FSP_IBUF_TREE_ROOT_PAGE_NO 4 /* in tablespace 0 */ + /* The ibuf tree root page number in + tablespace 0; its fseg inode is on the page + number FSP_FIRST_INODE_PAGE_NO */ +#define FSP_TRX_SYS_PAGE_NO 5 /* in tablespace 0 */ +#define FSP_FIRST_RSEG_PAGE_NO 6 /* in tablespace 0 */ +#define FSP_DICT_HDR_PAGE_NO 7 /* in tablespace 0 */ +/*--------------------------------------*/ + +#endif /* fsp0types_h */ diff --git a/storage/innobase/include/mtr0log.ic b/storage/innobase/include/mtr0log.ic index 5b1d1ed34d9..1626f1e77e5 100644 --- a/storage/innobase/include/mtr0log.ic +++ b/storage/innobase/include/mtr0log.ic @@ -9,6 +9,8 @@ Created 12/7/1995 Heikki Tuuri #include "mach0data.h" #include "ut0lst.h" #include "buf0buf.h" +#include "fsp0types.h" +#include "trx0sys.h" /************************************************************ Opens a buffer to mlog. It must be closed with mlog_close. */ @@ -174,6 +176,28 @@ mlog_write_initial_log_record_fast( space = buf_block_get_space(block); offset = buf_block_get_page_no(block); + /* check whether the page is in the doublewrite buffer; + the doublewrite buffer is located in pages + FSP_EXTENT_SIZE, ..., 3 * FSP_EXTENT_SIZE - 1 in the + system tablespace */ + if (space == TRX_SYS_SPACE + && offset >= FSP_EXTENT_SIZE && offset < 3 * FSP_EXTENT_SIZE) { + if (trx_doublewrite_buf_is_being_created) { + /* Do nothing: we only come to this branch in an + InnoDB database creation. We do not redo log + anything for the doublewrite buffer pages. */ + return(log_ptr); + } else { + fprintf(stderr, + "Error: trying to redo log a record of type " + "%d on page %lu of space %lu in the " + "doublewrite buffer, continuing anyway.\n" + "Please post a bug report to " + "bugs.mysql.com.\n", + type, offset, space); + } + } + mach_write_to_1(log_ptr, type); log_ptr++; log_ptr += mach_write_compressed(log_ptr, space); diff --git a/storage/innobase/include/trx0rseg.ic b/storage/innobase/include/trx0rseg.ic index eb1893587a6..577cd0dee7b 100644 --- a/storage/innobase/include/trx0rseg.ic +++ b/storage/innobase/include/trx0rseg.ic @@ -7,6 +7,7 @@ Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "srv0srv.h" +#include "mtr0log.h" /********************************************************************** Gets a rollback segment header. */ diff --git a/storage/innobase/include/trx0sys.h b/storage/innobase/include/trx0sys.h index a8da5cd51a3..bad3c9d570c 100644 --- a/storage/innobase/include/trx0sys.h +++ b/storage/innobase/include/trx0sys.h @@ -13,15 +13,12 @@ Created 3/26/1996 Heikki Tuuri #include "trx0types.h" #include "mtr0mtr.h" -#include "mtr0log.h" #include "ut0byte.h" #include "mem0mem.h" #include "sync0sync.h" #include "ut0lst.h" #include "buf0buf.h" #include "fil0fil.h" -#include "fut0lst.h" -#include "fsp0fsp.h" #include "read0types.h" /* In a MySQL replication slave, in crash recovery we store the master log @@ -45,6 +42,8 @@ extern trx_sys_t* trx_sys; /* Doublewrite system */ extern trx_doublewrite_t* trx_doublewrite; +/* Set to TRUE when the doublewrite buffer is being created */ +extern ibool trx_doublewrite_buf_is_being_created; extern ibool trx_doublewrite_must_reset_space_ids; extern ibool trx_sys_multiple_tablespace_format; @@ -302,6 +301,7 @@ trx_sys_print_mysql_master_log_pos(void); /* Space id and page no where the trx system file copy resides */ #define TRX_SYS_SPACE 0 /* the SYSTEM tablespace */ +#include "fsp0fsp.h" #define TRX_SYS_PAGE_NO FSP_TRX_SYS_PAGE_NO /* The offset of the transaction system header on the page */ diff --git a/storage/innobase/include/trx0sys.ic b/storage/innobase/include/trx0sys.ic index 86b71df08d6..55bcc12a414 100644 --- a/storage/innobase/include/trx0sys.ic +++ b/storage/innobase/include/trx0sys.ic @@ -9,6 +9,7 @@ Created 3/26/1996 Heikki Tuuri #include "srv0srv.h" #include "trx0trx.h" #include "data0type.h" +#include "mtr0log.h" /* The typedef for rseg slot in the file copy */ typedef byte trx_sysf_rseg_t; diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 2d9ed4fc944..6ec466cf995 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -1529,9 +1529,14 @@ row_unlock_for_mysql( index = btr_pcur_get_btr_cur(clust_pcur)->index; } + if (UNIV_UNLIKELY(!(index->type & DICT_CLUSTERED))) { + /* This is not a clustered index record. We + do not know how to unlock the record. */ + goto no_unlock; + } + /* If the record has been modified by this transaction, do not unlock it. */ - ut_a(index->type & DICT_CLUSTERED); if (index->trx_id_offset) { rec_trx_id = trx_read_trx_id(rec @@ -1568,7 +1573,7 @@ row_unlock_for_mysql( prebuilt->select_lock_type); } } - +no_unlock: mtr_commit(&mtr); } diff --git a/storage/innobase/trx/trx0purge.c b/storage/innobase/trx/trx0purge.c index f0e85ef1604..f0f300d918e 100644 --- a/storage/innobase/trx/trx0purge.c +++ b/storage/innobase/trx/trx0purge.c @@ -14,6 +14,7 @@ Created 3/26/1996 Heikki Tuuri #include "fsp0fsp.h" #include "mach0data.h" +#include "mtr0log.h" #include "trx0rseg.h" #include "trx0trx.h" #include "trx0roll.h" diff --git a/storage/innobase/trx/trx0rec.c b/storage/innobase/trx/trx0rec.c index 50f8b011463..38ad53fcfb0 100644 --- a/storage/innobase/trx/trx0rec.c +++ b/storage/innobase/trx/trx0rec.c @@ -23,6 +23,7 @@ Created 3/26/1996 Heikki Tuuri #include "que0que.h" #include "trx0purge.h" #include "row0row.h" +#include "mtr0log.h" /*=========== UNDO LOG RECORD CREATION AND DECODING ====================*/ diff --git a/storage/innobase/trx/trx0sys.c b/storage/innobase/trx/trx0sys.c index 40348dd4199..19c5159e15f 100644 --- a/storage/innobase/trx/trx0sys.c +++ b/storage/innobase/trx/trx0sys.c @@ -13,7 +13,7 @@ Created 3/26/1996 Heikki Tuuri #endif #include "fsp0fsp.h" -#include "mtr0mtr.h" +#include "mtr0log.h" #include "trx0trx.h" #include "trx0rseg.h" #include "trx0undo.h" @@ -25,6 +25,7 @@ Created 3/26/1996 Heikki Tuuri /* The transaction system */ trx_sys_t* trx_sys = NULL; trx_doublewrite_t* trx_doublewrite = NULL; +ibool trx_doublewrite_buf_is_being_created = FALSE; /* The following is set to TRUE when we are upgrading from the old format data files to the new >= 4.1.x format multiple tablespaces format data files */ @@ -180,6 +181,7 @@ trx_sys_create_doublewrite_buf(void) start_again: mtr_start(&mtr); + trx_doublewrite_buf_is_being_created = TRUE; page = buf_page_get(TRX_SYS_SPACE, TRX_SYS_PAGE_NO, RW_X_LATCH, &mtr); #ifdef UNIV_SYNC_DEBUG @@ -196,6 +198,7 @@ start_again: trx_doublewrite_init(doublewrite); mtr_commit(&mtr); + trx_doublewrite_buf_is_being_created = FALSE; } else { fprintf(stderr, "InnoDB: Doublewrite buffer not found:" @@ -274,14 +277,8 @@ start_again: buf_page_dbg_add_level(new_page, SYNC_NO_ORDER_CHECK); #endif /* UNIV_SYNC_DEBUG */ - /* Make a dummy change to the page to ensure it will - be written to disk in a flush */ - - mlog_write_ulint(new_page + FIL_PAGE_DATA, - TRX_SYS_DOUBLEWRITE_MAGIC_N, - MLOG_4BYTES, &mtr); - if (i == FSP_EXTENT_SIZE / 2) { + ut_a(page_no == FSP_EXTENT_SIZE); mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_BLOCK1, page_no, MLOG_4BYTES, &mtr); @@ -291,6 +288,7 @@ start_again: page_no, MLOG_4BYTES, &mtr); } else if (i == FSP_EXTENT_SIZE / 2 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) { + ut_a(page_no == 2 * FSP_EXTENT_SIZE); mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_BLOCK2, page_no, MLOG_4BYTES, &mtr); diff --git a/storage/innobase/trx/trx0undo.c b/storage/innobase/trx/trx0undo.c index b31580d0ce0..deb6c85e6e3 100644 --- a/storage/innobase/trx/trx0undo.c +++ b/storage/innobase/trx/trx0undo.c @@ -14,6 +14,7 @@ Created 3/26/1996 Heikki Tuuri #include "fsp0fsp.h" #include "mach0data.h" +#include "mtr0log.h" #include "trx0rseg.h" #include "trx0trx.h" #include "srv0srv.h" diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index cf290e2018a..d0e5f5a5c88 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -95,7 +95,7 @@ static void mi_check_print_msg(MI_CHECK *param, const char* msg_type, if (!thd->vio_ok()) { - sql_print_error(msgbuf); + sql_print_error("%s", msgbuf); return; } @@ -1112,6 +1112,9 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool do_optimize) param.out_flag= 0; strmov(fixed_name,file->filename); + // Release latches since this can take a long time + ha_release_temporary_latches(thd); + // Don't lock tables if we have used LOCK TABLE if (!thd->locked_tables && mi_lock_database(file, table->s->tmp_table ? F_EXTRA_LCK : F_WRLCK)) @@ -1867,6 +1870,12 @@ int ha_myisam::delete_all_rows() return mi_delete_all_rows(file); } +int ha_myisam::reset_auto_increment(ulonglong value) +{ + file->s->state.auto_increment= value; + return 0; +} + int ha_myisam::delete_table(const char *name) { return mi_delete_table(name); diff --git a/storage/myisam/ha_myisam.h b/storage/myisam/ha_myisam.h index ca44ae9ad87..55a5eac92de 100644 --- a/storage/myisam/ha_myisam.h +++ b/storage/myisam/ha_myisam.h @@ -101,6 +101,7 @@ class ha_myisam: public handler int reset(void); int external_lock(THD *thd, int lock_type); int delete_all_rows(void); + int reset_auto_increment(ulonglong value); int disable_indexes(uint mode); int enable_indexes(uint mode); int indexes_are_disabled(void); diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 1e82983b97c..19510d0eae1 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -545,7 +545,8 @@ int ha_myisammrg::attach_children(void) if (myrg_attach_children(this->file, this->test_if_locked | current_thd->open_options, - myisammrg_attach_children_callback, this)) + myisammrg_attach_children_callback, this, + (my_bool *) &need_compat_check)) { DBUG_PRINT("error", ("my_errno %d", my_errno)); DBUG_RETURN(my_errno ? my_errno : -1); diff --git a/storage/myisammrg/myrg_create.c b/storage/myisammrg/myrg_create.c index df81b730bfd..eaed470daec 100644 --- a/storage/myisammrg/myrg_create.c +++ b/storage/myisammrg/myrg_create.c @@ -46,7 +46,7 @@ int myrg_create(const char *name, const char **table_names, fn_same(buff,name,4); *(end=strend(buff))='\n'; end[1]=0; - if (my_write(file,(char*) buff,(uint) (end-buff+1), + if (my_write(file,(uchar*) buff,(uint) (end-buff+1), MYF(MY_WME | MY_NABP))) goto err; } diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c index 14ba2853b22..01420f47a0c 100644 --- a/storage/myisammrg/myrg_open.c +++ b/storage/myisammrg/myrg_open.c @@ -365,11 +365,14 @@ MYRG_INFO *myrg_parent_open(const char *parent_name, The callback returns the MyISAM table handle of the child table. Check table definition match. - @param[in] m_info MERGE parent table structure - @param[in] handle_locking if contains HA_OPEN_FOR_REPAIR, warn about - incompatible child tables, but continue - @param[in] callback function to call for each child table - @param[in] callback_param data pointer to give to the callback + @param[in] m_info MERGE parent table structure + @param[in] handle_locking if contains HA_OPEN_FOR_REPAIR, warn about + incompatible child tables, but continue + @param[in] callback function to call for each child table + @param[in] callback_param data pointer to give to the callback + @param[in] need_compat_check pointer to ha_myisammrg::need_compat_check + (we need this one to decide if previously + allocated buffers can be reused). @return status @retval 0 OK @@ -382,7 +385,7 @@ MYRG_INFO *myrg_parent_open(const char *parent_name, int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, MI_INFO *(*callback)(void*), - void *callback_param) + void *callback_param, my_bool *need_compat_check) { ulonglong file_offset; MI_INFO *myisam; @@ -423,6 +426,11 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, m_info->reclength= myisam->s->base.reclength; min_keys= myisam->s->base.keys; key_parts= myisam->s->base.key_parts; + if (*need_compat_check && m_info->rec_per_key_part) + { + my_free((char *) m_info->rec_per_key_part, MYF(0)); + m_info->rec_per_key_part= NULL; + } if (!m_info->rec_per_key_part) { if(!(m_info->rec_per_key_part= (ulong*) |