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