terom@21: #ifndef EVSQL_H terom@21: #define EVSQL_H terom@21: terom@21: /* terom@21: * An event-based (Postgre)SQL client API using libevent terom@21: */ terom@21: terom@28: #include terom@21: #include terom@21: terom@45: #include "lib/err.h" terom@44: terom@21: /* terom@21: * The generic context handle terom@21: */ terom@21: struct evsql; terom@21: terom@21: /* terom@43: * Transaction handle terom@43: */ terom@43: struct evsql_trans; terom@43: terom@43: /* terom@21: * A query handle terom@21: */ terom@21: struct evsql_query; terom@21: terom@21: /* terom@43: * A result handle terom@25: */ terom@43: struct evsql_result; terom@43: terom@25: terom@25: /* terom@25: * Transaction type terom@25: */ terom@25: enum evsql_trans_type { terom@25: EVSQL_TRANS_DEFAULT, terom@25: EVSQL_TRANS_SERIALIZABLE, terom@25: EVSQL_TRANS_REPEATABLE_READ, terom@25: EVSQL_TRANS_READ_COMMITTED, terom@25: EVSQL_TRANS_READ_UNCOMMITTED, terom@25: }; terom@25: terom@25: /* terom@43: * Parameters and Result fields are both items terom@24: */ terom@43: enum evsql_item_format { terom@24: EVSQL_FMT_TEXT, terom@24: EVSQL_FMT_BINARY, terom@24: }; terom@24: terom@43: enum evsql_item_type { terom@43: EVSQL_TYPE_INVALID, terom@24: terom@43: EVSQL_TYPE_NULL_, terom@31: terom@43: EVSQL_TYPE_BINARY, terom@43: EVSQL_TYPE_STRING, terom@43: EVSQL_TYPE_UINT16, terom@43: EVSQL_TYPE_UINT32, terom@43: EVSQL_TYPE_UINT64, terom@43: }; terom@43: terom@44: struct evsql_item_binary { terom@44: const char *ptr; terom@44: size_t len; terom@44: }; terom@44: terom@44: /* terom@44: * Metadata about the type of an item terom@44: */ terom@44: struct evsql_item_info { terom@43: // format terom@43: enum evsql_item_format format; terom@43: terom@43: // type terom@43: enum evsql_item_type type; terom@43: terom@44: // flags terom@44: struct evsql_item_flags { terom@44: uint8_t null_ok : 1; terom@44: } flags; terom@44: }; terom@44: terom@44: /* terom@45: * Storage for various scalar values terom@45: */ terom@45: union evsql_item_value { terom@45: uint16_t uint16; terom@45: uint32_t uint32; terom@45: uint64_t uint64; terom@45: }; terom@45: terom@45: /* terom@44: * The type and value of an item terom@44: */ terom@44: struct evsql_item { terom@44: // "header" terom@44: struct evsql_item_info info; terom@44: terom@45: // pointer to the raw databytes. Set to NULL to indicate SQL-NULL, &value, or an external buf terom@43: const char *bytes; terom@43: terom@43: // size of byte array pointed to by bytes, zero for text terom@43: size_t length; terom@43: terom@43: // the decoded value terom@45: union evsql_item_value value; terom@45: terom@45: // (internal) flags terom@45: struct { terom@45: char has_value : 1; terom@45: } flags; terom@24: }; terom@24: terom@24: /* terom@43: * Query info, similar to prepared statements terom@44: * terom@44: * Contains the literal SQL query and the types of the arguments terom@23: */ terom@43: struct evsql_query_info { terom@43: // the SQL query itself terom@43: const char *sql; terom@23: terom@43: // the list of items terom@44: struct evsql_item_info params[]; terom@44: }; terom@44: terom@44: /* terom@44: * Contains the query parameter types and their values terom@44: */ terom@44: struct evsql_query_params { terom@44: // result format terom@44: enum evsql_item_format result_format; terom@44: terom@44: // list of params terom@44: struct evsql_item list[]; terom@23: }; terom@23: terom@43: /* terom@43: * Result info terom@44: * terom@44: * Contains the types of the result columns terom@43: */ terom@43: struct evsql_result_info { terom@44: // XXX: put something useful here? terom@44: int _unused; terom@43: terom@43: // the list of fields terom@44: struct evsql_item_info columns[]; terom@21: }; terom@21: terom@21: /* terom@25: * Callback for handling query results. terom@21: * terom@45: * The query has completed, either succesfully or unsuccesfully. terom@25: * terom@45: * Use the evsql_result_* functions to manipulate the results, and call evsql_result_free (or equivalent) once done. terom@21: */ terom@44: typedef void (*evsql_query_cb)(struct evsql_result *res, void *arg); terom@21: terom@21: /* terom@25: * Callback for handling global-level errors. terom@21: * terom@25: * The evsql is not useable anymore. terom@25: * terom@25: * XXX: this is not actually called yet, no retry logic implemented. terom@21: */ terom@21: typedef void (*evsql_error_cb)(struct evsql *evsql, void *arg); terom@21: terom@21: /* terom@45: * Callback for handling transaction-level errors. This may be called at any time during a transaction's lifetime, terom@45: * including from within the evsql_query_* functions. terom@25: * terom@25: * The transaction is not useable anymore. terom@25: */ terom@25: typedef void (*evsql_trans_error_cb)(struct evsql_trans *trans, void *arg); terom@25: terom@25: /* terom@45: * Callback for handling evsql_trans/evsql_query_abort completion. The transaction is ready for use with evsql_query_*. terom@25: */ terom@25: typedef void (*evsql_trans_ready_cb)(struct evsql_trans *trans, void *arg); terom@25: terom@25: /* terom@45: * Callback for handling evsql_trans_commit completion. The transaction was commited, and should not be used anymore. terom@26: */ terom@26: typedef void (*evsql_trans_done_cb)(struct evsql_trans *trans, void *arg); terom@26: terom@26: /* terom@21: * Create a new PostgreSQL/libpq(evpq) -based evsql using the given conninfo. terom@25: * terom@25: * The given conninfo must stay valid for the duration of the evsql's lifetime. terom@21: */ terom@45: struct evsql *evsql_new_pq (struct event_base *ev_base, const char *pq_conninfo, terom@45: evsql_error_cb error_fn, terom@45: void *cb_arg terom@45: ); terom@21: terom@21: /* terom@25: * Create a new transaction. terom@25: * terom@45: * A transaction will be allocated its own connection, and the "BEGIN TRANSACTION ..." query will be sent (use the terom@45: * evsql_trans_type argument to specify this). terom@25: * terom@45: * Once the transaction has been opened, the given evsql_trans_ready_cb will be triggered, and the transaction can then terom@45: * be used (see evsql_query_*). terom@26: * terom@45: * If, at any point, the transaction-connection fails, and pending query will be forgotten (i.e. the query callback terom@45: * will NOT be called), and the given evsql_trans_error_cb will be called. Note that this includes some, but not all, terom@45: * cases where evsql_query_* returns an error. terom@45: * terom@45: * Once you are done with the transaction, call either evsql_trans_commit or evsql_trans_abort. terom@21: */ terom@45: struct evsql_trans *evsql_trans (struct evsql *evsql, enum evsql_trans_type type, terom@45: evsql_trans_error_cb error_fn, terom@45: evsql_trans_ready_cb ready_fn, terom@45: evsql_trans_done_cb done_fn, terom@45: void *cb_arg terom@45: ); terom@21: terom@21: /* terom@25: * Queue the given query for execution. terom@25: * terom@25: * If trans is specified (not NULL), then the transaction must be idle, and the query will be executed in that terom@45: * transaction's context. Otherwise, the query will be executed without a transaction using an idle connection, or terom@45: * enqueued for later execution. terom@25: * terom@45: * Once the query is complete (got a result, got an error, the connection failed), then the query_cb will be called. terom@45: * The callback can used the evsql_result_* functions to manipulate it. terom@45: * terom@45: * The returned evsql_query handle can be passed to evsql_query_abort at any point before query_fn being called. terom@45: * terom@23: */ terom@25: struct evsql_query *evsql_query (struct evsql *evsql, struct evsql_trans *trans, const char *command, evsql_query_cb query_fn, void *cb_arg); terom@25: terom@25: /* terom@45: * Execute the given SQL query using the list of parameter types/values given via evsql_query_params. terom@45: * terom@45: * See evsql_query for more info about behaviour. terom@25: */ terom@44: struct evsql_query *evsql_query_params (struct evsql *evsql, struct evsql_trans *trans, terom@44: const char *command, const struct evsql_query_params *params, terom@44: evsql_query_cb query_fn, void *cb_arg terom@44: ); terom@44: terom@44: /* terom@45: * Execute the given query_info's SQL query using the values given as variable arguments, using the evsql_query_info to terom@45: * resolve the types. terom@45: * terom@45: * See evsql_query for more info about behaviour. terom@44: */ terom@44: struct evsql_query *evsql_query_exec (struct evsql *evsql, struct evsql_trans *trans, terom@44: const struct evsql_query_info *query_info, terom@44: evsql_query_cb query_fn, void *cb_arg, terom@44: ... terom@44: ); terom@24: terom@24: /* terom@45: * Abort a query returned by evsql_query_* that has not yet completed (query_fn has not been called yet). terom@27: * terom@45: * The actual query itself may or may not be aborted (and hence may or may not be executed on the server), but query_fn terom@45: * will not be called anymore, and the query will dispose of itself and any results returned. terom@27: * terom@27: * If the query is part of a transaction, then trans must be given, and the query must be the query that is currently terom@45: * executing on that trans. The transaction's ready_fn will be called once the query has been aborted and the terom@45: * transaction is now idle again. terom@27: */ terom@27: void evsql_query_abort (struct evsql_trans *trans, struct evsql_query *query); terom@27: terom@27: /* terom@45: * Commit a transaction using "COMMIT TRANSACTION". terom@27: * terom@45: * The transaction must be idle, just like for evsql_query. Once the transaction has been commited, the transaction's terom@45: * done_fn will be called, after which the transaction must not be used. terom@28: * terom@27: * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is a FATAL error. terom@45: * terom@45: * Note that done_fn will never be called directly, always indirectly via the event loop. terom@26: */ terom@26: int evsql_trans_commit (struct evsql_trans *trans); terom@26: terom@26: /* terom@45: * Abort a transaction, using "ROLLBACK TRANSACTION". terom@45: * terom@45: * No more transaction callbacks will be called, if there was a query running, it will be aborted, and the transaction terom@45: * then rollback'd. terom@27: * terom@27: * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is a FATAL error. terom@45: * terom@45: * Do not call evsql_trans_abort from within evsql_trans_error_cb! terom@26: */ terom@27: void evsql_trans_abort (struct evsql_trans *trans); terom@26: terom@26: /* terom@26: * Transaction-handling functions terom@26: */ terom@26: terom@26: // error string, meant to be called from evsql_trans_error_cb terom@26: const char *evsql_trans_error (struct evsql_trans *trans); terom@26: terom@26: /* terom@24: * Param-building functions terom@24: */ terom@31: int evsql_param_binary (struct evsql_query_params *params, size_t param, const char *ptr, size_t len); terom@24: int evsql_param_string (struct evsql_query_params *params, size_t param, const char *ptr); terom@31: int evsql_param_uint16 (struct evsql_query_params *params, size_t param, uint16_t uval); terom@24: int evsql_param_uint32 (struct evsql_query_params *params, size_t param, uint32_t uval); terom@38: int evsql_param_null (struct evsql_query_params *params, size_t param); terom@31: int evsql_params_clear (struct evsql_query_params *params); terom@31: terom@31: /* terom@31: * Query-handling functions terom@31: */ terom@31: terom@31: // print out a textual repr of the given query/params via DEBUG terom@31: void evsql_query_debug (const char *sql, const struct evsql_query_params *params); terom@24: terom@24: /* terom@24: * Result-handling functions terom@24: */ terom@24: terom@45: /* terom@45: * Check the result for errors. Intended for use with non-data queries, i.e. CREATE, etc. terom@45: * terom@45: * Returns zero if the query was OK, err otherwise. EIO indicates an SQL error, the error message can be retrived terom@45: * using evsql_result_error. terom@45: */ terom@45: err_t evsql_result_check (struct evsql_result *res); terom@44: terom@44: /* terom@45: * The iterator-based interface results interface. terom@44: * terom@45: * Define an evsql_result_info struct that describes the columns returned by the query, and call evsql_result_begin on terom@45: * the evsql_result. This verifies the query result, and then prepares it for iteration using evsql_result_next. terom@45: * terom@45: * Call evsql_result_end once you've stopped iteration. terom@45: * terom@45: * Returns zero if the evsql_result is ready for iteration, err otherwise. EIO indicates an SQL error, the error terom@45: * message can be retreived using evsql_result_error. terom@45: * terom@45: * Note: currently the iterator state is simply stored in evsql_result, so only one iterator at a time per evsql_result. terom@44: */ terom@44: err_t evsql_result_begin (struct evsql_result_info *info, struct evsql_result *res); terom@45: terom@45: /* terom@45: * Reads the next result row, storing the field values into the pointer arguments given. The types are resolved using terom@45: * the evsql_result_info given to evsql_result_begin. terom@45: * terom@45: * Returns >0 when a row was read, 0 when there are no more rows, and -err if there was an error. terom@45: */ terom@44: int evsql_result_next (struct evsql_result *res, ...); terom@45: terom@45: /* terom@45: * Ends the result iteration, releasing any associated resources and the result itself. terom@45: * terom@45: * The result should not be iterated or accessed anymore. terom@45: * terom@45: * Note: this does the same thing as evsql_result_free, and works regardless of evsql_result_begin returning terom@45: * succesfully or not. terom@45: */ terom@44: void evsql_result_end (struct evsql_result *res); terom@24: terom@45: terom@45: // get error message associated with function terom@45: const char *evsql_result_error (const struct evsql_result *res); terom@45: terom@24: // number of rows in the result terom@44: size_t evsql_result_rows (const struct evsql_result *res); terom@24: terom@24: // number of columns in the result terom@44: size_t evsql_result_cols (const struct evsql_result *res); terom@24: terom@34: // number of affected rows for UPDATE/INSERT terom@44: size_t evsql_result_affected (const struct evsql_result *res); terom@34: terom@24: // fetch the raw binary value from a result set, and return it via ptr terom@24: // if size is nonzero, check that the size of the field data matches terom@44: int evsql_result_binary (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size, int nullok); terom@44: int evsql_result_string (const struct evsql_result *res, size_t row, size_t col, const char **ptr, int nullok); terom@24: terom@24: // fetch certain kinds of values from a binary result set terom@44: int evsql_result_uint16 (const struct evsql_result *res, size_t row, size_t col, uint16_t *uval, int nullok); terom@44: int evsql_result_uint32 (const struct evsql_result *res, size_t row, size_t col, uint32_t *uval, int nullok); terom@44: int evsql_result_uint64 (const struct evsql_result *res, size_t row, size_t col, uint64_t *uval, int nullok); terom@24: terom@24: // release the result set, freeing its memory terom@44: void evsql_result_free (struct evsql_result *res); terom@24: terom@23: /* terom@21: * Close a connection. Callbacks for waiting queries will not be run. terom@21: * terom@21: * XXX: not implemented yet. terom@21: */ terom@21: void evsql_close (struct evsql *evsql); terom@21: terom@21: #endif /* EVSQL_H */