File indexing completed on 2024-03-24 16:11:11

0001 /* This file is part of the KDE project
0002    Copyright (C) 2006-2012 Jarosław Staniek <staniek@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KDbAlter.h"
0021 #include "KDb.h"
0022 #include "KDbConnection.h"
0023 #include "KDbConnectionOptions.h"
0024 #include "kdb_debug.h"
0025 
0026 #include <QMap>
0027 
0028 #include <stdlib.h>
0029 
0030 class Q_DECL_HIDDEN KDbAlterTableHandler::Private
0031 {
0032 public:
0033     Private() {}
0034     ~Private() {
0035         qDeleteAll(actions);
0036     }
0037     ActionList actions;
0038 //! @todo IMPORTANT: replace QPointer<KDbConnection> conn;
0039     KDbConnection* conn;
0040 private:
0041     Q_DISABLE_COPY(Private)
0042 };
0043 
0044 //! Define a global instance used to when returning null is needed
0045 #define DEFINE_NULL_OBJECT(name) \
0046 class Null ## name : public KDbAlterTableHandler::name \
0047 { \
0048     public: \
0049         Null ## name() : KDbAlterTableHandler::name(true) {} \
0050 }; \
0051 Null ## name null ## name
0052 
0053 DEFINE_NULL_OBJECT(ChangeFieldPropertyAction);
0054 DEFINE_NULL_OBJECT(RemoveFieldAction);
0055 DEFINE_NULL_OBJECT(InsertFieldAction);
0056 DEFINE_NULL_OBJECT(MoveFieldPositionAction);
0057 
0058 //--------------------------------------------------------
0059 
0060 KDbAlterTableHandler::ActionBase::ActionBase(bool null)
0061         : m_alteringRequirements(0)
0062         , m_order(-1)
0063         , m_null(null)
0064 {
0065 }
0066 
0067 KDbAlterTableHandler::ActionBase::~ActionBase()
0068 {
0069 }
0070 
0071 KDbAlterTableHandler::ChangeFieldPropertyAction& KDbAlterTableHandler::ActionBase::toChangeFieldPropertyAction()
0072 {
0073     if (dynamic_cast<ChangeFieldPropertyAction*>(this))
0074         return *dynamic_cast<ChangeFieldPropertyAction*>(this);
0075     return nullChangeFieldPropertyAction;
0076 }
0077 
0078 KDbAlterTableHandler::RemoveFieldAction& KDbAlterTableHandler::ActionBase::toRemoveFieldAction()
0079 {
0080     if (dynamic_cast<RemoveFieldAction*>(this))
0081         return *dynamic_cast<RemoveFieldAction*>(this);
0082     return nullRemoveFieldAction;
0083 }
0084 
0085 KDbAlterTableHandler::InsertFieldAction& KDbAlterTableHandler::ActionBase::toInsertFieldAction()
0086 {
0087     if (dynamic_cast<InsertFieldAction*>(this))
0088         return *dynamic_cast<InsertFieldAction*>(this);
0089     return nullInsertFieldAction;
0090 }
0091 
0092 KDbAlterTableHandler::MoveFieldPositionAction& KDbAlterTableHandler::ActionBase::toMoveFieldPositionAction()
0093 {
0094     if (dynamic_cast<MoveFieldPositionAction*>(this))
0095         return *dynamic_cast<MoveFieldPositionAction*>(this);
0096     return nullMoveFieldPositionAction;
0097 }
0098 
0099 void KDbAlterTableHandler::ActionBase::debug(const DebugOptions& debugOptions)
0100 {
0101     kdbDebug() << debugString(debugOptions)
0102     << " (req = " << alteringRequirements() << ")";
0103 }
0104 
0105 //--------------------------------------------------------
0106 
0107 KDbAlterTableHandler::FieldActionBase::FieldActionBase(const QString& fieldName, int uid)
0108         : ActionBase(false)
0109         , m_fieldUID(uid)
0110         , m_fieldName(fieldName)
0111 {
0112 }
0113 
0114 KDbAlterTableHandler::FieldActionBase::FieldActionBase(bool)
0115         : ActionBase(true)
0116         , m_fieldUID(-1)
0117 {
0118 }
0119 
0120 KDbAlterTableHandler::FieldActionBase::~FieldActionBase()
0121 {
0122 }
0123 
0124 //--------------------------------------------------------
0125 
0126 //! @internal
0127 struct KDb_AlterTableHandlerStatic {
0128     KDb_AlterTableHandlerStatic() {
0129 #define I(name, type) \
0130     types.insert(QByteArray(name).toLower(), int(KDbAlterTableHandler::type))
0131 #define I2(name, type1, type2) \
0132     flag = int(KDbAlterTableHandler::type1)|int(KDbAlterTableHandler::type2); \
0133     if (flag & KDbAlterTableHandler::PhysicalAlteringRequired) \
0134         flag |= KDbAlterTableHandler::MainSchemaAlteringRequired; \
0135     types.insert(QByteArray(name).toLower(), flag)
0136 
0137         /* useful links:
0138           https://dev.mysql.com/doc/refman/5.0/en/create-table.html
0139         */
0140         // ExtendedSchemaAlteringRequired is here because when the field is renamed,
0141         // we need to do the same rename in extended table schema: <field name="...">
0142         int flag;
0143         I2("name", PhysicalAlteringRequired, MainSchemaAlteringRequired);
0144         I2("type", PhysicalAlteringRequired, DataConversionRequired);
0145         I("caption", MainSchemaAlteringRequired);
0146         I("description", MainSchemaAlteringRequired);
0147         I2("unsigned", PhysicalAlteringRequired, DataConversionRequired); // always?
0148         I2("maxLength", PhysicalAlteringRequired, DataConversionRequired); // always?
0149         I2("precision", PhysicalAlteringRequired, DataConversionRequired); // always?
0150         I("defaultWidth", ExtendedSchemaAlteringRequired);
0151         // defaultValue: depends on backend, for mysql it can only by a constant or now()...
0152         // -- should we look at KDbDriver here?
0153 #ifdef KDB_UNFINISHED
0154         I2("defaultValue", PhysicalAlteringRequired, MainSchemaAlteringRequired);
0155 #else
0156         //! @todo reenable
0157         I("defaultValue", MainSchemaAlteringRequired);
0158 #endif
0159         I2("primaryKey", PhysicalAlteringRequired, DataConversionRequired);
0160         I2("unique", PhysicalAlteringRequired, DataConversionRequired); // we may want to add an Index here
0161         I2("notNull", PhysicalAlteringRequired, DataConversionRequired); // we may want to add an Index here
0162         // allowEmpty: only support it just at kexi level? maybe there is a backend that supports this?
0163         I2("allowEmpty", PhysicalAlteringRequired, MainSchemaAlteringRequired);
0164         I2("autoIncrement", PhysicalAlteringRequired, DataConversionRequired); // data conversion may be hard here
0165         I2("indexed", PhysicalAlteringRequired, DataConversionRequired); // we may want to add an Index here
0166 
0167         // easier cases follow...
0168         I("visibleDecimalPlaces", ExtendedSchemaAlteringRequired);
0169 
0170         //more to come...
0171 #undef I
0172 #undef I2
0173     }
0174 
0175     QHash<QByteArray, int> types;
0176 };
0177 
0178 Q_GLOBAL_STATIC(KDb_AlterTableHandlerStatic, KDb_alteringTypeForProperty)
0179 
0180 //! @internal
0181 int KDbAlterTableHandler::alteringTypeForProperty(const QByteArray& propertyName)
0182 {
0183     const int res = KDb_alteringTypeForProperty->types[propertyName.toLower()];
0184     if (res == 0) {
0185         if (KDb::isExtendedTableFieldProperty(propertyName))
0186             return int(ExtendedSchemaAlteringRequired);
0187         kdbWarning() << "property" << propertyName << "not found!";
0188     }
0189     return res;
0190 }
0191 
0192 //---
0193 
0194 KDbAlterTableHandler::ChangeFieldPropertyAction::ChangeFieldPropertyAction(
0195     const QString& fieldName, const QString& propertyName, const QVariant& newValue, int uid)
0196         : FieldActionBase(fieldName, uid)
0197         , m_propertyName(propertyName)
0198         , m_newValue(newValue)
0199 {
0200 }
0201 
0202 KDbAlterTableHandler::ChangeFieldPropertyAction::ChangeFieldPropertyAction()
0203     : ChangeFieldPropertyAction(true)
0204 {
0205 }
0206 
0207 KDbAlterTableHandler::ChangeFieldPropertyAction::ChangeFieldPropertyAction(bool null)
0208         : FieldActionBase(null)
0209 {
0210 }
0211 
0212 KDbAlterTableHandler::ChangeFieldPropertyAction::~ChangeFieldPropertyAction()
0213 {
0214 }
0215 
0216 void KDbAlterTableHandler::ChangeFieldPropertyAction::updateAlteringRequirements()
0217 {
0218     setAlteringRequirements(alteringTypeForProperty(m_propertyName.toLatin1()));
0219 }
0220 
0221 QString KDbAlterTableHandler::ChangeFieldPropertyAction::debugString(const DebugOptions& debugOptions)
0222 {
0223     QString s = QString::fromLatin1("Set \"%1\" property for table field \"%2\" to \"%3\"")
0224                 .arg(m_propertyName, fieldName(), m_newValue.toString());
0225     if (debugOptions.showUID) {
0226         s.append(QString::fromLatin1(" (UID=%1)").arg(m_fieldUID));
0227     }
0228     return s;
0229 }
0230 
0231 static KDbAlterTableHandler::ActionDict* createActionDict(
0232     KDbAlterTableHandler::ActionDictDict *fieldActions, int forFieldUID)
0233 {
0234     KDbAlterTableHandler::ActionDict* dict = new KDbAlterTableHandler::ActionDict();
0235     fieldActions->insert(forFieldUID, dict);
0236     return dict;
0237 }
0238 
0239 static void debugAction(KDbAlterTableHandler::ActionBase *action, int nestingLevel,
0240                         bool simulate, const QString& prependString = QString(), QString * debugTarget = nullptr)
0241 {
0242     QString debugString;
0243     if (!debugTarget)
0244         debugString = prependString;
0245     if (action) {
0246         KDbAlterTableHandler::ActionBase::DebugOptions debugOptions;
0247         debugOptions.showUID = debugTarget == nullptr;
0248         debugOptions.showFieldDebug = debugTarget != nullptr;
0249         debugString += action->debugString(debugOptions);
0250     } else {
0251         if (!debugTarget) {
0252             debugString += QLatin1String("[No action]"); //hmm
0253         }
0254     }
0255     if (debugTarget) {
0256         if (!debugString.isEmpty()) {
0257             *debugTarget += debugString + QLatin1Char('\n');
0258         }
0259     } else {
0260         kdbDebug() << debugString;
0261 #ifdef KDB_DEBUG_GUI
0262         if (simulate)
0263             KDb::alterTableActionDebugGUI(debugString, nestingLevel);
0264 #else
0265         Q_UNUSED(simulate)
0266         Q_UNUSED(nestingLevel)
0267 #endif
0268     }
0269 }
0270 
0271 static void debugActionDict(KDbAlterTableHandler::ActionDict *dict, int fieldUID, bool simulate)
0272 {
0273     QString fieldName;
0274     KDbAlterTableHandler::ActionDictConstIterator it(dict->constBegin());
0275     if (it != dict->constEnd() && dynamic_cast<KDbAlterTableHandler::FieldActionBase*>(it.value())) {
0276         //retrieve field name from the 1st related action
0277         fieldName = dynamic_cast<KDbAlterTableHandler::FieldActionBase*>(it.value())->fieldName();
0278     }
0279     else {
0280         fieldName = QLatin1String("??");
0281     }
0282     QString dbg(QString::fromLatin1("Action dict for field \"%1\" (%2, UID=%3):")
0283                         .arg(fieldName).arg(dict->count()).arg(fieldUID));
0284     kdbDebug() << dbg;
0285 #ifdef KDB_DEBUG_GUI
0286     if (simulate)
0287         KDb::alterTableActionDebugGUI(dbg, 1);
0288 #endif
0289     for (;it != dict->constEnd(); ++it) {
0290         debugAction(it.value(), 2, simulate);
0291     }
0292 }
0293 
0294 static void debugFieldActions(const KDbAlterTableHandler::ActionDictDict &fieldActions, bool simulate)
0295 {
0296 #ifdef KDB_DEBUG_GUI
0297     if (simulate)
0298         KDb::alterTableActionDebugGUI(QLatin1String("** Simplified Field Actions:"));
0299 #endif
0300     for (KDbAlterTableHandler::ActionDictDictConstIterator it(fieldActions.constBegin()); it != fieldActions.constEnd(); ++it) {
0301         debugActionDict(it.value(), it.key(), simulate);
0302     }
0303 }
0304 
0305 /*!
0306  Legend: A,B==fields, P==property, [....]==action, (..,..,..) group of actions, <...> internal operation.
0307  Case 1. (special)
0308     when new action=[rename A to B]
0309     and exists=[rename B to C]
0310     =>
0311     remove [rename B to C]
0312     and set result to new [rename A to C]
0313     and go to 1b.
0314  Case 1b. when new action=[rename A to B]
0315     and actions exist like [set property P to C in field B]
0316     or like [delete field B]
0317     or like [move field B]
0318     =>
0319     change B to A for all these actions
0320  Case 2. when new action=[change property in field A] (property != name)
0321     and exists=[remove A] or exists=[change property in field A]
0322     =>
0323     do not add [change property in field A] because it will be removed anyway or the property will change
0324 */
0325 void KDbAlterTableHandler::ChangeFieldPropertyAction::simplifyActions(ActionDictDict *fieldActions)
0326 {
0327     ActionDict *actionsLikeThis = fieldActions->value(uid());
0328     if (m_propertyName == QLatin1String("name")) {
0329         // Case 1. special: name1 -> name2, i.e. rename action
0330         QByteArray newName(newValue().toString().toLatin1());
0331         // try to find rename(newName, otherName) action
0332         ActionBase *renameActionLikeThis = actionsLikeThis ? actionsLikeThis->value(newName) : nullptr;
0333         if (dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)) {
0334             // 1. instead of having rename(fieldName(), newValue()) action,
0335             // let's have rename(fieldName(), otherName) action
0336             m_newValue = dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)->m_newValue;
0337             /*   KDbAlterTableHandler::ChangeFieldPropertyAction* newRenameAction
0338                     = new KDbAlterTableHandler::ChangeFieldPropertyAction( *this );
0339                   newRenameAction->m_newValue = dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)->m_newValue;
0340                   // (m_order is the same as in newAction)
0341                   // replace prev. rename action (if any)
0342                   actionsLikeThis->remove( "name" );
0343                   ActionDict *adict = (*fieldActions)[ fieldName().toLatin1() ];
0344                   if (!adict)
0345                     adict = createActionDict( fieldActions, fieldName() );
0346                   adict->insert(m_propertyName.toLatin1(), newRenameAction);*/
0347         } else {
0348             ActionBase *removeActionForThisField = actionsLikeThis ? actionsLikeThis->value(":remove:") : nullptr;
0349             if (removeActionForThisField) {
0350                 //if this field is going to be removed, just change the action's field name
0351                 // and do not add a new action
0352             } else {
0353                 //just insert a copy of the rename action
0354                 if (!actionsLikeThis)
0355                     actionsLikeThis = createActionDict(fieldActions, uid());
0356                 KDbAlterTableHandler::ChangeFieldPropertyAction* newRenameAction
0357                     = new KDbAlterTableHandler::ChangeFieldPropertyAction(*this);
0358                 kdbDebug() << "insert into" << fieldName() << "dict:" << newRenameAction->debugString();
0359                 actionsLikeThis->insert(m_propertyName.toLatin1(), newRenameAction);
0360                 return;
0361             }
0362         }
0363         if (actionsLikeThis) {
0364             // Case 1b. change "field name" information to fieldName() in any action that
0365             //    is related to newName
0366             //    e.g. if there is setCaption("B", "captionA") action after rename("A","B"),
0367             //    replace setCaption action with setCaption("A", "captionA")
0368             foreach(ActionBase* action, *actionsLikeThis) {
0369                 dynamic_cast<FieldActionBase*>(action)->setFieldName(fieldName());
0370             }
0371         }
0372         return;
0373     }
0374     ActionBase *removeActionForThisField = actionsLikeThis ? actionsLikeThis->value(":remove:") : nullptr;
0375     if (removeActionForThisField) {
0376         //if this field is going to be removed, do not add a new action
0377         return;
0378     }
0379     // Case 2. other cases: just give up with adding this "intermediate" action
0380     // so, e.g. [ setCaption(A, "captionA"), setCaption(A, "captionB") ]
0381     //  becomes: [ setCaption(A, "captionB") ]
0382     // because adding this action does nothing
0383     ActionDict *nextActionsLikeThis = fieldActions->value(uid());
0384     if (!nextActionsLikeThis || !nextActionsLikeThis->value(m_propertyName.toLatin1())) {
0385         //no such action, add this
0386         KDbAlterTableHandler::ChangeFieldPropertyAction* newAction
0387             = new KDbAlterTableHandler::ChangeFieldPropertyAction(*this);
0388         if (!nextActionsLikeThis)
0389             nextActionsLikeThis = createActionDict(fieldActions, uid());
0390         nextActionsLikeThis->insert(m_propertyName.toLatin1(), newAction);
0391     }
0392 }
0393 
0394 bool KDbAlterTableHandler::ChangeFieldPropertyAction::shouldBeRemoved(ActionDictDict *fieldActions)
0395 {
0396     Q_UNUSED(fieldActions);
0397     return 0 == fieldName().compare(m_newValue.toString(), Qt::CaseInsensitive);
0398 }
0399 
0400 tristate KDbAlterTableHandler::ChangeFieldPropertyAction::updateTableSchema(KDbTableSchema* table, KDbField* field,
0401         QHash<QString, QString>* fieldHash)
0402 {
0403     //1. Simpler cases first: changes that do not affect table schema at all
0404     // "caption", "description", "defaultWidth", "visibleDecimalPlaces"
0405     if (SchemaAlteringRequired & alteringTypeForProperty(m_propertyName.toLatin1())) {
0406         bool result = KDb::setFieldProperty(field, m_propertyName.toLatin1(), newValue());
0407         return result;
0408     }
0409 
0410     if (m_propertyName == QLatin1String("name")) {
0411         if (fieldHash->value(field->name()) == field->name())
0412             fieldHash->remove(field->name());
0413         fieldHash->insert(newValue().toString(), field->name());
0414         (void)table->renameField(field, newValue().toString());
0415         return true;
0416     }
0417     return cancelled;
0418 }
0419 
0420 /*! Many of the properties must be applied using a separate algorithm.
0421 */
0422 tristate KDbAlterTableHandler::ChangeFieldPropertyAction::execute(KDbConnection* conn, KDbTableSchema* table)
0423 {
0424     Q_UNUSED(conn);
0425     KDbField *field = table->field(fieldName());
0426     if (!field) {
0427         //! @todo errmsg
0428         return false;
0429     }
0430     bool result;
0431     //1. Simpler cases first: changes that do not affect table schema at all
0432     // "caption", "description", "defaultWidth", "visibleDecimalPlaces"
0433     if (SchemaAlteringRequired & alteringTypeForProperty(m_propertyName.toLatin1())) {
0434         result = KDb::setFieldProperty(field, m_propertyName.toLatin1(), newValue());
0435         return result;
0436     }
0437 
0438 //! @todo
0439 #if 1
0440     return true;
0441 #else
0442     //2. Harder cases, that often require special care
0443     if (m_propertyName == QLatin1String("name")) {
0444         /*mysql:
0445          A. Get real field type (it's safer):
0446             let <TYPE> be the 2nd "Type" column from result of "DESCRIBE tablename oldfieldname"
0447           ( https://dev.mysql.com/doc/refman/5.0/en/describe.html )
0448          B. Run "ALTER TABLE tablename CHANGE oldfieldname newfieldname <TYPE>";
0449           ( https://dev.mysql.com/doc/refman/5.0/en/alter-table.html )
0450         */
0451     }
0452     if (m_propertyName == QLatin1String("type")) {
0453         /*mysql:
0454          A. Like A. for "name" property above
0455          B. Construct <TYPE> string, eg. "varchar(50)" using the driver
0456          C. Like B. for "name" property above
0457          (mysql then truncate the values for changes like varchar -> integer,
0458          and properly convert the values for changes like integer -> varchar)
0459         */
0460         //! @todo more cases to check
0461     }
0462     if (m_propertyName == QLatin1String("maxLength")) {
0463         //! @todo use "select max( length(o_name) ) from kexi__objects"
0464 
0465     }
0466     if (m_propertyName == QLatin1String("primaryKey")) {
0467 //! @todo
0468     }
0469 
0470     /*
0471          "name", "unsigned", "precision",
0472          "defaultValue", "primaryKey", "unique", "notNull", "allowEmpty",
0473          "autoIncrement", "indexed",
0474 
0475 
0476       bool result = KDb::setFieldProperty(*field, m_propertyName.toLatin1(), newValue());
0477     */
0478     return result;
0479 #endif
0480 }
0481 
0482 //--------------------------------------------------------
0483 
0484 KDbAlterTableHandler::RemoveFieldAction::RemoveFieldAction(const QString& fieldName, int uid)
0485         : FieldActionBase(fieldName, uid)
0486 {
0487 }
0488 
0489 KDbAlterTableHandler::RemoveFieldAction::RemoveFieldAction(bool null)
0490         : FieldActionBase(null)
0491 {
0492 }
0493 
0494 KDbAlterTableHandler::RemoveFieldAction::~RemoveFieldAction()
0495 {
0496 }
0497 
0498 void KDbAlterTableHandler::RemoveFieldAction::updateAlteringRequirements()
0499 {
0500 //! @todo sometimes add DataConversionRequired (e.g. when relationships require removing orphaned records) ?
0501 
0502     setAlteringRequirements(PhysicalAlteringRequired);
0503     //! @todo
0504 }
0505 
0506 QString KDbAlterTableHandler::RemoveFieldAction::debugString(const DebugOptions& debugOptions)
0507 {
0508     QString s = QString::fromLatin1("Delete table field \"%1\"").arg(fieldName());
0509     if (debugOptions.showUID) {
0510         s.append(QString::fromLatin1(" (UID=%1)").arg(uid()));
0511     }
0512     return s;
0513 }
0514 
0515 /*!
0516  Legend: A,B==objects, P==property, [....]==action, (..,..,..) group of actions, <...> internal operation.
0517  Preconditions: we assume there cannot be such case encountered: ([remove A], [do something related on A])
0518   (except for [remove A], [insert A])
0519  General Case: it's safe to always insert a [remove A] action.
0520 */
0521 void KDbAlterTableHandler::RemoveFieldAction::simplifyActions(ActionDictDict *fieldActions)
0522 {
0523     //! @todo not checked
0524     KDbAlterTableHandler::RemoveFieldAction* newAction
0525         = new KDbAlterTableHandler::RemoveFieldAction(*this);
0526     ActionDict *actionsLikeThis = fieldActions->value(uid());
0527     if (!actionsLikeThis)
0528         actionsLikeThis = createActionDict(fieldActions, uid());
0529     actionsLikeThis->insert(":remove:", newAction);   //special
0530 }
0531 
0532 tristate KDbAlterTableHandler::RemoveFieldAction::updateTableSchema(KDbTableSchema* table, KDbField* field,
0533         QHash<QString, QString>* fieldHash)
0534 {
0535     fieldHash->remove(field->name());
0536     table->removeField(field);
0537     return true;
0538 }
0539 
0540 tristate KDbAlterTableHandler::RemoveFieldAction::execute(KDbConnection* conn, KDbTableSchema* table)
0541 {
0542     Q_UNUSED(conn);
0543     Q_UNUSED(table);
0544     //! @todo
0545     return true;
0546 }
0547 
0548 //--------------------------------------------------------
0549 
0550 KDbAlterTableHandler::InsertFieldAction::InsertFieldAction(int fieldIndex, KDbField *field, int uid)
0551         : FieldActionBase(field->name(), uid)
0552         , m_index(fieldIndex)
0553         , m_field(nullptr)
0554 {
0555     Q_ASSERT(field);
0556     setField(field);
0557 }
0558 
0559 KDbAlterTableHandler::InsertFieldAction::InsertFieldAction(const InsertFieldAction& action)
0560         : FieldActionBase(action) //action.fieldName(), action.uid())
0561         , m_index(action.index())
0562 {
0563     m_field = new KDbField(*action.field());
0564 }
0565 
0566 KDbAlterTableHandler::InsertFieldAction::InsertFieldAction()
0567         : InsertFieldAction(true)
0568 {
0569 }
0570 
0571 KDbAlterTableHandler::InsertFieldAction::InsertFieldAction(bool null)
0572         : FieldActionBase(null)
0573         , m_index(0)
0574         , m_field(nullptr)
0575 {
0576 }
0577 
0578 KDbAlterTableHandler::InsertFieldAction::~InsertFieldAction()
0579 {
0580     delete m_field;
0581 }
0582 
0583 void KDbAlterTableHandler::InsertFieldAction::setField(KDbField* field)
0584 {
0585     if (m_field)
0586         delete m_field;
0587     m_field = field;
0588     setFieldName(m_field ? m_field->name() : QString());
0589 }
0590 
0591 void KDbAlterTableHandler::InsertFieldAction::updateAlteringRequirements()
0592 {
0593 //! @todo sometimes add DataConversionRequired (e.g. when relationships require removing orphaned records) ?
0594 
0595     setAlteringRequirements(PhysicalAlteringRequired);
0596     //! @todo
0597 }
0598 
0599 QString KDbAlterTableHandler::InsertFieldAction::debugString(const DebugOptions& debugOptions)
0600 {
0601     QString s = QString::fromLatin1("Insert table field \"%1\" at position %2")
0602                 .arg(m_field->name()).arg(m_index);
0603     if (debugOptions.showUID) {
0604         s.append(QString::fromLatin1(" (UID=%1)").arg(m_fieldUID));
0605     }
0606     if (debugOptions.showFieldDebug) {
0607         s.append(QString::fromLatin1(" (%1)").arg(KDbUtils::debugString<KDbField>(*m_field)));
0608     }
0609     return s;
0610 }
0611 
0612 /*!
0613  Legend: A,B==fields, P==property, [....]==action, (..,..,..) group of actions, <...> internal operation.
0614 
0615 
0616  Case 1: there are "change property" actions after the Insert action.
0617   -> change the properties in the Insert action itself and remove the "change property" actions.
0618  Examples:
0619    [Insert A] && [rename A to B] => [Insert B]
0620    [Insert A] && [change property P in field A] => [Insert A with P altered]
0621  Comment: we need to do this reduction because otherwise we'd need to do psyhical altering
0622   right after [Insert A] if [rename A to B] follows.
0623 */
0624 void KDbAlterTableHandler::InsertFieldAction::simplifyActions(ActionDictDict *fieldActions)
0625 {
0626     // Try to find actions related to this action
0627     ActionDict *actionsForThisField = fieldActions->value(uid());
0628 
0629     ActionBase *removeActionForThisField = actionsForThisField ? actionsForThisField->value(":remove:") : nullptr;
0630     if (removeActionForThisField) {
0631         //if this field is going to be removed, do not add a new action
0632         //and remove the "Remove" action
0633         actionsForThisField->remove(":remove:");
0634         return;
0635     }
0636     if (actionsForThisField) {
0637         //collect property values that have to be changed in this field
0638         QMap<QByteArray, QVariant> values;
0639         ActionDict *newActionsForThisField = new ActionDict(); // this will replace actionsForThisField after the loop
0640         QSet<ActionBase*> actionsToDelete; // used to collect actions taht we soon delete but cannot delete in the loop below
0641         for (ActionDictConstIterator it(actionsForThisField->constBegin()); it != actionsForThisField->constEnd();++it) {
0642             ChangeFieldPropertyAction* changePropertyAction = dynamic_cast<ChangeFieldPropertyAction*>(it.value());
0643             if (changePropertyAction) {
0644                 //if this field is going to be renamed, also update fieldName()
0645                 if (changePropertyAction->propertyName() == QLatin1String("name")) {
0646                     setFieldName(changePropertyAction->newValue().toString());
0647                 }
0648                 values.insert(changePropertyAction->propertyName().toLatin1(), changePropertyAction->newValue());
0649                 //the subsequent "change property" action is no longer needed
0650                 actionsToDelete.insert(it.value());
0651             } else {
0652                 //keep
0653                 newActionsForThisField->insert(it.key(), it.value());
0654             }
0655         }
0656         qDeleteAll(actionsToDelete);
0657         actionsForThisField->setAutoDelete(false);
0658         delete actionsForThisField;
0659         actionsForThisField = newActionsForThisField;
0660         fieldActions->take(uid());
0661         fieldActions->insert(uid(), actionsForThisField);
0662         if (!values.isEmpty()) {
0663             //update field, so it will be created as one step
0664             KDbField *f = new KDbField(*field());
0665             if (KDb::setFieldProperties(f, values)) {
0666                 setField(f);
0667                 kdbDebug() << field();
0668 #ifdef KDB_DEBUG_GUI
0669                 KDb::alterTableActionDebugGUI(
0670                     QLatin1String("** Property-set actions moved to field definition itself:\n")
0671                         + KDbUtils::debugString<KDbField>(*field()), 0);
0672 #endif
0673             } else {
0674 #ifdef KDB_DEBUG_GUI
0675                 KDb::alterTableActionDebugGUI(
0676                     QLatin1String("** Failed to set properties for field ") + KDbUtils::debugString<KDbField>(*field()), 0);
0677 #endif
0678                 kdbWarning() << "setFieldProperties() failed!";
0679                 delete f;
0680             }
0681         }
0682     }
0683     //ok, insert this action
0684     //! @todo not checked
0685     KDbAlterTableHandler::InsertFieldAction* newAction
0686         = new KDbAlterTableHandler::InsertFieldAction(*this);
0687     if (!actionsForThisField)
0688         actionsForThisField = createActionDict(fieldActions, uid());
0689     actionsForThisField->insert(":insert:", newAction);   //special
0690 }
0691 
0692 tristate KDbAlterTableHandler::InsertFieldAction::updateTableSchema(KDbTableSchema* table, KDbField* field,
0693         QHash<QString, QString>* fieldMap)
0694 {
0695     //in most cases we won't add the field to fieldMap
0696     Q_UNUSED(field);
0697 //! @todo add it only when there should be fixed value (e.g. default) set for this new field...
0698     fieldMap->remove(this->field()->name());
0699     table->insertField(index(), new KDbField(*this->field()));
0700     return true;
0701 }
0702 
0703 tristate KDbAlterTableHandler::InsertFieldAction::execute(KDbConnection* conn, KDbTableSchema* table)
0704 {
0705     Q_UNUSED(conn);
0706     Q_UNUSED(table);
0707     //! @todo
0708     return true;
0709 }
0710 
0711 //--------------------------------------------------------
0712 
0713 KDbAlterTableHandler::MoveFieldPositionAction::MoveFieldPositionAction(
0714     int fieldIndex, const QString& fieldName, int uid)
0715         : FieldActionBase(fieldName, uid)
0716         , m_index(fieldIndex)
0717 {
0718 }
0719 
0720 KDbAlterTableHandler::MoveFieldPositionAction::MoveFieldPositionAction(bool null)
0721         : FieldActionBase(null)
0722         , m_index(-1)
0723 {
0724 }
0725 
0726 KDbAlterTableHandler::MoveFieldPositionAction::~MoveFieldPositionAction()
0727 {
0728 }
0729 
0730 void KDbAlterTableHandler::MoveFieldPositionAction::updateAlteringRequirements()
0731 {
0732     setAlteringRequirements(MainSchemaAlteringRequired);
0733     //! @todo
0734 }
0735 
0736 QString KDbAlterTableHandler::MoveFieldPositionAction::debugString(const DebugOptions& debugOptions)
0737 {
0738     QString s = QString::fromLatin1("Move table field \"%1\" to position %2")
0739                 .arg(fieldName()).arg(m_index);
0740     if (debugOptions.showUID) {
0741         s.append(QString::fromLatin1(" (UID=%1)").arg(uid()));
0742     }
0743     return s;
0744 }
0745 
0746 void KDbAlterTableHandler::MoveFieldPositionAction::simplifyActions(ActionDictDict *fieldActions)
0747 {
0748     Q_UNUSED(fieldActions);
0749     //! @todo
0750 }
0751 
0752 tristate KDbAlterTableHandler::MoveFieldPositionAction::execute(KDbConnection* conn, KDbTableSchema* table)
0753 {
0754     Q_UNUSED(conn);
0755     Q_UNUSED(table);
0756     //! @todo
0757     return true;
0758 }
0759 
0760 //--------------------------------------------------------
0761 
0762 KDbAlterTableHandler::KDbAlterTableHandler(KDbConnection* conn)
0763         : d(new Private())
0764 {
0765     d->conn = conn;
0766 }
0767 
0768 KDbAlterTableHandler::~KDbAlterTableHandler()
0769 {
0770     delete d;
0771 }
0772 
0773 void KDbAlterTableHandler::addAction(ActionBase* action)
0774 {
0775     d->actions.append(action);
0776 }
0777 
0778 KDbAlterTableHandler& KDbAlterTableHandler::operator<< (ActionBase* action)
0779 {
0780     d->actions.append(action);
0781     return *this;
0782 }
0783 
0784 const KDbAlterTableHandler::ActionList& KDbAlterTableHandler::actions() const
0785 {
0786     return d->actions;
0787 }
0788 
0789 void KDbAlterTableHandler::removeAction(int index)
0790 {
0791     d->actions.removeAt(index);
0792 }
0793 
0794 void KDbAlterTableHandler::clear()
0795 {
0796     d->actions.clear();
0797 }
0798 
0799 void KDbAlterTableHandler::setActions(const ActionList& actions)
0800 {
0801     qDeleteAll(d->actions);
0802     d->actions = actions;
0803 }
0804 
0805 void KDbAlterTableHandler::debug()
0806 {
0807     kdbDebug() << "KDbAlterTableHandler's actions:";
0808     foreach(ActionBase* action, d->actions) {
0809         action->debug();
0810     }
0811 }
0812 
0813 KDbTableSchema* KDbAlterTableHandler::execute(const QString& tableName, ExecutionArguments* args)
0814 {
0815     args->result = false;
0816     if (!d->conn) {
0817 //! @todo err msg?
0818         return nullptr;
0819     }
0820     if (d->conn->options()->isReadOnly()) {
0821 //! @todo err msg?
0822         return nullptr;
0823     }
0824     if (!d->conn->isDatabaseUsed()) {
0825 //! @todo err msg?
0826         return nullptr;
0827     }
0828     KDbTableSchema *oldTable = d->conn->tableSchema(tableName);
0829     if (!oldTable) {
0830 //! @todo err msg?
0831         return nullptr;
0832     }
0833 
0834     if (!args->debugString)
0835         debug();
0836 
0837     // Find a sum of requirements...
0838     int allActionsCount = 0;
0839     foreach(ActionBase* action, d->actions) {
0840         action->updateAlteringRequirements();
0841         action->m_order = allActionsCount++;
0842     }
0843 
0844     /* Simplify actions list if possible and check for errors
0845 
0846     How to do it?
0847     - track property changes/deletions in reversed order
0848     - reduce intermediate actions
0849 
0850     Trivial example 1:
0851      *action1: "rename field a to b"
0852      *action2: "rename field b to c"
0853      *action3: "rename field c to d"
0854 
0855      After reduction:
0856      *action1: "rename field a to d"
0857      Summing up: we have tracked what happens to field curently named "d"
0858      and eventually discovered that it was originally named "a".
0859 
0860     Trivial example 2:
0861      *action1: "rename field a to b"
0862      *action2: "rename field b to c"
0863      *action3: "remove field b"
0864      After reduction:
0865      *action3: "remove field b"
0866      Summing up: we have noticed that field "b" has beed eventually removed
0867      so we needed to find all actions related to this field and remove them.
0868      This is good optimization, as some of the eventually removed actions would
0869      be difficult to perform and/or costly, what would be a waste of resources
0870      and a source of unwanted questions sent to the user.
0871     */
0872 
0873 
0874 
0875     // Fields-related actions.
0876     ActionDictDict fieldActions;
0877     ActionBase* action;
0878     for (int i = d->actions.count() - 1; i >= 0; i--) {
0879         d->actions[i]->simplifyActions(&fieldActions);
0880     }
0881 
0882     if (!args->debugString)
0883         debugFieldActions(fieldActions, args->simulate);
0884 
0885     // Prepare actions for execution ----
0886     // - Sort actions by order
0887     ActionsVector actionsVector(allActionsCount);
0888     int currentActionsCount = 0; //some actions may be removed
0889     args->requirements = 0;
0890     QSet<QString> fieldsWithChangedMainSchema; // Used to collect fields with changed main schema.
0891     // This will be used when recreateTable is false to update kexi__fields
0892     for (ActionDictDictConstIterator it(fieldActions.constBegin()); it != fieldActions.constEnd(); ++it) {
0893         for (KDbAlterTableHandler::ActionDictConstIterator it2(it.value()->constBegin());
0894              it2 != it.value()->constEnd(); ++it2, currentActionsCount++)
0895         {
0896             if (it2.value()->shouldBeRemoved(&fieldActions))
0897                 continue;
0898             actionsVector[ it2.value()->m_order ] = it2.value();
0899             // a sum of requirements...
0900             const int r = it2.value()->alteringRequirements();
0901             args->requirements |= r;
0902             if (r & MainSchemaAlteringRequired && dynamic_cast<ChangeFieldPropertyAction*>(it2.value())) {
0903                 // Remember, this will be used when recreateTable is false to update kexi__fields, below.
0904                 fieldsWithChangedMainSchema.insert(
0905                     dynamic_cast<ChangeFieldPropertyAction*>(it2.value())->fieldName());
0906             }
0907         }
0908     }
0909     // - Debug
0910     QString dbg = QString::fromLatin1("** Overall altering requirements: %1").arg(args->requirements);
0911     kdbDebug() << dbg;
0912 
0913     if (args->onlyComputeRequirements) {
0914         args->result = true;
0915         return nullptr;
0916     }
0917 
0918     const bool recreateTable = (args->requirements & PhysicalAlteringRequired);
0919 
0920 #ifdef KDB_DEBUG_GUI
0921     if (args->simulate)
0922         KDb::alterTableActionDebugGUI(dbg, 0);
0923 #endif
0924     dbg = QString::fromLatin1("** Ordered, simplified actions (%1, was %2):")
0925             .arg(currentActionsCount).arg(allActionsCount);
0926     kdbDebug() << dbg;
0927 #ifdef KDB_DEBUG_GUI
0928     if (args->simulate)
0929         KDb::alterTableActionDebugGUI(dbg, 0);
0930 #endif
0931     for (int i = 0; i < allActionsCount; i++) {
0932         debugAction(actionsVector.at(i), 1, args->simulate,
0933                     QString::fromLatin1("%1: ").arg(i + 1), args->debugString);
0934     }
0935 
0936     if (args->requirements == 0) {//nothing to do
0937         args->result = true;
0938         return oldTable;
0939     }
0940     if (args->simulate) {//do not execute
0941         args->result = true;
0942         return oldTable;
0943     }
0944 //! @todo transaction!
0945 
0946     // Create a new KDbTableSchema
0947     KDbTableSchema *newTable = recreateTable ? new KDbTableSchema(*oldTable, false/*!copy id*/) : oldTable;
0948     // find nonexisting temp name for new table schema
0949     if (recreateTable) {
0950         QString tempDestTableName = KDb::temporaryTableName(d->conn, newTable->name());
0951         newTable->setName(tempDestTableName);
0952     }
0953     kdbDebug() << *oldTable;
0954     if (recreateTable && !args->debugString) {
0955         kdbDebug() << *newTable;
0956     }
0957 
0958     // Update table schema in memory ----
0959     int lastUID = -1;
0960     KDbField *currentField = nullptr;
0961     QHash<QString, QString> fieldHash; // a map from new value to old value
0962     foreach(KDbField* f, *newTable->fields()) {
0963         fieldHash.insert(f->name(), f->name());
0964     }
0965     for (int i = 0; i < allActionsCount; i++) {
0966         action = actionsVector.at(i);
0967         if (!action)
0968             continue;
0969         //remember the current KDbField object because soon we may be unable to find it by name:
0970         FieldActionBase *fieldAction = dynamic_cast<FieldActionBase*>(action);
0971         if (!fieldAction) {
0972             currentField = nullptr;
0973         } else {
0974             if (lastUID != fieldAction->uid()) {
0975                 currentField = newTable->field(fieldAction->fieldName());
0976                 lastUID = currentField ? fieldAction->uid() : -1;
0977             }
0978             InsertFieldAction *insertFieldAction = dynamic_cast<InsertFieldAction*>(action);
0979             if (insertFieldAction && insertFieldAction->index() > newTable->fieldCount()) {
0980                 //update index: there can be empty rows
0981                 insertFieldAction->setIndex(newTable->fieldCount());
0982             }
0983         }
0984         args->result = action->updateTableSchema(newTable, currentField, &fieldHash);
0985         if (args->result != true) {
0986             if (recreateTable)
0987                 delete newTable;
0988             return nullptr;
0989         }
0990     }
0991 
0992     if (recreateTable) {
0993         // Create the destination table with temporary name
0994         if (!d->conn->createTable(newTable,
0995           KDbConnection::CreateTableOptions(KDbConnection::CreateTableOption::Default)
0996               & ~KDbConnection::CreateTableOptions(KDbConnection::CreateTableOption::DropDestination)))
0997         {
0998             m_result = d->conn->result();
0999             delete newTable;
1000             args->result = false;
1001             return nullptr;
1002         }
1003     }
1004 
1005 #if 0
1006     //! @todo
1007     // Execute actions ----
1008     for (int i = 0; i < allActionsCount; i++) {
1009         action = actionsVector.at(i);
1010         if (!action)
1011             continue;
1012         args.result = action->execute(*d->conn, *newTable);
1013         if (!args.result || ~args.result) {
1014 //! @todo delete newTable...
1015             args.result = false;
1016             return 0;
1017         }
1018     }
1019 #endif
1020 
1021     // update extended table schema after executing the actions
1022     if (!d->conn->storeExtendedTableSchemaData(newTable)) {
1023 //! @todo better errmsg?
1024         m_result = d->conn->result();
1025 //! @todo delete newTable...
1026         args->result = false;
1027         return nullptr;
1028     }
1029 
1030     if (recreateTable) {
1031         // Copy the data:
1032         // Build "INSERT INTO ... SELECT FROM ..." SQL statement
1033         // The order is based on the order of the source table fields.
1034         // Notes:
1035         // -Some source fields can be skipped in case when there are deleted fields.
1036         // -Some destination fields can be skipped in case when there
1037         //  are new empty fields without fixed/default value.
1038         KDbEscapedString sql = KDbEscapedString("INSERT INTO %1 (").arg(d->conn->escapeIdentifier(newTable->name()));
1039         //insert list of dest. fields
1040         bool first = true;
1041         KDbEscapedString sourceFields;
1042         foreach(KDbField* f, *newTable->fields()) {
1043             QString renamedFieldName(fieldHash.value(f->name()));
1044             KDbEscapedString sourceSqlString;
1045             const KDbField::Type type = f->type(); // cache: evaluating type of expressions can be expensive
1046             if (!renamedFieldName.isEmpty()) {
1047                 //this field should be renamed
1048                 sourceSqlString = KDbEscapedString(d->conn->escapeIdentifier(renamedFieldName));
1049             } else if (!f->defaultValue().isNull()) {
1050                 //this field has a default value defined
1051 //! @todo support expressions (eg. TODAY()) as a default value
1052 //! @todo this field can be notNull or notEmpty - check whether the default is ok
1053 //!       (or do this checking also in the Table Designer?)
1054                 sourceSqlString = d->conn->driver()->valueToSql(type, f->defaultValue());
1055             } else if (f->isNotNull()) {
1056                 //this field cannot be null
1057                 sourceSqlString = d->conn->driver()->valueToSql(
1058                                       type, KDb::emptyValueForFieldType(type));
1059             } else if (f->isNotEmpty()) {
1060                 //this field cannot be empty - use any nonempty value..., e.g. " " for text or 0 for number
1061                 sourceSqlString = d->conn->driver()->valueToSql(
1062                                       type, KDb::notEmptyValueForFieldType(type));
1063             }
1064 //! @todo support unique, validatationRule, unsigned flags...
1065 //! @todo check for foreignKey values...
1066 
1067             if (!sourceSqlString.isEmpty()) {
1068                 if (first) {
1069                     first = false;
1070                 } else {
1071                     sql.append(", ");
1072                     sourceFields.append(", ");
1073                 }
1074                 sql += d->conn->escapeIdentifier(f->name());
1075                 sourceFields.append(sourceSqlString);
1076             }
1077         }
1078         sql += (") SELECT " + sourceFields + " FROM " + oldTable->name());
1079         kdbDebug() << " ** " << sql;
1080         if (!d->conn->executeSql(sql)) {
1081             m_result = d->conn->result();
1082 //! @todo delete newTable...
1083             args->result = false;
1084             return nullptr;
1085         }
1086 
1087         const QString oldTableName = oldTable->name();
1088         /*  args.result = d->conn->dropTable( oldTable );
1089             if (!args.result || ~args.result) {
1090               setError(d->conn);
1091         //! @todo delete newTable...
1092               return 0;
1093             }
1094             oldTable = 0;*/
1095 
1096         // Replace the old table with the new one (oldTable will be destroyed)
1097         if (!d->conn->alterTableName(newTable, oldTableName,
1098             KDbConnection::AlterTableNameOption::Default | KDbConnection::AlterTableNameOption::DropDestination))
1099         {
1100             m_result = d->conn->result();
1101 //! @todo delete newTable...
1102             args->result = false;
1103             return nullptr;
1104         }
1105         oldTable = nullptr;
1106     }
1107 
1108     if (!recreateTable) {
1109         if ((MainSchemaAlteringRequired & args->requirements) && !fieldsWithChangedMainSchema.isEmpty()) {
1110             //update main schema (kexi__fields) for changed fields
1111             foreach(const QString& changeFieldPropertyActionName, fieldsWithChangedMainSchema) {
1112                 KDbField *f = newTable->field(changeFieldPropertyActionName);
1113                 if (f) {
1114                     if (!d->conn->storeMainFieldSchema(f)) {
1115                         m_result = d->conn->result();
1116                         //! @todo delete newTable...
1117                         args->result = false;
1118                         return nullptr;
1119                     }
1120                 }
1121             }
1122         }
1123     }
1124     args->result = true;
1125     return newTable;
1126 }
1127 
1128 /*KDbTableSchema* KDbAlterTableHandler::execute(const QString& tableName, tristate &result, bool simulate)
1129 {
1130   return executeInternal( tableName, result, simulate, 0 );
1131 }
1132 
1133 tristate KDbAlterTableHandler::simulateExecution(const QString& tableName, QString& debugString)
1134 {
1135   tristate result;
1136   (void)executeInternal( tableName, result, true//simulate
1137   , &debugString );
1138   return result;
1139 }
1140 */