summaryrefslogtreecommitdiffstats
path: root/kexi/kexidb/field.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kexi/kexidb/field.cpp')
-rw-r--r--kexi/kexidb/field.cpp726
1 files changed, 726 insertions, 0 deletions
diff --git a/kexi/kexidb/field.cpp b/kexi/kexidb/field.cpp
new file mode 100644
index 00000000..88233272
--- /dev/null
+++ b/kexi/kexidb/field.cpp
@@ -0,0 +1,726 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Lucijan Busch <[email protected]>
+ Copyright (C) 2002 Joseph Wenninger <[email protected]>
+ Copyright (C) 2003-2006 Jaroslaw Staniek <[email protected]>
+
+ 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 <kdebug.h>
+#include <klocale.h>
+
+#include <qdatetime.h>
+
+#include <assert.h>
+
+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 QString& name, Type ctype,
+ uint cconst, uint options, uint length, uint precision,
+ QVariant defaultValue, const QString& caption, const QString& 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 = QVariant(QString::null);
+ 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;
+}
+
+QVariant::Type Field::variantType(uint type)
+{
+ switch(type)
+ {
+ case Byte:
+ case ShortInteger:
+ case Integer:
+ case BigInteger:
+ return QVariant::Int;
+ case Boolean:
+ return QVariant::Bool;
+ case Date:
+ return QVariant::Date;
+ case DateTime:
+ return QVariant::DateTime;
+ case Time:
+ return QVariant::Time;
+ case Float:
+ case Double:
+ return QVariant::Double;
+ case Text:
+ case LongText:
+ return QVariant::String;
+ case BLOB:
+ return QVariant::ByteArray;
+ default:
+ return QVariant::Invalid;
+ }
+
+ return QVariant::Invalid;
+}
+
+QString Field::typeName(uint type)
+{
+ m_typeNames.init();
+ return (type <= LastType) ? m_typeNames.at(type) : QString::number(type);
+}
+
+QString Field::typeString(uint type)
+{
+ m_typeNames.init();
+ return (type <= LastType) ? m_typeNames.at((int)LastType+1 + type) : QString("Type%1").arg(type);
+}
+
+QString Field::typeGroupName(uint typeGroup)
+{
+ m_typeGroupNames.init();
+ return (typeGroup <= LastTypeGroup) ? m_typeGroupNames.at(typeGroup) : typeGroupString(typeGroup);
+}
+
+QString Field::typeGroupString(uint typeGroup)
+{
+ m_typeGroupNames.init();
+ return (typeGroup <= LastTypeGroup) ? m_typeGroupNames.at((int)LastTypeGroup+1 + typeGroup) : QString("TypeGroup%1").arg(typeGroup);
+}
+
+Field::Type Field::typeForString(const QString& typeString)
+{
+ m_typeNames.init();
+ QMap<QString,Type>::ConstIterator it = m_typeNames.str2num.find(typeString.lower());
+ if (it==m_typeNames.str2num.end())
+ return InvalidType;
+ return it.data();
+}
+
+Field::TypeGroup Field::typeGroupForString(const QString& typeGroupString)
+{
+ m_typeGroupNames.init();
+ QMap<QString,TypeGroup>::ConstIterator it = m_typeGroupNames.str2num.find(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<TableSchema*>(m_parent);
+}
+
+void
+Field::setTable(TableSchema *tableSchema)
+{
+ m_parent = tableSchema;
+}
+
+QuerySchema*
+Field::query() const
+{
+ return dynamic_cast<QuerySchema*>(m_parent);
+}
+
+void
+Field::setQuery(QuerySchema *querySchema)
+{
+ m_parent = querySchema;
+}
+
+void
+Field::setName(const QString& n)
+{
+ m_name = n.lower();
+}
+
+void
+Field::setType(Type t)
+{
+ if (m_expr) {
+ KexiDBWarn << QString("Field::setType(%1)").arg(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 QVariant& def)
+{
+ m_defaultValue = def;
+}
+
+bool
+Field::setDefaultValue(const QCString& def)
+{
+ if (def.isNull()) {
+ m_defaultValue = QVariant();
+ return true;
+ }
+
+ bool ok;
+ switch(type())
+ {
+ case Byte: {
+ unsigned int v = def.toUInt(&ok);
+ if (!ok || v > 255)
+ m_defaultValue = QVariant();
+ else
+ m_defaultValue = QVariant(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 = QVariant();
+ else
+ m_defaultValue = QVariant(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 = QVariant();
+ else
+ m_defaultValue = QVariant((Q_LLONG)v);
+ break;
+ }case BigInteger: {//8 bytes
+//! @todo BigInteger support
+/*
+ Q_LLONG long v = def.toLongLong(&ok);
+//TODO: 2-part decoding
+ if (!ok || (!(m_options & Unsigned) && (-v > 0x080000000 || v > (0x080000000-1))))
+ m_defaultValue = QVariant();
+ else
+ if (m_options & Unsigned)
+ m_defaultValue=QVariant((Q_ULLONG) v);
+ else
+ m_defaultValue = QVariant((Q_LLONG)v);*/
+ break;
+ }case Boolean: {
+ unsigned short v = def.toUShort(&ok);
+ if (!ok || v > 1)
+ m_defaultValue = QVariant();
+ else
+ m_defaultValue = QVariant((bool)v);
+ break;
+ }case Date: {//YYYY-MM-DD
+ QDate date = QDate::fromString( def, Qt::ISODate );
+ if (!date.isValid())
+ m_defaultValue = QVariant();
+ else
+ m_defaultValue = QVariant(date);
+ break;
+ }case DateTime: {//YYYY-MM-DDTHH:MM:SS
+ QDateTime dt = QDateTime::fromString( def, Qt::ISODate );
+ if (!dt.isValid())
+ m_defaultValue = QVariant();
+ else
+ m_defaultValue = QVariant(dt);
+ break;
+ }case Time: {//HH:MM:SS
+ QTime time = QTime::fromString( def, Qt::ISODate );
+ if (!time.isValid())
+ m_defaultValue = QVariant();
+ else
+ m_defaultValue = QVariant(time);
+ break;
+ }case Float: {
+ float v = def.toFloat(&ok);
+ if (!ok || ((m_options & Unsigned) && (v < 0.0)))
+ m_defaultValue = QVariant();
+ else
+ m_defaultValue = QVariant(v);
+ break;
+ }case Double: {
+ double v = def.toDouble(&ok);
+ if (!ok || ((m_options & Unsigned) && (v < 0.0)))
+ m_defaultValue = QVariant();
+ else
+ m_defaultValue = QVariant(v);
+ break;
+ }case Text: {
+ if (def.isNull() || (def.length() > 255))
+ m_defaultValue = QVariant();
+ else
+ m_defaultValue = QVariant((QString)def);
+ break;
+ }case LongText: {
+ if (def.isNull())
+ m_defaultValue = QVariant();
+ else
+ m_defaultValue = QVariant((QString)def);
+ break;
+ }case BLOB: {
+//TODO
+ if (def.isNull())
+ m_defaultValue = QVariant();
+ else
+ m_defaultValue = QVariant(def);
+ break;
+ }default:
+ m_defaultValue = QVariant();
+ }
+ return m_defaultValue.isNull();
+}
+
+void
+Field::setAutoIncrement(bool a)
+{
+ if (a && !isAutoIncrementAllowed())
+ return;
+ if (isAutoIncrement() != a)
+ m_constraints = static_cast<Field::Constraints>(m_constraints ^ Field::AutoInc);
+}
+
+void
+Field::setPrimaryKey(bool p)
+{
+ if(isPrimaryKey() != p)
+ m_constraints = static_cast<Field::Constraints>(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<Field::Constraints>(m_constraints ^ Field::Unique);
+ if (u)
+ setNotNull(true);
+ }
+}
+
+void
+Field::setForeignKey(bool f)
+{
+ if (isForeignKey() != f)
+ m_constraints = static_cast<Field::Constraints>(m_constraints ^ Field::ForeignKey);
+}
+
+void
+Field::setNotNull(bool n)
+{
+ if (isNotNull() != n)
+ m_constraints = static_cast<Field::Constraints>(m_constraints ^ Field::NotNull);
+}
+
+void Field::setNotEmpty(bool n)
+{
+ if (isNotEmpty() != n)
+ m_constraints = static_cast<Field::Constraints>(m_constraints ^ Field::NotEmpty);
+}
+
+void Field::setIndexed(bool s)
+{
+ if (isIndexed() != s)
+ m_constraints = static_cast<Field::Constraints>(m_constraints ^ Field::Indexed);
+ if (!s) {//also set implied constraints
+ setPrimaryKey(false);
+ setUniqueKey(false);
+ setNotNull(false);
+ setNotEmpty(false);
+ }
+}
+
+
+QString Field::debugString() const
+{
+ KexiDB::Connection *conn = table() ? table()->connection() : 0;
+ QString dbg = (m_name.isEmpty() ? "<NONAME> " : 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 += QString::fromLatin1("(%1,%2)").arg(m_precision).arg(scale());
+ else
+ dbg += QString::fromLatin1("(%1)").arg(m_precision);
+ }
+ else if (m_type==Field::Text && m_length>0)
+ dbg += QString::fromLatin1("(%1)").arg(m_length);
+ if (m_constraints & Field::AutoInc)
+ dbg += " AUTOINC";
+ if (m_constraints & Field::Unique)
+ dbg += " UNIQUE";
+ 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 += QString(" DEFAULT=[%1]").arg(m_defaultValue.typeName()) + KexiDB::variantToString(m_defaultValue);
+ if (m_expr)
+ dbg += " EXPRESSION=" + m_expr->debugString();
+ if (m_customProperties && !m_customProperties->isEmpty()) {
+ dbg += QString(" CUSTOM PROPERTIES (%1): ").arg(m_customProperties->count());
+ bool first = true;
+ foreach (CustomPropertiesMap::ConstIterator, it, *m_customProperties) {
+ if (first)
+ first = false;
+ else
+ dbg += ", ";
+ dbg += QString("%1 = %2 (%3)").arg(it.key()).arg(it.data().toString()).arg(it.data().typeName());
+ }
+ }
+ return dbg;
+}
+
+void Field::debug()
+{
+ KexiDBDbg << debugString() << endl;
+}
+
+void Field::setExpression(KexiDB::BaseExpr *expr)
+{
+ assert(!m_parent || dynamic_cast<QuerySchema*>(m_parent));
+ if (m_expr==expr)
+ return;
+ if (m_expr) {
+ delete m_expr;
+ }
+ m_expr = expr;
+}
+
+QVariant Field::customProperty(const QCString& propertyName,
+ const QVariant& defaultValue) const
+{
+ if (!m_customProperties)
+ return defaultValue;
+ CustomPropertiesMap::ConstIterator it(m_customProperties->find(propertyName));
+ if (it==m_customProperties->constEnd())
+ return defaultValue;
+ return it.data();
+}
+
+void Field::setCustomProperty(const QCString& propertyName, const QVariant& 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(QString::fromLatin1(str).lower(), type)
+#define ADDGROUP(type, i18, str) this->at(Field::type) = i18; \
+ this->at(Field::type+Field::LastTypeGroup+1) = str; \
+ str2num.insert(QString::fromLatin1(str).lower(), type)
+
+Field::FieldTypeNames::FieldTypeNames()
+ : QValueVector<QString>()
+ , 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()
+ : QValueVector<QString>()
+ , 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" );
+}
+
+//-------------------------------------------------------
+