LCOV - code coverage report
Current view: top level - libdbproxy - libdbproxy.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 158 0.6 %
Date: 2019-12-15 17:13:15 Functions: 2 31 6.5 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13