Skip to content

Commit 1a4174a

Browse files
committed
Improve the efficiency of certain jsonb get operations.
Instead of iterating over jsonb structures, use the inbuilt functions findJsonbValueFromContainerLen() and getIthJsonbValueFromContainer() to extract values directly. These functions use algorithms that are O(n log n) and O(1) respectively, whereas iterating is O(n), so we should see considerable speedup here. Teodor Sigaev.
1 parent a575098 commit 1a4174a

File tree

1 file changed

+64
-112
lines changed

1 file changed

+64
-112
lines changed

src/backend/utils/adt/jsonfuncs.c

+64-112
Original file line numberDiff line numberDiff line change
@@ -466,12 +466,8 @@ Datum
466466
jsonb_object_field(PG_FUNCTION_ARGS)
467467
{
468468
Jsonb *jb = PG_GETARG_JSONB(0);
469-
char *key = text_to_cstring(PG_GETARG_TEXT_P(1));
470-
int klen = strlen(key);
471-
JsonbIterator *it;
472-
JsonbValue v;
473-
int r;
474-
bool skipNested = false;
469+
text *key = PG_GETARG_TEXT_PP(1);
470+
JsonbValue *v;
475471

476472
if (JB_ROOT_IS_SCALAR(jb))
477473
ereport(ERROR,
@@ -484,25 +480,11 @@ jsonb_object_field(PG_FUNCTION_ARGS)
484480

485481
Assert(JB_ROOT_IS_OBJECT(jb));
486482

487-
it = JsonbIteratorInit(&jb->root);
488-
489-
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
490-
{
491-
skipNested = true;
483+
v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
484+
VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
492485

493-
if (r == WJB_KEY)
494-
{
495-
if (klen == v.val.string.len && strncmp(key, v.val.string.val, klen) == 0)
496-
{
497-
/*
498-
* The next thing the iterator fetches should be the value, no
499-
* matter what shape it is.
500-
*/
501-
(void) JsonbIteratorNext(&it, &v, skipNested);
502-
PG_RETURN_JSONB(JsonbValueToJsonb(&v));
503-
}
504-
}
505-
}
486+
if (v != NULL)
487+
PG_RETURN_JSONB(JsonbValueToJsonb(v));
506488

507489
PG_RETURN_NULL();
508490
}
@@ -527,12 +509,8 @@ Datum
527509
jsonb_object_field_text(PG_FUNCTION_ARGS)
528510
{
529511
Jsonb *jb = PG_GETARG_JSONB(0);
530-
char *key = text_to_cstring(PG_GETARG_TEXT_P(1));
531-
int klen = strlen(key);
532-
JsonbIterator *it;
533-
JsonbValue v;
534-
int r;
535-
bool skipNested = false;
512+
text *key = PG_GETARG_TEXT_PP(1);
513+
JsonbValue *v;
536514

537515
if (JB_ROOT_IS_SCALAR(jb))
538516
ereport(ERROR,
@@ -545,47 +523,41 @@ jsonb_object_field_text(PG_FUNCTION_ARGS)
545523

546524
Assert(JB_ROOT_IS_OBJECT(jb));
547525

548-
it = JsonbIteratorInit(&jb->root);
526+
v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
527+
VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
549528

550-
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
529+
if (v != NULL)
551530
{
552-
skipNested = true;
531+
text *result = NULL;
553532

554-
if (r == WJB_KEY)
533+
switch(v->type)
555534
{
556-
if (klen == v.val.string.len && strncmp(key, v.val.string.val, klen) == 0)
557-
{
558-
text *result;
559-
560-
/*
561-
* The next thing the iterator fetches should be the value, no
562-
* matter what shape it is.
563-
*/
564-
r = JsonbIteratorNext(&it, &v, skipNested);
565-
566-
/*
567-
* if it's a scalar string it needs to be de-escaped,
568-
* otherwise just return the text
569-
*/
570-
if (v.type == jbvString)
535+
case jbvNull:
536+
break;
537+
case jbvBool:
538+
result = cstring_to_text(v->val.boolean ? "true" : "false");
539+
break;
540+
case jbvString:
541+
result = cstring_to_text_with_len(v->val.string.val, v->val.string.len);
542+
break;
543+
case jbvNumeric:
544+
result = cstring_to_text(DatumGetCString(DirectFunctionCall1(numeric_out,
545+
PointerGetDatum(v->val.numeric))));
546+
break;
547+
case jbvBinary:
571548
{
572-
result = cstring_to_text_with_len(v.val.string.val, v.val.string.len);
573-
}
574-
else if (v.type == jbvNull)
575-
{
576-
PG_RETURN_NULL();
577-
}
578-
else
579-
{
580-
StringInfo jtext = makeStringInfo();
581-
Jsonb *tjb = JsonbValueToJsonb(&v);
549+
StringInfo jtext = makeStringInfo();
582550

583-
(void) JsonbToCString(jtext, &tjb->root , -1);
551+
(void) JsonbToCString(jtext, v->val.binary.data, -1);
584552
result = cstring_to_text_with_len(jtext->data, jtext->len);
585553
}
586-
PG_RETURN_TEXT_P(result);
587-
}
554+
break;
555+
default:
556+
elog(ERROR, "Wrong jsonb type: %d", v->type);
588557
}
558+
559+
if (result)
560+
PG_RETURN_TEXT_P(result);
589561
}
590562

591563
PG_RETURN_NULL();
@@ -611,11 +583,7 @@ jsonb_array_element(PG_FUNCTION_ARGS)
611583
{
612584
Jsonb *jb = PG_GETARG_JSONB(0);
613585
int element = PG_GETARG_INT32(1);
614-
JsonbIterator *it;
615-
JsonbValue v;
616-
int r;
617-
bool skipNested = false;
618-
int element_number = 0;
586+
JsonbValue *v;
619587

620588
if (JB_ROOT_IS_SCALAR(jb))
621589
ereport(ERROR,
@@ -628,18 +596,9 @@ jsonb_array_element(PG_FUNCTION_ARGS)
628596

629597
Assert(JB_ROOT_IS_ARRAY(jb));
630598

631-
it = JsonbIteratorInit(&jb->root);
632-
633-
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
634-
{
635-
skipNested = true;
636-
637-
if (r == WJB_ELEM)
638-
{
639-
if (element_number++ == element)
640-
PG_RETURN_JSONB(JsonbValueToJsonb(&v));
641-
}
642-
}
599+
v = getIthJsonbValueFromContainer(&jb->root, element);
600+
if (v != NULL)
601+
PG_RETURN_JSONB(JsonbValueToJsonb(v));
643602

644603
PG_RETURN_NULL();
645604
}
@@ -664,12 +623,7 @@ jsonb_array_element_text(PG_FUNCTION_ARGS)
664623
{
665624
Jsonb *jb = PG_GETARG_JSONB(0);
666625
int element = PG_GETARG_INT32(1);
667-
JsonbIterator *it;
668-
JsonbValue v;
669-
int r;
670-
bool skipNested = false;
671-
int element_number = 0;
672-
626+
JsonbValue *v;
673627

674628
if (JB_ROOT_IS_SCALAR(jb))
675629
ereport(ERROR,
@@ -682,41 +636,39 @@ jsonb_array_element_text(PG_FUNCTION_ARGS)
682636

683637
Assert(JB_ROOT_IS_ARRAY(jb));
684638

685-
it = JsonbIteratorInit(&jb->root);
686-
687-
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
639+
v = getIthJsonbValueFromContainer(&jb->root, element);
640+
if (v != NULL)
688641
{
689-
skipNested = true;
642+
text *result = NULL;
690643

691-
if (r == WJB_ELEM)
644+
switch(v->type)
692645
{
693-
if (element_number++ == element)
694-
{
695-
/*
696-
* if it's a scalar string it needs to be de-escaped,
697-
* otherwise just return the text
698-
*/
699-
text *result;
700-
701-
if (v.type == jbvString)
702-
{
703-
result = cstring_to_text_with_len(v.val.string.val, v.val.string.len);
704-
}
705-
else if (v.type == jbvNull)
706-
{
707-
PG_RETURN_NULL();
708-
}
709-
else
646+
case jbvNull:
647+
break;
648+
case jbvBool:
649+
result = cstring_to_text(v->val.boolean ? "true" : "false");
650+
break;
651+
case jbvString:
652+
result = cstring_to_text_with_len(v->val.string.val, v->val.string.len);
653+
break;
654+
case jbvNumeric:
655+
result = cstring_to_text(DatumGetCString(DirectFunctionCall1(numeric_out,
656+
PointerGetDatum(v->val.numeric))));
657+
break;
658+
case jbvBinary:
710659
{
711-
StringInfo jtext = makeStringInfo();
712-
Jsonb *tjb = JsonbValueToJsonb(&v);
660+
StringInfo jtext = makeStringInfo();
713661

714-
(void) JsonbToCString(jtext, &tjb->root, -1);
662+
(void) JsonbToCString(jtext, v->val.binary.data, -1);
715663
result = cstring_to_text_with_len(jtext->data, jtext->len);
716664
}
717-
PG_RETURN_TEXT_P(result);
718-
}
665+
break;
666+
default:
667+
elog(ERROR, "Wrong jsonb type: %d", v->type);
719668
}
669+
670+
if (result)
671+
PG_RETURN_TEXT_P(result);
720672
}
721673

722674
PG_RETURN_NULL();

0 commit comments

Comments
 (0)