kexi

alter.cpp

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