|
1 |
|
2 #include "internal.h" |
|
3 #include "lib/error.h" |
|
4 #include "lib/misc.h" |
|
5 |
|
6 #include <stdlib.h> |
|
7 #include <assert.h> |
|
8 |
|
9 const char *evsql_result_error (const struct evsql_result *res) { |
|
10 if (!res->error) |
|
11 return "No error"; |
|
12 |
|
13 switch (res->evsql->type) { |
|
14 case EVSQL_EVPQ: |
|
15 if (!res->result.pq) |
|
16 return "unknown error (no result)"; |
|
17 |
|
18 return PQresultErrorMessage(res->result.pq); |
|
19 |
|
20 default: |
|
21 FATAL("res->evsql->type"); |
|
22 } |
|
23 |
|
24 } |
|
25 |
|
26 size_t evsql_result_rows (const struct evsql_result *res) { |
|
27 switch (res->evsql->type) { |
|
28 case EVSQL_EVPQ: |
|
29 return PQntuples(res->result.pq); |
|
30 |
|
31 default: |
|
32 FATAL("res->evsql->type"); |
|
33 } |
|
34 } |
|
35 |
|
36 size_t evsql_result_cols (const struct evsql_result *res) { |
|
37 switch (res->evsql->type) { |
|
38 case EVSQL_EVPQ: |
|
39 return PQnfields(res->result.pq); |
|
40 |
|
41 default: |
|
42 FATAL("res->evsql->type"); |
|
43 } |
|
44 } |
|
45 |
|
46 size_t evsql_result_affected (const struct evsql_result *res) { |
|
47 switch (res->evsql->type) { |
|
48 case EVSQL_EVPQ: |
|
49 // XXX: errors? |
|
50 return strtol(PQcmdTuples(res->result.pq), NULL, 10); |
|
51 |
|
52 default: |
|
53 FATAL("res->evsql->type"); |
|
54 } |
|
55 } |
|
56 |
|
57 |
|
58 int evsql_result_null (const struct evsql_result *res, size_t row, size_t col) { |
|
59 switch (res->evsql->type) { |
|
60 case EVSQL_EVPQ: |
|
61 return PQgetisnull(res->result.pq, row, col); |
|
62 |
|
63 default: |
|
64 FATAL("res->evsql->type"); |
|
65 } |
|
66 } |
|
67 |
|
68 int evsql_result_field (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size) { |
|
69 *ptr = NULL; |
|
70 |
|
71 switch (res->evsql->type) { |
|
72 case EVSQL_EVPQ: |
|
73 if (PQfformat(res->result.pq, col) != 1) |
|
74 ERROR("[%zu:%zu] PQfformat is not binary: %d", row, col, PQfformat(res->result.pq, col)); |
|
75 |
|
76 *size = PQgetlength(res->result.pq, row, col); |
|
77 *ptr = PQgetvalue(res->result.pq, row, col); |
|
78 |
|
79 return 0; |
|
80 |
|
81 default: |
|
82 FATAL("res->evsql->type"); |
|
83 } |
|
84 |
|
85 error: |
|
86 return -1; |
|
87 } |
|
88 |
|
89 err_t evsql_result_check (struct evsql_result *res) { |
|
90 // so simple... |
|
91 return res->error ? EIO : 0; |
|
92 } |
|
93 |
|
94 err_t evsql_result_begin (struct evsql_result_info *info, struct evsql_result *res) { |
|
95 struct evsql_item_info *col; |
|
96 size_t cols = 0, nrows; |
|
97 err_t err; |
|
98 |
|
99 // count info columns |
|
100 for (col = info->columns; col->type; col++) |
|
101 cols++; |
|
102 |
|
103 // number of rows returned/affected |
|
104 nrows = evsql_result_rows(res) || evsql_result_affected(res); |
|
105 |
|
106 // did the query fail outright? |
|
107 if (res->error) |
|
108 // dump error message |
|
109 NXERROR(err = EIO, evsql_result_error(res)); |
|
110 |
|
111 /* |
|
112 // SELECT/DELETE/UPDATE WHERE didn't match any rows -> ENOENT |
|
113 if (nrows == 0) |
|
114 XERROR(err = ENOENT, "no rows returned/affected"); |
|
115 */ |
|
116 |
|
117 // correct number of columns |
|
118 if (evsql_result_cols(res) != cols) |
|
119 XERROR(err = EINVAL, "wrong number of columns: %zu, should be %zu", evsql_result_cols(res), cols); |
|
120 |
|
121 // assign |
|
122 res->info = info; |
|
123 res->row_offset = 0; |
|
124 |
|
125 // good |
|
126 return 0; |
|
127 |
|
128 error: |
|
129 return err; |
|
130 |
|
131 } |
|
132 |
|
133 int evsql_result_next (struct evsql_result *res, ...) { |
|
134 va_list vargs; |
|
135 struct evsql_item_info *col; |
|
136 size_t col_idx, row_idx = res->row_offset; |
|
137 err_t err; |
|
138 |
|
139 // ensure that evsql_result_begin has been called |
|
140 assert(res->info); |
|
141 |
|
142 // check if we're past the end |
|
143 if (row_idx >= evsql_result_rows(res)) |
|
144 return 0; |
|
145 |
|
146 // varargs |
|
147 va_start(vargs, res); |
|
148 |
|
149 for (col = res->info->columns, col_idx = 0; col->type; col++, col_idx++) { |
|
150 const char *value = NULL; |
|
151 size_t length = 0; |
|
152 |
|
153 // check for NULLs, then try and get the field value |
|
154 if (evsql_result_null(res, row_idx, col_idx)) { |
|
155 if (!col->flags.null_ok) |
|
156 XERROR(err = EINVAL, "r%zu:c%zu: NULL", row_idx, col_idx); |
|
157 |
|
158 } else if (evsql_result_field(res, row_idx, col_idx, &value, &length)) { |
|
159 SERROR(err = EINVAL); |
|
160 |
|
161 } |
|
162 |
|
163 // read the arg |
|
164 switch (col->type) { |
|
165 case EVSQL_TYPE_BINARY: { |
|
166 struct evsql_item_binary *item_ptr = va_arg(vargs, struct evsql_item_binary *); |
|
167 |
|
168 if (value) { |
|
169 item_ptr->ptr = value; |
|
170 item_ptr->len = length; |
|
171 } |
|
172 } break; |
|
173 |
|
174 case EVSQL_TYPE_STRING: { |
|
175 const char **str_ptr = va_arg(vargs, const char **); |
|
176 |
|
177 if (value) { |
|
178 *str_ptr = value; |
|
179 } |
|
180 |
|
181 } break; |
|
182 |
|
183 case EVSQL_TYPE_UINT16: { |
|
184 uint16_t *uval_ptr = va_arg(vargs, uint16_t *); |
|
185 |
|
186 if (!value) break; |
|
187 |
|
188 if (length != sizeof(uint16_t)) XERROR(err = EINVAL, "r%zu:c%zu: wrong size for uint16_t: %zu", row_idx, col_idx, length); |
|
189 |
|
190 int16_t sval = ntohs(*((int16_t *) value)); |
|
191 |
|
192 if (sval < 0) XERROR(err = ERANGE, "r%zu:c%zu: out of range for uint16_t: %hd", row_idx, col_idx, (signed short) sval); |
|
193 |
|
194 *uval_ptr = sval; |
|
195 } break; |
|
196 |
|
197 case EVSQL_TYPE_UINT32: { |
|
198 uint32_t *uval_ptr = va_arg(vargs, uint32_t *); |
|
199 |
|
200 if (!value) break; |
|
201 |
|
202 if (length != sizeof(uint32_t)) XERROR(err = EINVAL, "r%zu:c%zu: wrong size for uint32_t: %zu", row_idx, col_idx, length); |
|
203 |
|
204 int32_t sval = ntohl(*((int32_t *) value)); |
|
205 |
|
206 if (sval < 0) XERROR(err = ERANGE, "r%zu:c%zu: out of range for uint32_t: %ld", row_idx, col_idx, (signed long) sval); |
|
207 |
|
208 *uval_ptr = sval; |
|
209 } break; |
|
210 |
|
211 case EVSQL_TYPE_UINT64: { |
|
212 uint64_t *uval_ptr = va_arg(vargs, uint64_t *); |
|
213 |
|
214 if (!value) break; |
|
215 |
|
216 if (length != sizeof(uint64_t)) XERROR(err = EINVAL, "r%zu:c%zu: wrong size for uint64_t: %zu", row_idx, col_idx, length); |
|
217 |
|
218 int64_t sval = ntohq(*((int64_t *) value)); |
|
219 |
|
220 if (sval < 0) XERROR(err = ERANGE, "r%zu:c%zu: out of range for uint64_t: %lld", row_idx, col_idx, (signed long long) sval); |
|
221 |
|
222 *uval_ptr = sval; |
|
223 } break; |
|
224 |
|
225 default: |
|
226 XERROR(err = EINVAL, "r%zu:c%zu: invalid type: %d", row_idx, col_idx, col->type); |
|
227 } |
|
228 } |
|
229 |
|
230 // advance row index |
|
231 res->row_offset++; |
|
232 |
|
233 // row handled succesfully |
|
234 return 1; |
|
235 |
|
236 error: |
|
237 return -err; |
|
238 } |
|
239 |
|
240 void evsql_result_end (struct evsql_result *res) { |
|
241 // not much more to it... |
|
242 evsql_result_free(res); |
|
243 } |
|
244 |
|
245 void evsql_result_free (struct evsql_result *res) { |
|
246 // note that the result itself might be NULL... |
|
247 // in the case of internal-error results, these may be free'd multiple times! |
|
248 switch (res->evsql->type) { |
|
249 case EVSQL_EVPQ: |
|
250 if (res->result.pq) |
|
251 return PQclear(res->result.pq); |
|
252 |
|
253 default: |
|
254 FATAL("res->evsql->type"); |
|
255 } |
|
256 } |
|
257 |
|
258 |