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

          Line data    Source code
       1             : /*
       2             :  * Text:
       3             :  *      libsnapwebsites/src/libdbproxy/order.cpp
       4             :  *
       5             :  * Description:
       6             :  *      Manager an order to be sent to the snapdbproxy daemon.
       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             : #include "libdbproxy/order.h"
      38             : 
      39             : #include "libdbproxy/exception.h"
      40             : #include "libdbproxy/value.h"
      41             : 
      42             : #include "snapwebsites/log.h"
      43             : 
      44             : #include <QtCore>
      45             : 
      46             : #include <iostream>
      47             : #include <sstream>
      48             : 
      49             : namespace libdbproxy
      50             : {
      51             : 
      52             : 
      53             : 
      54           0 : order::type_of_result_t order::get_type_of_result() const
      55             : {
      56           0 :     return f_type_of_result;
      57             : }
      58             : 
      59             : 
      60             : 
      61             : /** \brief Get the CQL command.
      62             :  *
      63             :  * This function returns the UTF-8 encoded CQL order in a QString.
      64             :  *
      65             :  * \return The CQL order converted to a QString.
      66             :  */
      67           0 : QString order::cql() const
      68             : {
      69           0 :     return f_cql;
      70             : }
      71             : 
      72             : 
      73           0 : void order::setCql(QString const & cql_string, type_of_result_t const result_type)
      74             : {
      75           0 :     f_cql = cql_string;
      76           0 :     f_type_of_result = result_type;
      77           0 : }
      78             : 
      79             : 
      80             : /** \brief Check whether the order is considered valid.
      81             :  *
      82             :  * By default, an order is considered valid. It may be marked as invalid
      83             :  * to avoid sending it or on receipt to know that the order could not be
      84             :  * properly parsed back in the structure.
      85             :  *
      86             :  * \return true if the order is considered valid, false otherwise.
      87             :  */
      88           0 : bool order::validOrder() const
      89             : {
      90           0 :     return f_valid;
      91             : }
      92             : 
      93             : 
      94             : /** \brief Set whether the order is valid.
      95             :  *
      96             :  * Orders are considered valid by default. It is possible to change that
      97             :  * value to false to mark them as invalid.
      98             :  *
      99             :  * The decodeOrder() function makes use of this function mark the order
     100             :  * result as invalid up until the entire order was parsed from the
     101             :  * source.
     102             :  *
     103             :  * \return true if the order is considered valid, false otherwise.
     104             :  */
     105           0 : void order::setValidOrder(bool const valid)
     106             : {
     107           0 :     f_valid = valid;
     108           0 : }
     109             : 
     110             : 
     111             : /** \brief Retrieve the consistency level for this order.
     112             :  *
     113             :  * This function returns the consistency level to be used with this
     114             :  * CQL order.
     115             :  *
     116             :  * \return The order consistency level.
     117             :  */
     118           0 : consistency_level_t order::consistencyLevel() const
     119             : {
     120           0 :     return f_consistency_level;
     121             : }
     122             : 
     123             : 
     124             : /** \brief Change the consistency level for this order.
     125             :  *
     126             :  * This function sets the consistency level of this order.
     127             :  *
     128             :  * \param[in] consistency_level  The new consistency level.
     129             :  */
     130           0 : void order::setConsistencyLevel(consistency_level_t consistency_level)
     131             : {
     132           0 :     f_consistency_level = consistency_level;
     133           0 : }
     134             : 
     135             : 
     136           0 : int64_t order::timestamp() const
     137             : {
     138           0 :     return f_timestamp;
     139             : }
     140             : 
     141             : 
     142           0 : void order::setTimestamp(int64_t const user_timestamp)
     143             : {
     144           0 :     f_timestamp = user_timestamp;
     145           0 : }
     146             : 
     147             : 
     148           0 : int32_t order::timeout() const
     149             : {
     150           0 :     return f_timeout_ms;
     151             : }
     152             : 
     153             : 
     154           0 : void order::setTimeout(int32_t const statement_timeout_ms)
     155             : {
     156           0 :     f_timeout_ms = statement_timeout_ms;
     157           0 : }
     158             : 
     159             : 
     160           0 : int8_t order::columnCount() const
     161             : {
     162           0 :     return f_column_count;
     163             : }
     164             : 
     165             : 
     166           0 : void order::setColumnCount(int8_t const column_count)
     167             : {
     168           0 :     f_column_count = column_count;
     169           0 : }
     170             : 
     171             : 
     172           0 : int32_t order::pagingSize() const
     173             : {
     174           0 :     return f_paging_size;
     175             : }
     176             : 
     177             : 
     178           0 : void order::setPagingSize(int32_t const paging_size)
     179             : {
     180           0 :     f_paging_size = paging_size;
     181           0 : }
     182             : 
     183             : 
     184           0 : int32_t order::cursorIndex() const
     185             : {
     186           0 :     return f_cursor_index;
     187             : }
     188             : 
     189             : 
     190           0 : void order::setCursorIndex(int32_t const cursor_index)
     191             : {
     192           0 :     f_cursor_index = cursor_index;
     193           0 : }
     194             : 
     195             : 
     196           0 : int32_t order::batchIndex() const
     197             : {
     198           0 :     return f_batch_index;
     199             : }
     200             : 
     201             : 
     202           0 : void order::setBatchIndex(int32_t const batch_index)
     203             : {
     204           0 :     f_batch_index = batch_index;
     205           0 : }
     206             : 
     207             : 
     208           0 : bool order::clearClusterDescription() const
     209             : {
     210           0 :     return f_clear_cluster_description;
     211             : }
     212             : 
     213             : 
     214           0 : void order::setClearClusterDescription(bool const clear)
     215             : {
     216           0 :     f_clear_cluster_description = clear;
     217           0 : }
     218             : 
     219             : 
     220           0 : bool order::blocking() const
     221             : {
     222           0 :     return f_blocking;
     223             : }
     224             : 
     225             : 
     226           0 : void order::setBlocking(bool const block)
     227             : {
     228           0 :     f_blocking = block;
     229           0 : }
     230             : 
     231             : 
     232           0 : size_t order::parameterCount() const
     233             : {
     234           0 :     return f_parameter.size();
     235             : }
     236             : 
     237             : 
     238           0 : QByteArray const & order::parameter(int index) const
     239             : {
     240           0 :     if(static_cast<size_t>(index) >= f_parameter.size())
     241             :     {
     242           0 :         throw overflow_exception("order::parameter() called with an index too large.");
     243             :     }
     244           0 :     return f_parameter[index];
     245             : }
     246             : 
     247             : 
     248           0 : void order::addParameter(QByteArray const & data)
     249             : {
     250           0 :     f_parameter.push_back(data);
     251           0 : }
     252             : 
     253             : 
     254             : /** \brief Encode the order so it can be sent to snapdbproxy.
     255             :  *
     256             :  * The function transforms the order in a blob that we can send over
     257             :  * the wire.
     258             :  *
     259             :  * The format is as follow:
     260             :  *
     261             :  * \li flags -- one byte with the type of result expected; whether this is
     262             :  *              a blocking call; and whether timestamp is included
     263             :  *
     264             :  * \li consistency level -- one byte, since these are still very small numbers
     265             :  *
     266             :  * \li CQL order -- size on 2 bytes (uint16_t) and then the string itself
     267             :  *
     268             :  * \li timestamp -- if bit 5 of the flags is set, 8 bytes with a timestamp
     269             :  *                  (at this time we use a timestamp only when deleting
     270             :  *                  something because it does not always delete otherwise...)
     271             :  *
     272             :  * \li number of parameters -- count on 2 bytes (uint16_t); this size may be
     273             :  *                             zero (i.e. no additional parameters)
     274             :  *
     275             :  * \li parameters -- a sequence of size (uint32_t) followed by parameter data                  
     276             :  *                   repeated for each parameter; if the number of parameters
     277             :  *                   is zero, then none of this exists
     278             :  *
     279             :  * \return A blob one can send to the snapdbproxy daemon.
     280             :  */
     281           0 : QByteArray order::encodeOrder() const
     282             : {
     283             :     // Size of an order:
     284             :     //    4     tag (CQLP)
     285             :     //    4     size
     286             :     //    2     flags
     287             :     //    1     consistency level
     288             :     //    2     CQL length
     289             :     //  ...     length of CQL string
     290             :     //    8     timestamp
     291             :     //    4     timeout
     292             :     //    1     column count
     293             :     //    4     paging size
     294             :     //    2     cursor index
     295             :     //    2     number of parameters
     296             :     //    {
     297             :     //    4     parameter size
     298             :     //  ...     length of parameter
     299             :     //    }*
     300             :     //
     301             :     // Byte providing the expected size we get a single malloc()
     302             :     // instead of 1 malloc + X realloc()
     303             :     //
     304           0 :     uint32_t expected_size(4 + 4 + 2 + 1 + 2 + f_cql.length() + 8 + 4 + 1 + 4 + 2 + 2);
     305           0 :     for(auto param : f_parameter)
     306             :     {
     307           0 :         expected_size += 4 + param.size();
     308             :     }
     309           0 :     QCassandraEncoder encoder(expected_size);
     310             : 
     311             :     // Sending plain CQL (P for plain). We may later support CQLZ to send
     312             :     // a compressed QByteArray. (i.e. right now all snapdbproxies are
     313             :     // expected to be local so compression is not that useful, especially
     314             :     // for orders that are generally small except when uploading a file.)
     315             :     //
     316           0 :     encoder.appendSignedCharValue('C');
     317           0 :     encoder.appendSignedCharValue('Q');
     318           0 :     encoder.appendSignedCharValue('L');
     319           0 :     encoder.appendSignedCharValue('P');
     320             : 
     321           0 :     encoder.appendUInt32Value(expected_size - 8);
     322             : 
     323             :     // TBD: should we err if the f_valid flag is false?
     324             : 
     325             :     // flags
     326             :     //   f_type_of_result (bit 0 to 3)
     327             :     //   f_blocking (bit 4)
     328             :     //   f_timestamp included (bit 5)
     329             :     //   f_timeout_ms included (bit 6)
     330             :     //   f_column_count included (bit 7)
     331             :     //   f_paging_size included (bit 8)
     332             :     //   f_cursor_index included (bit 9)
     333             :     //   f_clear_cluster_description (bit 10)
     334             :     //
     335           0 :     uint16_t const flags(
     336           0 :                   (static_cast<uint16_t>(f_type_of_result) & 15)
     337           0 :                 | (f_blocking                  ? 0x0010 : 0)
     338           0 :                 | (f_timestamp != 0            ? 0x0020 : 0)
     339           0 :                 | (f_timeout_ms != 0           ? 0x0040 : 0)
     340           0 :                 | (f_column_count != 1         ? 0x0080 : 0)
     341           0 :                 | (f_paging_size != 0          ? 0x0100 : 0)
     342           0 :                 | (f_cursor_index != -1        ? 0x0200 : 0)
     343           0 :                 | (f_clear_cluster_description ? 0x0400 : 0)
     344           0 :                 | (f_batch_index != -1         ? 0x0800 : 0)
     345             :             );
     346           0 :     encoder.appendUInt16Value(flags);
     347             : 
     348             :     // consistency level (saved as one byte, signed)
     349             :     //
     350           0 :     signed char consistency_level(static_cast<signed char>(f_consistency_level));
     351           0 :     encoder.appendSignedCharValue(consistency_level);
     352             : 
     353             :     // CQL command as a PSTR (size is 2 bytes, max. 64Kb)
     354             :     //
     355           0 :     encoder.appendP16StringValue(f_cql);
     356             : 
     357             :     // the timestamp if not zero (save as 8 bytes, time in microseconds)
     358             :     //
     359           0 :     if(f_timestamp != 0)
     360             :     {
     361           0 :         encoder.appendInt64Value(f_timestamp);
     362             :     }
     363             : 
     364             :     // the timeout if not zero (save as 4 bytes, time in seconds)
     365             :     //
     366           0 :     if(f_timeout_ms != 0)
     367             :     {
     368           0 :         encoder.appendInt32Value(f_timeout_ms);
     369             :     }
     370             : 
     371             :     // the column count if not 1 (save as 1 bytes, 0 to 255 column in a select...)
     372             :     //
     373           0 :     if(f_column_count != 1)
     374             :     {
     375           0 :         encoder.appendSignedCharValue(f_column_count);
     376             :     }
     377             : 
     378             :     // the paging size if not zero (save as 4 bytes, 1 to 4 billion...)
     379             :     //
     380           0 :     if(f_paging_size != 0)
     381             :     {
     382           0 :         encoder.appendInt32Value(f_paging_size);
     383             :     }
     384             : 
     385             :     // the cursor index if not -1 (save as 2 bytes)
     386             :     //
     387           0 :     if(f_cursor_index != -1)
     388             :     {
     389           0 :         encoder.appendUInt16Value(f_cursor_index);
     390             :     }
     391             : 
     392             :     // the batch index if not -1 (save as 2 bytes)
     393             :     //
     394           0 :     if(f_batch_index != -1)
     395             :     {
     396           0 :         encoder.appendUInt16Value(f_batch_index);
     397             :     }
     398             : 
     399             :     // parameters, if any
     400             :     //
     401             :     // here we first save the number of parameters, possibly zero
     402             :     // (maximum of 64Kb); then we save the parameters as size
     403             :     // (up to 4Gb) and then the data
     404             :     //
     405           0 :     encoder.appendUInt16Value(f_parameter.size());
     406           0 :     for(auto param : f_parameter)
     407             :     {
     408           0 :         encoder.appendBinaryValue(param);
     409             :     }
     410             : 
     411             :     // adjust the size to match the buffer size exactly
     412             :     //
     413           0 :     encoder.replaceUInt32Value(encoder.size() - 8, 4);
     414             : 
     415           0 :     return encoder.result();
     416             : }
     417             : 
     418             : 
     419             : /** \brief Decode an order that was encoded with encodeOrder().
     420             :  *
     421             :  * snapdbproxy calls this function to get a order from
     422             :  * data received from a client.
     423             :  *
     424             :  * \exception runtime_error
     425             :  * If the buffer is of the wrong size, the reading of the data will
     426             :  * fail raising this exception. We may later add a try/catch within
     427             :  * this function to return false instead. Yet, if the order is wrong
     428             :  * we are going to have a hard time reading the next buffer. Plus,
     429             :  * if things work as expected, synchronizing the input should never
     430             :  * be required.
     431             :  *
     432             :  * \param[in] encoded_order  The order received from a client.
     433             :  * \param[in] size  The number of bytes in the specified buffer.
     434             :  *
     435             :  * \return true if the buffer looked proper and can be processed further.
     436             :  */
     437           0 : bool order::decodeOrder(unsigned char const * encoded_order, size_t size)
     438             : {
     439             :     // WARNING: Here I use the horrible fromRawData() function which does
     440             :     //          NOT copy the data to be checked here. However, that gives
     441             :     //          me full access to the value functions against
     442             :     //          QByteArray which is practical.
     443             :     //
     444             :     //          Just make sure to only use that 'encoded' buffer in this
     445             :     //          function. Do not pass it anywhere, or worst, return it!
     446             :     //
     447           0 :     QByteArray const encoded(QByteArray::fromRawData(reinterpret_cast<char const *>(encoded_order), size));
     448           0 :     QCassandraDecoder const decoder(encoded);
     449             : 
     450             :     try
     451             :     {
     452             :         // get the flags
     453             :         //
     454           0 :         int const flags(decoder.uint16Value());
     455             : 
     456           0 :         f_type_of_result = static_cast<type_of_result_t>(flags & 15);
     457           0 :         f_blocking = (flags & 0x10) != 0;
     458           0 :         f_clear_cluster_description = (flags & 0x400) != 0;
     459             : 
     460             :         // get the consistency level
     461             :         //
     462           0 :         f_consistency_level = static_cast<consistency_level_t>(decoder.signedCharValue());
     463             : 
     464             :         // get the CQL string (expected to be in UTF-8)
     465             :         //
     466           0 :         f_cql = decoder.p16StringValue();
     467             : 
     468             :         // if the timestamp was included, read it
     469             :         //
     470           0 :         if((flags & 0x20) != 0)
     471             :         {
     472           0 :             f_timestamp = decoder.int64Value();
     473             :         }
     474             :         else
     475             :         {
     476             :             // not included means we do not need it, i.e. zero
     477           0 :             f_timestamp = 0;
     478             :         }
     479             : 
     480             :         // if the timeout was included, read it
     481             :         //
     482           0 :         if((flags & 0x40) != 0)
     483             :         {
     484           0 :             f_timeout_ms = decoder.int32Value();
     485             :         }
     486             :         else
     487             :         {
     488             :             // not included means we do not need it, i.e. zero
     489           0 :             f_timeout_ms = 0;
     490             :         }
     491             : 
     492             :         // if the paging size was included, read it
     493             :         //
     494           0 :         if((flags & 0x80) != 0)
     495             :         {
     496           0 :             f_column_count = decoder.signedCharValue();
     497             :         }
     498             :         else
     499             :         {
     500             :             // not included means we do not need it, i.e. one
     501           0 :             f_column_count = 1;
     502             :         }
     503             : 
     504             :         // if the paging size was included, read it
     505             :         //
     506           0 :         if((flags & 0x100) != 0)
     507             :         {
     508           0 :             f_paging_size = decoder.int32Value();
     509             :         }
     510             :         else
     511             :         {
     512             :             // not included means we do not need it, i.e. zero
     513           0 :             f_paging_size = 0;
     514             :         }
     515             : 
     516             :         // if the cursor index was included, read it
     517             :         //
     518           0 :         if((flags & 0x200) != 0)
     519             :         {
     520           0 :             f_cursor_index = static_cast<int32_t>(decoder.uint16Value());
     521             :         }
     522             :         else
     523             :         {
     524             :             // not included means we do not need it, i.e. -1
     525           0 :             f_cursor_index = -1;
     526             :         }
     527             : 
     528             :         // if the batch index was included, read it
     529             :         //
     530           0 :         if((flags & 0x800) != 0)
     531             :         {
     532           0 :             f_batch_index = static_cast<int32_t>(decoder.uint16Value());
     533             :         }
     534             :         else
     535             :         {
     536             :             // not included means we do not need it, i.e. -1
     537           0 :             f_batch_index = -1;
     538             :         }
     539             : 
     540             :         // read the number of parameters that were included
     541             :         // this may be zero
     542             :         //
     543           0 :         size_t const param_count(decoder.uint16Value());
     544           0 :         for(size_t idx(0); idx < param_count; ++idx)
     545             :         {
     546             :             // read this parameter data and immediately push it in the
     547             :             // list of parameters; the binaryValue() function knows
     548             :             // to read the size first
     549             :             //
     550           0 :             f_parameter.push_back(decoder.binaryValue());
     551             :         }
     552             :     }
     553           0 :     catch( std::exception const & x )
     554             :     {
     555           0 :         SNAP_LOG_ERROR("decodeOrder(): exception! what=")(x.what());
     556           0 :         throw;
     557             :     }
     558           0 :     catch( ... )
     559             :     {
     560           0 :         SNAP_LOG_ERROR("unknown exception caught!");
     561           0 :         throw;
     562             :     }
     563             : 
     564           0 :     return true;
     565             : }
     566             : 
     567             : 
     568             : 
     569           6 : } // namespace libdbproxy
     570             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13