|
12 | 12 | #include "libpq/pqformat.h" |
13 | 13 | #include "utils/builtins.h" |
14 | 14 | #include "utils/json.h" |
| 15 | +#include "utils/jsonb.h" |
15 | 16 | #include "utils/lsyscache.h" |
16 | 17 | #include "utils/memutils.h" |
17 | 18 | #include "utils/typcache.h" |
@@ -1374,3 +1375,167 @@ hstore_to_json(PG_FUNCTION_ARGS) |
1374 | 1375 |
|
1375 | 1376 | PG_RETURN_TEXT_P(cstring_to_text(dst.data)); |
1376 | 1377 | } |
| 1378 | + |
| 1379 | +PG_FUNCTION_INFO_V1(hstore_to_jsonb); |
| 1380 | +Datum hstore_to_jsonb(PG_FUNCTION_ARGS); |
| 1381 | +Datum |
| 1382 | +hstore_to_jsonb(PG_FUNCTION_ARGS) |
| 1383 | +{ |
| 1384 | + HStore *in = PG_GETARG_HS(0); |
| 1385 | + int i; |
| 1386 | + int count = HS_COUNT(in); |
| 1387 | + char *base = STRPTR(in); |
| 1388 | + HEntry *entries = ARRPTR(in); |
| 1389 | + JsonbParseState *state = NULL; |
| 1390 | + JsonbValue *res; |
| 1391 | + |
| 1392 | + res = pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); |
| 1393 | + |
| 1394 | + for (i = 0; i < count; i++) |
| 1395 | + { |
| 1396 | + JsonbValue key, val; |
| 1397 | + |
| 1398 | + key.estSize = sizeof(JEntry); |
| 1399 | + key.type = jbvString; |
| 1400 | + key.string.len = HS_KEYLEN(entries, i); |
| 1401 | + key.string.val = pnstrdup(HS_KEY(entries, base, i), key.string.len); |
| 1402 | + key.estSize += key.string.len; |
| 1403 | + |
| 1404 | + res = pushJsonbValue(&state, WJB_KEY, &key); |
| 1405 | + |
| 1406 | + if (HS_VALISNULL(entries, i)) |
| 1407 | + { |
| 1408 | + val.estSize = sizeof(JEntry); |
| 1409 | + val.type = jbvNull; |
| 1410 | + } |
| 1411 | + else |
| 1412 | + { |
| 1413 | + val.estSize = sizeof(JEntry); |
| 1414 | + val.type = jbvString; |
| 1415 | + val.string.len = HS_VALLEN(entries, i); |
| 1416 | + val.string.val = pnstrdup(HS_VAL(entries, base, i), val.string.len); |
| 1417 | + val.estSize += val.string.len; |
| 1418 | + } |
| 1419 | + res = pushJsonbValue(&state, WJB_VALUE, &val); |
| 1420 | + } |
| 1421 | + |
| 1422 | + res = pushJsonbValue(&state, WJB_END_OBJECT, NULL); |
| 1423 | + |
| 1424 | + PG_RETURN_POINTER(JsonbValueToJsonb(res)); |
| 1425 | +} |
| 1426 | + |
| 1427 | +PG_FUNCTION_INFO_V1(hstore_to_jsonb_loose); |
| 1428 | +Datum hstore_to_jsonb_loose(PG_FUNCTION_ARGS); |
| 1429 | +Datum |
| 1430 | +hstore_to_jsonb_loose(PG_FUNCTION_ARGS) |
| 1431 | +{ |
| 1432 | + HStore *in = PG_GETARG_HS(0); |
| 1433 | + int i; |
| 1434 | + int count = HS_COUNT(in); |
| 1435 | + char *base = STRPTR(in); |
| 1436 | + HEntry *entries = ARRPTR(in); |
| 1437 | + JsonbParseState *state = NULL; |
| 1438 | + JsonbValue *res; |
| 1439 | + StringInfoData tmp; |
| 1440 | + bool is_number; |
| 1441 | + |
| 1442 | + initStringInfo(&tmp); |
| 1443 | + |
| 1444 | + res = pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); |
| 1445 | + |
| 1446 | + for (i = 0; i < count; i++) |
| 1447 | + { |
| 1448 | + JsonbValue key, val; |
| 1449 | + |
| 1450 | + key.estSize = sizeof(JEntry); |
| 1451 | + key.type = jbvString; |
| 1452 | + key.string.len = HS_KEYLEN(entries, i); |
| 1453 | + key.string.val = pnstrdup(HS_KEY(entries, base, i), key.string.len); |
| 1454 | + key.estSize += key.string.len; |
| 1455 | + |
| 1456 | + res = pushJsonbValue(&state, WJB_KEY, &key); |
| 1457 | + |
| 1458 | + val.estSize = sizeof(JEntry); |
| 1459 | + |
| 1460 | + if (HS_VALISNULL(entries, i)) |
| 1461 | + { |
| 1462 | + val.type = jbvNull; |
| 1463 | + } |
| 1464 | + /* guess that values of 't' or 'f' are booleans */ |
| 1465 | + else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't') |
| 1466 | + { |
| 1467 | + val.type = jbvBool; |
| 1468 | + val.boolean = true; |
| 1469 | + } |
| 1470 | + else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f') |
| 1471 | + { |
| 1472 | + val.type = jbvBool; |
| 1473 | + val.boolean = false; |
| 1474 | + } |
| 1475 | + else |
| 1476 | + { |
| 1477 | + is_number = false; |
| 1478 | + resetStringInfo(&tmp); |
| 1479 | + |
| 1480 | + appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); |
| 1481 | + |
| 1482 | + /* |
| 1483 | + * don't treat something with a leading zero followed by another |
| 1484 | + * digit as numeric - could be a zip code or similar |
| 1485 | + */ |
| 1486 | + if (tmp.len > 0 && |
| 1487 | + !(tmp.data[0] == '0' && |
| 1488 | + isdigit((unsigned char) tmp.data[1])) && |
| 1489 | + strspn(tmp.data, "+-0123456789Ee.") == tmp.len) |
| 1490 | + { |
| 1491 | + /* |
| 1492 | + * might be a number. See if we can input it as a numeric |
| 1493 | + * value. Ignore any actual parsed value. |
| 1494 | + */ |
| 1495 | + char *endptr = "junk"; |
| 1496 | + long lval; |
| 1497 | + |
| 1498 | + lval = strtol(tmp.data, &endptr, 10); |
| 1499 | + (void) lval; |
| 1500 | + if (*endptr == '\0') |
| 1501 | + { |
| 1502 | + /* |
| 1503 | + * strol man page says this means the whole string is |
| 1504 | + * valid |
| 1505 | + */ |
| 1506 | + is_number = true; |
| 1507 | + } |
| 1508 | + else |
| 1509 | + { |
| 1510 | + /* not an int - try a double */ |
| 1511 | + double dval; |
| 1512 | + |
| 1513 | + dval = strtod(tmp.data, &endptr); |
| 1514 | + (void) dval; |
| 1515 | + if (*endptr == '\0') |
| 1516 | + is_number = true; |
| 1517 | + } |
| 1518 | + } |
| 1519 | + if (is_number) |
| 1520 | + { |
| 1521 | + val.type = jbvNumeric; |
| 1522 | + val.numeric = DatumGetNumeric( |
| 1523 | + DirectFunctionCall3(numeric_in, CStringGetDatum(tmp.data), 0, -1)); |
| 1524 | + val.estSize += VARSIZE_ANY(val.numeric) +sizeof(JEntry); |
| 1525 | + } |
| 1526 | + else |
| 1527 | + { |
| 1528 | + val.estSize = sizeof(JEntry); |
| 1529 | + val.type = jbvString; |
| 1530 | + val.string.len = HS_VALLEN(entries, i); |
| 1531 | + val.string.val = pnstrdup(HS_VAL(entries, base, i), val.string.len); |
| 1532 | + val.estSize += val.string.len; |
| 1533 | + } |
| 1534 | + } |
| 1535 | + res = pushJsonbValue(&state, WJB_VALUE, &val); |
| 1536 | + } |
| 1537 | + |
| 1538 | + res = pushJsonbValue(&state, WJB_END_OBJECT, NULL); |
| 1539 | + |
| 1540 | + PG_RETURN_POINTER(JsonbValueToJsonb(res)); |
| 1541 | +} |
0 commit comments