1 /* This file is part of the KDE project 2 Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org> 3 4 This program 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 program 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 program; see the file COPYING. 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_CURSOR_H 21 #define KDB_CURSOR_H 22 23 #include <QString> 24 #include <QVariant> 25 26 #include "KDbResult.h" 27 #include "KDbQueryColumnInfo.h" 28 29 class KDbConnection; 30 class KDbRecordData; 31 class KDbQuerySchema; 32 class KDbRecordEditBuffer; 33 34 //! Provides database cursor functionality. 35 /*! 36 Cursor can be defined in two ways: 37 38 -# by passing KDbQuerySchema object to KDbConnection::executeQuery() or KDbConnection::prepareQuery(); 39 then query is defined for in engine-independent way -- this is recommended usage 40 41 -# by passing raw query statement string to KDbConnection::executeQuery() or KDbConnection::prepareQuery(); 42 then query may be defined for in engine-dependent way -- this is not recommended usage, 43 but convenient when we can't or do not want to allocate KDbQuerySchema object, while we 44 know that the query statement is syntactically and logically ok in our context. 45 46 You can move cursor to next record with moveNext() and move back with movePrev(). 47 The cursor is always positioned on record, not between records, with exception that 48 after open() it is positioned before the first record (if any) -- then bof() equals true. 49 The cursor can also be positioned after the last record (if any) with moveNext() -- then eof() equals true. 50 For example, if you have four records, 1, 2, 3, 4, then after calling open(), moveNext(), 51 moveNext(), moveNext(), movePrev() you are going through records: 1, 2, 3, 2. 52 53 Cursor can be buffered or unbuferred. 54 55 @warning Buffered cursors are not implemented! 56 57 Buffering in this class is not related to any SQL engine capatibilities for server-side cursors 58 (eg. like 'DECLARE CURSOR' statement) - buffered data is at client (application) side. 59 Any record retrieved in buffered cursor will be stored inside an internal buffer 60 and reused when needed. Unbuffered cursor always requires one record fetching from 61 db connection at every step done with moveNext(), movePrev(), etc. 62 63 Notes: 64 - Do not use delete operator for KDbCursor objects - this will fail; use KDbConnection::deleteCursor() 65 instead. 66 - KDbQuerySchema object is not owned by KDbCursor object that uses it. 67 */ 68 class KDB_EXPORT KDbCursor: public KDbResultable 69 { 70 Q_DECLARE_TR_FUNCTIONS(KDbCursor) 71 public: 72 //! Options that describe behavior of database cursor 73 enum class Option { 74 None = 0, 75 Buffered = 1 76 }; 77 Q_DECLARE_FLAGS(Options, Option) 78 79 /*! @return connection used for the cursor */ 80 KDbConnection* connection(); 81 82 //! @overload 83 //! @since 3.1 84 const KDbConnection* connection() const; 85 86 /*! Opens the cursor using data provided on creation. 87 The data might be either KDbQuerySchema or a raw SQL statement. */ 88 bool open(); 89 90 /*! Closes and then opens again the same cursor. 91 If the cursor is not opened it is just opened and result of this open is returned. 92 Otherwise, true is returned if cursor is successfully closed and then opened. */ 93 bool reopen(); 94 95 /*! Closes previously opened cursor. 96 If the cursor is closed, nothing happens. */ 97 virtual bool close(); 98 99 /*! @return query schema used to define this cursor 100 or 0 if the cursor is not defined by a query schema but by a raw SQL statement. */ 101 KDbQuerySchema *query() const; 102 103 //! @return query parameters assigned to this cursor 104 QList<QVariant> queryParameters() const; 105 106 //! Sets query parameters @a params for this cursor. 107 void setQueryParameters(const QList<QVariant>& params); 108 109 /*! @return raw query statement used to define this cursor 110 or null string if raw statement instead (but KDbQuerySchema is defined instead). */ 111 KDbEscapedString rawSql() const; 112 113 /*! @return cursor options */ 114 Options options() const; 115 116 /*! @return true if the cursor is opened. */ 117 bool isOpened() const; 118 119 /*! @return true if the cursor is buffered. */ 120 bool isBuffered() const; 121 122 /*! Sets this cursor to buffered type or not. See description 123 of buffered and nonbuffered cursors in class description. 124 This method only works if cursor is not opened (isOpened()==false). 125 You can close already opened cursor and then switch this option on/off. 126 */ 127 void setBuffered(bool buffered); 128 129 /*! Moves current position to the first record and retrieves it. 130 @return true if the first record was retrieved. 131 False could mean that there was an error or there is no record available. */ 132 bool moveFirst(); 133 134 /*! Moves current position to the last record and retrieves it. 135 @return true if the last record was retrieved. 136 False could mean that there was an error or there is no record available. */ 137 virtual bool moveLast(); 138 139 /*! Moves current position to the next record and retrieves it. */ 140 virtual bool moveNext(); 141 142 /*! Moves current position to the next record and retrieves it. 143 Currently it's only supported for buffered cursors. */ 144 virtual bool movePrev(); 145 146 /*! @return true if current position is after last record. */ eof()147 inline bool eof() const { 148 return m_afterLast; 149 } 150 151 /*! @return true if current position is before first record. */ bof()152 inline bool bof() const { 153 return m_at == 0; 154 } 155 156 /*! @return current internal position of the cursor's query. 157 We are counting records from 0. 158 Value -1 means that cursor does not point to any valid record 159 (this happens eg. after open(), close(), 160 and after moving after last record or before first one. */ at()161 inline qint64 at() const { 162 return readAhead() ? 0 : (m_at - 1); 163 } 164 165 /*! @return number of fields available for this cursor. 166 This never includes ROWID column or other internal columns (e.g. lookup). */ fieldCount()167 inline int fieldCount() const { 168 return m_query ? m_logicalFieldCount : m_fieldCount; 169 } 170 171 /*! @return true if ROWID information is available for each record. 172 ROWID information is available 173 if KDbDriverBehavior::ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE == false 174 for a KDb database driver and the master table has no primary key defined. 175 Phisically, ROWID value is returned after last returned field, 176 so data vector's length is expanded by one. */ 177 bool containsRecordIdInfo() const; 178 179 /*! @return a value stored in column number @a i (counting from 0). 180 It has unspecified behavior if the cursor is not at valid record. 181 Note for driver developers: 182 If @a i is >= than m_fieldCount, null QVariant value should be returned. 183 To return a value typically you can use a pointer to internal structure 184 that contain current record data (buffered or unbuffered). */ 185 virtual QVariant value(int i) = 0; 186 187 /*! [PROTOTYPE] @return current record data or @c nullptr if there is no current records. */ 188 virtual const char ** recordData() const = 0; 189 190 /*! Sets a list of columns for ORDER BY section of the query. 191 Only works when the cursor has been created using KDbQuerySchema object 192 (i.e. when query()!=0; does not work with raw statements). 193 Each name on the list must be a field or alias present within the query 194 and must not be covered by aliases. If one or more names cannot be found within 195 the query, the method will have no effect. Any previous ORDER BY settings will be removed. 196 197 The order list provided here has priority over a list defined in the KDbQuerySchema 198 object itseld (using KDbQuerySchema::setOrderByColumnList()). 199 The KDbQuerySchema object itself is not modifed by this method: only order of records retrieved 200 by this cursor is affected. 201 202 Use this method before calling open(). You can also call reopen() after calling this method 203 to see effects of applying records order. */ 204 //! @todo implement this 205 void setOrderByColumnList(const QStringList& columnNames); 206 207 /*! Convenience method, similar to setOrderByColumnList(const QStringList&). */ 208 //! @todo implement this 209 void setOrderByColumnList(const QString& column1, const QString& column2 = QString(), 210 const QString& column3 = QString(), const QString& column4 = QString(), 211 const QString& column5 = QString()); 212 213 /*! @return a list of fields contained in ORDER BY section of the query. 214 @see setOrderBy(const QStringList&) */ 215 KDbQueryColumnInfo::Vector orderByColumnList() const; 216 217 /*! Allocates a new KDbRecordData and stores data in it (makes a deep copy of each field). 218 If the cursor is not at valid record, the result is undefined. 219 @return newly created record data object or 0 on error. */ 220 KDbRecordData* storeCurrentRecord() const; 221 222 /*! Puts current record's data into @a data (makes a deep copy of each field). 223 If the cursor is not at valid record, the result is undefined. 224 @return true on success. 225 @c false is returned if @a data is @c nullptr. */ 226 bool storeCurrentRecord(KDbRecordData* data) const; 227 228 bool updateRecord(KDbRecordData* data, KDbRecordEditBuffer* buf, bool useRecordId = false); 229 230 bool insertRecord(KDbRecordData* data, KDbRecordEditBuffer* buf, bool getRecrordId = false); 231 232 bool deleteRecord(KDbRecordData* data, bool useRecordId = false); 233 234 bool deleteAllRecords(); 235 236 protected: 237 /*! Cursor will operate on @a conn, raw SQL statement @a sql will be used to execute query. */ 238 KDbCursor(KDbConnection* conn, const KDbEscapedString& sql, Options options = KDbCursor::Option::None); 239 240 /*! Cursor will operate on @a conn, @a query schema will be used to execute query. */ 241 KDbCursor(KDbConnection* conn, KDbQuerySchema* query, Options options = KDbCursor::Option::None); 242 243 ~KDbCursor() override; 244 245 void init(KDbConnection* conn); 246 247 /*! Internal: cares about proper flag setting depending on result of drv_getNextRecord() 248 and depending on wherher a cursor is buffered. */ 249 bool getNextRecord(); 250 251 /*! Note for driver developers: this method should initialize engine-specific cursor's 252 resources using an SQL statement @a sql. It is not required to store @a sql statement somewhere 253 in your KDbCursor subclass (it is already stored in m_query or m_rawStatement, 254 depending query type) - only pass it to proper engine's function. */ 255 virtual bool drv_open(const KDbEscapedString& sql) = 0; 256 257 virtual bool drv_close() = 0; 258 virtual void drv_getNextRecord() = 0; 259 260 /*! Stores currently fetched record's values in appropriate place of the buffer. 261 Note for driver developers: 262 This place can be computed using m_at. Do not change value of m_at or any other 263 KDbCursor members, only change your internal structures like pointer to current 264 record, etc. If your database engine's API function (for record fetching) 265 do not allocates such a space, you want to allocate a space for current 266 record. Otherwise, reuse existing structure, what could be more efficient. 267 All functions like drv_appendCurrentRecordToBuffer() operates on the buffer, 268 i.e. array of stored records. You are not forced to have any particular 269 fixed structure for buffer item or buffer itself - the structure is internal and 270 only methods like storeCurrentRecord() visible to public. 271 */ 272 virtual void drv_appendCurrentRecordToBuffer() = 0; 273 /*! Moves pointer (that points to the buffer) -- to next item in this buffer. 274 Note for driver developers: probably just execute "your_pointer++" is enough. 275 */ 276 virtual void drv_bufferMovePointerNext() = 0; 277 /*! Like drv_bufferMovePointerNext() but execute "your_pointer--". */ 278 virtual void drv_bufferMovePointerPrev() = 0; 279 /*! Moves pointer (that points to the buffer) to a new place: @a at. 280 */ 281 virtual void drv_bufferMovePointerTo(qint64 at) = 0; 282 283 /*! Clears cursor's buffer if this was allocated (only for buffered cursor type). 284 Otherwise do nothing. For reimplementing. Default implementation does nothing. */ drv_clearBuffer()285 virtual void drv_clearBuffer() {} 286 287 //! @internal clears buffer with reimplemented drv_clearBuffer(). */ 288 void clearBuffer(); 289 290 /*! Puts current record's data into @a data (makes a deep copy of each field). 291 This method has unspecified behavior if the cursor is not at valid record. 292 @return true on success. 293 Note: For reimplementation in driver's code. Shortly, this method translates 294 a record data from internal representation (probably also used in buffer) 295 to simple public KDbRecordData representation. */ 296 virtual bool drv_storeCurrentRecord(KDbRecordData* data) const = 0; 297 298 KDbQuerySchema *m_query; 299 bool m_afterLast; 300 qint64 m_at; 301 int m_fieldCount; //!< cached field count information 302 int m_fieldsToStoreInRecord; //!< Used by storeCurrentRecord(), reimplement if needed 303 //!< (e.g. PostgreSQL driver, when m_containsRecordIdInfo is true 304 //!< sets m_fieldCount+1 here) 305 int m_logicalFieldCount; //!< logical field count, i.e. without internal values like Record Id or lookup 306 KDbCursor::Options m_options; //!< cursor options that describes its behavior 307 308 //! Possible results of record fetching, used for m_fetchResult 309 enum class FetchResult { 310 Invalid, //!< used before starting the fetching, result is not known yet 311 Error, //!< error of fetching 312 Ok, //!< the data is fetched 313 End //!< at the end of data 314 }; 315 316 FetchResult m_fetchResult; //!< result of a record fetching 317 318 //<members related to buffering> 319 int m_records_in_buf; //!< number of records currently stored in the buffer 320 bool m_buffering_completed; //!< true if we already have all records stored in the buffer 321 //</members related to buffering> 322 323 //! Useful e.g. for value(int) method to obtain access to schema definition. 324 KDbQueryColumnInfo::Vector* m_visibleFieldsExpanded; 325 326 private: 327 bool readAhead() const; 328 329 Q_DISABLE_COPY(KDbCursor) 330 friend class CursorDeleter; 331 class Private; 332 Private * const d; 333 }; 334 335 //! Sends information about object @a cursor to debug output @a dbg. 336 //! @since 3.1 337 KDB_EXPORT QDebug operator<<(QDebug dbg, KDbCursor& cursor); 338 339 //! Sends information about object @a cursor to debug output @a dbg. 340 KDB_EXPORT QDebug operator<<(QDebug dbg, const KDbCursor& cursor); 341 342 Q_DECLARE_OPERATORS_FOR_FLAGS(KDbCursor::Options) 343 344 #endif 345