summaryrefslogtreecommitdiffstats
path: root/src/lib-sql/sql-api.h
blob: 7936b98628bde04d70b5cff7edf4e03ed5b404da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#ifndef SQL_API_H
#define SQL_API_H

struct timespec;

/* This SQL API is designed to work asynchronously. The underlying drivers
   however may not. */

enum sql_db_flags {
	/* Set if queries are not executed asynchronously */
	SQL_DB_FLAG_BLOCKING		= 0x01,
	/* Set if database wants to use connection pooling */
	SQL_DB_FLAG_POOLED		= 0x02,
	/* Prepared statements are supported by the database. If they aren't,
	   the functions can still be used, but they're just internally
	   convered into regular statements. */
	SQL_DB_FLAG_PREP_STATEMENTS	= 0x04,
	/* Database supports INSERT .. ON DUPLICATE KEY syntax. */
	SQL_DB_FLAG_ON_DUPLICATE_KEY	= 0x08,
	/* Database supports INSERT .. ON CONFLICT DO UPDATE syntax. */
	SQL_DB_FLAG_ON_CONFLICT_DO	= 0x10,
};

enum sql_field_type {
	SQL_TYPE_STR,
	SQL_TYPE_UINT,
	SQL_TYPE_ULLONG,
	SQL_TYPE_BOOL
};

struct sql_field_def {
	enum sql_field_type type;
	const char *name;
	size_t offset;
};

enum sql_result_error_type {
	SQL_RESULT_ERROR_TYPE_UNKNOWN = 0,
	/* It's unknown whether write succeeded or not. This could be due to
	   a timeout or a disconnection from server. */
	SQL_RESULT_ERROR_TYPE_WRITE_UNCERTAIN
};

enum sql_result_next {
	/* Row was returned */
	SQL_RESULT_NEXT_OK = 1,
	/* There are no more rows */
	SQL_RESULT_NEXT_LAST = 0,
	/* Error occurred - see sql_result_get_error*() */
	SQL_RESULT_NEXT_ERROR = -1,
	/* There are more results - call sql_result_more() */
	SQL_RESULT_NEXT_MORE = -99
};

#define SQL_DEF_STRUCT(name, struct_name, type, c_type) \
	{ (type) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \
		((struct struct_name *)0)->name, c_type), \
	  #name, offsetof(struct struct_name, name) }

#define SQL_DEF_STRUCT_STR(name, struct_name) \
	SQL_DEF_STRUCT(name, struct_name, SQL_TYPE_STR, const char *)
#define SQL_DEF_STRUCT_UINT(name, struct_name) \
	SQL_DEF_STRUCT(name, struct_name, SQL_TYPE_UINT, unsigned int)
#define SQL_DEF_STRUCT_ULLONG(name, struct_name) \
	SQL_DEF_STRUCT(name, struct_name, SQL_TYPE_ULLONG, unsigned long long)
#define SQL_DEF_STRUCT_BOOL(name, struct_name) \
	SQL_DEF_STRUCT(name, struct_name, SQL_TYPE_BOOL, bool)

struct sql_db;
struct sql_result;

struct sql_commit_result {
	const char *error;
	enum sql_result_error_type error_type;
};

struct sql_settings {
	const char *driver;
	const char *connect_string;
	struct event *event_parent;
};

typedef void sql_query_callback_t(struct sql_result *result, void *context);
typedef void sql_commit_callback_t(const struct sql_commit_result *result, void *context);

void sql_drivers_init(void);
void sql_drivers_deinit(void);

/* register all built-in SQL drivers */
void sql_drivers_register_all(void);

void sql_driver_register(const struct sql_db *driver);
void sql_driver_unregister(const struct sql_db *driver);

/* Initialize database connections. db_driver is the database driver name,
   eg. "mysql" or "pgsql". connect_string is driver-specific. */
struct sql_db *sql_init(const char *db_driver, const char *connect_string);
int sql_init_full(const struct sql_settings *set, struct sql_db **db_r,
		  const char **error_r);

void sql_ref(struct sql_db *db);
void sql_unref(struct sql_db **db);

/* Returns SQL database state flags. */
enum sql_db_flags sql_get_flags(struct sql_db *db);

/* Explicitly connect to the database. It's not required to call this function
   though. Returns -1 if we're not connected, 0 if we started connecting or
   1 if we are fully connected now. */
int sql_connect(struct sql_db *db);
/* Explicitly disconnect from database and abort pending auth requests. */
void sql_disconnect(struct sql_db *db);

/* Escape the given string if needed and return it. */
const char *sql_escape_string(struct sql_db *db, const char *string);
/* Escape the given data as a string. */
const char *sql_escape_blob(struct sql_db *db,
			    const unsigned char *data, size_t size);

/* Execute SQL query without waiting for results. */
void sql_exec(struct sql_db *db, const char *query);
/* Execute SQL query and return result in callback. If fields list is given,
   the returned fields are validated to be of correct type, and you can use
   sql_result_next_row_get() */
void sql_query(struct sql_db *db, const char *query,
	       sql_query_callback_t *callback, void *context);
#define sql_query(db, query, callback, context) \
	sql_query(db, query - \
		CALLBACK_TYPECHECK(callback, void (*)( \
			struct sql_result *, typeof(context))), \
		(sql_query_callback_t *)callback, context)
/* Execute blocking SQL query and return result. */
struct sql_result *sql_query_s(struct sql_db *db, const char *query);

struct sql_prepared_statement *
sql_prepared_statement_init(struct sql_db *db, const char *query_template);
void sql_prepared_statement_unref(struct sql_prepared_statement **prep_stmt);

struct sql_statement *
sql_statement_init(struct sql_db *db, const char *query_template);
struct sql_statement *
sql_statement_init_prepared(struct sql_prepared_statement *prep_stmt);
void sql_statement_abort(struct sql_statement **stmt);
void sql_statement_set_timestamp(struct sql_statement *stmt,
				 const struct timespec *ts);
void sql_statement_bind_str(struct sql_statement *stmt,
			    unsigned int column_idx, const char *value);
void sql_statement_bind_binary(struct sql_statement *stmt,
			       unsigned int column_idx, const void *value,
			       size_t value_size);
void sql_statement_bind_int64(struct sql_statement *stmt,
			      unsigned int column_idx, int64_t value);
void sql_statement_query(struct sql_statement **stmt,
			 sql_query_callback_t *callback, void *context);
#define sql_statement_query(stmt, callback, context) \
	sql_statement_query(stmt, \
		(sql_query_callback_t *)callback, TRUE ? context : \
		CALLBACK_TYPECHECK(callback, void (*)( \
			struct sql_result *, typeof(context))))
struct sql_result *sql_statement_query_s(struct sql_statement **stmt);

void sql_result_setup_fetch(struct sql_result *result,
			    const struct sql_field_def *fields,
			    void *dest, size_t dest_size);

/* Go to next row. See enum sql_result_next. */
int sql_result_next_row(struct sql_result *result);

/* If sql_result_next_row() returned SQL_RESULT_NEXT_MORE, this can be called
   to continue returning more results. The result is freed with this call, so
   it must not be accesed anymore until the callback is finished. */
void sql_result_more(struct sql_result **result,
		     sql_query_callback_t *callback, void *context);
#define sql_result_more(result, callback, context) \
	sql_result_more(result - \
		CALLBACK_TYPECHECK(callback, void (*)( \
			struct sql_result *, typeof(context))), \
		(sql_query_callback_t *)callback, context)
/* Synchronous version of sql_result_more(). The result will be replaced with
   the new result. */
void sql_result_more_s(struct sql_result **result);

void sql_result_ref(struct sql_result *result);
/* Needs to be called only with sql_query_s() or when result has been
   explicitly referenced. */
void sql_result_unref(struct sql_result *result);

/* Return number of fields in result. */
unsigned int sql_result_get_fields_count(struct sql_result *result);
/* Return name of the given field index. */
const char *sql_result_get_field_name(struct sql_result *result,
				      unsigned int idx);
/* Return field index for given name, or -1 if not found. */
int sql_result_find_field(struct sql_result *result, const char *field_name);

/* Returns value of given field as string. Note that it can be NULL. */
const char *sql_result_get_field_value(struct sql_result *result,
				       unsigned int idx);
/* Returns a binary value. Note that a NULL is returned as NULL with size=0,
   while empty string returns non-NULL with size=0. */
const unsigned char *
sql_result_get_field_value_binary(struct sql_result *result,
				  unsigned int idx, size_t *size_r);
/* Find the field and return its value. NULL return value can mean that either
   the field didn't exist or that its value is NULL. */
const char *sql_result_find_field_value(struct sql_result *result,
					const char *field_name);
/* Return all values of current row. Note that this array is not
   NULL-terminated - you must use sql_result_get_fields_count() to find out
   the array's length. It's also possible that some of the values inside the
   array are NULL. */
const char *const *sql_result_get_values(struct sql_result *result);

/* Return last error message in result. */
const char *sql_result_get_error(struct sql_result *result);
enum sql_result_error_type sql_result_get_error_type(struct sql_result *result);

/* Begin a new transaction. Currently you're limited to only one open
   transaction at a time. */
struct sql_transaction_context *sql_transaction_begin(struct sql_db *db);
/* Commit transaction. */
void sql_transaction_commit(struct sql_transaction_context **ctx,
			    sql_commit_callback_t *callback, void *context);
#define sql_transaction_commit(ctx, callback, context) \
	  sql_transaction_commit(ctx - \
		CALLBACK_TYPECHECK(callback, void (*)( \
			const struct sql_commit_result *, typeof(context))), \
		(sql_commit_callback_t *)callback, context)
/* Synchronous commit. Returns 0 if ok, -1 if error. */
int sql_transaction_commit_s(struct sql_transaction_context **ctx,
			     const char **error_r);
void sql_transaction_rollback(struct sql_transaction_context **ctx);

/* Execute query in given transaction. */
void sql_update(struct sql_transaction_context *ctx, const char *query);
void sql_update_stmt(struct sql_transaction_context *ctx,
		     struct sql_statement **stmt);
/* Save the number of rows updated by this query. The value is set before
   commit callback is called. */
void sql_update_get_rows(struct sql_transaction_context *ctx, const char *query,
			 unsigned int *affected_rows);
void sql_update_stmt_get_rows(struct sql_transaction_context *ctx,
			      struct sql_statement **stmt,
			      unsigned int *affected_rows);

/* Wait for SQL query results. */
void sql_wait(struct sql_db *db);

#endif