kexi

alter.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "alter.h"
21#include "utils.h"
22#include <kexiutils/utils.h>
23
24#include <tqmap.h>
25
26#include <kstaticdeleter.h>
27
28#include <stdlib.h>
29
30namespace KexiDB {
31class AlterTableHandler::Private
32{
33 public:
34 Private()
35 {}
36 ~Private()
37 {}
38 ActionList actions;
39 TQGuardedPtr<Connection> conn;
40};
41}
42
43using namespace KexiDB;
44
46AlterTableHandler::ChangeFieldPropertyAction nullChangeFieldPropertyAction(true);
47AlterTableHandler::RemoveFieldAction nullRemoveFieldAction(true);
48AlterTableHandler::InsertFieldAction nullInsertFieldAction(true);
49AlterTableHandler::MoveFieldPositionAction nullMoveFieldPositionAction(true);
50
51//--------------------------------------------------------
52
53AlterTableHandler::ActionBase::ActionBase(bool null)
54 : m_alteringRequirements(0)
55 , m_order(-1)
56 , m_null(null)
57{
58}
59
60AlterTableHandler::ActionBase::~ActionBase()
61{
62}
63
64AlterTableHandler::ChangeFieldPropertyAction& AlterTableHandler::ActionBase::toChangeFieldPropertyAction()
65{
66 if (dynamic_cast<ChangeFieldPropertyAction*>(this))
67 return *dynamic_cast<ChangeFieldPropertyAction*>(this);
68 return nullChangeFieldPropertyAction;
69}
70
71AlterTableHandler::RemoveFieldAction& AlterTableHandler::ActionBase::toRemoveFieldAction()
72{
73 if (dynamic_cast<RemoveFieldAction*>(this))
74 return *dynamic_cast<RemoveFieldAction*>(this);
75 return nullRemoveFieldAction;
76}
77
78AlterTableHandler::InsertFieldAction& AlterTableHandler::ActionBase::toInsertFieldAction()
79{
80 if (dynamic_cast<InsertFieldAction*>(this))
81 return *dynamic_cast<InsertFieldAction*>(this);
82 return nullInsertFieldAction;
83}
84
85AlterTableHandler::MoveFieldPositionAction& AlterTableHandler::ActionBase::toMoveFieldPositionAction()
86{
87 if (dynamic_cast<MoveFieldPositionAction*>(this))
88 return *dynamic_cast<MoveFieldPositionAction*>(this);
89 return nullMoveFieldPositionAction;
90}
91
92//--------------------------------------------------------
93
94AlterTableHandler::FieldActionBase::FieldActionBase(const TQString& fieldName, int uid)
95 : ActionBase()
96 , m_fieldUID(uid)
97 , m_fieldName(fieldName)
98{
99}
100
101AlterTableHandler::FieldActionBase::FieldActionBase(bool)
102 : ActionBase(true)
103 , m_fieldUID(-1)
104{
105}
106
107AlterTableHandler::FieldActionBase::~FieldActionBase()
108{
109}
110
111//--------------------------------------------------------
112
113static KStaticDeleter< TQMap<TQCString,int> > KexiDB_alteringTypeForProperty_deleter;
114TQMap<TQCString,int> *KexiDB_alteringTypeForProperty = 0;
115
116int AlterTableHandler::alteringTypeForProperty(const TQCString& propertyName)
117{
118 if (!KexiDB_alteringTypeForProperty) {
119 KexiDB_alteringTypeForProperty_deleter.setObject( KexiDB_alteringTypeForProperty,
120 new TQMap<TQCString,int>() );
121#define I(name, type) \
122 KexiDB_alteringTypeForProperty->insert(TQCString(name).lower(), (int)AlterTableHandler::type)
123#define I2(name, type1, type2) \
124 flag = (int)AlterTableHandler::type1|(int)AlterTableHandler::type2; \
125 if (flag & AlterTableHandler::PhysicalAlteringRequired) \
126 flag |= AlterTableHandler::MainSchemaAlteringRequired; \
127 KexiDB_alteringTypeForProperty->insert(TQCString(name).lower(), flag)
128
129 /* useful links:
130 http://dev.mysql.com/doc/refman/5.0/en/create-table.html
131 */
132 // ExtendedSchemaAlteringRequired is here because when the field is renamed,
133 // we need to do the same rename in extended table schema: <field name="...">
134 int flag;
135 I2("name", PhysicalAlteringRequired, MainSchemaAlteringRequired);
137 I("caption", MainSchemaAlteringRequired);
138 I("description", MainSchemaAlteringRequired);
139 I2("unsigned", PhysicalAlteringRequired, DataConversionRequired); // always?
140 I2("length", PhysicalAlteringRequired, DataConversionRequired); // always?
141 I2("precision", PhysicalAlteringRequired, DataConversionRequired); // always?
142 I("width", MainSchemaAlteringRequired);
143 // defaultValue: depends on backend, for mysql it can only by a constant or now()...
144 // -- should we look at Driver here?
145#ifdef KEXI_NO_UNFINISHED
147 I("defaultValue", MainSchemaAlteringRequired);
148#else
149 I2("defaultValue", PhysicalAlteringRequired, MainSchemaAlteringRequired);
150#endif
152 I2("unique", PhysicalAlteringRequired, DataConversionRequired); // we may want to add an Index here
153 I2("notNull", PhysicalAlteringRequired, DataConversionRequired); // we may want to add an Index here
154 // allowEmpty: only support it just at kexi level? maybe there is a backend that supports this?
155 I2("allowEmpty", PhysicalAlteringRequired, MainSchemaAlteringRequired);
156 I2("autoIncrement", PhysicalAlteringRequired, DataConversionRequired); // data conversion may be hard here
157 I2("indexed", PhysicalAlteringRequired, DataConversionRequired); // we may want to add an Index here
158
159 // easier cases follow...
160 I("visibleDecimalPlaces", ExtendedSchemaAlteringRequired);
161
162 // lookup-field-related properties...
163/*moved to KexiDB::isExtendedTableFieldProperty()
164 I("boundColumn", ExtendedSchemaAlteringRequired);
165 I("rowSource", ExtendedSchemaAlteringRequired);
166 I("rowSourceType", ExtendedSchemaAlteringRequired);
167 I("rowSourceValues", ExtendedSchemaAlteringRequired);
168 I("visibleColumn", ExtendedSchemaAlteringRequired);
169 I("columnWidths", ExtendedSchemaAlteringRequired);
170 I("showColumnHeaders", ExtendedSchemaAlteringRequired);
171 I("listRows", ExtendedSchemaAlteringRequired);
172 I("limitToList", ExtendedSchemaAlteringRequired);
173 I("displayWidget", ExtendedSchemaAlteringRequired);*/
174
175 //more to come...
176#undef I
177#undef I2
178 }
179 const int res = (*KexiDB_alteringTypeForProperty)[propertyName.lower()];
180 if (res == 0) {
181 if (KexiDB::isExtendedTableFieldProperty(propertyName))
182 return (int)ExtendedSchemaAlteringRequired;
183 KexiDBWarn << TQString("AlterTableHandler::alteringTypeForProperty(): property \"%1\" not found!")
184 .arg(propertyName.data()) << endl;
185 }
186 return res;
187}
188
189//---
190
191AlterTableHandler::ChangeFieldPropertyAction::ChangeFieldPropertyAction(
192 const TQString& fieldName, const TQString& propertyName, const TQVariant& newValue, int uid)
193 : FieldActionBase(fieldName, uid)
194 , m_propertyName(propertyName)
195 , m_newValue(newValue)
196{
197}
198
199AlterTableHandler::ChangeFieldPropertyAction::ChangeFieldPropertyAction(bool)
200 : FieldActionBase(true)
201{
202}
203
204AlterTableHandler::ChangeFieldPropertyAction::~ChangeFieldPropertyAction()
205{
206}
207
208void AlterTableHandler::ChangeFieldPropertyAction::updateAlteringRequirements()
209{
210// m_alteringRequirements = ???;
211 setAlteringRequirements( alteringTypeForProperty( m_propertyName.latin1() ) );
212}
213
214TQString AlterTableHandler::ChangeFieldPropertyAction::debugString(const DebugOptions& debugOptions)
215{
216 TQString s = TQString("Set \"%1\" property for table field \"%2\" to \"%3\"")
217 .arg(m_propertyName).arg(fieldName()).arg(m_newValue.toString());
218 if (debugOptions.showUID)
219 s.append(TQString(" (UID=%1)").arg(m_fieldUID));
220 return s;
221}
222
223static AlterTableHandler::ActionDict* createActionDict(
224 AlterTableHandler::ActionDictDict &fieldActions, int forFieldUID )
225{
227 dict->setAutoDelete(true);
228 fieldActions.insert( forFieldUID, dict );
229 return dict;
230}
231
232static void debugAction(AlterTableHandler::ActionBase *action, int nestingLevel,
233 bool simulate, const TQString& prependString = TQString(), TQString* debugTarget = 0)
234{
235 TQString debugString;
236 if (!debugTarget)
237 debugString = prependString;
238 if (action) {
239 AlterTableHandler::ActionBase::DebugOptions debugOptions;
240 debugOptions.showUID = debugTarget==0;
241 debugOptions.showFieldDebug = debugTarget!=0;
242 debugString += action->debugString( debugOptions );
243 }
244 else {
245 if (!debugTarget)
246 debugString += "[No action]"; //hmm
247 }
248 if (debugTarget) {
249 if (!debugString.isEmpty())
250 *debugTarget += debugString + '\n';
251 }
252 else {
253 KexiDBDbg << debugString << endl;
254#ifdef KEXI_DEBUG_GUI
255 if (simulate)
256 KexiUtils::addAlterTableActionDebug(debugString, nestingLevel);
257#endif
258 }
259}
260
261static void debugActionDict(AlterTableHandler::ActionDict *dict, int fieldUID, bool simulate)
262{
263 TQString fieldName;
264 AlterTableHandler::ActionDictIterator it(*dict);
265 if (dynamic_cast<AlterTableHandler::FieldActionBase*>(it.current())) //retrieve field name from the 1st related action
266 fieldName = dynamic_cast<AlterTableHandler::FieldActionBase*>(it.current())->fieldName();
267 else
268 fieldName = "??";
269 TQString dbg = TQString("Action dict for field \"%1\" (%2, UID=%3):")
270 .arg(fieldName).arg(dict->count()).arg(fieldUID);
271 KexiDBDbg << dbg << endl;
272#ifdef KEXI_DEBUG_GUI
273 if (simulate)
274 KexiUtils::addAlterTableActionDebug(dbg, 1);
275#endif
276 for (;it.current(); ++it) {
277 debugAction(it.current(), 2, simulate);
278 }
279}
280
281static void debugFieldActions(const AlterTableHandler::ActionDictDict &fieldActions, bool simulate)
282{
283#ifdef KEXI_DEBUG_GUI
284 if (simulate)
285 KexiUtils::addAlterTableActionDebug("** Simplified Field Actions:");
286#endif
287 for (AlterTableHandler::ActionDictDictIterator it(fieldActions); it.current(); ++it) {
288 debugActionDict(it.current(), it.currentKey(), simulate);
289 }
290}
291
313{
314 ActionDict *actionsLikeThis = fieldActions[ uid() ];
315 if (m_propertyName=="name") {
316 // Case 1. special: name1 -> name2, i.e. rename action
317 TQString newName( newValue().toString() );
318 // try to find rename(newName, otherName) action
319 ActionBase *renameActionLikeThis = actionsLikeThis ? actionsLikeThis->find( "name" ) : 0;
320 if (dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)) {
321 // 1. instead of having rename(fieldName(), newValue()) action,
322 // let's have rename(fieldName(), otherName) action
323 dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)->m_newValue
324 = dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)->m_newValue;
325/* AlterTableHandler::ChangeFieldPropertyAction* newRenameAction
326 = new AlterTableHandler::ChangeFieldPropertyAction( *this );
327 newRenameAction->m_newValue = dynamic_cast<ChangeFieldPropertyAction*>(renameActionLikeThis)->m_newValue;
328 // (m_order is the same as in newAction)
329 // replace prev. rename action (if any)
330 actionsLikeThis->remove( "name" );
331 ActionDict *adict = fieldActions[ fieldName().latin1() ];
332 if (!adict)
333 adict = createActionDict( fieldActions, fieldName() );
334 adict->insert(m_propertyName.latin1(), newRenameAction);*/
335 }
336 else {
337 ActionBase *removeActionForThisField = actionsLikeThis ? actionsLikeThis->find( ":remove:" ) : 0;
338 if (removeActionForThisField) {
339 //if this field is going to be removed, jsut change the action's field name
340 // and do not add a new action
341 }
342 else {
343 //just insert a copy of the rename action
344 if (!actionsLikeThis)
345 actionsLikeThis = createActionDict( fieldActions, uid() ); //fieldName() );
348 KexiDBDbg << "ChangeFieldPropertyAction::simplifyActions(): insert into '"
349 << fieldName() << "' dict:" << newRenameAction->debugString() << endl;
350 actionsLikeThis->insert( m_propertyName.latin1(), newRenameAction );
351 return;
352 }
353 }
354 if (actionsLikeThis) {
355 // Case 1b. change "field name" information to fieldName() in any action that
356 // is related to newName
357 // e.g. if there is setCaption("B", "captionA") action after rename("A","B"),
358 // replace setCaption action with setCaption("A", "captionA")
359 foreach_dict (ActionDictIterator, it, *actionsLikeThis) {
360 dynamic_cast<FieldActionBase*>(it.current())->setFieldName( fieldName() );
361 }
362 }
363 return;
364 }
365 ActionBase *removeActionForThisField = actionsLikeThis ? actionsLikeThis->find( ":remove:" ) : 0;
366 if (removeActionForThisField) {
367 //if this field is going to be removed, do not add a new action
368 return;
369 }
370 // Case 2. other cases: just give up with adding this "intermediate" action
371 // so, e.g. [ setCaption(A, "captionA"), setCaption(A, "captionB") ]
372 // becomes: [ setCaption(A, "captionB") ]
373 // because adding this action does nothing
374 ActionDict *nextActionsLikeThis = fieldActions[ uid() ]; //fieldName().latin1() ];
375 if (!nextActionsLikeThis || !nextActionsLikeThis->find( m_propertyName.latin1() )) {
376 //no such action, add this
379 if (!nextActionsLikeThis)
380 nextActionsLikeThis = createActionDict( fieldActions, uid() );//fieldName() );
381 nextActionsLikeThis->insert( m_propertyName.latin1(), newAction );
382 }
383}
384
386{
387 Q_UNUSED(fieldActions);
388 return fieldName().lower() == m_newValue.toString().lower();
389}
390
391tristate AlterTableHandler::ChangeFieldPropertyAction::updateTableSchema(TableSchema &table, Field* field,
392 TQMap<TQString, TQString>& fieldMap)
393{
394 //1. Simpler cases first: changes that do not affect table schema at all
395 // "caption", "description", "width", "visibleDecimalPlaces"
396 if (SchemaAlteringRequired & alteringTypeForProperty(m_propertyName.latin1())) {
397 bool result = KexiDB::setFieldProperty(*field, m_propertyName.latin1(), newValue());
398 return result;
399 }
400
401 if (m_propertyName=="name") {
402 if (fieldMap[ field->name() ] == field->name())
403 fieldMap.remove( field->name() );
404 fieldMap.insert( newValue().toString(), field->name() );
405 table.renameField(field, newValue().toString());
406 return true;
407 }
408 return cancelled;
409}
410
414{
415 Q_UNUSED(conn);
416 Field *field = table.field( fieldName() );
417 if (!field) {
419 return false;
420 }
421 bool result;
422 //1. Simpler cases first: changes that do not affect table schema at all
423 // "caption", "description", "width", "visibleDecimalPlaces"
424 if (SchemaAlteringRequired & alteringTypeForProperty(m_propertyName.latin1())) {
425 result = KexiDB::setFieldProperty(*field, m_propertyName.latin1(), newValue());
426 return result;
427 }
428
429//todo
430return true;
431
432 //2. Harder cases, that often require special care
433 if (m_propertyName=="name") {
434 /*mysql:
435 A. Get real field type (it's safer):
436 let <TYPE> be the 2nd "Type" column from result of "DESCRIBE tablename oldfieldname"
437 ( http://dev.mysql.com/doc/refman/5.0/en/describe.html )
438 B. Run "ALTER TABLE tablename CHANGE oldfieldname newfieldname <TYPE>";
439 ( http://dev.mysql.com/doc/refman/5.0/en/alter-table.html )
440 */
441 }
442 if (m_propertyName=="type") {
443 /*mysql:
444 A. Like A. for "name" property above
445 B. Construct <TYPE> string, eg. "varchar(50)" using the driver
446 C. Like B. for "name" property above
447 (mysql then truncate the values for changes like varchar -> integer,
448 and properly convert the values for changes like integer -> varchar)
449
450 TODO: more cases to check
451 */
452 }
453 if (m_propertyName=="length") {
454 //use "select max( length(o_name) ) from kexi__Objects"
455
456 }
457 if (m_propertyName=="primaryKey") {
459 }
460
461/*
462 "name", "unsigned", "precision",
463 "defaultValue", "primaryKey", "unique", "notNull", "allowEmpty",
464 "autoIncrement", "indexed",
465
466
467 bool result = KexiDB::setFieldProperty(*field, m_propertyName.latin1(), newValue());
468*/
469 return result;
470}
471
472//--------------------------------------------------------
473
474AlterTableHandler::RemoveFieldAction::RemoveFieldAction(const TQString& fieldName, int uid)
475 : FieldActionBase(fieldName, uid)
476{
477}
478
479AlterTableHandler::RemoveFieldAction::RemoveFieldAction(bool)
480 : FieldActionBase(true)
481{
482}
483
484AlterTableHandler::RemoveFieldAction::~RemoveFieldAction()
485{
486}
487
495
496TQString AlterTableHandler::RemoveFieldAction::debugString(const DebugOptions& debugOptions)
497{
498 TQString s = TQString("Remove table field \"%1\"").arg(fieldName());
499 if (debugOptions.showUID)
500 s.append(TQString(" (UID=%1)").arg(uid()));
501 return s;
502}
503
511{
515 ActionDict *actionsLikeThis = fieldActions[ uid() ]; //fieldName().latin1() ];
516 if (!actionsLikeThis)
517 actionsLikeThis = createActionDict( fieldActions, uid() ); //fieldName() );
518 actionsLikeThis->insert( ":remove:", newAction ); //special
519}
520
521tristate AlterTableHandler::RemoveFieldAction::updateTableSchema(TableSchema &table, Field* field,
522 TQMap<TQString, TQString>& fieldMap)
523{
524 fieldMap.remove( field->name() );
525 table.removeField(field);
526 return true;
527}
528
530{
531 Q_UNUSED(conn);
532 Q_UNUSED(table);
534 return true;
535}
536
537//--------------------------------------------------------
538
539AlterTableHandler::InsertFieldAction::InsertFieldAction(int fieldIndex, KexiDB::Field *field, int uid)
540 : FieldActionBase(field->name(), uid)
541 , m_index(fieldIndex)
542 , m_field(0)
543{
544 Q_ASSERT(field);
545 setField(field);
546}
547
548AlterTableHandler::InsertFieldAction::InsertFieldAction(const InsertFieldAction& action)
549 : FieldActionBase(action) //action.fieldName(), action.uid())
550 , m_index(action.index())
551{
552 m_field = new KexiDB::Field( action.field() );
553}
554
555AlterTableHandler::InsertFieldAction::InsertFieldAction(bool)
556 : FieldActionBase(true)
557 , m_index(0)
558 , m_field(0)
559{
560}
561
562AlterTableHandler::InsertFieldAction::~InsertFieldAction()
563{
564 delete m_field;
565}
566
567void AlterTableHandler::InsertFieldAction::setField(KexiDB::Field* field)
568{
569 if (m_field)
570 delete m_field;
571 m_field = field;
572 setFieldName(m_field ? m_field->name() : TQString());
573}
574
582
583TQString AlterTableHandler::InsertFieldAction::debugString(const DebugOptions& debugOptions)
584{
585 TQString s = TQString("Insert table field \"%1\" at position %2")
586 .arg(m_field->name()).arg(m_index);
587 if (debugOptions.showUID)
588 s.append(TQString(" (UID=%1)").arg(m_fieldUID));
589 if (debugOptions.showFieldDebug)
590 s.append(TQString(" (%1)").arg(m_field->debugString()));
591 return s;
592}
593
607{
608 // Try to find actions related to this action
609 ActionDict *actionsForThisField = fieldActions[ uid() ]; //m_field->name().latin1() ];
610
611 ActionBase *removeActionForThisField = actionsForThisField ? actionsForThisField->find( ":remove:" ) : 0;
612 if (removeActionForThisField) {
613 //if this field is going to be removed, do not add a new action
614 //and remove the "Remove" action
615 actionsForThisField->remove(":remove:");
616 return;
617 }
618 if (actionsForThisField) {
619 //collect property values that have to be changed in this field
620 TQMap<TQCString, TQVariant> values;
621 for (ActionDictIterator it(*actionsForThisField); it.current();) {
622 ChangeFieldPropertyAction* changePropertyAction = dynamic_cast<ChangeFieldPropertyAction*>(it.current());
623 if (changePropertyAction) {
624 //if this field is going to be renamed, also update fieldName()
625 if (changePropertyAction->propertyName()=="name") {
626 setFieldName(changePropertyAction->newValue().toString());
627 }
628 values.insert( changePropertyAction->propertyName().latin1(), changePropertyAction->newValue() );
629 //the subsequent "change property" action is no longer needed
630 actionsForThisField->remove(changePropertyAction->propertyName().latin1());
631 }
632 else {
633 ++it;
634 }
635 }
636 if (!values.isEmpty()) {
637 //update field, so it will be created as one step
638 KexiDB::Field *f = new KexiDB::Field( field() );
639 if (KexiDB::setFieldProperties( *f, values )) {
640 //field() = f;
641 setField( f );
642 field().debug();
643#ifdef KEXI_DEBUG_GUI
644 KexiUtils::addAlterTableActionDebug(
645 TQString("** Property-set actions moved to field definition itself:\n")+field().debugString(), 0);
646#endif
647 }
648 else {
649#ifdef KEXI_DEBUG_GUI
650 KexiUtils::addAlterTableActionDebug(
651 TQString("** Failed to set properties for field ")+field().debugString(), 0);
652#endif
653 KexiDBWarn << "AlterTableHandler::InsertFieldAction::simplifyActions(): KexiDB::setFieldProperties() failed!" << endl;
654 delete f;
655 }
656 }
657 }
658 //ok, insert this action
662 if (!actionsForThisField)
663 actionsForThisField = createActionDict( fieldActions, uid() );
664 actionsForThisField->insert( ":insert:", newAction ); //special
665}
666
668 TQMap<TQString, TQString>& fieldMap)
669{
670 //in most cases we won't add the field to fieldMap
671 Q_UNUSED(field);
673 fieldMap.remove( this->field().name() );
674 table.insertField(index(), new Field(this->field()));
675 return true;
676}
677
679{
680 Q_UNUSED(conn);
681 Q_UNUSED(table);
683 return true;
684}
685
686//--------------------------------------------------------
687
688AlterTableHandler::MoveFieldPositionAction::MoveFieldPositionAction(
689 int fieldIndex, const TQString& fieldName, int uid)
690 : FieldActionBase(fieldName, uid)
691 , m_index(fieldIndex)
692{
693}
694
695AlterTableHandler::MoveFieldPositionAction::MoveFieldPositionAction(bool)
696 : FieldActionBase(true)
697{
698}
699
700AlterTableHandler::MoveFieldPositionAction::~MoveFieldPositionAction()
701{
702}
703
709
710TQString AlterTableHandler::MoveFieldPositionAction::debugString(const DebugOptions& debugOptions)
711{
712 TQString s = TQString("Move table field \"%1\" to position %2")
713 .arg(fieldName()).arg(m_index);
714 if (debugOptions.showUID)
715 s.append(TQString(" (UID=%1)").arg(uid()));
716 return s;
717}
718
720{
721 Q_UNUSED(fieldActions);
723}
724
726{
727 Q_UNUSED(conn);
728 Q_UNUSED(table);
730 return true;
731}
732
733//--------------------------------------------------------
734
735AlterTableHandler::AlterTableHandler(Connection &conn)
736 : Object()
737 , d( new Private() )
738{
739 d->conn = &conn;
740}
741
742AlterTableHandler::~AlterTableHandler()
743{
744 delete d;
745}
746
748{
749 d->actions.append(action);
750}
751
752AlterTableHandler& AlterTableHandler::operator<< ( ActionBase* action )
753{
754 d->actions.append(action);
755 return *this;
756}
757
759{
760 return d->actions;
761}
762
764{
765 d->actions.remove( d->actions.at(index) );
766}
767
769{
770 d->actions.clear();
771}
772
774{
775 d->actions = actions;
776}
777
779{
780 KexiDBDbg << "AlterTableHandler's actions:" << endl;
781 foreach_list (ActionListIterator, it, d->actions)
782 it.current()->debug();
783}
784
786{
787 args.result = false;
788 if (!d->conn) {
790 return 0;
791 }
792 if (d->conn->isReadOnly()) {
794 return 0;
795 }
796 if (!d->conn->isDatabaseUsed()) {
798 return 0;
799 }
800 TableSchema *oldTable = d->conn->tableSchema(tableName);
801 if (!oldTable) {
803 return 0;
804 }
805
806 if (!args.debugString)
807 debug();
808
809 // Find a sum of requirements...
810 int allActionsCount = 0;
811 for(ActionListIterator it(d->actions); it.current(); ++it, allActionsCount++) {
812 it.current()->updateAlteringRequirements();
813 it.current()->m_order = allActionsCount;
814 }
815
816 /* Simplify actions list if possible and check for errors
817
818 How to do it?
819 - track property changes/deletions in reversed order
820 - reduce intermediate actions
821
822 Trivial example 1:
823 *action1: "rename field a to b"
824 *action2: "rename field b to c"
825 *action3: "rename field c to d"
826
827 After reduction:
828 *action1: "rename field a to d"
829 Summing up: we have tracked what happens to field curently named "d"
830 and eventually discovered that it was originally named "a".
831
832 Trivial example 2:
833 *action1: "rename field a to b"
834 *action2: "rename field b to c"
835 *action3: "remove field b"
836 After reduction:
837 *action3: "remove field b"
838 Summing up: we have noticed that field "b" has beed eventually removed
839 so we needed to find all actions related to this field and remove them.
840 This is good optimization, as some of the eventually removed actions would
841 be difficult to perform and/or costly, what would be a waste of resources
842 and a source of unwanted questions sent to the user.
843 */
844
845 ActionListIterator it(d->actions);
846
847 // Fields-related actions.
848 ActionDictDict fieldActions(3001);
849 fieldActions.setAutoDelete(true);
850 ActionBase* action;
851 for(it.toLast(); (action = it.current()); --it) {
852 action->simplifyActions( fieldActions );
853 }
854
855 if (!args.debugString)
856 debugFieldActions(fieldActions, args.simulate);
857
858 // Prepare actions for execution ----
859 // - Sort actions by order
860 ActionVector actionsVector(allActionsCount);
861 int currentActionsCount = 0; //some actions may be removed
862 args.requirements = 0;
863 TQDict<char> fieldsWithChangedMainSchema(997); // Used to collect fields with changed main schema.
864 // This will be used when recreateTable is false to update kexi__fields
865 for (ActionDictDictIterator it(fieldActions); it.current(); ++it) {
866 for (AlterTableHandler::ActionDictIterator it2(*it.current());it2.current(); ++it2, currentActionsCount++) {
867 if (it2.current()->shouldBeRemoved(fieldActions))
868 continue;
869 actionsVector.insert( it2.current()->m_order, it2.current() );
870 // a sum of requirements...
871 const int r = it2.current()->alteringRequirements();
872 args.requirements |= r;
873 if (r & MainSchemaAlteringRequired && dynamic_cast<ChangeFieldPropertyAction*>(it2.current())) {
874 // Remember, this will be used when recreateTable is false to update kexi__fields, below.
875 fieldsWithChangedMainSchema.insert(
876 dynamic_cast<ChangeFieldPropertyAction*>(it2.current())->fieldName(), (char*)1 );
877 }
878 }
879 }
880 // - Debug
881 TQString dbg = TQString("** Overall altering requirements: %1").arg(args.requirements);
882 KexiDBDbg << dbg << endl;
883
884 if (args.onlyComputeRequirements) {
885 args.result = true;
886 return 0;
887 }
888
889 const bool recreateTable = (args.requirements & PhysicalAlteringRequired);
890
891#ifdef KEXI_DEBUG_GUI
892 if (args.simulate)
893 KexiUtils::addAlterTableActionDebug(dbg, 0);
894#endif
895 dbg = TQString("** Ordered, simplified actions (%1, was %2):").arg(currentActionsCount).arg(allActionsCount);
896 KexiDBDbg << dbg << endl;
897#ifdef KEXI_DEBUG_GUI
898 if (args.simulate)
899 KexiUtils::addAlterTableActionDebug(dbg, 0);
900#endif
901 for (int i=0; i<allActionsCount; i++) {
902 debugAction(actionsVector[i], 1, args.simulate, TQString("%1: ").arg(i+1), args.debugString);
903 }
904
905 if (args.requirements == 0) {//nothing to do
906 args.result = true;
907 return oldTable;
908 }
909 if (args.simulate) {//do not execute
910 args.result = true;
911 return oldTable;
912 }
913// @todo transaction!
914
915 // Create new TableSchema
916 TableSchema *newTable = recreateTable ? new TableSchema(*oldTable, false) : oldTable;
917 // find nonexisting temp name for new table schema
918 if (recreateTable) {
919 TQString tempDestTableName;
920 while (true) {
921 tempDestTableName = TQString("%1_temp%2%3").arg(newTable->name()).arg(TQString::number(rand(), 16)).arg(TQString::number(rand(), 16));
922 if (!d->conn->tableSchema(tempDestTableName))
923 break;
924 }
925 newTable->setName( tempDestTableName );
926 }
927 oldTable->debug();
928 if (recreateTable && !args.debugString)
929 newTable->debug();
930
931 // Update table schema in memory ----
932 int lastUID = -1;
933 Field *currentField = 0;
934 TQMap<TQString, TQString> fieldMap; // a map from new value to old value
935 foreach_list( Field::ListIterator, it, newTable->fieldsIterator() ) {
936 fieldMap.insert( it.current()->name(), it.current()->name() );
937 }
938 for (int i=0; i<allActionsCount; i++) {
939 action = actionsVector[i];
940 if (!action)
941 continue;
942 //remember the current Field object because soon we may be unable to find it by name:
943 FieldActionBase *fieldAction = dynamic_cast<FieldActionBase*>(action);
944 if (!fieldAction) {
945 currentField = 0;
946 }
947 else {
948 if (lastUID != fieldAction->uid()) {
949 currentField = newTable->field( fieldAction->fieldName() );
950 lastUID = currentField ? fieldAction->uid() : -1;
951 }
952 InsertFieldAction *insertFieldAction = dynamic_cast<InsertFieldAction*>(action);
953 if (insertFieldAction && insertFieldAction->index()>(int)newTable->fieldCount()) {
954 //update index: there can be empty rows
955 insertFieldAction->setIndex(newTable->fieldCount());
956 }
957 }
958 //if (!currentField)
959 // continue;
960 args.result = action->updateTableSchema(*newTable, currentField, fieldMap);
961 if (args.result!=true) {
962 if (recreateTable)
963 delete newTable;
964 return 0;
965 }
966 }
967
968 if (recreateTable) {
969 // Create the destination table with temporary name
970 if (!d->conn->createTable( newTable, false )) {
971 setError(d->conn);
972 delete newTable;
973 args.result = false;
974 return 0;
975 }
976 }
977
978#if 0//todo
979 // Execute actions ----
980 for (int i=0; i<allActionsCount; i++) {
981 action = actionsVector[i];
982 if (!action)
983 continue;
984 args.result = action->execute(*d->conn, *newTable);
985 if (!args.result || ~args.result) {
987 args.result = false;
988 return 0;
989 }
990 }
991#endif
992
993 // update extended table schema after executing the actions
994 if (!d->conn->storeExtendedTableSchemaData(*newTable)) {
996 setError(d->conn);
998 args.result = false;
999 return 0;
1000 }
1001
1002 if (recreateTable) {
1003 // Copy the data:
1004 // Build "INSERT INTO ... SELECT FROM ..." SQL statement
1005 // The order is based on the order of the source table fields.
1006 // Notes:
1007 // -Some source fields can be skipped in case when there are deleted fields.
1008 // -Some destination fields can be skipped in case when there
1009 // are new empty fields without fixed/default value.
1010 TQString sql = TQString("INSERT INTO %1 (").arg(d->conn->escapeIdentifier(newTable->name()));
1011 //insert list of dest. fields
1012 bool first = true;
1013 TQString sourceFields;
1014 foreach_list( Field::ListIterator, it, newTable->fieldsIterator() ) {
1015 Field * const f = it.current();
1016 TQString renamedFieldName( fieldMap[ f->name() ] );
1017 TQString sourceSQLString;
1018 if (!renamedFieldName.isEmpty()) {
1019 //this field should be renamed
1020 sourceSQLString = d->conn->escapeIdentifier(renamedFieldName);
1021 }
1022 else if (!f->defaultValue().isNull()) {
1023 //this field has a default value defined
1027 sourceSQLString = d->conn->driver()->valueToSQL( f->type(), f->defaultValue() );
1028 }
1029 else if (f->isNotNull()) {
1030 //this field cannot be null
1031 sourceSQLString = d->conn->driver()->valueToSQL(
1032 f->type(), KexiDB::emptyValueForType( f->type() ) );
1033 }
1034 else if (f->isNotEmpty()) {
1035 //this field cannot be empty - use any nonempty value..., e.g. " " for text or 0 for number
1036 sourceSQLString = d->conn->driver()->valueToSQL(
1037 f->type(), KexiDB::notEmptyValueForType( f->type() ) );
1038 }
1041
1042 if (!sourceSQLString.isEmpty()) {
1043 if (first) {
1044 first = false;
1045 }
1046 else {
1047 sql.append( ", " );
1048 sourceFields.append( ", " );
1049 }
1050 sql.append( d->conn->escapeIdentifier( f->name() ) );
1051 sourceFields.append( sourceSQLString );
1052 }
1053 }
1054 sql.append(TQString(") SELECT ") + sourceFields + " FROM " + oldTable->name());
1055 KexiDBDbg << " ** " << sql << endl;
1056 if (!d->conn->executeSQL( sql )) {
1057 setError(d->conn);
1059 args.result = false;
1060 return 0;
1061 }
1062
1063 const TQString oldTableName = oldTable->name();
1064/* args.result = d->conn->dropTable( oldTable );
1065 if (!args.result || ~args.result) {
1066 setError(d->conn);
1068 return 0;
1069 }
1070 oldTable = 0;*/
1071
1072 // Replace the old table with the new one (oldTable will be destroyed)
1073 if (!d->conn->alterTableName(*newTable, oldTableName, true /*replace*/)) {
1074 setError(d->conn);
1076 args.result = false;
1077 return 0;
1078 }
1079 oldTable = 0;
1080 }
1081
1082 if (!recreateTable) {
1083 if ((MainSchemaAlteringRequired & args.requirements) && !fieldsWithChangedMainSchema.isEmpty()) {
1084 //update main schema (kexi__fields) for changed fields
1085 foreach_list(TQDictIterator<char>, it, fieldsWithChangedMainSchema) {
1086 Field *f = newTable->field( it.currentKey() );
1087 if (f) {
1088 if (!d->conn->storeMainFieldSchema(f)) {
1089 setError(d->conn);
1091 args.result = false;
1092 return 0;
1093 }
1094 }
1095 }
1096 }
1097 }
1098
1099 args.result = true;
1100 return newTable;
1101}
1102
1103/*TableSchema* AlterTableHandler::execute(const TQString& tableName, tristate &result, bool simulate)
1104{
1105 return executeInternal( tableName, result, simulate, 0 );
1106}
1107
1108tristate AlterTableHandler::simulateExecution(const TQString& tableName, TQString& debugString)
1109{
1110 tristate result;
1111 (void)executeInternal( tableName, result, true//simulate
1112 , &debugString );
1113 return result;
1114}
1115*/
bool showUID
true if UID should be added to the action debug string (the default)
Definition alter.h:178
Abstract base class used for implementing all the AlterTable actions.
Definition alter.h:157
void setAlteringRequirements(int alteringRequirements)
Sets requirements for altering; used internally by AlterTableHandler object.
Definition alter.h:193
virtual void simplifyActions(ActionDictDict &fieldActions)
Definition alter.h:203
virtual void simplifyActions(ActionDictDict &fieldActions)
Definition alter.cpp:312
virtual bool shouldBeRemoved(ActionDictDict &fieldActions)
Definition alter.cpp:385
virtual tristate execute(Connection &conn, TableSchema &table)
Performs physical execution of this action.
Definition alter.cpp:413
Arguments for AlterTableHandler::execute().
Definition alter.h:400
Abstract base class used for implementing table field-related actions.
Definition alter.h:232
void setFieldName(const TQString &fieldName)
Sets field name for this action.
Definition alter.h:253
Defines an action for inserting a single table field.
Definition alter.h:322
virtual void simplifyActions(ActionDictDict &fieldActions)
Definition alter.cpp:606
virtual tristate updateTableSchema(TableSchema &table, Field *field, TQMap< TQString, TQString > &fieldMap)
Definition alter.cpp:667
virtual tristate execute(Connection &conn, TableSchema &table)
Performs physical execution of this action.
Definition alter.cpp:678
virtual void simplifyActions(ActionDictDict &fieldActions)
Definition alter.cpp:719
virtual tristate execute(Connection &conn, TableSchema &table)
Performs physical execution of this action.
Definition alter.cpp:725
Defines an action for removing a single table field.
Definition alter.h:301
virtual void simplifyActions(ActionDictDict &fieldActions)
Definition alter.cpp:510
virtual tristate execute(Connection &conn, TableSchema &table)
Performs physical execution of this action.
Definition alter.cpp:529
void setActions(const ActionList &actions)
Definition alter.cpp:773
TQPtrList< ActionBase > ActionList
Defines a type for action list.
Definition alter.h:151
const ActionList & actions() const
Definition alter.cpp:758
TableSchema * execute(const TQString &tableName, ExecutionArguments &args)
Definition alter.cpp:785
TQIntDict< ActionDict > ActionDictDict
for collecting groups of actions by field UID
Definition alter.h:145
TQPtrListIterator< ActionBase > ActionListIterator
Defines a type for action list's iterator.
Definition alter.h:154
void debug()
Displays debug information about all actions collected by the handler.
Definition alter.cpp:778
static int alteringTypeForProperty(const TQCString &propertyName)
Definition alter.cpp:116
void addAction(ActionBase *action)
Definition alter.cpp:747
TQAsciiDict< ActionBase > ActionDict
for collecting actions related to a single field
Definition alter.h:144
TQPtrVector< ActionBase > ActionVector
for collecting actions related to a single field
Definition alter.h:148
void removeAction(int index)
Definition alter.cpp:763
AlterTableHandler & operator<<(ActionBase *action)
Definition alter.cpp:752
Provides database connection, allowing queries and data modification.
Definition connection.h:62
uint fieldCount() const
Definition fieldlist.h:58
Field * field(uint id)
Definition fieldlist.h:76
Meta-data for a field.
Definition field.h:73
bool isNotNull() const
Definition field.h:243
Type type() const
Definition field.cpp:134
TQPtrListIterator< Field > ListIterator
iterator for list of fields
Definition field.h:77
bool isNotEmpty() const
Definition field.h:246
TQVariant defaultValue() const
Definition field.h:326
virtual void setError(int code=ERR_OTHER, const TQString &msg=TQString())
Definition object.cpp:53
virtual FieldList & insertField(uint index, Field *field)
virtual void removeField(KexiDB::Field *field)
3-state logical type with three values: true, false and cancelled and convenient operators.
Definition tristate.h:100
High-level database connectivity library with database backend drivers.
KEXI_DB_EXPORT TQVariant emptyValueForType(Field::Type type)
KEXI_DB_EXPORT TQVariant notEmptyValueForType(Field::Type type)
KEXI_DB_EXPORT bool setFieldProperty(Field &field, const TQCString &propertyName, const TQVariant &value)
KEXI_DB_EXPORT bool isExtendedTableFieldProperty(const TQCString &propertyName)
KEXI_DB_EXPORT bool setFieldProperties(Field &field, const TQMap< TQCString, TQVariant > &values)