KDb

KDbAlter.h
1/* This file is part of the KDE project
2 Copyright (C) 2006-2012 Jarosław Staniek <staniek@kde.org>
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#ifndef KDB_ALTER_H
21#define KDB_ALTER_H
22
23#include "KDbUtils.h"
24#include "KDbResult.h"
25#include "KDbTristate.h"
26#include "KDbTableSchema.h"
27
28#include <QList>
29#include <QHash>
30
31class KDbConnection;
32
33//! @short A tool for handling altering database table schema.
34/*! In relational (and other) databases, table schema altering is not an easy task.
35 It may be considered as easy if there is no data that user wants to keep while
36 the table schema is altered. Otherwise, if the table is alredy filled with data,
37 there could be no easy algorithm like:
38 1. Drop existing table
39 2. Create new one with altered schema.
40
41 Instead, more complex algorithm is needed. To perform the table schema alteration,
42 a list of well defined atomic operations is used as a "recipe".
43
44 1. Look at the current data, and:
45 1.1. analyze what values will be removed (in case of impossible conversion
46 or table field removal);
47 1.2. analyze what values can be converted (e.g. from numeric types to text), and so on.
48 2. Optimize the atomic actions knowing that sometimes a compilation of one action
49 and another that's opposite to the first means "do nothing". The optimization
50 is a simulating of actions' execution.
51 For example, when both action A="change field name from 'city' to 'town'"
52 and action B="change field name from 'town' to 'city'" is specified, the compilation
53 of the actions means "change field name from 'city' to 'city'", what is a NULL action.
54 On the other hand, we need to execute all the actions on the destination table
55 in proper order, and not just drop them. For the mentioned example, between actions
56 A and B there can be an action like C="change the type of field 'city' to LongText".
57 If A and B were simply removed, C would become invalid (there is no 'city' field).
58 3. Ask user whether she agrees with the results of analysis mentioned in 1.
59 3.2. Additionally, it may be possible to get some hints from the user, as humans usually
60 know more about logic behind the altered table schema than any machine.
61 If the user provided hints about the altering, apply them to the actions list.
62 4. Create (empty) destination table schema with temporary name, using
63 the information collected so far.
64 5. Copy the data from the source to destionation table. Convert values,
65 move them between fields, using the information collected.
66 6. Remove the source table.
67 7. Rename the destination table to the name previously assigned for the source table.
68
69 Notes:
70 * The actions 4 to 7 should be performed within a database transaction.
71 * [todo] We want to take care about database relationships as well.
72 For example, is a table field is removed, relationships related to this field should
73 be also removed (similar rules as in the Query Designer).
74 * Especially, care about primary keys and uniquess (indices). Recreate them when needed.
75 The problem could be if such analysis may require to fetch the entire table data
76 to the client side. Use "SELECT INTO" statements if possible to avoid such a treat.
77
78 The KDbAlterTableHandler is used in Kexi's Table Designer.
79 Already opened KDbConnection object is needed.
80
81 Use case:
82 @code
83 KDbConnection *conn = ...
84
85 // add some actions (in reality this is performed by tracking user's actions)
86 // Actions 1, 2 will require physical table altering PhysicalAltering
87 // Action 3 will only require changes in kexi__fields
88 // Action 4 will only require changes in extended table schema written in kexi__objectdata
89 AlterTable::ActionList list;
90
91 // 1. rename the "city" field to "town"
92 list << new ChangeFieldPropertyAction("city", "name", "town")
93
94 // 2. change type of "town" field to "LongText"
95 << new ChangeFieldPropertyAction("town", "type", "LongText")
96
97 // 3. set caption of "town" field to "Town"
98 << new ChangeFieldPropertyAction("town", "caption", "Town")
99
100 // 4. set visible decimal places to 4 for "cost" field
101 << new ChangeFieldPropertyAction("cost", "visibleDecimalPlaces", 4)
102
103 KDbAlterTableHandler::execute( *conn );
104
105 @endcode
106
107 Actions for Alter
108*/
109class KDB_EXPORT KDbAlterTableHandler : public KDbResultable
110{
111public:
113 class RemoveFieldAction;
114 class InsertFieldAction;
116
117 //! Defines flags for possible altering requirements; can be combined.
119 /*! Physical table altering is required; e.g. ALTER TABLE ADD COLUMN. */
120 PhysicalAlteringRequired = 1,
121
122 /*! Data conversion is required; e.g. converting integer
123 values to string after changing column type from integer to text. */
124 DataConversionRequired = 2,
125
126 /*! Changes to the main table schema (in kexi__fields) required,
127 this does not require physical changes for the table;
128 e.g. changing value of the "caption" or "description" property. */
129 MainSchemaAlteringRequired = 4,
130
131 /*! Only changes to extended table schema required,
132 this does not require physical changes for the table;
133 e.g. changing value of the "visibleDecimalPlaces" property
134 or any of the custom properties. */
135 ExtendedSchemaAlteringRequired = 8,
136
137 /*! Convenience flag, changes to the main or extended schema is required. */
138 SchemaAlteringRequired = ExtendedSchemaAlteringRequired | MainSchemaAlteringRequired
139 };
140
141 class ActionBase;
142 //! For collecting actions related to a single field
144 typedef KDbUtils::AutodeletedHash<int, ActionDict*> ActionDictDict; //!< for collecting groups of actions by field UID
145 typedef QHash<QByteArray, ActionBase*>::Iterator ActionDictIterator;
146 typedef QHash<QByteArray, ActionBase*>::ConstIterator ActionDictConstIterator;
147 typedef QHash<int, ActionDict*>::Iterator ActionDictDictIterator;
148 typedef QHash<int, ActionDict*>::ConstIterator ActionDictDictConstIterator;
149 typedef QVector<ActionBase*> ActionsVector; //!< for collecting actions related to a single field
150
151 //! Defines a type for action list.
153
154 //! Defines a type for action list's iterator.
156
157 //! Abstract base class used for implementing all the AlterTable actions.
158 class KDB_EXPORT ActionBase
159 {
160 public:
161 virtual ~ActionBase();
162
163 ChangeFieldPropertyAction& toChangeFieldPropertyAction();
164 RemoveFieldAction& toRemoveFieldAction();
165 InsertFieldAction& toInsertFieldAction();
166 MoveFieldPositionAction& toMoveFieldPositionAction();
167
168 //! @return true if the action is NULL; used in the Table Designer
169 //! for temporarily collecting actions that have no effect at all.
170 inline bool isNull() const {
171 return m_null;
172 }
173
174 //! Controls debug options for actions. Used in debugString() and debug().
176 {
177 public:
178 inline DebugOptions() : showUID(true), showFieldDebug(false) {}
179
180 //! true if UID should be added to the action debug string (the default)
182
183 //! true if the field associated with the action (if exists) should
184 //! be appended to the debug string (default is false)
186 };
187
188 inline virtual QString debugString(const DebugOptions& debugOptions = DebugOptions()) {
189 Q_UNUSED(debugOptions); return QLatin1String("ActionBase");
190 }
191
192//! @todo add QDebug operator <<
193 void debug(const DebugOptions& debugOptions = DebugOptions());
194
195 protected:
196 //! @internal, used for constructing null action
197 explicit ActionBase(bool null);
198
199 //! Sets requirements for altering; used internally by KDbAlterTableHandler object
200 inline void setAlteringRequirements(int alteringRequirements) {
201 m_alteringRequirements = alteringRequirements;
202 }
203
204 inline int alteringRequirements() const {
205 return m_alteringRequirements;
206 }
207
208 inline virtual void updateAlteringRequirements() {}
209
210 /*! Simplifies @a fieldActions dictionary. If this action has to be inserted
211 Into the dictionary, an ActionDict is created first and then a copy of this action
212 is inserted into it. */
213 inline virtual void simplifyActions(ActionDictDict *fieldActions) {
214 Q_UNUSED(fieldActions);
215 }
216
217 /*! After calling simplifyActions() for each action,
218 shouldBeRemoved() is called for them as an additional step.
219 This is used for ChangeFieldPropertyAction items so actions
220 that do not change property values are removed. */
221 inline virtual bool shouldBeRemoved(ActionDictDict *fieldActions) {
222 Q_UNUSED(fieldActions); return false;
223 }
224
225 inline virtual tristate updateTableSchema(KDbTableSchema* table, KDbField* field,
226 QHash<QString, QString>* fieldHash) {
227 Q_UNUSED(table); Q_UNUSED(field); Q_UNUSED(fieldHash); return true;
228 }
229
230 private:
231 //! Performs physical execution of this action.
232 inline virtual tristate execute(KDbConnection* /*conn*/, KDbTableSchema* /*table*/) {
233 return true;
234 }
235
236 //! requirements for altering; used internally by KDbAlterTableHandler object
237 int m_alteringRequirements;
238
239 //! @internal used for "simplify" algorithm
240 int m_order;
241
242 const bool m_null;
243
244 friend class KDbAlterTableHandler;
245 };
246
247 //! Abstract base class used for implementing table field-related actions.
248 class KDB_EXPORT FieldActionBase : public ActionBase
249 {
250 public:
251 FieldActionBase(const QString& fieldName, int uid);
252 ~FieldActionBase() override;
253
254 //! @return field name for this action
255 inline QString fieldName() const {
256 return m_fieldName;
257 }
258
259 /*! @return field's unique identifier
260 This id is needed because in the meantime there can be more than one
261 field sharing the same name, so we need to identify them unambiguously.
262 After the (valid) altering is completed all the names will be unique.
263
264 Example scenario when user exchanged the field names:
265 1. At the beginning: [field A], [field B]
266 2. Rename the 1st field to B: [field B], [field B]
267 3. Rename the 2nd field to A: [field B], [field A] */
268 inline int uid() const {
269 return m_fieldUID;
270 }
271
272 //! Sets field name for this action
273 inline void setFieldName(const QString& fieldName) {
274 m_fieldName = fieldName;
275 }
276
277 protected:
278 //! @internal, used for constructing null action
279 explicit FieldActionBase(bool null);
280
281 //! field's unique identifier, @see uid()
283 private:
284 QString m_fieldName;
285 };
286
287 /*! Defines an action for changing a single property value of a table field.
288 Supported properties are currently:
289 "name", "type", "caption", "description", "unsigned", "maxLength", "precision",
290 "defaultWidth", "defaultValue", "primaryKey", "unique", "notNull", "allowEmpty",
291 "autoIncrement", "indexed", "visibleDecimalPlaces"
292
293 More to come.
294 */
296 {
297 public:
298 ChangeFieldPropertyAction(const QString& fieldName,
299 const QString& propertyName, const QVariant& newValue, int uid);
300
301 //! Creates null action
303
305
306 inline QString propertyName() const {
307 return m_propertyName;
308 }
309 inline QVariant newValue() const {
310 return m_newValue;
311 }
312 QString debugString(const DebugOptions& debugOptions = DebugOptions()) override;
313
314 void simplifyActions(ActionDictDict *fieldActions) override;
315
316 bool shouldBeRemoved(ActionDictDict *fieldActions) override;
317
318 tristate updateTableSchema(KDbTableSchema *table, KDbField *field,
319 QHash<QString, QString> *fieldHash) override;
320
321 protected:
322 //! @internal, used for constructing null action
323 explicit ChangeFieldPropertyAction(bool null);
324
325 void updateAlteringRequirements() override;
326
327 //! Performs physical execution of this action.
328 tristate execute(KDbConnection* conn, KDbTableSchema* table) override;
329
330 QString m_propertyName;
331 QVariant m_newValue;
332 };
333
334 //! Defines an action for removing a single table field.
335 class KDB_EXPORT RemoveFieldAction : public FieldActionBase
336 {
337 public:
338 RemoveFieldAction(const QString& fieldName, int uid);
339
340 ~RemoveFieldAction() override;
341
342 QString debugString(const DebugOptions& debugOptions = DebugOptions()) override;
343
344 void simplifyActions(ActionDictDict *fieldActions) override;
345
346 tristate updateTableSchema(KDbTableSchema *table, KDbField *field,
347 QHash<QString, QString> *fieldHash) override;
348
349 protected:
350 //! @internal, used for constructing null action
351 explicit RemoveFieldAction(bool null);
352
353 void updateAlteringRequirements() override;
354
355 //! Performs physical execution of this action.
356 tristate execute(KDbConnection* conn, KDbTableSchema* table) override;
357 };
358
359 //! Defines an action for inserting a single table field.
360 class KDB_EXPORT InsertFieldAction : public FieldActionBase
361 {
362 public:
363 InsertFieldAction(int fieldIndex, KDbField *newField, int uid);
364
365 //! copy ctor
367
368 //! Creates null action
370
371 ~InsertFieldAction() override;
372
373 inline int index() const {
374 return m_index;
375 }
376 inline void setIndex(int index) {
377 m_index = index;
378 }
379 inline const KDbField* field() const {
380 return m_field;
381 }
382 void setField(KDbField* field);
383 QString debugString(const DebugOptions& debugOptions = DebugOptions()) override;
384
385 void simplifyActions(ActionDictDict *fieldActions) override;
386
387 tristate updateTableSchema(KDbTableSchema *table, KDbField *field,
388 QHash<QString, QString> *fieldHash) override;
389
390 protected:
391 //! @internal, used for constructing null action
392 explicit InsertFieldAction(bool null);
393
394 void updateAlteringRequirements() override;
395
396 //! Performs physical execution of this action.
397 tristate execute(KDbConnection* conn, KDbTableSchema* table) override;
398
399 int m_index;
400
401 private:
402 KDbField *m_field;
403 };
404
405 /*! Defines an action for moving a single table field to a different
406 position within table schema. */
407 class KDB_EXPORT MoveFieldPositionAction : public FieldActionBase
408 {
409 public:
410 MoveFieldPositionAction(int fieldIndex, const QString& fieldName, int uid);
411
412 ~MoveFieldPositionAction() override;
413
414 inline int index() const {
415 return m_index;
416 }
417 QString debugString(const DebugOptions& debugOptions = DebugOptions()) override;
418
419 void simplifyActions(ActionDictDict *fieldActions) override;
420
421 protected:
422 //! @internal, used for constructing null action
423 explicit MoveFieldPositionAction(bool null);
424
425 void updateAlteringRequirements() override;
426
427 //! Performs physical execution of this action.
428 tristate execute(KDbConnection* conn, KDbTableSchema* table) override;
429
430 int m_index;
431 };
432
433 explicit KDbAlterTableHandler(KDbConnection* conn);
434
435 ~KDbAlterTableHandler() override;
436
437 /*! Appends @a action for the alter table tool. */
438 void addAction(ActionBase* action);
439
440 /*! Provided for convenience, @see addAction(const ActionBase& action). */
441 KDbAlterTableHandler& operator<< (ActionBase* action);
442
443 /*! Removes an action from the alter table tool at index @a index. */
444 void removeAction(int index);
445
446 /*! Removes all actions from the alter table tool. */
447 void clear();
448
449 /*! Sets @a actions for the alter table tool. Previous actions are cleared.
450 @a actions will be owned by the KDbAlterTableHandler object. */
451 void setActions(const ActionList& actions);
452
453 /*! @return a list of actions for this AlterTable object.
454 Use ActionBase::ListIterator to iterate over the list items. */
455 const ActionList& actions() const;
456
457 //! Arguments for KDbAlterTableHandler::execute().
459 {
460 public:
461 inline ExecutionArguments()
462 : debugString(nullptr)
463 , requirements(0)
464 , result(false)
465 , simulate(false)
466 , onlyComputeRequirements(false) {
467 }
468 /*! If not 0, debug is directed here. Used only in the alter table test suite. */
470 /*! Requrements computed, a combination of AlteringRequirements values. */
472 /*! Set to true on success, to false on failure. */
474 /*! Used only in the alter table test suite. */
476 /*! Set to true if requirements should be computed
477 and the execute() method should return afterwards. */
479 private:
480 Q_DISABLE_COPY(ExecutionArguments)
481 };
482
483 /*! Performs table alteration using predefined actions for table named @a tableName,
484 assuming it already exists. The KDbConnection object passed to the constructor must exist,
485 must be connected and a database must be used. The connection must not be read-only.
486
487 If args.simulate is true, the execution is only simulated, i.e. al lactions are processed
488 like for regular execution but no changes are performed physically.
489 This mode is used only for debugging purposes.
490
491 @todo For some cases, table schema can completely change, so it will be needed
492 to refresh all objects depending on it.
493 Implement this!
494
495 Sets args.result to true on success, to false on failure or when the above requirements are not met
496 (then, you can get a detailed error message from KDbObject).
497 When the action has been cancelled (stopped), args.result is set to cancelled value.
498 If args.debugString is not 0, it will be filled with debugging output.
499 @return the new table schema object created as a result of schema altering.
500 The old table is returned if recreating table schema was not necessary or args.simulate is true.
501 0 is returned if args.result is not true. */
502 KDbTableSchema* execute(const QString& tableName, ExecutionArguments* args);
503
504 //! Displays debug information about all actions collected by the handler.
505 void debug();
506
507 /*! Like execute() with simulate set to true, but debug is directed to debugString.
508 This function is used only in the alter table test suite. */
509// tristate simulateExecution(const QString& tableName, QString& debugString);
510
511 /*! Helper. @return a combination of AlteringRequirements values decribing altering type required
512 when a given property field's @a propertyName is altered.
513 Used internally KDbAlterTableHandler. Moreover it can be also used in the Table Designer's code
514 as a temporary replacement before KDbAlterTableHandler is fully implemented.
515 Thus, it is possible to identify properties that have no PhysicalAlteringRequired flag set
516 (e.g. caption or extended properties like visibleDecimalPlaces. */
517 static int alteringTypeForProperty(const QByteArray& propertyName);
518
519private:
520 Q_DISABLE_COPY(KDbAlterTableHandler)
521 class Private;
522 Private * const d;
523};
524
525#endif
Controls debug options for actions. Used in debugString() and debug().
Definition KDbAlter.h:176
bool showFieldDebug
true if the field associated with the action (if exists) should be appended to the debug string (defa...
Definition KDbAlter.h:185
bool showUID
true if UID should be added to the action debug string (the default)
Definition KDbAlter.h:181
Abstract base class used for implementing all the AlterTable actions.
Definition KDbAlter.h:159
virtual void simplifyActions(ActionDictDict *fieldActions)
Definition KDbAlter.h:213
virtual bool shouldBeRemoved(ActionDictDict *fieldActions)
Definition KDbAlter.h:221
void setAlteringRequirements(int alteringRequirements)
Sets requirements for altering; used internally by KDbAlterTableHandler object.
Definition KDbAlter.h:200
Arguments for KDbAlterTableHandler::execute().
Definition KDbAlter.h:459
Abstract base class used for implementing table field-related actions.
Definition KDbAlter.h:249
void setFieldName(const QString &fieldName)
Sets field name for this action.
Definition KDbAlter.h:273
int m_fieldUID
field's unique identifier,
Definition KDbAlter.h:282
Defines an action for inserting a single table field.
Definition KDbAlter.h:361
Defines an action for removing a single table field.
Definition KDbAlter.h:336
A tool for handling altering database table schema.
Definition KDbAlter.h:110
KDbUtils::AutodeletedHash< QByteArray, ActionBase * > ActionDict
For collecting actions related to a single field.
Definition KDbAlter.h:143
QVector< ActionBase * > ActionsVector
for collecting actions related to a single field
Definition KDbAlter.h:149
AlteringRequirements
Defines flags for possible altering requirements; can be combined.
Definition KDbAlter.h:118
KDbUtils::AutodeletedHash< int, ActionDict * > ActionDictDict
for collecting groups of actions by field UID
Definition KDbAlter.h:144
QList< ActionBase * >::ConstIterator ActionListIterator
Defines a type for action list's iterator.
Definition KDbAlter.h:155
QList< ActionBase * > ActionList
Defines a type for action list.
Definition KDbAlter.h:152
Provides database connection, allowing queries and data modification.
Meta-data for a field.
Definition KDbField.h:72
Interface for classes providing a result.
Autodeleting hash.
Definition KDbUtils.h:179
3-state logical type with three values: true, false and cancelled and convenient operators.
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:59 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.