/* 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 find) 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.replace( 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.find( 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() { }