1 /**
2  * @file   context.h
3  *
4  * @author Ravi Gaddipati
5  *
6  * @section LICENSE
7  *
8  * The MIT License
9  *
10  * @copyright Copyright (c) 2017-2021 TileDB, Inc.
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy
13  * of this software and associated documentation files (the "Software"), to deal
14  * in the Software without restriction, including without limitation the rights
15  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16  * copies of the Software, and to permit persons to whom the Software is
17  * furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in
20  * all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28  * THE SOFTWARE.
29  *
30  * @section DESCRIPTION
31  *
32  * This file declares the C++ API for the TileDB Context object.
33  */
34 
35 #ifndef TILEDB_CPP_API_CONTEXT_H
36 #define TILEDB_CPP_API_CONTEXT_H
37 
38 #include "config.h"
39 #include "exception.h"
40 #include "tiledb.h"
41 
42 #include <functional>
43 #include <iostream>
44 #include <iterator>
45 #include <memory>
46 #include <string>
47 #include <vector>
48 
49 namespace tiledb {
50 
51 /**
52  * A TileDB context wraps a TileDB storage manager "instance."
53  * Most objects and functions will require a Context.
54  *
55  * Internal error handling is also defined by the Context; the default error
56  * handler throws a TileDBError with a specific message.
57  *
58  * **Example:**
59  *
60  * @code{.cpp}
61  * tiledb::Context ctx;
62  * // Use ctx when creating other objects:
63  * tiledb::ArraySchema schema(ctx, TILEDB_SPARSE);
64  *
65  * // Set a custom error handler:
66  * ctx.set_error_handler([](const std::string &msg) {
67  *     std::cerr << msg << std::endl;
68  * });
69  * @endcode
70  *
71  */
72 class Context {
73  public:
74   /* ********************************* */
75   /*     CONSTRUCTORS & DESTRUCTORS    */
76   /* ********************************* */
77 
78   /**
79    * Constructor. Creates a TileDB Context with default configuration.
80    * @throws TileDBError if construction fails
81    */
Context()82   Context() {
83     tiledb_ctx_t* ctx;
84     if (tiledb_ctx_alloc(nullptr, &ctx) != TILEDB_OK)
85       throw TileDBError("[TileDB::C++API] Error: Failed to create context");
86     ctx_ = std::shared_ptr<tiledb_ctx_t>(ctx, Context::free);
87     error_handler_ = default_error_handler;
88 
89     set_tag("x-tiledb-api-language", "c++");
90   }
91 
92   /**
93    * Constructor. Creates a TileDB context with the given configuration.
94    * @throws TileDBError if construction fails
95    */
Context(const Config & config)96   explicit Context(const Config& config) {
97     tiledb_ctx_t* ctx;
98     if (tiledb_ctx_alloc(config.ptr().get(), &ctx) != TILEDB_OK)
99       throw TileDBError("[TileDB::C++API] Error: Failed to create context");
100     ctx_ = std::shared_ptr<tiledb_ctx_t>(ctx, Context::free);
101     error_handler_ = default_error_handler;
102 
103     set_tag("x-tiledb-api-language", "c++");
104   }
105 
106   /**
107    * Constructor. Creates a TileDB context from the given pointer.
108    * @param own=true If false, disables underlying cleanup upon destruction.
109    * @throws TileDBError if construction fails
110    */
111   Context(tiledb_ctx_t* ctx, bool own = true) {
112     if (ctx == nullptr)
113       throw TileDBError(
114           "[TileDB::C++API] Error: Failed to create Context from pointer");
115 
116     ctx_ = std::shared_ptr<tiledb_ctx_t>(ctx, [own](tiledb_ctx_t* p) {
117       if (own) {
118         Context::free(p);
119       }
120     });
121 
122     error_handler_ = default_error_handler;
123 
124     set_tag("x-tiledb-api-language", "c++");
125   }
126   /* ********************************* */
127   /*                API                */
128   /* ********************************* */
129 
130   /**
131    * Error handler for the TileDB C API calls. Throws an exception
132    * in case of error.
133    *
134    * @param rc If != TILEDB_OK, calls error handler
135    */
handle_error(int rc)136   void handle_error(int rc) const {
137     // Do nothing if there is not error
138     if (rc == TILEDB_OK)
139       return;
140 
141     // Get error
142     const auto& ctx = ctx_.get();
143     tiledb_error_t* err = nullptr;
144     const char* msg = nullptr;
145     rc = tiledb_ctx_get_last_error(ctx, &err);
146     if (rc != TILEDB_OK) {
147       tiledb_error_free(&err);
148       error_handler_("[TileDB::C++API] Error: Non-retrievable error occurred");
149     }
150 
151     // Get error message
152     rc = tiledb_error_message(err, &msg);
153     if (rc != TILEDB_OK) {
154       tiledb_error_free(&err);
155       error_handler_("[TileDB::C++API] Error: Non-retrievable error occurred");
156     }
157     auto msg_str = std::string(msg);
158 
159     // Clean up
160     tiledb_error_free(&err);
161 
162     // Throw exception
163     error_handler_(msg_str);
164   }
165 
166   /** Returns the C TileDB context object. */
ptr()167   std::shared_ptr<tiledb_ctx_t> ptr() const {
168     return ctx_;
169   }
170 
171   /**
172    * Sets the error handler callback. If none is set, the
173    * `default_error_handler` is used. The callback accepts an error
174    * message.
175    *
176    * @param fn Error handler callback function
177    * @return Reference to this Context
178    */
set_error_handler(const std::function<void (const std::string &)> & fn)179   Context& set_error_handler(
180       const std::function<void(const std::string&)>& fn) {
181     error_handler_ = fn;
182     return *this;
183   }
184 
185   /** Returns a copy of the configuration of the context. **/
config()186   Config config() const {
187     tiledb_config_t* c;
188     handle_error(tiledb_ctx_get_config(ctx_.get(), &c));
189     return Config(&c);
190   }
191 
192   /**
193    * Return true if the given filesystem backend is supported.
194    *
195    * **Example:**
196    * @code{.cpp}
197    * tiledb::Context ctx;
198    * bool s3_supported = ctx.is_supported_fs(TILEDB_S3);
199    * @endcode
200    *
201    * @param fs Filesystem to check
202    */
is_supported_fs(tiledb_filesystem_t fs)203   bool is_supported_fs(tiledb_filesystem_t fs) const {
204     int ret;
205     handle_error(tiledb_ctx_is_supported_fs(ctx_.get(), fs, &ret));
206     return ret != 0;
207   }
208 
209   /**
210    * Cancels all background or async tasks associated with this context.
211    */
cancel_tasks()212   void cancel_tasks() const {
213     handle_error(tiledb_ctx_cancel_tasks(ctx_.get()));
214   }
215 
216   /** Sets a string/string KV tag on the context. */
set_tag(const std::string & key,const std::string & value)217   void set_tag(const std::string& key, const std::string& value) {
218     handle_error(tiledb_ctx_set_tag(ctx_.get(), key.c_str(), value.c_str()));
219   }
220 
221   /** Returns a JSON-formatted string of the stats. */
stats()222   std::string stats() {
223     char* c_str;
224     handle_error(tiledb_ctx_get_stats(ctx_.get(), &c_str));
225 
226     // Copy `c_str` into `str`.
227     std::string str(c_str);
228     ::free(c_str);
229 
230     return str;
231   }
232 
233   /* ********************************* */
234   /*          STATIC FUNCTIONS         */
235   /* ********************************* */
236 
237   /**
238    * The default error handler callback.
239    * @throws TileDBError with the error message
240    */
default_error_handler(const std::string & msg)241   static void default_error_handler(const std::string& msg) {
242     throw TileDBError(msg);
243   }
244 
245  private:
246   /* ********************************* */
247   /*         PRIVATE ATTRIBUTES        */
248   /* ********************************* */
249 
250   /** The C TileDB context object. */
251   std::shared_ptr<tiledb_ctx_t> ctx_;
252 
253   /** An error handler callback. */
254   std::function<void(const std::string&)> error_handler_;
255 
256   /** Wrapper function for freeing a context C object. */
free(tiledb_ctx_t * ctx)257   static void free(tiledb_ctx_t* ctx) {
258     tiledb_ctx_free(&ctx);
259   }
260 };
261 
262 }  // namespace tiledb
263 
264 #endif  // TILEDB_CPP_API_CONTEXT_H
265