KDb

KDbAlter.h
1 /* This file is part of the KDE project
2  Copyright (C) 2006-2012 JarosÅ‚aw Staniek <[email protected]>
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 
31 class 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 */
109 class KDB_EXPORT KDbAlterTableHandler : public KDbResultable
110 {
111 public:
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)
181  bool showUID;
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  */
295  class KDB_EXPORT ChangeFieldPropertyAction : public FieldActionBase
296  {
297  public:
298  ChangeFieldPropertyAction(const QString& fieldName,
299  const QString& propertyName, const QVariant& newValue, int uid);
300 
301  //! Creates null action
303 
304  ~ChangeFieldPropertyAction() override;
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
366  InsertFieldAction(const InsertFieldAction& action);
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). */
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. */
475  bool simulate;
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 
519 private:
520  Q_DISABLE_COPY(KDbAlterTableHandler)
521  class Private;
522  Private * const d;
523 };
524 
525 #endif
KDbUtils::AutodeletedHash< QByteArray, ActionBase * > ActionDict
For collecting actions related to a single field.
Definition: KDbAlter.h:141
Abstract base class used for implementing all the AlterTable actions.
Definition: KDbAlter.h:158
void removeAction(int index)
Definition: KDbAlter.cpp:789
KDbUtils::AutodeletedHash< int, ActionDict * > ActionDictDict
for collecting groups of actions by field UID
Definition: KDbAlter.h:144
QVector< ActionBase * > ActionsVector
for collecting actions related to a single field
Definition: KDbAlter.h:149
const ActionList & actions() const
Definition: KDbAlter.cpp:784
Defines an action for removing a single table field.
Definition: KDbAlter.h:335
bool showUID
true if UID should be added to the action debug string (the default)
Definition: KDbAlter.h:181
virtual bool shouldBeRemoved(ActionDictDict *fieldActions)
Definition: KDbAlter.h:221
Abstract base class used for implementing table field-related actions.
Definition: KDbAlter.h:248
virtual void simplifyActions(ActionDictDict *fieldActions)
Definition: KDbAlter.h:213
Defines an action for inserting a single table field.
Definition: KDbAlter.h:360
Interface for classes providing a result.
void setFieldName(const QString &fieldName)
Sets field name for this action.
Definition: KDbAlter.h:273
void addAction(ActionBase *action)
Definition: KDbAlter.cpp:773
KDbAlterTableHandler & operator<<(ActionBase *action)
Definition: KDbAlter.cpp:778
void setAlteringRequirements(int alteringRequirements)
Sets requirements for altering; used internally by KDbAlterTableHandler object.
Definition: KDbAlter.h:200
void debug(const DebugOptions &debugOptions=DebugOptions())
Definition: KDbAlter.cpp:99
static int alteringTypeForProperty(const QByteArray &propertyName)
Definition: KDbAlter.cpp:181
QList< ActionBase * >::ConstIterator ActionListIterator
Defines a type for action list's iterator.
Definition: KDbAlter.h:155
void setActions(const ActionList &actions)
Definition: KDbAlter.cpp:799
bool showFieldDebug
true if the field associated with the action (if exists) should be appended to the debug string (defa...
Definition: KDbAlter.h:185
Autodeleting hash.
Definition: KDbUtils.h:178
3-state logical type with three values: true, false and cancelled and convenient operators.
Definition: KDbTristate.h:100
int m_fieldUID
field's unique identifier,
Definition: KDbAlter.h:282
Meta-data for a field.
Definition: KDbField.h:71
AlteringRequirements
Defines flags for possible altering requirements; can be combined.
Definition: KDbAlter.h:118
Arguments for KDbAlterTableHandler::execute().
Definition: KDbAlter.h:458
Provides database connection, allowing queries and data modification.
Definition: KDbConnection.h:51
QList< ActionBase * > ActionList
Defines a type for action list.
Definition: KDbAlter.h:152
A tool for handling altering database table schema.
Definition: KDbAlter.h:109
Controls debug options for actions. Used in debugString() and debug().
Definition: KDbAlter.h:175
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Dec 7 2023 04:09:06 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.