/*############################################################################## Copyright (C) 2011 HPCC Systems. All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . ############################################################################## */ #include "platform.h" #if defined(_DEBUG) && defined(_WIN32) && !defined(USING_MPATROL) #undef new #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) #endif #include "jlib.hpp" #include "jstream.hpp" #include "jmisc.hpp" #include "defvalue.hpp" #include "jexcept.hpp" #include "deftype.ipp" #include #include #include #include "bcd.hpp" #include "eclrtl.hpp" #include "eclrtl_imp.hpp" #if defined(_DEBUG) && defined(_WIN32) && !defined(USING_MPATROL) #undef new #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) #endif //#define DATA_STRING_COMPATIBLE #define HASHFIELD(p) hashcode = hashc((unsigned char *) &p, sizeof(p), hashcode) static _ATOM asciiAtom; static _ATOM dataAtom; static _ATOM ebcdicAtom; static _ATOM utf8Atom; static _ATOM asciiCodepageAtom; static _ATOM ebcdicCodepageAtom; static _ATOM ascii2ebcdicAtom; static _ATOM ebcdic2asciiAtom; static _ATOM emptyAtom; static CriticalSection * typeCS; static TypeCache * globalTypeCache; static CBoolTypeInfo *btt = NULL; static CBlobTypeInfo *bltt = NULL; static CVoidTypeInfo *vtt = NULL; static CNullTypeInfo *ntt = NULL; static CRecordTypeInfo *rtt = NULL; static CAnyTypeInfo *anytt = NULL; static CPatternTypeInfo *patt = NULL; static CTokenTypeInfo *tokentt = NULL; static CFeatureTypeInfo *featurett = NULL; static CStringTypeToTypeMap * stt; static CStringTypeToTypeMap * datatt; static CStringTypeToTypeMap * vstt; static CStringTypeToTypeMap * qstt; static CUnicodeTypeToTypeMap * utt; static CUnicodeTypeToTypeMap * vutt; static CUnicodeTypeToTypeMap * u8tt; static CIntTypeInfo *itt[2][8]; static CSwapIntTypeInfo *sitt[2][8]; static TypeToTypeMap * pitt; static CRealTypeInfo *realtt[10]; static CBitfieldTypeInfo *bftt[64][8]; static CCharTypeInfo * ctt[2]; static TypeToTypeMap * setTT; static CEventTypeInfo *ett = NULL; //charssets and collation... static ICharsetInfo * dataCharset; static ICharsetInfo * asciiCharset; static ICharsetInfo * ebcdicCharset; static ICharsetInfo * utf8Charset; static ICollationInfo * asciiCollation; static ICollationInfo * ebcdicCollation; static ITranslationInfo * ascii2ebcdic; static ITranslationInfo * ebcdic2ascii; //--------------------------------------------------------------------------- MODULE_INIT(INIT_PRIORITY_DEFTYPE) { typeCS = new CriticalSection; asciiAtom = createLowerCaseAtom("ascii"); dataAtom = createLowerCaseAtom("data"); ebcdicAtom = createLowerCaseAtom("ebcdic"); utf8Atom = createLowerCaseAtom("utf-8"); asciiCodepageAtom = createLowerCaseAtom(ASCII_LIKE_CODEPAGE); ebcdicCodepageAtom = createLowerCaseAtom("IBM037"); ascii2ebcdicAtom = createAtom("ascii2ebcdic"); ebcdic2asciiAtom = createAtom("ebcdic2ascii"); emptyAtom = createAtom(""); globalTypeCache = new TypeCache; stt = new CStringTypeToTypeMap; datatt = new CStringTypeToTypeMap; vstt = new CStringTypeToTypeMap; qstt = new CStringTypeToTypeMap; utt = new CUnicodeTypeToTypeMap; vutt = new CUnicodeTypeToTypeMap; u8tt = new CUnicodeTypeToTypeMap; pitt = new TypeToTypeMap; setTT = new TypeToTypeMap; return 1; } MODULE_EXIT() { ClearTypeCache(); delete globalTypeCache; delete stt; delete datatt; delete vstt; delete qstt; delete utt; delete vutt; delete u8tt; delete pitt; delete setTT; delete typeCS; } //--------------------------------------------------------------------------- int cards[] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000 }; unsigned promotedIntSize[9] = { 0, 1, 2, 4, 4, 8, 8, 8, 8 }; //=========================================================================== ITypeInfo *makeType(type_t type, int size) { switch (type) { case type_string: return makeStringType(size, NULL, NULL); // MORE!! case type_varstring: return makeVarStringType(size); case type_qstring: return makeQStringType(size); case type_unicode: return makeUnicodeType(size, 0); case type_varunicode: return makeVarUnicodeType(size, 0); case type_utf8: return makeUtf8Type(size, 0); case type_data: return makeDataType(size); case type_int: return makeIntType(size, false); case type_swapint: return makeSwapIntType(size, false); case type_packedint: return makePackedIntType(size, false); case type_real: return makeRealType(size); case type_bitfield: return makeBitfieldType(size); case type_boolean: return makeBoolType(); case type_blob: return makeBlobType(); case type_void: return makeVoidType(); case type_null: return makeNullType(); case type_record: return makeRecordType(); case type_pattern: return makePatternType(); case type_rule: return makeRuleType(NULL); case type_token: return makeTokenType(); case type_feature: return makeFeatureType(); case type_event: return makeEventType(); case type_any: return makeAnyType(); case type_date: return makeBoolType(); // JCSMORE: Todo. } return NULL; } static IValue * castViaString(ITypeInfo * type, size32_t len, const char * text) { Owned temp = createStringValue(text, len); return temp->castTo(type); } static IValue * castViaString(ITypeInfo * type, bool isSignedValue, __int64 value, int len) { Owned stype = makeStringType(len, NULL, NULL); Owned temp = createIntValue(value, 8, isSignedValue); Owned temp2 = temp->castTo(stype); return temp2->castTo(type); } //=========================================================================== CTypeInfo::~CTypeInfo() { } IValue * CTypeInfo::castFrom(double value) { //NB: Force VMT use... if (value < 0) return ((ITypeInfo *)this)->castFrom(true, (__int64)value); return ((ITypeInfo *)this)->castFrom(false, (__int64)(unsigned __int64)value); } IValue * CTypeInfo::castFrom(size32_t len, const UChar * text) { unsigned bufflen; rtlDataAttr buff; rtlUnicodeToStrX(bufflen, buff.refstr(), len, text); return ((ITypeInfo *)this)->castFrom(bufflen, buff.getstr()); } unsigned CTypeInfo::getCardinality() { if (length < 4) return 1U << (length * 8); return (unsigned)-1; } unsigned CTypeInfo::getCrc() { unsigned crc = getTypeCode(); crc = hashc((const byte *)&length, sizeof(length), crc); return crc; } unsigned CTypeInfo::getHash() const { unsigned hashcode = getHashKind(); HASHFIELD(length); return hashcode; } bool CTypeInfo::equals(const CTypeInfo & other) const { return (getHashKind() == other.getHashKind()) && (length == other.length); } void CHashedTypeInfo::addObserver(IObserver & observer) { assertex(!observed); assert(&observer == globalTypeCache); observed = true; } void CHashedTypeInfo::removeObserver(IObserver & observer) { assertex(observed); assert(&observer == globalTypeCache); observed = false; } void CHashedTypeInfo::beforeDispose() { CriticalBlock block(*typeCS); if (observed) globalTypeCache->removeExact(this); assertex(!observed); } //=========================================================================== inline unsigned getPromotedBitfieldSize(unsigned len) { if (len <= 8) return 1; else if (len <= 16) return 2; else if (len <= 32) return 4; else return 8; } inline ITypeInfo * getPromotedBitfieldType(unsigned len) { return makeIntType(getPromotedBitfieldSize(len), false); } CBitfieldTypeInfo::CBitfieldTypeInfo(int _length, ITypeInfo * _baseType) : CTypeInfo((_length+7)/8), bitLength(_length) { storeType = _baseType; promoted = getPromotedBitfieldType(_length); } bool CBitfieldTypeInfo::assignableFrom(ITypeInfo *t2) { switch (t2->getTypeCode()) { case type_real: case type_int: case type_decimal: case type_swapint: case type_bitfield: case type_any: case type_packedint: return true; } return false; } IValue * CBitfieldTypeInfo::castFrom(bool isSignedValue, __int64 value) { return createBitfieldValue(value, LINK(this)); } IValue * CBitfieldTypeInfo::castFrom(size32_t len, const char * text) { unsigned __int64 value; if (false)//typeIsSigned) value = rtlStrToInt8(len, text); else value = rtlStrToUInt8(len, text); return createBitfieldValue(value, LINK(this)); } unsigned CBitfieldTypeInfo::getCrc() { unsigned crc = CTypeInfo::getCrc(); crc = hashc((const byte *)&bitLength, sizeof(bitLength), crc); unsigned childCrc = storeType->getCrc(); crc = hashc((const byte *)&childCrc, sizeof(childCrc), crc); return crc; } StringBuffer &CBitfieldTypeInfo::getECLType(StringBuffer &out) { out.append("bitfield").append(bitLength); if (storeType->getSize() != getPromotedBitfieldSize(bitLength)) out.append("_").append(storeType->getSize()); return out; } //--------------------------------------------------------------------------- bool CIntTypeInfo::assignableFrom(ITypeInfo *t2) { switch (t2->getTypeCode()) { case type_real: case type_int: case type_decimal: case type_swapint: case type_bitfield: case type_any: case type_packedint: return true; } return false; } unsigned CIntTypeInfo::getStringLen(void) { unsigned sign = isSigned() ? 1 : 0; return getDigits() + sign; } unsigned CIntTypeInfo::getDigits(void) { switch (length) { case 1: return 3; case 2: return 5; case 3: return 8; case 4: return 10; case 5: return 13; case 6: return 15; case 7: return 17; case 8: return 20; } assertex(false); return 0; } unsigned CIntTypeInfo::getCrc() { unsigned crc = CTypeInfo::getCrc(); crc = hashc((const byte *)&typeIsSigned, sizeof(typeIsSigned), crc); return crc; } StringBuffer &CIntTypeInfo::getECLType(StringBuffer &out) { if (!typeIsSigned) out.append("unsigned"); else out.append("integer"); if(length >0) out.append(length); return out; } //--------------------------------------------------------------------------- StringBuffer &CSwapIntTypeInfo::getECLType(StringBuffer &out) { #if __BYTE_ORDER == __LITTLE_ENDIAN out.append("big_endian "); #else out.append("little_endian "); #endif if (!typeIsSigned) out.append("unsigned "); out.append("integer"); if(length >0) out.append(length); return out; } //--------------------------------------------------------------------------- bool CPackedIntTypeInfo::assignableFrom(ITypeInfo *t2) { switch (t2->getTypeCode()) { case type_real: case type_int: case type_decimal: case type_swapint: case type_bitfield: case type_any: case type_packedint: return true; } return false; } unsigned CPackedIntTypeInfo::getCrc() { return CTypeInfo::getCrc() ^ basetype->getCrc(); } StringBuffer &CPackedIntTypeInfo::getECLType(StringBuffer &out) { out.append("packed "); if (!isSigned()) out.append("unsigned "); out.append("integer"); out.append(basetype->getSize()); return out; } IValue * CPackedIntTypeInfo::castFrom(bool isSignedValue, __int64 value) { return createTruncIntValue(value, LINK(this)); } IValue * CPackedIntTypeInfo::castFrom(size32_t len, const char * text) { unsigned __int64 value; if (basetype->isSigned()) value = rtlStrToInt8(len, text); else value = rtlStrToUInt8(len, text); return createTruncIntValue(value, LINK(this)); } //--------------------------------------------------------------------------- unsigned CRealTypeInfo::getStringLen(void) { switch (length) { // sign + digits + dot + E+- case 4: return 1 + FLOAT_SIG_DIGITS + 1 + 4; case 8: return 1 + DOUBLE_SIG_DIGITS + 1 + 5; } assertex(false); return 0; } unsigned CRealTypeInfo::getDigits(void) { switch (length) { case 4: return FLOAT_SIG_DIGITS; case 8: return DOUBLE_SIG_DIGITS; } assertex(false); return 0; } IValue * CRealTypeInfo::castFrom(bool isSignedValue, __int64 value) { if (isSignedValue) return createRealValue((double)value, LINK(this)); return createRealValue((double)(unsigned __int64)value, LINK(this)); } IValue * CRealTypeInfo::castFrom(double value) { return createRealValue(value, LINK(this)); } IValue * CRealTypeInfo::castFrom(size32_t len, const char * text) { return createRealValue(rtlStrToReal(len, text), LINK(this)); } StringBuffer &CRealTypeInfo::getECLType(StringBuffer &out) { out.append("real"); if(length >0) out.append(length); return out; } bool CRealTypeInfo::assignableFrom(ITypeInfo *t2) { switch (t2->getTypeCode()) { case type_real: case type_int: case type_decimal: case type_swapint: case type_bitfield: case type_any: case type_packedint: return true; } return false; } //--------------------------------------------------------------------------- inline unsigned cvtDigitsToLength(bool isSigned, unsigned digits) { if (digits == UNKNOWN_LENGTH) return UNKNOWN_LENGTH; return isSigned ? digits/2+1 : (digits+1)/2; } CDecimalTypeInfo::CDecimalTypeInfo(unsigned _digits, unsigned _prec, bool _isSigned) : CHashedTypeInfo(cvtDigitsToLength(_isSigned, _digits)) { digits = (_digits == UNKNOWN_LENGTH) ? UNKNOWN_DIGITS : (byte)_digits; prec = (_prec == UNKNOWN_LENGTH) ? UNKNOWN_DIGITS : (byte)_prec; typeIsSigned = _isSigned; }; IValue * CDecimalTypeInfo::createValueFromStack() { Linked retType; if ((length == UNKNOWN_LENGTH) || (length == MAX_DECIMAL_LEADING + MAX_DECIMAL_PRECISION)) { unsigned tosDigits, tosPrecision; DecClipInfo(tosDigits, tosPrecision); unsigned tosLeading = tosDigits - tosPrecision; if (tosLeading > MAX_DECIMAL_LEADING) tosLeading = MAX_DECIMAL_LEADING; if (tosPrecision > MAX_DECIMAL_PRECISION) tosPrecision = MAX_DECIMAL_PRECISION; Owned newType = makeDecimalType(tosLeading+tosPrecision, tosPrecision, typeIsSigned); return createDecimalValueFromStack(newType); } void * val = alloca(length); DecSetPrecision(getDigits(), prec); if (typeIsSigned) DecPopDecimal(val, length, prec); else DecPopUDecimal(val, length, prec); return createDecimalValue(val, LINK(this)); } IValue * CDecimalTypeInfo::castFrom(bool isSignedValue, __int64 value) { BcdCriticalBlock bcdBlock; if (isSignedValue) DecPushInt64(value); else DecPushUInt64(value); return createValueFromStack(); } IValue * CDecimalTypeInfo::castFrom(double value) { DecPushReal(value); return createValueFromStack(); } IValue * CDecimalTypeInfo::castFrom(size32_t len, const char * text) { DecPushString(len, text); return createValueFromStack(); } unsigned CDecimalTypeInfo::getHash() const { unsigned hashcode = CHashedTypeInfo::getHash(); HASHFIELD(prec); HASHFIELD(digits); HASHFIELD(typeIsSigned); return hashcode; } bool CDecimalTypeInfo::equals(const CTypeInfo & _other) const { if (!CHashedTypeInfo::equals(_other)) return false; const CDecimalTypeInfo & other = static_cast(_other); return (prec == other.prec) && (digits == other.digits) && (typeIsSigned == other.typeIsSigned); } unsigned CDecimalTypeInfo::getStringLen(void) { if (length == UNKNOWN_LENGTH) return UNKNOWN_LENGTH; return (typeIsSigned ? 1 : 0) + getDigits() + (prec ? 1 : 0); // sign + digits + dot } StringBuffer &CDecimalTypeInfo::getECLType(StringBuffer &out) { if (!typeIsSigned) out.append('u'); out.append("decimal"); if (digits != UNKNOWN_DIGITS) { out.append((int)digits); if (prec) out.append("_").append((int)prec); } return out; } bool CDecimalTypeInfo::assignableFrom(ITypeInfo *t2) { switch (t2->getTypeCode()) { case type_real: case type_int: case type_decimal: case type_swapint: case type_any: case type_packedint: return true; } return false; } unsigned CDecimalTypeInfo::getBitSize() { if (digits == UNKNOWN_DIGITS) return UNKNOWN_LENGTH; if (typeIsSigned) return (digits+1)*4; else return digits*4; }; unsigned CDecimalTypeInfo::getCrc() { unsigned crc = CTypeInfo::getCrc(); crc = hashc((const byte *)&typeIsSigned, sizeof(typeIsSigned), crc); crc = hashc((const byte *)&prec, sizeof(prec), crc); crc = hashc((const byte *)&digits, sizeof(digits), crc); return crc; } void CDecimalTypeInfo::serialize(MemoryBuffer &tgt) { CTypeInfo::serialize(tgt); tgt.append(prec); tgt.append(digits); tgt.append(typeIsSigned); } //--------------------------------------------------------------------------- bool CBoolTypeInfo::assignableFrom(ITypeInfo *t2) { // FIX: only bool or int type can be assigned to a bool type //return (t2->isScalar()); switch(t2->getTypeCode()) { case type_boolean: case type_int: case type_any: return true; } return false; } IValue * CBoolTypeInfo::castFrom(bool /*isSignedValue*/, __int64 value) { return createBoolValue(value != 0); } IValue * CBoolTypeInfo::castFrom(size32_t len, const char * text) { return castViaString(this, len, text); } StringBuffer &CBoolTypeInfo::getECLType(StringBuffer &out) { return out.append("boolean"); } //--------------------------------------------------------------------------- IValue * CVoidTypeInfo::castFrom(bool isSignedValue, __int64 value) { return NULL; } IValue * CVoidTypeInfo::castFrom(size32_t len, const char * text) { return NULL; } StringBuffer &CVoidTypeInfo::getECLType(StringBuffer &out) { return out; } //--------------------------------------------------------------------------- //Make crc match void for backward compatibility unsigned CNullTypeInfo::getCrc() { unsigned crc = type_void; crc = hashc((const byte *)&length, sizeof(length), crc); return crc; } //--------------------------------------------------------------------------- IValue * CAnyTypeInfo::castFrom(bool isSignedValue, __int64 value) { return createIntValue(value, sizeof(__int64), isSignedValue); } IValue * CAnyTypeInfo::castFrom(size32_t len, const char * text) { return createStringValue(text, len); } IValue * CAnyTypeInfo::castFrom(double value) { return createRealValue(value, sizeof(double)); } StringBuffer &CAnyTypeInfo::getECLType(StringBuffer &out) { return out.append("any"); } //--------------------------------------------------------------------------- CStringTypeInfo::CStringTypeInfo(unsigned _length, ICharsetInfo * _charset, ICollationInfo * _collation) : CTypeInfo(_length), charset(_charset), collation(_collation) { if (!charset) charset.setown(getCharset(NULL)); if (!collation) collation.set(charset->queryDefaultCollation()); } unsigned cardGuesses[] = { 0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; unsigned CStringTypeInfo::getCardinality() { // Wild guess at cardinalities. Probably more accurate than the default integer variety... unsigned numChars = getStringLen(); if (numChars < 10) return cardGuesses[numChars]; return (unsigned)-1; } bool CStringTypeInfo::assignableFrom(ITypeInfo *t2) { switch (t2->getTypeCode()) { case type_string: case type_varstring: case type_qstring: case type_any: #ifdef DATA_STRING_COMPATIBLE case type_data: #endif return true; } return false; } IValue * CStringTypeInfo::castFrom(bool isSignedValue, __int64 value) { return castViaString(this, isSignedValue, value, getStringLen()); } IValue * CStringTypeInfo::castFrom(double value) { char * text = NULL; unsigned length = getStringLen(); if (length == UNKNOWN_LENGTH) { rtlRealToStrX(length, text, value); } else { text = (char *)malloc(length); rtlRealToStr(length, text, value); } IValue * ret = castFrom(length, text); free(text); return ret; } IValue * CStringTypeInfo::castFrom(size32_t len, const char * text) { Owned ascii = getCharset(asciiAtom); return createStringValue(text, LINK(this), len, ascii); } unsigned CStringTypeInfo::getCrc() { unsigned crc = CTypeInfo::getCrc(); if (charset) { const char * name = charset->queryName()->str(); //MORE: This and following should really be case insensitive, but we get away with it at the moment because the atoms are created very early crc = hashc((const byte *)name, (size32_t)strlen(name), crc); } if (collation) { const char * name = collation->queryName()->str(); crc = hashc((const byte *)name, (size32_t)strlen(name), crc); } return crc; } StringBuffer &CStringTypeInfo::getECLType(StringBuffer &out) { if (charset->queryName() == ebcdicAtom) out.append("EBCDIC "); out.append("string"); if (length != UNKNOWN_LENGTH) out.append(length); return out; } StringBuffer &CStringTypeInfo::getDescriptiveType(StringBuffer &out) { out.append("string"); if (length != UNKNOWN_LENGTH) out.append(length); if(charset || collation) { out.append(" ("); if(charset) out.append("charset:").append(charset->queryName()->str()); if(charset && collation) out.append(", "); if(collation) out.append("collation:").append(collation->queryName()->str()); out.append(")"); } return out; } //--------------------------------------------------------------------------- unsigned CUnicodeTypeInfo::getCardinality() { // Wild guess at cardinalities. Probably more accurate than the default integer variety... if (length < 19) return cardGuesses[(length+1)/2]; return (unsigned)-1; } IValue * CUnicodeTypeInfo::castFrom(bool isSignedValue, __int64 value) { Owned asciiType = makeStringType(getStringLen(), 0, 0); Owned asciiValue = asciiType->castFrom(isSignedValue, value); return asciiValue->castTo(this); } IValue * CUnicodeTypeInfo::castFrom(double value) { Owned asciiType = makeStringType(getStringLen(), 0, 0); Owned asciiValue = asciiType->castFrom(value); return asciiValue->castTo(this); } IValue * CUnicodeTypeInfo::castFrom(size32_t len, const char * text) { Owned unicodeValue = createUnicodeValue(text, len, locale->str(), false); return unicodeValue->castTo(this); } IValue * CUnicodeTypeInfo::castFrom(size32_t len, const UChar * uchars) { return createUnicodeValue(len, uchars, LINK(this)); } StringBuffer &CUnicodeTypeInfo::getECLType(StringBuffer &out) { out.append("unicode"); if(locale && *locale->str()) out.append('_').append(locale->str()); if(length != UNKNOWN_LENGTH) out.append(length/2); return out; } bool CUnicodeTypeInfo::assignableFrom(ITypeInfo *t2) { switch (t2->getTypeCode()) { case type_any: return true; } //All string types can be converted to unicode with no loss of information, so allow an assign without a cast if (isStringType(t2)) return true; return isUnicodeType(t2) && haveCommonLocale(this, t2); } //--------------------------------------------------------------------------- CVarUnicodeTypeInfo::CVarUnicodeTypeInfo(unsigned len, _ATOM _locale) : CUnicodeTypeInfo(len, _locale) { #if UNKNOWN_LENGTH != 0 assertex(len != 0); #endif }; IValue * CVarUnicodeTypeInfo::castFrom(size32_t len, const char * text) { Owned unicodeValue = createVarUnicodeValue(text, len, locale->str(), false); return unicodeValue->castTo(this); } IValue * CVarUnicodeTypeInfo::castFrom(size32_t len, const UChar * uchars) { return createVarUnicodeValue(len, uchars, LINK(this)); } StringBuffer & CVarUnicodeTypeInfo::getECLType(StringBuffer & out) { out.append("varunicode"); if(locale && *locale->str()) out.append('_').append(locale->str()); if(length != UNKNOWN_LENGTH) out.append(length/2-1); return out; } //--------------------------------------------------------------------------- IValue * CUtf8TypeInfo::castFrom(size32_t len, const UChar * uchars) { unsigned tlen = getStringLen(); if (tlen == UNKNOWN_LENGTH) tlen = len; rtlDataAttr buff(tlen * 4); rtlUnicodeToUtf8(tlen, buff.getstr(), len, uchars); return createUtf8Value(tlen, buff.getstr(), LINK(this)); } StringBuffer &CUtf8TypeInfo::getECLType(StringBuffer &out) { out.append("utf8"); if(locale && *locale->str()) out.append('_').append(locale->str()); if(length != UNKNOWN_LENGTH) out.append('_').append(length/4); return out; } //--------------------------------------------------------------------------- CDataTypeInfo::CDataTypeInfo(int _length) : CStringTypeInfo(_length, getCharset(dataAtom), NULL) { } StringBuffer &CDataTypeInfo::getECLType(StringBuffer &out) { if(length != UNKNOWN_LENGTH && length != INFINITE_LENGTH) return out.append("data").append(length); else return out.append("data"); } bool CDataTypeInfo::assignableFrom(ITypeInfo *t2) { switch (t2->getTypeCode()) { case type_data: case type_any: #ifdef DATA_STRING_COMPATIBLE case type_string: case type_varstring: #endif return true; } return false; } //--------------------------------------------------------------------------- CVarStringTypeInfo::CVarStringTypeInfo(unsigned len, ICharsetInfo * _charset, ICollationInfo * _collation) : CStringTypeInfo(len, _charset, _collation) { #if UNKNOWN_LENGTH != 0 assertex(len != 0); #endif }; IValue * CVarStringTypeInfo::castFrom(size32_t len, const char * text) { Owned ascii = getCharset(asciiAtom); return createVarStringValue(text, LINK(this), len, ascii); } StringBuffer &CVarStringTypeInfo::getECLType(StringBuffer &out) { out.append("varstring"); if (length != UNKNOWN_LENGTH) out.append(length-1); return out; } StringBuffer &CVarStringTypeInfo::getDescriptiveType(StringBuffer &out) { out.append("varstring"); if (length != UNKNOWN_LENGTH) out.append(length-1); if(charset || collation) { out.append(" ("); if(charset) out.append("charset:").append(charset->queryName()->str()); if(charset && collation) out.append(", "); if(collation) out.append("collation:").append(collation->queryName()->str()); out.append(")"); } return out; } //--------------------------------------------------------------------------- CQStringTypeInfo::CQStringTypeInfo(unsigned _strLength) : CStringTypeInfo(_strLength == UNKNOWN_LENGTH ? UNKNOWN_LENGTH : rtlQStrSize(_strLength), NULL, NULL) { strLength = _strLength; } #if 0 bool CQStringTypeInfo::assignableFrom(ITypeInfo *t2) { switch (t2->getTypeCode()) { case type_qstring: case type_any: return true; } return false; } #endif IValue * CQStringTypeInfo::castFrom(size32_t len, const char * text) { if (length != UNKNOWN_LENGTH) { if (len >= strLength) len = strLength; } return createQStringValue(len, text, LINK(this)); } StringBuffer &CQStringTypeInfo::getECLType(StringBuffer &out) { out.append("qstring"); if (length != UNKNOWN_LENGTH) out.append(strLength); return out; } //--------------------------------------------------------------------------- IValue * CCharTypeInfo::castFrom(bool isSignedValue, __int64 value) { return castViaString(this, isSignedValue, value, 1); } StringBuffer &CCharTypeInfo::getECLType(StringBuffer &out) { assertex(false); return out; } IValue * CCharTypeInfo::castFrom(size32_t len, const char * text) { return createCharValue(len ? *text : ' ', caseSensitive); } //--------------------------------------------------------------------------- IValue * CIntTypeInfo::castFrom(bool isSignedValue, __int64 value) { return createTruncIntValue(value, LINK(this)); } IValue * CIntTypeInfo::castFrom(size32_t len, const char * text) { unsigned __int64 value; if (typeIsSigned) value = rtlStrToInt8(len, text); else value = rtlStrToUInt8(len, text); return createTruncIntValue(value, LINK(this)); } //=========================================================================== class StringValueMapper : public Mapping { size32_t index; public: StringValueMapper(const void *k, int ksize, size32_t index); size32_t getIndex() { return index; } }; StringValueMapper::StringValueMapper(const void *_key, int _ksize, size32_t _index) : Mapping(_key, _ksize) { index = _index; } static int getIntSize(size32_t range) { if (range <= 0xff) return 1; else if (range <= 0xffff) return 2; else return 4; } //=========================================================================== CEnumeratedTypeInfo::CEnumeratedTypeInfo(ITypeInfo *_base, size32_t _numValues) : CTypeInfo(getIntSize(_numValues)), valueMap(_base->getSize(), false), base(_base) { numValues = _numValues; // valueMap.setCapacity(_numValues); } StringBuffer &CEnumeratedTypeInfo::getECLType(StringBuffer &out) { assertex(false); return out; } ITypeInfo *CEnumeratedTypeInfo::getTypeInfo() { Link(); return this; } int CEnumeratedTypeInfo::addValue(IValue *val, size32_t frequency) { assertex(!IsShared()); size32_t baseSize = base->getSize(); assertex(val->queryType()==base); void *buf = malloc(baseSize); val->toMem(buf); size32_t index = valueList.length(); valueMap.addOwn(*new StringValueMapper(buf, baseSize, index)); valueList.append(*val); free(buf); return index; } IValue *CEnumeratedTypeInfo::castFrom(bool isSignedValue, __int64 value) { return createEnumValue((int) value, LINK(this)); } IValue *CEnumeratedTypeInfo::castFrom(size32_t len, const char * text) { MemoryAttr temp; size32_t baselen = base->getSize(); if (lengetIndex(), LINK(this)); else { // assertex(false); return NULL; // MORE - is this right? } } IValue *CEnumeratedTypeInfo::queryValue(size32_t index) { if (valueList.isItem(index)) return (IValue *) &valueList.item(index); else return NULL; } ITypeInfo *CEnumeratedTypeInfo::queryBase() { return base; } extern DEFTYPE_API IEnumeratedTypeBuilder *makeEnumeratedTypeBuilder(ITypeInfo *base, aindex_t numValues) { return new CEnumeratedTypeInfo(base, numValues); } //=========================================================================== ITypeInfo *CBasedTypeInfo::queryChildType() { return basetype; } bool CBasedTypeInfo::assignableFrom(ITypeInfo *t2) { return getTypeCode()==t2->getTypeCode() && queryChildType()==t2->queryChildType(); } unsigned CBasedTypeInfo::getHash() const { unsigned hashcode = CHashedTypeInfo::getHash(); HASHFIELD(basetype); return hashcode; } bool CBasedTypeInfo::equals(const CTypeInfo & _other) const { if (!CHashedTypeInfo::equals(_other)) return false; const CBasedTypeInfo & other = static_cast(_other); return basetype == other.basetype; } void CBasedTypeInfo::serializeSkipChild(MemoryBuffer &tgt) { CTypeInfo::serialize(tgt); ITypeInfo * child = basetype ? basetype->queryChildType() : NULL; serializeType(tgt, child); } //=========================================================================== bool CTransformTypeInfo::assignableFrom(ITypeInfo *t2) { if (getTypeCode()==t2->getTypeCode()) { ITypeInfo *c1 = queryChildType(); ITypeInfo *c2 = t2->queryChildType(); if (c1==NULL || c2==NULL || c1->assignableFrom(c2)) return true; } return false; } bool CSortListTypeInfo::assignableFrom(ITypeInfo *t2) { return (getTypeCode()==t2->getTypeCode()); { //Not convinced any of this should be here... ITypeInfo *c1 = queryChildType(); ITypeInfo *c2 = t2->queryChildType(); if (c1 == c2) return true; if (!c1 || !c2) return false; if (c1->assignableFrom(c2)) return true; return false; } return queryChildType()->assignableFrom(t2); } bool CRowTypeInfo::assignableFrom(ITypeInfo *t2) { if (getTypeCode()==t2->getTypeCode()) { ITypeInfo *c1 = queryChildType(); ITypeInfo *c2 = t2->queryChildType(); if (c1==NULL || c2==NULL || c1->assignableFrom(c2)) return true; } return false; } //=========================================================================== bool CDictionaryTypeInfo::assignableFrom(ITypeInfo *t2) { if (getTypeCode()==t2->getTypeCode()) { ITypeInfo *c1 = queryChildType(); ITypeInfo *c2 = t2->queryChildType(); if (c1==NULL || c2==NULL || c1->assignableFrom(c2)) return true; } return false; } StringBuffer & CDictionaryTypeInfo::getECLType(StringBuffer & out) { ITypeInfo * recordType = ::queryRecordType(this); out.append(queryTypeName()); if (recordType) { out.append(" of "); recordType->getECLType(out); } return out; } void CDictionaryTypeInfo::serialize(MemoryBuffer &tgt) { CBasedTypeInfo::serializeSkipChild(tgt); } //=========================================================================== bool CTableTypeInfo::assignableFrom(ITypeInfo *t2) { if (getTypeCode()==t2->getTypeCode()) { ITypeInfo *c1 = queryChildType(); ITypeInfo *c2 = t2->queryChildType(); if (c1==NULL || c2==NULL || c1->assignableFrom(c2)) return true; } return false; } IInterface *CTableTypeInfo::queryDistributeInfo() { return distributeinfo; } IInterface *CTableTypeInfo::queryGlobalSortInfo() { return globalsortinfo; } IInterface *CTableTypeInfo::queryLocalUngroupedSortInfo() { return localsortinfo; } unsigned CTableTypeInfo::getHash() const { unsigned hashcode = CBasedTypeInfo::getHash(); HASHFIELD(distributeinfo); HASHFIELD(globalsortinfo); HASHFIELD(localsortinfo); return hashcode; } bool CTableTypeInfo::equals(const CTypeInfo & _other) const { if (!CBasedTypeInfo::equals(_other)) return false; const CTableTypeInfo & other = static_cast(_other); return (distributeinfo == other.distributeinfo) && (globalsortinfo == other.globalsortinfo) && (localsortinfo == other.localsortinfo); } StringBuffer & CTableTypeInfo::getECLType(StringBuffer & out) { ITypeInfo * recordType = ::queryRecordType(this); out.append(queryTypeName()); if (recordType) { out.append(" of "); recordType->getECLType(out); } return out; } void CTableTypeInfo::serialize(MemoryBuffer &tgt) { assertex(!globalsortinfo && !localsortinfo && !distributeinfo); CBasedTypeInfo::serializeSkipChild(tgt); } bool CGroupedTableTypeInfo::assignableFrom(ITypeInfo *t2) { if (getTypeCode()==t2->getTypeCode()) { ITypeInfo *c1 = queryChildType(); ITypeInfo *c2 = t2->queryChildType(); if (!c1 || !c2) return c1==c2; if (c1->assignableFrom(c2)) return true; } return false; } IInterface *CGroupedTableTypeInfo::queryDistributeInfo() { return queryChildType()->queryDistributeInfo(); } IInterface *CGroupedTableTypeInfo::queryGroupInfo() { return groupinfo; } IInterface *CGroupedTableTypeInfo::queryGlobalSortInfo() { return queryChildType()->queryGlobalSortInfo(); } IInterface *CGroupedTableTypeInfo::queryLocalUngroupedSortInfo() { return queryChildType()->queryLocalUngroupedSortInfo(); } IInterface *CGroupedTableTypeInfo::queryGroupSortInfo() { return groupsortinfo; } unsigned CGroupedTableTypeInfo::getHash() const { unsigned hashcode = CBasedTypeInfo::getHash(); HASHFIELD(groupinfo); HASHFIELD(groupsortinfo); return hashcode; } bool CGroupedTableTypeInfo::equals(const CTypeInfo & _other) const { if (!CBasedTypeInfo::equals(_other)) return false; const CGroupedTableTypeInfo & other = static_cast(_other); return (groupsortinfo == other.groupsortinfo) && (groupinfo == other.groupinfo); } void CGroupedTableTypeInfo::serialize(MemoryBuffer &tgt) { assertex(!groupsortinfo && !groupinfo); CBasedTypeInfo::serialize(tgt); } bool CSetTypeInfo::assignableFrom(ITypeInfo *t2) { return getTypeCode()==t2->getTypeCode() && (!queryChildType() || !t2->queryChildType() || queryChildType()->assignableFrom(t2->queryChildType())); } StringBuffer & CSetTypeInfo::getECLType(StringBuffer & out) { out.append(queryTypeName()); if (basetype) { out.append(" of "); queryChildType()->getECLType(out); } else out.append(" of any"); return out; } //=========================================================================== bool CFunctionTypeInfo::assignableFrom(ITypeInfo *t2) { return this == t2; } IInterface *CFunctionTypeInfo::queryModifierExtra() { return static_cast(this); } unsigned CFunctionTypeInfo::getHash() const { unsigned hashcode = CBasedTypeInfo::getHash(); HASHFIELD(parameters); HASHFIELD(defaults); return hashcode; } bool CFunctionTypeInfo::equals(const CTypeInfo & _other) const { if (!CBasedTypeInfo::equals(_other)) return false; const CFunctionTypeInfo & other = static_cast(_other); return (parameters == other.parameters) && (defaults == other.defaults); } void CFunctionTypeInfo::serialize(MemoryBuffer &tgt) { throwUnexpected(); CBasedTypeInfo::serialize(tgt); } //=========================================================================== size32_t CArrayTypeInfo::getSize() { if (length == UNKNOWN_LENGTH) return UNKNOWN_LENGTH; if (basetype->isReference()) return length * sizeof(void *); size32_t baseSize = basetype->getSize(); if (baseSize == UNKNOWN_LENGTH) return UNKNOWN_LENGTH; return baseSize * length; } //=========================================================================== unsigned CModifierTypeInfo::getHash() const { unsigned hashcode = CHashedTypeInfo::getHash(); HASHFIELD(baseType); HASHFIELD(kind); HASHFIELD(extra); return hashcode; } bool CModifierTypeInfo::equals(const CTypeInfo & _other) const { if (!CHashedTypeInfo::equals(_other)) return false; const CModifierTypeInfo & other = static_cast(_other); return (baseType == other.baseType) && (kind == other.kind) && (extra == other.extra); } //=========================================================================== extern DEFTYPE_API ITypeInfo *makeStringType(unsigned len, ICharsetInfo * charset, ICollationInfo * collation) { if (!charset) charset = getCharset(NULL); if (!collation) { collation = charset->queryDefaultCollation(); collation->Link(); } CStringTypeKey key; key.length = len; key.charset.set(charset); key.collation.set(collation); CriticalBlock procedure(*typeCS); ITypeInfo *ret; IInterface * * match = stt->getValue(key); if (match) { ::Release(charset); ::Release(collation); ret = (ITypeInfo *)LINK(*match); } else { CStringTypeInfo* t = new CStringTypeInfo(len, charset, collation); ret = t; stt->setValue(key, ret); } return ret; } extern DEFTYPE_API ITypeInfo *makeVarStringType(unsigned len, ICharsetInfo * charset, ICollationInfo * collation) { //NB: Length passed is the number of characters.... unsigned size = (len != UNKNOWN_LENGTH) ? len + 1 : UNKNOWN_LENGTH; //NB: Length passed is the number of characters.... if (!charset) charset = getCharset(NULL); if (!collation) { collation = charset->queryDefaultCollation(); collation->Link(); } CStringTypeKey key; key.length = size; key.charset.set(charset); key.collation.set(collation); CriticalBlock procedure(*typeCS); ITypeInfo *ret; IInterface * * match = vstt->getValue(key); if (match) { ::Release(charset); ::Release(collation); ret = (ITypeInfo *)LINK(*match); } else { ret = new CVarStringTypeInfo(size, charset, collation); vstt->setValue(key, ret); } return ret; } extern DEFTYPE_API ITypeInfo *makeQStringType(int len) { CStringTypeKey key; key.length = len; key.charset.set(NULL); key.collation.set(NULL); CriticalBlock procedure(*typeCS); ITypeInfo *ret; IInterface * * match = qstt->getValue(key); if (match) { ret = (ITypeInfo *)LINK(*match); } else { ret = new CQStringTypeInfo(len); qstt->setValue(key, ret); } return ret; } extern DEFTYPE_API ITypeInfo *makeUnicodeType(unsigned len, _ATOM locale) { if(!locale) locale = emptyAtom; CUnicodeTypeKey key; key.length = len; key.locale.set(locale); CriticalBlock procedure(*typeCS); ITypeInfo *ret; IInterface * * match = utt->getValue(key); if(match) ret = (ITypeInfo *)LINK(*match); else { if(len == UNKNOWN_LENGTH) ret = new CUnicodeTypeInfo(UNKNOWN_LENGTH, locale); else ret = new CUnicodeTypeInfo(len*2, locale); utt->setValue(key, ret); } return ret; } extern DEFTYPE_API ITypeInfo *makeVarUnicodeType(unsigned len, _ATOM locale) { if(!locale) locale = emptyAtom; CUnicodeTypeKey key; key.length = len; key.locale.set(locale); CriticalBlock procedure(*typeCS); ITypeInfo *ret; IInterface * * match = vutt->getValue(key); if(match) ret = (ITypeInfo *)LINK(*match); else { if(len == UNKNOWN_LENGTH) ret = new CVarUnicodeTypeInfo(UNKNOWN_LENGTH, locale); else ret = new CVarUnicodeTypeInfo((len+1)*2, locale); vutt->setValue(key, ret); } return ret; } extern DEFTYPE_API ITypeInfo *makeUtf8Type(unsigned len, _ATOM locale) { if(!locale) locale = emptyAtom; CUnicodeTypeKey key; key.length = len; key.locale.set(locale); CriticalBlock procedure(*typeCS); ITypeInfo *ret; IInterface * * match = u8tt->getValue(key); if(match) ret = (ITypeInfo *)LINK(*match); else { if (len == UNKNOWN_LENGTH) ret = new CUtf8TypeInfo(UNKNOWN_LENGTH, locale); else ret = new CUtf8TypeInfo(len*4, locale); u8tt->setValue(key, ret); } return ret; } extern DEFTYPE_API ITypeInfo *makeDataType(int len) { CStringTypeKey key; key.length = len; key.charset.set(NULL); key.collation.set(NULL); CriticalBlock procedure(*typeCS); ITypeInfo *ret; IInterface * * match = datatt->getValue(key); if (match) { ret = (ITypeInfo *)LINK(*match); } else { ret = new CDataTypeInfo(len); datatt->setValue(key, ret); } return ret; } extern DEFTYPE_API ITypeInfo *makeIntType(int len, bool isSigned) { assertex(len>0 && len <= 8); if (len <= 0 || len > 8) return NULL; CriticalBlock procedure(*typeCS); CIntTypeInfo *ret = itt[isSigned][len-1]; if (ret==NULL) ret = itt[isSigned][len-1] = new CIntTypeInfo(len, isSigned); ::Link(ret); return ret; } extern DEFTYPE_API ITypeInfo *makeSwapIntType(int len, bool isSigned) { assertex(len>0 && len <= 8); if (len <= 0 || len > 8) return NULL; CriticalBlock procedure(*typeCS); CIntTypeInfo *ret = sitt[isSigned][len-1]; if (ret==NULL) ret = sitt[isSigned][len-1] = new CSwapIntTypeInfo(len, isSigned); ::Link(ret); return ret; } extern DEFTYPE_API ITypeInfo *makePackedIntType(ITypeInfo * basetype) { CriticalBlock procedure(*typeCS); IInterface * * match = pitt->getValue(basetype); if (match) { ::Release(basetype); return (ITypeInfo *)LINK(*match); } ITypeInfo * next = new CPackedIntTypeInfo(basetype); pitt->setValue(basetype, next); return next; } extern DEFTYPE_API ITypeInfo *makePackedIntType(int len, bool isSigned) { return makePackedIntType(makeIntType(len, isSigned)); } extern DEFTYPE_API ITypeInfo *makeRealType(int len) { assertex(len == 4 || len == 8); if (len != 4 && len != 8) return NULL; CriticalBlock procedure(*typeCS); CRealTypeInfo *ret = realtt[len-1]; if (ret==NULL) ret = realtt[len-1] = new CRealTypeInfo(len); ::Link(ret); return ret; } /* Precondition: len>0 && len>64 */ extern DEFTYPE_API ITypeInfo *makeBitfieldType(int len, ITypeInfo * basetype) { assertex(len>0 && len <= 64); if (len <= 0 || len > 64) return NULL; CriticalBlock procedure(*typeCS); if (basetype) { assertex(basetype->getTypeCode() == type_int); } else basetype = getPromotedBitfieldType(len); unsigned baseSize=basetype->getSize(); CBitfieldTypeInfo *ret = bftt[len-1][baseSize-1]; if (ret==NULL) ret = bftt[len-1][baseSize-1] = new CBitfieldTypeInfo(len, basetype); else ::Release(basetype); ::Link(ret); return ret; } extern DEFTYPE_API ITypeInfo *makeBoolType() { CriticalBlock procedure(*typeCS); if (!btt) btt = new CBoolTypeInfo(); ::Link(btt); return btt; } extern DEFTYPE_API ITypeInfo *makeBlobType() { CriticalBlock procedure(*typeCS); if (!bltt) bltt = new CBlobTypeInfo(); ::Link(bltt); return bltt; } extern DEFTYPE_API ITypeInfo *makeVoidType() { CriticalBlock procedure(*typeCS); if (!vtt) vtt = new CVoidTypeInfo(); ::Link(vtt); return vtt; } extern DEFTYPE_API ITypeInfo *makeNullType() { CriticalBlock procedure(*typeCS); if (!ntt) ntt = new CNullTypeInfo(); ::Link(ntt); return ntt; } extern DEFTYPE_API ITypeInfo *makeRecordType() { CriticalBlock procedure(*typeCS); if (!rtt) rtt = new CRecordTypeInfo(); ::Link(rtt); return rtt; } extern DEFTYPE_API ITypeInfo *makePatternType() { CriticalBlock procedure(*typeCS); if (!patt) patt = new CPatternTypeInfo(); ::Link(patt); return patt; } extern DEFTYPE_API ITypeInfo *makeTokenType() { CriticalBlock procedure(*typeCS); if (!tokentt) tokentt = new CTokenTypeInfo(); ::Link(tokentt); return tokentt; } extern DEFTYPE_API ITypeInfo *makeFeatureType() { CriticalBlock procedure(*typeCS); if (!featurett) featurett = new CFeatureTypeInfo(); ::Link(featurett); return featurett; } extern DEFTYPE_API ITypeInfo *makeEventType() { CriticalBlock procedure(*typeCS); if (!ett) ett = new CEventTypeInfo(); ::Link(ett); return ett; } extern DEFTYPE_API ITypeInfo *makeAnyType() { CriticalBlock procedure(*typeCS); if (!anytt) anytt = new CAnyTypeInfo(); ::Link(anytt); return anytt; } extern DEFTYPE_API ITypeInfo *makeCharType(bool caseSensitive) { CriticalBlock procedure(*typeCS); if (!ctt[caseSensitive]) ctt[caseSensitive] = new CCharTypeInfo(caseSensitive); ::Link(ctt[caseSensitive]); return ctt[caseSensitive]; } extern DEFTYPE_API ITypeInfo *makeSetType(ITypeInfo *basetype) { CriticalBlock procedure(*typeCS); IInterface * * match = setTT->getValue(basetype); if (match) { ::Release(basetype); return (ITypeInfo *)LINK(*match); } ITypeInfo * next = new CSetTypeInfo(basetype); setTT->setValue(basetype, next); return next; } //----------------------------- static ITypeInfo * commonUpType(CHashedTypeInfo * candidate) { ITypeInfo * match; { CriticalBlock block(*typeCS); match = globalTypeCache->addOrFind(*candidate); if (match == candidate) return match; match->Link(); if (!static_cast(match)->isAlive()) { globalTypeCache->replace(*candidate); return candidate; } } candidate->Release(); return match; } extern DEFTYPE_API ITypeInfo *makeDecimalType(unsigned digits, unsigned prec, bool isSigned) { assertex((digits == UNKNOWN_LENGTH) || (digits - prec <= MAX_DECIMAL_LEADING)); assertex((prec == UNKNOWN_LENGTH) || ((prec <= digits) && (prec <= MAX_DECIMAL_PRECISION))); assertex((prec != UNKNOWN_LENGTH) || (digits == UNKNOWN_LENGTH)); return commonUpType(new CDecimalTypeInfo(digits, prec, isSigned)); } extern DEFTYPE_API ITypeInfo *makeRuleType(ITypeInfo *basetype) { return commonUpType(new CRuleTypeInfo(basetype)); } extern DEFTYPE_API ITypeInfo *makeTableType(ITypeInfo *basetype, IInterface * distributeinfo, IInterface *globalsortinfo, IInterface *localsortinfo) { assertex(!basetype || basetype->getTypeCode() == type_row); return commonUpType(new CTableTypeInfo(basetype, distributeinfo, globalsortinfo, localsortinfo)); } extern DEFTYPE_API ITypeInfo *makeDictionaryType(ITypeInfo *basetype) { assertex(!basetype || basetype->getTypeCode() == type_row); return commonUpType(new CDictionaryTypeInfo(basetype)); } extern DEFTYPE_API ITypeInfo *makeGroupedTableType(ITypeInfo *basetype, IInterface *groupinfo, IInterface *sortinfo) { return commonUpType(new CGroupedTableTypeInfo(basetype, groupinfo, sortinfo)); } extern DEFTYPE_API ITypeInfo *makeFunctionType(ITypeInfo *basetype, IInterface * parameters, IInterface * defaults) { assertex(basetype->getTypeCode() != type_function); // not just yet anyway if (!basetype || !parameters) { basetype->Release(); parameters->Release(); defaults->Release(); throwUnexpected(); } return commonUpType(new CFunctionTypeInfo(basetype, parameters, defaults)); } /* In basetype: linked. Return: linked */ extern DEFTYPE_API ITypeInfo *makeRowType(ITypeInfo *basetype) { assertex(!basetype || basetype->getTypeCode() == type_record); return commonUpType(new CRowTypeInfo(basetype)); } extern DEFTYPE_API ITypeInfo *makeTransformType(ITypeInfo *basetype) { assertex(!basetype || basetype->getTypeCode() == type_record); return commonUpType(new CTransformTypeInfo(basetype)); } extern DEFTYPE_API ITypeInfo *makeSortListType(ITypeInfo *basetype) { return commonUpType(new CSortListTypeInfo(basetype)); } /* In basetype: linked. Return: linked */ ITypeInfo * makeArrayType(ITypeInfo * basetype, unsigned size) { return commonUpType(new CArrayTypeInfo(basetype, size)); } /* In basetype: linked. Return: linked */ ITypeInfo * makePointerType(ITypeInfo * basetype) { return commonUpType(new CPointerTypeInfo(basetype)); } /* In basetype: linked. Return: linked */ ITypeInfo * makeConstantModifier(ITypeInfo * basetype) { return makeModifier(basetype, typemod_const, NULL); } /* In basetype: linked. Return: linked */ ITypeInfo * makeReferenceModifier(ITypeInfo * basetype) { return makeModifier(basetype, typemod_ref, NULL); } ITypeInfo * makeWrapperModifier(ITypeInfo * basetype) { return makeModifier(basetype, typemod_wrapper, NULL); } ITypeInfo * makeModifier(ITypeInfo * basetype, typemod_t kind, IInterface * extra) { if (kind == typemod_none) { ::Release(extra); return basetype; } #ifdef _DEBUG ITypeInfo * cur = basetype; loop { if (cur->queryModifier() == typemod_none) break; if (cur->queryModifier() == kind) { if (cur->queryModifierExtra() == extra) throwUnexpected(); } cur = cur->queryTypeBase(); } #endif return commonUpType(new CModifierTypeInfo(basetype, kind, extra)); } /* In basetype: linked. Return: linked */ ITypeInfo * makeClassType(const char * name) { //MORE!! return new CClassTypeInfo(name); } //=========================================================================== template inline void ReleaseAndClear(T * & ptr) { if (ptr) { ptr->Release(); ptr = NULL; } } void ClearTypeCache() { size32_t i; stt->kill(); vstt->kill(); qstt->kill(); datatt->kill(); utt->kill(); vutt->kill(); u8tt->kill(); for (i = 0; i < 8; i++) { ReleaseAndClear(itt[false][i]); ReleaseAndClear(itt[true][i]); ReleaseAndClear(sitt[false][i]); ReleaseAndClear(sitt[true][i]); } for (i = 0; i < _elements_in(bftt); i++) { int j; for (j = 0; j < _elements_in(bftt[0]); j++) ReleaseAndClear(bftt[i][j]); } for (i = 0; i < _elements_in(realtt); i++) ReleaseAndClear(realtt[i]); for (i = 0; i < _elements_in(ctt); i++) ReleaseAndClear(ctt[i]); ReleaseAndClear(btt); ReleaseAndClear(bltt); ReleaseAndClear(vtt); ReleaseAndClear(ntt); ReleaseAndClear(rtt); ReleaseAndClear(patt); ReleaseAndClear(tokentt); ReleaseAndClear(featurett); ReleaseAndClear(ett); ReleaseAndClear(anytt); pitt->kill(); setTT->kill(); globalTypeCache->kill(); ReleaseAndClear(dataCharset); ReleaseAndClear(asciiCharset); ReleaseAndClear(ebcdicCharset); ReleaseAndClear(utf8Charset); ReleaseAndClear(asciiCollation); ReleaseAndClear(ebcdicCollation); ReleaseAndClear(ascii2ebcdic); ReleaseAndClear(ebcdic2ascii); } //=========================================================================== //This function is here for efficiency - if a particular case is not implemented, //the default case handles it... MemoryBuffer & appendBufferFromMem(MemoryBuffer & mem, ITypeInfo * type, const void * data) { unsigned len = type->getSize(); switch (type->getTypeCode()) { case type_string: case type_data: case type_varstring: case type_qstring: case type_decimal: mem.append(len, data); break; default: mem.appendEndian(len, data); break; } return mem; } //============================================================================ bool isNumericType(ITypeInfo * type) { switch (type->getTypeCode()) { case type_bitfield: case type_real: case type_int: case type_swapint: case type_packedint: case type_decimal: return true; } return false; } bool isStringType(ITypeInfo * type) { switch (type->getTypeCode()) { case type_data: case type_string: case type_varstring: case type_qstring: return true; } return false; } bool isSimpleStringType(ITypeInfo * type) { switch (type->getTypeCode()) { case type_data: case type_string: case type_varstring: return true; } return false; } bool isIntegralType(ITypeInfo * type) { switch (type->getTypeCode()) { case type_bitfield: case type_int: case type_swapint: case type_packedint: return true; } return false; } bool isPatternType(ITypeInfo * type) { switch(type->getTypeCode()) { case type_pattern: case type_token: case type_rule: return true; default: return false; } } bool isUnicodeType(ITypeInfo * type) { switch(type->getTypeCode()) { case type_unicode: case type_varunicode: case type_utf8: return true; default: return false; } } bool isDatasetType(ITypeInfo * type) { switch(type->getTypeCode()) { case type_table: case type_groupedtable: return true; default: return false; } } bool isSingleValuedType(ITypeInfo * type) { if (!type) return false; switch (type->getTypeCode()) { case type_boolean: case type_int: case type_real: case type_decimal: case type_string: case type_date: case type_bitfield: case type_char: case type_enumerated: case type_varstring: case type_data: case type_alien: case type_swapint: case type_packedint: case type_qstring: case type_unicode: case type_varunicode: case type_utf8: return true; } return false; } //============================================================================ ITypeInfo * getNumericType(ITypeInfo * type) { unsigned digits = 9; switch (type->getTypeCode()) { case type_real: case type_int: case type_swapint: case type_decimal: case type_packedint: return LINK(type); case type_bitfield: { ITypeInfo * promoted = type->queryPromotedType(); return LINK(promoted); } case type_varstring: case type_qstring: case type_string: case type_unicode: case type_utf8: case type_varunicode: case type_data: digits = type->getDigits(); if (digits == UNKNOWN_LENGTH) digits = 20; break; case type_boolean: digits = 1; break; } if (digits > 9) return makeIntType(8, true); if (digits > 4) return makeIntType(4, true); if (digits > 2) return makeIntType(2, true); return makeIntType(1, true); } ITypeInfo * getStringType(ITypeInfo * type) { switch (type->getTypeCode()) { case type_string: case type_varstring: return LINK(type); } return makeStringType(type->getStringLen(), NULL, NULL); } ITypeInfo * getVarStringType(ITypeInfo * type) { switch (type->getTypeCode()) { case type_string: case type_varstring: return LINK(type); } return makeVarStringType(type->getStringLen()); } //============================================================================ static ITypeInfo * getPromotedSet(ITypeInfo * left, ITypeInfo * right, bool isCompare) { ITypeInfo * leftChild = left; ITypeInfo * rightChild = right; if (left->getTypeCode() == type_set) leftChild = left->queryChildType(); if (right->getTypeCode() == type_set) rightChild = right->queryChildType(); if (leftChild && rightChild) return makeSetType(getPromotedType(leftChild, rightChild)); if (!leftChild) return LINK(right); return LINK(left); } static ITypeInfo * getPromotedUnicode(ITypeInfo * left, ITypeInfo * right) { unsigned lLen = left->getStringLen(); unsigned rLen = right->getStringLen(); if(lLen < rLen) lLen = rLen; return makeUnicodeType(lLen, getCommonLocale(left, right)); } static ITypeInfo * getPromotedVarUnicode(ITypeInfo * left, ITypeInfo * right) { unsigned lLen = left->getStringLen(); unsigned rLen = right->getStringLen(); if(lLen < rLen) lLen = rLen; return makeVarUnicodeType(lLen, getCommonLocale(left, right)); } static ITypeInfo * getPromotedUtf8(ITypeInfo * left, ITypeInfo * right) { unsigned lLen = left->getStringLen(); unsigned rLen = right->getStringLen(); if(lLen < rLen) lLen = rLen; return makeUtf8Type(lLen, getCommonLocale(left, right)); } static ITypeInfo * getPromotedVarString(ITypeInfo * left, ITypeInfo * right) { unsigned lLen = left->getStringLen(); unsigned rLen = right->getStringLen(); if (lLen < rLen) lLen = rLen; //MORE: Didn't this ought to have the charset logic of getPromotedString? return makeVarStringType(lLen); } static ITypeInfo * getPromotedString(ITypeInfo * left, ITypeInfo * right) { unsigned lLen = left->getStringLen(); unsigned rLen = right->getStringLen(); if (lLen < rLen) lLen = rLen; ICollationInfo * collation = left->queryCollation(); //MORE!! ICharsetInfo * lCharset = left->queryCharset(); ICharsetInfo * rCharset = right->queryCharset(); if (!lCharset || (rCharset && (lCharset != rCharset) && (rCharset->queryName() == asciiAtom))) { lCharset = rCharset; collation = right->queryCollation(); } return makeStringType(lLen, LINK(lCharset), LINK(collation)); } static ITypeInfo * getPromotedQString(ITypeInfo * left, ITypeInfo * right) { unsigned lLen = left->getStringLen(); unsigned rLen = right->getStringLen(); if (lLen < rLen) lLen = rLen; return makeQStringType(lLen); } static ITypeInfo * getPromotedData(ITypeInfo * left, ITypeInfo * right) { unsigned lLen = left->getStringLen(); unsigned rLen = right->getStringLen(); assertex(lLen != rLen); return makeDataType(UNKNOWN_LENGTH); } static ITypeInfo * makeUnknownLengthDecimal(bool isCompare) { if (isCompare) return makeDecimalType(UNKNOWN_LENGTH, UNKNOWN_LENGTH, true); return makeDecimalType(MAX_DECIMAL_DIGITS, MAX_DECIMAL_PRECISION, true); } static ITypeInfo * getPromotedDecimalReal(ITypeInfo * type, bool isCompare) { return makeUnknownLengthDecimal(isCompare); } static ITypeInfo * getPromotedDecimal(ITypeInfo * left, ITypeInfo * right, bool isCompare) { if (left->getTypeCode() == type_real) return getPromotedDecimalReal(right, isCompare); if (right->getTypeCode() == type_real) return getPromotedDecimalReal(left, isCompare); unsigned lDigits = left->getDigits(); unsigned rDigits = right->getDigits(); if (lDigits == UNKNOWN_LENGTH || rDigits == UNKNOWN_LENGTH) return makeDecimalType(UNKNOWN_LENGTH, UNKNOWN_LENGTH, left->isSigned() || right->isSigned()); if (isCompare) return makeUnknownLengthDecimal(isCompare); unsigned lPrec = left->getPrecision(); unsigned rPrec = right->getPrecision(); unsigned lLead = lDigits - lPrec; unsigned rLead = rDigits - rPrec; if (lLead < rLead) lLead = rLead; if (lPrec < rPrec) lPrec = rPrec; return makeDecimalType(lLead + lPrec, lPrec, left->isSigned() || right->isSigned()); } static ITypeInfo * getPromotedReal(ITypeInfo * left, ITypeInfo * right) { unsigned lDigits = left->getDigits(); unsigned rDigits = right->getDigits(); if (lDigits < rDigits) lDigits = rDigits; if (lDigits >= DOUBLE_SIG_DIGITS) return makeRealType(8); return makeRealType(4); } static void getPromotedIntegerSize(ITypeInfo * left, ITypeInfo * right, unsigned & pSize, bool & pSigned) { unsigned lSize = left->getSize(); unsigned rSize = right->getSize(); bool lSigned = left->isSigned(); bool rSigned = right->isSigned(); //This assumes it's an addition! For other operands rules are not necessarily the same! //Need to get the correct size and sign combination. //Try and preserve value whenever possible. //u1+s1=>s2 s1+u4=>s8 u2+u4=>u4 u1+s2=s2 b+s1->s1 b+u1=>u1 //Also needs to cope with integer6 etc... if (left->getTypeCode() == type_boolean) { lSigned = rSigned; lSize = rSize; } else if (right->getTypeCode() == type_boolean) { //don't need to do anything.... } else if (lSigned != rSigned) { if (lSigned) { if (lSize <= rSize) { lSize = promotedIntSize[rSize]; if ((lSize == rSize) && (lSize != 8)) lSize += lSize; } } else { lSigned = true; if (lSize < rSize) lSize = rSize; else { if (lSize != promotedIntSize[lSize]) lSize = promotedIntSize[lSize]; else if (lSize != 8) lSize += lSize; } } } else { if (lSize < rSize) lSize = rSize; } pSize = lSize; pSigned = lSigned; } static ITypeInfo * getPromotedInteger(ITypeInfo * left, ITypeInfo * right) { unsigned size; bool isSigned; getPromotedIntegerSize(left, right, size, isSigned); return makeIntType(size, isSigned); } static ITypeInfo * getPromotedSwapInteger(ITypeInfo * left, ITypeInfo * right) { unsigned size; bool isSigned; getPromotedIntegerSize(left, right, size, isSigned); return makeSwapIntType(size, isSigned); } static ITypeInfo * getPromotedPackedInteger(ITypeInfo * left, ITypeInfo * right) { unsigned size; bool isSigned; getPromotedIntegerSize(left, right, size, isSigned); return makePackedIntType(size, isSigned); } //============================================================================ static ITypeInfo * getPromotedType(ITypeInfo * lType, ITypeInfo * rType, bool isCompare) { ITypeInfo * l = lType->queryPromotedType(); ITypeInfo * r = rType->queryPromotedType(); if (l == r) return LINK(l); type_t lcode = l->getTypeCode(); type_t rcode = r->getTypeCode(); if ((lcode == type_any)) return LINK(r); if ((rcode == type_any)) return LINK(l); if ((lcode == type_set) || (rcode == type_set)) return getPromotedSet(l, r, isCompare); if ((lcode == type_unicode) || (rcode == type_unicode)) return getPromotedUnicode(l, r); if ((lcode == type_utf8) || (rcode == type_utf8)) return getPromotedUtf8(l, r); if ((lcode == type_varunicode) || (rcode == type_varunicode)) return getPromotedVarUnicode(l, r); if ((lcode == type_string) || (rcode == type_string)) return getPromotedString(l, r); if ((lcode == type_varstring) || (rcode == type_varstring)) return getPromotedVarString(l, r); if ((lcode == type_data) || (rcode == type_data)) return getPromotedData(l, r); if ((lcode == type_qstring) || (rcode == type_qstring)) return getPromotedQString(l, r); if ((lcode == type_decimal) || (rcode == type_decimal)) return getPromotedDecimal(l, r, isCompare); if ((lcode == type_real) || (rcode == type_real)) return getPromotedReal(l, r); if ((lcode == type_int) || (rcode == type_int)) return getPromotedInteger(l, r); if (lcode == type_boolean) return LINK(l); if (rcode == type_boolean) return LINK(r); if ((lcode == type_swapint) || (rcode == type_swapint)) return getPromotedSwapInteger(l, r); if ((lcode == type_packedint) || (rcode == type_packedint)) return getPromotedPackedInteger(l, r); //NB: Enumerations should come last... if (l->getSize() >= r->getSize()) return LINK(l); return LINK(r); //MORE(!) //return makeIntType(4); } ITypeInfo * getPromotedType(ITypeInfo * lType, ITypeInfo * rType) { return getPromotedType(lType, rType, false); } ITypeInfo * getPromotedNumericType(ITypeInfo * l_type, ITypeInfo * r_type) { Owned l = getNumericType(l_type->queryPromotedType()); Owned r = getNumericType(r_type->queryPromotedType()); return getPromotedType(l,r,false); } ITypeInfo * getPromotedAddSubType(ITypeInfo * lType, ITypeInfo * rType) { Owned ret = getPromotedNumericType(lType, rType); if (isDecimalType(ret) && !isUnknownSize(ret) && (ret->getDigits() - ret->getPrecision() < MAX_DECIMAL_LEADING)) return makeDecimalType(ret->getDigits()+1, ret->getPrecision(), ret->isSigned()); return ret.getClear(); } ITypeInfo * getPromotedMulDivType(ITypeInfo * lType, ITypeInfo * rType) { Owned ret = getPromotedNumericType(lType, rType); if (isDecimalType(ret) && !isUnknownSize(ret)) return makeUnknownLengthDecimal(false); return ret.getClear(); } ITypeInfo * getPromotedCompareType(ITypeInfo * left, ITypeInfo * right) { Owned promoted = getPromotedType(left, right, true); if (left != right) { type_t ptc = promoted->getTypeCode(); switch (ptc) { case type_string: { if ((left->getTypeCode() == ptc) && (right->getTypeCode() == ptc)) { if ((left->queryCollation() == right->queryCollation()) && (left->queryCharset() == right->queryCharset())) { promoted.setown(getStretchedType(UNKNOWN_LENGTH, left)); } } } break; case type_unicode: case type_utf8: { } break; } } return promoted.getClear(); } static bool preservesValue(ITypeInfo * after, ITypeInfo * before, bool preserveInformation) { type_t beforeType = before->getTypeCode(); type_t afterType = after->getTypeCode(); switch (beforeType) { case type_boolean: return true; case type_packedint: before = before->queryPromotedType(); //fall through case type_int: case type_swapint: case type_enumerated: switch (afterType) { case type_packedint: after = after->queryPromotedType(); //fall through. case type_int: case type_swapint: case type_enumerated: { bool beforeSigned = before->isSigned(); bool afterSigned = after->isSigned(); size32_t beforeSize = before->getSize(); size32_t afterSize = after->getSize(); if (preserveInformation && (beforeSize <= afterSize)) return true; // sign doesn't matter... return !((beforeSigned && !afterSigned) || (beforeSize > afterSize) || (!beforeSigned && afterSigned && (beforeSize == afterSize))); } case type_decimal: { bool beforeSigned = before->isSigned(); bool afterSigned = after->isSigned(); size32_t beforeSize, afterSize; beforeSize = before->getDigits(); afterSize = after->getDigits() - after->getPrecision(); return ((!beforeSigned || afterSigned) && (beforeSize <= afterSize)); } case type_real: return (before->getSize() < after->getSize()); // I think this is correct for all instances case type_string: case type_data: case type_varstring: case type_qstring: case type_unicode: case type_varunicode: case type_utf8: return (after->getDigits() >= before->getDigits()); } return false; case type_decimal: switch (afterType) { case type_decimal: { unsigned beforePrec = before->getPrecision(); unsigned afterPrec = after->getPrecision(); return (!before->isSigned() || after->isSigned()) && (beforePrec <= afterPrec) && (before->getDigits() - beforePrec <= after->getDigits() - afterPrec); } case type_real: return before->getDigits() < after->getDigits(); //NB: Not <= since real errs on over estimation case type_int: case type_swapint: case type_packedint: return ((before->getPrecision() == 0) && (after->getDigits() > before->getDigits()) && (!before->isSigned() || after->isSigned())); case type_string: case type_varstring: case type_data: case type_qstring: case type_unicode: case type_varunicode: case type_utf8: return (after->getStringLen() >= before->getStringLen()); } return false; case type_varstring: //casting from a var string may lose the length information, and so be irreversible. switch (afterType) { case type_varstring: case type_varunicode: return (before->getStringLen() <= after->getStringLen()); } return false; case type_string: case type_data: case type_qstring: switch (afterType) { case type_string: case type_data: case type_varstring: case type_unicode: case type_varunicode: case type_utf8: return (before->getStringLen() <= after->getStringLen()); case type_qstring: return (beforeType == type_qstring) && (before->getStringLen() <= after->getStringLen()); } return false; case type_set: if (afterType != type_set) return false; if (!before->queryChildType()) return true; if (!after->queryChildType()) return false; return preservesValue(after->queryChildType(), before->queryChildType()); case type_varunicode: switch (afterType) { case type_varunicode: return (before->getStringLen() <= after->getStringLen()); } return false; case type_unicode: case type_utf8: switch (afterType) { case type_unicode: case type_varunicode: case type_utf8: return (before->getStringLen() <= after->getStringLen()); } return false; default: return (beforeType == afterType) && (after->getSize() >= before->getSize()); } } bool preservesValue(ITypeInfo * after, ITypeInfo * before) { return preservesValue(after, before, false); } bool castLosesInformation(ITypeInfo * after, ITypeInfo * before) { return !preservesValue(after, before, true); } // should always call preservesValue first to determine if conversion is possible bool preservesOrder(ITypeInfo * after, ITypeInfo * before) { type_t beforeType = before->getTypeCode(); type_t afterType = after->getTypeCode(); switch (beforeType) { case type_boolean: return true; case type_decimal: switch (afterType) { case type_real: case type_decimal: return true; case type_int: case type_swapint: case type_packedint: return (before->getPrecision() == 0); } return false; case type_int: case type_swapint: case type_packedint: switch (afterType) { case type_int: case type_swapint: case type_real: case type_enumerated: case type_decimal: case type_packedint: return true; } return false; case type_string: case type_varstring: case type_data: case type_qstring: switch (afterType) { case type_string: case type_varstring: case type_data: case type_enumerated: return true; case type_qstring: return (beforeType == type_qstring); } return false; case type_enumerated: switch (afterType) { case type_string: case type_varstring: case type_data: case type_enumerated: case type_int: case type_swapint: case type_packedint: return true; } return false; default: return (beforeType == afterType); } } bool isSameBasicType(ITypeInfo * left, ITypeInfo * right) { if (!left || !right) return left==right; while (left->isReference()) left = left->queryTypeBase(); while (right->isReference()) right = right->queryTypeBase(); return queryUnqualifiedType(left)==queryUnqualifiedType(right); } extern DEFTYPE_API ITypeInfo * getRoundType(ITypeInfo * type) { if (type->getTypeCode() == type_decimal) { unsigned olddigits = type->getDigits(); if (olddigits == UNKNOWN_LENGTH) return LINK(type); //rounding could increase the number of digits by 1. unsigned newdigits = (olddigits - type->getPrecision())+1; if (newdigits > MAX_DECIMAL_LEADING) newdigits = MAX_DECIMAL_LEADING; return makeDecimalType(newdigits, 0, type->isSigned()); } return makeIntType(8, true); } extern DEFTYPE_API ITypeInfo * getRoundToType(ITypeInfo * type) { if (type->getTypeCode() == type_decimal) { unsigned olddigits = type->getDigits(); unsigned oldPrecision = type->getPrecision(); if ((olddigits == UNKNOWN_LENGTH) || (olddigits-oldPrecision == MAX_DECIMAL_LEADING)) return LINK(type); //rounding could increase the number of digits by 1. return makeDecimalType(olddigits+1, oldPrecision, type->isSigned()); } return makeRealType(8); } extern DEFTYPE_API ITypeInfo * getTruncType(ITypeInfo * type) { if (type->getTypeCode() == type_decimal) { unsigned olddigits = type->getDigits(); if (olddigits == UNKNOWN_LENGTH) return LINK(type); unsigned newdigits = (olddigits - type->getPrecision()); return makeDecimalType(newdigits, 0, type->isSigned()); } return makeIntType(8, true); } //--------------------------------------------------------------------------- CCharsetInfo::~CCharsetInfo() { ::Release(defaultCollation); } ICollationInfo * CCharsetInfo::queryDefaultCollation() { if (!defaultCollation) defaultCollation = getCollation(name); return defaultCollation; } CCollationInfo::~CCollationInfo() { } ICharsetInfo * CCollationInfo::getCharset() { return ::getCharset(name); } //--------------------------------------------------------------------------- CTranslationInfo::CTranslationInfo(_ATOM _name, ICharsetInfo * _src, ICharsetInfo * _tgt) : src(_src), tgt(_tgt) { name = _name; } _ATOM CTranslationInfo::queryName() { return name; } ICharsetInfo * CTranslationInfo::querySourceCharset() { return src; } ICharsetInfo * CTranslationInfo::queryTargetCharset() { return tgt; } //--------------------------------------------------------------------------- CAscii2EbcdicTranslationInfo::CAscii2EbcdicTranslationInfo() : CTranslationInfo(ascii2ebcdicAtom, getCharset(asciiAtom), getCharset(ebcdicAtom)) { } const char * CAscii2EbcdicTranslationInfo::queryRtlFunction() { return "ascii2ebcdic"; } const char * CAscii2EbcdicTranslationInfo::queryVarRtlFunction() { return "ascii2ebcdicX"; } StringBuffer & CAscii2EbcdicTranslationInfo::translate(StringBuffer & tgt, unsigned len, const char * src) { char * buf = (char*)malloc(len); rtlStrToEStr(len, buf, len, src); tgt.append(len, buf); free(buf); return tgt; } CEbcdic2AsciiTranslationInfo::CEbcdic2AsciiTranslationInfo() : CTranslationInfo(ebcdic2asciiAtom, getCharset(asciiAtom), getCharset(ebcdicAtom)) { } const char * CEbcdic2AsciiTranslationInfo::queryRtlFunction() { return "ebcdic2ascii"; } const char * CEbcdic2AsciiTranslationInfo::queryVarRtlFunction() { return "ebcdic2asciiX"; } StringBuffer & CEbcdic2AsciiTranslationInfo::translate(StringBuffer & tgt, unsigned len, const char * src) { char * buf = (char*)malloc(len); rtlEStrToStr(len, buf, len, src); tgt.append(len, buf); free(buf); return tgt; } //--------------------------------------------------------------------------- ICharsetInfo * getCharset(_ATOM atom) { if ((atom == NULL) || (atom == asciiAtom)) { if (!asciiCharset) asciiCharset = new CCharsetInfo(asciiAtom, 0x20, asciiCodepageAtom); return LINK(asciiCharset); } else if (atom == dataAtom) { if (!dataCharset) dataCharset = new CCharsetInfo(dataAtom, 0, asciiCodepageAtom); return LINK(dataCharset); } else if (atom == ebcdicAtom) { if (!ebcdicCharset) ebcdicCharset = new CCharsetInfo(ebcdicAtom, 0x40, ebcdicCodepageAtom); return LINK(ebcdicCharset); } else if (atom == utf8Atom) { if (!utf8Charset) utf8Charset = new CCharsetInfo(utf8Atom, 0x20, utf8Atom); return LINK(utf8Charset); } return NULL; } ICollationInfo * getCollation(_ATOM atom) { #if _DEBUG const char* name = atom->str(); #endif if ((atom == NULL) || (atom == asciiAtom) || (atom == dataAtom) || (atom == utf8Atom)) { if (!asciiCollation) asciiCollation = new CSimpleCollationInfo(asciiAtom); return LINK(asciiCollation); } else if (atom == ebcdicAtom) { if (!ebcdicCollation) ebcdicCollation = new CSimpleCollationInfo(ebcdicAtom); return LINK(ebcdicCollation); } return NULL; } ITranslationInfo * queryDefaultTranslation(ICharsetInfo * tgt, ICharsetInfo * src) { if ((src == asciiCharset) && (tgt == ebcdicCharset)) { if (!ascii2ebcdic) ascii2ebcdic = new CAscii2EbcdicTranslationInfo; return ascii2ebcdic; } if ((tgt == asciiCharset) && (src == ebcdicCharset)) { if (!ebcdic2ascii) ebcdic2ascii = new CEbcdic2AsciiTranslationInfo; return ebcdic2ascii; } return NULL; } ITranslationInfo * getDefaultTranslation(ICharsetInfo * tgt, ICharsetInfo * src) { ITranslationInfo *translator = queryDefaultTranslation(tgt, src); ::Link(translator); return translator; } //--------------------------------------------------------------------------- ITypeInfo * getStretchedType(unsigned newLen, ITypeInfo * type) { switch (type->getTypeCode()) { case type_string: return makeStringType(newLen, LINK(type->queryCharset()), LINK(type->queryCollation())); case type_varstring: return makeVarStringType(newLen, LINK(type->queryCharset()), LINK(type->queryCollation())); case type_unicode: return makeUnicodeType(newLen, type->queryLocale()); case type_varunicode: return makeVarUnicodeType(newLen, type->queryLocale()); case type_utf8: return makeUtf8Type(newLen, type->queryLocale()); case type_qstring: return makeQStringType(newLen); case type_data: return makeDataType(newLen); case type_int: return makeIntType(newLen, type->isSigned()); default: throw MakeStringException(99, "Internal error: getStretchedType"); } return NULL; } ITypeInfo * getAsciiType(ITypeInfo * type) { ICharsetInfo * charset = type->queryCharset(); if (charset && (charset->queryName() != asciiAtom)) { switch (type->getTypeCode()) { case type_string: return makeStringType(type->getSize(), NULL, NULL); case type_varstring: return makeVarStringType(type->getStringLen(), NULL, NULL); } } return LINK(type); } ITypeInfo * getBandType(ITypeInfo * lType, ITypeInfo * rType) { if (lType->isBoolean() && rType->isBoolean()) return LINK(lType); unsigned lSize = lType->getSize(); unsigned rSize = rType->getSize(); return makeIntType(std::min(lSize,rSize),lType->isSigned()&&rType->isSigned()); } ITypeInfo * getBorType(ITypeInfo * lType, ITypeInfo * rType) { if (lType->isBoolean() && rType->isBoolean()) return LINK(lType); unsigned lSize = lType->getSize(); unsigned rSize = rType->getSize(); return makeIntType(std::max(lSize,rSize),lType->isSigned()&&rType->isSigned()); } bool hasDefaultLocale(ITypeInfo * type) { return ((type->queryLocale() == 0) || (*type->queryLocale()->str() == 0)); } bool haveCommonLocale(ITypeInfo * type1, ITypeInfo * type2) { //for the moment, disallow binary ops unless locales identical or one is default --- may later change, e.g. to use common parent where present return ((type1->queryLocale() == type2->queryLocale()) || hasDefaultLocale(type1) || hasDefaultLocale(type2)); } _ATOM getCommonLocale(ITypeInfo * type1, ITypeInfo * type2) { //for the moment, disallow binary ops unless locales identical or one is default --- may later change, e.g. to use common parent where present if(!hasDefaultLocale(type1)) return type1->queryLocale(); return type2->queryLocale(); } bool isLittleEndian(ITypeInfo * type) { switch (type->getTypeCode()) { case type_packedint: return true; case type_int: #if __BYTE_ORDER == __LITTLE_ENDIAN return true; #else return false; #endif case type_swapint: #if __BYTE_ORDER == __LITTLE_ENDIAN return false; #else return true; #endif default: return true; } } inline ITypeInfo * queryChildType(ITypeInfo * t, type_t search) { while (t) { type_t code = t->getTypeCode(); if (code == search) return t; switch (code) { case type_set: case type_dictionary: case type_groupedtable: case type_row: case type_table: case type_rule: case type_transform: case type_function: case type_pointer: case type_array: t = t->queryChildType(); break; default: return NULL; } } return NULL; } ITypeInfo * queryRowType(ITypeInfo * t) { return queryChildType(t, type_row); } ITypeInfo * queryRecordType(ITypeInfo * t) { return queryChildType(t, type_record); } ITypeInfo * queryUnqualifiedType(ITypeInfo * t) { if (!t) return t; loop { ITypeInfo * base = t->queryTypeBase(); if (base == t) return t; t = base; } } ITypeInfo * getFullyUnqualifiedType(ITypeInfo * t) { if (!t) return t; loop { ITypeInfo * base = t->queryTypeBase(); if (base == t) { ITypeInfo * child = t->queryChildType(); if (!child) return LINK(t); Owned newChild = getFullyUnqualifiedType(child); return replaceChildType(t, newChild); } t = base; } } ITypeInfo * removeModifier(ITypeInfo * t, typemod_t modifier) { typemod_t curModifier = t->queryModifier(); if (curModifier == typemod_none) return LINK(t); ITypeInfo * base = t->queryTypeBase(); if (curModifier == modifier) return LINK(base); OwnedITypeInfo newBase = removeModifier(base, modifier); if (newBase == base) return LINK(t); return makeModifier(newBase.getClear(), curModifier, LINK(t->queryModifierExtra())); } bool hasModifier(ITypeInfo * t, typemod_t modifier) { loop { typemod_t curModifier = t->queryModifier(); if (curModifier == modifier) return true; if (curModifier == typemod_none) return false; t = t->queryTypeBase(); } } ITypeInfo * queryModifier(ITypeInfo * t, typemod_t modifier) { loop { typemod_t curModifier = t->queryModifier(); if (curModifier == modifier) return t; if (curModifier == typemod_none) return NULL; t = t->queryTypeBase(); } } ITypeInfo * cloneModifier(ITypeInfo * donorModifier, ITypeInfo * srcType) { typemod_t curModifier = donorModifier->queryModifier(); assertex(curModifier != typemod_none); return makeModifier(LINK(srcType), curModifier, LINK(donorModifier->queryModifierExtra())); } ITypeInfo * cloneModifiers(ITypeInfo * donorType, ITypeInfo * srcType) { typemod_t curModifier = donorType->queryModifier(); if (curModifier == typemod_none) return LINK(srcType); ITypeInfo * base = donorType->queryTypeBase(); return makeModifier(cloneModifiers(base, srcType), curModifier, LINK(donorType->queryModifierExtra())); } ITypeInfo * replaceChildType(ITypeInfo * type, ITypeInfo * newChild) { if (type->queryChildType() == newChild) return LINK(type); OwnedITypeInfo newType; switch (type->getTypeCode()) { case type_dictionary: newType.setown(makeDictionaryType(LINK(newChild))); break; case type_table: newType.setown(makeTableType(LINK(newChild), LINK(type->queryDistributeInfo()), LINK(type->queryGlobalSortInfo()), LINK(type->queryLocalUngroupedSortInfo()))); break; case type_groupedtable: newType.setown(makeGroupedTableType(LINK(newChild), LINK(type->queryGroupInfo()), LINK(type->queryGroupSortInfo()))); break; case type_row: newType.setown(makeRowType(LINK(newChild))); break; case type_set: newType.setown(makeSetType(LINK(newChild))); break; case type_transform: newType.setown(makeTransformType(LINK(newChild))); break; case type_sortlist: newType.setown(makeSortListType(LINK(newChild))); break; default: throwUnexpected(); } return cloneModifiers(type, newType); } //--------------------------------------------------------------------------- extern unsigned getClarionResultType(ITypeInfo *type) { if (type) { return type->getTypeCode() | (type->getSize() << 16) | (type->isInteger() && !type->isSigned() ? type_unsigned : 0) | (type->queryCharset() && type->queryCharset()->queryName()==ebcdicAtom ? type_ebcdic : 0); } else return 0; } //--------------------------------------------------------------------------- extern DEFTYPE_API ICharsetInfo * deserializeCharsetInfo(MemoryBuffer &src) { StringAttr name; src.read(name); return getCharset(createLowerCaseAtom(name)); } extern DEFTYPE_API ICollationInfo * deserializeCollationInfo(MemoryBuffer &src) { StringAttr name; src.read(name); return getCollation(createLowerCaseAtom(name)); } extern DEFTYPE_API ITypeInfo * deserializeType(MemoryBuffer &src) { unsigned char tc; src.read(tc); switch(tc) { case type_none: return NULL; case type_int: { unsigned char size; bool isSigned; src.read(size); src.read(isSigned); return makeIntType(size, isSigned); } case type_swapint: { unsigned char size; bool isSigned; src.read(size); src.read(isSigned); return makeSwapIntType(size, isSigned); } case type_packedint: { unsigned char size; bool isSigned; src.read(size); src.read(isSigned); return makePackedIntType(size, isSigned); } case type_char: { bool isCaseSensitive; src.read(isCaseSensitive); return makeCharType(isCaseSensitive); } case type_real: { unsigned char size; src.read(size); return makeRealType(size); } case type_boolean: return makeBoolType(); case type_blob: return makeBlobType(); case type_void: return makeVoidType(); case type_null: return makeNullType(); case type_pattern: return makePatternType(); case type_rule: { ITypeInfo *base = deserializeType(src); return makeRuleType(base); } case type_token: return makeTokenType(); case type_feature: return makeFeatureType(); case type_event: return makeEventType(); case type_string: case type_varstring: { size32_t size; bool b; src.read(size); src.read(b); ICollationInfo *collation = b ? deserializeCollationInfo(src) : NULL; src.read(b); ICharsetInfo *charset = b ? deserializeCharsetInfo(src) : NULL; if (tc==type_string) return makeStringType(size, charset, collation); else { if (size != UNKNOWN_LENGTH) size--; return makeVarStringType(size, charset, collation); } } case type_unicode: { size32_t size; StringAttr locale; src.read(size); src.read(locale); return makeUnicodeType(size, createLowerCaseAtom(locale.get())); } case type_varunicode: { size32_t size; StringAttr locale; src.read(size); src.read(locale); return makeVarUnicodeType(size, createLowerCaseAtom(locale.get())); } case type_utf8: { size32_t size; StringAttr locale; src.read(size); src.read(locale); return makeUtf8Type(size, createLowerCaseAtom(locale.get())); } case type_qstring: { size32_t size; src.read(size); return makeQStringType(size); } case type_data: { size32_t size; src.read(size); return makeDataType(size); } case type_decimal: { unsigned char prec, digits; bool isSigned; src.read(prec); src.read(digits); src.read(isSigned); unsigned fulldigits = (digits == CDecimalTypeInfo::UNKNOWN_DIGITS) ? UNKNOWN_LENGTH : digits; unsigned fullprec = (prec == CDecimalTypeInfo::UNKNOWN_DIGITS) ? UNKNOWN_LENGTH : prec; return makeDecimalType(fulldigits, fullprec, isSigned); } case type_bitfield: { int bitLength; src.read(bitLength); ITypeInfo *base = deserializeType(src); return makeBitfieldType(bitLength, base); } case type_set: { ITypeInfo *base = deserializeType(src); return makeSetType(base); } case type_pointer: { ITypeInfo *base = deserializeType(src); return makePointerType(base); } case type_array: { size32_t size; src.read(size); ITypeInfo *base = deserializeType(src); return makeArrayType(base, size); } case type_class: { StringAttr name; src.read(name); return makeClassType(name); } case type_record: return makeRecordType(); case type_table: { ITypeInfo *base = deserializeType(src); return makeTableType(makeRowType(base), NULL, NULL, NULL); } case type_groupedtable: { ITypeInfo *base = deserializeType(src); return makeGroupedTableType(base, NULL, NULL); } } assertex(false); return NULL; } void serializeType(MemoryBuffer &tgt, ITypeInfo * type) { if (type) type->serialize(tgt); else tgt.append((byte)type_none); } bool getNormalizedLocaleName(unsigned len, char const * str, StringBuffer & buff) { return rtlGetNormalizedUnicodeLocaleName(len, str, buff.reserve(len)); } //--------------------------------------------------------------------------- static bool alreadyHadSize(int size, IntArray &sizes) { ForEachItemIn(idx, sizes) { if (sizes.item(idx)==size) return true; } sizes.append(size); return false; } void XmlSchemaBuilder::addSchemaPrefix() { if (addHeader) xml.append(""); xml.append( "\n" "" "" "\n" "" "" "\n"); } void XmlSchemaBuilder::addSchemaSuffix() { xml.append( "" "" "\n" "" "" "\n"); xml.append(typesXml); xml.append(""); } void XmlSchemaBuilder::getXmlTypeName(StringBuffer & xmlType, ITypeInfo & type) { size32_t len = type.getStringLen(); switch (type.getTypeCode()) { case type_boolean: xmlType.append("xs:boolean"); break; case type_real: xmlType.append("xs:double"); break; case type_int: case type_swapint: case type_packedint: case type_bitfield: //MORE: Could generate different types depending on the size of the fields (e.g., long/unsignedLong) if (type.isSigned()) xmlType.append("xs:integer"); else xmlType.append("xs:nonNegativeInteger"); break; case type_data: if (len == UNKNOWN_LENGTH) xmlType.append("xs:hexBinary"); else { xmlType.append("data").append(len); if (!alreadyHadSize(len, dataSizes)) { typesXml.appendf( "" "" "" "" "", len, len).newline(); } } break; case type_decimal: type.getECLType(xmlType); len = type.getDigits()*255 + type.getPrecision(); if (!alreadyHadSize(len, decimalSizes)) { typesXml.append(""); typesXml.appendf("", type.getDigits()); typesXml.appendf("", type.getPrecision()); typesXml.append("").newline(); } break; case type_string: case type_qstring: case type_unicode: case type_varstring: case type_varunicode: case type_utf8: //NB: xs::maxLength is in unicode characters... if (len==UNKNOWN_LENGTH) xmlType.append("xs:string"); else { xmlType.append("string").append(len); if (!alreadyHadSize(len, stringSizes)) { typesXml.appendf("" "" "" "" "", len, len).newline(); } } break; case type_set: { StringBuffer elementName; getXmlTypeName(elementName, *type.queryChildType()); unsigned typeIndex = setTypes.find(type); if (typeIndex == NotFound) { typeIndex = setTypes.ordinality(); setTypes.append(type); typesXml.appendf("" "" "" "", elementName.str(), typeIndex, elementName.str()).newline(); } //%d is to ensure it is unique e.g., integers come back as the same xml type. xmlType.appendf("setof_%s_%d", elementName.str(), typeIndex); break; } default: UNIMPLEMENTED; } } void XmlSchemaBuilder::appendField(StringBuffer &s, const char * name, ITypeInfo & type) { const char * tag = name; if (*tag == '@') { s.append("\n"); } void XmlSchemaBuilder::addField(const char * name, ITypeInfo & type) { if (xml.length() == 0) addSchemaPrefix(); if (*name == '@') { if (attributes.length()) appendField(attributes.tos(), name, type); } else appendField(xml, name, type); } void XmlSchemaBuilder::addSetField(const char * name, const char * itemname, ITypeInfo & type) { if (xml.length() == 0) addSchemaPrefix(); StringBuffer elementType; getXmlTypeName(elementType, *type.queryChildType()); if (name && *name) { xml.append("").newline(); xml.append(""); // could use xs::choice instead xml.append("").newline(); } xml.append("").newline(); if (name && *name) xml.append("").newline(); } void XmlSchemaBuilder::beginRecord(const char * name) { if (!name || !*name) return; if (xml.length() == 0) addSchemaPrefix(); attributes.append(*new StringBufferItem); xml.append("").newline(); xml.append("").newline(); nesting.append(optionalNesting); optionalNesting = 0; } void XmlSchemaBuilder::endRecord(const char * name) { if (!name || !*name) return; xml.append("").newline(); xml.append(attributes.tos()); attributes.pop(); xml.append("").newline(); xml.append("").newline(); optionalNesting = nesting.pop(); } bool XmlSchemaBuilder::beginDataset(const char * name, const char * row) { if (xml.length() == 0) addSchemaPrefix(); if (name && *name) { xml.append("").newline(); xml.append("").newline(); } xml.append("").newline(); if (row && *row) { attributes.append(*new StringBufferItem); xml.append("").newline(); } nesting.append(optionalNesting); optionalNesting = 0; return true; } void XmlSchemaBuilder::endDataset(const char * name, const char * row) { if (row && *row) { xml.append("").newline(); xml.append(attributes.tos()); attributes.pop(); xml.append("").newline(); } xml.append("").newline(); if (name && *name) { xml.append("").newline(); xml.append("").newline(); } optionalNesting = nesting.pop(); } bool XmlSchemaBuilder::addSingleFieldDataset(const char * name, const char * childname, ITypeInfo & type) { if (xml.length() == 0) addSchemaPrefix(); if (name && *name) { xml.append("").newline(); xml.append("").newline(); } xml.append("").newline(); addField(childname, type); xml.append("").newline(); if (name && *name) { xml.append("").newline(); xml.append("").newline(); } return true; } void XmlSchemaBuilder::clear() { xml.clear(); dataSizes.kill(); stringSizes.kill(); decimalSizes.kill(); nesting.kill(); optionalNesting = 0; } void XmlSchemaBuilder::getXml(StringBuffer & results) { if (xml.length() != 0) { addSchemaSuffix(); results.append(xml); clear(); } } void XmlSchemaBuilder::getXml(IStringVal & results) { if (xml.length() != 0) { addSchemaSuffix(); results.set(xml); clear(); } else results.clear(); }