Line data Source code
1 : /*
2 : * Text:
3 : * libsnapwebsites/src/libdbproxy/context.cpp
4 : *
5 : * Description:
6 : * Handling of Cassandra Keyspace which is a context.
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/context.h"
38 : #include "libdbproxy/libdbproxy.h"
39 :
40 : #include "snapwebsites/log.h"
41 :
42 : #include <casswrapper/schema.h>
43 :
44 : #include <stdexcept>
45 : #include <unistd.h>
46 :
47 : #include <sstream>
48 :
49 : #include <QtCore>
50 : //#include <QDebug>
51 :
52 :
53 : namespace libdbproxy
54 : {
55 :
56 : /** \class context
57 : * \brief Hold a Cassandra keyspace definition.
58 : *
59 : * This class defines objects that can hold all the necessary information
60 : * for a Cassandra keyspace definition.
61 : *
62 : * A keyspace is similar to a context in the sense that to work on a keyspace
63 : * you need to make it the current context. Whenever you use a context, this
64 : * class automatically makes it the current context. This works well in a non
65 : * threaded environment. In a threaded environment, you want to either make
66 : * sure that only one thread makes use of the Cassandra objects or that you
67 : * protect all the calls. This library does not.
68 : *
69 : * You may think of this context as one database of an SQL environment. If
70 : * you have used OpenGL, this is very similar to the OpenGL context.
71 : */
72 :
73 : /** \typedef context::QCassandraContextOptions
74 : * \brief A map of context options.
75 : *
76 : * This map defines options as name / value pairs.
77 : *
78 : * Only known otion names should be used or a protocol error may result.
79 : */
80 :
81 : /** \var context::f_schema
82 : * \brief The pointer to the casswrapper::schema meta object.
83 : *
84 : * This pointer is a shared pointer to the private definition of
85 : * the Cassandra context (i.e. a keyspace definition.)
86 : *
87 : * The pointer is created at the time the context is created.
88 : */
89 :
90 : /** \var context::f_cassandra
91 : * \brief A pointer back to the libdbproxy object.
92 : *
93 : * The bare pointer is used by the context to access the cassandra
94 : * private object and make the context the current context. It is
95 : * a bare pointer because the libdbproxy object cannot be deleted
96 : * without the context getting deleted first.
97 : *
98 : * Note that when deleting a context object, you may still
99 : * have a shared pointer referencing the object. This means the
100 : * object itself will not be deleted. In that case, the f_cassandra
101 : * parameter becomes NULL and calling functions that make use of
102 : * it throw an error.
103 : *
104 : * \note
105 : * If you look at the implementation, many functions call the
106 : * makeCurrent() which checks the f_cassandra pointer, thus these
107 : * functions don't actually test the pointer.
108 : */
109 :
110 : /** \var context::f_tables
111 : * \brief List of tables.
112 : *
113 : * A list of the tables defined in this context. The tables may be created
114 : * in memory only.
115 : *
116 : * The list is a map using the table binary key as the its own key.
117 : */
118 :
119 : /** \brief Initialize a context object.
120 : *
121 : * This function initializes a context object.
122 : *
123 : * Note that the constructor is private. To create a new context, you must
124 : * use the libdbproxy::context() function.
125 : *
126 : * All the parameters are set to the defaults as defined in the Cassandra
127 : * definition of the KsDef message. You can use the different functions to
128 : * change the default values.
129 : *
130 : * A context name must be composed of letters (A-Za-z), digits (0-9) and
131 : * underscore (_). It must start with a letter. The corresponding lexical
132 : * expression is: /[A-Za-z][A-Za-z0-9_]*\/
133 : *
134 : * \note
135 : * A context can be created, updated, and dropped. In all those cases, the
136 : * functions return once the Cassandra instance with which you are
137 : * connected is ready.
138 : *
139 : * \param[in] cassandra The libdbproxy object owning this context.
140 : * \param[in] context_name The name of the Cassandra context.
141 : *
142 : * \sa contextName()
143 : * \sa libdbproxy::context()
144 : */
145 0 : context::context(libdbproxy::pointer_t cassandra, const QString& context_name)
146 : //: f_schema() -- auto-init
147 : : f_cassandra(cassandra)
148 0 : , f_context_name(context_name)
149 : //f_tables() -- auto-init
150 : {
151 : // verify the name here (faster than waiting for the server and good documentation)
152 0 : QRegExp re("[A-Za-z][A-Za-z0-9_]*");
153 0 : if(!re.exactMatch(context_name))
154 : {
155 0 : throw exception("invalid context name (does not match [A-Za-z][A-Za-z0-9_]*)");
156 : }
157 :
158 0 : resetSchema();
159 0 : }
160 :
161 : /** \brief Clean up the context object.
162 : *
163 : * This function ensures that all resources allocated by the
164 : * context are released.
165 : *
166 : * Note that does not in any way destroy the context in the
167 : * Cassandra cluster.
168 : */
169 0 : context::~context()
170 : {
171 0 : }
172 :
173 :
174 0 : void context::resetSchema()
175 : {
176 0 : f_schema = std::make_shared<casswrapper::schema::KeyspaceMeta>( f_context_name );
177 :
178 0 : casswrapper::schema::Value replication;
179 0 : auto& replication_map(replication.map());
180 0 : replication_map["class"] = QVariant("SimpleStrategy");
181 0 : replication_map["replication_factor"] = QVariant(1);
182 :
183 0 : auto& field_map(f_schema->getFields());
184 0 : field_map["replication"] = replication;
185 0 : field_map["durable_writes"] = QVariant(true);
186 0 : }
187 :
188 :
189 : /** \brief Retrieve the name of this context.
190 : *
191 : * This function returns the name of this context.
192 : *
193 : * Note that the name cannot be modified. It is set by the constructor as
194 : * you create a context.
195 : *
196 : * \return A QString with the context name.
197 : */
198 0 : const QString& context::contextName() const
199 : {
200 0 : return f_context_name;
201 : }
202 :
203 :
204 0 : const casswrapper::schema::Value::map_t& context::fields() const
205 : {
206 0 : return f_schema->getFields();
207 : }
208 :
209 :
210 0 : casswrapper::schema::Value::map_t& context::fields()
211 : {
212 0 : return f_schema->getFields();
213 : }
214 :
215 :
216 : /** \brief Retrieve a table definition by name.
217 : *
218 : * This function is used to retrieve a table definition by name.
219 : * If the table doesn't exist, it gets created.
220 : *
221 : * Note that if the context is just a memory context (i.e. it does not yet
222 : * exist in the Cassandra cluster,) then the table is just created in memory.
223 : * This is useful to create a new context with all of its tables all at
224 : * once. The process is to call the libdbproxy::context() function to get
225 : * an in memory context, and then call this table() function for each one of
226 : * the table you want to create. Finally, to call the create() function to
227 : * actually create the context and its table in the Cassandra cluster.
228 : *
229 : * You can test whether the result is null with the isNull() function
230 : * of the std::shared_ptr<> class.
231 : *
232 : * \note
233 : * In the old days, I used this function to get a table and prepare it to
234 : * add it to the Cassandra cluster. Now that doesn't work right because
235 : * the table description is missing in the situation where the table doesn't
236 : * exist yet in the database. You must call the createTable() instead for
237 : * now.
238 : *
239 : * \param[in] table_name The name of the table to retrieve.
240 : *
241 : * \return A shared pointer to the table definition found or a null shared pointer.
242 : */
243 0 : table::pointer_t context::getTable(const QString& table_name)
244 : {
245 0 : table::pointer_t t( findTable( table_name ) );
246 0 : if( t != nullptr )
247 : {
248 0 : return t;
249 : }
250 :
251 : // this is a new table, allocate it
252 0 : t.reset( new table(shared_from_this(), table_name) );
253 0 : f_tables.insert( table_name, t );
254 0 : return t;
255 : }
256 :
257 :
258 : /** \brief Create a new table in Cassandra.
259 : *
260 : * In the old days, we could use the getTable() for both functions, but
261 : * the newer version requires a call to the parseTableDefinition() function
262 : * to make things work properly and get the table in Cassandra. Making
263 : * that call in the getTable() causes problems so instead I created this
264 : * function. I don't think this is 100% correct, but for now it works.
265 : * So this function as a whole is a TBD.
266 : *
267 : * Note that if the table already exists, then this function doesn't
268 : * hurt. It won't try to re-create the table.
269 : *
270 : * To make the table permanent in the Cassandra cluster, you must still
271 : * call the table->create() function after you setup the various parameters
272 : * (i.e. compression, compaction, GC, etc.)
273 : *
274 : * \param[in] table_name The name of the table to create.
275 : *
276 : * \return A pointer to the new Cassandra table.
277 : */
278 0 : table::pointer_t context::createTable(const QString& table_name)
279 : {
280 0 : table::pointer_t t( findTable( table_name ) );
281 0 : if( t != nullptr )
282 : {
283 0 : return t;
284 : }
285 :
286 : // this is a new table, allocate it
287 0 : t.reset( new table(shared_from_this(), table_name) );
288 0 : f_tables.insert( table_name, t );
289 0 : t->parseTableDefinition(f_schema->createTable(table_name));
290 0 : return t;
291 : }
292 :
293 :
294 : /** \brief Retrieve a reference to the tables.
295 : *
296 : * This function retrieves a constant reference to the map of table definitions.
297 : * The list is read-only, however, it is strongly suggested that you make a copy
298 : * if your code is going to modifiy tables later (i.e. calling table() may
299 : * affect the result of this call if you did not first copy the map.)
300 : *
301 : * \return A reference to the table definitions of this context.
302 : */
303 0 : const tables& context::getTables()
304 : {
305 : #if 0
306 : if( f_tables.empty() )
307 : {
308 : loadTables();
309 : }
310 : #endif
311 :
312 0 : return f_tables;
313 : }
314 :
315 : /** \brief Search for a table.
316 : *
317 : * This function searches for a table. If it exists, its shared pointer is
318 : * returned. Otherwise, it returns a NULL pointer (i.e. the
319 : * std::shared_ptr<>::operator bool() function returns true.)
320 : *
321 : * \note
322 : * Since the system reads the list of existing tables when it starts, this
323 : * function returns tables that exist in the database and in memory only.
324 : *
325 : * \param[in] table_name The name of the table to retrieve.
326 : *
327 : * \return A shared pointer to the table.
328 : */
329 0 : table::pointer_t context::findTable(const QString& table_name)
330 : {
331 : #if 0
332 : if( f_tables.empty() )
333 : {
334 : loadTables();
335 : }
336 : #endif
337 :
338 0 : tables::const_iterator it(f_tables.find(table_name));
339 0 : if(it == f_tables.end())
340 : {
341 0 : return table::pointer_t();
342 : }
343 0 : return *it;
344 : }
345 :
346 : /** \brief Retrieve a table reference from a context.
347 : *
348 : * The array operator searches for a table by name and returns
349 : * its reference. This is useful to access data with array like
350 : * syntax as in:
351 : *
352 : * \code
353 : * context[table_name][column_name] = value;
354 : * \endcode
355 : *
356 : * \exception exception
357 : * If the table doesn't exist, this function raises an exception
358 : * since otherwise the reference would be a NULL pointer.
359 : *
360 : * \param[in] table_name The name of the table to retrieve.
361 : *
362 : * \return A reference to the named table.
363 : */
364 0 : table& context::operator [] (const QString& table_name)
365 : {
366 0 : table::pointer_t ptable( findTable(table_name) );
367 0 : if( !ptable ) {
368 0 : throw exception("named table was not found, cannot return a reference");
369 : }
370 :
371 0 : return *ptable;
372 : }
373 :
374 :
375 : #if 0
376 : /** \brief Set the replication factor.
377 : *
378 : * This function sets the replication factor of the context.
379 : *
380 : * \deprecated
381 : * Since version 1.1 of Cassandra, the context replication
382 : * factor is viewed as a full option. This function automatically
383 : * sets the factor using the setDescriptionOption() function.
384 : * This means calling the setDescriptionOptions()
385 : * and overwriting all the options has the side effect of
386 : * cancelling this call. Note that may not work right with
387 : * older version of Cassandra. Let me know if that's the case.
388 : *
389 : * \param[in] factor The new replication factor.
390 : */
391 : void context::setReplicationFactor(int32_t factor)
392 : {
393 : // since version 1.1 of Cassandra, the replication factor
394 : // defined in the structure is ignored
395 : QString value(QString("%1").arg(factor));
396 : setDescriptionOption("replication_factor", value);
397 : }
398 :
399 : /** \brief Unset the replication factor.
400 : *
401 : * This function unsets the replication factor in case it was set.
402 : * In general it is not necessary to call this function unless you
403 : * are initializing a new context and you want to make sure that
404 : * the default replication factor is used.
405 : */
406 : void context::unsetReplicationFactor()
407 : {
408 : eraseDescriptionOption("replication_factor");
409 : }
410 :
411 : /** \brief Check whether the replication factor is defined.
412 : *
413 : * This function retrieves the current status of the replication factor parameter.
414 : *
415 : * \return True if the replication factor parameter is defined.
416 : */
417 : bool context::hasReplicationFactor() const
418 : {
419 : return f_private->__isset.replication_factor;
420 : }
421 :
422 : /** \brief Retrieve the current replication factor.
423 : *
424 : * This function reads and return the current replication factor of
425 : * the context.
426 : *
427 : * If the replication factor is not defined, zero is returned.
428 : *
429 : * \return The current replication factor.
430 : */
431 : int32_t context::replicationFactor() const
432 : {
433 : if(f_private->__isset.replication_factor) {
434 : return f_private->replication_factor;
435 : }
436 : return 0;
437 : }
438 :
439 : /** \brief Set whether the writes are durable.
440 : *
441 : * Temporary and permanent contexts can be created. This option defines
442 : * whether it is one of the other. Set to true to create a permanent
443 : * context (this is the default.)
444 : *
445 : * \param[in] durable_writes Set whether writes are durable.
446 : */
447 : void context::setDurableWrites(bool durable_writes)
448 : {
449 : f_private->__set_durable_writes(durable_writes);
450 : }
451 :
452 : /** \brief Unset the durable writes flag.
453 : *
454 : * This function marks the durable write flag as not set. This does
455 : * not otherwise change the flag. It will just not be sent over the
456 : * network and the default will be used when required.
457 : */
458 : void context::unsetDurableWrites()
459 : {
460 : f_private->__isset.durable_writes = false;
461 : }
462 :
463 : /** \brief Check whether the durable writes is defined.
464 : *
465 : * This function retrieves the current status of the durable writes parameter.
466 : *
467 : * \return True if the durable writes parameter is defined.
468 : */
469 : bool context::hasDurableWrites() const
470 : {
471 : return f_private->__isset.durable_writes;
472 : }
473 :
474 : /** \brief Retrieve the durable write flag.
475 : *
476 : * This function returns the durable flag that determines whether a
477 : * context is temporary (false) or permanent (true).
478 : *
479 : * \return The current durable writes flag status.
480 : */
481 : bool context::durableWrites() const
482 : {
483 : if(f_private->__isset.durable_writes) {
484 : return f_private->durable_writes;
485 : }
486 : return false;
487 : }
488 :
489 :
490 : /** \brief Prepare the context.
491 : *
492 : * This function prepares the context so it can be copied in a
493 : * keyspace definition later used to create a keyspace or to
494 : * update an existing keyspace.
495 : *
496 : * \todo
497 : * Verify that the strategy options are properly defined for the strategy
498 : * class (i.e. the "replication_factor" parameter is only for SimpleStrategy
499 : * and a list of at least one data center for other strategies.) However,
500 : * I'm not 100% sure that this is good idea since a user may add strategies
501 : * that we do not know anything about!
502 : *
503 : * \param[out] data The output keyspace definition.
504 : *
505 : * \sa parseContextDefinition()
506 : */
507 : void context::prepareContextDefinition(KsDef *ks) const
508 : {
509 : *ks = *f_private;
510 :
511 : if(ks->strategy_class == "") {
512 : ks->strategy_class = "LocalStrategy";
513 : }
514 :
515 : // copy the options
516 : ks->strategy_options.clear();
517 : for(QCassandraContextOptions::const_iterator
518 : o = f_options.begin(); o != f_options.end(); ++o)
519 : {
520 : ks->strategy_options.insert(
521 : std::pair<std::string, std::string>(o.key().toUtf8().data(),
522 : o.value().toUtf8().data()));
523 : }
524 : ks->__isset.strategy_options = !ks->strategy_options.empty();
525 :
526 : // copy the tables -- apparently we cannot do that here!
527 : // instead we have to loop through the table in the previous
528 : // level and update each column family separately
529 : ks->cf_defs.clear();
530 : for( auto t : f_tables )
531 : {
532 : CfDef cf;
533 : t->prepareTableDefinition(&cf);
534 : ks->cf_defs.push_back(cf);
535 : }
536 : //if(ks->cf_defs.empty()) ... problem? it's not optional...
537 : }
538 :
539 :
540 : /** \brief Generate the replication stanza for the CQL keyspace schema.
541 : */
542 : QString context::generateReplicationStanza() const
543 : {
544 : QString replication_stanza;
545 : if( f_private->strategy_class == "SimpleStrategy" )
546 : {
547 : replication_stanza = QString("'class': 'SimpleStrategy', 'replication_factor' : %1")
548 : .arg(f_options["replication_factor"]);
549 : }
550 : else if( f_private->strategy_class == "NetworkTopologyStrategy" )
551 : {
552 : QString datacenters;
553 : for( QString key : f_options.keys() )
554 : {
555 : if( !datacenters.isEmpty() )
556 : {
557 : datacenters += ", ";
558 : }
559 : datacenters += QString("'%1': %2").arg(key).arg(f_options[key]);
560 : }
561 : //
562 : replication_stanza = QString("'class': 'NetworkTopologyStrategy', %1")
563 : .arg(datacenters);
564 : }
565 : else
566 : {
567 : std::stringstream ss;
568 : ss << "This strategy class, '" << f_private->strategy_class << "', is not currently supported!";
569 : throw exception( ss.str().c_str() );
570 : }
571 :
572 : return replication_stanza;
573 : }
574 : #endif
575 :
576 :
577 : /** \brief This is an internal function used to parse a KsDef structure.
578 : *
579 : * This function is called internally to parse a KsDef object.
580 : *
581 : * \param[in] data The pointer to the KsDef object.
582 : *
583 : * \sa prepareContextDefinition()
584 : */
585 0 : void context::parseContextDefinition( casswrapper::schema::KeyspaceMeta::pointer_t keyspace_meta )
586 : {
587 0 : f_schema = keyspace_meta;
588 0 : for( const auto pair : f_schema->getTables() )
589 : {
590 0 : table::pointer_t t(getTable(pair.first));
591 0 : t->parseTableDefinition(pair.second);
592 : }
593 0 : }
594 :
595 :
596 : /** \brief Make this context the current context.
597 : *
598 : * This function marks this context as the current context where further
599 : * function calls will be made (i.e. table and cell editing.)
600 : *
601 : * Note that whenever you call a function that requires this context to
602 : * be current, this function is called automatically. If the context is
603 : * already the current context, then no message is sent to the Cassandra
604 : * server.
605 : *
606 : * \sa libdbproxy::setContext()
607 : */
608 0 : void context::makeCurrent()
609 : {
610 0 : parentCassandra()->setCurrentContext( shared_from_this() );
611 0 : }
612 :
613 :
614 0 : QString context::getKeyspaceOptions()
615 : {
616 0 : QString q_str;
617 0 : for( const auto& pair : f_schema->getFields() )
618 : {
619 0 : if( q_str.isEmpty() )
620 : {
621 0 : q_str = "WITH ";
622 : }
623 : else
624 : {
625 0 : q_str += "AND ";
626 : }
627 0 : q_str += QString("%1 = %2\n")
628 0 : .arg(pair.first)
629 0 : .arg(pair.second.output())
630 : ;
631 : }
632 :
633 0 : return q_str;
634 : }
635 :
636 :
637 : /** \brief Create a new context.
638 : *
639 : * This function is used to create a new context (keyspace) in the current
640 : * Cassandra cluster. Once created, you can make use of it whether it is
641 : * attached to the Cassandra cluster or not.
642 : *
643 : * If you want to include tables in your new context, then create them before
644 : * calling this function. It will be faster since you'll end up with one
645 : * single request.
646 : *
647 : * There is an example on how to create a new context with this library:
648 : *
649 : * \code
650 : * libdbproxy::context context("qt_cassandra_test_context");
651 : * // default strategy is LocalStrategy which you usually do not want
652 : * context.setStrategyClass("org.apache.cassandra.locator.SimpleStrategy");
653 : * context.setDurableWrites(true); // by default this is 'true'
654 : * context.setReplicationFactor(1); // by default this is undefined
655 : * ... // add tables before calling create() if you also want tables
656 : * context.create();
657 : * \endcode
658 : *
659 : * With newer versions of Cassandra (since 1.1) and a network or local
660 : * strategy you have to define the replication factors using your data
661 : * center names (the "replication_factor" parameter is ignored in that
662 : * case):
663 : *
664 : * \code
665 : * context.setDescriptionOption("strategy_class", "org.apache.cassandra.locator.NetworkTopologyStrategy");
666 : * context.setDescriptionOption("data_center1", "3");
667 : * context.setDescriptionOption("data_center2", "3");
668 : * context.setDurableWrites(true);
669 : * context.create();
670 : * \endcode
671 : *
672 : * Note that the replication factor is not set by default, yet it is a required
673 : * parameter.
674 : *
675 : * Also, the replication factor can be set to 1, although if you have more
676 : * than one node it is probably a poor choice. You probably want a minimum
677 : * of 3 for the replication factor, and you probably want a minimum of
678 : * 3 nodes in any live cluster.
679 : *
680 : * \sa table::create()
681 : */
682 0 : void context::create()
683 : {
684 0 : QString q_str( QString("CREATE KEYSPACE IF NOT EXISTS %1").arg(f_context_name) );
685 0 : q_str += getKeyspaceOptions();
686 :
687 0 : order create_keyspace;
688 0 : create_keyspace.setCql( q_str, order::type_of_result_t::TYPE_OF_RESULT_SUCCESS );
689 0 : create_keyspace.setClearClusterDescription(true);
690 0 : order_result const create_keyspace_result(parentCassandra()->getProxy()->sendOrder(create_keyspace));
691 0 : if(!create_keyspace_result.succeeded())
692 : {
693 0 : throw exception("keyspace creation failed");
694 : }
695 :
696 0 : for( auto t : f_tables )
697 : {
698 0 : t->create();
699 : }
700 0 : }
701 :
702 : /** \brief Update a context with new properties.
703 : *
704 : * This function defines a new set of properties in the specified context.
705 : * In general, the context will be searched in the cluster definitions,
706 : * updated in memory then this function called.
707 : */
708 0 : void context::update()
709 : {
710 0 : QString q_str( QString("ALTER KEYSPACE %1").arg(f_context_name) );
711 0 : q_str += getKeyspaceOptions();
712 :
713 0 : order alter_keyspace;
714 0 : alter_keyspace.setCql( q_str, order::type_of_result_t::TYPE_OF_RESULT_SUCCESS );
715 0 : alter_keyspace.setClearClusterDescription(true);
716 0 : order_result const alter_keyspace_result(parentCassandra()->getProxy()->sendOrder(alter_keyspace));
717 0 : if(!alter_keyspace_result.succeeded())
718 : {
719 0 : throw exception("keyspace creation failed");
720 : }
721 0 : }
722 :
723 :
724 : /** \brief Drop this context.
725 : *
726 : * This function drops this context in the Cassandra database.
727 : *
728 : * Note that contexts are dropped by name so we really only use the name of
729 : * the context in this case.
730 : *
731 : * The context object is still valid afterward, although, obviously
732 : * no data can be read from or written to the Cassandra server since the
733 : * context is gone from the cluster.
734 : *
735 : * You may change the parameters of the context and call create() to create
736 : * a new context with the same name.
737 : *
738 : * \warning
739 : * If the context does not exist in Cassandra, this function call
740 : * raises an exception in newer versions of the Cassandra system
741 : * (in version 0.8 it would just return silently.) You may want to
742 : * call the libdbproxy::findContext() function first to know whether
743 : * the context exists before calling this function.
744 : *
745 : * \sa libdbproxy::dropContext()
746 : * \sa libdbproxy::findContext()
747 : */
748 0 : void context::drop()
749 : {
750 0 : QString q_str(QString("DROP KEYSPACE IF EXISTS %1").arg(f_context_name));
751 :
752 0 : order drop_keyspace;
753 0 : drop_keyspace.setCql( q_str, order::type_of_result_t::TYPE_OF_RESULT_SUCCESS );
754 0 : drop_keyspace.setClearClusterDescription(true);
755 0 : order_result const drop_keyspace_result(parentCassandra()->getProxy()->sendOrder(drop_keyspace));
756 0 : if(!drop_keyspace_result.succeeded())
757 : {
758 0 : throw exception("drop keyspace failed");
759 : }
760 :
761 0 : resetSchema();
762 0 : f_tables.clear();
763 0 : }
764 :
765 :
766 : /** \brief Drop the specified table from the Cassandra database.
767 : *
768 : * This function sends a message to the Cassandra server so the named table
769 : * gets droped from it.
770 : *
771 : * The function also deletes the table from memory (which means all its
772 : * rows and cells are also deleted.) Do not use the table after this call,
773 : * even if you kept a shared pointer to it. You may create a new one
774 : * with the same name though.
775 : *
776 : * Note that tables get dropped immediately from the Cassandra database
777 : * (contrary to rows).
778 : *
779 : * \param[in] table_name The name of the table to drop.
780 : */
781 0 : void context::dropTable(const QString& table_name)
782 : {
783 0 : if(f_tables.find(table_name) == f_tables.end())
784 : {
785 0 : return;
786 : }
787 :
788 : // keep a shared pointer on the table
789 0 : table::pointer_t t(getTable(table_name));
790 :
791 : // remove from the Cassandra database
792 0 : makeCurrent();
793 :
794 0 : QString q_str(QString("DROP TABLE IF EXISTS %1.%2").arg(f_context_name).arg(table_name));
795 :
796 0 : order drop_table;
797 0 : drop_table.setCql( q_str, order::type_of_result_t::TYPE_OF_RESULT_SUCCESS );
798 0 : drop_table.setTimeout(5 * 60 * 1000);
799 0 : drop_table.setClearClusterDescription(true);
800 0 : order_result const drop_table_result(parentCassandra()->getProxy()->sendOrder(drop_table));
801 0 : if(!drop_table_result.succeeded())
802 : {
803 0 : throw exception("drop table failed");
804 : }
805 :
806 : // disconnect all the cached data from this table
807 0 : f_tables.remove(table_name);
808 : }
809 :
810 :
811 : /** \brief Clear the context cache.
812 : *
813 : * This function clears the context cache. This means all the tables, their
814 : * rows, and the cells of those rows all get cleared. None of these can be
815 : * used after this call even if you kept a shared pointer to any of these
816 : * objects.
817 : */
818 0 : void context::clearCache()
819 : {
820 0 : f_tables.clear();
821 0 : parentCassandra()->retrieveContextMeta( shared_from_this(), f_context_name );
822 0 : }
823 :
824 :
825 : /** \brief Get the pointer to the parent object.
826 : *
827 : * \return Shared pointer to the cassandra object.
828 : */
829 0 : libdbproxy::pointer_t context::parentCassandra() const
830 : {
831 0 : libdbproxy::pointer_t cassandra(f_cassandra.lock());
832 0 : if(cassandra == nullptr)
833 : {
834 0 : throw exception("this context was dropped and is not attached to a cassandra cluster anymore");
835 : }
836 :
837 0 : return cassandra;
838 : }
839 :
840 :
841 6 : } // namespace libdbproxy
842 : // vim: ts=4 sw=4 et
|