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/tableschema.cpp

454 lines
10 KiB

/* This file is part of the KDE project
Copyright (C) 2003 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 "tableschema.h"
#include "driver.h"
#include "connection.h"
#include "lookupfieldschema.h"
#include <assert.h>
#include <kdebug.h>
namespace KexiDB {
//! @internal
class TableSchema::Private
{
public:
Private()
: anyNonPKField(0)
{
}
~Private()
{
clearLookupFields();
}
void clearLookupFields()
{
for (TQMap<const Field*, LookupFieldSchema*>::ConstIterator it = lookupFields.constBegin();
it!=lookupFields.constEnd(); ++it)
{
delete it.data();
}
lookupFields.clear();
}
Field *anyNonPKField;
TQMap<const Field*, LookupFieldSchema*> lookupFields;
TQPtrVector<LookupFieldSchema> lookupFieldsList;
};
}
//-------------------------------------
using namespace KexiDB;
TableSchema::TableSchema(const TQString& name)
: FieldList(true)
, SchemaData(KexiDB::TableObjectType)
, m_query(0)
, m_isKexiDBSystem(false)
{
m_name = name.lower();
init();
}
TableSchema::TableSchema(const SchemaData& sdata)
: FieldList(true)
, SchemaData(sdata)
, m_query(0)
, m_isKexiDBSystem(false)
{
init();
}
TableSchema::TableSchema()
: FieldList(true)
, SchemaData(KexiDB::TableObjectType)
, m_query(0)
, m_isKexiDBSystem(false)
{
init();
}
TableSchema::TableSchema(const TableSchema& ts, bool copyId)
: FieldList(static_cast<const FieldList&>(ts))
, SchemaData(static_cast<const SchemaData&>(ts))
{
init(ts, copyId);
}
TableSchema::TableSchema(const TableSchema& ts, int setId)
: FieldList(static_cast<const FieldList&>(ts))
, SchemaData(static_cast<const SchemaData&>(ts))
{
init(ts, false);
m_id = setId;
}
// used by Connection
TableSchema::TableSchema(Connection *conn, const TQString & name)
: FieldList(true)
, SchemaData(KexiDB::TableObjectType)
, m_conn( conn )
, m_query(0)
, m_isKexiDBSystem(false)
{
d = new Private();
assert(conn);
m_name = name;
m_indices.setAutoDelete( true );
m_pkey = new IndexSchema(this);
m_indices.append(m_pkey);
}
TableSchema::~TableSchema()
{
if (m_conn)
m_conn->removeMe( this );
delete m_query;
delete d;
}
void TableSchema::init()
{
d = new Private();
m_indices.setAutoDelete( true );
m_pkey = new IndexSchema(this);
m_indices.append(m_pkey);
}
void TableSchema::init(const TableSchema& ts, bool copyId)
{
m_conn = ts.m_conn;
m_query = 0; //not cached
m_isKexiDBSystem = false;
d = new Private();
m_name = ts.m_name;
m_indices.setAutoDelete( true );
m_pkey = 0; //will be copied
if (!copyId)
m_id = -1;
//deep copy all members
IndexSchema::ListIterator idx_it(ts.m_indices);
for (;idx_it.current();++idx_it) {
IndexSchema *idx = new IndexSchema(
*idx_it.current(), *this /*fields from _this_ table will be assigned to the index*/);
if (idx->isPrimaryKey()) {//assign pkey
m_pkey = idx;
}
m_indices.append(idx);
}
}
void TableSchema::setPrimaryKey(IndexSchema *pkey)
{
if (m_pkey && m_pkey!=pkey) {
if (m_pkey->fieldCount()==0) {//this is empty key, probably default - remove it
m_indices.remove(m_pkey);
}
else {
m_pkey->setPrimaryKey(false); //there can be only one pkey..
//thats ok, the old pkey is still on indices list, if not empty
}
// m_pkey=0;
}
if (!pkey) {//clearing - set empty pkey
pkey = new IndexSchema(this);
}
m_pkey = pkey; //todo
m_pkey->setPrimaryKey(true);
d->anyNonPKField = 0; //for safety
}
FieldList& TableSchema::insertField(uint index, Field *field)
{
assert(field);
FieldList::insertField(index, field);
if (!field || index>m_fields.count())
return *this;
field->setTable(this);
field->m_order = index; //m_fields.count();
//update order for next next fields
Field *f = m_fields.at(index+1);
for (int i=index+1; f; i++, f = m_fields.next())
f->m_order = i;
//Check for auto-generated indices:
IndexSchema *idx = 0;
if (field->isPrimaryKey()) {// this is auto-generated single-field unique index
idx = new IndexSchema(this);
idx->setAutoGenerated(true);
idx->addField( field );
setPrimaryKey(idx);
}
if (field->isUniqueKey()) {
if (!idx) {
idx = new IndexSchema(this);
idx->setAutoGenerated(true);
idx->addField( field );
}
idx->setUnique(true);
}
if (field->isIndexed()) {// this is auto-generated single-field
if (!idx) {
idx = new IndexSchema(this);
idx->setAutoGenerated(true);
idx->addField( field );
}
}
if (idx)
m_indices.append(idx);
return *this;
}
void TableSchema::removeField(KexiDB::Field *field)
{
if (d->anyNonPKField && field == d->anyNonPKField) //d->anyNonPKField will be removed!
d->anyNonPKField = 0;
delete d->lookupFields[field];
d->lookupFields.remove(field);
FieldList::removeField(field);
}
#if 0 //original
KexiDB::FieldList& TableSchema::addField(KexiDB::Field* field)
{
assert(field);
FieldList::addField(field);
field->setTable(this);
field->m_order = m_fields.count();
//Check for auto-generated indices:
IndexSchema *idx = 0;
if (field->isPrimaryKey()) {// this is auto-generated single-field unique index
idx = new IndexSchema(this);
idx->setAutoGenerated(true);
idx->addField( field );
setPrimaryKey(idx);
}
if (field->isUniqueKey()) {
if (!idx) {
idx = new IndexSchema(this);
idx->setAutoGenerated(true);
idx->addField( field );
}
idx->setUnique(true);
}
if (field->isIndexed()) {// this is auto-generated single-field
if (!idx) {
idx = new IndexSchema(this);
idx->setAutoGenerated(true);
idx->addField( field );
}
}
if (idx)
m_indices.append(idx);
return *this;
}
#endif
void TableSchema::clear()
{
m_indices.clear();
d->clearLookupFields();
FieldList::clear();
SchemaData::clear();
m_conn = 0;
}
/*
void TableSchema::addPrimaryKey(const TQString& key)
{
m_primaryKeys.append(key);
}*/
/*TQStringList TableSchema::primaryKeys() const
{
return m_primaryKeys;
}
bool TableSchema::hasPrimaryKeys() const
{
return !m_primaryKeys.isEmpty();
}
*/
//const TQString& TableSchema::name() const
//{
// return m_name;
//}
//void TableSchema::setName(const TQString& name)
//{
// m_name=name;
/* ListIterator it( m_fields );
Field *field;
for (; (field = it.current())!=0; ++it) {
int fcnt=m_fields.count();
for (int i=0;i<fcnt;i++) {
m_fields[i].setTable(name);
}*/
//}
/*KexiDB::Field TableSchema::field(unsigned int id) const
{
if (id<m_fields.count()) return m_fields[id];
return KexiDB::Field();
}
unsigned int TableSchema::fieldCount() const
{
return m_fields.count();
}*/
TQString TableSchema::debugString()
{
return debugString(true);
}
TQString TableSchema::debugString(bool includeTableName)
{
TQString s;
if (includeTableName)
s = TQString("TABLE ") + schemaDataDebugString() + "\n";
s.append( FieldList::debugString() );
Field *f;
for (Field::ListIterator it(m_fields); (f = it.current()); ++it) {
LookupFieldSchema *lookupSchema = lookupFieldSchema( *f );
if (lookupSchema)
s.append( TQString("\n") + lookupSchema->debugString() );
}
return s;
}
void TableSchema::setKexiDBSystem(bool set)
{
if (set)
m_native=true;
m_isKexiDBSystem = set;
}
void TableSchema::setNative(bool set)
{
if (m_isKexiDBSystem && !set) {
KexiDBWarn << "TableSchema::setNative(): cannot set native off"
" when KexiDB system flag is set on!" << endl;
return;
}
m_native=set;
}
QuerySchema* TableSchema::query()
{
if (m_query)
return m_query;
m_query = new QuerySchema( *this ); //it's owned by me
return m_query;
}
Field* TableSchema::anyNonPKField()
{
if (!d->anyNonPKField) {
Field *f;
Field::ListIterator it(m_fields);
it.toLast(); //from the end (higher chances to tqfind)
for (; (f = it.current()); --it) {
if (!f->isPrimaryKey() && (!m_pkey || !m_pkey->hasField(f)))
break;
}
d->anyNonPKField = f;
}
return d->anyNonPKField;
}
bool TableSchema::setLookupFieldSchema( const TQString& fieldName, LookupFieldSchema *lookupFieldSchema )
{
Field *f = field(fieldName);
if (!f) {
KexiDBWarn << "TableSchema::setLookupFieldSchema(): no such field '" << fieldName
<< "' in table " << name() << endl;
return false;
}
if (lookupFieldSchema)
d->lookupFields.tqreplace( f, lookupFieldSchema );
else {
delete d->lookupFields[f];
d->lookupFields.remove( f );
}
d->lookupFieldsList.clear(); //this will force to rebuid the internal cache
return true;
}
LookupFieldSchema *TableSchema::lookupFieldSchema( const Field& field ) const
{
return d->lookupFields[ &field ];
}
LookupFieldSchema *TableSchema::lookupFieldSchema( const TQString& fieldName )
{
Field *f = TableSchema::field(fieldName);
if (!f)
return 0;
return lookupFieldSchema( *f );
}
const TQPtrVector<LookupFieldSchema>& TableSchema::lookupFieldsList()
{
if (d->lookupFields.isEmpty())
return d->lookupFieldsList;
if (!d->lookupFields.isEmpty() && !d->lookupFieldsList.isEmpty())
return d->lookupFieldsList; //already updated
//update
d->lookupFieldsList.clear();
d->lookupFieldsList.resize( d->lookupFields.count() );
uint i = 0;
for (Field::ListIterator it(m_fields); it.current(); ++it) {
TQMap<const Field*, LookupFieldSchema*>::ConstIterator itMap = d->lookupFields.tqfind( it.current() );
if (itMap != d->lookupFields.constEnd()) {
d->lookupFieldsList.insert( i, itMap.data() );
i++;
}
}
return d->lookupFieldsList;
}
//--------------------------------------
InternalTableSchema::InternalTableSchema(const TQString& name)
: TableSchema(name)
{
}
InternalTableSchema::InternalTableSchema(const TableSchema& ts)
: TableSchema(ts, false)
{
}
InternalTableSchema::~InternalTableSchema()
{
}