/* This file is part of the KDE project Copyright (C) 2002 Lucijan Busch Copyright (C) 2002 Joseph Wenninger Copyright (C) 2003-2006 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "field.h" #include "connection.h" #include "driver.h" #include "expression.h" #include "utils.h" // we use here i18n() but this depends on kde libs: TODO: add #ifdefs #include #include #include #include using namespace KexiDB; Field::FieldTypeNames Field::m_typeNames; Field::FieldTypeGroupNames Field::m_typeGroupNames; Field::Field() { init(); setConstraints(NoConstraints); } Field::Field(TableSchema *tableSchema) { init(); m_parent = tableSchema; m_order = tableSchema->fieldCount(); setConstraints(NoConstraints); } Field::Field(QuerySchema *querySchema, BaseExpr* expr) { init(); m_parent = querySchema; m_order = querySchema->fieldCount(); setConstraints(NoConstraints); if (expr) setExpression(expr); } Field::Field(const TQString& name, Type ctype, uint cconst, uint options, uint length, uint precision, TQVariant defaultValue, const TQString& caption, const TQString& description, uint width) : m_parent(0) ,m_name(name.lower()) ,m_length(length) ,m_precision(precision) ,m_visibleDecimalPlaces(-1) ,m_options(options) ,m_defaultValue(defaultValue) ,m_order(-1) ,m_caption(caption) ,m_desc(description) ,m_width(width) ,m_expr(0) ,m_customProperties(0) ,m_type(ctype) { setConstraints(cconst); if (m_length==0) {//0 means default length: if (m_type==Field::Text) m_length = defaultTextLength(); } } /*! Copy constructor. */ Field::Field(const Field& f) { (*this) = f; if (f.m_customProperties) m_customProperties = new CustomPropertiesMap( f.customProperties() ); if (f.m_expr) {//deep copy the expression //TODO m_expr = new BaseExpr(*f.m_expr); // m_expr->m_field = this; } else m_expr = 0; } Field::~Field() { delete m_expr; delete m_customProperties; } Field* Field::copy() const { return new Field(*this); } void Field::init() { m_parent = 0; m_name = ""; m_type = InvalidType; m_length = 0; m_precision = 0; m_visibleDecimalPlaces = -1; m_options = NoOptions; m_defaultValue = TQVariant(TQString()); m_order = -1; m_width = 0; m_expr = 0; m_customProperties = 0; } Field::Type Field::type() const { if (m_expr) return m_expr->type(); return m_type; } TQVariant::Type Field::variantType(uint type) { switch(type) { case Byte: case ShortInteger: case Integer: case BigInteger: return TQVariant::Int; case Boolean: return TQVariant::Bool; case Date: return TQVariant::Date; case DateTime: return TQVariant::DateTime; case Time: return TQVariant::Time; case Float: case Double: return TQVariant::Double; case Text: case LongText: return TQVariant::String; case BLOB: return TQVariant::ByteArray; default: return TQVariant::Invalid; } return TQVariant::Invalid; } TQString Field::typeName(uint type) { m_typeNames.init(); return (type <= LastType) ? m_typeNames.at(type) : TQString::number(type); } TQString Field::typeString(uint type) { m_typeNames.init(); return (type <= LastType) ? m_typeNames.at((int)LastType+1 + type) : TQString("Type%1").tqarg(type); } TQString Field::typeGroupName(uint typeGroup) { m_typeGroupNames.init(); return (typeGroup <= LastTypeGroup) ? m_typeGroupNames.at(typeGroup) : typeGroupString(typeGroup); } TQString Field::typeGroupString(uint typeGroup) { m_typeGroupNames.init(); return (typeGroup <= LastTypeGroup) ? m_typeGroupNames.at((int)LastTypeGroup+1 + typeGroup) : TQString("TypeGroup%1").tqarg(typeGroup); } Field::Type Field::typeForString(const TQString& typeString) { m_typeNames.init(); TQMap::ConstIterator it = m_typeNames.str2num.tqfind(typeString.lower()); if (it==m_typeNames.str2num.end()) return InvalidType; return it.data(); } Field::TypeGroup Field::typeGroupForString(const TQString& typeGroupString) { m_typeGroupNames.init(); TQMap::ConstIterator it = m_typeGroupNames.str2num.tqfind(typeGroupString.lower()); if (it==m_typeGroupNames.str2num.end()) return InvalidGroup; return it.data(); } bool Field::isIntegerType( uint type ) { switch (type) { case Field::Byte: case Field::ShortInteger: case Field::Integer: case Field::BigInteger: return true; default:; } return false; } bool Field::isNumericType( uint type ) { switch (type) { case Field::Byte: case Field::ShortInteger: case Field::Integer: case Field::BigInteger: case Field::Float: case Field::Double: return true; default:; } return false; } bool Field::isFPNumericType( uint type ) { return type==Field::Float || type==Field::Double; } bool Field::isDateTimeType(uint type) { switch (type) { case Field::Date: case Field::DateTime: case Field::Time: return true; default:; } return false; } bool Field::isTextType( uint type ) { switch (type) { case Field::Text: case Field::LongText: return true; default:; } return false; } bool Field::hasEmptyProperty(uint type) { return Field::isTextType(type) || type==BLOB; } bool Field::isAutoIncrementAllowed(uint type) { return Field::isIntegerType(type); } Field::TypeGroup Field::typeGroup(uint type) { if (Field::isTextType(type)) return TextGroup; else if (Field::isIntegerType(type)) return IntegerGroup; else if (Field::isFPNumericType(type)) return FloatGroup; else if (type==Boolean) return BooleanGroup; else if (Field::isDateTimeType(type)) return DateTimeGroup; else if (type==BLOB) return BLOBGroup; return InvalidGroup; //unknown } TableSchema* Field::table() const { return dynamic_cast(m_parent); } void Field::setTable(TableSchema *tableSchema) { m_parent = tableSchema; } QuerySchema* Field::query() const { return dynamic_cast(m_parent); } void Field::setQuery(QuerySchema *querySchema) { m_parent = querySchema; } void Field::setName(const TQString& n) { m_name = n.lower(); } void Field::setType(Type t) { if (m_expr) { KexiDBWarn << TQString("Field::setType(%1)").tqarg(t) << " could not set type because the field has expression assigned!" << endl; return; } m_type = t; } void Field::setConstraints(uint c) { m_constraints = c; //pkey must be unique notnull if (isPrimaryKey()) { setPrimaryKey(true); } if (isIndexed()) { setIndexed(true); } if (isAutoIncrement() && !isAutoIncrementAllowed()) { setAutoIncrement(false); } } void Field::setLength(uint l) { if (type()!=Field::Text) return; m_length = l; } void Field::setPrecision(uint p) { if (!isFPNumericType()) return; m_precision = p; } void Field::setScale(uint s) { if (!isFPNumericType()) return; m_length = s; } void Field::setVisibleDecimalPlaces(int p) { if (!KexiDB::supportsVisibleDecimalPlacesProperty(type())) return; m_visibleDecimalPlaces = p < 0 ? -1 : p; } void Field::setUnsigned(bool u) { m_options |= Unsigned; m_options ^= (!u * Unsigned); } void Field::setDefaultValue(const TQVariant& def) { m_defaultValue = def; } bool Field::setDefaultValue(const TQCString& def) { if (def.isNull()) { m_defaultValue = TQVariant(); return true; } bool ok; switch(type()) { case Byte: { unsigned int v = def.toUInt(&ok); if (!ok || v > 255) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant(v); break; }case ShortInteger: { int v = def.toInt(&ok); if (!ok || (!(m_options & Unsigned) && (v < -32768 || v > 32767)) || ((m_options & Unsigned) && (v < 0 || v > 65535))) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant(v); break; }case Integer: {//4 bytes long v = def.toLong(&ok); //js: FIXME if (!ok || (!(m_options & Unsigned) && (-v > 0x080000000 || v > (0x080000000-1))) || ((m_options & Unsigned) && (v < 0 || v > 0x100000000))) if (!ok || (!(m_options & Unsigned) && (-v > (int)0x07FFFFFFF || v > (int)(0x080000000-1)))) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant((TQ_LLONG)v); break; }case BigInteger: {//8 bytes //! @todo BigInteger support /* TQ_LLONG long v = def.toLongLong(&ok); //TODO: 2-part decoding if (!ok || (!(m_options & Unsigned) && (-v > 0x080000000 || v > (0x080000000-1)))) m_defaultValue = TQVariant(); else if (m_options & Unsigned) m_defaultValue=TQVariant((TQ_ULLONG) v); else m_defaultValue = TQVariant((TQ_LLONG)v);*/ break; }case Boolean: { unsigned short v = def.toUShort(&ok); if (!ok || v > 1) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant((bool)v); break; }case Date: {//YYYY-MM-DD TQDate date = TQDate::fromString( def, Qt::ISODate ); if (!date.isValid()) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant(date); break; }case DateTime: {//YYYY-MM-DDTHH:MM:SS TQDateTime dt = TQDateTime::fromString( def, Qt::ISODate ); if (!dt.isValid()) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant(dt); break; }case Time: {//HH:MM:SS TQTime time = TQTime::fromString( def, Qt::ISODate ); if (!time.isValid()) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant(time); break; }case Float: { float v = def.toFloat(&ok); if (!ok || ((m_options & Unsigned) && (v < 0.0))) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant(v); break; }case Double: { double v = def.toDouble(&ok); if (!ok || ((m_options & Unsigned) && (v < 0.0))) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant(v); break; }case Text: { if (def.isNull() || (def.length() > 255)) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant((TQString)def); break; }case LongText: { if (def.isNull()) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant((TQString)def); break; }case BLOB: { //TODO if (def.isNull()) m_defaultValue = TQVariant(); else m_defaultValue = TQVariant(def); break; }default: m_defaultValue = TQVariant(); } return m_defaultValue.isNull(); } void Field::setAutoIncrement(bool a) { if (a && !isAutoIncrementAllowed()) return; if (isAutoIncrement() != a) m_constraints = static_cast(m_constraints ^ Field::AutoInc); } void Field::setPrimaryKey(bool p) { if(isPrimaryKey() != p) m_constraints = static_cast(m_constraints ^ Field::PrimaryKey); if (p) {//also set implied constraints setUniqueKey(true); setNotNull(true); setNotEmpty(true); setIndexed(true); } else { //! \todo is this ok for all engines? setAutoIncrement(false); } } void Field::setUniqueKey(bool u) { if(isUniqueKey() != u) { m_constraints = static_cast(m_constraints ^ Field::Unique); if (u) setNotNull(true); } } void Field::setForeignKey(bool f) { if (isForeignKey() != f) m_constraints = static_cast(m_constraints ^ Field::ForeignKey); } void Field::setNotNull(bool n) { if (isNotNull() != n) m_constraints = static_cast(m_constraints ^ Field::NotNull); } void Field::setNotEmpty(bool n) { if (isNotEmpty() != n) m_constraints = static_cast(m_constraints ^ Field::NotEmpty); } void Field::setIndexed(bool s) { if (isIndexed() != s) m_constraints = static_cast(m_constraints ^ Field::Indexed); if (!s) {//also set implied constraints setPrimaryKey(false); setUniqueKey(false); setNotNull(false); setNotEmpty(false); } } TQString Field::debugString() const { KexiDB::Connection *conn = table() ? table()->connection() : 0; TQString dbg = (m_name.isEmpty() ? " " : m_name + " "); if (m_options & Field::Unsigned) dbg += " UNSIGNED "; dbg += (conn && conn->driver()) ? conn->driver()->sqlTypeName(type()) : Driver::defaultSQLTypeName(type()); if (isFPNumericType() && m_precision>0) { if (scale()>0) dbg += TQString::tqfromLatin1("(%1,%2)").tqarg(m_precision).tqarg(scale()); else dbg += TQString::tqfromLatin1("(%1)").tqarg(m_precision); } else if (m_type==Field::Text && m_length>0) dbg += TQString::tqfromLatin1("(%1)").tqarg(m_length); if (m_constraints & Field::AutoInc) dbg += " AUTOINC"; if (m_constraints & Field::Unique) dbg += " UNITQUE"; if (m_constraints & Field::PrimaryKey) dbg += " PKEY"; if (m_constraints & Field::ForeignKey) dbg += " FKEY"; if (m_constraints & Field::NotNull) dbg += " NOTNULL"; if (m_constraints & Field::NotEmpty) dbg += " NOTEMPTY"; if (!m_defaultValue.isNull()) dbg += TQString(" DEFAULT=[%1]").tqarg(m_defaultValue.typeName()) + KexiDB::variantToString(m_defaultValue); if (m_expr) dbg += " EXPRESSION=" + m_expr->debugString(); if (m_customProperties && !m_customProperties->isEmpty()) { dbg += TQString(" CUSTOM PROPERTIES (%1): ").tqarg(m_customProperties->count()); bool first = true; foreach (CustomPropertiesMap::ConstIterator, it, *m_customProperties) { if (first) first = false; else dbg += ", "; dbg += TQString("%1 = %2 (%3)").tqarg(TQString(it.key())).tqarg(TQString(it.data().toString())).tqarg(TQString(it.data().typeName())); } } return dbg; } void Field::debug() { KexiDBDbg << debugString() << endl; } void Field::setExpression(KexiDB::BaseExpr *expr) { assert(!m_parent || dynamic_cast(m_parent)); if (m_expr==expr) return; if (m_expr) { delete m_expr; } m_expr = expr; } TQVariant Field::customProperty(const TQCString& propertyName, const TQVariant& defaultValue) const { if (!m_customProperties) return defaultValue; CustomPropertiesMap::ConstIterator it(m_customProperties->tqfind(propertyName)); if (it==m_customProperties->constEnd()) return defaultValue; return it.data(); } void Field::setCustomProperty(const TQCString& propertyName, const TQVariant& value) { if (propertyName.isEmpty()) return; if (!m_customProperties) m_customProperties = new CustomPropertiesMap(); m_customProperties->insert(propertyName, value); } //------------------------------------------------------- #define ADDTYPE(type, i18, str) this->at(Field::type) = i18; \ this->at(Field::type+Field::LastType+1) = str; \ str2num.insert(TQString::tqfromLatin1(str).lower(), type) #define ADDGROUP(type, i18, str) this->at(Field::type) = i18; \ this->at(Field::type+Field::LastTypeGroup+1) = str; \ str2num.insert(TQString::tqfromLatin1(str).lower(), type) Field::FieldTypeNames::FieldTypeNames() : TQValueVector() , m_initialized(false) { } void Field::FieldTypeNames::init() { if (m_initialized) return; m_initialized = true; resize((Field::LastType + 1)*2); ADDTYPE( InvalidType, i18n("Invalid Type"), "InvalidType" ); ADDTYPE( Byte, i18n("Byte"), "Byte" ); ADDTYPE( ShortInteger, i18n("Short Integer Number"), "ShortInteger" ); ADDTYPE( Integer, i18n("Integer Number"), "Integer" ); ADDTYPE( BigInteger, i18n("Big Integer Number"), "BigInteger" ); ADDTYPE( Boolean, i18n("Yes/No Value"), "Boolean" ); ADDTYPE( Date, i18n("Date"), "Date" ); ADDTYPE( DateTime, i18n("Date and Time"), "DateTime" ); ADDTYPE( Time, i18n("Time"), "Time" ); ADDTYPE( Float, i18n("Single Precision Number"), "Float" ); ADDTYPE( Double, i18n("Double Precision Number"), "Double" ); ADDTYPE( Text, i18n("Text"), "Text" ); ADDTYPE( LongText, i18n("Long Text"), "LongText" ); ADDTYPE( BLOB, i18n("Object"), "BLOB" ); } //------------------------------------------------------- Field::FieldTypeGroupNames::FieldTypeGroupNames() : TQValueVector() , m_initialized(false) { } void Field::FieldTypeGroupNames::init() { if (m_initialized) return; m_initialized = true; resize((Field::LastTypeGroup + 1)*2); ADDGROUP( InvalidGroup, i18n("Invalid Group"), "InvalidGroup" ); ADDGROUP( TextGroup, i18n("Text"), "TextGroup" ); ADDGROUP( IntegerGroup, i18n("Integer Number"), "IntegerGroup" ); ADDGROUP( FloatGroup, i18n("Floating Point Number"), "FloatGroup" ); ADDGROUP( BooleanGroup, i18n("Yes/No"), "BooleanGroup" ); ADDGROUP( DateTimeGroup, i18n("Date/Time"), "DateTimeGroup" ); ADDGROUP( BLOBGroup, i18n("Object"), "BLOBGroup" ); } //-------------------------------------------------------