# HG changeset patch # User Tero Marttila # Date 1229134856 -7200 # Node ID c65d0f4c3bff601db31cd951ede7b4a7268af941 # Parent 881de320e4833a9e4ed33688ab90fcca4c1e8535 evsql: documentation improvements, fix consts, use bools diff -r 881de320e483 -r c65d0f4c3bff src/evsql.h --- a/src/evsql.h Sat Nov 29 01:50:32 2008 +0200 +++ b/src/evsql.h Sat Dec 13 04:20:56 2008 +0200 @@ -1,38 +1,46 @@ #ifndef EVSQL_H #define EVSQL_H -/* - * An event-based (Postgre)SQL client API using libevent +/** + * A SQL library designed for use with libevent and PostgreSQL's libpq. Provides support for queueing non-transactional + * requests, transaction support, parametrized queries and result iteration. + * + * Currently, the API does not expose the underlying libpq data structures, but since it is currently the only + * underlying implementation, there is no garuntee that the same API will actually work with other databases' interface + * libraries... */ #include +#include #include +/** + * For err_t. + */ #include "lib/err.h" -/* - * The generic context handle +/** + * The generic context handle used to manage a single "database connector" with multiple queries/transactions. */ struct evsql; -/* - * Transaction handle +/** + * Opaque transaction handle */ struct evsql_trans; -/* - * A query handle +/** + * Opaque query handle */ struct evsql_query; -/* - * A result handle +/** + * Opaque result handle */ struct evsql_result; - -/* - * Transaction type +/** + * Various transaction isolation levels for conveniance */ enum evsql_trans_type { EVSQL_TRANS_DEFAULT, @@ -42,49 +50,76 @@ EVSQL_TRANS_READ_UNCOMMITTED, }; -/* - * Parameters and Result fields are both items +/** + * An item can be in different formats, the classical text-based format (i.e. snprintf "1234") or a more low-level + * binary format (i.e uint16_t 0x04F9 in network-byte order). */ enum evsql_item_format { + /** Format values as text strings */ EVSQL_FMT_TEXT, + + /** Type-specific binary encoding */ EVSQL_FMT_BINARY, }; +/** + * An item has a specific type, these correspond somewhat to the native database types. + */ enum evsql_item_type { + /** End marker, zero */ EVSQL_TYPE_INVALID, - + + /** A SQL NULL */ EVSQL_TYPE_NULL_, + /** A `struct evsql_item_binary` */ EVSQL_TYPE_BINARY, + + /** A NUL-terminated char* */ EVSQL_TYPE_STRING, + + /** A uint16_t value */ EVSQL_TYPE_UINT16, + + /** A uint32_t value */ EVSQL_TYPE_UINT32, + + /** A uint64_t value */ EVSQL_TYPE_UINT64, + + EVSQL_TYPE_MAX }; +/** + * Value for use with EVSQL_TYPE_BINARY, this just a non-NUL-terminated char* and an explicit length + */ struct evsql_item_binary { const char *ptr; size_t len; }; -/* - * Metadata about the type of an item +/** + * Metadata about the format and type of an item, this does not hold any actual value. */ struct evsql_item_info { - // format + /** The format */ enum evsql_item_format format; - // type + /** The type type */ enum evsql_item_type type; - // flags + /** Various flags */ struct evsql_item_flags { - uint8_t null_ok : 1; + /** + * The value may be NULL; behaviour varies for query and result + * XXX: document + */ + bool null_ok : 1; } flags; }; -/* - * Storage for various scalar values +/** + * An union to provide storage for the values of small types */ union evsql_item_value { uint16_t uint16; @@ -92,137 +127,215 @@ uint64_t uint64; }; -/* - * The type and value of an item +/** + * A generic structure containing the type and value of a query parameter or a result field. */ struct evsql_item { - // "header" + /** The "header" containing the type and format */ struct evsql_item_info info; - // pointer to the raw databytes. Set to NULL to indicate SQL-NULL, &value, or an external buf + /** + * Pointer to the raw databytes. + * Set to NULL for SQL NULLs, otherwise &value or an external buf + */ const char *bytes; - // size of byte array pointed to by bytes, zero for text + /** + * Size of the byte array pointed to by bytes, zero for EVSQL_FMT_TEXT data. + */ size_t length; - // the decoded value + /** + * Inline storage for small values + */ union evsql_item_value value; - // (internal) flags + /** Internal flags */ struct { - uint8_t has_value : 1; + /** + * The item has a value stored in `value` + */ + bool has_value : 1; } flags; }; -/* - * Query info, similar to prepared statements +/** + * Query meta-info, similar to a prepared statement. * - * Contains the literal SQL query and the types of the arguments + * Contains the literal SQL query and the types of the parameters, but no more. */ struct evsql_query_info { - // the SQL query itself + /** The SQL query itself */ const char *sql; - // the list of items + /** + * A variable-length array of the item_info parameters, terminated by an EVSQL_TYPE_INVALID entry. + */ struct evsql_item_info params[]; }; -/* - * Contains the query parameter types and their values +/** + * Contains the query parameter types and their actual values */ struct evsql_query_params { - // result format + /** Requested result format for this query. XXX: move elsewhere */ enum evsql_item_format result_format; - // list of params + /** + * A variable-length array of the item parameter-values, terminated by an EVSQL_TYPE_INVALID entry. + */ struct evsql_item list[]; }; -/* - * Result info - * - * Contains the types of the result columns +/** + * Result layout metadata. This contains the stucture needed to decode result rows. */ struct evsql_result_info { - // XXX: put something useful here? + // XXX: make up something useful to stick here int _unused; - // the list of fields + /** + * A variable-length array of the item_info column types. + */ struct evsql_item_info columns[]; }; -/* - * Macros for defining param/result infos/lists +/** + * Magic macros for defining param/result info -lists + */ + +/** + * A `struct evsql_item_info` initializer, using FMT_BINARY and the given EVSQL_TYPE_ -suffix. + * + * @see struct evsql_item_info + * @see enum evsql_item_type + */ +#define EVSQL_TYPE(typenam) { EVSQL_FMT_BINARY, EVSQL_TYPE_ ## typenam } + +/** + * End marker for a `struct evsql_item_info` array. + * + * @see struct evsql_item_info + */ +#define EVSQL_TYPE_END { EVSQL_FMT_BINARY, EVSQL_TYPE_INVALID } + +/** + * Initializer block for a `struct evsql_query_params` struct. EVSQL_PARAMS/EVSQL_PARAMS_END should be used as a + * pseudo-block with the following layout: + * + * static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { + * EVSQL_PARAM(...), + * ..., + * + * EVSQL_PARAMS_END + * }; */ #define EVSQL_PARAMS(result_fmt) { result_fmt, #define EVSQL_PARAM(typenam) { EVSQL_TYPE(typenam) } #define EVSQL_PARAMS_END { EVSQL_TYPE_END } \ } // <<< -#define EVSQL_TYPE(typenam) { EVSQL_FMT_BINARY, EVSQL_TYPE_ ## typenam } -#define EVSQL_TYPE_END { EVSQL_FMT_BINARY, EVSQL_TYPE_INVALID } - -/* +/** * Callback for handling query results. * * The query has completed, either succesfully or unsuccesfully. * * Use the evsql_result_* functions to manipulate the results, and call evsql_result_free (or equivalent) once done. + * + * @param res The result handle that must be result_free'd after use + * @param arg The void* passed to evsql_query_* + * + * @see evsql_query */ typedef void (*evsql_query_cb)(struct evsql_result *res, void *arg); -/* +/** * Callback for handling global-level errors. * * The evsql is not useable anymore. * - * XXX: this is not actually called yet, no retry logic implemented. + * XXX: this is not actually called yet, as no retry logic is implemented, so an evsql itself never fails. + * + * @see evsql_new_pq */ typedef void (*evsql_error_cb)(struct evsql *evsql, void *arg); -/* +/** * Callback for handling transaction-level errors. This may be called at any time during a transaction's lifetime, - * including from within the evsql_query_* functions. + * including from within the evsql_query_* functions (but not always). * * The transaction is not useable anymore. + * + * @param trans the transaction in question + * @param arg the void* passed to evsql_trans + * + * @see evsql_trans */ typedef void (*evsql_trans_error_cb)(struct evsql_trans *trans, void *arg); -/* +/** * Callback for handling evsql_trans/evsql_query_abort completion. The transaction is ready for use with evsql_query_*. + * + * @param trans the transaction in question + * @param arg the void* passed to evsql_trans + * + * @see evsql_trans + * @see evsql_query_abort */ typedef void (*evsql_trans_ready_cb)(struct evsql_trans *trans, void *arg); -/* +/** * Callback for handling evsql_trans_commit completion. The transaction was commited, and should not be used anymore. + * + * @param trans the transaction in question + * @param arg the void* passed to evsql_trans + * + * @see evsql_trans + * @see evsql_trans_commit */ typedef void (*evsql_trans_done_cb)(struct evsql_trans *trans, void *arg); -/* - * Create a new PostgreSQL/libpq(evpq) -based evsql using the given conninfo. +/** + * Create a new PostgreSQL/libpq (evpq) -based evsql using the given conninfo. * * The given conninfo must stay valid for the duration of the evsql's lifetime. + * + * See the libpq reference manual for the syntax of pq_conninfo + * + * @param ev_base the libevent base to use + * @param pq_conninfo the libpq connection information + * @param error_fn XXX: not used, may be NULL + * @param cb_arg: XXX: not used, argument for error_fn + * @return the evsql context handle for use with other functions */ struct evsql *evsql_new_pq (struct event_base *ev_base, const char *pq_conninfo, evsql_error_cb error_fn, void *cb_arg ); -/* +/** * Create a new transaction. * * A transaction will be allocated its own connection, and the "BEGIN TRANSACTION ..." query will be sent (use the - * evsql_trans_type argument to specify this). + * trans_type argument to specify this). * - * Once the transaction has been opened, the given evsql_trans_ready_cb will be triggered, and the transaction can then + * Once the transaction has been opened, the given trans_ready_cb will be triggered, and the transaction can then * be used (see evsql_query_*). * - * If, at any point, the transaction-connection fails, and pending query will be forgotten (i.e. the query callback - * will NOT be called), and the given evsql_trans_error_cb will be called. Note that this includes some, but not all, + * If, at any point, the transaction-connection fails, any pending query will be forgotten (i.e. the query callback + * will NOT be called), and the given trans_error_cb will be called. Note that this includes some, but not all, * cases where evsql_query_* returns an error. * * Once you are done with the transaction, call either evsql_trans_commit or evsql_trans_abort. + * + * @param evsql the context handle from evsql_new_* + * @param type the type of transaction to create + * @param error_fn the trans_error_cb to call if this transaction fails + * @param ready_fn the trans_ready_cb to call once this transaction is ready for use + * @param done_fn the trans_done_cb to call once this transaction has been commmited + * @param cb_arg the void* to pass to the above + * @return the evsql_trans handle for use with other functions */ struct evsql_trans *evsql_trans (struct evsql *evsql, enum evsql_trans_type type, evsql_trans_error_cb error_fn, @@ -231,36 +344,53 @@ void *cb_arg ); -/* +/** * Queue the given query for execution. * - * If trans is specified (not NULL), then the transaction must be idle, and the query will be executed in that + * If a trans is given (i.e. not NULL), then the transaction must be idle, and the query will be executed in that * transaction's context. Otherwise, the query will be executed without a transaction using an idle connection, or * enqueued for later execution. * * Once the query is complete (got a result, got an error, the connection failed), then the query_cb will be called. - * The callback can used the evsql_result_* functions to manipulate it. + * The callback can use the evsql_result_* functions to manipulate it. * * The returned evsql_query handle can be passed to evsql_query_abort at any point before query_fn being called. * + * @param evsql the context handle from evsql_new_* + * @param trans the optional transaction handle from evsql_trans + * @param command the raw SQL command itself + * @param query_fn the query_cb to call once the query is complete + * @param cb_arg the void* passed to the above + * @return the evsql_query handle that can be used to abort the query */ struct evsql_query *evsql_query (struct evsql *evsql, struct evsql_trans *trans, const char *command, evsql_query_cb query_fn, void *cb_arg); -/* +/** * Execute the given SQL query using the list of parameter types/values given via evsql_query_params. * + * Use the EVSQL_PARAMS macros to declare the struct, and the evsql_param_* functions to populate the values. + * * See evsql_query for more info about behaviour. + * + * XXX: explain SQL param syntax... + * + * @param command the SQL command to bind the parameters to + * @param params the parameter types and values + * @see evsql_query */ struct evsql_query *evsql_query_params (struct evsql *evsql, struct evsql_trans *trans, const char *command, const struct evsql_query_params *params, evsql_query_cb query_fn, void *cb_arg ); -/* - * Execute the given query_info's SQL query using the values given as variable arguments, using the evsql_query_info to +/** + * Execute the given query_info's SQL query with the values given as variable arguments, using the evsql_query_info to * resolve the types. * * See evsql_query for more info about behaviour. + * + * @param query_info the SQL query information + * @see evsql_query */ struct evsql_query *evsql_query_exec (struct evsql *evsql, struct evsql_trans *trans, const struct evsql_query_info *query_info, @@ -268,7 +398,7 @@ ... ); -/* +/** * Abort a query returned by evsql_query_* that has not yet completed (query_fn has not been called yet). * * The actual query itself may or may not be aborted (and hence may or may not be executed on the server), but query_fn @@ -277,42 +407,61 @@ * If the query is part of a transaction, then trans must be given, and the query must be the query that is currently * executing on that trans. The transaction's ready_fn will be called once the query has been aborted and the * transaction is now idle again. + * + * @param trans if the query is part of a transaction, then it MUST be given here + * @param query the in-progress query to abort */ void evsql_query_abort (struct evsql_trans *trans, struct evsql_query *query); -/* +/** * Commit a transaction using "COMMIT TRANSACTION". * * The transaction must be idle, just like for evsql_query. Once the transaction has been commited, the transaction's - * done_fn will be called, after which the transaction must not be used. + * done_fn will be called, after which the transaction must not be used anymore. * - * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is a FATAL error. + * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is an error. * * Note that done_fn will never be called directly, always indirectly via the event loop. + * + * @param trans the transaction handle from evsql_trans to commit + * @see evsql_trans */ int evsql_trans_commit (struct evsql_trans *trans); -/* +/** * Abort a transaction, using "ROLLBACK TRANSACTION". * - * No more transaction callbacks will be called, if there was a query running, it will be aborted, and the transaction + * No more transaction callbacks will be called. If there was a query running, it will be aborted, and the transaction * then rollback'd. * - * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is a FATAL error. + * You cannot abort a COMMIT, calling trans_abort on trans after a call to trans_commit is an error. * * Do not call evsql_trans_abort from within evsql_trans_error_cb! + * + * @param trans the transaction from evsql_trans to abort + * @see evsql_trans */ void evsql_trans_abort (struct evsql_trans *trans); -/* +// @{ +/** * Transaction-handling functions */ -// error string, meant to be called from evsql_trans_error_cb +/** + * Retrieve the transaction-specific error message from the underlying engine. + * + * Intended to be called from trans_error_cb + */ const char *evsql_trans_error (struct evsql_trans *trans); -/* - * Param-building functions +// @} + +// @{ +/** + * Parameter-building functions. + * + * These manipulate the value of the given parameter index. */ int evsql_param_binary (struct evsql_query_params *params, size_t param, const char *ptr, size_t len); int evsql_param_string (struct evsql_query_params *params, size_t param, const char *ptr); @@ -321,26 +470,37 @@ int evsql_param_null (struct evsql_query_params *params, size_t param); int evsql_params_clear (struct evsql_query_params *params); -/* +// @} + +// @{ +/** * Query-handling functions */ -// print out a textual repr of the given query/params via DEBUG +/** + * Print out a textual dump of the given query and parameters using DEBUG + */ void evsql_query_debug (const char *sql, const struct evsql_query_params *params); -/* +// @} + +// @{ +/** * Result-handling functions */ -/* +/** * Check the result for errors. Intended for use with non-data queries, i.e. CREATE, etc. * * Returns zero if the query was OK, err otherwise. EIO indicates an SQL error, the error message can be retrived * using evsql_result_error. + * + * @param res the result handle passed to query_cb + * @return zero on success, EIO on SQL error, positive error code otherwise */ err_t evsql_result_check (struct evsql_result *res); -/* +/** * The iterator-based interface results interface. * * Define an evsql_result_info struct that describes the columns returned by the query, and call evsql_result_begin on @@ -349,60 +509,140 @@ * Call evsql_result_end once you've stopped iteration. * * Returns zero if the evsql_result is ready for iteration, err otherwise. EIO indicates an SQL error, the error - * message can be retreived using evsql_result_error. + * message can be retreived using evsql_result_error. The result must be released in both cases. * * Note: currently the iterator state is simply stored in evsql_result, so only one iterator at a time per evsql_result. + * + * @param info the metadata to use to handle the result row columns + * @param res the result handle passed to query_cb + * @return zero on success, +err on error */ err_t evsql_result_begin (struct evsql_result_info *info, struct evsql_result *res); -/* - * Reads the next result row, storing the field values into the pointer arguments given. The types are resolved using - * the evsql_result_info given to evsql_result_begin. +/** + * Reads the next result row from the result prepared using evsql_result_begin. Stores the field values into to given + * pointer arguments based on the evsql_result_info given to evsql_result_begin. * - * Returns >0 when a row was read, 0 when there are no more rows, and -err if there was an error. + * @param res the result handle previous prepared using evsql_result_begin + * @param ... a set of pointers corresponding to the evsql_result_info specified using evsql_result_begin + * @return >0 when a row was read, zero when there are no more rows left, and -err on error */ int evsql_result_next (struct evsql_result *res, ...); -/* +/** * Ends the result iteration, releasing any associated resources and the result itself. * * The result should not be iterated or accessed anymore. * * Note: this does the same thing as evsql_result_free, and works regardless of evsql_result_begin returning * succesfully or not. + * + * @param res the result handle passed to query_cb + * @see evsql_result_free */ void evsql_result_end (struct evsql_result *res); - -// get error message associated with function +/** + * Get the error message associated with the result, intended for use after evsql_result_check/begin return an error + * code. + * + * @param res the result handle passed to query_cb + * @return a char* containing the NUL-terminated error string. Valid until evsql_result_free is called. + */ const char *evsql_result_error (const struct evsql_result *res); -// number of rows in the result +/** + * Get the number of data rows returned by the query + * + * @param res the result handle passed to query_cb + * @return the number of rows, >= 0 + */ size_t evsql_result_rows (const struct evsql_result *res); -// number of columns in the result +/** + * Get the number of columns in the data results from the query + * + * @param res the result handle passed to query_cb + * @return the number of columns. XXX: zero if no data? + */ size_t evsql_result_cols (const struct evsql_result *res); -// number of affected rows for UPDATE/INSERT +/** + * Get the number of rows affected by an UPDATE/INSERT/etc query. + * + * @param res the result handle passed to query_cb + * @return the number of rows affected, >= 0 + */ size_t evsql_result_affected (const struct evsql_result *res); -// fetch the raw binary value from a result set, and return it via ptr -// if size is nonzero, check that the size of the field data matches -int evsql_result_binary (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size, int nullok); +/** + * Fetch the raw binary value for the given field, returning it via ptr/size. + * + * The given row/col must be within bounds as returned by evsql_result_rows/cols. + * + * *ptr will point to *size bytes of read-only memory allocated internally. + * + * @param res the result handle passed to query_cb + * @param row the row index to access + * @param col the column index to access + * @param ptr where to store a pointer to the read-only field data, free'd upon evsql_result_free + * @param size updated to the size of the field value pointed to by ptr + * @param nullok when true and the field value is NULL, *ptr and *size are not modified, otherwise NULL means an error + * @return zero on success, <0 on error + */ +int evsql_result_binary (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size, bool nullok); + +/** + * Fetch the textual value of the given field, returning it via ptr. + * + * The given row/col must be within bounds as returned by evsql_result_rows/cols. + * + * *ptr will point to a NUL-terminated string allocated internally. + * + * @param res the result handle passed to query_cb + * @param row the row index to access + * @param col the column index to access + * @param ptr where to store a pointer to the read-only field data, free'd upon evsql_result_free + * @param nullok when true and the field value is NULL, *ptr and *size are not modified, otherwise NULL means an error + * @return zero on success, <0 on error + */ int evsql_result_string (const struct evsql_result *res, size_t row, size_t col, const char **ptr, int nullok); -// fetch certain kinds of values from a binary result set +// @{ +/** + * Use evsql_result_binary to read a binary field value, and then convert it using ntoh[slq], storing the value in + * *val. + * + * The given row/col must be within bounds as returned by evsql_result_rows/cols. + * + * @param res the result handle passed to query_cb + * @param row the row index to access + * @param col the column index to access + * @param val where to store the decoded value + * @param nullok when true and the field value is NULL, *ptr and *size are not modified, otherwise NULL means an error + * @return zero on success, <0 on error + */ int evsql_result_uint16 (const struct evsql_result *res, size_t row, size_t col, uint16_t *uval, int nullok); int evsql_result_uint32 (const struct evsql_result *res, size_t row, size_t col, uint32_t *uval, int nullok); int evsql_result_uint64 (const struct evsql_result *res, size_t row, size_t col, uint64_t *uval, int nullok); -// release the result set, freeing its memory +// @} + +/** + * Every result handle passed to query_cb MUST be released by the user, using this function. + * + * @param res the result handle passed to query_cb + */ void evsql_result_free (struct evsql_result *res); -/* +// @} + +/** * Close a connection. Callbacks for waiting queries will not be run. * * XXX: not implemented yet. + * + * @param evsql the context handle from evsql_new_* */ void evsql_close (struct evsql *evsql); diff -r 881de320e483 -r c65d0f4c3bff src/evsql/result.c --- a/src/evsql/result.c Sat Nov 29 01:50:32 2008 +0200 +++ b/src/evsql/result.c Sat Dec 13 04:20:56 2008 +0200 @@ -65,7 +65,7 @@ } } -int evsql_result_field (const struct evsql_result *res, size_t row, size_t col, char ** const ptr, size_t *size) { +int evsql_result_field (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size) { *ptr = NULL; switch (res->evsql->type) { @@ -147,7 +147,7 @@ va_start(vargs, res); for (col = res->info->columns, col_idx = 0; col->type; col++, col_idx++) { - char *value = NULL; + const char *value = NULL; size_t length = 0; // check for NULLs, then try and get the field value @@ -172,7 +172,7 @@ } break; case EVSQL_TYPE_STRING: { - char **str_ptr = va_arg(vargs, char **); + const char **str_ptr = va_arg(vargs, const char **); if (value) { *str_ptr = value; diff -r 881de320e483 -r c65d0f4c3bff src/evsql/util.c --- a/src/evsql/util.c Sat Nov 29 01:50:32 2008 +0200 +++ b/src/evsql/util.c Sat Dec 13 04:20:56 2008 +0200 @@ -125,7 +125,7 @@ } } -int evsql_result_binary (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size, int nullok) { +int evsql_result_binary (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size, bool nullok) { *ptr = NULL; switch (res->evsql->type) {