mirror of
https://github.com/postgres/postgres.git
synced 2026-04-28 17:49:35 -04:00
Add additional jsonpath string methods
Add the following jsonpath methods: * l/r/btrim() * lower(), upper() * initcap() * replace() * split_part() Each simply dispatches to the standard string processing functions. These depend on the locale, but since it's set at `initdb`, they can be considered immutable and therefore allowed in any jsonpath expression. Author: Florents Tselai <florents.tselai@gmail.com> Co-authored-by: David E. Wheeler <david@justatheory.com> Reviewed-by: Chao Li <li.evan.chao@gmail.com> Reviewed-by: Andrew Dunstan <andrew@dunslane.net> Discussion: https://postgr.es/m/CA+v5N40sJF39m0v7h=QN86zGp0CUf9F1WKasnZy9nNVj_VhCZQ@mail.gmail.com
This commit is contained in:
parent
a35c9d524e
commit
bd4f879a9c
12 changed files with 1212 additions and 7 deletions
|
|
@ -2781,6 +2781,146 @@ ERROR: jsonpath member accessor can only be applied to an object
|
|||
<returnvalue>[{"id": 0, "key": "x", "value": "20"}, {"id": 0, "key": "y", "value": 32}]</returnvalue>
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<replaceable>string</replaceable> <literal>.</literal> <literal>lower()</literal>
|
||||
<returnvalue><replaceable>string</replaceable></returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
String converted to all lower case according to the rules of the database's locale.
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('"TOM"', '$.lower()')</literal>
|
||||
<returnvalue>"tom"</returnvalue>
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<replaceable>string</replaceable> <literal>.</literal> <literal>upper()</literal>
|
||||
<returnvalue><replaceable>string</replaceable></returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
String converted to all upper case according to the rules of the database's locale.
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('"tom"', '$.upper()')</literal>
|
||||
<returnvalue>"TOM"</returnvalue>
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<replaceable>string</replaceable> <literal>.</literal> <literal>initcap()</literal>
|
||||
<returnvalue><replaceable>string</replaceable></returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
String with the first letter of each word converted to upper case
|
||||
according to the rules of the database's locale. Words are sequences
|
||||
of alphanumeric characters separated by non-alphanumeric characters.
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('"hi THOMAS"', '$.initcap()')</literal>
|
||||
<returnvalue>"Hi Thomas"</returnvalue>
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<replaceable>string</replaceable> <literal>.</literal> <literal>replace(<replaceable>from</replaceable>, <replaceable>to</replaceable>)</literal>
|
||||
<returnvalue><replaceable>string</replaceable></returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
String with all occurrences of substring from replaced with substring to.
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('"abcdefabcdef"', '$.replace("cd", "XX")')</literal>
|
||||
<returnvalue>"abXXefabXXef"</returnvalue>
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<replaceable>string</replaceable> <literal>.</literal> <literal>split_part(<replaceable>delimiter</replaceable>, <replaceable>n</replaceable>)</literal>
|
||||
<returnvalue><replaceable>string</replaceable></returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
String split at occurrences of <replaceable>delimiter</replaceable>
|
||||
and returns the <replaceable>n</replaceable>'th field (counting from
|
||||
one) or, when <replaceable>n</replaceable> is negative, returns the
|
||||
|<replaceable>n</replaceable>|'th-from-last field.
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('"abc~@~def~@~ghi"', '$.split_part("~@~", 2)')</literal>
|
||||
<returnvalue>"def"</returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('"abc,def,ghi,jkl"', '$.split_part(",", 3)')</literal>
|
||||
<returnvalue>"ghi"</returnvalue>
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<replaceable>string</replaceable> <literal>.</literal> <literal>ltrim([ <replaceable>characters</replaceable> ])</literal>
|
||||
<returnvalue><replaceable>string</replaceable></returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
String with the longest string containing only spaces or the
|
||||
characters in <replaceable>characters</replaceable> removed from the
|
||||
start of <replaceable>string</replaceable>
|
||||
</para>
|
||||
<para>
|
||||
<literal> jsonb_path_query('" hello"', '$.ltrim()')</literal>
|
||||
<returnvalue>"hello"</returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('"zzzytest"', '$.ltrim("xyz")')</literal>
|
||||
<returnvalue>"test"</returnvalue>
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<replaceable>string</replaceable> <literal>.</literal> <literal>rtrim([ <replaceable>characters</replaceable> ])</literal>
|
||||
<returnvalue><replaceable>string</replaceable></returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
String with the longest string containing only spaces or the
|
||||
characters in <replaceable>characters</replaceable> removed from the
|
||||
end of <replaceable>string</replaceable>
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('"hello "', '$.rtrim()')</literal>
|
||||
<returnvalue>"hello"</returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('"testxxzx"', '$.rtrim("xyz")')</literal>
|
||||
<returnvalue>"test"</returnvalue>
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<replaceable>string</replaceable> <literal>.</literal> <literal>btrim([ <replaceable>characters</replaceable> ])</literal>
|
||||
<returnvalue><replaceable>string</replaceable></returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
String with the longest string containing only spaces or the
|
||||
characters in <replaceable>characters</replaceable> removed from the
|
||||
start and end of <replaceable>string</replaceable>
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('" hello "', '$.btrim()')</literal>
|
||||
<returnvalue>"hello"</returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
<literal>jsonb_path_query('"xyxtrimyyx"', '$.btrim("xyz")')</literal>
|
||||
<returnvalue>"trim"</returnvalue>
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -298,6 +298,8 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
|
|||
case jpiMod:
|
||||
case jpiStartsWith:
|
||||
case jpiDecimal:
|
||||
case jpiStrReplace:
|
||||
case jpiStrSplitPart:
|
||||
{
|
||||
/*
|
||||
* First, reserve place for left/right arg's positions, then
|
||||
|
|
@ -362,6 +364,9 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
|
|||
case jpiTimeTz:
|
||||
case jpiTimestamp:
|
||||
case jpiTimestampTz:
|
||||
case jpiStrLtrim:
|
||||
case jpiStrRtrim:
|
||||
case jpiStrBtrim:
|
||||
{
|
||||
int32 arg = reserveSpaceForItemPointer(buf);
|
||||
|
||||
|
|
@ -457,6 +462,9 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
|
|||
case jpiInteger:
|
||||
case jpiNumber:
|
||||
case jpiStringFunc:
|
||||
case jpiStrLower:
|
||||
case jpiStrUpper:
|
||||
case jpiStrInitcap:
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
|
||||
|
|
@ -831,6 +839,60 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
|
|||
}
|
||||
appendStringInfoChar(buf, ')');
|
||||
break;
|
||||
case jpiStrReplace:
|
||||
appendStringInfoString(buf, ".replace(");
|
||||
jspGetLeftArg(v, &elem);
|
||||
printJsonPathItem(buf, &elem, false, false);
|
||||
appendStringInfoChar(buf, ',');
|
||||
jspGetRightArg(v, &elem);
|
||||
printJsonPathItem(buf, &elem, false, false);
|
||||
appendStringInfoChar(buf, ')');
|
||||
break;
|
||||
case jpiStrLower:
|
||||
appendStringInfoString(buf, ".lower()");
|
||||
break;
|
||||
case jpiStrUpper:
|
||||
appendStringInfoString(buf, ".upper()");
|
||||
break;
|
||||
case jpiStrSplitPart:
|
||||
appendStringInfoString(buf, ".split_part(");
|
||||
jspGetLeftArg(v, &elem);
|
||||
printJsonPathItem(buf, &elem, false, false);
|
||||
appendStringInfoChar(buf, ',');
|
||||
jspGetRightArg(v, &elem);
|
||||
printJsonPathItem(buf, &elem, false, false);
|
||||
appendStringInfoChar(buf, ')');
|
||||
break;
|
||||
case jpiStrLtrim:
|
||||
appendStringInfoString(buf, ".ltrim(");
|
||||
if (v->content.arg)
|
||||
{
|
||||
jspGetArg(v, &elem);
|
||||
printJsonPathItem(buf, &elem, false, false);
|
||||
}
|
||||
appendStringInfoChar(buf, ')');
|
||||
break;
|
||||
case jpiStrRtrim:
|
||||
appendStringInfoString(buf, ".rtrim(");
|
||||
if (v->content.arg)
|
||||
{
|
||||
jspGetArg(v, &elem);
|
||||
printJsonPathItem(buf, &elem, false, false);
|
||||
}
|
||||
appendStringInfoChar(buf, ')');
|
||||
break;
|
||||
case jpiStrBtrim:
|
||||
appendStringInfoString(buf, ".btrim(");
|
||||
if (v->content.arg)
|
||||
{
|
||||
jspGetArg(v, &elem);
|
||||
printJsonPathItem(buf, &elem, false, false);
|
||||
}
|
||||
appendStringInfoChar(buf, ')');
|
||||
break;
|
||||
case jpiStrInitcap:
|
||||
appendStringInfoString(buf, ".initcap()");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
|
||||
}
|
||||
|
|
@ -914,6 +976,22 @@ jspOperationName(JsonPathItemType type)
|
|||
return "timestamp";
|
||||
case jpiTimestampTz:
|
||||
return "timestamp_tz";
|
||||
case jpiStrReplace:
|
||||
return "replace";
|
||||
case jpiStrLower:
|
||||
return "lower";
|
||||
case jpiStrUpper:
|
||||
return "upper";
|
||||
case jpiStrLtrim:
|
||||
return "ltrim";
|
||||
case jpiStrRtrim:
|
||||
return "rtrim";
|
||||
case jpiStrBtrim:
|
||||
return "btrim";
|
||||
case jpiStrInitcap:
|
||||
return "initcap";
|
||||
case jpiStrSplitPart:
|
||||
return "split_part";
|
||||
default:
|
||||
elog(ERROR, "unrecognized jsonpath item type: %d", type);
|
||||
return NULL;
|
||||
|
|
@ -1016,6 +1094,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
|
|||
case jpiInteger:
|
||||
case jpiNumber:
|
||||
case jpiStringFunc:
|
||||
case jpiStrLower:
|
||||
case jpiStrUpper:
|
||||
case jpiStrInitcap:
|
||||
break;
|
||||
case jpiString:
|
||||
case jpiKey:
|
||||
|
|
@ -1041,6 +1122,8 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
|
|||
case jpiMod:
|
||||
case jpiStartsWith:
|
||||
case jpiDecimal:
|
||||
case jpiStrReplace:
|
||||
case jpiStrSplitPart:
|
||||
read_int32(v->content.args.left, base, pos);
|
||||
read_int32(v->content.args.right, base, pos);
|
||||
break;
|
||||
|
|
@ -1055,6 +1138,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
|
|||
case jpiTimeTz:
|
||||
case jpiTimestamp:
|
||||
case jpiTimestampTz:
|
||||
case jpiStrLtrim:
|
||||
case jpiStrRtrim:
|
||||
case jpiStrBtrim:
|
||||
read_int32(v->content.arg, base, pos);
|
||||
break;
|
||||
case jpiIndexArray:
|
||||
|
|
@ -1090,7 +1176,10 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
|
|||
v->type == jpiTime ||
|
||||
v->type == jpiTimeTz ||
|
||||
v->type == jpiTimestamp ||
|
||||
v->type == jpiTimestampTz);
|
||||
v->type == jpiTimestampTz ||
|
||||
v->type == jpiStrLtrim ||
|
||||
v->type == jpiStrRtrim ||
|
||||
v->type == jpiStrBtrim);
|
||||
|
||||
jspInitByBuffer(a, v->base, v->content.arg);
|
||||
}
|
||||
|
|
@ -1152,7 +1241,15 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
|
|||
v->type == jpiTime ||
|
||||
v->type == jpiTimeTz ||
|
||||
v->type == jpiTimestamp ||
|
||||
v->type == jpiTimestampTz);
|
||||
v->type == jpiTimestampTz ||
|
||||
v->type == jpiStrReplace ||
|
||||
v->type == jpiStrLower ||
|
||||
v->type == jpiStrUpper ||
|
||||
v->type == jpiStrLtrim ||
|
||||
v->type == jpiStrRtrim ||
|
||||
v->type == jpiStrBtrim ||
|
||||
v->type == jpiStrInitcap ||
|
||||
v->type == jpiStrSplitPart);
|
||||
|
||||
if (a)
|
||||
jspInitByBuffer(a, v->base, v->nextPos);
|
||||
|
|
@ -1179,7 +1276,9 @@ jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
|
|||
v->type == jpiDiv ||
|
||||
v->type == jpiMod ||
|
||||
v->type == jpiStartsWith ||
|
||||
v->type == jpiDecimal);
|
||||
v->type == jpiDecimal ||
|
||||
v->type == jpiStrReplace ||
|
||||
v->type == jpiStrSplitPart);
|
||||
|
||||
jspInitByBuffer(a, v->base, v->content.args.left);
|
||||
}
|
||||
|
|
@ -1201,7 +1300,9 @@ jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
|
|||
v->type == jpiDiv ||
|
||||
v->type == jpiMod ||
|
||||
v->type == jpiStartsWith ||
|
||||
v->type == jpiDecimal);
|
||||
v->type == jpiDecimal ||
|
||||
v->type == jpiStrReplace ||
|
||||
v->type == jpiStrSplitPart);
|
||||
|
||||
jspInitByBuffer(a, v->base, v->content.args.right);
|
||||
}
|
||||
|
|
@ -1501,6 +1602,14 @@ jspIsMutableWalker(JsonPathItem *jpi, struct JsonPathMutableContext *cxt)
|
|||
case jpiInteger:
|
||||
case jpiNumber:
|
||||
case jpiStringFunc:
|
||||
case jpiStrReplace:
|
||||
case jpiStrLower:
|
||||
case jpiStrUpper:
|
||||
case jpiStrLtrim:
|
||||
case jpiStrRtrim:
|
||||
case jpiStrBtrim:
|
||||
case jpiStrInitcap:
|
||||
case jpiStrSplitPart:
|
||||
status = jpdsNonDateTime;
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -329,6 +329,8 @@ static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
|
|||
JsonValueList *found);
|
||||
static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
||||
JsonbValue *jb, JsonValueList *found);
|
||||
static JsonPathExecResult executeStringInternalMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
||||
JsonbValue *jb, JsonValueList *found);
|
||||
static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
|
||||
JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
|
||||
static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
|
||||
|
|
@ -1680,6 +1682,22 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
|||
}
|
||||
break;
|
||||
|
||||
case jpiStrReplace:
|
||||
case jpiStrLower:
|
||||
case jpiStrUpper:
|
||||
case jpiStrLtrim:
|
||||
case jpiStrRtrim:
|
||||
case jpiStrBtrim:
|
||||
case jpiStrInitcap:
|
||||
case jpiStrSplitPart:
|
||||
{
|
||||
if (unwrap && JsonbType(jb) == jbvArray)
|
||||
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
|
||||
|
||||
return executeStringInternalMethod(cxt, jsp, jb, found);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
|
||||
}
|
||||
|
|
@ -2879,6 +2897,166 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
|||
return executeNextItem(cxt, jsp, &elem, &jbv, found);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of .upper(), .lower() et al. string methods,
|
||||
* that forward their actual implementation to internal functions.
|
||||
*/
|
||||
static JsonPathExecResult
|
||||
executeStringInternalMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
||||
JsonbValue *jb, JsonValueList *found)
|
||||
{
|
||||
JsonbValue jbv;
|
||||
bool hasNext;
|
||||
JsonPathExecResult res = jperNotFound;
|
||||
JsonPathItem elem;
|
||||
Datum str; /* Datum representation for the current string
|
||||
* value. The first argument to internal
|
||||
* functions */
|
||||
char *resStr = NULL;
|
||||
|
||||
Assert(jsp->type == jpiStrReplace ||
|
||||
jsp->type == jpiStrLower ||
|
||||
jsp->type == jpiStrUpper ||
|
||||
jsp->type == jpiStrLtrim ||
|
||||
jsp->type == jpiStrRtrim ||
|
||||
jsp->type == jpiStrBtrim ||
|
||||
jsp->type == jpiStrInitcap ||
|
||||
jsp->type == jpiStrSplitPart);
|
||||
|
||||
if (!(jb = getScalar(jb, jbvString)))
|
||||
RETURN_ERROR(ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("jsonpath item method .%s() can only be applied to a string",
|
||||
jspOperationName(jsp->type)))));
|
||||
|
||||
str = PointerGetDatum(cstring_to_text_with_len(jb->val.string.val, jb->val.string.len));
|
||||
|
||||
/* Dispatch to the appropriate internal string function */
|
||||
switch (jsp->type)
|
||||
{
|
||||
case jpiStrReplace:
|
||||
{
|
||||
char *from_str,
|
||||
*to_str;
|
||||
|
||||
jspGetLeftArg(jsp, &elem);
|
||||
if (elem.type != jpiString)
|
||||
elog(ERROR, "invalid jsonpath item type for .replace() from");
|
||||
|
||||
from_str = jspGetString(&elem, NULL);
|
||||
|
||||
jspGetRightArg(jsp, &elem);
|
||||
if (elem.type != jpiString)
|
||||
elog(ERROR, "invalid jsonpath item type for .replace() to");
|
||||
|
||||
to_str = jspGetString(&elem, NULL);
|
||||
|
||||
resStr = TextDatumGetCString(DirectFunctionCall3Coll(replace_text,
|
||||
DEFAULT_COLLATION_OID,
|
||||
str,
|
||||
CStringGetTextDatum(from_str),
|
||||
CStringGetTextDatum(to_str)));
|
||||
break;
|
||||
}
|
||||
case jpiStrLower:
|
||||
resStr = TextDatumGetCString(DirectFunctionCall1Coll(lower, DEFAULT_COLLATION_OID, str));
|
||||
break;
|
||||
case jpiStrUpper:
|
||||
resStr = TextDatumGetCString(DirectFunctionCall1Coll(upper, DEFAULT_COLLATION_OID, str));
|
||||
break;
|
||||
case jpiStrLtrim:
|
||||
case jpiStrRtrim:
|
||||
case jpiStrBtrim:
|
||||
{
|
||||
PGFunction func1 = NULL;
|
||||
PGFunction func2 = NULL;
|
||||
|
||||
switch (jsp->type)
|
||||
{
|
||||
case jpiStrLtrim:
|
||||
func1 = ltrim1;
|
||||
func2 = ltrim;
|
||||
break;
|
||||
case jpiStrRtrim:
|
||||
func1 = rtrim1;
|
||||
func2 = rtrim;
|
||||
break;
|
||||
case jpiStrBtrim:
|
||||
func1 = btrim1;
|
||||
func2 = btrim;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (jsp->content.arg)
|
||||
{
|
||||
char *characters_str;
|
||||
|
||||
jspGetArg(jsp, &elem);
|
||||
if (elem.type != jpiString)
|
||||
elog(ERROR, "invalid jsonpath item type for .%s() argument",
|
||||
jspOperationName(jsp->type));
|
||||
|
||||
characters_str = jspGetString(&elem, NULL);
|
||||
resStr = TextDatumGetCString(DirectFunctionCall2Coll(func2,
|
||||
DEFAULT_COLLATION_OID, str,
|
||||
CStringGetTextDatum(characters_str)));
|
||||
}
|
||||
else
|
||||
{
|
||||
resStr = TextDatumGetCString(DirectFunctionCall1Coll(func1,
|
||||
DEFAULT_COLLATION_OID, str));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case jpiStrInitcap:
|
||||
resStr = TextDatumGetCString(DirectFunctionCall1Coll(initcap, DEFAULT_COLLATION_OID, str));
|
||||
break;
|
||||
case jpiStrSplitPart:
|
||||
{
|
||||
char *from_str;
|
||||
Numeric n;
|
||||
|
||||
jspGetLeftArg(jsp, &elem);
|
||||
if (elem.type != jpiString)
|
||||
elog(ERROR, "invalid jsonpath item type for .split_part()");
|
||||
|
||||
from_str = jspGetString(&elem, NULL);
|
||||
|
||||
jspGetRightArg(jsp, &elem);
|
||||
if (elem.type != jpiNumeric)
|
||||
elog(ERROR, "invalid jsonpath item type for .split_part()");
|
||||
|
||||
n = jspGetNumeric(&elem);
|
||||
|
||||
resStr = TextDatumGetCString(DirectFunctionCall3Coll(split_part,
|
||||
DEFAULT_COLLATION_OID,
|
||||
str,
|
||||
CStringGetTextDatum(from_str),
|
||||
DirectFunctionCall1(numeric_int4, NumericGetDatum(n))));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
elog(ERROR, "unsupported jsonpath item type: %d", jsp->type);
|
||||
}
|
||||
|
||||
if (resStr)
|
||||
res = jperOk;
|
||||
|
||||
hasNext = jspGetNext(jsp, &elem);
|
||||
|
||||
if (!hasNext && !found)
|
||||
return res;
|
||||
|
||||
jbv.type = jbvString;
|
||||
jbv.val.string.val = resStr;
|
||||
jbv.val.string.len = strlen(resStr);
|
||||
|
||||
return executeNextItem(cxt, jsp, &elem, &jbv, found);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of .keyvalue() method.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
|
|||
%token <str> DATETIME_P
|
||||
%token <str> BIGINT_P BOOLEAN_P DATE_P DECIMAL_P INTEGER_P NUMBER_P
|
||||
%token <str> STRINGFUNC_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
|
||||
%token <str> STR_REPLACE_P STR_LOWER_P STR_UPPER_P STR_LTRIM_P STR_RTRIM_P STR_BTRIM_P
|
||||
STR_INITCAP_P STR_SPLIT_PART_P
|
||||
|
||||
%type <result> result
|
||||
|
||||
|
|
@ -95,7 +97,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
|
|||
str_elem opt_str_arg int_elem
|
||||
uint_elem opt_uint_arg
|
||||
|
||||
%type <elems> accessor_expr int_list opt_int_list
|
||||
%type <elems> accessor_expr int_list opt_int_list str_int_args str_str_args
|
||||
|
||||
%type <indexs> index_list
|
||||
|
||||
|
|
@ -278,6 +280,16 @@ accessor_op:
|
|||
{ $$ = makeItemUnary(jpiTimestamp, $4); }
|
||||
| '.' TIMESTAMP_TZ_P '(' opt_uint_arg ')'
|
||||
{ $$ = makeItemUnary(jpiTimestampTz, $4); }
|
||||
| '.' STR_REPLACE_P '(' str_str_args ')'
|
||||
{ $$ = makeItemBinary(jpiStrReplace, linitial($4), lsecond($4)); }
|
||||
| '.' STR_SPLIT_PART_P '(' str_int_args ')'
|
||||
{ $$ = makeItemBinary(jpiStrSplitPart, linitial($4), lsecond($4)); }
|
||||
| '.' STR_LTRIM_P '(' opt_str_arg ')'
|
||||
{ $$ = makeItemUnary(jpiStrLtrim, $4); }
|
||||
| '.' STR_RTRIM_P '(' opt_str_arg ')'
|
||||
{ $$ = makeItemUnary(jpiStrRtrim, $4); }
|
||||
| '.' STR_BTRIM_P '(' opt_str_arg ')'
|
||||
{ $$ = makeItemUnary(jpiStrBtrim, $4); }
|
||||
;
|
||||
|
||||
int_elem:
|
||||
|
|
@ -317,6 +329,14 @@ opt_str_arg:
|
|||
| /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
str_int_args:
|
||||
str_elem ',' int_elem { $$ = list_make2($1, $3); }
|
||||
;
|
||||
|
||||
str_str_args:
|
||||
str_elem ',' str_elem { $$ = list_make2($1, $3); }
|
||||
;
|
||||
|
||||
key:
|
||||
key_name { $$ = makeItemKey(&$1); }
|
||||
;
|
||||
|
|
@ -357,6 +377,14 @@ key_name:
|
|||
| TIME_TZ_P
|
||||
| TIMESTAMP_P
|
||||
| TIMESTAMP_TZ_P
|
||||
| STR_LOWER_P
|
||||
| STR_UPPER_P
|
||||
| STR_INITCAP_P
|
||||
| STR_REPLACE_P
|
||||
| STR_SPLIT_PART_P
|
||||
| STR_LTRIM_P
|
||||
| STR_RTRIM_P
|
||||
| STR_BTRIM_P
|
||||
;
|
||||
|
||||
method:
|
||||
|
|
@ -373,6 +401,9 @@ method:
|
|||
| INTEGER_P { $$ = jpiInteger; }
|
||||
| NUMBER_P { $$ = jpiNumber; }
|
||||
| STRINGFUNC_P { $$ = jpiStringFunc; }
|
||||
| STR_LOWER_P { $$ = jpiStrLower; }
|
||||
| STR_UPPER_P { $$ = jpiStrUpper; }
|
||||
| STR_INITCAP_P { $$ = jpiStrInitcap; }
|
||||
;
|
||||
%%
|
||||
|
||||
|
|
|
|||
|
|
@ -413,8 +413,13 @@ static const JsonPathKeyword keywords[] = {
|
|||
{4, true, TRUE_P, "true"},
|
||||
{4, false, TYPE_P, "type"},
|
||||
{4, false, WITH_P, "with"},
|
||||
{5, false, STR_BTRIM_P, "btrim"},
|
||||
{5, true, FALSE_P, "false"},
|
||||
{5, false, FLOOR_P, "floor"},
|
||||
{5, false, STR_LOWER_P, "lower"},
|
||||
{5, false, STR_LTRIM_P, "ltrim"},
|
||||
{5, false, STR_RTRIM_P, "rtrim"},
|
||||
{5, false, STR_UPPER_P, "upper"},
|
||||
{6, false, BIGINT_P, "bigint"},
|
||||
{6, false, DOUBLE_P, "double"},
|
||||
{6, false, EXISTS_P, "exists"},
|
||||
|
|
@ -425,13 +430,16 @@ static const JsonPathKeyword keywords[] = {
|
|||
{7, false, BOOLEAN_P, "boolean"},
|
||||
{7, false, CEILING_P, "ceiling"},
|
||||
{7, false, DECIMAL_P, "decimal"},
|
||||
{7, false, STR_INITCAP_P, "initcap"},
|
||||
{7, false, INTEGER_P, "integer"},
|
||||
{7, false, STR_REPLACE_P, "replace"},
|
||||
{7, false, TIME_TZ_P, "time_tz"},
|
||||
{7, false, UNKNOWN_P, "unknown"},
|
||||
{8, false, DATETIME_P, "datetime"},
|
||||
{8, false, KEYVALUE_P, "keyvalue"},
|
||||
{9, false, TIMESTAMP_P, "timestamp"},
|
||||
{10, false, LIKE_REGEX_P, "like_regex"},
|
||||
{10, false, STR_SPLIT_PART_P, "split_part"},
|
||||
{12, false, TIMESTAMP_TZ_P, "timestamp_tz"},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -115,6 +115,14 @@ typedef enum JsonPathItemType
|
|||
jpiTimeTz, /* .time_tz() item method */
|
||||
jpiTimestamp, /* .timestamp() item method */
|
||||
jpiTimestampTz, /* .timestamp_tz() item method */
|
||||
jpiStrReplace, /* .replace() item method */
|
||||
jpiStrLower, /* .lower() item method */
|
||||
jpiStrUpper, /* .upper() item method */
|
||||
jpiStrLtrim, /* .ltrim() item method */
|
||||
jpiStrRtrim, /* .rtrim() item method */
|
||||
jpiStrBtrim, /* .btrim() item method */
|
||||
jpiStrInitcap, /* .initcap() item method */
|
||||
jpiStrSplitPart, /* .split_part() item method */
|
||||
} JsonPathItemType;
|
||||
|
||||
/* XQuery regex mode flags for LIKE_REGEX predicate */
|
||||
|
|
|
|||
|
|
@ -2723,6 +2723,387 @@ select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().string()');
|
|||
(1 row)
|
||||
|
||||
rollback;
|
||||
-- test .ltrim()
|
||||
select jsonb_path_query('" hello "', '$.ltrim(" ")');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"hello "
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('" hello "', '$.ltrim()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"hello "
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"zzzytest"', '$.ltrim("xyz")');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"test"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('null', '$.ltrim()');
|
||||
ERROR: jsonpath item method .ltrim() can only be applied to a string
|
||||
select jsonb_path_query('null', '$.ltrim()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('[]', '$.ltrim()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('[]', 'strict $.ltrim()');
|
||||
ERROR: jsonpath item method .ltrim() can only be applied to a string
|
||||
select jsonb_path_query('{}', '$.ltrim()');
|
||||
ERROR: jsonpath item method .ltrim() can only be applied to a string
|
||||
select jsonb_path_query('[]', 'strict $.ltrim()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('{}', '$.ltrim()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('1.23', '$.ltrim()');
|
||||
ERROR: jsonpath item method .ltrim() can only be applied to a string
|
||||
select jsonb_path_query('"1.23"', '$.ltrim()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"1.23"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"1.23aaa"', '$.ltrim()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"1.23aaa"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('1234', '$.ltrim()');
|
||||
ERROR: jsonpath item method .ltrim() can only be applied to a string
|
||||
select jsonb_path_query('true', '$.ltrim()');
|
||||
ERROR: jsonpath item method .ltrim() can only be applied to a string
|
||||
select jsonb_path_query('1234', '$.ltrim().type()');
|
||||
ERROR: jsonpath item method .ltrim() can only be applied to a string
|
||||
select jsonb_path_query('[2, true]', '$.ltrim()');
|
||||
ERROR: jsonpath item method .ltrim() can only be applied to a string
|
||||
select jsonb_path_query_array('[" maybe ", " yes", " no"]', '$[*].ltrim()');
|
||||
jsonb_path_query_array
|
||||
--------------------------
|
||||
["maybe ", "yes", "no"]
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query_array('[" maybe ", " yes", " no"]', '$[*].ltrim().type()');
|
||||
jsonb_path_query_array
|
||||
--------------------------------
|
||||
["string", "string", "string"]
|
||||
(1 row)
|
||||
|
||||
-- test .rtrim()
|
||||
select jsonb_path_query('" hello "', '$.rtrim(" ")');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
" hello"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"testxxzx"', '$.rtrim("xyz")');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"test"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('" hello "', '$.rtrim()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
" hello"
|
||||
(1 row)
|
||||
|
||||
-- test .btrim()
|
||||
select jsonb_path_query('" hello "', '$.btrim(" ")');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"hello"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"xyxtrimyyx"', '$.btrim("xyz")');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"trim"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('" hello "', '$.btrim()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"hello"
|
||||
(1 row)
|
||||
|
||||
-- test .lower()
|
||||
select jsonb_path_query('null', '$.lower()');
|
||||
ERROR: jsonpath item method .lower() can only be applied to a string
|
||||
select jsonb_path_query('null', '$.lower()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('[]', '$.lower()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('[]', 'strict $.lower()');
|
||||
ERROR: jsonpath item method .lower() can only be applied to a string
|
||||
select jsonb_path_query('{}', '$.lower()');
|
||||
ERROR: jsonpath item method .lower() can only be applied to a string
|
||||
select jsonb_path_query('[]', 'strict $.lower()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('{}', '$.lower()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('1.23', '$.lower()');
|
||||
ERROR: jsonpath item method .lower() can only be applied to a string
|
||||
select jsonb_path_query('"1.23"', '$.lower()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"1.23"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"1.23aaa"', '$.lower()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"1.23aaa"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('1234', '$.lower()');
|
||||
ERROR: jsonpath item method .lower() can only be applied to a string
|
||||
select jsonb_path_query('true', '$.lower()');
|
||||
ERROR: jsonpath item method .lower() can only be applied to a string
|
||||
select jsonb_path_query('1234', '$.lower().type()');
|
||||
ERROR: jsonpath item method .lower() can only be applied to a string
|
||||
select jsonb_path_query('[2, true]', '$.lower()');
|
||||
ERROR: jsonpath item method .lower() can only be applied to a string
|
||||
select jsonb_path_query_array('["maybe", "yes", "no"]', '$[*].lower()');
|
||||
jsonb_path_query_array
|
||||
------------------------
|
||||
["maybe", "yes", "no"]
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query_array('["maybe", "yes", "no"]', '$[*].lower().type()');
|
||||
jsonb_path_query_array
|
||||
--------------------------------
|
||||
["string", "string", "string"]
|
||||
(1 row)
|
||||
|
||||
-- test .upper()
|
||||
select jsonb_path_query('null', '$.upper()');
|
||||
ERROR: jsonpath item method .upper() can only be applied to a string
|
||||
select jsonb_path_query('null', '$.upper()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('[]', '$.upper()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('[]', 'strict $.upper()');
|
||||
ERROR: jsonpath item method .upper() can only be applied to a string
|
||||
select jsonb_path_query('{}', '$.upper()');
|
||||
ERROR: jsonpath item method .upper() can only be applied to a string
|
||||
select jsonb_path_query('[]', 'strict $.upper()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('{}', '$.upper()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('1.23', '$.upper()');
|
||||
ERROR: jsonpath item method .upper() can only be applied to a string
|
||||
select jsonb_path_query('"1.23"', '$.upper()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"1.23"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"1.23aaa"', '$.upper()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"1.23AAA"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('1234', '$.upper()');
|
||||
ERROR: jsonpath item method .upper() can only be applied to a string
|
||||
select jsonb_path_query('true', '$.upper()');
|
||||
ERROR: jsonpath item method .upper() can only be applied to a string
|
||||
select jsonb_path_query('1234', '$.upper().type()');
|
||||
ERROR: jsonpath item method .upper() can only be applied to a string
|
||||
select jsonb_path_query('[2, true]', '$.upper()');
|
||||
ERROR: jsonpath item method .upper() can only be applied to a string
|
||||
select jsonb_path_query_array('["maybe", "yes", "no"]', '$[*].upper()');
|
||||
jsonb_path_query_array
|
||||
------------------------
|
||||
["MAYBE", "YES", "NO"]
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query_array('["maybe", "yes", "no"]', '$[*].upper().type()');
|
||||
jsonb_path_query_array
|
||||
--------------------------------
|
||||
["string", "string", "string"]
|
||||
(1 row)
|
||||
|
||||
-- test .initcap()
|
||||
select jsonb_path_query('null', '$.initcap()');
|
||||
ERROR: jsonpath item method .initcap() can only be applied to a string
|
||||
select jsonb_path_query('null', '$.initcap()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('[]', '$.initcap()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('[]', 'strict $.initcap()');
|
||||
ERROR: jsonpath item method .initcap() can only be applied to a string
|
||||
select jsonb_path_query('{}', '$.initcap()');
|
||||
ERROR: jsonpath item method .initcap() can only be applied to a string
|
||||
select jsonb_path_query('[]', 'strict $.initcap()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('{}', '$.initcap()', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('1.23', '$.initcap()');
|
||||
ERROR: jsonpath item method .initcap() can only be applied to a string
|
||||
select jsonb_path_query('"1.23"', '$.initcap()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"1.23"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"1.23aaa"', '$.initcap()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"1.23aaa"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('1234', '$.initcap()');
|
||||
ERROR: jsonpath item method .initcap() can only be applied to a string
|
||||
select jsonb_path_query('true', '$.initcap()');
|
||||
ERROR: jsonpath item method .initcap() can only be applied to a string
|
||||
select jsonb_path_query('1234', '$.initcap().type()');
|
||||
ERROR: jsonpath item method .initcap() can only be applied to a string
|
||||
select jsonb_path_query('[2, true]', '$.initcap()');
|
||||
ERROR: jsonpath item method .initcap() can only be applied to a string
|
||||
select jsonb_path_query('["maybe yes", "probably no"]', '$.initcap()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"Maybe Yes"
|
||||
"Probably No"
|
||||
(2 rows)
|
||||
|
||||
-- Test .replace()
|
||||
select jsonb_path_query('null', '$.replace("x", "bye")');
|
||||
ERROR: jsonpath item method .replace() can only be applied to a string
|
||||
select jsonb_path_query('null', '$.replace("x", "bye")', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('["x", "y", "z"]', '$.replace("x", "bye")');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"bye"
|
||||
"y"
|
||||
"z"
|
||||
(3 rows)
|
||||
|
||||
select jsonb_path_query('{}', '$.replace("x", "bye")');
|
||||
ERROR: jsonpath item method .replace() can only be applied to a string
|
||||
select jsonb_path_query('[]', 'strict $.replace("x", "bye")', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('{}', '$.replace("x", "bye")', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('1.23', '$.replace("x", "bye")');
|
||||
ERROR: jsonpath item method .replace() can only be applied to a string
|
||||
select jsonb_path_query('"hello world"', '$.replace("hello","bye")');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"bye world"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"hello world"', '$.replace("hello","bye") starts with "bye"');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
true
|
||||
(1 row)
|
||||
|
||||
-- Test .split_part()
|
||||
select jsonb_path_query('"abc~@~def~@~ghi"', '$.split_part("~@~", 2)');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"def"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"abc,def,ghi,jkl"', '$.split_part(",", -2)');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"ghi"
|
||||
(1 row)
|
||||
|
||||
-- Test string methods play nicely together
|
||||
select jsonb_path_query('"hello world"', '$.replace("hello","bye").upper()');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"BYE WORLD"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"hElLo WorlD"', '$.lower().upper().lower().replace("hello","bye")');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"bye world"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"hElLo WorlD"', '$.upper().lower().upper().replace("HELLO", "BYE")');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
"BYE WORLD"
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('"hElLo WorlD"', '$.lower().upper().lower().replace("hello","bye") starts with "bye"');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
true
|
||||
(1 row)
|
||||
|
||||
select jsonb_path_query('" hElLo WorlD "', '$.btrim().lower().upper().lower().replace("hello","bye") starts with "bye"');
|
||||
jsonb_path_query
|
||||
------------------
|
||||
true
|
||||
(1 row)
|
||||
|
||||
-- Test .time()
|
||||
select jsonb_path_query('null', '$.time()');
|
||||
ERROR: jsonpath item method .time() can only be applied to a string
|
||||
|
|
|
|||
|
|
@ -435,6 +435,192 @@ select '$.string()'::jsonpath;
|
|||
$.string()
|
||||
(1 row)
|
||||
|
||||
select '$.replace("hello","bye")'::jsonpath;
|
||||
jsonpath
|
||||
--------------------------
|
||||
$.replace("hello","bye")
|
||||
(1 row)
|
||||
|
||||
select '$.lower()'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
$.lower()
|
||||
(1 row)
|
||||
|
||||
select '$.upper()'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
$.upper()
|
||||
(1 row)
|
||||
|
||||
select '$.lower().upper().lower().replace("hello","bye")'::jsonpath;
|
||||
jsonpath
|
||||
--------------------------------------------------
|
||||
$.lower().upper().lower().replace("hello","bye")
|
||||
(1 row)
|
||||
|
||||
select '$.ltrim()'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
$.ltrim()
|
||||
(1 row)
|
||||
|
||||
select '$.ltrim("xyz")'::jsonpath;
|
||||
jsonpath
|
||||
----------------
|
||||
$.ltrim("xyz")
|
||||
(1 row)
|
||||
|
||||
select '$.rtrim()'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
$.rtrim()
|
||||
(1 row)
|
||||
|
||||
select '$.rtrim("xyz")'::jsonpath;
|
||||
jsonpath
|
||||
----------------
|
||||
$.rtrim("xyz")
|
||||
(1 row)
|
||||
|
||||
select '$.btrim()'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
$.btrim()
|
||||
(1 row)
|
||||
|
||||
select '$.btrim("xyz")'::jsonpath;
|
||||
jsonpath
|
||||
----------------
|
||||
$.btrim("xyz")
|
||||
(1 row)
|
||||
|
||||
select '$.initcap()'::jsonpath;
|
||||
jsonpath
|
||||
-------------
|
||||
$.initcap()
|
||||
(1 row)
|
||||
|
||||
select '$.split_part("~@~", 2)'::jsonpath;
|
||||
jsonpath
|
||||
-----------------------
|
||||
$.split_part("~@~",2)
|
||||
(1 row)
|
||||
|
||||
-- Parse errors
|
||||
select '$.replace("hello")'::jsonpath;
|
||||
ERROR: syntax error at or near ")" of jsonpath input
|
||||
LINE 1: select '$.replace("hello")'::jsonpath;
|
||||
^
|
||||
select '$.replace()'::jsonpath;
|
||||
ERROR: syntax error at or near ")" of jsonpath input
|
||||
LINE 1: select '$.replace()'::jsonpath;
|
||||
^
|
||||
select '$.replace("hello","bye","extra")'::jsonpath;
|
||||
ERROR: syntax error at or near "," of jsonpath input
|
||||
LINE 1: select '$.replace("hello","bye","extra")'::jsonpath;
|
||||
^
|
||||
select '$.split_part("~@~")'::jsonpath;
|
||||
ERROR: syntax error at or near ")" of jsonpath input
|
||||
LINE 1: select '$.split_part("~@~")'::jsonpath;
|
||||
^
|
||||
select '$.split_part()'::jsonpath;
|
||||
ERROR: syntax error at or near ")" of jsonpath input
|
||||
LINE 1: select '$.split_part()'::jsonpath;
|
||||
^
|
||||
select '$.split_part("~@~", "hi")'::jsonpath;
|
||||
ERROR: syntax error at or near """ of jsonpath input
|
||||
LINE 1: select '$.split_part("~@~", "hi")'::jsonpath;
|
||||
^
|
||||
select '$.split_part("~@~", 2, "extra")'::jsonpath;
|
||||
ERROR: syntax error at or near "," of jsonpath input
|
||||
LINE 1: select '$.split_part("~@~", 2, "extra")'::jsonpath;
|
||||
^
|
||||
select '$.lower("hi")'::jsonpath;
|
||||
ERROR: syntax error at or near """ of jsonpath input
|
||||
LINE 1: select '$.lower("hi")'::jsonpath;
|
||||
^
|
||||
select '$.upper("hi")'::jsonpath;
|
||||
ERROR: syntax error at or near """ of jsonpath input
|
||||
LINE 1: select '$.upper("hi")'::jsonpath;
|
||||
^
|
||||
select '$.initcap("hi")'::jsonpath;
|
||||
ERROR: syntax error at or near """ of jsonpath input
|
||||
LINE 1: select '$.initcap("hi")'::jsonpath;
|
||||
^
|
||||
select '$.ltrim(42)'::jsonpath;
|
||||
ERROR: syntax error at or near "42" of jsonpath input
|
||||
LINE 1: select '$.ltrim(42)'::jsonpath;
|
||||
^
|
||||
select '$.ltrim("x", "y")'::jsonpath;
|
||||
ERROR: syntax error at or near "," of jsonpath input
|
||||
LINE 1: select '$.ltrim("x", "y")'::jsonpath;
|
||||
^
|
||||
select '$.rtrim(42)'::jsonpath;
|
||||
ERROR: syntax error at or near "42" of jsonpath input
|
||||
LINE 1: select '$.rtrim(42)'::jsonpath;
|
||||
^
|
||||
select '$.rtrim("x", "y")'::jsonpath;
|
||||
ERROR: syntax error at or near "," of jsonpath input
|
||||
LINE 1: select '$.rtrim("x", "y")'::jsonpath;
|
||||
^
|
||||
select '$.trim(42)'::jsonpath;
|
||||
ERROR: syntax error at or near "(" of jsonpath input
|
||||
LINE 1: select '$.trim(42)'::jsonpath;
|
||||
^
|
||||
select '$.trim("x", "y")'::jsonpath;
|
||||
ERROR: syntax error at or near "(" of jsonpath input
|
||||
LINE 1: select '$.trim("x", "y")'::jsonpath;
|
||||
^
|
||||
-- Verify method keywords work as object key names
|
||||
select '$.lower'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
$."lower"
|
||||
(1 row)
|
||||
|
||||
select '$.upper'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
$."upper"
|
||||
(1 row)
|
||||
|
||||
select '$.initcap'::jsonpath;
|
||||
jsonpath
|
||||
-------------
|
||||
$."initcap"
|
||||
(1 row)
|
||||
|
||||
select '$.replace'::jsonpath;
|
||||
jsonpath
|
||||
-------------
|
||||
$."replace"
|
||||
(1 row)
|
||||
|
||||
select '$.split_part'::jsonpath;
|
||||
jsonpath
|
||||
----------------
|
||||
$."split_part"
|
||||
(1 row)
|
||||
|
||||
select '$.ltrim'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
$."ltrim"
|
||||
(1 row)
|
||||
|
||||
select '$.rtrim'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
$."rtrim"
|
||||
(1 row)
|
||||
|
||||
select '$.btrim'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
$."btrim"
|
||||
(1 row)
|
||||
|
||||
select '$.time()'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
|
|
|
|||
|
|
@ -1147,7 +1147,7 @@ INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
|
|||
ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4"
|
||||
DETAIL: Failing row contains ({"a": 10}, 1, [1, 2]).
|
||||
DROP TABLE test_jsonb_constraints;
|
||||
-- Test mutabilily of query functions
|
||||
-- Test mutability of query functions
|
||||
CREATE TABLE test_jsonb_mutability(js jsonb, b int);
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.a[0]'));
|
||||
|
|
@ -1272,6 +1272,14 @@ CREATE INDEX ON test_jsonb_mutability (JSON_VALUE(js, '$' DEFAULT random()::int
|
|||
ERROR: functions in index expression must be marked IMMUTABLE
|
||||
LINE 1: CREATE INDEX ON test_jsonb_mutability (JSON_VALUE(js, '$' DE...
|
||||
^
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.rtrim()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.ltrim()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.btrim()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.lower()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.upper()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.initcap()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.replace("hello", "bye")'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.split_part(",", 2)'));
|
||||
-- DEFAULT expression
|
||||
CREATE OR REPLACE FUNCTION ret_setint() RETURNS SETOF integer AS
|
||||
$$
|
||||
|
|
|
|||
|
|
@ -623,6 +623,112 @@ select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp_tz().string(
|
|||
select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().string()');
|
||||
rollback;
|
||||
|
||||
-- test .ltrim()
|
||||
select jsonb_path_query('" hello "', '$.ltrim(" ")');
|
||||
select jsonb_path_query('" hello "', '$.ltrim()');
|
||||
select jsonb_path_query('"zzzytest"', '$.ltrim("xyz")');
|
||||
select jsonb_path_query('null', '$.ltrim()');
|
||||
select jsonb_path_query('null', '$.ltrim()', silent => true);
|
||||
select jsonb_path_query('[]', '$.ltrim()');
|
||||
select jsonb_path_query('[]', 'strict $.ltrim()');
|
||||
select jsonb_path_query('{}', '$.ltrim()');
|
||||
select jsonb_path_query('[]', 'strict $.ltrim()', silent => true);
|
||||
select jsonb_path_query('{}', '$.ltrim()', silent => true);
|
||||
select jsonb_path_query('1.23', '$.ltrim()');
|
||||
select jsonb_path_query('"1.23"', '$.ltrim()');
|
||||
select jsonb_path_query('"1.23aaa"', '$.ltrim()');
|
||||
select jsonb_path_query('1234', '$.ltrim()');
|
||||
select jsonb_path_query('true', '$.ltrim()');
|
||||
select jsonb_path_query('1234', '$.ltrim().type()');
|
||||
select jsonb_path_query('[2, true]', '$.ltrim()');
|
||||
select jsonb_path_query_array('[" maybe ", " yes", " no"]', '$[*].ltrim()');
|
||||
select jsonb_path_query_array('[" maybe ", " yes", " no"]', '$[*].ltrim().type()');
|
||||
|
||||
-- test .rtrim()
|
||||
select jsonb_path_query('" hello "', '$.rtrim(" ")');
|
||||
select jsonb_path_query('"testxxzx"', '$.rtrim("xyz")');
|
||||
select jsonb_path_query('" hello "', '$.rtrim()');
|
||||
|
||||
-- test .btrim()
|
||||
select jsonb_path_query('" hello "', '$.btrim(" ")');
|
||||
select jsonb_path_query('"xyxtrimyyx"', '$.btrim("xyz")');
|
||||
select jsonb_path_query('" hello "', '$.btrim()');
|
||||
|
||||
-- test .lower()
|
||||
select jsonb_path_query('null', '$.lower()');
|
||||
select jsonb_path_query('null', '$.lower()', silent => true);
|
||||
select jsonb_path_query('[]', '$.lower()');
|
||||
select jsonb_path_query('[]', 'strict $.lower()');
|
||||
select jsonb_path_query('{}', '$.lower()');
|
||||
select jsonb_path_query('[]', 'strict $.lower()', silent => true);
|
||||
select jsonb_path_query('{}', '$.lower()', silent => true);
|
||||
select jsonb_path_query('1.23', '$.lower()');
|
||||
select jsonb_path_query('"1.23"', '$.lower()');
|
||||
select jsonb_path_query('"1.23aaa"', '$.lower()');
|
||||
select jsonb_path_query('1234', '$.lower()');
|
||||
select jsonb_path_query('true', '$.lower()');
|
||||
select jsonb_path_query('1234', '$.lower().type()');
|
||||
select jsonb_path_query('[2, true]', '$.lower()');
|
||||
select jsonb_path_query_array('["maybe", "yes", "no"]', '$[*].lower()');
|
||||
select jsonb_path_query_array('["maybe", "yes", "no"]', '$[*].lower().type()');
|
||||
|
||||
-- test .upper()
|
||||
select jsonb_path_query('null', '$.upper()');
|
||||
select jsonb_path_query('null', '$.upper()', silent => true);
|
||||
select jsonb_path_query('[]', '$.upper()');
|
||||
select jsonb_path_query('[]', 'strict $.upper()');
|
||||
select jsonb_path_query('{}', '$.upper()');
|
||||
select jsonb_path_query('[]', 'strict $.upper()', silent => true);
|
||||
select jsonb_path_query('{}', '$.upper()', silent => true);
|
||||
select jsonb_path_query('1.23', '$.upper()');
|
||||
select jsonb_path_query('"1.23"', '$.upper()');
|
||||
select jsonb_path_query('"1.23aaa"', '$.upper()');
|
||||
select jsonb_path_query('1234', '$.upper()');
|
||||
select jsonb_path_query('true', '$.upper()');
|
||||
select jsonb_path_query('1234', '$.upper().type()');
|
||||
select jsonb_path_query('[2, true]', '$.upper()');
|
||||
select jsonb_path_query_array('["maybe", "yes", "no"]', '$[*].upper()');
|
||||
select jsonb_path_query_array('["maybe", "yes", "no"]', '$[*].upper().type()');
|
||||
|
||||
-- test .initcap()
|
||||
select jsonb_path_query('null', '$.initcap()');
|
||||
select jsonb_path_query('null', '$.initcap()', silent => true);
|
||||
select jsonb_path_query('[]', '$.initcap()');
|
||||
select jsonb_path_query('[]', 'strict $.initcap()');
|
||||
select jsonb_path_query('{}', '$.initcap()');
|
||||
select jsonb_path_query('[]', 'strict $.initcap()', silent => true);
|
||||
select jsonb_path_query('{}', '$.initcap()', silent => true);
|
||||
select jsonb_path_query('1.23', '$.initcap()');
|
||||
select jsonb_path_query('"1.23"', '$.initcap()');
|
||||
select jsonb_path_query('"1.23aaa"', '$.initcap()');
|
||||
select jsonb_path_query('1234', '$.initcap()');
|
||||
select jsonb_path_query('true', '$.initcap()');
|
||||
select jsonb_path_query('1234', '$.initcap().type()');
|
||||
select jsonb_path_query('[2, true]', '$.initcap()');
|
||||
select jsonb_path_query('["maybe yes", "probably no"]', '$.initcap()');
|
||||
|
||||
-- Test .replace()
|
||||
select jsonb_path_query('null', '$.replace("x", "bye")');
|
||||
select jsonb_path_query('null', '$.replace("x", "bye")', silent => true);
|
||||
select jsonb_path_query('["x", "y", "z"]', '$.replace("x", "bye")');
|
||||
select jsonb_path_query('{}', '$.replace("x", "bye")');
|
||||
select jsonb_path_query('[]', 'strict $.replace("x", "bye")', silent => true);
|
||||
select jsonb_path_query('{}', '$.replace("x", "bye")', silent => true);
|
||||
select jsonb_path_query('1.23', '$.replace("x", "bye")');
|
||||
select jsonb_path_query('"hello world"', '$.replace("hello","bye")');
|
||||
select jsonb_path_query('"hello world"', '$.replace("hello","bye") starts with "bye"');
|
||||
|
||||
-- Test .split_part()
|
||||
select jsonb_path_query('"abc~@~def~@~ghi"', '$.split_part("~@~", 2)');
|
||||
select jsonb_path_query('"abc,def,ghi,jkl"', '$.split_part(",", -2)');
|
||||
|
||||
-- Test string methods play nicely together
|
||||
select jsonb_path_query('"hello world"', '$.replace("hello","bye").upper()');
|
||||
select jsonb_path_query('"hElLo WorlD"', '$.lower().upper().lower().replace("hello","bye")');
|
||||
select jsonb_path_query('"hElLo WorlD"', '$.upper().lower().upper().replace("HELLO", "BYE")');
|
||||
select jsonb_path_query('"hElLo WorlD"', '$.lower().upper().lower().replace("hello","bye") starts with "bye"');
|
||||
select jsonb_path_query('" hElLo WorlD "', '$.btrim().lower().upper().lower().replace("hello","bye") starts with "bye"');
|
||||
|
||||
-- Test .time()
|
||||
select jsonb_path_query('null', '$.time()');
|
||||
select jsonb_path_query('true', '$.time()');
|
||||
|
|
|
|||
|
|
@ -78,6 +78,47 @@ select '$.boolean()'::jsonpath;
|
|||
select '$.date()'::jsonpath;
|
||||
select '$.decimal(4,2)'::jsonpath;
|
||||
select '$.string()'::jsonpath;
|
||||
select '$.replace("hello","bye")'::jsonpath;
|
||||
select '$.lower()'::jsonpath;
|
||||
select '$.upper()'::jsonpath;
|
||||
select '$.lower().upper().lower().replace("hello","bye")'::jsonpath;
|
||||
select '$.ltrim()'::jsonpath;
|
||||
select '$.ltrim("xyz")'::jsonpath;
|
||||
select '$.rtrim()'::jsonpath;
|
||||
select '$.rtrim("xyz")'::jsonpath;
|
||||
select '$.btrim()'::jsonpath;
|
||||
select '$.btrim("xyz")'::jsonpath;
|
||||
select '$.initcap()'::jsonpath;
|
||||
select '$.split_part("~@~", 2)'::jsonpath;
|
||||
|
||||
-- Parse errors
|
||||
select '$.replace("hello")'::jsonpath;
|
||||
select '$.replace()'::jsonpath;
|
||||
select '$.replace("hello","bye","extra")'::jsonpath;
|
||||
select '$.split_part("~@~")'::jsonpath;
|
||||
select '$.split_part()'::jsonpath;
|
||||
select '$.split_part("~@~", "hi")'::jsonpath;
|
||||
select '$.split_part("~@~", 2, "extra")'::jsonpath;
|
||||
select '$.lower("hi")'::jsonpath;
|
||||
select '$.upper("hi")'::jsonpath;
|
||||
select '$.initcap("hi")'::jsonpath;
|
||||
select '$.ltrim(42)'::jsonpath;
|
||||
select '$.ltrim("x", "y")'::jsonpath;
|
||||
select '$.rtrim(42)'::jsonpath;
|
||||
select '$.rtrim("x", "y")'::jsonpath;
|
||||
select '$.trim(42)'::jsonpath;
|
||||
select '$.trim("x", "y")'::jsonpath;
|
||||
|
||||
-- Verify method keywords work as object key names
|
||||
select '$.lower'::jsonpath;
|
||||
select '$.upper'::jsonpath;
|
||||
select '$.initcap'::jsonpath;
|
||||
select '$.replace'::jsonpath;
|
||||
select '$.split_part'::jsonpath;
|
||||
select '$.ltrim'::jsonpath;
|
||||
select '$.rtrim'::jsonpath;
|
||||
select '$.btrim'::jsonpath;
|
||||
|
||||
select '$.time()'::jsonpath;
|
||||
select '$.time(6)'::jsonpath;
|
||||
select '$.time_tz()'::jsonpath;
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@ INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
|
|||
|
||||
DROP TABLE test_jsonb_constraints;
|
||||
|
||||
-- Test mutabilily of query functions
|
||||
-- Test mutability of query functions
|
||||
CREATE TABLE test_jsonb_mutability(js jsonb, b int);
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.a[0]'));
|
||||
|
|
@ -402,6 +402,15 @@ CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$[1, 0 to $.a ? (@.dateti
|
|||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$[1, $.a ? (@.datetime("HH:MI") == $x)]' PASSING '12:34'::time AS x));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_VALUE(js, '$' DEFAULT random()::int ON ERROR));
|
||||
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.rtrim()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.ltrim()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.btrim()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.lower()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.upper()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.initcap()'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.replace("hello", "bye")'));
|
||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.split_part(",", 2)'));
|
||||
|
||||
-- DEFAULT expression
|
||||
CREATE OR REPLACE FUNCTION ret_setint() RETURNS SETOF integer AS
|
||||
$$
|
||||
|
|
|
|||
Loading…
Reference in a new issue