source: trunk/src/sql/models/qsqlrelationaltablemodel.cpp@ 636

Last change on this file since 636 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 23.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtSql module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsqlrelationaltablemodel.h"
43
44#include "qhash.h"
45#include "qstringlist.h"
46#include "qsqldatabase.h"
47#include "qsqldriver.h"
48#include "qsqlerror.h"
49#include "qsqlfield.h"
50#include "qsqlindex.h"
51#include "qsqlquery.h"
52#include "qsqlrecord.h"
53
54#include "qsqltablemodel_p.h"
55
56#include "qdebug.h"
57
58QT_BEGIN_NAMESPACE
59
60/*!
61 \class QSqlRelation
62 \brief The QSqlRelation class stores information about an SQL foreign key.
63
64 QSqlRelation is a helper class for QSqlRelationalTableModel. See
65 QSqlRelationalTableModel::setRelation() and
66 QSqlRelationalTableModel::relation() for details.
67
68 \sa QSqlRelationalTableModel, QSqlRelationalDelegate,
69 {Relational Table Model Example}
70*/
71
72/*!
73 \fn QSqlRelation::QSqlRelation()
74
75 Constructs an invalid QSqlRelation object.
76
77 For such an object, the tableName(), indexColumn(), and
78 displayColumn() functions return an empty string.
79
80 \sa isValid()
81*/
82
83/*!
84 \fn QSqlRelation::QSqlRelation(const QString &tableName, const QString &indexColumn,
85 const QString &displayColumn)
86
87 Constructs a QSqlRelation object, where \a tableName is the SQL
88 table name to which a foreign key refers, \a indexColumn is the
89 foreign key, and \a displayColumn is the field that should be
90 presented to the user.
91
92 \sa tableName(), indexColumn(), displayColumn()
93*/
94
95/*!
96 \fn QString QSqlRelation::tableName() const
97
98 Returns the name of the table to which a foreign key refers.
99*/
100
101/*!
102 \fn QString QSqlRelation::indexColumn() const
103
104 Returns the index column from table tableName() to which a
105 foreign key refers.
106*/
107
108/*!
109 \fn QString QSqlRelation::displayColumn() const
110
111 Returns the column from table tableName() that should be
112 presented to the user instead of a foreign key.
113*/
114
115/*!
116 \fn bool QSqlRelation::isValid() const
117
118 Returns true if the QSqlRelation object is valid; otherwise
119 returns false.
120*/
121
122struct QRelation
123{
124 public:
125 QRelation(): model(0),m_parent(0),m_dictInitialized(false){}
126 void init(QSqlRelationalTableModel *parent, const QSqlRelation &relation);
127
128 void populateModel();
129
130 bool isDictionaryInitialized();
131 void populateDictionary();
132 void clearDictionary();
133
134 void clear();
135 bool isValid();
136
137 QSqlRelation rel;
138 QSqlTableModel *model;
139 QHash<QString, QVariant> dictionary;//maps keys to display values
140
141 private:
142 QSqlRelationalTableModel *m_parent;
143 bool m_dictInitialized;
144};
145
146/*
147 A QRelation must be initialized before it is considered valid.
148 Note: population of the model and dictionary are kept separate
149 from initialization, and are populated on an as needed basis.
150*/
151void QRelation::init(QSqlRelationalTableModel *parent, const QSqlRelation &relation)
152{
153 Q_ASSERT(parent != NULL);
154 m_parent = parent;
155 rel = relation;
156}
157
158void QRelation::populateModel()
159{
160 if (!isValid())
161 return;
162 Q_ASSERT(m_parent != NULL);
163
164 if (!model) {
165 model = new QSqlTableModel(m_parent, m_parent->database());
166 model->setTable(rel.tableName());
167 model->select();
168 }
169}
170
171bool QRelation::isDictionaryInitialized()
172{
173 return m_dictInitialized;
174}
175
176void QRelation::populateDictionary()
177{
178 if (!isValid())
179 return;
180
181 if (model == NULL)
182 populateModel();
183
184 QSqlRecord record;
185 QString indexColumn;
186 QString displayColumn;
187 for (int i=0; i < model->rowCount(); ++i) {
188 record = model->record(i);
189
190 indexColumn = rel.indexColumn();
191 if (m_parent->database().driver()->isIdentifierEscaped(indexColumn, QSqlDriver::FieldName))
192 indexColumn = m_parent->database().driver()->stripDelimiters(indexColumn, QSqlDriver::FieldName);
193
194 displayColumn = rel.displayColumn();
195 if (m_parent->database().driver()->isIdentifierEscaped(displayColumn, QSqlDriver::FieldName))
196 displayColumn = m_parent->database().driver()->stripDelimiters(displayColumn, QSqlDriver::FieldName);
197
198 dictionary[record.field(indexColumn).value().toString()] =
199 record.field(displayColumn).value();
200 }
201 m_dictInitialized = true;
202}
203
204void QRelation::clearDictionary()
205{
206 dictionary.clear();
207 m_dictInitialized = false;
208}
209
210void QRelation::clear()
211{
212 delete model;
213 model = 0;
214 clearDictionary();
215}
216
217bool QRelation::isValid()
218{
219 return (rel.isValid() && m_parent != NULL);
220}
221
222class QSqlRelationalTableModelPrivate: public QSqlTableModelPrivate
223{
224 Q_DECLARE_PUBLIC(QSqlRelationalTableModel)
225public:
226 QSqlRelationalTableModelPrivate()
227 : QSqlTableModelPrivate()
228 {}
229 QString relationField(const QString &tableName, const QString &fieldName) const;
230
231 int nameToIndex(const QString &name) const;
232 mutable QVector<QRelation> relations;
233 QSqlRecord baseRec; // the record without relations
234 void clearChanges();
235 void clearEditBuffer();
236 void clearCache();
237 void revertCachedRow(int row);
238
239 void translateFieldNames(int row, QSqlRecord &values) const;
240};
241
242static void qAppendWhereClause(QString &query, const QString &clause1, const QString &clause2)
243{
244 if (clause1.isEmpty() && clause2.isEmpty())
245 return;
246 if (clause1.isEmpty() || clause2.isEmpty())
247 query.append(QLatin1String(" WHERE (")).append(clause1).append(clause2);
248 else
249 query.append(QLatin1String(" WHERE (")).append(clause1).append(
250 QLatin1String(") AND (")).append(clause2);
251 query.append(QLatin1String(") "));
252}
253
254void QSqlRelationalTableModelPrivate::clearChanges()
255{
256 for (int i = 0; i < relations.count(); ++i) {
257 QRelation &rel = relations[i];
258 rel.clear();
259 }
260}
261
262void QSqlRelationalTableModelPrivate::revertCachedRow(int row)
263{
264 QSqlTableModelPrivate::revertCachedRow(row);
265}
266
267int QSqlRelationalTableModelPrivate::nameToIndex(const QString &name) const
268{
269 QString fieldname = name;
270 if (db.driver()->isIdentifierEscaped(fieldname, QSqlDriver::FieldName))
271 fieldname = db.driver()->stripDelimiters(fieldname, QSqlDriver::FieldName);
272 return baseRec.indexOf(fieldname);
273}
274
275void QSqlRelationalTableModelPrivate::clearEditBuffer()
276{
277 editBuffer = baseRec;
278}
279
280/*!
281 \reimp
282*/
283void QSqlRelationalTableModelPrivate::clearCache()
284{
285 for (int i = 0; i < relations.count(); ++i)
286 relations[i].clearDictionary();
287
288 QSqlTableModelPrivate::clearCache();
289}
290
291/*!
292 \class QSqlRelationalTableModel
293 \brief The QSqlRelationalTableModel class provides an editable
294 data model for a single database table, with foreign key support.
295
296 \ingroup database
297 \inmodule QtSql
298
299 QSqlRelationalTableModel acts like QSqlTableModel, but allows
300 columns to be set as foreign keys into other database tables.
301
302 \table
303 \row \o \inlineimage noforeignkeys.png
304 \o \inlineimage foreignkeys.png
305 \endtable
306
307 The screenshot on the left shows a plain QSqlTableModel in a
308 QTableView. Foreign keys (\c city and \c country) aren't resolved
309 to human-readable values. The screenshot on the right shows a
310 QSqlRelationalTableModel, with foreign keys resolved into
311 human-readable text strings.
312
313 The following code snippet shows how the QSqlRelationalTableModel
314 was set up:
315
316 \snippet examples/sql/relationaltablemodel/relationaltablemodel.cpp 0
317 \codeline
318 \snippet examples/sql/relationaltablemodel/relationaltablemodel.cpp 1
319 \snippet examples/sql/relationaltablemodel/relationaltablemodel.cpp 2
320
321 The setRelation() function calls establish a relationship between
322 two tables. The first call specifies that column 2 in table \c
323 employee is a foreign key that maps with field \c id of table \c
324 city, and that the view should present the \c{city}'s \c name
325 field to the user. The second call does something similar with
326 column 3.
327
328 If you use a read-write QSqlRelationalTableModel, you probably
329 want to use QSqlRelationalDelegate on the view. Unlike the default
330 delegate, QSqlRelationalDelegate provides a combobox for fields
331 that are foreign keys into other tables. To use the class, simply
332 call QAbstractItemView::setItemDelegate() on the view with an
333 instance of QSqlRelationalDelegate:
334
335 \snippet examples/sql/relationaltablemodel/relationaltablemodel.cpp 4
336
337 The \l{sql/relationaltablemodel} example illustrates how to use
338 QSqlRelationalTableModel in conjunction with
339 QSqlRelationalDelegate to provide tables with foreigh key
340 support.
341
342 \image relationaltable.png
343
344 Notes:
345
346 \list
347 \o The table must have a primary key declared.
348 \o The table's primary key may not contain a relation to
349 another table.
350 \o If a relational table contains keys that refer to non-existent
351 rows in the referenced table, the rows containing the invalid
352 keys will not be exposed through the model. The user or the
353 database is responsible for keeping referential integrity.
354 \o If a relation's display column name is also used as a column
355 name in the main table, or if it is used as display column
356 name in more than one relation it will be aliased. The alias is
357 is the relation's table name and display column name joined
358 by an underscore (e.g. tablename_columnname). All occurrences
359 of the duplicate display column name are aliased when
360 duplication is detected, but no aliasing is done to the column
361 names in the main table. The aliasing doesn't affect
362 QSqlRelation, so QSqlRelation::displayColumn() will return the
363 original display column name, but QSqlRecord::fieldName() will
364 return aliases.
365 \o When using setData() the role should always be Qt::EditRole,
366 and when using data() the role should always be Qt::DisplayRole.
367 \endlist
368
369 \sa QSqlRelation, QSqlRelationalDelegate,
370 {Relational Table Model Example}
371*/
372
373
374/*!
375 Creates an empty QSqlRelationalTableModel and sets the parent to \a parent
376 and the database connection to \a db. If \a db is not valid, the
377 default database connection will be used.
378*/
379QSqlRelationalTableModel::QSqlRelationalTableModel(QObject *parent, QSqlDatabase db)
380 : QSqlTableModel(*new QSqlRelationalTableModelPrivate, parent, db)
381{
382}
383
384/*!
385 Destroys the object and frees any allocated resources.
386*/
387QSqlRelationalTableModel::~QSqlRelationalTableModel()
388{
389}
390
391/*!
392 \reimp
393*/
394QVariant QSqlRelationalTableModel::data(const QModelIndex &index, int role) const
395{
396 Q_D(const QSqlRelationalTableModel);
397
398 if (role == Qt::DisplayRole && index.column() > 0 && index.column() < d->relations.count() &&
399 d->relations.value(index.column()).isValid()) {
400 QRelation &relation = d->relations[index.column()];
401 if (!relation.isDictionaryInitialized())
402 relation.populateDictionary();
403
404 //only perform a dictionary lookup for the display value
405 //when the value at index has been changed or added.
406 //At an unmodified index, the underlying model will
407 //already have the correct display value.
408 QVariant v;
409 switch (d->strategy) {
410 case OnFieldChange:
411 break;
412 case OnRowChange:
413 if (index.row() == d->editIndex || index.row() == d->insertIndex) {
414 v = d->editBuffer.value(index.column());
415 }
416 break;
417 case OnManualSubmit:
418 const QSqlTableModelPrivate::ModifiedRow row = d->cache.value(index.row());
419 v = row.rec.value(index.column());
420 break;
421 }
422 if (v.isValid())
423 return relation.dictionary[v.toString()];
424 }
425 return QSqlTableModel::data(index, role);
426}
427
428/*!
429 Sets the data for the \a role in the item with the specified \a
430 index to the \a value given. Depending on the edit strategy, the
431 value might be applied to the database at once, or it may be
432 cached in the model.
433
434 Returns true if the value could be set, or false on error (for
435 example, if \a index is out of bounds).
436
437 For relational columns, \a value must be the index, not the
438 display value. The index must also exist in the referenced
439 table, otherwise the function returns false.
440
441 \sa editStrategy(), data(), submit(), revertRow()
442*/
443bool QSqlRelationalTableModel::setData(const QModelIndex &index, const QVariant &value,
444 int role)
445{
446 Q_D(QSqlRelationalTableModel);
447 if ( role == Qt::EditRole && index.column() > 0 && index.column() < d->relations.count()
448 && d->relations.value(index.column()).isValid()) {
449 QRelation &relation = d->relations[index.column()];
450 if (!relation.isDictionaryInitialized())
451 relation.populateDictionary();
452 if (!relation.dictionary.contains(value.toString()))
453 return false;
454 }
455 return QSqlTableModel::setData(index, value, role);
456}
457
458/*!
459 Lets the specified \a column be a foreign index specified by \a relation.
460
461 Example:
462
463 \snippet examples/sql/relationaltablemodel/relationaltablemodel.cpp 0
464 \codeline
465 \snippet examples/sql/relationaltablemodel/relationaltablemodel.cpp 1
466
467 The setRelation() call specifies that column 2 in table \c
468 employee is a foreign key that maps with field \c id of table \c
469 city, and that the view should present the \c{city}'s \c name
470 field to the user.
471
472 Note: The table's primary key may not contain a relation to another table.
473
474 \sa relation()
475*/
476void QSqlRelationalTableModel::setRelation(int column, const QSqlRelation &relation)
477{
478 Q_D(QSqlRelationalTableModel);
479 if (column < 0)
480 return;
481 if (d->relations.size() <= column)
482 d->relations.resize(column + 1);
483 d->relations[column].init(this, relation);
484}
485
486/*!
487 Returns the relation for the column \a column, or an invalid
488 relation if no relation is set.
489
490 \sa setRelation(), QSqlRelation::isValid()
491*/
492QSqlRelation QSqlRelationalTableModel::relation(int column) const
493{
494 Q_D(const QSqlRelationalTableModel);
495 return d->relations.value(column).rel;
496}
497
498QString QSqlRelationalTableModelPrivate::relationField(const QString &tableName,
499 const QString &fieldName) const
500{
501 QString ret;
502 ret.reserve(tableName.size() + fieldName.size() + 1);
503 ret.append(tableName).append(QLatin1Char('.')).append(fieldName);
504
505 return ret;
506}
507
508/*!
509 \reimp
510*/
511QString QSqlRelationalTableModel::selectStatement() const
512{
513 Q_D(const QSqlRelationalTableModel);
514 QString query;
515
516 if (tableName().isEmpty())
517 return query;
518 if (d->relations.isEmpty())
519 return QSqlTableModel::selectStatement();
520
521 QString tList;
522 QString fList;
523 QString where;
524
525 QSqlRecord rec = d->baseRec;
526 QStringList tables;
527 const QRelation nullRelation;
528
529 // Count how many times each field name occurs in the record
530 QHash<QString, int> fieldNames;
531 QStringList fieldList;
532 for (int i = 0; i < rec.count(); ++i) {
533 QSqlRelation relation = d->relations.value(i, nullRelation).rel;
534 QString name;
535 if (relation.isValid())
536 {
537 // Count the display column name, not the original foreign key
538 name = relation.displayColumn();
539 if (d->db.driver()->isIdentifierEscaped(name, QSqlDriver::FieldName))
540 name = d->db.driver()->stripDelimiters(name, QSqlDriver::FieldName);
541
542 QSqlRecord rec = database().record(relation.tableName());
543 for (int i = 0; i < rec.count(); ++i) {
544 if (name.compare(rec.fieldName(i), Qt::CaseInsensitive) == 0) {
545 name = rec.fieldName(i);
546 break;
547 }
548 }
549 }
550 else
551 name = rec.fieldName(i);
552 fieldNames.insert(name, fieldNames.value(name, 0) + 1);
553 fieldList.append(name);
554 }
555
556 for (int i = 0; i < rec.count(); ++i) {
557 QSqlRelation relation = d->relations.value(i, nullRelation).rel;
558 if (relation.isValid()) {
559 QString relTableAlias = QString::fromLatin1("relTblAl_%1").arg(i);
560 if (!fList.isEmpty())
561 fList.append(QLatin1String(", "));
562 fList.append(d->relationField(relTableAlias,relation.displayColumn()));
563
564 // If there are duplicate field names they must be aliased
565 if (fieldNames.value(fieldList[i]) > 1) {
566 QString relTableName = relation.tableName().section(QChar::fromLatin1('.'), -1, -1);
567 if (d->db.driver()->isIdentifierEscaped(relTableName, QSqlDriver::TableName))
568 relTableName = d->db.driver()->stripDelimiters(relTableName, QSqlDriver::TableName);
569 QString displayColumn = relation.displayColumn();
570 if (d->db.driver()->isIdentifierEscaped(displayColumn, QSqlDriver::FieldName))
571 displayColumn = d->db.driver()->stripDelimiters(displayColumn, QSqlDriver::FieldName);
572 fList.append(QString::fromLatin1(" AS %1_%2_%3").arg(relTableName).arg(displayColumn).arg(fieldNames.value(fieldList[i])));
573 fieldNames.insert(fieldList[i], fieldNames.value(fieldList[i])-1);
574 }
575
576 // this needs fixing!! the below if is borken.
577 tables.append(relation.tableName().append(QLatin1Char(' ')).append(relTableAlias));
578 if(!where.isEmpty())
579 where.append(QLatin1String(" AND "));
580 where.append(d->relationField(tableName(), d->db.driver()->escapeIdentifier(rec.fieldName(i), QSqlDriver::FieldName)));
581 where.append(QLatin1String(" = "));
582 where.append(d->relationField(relTableAlias, relation.indexColumn()));
583 } else {
584 if (!fList.isEmpty())
585 fList.append(QLatin1String(", "));
586 fList.append(d->relationField(tableName(), d->db.driver()->escapeIdentifier(rec.fieldName(i), QSqlDriver::FieldName)));
587 }
588 }
589 if (!tables.isEmpty())
590 tList.append(tables.join(QLatin1String(", ")));
591 if (fList.isEmpty())
592 return query;
593 if(!tList.isEmpty())
594 tList.prepend(QLatin1String(", "));
595 tList.prepend(tableName());
596 query.append(QLatin1String("SELECT "));
597 query.append(fList).append(QLatin1String(" FROM ")).append(tList);
598 qAppendWhereClause(query, where, filter());
599
600 QString orderBy = orderByClause();
601 if (!orderBy.isEmpty())
602 query.append(QLatin1Char(' ')).append(orderBy);
603
604 return query;
605}
606
607/*!
608 Returns a QSqlTableModel object for accessing the table for which
609 \a column is a foreign key, or 0 if there is no relation for the
610 given \a column.
611
612 The returned object is owned by the QSqlRelationalTableModel.
613
614 \sa setRelation(), relation()
615*/
616QSqlTableModel *QSqlRelationalTableModel::relationModel(int column) const
617{
618 Q_D(const QSqlRelationalTableModel);
619 if ( column < 0 || column >= d->relations.count())
620 return 0;
621
622 QRelation &relation = const_cast<QSqlRelationalTableModelPrivate *>(d)->relations[column];
623 if (!relation.isValid())
624 return 0;
625
626 if (!relation.model)
627 relation.populateModel();
628 return relation.model;
629}
630
631/*!
632 \reimp
633*/
634void QSqlRelationalTableModel::revertRow(int row)
635{
636 QSqlTableModel::revertRow(row);
637}
638
639/*!
640 \reimp
641*/
642void QSqlRelationalTableModel::clear()
643{
644 Q_D(QSqlRelationalTableModel);
645 d->clearChanges();
646 d->relations.clear();
647 QSqlTableModel::clear();
648}
649
650/*!
651 \reimp
652*/
653bool QSqlRelationalTableModel::select()
654{
655 return QSqlTableModel::select();
656}
657
658/*!
659 \reimp
660*/
661void QSqlRelationalTableModel::setTable(const QString &table)
662{
663 Q_D(QSqlRelationalTableModel);
664
665 // memorize the table before applying the relations
666 d->baseRec = d->db.record(table);
667
668 QSqlTableModel::setTable(table);
669}
670
671/*! \internal
672 */
673void QSqlRelationalTableModelPrivate::translateFieldNames(int row, QSqlRecord &values) const
674{
675 Q_Q(const QSqlRelationalTableModel);
676
677 for (int i = 0; i < values.count(); ++i) {
678 int realCol = q->indexInQuery(q->createIndex(row, i)).column();
679 if (realCol != -1 && relations.value(realCol).isValid()) {
680 QVariant v = values.value(i);
681 values.replace(i, baseRec.field(realCol));
682 values.setValue(i, v);
683 }
684 }
685}
686
687/*!
688 \reimp
689*/
690bool QSqlRelationalTableModel::updateRowInTable(int row, const QSqlRecord &values)
691{
692 Q_D(QSqlRelationalTableModel);
693
694 QSqlRecord rec = values;
695 d->translateFieldNames(row, rec);
696
697 return QSqlTableModel::updateRowInTable(row, rec);
698}
699
700/*!
701 \reimp
702*/
703bool QSqlRelationalTableModel::insertRowIntoTable(const QSqlRecord &values)
704{
705 Q_D(QSqlRelationalTableModel);
706
707 QSqlRecord rec = values;
708 d->translateFieldNames(0, rec);
709
710 return QSqlTableModel::insertRowIntoTable(rec);
711}
712
713/*!
714 \reimp
715*/
716QString QSqlRelationalTableModel::orderByClause() const
717{
718 Q_D(const QSqlRelationalTableModel);
719
720 const QSqlRelation rel = d->relations.value(d->sortColumn).rel;
721 if (!rel.isValid())
722 return QSqlTableModel::orderByClause();
723
724 QString s = QLatin1String("ORDER BY ");
725 s.append(d->relationField(QLatin1String("relTblAl_") + QString::number(d->sortColumn),
726 rel.displayColumn()));
727 s += d->sortOrder == Qt::AscendingOrder ? QLatin1String(" ASC") : QLatin1String(" DESC");
728 return s;
729}
730
731/*!
732 \reimp
733*/
734bool QSqlRelationalTableModel::removeColumns(int column, int count, const QModelIndex &parent)
735{
736 Q_D(QSqlRelationalTableModel);
737
738 if (parent.isValid() || column < 0 || column + count > d->rec.count())
739 return false;
740
741 for (int i = 0; i < count; ++i) {
742 d->baseRec.remove(column);
743 if (d->relations.count() > column)
744 d->relations.remove(column);
745 }
746 return QSqlTableModel::removeColumns(column, count, parent);
747}
748
749QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.