Line data Source code
1 : /*
2 : * Text:
3 : * libsnapwebsites/src/libdbproxy/libdbproxy.cpp
4 : *
5 : * Description:
6 : * Handling of the cassandra session.
7 : *
8 : * Documentation:
9 : * See each function below.
10 : *
11 : * License:
12 : * Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved
13 : *
14 : * https://snapwebsites.org/
15 : * contact@m2osw.com
16 : *
17 : * Permission is hereby granted, free of charge, to any person obtaining a
18 : * copy of this software and associated documentation files (the
19 : * "Software"), to deal in the Software without restriction, including
20 : * without limitation the rights to use, copy, modify, merge, publish,
21 : * distribute, sublicense, and/or sell copies of the Software, and to
22 : * permit persons to whom the Software is furnished to do so, subject to
23 : * the following conditions:
24 : *
25 : * The above copyright notice and this permission notice shall be included
26 : * in all copies or substantial portions of the Software.
27 : *
28 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
29 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
31 : * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
32 : * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
33 : * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
34 : * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 : */
36 :
37 : #pragma GCC push
38 : #pragma GCC diagnostic ignored "-Wundef"
39 : #include <sys/time.h>
40 : #pragma GCC pop
41 :
42 : #include "libdbproxy/libdbproxy.h"
43 :
44 : #include "snapwebsites/log.h"
45 :
46 : #include <casswrapper/schema.h>
47 :
48 : #include <QtCore>
49 :
50 : #include <iostream>
51 : #include <sstream>
52 :
53 : #include <unistd.h>
54 :
55 :
56 : /** \brief The libdbproxy namespace includes all the Cassandra extensions.
57 : *
58 : * This namespace includes all the Cassandra extensions in Qt. Note that we
59 : * suggest that you try to avoid using this namespace as in:
60 : *
61 : * \code
62 : * using namespace libdbproxy;
63 : * \endcode
64 : *
65 : * Yet... in older C++ compilers, without using the namespace, all
66 : * the [] operators are not accessible so the advanced C++ syntax is
67 : * not available to you. In other words, you should have such statements
68 : * wherever you want to access the libdbproxy data with the advanced
69 : * C++ syntax.
70 : */
71 : namespace libdbproxy
72 : {
73 :
74 : /** \mainpage
75 : *
76 : * \image html logo.png
77 : *
78 : * \section summary Summary
79 : *
80 : * -- \ref libqtcassandra
81 : *
82 : * \par
83 : * \ref cpplib
84 : *
85 : * \par
86 : * \ref organization
87 : *
88 : * \par
89 : * \ref communication
90 : *
91 : * \par
92 : * \ref threading
93 : *
94 : * \par
95 : * \ref qt
96 : *
97 : * -- \ref cassandra
98 : *
99 : * \par
100 : * \ref terminology
101 : *
102 : * \par
103 : * \ref faq
104 : *
105 : * \par
106 : * \ref changes
107 : *
108 : * -- \ref copyright
109 : *
110 : * \section libqtcassandra libQtCassandra
111 : *
112 : * \subsection cpplib A C++ library
113 : *
114 : * It looks like there was a need for a C++ library for the Cassandra system.
115 : * Looking around the only one available was indeed in C++ and helping a bit
116 : * by hiding some of the Thrift implementation, but in the end you still had
117 : * to know how the Thrift structure had to be used to make it all work.
118 : *
119 : * So, I decided to write my own version, especially, I was thinking that it
120 : * should be possible to access the data with the following simple array like
121 : * syntax:
122 : *
123 : * \code
124 : * cluster[context][table][row][column] = value;
125 : * value = cluster[context][table][row][column];
126 : * \endcode
127 : *
128 : * In other words, you should be able to create a cluster object and then from
129 : * that object access the contexts (keyspaces), the tables (column families),
130 : * the rows and columns (actually, the values are saved in cells in this
131 : * library, but the name used in the cell is the name of the column.)
132 : *
133 : * Looking at the implementation of a system I will create soon after having
134 : * done this library it very much feels like this is going to be the basis for
135 : * many accesses to the Cassandra database.
136 : *
137 : * \todo
138 : * The one thing still missing is a clean way of handling the exception. At
139 : * this time the libQtCassandra library generate runtime_error and logic_error
140 : * when you missuse a function. It also captures some Thrift exception
141 : * in a few cases (although we try to avoid doing so, the connect() is the
142 : * main one that we capture so you can attempt to connect and instead of
143 : * throwing you get a return value of false when the connect failed.)
144 : * But... at this time, if you get a Cassandra Thrift exception, you cannot
145 : * do anything with it unless you include the Cassandra Thrift header files
146 : * (otherwise you don't get the declaration of these exceptions.)
147 : * Thus, we should overload all the exceptions and have try/catch around
148 : * all the calls to the Cassandra Thrift interface and convert those
149 : * exception in a libQtCassandra exception.
150 : *
151 : * \subsection organization The libQtCassandra organization
152 : *
153 : * \li Cluster
154 : *
155 : * The cluster is defined in a libdbproxy object. At the time you call connect()
156 : * you get a connection to the Cassandra server and you can start using the
157 : * other functions.
158 : *
159 : * Clusters are used to manage contexts and to access the Cassandra server.
160 : *
161 : * Note that internally all accesses to the Cassandra system is done with
162 : * one f_client pointer.
163 : *
164 : * \li Contexts
165 : *
166 : * A cluster can have many contexts. It has the default System contexts that
167 : * comes with the Cassandra environment. Others can be create()'d, and opened
168 : * on later connections.
169 : *
170 : * Contexts can be used to manage tables.
171 : *
172 : * \li Tables
173 : *
174 : * A table has a name and it includes one to many rows. The number of rows can
175 : * be really large and not impact the Cassandra cluster much.
176 : *
177 : * The readRows() function is used to read a range of rows as determined by
178 : * the row predicate.
179 : *
180 : * \li Column Definitions
181 : *
182 : * A table may have a list of column definitions. You generally do not need a
183 : * column definition unless you want to force a certain type of data in a
184 : * column. In that case a column definition is required (Assuming that the
185 : * default type is not what you want for that column.)
186 : *
187 : * We do not use this information internally. Only to forward to the Cassandra
188 : * server (and also we read the current status from the Cassandra server.)
189 : *
190 : * At a later time, we may check the type defined here and in the row (if not
191 : * defined as a column type) and check the data supplied to verify that the
192 : * data passed to the row is valid before sending it to the server. This is
193 : * probably not necessary because if you have bad data your application won't
194 : * work, no matter what.
195 : *
196 : * \li Rows
197 : *
198 : * A row is named using a key, which means the name can include any
199 : * character including '\0' (it just cannot be empty.) The key can actually
200 : * be an integer, or even a complete structure. Rows include cells.
201 : *
202 : * Contrary to an SQL engine where the number of columns is generally relatively
203 : * restrained, the number of cells in a row can be very large and accessing a
204 : * specific cell remains fast (assuming the cell names remain reasonably small.)
205 : *
206 : * For this reason, when you read a row, you do not have to read all the
207 : * cells on that row. Instead, you may read just one or two cells.
208 : *
209 : * Row names can use UUIDs, in that case use a QUuid object and directly call
210 : * the different table functions that accept a row key name.
211 : *
212 : * \li Cells
213 : *
214 : * A cell is named using a column key, which means the name can include any
215 : * character including '\0' (it just cannot be empty.) Just like for rows,
216 : * the key can be an integer, or a structure. With a well organized structure
217 : * you can actually sort your data in some pretty advanced manner.
218 : *
219 : * Cell keys are attached to a value. This value is limited to 2Gb in size,
220 : * although remember that sending 2Gb over a network connection is going to
221 : * be slow and the entire data of one cell must be transferred when written
222 : * or read.
223 : *
224 : * Note that when you create an index, you may use a default value (including
225 : * the Null value.) For example, if you have a Page table with a row numbered
226 : * 3 and that row has a cell named Path with a value X. You may add this row
227 : * like this (somewhat simplified for this example):
228 : *
229 : * \code
230 : * // add the value "X" to row "3" in table "Page"
231 : * int64_t row_number = 3;
232 : * value row_id;
233 : * row_id = row_number;
234 : * value value;
235 : * value = "X";
236 : * context["Page"][row_id]["Path"] = value;
237 : *
238 : * // "simultaneously," create an index from value to row identifier
239 : * value empty;
240 : * context["Page_Path_Index"][value.binaryValue()][row_id] = empty;
241 : *
242 : * // search rows in pages that have a path set to value
243 : * context["Page_Path_Index"][value.binaryValue()].readCells(predicate);
244 : * \endcode
245 : *
246 : * As you can see, the second write uses the empty value so you do not waste
247 : * any space in the database (note however that at this time there is a
248 : * problem with the QRow::exists() function which creates a cell when it
249 : * doesn't exist so an empty value may not always be practical.)
250 : *
251 : * Note that when writing a value to a cell, the consistency level of the
252 : * value is used. When reading a cell, the value is returned so its
253 : * consistency level cannot be changed by you before the call. By default
254 : * the value as defined by the setDefaultConsistencyLevel() function is
255 : * used (the default being ONE.) You may change that default if you are
256 : * expected to use that consistency level in the majority of places.
257 : * Otherwise, you can also set the consistency level on a cell. Remember
258 : * that this is used for reads only. There are additional details in the
259 : * cell::setConsistencyLevel() function
260 : *
261 : * Cell names can use UUIDs, in that case use a QUuid object and directly call
262 : * the different row functions that accept a cell key name.
263 : *
264 : * \li Values
265 : *
266 : * The cells are set to a specific value using the value class.
267 : * Beside their binary data, values have a timestamp that represents the time
268 : * and date when they were created and a TTL (time to live) value in seconds.
269 : * By default the timestamp is set to gettimeofday() and the TTL is set to 0
270 : * (which means permanent.)
271 : *
272 : * The values are cached in memory by the libQtCassandra library. Not only
273 : * that, multiple write of the same value to the same cell will generate a
274 : * single write to the Cassandra database (i.e. the timestamp is ignored in
275 : * this case, see the cell class for more info.)
276 : *
277 : * Values also include a consistency level. By default this is set to ONE
278 : * which may not be what you want to have... (in many cases QUORUM is
279 : * better for writes.) The consistency level is defined here because it can
280 : * then easily be propagated when using the array syntax.
281 : *
282 : * \code
283 : * value v;
284 : * v.setDoubleValue(3.14159);
285 : * v.setConsistencyLevel(libdbproxy::CONSISTENCY_LEVEL_EACH_QUORUM);
286 : * v.setTimestamp(counter);
287 : * v.setTtl(60 * 60 * 24); // live for 1 day
288 : * ...
289 : * \endcode
290 : *
291 : * \image html CassandraOrganization.png
292 : *
293 : * \subsection communication Communication between objects
294 : *
295 : * In general, the objects communicate between parent and child. However, some
296 : * times the children need to access the QCassandraPrivate functions to send
297 : * an order to the Cassandra server. This is done by implementing functions
298 : * in the parents and calling functions in cascade.
299 : *
300 : * The simplest message sent to the Cassandra server comes from the connect()
301 : * which is the first time happen. It starts from the libdbproxy object and
302 : * looks something like this:
303 : *
304 : * \msc
305 : * libdbproxy,QCassandraPrivate,Thrift,Cassandra;
306 : * libdbproxy=>QCassandraPrivate [label="connect()"];
307 : * QCassandraPrivate=>Thrift [label="connect()"];
308 : * Thrift->Cassandra [label="[RPC Call]"];
309 : * ...;
310 : * Cassandra->Thrift [label="[RPC Reply]"];
311 : * Thrift>>QCassandraPrivate [label="return"];
312 : * QCassandraPrivate>>libdbproxy [label="return"];
313 : * \endmsc
314 : *
315 : * When you save a value in a cell, then the cell calls
316 : * the row, which calls the table, which calls the context which has access
317 : * to the QCassandraPrivate:
318 : *
319 : * \msc
320 : * width=900;
321 : * cell,row,table,context,libdbproxy,QCassandraPrivate,Thrift,Cassandra;
322 : * cell=>row [label="insertValue()"];
323 : * row=>table [label="insertValue()"];
324 : * table=>context [label="insertValue()"];
325 : * context=>libdbproxy [label="makeCurrent()", linecolor="red"];
326 : * libdbproxy abox Cassandra [label="if context is not already current"];
327 : * libdbproxy=>QCassandraPrivate [label="setContext()"];
328 : * QCassandraPrivate=>Thrift [label="set_keyspace()"];
329 : * Thrift->Cassandra [label="[RPC Call]"];
330 : * ...;
331 : * Cassandra->Thrift [label="[RPC Reply]"];
332 : * Thrift>>QCassandraPrivate [label="return"];
333 : * QCassandraPrivate>>libdbproxy [label="return"];
334 : * libdbproxy abox Cassandra [label="end if"];
335 : * libdbproxy>>context [label="return"];
336 : * context=>libdbproxy [label="getPrivate()"];
337 : * libdbproxy>>context [label="return"];
338 : * context=>QCassandraPrivate [label="insertValue()"];
339 : * QCassandraPrivate=>Thrift [label="insert()"];
340 : * Thrift->Cassandra [label="[RPC Call]"];
341 : * ...;
342 : * Cassandra->Thrift [label="[RPC Reply]"];
343 : * Thrift>>QCassandraPrivate [label="return"];
344 : * QCassandraPrivate>>context [label="return"];
345 : * context>>table [label="return"];
346 : * table>>row [label="return"];
347 : * row>>cell [label="return"];
348 : * \endmsc
349 : *
350 : * As you can see the context makes itself current as required. This ensures
351 : * that the correct context is current when a Cassandra RPC event is sent.
352 : * This is completely automatic so you do not need to know whether the
353 : * context is current. Note that the function is optimized (i.e. the pointer
354 : * of the current context is saved in the libdbproxy object so if it doesn't
355 : * change we avoid the set_keyspace() call.)
356 : *
357 : * Messages from rows, tables, and contexts work the same way, only they do
358 : * not include calls from the lower layers.
359 : *
360 : * The drop() calls have one additional call which deletes the children that
361 : * were just dropped. (i.e. if you drop a row, then its cells are also dropped.)
362 : *
363 : * \subsection threading Multi-thread support
364 : *
365 : * The libQtCassandra library is <strong>NOT</strong> multi-thread safe. This
366 : * is for several reasons, the main one being that we are not currently looking
367 : * into writing multi-threaded applications (on a server with a heavy load, you
368 : * have many processes running in parallel and having multiple threads in each
369 : * doesn't save you anything.)
370 : *
371 : * If you plan to have multiple threads, I currently suggest you create one
372 : * libdbproxy object per thread. The results will be similar, although it will
373 : * make use of more memory and more accesses to the Cassandra server (assuming
374 : * each thread accesses the common data, in that case you probably want to
375 : * manage
376 : * your own cache of the data.)
377 : *
378 : * \subsection qt Why Qt?
379 : *
380 : * First of all, calm down, you won't need to run X or some other graphical
381 : * environment. We only use QtCore.
382 : *
383 : * Qt has many objects ready made that work. It seems to make sense to use
384 : * them. More specifically, it has a QString which supports UTF-8 out of the
385 : * box. So instead of using iconv and std::string, Qt seems like a good choice
386 : * to me.
387 : *
388 : * The main objects we use are the QString, QByteArray, QMap, QVector, and
389 : * QObject. The QByteArray is used to transport binary data. The QMap is
390 : * used to sort out the list of contexts, tables, rows, cells, and column
391 : * definitions.
392 : *
393 : * Note that the QByteArray has a great advantage over having your own buffer:
394 : * it knows how to share its data pointer between multiple instances. Only a
395 : * write will require a copy of the data buffer.
396 : *
397 : * Many of the classes in the libQtCassandra derive from the QObject class.
398 : * This means they cannot be copied. Especially, the libdbproxy object
399 : * which includes the socket connection to the Cassandra server (on top of
400 : * the contexts, tables, rows, cells, column definitions... and since they
401 : * all have a parent/child scheme!)
402 : *
403 : * \todo
404 : * In regard to QObject's, we want to make use of the QObject name field to save
405 : * smaller, QString names in objects. That way you could look for an object
406 : * in memory with the find<>() algorithm.
407 : *
408 : * \todo
409 : * The QObject is capable of managing parent/children. We want to make use
410 : * of that capability for the cluster's contexts, the context's tables,
411 : * the table's rows, the row's cells. That way the data is managed 100% like
412 : * a Qt tree which would allow some algorithms to work on the tree without
413 : * having to know anything of the libQtCassandra library.
414 : *
415 : * \section cassandra The Cassandra System
416 : *
417 : * \subsection terminology Terminology Explained
418 : *
419 : * The Cassandra System comes with a terminology that can easily throw off
420 : * people who are used to more conventional database systems (most of that
421 : * terminology comes from the
422 : * <a href="https://snapwebsites.org/book/other-interesting-projects#big_tables">
423 : * Big Table document by Google</a>.)
424 : *
425 : * This library attempts to hide some of the Cassandra terminology by offering
426 : * objects that seem to be a little closer to what you'd otherwise expect in a
427 : * database environment.
428 : *
429 : * One Cassandra server instance runs against one cluster. We kept the term
430 : * cluster as it is the usual term for a set of databases. Writing this in
431 : * terms of C++ array syntax, the system looks like a multi-layer array as
432 : * in (you can use this syntax with libQtCassandra, btw):
433 : *
434 : * \code
435 : * cluster[context][table][row][column] = value;
436 : * value = cluster[context][table][row][column];
437 : * \endcode
438 : *
439 : * Note that in Cassandra terms, it would look like this instead:
440 : *
441 : * \code
442 : * cluster[keyspace][column_family][key][column] = value;
443 : * value = cluster[keyspace][column_family][key][column];
444 : * \endcode
445 : *
446 : * One cluster is composed of multiple contexts, what Cassandra calls a
447 : * keyspace. One context corresponds to one database. A context can be
448 : * setup to replicate or not and it manages memory caches (it includes
449 : * many replication and cache parameters.) We call these the context
450 : * because once a cluster connection is up, you can only have one
451 : * active context at a time. (If you worked with OpenGL, then this
452 : * is very similar to the glMakeCurrent() function call.)
453 : *
454 : * Although the libQtCassandra library 100% hides the current context
455 : * calls since it knows when a context or another needs to be curent,
456 : * switching between contexts can be costly. Instead you may want to
457 : * look into using two libdbproxy objects each with a different context.
458 : *
459 : * Different contexts are useful in case you want to use one context for
460 : * statistic data or other data that are not required to be read as
461 : * quickly as your main data and possibly needs much less replication
462 : * (i.e. ONE for writes and ALL for reads on a statistic table would
463 : * work greatly.)
464 : *
465 : * One context is composed of tables, what Cassandra calls a column family.
466 : * By default, all the tables are expected to get replicated as defined
467 : * in this context. However, some data may be marked as temporary with
468 : * a time to live (TTL). Data with a very small TTL is likely to only
469 : * live in the memory cache and never make it to the disk.
470 : *
471 : * Note that the libQtCassandra library lets you create table objects
472 : * that do not exist in the Cassandra system. These are memory only
473 : * tables (when you quit they're gone!) These can be used to manage
474 : * run-time globals via the libQtCassandra system. Obviously, it would
475 : * most certainly be more effective (faster) to just use normal C/C++
476 : * globals. However, it can be useful to call a function that usually
477 : * accesses a Cassandra table, but in that case you dynamically
478 : * generate said data.
479 : *
480 : * A table is identified by a name. At this time, we only offer QString
481 : * for table names. Table names must be letters, digits and the
482 : * underscore. This limitation comes from the fact that it is used to
483 : * represent a filename. Similarly, it may be limited in length (OS
484 : * dependent I guess, the Cassandra system does not say from what I've
485 : * seen. Anyway it should be easy to keep the table names small.)
486 : *
487 : * Tables are composed of rows. Here the scheme somewhat breaks from
488 : * the usual SQL database as rows are independent from each others.
489 : * This is because one row may have 10 "columns," and the other may have
490 : * just 1. Each row is identified by what Cassandra calls a key. The key
491 : * can either be a string or a binary identifier (i.e. an int64_t for
492 : * example, we use QByteArray for those in libQtCassandra.)
493 : *
494 : * The name of a row can be typed. In most cases, the default binary
495 : * type is enough (assuming you save integers in big endians, which
496 : * is what the libQtCassandra does.) This is important when you want
497 : * to use a Row Predicate.
498 : *
499 : * Rows are composed of cells. Cassandra calls them columns, but in
500 : * practice, the name/value pair is just a Cell. Although some tables
501 : * may define column types and those cells (with the same name) will
502 : * then be typed and part of that column.
503 : *
504 : * A column is a name and a value pair. It is possible to change the
505 : * characteristics of a column on how it gets duplicated, cached, and
506 : * how the column gets compared with a Column Predicate.
507 : *
508 : * The name of a column can be a QString or binary data. It is often
509 : * a QString as it looks like the name of a variable (var=\<value>).
510 : *
511 : * The row and column names are both limited to 64Kb. The value of
512 : * a column is currently limited to 2Gb, however, you'll need a
513 : * HUGE amount of memory (~6Gb+) to be able to handle such large
514 : * values and not only that, you need to do it serially (i.e. one
515 : * process at a time can send that much data or the memory will
516 : * quickly exhaust and the processes will fail.) It is strongly
517 : * advised that you limit your values to Mb instead.
518 : *
519 : * By default, the libdbproxy object checks the size with a much
520 : * small limit (64Mb) to prevent problems. At a later time, we may
521 : * offer a blob handling that will save large files by breaking
522 : * them up in small parts saved in the Cassandra database.
523 : *
524 : * \subsection faq FAQ
525 : *
526 : * In general, the libQtCassandra documentation will tell you how
527 : * things work in the function you're trying to use. This means
528 : * you should not really need to know all the details about the
529 : * Cassandra system up front to start using it in C++ with this
530 : * library.
531 : *
532 : * For example, the table::dropRow() function works.
533 : * The row doesn't disappear immediately from the database, but
534 : * all the cells are. The table::readRows() function
535 : * will correctly skip the row as long as you include at least
536 : * one column in your predicate. All of these details are found
537 : * in the corresponding function documentation.
538 : *
539 : * Actual Cassandra's FAQ: http://wiki.apache.org/cassandra/FAQ
540 : *
541 : * \section copyright libQtCassandra copyright and license
542 : *
543 : * Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved
544 : *
545 : * https://snapwebsites.org/<br/>
546 : * contact@m2osw.com
547 : *
548 : * Permission is hereby granted, free of charge, to any person obtaining a
549 : * copy of this software and associated documentation files (the
550 : * "Software"), to deal in the Software without restriction, including
551 : * without limitation the rights to use, copy, modify, merge, publish,
552 : * distribute, sublicense, and/or sell copies of the Software, and to
553 : * permit persons to whom the Software is furnished to do so, subject to
554 : * the following conditions:
555 : *
556 : * The above copyright notice and this permission notice shall be included
557 : * in all copies or substantial portions of the Software.
558 : *
559 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
560 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
561 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
562 : * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
563 : * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
564 : * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
565 : * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
566 : */
567 :
568 :
569 : /** \class libdbproxy
570 : * \brief The Cassandra class definition.
571 : *
572 : * This class is used to handle a Cassandra connection and read/write data
573 : * to and from a Cassandra database.
574 : *
575 : * This is the first object you want to create as all things come out of it.
576 : */
577 :
578 :
579 : /** \var const int LIBDBPROXY_LIBRARY_VERSION_MAJOR;
580 : * \brief The major library version.
581 : *
582 : * This value represents the major library version at the time you
583 : * compile your program. To get the library you are linked to at
584 : * runtime use libdbproxy::versionMajor().
585 : *
586 : * \sa versionMajor()
587 : */
588 :
589 :
590 : /** \var const int LIBDBPROXY_LIBRARY_VERSION_MINOR;
591 : * \brief The minor library version.
592 : *
593 : * This value represents the minor library version at the time you
594 : * compile your program. To get the library you are linked to at
595 : * runtime use libdbproxy::versionMinor().
596 : *
597 : * \sa versionMinor()
598 : */
599 :
600 :
601 : /** \var const int LIBDBPROXY_LIBRARY_VERSION_PATCH;
602 : * \brief The patch library version.
603 : *
604 : * This value represents the patch library version at the time you
605 : * compile your program. To get the library you are linked to at
606 : * runtime use libdbproxy::versionPatch().
607 : *
608 : * \sa versionPatch()
609 : */
610 :
611 :
612 : /** \var libdbproxy::f_current_context
613 : * \brief A pointer to the current context.
614 : *
615 : * The current context has to be set once. We save the pointer that way
616 : * we can avoid call the set_keyspace() function if the context you are
617 : * trying to make current already is current.
618 : *
619 : * The libQtCassandra library does not give you access to the
620 : * context::makeCurrent() function
621 : */
622 :
623 :
624 : /** \var libdbproxy::f_contexts_read
625 : * \brief Whether the map of contexts were read from Cassandra.
626 : *
627 : * This flag defines whether the f_contexts was already initialized or
628 : * not. This allows to call the describe_keyspaces() function a maximum
629 : * of one time per connection.
630 : */
631 :
632 :
633 : /** \var libdbproxy::f_contexts
634 : * \brief The map of contexts defined in memory.
635 : *
636 : * This variable holds the list of contexts defined in memory.
637 : * Whenever you try to access a context the system reads the cluster
638 : * description (once) and return the corresponding Cassandra database
639 : * context.
640 : *
641 : * It is possible to create a memory context (i.e. get a context that
642 : * doesn't exist in the Cassandra database and never call the
643 : * Context::create() function.)
644 : */
645 :
646 :
647 : /** \var libdbproxy::f_cluster_name
648 : * \brief The name of the cluster we're connected to.
649 : *
650 : * This variable holds the name of the cluster the libdbproxy object
651 : * is connected to. This variable caches the cluster name so we don't
652 : * have to query the cluster about its name more than once.
653 : */
654 :
655 :
656 : /** \var libdbproxy::f_protocol_version
657 : * \brief The version of the protocol we're connected to.
658 : *
659 : * This variable holds the version of the protocol the libdbproxy object
660 : * is connected to. This variable caches the protocol version so we don't
661 : * have to query the cluster about its protocol version more than once.
662 : */
663 :
664 :
665 : /** \var libdbproxy::f_partitioner
666 : * \brief The partitioner available in this Cassandra cluster.
667 : *
668 : * This variable caches the name of the partitioner as read from the
669 : * Cassandra cluster.
670 : */
671 :
672 :
673 : /** \var libdbproxy::f_snitch
674 : * \brief The snitch used by this Cassandra cluster.
675 : *
676 : * This variable caches the name of the snitch as read from the
677 : * Cassandra cluster.
678 : */
679 :
680 :
681 : /** \brief Initialize the libdbproxy object.
682 : *
683 : * This function makes the libdbproxy object ready.
684 : *
685 : * Mainly, it allocates a QCassandraPrivate object that handles all
686 : * the necessary data transfers between the libdbproxy object and the
687 : * Cassandra server.
688 : *
689 : * Next you are expected to connect to the server and eventually
690 : * change the default consistency level and on larger system.
691 : *
692 : * \sa connect()
693 : * \sa setDefaultConsistencyLevel()
694 : */
695 0 : libdbproxy::libdbproxy()
696 : {
697 0 : }
698 :
699 :
700 : /** \brief Create the libdbproxy instance.
701 : *
702 : * This factory creates a new object wrapped in a shared pointer. The contructor
703 : * is private, so this function must be used.
704 : *
705 : * \sa libdbproxy()
706 : */
707 0 : libdbproxy::pointer_t libdbproxy::create()
708 : {
709 0 : return pointer_t( new libdbproxy );
710 : }
711 :
712 :
713 : /** \brief Cleanup the Cassandra object.
714 : *
715 : * This function cleans up the libdbproxy object.
716 : *
717 : * This includes disconnecting from the Cassandra server and release
718 : * memory resources tight to this object.
719 : */
720 0 : libdbproxy::~libdbproxy()
721 : {
722 0 : disconnect();
723 0 : }
724 :
725 :
726 : /** \brief Connect to a snapdbproxy daemon.
727 : *
728 : * This function connects to a Cassandra Cluster through a snapdbproxy
729 : * daemon. In most cases the default host and port information are enough.
730 : * (localhost and 4042, respectively.)
731 : *
732 : * One cluster may include many database contexts (i.e. keyspaces.)
733 : * Each database context (keyspace) has a set of parameters defining
734 : * its duplication mechanism among other things. Before working with
735 : * a database context, one must call the the setCurrentContext()
736 : * function.
737 : *
738 : * The function first disconnects the existing connection when
739 : * there is one.
740 : *
741 : * Many other functions require you to call this connect() function
742 : * first. You are likely to get a runtime exception if you do not.
743 : *
744 : * Note that the previous connection is lost whether or not the new
745 : * one succeeds.
746 : *
747 : * \warning
748 : * Note that the handling of the Cassandra cluster is done in the
749 : * snapdbproxy. i.e. it will connect to any number of nodes and
750 : * retrieve the data using any one of those nodes and stay connected
751 : * with those nodes so things should be pretty fast (faster than
752 : * having to connect directly to a node each time--or worst, connected
753 : * to 5 or 6 nodes...)
754 : *
755 : * \warning
756 : * The proxy does not connect in its constructor, instead it waits until
757 : * the first use of the proxy. Luckily, there is such a use within the
758 : * connect() function to gather the cluster basic information. So on return
759 : * of the connect() you can safely call the isConnected() function and get
760 : * the expected response (true). However, the proxy works in a different
761 : * way than the old connection: if we lose the connection, the proxy tries
762 : * to reconnect, automatically.
763 : *
764 : * \exception exception
765 : * If the function cannot gather the cluster information, then it raises
766 : * this exception.
767 : *
768 : * \param[in] host The host, defaults to "localhost" (an IP address,
769 : * computer hostname, domain name, etc.)
770 : * \param[in] port The connection port, defaults to 4042.
771 : *
772 : * \return true if the connection succeeds, throws otherwise
773 : */
774 0 : bool libdbproxy::connect( QString const & host, int const port )
775 : {
776 : // disconnect any existing connection
777 : //
778 0 : disconnect();
779 :
780 : // connect to snapdbproxy
781 : //
782 0 : f_proxy.reset(new proxy(host, port));
783 :
784 : // get cluster information
785 : //
786 : class save_consistency_t
787 : {
788 : public:
789 0 : save_consistency_t(consistency_level_t & org)
790 0 : : f_org(org)
791 0 : , f_save(org)
792 : {
793 : // always force QUORUM for this SELECT
794 : //
795 0 : org = CONSISTENCY_LEVEL_QUORUM;
796 0 : }
797 :
798 0 : ~save_consistency_t()
799 0 : {
800 0 : f_org = f_save;
801 0 : }
802 :
803 : private:
804 : consistency_level_t & f_org;
805 : consistency_level_t f_save;
806 : };
807 :
808 : // use RAII object to save the default consistency level
809 : //
810 0 : save_consistency_t save_consistency(f_default_consistency_level);
811 :
812 0 : order local_table;
813 0 : local_table.setCql( "SELECT cluster_name,native_protocol_version,partitioner FROM system.local",
814 : order::type_of_result_t::TYPE_OF_RESULT_ROWS );
815 0 : local_table.setColumnCount(3);
816 0 : order_result const local_table_result(f_proxy->sendOrder(local_table));
817 :
818 : // even just cluster info cannot be retrieved, forget it
819 : //
820 0 : if( !local_table_result.succeeded() )
821 : {
822 0 : throw exception( "Error reading database table system.local!" );
823 : }
824 :
825 : // got success but no data?!
826 : //
827 0 : if( local_table_result.resultCount() != 3 )
828 : {
829 0 : throw exception( "Somehow system.local could not return the Cassandra cluster name, native protocol and partitioner information" );
830 : }
831 :
832 : // save data here
833 : //
834 0 : f_cluster_name = local_table_result.result( 0 );
835 0 : f_protocol_version = local_table_result.result( 1 );
836 0 : f_partitioner = local_table_result.result( 2 );
837 :
838 0 : return true;
839 : }
840 :
841 :
842 : /** \brief Break the connection to Cassandra.
843 : *
844 : * This function breaks the connection to Cassandra.
845 : *
846 : * This function has the side effect of clearing the cluster name,
847 : * protocol version, and current context.
848 : */
849 0 : void libdbproxy::disconnect()
850 : {
851 : // TBD: should we send a "CLOSE" to the proxy?
852 : // (the socket should receive the HUP signal anyway)
853 : //
854 0 : f_proxy.reset();
855 :
856 0 : f_current_context.reset();
857 0 : f_contexts.clear();
858 0 : f_cluster_name.clear();
859 0 : f_protocol_version.clear();
860 0 : f_partitioner.clear();
861 0 : f_default_consistency_level = CONSISTENCY_LEVEL_ONE;
862 0 : }
863 :
864 :
865 : /** \brief Check whether the object is connected to the server.
866 : *
867 : * This function returns true when this object is connected to the
868 : * backend Cassandra server.
869 : *
870 : * The function is fast and does not actually verify that the TCP/IP
871 : * connection is still up.
872 : *
873 : * \return true if connect() was called and succeeded.
874 : */
875 0 : bool libdbproxy::isConnected() const
876 : {
877 0 : if(!f_proxy)
878 : {
879 0 : return false;
880 : }
881 0 : return f_proxy->isConnected();
882 : }
883 :
884 :
885 : /** \brief Get the name of the Cassandra cluster.
886 : *
887 : * This function determines the name of the cluster. The name cannot
888 : * change unless you renew the connection to a different Cassandra
889 : * cluster.
890 : *
891 : * The libdbproxy object remembers the name. Calling this function
892 : * more than once is very fast.
893 : *
894 : * You must be connected for this function to work.
895 : *
896 : * \note
897 : * The f_cluster_name variable will be set even though the function
898 : * is a const. This is a cache so it makes sense. The variable is
899 : * NOT marked mutable because this is the only case where it happens.
900 : *
901 : * \return The name of the cluster.
902 : */
903 0 : QString const & libdbproxy::clusterName() const
904 : {
905 0 : return f_cluster_name;
906 : }
907 :
908 :
909 : /** \brief Get the version of the cluster protocol.
910 : *
911 : * This function determines the version of the protocol. The version
912 : * cannot change unless you renew the connection to a different
913 : * Cassandra cluster.
914 : *
915 : * The libdbproxy object remembers the version. Calling this function
916 : * more than once is very fast.
917 : *
918 : * You must be connected for this function to work.
919 : *
920 : * \note
921 : * The f_protocol_version variable will be set even though the function
922 : * is a const. This is a cache so it makes sense. The variable is
923 : * NOT marked mutable because this is the only case where it happens.
924 : *
925 : * \return The version of the protocol.
926 : */
927 0 : QString const & libdbproxy::protocolVersion() const
928 : {
929 0 : return f_protocol_version;
930 : }
931 :
932 :
933 : /** \brief Get the partitioner of the cluster.
934 : *
935 : * This function retrieves the name of the partitioner in use by the
936 : * cluster. Some paritioners do not support readRows() and thus knowing
937 : * what partitioner is in use can be useful to know which algorithm
938 : * your application should use.
939 : *
940 : * \note
941 : * Cassandra defines the RandomPartitioner by default. That partition
942 : * does not work well with readRows().
943 : *
944 : * \return The name of the partitioner.
945 : *
946 : * \sa readRows()
947 : */
948 0 : QString const & libdbproxy::partitioner() const
949 : {
950 0 : return f_partitioner;
951 : }
952 :
953 :
954 : /** \brief Retrieve a context by name.
955 : *
956 : * This function searches for a context by name. If the context does not
957 : * exist yet, then it gets created in memory.
958 : *
959 : * You should be connected for this function to work as expected although
960 : * it is possible to create a memory only context.
961 : *
962 : * The context is not created in the Cassandra database. If it doesn't
963 : * exist in Cassandra, it is only created in memory until you call its
964 : * create() function. This gives you an opportunity to setup the context
965 : * including its tables before creating it.
966 : *
967 : * The following shows you an example:
968 : *
969 : * \code
970 : * context = cassandra.context(context_name);
971 : * context->setStrategyClass("org.apache.cassandra.locator.SimpleStrategy");
972 : * context->setReplicationFactor(1);
973 : * context->create();
974 : * \endcode
975 : *
976 : * Note that if you do not know whether the context exists, use the
977 : * findContext() function first, then check whether the context was found.
978 : *
979 : * \param[in] context_name The name of the context to search.
980 : *
981 : * \return A shared pointer to a cassandra context.
982 : */
983 0 : context::pointer_t libdbproxy::getContext( QString const & context_name )
984 : {
985 : // get the list of existing contexts
986 0 : contexts const & cs(getContexts());
987 :
988 : // already exists?
989 0 : contexts::const_iterator ci(cs.find( context_name ));
990 0 : if ( ci != cs.end() )
991 : {
992 0 : return ci.value();
993 : }
994 :
995 : // otherwise create a new one
996 0 : context::pointer_t c( new context( shared_from_this(), context_name ) );
997 0 : f_contexts.insert( context_name, c );
998 0 : retrieveContextMeta( c, context_name );
999 :
1000 0 : return c;
1001 : }
1002 :
1003 :
1004 : /** \brief Create a context from a keyspace.
1005 : *
1006 : *
1007 : * \param[in] keyspace A pointer to the context keyspace.
1008 : *
1009 : * \return A shared pointer to a cassandra context.
1010 : */
1011 0 : context::pointer_t libdbproxy::getContext( casswrapper::schema::KeyspaceMeta::pointer_t keyspace_meta )
1012 : {
1013 : // get the list of existing contexts
1014 : //
1015 : // we have to make that call to make sure that the contexts actually
1016 : // get created
1017 : //
1018 0 : contexts const & cs(getContexts());
1019 :
1020 : // already exists?
1021 0 : contexts::const_iterator ci(cs.find( keyspace_meta->getName() ));
1022 0 : if ( ci != cs.end() )
1023 : {
1024 0 : return ci.value();
1025 : }
1026 :
1027 : // otherwise create a new one
1028 0 : context::pointer_t c( new context( shared_from_this(), keyspace_meta->getName() ) );
1029 0 : f_contexts.insert( keyspace_meta->getName(), c );
1030 : //retrieveContextMeta( context_name ); -- we have the keyspace meta data, just use it
1031 0 : c->parseContextDefinition( keyspace_meta );
1032 :
1033 0 : return c;
1034 : }
1035 :
1036 :
1037 : /** \brief Make the specified context the current context.
1038 : *
1039 : * This function assigns the specified context as the current context
1040 : * of the Cassandra server.
1041 : *
1042 : * The Cassandra servers work with keyspaces. One keyspace is similar
1043 : * to a database. This defines what we call a context. The
1044 : * setCurrentContext() function defines the active context.
1045 : *
1046 : * If the libdbproxy object is not yet connected, the context is saved
1047 : * as the current context of the libdbproxy object, but it gets lost
1048 : * once you connect.
1049 : *
1050 : * The only way to make a context not the current one is by setting
1051 : * another context as the current one.
1052 : *
1053 : * You must be connected for this function to really work.
1054 : *
1055 : * \note
1056 : * It is not required to set the current context manually. This is done
1057 : * automatically when you use a context.
1058 : *
1059 : * \note
1060 : * It is wiser to create multiple libdbproxy objects than to swap contexts all
1061 : * the time. Note that even if you do not explicitly call this function, using
1062 : * multiple contexts from the same libdbproxy object will be slower as the
1063 : * context has to be switched each time.
1064 : *
1065 : * \param[in] c The context to set as current.
1066 : *
1067 : * \sa currentContext()
1068 : * \sa context::makeCurrent()
1069 : */
1070 0 : void libdbproxy::setCurrentContext( context::pointer_t c )
1071 : {
1072 : // emit the change only if not the same context
1073 0 : if ( f_current_context != c )
1074 : {
1075 : // f_private->setContext( c->contextName() );
1076 : // we save the current context only AFTER the call to setContext()
1077 : // in case it throws (and then the current context would be wrong)
1078 0 : f_current_context = c;
1079 : }
1080 0 : }
1081 :
1082 :
1083 : /** \brief Internal function that clears the current context as required.
1084 : *
1085 : * Whenever a context is being dropped, it cannot remain the current context.
1086 : * This function is used to remove it from being current.
1087 : *
1088 : * \param[in] c The context that is about to be dropped.
1089 : */
1090 0 : void libdbproxy::clearCurrentContextIf( context const & c )
1091 : {
1092 0 : if ( f_current_context.get() == &c )
1093 : {
1094 0 : f_current_context.reset();
1095 : }
1096 0 : }
1097 :
1098 :
1099 : /** \brief Retrieve a context by name.
1100 : *
1101 : * This function creates a new context by name. It first searches the
1102 : * list of existing keyspaces, if it finds one with the specified name,
1103 : * then it creates a corresponding context.
1104 : *
1105 : * \param[in] context_name The name of the context to create in memory.
1106 : */
1107 0 : void libdbproxy::retrieveContextMeta( context::pointer_t c, QString const & context_name ) const
1108 : {
1109 0 : if(!f_proxy)
1110 : {
1111 0 : throw exception( "libdbproxy::retrieveContextMeta(): called when not connected" );
1112 : }
1113 :
1114 : // TODO: Calling the DESCRIBE CLUSTER each time is slow
1115 : //
1116 : // (TBD: although we really only do it once for
1117 : // the snap_websites context?)
1118 : //
1119 : // It is now only done once in the snapdbproxy at least, but
1120 : // it's still 250Kb of data to transfer to each snap_child!
1121 : // Instead we want to switch to using our <name>-table.xml files
1122 : // (way smaller!)
1123 : //
1124 0 : order describe_cluster;
1125 0 : describe_cluster.setCql( "DESCRIBE CLUSTER", order::type_of_result_t::TYPE_OF_RESULT_DESCRIBE );
1126 0 : order_result const describe_cluster_result(f_proxy->sendOrder(describe_cluster));
1127 :
1128 0 : if(!describe_cluster_result.succeeded())
1129 : {
1130 0 : throw exception( "libdbproxy::retrieveContextMeta(): DESCRIBE CLUSTER failed" );
1131 : }
1132 :
1133 0 : if(describe_cluster_result.resultCount() != 1)
1134 : {
1135 0 : throw exception( "libdbproxy::retrieveContextMeta(): result does not have one blob as expected" );
1136 : }
1137 :
1138 0 : casswrapper::schema::SessionMeta::pointer_t session_meta(new casswrapper::schema::SessionMeta);
1139 0 : session_meta->decodeSessionMeta(describe_cluster_result.result(0));
1140 0 : auto const & keyspaces(session_meta->getKeyspaces());
1141 0 : auto iter(keyspaces.find(context_name));
1142 0 : if( iter != keyspaces.end() )
1143 : {
1144 0 : c->parseContextDefinition( iter->second );
1145 : }
1146 0 : }
1147 :
1148 :
1149 : /** \brief Get the map of contexts.
1150 : *
1151 : * This function returns the map of contexts (keyspaces) help in this
1152 : * Cassandra cluster.
1153 : *
1154 : * The cluster may include any number of contexts also it is wise to
1155 : * limit yourself to a relatively small number of contexts since it
1156 : * is loaded each time you connect to the database.
1157 : *
1158 : * Note that the function returns a reference to the internal map of
1159 : * contexts. It is wise to stop using it if you call a function that
1160 : * may change the map (createContext(), dropContext(), etc.)
1161 : *
1162 : * You must be connected for this function to work.
1163 : *
1164 : * \warning
1165 : * You should nearly never use 'true' for the \p reset parameter. This
1166 : * will cause a terrible slowness. However, using the parameter is
1167 : * useful when you run a command that manages the context, table, or
1168 : * column definitions. For example, the snapcreatetables sets that
1169 : * flag to true to make sure that it has the latest version of the
1170 : * context.
1171 : *
1172 : * \param[in] reset Whether to reset the context description.
1173 : *
1174 : * \return A reference to the internal map of contexts.
1175 : */
1176 0 : contexts const & libdbproxy::getContexts(bool reset) const
1177 : {
1178 0 : if(!f_proxy)
1179 : {
1180 0 : throw exception( "libdbproxy::getContexts(): called when not connected" );
1181 : }
1182 :
1183 0 : if( !f_contexts_read )
1184 : {
1185 : //int64_t n(timeofday());
1186 0 : order describe_cluster;
1187 0 : describe_cluster.setCql(
1188 : reset ? "DESCRIBE CLUSTER ANEW"
1189 : : "DESCRIBE CLUSTER"
1190 : , order::type_of_result_t::TYPE_OF_RESULT_DESCRIBE );
1191 0 : order_result const describe_cluster_result(f_proxy->sendOrder(describe_cluster));
1192 :
1193 0 : if(!describe_cluster_result.succeeded())
1194 : {
1195 0 : throw exception( "libdbproxy::getContexts(): DESCRIBE CLUSTER failed" );
1196 : }
1197 :
1198 0 : if(describe_cluster_result.resultCount() != 1)
1199 : {
1200 0 : throw exception( "libdbproxy::getContexts(): result does not have one blob as expected" );
1201 : }
1202 :
1203 : // WARNING: the location where this flag is set to true is very
1204 : // important, we do not want to put it too soon in case
1205 : // we throw and never actually initialize any contexts
1206 : // and we do not want to have it after the following
1207 : // for() statement because otherwise we get a looping
1208 : // call to getContexts()
1209 : //
1210 0 : f_contexts_read = true;
1211 :
1212 0 : casswrapper::schema::SessionMeta::pointer_t session_meta(new casswrapper::schema::SessionMeta);
1213 0 : session_meta->decodeSessionMeta(describe_cluster_result.result(0));
1214 :
1215 0 : for( auto keyspace : session_meta->getKeyspaces() )
1216 : {
1217 0 : const_cast<libdbproxy *>(this)->getContext(keyspace.second);
1218 : }
1219 : //std::cerr << "[" << getpid() << "] DESCRIBE CLUSTER: completed in " << (timeofday() - n) << " us.\n";
1220 : }
1221 :
1222 0 : return f_contexts;
1223 : }
1224 :
1225 :
1226 : /** \brief Search for a context.
1227 : *
1228 : * This function searches for a context. If it exists, its shared pointer is
1229 : * returned. Otherwise, it returns a NULL pointer (i.e. the
1230 : * QSharedPointer::isNull() function returns true.)
1231 : *
1232 : * Note that if you do create an in memory context, then this function can be
1233 : * used to retrieve it too. In memory contexts are those that did not yet
1234 : * exist in the database and never had their create() function called.
1235 : *
1236 : * \todo
1237 : * Add a way to distinguish in memory only contexts and Cassandra contexts.
1238 : * This is important to know whether a context can be dropped.
1239 : *
1240 : * \param[in] context_name The name of the context to retrieve.
1241 : *
1242 : * \return A shared pointer to the context.
1243 : *
1244 : * \sa getContexts()
1245 : * \sa context::create()
1246 : */
1247 0 : context::pointer_t libdbproxy::findContext( QString const & context_name ) const
1248 : {
1249 0 : contexts::const_iterator ci( getContexts().find( context_name ) );
1250 0 : if ( ci == f_contexts.end() )
1251 : {
1252 0 : return context::pointer_t();
1253 : }
1254 0 : return *ci;
1255 : }
1256 :
1257 : /** \brief Retrieve a context reference.
1258 : *
1259 : * The array operator searches for a context by name and returns
1260 : * its reference. This is useful to access data with array like
1261 : * syntax as in:
1262 : *
1263 : * \code
1264 : * cluster[context_name][table_name][column_name] = value;
1265 : * \endcode
1266 : *
1267 : * \exception exception
1268 : * If the context doesn't exist, this function raises an exception
1269 : * since otherwise the reference would be a NULL pointer.
1270 : *
1271 : * \param[in] context_name The name of the context to retrieve.
1272 : *
1273 : * \return A reference to the named context.
1274 : */
1275 0 : context& libdbproxy::operator[]( QString const & context_name )
1276 : {
1277 0 : context::pointer_t context_obj( findContext( context_name ) );
1278 0 : if ( !context_obj )
1279 : {
1280 : throw exception(
1281 0 : "named context was not found, cannot return a reference" );
1282 : }
1283 :
1284 0 : return *context_obj;
1285 : }
1286 :
1287 : /** \brief Retrieve a constant context reference.
1288 : *
1289 : * This array operator is the same as the other one, just this one deals
1290 : * with constant contexts. It can be used to retrieve values from the
1291 : * Cassandra cluster you're connected to:
1292 : *
1293 : * \code
1294 : * value = cluster[context_name][table_name][column_name];
1295 : * \endcode
1296 : *
1297 : * \exception exception
1298 : * If the context doesn't exist, this function raises an exception
1299 : * since otherwise the reference would be a NULL pointer.
1300 : *
1301 : * \param[in] context_name The name of the context to retrieve.
1302 : *
1303 : * \return A constant reference to the named context.
1304 : */
1305 0 : context const & libdbproxy::
1306 : operator[]( QString const & context_name ) const
1307 : {
1308 : context::pointer_t const context_obj(
1309 0 : findContext( context_name ) );
1310 0 : if ( !context_obj )
1311 : {
1312 : throw exception(
1313 0 : "named context was not found, cannot return a reference" );
1314 : }
1315 :
1316 0 : return *context_obj;
1317 : }
1318 :
1319 : /** \brief Drop a context from the database and memory.
1320 : *
1321 : * Use this function when you want to completely drop a context
1322 : * from the Cassandra system and from memory. After this call
1323 : * the context, its tables, their rows, and cells are all marked
1324 : * as dead whether you still have shared pointers on them or not.
1325 : * (i.e. you cannot use them anymore.)
1326 : *
1327 : * \warning
1328 : * If the context does not exist in Cassandra, this function call
1329 : * raises an exception in newer versions of the Cassandra system
1330 : * (in version 0.8 it would just return silently.) You may want to
1331 : * call the findContext() function first to know whether the context
1332 : * exists before calling this function.
1333 : *
1334 : * \param[in] context_name The name of the context to drop.
1335 : *
1336 : * \sa context::drop()
1337 : */
1338 0 : void libdbproxy::dropContext( QString const & context_name )
1339 : {
1340 0 : context::pointer_t c( getContext( context_name ) );
1341 :
1342 : // first do the context drop in Cassandra
1343 0 : c->drop();
1344 :
1345 : // forget about this context in the libdbproxy object
1346 0 : f_contexts.remove( context_name );
1347 0 : }
1348 :
1349 : /** \brief Retrieve the current default consistency level.
1350 : *
1351 : * This function returns the current default consistency level used by
1352 : * most of the server functions. You may change the default from the
1353 : * default Cassandra value of ONE (which is good for reads.) In many cases,
1354 : * it is recommended that you use QUORUM, or at least LOCAL QUORUM.
1355 : *
1356 : * Different predicate and the value object have their own consistency
1357 : * levels. If those are set to DEFAULT, then this very value is used
1358 : * instead.
1359 : *
1360 : * \return The current default consistency level.
1361 : */
1362 0 : consistency_level_t libdbproxy::defaultConsistencyLevel() const
1363 : {
1364 0 : return f_default_consistency_level;
1365 : }
1366 :
1367 : /** \brief Change the current default consistency level.
1368 : *
1369 : * This function changes the current default consistency level used by
1370 : * most of the server functions. In many cases, it is recommended that
1371 : * you use QUORUM, but it very much depends on your application and
1372 : * node setup.
1373 : *
1374 : * Different predicate and the value object have their own consistency
1375 : * levels. If those are set to DEFAULT (their default,) then this very
1376 : * value is used instead.
1377 : *
1378 : * \note
1379 : * This function does not accept the CONSISTENCY_LEVEL_DEFAULT since
1380 : * that is not a valid Cassandra consistency level.
1381 : *
1382 : * \exception exception
1383 : * This exception is raised if the value passed to this function is not
1384 : * a valid consistency level.
1385 : *
1386 : * \param[in] default_consistency_level The new default consistency level.
1387 : */
1388 0 : void libdbproxy::setDefaultConsistencyLevel(
1389 : consistency_level_t default_consistency_level )
1390 : {
1391 : // make sure the consistency level exists
1392 0 : if ( default_consistency_level != CONSISTENCY_LEVEL_ONE &&
1393 0 : default_consistency_level != CONSISTENCY_LEVEL_QUORUM &&
1394 0 : default_consistency_level != CONSISTENCY_LEVEL_LOCAL_QUORUM &&
1395 0 : default_consistency_level != CONSISTENCY_LEVEL_EACH_QUORUM &&
1396 0 : default_consistency_level != CONSISTENCY_LEVEL_ALL &&
1397 0 : default_consistency_level != CONSISTENCY_LEVEL_ANY &&
1398 0 : default_consistency_level != CONSISTENCY_LEVEL_TWO &&
1399 : default_consistency_level != CONSISTENCY_LEVEL_THREE )
1400 : {
1401 0 : throw exception( "invalid default server consistency level" );
1402 : }
1403 :
1404 0 : f_default_consistency_level = default_consistency_level;
1405 0 : }
1406 :
1407 : /** \brief Retrieve the major version number.
1408 : *
1409 : * This function dynamically returns the library major version.
1410 : *
1411 : * \return The major version number.
1412 : */
1413 0 : int libdbproxy::versionMajor()
1414 : {
1415 0 : return LIBDBPROXY_LIBRARY_VERSION_MAJOR;
1416 : }
1417 :
1418 : /** \brief Retrieve the minor version number.
1419 : *
1420 : * This function dynamically returns the library minor version.
1421 : *
1422 : * \return The minor version number.
1423 : */
1424 0 : int libdbproxy::versionMinor()
1425 : {
1426 0 : return LIBDBPROXY_LIBRARY_VERSION_MINOR;
1427 : }
1428 :
1429 : /** \brief Retrieve the patch version number.
1430 : *
1431 : * This function dynamically returns the library patch version.
1432 : *
1433 : * \return The patch version number.
1434 : */
1435 0 : int libdbproxy::versionPatch()
1436 : {
1437 0 : return LIBDBPROXY_LIBRARY_VERSION_PATCH;
1438 : }
1439 :
1440 : /** \brief Retrieve the library version number in the form of a string.
1441 : *
1442 : * This function dynamically returns the library version as a string.
1443 : * This string includes all 3 parts of the version number separated
1444 : * by period (.) characters.
1445 : *
1446 : * \return The patch version number.
1447 : */
1448 0 : char const * libdbproxy::version()
1449 : {
1450 0 : return LIBDBPROXY_LIBRARY_VERSION_STRING;
1451 : }
1452 :
1453 : /** \brief Get the time of day.
1454 : *
1455 : * This function returns the time of day in micro seconds. It is a
1456 : * static function since there is no need for the this pointer in
1457 : * this helper function.
1458 : *
1459 : * \return The time of day in microseconds.
1460 : */
1461 0 : int64_t libdbproxy::timeofday()
1462 : {
1463 : struct timeval tv;
1464 :
1465 : // we ignore timezone as it can also generate an error
1466 0 : if(gettimeofday( &tv, NULL ) != 0)
1467 : {
1468 0 : throw exception("gettimeofday() failed.");
1469 : }
1470 :
1471 0 : return static_cast<int64_t>( tv.tv_sec ) * 1000000 +
1472 0 : static_cast<int64_t>( tv.tv_usec );
1473 : }
1474 :
1475 6 : } // namespace libdbproxy
1476 : // vim: ts=4 sw=4 et
|