diff options
Diffstat (limited to 'src/pq/pq_result_helper.c')
-rw-r--r-- | src/pq/pq_result_helper.c | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c index f945c5d2e..808445b3b 100644 --- a/src/pq/pq_result_helper.c +++ b/src/pq/pq_result_helper.c | |||
@@ -22,9 +22,12 @@ | |||
22 | * @brief functions to extract result values | 22 | * @brief functions to extract result values |
23 | * @author Christian Grothoff | 23 | * @author Christian Grothoff |
24 | */ | 24 | */ |
25 | #include "gnunet_common.h" | ||
26 | #include "gnunet_time_lib.h" | ||
25 | #include "platform.h" | 27 | #include "platform.h" |
26 | #include "gnunet_util_lib.h" | 28 | #include "gnunet_util_lib.h" |
27 | #include "gnunet_pq_lib.h" | 29 | #include "gnunet_pq_lib.h" |
30 | #include "pq.h" | ||
28 | 31 | ||
29 | 32 | ||
30 | struct GNUNET_PQ_ResultSpec | 33 | struct GNUNET_PQ_ResultSpec |
@@ -1117,4 +1120,530 @@ GNUNET_PQ_result_spec_uint64 (const char *name, | |||
1117 | } | 1120 | } |
1118 | 1121 | ||
1119 | 1122 | ||
1123 | /** | ||
1124 | * Closure for the array result specifications. Contains type information | ||
1125 | * for the generic parser extract_array_generic and out-pointers for the results. | ||
1126 | */ | ||
1127 | struct array_result_cls | ||
1128 | { | ||
1129 | /* Oid of the expected type, must match the oid in the header of the PQResult struct */ | ||
1130 | Oid oid; | ||
1131 | |||
1132 | /* Target type */ | ||
1133 | enum array_types typ; | ||
1134 | |||
1135 | /* If not 0, defines the expected size of each entry */ | ||
1136 | size_t same_size; | ||
1137 | |||
1138 | /* Out-pointer to write the number of elements in the array */ | ||
1139 | size_t *num; | ||
1140 | |||
1141 | /* Out-pointer. If @a typ is array_of_byte and @a same_size is 0, | ||
1142 | * allocate and put the array of @a num sizes here. NULL otherwise */ | ||
1143 | size_t **sizes; | ||
1144 | }; | ||
1145 | |||
1146 | |||
1147 | /** | ||
1148 | * Extract data from a Postgres database @a result as array of a specific type | ||
1149 | * from row @a row. The type information and optionally additional | ||
1150 | * out-parameters are given in @a cls which is of type array_result_cls. | ||
1151 | * | ||
1152 | * @param cls closure of type array_result_cls | ||
1153 | * @param result where to extract data from | ||
1154 | * @param row row to extract data from | ||
1155 | * @param fname name (or prefix) of the fields to extract from | ||
1156 | * @param[in,out] dst_size where to store size of result, may be NULL | ||
1157 | * @param[out] dst where to store the result | ||
1158 | * @return | ||
1159 | * #GNUNET_YES if all results could be extracted | ||
1160 | * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) | ||
1161 | */ | ||
1162 | static enum GNUNET_GenericReturnValue | ||
1163 | extract_array_generic ( | ||
1164 | void *cls, | ||
1165 | PGresult *result, | ||
1166 | int row, | ||
1167 | const char *fname, | ||
1168 | size_t *dst_size, | ||
1169 | void *dst) | ||
1170 | { | ||
1171 | const struct array_result_cls *info = cls; | ||
1172 | int data_sz; | ||
1173 | char *data; | ||
1174 | void *out = NULL; | ||
1175 | struct pq_array_header header; | ||
1176 | int col_num; | ||
1177 | |||
1178 | GNUNET_assert (NULL != dst); | ||
1179 | *((void **) dst) = NULL; | ||
1180 | |||
1181 | #define FAIL_IF(cond) \ | ||
1182 | do { \ | ||
1183 | if ((cond)) \ | ||
1184 | { \ | ||
1185 | GNUNET_break (! (cond)); \ | ||
1186 | goto FAIL; \ | ||
1187 | } \ | ||
1188 | } while(0) | ||
1189 | |||
1190 | col_num = PQfnumber (result, fname); | ||
1191 | FAIL_IF (0 > col_num); | ||
1192 | |||
1193 | data_sz = PQgetlength (result, row, col_num); | ||
1194 | FAIL_IF (0 > data_sz); | ||
1195 | FAIL_IF (sizeof(header) > (size_t) data_sz); | ||
1196 | |||
1197 | data = PQgetvalue (result, row, col_num); | ||
1198 | FAIL_IF (NULL == data); | ||
1199 | |||
1200 | { | ||
1201 | struct pq_array_header *h = | ||
1202 | (struct pq_array_header *) data; | ||
1203 | |||
1204 | header.ndim = ntohl (h->ndim); | ||
1205 | header.has_null = ntohl (h->has_null); | ||
1206 | header.oid = ntohl (h->oid); | ||
1207 | header.dim = ntohl (h->dim); | ||
1208 | header.lbound = ntohl (h->lbound); | ||
1209 | |||
1210 | FAIL_IF (1 != header.ndim); | ||
1211 | FAIL_IF ((0 > header.dim) || (INT_MAX == header.dim)); | ||
1212 | FAIL_IF (0 != header.has_null); | ||
1213 | FAIL_IF (1 != header.lbound); | ||
1214 | FAIL_IF (info->oid != header.oid); | ||
1215 | } | ||
1216 | |||
1217 | *info->num = header.dim; | ||
1218 | switch (info->typ) | ||
1219 | { | ||
1220 | case array_of_bool: | ||
1221 | if (NULL != dst_size) | ||
1222 | *dst_size = sizeof(bool) * (*info->num); | ||
1223 | out = GNUNET_new_array (*info->num, bool); | ||
1224 | break; | ||
1225 | case array_of_uint16: | ||
1226 | if (NULL != dst_size) | ||
1227 | *dst_size = sizeof(uint16_t) * (*info->num); | ||
1228 | out = GNUNET_new_array (*info->num, uint16_t); | ||
1229 | break; | ||
1230 | case array_of_uint32: | ||
1231 | if (NULL != dst_size) | ||
1232 | *dst_size = sizeof(uint32_t) * (*info->num); | ||
1233 | out = GNUNET_new_array (*info->num, uint32_t); | ||
1234 | break; | ||
1235 | case array_of_uint64: | ||
1236 | if (NULL != dst_size) | ||
1237 | *dst_size = sizeof(uint64_t) * (*info->num); | ||
1238 | out = GNUNET_new_array (*info->num, uint64_t); | ||
1239 | break; | ||
1240 | case array_of_abs_time: | ||
1241 | if (NULL != dst_size) | ||
1242 | *dst_size = sizeof(struct GNUNET_TIME_Absolute) * (*info->num); | ||
1243 | out = GNUNET_new_array (*info->num, struct GNUNET_TIME_Absolute); | ||
1244 | break; | ||
1245 | case array_of_rel_time: | ||
1246 | if (NULL != dst_size) | ||
1247 | *dst_size = sizeof(struct GNUNET_TIME_Relative) * (*info->num); | ||
1248 | out = GNUNET_new_array (*info->num, struct GNUNET_TIME_Relative); | ||
1249 | break; | ||
1250 | case array_of_timestamp: | ||
1251 | if (NULL != dst_size) | ||
1252 | *dst_size = sizeof(struct GNUNET_TIME_Timestamp) * (*info->num); | ||
1253 | out = GNUNET_new_array (*info->num, struct GNUNET_TIME_Timestamp); | ||
1254 | break; | ||
1255 | case array_of_byte: | ||
1256 | if (0 == info->same_size) | ||
1257 | *info->sizes = GNUNET_new_array (header.dim, size_t); | ||
1258 | /* fallthrough */ | ||
1259 | case array_of_string: | ||
1260 | { | ||
1261 | size_t total = 0; | ||
1262 | bool is_string = (array_of_string == info->typ); | ||
1263 | |||
1264 | /* first, calculate total size required for allocation */ | ||
1265 | { | ||
1266 | char *ptr = data + sizeof(header); | ||
1267 | for (uint32_t i = 0; i < header.dim; i++) | ||
1268 | { | ||
1269 | uint32_t sz; | ||
1270 | |||
1271 | sz = ntohl (*(uint32_t *) ptr); | ||
1272 | sz += is_string ? 1 : 0; | ||
1273 | total += sz; | ||
1274 | ptr += sizeof(uint32_t); | ||
1275 | ptr += sz; | ||
1276 | |||
1277 | if ((! is_string) && | ||
1278 | (0 == info->same_size)) | ||
1279 | (*info->sizes)[i] = sz; | ||
1280 | |||
1281 | FAIL_IF ((0 != info->same_size) && | ||
1282 | (sz != info->same_size)); | ||
1283 | FAIL_IF (total < sz); | ||
1284 | } | ||
1285 | } | ||
1286 | |||
1287 | if (NULL != dst_size) | ||
1288 | *dst_size = total; | ||
1289 | |||
1290 | if (0 < total) | ||
1291 | out = GNUNET_malloc (total); | ||
1292 | |||
1293 | break; | ||
1294 | } | ||
1295 | default: | ||
1296 | FAIL_IF (1 != 0); | ||
1297 | } | ||
1298 | |||
1299 | *((void **) dst) = out; | ||
1300 | |||
1301 | /* copy data */ | ||
1302 | { | ||
1303 | char *in = data + sizeof(header); | ||
1304 | |||
1305 | for (uint32_t i = 0; i < header.dim; i++) | ||
1306 | { | ||
1307 | size_t sz = ntohl (*(uint32_t *) in); | ||
1308 | in += sizeof(uint32_t); | ||
1309 | |||
1310 | switch (info->typ) | ||
1311 | { | ||
1312 | case array_of_bool: | ||
1313 | FAIL_IF (sz != sizeof(bool)); | ||
1314 | *(bool *) out = *(bool *) in; | ||
1315 | break; | ||
1316 | case array_of_uint16: | ||
1317 | FAIL_IF (sz != sizeof(uint16_t)); | ||
1318 | *(uint16_t *) out = ntohs (*(uint16_t *) in); | ||
1319 | break; | ||
1320 | case array_of_uint32: | ||
1321 | FAIL_IF (sz != sizeof(uint32_t)); | ||
1322 | *(uint32_t *) out = ntohl (*(uint32_t *) in); | ||
1323 | break; | ||
1324 | case array_of_uint64: | ||
1325 | FAIL_IF (sz != sizeof(uint64_t)); | ||
1326 | *(uint64_t *) out = GNUNET_ntohll (*(uint64_t *) in); | ||
1327 | break; | ||
1328 | case array_of_abs_time: | ||
1329 | case array_of_rel_time: | ||
1330 | case array_of_timestamp: | ||
1331 | FAIL_IF (sz != sizeof(uint64_t)); | ||
1332 | { | ||
1333 | uint64_t val = GNUNET_ntohll (*(uint64_t *) in); | ||
1334 | switch (info->typ) | ||
1335 | { | ||
1336 | case array_of_abs_time: | ||
1337 | ((struct GNUNET_TIME_Absolute *) out)->abs_value_us = val; | ||
1338 | break; | ||
1339 | case array_of_rel_time: | ||
1340 | ((struct GNUNET_TIME_Relative *) out)->rel_value_us = val; | ||
1341 | break; | ||
1342 | case array_of_timestamp: | ||
1343 | ((struct GNUNET_TIME_Timestamp *) out)->abs_time.abs_value_us = val; | ||
1344 | break; | ||
1345 | default: | ||
1346 | FAIL_IF (1 != 0); | ||
1347 | } | ||
1348 | } | ||
1349 | break; | ||
1350 | case array_of_byte: | ||
1351 | case array_of_string: | ||
1352 | GNUNET_memcpy (out, in, sz); | ||
1353 | break; | ||
1354 | default: | ||
1355 | FAIL_IF (1 != 0); | ||
1356 | } | ||
1357 | |||
1358 | in += sz; | ||
1359 | out += sz; | ||
1360 | out += (array_of_string == info->typ) ? 1 : 0; | ||
1361 | } | ||
1362 | } | ||
1363 | |||
1364 | return GNUNET_OK; | ||
1365 | |||
1366 | FAIL: | ||
1367 | GNUNET_free (*(void **) dst); | ||
1368 | return GNUNET_SYSERR; | ||
1369 | #undef FAIL_IF | ||
1370 | } | ||
1371 | |||
1372 | |||
1373 | /** | ||
1374 | * Cleanup of the data and closure of an array spec. | ||
1375 | */ | ||
1376 | static void | ||
1377 | array_cleanup (void *cls, | ||
1378 | void *rd) | ||
1379 | { | ||
1380 | |||
1381 | struct array_result_cls *info = cls; | ||
1382 | void **dst = rd; | ||
1383 | |||
1384 | if ((array_of_byte == info->typ) && | ||
1385 | (0 == info->same_size) && | ||
1386 | (NULL != info->sizes)) | ||
1387 | GNUNET_free (*(info->sizes)); | ||
1388 | |||
1389 | GNUNET_free (cls); | ||
1390 | GNUNET_free (*dst); | ||
1391 | *dst = NULL; | ||
1392 | } | ||
1393 | |||
1394 | |||
1395 | struct GNUNET_PQ_ResultSpec | ||
1396 | GNUNET_PQ_result_spec_array_bool ( | ||
1397 | const struct GNUNET_PQ_Context *db, | ||
1398 | const char *name, | ||
1399 | size_t *num, | ||
1400 | bool **dst) | ||
1401 | { | ||
1402 | struct array_result_cls *info = | ||
1403 | GNUNET_new (struct array_result_cls); | ||
1404 | |||
1405 | info->num = num; | ||
1406 | info->typ = array_of_bool; | ||
1407 | info->oid = db->oids[GNUNET_PQ_DATATYPE_BOOL]; | ||
1408 | |||
1409 | struct GNUNET_PQ_ResultSpec res = { | ||
1410 | .conv = extract_array_generic, | ||
1411 | .cleaner = array_cleanup, | ||
1412 | .dst = (void *) dst, | ||
1413 | .fname = name, | ||
1414 | .cls = info | ||
1415 | }; | ||
1416 | return res; | ||
1417 | } | ||
1418 | |||
1419 | |||
1420 | struct GNUNET_PQ_ResultSpec | ||
1421 | GNUNET_PQ_result_spec_array_uint16 ( | ||
1422 | const struct GNUNET_PQ_Context *db, | ||
1423 | const char *name, | ||
1424 | size_t *num, | ||
1425 | uint16_t **dst) | ||
1426 | { | ||
1427 | struct array_result_cls *info = | ||
1428 | GNUNET_new (struct array_result_cls); | ||
1429 | |||
1430 | info->num = num; | ||
1431 | info->typ = array_of_uint16; | ||
1432 | info->oid = db->oids[GNUNET_PQ_DATATYPE_INT2]; | ||
1433 | |||
1434 | struct GNUNET_PQ_ResultSpec res = { | ||
1435 | .conv = extract_array_generic, | ||
1436 | .cleaner = array_cleanup, | ||
1437 | .dst = (void *) dst, | ||
1438 | .fname = name, | ||
1439 | .cls = info | ||
1440 | }; | ||
1441 | return res; | ||
1442 | } | ||
1443 | |||
1444 | |||
1445 | struct GNUNET_PQ_ResultSpec | ||
1446 | GNUNET_PQ_result_spec_array_uint32 ( | ||
1447 | const struct GNUNET_PQ_Context *db, | ||
1448 | const char *name, | ||
1449 | size_t *num, | ||
1450 | uint32_t **dst) | ||
1451 | { | ||
1452 | struct array_result_cls *info = | ||
1453 | GNUNET_new (struct array_result_cls); | ||
1454 | |||
1455 | info->num = num; | ||
1456 | info->typ = array_of_uint32; | ||
1457 | info->oid = db->oids[GNUNET_PQ_DATATYPE_INT4]; | ||
1458 | |||
1459 | struct GNUNET_PQ_ResultSpec res = { | ||
1460 | .conv = extract_array_generic, | ||
1461 | .cleaner = array_cleanup, | ||
1462 | .dst = (void *) dst, | ||
1463 | .fname = name, | ||
1464 | .cls = info | ||
1465 | }; | ||
1466 | return res; | ||
1467 | } | ||
1468 | |||
1469 | |||
1470 | struct GNUNET_PQ_ResultSpec | ||
1471 | GNUNET_PQ_result_spec_array_uint64 ( | ||
1472 | const struct GNUNET_PQ_Context *db, | ||
1473 | const char *name, | ||
1474 | size_t *num, | ||
1475 | uint64_t **dst) | ||
1476 | { | ||
1477 | struct array_result_cls *info = | ||
1478 | GNUNET_new (struct array_result_cls); | ||
1479 | |||
1480 | info->num = num; | ||
1481 | info->typ = array_of_uint64; | ||
1482 | info->oid = db->oids[GNUNET_PQ_DATATYPE_INT8]; | ||
1483 | |||
1484 | struct GNUNET_PQ_ResultSpec res = { | ||
1485 | .conv = extract_array_generic, | ||
1486 | .cleaner = array_cleanup, | ||
1487 | .dst = (void *) dst, | ||
1488 | .fname = name, | ||
1489 | .cls = info | ||
1490 | }; | ||
1491 | return res; | ||
1492 | } | ||
1493 | |||
1494 | |||
1495 | struct GNUNET_PQ_ResultSpec | ||
1496 | GNUNET_PQ_result_spec_array_abs_time ( | ||
1497 | const struct GNUNET_PQ_Context *db, | ||
1498 | const char *name, | ||
1499 | size_t *num, | ||
1500 | struct GNUNET_TIME_Absolute **dst) | ||
1501 | { | ||
1502 | struct array_result_cls *info = | ||
1503 | GNUNET_new (struct array_result_cls); | ||
1504 | |||
1505 | info->num = num; | ||
1506 | info->typ = array_of_abs_time; | ||
1507 | info->oid = db->oids[GNUNET_PQ_DATATYPE_INT8]; | ||
1508 | |||
1509 | struct GNUNET_PQ_ResultSpec res = { | ||
1510 | .conv = extract_array_generic, | ||
1511 | .cleaner = array_cleanup, | ||
1512 | .dst = (void *) dst, | ||
1513 | .fname = name, | ||
1514 | .cls = info | ||
1515 | }; | ||
1516 | return res; | ||
1517 | } | ||
1518 | |||
1519 | |||
1520 | struct GNUNET_PQ_ResultSpec | ||
1521 | GNUNET_PQ_result_spec_array_rel_time ( | ||
1522 | const struct GNUNET_PQ_Context *db, | ||
1523 | const char *name, | ||
1524 | size_t *num, | ||
1525 | struct GNUNET_TIME_Relative **dst) | ||
1526 | { | ||
1527 | struct array_result_cls *info = | ||
1528 | GNUNET_new (struct array_result_cls); | ||
1529 | |||
1530 | info->num = num; | ||
1531 | info->typ = array_of_rel_time; | ||
1532 | info->oid = db->oids[GNUNET_PQ_DATATYPE_INT8]; | ||
1533 | |||
1534 | struct GNUNET_PQ_ResultSpec res = { | ||
1535 | .conv = extract_array_generic, | ||
1536 | .cleaner = array_cleanup, | ||
1537 | .dst = (void *) dst, | ||
1538 | .fname = name, | ||
1539 | .cls = info | ||
1540 | }; | ||
1541 | return res; | ||
1542 | } | ||
1543 | |||
1544 | |||
1545 | struct GNUNET_PQ_ResultSpec | ||
1546 | GNUNET_PQ_result_spec_array_timestamp ( | ||
1547 | const struct GNUNET_PQ_Context *db, | ||
1548 | const char *name, | ||
1549 | size_t *num, | ||
1550 | struct GNUNET_TIME_Timestamp **dst) | ||
1551 | { | ||
1552 | struct array_result_cls *info = | ||
1553 | GNUNET_new (struct array_result_cls); | ||
1554 | |||
1555 | info->num = num; | ||
1556 | info->typ = array_of_timestamp; | ||
1557 | info->oid = db->oids[GNUNET_PQ_DATATYPE_INT8]; | ||
1558 | |||
1559 | struct GNUNET_PQ_ResultSpec res = { | ||
1560 | .conv = extract_array_generic, | ||
1561 | .cleaner = array_cleanup, | ||
1562 | .dst = (void *) dst, | ||
1563 | .fname = name, | ||
1564 | .cls = info | ||
1565 | }; | ||
1566 | return res; | ||
1567 | } | ||
1568 | |||
1569 | |||
1570 | struct GNUNET_PQ_ResultSpec | ||
1571 | GNUNET_PQ_result_spec_array_variable_size ( | ||
1572 | const struct GNUNET_PQ_Context *db, | ||
1573 | const char *name, | ||
1574 | size_t *num, | ||
1575 | size_t **sizes, | ||
1576 | void **dst) | ||
1577 | { | ||
1578 | struct array_result_cls *info = | ||
1579 | GNUNET_new (struct array_result_cls); | ||
1580 | |||
1581 | info->num = num; | ||
1582 | info->sizes = sizes; | ||
1583 | info->typ = array_of_byte; | ||
1584 | info->oid = db->oids[GNUNET_PQ_DATATYPE_BYTEA]; | ||
1585 | |||
1586 | struct GNUNET_PQ_ResultSpec res = { | ||
1587 | .conv = extract_array_generic, | ||
1588 | .cleaner = array_cleanup, | ||
1589 | .dst = (void *) dst, | ||
1590 | .fname = name, | ||
1591 | .cls = info | ||
1592 | }; | ||
1593 | return res; | ||
1594 | } | ||
1595 | |||
1596 | |||
1597 | struct GNUNET_PQ_ResultSpec | ||
1598 | GNUNET_PQ_result_spec_array_fixed_size ( | ||
1599 | const struct GNUNET_PQ_Context *db, | ||
1600 | const char *name, | ||
1601 | size_t size, | ||
1602 | size_t *num, | ||
1603 | void **dst) | ||
1604 | { | ||
1605 | struct array_result_cls *info = | ||
1606 | GNUNET_new (struct array_result_cls); | ||
1607 | |||
1608 | info->num = num; | ||
1609 | info->same_size = size; | ||
1610 | info->typ = array_of_byte; | ||
1611 | info->oid = db->oids[GNUNET_PQ_DATATYPE_BYTEA]; | ||
1612 | |||
1613 | struct GNUNET_PQ_ResultSpec res = { | ||
1614 | .conv = extract_array_generic, | ||
1615 | .cleaner = array_cleanup, | ||
1616 | .dst = (void *) dst, | ||
1617 | .fname = name, | ||
1618 | .cls = info | ||
1619 | }; | ||
1620 | return res; | ||
1621 | } | ||
1622 | |||
1623 | |||
1624 | struct GNUNET_PQ_ResultSpec | ||
1625 | GNUNET_PQ_result_spec_array_string ( | ||
1626 | const struct GNUNET_PQ_Context *db, | ||
1627 | const char *name, | ||
1628 | size_t *num, | ||
1629 | char **dst) | ||
1630 | { | ||
1631 | struct array_result_cls *info = | ||
1632 | GNUNET_new (struct array_result_cls); | ||
1633 | |||
1634 | info->num = num; | ||
1635 | info->typ = array_of_string; | ||
1636 | info->oid = db->oids[GNUNET_PQ_DATATYPE_VARCHAR]; | ||
1637 | |||
1638 | struct GNUNET_PQ_ResultSpec res = { | ||
1639 | .conv = extract_array_generic, | ||
1640 | .cleaner = array_cleanup, | ||
1641 | .dst = (void *) dst, | ||
1642 | .fname = name, | ||
1643 | .cls = info | ||
1644 | }; | ||
1645 | return res; | ||
1646 | } | ||
1647 | |||
1648 | |||
1120 | /* end of pq_result_helper.c */ | 1649 | /* end of pq_result_helper.c */ |