~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/sql/models/qsqlquerymodel.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the sql module of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "qsqlquerymodel.h"
 
30
 
 
31
#include <qsqldriver.h>
 
32
#include <qsqlfield.h>
 
33
 
 
34
#include "qsqlquerymodel_p.h"
 
35
 
 
36
#define QSQL_PREFETCH 255
 
37
 
 
38
void QSqlQueryModelPrivate::prefetch(int limit)
 
39
{
 
40
    Q_Q(QSqlQueryModel);
 
41
 
 
42
    if (atEnd || limit <= bottom.row())
 
43
        return;
 
44
 
 
45
    QModelIndex oldBottom = q->createIndex(bottom.row(), 0);
 
46
    QModelIndex newBottom;
 
47
 
 
48
    // try to seek directly
 
49
    if (query.seek(limit)) {
 
50
        newBottom = q->createIndex(limit, bottom.column());
 
51
    } else {
 
52
        // fetch as far as we can
 
53
        if (query.last()) {
 
54
            newBottom = q->createIndex(query.at(), bottom.column());
 
55
        } else {
 
56
            error = query.lastError();
 
57
            newBottom = q->createIndex(-1, bottom.column());
 
58
        }
 
59
        atEnd = true; // this is the end.
 
60
    }
 
61
    if (newBottom.row() > oldBottom.row()) {
 
62
        emit q->beginInsertRows(QModelIndex(), oldBottom.row(), newBottom.row());
 
63
        bottom = newBottom;
 
64
        emit q->endInsertRows();
 
65
    } else {
 
66
        bottom = newBottom;
 
67
    }
 
68
}
 
69
 
 
70
QSqlQueryModelPrivate::~QSqlQueryModelPrivate()
 
71
{
 
72
}
 
73
 
 
74
/*!
 
75
    \class QSqlQueryModel
 
76
    \brief The QSqlQueryModel class provides a read-only data model for SQL
 
77
    result sets.
 
78
 
 
79
    \ingroup database
 
80
    \module sql
 
81
 
 
82
    QSqlQueryModel is a high-level interface for executing SQL
 
83
    statements and traversing the result set. It is built on top of
 
84
    the lower-level QSqlQuery and can be used to provide data to
 
85
    view classes such as QTableView. For example:
 
86
 
 
87
    \quotefromfile snippets/sqldatabase/sqldatabase.cpp
 
88
    \skipto QSqlQueryModel_snippets
 
89
    \skipto QSqlQueryModel *model
 
90
    \printuntil show()
 
91
 
 
92
    We set the model's query, then we set up the labels displayed in
 
93
    the view header.
 
94
 
 
95
    QSqlQueryModel can also be used to access a database
 
96
    programmatically, without binding it to a view:
 
97
 
 
98
    \skipto QSqlQueryModel model;
 
99
    \printuntil int salary =
 
100
 
 
101
    The code snippet above extracts the \c salary field from record 4 in
 
102
    the result set of the query \c{SELECT * from employee}. Assuming
 
103
    that \c salary is column 2, we can rewrite the last line as follows:
 
104
 
 
105
    \skipto int salary =
 
106
    \printline int salary =
 
107
 
 
108
    The model is read-only by default. To make it read-write, you
 
109
    must subclass it and reimplement setData() and flags(). Another
 
110
    option is to use QSqlTableModel, which provides a read-write
 
111
    model based on a single database table.
 
112
 
 
113
    The \l{sql/querymodel} example illustrates how to use
 
114
    QSqlQueryModel to display the result of a query. It also shows
 
115
    how to subclass QSqlQueryModel to customize the contents of the
 
116
    data before showing it to the user, and how to create a
 
117
    read-write model based on QSqlQueryModel.
 
118
 
 
119
    \sa QSqlTableModel, QSqlRelationalTableModel, QSqlQuery,
 
120
        {Model/View Programming}
 
121
*/
 
122
 
 
123
/*!
 
124
    Creates an empty QSqlQueryModel with the given \a parent.
 
125
 */
 
126
QSqlQueryModel::QSqlQueryModel(QObject *parent)
 
127
    : QAbstractTableModel(*new QSqlQueryModelPrivate, parent)
 
128
{
 
129
}
 
130
 
 
131
/*! \internal
 
132
 */
 
133
QSqlQueryModel::QSqlQueryModel(QSqlQueryModelPrivate &dd, QObject *parent)
 
134
    : QAbstractTableModel(dd, parent)
 
135
{
 
136
}
 
137
 
 
138
/*!
 
139
    Destroys the object and frees any allocated resources.
 
140
 
 
141
    \sa clear()
 
142
*/
 
143
QSqlQueryModel::~QSqlQueryModel()
 
144
{
 
145
}
 
146
 
 
147
/*!
 
148
    \reimp
 
149
*/
 
150
void QSqlQueryModel::fetchMore(const QModelIndex &parent)
 
151
{
 
152
    Q_D(QSqlQueryModel);
 
153
    if (parent.isValid())
 
154
        return;
 
155
    d->prefetch(d->bottom.row() + QSQL_PREFETCH);
 
156
}
 
157
 
 
158
/*! \reimp
 
159
 */
 
160
bool QSqlQueryModel::canFetchMore(const QModelIndex &parent) const
 
161
{
 
162
    Q_D(const QSqlQueryModel);
 
163
    return (!parent.isValid() && !d->atEnd);
 
164
}
 
165
 
 
166
/*! \reimp
 
167
 */
 
168
int QSqlQueryModel::rowCount(const QModelIndex &) const
 
169
{
 
170
    Q_D(const QSqlQueryModel);
 
171
    return d->bottom.row() + 1;
 
172
}
 
173
 
 
174
/*! \reimp
 
175
 */
 
176
int QSqlQueryModel::columnCount(const QModelIndex &) const
 
177
{
 
178
    Q_D(const QSqlQueryModel);
 
179
    return d->rec.count();
 
180
}
 
181
 
 
182
/*!
 
183
    Returns the value for the specified \a item and \a role.
 
184
 
 
185
    If \a item is out of bounds or if an error occurred, an invalid
 
186
    QVariant is returned.
 
187
 
 
188
    \sa lastError()
 
189
*/
 
190
QVariant QSqlQueryModel::data(const QModelIndex &item, int role) const
 
191
{
 
192
    Q_D(const QSqlQueryModel);
 
193
    if (!item.isValid())
 
194
        return QVariant();
 
195
 
 
196
    QVariant v;
 
197
    if (role & ~(Qt::DisplayRole | Qt::EditRole))
 
198
        return v;
 
199
 
 
200
    if (!d->rec.isGenerated(item.column()))
 
201
        return v;
 
202
    QModelIndex dItem = indexInQuery(item);
 
203
    if (dItem.row() > d->bottom.row())
 
204
        const_cast<QSqlQueryModelPrivate *>(d)->prefetch(dItem.row());
 
205
 
 
206
    if (!d->query.seek(dItem.row())) {
 
207
        d->error = d->query.lastError();
 
208
        return v;
 
209
    }
 
210
 
 
211
    return d->query.value(dItem.column());
 
212
}
 
213
 
 
214
/*!
 
215
    Returns the header data for the given \a role in the \a section
 
216
    of the header with the specified \a orientation.
 
217
*/
 
218
QVariant QSqlQueryModel::headerData(int section, Qt::Orientation orientation, int role) const
 
219
{
 
220
    Q_D(const QSqlQueryModel);
 
221
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
 
222
        QVariant val = d->headers.value(section);
 
223
        if (val.isValid())
 
224
            return val;
 
225
        if (d->rec.count() > section)
 
226
            return d->rec.fieldName(section);
 
227
    }
 
228
    return QAbstractItemModel::headerData(section, orientation, role);
 
229
}
 
230
 
 
231
/*!
 
232
    This virtual function is called whenever the query changes. The
 
233
    default implementation does nothing.
 
234
 
 
235
    query() returns the new query.
 
236
 
 
237
    \sa query(), setQuery()
 
238
 */
 
239
void QSqlQueryModel::queryChange()
 
240
{
 
241
    // do nothing
 
242
}
 
243
 
 
244
/*!
 
245
    Resets the model and sets the data provider to be the given \a
 
246
    query. Note that the query must be active and must not be
 
247
    isForwardOnly().
 
248
 
 
249
    \sa query(), QSqlQuery::isActive(), QSqlQuery::setForwardOnly()
 
250
*/
 
251
void QSqlQueryModel::setQuery(const QSqlQuery &query)
 
252
{
 
253
    Q_D(QSqlQueryModel);
 
254
    QSqlRecord newRec = query.record();
 
255
    bool columnsChanged = (newRec != d->rec);
 
256
    bool hasQuerySize = d->query.driver()->hasFeature(QSqlDriver::QuerySize);
 
257
 
 
258
    if (d->colOffsets.size() != newRec.count() || columnsChanged) {
 
259
        d->colOffsets.resize(newRec.count());
 
260
        memset(d->colOffsets.data(), 0, d->colOffsets.size() * sizeof(int));
 
261
    }
 
262
 
 
263
    beginRemoveRows(QModelIndex(), 0, d->bottom.row());
 
264
 
 
265
    d->bottom = QModelIndex();
 
266
    d->error = QSqlError();
 
267
    d->atEnd = false;
 
268
    d->query = query;
 
269
    d->rec = newRec;
 
270
 
 
271
    endRemoveRows();
 
272
 
 
273
    if (columnsChanged)
 
274
        reset();
 
275
 
 
276
    if (!query.isActive() || query.isForwardOnly()) {
 
277
        d->atEnd = true;
 
278
        d->bottom = QModelIndex();
 
279
        if (query.isForwardOnly())
 
280
            d->error = QSqlError(QLatin1String("Forward-only queries "
 
281
                                               "cannot be used in a data model"),
 
282
                                 QString(), QSqlError::ConnectionError);
 
283
        else
 
284
            d->error = query.lastError();
 
285
        return;
 
286
    }
 
287
    QModelIndex newBottom;
 
288
    if (hasQuerySize) {
 
289
        beginInsertRows(QModelIndex(), 0, newBottom.row());
 
290
        newBottom = createIndex(d->query.size() - 1, d->rec.count() - 1);
 
291
        d->bottom = createIndex(d->query.size() - 1, columnsChanged ? 0 : d->rec.count() - 1);
 
292
        d->atEnd = true;
 
293
        endInsertRows();
 
294
    } else {
 
295
        newBottom = createIndex(0, d->rec.count() - 1);
 
296
    }
 
297
    if (columnsChanged) {
 
298
        beginInsertColumns(QModelIndex(), 0, newBottom.column());
 
299
        d->bottom = newBottom;
 
300
        endInsertColumns();
 
301
    } else {
 
302
        d->bottom = newBottom;
 
303
    }
 
304
 
 
305
    queryChange();
 
306
 
 
307
    // fetchMore does the rowsInserted stuff for incremental models
 
308
    fetchMore();
 
309
}
 
310
 
 
311
/*! \overload
 
312
 
 
313
    Executes the query \a query for the given database connection \a
 
314
    db. If no database is specified, the default connection is used.
 
315
 
 
316
    \sa query(), queryChange()
 
317
*/
 
318
void QSqlQueryModel::setQuery(const QString &query, const QSqlDatabase &db)
 
319
{
 
320
    setQuery(QSqlQuery(query, db));
 
321
}
 
322
 
 
323
/*!
 
324
    Clears the model and releases any aquired resource.
 
325
*/
 
326
void QSqlQueryModel::clear()
 
327
{
 
328
    Q_D(QSqlQueryModel);
 
329
    d->error = QSqlError();
 
330
    d->atEnd = true;
 
331
    d->query.clear();
 
332
    d->rec.clear();
 
333
    d->colOffsets.clear();
 
334
    d->bottom = QModelIndex();
 
335
    d->headers.clear();
 
336
}
 
337
 
 
338
/*!
 
339
    Sets the caption for the header with the given \a orientation to
 
340
    the specified \a value. This is useful if the model is used to
 
341
    display data in a view (e.g., QTableView).
 
342
 
 
343
    Returns true if \a role is \c Qt::DisplayRole and
 
344
    the \a section refers to a valid section; otherwise returns
 
345
    false.
 
346
 
 
347
    Note that this function cannot be used to modify values in the
 
348
    database since the model is read-only.
 
349
 
 
350
    \sa data()
 
351
 */
 
352
bool QSqlQueryModel::setHeaderData(int section, Qt::Orientation orientation,
 
353
                                   const QVariant &value, int role)
 
354
{
 
355
    Q_D(QSqlQueryModel);
 
356
    if ((role != Qt::EditRole && role != Qt::DisplayRole)
 
357
        || orientation != Qt::Horizontal || section < 0)
 
358
        return false;
 
359
 
 
360
    if (d->headers.size() <= section)
 
361
        d->headers.resize(qMax(section + 1, 16));
 
362
    d->headers[section] = value;
 
363
    return true;
 
364
}
 
365
 
 
366
/*!
 
367
    Returns the QSqlQuery associated with this model.
 
368
 
 
369
    \sa setQuery()
 
370
*/
 
371
QSqlQuery QSqlQueryModel::query() const
 
372
{
 
373
    Q_D(const QSqlQueryModel);
 
374
    return d->query;
 
375
}
 
376
 
 
377
/*!
 
378
    Returns information about the last error that occurred on the
 
379
    database.
 
380
*/
 
381
QSqlError QSqlQueryModel::lastError() const
 
382
{
 
383
    Q_D(const QSqlQueryModel);
 
384
    return d->error;
 
385
}
 
386
 
 
387
/*!
 
388
   Protected function which allows derived classes to set the value of
 
389
   the last error that occurred on the database to \a error.
 
390
 
 
391
   \sa lastError()
 
392
*/
 
393
void QSqlQueryModel::setLastError(const QSqlError &error)
 
394
{
 
395
    Q_D(QSqlQueryModel);
 
396
    d->error = error;
 
397
}
 
398
 
 
399
/*!
 
400
    Returns the record containing information about the fields of the
 
401
    current query. If \a row is the index of a valid row, the record
 
402
    will be populated with values from that row.
 
403
 
 
404
    If the model is not initialized, an empty record will be
 
405
    returned.
 
406
 
 
407
    \sa QSqlRecord::isEmpty()
 
408
*/
 
409
QSqlRecord QSqlQueryModel::record(int row) const
 
410
{
 
411
    Q_D(const QSqlQueryModel);
 
412
    if (row < 0)
 
413
        return d->rec;
 
414
 
 
415
    QSqlRecord rec = d->rec;
 
416
    for (int i = 0; i < rec.count(); ++i)
 
417
        rec.setValue(i, data(createIndex(row, i), Qt::EditRole));
 
418
    return rec;
 
419
}
 
420
 
 
421
/*! \overload
 
422
 
 
423
    Returns an empty record containing information about the fields
 
424
    of the current query.
 
425
 
 
426
    If the model is not initialized, an empty record will be
 
427
    returned.
 
428
 
 
429
    \sa QSqlRecord::isEmpty()
 
430
 */
 
431
QSqlRecord QSqlQueryModel::record() const
 
432
{
 
433
    Q_D(const QSqlQueryModel);
 
434
    return d->rec;
 
435
}
 
436
 
 
437
/*!
 
438
    Inserts \a count columns into the model at position \a column. The
 
439
    \a parent parameter must always be an invalid QModelIndex, since
 
440
    the model does not support parent-child relationships.
 
441
 
 
442
    Returns true if \a column is within bounds; otherwise returns false.
 
443
 
 
444
    By default, inserted columns are empty. To fill them with data,
 
445
    reimplement data() and handle any inserted column separately:
 
446
 
 
447
    \quotefromfile snippets/sqldatabase/sqldatabase.cpp
 
448
    \skipto QSqlQueryModel_snippets
 
449
    \skipto MyModel::data(
 
450
    \printuntil /^\}/
 
451
 
 
452
    \sa removeColumns()
 
453
*/
 
454
bool QSqlQueryModel::insertColumns(int column, int count, const QModelIndex &parent)
 
455
{
 
456
    Q_D(QSqlQueryModel);
 
457
    if (count <= 0 || parent.isValid() || column < 0 || column > d->rec.count())
 
458
        return false;
 
459
 
 
460
    beginInsertColumns(parent, column, column + count - 1);
 
461
    for (int c = 0; c < count; ++c) {
 
462
        QSqlField field;
 
463
        field.setReadOnly(true);
 
464
        field.setGenerated(false);
 
465
        d->rec.insert(column, field);
 
466
        if (d->colOffsets.size() < d->rec.count()) {
 
467
            int nVal = d->colOffsets.isEmpty() ? 0 : d->colOffsets[d->colOffsets.size() - 1];
 
468
            d->colOffsets.append(nVal);
 
469
            Q_ASSERT(d->colOffsets.size() >= d->rec.count());
 
470
        }
 
471
        for (int i = column + 1; i < d->colOffsets.count(); ++i)
 
472
            ++d->colOffsets[i];
 
473
    }
 
474
    endInsertColumns();
 
475
    return true;
 
476
}
 
477
 
 
478
/*!
 
479
    Removes \a count columns from the model starting from position \a
 
480
    column. The \a parent parameter must always be an invalid
 
481
    QModelIndex, since the model does not support parent-child
 
482
    relationships.
 
483
 
 
484
    Removing columns effectively hides them. It does not affect the
 
485
    underlying QSqlQuery.
 
486
 
 
487
    Returns true if the columns were removed; otherwise returns false.
 
488
 */
 
489
bool QSqlQueryModel::removeColumns(int column, int count, const QModelIndex &parent)
 
490
{
 
491
    Q_D(QSqlQueryModel);
 
492
    if (count <= 0 || parent.isValid() || column < 0 || column >= d->rec.count())
 
493
        return false;
 
494
 
 
495
    beginRemoveColumns(parent, column, column + count - 1);
 
496
 
 
497
    int i;
 
498
    for (i = 0; i < count; ++i)
 
499
        d->rec.remove(column);
 
500
    for (i = column; i < d->colOffsets.count(); ++i)
 
501
        d->colOffsets[i] -= count;
 
502
 
 
503
    endRemoveColumns();
 
504
    return true;
 
505
}
 
506
 
 
507
/*!
 
508
    Returns the index of the value in the database result set for the
 
509
    given \a item in the model.
 
510
 
 
511
    The return value is identical to \a item if no columns or rows
 
512
    have been inserted, removed, or moved around.
 
513
 
 
514
    Returns an invalid model index if \a item is out of bounds or if
 
515
    \a item does not point to a value in the result set.
 
516
 
 
517
    \sa QSqlTableModel::indexInQuery(), insertColumns(), removeColumns()
 
518
*/
 
519
QModelIndex QSqlQueryModel::indexInQuery(const QModelIndex &item) const
 
520
{
 
521
    Q_D(const QSqlQueryModel);
 
522
    if (item.column() < 0 || item.column() >= d->rec.count()
 
523
        || !d->rec.isGenerated(item.column()))
 
524
        return QModelIndex();
 
525
    return createIndex(item.row(), item.column() - d->colOffsets[item.column()],
 
526
                       item.internalPointer());
 
527
}
 
528