Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qsqlquerymodel.cpp
Go to the documentation of this file.
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
// Qt-Security score:significant reason:default
4
5
#
include
"qsqlquerymodel.h"
6
#
include
"qsqlquerymodel_p.h"
7
8
#
include
<
qdebug
.
h
>
9
#
include
<
qsqldriver
.
h
>
10
#
include
<
qsqlfield
.
h
>
11
12
QT_BEGIN_NAMESPACE
13
14
using
namespace
Qt::StringLiterals;
15
16
#
define
QSQL_PREFETCH
255
17
18
void
QSqlQueryModelPrivate::prefetch(
int
limit)
19
{
20
Q_Q(QSqlQueryModel);
21
22
if
(atEnd || limit <= bottom.row() || bottom.column() == -1)
23
return
;
24
25
QModelIndex newBottom;
26
const
int
oldBottomRow = qMax(bottom.row(), 0);
27
28
// try to seek directly
29
if
(query.seek(limit)) {
30
newBottom = q->createIndex(limit, bottom.column());
31
}
else
{
32
// have to seek back to our old position for MS Access
33
int
i = oldBottomRow;
34
if
(query.seek(i)) {
35
while
(query.next())
36
++i;
37
newBottom = q->createIndex(i, bottom.column());
38
}
else
{
39
// empty or invalid query
40
newBottom = q->createIndex(-1, bottom.column());
41
}
42
atEnd =
true
;
// this is the end.
43
}
44
if
(newBottom.row() >= 0 && newBottom.row() > bottom.row()) {
45
q->beginInsertRows(QModelIndex(), bottom.row() + 1, newBottom.row());
46
bottom = newBottom;
47
q->endInsertRows();
48
}
else
{
49
bottom = newBottom;
50
}
51
}
52
53
QSqlQueryModelPrivate::~QSqlQueryModelPrivate()
54
{
55
}
56
57
void
QSqlQueryModelPrivate::initColOffsets(
int
size)
58
{
59
colOffsets.resize(size);
60
memset(colOffsets.data(), 0, colOffsets.size() *
sizeof
(
int
));
61
}
62
63
int
QSqlQueryModelPrivate::columnInQuery(
int
modelColumn)
const
64
{
65
if
(modelColumn < 0 || modelColumn >= rec.count() || !rec.isGenerated(modelColumn) || modelColumn >= colOffsets.size())
66
return
-1;
67
return
modelColumn - colOffsets[modelColumn];
68
}
69
70
/*!
71
\class QSqlQueryModel
72
\brief The QSqlQueryModel class provides a read-only data model for SQL
73
result sets.
74
75
\ingroup database
76
\inmodule QtSql
77
78
QSqlQueryModel is a high-level interface for executing SQL
79
statements and traversing the result set. It is built on top of
80
the lower-level QSqlQuery and can be used to provide data to
81
view classes such as QTableView. For example:
82
83
\snippet sqldatabase/sqldatabase_snippet.cpp 16
84
85
We set the model's query, then we set up the labels displayed in
86
the view header.
87
88
QSqlQueryModel can also be used to access a database
89
programmatically, without binding it to a view:
90
91
\snippet sqldatabase/sqldatabase.cpp 21
92
93
The code snippet above extracts the \c salary field from record 4 in
94
the result set of the \c SELECT query. Since \c salary is the 2nd
95
column (or column index 1), we can rewrite the last line as follows:
96
97
\snippet sqldatabase/sqldatabase.cpp 22
98
99
The model is read-only by default. To make it read-write, you
100
must subclass it and reimplement setData() and flags(). Another
101
option is to use QSqlTableModel, which provides a read-write
102
model based on a single database table.
103
104
The \l{querymodel} example illustrates how to use
105
QSqlQueryModel to display the result of a query. It also shows
106
how to subclass QSqlQueryModel to customize the contents of the
107
data before showing it to the user, and how to create a
108
read-write model based on QSqlQueryModel.
109
110
If the database doesn't return the number of selected rows in
111
a query, the model will fetch rows incrementally.
112
See fetchMore() for more information.
113
114
\sa QSqlTableModel, QSqlRelationalTableModel, QSqlQuery,
115
{Model/View Programming}, {Query Model Example}
116
*/
117
118
/*!
119
Creates an empty QSqlQueryModel with the given \a parent.
120
*/
121
QSqlQueryModel::QSqlQueryModel(QObject *parent)
122
: QAbstractTableModel(*
new
QSqlQueryModelPrivate, parent)
123
{
124
}
125
126
/*! \internal
127
*/
128
QSqlQueryModel::QSqlQueryModel(QSqlQueryModelPrivate &dd, QObject *parent)
129
: QAbstractTableModel(dd, parent)
130
{
131
}
132
133
/*!
134
Destroys the object and frees any allocated resources.
135
136
\sa clear()
137
*/
138
QSqlQueryModel::~QSqlQueryModel()
139
{
140
}
141
142
/*!
143
Fetches more rows from a database.
144
This only affects databases that don't report back the size of a query
145
(see QSqlDriver::hasFeature()).
146
147
To force fetching of the entire result set, you can use the following:
148
149
\snippet code/src_sql_models_qsqlquerymodel.cpp 0
150
151
\a parent should always be an invalid QModelIndex.
152
153
\sa canFetchMore()
154
*/
155
void
QSqlQueryModel::fetchMore(
const
QModelIndex &parent)
156
{
157
Q_D(QSqlQueryModel);
158
if
(parent.isValid())
159
return
;
160
d->prefetch(qMax(d->bottom.row(), 0) +
QSQL_PREFETCH
);
161
}
162
163
/*!
164
Returns \c true if it is possible to read more rows from the database.
165
This only affects databases that don't report back the size of a query
166
(see QSqlDriver::hasFeature()).
167
168
\a parent should always be an invalid QModelIndex.
169
170
\sa fetchMore()
171
*/
172
bool
QSqlQueryModel::canFetchMore(
const
QModelIndex &parent)
const
173
{
174
Q_D(
const
QSqlQueryModel);
175
return
(!parent.isValid() && !d->atEnd);
176
}
177
178
/*!
179
\since 5.10
180
\reimp
181
182
Returns the model's role names.
183
184
Qt defines only one role for the QSqlQueryModel:
185
186
\table
187
\header
188
\li Qt Role
189
\li QML Role Name
190
\row
191
\li Qt::DisplayRole
192
\li display
193
\endtable
194
*/
195
QHash<
int
, QByteArray> QSqlQueryModel::roleNames()
const
196
{
197
static
const
QHash<
int
, QByteArray> names = {
198
{ Qt::DisplayRole, QByteArrayLiteral(
"display"
) }
199
};
200
return
names;
201
}
202
203
/*! \internal
204
*/
205
void
QSqlQueryModel::beginInsertRows(
const
QModelIndex &parent,
int
first,
int
last)
206
{
207
Q_D(QSqlQueryModel);
208
if
(!d->nestedResetLevel)
209
QAbstractTableModel::beginInsertRows(parent, first, last);
210
}
211
212
/*! \internal
213
*/
214
void
QSqlQueryModel::endInsertRows()
215
{
216
Q_D(QSqlQueryModel);
217
if
(!d->nestedResetLevel)
218
QAbstractTableModel::endInsertRows();
219
}
220
221
/*! \internal
222
*/
223
void
QSqlQueryModel::beginRemoveRows(
const
QModelIndex &parent,
int
first,
int
last)
224
{
225
Q_D(QSqlQueryModel);
226
if
(!d->nestedResetLevel)
227
QAbstractTableModel::beginRemoveRows(parent, first, last);
228
}
229
230
/*! \internal
231
*/
232
void
QSqlQueryModel::endRemoveRows()
233
{
234
Q_D(QSqlQueryModel);
235
if
(!d->nestedResetLevel)
236
QAbstractTableModel::endRemoveRows();
237
}
238
239
/*! \internal
240
*/
241
void
QSqlQueryModel::beginInsertColumns(
const
QModelIndex &parent,
int
first,
int
last)
242
{
243
Q_D(QSqlQueryModel);
244
if
(!d->nestedResetLevel)
245
QAbstractTableModel::beginInsertColumns(parent, first, last);
246
}
247
248
/*! \internal
249
*/
250
void
QSqlQueryModel::endInsertColumns()
251
{
252
Q_D(QSqlQueryModel);
253
if
(!d->nestedResetLevel)
254
QAbstractTableModel::endInsertColumns();
255
}
256
257
/*! \internal
258
*/
259
void
QSqlQueryModel::beginRemoveColumns(
const
QModelIndex &parent,
int
first,
int
last)
260
{
261
Q_D(QSqlQueryModel);
262
if
(!d->nestedResetLevel)
263
QAbstractTableModel::beginRemoveColumns(parent, first, last);
264
}
265
266
/*! \internal
267
*/
268
void
QSqlQueryModel::endRemoveColumns()
269
{
270
Q_D(QSqlQueryModel);
271
if
(!d->nestedResetLevel)
272
QAbstractTableModel::endRemoveColumns();
273
}
274
275
/*! \internal
276
*/
277
void
QSqlQueryModel::beginResetModel()
278
{
279
Q_D(QSqlQueryModel);
280
if
(!d->nestedResetLevel)
281
QAbstractTableModel::beginResetModel();
282
++d->nestedResetLevel;
283
}
284
285
/*! \internal
286
*/
287
void
QSqlQueryModel::endResetModel()
288
{
289
Q_D(QSqlQueryModel);
290
--d->nestedResetLevel;
291
if
(!d->nestedResetLevel)
292
QAbstractTableModel::endResetModel();
293
}
294
295
/*! \fn int QSqlQueryModel::rowCount(const QModelIndex &parent) const
296
297
If the database supports returning the size of a query
298
(see QSqlDriver::hasFeature()), the number of rows of the current
299
query is returned. Otherwise, returns the number of rows
300
currently cached on the client.
301
302
\a parent should always be an invalid QModelIndex.
303
304
\sa canFetchMore(), QSqlDriver::hasFeature()
305
*/
306
int
QSqlQueryModel::rowCount(
const
QModelIndex &index)
const
307
{
308
Q_D(
const
QSqlQueryModel);
309
return
index.isValid() ? 0 : d->bottom.row() + 1;
310
}
311
312
/*! \reimp
313
*/
314
int
QSqlQueryModel::columnCount(
const
QModelIndex &index)
const
315
{
316
Q_D(
const
QSqlQueryModel);
317
return
index.isValid() ? 0 : d->rec.count();
318
}
319
320
/*!
321
Returns the value for the specified \a item and \a role.
322
323
If \a item is out of bounds or if an error occurred, an invalid
324
QVariant is returned.
325
326
\sa lastError()
327
*/
328
QVariant QSqlQueryModel::data(
const
QModelIndex &item,
int
role)
const
329
{
330
Q_D(
const
QSqlQueryModel);
331
if
(!item.isValid())
332
return
QVariant();
333
334
if
(role & ~(Qt::DisplayRole | Qt::EditRole))
335
return
QVariant();
336
337
if
(!d->rec.isGenerated(item.column()))
338
return
QVariant();
339
QModelIndex dItem = indexInQuery(item);
340
if
(dItem.row() > d->bottom.row())
341
const_cast
<QSqlQueryModelPrivate *>(d)->prefetch(dItem.row());
342
343
if
(!d->query.seek(dItem.row())) {
344
d->error = d->query.lastError();
345
return
QVariant();
346
}
347
348
return
d->query.value(dItem.column());
349
}
350
351
/*!
352
Returns the header data for the given \a role in the \a section
353
of the header with the specified \a orientation.
354
*/
355
QVariant QSqlQueryModel::headerData(
int
section, Qt::Orientation orientation,
int
role)
const
356
{
357
Q_D(
const
QSqlQueryModel);
358
if
(orientation == Qt::Horizontal) {
359
QVariant val = d->headers.value(section).value(role);
360
if
(role == Qt::DisplayRole && !val.isValid())
361
val = d->headers.value(section).value(Qt::EditRole);
362
if
(val.isValid())
363
return
val;
364
if
(role == Qt::DisplayRole && d->rec.count() > section && d->columnInQuery(section) != -1)
365
return
d->rec.fieldName(section);
366
}
367
return
QAbstractItemModel::headerData(section, orientation, role);
368
}
369
370
/*!
371
This virtual function is called whenever the query changes. The
372
default implementation does nothing.
373
374
query() returns the new query.
375
376
\sa query(), setQuery()
377
*/
378
void
QSqlQueryModel::queryChange()
379
{
380
// do nothing
381
}
382
383
#
if
QT_REMOVAL_QT7_DEPRECATED_SINCE
(
6
,
2
)
384
/*!
385
\deprecated [6.2] Use the \c{setQuery(QSqlQuery &&query)} overload instead.
386
This overload will be removed in Qt 7.
387
388
\overload
389
*/
390
void
QSqlQueryModel::setQuery(
const
QSqlQuery &query)
391
{
392
QT_IGNORE_DEPRECATIONS(QSqlQuery copy = query;)
393
setQuery(std::move(copy));
394
}
395
#
endif
// QT_REMOVAL_QT7_DEPRECATED_SINCE(6, 2)
396
397
/*!
398
Resets the model and sets the data provider to be the given \a
399
query. Note that the query must be active and must not be
400
isForwardOnly().
401
402
lastError() can be used to retrieve verbose information if there
403
was an error setting the query.
404
405
\note Calling setQuery() will remove any inserted columns.
406
407
\since 6.2
408
409
\sa query(), QSqlQuery::isActive(), QSqlQuery::setForwardOnly(), lastError()
410
*/
411
void
QSqlQueryModel::setQuery(QSqlQuery &&query)
412
{
413
Q_D(QSqlQueryModel);
414
beginResetModel();
415
416
QSqlRecord newRec = query.record();
417
bool
columnsChanged = (newRec != d->rec);
418
419
if
(d->colOffsets.size() != newRec.count() || columnsChanged)
420
d->initColOffsets(newRec.count());
421
422
d->bottom = QModelIndex();
423
d->error = QSqlError();
424
d->query = std::move(query);
425
d->rec = newRec;
426
d->atEnd =
true
;
427
428
if
(d->query.isForwardOnly()) {
429
d->error = QSqlError(
"Forward-only queries cannot be used in a data model"_L1
,
430
QString(), QSqlError::ConnectionError);
431
endResetModel();
432
return
;
433
}
434
435
if
(!d->query.isActive()) {
436
d->error = d->query.lastError();
437
endResetModel();
438
return
;
439
}
440
441
if
(d->query.driver()->hasFeature(QSqlDriver::QuerySize) && d->query.size() > 0) {
442
d->bottom = createIndex(d->query.size() - 1, d->rec.count() - 1);
443
}
else
{
444
d->bottom = createIndex(-1, d->rec.count() - 1);
445
d->atEnd =
false
;
446
}
447
448
449
// fetchMore does the rowsInserted stuff for incremental models
450
fetchMore();
451
452
endResetModel();
453
queryChange();
454
}
455
456
/*! \overload
457
458
Executes the query \a query for the given database connection \a
459
db. If no database (or an invalid database) is specified, the
460
default connection is used.
461
462
lastError() can be used to retrieve verbose information if there
463
was an error setting the query.
464
465
Example:
466
\snippet code/src_sql_models_qsqlquerymodel.cpp 1
467
468
\sa query(), queryChange(), lastError()
469
*/
470
void
QSqlQueryModel::setQuery(
const
QString &query,
const
QSqlDatabase &db)
471
{
472
setQuery(QSqlQuery(query, db));
473
}
474
475
/*!
476
\since 6.9
477
Re-executes the current query to fetch the data from the same database connection.
478
479
\note \c refresh() is not applicable when the query contains bound values.
480
481
\sa setQuery(QSqlQuery &&query), QSqlQuery::boundValue()
482
*/
483
void
QSqlQueryModel::refresh()
484
{
485
Q_D(QSqlQueryModel);
486
const
auto
connName = d->query.driver()
487
? d->query.driver()->connectionName() : QString();
488
setQuery(d->query.executedQuery(), QSqlDatabase::database(connName));
489
}
490
491
/*!
492
Clears the model and releases any acquired resource.
493
*/
494
void
QSqlQueryModel::clear()
495
{
496
Q_D(QSqlQueryModel);
497
beginResetModel();
498
d->error = QSqlError();
499
d->atEnd =
true
;
500
d->query.clear();
501
d->rec.clear();
502
d->colOffsets.clear();
503
d->bottom = QModelIndex();
504
d->headers.clear();
505
endResetModel();
506
}
507
508
/*!
509
Sets the caption for a horizontal header for the specified \a role to
510
\a value. This is useful if the model is used to
511
display data in a view (e.g., QTableView).
512
513
Returns \c true if \a orientation is Qt::Horizontal and
514
the \a section refers to a valid section; otherwise returns
515
false.
516
517
Note that this function cannot be used to modify values in the
518
database since the model is read-only.
519
520
\sa data()
521
*/
522
bool
QSqlQueryModel::setHeaderData(
int
section, Qt::Orientation orientation,
523
const
QVariant &value,
int
role)
524
{
525
Q_D(QSqlQueryModel);
526
if
(orientation != Qt::Horizontal || section < 0 || columnCount() <= section)
527
return
false
;
528
529
if
(d->headers.size() <= section)
530
d->headers.resize(qMax(section + 1, 16));
531
d->headers[section][role] = value;
532
emit headerDataChanged(orientation, section, section);
533
return
true
;
534
}
535
536
/*!
537
Returns a reference to the const QSqlQuery object associated with this model.
538
539
\sa setQuery()
540
*/
541
const
QSqlQuery &QSqlQueryModel::query(QT6_IMPL_NEW_OVERLOAD)
const
542
{
543
Q_D(
const
QSqlQueryModel);
544
return
d->query;
545
}
546
547
/*!
548
Returns information about the last error that occurred on the
549
database.
550
551
\sa query()
552
*/
553
QSqlError QSqlQueryModel::lastError()
const
554
{
555
Q_D(
const
QSqlQueryModel);
556
return
d->error;
557
}
558
559
/*!
560
Protected function which allows derived classes to set the value of
561
the last error that occurred on the database to \a error.
562
563
\sa lastError()
564
*/
565
void
QSqlQueryModel::setLastError(
const
QSqlError &error)
566
{
567
Q_D(QSqlQueryModel);
568
d->error = error;
569
}
570
571
/*!
572
Returns the record containing information about the fields of the
573
current query. If \a row is the index of a valid row, the record
574
will be populated with values from that row.
575
576
If the model is not initialized, an empty record will be
577
returned.
578
579
\sa QSqlRecord::isEmpty()
580
*/
581
QSqlRecord QSqlQueryModel::record(
int
row)
const
582
{
583
Q_D(
const
QSqlQueryModel);
584
if
(row < 0)
585
return
d->rec;
586
587
QSqlRecord rec = d->rec;
588
for
(
int
i = 0; i < rec.count(); ++i)
589
rec.setValue(i, data(createIndex(row, i), Qt::EditRole));
590
return
rec;
591
}
592
593
/*! \overload
594
595
Returns an empty record containing information about the fields
596
of the current query.
597
598
If the model is not initialized, an empty record will be
599
returned.
600
601
\sa QSqlRecord::isEmpty()
602
*/
603
QSqlRecord QSqlQueryModel::record()
const
604
{
605
Q_D(
const
QSqlQueryModel);
606
return
d->rec;
607
}
608
609
/*!
610
Inserts \a count columns into the model at position \a column. The
611
\a parent parameter must always be an invalid QModelIndex, since
612
the model does not support parent-child relationships.
613
614
Returns \c true if \a column is within bounds; otherwise returns \c false.
615
616
By default, inserted columns are empty. To fill them with data,
617
reimplement data() and handle any inserted column separately:
618
619
\snippet sqldatabase/sqldatabase.cpp 23
620
621
\sa removeColumns()
622
*/
623
bool
QSqlQueryModel::insertColumns(
int
column,
int
count,
const
QModelIndex &parent)
624
{
625
Q_D(QSqlQueryModel);
626
if
(count <= 0 || parent.isValid() || column < 0 || column > d->rec.count())
627
return
false
;
628
629
beginInsertColumns(parent, column, column + count - 1);
630
for
(
int
c = 0; c < count; ++c) {
631
QSqlField field;
632
field.setReadOnly(
true
);
633
field.setGenerated(
false
);
634
d->rec.insert(column, field);
635
if
(d->colOffsets.size() < d->rec.count()) {
636
int
nVal = d->colOffsets.isEmpty() ? 0 : d->colOffsets[d->colOffsets.size() - 1];
637
d->colOffsets.append(nVal);
638
Q_ASSERT(d->colOffsets.size() >= d->rec.count());
639
}
640
for
(qsizetype i = column + 1; i < d->colOffsets.size(); ++i)
641
++d->colOffsets[i];
642
}
643
endInsertColumns();
644
return
true
;
645
}
646
647
/*!
648
Removes \a count columns from the model starting from position \a
649
column. The \a parent parameter must always be an invalid
650
QModelIndex, since the model does not support parent-child
651
relationships.
652
653
Removing columns effectively hides them. It does not affect the
654
underlying QSqlQuery.
655
656
Returns \c true if the columns were removed; otherwise returns \c false.
657
*/
658
bool
QSqlQueryModel::removeColumns(
int
column,
int
count,
const
QModelIndex &parent)
659
{
660
Q_D(QSqlQueryModel);
661
if
(count <= 0 || parent.isValid() || column < 0 || column >= d->rec.count())
662
return
false
;
663
664
beginRemoveColumns(parent, column, column + count - 1);
665
666
for
(
int
i = 0; i < count; ++i)
667
d->rec.remove(column);
668
for
(qsizetype i = column; i < d->colOffsets.size(); ++i)
669
d->colOffsets[i] -= count;
670
671
endRemoveColumns();
672
return
true
;
673
}
674
675
/*!
676
Returns the index of the value in the database result set for the
677
given \a item in the model.
678
679
The return value is identical to \a item if no columns or rows
680
have been inserted, removed, or moved around.
681
682
Returns an invalid model index if \a item is out of bounds or if
683
\a item does not point to a value in the result set.
684
685
\sa QSqlTableModel::indexInQuery(), insertColumns(), removeColumns()
686
*/
687
QModelIndex QSqlQueryModel::indexInQuery(
const
QModelIndex &item)
const
688
{
689
Q_D(
const
QSqlQueryModel);
690
int
modelColumn = d->columnInQuery(item.column());
691
if
(modelColumn < 0)
692
return
QModelIndex();
693
return
createIndex(item.row(), modelColumn, item.internalPointer());
694
}
695
696
QT_END_NAMESPACE
697
698
#
include
"moc_qsqlquerymodel.cpp"
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
QSQL_PREFETCH
#define QSQL_PREFETCH
Definition
qsqlquerymodel.cpp:16
qtbase
src
sql
models
qsqlquerymodel.cpp
Generated on
for Qt by
1.18.0