Line data Source code
1 : /*
2 : * Text:
3 : * libsnapwebsites/src/libdbproxy/row.cpp
4 : *
5 : * Description:
6 : * Handling of rows. There is no class representing a row in Cassandra.
7 : * A row is just a key. We have this object to allow for our C++ array
8 : * syntax to access the Cassandra data.
9 : *
10 : * Documentation:
11 : * See each function below.
12 : *
13 : * License:
14 : * Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved
15 : *
16 : * https://snapwebsites.org/
17 : * contact@m2osw.com
18 : *
19 : * Permission is hereby granted, free of charge, to any person obtaining a
20 : * copy of this software and associated documentation files (the
21 : * "Software"), to deal in the Software without restriction, including
22 : * without limitation the rights to use, copy, modify, merge, publish,
23 : * distribute, sublicense, and/or sell copies of the Software, and to
24 : * permit persons to whom the Software is furnished to do so, subject to
25 : * the following conditions:
26 : *
27 : * The above copyright notice and this permission notice shall be included
28 : * in all copies or substantial portions of the Software.
29 : *
30 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
31 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
33 : * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
34 : * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
35 : * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
36 : * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 : */
38 :
39 : #include "libdbproxy/row.h"
40 : #include "libdbproxy/table.h"
41 : #include "libdbproxy/context.h"
42 : #include "libdbproxy/libdbproxy.h"
43 :
44 : #include <iostream>
45 : #include <stdexcept>
46 :
47 : namespace libdbproxy
48 : {
49 :
50 : /** \class row
51 : * \brief The row class to hold a set of cells.
52 : *
53 : * These objects are created by the table whenever data is
54 : * being read or written to a cell. Rows have a binary key (may be set
55 : * as a UTF-8 string) and a map of cells indexed by the names of the
56 : * cells.
57 : *
58 : * The name of a row is limited to nearly 64Kb. Although, if you have
59 : * a table with very long names, you may want to consider computing
60 : * an md5sum or equivalent. This is important if you want to avoid
61 : * slowing down the Cassandra server. Searching through a large set
62 : * of 60Kb keys is much slower than doing the same through their
63 : * md5sums. Of course, that means you lose the automatic sorting of
64 : * the original key.
65 : *
66 : * By default, most of the functions will create a new cell. If you
67 : * need to test the existance without creating a cell, use the
68 : * exists() function with the column name of the cell check out.
69 : *
70 : * \sa exists()
71 : */
72 :
73 :
74 : /** \typedef row::composite_column_names_t
75 : * \brief The set of column names.
76 : *
77 : * This type is used to declare a set of composite column names as
78 : * used by the compositeCell() functions.
79 : *
80 : * The type simply defines an array of column names. Each name can be any
81 : * valid column name key. In other words, a value with any value.
82 : * However, to be compatible with CQL and the CLI and possibly other
83 : * Cassandra features, you want to limit your names to things that do not
84 : * include the colon characters. The CQL and CLI make use of names
85 : * separated by colons (i.e. "blah:foo:123".) However, the Cassandra
86 : * cluster itself has no limit to the content of the names except for
87 : * their length which is 65536 bytes each.
88 : */
89 :
90 :
91 : /** \var row::f_table
92 : * \brief The table this row is part of.
93 : *
94 : * This bare pointer is used to access the table this row is part of.
95 : * It is a bare pointer because you cannot create a row without having
96 : * a table and the table keeps a tight reference on the row.
97 : */
98 :
99 :
100 : /** \var row::f_key
101 : * \brief The binary key of the row.
102 : *
103 : * The binary key of the row is set to UTF-8 when defined with a
104 : * string. Otherwise it is defined as specified by the user.
105 : */
106 :
107 :
108 : /** \var row::f_cells
109 : * \brief The array of cells defined in this row.
110 : *
111 : * This is a map of cells. Cells are names and values pairs.
112 : *
113 : * The values are defined with a timestamp and ttl value.
114 : */
115 :
116 :
117 : /** \brief Initialize a row object.
118 : *
119 : * This function initializes a row object. You must specify the
120 : * key of the row and that's the only parameter that a row supports at
121 : * this time.
122 : *
123 : * The key of the row is a binary buffer of data. It must be at least 1 byte
124 : * and at most 64Kb minus 1 (65535 bytes).
125 : *
126 : * A row is composed of multiple cells (called columns in Cassandra.)
127 : *
128 : * \exception exception
129 : * The key of the row cannot be empty or more than 64Kb. If that happens,
130 : * this exception is raised.
131 : *
132 : * \param[in] table The parent table of this row.
133 : * \param[in] row_key The key of this row.
134 : */
135 0 : row::row(std::shared_ptr<table> table, const QByteArray& row_key)
136 : : f_table(table)
137 0 : , f_key(row_key)
138 : //f_cells() -- auto-init
139 : {
140 0 : if(f_key.size() == 0) {
141 0 : throw exception("row key cannot be empty");
142 : }
143 0 : if(f_key.size() > 65535) {
144 0 : throw exception("row key is more than 64Kb");
145 : }
146 0 : }
147 :
148 :
149 : /** \brief Clean up the row object.
150 : *
151 : * This function ensures that all resources allocated by the
152 : * row are released.
153 : */
154 0 : row::~row()
155 : {
156 : try
157 : {
158 : // do an explicit clearCache() so we can try/catch otherwise we
159 : // could get a throw in the destructor
160 : //
161 0 : clearCache();
162 : }
163 0 : catch(const exception&)
164 : {
165 : // ignore, not much else we can do in a destructor
166 : }
167 0 : }
168 :
169 :
170 : /** \brief Retrieve the name of the row.
171 : *
172 : * This function returns the name of the row as specified in the
173 : * constructor.
174 : *
175 : * The name cannot be changed.
176 : *
177 : * Note that if you created the row with a binary key (i.e. a
178 : * QByteArray parameter) then you CANNOT retrieve the row name.
179 : * Instead, use the rowKey() function.
180 : *
181 : * \return A string with the key name.
182 : *
183 : * \sa rowKey()
184 : */
185 0 : QString row::rowName() const
186 : {
187 0 : return QString::fromUtf8(f_key.data());
188 : }
189 :
190 :
191 : /** \brief Retrieve the row key.
192 : *
193 : * This function returns the key of this row. The key is a binary buffer
194 : * of data. This function works whether the row was created with a name
195 : * or a key.
196 : *
197 : * Note that when creating a row with a binary key, you cannot retrieve
198 : * it using the rowName() function.
199 : *
200 : * \return A buffer of data representing the row key.
201 : *
202 : * \sa rowName()
203 : */
204 0 : const QByteArray& row::rowKey() const
205 : {
206 0 : return f_key;
207 : }
208 :
209 :
210 : /** \brief Retrieve the current timeout.
211 : *
212 : * This function retrieves the timeout used to wait less or more that
213 : * a statement completes.
214 : *
215 : * The value is in milliseconds.
216 : *
217 : * If the value is set to 0 then the default order timeout gets used.
218 : * This is often 10 seconds. This is often used the backend to increase
219 : * our chances of getting the data back because in many cases getting the
220 : * info is more important than speed.
221 : *
222 : * \return The timeout in milliseconds.
223 : */
224 0 : int32_t row::timeout() const
225 : {
226 0 : return f_timeout_ms;
227 : }
228 :
229 :
230 : /** \brief Timeout for orders sent from this row object.
231 : *
232 : * This value is the timeout in milleseconds for any CQL statement sent
233 : * to the backend by the row object.
234 : *
235 : * \param[in] timeout_ms The new timeout value in milliseconds.
236 : */
237 0 : void row::setTimeout(int32_t const timeout_ms)
238 : {
239 0 : f_timeout_ms = timeout_ms;
240 0 : }
241 :
242 :
243 : /** \brief Retrieve the number of cells defined in this row.
244 : *
245 : * This function retrieves the number of cells currently defined in this row,
246 : * depending on the specified predicate (by default, all the cells.)
247 : *
248 : * This counts the number of cells available in the Cassandra database. It
249 : * may be different from the number of cells in the memory cache. (i.e. the
250 : * value returned by getCells().size())
251 : *
252 : * \param[in] column_predicate The predicate used to select which columns to count.
253 : *
254 : * \return The number of cells defined in this row.
255 : *
256 : * \note This method no longer changes the row set from the query!
257 : *
258 : * \sa getCells(), table::readRows()
259 : */
260 0 : int row::cellCount( const cell_predicate::pointer_t column_predicate )
261 : {
262 : //return static_cast<int>(f_cells.size());
263 0 : return parentTable()->getCellCount(f_key, column_predicate);
264 : }
265 :
266 :
267 : /** \brief Read the cells as defined by a default column predicate.
268 : *
269 : * This function is the same as the readCells() with a column predicate
270 : * only it uses a default predicate which is to read all the columns
271 : * available in that row, with a limit of 100 cells.
272 : *
273 : * In this case the predicate has no column names or range boundaries
274 : * and no index capabilities. This mode should be used on rows which you
275 : * know have a limited number of cells. Otherwise, you should use the
276 : * other readCells() version.
277 : *
278 : * To know how many cells were read, save the returned value. When you
279 : * use the count() function on the map returned by the getCells() function,
280 : * you actually get the total number of cells ever read since the
281 : * creation of this row in memory or the last call to clearCache().
282 : * Most importantly, the cellCount() function returns the total number of
283 : * cells available in the Cassandra database. Not the number of cells
284 : * currently available in memory.
285 : *
286 : * \note
287 : * The number of columns read (the value returned by this function) may be
288 : * smaller than the total number of columns available in memory if you did
289 : * not clear the cache first.
290 : *
291 : * \return The number of cells read from Cassandra, can be zero.
292 : *
293 : * \sa cellCount()
294 : * \sa getCells()
295 : * \sa clearCache()
296 : */
297 0 : uint32_t row::readCells()
298 : {
299 0 : return f_cells.size();
300 : }
301 :
302 :
303 : /** \brief Read the cells as defined by the predicate.
304 : *
305 : * This function reads a set of cells as specified by the specified
306 : * predicate. If you use the default cell_predicate, then
307 : * the first 100 cells are read.
308 : *
309 : * If you are using columns as an index, then the column_predicate
310 : * parameter gets modified by this function. The start column name
311 : * is updated with the name of the last row read on each iteration.
312 : *
313 : * See the cell_predicate for more information on how to
314 : * select columns.
315 : *
316 : * This function is often called to read an entire row in memory all
317 : * at once (this is faster than reading the row one value at a time
318 : * if you anyway are likely to read most of the columns.) However,
319 : * this function should not be used that way if the row includes an
320 : * index.
321 : *
322 : * \param[in,out] column_predicate The predicate used to select which columns to read.
323 : *
324 : * \note This method no longer changes the row set from the query!
325 : *
326 : * \sa setIndex(), table::readRows()
327 : */
328 0 : uint32_t row::readCells( cell_predicate::pointer_t column_predicate )
329 : {
330 0 : size_t idx(0);
331 0 : order_result selected_cells_result;
332 :
333 0 : f_cells.clear();
334 :
335 0 : if( f_cursor_index != -1 )
336 : {
337 : // Note: the "FETCH" is ignored, only the type is used in this case
338 : //
339 0 : order select_more_cells;
340 0 : select_more_cells.setCql("FETCH", order::type_of_result_t::TYPE_OF_RESULT_FETCH);
341 0 : select_more_cells.setCursorIndex(f_cursor_index);
342 0 : if(f_timeout_ms > 0)
343 : {
344 0 : select_more_cells.setTimeout(f_timeout_ms);
345 : }
346 0 : order_result select_more_cells_result(parentTable()->getProxy()->sendOrder(select_more_cells));
347 0 : selected_cells_result.swap(select_more_cells_result);
348 0 : if(!selected_cells_result.succeeded())
349 : {
350 0 : throw exception("select cells failed (FETCH)");
351 : }
352 :
353 0 : if(selected_cells_result.resultCount() == 0)
354 : {
355 0 : closeCursor();
356 0 : return 0;
357 : }
358 : }
359 : else
360 : {
361 0 : auto row_predicate = std::make_shared<row_key_predicate>();
362 0 : row_predicate->setRowKey( f_key );
363 :
364 : // setup the consistency level
365 0 : consistency_level_t consistency_level( parentTable()->parentContext()->parentCassandra()->defaultConsistencyLevel() );
366 0 : if( column_predicate )
367 : {
368 0 : consistency_level_t const default_consistency_level(consistency_level);
369 0 : consistency_level = column_predicate->consistencyLevel();
370 0 : if( consistency_level == CONSISTENCY_LEVEL_DEFAULT )
371 : {
372 : // reset back to default if not defined in the column_predicate
373 0 : consistency_level = default_consistency_level;
374 : }
375 : }
376 :
377 : // prepare the CQL order
378 0 : QString query_string( QString("SELECT column1,value FROM %1.%2")
379 0 : .arg(parentTable()->contextName())
380 0 : .arg(parentTable()->tableName())
381 0 : );
382 0 : int bind_count = 0;
383 0 : if( column_predicate )
384 : {
385 0 : row_predicate->setCellPredicate( column_predicate );
386 : }
387 0 : row_predicate->appendQuery( query_string, bind_count );
388 :
389 : // WARNING: the row_predicate we create right here, when the
390 : // ALLOW FILTERING flag can only be set by the caller
391 : // in the column_predicate
392 : //
393 0 : if(column_predicate->allowFiltering())
394 : {
395 0 : query_string += " ALLOW FILTERING";
396 : }
397 :
398 : //
399 : //std::cerr << "query=[" << query_string.toUtf8().data() << "]" << std::endl;
400 0 : order select_cells;
401 0 : select_cells.setCql(query_string, order::type_of_result_t::TYPE_OF_RESULT_DECLARE);
402 0 : select_cells.setConsistencyLevel( consistency_level );
403 0 : select_cells.setColumnCount(2);
404 0 : if(f_timeout_ms > 0)
405 : {
406 0 : select_cells.setTimeout(f_timeout_ms);
407 : }
408 :
409 0 : row_predicate->bindOrder( select_cells );
410 :
411 : //
412 0 : if( column_predicate )
413 : {
414 0 : select_cells.setPagingSize( column_predicate->count() );
415 : }
416 : //
417 :
418 0 : order_result select_cells_result(parentTable()->getProxy()->sendOrder(select_cells));
419 0 : selected_cells_result.swap(select_cells_result);
420 0 : if(!selected_cells_result.succeeded())
421 : {
422 0 : throw exception("select cells failed (SELECT)");
423 : }
424 :
425 0 : if(selected_cells_result.resultCount() < 1)
426 : {
427 0 : throw exception("select cells did not return a cursor index");
428 : }
429 0 : f_cursor_index = int32Value(selected_cells_result.result(0));
430 0 : if(f_cursor_index < 0)
431 : {
432 0 : throw logic_exception("received a negative number as cursor index");
433 : }
434 :
435 : // ignore parameter one, it is not a row of data
436 0 : idx = 1;
437 : }
438 :
439 0 : size_t const max_results(selected_cells_result.resultCount());
440 : #ifdef _DEBUG
441 0 : if((max_results - idx) % 2 != 0)
442 : {
443 : // the number of results must be a multiple of 2, although on
444 : // the SELECT (first time in) we expect one additional result
445 : // which represents the cursor index
446 0 : throw logic_exception("the number of results must be an exact multipled of 2!");
447 : }
448 : #endif
449 0 : size_t result_size = 0;
450 0 : for(; idx < max_results; idx += 2, ++result_size )
451 : {
452 0 : const QByteArray column_key( selected_cells_result.result( idx + 0 ) );
453 0 : const QByteArray data ( selected_cells_result.result( idx + 1 ) );
454 :
455 0 : cell::pointer_t new_cell( getCell( column_key ) );
456 0 : new_cell->assignValue( value(data) );
457 : }
458 :
459 0 : return result_size;
460 : }
461 :
462 :
463 : /** \brief Retrieve a cell from the row.
464 : *
465 : * This function retrieves a cell from this row. If the cell
466 : * does not exist, it is created.
467 : *
468 : * Note that the cell is not saved in the Cassandra database
469 : * unless you save a value in it (and assuming the context does
470 : * not only exist in memory.)
471 : *
472 : * This function accepts a column name. The UTF-8 version of it is used to
473 : * retrieve the data from Cassandra.
474 : *
475 : * \param[in] column_name The name of the column referencing this cell.
476 : *
477 : * \return A shared pointer to the cell.
478 : */
479 0 : cell::pointer_t row::getCell(const char *column_name)
480 : {
481 0 : return getCell( QByteArray(column_name, qstrlen(column_name)) );
482 : }
483 :
484 :
485 : /** \brief Retrieve a cell from the row.
486 : *
487 : * This function retrieves a cell from this row. If the cell
488 : * does not exist, it is created.
489 : *
490 : * Note that the cell is not saved in the Cassandra database
491 : * unless you save a value in it (and assuming the context does
492 : * not only exist in memory.)
493 : *
494 : * This function accepts a column name. The UTF-8 version of it is used to
495 : * retrieve the data from Cassandra.
496 : *
497 : * \param[in] column_name The name of the column referencing this cell.
498 : *
499 : * \return A shared pointer to the cell.
500 : */
501 0 : cell::pointer_t row::getCell(const QString& column_name)
502 : {
503 0 : return getCell(column_name.toUtf8());
504 : }
505 :
506 :
507 : /** \brief Retrieve a cell from the row.
508 : *
509 : * This function retrieves a cell from this row. If the cell
510 : * does not exist, it is created.
511 : *
512 : * Note that the cell is not saved in the Cassandra database
513 : * unless you save a value in it (and assuming the context does
514 : * not only exist in memory.)
515 : *
516 : * This function makes use of a binary key to reference the cell.
517 : *
518 : * \note
519 : * This function cannot be used to read a composite column unless
520 : * you know how to build the QByteArray to do so. I suggest you
521 : * use the compositeCell() function instead.
522 : *
523 : * \param[in] column_key The binary key of the column referencing this cell.
524 : *
525 : * \return A shared pointer to the cell.
526 : *
527 : * \sa compositeCell()
528 : * \sa compositeCell() const
529 : */
530 0 : cell::pointer_t row::getCell(const QByteArray& column_key)
531 : {
532 : // column already exists?
533 0 : cells::iterator ci(f_cells.find(column_key));
534 0 : if(ci != f_cells.end()) {
535 0 : return ci.value();
536 : }
537 :
538 : // this is a new column, allocate it
539 0 : cell::pointer_t c(new cell(shared_from_this(), column_key));
540 0 : f_cells.insert(column_key, c);
541 0 : return c;
542 : }
543 :
544 :
545 : /** \brief Retrieve the map of cells.
546 : *
547 : * This function returns a constant reference to the map of cells defined in
548 : * the row.
549 : *
550 : * This map does not generally represent all the cells of a row as only those
551 : * that you already accessed in read or write mode will be defined in memory.
552 : *
553 : * \note
554 : * The order of the cells in memory may not be the same as the order of the
555 : * cells in the database. This is especially true if the data is not integers.
556 : * (i.e. floating point numbers, UTF-8 or other encodings with characters
557 : * that are not ordered in the same order as the bytes representing them,
558 : * etc.) This also depends on the definition of the type in Cassandra.
559 : * Positive integers should always be properly sorted. Negative integers may
560 : * be a problem if you use the "wrong" type in Cassandra. FYI, the order of
561 : * the map of cells uses the QByteArray < operator.
562 : *
563 : * \warning
564 : * When reading cells from a row representing an index, you probably want to
565 : * clear the cells already read before the next read. This is done with the
566 : * clearCache() function. Then getCells().isEmpty() returns true if no more
567 : * cells can be read from the database.
568 : *
569 : * \warning
570 : * The dropCell() function actually deletes the cell from this map and
571 : * therefore the map becomes invalid if you use an iterator in a loop.
572 : * Make sure you reset the iterator each time.
573 : *
574 : * \return The map of cells referenced by column keys.
575 : *
576 : * \sa clearCache()
577 : */
578 0 : const cells& row::getCells() const
579 : {
580 0 : return f_cells;
581 : }
582 :
583 :
584 : /** \brief Retrieve a cell from the row.
585 : *
586 : * This function retrieves a cell from this row. If the cell
587 : * does not exist, it returns a NULL pointer (i.e. isNull() on the
588 : * shared pointer returns true.)
589 : *
590 : * This function accepts a column name. The UTF-8 version of it is used to
591 : * retrieve the data from Cassandra.
592 : *
593 : * \warning
594 : * This function does NOT attempt to read the cell from the Cassandra database
595 : * system. It only checks whether the cell already exists in memory. To check
596 : * whether the cell exists in the database, use the exists() function instead.
597 : *
598 : * \param[in] column_name The name of the column referencing this cell.
599 : *
600 : * \return A shared pointer to the cell.
601 : *
602 : * \sa getCell()
603 : * \sa exists()
604 : */
605 0 : cell::pointer_t row::findCell(const QString& column_name) const
606 : {
607 0 : return findCell(column_name.toUtf8());
608 : }
609 :
610 :
611 : /** \brief Retrieve a cell from the row.
612 : *
613 : * This function retrieves a cell from this row. If the cell
614 : * does not exist, it returns a NULL pointer (i.e. isNull() on the
615 : * shared pointer returns true.)
616 : *
617 : * This function makes use of a binary key to reference the cell.
618 : *
619 : * \warning
620 : * This function does NOT attempt to read the cell from the Cassandra database
621 : * system. It only checks whether the cell already exists in memory. To check
622 : * whether the cell exists in the database, use the exists() function instead.
623 : *
624 : * \param[in] column_key The binary key of the column referencing this cell.
625 : *
626 : * \return A shared pointer to the cell.
627 : *
628 : * \sa getCell()
629 : * \sa exists()
630 : */
631 0 : cell::pointer_t row::findCell(const QByteArray& column_key) const
632 : {
633 0 : cells::const_iterator ci(f_cells.find(column_key));
634 0 : if(ci == f_cells.end()) {
635 0 : cell::pointer_t null;
636 0 : return null;
637 : }
638 0 : return *ci;
639 : }
640 :
641 :
642 : /** \brief Check whether a cell exists in this row.
643 : *
644 : * The check is happening in memory first. If the cell doesn't exist in memory,
645 : * then the row checks in the Cassandra database.
646 : *
647 : * \todo
648 : * Look into why a cell is created when just checking for its existance.
649 : *
650 : * \bug
651 : * At this time this function CREATES the cell if it did not yet
652 : * exist!
653 : *
654 : * \param[in] column_name The column name.
655 : *
656 : * \return true if the cell exists, false otherwise.
657 : */
658 0 : bool row::exists(const char * column_name) const
659 : {
660 0 : return exists(QString(column_name));
661 : }
662 :
663 :
664 : /** \brief Check whether a cell exists in this row.
665 : *
666 : * The check is happening in memory first. If the cell doesn't exist in memory,
667 : * then the row checks in the Cassandra database.
668 : *
669 : * \todo
670 : * Look into why a cell is created when just checking for its existance.
671 : *
672 : * \bug
673 : * At this time this function CREATES the cell if it did not yet
674 : * exist!
675 : *
676 : * \param[in] column_name The column name.
677 : *
678 : * \return true if the cell exists, false otherwise.
679 : */
680 0 : bool row::exists(const QString& column_name) const
681 : {
682 0 : return exists(column_name.toUtf8());
683 : }
684 :
685 :
686 : /** \brief Check whether a cell exists in this row.
687 : *
688 : * The check is happening in memory first. If the cell doesn't exist in memory,
689 : * then the row checks in the Cassandra database.
690 : *
691 : * \param[in] column_key The column binary key.
692 : *
693 : * \return true if the cell exists, false otherwise.
694 : */
695 0 : bool row::exists(const QByteArray& column_key) const
696 : {
697 0 : cells::const_iterator ci(f_cells.find(column_key));
698 0 : if(ci != f_cells.end())
699 : {
700 : // exists in the cache already
701 0 : return true;
702 : }
703 :
704 : // This is already done by the readRows() method of table.
705 : // If you have an instance of this class without going through the
706 : // table interface, it won't work right.
707 : //
708 : // try reading this cell
709 0 : value value;
710 : try
711 : {
712 0 : if(!parentTable()->getValue(f_key, column_key, value))
713 : {
714 0 : return false;
715 : }
716 : }
717 0 : catch( const std::exception& )
718 : {
719 0 : return false;
720 : }
721 :
722 : // since we just got the value, we might as well cache it
723 : //
724 0 : cell::pointer_t c(const_cast<row *>(this)->getCell(column_key));
725 0 : c->assignValue(value);
726 :
727 0 : return true;
728 : }
729 :
730 :
731 : /** \brief Retrieve a cell from the row.
732 : *
733 : * This function retrieves a reference to a cell from this row in
734 : * array syntax.
735 : *
736 : * This version returns a writable cell and it creates a new cell
737 : * when one with the specified name doesn't already exist.
738 : *
739 : * This function accepts a column name. The UTF-8 version of it is used to
740 : * retrieve the data from Cassandra.
741 : *
742 : * \param[in] column_name The name of the column referencing this cell.
743 : *
744 : * \return A shared pointer to the cell.
745 : */
746 0 : cell& row::operator [] (const char * column_name)
747 : {
748 0 : return *getCell(column_name);
749 : }
750 :
751 :
752 : /** \brief Retrieve a cell from the row.
753 : *
754 : * This function retrieves a reference to a cell from this row in
755 : * array syntax.
756 : *
757 : * This version returns a writable cell and it creates a new cell
758 : * when one with the specified name doesn't already exist.
759 : *
760 : * This function accepts a column name. The UTF-8 version of it is used to
761 : * retrieve the data from Cassandra.
762 : *
763 : * \param[in] column_name The name of the column referencing this cell.
764 : *
765 : * \return A shared pointer to the cell.
766 : */
767 0 : cell& row::operator [] (const QString& column_name)
768 : {
769 0 : return *getCell(column_name);
770 : }
771 :
772 :
773 : /** \brief Retrieve a cell from the row.
774 : *
775 : * This function retrieves a reference to a cell from this row in
776 : * array syntax.
777 : *
778 : * This version returns a writable cell and it creates a new cell
779 : * when one with the specified name doesn't already exist.
780 : *
781 : * This function makes use of a binary key to reference the cell.
782 : *
783 : * \param[in] column_key The binary key of the column referencing this cell.
784 : *
785 : * \return A shared pointer to the cell.
786 : */
787 0 : cell& row::operator [] (const QByteArray& column_key)
788 : {
789 0 : return *getCell(column_key);
790 : }
791 :
792 :
793 : /** \brief Retrieve a cell from the row.
794 : *
795 : * This function retrieves a constant reference to a cell from this row
796 : * in array syntax.
797 : *
798 : * This version returns a read-only cell. If the cell doesn't exist,
799 : * the funtion raises an exception.
800 : *
801 : * This function accepts a column name. The UTF-8 version of it is used to
802 : * retrieve the data from Cassandra.
803 : *
804 : * \exception exception
805 : * This function requires that the cell being accessed already exist
806 : * in memory. If not, this exception is raised.
807 : *
808 : * \param[in] column_name The name of the column referencing this cell.
809 : *
810 : * \return A shared pointer to the cell.
811 : */
812 0 : const cell& row::operator [] (const char* column_name) const
813 : {
814 0 : return operator [] (QString(column_name));
815 : }
816 :
817 :
818 : /** \brief Retrieve a cell from the row.
819 : *
820 : * This function retrieves a constant reference to a cell from this row
821 : * in array syntax.
822 : *
823 : * This version returns a read-only cell. If the cell doesn't exist,
824 : * the funtion raises an exception.
825 : *
826 : * This function accepts a column name. The UTF-8 version of it is used to
827 : * retrieve the data from Cassandra.
828 : *
829 : * \exception exception
830 : * This function requires that the cell being accessed already exist
831 : * in memory. If not, this exception is raised.
832 : *
833 : * \param[in] column_name The name of the column referencing this cell.
834 : *
835 : * \return A shared pointer to the cell.
836 : */
837 0 : const cell& row::operator [] (const QString& column_name) const
838 : {
839 0 : return operator [] (column_name.toUtf8());
840 : }
841 :
842 :
843 : /** \brief Retrieve a cell from the row.
844 : *
845 : * This function retrieves a cell from this row in array syntax.
846 : *
847 : * This version returns a writable cell and it creates a new cell
848 : * when one with the specified name doesn't already exist.
849 : *
850 : * This function makes use of a binary key to reference the cell.
851 : *
852 : * \param[in] column_key The binary key of the column referencing this cell.
853 : *
854 : * \return A shared pointer to the cell.
855 : */
856 0 : const cell& row::operator [] (const QByteArray& column_key) const
857 : {
858 0 : cell::pointer_t p_cell( findCell(column_key) );
859 0 : if( !p_cell )
860 : {
861 0 : throw exception("named column while retrieving a cell was not found, cannot return a reference");
862 : }
863 :
864 0 : return *p_cell;
865 : }
866 :
867 :
868 : /** \brief Clear the cached cells.
869 : *
870 : * This function is used to clear all the cells that were cached in this row.
871 : *
872 : * As a side effect, all the cell objects are unparented which means
873 : * that you cannot use them anymore (doing so raises an exception.)
874 : */
875 0 : void row::clearCache()
876 : {
877 0 : closeCursor();
878 :
879 0 : f_cells.clear();
880 0 : }
881 :
882 :
883 : /** \brief Close the current cursor.
884 : *
885 : * This function closes the current cursor (i.e. the cursor used
886 : * to gather a set of rows and their data from a table.)
887 : */
888 0 : void row::closeCursor()
889 : {
890 0 : if(f_cursor_index >= 0)
891 : {
892 : // Note: the "CLOSE" CQL string is ignored
893 : //
894 0 : order close_cursor;
895 0 : close_cursor.setCql("CLOSE", order::type_of_result_t::TYPE_OF_RESULT_CLOSE);
896 0 : close_cursor.setCursorIndex(f_cursor_index);
897 0 : if(f_timeout_ms > 0)
898 : {
899 0 : close_cursor.setTimeout(f_timeout_ms);
900 : }
901 0 : order_result close_cursor_result(parentTable()->getProxy()->sendOrder(close_cursor));
902 0 : if(!close_cursor_result.succeeded())
903 : {
904 0 : throw exception("row::closeCursor(): closing cursor failed.");
905 : }
906 0 : f_cursor_index = -1;
907 : }
908 0 : }
909 :
910 :
911 : /** \brief Drop the named cell.
912 : *
913 : * This function is the same as the dropCell() that accepts a QByteArray
914 : * as its column key. It simply calls it after changing the column name into
915 : * a key.
916 : *
917 : * \param[in] column_name The name of the column to drop.
918 : */
919 0 : void row::dropCell(const char * column_name)
920 : {
921 0 : dropCell(QByteArray(column_name, qstrlen(column_name)));
922 0 : }
923 :
924 :
925 : /** \brief Drop the named cell.
926 : *
927 : * This function is the same as the dropCell() that accepts a QByteArray
928 : * as its column key. It simply calls it after changing the column name into
929 : * a key.
930 : *
931 : * \param[in] column_name The name of the column to drop.
932 : */
933 0 : void row::dropCell(const QString& column_name)
934 : {
935 0 : dropCell(column_name.toUtf8());
936 0 : }
937 :
938 :
939 : /** \brief Drop the specified cell from the Cassandra database.
940 : *
941 : * This function deletes the specified cell and its data from the Cassandra
942 : * database and from memory. To delete the cell immediately you want to set
943 : * the timestamp to now (i.e. use libdbproxy::timeofday() with the DEFINED
944 : * mode as mentioned below.)
945 : *
946 : * The timestamp \p mode can be set to value::TIMESTAMP_MODE_DEFINED
947 : * in which case the value defined in the \p timestamp parameter is used by the
948 : * Cassandra remove() function.
949 : *
950 : * By default the \p mode parameter is set to
951 : * value::TIMESTAMP_MODE_AUTO which means that the timestamp
952 : * value of the cell f_value parameter is used. This will NOT WORK RIGHT
953 : * if the timestamp of the cell value was never set properly (i.e. you
954 : * never read the cell from the Cassandra database and never called
955 : * the setTimestamp() function on the cell value.) You have two choices here.
956 : * You may specify the timestamp on the dropCell() call, or you may change
957 : * the cell timestamp:
958 : *
959 : * \code
960 : * // this is likely to fail if you created the cell in this session
961 : * row->dropCell(cell_name);
962 : *
963 : * // define the timestamp in the cell
964 : * cell->setTimestamp(libdbproxy::timeofday());
965 : *
966 : * // define the timestamp in the dropCell() call
967 : * row->dropCell(cell_name, value::TIMESTAMP_MODE_DEFINED, libdbproxy::timeofday());
968 : * \endcode
969 : *
970 : * The consistency level of the cell f_value is also passed to the Cassandra
971 : * remove() function. This means that by default you'll get whatever the
972 : * default is from your libdbproxy object, the default in the value or
973 : * whatever the value was when you last read the value. To change that
974 : * default you can retrieve the cell and set the consistency level as follow:
975 : *
976 : * \code
977 : * cell::pointer_t c(f_row->getCell(f_cell));
978 : * \endcode
979 : *
980 : * These 2 lines of code do NOT create the cell in the Cassandra cluster.
981 : * It only creates it in memory unless it was read earlier in which case
982 : * the cached copy is returned.
983 : *
984 : * \warning
985 : * The corresponding cell is marked as dropped, whether you kept a shared
986 : * pointer of that cell does not make it reusable. You must forget about it
987 : * after this call.
988 : *
989 : * \warning
990 : * If you used the getCells() function to retrieve a reference to the list of
991 : * cells as a reference, that list is broken when this function returns.
992 : * Make sure to re-retrieve the list after each call to the dropCell()
993 : * function.
994 : *
995 : * \param[in] column_key A shared pointer to the cell to remove.
996 : * \param[in] mode Specify the timestamp mode.
997 : * \param[in] timestamp Specify the timestamp to remove only cells that are equal or older.
998 : *
999 : * \sa getCell()
1000 : * \sa getCells()
1001 : * \sa libdbproxy::timeofday()
1002 : */
1003 0 : void row::dropCell( const QByteArray& column_key )
1004 : {
1005 0 : cell::pointer_t c(getCell(column_key));
1006 :
1007 0 : parentTable()->remove( f_key, column_key, c->consistencyLevel() );
1008 0 : f_cells.remove(column_key);
1009 0 : }
1010 :
1011 :
1012 : /** \brief Get the pointer to the parent object.
1013 : *
1014 : * \return Shared pointer to the table object with owns this object.
1015 : */
1016 0 : table::pointer_t row::parentTable() const
1017 : {
1018 0 : table::pointer_t table(f_table.lock());
1019 0 : if(table == nullptr)
1020 : {
1021 0 : throw exception("this row was dropped and is not attached to a table anymore");
1022 : }
1023 :
1024 0 : return table;
1025 : }
1026 :
1027 :
1028 : /** \brief Save a cell value that changed.
1029 : *
1030 : * This function calls the table insertValue() function to save the new value that
1031 : * was defined in a cell.
1032 : *
1033 : * \param[in] column_key The key used to identify the column.
1034 : * \param[in] value The new value of the cell.
1035 : */
1036 0 : void row::insertValue(const QByteArray& column_key, const value& value)
1037 : {
1038 0 : parentTable()->insertValue(f_key, column_key, value);
1039 0 : }
1040 :
1041 :
1042 : /** \brief Get a cell value from Cassandra.
1043 : *
1044 : * This function calls the table getValue() function to retrieve the currrent
1045 : * value that defined in a cell.
1046 : *
1047 : * If the cell does not exist, then value is set to the Null value.
1048 : *
1049 : * \param[in] column_key The key used to identify the column.
1050 : * \param[out] value To return the value of the cell.
1051 : *
1052 : * \return false when the value was not found in the database, true otherwise
1053 : */
1054 0 : bool row::getValue(const QByteArray& column_key, value& value)
1055 : {
1056 0 : return parentTable()->getValue(f_key, column_key, value);
1057 : }
1058 :
1059 :
1060 : /** \brief Add a value to a Cassandra counter.
1061 : *
1062 : * This function calls the table addValue() function to add the specified
1063 : * value to the Cassandra counter that is defined in a cell.
1064 : *
1065 : * If the cell counter does not exist yet, then value is set to the specified
1066 : * value.
1067 : *
1068 : * \note this is a synonym for row::insertValue(), since counters
1069 : * are automatically sensed and handled with an "UPDATE" instead of an "INSERT".
1070 : *
1071 : * \param[in] column_key The key used to identify the column.
1072 : * \param[in] value To value to add to this counter.
1073 : */
1074 0 : void row::addValue(const QByteArray& column_key, int64_t value)
1075 : {
1076 0 : return parentTable()->insertValue(f_key, column_key, value);
1077 : }
1078 :
1079 :
1080 6 : } // namespace libdbproxy
1081 : // vim: ts=4 sw=4 et
|