kexi

alter.cpp

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