</para>
<para>
<literal>jsonb_set_lax('[{"f1":1,"f2":null},2,null,3]', '{0,f1}', null)</literal>
- <returnvalue>[{"f1": null, "f2": null}, 2, null, 3]</returnvalue>
+ <returnvalue>[{"f1":null,"f2":null},2,null,3]</returnvalue>
</para>
<para>
<literal>jsonb_set_lax('[{"f1":99,"f2":null},2]', '{0,f3}', null, true, 'return_target')</literal>
comparisons.
</para>
<para>
- <literal>jsonb_path_exists_tz('["2015-08-01 12:00:00-05"]', '$[*] ? (@.datetime() < "2015-08-02".datetime())')</literal>
+ <literal>jsonb_path_exists_tz('["2015-08-01 12:00:00 -05"]', '$[*] ? (@.datetime() < "2015-08-02".datetime())')</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
</programlisting>
</para>
</sect3>
- </sect2>
-
- <sect2 id="functions-sqljson">
- <title>SQL/JSON Functions and Expressions</title>
- <indexterm zone="functions-json">
- <primary>SQL/JSON</primary>
- <secondary>functions and expressions</secondary>
- </indexterm>
-
- <para>
- To provide native support for JSON data types within the SQL environment,
- <productname>PostgreSQL</productname> implements the
- <firstterm>SQL/JSON data model</firstterm>.
- This model comprises sequences of items. Each item can hold SQL scalar
- values, with an additional SQL/JSON null value, and composite data structures
- that use JSON arrays and objects. The model is a formalization of the implied
- data model in the JSON specification
- <ulink url="https://tools.ietf.org/html/rfc7159">RFC 7159</ulink>.
- </para>
-
- <para>
- SQL/JSON allows you to handle JSON data alongside regular SQL data,
- with transaction support, including:
- </para>
-
- <itemizedlist>
- <listitem>
- <para>
- Uploading JSON data into the database and storing it in
- regular SQL columns as character or binary strings.
- </para>
- </listitem>
- <listitem>
- <para>
- Generating JSON objects and arrays from relational data.
- </para>
- </listitem>
- <listitem>
- <para>
- Querying JSON data using SQL/JSON query functions and
- SQL/JSON path language expressions.
- </para>
- </listitem>
- </itemizedlist>
-
- <para>
- There are two groups of SQL/JSON functions.
- <link linkend="functions-sqljson-producing">Constructor functions</link>
- generate JSON data from values of SQL types.
- <link linkend="functions-sqljson-querying">Query functions</link>
- evaluate SQL/JSON path language expressions against JSON values
- and produce values of SQL/JSON types, which are converted to SQL types.
- </para>
-
- <para>
- Many SQL/JSON functions have an optional <literal>FORMAT</literal>
- clause. This is provided to conform with the SQL standard, but has no
- effect except where noted otherwise.
- </para>
-
- <para>
- <xref linkend="functions-sqljson-producing" /> lists the SQL/JSON
- Constructor functions. Each function has a <literal>RETURNING</literal>
- clause specifying the data type returned. For the <function>json</function> and
- <function>json_scalar</function> functions, this needs to be either <type>json</type> or
- <type>jsonb</type>. For the other constructor functions it must be one of <type>json</type>,
- <type>jsonb</type>, <type>bytea</type>, a character string type (<type>text</type>, <type>char</type>,
- <type>varchar</type>, or <type>nchar</type>), or a type for which there is a cast
- from <type>json</type> to that type.
- By default, the <type>json</type> type is returned.
- </para>
-
- <note>
- <para>
- Many of the results that can be obtained from the SQL/JSON Constructor
- functions can also be obtained by calling
- <productname>PostgreSQL</productname>-specific functions detailed in
- <xref linkend="functions-json-creation-table" /> and
- <xref linkend="functions-aggregate-table"/>.
- </para>
- </note>
-
- <table id="functions-sqljson-producing">
- <title>SQL/JSON Constructor Functions</title>
- <tgroup cols="1">
- <thead>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- Function signature
- </para>
- <para>
- Description
- </para>
- <para>
- Example(s)
- </para></entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm><primary>json constructor</primary></indexterm>
- <function>json</function> (
- <parameter>expression</parameter>
- <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional></optional>
- <optional> { <literal>WITH</literal> | <literal>WITHOUT</literal> } <literal>UNIQUE</literal> <optional> <literal>KEYS</literal> </optional></optional>
- <optional> <literal>RETURNING</literal> <replaceable>json_data_type</replaceable> </optional>)
- </para>
- <para>
- The <parameter>expression</parameter> can be any text type or a
- <type>bytea</type> in UTF8 encoding. If the
- <parameter>expression</parameter> is NULL, an
- <acronym>SQL</acronym> null value is returned.
- If <literal>WITH UNIQUE</literal> is specified, the
- <parameter>expression</parameter> must not contain any duplicate
- object keys.
- </para>
- <para>
- <literal>json('{"a":123, "b":[true,"foo"], "a":"bar"}')</literal>
- <returnvalue>{"a":123, "b":[true,"foo"], "a":"bar"}</returnvalue>
- </para>
- <para>
- <literal>json('{"a":123,"b":[true,"foo"],"a":"bar"}' returning jsonb)</literal>
- <returnvalue>{"a": "bar", "b": [true, "foo"]}</returnvalue>
- </para></entry>
- </row>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm><primary>json_scalar</primary></indexterm>
- <function>json_scalar</function> (<parameter>expression</parameter>
- <optional> <literal>RETURNING</literal> <replaceable>json_data_type</replaceable> </optional>)
- </para>
- <para>
- Returns a JSON scalar value representing
- <parameter>expression</parameter>.
- If the input is NULL, an SQL NULL is returned. If the input is a number
- or a boolean value, a corresponding JSON number or boolean value is
- returned. For any other value a JSON string is returned.
- </para>
- <para>
- <literal>json_scalar(123.45)</literal>
- <returnvalue>123.45</returnvalue>
- </para>
- <para>
- <literal>json_scalar(CURRENT_TIMESTAMP)</literal>
- <returnvalue>"2022-05-10T10:51:04.62128-04:00"</returnvalue>
- </para></entry>
- </row>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm><primary>json_object</primary></indexterm>
- <function>json_object</function> (
- <optional> { <parameter>key_expression</parameter> { <literal>VALUE</literal> | ':' }
- <parameter>value_expression</parameter> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> }<optional>, ...</optional> </optional>
- <optional> { <literal>NULL</literal> | <literal>ABSENT</literal> } <literal>ON NULL</literal> </optional>
- <optional> { <literal>WITH</literal> | <literal>WITHOUT</literal> } <literal>UNIQUE</literal> <optional> <literal>KEYS</literal> </optional> </optional>
- <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
- </para>
- <para>
- Constructs a JSON object of all the key value pairs given,
- or an empty object if none are given.
- <parameter>key_expression</parameter> is a scalar expression
- defining the <acronym>JSON</acronym> key, which is
- converted to the <type>text</type> type.
- It cannot be <literal>NULL</literal> nor can it
- belong to a type that has a cast to the <type>json</type>.
- If <literal>WITH UNIQUE</literal> is specified, there must not
- be any duplicate <parameter>key_expression</parameter>.
- If <literal>ABSENT ON NULL</literal> is specified, the entire
- pair is omitted if the <parameter>value_expression</parameter>
- is <literal>NULL</literal>.
- </para>
- <para>
- <literal>json_object('code' VALUE 'P123', 'title': 'Jaws')</literal>
- <returnvalue>{"code" : "P123", "title" : "Jaws"}</returnvalue>
- </para></entry>
- </row>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm><primary>json_objectagg</primary></indexterm>
- <function>json_objectagg</function> (
- <optional> { <parameter>key_expression</parameter> { <literal>VALUE</literal> | ':' } <parameter>value_expression</parameter> } </optional>
- <optional> { <literal>NULL</literal> | <literal>ABSENT</literal> } <literal>ON NULL</literal> </optional>
- <optional> { <literal>WITH</literal> | <literal>WITHOUT</literal> } <literal>UNIQUE</literal> <optional> <literal>KEYS</literal> </optional> </optional>
- <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
- </para>
- <para>
- Behaves like <function>json_object</function> above, but as an
- aggregate function, so it only takes one
- <parameter>key_expression</parameter> and one
- <parameter>value_expression</parameter> parameter.
- </para>
- <para>
- <literal>SELECT json_objectagg(k:v) FROM (VALUES ('a'::text,current_date),('b',current_date + 1)) AS t(k,v)</literal>
- <returnvalue>{ "a" : "2022-05-10", "b" : "2022-05-11" }</returnvalue>
- </para></entry>
- </row>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm><primary>json_array</primary></indexterm>
- <function>json_array</function> (
- <optional> { <parameter>value_expression</parameter> <optional> <literal>FORMAT JSON</literal> </optional> } <optional>, ...</optional> </optional>
- <optional> { <literal>NULL</literal> | <literal>ABSENT</literal> } <literal>ON NULL</literal> </optional>
- <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
- </para>
- <para role="func_signature">
- <function>json_array</function> (
- <optional> <replaceable>query_expression</replaceable> </optional>
- <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
- </para>
- <para>
- Constructs a JSON array from either a series of
- <parameter>value_expression</parameter> parameters or from the results
- of <replaceable>query_expression</replaceable>,
- which must be a SELECT query returning a single column. If
- <literal>ABSENT ON NULL</literal> is specified, NULL values are ignored.
- This is always the case if a
- <replaceable>query_expression</replaceable> is used.
- </para>
- <para>
- <literal>json_array(1,true,json '{"a":null}')</literal>
- <returnvalue>[1, true, {"a":null}]</returnvalue>
- </para>
- <para>
- <literal>json_array(SELECT * FROM (VALUES(1),(2)) t)</literal>
- <returnvalue>[1, 2]</returnvalue>
- </para></entry>
- </row>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm><primary>json_arrayagg</primary></indexterm>
- <function>json_arrayagg</function> (
- <optional> <parameter>value_expression</parameter> </optional>
- <optional> <literal>ORDER BY</literal> <replaceable>sort_expression</replaceable> </optional>
- <optional> { <literal>NULL</literal> | <literal>ABSENT</literal> } <literal>ON NULL</literal> </optional>
- <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
- </para>
- <para>
- Behaves in the same way as <function>json_array</function>
- but as an aggregate function so it only takes one
- <parameter>value_expression</parameter> parameter.
- If <literal>ABSENT ON NULL</literal> is specified, any NULL
- values are omitted.
- If <literal>ORDER BY</literal> is specified, the elements will
- appear in the array in that order rather than in the input order.
- </para>
- <para>
- <literal>SELECT json_arrayagg(v) FROM (VALUES(2),(1)) t(v)</literal>
- <returnvalue>[2, 1]</returnvalue>
- </para></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
- <para>
- <xref linkend="functions-sqljson-misc" /> details SQL/JSON
- facilities for testing and serializing JSON.
- </para>
-
- <table id="functions-sqljson-misc">
- <title>SQL/JSON Testing and Serializing Functions</title>
- <tgroup cols="1">
- <thead>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- Function signature
- </para>
- <para>
- Description
- </para>
- <para>
- Example(s)
- </para></entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm><primary>IS JSON</primary></indexterm>
- <parameter>expression</parameter> <literal>IS</literal> <optional> <literal>NOT</literal> </optional> <literal>JSON</literal>
- <optional> { <literal>VALUE</literal> | <literal>SCALAR</literal> | <literal>ARRAY</literal> | <literal>OBJECT</literal> } </optional>
- <optional> { <literal>WITH</literal> | <literal>WITHOUT</literal> } <literal>UNIQUE</literal> <optional> <literal>KEYS</literal> </optional> </optional>
- </para>
- <para>
- This predicate tests whether <parameter>expression</parameter> can be
- parsed as JSON, possibly of a specified type.
- If <literal>SCALAR</literal> or <literal>ARRAY</literal> or
- <literal>OBJECT</literal> is specified, the
- test is whether or not the JSON is of that particular type. If
- <literal>WITH UNIQUE</literal> is specified, then an any object in the
- <parameter>expression</parameter> is also tested to see if it
- has duplicate keys.
- </para>
- <para>
-<screen>
-SELECT js,
- js IS JSON "json?",
- js IS JSON SCALAR "scalar?",
- js IS JSON OBJECT "object?",
- js IS JSON ARRAY "array?"
-FROM
-(VALUES ('123'), ('"abc"'), ('{"a": "b"}'),
-('[1,2]'),('abc')) foo(js);
- js | json? | scalar? | object? | array?
-------------+-------+---------+---------+--------
- 123 | t | t | f | f
- "abc" | t | t | f | f
- {"a": "b"} | t | f | t | f
- [1,2] | t | f | f | t
- abc | f | f | f | f
-</screen>
- </para></entry>
- </row>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <function>json_serialize</function> (
- <parameter>expression</parameter> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional>
- <optional> <literal>RETURNING</literal> <parameter>data_type</parameter> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
- </para>
- <para>
- Transforms an SQL/JSON value into a character or binary string. The
- <parameter>expression</parameter> can be of any JSON type, any
- character string type, or <type>bytea</type> in UTF8 encoding.
- The returned type can be any character string type or
- <type>bytea</type>. The default is <type>text</type>.
- </para>
- <para>
- <literal>json_serialize('{ "a" : 1 } ' RETURNING bytea)</literal>
- <returnvalue>\x7b20226122203a2031207d20</returnvalue>
- </para></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
- <para>
- <xref linkend="functions-sqljson-querying"/> details the SQL/JSON
- functions that can be used to query JSON data, except
- for <function>json_table</function>.
- </para>
-
- <note>
- <para>
- SQL/JSON paths can only be applied to the <type>jsonb</type> type, so it
- might be necessary to cast the <parameter>context_item</parameter>
- argument of these functions to <type>jsonb</type>.
- </para>
- </note>
-
- <table id="functions-sqljson-querying">
- <title>SQL/JSON Query Functions</title>
- <tgroup cols="1">
- <thead>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- Function signature
- </para>
- <para>
- Description
- </para>
- <para>
- Example(s)
- </para></entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm><primary>json_exists</primary></indexterm>
- <function>json_exists</function> (
- <parameter>context_item</parameter>, <parameter>path_expression</parameter> <optional> <literal>PASSING</literal> { <parameter>value</parameter> <literal>AS</literal> <replaceable>varname</replaceable> } <optional>, ...</optional></optional>
- <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> </optional>
- <optional> { <literal>TRUE</literal> | <literal>FALSE</literal> |<literal> UNKNOWN</literal> | <literal>ERROR</literal> } <literal>ON ERROR</literal> </optional>)
- </para>
- <para>
- Returns true if the SQL/JSON <parameter>path_expression</parameter>
- applied to the <parameter>context_item</parameter> using the
- <parameter>value</parameter>s yields any items.
- The <literal>ON ERROR</literal> clause specifies what is returned if
- an error occurs. Note that if the <parameter>path_expression</parameter>
- is <literal>strict</literal>, an error is generated if it yields no items.
- The default value is <literal>UNKNOWN</literal> which causes a NULL
- result.
- </para>
- <para>
- <literal>json_exists(jsonb '{"key1": [1,2,3]}', 'strict $.key1[*] ? (@ > 2)')</literal>
- <returnvalue>t</returnvalue>
- </para>
- <para>
- <literal>json_exists(jsonb '{"a": [1,2,3]}', 'lax $.a[5]' ERROR ON ERROR)</literal>
- <returnvalue>f</returnvalue>
- </para>
- <para>
- <literal>json_exists(jsonb '{"a": [1,2,3]}', 'strict $.a[5]' ERROR ON ERROR)</literal>
- <returnvalue>ERROR: jsonpath array subscript is out of bounds</returnvalue>
- </para></entry>
- </row>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm><primary>json_value</primary></indexterm>
- <function>json_value</function> (
- <parameter>context_item</parameter>, <parameter>path_expression</parameter>
- <optional> <literal>PASSING</literal> { <parameter>value</parameter> <literal>AS</literal> <parameter>varname</parameter> } <optional>, ...</optional></optional>
- <optional> <literal>RETURNING</literal> <parameter>data_type</parameter> </optional>
- <optional> { <literal>ERROR</literal> | <literal>NULL</literal> | <literal>DEFAULT</literal> <parameter>expression</parameter> } <literal>ON EMPTY</literal> </optional>
- <optional> { <literal>ERROR</literal> | <literal>NULL</literal> | <literal>DEFAULT</literal> <parameter>expression</parameter> } <literal>ON ERROR</literal> </optional>)
- </para>
- <para>
- Returns the result of applying the
- <parameter>path_expression</parameter> to the
- <parameter>context_item</parameter> using the
- <parameter>value</parameter>s. The extracted value must be
- a single <acronym>SQL/JSON</acronym> scalar item. For results that
- are objects or arrays, use the <function>json_query</function>
- instead.
- The returned <parameter>data_type</parameter> has the same semantics
- as for constructor functions like <function>json_objectagg</function>.
- The default returned type is <type>text</type>.
- The <literal>ON EMPTY</literal> clause specifies the behavior if the
- <parameter>path_expression</parameter> yields no value at all.
- The <literal>ON ERROR</literal> clause specifies the behavior if an
- error occurs, as a result of either the evaluation or the application
- of the <literal>ON EMPTY</literal> clause.
- </para>
- <para>
- <literal>json_value(jsonb '"123.45"', '$' RETURNING float)</literal>
- <returnvalue>123.45</returnvalue>
- </para>
- <para>
- <literal>json_value(jsonb '"03:04 2015-02-01"', '$.datetime("HH24:MI YYYY-MM-DD")' RETURNING date)</literal>
- <returnvalue>2015-02-01</returnvalue>
- </para>
- <para>
- <literal>json_value(jsonb '[1,2]', 'strict $[*]' DEFAULT 9 ON ERROR)</literal>
- <returnvalue>9</returnvalue>
- </para></entry>
- </row>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm><primary>json_query</primary></indexterm>
- <function>json_query</function> (
- <parameter>context_item</parameter>, <parameter>path_expression</parameter> <optional> <literal>PASSING</literal> { <parameter>value</parameter> <literal>AS</literal> <parameter>varname</parameter> } <optional>, ...</optional></optional>
- <optional> <literal>RETURNING</literal> <parameter>data_type</parameter> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>
- <optional> { <literal>WITHOUT</literal> | <literal>WITH</literal> { <literal>CONDITIONAL</literal> | <optional><literal>UNCONDITIONAL</literal></optional> } } <optional> <literal>ARRAY</literal> </optional> <literal>WRAPPER</literal> </optional>
- <optional> { <literal>KEEP</literal> | <literal>OMIT</literal> } <literal>QUOTES</literal> <optional> <literal>ON SCALAR STRING</literal> </optional> </optional>
- <optional> { <literal>ERROR</literal> | <literal>NULL</literal> | <literal>EMPTY</literal> { <optional> <literal>ARRAY</literal> </optional> | <literal>OBJECT</literal> } | <literal>DEFAULT</literal> <parameter>expression</parameter> } <literal>ON EMPTY</literal> </optional>
- <optional> { <literal>ERROR</literal> | <literal>NULL</literal> | <literal>EMPTY</literal> { <optional> <literal>ARRAY</literal> </optional> | <literal>OBJECT</literal> } | <literal>DEFAULT</literal> <parameter>expression</parameter> } <literal>ON ERROR</literal> </optional>)
- </para>
- <para>
- Returns the result of applying the
- <parameter>path_expression</parameter> to the
- <parameter>context_item</parameter> using the
- <parameter>value</parameter>s.
- This function must return a JSON string, so if the path expression
- returns multiple SQL/JSON items, you must wrap the result using the
- <literal>WITH WRAPPER</literal> clause. If the wrapper is
- <literal>UNCONDITIONAL</literal>, an array wrapper will always
- be applied, even if the returned value is already a single JSON object
- or array, but if it is <literal>CONDITIONAL</literal> it will not be
- applied to a single array or object. <literal>UNCONDITIONAL</literal>
- is the default.
- If the result is a scalar string, by default the value returned will have
- surrounding quotes making it a valid JSON value. However, this behavior
- is reversed if <literal>OMIT QUOTES</literal> is specified.
- The <literal>ON ERROR</literal> and <literal>ON EMPTY</literal>
- clauses have similar semantics to those clauses for
- <function>json_value</function>.
- The returned <parameter>data_type</parameter> has the same semantics
- as for constructor functions like <function>json_objectagg</function>.
- The default returned type is <type>text</type>.
- </para>
- <para>
- <literal>json_query(jsonb '[1,[2,3],null]', 'lax $[*][1]' WITH CONDITIONAL WRAPPER)</literal>
- <returnvalue>[3]</returnvalue>
- </para></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
- </sect2>
-
- <sect2 id="functions-sqljson-table">
- <title>JSON_TABLE</title>
- <indexterm>
- <primary>json_table</primary>
- </indexterm>
-
- <para>
- <function>json_table</function> is an SQL/JSON function which
- queries <acronym>JSON</acronym> data
- and presents the results as a relational view, which can be accessed as a
- regular SQL table. You can only use <function>json_table</function> inside the
- <literal>FROM</literal> clause of a <literal>SELECT</literal> statement.
- </para>
-
- <para>
- Taking JSON data as input, <function>json_table</function> uses
- a path expression to extract a part of the provided data that
- will be used as a <firstterm>row pattern</firstterm> for the
- constructed view. Each SQL/JSON item at the top level of the row pattern serves
- as the source for a separate row in the constructed relational view.
- </para>
-
- <para>
- To split the row pattern into columns, <function>json_table</function>
- provides the <literal>COLUMNS</literal> clause that defines the
- schema of the created view. For each column to be constructed,
- this clause provides a separate path expression that evaluates
- the row pattern, extracts a JSON item, and returns it as a
- separate SQL value for the specified column. If the required value
- is stored in a nested level of the row pattern, it can be extracted
- using the <literal>NESTED PATH</literal> subclause. Joining the
- columns returned by <literal>NESTED PATH</literal> can add multiple
- new rows to the constructed view. Such rows are called
- <firstterm>child rows</firstterm>, as opposed to the <firstterm>parent row</firstterm>
- that generates them.
- </para>
-
- <para>
- The rows produced by <function>JSON_TABLE</function> are laterally
- joined to the row that generated them, so you do not have to explicitly join
- the constructed view with the original table holding <acronym>JSON</acronym>
- data. Optionally, you can specify how to join the columns returned
- by <literal>NESTED PATH</literal> using the <literal>PLAN</literal> clause.
- </para>
-
- <para>
- Each <literal>NESTED PATH</literal> clause can generate one or more
- columns. Columns produced by <literal>NESTED PATH</literal>s at the
- same level are considered to be <firstterm>siblings</firstterm>,
- while a column produced by a <literal>NESTED PATH</literal> is
- considered to be a child of the column produced by and
- <literal>NESTED PATH</literal> or row expression at a higher level.
- Sibling columns are always joined first. Once they are processed,
- the resulting rows are joined to the parent row.
- </para>
-
- <variablelist>
- <varlistentry>
- <term>
- <literal><parameter>context_item</parameter>, <parameter>path_expression</parameter> <optional> <literal>AS</literal> <parameter>json_path_name</parameter> </optional> <optional> <literal>PASSING</literal> { <parameter>value</parameter> <literal>AS</literal> <parameter>varname</parameter> } <optional>, ...</optional></optional></literal>
- </term>
- <listitem>
- <para>
- The input data to query, the JSON path expression defining the query,
- and an optional <literal>PASSING</literal> clause, which can provide data
- values to the <parameter>path_expression</parameter>.
- The result of the input data
- evaluation is called the <firstterm>row pattern</firstterm>. The row
- pattern is used as the source for row values in the constructed view.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <literal>COLUMNS</literal>( <parameter>json_table_column</parameter> <optional>, ...</optional> )
- </term>
- <listitem>
-
- <para>
- The <literal>COLUMNS</literal> clause defining the schema of the
- constructed view. In this clause, you must specify all the columns
- to be filled with SQL/JSON items.
- The <parameter>json_table_column</parameter>
- expression has the following syntax variants:
- </para>
-
- <variablelist>
- <varlistentry>
- <term>
- <literal><parameter>name</parameter> <parameter>type</parameter>
- <optional> <literal>PATH</literal> <parameter>json_path_specification</parameter> </optional></literal>
- </term>
- <listitem>
-
- <para>
- Inserts a single SQL/JSON item into each row of
- the specified column.
- </para>
- <para>
- The provided <literal>PATH</literal> expression parses the
- row pattern defined by <parameter>json_api_common_syntax</parameter>
- and fills the column with produced SQL/JSON items, one for each row.
- If the <literal>PATH</literal> expression is omitted,
- <function>JSON_TABLE</function> uses the
- <literal>$.<replaceable>name</replaceable></literal> path expression,
- where <replaceable>name</replaceable> is the provided column name.
- In this case, the column name must correspond to one of the
- keys within the SQL/JSON item produced by the row pattern.
- </para>
- <para>
- Optionally, you can add <literal>ON EMPTY</literal> and
- <literal>ON ERROR</literal> clauses to define how to handle missing values
- or structural errors.
- <literal>WRAPPER</literal> and <literal>QUOTES</literal> clauses can only
- be used with JSON, array, and composite types.
- These clauses have the same syntax and semantics as for
- <function>json_value</function> and <function>json_query</function>.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <parameter>name</parameter> <parameter>type</parameter> <literal>FORMAT</literal> <parameter>json_representation</parameter>
- <optional> <literal>PATH</literal> <parameter>json_path_specification</parameter> </optional>
- </term>
- <listitem>
-
- <para>
- Generates a column and inserts a composite SQL/JSON
- item into each row of this column.
- </para>
- <para>
- The provided <literal>PATH</literal> expression parses the
- row pattern defined by <parameter>json_api_common_syntax</parameter>
- and fills the column with produced SQL/JSON items, one for each row.
- If the <literal>PATH</literal> expression is omitted,
- <function>JSON_TABLE</function> uses the
- <literal>$.<parameter>name</parameter></literal> path expression,
- where <parameter>name</parameter> is the provided column name.
- In this case, the column name must correspond to one of the
- keys within the SQL/JSON item produced by the row pattern.
- </para>
- <para>
- Optionally, you can add <literal>WRAPPER</literal>, <literal>QUOTES</literal>,
- <literal>ON EMPTY</literal> and <literal>ON ERROR</literal> clauses
- to define additional settings for the returned SQL/JSON items.
- These clauses have the same syntax and semantics as
- for <function>json_query</function>.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <parameter>name</parameter> <parameter>type</parameter>
- <literal>EXISTS</literal> <optional> <literal>PATH</literal> <parameter>json_path_specification</parameter> </optional>
- </term>
- <listitem>
-
- <para>
- Generates a column and inserts a boolean item into each row of this column.
- </para>
- <para>
- The provided <literal>PATH</literal> expression parses the
- row pattern defined by <parameter>json_api_common_syntax</parameter>,
- checks whether any SQL/JSON items were returned, and fills the column with
- resulting boolean value, one for each row.
- The specified <parameter>type</parameter> should have cast from
- <type>boolean</type>.
- If the <literal>PATH</literal> expression is omitted,
- <function>JSON_TABLE</function> uses the
- <literal>$.<replaceable>name</replaceable></literal> path expression,
- where <replaceable>name</replaceable> is the provided column name.
- </para>
- <para>
- Optionally, you can add <literal>ON ERROR</literal> clause to define
- error behavior. This clause has the same syntax and semantics as
- for <function>json_exists</function>.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <literal>NESTED PATH</literal> <parameter>json_path_specification</parameter> <optional> <literal>AS</literal> <parameter>json_path_name</parameter> </optional>
- <literal>COLUMNS</literal> ( <parameter>json_table_column</parameter> <optional>, ...</optional> )
- </term>
- <listitem>
-
- <para>
- Extracts SQL/JSON items from nested levels of the row pattern,
- generates one or more columns as defined by the <literal>COLUMNS</literal>
- subclause, and inserts the extracted SQL/JSON items into each row of these columns.
- The <parameter>json_table_column</parameter> expression in the
- <literal>COLUMNS</literal> subclause uses the same syntax as in the
- parent <literal>COLUMNS</literal> clause.
- </para>
-
- <para>
- The <literal>NESTED PATH</literal> syntax is recursive,
- so you can go down multiple nested levels by specifying several
- <literal>NESTED PATH</literal> subclauses within each other.
- It allows to unnest the hierarchy of JSON objects and arrays
- in a single function invocation rather than chaining several
- <function>JSON_TABLE</function> expressions in an SQL statement.
- </para>
-
- <para>
- You can use the <literal>PLAN</literal> clause to define how
- to join the columns returned by <parameter>NESTED PATH</parameter> clauses.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <parameter>name</parameter> <literal>FOR ORDINALITY</literal>
- </term>
- <listitem>
-
- <para>
- Adds an ordinality column that provides sequential row numbering.
- You can have only one ordinality column per table. Row numbering
- is 1-based. For child rows that result from the <literal>NESTED PATH</literal>
- clauses, the parent row number is repeated.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
-
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <literal>AS</literal> <parameter>json_path_name</parameter>
- </term>
- <listitem>
-
- <para>
- The optional <parameter>json_path_name</parameter> serves as an
- identifier of the provided <parameter>json_path_specification</parameter>.
- The path name must be unique and distinct from the column names.
- When using the <literal>PLAN</literal> clause, you must specify the names
- for all the paths, including the row pattern. Each path name can appear in
- the <literal>PLAN</literal> clause only once.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <literal>PLAN</literal> ( <parameter>json_table_plan</parameter> )
- </term>
- <listitem>
-
- <para>
- Defines how to join the data returned by <literal>NESTED PATH</literal>
- clauses to the constructed view.
- </para>
- <para>
- To join columns with parent/child relationship, you can use:
- </para>
- <variablelist>
- <varlistentry>
- <term>
- <literal>INNER</literal>
- </term>
- <listitem>
-
- <para>
- Use <literal>INNER JOIN</literal>, so that the parent row
- is omitted from the output if it does not have any child rows
- after joining the data returned by <literal>NESTED PATH</literal>.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <literal>OUTER</literal>
- </term>
- <listitem>
-
- <para>
- Use <literal>LEFT OUTER JOIN</literal>, so that the parent row
- is always included into the output even if it does not have any child rows
- after joining the data returned by <literal>NESTED PATH</literal>, with NULL values
- inserted into the child columns if the corresponding
- values are missing.
- </para>
- <para>
- This is the default option for joining columns with parent/child relationship.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
-
- <para>
- To join sibling columns, you can use:
- </para>
-
- <variablelist>
- <varlistentry>
- <term>
- <literal>UNION</literal>
- </term>
- <listitem>
-
- <para>
- Generate one row for each value produced by each of the sibling
- columns. The columns from the other siblings are set to null.
- </para>
- <para>
- This is the default option for joining sibling columns.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <literal>CROSS</literal>
- </term>
- <listitem>
-
- <para>
- Generate one row for each combination of values from the sibling columns.
- </para>
- </listitem>
- </varlistentry>
-
- </variablelist>
-
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <literal>PLAN DEFAULT</literal> ( <replaceable>OUTER | INNER</replaceable> <optional>, <replaceable>UNION | CROSS</replaceable> </optional> )
- </term>
- <listitem>
- <para>
- The terms can also be specified in reverse order. The
- <literal>INNER</literal> or <literal>OUTER</literal> option defines the
- joining plan for parent/child columns, while <literal>UNION</literal> or
- <literal>CROSS</literal> affects joins of sibling columns. This form
- of <literal>PLAN</literal> overrides the default plan for
- all columns at once. Even though the path names are not included in the
- <literal>PLAN DEFAULT</literal> form, to conform to the SQL/JSON standard
- they must be provided for all the paths if the <literal>PLAN</literal>
- clause is used.
- </para>
- <para>
- <literal>PLAN DEFAULT</literal> is simpler than specifying a complete
- <literal>PLAN</literal>, and is often all that is required to get the desired
- output.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
-
- <para>Examples</para>
-
- <para>
- In these examples the following small table storing some JSON data will be used:
-<programlisting>
-CREATE TABLE my_films ( js jsonb );
-
-INSERT INTO my_films VALUES (
-'{ "favorites" : [
- { "kind" : "comedy", "films" : [
- { "title" : "Bananas",
- "director" : "Woody Allen"},
- { "title" : "The Dinner Game",
- "director" : "Francis Veber" } ] },
- { "kind" : "horror", "films" : [
- { "title" : "Psycho",
- "director" : "Alfred Hitchcock" } ] },
- { "kind" : "thriller", "films" : [
- { "title" : "Vertigo",
- "director" : "Alfred Hitchcock" } ] },
- { "kind" : "drama", "films" : [
- { "title" : "Yojimbo",
- "director" : "Akira Kurosawa" } ] }
- ] }');
-</programlisting>
- </para>
- <para>
- Query the <structname>my_films</structname> table holding
- some JSON data about the films and create a view that
- distributes the film genre, title, and director between separate columns:
-<screen>
-SELECT jt.* FROM
- my_films,
- JSON_TABLE ( js, '$.favorites[*]' COLUMNS (
- id FOR ORDINALITY,
- kind text PATH '$.kind',
- NESTED PATH '$.films[*]' COLUMNS (
- title text PATH '$.title',
- director text PATH '$.director'))) AS jt;
-----+----------+------------------+-------------------
- id | kind | title | director
-----+----------+------------------+-------------------
- 1 | comedy | Bananas | Woody Allen
- 1 | comedy | The Dinner Game | Francis Veber
- 2 | horror | Psycho | Alfred Hitchcock
- 3 | thriller | Vertigo | Alfred Hitchcock
- 4 | drama | Yojimbo | Akira Kurosawa
- (5 rows)
-</screen>
- </para>
-
- <para>
- Find a director that has done films in two different genres:
-<screen>
-SELECT
- director1 AS director, title1, kind1, title2, kind2
-FROM
- my_films,
- JSON_TABLE ( js, '$.favorites' AS favs COLUMNS (
- NESTED PATH '$[*]' AS films1 COLUMNS (
- kind1 text PATH '$.kind',
- NESTED PATH '$.films[*]' AS film1 COLUMNS (
- title1 text PATH '$.title',
- director1 text PATH '$.director')
- ),
- NESTED PATH '$[*]' AS films2 COLUMNS (
- kind2 text PATH '$.kind',
- NESTED PATH '$.films[*]' AS film2 COLUMNS (
- title2 text PATH '$.title',
- director2 text PATH '$.director'
- )
- )
- )
- PLAN (favs OUTER ((films1 INNER film1) CROSS (films2 INNER film2)))
- ) AS jt
- WHERE kind1 > kind2 AND director1 = director2;
-
- director | title1 | kind1 | title2 | kind2
-------------------+---------+----------+--------+--------
- Alfred Hitchcock | Vertigo | thriller | Psycho | horror
-(1 row)
-</screen>
- </para>
- </sect2>
-
+ </sect2>
</sect1>
<sect1 id="functions-sequence">
<entry>No</entry>
</row>
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm>
- <primary>json_agg_strict</primary>
- </indexterm>
- <function>json_agg_strict</function> ( <type>anyelement</type> )
- <returnvalue>json</returnvalue>
- </para>
- <para role="func_signature">
- <indexterm>
- <primary>jsonb_agg_strict</primary>
- </indexterm>
- <function>jsonb_agg_strict</function> ( <type>anyelement</type> )
- <returnvalue>jsonb</returnvalue>
- </para>
- <para>
- Collects all the input values, skipping nulls, into a JSON array.
- Values are converted to JSON as per <function>to_json</function>
- or <function>to_jsonb</function>.
- </para></entry>
- <entry>No</entry>
- </row>
-
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
</para>
<para>
Collects all the key/value pairs into a JSON object. Key arguments
- are coerced to text; value arguments are converted as per
- <function>to_json</function> or <function>to_jsonb</function>
- Values can be null, but keys cannot.
- </para></entry>
- <entry>No</entry>
- </row>
-
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm>
- <primary>json_object_agg_strict</primary>
- </indexterm>
- <function>json_object_agg_strict</function> (
- <parameter>key</parameter> <type>"any"</type>,
- <parameter>value</parameter> <type>"any"</type> )
- <returnvalue>json</returnvalue>
- </para>
- <para role="func_signature">
- <indexterm>
- <primary>jsonb_object_agg_strict</primary>
- </indexterm>
- <function>jsonb_object_agg_strict</function> (
- <parameter>key</parameter> <type>"any"</type>,
- <parameter>value</parameter> <type>"any"</type> )
- <returnvalue>jsonb</returnvalue>
- </para>
- <para>
- Collects all the key/value pairs into a JSON object. Key arguments
- are coerced to text; value arguments are converted as per
- <function>to_json</function> or <function>to_jsonb</function>.
- The <parameter>key</parameter> can not be null. If the
- <parameter>value</parameter> is null then the entry is skipped,
- </para></entry>
- <entry>No</entry>
- </row>
-
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm>
- <primary>json_object_agg_unique</primary>
- </indexterm>
- <function>json_object_agg_unique</function> (
- <parameter>key</parameter> <type>"any"</type>,
- <parameter>value</parameter> <type>"any"</type> )
- <returnvalue>json</returnvalue>
- </para>
- <para role="func_signature">
- <indexterm>
- <primary>jsonb_object_agg_unique</primary>
- </indexterm>
- <function>jsonb_object_agg_unique</function> (
- <parameter>key</parameter> <type>"any"</type>,
- <parameter>value</parameter> <type>"any"</type> )
- <returnvalue>jsonb</returnvalue>
- </para>
- <para>
- Collects all the key/value pairs into a JSON object. Key arguments
- are coerced to text; value arguments are converted as per
- <function>to_json</function> or <function>to_jsonb</function>.
- Values can be null, but keys cannot.
- If there is a duplicate key an error is thrown.
- </para></entry>
- <entry>No</entry>
- </row>
-
- <row>
- <entry role="func_table_entry"><para role="func_signature">
- <indexterm>
- <primary>json_object_agg_unique_strict</primary>
- </indexterm>
- <function>json_object_agg_unique_strict</function> (
- <parameter>key</parameter> <type>"any"</type>,
- <parameter>value</parameter> <type>"any"</type> )
- <returnvalue>json</returnvalue>
- </para>
- <para role="func_signature">
- <indexterm>
- <primary>jsonb_object_agg_unique_strict</primary>
- </indexterm>
- <function>jsonb_object_agg_unique_strict</function> (
- <parameter>key</parameter> <type>"any"</type>,
- <parameter>value</parameter> <type>"any"</type> )
- <returnvalue>jsonb</returnvalue>
- </para>
- <para>
- Collects all the key/value pairs into a JSON object. Key arguments
- are coerced to text; value arguments are converted as per
- <function>to_json</function> or <function>to_jsonb</function>.
- The <parameter>key</parameter> can not be null. If the
- <parameter>value</parameter> is null then the entry is skipped,
- If there is a duplicate key an error is thrown.
+ are coerced to text; value arguments are converted as
+ per <function>to_json</function> or <function>to_jsonb</function>.
+ Values can be null, but not keys.
</para></entry>
<entry>No</entry>
</row>
<para>
The aggregate functions <function>array_agg</function>,
<function>json_agg</function>, <function>jsonb_agg</function>,
- <function>json_agg_strict</function>, <function>jsonb_agg_strict</function>,
<function>json_object_agg</function>, <function>jsonb_object_agg</function>,
- <function>json_object_agg_strict</function>, <function>jsonb_object_agg_strict</function>,
- <function>json_object_agg_unique</function>, <function>jsonb_object_agg_unique</function>,
- <function>json_object_agg_unique_strict</function>,
- <function>jsonb_object_agg_unique_strict</function>,
<function>string_agg</function>,
and <function>xmlagg</function>, as well as similar user-defined
aggregate functions, produce meaningfully different result values
subquery's output to be reordered before the aggregate is computed.
</para>
- <note>
- <para>
- In addition to the JSON aggregates shown here, see the <function>json_objectagg</function>
- and <function>json_arrayagg</function> constructors in <xref linkend="functions-sqljson"/>.
- </para>
- </note>
-
<note>
<indexterm>
<primary>ANY</primary>
INTO
IS
JOIN
-JSON
JSON_ARRAY
JSON_ARRAYAGG
JSON_EXISTS
T653 SQL-schema statements in external routines YES
T654 SQL-dynamic statements in external routines NO
T655 Cyclically dependent routines YES
-T811 Basic SQL/JSON constructor functions YES
-T812 SQL/JSON: JSON_OBJECTAGG YES
-T813 SQL/JSON: JSON_ARRAYAGG with ORDER BY YES
-T814 Colon in JSON_OBJECT or JSON_OBJECTAGG YES
-T821 Basic SQL/JSON query operators YES
-T822 SQL/JSON: IS JSON WITH UNIQUE KEYS predicate YES
-T823 SQL/JSON: PASSING clause YES
-T824 JSON_TABLE: specific PLAN clause YES
-T825 SQL/JSON: ON EMPTY and ON ERROR clauses YES
-T826 General value expression in ON ERROR or ON EMPTY clauses YES
-T827 JSON_TABLE: sibling NESTED COLUMNS clauses YES
-T828 JSON_QUERY YES
-T829 JSON_QUERY: array wrapper options YES
-T830 Enforcing unique keys in SQL/JSON constructor functions YES
+T811 Basic SQL/JSON constructor functions NO
+T812 SQL/JSON: JSON_OBJECTAGG NO
+T813 SQL/JSON: JSON_ARRAYAGG with ORDER BY NO
+T814 Colon in JSON_OBJECT or JSON_OBJECTAGG NO
+T821 Basic SQL/JSON query operators NO
+T822 SQL/JSON: IS JSON WITH UNIQUE KEYS predicate NO
+T823 SQL/JSON: PASSING clause NO
+T824 JSON_TABLE: specific PLAN clause NO
+T825 SQL/JSON: ON EMPTY and ON ERROR clauses NO
+T826 General value expression in ON ERROR or ON EMPTY clauses NO
+T827 JSON_TABLE: sibling NESTED COLUMNS clauses NO
+T828 JSON_QUERY NO
+T829 JSON_QUERY: array wrapper options NO
+T830 Enforcing unique keys in SQL/JSON constructor functions NO
T831 SQL/JSON path language: strict mode YES
T832 SQL/JSON path language: item method YES
T833 SQL/JSON path language: multiple subscripts YES
T835 SQL/JSON path language: filter expressions YES
T836 SQL/JSON path language: starts with predicate YES
T837 SQL/JSON path language: regex_like predicate YES
-T838 JSON_TABLE: PLAN DEFAULT clause YES
+T838 JSON_TABLE: PLAN DEFAULT clause NO
T839 Formatted cast of datetimes to/from character strings NO
M001 Datalinks NO
M002 Datalinks via SQL/CLI NO
break;
case T_TableFuncScan:
Assert(rte->rtekind == RTE_TABLEFUNC);
- if (rte->tablefunc)
- if (rte->tablefunc->functype == TFT_XMLTABLE)
- objectname = "xmltable";
- else /* Must be TFT_JSON_TABLE */
- objectname = "json_table";
- else
- objectname = NULL;
+ objectname = "xmltable";
objecttag = "Table Function Name";
break;
case T_ValuesScan:
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
-#include "utils/json.h"
-#include "utils/jsonb.h"
-#include "utils/jsonpath.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
bool nullcheck);
-static ExprState *
-ExecInitExprInternal(Expr *node, PlanState *parent, ParamListInfo ext_params,
- Datum *caseval, bool *casenull)
-{
- ExprState *state;
- ExprEvalStep scratch = {0};
-
- /* Special case: NULL expression produces a NULL ExprState pointer */
- if (node == NULL)
- return NULL;
-
- /* Initialize ExprState with empty step list */
- state = makeNode(ExprState);
- state->expr = node;
- state->parent = parent;
- state->ext_params = ext_params;
- state->innermost_caseval = caseval;
- state->innermost_casenull = casenull;
-
- /* Insert EEOP_*_FETCHSOME steps as needed */
- ExecInitExprSlots(state, (Node *) node);
-
- /* Compile the expression proper */
- ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
-
- /* Finally, append a DONE step */
- scratch.opcode = EEOP_DONE;
- ExprEvalPushStep(state, &scratch);
-
- ExecReadyExpr(state);
-
- return state;
-}
-
/*
* ExecInitExpr: prepare an expression tree for execution
*
ExprState *
ExecInitExpr(Expr *node, PlanState *parent)
{
- return ExecInitExprInternal(node, parent, NULL, NULL, NULL);
+ ExprState *state;
+ ExprEvalStep scratch = {0};
+
+ /* Special case: NULL expression produces a NULL ExprState pointer */
+ if (node == NULL)
+ return NULL;
+
+ /* Initialize ExprState with empty step list */
+ state = makeNode(ExprState);
+ state->expr = node;
+ state->parent = parent;
+ state->ext_params = NULL;
+
+ /* Insert EEOP_*_FETCHSOME steps as needed */
+ ExecInitExprSlots(state, (Node *) node);
+
+ /* Compile the expression proper */
+ ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+
+ /* Finally, append a DONE step */
+ scratch.opcode = EEOP_DONE;
+ ExprEvalPushStep(state, &scratch);
+
+ ExecReadyExpr(state);
+
+ return state;
}
/*
ExprState *
ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
{
- return ExecInitExprInternal(node, NULL, ext_params, NULL, NULL);
-}
+ ExprState *state;
+ ExprEvalStep scratch = {0};
-/*
- * ExecInitExprWithCaseValue: prepare an expression tree for execution
- *
- * This is the same as ExecInitExpr, except that a pointer to the value for
- * CasTestExpr is passed here.
- */
-ExprState *
-ExecInitExprWithCaseValue(Expr *node, PlanState *parent,
- Datum *caseval, bool *casenull)
-{
- return ExecInitExprInternal(node, parent, NULL, caseval, casenull);
+ /* Special case: NULL expression produces a NULL ExprState pointer */
+ if (node == NULL)
+ return NULL;
+
+ /* Initialize ExprState with empty step list */
+ state = makeNode(ExprState);
+ state->expr = node;
+ state->parent = NULL;
+ state->ext_params = ext_params;
+
+ /* Insert EEOP_*_FETCHSOME steps as needed */
+ ExecInitExprSlots(state, (Node *) node);
+
+ /* Compile the expression proper */
+ ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+
+ /* Finally, append a DONE step */
+ scratch.opcode = EEOP_DONE;
+ ExprEvalPushStep(state, &scratch);
+
+ ExecReadyExpr(state);
+
+ return state;
}
/*
break;
}
- case T_JsonValueExpr:
- {
- JsonValueExpr *jve = (JsonValueExpr *) node;
-
- ExecInitExprRec(jve->raw_expr, state, resv, resnull);
-
- if (jve->formatted_expr)
- {
- Datum *innermost_caseval = state->innermost_caseval;
- bool *innermost_isnull = state->innermost_casenull;
-
- state->innermost_caseval = resv;
- state->innermost_casenull = resnull;
-
- ExecInitExprRec(jve->formatted_expr, state, resv, resnull);
-
- state->innermost_caseval = innermost_caseval;
- state->innermost_casenull = innermost_isnull;
- }
- break;
- }
-
- case T_JsonConstructorExpr:
- {
- JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
- List *args = ctor->args;
- ListCell *lc;
- int nargs = list_length(args);
- int argno = 0;
-
- if (ctor->func)
- {
- ExecInitExprRec(ctor->func, state, resv, resnull);
- }
- else if ((ctor->type == JSCTOR_JSON_PARSE && !ctor->unique) ||
- ctor->type == JSCTOR_JSON_SERIALIZE)
- {
- /* Use the value of the first argument as a result */
- ExecInitExprRec(linitial(args), state, resv, resnull);
- }
- else
- {
- JsonConstructorExprState *jcstate;
-
- jcstate = palloc0(sizeof(JsonConstructorExprState));
-
- scratch.opcode = EEOP_JSON_CONSTRUCTOR;
- scratch.d.json_constructor.jcstate = jcstate;
-
- jcstate->constructor = ctor;
- jcstate->arg_values = palloc(sizeof(Datum) * nargs);
- jcstate->arg_nulls = palloc(sizeof(bool) * nargs);
- jcstate->arg_types = palloc(sizeof(Oid) * nargs);
- jcstate->nargs = nargs;
-
- foreach(lc, args)
- {
- Expr *arg = (Expr *) lfirst(lc);
-
- jcstate->arg_types[argno] = exprType((Node *) arg);
-
- if (IsA(arg, Const))
- {
- /* Don't evaluate const arguments every round */
- Const *con = (Const *) arg;
-
- jcstate->arg_values[argno] = con->constvalue;
- jcstate->arg_nulls[argno] = con->constisnull;
- }
- else
- {
- ExecInitExprRec(arg, state,
- &jcstate->arg_values[argno],
- &jcstate->arg_nulls[argno]);
- }
- argno++;
- }
-
- /* prepare type cache for datum_to_json[b]() */
- if (ctor->type == JSCTOR_JSON_SCALAR)
- {
- bool is_jsonb =
- ctor->returning->format->format_type == JS_FORMAT_JSONB;
-
- jcstate->arg_type_cache =
- palloc(sizeof(*jcstate->arg_type_cache) * nargs);
-
- for (int i = 0; i < nargs; i++)
- {
- int category;
- Oid outfuncid;
- Oid typid = jcstate->arg_types[i];
-
- if (is_jsonb)
- {
- JsonbTypeCategory jbcat;
-
- jsonb_categorize_type(typid, &jbcat, &outfuncid);
-
- category = (int) jbcat;
- }
- else
- {
- JsonTypeCategory jscat;
-
- json_categorize_type(typid, &jscat, &outfuncid);
-
- category = (int) jscat;
- }
-
- jcstate->arg_type_cache[i].outfuncid = outfuncid;
- jcstate->arg_type_cache[i].category = category;
- }
- }
-
- ExprEvalPushStep(state, &scratch);
- }
-
- if (ctor->coercion)
- {
- Datum *innermost_caseval = state->innermost_caseval;
- bool *innermost_isnull = state->innermost_casenull;
-
- state->innermost_caseval = resv;
- state->innermost_casenull = resnull;
-
- ExecInitExprRec(ctor->coercion, state, resv, resnull);
-
- state->innermost_caseval = innermost_caseval;
- state->innermost_casenull = innermost_isnull;
- }
- }
- break;
-
- case T_JsonIsPredicate:
- {
- JsonIsPredicate *pred = (JsonIsPredicate *) node;
-
- ExecInitExprRec((Expr *) pred->expr, state, resv, resnull);
-
- scratch.opcode = EEOP_IS_JSON;
- scratch.d.is_json.pred = pred;
-
- ExprEvalPushStep(state, &scratch);
- break;
- }
-
- case T_JsonExpr:
- {
- JsonExpr *jexpr = castNode(JsonExpr, node);
- JsonExprState *jsestate = palloc0(sizeof(JsonExprState));
- ListCell *argexprlc;
- ListCell *argnamelc;
-
- scratch.opcode = EEOP_JSONEXPR;
- scratch.d.jsonexpr.jsestate = jsestate;
-
- jsestate->jsexpr = jexpr;
-
- jsestate->formatted_expr =
- palloc(sizeof(*jsestate->formatted_expr));
-
- ExecInitExprRec((Expr *) jexpr->formatted_expr, state,
- &jsestate->formatted_expr->value,
- &jsestate->formatted_expr->isnull);
-
- jsestate->pathspec =
- palloc(sizeof(*jsestate->pathspec));
-
- ExecInitExprRec((Expr *) jexpr->path_spec, state,
- &jsestate->pathspec->value,
- &jsestate->pathspec->isnull);
-
- jsestate->res_expr =
- palloc(sizeof(*jsestate->res_expr));
-
- jsestate->result_expr = jexpr->result_coercion
- ? ExecInitExprWithCaseValue((Expr *) jexpr->result_coercion->expr,
- state->parent,
- &jsestate->res_expr->value,
- &jsestate->res_expr->isnull)
- : NULL;
-
- jsestate->default_on_empty = !jexpr->on_empty ? NULL :
- ExecInitExpr((Expr *) jexpr->on_empty->default_expr,
- state->parent);
-
- jsestate->default_on_error =
- ExecInitExpr((Expr *) jexpr->on_error->default_expr,
- state->parent);
-
- if (jexpr->omit_quotes ||
- (jexpr->result_coercion && jexpr->result_coercion->via_io))
- {
- Oid typinput;
-
- /* lookup the result type's input function */
- getTypeInputInfo(jexpr->returning->typid, &typinput,
- &jsestate->input.typioparam);
- fmgr_info(typinput, &jsestate->input.func);
- }
-
- jsestate->args = NIL;
-
- forboth(argexprlc, jexpr->passing_values,
- argnamelc, jexpr->passing_names)
- {
- Expr *argexpr = (Expr *) lfirst(argexprlc);
- String *argname = lfirst_node(String, argnamelc);
- JsonPathVariableEvalContext *var = palloc(sizeof(*var));
-
- var->name = pstrdup(argname->sval);
- var->typid = exprType((Node *) argexpr);
- var->typmod = exprTypmod((Node *) argexpr);
- var->estate = ExecInitExpr(argexpr, state->parent);
- var->econtext = NULL;
- var->mcxt = NULL;
- var->evaluated = false;
- var->value = (Datum) 0;
- var->isnull = true;
-
- jsestate->args =
- lappend(jsestate->args, var);
- }
-
- jsestate->cache = NULL;
-
- if (jexpr->coercions)
- {
- JsonCoercion **coercion;
- struct JsonCoercionState *cstate;
- Datum *caseval;
- bool *casenull;
-
- jsestate->coercion_expr =
- palloc(sizeof(*jsestate->coercion_expr));
-
- caseval = &jsestate->coercion_expr->value;
- casenull = &jsestate->coercion_expr->isnull;
-
- for (cstate = &jsestate->coercions.null,
- coercion = &jexpr->coercions->null;
- coercion <= &jexpr->coercions->composite;
- coercion++, cstate++)
- {
- cstate->coercion = *coercion;
- cstate->estate = *coercion ?
- ExecInitExprWithCaseValue((Expr *) (*coercion)->expr,
- state->parent,
- caseval, casenull) : NULL;
- }
- }
-
- ExprEvalPushStep(state, &scratch);
- break;
- }
-
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
#include "postgres.h"
#include "access/heaptoast.h"
-#include "access/xact.h"
-#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
#include "miscadmin.h"
-#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parsetree.h"
-#include "parser/parse_expr.h"
#include "pgstat.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datum.h"
#include "utils/expandedrecord.h"
-#include "utils/json.h"
-#include "utils/jsonb.h"
-#include "utils/jsonfuncs.h"
-#include "utils/jsonpath.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
-#include "utils/resowner.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
#include "utils/xml.h"
&&CASE_EEOP_GROUPING_FUNC,
&&CASE_EEOP_WINDOW_FUNC,
&&CASE_EEOP_SUBPLAN,
- &&CASE_EEOP_JSON_CONSTRUCTOR,
- &&CASE_EEOP_IS_JSON,
- &&CASE_EEOP_JSONEXPR,
&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
&&CASE_EEOP_AGG_DESERIALIZE,
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
{
/* too complex for an inline implementation */
ExecEvalAggOrderedTransTuple(state, op, econtext);
- EEO_NEXT();
- }
-
- EEO_CASE(EEOP_JSON_CONSTRUCTOR)
- {
- /* too complex for an inline implementation */
- ExecEvalJsonConstructor(state, op, econtext);
- EEO_NEXT();
- }
-
- EEO_CASE(EEOP_IS_JSON)
- {
- /* too complex for an inline implementation */
- ExecEvalJsonIsPredicate(state, op);
- EEO_NEXT();
- }
- EEO_CASE(EEOP_JSONEXPR)
- {
- /* too complex for an inline implementation */
- ExecEvalJson(state, op, econtext);
EEO_NEXT();
}
}
}
-void
-ExecEvalJsonIsPredicate(ExprState *state, ExprEvalStep *op)
-{
- JsonIsPredicate *pred = op->d.is_json.pred;
- Datum js = *op->resvalue;
- Oid exprtype;
- bool res;
-
- if (*op->resnull)
- {
- *op->resvalue = BoolGetDatum(false);
- return;
- }
-
- exprtype = exprType(pred->expr);
-
- if (exprtype == TEXTOID || exprtype == JSONOID)
- {
- text *json = DatumGetTextP(js);
-
- if (pred->item_type == JS_TYPE_ANY)
- res = true;
- else
- {
- switch (json_get_first_token(json, false))
- {
- case JSON_TOKEN_OBJECT_START:
- res = pred->item_type == JS_TYPE_OBJECT;
- break;
- case JSON_TOKEN_ARRAY_START:
- res = pred->item_type == JS_TYPE_ARRAY;
- break;
- case JSON_TOKEN_STRING:
- case JSON_TOKEN_NUMBER:
- case JSON_TOKEN_TRUE:
- case JSON_TOKEN_FALSE:
- case JSON_TOKEN_NULL:
- res = pred->item_type == JS_TYPE_SCALAR;
- break;
- default:
- res = false;
- break;
- }
- }
-
- /*
- * Do full parsing pass only for uniqueness check or for JSON text
- * validation.
- */
- if (res && (pred->unique_keys || exprtype == TEXTOID))
- res = json_validate(json, pred->unique_keys, false);
- }
- else if (exprtype == JSONBOID)
- {
- if (pred->item_type == JS_TYPE_ANY)
- res = true;
- else
- {
- Jsonb *jb = DatumGetJsonbP(js);
-
- switch (pred->item_type)
- {
- case JS_TYPE_OBJECT:
- res = JB_ROOT_IS_OBJECT(jb);
- break;
- case JS_TYPE_ARRAY:
- res = JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb);
- break;
- case JS_TYPE_SCALAR:
- res = JB_ROOT_IS_ARRAY(jb) && JB_ROOT_IS_SCALAR(jb);
- break;
- default:
- res = false;
- break;
- }
- }
-
- /* Key uniqueness check is redundant for jsonb */
- }
- else
- res = false;
-
- *op->resvalue = BoolGetDatum(res);
-}
-
/*
* ExecEvalGroupingFunc
*
MemoryContextSwitchTo(oldContext);
}
-
-/*
- * Evaluate a JSON constructor expression.
- */
-void
-ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
- ExprContext *econtext)
-{
- Datum res;
- JsonConstructorExprState *jcstate = op->d.json_constructor.jcstate;
- JsonConstructorExpr *ctor = jcstate->constructor;
- bool is_jsonb = ctor->returning->format->format_type == JS_FORMAT_JSONB;
- bool isnull = false;
-
- if (ctor->type == JSCTOR_JSON_ARRAY)
- res = (is_jsonb ?
- jsonb_build_array_worker :
- json_build_array_worker) (jcstate->nargs,
- jcstate->arg_values,
- jcstate->arg_nulls,
- jcstate->arg_types,
- ctor->absent_on_null);
- else if (ctor->type == JSCTOR_JSON_OBJECT)
- res = (is_jsonb ?
- jsonb_build_object_worker :
- json_build_object_worker) (jcstate->nargs,
- jcstate->arg_values,
- jcstate->arg_nulls,
- jcstate->arg_types,
- ctor->absent_on_null,
- ctor->unique);
- else if (ctor->type == JSCTOR_JSON_SCALAR)
- {
- if (jcstate->arg_nulls[0])
- {
- res = (Datum) 0;
- isnull = true;
- }
- else
- {
- Datum value = jcstate->arg_values[0];
- int category = jcstate->arg_type_cache[0].category;
- Oid outfuncid = jcstate->arg_type_cache[0].outfuncid;
-
- if (is_jsonb)
- res = to_jsonb_worker(value, category, outfuncid);
- else
- res = to_json_worker(value, category, outfuncid);
- }
- }
- else if (ctor->type == JSCTOR_JSON_PARSE)
- {
- if (jcstate->arg_nulls[0])
- {
- res = (Datum) 0;
- isnull = true;
- }
- else
- {
- Datum value = jcstate->arg_values[0];
- text *js = DatumGetTextP(value);
-
- if (is_jsonb)
- res = jsonb_from_text(js, true);
- else
- {
- (void) json_validate(js, true, true);
- res = value;
- }
- }
- }
- else
- {
- res = (Datum) 0;
- elog(ERROR, "invalid JsonConstructorExpr type %d", ctor->type);
- }
-
- *op->resvalue = res;
- *op->resnull = isnull;
-}
-
-/*
- * Evaluate a JSON error/empty behavior result.
- */
-static Datum
-ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
- ExprState *default_estate, bool *is_null)
-{
- *is_null = false;
-
- switch (behavior->btype)
- {
- case JSON_BEHAVIOR_EMPTY_ARRAY:
- return JsonbPGetDatum(JsonbMakeEmptyArray());
-
- case JSON_BEHAVIOR_EMPTY_OBJECT:
- return JsonbPGetDatum(JsonbMakeEmptyObject());
-
- case JSON_BEHAVIOR_TRUE:
- return BoolGetDatum(true);
-
- case JSON_BEHAVIOR_FALSE:
- return BoolGetDatum(false);
-
- case JSON_BEHAVIOR_NULL:
- case JSON_BEHAVIOR_UNKNOWN:
- case JSON_BEHAVIOR_EMPTY:
- *is_null = true;
- return (Datum) 0;
-
- case JSON_BEHAVIOR_DEFAULT:
- return ExecEvalExpr(default_estate, econtext, is_null);
-
- default:
- elog(ERROR, "unrecognized SQL/JSON behavior %d", behavior->btype);
- return (Datum) 0;
- }
-}
-
-/*
- * Evaluate a coercion of a JSON item to the target type.
- */
-static Datum
-ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
- Datum res, bool *isNull, void *p, bool *error)
-{
- ExprState *estate = p;
- JsonExprState *jsestate;
-
- if (estate) /* coerce using specified expression */
- return ExecEvalExpr(estate, econtext, isNull);
-
- jsestate = op->d.jsonexpr.jsestate;
-
- if (jsestate->jsexpr->op != JSON_EXISTS_OP)
- {
- JsonCoercion *coercion = jsestate->jsexpr->result_coercion;
- JsonExpr *jexpr = jsestate->jsexpr;
- Jsonb *jb = *isNull ? NULL : DatumGetJsonbP(res);
-
- if ((coercion && coercion->via_io) ||
- (jexpr->omit_quotes && !*isNull &&
- JB_ROOT_IS_SCALAR(jb)))
- {
- /* strip quotes and call typinput function */
- char *str = *isNull ? NULL : JsonbUnquote(jb);
-
- return InputFunctionCall(&jsestate->input.func, str,
- jsestate->input.typioparam,
- jexpr->returning->typmod);
- }
- else if (coercion && coercion->via_populate)
- return json_populate_type(res, JSONBOID,
- jexpr->returning->typid,
- jexpr->returning->typmod,
- &jsestate->cache,
- econtext->ecxt_per_query_memory,
- isNull);
- }
-
- if (jsestate->result_expr)
- {
- jsestate->res_expr->value = res;
- jsestate->res_expr->isnull = *isNull;
-
- res = ExecEvalExpr(jsestate->result_expr, econtext, isNull);
- }
-
- return res;
-}
-
-/*
- * Evaluate a JSON path variable caching computed value.
- */
-int
-EvalJsonPathVar(void *cxt, char *varName, int varNameLen,
- JsonbValue *val, JsonbValue *baseObject)
-{
- JsonPathVariableEvalContext *var = NULL;
- List *vars = cxt;
- ListCell *lc;
- int id = 1;
-
- if (!varName)
- return list_length(vars);
-
- foreach(lc, vars)
- {
- var = lfirst(lc);
-
- if (!strncmp(var->name, varName, varNameLen))
- break;
-
- var = NULL;
- id++;
- }
-
- if (!var)
- return -1;
-
- if (!var->evaluated)
- {
- MemoryContext oldcxt = var->mcxt ?
- MemoryContextSwitchTo(var->mcxt) : NULL;
-
- var->value = ExecEvalExpr(var->estate, var->econtext, &var->isnull);
- var->evaluated = true;
-
- if (oldcxt)
- MemoryContextSwitchTo(oldcxt);
- }
-
- if (var->isnull)
- {
- val->type = jbvNull;
- return 0;
- }
-
- JsonItemFromDatum(var->value, var->typid, var->typmod, val);
-
- *baseObject = *val;
- return id;
-}
-
-/*
- * Prepare SQL/JSON item coercion to the output type. Returned a datum of the
- * corresponding SQL type and a pointer to the coercion state.
- */
-Datum
-ExecPrepareJsonItemCoercion(JsonbValue *item,
- JsonReturning *returning,
- struct JsonCoercionsState *coercions,
- struct JsonCoercionState **pcoercion)
-{
- struct JsonCoercionState *coercion;
- Datum res;
- JsonbValue buf;
-
- if (item->type == jbvBinary &&
- JsonContainerIsScalar(item->val.binary.data))
- {
- bool res PG_USED_FOR_ASSERTS_ONLY;
-
- res = JsonbExtractScalar(item->val.binary.data, &buf);
- item = &buf;
- Assert(res);
- }
-
- /* get coercion state reference and datum of the corresponding SQL type */
- switch (item->type)
- {
- case jbvNull:
- coercion = &coercions->null;
- res = (Datum) 0;
- break;
-
- case jbvString:
- coercion = &coercions->string;
- res = PointerGetDatum(cstring_to_text_with_len(item->val.string.val,
- item->val.string.len));
- break;
-
- case jbvNumeric:
- coercion = &coercions->numeric;
- res = NumericGetDatum(item->val.numeric);
- break;
-
- case jbvBool:
- coercion = &coercions->boolean;
- res = BoolGetDatum(item->val.boolean);
- break;
-
- case jbvDatetime:
- res = item->val.datetime.value;
- switch (item->val.datetime.typid)
- {
- case DATEOID:
- coercion = &coercions->date;
- break;
- case TIMEOID:
- coercion = &coercions->time;
- break;
- case TIMETZOID:
- coercion = &coercions->timetz;
- break;
- case TIMESTAMPOID:
- coercion = &coercions->timestamp;
- break;
- case TIMESTAMPTZOID:
- coercion = &coercions->timestamptz;
- break;
- default:
- elog(ERROR, "unexpected jsonb datetime type oid %u",
- item->val.datetime.typid);
- return (Datum) 0;
- }
- break;
-
- case jbvArray:
- case jbvObject:
- case jbvBinary:
- coercion = &coercions->composite;
- res = JsonbPGetDatum(JsonbValueToJsonb(item));
- break;
-
- default:
- elog(ERROR, "unexpected jsonb value type %d", item->type);
- return (Datum) 0;
- }
-
- *pcoercion = coercion;
-
- return res;
-}
-
-typedef Datum (*JsonFunc) (ExprEvalStep *op, ExprContext *econtext,
- Datum item, bool *resnull, void *p, bool *error);
-
-static Datum
-ExecEvalJsonExprSubtrans(JsonFunc func, ExprEvalStep *op,
- ExprContext *econtext,
- Datum res, bool *resnull,
- void *p, bool *error, bool subtrans)
-{
- MemoryContext oldcontext;
- ResourceOwner oldowner;
-
- if (!subtrans)
- /* No need to use subtransactions. */
- return func(op, econtext, res, resnull, p, error);
-
- /*
- * We should catch exceptions of category ERRCODE_DATA_EXCEPTION and
- * execute the corresponding ON ERROR behavior then.
- */
- oldcontext = CurrentMemoryContext;
- oldowner = CurrentResourceOwner;
-
- Assert(error);
-
- BeginInternalSubTransaction(NULL);
- /* Want to execute expressions inside function's memory context */
- MemoryContextSwitchTo(oldcontext);
-
- PG_TRY();
- {
- res = func(op, econtext, res, resnull, p, error);
-
- /* Commit the inner transaction, return to outer xact context */
- ReleaseCurrentSubTransaction();
- MemoryContextSwitchTo(oldcontext);
- CurrentResourceOwner = oldowner;
- }
- PG_CATCH();
- {
- ErrorData *edata;
- int ecategory;
-
- /* Save error info in oldcontext */
- MemoryContextSwitchTo(oldcontext);
- edata = CopyErrorData();
- FlushErrorState();
-
- /* Abort the inner transaction */
- RollbackAndReleaseCurrentSubTransaction();
- MemoryContextSwitchTo(oldcontext);
- CurrentResourceOwner = oldowner;
-
- ecategory = ERRCODE_TO_CATEGORY(edata->sqlerrcode);
-
- if (ecategory != ERRCODE_DATA_EXCEPTION && /* jsonpath and other data
- * errors */
- ecategory != ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION) /* domain errors */
- ReThrowError(edata);
-
- res = (Datum) 0;
- *error = true;
- }
- PG_END_TRY();
-
- return res;
-}
-
-
-typedef struct
-{
- JsonPath *path;
- bool *error;
- bool coercionInSubtrans;
-} ExecEvalJsonExprContext;
-
-static Datum
-ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
- Datum item, bool *resnull, void *pcxt,
- bool *error)
-{
- ExecEvalJsonExprContext *cxt = pcxt;
- JsonPath *path = cxt->path;
- JsonExprState *jsestate = op->d.jsonexpr.jsestate;
- JsonExpr *jexpr = jsestate->jsexpr;
- ExprState *estate = NULL;
- bool empty = false;
- Datum res = (Datum) 0;
-
- switch (jexpr->op)
- {
- case JSON_QUERY_OP:
- res = JsonPathQuery(item, path, jexpr->wrapper, &empty, error,
- jsestate->args);
- if (error && *error)
- {
- *resnull = true;
- return (Datum) 0;
- }
- *resnull = !DatumGetPointer(res);
- break;
-
- case JSON_VALUE_OP:
- {
- struct JsonCoercionState *jcstate;
- JsonbValue *jbv = JsonPathValue(item, path, &empty, error,
- jsestate->args);
-
- if (error && *error)
- return (Datum) 0;
-
- if (!jbv) /* NULL or empty */
- break;
-
- Assert(!empty);
-
- *resnull = false;
-
- /* coerce scalar item to the output type */
- if (jexpr->returning->typid == JSONOID ||
- jexpr->returning->typid == JSONBOID)
- {
- /* Use result coercion from json[b] to the output type */
- res = JsonbPGetDatum(JsonbValueToJsonb(jbv));
- break;
- }
-
- /* Use coercion from SQL/JSON item type to the output type */
- res = ExecPrepareJsonItemCoercion(jbv,
- jsestate->jsexpr->returning,
- &jsestate->coercions,
- &jcstate);
-
- if (jcstate->coercion &&
- (jcstate->coercion->via_io ||
- jcstate->coercion->via_populate))
- {
- if (error)
- {
- *error = true;
- return (Datum) 0;
- }
-
- /*
- * Coercion via I/O means here that the cast to the target
- * type simply does not exist.
- */
- ereport(ERROR,
- (errcode(ERRCODE_SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE),
- errmsg("SQL/JSON item cannot be cast to target type")));
- }
- else if (!jcstate->estate)
- return res; /* no coercion */
-
- /* coerce using specific expression */
- estate = jcstate->estate;
- jsestate->coercion_expr->value = res;
- jsestate->coercion_expr->isnull = *resnull;
- break;
- }
-
- case JSON_EXISTS_OP:
- {
- bool exists = JsonPathExists(item, path,
- jsestate->args,
- error);
-
- *resnull = error && *error;
- res = BoolGetDatum(exists);
-
- if (!jsestate->result_expr)
- return res;
-
- /* coerce using result expression */
- estate = jsestate->result_expr;
- jsestate->res_expr->value = res;
- jsestate->res_expr->isnull = *resnull;
- break;
- }
-
- case JSON_TABLE_OP:
- *resnull = false;
- return item;
-
- default:
- elog(ERROR, "unrecognized SQL/JSON expression op %d", jexpr->op);
- return (Datum) 0;
- }
-
- if (empty)
- {
- Assert(jexpr->on_empty); /* it is not JSON_EXISTS */
-
- if (jexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
- {
- if (error)
- {
- *error = true;
- return (Datum) 0;
- }
-
- ereport(ERROR,
- (errcode(ERRCODE_NO_SQL_JSON_ITEM),
- errmsg("no SQL/JSON item")));
- }
-
- if (jexpr->on_empty->btype == JSON_BEHAVIOR_DEFAULT)
-
- /*
- * Execute DEFAULT expression as a coercion expression, because
- * its result is already coerced to the target type.
- */
- estate = jsestate->default_on_empty;
- else
- /* Execute ON EMPTY behavior */
- res = ExecEvalJsonBehavior(econtext, jexpr->on_empty,
- jsestate->default_on_empty,
- resnull);
- }
-
- return ExecEvalJsonExprSubtrans(ExecEvalJsonExprCoercion, op, econtext,
- res, resnull, estate, error,
- cxt->coercionInSubtrans);
-}
-
-bool
-ExecEvalJsonNeedsSubTransaction(JsonExpr *jsexpr,
- struct JsonCoercionsState *coercions)
-{
- if (jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
- return false;
-
- if (jsexpr->op == JSON_EXISTS_OP && !jsexpr->result_coercion)
- return false;
-
- if (!coercions)
- return true;
-
- return false;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalJson
- * ----------------------------------------------------------------
- */
-void
-ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
-{
- ExecEvalJsonExprContext cxt;
- JsonExprState *jsestate = op->d.jsonexpr.jsestate;
- JsonExpr *jexpr = jsestate->jsexpr;
- Datum item;
- Datum res = (Datum) 0;
- JsonPath *path;
- ListCell *lc;
- bool error = false;
- bool needSubtrans;
- bool throwErrors = jexpr->on_error->btype == JSON_BEHAVIOR_ERROR;
-
- *op->resnull = true; /* until we get a result */
- *op->resvalue = (Datum) 0;
-
- if (jsestate->formatted_expr->isnull || jsestate->pathspec->isnull)
- {
- /* execute domain checks for NULLs */
- (void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull,
- NULL, NULL);
-
- Assert(*op->resnull);
- return;
- }
-
- item = jsestate->formatted_expr->value;
- path = DatumGetJsonPathP(jsestate->pathspec->value);
-
- /* reset JSON path variable contexts */
- foreach(lc, jsestate->args)
- {
- JsonPathVariableEvalContext *var = lfirst(lc);
-
- var->econtext = econtext;
- var->evaluated = false;
- }
-
- needSubtrans = ExecEvalJsonNeedsSubTransaction(jexpr, &jsestate->coercions);
-
- cxt.path = path;
- cxt.error = throwErrors ? NULL : &error;
- cxt.coercionInSubtrans = !needSubtrans && !throwErrors;
- Assert(!needSubtrans || cxt.error);
-
- res = ExecEvalJsonExprSubtrans(ExecEvalJsonExpr, op, econtext, item,
- op->resnull, &cxt, cxt.error,
- needSubtrans);
-
- if (error)
- {
- /* Execute ON ERROR behavior */
- res = ExecEvalJsonBehavior(econtext, jexpr->on_error,
- jsestate->default_on_error,
- op->resnull);
-
- /* result is already coerced in DEFAULT behavior case */
- if (jexpr->on_error->btype != JSON_BEHAVIOR_DEFAULT)
- res = ExecEvalJsonExprCoercion(op, econtext, res,
- op->resnull,
- NULL, NULL);
- }
-
- *op->resvalue = res;
-}
#include "miscadmin.h"
#include "nodes/execnodes.h"
#include "utils/builtins.h"
-#include "utils/jsonpath.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/xml.h"
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
- /* Only XMLTABLE and JSON_TABLE are supported currently */
- scanstate->routine =
- tf->functype == TFT_XMLTABLE ? &XmlTableRoutine : &JsonbTableRoutine;
+ /* Only XMLTABLE is supported currently */
+ scanstate->routine = &XmlTableRoutine;
scanstate->perTableCxt =
AllocSetContextCreate(CurrentMemoryContext,
routine->SetNamespace(tstate, ns_name, ns_uri);
}
- if (routine->SetRowFilter)
- {
- /* Install the row filter expression into the table builder context */
- value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
- if (isnull)
- ereport(ERROR,
- (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("row filter expression must not be null")));
+ /* Install the row filter expression into the table builder context */
+ value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
+ if (isnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("row filter expression must not be null")));
- routine->SetRowFilter(tstate, TextDatumGetCString(value));
- }
+ routine->SetRowFilter(tstate, TextDatumGetCString(value));
/*
* Install the column filter expressions into the table builder context.
LLVMBuildBr(b, opblocks[opno + 1]);
break;
- case EEOP_JSON_CONSTRUCTOR:
- build_EvalXFunc(b, mod, "ExecEvalJsonConstructor",
- v_state, op, v_econtext);
- LLVMBuildBr(b, opblocks[opno + 1]);
- break;
-
- case EEOP_IS_JSON:
- build_EvalXFunc(b, mod, "ExecEvalJsonIsPredicate",
- v_state, op);
- LLVMBuildBr(b, opblocks[opno + 1]);
- break;
-
- case EEOP_JSONEXPR:
- build_EvalXFunc(b, mod, "ExecEvalJson",
- v_state, op, v_econtext);
- LLVMBuildBr(b, opblocks[opno + 1]);
- break;
-
case EEOP_LAST:
Assert(false);
break;
ExecEvalSysVar,
ExecEvalWholeRowVar,
ExecEvalXmlExpr,
- ExecEvalJsonConstructor,
- ExecEvalJsonIsPredicate,
- ExecEvalJson,
MakeExpandedObjectReadOnlyInternal,
slot_getmissingattrs,
slot_getsomeattrs_int,
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
-#include "utils/errcodes.h"
#include "utils/lsyscache.h"
v->va_cols = va_cols;
return v;
}
-
-/*
- * makeJsonFormat -
- * creates a JsonFormat node
- */
-JsonFormat *
-makeJsonFormat(JsonFormatType type, JsonEncoding encoding, int location)
-{
- JsonFormat *jf = makeNode(JsonFormat);
-
- jf->format_type = type;
- jf->encoding = encoding;
- jf->location = location;
-
- return jf;
-}
-
-/*
- * makeJsonValueExpr -
- * creates a JsonValueExpr node
- */
-JsonValueExpr *
-makeJsonValueExpr(Expr *expr, JsonFormat *format)
-{
- JsonValueExpr *jve = makeNode(JsonValueExpr);
-
- jve->raw_expr = expr;
- jve->formatted_expr = NULL;
- jve->format = format;
-
- return jve;
-}
-
-/*
- * makeJsonBehavior -
- * creates a JsonBehavior node
- */
-JsonBehavior *
-makeJsonBehavior(JsonBehaviorType type, Node *default_expr)
-{
- JsonBehavior *behavior = makeNode(JsonBehavior);
-
- behavior->btype = type;
- behavior->default_expr = default_expr;
-
- return behavior;
-}
-
-/*
- * makeJsonTableJoinedPlan -
- * creates a joined JsonTablePlan node
- */
-Node *
-makeJsonTableJoinedPlan(JsonTablePlanJoinType type, Node *plan1, Node *plan2,
- int location)
-{
- JsonTablePlan *n = makeNode(JsonTablePlan);
-
- n->plan_type = JSTP_JOINED;
- n->join_type = type;
- n->plan1 = castNode(JsonTablePlan, plan1);
- n->plan2 = castNode(JsonTablePlan, plan2);
- n->location = location;
-
- return (Node *) n;
-}
-
-/*
- * makeJsonEncoding -
- * converts JSON encoding name to enum JsonEncoding
- */
-JsonEncoding
-makeJsonEncoding(char *name)
-{
- if (!pg_strcasecmp(name, "utf8"))
- return JS_ENC_UTF8;
- if (!pg_strcasecmp(name, "utf16"))
- return JS_ENC_UTF16;
- if (!pg_strcasecmp(name, "utf32"))
- return JS_ENC_UTF32;
-
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized JSON encoding: %s", name)));
-
- return JS_ENC_DEFAULT;
-}
-
-/*
- * makeJsonKeyValue -
- * creates a JsonKeyValue node
- */
-Node *
-makeJsonKeyValue(Node *key, Node *value)
-{
- JsonKeyValue *n = makeNode(JsonKeyValue);
-
- n->key = (Expr *) key;
- n->value = castNode(JsonValueExpr, value);
-
- return (Node *) n;
-}
-
-/*
- * makeJsonIsPredicate -
- * creates a JsonIsPredicate node
- */
-Node *
-makeJsonIsPredicate(Node *expr, JsonFormat *format, JsonValueType item_type,
- bool unique_keys, int location)
-{
- JsonIsPredicate *n = makeNode(JsonIsPredicate);
-
- n->expr = expr;
- n->format = format;
- n->item_type = item_type;
- n->unique_keys = unique_keys;
- n->location = location;
-
- return (Node *) n;
-}
case T_PlaceHolderVar:
type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
break;
- case T_JsonValueExpr:
- {
- const JsonValueExpr *jve = (const JsonValueExpr *) expr;
-
- type = exprType((Node *) (jve->formatted_expr ? jve->formatted_expr : jve->raw_expr));
- }
- break;
- case T_JsonConstructorExpr:
- type = ((const JsonConstructorExpr *) expr)->returning->typid;
- break;
- case T_JsonIsPredicate:
- type = BOOLOID;
- break;
- case T_JsonExpr:
- type = ((const JsonExpr *) expr)->returning->typid;
- break;
- case T_JsonCoercion:
- type = exprType(((const JsonCoercion *) expr)->expr);
- break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
type = InvalidOid; /* keep compiler quiet */
return ((const SetToDefault *) expr)->typeMod;
case T_PlaceHolderVar:
return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
- case T_JsonValueExpr:
- return exprTypmod((Node *) ((const JsonValueExpr *) expr)->formatted_expr);
- case T_JsonConstructorExpr:
- return ((const JsonConstructorExpr *) expr)->returning->typmod;
- case T_JsonExpr:
- return ((JsonExpr *) expr)->returning->typmod;
- case T_JsonCoercion:
- return exprTypmod(((const JsonCoercion *) expr)->expr);
default:
break;
}
case T_PlaceHolderVar:
coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
break;
- case T_JsonValueExpr:
- coll = exprCollation((Node *) ((const JsonValueExpr *) expr)->formatted_expr);
- break;
- case T_JsonConstructorExpr:
- {
- const JsonConstructorExpr *ctor = (const JsonConstructorExpr *) expr;
-
- if (ctor->coercion)
- coll = exprCollation((Node *) ctor->coercion);
- else
- coll = InvalidOid;
- }
- break;
- case T_JsonIsPredicate:
- coll = InvalidOid; /* result is always an boolean type */
- break;
- case T_JsonExpr:
- {
- JsonExpr *jexpr = (JsonExpr *) expr;
- JsonCoercion *coercion = jexpr->result_coercion;
-
- if (!coercion)
- coll = InvalidOid;
- else if (coercion->expr)
- coll = exprCollation(coercion->expr);
- else if (coercion->via_io || coercion->via_populate)
- coll = coercion->collation;
- else
- coll = InvalidOid;
- }
- break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
coll = InvalidOid; /* keep compiler quiet */
/* NextValueExpr's result is an integer type ... */
Assert(!OidIsValid(collation)); /* ... so never set a collation */
break;
- case T_JsonValueExpr:
- exprSetCollation((Node *) ((JsonValueExpr *) expr)->formatted_expr,
- collation);
- break;
- case T_JsonConstructorExpr:
- {
- JsonConstructorExpr *ctor = (JsonConstructorExpr *) expr;
-
- if (ctor->coercion)
- exprSetCollation((Node *) ctor->coercion, collation);
- else
- Assert(!OidIsValid(collation)); /* result is always a
- * json[b] type */
- }
- break;
- case T_JsonIsPredicate:
- Assert(!OidIsValid(collation)); /* result is always boolean */
- break;
- case T_JsonExpr:
- {
- JsonExpr *jexpr = (JsonExpr *) expr;
- JsonCoercion *coercion = jexpr->result_coercion;
-
- if (!coercion)
- Assert(!OidIsValid(collation));
- else if (coercion->expr)
- exprSetCollation(coercion->expr, collation);
- else if (coercion->via_io || coercion->via_populate)
- coercion->collation = collation;
- else
- Assert(!OidIsValid(collation));
- }
- break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
break;
case T_PartitionRangeDatum:
loc = ((const PartitionRangeDatum *) expr)->location;
break;
- case T_JsonValueExpr:
- loc = exprLocation((Node *) ((const JsonValueExpr *) expr)->raw_expr);
- break;
- case T_JsonConstructorExpr:
- loc = ((const JsonConstructorExpr *) expr)->location;
- break;
- case T_JsonIsPredicate:
- loc = ((const JsonIsPredicate *) expr)->location;
- break;
- case T_JsonExpr:
- {
- const JsonExpr *jsexpr = (const JsonExpr *) expr;
-
- /* consider both function name and leftmost arg */
- loc = leftmostLoc(jsexpr->location,
- exprLocation(jsexpr->formatted_expr));
- }
- break;
default:
/* for any other node type it's just unknown... */
loc = -1;
return true;
if (walker(tf->coldefexprs, context))
return true;
- if (walker(tf->colvalexprs, context))
- return true;
- }
- break;
- case T_JsonValueExpr:
- {
- JsonValueExpr *jve = (JsonValueExpr *) node;
-
- if (walker(jve->raw_expr, context))
- return true;
- if (walker(jve->formatted_expr, context))
- return true;
- }
- break;
- case T_JsonConstructorExpr:
- {
- JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
-
- if (walker(ctor->args, context))
- return true;
- if (walker(ctor->func, context))
- return true;
- if (walker(ctor->coercion, context))
- return true;
- }
- break;
- case T_JsonIsPredicate:
- return walker(((JsonIsPredicate *) node)->expr, context);
- case T_JsonExpr:
- {
- JsonExpr *jexpr = (JsonExpr *) node;
-
- if (walker(jexpr->formatted_expr, context))
- return true;
- if (walker(jexpr->result_coercion, context))
- return true;
- if (walker(jexpr->passing_values, context))
- return true;
- /* we assume walker doesn't care about passing_names */
- if (jexpr->on_empty &&
- walker(jexpr->on_empty->default_expr, context))
- return true;
- if (walker(jexpr->on_error->default_expr, context))
- return true;
- if (walker(jexpr->coercions, context))
- return true;
- }
- break;
- case T_JsonCoercion:
- return walker(((JsonCoercion *) node)->expr, context);
- case T_JsonItemCoercions:
- {
- JsonItemCoercions *coercions = (JsonItemCoercions *) node;
-
- if (walker(coercions->null, context))
- return true;
- if (walker(coercions->string, context))
- return true;
- if (walker(coercions->numeric, context))
- return true;
- if (walker(coercions->boolean, context))
- return true;
- if (walker(coercions->date, context))
- return true;
- if (walker(coercions->time, context))
- return true;
- if (walker(coercions->timetz, context))
- return true;
- if (walker(coercions->timestamp, context))
- return true;
- if (walker(coercions->timestamptz, context))
- return true;
- if (walker(coercions->composite, context))
- return true;
}
break;
default:
case T_RangeTblRef:
case T_SortGroupClause:
case T_CTESearchClause:
- case T_JsonFormat:
return (Node *) copyObject(node);
case T_WithCheckOption:
{
MUTATE(newnode->rowexpr, tf->rowexpr, Node *);
MUTATE(newnode->colexprs, tf->colexprs, List *);
MUTATE(newnode->coldefexprs, tf->coldefexprs, List *);
- MUTATE(newnode->colvalexprs, tf->colvalexprs, List *);
- return (Node *) newnode;
- }
- break;
- case T_JsonReturning:
- {
- JsonReturning *jr = (JsonReturning *) node;
- JsonReturning *newnode;
-
- FLATCOPY(newnode, jr, JsonReturning);
- MUTATE(newnode->format, jr->format, JsonFormat *);
-
- return (Node *) newnode;
- }
- case T_JsonValueExpr:
- {
- JsonValueExpr *jve = (JsonValueExpr *) node;
- JsonValueExpr *newnode;
-
- FLATCOPY(newnode, jve, JsonValueExpr);
- MUTATE(newnode->raw_expr, jve->raw_expr, Expr *);
- MUTATE(newnode->formatted_expr, jve->formatted_expr, Expr *);
- MUTATE(newnode->format, jve->format, JsonFormat *);
-
- return (Node *) newnode;
- }
- case T_JsonConstructorExpr:
- {
- JsonConstructorExpr *jve = (JsonConstructorExpr *) node;
- JsonConstructorExpr *newnode;
-
- FLATCOPY(newnode, jve, JsonConstructorExpr);
- MUTATE(newnode->args, jve->args, List *);
- MUTATE(newnode->func, jve->func, Expr *);
- MUTATE(newnode->coercion, jve->coercion, Expr *);
- MUTATE(newnode->returning, jve->returning, JsonReturning *);
-
- return (Node *) newnode;
- }
- break;
- case T_JsonIsPredicate:
- {
- JsonIsPredicate *pred = (JsonIsPredicate *) node;
- JsonIsPredicate *newnode;
-
- FLATCOPY(newnode, pred, JsonIsPredicate);
- MUTATE(newnode->expr, pred->expr, Node *);
-
- return (Node *) newnode;
- }
- break;
- case T_JsonExpr:
- {
- JsonExpr *jexpr = (JsonExpr *) node;
- JsonExpr *newnode;
-
- FLATCOPY(newnode, jexpr, JsonExpr);
- MUTATE(newnode->path_spec, jexpr->path_spec, Node *);
- MUTATE(newnode->formatted_expr, jexpr->formatted_expr, Node *);
- MUTATE(newnode->result_coercion, jexpr->result_coercion, JsonCoercion *);
- MUTATE(newnode->passing_values, jexpr->passing_values, List *);
- /* assume mutator does not care about passing_names */
- if (newnode->on_empty)
- MUTATE(newnode->on_empty->default_expr,
- jexpr->on_empty->default_expr, Node *);
- MUTATE(newnode->on_error->default_expr,
- jexpr->on_error->default_expr, Node *);
- return (Node *) newnode;
- }
- break;
- case T_JsonCoercion:
- {
- JsonCoercion *coercion = (JsonCoercion *) node;
- JsonCoercion *newnode;
-
- FLATCOPY(newnode, coercion, JsonCoercion);
- MUTATE(newnode->expr, coercion->expr, Node *);
- return (Node *) newnode;
- }
- break;
- case T_JsonItemCoercions:
- {
- JsonItemCoercions *coercions = (JsonItemCoercions *) node;
- JsonItemCoercions *newnode;
-
- FLATCOPY(newnode, coercions, JsonItemCoercions);
- MUTATE(newnode->null, coercions->null, JsonCoercion *);
- MUTATE(newnode->string, coercions->string, JsonCoercion *);
- MUTATE(newnode->numeric, coercions->numeric, JsonCoercion *);
- MUTATE(newnode->boolean, coercions->boolean, JsonCoercion *);
- MUTATE(newnode->date, coercions->date, JsonCoercion *);
- MUTATE(newnode->time, coercions->time, JsonCoercion *);
- MUTATE(newnode->timetz, coercions->timetz, JsonCoercion *);
- MUTATE(newnode->timestamp, coercions->timestamp, JsonCoercion *);
- MUTATE(newnode->timestamptz, coercions->timestamptz, JsonCoercion *);
- MUTATE(newnode->composite, coercions->composite, JsonCoercion *);
return (Node *) newnode;
}
break;
case T_ParamRef:
case T_A_Const:
case T_A_Star:
- case T_JsonFormat:
/* primitive node types with no subnodes */
break;
case T_Alias:
case T_CommonTableExpr:
/* search_clause and cycle_clause are not interesting here */
return walker(((CommonTableExpr *) node)->ctequery, context);
- case T_JsonReturning:
- return walker(((JsonReturning *) node)->format, context);
- case T_JsonValueExpr:
- {
- JsonValueExpr *jve = (JsonValueExpr *) node;
-
- if (walker(jve->raw_expr, context))
- return true;
- if (walker(jve->formatted_expr, context))
- return true;
- if (walker(jve->format, context))
- return true;
- }
- break;
- case T_JsonParseExpr:
- {
- JsonParseExpr *jpe = (JsonParseExpr *) node;
-
- if (walker(jpe->expr, context))
- return true;
- if (walker(jpe->output, context))
- return true;
- }
- break;
- case T_JsonScalarExpr:
- {
- JsonScalarExpr *jse = (JsonScalarExpr *) node;
-
- if (walker(jse->expr, context))
- return true;
- if (walker(jse->output, context))
- return true;
- }
- break;
- case T_JsonSerializeExpr:
- {
- JsonSerializeExpr *jse = (JsonSerializeExpr *) node;
-
- if (walker(jse->expr, context))
- return true;
- if (walker(jse->output, context))
- return true;
- }
- break;
- case T_JsonConstructorExpr:
- {
- JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
-
- if (walker(ctor->args, context))
- return true;
- if (walker(ctor->func, context))
- return true;
- if (walker(ctor->coercion, context))
- return true;
- if (walker(ctor->returning, context))
- return true;
- }
- break;
- case T_JsonOutput:
- {
- JsonOutput *out = (JsonOutput *) node;
-
- if (walker(out->typeName, context))
- return true;
- if (walker(out->returning, context))
- return true;
- }
- break;
- case T_JsonKeyValue:
- {
- JsonKeyValue *jkv = (JsonKeyValue *) node;
-
- if (walker(jkv->key, context))
- return true;
- if (walker(jkv->value, context))
- return true;
- }
- break;
- case T_JsonObjectConstructor:
- {
- JsonObjectConstructor *joc = (JsonObjectConstructor *) node;
-
- if (walker(joc->output, context))
- return true;
- if (walker(joc->exprs, context))
- return true;
- }
- break;
- case T_JsonArrayConstructor:
- {
- JsonArrayConstructor *jac = (JsonArrayConstructor *) node;
-
- if (walker(jac->output, context))
- return true;
- if (walker(jac->exprs, context))
- return true;
- }
- break;
- case T_JsonAggConstructor:
- {
- JsonAggConstructor *ctor = (JsonAggConstructor *) node;
-
- if (walker(ctor->output, context))
- return true;
- if (walker(ctor->agg_order, context))
- return true;
- if (walker(ctor->agg_filter, context))
- return true;
- if (walker(ctor->over, context))
- return true;
- }
- break;
- case T_JsonObjectAgg:
- {
- JsonObjectAgg *joa = (JsonObjectAgg *) node;
-
- if (walker(joa->constructor, context))
- return true;
- if (walker(joa->arg, context))
- return true;
- }
- break;
- case T_JsonArrayAgg:
- {
- JsonArrayAgg *jaa = (JsonArrayAgg *) node;
-
- if (walker(jaa->constructor, context))
- return true;
- if (walker(jaa->arg, context))
- return true;
- }
- break;
- case T_JsonArrayQueryConstructor:
- {
- JsonArrayQueryConstructor *jaqc = (JsonArrayQueryConstructor *) node;
-
- if (walker(jaqc->output, context))
- return true;
- if (walker(jaqc->query, context))
- return true;
- }
- break;
- case T_JsonIsPredicate:
- return walker(((JsonIsPredicate *) node)->expr, context);
- case T_JsonArgument:
- return walker(((JsonArgument *) node)->val, context);
- case T_JsonCommon:
- {
- JsonCommon *jc = (JsonCommon *) node;
-
- if (walker(jc->expr, context))
- return true;
- if (walker(jc->pathspec, context))
- return true;
- if (walker(jc->passing, context))
- return true;
- }
- break;
- case T_JsonBehavior:
- {
- JsonBehavior *jb = (JsonBehavior *) node;
-
- if (jb->btype == JSON_BEHAVIOR_DEFAULT &&
- walker(jb->default_expr, context))
- return true;
- }
- break;
- case T_JsonFuncExpr:
- {
- JsonFuncExpr *jfe = (JsonFuncExpr *) node;
-
- if (walker(jfe->common, context))
- return true;
- if (jfe->output && walker(jfe->output, context))
- return true;
- if (walker(jfe->on_empty, context))
- return true;
- if (walker(jfe->on_error, context))
- return true;
- }
- break;
- case T_JsonTable:
- {
- JsonTable *jt = (JsonTable *) node;
-
- if (walker(jt->common, context))
- return true;
- if (walker(jt->columns, context))
- return true;
- }
- break;
- case T_JsonTableColumn:
- {
- JsonTableColumn *jtc = (JsonTableColumn *) node;
-
- if (walker(jtc->typeName, context))
- return true;
- if (walker(jtc->on_empty, context))
- return true;
- if (walker(jtc->on_error, context))
- return true;
- if (jtc->coltype == JTC_NESTED && walker(jtc->columns, context))
- return true;
- }
- break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
IsA(node, SQLValueFunction) ||
IsA(node, XmlExpr) ||
IsA(node, CoerceToDomain) ||
- IsA(node, NextValueExpr) ||
- IsA(node, JsonExpr))
+ IsA(node, NextValueExpr))
{
/* Treat all these as having cost 1 */
context->total.per_tuple += cpu_operator_cost;
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "executor/functions.h"
-#include "executor/execExpr.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
-#include "utils/json.h"
-#include "utils/jsonb.h"
-#include "utils/jsonpath.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
context))
return true;
- if (IsA(node, JsonConstructorExpr))
- {
- const JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
- ListCell *lc;
- bool is_jsonb =
- ctor->returning->format->format_type == JS_FORMAT_JSONB;
-
- /* Check argument_type => json[b] conversions */
- foreach(lc, ctor->args)
- {
- Oid typid = exprType(lfirst(lc));
-
- if (is_jsonb ?
- !to_jsonb_is_immutable(typid) :
- !to_json_is_immutable(typid))
- return true;
- }
-
- /* Check all subnodes */
- }
-
- if (IsA(node, JsonExpr))
- {
- JsonExpr *jexpr = castNode(JsonExpr, node);
- Const *cnst;
-
- if (!IsA(jexpr->path_spec, Const))
- return true;
-
- cnst = castNode(Const, jexpr->path_spec);
-
- Assert(cnst->consttype == JSONPATHOID);
- if (cnst->constisnull)
- return false;
-
- return jspIsMutable(DatumGetJsonPathP(cnst->constvalue),
- jexpr->passing_names, jexpr->passing_values);
- }
-
if (IsA(node, SQLValueFunction))
{
/* all variants of SQLValueFunction are stable */
context, 0);
}
- /* JsonExpr is parallel-unsafe if subtransactions can be used. */
- else if (IsA(node, JsonExpr))
- {
- JsonExpr *jsexpr = (JsonExpr *) node;
-
- if (ExecEvalJsonNeedsSubTransaction(jsexpr, NULL))
- {
- context->max_hazard = PROPARALLEL_UNSAFE;
- return true;
- }
- }
-
/* Recurse to check arguments */
return expression_tree_walker(node,
max_parallel_hazard_walker,
return ece_evaluate_expr((Node *) newcre);
return (Node *) newcre;
}
- case T_JsonValueExpr:
- {
- JsonValueExpr *jve = (JsonValueExpr *) node;
- Node *raw = eval_const_expressions_mutator((Node *) jve->raw_expr,
- context);
-
- if (raw && IsA(raw, Const))
- {
- Node *formatted;
- Node *save_case_val = context->case_val;
-
- context->case_val = raw;
-
- formatted = eval_const_expressions_mutator((Node *) jve->formatted_expr,
- context);
-
- context->case_val = save_case_val;
-
- if (formatted && IsA(formatted, Const))
- return formatted;
- }
- break;
- }
default:
break;
}
parse_enr.o \
parse_expr.o \
parse_func.o \
- parse_jsontable.o \
parse_merge.o \
parse_node.o \
parse_oper.o \
MergeWhenClause *mergewhen;
struct KeyActions *keyactions;
struct KeyAction *keyaction;
- JsonBehavior *jsbehavior;
- struct
- {
- JsonBehavior *on_empty;
- JsonBehavior *on_error;
- } on_behavior;
- JsonQuotes js_quotes;
}
%type <node> stmt toplevel_stmt schema_stmt routine_body_stmt
%type <list> copy_options
%type <typnam> Typename SimpleTypename ConstTypename
- GenericType Numeric opt_float JsonType
+ GenericType Numeric opt_float
Character ConstCharacter
CharacterWithLength CharacterWithoutLength
ConstDatetime ConstInterval
%type <defelt> hash_partbound_elem
-%type <node> json_format_clause_opt
- json_representation
- json_value_expr
- json_func_expr
- json_value_func_expr
- json_query_expr
- json_exists_predicate
- json_parse_expr
- json_scalar_expr
- json_serialize_expr
- json_api_common_syntax
- json_context_item
- json_argument
- json_output_clause_opt
- json_returning_clause_opt
- json_value_constructor
- json_object_constructor
- json_object_constructor_args
- json_object_constructor_args_opt
- json_object_args
- json_object_func_args
- json_array_constructor
- json_name_and_value
- json_aggregate_func
- json_object_aggregate_constructor
- json_array_aggregate_constructor
- json_path_specification
- json_table
- json_table_column_definition
- json_table_ordinality_column_definition
- json_table_regular_column_definition
- json_table_formatted_column_definition
- json_table_exists_column_definition
- json_table_nested_columns
- json_table_plan_clause_opt
- json_table_specific_plan
- json_table_plan
- json_table_plan_simple
- json_table_plan_parent_child
- json_table_plan_outer
- json_table_plan_inner
- json_table_plan_sibling
- json_table_plan_union
- json_table_plan_cross
- json_table_plan_primary
- json_table_default_plan
-
-%type <list> json_name_and_value_list
- json_value_expr_list
- json_array_aggregate_order_by_clause_opt
- json_arguments
- json_passing_clause_opt
- json_table_columns_clause
- json_table_column_definition_list
-
-%type <str> json_table_path_name
- json_as_path_name_clause_opt
- json_table_column_path_specification_clause_opt
-
-%type <ival> json_encoding
- json_encoding_clause_opt
- json_table_default_plan_choices
- json_table_default_plan_inner_outer
- json_table_default_plan_union_cross
- json_wrapper_clause_opt
- json_wrapper_behavior
- json_conditional_or_unconditional_opt
- json_predicate_type_constraint_opt
-
-%type <jsbehavior> json_behavior_error
- json_behavior_null
- json_behavior_true
- json_behavior_false
- json_behavior_unknown
- json_behavior_empty
- json_behavior_empty_array
- json_behavior_empty_object
- json_behavior_default
- json_value_behavior
- json_query_behavior
- json_exists_error_behavior
- json_exists_error_clause_opt
- json_table_error_behavior
- json_table_error_clause_opt
-
-%type <on_behavior> json_value_on_behavior_clause_opt
- json_query_on_behavior_clause_opt
-
-%type <js_quotes> json_quotes_behavior
- json_quotes_clause_opt
-
-%type <boolean> json_key_uniqueness_constraint_opt
- json_object_constructor_null_clause_opt
- json_array_constructor_null_clause_opt
-
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
*/
/* ordinary key words in alphabetical order */
-%token <keyword> ABORT_P ABSENT ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
+%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION
CACHE CALL CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
- COMMITTED COMPRESSION CONCURRENTLY CONDITIONAL CONFIGURATION CONFLICT
+ COMMITTED COMPRESSION CONCURRENTLY CONFIGURATION CONFLICT
CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY
COST CREATE CROSS CSV CUBE CURRENT_P
CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE EMPTY_P ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ERROR_P ESCAPE
- EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXPRESSION
+ EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
+ EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXPRESSION
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FINALIZE FIRST_P FLOAT_P FOLLOWING FOR
- FORCE FOREIGN FORMAT FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
+ FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPS
INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
- JOIN JSON JSON_ARRAY JSON_ARRAYAGG JSON_EXISTS JSON_OBJECT JSON_OBJECTAGG
- JSON_QUERY JSON_SCALAR JSON_SERIALIZE JSON_TABLE JSON_VALUE
+ JOIN
- KEY KEYS KEEP
+ KEY
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MERGE METHOD
MINUTE_P MINVALUE MODE MONTH_P MOVE
- NAME_P NAMES NATIONAL NATURAL NCHAR NESTED NEW NEXT NFC NFD NFKC NFKD NO
- NONE NORMALIZE NORMALIZED
+ NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NFC NFD NFKC NFKD NO NONE
+ NORMALIZE NORMALIZED
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
NULLS_P NUMERIC
- OBJECT_P OF OFF OFFSET OIDS OLD OMIT ON ONLY OPERATOR OPTION OPTIONS OR
+ OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OPTIONS OR
ORDER ORDINALITY OTHERS OUT_P OUTER_P
OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
- PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PATH
- PLACING PLAN PLANS POLICY
+ PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD
+ PLACING PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
- QUOTE QUOTES
+ QUOTE
RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REFERENCING
REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
RESET RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROUTINE ROUTINES ROW ROWS RULE
- SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT
- SEQUENCE SEQUENCES SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF
- SHARE SHOW SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
- START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRING STRIP_P
+ SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
+ SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
+ SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
+ START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRIP_P
SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P
TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
TREAT TRIGGER TRIM TRUE_P
TRUNCATE TRUSTED TYPE_P TYPES_P
- UESCAPE UNBOUNDED UNCONDITIONAL UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN
+ UESCAPE UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN
UNLISTEN UNLOGGED UNTIL UPDATE USER USING
VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
* as NOT, at least with respect to their left-hand subexpression.
* NULLS_LA and WITH_LA are needed to make the grammar LALR(1).
*/
-%token NOT_LA NULLS_LA WITH_LA WITH_LA_UNIQUE WITHOUT_LA
+%token NOT_LA NULLS_LA WITH_LA
/*
* The grammar likewise thinks these tokens are keywords, but they are never
/* Precedence: lowest to highest */
%nonassoc SET /* see relation_expr_opt_alias */
-%right FORMAT
%left UNION EXCEPT
%left INTERSECT
%left OR
* Using the same precedence as IDENT seems right for the reasons given above.
*/
%nonassoc UNBOUNDED /* ideally would have same precedence as IDENT */
-%nonassoc ERROR_P EMPTY_P DEFAULT ABSENT /* JSON error/empty behavior */
-%nonassoc FALSE_P KEEP OMIT PASSING TRUE_P UNKNOWN UNIQUE JSON COLUMNS
%nonassoc IDENT PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
%left Op OPERATOR /* multi-character ops and user-defined operators */
%left '+' '-'
%left '*' '/' '%'
%left '^'
-%left KEYS /* UNIQUE [ KEYS ] */
-%left OBJECT_P SCALAR VALUE_P /* JSON [ OBJECT | SCALAR | VALUE ] */
/* Unary Operators */
%left AT /* sets precedence for AT TIME ZONE */
%left COLLATE
*/
%left JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL
-%nonassoc json_table_column
-%nonassoc NESTED
-%left PATH
-
-%nonassoc empty_json_unique
-%left WITHOUT WITH_LA_UNIQUE
-
%%
/*
$2->alias = $4;
$$ = (Node *) $2;
}
- | json_table opt_alias_clause
- {
- JsonTable *jt = castNode(JsonTable, $1);
-
- jt->alias = $2;
- $$ = (Node *) jt;
- }
- | LATERAL_P json_table opt_alias_clause
- {
- JsonTable *jt = castNode(JsonTable, $2);
-
- jt->alias = $3;
- jt->lateral = true;
- $$ = (Node *) jt;
- }
;
{ $$ = makeDefElem("is_not_null", (Node *) makeBoolean(true), @1); }
| NULL_P
{ $$ = makeDefElem("is_not_null", (Node *) makeBoolean(false), @1); }
- | PATH b_expr
- { $$ = makeDefElem("path", $2, @1); }
;
xml_namespace_list:
$$->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1),
makeIntConst($3, @3));
}
- | JsonType { $$ = $1; }
;
/* We have a separate ConstTypename to allow defaulting fixed-length
| ConstBit { $$ = $1; }
| ConstCharacter { $$ = $1; }
| ConstDatetime { $$ = $1; }
- | JsonType { $$ = $1; }
;
/*
opt_timezone:
WITH_LA TIME ZONE { $$ = true; }
- | WITHOUT_LA TIME ZONE { $$ = false; }
+ | WITHOUT TIME ZONE { $$ = false; }
| /*EMPTY*/ { $$ = false; }
;
}
;
-JsonType:
- JSON
- {
- $$ = SystemTypeName("json");
- $$->location = @1;
- }
- ;
/*****************************************************************************
*
@2),
@2);
}
- | a_expr
- IS json_predicate_type_constraint_opt
- json_key_uniqueness_constraint_opt %prec IS
- {
- JsonFormat *format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
- $$ = makeJsonIsPredicate($1, format, $3, $4, @1);
- }
- /*
- * Required by standard, but it would conflict with expressions
- * like: 'str' || format(...)
- | a_expr
- FORMAT json_representation
- IS json_predicate_type_constraint_opt
- json_key_uniqueness_constraint_opt %prec FORMAT
- {
- $3.location = @2;
- $$ = makeJsonIsPredicate($1, $3, $5, $6, @1);
- }
- */
- | a_expr
- IS NOT
- json_predicate_type_constraint_opt
- json_key_uniqueness_constraint_opt %prec IS
- {
- JsonFormat *format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
-
- $$ = makeNotExpr(makeJsonIsPredicate($1, format, $4, $5, @1), @1);
- }
- /*
- * Required by standard, but it would conflict with expressions
- * like: 'str' || format(...)
- | a_expr
- FORMAT json_representation
- IS NOT
- json_predicate_type_constraint_opt
- json_key_uniqueness_constraint_opt %prec FORMAT
- {
- $3.location = @2;
- $$ = makeNotExpr(makeJsonIsPredicate($1, $3, $6, $7, @1), @1);
- }
- */
| DEFAULT
{
/*
}
;
-json_predicate_type_constraint_opt:
- JSON { $$ = JS_TYPE_ANY; }
- | JSON VALUE_P { $$ = JS_TYPE_ANY; }
- | JSON ARRAY { $$ = JS_TYPE_ARRAY; }
- | JSON OBJECT_P { $$ = JS_TYPE_OBJECT; }
- | JSON SCALAR { $$ = JS_TYPE_SCALAR; }
- ;
-
-json_key_uniqueness_constraint_opt:
- WITH_LA_UNIQUE unique_keys { $$ = true; }
- | WITHOUT unique_keys { $$ = false; }
- | /* EMPTY */ %prec empty_json_unique { $$ = false; }
- ;
-
-unique_keys:
- UNIQUE
- | UNIQUE KEYS
- ;
-
/*
* Productions that can be used in both a_expr and b_expr.
*
n->over = $4;
$$ = (Node *) n;
}
- | json_aggregate_func filter_clause over_clause
- {
- JsonAggConstructor *n = IsA($1, JsonObjectAgg) ?
- ((JsonObjectAgg *) $1)->constructor :
- ((JsonArrayAgg *) $1)->constructor;
-
- n->agg_filter = $2;
- n->over = $3;
- $$ = (Node *) $1;
- }
| func_expr_common_subexpr
{ $$ = $1; }
;
func_expr_windowless:
func_application { $$ = $1; }
| func_expr_common_subexpr { $$ = $1; }
- | json_aggregate_func { $$ = $1; }
;
/*
n->location = @1;
$$ = (Node *) n;
}
- | json_func_expr
- { $$ = $1; }
;
/*
| /*EMPTY*/
;
-/* SQL/JSON support */
-json_func_expr:
- json_value_constructor
- | json_value_func_expr
- | json_query_expr
- | json_exists_predicate
- | json_parse_expr
- | json_scalar_expr
- | json_serialize_expr
- ;
-
-json_parse_expr:
- JSON '(' json_value_expr json_key_uniqueness_constraint_opt
- json_returning_clause_opt ')'
- {
- JsonParseExpr *n = makeNode(JsonParseExpr);
-
- n->expr = (JsonValueExpr *) $3;
- n->unique_keys = $4;
- n->output = (JsonOutput *) $5;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_scalar_expr:
- JSON_SCALAR '(' a_expr json_returning_clause_opt ')'
- {
- JsonScalarExpr *n = makeNode(JsonScalarExpr);
-
- n->expr = (Expr *) $3;
- n->output = (JsonOutput *) $4;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_serialize_expr:
- JSON_SERIALIZE '(' json_value_expr json_output_clause_opt ')'
- {
- JsonSerializeExpr *n = makeNode(JsonSerializeExpr);
-
- n->expr = (JsonValueExpr *) $3;
- n->output = (JsonOutput *) $4;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_value_func_expr:
- JSON_VALUE '('
- json_api_common_syntax
- json_returning_clause_opt
- json_value_on_behavior_clause_opt
- ')'
- {
- JsonFuncExpr *n = makeNode(JsonFuncExpr);
-
- n->op = JSON_VALUE_OP;
- n->common = (JsonCommon *) $3;
- n->output = (JsonOutput *) $4;
- n->on_empty = $5.on_empty;
- n->on_error = $5.on_error;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_api_common_syntax:
- json_context_item ',' json_path_specification
- json_as_path_name_clause_opt
- json_passing_clause_opt
- {
- JsonCommon *n = makeNode(JsonCommon);
-
- n->expr = (JsonValueExpr *) $1;
- n->pathspec = $3;
- n->pathname = $4;
- n->passing = $5;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_context_item:
- json_value_expr { $$ = $1; }
- ;
-
-json_path_specification:
- a_expr { $$ = $1; }
- ;
-
-json_as_path_name_clause_opt:
- AS json_table_path_name { $$ = $2; }
- | /* EMPTY */ { $$ = NULL; }
- ;
-
-json_table_path_name:
- name { $$ = $1; }
- ;
-
-json_passing_clause_opt:
- PASSING json_arguments { $$ = $2; }
- | /* EMPTY */ { $$ = NIL; }
- ;
-
-json_arguments:
- json_argument { $$ = list_make1($1); }
- | json_arguments ',' json_argument { $$ = lappend($1, $3); }
- ;
-
-json_argument:
- json_value_expr AS ColLabel
- {
- JsonArgument *n = makeNode(JsonArgument);
-
- n->val = (JsonValueExpr *) $1;
- n->name = $3;
- $$ = (Node *) n;
- }
- ;
-
-json_value_expr:
- a_expr json_format_clause_opt
- {
- $$ = (Node *) makeJsonValueExpr((Expr *) $1, castNode(JsonFormat, $2));
- }
- ;
-
-json_format_clause_opt:
- FORMAT json_representation
- {
- $$ = $2;
- castNode(JsonFormat, $$)->location = @1;
- }
- | /* EMPTY */
- {
- $$ = (Node *) makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
- }
- ;
-
-json_representation:
- JSON json_encoding_clause_opt
- {
- $$ = (Node *) makeJsonFormat(JS_FORMAT_JSON, $2, @1);
- }
- /* | other implementation defined JSON representation options (BSON, AVRO etc) */
- ;
-
-json_encoding_clause_opt:
- ENCODING json_encoding { $$ = $2; }
- | /* EMPTY */ { $$ = JS_ENC_DEFAULT; }
- ;
-
-json_encoding:
- name { $$ = makeJsonEncoding($1); }
- ;
-
-json_behavior_error:
- ERROR_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_ERROR, NULL); }
- ;
-
-json_behavior_null:
- NULL_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_NULL, NULL); }
- ;
-
-json_behavior_true:
- TRUE_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_TRUE, NULL); }
- ;
-
-json_behavior_false:
- FALSE_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_FALSE, NULL); }
- ;
-
-json_behavior_unknown:
- UNKNOWN { $$ = makeJsonBehavior(JSON_BEHAVIOR_UNKNOWN, NULL); }
- ;
-
-json_behavior_empty:
- EMPTY_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_EMPTY_OBJECT, NULL); }
- ;
-
-json_behavior_empty_array:
- EMPTY_P ARRAY { $$ = makeJsonBehavior(JSON_BEHAVIOR_EMPTY_ARRAY, NULL); }
- /* non-standard, for Oracle compatibility only */
- | EMPTY_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_EMPTY_ARRAY, NULL); }
- ;
-
-json_behavior_empty_object:
- EMPTY_P OBJECT_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_EMPTY_OBJECT, NULL); }
- ;
-
-json_behavior_default:
- DEFAULT a_expr { $$ = makeJsonBehavior(JSON_BEHAVIOR_DEFAULT, $2); }
- ;
-
-
-json_value_behavior:
- json_behavior_null
- | json_behavior_error
- | json_behavior_default
- ;
-
-json_value_on_behavior_clause_opt:
- json_value_behavior ON EMPTY_P
- { $$.on_empty = $1; $$.on_error = NULL; }
- | json_value_behavior ON EMPTY_P json_value_behavior ON ERROR_P
- { $$.on_empty = $1; $$.on_error = $4; }
- | json_value_behavior ON ERROR_P
- { $$.on_empty = NULL; $$.on_error = $1; }
- | /* EMPTY */
- { $$.on_empty = NULL; $$.on_error = NULL; }
- ;
-
-json_query_expr:
- JSON_QUERY '('
- json_api_common_syntax
- json_output_clause_opt
- json_wrapper_clause_opt
- json_quotes_clause_opt
- json_query_on_behavior_clause_opt
- ')'
- {
- JsonFuncExpr *n = makeNode(JsonFuncExpr);
-
- n->op = JSON_QUERY_OP;
- n->common = (JsonCommon *) $3;
- n->output = (JsonOutput *) $4;
- n->wrapper = $5;
- if (n->wrapper != JSW_NONE && $6 != JS_QUOTES_UNSPEC)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("SQL/JSON QUOTES behavior must not be specified when WITH WRAPPER is used"),
- parser_errposition(@6)));
- n->omit_quotes = $6 == JS_QUOTES_OMIT;
- n->on_empty = $7.on_empty;
- n->on_error = $7.on_error;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_wrapper_clause_opt:
- json_wrapper_behavior WRAPPER { $$ = $1; }
- | /* EMPTY */ { $$ = 0; }
- ;
-
-json_wrapper_behavior:
- WITHOUT array_opt { $$ = JSW_NONE; }
- | WITH json_conditional_or_unconditional_opt array_opt { $$ = $2; }
- ;
-
-array_opt:
- ARRAY { }
- | /* EMPTY */ { }
- ;
-
-json_conditional_or_unconditional_opt:
- CONDITIONAL { $$ = JSW_CONDITIONAL; }
- | UNCONDITIONAL { $$ = JSW_UNCONDITIONAL; }
- | /* EMPTY */ { $$ = JSW_UNCONDITIONAL; }
- ;
-
-json_quotes_clause_opt:
- json_quotes_behavior QUOTES json_on_scalar_string_opt { $$ = $1; }
- | /* EMPTY */ { $$ = JS_QUOTES_UNSPEC; }
- ;
-
-json_quotes_behavior:
- KEEP { $$ = JS_QUOTES_KEEP; }
- | OMIT { $$ = JS_QUOTES_OMIT; }
- ;
-
-json_on_scalar_string_opt:
- ON SCALAR STRING { }
- | /* EMPTY */ { }
- ;
-
-json_query_behavior:
- json_behavior_error
- | json_behavior_null
- | json_behavior_empty_array
- | json_behavior_empty_object
- | json_behavior_default
- ;
-
-json_query_on_behavior_clause_opt:
- json_query_behavior ON EMPTY_P
- { $$.on_empty = $1; $$.on_error = NULL; }
- | json_query_behavior ON EMPTY_P json_query_behavior ON ERROR_P
- { $$.on_empty = $1; $$.on_error = $4; }
- | json_query_behavior ON ERROR_P
- { $$.on_empty = NULL; $$.on_error = $1; }
- | /* EMPTY */
- { $$.on_empty = NULL; $$.on_error = NULL; }
- ;
-
-json_table:
- JSON_TABLE '('
- json_api_common_syntax
- json_table_columns_clause
- json_table_plan_clause_opt
- json_table_error_clause_opt
- ')'
- {
- JsonTable *n = makeNode(JsonTable);
-
- n->common = (JsonCommon *) $3;
- n->columns = $4;
- n->plan = (JsonTablePlan *) $5;
- n->on_error = $6;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_table_columns_clause:
- COLUMNS '(' json_table_column_definition_list ')' { $$ = $3; }
- ;
-
-json_table_column_definition_list:
- json_table_column_definition
- { $$ = list_make1($1); }
- | json_table_column_definition_list ',' json_table_column_definition
- { $$ = lappend($1, $3); }
- ;
-
-json_table_column_definition:
- json_table_ordinality_column_definition %prec json_table_column
- | json_table_regular_column_definition %prec json_table_column
- | json_table_formatted_column_definition %prec json_table_column
- | json_table_exists_column_definition %prec json_table_column
- | json_table_nested_columns
- ;
-
-json_table_ordinality_column_definition:
- ColId FOR ORDINALITY
- {
- JsonTableColumn *n = makeNode(JsonTableColumn);
-
- n->coltype = JTC_FOR_ORDINALITY;
- n->name = $1;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_table_regular_column_definition:
- ColId Typename
- json_table_column_path_specification_clause_opt
- json_wrapper_clause_opt
- json_quotes_clause_opt
- json_value_on_behavior_clause_opt
- {
- JsonTableColumn *n = makeNode(JsonTableColumn);
-
- n->coltype = JTC_REGULAR;
- n->name = $1;
- n->typeName = $2;
- n->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
- n->wrapper = $4; /* JSW_NONE */
- n->omit_quotes = $5; /* false */
- n->pathspec = $3;
- n->on_empty = $6.on_empty;
- n->on_error = $6.on_error;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_table_exists_column_definition:
- ColId Typename
- EXISTS json_table_column_path_specification_clause_opt
- json_exists_error_clause_opt
- {
- JsonTableColumn *n = makeNode(JsonTableColumn);
-
- n->coltype = JTC_EXISTS;
- n->name = $1;
- n->typeName = $2;
- n->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
- n->wrapper = JSW_NONE;
- n->omit_quotes = false;
- n->pathspec = $4;
- n->on_empty = NULL;
- n->on_error = $5;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_table_error_behavior:
- json_behavior_error
- | json_behavior_empty
- ;
-
-json_table_error_clause_opt:
- json_table_error_behavior ON ERROR_P { $$ = $1; }
- | /* EMPTY */ { $$ = NULL; }
- ;
-
-json_table_column_path_specification_clause_opt:
- PATH Sconst { $$ = $2; }
- | /* EMPTY */ %prec json_table_column { $$ = NULL; }
- ;
-
-json_table_formatted_column_definition:
- ColId Typename FORMAT json_representation
- json_table_column_path_specification_clause_opt
- json_wrapper_clause_opt
- json_quotes_clause_opt
- json_query_on_behavior_clause_opt
- {
- JsonTableColumn *n = makeNode(JsonTableColumn);
-
- n->coltype = JTC_FORMATTED;
- n->name = $1;
- n->typeName = $2;
- n->format = castNode(JsonFormat, $4);
- n->pathspec = $5;
- n->wrapper = $6;
- if (n->wrapper != JSW_NONE && $7 != JS_QUOTES_UNSPEC)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("SQL/JSON QUOTES behavior must not be specified when WITH WRAPPER is used"),
- parser_errposition(@7)));
- n->omit_quotes = $7 == JS_QUOTES_OMIT;
- n->on_empty = $8.on_empty;
- n->on_error = $8.on_error;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_table_nested_columns:
- NESTED path_opt Sconst
- json_as_path_name_clause_opt
- json_table_columns_clause
- {
- JsonTableColumn *n = makeNode(JsonTableColumn);
-
- n->coltype = JTC_NESTED;
- n->pathspec = $3;
- n->pathname = $4;
- n->columns = $5;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-path_opt:
- PATH { }
- | /* EMPTY */ { }
- ;
-
-json_table_plan_clause_opt:
- json_table_specific_plan { $$ = $1; }
- | json_table_default_plan { $$ = $1; }
- | /* EMPTY */ { $$ = NULL; }
- ;
-
-json_table_specific_plan:
- PLAN '(' json_table_plan ')' { $$ = $3; }
- ;
-
-json_table_plan:
- json_table_plan_simple
- | json_table_plan_parent_child
- | json_table_plan_sibling
- ;
-
-json_table_plan_simple:
- json_table_path_name
- {
- JsonTablePlan *n = makeNode(JsonTablePlan);
-
- n->plan_type = JSTP_SIMPLE;
- n->pathname = $1;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_table_plan_parent_child:
- json_table_plan_outer
- | json_table_plan_inner
- ;
-
-json_table_plan_outer:
- json_table_plan_simple OUTER_P json_table_plan_primary
- { $$ = makeJsonTableJoinedPlan(JSTPJ_OUTER, $1, $3, @1); }
- ;
-
-json_table_plan_inner:
- json_table_plan_simple INNER_P json_table_plan_primary
- { $$ = makeJsonTableJoinedPlan(JSTPJ_INNER, $1, $3, @1); }
- ;
-
-json_table_plan_sibling:
- json_table_plan_union
- | json_table_plan_cross
- ;
-
-json_table_plan_union:
- json_table_plan_primary UNION json_table_plan_primary
- { $$ = makeJsonTableJoinedPlan(JSTPJ_UNION, $1, $3, @1); }
- | json_table_plan_union UNION json_table_plan_primary
- { $$ = makeJsonTableJoinedPlan(JSTPJ_UNION, $1, $3, @1); }
- ;
-
-json_table_plan_cross:
- json_table_plan_primary CROSS json_table_plan_primary
- { $$ = makeJsonTableJoinedPlan(JSTPJ_CROSS, $1, $3, @1); }
- | json_table_plan_cross CROSS json_table_plan_primary
- { $$ = makeJsonTableJoinedPlan(JSTPJ_CROSS, $1, $3, @1); }
- ;
-
-json_table_plan_primary:
- json_table_plan_simple { $$ = $1; }
- | '(' json_table_plan ')'
- {
- castNode(JsonTablePlan, $2)->location = @1;
- $$ = $2;
- }
- ;
-
-json_table_default_plan:
- PLAN DEFAULT '(' json_table_default_plan_choices ')'
- {
- JsonTablePlan *n = makeNode(JsonTablePlan);
-
- n->plan_type = JSTP_DEFAULT;
- n->join_type = $4;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_table_default_plan_choices:
- json_table_default_plan_inner_outer { $$ = $1 | JSTPJ_UNION; }
- | json_table_default_plan_inner_outer ','
- json_table_default_plan_union_cross { $$ = $1 | $3; }
- | json_table_default_plan_union_cross { $$ = $1 | JSTPJ_OUTER; }
- | json_table_default_plan_union_cross ','
- json_table_default_plan_inner_outer { $$ = $1 | $3; }
- ;
-
-json_table_default_plan_inner_outer:
- INNER_P { $$ = JSTPJ_INNER; }
- | OUTER_P { $$ = JSTPJ_OUTER; }
- ;
-
-json_table_default_plan_union_cross:
- UNION { $$ = JSTPJ_UNION; }
- | CROSS { $$ = JSTPJ_CROSS; }
- ;
-
-json_returning_clause_opt:
- RETURNING Typename
- {
- JsonOutput *n = makeNode(JsonOutput);
-
- n->typeName = $2;
- n->returning = makeNode(JsonReturning);
- n->returning->format =
- makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, @2);
- $$ = (Node *) n;
- }
- | /* EMPTY */ { $$ = NULL; }
- ;
-
-json_output_clause_opt:
- RETURNING Typename json_format_clause_opt
- {
- JsonOutput *n = makeNode(JsonOutput);
-
- n->typeName = $2;
- n->returning = makeNode(JsonReturning);
- n->returning->format = (JsonFormat *) $3;
- $$ = (Node *) n;
- }
- | /* EMPTY */ { $$ = NULL; }
- ;
-
-json_exists_predicate:
- JSON_EXISTS '('
- json_api_common_syntax
- json_returning_clause_opt
- json_exists_error_clause_opt
- ')'
- {
- JsonFuncExpr *p = makeNode(JsonFuncExpr);
-
- p->op = JSON_EXISTS_OP;
- p->common = (JsonCommon *) $3;
- p->output = (JsonOutput *) $4;
- p->on_error = $5;
- p->location = @1;
- $$ = (Node *) p;
- }
- ;
-
-json_exists_error_clause_opt:
- json_exists_error_behavior ON ERROR_P { $$ = $1; }
- | /* EMPTY */ { $$ = NULL; }
- ;
-
-json_exists_error_behavior:
- json_behavior_error
- | json_behavior_true
- | json_behavior_false
- | json_behavior_unknown
- ;
-
-json_value_constructor:
- json_object_constructor
- | json_array_constructor
- ;
-
-json_object_constructor:
- JSON_OBJECT '(' json_object_args ')'
- {
- $$ = $3;
- }
- ;
-
-json_object_args:
- json_object_constructor_args
- | json_object_func_args
- ;
-
-json_object_func_args:
- func_arg_list
- {
- List *func = list_make1(makeString("json_object"));
-
- $$ = (Node *) makeFuncCall(func, $1, COERCE_EXPLICIT_CALL, @1);
- }
- ;
-
-json_object_constructor_args:
- json_object_constructor_args_opt json_output_clause_opt
- {
- JsonObjectConstructor *n = (JsonObjectConstructor *) $1;
-
- n->output = (JsonOutput *) $2;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_object_constructor_args_opt:
- json_name_and_value_list
- json_object_constructor_null_clause_opt
- json_key_uniqueness_constraint_opt
- {
- JsonObjectConstructor *n = makeNode(JsonObjectConstructor);
-
- n->exprs = $1;
- n->absent_on_null = $2;
- n->unique = $3;
- $$ = (Node *) n;
- }
- | /* EMPTY */
- {
- JsonObjectConstructor *n = makeNode(JsonObjectConstructor);
-
- n->exprs = NULL;
- n->absent_on_null = false;
- n->unique = false;
- $$ = (Node *) n;
- }
- ;
-
-json_name_and_value_list:
- json_name_and_value
- { $$ = list_make1($1); }
- | json_name_and_value_list ',' json_name_and_value
- { $$ = lappend($1, $3); }
- ;
-
-json_name_and_value:
-/* TODO This is not supported due to conflicts
- KEY c_expr VALUE_P json_value_expr %prec POSTFIXOP
- { $$ = makeJsonKeyValue($2, $4); }
- |
-*/
- c_expr VALUE_P json_value_expr
- { $$ = makeJsonKeyValue($1, $3); }
- |
- a_expr ':' json_value_expr
- { $$ = makeJsonKeyValue($1, $3); }
- ;
-
-json_object_constructor_null_clause_opt:
- NULL_P ON NULL_P { $$ = false; }
- | ABSENT ON NULL_P { $$ = true; }
- | /* EMPTY */ { $$ = false; }
- ;
-
-json_array_constructor:
- JSON_ARRAY '('
- json_value_expr_list
- json_array_constructor_null_clause_opt
- json_output_clause_opt
- ')'
- {
- JsonArrayConstructor *n = makeNode(JsonArrayConstructor);
-
- n->exprs = $3;
- n->absent_on_null = $4;
- n->output = (JsonOutput *) $5;
- n->location = @1;
- $$ = (Node *) n;
- }
- | JSON_ARRAY '('
- select_no_parens
- /* json_format_clause_opt */
- /* json_array_constructor_null_clause_opt */
- json_output_clause_opt
- ')'
- {
- JsonArrayQueryConstructor *n = makeNode(JsonArrayQueryConstructor);
-
- n->query = $3;
- n->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
- /* n->format = $4; */
- n->absent_on_null = true /* $5 */;
- n->output = (JsonOutput *) $4;
- n->location = @1;
- $$ = (Node *) n;
- }
- | JSON_ARRAY '('
- json_output_clause_opt
- ')'
- {
- JsonArrayConstructor *n = makeNode(JsonArrayConstructor);
-
- n->exprs = NIL;
- n->absent_on_null = true;
- n->output = (JsonOutput *) $3;
- n->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_value_expr_list:
- json_value_expr { $$ = list_make1($1); }
- | json_value_expr_list ',' json_value_expr { $$ = lappend($1, $3);}
- ;
-
-json_array_constructor_null_clause_opt:
- NULL_P ON NULL_P { $$ = false; }
- | ABSENT ON NULL_P { $$ = true; }
- | /* EMPTY */ { $$ = true; }
- ;
-
-json_aggregate_func:
- json_object_aggregate_constructor
- | json_array_aggregate_constructor
- ;
-
-json_object_aggregate_constructor:
- JSON_OBJECTAGG '('
- json_name_and_value
- json_object_constructor_null_clause_opt
- json_key_uniqueness_constraint_opt
- json_output_clause_opt
- ')'
- {
- JsonObjectAgg *n = makeNode(JsonObjectAgg);
-
- n->arg = (JsonKeyValue *) $3;
- n->absent_on_null = $4;
- n->unique = $5;
- n->constructor = makeNode(JsonAggConstructor);
- n->constructor->output = (JsonOutput *) $6;
- n->constructor->agg_order = NULL;
- n->constructor->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_array_aggregate_constructor:
- JSON_ARRAYAGG '('
- json_value_expr
- json_array_aggregate_order_by_clause_opt
- json_array_constructor_null_clause_opt
- json_output_clause_opt
- ')'
- {
- JsonArrayAgg *n = makeNode(JsonArrayAgg);
-
- n->arg = (JsonValueExpr *) $3;
- n->absent_on_null = $5;
- n->constructor = makeNode(JsonAggConstructor);
- n->constructor->agg_order = $4;
- n->constructor->output = (JsonOutput *) $6;
- n->constructor->location = @1;
- $$ = (Node *) n;
- }
- ;
-
-json_array_aggregate_order_by_clause_opt:
- ORDER BY sortby_list { $$ = $3; }
- | /* EMPTY */ { $$ = NIL; }
- ;
/*****************************************************************************
*
*/
unreserved_keyword:
ABORT_P
- | ABSENT
| ABSOLUTE_P
| ACCESS
| ACTION
| COMMIT
| COMMITTED
| COMPRESSION
- | CONDITIONAL
| CONFIGURATION
| CONFLICT
| CONNECTION
| DOUBLE_P
| DROP
| EACH
- | EMPTY_P
| ENABLE_P
| ENCODING
| ENCRYPTED
| ENUM_P
- | ERROR_P
| ESCAPE
| EVENT
| EXCLUDE
| FIRST_P
| FOLLOWING
| FORCE
- | FORMAT
| FORWARD
| FUNCTION
| FUNCTIONS
| INSTEAD
| INVOKER
| ISOLATION
- | KEEP
| KEY
- | KEYS
| LABEL
| LANGUAGE
| LARGE_P
| MOVE
| NAME_P
| NAMES
- | NESTED
| NEW
| NEXT
| NFC
| OFF
| OIDS
| OLD
- | OMIT
| OPERATOR
| OPTION
| OPTIONS
| PARTITION
| PASSING
| PASSWORD
- | PATH
- | PLAN
| PLANS
| POLICY
| PRECEDING
| PROGRAM
| PUBLICATION
| QUOTE
- | QUOTES
| RANGE
| READ
| REASSIGN
| ROWS
| RULE
| SAVEPOINT
- | SCALAR
| SCHEMA
| SCHEMAS
| SCROLL
| STORAGE
| STORED
| STRICT_P
- | STRING
| STRIP_P
| SUBSCRIPTION
| SUPPORT
| UESCAPE
| UNBOUNDED
| UNCOMMITTED
- | UNCONDITIONAL
| UNENCRYPTED
| UNKNOWN
| UNLISTEN
| INT_P
| INTEGER
| INTERVAL
- | JSON
- | JSON_ARRAY
- | JSON_ARRAYAGG
- | JSON_EXISTS
- | JSON_OBJECT
- | JSON_OBJECTAGG
- | JSON_QUERY
- | JSON_SCALAR
- | JSON_SERIALIZE
- | JSON_TABLE
- | JSON_VALUE
| LEAST
| NATIONAL
| NCHAR
*/
bare_label_keyword:
ABORT_P
- | ABSENT
| ABSOLUTE_P
| ACCESS
| ACTION
| COMMITTED
| COMPRESSION
| CONCURRENTLY
- | CONDITIONAL
| CONFIGURATION
| CONFLICT
| CONNECTION
| DROP
| EACH
| ELSE
- | EMPTY_P
| ENABLE_P
| ENCODING
| ENCRYPTED
| END_P
| ENUM_P
- | ERROR_P
| ESCAPE
| EVENT
| EXCLUDE
| FOLLOWING
| FORCE
| FOREIGN
- | FORMAT
| FORWARD
| FREEZE
| FULL
| IS
| ISOLATION
| JOIN
- | JSON
- | JSON_ARRAY
- | JSON_ARRAYAGG
- | JSON_EXISTS
- | JSON_OBJECT
- | JSON_OBJECTAGG
- | JSON_QUERY
- | JSON_SCALAR
- | JSON_SERIALIZE
- | JSON_TABLE
- | JSON_VALUE
- | KEEP
| KEY
- | KEYS
| LABEL
| LANGUAGE
| LARGE_P
| NATIONAL
| NATURAL
| NCHAR
- | NESTED
| NEW
| NEXT
| NFC
| OFF
| OIDS
| OLD
- | OMIT
| ONLY
| OPERATOR
| OPTION
| PARTITION
| PASSING
| PASSWORD
- | PATH
| PLACING
- | PLAN
| PLANS
| POLICY
| POSITION
| PROGRAM
| PUBLICATION
| QUOTE
- | QUOTES
| RANGE
| READ
| REAL
| ROWS
| RULE
| SAVEPOINT
- | SCALAR
| SCHEMA
| SCHEMAS
| SCROLL
| STORAGE
| STORED
| STRICT_P
- | STRING
| STRIP_P
| SUBSCRIPTION
| SUBSTRING
| UESCAPE
| UNBOUNDED
| UNCOMMITTED
- | UNCONDITIONAL
| UNENCRYPTED
| UNIQUE
| UNKNOWN
char **names;
int colno;
- /* Currently only XMLTABLE and JSON_TABLE are supported */
-
- tf->functype = TFT_XMLTABLE;
+ /* Currently only XMLTABLE is supported */
constructName = "XMLTABLE";
docType = XMLOID;
rtr->rtindex = nsitem->p_rtindex;
return (Node *) rtr;
}
- else if (IsA(n, RangeTableFunc) || IsA(n, JsonTable))
+ else if (IsA(n, RangeTableFunc))
{
/* table function is like a plain relation */
RangeTblRef *rtr;
ParseNamespaceItem *nsitem;
- if (IsA(n, RangeTableFunc))
- nsitem = transformRangeTableFunc(pstate, (RangeTableFunc *) n);
- else
- nsitem = transformJsonTable(pstate, (JsonTable *) n);
-
+ nsitem = transformRangeTableFunc(pstate, (RangeTableFunc *) n);
*top_nsitem = nsitem;
*namespace = list_make1(nsitem);
rtr = makeNode(RangeTblRef);
&loccontext);
}
break;
- case T_JsonExpr:
-
- /*
- * Context item and PASSING arguments are already
- * marked with collations in parse_expr.c.
- */
- break;
default:
/*
#include "postgres.h"
-#include "catalog/pg_aggregate.h"
-#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/date.h"
-#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
#include "utils/xml.h"
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
-static Node *transformJsonObjectConstructor(ParseState *pstate,
- JsonObjectConstructor *ctor);
-static Node *transformJsonArrayConstructor(ParseState *pstate,
- JsonArrayConstructor *ctor);
-static Node *transformJsonArrayQueryConstructor(ParseState *pstate,
- JsonArrayQueryConstructor *ctor);
-static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
-static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
-static Node *transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *p);
-static Node *transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *p);
-static Node *transformJsonValueExpr(ParseState *pstate, JsonValueExpr *jve);
-static Node *transformJsonParseExpr(ParseState *pstate, JsonParseExpr *expr);
-static Node *transformJsonScalarExpr(ParseState *pstate, JsonScalarExpr *expr);
-static Node *transformJsonSerializeExpr(ParseState *pstate,
- JsonSerializeExpr *expr);
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
List *largs, List *rargs, int location);
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
break;
}
- case T_JsonObjectConstructor:
- result = transformJsonObjectConstructor(pstate, (JsonObjectConstructor *) expr);
- break;
-
- case T_JsonArrayConstructor:
- result = transformJsonArrayConstructor(pstate, (JsonArrayConstructor *) expr);
- break;
-
- case T_JsonArrayQueryConstructor:
- result = transformJsonArrayQueryConstructor(pstate, (JsonArrayQueryConstructor *) expr);
- break;
-
- case T_JsonObjectAgg:
- result = transformJsonObjectAgg(pstate, (JsonObjectAgg *) expr);
- break;
-
- case T_JsonArrayAgg:
- result = transformJsonArrayAgg(pstate, (JsonArrayAgg *) expr);
- break;
-
- case T_JsonIsPredicate:
- result = transformJsonIsPredicate(pstate, (JsonIsPredicate *) expr);
- break;
-
- case T_JsonFuncExpr:
- result = transformJsonFuncExpr(pstate, (JsonFuncExpr *) expr);
- break;
-
- case T_JsonValueExpr:
- result = transformJsonValueExpr(pstate, (JsonValueExpr *) expr);
- break;
-
- case T_JsonParseExpr:
- result = transformJsonParseExpr(pstate, (JsonParseExpr *) expr);
- break;
-
- case T_JsonScalarExpr:
- result = transformJsonScalarExpr(pstate, (JsonScalarExpr *) expr);
- break;
-
- case T_JsonSerializeExpr:
- result = transformJsonSerializeExpr(pstate, (JsonSerializeExpr *) expr);
- break;
-
default:
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
}
return "unrecognized expression kind";
}
-
-/*
- * Make string Const node from JSON encoding name.
- *
- * UTF8 is default encoding.
- */
-static Const *
-getJsonEncodingConst(JsonFormat *format)
-{
- JsonEncoding encoding;
- const char *enc;
- Name encname = palloc(sizeof(NameData));
-
- if (!format ||
- format->format_type == JS_FORMAT_DEFAULT ||
- format->encoding == JS_ENC_DEFAULT)
- encoding = JS_ENC_UTF8;
- else
- encoding = format->encoding;
-
- switch (encoding)
- {
- case JS_ENC_UTF16:
- enc = "UTF16";
- break;
- case JS_ENC_UTF32:
- enc = "UTF32";
- break;
- case JS_ENC_UTF8:
- enc = "UTF8";
- break;
- default:
- elog(ERROR, "invalid JSON encoding: %d", encoding);
- break;
- }
-
- namestrcpy(encname, enc);
-
- return makeConst(NAMEOID, -1, InvalidOid, NAMEDATALEN,
- NameGetDatum(encname), false, false);
-}
-
-/*
- * Make bytea => text conversion using specified JSON format encoding.
- */
-static Node *
-makeJsonByteaToTextConversion(Node *expr, JsonFormat *format, int location)
-{
- Const *encoding = getJsonEncodingConst(format);
- FuncExpr *fexpr = makeFuncExpr(F_CONVERT_FROM, TEXTOID,
- list_make2(expr, encoding),
- InvalidOid, InvalidOid,
- COERCE_EXPLICIT_CALL);
-
- fexpr->location = location;
-
- return (Node *) fexpr;
-}
-
-/*
- * Make CaseTestExpr node.
- */
-static Node *
-makeCaseTestExpr(Node *expr)
-{
- CaseTestExpr *placeholder = makeNode(CaseTestExpr);
-
- placeholder->typeId = exprType(expr);
- placeholder->typeMod = exprTypmod(expr);
- placeholder->collation = exprCollation(expr);
-
- return (Node *) placeholder;
-}
-
-/*
- * Transform JSON value expression using specified input JSON format or
- * default format otherwise.
- */
-static Node *
-transformJsonValueExprExt(ParseState *pstate, JsonValueExpr *ve,
- JsonFormatType default_format, bool isarg,
- Oid targettype)
-{
- Node *expr = transformExprRecurse(pstate, (Node *) ve->raw_expr);
- Node *rawexpr;
- JsonFormatType format;
- Oid exprtype;
- int location;
- char typcategory;
- bool typispreferred;
-
- if (exprType(expr) == UNKNOWNOID)
- expr = coerce_to_specific_type(pstate, expr, TEXTOID, "JSON_VALUE_EXPR");
-
- rawexpr = expr;
- exprtype = exprType(expr);
- location = exprLocation(expr);
-
- get_type_category_preferred(exprtype, &typcategory, &typispreferred);
-
- rawexpr = expr;
-
- if (ve->format->format_type != JS_FORMAT_DEFAULT)
- {
- if (ve->format->encoding != JS_ENC_DEFAULT && exprtype != BYTEAOID)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("JSON ENCODING clause is only allowed for bytea input type"),
- parser_errposition(pstate, ve->format->location)));
-
- if (exprtype == JSONOID || exprtype == JSONBOID)
- {
- format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
- ereport(WARNING,
- (errmsg("FORMAT JSON has no effect for json and jsonb types"),
- parser_errposition(pstate, ve->format->location)));
- }
- else
- format = ve->format->format_type;
- }
- else if (isarg)
- {
- /* Pass SQL/JSON item types directly without conversion to json[b]. */
- switch (exprtype)
- {
- case TEXTOID:
- case NUMERICOID:
- case BOOLOID:
- case INT2OID:
- case INT4OID:
- case INT8OID:
- case FLOAT4OID:
- case FLOAT8OID:
- case DATEOID:
- case TIMEOID:
- case TIMETZOID:
- case TIMESTAMPOID:
- case TIMESTAMPTZOID:
- return expr;
-
- default:
- if (typcategory == TYPCATEGORY_STRING)
- return coerce_to_specific_type(pstate, expr, TEXTOID,
- "JSON_VALUE_EXPR");
- /* else convert argument to json[b] type */
- break;
- }
-
- format = default_format;
- }
- else if (exprtype == JSONOID || exprtype == JSONBOID)
- format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
- else
- format = default_format;
-
- if (format == JS_FORMAT_DEFAULT &&
- (!OidIsValid(targettype) || exprtype == targettype))
- expr = rawexpr;
- else
- {
- Node *orig = makeCaseTestExpr(expr);
- Node *coerced;
- bool cast_is_needed = OidIsValid(targettype);
-
- if (!isarg && !cast_is_needed &&
- exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg(ve->format->format_type == JS_FORMAT_DEFAULT ?
- "cannot use non-string types with implicit FORMAT JSON clause" :
- "cannot use non-string types with explicit FORMAT JSON clause"),
- parser_errposition(pstate, ve->format->location >= 0 ?
- ve->format->location : location)));
-
- expr = orig;
-
- /* Convert encoded JSON text from bytea. */
- if (format == JS_FORMAT_JSON && exprtype == BYTEAOID)
- {
- expr = makeJsonByteaToTextConversion(expr, ve->format, location);
- exprtype = TEXTOID;
- }
-
- if (!OidIsValid(targettype))
- targettype = format == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
-
- /* Try to coerce to the target type. */
- coerced = coerce_to_target_type(pstate, expr, exprtype,
- targettype, -1,
- COERCION_EXPLICIT,
- COERCE_EXPLICIT_CAST,
- location);
-
- if (!coerced)
- {
- /* If coercion failed, use to_json()/to_jsonb() functions. */
- FuncExpr *fexpr;
- Oid fnoid;
-
- if (cast_is_needed) /* only CAST is allowed */
- ereport(ERROR,
- (errcode(ERRCODE_CANNOT_COERCE),
- errmsg("cannot cast type %s to %s",
- format_type_be(exprtype),
- format_type_be(targettype)),
- parser_errposition(pstate, location)));
-
- fnoid = targettype == JSONOID ? F_TO_JSON : F_TO_JSONB;
- fexpr = makeFuncExpr(fnoid, targettype, list_make1(expr),
- InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
-
- fexpr->location = location;
-
- coerced = (Node *) fexpr;
- }
-
- if (coerced == orig)
- expr = rawexpr;
- else
- {
- ve = copyObject(ve);
- ve->raw_expr = (Expr *) rawexpr;
- ve->formatted_expr = (Expr *) coerced;
-
- expr = (Node *) ve;
- }
- }
-
- return expr;
-}
-
-/*
- * Transform JSON value expression using FORMAT JSON by default.
- */
-static Node *
-transformJsonValueExpr(ParseState *pstate, JsonValueExpr *jve)
-{
- return transformJsonValueExprExt(pstate, jve, JS_FORMAT_JSON, false,
- InvalidOid);
-}
-
-/*
- * Transform JSON value expression using unspecified format by default.
- */
-static Node *
-transformJsonValueExprDefault(ParseState *pstate, JsonValueExpr *jve)
-{
- return transformJsonValueExprExt(pstate, jve, JS_FORMAT_DEFAULT, false,
- InvalidOid);
-}
-
-/*
- * Checks specified output format for its applicability to the target type.
- */
-static void
-checkJsonOutputFormat(ParseState *pstate, const JsonFormat *format,
- Oid targettype, bool allow_format_for_non_strings)
-{
- if (!allow_format_for_non_strings &&
- format->format_type != JS_FORMAT_DEFAULT &&
- (targettype != BYTEAOID &&
- targettype != JSONOID &&
- targettype != JSONBOID))
- {
- char typcategory;
- bool typispreferred;
-
- get_type_category_preferred(targettype, &typcategory, &typispreferred);
-
- if (typcategory != TYPCATEGORY_STRING)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- parser_errposition(pstate, format->location),
- errmsg("cannot use JSON format with non-string output types")));
- }
-
- if (format->format_type == JS_FORMAT_JSON)
- {
- JsonEncoding enc = format->encoding != JS_ENC_DEFAULT ?
- format->encoding : JS_ENC_UTF8;
-
- if (targettype != BYTEAOID &&
- format->encoding != JS_ENC_DEFAULT)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- parser_errposition(pstate, format->location),
- errmsg("cannot set JSON encoding for non-bytea output types")));
-
- if (enc != JS_ENC_UTF8)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("unsupported JSON encoding"),
- errhint("Only UTF8 JSON encoding is supported."),
- parser_errposition(pstate, format->location)));
- }
-}
-
-/*
- * Transform JSON output clause.
- *
- * Assigns target type oid and modifier.
- * Assigns default format or checks specified format for its applicability to
- * the target type.
- */
-static JsonReturning *
-transformJsonOutput(ParseState *pstate, const JsonOutput *output,
- bool allow_format)
-{
- JsonReturning *ret;
-
- /* if output clause is not specified, make default clause value */
- if (!output)
- {
- ret = makeNode(JsonReturning);
-
- ret->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
- ret->typid = InvalidOid;
- ret->typmod = -1;
-
- return ret;
- }
-
- ret = copyObject(output->returning);
-
- typenameTypeIdAndMod(pstate, output->typeName, &ret->typid, &ret->typmod);
-
- if (output->typeName->setof)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("returning SETOF types is not supported in SQL/JSON functions")));
-
- if (ret->format->format_type == JS_FORMAT_DEFAULT)
- /* assign JSONB format when returning jsonb, or JSON format otherwise */
- ret->format->format_type =
- ret->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON;
- else
- checkJsonOutputFormat(pstate, ret->format, ret->typid, allow_format);
-
- return ret;
-}
-
-/*
- * Transform JSON output clause of JSON constructor functions.
- *
- * Derive RETURNING type, if not specified, from argument types.
- */
-static JsonReturning *
-transformJsonConstructorOutput(ParseState *pstate, JsonOutput *output,
- List *args)
-{
- JsonReturning *returning = transformJsonOutput(pstate, output, true);
-
- if (!OidIsValid(returning->typid))
- {
- ListCell *lc;
- bool have_jsonb = false;
-
- foreach(lc, args)
- {
- Node *expr = lfirst(lc);
- Oid typid = exprType(expr);
-
- have_jsonb |= typid == JSONBOID;
-
- if (have_jsonb)
- break;
- }
-
- if (have_jsonb)
- {
- returning->typid = JSONBOID;
- returning->format->format_type = JS_FORMAT_JSONB;
- }
- else
- {
- /* XXX TEXT is default by the standard, but we return JSON */
- returning->typid = JSONOID;
- returning->format->format_type = JS_FORMAT_JSON;
- }
-
- returning->typmod = -1;
- }
-
- return returning;
-}
-
-/*
- * Coerce json[b]-valued function expression to the output type.
- */
-static Node *
-coerceJsonFuncExpr(ParseState *pstate, Node *expr,
- const JsonReturning *returning, bool report_error)
-{
- Node *res;
- int location;
- Oid exprtype = exprType(expr);
-
- /* if output type is not specified or equals to function type, return */
- if (!OidIsValid(returning->typid) || returning->typid == exprtype)
- return expr;
-
- location = exprLocation(expr);
-
- if (location < 0)
- location = returning->format->location;
-
- /* special case for RETURNING bytea FORMAT json */
- if (returning->format->format_type == JS_FORMAT_JSON &&
- returning->typid == BYTEAOID)
- {
- /* encode json text into bytea using pg_convert_to() */
- Node *texpr = coerce_to_specific_type(pstate, expr, TEXTOID,
- "JSON_FUNCTION");
- Const *enc = getJsonEncodingConst(returning->format);
- FuncExpr *fexpr = makeFuncExpr(F_CONVERT_TO, BYTEAOID,
- list_make2(texpr, enc),
- InvalidOid, InvalidOid,
- COERCE_EXPLICIT_CALL);
-
- fexpr->location = location;
-
- return (Node *) fexpr;
- }
-
- /* try to coerce expression to the output type */
- res = coerce_to_target_type(pstate, expr, exprtype,
- returning->typid, returning->typmod,
- /* XXX throwing errors when casting to char(N) */
- COERCION_EXPLICIT,
- COERCE_EXPLICIT_CAST,
- location);
-
- if (!res && report_error)
- ereport(ERROR,
- (errcode(ERRCODE_CANNOT_COERCE),
- errmsg("cannot cast type %s to %s",
- format_type_be(exprtype),
- format_type_be(returning->typid)),
- parser_coercion_errposition(pstate, location, expr)));
-
- return res;
-}
-
-static Node *
-makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type,
- List *args, Expr *fexpr, JsonReturning *returning,
- bool unique, bool absent_on_null, int location)
-{
- JsonConstructorExpr *jsctor = makeNode(JsonConstructorExpr);
- Node *placeholder;
- Node *coercion;
- Oid intermediate_typid =
- returning->format->format_type == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
-
- jsctor->args = args;
- jsctor->func = fexpr;
- jsctor->type = type;
- jsctor->returning = returning;
- jsctor->unique = unique;
- jsctor->absent_on_null = absent_on_null;
- jsctor->location = location;
-
- if (fexpr)
- placeholder = makeCaseTestExpr((Node *) fexpr);
- else
- {
- CaseTestExpr *cte = makeNode(CaseTestExpr);
-
- cte->typeId = intermediate_typid;
- cte->typeMod = -1;
- cte->collation = InvalidOid;
-
- placeholder = (Node *) cte;
- }
-
- coercion = coerceJsonFuncExpr(pstate, placeholder, returning, true);
-
- if (coercion != placeholder)
- jsctor->coercion = (Expr *) coercion;
-
- return (Node *) jsctor;
-}
-
-/*
- * Transform JSON_OBJECT() constructor.
- *
- * JSON_OBJECT() is transformed into json[b]_build_object[_ext]() call
- * depending on the output JSON format. The first two arguments of
- * json[b]_build_object_ext() are absent_on_null and check_key_uniqueness.
- *
- * Then function call result is coerced to the target type.
- */
-static Node *
-transformJsonObjectConstructor(ParseState *pstate, JsonObjectConstructor *ctor)
-{
- JsonReturning *returning;
- List *args = NIL;
-
- /* transform key-value pairs, if any */
- if (ctor->exprs)
- {
- ListCell *lc;
-
- /* transform and append key-value arguments */
- foreach(lc, ctor->exprs)
- {
- JsonKeyValue *kv = castNode(JsonKeyValue, lfirst(lc));
- Node *key = transformExprRecurse(pstate, (Node *) kv->key);
- Node *val = transformJsonValueExprDefault(pstate, kv->value);
-
- args = lappend(args, key);
- args = lappend(args, val);
- }
- }
-
- returning = transformJsonConstructorOutput(pstate, ctor->output, args);
-
- return makeJsonConstructorExpr(pstate, JSCTOR_JSON_OBJECT, args, NULL,
- returning, ctor->unique,
- ctor->absent_on_null, ctor->location);
-}
-
-/*
- * Transform JSON_ARRAY(query [FORMAT] [RETURNING] [ON NULL]) into
- * (SELECT JSON_ARRAYAGG(a [FORMAT] [RETURNING] [ON NULL]) FROM (query) q(a))
- */
-static Node *
-transformJsonArrayQueryConstructor(ParseState *pstate,
- JsonArrayQueryConstructor *ctor)
-{
- SubLink *sublink = makeNode(SubLink);
- SelectStmt *select = makeNode(SelectStmt);
- RangeSubselect *range = makeNode(RangeSubselect);
- Alias *alias = makeNode(Alias);
- ResTarget *target = makeNode(ResTarget);
- JsonArrayAgg *agg = makeNode(JsonArrayAgg);
- ColumnRef *colref = makeNode(ColumnRef);
- Query *query;
- ParseState *qpstate;
-
- /* Transform query only for counting target list entries. */
- qpstate = make_parsestate(pstate);
-
- query = transformStmt(qpstate, ctor->query);
-
- if (count_nonjunk_tlist_entries(query->targetList) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("subquery must return only one column"),
- parser_errposition(pstate, ctor->location)));
-
- free_parsestate(qpstate);
-
- colref->fields = list_make2(makeString(pstrdup("q")),
- makeString(pstrdup("a")));
- colref->location = ctor->location;
-
- agg->arg = makeJsonValueExpr((Expr *) colref, ctor->format);
- agg->absent_on_null = ctor->absent_on_null;
- agg->constructor = makeNode(JsonAggConstructor);
- agg->constructor->agg_order = NIL;
- agg->constructor->output = ctor->output;
- agg->constructor->location = ctor->location;
-
- target->name = NULL;
- target->indirection = NIL;
- target->val = (Node *) agg;
- target->location = ctor->location;
-
- alias->aliasname = pstrdup("q");
- alias->colnames = list_make1(makeString(pstrdup("a")));
-
- range->lateral = false;
- range->subquery = ctor->query;
- range->alias = alias;
-
- select->targetList = list_make1(target);
- select->fromClause = list_make1(range);
-
- sublink->subLinkType = EXPR_SUBLINK;
- sublink->subLinkId = 0;
- sublink->testexpr = NULL;
- sublink->operName = NIL;
- sublink->subselect = (Node *) select;
- sublink->location = ctor->location;
-
- return transformExprRecurse(pstate, (Node *) sublink);
-}
-
-/*
- * Common code for JSON_OBJECTAGG and JSON_ARRAYAGG transformation.
- */
-static Node *
-transformJsonAggConstructor(ParseState *pstate, JsonAggConstructor *agg_ctor,
- JsonReturning *returning, List *args,
- const char *aggfn, Oid aggtype,
- JsonConstructorType ctor_type,
- bool unique, bool absent_on_null)
-{
- Oid aggfnoid;
- Node *node;
- Expr *aggfilter = agg_ctor->agg_filter ? (Expr *)
- transformWhereClause(pstate, agg_ctor->agg_filter,
- EXPR_KIND_FILTER, "FILTER") : NULL;
-
- aggfnoid = DatumGetInt32(DirectFunctionCall1(regprocin,
- CStringGetDatum(aggfn)));
-
- if (agg_ctor->over)
- {
- /* window function */
- WindowFunc *wfunc = makeNode(WindowFunc);
-
- wfunc->winfnoid = aggfnoid;
- wfunc->wintype = aggtype;
- /* wincollid and inputcollid will be set by parse_collate.c */
- wfunc->args = args;
- /* winref will be set by transformWindowFuncCall */
- wfunc->winstar = false;
- wfunc->winagg = true;
- wfunc->aggfilter = aggfilter;
- wfunc->location = agg_ctor->location;
-
- /*
- * ordered aggs not allowed in windows yet
- */
- if (agg_ctor->agg_order != NIL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("aggregate ORDER BY is not implemented for window functions"),
- parser_errposition(pstate, agg_ctor->location)));
-
- /* parse_agg.c does additional window-func-specific processing */
- transformWindowFuncCall(pstate, wfunc, agg_ctor->over);
-
- node = (Node *) wfunc;
- }
- else
- {
- Aggref *aggref = makeNode(Aggref);
-
- aggref->aggfnoid = aggfnoid;
- aggref->aggtype = aggtype;
-
- /* aggcollid and inputcollid will be set by parse_collate.c */
- aggref->aggtranstype = InvalidOid; /* will be set by planner */
- /* aggargtypes will be set by transformAggregateCall */
- /* aggdirectargs and args will be set by transformAggregateCall */
- /* aggorder and aggdistinct will be set by transformAggregateCall */
- aggref->aggfilter = aggfilter;
- aggref->aggstar = false;
- aggref->aggvariadic = false;
- aggref->aggkind = AGGKIND_NORMAL;
- aggref->aggpresorted = false;
- /* agglevelsup will be set by transformAggregateCall */
- aggref->aggsplit = AGGSPLIT_SIMPLE; /* planner might change this */
- aggref->location = agg_ctor->location;
-
- transformAggregateCall(pstate, aggref, args, agg_ctor->agg_order, false);
-
- node = (Node *) aggref;
- }
-
- return makeJsonConstructorExpr(pstate, ctor_type, NIL, (Expr *) node,
- returning, unique, absent_on_null,
- agg_ctor->location);
-}
-
-/*
- * Transform JSON_OBJECTAGG() aggregate function.
- *
- * JSON_OBJECTAGG() is transformed into
- * json[b]_objectagg(key, value, absent_on_null, check_unique) call depending on
- * the output JSON format. Then the function call result is coerced to the
- * target output type.
- */
-static Node *
-transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
-{
- JsonReturning *returning;
- Node *key;
- Node *val;
- List *args;
- const char *aggfnname;
- Oid aggtype;
-
- key = transformExprRecurse(pstate, (Node *) agg->arg->key);
- val = transformJsonValueExprDefault(pstate, agg->arg->value);
- args = list_make2(key, val);
-
- returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
- args);
-
- if (returning->format->format_type == JS_FORMAT_JSONB)
- {
- if (agg->absent_on_null)
- if (agg->unique)
- aggfnname = "pg_catalog.jsonb_object_agg_unique_strict"; /* F_JSONB_OBJECT_AGG_UNIQUE_STRICT */
- else
- aggfnname = "pg_catalog.jsonb_object_agg_strict"; /* F_JSONB_OBJECT_AGG_STRICT */
- else if (agg->unique)
- aggfnname = "pg_catalog.jsonb_object_agg_unique"; /* F_JSONB_OBJECT_AGG_UNIQUE */
- else
- aggfnname = "pg_catalog.jsonb_object_agg"; /* F_JSONB_OBJECT_AGG */
-
- aggtype = JSONBOID;
- }
- else
- {
- if (agg->absent_on_null)
- if (agg->unique)
- aggfnname = "pg_catalog.json_object_agg_unique_strict"; /* F_JSON_OBJECT_AGG_UNIQUE_STRICT */
- else
- aggfnname = "pg_catalog.json_object_agg_strict"; /* F_JSON_OBJECT_AGG_STRICT */
- else if (agg->unique)
- aggfnname = "pg_catalog.json_object_agg_unique"; /* F_JSON_OBJECT_AGG_UNIQUE */
- else
- aggfnname = "pg_catalog.json_object_agg"; /* F_JSON_OBJECT_AGG */
-
- aggtype = JSONOID;
- }
-
- return transformJsonAggConstructor(pstate, agg->constructor, returning,
- args, aggfnname, aggtype,
- JSCTOR_JSON_OBJECTAGG,
- agg->unique, agg->absent_on_null);
-}
-
-/*
- * Transform JSON_ARRAYAGG() aggregate function.
- *
- * JSON_ARRAYAGG() is transformed into json[b]_agg[_strict]() call depending
- * on the output JSON format and absent_on_null. Then the function call result
- * is coerced to the target output type.
- */
-static Node *
-transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg)
-{
- JsonReturning *returning;
- Node *arg;
- const char *aggfnname;
- Oid aggtype;
-
- arg = transformJsonValueExprDefault(pstate, agg->arg);
-
- returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
- list_make1(arg));
-
- if (returning->format->format_type == JS_FORMAT_JSONB)
- {
- aggfnname = agg->absent_on_null ?
- "pg_catalog.jsonb_agg_strict" : "pg_catalog.jsonb_agg";
- aggtype = JSONBOID;
- }
- else
- {
- aggfnname = agg->absent_on_null ?
- "pg_catalog.json_agg_strict" : "pg_catalog.json_agg";
- aggtype = JSONOID;
- }
-
- return transformJsonAggConstructor(pstate, agg->constructor, returning,
- list_make1(arg), aggfnname, aggtype,
- JSCTOR_JSON_ARRAYAGG,
- false, agg->absent_on_null);
-}
-
-/*
- * Transform JSON_ARRAY() constructor.
- *
- * JSON_ARRAY() is transformed into json[b]_build_array[_ext]() call
- * depending on the output JSON format. The first argument of
- * json[b]_build_array_ext() is absent_on_null.
- *
- * Then function call result is coerced to the target type.
- */
-static Node *
-transformJsonArrayConstructor(ParseState *pstate, JsonArrayConstructor *ctor)
-{
- JsonReturning *returning;
- List *args = NIL;
-
- /* transform element expressions, if any */
- if (ctor->exprs)
- {
- ListCell *lc;
-
- /* transform and append element arguments */
- foreach(lc, ctor->exprs)
- {
- JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
- Node *val = transformJsonValueExprDefault(pstate, jsval);
-
- args = lappend(args, val);
- }
- }
-
- returning = transformJsonConstructorOutput(pstate, ctor->output, args);
-
- return makeJsonConstructorExpr(pstate, JSCTOR_JSON_ARRAY, args, NULL,
- returning, false, ctor->absent_on_null,
- ctor->location);
-}
-
-static Node *
-transformJsonParseArg(ParseState *pstate, Node *jsexpr, JsonFormat *format,
- Oid *exprtype)
-{
- Node *raw_expr = transformExprRecurse(pstate, jsexpr);
- Node *expr = raw_expr;
-
- *exprtype = exprType(expr);
-
- /* prepare input document */
- if (*exprtype == BYTEAOID)
- {
- JsonValueExpr *jve;
-
- expr = makeCaseTestExpr(raw_expr);
- expr = makeJsonByteaToTextConversion(expr, format, exprLocation(expr));
- *exprtype = TEXTOID;
-
- jve = makeJsonValueExpr((Expr *) raw_expr, format);
-
- jve->formatted_expr = (Expr *) expr;
- expr = (Node *) jve;
- }
- else
- {
- char typcategory;
- bool typispreferred;
-
- get_type_category_preferred(*exprtype, &typcategory, &typispreferred);
-
- if (*exprtype == UNKNOWNOID || typcategory == TYPCATEGORY_STRING)
- {
- expr = coerce_to_target_type(pstate, (Node *) expr, *exprtype,
- TEXTOID, -1,
- COERCION_IMPLICIT,
- COERCE_IMPLICIT_CAST, -1);
- *exprtype = TEXTOID;
- }
-
- if (format->encoding != JS_ENC_DEFAULT)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- parser_errposition(pstate, format->location),
- errmsg("cannot use JSON FORMAT ENCODING clause for non-bytea input types")));
- }
-
- return expr;
-}
-
-/*
- * Transform IS JSON predicate.
- */
-static Node *
-transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *pred)
-{
- Oid exprtype;
- Node *expr = transformJsonParseArg(pstate, pred->expr, pred->format,
- &exprtype);
-
- /* make resulting expression */
- if (exprtype != TEXTOID && exprtype != JSONOID && exprtype != JSONBOID)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot use type %s in IS JSON predicate",
- format_type_be(exprtype))));
-
- /* This intentionally(?) drops the format clause. */
- return makeJsonIsPredicate(expr, NULL, pred->item_type,
- pred->unique_keys, pred->location);
-}
-
-/*
- * Transform a JSON PASSING clause.
- */
-static void
-transformJsonPassingArgs(ParseState *pstate, JsonFormatType format, List *args,
- List **passing_values, List **passing_names)
-{
- ListCell *lc;
-
- *passing_values = NIL;
- *passing_names = NIL;
-
- foreach(lc, args)
- {
- JsonArgument *arg = castNode(JsonArgument, lfirst(lc));
- Node *expr = transformJsonValueExprExt(pstate, arg->val,
- format, true, InvalidOid);
-
- assign_expr_collations(pstate, expr);
-
- *passing_values = lappend(*passing_values, expr);
- *passing_names = lappend(*passing_names, makeString(arg->name));
- }
-}
-
-/*
- * Transform a JSON BEHAVIOR clause.
- */
-static JsonBehavior *
-transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
- JsonBehaviorType default_behavior)
-{
- JsonBehaviorType behavior_type = default_behavior;
- Node *default_expr = NULL;
-
- if (behavior)
- {
- behavior_type = behavior->btype;
- if (behavior_type == JSON_BEHAVIOR_DEFAULT)
- default_expr = transformExprRecurse(pstate, behavior->default_expr);
- }
- return makeJsonBehavior(behavior_type, default_expr);
-}
-
-/*
- * Common code for JSON_VALUE, JSON_QUERY, JSON_EXISTS transformation
- * into a JsonExpr node.
- */
-static JsonExpr *
-transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
-{
- JsonExpr *jsexpr = makeNode(JsonExpr);
- Node *pathspec;
- JsonFormatType format;
-
- if (func->common->pathname && func->op != JSON_TABLE_OP)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("JSON_TABLE path name is not allowed here"),
- parser_errposition(pstate, func->location)));
-
- jsexpr->location = func->location;
- jsexpr->op = func->op;
- jsexpr->formatted_expr = transformJsonValueExpr(pstate, func->common->expr);
-
- assign_expr_collations(pstate, jsexpr->formatted_expr);
-
- /* format is determined by context item type */
- format = exprType(jsexpr->formatted_expr) == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON;
-
- jsexpr->result_coercion = NULL;
- jsexpr->omit_quotes = false;
-
- jsexpr->format = func->common->expr->format;
-
- pathspec = transformExprRecurse(pstate, func->common->pathspec);
-
- jsexpr->path_spec =
- coerce_to_target_type(pstate, pathspec, exprType(pathspec),
- JSONPATHOID, -1,
- COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
- exprLocation(pathspec));
- if (!jsexpr->path_spec)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("JSON path expression must be type %s, not type %s",
- "jsonpath", format_type_be(exprType(pathspec))),
- parser_errposition(pstate, exprLocation(pathspec))));
-
- /* transform and coerce to json[b] passing arguments */
- transformJsonPassingArgs(pstate, format, func->common->passing,
- &jsexpr->passing_values, &jsexpr->passing_names);
-
- if (func->op != JSON_EXISTS_OP && func->op != JSON_TABLE_OP)
- jsexpr->on_empty = transformJsonBehavior(pstate, func->on_empty,
- JSON_BEHAVIOR_NULL);
-
- if (func->op == JSON_EXISTS_OP)
- jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
- JSON_BEHAVIOR_FALSE);
- else if (func->op == JSON_TABLE_OP)
- jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
- JSON_BEHAVIOR_EMPTY);
- else
- jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
- JSON_BEHAVIOR_NULL);
-
- return jsexpr;
-}
-
-/*
- * Assign default JSON returning type from the specified format or from
- * the context item type.
- */
-static void
-assignDefaultJsonReturningType(Node *context_item, JsonFormat *context_format,
- JsonReturning *ret)
-{
- bool is_jsonb;
-
- ret->format = copyObject(context_format);
-
- if (ret->format->format_type == JS_FORMAT_DEFAULT)
- is_jsonb = exprType(context_item) == JSONBOID;
- else
- is_jsonb = ret->format->format_type == JS_FORMAT_JSONB;
-
- ret->typid = is_jsonb ? JSONBOID : JSONOID;
- ret->typmod = -1;
-}
-
-/*
- * Try to coerce expression to the output type or
- * use json_populate_type() for composite, array and domain types or
- * use coercion via I/O.
- */
-static JsonCoercion *
-coerceJsonExpr(ParseState *pstate, Node *expr, const JsonReturning *returning)
-{
- char typtype;
- JsonCoercion *coercion = makeNode(JsonCoercion);
-
- coercion->expr = coerceJsonFuncExpr(pstate, expr, returning, false);
-
- if (coercion->expr)
- {
- if (coercion->expr == expr)
- coercion->expr = NULL;
-
- return coercion;
- }
-
- typtype = get_typtype(returning->typid);
-
- if (returning->typid == RECORDOID ||
- typtype == TYPTYPE_COMPOSITE ||
- typtype == TYPTYPE_DOMAIN ||
- type_is_array(returning->typid))
- coercion->via_populate = true;
- else
- coercion->via_io = true;
-
- return coercion;
-}
-
-/*
- * Transform a JSON output clause of JSON_VALUE and JSON_QUERY.
- */
-static void
-transformJsonFuncExprOutput(ParseState *pstate, JsonFuncExpr *func,
- JsonExpr *jsexpr)
-{
- Node *expr = jsexpr->formatted_expr;
-
- jsexpr->returning = transformJsonOutput(pstate, func->output, false);
-
- /* JSON_VALUE returns text by default */
- if (func->op == JSON_VALUE_OP && !OidIsValid(jsexpr->returning->typid))
- {
- jsexpr->returning->typid = TEXTOID;
- jsexpr->returning->typmod = -1;
- }
-
- if (OidIsValid(jsexpr->returning->typid))
- {
- JsonReturning ret;
-
- if (func->op == JSON_VALUE_OP &&
- jsexpr->returning->typid != JSONOID &&
- jsexpr->returning->typid != JSONBOID)
- {
- /* Forced coercion via I/O for JSON_VALUE for non-JSON types */
- jsexpr->result_coercion = makeNode(JsonCoercion);
- jsexpr->result_coercion->expr = NULL;
- jsexpr->result_coercion->via_io = true;
- return;
- }
-
- assignDefaultJsonReturningType(jsexpr->formatted_expr, jsexpr->format, &ret);
-
- if (ret.typid != jsexpr->returning->typid ||
- ret.typmod != jsexpr->returning->typmod)
- {
- Node *placeholder = makeCaseTestExpr(expr);
-
- Assert(((CaseTestExpr *) placeholder)->typeId == ret.typid);
- Assert(((CaseTestExpr *) placeholder)->typeMod == ret.typmod);
-
- jsexpr->result_coercion = coerceJsonExpr(pstate, placeholder,
- jsexpr->returning);
- }
- }
- else
- assignDefaultJsonReturningType(jsexpr->formatted_expr, jsexpr->format,
- jsexpr->returning);
-}
-
-/*
- * Coerce an expression in JSON DEFAULT behavior to the target output type.
- */
-static Node *
-coerceDefaultJsonExpr(ParseState *pstate, JsonExpr *jsexpr, Node *defexpr)
-{
- int location;
- Oid exprtype;
-
- if (!defexpr)
- return NULL;
-
- exprtype = exprType(defexpr);
- location = exprLocation(defexpr);
-
- if (location < 0)
- location = jsexpr->location;
-
- defexpr = coerce_to_target_type(pstate,
- defexpr,
- exprtype,
- jsexpr->returning->typid,
- jsexpr->returning->typmod,
- COERCION_EXPLICIT,
- COERCE_IMPLICIT_CAST,
- location);
-
- if (!defexpr)
- ereport(ERROR,
- (errcode(ERRCODE_CANNOT_COERCE),
- errmsg("cannot cast DEFAULT expression type %s to %s",
- format_type_be(exprtype),
- format_type_be(jsexpr->returning->typid)),
- parser_errposition(pstate, location)));
-
- return defexpr;
-}
-
-/*
- * Initialize SQL/JSON item coercion from the SQL type "typid" to the target
- * "returning" type.
- */
-static JsonCoercion *
-initJsonItemCoercion(ParseState *pstate, Oid typid,
- const JsonReturning *returning)
-{
- Node *expr;
-
- if (typid == UNKNOWNOID)
- {
- expr = (Node *) makeNullConst(UNKNOWNOID, -1, InvalidOid);
- }
- else
- {
- CaseTestExpr *placeholder = makeNode(CaseTestExpr);
-
- placeholder->typeId = typid;
- placeholder->typeMod = -1;
- placeholder->collation = InvalidOid;
-
- expr = (Node *) placeholder;
- }
-
- return coerceJsonExpr(pstate, expr, returning);
-}
-
-static void
-initJsonItemCoercions(ParseState *pstate, JsonItemCoercions *coercions,
- const JsonReturning *returning, Oid contextItemTypeId)
-{
- struct
- {
- JsonCoercion **coercion;
- Oid typid;
- } *p,
- coercionTypids[] =
- {
- {&coercions->null, UNKNOWNOID},
- {&coercions->string, TEXTOID},
- {&coercions->numeric, NUMERICOID},
- {&coercions->boolean, BOOLOID},
- {&coercions->date, DATEOID},
- {&coercions->time, TIMEOID},
- {&coercions->timetz, TIMETZOID},
- {&coercions->timestamp, TIMESTAMPOID},
- {&coercions->timestamptz, TIMESTAMPTZOID},
- {&coercions->composite, contextItemTypeId},
- {NULL, InvalidOid}
- };
-
- for (p = coercionTypids; p->coercion; p++)
- *p->coercion = initJsonItemCoercion(pstate, p->typid, returning);
-}
-
-/*
- * Transform JSON_VALUE, JSON_QUERY, JSON_EXISTS functions into a JsonExpr node.
- */
-static Node *
-transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
-{
- JsonExpr *jsexpr = transformJsonExprCommon(pstate, func);
- const char *func_name = NULL;
- Node *contextItemExpr = jsexpr->formatted_expr;
-
- switch (func->op)
- {
- case JSON_VALUE_OP:
- func_name = "JSON_VALUE";
-
- transformJsonFuncExprOutput(pstate, func, jsexpr);
-
- jsexpr->returning->format->format_type = JS_FORMAT_DEFAULT;
- jsexpr->returning->format->encoding = JS_ENC_DEFAULT;
-
- jsexpr->on_empty->default_expr =
- coerceDefaultJsonExpr(pstate, jsexpr,
- jsexpr->on_empty->default_expr);
-
- jsexpr->on_error->default_expr =
- coerceDefaultJsonExpr(pstate, jsexpr,
- jsexpr->on_error->default_expr);
-
- jsexpr->coercions = makeNode(JsonItemCoercions);
- initJsonItemCoercions(pstate, jsexpr->coercions, jsexpr->returning,
- exprType(contextItemExpr));
-
- break;
-
- case JSON_QUERY_OP:
- func_name = "JSON_QUERY";
-
- transformJsonFuncExprOutput(pstate, func, jsexpr);
-
- jsexpr->on_empty->default_expr =
- coerceDefaultJsonExpr(pstate, jsexpr,
- jsexpr->on_empty->default_expr);
-
- jsexpr->on_error->default_expr =
- coerceDefaultJsonExpr(pstate, jsexpr,
- jsexpr->on_error->default_expr);
-
- jsexpr->wrapper = func->wrapper;
- jsexpr->omit_quotes = func->omit_quotes;
-
- break;
-
- case JSON_EXISTS_OP:
- func_name = "JSON_EXISTS";
-
- jsexpr->returning = transformJsonOutput(pstate, func->output, false);
-
- jsexpr->returning->format->format_type = JS_FORMAT_DEFAULT;
- jsexpr->returning->format->encoding = JS_ENC_DEFAULT;
-
- if (!OidIsValid(jsexpr->returning->typid))
- {
- jsexpr->returning->typid = BOOLOID;
- jsexpr->returning->typmod = -1;
- }
- else if (jsexpr->returning->typid != BOOLOID)
- {
- CaseTestExpr *placeholder = makeNode(CaseTestExpr);
- int location = exprLocation((Node *) jsexpr);
-
- placeholder->typeId = BOOLOID;
- placeholder->typeMod = -1;
- placeholder->collation = InvalidOid;
-
- jsexpr->result_coercion = makeNode(JsonCoercion);
- jsexpr->result_coercion->expr =
- coerce_to_target_type(pstate, (Node *) placeholder, BOOLOID,
- jsexpr->returning->typid,
- jsexpr->returning->typmod,
- COERCION_EXPLICIT,
- COERCE_IMPLICIT_CAST,
- location);
-
- if (!jsexpr->result_coercion->expr)
- ereport(ERROR,
- (errcode(ERRCODE_CANNOT_COERCE),
- errmsg("cannot cast type %s to %s",
- format_type_be(BOOLOID),
- format_type_be(jsexpr->returning->typid)),
- parser_coercion_errposition(pstate, location, (Node *) jsexpr)));
-
- if (jsexpr->result_coercion->expr == (Node *) placeholder)
- jsexpr->result_coercion->expr = NULL;
- }
- break;
-
- case JSON_TABLE_OP:
- jsexpr->returning = makeNode(JsonReturning);
- jsexpr->returning->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
- jsexpr->returning->typid = exprType(contextItemExpr);
- jsexpr->returning->typmod = -1;
-
- if (jsexpr->returning->typid != JSONBOID)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("JSON_TABLE() is not yet implemented for the json type"),
- errhint("Try casting the argument to jsonb"),
- parser_errposition(pstate, func->location)));
-
- break;
- }
-
- if (exprType(contextItemExpr) != JSONBOID)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("%s() is not yet implemented for the json type", func_name),
- errhint("Try casting the argument to jsonb"),
- parser_errposition(pstate, func->location)));
-
- return (Node *) jsexpr;
-}
-
-static JsonReturning *
-transformJsonConstructorRet(ParseState *pstate, JsonOutput *output, const char *fname)
-{
- JsonReturning *returning;
-
- if (output)
- {
- returning = transformJsonOutput(pstate, output, false);
-
- Assert(OidIsValid(returning->typid));
-
- if (returning->typid != JSONOID && returning->typid != JSONBOID)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot use RETURNING type %s in %s",
- format_type_be(returning->typid), fname),
- parser_errposition(pstate, output->typeName->location)));
- }
- else
- {
- Oid targettype = JSONOID;
- JsonFormatType format = JS_FORMAT_JSON;
-
- returning = makeNode(JsonReturning);
- returning->format = makeJsonFormat(format, JS_ENC_DEFAULT, -1);
- returning->typid = targettype;
- returning->typmod = -1;
- }
-
- return returning;
-}
-
-/*
- * Transform a JSON() expression.
- */
-static Node *
-transformJsonParseExpr(ParseState *pstate, JsonParseExpr *jsexpr)
-{
- JsonReturning *returning = transformJsonConstructorRet(pstate, jsexpr->output,
- "JSON()");
- Node *arg;
-
- if (jsexpr->unique_keys)
- {
- /*
- * Coerce string argument to text and then to json[b] in the executor
- * node with key uniqueness check.
- */
- JsonValueExpr *jve = jsexpr->expr;
- Oid arg_type;
-
- arg = transformJsonParseArg(pstate, (Node *) jve->raw_expr, jve->format,
- &arg_type);
-
- if (arg_type != TEXTOID)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot use non-string types with WITH UNIQUE KEYS clause"),
- parser_errposition(pstate, jsexpr->location)));
- }
- else
- {
- /*
- * Coerce argument to target type using CAST for compatibility with PG
- * function-like CASTs.
- */
- arg = transformJsonValueExprExt(pstate, jsexpr->expr, JS_FORMAT_JSON,
- false, returning->typid);
- }
-
- return makeJsonConstructorExpr(pstate, JSCTOR_JSON_PARSE, list_make1(arg), NULL,
- returning, jsexpr->unique_keys, false,
- jsexpr->location);
-}
-
-/*
- * Transform a JSON_SCALAR() expression.
- */
-static Node *
-transformJsonScalarExpr(ParseState *pstate, JsonScalarExpr *jsexpr)
-{
- Node *arg = transformExprRecurse(pstate, (Node *) jsexpr->expr);
- JsonReturning *returning = transformJsonConstructorRet(pstate, jsexpr->output,
- "JSON_SCALAR()");
-
- if (exprType(arg) == UNKNOWNOID)
- arg = coerce_to_specific_type(pstate, arg, TEXTOID, "JSON_SCALAR");
-
- return makeJsonConstructorExpr(pstate, JSCTOR_JSON_SCALAR, list_make1(arg), NULL,
- returning, false, false, jsexpr->location);
-}
-
-/*
- * Transform a JSON_SERIALIZE() expression.
- */
-static Node *
-transformJsonSerializeExpr(ParseState *pstate, JsonSerializeExpr *expr)
-{
- Node *arg = transformJsonValueExpr(pstate, expr->expr);
- JsonReturning *returning;
-
- if (expr->output)
- {
- returning = transformJsonOutput(pstate, expr->output, true);
-
- if (returning->typid != BYTEAOID)
- {
- char typcategory;
- bool typispreferred;
-
- get_type_category_preferred(returning->typid, &typcategory,
- &typispreferred);
- if (typcategory != TYPCATEGORY_STRING)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot use RETURNING type %s in %s",
- format_type_be(returning->typid),
- "JSON_SERIALIZE()"),
- errhint("Try returning a string type or bytea.")));
- }
- }
- else
- {
- /* RETURNING TEXT FORMAT JSON is by default */
- returning = makeNode(JsonReturning);
- returning->format = makeJsonFormat(JS_FORMAT_JSON, JS_ENC_DEFAULT, -1);
- returning->typid = TEXTOID;
- returning->typmod = -1;
- }
-
- return makeJsonConstructorExpr(pstate, JSCTOR_JSON_SERIALIZE, list_make1(arg),
- NULL, returning, false, false, expr->location);
-}
+++ /dev/null
-/*-------------------------------------------------------------------------
- *
- * parse_jsontable.c
- * parsing of JSON_TABLE
- *
- * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/backend/parser/parse_jsontable.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "catalog/pg_collation.h"
-#include "catalog/pg_type.h"
-#include "miscadmin.h"
-#include "nodes/makefuncs.h"
-#include "nodes/nodeFuncs.h"
-#include "optimizer/optimizer.h"
-#include "parser/parse_clause.h"
-#include "parser/parse_collate.h"
-#include "parser/parse_expr.h"
-#include "parser/parse_relation.h"
-#include "parser/parse_type.h"
-#include "utils/builtins.h"
-#include "utils/json.h"
-#include "utils/lsyscache.h"
-
-/* Context for JSON_TABLE transformation */
-typedef struct JsonTableContext
-{
- ParseState *pstate; /* parsing state */
- JsonTable *table; /* untransformed node */
- TableFunc *tablefunc; /* transformed node */
- List *pathNames; /* list of all path and columns names */
- int pathNameId; /* path name id counter */
- Oid contextItemTypid; /* type oid of context item (json/jsonb) */
-} JsonTableContext;
-
-static JsonTableParent *transformJsonTableColumns(JsonTableContext *cxt,
- JsonTablePlan *plan,
- List *columns,
- char *pathSpec,
- char **pathName,
- int location);
-
-static Node *
-makeStringConst(char *str, int location)
-{
- A_Const *n = makeNode(A_Const);
-
- n->val.node.type = T_String;
- n->val.sval.sval = str;
- n->location = location;
-
- return (Node *) n;
-}
-
-/*
- * Transform JSON_TABLE column
- * - regular column into JSON_VALUE()
- * - FORMAT JSON column into JSON_QUERY()
- * - EXISTS column into JSON_EXISTS()
- */
-static Node *
-transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr,
- List *passingArgs, bool errorOnError)
-{
- JsonFuncExpr *jfexpr = makeNode(JsonFuncExpr);
- JsonCommon *common = makeNode(JsonCommon);
- JsonOutput *output = makeNode(JsonOutput);
- char *pathspec;
- JsonFormat *default_format;
-
- jfexpr->op =
- jtc->coltype == JTC_REGULAR ? JSON_VALUE_OP :
- jtc->coltype == JTC_EXISTS ? JSON_EXISTS_OP : JSON_QUERY_OP;
- jfexpr->common = common;
- jfexpr->output = output;
- jfexpr->on_empty = jtc->on_empty;
- jfexpr->on_error = jtc->on_error;
- if (!jfexpr->on_error && errorOnError)
- jfexpr->on_error = makeJsonBehavior(JSON_BEHAVIOR_ERROR, NULL);
- jfexpr->omit_quotes = jtc->omit_quotes;
- jfexpr->wrapper = jtc->wrapper;
- jfexpr->location = jtc->location;
-
- output->typeName = jtc->typeName;
- output->returning = makeNode(JsonReturning);
- output->returning->format = jtc->format;
-
- default_format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
-
- common->pathname = NULL;
- common->expr = makeJsonValueExpr((Expr *) contextItemExpr, default_format);
- common->passing = passingArgs;
-
- if (jtc->pathspec)
- pathspec = jtc->pathspec;
- else
- {
- /* Construct default path as '$."column_name"' */
- StringInfoData path;
-
- initStringInfo(&path);
-
- appendStringInfoString(&path, "$.");
- escape_json(&path, jtc->name);
-
- pathspec = path.data;
- }
-
- common->pathspec = makeStringConst(pathspec, -1);
-
- return (Node *) jfexpr;
-}
-
-static bool
-isJsonTablePathNameDuplicate(JsonTableContext *cxt, const char *pathname)
-{
- ListCell *lc;
-
- foreach(lc, cxt->pathNames)
- {
- if (!strcmp(pathname, (const char *) lfirst(lc)))
- return true;
- }
-
- return false;
-}
-
-/* Register the column name in the path name list. */
-static void
-registerJsonTableColumn(JsonTableContext *cxt, char *colname)
-{
- if (isJsonTablePathNameDuplicate(cxt, colname))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_ALIAS),
- errmsg("duplicate JSON_TABLE column name: %s", colname),
- errhint("JSON_TABLE column names must be distinct from one another.")));
-
- cxt->pathNames = lappend(cxt->pathNames, colname);
-}
-
-/* Recursively register all nested column names in the path name list. */
-static void
-registerAllJsonTableColumns(JsonTableContext *cxt, List *columns)
-{
- ListCell *lc;
-
- foreach(lc, columns)
- {
- JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc));
-
- if (jtc->coltype == JTC_NESTED)
- {
- if (jtc->pathname)
- registerJsonTableColumn(cxt, jtc->pathname);
-
- registerAllJsonTableColumns(cxt, jtc->columns);
- }
- else
- {
- registerJsonTableColumn(cxt, jtc->name);
- }
- }
-}
-
-/* Generate a new unique JSON_TABLE path name. */
-static char *
-generateJsonTablePathName(JsonTableContext *cxt)
-{
- char namebuf[32];
- char *name = namebuf;
-
- do
- {
- snprintf(namebuf, sizeof(namebuf), "json_table_path_%d",
- ++cxt->pathNameId);
- } while (isJsonTablePathNameDuplicate(cxt, name));
-
- name = pstrdup(name);
- cxt->pathNames = lappend(cxt->pathNames, name);
-
- return name;
-}
-
-/* Collect sibling path names from plan to the specified list. */
-static void
-collectSiblingPathsInJsonTablePlan(JsonTablePlan *plan, List **paths)
-{
- if (plan->plan_type == JSTP_SIMPLE)
- *paths = lappend(*paths, plan->pathname);
- else if (plan->plan_type == JSTP_JOINED)
- {
- if (plan->join_type == JSTPJ_INNER ||
- plan->join_type == JSTPJ_OUTER)
- {
- Assert(plan->plan1->plan_type == JSTP_SIMPLE);
- *paths = lappend(*paths, plan->plan1->pathname);
- }
- else if (plan->join_type == JSTPJ_CROSS ||
- plan->join_type == JSTPJ_UNION)
- {
- collectSiblingPathsInJsonTablePlan(plan->plan1, paths);
- collectSiblingPathsInJsonTablePlan(plan->plan2, paths);
- }
- else
- elog(ERROR, "invalid JSON_TABLE join type %d",
- plan->join_type);
- }
-}
-
-/*
- * Validate child JSON_TABLE plan by checking that:
- * - all nested columns have path names specified
- * - all nested columns have corresponding node in the sibling plan
- * - plan does not contain duplicate or extra nodes
- */
-static void
-validateJsonTableChildPlan(ParseState *pstate, JsonTablePlan *plan,
- List *columns)
-{
- ListCell *lc1;
- List *siblings = NIL;
- int nchildren = 0;
-
- if (plan)
- collectSiblingPathsInJsonTablePlan(plan, &siblings);
-
- foreach(lc1, columns)
- {
- JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc1));
-
- if (jtc->coltype == JTC_NESTED)
- {
- ListCell *lc2;
- bool found = false;
-
- if (!jtc->pathname)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("nested JSON_TABLE columns must contain an explicit AS pathname specification if an explicit PLAN clause is used"),
- parser_errposition(pstate, jtc->location)));
-
- /* find nested path name in the list of sibling path names */
- foreach(lc2, siblings)
- {
- if ((found = !strcmp(jtc->pathname, lfirst(lc2))))
- break;
- }
-
- if (!found)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid JSON_TABLE plan"),
- errdetail("Plan node for nested path %s was not found in plan.", jtc->pathname),
- parser_errposition(pstate, jtc->location)));
-
- nchildren++;
- }
- }
-
- if (list_length(siblings) > nchildren)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid JSON_TABLE plan"),
- errdetail("Plan node contains some extra or duplicate sibling nodes."),
- parser_errposition(pstate, plan ? plan->location : -1)));
-}
-
-static JsonTableColumn *
-findNestedJsonTableColumn(List *columns, const char *pathname)
-{
- ListCell *lc;
-
- foreach(lc, columns)
- {
- JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc));
-
- if (jtc->coltype == JTC_NESTED &&
- jtc->pathname &&
- !strcmp(jtc->pathname, pathname))
- return jtc;
- }
-
- return NULL;
-}
-
-static Node *
-transformNestedJsonTableColumn(JsonTableContext *cxt, JsonTableColumn *jtc,
- JsonTablePlan *plan)
-{
- JsonTableParent *node;
- char *pathname = jtc->pathname;
-
- node = transformJsonTableColumns(cxt, plan, jtc->columns, jtc->pathspec,
- &pathname, jtc->location);
- node->name = pstrdup(pathname);
-
- return (Node *) node;
-}
-
-static Node *
-makeJsonTableSiblingJoin(bool cross, Node *lnode, Node *rnode)
-{
- JsonTableSibling *join = makeNode(JsonTableSibling);
-
- join->larg = lnode;
- join->rarg = rnode;
- join->cross = cross;
-
- return (Node *) join;
-}
-
-/*
- * Recursively transform child JSON_TABLE plan.
- *
- * Default plan is transformed into a cross/union join of its nested columns.
- * Simple and outer/inner plans are transformed into a JsonTableParent by
- * finding and transforming corresponding nested column.
- * Sibling plans are recursively transformed into a JsonTableSibling.
- */
-static Node *
-transformJsonTableChildPlan(JsonTableContext *cxt, JsonTablePlan *plan,
- List *columns)
-{
- JsonTableColumn *jtc = NULL;
-
- if (!plan || plan->plan_type == JSTP_DEFAULT)
- {
- /* unspecified or default plan */
- Node *res = NULL;
- ListCell *lc;
- bool cross = plan && (plan->join_type & JSTPJ_CROSS);
-
- /* transform all nested columns into cross/union join */
- foreach(lc, columns)
- {
- JsonTableColumn *col = castNode(JsonTableColumn, lfirst(lc));
- Node *node;
-
- if (col->coltype != JTC_NESTED)
- continue;
-
- node = transformNestedJsonTableColumn(cxt, col, plan);
-
- /* join transformed node with previous sibling nodes */
- res = res ? makeJsonTableSiblingJoin(cross, res, node) : node;
- }
-
- return res;
- }
- else if (plan->plan_type == JSTP_SIMPLE)
- {
- jtc = findNestedJsonTableColumn(columns, plan->pathname);
- }
- else if (plan->plan_type == JSTP_JOINED)
- {
- if (plan->join_type == JSTPJ_INNER ||
- plan->join_type == JSTPJ_OUTER)
- {
- Assert(plan->plan1->plan_type == JSTP_SIMPLE);
- jtc = findNestedJsonTableColumn(columns, plan->plan1->pathname);
- }
- else
- {
- Node *node1 = transformJsonTableChildPlan(cxt, plan->plan1,
- columns);
- Node *node2 = transformJsonTableChildPlan(cxt, plan->plan2,
- columns);
-
- return makeJsonTableSiblingJoin(plan->join_type == JSTPJ_CROSS,
- node1, node2);
- }
- }
- else
- elog(ERROR, "invalid JSON_TABLE plan type %d", plan->plan_type);
-
- if (!jtc)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid JSON_TABLE plan"),
- errdetail("Path name was %s not found in nested columns list.",
- plan->pathname),
- parser_errposition(cxt->pstate, plan->location)));
-
- return transformNestedJsonTableColumn(cxt, jtc, plan);
-}
-
-/* Check whether type is json/jsonb, array, or record. */
-static bool
-typeIsComposite(Oid typid)
-{
- char typtype;
-
- if (typid == JSONOID ||
- typid == JSONBOID ||
- typid == RECORDOID ||
- type_is_array(typid))
- return true;
-
- typtype = get_typtype(typid);
-
- if (typtype == TYPTYPE_COMPOSITE)
- return true;
-
- if (typtype == TYPTYPE_DOMAIN)
- return typeIsComposite(getBaseType(typid));
-
- return false;
-}
-
-/* Append transformed non-nested JSON_TABLE columns to the TableFunc node */
-static void
-appendJsonTableColumns(JsonTableContext *cxt, List *columns)
-{
- ListCell *col;
- ParseState *pstate = cxt->pstate;
- JsonTable *jt = cxt->table;
- TableFunc *tf = cxt->tablefunc;
- bool errorOnError = jt->on_error &&
- jt->on_error->btype == JSON_BEHAVIOR_ERROR;
-
- foreach(col, columns)
- {
- JsonTableColumn *rawc = castNode(JsonTableColumn, lfirst(col));
- Oid typid;
- int32 typmod;
- Node *colexpr;
-
- if (rawc->name)
- {
- /* make sure column names are unique */
- ListCell *colname;
-
- foreach(colname, tf->colnames)
- if (!strcmp((const char *) colname, rawc->name))
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("column name \"%s\" is not unique",
- rawc->name),
- parser_errposition(pstate, rawc->location)));
-
- tf->colnames = lappend(tf->colnames,
- makeString(pstrdup(rawc->name)));
- }
-
- /*
- * Determine the type and typmod for the new column. FOR ORDINALITY
- * columns are INTEGER by standard; the others are user-specified.
- */
- switch (rawc->coltype)
- {
- case JTC_FOR_ORDINALITY:
- colexpr = NULL;
- typid = INT4OID;
- typmod = -1;
- break;
-
- case JTC_REGULAR:
- typenameTypeIdAndMod(pstate, rawc->typeName, &typid, &typmod);
-
- /*
- * Use implicit FORMAT JSON for composite types (arrays and
- * records)
- */
- if (typeIsComposite(typid))
- rawc->coltype = JTC_FORMATTED;
- else if (rawc->wrapper != JSW_NONE)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("cannot use WITH WRAPPER clause with scalar columns"),
- parser_errposition(pstate, rawc->location)));
- else if (rawc->omit_quotes)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("cannot use OMIT QUOTES clause with scalar columns"),
- parser_errposition(pstate, rawc->location)));
-
- /* FALLTHROUGH */
- case JTC_EXISTS:
- case JTC_FORMATTED:
- {
- Node *je;
- CaseTestExpr *param = makeNode(CaseTestExpr);
-
- param->collation = InvalidOid;
- param->typeId = cxt->contextItemTypid;
- param->typeMod = -1;
-
- je = transformJsonTableColumn(rawc, (Node *) param,
- NIL, errorOnError);
-
- colexpr = transformExpr(pstate, je, EXPR_KIND_FROM_FUNCTION);
- assign_expr_collations(pstate, colexpr);
-
- typid = exprType(colexpr);
- typmod = exprTypmod(colexpr);
- break;
- }
-
- case JTC_NESTED:
- continue;
-
- default:
- elog(ERROR, "unknown JSON_TABLE column type: %d", rawc->coltype);
- break;
- }
-
- tf->coltypes = lappend_oid(tf->coltypes, typid);
- tf->coltypmods = lappend_int(tf->coltypmods, typmod);
- tf->colcollations = lappend_oid(tf->colcollations, get_typcollation(typid));
- tf->colvalexprs = lappend(tf->colvalexprs, colexpr);
- }
-}
-
-/*
- * Create transformed JSON_TABLE parent plan node by appending all non-nested
- * columns to the TableFunc node and remembering their indices in the
- * colvalexprs list.
- */
-static JsonTableParent *
-makeParentJsonTableNode(JsonTableContext *cxt, char *pathSpec, List *columns)
-{
- JsonTableParent *node = makeNode(JsonTableParent);
-
- node->path = makeConst(JSONPATHOID, -1, InvalidOid, -1,
- DirectFunctionCall1(jsonpath_in,
- CStringGetDatum(pathSpec)),
- false, false);
-
- /* save start of column range */
- node->colMin = list_length(cxt->tablefunc->colvalexprs);
-
- appendJsonTableColumns(cxt, columns);
-
- /* save end of column range */
- node->colMax = list_length(cxt->tablefunc->colvalexprs) - 1;
-
- node->errorOnError =
- cxt->table->on_error &&
- cxt->table->on_error->btype == JSON_BEHAVIOR_ERROR;
-
- return node;
-}
-
-static JsonTableParent *
-transformJsonTableColumns(JsonTableContext *cxt, JsonTablePlan *plan,
- List *columns, char *pathSpec, char **pathName,
- int location)
-{
- JsonTableParent *node;
- JsonTablePlan *childPlan;
- bool defaultPlan = !plan || plan->plan_type == JSTP_DEFAULT;
-
- if (!*pathName)
- {
- if (cxt->table->plan)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid JSON_TABLE expression"),
- errdetail("JSON_TABLE columns must contain "
- "explicit AS pathname specification if "
- "explicit PLAN clause is used"),
- parser_errposition(cxt->pstate, location)));
-
- *pathName = generateJsonTablePathName(cxt);
- }
-
- if (defaultPlan)
- childPlan = plan;
- else
- {
- /* validate parent and child plans */
- JsonTablePlan *parentPlan;
-
- if (plan->plan_type == JSTP_JOINED)
- {
- if (plan->join_type != JSTPJ_INNER &&
- plan->join_type != JSTPJ_OUTER)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid JSON_TABLE plan"),
- errdetail("Expected INNER or OUTER JSON_TABLE plan node."),
- parser_errposition(cxt->pstate, plan->location)));
-
- parentPlan = plan->plan1;
- childPlan = plan->plan2;
-
- Assert(parentPlan->plan_type != JSTP_JOINED);
- Assert(parentPlan->pathname);
- }
- else
- {
- parentPlan = plan;
- childPlan = NULL;
- }
-
- if (strcmp(parentPlan->pathname, *pathName))
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid JSON_TABLE plan"),
- errdetail("Path name mismatch: expected %s but %s is given.",
- *pathName, parentPlan->pathname),
- parser_errposition(cxt->pstate, plan->location)));
-
- validateJsonTableChildPlan(cxt->pstate, childPlan, columns);
- }
-
- /* transform only non-nested columns */
- node = makeParentJsonTableNode(cxt, pathSpec, columns);
- node->name = pstrdup(*pathName);
-
- if (childPlan || defaultPlan)
- {
- /* transform recursively nested columns */
- node->child = transformJsonTableChildPlan(cxt, childPlan, columns);
- if (node->child)
- node->outerJoin = !plan || (plan->join_type & JSTPJ_OUTER);
- /* else: default plan case, no children found */
- }
-
- return node;
-}
-
-/*
- * transformJsonTable -
- * Transform a raw JsonTable into TableFunc.
- *
- * Transform the document-generating expression, the row-generating expression,
- * the column-generating expressions, and the default value expressions.
- */
-ParseNamespaceItem *
-transformJsonTable(ParseState *pstate, JsonTable *jt)
-{
- JsonTableContext cxt;
- TableFunc *tf = makeNode(TableFunc);
- JsonFuncExpr *jfe = makeNode(JsonFuncExpr);
- JsonTablePlan *plan = jt->plan;
- JsonCommon *jscommon;
- char *rootPathName = jt->common->pathname;
- char *rootPath;
- bool is_lateral;
-
- cxt.pstate = pstate;
- cxt.table = jt;
- cxt.tablefunc = tf;
- cxt.pathNames = NIL;
- cxt.pathNameId = 0;
-
- if (rootPathName)
- registerJsonTableColumn(&cxt, rootPathName);
-
- registerAllJsonTableColumns(&cxt, jt->columns);
-
-#if 0 /* XXX it' unclear from the standard whether
- * root path name is mandatory or not */
- if (plan && plan->plan_type != JSTP_DEFAULT && !rootPathName)
- {
- /* Assign root path name and create corresponding plan node */
- JsonTablePlan *rootNode = makeNode(JsonTablePlan);
- JsonTablePlan *rootPlan = (JsonTablePlan *)
- makeJsonTableJoinedPlan(JSTPJ_OUTER, (Node *) rootNode,
- (Node *) plan, jt->location);
-
- rootPathName = generateJsonTablePathName(&cxt);
-
- rootNode->plan_type = JSTP_SIMPLE;
- rootNode->pathname = rootPathName;
-
- plan = rootPlan;
- }
-#endif
-
- jscommon = copyObject(jt->common);
- jscommon->pathspec = makeStringConst(pstrdup("$"), -1);
-
- jfe->op = JSON_TABLE_OP;
- jfe->common = jscommon;
- jfe->on_error = jt->on_error;
- jfe->location = jt->common->location;
-
- /*
- * We make lateral_only names of this level visible, whether or not the
- * RangeTableFunc is explicitly marked LATERAL. This is needed for SQL
- * spec compliance and seems useful on convenience grounds for all
- * functions in FROM.
- *
- * (LATERAL can't nest within a single pstate level, so we don't need
- * save/restore logic here.)
- */
- Assert(!pstate->p_lateral_active);
- pstate->p_lateral_active = true;
-
- tf->functype = TFT_JSON_TABLE;
- tf->docexpr = transformExpr(pstate, (Node *) jfe, EXPR_KIND_FROM_FUNCTION);
-
- cxt.contextItemTypid = exprType(tf->docexpr);
-
- if (!IsA(jt->common->pathspec, A_Const) ||
- castNode(A_Const, jt->common->pathspec)->val.node.type != T_String)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only string constants supported in JSON_TABLE path specification"),
- parser_errposition(pstate,
- exprLocation(jt->common->pathspec))));
-
- rootPath = castNode(A_Const, jt->common->pathspec)->val.sval.sval;
-
- tf->plan = (Node *) transformJsonTableColumns(&cxt, plan, jt->columns,
- rootPath, &rootPathName,
- jt->common->location);
-
- tf->ordinalitycol = -1; /* undefine ordinality column number */
- tf->location = jt->location;
-
- pstate->p_lateral_active = false;
-
- /*
- * Mark the RTE as LATERAL if the user said LATERAL explicitly, or if
- * there are any lateral cross-references in it.
- */
- is_lateral = jt->lateral || contain_vars_of_level((Node *) tf, 0);
-
- return addRangeTableEntryForTableFunc(pstate,
- tf, jt->alias, is_lateral, true);
-}
bool inFromCl)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
- char *refname;
+ char *refname = alias ? alias->aliasname : pstrdup("xmltable");
Alias *eref;
int numaliases;
Assert(list_length(tf->coltypmods) == list_length(tf->colnames));
Assert(list_length(tf->colcollations) == list_length(tf->colnames));
- refname = alias ? alias->aliasname :
- pstrdup(tf->functype == TFT_XMLTABLE ? "xmltable" : "json_table");
+ refname = alias ? alias->aliasname : pstrdup("xmltable");
rte->rtekind = RTE_TABLEFUNC;
rte->relid = InvalidOid;
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("%s function has %d columns available but %d columns specified",
- tf->functype == TFT_XMLTABLE ? "XMLTABLE" : "JSON_TABLE",
+ "XMLTABLE",
list_length(tf->colnames), numaliases)));
rte->eref = eref;
case T_XmlSerialize:
*name = "xmlserialize";
return 2;
- case T_JsonParseExpr:
- *name = "json";
- return 2;
- case T_JsonScalarExpr:
- *name = "json_scalar";
- return 2;
- case T_JsonSerializeExpr:
- *name = "json_serialize";
- return 2;
- case T_JsonObjectConstructor:
- *name = "json_object";
- return 2;
- case T_JsonArrayConstructor:
- case T_JsonArrayQueryConstructor:
- *name = "json_array";
- return 2;
- case T_JsonObjectAgg:
- *name = "json_objectagg";
- return 2;
- case T_JsonArrayAgg:
- *name = "json_arrayagg";
- return 2;
- case T_JsonFuncExpr:
- /* make SQL/JSON functions act like a regular function */
- switch (((JsonFuncExpr *) node)->op)
- {
- case JSON_QUERY_OP:
- *name = "json_query";
- return 2;
- case JSON_VALUE_OP:
- *name = "json_value";
- return 2;
- case JSON_EXISTS_OP:
- *name = "json_exists";
- return 2;
- case JSON_TABLE_OP:
- *name = "json_table";
- return 2;
- }
- break;
default:
break;
}
case USCONST:
cur_token_length = strlen(yyextra->core_yy_extra.scanbuf + *llocp);
break;
- case WITHOUT:
- cur_token_length = 7;
- break;
default:
return cur_token;
}
case ORDINALITY:
cur_token = WITH_LA;
break;
- case UNIQUE:
- cur_token = WITH_LA_UNIQUE;
- break;
- }
- break;
-
- case WITHOUT:
- /* Replace WITHOUT by WITHOUT_LA if it's followed by TIME */
- switch (next_token)
- {
- case TIME:
- cur_token = WITHOUT_LA;
- break;
}
break;
else
buf = pstrdup("character varying");
break;
-
- case JSONOID:
- buf = pstrdup("json");
- break;
}
if (buf == NULL)
*L_currency_symbol;
} NUMProc;
+/* Return flags for DCH_from_char() */
+#define DCH_DATED 0x01
+#define DCH_TIMED 0x02
+#define DCH_ZONED 0x04
+
/* ----------
* Functions
* ----------
NUM_TOCHAR_finish;
PG_RETURN_TEXT_P(result);
}
-
-int
-datetime_format_flags(const char *fmt_str, bool *have_error)
-{
- bool incache;
- int fmt_len = strlen(fmt_str);
- int result;
- FormatNode *format;
-
- if (fmt_len > DCH_CACHE_SIZE)
- {
- /*
- * Allocate new memory if format picture is bigger than static cache
- * and do not use cache (call parser always)
- */
- incache = false;
-
- format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
-
- parse_format(format, fmt_str, DCH_keywords,
- DCH_suff, DCH_index, DCH_FLAG, NULL);
- }
- else
- {
- /*
- * Use cache buffers
- */
- DCHCacheEntry *ent = DCH_cache_fetch(fmt_str, false);
-
- incache = true;
- format = ent->format;
- }
-
- result = DCH_datetime_type(format, have_error);
-
- if (!incache)
- pfree(format);
-
- return result;
-}
*/
#include "postgres.h"
-#include "access/hash.h"
-#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
-#include "common/hashfn.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
-/* Common context for key uniqueness check */
-typedef struct HTAB *JsonUniqueCheckState; /* hash table for key names */
-
-/* Hash entry for JsonUniqueCheckState */
-typedef struct JsonUniqueHashEntry
-{
- const char *key;
- int key_len;
- int object_id;
-} JsonUniqueHashEntry;
-
-/* Context for key uniqueness check in builder functions */
-typedef struct JsonUniqueBuilderState
-{
- JsonUniqueCheckState check; /* unique check */
- StringInfoData skipped_keys; /* skipped keys with NULL values */
- MemoryContext mcxt; /* context for saving skipped keys */
-} JsonUniqueBuilderState;
-
-/* Element of object stack for key uniqueness check during json parsing */
-typedef struct JsonUniqueStackEntry
+typedef enum /* type categories for datum_to_json */
{
- struct JsonUniqueStackEntry *parent;
- int object_id;
-} JsonUniqueStackEntry;
-
-/* State for key uniqueness check during json parsing */
-typedef struct JsonUniqueParsingState
-{
- JsonLexContext *lex;
- JsonUniqueCheckState check;
- JsonUniqueStackEntry *stack;
- int id_counter;
- bool unique;
-} JsonUniqueParsingState;
+ JSONTYPE_NULL, /* null, so we didn't bother to identify */
+ JSONTYPE_BOOL, /* boolean (built-in types only) */
+ JSONTYPE_NUMERIC, /* numeric (ditto) */
+ JSONTYPE_DATE, /* we use special formatting for datetimes */
+ JSONTYPE_TIMESTAMP,
+ JSONTYPE_TIMESTAMPTZ,
+ JSONTYPE_JSON, /* JSON itself (and JSONB) */
+ JSONTYPE_ARRAY, /* array */
+ JSONTYPE_COMPOSITE, /* composite */
+ JSONTYPE_CAST, /* something with an explicit cast to JSON */
+ JSONTYPE_OTHER /* all else */
+} JsonTypeCategory;
typedef struct JsonAggState
{
Oid key_output_func;
JsonTypeCategory val_category;
Oid val_output_func;
- JsonUniqueBuilderState unique_check;
} JsonAggState;
static void composite_to_json(Datum composite, StringInfo result,
bool use_line_feeds);
static void array_to_json_internal(Datum array, StringInfo result,
bool use_line_feeds);
+static void json_categorize_type(Oid typoid,
+ JsonTypeCategory *tcategory,
+ Oid *outfuncoid);
static void datum_to_json(Datum val, bool is_null, StringInfo result,
JsonTypeCategory tcategory, Oid outfuncoid,
bool key_scalar);
* output function OID. If the returned category is JSONTYPE_CAST, we
* return the OID of the type->JSON cast function instead.
*/
-void
+static void
json_categorize_type(Oid typoid,
JsonTypeCategory *tcategory,
Oid *outfuncoid)
PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
}
-Datum
-to_json_worker(Datum val, JsonTypeCategory tcategory, Oid outfuncoid)
-{
- StringInfo result = makeStringInfo();
-
- datum_to_json(val, false, result, tcategory, outfuncoid, false);
-
- return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
-}
-
-bool
-to_json_is_immutable(Oid typoid)
-{
- JsonTypeCategory tcategory;
- Oid outfuncoid;
-
- json_categorize_type(typoid, &tcategory, &outfuncoid);
-
- switch (tcategory)
- {
- case JSONTYPE_BOOL:
- case JSONTYPE_JSON:
- return true;
-
- case JSONTYPE_DATE:
- case JSONTYPE_TIMESTAMP:
- case JSONTYPE_TIMESTAMPTZ:
- return false;
-
- case JSONTYPE_ARRAY:
- return false; /* TODO recurse into elements */
-
- case JSONTYPE_COMPOSITE:
- return false; /* TODO recurse into fields */
-
- case JSONTYPE_NUMERIC:
- case JSONTYPE_CAST:
- default:
- return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
- }
-}
-
/*
* SQL function to_json(anyvalue)
*/
{
Datum val = PG_GETARG_DATUM(0);
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+ StringInfo result;
JsonTypeCategory tcategory;
Oid outfuncoid;
json_categorize_type(val_type,
&tcategory, &outfuncoid);
- PG_RETURN_DATUM(to_json_worker(val, tcategory, outfuncoid));
+ result = makeStringInfo();
+
+ datum_to_json(val, false, result, tcategory, outfuncoid, false);
+
+ PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
}
/*
*
* aggregate input column as a json array value.
*/
-static Datum
-json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
+Datum
+json_agg_transfn(PG_FUNCTION_ARGS)
{
MemoryContext aggcontext,
oldcontext;
else
{
state = (JsonAggState *) PG_GETARG_POINTER(0);
- }
-
- if (absent_on_null && PG_ARGISNULL(1))
- PG_RETURN_POINTER(state);
-
- if (state->str->len > 1)
appendStringInfoString(state->str, ", ");
+ }
/* fast path for NULLs */
if (PG_ARGISNULL(1))
val = PG_GETARG_DATUM(1);
/* add some whitespace if structured type and not first item */
- if (!PG_ARGISNULL(0) && state->str->len > 1 &&
+ if (!PG_ARGISNULL(0) &&
(state->val_category == JSONTYPE_ARRAY ||
state->val_category == JSONTYPE_COMPOSITE))
{
PG_RETURN_POINTER(state);
}
-
-/*
- * json_agg aggregate function
- */
-Datum
-json_agg_transfn(PG_FUNCTION_ARGS)
-{
- return json_agg_transfn_worker(fcinfo, false);
-}
-
-/*
- * json_agg_strict aggregate function
- */
-Datum
-json_agg_strict_transfn(PG_FUNCTION_ARGS)
-{
- return json_agg_transfn_worker(fcinfo, true);
-}
-
/*
* json_agg final function
*/
PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
}
-/* Functions implementing hash table for key uniqueness check */
-static uint32
-json_unique_hash(const void *key, Size keysize)
-{
- const JsonUniqueHashEntry *entry = (JsonUniqueHashEntry *) key;
- uint32 hash = hash_bytes_uint32(entry->object_id);
-
- hash ^= hash_bytes((const unsigned char *) entry->key, entry->key_len);
-
- return DatumGetUInt32(hash);
-}
-
-static int
-json_unique_hash_match(const void *key1, const void *key2, Size keysize)
-{
- const JsonUniqueHashEntry *entry1 = (const JsonUniqueHashEntry *) key1;
- const JsonUniqueHashEntry *entry2 = (const JsonUniqueHashEntry *) key2;
-
- if (entry1->object_id != entry2->object_id)
- return entry1->object_id > entry2->object_id ? 1 : -1;
-
- if (entry1->key_len != entry2->key_len)
- return entry1->key_len > entry2->key_len ? 1 : -1;
-
- return strncmp(entry1->key, entry2->key, entry1->key_len);
-}
-
-/* Functions implementing object key uniqueness check */
-static void
-json_unique_check_init(JsonUniqueCheckState *cxt)
-{
- HASHCTL ctl;
-
- memset(&ctl, 0, sizeof(ctl));
- ctl.keysize = sizeof(JsonUniqueHashEntry);
- ctl.entrysize = sizeof(JsonUniqueHashEntry);
- ctl.hcxt = CurrentMemoryContext;
- ctl.hash = json_unique_hash;
- ctl.match = json_unique_hash_match;
-
- *cxt = hash_create("json object hashtable",
- 32,
- &ctl,
- HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
-}
-
-static bool
-json_unique_check_key(JsonUniqueCheckState *cxt, const char *key, int object_id)
-{
- JsonUniqueHashEntry entry;
- bool found;
-
- entry.key = key;
- entry.key_len = strlen(key);
- entry.object_id = object_id;
-
- (void) hash_search(*cxt, &entry, HASH_ENTER, &found);
-
- return !found;
-}
-
-static void
-json_unique_builder_init(JsonUniqueBuilderState *cxt)
-{
- json_unique_check_init(&cxt->check);
- cxt->mcxt = CurrentMemoryContext;
- cxt->skipped_keys.data = NULL;
-}
-
-/* On-demand initialization of skipped_keys StringInfo structure */
-static StringInfo
-json_unique_builder_get_skipped_keys(JsonUniqueBuilderState *cxt)
-{
- StringInfo out = &cxt->skipped_keys;
-
- if (!out->data)
- {
- MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt);
-
- initStringInfo(out);
- MemoryContextSwitchTo(oldcxt);
- }
-
- return out;
-}
-
/*
* json_object_agg transition function.
*
* aggregate two input columns as a single json object value.
*/
-static Datum
-json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
- bool absent_on_null, bool unique_keys)
+Datum
+json_object_agg_transfn(PG_FUNCTION_ARGS)
{
MemoryContext aggcontext,
oldcontext;
JsonAggState *state;
- StringInfo out;
Datum arg;
- bool skip;
- int key_offset;
if (!AggCheckCallContext(fcinfo, &aggcontext))
{
oldcontext = MemoryContextSwitchTo(aggcontext);
state = (JsonAggState *) palloc(sizeof(JsonAggState));
state->str = makeStringInfo();
- if (unique_keys)
- json_unique_builder_init(&state->unique_check);
- else
- memset(&state->unique_check, 0, sizeof(state->unique_check));
MemoryContextSwitchTo(oldcontext);
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
else
{
state = (JsonAggState *) PG_GETARG_POINTER(0);
+ appendStringInfoString(state->str, ", ");
}
/*
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("field name must not be null")));
- /* Skip null values if absent_on_null */
- skip = absent_on_null && PG_ARGISNULL(2);
-
- if (skip)
- {
- /* If key uniqueness check is needed we must save skipped keys */
- if (!unique_keys)
- PG_RETURN_POINTER(state);
-
- out = json_unique_builder_get_skipped_keys(&state->unique_check);
- }
- else
- {
- out = state->str;
-
- /*
- * Append comma delimiter only if we have already outputted some
- * fields after the initial string "{ ".
- */
- if (out->len > 2)
- appendStringInfoString(out, ", ");
- }
-
arg = PG_GETARG_DATUM(1);
- key_offset = out->len;
-
- datum_to_json(arg, false, out, state->key_category,
+ datum_to_json(arg, false, state->str, state->key_category,
state->key_output_func, true);
- if (unique_keys)
- {
- const char *key = &out->data[key_offset];
-
- if (!json_unique_check_key(&state->unique_check.check, key, 0))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
- errmsg("duplicate JSON key %s", key)));
-
- if (skip)
- PG_RETURN_POINTER(state);
- }
-
appendStringInfoString(state->str, " : ");
if (PG_ARGISNULL(2))
PG_RETURN_POINTER(state);
}
-/*
- * json_object_agg aggregate function
- */
-Datum
-json_object_agg_transfn(PG_FUNCTION_ARGS)
-{
- return json_object_agg_transfn_worker(fcinfo, false, false);
-}
-
-/*
- * json_object_agg_strict aggregate function
- */
-Datum
-json_object_agg_strict_transfn(PG_FUNCTION_ARGS)
-{
- return json_object_agg_transfn_worker(fcinfo, true, false);
-}
-
-/*
- * json_object_agg_unique aggregate function
- */
-Datum
-json_object_agg_unique_transfn(PG_FUNCTION_ARGS)
-{
- return json_object_agg_transfn_worker(fcinfo, false, true);
-}
-
-/*
- * json_object_agg_unique_strict aggregate function
- */
-Datum
-json_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
-{
- return json_object_agg_transfn_worker(fcinfo, true, true);
-}
-
/*
* json_object_agg final function.
*/
return result;
}
+/*
+ * SQL function json_build_object(variadic "any")
+ */
Datum
-json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
- bool absent_on_null, bool unique_keys)
+json_build_object(PG_FUNCTION_ARGS)
{
+ int nargs;
int i;
const char *sep = "";
StringInfo result;
- JsonUniqueBuilderState unique_check;
+ Datum *args;
+ bool *nulls;
+ Oid *types;
+
+ /* fetch argument values to build the object */
+ nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
+
+ if (nargs < 0)
+ PG_RETURN_NULL();
if (nargs % 2 != 0)
ereport(ERROR,
appendStringInfoChar(result, '{');
- if (unique_keys)
- json_unique_builder_init(&unique_check);
-
for (i = 0; i < nargs; i += 2)
{
- StringInfo out;
- bool skip;
- int key_offset;
-
- /* Skip null values if absent_on_null */
- skip = absent_on_null && nulls[i + 1];
-
- if (skip)
- {
- /* If key uniqueness check is needed we must save skipped keys */
- if (!unique_keys)
- continue;
-
- out = json_unique_builder_get_skipped_keys(&unique_check);
- }
- else
- {
- appendStringInfoString(result, sep);
- sep = ", ";
- out = result;
- }
+ appendStringInfoString(result, sep);
+ sep = ", ";
/* process key */
if (nulls[i])
errmsg("argument %d cannot be null", i + 1),
errhint("Object keys should be text.")));
- /* save key offset before key appending */
- key_offset = out->len;
-
- add_json(args[i], false, out, types[i], true);
-
- if (unique_keys)
- {
- /* check key uniqueness after key appending */
- const char *key = &out->data[key_offset];
-
- if (!json_unique_check_key(&unique_check.check, key, 0))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
- errmsg("duplicate JSON key %s", key)));
-
- if (skip)
- continue;
- }
+ add_json(args[i], false, result, types[i], true);
appendStringInfoString(result, " : ");
appendStringInfoChar(result, '}');
- return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
-}
-
-/*
- * SQL function json_build_object(variadic "any")
- */
-Datum
-json_build_object(PG_FUNCTION_ARGS)
-{
- Datum *args;
- bool *nulls;
- Oid *types;
-
- /* build argument values to build the object */
- int nargs = extract_variadic_args(fcinfo, 0, true,
- &args, &types, &nulls);
-
- if (nargs < 0)
- PG_RETURN_NULL();
-
- PG_RETURN_DATUM(json_build_object_worker(nargs, args, nulls, types, false, false));
+ PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
}
/*
PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
}
+/*
+ * SQL function json_build_array(variadic "any")
+ */
Datum
-json_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
- bool absent_on_null)
+json_build_array(PG_FUNCTION_ARGS)
{
+ int nargs;
int i;
const char *sep = "";
StringInfo result;
+ Datum *args;
+ bool *nulls;
+ Oid *types;
+
+ /* fetch argument values to build the array */
+ nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
+
+ if (nargs < 0)
+ PG_RETURN_NULL();
result = makeStringInfo();
for (i = 0; i < nargs; i++)
{
- if (absent_on_null && nulls[i])
- continue;
-
appendStringInfoString(result, sep);
sep = ", ";
add_json(args[i], nulls[i], result, types[i], false);
appendStringInfoChar(result, ']');
- return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
-}
-
-/*
- * SQL function json_build_array(variadic "any")
- */
-Datum
-json_build_array(PG_FUNCTION_ARGS)
-{
- Datum *args;
- bool *nulls;
- Oid *types;
-
- /* build argument values to build the object */
- int nargs = extract_variadic_args(fcinfo, 0, true,
- &args, &types, &nulls);
-
- if (nargs < 0)
- PG_RETURN_NULL();
-
- PG_RETURN_DATUM(json_build_array_worker(nargs, args, nulls, types, false));
+ PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
}
/*
appendStringInfoCharMacro(buf, '"');
}
-/* Semantic actions for key uniqueness check */
-static void
-json_unique_object_start(void *_state)
-{
- JsonUniqueParsingState *state = _state;
- JsonUniqueStackEntry *entry;
-
- if (!state->unique)
- return;
-
- /* push object entry to stack */
- entry = palloc(sizeof(*entry));
- entry->object_id = state->id_counter++;
- entry->parent = state->stack;
- state->stack = entry;
-}
-
-static void
-json_unique_object_end(void *_state)
-{
- JsonUniqueParsingState *state = _state;
- JsonUniqueStackEntry *entry;
-
- if (!state->unique)
- return;
-
- entry = state->stack;
- state->stack = entry->parent; /* pop object from stack */
- pfree(entry);
-}
-
-static void
-json_unique_object_field_start(void *_state, char *field, bool isnull)
-{
- JsonUniqueParsingState *state = _state;
- JsonUniqueStackEntry *entry;
-
- if (!state->unique)
- return;
-
- /* find key collision in the current object */
- if (json_unique_check_key(&state->check, field, state->stack->object_id))
- return;
-
- state->unique = false;
-
- /* pop all objects entries */
- while ((entry = state->stack))
- {
- state->stack = entry->parent;
- pfree(entry);
- }
-}
-
-/* Validate JSON text and additionally check key uniqueness */
-bool
-json_validate(text *json, bool check_unique_keys, bool throw_error)
-{
- JsonLexContext *lex = makeJsonLexContext(json, check_unique_keys);
- JsonSemAction uniqueSemAction = {0};
- JsonUniqueParsingState state;
- JsonParseErrorType result;
-
- if (check_unique_keys)
- {
- state.lex = lex;
- state.stack = NULL;
- state.id_counter = 0;
- state.unique = true;
- json_unique_check_init(&state.check);
-
- uniqueSemAction.semstate = &state;
- uniqueSemAction.object_start = json_unique_object_start;
- uniqueSemAction.object_field_start = json_unique_object_field_start;
- uniqueSemAction.object_end = json_unique_object_end;
- }
-
- result = pg_parse_json(lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
-
- if (result != JSON_SUCCESS)
- {
- if (throw_error)
- json_ereport_error(result, lex);
-
- return false; /* invalid json */
- }
-
- if (check_unique_keys && !state.unique)
- {
- if (throw_error)
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
- errmsg("duplicate JSON object key value")));
-
- return false; /* not unique keys */
- }
-
- return true; /* ok */
-}
-
/*
* SQL function json_typeof(json) -> text
*
Datum
json_typeof(PG_FUNCTION_ARGS)
{
- text *json = PG_GETARG_TEXT_PP(0);
- char *type;
+ text *json;
+
+ JsonLexContext *lex;
JsonTokenType tok;
+ char *type;
+ JsonParseErrorType result;
- /* Lex exactly one token from the input and check its type. */
- tok = json_get_first_token(json, true);
+ json = PG_GETARG_TEXT_PP(0);
+ lex = makeJsonLexContext(json, false);
+ /* Lex exactly one token from the input and check its type. */
+ result = json_lex(lex);
+ if (result != JSON_SUCCESS)
+ json_ereport_error(result, lex);
+ tok = lex->token_type;
switch (tok)
{
case JSON_TOKEN_OBJECT_START:
#include "access/htup_details.h"
#include "access/transam.h"
-#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
{
JsonbParseState *parseState;
JsonbValue *res;
- bool unique_keys;
} JsonbInState;
+/* unlike with json categories, we need to treat json and jsonb differently */
+typedef enum /* type categories for datum_to_jsonb */
+{
+ JSONBTYPE_NULL, /* null, so we didn't bother to identify */
+ JSONBTYPE_BOOL, /* boolean (built-in types only) */
+ JSONBTYPE_NUMERIC, /* numeric (ditto) */
+ JSONBTYPE_DATE, /* we use special formatting for datetimes */
+ JSONBTYPE_TIMESTAMP, /* we use special formatting for timestamp */
+ JSONBTYPE_TIMESTAMPTZ, /* ... and timestamptz */
+ JSONBTYPE_JSON, /* JSON */
+ JSONBTYPE_JSONB, /* JSONB */
+ JSONBTYPE_ARRAY, /* array */
+ JSONBTYPE_COMPOSITE, /* composite */
+ JSONBTYPE_JSONCAST, /* something with an explicit cast to JSON */
+ JSONBTYPE_OTHER /* all else */
+} JsonbTypeCategory;
+
typedef struct JsonbAggState
{
JsonbInState *res;
Oid val_output_func;
} JsonbAggState;
-static inline Datum jsonb_from_cstring(char *json, int len, bool unique_keys);
+static inline Datum jsonb_from_cstring(char *json, int len);
static size_t checkStringLen(size_t len);
static void jsonb_in_object_start(void *pstate);
static void jsonb_in_object_end(void *pstate);
static void jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal);
static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
+static void jsonb_categorize_type(Oid typoid,
+ JsonbTypeCategory *tcategory,
+ Oid *outfuncoid);
static void composite_to_jsonb(Datum composite, JsonbInState *result);
static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims,
Datum *vals, bool *nulls, int *valcount,
JsonbTypeCategory tcategory, Oid outfuncoid);
static void array_to_jsonb_internal(Datum array, JsonbInState *result);
+static void jsonb_categorize_type(Oid typoid,
+ JsonbTypeCategory *tcategory,
+ Oid *outfuncoid);
static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
JsonbTypeCategory tcategory, Oid outfuncoid,
bool key_scalar);
{
char *json = PG_GETARG_CSTRING(0);
- return jsonb_from_cstring(json, strlen(json), false);
+ return jsonb_from_cstring(json, strlen(json));
}
/*
else
elog(ERROR, "unsupported jsonb version number %d", version);
- return jsonb_from_cstring(str, nbytes, false);
+ return jsonb_from_cstring(str, nbytes);
}
/*
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
-Datum
-jsonb_from_text(text *js, bool unique_keys)
-{
- return jsonb_from_cstring(VARDATA_ANY(js),
- VARSIZE_ANY_EXHDR(js),
- unique_keys);
-}
-
/*
* Get the type name of a jsonb container.
*/
* Uses the json parser (with hooks) to construct a jsonb.
*/
static inline Datum
-jsonb_from_cstring(char *json, int len, bool unique_keys)
+jsonb_from_cstring(char *json, int len)
{
JsonLexContext *lex;
JsonbInState state;
memset(&sem, 0, sizeof(sem));
lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
- state.unique_keys = unique_keys;
-
sem.semstate = (void *) &state;
sem.object_start = jsonb_in_object_start;
JsonbInState *_state = (JsonbInState *) pstate;
_state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_OBJECT, NULL);
- _state->parseState->unique_keys = _state->unique_keys;
}
static void
* output function OID. If the returned category is JSONBTYPE_JSONCAST,
* we return the OID of the relevant cast function instead.
*/
-void
+static void
jsonb_categorize_type(Oid typoid,
JsonbTypeCategory *tcategory,
Oid *outfuncoid)
datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar);
}
-Datum
-to_jsonb_worker(Datum val, JsonbTypeCategory tcategory, Oid outfuncoid)
-{
- JsonbInState result;
-
- memset(&result, 0, sizeof(JsonbInState));
-
- datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
-
- return JsonbPGetDatum(JsonbValueToJsonb(result.res));
-}
-
-bool
-to_jsonb_is_immutable(Oid typoid)
-{
- JsonbTypeCategory tcategory;
- Oid outfuncoid;
-
- jsonb_categorize_type(typoid, &tcategory, &outfuncoid);
-
- switch (tcategory)
- {
- case JSONBTYPE_BOOL:
- case JSONBTYPE_JSON:
- case JSONBTYPE_JSONB:
- return true;
-
- case JSONBTYPE_DATE:
- case JSONBTYPE_TIMESTAMP:
- case JSONBTYPE_TIMESTAMPTZ:
- return false;
-
- case JSONBTYPE_ARRAY:
- return false; /* TODO recurse into elements */
-
- case JSONBTYPE_COMPOSITE:
- return false; /* TODO recurse into fields */
-
- case JSONBTYPE_NUMERIC:
- case JSONBTYPE_JSONCAST:
- default:
- return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
- }
-}
-
/*
* SQL function to_jsonb(anyvalue)
*/
{
Datum val = PG_GETARG_DATUM(0);
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+ JsonbInState result;
JsonbTypeCategory tcategory;
Oid outfuncoid;
jsonb_categorize_type(val_type,
&tcategory, &outfuncoid);
- PG_RETURN_DATUM(to_jsonb_worker(val, tcategory, outfuncoid));
+ memset(&result, 0, sizeof(JsonbInState));
+
+ datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
+
+ PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
}
+/*
+ * SQL function jsonb_build_object(variadic "any")
+ */
Datum
-jsonb_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
- bool absent_on_null, bool unique_keys)
+jsonb_build_object(PG_FUNCTION_ARGS)
{
+ int nargs;
int i;
JsonbInState result;
+ Datum *args;
+ bool *nulls;
+ Oid *types;
+
+ /* build argument values to build the object */
+ nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+ if (nargs < 0)
+ PG_RETURN_NULL();
if (nargs % 2 != 0)
ereport(ERROR,
memset(&result, 0, sizeof(JsonbInState));
result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
- result.parseState->unique_keys = unique_keys;
- result.parseState->skip_nulls = absent_on_null;
for (i = 0; i < nargs; i += 2)
{
/* process key */
- bool skip;
-
if (nulls[i])
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("argument %d: key must not be null", i + 1)));
- /* skip null values if absent_on_null */
- skip = absent_on_null && nulls[i + 1];
-
- /* we need to save skipped keys for the key uniqueness check */
- if (skip && !unique_keys)
- continue;
-
add_jsonb(args[i], false, &result, types[i], true);
/* process value */
result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
- return JsonbPGetDatum(JsonbValueToJsonb(result.res));
-}
-
-/*
- * SQL function jsonb_build_object(variadic "any")
- */
-Datum
-jsonb_build_object(PG_FUNCTION_ARGS)
-{
- Datum *args;
- bool *nulls;
- Oid *types;
-
- /* build argument values to build the object */
- int nargs = extract_variadic_args(fcinfo, 0, true,
- &args, &types, &nulls);
-
- if (nargs < 0)
- PG_RETURN_NULL();
-
- PG_RETURN_DATUM(jsonb_build_object_worker(nargs, args, nulls, types, false, false));
+ PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
}
/*
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
}
-Datum
-jsonb_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
- bool absent_on_null)
-{
- int i;
- JsonbInState result;
-
- memset(&result, 0, sizeof(JsonbInState));
-
- result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
-
- for (i = 0; i < nargs; i++)
- {
- if (absent_on_null && nulls[i])
- continue;
-
- add_jsonb(args[i], nulls[i], &result, types[i], false);
- }
-
- result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
-
- return JsonbPGetDatum(JsonbValueToJsonb(result.res));
-}
-
/*
* SQL function jsonb_build_array(variadic "any")
*/
Datum
jsonb_build_array(PG_FUNCTION_ARGS)
{
+ int nargs;
+ int i;
+ JsonbInState result;
Datum *args;
bool *nulls;
Oid *types;
- /* build argument values to build the object */
- int nargs = extract_variadic_args(fcinfo, 0, true,
- &args, &types, &nulls);
+ /* build argument values to build the array */
+ nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
if (nargs < 0)
PG_RETURN_NULL();
- PG_RETURN_DATUM(jsonb_build_array_worker(nargs, args, nulls, types, false));
-}
+ memset(&result, 0, sizeof(JsonbInState));
+
+ result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
+ for (i = 0; i < nargs; i++)
+ add_jsonb(args[i], nulls[i], &result, types[i], false);
+
+ result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
+
+ PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+}
/*
* degenerate case of jsonb_build_array where it gets 0 arguments.
{
ocursor->contVal = icursor->contVal;
ocursor->size = icursor->size;
- ocursor->unique_keys = icursor->unique_keys;
- ocursor->skip_nulls = icursor->skip_nulls;
icursor = icursor->next;
if (icursor == NULL)
break;
return result;
}
-static Datum
-jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
+
+/*
+ * jsonb_agg aggregate function
+ */
+Datum
+jsonb_agg_transfn(PG_FUNCTION_ARGS)
{
MemoryContext oldcontext,
aggcontext;
result = state->res;
}
- if (absent_on_null && PG_ARGISNULL(1))
- PG_RETURN_POINTER(state);
-
/* turn the argument into jsonb in the normal function context */
val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
PG_RETURN_POINTER(state);
}
-/*
- * jsonb_agg aggregate function
- */
-Datum
-jsonb_agg_transfn(PG_FUNCTION_ARGS)
-{
- return jsonb_agg_transfn_worker(fcinfo, false);
-}
-
-/*
- * jsonb_agg_strict aggregate function
- */
-Datum
-jsonb_agg_strict_transfn(PG_FUNCTION_ARGS)
-{
- return jsonb_agg_transfn_worker(fcinfo, true);
-}
-
Datum
jsonb_agg_finalfn(PG_FUNCTION_ARGS)
{
PG_RETURN_POINTER(out);
}
-static Datum
-jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
- bool absent_on_null, bool unique_keys)
+/*
+ * jsonb_object_agg aggregate function
+ */
+Datum
+jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
{
MemoryContext oldcontext,
aggcontext;
*jbval;
JsonbValue v;
JsonbIteratorToken type;
- bool skip;
if (!AggCheckCallContext(fcinfo, &aggcontext))
{
state->res = result;
result->res = pushJsonbValue(&result->parseState,
WJB_BEGIN_OBJECT, NULL);
- result->parseState->unique_keys = unique_keys;
- result->parseState->skip_nulls = absent_on_null;
-
MemoryContextSwitchTo(oldcontext);
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("field name must not be null")));
- /*
- * Skip null values if absent_on_null unless key uniqueness check is
- * needed (because we must save keys in this case).
- */
- skip = absent_on_null && PG_ARGISNULL(2);
-
- if (skip && !unique_keys)
- PG_RETURN_POINTER(state);
-
val = PG_GETARG_DATUM(1);
memset(&elem, 0, sizeof(JsonbInState));
}
result->res = pushJsonbValue(&result->parseState,
WJB_KEY, &v);
-
- if (skip)
- {
- v.type = jbvNull;
- result->res = pushJsonbValue(&result->parseState,
- WJB_VALUE, &v);
- MemoryContextSwitchTo(oldcontext);
- PG_RETURN_POINTER(state);
- }
-
break;
case WJB_END_ARRAY:
break;
PG_RETURN_POINTER(state);
}
-/*
- * jsonb_object_agg aggregate function
- */
-Datum
-jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
-{
- return jsonb_object_agg_transfn_worker(fcinfo, false, false);
-}
-
-
-/*
- * jsonb_object_agg_strict aggregate function
- */
-Datum
-jsonb_object_agg_strict_transfn(PG_FUNCTION_ARGS)
-{
- return jsonb_object_agg_transfn_worker(fcinfo, true, false);
-}
-
-/*
- * jsonb_object_agg_unique aggregate function
- */
-Datum
-jsonb_object_agg_unique_transfn(PG_FUNCTION_ARGS)
-{
- return jsonb_object_agg_transfn_worker(fcinfo, false, true);
-}
-
-/*
- * jsonb_object_agg_unique_strict aggregate function
- */
-Datum
-jsonb_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
-{
- return jsonb_object_agg_transfn_worker(fcinfo, true, true);
-}
-
Datum
jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
{
PG_RETURN_DATUM(retValue);
}
-
-/*
- * Construct an empty array jsonb.
- */
-Jsonb *
-JsonbMakeEmptyArray(void)
-{
- JsonbValue jbv;
-
- jbv.type = jbvArray;
- jbv.val.array.elems = NULL;
- jbv.val.array.nElems = 0;
- jbv.val.array.rawScalar = false;
-
- return JsonbValueToJsonb(&jbv);
-}
-
-/*
- * Construct an empty object jsonb.
- */
-Jsonb *
-JsonbMakeEmptyObject(void)
-{
- JsonbValue jbv;
-
- jbv.type = jbvObject;
- jbv.val.object.pairs = NULL;
- jbv.val.object.nPairs = 0;
-
- return JsonbValueToJsonb(&jbv);
-}
-
-/*
- * Convert jsonb to a C-string stripping quotes from scalar strings.
- */
-char *
-JsonbUnquote(Jsonb *jb)
-{
- if (JB_ROOT_IS_SCALAR(jb))
- {
- JsonbValue v;
-
- (void) JsonbExtractScalar(&jb->root, &v);
-
- if (v.type == jbvString)
- return pnstrdup(v.val.string.val, v.val.string.len);
- else if (v.type == jbvBool)
- return pstrdup(v.val.boolean ? "true" : "false");
- else if (v.type == jbvNumeric)
- return DatumGetCString(DirectFunctionCall1(numeric_out,
- PointerGetDatum(v.val.numeric)));
- else if (v.type == jbvNull)
- return pstrdup("null");
- else
- {
- elog(ERROR, "unrecognized jsonb value type %d", v.type);
- return NULL;
- }
- }
- else
- return JsonbToCString(NULL, &jb->root, VARSIZE(jb));
-}
static int lengthCompareJsonbString(const char *val1, int len1,
const char *val2, int len2);
static int lengthCompareJsonbPair(const void *a, const void *b, void *arg);
-static void uniqueifyJsonbObject(JsonbValue *object, bool unique_keys,
- bool skip_nulls);
+static void uniqueifyJsonbObject(JsonbValue *object);
static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
JsonbIteratorToken seq,
JsonbValue *scalarVal);
appendElement(*pstate, scalarVal);
break;
case WJB_END_OBJECT:
- uniqueifyJsonbObject(&(*pstate)->contVal,
- (*pstate)->unique_keys,
- (*pstate)->skip_nulls);
+ uniqueifyJsonbObject(&(*pstate)->contVal);
/* fall through! */
case WJB_END_ARRAY:
/* Steps here common to WJB_END_OBJECT case */
JsonbParseState *ns = palloc(sizeof(JsonbParseState));
ns->next = *pstate;
- ns->unique_keys = false;
- ns->skip_nulls = false;
-
return ns;
}
* Sort and unique-ify pairs in JsonbValue object
*/
static void
-uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, bool skip_nulls)
+uniqueifyJsonbObject(JsonbValue *object)
{
bool hasNonUniq = false;
qsort_arg(object->val.object.pairs, object->val.object.nPairs, sizeof(JsonbPair),
lengthCompareJsonbPair, &hasNonUniq);
- if (hasNonUniq && unique_keys)
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
- errmsg("duplicate JSON object key value")));
-
- if (hasNonUniq || skip_nulls)
+ if (hasNonUniq)
{
- JsonbPair *ptr,
- *res;
-
- while (skip_nulls && object->val.object.nPairs > 0 &&
- object->val.object.pairs->value.type == jbvNull)
- {
- /* If skip_nulls is true, remove leading items with null */
- object->val.object.pairs++;
- object->val.object.nPairs--;
- }
-
- ptr = object->val.object.pairs + 1;
- res = object->val.object.pairs;
+ JsonbPair *ptr = object->val.object.pairs + 1,
+ *res = object->val.object.pairs;
while (ptr - object->val.object.pairs < object->val.object.nPairs)
{
- /* Avoid copying over duplicate or null */
- if (lengthCompareJsonbStringValue(ptr, res) != 0 &&
- (!skip_nulls || ptr->value.type != jbvNull))
+ /* Avoid copying over duplicate */
+ if (lengthCompareJsonbStringValue(ptr, res) != 0)
{
res++;
if (ptr != res)
check_stack_depth();
- if (jbv->type != jbvBinary ||
- !JsonContainerIsArray(jbc) ||
- JsonContainerIsScalar(jbc))
+ if (jbv->type != jbvBinary || !JsonContainerIsArray(jbc))
populate_array_report_expected_array(ctx, ndim - 1);
+ Assert(!JsonContainerIsScalar(jbc));
+
it = JsonbIteratorInit(jbc);
tok = JsonbIteratorNext(&it, &val, true);
}
}
-/* recursively populate specified type from a json/jsonb value */
-Datum
-json_populate_type(Datum json_val, Oid json_type, Oid typid, int32 typmod,
- void **cache, MemoryContext mcxt, bool *isnull)
-{
- JsValue jsv = {0};
- JsonbValue jbv;
-
- jsv.is_json = json_type == JSONOID;
-
- if (*isnull)
- {
- if (jsv.is_json)
- jsv.val.json.str = NULL;
- else
- jsv.val.jsonb = NULL;
- }
- else if (jsv.is_json)
- {
- text *json = DatumGetTextPP(json_val);
-
- jsv.val.json.str = VARDATA_ANY(json);
- jsv.val.json.len = VARSIZE_ANY_EXHDR(json);
- jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in
- * populate_composite() */
- }
- else
- {
- Jsonb *jsonb = DatumGetJsonbP(json_val);
-
- jsv.val.jsonb = &jbv;
-
- /* fill binary jsonb value pointing to jb */
- jbv.type = jbvBinary;
- jbv.val.binary.data = &jsonb->root;
- jbv.val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
- }
-
- if (!*cache)
- *cache = MemoryContextAllocZero(mcxt, sizeof(ColumnIOData));
-
- return populate_record_field(*cache, typid, typmod, NULL, mcxt,
- PointerGetDatum(NULL), &jsv, isnull);
-}
-
static RecordIOData *
allocate_record_info(MemoryContext mcxt, int ncolumns)
{
else
appendStringInfoString(_state->strval, token);
}
-
-JsonTokenType
-json_get_first_token(text *json, bool throw_error)
-{
- JsonLexContext *lex;
- JsonParseErrorType result;
-
- lex = makeJsonLexContext(json, false);
-
- /* Lex exactly one token from the input and check its type. */
- result = json_lex(lex);
-
- if (result == JSON_SUCCESS)
- return lex->token_type;
-
- if (throw_error)
- json_ereport_error(result, lex);
-
- return JSON_TOKEN_INVALID; /* invalid json */
-}
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
-#include "nodes/nodeFuncs.h"
#include "utils/builtins.h"
-#include "utils/formatting.h"
#include "utils/json.h"
#include "utils/jsonpath.h"
return true;
}
-
-/* SQL/JSON datatype status: */
-typedef enum JsonPathDatatypeStatus
-{
- jpdsNonDateTime, /* null, bool, numeric, string, array, object */
- jpdsUnknownDateTime, /* unknown datetime type */
- jpdsDateTimeZoned, /* timetz, timestamptz */
- jpdsDateTimeNonZoned /* time, timestamp, date */
-} JsonPathDatatypeStatus;
-
-/* Context for jspIsMutableWalker() */
-typedef struct JsonPathMutableContext
-{
- List *varnames; /* list of variable names */
- List *varexprs; /* list of variable expressions */
- JsonPathDatatypeStatus current; /* status of @ item */
- bool lax; /* jsonpath is lax or strict */
- bool mutable; /* resulting mutability status */
-} JsonPathMutableContext;
-
-/*
- * Recursive walker for jspIsMutable()
- */
-static JsonPathDatatypeStatus
-jspIsMutableWalker(JsonPathItem *jpi, JsonPathMutableContext *cxt)
-{
- JsonPathItem next;
- JsonPathDatatypeStatus status = jpdsNonDateTime;
-
- while (!cxt->mutable)
- {
- JsonPathItem arg;
- JsonPathDatatypeStatus leftStatus;
- JsonPathDatatypeStatus rightStatus;
-
- switch (jpi->type)
- {
- case jpiRoot:
- Assert(status == jpdsNonDateTime);
- break;
-
- case jpiCurrent:
- Assert(status == jpdsNonDateTime);
- status = cxt->current;
- break;
-
- case jpiFilter:
- {
- JsonPathDatatypeStatus prevStatus = cxt->current;
-
- cxt->current = status;
- jspGetArg(jpi, &arg);
- jspIsMutableWalker(&arg, cxt);
-
- cxt->current = prevStatus;
- break;
- }
-
- case jpiVariable:
- {
- int32 len;
- const char *name = jspGetString(jpi, &len);
- ListCell *lc1;
- ListCell *lc2;
-
- Assert(status == jpdsNonDateTime);
-
- forboth(lc1, cxt->varnames, lc2, cxt->varexprs)
- {
- String *varname = lfirst_node(String, lc1);
- Node *varexpr = lfirst(lc2);
-
- if (strncmp(varname->sval, name, len))
- continue;
-
- switch (exprType(varexpr))
- {
- case DATEOID:
- case TIMEOID:
- case TIMESTAMPOID:
- status = jpdsDateTimeNonZoned;
- break;
-
- case TIMETZOID:
- case TIMESTAMPTZOID:
- status = jpdsDateTimeZoned;
- break;
-
- default:
- status = jpdsNonDateTime;
- break;
- }
-
- break;
- }
- break;
- }
-
- case jpiEqual:
- case jpiNotEqual:
- case jpiLess:
- case jpiGreater:
- case jpiLessOrEqual:
- case jpiGreaterOrEqual:
- Assert(status == jpdsNonDateTime);
- jspGetLeftArg(jpi, &arg);
- leftStatus = jspIsMutableWalker(&arg, cxt);
-
- jspGetRightArg(jpi, &arg);
- rightStatus = jspIsMutableWalker(&arg, cxt);
-
- /*
- * Comparison of datetime type with different timezone status
- * is mutable.
- */
- if (leftStatus != jpdsNonDateTime &&
- rightStatus != jpdsNonDateTime &&
- (leftStatus == jpdsUnknownDateTime ||
- rightStatus == jpdsUnknownDateTime ||
- leftStatus != rightStatus))
- cxt->mutable = true;
- break;
-
- case jpiNot:
- case jpiIsUnknown:
- case jpiExists:
- case jpiPlus:
- case jpiMinus:
- Assert(status == jpdsNonDateTime);
- jspGetArg(jpi, &arg);
- &nb