You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kexi/kexidb/field.cpp

727 lines
17 KiB

/* This file is part of the KDE project
Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
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 <tqdatetime.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 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<TQString,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 TQString& typeGroupString)
{
m_typeGroupNames.init();
TQMap<TQString,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 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<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);
}
}
TQString Field::debugString() const
{
KexiDB::Connection *conn = table() ? table()->connection() : 0;
TQString 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 += 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<QuerySchema*>(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->find(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<TQString>()
, 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<TQString>()
, 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" );
}
//-------------------------------------------------------