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