Kexi API Documentation (2.0 alpha)

alter.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "alter.h"
00021 #include "utils.h"
00022 #include <kexiutils/utils.h>
00023 
00024 #include <qmap.h>
00025 
00026 #include <kstaticdeleter.h>
00027 
00028 #include <stdlib.h>
00029 
00030 namespace KexiDB {
00031 class AlterTableHandler::Private
00032 {
00033     public:
00034         Private()
00035         {}
00036         ~Private()
00037         {}
00038         ActionList actions;
00039         QGuardedPtr<Connection> conn;
00040 };
00041 }
00042 
00043 using namespace KexiDB;
00044 
00046 AlterTableHandler::ChangeFieldPropertyAction nullChangeFieldPropertyAction(true);
00047 AlterTableHandler::RemoveFieldAction nullRemoveFieldAction(true);
00048 AlterTableHandler::InsertFieldAction nullInsertFieldAction(true);
00049 AlterTableHandler::MoveFieldPositionAction nullMoveFieldPositionAction(true);
00050 
00051 //--------------------------------------------------------
00052 
00053 AlterTableHandler::ActionBase::ActionBase(bool null)
00054  : m_alteringRequirements(0)
00055  , m_order(-1)
00056  , m_null(null)
00057 {
00058 }
00059 
00060 AlterTableHandler::ActionBase::~ActionBase()
00061 {
00062 }
00063 
00064 AlterTableHandler::ChangeFieldPropertyAction& AlterTableHandler::ActionBase::toChangeFieldPropertyAction()
00065 {
00066     if (dynamic_cast<ChangeFieldPropertyAction*>(this))
00067         return *dynamic_cast<ChangeFieldPropertyAction*>(this);
00068     return nullChangeFieldPropertyAction;
00069 }
00070 
00071 AlterTableHandler::RemoveFieldAction& AlterTableHandler::ActionBase::toRemoveFieldAction()
00072 {
00073     if (dynamic_cast<RemoveFieldAction*>(this))
00074         return *dynamic_cast<RemoveFieldAction*>(this);
00075     return nullRemoveFieldAction;
00076 }
00077 
00078 AlterTableHandler::InsertFieldAction& AlterTableHandler::ActionBase::toInsertFieldAction()
00079 {
00080     if (dynamic_cast<InsertFieldAction*>(this))
00081         return *dynamic_cast<InsertFieldAction*>(this);
00082     return nullInsertFieldAction;
00083 }
00084 
00085 AlterTableHandler::MoveFieldPositionAction& AlterTableHandler::ActionBase::toMoveFieldPositionAction()
00086 {
00087     if (dynamic_cast<MoveFieldPositionAction*>(this))
00088         return *dynamic_cast<MoveFieldPositionAction*>(this);
00089     return nullMoveFieldPositionAction;
00090 }
00091 
00092 //--------------------------------------------------------
00093 
00094 AlterTableHandler::FieldActionBase::FieldActionBase(const QString& fieldName, int uid)
00095  : ActionBase()
00096  , m_fieldUID(uid)
00097  , m_fieldName(fieldName)
00098 {
00099 }
00100 
00101 AlterTableHandler::FieldActionBase::FieldActionBase(bool)
00102  : ActionBase(true)
00103  , m_fieldUID(-1)
00104 {
00105 }
00106 
00107 AlterTableHandler::FieldActionBase::~FieldActionBase()
00108 {
00109 }
00110 
00111 //--------------------------------------------------------
00112 
00113 static KStaticDeleter< QMap<QCString,int> > KexiDB_alteringTypeForProperty_deleter;
00114 QMap<QCString,int> *KexiDB_alteringTypeForProperty = 0;
00115 
00116 int AlterTableHandler::alteringTypeForProperty(const QCString& propertyName)
00117 {
00118     if (!KexiDB_alteringTypeForProperty) {
00119         KexiDB_alteringTypeForProperty_deleter.setObject( KexiDB_alteringTypeForProperty, 
00120             new QMap<QCString,int>() );
00121 #define I(name, type) \
00122     KexiDB_alteringTypeForProperty->insert(QCString(name).lower(), (int)AlterTableHandler::type)
00123 #define I2(name, type1, type2) \
00124     flag = (int)AlterTableHandler::type1|(int)AlterTableHandler::type2; \
00125     if (flag & AlterTableHandler::PhysicalAlteringRequired) \
00126         flag |= AlterTableHandler::MainSchemaAlteringRequired; \
00127     KexiDB_alteringTypeForProperty->insert(QCString(name).lower(), flag)
00128 
00129     /* useful links: 
00130         http://dev.mysql.com/doc/refman/5.0/en/create-table.html
00131     */
00132         // ExtendedSchemaAlteringRequired is here because when the field is renamed, 
00133         // we need to do the same rename in extended table schema: <field name="...">
00134         int flag;
00135         I2("name", PhysicalAlteringRequired, MainSchemaAlteringRequired);
00136         I2("type", PhysicalAlteringRequired, DataConversionRequired);
00137         I("caption", MainSchemaAlteringRequired);
00138         I("description", MainSchemaAlteringRequired);
00139         I2("unsigned", PhysicalAlteringRequired, DataConversionRequired); // always?
00140         I2("length", PhysicalAlteringRequired, DataConversionRequired); // always?
00141         I2("precision", PhysicalAlteringRequired, DataConversionRequired); // always?
00142         I("width", MainSchemaAlteringRequired);
00143         // defaultValue: depends on backend, for mysql it can only by a constant or now()...
00144         // -- should we look at Driver here?
00145 #ifdef KEXI_NO_UNFINISHED
00146 
00147         I("defaultValue", MainSchemaAlteringRequired);
00148 #else
00149         I2("defaultValue", PhysicalAlteringRequired, MainSchemaAlteringRequired);
00150 #endif
00151         I2("primaryKey", PhysicalAlteringRequired, DataConversionRequired);
00152         I2("unique", PhysicalAlteringRequired, DataConversionRequired); // we may want to add an Index here
00153         I2("notNull", PhysicalAlteringRequired, DataConversionRequired); // we may want to add an Index here
00154         // allowEmpty: only support it just at kexi level? maybe there is a backend that supports this?
00155         I2("allowEmpty", PhysicalAlteringRequired, MainSchemaAlteringRequired); 
00156         I2("autoIncrement", PhysicalAlteringRequired, DataConversionRequired); // data conversion may be hard here
00157         I2("indexed", PhysicalAlteringRequired, DataConversionRequired); // we may want to add an Index here
00158 
00159         // easier cases follow...
00160         I("visibleDecimalPlaces", ExtendedSchemaAlteringRequired);
00161         //more to come...
00162 #undef I
00163 #undef I2
00164     }
00165     return (*KexiDB_alteringTypeForProperty)[propertyName.lower()]; 
00166 }
00167 
00168 //---
00169 
00170 AlterTableHandler::ChangeFieldPropertyAction::ChangeFieldPropertyAction(
00171     const QString& fieldName, const QString& propertyName, const QVariant& newValue, int uid)
00172  : FieldActionBase(fieldName, uid)
00173  , m_propertyName(propertyName)
00174  , m_newValue(newValue)
00175 {
00176 }
00177 
00178 AlterTableHandler::ChangeFieldPropertyAction::ChangeFieldPropertyAction(bool)
00179  : FieldActionBase(true)
00180 {
00181 }
00182 
00183 AlterTableHandler::ChangeFieldPropertyAction::~ChangeFieldPropertyAction()
00184 {
00185 }
00186 
00187 void AlterTableHandler::ChangeFieldPropertyAction::updateAlteringRequirements()
00188 {
00189 //  m_alteringRequirements = ???;
00190     setAlteringRequirements( alteringTypeForProperty( m_propertyName.latin1() ) );
00191 }
00192 
00193 QString AlterTableHandler::ChangeFieldPropertyAction::debugString(const DebugOptions& debugOptions)
00194 {
00195     QString s = QString("Set \"%1\" property for table field \"%2\" to \"%3\"")
00196         .arg(m_propertyName).arg(fieldName()).arg(m_newValue.toString());
00197     if (debugOptions.showUID)
00198         s.append(QString(" (UID=%1)").arg(m_fieldUID));
00199     return s;
00200 }
00201 
00202 static AlterTableHandler::ActionDict* createActionDict( 
00203     AlterTableHandler::ActionDictDict &fieldActions, int forFieldUID )
00204 {
00205     AlterTableHandler::ActionDict* dict = new AlterTableHandler::ActionDict(101, false);
00206     dict->setAutoDelete(true);
00207     fieldActions.insert( forFieldUID, dict );
00208     return dict;
00209 }
00210 
00211 static void debugAction(AlterTableHandler::ActionBase *action, int nestingLevel, 
00212   bool simulate, const QString& prependString = QString::null, QString* debugTarget = 0)
00213 {
00214     QString debugString;
00215     if (!debugTarget)
00216         debugString = prependString;
00217     if (action) {
00218         AlterTableHandler::ActionBase::DebugOptions debugOptions;
00219         debugOptions.showUID = debugTarget==0;
00220         debugOptions.showFieldDebug = debugTarget!=0;
00221         debugString += action->debugString( debugOptions );
00222     }
00223     else {
00224         if (!debugTarget)
00225             debugString += "[No action]"; //hmm
00226     }
00227     if (debugTarget) {
00228         if (!debugString.isEmpty())
00229             *debugTarget += debugString + '\n';
00230     }
00231     else {
00232         KexiDBDbg << debugString << endl;
00233 #ifdef KEXI_DEBUG_GUI
00234         if (simulate)
00235             KexiUtils::addAlterTableActionDebug(debugString, nestingLevel);
00236 #endif
00237     }
00238 }
00239 
00240 static void debugActionDict(AlterTableHandler::ActionDict *dict, int fieldUID, bool simulate)
00241 {
00242     QString fieldName;
00243     AlterTableHandler::ActionDictIterator it(*dict);
00244     if (dynamic_cast<AlterTableHandler::FieldActionBase*>(it.current())) //retrieve field name from the 1st related action
00245         fieldName = dynamic_cast<AlterTableHandler::FieldActionBase*>(it.current())->fieldName();
00246     else
00247         fieldName = "??";
00248     QString dbg = QString("Action dict for field \"%1\" (%2, UID=%3):")
00249         .arg(fieldName).arg(dict->count()).arg(fieldUID);
00250     KexiDBDbg << dbg << endl;
00251 #ifdef KEXI_DEBUG_GUI
00252     if (simulate)
00253         KexiUtils::addAlterTableActionDebug(dbg, 1);
00254 #endif
00255     for (;it.current(); ++it) {
00256         debugAction(it.current(), 2, simulate);
00257     }
00258 }
00259 
00260 static void debugFieldActions(const AlterTableHandler::ActionDictDict &fieldActions, bool simulate)
00261 {
00262 #ifdef KEXI_DEBUG_GUI
00263     if (simulate)
00264         KexiUtils::addAlterTableActionDebug("** Simplified Field Actions:");
00265 #endif
00266     for (AlterTableHandler::ActionDictDictIterator it(fieldActions); it.current(); ++it) {
00267         debugActionDict(it.current(), it.currentKey(), simulate);
00268     }
00269 }
00270 
00291 void AlterTableHandler::ChangeFieldPropertyAction::simplifyActions(ActionDictDict &fieldActions)
00292 {
00293     ActionDict *actionsLikeThis = fieldActions[ uid() ];
00294     if (m_propertyName=="name") {
00295         // Case 1. special: name1 -> name2, i.e. rename action
00296         QString newName( newValue().toString() );
00297         // try to find rename(newName, otherName) action
00298         ActionBase *renameActionLikeThis = actionsLikeThis ? actionsLikeThis->find( "name" ) : 0;
00299         if (dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)) {
00300             // 1. instead of having rename(fieldName(), newValue()) action,
00301             // let's have rename(fieldName(), otherName) action
00302             dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)->m_newValue 
00303                 = dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)->m_newValue;
00304 /*          AlterTableHandler::ChangeFieldPropertyAction* newRenameAction 
00305                 = new AlterTableHandler::ChangeFieldPropertyAction( *this );
00306             newRenameAction->m_newValue = dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)->m_newValue;
00307             // (m_order is the same as in newAction)
00308             // replace prev. rename action (if any)
00309             actionsLikeThis->remove( "name" );
00310             ActionDict *adict = fieldActions[ fieldName().latin1() ];
00311             if (!adict)
00312                 adict = createActionDict( fieldActions, fieldName() );
00313             adict->insert(m_propertyName.latin1(), newRenameAction);*/
00314         }
00315         else {
00316             ActionBase *removeActionForThisField = actionsLikeThis ? actionsLikeThis->find( ":remove:" ) : 0;
00317             if (removeActionForThisField) {
00318                 //if this field is going to be removed, jsut change the action's field name 
00319                 // and do not add a new action
00320             }
00321             else {
00322                 //just insert a copy of the rename action
00323                 if (!actionsLikeThis)
00324                     actionsLikeThis = createActionDict( fieldActions, uid() ); //fieldName() );
00325                 AlterTableHandler::ChangeFieldPropertyAction* newRenameAction 
00326                     = new AlterTableHandler::ChangeFieldPropertyAction( *this );
00327                 KexiDBDbg << "ChangeFieldPropertyAction::simplifyActions(): insert into '"
00328                     << fieldName() << "' dict:"  << newRenameAction->debugString() << endl;
00329                 actionsLikeThis->insert( m_propertyName.latin1(), newRenameAction );
00330                 return;
00331             }
00332         }
00333         if (actionsLikeThis) {
00334             // Case 1b. change "field name" information to fieldName() in any action that 
00335             //    is related to newName
00336             //    e.g. if there is setCaption("B", "captionA") action after rename("A","B"),
00337             //    replace setCaption action with setCaption("A", "captionA")
00338             foreach_dict (ActionDictIterator, it, *actionsLikeThis) {
00339                 dynamic_cast<FieldActionBase*>(it.current())->setFieldName( fieldName() );
00340             }
00341         }
00342         return;
00343     }
00344     ActionBase *removeActionForThisField = actionsLikeThis ? actionsLikeThis->find( ":remove:" ) : 0;
00345     if (removeActionForThisField) {
00346         //if this field is going to be removed, do not add a new action
00347         return;
00348     }
00349     // Case 2. other cases: just give up with adding this "intermediate" action
00350     // so, e.g. [ setCaption(A, "captionA"), setCaption(A, "captionB") ]
00351     //  becomes: [ setCaption(A, "captionB") ]
00352     // because adding this action does nothing
00353     ActionDict *nextActionsLikeThis = fieldActions[ uid() ]; //fieldName().latin1() ];
00354     if (!nextActionsLikeThis || !nextActionsLikeThis->find( m_propertyName.latin1() )) { 
00355         //no such action, add this
00356         AlterTableHandler::ChangeFieldPropertyAction* newAction 
00357             = new AlterTableHandler::ChangeFieldPropertyAction( *this );
00358         if (!nextActionsLikeThis)
00359             nextActionsLikeThis = createActionDict( fieldActions, uid() );//fieldName() );
00360         nextActionsLikeThis->insert( m_propertyName.latin1(), newAction );
00361     }
00362 }
00363 
00364 bool AlterTableHandler::ChangeFieldPropertyAction::shouldBeRemoved(ActionDictDict &fieldActions)
00365 {
00366     Q_UNUSED(fieldActions);
00367     return fieldName().lower() == m_newValue.toString().lower();
00368 }
00369 
00370 tristate AlterTableHandler::ChangeFieldPropertyAction::updateTableSchema(TableSchema &table, Field* field,
00371     QMap<QString, QString>& fieldMap)
00372 {
00373     //1. Simpler cases first: changes that do not affect table schema at all
00374     // "caption", "description", "width", "visibleDecimalPlaces"
00375     if (SchemaAlteringRequired & alteringTypeForProperty(m_propertyName.latin1())) {
00376         bool result = KexiDB::setFieldProperty(*field, m_propertyName.latin1(), newValue());
00377         return result;
00378     }
00379 
00380     if (m_propertyName=="name") {
00381         if (fieldMap[ field->name() ] == field->name())
00382             fieldMap.remove( field->name() );
00383         fieldMap.insert( newValue().toString(), field->name() );
00384         table.renameField(field, newValue().toString());
00385         return true;
00386     }
00387     return cancelled;
00388 }
00389 
00392 tristate AlterTableHandler::ChangeFieldPropertyAction::execute(Connection &conn, TableSchema &table)
00393 {
00394     Q_UNUSED(conn);
00395     Field *field = table.field( fieldName() );
00396     if (!field) {
00398         return false;
00399     }
00400     bool result;
00401     //1. Simpler cases first: changes that do not affect table schema at all
00402     // "caption", "description", "width", "visibleDecimalPlaces"
00403     if (SchemaAlteringRequired & alteringTypeForProperty(m_propertyName.latin1())) {
00404         result = KexiDB::setFieldProperty(*field, m_propertyName.latin1(), newValue());
00405         return result;
00406     }
00407 
00408 //todo
00409 return true;
00410 
00411     //2. Harder cases, that often require special care
00412     if (m_propertyName=="name") {
00413         /*mysql:
00414          A. Get real field type (it's safer): 
00415             let <TYPE> be the 2nd "Type" column from result of "DESCRIBE tablename oldfieldname"
00416             ( http://dev.mysql.com/doc/refman/5.0/en/describe.html )
00417          B. Run "ALTER TABLE tablename CHANGE oldfieldname newfieldname <TYPE>";
00418             ( http://dev.mysql.com/doc/refman/5.0/en/alter-table.html )
00419         */
00420     }
00421     if (m_propertyName=="type") {
00422         /*mysql:
00423          A. Like A. for "name" property above
00424          B. Construct <TYPE> string, eg. "varchar(50)" using the driver
00425          C. Like B. for "name" property above
00426          (mysql then truncate the values for changes like varchar -> integer,
00427          and properly convert the values for changes like integer -> varchar)
00428 
00429          TODO: more cases to check
00430         */
00431     }
00432     if (m_propertyName=="length") {
00433         //use "select max( length(o_name) ) from kexi__Objects"
00434         
00435     }
00436     if (m_propertyName=="primaryKey") {
00438     }
00439 
00440 /*
00441          "name", "unsigned", "precision", 
00442          "defaultValue", "primaryKey", "unique", "notNull", "allowEmpty",
00443          "autoIncrement", "indexed", 
00444 
00445 
00446     bool result = KexiDB::setFieldProperty(*field, m_propertyName.latin1(), newValue());
00447 */
00448     return result;
00449 }
00450 
00451 //--------------------------------------------------------
00452 
00453 AlterTableHandler::RemoveFieldAction::RemoveFieldAction(const QString& fieldName, int uid)
00454  : FieldActionBase(fieldName, uid)
00455 {
00456 }
00457 
00458 AlterTableHandler::RemoveFieldAction::RemoveFieldAction(bool)
00459  : FieldActionBase(true)
00460 {
00461 }
00462 
00463 AlterTableHandler::RemoveFieldAction::~RemoveFieldAction()
00464 {
00465 }
00466 
00467 void AlterTableHandler::RemoveFieldAction::updateAlteringRequirements()
00468 {
00470 
00471     setAlteringRequirements( PhysicalAlteringRequired );
00473 }
00474 
00475 QString AlterTableHandler::RemoveFieldAction::debugString(const DebugOptions& debugOptions)
00476 {
00477     QString s = QString("Remove table field \"%1\"").arg(fieldName());
00478     if (debugOptions.showUID)
00479         s.append(QString(" (UID=%1)").arg(uid()));
00480     return s;
00481 }
00482 
00489 void AlterTableHandler::RemoveFieldAction::simplifyActions(ActionDictDict &fieldActions)
00490 {
00492     AlterTableHandler::RemoveFieldAction* newAction 
00493             = new AlterTableHandler::RemoveFieldAction( *this );
00494     ActionDict *actionsLikeThis = fieldActions[ uid() ]; //fieldName().latin1() ];
00495     if (!actionsLikeThis)
00496         actionsLikeThis = createActionDict( fieldActions, uid() ); //fieldName() );
00497     actionsLikeThis->insert( ":remove:", newAction ); //special
00498 }
00499 
00500 tristate AlterTableHandler::RemoveFieldAction::updateTableSchema(TableSchema &table, Field* field,
00501     QMap<QString, QString>& fieldMap)
00502 {
00503     fieldMap.remove( field->name() );
00504     table.removeField(field);
00505     return true;
00506 }
00507 
00508 tristate AlterTableHandler::RemoveFieldAction::execute(Connection& conn, TableSchema& table)
00509 {
00510     Q_UNUSED(conn);
00511     Q_UNUSED(table);
00513     return true;
00514 }
00515 
00516 //--------------------------------------------------------
00517 
00518 AlterTableHandler::InsertFieldAction::InsertFieldAction(int fieldIndex, KexiDB::Field *field, int uid)
00519  : FieldActionBase(field->name(), uid)
00520  , m_index(fieldIndex)
00521  , m_field(0)
00522 {
00523     Q_ASSERT(field);
00524     setField(field);
00525 }
00526 
00527 AlterTableHandler::InsertFieldAction::InsertFieldAction(const InsertFieldAction& action)
00528  : FieldActionBase(action) //action.fieldName(), action.uid())
00529  , m_index(action.index())
00530 {
00531     m_field = new KexiDB::Field( action.field() );
00532 }
00533 
00534 AlterTableHandler::InsertFieldAction::InsertFieldAction(bool)
00535  : FieldActionBase(true)
00536  , m_index(0)
00537  , m_field(0)
00538 {
00539 }
00540 
00541 AlterTableHandler::InsertFieldAction::~InsertFieldAction()
00542 {
00543     delete m_field;
00544 }
00545 
00546 void AlterTableHandler::InsertFieldAction::setField(KexiDB::Field* field)
00547 {
00548     if (m_field)
00549         delete m_field;
00550     m_field = field;
00551     setFieldName(m_field ? m_field->name() : QString::null);
00552 }
00553 
00554 void AlterTableHandler::InsertFieldAction::updateAlteringRequirements()
00555 {
00557 
00558     setAlteringRequirements( PhysicalAlteringRequired );
00560 }
00561 
00562 QString AlterTableHandler::InsertFieldAction::debugString(const DebugOptions& debugOptions)
00563 {
00564     QString s = QString("Insert table field \"%1\" at position %2")
00565         .arg(m_field->name()).arg(m_index);
00566     if (debugOptions.showUID)
00567         s.append(QString(" (UID=%1)").arg(m_fieldUID));
00568     if (debugOptions.showFieldDebug)
00569         s.append(QString(" (%1)").arg(m_field->debugString()));
00570     return s;
00571 }
00572 
00585 void AlterTableHandler::InsertFieldAction::simplifyActions(ActionDictDict &fieldActions)
00586 {
00587     // Try to find actions related to this action
00588     ActionDict *actionsForThisField = fieldActions[ uid() ]; //m_field->name().latin1() ];
00589 
00590     ActionBase *removeActionForThisField = actionsForThisField ? actionsForThisField->find( ":remove:" ) : 0;
00591     if (removeActionForThisField) {
00592         //if this field is going to be removed, do not add a new action
00593         //and remove the "Remove" action
00594         actionsForThisField->remove(":remove:");
00595         return;
00596     }
00597     if (actionsForThisField) {
00598         //collect property values that have to be changed in this field
00599         QMap<QCString, QVariant> values;
00600         for (ActionDictIterator it(*actionsForThisField); it.current();) {
00601             ChangeFieldPropertyAction* changePropertyAction = dynamic_cast<ChangeFieldPropertyAction*>(it.current());
00602             if (changePropertyAction) {
00603                 //if this field is going to be renamed, also update fieldName()
00604                 if (changePropertyAction->propertyName()=="name") {
00605                     setFieldName(changePropertyAction->newValue().toString());
00606                 }
00607                 values.insert( changePropertyAction->propertyName().latin1(), changePropertyAction->newValue() );
00608                 //the subsequent "change property" action is no longer needed
00609                 actionsForThisField->remove(changePropertyAction->propertyName().latin1());
00610             }
00611             else {
00612                 ++it;
00613             }
00614         }
00615         if (!values.isEmpty()) {
00616             //update field, so it will be created as one step
00617             KexiDB::Field *f = new KexiDB::Field( field() );
00618             if (KexiDB::setFieldProperties( *f, values )) {
00619                 //field() = f;
00620                 setField( f );
00621                 field().debug();
00622 #ifdef KEXI_DEBUG_GUI
00623                 KexiUtils::addAlterTableActionDebug(
00624                     QString("** Property-set actions moved to field definition itself:\n")+field().debugString(), 0);
00625 #endif
00626             }
00627             else {
00628 #ifdef KEXI_DEBUG_GUI
00629                 KexiUtils::addAlterTableActionDebug(
00630                     QString("** Failed to set properties for field ")+field().debugString(), 0);
00631 #endif
00632                 KexiDBWarn << "AlterTableHandler::InsertFieldAction::simplifyActions(): KexiDB::setFieldProperties() failed!" << endl;
00633                 delete f;
00634             }
00635         }
00636     }
00637     //ok, insert this action
00639     AlterTableHandler::InsertFieldAction* newAction 
00640             = new AlterTableHandler::InsertFieldAction( *this );
00641     if (!actionsForThisField)
00642         actionsForThisField = createActionDict( fieldActions, uid() );
00643     actionsForThisField->insert( ":insert:", newAction ); //special
00644 }
00645 
00646 tristate AlterTableHandler::InsertFieldAction::updateTableSchema(TableSchema &table, Field* field,
00647     QMap<QString, QString>& fieldMap)
00648 {
00649     //in most cases we won't add the field to fieldMap
00650     Q_UNUSED(field);
00652     fieldMap.remove( this->field().name() );
00653     table.insertField(index(), new Field(this->field()));
00654     return true;
00655 }
00656 
00657 tristate AlterTableHandler::InsertFieldAction::execute(Connection& conn, TableSchema& table)
00658 {
00659     Q_UNUSED(conn);
00660     Q_UNUSED(table);
00662     return true;
00663 }
00664 
00665 //--------------------------------------------------------
00666 
00667 AlterTableHandler::MoveFieldPositionAction::MoveFieldPositionAction(
00668     int fieldIndex, const QString& fieldName, int uid)
00669  : FieldActionBase(fieldName, uid)
00670  , m_index(fieldIndex)
00671 {
00672 }
00673 
00674 AlterTableHandler::MoveFieldPositionAction::MoveFieldPositionAction(bool)
00675  : FieldActionBase(true)
00676 {
00677 }
00678 
00679 AlterTableHandler::MoveFieldPositionAction::~MoveFieldPositionAction()
00680 {
00681 }
00682 
00683 void AlterTableHandler::MoveFieldPositionAction::updateAlteringRequirements()
00684 {
00685     setAlteringRequirements( MainSchemaAlteringRequired );
00687 }
00688 
00689 QString AlterTableHandler::MoveFieldPositionAction::debugString(const DebugOptions& debugOptions)
00690 {
00691     QString s = QString("Move table field \"%1\" to position %2")
00692         .arg(fieldName()).arg(m_index);
00693     if (debugOptions.showUID)
00694         s.append(QString(" (UID=%1)").arg(uid()));
00695     return s;
00696 }
00697 
00698 void AlterTableHandler::MoveFieldPositionAction::simplifyActions(ActionDictDict &fieldActions)
00699 {
00700     Q_UNUSED(fieldActions);
00702 }
00703 
00704 tristate AlterTableHandler::MoveFieldPositionAction::execute(Connection& conn, TableSchema& table)
00705 {
00706     Q_UNUSED(conn);
00707     Q_UNUSED(table);
00709     return true;
00710 }
00711 
00712 //--------------------------------------------------------
00713 
00714 AlterTableHandler::AlterTableHandler(Connection &conn)
00715  : Object()
00716  , d( new Private() )
00717 {
00718     d->conn = &conn;
00719 }
00720 
00721 AlterTableHandler::~AlterTableHandler()
00722 {
00723     delete d;
00724 }
00725 
00726 void AlterTableHandler::addAction(ActionBase* action)
00727 {
00728     d->actions.append(action);
00729 }
00730 
00731 AlterTableHandler& AlterTableHandler::operator<< ( ActionBase* action )
00732 {
00733     d->actions.append(action);
00734     return *this;
00735 }
00736 
00737 const AlterTableHandler::ActionList& AlterTableHandler::actions() const
00738 {
00739     return d->actions;
00740 }
00741 
00742 void AlterTableHandler::removeAction(int index)
00743 {
00744     d->actions.remove( d->actions.at(index) );
00745 }
00746 
00747 void AlterTableHandler::clear()
00748 {
00749     d->actions.clear();
00750 }
00751 
00752 void AlterTableHandler::setActions(const ActionList& actions)
00753 {
00754     d->actions = actions;
00755 }
00756 
00757 void AlterTableHandler::debug()
00758 {
00759     KexiDBDbg << "AlterTableHandler's actions:" << endl;
00760     foreach_list (ActionListIterator, it, d->actions)
00761         it.current()->debug();
00762 }
00763 
00764 TableSchema* AlterTableHandler::execute(const QString& tableName, ExecutionArguments& args)
00765 {
00766     args.result = false;
00767     if (!d->conn) {
00769         return 0;
00770     }
00771     if (d->conn->isReadOnly()) {
00773         return 0;
00774     }
00775     if (!d->conn->isDatabaseUsed()) {
00777         return 0;
00778     }
00779     TableSchema *oldTable = d->conn->tableSchema(tableName);
00780     if (!oldTable) {
00782         return 0;
00783     }
00784 
00785     if (!args.debugString)
00786         debug();
00787 
00788     // Find a sum of requirements...
00789     int allActionsCount = 0;
00790     for(ActionListIterator it(d->actions); it.current(); ++it, allActionsCount++) {
00791         it.current()->updateAlteringRequirements();
00792         it.current()->m_order = allActionsCount;
00793     }
00794 
00795     /* Simplify actions list if possible and check for errors
00796 
00797     How to do it?
00798     - track property changes/deletions in reversed order
00799     - reduce intermediate actions
00800 
00801     Trivial example 1:
00802      *action1: "rename field a to b"
00803      *action2: "rename field b to c"
00804      *action3: "rename field c to d"
00805 
00806      After reduction:
00807      *action1: "rename field a to d" 
00808      Summing up: we have tracked what happens to field curently named "d"
00809      and eventually discovered that it was originally named "a".
00810 
00811     Trivial example 2:
00812      *action1: "rename field a to b"
00813      *action2: "rename field b to c"
00814      *action3: "remove field b"
00815      After reduction:
00816      *action3: "remove field b"
00817      Summing up: we have noticed that field "b" has beed eventually removed
00818      so we needed to find all actions related to this field and remove them.
00819      This is good optimization, as some of the eventually removed actions would 
00820      be difficult to perform and/or costly, what would be a waste of resources
00821      and a source of unwanted questions sent to the user.
00822     */
00823 
00824     ActionListIterator it(d->actions);
00825 
00826     // Fields-related actions. 
00827     ActionDictDict fieldActions(3001);
00828     fieldActions.setAutoDelete(true);
00829     ActionBase* action;
00830     for(it.toLast(); (action = it.current()); --it) {
00831         action->simplifyActions( fieldActions );
00832     }
00833 
00834     if (!args.debugString)
00835         debugFieldActions(fieldActions, args.simulate);
00836 
00837     // Prepare actions for execution ----
00838     // - Sort actions by order
00839     ActionVector actionsVector(allActionsCount);
00840     int currentActionsCount = 0; //some actions may be removed
00841     args.requirements = 0;
00842     QDict<char> fieldsWithChangedMainSchema(997); // Used to collect fields with changed main schema.
00843                                                   // This will be used when recreateTable is false to update kexi__fields
00844     for (ActionDictDictIterator it(fieldActions); it.current(); ++it) {
00845         for (AlterTableHandler::ActionDictIterator it2(*it.current());it2.current(); ++it2, currentActionsCount++) {
00846             if (it2.current()->shouldBeRemoved(fieldActions))
00847                 continue;
00848             actionsVector.insert( it2.current()->m_order, it2.current() );
00849             // a sum of requirements...
00850             const int r = it2.current()->alteringRequirements();
00851             args.requirements |= r;
00852             if (r & MainSchemaAlteringRequired && dynamic_cast<ChangeFieldPropertyAction*>(it2.current())) {
00853                 // Remember, this will be used when recreateTable is false to update kexi__fields, below.
00854                 fieldsWithChangedMainSchema.insert( 
00855                     dynamic_cast<ChangeFieldPropertyAction*>(it2.current())->fieldName(), (char*)1 );
00856             }
00857         }
00858     }
00859     // - Debug
00860     QString dbg = QString("** Overall altering requirements: %1").arg(args.requirements);
00861     KexiDBDbg << dbg << endl;
00862 
00863     if (args.onlyComputeRequirements) {
00864         args.result = true;
00865         return 0;
00866     }
00867 
00868     const bool recreateTable = (args.requirements & PhysicalAlteringRequired);
00869 
00870 #ifdef KEXI_DEBUG_GUI
00871     if (args.simulate)
00872         KexiUtils::addAlterTableActionDebug(dbg, 0);
00873 #endif
00874     dbg = QString("** Ordered, simplified actions (%1, was %2):").arg(currentActionsCount).arg(allActionsCount);
00875     KexiDBDbg << dbg << endl;
00876 #ifdef KEXI_DEBUG_GUI
00877     if (args.simulate)
00878         KexiUtils::addAlterTableActionDebug(dbg, 0);
00879 #endif
00880     for (int i=0; i<allActionsCount; i++) {
00881         debugAction(actionsVector[i], 1, args.simulate, QString("%1: ").arg(i+1), args.debugString);
00882     }
00883 
00884     if (args.requirements == 0) {//nothing to do
00885         args.result = true;
00886         return oldTable;
00887     }
00888     if (args.simulate) {//do not execute
00889         args.result = true;
00890         return oldTable;
00891     }
00892 // @todo transaction!
00893 
00894     // Create new TableSchema
00895     TableSchema *newTable = recreateTable ? new TableSchema(*oldTable, false) : oldTable;
00896     // find nonexisting temp name for new table schema
00897     if (recreateTable) {
00898         QString tempDestTableName;
00899         while (true) {
00900             tempDestTableName = QString("%1_temp%2%3").arg(newTable->name()).arg(QString::number(rand(), 16)).arg(QString::number(rand(), 16));
00901             if (!d->conn->tableSchema(tempDestTableName))
00902                 break;
00903         }
00904         newTable->setName( tempDestTableName );
00905     }
00906     oldTable->debug();
00907     if (recreateTable && !args.debugString)
00908         newTable->debug();
00909 
00910     // Update table schema in memory ----
00911     int lastUID = -1;
00912     Field *currentField = 0;
00913     QMap<QString, QString> fieldMap; // a map from new value to old value
00914     foreach_list( Field::ListIterator, it, newTable->fieldsIterator() ) {
00915         fieldMap.insert( it.current()->name(), it.current()->name() );
00916     }
00917     for (int i=0; i<allActionsCount; i++) {
00918         action = actionsVector[i];
00919         if (!action)
00920             continue;
00921         //remember the current Field object because soon we may be unable to find it by name:
00922         FieldActionBase *fieldAction = dynamic_cast<FieldActionBase*>(action);
00923         if (!fieldAction) {
00924             currentField = 0;
00925         }
00926         else {
00927             if (lastUID != fieldAction->uid()) {
00928                 currentField = newTable->field( fieldAction->fieldName() );
00929                 lastUID = currentField ? fieldAction->uid() : -1;
00930             }
00931             InsertFieldAction *insertFieldAction = dynamic_cast<InsertFieldAction*>(action);
00932             if (insertFieldAction && insertFieldAction->index()>(int)newTable->fieldCount()) {
00933                 //update index: there can be empty rows
00934                 insertFieldAction->setIndex(newTable->fieldCount());
00935             }
00936         }
00937         //if (!currentField)
00938         //  continue;
00939         args.result = action->updateTableSchema(*newTable, currentField, fieldMap);
00940         if (args.result!=true) {
00941             if (recreateTable)
00942                 delete newTable;
00943             return 0;
00944         }
00945     }
00946 
00947     if (recreateTable) {
00948         // Create the destination table with temporary name
00949         if (!d->conn->createTable( newTable, false )) {
00950             setError(d->conn);
00951             delete newTable;
00952             args.result = false;
00953             return 0;
00954         }
00955     }
00956 
00957 #if 0//todo
00958     // Execute actions ----
00959     for (int i=0; i<allActionsCount; i++) {
00960         action = actionsVector[i];
00961         if (!action)
00962             continue;
00963         args.result = action->execute(*d->conn, *newTable);
00964         if (!args.result || ~args.result) {
00966             args.result = false;
00967             return 0;
00968         }
00969     }
00970 #endif
00971 
00972     // update extended table schema after executing the actions
00973     if (!d->conn->storeExtendedTableSchemaData(*newTable)) {
00975         setError(d->conn);
00977         args.result = false;
00978         return 0;
00979     }
00980 
00981     if (recreateTable) {
00982         // Copy the data:
00983         // Build "INSERT INTO ... SELECT FROM ..." SQL statement
00984         // The order is based on the order of the source table fields.
00985         // Notes:
00986         // -Some source fields can be skipped in case when there are deleted fields.
00987         // -Some destination fields can be skipped in case when there 
00988         //  are new empty fields without fixed/default value.
00989         QString sql = QString("INSERT INTO %1 (").arg(d->conn->escapeIdentifier(newTable->name()));
00990         //insert list of dest. fields
00991         bool first = true;
00992         QString sourceFields;
00993         foreach_list( Field::ListIterator, it, newTable->fieldsIterator() ) {
00994             Field * const f = it.current();
00995             QString renamedFieldName( fieldMap[ f->name() ] );
00996             QString sourceSQLString;
00997             if (!renamedFieldName.isEmpty()) {
00998                 //this field should be renamed
00999                 sourceSQLString = d->conn->escapeIdentifier(renamedFieldName);
01000             }
01001             else if (!f->defaultValue().isNull()) {
01002                 //this field has a default value defined
01006                 sourceSQLString = d->conn->driver()->valueToSQL( f->type(), f->defaultValue() );
01007             }
01008             else if (f->isNotNull()) {
01009                 //this field cannot be null
01010                 sourceSQLString = d->conn->driver()->valueToSQL( 
01011                     f->type(), KexiDB::emptyValueForType( f->type() ) );
01012             }
01013             else if (f->isNotEmpty()) {
01014                 //this field cannot be empty - use any nonempty value..., e.g. " " for text or 0 for number
01015                 sourceSQLString = d->conn->driver()->valueToSQL( 
01016                     f->type(), KexiDB::notEmptyValueForType( f->type() ) );
01017             }
01020 
01021             if (!sourceSQLString.isEmpty()) {
01022                 if (first) {
01023                     first = false;
01024                 }
01025                 else {
01026                     sql.append( ", " );
01027                     sourceFields.append( ", " );
01028                 }
01029                 sql.append( d->conn->escapeIdentifier( f->name() ) );
01030                 sourceFields.append( sourceSQLString );
01031             }
01032         }
01033         sql.append(QString(") SELECT ") + sourceFields + " FROM " + oldTable->name());
01034         KexiDBDbg << " ** " << sql << endl;
01035         if (!d->conn->executeSQL( sql )) {
01036             setError(d->conn);
01038             args.result = false;
01039             return 0;
01040         }
01041 
01042         const QString oldTableName = oldTable->name();
01043 /*      args.result = d->conn->dropTable( oldTable );
01044         if (!args.result || ~args.result) {
01045             setError(d->conn);
01047             return 0;
01048         }
01049         oldTable = 0;*/
01050 
01051         // Replace the old table with the new one (oldTable will be destroyed)
01052         if (!d->conn->alterTableName(*newTable, oldTableName, true /*replace*/)) {
01053             setError(d->conn);
01055             args.result = false;
01056             return 0;
01057         }
01058         oldTable = 0;
01059     }
01060 
01061     if (!recreateTable) {
01062         if ((MainSchemaAlteringRequired & args.requirements) && !fieldsWithChangedMainSchema.isEmpty()) {
01063             //update main schema (kexi__fields) for changed fields
01064             foreach_list(QDictIterator<char>, it, fieldsWithChangedMainSchema) {
01065                 Field *f = newTable->field( it.currentKey() );
01066                 if (f) {
01067                     if (!d->conn->storeMainFieldSchema(f)) {
01068                         setError(d->conn);
01070                         args.result = false;
01071                         return 0;
01072                     }
01073                 }
01074             }
01075         }
01076     }
01077 
01078     args.result = true;
01079     return newTable;
01080 }
01081 
01082 /*TableSchema* AlterTableHandler::execute(const QString& tableName, tristate &result, bool simulate)
01083 {
01084     return executeInternal( tableName, result, simulate, 0 );
01085 }
01086 
01087 tristate AlterTableHandler::simulateExecution(const QString& tableName, QString& debugString)
01088 {
01089     tristate result;
01090     (void)executeInternal( tableName, result, true//simulate
01091     , &debugString );
01092     return result;
01093 }
01094 */
KDE Logo
This file is part of the documentation for Kexi 2.0 alpha.
Documentation copyright © 2002-2007 the Kexi Team.
Generated on Tue Apr 1 20:47:53 2008 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003